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)转换后才可以传入

你可能感兴趣的:(python调用c++下的opencv,并实现小幅加速(Linux))