My simplified problem is to animate some text on a 3D plot.
I have a cube,
vert = [1 1 0;0 1 0;0 1 1;1 1 1;0 0 1;1 0 1;1 0 0;0 0 0];
fac = [1 2 3 4; 4 3 5 6; 6 7 8 5; 1 2 8 7; 6 7 1 4; 2 3 5 8];
patch('Faces',fac,'Vertices',vert,'FaceColor',[.8 .5 .2]);
axis([0, 1, 0, 1, 0, 1]);
axis equal
axis off
Is it possible to get something like this?
Using text doesn't help (it looks fake!),
Thanks,
The idea is to use texture mapping as #Hoki showed. I tried to implement this on my end, here is what I came up with.
First we'll need 6 images to apply on the cube faces. These can be any random images of any size. For example:
% just a bunch of demo images from IPT toolbox
function imgs = get_images()
imgs = {
imread('autumn.tif');
imread('coloredChips.png');
imread('toysflash.png');
imread('football.jpg');
imread('pears.png');
imread('peppers.png');
};
end
Better yet, let's use an online service that returns placeholder images containing digits 1 to 6:
% online API for placeholder images
function imgs = get_images()
imgs = cell(6,1);
clr = round(255*brighten(lines(6),0.75));
for i=1:6
%bg = randsample(['0':'9' 'a':'f'], 6, true);
%fg = randsample(['0':'9' 'a':'f'], 6, true);
bg = strjoin(cellstr(dec2hex(clr(i,:))).', '');
fg = strjoin(cellstr(dec2hex(clr(7-i,:))).', '');
[img,map] = imread(sprintf(...
'http://placehold.it/100x100/%s/%s&text=%d', bg, fg, i));
imgs{i} = im2uint8(ind2rgb(img,map));
end
end
Here are the resulting images:
>> imgs = get_images();
>> montage(cat(4,imgs{:}))
Next let's create a function that renders a unit cube with images texture-mapped as faces:
function h = get_unit_cube(imgs)
% we need a cell array of 6 images, one for each face (they can be any size)
assert(iscell(imgs) && numel(imgs)==6);
% coordinates for unit cube
[D1,D2,D3] = meshgrid([-0.5 0.5], [-0.5 0.5], 0.5);
% texture mapped surfaces
opts = {'FaceColor','texturemap', 'EdgeColor','none'};
h = zeros(6,1);
h(6) = surface(D1, flipud(D2), D3, imgs{6}, opts{:}); % Z = +0.5 (top)
h(5) = surface(D1, D2, -D3, imgs{5}, opts{:}); % Z = -0.5 (bottom)
h(4) = surface(fliplr(D1), D3, flipud(D2), imgs{4}, opts{:}); % Y = +0.5 (right)
h(3) = surface(D1, -D3, flipud(D2), imgs{3}, opts{:}); % Y = -0.5 (left)
h(2) = surface(D3, D1, flipud(D2), imgs{2}, opts{:}); % X = +0.5 (front)
h(1) = surface(-D3, fliplr(D1), flipud(D2), imgs{1}, opts{:}); % X = -0.5 (back)
end
Here is what it looks like:
imgs = get_images();
h = get_unit_cube(imgs);
view(3), axis vis3d, rotate3d on
Now we can have some fun with this creating interesting animations. Consider the following:
% create two separate unit cubes
figure('Renderer','OpenGL')
h1 = get_unit_cube(get_images());
h2 = get_unit_cube(get_images());
set([h1;h2], 'FaceAlpha',0.8) % semi-transparent
view(3), axis vis3d off, rotate3d on
% create transformation objects, used as parents of cubes
t1 = hgtransform('Parent',gca);
t2 = hgtransform('Parent',gca);
set(h1, 'Parent',t1)
set(h2, 'Parent',t2)
% transform the second cube (scaled, rotated, then shifted)
M = makehgtform('translate', [-0.7 1.2 0.5]) * ...
makehgtform('yrotate', 15*(pi/180)) * ...
makehgtform('scale', 0.5);
set(t2, 'Matrix',M)
drawnow
axis on, axis([-2 2 -2 2 -0.7 1]), box on
xlabel x, ylabel y, zlabel z
% create animation by rotating cubes 5 times
% (1st rotated around z-axis, 2nd around its own z-axis in opposite
% direction as well as orbiting the 1st)
for r = linspace(0,10*pi,90)
R = makehgtform('zrotate', r);
set(t1, 'Matrix',R)
set(t2, 'Matrix',R\(M/R))
pause(0.1)
end
I'm using the hgtransform function to manage transformations, this is much more efficient than continuously changing the x/y/z data points of the graphics objects.
BTW I've used slightly different images in the animation above.
EDIT:
Let's replace the rotating cubes with images of planet earth mapped onto spheres. First here are two functions to render the spheres (I'm borrowing code from these examples in the MATLAB documentation):
get_earth_sphere1.m
function h = get_earth_sphere1()
% read images of planet earth
earth = imread('landOcean.jpg');
clouds = imread('cloudCombined.jpg');
% unit sphere with 35x35 faces
[X,Y,Z] = sphere(35);
Z = flipud(Z);
a = 1.02;
% render first sphere with earth mapped onto the surface,
% then a second transparent surface with clouds layer
if verLessThan('matlab','8.4.0')
h = zeros(2,1);
else
h = gobjects(2,1);
end
h(1) = surface(X, Y, Z, earth, ...
'FaceColor','texturemap', 'EdgeColor','none');
h(2) = surface(X*a, Y*a, Z*a, clouds, ...
'FaceColor','texturemap', 'EdgeColor','none', ...
'FaceAlpha','texturemap', 'AlphaData',max(clouds,[],3));
end
get_earth_sphere2.m
function h = get_earth_sphere2()
% load topographic data
S = load('topo.mat');
C = S.topo;
cmap = S.topomap1;
n = size(cmap,1);
% convert altitude data and colormap to RGB image
C = (C - min(C(:))) ./ range(C(:)); % scale to [0,1]
C = ind2rgb(round(C*(n-1)+1), cmap); % convert indexed to RGB
% unit sphere with 50x50 faces
[X,Y,Z] = sphere(50);
% render sphere with earth mapped onto the surface
h = surface(X, Y, Z, C, ...
'FaceColor','texturemap', 'EdgeColor','none');
end
The animation script is similar to before (with minor changes), so I'm not gonna repeat it. Here is the result:
(This time I'm using the new graphics system in R2014b)
I have a solution which renders ok by using texture mapping.
The idea is to apply an image to the face and let Matlab take care of everything else. The great advantage is that matlab will take care of all the perspective aspects, and the rendering is pretty good. The small disadvantage is that you can only apply texture to surface objects, and since you want 6 different images, you'll have to manage 6 different surfaces.
The code below shows an example of how to do it:
%% // read faces images
idxFaces = [1 2 3 4 5 6] ;
for iface = idxFaces
imgface{iface} = imread( ['E:\ICONS\number_blue_' num2str(iface) '-150x150.png'] ) ;
end
%% // Define cube properties
cubeLenght = 1 ;
x = linspace(-cubeLenght/2,cubeLenght/2,21) ;
[X,Y] = meshgrid(x,x) ;
Zp = ones(size(X))*cubeLenght/2 ; Zm = Zp-cubeLenght ;
%// draw face surfaces (organised 2 by 2)
hcubeface(1) = surf(X,Y,Zp ,'CData',imgface{1},'FaceColor','texturemap','LineStyle','none') ; hold on
hcubeface(6) = surf(X,Y,Zm ,'CData',imgface{6},'FaceColor','texturemap','LineStyle','none') ;
hcubeface(2) = surf(X,Zp,Y ,'CData',imgface{2},'FaceColor','texturemap','LineStyle','none') ;
hcubeface(5) = surf(X,Zm,Y ,'CData',imgface{5},'FaceColor','texturemap','LineStyle','none') ;
hcubeface(3) = surf(Zp,X,Y ,'CData',imgface{3},'FaceColor','texturemap','LineStyle','none') ;
hcubeface(4) = surf(Zm,X,Y ,'CData',imgface{4},'FaceColor','texturemap','LineStyle','none') ;
axis square
axis off
This will render the following figure:
Sorry for the actual images I took the first one I had available. You will have to generate a nice image of your cube faces, then apply them as shown above.
If you rotate your cube with the figure interactive tool, you will have no problem.
If you want to rotate it programatically, you have 2 options:
if the object is the only one displayed, then the easiest is to only move the point of view (using view or other camera manipulation functions).
if you have multiple objects and you only want to rotate your cube, then you will have to rotate each surface individually. In this case I would suggest writing a helper function which will rotate the 6 faces for you given an angle and a direction.
An even neater way would be to have your cube managed in a class, then add a method to rotate it (internally it will rotate the 6 faces).
Edit: just for fun, the snippet below will animate your dice (not the pretiest way but it shows you an example of the first option above).
%% // animate by changing point of view
azi = linspace(-180,180,100) ;
for iv =1:100
view(azi(iv),azi(iv)+randi(5))
pause(0.01)
end
view(45,45)
Run this block to see your dice dancing ;)
Edit (again)
And this is an example on how to animate the cube object by rotating the object itself
%% // animate by rotating the object
%// this is necessary because the first rotation reset the axes to shading interp
rotate(hcubeface , [0 1 1] , 0.5)
for iface = 1:6 ; set( hcubeface(iface) , 'CData',imgface{iface}, 'FaceColor','texturemap' ) ; end
%// after that, no need to reset the texture map, enjoy as many rotations as you like
for iv =1:360
axerot = rand(1,3) ; % // pick a random axis to rotate around
rotate(hcubeface , axerot , 0.5) %// rotate the 6 faces by 0.5 degrees
pause(0.01)
end
Note that I modified the definition of the cube in order to have all the face surfaces handles in one array. This allow to apply the rotate command to all the surfaces in one go just by sending the handle array as parameter.
Related
I have 2 images ("before" and "after"). I would like to show a final image where the left half is taken from the before image and the right half is taken from the after image.
The images should be separated by a white diagonal line of predefined width (2 or 3 pixels), where the diagonal is specified either by a certain angle or by 2 start and end coordinates. The diagonal should overwrite a part of the final image such that the size is the same as the sources'.
Example:
I know it can be done by looping over all pixels to recombine and create the final image, but is there an efficient way, or better yet, a built-in function that can do this?
Unfortunately I don't believe there is a built-in solution to your problem, but I've developed some code to help you do this but it will unfortunately require the image processing toolbox to play nicely with the code. As mentioned in your comments, you have this already so we should be fine.
The logic behind this is relatively simple. We will assume that your before and after pictures are the same size and also share the same number of channels. The first part is to declare a blank image and we draw a straight line down the middle of a certain thickness. The intricacy behind this is to declare an image that is slightly bigger than the original size of the image. The reason why is because I'm going to draw a line down the middle, then rotate this blank image by a certain angle to achieve the first part of what you desire. I'll be using imrotate to rotate an image by any angle you desire. The first instinct is to declare an image that's the same size as either the originals, draw a line down the middle and rotate it. However, if you do this you'll end up with the line being disconnected and not draw from the top to the bottom of the image. That makes sense because the line being drawn on an angle covers more pixels than if you were to draw this vertically.
Using Pythagorean's theorem, we know that the longest line that can ever be drawn on your image is the diagonal. Therefore we declare an image that is sqrt(rows*rows + cols*cols) in both the rows and columns where rows and cols are the rows and columns of the original image. After, we'll take the ceiling to make sure we've covered as much as possible and we add a bit of extra room to accommodate for the width of the line. We draw a line on this image, rotate it then we'll crop the image after so that it's the same size as the input images. This ensures that the line drawn at whatever angle you wish is fully drawn from top to bottom.
That logic is the hardest part. Once you do that, you declare two logical masks where you use imfill to fill the left side of the mask as one mask and we'll invert the mask to find the other mask. You will also need to use the line image that we created earlier with imrotate to index into the masks and set the values to false so that we ignore these pixels that are on the line.
Finally, you take each mask, index into your image and copy over each portion of the image you desire. You finally use the line image to index into the output and set the values to white.
Without further ado, here's the code:
% Load some example data
load mandrill;
% im is the image before
% im2 is the image after
% Before image is a colour image
im = im2uint8(ind2rgb(X, map));
% After image is a grayscale image
im2 = rgb2gray(im);
im2 = cat(3, im2, im2, im2);
% Declare line image
rows = size(im, 1); cols = size(im, 2);
width = 5;
m = ceil(sqrt(rows*rows + cols*cols + width*width));
ln = false([m m]);
mhalf = floor(m / 2); % Find halfway point width wise and draw the line
ln(:,mhalf - floor(width/2) : mhalf + floor(width/2)) = true;
% Rotate the line image
ang = 20; % 20 degrees
lnrotate = imrotate(ln, ang, 'crop');
% Crop the image so that it's the same dimensions as the originals
mrowstart = mhalf - floor(rows/2);
mcolstart = mhalf - floor(cols/2);
lnfinal = lnrotate(mrowstart : mrowstart + rows - 1, mcolstart : mcolstart + cols - 1);
% Make the masks
mask1 = imfill(lnfinal, [1 1]);
mask2 = ~mask1;
mask1(lnfinal) = false;
mask2(lnfinal) = false;
% Make sure the masks have as many channels as the original
mask1 = repmat(mask1, [1 1 size(im,3)]);
mask2 = repmat(mask2, [1 1 size(im,3)]);
% Do the same for the line
lnfinal = repmat(lnfinal, [1 1 size(im, 3)]);
% Specify output image
out = zeros(size(im), class(im));
out(mask1) = im(mask1);
out(mask2) = im2(mask2);
out(lnfinal) = 255;
% Show the image
figure;
imshow(out);
We get:
If you want the line to go in the other direction, simply make the angle ang negative. In the example script above, I've made the angle 20 degrees counter-clockwise (i.e. positive). To reproduce the example you gave, specify -20 degrees instead. I now get this image:
Here's a solution using polygons:
function q44310306
% Load some image:
I = imread('peppers.png');
B = rgb2gray(I);
lt = I; rt = B;
% Specify the boundaries of the white line:
width = 2; % [px]
offset = 13; % [px]
sz = size(I);
wlb = [floor(sz(2)/2)-offset+[0,width]; ceil(sz(2)/2)+offset-[width,0]];
% [top-left, top-right; bottom-left, bottom-right]
% Configure two polygons:
leftPoly = struct('x',[1 wlb(1,2) wlb(2,2) 1], 'y',[1 1 sz(1) sz(1)]);
rightPoly = struct('x',[sz(2) wlb(1,1) wlb(2,1) sz(2)],'y',[1 1 sz(1) sz(1)]);
% Define a helper grid:
[XX,YY] = meshgrid(1:sz(2),1:sz(1));
rt(inpolygon(XX,YY,leftPoly.x,leftPoly.y)) = intmin('uint8');
lt(repmat(inpolygon(XX,YY,rightPoly.x,rightPoly.y),1,1,3)) = intmin('uint8');
rt(inpolygon(XX,YY,leftPoly.x,leftPoly.y) & ...
inpolygon(XX,YY,rightPoly.x,rightPoly.y)) = intmax('uint8');
final = bsxfun(#plus,lt,rt);
% Plot:
figure(); imshow(final);
The result:
One solution:
im1 = imread('peppers.png');
im2 = repmat(rgb2gray(im1),1,1,3);
imgsplitter(im1,im2,80) %imgsplitter(image1,image2,angle [0-100])
function imgsplitter(im1,im2,p)
s1 = size(im1,1); s2 = size(im1,2);
pix = floor(p*size(im1,2)/100);
val = abs(pix -(s2-pix));
dia = imresize(tril(ones(s1)),[s1 val]);
len = min(abs([0-pix,s2-pix]));
if p>50
ind = [ones(s1,len) fliplr(~dia) zeros(s1,len)];
else
ind = [ones(s1,len) dia zeros(s1,len)];
end
ind = uint8(ind);
imshow(ind.*im1+uint8(~ind).*im2)
hold on
plot([pix,s2-pix],[0,s1],'w','LineWidth',1)
end
OUTPUT:
I need to do the following:
I have a fixed environment with a point in it
At each time step the point moves and I need to take a screenshot of the current status (environment + point)
What I do is
function getPixels(state)
fig = figure('visible','off')
hold all
plot_environment() % calls patch and other stuff
plot(state(1),state(2),'r+')
f = getframe();
data = f.cdata;
close(fig)
The problem is that it is very slow (0.6s which for me is really too much).
I tried using persistent fig and I can go down to 0.4s, still too much.
I read about using print or hardcopy, but it did not help. Even reducing the number of pixels by -r20 (1/5 of my default size) did not speed it up.
Any suggestion? Is there a faster way to get the pixels?
EDIT: ADDITIONAL DETAILS
The state is just a 2d point.
The environment is defined by some fixed known variables used to draw shapes. More specifically I have some points
points = [c11 c12
c21 c22
.....]
used to patch rectangles, circles and triangles. For this I use patch and circles.
So in the end I want to plot everything together and get the resulting pixels. Is there a way to do it without getframe or a way to speed it up?
COMPLETE EXAMPLE
It requires circles.
Launch tic, getPixels([0.1, 0.2]'); toc
It takes 0.43s on average. The getframe command alone takes 0.29s.
function data = getPixels(state)
fig = figure('visible','off');
hold all
c1 = [0.1 0.75;
0.45 0.75];
c2 = [0.45 0.4;
0.45 0.8];
radius = 0.1;
grey = [0.4,0.4,0.4];
% Circles
p = [c1; c2];
circles(p(:,1), p(:,2), radius, 'color', grey, 'edgecolor', grey)
% Rectangles
patch([0.1 0.45 0.45 0.1], [0.65 0.65 0.85 0.85], grey, 'EdgeAlpha', 0)
patch([0.35 0.55 0.55 0.35], [0.4 0.4 0.8 0.8], grey, 'EdgeAlpha', 0)
% Triangle
x = [0.95, 1.0, 1.0];
y = [1.0, 0.95, 1.0];
fill(x, y, 'r')
axis([0 1 0 1])
box on
axis square
% Point
plot(state(1),state(2),'ro','MarkerSize',8,'MarkerFaceColor','r');
f = getframe();
data = f.cdata;
close(fig)
You can reduce the execution time of Matlab's getframe() function by a factor of ten. The trick consists of not creating a figure each time you call the getPixels() function but using an existing one. You may pass the figure handle via the function parameters. And use the Matlab's function clf that clears the current figure window between two calls.
EDIT
Here is an example of the way I play with figure et getframe.
The following performance chart
is given by
%%
clear al
close all
clc
nbSim = 10 %number of getframe calls
tElapsed = zeros(nbSim, 2); %two types of getting frames
%% METHOD 1: figure within loop
for ind_sim = 1:nbSim
fig = figure;
%some graphical elements
hold all
patch(rand(1,4), rand(1,4), rand(1,3), 'EdgeAlpha', 0)
patch(rand(1,4), rand(1,4), rand(1,3), 'EdgeAlpha', 0)
fill(rand(1,3), rand(1,3), 'r')
plot(rand,rand,'ro','MarkerSize',8,'MarkerFaceColor','k');
%some axes properties
axis([0 1 0 1])
box on
axis square
tStart = tic;
f = getframe();
tElapsed(ind_sim,1) = toc(tStart);
data = f.cdata;
close(fig)
end
%% METHOD 2: figure outside loop
fig = figure;
for ind_sim = 1:nbSim
%some graphical elements
hold all
patch(rand(1,4), rand(1,4), rand(1,3), 'EdgeAlpha', 0)
patch(rand(1,4), rand(1,4), rand(1,3), 'EdgeAlpha', 0)
fill(rand(1,3), rand(1,3), 'r')
plot(rand,rand,'ro','MarkerSize',8,'MarkerFaceColor','k');
%some axes properties
axis([0 1 0 1])
box on
axis square
tStart = tic;
f = getframe();
tElapsed(ind_sim,2) = toc(tStart);
data = f.cdata;
clf
end
close(fig)
%% plot results
plot(tElapsed);
set(gca, 'YLim', [0 max(tElapsed(:))+0.1])
xlabel('Number of calls');
ylabel('Execution time');
legend({'within (method 1)';'outside (method 2)'});
title('GetFrame exectution time');
You have to drop the creation of a figure, even if it is declared not visible. This impairs the execution times.
Suppose i would like to draw an image like the following:
Where the pixel values are refined to 0 for black and white for 1.
These line are drawn with specific radius and angles
Now I create a 80 x 160 matrix
texturematrix = zeros(80,160);
then i want to change particular elements to be 1 according to the lines conditions
but how do i make them repeatedly with specific distance apart from each others effectively?
Thanks a lot everyone!
This might not be what you are looking for, but generating such an image could be done by plotting a set of lines, as follows:
% grid sizes
m = 6;
n = 5;
% line length and angle
len = 1;
theta = .1*pi;
[a,b] = meshgrid(1:m,1:n);
x = reshape([a(:),a(:)+len*cos(theta),nan(numel(a),1)]',[],1);
y = reshape([b(:),b(:)+len*sin(theta),nan(numel(b),1)]',[],1);
h = figure();
plot(x,y,'k', 'LineWidth', 2);
But this has nothing to do with a texture matrix. So, we construct a matrix of desired size:
set(gca, 'position',[0 0 1 1], 'units','normalized', 'YTick',[], 'XTick',[]);
frame = frame2im(getframe(h),[0 0 1 1]);
im = imresize(frame,[80 160]);
M = ~(im(2:end,2:end,1)==255);
I am plotting a 7x7 pixel 'image' in MATLAB, using the imagesc command:
imagesc(conf_matrix, [0 1]);
This represents a confusion matrix, between seven different objects. I have a thumbnail picture of each of the seven objects that I would like to use as the axes tick labels. Is there an easy way to do this?
I don't know an easy way. The axes properties XtickLabel which determines the labels, can only be strings.
If you want a not-so-easy way, you could do something in the spirit of the following non-complete (in the sense of a non-complete solution) code, creating one label:
h = imagesc(rand(7,7));
axh = gca;
figh = gcf;
xticks = get(gca,'xtick');
yticks = get(gca,'ytick');
set(gca,'XTickLabel','');
set(gca,'YTickLabel','');
pos = get(axh,'position'); % position of current axes in parent figure
pic = imread('coins.png');
x = pos(1);
y = pos(2);
dlta = (pos(3)-pos(1)) / length(xticks); % square size in units of parant figure
% create image label
lblAx = axes('parent',figh,'position',[x+dlta/4,y-dlta/2,dlta/2,dlta/2]);
imagesc(pic,'parent',lblAx)
axis(lblAx,'off')
One problem is that the label will have the same colormap of the original image.
#Itmar Katz gives a solution very close to what I want to do, which I've marked as 'accepted'. In the meantime, I made this dirty solution using subplots, which I've given here for completeness. It only works up to a certain size input matrix though, and only displays well when the figure is square.
conf_mat = randn(5);
A = imread('peppers.png');
tick_images = {A, A, A, A, A};
n = length(conf_mat) + 1;
% plotting axis labels at left and top
for i = 1:(n-1)
subplot(n, n, i + 1);
imshow(tick_images{i});
subplot(n, n, i * n + 1);
imshow(tick_images{i});
end
% generating logical array for where the confusion matrix should be
idx = 1:(n*n);
idx(1:n) = 0;
idx(mod(idx, n)==1) = 0;
% plotting the confusion matrix
subplot(n, n, find(idx~=0));
imshow(conf_mat);
axis image
colormap(gray)
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)