matlab: efficient computation of local histograms within circular neighboorhoods

旧城冷巷雨未停 提交于 2019-12-29 06:24:13

问题


I've an image over which I would like to compute a local histogram within a circular neighborhood. The size of the neighborhood is given by a radius. Although the code below does the job, it's computationally expensive. I run the profiler and the way I'm accessing to the pixels within the circular neighborhoods is already expensive.

Is there any sort of improvement/optimization based maybe on vectorization? Or for instance, storing the neighborhoods as columns? I found a similar question in this post and the proposed solution is quite in the spirit of the code below, however the solution is still not appropriate to my case. Any ideas are really welcomed :-) Imagine for the moment, the image is binary, but the method should also ideally work with gray-level images :-)

[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

EDIT 1 Given the received feedback, I tried to update the solution. However, it's not yet correct

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

回答1:


Following on the technique I described in my answer to a similar question you could try to do the following:

  1. compute the index offsets from a particular voxel that get you to all the neighbors within a radius
  2. Determine which voxels have all neighbors at least radius away from the edge
  3. Compute the neighbors for all these voxels
  4. Generate your histograms for each neighborhood

It is not hard to vectorize this, but note that

  1. It will be slow when the neighborhood is large
  2. It involves generating an intermediate matrix that is NxM (N = voxels in image, M = voxels in neighborhood) which could get very large

Here is the code:

% 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

A 2D version of the same code (which might be all you need, and is considerably faster):

% 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



回答2:


You're right, I don't think that colfilt can be used as you're not applying a filter. You'll have to check the correctness, but here's my attempt using im2col and your diskfilter function (I did remove the conversion to double so it now output logicals):

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);

If you want to set your ranges (edges) based on each neighborhood then you'll need to make sure that the vector is always the same length if you want to build a big matrix (and then the rows of that matrix won't correspond to each other).

You should note that the shape of the disk returned by fspecial is not as circular as what you were using. It's meant to be used a smoothing/averaging filter so the edges are fuzzy (anti-aliased). Thus when you use ~=0 it will grab more pixels. It'd stick with your own function, which is faster anyways.




回答3:


You could try processing with an opposite logic (as briefly explained in the comment)

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


来源:https://stackoverflow.com/questions/21750989/matlab-efficient-computation-of-local-histograms-within-circular-neighboorhoods

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!