Plotting over displayed image in GNU Octave - image

I am developing some routines in Octave and need to display an image, then plot a curve on top which will hopefully overlay some image features.
However, I cannot work out how to match the origin/scale of the image and the plot. For example, given a 1024x1024 pixel image I can do:
a=imread('image.png');
x=linspace(1,1024,100);
y=x;
imshow(a);
hold on;
plot(x,y);
But the line is not scaled to the image and does not start at a corner. (I know that the image and plot should have origins in different corners). When I examine the graphic coordinates from the cursor position, the image is clearly not at the origin, so I guess this is the basis of the problem.

Use image() instead of imshow() in this case
a = imread ('image.png');
x = linspace (1, 1024, 100);
y = x;
image (a);
hold on
plot (x, y);
axis square

You can plot functions over images this way:
Create an image called stuff.jpg like this, any size is possible but I made mine roughly 6x6 pixels so I could test:
You can plot functions over other functions this way:
octave> x = 0:1:5;
octave> plot(x, (3/2).^x, "linewidth", 2, "color", "blue");
octave> hold on
octave> plot(x, 2.^x, "linewidth", 2, "color", "red");
octave> plot(x, factorial(x), "linewidth", 2, "color", "green");
octave> plot(x, x.^3, "linewidth", 2, "color", "black");
octave>
For me it shows this:
Found that here, it has a walkthrough:
http://ericleschinski.com/c/algorithm_complexity_big_o_notation/
Which plots my power level given my age. It's already over nine thousand.

The problem with image is that it puts (0,0) (rather (min_x,min_y)) at upper-left while we usually expect (0,0) to be at bottom-left.
Also it only uses max and min values of the x and y vectors so doing y(end:-1:1) doesn't work.
im = imread('file.png'); %read the file
image([xmin xmax],[ymin ymax],im(end:-1:1,:,:)); %put the image on the screen upside down
axis('xy'); % flip the image by putting (0,0) at bottom left. Image now right side up
axis('square'); if you want to aspect ratio of the image to be 1:1
hold on;
plot([xmin xmax],[ymin ymax]) % this should draw a diagonal from bottom left to upper right.
% plot whatever you want to overlay

Related

How to get actual pixel indices from MATLAB function ginput?

I am using the MATLAB function ginput to label my image data for further process. Here is my code:
file_name = "test.jpg";
% Read the image
img = imread(file_name);
% Get the image dimension
imgInfo = imfinfo(file_name);
width = imgInfo.Width;
height = imgInfo.Height;
% Using ginput function to label the image
figure(1);
imshow(img);
hold on;
[x, y] = ginput(4); % Manually label 4 points
scatter(x, y, 100, 'ro', 'filled'); % Plot the marked points on img
hold off;
My Problem:
I found that the output x and yare not integers, so they are not representing the pixel indices.
Sometimes, these two conditions max(x) > width and max(y) > height are satisfied. It seems to suggest that the 4 points I marked using ginput are outside the image (but actually it is not).
I am aware of this issue is related to Image Coordinate System setting, but I am still not sure how to convert x and y obtained from ginput function to the actual pixel indices?
Thanks.
The code below shows a 2x2 image, enlarges the axes so we can see it, then turns on the axes ticks and labels. What this does is allow you to see the coordinate system used to render images in an axes object.
imshow([255,0;0,255])
set(gca,'position',[0.2,0.2,0.6,0.6])
axis on
The coordinates returned by ginput match these coordinates.
In short, what you need to do is simply round the coordinates returned by ginput to get indices into the image:
[x, y] = ginput(4); % Manually label 4 points
x = round(x);
y = round(y);

Image Repetition from Binary to Cartesian

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:

How to do bar plot in the same scale and dimension as an image?

I have an image, then I am projecting it on one of it's edges by some aggregation functions like mean. I am getting 1D digitized data this way. How to plot it exactly below source image in exactly the same horizontal or vertical scale?
Sample code:
% loading image
image = imread('..\..\FruitSample_small.png');
%computing gradients
dx=double(image(1:end-1,2:end,:))-double(image(1:end-1,1:end-1,:));
dy=double(image(2:end,1:end-1,:))-double(image(1:end-1,1:end-1,:));
% computing total magnitude
a=sqrt(sum(dx.^2,3)+sum(dy.^2,3));
% projection on bottom
h = mean(a,1);
% drawing
figure;
subplot(2,1,1);
imshow(image);
subplot(2,1,2);
bar(h);
axis image;
I want bar plot here to be of the same width as image above. Also I want histogram to spread exactly to the same horizontal coordinate, as image does.
The challenge here is that "axis image" enforces an aspect ratio on the image axis, which allows the display size of the image to vary from its set dimensions. My suggestion is to forget the auto-sizing capabilities that come from normalized units, and adjust the sizes based on pixel sizing. If you set the size of the image axis to exactly the size of the image in pixels, then it will have the correct aspect ratio, and you can use the same size for the bar plot.
load clown; % use a built-in demo image. Note the image is now in X
%computing gradients
dx=double(X(1:end-1,2:end,:))-double(X(1:end-1,1:end-1,:));
dy=double(X(2:end,1:end-1,:))-double(X(1:end-1,1:end-1,:));
% computing total magnitude
a=sqrt(sum(dx.^2,3)+sum(dy.^2,3));
% projection on bottom
h = mean(a,1);
% drawing
figure;
subplot(2,1,1);
imagesc(X);
size_x = size(X,2);
size_y = size(X,1);
set(gcf, 'Units', 'pixels')
fp=get(gcf, 'Position');
set(gca, 'Units', 'pixels');
set(gca, 'Position', [(fp(3)-size_x) / 2, (fp(4)-size_y) / 2 + 100, ...
size_x, size_y]);
subplot(2,1,2);
bar(h);
set(gca, 'Units', 'pixels');
set(gca, 'Position', [(fp(3)-size_x) / 2, 100, size_x, 100]);
set(gca, 'XLim', [1 size_x]);
I'm computing the offsets automatically from the figure size, to center the results on the figure. This is not resizeable. You'd have to rerun the set('Position') stuff to readjust the locations if you resize the figure itself. If you need it to be dynamically resizeable, you'd have to add a handler for a resize event, and adjust the sizes on every resize.

How can I "plot" an image on top of another image with a different colormap?

I've got two images, one 100x100 that I want to plot in grayscale and one 20x20 that I want to plot using another colormap. The latter should be superimposed on the former.
This is my current attempt:
A = randn(100);
B = ones(20);
imagesc(A);
colormap(gray);
hold on;
imagesc(B);
colormap(jet);
There are a couple of problems with this:
I can't change the offset of the smaller image. (They always share the upper-left pixel.)
They have the same colormap. (The second colormap changes the color of all pixels.)
The pixel values are normalised over the composite image, so that the first image changes if the second image introduces new extreme values. The scalings for the two images should be separate.
How can I fix this?
I want an effect similar to this, except that my coloured overlay is rectangular and not wibbly:
Just change it so that you pass in a full and proper color matrix for A (i.e. 100x100x3 matrix), rather than letting it decide:
A = rand(100); % Using rand not randn because image doesn't like numbers > 1
A = repmat(A, [1, 1, 3]);
B = rand(20); % Changed to rand to illustrate effect of colormap
imagesc(A);
hold on;
Bimg = imagesc(B);
colormap jet;
To set the position of B's image within its parent axes, you can use its XData and YData properties, which are both set to [1 20] when this code has completed. The first number specifies the coordinate of the leftmost/uppermost point in the image, and the second number the coordinate of the rightmost/lowest point in the image. It will stretch the image if it doesn't match the original size.
Example:
xpos = get(Bimg, 'XData');
xpos = xpos + 20; % shift right a bit
set(Bimg, 'XData', xpos);

How to lock image dimensions in MATLAB

So I have this matrix in MATLAB, 200 deep x 600 wide. It represents an image that is 2cm deep x 6cm wide. How can I plot this image so that it is locked into proper dimensions, i.e. 2cm x 6cm? If I use the image or imagesc commands it stretches it all out of shape and shows it the wrong size. Is there a way to lock it into showing an image where the x and y axes are proportional?
Second question, I need to then set this image into a 640x480 frame (20 pixel black margin on left and right, 280 pixel black margin on bottom). Is there a way to do this?
To keep aspect ratio, you can use axis equal or axis image commands.
Quoting the documentation:
axis equal sets the aspect ratio so that the data units are the same in every direction. The aspect ratio of the x-, y-, and z-axis is adjusted automatically according to the range of data units in the x, y, and z directions.
axis image is the same as axis equal except that the plot box fits tightly around the data`
For second question:
third_dimension_size=1; %# for b&w images, use 3 for rgb
framed_image=squeeze(zeros(640,480,third_dimension_size));
framed_image(20:20+600-1,140:140+200-1)= my_600_200_image;
imagesc(framed_image'); axis image;
set(gca,'DataAspectRatio',[1 1 1])
Second question:
new_image = zeros(480,640);
new_image(20:(200+20-1),20:(600+20-1)) = old_image;
As an alternative to the other answers, you might want:
set(gca, 'Units', 'centimeters', 'Position', [1 1 6 2])
Make sure you do this after plotting the image to get the other axis properties correct.
For the second question, take care with the number of colour channels:
new_image = zeros(480,640, size(old_image));
new_image(20:(200+20-1),20:(600+20-1),:) = old_image;

Resources