C'est le 21e jour du calendrier de l'Avent OpenCV. Je le posterai tard.
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.
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.
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
À 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
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.
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.
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})
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.
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