初遇Python-numpy使用

因工作上的原因,偶尔会接触到一些图像处理的活。作为一名Java出身的鶸,很早就关注python。也借着工作的机会,偶尔上手玩了玩。本篇blog主要记载今天趟过的坑。

起初一直愉快地使用Opencv进行简单地图像处理开发,比如说使用对比度判断图像的清晰与模糊,利用pHash计算图像相似度(后来被大佬教学说机器学习更好用)等。这些都是在jpg,png等通用格式上进行处理。

直到需要处理raw image时,才犯了难。Opencv貌似没有API去处理raw图,可能是因为我没找到。于是便开始了折腾之路。

Raw image其实就是一个二进制文件,除了pixel value之外不包含任何其他的图像信息。因此,起初我使用了直接读二进制数据的方法,在用struct对字节进行处理,得到图像矩阵。

1
2
3
4
5
6
#example
with open(rawimage,'rb') as raw:
while True:
temp = raw.read(2) //如果raw image为8bit,则为1
temp = struct.unpack('<h',temp)
//statement

同时处理两张raw图进行相关的计算。上述代码在处理2832*2128的size的raw图大约需要5s以上。当然我认为这并不是一个很满意的值。但在我苦思冥想,按照自己的想法对内循环,变量等进行优化的时候,发现反而耗时越来越多。

直到偶然瞥到一句话,虽说不中肯,但是至少给了我一个提醒:Python能用外部库的不要自己实现。

于是,我铁了心要优化这5s多的耗时。虽说python比C要满,但C处理起来只是一个眨眼的功夫,python却需要5s以上,有点太夸张了。

之后就翻到了一直在使用的numpy,原本以为numpy只是一个矩阵操作的库,现在看来好像能做的事情比我想象的多得多。

查阅相关资料后,了解了numpy的frombytes,fromfile,fromstring函数功能,觉得这正是我想要的,就把代码撸了进去。

1
2
3
4
#example
with open(rawimage,'rb') as raw:
data = numpy.fromfile(file,dtype=uint16) //如果raw image为8bit,则为uint8
//statement

我兴高采烈地更改程序,并期待地运行起来,结果出乎意料:15s以上的耗时。

我彻底懵了。明明fromfile经过我的测试,读取raw图只需要不到1s的时间,怎么反而时间还多了3倍不止。

心情苦闷地对代码进行分段的耗时测试之后,终于发现了问题所在,在对numpy.fromfile返回的data进行遍历时,整整耗时了13s。

此时我才知道,遍历一个百万数量级元素的ndarray耗时如此巨大;但遍历同等数量级的dict却非常迅速。我意识到,我对ndarray的遍历方式是错误的。为了存储两张raw图的计算结果,我创建了一个同等长度的一维数组,并使用index进行遍历,同时访问data进行计算。而这种data[index]的访问方式,带来了过多的开销。

因为要和一维数组对齐的缘故,最后我并没有尝试nditer,而是将ndarray转换成list进行处理。做之前我非常担心转换的开销,但实际表现好于我的预期。整个函数的耗时没有因为这一步而有太大的变化。

经过这些修改之后,程序的耗时从5s+优化到了1s+,作为初入python的鶸,这种提升还是让自己心满意足的。

不过也从中得知现在自己对于python学习,处于要用什么才学什么的阶段,没有系统地学习导致了自己不懂python的语言特性,进了很多坑。

有时间还是要好好系统地学习一下,至少,numpy的文档得好好读了。