I want to detect circles in the image below but this approach, of using cv2.approxPolyDP on contours and counting the sides of the polygon, doesn't work here since some of the circle is missing. Is there a faster way of detecting shapes in this case than using the Hough Transform?
this is John BG. Found a way to detect the traffic signal circle avoiding imfindcircles that requires the guessing of its parameter Sensitivity.
1.- Acquiring image
clear all;clc;close all
A=imread('001.jpg');
figure(1);imshow(A);
% 001
A1=A(:,:,1);
[sz1 sz2]=size(A1) % sz1: Y axis sz2: X axis
th1=120 % binarize
A2=A1;
A2(A2>th1)=255;
A2(A2<=th1)=0;
% figure(2);imshow(A2)
A3=bwmorph(A2,'skel',Inf) % clean and thin
figure(3);imshow(A3)
% 002
A4=bwlabel(A3,8); % number objects
hf4=figure(4);imshow(A4)
range_A4=unique(A4)'; % count objects
amount_segments=max(range_A4)
PA={}
PA: each element of cell PA contains all pixels of 1 segment
CM= [1,1,0; % yellow
1,0,1; % magenta
0,1,1; % cyan
1,0,0; % red
0,1,0; % green
0,0,1; % blue
1,1,1] % white
nCM=[mod([1:1:amount_segments],size(CM,1))];
nCM(nCM==0)=size(CM,1)
for k=1:1:amount_segments % put pixels same object in same cell and colour objects
[col1,row1,v]=find(A4==k);
P_segment=[col1 row1];
PA{k}=P_segment
hold all % check all found points belong to same segment
for m=1:1:length(P_segment)
plot(hf4.CurrentAxes,P_segment(m,2),P_segment(m,1),'*','LineStyle',':','Color',CM(nCM(k),:))
end
end
% 003
nPA=0;
confirming that majority objects are small and of no interest
for k=1:1:size(PA,2)
nPA=[nPA size(PA{k},1)]
end
max(nPA)
figure(5);histogram(nPA,max(nPA))
nPA(1)=[]
% 004
sizeCelln=@(n,cell1) size(cell1{n},1)
n2 format: [amount pixels in given segment , unique numeral identifying each segment in A4]
n2=[0]
for k=1:1:amount_segments
if sizeCelln(k,PA)>floor(max(nPA)/3)
n2=[n2 k ]; % k is same as A4(PA{k}(1,1),PA{k}(1,2))
end
end
n2(1)=[];
PA2=PA(n2)
keep only the ones large enough to potentially look like circle
close(hf4);
hf4=figure(4);imshow(A4)
hold all
for k=n2 % check all found points belong to same segment
L3=PA{k}
for m=1:1:size(L3,1)
plot(hf4.CurrentAxes,L3(m,2),L3(m,1),'b*')
end
end
Following, the best candidates (by amount pixels only) to circle
s=[0 0 0 0 0 0 0];
s format: [numeral mean(R) var(R) range(x) range(y) midpoint(x) midpoint(y)]
for k=n2
L2=PA{k};
x_midpoint=.5*(min(L2(:,1))+max(L2(:,1)))
y_midpoint=.5*(min(L2(:,2))+max(L2(:,2)))
R=((L2(:,1)-x_midpoint).^2+(L2(:,2)-y_midpoint).^2).^.5
s=[s; k mean(R) var(R) range(L2(:,1)) range(L2(:,2)) x_midpoint y_midpoint]
end
s(1,:)=[];
The segment(s) that best resemble(s) circle(s) has/have characteristics clearly distinctive against other geometries like straight lines, squares, rectangles or ellipses.
nid=s(:,1);
R0=floor(s(:,2));
Rvar=.1*floor(10*s(:,3));
x_range=s(:,4);y_range=s(:,5);
x_mpoint=s(:,6);y_mpoint=s(:,7)
T1=table(nid,R0,Rvar,x_range,y_range,x_mpoint,y_mpoint)
T1 =
10×7 table
nid R0 Rvar x_range y_range x_mpoint y_mpoint
___ ___ _____ _______ _______ ________ ________
24 105 31.2 226 199 122 217.5
28 67 260.6 92 166 300 218
29 70 245.8 95 165 407.5 217.5
32 58 416.1 142 121 137 205.5
35 52 280.1 126 107 139 204.5
43 57 329.4 139 117 106.5 234.5
46 51 288.3 124 105 106 235.5
50 28 140.9 44 93 409 244.5
54 85 733.5 241 108 121.5 280
57 69 628.9 196 67 344 272.5
Circles have properties like:
single point centers
constant radius
range(x) ~ range(y)
Only segment points distributed along a circle meet these 3 conditions.
n=[1:1:numel(nid)]
R2half_x_range_ratio=R0-abs(x_range)/2
R2half_y_range_ratio=R0-abs(y_range)/2
dxdy=abs(abs(x_range)-abs(y_range))
hf8=figure(6);plot(n,R0,n,Rvar,n,dxdy); grid on
xticks(hf8.CurrentAxes,[1:1:numel(nid)])
hf8.CurrentAxes.XTickLabelMode='manual'
hf8.CurrentAxes.XTickLabel=string(nid)'
xlabel('A4 segment numerals')
legend(hf8.CurrentAxes,'R','var(R)','\Deltax-\Deltay')
In this context the key parameters to catch circles are lowest radius dispersion and at the same time among segments with largest amount of pixels.
Even with the horn marked with a red arrow in the previous figure, the circle found is fairly centred and the radius is correct.
lowest Radius dispersion
n10=find(Rvar==min(Rvar))
nid(n10)
C1=[T1.x_mpoint(n10) T1.y_mpoint(n10)]
R1=T1.R0(n10)
da=2*pi/100;
a=[0:da:2*pi];
Px1=R1*cos(a)+C1(2);Py1=R1*sin(a)+C1(1);
hold all;plot(hf4.CurrentAxes,Px1,Py1,'r','LineWidth',10)
So, this is it, the traffic signal circle has been correctly detected.
Following, additional criteria that have been checked, but are not as clear as the lowest radius centred variance:
Similar x and y ranges
n11=find(dxdy==min(dxdy))
nid(n11)
C2=[T1.x_mpoint(n11) T1.y_mpoint(n11)]
R2=T1.R0(n11)
Px2=R2*cos(a)+C2(:,2)
Py2=R2*sin(a)+C2(:,1)
hold all;
plot(hf4.CurrentAxes,Px2(1,:),Py2(1,:),'m','LineWidth',2)
plot(hf4.CurrentAxes,Px2(2,:),Py2(2,:),'m','LineWidth',2)
Segments with R Most similar to half ranges
RxRy=abs(R2half_x_range_ratio)-abs(R2half_y_range_ratio)
n12=find(RxRy==min(RxRy))
nid(n12)
C3=[T1.x_mpoint(n12) T1.y_mpoint(n12)]
R3=T1.R0(n12)
discarding those segments with Radius too small
Without removing shortest segments
this is, without removing any segment with radius 1/3rd or shorter than peak radius.
% Run lines 1 to 68 of this script, then hop here:
PA2=PA
n2=[1:1:numel(PA)]
T2=table(nid,R0,Rvar,x_range,y_range,x_mpoint,y_mpoint)
T2 =
85×7 table
nid R0 Rvar x_range y_range x_mpoint y_mpoint
___ ___ _____ _______ _______ ________ ________
1 4 3.2 14 4 57 3
2 10 37.4 37 16 85.5 9
3 10 36.1 36 23 94 12.5
..
83 2 2.2 7 6 431.5 422
84 2 0.9 7 2 437.5 428
85 3 0.5 9 7 448.5 430.5
The compact script is available by email, feel free to contact, email address in Profile. Also welcome any correction comment or suggestion for further development of this answer.
Appreciating time and attention
John BG
%%%%%%%%%%%%%%%%%%%%%%
Comments:
1.- When too many small objects the graphs get too cluttered.
2.- The start picture does not have perspective correction.
This causing circles to look like ellipses.
3.- The above steps also allow correction of perspective angle, and even calculate dimensions of nearby objects.
4.- The traffic signal dish has to be relatively large to all other circle-like shapes which is the same as the photo has to be taken relatively close to the signal traffic, for these steps to.
5.- The command imfindcircles requires the parameter Sensitivity to go up to .95 in order to start detecting circles, but it oddly shows a really span of Sensitivity where just a valid circle is caught. Just a bit off 0.95 and as shown in the 2nd figure below a multitude of non-relevant circles are added.
Rmin=20;Rmax=200;
close(hf4);
hf4=figure(4);imshow(A4)
[centers, radii] = imfindcircles(A4,[Rmin Rmax],'ObjectPolarity','dark','Sensitivity',.98);
viscircles(centers, radii,'Color','b');
close(hf4);
hf4=figure(4);imshow(A4)
[centers, radii] = imfindcircles(A4,[Rmin Rmax],'ObjectPolarity','dark','Sensitivity',.98);
viscircles(centers, radii,'Color','b');
% 012