HDUMIL / summer-school

Media Intelligence Laboratory Machine Learning / Deep Learning Summer School
https://hdumil.github.io/summer-school/
MIT License
17 stars 3 forks source link

关于PIL显示图片颜色失真问题 后续 #9

Closed hwZhang98 closed 5 years ago

hwZhang98 commented 5 years ago

在看过助教给的博客和可能BUG分析后,我又专门的进行了尝试,首先全部代码如下: `from PIL import Image import numpy as np import matplotlib.pyplot as plt

IMG_URL = "lenna.jpg" img = Image.open(IMG_URL) print('img mode'+str(img.mode)) img_np = np.array(img) plt.subplot(1,2,1) plt.imshow(img) print('img size'+str(img.size)) print('img_np shape'+str(img_np.shape)) print('img_np size'+str(img_np.size))

img_1 = img.convert('L') img_1_np = np.array(img_1) print('img_1 mode'+str(img_1.mode)) plt.subplot(1,2,2) plt.imshow(img_1) print('img_1 size'+str(img_1.size)) print('img_1_np shape'+str(img_1_np.shape)) print('img_1_np size'+str(img_1_np.size)) print('Value range from ' + str(img_1_np.min()) + ' to ' + str(img_1_np.max())) plt.show() print(img_1_np)

下面为分离通道测试------------------------------------------------------------------------分割线

img_r, img_g, img_b = img.convert('RGB').split()

plt.figure(figsize=(10,10)) # 调整子图大小 plt.subplots_adjust(wspace =0.5, hspace =0.5) # 调整子图间距

plt.subplot(2,3,1) plt.imshow(img_r) # 红色 Red plt.xlabel('red channel')

plt.subplot(2,3,2) plt.imshow(img_g) # 绿色 Green plt.xlabel('green channel')

plt.subplot(2,3,3) plt.imshow(img_b) # 蓝色 Blue plt.xlabel('blue channel')

plt.subplot(2,3,4) plt.imshow(img_np[:, :, 0]) # 红色 Red plt.xlabel('red channel2')

plt.subplot(2,3,5) plt.imshow(img_np[:, :, 1]) # 绿色 Green plt.xlabel('green channel2')

plt.subplot(2,3,6) plt.imshow(img_np[:, :, 2]) # 蓝色 Blue plt.xlabel('blue channel2')

plt.show()` 博客指出问题可能是以下两种情况 1.第一种是用open打开函数后,图片为错误的两维图片而不是三维,会将强制性把图片认为是灰度图格式。运行一次代码,结果如下:

QQ图片20190704160525

R(@HA%% 3OKF1 GH0%1F$XR 可以看到,图片转化为矩阵后的维度没有问题,是三维的,并且mode也为RGB模式,所以第一种情况排除。 2.第二种是如果是三维的图片,可能打开的图片错误的用BGR格式打开,此时需要转化为RGB,我用博客上的方法进行转化后,结果如下: K{XSUZG J(~QDDFUO 0352 可以看到输入的原图因为格式的原因发生了变化,所以说明输入的本来就是RGB格式,第二种情况也排除。 接下来我又进行了思考和分析,首先进行convert转化为灰度模式后,img_1的模式确实是‘L’,说明确实是灰度模式,接着我发现这个图和应该正常的结果像素大小范围也是一样的,并且矩阵中每一个元素都一样。。。也就是模式一样都是L,矩阵元素也都一样,可是显示出来却不对,对比如下: O_ZYIFH06JT~HK~G~PSBLQ6 $8UT_)BHUXSV@3LWNY7NGX1 接下来我又分离三通道,也就是分割线之下的代码,发现了和示例不一样的地方,文档说用手动分离和用函数分离得到的单通道图不一样,可是我的经过分离之后,两种情况却一样。。。。无语了。。下图是示例结果,接着是我的结果: }C19NG%YC@CT27)BHD)AMVF )_92OV(EC06(DZWNMQMI5(F 49(XY$YX6KHJ3PLDS)X)UEW 我觉得已经超出我能力范围了,因为确实一些函数文档全英我看的很吃力,只能看懂一部分,而且明明模式一样,每个像素矩阵也一样,为什么显示就不一样

cheekyshibe commented 5 years ago

在前面的回答 #8 中,我疏忽了语法问题,Numpy 数组 RGB 转 BGR 应该是:

IMG_URL = "lenna.jpg"
img = Image.open(IMG_URL).convert('RGB')
plt.imshow(np.array(img)[:,:,::-1])
plt.show()

我之前给出的 plt.imshow(np.array(img)[:,:,-1]) 作用是显示索引为 -1 的通道,即绿色通道。


回到这个问题,先看一个我机器上的对比例程:

from PIL import Image
import numpy as np
import matplotlib.pyplot as plt

img = Image.open('lenna.jpg')
img = np.array(img)
if img.ndim == 3:
    img = img[:,:,1]
plt.subplot(221); plt.imshow(img)
plt.subplot(222); plt.imshow(img, cmap ='gray')
plt.subplot(223); plt.imshow(img, cmap = plt.cm.gray)
plt.subplot(224); plt.imshow(img, cmap = plt.cm.gray_r)
plt.show()

download

我想你现在已经知道理想输出的情况是什么了,而且分析了这么多,我们可以认为 plt.imshow() 这个函数在你的机器上没有体现出差异。

plt.imshow() 对于 PIL 和 Numpy 的显示机制不一样,用代码来显示这个差异就是:

IMG_URL = "lenna.jpg"
img = Image.open(IMG_URL).convert('RGB')

plt.subplot(1,2,1)
plt.imshow(img.split()[1])        # PIL (without cmap setting)

plt.subplot(1,2,2)
plt.imshow(np.array(img)[:,:,1])  # numpy

plt.show()

download

如果我对你的问题理解没有出错,你运行上面这段代码,可能会得到效果相同的图片 : ( 就像下面这样

download

而通过观察 Numpy 数组我们排除了 convert('L') 的嫌疑

所以问题是 imshow.() 对于单通道的 PIL 图片没有按照默认的灰度图方式去显示...

这个问题在其他人的机器上能复现吗?因为产生这样的情况的原因不好分析了,只能用 cmap='gray' 临时纠正这种问题。

如果的确是 matplotlib 不能够很好地显示图片,你可以使用 PIL 自带的显示方式看看效果:

img = Image.open(IMG_URL, 'r').convert('L')
img.show()     # 不用带参数

我们只能猜测 WTF , matplotlibimshow() 不够智能,在我机器上的 matplotlib 是 3.0.2 版本,一切都是正常的。

不要因为这个地方的一些小 BUG 打击自信心,你已经做得相当好了!

hwZhang98 commented 5 years ago

谢谢助教,自己去钻研也确实搞明白很多东西

cheekyshibe commented 5 years ago

我现在在想的是,是不是由于 plt.imshow() 的默认显示方式设置出现了问题,你尝试运行下面这个代码:

import matplotlib.pyplot as plt
plt.gray()

官方文档在这里 , 我帮你大概翻译一下:

colormap 设置为 “gray” - 这会更改默认的 colormap 以及当前图像的 colormap (如果有的话).

它的源码也很简单:

def gray():
    """
    Set the colormap to "gray".

    This changes the default colormap as well as the colormap of the current
    image if there is one. See ``help(colormaps)`` for more information.
    """
    set_cmap("gray")

如果的确是这个原因,这将一劳永逸地解决你的问题。 这也说明可能你在 python 练习笔记本中玩 matplotlib 的时候对默认的参数不小心进行了改动。

hwZhang98 commented 5 years ago

可以了,加上这句代码后,结果正常了,但是imshow()依然没有在我的机器上体现出差异,convert可以正常使用了,结果如下 }T$_{ R`9`D_YC}WEP_RQMU

EKF5Z4@K2VLJZ%Q(B5}6BE

cheekyshibe commented 5 years ago

行,先这样处理吧,周五的时候线下再看看。:blush: