使用Matlab实现图像直方图

时间:2015-06-30 05:04:13

标签: matlab image-processing histogram

我正在试图实现(我知道有一个实现它的自定义函数)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')

我获得了一些图表:

enter image description here

这显然不是我所期望的,x轴应该从0到255,y轴从0到最大值存储在histogram_values中。

我需要获得imhist提供的内容:

enter image description here

我该如何设置?我做得不好吗?

修改

我已将代码更改为@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)

然而直方图不是预期的:

enter image description here

这里很明显,y轴上存在一些问题或错误,因为它肯定会超过2个。

2 个答案:

答案 0 :(得分:10)

在计算直方图方面,虽然存在轻微误差,但每个强度的频率计算是正确的......稍后会有更多。另外,我个人会避免在这里使用循环。请参阅本文末尾的小记。

然而,您的代码存在三个问题:

问题#1 - 直方图未正确初始化

histogram_values应包含您的直方图,但您正在使用0:255的向量初始化直方图。每个强度值应以0 开始,因此您实际需要这样做:

histogram_values = zeros(256,1);

问题#2 - 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

问题#3 - 未正确调用histogram

您应该覆盖histogram的行为并包含边缘。基本上,这样做:

histogram(histogram_values, 0:255);

第二个向量指定我们应该在x - 轴上放置小节的位置。

小记

您可以在没有任何for循环的情况下自己完全实现直方图计算。您可以结合bsxfunpermutereshape和两个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到25​​5,然后使用bsxfuneq(等于)函数,我们使用广播,这样我们得到一个3D矩阵,其中每个切片与灰度图像的大小相同,并给我们的位置等于感兴趣的强度。具体来说,第一个切片告诉你元素在哪里等于0,第二个切片告诉你哪个元素等于1,直到最后一个切片告诉你元素等于255的位置。

对于第二行代码,一旦我们计算了这个3D矩阵,我们计算两个总和 - 首先独立地对每一行求和,然后对该中间结果的每一列求和。然后我们得到每个切片的总和,它告诉我们每个强度有多少个值。因此,这是一个3D矢量,因此我们将reshape转换回单个1D矢量以完成计算。

为了显示直方图,我会将barhistc标志一起使用。如果我们使用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');

我们得到了这个:

enter image description here

答案 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