3D reconstruction from a 2D image in MATLAB - image

I have some difficulties to reconstruct a 3D scene from a 2D image under Matlab.
I would like to create a top view of scene removing the perspective, in other words, realize an inverse perspective mapping.
Let's assume we know the camera position, orientation and its parameters. Moreover we consider all the captured points lie on the same plane XY.
Then, it is easy to prove that a pixel at a (u,v) location in the image will move to the coordinate (X,Y,0) in the 3D space with:
X=-((u*P(3,4)-P(1,4))*(v*P(3,1)-P(2,1)) + (v*P(3,4)-P(2,4))*(P(1,1)-u*P(3,1)))/((u*P(3,2)-P(1,2))*(v*P(3,1)-P(2,1)) + (v*P(3,2)-P(2,2))*(P(1,1)-u*P(3,1)));
Y=(X*(u*P(3,2)-P(1,2)) + (u*P(3,4)-P(1,4)))/(P(1,1)-u*P(3,1));
P is the projection matrix such that: P=[KR KT] with K,R and T respectively the intrinsic, rotation and translation matrices.
Once all the 3D locations of each pixel are computed, I would like to display the XY plane with the color information of the original pixel as if it was a 2D image.
However, a pixel (u,v) can mapped in 3D space to a non integer location meaning that I get a non-regular scatter plot were each (X,Y) point contain a color information.
I tried to divide the XY plane into small windows and then compute the average color of all points into each squares but it is very slow.
Please find my code below.
Some help would be appreciated.
Thank you in advance,
Pm
% This program aims to convert a 2D image into a 3D scenario. Let's assume that
% all the points in the image lie on the same plan in the 3D space.
% Program:
% 1-Generate the 3D scenario with 4 squares from diferrent colors
% 2-Take a picture of these squares by projecting the scene into an image 2D space
% 3-Reconstruct the 3D scene from the 2D picture
% Author: Pierre-Marie Damon
clear all; close all; clc;
%% 4 SQUARES DEFINITION
c=10;
sq1_3D = [ 0 0 0;
0 c 0;
c c 0;
c 0 0];
sq2_3D = [ 0 0 0;
c 0 0;
c -c 0;
0 -c 0];
sq3_3D = [ 0 0 0;
0 -c 0;
-c -c 0;
-c 0 0];
sq4_3D = [ 0 0 0;
-c 0 0;
-c c 0;
0 c 0];
SQ_3D = [sq1_3D;
sq2_3D;
sq3_3D;
sq4_3D];
%% CAMERA DEFINITION
% Image resolution:
image_size = [640,480];
% Intrinsic matrix:
fx=150; fy=150; % fx, fy: focal lengths in pixel
x0=image_size(1)/2; y0=image_size(2)/2; % x0, y0: optical center projection coordinates in the 2D cxamera space
K = [fx 0 x0 ;
0 fy y0 ;
0 0 1 ];
% 3D camera orientation:
Rot_cam = [ 0 0 1;
-1 0 0;
0 -1 0]';
% 3D camera rotations:
yaw = -20*pi/180;
roll = -0*pi/180;
pitch = -20*pi/180;
% 3D camera position:
O_Rcam = [-20;0;10];
h_cam = O_Rcam(3); % camera height
% Projection & transformation matrices
R = rotationVectorToMatrix([pitch,yaw,roll])*Rot_cam; % Rotation matrix
T = -R*O_Rcam; % Translation matrix
P = [K*R K*T; zeros(1,3) 1]; %Projection Matrix
%% PROJECTION FROM 3D TO THE 2D IMAGE
SQ_2D = (P*[SQ_3D ones(size(SQ_3D,1),1)]')'; % homogeneous coordinates
SQ_2D = SQ_2D(:,1:2)./repmat(SQ_2D(:,3),1,2); % Normalization
% Square splits:
sq1_2D = SQ_2D(1:4,:);
sq2_2D = SQ_2D(5:8,:);
sq3_2D = SQ_2D(9:12,:);
sq4_2D = SQ_2D(13:16,:);
%% PLOT THE 3D SCENARIO
figure('units','normalized','outerposition',[0 0 1 1]);
f1=subplot(1,2,1);hold on; grid on; view(50, 30); axis equal;
xlabel('X');ylabel('Y');zlabel('Z'); title('3D Scene');
% Plot the camera
cam = plotCamera('Location',O_Rcam,'Orientation',R,'Size',1,'AxesVisible',0);
% Plot the squares
patch([sq1_3D(:,1)],[sq1_3D(:,2)],'r','EdgeColor','k');
patch([sq2_3D(:,1)],[sq2_3D(:,2)],'g','EdgeColor','k');
patch([sq3_3D(:,1)],[sq3_3D(:,2)],'b','EdgeColor','k');
patch([sq4_3D(:,1)],[sq4_3D(:,2)],'m','EdgeColor','k');
%% PLOT THE 2D IMAGE
f2=subplot(1,2,2); hold on; grid on; axis equal; set(gca,'YDir','Reverse'); title('2D Image'); axis off;
xlim([0 image_size(1)]); ylim([0 image_size(2)]);
% plot the projected squares
patch ([sq1_2D(:,1)],[sq1_2D(:,2)],'r','EdgeColor','none');
patch ([sq2_2D(:,1)],[sq2_2D(:,2)],'g','EdgeColor','none');
patch ([sq3_2D(:,1)],[sq3_2D(:,2)],'b','EdgeColor','none');
patch ([sq4_2D(:,1)],[sq4_2D(:,2)],'m','EdgeColor','none');
% Plot the image borders
plot([0 image_size(1)],[0 0],'k','linewidth',3);
plot([image_size(1) image_size(1)],[0 image_size(2)],'k','linewidth',3);
plot([0 image_size(1)],[image_size(2) image_size(2)],'k','linewidth',3);
plot([0 0],[0 image_size(2)],'k','linewidth',3);
%% GENERATE A JPG IMAGE
figure; hold on; grid on; set(gca,'YDir','Reverse'); axis off;
hFig = gcf; hAx = gca; % get the figure and axes handles
set(hFig,'units','normalized','outerposition',[0 0 1 1]); % set the figure to full screen
set(hAx,'Unit','normalized','Position',[0 0 1 1]); % set the axes to full screen
set(hFig,'menubar','none'); % hide the toolbar
set(hFig,'NumberTitle','off'); % to hide the title
xlim([0 image_size(1)]); ylim([0 image_size(2)]);
% Plot the squares
patch ([sq1_2D(:,1)],[sq1_2D(:,2)],'r','EdgeColor','none');
patch ([sq2_2D(:,1)],[sq2_2D(:,2)],'g','EdgeColor','none');
patch ([sq3_2D(:,1)],[sq3_2D(:,2)],'b','EdgeColor','none');
patch ([sq4_2D(:,1)],[sq4_2D(:,2)],'m','EdgeColor','none');
% Create the image
set(gcf,'PaperUnits','inches','PaperPosition',[0 0 image_size(1)/100 image_size(2)/100])
print -djpeg Image.jpg -r100
save('ImageParam.mat', 'K','R','T','P','O_Rcam' )
%% 3D RECONSTRUCTION FROM 2D IMAGE
clear all;
close all;
clc;
load('ImageParam');
I=imread('Image.jpg');
I1 = rgb2gray(I);
figure;imshow(I1);impixelinfo;
I2 = zeros(size(I1));
k=1; i=1; j=1;
tic
for y=size(I2,1):-1:1
for x=1:size(I2,2)
% The formula below comes from the projection equations of
% the camera and the additional constraint that all the points lie on the XY
% plane
X(k)=-((x*P(3,4)-P(1,4))*(y*P(3,1)-P(2,1)) + (y*P(3,4)-P(2,4))*(P(1,1)-x*P(3,1)))/((x*P(3,2)-P(1,2))*(y*P(3,1)-P(2,1)) + (y*P(3,2)-P(2,2))*(P(1,1)-x*P(3,1)));
Y(k)=(X(k)*(x*P(3,2)-P(1,2)) + (x*P(3,4)-P(1,4)))/(P(1,1)-x*P(3,1));
Z(k)=0;
C(k)=I1(y,x); % Color (gray intensity) information
k=k+1;
end
end
toc
figure;hold on;axis equal;
plot(X,Y,'.');
grid on;

Related

Rotate image in Matlab about arbitrary points

I am trying to rotate an image in Matlab about an arbitrary set of points.
So far, I have used imrotate, but it looks like imrotate only rotates about the center.
Is there a nice way of doing this without first padding the image and then using imrotate?
Thank you
The "nice way" is using imwarp
Building the transformation matrix is a little tricky.
I figured out how to build it from a the following question: Matlab image rotation
The transformation supports rotation, translation and zoom.
Parameters:
(x0, y0) is the center point that you rotate around it.
phi is rotation angle.
sx, sy are horizontal and vertical zoom (set to value to 1).
W and H are width and height of input (and output) images.
Building 3x3 transformation matrix:
T = [sx*cos(phi), -sx*sin(phi), 0
sy*sin(phi), sy*cos(phi), 0
(W+1)/2-((sx*x0*cos(phi))+(sy*y0*sin(phi))), (H+1)/2+((sx*x0*sin(phi))-(sy*y0*cos(phi))), 1];
Using imwarp:
tform = affine2d(T);
J = imwarp(I, tform, 'OutputView', imref2d([H, W]), 'Interp', 'cubic');
Here is a complete executable code sample:
I = imresize(imread('peppers.png'), 0.5); %I is the input image
[H, W, ~] = size(I); %Height and Width of I
phi = 120*pi/180; %Rotate 120 degrees
%Zoom coefficients
sx = 1;
sy = 1;
%Center point (the point that the image is rotated around it).
x0 = (W+1)/2 + 50;
y0 = (H+1)/2 + 20;
%Draw white cross at the center of the point of the input image.
I(y0-0.5:y0+0.5, x0-19.5:x0+19.5, :) = 255;
I(y0-19.5:y0+19.5, x0-0.5:x0+0.5, :) = 255;
%Build transformation matrix.
T = [sx*cos(phi), -sx*sin(phi), 0
sy*sin(phi), sy*cos(phi), 0
(W+1)/2-((sx*x0*cos(phi))+(sy*y0*sin(phi))), (H+1)/2+((sx*x0*sin(phi))-(sy*y0*cos(phi))), 1];
tform = affine2d(T);
J = imwarp(I, tform, 'OutputView', imref2d([H, W]), 'Interp', 'cubic');
%Draw black cross at the center of the output image:
J(end/2:end/2+1, end/2-15:end/2+15, :) = 0;
J(end/2-15:end/2+15, end/2:end/2+1, :) = 0;
%Shows that the center of the output image is the point that the image was rotated around it.
figure;imshow(J)
Input image:
Output image:
Note:
Important advantage over other methods (like imrotate after padding), is that the center coordinates don't have to be integer values.
You can rotate around (100.4, 80.7) for example.

convert an image from Cartesian to Polar

I'm trying to convert an image with many circles with the same center, from Cartesian to Polar (so that the new image will be the circles but lines instead of the circles, see the image below), and that's working out just fine using the following code:
[r, c] = size(img);
r=floor(r/2);
c=floor(c/2);
[X, Y] = meshgrid(-c:c-1,-r:r-1);
[theta, rho] = cart2pol(X, Y);
subplot(221), imshow(img), axis on;
hold on;
subplot(221), plot(xCenter,yCenter, 'r+');
subplot(222), warp(theta, rho, zeros(size(theta)), img);
view(2), axis square;
The problem is, I don't understand why does it even work? (obviously it's not my code), I mean, when I use the function cart2pol I don't even use the image, it's just some vectors x and y generated from the meshgrid function..
and another problem is, I want somehow to have a new image (not just to be able to draw it with the wrap function) which is the original image but by the theta and rho coordinates (meaning the same pixels but rearranged)... I'm not even sure how to ask this, in the end I want to have an image which is a matrix so that I can sum each row and turn the matrix into a column vector...
You can think of your image as being a 2D matrix, where each pixel has an X and Y coordinate
[(1,1) (1,2) (1,3) .... (1,c)]
[(2,1) (2,2) (2,3) .... (2,c)]
[(3,1) (3,2) (3,3) .... (3,c)]
[.... .... .... .... .... ]
[(r,1) (r,2) (r,3) .... (r,c)]
In the code that you posted, it maps each of these (X,Y) coordinates to it's equivalent polar coordinate (R, theta) using the center of the image floor(c/2) and floor(r/2) as the reference point.
% Map pixel value at (1,1) to it's polar equivalent
[r,theta] = cart2pol(1 - floor(r/2),1 - floor(c/2));
So whatever pixel value was used for (1,1) should now appear in your new polar coordinate space at (r,theta). It is important to note that to do this conversion, no information about the actual pixel values in the image matters, rather we just want to perform this transformation for each pixel within the image.
So first we figure out where the center of the image is:
[r, c] = size(img);
r = floor(r / 2);
c = floor(c / 2);
Then we figure out the (X,Y) coordinates for every point in the image (after the center has already been subtracted out
[X, Y] = meshgrid(-c:c-1,-r:r-1);
Now convert all of these cartesian points to polar coordinates
[theta, rho] = cart2pol(X, Y);
All that warp now does, is say "display the value of img at (X,Y) at it's corresponding location in (theta, rho)"
warp(theta, rho, zeros(size(theta)), img);
Now it seems that you want a new 2D image where the dimensions are [nTheta, nRho]. To do this, you could use griddata to interpolate your scattered (theta, rho) image (which is displayed by warp above) to a regular grid.
% These is the spacing of your radius axis (columns)
rhoRange = linspace(0, max(rho(:)), 100);
% This is the spacing of your theta axis (rows)
thetaRange = linspace(-pi, pi, 100);
% Generate a grid of all (theta, rho) coordinates in your destination image
[T,R] = meshgrid(thetaRange, rhoRange);
% Now map the values in img to your new image domain
theta_rho_image = griddata(theta, rho, double(img), T, R);
Take a look at all the interpolation methods for griddata to figure out which is most appropriate for your scenario.
There were a couple other issues (like the rounding of the center) which caused the result to be slightly incorrect. A fully working example is provided below
% Create an image of circles
radii = linspace(0, 40, 10);
rows = 100;
cols = 100;
img = zeros(rows, cols);
for k = 1:numel(radii)
t = linspace(0, 2*pi, 1000);
xx = round((cos(t) * radii(k)) + (cols / 2));
yy = round((sin(t) * radii(k)) + (rows / 2));
toremove = xx > cols | xx < 1 | yy > rows | yy < 1;
inds = sub2ind(size(img), xx(~toremove), yy(~toremove));
img(inds) = 1;
end
[r,c] = size(img);
center_row = r / 2;
center_col = c / 2;
[X,Y] = meshgrid((1:c) - center_col, (1:r) - center_row);
[theta, rho] = cart2pol(X, Y);
rhoRange = linspace(0, max(rho(:)), 1000);
thetaRange = linspace(-pi, pi, 1000);
[T, R] = meshgrid(thetaRange, rhoRange);
theta_rho_image = griddata(theta, rho, double(img), T, R);
figure
subplot(1,2,1);
imshow(img);
title('Original Image')
subplot(1,2,2);
imshow(theta_rho_image);
title('Polar Image')
And the result

How can I change the outer limits of a Circular mask to a different colour

I have the following function that is successful in creating a grey circular mask over the image input, such that the new image is a grey border around a circular image. Example: Grey circular mask.
All I want to do is make the mask a very specific green, but I haven't been successful.
Here is the code:
function [newIm] = myCircularMask(im)
%Setting variables
rad = size(im,1)/2.1; %Radius of the circle window
im = double(im);
[rows, cols, planes]= size(im);
newIm = zeros(rows, cols, planes);
%Generating hard-edged circular mask with 1 inside and 0 outside
M = rows;
[X,Y] = meshgrid(-M/2:1:(M-1)/2, -M/2:1:(M-1)/2);
mask = double(zeros(M,M));
mask(X.^2 + Y.^2 < rad^2) = 1;
% Soften edge of mask
gauss = fspecial('gaussian',[12 12],0.1);
mask = conv2(mask,gauss,'same');
% Multiply image by mask, i.e. x1 inside x0 outside
for k=1:planes
newIm(:,:,k) = im(:,:,k).*mask;
end
% Make mask either 0 inside or -127 outside
mask = (abs(mask-1)*127);
% now add mask to image
for k=1:planes
newIm(:,:,k) = newIm(:,:,k)+mask;
end
newIm = floor(newIm)/255;
The type of green I would like to use is of RGB values [59 178 74].
I'm a beginner with MATLAB, so any help would be greatly appreciated.
Cheers!
Steve
After masking your image, create a color version of your mask:
% test with simple mask
mask = ones(10,10);
mask(5:7,5:7)=0;
% invert mask, multiply with rgb-values, make rgb-matrix:
r_green=59/255; g_green=178/255; b_green=74/255;
invmask=(1-mask); % use mask with ones/zeroes
rgbmask=cat(3,invmask*r_green,invmask*g_green,invmask*b_green);
Add this to your masked image.
Edit:
function [newIm] = myCircularMask(im)
%Setting variables
rad = size(im,1)/2.1; %Radius of the circle window
im = double(im);
[rows, cols, planes]= size(im);
newIm = zeros(rows, cols, planes);
%Generating hard-edged circular mask with 1 inside and 0 outside
M = rows;
[X,Y] = meshgrid(-M/2:1:(M-1)/2, -M/2:1:(M-1)/2);
mask = double(zeros(M,M));
mask(X.^2 + Y.^2 < rad^2) = 1;
% Soften edge of mask
gauss = fspecial('gaussian',[12 12],0.1);
mask = conv2(mask,gauss,'same');
% Multiply image by mask, i.e. x1 inside x0 outside
for k=1:planes
newIm(:,:,k) = im(:,:,k).*mask;
end
% Here follows the new code:
% invert mask, multiply with rgb-values, make rgb-matrix:
r_green=59/255; g_green=178/255; b_green=74/255;
invmask=(1-mask); % use mask with ones/zeroes
rgbmask=cat(3,invmask*r_green,invmask*g_green,invmask*b_green);
newIm=newIm+rgbmask;
Note that I haven't been able to test my suggestion, so there might be errors.

Plotting a 2D Moving Image in MatLab [duplicate]

I'm trying to plot small images on a larger plot... Actually its isomap algorithm, I got many points, now each point correspond to some image, I know which image is it... The porblem is how to load that image and plot on the graph?
One more thing I have to plot both image and the points, so, basically the images will overlap the points.
Certainly, the type of image given here
Something like this should get you started. You can use the low-level version of the image function to draw onto a set of axes.
% Define some random data
N = 5;
x = rand(N, 1);
y = rand(N, 1);
% Load an image
rgb = imread('ngc6543a.jpg');
% Draw a scatter plot
scatter(x, y);
axis([0 1 0 1]);
% Offsets of image from associated point
dx = 0.02;
dy = 0.02;
width = 0.1;
height = size(rgb, 1) / size(rgb, 2) * width;
for i = 1:N
image('CData', rgb,...
'XData', [x(i)-dx x(i)-(dx+width)],...
'YData', [y(i)-dy y(i)-(dy+height)]);
end

Detect angle of text in image with horizontal and rotate in matlab [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 7 years ago.
Improve this question
See the image below :
As you can seen in the image the written text in rotated by 90 deg angle and I want to rotate only the text to be horizontal. By what ever angle it is rotated I want to make it horizontal as in below :
I don't want the complete image to the rotated. I want that the text only is bounded and the angle by which the text is rotated is measured and then rotated by that angle to make it horizontal.
Can you suggest me a way to do so and then fit an ellipse onto the text ?
Thank you.
First, let's find x-y coordinates of all the dark pixels
bw = imread('http://i.imgur.com/0LxC6bd.png');
bw = min( bw, [], 3 ) < 50 ; % dark pixels - intensity lower than 50
[y x] = find( bw ); % note that find returns row-col coordinates.
Compute the covariance matrix of the text coordinates
mx = mean(x);
my = mean(y);
C = [ mean( (x-mx).^2 ), mean( (x-mx).*(y-my) );...
mean( (x-mx).*(y-my) ) mean( (y-my).^2 ) ];
You can get the orientation of the ellipse from the eigen vectors and eigen values of C:
[V D] = eig( C );
figure; imshow( bw ); hold on;
quiver( mx([1 1]), my([1 1]), (V(1,:)*D), (V(2,:)*D), .05 );
Looking at the eigenvectors and eigen values:
V =
-0.9979 -0.0643
-0.0643 0.9979
D =
1.0e+003 *
0.1001 0
0 1.3652
You can see that the eigen-vectors (columns of V) are approximately the pointing to the -X direction (first column) and the Y direction (second column). Examining the eigen values (diagonal of D) you can see that the second eigen value is much larger than the first - this is the major axis of your ellipse. Now you can recover the orientation of the ellipse:
[~, mxi] = max(diag(D)); % find major axis index: largest eigen-value
Recover the angle from the corresponding eigen-vector
or = atan2( V(2,mxi), V(1,mxi) ) * 180/pi ; % convert to degrees for readability
or =
93.6869
As you can see the major axis of the ellipse is almost 90deg off the horizon.
You can rotate the image back
imrotate( bw, -or );
Drawing an ellipse given the covariance matrix:
th = linspace(0, 2*pi, 500 );
xy = [cos(th);sin(th)];
RR = chol( C ); % cholesky decomposition
exy = xy'*RR; %//'
figure;imshow( bw ); hold on;
plot( 2*exy(:,1)+mx, 2*exy(:,2)+my, 'r', 'LineWidth', 2 );
I used this solution:
bw = im2;
bw = sum((1-im2).^2, 3) > .5;
%bw = min( bw, [], 3 ) < 50 ; % dark pixels - intensity lower than 50
[y x] = find( bw ); % note that find returns row-col coordinates.
mx = mean(x);
my = mean(y);
C = [ mean( (x-mx).^2 ), mean( (x-mx).*(y-my) );...
mean( (x-mx).*(y-my) ) mean( (y-my).^2 ) ];
[V D] = eig( C );
quiver( mx([1 1]), my([1 1]), (V(1,:)*D), (V(2,:)*D), .05 );
[~,mxi] = max(diag(D)); % find major axis index: largest eigen-value
or = atan2( V(2,mxi), V(1,mxi) ) * 180/pi ; % convert to degrees for readability
rotate = imrotate( im2, or-180 );
axes(handles.axes2);
imshow( rotate );
set(handles.text3, 'String',or-180);

Resources