Ear Image Processing - Finding the point of intersection of line and curve in MATLAB - matlab

!1I have the Canny edge output of a ear... i have connected the farthest two boundaries with a line(green). Now I want to draw a normal from the midpoint of this line to the outer boundary(left side).
The code i have written helps me to plot a normal but i want the red line to exactly meet the white boundary. Also I want the point of intersection at the point where it meets. I have also thought about another method for the same.By changing 50 to 60 pixels (in the code) the red line crosses the white boundary. If I get the point of intersection of the same then I can easily plot the line of the desired length. I found some code on the internet and Mathworks, but it is for intersection of 2 lines....Can anybody plz help.
for i=1:numel(p)
x = [ p{i}(1), p{i}(3)];
y = [p{i}(2), p{i}(4)];
line(x,y,'color','g','LineWidth',2);
m = (diff(y)/diff(x));
minv = -1/m;
line([mean(x) mean(x)-50],[mean(y) mean(y)-50*minv],'Color','red')
axis equal
end ;
![][2]

Picked up the input image from here.
This is the code to get the intersection point and plot it-
%% Read image and convert to BW
img1 = imread('ear.png');
BW = im2bw(img1);
L = bwlabel(BW,8);
[bw_rows,bw_cols] =find(L==1);
bw_rowcol = [bw_rows bw_cols];
bw_rowcol(:,1) = size(BW,1) - bw_rowcol(:,1); % To offset for the MATLAB's terminology of showing height on graphs
%% Get the farthest two points on the outer curve and midpoint of those points
distmat = dist2s(bw_rowcol,bw_rowcol);
[maxdist_val,maxdist_ind] = max(distmat(:),[],1);
[R,C] = ind2sub(size(distmat),maxdist_ind);
farther_pt1 = bw_rowcol(R,:);
farther_pt2 = bw_rowcol(C,:);
midpoint = round(mean([farther_pt1 ; farther_pt2]));
%% Draw points on the normal from the midpoint across the image
slope_farthest_pts = (farther_pt1(1) - farther_pt2(1)) / (farther_pt1(2) - farther_pt2(2));
slope_normal = -1/slope_farthest_pts;
y1 = midpoint(1);
x1 = midpoint(2);
c1 = y1 -slope_normal*x1;
x_arr = [1:size(BW,2)]';
y_arr = slope_normal*x_arr + c1;
yx_arr = round([y_arr x_arr]);
%% Finally get the intersection point
distmat2 = dist2s(bw_rowcol,yx_arr);
[mindist_val2,mindist_ind2] = min(distmat2(:),[],1);
[R2,C2] = ind2sub(size(distmat2),mindist_ind2);
intersection_pt = bw_rowcol(R2,:); % Verify that this is equal to -> yx_arr(C2,:)
%% Plot
figure,imshow(img1)
hold on
x=[farther_pt1(2),farther_pt2(2)];
y=size(BW,1)-[farther_pt1(1),farther_pt2(1)];
plot(x,y)
hold on
x=[intersection_pt(2),midpoint(2)];
y=size(BW,1)-[intersection_pt(1),midpoint(1)];
plot(x,y,'r')
text(x(1),y(1),strcat('Int Pt = ','[',num2str(x(1)),',',num2str(y(1)),']'),'Color','green','FontSize',24,'EdgeColor','red','LineWidth',3)
Don't forget to use this associated function-
function out = dist2s(pt1,pt2)
out = NaN(size(pt1,1),size(pt2,1));
for m = 1:size(pt1,1)
for n = 1:size(pt2,1)
if(m~=n)
out(m,n) = sqrt( (pt1(m,1)-pt2(n,1)).^2 + (pt1(m,2)-pt2(n,2)).^2 );
end
end
end
return;
The output -
Hope this helps and let us know it goes. Interesting project!
EDIT 1: Based on the edited photo shown below, I got few questions for you.
Questions: Do you want a line from PT1 to MP and let it extend and touch the outer ear at PT3? If so, how do you define PT1? How do you differentiate between PT1 and PT2? You could have tried to plot a line from PT2 to MP and let it touch the outer ear at some other point, so why not PT2 instead of PT1?

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

Searching Across a Line in a Matrix in Octave

The attached image has a line with a break in it.
My code finds the line using a hough transform resulting in r=32 and theta=2.3213. The hough transform isn't perfect, the angle (especially with a more complex image) is always off by a little bit, and in this case, because of the edge detection, the line is offset. I want to read values across the line to find the breaks in it. In order to do this, I will need to be able to sample values on either side of the line to find where the maximum density of the line is.
Further explanation (if you want it):
If you look closely at the image you can see areas where the line crosses a pixel pretty much dead on resulting in a value of nearly 1/white. Other areas have two pixels side by side with values of about .5/gray. I need to find a solution that takes into account the anti-aliasing of the line, and allows me to extract the breaks in it.
%Program Preparation
clear ; close all; clc %clearing command window
pkg load image %loading image analyzation suite
pkg load optim
%Import Image
I_original = imread("C:/Users/3015799/Desktop/I.jpg");
%Process Image to make analysis quicker and more effective
I = mat2gray(I_original); %convert to black and white
I = edge(I, 'sobel');
%Perform Hough Transform
angles = pi*[-10:189]/180;
hough = houghtf(I,"line",angles);
%Detect hot spots in hough transform
detect = hough>.5*max(hough(:));
%Shrink hotspots to geometric center, and index
detect = bwmorph(detect,'shrink',inf);
[ii, jj] = find(detect);
r = ii - (size(hough,1)-1)/2;
theta = angles(jj);
%Cull duplicates. i.e outside of 0-180 degrees
dup = theta<-1e-6 | theta>=pi-1e-6;
r(dup) = [];
theta(dup) = [];
%Compute line parameters (using Octave's implicit singleton expansion)
r = r(:)'
theta = theta(:)'
x = repmat([1;1133],1,length(r)); % 2xN matrix, N==length(r)
y = (r - x.*cos(theta))./sin(theta); % solve line equation for y
%The above goes wrong when theta==0, fix that:
horizontal = theta < 1e-6;
x(:,horizontal) = r(horizontal);
y(:,horizontal) = [1;:];
%Plot
figure
imshow(I)
hold on
plot(y,x,'r-','linewidth',2)
If you are only interested in the length of the gap, this would be very easy:
clear all
pkg load image
img_fn = "input.jpg";
if (! exist (img_fn, "file"))
urlwrite ("https://i.stack.imgur.com/5UnpO.jpg", img_fn);
endif
Io = imread(img_fn);
I = im2bw (Io);
r = max(I);
c = max(I');
ri = find (diff(r));
ci = find (diff(c));
## both should have 4 elements (one break)
assert (numel (ri) == 4);
assert (numel (ci) == 4);
## the gap is in the middle
dx = diff(ri(2:3))
dy = diff(ci(2:3))
# the length is now easy
l = hypot (dy, dx)
gives
dx = 5
dy = 5
l = 7.0711
without any hogh transform. Of course you have to also check the corener cases for horizontal and vertical lines but this should give you an idea

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')

Matlab - Find Coordinates between a straight line and a perimeter

I segmented a mouse and get its image-properties using bwlabel. Thereby I have access to the position of the centroid and the orientation of the mouse. I also get the perimeter of the mouse using bwperim.
I want to find the two points of the straight line passing through the centroid and having the same direction than the orientation of the mouse cutting the perimeter.
I find the equation of the straight line using that code :
% E is a 2*2 matrix containing the coordinates of the centroid and the
% coordinates of the point which belong to the straight line and making
% the right angle given by the orientation
coeffs = polyfit(E(:,1),E(:,2),1);
% Create the equation of the straight line
x = 1:width;
yfit = coeffs(1)*x+coeffs(2);
% Make sure there are only int values.
yfit = uint16(yfit);
I convert my values to uint16 because i want to fill a new matrix that I will compare with the matrix containing the perimeter. Here is what I do then :
% Create a matrix of zeros and set to 1 all the pixels which belong to the
% straight line
k = 1;
temp = false;
m = false(size(iPerim));
while temp~=true
temp = false;
if yfit(k) > 0
m(yfit(k),k)=1;
temp = true;
end
k = k+1;
end
[t,p] = ind2sub(size(m), find(m==1));
minM = [min(p),min(t)];
% complete the straight line to don't have little holes
x = linspace(minM(1),D(1),width);
y = coeffs(1)*x+coeffs(2);
idx = sub2ind(size(m),round(y),round(x));
m(idx) = 1;
Then I compare m with iPerim which is the matrix containing my perimeter:
% Compare the matrix of the perimeter and the matrix of the straight line
% and find the two points in common. It is the points where the straight
% line cut the perimeter
p = m & iPerim;
% Extract thoses coordinates
[coordsY,coordsX] = ind2sub(size(p), find(p==1));
Well I am a new user of Matlab so I think this is not a elegant solution but there is the result:
Matrix m
Perimeter in which I plot yfit
As you can see the algorithm detects only one point and not the second one (the yellow spot)... I figure why but I can't find the solution. It is because the line straight is cutting the perimeter through a diagonal but there are not coordinates in common...
Somebody has a solution to my problem ? And of course I am taking any advises conerning my code :)
Thank you very much !
Edit: If there is a easier solution I take it obviously
When the coordinate of the point where the mouse-perimeter and the line cross are E(2,:), then the position of this point in the line is where the distance is minimal. E.g. like:
[xLine, yLine] = find(m); % x,y positions of the line
dX = abs(xline-E(2,1)) % x-distance to x-coordinate of direction-point
dY = abs(yLine-E(2,2)) % y-distance to y-coordinate of direction-point
distP = sqrt(dX.^2+dY.^2) % distance of line-points to directon-point
[~,indMin] = min(distP); % index of line-point which has the minimum distance
xPoint = xLine(indMin(1));
yPoint = yLine(indMin(1));
The abs and sqrtfunctions are not necessary here for finding the right point, only for the correct intermediate values...
From the Matlab Documentation about ind2sub:
For matrices, [I,J] = ind2sub(size(A),find(A>5)) returns the same values as [I,J] = find(A>5).

Find intersection in noisy Hough transform

I have various sets of points, which are supposed to be part of different lines (see image below). What I want to do is to group the points which are part of the same line. To do so, I made a Hough transform that turns cartesian coordinates into polar one. What I need to do is to locate the position of the intersection in the Hough plane; do you have any suggestion on how to do it?
This is what I am doing now:
Apply a threshold to the image of the Hough transform, so to
consider only the pixels with at least half of the maximum intensity
max_Hough = max(max(Hough));
for ii = 1:dim_X
for jj = 1:dim_Y
if Hough(ii,jj) > max_Hough*0.5
Hough_bin(ii,jj) = Hough(ii,jj);
end
end
end
Locate the blobs in the corresponding image
se = strel('disk',1);
Hough_final = imclose(Hough_bin,se);
[L,num] = bwlabel(Hough_final);
% Let's exclude the single points
counts = sum(bsxfun(#eq,L(:),1:num));
valid_blobs = find(counts>threshold);
number_valid = length(find(counts>threshold));
if (number_valid~=0)
for blob_num = (1:number_valid)
for x = (1:dim_X)
for y = (1:dim_X)
if (L(x,y) == valid_blobs(blob_num))
hist_bin_clean(x,y) = Hough_final(x,y);
end
end
end
end
end
Find the centroids of the blobs
Ilabel = bwlabel(hist_bin_clean);
stat = regionprops(Ilabel,'centroid');
You can see the result in the second image below, with the centroid pointed by the white arrow. I would expect it to be a few pixels higher and to the left... How would you improve the result?