本文主要用于python和c++相互通信,通过共享内存相互传递数据,图像,数组,结构体。
python优势在于开发快速方便,有很多扩展库可用,且深度学习很多都是python写的。
c++底层速度快,但是开发慢,尤其是很多SLAM和图像处理的只有c++版本。
为了调试开发方便,有时候需要嫁接两个工程,根据自己实际需要决定。
大概思路
1 c++编译动态库完成各种共享内存的实际操作。
2 python端调用c++动态库进行共享内存数据交互。
3 c++端调用c++动态库进行共享内存数据交互。
主要用的的是ctypes
资料
https://blog.csdn.net/koibiki/article/details/89478458
https://docs.python.org/3/library/ctypes.html
共享内存
在 Linux 实现内存共享的函数主要有 shmget、shmat、shmdt、shmctl 这么四个。
1、shmget 得到或者创建 一个共享内存对象
int shmget(key_t key, size_t size, int shmflg)
其中 key_t key 的值为一个IPC键值,可以通过IPC_PRIVATE 建立一个新的键值为0的共享对象 ,但这并不能保证与IPC对象的对应关系,不同进程之间需要同一个且不会重复的IPC键值来完成通信,一般此参数使用ftok函数来进行创建IPC键值。
size_t size 是映射共享内存的大小,单位为字节 (Byte),但在创建时这里的最小分配单位是一页,大致为4KB,当你超过4KB但是小于8KB时会主动分配两页,也就是不足一页(4KB)的按照一页计算。
int shmflg 参数是需要注明的操作模式。
- 0:取键值对应的共享内存,若此键值相应的共享内存不存在就报错。
- IPC_CREAT:存在与键值对应的共享内存则返回标识符,若不存在则创建共享内存返回标识符。
- IPC_CREAT|IPC_EXCL:不存在相应键值的共享内存则创建此共享内存,若存在则报错
返回值:成功返回共享内存的标识符,失败返回-1。
2、shmat 获得共享内存的地址(与进程建立映射)
void *shmat(int shmid, const void *shmaddr, int shmflg)
int shmid 接收一个共享内存标识符参数,通常由 shmget 返回。
const void *shmaddr 共享内存地址映射到进程哪块内存地址,若设 NULL 则让内核决定。
int shmflg 选择操作模式,设 SHM_RDONLY 为只读共享内存,其他或0为可读写。
返回值:成功返回映射完成的内存地址,失败返回-1。
3、shmdt 与进程断开映射关系
int shmdt(const void *shmaddr)
const void *shmaddr 在进程中用于映射内存地址的指针。
返回值:成功返回0,失败返回-1。
4、shmctl 管理共享内存(释放共享内存)
int shmctl(int shmid, int cmd, struct shmid_ds *buf)
int shmid 接收一个共享内存标识符参数,通常由 shmget 返回。
int cmd 是需要注明的操作模式。
- IPC_STAT:获取共享内存状态,将共享内存的 shmid_ds 信息拷贝到 shmid_ds *buf 中。
- IPC_SET:更改共享内存状态,将 shmid_ds *buf 所指内容拷贝到共享内存 shmid_ds 中。
- IPC_RMID:删除释放这块共享内存。
struct shmid_ds *buf 内存管理结构体,这个结构在 shm.h 中定义,网上能查到信息。
返回值:成功返回0,失败返回-1。
简单实例
#include <stdio.h> #include <string.h> //共享内存 #include <sys/shm.h> //挂起进程 #include <unistd.h> int main(void) { //分配内存标志 int segment_id; //要映射的地址 char* text; //共享内存结构 struct shmid_ds shmbuffer; //创建共享内存 segment_id = shmget((key_t)1234567, 1, IPC_CREAT); //映射共享地址 text = (char*)shmat(segment_id, NULL, 0); while (1) { //映射共享地址 //text = (char*)shmat(segment_id, NULL, 0); //断开映射前,不能重复映射否则内存泄露 //拷入共享地址 memcpy(text, "A", 2); //10000微妙,10毫秒 usleep(10000); //输出当前共享内存区域地址 printf("%s\n", text); } //断开映射 shmdt(text); //释放共享内存块 shmctl(segment_id, IPC_RMID, 0); return 0; }
问题注意
共享内存使用完,例如每次更新一张图像后.
1每次程序要映射同一个地址的时候,要断开先前的映射,不然在映射的时候会认为地址被占用,重新随机映射分配新地址,造成共享内存不停的再增大.
shmdt("名字");
2最后是用完要释放共享内存
shmctl(segment_id, IPC_RMID, 0);
3 使用 shmget -> shmat -> shmctl 这样一个流程就能描述一个内存从创建到销毁的过程。
(创建)->(映射)-> (断开映射)->(销毁)
测试环境
ubuntu18.05
联想笔记本
工程
1编译
cd bulid
删除清空
cmake ..
make
生成
c++ 发送测试端 send
c++ 接收测试端 client
c++ python和c++的交互动态库 libpython2share.so
同时python的测试样例
python的发送端 py_send.py
python的接收端 py_client.py
2测试样例
测试数据: 共享内存
-1图像1920*1080
-2 gps数组(经度 维度 高度 时间戳)
-3 gps结构体(标志位 字符串说明 经度 维度 高度 时间戳)
2-1 c++ 发送,c++接收
2-2 c++发送,pytho接受
2-3 python发送,c++接受
2-3 python发送,python接收
(如果是单纯的pyton接受,python有自己的共享内存机制,更好用)
参考整理 python共享内存 https://www.cnblogs.com/gooutlook/p/12262304.html
3其他说明
3-1 while不要出现空循环(什么也不做),容易导致一个进程占用cpu,导致其他进程无法获取cpu控制权,无法修改共享内存数据,造成假死状态。在while中加入sleep(0.001)操作。
虽然是多核CPU,从道理上来说不应出现这种问题,但是各种程序测试都是类似问题。
4代码结构
CMakeLists.txt
# cmake needs this line cmake_minimum_required(VERSION 3.1) # Define project name project(opencv_example_project) find_package(OpenCV REQUIRED) message(STATUS "OpenCV library status:") message(STATUS " version: ${OpenCV_VERSION}") message(STATUS " libraries: ${OpenCV_LIBS}") message(STATUS " include path: ${OpenCV_INCLUDE_DIRS}") # Declare the executable target built from your sources add_library(python2share SHARED ApiShare_dll.cpp) #add_executable(send send_example.cpp ApiShare_dll.cpp) #add_executable(client recive_example.cpp ApiShare.cpp) add_executable(send send_example.cpp) add_executable(client recive_example.cpp) # Link your application with OpenCV libraries target_link_libraries(send ${OpenCV_LIBS} ) target_link_libraries(client ${OpenCV_LIBS} ) target_link_libraries(python2share ${OpenCV_LIBS})
C++动态库
ApiShare_dll.cpp
使用前修改以下数据:
1 图像分辨率 默认1920*1080
#define IMAGE_W 1920 //图像宽 #define IMAGE_H 1080 //图像高 #define IMAGE_SIZE IMAGE_W*IMAGE_H*3 //图片像素总大小 3通道彩色
2 共享内存标识,默认一个共享内存地址,未来如果开启多个地址同可修改这个
#define Shm_addrees 1203 //共享内存地址标识
3 共享内存数据结构,未来根据自己需要修改
//共享内存-图像 typedef struct ShareData { int flag; int rows;//图像高 int cols;//图像宽 char imgdata[IMAGE_SIZE];//图像数据一维数据,之前用了cv::Mat不行,因为无法在结构体里初始化大小 float Gps[4];//保存gps信息 经纬高时间戳 }ShareData_;
4 共享内存图像格式默认是char,其中类中图像变量cvoutImg 是用来暂存一个mat类型图像,便于直接访问无需转化才能访问。根据不同的数据修改图像类型
cv::Mat cvoutImg = cv::Mat(IMAGE_H,IMAGE_W,CV_8UC3,cv::Scalar(255, 255, 255));//bufHight,bufWidthl
例如 zed双目相机出来的图格式是CV_8UC4而不是文件中默认的CV_8UC3。所以就要改成
cv::Mat cvoutImg = cv::Mat(IMAGE_H,IMAGE_W,CV_8UC4,cv::Scalar(255, 255, 255));//bufHight,bufWidthl
ApiShare_dll.cpp 完整代码
#ifndef SHARE #define SHARE #include <stdio.h> #include <string.h> #include <unistd.h> #include <stdlib.h> #include <sys/shm.h> #include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp> #include <iostream> #include <vector> #include <string> using namespace std; using namespace cv; //修改共享内存数据 #define IMAGE_W 5472 //图像宽 #define IMAGE_H 3648 //图像高 #define IMAGE_TYPE CV_8UC3 // CV_8UC1 灰度图 CV_8UC3 3通道图像 #define IMAGE_SIZE IMAGE_W*IMAGE_H*3 //图片像素总大小 CV_8UC1--1通道 CV_8UC3--3通道彩色 #define Shm_addrees 1209 //共享内存地址标识 // 所有的返回函数必须有返回值 不然调用报错 namespace MyShare{ //共享内存-图像 typedef struct ShareData { int flag; int rows;//图像高 int cols;//图像宽 char imgdata[IMAGE_SIZE];//图像数据一维数据,之前用了cv::Mat不行,因为无法在结构体里初始化大小 float Gps[4];//保存gps信息 经纬高时间戳 }ShareData_; // 非共享内存-传送gps混合数据 typedef struct StructGpsData { int flag; char *msg; float longitude; float latitude; float high; float time; } ; class Share_class { //变量定义 public: //1创建共享内存 int shmid = shmget((key_t)Shm_addrees, sizeof(ShareData), 0666|IPC_CREAT); //2映射共享内存地址 shm指针记录了起始地址 void *shm = shmat(shmid, (void*)0, 0);//如果创建一个函数每次调用都执行,需要执行完释放一下shmdt //printf("共享内存地址 : %p\n", (int *)(shm)); //2-1以ShareData结构体类型-访问共享内存 ShareData *pShareData= (ShareData*)shm; //用来保存转化后的共享内存的图像结果 cv::Mat cvoutImg = cv::Mat(IMAGE_H,IMAGE_W,IMAGE_TYPE,cv::Scalar(255, 255, 255));//bufHight,bufWidth //未来加速 可以搞一个图像队列 队列大小3 不停存图,然后挨着丢进共享内存,满了就清除。 //函数定义 public: //1初始化执行 Share_class(){ printf("共享内存地址 : %p\n", (int *)(shm)); //存图要先把图像标志位初始给0,这里给会导致接收端调用开启的时候再次给0覆盖了1导致取图失败。 }//1构造函数 //0销毁执行 ~Share_class() { cout<<"析构函数执行"<<endl; DestroyShare(); } //0结束销毁函数 //3销毁共享内存 int DestroyShare(){ //4断开映射 ,保证下次访问不被占用 shmdt(shm); //5释放共享内存地址 shmctl(shmid, IPC_RMID, 0); cout<<"共享内存已经销毁"<<endl; return 1; } /* 函数功能: c++-》c++库-》共享内存 c++ 模式调用, c++ 发送图像一张到共享内存 , 修改图像标志位1,允许c++和pythoN调用接收函数接受图像。 函数输入: cv::Mat Img 要发送的图像 函数输出: pShareData->flag = 1; 图像标志位 Mat cvoutImg 该类图像变量 */ int Send_pic2_share_once(cv::Mat Img){ int i = 0; if(pShareData->flag == 0) { //cv::Mat Img=cv::imread("../data_img/1.jpg",cv::IMREAD_COLOR); if(Img.data== nullptr)//nullptr是c++11新出现的空指针常量 { printf("图片文件不存在\n"); return 0; } else{ // printf("图片文件存在\n"); } //3-1共享内存保存图像宽和高 pShareData->rows =Img.rows; pShareData->cols = Img.cols; //3-2共享内存保存图像数据 int size = Img.cols * Img.rows * Img.channels(); char *cvoutImg = (char*)Img.data; memcpy(pShareData->imgdata, cvoutImg, size); //3-3共享内存保存标志位 pShareData->flag = 1; return 1; } else{ return 0; } //getchar(); } /* 函数功能:python 或 c++-》c++库-》共享内存 python 和 c++ 都可调用 c++ 或python 发送图像一张到共享内存 , 修改图像标志位1,允许c++和pythoN调用接收函数接受图像。 如果是python模式通过c++库调用,可开启图像展示验证图像是否完整 函数输入: uchar *frame_data, 要发送图像的图像数据 Mat img.data int height, 图像高 int width 图像宽 函数输出: pShareData->flag = 1; 图像标志位 Mat cvoutImg 该类图像变量 */ int pySend_pic2_share_once(uchar *frame_data, int height, int width){ if(pShareData->flag == 0) { //assert(height*width*3<=IMAGE_SIZE); if(frame_data== nullptr)//nullptr是c++11新出现的空指针常量 { printf("图片文件不存在\n"); return 0; } else{ // printf("图片文件存在\n"); } pShareData->cols=width; pShareData->rows=height; int size = height*width*3; memcpy(pShareData->imgdata, frame_data, size); //3-3共享内存保存标志位 pShareData->flag = 1; //printf("数据保存成功 %d\n",pShareData->flag); /* //python模式下用来验证发送的图像是否完整。 python-》c++库-》共享内存 int showimg=0; //如果要显示 打开设置1 if(!showimg) return 0; int channel=3; cv::Mat image(height, width, CV_8UC3); for (int row = 0; row < height; row++) { uchar *pxvec = image.ptr<uchar>(row); for (int col = 0; col < width; col++) { for (int c = 0; c < channel; c++) { pxvec[col * channel + c] = frame_data[row * width * channel + channel * col + c]; } } } cv::imshow("image", image); cv::waitKey(3); */ return 1; } else{ return 0; } } /* 函数功能: 共享内存 -> c++库-> c++ C++ 调用 C++从共享内存读取一张图片 修改图像标志位0,允许发送端往共享内存存图。 函数输入: 函数输出: pShareData->flag = 0; 图像标志位 Mat cvoutImg 该类图像变量 */ int Rec_pic2_data_once() { //cv::Mat &cvoutImg_in //3-1共享内存读取标志位 if(pShareData->flag == 1) { //3-2从共享内存获取图像高和宽 int IMAGE_h=pShareData->rows;//从共享内存获取图像高 int IMAGE_w=pShareData->cols;//从共享内存获取图像宽 //3-3共享内存读取图像数据 //cv::Mat cvoutImg = cv::Mat(IMAGE_h,IMAGE_w,CV_8UC3,cv::Scalar(255, 255, 255));//bufHight,bufWidth int size = cvoutImg.cols * cvoutImg.rows * cvoutImg.channels(); memcpy((char*)cvoutImg.data, pShareData->imgdata,size); //cv::imshow("RecData_Show",cvoutImg); //cv::waitKey(1); // printf("数据跟新一次\n"); //3-4共享内存修改标志位 pShareData->flag = 0; return 1; } else{ return 0; } } /* 函数功能: 共享内存 -> c++库-> python 或 c++ python 调用(C++用Rec_pic2_data_once) 主要是给python用获取共享内存的图像 python从共享内存读取一张图片 修改图像标志位0,允许发送端往共享内存存图。 函数输入: 调用前先用c++端更新共享内存最新的图像 函数输出: pShareData->flag = 0; 图像标志位 (uchar*)cvoutImg.data 该类图像变量cvoutImg的数据data指针 */ uchar* Img_Cgg2py(){ //uchar* frame_data, int rows, int cols, int channels //将共享内存现有的图像数据发送 if(pShareData->flag == 1){ // cvoutImg=cv::imread("/home/dongdong/3Code/python2c/1/c++2c++/v4_c++_class_python/img_data/00001.jpg"); if(cvoutImg.data== nullptr)//nullptr是c++11新出现的空指针常量 { printf("图片文件不存在\n"); return 0; } else{ // printf("图片文件存在\n"); } //pShareData->flag = 0; //等python完成数组转化到图像,在python端完成标志位0 return (uchar*)pShareData->imgdata;//这里只试穿了一个数组,瞬间完成 // pShareData->flag = 0; //等python完成数组转化到图像,在python端完成标志位0 } //重新建立新的数据发送模式 /* Mat image =cv::imread("/home/dongdong/3Code/python2c/1/c++2c++/v4_c++_class_python/img_data/00001.jpg"); if (!image.empty()) { //cout<< "cgg2py new pic"<<endl; //cv::imshow("cgg", image); //cv::waitKey(0); int rows = image.rows; int cols = image.cols; int channels = image.channels(); // printf("rows = %d cols = %d channels = %d size = %d\n", rows, cols, channels, rows*cols*channels); uchar *data = (uchar*)malloc(sizeof(uchar) * rows * cols * channels); memcpy(data, image.data, rows * cols * channels); return data; } */ } /* 函数功能: 共享内存 -> c++库-> python 或 c++ python 调用(C++直接通过类变量引用) 主要是给python通过函数方式用获取共享内存的int flag数据 获取图像标志位,用于判断是否可以读写 函数输入: 函数输出: pShareData->flag = 0 or 1; 图像标志位 */ //4-1获取图像保存标志位 int Get_ImgFlag(){ return pShareData->flag ; } /* 函数功能: 共享内存 -> c++库-> python 或 c++ python 调用(C++直接通过类变量引用) 主要是给python通过函数方式用修改共享内存的int flag数据 设置图像标志位,用于开启是否可以读写 函数输入: int value pythoN需要将数据转化为 ctypes.c_int 送进来 默认int不需要 函数输出: pShareData->flag = 0 or 1; 图像标志位 pythoN需要将数据转化为 ctypes.c_int 接受 默认int不需要 */ int Set_ImgFalg(int value){ pShareData->flag =value; } /* 函数功能: python send -> c++库 -> 共享内存 -> c++库-> python rec python 调用 (c++端直接通过类的变量引用就可以) python 修改共享内存中的gps数据 函数输入: float *data python的数组索引 和 类型 int len python的数组长度 函数输出: float result 操作结果 可不使用(python接收端需要指明接收数据类型 c float指针 ctypes.POINTER(ctypes.c_float) ) */ //5传输数组 接受py数组并修改python原数组,返回总和结果 float pyarr_set_cgg_gps_share(float *data, int len) { float result=1; for (int i = 0; i < len; i++) { pShareData->Gps[i]=data[i] ; } return result; } /* 函数功能: python -> c++库 -> 共享内存 -> c++库-> python python 调用 (c++端直接通过类的变量引用就可以) python 获取共享内存中的gps数据 函数输入: float pShareData->Gps[4] C++ 共享内存结构体pShareData中的GPS数据 函数输出: (uchar*) pShareData->Gps; (python接收端需要指明接收数据类型 c float指针 ctypes.POINTER(ctypes.c_float) ) */ uchar* py_get_cgg_gps_share(){ // c++发送端调用其他函数更新GPS数据,保存在共内存(简单举例) //pShareData->Gps[0]=1.56; //pShareData->Gps[1]=2.34; //pShareData->Gps[2]=3.14; //pShareData->Gps[3]=4.78; return (uchar*) pShareData->Gps; //返回指针 } /* 函数功能: python -> c++库 -> 共享内存 -> c++库-> python python 调用 (c++端直接通过类的变量引用就可以) python 获取共享内存中的gps数据 python传送过来一个结构体,修改pytho原结构体返回 函数输入: float pShareData->Gps[4] C++ 共享内存结构体pShareData中的GPS数据 StructGpsData gps C++的结构体- python需要将对应的python结构体输入 函数输出: StructGpsData gps (python接收端需要指明接收数据类型 ) */ StructGpsData py_get_cgg_gps_Struct( StructGpsData gps){ // c++发送端调用其他函数更新GPS数据,保存在共内存(简单举例) //pShareData->Gps[0]=1.56; //pShareData->Gps[1]=2.34; //pShareData->Gps[2]=3.14; //pShareData->Gps[3]=4.78; //共享内存数据更新gps数据 gps.flag=1; gps.msg="new share data from c++ share"; gps.longitude=pShareData->Gps[0]; gps.latitude=pShareData->Gps[1]; gps.high=pShareData->Gps[2]; gps.time=pShareData->Gps[3]; return gps; } /* 函数功能: python -> c++库 -> 共享内存 python 调用 (c++端直接通过类的变量引用就可以) python 修改共享内存中的gps数据 python传送过来一个结构体,修改C++原结构体 函数输入: float pShareData->Gps[4] C++ 共享内存结构体pShareData中的GPS数据 StructGpsData gps C++的结构体- python需要将对应的python结构体输入 函数输出: StructGpsData gps (python接收端需要指明接收数据类型 ) */ StructGpsData py_Set_cgg_gps_Struct( StructGpsData gps){ // c++发送端调用其他函数更新GPS数据,保存在共内存(简单举例) gps.flag=1; gps.msg="new share have set share c++"; pShareData->Gps[0]=gps.longitude; pShareData->Gps[1]=gps.latitude; pShareData->Gps[2]= gps.high; pShareData->Gps[3]=gps.time; return gps; } };//类定义结束 }//namespace 定义 //按照C语言格式重新打包-python调用 //如果调用端开启了线程 这部份无法直接访问到 需要在线程里面重新创建这个类 //如果是单纯的C++调用,不需要这个样子封装 extern "C" { MyShare::Share_class useShare; int DestroyShare_(){ useShare.DestroyShare(); } int Send_pic2_share_once_(cv::Mat Img){ useShare.Send_pic2_share_once(Img); } int pySend_pic2_share_once_(uchar *frame_data, int height, int width){ useShare.pySend_pic2_share_once(frame_data, height, width); } int Rec_pic2_data_once_(){ useShare.Rec_pic2_data_once(); } uchar* Img_Cgg2py_(){ useShare.Img_Cgg2py(); } int Get_ImgFlag_(){ useShare.Get_ImgFlag(); } int Set_ImgFalg_(int value){ useShare.Set_ImgFalg(value); } float pyarr_set_cgg_gps_share_(float *data, int len) { useShare.pyarr_set_cgg_gps_share( data, len); } uchar* py_get_cgg_gps_share_(){ useShare.py_get_cgg_gps_share(); } MyShare::StructGpsData py_get_cgg_gps_Struct_( MyShare::StructGpsData gps){ return useShare.py_get_cgg_gps_Struct(gps); } MyShare::StructGpsData py_Set_cgg_gps_Struct_(MyShare::StructGpsData gps){ return useShare.py_Set_cgg_gps_Struct(gps); } } #endif
c++发送端
send_example.cpp
#include <stdio.h> #include <string.h> #include <unistd.h> #include <stdlib.h> #include <sys/shm.h> #include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp> #include <iostream> #include <vector> #include <string> #include "ApiShare_dll.cpp" using namespace std; int main() { //1创建对象 可以不创建 调用文件自动有一个 //1-1如果调用端在新的线程调用了,需要在新的线程创建这个类 //1-2如果不用python库编译 可以注销了C格式的类封装 每次调用临时创建 //MyShare::Share_class useShare; //头文件c++封装c函数时候已经创建了 //2-1发送数据-读取数据 cv::Mat Img=cv::imread("00001.JPG",cv::IMREAD_COLOR); //cv::imshow("RecData_Show",Img); //char key=cv::waitKey(0); //2-2修改标志位--用来控制读取同步 useShare.pShareData->flag = 0; //提醒。第一次初始化强制给0,清除上次程序残留结果,类初始化已经给0了 //3发送图像 while (1){ sleep(0.001);//防止绝对的空循环导致CPu占用,其他进程无法修改共享内存 //发送gps-C++直接访问类变量发送修改 if( useShare.pShareData->flag ==0){ useShare.pShareData->Gps[0]=32.3; useShare.pShareData->Gps[1]=12.3; useShare.pShareData->Gps[2]=72.3; useShare.pShareData->Gps[3]=12.3; } if( useShare.pShareData->flag ==0){//读取完毕,允许存图 // 发送图像-C++模式 T256相机通用模式 输入Mat 类型图像 //useShare.Send_pic2_share_once(Img);//发送一张图像 //发送图像-C++和python模式 注意图像格式为 uchar* 数组 有的数据例如 T265图像就用不了 useShare.pySend_pic2_share_once((uchar*)Img.data,Img.rows,Img.cols);//发送一张图 useShare.pShareData->flag =1;//存储完毕,允许读图 } if(useShare.pShareData->flag ==3){ //接收端读取成功并且将标志位修改成3 关闭 break; } } //销毁 //useShare.DestroyShare(); //销毁共享内存 类释放时候会自动销毁,这里做个提醒 }
C++接收端
recive_example.cpp
#include <stdio.h> #include <string.h> #include <unistd.h> #include <stdlib.h> #include <sys/shm.h> #include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp> #include <iostream> #include <vector> #include <string> #include "ApiShare_dll.cpp" using namespace std; int main() { //1创建对象 //1-1如果调用端在新的线程调用了,需要在新的线程创建这个类 //1-2如果不用python库编译 可以注销了C格式的类封装 每次调用临时创建 //MyShare::Share_class useShare; //2创建接受图像 cv::Mat RecImg = cv::Mat(IMAGE_H,IMAGE_W,IMAGE_TYPE,cv::Scalar(255, 255, 255));//bufHight,bufWidth cv::namedWindow("RecData_Show",0); //3接受GPS数据 float Gps_c[4]; //3接受图像 while (1){ sleep(0.001);//防止绝对的空循环导致CPu占用,其他进程无法修改共享内存 //接受GPS if( useShare.pShareData->flag ==1){ Gps_c[0]=useShare.pShareData->Gps[0]; Gps_c[1]=useShare.pShareData->Gps[1]; Gps_c[2]=useShare.pShareData->Gps[2]; Gps_c[3]=useShare.pShareData->Gps[3]; for(int i=0;i<=3;i++){cout<< "GPS_DATA"<<i <<" "<<Gps_c[i] <<" ";} cout<<endl; } //接受图像 if( useShare.pShareData->flag ==1){//存储完毕,允许读图 useShare.Rec_pic2_data_once();//接受一张图像自动保存在useShare.cvoutImg中,然后修改标志位 RecImg=useShare.cvoutImg; useShare.pShareData->flag =0;//读取完毕,允许存图 } cv::imshow("RecData_Show",RecImg); char key=cv::waitKey(1); if(key=='q'){ useShare.pShareData->flag=3; printf("按键退出"); break;} } //销毁 //useShare.DestroyShare(); //销毁共享内存 类释放自动销毁 return 0; }
python发送端
py_send.py
import numpy as np import time import ctypes import os import cv2 #更多类型参看 https://docs.python.org/3/library/ctypes.html # c_bool c_char c_int c_long c_float c_double # c_char_p c_wchar_p c_void_p #1载入库 libLoad = ctypes.cdll.LoadLibrary sharelib = libLoad("./build/libpython2share.so") #--------------0-发送单个int 初始第一次设置为0 允许开始存图------------------------ sharelib.Set_ImgFalg_.argtype = ctypes.c_int # 设置函数输入值类型 int可忽略 #sharelib.Set_ImgFalg_(0) #修改c++类的某一个变量(py只能通过函数而非直接访问修改) sharelib.Get_ImgFlag_.restype = ctypes.c_int # 设置返回值类型 int可忽略 #reuslt=sharelib.Get_ImgFlag_()#获取变量的结果 #--------------------2-1 数组修改GPS ---------------- def py_Arr_GPS(pyarray): sharelib.pyarr_set_cgg_gps_share_.restype = ctypes.c_float #定义函数返回值类型 #pyarray = [1., 2., 3., 4.] #创建py数组 carray2cgg = (ctypes.c_float * len(pyarray))(*pyarray) #转化成c_float指针 reuls_sum=sharelib.pyarr_set_cgg_gps_share_(carray2cgg, len(pyarray)) #调用查询 size=(1,4) #将获取的数组变成 1*3矩阵 pose = np.reshape(np.array(np.fromiter(carray2cgg, np.float64, size[0]*size[1])), size) print("c++修改后返回,python n*n维度数组:\n",pose) #--------------------2-2 结构体修改GPS ---------------- #python创建结构体 class py_Struct_GPS(ctypes.Structure): _fields_ = [ ('flag', ctypes.c_int), #c++ int ('msg', ctypes.c_char_p), #c++ char* ('longitude', ctypes.c_float), # c++ float ('latitude', ctypes.c_float),# c++ float ('high', ctypes.c_float),# c++ float ('time', ctypes.c_float)# c++ float ] #c++ char*[] #python结构体赋予初值 struct_gps=py_Struct_GPS() struct_gps.flag=ctypes.c_int(0) struct_gps.msg=ctypes.c_char_p('GPS DATA'.encode()) struct_gps.longitude=ctypes.c_float(0.) struct_gps.latitude=ctypes.c_float(0.) struct_gps.high=ctypes.c_float(0.) struct_gps.time=ctypes.c_float(0.) print("结构体gps_old \n",struct_gps) print(struct_gps.flag,struct_gps.msg.decode(),struct_gps.longitude,struct_gps.latitude,struct_gps.high,struct_gps.time) #0 GPS DATA 0.0 0.0 0.0 0.0 def SetGPSToShare_struct(struct_gps,flag,msg,longitude,latitude,high,time): struct_gps.flag=ctypes.c_int(flag) struct_gps.msg=ctypes.c_char_p(msg.encode()) struct_gps.longitude=ctypes.c_float(longitude) struct_gps.latitude=ctypes.c_float(latitude) struct_gps.high=ctypes.c_float(high) struct_gps.time=ctypes.c_float(time) #从c++获取数据-数据保存在 共享内存或C++直接生成 # 定义返回类型为结构体类型 sharelib.py_Set_cgg_gps_Struct_.restype = py_Struct_GPS # 调用获取gps数据-返回数据结构体 struct_gps= sharelib.py_Set_cgg_gps_Struct_(struct_gps) #print(struct_gps.flag) print(struct_gps.msg.decode()) #必须解码 #print("结构体gps_new \n",struct_gps) #print(struct_gps.flag,struct_gps.msg.decode(),struct_gps.longitude,struct_gps.latitude,struct_gps.high,struct_gps.time) #1 new data 1.559999942779541 2.559999942779541 3.559999942779541 5.559999942779541 #--------------------2-2 结构体拿GPS ---------------- #3发送图像 sharelib.Set_ImgFalg_(0) #允许存图 pyimage = cv2.imread("img_data/00001.jpg")#读取原始图像 #cv2.imshow("1 ", pyimage) #cv2.waitKey(0) def SendImgFromShare(image): image_data = np.asarray(image, dtype=np.uint8)#图像转化成数组 image_data = image_data.ctypes.data_as(ctypes.c_char_p)#数组转化成C指针一维数组 sharelib.pySend_pic2_share_once_(image_data,image.shape[0],image.shape[1])#发送到共享内存 while True: time.sleep(0.001)#防止绝对的空循环导致CPu占用,其他进程无法修改共享内存 #发送gps数据-数组模式 gps_p=[1.31, 3.42, 2.41, 6.41] py_Arr_GPS(gps_p) #发送gps数据-结构体模式 #SetGPSToShare_struct(struct_gps,0,"set gps to share",11.034,145.565,80.0,121314) #发送图像数据 if sharelib.Get_ImgFlag_()==0:#读图结束,允许存图 SendImgFromShare(pyimage)#发送图像 sharelib.Set_ImgFalg_(1)#存图结束,允许读图 if sharelib.Get_ImgFlag_()==3: print("接收端停止") break
c++接收端
py_client.py
import numpy as np import time import ctypes import os import cv2 #更多类型参看 https://docs.python.org/3/library/ctypes.html # c_bool c_char c_int c_long c_float c_double # c_char_p c_wchar_p c_void_p #1载入库 libLoad = ctypes.cdll.LoadLibrary sharelib = libLoad("./build/libpython2share.so") #--------------0-发送单个int 初始第一次设置为0 允许开始存图------------------------ sharelib.Set_ImgFalg_.argtype = ctypes.c_int # 设置初始值类型 #sharelib.Set_ImgFalg_(0) #修改c++类的某一个变量(py只能通过函数而非直接访问修改) sharelib.Get_ImgFlag_.restype = ctypes.c_int # 设置返回值类型 #reuslt=sharelib.Get_ImgFlag_()#获取变量的结果 # -----------------2-1 获取gps数组---------------- # 设置输出数据类型为uint8的指针 def ReadGPSFromShare(): sharelib.py_get_cgg_gps_share_.restype =ctypes.POINTER(ctypes.c_float) #c_char_p #获取输出图像数据指针 pointer_f_gps= sharelib.py_get_cgg_gps_share_() size=(1,4) #数据转化成图像 gps = np.reshape(np.array(np.fromiter(pointer_f_gps, dtype=np.float, count=size[0]*size[1])) ,size) print("数组模式获取的GPS:",gps) return gps # -----------------2-1 获取gps数组结束---------------- #--------------------2-2 结构体拿GPS ---------------- #python创建结构体 class py_Struct_GPS(ctypes.Structure): _fields_ = [ ('flag', ctypes.c_int), #c++ int ('msg', ctypes.c_char_p), #c++ char* ('longitude', ctypes.c_float), # c++ float ('latitude', ctypes.c_float),# c++ float ('high', ctypes.c_float),# c++ float ('time', ctypes.c_float)# c++ float ] #c++ char*[] #python结构体赋予初值 struct_gps=py_Struct_GPS() struct_gps.flag=ctypes.c_int(0) struct_gps.msg=ctypes.c_char_p('GPS DATA'.encode()) struct_gps.longitude=ctypes.c_float(0.) struct_gps.latitude=ctypes.c_float(0.) struct_gps.high=ctypes.c_float(0.) struct_gps.time=ctypes.c_float(0.) print("结构体gps_old \n",struct_gps) print(struct_gps.flag,struct_gps.msg.decode(),struct_gps.longitude,struct_gps.latitude,struct_gps.high,struct_gps.time) #0 GPS DATA 0.0 0.0 0.0 0.0 #从c++获取数据-数据保存在 共享内存或C++直接生成 # 定义返回类型为结构体类型 sharelib.py_get_cgg_gps_Struct_.restype = py_Struct_GPS # 调用获取gps数据-返回数据结构体 struct_gps= sharelib.py_get_cgg_gps_Struct_(struct_gps) #print(struct_gps.flag) #print(struct_gps.msg.decode()) #必须解码 print("结构体gps_new \n",struct_gps) print(struct_gps.flag,struct_gps.msg.decode(),struct_gps.longitude,struct_gps.latitude,struct_gps.high,struct_gps.time) #1 new data 1.559999942779541 2.559999942779541 3.559999942779541 5.559999942779541 #--------------------2-2 结构体拿GPS ---------------- # -----------------3python read cgg 读取图像------------------- def ReadImgFromShare(): # 设置输出数据类型为uint8的指针 sharelib.Img_Cgg2py_.restype =ctypes.POINTER(ctypes.c_uint8) #c_char_p #获取输出图像数据指针 pointer_img = sharelib.Img_Cgg2py_() size=(1080,1920,3) #数据转化成图像 RecImg = np.reshape(np.array(np.fromiter(pointer_img, dtype=np.uint8, count=size[0]*size[1]*size[2])) ,size) ''' print(RecImg.shape)#1920*1080*3 print(RecImg) if RecImg is not None: #展示图像 #cv2.namedWindow("showimg",0) cv2.imshow("showimg ", RecImg) cv2.waitKey(1) #return RecImg else: return 1 ''' return RecImg while True: time.sleep(0.001)#防止绝对的空循环导致CPu占用,其他进程无法修改共享内存 if sharelib.Get_ImgFlag_()==1:#存图结束,允许读图 #方法1-直接访问共享内存接受GPS数据 PyArr1x4Gps=ReadGPSFromShare()#获取GPS print("python直接访问共享内存GPS数据索引:\n",PyArr1x4Gps[0][0],PyArr1x4Gps[0][1],PyArr1x4Gps[0][2],PyArr1x4Gps[0][3]) #方法2-通过结构体访问共享内存接受GPS数据 # 调用获取gps数据-返回数据结构体 struct_gps= sharelib.py_get_cgg_gps_Struct_(struct_gps) #print("python通过结构体获取共享内存GPS数据:\n",struct_gps.flag,struct_gps.msg.decode(),struct_gps.longitude,struct_gps.latitude,struct_gps.high,struct_gps.time) #接受图像 Py_img=ReadImgFromShare() #展示图像 cv2.namedWindow("showimg1",0) cv2.imshow("showimg1 ", Py_img) cv2.waitKey(1) sharelib.Set_ImgFalg_(0)#读图结束,允许存图 if sharelib.Get_ImgFlag_()==3: break