Transmettez les données OpenCV de la bibliothèque C ++ d'origine à Python

C'est le 21e jour du calendrier de l'Avent OpenCV. Je le posterai tard.

Résumé de cet article

Beaucoup de gens (moins) souhaitent utiliser divers codes C ++ en utilisant OpenCV avec python. Je suis l'un d'eux. Par conséquent, dans cet article, nous allons permettre de passer des données telles que cv :: Mat ou std :: vector << cv :: Rect >> à python au format numpy. Bien sûr, si vous appelez simplement le code C ++, il n'y a rien de mal à cela. Le but de cette fois est de convertir Mat et vecteur en numpy et de le transmettre, tout comme en utilisant OpenCV en Python.

Boost.Python est un mécanisme bien connu pour appeler des bibliothèques écrites en C ++ avec python, mais essayer de convertir une classe complexe comme Mat en C ++ avec Boost.Python est assez difficile. En plus de cela, si vous essayez de porter non seulement Mat, mais Point, Rect et toutes les autres classes, vous mourrez. D'autre part, les fonctions OpenCV peuvent être facilement appelées à partir de python en premier lieu, et les vecteurs Mat et Rect peuvent être facilement gérés en python dans le format commun de numpy. La motivation est que vous pouvez facilement appeler votre propre bibliothèque C ++ à partir de python en utilisant ce mécanisme.

En fin de compte, ça ressemble à ça.

c++~ côté


cv::Mat aaaa() {
  return cv::Mat();
}
std::vector<cv::Rect> bbbb() {
  return std::vector<cv::Rect>(4);
}

côté python


import xxxx
mat = xxxx.aaaa()
vec = xxxx.bbbb()

Aussi, je suis désolé, je n'ai pas confirmé l'opération sur Windows cette fois. Je me souviens d'avoir abandonné en un instant, disant que cela ne marcherait pas avant un certain temps. Peut-être si vous faites de votre mieux. Ubuntu a déjà travaillé dans le passé. En tant qu'environnement de test actuel, nous avons confirmé l'opération sur le Mac actuel.

Comment ça fonctionne

Cette fois, nous utiliserons Boost.Python. Boost.Python est une belle bibliothèque qui encapsule les classes et fonctions C ++ et les rend disponibles pour Python.

http://d.hatena.ne.jp/moriyoshi/20091214/1260779899

Le mécanisme et les avantages de Boost.Python sont décrits en détail dans, je vais donc l'omettre ici. Je ne connais pas Boost.Python en premier lieu ...

À propos, tout à coup, OpenCV est dans modules / python / common.cmake au moment de la construction.

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

Comme indiqué dans, nous appelons gen2.py pour convertir chaque fonction OpenCV afin qu'elle puisse être utilisée dans Boost.Python. De plus, en utilisant cv2.cpp en dessous, Mat et Rect sont convertis de C ++ en numpy. Par exemple, la conversion de Mat est la suivante.

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

Comme vous pouvez le voir en lisant, Py_INCREF est utilisé pour augmenter le nombre de références, et g_numpyAllocator au milieu est utilisé pour allouer de la mémoire.

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

Etc. C'est difficile à faire vous-même, alors cette fois, utilisons le code ici tel quel et convertissons le format C ++ Mat, etc.

Bibliothèques dépendantes

Je suis désolé, j'en ai mis beaucoup dans mon environnement, donc je ne suis pas sûr que ce qui suit soit suffisant, mais si ce n'est pas suffisant, merci de commenter autant que possible. Les commentaires sont les bienvenus sur le fait que cela a fonctionné si vous mettez cela.

OpenCV3 with python
numpy
boost
boost-python

Créer un projet

À titre d'exemple, créons un nouveau projet. Cette fois, nous l'appellerons sample_project. Créez sample_project dans un emplacement approprié.

mkdir sample_project
cd sample_project

Créez un répertoire contenant le code de la bibliothèque. Cette fois avec boost_opencv.

mkdir boost_opencv

Créez un dossier pour mettre le code du script de test python.

mkdir scripts

À propos du détournement d'OpenCV

Cette fois, ce sera le principal. Cependant, le détournement est très facile. Copiez cv2 de la version précédente dans le répertoire de votre bibliothèque. Il n'y a pas de grande raison, mais je vais renommer le code de cv2.cpp en fichier d'en-tête.

cd boost_opencv
wget  https://raw.githubusercontent.com/opencv/opencv/3.1.0/modules/python/src2/cv2.cpp
mv cv2.cpp cv2.hpp

Je ne veux utiliser qu'une partie de ce temps, et je n'utiliserai pas la partie générée automatiquement par gen2.py expliquée précédemment, donc je la modifierai de différentes manières. Tout d'abord, je vais commenter ce qui suit comprend des parties éparpillées dans différentes parties du code.

#include "pyopencv_generated_types.h"
#include "pyopencv_generated_funcs.h"
#include "pyopencv_generated_ns_reg.h"
#include "pyopencv_generated_type_reg.h"

Ensuite, commentez toute la partie d'initialisation. L'initialisation se fera séparément, vous pouvez donc la gratter. Au début du rasage

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.",

Donc, je vais tout commenter d'ici à la fin. Cependant, ce sont des informations pour le moment, donc je pense que cela changera avec le temps. Dans ce cas, je pense que vous pouvez ignorer ici une fois et supprimer la partie où l'erreur s'est produite à l'étape de construction plus tard. Ceci termine la section de déviation.

Exemple de code de bibliothèque

Ici, à titre d'exemple simple, nous allons préparer une fonction qui retourne Mat et une fonction qui retourne un vecteur de Rect. Ici, nous retournerons une matrice d'initialisation zéro 3x3 et un vecteur avec quatre Rects.

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

Le plus important ici est pyopencv_from. Voici le code qui convertit Mat et vecteur en numpy. À propos, vector_Rect2d est un vecteur de Rect2d et est défini quelque part dans cv2.cpp. N'oubliez pas non plus d'appeler import_array () pour utiliser numpy. Aussi, bien que ce soit la méthode de Boost.Python, il est nécessaire de mettre le nom de bibliothèque correct dans l'argument de BOOST_PYTHON_MODULE.

Préparation de la construction

Maintenant que la préparation de base est terminée, écrivons CMakeLists.txt. Commencez par créer la racine CMakeLists.txt comme suit.

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)

En guise de mise en garde, lors du chargement de PythonLibs, ubuntu peut être fait tel quel, mais si vous avez installé python en utilisant Homebrew sur Mac, cela ne semble pas fonctionner. Je suis entré directement dans le col car c'était gênant ...

Eh bien, s'il est laissé tel quel, on dira que Numpy ne peut pas être trouvé, alors apportons le chemin d'inclusion et le chemin de bibliothèque de Numpy. Tant que NUMPY_INCLUDE_DIR est défini à la fin, toute méthode convient, mais j'ai utilisé FindNumPy.cmake de Caffe.

mkdir cmake-modules
cd cmake-modules
wget https://raw.githubusercontent.com/BVLC/caffe/master/cmake/Modules/FindNumPy.cmake

De plus, FindNumPy.cmake of caffe semble avoir le paramètre caffe à la fin, donc je ne suis pas sûr, mais commentons-le.

# caffe_clear_vars(__result __output __error_value __values __ver_check __error_value)

Ensuite, le boost_opencv / CMakeLists.txt de la bibliothèque ressemble à ceci: En l'état, je vais omettre les détails.

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

Construire

C'est juste ça. Créez simplement un répertoire de construction et faites cmake et make.

mkdir build
cd build
cmake ..
make

Ensuite, libboost_opencv.so (libboost_opencv.dylib sur Mac) sera créé dans le répertoire lib. Tout ce que vous avez à faire est d'importer ceci avec python et de l'appeler.

Appel à la bibliothèque

C'est vraiment simple, mais je le posterai pour l'instant. Il n'y a pas de règle, mais préparez un script approprié dans le répertoire scripts.

scripts/test.py


import sys
sys.path.append('../lib')
import libboost_opencv
print(libboost_opencv.GetImage())
print(libboost_opencv.GetObject(0))

Au fait, dylib fait sur Mac n'est pas importé par python, j'ai donc changé l'extension en so et il l'a lu. Le résultat est,

[[ 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.]]

J'ai donc pu le faire. Vous pouvez maintenant convertir le vecteur C ++ Mat ou Rect en numpy et le transmettre à python. Je suis heureux.

Recommended Posts

Transmettez les données OpenCV de la bibliothèque C ++ d'origine à Python
Comment utiliser la bibliothèque C en Python
Je voulais utiliser la bibliothèque Python de MATLAB
Extension Python C / C ++: transmettre une partie des données à Python en tant que np.array (set stride)
[pepper] Transmettez toutes les données JSON obtenues par requête python à la tablette.
Passer la liste de Python vers C ++ par référence dans pybind11
Conseils aux débutants en Python pour utiliser l'exemple Scikit-image pour eux-mêmes 9 Utilisation à partir du langage C
Modèle d'extension Python C / C ++ - Passez des données à Python en tant que np.array
[Python] Comment lire les données de CIFAR-10 et CIFAR-100
[Python] Flux du scraping Web à l'analyse des données
Je veux créer du code C ++ à partir de code Python!
Rendre la bibliothèque créée par Eigen of C ++ disponible à partir de Python avec Boost.Numpy.
Trouvez la position dans l'image d'origine à partir des coordonnées après conversion affine (Python + OpenCV)
Changements de Python 2 à Python 3.0
Introduction à OpenCV (python) - (2)
Le mur lors du passage du service Django de Python 2.7 à la série Python 3
Envoyer les données du journal du serveur vers Splunk Cloud
Points Python du point de vue d'un programmeur en langage C
Envoyer des données de Python au traitement via une communication socket
[Python] Comment utiliser la bibliothèque de création de graphes Altair
Écrire des données dans KINTONE à l'aide du module de requêtes Python
La première étape pour obtenir Blender disponible à partir de Python
Exécuter la fonction Python à partir de Powershell (comment passer des arguments)
J'ai senti que j'avais porté le code Python en C ++ 98.
[Introduction à Python] Utilisation basique de la bibliothèque matplotlib
[Python] Comment appeler une fonction de c depuis python (édition ctypes)
Python Open CV a essayé d'afficher l'image sous forme de texte.
Tutoriel "Cython" pour rendre Python explosif: Passez l'objet de classe C ++ à l'objet de classe côté Python. Partie 2
Programmation Python Machine Learning Chapitre 1 donne aux ordinateurs la possibilité d'apprendre à partir du résumé des données
Tutoriel "Cython" qui rend Python explosif: lorsque le code C ++ dépend de la bibliothèque. Préparation
Comment passer des arguments lors de l'appel d'un script python depuis Blender sur la ligne de commande
Passez un tableau de PHP à PYTHON et effectuez un traitement numpy pour obtenir le résultat
Appeler des fonctions du langage C depuis Python pour échanger des tableaux multidimensionnels
Python --Lisez des données à partir d'un fichier de données numériques pour trouver des matrices, des valeurs propres et des vecteurs propres distribués co-distribués
J'ai essayé d'utiliser la bibliothèque Python de Ruby avec PyCall
[Python] Il peut être utile de lister les trames de données
Flirter de PHP à Python
Comment déboguer une bibliothèque Python standard dans Visual Studio
[python] Comment utiliser Matplotlib, une bibliothèque pour dessiner des graphiques
Tutoriel "Cython" pour rendre Python explosif: Passez un objet de classe C ++ à un objet de classe côté Python. Partie ①
Anaconda mis à jour de 4.2.0 à 4.3.0 (python3.5 mis à jour vers python3.6)
Différentes façons de calculer la similitude entre les données avec python
Définition du chemin de la bibliothèque pour réussir le test d'unité locale GAE / Python
J'ai essayé de reconnaître le visage de la vidéo (OpenCV: version python)
[Introduction à matplotlib] Lire l'heure de fin à partir des données COVID-19 ♬
Créer un environnement Python et transférer des données vers le serveur
Tutoriel "Cython" qui rend Python explosif: lorsque le code C ++ dépend de la bibliothèque. Écrivez setup.py.
Passer de python2.7 à python3.6 (centos7)
L'histoire de la copie de données de S3 vers TeamDrive de Google
Connectez-vous à sqlite depuis python
Appuyez sur REST en Python pour obtenir des données de New Relic
Météorologie x Python ~ De l'acquisition de données météorologiques à l'analyse spectrale ~
J'ai essayé de changer le script python de 2.7.11 à 3.6.0 sur Windows10
Programme pour juger si c'est une année calme du calendrier occidental [Python]
[Introduction à Python] Comment obtenir des données avec la fonction listdir
Extraire la valeur la plus proche d'une valeur à partir d'un élément de liste en Python
Porté du langage R de "Sazae-san's Janken Data Analysis" vers Python
[Python] Hit Keras depuis TensorFlow et TensorFlow depuis c ++ pour accélérer l'exécution.
Comment réussir et étudier l'examen de base de la certification d'ingénieur Python 3