一、背景

本人准备用python做图像和视频编辑的操作,却发现opencv和PIL的效率并不是很理想,并且同样的需求有多种不同的写法并有着不同的效率。见全网并无较完整的效率对比文档,遂决定自己丰衣足食。

二、目的

本篇文章将对Python下的opencv接口函数及PIL(Pillow)函数的常用部分进行逐个运行并计时(多次测算取平均时间和最短时间,次数一般在100次以上),并简单使用numba、ctypes、cython等方法优化代码。

三、测试方法及环境

1.硬件

CPU:Intel(R) Core(TM) i3-3220 CPU @ 3.30GHz 3.30 GHz

内存:4.00 GB

硬盘:ATA WDC WD5000AAKX-7 SCSI Disk Device

2.软件:

操作系统:Windows 7 Service Pack 1 Ultimate 64bit zh-cn

Python解释器:3.7.5 64bit (provided by Anaconda)

各模块:皆为最新

(事情有所变化,暂时使用下面机房电脑的配置进行测试)

1.硬件

CPU:Intel(R) Xeon(R) Silver 4116 CPU @ 2.10GHz 2.10 GHz

内存:3.00 GB

硬盘:VMware Virtual disk SCSI Disk Service

2.软件:

操作系统:Windows 7 Service Pack 1 Ultimate 64bit zh-cn (powered by VMware Horizon View Client)

Python解释器:3.7.3 64bit (provided by Anaconda)

各模块:皆为最新

四、具体实现

1.待测试函数

以下定义新建的视频规定为MP4格式、mp4v编码、1920*1080尺寸、60帧速率;定义新建的图片为JPG格式、1920*1080尺寸、RGB通道。

根据实际需要(其实是我自己的需要),先暂定测试以下函数[1][2]:

1)创建视频

vw = cv2.VideoWriter('out.mp4', cv2.VideoWriter_fourcc(*'mp4v'), 60, (1920, 1080)) # Return MP4 video object

2)视频帧读取(视频不好做测试数据,故使用了手头上现成的。in.mp4参数:时长27秒,尺寸1920x1080,数据速率17073kbps,总比特率17331kbps,帧速率29fps,大小55.7MB)

cap = cv2.VideoCapture('in.mp4')

while cap.isOpened():
ret, frame = cap.read() # frame return a numpy.ndarray object (WRITEABLE) with RGB of pixels
if not ret: # Return True when read operation is successful
break # Read operation fails and break cap.release()

3)视频帧写入[3] (PS:为什么Opencv官方教程中没有这个函数...)

vw.write(frame)

4)写入视频(后来发现这个应该类似于file.close(),只是一个释放文件对象的过程,并不是真的在这个时候写入所有的数据。之前看见在release之前文件是空的应该是数据还没有从内存写入磁盘导致的)

vw.release()

5)创建图片 ( matrix & pillow object )

# Matrix
arr = np.zeros((1080, 1920, 3), dtype=np.uint8) # numpy中xy貌似是颠倒的,于是长1920宽1080的图像输出的shape应该是1080x1920,第三维度3表示图片通道为RGB
# Return a numpy.ndarray object (WRITEABLE) # Pillow
img = Image.new('RGB', (1920, 1080)) # 这里的xy没有颠倒

6)图片读取(opencv & pillow)(使用新建的图片,满足上面的定义,大小33kb)

# OpenCV
arr = cv2.imread('in.jpg') # Notice that OpenCV don't support ALPHA channel # Pillow
img = Image.open('in.jpg') # Return a PIL.Image.Image object

7)图片数据结构转换

arr1 = list(img.im) # Return a list

arr2 = np.asarray(img) # Return a np.ndarray object (NOT WRITEABLE) (Shallow copy)

arr3 = np.array(img) # Return a np.ndarray object (WRITEABLE) (Deep copy)

8)图片点操作(matrix & pillow object )

# Matrix
arr3[0][0] = (255, 255, 255) # Pillow
img.putpixel((0, 0), (255, 255, 255)) # Putpixel draw = ImageDraw.Draw(img) # ImageDraw.Point
draw.point((0, 0), (255, 255, 255)) # PS: OpenCV don't has a function that draw a pixel directly so we don't show the code here

9)图片其他绘图操作(matrix & pillow object & opencv )

这里我们测试画直线、画矩形、画圆(不包括matrix)、画椭圆操作(不包括matrix)、绘制文字(不包括matrix)。

注:pillow中默认绘制的图形都是实心的[4],而opencv要设置线宽为负值才是实心的[5]。

### Line
# Matrix
for x in range(100, 500):
arr3[100][x] = (255, 255, 255) # 注意到numpy的颠倒 # Pillow
draw.line((100, 100, 500, 100), (255, 255, 255)) # OpenCV
cv2.line(arr, (100, 100), (500, 100), (255, 255, 255), 1) # 最后的1表示线宽 ### Rectangle
# Matrix
for x in range(100, 500):
for y in range(100, 500):
arr3[y][x] = (255, 255, 255) # Pillow
draw.rectangle((100, 100, 500, 500), (255, 255, 255)) # OpenCV
cv2.rectangle(arr, (100, 100), (500, 500), (255, 255, 255), -1) ### Circle
# Pillow
draw.arc((100, 100, 500, 500), 0, 360, (255, 255, 255)) # PIL.ImageDraw.Draw.arc
# arc方法前一个四元元组表示圆弧的左上点右下点,这里表示半径200、中心(300, 300);后面两个整数表示度数(0-360表示整个圆) draw.ellipse((100, 100, 500, 500), (255, 255, 255)) # PIL.ImageDraw.Draw.ellipse
# ellipse方法同样表示两点 # OpenCV
cv2.circle(arr, (300, 300), 200, (255, 255, 255), -1) # cv2.circle
# 与Pillow不同的是,这里读取的是中心点和半径,更符合正常的习惯;1表示线宽,如果是-1则是实心圆 cv2.ellipse(arr, (300, 300), (200, 200), 0, 0, 360, (255, 255, 255), -1) # cv2.ellipse
# 这里第一个二元组是椭圆中心,第二个二元组分别表示半长轴长和半短轴长(注:中文文档漏掉了“半”字),后面三个参数分别表示椭圆本身逆时针旋转角(相当于坐标轴旋转)、起始角度和终止角度(0-360表示整个圆) ### Ellipse
# Pillow
draw.ellipse((100, 100, 700, 500), (255, 255, 255)) # 表示椭圆中心(400, 300),半长轴300,半短轴200 # OpenCV
cv2.ellipse(arr, (400, 300), (300, 200), 0, 0, 360, (255, 255, 255), -1) ### Text
# Pillow
font = ImageFont.truetype('simkai.ttf', 32) # 楷体,字号32
draw.text((100, 100), 'Hello, world!', (255, 255, 255), font) # 这里的坐标是左上角 # OpenCV
font = cv2.FONT_HERSHEY_SIMPLEX
cv2.putText(arr, 'Hello, world!', (100, 200), font, 2, (255, 255, 255), 1, cv2.LINE_AA) # 这里的坐标是左下角,1表示线宽(cv2不支持中文输出,故不测试中文)

其中opencv的字体参数参考:[6]

10)图片其他操作

11)写入图片( Pillow & OpenCV)

# Pillow
img.save('out.jpg') # OpenCV
cv2.imwrite('out.jpg', arr) # Read from cv2.imread cv2.imwrite('out.jpg', arr2) # np.asarray cv2.imwrite('out.jpg', arr3) # np.array

2.时间计算工具

这里的时间计算工具用一个类实现给定次数的循环智能循环(自动控制循环次数)的功能,并能给出每次循环的函数返回值、循环次数、平均时间、最短时间、最长时间、总共用时。

对于自动判断循环次数的算法参考了Python的timeit模块源码(autorange函数)[7]:

 # -*- coding: utf-8 -*-

 import time
import cv2
from PIL import Image, ImageDraw, ImageFont
import numpy as np # Class
class FunctionTimer(object):
MAX_WAIT_SEC = 0.5
INF = 2147483647
SMART_LOOP = -1 def __init__(self, timer=None, count=None):
self._timer = timer if timer != None else time.perf_counter
self._count = count if count != None else 100 def _get_single_time(self, func, *args, **kwargs):
s = self._timer()
ret = func(*args, **kwargs)
f = self._timer()
return ret, f - s def _get_repeat_time(self, number, func, *args, **kwargs):
time_min, time_max, time_sum = self.INF, 0, 0
for i in range(number):
ret, delta = self._get_single_time(func, *args, **kwargs)
time_min = min(time_min, delta)
time_max = max(time_max, delta)
time_sum += delta
return func, ret, number, time_sum / number, time_min, time_max, time_sum def gettime(self, func, *args, **kwargs):
if self._count != self.SMART_LOOP:
return self._get_repeat_time(self._count, func, *args, **kwargs)
else:
# Arrange loop count automatically
# Refer to Lib/timeit.py
i = 1
while True:
for j in 1, 2, 5:
number = i * j
func, ret, number, time_ave, time_min, time_max, time_sum = self._get_repeat_time(number, func, *args, **kwargs)
if time_sum >= self.MAX_WAIT_SEC:
return func, ret, number, time_ave, time_min, time_max, time_sum
i *= 10 def better_print(self, params):
func, ret, count, ave, minn, maxn, sumn = params
print('========================================')
print(' Function name:')
print(' ' + func.__repr__())
print('========================================')
print(' Function has the return content below:')
print(' ' + ret.__name__)
print('========================================')
print(' Summary of Function Timer:')
print(' Count of loops: {}'.format(count))
print(' Average time of loops: {} (sec)'.format(ave))
print(' Minimum of every loop time: {} (sec)'.format(minn))
print(' Maximum of every loop time: {} (sec)'.format(maxn))
print(' Total time of loops: {} (sec)'.format(sumn))
print('========================================') # Function
def testfunc(x=10000000):
for i in range(x):
pass
return i # Main Function
timer = FunctionTimer()

测试结果(将整个文件作为模块以op为名字调用):

In [168]: op.timer.better_print(op.timer.gettime(op.testfunc, 10000))
========================================
Function name:
testfunc
========================================
Function has the return content below:
9999
========================================
Summary of Function Timer:
Count of loops: 100
Average time of loops: 0.00039519199983260476 (sec)
Minimum of every loop time: 0.0002532999988034135 (sec)
Maximum of every loop time: 0.0010392999993200647 (sec)
Total time of loops: 0.03951919998326048 (sec)
======================================== In [169]: op.timer.better_print(op.timer.gettime(op.testfunc, 100000))
========================================
Function name:
testfunc
========================================
Function has the return content below:
99999
========================================
Summary of Function Timer:
Count of loops: 100
Average time of loops: 0.0029596240000137187 (sec)
Minimum of every loop time: 0.002567899999121437 (sec)
Maximum of every loop time: 0.006201700000019628 (sec)
Total time of loops: 0.29596240000137186 (sec)
======================================== In [170]: op.timer.better_print(op.timer.gettime(op.testfunc, 10))
========================================
Function name:
testfunc
========================================
Function has the return content below:
9
========================================
Summary of Function Timer:
Count of loops: 100
Average time of loops: 9.039999349624849e-07 (sec)
Minimum of every loop time: 7.999988156370819e-07 (sec)
Maximum of every loop time: 2.6999987312592566e-06 (sec)
Total time of loops: 9.03999934962485e-05 (sec)
========================================

3.完整代码

 # opencv_pil_time.py

 # -*- coding: utf-8 -*-

 import time
import cv2
from PIL import Image, ImageDraw, ImageFont
import numpy as np # Class
class FunctionTimer(object):
MAX_WAIT_SEC = 0.5
INF = 2147483647
SMART_LOOP = -1 def __init__(self, timer=None, count=None):
self._timer = timer if timer != None else time.perf_counter
self._count = count if count != None else 100 def _get_single_time(self, func, *args, **kwargs):
s = self._timer()
ret = func(*args, **kwargs)
f = self._timer()
return ret, f - s def _get_repeat_time(self, number, func, *args, **kwargs):
time_min, time_max, time_sum = self.INF, 0, 0
for i in range(number):
ret, delta = self._get_single_time(func, *args, **kwargs)
time_min = min(time_min, delta)
time_max = max(time_max, delta)
time_sum += delta
return func, ret, number, time_sum / number, time_min, time_max, time_sum def gettime(self, func, *args, **kwargs):
if self._count != self.SMART_LOOP:
return self._get_repeat_time(self._count, func, *args, **kwargs)
else:
# Arrange loop count automatically
# Refer to Lib/timeit.py
i = 1
while True:
for j in 1, 2, 5:
number = i * j
func, ret, number, time_ave, time_min, time_max, time_sum = self._get_repeat_time(number, func, *args, **kwargs)
if time_sum >= self.MAX_WAIT_SEC:
return func, ret, number, time_ave, time_min, time_max, time_sum
i *= 10 def better_print(self, params):
func, ret, count, ave, minn, maxn, sumn = params
print('========================================')
print(' Function name:')
print(' ' + func.__name__)
print('========================================')
print(' Function has the return content below:')
print(' ' + ret.__repr__())
print('========================================')
print(' Summary of Function Timer:')
print(' Count of loops: {}'.format(count))
print(' Average time of loops: {} (sec)'.format(ave))
print(' Minimum of every loop time: {} (sec)'.format(minn))
print(' Maximum of every loop time: {} (sec)'.format(maxn))
print(' Total time of loops: {} (sec)'.format(sumn))
print('========================================') # Function
# Debug
def testfunc(x=10000000):
for i in range(x):
pass
return i # Test Function
def task_1():
vw = cv2.VideoWriter('out.mp4', cv2.VideoWriter_fourcc(*'mp4v'), 60, (1920, 1080)) def task_2():
cap = cv2.VideoCapture('in.mp4')
while cap.isOpened():
ret, frame = cap.read()
if not ret:
break
cap.release() def task_3(vw, frame): # Use a new blank video file when testing
vw.write(frame) def task_4(vw):
vw.release() def task_5_matrix():
arr = np.zeros((1080, 1920, 3), dtype=np.uint8) def task_5_pillow():
img = Image.new('RGB', (1920, 1080)) def task_6_opencv():
arr = cv2.imread('in.jpg') def task_6_pillow():
img = Image.open('in.jpg') def task_7_list(img):
arr1 = list(img.im) def task_7_asarray(img):
arr2 = np.asarray(img) def task_7_array(img):
arr3 = np.array(img) def task_8_matrix(arr3):
arr3[0][0] = (255, 255, 255) def task_8_pillow_putpixel(img):
img.putpixel((0, 0), (255, 255, 255)) def task_8_pillow_point(draw):
draw.point((0, 0), (255, 255, 255)) def task_9_line_matrix(arr3):
for x in range(100, 500):
arr3[100][x] = (255, 255, 255) def task_9_line_pillow(draw):
draw.line((100, 100, 500, 100), (255, 255, 255)) def task_9_line_opencv(arr):
cv2.line(arr, (100, 100), (500, 100), (255, 255, 255), 1) def task_9_rectangle_matrix(arr3):
for x in range(100, 500):
for y in range(100, 500):
arr3[y][x] = (255, 255, 255) def task_9_rectangle_pillow(draw):
draw.rectangle((100, 100, 500, 500), (255, 255, 255)) def task_9_rectangle_opencv(arr):
cv2.rectangle(arr, (100, 100), (500, 500), (255, 255, 255), -1) def task_9_circle_pillow_arc(draw):
draw.arc((100, 100, 500, 500), 0, 360, (255, 255, 255)) def task_9_circle_pillow_ellipse(draw):
draw.ellipse((100, 100, 500, 500), (255, 255, 255)) def task_9_circle_opencv_circle(arr):
cv2.circle(arr, (300, 300), 200, (255, 255, 255), -1) def task_9_circle_opencv_ellipse(arr):
cv2.ellipse(arr, (300, 300), (200, 200), 0, 0, 360, (255, 255, 255), -1) def task_9_ellipse_pillow(draw):
draw.ellipse((100, 100, 700, 500), (255, 255, 255)) def task_9_ellipse_opencv(arr):
cv2.ellipse(arr, (400, 300), (300, 200), 0, 0, 360, (255, 255, 255), -1) def task_9_text_pillow(draw, font):
draw.text((100, 100), 'Hello, world!', (255, 255, 255), font) def task_9_text_opencv(arr, font):
cv2.putText(arr, 'Hello, world!', (100, 200), font, 2, (255, 255, 255), 1, cv2.LINE_AA) def task_10():
pass def task_11_pillow(img):
img.save('out.jpg') def task_11_opencv_imread(arr):
cv2.imwrite('out.jpg', arr) def task_11_opencv_asarray(arr2):
cv2.imwrite('out.jpg', arr2) def task_11_opencv_array(arr3):
cv2.imwrite('out.jpg', arr3) # Main Function
if __name__ == '__main__':
timer = FunctionTimer()
# timer.better_print(timer.gettime(func, *args, **kwargs))
timer.better_print(timer.gettime(task_1))
vw = cv2.VideoWriter('out.mp4', cv2.VideoWriter_fourcc(*'mp4v'), 60, (1920, 1080))
# timer.better_print(timer.gettime(task_2)) # task_2 takes up much time and we don't test it!
frame = np.zeros((1080, 1920, 3), dtype=np.uint8)
timer.better_print(timer.gettime(task_3, vw, frame))
timer.better_print(timer.gettime(task_4, vw))
timer.better_print(timer.gettime(task_5_matrix))
timer.better_print(timer.gettime(task_5_pillow))
timer.better_print(timer.gettime(task_6_opencv))
arr = cv2.imread('in.jpg')
timer.better_print(timer.gettime(task_6_pillow))
img = Image.new('RGB', (1920, 1080))
timer.better_print(timer.gettime(task_7_list, img))
timer.better_print(timer.gettime(task_7_asarray, img))
timer.better_print(timer.gettime(task_7_array, img))
arr2 = np.asarray(img)
arr3 = np.array(img)
timer.better_print(timer.gettime(task_8_matrix, arr3))
timer.better_print(timer.gettime(task_8_pillow_putpixel, img))
draw = ImageDraw.Draw(img)
timer.better_print(timer.gettime(task_8_pillow_point, draw))
timer.better_print(timer.gettime(task_9_line_matrix, arr3))
timer.better_print(timer.gettime(task_9_line_pillow, draw))
timer.better_print(timer.gettime(task_9_line_opencv, arr))
timer.better_print(timer.gettime(task_9_rectangle_matrix, arr3))
timer.better_print(timer.gettime(task_9_rectangle_pillow, draw))
timer.better_print(timer.gettime(task_9_rectangle_opencv, arr))
timer.better_print(timer.gettime(task_9_circle_pillow_arc, draw))
timer.better_print(timer.gettime(task_9_circle_pillow_ellipse, draw))
timer.better_print(timer.gettime(task_9_circle_opencv_circle, arr))
timer.better_print(timer.gettime(task_9_circle_opencv_ellipse, arr))
timer.better_print(timer.gettime(task_9_ellipse_pillow, draw))
timer.better_print(timer.gettime(task_9_ellipse_opencv, arr))
font = ImageFont.truetype('simkai.ttf', 32)
timer.better_print(timer.gettime(task_9_text_pillow, draw, font))
font = cv2.FONT_HERSHEY_SIMPLEX
timer.better_print(timer.gettime(task_9_text_opencv, arr, font))
timer.better_print(timer.gettime(task_11_pillow, img))
timer.better_print(timer.gettime(task_11_opencv_imread, arr))
timer.better_print(timer.gettime(task_11_opencv_asarray, arr2))
timer.better_print(timer.gettime(task_11_opencv_array, arr3))

在此我先停一下,各位可以猜猜哪种方式更胜一筹。

flag

flag

flag

flag

flag

flag

flag

flag

flag

flag

flag

flag

flag

五、结果

1.现象

其中task_2(读取视频文件)占用时间过多,我们不予循环测试,下面的结果栏中将给出单次运行的结果(取第一次)。

In [10]: import time

In [11]: s = time.perf_counter(); op.task_2(); f = time.perf_counter(); f - s
Out[11]: 8.617467135000027 In [12]: s = time.perf_counter(); op.task_2(); f = time.perf_counter(); f - s
Out[12]: 8.663589091999995

cmder.exe中运行结果:

E:\test1
$ python3 opencv_pil_time.py
========================================
Function name:
task_1
========================================
Function has the return content below:
None
========================================
Summary of Function Timer:
Count of loops: 100
Average time of loops: 0.0016054189199999984 (sec)
Minimum of every loop time: 0.0013979550000000063 (sec)
Maximum of every loop time: 0.0057973939999999835 (sec)
Total time of loops: 0.16054189199999985 (sec)
========================================
========================================
Function name:
task_3
========================================
Function has the return content below:
None
========================================
Summary of Function Timer:
Count of loops: 100
Average time of loops: 0.013229802739999979 (sec)
Minimum of every loop time: 0.01082132600000002 (sec)
Maximum of every loop time: 0.018015121000000023 (sec)
Total time of loops: 1.3229802739999978 (sec)
========================================
========================================
Function name:
task_4
========================================
Function has the return content below:
None
========================================
Summary of Function Timer:
Count of loops: 100
Average time of loops: 2.1959869999998995e-05 (sec)
Minimum of every loop time: 3.109999999750812e-07 (sec)
Maximum of every loop time: 0.0021468490000000617 (sec)
Total time of loops: 0.0021959869999998993 (sec)
========================================
========================================
Function name:
task_5_matrix
========================================
Function has the return content below:
None
========================================
Summary of Function Timer:
Count of loops: 100
Average time of loops: 1.4977880000011101e-05 (sec)
Minimum of every loop time: 1.0263000000065858e-05 (sec)
Maximum of every loop time: 4.571699999988965e-05 (sec)
Total time of loops: 0.0014977880000011101 (sec)
========================================
========================================
Function name:
task_5_pillow
========================================
Function has the return content below:
None
========================================
Summary of Function Timer:
Count of loops: 100
Average time of loops: 0.0029445669399999997 (sec)
Minimum of every loop time: 0.0026519169999998926 (sec)
Maximum of every loop time: 0.00473345600000008 (sec)
Total time of loops: 0.29445669399999996 (sec)
========================================
========================================
Function name:
task_6_opencv
========================================
Function has the return content below:
None
========================================
Summary of Function Timer:
Count of loops: 100
Average time of loops: 0.02255292473999999 (sec)
Minimum of every loop time: 0.021661312000000432 (sec)
Maximum of every loop time: 0.032752587999999694 (sec)
Total time of loops: 2.255292473999999 (sec)
========================================
========================================
Function name:
task_6_pillow
========================================
Function has the return content below:
None
========================================
Summary of Function Timer:
Count of loops: 100
Average time of loops: 0.00025689415000005765 (sec)
Minimum of every loop time: 0.0001309319999993619 (sec)
Maximum of every loop time: 0.011476918999999697 (sec)
Total time of loops: 0.025689415000005766 (sec)
========================================
========================================
Function name:
task_7_list
========================================
Function has the return content below:
None
========================================
Summary of Function Timer:
Count of loops: 100
Average time of loops: 0.38457812533999997 (sec)
Minimum of every loop time: 0.3564736689999961 (sec)
Maximum of every loop time: 0.4698194010000005 (sec)
Total time of loops: 38.457812534 (sec)
========================================
========================================
Function name:
task_7_asarray
========================================
Function has the return content below:
None
========================================
Summary of Function Timer:
Count of loops: 100
Average time of loops: 0.007278045390000258 (sec)
Minimum of every loop time: 0.007068772000003776 (sec)
Maximum of every loop time: 0.007784698999998341 (sec)
Total time of loops: 0.7278045390000258 (sec)
========================================
========================================
Function name:
task_7_array
========================================
Function has the return content below:
None
========================================
Summary of Function Timer:
Count of loops: 100
Average time of loops: 0.010643305210000377 (sec)
Minimum of every loop time: 0.009964515000000063 (sec)
Maximum of every loop time: 0.011806892999999263 (sec)
Total time of loops: 1.0643305210000378 (sec)
========================================
========================================
Function name:
task_8_matrix
========================================
Function has the return content below:
None
========================================
Summary of Function Timer:
Count of loops: 100
Average time of loops: 2.8363499999528583e-06 (sec)
Minimum of every loop time: 1.5549999972108708e-06 (sec)
Maximum of every loop time: 4.1673999994884525e-05 (sec)
Total time of loops: 0.00028363499999528585 (sec)
========================================
========================================
Function name:
task_8_pillow_putpixel
========================================
Function has the return content below:
None
========================================
Summary of Function Timer:
Count of loops: 100
Average time of loops: 2.1925700001901305e-06 (sec)
Minimum of every loop time: 1.2439999963476112e-06 (sec)
Maximum of every loop time: 2.1769999996479328e-05 (sec)
Total time of loops: 0.00021925700001901305 (sec)
========================================
========================================
Function name:
task_8_pillow_point
========================================
Function has the return content below:
None
========================================
Summary of Function Timer:
Count of loops: 100
Average time of loops: 2.3574000000081697e-06 (sec)
Minimum of every loop time: 1.5549999972108708e-06 (sec)
Maximum of every loop time: 1.8971000002920846e-05 (sec)
Total time of loops: 0.00023574000000081696 (sec)
========================================
========================================
Function name:
task_9_line_matrix
========================================
Function has the return content below:
None
========================================
Summary of Function Timer:
Count of loops: 100
Average time of loops: 0.0004368183000000414 (sec)
Minimum of every loop time: 0.0004301160000039772 (sec)
Maximum of every loop time: 0.000561359000002426 (sec)
Total time of loops: 0.04368183000000414 (sec)
========================================
========================================
Function name:
task_9_line_pillow
========================================
Function has the return content below:
None
========================================
Summary of Function Timer:
Count of loops: 100
Average time of loops: 3.4956700000066122e-06 (sec)
Minimum of every loop time: 2.4879999998006497e-06 (sec)
Maximum of every loop time: 2.519200000250521e-05 (sec)
Total time of loops: 0.0003495670000006612 (sec)
========================================
========================================
Function name:
task_9_line_opencv
========================================
Function has the return content below:
None
========================================
Summary of Function Timer:
Count of loops: 100
Average time of loops: 3.5982899999709163e-06 (sec)
Minimum of every loop time: 2.4879999998006497e-06 (sec)
Maximum of every loop time: 4.727200000331777e-05 (sec)
Total time of loops: 0.0003598289999970916 (sec)
========================================
========================================
Function name:
task_9_rectangle_matrix
========================================
Function has the return content below:
None
========================================
Summary of Function Timer:
Count of loops: 100
Average time of loops: 0.1735227326999994 (sec)
Minimum of every loop time: 0.17267937900000163 (sec)
Maximum of every loop time: 0.19454626299999944 (sec)
Total time of loops: 17.35227326999994 (sec)
========================================
========================================
Function name:
task_9_rectangle_pillow
========================================
Function has the return content below:
None
========================================
Summary of Function Timer:
Count of loops: 100
Average time of loops: 3.0409819999803745e-05 (sec)
Minimum of every loop time: 2.9545000003849964e-05 (sec)
Maximum of every loop time: 7.153000000670318e-05 (sec)
Total time of loops: 0.0030409819999803744 (sec)
========================================
========================================
Function name:
task_9_rectangle_opencv
========================================
Function has the return content below:
None
========================================
Summary of Function Timer:
Count of loops: 100
Average time of loops: 6.522652000001016e-05 (sec)
Minimum of every loop time: 6.25109999958795e-05 (sec)
Maximum of every loop time: 0.0002674619999964989 (sec)
Total time of loops: 0.006522652000001017 (sec)
========================================
========================================
Function name:
task_9_circle_pillow_arc
========================================
Function has the return content below:
None
========================================
Summary of Function Timer:
Count of loops: 100
Average time of loops: 2.7626349999891885e-05 (sec)
Minimum of every loop time: 2.6745999996080627e-05 (sec)
Maximum of every loop time: 6.531100000017886e-05 (sec)
Total time of loops: 0.0027626349999891886 (sec)
========================================
========================================
Function name:
task_9_circle_pillow_ellipse
========================================
Function has the return content below:
None
========================================
Summary of Function Timer:
Count of loops: 100
Average time of loops: 0.0002000553400001337 (sec)
Minimum of every loop time: 0.00019841900000017176 (sec)
Maximum of every loop time: 0.0002512900000013474 (sec)
Total time of loops: 0.02000553400001337 (sec)
========================================
========================================
Function name:
task_9_circle_opencv_circle
========================================
Function has the return content below:
None
========================================
Summary of Function Timer:
Count of loops: 100
Average time of loops: 6.074186999960318e-05 (sec)
Minimum of every loop time: 5.815699999800472e-05 (sec)
Maximum of every loop time: 0.00016856299999545854 (sec)
Total time of loops: 0.006074186999960318 (sec)
========================================
========================================
Function name:
task_9_circle_opencv_ellipse
========================================
Function has the return content below:
None
========================================
Summary of Function Timer:
Count of loops: 100
Average time of loops: 6.716407000013192e-05 (sec)
Minimum of every loop time: 6.593300000190538e-05 (sec)
Maximum of every loop time: 0.00012471200000163662 (sec)
Total time of loops: 0.0067164070000131915 (sec)
========================================
========================================
Function name:
task_9_ellipse_pillow
========================================
Function has the return content below:
None
========================================
Summary of Function Timer:
Count of loops: 100
Average time of loops: 0.0002104615099997176 (sec)
Minimum of every loop time: 0.00020619399999333154 (sec)
Maximum of every loop time: 0.00040772399999866593 (sec)
Total time of loops: 0.021046150999971758 (sec)
========================================
========================================
Function name:
task_9_ellipse_opencv
========================================
Function has the return content below:
None
========================================
Summary of Function Timer:
Count of loops: 100
Average time of loops: 8.027900999998394e-05 (sec)
Minimum of every loop time: 7.837199999727318e-05 (sec)
Maximum of every loop time: 0.00020712799999955678 (sec)
Total time of loops: 0.008027900999998394 (sec)
========================================
========================================
Function name:
task_9_text_pillow
========================================
Function has the return content below:
None
========================================
Summary of Function Timer:
Count of loops: 100
Average time of loops: 0.0007998544599997359 (sec)
Minimum of every loop time: 0.0007778169999994589 (sec)
Maximum of every loop time: 0.0016240550000006237 (sec)
Total time of loops: 0.07998544599997359 (sec)
========================================
========================================
Function name:
task_9_text_opencv
========================================
Function has the return content below:
None
========================================
Summary of Function Timer:
Count of loops: 100
Average time of loops: 3.116865999970742e-05 (sec)
Minimum of every loop time: 3.0166999998471056e-05 (sec)
Maximum of every loop time: 9.610000000037644e-05 (sec)
Total time of loops: 0.0031168659999707415 (sec)
========================================
========================================
Function name:
task_11_pillow
========================================
Function has the return content below:
None
========================================
Summary of Function Timer:
Count of loops: 100
Average time of loops: 0.033835311859999495 (sec)
Minimum of every loop time: 0.03373037900000497 (sec)
Maximum of every loop time: 0.034273077999998236 (sec)
Total time of loops: 3.3835311859999493 (sec)
========================================
========================================
Function name:
task_11_opencv_imread
========================================
Function has the return content below:
None
========================================
Summary of Function Timer:
Count of loops: 100
Average time of loops: 0.028288081510000042 (sec)
Minimum of every loop time: 0.028133581999995272 (sec)
Maximum of every loop time: 0.02905974700000513 (sec)
Total time of loops: 2.828808151000004 (sec)
========================================
========================================
Function name:
task_11_opencv_asarray
========================================
Function has the return content below:
None
========================================
Summary of Function Timer:
Count of loops: 100
Average time of loops: 0.02815422919999975 (sec)
Minimum of every loop time: 0.0279864769999989 (sec)
Maximum of every loop time: 0.029095201000004067 (sec)
Total time of loops: 2.8154229199999747 (sec)
========================================
========================================
Function name:
task_11_opencv_array
========================================
Function has the return content below:
None
========================================
Summary of Function Timer:
Count of loops: 100
Average time of loops: 0.028195894160001414 (sec)
Minimum of every loop time: 0.028047434000001203 (sec)
Maximum of every loop time: 0.02866104100000655 (sec)
Total time of loops: 2.8195894160001416 (sec)
========================================

(很奇怪为什么循环次数都是100次,感觉可能timer算法有问题)

时间单位:秒,精确度:3位有效数字,制作成表格(红字表示所在子操作名中平均时间最短的函数,如若平均时间最短按照时间排列顺序依次比较)(图片读取一栏的红字标错位置了,应该打在pillow的下面)

2.结论

1)前四项由于没有对比就不多说了,不过感觉opencv读取视频的速度确实有些慢(6.5MB/s,90.8frame/s)。当然写入数据也很慢(75.8frame/s),不过尺寸不同,就不互相比较了。

2)创建图片操作numpy数组要比pillow的对象要快一些(也就两个数量级吧~)

3)数据结构转换中numpy比list快几乎是显然的hhh,其中asarray要比array略快一点,大概是因为array深复制而asarray浅复制;当然asarray的结果是not writable的,估计是因为image对象存储的数组本身就是只读的吧。如果只是为了读取图片方便塞视频里就用asarray。

4)没想到图片点操作里面numpy的索引赋值竟然比putpixel还要慢一点!真是大开眼界。。。果然pillow源码里面说“自带api要快一点”是真的。。。

5)图片读取、图片绘图绝大多数情况下pillow秒杀numpy和opencv,只有在写文字的时候opencv体现出比较大的效率优势,但是opencv的字体有很多限制,还是弃置了。(我手头上有一套字模,还是可以测试一下numpy写字速度的,不过估计还是要慢一些,而且字模做起来也比较臃肿,就不试了~)

6)写图片还是opencv要快一点点,当然asarray和array在多精确几个数字就是asarray快了,如果只有三位那就是array更快一点。

六、优化

(待续)

七、总结反思

这个项目我大概从一个月前就有想法了,最近一周一直在抽时间做,净时间估计都有十几个小时了。最后一天(11月16日)晚上我拖到12点,作业还没做完,困得要死,也就做了个大概--没有优化的部分,也没有表格,还因为事先没查好api返工了好几次。这件事让我深感个人的力量的薄弱 ,以及我自己水平的低下。

不过这次的项目让我掌握了多方面搜索数据(尤其是api)的能力,诸如找官方文档啊,看源码啊之类的,晦涩难懂的源代码和英文文档我也尽可能啃掉了,也算是一大进步了吧。

然后就是项目的内容。本次的测试我尽可能从自己能想到的角度给出足够多的实现方法来对比运行效率,孰优孰劣一下子就清楚了。不过也要看情况,比如说给定的数据全是数组,你要是为了追求图像处理函数的效率而全部转成pil对象,也并不是好的。除了时间效率的差距,我们也可以看出PIL的图像处理能力果然还是上等,opencv只是视频库附带一个简陋的图像处理能力,真正到解决图像问题时候还是应该选择PIL。

当然,这次的实验也有不科学的地方,诸如没有控制好无关变量,甚至可能导致相反的结果。我不是专业搞cs得,而且我还是高二生,实在无力全身心投入其中。实验方法带来的误差以及内容的错漏,尚希见谅!

最后希望各位能在这篇充满艰辛的博客中得到点什么。哪怕是一点处理编程项目时的教训而不是博客内容本身,我也心满意足了。


参考资料:

[1]Pillow (PIL Fork) 7.0.0.dev0 英文文档

[2]OpenCV Python Tutorials 翻译        OpenCV-Python Tutorials

[3]Python&OpenCV - 读写(read&write)视频(video) 详解 及 代码

[4]Python图像处理库PIL的ImageDraw模块介绍

[5]python opencv cv2.rectangle 参数含义

[6]python中cv2.putText参数详解

[7]Python 3.7 timeit

【Python | opencv+PIL】常见操作(创建、添加帧、绘图、读取等)的效率对比及其优化的更多相关文章

  1. python opencv 按一定间隔截取视频帧

    前言关于opencvOpenCV 是 Intel 开源计算机视觉库 (Computer Version) .它由一系列 C 函数和少量 C++ 类构成,实现了图像处理和计算机视觉方面的很多通用算法. ...

  2. python Opencv图像基础操作

    读取并显示图像 如果读取图像首先要导入OpenCV包,方法为: import cv2 读取并显示图像 img = cv2.imread("C:\test1.jpg") OpenCV ...

  3. python 字典的常见操作

    字典 字典的增删改查 字典的创建方式: # 创建字典类型 info = { 'name':'李白', ', 'sex':'男' } msg = { 'user01':'Longzeluola', 'u ...

  4. python opencv:像素操作

    图片的像素 像素:组成图片的单位 RGB:颜色由 RGB三种颜色组成 颜色深度:对于8bit的颜色深度来说,它可以表示的颜色范围是 0 ~ 255,对于RGB图片来说,8位颜色深度可以表示 (2^8) ...

  5. Python opencv PIL numpy base64互相转化

    PIL2numpy and numpy2PIL from PIL import Image import numpy image = Image.open('timg.jpeg')# image is ...

  6. python字符串的常见操作

    find: 根据指定字符串获取对应的下标, 如果找不到对应的数据返回-1, 这里的-1表示没有找到数据 my_str = "hello" # find: 根据指定字符串获取对应的下 ...

  7. python之列表常见操作

    list = [1,2,3,4,5,6,7,8,9,0,0,0,0,0] listSet = list.set(list)#将列表中的数据进行去重处理 此时listSet中的数据为[1,2,3,4,5 ...

  8. Python中字符串常见操作

    (1)find 查找 格式:mystr.find(str, start, end) 例如: mystr.find(str, start=0, end=len(mystr)) 作用:检测str是否包含在 ...

  9. 【代码学习】PYTHON字符串的常见操作

    一.字符串运算符 下表实例变量 a 值为字符串 "Hello",b 变量值为 "Python": 操作符 描述 实例 + 字符串连接 >>>a ...

随机推荐

  1. hadoop--presto安装部署

    系统环境:hadoop + hive已经配置完成 1.下载presto:https://repo1.maven.org/maven2/com/facebook/presto/presto-server ...

  2. [转载]virtual topology虚拟拓扑

    原文地址:topology虚拟拓扑">virtual topology虚拟拓扑作者:一丝尘埃 topology虚拟拓扑" title="[转载]virtual to ...

  3. JVM的内存配置参数

    JVM的结构问题:JVM分两块:PermanentSapce和HeapSpace, HeapSpace = [old + new{=Eden,from,to}] PermantSpace主要负责存放加 ...

  4. C语言--输入输出格式

    一.PTA实验作业 题目1:7-3 温度转换 本题要求编写程序,计算华氏温度150°F对应的摄氏温度.计算公式:C=5×(F−32)/9,式中:C表示摄氏温度,F表示华氏温度,输出数据要求为整型. 1 ...

  5. vue 页面间传值

    使用params传参 ,不能使用path 只能使用name 使用params传参,刷新参数会消失 router/index.js import Vue from 'vue' import Router ...

  6. golang gin解决跨域访问

    package middleware import ( "github.com/gin-gonic/gin" "net/http") func Cors() g ...

  7. python __new__

    1.__new__的作用是什么? 依照Python官方文档的说法,__new__方法主要是当你继承一些不可变的class时(比如int, str, tuple), 提供给你一个自定义这些类的实例化过程 ...

  8. 在Springmvc普通类@Autowired注入request为null解决方法

    在Springmvc普通类@Autowired注入request为null解决方法   在类中加入以下注入request对象的代码,运行时发现request为null,注入失败.在@Controlle ...

  9. PAT 甲级 1037 Magic Coupon (25 分) (较简单,贪心)

    1037 Magic Coupon (25 分)   The magic shop in Mars is offering some magic coupons. Each coupon has an ...

  10. WinSCP 上传文件至Cetos 7 后用户无权限

    WinSCP是一个支持SSH的SCP文件传输软件. 可以用Windows环境向Linux环境传输文件,今天给新的Elasticsearch 服务器(cetos 7 )部署新的集群节点的时候,发现传输后 ...