convert image from Cartesian to Polar - matlab

I want to convert an image from Cartesian to Polar and to use it for opengl texture.
So I used matlab referring to the two articles below.
Link 1
Link 2
My code is exactly same with Link 2's anwser
% load image
img = imread('my_image.png');
% convert pixel coordinates from cartesian to polar
[h,w,~] = size(img);
[X,Y] = meshgrid((1:w)-floor(w/2), (1:h)-floor(h/2));
[theta,rho] = cart2pol(X, Y);
Z = zeros(size(theta));
% show pixel locations (subsample to get less dense points)
XX = X(1:8:end,1:4:end);
YY = Y(1:8:end,1:4:end);
tt = theta(1:8:end,1:4:end);
rr = rho(1:8:end,1:4:end);
subplot(121), scatter(XX(:),YY(:),3,'filled'), axis ij image
subplot(122), scatter(tt(:),rr(:),3,'filled'), axis ij square tight
% show images
figure
subplot(121), imshow(img), axis on
subplot(122), warp(theta, rho, Z, img), view(2), axis square
The result was exactly what I wanted, and I was very satisfied except for one thing. It's the area (red circled area) in the picture just below. Considering that the opposite side (blue circled area) is not, I think this part should also be filled. Because of this part is empty, so there is a problem when using it as a texture.
And I wonder how I can fill this part. Thank you.
(little difference from Link 2's answer code like degree<->radian and axis values. but i think it is not important.)

Those issues you show in your question happen because your algorithm is wrong.
What you did (push):
throw a grid on the source image
transform those points
try to plot these colored points and let MATLAB do some magic to make it look like a dense picture
Do it the other way around (pull):
throw a grid on the output
transform that backwards
sample the input at those points
The distinction is called "push" (into output) vs "pull" (from input). Only Pull gives proper results.
Very little MATLAB code is necessary. You just need pol2cart and interp2, and a meshgrid.
With interp2 you get to choose the interpolation (linear, cubic, ...). Nearest-neighbor interpolation leaves visible artefacts.
im = im2single(imread("PQFax.jpg"));
% center of polar map, manually picked
cx = 10 + 409/2;
cy = 7 + 413/2;
% output parameters
radius = 212;
dRho = 1;
dTheta = 2*pi / (2*pi * radius);
Thetas = pi/2 - (0:dTheta:2*pi);
Rhos = (0:dRho:radius);
% polar mesh
[Theta, Rho] = meshgrid(Thetas, Rhos);
% transform...
[Xq,Yq] = pol2cart(Theta, Rho);
% translate to sit on the circle's center
Xq = Xq + cx;
Yq = Yq + cy;
% sample image at those points
Ro = interp2(im(:,:,1), Xq,Yq, "cubic");
Go = interp2(im(:,:,2), Xq,Yq, "cubic");
Bo = interp2(im(:,:,3), Xq,Yq, "cubic");
Vo = cat(3, Ro, Go, Bo);
Vo = imrotate(Vo, 180);
imshow(Vo)
The other way around (get a "torus" from a "ribbon") is quite similar. Throw a meshgrid on the torus space, subtract center, transform from cartesian to polar, and use those to sample from the "ribbon" image into the "torus" image.
I'm more familiar with OpenCV than with MATLAB. Perhaps MATLAB has something like OpenCV's warpPolar(), or a generic remap(). In any case, the operation is trivial to do entirely "by hand" but there are enough supporting functions to take the heavy lifting off your hands (interp2, pol2cart, meshgrid).

1.- The white arcs tell that the used translation pol-cart introduces significant errors.
2.- Reversing the following script solves your question.
It's a script that goes from cart-pol without introducing errors or ignoring input data, which is what happens when such wide white arcs show up upon translation apparently correct.
clear all;clc;close all
clc,cla;
format long;
A=imread('shaffen dass.jpg');
[sz1 sz2 sz3]=size(A);
szx=sz2;szy=sz1;
A1=A(:,:,1);A2=A(:,:,2);A3=A(:,:,3); % working with binary maps or grey scale images this wouldn't be necessary
figure(1);imshow(A);
hold all;
Cx=floor(szx/2);Cy=floor(szy/2);
plot(Cx,Cy,'co'); % because observe image centre not centered
Rmin=80;Rmax=400; % radius search range for imfindcircles
[centers, radii]=imfindcircles(A,[Rmin Rmax],... % outer circle
'ObjectPolarity','dark','Sensitivity',0.9);
h=viscircles(centers,radii);
hold all; % inner circle
[centers2, radii2]=imfindcircles(A,[Rmin Rmax],...
'ObjectPolarity','bright');
h=viscircles(centers2,radii2);
% L=floor(.5*(radii+radii2)); % this is NOT the length X that should have the resulting XY morphed graph
L=floor(2*pi*radii); % expected length of the morphed graph
cx=floor(.5*(centers(1)+centers2(1))); % coordinates of averaged circle centres
cy=floor(.5*(centers(2)+centers2(2)));
plot(cx,cy,'r*'); % check avg centre circle is not aligned to figure centre
plot([cx 1],[cy 1],'r-.');
t=[45:360/L:404+1-360/L]; % if step=1 then we only get 360 points but need an amount of L points
% if angle step 1/L over minute waiting for for loop to finish
R=radii+5;x=R*sind(t)+cx;y=R*cosd(t)+cy; % build outer perimeter
hL1=plot(x,y,'m'); % axis equal;grid on;
% hold all;
% plot(hL1.XData,hL1.YData,'ro');
x_ref=hL1.XData;y_ref=hL1.YData;
% Sx=zeros(ceil(R),1);Sy=zeros(ceil(R),1);
Sx={};Sy={};
for k=1:1:numel(hL1.XData)
Lx=floor(linspace(x_ref(k),cx,ceil(R)));
Ly=floor(linspace(y_ref(k),cy,ceil(R)));
% plot(Lx,Ly,'go'); % check
% plot([cx x(k)],[cy y(k)],'r');
% L1=unique([Lx;Ly]','rows');
Sx=[Sx Lx'];Sy=[Sy Ly'];
end
sx=cell2mat(Sx);sy=cell2mat(Sy);
[s1 s2]=size(sx);
B1=uint8(zeros(s1,s2));
B2=uint8(zeros(s1,s2));
B3=uint8(zeros(s1,s2));
for n=1:1:s2
for k=1:1:s1
B1(k,n)=A1(sx(k,n),sy(k,n));
B2(k,n)=A2(sx(k,n),sy(k,n));
B3(k,n)=A3(sx(k,n),sy(k,n));
end
end
C=uint8(zeros(s1,s2,3));
C(:,:,1)=B1;
C(:,:,2)=B2;
C(:,:,3)=B3;
figure(2);imshow(C);
the resulting
3.- let me know if you'd like some assistance writing pol-cart from this script.
Regards
John BG

Related

How to improve the linear best fit for this image?

I am trying to get a Linear best fit line through certain points in an image but am unable to get a line that would seem logical. I am not sure if I am doing something wrong here, or if that really is the best fit line in this case.
This is the original image:
This is the result after selecting an area of interest, filtering the edge, and then plotting the best fit line through the pixels of that edge.
I expected the best fit line (blue line in the results) to be almost vertical. However, as shown in the image, it is tilted at an angle while the red points are all close to a vertical and not scattered around the image. How can I correctly detect the vertical line?
Code:
image = imread('https://i.stack.imgur.com/rQ1SQ.png');
image = rgb2gray(image(85:270, 165:210, :));
filter = [ones(1, 6);zeros(1,6);-ones(1,6)];
filter = filter.';
filtered =imfilter(image,filter);
regions = regionprops(bwlabel(filtered,8), 'Area', 'PixelList');
[maxarea, maxindex] = max([regions.Area]);
p = polyfit(regions(maxindex).PixelList(:,1), regions(maxindex).PixelList(:,2), 1);
x = 1:size(image,2);
y = polyval(p,x);
subplot(1,3,1), imshow(image, 'border','tight');
hold on;
plot(x,y,'b');
title('Area of Interest in Original Image')
subplot(1,3,2), imshow(filtered);
hold on;
plot(x,y,'b');
title('Vertical Edge after Filtering')
subplot(1,3,3), imshow(fim);
hold on;
plot(x,y,'b');
plot(regions(maxindex).PixelList(:,1), regions(maxindex).PixelList(:,2), 'r.');
title('Image with points plotted')
Code Reference:
Shai's answer on a related question.
First a few comments. First you clearly have a vertical line here, but polyfit isn't fitting a vertical line. Why? because you are using a least squares fit on points with a high aspect ratio (4 px wide, 168 px high). One way to work around it is to rotate your data, fit a line, then rotate it back.
% rotate imroi points by 45 degrees
X = regions(maxindex).PixelList(:,1);
Y = regions(maxindex).PixelList(:,2);
N = size(X,1);
a = 45; %rotation angle
A = [cosd(a), sind(a); -sind(a), cosd(a)];
Z = (A * [X,Y]')'; %rotate points
figure;
subplot(1,2,1)
% plot rotated points
plot(Z(:,1), Z(:,2), 'r.')
hold on;
XRot = ones(N, 2); XRot(:,1) = Z(:,1);
b = XRot \ Z(:,2);
z = [linspace(min(Z(:,1)),max(Z(:,1)),N)', ones(N,1)];
plot(z(:,1), z*b, 'b-');
axis ij
title('Rotated imroi points')
subplot(1,2,2)
% plot un-rotated points
a = -45; %rotation angle
A = [cosd(a), sind(a); -sind(a), cosd(a)];
imshow(filtered);
hold on;
plot(X, Y, 'r.')
W = (A * [z(:,1) ,z * b]')';
plot(W(:,1), W(:,2), 'b-')
title('Image with points plotted')
I'd say in general your best bet for this kind of work is a computer vision package like vl_feat, which has a nice MATLAB (MEX) wrapper.
As pointed out by Peter in his answer, the issue in this problem is that the aspect ratio of the data points is very high: they are almost a vertical line already. The default Least Squares Method for a best fit line does not work well in this case. This can be resolved by using Orthogonal Least Squares method and to better understand that concept, have a look here which I found from Andrey's answer to a similar problem.
Luckily, there is a package developed by F.Carr which makes it extremely easy to implement the Orthogonal Linear Regression method.
The results by using this method are shown here.
This seems to have solved the problem for now although I haven't tried it on a large number of cases yet.

How can I reduce the number of mesh lines shown in a surface plot?

I've found this answer, but I can't complete my work. I wanted to plot more precisely the functions I am studying, without overcoloring my function with black ink... meaning reducing the number of mesh lines. I precise that the functions are complex.
I tried to add to my already existing code the work written at the link above.
This is what I've done:
r = (0:0.35:15)'; % create a matrix of complex inputs
theta = pi*(-2:0.04:2);
z = r*exp(1i*theta);
w = z.^2;
figure('Name','Graphique complexe','units','normalized','outerposition',[0.08 0.1 0.8 0.55]);
s = surf(real(z),imag(z),imag(w),real(w)); % visualize the complex function using surf
s.EdgeColor = 'none';
x=s.XData;
y=s.YData;
z=s.ZData;
x=x(1,:);
y=y(:,1);
% Divide the lengths by the number of lines needed
xnumlines = 10; % 10 lines
ynumlines = 10; % 10 partitions
xspacing = round(length(x)/xnumlines);
yspacing = round(length(y)/ynumlines);
hold on
for i = 1:yspacing:length(y)
Y1 = y(i)*ones(size(x)); % a constant vector
Z1 = z(i,:);
plot3(x,Y1,Z1,'-k');
end
% Plotting lines in the Y-Z plane
for i = 1:xspacing:length(x)
X2 = x(i)*ones(size(y)); % a constant vector
Z2 = z(:,i);
plot3(X2,y,Z2,'-k');
end
hold off
But the problem is that the mesh is still invisible. How to fix this? Where is the problem?
And maybe, instead of drawing a grid, perhaps it is possible to draw circles and radiuses like originally on the graph?
I found an old script of mine where I did more or less what you're looking for. I adapted it to the radial plot you have here.
There are two tricks in this script:
The surface plot contains all the data, but because there is no mesh drawn, it is hard to see the details in this surface (your data is quite smooth, this is particularly true for a more bumpy surface, so I added some noise to the data to show this off). To improve the visibility, we use interpolation for the color, and add a light source.
The mesh drawn is a subsampled version of the original data. Because the original data is radial, the XData and YData properties are not a rectangular grid, and therefore one cannot just take the first row and column of these arrays. Instead, we use the full matrices, but subsample rows for drawing the circles and subsample columns for drawing the radii.
% create a matrix of complex inputs
% (similar to OP, but with more data points)
r = linspace(0,15,101).';
theta = linspace(-pi,pi,101);
z = r * exp(1i*theta);
w = z.^2;
figure, hold on
% visualize the complex function using surf
% (similar to OP, but with a little bit of noise added to Z)
s = surf(real(z),imag(z),imag(w)+5*rand(size(w)),real(w));
s.EdgeColor = 'none';
s.FaceColor = 'interp';
% get data back from figure
x = s.XData;
y = s.YData;
z = s.ZData;
% draw circles -- loop written to make sure the outer circle is drawn
for ii=size(x,1):-10:1
plot3(x(ii,:),y(ii,:),z(ii,:),'k-');
end
% draw radii
for ii=1:5:size(x,2)
plot3(x(:,ii),y(:,ii),z(:,ii),'k-');
end
% set axis properties for better 3D viewing of data
set(gca,'box','on','projection','perspective')
set(gca,'DataAspectRatio',[1,1,40])
view(-10,26)
% add lighting
h = camlight('left');
lighting gouraud
material dull
How about this approach?
[X,Y,Z] = peaks(500) ;
surf(X,Y,Z) ;
shading interp ;
colorbar
hold on
miss = 10 ; % enter the number of lines you want to miss
plot3(X(1:miss:end,1:miss:end),Y(1:miss:end,1:miss:end),Z(1:miss:end,1:miss:end),'k') ;
plot3(X(1:miss:end,1:miss:end)',Y(1:miss:end,1:miss:end)',Z(1:miss:end,1:miss:end)','k') ;

Count circle objects in an image using matlab

How to count circle objects in a bright image using MATLAB?
The input image is:
imfindcircles function can't find any circle in this image.
Based on well known image processing techniques, you can write your own processing tool:
img = imread('Mlj6r.jpg'); % read the image
imgGray = rgb2gray(img); % convert to grayscale
sigma = 1;
imgGray = imgaussfilt(imgGray, sigma); % filter the image (we will take derivatives, which are sensitive to noise)
imshow(imgGray) % show the image
[gx, gy] = gradient(double(imgGray)); % take the first derivative
[gxx, gxy] = gradient(gx); % take the second derivatives
[gxy, gyy] = gradient(gy); % take the second derivatives
k = 0.04; %0.04-0.15 (see wikipedia)
blob = (gxx.*gyy - gxy.*gxy - k*(gxx + gyy).^2); % Harris corner detector (high second derivatives in two perpendicular directions)
blob = blob .* (gxx < 0 & gyy < 0); % select the top of the corner (i.e. positive second derivative)
figure
imshow(blob) % show the blobs
blobThresshold = 1;
circles = imregionalmax(blob) & blob > blobThresshold; % find local maxima and apply a thresshold
figure
imshow(imgGray) % show the original image
hold on
[X, Y] = find(circles); % find the position of the circles
plot(Y, X, 'w.'); % plot the circle positions on top of the original figure
nCircles = length(X)
This code counts 2710 circles, which is probably a slight (but not so bad) overestimation.
The following figure shows the original image with the circle positions indicated as white dots. Some wrong detections are made at the border of the object. You can try to make some adjustments to the constants sigma, k and blobThresshold to obtain better results. In particular, higher k may be beneficial. See wikipedia, for more information about the Harris corner detector.

How to draw a straight across the centroid points of the barcode using best fit points Matlab

This is the processed image and I can't increase the bwareaopen() as it won't work for my other image.
Anyway I'm trying to find the shortest points in the centre points of the barcode, to get the straight line across the centre points in the barcode.
Example:
After doing a centroid command, the points in the barcode are near to each other. Therefore, I just wanted to get the shortest points(which is the barcode) and draw a straight line across.
All the points need not be join, best fit points will do.
Step 1
Step 2
Step 3
If you dont have the x,y elements Andrey uses, you can find them by segmenting the image and using a naive threshold value on the area to avoid including the number below the bar code.
I've hacked out a solution in MATLAB doing the following:
Loading the image and making it binary
Extracting all connected components using bwlabel().
Getting useful information about each of them via regionprops() [.centroid will be a good approximation to the middel point for the lines].
Thresholded out small regions (noise and numbers)
Extracted x,y coordinates
Used Andreys linear fit solution
Code:
set(0,'DefaultFigureWindowStyle','docked');
close all;clear all;clc;
Im = imread('29ekeap.jpg');
Im=rgb2gray(Im);
%%
%Make binary
temp = zeros(size(Im));
temp(Im > mean(Im(:)))=1;
Im = temp;
%Visualize
f1 = figure(1);
imagesc(Im);colormap(gray);
%Find connected components
LabelIm = bwlabel(Im);
RegionInfo = regionprops(LabelIm);
%Remove background region
RegionInfo(1) = [];
%Get average area of regions
AvgArea = mean([RegionInfo(1:end).Area]);
%Vector to keep track of likely "bar elements"
Bar = zeros(length(RegionInfo),1);
%Iterate over regions, plot centroids if area is big enough
for i=1:length(RegionInfo)
if RegionInfo(i).Area > AvgArea
hold on;
plot(RegionInfo(i).Centroid(1),RegionInfo(i).Centroid(2),'r*')
Bar(i) = 1;
end
end
%Extract x,y points for interpolation
X = [RegionInfo(Bar==1).Centroid];
X = reshape(X,2,length(X)/2);
x = X(1,:);
y = X(2,:);
%Plot line according to Andrey
p = polyfit(x,y,1);
xMin = min(x(:));
xMax = max(x(:));
xRange = xMin:0.01:xMax;
yRange = p(1).*xRange + p(2);
plot(xRange,yRange,'LineWidth',2,'Color',[0.9 0.2 0.2]);
The result is a pretty good fitted line. You should be able to extend it to the ends by using the 'p' polynomal and evaluate when you dont encounter any more '1's if needed.
Result:
If you already found the x,y of the centers, you should use polyfit function:
You will then find the polynomial coefficients of the best line. In order to draw a segment, you can take the minimal and maximal x
p = polyfit(x,y,1);
xMin = min(x(:));
xMax = max(x(:));
xRange = xMin:0.01:xMax;
yRange = p(1).*xRange + p(2);
plot(xRange,yRange);
If your ultimate goal is to generate a line perpendicular to the bars in the bar code and passing roughly through the centroids of the bars, then I have another option for you to consider...
A simple solution would be to perform a Hough transform to detect the primary orientation of lines in the bar code. Once you find the angle of the lines in the bar code, all you have to do is rotate that by 90 degrees to get the slope of a perpendicular line. The centroid of the entire bar code can then be used as an intercept for this line. Using the functions HOUGH and HOUGHPEAKS from the Image Processing Toolbox, here's the code starting with a cropped version of your image from step 1:
img = imread('bar_code.jpg'); %# Load the image
img = im2bw(img); %# Convert from RGB to BW
[H, theta, rho] = hough(img); %# Perform the Hough transform
peak = houghpeaks(H); %# Find the peak pt in the Hough transform
barAngle = theta(peak(2)); %# Find the angle of the bars
slope = -tan(pi*(barAngle + 90)/180); %# Compute the perpendicular line slope
[y, x] = find(img); %# Find the coordinates of all the white image points
xMean = mean(x); %# Find the x centroid of the bar code
yMean = mean(y); %# Find the y centroid of the bar code
xLine = 1:size(img,2); %# X points of perpendicular line
yLine = slope.*(xLine - xMean) + yMean; %# Y points of perpendicular line
imshow(img); %# Plot bar code image
hold on; %# Add to the plot
plot(xMean, yMean, 'r*'); %# Plot the bar code centroid
plot(xLine, yLine, 'r'); %# Plot the perpendicular line
And here's the resulting image:

How do I display an arrow positioned at a specific angle in MATLAB?

I am working in MATLAB and I'm stuck on a very simple problem: I've got an object defined by its position (x,y) and theta (an angle, in degrees). I would like to plot the point and add an arrow, starting from the point and pointing toward the direction defined by the angle. It actually doesn't even have to be an arrow, anything graphically showing the value of the angle will do!
Here's a picture showing the kind of thing I'm trying to draw:
removed dead ImageShack link
The quiver() plotting function plots arrows like this. Take your theta value and convert it to (x,y) cartesian coordinates representing the vector you want to plot as an arrow and use those as the (u,v) parameters to quiver().
theta = pi/9;
r = 3; % magnitude (length) of arrow to plot
x = 4; y = 5;
u = r * cos(theta); % convert polar (theta,r) to cartesian
v = r * sin(theta);
h = quiver(x,y,u,v);
set(gca, 'XLim', [1 10], 'YLim', [1 10]);
Take a look through online the Matlab documentation to see other plot types; there's a lot, including several radial plots. They're in the MATLAB > Functions > Graphics > Specialized Plotting section. Do "doc quiver" at the command line and browse around.
If you want to try and make something that looks like the image you linked to, here's some code to help you do it (NOTE: you would first have to download the submission arrow.m by Erik Johnson on the MathWorks File Exchange, which I always like to use for generating arrows of any shape and size):
x = 1; % X coordinate of arrow start
y = 2; % Y coordinate of arrow start
theta = pi/4; % Angle of arrow, from x-axis
L = 2; % Length of arrow
xEnd = x+L*cos(theta); % X coordinate of arrow end
yEnd = y+L*sin(theta); % Y coordinate of arrow end
points = linspace(0, theta); % 100 points from 0 to theta
xCurve = x+(L/2).*cos(points); % X coordinates of curve
yCurve = y+(L/2).*sin(points); % Y coordinates of curve
plot(x+[-L L], [y y], '--k'); % Plot dashed line
hold on; % Add subsequent plots to the current axes
axis([x+[-L L] y+[-L L]]); % Set axis limits
axis equal; % Make tick increments of each axis equal
arrow([x y], [xEnd yEnd]); % Plot arrow
plot(xCurve, yCurve, '-k'); % Plot curve
plot(x, y, 'o', 'MarkerEdgeColor', 'k', 'MarkerFaceColor', 'w'); % Plot point
And here's what it would look like:
You can then add text to the plot (for the angle and the coordinate values) using the text function.
Here's a partial answer, I expect you can figure out the rest. I fired up the Figures editor and opened the plot tools. I dragged an arrow from the palette onto my figure. Then I generated an m-file. This included the line:
annotation(figure1,'arrow',[0.1489 0.2945],[0.5793 0.6481]);
So, the first pair of coordinates is the start of the arrow. You're going to have to figure out the pointy end (second pair of coordinates) using a little bit of trigonometry. You might even be able to get the little arc if you do some more fiddling around with plot tools.
Let us know if the trig defeats you. Oh, and I forgot to plot the point, but I guess you can figure that out ?