【问题标题】:matlab: efficient computation of local histograms within circular neighboorhoodsmatlab:圆形邻域内局部直方图的有效计算
【发布时间】:2014-03-12 03:20:52
【问题描述】:

我有一张图像,我想在其上计算圆形邻域内的局部直方图。邻域的大小由radius 给出。尽管下面的代码可以完成这项工作,但它的计算成本很高。我运行分析器,我访问圆形邻域内的像素的方式已经很昂贵了。

是否有任何基于矢量化的改进/优化?或者例如,将社区存储为列? 我在post 中发现了一个类似的问题,并且建议的解决方案非常符合下面代码的精神,但是该解决方案仍然不适合我的情况。任何想法都非常受欢迎:-) 现在想象一下,图像是二进制的,但该方法也应该理想地适用于灰度图像:-)

[rows,cols] = size(img);
hist_img      = zeros(rows, cols, 2);
[XX, YY]      = meshgrid(1:cols, 1:rows);
for rr=1:rows
        for cc=1:cols
            distance      = sqrt( (YY-rr).^2 + (XX-cc).^2  );
            mask_radii = (distance <= radius);
            bwresponses   = img(mask_radii);
            [nelems, ~]   = histc(double(bwresponses),0:255);
            % do some processing over the histogram
            ...
        end
end

编辑 1 鉴于收到的反馈,我尝试更新解决方案。但是,它还不正确

radius = sqrt(2.0);
disk   = diskfilter(radius);
fun    = @(x) histc( x(disk>0), min(x(:)):max(x(:)) ); 
output = im2col(im, size(disk), fun);

function disk = diskfilter(radius)
    height  = 2*ceil(radius)+1;
    width   = 2*ceil(radius)+1;
    [XX,YY] = meshgrid(1:width,1:height);
    dist    = sqrt((XX-ceil(width/2)).^2+(YY-ceil(height/2)).^2);
    circfilter = (dist <= radius);
end

【问题讨论】:

  • 当速度真的很重要时,我会为它混合 c 函数
  • 也许可以尝试blockproc,块大小为radius,并仅在每个块的内切圆上计算直方图。
  • 看吧,块处理和移动过滤器不一样。
  • @Jigg 是正确的。根据您在第一次尝试中所做的事情,您可能应该改用 colfiltim2col
  • 对于二值图像,您只需将图像添加到 N 个不同的位置,其中 N 是圆形内核中的像素数。对于灰度级,(矩阵)操作数为 N*(Q-1),其中 Q 是量化级数。

标签: performance matlab optimization image-processing histogram


【解决方案1】:

按照我在answer to a similar question 中描述的技术,您可以尝试执行以下操作:

  1. 计算特定体素的索引偏移量,让您到达半径内的所有邻居
  2. 确定哪些体素的所有邻居都至少距离边缘半径
  3. 计算所有这些体素的邻居
  4. 为每个社区生成直方图

矢量化并不难,但请注意

  1. 社区很大时会很慢
  2. 它涉及生成一个 NxM 的中间矩阵(N = 图像中的体素,M = 邻域中的体素),它可能会变得非常大

代码如下:

% generate histograms for neighborhood within radius r
A = rand(200,200,200);
radius = 2.5;
tic
sz=size(A);
[xx yy zz] = meshgrid(1:sz(2), 1:sz(1), 1:sz(3));
center = round(sz/2);
centerPoints = find((xx - center(1)).^2 + (yy - center(2)).^2 + (zz - center(3)).^2 < radius.^2);
centerIndex = sub2ind(sz, center(1), center(2), center(3));

% limit to just the points that are "far enough on the inside":
inside = find(xx > radius+1 & xx < sz(2) - radius & ...
    yy > radius + 1 & yy < sz(1) - radius & ...
    zz > radius + 1 & zz < sz(3) - radius);

offsets = centerPoints - centerIndex;
allPoints = 1:prod(sz);
insidePoints = allPoints(inside);
indices = bsxfun(@plus, offsets, insidePoints);

hh = histc(A(indices), 0:0.1:1);  % <<<< modify to give you the histogram you want
toc

相同代码的 2D 版本(这可能就是您所需要的,而且速度要快得多):

% generate histograms for neighborhood within radius r
A = rand(200,200);
radius = 2.5;
tic
sz=size(A);
[xx yy] = meshgrid(1:sz(2), 1:sz(1));
center = round(sz/2);
centerPoints = find((xx - center(1)).^2 + (yy - center(2)).^2  < radius.^2);
centerIndex = sub2ind(sz, center(1), center(2));

% limit to just the points that are "far enough on the inside":
inside = find(xx > radius+1 & xx < sz(2) - radius & ...
    yy > radius + 1 & yy < sz(1) - radius);

offsets = centerPoints - centerIndex;
allPoints = 1:prod(sz);
insidePoints = allPoints(inside);
indices = bsxfun(@plus, offsets, insidePoints);

hh = histc(A(indices), 0:0.1:1);  % <<<< modify to give you the histogram you want
toc

【讨论】:

  • @Wok - 是的,这部分是为了回应您在我发布的另一个答案中的评论!
  • @Floris,感谢您的发帖。据我了解,直方图不是在窗口不完全适合(points far enough on the inside)的邻域中计算的,对吗?是否可以计算直方图,还包括边界处的那些像素?在这种情况下,应仅针对有效/现有像素计算直方图并消除例如 padded 像素?
  • 比如只有一个值,建直方图是什么意思?
  • @Floris 因此,我认为应该只为完整的社区构建直方图。
  • @wok 为什么你认为我只为完整的社区写了这个例子(除了“它更容易”)?...
【解决方案2】:

你是对的,我不认为colfilt 可以使用,因为你没有应用过滤器。您必须检查正确性,但这是我使用im2col 和您的diskfilter 函数的尝试(我确实删除了到double 的转换,所以它现在输出逻辑):

function circhist

% Example data
im = randi(256,20)-1;

% Ranges - I do this globally for the whole image rather than for each neighborhood
mini = min(im(:));
maxi = max(im(:));
edges = linspace(mini,maxi,20);

% Disk filter
radius = sqrt(2.0);
disk = diskfilter(radius); % Returns logical matrix

% Pad array with -1
im_pad = padarray(im, (size(disk)-1)/2, -1);

% Convert sliding neighborhoods to columns
B = im2col(im_pad, size(disk), 'sliding');

% Get elements from each column that correspond to disk (logical indexing)
C = B(disk(:), :);

% Apply histogram across columns to count number of elements
out = histc(C, edges)

% Display output
figure
imagesc(out)
h = colorbar;
ylabel(h,'Counts');
xlabel('Neighborhood #')
ylabel('Bins')
axis xy

function disk = diskfilter(radius)
height  = 2*ceil(radius)+1;
width   = 2*ceil(radius)+1;
[XX,YY] = meshgrid(1:width,1:height);
dist    = sqrt((XX-ceil(width/2)).^2+(YY-ceil(height/2)).^2);
disk = (dist <= radius);

如果您想根据每个邻域设置范围 (edges),那么如果您想构建一个大矩阵(然后是该矩阵的行),则需要确保该向量始终具有相同的长度矩阵不会相互对应)。

您应该注意fspecial 返回的磁盘形状不像您使用的那样圆。它旨在用于平滑/平均滤波器,因此边缘是模糊的(抗锯齿)。因此,当您使用~=0 时,它会抓取更多像素。它会坚持使用您自己的功能,反正速度更快。

【讨论】:

  • 感谢您的解决方案@horchler!我正在查看它,尤其是在从矩阵B 到矩阵C 的步骤之间。鉴于结果,看起来社区没有居中。例如,给定 [3,3] 的邻域和 (x,y) 处的像素,它会查找以下邻域:(x:x+3,y:y+3),而不是 (x-1:x+1,y-1:y+1)。我想,也许填充可以帮助?但是,问题是如何在计算列直方图时移除那些填充的新像素?
  • @Tin:如果您阅读了im2col 的帮助,它会清楚地说明在使用'sliding' 选项时它不会进行零填充。无论如何,您的问题并未定义边缘应该发生什么。不过,在 im 之前填充正确数量的零非常容易 - 请参阅 padarray
  • @Tin:disk 矩阵也可能只需要在应用disk(:) 之前进行转置,以便索引正确对应。
  • 感谢@horchler 的反馈。我认为,由于disk 是对称的,因此不需要转置它。此外,是的,填充物很容易制作。为了使列向量具有相同大小,我的想法是用例如填充图像。 -1 在边界中,然后在计算按列直方图时仅查找正像素值,例如:out = histc(C(C&gt;0), edges);,其中 C 是在边界处用 -1 填充图像后的按列矩阵.问题是我正在做的掩蔽(C&gt;0) 失去了矩阵的形状。有什么想法吗?
  • @Tin:我在答案中添加了-1 填充。 histc 不会计算具有该值的元素,只要 edges 的范围不包含它。我还添加了我正在使用的diskfilter 的版本。我不确定你是否需要做任何其他事情。但是,我仍然不知道您要使用直方图计算什么。您的问题没有明确定义。
【解决方案3】:

您可以尝试使用相反的逻辑进行处理(如评论中简要说明的那样)

hist = zeros(W+2*R, H+2*R, Q);
for i = 1:R+1;
  for j = 1:R+1;  
      if ((i-R-1)^2+(j-R-1)^2 < R*R)
         for q = 0:1:Q-1;
             hist(i:i+W-1,j:j+H-1,q+1) += (image == q);
         end
      end
   end
end

【讨论】:

  • 3 for 循环。这不是比OP更糟糕吗?
  • 取决于尺寸。当 QN 小时,这只野兽会飞。当 WH 很大时,可以通过块条带化来避免缓存惩罚。
猜你喜欢
  • 2012-10-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-12-17
  • 2014-03-10
  • 1970-01-01
  • 2015-04-27
  • 1970-01-01
相关资源
最近更新 更多