使用Cython加密Python code(1)

为什么需要在项目中引入cython模块

  1. 在python版本的mtcnn需要100多ms才能做完一次detection,而相同功能的c++的library通过封装后,就能做到只需要10几个ms就能轻松搞定。
  2. python的代码加密,由于有一些商业敏感的内容在python的代码中。如果部署在客户那里,就需要有一套比较合适的加密机制。

c++的封装

  1. 首先需要在conda或者virtualenv的环境中通过pip安装cython

    pip install cython==0.28.2
    
  2. 编译c++的功能实现,目前我采用的做法是实现的库文件尽量的编译成静态库,这样引用关系相对单纯很多。现在一般项目用cmake写,编译静态库非常的方便。

  3. 封装功能的实现

    1. setup.py文件,这个文件的主要功能是生成编译和安装脚本

      import setuptools
      from distutils.core import setup
      from distutils.extension import Extension
      from Cython.Build import cythonize
      import numpy
              
      extensions = [
          Extension(
              'mtcnn_ext',
              ['mtcnn_ext.pyx'],
              language="c++",  # generate C++ code
              include_dirs=['../include', '../../lib_a', '../../lib_b', numpy.get_include()],
              library_dirs=['/path/to/some/extra/library/dir', '.'],
              libraries=['opencv_core', 'opencv_imgproc'],
              extra_compile_args=['-std=c++11'],
              # for mac
              # extra_compile_args=['-std=c++11', '-mmacosx-version-min=10.9'],
              extra_objects=['../../build/lib_a/lib_a.a', '../../build/lib_b/lib_b.a'])
      ]
              
      setup(
          name='mtcnn_ext',
          version='0.2.3',
          ext_modules=cythonize(extensions), )
      

      NOTE: 如果需要编译mac的版本extra_compile_args要改成注释的部分。不然后又类似于array头文件找不到之类的错误。

    2. pyx文件,这个文件需要注意的地方有:

      1. c++的结构体定义和函数定义,得对应起来(有点像封装jni的时候做的事情),这里仅用opencv里的一些定义作为示例。

        cdef extern from "opencv2/core/core.hpp":
            cdef int CV_8UC1
            cdef int CV_8UC3
        cdef extern from "opencv2/core/core.hpp" namespace "cv":
                        
            cdef cppclass Mat:
                Mat() except+
                void create(int, int, int)
                void* data
                int rows
                int cols
                int channels()
                        
            cdef cppclass Rect_[_Tp]:
                _Tp x
                _Tp y
                _Tp width
                _Tp height
                        
            ctypedef Rect_[int] Rect
                    
            cdef cppclass Point_[_Tp]:
                _Tp x
                _Tp y
                        
            ctypedef Point_[float] Point2
                        
            cdef cppclass MTNCNN:
                MTNCNN() except+
                detect(xxx,xxx,xxx)
        
      2. 封装一个python的cdef class供python接口调用

        cdef class PyMtcnn:
            cdef MTNCNN* c_mtcnn
            def __cinit__(self):
                self.c_mtcnn = new MTNCNN()
                    
            def __dealloc(self):
                del self.c_mtcnn
        
      3. 编译cython模块的so

        # 调试
        python setup.py build_ext --inplace
        
        # 打包
        python setup.py bdist_wheel
        
      4. python code调用

        import mtcnn_ext
        mtcnn = mtcnn_ext.PyMtcnn()
        mtcnn.detect(xxx,xxx,xxx)
        

c++的封装基本流程就是这样。貌似这篇记录已经有点长了,python的加密部分放到下一篇吧。