【问题标题】:What color conversion (or other operation) does imshow perform on this imageimshow 对此图像执行什么颜色转换(或其他操作)
【发布时间】:2017-06-27 18:09:01
【问题描述】:

我做了一个非常简单的程序,它读取图像,评估 sobel 过滤器,然后用 imshow 呈现它。

import cv2
img = cv2.imread("/home/alex/imagens/train_5.jpg")

sobelx = cv2.Sobel(img, cv2.CV_64F, 1, 0, ksize=3)  # x
sobely = cv2.Sobel(img, cv2.CV_64F, 0, 1, ksize=3)
norm = cv2.magnitude(sobelx, sobely)

normUint8 = norm.astype('uint8')

cv2.imshow("img", img)
cv2.imshow("norm", norm)
cv2.imshow("normUint8", normUint8)

print "img=" + str(img.dtype) + ", sobel=" + str(norm.dtype) + ", normUint8=" + str(normUint8.dtype)

cv2.waitKey(0)
cv2.destroyAllWindows()

这里我附上结果。

我预计显示 norm 和 normUint8 的结果会相同或非常相似,因为它们的值在每个像素处的差异小于 1。 因此,当我使用 CV_64FC3 图像时,我相信 opencv 在呈现之前正在执行一些操作。

我有兴趣找到这个操作以便使用它。

有人可以帮忙吗?

在这里我附上我使用的原始图像。

谢谢。

【问题讨论】:

  • 您使用的是哪个版本的opencv?您正在使用名称“img”、“norm”和“normUint8”显示带有imshow() 的图像,但您呈现的结果有其他名称(img、sobel 和 saved_sobel),您确定它们没问题吗?
  • 我确定。我更改了代码并没有更改图像。我会解决的。
  • 我在 ubuntu 16.04 上使用 synaptic 包中的 2.4.9 版本。
  • 这里是该代码对应的正确图片:ibb.co/gBiBck

标签: python opencv


【解决方案1】:

您正在向imshow 提供64FC3(3 通道,64 位浮点)图像。该函数的文档说明:

该函数可以缩放图像,具体取决于其深度:

  • 如果图像是 8 位无符号的,则按原样显示。
  • 如果图像是16位无符号或32位整数,则像素除以256。即取值范围[0,255*256]映射到[0,255]。
  • 如果图像是32位浮点,则像素值乘以255。即取值范围[0,1]映射到[0,255]。

尽管没有提到 64 位浮点数,但我们可以做出一个体面的假设,即它们的处理方式与 32 位浮点数相同。如果我们查看源代码,我们会发现转换是由函数cvConvertImage 完成的。具体来说,在line 622

double scale = src_depth <= CV_8S ? 1 : src_depth <= CV_32S ? 1./256 : 255;

为了给那些不熟悉枚举类型顺序的人解释一下,它是 8U、8S、16U、16S、32S、32F、64F。因此,字节没有缩放,其他整数除法,其余(浮点数)乘法。

由于我们需要一个 8 位图像进行显示,因此需要注意的是,缩放将使用饱和度完成(在这种情况下,任何超过 255 的都变为 255,任何低于 0 的都变为 0)。


既然已经清楚imshow 做了什么样的变换,那么让我们看看为什么你会在一片白海中看到这些色块。

由于normuint8 的简单转换为您提供了一个并非全黑的图像,我们可以安全地假设norm 的值[0.0-1.0] 范围内。当值按 255 缩放时,任何大于或等于 1.0 的值都将变为 255(白色)。由于是 3 通道图像,我们最终会得到只有部分通道不饱和的地方,因此我们会看到各种颜色的斑块。

我们可以通过以下脚本来模拟这种行为:

b,g,r = cv2.split(norm)

r = np.uint8(np.where(r < 1.0, 0, 255))
g = np.uint8(np.where(g < 1.0, 0, 255))
b = np.uint8(np.where(b < 1.0, 0, 255))

cv2.imwrite('sobel_out.png', cv2.merge([b,g,r]))

对于值

看起来很眼熟?


注意:我怀疑方形图案来自您用于输入的 JPEG 压缩。

【讨论】:

【解决方案2】:

Dan 的回答非常出色,并描述了为什么float 图像可能具有意外的显示属性。图像需要一个最小值和最大值才能知道什么是黑白,这并不总是为float 图像精确定义。

例如,您可以使用 float 图像,该图像的值仍严格介于 0 和 255 之间,仅用于计算精度,稍后将舍入为 int 以进行显示。但是在文献中通常使用 0 和 1 作为图像值的最小值和最大值,因为它使数学变得更加简单;由于您需要 float 值来表示 0 和 1 之间的值,因此 float 图像使用 0 到 1 范围是很常见的。因此,OpenCV 坚持使用此值来显示 float 图像。对于float,它饱和图像的范围为 0 到 1,这意味着它会截断上方和下方的值。

现在,如果您读取图像,默认情况下它会被读取为 8 位无符号整数(CV_U8C3 用于 3 通道图像)。当您应用 Sobel 运算符时,您指定了您想要返回一个 float 图像。这完全没问题,但要知道the Sobel operator is a convolution which multiplies multiple values and sums them up,所以这个操作可以给你比原始图像开始时更大的值。如果您使用不同的返回类型,那么这些值可能会饱和。但是,使用浮点数,直到显示时间它们才会饱和。这是非常有目的的; Sobel 运算符可用于任意矩阵,因此并不总是需要使值饱和。

为了显示没有奇怪伪影的图像,您需要手动缩放图像,如上面链接的 Stack Overflow 答案或使用cv2.normalize()。或者您可以像您一样直接转换为另一种类型,使值在其高端饱和。

【讨论】:

  • 关于如何缓解问题的有用链接和建议,尽管您有点回答实际问题。 ;) 你还是让我竖起大拇指。
  • 是的,我正要去吃午饭,回到您的出色回答,所以我没有费心编辑更多信息。你用文档和例子把它钉牢了,你早些时候得到了我的赞许:)。我将编辑我的答案,以便它引用您的答案,并坚持谈论索贝尔以获取不同的信息。
猜你喜欢
  • 1970-01-01
  • 2013-01-19
  • 1970-01-01
  • 2011-06-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多