PIL vs Opencv
- 0.前言
- 1.简要介绍
- 1.1 PIL
- 1.2 Opencv
- 1.3 二者初始对比
- 2.准备工作
- 2.1 平台信息
- 2.2 安装PIL和Opencv
- 2.3 查看版本信息
- 2.4 验证安装成功
- 3. 二者比较
- 3.1 测试图片
- 3.2 用时比较
- 第一阶段:PIL 打开图片 + resize至416x416
- 第二阶段:cv2 打开图片 + resize至416x416
- 第三阶段:cv2 打开图片 + 颜色空间转换 + 转换成array + resize至416x416
- 第四阶段:cv2 打开图片 + resize至416x416 + 颜色空间转换 + 转换成array
- 完整测试代码:
- 4 分析
0.前言
最近在做关于图像预处理加速的东西,在部署深度学习方案要处理一些图像方面的问题的时候,是想要能够快速的进行图像读取和转换的库的。
我产生了一个疑问 —— Pillow 和 Opencv二者哪一个更好一些呢?于是我进行了一系列的测试。
1.简要介绍
1.1 PIL
PIL(Python Imaging Library)是Python一个强大方便的图像处理库,名气也比较大。不过只支持到Python 2.7。
Pillow是PIL的一个派生分支,但如今已经发展成为比PIL本身更具活力的图像处理库。
PIL库可以完成图像归档和图像处理两方面功能需求:
(1)图像归档:对图像进行批处理、生成图像预览、图像格式转换等;
(2)图像处理:图像基本处理、像素处理、颜色处理等。
1.2 Opencv
OpenCV的全称是Open Source Computer Vision Library,是一个跨平台的计算机视觉库。OpenCV是由英特尔公司发起并参与开发,以BSD许可证授权发行,可以在商业和研究领域中免费使用。OpenCV可用于开发实时的图像处理、计算机视觉以及模式识别程序。该程序库也可以使用英特尔公司的IPP进行加速处理。
OpenCV用 C++ 语言编写,它的主要接口也是C++语言,但是依然保留了大量的C语言接口。该库也有大量的Python, Java and MATLAB/OCTAVE (版本2.5)的接口。这些语言的API接口函数可以透过在线文档获取。现在也提供对于C#, Ch,Ruby的支持。所有新的开发和算法都是用C++接口。一个使用CUDA的GPU接口也于2010年9月开始实现.
1.3 二者初始对比
从上述简要介绍来看,显然Opencv是一个更加强大和完善的库。而且,Opencv是C++语言,PIL是python,在效率方面应该是Opencv更有优势。
然而,在Pytorch中对于图像的读取变换均用的PIL——
图片来源:https://pytorch.org/docs/0.2.0/_modules/torchvision/transforms.html
是更加方便还是效率更高?
2.准备工作
由于我的预处理加速要在树莓派上进行,我的以下工作均是在树莓派4B上完成的。
2.1 平台信息
树莓派CPU信息如下:
2.2 安装PIL和Opencv
安装PIL:pip install pillow
安装Opencv:方法见树莓派(4):树莓派python3安装opencv 另外再安装一下matplotlib:sudo apt-get install python3-matplotlib
(注:我用sudo pip install matplotlib
没有成功)
2.3 查看版本信息
查看版本
运行代码
import PIL
import cv2
print("---PIL---")
print("PIL version is " + PIL.__version__)
print(PIL.__spec__)
print("---cv2---")
print("Opencv version is " + cv2.__version__)
print(cv2.__spec__)
输出结果为
---PIL---
PIL version is 5.4.1
ModuleSpec(name='PIL', loader=<_frozen_importlib_external.SourceFileLoader object at 0xb665bb10>, origin='/usr/lib/python3/dist-packages/PIL/__init__.py', submodule_search_locations=['/usr/lib/python3/dist-packages/PIL'])
---cv2---
Opencv version is 3.4.6
ModuleSpec(name='cv2.cv2', loader=<_frozen_importlib_external.ExtensionFileLoader object at 0xb65ff450>, origin='/home/pi/.local/lib/python3.7/site-packages/cv2/cv2.cpython-37m-arm-linux-gnueabihf.so')
2.4 验证安装成功
看一下能不能分别用PIL和Opencv查看图片——
验证代码为
import PIL
import cv2
import time
import numpy as np
from PIL import Image,ImageOps
import matplotlib.pylab as plt
def stage_1_PIL(filename):
img_pil = Image.open(filename)
img_pil = img_pil.resize((416,416))
return np.asarray(img_pil)
def stage_1_cv2(filename):
img_cv2 = cv2.imread(filename)
img_cv2 = cv2.cvtColor(img_cv2, cv2.COLOR_BGR2RGB)
img_cv2 = cv2.resize(img_cv2,(416,416),interpolation=cv2.INTER_CUBIC)
return img_cv2
f = "lenna.png"
r1 = stage_1_PIL(f)
r2 = stage_1_cv2(f)
plt.figure("test")
plt.subplot(131)
plt.imshow(r1)
plt.subplot(132)
plt.imshow(r2)
plt.subplot(133)
plt.imshow(np.abs(r1-r2))
plt.show()
结果为
或者Opencv采用如下显示方式
img_cv2 = cv2.imread(filename)
cv2.imshow("img",img_cv2)
cv2.waitKey(0)
Okay,接下来可以开始了!
3. 二者比较
3.1 测试图片
测试图片为lenna图,512x512像素,24位深度,719,641字节,PNG文件。
3.2 用时比较
第一阶段:PIL 打开图片 + resize至416x416
def stage_1(filename):
img_pil = Image.open(filename)
img_pil = img_pil.resize((416,416))
return np.asarray(img_pil)
第二阶段:cv2 打开图片 + resize至416x416
def stage_2(filename):
img_cv2 = cv2.imread(filename)
img_cv2 = cv2.resize(img_cv2,(416,416),interpolation=cv2.INTER_LINEAR)
return img_cv2
cv2.imread()接口读图像,读进来直接是BGR 格式,数据格式在 0~255
由于图片读出来的格式是BGR,不是我们最常见的RGB格式,往往需要转换一下
cv2.cvtColor(p1,p2) 是颜色空间转换函数,p1是需要转换的图片,p2是转换成何种格式。
第三阶段:cv2 打开图片 + 颜色空间转换 + 转换成array + resize至416x416
def stage_3(filename):
img = cv2.imread(filename)
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
img = Image.fromarray(img)
img = img.resize((416,416), Image.BILINEAR)
return np.asarray(img)
第四阶段:cv2 打开图片 + resize至416x416 + 颜色空间转换 + 转换成array
def stage_4(filename):
img = cv2.imread(filename)
img = cv2.resize(img,(416,416),interpolation=cv2.INTER_LINEAR)
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
img = Image.fromarray(img)
return np.asarray(img)
完整测试代码:
import PIL
import cv2
import time
import numpy as np
from PIL import Image,ImageOps
import matplotlib.pylab as plt
from timeit import Timer
def stage_1(filename):
img_pil = Image.open(filename)
img_pil = img_pil.resize((416,416))
return np.asarray(img_pil)
def stage_2(filename):
img_cv2 = cv2.imread(filename)
img_cv2 = cv2.resize(img_cv2,(416,416),interpolation=cv2.INTER_LINEAR)
return img_cv2
def stage_3(filename):
img = cv2.imread(filename)
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
img = Image.fromarray(img)
img = img.resize((416,416), Image.BILINEAR)
return np.asarray(img)
def stage_4(filename):
img = cv2.imread(filename)
img = cv2.resize(img,(416,416),interpolation=cv2.INTER_LINEAR)
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
img = Image.fromarray(img)
return np.asarray(img)
if __name__ == "__main__":
print("---PIL---")
print("PIL version is " + PIL.__version__)
print(PIL.__spec__)
print("---cv2---")
print("Opencv version is " + cv2.__version__)
print(cv2.__spec__)
print("---Test Start---")
f = "lenna.png"
'''
r1 = stage_1_PIL(f)
r2 = stage_1_cv2(f)
plt.figure("test")
plt.subplot(131)
plt.imshow(r1)
plt.subplot(132)
plt.imshow(r2)
plt.subplot(133)
plt.imshow(np.abs(r1-r2))
plt.show()
'''
start = time.time()
for i in range(1000):
r1 = stage_1(f)
print("stage1 time is ", (time.time()-start)," ms")
start = time.time()
for i in range(1000):
r2 = stage_1(f)
print("stage2 time is ", (time.time()-start)," ms")
#cv2.imshow("img",r2)
#cv2.waitKey(0)
start = time.time()
for i in range(1000):
r3 = stage_3(f)
print("stage3 time is ", (time.time()-start)," ms")
start = time.time()
for i in range(1000):
r4 = stage_4(f)
print("stage4 time is ", (time.time()-start)," ms")
输出结果:
pi@raspberrypi:~/test $ python PIL_vs_Opencv.py
---PIL---
PIL version is 5.4.1
ModuleSpec(name='PIL', loader=<_frozen_importlib_external.SourceFileLoader object at 0xb6690550>, origin='/usr/lib/python3/dist-packages/PIL/__init__.py', submodule_search_locations=['/usr/lib/python3/dist-packages/PIL'])
---cv2---
Opencv version is 3.4.6
ModuleSpec(name='cv2.cv2', loader=<_frozen_importlib_external.ExtensionFileLoader object at 0xb6690810>, origin='/home/pi/.local/lib/python3.7/site-packages/cv2/cv2.cpython-37m-arm-linux-gnueabihf.so')
---Test Start---
stage1 time is 19.369773864746094 ms
stage2 time is 19.356053113937378 ms
stage3 time is 35.645350217819214 ms
stage4 time is 24.45963740348816 ms
结果对比:
阶段 | 用时(ms) |
stage1: PIL 打开图片 + resize至416x416 | 19.369773864746094 |
stage2: cv2 打开图片 + resize至416x416 | 19.356053113937378 |
stage3: cv2 打开图片 + 颜色空间转换 + 转换成array + resize至416x416 | 35.645350217819214 |
stage4: cv2 打开图片 + resize至416x416 + 颜色空间转换 + 转换成array | 24.459637403488160 |
4 分析
对比第一二阶段,可以发现PIL和Opencv没有太大差别。
然而,在实际应用中,PIL调用摄像头并不友好,Opencv更强大一些,可单纯的用第二阶段又是BGR顺序,一般对神经网络不匹配。故而进行了第二三阶段的测试——
可以看到第二三阶段差别较大,主要是多了颜色空间转换 和 转换成array,经后来测试,前者只占用1.1ms,后者占用15.2ms,可见时间大部分耗在转换成array上了。
于是,显然先resize就能大幅度减少转换array的工作量,第四阶段是一个比较好的方式。
之前分析了摄像头,这次从PIL和Opencv入手分析,实在感觉预处理这边基本上没啥好加速的,治标不治本,之后试一下树莓派多进程加速yolo吧:)