经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » C++ » 查看文章
混合编程python与C++
来源:cnblogs  作者:Rurouni  时间:2023/6/8 9:26:25  对本文有异议

上个版本: 只是用到ctypes进行传输, 这次将python服务端更改为C++服务端,方便后续维护.
本文实现功能: python传输图片给C++, C++接受图片后对图片进行处理,并将结果返回给python客户端, pass image from python to C++

C++ 服务端

.h文件

注意文中的model

  1. // .h
  2. #pragma once
  3. #include <stdio.h>
  4. #include <stdlib.h>
  5. #include <string.h>
  6. #include <unistd.h>
  7. #include <sys/socket.h>
  8. #include <arpa/inet.h>
  9. #include <netinet/in.h>
  10. #include <signal.h>
  11. #include <opencv2/opencv.hpp>
  12. using namespace std;
  13. using namespace cv;
  14. class ModelManager;
  15. class ServerManager
  16. {
  17. private:
  18. int m_port;
  19. char *m_addr;
  20. cv::VideoCapture m_cap;
  21. int m_server;
  22. int m_accept; // client conn
  23. public:
  24. bool initialization(const int &port, const cv::VideoCapture &cap, char *addr = nullptr);
  25. bool initialization(const int &port, char *addr = nullptr);
  26. bool build_connect();
  27. bool acceptClient();
  28. void error_print(const char *ptr);
  29. bool free_connect();
  30. bool send_data_frame(ModelManager& model);
  31. bool receive_data_frame(cv::Mat &frame, ModelManager& model);
  32. };

.cpp文件

  1. #include "ServerManager.h"
  2. #include "ModelManager.h"
  3. #define BUFFER_SIZE 65538
  4. void ServerManager::error_print(const char * ptr) {
  5. perror(ptr);
  6. exit(EXIT_FAILURE);
  7. }
  8. bool ServerManager::initialization(const int& port, const cv::VideoCapture& cap, char* addr){
  9. m_port = htons(port);
  10. m_addr = addr;
  11. m_cap = cap;
  12. return true;
  13. }
  14. bool ServerManager::initialization(const int& port, char* addr){
  15. m_port = htons(port);
  16. m_addr = addr;
  17. return true;
  18. }
  19. bool ServerManager::build_connect() {
  20. struct sockaddr_in server_addr;
  21. bzero(&server_addr,sizeof(server_addr));
  22. server_addr.sin_family = AF_INET;
  23. server_addr.sin_addr.s_addr = m_addr?inet_addr(m_addr):INADDR_ANY;
  24. server_addr.sin_port = m_port;
  25. // create socket
  26. m_server = socket(AF_INET, SOCK_STREAM, 0);
  27. if(m_server < 0)
  28. error_print("socket bind error");
  29. // can reuse port
  30. int on = 1;
  31. if(setsockopt(m_server,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on)) < 0)
  32. error_print("setsockopt error");
  33. // bind addr
  34. if(bind(m_server, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0)
  35. error_print("bind error");
  36. // listen only one client
  37. if(listen(m_server, 1) < 0)
  38. error_print("listen failed");
  39. cout << "ServerManager is listening, plesae wait..." << endl;
  40. return true;
  41. }
  42. bool ServerManager::acceptClient(){
  43. struct sockaddr_in accept_addr;
  44. socklen_t accept_len = sizeof(accept_addr);
  45. bzero(&accept_addr,sizeof(accept_addr));
  46. // accept client connection
  47. if((m_accept = accept(m_server,(struct sockaddr*)&accept_addr,&accept_len)) < 0)
  48. error_print("accept error");
  49. std::cout << "Connection established" << std::endl;
  50. return true;
  51. }
  52. bool ServerManager::send_data_frame(ModelManager& model) {
  53. char *json_output = nullptr;
  54. json_output = model.createJson();
  55. if (json_output == nullptr) {
  56. return false;
  57. }
  58. // printf("send data %s\n", json_output);
  59. // just send json_output, dont memcpy new char*!!! it wastes me two hours
  60. // send json
  61. int result = send(m_accept, json_output, strlen(json_output), 0);
  62. if (result == -1) {
  63. cout << "send fail" << endl;
  64. return false;
  65. }
  66. return true;
  67. }
  68. bool ServerManager::receive_data_frame(Mat& frame, ModelManager& model) {
  69. // recv frame size
  70. int data_size;
  71. if (recv(m_accept, &data_size, sizeof(data_size), 0) != sizeof(data_size)) {
  72. // when client close, then close connection
  73. close(m_accept);
  74. cout << "close connection to client" << endl;
  75. acceptClient(); // restart a new accept, to accept new connection
  76. return false;
  77. }
  78. cout << data_size << endl;
  79. // recv frame data
  80. // char buf[data_size];
  81. // std::vector<uchar> decode;
  82. // int bytes_received = 0;
  83. // do
  84. // {
  85. // int nBytes = recv(m_accept, buf, data_size - bytes_received, 0);
  86. // for (int i = 0; i < nBytes; i++) // maybe can use memcpy, maybe faster
  87. // {
  88. // decode.emplace_back(buf[i]);
  89. // }
  90. // cout << bytes_received << endl;
  91. // bytes_received += nBytes;
  92. // } while (bytes_received < data_size);
  93. char *recv_char = new char[data_size];
  94. std::vector<uchar> decode(data_size, 0);
  95. int index = 0;
  96. int bytes_received = 0;
  97. int count = data_size;
  98. while (count > 0)// if count >= 0, dead loop
  99. {
  100. int iRet = recv(m_accept, recv_char, count, 0);
  101. if (index >= data_size) index = data_size;
  102. memcpy(&decode[index], recv_char , iRet);
  103. index += iRet;
  104. if (!iRet) { return -1; }
  105. count -= iRet;
  106. }
  107. // decode message
  108. frame = imdecode(decode, cv::IMREAD_COLOR);
  109. // push into Model's queueMat
  110. model.mtxQueueImg.lock();
  111. model.queueMat.push(frame);
  112. model.mtxQueueImg.unlock();
  113. return true;
  114. }
  115. bool ServerManager::free_connect() {
  116. m_cap.release();
  117. close(m_accept);
  118. close(m_server);
  119. return true;
  120. }

C++ model部分代码

.h文件

  1. #pragma once
  2. #include "CV_Classify.h"
  3. #include "CV_Detect.h"
  4. #include "ServerManager.h"
  5. #include <opencv2/opencv.hpp>
  6. #include <mutex>
  7. #include <queue>
  8. #include <unistd.h> // usleep
  9. #include <thread>
  10. #include "cJSON.h"
  11. #include <string>
  12. using namespace std;
  13. using namespace cv;
  14. class ModelManager{
  15. public:
  16. Detect objdetect;
  17. Classify objclassify;
  18. std::mutex mtxQueueDet; // mutex for detect queue
  19. std::mutex mtxQueueImg; // mutex for image queue
  20. std::mutex mtxQueueCls; // mutex for classify queue
  21. std::queue<cv::Mat> queueMat;
  22. std::queue<ObjDetectOutput> queueDetOut;// Detect queue
  23. std::queue<ObjClassifyOutput> queueClsOut;// Classify queue
  24. bool DetectFlag = true;
  25. bool ClassifyFlag = true;
  26. bool empty_flag = false;
  27. friend class ServerManager;
  28. public:
  29. void initDetectModel() ;
  30. void initClassifyModel() ;
  31. void DetectImg();
  32. void ClassifyImg();
  33. void getClsResult(ObjClassifyOutput &output);
  34. // ObjClassifyOutput getClsResult();
  35. char* createJson();
  36. };

.cpp文件

部分有删减,createJson可参考使用,利用json来传递值

  1. #include "ModelManager.h"
  2. void ModelManager::initDetectModel()
  3. {
  4. std::string config_path = "DetectConfig.yaml";
  5. objdetect.Init(config_path, 1);
  6. }
  7. void ModelManager::initClassifyModel()
  8. {
  9. std::string config_path = "ClassiflyConfig.yaml";
  10. objclassify.Init(config_path, 1);
  11. }
  12. void ModelManager::DetectImg()
  13. {
  14. DetectInput detect_input;
  15. DetectOutput detect_output;
  16. cv::Mat frame;
  17. size_t mm = 0;
  18. while(1)
  19. {
  20. if (queueMat.empty())
  21. {
  22. if(!DetectFlag)
  23. {
  24. break;
  25. }
  26. usleep(2000);
  27. continue;
  28. }
  29. // get image from queueMat
  30. mtxQueueImg.lock();
  31. frame = queueMat.front();
  32. queueMat.pop();
  33. mtxQueueImg.unlock();
  34. // run model
  35. objdetect.Run(detect_input, detect_output);
  36. // push detect result into queueDetOut
  37. mtxQueueDet.lock();
  38. queueDetOut.push(detect_output);
  39. // cout << "detect run !!" << endl;
  40. mtxQueueDet.unlock();
  41. }
  42. return;
  43. }
  44. void ModelManager::ClassifyImg()
  45. {
  46. ObjClassifyInput input;
  47. ObjClassifyOutput output;
  48. cv::Mat frame;
  49. Detoutput detect_result;
  50. while(1)
  51. {
  52. if (queueDetOut.empty())
  53. {
  54. if(!ClassifyFlag)
  55. {
  56. break;
  57. }
  58. usleep(2000);
  59. continue;
  60. }
  61. // get detect from queueDetOut
  62. mtxQueueDet.lock();
  63. detect_result = queueDetOut.front();
  64. queueDetOut.pop();
  65. mtxQueueDet.unlock();
  66. // run model
  67. objclassify.Run(input, output);
  68. // push cls result into queueClsOut
  69. mtxQueueCls.lock();
  70. queueClsOut.push(output);
  71. mtxQueueCls.unlock();
  72. }
  73. return;
  74. }
  75. void ModelManager::getClsResult(ObjClassifyOutput& output){
  76. if (queueClsOut.empty()){
  77. output.object_list.object_num = -1; // -1 is now empty;
  78. return; // must return in thread otherwise cant use &output
  79. }
  80. output = queueClsOut.front();
  81. queueClsOut.pop();
  82. return;
  83. }
  84. char* ModelManager::createJson() // dont know why cant use &value, need return value
  85. {
  86. mtxQueueCls.lock();
  87. ObjClassifyOutput output;
  88. getClsResult(output);
  89. mtxQueueCls.unlock();
  90. if (output.object_list.object_num == -1){
  91. return nullptr;
  92. }
  93. // prepare send data json
  94. cJSON* json_object_list = NULL;
  95. cJSON* json_ObjClassifyOutput = NULL;
  96. json_ObjClassifyOutput = cJSON_CreateObject();
  97. json_object_list = cJSON_CreateObject();
  98. cJSON_AddItemToObject(json_ObjClassifyOutput, "object_list", json_object_list);
  99. int obj_num = output.object_list.object_num;
  100. cJSON_AddNumberToObject(json_object_list, "object_num", obj_num);
  101. for (int i = 0; i < obj_num; ++i){
  102. cJSON* json_object = cJSON_CreateObject();
  103. cJSON* json_box = cJSON_CreateObject();
  104. cJSON_AddNumberToObject(json_box,"x", output.object_list.object[i].bbox.x);
  105. cJSON_AddNumberToObject(json_box,"y", output.object_list.object[i].bbox.y);
  106. cJSON_AddNumberToObject(json_box,"w", output.object_list.object[i].bbox.w);
  107. cJSON_AddNumberToObject(json_box,"h", output.object_list.object[i].bbox.h);
  108. cJSON_AddItemToObject(json_object,"bbox", json_box);
  109. cJSON_AddNumberToObject(json_object, "classes", output.object_list.object[i].classes);
  110. cJSON_AddNumberToObject(json_object, "objectness", output.object_list.object[i].objectness);
  111. // double prob = output.object_list.object[i].prob;
  112. // cJSON_AddNumberToObject(json_object, "prob", prob); // pointer cant use?
  113. string str = "object" + to_string(i);
  114. cJSON_AddItemToObject(json_object_list, str.c_str(), json_object);
  115. // printf("prob: %f", output.object_list.object[i].prob);
  116. }
  117. char* json_output = cJSON_Print(json_ObjClassifyOutput);
  118. cJSON_Delete(json_ObjClassifyOutput);
  119. return json_output;
  120. }

C++ 服务端运行

  1. #include <../include/ServerManager.h>
  2. #include <../include/ModelManager.h>
  3. #include <thread>
  4. #define PORT 8080
  5. void recvServer(ServerManager& s, ModelManager& model){
  6. int idx = 0;
  7. while (true){
  8. // auto start = std::chrono::steady_clock::now();
  9. cv::Mat frame;
  10. s.receive_data_frame(frame, model);
  11. // cal time cost
  12. // auto end = std::chrono::steady_clock::now();
  13. // std::chrono::duration<double, std::milli> elapsed = end - start;
  14. // std::cout << "recv execution time: " << elapsed.count() << " ms\n";
  15. if (frame.empty()) {
  16. usleep(2000);
  17. continue;
  18. }
  19. // cv::imwrite("image"+to_string(idx++)+".jpg", frame);
  20. std::cout << "Image " << idx++ <<" received !!" << std::endl;
  21. }
  22. }
  23. void sendServer(ServerManager& s, ModelManager& model){
  24. while (true){
  25. if (s.send_data_frame(model)) {
  26. cout << "send success!!" << endl;
  27. cout << endl;
  28. }else{
  29. // cout << "send fail!!" << endl;
  30. usleep(2000);
  31. }
  32. }
  33. }
  34. int main()
  35. {
  36. ServerManager s;
  37. ModelManager model;
  38. model.initDetectModel();
  39. model.initClassifyModel();
  40. cout << endl;
  41. s.initialization(PORT);
  42. s.build_connect();
  43. s.acceptClient();
  44. thread recv_server(recvServer, std::ref(s), std::ref(model));
  45. thread send_server(sendServer, std::ref(s), std::ref(model));
  46. thread detect(&ModelManager::DetectImg, &model);
  47. thread classfy(&ModelManager::ClassifyImg, &model);
  48. detect.join();
  49. classfy.join();
  50. recv_server.join();
  51. send_server.join();
  52. return 0;
  53. }

python客户端

  1. import json
  2. import socket
  3. import struct
  4. import time
  5. from multiprocessing import JoinableQueue
  6. from threading import Thread
  7. import os
  8. from natsort import ns, natsorted
  9. host = '192.168.0.2' # '192.168.0.2' 'localhost'
  10. port = 8080
  11. def img_encode(img_path):
  12. img = cv2.imread(img_path)
  13. # img = cv2.resize(img, (500, 500), interpolation=cv2.INTER_CUBIC)
  14. img_param = [95] # 图片压缩率0-100
  15. _, img = cv2.imencode('.jpg', img, img_param)
  16. img = img.tobytes()
  17. return img
  18. def img_product(img_queue, path, path_mode='image'):
  19. if path_mode == 'image':
  20. image = img_encode(path)
  21. img_queue.put(image)
  22. elif path_mode == 'dir':
  23. dir_list = os.listdir(path)
  24. files = natsorted(dir_list, alg=ns.PATH) # 顺序读取文件名
  25. for filename in files:
  26. img_path = path + '/' + filename
  27. image = img_encode(img_path)
  28. img_queue.put(image)
  29. img_queue.put('E')
  30. img_queue.join()
  31. def server_consumer(img_queue):
  32. while True:
  33. start = int(round(time.time() * 1000))
  34. # 1. get img from queue
  35. img_obj = img_queue.get()
  36. img_queue.task_done()
  37. # get end signal
  38. if img_obj[0] == 'E':
  39. client.close()
  40. break
  41. # 2. send package(img_bytes_size, img_bytes)
  42. pack_size = struct.pack("i", len(img_obj))
  43. client.send(pack_size + img_obj)
  44. end = int(round(time.time() * 1000))
  45. data = client.recv(65536)
  46. json_str = data.decode('utf8', 'ignore').strip(b'\x00'.decode())
  47. results = json.loads(json_str)
  48. end = int(round(time.time() * 1000))
  49. end_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
  50. print('send and recv cost time: ', (end - start))
  51. print(results)
  52. if __name__ == '__main__':
  53. client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  54. client.connect((host, port))
  55. img_dir = 'data'
  56. one_img = './data/image.jpg'
  57. mode = 'dir'
  58. img_jq = JoinableQueue()
  59. producer = Thread(target=img_product, args=(img_jq, img_dir, mode,))
  60. consumer = Thread(target=server_consumer, args=(img_jq,))
  61. producer.daemon = True # set daemon but not set join()
  62. producer.start()
  63. consumer.start()
  64. # producer.join() // 让生产者先关闭,防止close错误
  65. consumer.join()

总结

  • 其实这个项目真正做完感觉还是挺简单, 就是对socket通信不太熟悉, 以及传图片没做过.
  • 实际上传图片只需要读取图片后,imencode,然后tobytes,最后发送size和data即可.而接受端只需要拼接数组,然后imdecode即可.
  • 另外传输结果的话利用json传输可以让结果可读性可高, 传输也比较方便, 当时copy别人的发送代码, 没有细看,导致使用memcpy让json格式乱码,导致无法解码json.
  • 如果你感觉接收端没问题,一定要看看发送端.
  • 之后的新任务就是视频传输利用rtsp流,敬请期待

参考博客

原文链接:https://www.cnblogs.com/zds1366/p/17464756.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号