15

I have a grid in a binary image (may be rotated). How can I know approximate formula for that grid using MATLAB?

Example image:


(source: sjtu.edu.cn)

Sometimes these black dots are missing, so I need formula or ‘a way’ to estimate possible center of these black dots.

I have tried by using regionprops, it help me to get center of these exist black dots, but no idea if black dots a missing

clear all
im = imread('print5.jpg');
im = im2bw(im);
[sy,sx] = size(im);
im = imcomplement(im);
im(150:200,100:150) = 0; % let some dots missing!
im = imclearborder(im);
st = regionprops(im, 'Centroid');

imshow(im) hold on;
for j = 1:numel(st)
    px = round(st(j).Centroid(1,1));
    py = round(st(j).Centroid(1,2));
    plot(px,py,'b+')
end
Glorfindel
  • 21,988
  • 13
  • 81
  • 109
user2368870
  • 371
  • 2
  • 3
  • 1
    Try looking ad the frequency content: `fft2` the grid is very regular, you should be able to spot peaks in the frequency domain. – Shai May 10 '13 at 06:32
  • 5
    If you edit all the information from the above comments into your question you should be able to get it re-opened. – Paul R May 10 '13 at 07:20
  • 5
    wow, this attracted a lot of downvotes in a short amount of time. I understand why it was closed, but did it really deserve -20 downvotes... – Amro May 10 '13 at 07:54
  • 3
    @Amro If you check out the front-page, you'll see that questions haven't been updated in over an hour, that's probably why they're doing it. Usually questions with -3/-4 downvotes are removed from the 'newest questions' section anyway to stop further downvoting I believe, but because of this glitch, this question is still being shown :/ – dsgriffin May 10 '13 at 08:06
  • 3
    I added OP's code and description from comments. That should slow down ppl who downvote blindly. Those who did, please reconsider.. – Amro May 10 '13 at 08:12
  • That's a shame, this is a good question. – 000 Jun 12 '13 at 15:24
  • is this some sort of optical illusion? – Albert Laure Oct 14 '13 at 05:42

2 Answers2

41

here's a way using fft in 1D over the x and y projection:

First, I'll blur the image a bit to smooth the high freq noise by convolving with a gaussian:

m=double(imread('print5.jpg'));
m=abs(m-max(m(:))); % optional line if you want to look on the black square as "signal"
H=fspecial('gaussian',7,1);
m2=conv2(m,H,'same');

then I'll take the fft of a projection of each axis:

delta=1;
N=size(m,1);
df=1/(N*delta);        % the frequency resolution (df=1/max_T)
f_vector= df*((1:N)-1-N/2);     % frequency vector 

freq_vec=f_vector;
fft_vecx=fftshift(fft(sum(m2)));
fft_vecy=fftshift(fft(sum(m2')));
plot(freq_vec,abs(fft_vecx),freq_vec,abs(fft_vecy))

enter image description here

So we can see both axis yield a peak at 0.07422 which translate to a period of 1/0.07422 pixels or ~ 13.5 pixels.

A better way to get also the angle info is to go 2D, that is:

ml= log( abs( fftshift (fft2(m2)))+1);
imagesc(ml) 
colormap(bone)

enter image description here

and then apply tools such as simple geometry or regionprops if you want, you can get the angle and size of the squares. The size of the square is 1/ the size of the big rotated square on the background ( bit fuzzy because I blurred the image so try to do that without that), and the angle is atan(y/x). The distance between the squares is 1/ the distance between the strong peaks in the center part to the image center.

so if you threshold ml properly image say

 imagesc(ml>11)

you can access the center peaks for that...

yet another approach will be morphological operation on a binary image, for example I threshold the blurred image and shrink objects to points. It removes pixels so that objects without holes shrink to a point:

BW=m2>100;
BW2 = bwmorph(BW,'shrink',Inf);
figure, imshow(BW2)

enter image description here

Then you practically have a one pixel per lattice site grid! so you can feed it to Amro's solution using Hough transform, or analyze it with fft, or fit a block, etc...

bla
  • 25,846
  • 10
  • 70
  • 101
28

You could apply Hough transform to detect the grid lines. Once we have those you can infer the grid locations and the rotation angle:

%# load image, and process it
img = imread('print5.jpg');
img = imfilter(img, fspecial('gaussian',7,1));
BW = imcomplement(im2bw(img));
BW = imclearborder(BW);
BW(150:200,100:150) = 0;    %# simulate a missing chunk!

%# detect dots centers
st = regionprops(BW, 'Centroid');
c = vertcat(st.Centroid);

%# hough transform, detect peaks, then get lines segments
[H,T,R] = hough(BW);
P  = houghpeaks(H, 25);
L = houghlines(BW, T, R, P);

%# show image with overlayed connected components, their centers + detected lines
I = imoverlay(img, BW, [0.9 0.1 0.1]);
imshow(I, 'InitialMag',200, 'Border','tight'), hold on
line(c(:,1), c(:,2), 'LineStyle','none', 'Marker','+', 'Color','b')
for k = 1:length(L)
    xy = [L(k).point1; L(k).point2];
    plot(xy(:,1), xy(:,2), 'g-', 'LineWidth',2);
end
hold off

(I'm using imoverlay function from the File Exchange)

The result:

grid_lines_overlayed

Here is the accumulator matrix with the peaks corresponding to the lines detected highlighted:

accumulator_peaks


Now we can recover the angle of rotation by computing the mean slope of detected lines, filtered to those in one of the two directions (horizontals or verticals):

%# filter lines to extract almost vertical ones
%# Note that theta range is (-90:89), angle = theta + 90
LL = L( abs([L.theta]) < 30 );

%# compute the mean slope of those lines
slopes = vertcat(LL.point2) - vertcat(LL.point1);
slopes = atan2(slopes(:,2),slopes(:,1));
r = mean(slopes);

%# transform image by applying the inverse of the rotation
tform = maketform('affine', [cos(r) sin(r) 0; -sin(r) cos(r) 0; 0 0 1]);
img_align = imtransform(img, fliptform(tform));
imshow(img_align)

Here is the image rotated back so that the grid is aligned with the xy-axes:

aligned_img

Amro
  • 123,847
  • 25
  • 243
  • 454