Tangency point of two ellipses does not coincide for one of the ellipses - matlab

The function below will calculate the tendency points of two ellipses including the cross points and the tangency points. I have two pictures which I have to retrieve the vanishing points from two ellipses plane of the car. The function works well for the first image but for the second image it gives me the wrong coordinates for the cross points and tangency points of the front wheel.
This is the function:
function points = ellipsesPointsSelection( C1, H1, C2, H2)
% this function takes into account two ellipses matrices and the
% homographies to apply to them (rotation + translation) and returns
% the four tangency points of the two bitangent lines to the two ellipses
syms l1 l2
l = [l1; l2; 1];
eqns = [l.'*inv(inv(H1).'*C1*inv(H1))*l == 0, l.'*inv(inv(H2).'*C2*inv(H2))*l == 0];
vars = [l1 l2];
[sol_l1, sol_l2] = vpasolve(eqns, vars);
L = [double(sol_l1.'); double(sol_l2.'); ones(1,4)];
X = (inv(H1).'*C1*inv(H1))\L;
Y = (inv(H2).'*C2*inv(H2))\L;
pt_tng_ul = X(:,1)'; % upper (u) point (pt) tangent (tng) to the left (l) ellipse
pt_tng_ur = Y(:,1)';
pt_tng_dr = Y(:,2)';
pt_tng_dl = X(:,2)';
pt_cross_ul = X(:,4)'; % upper (u) point (pt) tangent (tng) to the left (l) ellipse
pt_cross_dl = X(:,3)';
pt_cross_dr = Y(:,4)';
pt_cross_ur = Y(:,3)';
points = [ pt_tng_ul;
pt_tng_ur;
pt_tng_dr;
pt_tng_dl;
pt_cross_ul;
pt_cross_ur;
pt_cross_dr;
pt_cross_dl];
end
I'm using the function like this in my main code:
norm_matrix = diag([1, 1, 1]);% normalization off
H1 = norm_matrix*H1; % normalized rototranslation
H2 = norm_matrix*H2; % normalized rototranslation
ellipsesPoints = ellipsesPointsSelection(C1, H1, C2, H2);
H1_1 = norm_matrix*H1_1; % normalized rototranslation
H2_2 = norm_matrix*H2_2; % normalized rototranslation
ellipsesPoints2 = ellipsesPointsSelection(C1_1, H1_1, C2_2, H2_2);
and the result for image1 is:
and the result for the second image tangency points is this one:
As you can see the tangency points for the front wheel are not correct. I tried to fix another ellipse for the front wheel of the second image and the parameters of C and H changed a little bit but still, tangency points are wrong. I appreciate your comments and opinions regarding this problem.

The ellipse detection technique is simply detecting the best fit ellipse by receiving the semi-major and semi-minor axis of the ellipses. and return and ellipse which:
x_c_1 = ellipse1_Front(1,1); % x position of the center of the image1 front wheel
y_c_1 = ellipse1_Front(1,2); % y position of the center of the image1 front wheel
semi_maj_1 = ellipse1_Front(1,3); % semi major axis of the image1 front wheel
semi_min_1 = ellipse1_Front(1,4); % semi minor axis of the image1 front wheel
theta1 = ellipse1_Front(1,5)*pi/180; % angle of the image1 front wheel
in my case, the fifth element of the ellipse which is the theta1 was problematic for the ellipsePointsSelection function. i tried to change the input parameter for ellipseDetection function it means changing the semi-major and semi-minor values and found a suitable ellipse which also has tangency points aligned on it:
enter image description here

Related

How to divide the area outside a polygon between left and right with respect to a certain given value in MATLAB?

I have another blocking problem, I extracted the x and y coordinates outside the polygon, when plotting them the result is correct, however, I want to extract x and y coordinates on the right side outside the polygon(considered for x < 1078), and the x and y coordinates on the right side outside the polygon otherwise, however, when running this code (where I think it is a more logical one), it's dividing the whole set of coordinates (in and out of the polygon) between left and right.
This's my code, and the resulted images, the first one is the resulted plot of my code, and the second one is the initial polygon with the inside and outside areas, and if any one has a better idea I will appreciate their help, thanks a lot.
%% Check inpolygon function
nb_lignes = size(image_in_ref,1);
nb_colonnes = size(image_in_ref,2);
[Xq,Yq] = meshgrid(1:nb_colonnes,1:nb_lignes);%grille du cube
[in,on] = inpolygon(Xq,Yq,X_ref,Y_ref);
out = ~inpolygon(Xq,Yq,X_ref,Y_ref);
f = numel(Xq(in));
p = numel(Xq(on));
X_out = Xq(~in);
Y_out = Yq(~in);
figure
plot(X_ref,Y_ref) % polygon
axis equal
hold on
plot(Xq(in),Yq(in),'r-') % points inside
plot(X_out,Y_out,'b:') % points outside
plot(out,'y')
hold off
%% Cocatenate X_out and Y_out into one table
My_Matrix_out=horzcat(X_out(:),Y_out(:));
colNames = {'x_out','y_out'};
sTable_out = array2table(My_Matrix_out,'VariableNames',colNames);
%% Divide between left and right side limits
LeftTable_out = sTable_out(sTable_out.x_out< 1078, :);
RightTable_out = sTable_out(sTable_out.x_out >= 1078 , :);
%% Extract X and Y coordinates from each table separately
x_Left_out = LeftTable_out{:,'x_out'}; % or LeftTable{:,1}
y_Left_out = LeftTable_out{:,'y_out'}; % or LeftTable{:,2}
x_Right_out = RightTable_out{:,'x_out'};
y_Right_out = RightTable_out{:,'y_out'};
%% Plot Each Part Separately
figure
Left_Side_out =plot(x_Left_out,y_Left_out, 'r-');
hold on;
Right_Side_out=plot(x_Right_out, y_Right_out , 'g:');
The resulted plot of the posted code
The polygon with the inside and outside areas

Matlab: Why is the fill function not filling the area between two circles?

I'm trying to fill the intersecting area between two circles in Matlab. I've quite literally copied and pasted this piece of code from this article on Matlab Central.
t = linspace(0, 2*pi, 100);
cir = #(r,ctr) [r*cos(t)+ctr(1); r*sin(t)+ctr(2)]; % Circle Function
c1 = cir(1.0, [0; 0]);
c2 = cir(1.5, [1; 1]);
in1 = find(inpolygon(c1(1,:), c1(2,:), c2(1,:), c2(2,:))); % Circle #1 Points Inside Circle #2
in2 = find(inpolygon(c2(1,:), c2(2,:), c1(1,:), c1(2,:))); % Circle #2 Points Inside Circle #1
[fillx,ix] = sort([c1(1,in1) c2(1,in2)]); % Sort Points
filly = [c1(2,in1) (c2(2,in2))];
filly = filly(ix);
figure(1)
plot(c1(1,:), c1(2,:))
hold on
plot(c2(1,:), c2(2,:))
fill([fillx fliplr(fillx)], [filly fliplr(filly)], 'g', 'EdgeColor','none')
hold off
axis square
What I end up with is the following image:
However, it should appear as this image:
Why is the area not being filled as it is in the example article?
If you have Mapping Toolbox you can use polybool to find the intersection between to polygones, and than patch (which dosen't require Mapping Toolbox, and is better than fill) to draw it. The folowing code works even without the first 2 lines that use poly2cw, but it issues some warnings. This can be solved with the poly2cw trasfomation:
[c1(1,:), c1(2,:)] = poly2cw(c1(1,:), c1(2,:)); % clock-wise transform
[c2(1,:), c2(2,:)] = poly2cw(c2(1,:), c2(2,:)); % clock-wise transform
[xb, yb] = polybool('intersection',c1(1,:),c1(2,:),c2(1,:), c2(2,:));
plot(c1(1,:), c1(2,:))
hold on
plot(c2(1,:), c2(2,:))
patch(xb, yb, 1, 'FaceColor', 'g','EdgeColor','none')
axis equal
The code in the question does not work because it has some mistake in the order of the points in fillx and filly. We can see that if we set the 'EdgeColor' to be visible, and follow the peripheral line of the patch (see below, here reduced to 20 points, for the illustration). We can clearly see that the point of the polygon to be filled are ordered in a 'zig-zag' between the circles, so it has no area at all. I have numbered the vertices of the polygon taken from each circle to demonstrate in what order the fill function read them.
In order to fill with color all the intersection between the circles, we need to define the 'polygon' by the points on the circles that intersect (in1 and in2) in the right order. This means we want them to make a closed shape if an imaginary pencil is drawing a line between them in the same order they are given. Like this:
We start with 1 in one of the circles and continue until the numbers on that circle are ended, and then move to 1 on the other circle, and when we reach the end in the second circle we close the polygon by connecting the last point to the first one. As you can see in both figure above, the starting point and the end point of the circles are really close, so we get 2 numbers above each other.
How can we order the points correctly?
We start by getting in1 and in2 as described in the question. Let's take a look at in1:
in1 =
1 2 3 4 5 6 7 19 20
Those are the indices of the points from c1 to be taken, they appear to be in order, but contains a gap. This gap is because inpolygon checks the points by the order in c1, and the starting point of c1 is within the intersection region. So we get the first 7 points, then we are out of the intersection, and we go back in as we reach points 19 and 20. However, for our polygon, we need this points to start from the closest point to one of that places where the circle intersect and to go around the circle until we reach the second point of intersection.
To do that, we look for the 'gap' in the points order:
gap = find(diff(in1)>1);
and reorder them correctly:
X1 = [c1(1,in1(gap+1:end)) c1(1,in1(1:gap))];
Y1 = [c1(2,in1(gap+1:end)) c1(2,in1(1:gap))];
But, there may be no 'gap', as we see in in2:
in2 =
11 12 13 14
So we need to wrap this within an if to check if the points need to be reordered:
if ~isempty(gap)
X1 = [c1(1,in1(gap+1:end)) c1(1,in1(1:gap))];
Y1 = [c1(2,in1(gap+1:end)) c1(2,in1(1:gap))];
else
X1 = c1(1,in1);
Y1 = c1(2,in1);
end
Now all we need to do it to concatenate X1 and X2 (for circle 2), and the same for the Ys, and use patch (which is like fill, but better) to draw it:
patch([X1 X2],[Y1 Y2],'g','EdgeColor','none')
With 20 points the circle is not really a circle and the intersection is partially colored, so here is the full code and the result with 200 points:
t = linspace(0, 2*pi, 200);
cir = #(r,ctr) [r*cos(t)+ctr(1); r*sin(t)+ctr(2)]; % Circle Function
c1 = cir(1.0, [0; 0]);
c2 = cir(1.5, [1; 1]);
plot(c1(1,:), c1(2,:))
hold on
plot(c2(1,:), c2(2,:))
axis equal
in1 = find(inpolygon(c1(1,:), c1(2,:), c2(1,:), c2(2,:)));
in2 = find(inpolygon(c2(1,:), c2(2,:), c1(1,:), c1(2,:)));
gap = find(diff(in1)>1);
if ~isempty(gap)
X1 = [c1(1,in1(gap+1:end)) c1(1,in1(1:gap))];
Y1 = [c1(2,in1(gap+1:end)) c1(2,in1(1:gap))];
else
X1 = c1(1,in1);
Y1 = c1(2,in1);
end
gap = find(diff(in2)>1);
if ~isempty(gap)
X2 = [c2(1,in2(gap+1:end)) c2(1,in2(1:gap))];
Y2 = [c2(2,in2(gap+1:end)) c2(2,in2(1:gap))];
else
X2 = c2(1,in2);
Y2 = c2(2,in2);
end
patch([X1 X2],[Y1 Y2],'g','EdgeColor','none')
hold off
All mentioned above could be replace with the use of convhull on the vertices that intersect and give the same result:
x = [c1(1,in1) c2(1,in2)]; % all x's for intersecting vertices
y = [c1(2,in1) c2(2,in2)]; % all y's for intersecting vertices
k = convhull(x,y); % calculate the convex polygon
patch(x(k),y(k),'g','EdgeColor','none')

mean value in a sphere

I'm trying to calculate the mean value of the pixels inside a circle. In the future this needs to be extended to 3D, but for now a 2D sollution would already help me out.
As can be seen in the image, some pixels are entirely inside the circle, but some are only partly inside the circle. The ones partly in the circle also need to contribute only partly to the mean value. The pixels are square. This will simplify the mathematics I hope.
I can calculate the distance from the pixelcorners to the central point, from this you can find the pixels enterly inside and enterly outside. The rest needs correction. But how to find this correction.
[edit] thanks to Heath Raftery the problem is solved! [/edit]
the integral of a circle with radius r
As an example: I want to know the average pixelvalue of pixels in this circle. I know it is 0.3425, since 34.25% of the circle has a value of 1 and the rest is 0.
Function to check what part of a pixel is in the circle:
function [ a ] = incirc( x,y,r )
%only handles the top right quadrant of a circle
if x<0||y<0,error('only positive x,y');end
%integral of sqrt(r^2-x^2) dx
F = #(x,r) (1/2)*(x*sqrt(r^2-x^2)+r^2*atan(x/sqrt(r^2-x^2)));
%find corner locations
x=[x-0.5,x+0.5];
y=[y-0.5,y+0.5];
d = sqrt(x.^2+y.^2); %distance to closed and furthest corner
if max(d)<r,a=1;return;end %inside circle
if min(d)>r,a=0;return;end %outside circle
%intersections with edges (r^2 = x^2+y^2)
inters = [sqrt(r^2-y(1)^2),sqrt(r^2-y(2)^2),sqrt(r^2-x(1)^2),sqrt(r^2-x(2)^2)]; %x(1) x(2) y(1) y(2)
%remove imaginary and out of range intersections
inters(imag(inters)~=0)=NaN;
inters(inters<1E-5)=NaN; %to find values that are zero
inters([~((x(1)<inters(1:2))&(inters(1:2)<x(2))),~((y(1)<inters(3:4))&(inters(3:4)<y(2)))])=NaN;
idx = find(~isnan(inters));
if numel(idx)~=2,error('need two intersections of circle with pixel');end
%check area of pixel inside circumference
if all(idx==[1,2]) %2 intersections on y-edge
a=(F(y(2),r)-F(y(1),r)) - x(1); %area
elseif all(idx==[3,4]) %2 intersections on x-edge
a=(F(x(2),r)-F(x(1),r)) - y(1); %area
elseif all(idx==[1,3]) %one intersection on y-edge one on x-edge (left&bottom)
a=(F(inters(1),r)-F(x(1),r))- (y(1)*(inters(1)-x(1)));
elseif all(idx==[2,4]) %one intersection on y-edge one on x-edge (top&right)
a=(inters(2)-x(1))+(F(x(2),r)-F(inters(2),r))-(y(1)*(x(2)-inters(2)));
else
error('geometry')
end
a=real(a);
if a<0||a>1
error('computational error');
end
end
Script to test the function
M = ones(100); %data
M(1:50,:)=0;
pos=[50.2,50];
r = 2;
%calculate what the result should be
h=50-pos(2)+0.5;
A=pi*r^2;
wedge = acos(h/r)/pi;
triangle = h*sqrt(r^2-h^2);
res=(A*wedge-triangle)/A
S=0;N=0;
for i = 1:size(M,1)
for j = 1:size(M,2)
x=abs(j-pos(1));
y=abs(i-pos(2));
n=incirc( x,y,r );
M_(i,j)=n;
S = S+M(i,j)*n;
N = N+n;
end
end
result = S/N
result = 0.3425
You can see the algorithm finds the part of the pixel in the circle.
The question is missing a question, but I'll assume that it's not how to calculate whether pixels are fully inside or outside the circle. That's a relatively simple task. That is, a pixel is fully inside if the furtherest corner of the pixel to the centre is less than a radius away from the centre, and a pixel is fully outside if the closest corner of the pixel to the centre is more than a radius away from the centre.
The question of what proportion of pixels on the circumference fall within the circumference is much trickier. There are two fundamental solutions:
Exact and hard.
Approximate and a bit easier.
In both cases, note the horizontal and vertical symmetry means only the top right quadrant need be considered.
Then, for (1), translate the circle centre to the origin (0, 0) and treat the circumference as the function y(x) = sqrt(r^2 - x^2). Then, the area of an overlapping pixel within the circle is the integral:
integral(y(x) - y0, from x0 to x1, with respect to x)
where y0 is the bottom coordinate of the pixel, x0 is the left coordinate and x1 is the right coordinate.
This integral can be solved exactly with a trigonometric identity and a trigonometric substitution.
For (2), just generate a set of random points within the pixel and count how many of them fall within the circumference. As the set gets larger, the proportion of points that fall within the circumference to the count of all point approaches the proportion of the pixel within the circumference.
You can use inpolygon, to get the indices which lie inside the circle, once you have those indices you can get your pixels and do what you want.
M = rand(100); %data
[nx,ny] = size(M) ;
[X,Y] = meshgrid(1:ny,1:nx) ;
pos=[20,20];
r = 5;
phi=linspace(0,2*pi,100);
imagesc(M);
axis image
hold on
plot(pos(1),pos(2),'rx')
xc = pos(1)+r*sin(phi) ;
yc = pos(2)+r*cos(phi) ;
plot(xc,yc,'-r');
% hold off
%% get indices which are inside the circle
idx = inpolygon(X(:),Y(:),xc,yc) ;
xi = X(idx) ; yi = Y(idx) ;
plot(xi,yi,'.r')
mypixels = M(idx) ;
You can also use rangesearch to get the points lying within the given radius of the circle. As below:
M = rand(100); %data
[nx,ny] = size(M) ;
[X,Y] = meshgrid(1:ny,1:nx) ;
pos=[20,20];
r = 5;
phi=linspace(0,2*pi,100);
imagesc(M);
axis image
hold on
plot(pos(1),pos(2),'rx')
xc = pos(1)+r*sin(phi) ;
yc = pos(2)+r*cos(phi) ;
plot(xc,yc,'-r');
% hold off
%% Use nearest neighbour search
idx = rangesearch([X(:),Y(:)],pos,r) ;
xi = X(idx{1}) ; yi = Y(idx{1}) ;
plot(xi,yi,'.r')
mypixels = M(idx{1}) ;

How to to identify letters on a license plate with varying perspectives

I am making a script in Matlab that takes in an image of the rear of a car. After some image processing I would like to output the original image of the car with a rectangle around the license plate of the car. Here is what I have written so far:
origImg = imread('CAR_IMAGE.jpg');
I = imresize(origImg, [500, NaN]); % easier viewing and edge connecting
G = rgb2gray(I);
M = imgaussfilt(G); % blur to remove some noise
E = edge(M, 'Canny', 0.4);
% I can assume all letters are somewhat upright
RP = regionprops(E, 'PixelIdxList', 'BoundingBox');
W = vertcat(RP.BoundingBox); W = W(:,3); % get the widths of the BBs
H = vertcat(RP.BoundingBox); H = H(:,4); % get the heights of the BBs
FATTIES = W > H; % find the BBs that are more wide than tall
RP = RP(FATTIES);
E(vertcat(RP.PixelIdxList)) = false; % remove more wide than tall regions
D = imdilate(E, strel('disk', 1)); % dilate for easier viewing
figure();
imshowpair(I, D, 'montage'); % display original image and processed image
Here are some examples:
From here I am unsure how to isolate the letters of the license plate, particularly like in the second example above where each letter has a decreased area due to the perspective of the image. My first idea was to get the bounding box of all regions and keep only the regions where the perimeter to area ratio is "similar" but this resulted in removing the letters of the plate that were connected when I dilate the image like the K and V in the fourth example above.
I would appreciate some suggestions on how I should go about isolating these letters. No code is necessary, and any advice is appreciated.
So I continued to work despite not receiving any answers here on SO and managed to get a working version through trial and error. All of the following code comes after the code in my original question and all plots below are from the first example image above. First, I found the variance for every single pixel row of the image and plotted them like so:
V = var(D, 0, 2);
X = 1:length(V);
figure();
hold on;
scatter(X, V);
I then fit a very high order polynomial to this scatter plot and saved the values where the slope of the polynomial was zero and the variance value was very low (i.e. the dark row of pixels immediately before or after a row with some white):
P = polyfit(X', V, 25);
PV = polyval(P, X);
Z = X(find(PV < 0.03 & abs(gradient(PV)) < 0.0001));
plot(X, PV); % red curve on plot
scatter(Z, zeros(1,length(Z))); % orange circles on x-axis
I then calculate the integral of the polynomial between any consecutive Z values (my dark rows), and save the two Z values between which the integral is the largest, which I mark with lines on the plot:
MAX_INTEG = -1;
MIN_ROW = -1;
MAX_ROW = -1;
for i = 1:(length(Z)-1)
TEMP_MIN = Z(i);
TEMP_MAX = Z(i+1);
Q = polyint(P);
TEMP_INTEG = diff(polyval(Q, [TEMP_MIN, TEMP_MAX]));
if (TEMP_INTEG > MAX_INTEG)
MAX_INTEG = TEMP_INTEG;
MIN_ROW = TEMP_MIN;
MAX_ROW = TEMP_MAX;
end
end
line([MIN_ROW, MIN_ROW], [-0.1, max(V)+0.1]);
line([MAX_ROW, MAX_ROW], [-0.1, max(V)+0.1]);
hold off;
Since the X-values of these lines correspond row numbers in the original image, I can crop my image between MIN_ROW and MAX_ROW:
I repeat the above steps now for the columns of pixels, crop, and remove any excess black rows of columns to result in the identified plate:
I then perform 2D cross correlation between this cropped image and the edged image D using Matlab's xcorr2 to locate the plate in the original image. After finding the location I just draw a rectangle around the discovered plate like so:

How to detect smooth curves in matlab

I am trying to detect a bent conveyor in an image. I used the following code using Hough transform to detect its edges
%# load image, and process it
I = imread('ggp\2.jpg');
g = rgb2gray(I);
bw = edge(g,'Canny');
[H,T,R] = hough(bw);
P = houghpeaks(H,500,'threshold',ceil(0.4*max(H(:))));
% I apply houghlines on the grayscale picture, otherwise it doesn't detect
% the straight lines shown in the picture
lines = houghlines(g,T,R,P,'FillGap',5,'MinLength',50);
figure, imshow(g), hold on
for k = 1:length(lines)
xy = [lines(k).point1; lines(k).point2];
deltaY = xy(2,2) - xy(1,2);
deltaX = xy(2,1) - xy(1,1);
angle = atan2(deltaY, deltaX) * 180 / pi;
if (angle == 0)
plot(xy(:,1),xy(:,2),'LineWidth',2,'Color','green');
% Plot beginnings and ends of lines
plot(xy(1,1),xy(1,2),'x','LineWidth',2,'Color','yellow');
plot(xy(2,1),xy(2,2),'x','LineWidth',2,'Color','red');
end
end
As it is shown, two straight lines successfully detect top and bottom edges of the conveyor but I don't know how to detect if it is bent or not (in the picture it is bent) and how to calculate the degree of that.
The curve approximately is drawn manually in the picture below (red color):
I found no code or function for Hough transform in matlab to detect such smooth curves (e.g., 2nd degree polynomials: y= a*x^2). Any other solution is also welcome.
It's the original image:
Looking at your straight lines detecting the conveyor belt, I assume you can focus your processing around the region of interest (rows 750 to 950 in the image).
Proceeding from that point:
oimg = imread('http://i.stack.imgur.com/xfXUS.jpg'); %// read the image
gimg = im2double( rgb2gray( oimg( 751:950, :, : ) ) ); %// convert to gray, only the relevant part
fimg = imfilter(gimg, [ones(7,50);zeros(1,50);-ones(7,50)] ); %// find horizontal edge
Select only strong horizontal edge pixels around the center of the region
[row, col] = find(abs(fimg)>50);
sel = row>50 & row < 150 & col > 750 & col < 3250;
row=row(sel);
col=col(sel);
Fit a 2nd degree polynom and a line to these edge points
[P, S, mu] = polyfit(col,row,2);
[L, lS, lmu] = polyfit(col, row, 1);
Plot the estimated curves
xx=1:4000;
figure;imshow(oimg,'border','tight');
hold on;
plot(xx,polyval(P,xx,[],mu)+750,'LineWidth',1.5,'Color','r');
plot(xx,polyval(L,xx,[],lmu)+750,':g', 'LineWidth', 1.5);
The result is
You can visually appreciate how the 2nd degree fit P fits better the boundary of the conveyor belt. Looking at the first coefficient
>> P(1)
ans =
1.4574
You see that the coefficient of x^2 of the curve is not negligible making the curve distinctly not a straight line.