Python中调用C/C++程序

在Python中调用C/C++代码有两种方式:一种是将C/C++代码编译成动态链接库,并打包成Python模块,在Python中通过import导入模块,调用其中的函数;另一种是将C/C++代码编译成动态链接库,在Python中通过ctypes.cdll.LoadLibrary()直接加载该动态链接库,调用其中的函数。

将C/C++代码打包成Python模块

安装swig

  • 安装pcre
    • 下载pcre源码

      http://www.pcre.org

    • 解压缩

      $ tar xzvf pcre2-10.36.tar.gz
      
    • 安装

      $ cd pcre2-10.36
      $ ./configure --prefix=/usr/local
      $ make
      $ make install
      
  • 安装swig
    • 下载swig源码

      http://www.swig.org/download.html

    • 解压缩

      $ tar -xvf swig-4.0.2.tar
      
    • 安装

      $ cd swig-4.0.2
      $ ./configure --prefix=/usr/local/swig
      $ make
      $ sudo make install
      

编写C/C++程序

  • factorial.c

    int factorial(int n)
    {
            if (n < 0)
            {
                return -1;
            }
            
            if (n == 0)
            {
                return 1;
            }
            
            return n * factorial(n-1);
    }
    
  • factorial.h

    int factorial(int n);  
    
  • factorial.i

    %module basic 
    %{
    #include "factorial.h"
    %}
    %include "factorial.h";
    

    %{%}中包含相关的头文件。

封装代码

$ swig -python factorial.i

执行该命令后,会生成factorial_wrap.c文件。如果是c++程序,则增加-c++选项。

生成动态链接库文件

  • 编写setup.py文件
    from distutils.core import setup, Extension
    
    factorial_module = Extension('_basic',          # Module name, '_' is necessary
                               sources = ['factorial_wrap.c',     # Wrapped interface file
                                          'factorial.c'
                                         ]
                              )
    setup(name = 'basic',                   # Package name
          version = '1.0',
          author = 'Thinker',
          description = 'Compute factorial',
          ext_modules = [factorial_module],
          py_modules = ['basic']          # List of modules needed to be wrapped
    
    
  • 生成动态链接库
    $ sudo python setup.py build_ext install
    
    其中,build_ext表示生成c/c++的扩展模块,install表示将生成的.py文件和.so动态链接库拷贝到Python的第三方模块目录(如/Library/Python/2.7/site-packages)中,并在该目录中生成.egg-info文件。如果将install替换为--inplace选项,则会在源程序目录下生成.py文件和.so动态链接库。

在Python中调用C/C++函数

import basic
basic.factorial(10)

在Python中加载c/c++动态链接库

  • 编写C/C++程序
    factorial.c

    int factorial(int n)
    {
            if (n < 0)
            {
                return -1;
            }
            
            if (n == 0)
            {
                return 1;
            }
            
            return n * factorial(n-1);
    }
    
  • 编译生成动态链接库文件

    $ gcc -o factorial.so -shared -fPIC factorial.c
    
  • 在Python中调用

    from ctypes import *
    basic = cdll.LoadLibrary('/Users/wisdom/program/basic/factorial.so')
    basic.factorial(5)
    

    在macOS中,须给出动态链接库文件的绝对路径,在Linux中只需给出相对路径。

Python与C/C++程序间传递图像

  • 编写C++程序
    showimage.cpp

    #include "showimage.h"
    
    using namespace cv;
    
    extern "C"
    {
        /*
        * Abstract: Convert data to image and show it
        * Parameters: width, height: width and height of image
        *             data: Pointer of the image data
        */
        void show(int width, int height, uchar* data)
        {
            Mat image(height, width, CV_8UC3);
            uchar * pimage;
            int index = 0;
    
            for (int row = 0; row < height; row++)
            {
                // Assign row pointer
                pimage = image.ptr<uchar>(row);
                for (int col = 0; col < width; col++)
                {
                    for (int channel = 0; channel < 3; channel++)
                    {
                        // Assign data to image
                        pimage[3*col+channel] = data[index++];
                    }
                }
            }
    
            imshow("Image", image);
            waitKey(0);
        }
    }
    
  • 编写C++程序头文件
    showimage.h

    #include <opencv2/opencv.hpp>
    
    extern "C"
    {
    	void show(int width, int height, uchar* data);
    }
    
    
  • 编译生成动态链接库

    $ gcc -o image.so -shared -fPIC showimage.cpp -lopencv_core -lopencv_highgui
    
  • 编写Python程序

    import cv2
    import ctypes
    from ctypes import cdll
    import numpy as np
    
    # Read image
    image = cv2.imread('test.jpg')
    image_data = np.asarray(image, dtype=np.uint8)
    # Convert data to one dimension char array of C language and get the pointer of the array
    image_data = image_data.ctypes.data_as(ctypes.c_char_p)
    
    # Load C/C++ dynamic library
    image_fun = cdll.LoadLibrary("./image.so")
    # Show image by C++ function
    image_fun.show(image.shape[1], image.shape[0], image_data)
    
  • 执行Python程序

    $ python3 showimage.py
    

    执行后,Python程序会读入图像,将其转换成C类型的字符数组,然后加载C语言动态链接库,将字符数组的指针及图像宽、高传给C语言的show()函数。show()函数会将传入的字符数组转换成图像,并显示出来。

问题及解决

  • 问题:在macOS系统中,通过swig将c语言程序打包成python模块,在python中import时,出现错误提示ImportError: dlopen(./_basic.so, 2): no suitable image found. Did find: file system relative paths not allowed in hardened programs
    解决方法:这主要是由于Python的第三方模块目录中没有_basic.sobasic.py文件。在sudo python setup.py build_ext命令中加入install选项,以将生成的.py文件和.so动态链接库拷到Python的第三方模块目录中。同时,应避免当前目录也有该.py文件和动态链接库文件,否则会出现查找错误的情况。

参考文献

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值