Related
I'd like to take in an RGB image, find the points in the image that are white, and get the cartesian coordinates of those points in the image. I've gotten most of the way there, but when I try to plot the cartesian coordinates, I get a vertically tiled image (i.e. 5 overlapped copies of what I should see). Anyone know what could be causing this?
,
Code: (JPG comes in as 2448 x x3264 x 3 uint8)
I = imread('IMG_0245.JPG');
imshow(I); % display unaltered image
% Convert image to grayscale
I = rgb2gray(I);
% Convert image to binary (black/white)
I = im2bw(I, 0.9);
% Generate cartesian coordinates of image
imageSize = size(I);
[x, y] = meshgrid( 1:imageSize(1), 1:imageSize(2) );
PerspectiveImage = [x(:), y(:), I(:)];
% Get indices of white points only
whiteIndices = find(PerspectiveImage(:,3));
figure; plot( PerspectiveImage(whiteIndices, 1), PerspectiveImage(whiteIndices, 2),'.');
% Flip vertically to correct indexing vs. plotting issue
axis ij
Very simple. You're declaring your meshgrid wrong. It should be:
[x, y] = meshgrid( 1:imageSize(2), 1:imageSize(1) );
The first parameter denotes the horizontal extents of the 2D grid, and so you want to make this vary for as many columns as you have. Similarly, the second parameter denotes the vertical extents of the 2D grid, and so you want to make this for as many rows as you have.
I had to pre-process some of your image to get some good results because your original image had a large white border surrounding the image. I had to remove this border by removing all pure white pixels. I also read in the image directly from StackOverflow:
I = imread('http://s7.postimg.org/ovb53w4ff/Track_example.jpg');
mask = all(I == 255, 3);
I = bsxfun(#times, I, uint8(~mask));
This was the image I get after doing my pre-processing:
Once I do this and change your meshgrid call, I get this:
What is the preferred way of converting from axis coordinates (e.g. those taken in by plot or those output in point1 and point2 of houghlines) to pixel coordinates in an image?
I see the function axes2pix in the Mathworks documentation, but it is unclear how it works. Specifically, what is the third argument? The examples just pass in 30, but it is unclear where this value comes from. The explanations depend on a knowledge of several other functions, which I don't know.
The related question: Axis coordinates to pixel coordinates? suggests using poly2mask, which would work for a polygon, but how do I do the same thing for a single point, or a list of points?
That question also links to Scripts to Convert Image to and from Graph Coordinates, but that code threw an exception:
Error using /
Matrix dimensions must agree.
Consider the following code. It shows how to convert from axes coordinates to image pixel coordinates.
This is especially useful if you plot the image using custom XData/YData locations other than the default 1:width and 1:height. I am shifting by 100 and 200 pixels in the x/y directions in the example below.
function imageExample()
%# RGB image
img = imread('peppers.png');
sz = size(img);
%# show image
hFig = figure();
hAx = axes();
image([1 sz(2)]+100, [1 sz(1)]+200, img) %# shifted XData/YData
%# hook-up mouse button-down event
set(hFig, 'WindowButtonDownFcn',#mouseDown)
function mouseDown(o,e)
%# get current point
p = get(hAx,'CurrentPoint');
p = p(1,1:2);
%# convert axes coordinates to image pixel coordinates
%# I am also rounding to integers
x = round( axes2pix(sz(2), [1 sz(2)], p(1)) );
y = round( axes2pix(sz(1), [1 sz(1)], p(2)) );
%# show (x,y) pixel in title
title( sprintf('image pixel = (%d,%d)',x,y) )
end
end
(note how the axis limits do not start at (1,1), thus the need for axes2pix)
There may be a built-in way that I haven't heard of, but this shouldn't be hard to do from scratch...
set(axes_handle,'units','pixels');
pos = get(axes_handle,'position');
xlim = get(axes_handle,'xlim');
ylim = get(axes_handle,'ylim');
Using these values, you can convert from axes coordinates to pixels easily.
x_in_pixels = pos(1) + pos(3) * (x_in_axes-xlim(1))/(xlim(2)-xlim(1));
%# etc...
The above uses pos(1) as the x-offset of the axes within the figure. If you don't care about this, don't use it. Likewise, if you want it in screen coordinates, add the x-offset of the position obtained by get(figure_handle,'position')
I use the following lines of code to plot the images:
for t=1:subplotCol
subplot(subplotRow,subplotCol,t)
imagesc([1 100],[1 100],c(:,:,nStep*t));
colorbar
xlabel('X-axis')
ylabel('Y-axis')
title(['Concentration profile at t_{',num2str(nStep*t),'}'])
subplot(subplotRow,subplotCol,subplotCol+t)
hold on;
plot(distance,gamma(:,1,t),'-ob');
plot(distance,gamma(:,2,t),'-or');
plot(distance,gamma(:,3,t),'-og');
xlabel('h');ylabel('\gamma (h)');
legend(['\Theta = ',num2str(theta(1))],...
['\Theta = ',num2str(theta(2))],['\Theta = ',num2str(theta(3))]);
end
I get the following subplot with images:
As you can see the images in first row are now scaled equally on X and Y axis (Y axis is longer than the X-axis) even though the size of the image matrix is 100x100 for each image on first row.
Can someone help with how to make images in first row look like squares than rectangles which I get currently. Thanks.
Use the dataAspectRatio properties of the axes, and set it to [1 1 1]
%# create a test image
imagesc(1:10,1:10,rand(10))
%# you should use the handle returned by subplot
%# instead of gca
set(gca,'dataAspectRatio',[1 1 1])
Another method is to use the command axis equal
Regarding the size of different objects, you can set the exact position of each axes on the figure, for example for the first one use:
subplot(2,2,1); set(gca,'Position',[0.05 0.05 0.4 0.4])
When laying out a figure in MATLAB, typing axis equal ensures that no matter what the figure dimensions, the axes will always be square:
My current problem is that I want to add a second axes to this plot. Usually, that's no problem; I would just type axes([x1 y1 x2 y2]), and a new square figure would be added with corners at (x1, y1), (x2, y2), which is a fixed location relative to the figure. The problem is, I want this new axes to be located at a fixed location relative to the first axes.
So, my questions are:
Does anyone know how I can position an axes in a figure by specifying the location relative to another axes?
Assuming I can do 1, how can I have this new axes remain in the same place even if I resize the figure?
An axis position property is relative to its parent container. Therefore, one possibility is to create a transparent panel with the same size as the first axis, then inside it create the second axis, and set its location and size as needed. The position specified would be as if it were relative to the first axis.
Now we need to always maintain the panel to be the same size/location as the first axis. Usually this can be done using LINKPROP which links a property of multiple graphic objects (panel and axis) to be the same, namely the 'Position' property.
However, this would fail in your case: when calling axis image, it fixes the data units to be the same in every direction by setting aspect ratio properties like 'PlotBoxAspectRatio' and 'DataAspectRatio'. The sad news is that the 'Position' property will not reflect the change in size, thus breaking the above solution. Here is an example to illustrate the problem: if you query the position property before/after issuing the axis image call, it will be the same:
figure, plot(1:10,1:10)
get(gca,'Position')
pause(1)
axis image
get(gca,'Position')
Fortunately for us, there is a submission on FEX (plotboxpos) that solves this exact issue, and returns the actual position of the plotting region of the axis. Once we have that, it's a matter of syncing the panel position to the axis position. One trick is to create a event listener for when the axis changes size (it appears that the 'TightInset' property changes unlike the 'Position' property, so that could be the trigger in our case).
I wrapped the above in a function AXESRELATIVE for convenience: you call it as you would the builtin AXES function. The only difference is you give it as first argument the handle to the axis you want to relatively-position the newly created axis against. It returns handles to both the new axis and its containing panel.
Here is an example usage:
%# automatic resize only works for normalized units
figure
hParentAx = axes('Units','normalized');
axis(hParentAx, 'image')
%# create a new axis positioned at normalized units with w.r.t the previous axis
%# the axis should maintain its relative position on resizing the figure
[hAx hPan] = axesRelative(hParentAx, ...
'Units','normalized', 'Position',[0.7 0.1 0.1 0.1]);
set(hAx, 'Color','r')
And the function implementation:
function [hAx hPan] = axesRelative(hParentAx, varargin)
%# create panel exactly on top of parent axis
s = warning('off', 'MATLAB:hg:ColorSpec_None');
hPan = uipanel('Parent',get(hParentAx, 'Parent'), ...
'BorderType','none', 'BackgroundColor','none', ...
'Units',get(hParentAx,'Units'), 'Position',plotboxpos(hParentAx));
warning(s)
%# sync panel to always match parent axis position
addlistener(handle(hParentAx), ...
{'TightInset' 'Position' 'PlotBoxAspectRatio' 'DataAspectRatio'}, ...
'PostSet',#(src,ev) set(hPan, 'Position',plotboxpos(hParentAx)) );
%# create new axis under the newly created panel
hAx = axes('Parent',hPan, varargin{:});
end
On a completely different note: before you recent edit, I got the impression that you were trying to produce a scatter plot of images (i.e like a usual scatter plot, but with full images instead of points).
What you suggested (from what I understand) is creating one axis for each image, and setting its position corresponding to the x/y coordinates of the point.
My solution is to use the IMAGE/IMAGESC functions and draw the small images by explicitly setting the 'XData' and 'YData' properties to shift and scale the images appropriately. The beauty of this is it require a single axis, and doesn't suffer from having to deal with resizing issues..
Here is a sample implementation for that:
%# create fan-shaped coordinates
[R,PHI] = meshgrid(linspace(1,2,5), linspace(0,pi/2,10));
X = R.*cos(PHI); Y = R.*sin(PHI);
X = X(:); Y = Y(:);
num = numel(X);
%# images at each point (they don't have to be the same)
img = imread('coins.png');
img = repmat({img}, [num 1]);
%# plot scatter of images
SCALE = 0.2; %# image size along the biggest dimension
figure
for i=1:num
%# compute XData/YData vectors of each image
[h w] = size(img{i});
if h>w
scaleY = SCALE;
scaleX = SCALE * w/h;
else
scaleX = SCALE;
scaleY = SCALE * h/w;
end
xx = linspace(-scaleX/2, scaleX/2, h) + X(i);
yy = linspace(-scaleY/2, scaleY/2, w) + Y(i);
%# note: we are using the low-level syntax of the function
image('XData',xx, 'YData',yy, 'CData',img{i}, 'CDataMapping','scaled')
end
axis image, axis ij
colormap gray, colorbar
set(gca, 'CLimMode','auto')
This is usually the sort of thing you can take care of with a custom 'ResizeFcn' for your figure which will adjust the position and size of the smaller axes with respect the the larger. Here's an example of a resize function that maintains the size of a subaxes so that it is always 15% the size of the larger square axes and located in the bottom right corner:
function resizeFcn(src,event,hAxes,hSubAxes)
figurePosition = get(get(hAxes,'Parent'),'Position');
axesPosition = get(hAxes,'Position').*figurePosition([3 4 3 4]);
width = axesPosition(3);
height = axesPosition(4);
minExtent = min(width,height);
newPosition = [axesPosition(1)+(width-minExtent)/2+0.8*minExtent ...
axesPosition(2)+(height-minExtent)/2+0.05*minExtent ...
0.15*minExtent ...
0.15*minExtent];
set(hSubAxes,'Units','pixels','Position',newPosition);
end
And here's an example of its use:
hFigure = figure('Units','pixels'); %# Use pixel units for figure
hAxes = axes('Units','normalized'); %# Normalized axes units so it auto-resizes
axis(hAxes,'image'); %# Make the axes square
hSubAxes = axes('Units','pixels'); %# Use pixel units for subaxes
set(hFigure,'ResizeFcn',{#resizeFcn,hAxes,hSubAxes}); %# Set resize function
I have an image in MATLAB:
im = rgb2gray(imread('some_image.jpg');
% normalize the image to be between 0 and 1
im = im/max(max(im));
And I've done some processing that resulted in a number of points that I want to highlight:
points = some_processing(im);
Where points is a matrix the same size as im with ones in the interesting points.
Now I want to draw a circle on the image in all the places where points is 1.
Is there any function in MATLAB that does this? The best I can come up with is:
[x_p, y_p] = find (points);
[x, y] = meshgrid(1:size(im,1), 1:size(im,2))
r = 5;
circles = zeros(size(im));
for k = 1:length(x_p)
circles = circles + (floor((x - x_p(k)).^2 + (y - y_p(k)).^2) == r);
end
% normalize circles
circles = circles/max(max(circles));
output = im + circles;
imshow(output)
This seems more than somewhat inelegant. Is there a way to draw circles similar to the line function?
You could use the normal PLOT command with a circular marker point:
[x_p,y_p] = find(points);
imshow(im); %# Display your image
hold on; %# Add subsequent plots to the image
plot(y_p,x_p,'o'); %# NOTE: x_p and y_p are switched (see note below)!
hold off; %# Any subsequent plotting will overwrite the image!
You can also adjust these other properties of the plot marker: MarkerEdgeColor, MarkerFaceColor, MarkerSize.
If you then want to save the new image with the markers plotted on it, you can look at this answer I gave to a question about maintaining image dimensions when saving images from figures.
NOTE: When plotting image data with IMSHOW (or IMAGE, etc.), the normal interpretation of rows and columns essentially becomes flipped. Normally the first dimension of data (i.e. rows) is thought of as the data that would lie on the x-axis, and is probably why you use x_p as the first set of values returned by the FIND function. However, IMSHOW displays the first dimension of the image data along the y-axis, so the first value returned by FIND ends up being the y-coordinate value in this case.
This file by Zhenhai Wang from Matlab Central's File Exchange does the trick.
%----------------------------------------------------------------
% H=CIRCLE(CENTER,RADIUS,NOP,STYLE)
% This routine draws a circle with center defined as
% a vector CENTER, radius as a scaler RADIS. NOP is
% the number of points on the circle. As to STYLE,
% use it the same way as you use the rountine PLOT.
% Since the handle of the object is returned, you
% use routine SET to get the best result.
%
% Usage Examples,
%
% circle([1,3],3,1000,':');
% circle([2,4],2,1000,'--');
%
% Zhenhai Wang <zhenhai#ieee.org>
% Version 1.00
% December, 2002
%----------------------------------------------------------------
Funny! There are 6 answers here, none give the obvious solution: the rectangle function.
From the documentation:
Draw a circle by setting the Curvature property to [1 1]. Draw the circle so that it fills the rectangular area between the points (2,4) and (4,6). The Position property defines the smallest rectangle that contains the circle.
pos = [2 4 2 2];
rectangle('Position',pos,'Curvature',[1 1])
axis equal
So in your case:
imshow(im)
hold on
[y, x] = find(points);
for ii=1:length(x)
pos = [x(ii),y(ii)];
pos = [pos-0.5,1,1];
rectangle('position',pos,'curvature',[1 1])
end
As opposed to the accepted answer, these circles will scale with the image, you can zoom in an they will always mark the whole pixel.
Hmm I had to re-switch them in this call:
k = convhull(x,y);
figure;
imshow(image); %# Display your image
hold on; %# Add subsequent plots to the image
plot(x,y,'o'); %# NOTE: x_p and y_p are switched (see note below)!
hold off; %# Any subsequent plotting will overwrite the image!
In reply to the comments:
x and y are created using the following code:
temp_hull = stats_single_object(k).ConvexHull;
for k2 = 1:length(temp_hull)
i = i+1;
[x(i,1)] = temp_hull(k2,1);
[y(i,1)] = temp_hull(k2,2);
end;
it might be that the ConvexHull is the other way around and therefore the plot is different. Or that I made a mistake and it should be
[x(i,1)] = temp_hull(k2,2);
[y(i,1)] = temp_hull(k2,1);
However the documentation is not clear about which colum = x OR y:
Quote: "Each row of the matrix contains the x- and y-coordinates of one vertex of the polygon. "
I read this as x is the first column and y is the second colum.
In newer versions of MATLAB (I have 2013b) the Computer Vision System Toolbox contains the vision.ShapeInserter System object which can be used to draw shapes on images. Here is an example of drawing yellow circles from the documentation:
yellow = uint8([255 255 0]); %// [R G B]; class of yellow must match class of I
shapeInserter = vision.ShapeInserter('Shape','Circles','BorderColor','Custom','CustomBorderColor',yellow);
I = imread('cameraman.tif');
circles = int32([30 30 20; 80 80 25]); %// [x1 y1 radius1;x2 y2 radius2]
RGB = repmat(I,[1,1,3]); %// convert I to an RGB image
J = step(shapeInserter, RGB, circles);
imshow(J);
With MATLAB and Image Processing Toolbox R2012a or newer, you can use the viscircles function to easily overlay circles over an image. Here is an example:
% Plot 5 circles at random locations
X = rand(5,1);
Y = rand(5,1);
% Keep the radius 0.1 for all of them
R = 0.1*ones(5,1);
% Make them blue
viscircles([X,Y],R,'EdgeColor','b');
Also, check out the imfindcircles function which implements the Hough circular transform. The online documentation for both functions (links above) have examples that show how to find circles in an image and how to display the detected circles over the image.
For example:
% Read the image into the workspace and display it.
A = imread('coins.png');
imshow(A)
% Find all the circles with radius r such that 15 ≤ r ≤ 30.
[centers, radii, metric] = imfindcircles(A,[15 30]);
% Retain the five strongest circles according to the metric values.
centersStrong5 = centers(1:5,:);
radiiStrong5 = radii(1:5);
metricStrong5 = metric(1:5);
% Draw the five strongest circle perimeters.
viscircles(centersStrong5, radiiStrong5,'EdgeColor','b');
Here's the method I think you need:
[x_p, y_p] = find (points);
% convert the subscripts to indicies, but transposed into a row vector
a = sub2ind(size(im), x_p, y_p)';
% assign all the values in the image that correspond to the points to a value of zero
im([a]) = 0;
% show the new image
imshow(im)