gpt4 book ai didi

浅析Python 读取图像文件的性能对比

转载 作者:qq735679552 更新时间:2022-09-28 22:32:09 25 4
gpt4 key购买 nike

CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.

这篇CFSDN的博客文章浅析Python 读取图像文件的性能对比由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.

使用 python 读取一个保存在本地硬盘上的视频文件,视频文件的编码方式是使用的原始的 rgba 格式写入的,即无压缩的原始视频文件。最开始直接使用 python 对读取到的文件数据进行处理,然后显示在 matplotlib 窗口上,后来发现视频播放的速度比同样的处理逻辑的 c++ 代码慢了很多,尝试了不同的方法,最终实现了在 python 中读取并显示视频文件,帧率能够达到 120 fps 以上.

读取一帧图片数据并显示在窗口上 。

最简单的方法是直接在 python 中读取文件,然后逐像素的分配 rgb 值到窗口中,最开始使用的是 matplotlib 的 pyplot 组件.

一些用到的常量:

?
1
2
3
4
5
file_name = "i:/video.dat"
width = 2096
height = 150
channels = 4
pack_size = width * height * channels

每帧图片的宽度是 2096 个像素,高度是 150 个像素,channels 指的是 rgba 四个通道,因此 pack_size 的大小就是一副图片占用空间的字节数.

首先需要读取文件。由于视频编码没有任何压缩处理,大概 70s 的视频(每帧约占 1.2m 空间,每秒 60 帧)占用达 4gb 的空间,所以我们不能直接将整个文件读取到内存中,借助 python functools 提供的  partial 方法,我们可以每次从文件中读取一小部分数据,将 partial 用 iter 包装起来,变成可迭代的对象,每次读取一帧图片后,使用 next 读取下一帧的数据,接下来先用这个方法将保存在文件中的一帧数据读取显示在窗口中.

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
with open ( file , 'rb' ) as f:
   e1 = cv.gettickcount()
   records = iter ( partial( f.read, pack_size), b'' ) # 生成一个 iterator
   frame = next ( records ) # 读取一帧数据
   img = np.zeros( ( height, width, channels ), dtype = np.uint8)
   for y in range ( 0 , height):
     for x in range ( 0 , width ):
       pos = (y * width + x) * channels
       for i in range ( 0 , channels - 1 ):
         img[y][x][i] = frame[ pos + i ]
       img[y][x][ 3 ] = 255
   plt.imshow( img )
   plt.tight_layout()
   plt.subplots_adjust(left = 0 , right = 1 , top = 1 , bottom = 0 )
   plt.xticks([])
   plt.yticks([])
   e2 = cv.gettickcount()
   elapsed = ( e2 - e1 ) / cv.gettickfrequency()
   print ( "time used: " , elapsed )
   plt.show()

需要说明的是,在保存文件时第 4 个通道保存的是透明度,因此值为 0,但在 matplotlib (包括 opencv)的窗口中显示时第 4 个通道保存的一般是不透明度。我将第 4 个通道直接赋值成 255,以便能够正常显示图片.

这样就可以在我们的窗口中显示一张图片了,不过由于图片的宽长比不协调,使用 matplotlib 绘制出来的窗口必须要缩放到很大才可以让图片显示的比较清楚.

为了方便稍后的性能比较,这里统一使用 opencv 提供的 gettickcount 方法测量用时。可以从控制台中看到显示一张图片,从读取文件到最终显示大概要用 1.21s 的时间。如果我们只测量三层嵌套循环的用时,可以发现有 0.8s 的时间都浪费在循环上了.

浅析Python 读取图像文件的性能对比

读取并显示一帧图片用时 1.21s 。

浅析Python 读取图像文件的性能对比

在处理循环上用时 0.8s 。

约百万级别的循环处理,同样的代码放在 c++ 里面性能完全没有问题,在 python 中执行起来就不一样了。在 python 中这样的处理速度最多就 1.2 fps。我们暂时不考虑其他方法进行优化,而是将多帧图片动态的显示在窗口上,达到播放视频的效果.

连续读取图片并显示 。

这时我们继续读取文件并显示在窗口上,为了能够动态的显示图片,我们可以使用  matplotlib.animation 动态显示图片,之前的程序需要进行相应的改动

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
fig = plt.figure()
ax1 = fig.add_subplot( 1 , 1 , 1 )
try :
   img = np.zeros( ( height, width, channels ), dtype = np.uint8)
   f = open ( file_name, 'rb' )
   records = iter ( partial( f.read, pack_size ), b'' )
  
   def animatefromdata(i):
     e1 = cv.gettickcount()
     frame = next ( records ) # drop a line data
     for y in range ( 0 , height ):
       for x in range ( 0 , width ):
         pos = (y * width + x) * channels
         for i in range ( 0 , channels - 1 ):
           img[y][x][i] = frame[ pos + i]
         img[y][x][ 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, animatefromdata, interval = 30 ) # 这里不要省略掉 a = 这个赋值操作
   plt.tight_layout()
   plt.subplots_adjust(left = 0 , right = 1 , top = 1 , bottom = 0 )
   plt.xticks([])
   plt.yticks([])
   plt.show()
except stopiteration:
   pass
finally :
   f.close()

和第 1 部分稍有不同的是,我们显示每帧图片的代码是在 animatefromdata 函数中执行的,使用 matplotlib.animation.funcanimation 函数循环读取每帧数据(给这个函数传递的 interval = 30 这个没有作用,因为处理速度跟不上)。另外值得注意的是不要省略掉 a = animation.funcanimation( fig, animatefromdata, interval=30 ) 这一行的赋值操作,虽然不太清楚原理,但是当我把 a = 删掉的时候,程序莫名的无法正常工作了.

控制台中显示的处理速度:

浅析Python 读取图像文件的性能对比

由于对 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:

?
1
2
3
4
5
6
7
8
9
10
11
12
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 帧。然而经过优化后的处理速度仍然是比较慢的:

浅析Python 读取图像文件的性能对比

优化过的代码执行结果 。

使用 numpy 提供的 memmap 。

在用 python 进行机器学习的过程中,发现如果完全使用 python 的话,很多运算量大的程序也是可以跑的起来的,所以我确信可以用 python 解决我的这个问题。在我不懈努力下找到 numpy 提供的  memmap api,这个 api 以数组的方式建立硬盘文件到内存的映射,使用这个 api 后程序就简单一些了:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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),每一帧打印出显示该帧所用的时间,最后显示总的时间和读取到的数据大小:

浅析Python 读取图像文件的性能对比

执行效率最高的结果 。

读取速度非常快,每帧用时只需几毫秒。这样的处理速度完全可以满足 60fps 的需求.

总结 。

python 语言写程序非常方便,但是原生的 python 代码执行效率确实不如 c++,当然了,比 js 还是要快一些。使用 python 开发一些性能要求高的程序时,要么使用 numpy 这样的库,要么自己编写一个 c 语言库供 python 调用。在实验过程中,我还使用 flask 读取文件后以流的形式发送的浏览器,让浏览器中的 js 文件进行显示,不过同样存在着很严重的性能问题和内存泄漏问题。这个过程留到之后再讲.

本文中的相应代码可以在 github 上查看.

reference 。

functools 。

partial 。

opencv 。

matplotlib animation 。

numpy 。

numpy reshape 。

memmap 。

matplotlib issue on github 。

C 语言扩展 。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我.

原文链接:https://www.cnblogs.com/brifuture/p/10116553.html 。

最后此篇关于浅析Python 读取图像文件的性能对比的文章就讲到这里了,如果你想了解更多关于浅析Python 读取图像文件的性能对比的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com