关于内存映射的进程间通信,我直接一篇博客已经详细介绍过,这里只是再次补充一个传输图像的应用,因为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!