Es ist der 21. Tag des OpenCV-Adventskalenders. Ich werde es spät posten.
Viele (wenige) möchten mit OpenCV mit Python verschiedenen C ++ - Code verwenden. Ich bin einer von denen. In diesem Artikel wird es daher möglich sein, Daten wie cv :: Mat oder std :: vector << cv :: Rect >> im Numpy-Format an Python zu übergeben. Wenn Sie nur den C ++ - Code aufrufen, ist daran natürlich nichts auszusetzen. Der Punkt dieser Zeit ist, Mat und Vektor in numpy zu konvertieren und zu übergeben, genau wie bei der Verwendung von OpenCV in Python.
Boost.Python ist ein bekannter Mechanismus zum Aufrufen von in C ++ geschriebenen Bibliotheken mit Python, aber der Versuch, eine komplexe Klasse wie Mat in C ++ mit Boost.Python zu konvertieren, ist ziemlich schwierig. Wenn Sie außerdem versuchen, nicht nur Mat, sondern auch Point, Rect und alle anderen Klassen zu portieren, werden Sie sterben. Auf der anderen Seite können OpenCV-Funktionen in erster Linie leicht von Python aus aufgerufen werden, und Mat- und Rect-Vektoren können in Python im gängigen Format von numpy leicht verarbeitet werden. Die Motivation ist, dass Sie mit diesem Mechanismus ganz einfach Ihre eigene C ++ - Bibliothek von Python aus aufrufen können.
Am Ende sieht es so aus.
c++~ Seite
cv::Mat aaaa() {
return cv::Mat();
}
std::vector<cv::Rect> bbbb() {
return std::vector<cv::Rect>(4);
}
Python-Seite
import xxxx
mat = xxxx.aaaa()
vec = xxxx.bbbb()
Außerdem habe ich diesmal den Vorgang unter Windows nicht bestätigt. Ich erinnere mich, dass ich sofort aufgegeben habe und gesagt habe, dass es für eine Weile nicht funktionieren würde. Vielleicht, wenn Sie Ihr Bestes geben. Ubuntu hat eine Erfolgsgeschichte in der Vergangenheit. Als aktuelle Testumgebung haben wir den Vorgang auf Ihrem Mac bestätigt.
Dieses Mal werden wir Boost.Python verwenden. Boost.Python ist eine nette Bibliothek, die C ++ - Klassen und -Funktionen umschließt und Python zur Verfügung stellt.
http://d.hatena.ne.jp/moriyoshi/20091214/1260779899
Der Mechanismus und die Vorzüge von Boost.Python werden ausführlich beschrieben, daher werde ich hier darauf verzichten. Ich bin mit Boost.Python überhaupt nicht vertraut ...
Übrigens befindet sich OpenCV zum Zeitpunkt der Erstellung plötzlich in modules / python / common.cmake.
add_custom_command(
OUTPUT ${cv2_generated_hdrs}
COMMAND ${PYTHON_EXECUTABLE} "${PYTHON_SOURCE_DIR}/src2/gen2.py" ${CMAKE_CURRENT_BINARY_DIR} "${CMAKE_CURRENT_BINARY_DIR}/headers.txt"
DEPENDS ${PYTHON_SOURCE_DIR}/src2/gen2.py
DEPENDS ${PYTHON_SOURCE_DIR}/src2/hdr_parser.py
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/headers.txt
DEPENDS ${opencv_hdrs})
ocv_add_library(${the_module} MODULE ${PYTHON_SOURCE_DIR}/src2/cv2.cpp ${cv2_generated_hdrs})
Wie in gezeigt, wird gen2.py aufgerufen, um jede OpenCV-Funktion so zu konvertieren, dass sie in Boost.Python verwendet werden kann. Darüber hinaus werden Mat und Rect mit cv2.cpp darunter von C ++ in numpy konvertiert. Zum Beispiel ist die Umwandlung von Mat wie folgt.
PyObject* pyopencv_from(const Mat& m)
{
if( !m.data )
Py_RETURN_NONE;
Mat temp, *p = (Mat*)&m;
if(!p->u || p->allocator != &g_numpyAllocator)
{
temp.allocator = &g_numpyAllocator;
ERRWRAP2(m.copyTo(temp));
p = &temp;
}
PyObject* o = (PyObject*)p->u->userdata;
Py_INCREF(o);
return o;
}
Wie Sie beim Lesen sehen können, wird Py_INCREF verwendet, um die Referenzanzahl zu erhöhen, und g_numpyAllocator in der Mitte wird verwendet, um Speicher zuzuweisen.
UMatData* allocate(int dims0, const int* sizes, int type, void* data, size_t* step, int flags, UMatUsageFlags usageFlags) const
{
if( data != 0 )
{
CV_Error(Error::StsAssert, "The data should normally be NULL!");
// probably this is safe to do in such extreme case
return stdAllocator->allocate(dims0, sizes, type, data, step, flags, usageFlags);
}
PyEnsureGIL gil;
int depth = CV_MAT_DEPTH(type);
int cn = CV_MAT_CN(type);
const int f = (int)(sizeof(size_t)/8);
int typenum = depth == CV_8U ? NPY_UBYTE : depth == CV_8S ? NPY_BYTE :
depth == CV_16U ? NPY_USHORT : depth == CV_16S ? NPY_SHORT :
depth == CV_32S ? NPY_INT : depth == CV_32F ? NPY_FLOAT :
depth == CV_64F ? NPY_DOUBLE : f*NPY_ULONGLONG + (f^1)*NPY_UINT;
int i, dims = dims0;
cv::AutoBuffer<npy_intp> _sizes(dims + 1);
for( i = 0; i < dims; i++ )
_sizes[i] = sizes[i];
if( cn > 1 )
_sizes[dims++] = cn;
PyObject* o = PyArray_SimpleNew(dims, _sizes, typenum);
if(!o)
CV_Error_(Error::StsError, ("The numpy array of typenum=%d, ndims=%d can not be created", typenum, dims));
return allocate(o, dims0, sizes, type, step);
}
Und so weiter. Es ist schwierig, dies selbst zu tun. Lassen Sie uns diesmal den Code hier so verwenden, wie er ist, und das C ++ - Format Mat usw. in Python konvertieren.
Es tut mir leid, ich habe viel in meine Umgebung gesteckt, daher bin ich mir nicht sicher, ob das Folgende ausreicht, aber wenn es nicht ausreicht, kommentieren Sie dies bitte und ich werde so viel wie möglich untersuchen. Kommentare sind willkommen, dass es funktioniert hat, wenn Sie dies eingeben.
OpenCV3 with python
numpy
boost
boost-python
Lassen Sie uns als Beispiel ein neues Projekt erstellen. Dieses Mal nennen wir es sample_project. Erstellen Sie sample_project an einem geeigneten Ort.
mkdir sample_project
cd sample_project
Erstellen Sie ein Verzeichnis mit dem Code für die Bibliothek. Diesmal mit boost_opencv.
mkdir boost_opencv
Erstellen Sie einen Ordner, um den Python-Testskriptcode abzulegen.
mkdir scripts
Diesmal wird es das wichtigste sein. Die Umleitung ist jedoch sehr einfach. Kopieren Sie cv2 von früher in Ihr Bibliotheksverzeichnis. Es gibt keinen großen Grund, aber ich werde den Code von cv2.cpp als Header-Datei umbenennen.
cd boost_opencv
wget https://raw.githubusercontent.com/opencv/opencv/3.1.0/modules/python/src2/cv2.cpp
mv cv2.cpp cv2.hpp
Ich möchte nur einen Teil dieser Zeit verwenden, und ich werde den zuvor von gen2.py automatisch generierten Teil nicht verwenden, daher werde ich ihn auf verschiedene Arten ändern. Zunächst möchte ich die folgenden Teile auskommentieren, die in verschiedenen Teilen des Codes verstreut sind.
#include "pyopencv_generated_types.h"
#include "pyopencv_generated_funcs.h"
#include "pyopencv_generated_ns_reg.h"
#include "pyopencv_generated_type_reg.h"
Kommentieren Sie als Nächstes den gesamten Initialisierungsteil aus. Die Initialisierung erfolgt separat, sodass Sie sie kratzen können. Zu Beginn der Rasur
1351 #if PY_MAJOR_VERSION >= 3
1352 extern "C" CV_EXPORTS PyObject* PyInit_cv2();
1353 static struct PyModuleDef cv2_moduledef =
1354 {
1355 PyModuleDef_HEAD_INIT,
1356 MODULESTR,
1357 "Python wrapper for OpenCV.",
Also werde ich von hier bis zum Ende alles auskommentieren. Dies sind jedoch Informationen zu diesem Zeitpunkt, daher denke ich, dass sie sich im Laufe der Zeit ändern werden. In diesem Fall können Sie hier einmal überspringen und den Teil entfernen, bei dem der Fehler später im Erstellungsschritt aufgetreten ist. Damit ist der Umleitungsabschnitt abgeschlossen.
Hier bereiten wir als einfaches Beispiel eine Funktion vor, die Mat zurückgibt, und eine Funktion, die einen Vektor von Rect zurückgibt. Hier geben wir eine 3x3-Nullinitialisierungsmatrix und einen Vektor mit vier Rects zurück.
boost_opencv/main.cpp
#include <boost/python.hpp>
#include "cv2.hpp"
PyObject* GetImage()
{
cv::Mat cv_img(3, 3, CV_32F, cv::Scalar(0.0));
return pyopencv_from(cv_img);
}
PyObject* GetObject(int index) {
vector_Rect2d rects;
rects.push_back(cv::Rect2d(0,0,0,0));
rects.push_back(cv::Rect2d(0,0,0,0));
rects.push_back(cv::Rect2d(0,0,0,0));
rects.push_back(cv::Rect2d(0,0,0,0));
return pyopencv_from(rects);
}
BOOST_PYTHON_MODULE(libboost_opencv)
{
using namespace boost::python;
import_array();
def("GetImage", &GetImage);
def("GetObject", &GetObject);
}
Das wichtigste hier ist pyopencv_from. Hier ist der Code, der Mat und Vektor in Numpy konvertiert. Vector_Rect2d ist übrigens ein Vektor von Rect2d und wird irgendwo in cv2.cpp definiert. Vergessen Sie auch nicht, import_array () aufzurufen, um numpy zu verwenden. Obwohl es sich um die Methode von Boost.Python handelt, muss der korrekte Bibliotheksname in das Argument von BOOST_PYTHON_MODULE eingefügt werden.
Nachdem die Grundvorbereitung abgeschlossen ist, schreiben wir CMakeLists.txt. Erstellen Sie zunächst die Root-Datei CMakeLists.txt wie folgt.
CMakeLists.txt
cmake_minimum_required(VERSION 3.7)
project(sample_project)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake-modules)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/lib)
find_package(OpenCV REQUIRED)
find_package(Boost REQUIRED COMPONENTS python)
find_package(PythonLibs REQUIRED)
# in the case of homebrew python
#set(PYTHON_INCLUDE_DIRS "/usr/local/Cellar/python/2.7.12_2/Frameworks/Python.framework/Versions/2.7/include/python2.7/")
#set(PYTHON_LIBRARIES "/usr/local/Cellar/python/2.7.12_2/Frameworks/Python.framework/Versions/2.7/lib/libpython2.7.dylib")
find_package(PythonInterp REQUIRED)
find_package(Numpy REQUIRED)
add_subdirectory(boost_opencv)
Als Einschränkung kann Ubuntu beim Laden von PythonLibs so ausgeführt werden, wie es ist. Wenn Sie jedoch Python mit Homebrew auf einem Mac installiert haben, scheint dies nicht zu funktionieren. Ich habe den Pass direkt eingegeben, weil es mühsam war ...
Nun, wenn es so bleibt, wie es ist, wird gesagt, dass Numpy nicht gefunden werden kann, also bringen wir den Include-Pfad und den Bibliothekspfad von Numpy. Solange NUMPY_INCLUDE_DIR am Ende festgelegt ist, ist jede Methode in Ordnung, aber ich habe FindNumPy.cmake von Caffe verwendet.
mkdir cmake-modules
cd cmake-modules
wget https://raw.githubusercontent.com/BVLC/caffe/master/cmake/Modules/FindNumPy.cmake
Außerdem scheint FindNumPy.cmake von caffe am Ende die Einstellung für caffe zu haben, daher bin ich mir nicht sicher, aber lassen Sie es uns auskommentieren.
# caffe_clear_vars(__result __output __error_value __values __ver_check __error_value)
Als Nächstes sieht die Datei boost_opencv / CMakeLists.txt für die Bibliothek folgendermaßen aus: Das stimmt, also werde ich die Details weglassen.
boost_opencv/CMakeLists.txt
project(boost_opencv)
include_directories(${OpenCV_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS} ${PYTHON_INCLUDE_DIRS} ${NUMPY_INCLUDE_DIR})
add_library(boost_opencv SHARED main.cpp)
target_link_libraries(boost_opencv ${OpenCV_LIBS} ${Boost_LIBRARIES} ${PYTHON_LIBRARIES})
Das ist genau das. Erstellen Sie einfach ein Build-Verzeichnis und machen Sie cmake und make.
mkdir build
cd build
cmake ..
make
Anschließend wird libboost_opencv.so (libboost_opencv.dylib auf dem Mac) im lib-Verzeichnis erstellt. Alles was Sie tun müssen, ist dies mit Python zu importieren und es aufzurufen.
Das ist wirklich einfach, aber ich werde es vorerst posten. Es gibt keine Regel, aber bereiten Sie ein geeignetes Skript im Skriptverzeichnis vor.
scripts/test.py
import sys
sys.path.append('../lib')
import libboost_opencv
print(libboost_opencv.GetImage())
print(libboost_opencv.GetObject(0))
Dylib, das auf einem Mac erstellt wurde, wird übrigens nicht von Python importiert, daher habe ich die Erweiterung in "so" geändert und sie gelesen. Ergebnis ist,
[[ 0. 0. 0.]
[ 0. 0. 0.]
[ 0. 0. 0.]]
[[ 0. 0. 0. 0.]
[ 0. 0. 0. 0.]
[ 0. 0. 0. 0.]
[ 0. 0. 0. 0.]]
Also konnte ich es schaffen. Jetzt können Sie den C ++ Mat- oder Rect-Vektor in numpy konvertieren und an Python übergeben. Ich bin glücklich.
Recommended Posts