C#与Python脚本使用共享内存通信

news2024/12/23 9:33:48

实现的功能:

C#中读取一张图像,通过共享内存传给python脚本进行处理后将图像进行存储,C#读取处理过后的图像。

C#与python通信有好几种,为什么选择共享内存?

处理图像的速度需求是1秒钟处理5张以上,通过进程调用的方式无法达到速度的要求,共享内存可以。
优点是速度快,缺点是需要在运行的电脑上安装python环境。
好了,废话说完了,下面是代码示例
我使用的框架是Framework4.6.2,首先需要安装pythonnet NuGet包。框架依赖项显示需要.Net2.0及以上,实测Framework4.6.2也可以使用
在这里插入图片描述
实现源码:C#部分

private void PythonInit()
{
    // 设置Python的安装目录
    string pathToVirtualEnv = @"C:\Users\worker\AppData\Local\Programs\Python\Python312";  // 替换为你自己的Python安装路径

    // 设置Python DLL和Python解释器的路径
    Runtime.PythonDLL = System.IO.Path.Combine(pathToVirtualEnv, "python312.dll");
    PythonEngine.PythonHome = System.IO.Path.Combine(pathToVirtualEnv, "python.exe");
    // 设置Python路径,包括必要的依赖库路径
    PythonEngine.PythonPath = "C:\\Users\\worker\\AppData\\Local\\Programs\\Python\\Python312\\Lib\\site-packages;C:\\Users\\worker\\AppData\\Local\\Programs\\Python\\Python312\\Lib;C:\\Users\\worker\\AppData\\Local\\Programs\\Python\\Python312\\DLLs;C:\\Users\\worker\\AppData\\Local\\Programs\\Python\\Python312\\tcl";
    PythonEngine.Initialize();
}

private async void BtnMemoryTest_Click(object sender, EventArgs e)
{
    // 使用 Task.Run 启动异步任务
    await Task.Run(() =>
    {
        try
        {
            PythonInit();
            var stopwatch = new System.Diagnostics.Stopwatch();

            // 计时开始
            stopwatch.Start();

            // 加载图像并写入共享内存
            Bitmap b = new Bitmap("output.png");
            using (MemoryStream ms = new MemoryStream())
            {
                b.Save(ms, System.Drawing.Imaging.ImageFormat.Bmp);
                byte[] bytes = ms.ToArray();  // 使用 ToArray 获取字节数组

                // 创建或打开共享内存
                using (var mmf = MemoryMappedFile.CreateOrOpen("test1", bytes.Length, MemoryMappedFileAccess.ReadWrite))
                {
                    using (var viewAccessor = mmf.CreateViewAccessor(0, bytes.Length))
                    {
                        viewAccessor.Write(0, bytes.Length);
                        viewAccessor.WriteArray<byte>(0, bytes, 0, bytes.Length);
                    }
                }

                // 显示写入成功
                this.Invoke(new Action(() => MessageBox.Show("Write ok, size: " + bytes.Length.ToString())));
            }

            // 在子线程中调用 Python 脚本
            using (Py.GIL())  // 必须获取 GIL 锁来执行 Python 脚本
            {
                string file = Path.Combine(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), "MemoryTest.py");
                using (var scope = Py.CreateScope())
                {
                    string code = File.ReadAllText(file);
                    var scriptCompiled = PythonEngine.Compile(code);
                    scope.Execute(scriptCompiled);
                }
            }

            // 计时结束
            stopwatch.Stop();
            this.Invoke(new Action(() => MessageBox.Show($"Execution completed in {stopwatch.ElapsedMilliseconds} ms")));
        }
        catch (Exception ex)
        {
            this.Invoke(new Action(() => MessageBox.Show("Error: " + ex.Message)));
        }
    });
}

Python源码:

import mmap
import cv2
import numpy as np
import matplotlib
import matplotlib.pyplot as plt

# 使用非图形界面的后端
matplotlib.use('Agg')

# 共享内存的字节大小
byteSize = 40054

# 共享内存的名称
file_name = 'test1'

# 打开共享内存文件
f = mmap.mmap(0, byteSize, file_name, mmap.ACCESS_READ)

# 读取共享内存中的数据并解码为图像
img = cv2.imdecode(np.frombuffer(f, np.uint8), cv2.IMREAD_COLOR)

# 将图像转换为灰度图像
gray_image = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# 平滑处理:应用高斯模糊
blurred_image = cv2.GaussianBlur(gray_image, (5, 5), 0)

# 获取图像的尺寸
height, width = gray_image.shape

# 创建网格数据,代表图像中的坐标点
X, Y = np.meshgrid(np.arange(0, width), np.arange(0, height))

# 创建一个图形,并在其上叠加等值线
plt.figure()

# 保存图像为文件
output_file = 'output_image.png'
plt.savefig(output_file, bbox_inches='tight', pad_inches=0)

# 关闭绘图窗口
plt.close()

print(f"Image saved successfully to {output_file}")

处理时遇到的问题

1、使用之前需要进行PythonEngine的初始化,且只需要初始化一次
所以BtnMemoryTest点击第一次可以成功运行,第二次的时候会报错{“This property must be set before runtime is initialized”}
为什么我要在异步中初始化,而不是在窗口加载时进行初始化?
因为Py.GIL()必须获取 GIL 锁来执行 Python 脚本,如果PythonEngine的初始化和Py.GIL()不在同一个线程ID中,Py.GIL()会一直获取不到GIL锁,导致程序卡住。
所以如果在线程中调用Python脚本,一定要在同线程中对PythonEngine进行初始化。
2、注意共享内存大小的修改
Python源码中byteSize = 40054,这个是需要根据你图像的大小去修改的,在C#源码中this.Invoke(new Action(() => MessageBox.Show("Write ok, size: " + bytes.Length.ToString())));会弹窗提示,根据bytes.Length进行修改即可。
3、运行环境配置
确保你的电脑已经将python添加到系统环境变量。如果报错:No module named ‘xxx’,是缺少了DLLs文件夹的路径,在安装目录下找到xxx的路径,在PythonEngine.PythonPath添加“xxx”的路径即可。
运行耗时
第一次运行会耗时长一点,100-300ms都是正常的,再次运行耗时很短,约几毫秒左右。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2140115.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

《中文Python穿云箭量化平台二次开发技术11》股票基本信息获取分析及应用示例【前十大股东占股比例提取及分析】

《中文Python穿云箭量化平台二次开发技术11》股票基本信息获取分析及应用示例【前十大股东占股比例提取及分析】 《中文Python穿云箭量化平台》是纯Python开发的量化平台&#xff0c;因此其中很多Python模块&#xff0c;我们可以自己设计新的量化工具&#xff0c;例如自己新的行…

1----安卓机型修复串码 开启端口 檫除基带 支持高通与MTK机型工具预览与操作解析

在玩机过程中。很多玩家会碰到各种各样的故障 。其中最多的就在于基带 串码类。由于目前的安卓机型必须修改或者写入串码等参数必须开启端口。而一些初级玩友不太了解开启参数端口的步骤。这个工具很简单的为安卓机型开启端口。并且操作相对简单。 此工具基本功能 1-----可以…

linux入门到实操-4 linux系统网络配置、连接测试、网络连接模式、修改静态IP、配置主机名

教程来源&#xff1a;B站视频BV1WY4y1H7d3 3天搞定Linux&#xff0c;1天搞定Shell&#xff0c;清华学神带你通关_哔哩哔哩_bilibili 整理汇总的课程内容笔记和课程资料&#xff08;包含课程同版本linux系统文件等内容&#xff09;&#xff0c;供大家学习交流下载&#xff1a;…

第15-03章:类的加载与ClassLoader的理解

3、类的加载与ClassLoader的理解 5.1.类加载(ClassLoad)的理解: a.类加载器的作用: 1.将class文件字节码内容加载到内存中&#xff0c;并将这些静态数据转换成方法区的运行时数据结构&#xff0c;然后在堆中生成一个代表这个类的java.lang.Class对象&#xff0c;作为方法区中…

一步迅速了解Linux

1&#xff0c;什么是LInux&#xff1f; Linux 是一个开源的操作系统(管理计算机硬件资源,人物调度)支持多用户,支持网络,支持多线程. 2&#xff0c;Linux特指什么&#xff1f; linux一词,特指的是linux内核 即最操作系统最核心的那一部分功能.负责管理 计算机的硬件资源&…

AIP接口调用

在当今数字化时代&#xff0c;API接口调用已成为连接不同软件和系统的重要手段。特别是在与淘宝这样的大型电商平台进行数据交互时&#xff0c;AIP&#xff08;人工智能平台&#xff09;接口的作用尤为显著。通过AIP接口&#xff0c;开发者可以访问和利用淘宝庞大的商品数据库&…

k8s介绍及部署

目录 一 Kubernetes 简介及部署方法 1.1 应用部署方式演变 1.2 容器编排应用 1.3 kubernetes 简介 1.4 K8S的设计架构 1.4.1 K8S各个组件用途 1.4.2 K8S 各组件之间的调用关系 1.4.3 K8S 的 常用名词感念 1.4.4 k8S的分层架构 二 K8S集群环境搭建 2.1 k8s中容器的管…

[苍穹外卖]-12Apache POI入门与实战

工作台 需求分析: 工作台是系统运营的数据看板, 并提供快捷操作入口, 可以有效提高商家的工作效率 营业额: 已完成订单的总金额有效订单: 已经完成订单的数量订单完成率: 有效订单数/总订单数*100%平均客单价: 营业额/有效订单数新增用户: 新增的用户数量 接口设计: 一个接口返…

RabbitMQ(高阶使用)死信队列

文章内容是学习过程中的知识总结&#xff0c;如有纰漏&#xff0c;欢迎指正 文章目录 一、什么是死信队列&#xff1f; 二、死信队列使用场景 三、死信队列如何使用 四、打车超时处理 1.打车超时实现 以下是本篇文章正文内容 一、什么是死信队列&#xff1f; 先从概念解释上搞…

嵌入式通信原理—SPI总线通信原理与应用

文章目录 SPI 简介基本原理工作模式特点 SPI寻址方式1. 片选&#xff08;Chip Select, CS&#xff09;2. 多从设备通信3. 菊花链&#xff08;Daisy-Chain&#xff09;模式4. 地址寄存器&#xff08;应用层&#xff09; SPI通信过程时钟信号生成&#xff08;SCLK&#xff09;数据…

supermap Iclient3d for cesium加载地形并夸大地形

先看效果图 这是没有夸张之前的都江堰 这是夸大五倍后的都江堰 下面展示代码 主要就是加载supermaponline的skt地形然后夸大 <template><div class"PartOneBox"><div id"cesiumContainer"></div></div> </template>…

华为eNSP使用详解

eNSP&#xff08;Enterprise Network Simulation Platform&#xff09;是华为提供的一款网络仿真平台&#xff0c;它允许用户在没有真实设备的情况下进行网络实验和学习网络技术。eNSP可以模拟各种网络设备&#xff0c;如交换机、路由器、防火墙等&#xff0c;并支持创建多种网…

【mechine learning-十-grading descent梯度下降实现】

grading descent 梯度下降参数更新方法 --导数和学习率 从导数项直观理解梯度下降 grading descent 算法就是更新参数&#xff0c;今天来学习下如何更新w和b 梯度下降 还是以线性回归的均方差损失函数如下为例&#xff1a; 损失函数的可视化图如下 &#xff1a; 横轴和纵轴分…

[C++]类和对象(上)

我们在之前已经将C的入门基础做了讲解&#xff0c;在本章我们将系统性的阐述C中类和对象的基本定义和用法 1.类的定义 目录 1.类的定义 1.类定义的格式 2.访问限定符 3.类域 2.实例化 1.实例化的概念 2.实例化的对象大小 3.this指针 3.类的默认成员函数 1.构造函数…

二、Kubernetes中pod的管理及优化

目录 一 kubernetes 中的资源 1.1 资源管理介绍 1.2 资源管理方式 1.2.1 命令式对象管理 1.2.2 资源类型 1.2.3 基本命令示例 1.2.4 运行和调试命令示例 1.2.5 高级命令示例 二 什么是pod 2.1 创建自主式pod &#xff08;生产不推荐&#xff09; 2.2 利用控制器管理…

CPLEX+Yalmip+MATLAB2022a配置

来源&#xff1a;yalmipcplex12.10文件及安装教程-CSDN博客https://blog.csdn.net/qq_41944352/article/details/126421198 安装包 来源&#xff1a;yalmipcplex12.10文件及安装教程-CSDN博客 Cplex 需下载&#xff1a; Microsoft Visual C 2015 Redistributable 添加路径&a…

时空大数据平台:激活新质生产力的智慧引擎

在数字化转型的浪潮中&#xff0c;时空大数据平台以其独特的价值&#xff0c;成为推动新质生产力发展的关键力量。本文不仅深入剖析时空大数据平台的定义与内涵&#xff0c;探讨其在智慧城市、智慧农业、环境管理、应急管理等领域的应用成效&#xff0c;还将详尽阐述平台如何通…

【C++】unordered系列

前言&#xff1a; 在C11及以后的标准中&#xff0c;unordered容器是标准模板库&#xff08;STL&#xff09;的一部分&#xff0c;提供了高效的数据结构选项&#xff0c;适用于需要快速查找和插入操作的场景。 unordered通常与关联容器一起使用&#xff0c;特别是unordered_map和…

【ESP32】ESP-IDF开发 | GPIO通用输入输出+LED点灯和按键输入例程

1. 简介 ESP32芯片有34个物理GPIO pad&#xff0c;每个GPIO pad都可用作一个通用IO或连接一个内部的外设信号。IO_MUX、RTC IO_MUX和GPIO交换矩阵用于将信号从外设传输至GPIO pad。 从上面看到&#xff0c;每个pad可以配置成GPIO功能&#xff08;连接GPIO交换矩阵&#xff09;或…

7-17 汉诺塔的非递归实现

输入样例: 3输出样例: a -> c a -> b c -> b a -> c b -> a b -> c a -> c 分析&#xff1a; 不会汉罗塔的uu们&#xff0c;先看看图解&#xff1a; 非递归代码&#xff1a; #include<iostream> #include<stack> using namespace std; s…