# Circle detection with incomplete edges

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

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
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.

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)


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

%%%%%%%%%%%%%%%%%%%%%%


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)