由于对 matplotlib 的了解不多,最开始我以为是 matplotlib 显示图像过慢导致了帧率上不去,打印出代码的用时后发现不是 matplotlib 的问题。因此我也使用了 PyQt5 对图像进行显示,结果依然是 1~2 帧的处理速度。因为只是换用了 Qt 的界面进行显示,逻辑处理的代码依然沿用的 matplotlib.animation 提供的方法,所以并没有本质上的区别。这段用 Qt 显示图片的代码来自于 github matplotlib issue,我对其进行了一些适配。
使用 Numpy 的数组处理 api我们知道,显示图片这么慢的原因就是在于 Python 处理 2096 * 150 这个两层循环占用了大量时间。接下来我们换用一种 numpy 的 reshape 方法将文件中的像素数据读取到内存中。注意 reshape 方法接收一个 ndarray 对象。我这种每帧数据创造一个 ndarray 数组的方法可能会存在内存泄漏的风险,实际上可以调用一个 ndarray 数组对象的 reshape 方法。这里不再深究。
重新定义一个用于动态显示图片的函数 optAnimateFromData,将其作为参数传递个 FuncAnimation:
def optAnimateFromData(i): e1 = cv.getTickCount() frame = next( records ) # one image data img = np.reshape( np.array( list( frame ), dtype = np.uint8 ), ( HEIGHT, WIDTH, CHANNELS ) ) img[ : , : , 3] = 255 ax1.clear() ax1.imshow( img ) e2 = cv.getTickCount() elapsed = ( e2 - e1 ) / cv.getTickFrequency() print( "FPS: %.2f, Used time: %.3f" % (1 / elapsed, elapsed )) a = animation.FuncAnimation( fig, optAnimateFromData, interval=30 )效果如下,可以看到使用 numpy 的 reshape 方法后,处理用时大幅减少,帧率可以达到 8~9 帧。然而经过优化后的处理速度仍然是比较慢的:
优化过的代码执行结果 使用 Numpy 提供的 memmap
在用 Python 进行机器学习的过程中,发现如果完全使用 Python 的话,很多运算量大的程序也是可以跑的起来的,所以我确信可以用 Python 解决我的这个问题。在我不懈努力下找到 Numpy 提供的 memmap api,这个 API 以数组的方式建立硬盘文件到内存的映射,使用这个 API 后程序就简单一些了:
cv.namedWindow("file") count = 0 start = time.time() try: number = 1 while True: e1 = cv.getTickCount() img = np.memmap(filename=FILE_NAME, dtype=np.uint8, shape=SHAPE, mode="r+", offset=count ) count += PACK_SIZE cv.imshow( "file", img ) e2 = cv.getTickCount() elapsed = ( e2 - e1 ) / cv.getTickFrequency() print("FPS: %.2f Used time: %.3f" % (number / elapsed, elapsed )) key = cv.waitKey(20) if key == 27: # exit on ESC break except StopIteration: pass finally: end = time.time() print( 'File Data read: {:.2f}Gb'.format( count / 1024 / 1024 / 1024), ' time used: {:.2f}s'.format( end - start ) ) cv.destroyAllWindows()将 memmap 读取到的数据 img 直接显示在窗口中 cv.imshow( "file", img),每一帧打印出显示该帧所用的时间,最后显示总的时间和读取到的数据大小:
执行效率最高的结果
读取速度非常快,每帧用时只需几毫秒。这样的处理速度完全可以满足 60FPS 的需求。
总结Python 语言写程序非常方便,但是原生的 Python 代码执行效率确实不如 C++,当然了,比 JS 还是要快一些。使用 Python 开发一些性能要求高的程序时,要么使用 Numpy 这样的库,要么自己编写一个 C 语言库供 Python 调用。在实验过程中,我还使用 Flask 读取文件后以流的形式发送的浏览器,让浏览器中的 JS 文件进行显示,不过同样存在着很严重的性能问题和内存泄漏问题。这个过程留到之后再讲。
本文中的相应代码可以在 github 上查看。
Referenceopencv
matplotlib animation
numpy
numpy reshape
memmap
matplotlib issue on github