解决问题:使用cv2.imshow/plt.imshow显示图片正常,但保存下来的图片不正常或者全黑

今天遇到了一个Bug,花了很大的时间精力解决,在这里记录一下。

Bug如题目所说,在对一张渲染出来的图片保存时,发现存成了全黑的图,尝试过将数据归一化/改变数据类型等等方式均失败,然而尝试了用 opencv 或者 matplotlib.pyplot 的 imshow 显示出来的图确是正常的,如下所示:

使用 matplotlib.pyplot 显示正常

 plt.imshow(rendering)
 plt.show()
 plt.axis('off')

 

使用cv2.imshow显示正常

rendering_img1 = cv2.cvtColor(rendering_img1, cv2.COLOR_RGB2BGR)
cv2.imshow("rendering_img1", rendering_img1)
cv2.waitKey()
cv2.destroyAllWindows()

 

 然而,当保存时,无论用OpenCV还是PIL保存,存下来的图片均为纯黑的图,如下所示。

 


CSDN上对于保存纯黑的图,已经有了解决方案,大致都是以下说法

 也就是imshow时的图片范围为[0,1],但保存时图片必须是[0,255]的范围,直接*个255即可。

以上答案是对的,但是,对于我的这个问题不适用,因为我发现,我的图片的范围即不是[0,1]也不是[0,255],而是[0,6.05]、[0,16.77]等范围,且数据类型为float32


于是,自然而然地想到,将图片像素值缩放到[0,255]这个范围,调用以下函数:

rendering_img1 = cv2.normalize(rendering_img1, None, 0, 255, cv2.NORM_MINMAX, cv2.CV_32F)

这个函数的原理很简单,就是根据最大最小像素点的值和当前像素点的值,按比例进行缩放,像素值最大的点变成255,最小的点变成0:

 然后,这样保存得到的结果虽然不是全黑的,但也和直接imshow显示出来的结果不一样,如下所示:

 


上述方法失败了,于是又自然而然地想到,既然imshow可以正常显示,那么在imshow显示的过程当中,自然也对输入数据进行了一定的处理,于是去查函数相关的官方文档。果不其然,有了重大发现。

 我们重点关注这行,Out-of-range RGB(A) values are clipped.也就是说,超出范围的RGB值将会被裁掉,注意,这里不是缩放,而是直接将超出范围的值变成最大值。也就是说,输入的数据位float32的话,对应[0,1]的范围,超过1的值全都会变成1.

模拟这个plt.imshow函数的处理方法,可以进行以下等价操作:

rendering_img1[rendering_img1>1.]=1. #即将大于1.0的数全都变成1
rendering_img1 = rendering_img1*255. #[0,1]->[0,255]
#注意,最后可能还需要进行np.uint8(rendering_img1)的操作将图片从float32变成unit8整型

经过这一步操作之后,再保存的图即imshow函数显示的图。


分析:

根据以上实验,我们还可以找到此前尝试用:

rendering_img1 = cv2.normalize(rendering_img1, None, 0, 255, cv2.NORM_MINMAX, cv2.CV_32F)

函数直接缩放,导致保存的图片虽然不是全黑,但也不正确的原因。

就是因为数据中出现了一些超过1.0的值,导致最大值可能很大(原本最大值应该为1),而根据这个缩放公式,导致其他正常值缩放后的值偏小,所以造成了结果虽然不是全黑,但也不正确的现象。

例如,如果正常来说,如果数据范围为[0,1],最大值缩放到255,最小值缩放到0,那么值为0.5的数据就缩放到了255/2=127的大小,而如果数据最大值不再是1,而变成了10,此时0.5的数据就变成了12,相距甚远。而我们知道,纯黑的像素的RGB为[0,0,0],因此,所有像素的值偏小的话,也就导致了虽然图片不是全黑,但也是黑漆漆一片。

  • 32
    点赞
  • 54
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值