本文旨在记录下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)