Reputation: 330
I am surviving to make my C++ program can embed python script so that I can modify the image processing code externally.
I have managed to make it can run on single picture,
but when I try to capture continuous picture and perform image processing, it failed.
Can you kindly help me out?
My environment is :
The following are my source codes.
Qt project file: testPyScript.pro
QT -= gui
CONFIG += c++11 console
CONFIG -= app_bundle
# The following define makes your compiler emit warnings if you use
# any Qt feature that has been marked deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS
# You can also make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
SOURCES += \
Python_wrapper.cpp \
main.cpp
# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
HEADERS += \
Python_wrapper.h
# Python
INCLUDEPATH += "C:/Python/Python38-32/include"
LIBS += -L"C:/Python/Python38-32/libs" \
-lpython38 \
-lpython3
#numpy
INCLUDEPATH +="C:/Python/Python38-32/Lib/site-packages/numpy/core/include"
# opencv
INCLUDEPATH += "C:/opencv/include"
CONFIG(debug, debug|release) {
LIBS += -L"C:/opencv/lib/Debug" \
-lopencv_core420d \
-lopencv_highgui420d \
-lopencv_imgcodecs420d \
-lopencv_imgproc420d \
-lopencv_videoio420d
}
CONFIG(release, debug|release) {
LIBS += -L"C:/opencv/lib/Release" \
-lopencv_core420 \
-lopencv_highgui420 \
-lopencv_imgcodecs420 \
-lopencv_imgproc420 \
-lopencv_videoio420
}
Python_wrapper.h
#ifndef PYTHON_WRAPPER_H
#define PYTHON_WRAPPER_H
#pragma push_macro("slots")
#undef slots
#include <Python.h>
#include <numpy/arrayobject.h>
#include <opencv2/core.hpp>
extern PyObject *pyModule,*pyFunc;
bool init_python();
void end_python();
PyObject* convertImage(const cv::Mat& image) ;
std::string type2str(int type) ;
#pragma pop_macro("slots")
#endif // PYTHON_WRAPPER_H
Python_wrapper.cpp
#include"Python_wrapper.h"
#include<fstream>
#include<QDebug>
#include <sys/stat.h>
PyObject *pyModule=nullptr;
PyObject *pyFunc=nullptr;
bool IsPathExist(const std::string &s)
{
struct stat buffer;
return (stat (s.c_str(), &buffer) == 0);
}
bool init_python()
{
if (!Py_IsInitialized())
{
//set python path
std::ifstream infile;
infile.open("PYTHON_PATH",std::ios::in);
if(infile)
{
qDebug()<<"Given python_path file."<<endl;
std::string python_path;
infile>>python_path;
infile.close();
qDebug()<<"Given python path:"<<python_path.c_str()<<endl;
// check path if exists
if(!IsPathExist(python_path))
{
qDebug()<<"Can not find given python path."<<endl;
return false;
}
std::string env = getenv("PATH");
env += ";"+python_path;
putenv(env.c_str());
}
else
{
qDebug()<<"No specify on python path. Default python will be used."<<endl;
}
qDebug()<<"Py_Initialize..."<<endl;
Py_Initialize();
if(Py_IsInitialized())
qDebug()<<"Py_Initialize. OK."<<endl;
else
{
qDebug()<<"Failed to initialize Python."<<endl;
return false;
}
qDebug()<<"Python version:"<<Py_GetVersion()<<endl;
//add current folder to module serach parth
QString modulePath=QString::fromWCharArray(Py_GetPath());
qDebug()<<"Module search path:"<<modulePath<<endl;
//import modoule
qDebug()<<"Import python module <py_cv>..."<<endl;
pyModule = PyImport_ImportModule("py_cv");
if (pyModule == nullptr)
{
qDebug()<<"Failed to load python module <py_cv>"<<endl;
PyErr_Print();
return false;
}
//import module function
qDebug()<<"Import python function <test>"<<endl;
pyFunc =PyObject_GetAttrString(pyModule,"test");
if (pyFunc == NULL)
{
qDebug()<<"Failed to load python function <test>"<<endl;
PyErr_Print();
return false;
}
}
import_array();
}
void end_python()
{
if(Py_IsInitialized())
Py_Finalize();
}
PyObject* convertImage(const cv::Mat& image) {
//2D image with 3 channels.
npy_intp dimensions[3] = {image.rows, image.cols, image.channels()};
//image.dims = 2 for a 2D image, so add another dimension for channels.
return PyArray_SimpleNewFromData(image.dims + 1, (npy_intp*)&dimensions, NPY_UINT8, image.data);
}
std::string type2str(int type) {
std::string r;
uchar depth = type & CV_MAT_DEPTH_MASK;
uchar chans = 1 + (type >> CV_CN_SHIFT);
switch ( depth ) {
case CV_8U: r = "8U"; break;
case CV_8S: r = "8S"; break;
case CV_16U: r = "16U"; break;
case CV_16S: r = "16S"; break;
case CV_32S: r = "32S"; break;
case CV_32F: r = "32F"; break;
case CV_64F: r = "64F"; break;
default: r = "User"; break;
}
r += "C";
r += (chans+'0');
return r;
}
main.cpp
#include "Python_wrapper.h"
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include<iostream>
int py_image_process(cv::Mat &img)
{
int ierr=-1;
std::cout<<"MatToNDArray"<<std::endl;
PyObject *pyMat =convertImage(img);
std::cout<<"Image type:"<<type2str(img.type()).c_str()<<std::endl;
double d=100.0;
PyObject *pyArgs = PyTuple_New(2);
PyObject* pyD=PyFloat_FromDouble(d);
PyTuple_SetItem(pyArgs,0, pyMat);
PyTuple_SetItem(pyArgs,1,pyD);
PyObject *pyValue= PyObject_CallObject(pyFunc, pyArgs);
if (pyValue != NULL)
{
std::cout<<"Function performed OK"<<std::endl;
if (PyTuple_Check(pyValue))
{
std::cout<<"Check PyValue as Tuple OK"<<std::endl;
ierr = PyLong_AsLong(PyTuple_GetItem(pyValue, 0));
PyObject* bytes = PyTuple_GetItem(pyValue, 1);
std::string msg = PyUnicode_AsUTF8(bytes);
std::cout<<"msg:"<<msg.c_str()<<std::endl;
}
Py_DECREF(pyValue);
}
else
std::cout<<"Failed to perform function"<<std::endl;
Py_XDECREF(pyArgs);
return ierr;
}
int main()
{
int ierr=-1;
std::cout<<"Test embeded python"<<std::endl;
if(!init_python())
{
end_python();
return 2;
}
cv::VideoCapture cap =cv::VideoCapture(0);
// cv::Mat img =cv::imread("0.jpg",cv::IMREAD_COLOR);
cv::Mat img;
for(;;)
{
cap.read(img);
if(!img.empty())
{
// ierr= py_image_process(img);
cv::imshow("image",img);
}
else
break;
if(cv::waitKey(5)>=0) break;
}
cv::destroyAllWindows();
return ierr;
}
and python test script: py_cv.py
import cv2
import numpy as np
def test(img,d):
print(type(img),type(d))
rows,cols,chs=img.shape
cx,cy=int(rows/2),int(cols/2)
d=int(d/2.0)
cv2.circle(img,(cx,cy),d,(0,255,0),2)
return -99,"test message"
Your help is highly appreciated.
Upvotes: 1
Views: 553
Reputation: 330
Finally, I checked my program and found my above code can work normally. But when I use move it in Qt thread( C++), some errors occurred. It seems that embedding python is difficult in a multi-thread application .
Upvotes: 0
Reputation: 117
Take a look at the examle given in the OpenCv documentation for VideoCapture.
//--- INITIALIZE VIDEOCAPTURE
VideoCapture cap;
// open the default camera using default API
// cap.open(0);
// OR advance usage: select any API backend
int deviceID = 0; // 0 = open default camera
int apiID = cv::CAP_ANY; // 0 = autodetect default API
// open selected camera using selected API
cap.open(deviceID + apiID);
// check if we succeeded
if (!cap.isOpened()) {
cerr << "ERROR! Unable to open camera\n";
return -1;
}
The code above is a small snippit from it.
It seems as if you don't open the VideoCapture and you also don't check if it is initialized correctly by using isOpened()
in your main()
in main.cpp.
Upvotes: 1