一、问题
在Python里使用OpenCV时,一般是通过cv2.imread读入图片,然后用plt.imshow显示图片,但最近学习OpenCV时这样做的结果与预期的结果有较大的出入。查找资料后,才明白OpenCV里的imshow()和Matplotlib.pyplot的imshow()在使用上有一些区别,不注意的话很容易就会导致很奇怪的结果。
import cv2 import matplotlib.pyplot as plt #以灰度模式读入图片 messi=cv2.imread('messi.jpg',0) #使用matplotlib.pyplot的imshow显示图片 plt.imshow(messi),plt.title('messi_plt') plt.xticks([]),plt.yticks([]) #隐藏坐标轴 plt.show() #使用opencv的imshow显示图片 cv2.imshow('messi_cv',messi) cv2.waitKey(0) cv2.destroyAllWindows()
运行结果如下:
左边是原图,中间是cv2.imshow的显示结果,右边是plt.imshow的显示结果。很明显地看到,明明是读入灰度图,plt.imshow的结果却更像是张彩图,这显然是有问题的。
二、分析和解决办法
为了比较和分析OpenCV里imshow()和Matplotlib.pyplot里imshow()这两者的差异,下面分别对显示彩图以及显示灰度图这两种情况来进行说明。
彩色图
对于彩色图片,一般由R,G,B三个通道构成。然而,需要注意的是,OpenCV里彩色图片加载时是按照BGR的顺序,Matplotlib里彩色图片加载时是按照RGB的顺序。所以,当我们用cv2.imread读入图片,用cv2.imshow来显示时自然是不会出问题的,但若用plt.imshow来显示就会出现问题,如下面的结果所示。
这里省略示例代码(和上面的几乎相同,只是不要在cv2.imread里设置“0”这个参数即可),运行结果如下:
左边是原图,中间是cv2.imshow的显示结果,右边是plt.imshow的显示结果。显然,plt.imshow的结果出现了问题。
为了解决这个问题,方法很简单,就是将通道R和通道B的内容调换一下,再用plt.imshow显示时就正常了。下面给出该方法的示例代码:
import cv2 import matplotlib.pyplot as plt #读入彩色图片 messi=cv2.imread('messi.jpg',1) #使用matplotlib.pyplot的imshow显示图片 plt.imshow(messi),plt.yticks([]) #隐藏坐标轴 plt.show() ##调换r、b通道,生成rgb顺序的图片并显示 b,g,r=cv2.split(messi) #通道的拆分 messi_rgb=cv2.merge((r,b)) #通道的融合 plt.imshow(messi_rgb),plt.title('messi_rgb_plt') plt.xticks([]),plt.yticks([]) plt.show() #使用opencv的imshow显示图片 cv2.imshow('messi_cv',messi) cv2.waitKey(0) cv2.destroyAllWindows()
灰度图
灰度图是单通道图片,按理说不会出现上面彩色图的那种问题,不管是用cv2.imshow显示还是用plt.imshow显示,结果都应该是一样的。然而,事实却并非如此(如最开始的示例所示)。我纠结了好久不知道原因是什么,一开始以为是opencv和matplotlib的版本不匹配,结果更新版本之后还是有这个问题。后来,去找来matplotlib的API文档才明白是咋回事。
matplotlib.pyplot.imshow函数里,有一个参数是cmap,API文档里给出的说明是:
cmap : str or Colormap,optional
The Colormap instance or registered colormap name used to map scalar data to colors. This parameter is ignored for RGB(A) data. Defaults to rcParams[“image.cmap”] = ‘viridis'.
大致的意思是说,cmap给出了标量值如何映射到颜色空间,并且对于RGB(A)图像此参数是忽略的;默认参数可查看rcParams[“image.cmap”]
。链接转过去的文档是matplotlib的示例配置文档matplotlibrc,里面定义了各种变量的默认值(这也是为什么我们在调用matplotlib里的函数时,有些参数我们没给值也能正常运行的原因)。在这里能看到,cmap的默认值是viridis,这也就说明了在使用plt.imshow显示灰度图时出现问题的原因。
因此,为了解决该问题,使plt.imshow能正常地显示灰度图,方法也很简单,就是修改cmap的值为'gray'。示例代码如下:
import cv2 import matplotlib.pyplot as plt #读入彩色图片 messi=cv2.imread('messi.jpg',0) #使用opencv的imshow显示图片 cv2.imshow('messi_cv',messi) cv2.waitKey(0) cv2.destroyAllWindows() #使用matplotlib.pyplot的imshow显示图片 #cmap使用默认值 plt.imshow(messi),plt.title('messi_camp_default') plt.xticks([]),plt.yticks([]) #隐藏坐标轴 plt.show() #使用matplotlib.pyplot的imshow显示图片 #cmap设置为'gray' plt.imshow(messi,cmap='gray'),plt.title('messi_camp_gray') plt.xticks([]),plt.yticks([]) #隐藏坐标轴 plt.show()
运行结果为:
左边是cv2.imshow的显示结果,中间和右边的是plt.imshow的显示结果。
另外,cmap的值除了可以取默认参数及'gray'外,还有很多值可供我们选择,详细的说明在这里。其实这些东西了解下就行,等实际使用时,查阅下、试一试。根据需要选择合适的就可以了。
注:
1.关于matplotlib里的imshow函数更详细的说明在这里。
2.关于matplotlib.matplotlibrc文档更详细的说明在这里。
3.如果想查看或编辑自己电脑里的matplotlibrc文件,可使用此命令matplotlib.matplotlib_fname()获取路径。
三、总结
由于OpenCV里的imshow和Matplotlib里的imshow的一些差异,在使用时主要是要注意两点:
1.显示彩色图时,要把b、r通道调换一下。
2.显示灰度图时,记得设置cmap的值为'gray'。