I have the following image:
Is there a way to connect the dots (draw a line between them) using MATLAB? I tried plot by passing it the image, but didn't work.
Thanks.
UPDATE
The way I'm expecting to connect the dots is roughly as shown through the red line in the image sample below:
There are a number of ways you can do this, for instance as follows:
img=im2double(imread('SWal5.png'));
m = bwmorph(~img,'shrink',Inf);
[ix iy] = find(m);
tri = delaunay(iy,ix);
image(img)
hold on
triplot(tri,iy,ix,'g')
set(gca,'Ydir','reverse')
axis square off
If instead you want something akin to a plot, then you can try this after running the find step:
[ix ii]=sort(ix);
iy = iy(ii);
imshow(img)
hold on
plot(iy,ix,'k')
set(gca,'Ydir','reverse')
To plot a trail through a number of points, you could define an adjacency matrix and use gplot to display.
Here's a snippet to show the idea, although you'll note that with this rather simple code the output is a bit messy. It depends how clean a line you want and if crossovers are allowed - there are probably better ways of creating the adjacency matrix.
This assumes you've extracted the position of your points into a matrix "data" which is size n x 2 (in this case, I just took 200 points out of your image to test).
Basically, the nearest points are found with knnsearch (requires Statistics toolbox), and then the adjacency matrix is filled in by picking a starting point and determining the nearest neighbour not yet used. This results in every point being connected to at maximum two other points, providing that the number of points found with knnsearch is sufficient that you never back yourself into a corner where all the nearest points (100 in this case) have already been used.
datal = length(data);
marked = ones(datal,1);
m = 100; % starting point - can be varied
marked(m)=0; % starting point marked as 'used'
adj = zeros(datal);
IDX = knnsearch(data, data,'K',100);
for n = 1:datal;
x = find(marked(IDX(m,:))==1,1,'first'); % nearest unused neighbour
adj(m,IDX(m,x))=1; % two points marked as connected in adj
marked(IDX(m,x))=0; % point marked as used
m = IDX(m,x);
end
gplot(adj, data); hold on; plot(data(:,1),data(:,2),'rx');
Related
I want to make a histogram of every column of a matrix, but I want the bins to be logarithmic and also normalized. And after I create the histogram I want to make a fit on it without showing the bars. This is what I have tried:
y=histogram(x,'Normalized','probability');
This gives me the histogram normalized, but I don't know how to make the bins logarithmic.
There are two different ways of creating a logarithmic histogram:
Compute the histogram of the logarithm of the data. This is probably the nicest approach, as you let the software decide on how many bins to create, etc. The x-axis now doesn't match your data, it matches the log of your data. For fitting a function, this is likely beneficial, but for display it could be confusing. Here I change the tick mark labels to show the actual value, keeping the tick marks themselves at their original values:
y = histogram(log(x),'Normalization','probability');
h = gca;
h.XTickLabels = exp(h.XTick);
Determine your own bin edges, on a logarithmic scale. Here you need to determine how many bins you need, depending on the number of samples and the distribution of samples.
b = 2.^(1:0.25:3);
y = histogram(x,b,'Normalization','probability');
set(gca,'XTick',b) % This just puts the tick marks in between bars so you can see what we did.
Method 1 lets MATLAB determine number of bins and bin edges automatically depending on the input data. Hence it is not suitable for creating multiple matching histograms. For that case, use method 2. The in edges can be obtained more simply this way:
N = 10; % number of bins
start = min(x); % first bin edge
stop = max(x); % last bin edge
b = 2.^linspace(log2(start),log2(stop),N+1);
I think the correct syntax would be Normalization.
To make it logarithmic, you have to change the axes object.
For example :
ha = axes;
y = histogram( x,'Normalization','probability' );
ha.YScale = 'log';
I have a 3D point cloud organised in a 3-colums format [x,y,z], with additional properties of each point from column 4 onwards. Note that the distance of all points is random. I am trying to implement a moving filter similar to blockproc in order to create a 2D matrix of size y,x that "flattens" the data along z and averages a given property of the point cloud in a volume. The volume should be a kernel of a fixed size along x and y, let's call them dx and dy within which values of any z will be taken. In addition, the volume should be able to slide, so for instance the moving steps (lets call them xStep and yStep) are not necessarily equal to dx and dy (though dx=dy and xStep=yStep).
So far, Matlab functions I found are:
blockproc: Has the ability to implement a sliding kernel but works on matrices
accumarray: Works on discrete points but no sliding kernel
Here is a cartoon of what I am trying to do conceptually . The function should capture the red points and apply a function (e.g. mean, stdev) to calculate the value of the red cell. Then move by xStep and re-apply the function.
Any idea of how I could achieve that? I've been stuck on that for a while. I wrote a function that indices the points at each iteration, but my datasets are rather large (>10^7 points) so this approach is very time consuming. This thread provides a MWE using accumarray, without the sliding kernel:
table = [ 20*rand(1000,1) 30*rand(1000,1) 40*rand(1000,1)]; % random data
x_partition = 0:2:20; % partition of x axis
y_partition = 0:5:30; % partition of y axis
L = size(table,1);
M = length(x_partition);
N = length(y_partition);
[~, ii] = max(repmat(table(:,1),1,M) <= repmat(x_partition,L,1),[],2);
[~, jj] = max(repmat(table(:,2),1,N) <= repmat(y_partition,L,1),[],2);
% Calculate the sum
result_sum = accumarray([ii jj], table(:,3), [M N], #sum, NaN);
Any input would be greatly appreciated, thank you!
PS: First post here, sorry if there is any bad formatting
I have 2 images im1 and im2 shown below. Theim2 picture is the same as im1, but the only difference between them is the colors. im1 has RGB ranges of (0-255, 0-255, 0-255) for each color channel while im2 has RGB ranges of (201-255, 126-255, 140-255). My exercise is to reverse the added effects so I can restore im2 to im1 as closely as I can. I have 2 thoughts in mind. The first is to match their histograms so they both have the same colors. I tried it using histeq but it restores only a portion of the image. Is there any way to change im2's histogram to be exactly the same as im1? The second approach was just to copy each pixel value from im1 to im2 but this is wrong since it doesn't restore the original image state. Are there any suggestions to restore the image?
#sepdek below pretty much suggested the method that #NKN alluded to, but I will provide another approach. One more alternative I can suggest is to perform a colour correction based on a least mean squared solution. What this alludes to is that we can assume that transforming a pixel from im2 to im1 requires a linear combination of weights. In other words, given a RGB pixel where its red, green and blue components are shaped into a 3 x 1 vector from the corrupted image (im2), there exists some linear transformation to get its equivalent pixel in the clean image (im1). In other words, we have this relationship:
[R_im1] [R_im2]
[G_im1] = A * [G_im2]
[B_im1] [B_im2]
Y = A * X
A in this case would be a 3 x 3 matrix. This is essentially performing a matrix multiplication to get your output corrected pixel. The input RGB pixel from im2 would be X and the output RGB pixel from im1 would be Y. We can extend this to as many pixels as we want, where pairs of pixels from im1 and im2 would establish columns along Y and X. In general, this would further extend X and Y to 3 x N matrices. To find the matrix A, you would find the least mean squared error solution. I won't get into it, but to find the optimal matrix of A, this requires finding the pseudo-inverse. In our case here, A would thus equal to:
Once you find this matrix A, you would need to take each pixel in your image, shape it so that it becomes a 3 x 1 vector, then multiply A with this vector like the approach above. One thing you're probably asking yourself is what kinds of pixels do I need to grab from both images to make the above approach work? One guideline you must adhere to is that you need to make sure that you're sampling from the same spatial location between the two images. As such, if we were to grab a pixel at... say... row 4, column 9, you need to make sure that both pixels from im1 and im2 come from this same row and same column, and they are placed in the same corresponding columns in X and Y.
Another small caveat with this approach is that you need to be sure that you sample a lot of pixels in the image to get a good solution, and you also need to make sure the spread of your sampling is over the entire image. If we localize the sampling to be within a small area, then you're not getting a good enough distribution of the colours and so the output will not look very nice. It's up to you on how many pixels you choose for the problem, but from experience, you get to a point where the output starts to plateau and you don't see any difference. For demonstration purposes, I chose 2000 pixels in random positions throughout the image.
As such, this is what the code would look like. I use randperm to generate a random permutation from 1 to M where M is the total number of pixels in the image. These generate linear indices so that we can sample from the images and construct our matrices. We then apply the above equation to find A, then take each pixel and apply a matrix multiplication with A to get the output. Without further ado:
close all;
clear all;
im1 = imread('http://i.stack.imgur.com/GtgHU.jpg');
im2 = imread('http://i.stack.imgur.com/wHW50.jpg');
rng(123); %// Set seed for reproducibility
num_colours = 2000;
ind = randperm(numel(im1) / size(im1,3), num_colours);
%// Grab colours from original image
red_out = im1(:,:,1);
green_out = im1(:,:,2);
blue_out = im1(:,:,3);
%// Grab colours from corrupted image
red_in = im2(:,:,1);
green_in = im2(:,:,2);
blue_in = im2(:,:,3);
%// Create 3 x N matrices
X = double([red_in(ind); green_in(ind); blue_in(ind)]);
Y = double([red_out(ind); green_out(ind); blue_out(ind)]);
%// Find A
A = Y*(X.')/(X*X.');
%// Cast im2 to double for precision
im2_double = double(im2);
%// Apply matrix multiplication
out = cast(reshape((A*reshape(permute(im2_double, [3 1 2]), 3, [])).', ...
[size(im2_double,1) size(im2_double,2), 3]), class(im2));
Let's go through this code slowly. I am reading your images directly from StackOverflow. After, I use rng to set the seed so that you can reproduce the same results on your end. Setting the seed is useful because it allows you to reproduce the random pixel selection that I did. We generate those linear indices, then create our 3 x N matrices for both im1 and im2. Finding A is exactly how I described, but you're probably not used to the rdivide / / operator. rdivide finds the inverse on the right side of the operator, then multiplies it with whatever is on the left side. This is a more efficient way of doing the calculation, rather than calculating the inverse of the right side separately, then multiplying with the left when you're done. In fact, MATLAB will give you a warning stating to avoid calculating the inverse separately and that you should the divide operators instead. Next, I cast im2 to double to ensure precision as A will most likely be floating point valued, then go through the multiplication of each pixel with A to compute the result. That last line of code looks pretty intimidating, but if you want to figure out how I derived this, I used this to create vintage style photos which also require a matrix multiplication much like this approach and you can read up about it here: How do I create vintage images in MATLAB? . out stores our final image. After running this code and showing what out looks like, this is what we get:
Now, the output looks completely scrambled, but the colour distribution more or less mimics what the input original image looks like. I have a few explanations on why this is the case:
There is quantization noise. If you take a look at the final image, there is various white spotting all over. This is probably due to the quantization error that is introduced when compressing your image. Pixels that should map to the same colours between the images will have slight variations due to quantization which gives us that spotting
There is more than one colour from im2 that maps to im1. If there is more than one colour from im2 that maps to im1, it is impossible for a linear multiplication with the matrix A to be able to generate more than one kind of colour for im1 given a single pixel in im2. Instead, the least mean-squared solution will try and generate a colour that minimizes the error and give you the best colour possible instead. This is probably way the face and other fine details of the image are obscured because of this exact reason.
The image is noisy. Your im2 is not completely clean. I can also see various spots of salt and pepper noise across all of the channels. One bad thing about this method is that if your image is subject to noise, then this method will not faithfully reconstruct the original image properly. Your image can only be corrupted by a wrong mapping of colours. Should there be any other type of image noise introduced, then this method will definitely not work as you are trying to reconstruct the original image based on a noisy image. There are pixels in the noisy image that were never present in the original image, so you'll have no luck getting it back to the way it was before!
If you want to take a look at the histograms of each channel between the original image and the output image, this is what we get:
The code I used to generate the above figure was:
names = {'Red', 'Green', 'Blue'};
figure;
for idx = 1 : 3
subplot(3,2,2*idx - 1);
imhist(im1(:,:,idx));
title([names{idx} ': Image 1']);
end
for idx = 1 : 3
subplot(3,2,2*idx);
imhist(out(:,:,idx));
title([names{idx} ': Output']);
end
The left side shows the red, green and blue histograms for the original image while the right side shows the same histograms for the reconstructed image. You can see that the general shape more or less mimics the original image, but there are some spikes throughout - most likely attributed to quantization noise and the non-unique mapping between colours of both images.
All in all, this is the best that I could do, but I think that was the whole point of the exercise.... to show that it isn't possible.
For more information on how to perform colour correction, check out Richard Alan Peters' II Digital Image Processing slides on colour correction. This was what I started with, and the derivation of how to calculate A can be found in his slides. Perhaps you can use some of what he talks about in your future work.
Good luck!
It seems that you need a scaling function to map the values of im2 to the values of im1.
This is fairly simple and you could write a scaling function to have it available for any such case.
A basic scaling mapping would work as follows:
out_value = min_output + (in_value - min_input) * (outrange / inrange)
given that there is an input value in_value that is within a range of values inrange=max_input-min_input and the mapping results an output value out_value within a range outrange=max_output-min_output. We also need to take into account the minimum input and output range bounds (min_input and min_output) to have a correct mapping.
See for example the following code for a scaling function:
%
% scale the values of a matrix using a set of limits
% possible ways to use:
% y = scale( x, in_range, out_range) --> ex. y = scale( x, [8 230], [0 255])
% y = scale( x, out_range) --> ex. y = scale( x, [0 1])
%
function y = scale( x, varargin );
if nargin<2,
error([upper(mfilename),':: Syntax: y=',mfilename,'(x[,in_range],out_range)']);
end;
if nargin==2,
inrange=[min(x(:)) max(x(:))]; % compute the limits of the input variable
outrange=varargin{1}; % get the output limits from the arguments
else
inrange=varargin{1}; % get the input limits from the arguments
outrange=varargin{2}; % get the output limits from the arguments
end;
if diff(inrange)==0, % row or column vector matrix or scalar
% just do a clipping...
if x>=outrange(2),
y=outrange(2);
elseif x<=outrange(1),
y=outrange(1);
else
y=x;
end;
else
% actually scale the data
% using: out = min_output + (x-min_input) * (outrange / inrange)
y = outrange(1) + (x-inrange(1))*abs(diff(outrange))/abs(diff(inrange));
end;
This function gets a matrix of values and scales them to a desired range.
In your case it could be used as following (variable img is the scaled im2):
for i=1:size(im1,3), % for each of the input/output image channels
output_range = [min(min(im1(:,:,i))) max(max(im1(:,:,i)))];
img(:,:,i) = scale( im2(:,:,i), output_range);
end;
This way im2 is scaled to the range of values of im1 one channel at a time. Output variable img should be the desired one.
I'm trying to write a script so that one can put his hand on the screen, click a few points with ginput, and have matlab generate an outline of the persons hand using splines. However, I'm quite unsure how you can have splines connect points that result from your clicks, as they of course are described by some sort of parametrization. How can you use the spline command built into matlab when the points aren't supposed to be connected 'from left to right'?
The code I have so far is not much, it just makes a box and lets you click some points
FigHandle = figure('Position', [15,15, 1500, 1500]);
rectangle('Position',[0,0,40,40])
daspect([1,1,1])
[x,y] = ginput;
So I suppose my question is really what to do with x and y so that you can spline them in such a way that they are connected 'chronologically'. (And, in the end, connecting the last one to the first one)
look into function cscvn
curve = cscvn(points)
returns a parametric variational, or natural, cubic spline curve (in ppform) passing through the given sequence points(:j), j = 1:end.
An excellent example here:
http://www.mathworks.com/help/curvefit/examples/constructing-spline-curves-in-2d-and-3d.html
I've found an alternative for using the cscvn function.
Using a semi-arclength parametrisation, I can create the spline from the arrays x and y as follows:
diffx = diff(x);
diffy = diff(y);
t = zeros(1,length(x)-1);
for n = 1:length(x)-1
t(n+1) = t(n) + sqrt(diffx(n).^2+diffy(n).^2);
end
tj = linspace(t(1),t(end),300);
xj = interp1(t,x,tj,'spline');
yj = interp1(t,y,tj,'spline');
plot(x,y,'b.',xj,yj,'r-')
This creates pretty decent outlines.
What this does is use the fact that a curve in the plane can be approximated by connecting a finite number of points on the curve using line segments to create a polygonal path. Using this we can parametrize the points (x,y) in terms of t. As we only have a few points to create t from, we create more by adding linearly spaced points in between. Using the function interp1, we then find the intermediate values of x and y that correspond to these linearly spaced t, ti.
Here is an example of how to do it using linear interpolation: Interpolating trajectory from unsorted array of 2D points where order matters. This should get you to the same result as plot(x,y).
The idea in that post is to loop through each consecutive pair of points and interpolate between just those points. You might be able to adapt this to work with splines, you need to give it 4 points each time though which could cause problems since they could double back.
To connect the start and end though just do this before interpolating:
x(end+1) = x(1);
y(end+1) = y(1);
1. What I WANT to do:
(i) Use input n to generate an n*n cartesian grid
[x y] = meshgrid(linspace(-1,1,n));
(ii) Generate polar coordinates
[theta r] = cart2pol(x,y);
(iii) Evaluate a function in cylindrical coordinates
z = f(theta,r);
(iv) Plot the result using (say) pcolor (or surf, or anything)
pcolor(x,y,abs(z).^2) %The function is complex, a Laguerre-Gauss to be exact.
2. What I CAN do... The only way I can get the plots to work is by starting with my polar parameters and working back to cartesian from there:
(i) Define parameters
r=linspace(0,1,n); theta=linspace(0,2*pi,n);
(ii) Create both grids and evaluate f
[theta r]=meshgrid(theta,r);
[x y]=pol2cart(theta,r);
z=f(theta,r);
(iii) Plot
pcolor(x,y,abs(z).^2)
The PROBLEM is that now my grid is circular, and I would like to evaluate the function everywhere ON A RECTANGULAR grid (because my analysis depends on having square pixel arrays). The reiterate, using method 2 above, I get a circular plot circumscribed in a square; imagine a black circle with white along the edges... but I WANT to evaluate the function in this "white" region. HOWEVER, using method 1 does NOT work -- the function is all messed up when I plot (Just google Laguerre-Gauss modes to see what the plots should look like).
I want to be able to start with a rect grid and assign every point a polar coordinate, instead of start with polar coordinates and assign them all cartesian points.
I've been messing with this on an off for a long time, and I can't figure out how to get around this seemingly simple issue.
Edit 1
It seems that the problem lies in how the coordinate matrices are generated. Below I've posted screen-shots of a simple 3by3 example illustrating how approach 1 and approach 2 generate different numbers.
How to make these numbers compatible?
I have no reputation points so I cannot upload the images directly... links below show the 3by3 example... see comments for links to actual images of the Laguerre-Gauss plots I'm trying to make...
apply cart2pol
apply pol2cart
Edit 2
Currently, the result of approach (1.) gives wrong results, as shown here:
desired approach, wrong result
The second approach gives the right images, unfortunately it's only a circle and not the entire square. It is shown here:
implemented approach, limited result
3D plots of both approaches are shown here - only the colorful part of the top figure is correct.
Edit 3
Here is a screenshot of the function f which is being used above. Note, that it asks for more input parameters than just r,theta. Typical values are:
w0 = 0.5;
p = 0;
l = 5;
The function C gives a normalization and L are Laguerre polynomials. Both of these functions have been thoroughly tested and yield the expected results.
Edit 4
Here is enough code to run my example z=U(0,5,r,phi,w0)+U(0,-5,r,phi,w0); explicitly. The plot itself is given by pcolor(x,y,abs(z).^2).
Note that the Lpl() function is inserted as a comment. This will have to be saved as its own m-file for the U function to run properly.
%% Laguerre-Gauss Modes U = U(p,l,r,phi,w0)
% Source: OAM theory paper section 2.A eqn 1.
% Assuming POLAR coordinates and evaluating AT beam waist.
% -- That is, z=0 for w(z)=w0(sqrt(1+z/zR))
% ---- ie, w(0) = w0
% Assuming z=0 also renders the Gouy phase arctan(z/zR) irrelevant.
% Note: Rayleigh Range zR is not explicitly defined because z=0 --> it is irrelevant too.
% Since zR is the only wavelength dependent term, wavelength also doesn't
% matter.
function out = U(p,l,r,phi,w0)
%Function handles for clarity
e = #(x) exp(x);
C = #(p,l) sqrt((2*factorial(p))/(pi*factorial(p+abs(l))));
L = #(p,l,z) Lpl(p,l,z);
%% Lpl() FUNCTION
% function out = Lpl(p,l,z)
%
% l=abs(l);
% LL=0;
% for mm=1:p+1
% m=mm-1;
% L=LL;
% LL= L+((-1)^m)*(factorial(p+l)/(factorial(p-m)*factorial(l+m)*factorial(m)))*(z.^m);
% end
% out = LL;
%%
out = (C(p,l)/w0)*...
(((sqrt(2).*r)/w0)^abs(l))*...
(e((-r.^2)/w0^2))*...
(L(p,l,((2.*r.^2)/w0^2)))*...
(e((-1)*1i*l.*phi)); ``
Edit
The answer was rewritten based on the code provided in Edit 4 of the question.
I think the trouble stems from the function U. You don't apply element wise operations to all parts of the equation. If you change it to:
out = (C(p,l)./w0).* ... % here it's a .* instead of *
(((sqrt(2).*r)./w0).^abs(l)).* ... % here it's a .* instead of *
(e((-r.^2)./w0.^2)).* ... % here it's a .* instead of *
(L(p,l,((2.*r.^2)./w0.^2))).* ... % here it's a .* instead of *
(e((-1)*1i*l.*phi));
You get the following two results, shown below.
This figure used an input of cartesian coordinates:
And this figure used the polar coordinates:
The "coarser" resolution in the second figure is due to the less suitable resolution of the grid. But in essence you resolve the same features.