linux C++调用python脚本:C++opencv数据与python opencv数据交互

文章目录

  • linux C++调用python脚本:C++opencv数据与python opencv数据交互
    • 安装 C++ opencv
    • 安装python3及相关包
    • 离线安装包
      • 离线安装Numpy
      • 离线安装Opencv
    • 代码

linux C++调用python脚本:C++opencv数据与python opencv数据交互

安装 C++ opencv

安装python3及相关包

  • sudo apt-get install python3在安装过程中会遇到一些问题
  • 关于下载源的设置:参考https://blog.csdn.net/ljzdyh/article/details/51073150
    https://blog.csdn.net/tiny_lxf/article/details/75027777
    https://blog.csdn.net/qq_36328643/article/details/89072628?utm_medium=distribute.pc_relevant.none-task-blog-OPENSEARCH-3.nonecase&depth_1-utm_source=distribute.pc_relevant.none-task-blog-OPENSEARCH-3.nonecase
  • linux中可能自带python2,所以安装包的时候需要注意装到python3下面。参考https://blog.csdn.net/df0128/article/details/88356401
  • 升级pip3 sudo pip3 install --upgrade pip
  • 安装包 sudo apt-get install python3-scipy或者pip3 install scipy这里吃了不少苦头,就是用前一条命令能安装上,用后面这条就不行。还有就是必须在liunx机器上直接安装才能成功,而通过远程的方式就不行。也许是因为我在linux机器上装之前执行了sudo root。这里还不清楚原因,记录一下。

离线安装包

有时候尽管更换了源,仍然无法成功执行sudo apt-get install python3-scipy或者pip3 install scipy。这时候就需要进行离线安装。离线安装之前需要确定这么几件事:
(1)确定linux机器是32位的还是64位的。在linux终端上输入getconf LONG_BIT确认。
(2)确定自己安装的Python版本:在终端输入python3,可以看到python3的版本。
(3)补充:尽管树莓派也是linux系统,但是由于嵌入式设备的构架和普通电脑的构架不一样,导致某些安装方法并不能通用。比如树莓派很难通过whl文件安装包,大多数都是通过源码安装。对于opencv-python来说,一般的linux机器可以直接安装,但是由于树莓派(我用的Nano pi)等嵌入式设备可能不是arm,因此没有与之匹配的opencv-python安装包,因此只能使用源码安装。

离线安装Numpy

  • 打开https://pypi.org/project/numpy/#files下载安装包。
    linux C++调用python脚本:C++opencv数据与python opencv数据交互_第1张图片
  • 关于i686和x86_64的区别可以参考https://blog.csdn.net/yandaqijian/article/details/41748759?locationNum=14
  • 点击上图中的View可以查看对应文件的MD5值。下载完毕以后最好比对一下文件的MD5,以免下载过程中出错,导致最终安装失败。关于在win10上计算MD5的方法,可以参考https://blog.csdn.net/u010953692/article/details/78567204/。
  • 将win10的文件传到linux上,可以使用FileZilla这个软件。
  • 使用pip3 install packagename,进行安装。
  • 经过大量的尝试建议尽量不要离线安装,原因如下:安装某个包的时候可能需要安装一些其他的依赖包;包的命名比较讲究,命名不正确的话装不上;难以安装合适的版本,版本太高或者太低都会导致失败。

离线安装Opencv

参考https://blog.csdn.net/weixin_34148456/article/details/94106195。

https://www.cnblogs.com/freeweb/p/5794447.html

https://blog.csdn.net/llfjcmx/article/details/81238766?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase

https://blog.csdn.net/YZXnuaa/article/details/79137762?utm_source=blogxgwz2

关于下载opencv安装包,可以参考https://www.bzblog.online/wordpress/index.php/2020/03/09/opencvdownload/

我的机器上已经安装了opencv4.1,但是我的python脚本需要opencv3的版本。而且经过尝试,虽然机器已经装了opencv4.1和python解释器,但是import cv2失败。此外我的numpy版本较低,不支持高版本的opencv。但是已有的C++程序框架又是基于opencv4的。各种原因,导致我不得不在机器上再次安装一个opencv3,涉及到多版本opencv共存的问题。经过尝试以后,探索出一条安装方法:

  • 下载opencv3.2源码,在机器的/home/pi/中新建一个文件夹opencv32,然后吧opencv源码的zip文件拷贝进来,然后解压。unzip opencv-3.4.2.zip
  • 在opencv-3.4.2文件中建立文件夹release。mkdir release
  • 安装依赖包。sudo apt-get install build-essential cmake libgtk2.0-dev pkg-config python-dev python-numpy libavcodec-dev libavformat-dev libswscale-dev
  • ==在/usr/local/下新建文件夹opencv32,之后我们要把opencv3.2安装到这个文件夹下。==原4.1版本默认是安装在/usr/local中的,因此opencv3.2要安装到一个新的文件夹下,避免两种版本的opencv冲突。
  • 进入release并编译。cd release
  • cmake -D CMAKE_BUILD_TYPE=RELEASE -D CMAKE_INSTALL_PREFIX=/usr/local/opencv32 -D PYTHON_EXECUTABLE=/usr/lib/python3.6..
  • 检查import cv2是否能运行。
  • 编译完成以后,尽管opencv3.2安装完成了,但是python解释器很有可能仍然无法import cv2。接下来需要将/home/pi/opencv32/opencv-3.2.0/release/lib中的cv2.so复制到/usr/local/lib/python3.6/site-packages,将/home/pi/opencv32/opencv-3.2.0/release/lib/python3中的cv2.cpython-36m-aarch64-linux-gnu.so文件复制到/usr/local/lib/python3.6/dist-packages

代码

代码部分参考多篇博客:
https://blog.csdn.net/iamqianrenzhan/article/details/86516440
https://blog.csdn.net/fireflychh/article/details/77413005
https://blog.csdn.net/xranmail/article/details/84759553
https://blog.csdn.net/qq_42517195/article/details/80769974?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-7.nonecase&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-7.nonecase
cmakelist

cmake_minimum_required(VERSION 3.10)
project(counting)
set(CMAKE_CXX_STANDARD 14)

find_package(OpenCV 3.2 REQUIRED)#找到opencv源
set(OpenCV_INCLUDE_DIRS ${OpenCV_INSTALL_PATH})
INCLUDE_DIRECTORIES(${OpenCV_INCLUDE_DIRS})
#LINK_LIBRARIES(${OpenCV_LIBS})
#include_directories(/usr/local/include)
#link_directories(/usr/local/lib)

find_package(PythonLibs 3.6 REQUIRED)
INCLUDE_DIRECTORIES(${PYTHON_INCLUDE_DIRS})
link_directories(${PYTHON_LIBRARY_DIRS})
#include_directories(/usr/include/python3.6)#添加头文件目录,相当于g++ -I参数
#link_directories(/usr/lib/python3.6)#动态链接库或静态链接库的搜索路径,相当于gcc的-L参数

add_executable(counting c_call_python.cpp)
target_link_libraries(counting ${OpenCV_LIBS})#添加opencv链接选项
target_link_libraries(counting ${PYTHON_LIBRARIES})#添加链接库,相同于指定gcc -l参数

C++主程序:(由于涉及到需要对大量指针进行内存释放以避免内存泄露,因此没有将一些功能封装成函数。此外程序的稳定性和内存泄漏问题仍需要进行测试)

#include "Python.h"
#include 
#include 
#include 

using namespace cv;
using namespace std;

int main() {
    // 初始化-----------------------------------------------
    Py_Initialize();
	cout << "Python start --------------------" << endl;
    if (!Py_IsInitialized()) {
        cout << "Python start defeat--------------------" << endl;
        return -1;
    }
    // 加载python文件----------------------------------------------
    PyRun_SimpleString("import sys");
    PyRun_SimpleString("sys.path.append('../')");	// demo_test.py的路径,这里与c_call_python放在同一个目录下
    PyObject *moduleName = PyUnicode_FromString("demo_test");  //载入名为demo_test.py的文件
    PyObject *pModule = PyImport_Import(moduleName);
    if (!pModule) {		//如果不存在改文件,则结束
        printf("can't find python file");
        Py_Finalize();			//关闭python解释器
        return -1;
    }
    PyObject *pFunc = PyObject_GetAttrString(pModule, "run");  //获取函数
    if (!pFunc)
    {
        printf("can't find function [run]");
        Py_Finalize();
        return -1;
    }

    // 循环执行任务-------------------------------------------------------
    int *a;
    for (int i = 1; i<10000000; i++) {
        cout << i << endl;
        // 读取图片---------------------------------------------------
        Mat img = imread("../su.jpg");  // 读取图片
        if (img.empty())
        {
            cout << "img read wrong" << endl;
            Py_Finalize();
            return -1;
        }
        cout << img.size() << endl;

        // CV::Mat 转 python numpy------------------------------------
        auto sz = img.size();	// 获取图像的尺寸
        int x = sz.width;
        int y = sz.height;
        int z = img.channels();
        uchar *CArrays = new uchar[x*y*z];//这一行申请的内存需要释放指针,否则存在内存泄漏的问题
        int iChannels = img.channels();
        int iRows = img.rows;
        int iCols = img.cols * iChannels;
        if (img.isContinuous())
        {
            iCols *= iRows;
            iRows = 1;
        }
        uchar* p;
        int id = -1;
        for (int i = 0; i < iRows; i++)
        {
            // get the pointer to the ith row
            p = img.ptr(i);
            // operates on each pixel
            for (int j = 0; j < iCols; j++)
            {
                CArrays[++id] = p[j];//连续空间
            }
        }
        import_array1(-1);
        npy_intp Dims[3] = { y, x, z }; //注意这个维度数据!
        PyObject *PyArray = PyArray_SimpleNewFromData(3, Dims, NPY_UBYTE, CArrays);

        // 准备其他python函数需要的参数
        //...


        // 将图片以及其他参数进行封装
        PyObject *ArgList = PyTuple_New(1);  //参数列表:创建一个长度为1的元组
        PyTuple_SetItem(ArgList, 0, PyArray); //将PyArray的引用指向元组ArgList的第0个元素

        PyObject *pReturn = PyObject_CallObject(pFunc, ArgList);
        if (pReturn == NULL) {
            printf("Return value is NULL.");
            Py_Finalize();
            return -1;
        }
        PyArrayObject *Py_array1;
        //读取从python脚本返回的numpy值
        //查看是否是元组数据
        if (PyTuple_Check(pReturn)) {
            //当返回值不止一个,pReturn是一个元组
            PyArg_UnpackTuple(pReturn, "ref", 2, 2, &Py_array1, &a);  //解析元组的内容
            //获取矩阵维度
            npy_intp *Py_array1_shape = PyArray_DIMS(Py_array1);	//获取元组第一个元素(矩阵)的大小
            npy_intp array1row = Py_array1_shape[0];
            npy_intp array1col = Py_array1_shape[1];
            npy_intp array1high = Py_array1_shape[2];

            Mat mask(array1row, array1col, CV_8UC3, PyArray_DATA(Py_array1));
            imwrite("../cut.jpg", mask);
            // Py_XDECREF(PyArray);
            /*这里Py_XDECREF(ArgList); 和 Py_XDECREF(PyArray);不能同时使用,否则会引起内存访问冲突
             * 我的理解是:PyTuple_SetItem并不复制数据,只是引用的复制。因此对这两个对象中的任意一个使用
             * Py_XDECREF都可以回收对象。使用两次的话反而会导致冲突。
             */
            Py_XDECREF(ArgList);
            delete[] CArrays;		// 释放数组内存,最好在PyArray被使用完以后释放
            CArrays = nullptr;
            Py_XDECREF(pReturn);
            // delete(Py_array1);
            // imshow("su", mask);
            // waitKey(0);
            mask.release();
        }
    }
    // Py_CLEAR(moduleName);
    // Py_CLEAR(pModule);
    Py_XDECREF(moduleName);
    Py_XDECREF(pModule);
    Py_XDECREF(pFunc);		//Py_XDECREF是很有必要的,为了避免内存泄漏
    Py_Finalize();	// 关闭Python
	return 0;
}

python函数:

#coding:utf-8
import cv2
import numpy as np

def run(imgdata):
	print(imgdata.shape)
	a, b = imgdata.shape[0], imgdata.shape[1]
	result = imgdata[0: a>>1, 0:b>>1]
	print(result.shape)
    #cv2.imshow("img", img)
    #cv2.waitKey(0)
	return result, 0

你可能感兴趣的:(Linux,Python,OpenCV,c++,python,linux,opencv)