I am using this code simulate real camera to capture 3D object:
patch(ax, Object3D, 'FaceColor', 'flat');
camva(ax, rad2deg(VerticalAOV)); % Set the camera field of view
camup(ax, [0 -1 0]);
campos(ax, [CameraPosition.X CameraPosition.Y CameraPosition.Z]); % Put the camera at the origin
camtarget(ax, [CameraPosition.X CameraPosition.Y CameraPosition.Z] + [0 0 1]); % The camera looks along the +Z axis
camproj(ax,'perspective');
axis image;
axis off;
WidthResolution = SensorWidthResolution/(ax.Position(3));
Image = export_fig('temp.jpg',ax, sprintf('-r%f', WidthResolution),'-nocrop');
[ImageHeight, ImageWidth, Channels] = size(Image);
Image = imcrop(Image,[(ImageWidth-Width)/2, (ImageHeight-Height)/2, Width-1, Height-1]);
The problems are:
I used VerticalAOV as input for camva. I chose VerticalAOV based on this image. However, this is illogical. To get real view, one should provide both Horizontal and Vertical Angle.
[ImageHeight, ImageWidth, Channels] = size(Image); returns wrong
values. ImageWidth and ImageHeight does not provide the correct
values. It is clear that one of the dimessions would wrong since only
one dimension of the AOV was provided. However, why both of them are
not correct?
The crop I made is to correct the dimensions of the image. However,
it seems to be not useful step (I checked the result using ground
truth data I have and they did not match).
P.S. export_fig can be found here: https://github.com/altmany/export_fig
Related
I have this 3D image generated from the simple code below.
% Input Image size
imageSizeY = 200;
imageSizeX = 120;
imageSizeZ = 100;
%# create coordinates
[rowsInImage, columnsInImage, pagesInImage] = meshgrid(1:imageSizeY, 1:imageSizeX, 1:imageSizeZ);
%# get coordinate array of vertices
vertexCoords = [rowsInImage(:), columnsInImage(:), pagesInImage(:)];
centerY = imageSizeY/2;
centerX = imageSizeX/2;
centerZ = imageSizeZ/2;
radius = 28;
%# calculate distance from center of the cube
sphereVoxels = (rowsInImage - centerY).^2 + (columnsInImage - centerX).^2 + (pagesInImage - centerZ).^2 <= radius.^2;
%# Now, display it using an isosurface and a patch
fv = isosurface(sphereVoxels,0);
patch(fv,'FaceColor',[0 0 .7],'EdgeColor',[0 0 1]); title('Binary volume of a sphere');
view(45,45);
axis equal;
grid on;
xlabel('x-axis [pixels]'); ylabel('y-axis [pixels]'); zlabel('z-axis [pixels]')
I have tried plotting the image with isosurface and some other volume visualization tools, but there remains quite a few surprises for me from the plots.
The code has been written to conform to the image coordinate system (eg. see: vertexCoords) which is a left-handed coordinate system I presume. Nonetheless, the image is displayed in the Cartesian (right-handed) coordinate system. I have tried to see this displayed as the figure below, but that’s simply not happening.
I am wondering if the visualization functions have been written to display the image the way they do.
Image coordinate system:
Going forward, there are other aspects of the code I am to write for example if I have an input image sphereVoxels as in above, in addition to visualizing it, I would want to find north, south east, west, top and bottom locations in the image, as well as number and count the coordinates of the vertices, plus more.
I foresee this would likely become confusing for me if I don’t stick to one coordinate system, and considering that the visualization tools predominantly use the right-hand coordinate system, I would want to stick with that from the onset. However, I really do not know how to go about this.
Right-hand coordinate system:
Any suggestions to get through this?
When you call meshgrid, the dimensions x and y axes are switched (contrary to ndgrid). For example, in your case, it means that rowsInImage is a [120x100x200] = [x,y,z] array and not a [100x120x200] = [y,x,z] array even if meshgrid was called with arguments in the y,x,z order. I would change those two lines to be in the classical x,y,z order :
[columnsInImage, rowsInImage, pagesInImage] = meshgrid(1:imageSizeX, 1:imageSizeY, 1:imageSizeZ);
vertexCoords = [columnsInImage(:), rowsInImage(:), pagesInImage(:)];
How to know the exact size and position of the axis box (without axis labels and numbers)? For example, if I use
figure
contourf(x,y,u,100,'linestyle','none')
axis equal
set(gca,'position',[0.1,0.1,0.7,0.8]) %normalized units
The size of the axis frame/box is varyed in the case of the figure window resizing (or using axis equal), but the value of get(gca,'position') remains unchanged. For example:
figure
Z = peaks(20);
contourf(Z,10)
set(gca,'Units','pixels')
get(gca,'position')
axis equal
get(gca,'position')
ans =
0.1300 0.1100 0.7750 0.8150
after axis equal, the axis box is changed, but get(gca,'position') gives the same coordinates:
ans =
0.1300 0.1100 0.7750 0.8150
I need these to align the colorbar to the axis box (with fixed gap between them) in the case of axis equal.
When you call axis equal, the axis box aspect ratio is fixed and the Position property is treated as a maximum size. When you resize the figure window, the axis box will remain centered in the Position rectangle, but in order to maintain the same aspect ratio as before, it may not take up the entire Position rectangle.
If you want it to take up the entire Position rectangle, you can call axis equal again. (this may depend on your MATLAB version; it worked for me in R2015b).
This is also discussed in a bit more detail on MATLAB Central.
To answer your original question, it's a bit complicated. You'd have to get the plot box aspect ratio (using pbaspect() or the axes PlotBoxAspectRatio property) and figure it out:
ah = gca();
% Get the axes Position rectangle in units of pixels
old_units = get(ah,'Units');
set(ah,'Units','pixels');
pos = get(ah,'Position');
set(ah,'Units',old_units);
% Figure the PlotBox and axes Position aspect ratios
pos_aspectRatio = pos(3) / pos(4);
box_aspect = pbaspect(ah);
box_aspectRatio = box_aspect(1) / box_aspect(2);
if (box_aspectRatio > pos_aspectRatio)
% PlotBox is wider than the Position rectangle
box_height = pos(3) / box_aspectRatio;
box_dy = (pos(4)-box_height) / 2;
box_position = [pos(1), pos(2)+box_dy, pos(3), box_height];
else
% PlotBox is taller than the Position rectangle
box_width = pos(4) * box_aspectRatio;
box_dx = (pos(3)-box_width) / 2;
box_position = [pos(1)+box_dx, pos(2), box_width, pos(4)];
end
Note that this will give you the box position in pixels; if you want it in the normalized units that are the axes default, you'll need to normalize it:
fig_pos = get(get(ah,'Parent'),'Position');
box_position = box_position ./ fig_pos([3 4 3 4]);
The background of this problem relates to my attempt to combine output from a ray tracer with Matlab's 3d plotters. When doing ray tracing, there is no need to apply a perspective transformation to the rendered image. You see this in the image below. Basically, the intersections of the rays with the viewport will automatically adjust for the perspective scaling.
Suppose I've gone and created a ray-traced image (so I am given my camera, my focal length, viewport dimensions, etc.). How do I create exactly the same view in Matlab's 3d plotting environment?
Here is an example:
clear
close all
evec = [0 200 300]; % Camera position
recw = 200; % cm width of box
recl = 200; % cm length of box
h = 150; % cm height of box
% Create the front face rectangle
front = zeros(3,5);
front(:,1) = [-recw/2; 0; -recl/2];
front(:,2) = [recw/2; 0; -recl/2];
front(:,3) = [recw/2; h; -recl/2];
front(:,4) = [-recw/2; h; -recl/2];
front(:,5) = front(:,1);
% Back face rectangle
back = zeros(3,5);
back(:,1) = [-recw/2; 0; recl/2];
back(:,2) = [recw/2; 0; recl/2];
back(:,3) = [recw/2; h; recl/2];
back(:,4) = [-recw/2; h; recl/2];
back(:,5) = back(:,1);
% Plot the world view
figure(1);
patch(front(1,:), front(2,:), front(3,:), 'r'); hold all
patch(back(1,:), back(2,:), back(3,:), 'b');
plot3(evec(1), evec(2), evec(3), 'bo');
xlabel('x'); ylabel('y'); zlabel('z');
title('world view'); view([-30 40]);
% Plot the camera view
figure(2);
patch(front(1,:), front(2,:), front(3,:), 'r'); hold all
patch(back(1,:), back(2,:), back(3,:), 'b');
xlabel('x'); ylabel('y'); zlabel('z');
title('Camera view');
campos(evec);
camup([0 1 0]); % Up vector is y+
camproj('perspective');
camtarget([evec(1), evec(2), 0]);
title('camera view');
Now you see the world view
and the camera view
I know how to adjust the camera position, the camera view angle, and orientation to match the output from my ray tracer. However, I do not know how to adjust Matlab's built-in perspective command
camproj('perspective')
for different distortions.
Note: within the documentation, there is the viewmtx command, which allows you to output a transformation matrix corresponding to a perspective distortion of a certain angle. This is not quite what I want. I want to do things in 3D and through Matlab's OpenGL viewer. In essence, I want a command like
camproj('perspective', distortionamount)
so I can match up the amount of distortion in Matlab's viewer with the distortion from the ray tracer. If you use the viewmtx command to create the 2D projections, you will not be able to use patch' orsurf' and keep colours and faces intact.
The MATLAB perspective projection works just like your raytracer. You don't need any transformation matrices to it use it. Perspective distortion is determined entirely by the camera position and direction of projection.
In the terminology of the raytracer diagram above, if the CameraPosition matches your raytracer's pinhole coordinates and the vector between CameraPosition and CameraTarget is perpendicular to your raytracer's viewport, the perspective distortion will also match. The rest is just scaling and alignment.
I want to display an image with axes that has a different vertical and horizontal scale.
The following code gives me an image that is very long and thin. If I multiply the scale of the y-axis by 250 (commented line) I get the aspect ratio of the image I want but now the scale on the y-axis is wrong.
A = rand(100,400);
A_image = mat2gray(A);
A_image = imresize(A_image,2);
RI = imref2d(size(A_image),[0 800],[-1 1]);
%RI = imref2d(size(A_image),[0 800],250*[-1 1]);
figure(1);
imshow(256*A_image,RI,jet)
xlabel('$t$ (s)');
ylabel('$z$ (m)');
Changing the world reference changes the axis labels to match that world reference, but you can always change the labels back.
xlabels=get(gca,'XTickLabels'); % //this will get your current labels;
nlabels=length(xlabels); % //Get how many we need
new_xlabels=linspace(-1,1,nlabels); % //Create a linear space at each label point
set(gca,'XTickLabels',new_xlabels); % //apply the new labels
Below is an arbitrary hand-drawn Intensity profile of a line in an image:
The task is to draw the line. The profile can be approximated to an arc of a circle or ellipse.
This I am doing for camera calibration. Since I do not have the actual industrial camera, I am trying to simulate the correction needed for calibration.
The question can be rephrased as I want pixel values which will follow a plot similar to the above. I want to do this using program (Preferably using opencv) and not manually enter these values because I have thousands of pixels in the line.
An algorithm/pseudo code will suffice. Also please note that I do not have any actual Intensity profile, otherwise I would have read those values.
When will you encounter such situation ?
Suppose you take a picture (assuming complete white) from a Camera, your object being placed on table, and camera just above it in vertical direction. The light coming on the center of the picture vertically downward from the camera will be stronger in intensity as compared to the light reflecting at the edges. You measure pixel values across any line in the Image, you will find intensity curve like shown above. Since I dont have camera for the time being, I want to emulate this situation. How to achieve this?
This is not exactly image processing, rather image generation... but anyways.
Since you want an arc, we still need three points on that arc, lets take the first, middle and last point (key characteristics in my opinion):
N = 100; % number of pixels
x1 = 1;
x2 = floor(N/2);
x3 = N;
y1 = 242;
y2 = 255;
y3 = 242;
and now draw a circle arc that contains these points.
This problem is already discussed here for matlab: http://www.mathworks.nl/matlabcentral/newsreader/view_thread/297070
x21 = x2-x1; y21 = y2-y1;
x31 = x3-x1; y31 = y3-y1;
h21 = x21^2+y21^2; h31 = x31^2+y31^2;
d = 2*(x21*y31-x31*y21);
a = x1+(h21*y31-h31*y21)/d; % circle center x
b = y1-(h21*x31-h31*x21)/d; % circle center y
r = sqrt(h21*h31*((x3-x2)^2+(y3-y2)^2))/abs(d); % circle radius
If you assume the middle value is always larger (and thus it's the upper part of the circle you'll have to plot), you can draw this with:
x = x1:x3;
y = b+sqrt(r^2-(x-a).^ 2);
plot(x,y);
you can adjust the visible window with
xlim([1 N]);
ylim([200 260]);
which gives me the following result: