我正在试图实现(我知道有一个实现它的自定义函数)Matlab中的灰度图像直方图,到目前为止我已经尝试过了:
function h = histogram_matlab(imageSource)
openImage = rgb2gray(imread(imageSource));
[rows,cols] = size(openImage);
histogram_values = [0:255];
for i = 1:rows
for j = 1:cols
p = openImage(i,j);
histogram_values(p) = histogram_values(p) + 1;
end
end
histogram(histogram_values)
但是当我调用该函数时,例如:histogram_matlab('Harris.png')
我获得了一些图表:
这显然不是我所期望的,x轴应该从0到255,y轴从0到最大值存储在histogram_values
中。
我需要获得imhist
提供的内容:
我该如何设置?我做得不好吗?
我已将代码更改为@rayryeng建议的改进和更正:
function h = histogram_matlab(imageSource)
openImage = rgb2gray(imread(imageSource));
[rows,cols] = size(openImage);
histogram_values = zeros(256,1)
for i = 1:rows
for j = 1:cols
p = double(openImage(i,j)) +1;
histogram_values(p) = histogram_values(p) + 1;
end
end
histogram(histogram_values, 0:255)
然而直方图不是预期的:
这里很明显,y轴上存在一些问题或错误,因为它肯定会超过2个。
答案 0 :(得分:10)
在计算直方图方面,虽然存在轻微误差,但每个强度的频率计算是正确的......稍后会有更多。另外,我个人会避免在这里使用循环。请参阅本文末尾的小记。
然而,您的代码存在三个问题:
histogram_values
应包含您的直方图,但您正在使用0:255
的向量初始化直方图。每个强度值应以0 开始,因此您实际需要这样做:
histogram_values = zeros(256,1);
for
循环中的轻微错误您的强度范围从0到255,但MATLAB开始索引为1.如果您的强度为0,则会出现越界错误。因此,正确的做法是取p
并将其添加为1,以便开始在1开始索引。但是,我需要指出的一个复杂性是,如果你有一个uint8
精度图片,加1为强度255将简单地将值饱和为255.它不会达到256 ....所以你也应该谨慎使用double
以确保达到256
因此:
histogram_values = zeros(256,1);
for i = 1:rows
for j = 1:cols
p = double(openImage(i,j)) + 1;
histogram_values(p) = histogram_values(p) + 1;
end
end
histogram
您应该覆盖histogram
的行为并包含边缘。基本上,这样做:
histogram(histogram_values, 0:255);
第二个向量指定我们应该在x
- 轴上放置小节的位置。
您可以在没有任何for
循环的情况下自己完全实现直方图计算。您可以结合bsxfun
,permute
,reshape
和两个sum
来电来尝试此操作:
mat = bsxfun(@eq, permute(0:255, [1 3 2]), im);
h = reshape(sum(sum(mat, 2), 1), 256, 1);
如果您想更详细地了解此代码的工作原理,请参阅kkuilla与我之间的对话:http://chat.stackoverflow.com/rooms/81987/conversation/explanation-of-computing-an-images-histogram-vectorized
但是,它的要点如下。
第一行代码创建一个1列的3D矢量,范围从permute
到0到255,然后使用bsxfun
和eq
(等于)函数,我们使用广播,这样我们得到一个3D矩阵,其中每个切片与灰度图像的大小相同,并给我们的位置等于感兴趣的强度。具体来说,第一个切片告诉你元素在哪里等于0,第二个切片告诉你哪个元素等于1,直到最后一个切片告诉你元素等于255的位置。
对于第二行代码,一旦我们计算了这个3D矩阵,我们计算两个总和 - 首先独立地对每一行求和,然后对该中间结果的每一列求和。然后我们得到每个切片的总和,它告诉我们每个强度有多少个值。因此,这是一个3D矢量,因此我们将reshape
转换回单个1D矢量以完成计算。
为了显示直方图,我会将bar
与histc
标志一起使用。如果我们使用cameraman.tif
图像,这是一个可重现的示例:
%// Read in grayscale image
openImage = imread('cameraman.tif');
[rows,cols] = size(openImage);
%// Your code corrected
histogram_values = zeros(256,1);
for i = 1:rows
for j = 1:cols
p = double(openImage(i,j)) + 1;
histogram_values(p) = histogram_values(p) + 1;
end
end
%// Show histogram
bar(0:255, histogram_values, 'histc');
我们得到了这个:
答案 1 :(得分:3)
您的代码看起来是正确的。问题在于调用直方图。您需要在直方图调用中提供二进制数,否则将自动计算它们。
尝试这个简单的修改,调用词干来获得正确的情节,而不是依靠直方图
function h = histogram_matlab(imageSource)
openImage = rgb2gray(imread(imageSource));
[rows,cols] = size(openImage);
histogram_values = [0:255];
for i = 1:rows
for j = 1:cols
p = openImage(i,j);
histogram_values(p) = histogram_values(p) + 1;
end
end
stem(histogram_values); axis tight;
编辑:在对代码进行一些检查后,您会收到0/1错误。如果您的像素值为零,那么histogram_value(p)
将为您提供索引错误
试试这个。这个简单的案例不需要矢量化:
function hv = histogram_matlab_vec(I)
assert(isa(I,'uint8')); % for now we assume uint8 with range [0, 255]
hv = zeros(1,256);
for i = 1 : numel(I)
p = I(i);
hv(p + 1) = hv(p + 1) + 1;
end
stem(hv); axis tight;
end