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的chardet库获得文件编码并修改编码
Jan 22 Python
Python使用Beautiful Soup包编写爬虫时的一些关键点
Jan 20 Python
详解python中字典的循环遍历的两种方式
Feb 07 Python
利用Python写一个爬妹子的爬虫
Jun 08 Python
Python爬虫框架Scrapy常用命令总结
Jul 26 Python
不到40行代码用Python实现一个简单的推荐系统
May 10 Python
python 设置xlabel,ylabel 坐标轴字体大小,字体类型
Jul 23 Python
在python中做正态性检验示例
Dec 09 Python
python 画条形图(柱状图)实例
Apr 24 Python
Python命名空间namespace及作用域原理解析
Jun 05 Python
python自动提取文本中的时间(包含中文日期)
Aug 31 Python
python+opencv实现视频抽帧示例代码
Jun 11 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中的超全局变量
2006/10/09 PHP
php下使用无限生命期Session的方法
2007/03/16 PHP
PHP+jQuery翻板抽奖功能实现
2015/10/19 PHP
Laravle eloquent 多对多模型关联实例详解
2017/11/22 PHP
ExtJs Excel导出并下载IIS服务器端遇到的问题
2011/09/16 Javascript
Fixie.js 自动填充内容的插件
2012/06/28 Javascript
使用jQuery.wechat构建微信WEB应用
2014/10/09 Javascript
SublimeText自带格式化代码功能之reindent
2015/12/27 Javascript
javascript解析ajax返回的xml和json格式数据实例详解
2017/01/05 Javascript
js 获取图像缩放后的实际宽高,位置等信息
2017/03/07 Javascript
javascript完美实现给定日期返回上月日期的方法
2017/06/15 Javascript
angularjs实现分页和搜索功能
2018/01/03 Javascript
详解刷新页面vuex数据不消失和不跳转页面的解决
2018/01/30 Javascript
原生js实现移动端Touch轮播图的方法步骤
2019/01/03 Javascript
基于JS实现一个随机生成验证码功能
2019/05/29 Javascript
Vue 列表上下过渡效果的实例代码
2019/06/25 Javascript
Vue项目环境搭建详细总结
2019/09/26 Javascript
[10:54]Team Spirit vs Navi
2018/06/07 DOTA
Python collections模块实例讲解
2014/04/07 Python
解决python flask中config配置管理的问题
2019/07/26 Python
Python类中的魔法方法之 __slots__原理解析
2019/08/26 Python
Python3.7实现验证码登录方式代码实例
2020/02/14 Python
印尼穆斯林时尚购物网站:Hijabenka
2016/12/10 全球购物
Juice Beauty官网:有机美容产品,护肤与化妆品
2020/06/13 全球购物
客户代表自我评价范例
2013/09/24 职场文书
决定成败的关键——创业计划书
2014/01/24 职场文书
大学生水果店创业计划书
2014/01/28 职场文书
带薪年假请假条
2014/02/04 职场文书
六一儿童节演讲稿
2014/05/23 职场文书
党员干部对十八届四中全会的期盼
2014/10/17 职场文书
2015年护士节活动总结
2015/02/10 职场文书
酒店财务总监岗位职责
2015/04/03 职场文书
张丽莉观后感
2015/06/16 职场文书
2016年社区中秋节活动总结
2016/04/05 职场文书
写好Python代码的几条重要技巧
2021/05/21 Python
python必学知识之文件操作(建议收藏)
2021/05/30 Python