如何使用MATLAB获取任意图像的背景颜色?

3

这是另一个源自这个的问题,

我该如何编程获取图像的背景色?

例如:

alt text

对于上面的图像,背景色为白色。


你是在尝试(1)分析像素矩阵以分离前景和背景特征,以确定主要的背景颜色,(2)识别用于填充图像的颜色,(3)从MATLAB图像/轴/图形对象中检索背景颜色属性,还是(4)完全不同的事情?一个例子会让回答你的问题更容易。 - RTBarnard
问题出在你对“背景颜色”的定义上。以这张图片为例(http://farm4.static.flickr.com/3104/3238518077_1ef13a8e93.jpg),什么应该被视为背景颜色?天空的颜色?水的颜色?还是山的颜色?一张图片只是一组有色像素,一个包含了一堆数字的矩阵,每个点都被解释为颜色。没有固定的“背景颜色”。你必须自己决定如何定义它(例如图像中所有颜色的平均值,最常出现的颜色等)。 - gnovice
@gnovice,我无法为您提供的图像背景颜色提供一个好的定义,但是我可以为我帖子中的图像提供一个定义,它是白色的。换句话说,我不知道如何在一般情况下定义它。 - user198729
@gnovice,是否可以将图像转换为正方形,而无需显式指定背景色,但仍保持结果图像自然? - user198729
@user198729:在另一个问题中,我使用PADARRAY的第三个解决方案是我能想到的唯一一个为您选择背景颜色的解决方案。使图像变成正方形而填充它的唯一其他方法是沿着较短的维度拉伸它,但这会扭曲图像。RTBarnard提供了一个自动选择背景颜色例程的好例子,但这样的事情很难以一种处理所有图像的方式来完成。 - gnovice
糟糕,我不知道为什么RTBarnard的答案没有完全起作用,返回的“modeColor”是0,而实际上应该是1,导致图像背景颜色变成了黑色:http://www.google.com/intl/en_ALL/images/srpr/logo1w.png - user198729
1个回答

5
正如在问题本身的评论中讨论的那样,“背景颜色”的概念相当主观,因此不可能编写算法以保证所有输入都得到所需的结果。
尽管如此,我认为我理解了你想要实现的内容,并编写了一些MATLAB函数,对于我尝试过的许多输入图像,它们都能成功地识别出可能的背景颜色。
我使用的启发式方法基于这样一个观察:通常情况下,图像的背景颜色很可能是低频信息区域,而前景则很可能是高频信息。 (请注意,当不是这种情况时,我的“getBackgroundColor”函数将失败。)因此,我在频率域中隔离高频信息,将其变换回空间域,将选择的像素“扩散”,以覆盖广泛的高频区域,然后简单地删除这些像素。
代码中有很多可以调整和优化以提高特定应用程序性能的地方,但就目前情况而言,它似乎可以很好地处理各种测试案例。
getBackgroundColor.m:
function [img, meanColor, modeColor] = getBackgroundColor (img)
%
% function [img, meanColor, modeColor] = getBackgroundColor (img)
%
%    img   -   Either a string representing the filename of an image to open
%              or an image itself.  If the latter, it must be either a
%              3-dimensional matrix representing an RGB image or a 2-dimensional
%              matrix representing a grayscale image.

if ischar(img)
  img = imread(imageFile);
end
img = double(img);

% Handle RGB and Grayscale separately.
if ndims(img)==3
  % There are probably some spiffy ways to consolidate this sprawl
  % so that the R, G, and B channels are not being processed
  % independently, but for the time being, this does work.
  red   = getBG(img(:, :, 1));
  green = getBG(img(:, :, 2));
  blue  = getBG(img(:, :, 3));

  % For each channel, remove the "foreground" regions identified in
  % each of the other channels.
  red(isnan(green)) = NaN;
  red(isnan(blue)) = NaN;

  green(isnan(red)) = NaN;
  green(isnan(blue)) = NaN;

  blue(isnan(red)) = NaN;
  blue(isnan(green)) = NaN;

  % Compute the mean and mode colors.
  meanColor = [ ...
      mean(mean( red(~isnan(red)) )) ...
      mean(mean( green(~isnan(green)) )) ...
      mean(mean( blue(~isnan(blue)) )) ];
  modeColor = [ ...
      mode(mode( red(~isnan(red)) )) ...
      mode(mode( green(~isnan(green)) )) ...
      mode(mode( blue(~isnan(blue)) )) ];

  % Update each the foreground regions of each channel and set them
  % to their mean colors.  This is only necessary for visualization.
  red(isnan(red)) = meanColor(1);
  green(isnan(green)) = meanColor(2);
  blue(isnan(blue)) = meanColor(3);

  img(:, :, 1) = red;
  img(:, :, 2) = green;
  img(:, :, 3) = blue;
else
  img = getBG(img);
  meanColor = mean(mean( img( ~isnan(img) ) ));
  modeColor = mode(mode( img( ~isnan(img) ) ));
  img(isnan(img)) = meanColor;
end

% Convert the image back to integers (optional)
img = uint8(img);

% Display the results before returning
display(meanColor)
display(modeColor)



  function image = getBG (image)
      mask = getAttenuationMask(size(image), min(size(image)) / 2, 0, 1);

      % Assume that the background is mostly constant, so isolate the high-frequency
      % parts of the image in the frequency domain and then transform it back into the spatial domain
      fftImage = fftshift(fft2(image));
      fftImage = fftImage .* mask;
      invFftImage = abs(ifft2(fftImage));

      % Expand the high-frequency areas of the image and fill in any holes.  This should
      % cover all but the (hopefully) low frequency background areas.
      edgeRegion = imfill(imdilate(invFftImage, strel('disk', 4, 4)), 'holes');

      % Now remove the parts of the image that are covered by edgeRegion
      edgeMean = mean(mean(edgeRegion));
      image(edgeRegion>edgeMean) = NaN;
  end
end

getAttenuationMask.m:

function mask = getAttenuationMask (maskSize, radius, centerValue, edgeValue)
%
% function mask = getAttenuationMask (maskSize, radius, centerValue, edgeValue)
%

if nargin==2
  centerValue = 1;
  edgeValue = 0;
end

width = maskSize(1);
height = maskSize(2);

mx = width / 2;
my = height / 2;

mask=zeros(maskSize);

for i=1:width
  for j=1:height
      d = sqrt( (i-mx)^2 + (j-my)^2 );
      if (d >= radius)
        d = edgeValue;
      else
        d = (centerValue * (1 - (d / radius))) + (edgeValue * (d / radius));
      end

      mask(i, j) = d;
  end
end

@RTBarnard,感谢您的回答!您能否看一下这个问题,看看是否有可能将图像转换为正方形,而无需显式分配背景颜色,但仍保持结果图像自然?http://stackoverflow.com/questions/2605202/how-to-automate-the-padding-for-arbitary-images-using-matlab - user198729
这种依赖于背景颜色的解决方案对于没有明显背景颜色的图像效果不佳,比如这个:http://farm4.static.flickr.com/3104/3238518077_1ef13a8e93.jpg - user198729
你在之前的两个评论中提供的图片都是我的测试图片之一。天空图像产生了一个漂亮、合理的蓝色;Google标志产生了接近白色(对于平均值)和纯白色(对于模式)。此外,在我的4核3GHz Mac Pro上,Google图像处理时间不到1秒,而天空图像需要大约1.5秒的处理时间。然而,可能会导致问题的一个因素是颜色格式。请确保图像是灰度或24位RGB。后者从imread加载为三维数组。 - RTBarnard
该图像的问题在于它使用了索引颜色。我的算法需要灰度或直接颜色映射。我已经使用ImageMagick对您提到的图像进行了转换,一旦完成,它就可以正常工作。请尝试以下任何一张图片:http://interfix.arane.us/stackoverflow/google.jpg,http://interfix.arane.us/stackoverflow/sky.jpg,http://interfix.arane.us/stackoverflow/sky-gray.tif。 - RTBarnard
我已经修改了我的回复以满足您的关注。getBackgroundColor函数现在可以接受图像或文件名。如果提供了字符数组,则假定所提供的参数是文件名,然后加载图像。否则,它假定提供了直接颜色灰度或RGB图像。这对您有用吗? - RTBarnard
显示剩余15条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接