本文主要用於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; namespace MyShare{ #define Shm_addrees 1203 //共享內存地址標識 #define IMAGE_W 1920 //圖像寬 #define IMAGE_H 1080 //圖像高 #define IMAGE_SIZE IMAGE_W*IMAGE_H*3 //圖片像素總大小 3通道彩色 //共享內存-圖像 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,CV_8UC3,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; } /* 函數功能: 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; } //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); */ } } /* 函數功能: 共享內存 -> 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; } } /* 函數功能: 共享內存 -> 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調用 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創建對象 可以不創建 調用文件自動有一個 //MyShare::Share_class useShare; //頭文件c++封裝c函數時候已經創建了 //2-1發送數據-讀取數據 cv::Mat Img=cv::imread("/home/dongdong/3Code/python2c/1/c++2c++/v4_c++_class_python/img_data/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++模式 //useShare.Send_pic2_share_once(Img);//發送一張圖像 //發送圖像-C++和python模式 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創建對象 //MyShare::Share_class useShare; //2創建接受圖像 cv::Mat RecImg = cv::Mat(676,1201,CV_8UC3,cv::Scalar(255, 255, 255));//bufHight,bufWidth //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