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——

PIL和opencv的区别 pixy2和opencv哪个好_python


图片来源:https://pytorch.org/docs/0.2.0/_modules/torchvision/transforms.html

是更加方便还是效率更高?

2.准备工作

由于我的预处理加速要在树莓派上进行,我的以下工作均是在树莓派4B上完成的。

2.1 平台信息

树莓派CPU信息如下:

PIL和opencv的区别 pixy2和opencv哪个好_颜色空间_02

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 查看版本信息

查看版本

PIL和opencv的区别 pixy2和opencv哪个好_颜色空间_03


运行代码

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()

结果为

PIL和opencv的区别 pixy2和opencv哪个好_python_04


或者Opencv采用如下显示方式

img_cv2 = cv2.imread(filename)
cv2.imshow("img",img_cv2)
cv2.waitKey(0)

Okay,接下来可以开始了!

3. 二者比较

3.1 测试图片

测试图片为lenna图,512x512像素,24位深度,719,641字节,PNG文件。

PIL和opencv的区别 pixy2和opencv哪个好_Image_05

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吧:)