本文旨在记录下c++和python之间的图像传输方式。由于项目的需要,我需要将c++编写的程序中的图像借助python的深度学习来做一些图像处理的算法设计。在网上查阅了大量资料后,找到一篇大牛写个博文,链接:https://blog.jiejingma.cn/2020/11/30/communication/zai-c-yu-python-jian-chuan-shi-pin-zheng/
按照大牛写的博文,我也利用共享内存的方式实现了图像的跨语言(c++到python)的传递,下文具体内容为转载大牛的博文。
引言
本案例旨在实现跨语言(C++和python间)视频的实时通信。这一工作内容在实际工程中很常见。由于python语言支持很多第三方库,对于开发深度学习项目很方便,验真算法速度快,很多开源算法也大多基于python实现。这时可能就会出现C++的代码借助python语言做一些图像处理(包括目标检测、姿态估计、目标跟踪等任务)的需求。
平台环境:
- Win10
- VS2019
- OpenCV
进程间通信方式:共享内存
1.进程间通信
进程间通信方式有很多种。工程上最常用的是共享内存和socket机制。前者效率高,基本思想就是开辟一块公共的内存空间,供两个或多个进程之间使用。为了标识这个公共空间,要给它起个名。但是共享内存的方式不支持多平台。而socket刚好就是支持多平台间进程通信的方式。当然这种方式也会慢一些。
在本案例中,分别尝试了两种方式。虽然最终共享内存的方式写内存帧率只达到15fps左右,但是要比socket快了近20倍(大概0.5-1fps左右)。下面将介绍这两种机制的具体实现过程。
2.基于共享内存的视频传输
2.1 C++之间的通信
2.1.1 接口函数
首先验证C++之间能通信。这里使用的是CreateFileMapping和MapViewOfFile进行共享内存的创建和映射。
其中CreateFileMapping的接口为,参数含义详解请点击链接。
HANDLE CreateFileMapping( HANDLE hFile, LPSECURITY_ATTRIBUTES lpFileMappingAttributes, DWORD flProtect, DWORD dwMaximumSizeHigh, DWORD dwMaximumSizeLow, LPCSTR lpName );
MapViewOfFile的接口为
LPVOID MapViewOfFile( HANDLE hFileMappingObject, DWORD dwDesiredAccess, DWORD dwFileOffsetHigh, DWORD dwFileOffsetLow, SIZE_T dwNumberOfBytesToMap );
2.1.2 创建数据格式和共享内存信息
首先需要一个图像的头部
typedef struct { int width; int height; int type; }ImgInf; //图像信息
由于sizeofintint=4,所以这里ImgInf结构体大小为12B。在进行共享内存映射时,我们需要这个大小去做偏移量,找到图像数据。
接下来要定义图像的数据信息
#define FRAME_NUMBER 1 // 图像路数 #define FRAME_W 1920 #define FRAME_H 1080 #define FRAME_W_H FRAME_W*FRAME_H // 图像分辨率:彩色图(3通道)+图像信息结构体 #define FRAME_SIZE FRAME_W_H*sizeof(unsigned char)*3+sizeof(ImgInf) #define MEMORY_SIZE FRAME_NUMBER*FRAME_SIZE

定义共享内存类SHAREDMEMORY
class SHAREDMEMORY { public: SHAREDMEMORY(); ~SHAREDMEMORY(); //void SendBox(TrackBox& BOX); //void RecBox(TrackBox& BOX); //void SendVectorBox(vector<TrackBox>& VTrackBox); //void RecieveVectorBox(vector<TrackBox>& VTrackBox); void SendMat(cv::Mat img, char indexAddress); cv::Mat ReceiveMat(char indexAddress); void SendStr(const char data[]); char* ReceiveStr(); public: int state; private: HANDLE hShareMem; //共享内存句柄 TCHAR sShareMemName[30] = TEXT("CppPytonSharedFrame"); // 共享内存名称 LPCTSTR pBuf; };
其中SendMat为图像数据发送,ReceiveMat为图像接收。
SendStr为字符串发送,ReceiveStr为字符串接收。
最后的ShareMemory.h文件如下:
#pragma once // ShareMemory.h : 此文件包含共享内存数据定义、大小确定、位置分配、信息定义 // Author : Jiejing.Ma // Update : 2020/11/27 #ifndef ShareMemory_H #define ShareMemory_H #include <opencv2/core.hpp> #include <opencv2/videoio.hpp> #include <opencv2/highgui.hpp> #include <opencv2/imgproc.hpp> // cv::Canny() #include <opencv2/opencv.hpp> #include <Windows.h> //=================================共享内存数据定义================================= typedef struct { int width; int height; int type; }ImgInf; //图像信息 //=================================共享内存大小确定================================= // 为图像分配空间 #define FRAME_NUMBER 1 // 图像路数 #define FRAME_W 1920 #define FRAME_H 1080 #define FRAME_W_H FRAME_W*FRAME_H // 图像分辨率:彩色图(3通道)+图像信息结构体 #define FRAME_SIZE FRAME_W_H*sizeof(unsigned char)*3+sizeof(ImgInf) #define MEMORY_SIZE FRAME_NUMBER*FRAME_SIZE //=================================共享内存信息定义================================= #define INITSUCCESS 0 #define CREATEMAPFAILED 1 #define MAPVIEWFAILED 2 class SHAREDMEMORY { public: SHAREDMEMORY(); ~SHAREDMEMORY(); void SendMat(cv::Mat img, char indexAddress); cv::Mat ReceiveMat(char indexAddress); void SendStr(const char data[]); char* ReceiveStr(); public: int state; private: HANDLE hShareMem; //共享内存句柄 TCHAR sShareMemName[30] = TEXT("ShareMedia"); // 共享内存名称 LPCTSTR pBuf; }; #endif // !ShareMemory_H
对应的ShareMemory.cpp文件为类的实现。
#pragma once // ShareMemory.cpp : 此文件包含信息定义SHAREDMEMOR类的实现 // Author : MJJ // Update : 2020/11/27 #ifndef ShareMemory_CPP #define ShareMemory_CPP #include "ShareMemory.h" #include <iostream> using namespace cv; using namespace std; /************************************************************************************* FuncName :SHAREDMEMORY::~SHAREDMEMORY() Desc :构造函数创建共享内存 Input :None Output :None **************************************************************************************/ SHAREDMEMORY::SHAREDMEMORY() { hShareMem = CreateFileMapping( INVALID_HANDLE_VALUE, // use paging file NULL, //default security PAGE_READWRITE, //read/write access 0, // maximum object size(high-order DWORD) MEMORY_SIZE, //maximum object size(low-order DWORD) sShareMemName); //name of mapping object if (hShareMem) { // 映射对象视图,得到共享内存指针,设置数据 pBuf = (LPTSTR)MapViewOfFile( hShareMem, //handle to map object FILE_MAP_ALL_ACCESS, // read/write permission 0, 0, MEMORY_SIZE); cout << "memory size:" << MEMORY_SIZE<< endl; // 若映射失败退出 if (pBuf == NULL) { std::cout << "Could not map view of framebuffer file." << GetLastError() << std::endl; CloseHandle(hShareMem); state = MAPVIEWFAILED; } } else { std::cout << "Could not create file mapping object." << GetLastError() << std::endl; state = CREATEMAPFAILED; } state = INITSUCCESS; } /************************************************************************************* FuncName :SHAREDMEMORY::~SHAREDMEMORY() Desc :析构函数释放 Input :None Output :None **************************************************************************************/ SHAREDMEMORY::~SHAREDMEMORY() { std::cout << "unmap shared addr." << std::endl; UnmapViewOfFile(pBuf); //释放; CloseHandle(hShareMem); } /************************************************************************************* FuncName :void SHAREDMEMORY::SendMat(cv::Mat img, char indexAddress) Desc :发送Mat数据 Input : Mat img 发送图像 char indexAddress 共享内存中起始位置,若只有一路视频则无偏移 Output :None **************************************************************************************/ void SHAREDMEMORY::SendMat(cv::Mat img, char indexAddress) { ImgInf img_head; img_head.width = img.cols; img_head.height = img.rows; img_head.type = img.type(); if (img_head.type == CV_64FC1) { memcpy((char*)pBuf + indexAddress, &img_head, sizeof(ImgInf)); memcpy((char*)pBuf + indexAddress + sizeof(ImgInf), // Address of dst img.data, // Src data img.cols * img.rows * img.channels() * sizeof(double) // size of data ); } else { memcpy((char*)pBuf + indexAddress, &img_head, sizeof(ImgInf)); memcpy((char*)pBuf + indexAddress + sizeof(ImgInf), // Address of dst img