我制作了一个非常简单的程序来读取图像,评估索贝尔过滤器然后用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()
我期望结果显示标准,并且normUint8将是相同或更相似的,因为它们的值在每个像素处小于1。 因此,我相信当我使用CV_64FC3图像时,opencv会在呈现之前执行某些操作。
我有兴趣找到这个操作才能使用它。
任何人都可以帮忙吗?
这里我附上我使用的原始图像。
感谢。
答案 0 :(得分:3)
您正在将64FC3
(3频道,64位浮动)图像投放到imshow
。该函数的文档说明:
该功能可能会缩放图像,具体取决于其深度:
- 如果图像是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
的转变是什么,那么让我们来看看为什么你会在白色的海洋中看到那些色块。
由于norm
到uint8
的简单投射会为您提供一个并非全黑的图像,我们可以放心地假设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]))
我们将像素设置为黑色以获得值&lt; 1.0,其他一切都变白了。当我们组合平面时,我们得到以下图像:
看起来很熟悉?
注意:我怀疑方形图案来自您用于输入的JPEG压缩。
答案 1 :(得分:1)
Dan的答案非常好,并描述为什么 float
图片可能会出现意外的显示属性。图像需要最小值和最大值才能知道黑色和白色是什么,并且float
图像并不总是精确定义。
例如,您可以使用float
图像,该图像仍然具有严格在0到255之间的值,仅用于计算精度,稍后舍入为int
进行显示。但是在文献中通常分别使用0和1作为图像值的最小值和最大值,因为它使数学更加简单;由于您需要float
个值来表示介于0和1之间的值,因此float
图像使用范围0到1的情况很常见。因此,OpenCV坚持使用此值来显示float
图像。对于float
,使图像在0到1范围内饱和,这意味着它会截断上下的值。
现在,如果您读入图像,默认情况下它将被读取为8位无符号整数(3通道图像为CV_U8C3
)。当您应用Sobel运算符时,您指定了想要的float
图像。这完全没问题,但是知道the Sobel operator is a convolution which multiplies multiple values and sums them up,所以这个操作可以给你的值大于原始图像的起始值。如果您使用了不同的返回类型,那么这些值可能会饱和。但是,使用浮动,它们在显示时间之前不会饱和。这非常有目的; Sobel算子可用于任意矩阵,因此并不总是需要使值饱和。
为了显示没有奇怪瑕疵的图像,您需要手动缩放图像,如上面链接的Stack Overflow应答或使用cv2.normalize()
。或者你可以像你一样直接投射到另一种类型,使价值达到高端。