上个版本: 只是用到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 65538 void 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 port
int on = 1;
if(setsockopt(m_server,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on)) < 0)
error_print("setsockopt error");
// bind addr
if(bind(m_server, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0)
error_print("bind error");
// listen only one client
if(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 connection
if((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 json
int 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 size
int data_size;
if (recv(m_accept, &data_size, sizeof(data_size), 0) != sizeof(data_size)) {
// when client close, then close connection
close(m_accept);
cout << "close connection to client" << endl;
acceptClient(); // restart a new accept, to accept new connection
return 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 message
frame = imdecode(decode, cv::IMREAD_COLOR);
// push into Model's queueMat
model.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 queue
std::mutex mtxQueueImg; // mutex for image queue
std::mutex mtxQueueCls; // mutex for classify queue
std::queue<cv::Mat> queueMat;
std::queue<ObjDetectOutput> queueDetOut;// Detect queue
std::queue<ObjClassifyOutput> queueClsOut;// Classify queue bool 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 queueMat
mtxQueueImg.lock();
frame = queueMat.front();
queueMat.pop();
mtxQueueImg.unlock();
// run model
objdetect.Run(detect_input, detect_output);
// push detect result into queueDetOut
mtxQueueDet.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 queueDetOut
mtxQueueDet.lock();
detect_result = queueDetOut.front();
queueDetOut.pop();
mtxQueueDet.unlock();
// run model
objclassify.Run(input, output);
// push cls result into queueClsOut
mtxQueueCls.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 json
cJSON* 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 Thread import os
from natsort import ns, natsorted host = '192.168.0.2' # '192.168.0.2' 'localhost'
port = 8080 def 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 img def 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 + '/' + filename
image = 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 queue
img_obj = img_queue.get()
img_queue.task_done()
# get end signal
if 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流,敬请期待

参考博客

混合编程python与C++的更多相关文章

  1. 混合编程[python+cpp+cuda]

    很多时候,我们是基于python进行模型的设计和运行,可是基于python本身的速度问题,使得原生态python代码无法满足生产需求,不过我们可以借助其他编程语言来缓解python开发的性能瓶颈.这里 ...

  2. 混合编程:如何用python11调用C++

    摘要:在实际开发过程中,免不了涉及到混合编程,比如,对于python这种脚本语言,性能还是有限的,在一些对性能要求高的情景下面,还是需要使用c/c++来完成. 那怎样做呢?我们能使用pybind11作 ...

  3. 批处理与python代码混合编程的实现方法

    批处理可以很方便地和其它各种语言混合编程,除了好玩,还有相当的实用价值, 比如windows版的ruby gem包管理器就是运用了批处理和ruby的混合编写, bathome出品的命令工具包管理器bc ...

  4. Python和C++的混合编程(使用Boost编写Python的扩展包)

    想要享受更轻松愉悦的编程,脚本语言是首选.想要更敏捷高效,c++则高山仰止.所以我一直试图在各种通用或者专用的脚本语言中将c++的优势融入其中.原来贡献过一篇<c++和js的混合编程>也是 ...

  5. 使用 ctypes 进行 Python 和 C 的混合编程

    Python 和 C 的混合编程工具有很多,这里介绍 Python 标准库自带的 ctypes 模块的使用方法. 初识 Python 的 ctypes 要使用 C 函数,需要先将 C 编译成动态链接库 ...

  6. 在Qt(C++)中与Python混合编程

    一.PythonQt库 在Qt(C++)中与Python混合编程,可以使用PythonQt库. 网站首页:http://pythonqt.sourceforge.net 下载页面:https://so ...

  7. 【转载】ANSYS的APDL与C语言混合编程(实例)

    原文地址:http://www.cnblogs.com/lyq105/archive/2010/05/04/1727557.html 本文讨论的不是利用C语言为ANSYS写扩展(或者说是用户子程序), ...

  8. 用c/c++混合编程方式为ios/android实现一个自绘日期选择控件(一)

    本文为原创,如有转载,请注明出处:http://www.cnblogs.com/jackybu 前言 章节: 1.需求描述以及c/c++实现日期和月历的基本操作 2.ios实现自绘日期选择控件 3.a ...

  9. 简单上手nodejs调用c++(c++和js的混合编程)

    因为项目的原因,最近经常使用node.js搭RESTful接口. 性能还是很不错啦,感觉比Spring Boot之类的要快.而且在不错的性能之外,只要程序结构组织好,别让太多的回调把程序结构搞乱,整体 ...

  10. C++与Java混合编程

    现在的程序员,不再像以前一样,掌握一种编程语言就可以混得有模有样了,现实的情况是,真实的项目中,通常是涉及多种编程语言,举几个简单的例子,一个软件为了快速开发,可能是使用Delphi或VB作为界面开发 ...

随机推荐

  1. linux服务器qps查询,查看当前linux服务器的QPS

    https://blog.csdn.net/weixin_42119281/article/details/116595205 QPS:每秒查询率(QPS,Queries-per-second)是对一 ...

  2. 日期时间数据的处理—R语言

    日期时间是一类独特的数据,在实际中有众多的应用.R语言的基础包中提供了两种类型的时间数据,一类是Date日期数据,它不包括时间和时区信息,另一类是POSIXct/POSIXlt类型数据,其中包括了日期 ...

  3. mysql数据库的登录脚本

    ######################## ku脚本: 可以使用以下ku脚本,它可以根据提供的参数登录到MySQL数据库: #!/bin/bash # Check for correct num ...

  4. Java学习笔记03

    1. 流程控制语句 在一个程序执行的过程中,各条语句的执行顺序对程序的结果是有直接影响的.所以,我们必须清楚每条语句的执行流程.而且,很多时候要通过控制语句的执行顺序来实现我们想要的功能. 1.1 分 ...

  5. 使用 Transformers 进行图分类

    在之前的 博文 中,我们探讨了图机器学习的一些理论知识.这一篇我们将探索如何使用 Transformers 库进行图分类.(你也可以从 此处 下载演示 notebook,跟着一起做!) 目前,Tran ...

  6. 我的第一个NPM包:panghu-planebattle-esm(胖虎飞机大战)使用说明

    好家伙,我的包终于开发完啦 欢迎使用胖虎的飞机大战包!! 为你的主页添加色彩 这是一个有趣的网页小游戏包,使用canvas和js开发 使用ES6模块化开发 效果图如下:  (觉得图片太sb的可以自己改 ...

  7. Qt开发技术:Q3D图表开发笔记(三):Q3DSurface三维曲面图介绍、Demo以及代码详解

    前言   qt提供了q3d进行三维开发,虽然这个框架没有得到大量运用也不是那么成功,性能上也有很大的欠缺,但是普通的点到为止的应用展示还是可以的.  其中就包括华丽绚烂的三维图表,数据量不大的时候是可 ...

  8. 深度学习实现案例(Tensorflow、PaddlePaddle)

    深度学习实验案例 文章目录 深度学习实验案例 一.基础理论 实验一:自定义感知机 实验二:验证图像卷积运算效果 二.Tensorflow 实验一:查看Tensorflow版本 实验二:Hellowor ...

  9. LeetCode 654:最大二叉树

    先立个flag吧,坚持每日刷题的小目标,希望自己能坚持下来,如果有需要一起打卡的uu,可以一起监督哈,在菜鸡的路上慢慢变好 题目:最大二叉树 给定一个不重复的整数数组 nums . 最大二叉树 可以用 ...

  10. Pwn系列之Protostar靶场 Stack6题解

    源码如下: #include <stdlib.h> #include <unistd.h> #include <stdio.h> #include <stri ...