混合编程python与C++

这篇具有很好参考价值的文章主要介绍了混合编程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 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来传递值文章来源地址https://www.toymoban.com/news/detail-474850.html

#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流,敬请期待

参考博客

  • 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++的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • [Python] 如何通过ctypes库来调用C++ 动态库 DLL?

    ctypes是Python的一个外部库,它提供了一种灵活的方式来调用C语言的动态链接库(DLL)或共享库(SO)。通过ctypes,我们可以在Python中直接调用 C语言编写的函数和变量 ,从而实现跨语言的互操作。 ctypes 它提供了与 C 兼容的数据类型,并允许调用 DLL 或共享库中的函数。可使用该模

    2024年01月19日
    浏览(29)
  • python多进程编程常用到的方法

    python中的多线程其实并不是真正的多线程,如果想要充分地使用多核CPU资源,在python中大部分情况需要使用多进程。 python提供了非常好用的多进程包Multiprocessing,只需要定义一个函数,python会完成其它所有事情。借助这个包,可以轻松完成从单进程到并发执行的转换。 mult

    2024年02月14日
    浏览(27)
  • C++和QML混合编程

    Qt元对象系统中注册C++类,在QML中实例化、访问。 C++中实例化并设置为QML上下文属性,在QML中直接使用。            比较: 方法1 可以使C++类在QML中作为一个数据类型,例如函数参数类型或属性类型,也可以使用其枚举类型、单例等,功能更强大。 派生自QObject类或QObject类

    2024年02月04日
    浏览(28)
  • C++和 C 混合编程处理

            原因是因为有很多功能是用 C 语言开发的,而 C++是兼容 C 的,C++应该能直接使用这些功能,那么我们把 C++调用 C 实现的功能的这个做法,称为混合编程         但是用 C 开发的功能,很可能已经用 C 编译器编程成目标文件(或打包成库了),那么目标文件中的函

    2024年02月05日
    浏览(79)
  • QT Quick之quick与C++混合编程

            Qt quick能够生成非常绚丽界面,但有其局限性的,对于一些业务逻辑和复杂算法,比如低阶的网络编程如 QTcpSocket ,多线程,又如 XML 文档处理类库 QXmlStreamReader / QXmlStreamWriter 等等,在 QML 中要么不可用,要么用起来不方便,所以就有了quick和C++混合编程的需求。

    2024年02月12日
    浏览(30)
  • CUDA和C++混合编程及CMakeLists.txt

    首先认识一个问题,单从CMakeLists.txt的角度来看,无法同时使用两种编译器编译两种语言。不过直接编写Makefile是可以的,通过设置不同的任务,可以实现一个Makefile编译两个语言。但这不是这里要讨论的重点。 使用CUDA和C++进行混合编程的意思是:在cpp文件中调用CUDA函数,实

    2024年02月11日
    浏览(31)
  • qt&python混合编程

    参考:Qt调用Python详细过程_贝勒里恩的博客-CSDN博客_qt python 1.环境搭建 1.1.python3.6 32bits 1.2.qt5.12.12 32bits 说明: (1). 务必保持版本位数一致,没有一致就得重新下载一致的版本 (2).以上软件环境的下载和安装,请自行百度,资料很多,在此重点说说混合编程遇到的错误如何解决. (3).请装

    2024年02月06日
    浏览(45)
  • Python调用Shell命令 (python,shell 混合编程)

    Python经常被称作“胶水语言”,因为它能够轻易地操作其他程序,轻易地包装使用其他语言编写的库,也当然可以用Python调用Shell命令。 用Python调用Shell命令有如下几种方式: os .system( \\\"The command you want\\\" ). os .system( \\\"lscpu\\\" ). os .system( \\\"ls -al\\\" ). 这个调用相当直接,且是同步进行的

    2024年02月16日
    浏览(36)
  • 详解cv2.addWeighted函数【使用 OpenCV 添加(混合)两个图像-Python版本】

    有的时候我们需要将两张图片在alpha通道进行混合,比如深度学习数据集增强方式MixUp。OpenCV的 addWeighted 提供了相关操作,此篇博客将详细介绍这个函数,并给出代码示例。🚀🚀 o u t p u t I m g = s a t u r a t e ( α ∗ i n p u t I m g 1 + β ∗ i n p u t I m g 2 + γ ) rm outputImg=saturate( al

    2024年02月06日
    浏览(54)
  • python通过ctypes传参numpy给c语言函数

    gcc -o demo.so -std=c++11 -shared -fPIC demo.c  python3 main.py 概述: 示例实现了numpy数组加上100并通过另外的数组的指针获取返回值。主要过程是 numpy数组转换成c_void_p类型,然后传参给c语言函数,c语言函数中指针强转到需要的数据类型,然后再处理。这样即可改变numpy数组中的数值实

    2024年02月12日
    浏览(30)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包