本文旨在記錄下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