上一节中,IntegralOccupancyMap()函数用来确定单词位置,其中调用了query_integral_image()方法。而query_integral_image是用来Cython。下边介绍Cython。

Cython介绍

Cython 的本质可以总结如下:Cython 是包含C 数据类型的Python。
Cython可以将Python代码编译成动态链接库,在某些情况下,可以极大提高Python程序的运行效率。

可以看到源码中包括了query_integral_image.pyxquery_integral_image.c两个文件。其中.c文件是Cython自动生产的对应query_integral_image.pyxC语言程序。

Cython的工作流程大致如下:

Cython流程

我们只需要关心.pyx文件中的代码


为了测试使用Cython是否真的可以提高程序效率,我们做如下测试,
系统:Ubuntu 16
环境:python3.5
依赖:

Cython==0.28.2
numpy==1.14.2
Pillow==5.1.0

测试代码均可从github下载


测试过程

目录tree

.
├── python_query_integral_image.py
├── query_integral_image.pyx
├── setup.py
├── test.py
└── venv

setup.py用来编译动态链接库,内容如下:

from distutils.core import setup
from Cython.Build import cythonize

setup(
    ext_modules = cythonize("query_integral_image.pyx")
)

执行:

python setup.py build_ext --inplace

然后目录变为了:

.
├── build
├── python_query_integral_image.py
├── query_integral_image.c
├── query_integral_image.cpython-35m-i386-linux-gnu.so
├── query_integral_image.pyx
├── setup.py
├── test.py
└── venv

下边我们就可以在python程序中import编译后的.so文件了, 测试程序test.py如下:

# 导入python编写的程序,为了和.so区别,改名为python_query_integral_image
from python_query_integral_image import query_integral_image as q1
# 导入经过Cython处理的.so链接库
from query_integral_image import query_integral_image as q2
from random import Random
import numpy as np
import timeit

DX = 3000
DY = 3000       # 相当于一个3000*3000=900万像素的图片
integral = np.zeros((DX, DY), dtype=np.uint32)
random_state = Random()

start_time = timeit.default_timer()
q1(integral, 50, 50, random_state)
end_time = timeit.default_timer()

q1_dur = (end_time - start_time)/60.

start_time = timeit.default_timer()
q2(integral, 50, 50, random_state)
end_time = timeit.default_timer()

q2_dur = (end_time - start_time)/60.

print('C程序耗时', q2_dur, 'Python耗时', q1_dur)
print('相差%f倍' % (float(q1_dur) / float(q2_dur)))

运行测试程序,结果令人吃惊:

C程序耗时 0.0007940871333024309 Python耗时 0.6854850105833369
相差863.236516倍

经过Cython简单的处理,同样的代码,运行效率提高了800多倍。刺不刺激?
可见在作矩阵计算或者循环次数较多时,Cython具有较大作用。

参考