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插入排序算法的实现代码
Nov 21 Python
python单例模式实例分析
Apr 08 Python
Python中的ctime()方法使用教程
May 22 Python
使用Python的urllib和urllib2模块制作爬虫的实例教程
Jan 20 Python
python pandas.DataFrame选取、修改数据最好用.loc,.iloc,.ix实现
Jun 11 Python
Python中循环后使用list.append()数据被覆盖问题的解决
Jul 01 Python
利用Pycharm断点调试Python程序的方法
Nov 29 Python
numpy.random.shuffle打乱顺序函数的实现
Sep 10 Python
flask框架自定义过滤器示例【markdown文件读取和展示功能】
Nov 08 Python
Python flask框架端口失效解决方案
Jun 04 Python
Python中的__init__作用是什么
Jun 09 Python
用python对excel查重
Dec 07 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里的JS打印函数
2006/10/09 PHP
memcached 和 mysql 主从环境下php开发代码详解
2010/05/16 PHP
深入理解PHP原理之异常机制
2010/08/21 PHP
PHP入门之常量简介和系统常量
2014/05/12 PHP
浅谈php扩展imagick
2014/06/02 PHP
PHP结合jquery ajax实现上传多张图片,并限制图片大小操作示例
2019/03/01 PHP
php报错502badgateway解决方法
2019/10/11 PHP
Js 弹出框口并返回值的两种常用方法
2010/12/30 Javascript
js判断元素是否隐藏的方法
2014/06/09 Javascript
简单介绍JavaScript中字符串创建的基本方法
2015/07/07 Javascript
jQuery+canvas实现简单的球体斜抛及颜色动态变换效果
2016/01/28 Javascript
GitHub上一些实用的JavaScript的文件压缩解压缩库推荐
2016/03/13 Javascript
javascript回到顶部特效
2016/07/30 Javascript
BootStrap下拉框在firefox浏览器界面不友好的解决方案
2016/08/18 Javascript
smartupload实现文件上传时获取表单数据(推荐)
2016/12/12 Javascript
js动态添加表格逐行添加、删除、遍历取值的实例代码
2018/01/25 Javascript
在vue组件中使用axios的方法
2018/03/16 Javascript
Vue中使用vee-validate表单验证的方法
2018/05/09 Javascript
CryptoJS中AES实现前后端通用加解密技术
2018/12/18 Javascript
vue遍历对象中的数组取值示例
2019/11/07 Javascript
[06:04]DOTA2英雄梦之声Vol19卓尔游侠
2014/06/20 DOTA
基于Python代码编辑器的选用(详解)
2017/09/13 Python
Python基于pycrypto实现的AES加密和解密算法示例
2018/04/10 Python
Django框架的使用教程路由请求响应的方法
2018/07/03 Python
Python Flask前后端Ajax交互的方法示例
2018/07/31 Python
使用pip安装python库的多种方式
2019/07/31 Python
html5 Canvas画图教程(6)—canvas里画曲线之arcTo方法
2013/01/09 HTML / CSS
New Balance加拿大官方网站:运动鞋和健身服装
2018/11/19 全球购物
C语言笔试题
2014/09/04 面试题
大专生自我鉴定怎么写
2014/09/16 职场文书
党政领导班子群众路线对照检查材料
2014/10/26 职场文书
员工加薪申请报告
2015/05/15 职场文书
海底两万里读书笔记
2015/06/26 职场文书
老干部座谈会主持词
2015/07/03 职场文书
干部考核工作总结
2015/08/12 职场文书
2016年世界艾滋病日宣传活动总结
2016/04/01 职场文书