-1

I have one vector of radii and second vector of hundreds of [X,Y] coordinates. For each possible coordinate-radius pair I have count all black pixels within a circle (whose center is placed in the coordinate) on an input binary image.

What is the fastest way to do it? My only idea is to iterate through every pixel of an image, check the circle equation and then the pixel color but it doesn't seem much optimized for several hundred such operations.

matt-pielat
  • 1,659
  • 3
  • 20
  • 33
  • 2
    Have you tried your idea for a small number? does it work? post some code. Also post your image. – Santhan Salai May 23 '15 at 11:20
  • perhaps you can use some function to get the border area of the black transition to another pixel and then mark it as an area, in each area find some reasonable radius, then use the area as the pixelcount of that area. Then you sum all the pixelcount of each area. It's been a long time since I opened my Matlab, sorry for not being explaining it precisely what function used and whatnot, just trying to give some ideas :) – Isaac May 23 '15 at 11:28
  • Assuming you'll have to iterate over all pixels: If your radii are constant, you could pre-compute all pixels belonging to `[X,Y]` (assuming you have a uniform grid). That way you could at least save this part of the computation. So for each radius you would have a list of pixels to check given a coordinate pair: `[X-3, Y-3], [X-2, Y-3]...` and so on. – Potaito May 23 '15 at 11:31
  • Can the circles overlap ? if yes be careful not to count the pixels covered twice. – Hoki May 23 '15 at 12:13

2 Answers2

1

Here is one implementation:

Advantages:

  • No loops, meshgrid/ndgrid. Instead used faster bsxfun and pdist2

  • Dots are counted only once, even when the circles overlap.

  • Variable radius used (radius of all circle is not same)

Code:

%// creating a binary image with little black dots
A = randi(600,256); 
imbw = A ~= 1;

%// Your binary image with black dots
imshow(imbw);

%// getting the index of black dots
[dotY, dotX] = find(~imbw);

nCoords = 10;               %// required number of circles

%// generating its random coordinates as it is unknown here
Coords = randi(size(A,1),nCoords,2);

%// calculating the distance from each coordinate with every black dots
out = pdist2(Coords,[dotX, dotY]).';  %//'

%// Getting only the black dots within the radius
%// using 'any' avoids calculating same dot twice
radius = randi([10,25],1,size(Coords,1));
pixelMask = any(bsxfun(@lt, out, radius),2);
nPixels = sum(pixelMask);

%// visualizing the results by plotting
hold on
scatter(dotX(pixelMask),dotY(pixelMask));
viscircles([Coords(:,1),Coords(:,2)],radius.');    %//'
hold off

Output:

>> nPixels

nPixels =

19

enter image description here

Santhan Salai
  • 3,888
  • 19
  • 29
1

Matlab is great for working with images thanks to the matrix syntax. It does also work with indices so most time you can avoid "iterating through pixels" (although sometimes you'll still have to).

Instead of checking all the pixels within each circle, and having to detect how many pixels were counted twice, another approach is to create a mask, the same size of you image. Blank this mask for each of your circles (so overlapping pixels are only 'blanked' once), then apply the mask on your original picture and count the remaining illuminated pixels.

For an example, I have to take some sample data, the image:

load trees
BW = im2bw(X,map,0.4);
imshow(BW)

treesBW

And 20 random point/circle coordinates (you can change the number of points and the min/max radii easily):

%// still building sample data
s = size(BW) ;
npt = 20 ; Rmin=5 ; Rmax=20 ; %// problem constants

x = randi([1 s(2)]   ,npt,1); %// random X coordinates
y = randi([1 s(1)]   ,npt,1); %//        Y
r = randi([Rmin Rmax],npt,1); %// radius size between 5 to 20 pixels.

Then we build your custom mask :

%// create empty mask with enough overlap for the circles on the border of the image
mask = false( s+2*Rmax ) ; 

%// prepare grid for a sub-mask of pixels, as wide as the maximum circle
xmask = -Rmax:Rmax ; 
[xg,yg] = ndgrid(xmask,xmask) ;
rg = sqrt( (xg.^2+yg.^2) ) ;    %// radius of each pixel in the subgrid

for ip=1:npt
    mrow = xmask+Rmax+y(ip) ;  %// calc coordinates of subgrid on original mask
    mcol = xmask+Rmax+x(ip) ;  %// calc coordinates of subgrid on original mask

    cmask = rg <= r(ip) ;      %// calculate submask for this radius
    mask(mrow,mcol) = mask(mrow,mcol) | cmask ; %// apply the sub-mask at the x,y coordinates
end
%// crop back the mask to image original size (=remove border overlap)
mask = mask(Rmax+1:end-Rmax,Rmax+1:end-Rmax) ;
imshow(mask)

bubbles

Then you just apply the mask and count :

%% // Now apply on original image
BWm = ~BW & mask ; %// note the ~ (not) operator because you want the "black" pixels
nb_black_pixels = sum(sum(BWm)) ;
imshow(BWm)

nb_black_pixels =
        5283

treeMasked

Hoki
  • 11,637
  • 1
  • 24
  • 43