Übergeben Sie die OpenCV-Daten der ursprünglichen C ++ - Bibliothek an Python

Es ist der 21. Tag des OpenCV-Adventskalenders. Ich werde es spät posten.

Zusammenfassung dieses Artikels

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.

Wie es funktioniert

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.

Abhängige Bibliotheken

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

Ein Projekt erstellen

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

Über die Umleitung von OpenCV

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.

Beispiel für einen Bibliothekscode

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.

Build-Vorbereitung

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})

Bauen

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.

Bibliotheksaufruf

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

Übergeben Sie die OpenCV-Daten der ursprünglichen C ++ - Bibliothek an Python
Verwendung der C-Bibliothek in Python
Ich wollte die Python-Bibliothek von MATLAB verwenden
Python C / C ++ - Erweiterung: Übergeben Sie einen Teil der Daten als np.array an Python (Schritt setzen)
[Pfeffer] Übergibt die gesamten JSON-Daten, die durch Python-Anforderung erhalten wurden, an das Tablet.
Übergeben Sie die Liste von Python an C ++ als Referenz in pybind11
Tipps für Python-Anfänger, um das Scikit-Image-Beispiel für sich selbst zu verwenden 9 Verwendung aus der C-Sprache
Python C / C ++ - Erweiterungsmuster-Übergeben Sie Daten als np.array an Python
[Python] Lesen von Daten aus CIFAR-10 und CIFAR-100
[Python] Fluss vom Web-Scraping zur Datenanalyse
Ich möchte C ++ - Code aus Python-Code erstellen!
Stellen Sie die von Eigen of C ++ erstellte Bibliothek mit Boost.Numpy in Python zur Verfügung.
Suchen Sie die Position im Originalbild anhand der Koordinaten nach der affinen Konvertierung (Python + OpenCV).
Änderungen von Python 2 zu Python 3.0
Einführung in OpenCV (Python) - (2)
Die Wand beim Ändern des Django-Dienstes von Python 2.7 auf Python 3-Serie
Senden Sie Protokolldaten vom Server an Splunk Cloud
Python zeigt aus der Perspektive eines C-Sprachprogrammierers
Senden Sie Daten von Python über die Socket-Kommunikation an Processing
[Python] Verwendung der Diagrammerstellungsbibliothek Altair
Schreiben Sie Daten mit dem Python-Anforderungsmodul in KINTONE
Der erste Schritt, um Blender von Python verfügbar zu machen
Führen Sie die Python-Funktion von Powershell aus (wie Sie Argumente übergeben).
Ich hatte das Gefühl, dass ich den Python-Code nach C ++ 98 portiert habe.
[Einführung in Python] Grundlegende Verwendung der Bibliothek matplotlib
[Python] So rufen Sie eine Funktion von c aus Python auf (ctypes edition)
Python Open CV hat versucht, das Bild im Text anzuzeigen.
"Cython" -Tutorial, um Python explosiv zu machen: Übergeben Sie das C ++ - Klassenobjekt an das Klassenobjekt auf der Python-Seite. Teil 2
Python Machine Learning Programming Kapitel 1 bietet Computern die Möglichkeit, aus der Datenzusammenfassung zu lernen
"Cython" -Tutorial, das Python explosiv macht: Wenn C ++ - Code von der Bibliothek abhängt. Vorbereitung
Übergeben von Argumenten beim Aufrufen von Python-Skripten über Blender in der Befehlszeile
Übergeben Sie ein Array von PHP an PYTHON und führen Sie eine Numpy-Verarbeitung durch, um das Ergebnis zu erhalten
Rufen Sie C-Sprachfunktionen von Python auf, um mehrdimensionale Arrays auszutauschen
Python - Lesen Sie Daten aus einer numerischen Datendatei, um die verteilte, gemeinsam verteilte Matrix, Eigenwerte und Eigenvektoren zu finden
Ich habe versucht, die Python-Bibliothek von Ruby mit PyCall zu verwenden
[Python] Es kann nützlich sein, die Datenrahmen aufzulisten
Flirte von PHP nach Python
So debuggen Sie eine Standard-Python-Bibliothek in Visual Studio
[Python] Verwendung von Matplotlib, einer Bibliothek zum Zeichnen von Diagrammen
"Cython" -Tutorial, um Python explosiv zu machen: Übergeben Sie ein C ++ - Klassenobjekt an ein Klassenobjekt auf der Python-Seite. Teil ①
Anaconda aktualisiert von 4.2.0 auf 4.3.0 (python3.5 aktualisiert auf python3.6)
Verschiedene Methoden zur Berechnung der Ähnlichkeit zwischen Daten mit Python
Festlegen des Bibliothekspfads zum Bestehen des lokalen GAE / Python-Komponententests
Ich habe versucht, das Gesicht aus dem Video zu erkennen (OpenCV: Python-Version)
[Einführung in matplotlib] Lesen Sie die Endzeit aus den COVID-19-Daten ♬
Erstellen Sie eine Python-Umgebung und übertragen Sie Daten auf den Server
"Cython" -Tutorial, das Python explosiv macht: Wenn C ++ - Code von der Bibliothek abhängt. Schreiben Sie setup.py.
Wechseln Sie von Python2.7 zu Python3.6 (centos7)
Die Geschichte des Kopierens von Daten von S3 auf Googles TeamDrive
Stellen Sie von Python aus eine Verbindung zu SQLite her
Drücken Sie in Python auf REST, um Daten von New Relic abzurufen
Meteorologie x Python ~ Von der Wetterdatenerfassung bis zur Spektrumanalyse ~
Ich habe versucht, das Python-Skript unter Windows 10 von 2.7.11 auf 3.6.0 zu ändern
Programm zur Beurteilung, ob es nach dem westlichen Kalender ein ruhiges Jahr ist [Python]
[Einführung in Python] So erhalten Sie Daten mit der Funktion listdir
Extrahieren Sie den Wert, der einem Wert am nächsten kommt, aus einem Listenelement in Python
Portiert von der R-Sprache von "Sazae-sans Janken Data Analysis" nach Python
[Python] Drücken Sie Keras von TensorFlow und TensorFlow von c ++, um die Ausführung zu beschleunigen.
Bestehen und Studieren der Python 3 Engineer-Zertifizierungsgrundprüfung