我试图使用对数变换来拉伸图像的直方图。基本上,我正在对每个像素的强度应用log
操作。当我尝试更改每个像素中的图像值时,不会保存新值,但直方图看起来没问题。此外,最大值不正确。这是我的代码:
import cv2
import numpy as np
import math
from matplotlib import pyplot as plt
img = cv2.imread('messi.jpg',0)
img2 = img
for i in range(0,img2.shape[0]-1):
for j in range(0,img2.shape[1]-1):
if (math.log(1+img2[i,j],2)) < 0:
img2[i,j]=0
else:
img2[i,j] = np.int(math.log(1+img2[i,j],2))
print (np.int(math.log(1+img2[i,j],2)))
print (img2.ravel().max())
cv2.imshow('LSP',img2)
cv2.waitKey(0)
fig = plt.gcf()
fig.canvas.set_window_title('LSP histogram')
plt.hist(img2.ravel(),256,[0,256]); plt.show()
img3 = img2
B = np.int(img3.max())
A = np.int(img3.min())
print ("Maximum intensity = ", B)
print ("minimum intensity = ", A)
这也是我得到的直方图:
然而,最大强度显示186!这根本不适用对数操作。
有什么想法吗?
答案 0 :(得分:7)
您编写的代码执行应用于图像强度的对数变换。您之所以获得如此高的杂散强度是因为您的for
循环错误。具体来说,您的range
不正确。 range
是独占的结束时间间隔,这意味着您必须分别前往img.shape[0]
和img.shape[1]
,而不是img.shape[0]-1
或{{ 1}}。因此,您缺少图像的最后一行和最后一列,并且这些不会被对数操作触及。报告的最大值来自您未触摸的最后一行或列中的其中一个像素。
一旦你纠正了这个,你就不会再那么强烈了:
img.shape[1]-1
现在这样做了我们:
for i in range(0,img2.shape[0]): # Change
for j in range(0,img2.shape[1]): # Change
if (math.log(1+img2[i,j],2)) < 0:
img2[i,j]=0
else:
img2[i,j] = np.int(math.log(1+img2[i,j],2))
然而,你现在要得到的是一张非常黑暗的图像。您向我们展示的直方图表明,所有图像像素都处于黑暗范围内......大致在('Maximum intensity = ', 7)
('minimum intensity = ', 0)
之间。因此,如果使用[0-7]
作为可视化的数据类型,则大部分图像将会变暗。请注意,我搜索了作为OpenCV教程一部分的Lionel Messi图像,这是我找到的图像:
来源:https://opencv-python-tutroals.readthedocs.org/en/latest/_images/roi.jpg
您的代码正在将其转换为灰度,这对您的问题来说很好。现在,使用上面的图像,如果您实际显示直方图计数的样子以及直方图中每个bin的强度,这就是uint8
得到的结果:
img2
正如您所看到的,大部分图像像素都悬停在In [41]: np.unique(img2)
Out[41]: array([0, 1, 2, 3, 4, 5, 6, 7], dtype=uint8)
In [42]: np.bincount(img2.ravel())
Out[42]: array([ 86, 88, 394, 3159, 14841, 29765, 58012, 19655])
范围之间,这就是为什么一切看起来都是黑色的原因。如果你想更好地看到这一点,也许可以大致[0-7]
左右缩放图像,我们可以更好地看到图像:
255 / 7 = 36
我们得到这张图片:
我也得到这个直方图:
个人看起来非常丑陋......至少对我而言。因此,如果您想要拉伸直方图,我建议您选择更有意义的图像转换。事实上,img2 = 36*img2
cv2.imshow('LSP',img2)
cv2.waitKey(0)
操作压缩直方图的动态范围。如果要拉伸直方图,请采用相反的方法并尝试幂律运算。具体而言,给定输入强度,输出定义为:
log
out = c*in^(p)
是输入强度,in
是幂,p
是常量,可确保您缩放图像,以便将最大强度映射到相同的最大强度你完成时的输入而不是更大的输入。这可以通过计算c
来完成,以便:
c
...... c = (img2.max()) / (img2.max()**p)
就是你想要的力量。此外,通过幂律进行的变换可以用这个漂亮的图解释:
来源:http://www.nptel.ac.in/courses/117104069/chapter_8/8_14.html
基本上,小于1的功率执行强度扩展,其中较暗的强度被推向较轻的一侧。类似地,大于1的功率执行强度压缩,其中较轻的强度被推到较暗的一侧。在您的情况下,您希望展开直方图,因此您需要第一个选项。具体来说,尝试使较小的强度朝向更大的范围。这可以通过选择小于1的功率来完成...例如尝试0.5。
您可以修改代码,使其如下所示:
p
这样做,我得到了这张图片:
我也得到这个直方图:
如果我可以就效率提出建议,我建议您不要遍历整个图像并单独设置每个像素......这就是img2 = img2.astype(np.float) # Cast to float
c = (img2.max()) / (img2.max()**(0.5))
for i in range(0,img2.shape[0]-1):
for j in range(0,img2.shape[1]-1):
img2[i,j] = np.int(c*img2[i,j]**(0.5))
# Cast back to uint8 for display
img2 = img2.astype(np.uint8)
数组不的方式应该被使用。您可以在一行代码中实现您想要的矢量化。
使用旧代码,使用numpy
,而不是np.log2
,基数2使用math.log
数组:
numpy
同样,如果你想应用幂律变换,那很简单:
import cv2
import numpy as np
from matplotlib import pyplot as plt
# Your code
img = cv2.imread('messi.jpg',0)
# New code
img2 = np.log2(1 + img.astype(np.float)).astype(np.uint8)
# Back to your code
img2 = 36*img2 # Edit from before
cv2.imshow('LSP',img2)
cv2.waitKey(0)
fig = plt.gcf()
fig.canvas.set_window_title('LSP histogram')
plt.hist(img2.ravel(),256,[0,256]); plt.show()
img3 = img2
B = np.int(img3.max())
A = np.int(img3.min())
print ("Maximum intensity = ", B)
print ("minimum intensity = ", A)
cv2.destroyAllWindows() # Don't forget this