问题描述篇:
今天在调试困扰了我很久的一个问题,在训练网络生成batch数据的时候读入原始图像,输出的时候却老是出问题。
我们都知道YUV和RGB之间的转换关系:
R = Y + 1.4075 * V;
G = Y - 0.3455 * U - 0.7169*V;
B = Y + 1.779 * U;
Y = 0.299 * R + 0.587 * G + 0.114 * B;
U = -0.169 * R - 0.331 * G + 0.5 * B ;
V = 0.5 * R - 0.419 * G - 0.081 * B;
问题在于,我从RGB到YUV的时候,cv.cvtColor(Img, cv2.COLOR_RGB2YUV)输出的是(Y,U,V),Y属于0-255,U和V却是在128左右徘徊(其实也是0-255)而不是公式计算出来的-127-127。
最终导致的结果就是存在色偏(可能不仅仅是色偏,连有一些结构都有改变,但是我准备先解决这个色偏的问题),具体情况如图:
原始图像:
输出图像:
于是寻找解决问题,发现在直接读进去的时候用
Img = cv.cvtColor(Img, cv2.COLOR_RGB2YUV)
Img = cv.cvtColor(Img, cv2.COLOR_YUV2RGB)
连续使用的时候是可以直接转换成功的,但是一旦中间经过网络输出之后,再用YUV转换到RGB的函数就会出错。
于是我把每次转换的内容存了下来。
原图(没问题):
YUV(也很正常):
转换回RGB(出问题了):
后来发现问题出在最后一步,转换回去的时候,从YUV空间变回RGB空间的时候,用的公式是上面那个没有经过量化的公式,计算出来的结果很多都是四百多五百多的值。找了很久网上的博客,都没有这个问题的解决方法。最后试验了各种情况,锁定了bug位置,然后仔细看了cv2的文档,最后调通了。
解谜篇:
先说一下在python的opencv中,是有三种图像文件的存储方式的,分别是:
CV_8U,指的是0-255的uint8型
CV_16U,指的是0-65535的uint16型
CV_32F,指的是0-1的float32型
在我们刚从文件中读进来一幅图像的时候,都是存储在uint8型的一个ndarray中,但是如果要严格按照公式转换成YUV空间的话,那么U或者V空间中必然会存在小于0的数,那怎么办呢?
cv.cvtColor(Img, cv2.COLOR_YUV2RGB)这个函数的输出范围刚才已经说过了,Y(0-255)U(0-255)V(0-255),所以这个函数其实是将UV空间做了一个平移,原来均值可能为零的地方平移到了127,所以才会避免刚才说的小于零的情况出现。
那么问题也就出现了,我在直接读入图片之后在YUV空间和RGB空间中来回转换是没有出现问题的。但是我把网络输出重新转换回RGB空间的时候这个函数的输出大于了255,所以导致输出图像出错。到底是为什么呢?
这就该我们刚才所说的三种文件存储方式登场了,其实在调用cv.cvtColor(Img, cv2.COLOR_YUV2RGB)的时候,会先判断你输入图像的类别,如果是CV_8U,那么UV空间的中心点就在127,如果是CV_16U,那么就在32768,如果是CV_32F,那么就在0.5。而我网络的输出由于是float32型,导致调用这个函数的时候判断为我的中心点在0.5左右,然后通过上面的公式计算之后导致我数据溢出。
最后,其实就直接在转存之前调整一下这个数据类型即可,即:
Pic = Pic.astype(‘uint8’)
问题解决!
总结一下:
这个问题困扰了我好久,当然之前因为各种原因一直不是很想面对,也一度觉得有没有可能是我的脑子出了问题(真实想法),不过最后比自己看文档找博客来解决之后还是很有成就感的,毕竟自己还在起步,多看paper多写代码,踏踏实实干点什么事情比什么都重要。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)