Python調用C++編寫的OpenCV函數

Python調用C++編寫的函數,利用OpenCV的方法


有時常常遇到兩種語言切換的問題,比如在C++中實現了一個圖像處理算法,將其封裝爲一個函數,該函數的輸入輸出類型爲OpenCV中常用的類型如cv::Mat等,需要在Python中調用,這時可以愉快地利用OpenCV生成Python包的方法。

原理

利用OpenCV生成Python包的原理,將我們寫的C++程序編譯成Python包(本文介紹的是在Windows環境下,使用VS,Python 64位,其他環境按需修改即可,主要是幾個文件)
原理參考 How OpenCV-Python Bindings Works?

參考learnopencv,本文介紹了cv2.cpp的修改方法,與參考有些區別,改動少一些

配置方法

環境注意事項

  1. Python位數,和使用的VS編譯器位數要相同,本文測試的都是64位
  2. 本文介紹的OpenCV版本是4.0.1,其他版本按需修改(主要是幾個文件,支持生成Python包的OpenCV版本應該都是可以的)

配置步驟

  • cv2.cpp,pycompat.hpp,gen2.py,hdr_parser.py這幾個文件從OpenCV源碼中拷貝出來,大致路徑在moudules/python/src

  • 目錄結構(要新建的後文介紹)

--cv2.cpp
--pycompat.hpp
--gen2.py
--hdr_parser.py
--headers.txt
--src
----pycvtest.h
----pycvtest.cpp
--pycvtest
  • pycvtest.h中寫這些,注意命名空間必須使用cv,減少後續工作量,注意不要直接導入opencv.hpp,因爲它導入了大量的庫,如果不需要不用導入,按需修改,本例中只是用了core.hpp用來展示
#pragma once
//#include <opencv.hpp>
#include <opencv2/core.hpp>

namespace cv {
    CV_EXPORTS_W void add_one_for_test(cv::InputArray t_in, cv::OutputArray t_out);
}
  • pycvtest.cpp中寫這些
#include "pycvtest.h"

namespace cv {
    void add_one_for_test(cv::InputArray t_in, cv::OutputArray t_out)
    {
        cv::add(t_in, 1, t_out);
    }
  • 修改hdr_parser.py中的列表opencv_hdr_list,把我們要加的頭文件寫進去,其他都不要
opencv_hdr_list = [
	"src/pycvtest.h"
]
  • 註釋gen2.py
if hdr.find('opencv2/') >= 0: #Avoid including the shadow files
   self.code_include.write( '#include "{0}"\n'.format(hdr[hdr.rindex('opencv2/'):]) )

改成

self.code_include.write( '#include "{0}"\n'.format(hdr[hdr.rindex('src/'):]) )

這個會影響好後生成的pyopencv_generated_include.h文件導入頭文件的代碼,生成後可查看,按需修改~

  • 新建headers.txt寫頭文件路徑
src/pycvtest.h
  • 新建pycvtest文件夾,用於生成頭文件~
  • 運行python gen2.py pycvtest headers.txt,這時看pycvtest文件夾,應該已經生成好了pyopencv開頭的文件了
  • 在VS中新建DLL工程,注意事項(默認已經配置好了OpenCV
    – 添加cv2.cpp,pycompat.h,pycvtest.h,pycvtest.cpp
    – 添加include路徑,就是剛剛生成以pyopencv_文件名開頭的文件目錄pycvtest
    – 屬性中C/C++ 預處理器添加宏定義CVAPI_EXPORTS,必須寫出來,不然不會導出
    – 添加Python庫lib目錄,大致是Python安裝路徑\Python35\libs
    – 添加Python包含include目錄,大致是Python安裝路徑\Python35\include
    – 添加Numpy包含目錄,大致是什麼什麼\Lib\site-packages\numpy\core\include
    – 屬性常規選項卡,目標文件擴展名.pyd
    – 屬性常規選項卡,目標文件名pycvtest

下面愉快地改cv2.cpp文件

  • 前面有個模板類PyOpenCV_Converter,裏面的兩個函數to from沒有寫,但是後面調用了,VS會編譯不通過,我改的方法是註釋取消,然後把函數定義寫出來,函數內容什麼都不用寫就是下面這樣,後面針對具體類型的模板函數會重載下面的pyopencv_topyopencv_from,所以不用擔心,有更好改法的朋友請告知,謝謝~
template<typename T, class TEnable = void>  // TEnable is used for SFINAE checks
struct PyOpenCV_Converter
{
    static inline bool to(PyObject* obj, T& p, const char* name) {
        return true;
    }
    static inline PyObject* from(const T& src) {
        return nullptr;
    }
};
  • 註釋掉
    // #include "opencv2/opencv_modules.hpp"
    因爲它導入了太多包,按需修改~
  • 取消沒有用到的宏定義,按需修改!
#undef HAVE_OPENCV_HIGHGUI
#undef HAVE_OPENCV_DNN
  • 註釋掉沒有使用的文件,按需修改~
//#include "pyopencv_custom_headers.h"
  • 最後重要步驟!把PyInit_cv2() 這個初始化包的函數名稱改成自己的PyInit_pycvtest(),文件中的全都改一下,就改兩個地方就行了,就函數聲明那個位置

  • 然後編譯吧~,因該主要地方都改了,有小錯誤按需修改。編譯好之後就會編出個pycvtest.pyd

使用

拷貝需要的動態鏈接庫到pyd文件夾,或者添加動態鏈接庫的目錄到Python運行目錄,然後導入包,還有一種方法是先導入cv2然後再導入pycvtest,如下所示

import numpy as np
import cv2
import pycvtest as pct
a = np.array([[2, 3, 4]])
output = pct.add_one_for_test(a)
print(output)

好了~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章