混合编程python与C++
上个版本: 只是用到ctypes进行传输, 这次将python服务端更改为C++服务端,方便后续维护.
本文实现功能: python传输图片给C++, C++接受图片后对图片进行处理,并将结果返回给python客户端, pass image from python to C++
C++ 服务端
.h文件
注意文中的model
// .h
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <signal.h>
#include <opencv2/opencv.hpp>using namespace std;
using namespace cv;class ModelManager;class ServerManager
{
private:int m_port;char *m_addr;cv::VideoCapture m_cap;int m_server;int m_accept; // client conn
public:bool initialization(const int &port, const cv::VideoCapture &cap, char *addr = nullptr);bool initialization(const int &port, char *addr = nullptr);bool build_connect();bool acceptClient();void error_print(const char *ptr);bool free_connect();bool send_data_frame(ModelManager& model);bool receive_data_frame(cv::Mat &frame, ModelManager& model);
};
.cpp文件
#include "ServerManager.h"
#include "ModelManager.h"
#define BUFFER_SIZE 65538void ServerManager::error_print(const char * ptr) {perror(ptr);exit(EXIT_FAILURE);
}bool ServerManager::initialization(const int& port, const cv::VideoCapture& cap, char* addr){m_port = htons(port);m_addr = addr;m_cap = cap;return true;
}bool ServerManager::initialization(const int& port, char* addr){m_port = htons(port);m_addr = addr;return true;
}bool ServerManager::build_connect() {struct sockaddr_in server_addr;bzero(&server_addr,sizeof(server_addr));server_addr.sin_family = AF_INET;server_addr.sin_addr.s_addr = m_addr?inet_addr(m_addr):INADDR_ANY; server_addr.sin_port = m_port;// create socket m_server = socket(AF_INET, SOCK_STREAM, 0);if(m_server < 0)error_print("socket bind error");// can reuse portint on = 1;if(setsockopt(m_server,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on)) < 0)error_print("setsockopt error");// bind addrif(bind(m_server, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0)error_print("bind error");// listen only one clientif(listen(m_server, 1) < 0)error_print("listen failed");cout << "ServerManager is listening, plesae wait..." << endl;return true;
}bool ServerManager::acceptClient(){struct sockaddr_in accept_addr;socklen_t accept_len = sizeof(accept_addr);bzero(&accept_addr,sizeof(accept_addr));// accept client connectionif((m_accept = accept(m_server,(struct sockaddr*)&accept_addr,&accept_len)) < 0)error_print("accept error");std::cout << "Connection established" << std::endl;return true;
}bool ServerManager::send_data_frame(ModelManager& model) {char *json_output = nullptr;json_output = model.createJson();if (json_output == nullptr) {return false;}// printf("send data %s\n", json_output);// just send json_output, dont memcpy new char*!!! it wastes me two hours// send jsonint result = send(m_accept, json_output, strlen(json_output), 0);if (result == -1) {cout << "send fail" << endl;return false;}return true;
}bool ServerManager::receive_data_frame(Mat& frame, ModelManager& model) {// recv frame sizeint data_size;if (recv(m_accept, &data_size, sizeof(data_size), 0) != sizeof(data_size)) {// when client close, then close connectionclose(m_accept);cout << "close connection to client" << endl;acceptClient(); // restart a new accept, to accept new connectionreturn false;}cout << data_size << endl;// recv frame data// char buf[data_size];// std::vector<uchar> decode;// int bytes_received = 0;// do// {// int nBytes = recv(m_accept, buf, data_size - bytes_received, 0);// for (int i = 0; i < nBytes; i++) // maybe can use memcpy, maybe faster// {// decode.emplace_back(buf[i]);// }// cout << bytes_received << endl;// bytes_received += nBytes;// } while (bytes_received < data_size);char *recv_char = new char[data_size];std::vector<uchar> decode(data_size, 0);int index = 0;int bytes_received = 0;int count = data_size;while (count > 0)// if count >= 0, dead loop{int iRet = recv(m_accept, recv_char, count, 0);if (index >= data_size) index = data_size;memcpy(&decode[index], recv_char , iRet);index += iRet;if (!iRet) { return -1; }count -= iRet;}// decode messageframe = imdecode(decode, cv::IMREAD_COLOR);// push into Model's queueMatmodel.mtxQueueImg.lock();model.queueMat.push(frame);model.mtxQueueImg.unlock();return true;
}bool ServerManager::free_connect() {m_cap.release();close(m_accept);close(m_server);return true;
}
C++ model部分代码
.h文件
#pragma once
#include "CV_Classify.h"
#include "CV_Detect.h"
#include "ServerManager.h"
#include <opencv2/opencv.hpp>
#include <mutex>
#include <queue>
#include <unistd.h> // usleep
#include <thread>
#include "cJSON.h"
#include <string>using namespace std;
using namespace cv;class ModelManager{
public:Detect objdetect;Classify objclassify;std::mutex mtxQueueDet; // mutex for detect queuestd::mutex mtxQueueImg; // mutex for image queuestd::mutex mtxQueueCls; // mutex for classify queuestd::queue<cv::Mat> queueMat;std::queue<ObjDetectOutput> queueDetOut;// Detect queuestd::queue<ObjClassifyOutput> queueClsOut;// Classify queuebool DetectFlag = true;bool ClassifyFlag = true;bool empty_flag = false;friend class ServerManager;
public:void initDetectModel() ;void initClassifyModel() ;void DetectImg();void ClassifyImg();void getClsResult(ObjClassifyOutput &output);// ObjClassifyOutput getClsResult();char* createJson();
};
.cpp文件
部分有删减,createJson可参考使用,利用json来传递值
#include "ModelManager.h"void ModelManager::initDetectModel()
{std::string config_path = "DetectConfig.yaml";objdetect.Init(config_path, 1);
}
void ModelManager::initClassifyModel()
{std::string config_path = "ClassiflyConfig.yaml";objclassify.Init(config_path, 1);
}void ModelManager::DetectImg()
{DetectInput detect_input;DetectOutput detect_output;cv::Mat frame;size_t mm = 0;while(1){ if (queueMat.empty()) {if(!DetectFlag){break;}usleep(2000);continue;}// get image from queueMatmtxQueueImg.lock();frame = queueMat.front();queueMat.pop();mtxQueueImg.unlock();// run modelobjdetect.Run(detect_input, detect_output);// push detect result into queueDetOutmtxQueueDet.lock();queueDetOut.push(detect_output);// cout << "detect run !!" << endl;mtxQueueDet.unlock();}return;
}
void ModelManager::ClassifyImg()
{ObjClassifyInput input;ObjClassifyOutput output;cv::Mat frame;Detoutput detect_result;while(1){if (queueDetOut.empty()) {if(!ClassifyFlag){break;}usleep(2000);continue;}// get detect from queueDetOutmtxQueueDet.lock();detect_result = queueDetOut.front();queueDetOut.pop();mtxQueueDet.unlock();// run modelobjclassify.Run(input, output);// push cls result into queueClsOutmtxQueueCls.lock();queueClsOut.push(output);mtxQueueCls.unlock(); }return;
}void ModelManager::getClsResult(ObjClassifyOutput& output){if (queueClsOut.empty()){output.object_list.object_num = -1; // -1 is now empty;return; // must return in thread otherwise cant use &output}output = queueClsOut.front();queueClsOut.pop();return;
}char* ModelManager::createJson() // dont know why cant use &value, need return value
{mtxQueueCls.lock();ObjClassifyOutput output;getClsResult(output); mtxQueueCls.unlock();if (output.object_list.object_num == -1){return nullptr;}// prepare send data jsoncJSON* json_object_list = NULL;cJSON* json_ObjClassifyOutput = NULL;json_ObjClassifyOutput = cJSON_CreateObject();json_object_list = cJSON_CreateObject();cJSON_AddItemToObject(json_ObjClassifyOutput, "object_list", json_object_list);int obj_num = output.object_list.object_num;cJSON_AddNumberToObject(json_object_list, "object_num", obj_num);for (int i = 0; i < obj_num; ++i){cJSON* json_object = cJSON_CreateObject();cJSON* json_box = cJSON_CreateObject();cJSON_AddNumberToObject(json_box,"x", output.object_list.object[i].bbox.x);cJSON_AddNumberToObject(json_box,"y", output.object_list.object[i].bbox.y);cJSON_AddNumberToObject(json_box,"w", output.object_list.object[i].bbox.w);cJSON_AddNumberToObject(json_box,"h", output.object_list.object[i].bbox.h);cJSON_AddItemToObject(json_object,"bbox", json_box);cJSON_AddNumberToObject(json_object, "classes", output.object_list.object[i].classes);cJSON_AddNumberToObject(json_object, "objectness", output.object_list.object[i].objectness);// double prob = output.object_list.object[i].prob;// cJSON_AddNumberToObject(json_object, "prob", prob); // pointer cant use?string str = "object" + to_string(i);cJSON_AddItemToObject(json_object_list, str.c_str(), json_object);// printf("prob: %f", output.object_list.object[i].prob);}char* json_output = cJSON_Print(json_ObjClassifyOutput);cJSON_Delete(json_ObjClassifyOutput);return json_output;
}
C++ 服务端运行
#include <../include/ServerManager.h>
#include <../include/ModelManager.h>
#include <thread>
#define PORT 8080
void recvServer(ServerManager& s, ModelManager& model){int idx = 0;while (true){// auto start = std::chrono::steady_clock::now();cv::Mat frame;s.receive_data_frame(frame, model);// cal time cost// auto end = std::chrono::steady_clock::now();// std::chrono::duration<double, std::milli> elapsed = end - start;// std::cout << "recv execution time: " << elapsed.count() << " ms\n";if (frame.empty()) {usleep(2000);continue;}// cv::imwrite("image"+to_string(idx++)+".jpg", frame);std::cout << "Image " << idx++ <<" received !!" << std::endl;}
}void sendServer(ServerManager& s, ModelManager& model){while (true){if (s.send_data_frame(model)) {cout << "send success!!" << endl;cout << endl;}else{// cout << "send fail!!" << endl;usleep(2000);}}
}int main()
{ServerManager s;ModelManager model;model.initDetectModel();model.initClassifyModel();cout << endl;s.initialization(PORT);s.build_connect();s.acceptClient();thread recv_server(recvServer, std::ref(s), std::ref(model));thread send_server(sendServer, std::ref(s), std::ref(model));thread detect(&ModelManager::DetectImg, &model);thread classfy(&ModelManager::ClassifyImg, &model);detect.join();classfy.join();recv_server.join();send_server.join();return 0;
}
python客户端
import json
import socket
import struct
import time
from multiprocessing import JoinableQueue
from threading import Threadimport os
from natsort import ns, natsortedhost = '192.168.0.2' # '192.168.0.2' 'localhost'
port = 8080def img_encode(img_path):img = cv2.imread(img_path)# img = cv2.resize(img, (500, 500), interpolation=cv2.INTER_CUBIC)img_param = [95] # 图片压缩率0-100_, img = cv2.imencode('.jpg', img, img_param)img = img.tobytes()return imgdef img_product(img_queue, path, path_mode='image'):if path_mode == 'image':image = img_encode(path)img_queue.put(image)elif path_mode == 'dir':dir_list = os.listdir(path)files = natsorted(dir_list, alg=ns.PATH) # 顺序读取文件名for filename in files:img_path = path + '/' + filenameimage = img_encode(img_path)img_queue.put(image)img_queue.put('E')img_queue.join()def server_consumer(img_queue):while True:start = int(round(time.time() * 1000))# 1. get img from queueimg_obj = img_queue.get()img_queue.task_done()# get end signalif img_obj[0] == 'E':client.close()break# 2. send package(img_bytes_size, img_bytes)pack_size = struct.pack("i", len(img_obj))client.send(pack_size + img_obj)end = int(round(time.time() * 1000))data = client.recv(65536)json_str = data.decode('utf8', 'ignore').strip(b'\x00'.decode())results = json.loads(json_str)end = int(round(time.time() * 1000))end_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())print('send and recv cost time: ', (end - start))print(results)if __name__ == '__main__':client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)client.connect((host, port))img_dir = 'data'one_img = './data/image.jpg'mode = 'dir'img_jq = JoinableQueue()producer = Thread(target=img_product, args=(img_jq, img_dir, mode,))consumer = Thread(target=server_consumer, args=(img_jq,))producer.daemon = True # set daemon but not set join()producer.start()consumer.start()# producer.join() // 让生产者先关闭,防止close错误consumer.join()
总结
- 其实这个项目真正做完感觉还是挺简单, 就是对socket通信不太熟悉, 以及传图片没做过.
- 实际上传图片只需要读取图片后,imencode,然后tobytes,最后发送size和data即可.而接受端只需要拼接数组,然后imdecode即可.
- 另外传输结果的话利用json传输可以让结果可读性可高, 传输也比较方便, 当时copy别人的发送代码, 没有细看,导致使用memcpy让json格式乱码,导致无法解码json.
- 如果你感觉接收端没问题,一定要看看发送端.
- 之后的新任务就是视频传输利用rtsp流,敬请期待
参考博客
- use memcpy in receive_frame function if you want,对应github地址
- a pure client and server code to create a simple demo
- 如果用ctrl+z中断,可能导致address in use,使用bg/fg
- 用户可以使用fg/bg操作继续前台或后台的任务,fg命令重新启动前台被中断的任务,bg命令把被中断的任务放在后台执行
- zmq send img
- 优化imdecode速度,本代码未使用
- 视频传输跟图片传输差不多
- Linux c++获取本地毫秒级精确时间
- 如果不想用json,可以试试struct
- 利用json来传输分类结果
相关文章:
混合编程python与C++
上个版本: 只是用到ctypes进行传输, 这次将python服务端更改为C服务端,方便后续维护. 本文实现功能: python传输图片给C, C接受图片后对图片进行处理,并将结果返回给python客户端, pass image from python to C C 服务端 .h文件 注意文中的model // .h #pragma once #inclu…...
【单片机】单片机入门指南
一、概述 单片机(Microcontroller,简称MCU)是一种集成了微处理器、存储器、时钟、IO端口和外设接口等的集成电路芯片。它可以通过编程实现各种控制、运算等功能,常用于自动化控制、家电、智能仪表等领域。 二、单片机的种类和选…...
【PyQt】下载文件时弹出提示用户选择保存文件位置的对话框
1 需求 在界面软件中,用户点击下载某个文件,此时软件需要提示用户选择保存到电脑的某个位置,然后软件才能将文件保存到用户指定的电脑文件夹中。 2 代码 # 需引入的库 import os import sys from PyQt5.QtWidgets import QFileDialogsrc .…...
工具分享 | PDF文档解析工具PyMuPDF
1 需求描述 最近工作需要从PDF文档中按照章节解析出对应的文本和图片(后续可能还会有表格),经过调研,找到了一个功能强大的解析工具MuPDF,对应的Python包是PyMuPDF。本篇博客记录使用它来实现具体功能。 官方文档:https://pymupd…...
QML Book 学习基础5(An Image Viewer)
目录 桌面版(win端) 移动端 下面我们用更有挑战性例子来使用Qt控件,将创建一个简单的图像查看器。 桌面版(win端) 程序主要由四个主要区域组成,如下所示。菜单栏、工具栏和状态栏,通常由控件…...
解决Jackson解析JSON时出现的Illegal Character错误
🌷🍁 博主猫头虎 带您 Go to New World.✨🍁 🦄 博客首页——猫头虎的博客🎐 🐳《面试题大全专栏》 文章图文并茂🦕生动形象🦖简单易学!欢迎大家来踩踩~🌺 &a…...
feign和openfeign的区别
1.OpenFeign介绍 是一个Web声明式的Http客户端远程远程调用工具,底层是封装HttpClient技术,提供接口和注解形式调用。 注意feign客户端调用的事项: a、如果请求参数没有加上注解的话,默认采用post请求发送。 b、服务的名称命名不能…...
Python飞机大战小游戏
游戏规则:键盘上下左右键控制飞机移动 游戏展示图片: 源码: 第一个py命名为:plane_main.py import pygamefrom plane_sprites import *class PlaneGame(object):# """飞机大战主游戏"""def __in…...
【python爬虫】7.爬到的数据存到哪里?
文章目录 前言存储数据的方式存储数据的基础知识基础知识:Excel写入与读取基础知识:csv写入与读取项目:存储周杰伦的歌曲信息 复习 前言 上一关我们以QQ音乐为例,主要学习了如何带参数地请求数据(get请求)…...
Docker 的快速使用
ubuntu安装 centos安装 安装完毕之后执行一下这条命令,可以避免每次使用docker命令都需要sudo权限 sudo usermod -aG docker $USER阿里云docker镜像加速 DockerHub 遇到不懂或者不会使用的命令可以使用docker --help查看文档 docker --help 如: dock…...
Docker consul容器服务自动发现和更新
目录 一、什么是服务注册与发现 二、Docker-consul集群 1.Docker-consul 2.registrator 3.Consul-template 三、Docker-consul实现过程 四、Docker-consul集群配置 1.下载consul服务 2.web服务器启动多例nginx容器,使用registrator自动发现 3.使用…...
MPI内置类型与自定义类型
内置类型 MPI_CHAR: 字符型 MPI_UNSIGNED_CHAR: 无符号字符型MPI_BYTE: 字节型MPI_SHORT: 短整型MPI_UNSIGNED_SHORT: 无符号短整型MPI_INT: 整型MPI_UNSIGNED: 无符号整型MPI_LONG: 长整型MPI_UNSIGNED_LONG: 无符号长整型MPI_FLOAT: 单精度浮点型MPI_DOUBLE: 双精度浮点型M…...
【ES新特性三】Object 原型、原型链相关方法
一、Object 原型、原型链相关方法 1.1 静态方法(Object 调用): Object.setPrototypeOf(obj,prototype) 方法用于设置某个实例对象的原型(可以是null,也可以是一个对象) Object.getPrototypeOf(obj) …...
学习大数据应该掌握哪些基础语言
大数据技术的体系庞大且复杂,每年都会涌现出大量新的技术,目前大数据行业所涉及到的核心技术主要就是:数据采集、数据存储、数据清洗、数据查询分析和数据可视化。 学习大数据需要掌握什么语言基础? 1、Java基础 大数据框架90%以…...
Kubernetes技术--k8s核心技术 ingress
1.引入 我们之前在部署应用(如nginx)的时候,如果你需要外部进行访问,使用的是service中的nodePort方式进行对外的暴露。然后外部就可以使用ip + 端口号来进行访问部署应用。 其实这一种方式是存在着较为明显的缺陷,每一个端口你只能够使用一次,一个端口对应一个应用,而且访…...
中级深入--day15
案例:使用BeautifuSoup4的爬虫 我们以腾讯社招页面来做演示:搜索 | 腾讯招聘 使用BeautifuSoup4解析器,将招聘网页上的职位名称、职位类别、招聘人数、工作地点、发布时间,以及每个职位详情的点击链接存储出来。 # bs4_tencent.p…...
内存四区(个人学习笔记黑马学习)
1、内存分区模型 C程序在执行时,将内存大方向划分为4个区域: 代码区:存放函数体的二进制代码,由操作系统进行管理的全局区:存放全局变量和静态变量以及常量栈区:编译器自动分配释放,存放函数的参数值,局部变量等 堆区:由程序员分配和释放,若程…...
如何使用RPA + ChatGPT自动化提高自己的工作效率
使用RPA(Robotic Process Automation)和ChatGPT可以结合来自动化提高自己的工作效率。下面是一些步骤: (1)确定自动化任务 首先,需要确定哪些任务或工作流程可以通过自动化来提高效率。这些任务应该是重复…...
uni-app之android项目配置和打包
1,项目根目录,找到mainfest.json,如果appid是空的,需要生成一个appid 2,点击重新获取appid,这个时候需要登录,那就输入账号密码登录下 3,登陆后可以看到获取appid成功 4,…...
go语言配置
1、Go语言的环境变量 与Java等编程语言一样,安装Go语言开发环境需要设置全局的操作系统环境变量(除非是用包管理工具直接安装) 主要的系统级别的环境变量有两个: (1)GOROOT:表示Go语言环境在计算机上的安…...
Vim 调用外部命令学习笔记
Vim 外部命令集成完全指南 文章目录 Vim 外部命令集成完全指南核心概念理解命令语法解析语法对比 常用外部命令详解文本排序与去重文本筛选与搜索高级 grep 搜索技巧文本替换与编辑字符处理高级文本处理编程语言处理其他实用命令 范围操作示例指定行范围处理复合命令示例 实用技…...
第19节 Node.js Express 框架
Express 是一个为Node.js设计的web开发框架,它基于nodejs平台。 Express 简介 Express是一个简洁而灵活的node.js Web应用框架, 提供了一系列强大特性帮助你创建各种Web应用,和丰富的HTTP工具。 使用Express可以快速地搭建一个完整功能的网站。 Expre…...
Flask RESTful 示例
目录 1. 环境准备2. 安装依赖3. 修改main.py4. 运行应用5. API使用示例获取所有任务获取单个任务创建新任务更新任务删除任务 中文乱码问题: 下面创建一个简单的Flask RESTful API示例。首先,我们需要创建环境,安装必要的依赖,然后…...
如何在看板中体现优先级变化
在看板中有效体现优先级变化的关键措施包括:采用颜色或标签标识优先级、设置任务排序规则、使用独立的优先级列或泳道、结合自动化规则同步优先级变化、建立定期的优先级审查流程。其中,设置任务排序规则尤其重要,因为它让看板视觉上直观地体…...
可靠性+灵活性:电力载波技术在楼宇自控中的核心价值
可靠性灵活性:电力载波技术在楼宇自控中的核心价值 在智能楼宇的自动化控制中,电力载波技术(PLC)凭借其独特的优势,正成为构建高效、稳定、灵活系统的核心解决方案。它利用现有电力线路传输数据,无需额外布…...
定时器任务——若依源码分析
分析util包下面的工具类schedule utils: ScheduleUtils 是若依中用于与 Quartz 框架交互的工具类,封装了定时任务的 创建、更新、暂停、删除等核心逻辑。 createScheduleJob createScheduleJob 用于将任务注册到 Quartz,先构建任务的 JobD…...
论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一)
宇树机器人多姿态起立控制强化学习框架论文解析 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一) 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化…...
在WSL2的Ubuntu镜像中安装Docker
Docker官网链接: https://docs.docker.com/engine/install/ubuntu/ 1、运行以下命令卸载所有冲突的软件包: for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done2、设置Docker…...
蓝桥杯3498 01串的熵
问题描述 对于一个长度为 23333333的 01 串, 如果其信息熵为 11625907.5798, 且 0 出现次数比 1 少, 那么这个 01 串中 0 出现了多少次? #include<iostream> #include<cmath> using namespace std;int n 23333333;int main() {//枚举 0 出现的次数//因…...
GC1808高性能24位立体声音频ADC芯片解析
1. 芯片概述 GC1808是一款24位立体声音频模数转换器(ADC),支持8kHz~96kHz采样率,集成Δ-Σ调制器、数字抗混叠滤波器和高通滤波器,适用于高保真音频采集场景。 2. 核心特性 高精度:24位分辨率,…...
