以为c++为主程序,传入参数到Python环境中,进行数据处理后,获得返回值再传到主程序中。
编译环境:VS2015,Python35, 下载的python中没有自带python35_d.lib,所以配置平台用release x64(后续如果需要会尝试补充debug版本),当然需要图像处理肯定还要用opencv的,版本3.x以上都可以
1.编写需要调用的python程序,代码如下,分别实现2个函数: Cal:实现数值计算功能,ImgProcessFromData实现从数据获得图像(函数中仅将图像显示出来,说明在Python中可以应用c++的图像数据),主要目的是实现将C++环境中获得的图像数据,传入到Python程序中进行处理,尤其在深度学习领域,Python实现更加便捷,而正常数据的采集和预处理等,C++的执行效率远高于Python,且很多实际应用中(比如一些相机的SDK)的主流还是C++。
import numpy as np
import cv2
def Cal(a,b,c,d):
try:
return (a+b)*c/d
except ZeroDivisionError:
print('divesion by zero')
def ImgProcessFromData(data):
cv2.namedWindow('from data', cv2.WINDOW_NORMAL)
cv2.imshow('from data',data)
cv2.waitKey(0)
2.创建MFC对话框工程:下面也简单介绍了怎么做,懂MFC的可以直接跳过这
- 建立MFC应用程序
- “应用程序类型”选择基于对话框,其他不用改,直接点击完成即可
- 添加2个按钮,分别为“数值计算”,以及“从数据获得图像”
3.配置调用环境
- 首先要把实现对应功能的.py文件放到C++的工程文件夹里面
- 设置包含目录,除了要包含Python本身的目录外,为了将C++的图像数据转换成numpy类型传到Python中,还行包含nump目录
- 设置库目录
- 添加附加依赖项
- 到此为止,所有准备步骤的都做好了,下面就可以开始实现我们的混合编程啦,通过高效运行的C++程序进行数据采集和预处理,再让Python对图像进行方便高效的分割,分类,目标检测等等。
4.C++调用Python程序
先贴上调用Python功能的类定义及实现,主要的地方都有注释
CallPython.h
#pragma once
#include "stdafx.h"
#include<iostream>
#include<stdio.h>
#include<stdlib.h>
#include<fstream>
#include<Python.h>//
#include <arrayobject.h>//
#include "object.h"//这3个就是包含python和numpy的头文件
#include<string.h>
#include<vector>
#include<time.h>
#include<opencv2\opencv.hpp>
using namespace std;
using namespace cv;
class CallPy
{
public:
CallPy();
~CallPy();
PyObject* pModule, *pDict, *pCal, *pImgProcessFromData;
void PyInit(char filename[]);//初始化Python环境,加载模块和模块中的函数,filename是要调用的.py文件名
float CalbyPy(float a, float b, float c, float d);//调用模块中的 ‘Cal’函数
void ImgProFD(Mat img);//调用模块中的 ‘ImgProcessFromData’
private:
};
CallPython.cpp
#include "stdafx.h"
#include"CallPython.h"
int init_numpy()
{
import_array();
}
void CallPy::PyInit(char filename[])
{
Py_Initialize();//初始化Python环境
init_numpy();//导入numpy库的c++版本
PyRun_SimpleString("import sys");
PyRun_SimpleString("sys.path.append('./')");
if (!Py_IsInitialized())
return ;
pModule = PyImport_ImportModule(filename);//加载.py文件
if (!pModule) {
printf("Cant open python file!/n");
return ;
}
pDict = PyModule_GetDict(pModule);//加载文件中的函数名
if (!pDict)
{
printf("Cant find dictionary./n");
return ;
}
pCal = PyDict_GetItemString(pDict, "Cal");//根据函数名获得函数功能块,‘Cal‘为Python中定义的函数名
if (!pCal)
{
printf("Cant find Function. Cal /n");
}
pImgProcessFromData = PyDict_GetItemString(pDict, "ImgProcessFromData");//同样根据函数名获得函数功能块,‘ImgProcessFromData‘为Python中定义的函数名
if (!pImgProcessFromData)
{
printf("Cant find Function. ImgProcessFromData /n");
}
cout << "import python model done" << endl;
return;
}
float CallPy::CalbyPy(float a, float b, float c, float d)
{
PyObject*args = PyTuple_New(4);//传入参数必须为Tuple类型,有几个参数,Tuple大小就为几
PyTuple_SetItem(args, 0, Py_BuildValue("d", a));//将c++的float类型数据赋值给输入参数
PyTuple_SetItem(args, 1, Py_BuildValue("d", b));
PyTuple_SetItem(args, 2, Py_BuildValue("d", c));
PyTuple_SetItem(args, 3, Py_BuildValue("d", d));
PyObject*out = PyObject_CallObject(pCal, args);//调用函数,获得返回值
float result = PyFloat_AsDouble(out);//将返回值转换成c++类型,就可以在后续的c++程序中应用啦
cout <<"cal in c++:" <<result << endl;
return result;
}
//处理图像需要先将Mat类型的数据转换成numpy类型,在赋值给传入参数的Tuple中,然后调用图像处理函数即可
void CallPy::ImgProFD(Mat img)
{
PyObject *ArgList = PyTuple_New(1);
auto sz = img.size();
int x = sz.width;
int y = sz.height;
int z = img.channels();
//cout << x<<" " << y <<" "<< z << endl;
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<uchar>(i);
// operates on each pixel
for (int j = 0; j < iCols; j++)
{
CArrays[++id] = p[j];//连续空间
}
}
npy_intp Dims[3] = { y, x, z }; //注意这个维度数据!
PyObject *PyArray = PyArray_SimpleNewFromData(3, Dims, NPY_UBYTE, CArrays);
PyTuple_SetItem(ArgList, 0, PyArray);
PyObject_CallObject(pImgProcessFromData, ArgList);
Py_DECREF(PyArray);
Py_DECREF(ArgList);
}
CallPy::CallPy()
{
}
CallPy::~CallPy()
{
}
- 对话框主程序修改:在生成的xxxDlg.cpp文件中增加以下内容
在开头处实例化调用对象: CallPy cCallPy;
在 OnInitDialog()中初始化:
AllocConsole();//控制台调试窗口开启,主要是为了显示一些调试信息
freopen("CONOUT$", "w+t", stdin);
freopen("CONOUT$", "w+t", stdout);
freopen("CONOUT$", "w+t", stderr);
cCallPy.PyInit("BeCalled");//初始化调用对象,调用的文件为‘BeCalled.py’在数值计算按钮函数中添加:cCallPy.CalbyPy(1, 2, 3, 4); //利用Python计算 (1+2)*3/4,获得返回值给c++主程序
在从数据获得图像按钮函数中添加:
Mat src = imread("1.jpg");
namedWindow("c++", CV_WINDOW_NORMAL);
imshow("c++", src);
cCallPy.ImgProFD(src);
运行结果: