Plot/Draw on image?

30 views (last 30 days)
Qingyang
Qingyang on 17 Jul 2012
Edited: DGM on 22 Mar 2023
Hi, I would like to draw points on an image from an array (in white), and I've what I've tried is drawing the image on a figure, overlaying a plot of the points, and storing the figure (using getframe, cdata, etc.) into the new image.
However, when I do this, the size of new image is about a third smaller in both dimensions than the original. Is there any way to prevent this or an alternative for drawing points on an image?
Thanks!

Answers (1)

DGM
DGM on 22 Mar 2023
Edited: DGM on 22 Mar 2023
Avoid figure capture of images whenever possible. It's the same as taking a screenshot. The image will likely be resized and padded uncontrollably.
If you want to edit an image, work directly on the image and consider the figure a display-only tool.
% an image
inpict = imread('peppers.png');
sz = size(inpict);
% a set of random x,y points in image coordinates
% for sake of example, these are not integer-valued
% though it's assumed they do not lie outside the image area
npoints = 50;
x = rand(npoints,1)*(sz(2)-1) + 1;
y = rand(npoints,1)*(sz(1)-1) + 1;
% a marker image
% this should have odd geometry
marker = [0 0 0 1 0 0 0;
0 0 0 1 0 0 0;
0 0 0 1 0 0 0;
1 1 1 1 1 1 1;
0 0 0 1 0 0 0;
0 0 0 1 0 0 0;
0 0 0 1 0 0 0];
% create mask with one pixel per data point
idx = sub2ind(sz(1:2),round(y),round(x));
mask = zeros(sz(1:2));
mask(idx) = 1;
imshow(mask)
% convolve point mask with marker image
% clamp values in case markers overlap
mask = min(conv2(mask,marker,'same'),1);
imshow(mask)
% perform multiplicative composition directly
% since we're living in 2012, we'd be using bsxfun()
markercolor = [0.8 0.3 1];
fgc = bsxfun(@times,mask,permute(markercolor,[1 3 2]));
bgc = bsxfun(@times,1-mask,im2double(inpict));
outpict = bsxfun(@plus,fgc,bgc);
outpict = im2uint8(outpict);
% do the same thing without bsxfun() in R2016b or newer
%outpict = mask.*permute(markercolor,[1 3 2]) ...
% + (1-mask).*im2double(inpict);
%outpict = im2uint8(outpict);
% add markers to original image using imoverlay()
% this wouldn't be an option until R2016a
%outpict = imoverlay(inpict,mask,markercolor);
% add markers to original image using MIMT replacepixels()
% while this wasn't available until 2014, it should still work back to ~R2007a
% it's more convenient than direct multiplication and more flexible than imoverlay
%outpict = replacepixels(markercolor,inpict,mask);
imshow(outpict)
I included four different ways to do the final composition, though only the first one would have been an option at the time the question was asked. If running a legacy version (e.g. R2012a) in the present, only the first and last examples would be options.
Note that the display interpolation causes some of the single-pixel marker lines to disappear when viewed. This is exactly why you don't save displayed images. The markers are in the image. What's displayed is a crude facsimile that exists only for visualization.
  1 Comment
DGM
DGM on 22 Mar 2023
Edited: DGM on 22 Mar 2023
Let's say you wanted something more complicated for markers. What if you wanted fancy arrows or a crosshair or maybe you have a CUR or ICO file that you want to use. Here's one example using a transparent PNG for a marker.
% an image
inpict = imread('peppers.png');
sz = size(inpict);
% a set of random x,y points in image coordinates
% for sake of example, these are not integer-valued
% though it's assumed they do not lie outside the image area
npoints = 5;
x = rand(npoints,1)*(sz(2)-1) + 1;
y = rand(npoints,1)*(sz(1)-1) + 1;
% a marker image from a transparent PNG
[marker,~,alpha] = imread('fancyalpaca.png'); % RGB+A
marker = cat(3,marker,alpha); % RGBA
marker = im2double(marker); % must be unit-scale float
% create mask with one pixel per data point
idx = sub2ind(sz(1:2),round(y),round(x));
dotmask = zeros(sz(1:2));
dotmask(idx) = 1;
% convolve point mask with marker image
% clamp values in case markers overlap
FG = zeros([sz(1:2) size(marker,3)]);
for c = 1:size(marker,3)
FG(:,:,c) = min(conv2(dotmask,marker(:,:,c),'same'),1);
end
% perform multiplicative composition directly
% since we're living in 2012, we'd be using bsxfun()
outpict = bsxfun(@times,FG(:,:,4),FG(:,:,1:3)) ...
+ bsxfun(@times,1-FG(:,:,4),im2double(inpict));
outpict = im2uint8(outpict);
% do the same thing without bsxfun() in R2016b or newer
%outpict = FG(:,:,4).*FG(:,:,1:3) ...
% + (1-FG(:,:,4)).*im2double(inpict);
%outpict = im2uint8(outpict);
% imoverlay() cannot do this
% MIMT replacepixels handles RGBA and mixed class images directly
%outpict = replacepixels(FG,inpict,1);
imshow(outpict)
Perhaps placing the markers by convolution isn't the ideal method here (note what happens when markers overlap), but I think it's worthwhile to show how the prior example could be extended this far.
While MIMT replacepixels makes the composition simple, using bsxfun() still works fine so long as the input classes are appropriate and you take care to make sure that the marker image has the expected number of channels (RGB+A).
If the marker image is indexed color (e.g. if it's a CUR file), convert it to RGB first using ind2rgb().

Sign in to comment.

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!