How do I create an image matrix with a line drawn in it in MATLAB? - matlab

I want to plot a line from one well-defined point to another and then turn it into an image matrix to use a Gaussian filter on it for smoothing. For this I use the functions line and getframe to plot a line and capture the figure window in an image, but getframe is very slow and not very reliable. I noticed that it does not capture anything when the computer is locked and I got an out of memory error after 170 executions.
My questions are:
Is there a substitute to getframe that I can use?
Is there a way to create the image matrix and draw the line directly in it?
Here is a minimal code sample:
figure1=line([30 35] ,[200 60]);
F= getframe;
hsize=40; sigma=20;
h = fspecial('gaussian',hsize,sigma);
filteredImg = imfilter(double(F.cdata), h,256);
imshow(uint8(filteredImg));
[update]
High-Performance Mark's idea with linspace looks very promising, but how do I access the matrix coordinates calculated with linspace? I tried the following code, but it does not work as I think it should. I assume it is a very simple and basic MATLAB thing, but I just can not wrap my head around it:
matrix=zeros(200,60);
diagonal=round([linspace(30,200,numSteps); linspace(35,60,numSteps)]);
matrix(diagonal(1,:), diagonal(2,:))=1;
imshow(matrix);

Here's one example of drawing a line directly into a matrix. First, we'll create a matrix of zeros for an empty image:
mat = zeros(250, 250, 'uint8'); % A 250-by-250 matrix of type uint8
Then, let's say we want to draw a line running from (30, 35) to (200, 60). We'll first compute how many pixels long the line will have to be:
x = [30 200]; % x coordinates (running along matrix columns)
y = [35 60]; % y coordinates (running along matrix rows)
nPoints = max(abs(diff(x)), abs(diff(y)))+1; % Number of points in line
Next, we compute row and column indices for the line pixels using linspace, convert them from subscripted indices to linear indices using sub2ind, then use them to modify mat:
rIndex = round(linspace(y(1), y(2), nPoints)); % Row indices
cIndex = round(linspace(x(1), x(2), nPoints)); % Column indices
index = sub2ind(size(mat), rIndex, cIndex); % Linear indices
mat(index) = 255; % Set the line pixels to the max value of 255 for uint8 types
You can then visualize the line and the filtered version with the following:
subplot(1, 2, 1);
image(mat); % Show original line image
colormap(gray); % Change colormap
title('Line');
subplot(1, 2, 2);
h = fspecial('gaussian', 20, 10); % Create filter
filteredImg = imfilter(mat, h); % Filter image
image(filteredImg); % Show filtered line image
title('Filtered line');

If you have Computer Vision System toolbox there is a ShapeInserter object available. This can be used to draw lines, circles, rectangles and polygons on the image.
mat = zeros(250,250,'uint8');
shapeInserter = vision.ShapeInserter('Shape', 'Lines', 'BorderColor', 'White');
y = step(shapeInserter, mat, int32([30 60 180 210]));
imshow(y);
http://www.mathworks.com/help/vision/ref/vision.shapeinserterclass.html

You can check my answer here. It is robust way to achieve what you are asking for. Advantage of my approach is that it doesn't need additional parameters to control density of the line drawn.

Something like this:
[linspace(30,200,numSteps); linspace(35,60,numSteps)]
Does that work for you ?
Mark

Related

Get pixel values in RGB images using PixelList in MATLAB

I am trying to get pixel intensity values from regions of interest in RGB images.
I segmented the image and saved the regions of interest (ROI) using regionprops 'PixelList' in MATLAB, as shown below:
In this example I am using "onion.png" image built in MATLAB. (But in reality I have hundreds of images, and each of them have several ROIs hence why I'm saving the ROIs separately.)
%SEGMENTATION PROGRAM:
a=imread('C:\Program Files\MATLAB\MATLAB Production Server\R2015a\toolbox\images\imdata\onion.png');warning('off', 'Images:initSize:adjustingMag');
figure; imshow(a,[]);
nrows = size(a,1);ncols = size(a,2);
zr=ones(nrows,ncols); %matrix of ones
r=a(:,:,1);g=a(:,:,2);b=a(:,:,3); %get RGB values
rr=double(r);gg=double(g);bb=double(b);% convert to double to avoid uint8 sums
bgd=(rr+bb)./(2*gg); %calculation RGB ratio of background
zr1=bgd>1.15; %matrix containing background as 1 and ROI as 0
% remake binary image for holes command which requires white object to fill % (this step is not relevant for this example, but it is for my data)
zr2=zr1<0.5;
zr3=imfill(zr2, 'holes');
figure;imshow(zr3); pause;
roi=regionprops(zr3,'Centroid','PixelList','Area');
ar=[roi.Area];
% find sort order , largest first
[as, ia]=sort(ar(1,:),'descend');
for w=1:length(roi); xy(w,:)=roi(w).Centroid;end
% use sort index to put cenrtoid list in same order
xy1=xy(ia,:);
%and pixel id list
for w=1:length(roi)
roi2(w).PixelList=roi(ia(w)).PixelList;
end
%extract centriod positions as two colums
%SAVE PIXEL LIST FOR EACH ROI IN A SEPARATE FILE
for ww=1:w;
k=roi2(ww).PixelList;
save('onion_PL','k');
end
How do I use this pixel list to get the intensity values in the original image? More specifically, I need to get the ratio of pixels in Green channel over Red ("grr=rdivide(gg,rr);"), but only in the region of interest labeled with PixelList. Here's my code so far:
%PL is the PixelList output we got above.
a=imread('C:\Program Files\MATLAB\MATLAB Production Server\R2015a\toolbox\images\imdata\onion.png');warning('off', 'Images:initSize:adjustingMag');
PL=dir(['*PL.mat']); %load file PixelList files. "dir" is a variable with directory path containing the pixelist files. In this example, we saved "onion_PL.mat"
for m=1:length(PL);
load(PL(m).name);
ex=[]; %empty matrix to hold the extracted values
for mm=1:length(k);
%INSERT ANSWER HERE
end
This next bit is wrong because it's based on the entire image ("a"), but it contains the calculations that I would like to perform in the ROIs
figure; imshow(a,[]);
pause;
nrows = size(a,1);ncols = size(a,2);
zr=ones(nrows,ncols); %matrix of ones
r=a(:,:,1);g=a(:,:,2);b=a(:,:,3); %get RGB values
rr=double(r);gg=double(g);bb=double(b);% convert to double to avoid uint8 sums
grr=rdivide(gg,rr);
I am brand new to MATLAB, so my code is not the greatest... Any suggestions will be greatly appreciated. Thank you in advance!
The loop you are looking for seems simple:
grr = zeros(nrows, ncols); % Initialize grr with zeros.
for mm = 1:length(k)
x = k(mm, 1); % Get the X (column) coordinate.
y = k(mm, 2); % Get the Y (row) coordinate.
grr(y, x) = gg(y, x) / rr(y, x);
end
A more efficient solution is using sub2ind for converting the x,y coordinates to linear indices:
% Convert k to linear indices.
kInd = sub2ind([nrows, ncols], k(:,2), k(:,1));
% Update only the indices in the PixelList.
grr(kInd) = rdivide(gg(kInd), rr(kInd));
In your given code sample there are 5 PixelLists.
I don't know how do you want to "arrange" the result.
In my code sample, I am saving the 5 results to 5 mat files.
Here is an executable code sample:
close all
%SEGMENTATION PROGRAM:
a=imread('onion.png');warning('off', 'Images:initSize:adjustingMag');
figure; imshow(a,[]);
nrows = size(a,1);ncols = size(a,2);
zr=ones(nrows,ncols); %matrix of ones
r=a(:,:,1);g=a(:,:,2);b=a(:,:,3); %get RGB values
rr=double(r);gg=double(g);bb=double(b);% convert to double to avoid uint8 sums
bgd=(rr+bb)./(2*gg); %calculation RGB ratio of background
zr1=bgd>1.15; %matrix containing background as 1 and ROI as 0
% remake binary image for holes command which requires white object to fill % (this step is not relevant for this example, but it is for my data)
zr2=zr1<0.5;
zr3=imfill(zr2, 'holes');
figure;imshow(zr3); %pause;
roi=regionprops(zr3,'Centroid','PixelList','Area');
ar=[roi.Area];
% find sort order , largest first
[as, ia]=sort(ar(1,:),'descend');
for w=1:length(roi); xy(w,:)=roi(w).Centroid;end
% use sort index to put cenrtoid list in same order
xy1=xy(ia,:);
%and pixel id list
for w=1:length(roi)
roi2(w).PixelList=roi(ia(w)).PixelList;
end
%extract centroid positions as two columns
%SAVE PIXEL LIST FOR EACH ROI IN A SEPARATE FILE
for ww=1:w
k=roi2(ww).PixelList;
%save('onion_PL', 'k');
save(['onion', num2str(ww), '_PL'], 'k'); % Store in multiple files - onion1_PL.mat, onion2_PL.mat, ... onion5_PL.mat
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
clear % Use clear for testing - the variables are going to be read from the mat file.
%PL is the PixelList output we got above.
a=imread('onion.png');warning('off', 'Images:initSize:adjustingMag');
nrows = size(a,1);ncols = size(a,2);
zr=ones(nrows,ncols); %matrix of ones
r=a(:,:,1);g=a(:,:,2);b=a(:,:,3); %get RGB values
rr=double(r);gg=double(g);bb=double(b);% convert to double to avoid uint8 sums
grr=rdivide(gg,rr);
PL=dir('*PL.mat'); %load file PixelList files. "dir" is a variable with directory path containing the pixelist files. In this example, we saved "onion_PL.mat"
for m = 1:length(PL)
load(PL(m).name);
ex=[]; %empty matrix to hold the extracted values
%for mm=1:length(k)
%INSERT ANSWER HERE
grr = zeros(nrows, ncols); % Initialize grr with zeros.
for mm = 1:length(k)
x = k(mm, 1); % Get the X (column) coordinate.
y = k(mm, 2); % Get the Y (row) coordinate.
grr(y, x) = gg(y, x) / rr(y, x);
end
% Instead of using a loop, it's more efficient to use sub2ind
if false
% Convert k to linear indices.
kInd = sub2ind([nrows, ncols], k(:,2), k(:,1));
% Update only the indices in the PixelList.
grr(kInd) = rdivide(gg(kInd), rr(kInd));
end
figure;imshow(grr);title(['grr of m=', num2str(m)]) % Show grr for testing.
save(['grr', num2str(m)], 'grr'); % Save grr for testing.
imwrite(imadjust(grr, stretchlim(grr)), ['grr', num2str(m), '.png']); % Store grr as image for testing
end
First two grr matrices as images (used for testing):
grr1.png:
grr2.png:

How can I measure the length of line segments in image mask in MATLAB?

I have a binary image (BW_roi.mat), displayed below. And I wanted to measure the length of every line segment.
I tried the solution given in this link hough-transform. But it does not work out for me. It just measured the length of some lines as shown below.
I tried the other code
clc; clear all; close all;
load BW_ROI.mat
boundaries = bwboundaries(BW_roi_nodot);
patchno=1 %Let select patch 1
b = boundaries{patchno}; % Extract N by 2 matrix of (x,y) locations.
x = b(:, 1);
y = b(:, 2);
It though gives me points (x,y) that make up these polygons. But how can I get the line segment length of specific patch?
I'd propose using convexhull in combination with the algorithm here to reduce the number of vertices in the detected polygons. Seems to work pretty well. I'll leave it to you to use the adjacency matrix produced by bwboundaries to throw away duplicates.
load BW_ROI.mat
[B,L,N,A] = bwboundaries(BW_roi_nodot);
for k = 1:N
boundary = fliplr(B{k});
% take a convex hull of the bounded polygon
idxn = convhull(boundary, 'Simplify', true);
% use this algorithm to further simplify the polygon
% https://ww2.mathworks.cn/matlabcentral/fileexchange/41986-ramer-douglas-peucker-algorithm
point_reduced = DouglasPeucker(boundary(idxn,:), 5);
% make the shape, get the side lengths
shp = polyshape(point_reduced);
vertex_deltas = diff([shp.Vertices; shp.Vertices(1,:)], 1);
edge_lengths = sqrt(sum(vertex_deltas.^2, 2));
% plot animation
imshow(BW_roi_nodot), hold on
plot(shp), drawnow, pause(0.5)
end

How to draw a line between two coordinates of an image permanently in Matlab? [duplicate]

This question already has answers here:
MATLAB: Drawing a line over a black and white image
(5 answers)
Closed 4 years ago.
I have a set of points that I want to connect sequentially. Suppose the points are (A1,A2,A3,...A9); I want to connect A1 to A2, A2 to A3 and so on and finally connect A9 to A1.
All I need is to know a function that would help me connect A1 to A2, I could do the rest using for loops.
I know connecting two points is a question that has been asked here several times before but I couldn't find the answer I required. Several of the solutions suggest using "plot" and "line" but these functions overlay the results on the image and don't actually make any changes to the original image.
I did try them out and managed to save the resulting figure using the "saveas" and "print" functions but the image doesn't get saved in the proper format and there are a lot of problems using the parameters for these functions. Besides, I don't really want to save the image, it's just an unnecessary overhead I was willing to add if I could get the desired image with the lines.
I've also tried "imline" to draw lines but it seems to be interactive.
This particular question reflects my problem perfectly but when I ran the code snippets given as solutions, all of them gave a set of dots in the resulting image.
I tried the above mentioned codes in the link with this image that I found here.
A dotted line was an output for all three code snippets in the link above.
For example, I ran the first code like this:
I = imread('berries_copy.png');
grayImage=rgb2gray(I);
img =false(size(grayImage,1), size(grayImage,2));
I wrote the above piece of code just to get a black image for the following operations:
x = [500 470]; % x coordinates
y = [300 310]; % y coordinates
nPoints = max(abs(diff(x)), abs(diff(y)))+1; % Number of points in line
rIndex = round(linspace(y(1), y(2), nPoints)); % Row indices
cIndex = round(linspace(x(1), x(2), nPoints)); % Column indices
index = sub2ind(size(img), rIndex, cIndex); % Linear indices
img(index) = 255; % Set the line points to white
imshow(img); % Display the image
This is the resulting image for the above code as well as the other two, which as you can see, is just a few points on a black background which isn't the desired output.
I changed the code and used the "plot" function for the same to get this output which is what I want. Is there anyway I can change the dotted output into a solid line?
Or if could anyone suggest a simple function or a method that would draw a line from A1 to A2 and would actually make a change in the input image, I'd be grateful. (I really hope this is just me being a novice rather than Matlab not having a simple function to draw a line in an image.)
P.S. I don't have the Computer Vision toolbox and if possible, I'd like to find a solution that doesn't involve it.
Your first problem is that you are creating a blank image the same size as the image you load with this line:
img =false(size(grayImage,1), size(grayImage,2));
When you add the line to it, you get a black image with a white line on it, as expected.
Your second problem is that you are trying to apply a solution for grayscale intensity images to an RGB (Truecolor) image, which requires you to modify the data at the given indices for all three color planes (red, green, and blue). Here's how you can modify the grayscale solution from my other answer:
img = imread('berries_copy.png'); % Load image
[R, C, D] = size(img); % Get dimension sizes, D should be 3
x = [500 470]; % x coordinates
y = [300 310]; % y coordinates
nPoints = max(abs(diff(x)), abs(diff(y)))+1; % Number of points in line
rIndex = round(linspace(y(1), y(2), nPoints)); % Row indices
cIndex = round(linspace(x(1), x(2), nPoints)); % Column indices
index = sub2ind([R C], rIndex, cIndex); % Linear indices
img(index) = 255; % Modify red plane
img(index+R*C) = 255; % Modify green plane
img(index+2*R*C) = 255; % Modify blue plane
imshow(img); % Display image
imwrite(img, 'berries_line.png'); % Save image, if desired
And the resulting image (note the white line above the berry in the bottom right corner):
You can use the Bresenham Algorithm. Of course it has been implemented and you can find it here: Bresenham optimized for Matlab. This algorithm selects the pixels approximating a line.
A simple example, using your variable name could be:
I = rgb2gray(imread('peppers.png'));
A1 = [1 1];
A2 = [40 40];
[x y] = bresenham(A1(1),A1(2),A2(1),A2(2));
ind = sub2ind(size(I),x,y);
I(ind) = 255;
imshow(I)
You can use imshow to display a image and then use plot to plot lines and save the figure. Check the below code:
I = imread('peppers.png') ;
imshow(I)
hold on
[m,n,p] = size(I) ;
%% Get random points A1, A2,..A10
N = 9 ;
x = (n-1)*rand(1,N)+1 ;
y = (m-1)*rand(1,N)+1 ;
P = [x; y]; % coordinates / points
c = mean(P,2); % mean/ central point
d = P-c ; % vectors connecting the central point and the given points
th = atan2(d(2,:),d(1,:)); % angle above x axis
[th, idx] = sort(th); % sorting the angles
P = P(:,idx); % sorting the given points
P = [P P(:,1)]; % add the first at the end to close the polygon
plot( P(1,:), P(2,:), 'k');
saveas(gcf,'image.png')

Matlab: 1D array to RGB triplets with colormap

I'm trying to draw a set of rectangles, each with a fill color representing some value between 0 and 1. Ideally, I would like to use any standard colormap.
Note that the rectangles are not placed in a nice grid, so using imagesc, surf, or similar seems unpractical. Also, the scatter function does not seem to allow me to assign a custom marker shape. Hence, I'm stuck to plotting a bunch of Rectangles in a for-loop and assigning a FillColor by hand.
What's the most efficient way to compute RGB triplets from the scalar values? I've been unable to find a function along the lines of [r,g,b] = val2rgb(value,colormap). Right now, I've built a function which computes 'jet' values, after inspecting rgbplot(jet). This seems a bit silly. I could, of course, obtain values from an arbitrary colormap by interpolation, but this would be slow for large datasets.
So, what would an efficient [r,g,b] = val2rgb(value,colormap) look like?
You have another way to handle it: Draw your rectangles using patch or fill specifying the color scale value, C, as the third parameter. Then you can add and adjust the colorbar:
x = [1,3,3,1,1];
y = [1,1,2,2,1];
figure
for ii = 1:10
patch(x + 4 * rand(1), y + 2 * rand(1), rand(1), 'EdgeColor', 'none')
end
colorbar
With this output:
I think erfan's patch solution is much more elegant and flexible than my rectangle approach.
Anyway, for those who seek to convert scalars to RGB triplets, I'll add my final thoughts on the issue. My approach to the problem was wrong: colors should be drawn from the closest match in the colormap without interpolation. The solution becomes trivial; I've added some code for those who stumble upon this issue in the future.
% generate some data
x = randn(1,1000);
% pick a range of values that should map to full color scale
c_range = [-1 1];
% pick a colormap
colormap('jet');
% get colormap data
cmap = colormap;
% get the number of rows in the colormap
cmap_size = size(cmap,1);
% translate x values to colormap indices
x_index = ceil( (x - c_range(1)) .* cmap_size ./ (c_range(2) - c_range(1)) );
% limit indices to array bounds
x_index = max(x_index,1);
x_index = min(x_index,cmap_size);
% read rgb values from colormap
x_rgb = cmap(x_index,:);
% plot rgb breakdown of x values; this should fall onto rgbplot(colormap)
hold on;
plot(x,x_rgb(:,1),'ro');
plot(x,x_rgb(:,2),'go');
plot(x,x_rgb(:,3),'bo');
axis([c_range 0 1]);
xlabel('Value');
ylabel('RGB component');
With the following result:

how can i use lsqcurvefit for image registration?

I have two 3D images, i need to register these two images using "lsqcurvefit". I know that I can use "imregister" but I want to use my own registration using "lsqcurvefit" in Matlab. My images are are following Gaussian distribution. it is not documented well that how should I provide it, anyone can help me in detail?
image registration is a repeated process of maping source image to target image using i.e affine. i want to use intensity base registration, and i use all voxels of my image. therefore, i need to fit these two images as much as possible.
Thanks
Here's an example of how to do point-wise image registration using lsqcurvefit. Basically you make a function that takes a set of points and an Affine matrix (we're just going to use the translate and rotate parts but you can use skew and magnify if desired) and returns a new set of points. There's probably a built-in function for this already but it's only two lines so it's easy to write. That function is:
function TformPts = TransformPoints(StartCoordinates, TransformMatrix)
TformPts = StartCoordinates*TransformMatrix;
Here's a script that generates some points, rotates and translates them by a random angle and vector, then uses the TransformPoints function as the input for lsqcurvefit to fit the needed transformation matrix for the registration. Then it's just a matrix multiplication to generate the registered set of points. If we did this all right the red circles (original data) will line up with the black stars (shifted then registered points) very well when the code below is run.
% 20 random points in x and y between 0 and 100
% row of ones pads out third dimension
pointsList = [100*rand(2, 20); ones(1, 20)];
rotateTheta = pi*rand(1); % add rotation, in radians
translateVector = 10*rand(1,2); % add translation, up to 10 units here
% 2D transformation matrix
% last row pads out third dimension
inputTransMatrix = [cos(rotateTheta), -sin(rotateTheta), translateVector(1);
sin(rotateTheta), cos(rotateTheta), translateVector(2);
0 0 1];
% Transform starting points by this matrix to make an array of shifted
% points.
% For point-wise registration, pointsList represents points from one image,
% shiftedPoints points from the other image
shiftedPoints = inputTransMatrix*pointsList;
% Add some random noise
% Remove this line if you want the registration to be exact
shiftedPoints = shiftedPoints + rand(size(shiftedPoints, 1), size(shiftedPoints, 2));
% Plot starting sets of points
figure(1)
plot(pointsList(1,:), pointsList(2,:), 'ro');
hold on
plot(shiftedPoints(1,:), shiftedPoints(2,:), 'bx');
hold off
% Fitting routine
% Make some initial, random guesses
initialFitTheta = pi*rand(1);
initialFitTranslate = [2, 2];
guessTransMatrix = [cos(initialFitTheta), -sin(initialFitTheta), initialFitTranslate(1);
sin(initialFitTheta), cos(initialFitTheta), initialFitTranslate(2);
0 0 1];
% fit = lsqcurvefit(#fcn, initialGuess, shiftedPoints, referencePoints)
fitTransMatrix = lsqcurvefit(#TransformPoints, guessTransMatrix, pointsList, shiftedPoints);
% Un-shift second set of points by fit values
fitShiftPoints = fitTransMatrix\shiftedPoints;
% Plot it up
figure(1)
hold on
plot(fitShiftPoints(1,:), fitShiftPoints(2,:), 'k*');
hold off
% Display start transformation and result fit
disp(inputTransMatrix)
disp(fitTransMatrix)