MATLAB Add Mouse Click Coordinate to Matrix? - arrays

I am creating an image processing project in matlab. I want to get 5 coordinates from image with mouse and save them to a matrix. When I run project, I click only once but the matrix is taking same coordinates. How can I fix it ?
function ImageClickCallback ( objectHandle , eventData )
array = 3:2;
for i = 1:3
axesHandle = get(objectHandle,'Parent');
coordinates = get(axesHandle,'CurrentPoint');
coordinates = coordinates(1,1:2);
array(i,1) = coordinates(1);
array(i,2) = coordinates(2);
end
disp(array);
For example when I click a point on image, I am taking this result. I can't select another points.
99.3806 37.1915
99.3806 37.1915
99.3806 37.1915

A much safer alternative would be to use ginput, with which you can select a variable number of points and store their coordinates easily. You don't need a loop; the coordinates are stored once all the points are selected.
Here is a simple example:
clear
clc
close all
A = imread('peppers.png');
imshow(A,[]);
hold on
%// Number of points
N = 5;
array = zeros(N,2);
%// Use a for loop and plot 1 point at a time.
for k = 1:N
[array(k,1),array(k,2)] = ginput(1);
%// Display the points
scatter(array(k,1),array(k,2),60,'k','filled')
end
Sample output for array:
123.0000 87.0000
95.0000 206.0000
256.0000 85.0000
236.0000 253.0000
395.0000 117.0000
And the image with the points displayed:
Therefore, you could would look like this:
function ImageClickCallback ( objectHandle , eventData )
array = zeros(3,2);
[array(:,1),array(:,2)] = ginput(3);

Related

Plot cell array without loop

I have the following code:
SimRun = 0
Count = 0
for b = 1:0.5:3000
.
.
.
.
Count = Count + 1;
ArrayT(Count) = Time;
ArrayTgo(Count) = tgo;
ArrayY(Count) = Y;
ArrayYD(Count) = YD;
end
SimRun = SimRun + 1;
MAT_ArrayT{SimRun,:} = ArrayT;
MAT_ArrayTgo{SimRun,:} = ArrayTgo;
MAT_ArrayY{SimRun,:} = ArrayY;
MAT_ArrayYD{SimRun,:} = ArrayYD;
As you can see, I have 2 for loops. From the inner loop I receive a vector and from the outer loop I receive in the end a cell array where each cell is a vector.
Now, I had like to plot the cell array to plot basically around 6000 lines and I did it as follows:
for i = 1:SimRun
figure(1)
hold on
plot(MAT_ArrayT{i,:},MAT_ArrayY{i,:})
figure(2)
hold on
plot(MAT_ArrayT{i,:},MAT_ArrayYD{i,:})
end
However this solution takes pretty much time to draw all the lines.
Is there any better solution to store "lines" and plot all of them in one hit at the end?
Thank you.
The plot command takes a matrix and plots each column as a separate line. Assuming you have column vectors in your cell arrays, and they're all the same length, you can do this:
x = [MAT_ArrayT{:}];
y = [MAT_ArrayY{:}];
plot(x,y)
Even better would be to store those vectors in a numeric matrix to start with, so you don't need to make the extra copy.

MATLAB - copying specific values from one 3d array to another

I need to update image 1 with rgb values from image 2 for specific coordinates.
I have two 2d matrices (im1Cart_toupdate 2x114056 and im2EstCart_tocopyfrom also 2x114056). These contain the ordered x-y pairs for which I want to copy rgb values from image 2 to image 1.
i.e. there are 114,056 pixels where I want to copy colours across.
im1 (440x1370x3) and im2 (240x320x3) are the image arrays. Note im2 is going to be stretched, so some pixels from im2 will appear more than once in im2EstCart_tocopyfrom.
I need an efficient way of doing this, as even with the above image sizes my current implementation is very slow. I had thought that there may be some approach using sub2ind - but am not sure how to do this with 3d arrays.
Here's my current code. It's the for loop that's killing me!
%Create a matrix of all pixel coordinates in im1 (homogenised form)
[im1gridx im1gridy]=meshgrid(1:im1width,1:im1height);
im1Cart = [im1gridx(:) im1gridy(:)]';
im1Hom = [im1Cart; ones(1,numel(im1gridy))];
%transform pixel positions with homography (HEst is a matrix built
%elsewhere) to find where they are in the coordinates of image 2
im2EstHom = HEst*im1Hom;
im2EstCart = im2EstHom(1:2,:)./repmat(im2EstHom(3,:),2,1);
im2EstCart = round(im2EstCart);
%check if the the transformed position is within the boundary of image 2
validCoords = im2EstCart(1,:)>0 & im2EstCart(2,:)>0 & im2EstCart(1,:)<=im2width & im2EstCart(2,:)<=im2height;
im1Cart_toupdate=im1Cart(:,validCoords);
im2EstCart_tocopyfrom=im2EstCart(:,validCoords);
%copy colour from image 2 to image 1 - currently pixel by pixel
%but CAN THIS BE VECTORISED?
for i=1:size(im1Cart_toupdate,2)
im1y=im1Cart_toupdate(1,i);
im1x=im1Cart_toupdate(2,i);
im2y=im2EstCart_tocopyfrom(1,i);
im2x=im2EstCart_tocopyfrom(2,i);
im1(im1y,im1x,:) = im2(im2y,im2x,:);
drawnow
end
Many thanks for any advice!
Approach #1
This would be one vectorized approach using linear indexing with bsxfun -
[m2,n2,r2] = size(im2);
RHS_idx1 = (im2EstCart_tocopyfrom(2,:)-1)*m2 + im2EstCart_tocopyfrom(1,:)
RHS_allidx = bsxfun(#plus,RHS_idx1(:),(0:r2-1)*m2*n2)
[m1,n1,r1] = size(im1);
LHS_idx1 = (im1Cart_toupdate(2,:)-1)*m1 + im1Cart_toupdate(1,:)
LHS_allidx = bsxfun(#plus,LHS_idx1(:),(0:r1-1)*m1*n1)
im1(LHS_allidx) = im2(RHS_allidx)
Approach #2
Here's another approach that reshapes the input 3D array to a 2D array after merging the first two dimensions, then using linear indexing for extracting and setting values and finally reshaping back to its original 3D size, like so -
[m2,n2,r2] = size(im2)
RHS_idx1 = (im2EstCart_tocopyfrom(2,:)-1)*m2 + im2EstCart_tocopyfrom(1,:)
im2r = reshape(im2,[],r2)
[m1,n1,r1] = size(im1)
LHS_idx1 = (im1Cart_toupdate(2,:)-1)*m1 + im1Cart_toupdate(1,:)
im1r = reshape(im1,[],r1)
im1r(LHS_idx1,:) = im2r(RHS_idx1,:)
im1 = reshape(im1r,size(im1));

Connecting random points in MATLAB without intersecting lines

I need help with solving this problem. I have randomly generated points (example on Picture #1) and I want to connect them with lines (example on Picture #2). Lines can't be intersected and after connection, the connected points should look like an irregular area.
%Generating random points
xn = randi([3 7],1,10);
yn = randi([3 6],1,10);
%Generated points
xn = [6,3,7,7,6,6,6,4,6,3];
yn = [5,3,4,3,3,6,5,4,6,3];
Picture #1:
Result should be like this:
Picture #2:
Any idea how to solve this?
I suppose for the general case it can be very difficult to come up with a solution. But, assuming your points are scattered "nicely" there is quite a simple solution.
If you sort your points according to the angle above the x axis of the vector connecting the point and the center of the point cloud then:
P = [xn;yn]; %// group the points as columns in a matrix
c = mean(P,2); %// center point relative to which you compute the angles
d = bsxfun(#minus, P, c ); %// vectors connecting the central point and the dots
th = atan2(d(2,:),d(1,:)); %// angle above x axis
[st si] = sort(th);
sP = P(:,si); %// sorting the points
And that's about it. To plot the result:
sP = [sP sP(:,1)]; %// add the first point again to close the polygon
figure;plot( sP(1,:), sP(2,:), 'x-');axis([0 10 0 10]);
This algorithm will fail if several points has the same angle w.r.t the center of the point cloud.
An example with 20 random points:
P = rand(2,50);
You could adapt the code from another answer I gave for generating random simple polygons of an arbitrary number of sides. The difference here is you already have your set of points chosen and thus implicitly the number of sides you want (i.e. the same as the number of unique points). Here's what the code would look like:
xn = [6,3,7,7,6,6,6,4,6,3]; % Sample x points
yn = [5,3,4,3,3,6,5,4,6,3]; % Sample y points
[~, index] = unique([xn.' yn.'], 'rows', 'stable'); % Get the unique pairs of points
x = xn(index).';
y = yn(index).';
numSides = numel(index);
dt = DelaunayTri(x, y);
boundaryEdges = freeBoundary(dt);
numEdges = size(boundaryEdges, 1);
while numEdges ~= numSides
if numEdges > numSides
triIndex = vertexAttachments(dt, boundaryEdges(:,1));
triIndex = triIndex(randperm(numel(triIndex)));
keep = (cellfun('size', triIndex, 2) ~= 1);
end
if (numEdges < numSides) || all(keep)
triIndex = edgeAttachments(dt, boundaryEdges);
triIndex = triIndex(randperm(numel(triIndex)));
triPoints = dt([triIndex{:}], :);
keep = all(ismember(triPoints, boundaryEdges(:,1)), 2);
end
if all(keep)
warning('Couldn''t achieve desired number of sides!');
break
end
triPoints = dt.Triangulation;
triPoints(triIndex{find(~keep, 1)}, :) = [];
dt = TriRep(triPoints, x, y);
boundaryEdges = freeBoundary(dt);
numEdges = size(boundaryEdges, 1);
end
boundaryEdges = [boundaryEdges(:,1); boundaryEdges(1,1)];
x = dt.X(boundaryEdges, 1);
y = dt.X(boundaryEdges, 2);
And here's the resulting polygon:
patch(x,y,'w');
hold on;
plot(x,y,'r*');
axis([0 10 0 10]);
Two things to note:
Some sets of points (like the ones you chose here) will not have a unique solution. Notice how my code connected the top 4 points in a slightly different way than you did.
I made use of the TriRep and DelaunayTri classes, both of which may be removed in future MATLAB releases in favor of the delaunayTriangulation class.

exchange circle pixels in an image in matlab

All I want is to add a circle into an image of a radius and centre of my choice, however I DO NOT WANT to use plot COMMAND, I want a new output array which has my chosen image and circle on it.
here is my function
function [ out_image ] = draw_circle( in_image,x0,y0,r,value)
where in_image is the input image
x0 y0 he centre of circle and r the radius
value the intensity of image
this is my attempt which does not work
ang=0:pi/100:2*pi
x=r*cos(ang)+x0;
y=r*sin(ang)+y0;
cir=[x,y];
[j,k]=size(cir);
for cir=1:j
for cir=1:k
if cir(i,j)==0
out_image(i,j)=in_image(i,j);
end
end
end
imshow(out_image)
end
I'm pretty sure there are better ways, but following your approach, you might use the following code:
out_image = in_image;
cir = round([r*cos(ang)+x0;r*sin(ang)+y0]);
for i = 1:size(cir,2)
out_image(cir(1,i),cir(2,i),:) = 255;
end
Your for loops did not work the way you wrote them. Also, you should use round to get indices, and not floating point values. However, the circle is quite small (only 1 pixel), which might not be enough for large pictures. See example below. Also, you might need a finer discritization for large pictures, i.e. a larger number of elements in ang.
In case you want a larger linewidth, you can use
lw = 3; % Enter linewidth here
for i = 1:size(cir,2)
out_image(cir(1,i)+(-lw:lw),cir(2,i)+(-lw:lw),:) = 255;
end
EDIT: In case you don't see it. The circle is close to the top left corner.
The complete code for the function is:
function [ out_image ] = draw_circle( in_image,x0,y0,r,value,lw)
out_image = in_image;
ang = 0:pi/100:2*pi;
cir = round([r*cos(ang)+x0;r*sin(ang)+y0]);
for i = 1:size(cir,2)
out_image(cir(1,i)+(-lw:lw),cir(2,i)+(-lw:lw),:) = value;
end
imshow(out_image)
You can call it from command line with
draw_circle(in_image,100,200,50,0,3);
Note, I took the unused value as grayscale value.

Display a Gaussian pyramid stored in a cell array in a single figure

I'm working on a Gaussian Pyramid code for matlab. Basically it loads an image, creates a cell array and fills it with different levels of the gaussian pyramid.
I want to show the content of my cell array filled with images in one single figure,
so you can see the gaussian pyramid effect. Meaning the original image is at full size and the rest are downsampled by 2 each. And all that in one figure.
I'm quite the amateur when it comes to Matlab so I don't really know how to do that.
I already tried it somewhat with subplots but failed.
Thanks in advance.
I used a loop to add zeros at the top of all images then merged them
Sample cell,
im = imread('peppers.png');
for i = 1 : 5
I{i} = im(1 : 2*i : end, 1 : 2*i : end,:);
end
The code, I being your cell,
m = size(I{1}, 1);
newI = I{1};
for i = 2 : numel(I)
[q,p,~] = size(I{i});
I{i} = cat(1,repmat(zeros(1, p, 3),[m - q , 1]),I{i});
newI = cat(2,newI,I{i});
end
imshow(newI)
For 2D images use : I{i} = cat(1,repmat(zeros(1 , p),[m - q , 1]),I{i});
How about:
subplot(numel(YourCell), 1, 1), imshow(YourCell{1});
for k=2:5
subplot(1,numel(YourCell),k), imshow(YourCell{k})
xlim([1 size(YourCell{1},1)]);
ylim([1 size(YourCell{1},2)]);
end
Result (with dummy data):
Edit:
You can play with the arrangement of your tiles by calculating the position of the next one. Here is a quick and dirty example, you can surely do a better job:
Side by side:
border=5;
MergedImage=ones(size(YourCell{1},1), 2.5*size(YourCell{1},2));
MergedImage(1:size(YourCell{1},1), 1:size(YourCell{1},2))=YourCell{1};
Pos=[1, size(YourCell{1},1)+border];
for k=1:(numel(YourCell)-1)
MergedImage(Pos(1):Pos(1)+size(YourCell{k+1}, 1)-1, Pos(2):Pos(2)+size(YourCell{k+1}, 2)-1)=YourCell{k+1};
Pos=[Pos(1), Pos(2)+size(YourCell{k+1}, 2)+border];
end
imshow(MergedImage);
Or a tighter arrangement:
border=5;
MergedImage=ones(size(YourCell{1},1), 2*size(YourCell{1},2));
MergedImage(1:size(YourCell{1},1), 1:size(YourCell{1},2))=YourCell{1};
Pos=[1, size(YourCell{1},1)+border];
for k=1:(numel(YourCell)-1)
MergedImage(Pos(1):Pos(1)+size(YourCell{k+1}, 1)-1, Pos(2):Pos(2)+size(YourCell{k+1}, 2)-1)=YourCell{k+1};
if mod(k,2) == 0
Pos=[Pos(1)+size(YourCell{k+1}, 1)+border, Pos(2)];
else
Pos=[Pos(1), Pos(2)+size(YourCell{k+1}, 2)+border];
end
end
imshow(MergedImage);

Resources