Python 调用C++封装的进一步探索交流


Posted in Python onMarch 04, 2021

我们知道,C++和python各有优缺点,C++可以直接映射到硬件底层,实现高效运行,而python能够方便地来进行编程,有助于工程的快速实现。

那能不能发挥两者的优势将它们结合起来?当然是可以的!有多种方法可以实现它们之间的相互转换。

链接文章中,有提到一个简单的例子,来教我们如何生成可以被python加载的文件。

但是这只能针对简单的数据进行封装,一旦涉及到自定义的类等封装数据,就需要借助第三方库来帮助更好实现。

比如numpy与C++的数据接口。

这里对python调用C++生成的pyd(so/dll)文件进行进一步的探索。

1.首先进行如下配置,在VC++目录中包含python和numpy的文件目录:

Python 调用C++封装的进一步探索交流

配置为Release平台,不然numpy的头文件无法被包含,导致编译器链接出错。

特别要注意的一点是用cmd生成pyd文件时,VS2013可能要输入: SET VS90COMNTOOLS=%VS120COMNTOOLS%(每次重新打开cmd窗口运行pythonsetup.py build的时候都要输入一次)才能生成成功。

2.理解python调用C++的数据交互过程:

Python 调用C++封装的进一步探索交流

Python中的代码通过CPython等将语句解释为C/C++语言,然后编译器调用binding入口函数,将传进来的PyObject*参数通过PyFloat_AsDouble()等转换成C/C++变量。

这些作为输入变量传进已经写好的C++函数,调用该函数,返回C++结果。最后反过来,将C/C++变量转成CPython可以识别的PyObject*对象返回给python编译器(如函数PyFloat_FromDouble()),完成python到C++的调用。

当C/C++里面的输入变量或者返回值都不是基本类型时,比如自定义的类,那我们同样要按照类里面定义数据的方式以数据的方式来对应改成python能识别的基本类型的组合。

以Mat和numpy的array对象相互转换为例:

//以Mat的allocator作为基类,Numpy的Allocator作为继承类
//这样可以用派生对象指针对基类数据进行操作
class NumpyAllocator : public MatAllocator
{
public:
 NumpyAllocator() { stdAllocator = Mat::getStdAllocator(); }
 ~NumpyAllocator() {}
 
 UMatData* allocate(PyObject* o, int dims, const int* sizes, int type, size_t* step) const
 {
  UMatData* u = new UMatData(this);
  u->data = u->origdata = (uchar*)PyArray_DATA((PyArrayObject*) o);
  npy_intp* _strides = PyArray_STRIDES((PyArrayObject*) o);
  for( int i = 0; i < dims - 1; i++ )
   step[i] = (size_t)_strides[i];
  step[dims-1] = CV_ELEM_SIZE(type);
  u->size = sizes[0]*step[0];
  u->userdata = o;
  return u;
 }
 
 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);
  }
 //确保当前使用python的C API是线程安全的
  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);
 }
 
 bool allocate(UMatData* u, int accessFlags, UMatUsageFlags usageFlags) const
 {
  return stdAllocator->allocate(u, accessFlags, usageFlags);
 }
 
 void deallocate(UMatData* u) const
 {
  if(!u)
   return;
  PyEnsureGIL gil;
  CV_Assert(u->urefcount >= 0);
  CV_Assert(u->refcount >= 0);
  if(u->refcount == 0)
  {
   PyObject* o = (PyObject*)u->userdata;
   Py_XDECREF(o);
   delete u;
  }
 }
 //基类指针,调用allocate函数进行内存分配
 const MatAllocator* stdAllocator;
};

上面是先构造好能够相互交互的allocator。

//将PyObject的特性幅值给size,ndims,type
 int typenum = PyArray_TYPE(oarr), new_typenum = typenum;
 int type = typenum == NPY_UBYTE ? CV_8U :
    typenum == NPY_BYTE ? CV_8S :
    typenum == NPY_USHORT ? CV_16U :
    typenum == NPY_SHORT ? CV_16S :
    typenum == NPY_INT ? CV_32S :
    typenum == NPY_INT32 ? CV_32S :
    typenum == NPY_FLOAT ? CV_32F :
    typenum == NPY_DOUBLE ? CV_64F : -1;
 
 //....
 
 int ndims = PyArray_NDIM(oarr);
 //....
 
 const npy_intp* _sizes = PyArray_DIMS(oarr);
 
 const npy_intp* _strides = PyArray_STRIDES(oarr);
 for ( int i = ndims - 1; i >= 0; --i )
 {
  size[i] = (int)_sizes[i];
  if ( size[i] > 1 )
  {
   step[i] = (size_t)_strides[i];
   default_step = step[i] * size[i];
  }
  else
  {
   step[i] = default_step;
   default_step *= size[i];
  }
 }
 //....
 
//这一步直接用PyObject初始化Mat m
 m = Mat(ndims, size, type, PyArray_DATA(oarr), step);
 m.u = g_numpyAllocator.allocate(o, ndims, size, type, step);
 m.addref();

上面是将PyObject对象转为Mat的部分代码,具体可以参考opencv的cv2.cpp文件:..\OpenCV\sources\modules\python\src2

//将Mat转换为PyObject*
template<>
PyObject* pyopencv_from(const Mat& m)
{
 if( !m.data )
  Py_RETURN_NONE;
 Mat temp, *p = (Mat*)&m;
 //确保数据拷贝不会对原始数据m产生破坏
 if(!p->u || p->allocator != &g_numpyAllocator)
 {
  temp.allocator = &g_numpyAllocator;
  ERRWRAP2(m.copyTo(temp));
  p = &temp;
 }
 //将Mat封装好的userdata指针转给Pyobject*
 PyObject* o = (PyObject*)p->u->userdata;
 //引用计数器加一
 Py_INCREF(o);
 return o;
}

3.不是所有C++的语法都能转为python可调用的pyd文件

一个很重要的知识点是,pyd文件跟dll文件非常相似,所以生成dll比较困难的C++代码同样难以生成pyd,C++跟python编译器各自编译特性的区别也会使得转换存在困难,比如C++的动态编译。

下面是可以进行相互转换的C++特性(可以用swig生成):

类;构造函数和析构函数;虚函数;(多重)公有继承;

静态函数;重载(包括大多数操作符重载);引用;

模板编程(特化和成员模板);命名空间;默认参数;智能指针。

下面是不能或者比较困难进行转换的C++特性:

嵌套类;特定操作符的重载比如new和delete。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持三水点靠木。如有错误或未考虑完全的地方,望不吝赐教。

Python 相关文章推荐
Python Web框架Pylons中使用MongoDB的例子
Dec 03 Python
Python isinstance函数介绍
Apr 14 Python
详解Python命令行解析工具Argparse
Apr 20 Python
Python学习教程之常用的内置函数大全
Jul 14 Python
Python输出各行命令详解
Feb 01 Python
Python生成器generator用法示例
Aug 10 Python
python使用pandas处理excel文件转为csv文件的方法示例
Jul 18 Python
python输出带颜色字体实例方法
Sep 01 Python
Django之路由层的实现
Sep 09 Python
使用Python来做一个屏幕录制工具的操作代码
Jan 18 Python
python中导入 train_test_split提示错误的解决
Jun 19 Python
10款最佳Python开发工具推荐,每一款都是神器
Oct 15 Python
使用Python webdriver图书馆抢座自动预约的正确方法
Mar 04 #Python
Python与C/C++的相互调用案例
Mar 04 #Python
解决Python import .pyd 可能遇到路径的问题
Mar 04 #Python
关于PySnooper 永远不要使用print进行调试的问题
Mar 04 #Python
pip/anaconda修改镜像源,加快python模块安装速度的操作
Mar 04 #Python
Pytorch实现WGAN用于动漫头像生成
Mar 04 #Python
基于PyInstaller各参数的含义说明
Mar 04 #Python
You might like
php用数组返回无限分类的列表数据的代码
2010/08/08 PHP
Admin generator, filters and I18n
2011/10/06 PHP
php+jQuery+Ajax实现点赞效果的方法(附源码下载)
2020/07/21 PHP
利用PHP获取网站访客的所在地位置
2017/01/18 PHP
PHP排序算法之简单选择排序(Simple Selection Sort)实例分析
2018/04/20 PHP
Extjs优化(一)删除冗余代码提高运行速度
2013/04/15 Javascript
Extjs NumberField后面加单位实现思路
2013/07/30 Javascript
jQuery父级以及同级元素查找介绍
2013/09/04 Javascript
Javascript 按位与赋值运算符 (&amp;=)使用介绍
2014/02/04 Javascript
IE6浏览器中window.location.href无效的解决方法
2014/11/20 Javascript
浅谈jQuery中setInterval()方法
2015/07/07 Javascript
jQuery获取复选框被选中数量及判断选择值的方法详解
2016/05/25 Javascript
node+experss实现爬取电影天堂爬虫
2016/11/20 Javascript
JS基于正则截取替换特定字符之间字符串操作示例
2017/02/03 Javascript
微信小程序 弹幕功能简单实例
2017/02/14 Javascript
详解基于vue-cli配置移动端自适应
2018/01/13 Javascript
Vue全局loading及错误提示的思路与实现
2019/08/09 Javascript
vue中使用[provide/inject]实现页面reload的方法
2019/09/30 Javascript
vue3+typescript实现图片懒加载插件
2020/10/26 Javascript
Python脚本实现12306火车票查询系统
2016/09/30 Python
python 多线程中子线程和主线程相互通信方法
2018/11/09 Python
python实现Excel文件转换为TXT文件
2019/04/28 Python
纯python进行矩阵的相乘运算的方法示例
2019/07/17 Python
Python列表元素常见操作简单示例
2019/10/25 Python
美国咖啡批发网站:Coffee.org
2017/06/29 全球购物
李宁官方网店:中国运动品牌
2017/11/02 全球购物
Snapfish爱尔兰:在线照片打印和个性化照片礼品
2018/09/17 全球购物
Allsole美国/加拿大:英国一家专门出售品牌鞋子的网站
2018/10/21 全球购物
产品生产计划书
2014/05/07 职场文书
小学中等生评语
2014/12/29 职场文书
2015年新学期寄语
2015/02/26 职场文书
2015年生活老师工作总结
2015/05/27 职场文书
2016年记者节感言
2015/12/08 职场文书
反邪教学习心得体会
2016/01/15 职场文书
Tomcat执行startup.bat出现闪退的原因及解决办法
2022/04/20 Servers
table设置超出部分隐藏,鼠标移上去显示全部内容的方法
2022/12/24 HTML / CSS