这两天在训练图像的时候出了点问题。因为这段时间图像训练的效果出奇的好,所以带着主观偏见在继续做研究,没有仔细看为什么。后来发现了问题,我表示这段时间的训练都白搭了。
这个问题就出在读取hdr图片上。python读取图片的方式,据我所知主要有两种。一种是用imageio读,另一个是用opencv。读取的图片一般用matplotlib显示。我总得看看我读取的图片对不对吧。
#用imageio读图片的时候
img_path = 'xxx\xx\xxx.hdr';
hdr = io.imread(img_path)
#如果用opencv读的话
hdr = cv2.imread(img.path).astype('float32')
#显示图片的时候
plt.imshow(hdr)
plt.show()
可能是因为我这个人比较不求甚解,并且做事情爱偷懒,容易想当然。一开始用imageio读取的图片用plt显示出来是花的,就像rgb通道没有全显示出来一样,一大片纯蓝色或者纯红色。我一开始觉得这种读法有问题,就转用了opencv,结果读取的图片效果效果显示很好。
殊不知,opencv这个开源函数库有很多坑。很多操作没有记录在documentation里面,比如这个hdr图像的读取就是其中之一。
做高动态图像的人肯定知道,ldr图像是8bit的,也就是0-255整数的值域。比如常见的jpg图像就是这个值域范围。而高动态hdr图像就不一定了,我见过的全是小数 0到+Inf,多大都有可能。在cv2.imread()方程的document中明确的写着支持读取.hdr图像,我就以为opencv可以乖乖的把hdr图像读下来存在变量中。鬼知道读取的图像竟然是uint8的格式,也就是256位!!hdr那么大的范围是怎么变成uint8这么小的值域的呢?我没找到任何地方标出过这个过程。单单从效果上来看,应该是做了一次简单的色调映射(tone-mapping)。映射过的图像视觉效果当然好了,这也就导致我的训练全TM是假象!!!
划重点:
1,读取图片的格式在图像处理中尤为重要。当你在做data preprocessing的时候,可能因为格式不对而mess up整个处理过程。
2,imageio读取图像比较老实。如果是.jpg图像,会读成uint8格式。如果是.hdr图像会读成float(八成是float32)存起来,不做任何的多余操作。
3,opencv的imread()方程在读取.hdr图像的时候,会做一次没有记录的图像值域压缩。压缩至0-255之间。
4,matplotlib的imshow方程显示图像的机理比较复杂。
至于Matplotlib的imshow是怎么运作的,有人在stackoverflow上做过简单的介绍。大概的规律是这样的:
- 如果是NxM的数组,如果是float类型的0-1,他会被解析成灰度图(document上是这么说的,但是在实践中不是这样的结果。。)。其他的类型会用colormap解析(如果没有明确标明,那就会被autoscale)。
- 如果是NxMx3 float的数组的话,那个x3会被解析成RGB通道。他的值域会被理解为0到1之间。 如果超出这个范围的话,会被取正1的mod。 比如1.2 mod 1 = 0.2. -1.7 mod 1 = 0.3
- 如果是NxMx3 uint8 数组的话,会被读成标准的图片,值域范围是0到255
- 如果是NxMx4的话,解析规律和上面一样。但是第四维度会被理解为不透明度,就是alpha通道。
Ref:
https://stackoverflow.com/questions/24739769/matplotlib-imshow-plots-different-if-using-colormap-or-rgb-array
网友评论