在 C++ 程序中嵌入 python-opencv 的问题(单张图片正常,但在网络摄像头上失败)

Posted

技术标签:

【中文标题】在 C++ 程序中嵌入 python-opencv 的问题(单张图片正常,但在网络摄像头上失败)【英文标题】:Problem of embedding python-opencv in C++ program(OK for single picture, but failed on web camera) 【发布时间】:2020-02-26 02:41:22 【问题描述】:

我正在努力让我的 C++ 程序可以嵌入 python 脚本,以便我可以在外部修改图像处理代码。

我已经设法让它可以在单张图片上运行,

但是当我尝试捕捉连续图片并进行图像处理时,它失败了。

你能帮帮我吗?

我的环境是:

Windows 10,Python 版本:3.8.1(32 位)和对应的 numpy Visual Studio 2019 v16.4.3 [vcvarsall.bat] 环境初始化为:'x86' Qt creator 5.14(MSVC 2017,32 bit) 及其 qmake 作为我的 IDE

以下是我的源代码。

Qt 项目文件: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;



和python测试脚本: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"

非常感谢您的帮助。

【问题讨论】:

【参考方案1】:

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

上面的代码是其中的一个小片段。

似乎您没有打开 VideoCapture,也没有通过在 main.cpp 中的 main() 中使用 isOpened() 来检查它是否已正确初始化。

【讨论】:

感谢您的建议。最后,我检查了我的程序,发现我上面的代码可以正常工作。但是当我在 Qt 线程(C++)中使用 move 它时,发生了一些错误。在多线程应用程序中嵌入 python 似乎很困难。我不得不放弃。非常感谢。【参考方案2】:

最后,我检查了我的程序,发现我上面的代码可以正常工作。但是当我在 Qt 线程(C++)中使用 move 它时,发生了一些错误。在多线程应用程序中嵌入 python 似乎很困难。

【讨论】:

以上是关于在 C++ 程序中嵌入 python-opencv 的问题(单张图片正常,但在网络摄像头上失败)的主要内容,如果未能解决你的问题,请参考以下文章

在 Python GUI 中嵌入 C++ 程序

Python-OpenCV —— 基本操作详解

在 C++ 应用程序中嵌入 python 环境

在 Cygwin/GCC C++ 程序中嵌入 Windows Python

将 Java 嵌入到 C++ 应用程序中?

将 Java 嵌入到 C++ 应用程序中?