关于内存映射的进程间通信,我直接一篇博客已经详细介绍过,这里只是再次补充一个传输图像的应用,因为OpenCV中Mat格式较为常用(并且也很方便传输),刚好用到,就做个笔记好了。应用的场景是B进程中的Mat传给A进程,A进程接受并恢复为Mat格式。其它也没什么好说的,有细节为题就参见我之前的博客,那么开始吧。
进程A创建映射空间
HANDLE d_hMutex是一个时间的句柄用于进程同步,d_hFileMapping是内存映射的句柄,LPTSTR d_pMsg是映射内存的地址指针
d_hFileMapping = CreateFileMapping(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, 0, 640*480*3*20, _T("SVAF_ALG2GUI_DATA"));
d_hMutex = CreateEvent(nullptr, false, false, _T("SVAF_ALG2GUI_DATA_MUTEX"));
d_pMsg = (LPTSTR)MapViewOfFile(d_hFileMapping, FILE_MAP_ALL_ACCESS, 0, 0, 0);
if (d_pMsg == NULL){
MessageBox(_T("Create Inter Process Error!"));
exit(-1);
}
进程B打开映射空间
这里应该也是可以用OpenEvent的,但是我调的时候OpenEvent在A中总是无法触发,很纠结不知道为什么,算了,就CreateEvent好了,如果你知道原因,请下方留言告知博主,谢谢。
d_mutex_ = CreateEvent(nullptr, false, false, "SVAF_ALG2GUI_DATA_MUTEX"); d_fileMapping_ = OpenFileMapping(FILE_MAP_ALL_ACCESS, false, "SVAF_ALG2GUI_DATA"); d_pMsg_ = (LPTSTR)MapViewOfFile(d_fileMapping_, FILE_MAP_ALL_ACCESS, 0, 0, 0);
进程B发送图像
传送数据有三部分组成,一部分是帧头,另一部分是图像数据,还有一部分是电源数据,当然,我们只需要关注两部分,一个是帧头,定义了有多少图像,每幅图像的大小通道和指针偏移,第二部分是数据,指针转换直接往内存映射区填就可以了。点云部分时博主自己的,可以忽略。
这里强调下,在传输图像数据时,模式cv::Mat的图像数据是continue的,如果你接受到的数据有断行或者其他问题,那么也就是说图像数据在内存中可能不是连续的(需要通过Mat.isContinue()来判断),那么你把它拷贝一遍就连续了(深拷贝)。然后直接把数据的Mat.data指针放上来,拷贝到映射内存区,拷贝完激活一个事件,告诉A进程要接受数据了。
using Bucket = struct{
char head[4];
char message[10][128];
int msgCount;
int imgCount;
int cols[8];
int rows[8];
int chns[8];
int offs[8];
int PointSize[4];
int PointChns[4];// xyz(3) or xyzrgb(6)
int PointOffs[4];
int pclCount;
};
void Circuit::SendData(){
if (!useMapping_ || !d_pMsg_){
return;
}
LPTSTR p = d_pMsg_;
char *pBuf = p;
Bucket *pBucket = (Bucket*)pBuf;
sprintf(pBucket->head, "pch");
sprintf(pBucket->message[0], "123");
pBucket->msgCount = 1;
int frameCount = 0;
int pointCount = 0;
int offset = sizeof(Bucket);
for (int i = 0; i < disp_.size(); ++i){
if (disp_[i].isOutput && (!disp_[i].isOutput3DPoint) && (!disp_[i].image.empty()) && frameCount < 8){
int cols = disp_[i].image.cols;
int rows = disp_[i].image.rows;
int chns = disp_[i].image.channels();
int length = cols * rows * chns;
memcpy(pBuf + offset, disp_[i].image.data, length);
pBucket->cols[frameCount] = cols;
pBucket->rows[frameCount] = rows;
pBucket->chns[frameCount] = chns;
pBucket->offs[frameCount] = offset;
offset += (cols * rows * chns);
frameCount++;
}
if (disp_[i].isOutput && disp_[i].isOutput3DPoint && pointCount < 4){
int count = disp_[i].point3d.size();
if (count <= 0){
continue;
}
int chns = (disp_[i].color3d.size() == disp_[i].point3d.size()) ? 6 : 3;
float *points = (float *)(pBuf + offset);
if (chns == 3){
for (int j = 0; j < count; ++j){
points[j * 3 + 0] = disp_[i].point3d[j].x;
points[j * 3 + 1] = disp_[i].point3d[j].y;
points[j * 3 + 2] = disp_[i].point3d[j].z;
}
} else{
for (int j = 0; j < count; ++j){
points[j * 6 + 0] = disp_[i].point3d[j].x;
points[j * 6 + 1] = disp_[i].point3d[j].y;
points[j * 6 + 2] = disp_[i].point3d[j].z;
points[j * 6 + 3] = disp_[i].color3d[j].r;
points[j * 6 + 4] = disp_[i].color3d[j].g;
points[j * 6 + 5] = disp_[i].color3d[j].b;
}
}
memcpy(pBuf + offset, points, count * chns * sizeof(float));
pBucket->PointSize[pointCount] = count;
pBucket->PointChns[pointCount] = chns;
pBucket->PointOffs[pointCount] = offset;
offset += (count * chns * sizeof(float));
pointCount++;
}
}
pBucket->imgCount = frameCount;
pBucket->pclCount = pointCount;
SetEvent(d_mutex_);
}
进程A接受图像
进程A在WaitForSingleObject的地方阻塞等待,在B激活事件后,A就从WaitForSingleObject这里往后执行,然后打开内存映射区域,解析数据包,然后往cv::Mat里填就可以了。
using Bucket = struct{
char head[4];
char message[10][128];
int msgCount;
int imgCount;
int cols[8];
int rows[8];
int chns[8];
int offs[8];
int PointSize[4];
int PointChns[4];// xyz(3) or xyzrgb(6)
int PointOffs[4];
int pclCount;
};
void CRVAFGUIDlg::ReciveDataInterprocess(){
while (true){
WaitForSingleObject(d_hMutex, INFINITE);
m_imgs.clear();
pointclouds.clear();
LPTSTR p = d_pMsg;
Bucket* pBucket = (Bucket*)d_pMsg;
char *pBuf = (char *)p;
int offset = sizeof(Bucket);
int frameCount = pBucket->imgCount;
int pointCount = pBucket->pclCount;
for (int i = 0; i < frameCount; ++i){
int type = (pBucket->chns[i] == 1) ? CV_8UC1 : CV_8UC3;
cv::Mat img(pBucket->rows[i], pBucket->cols[i], type, pBuf + pBucket->offs[i]);
m_imgs.push_back(img.clone());
}
for (int i = 0; i < pointCount; ++i){
int pnts = pBucket->PointSize[i];
int chns = pBucket->PointChns[i];
float *pPC = (float*)(pBuf + pBucket->PointOffs[i]);
vector<Pointf> pointcloud;
pointcloud.resize(pnts);
if (chns == 3){
for (int j = 0; j < pnts; ++j){
pointcloud[j].x = pPC[j * chns];
pointcloud[j].y = pPC[j * chns + 1];
pointcloud[j].z = pPC[j * chns + 2];
pointcloud[j].r = 1;
pointcloud[j].g = 1;
pointcloud[j].b = 0.5;
}
} else if(chns == 6){
for (int j = 0; j < pnts; ++j){
pointcloud[j].x = pPC[j * chns];
pointcloud[j].y = pPC[j * chns + 1];
pointcloud[j].z = pPC[j * chns + 2];
pointcloud[j].r = pPC[j * chns + 3];
pointcloud[j].g = pPC[j * chns + 4];
pointcloud[j].b = pPC[j * chns + 5];
}
}
pointclouds.push_back(pointcloud);
}
SendMessage(WM_PAINT);
continue;
}
return;
}
进程A销毁资源
放在OnDestroy或者析构函数中,在退出时销毁。
CloseHandle(d_hFileMapping); CloseHandle(d_hMutex);
进程B销毁资源
也是一样
if (!UnmapViewOfFile(d_fileMapping_)){}
CloseHandle(d_fileMapping_);
CloseHandle(d_mutex_);
OK,See You Next Chapter!