python调用c++下的opencv,并实现小幅加速(Linux)

目的:在python中调用C++的OPENCV功能,并探索加速的可能性
假设:已有python cv2包,已有g++,cmake
实验结果:得到了相对较快的调用方案

  1. 配置OPENCV环境

    1. https://www.cnblogs.com/fx-blog/p/8213704.html 这篇是大致的流程,执行完make -j8就算完成
    2. https://blog.csdn.net/u010739369/article/details/79966263 这篇是针对某个ippicv文件因为网络问题一直下载失败的方案
    3. 一般来说即便是跟着步骤做,编译完毕报出来的结果也不一定是全部编译好了,只需要关心C++相关的有没有编译好,建议新建一个文件test.cpp测试一下环境配好没,随便在哪里,测试代码放在文末命名为test.cpp(CSDN的代码块功能越来越好用了^^),请尝试用这个编译指令去编译该文件 g++ `pkg-config --cflags opencv` -o test.so -shared -fPIC test.cpp `pkg-config --libs opencv`,不报错的话说明环境应该好用了,如果对编译指令有疑惑,请见https://www.cnblogs.com/yanzi-meng/p/8066944.html和https://blog.csdn.net/shaoxiaohu1/article/details/8486448
    4. 配置完毕后,需要看下这篇https://www.cnblogs.com/yanzi-meng/p/8066944.html,从而了解下将C++的函数暴露给python调用的一套流程,不然后面代码可能不太理解
  2. python调用C++

    1. 相关代码在文末,命名为example.py和example.cpp,如果能看懂的话就直接看代码就行,不用看解释
    2. 我写的这个测试是关于cv2.absdiff和cv::absdiff的测试,就是调用C++的方法来实现这个cv2的功能
    3. 在example.py的开头,用ctypes.cdll.LoadLibrary函数将.so加载进来,从而获得一个可以重复调用的函数接口
    4. 在example.py的cpp_absdiff函数调用前,需要先将图片灰度化并将数据格式设置为uint8,这样才能和C++版本中的格式对上
    5. 在example.py的cpp_absdiff函数中,首先获取了灰度图的h和w,因为构建C++中的Mat需要用到。然后用src.ctypes.data_as(ctypes.c_char_p)将传入的numpy array转化成了一个字符指针,指向了numpy定义的数组,方便传参用(这个指针类型在待会儿可能会觉得有点别扭)
    6. dll.cpp_absdiff(h,w,src1, src2, src3)直接调用C++程序暴露出来的函数接口,注意该C++函数会将最终的差分结果放在src3所指向的numpy array中
  3. C++下的实现

    1. C++文件的编译指令g++ `pkg-config --cflags opencv` -o test.so -shared -fPIC example.cpp `pkg-config --libs opencv`
    2. 在C++代码中首先用uchar*数据类型接受python那边传过来的指针(这里两种指针类型似乎是不同的,但是好像又可以赋上去)
    3. cv::Mat src(height, width, CV_8UC1, data);利用传过来的指针,直接构造Mat,注意这种构造方法的一个特点,就是会直接在data指向的数据上进行操作,且在程序结束指向时不修改data指向的数据。第三个参数CV_8UC1是说明通道数和每个像素所用比特数目的,在这个absdiff中用的是单通道8bit数据,如果是处理彩色图要注意修改。
    4. cv::absdiff(src, src2, dist);调用C++下的函数,该函数所进行的操作是将结果放到dist中,考虑到3.2中提到的特性,我们这么调用的话会比原版cv2快,因为原来的cv2为了能完成类似于dist = cv2.absdiff(src, src2)这样的调用形式,在它的实现中必然要先重新分配一个用于存储dist数据的数据块,然后再把这个引用返回给python中的变量,这样子做涉及到一个空间分配的步骤以及一个数据类型转换(将C++的数据块指针转换为numpy引用,这个部分其实用numpy提供的接口实现起来非常慢,但是cv2却能实现的相当快了)的步骤,因此可以提速。
/* test.cpp */
/* 测试环境用的代码 */
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/opencv.hpp>                   
#include <stdlib.h>   
using namespace cv;
extern "C" uchar* cpp_absdiff(int height, int width, uchar* data, uchar* data2) {
	cv::Mat src(height, width, CV_8UC1, data);
	cv::Mat src2(height, width, CV_8UC1, data2);
	cv::Mat dst;
	cv::absdiff(src, src2, dst);
	uchar* buffer = (uchar*)malloc(sizeof(uchar)*height*width);
	memcpy(buffer, dst.data, height*width);
	return buffer;
}
extern "C" void release(uchar* data) {
	free(data);
}
# example.py
import cv2
from numpy.ctypeslib import ndpointer
import ctypes
import numpy as np
dll=ctypes.cdll.LoadLibrary('./test.so') 
def cpp_absdiff(src, src2, dst):
    h, w=src.shape[0],src.shape[1] 
    # 获取numpy对象的数据指针
    src = src.ctypes.data_as(ctypes.c_char_p)  
    src2 = src2.ctypes.data_as(ctypes.c_char_p)  
    src3 = src3.ctypes.data_as(ctypes.c_char_p)
	
	#调用dll里面的cpp_absdiff函数,结果会被直接写到dst里面
    pointer = dll.cpp_absdiff(h,w,src, src2, dst)
    return

img=cv2.imread('test.jpg')
img2 = cv2.imread('test.jpg')
img3 = cv2.imread('test.jpg')

img=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
img2=cv2.cvtColor(img2,cv2.COLOR_BGR2GRAY)
img3=cv2.cvtColor(img3,cv2.COLOR_BGR2GRAY)
img=np.asarray(img, dtype=np.uint8)
img2=np.asarray(img2, dtype=np.uint8)
img3=np.asarray(img3, dtype=np.uint8)

import time
start = time.time()
for i in range(1):
    cpp_absdiff(img, img2, img3)
end = time.time()
print(end - start)

for i in range(1):
    cv2.absdiff(img, img2, img3)
end = time.time()
print(end - start)
// example.cpp
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/opencv.hpp>                   
#include <stdlib.h>   

using namespace cv;

extern "C" void cpp_absdiff(int height, int width, uchar* data, uchar* data2, uchar* data3) {
	cv::Mat src(height, width, CV_8UC1, data);
	cv::Mat src2(height, width, CV_8UC1, data2);
	cv::Mat dst(height, width, CV_8UC1, data3);
	cv::absdiff(src, src2, dst);
	return;
}
  1. 备注
    1. 代码不保证完全无误,因为最终结果代码不在手里,请根据实际情况DEBUG,最好可以反馈
    2. 实验结果不一定普适,因为我们的情境是高分辨率图像,两种absdiff速度差距明显,调用C++会比cv2快一倍
    3. 其实我觉得还是应该还可以看看https://blog.csdn.net/huachao1001/article/details/89030628,虽然这篇里面的代码写法不是最优,但是思路也体现的很清晰,而且还有很多在这篇中被我避开的细节可以关注下
    4. 如果要在调用C++的时候要传递字符串,会遇到一个坑好像,我记得是要用https://www.runoob.com/python/att-string-encode.html这种str.encode为gbk,然后再用x.ctypes.data_as(ctypes.c_char_p)转换后才可以传入
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
实现 Python 调用 C++ 函数并返回 cv::Mat,可以使用以下步骤: 1. 在 C++ 中编写一个函数,该函数接受输入参数并返回 cv::Mat 类型的值。例如: ```c++ #include <opencv2/opencv.hpp> cv::Mat myFunction(cv::Mat inputImage) { // 这里可以进行一些图像处理操作 cv::Mat outputImage = inputImage.clone(); // 这里只是简单地将输入图像复制到输出图像中 return outputImage; } ``` 2. 将 C++ 函数编译为动态链接库(DLL)或共享对象(SO)文件,以便能够从 Python调用该函数。 3. 在 Python 中使用 ctypes 模块加载 C++ 动态链接库或共享对象,并使用 ctypes 将输入参数传递给 C++ 函数,并将返回值转换为 Python 中的 numpy 数组或 PIL 图像等格式。例如: ```python import numpy as np import ctypes import cv2 # 加载 C++ 动态链接库或共享对象 my_lib = ctypes.cdll.LoadLibrary('./my_lib.so') # 定义 C++ 函数的输入和输出类型 my_lib.myFunction.argtypes = [ctypes.c_void_p] my_lib.myFunction.restype = ctypes.c_void_p # 加载输入图像并将其转换为 cv::Mat 类型 input_image = cv2.imread('input.jpg') input_mat = cv2.cvtColor(input_image, cv2.COLOR_BGR2RGB) input_mat_ptr = ctypes.c_void_p(input_mat.ctypes.data) # 调用 C++ 函数并将返回值转换为 numpy 数组 output_mat_ptr = my_lib.myFunction(input_mat_ptr) output_mat = np.ctypeslib.as_array(output_mat_ptr, shape=input_mat.shape) # 将 cv::Mat 转换为 PIL 图像 output_image = cv2.cvtColor(output_mat, cv2.COLOR_RGB2BGR) pil_image = Image.fromarray(output_image) pil_image.show() ``` 以上就是实现 Python 调用 C++ 函数并返回 cv::Mat 的一般方法。注意,在使用 ctypes 模块进行转换时,需要确保传递给 C++ 函数的指针和返回的指针类型正确,并且需要进行适当的内存管理以避免内存泄漏。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值