Using meshgrid to interpolate image's physical coordinates - matlab

I am new to Matlab and am simulating a physical phenomena that requires the physical coordinates of my image. For example I can use the following to give the dimensions of my image.
a = phantom(80,250)
a(81:250,:) = [];
For my physical system, I need the spacing to be 2 between each pixel and for the object to go from 0:2:280 in x and 0:2:410 in y. I am trying to use meshgrid to see if it will help the case by starting with
[x,y] = meshgrid(1:1:100, 1:1:300);
[xm,ym] = meshgrid(1:.5:300, 1:.5:450);
M = interp2(x,y,a,xm,ym,'nearest');
This does not give me what I want but is how I think I can potentially achieve a solution.
My basic problem is I have the image which is size (80,250) and I need to sample/scale it so that I can correlate a point on the top right as location (280mm,410mm) with a sample of 2mm between each pixel. Is this the right approach or should I use another function?

First of all, the image is not 80 x 250. Be sure to go checkout the documentation.
What exactly are you hoping to do after this step? That really determines whether this is the appropriate way to go about this.
But based on your code and your last statement, you want the x range to be 0 - 280 and the y range to be 0 - 410.
xrange = linspace(0, 280, size(a, 2));
yrange = linspace(0, 410, size(a, 1));
So now your reference coordinates for your image would be
[xx,yy] = meshgrid(xrange, yrange);
Now how you want to sample that is really up to you. It sounds like you want every 2mm? So let's construct the grid to sample on.
[xq, yq] = meshgrid(0:2:max(xx(:)), 0:2:max(yy(:)));
Now we can actually do our interpolation. The important thing to remember is that the reference coordinates (x and y) must be the same size as your input image (a). This was one of the problems with your initial attempt.
M = interp2(x,y,a,xq,yq,'nearest');

Related

Flip a matrix for scatter plot

I want to flip an "arc" to the right side. I have tried imrotate but it gives me "arc" facing down instead of right side as shown in figure below. Please have a look at the code I am using. Thank you in advance.
R = 8; x_c = 5; y_c = 8;
thetas = 0:pi/499:pi;
xs = x_c + R*cos(thetas);
ys = y_c + R*sin(thetas);
% Now add some random noise to make the problem a bit more challenging:
mult = 0.5;
xs = xs+mult*randn(size(xs));
ys = ys+mult*randn(size(ys));
c = linspace(1,50,500);
D = [xs' ys'];
Dx = imrotate(D, 180, 'crop');
Dy=Dx;
Dy = imrotate(Dy, 180, 'crop') ;
subplot(211)
scatter(Dx(:,1), Dx(:,2), 140, c, 'filled', 'LineWidth',1.5)
subplot(212)
scatter(Dy(:,1), Dy(:,2),[],c, 'd','LineWidth',1.5)
You want to reflect the distribution for Dx. The operation of reflection means take x and make it -x.
Here the x-axis for Dx is Dx(:,1), so at first we need to write Dx(:,1) = - Dx(:,1).
When you do so and plot you'll see that the values of your x-axis has shifted to negative values. Maybe that is good enough for your purpose and that's it.
If I understand you correctly, this is not enough. Now, in order to bring this distribution back to positive x-axis value it needs to be translated.
This means:
Dx(:,1) = - Dx(:,1) + some number that translated x-axis to positive values.
you can choose that number by first taking the average (or center of mass) of the distribution, i.e. mean(Dx(:,1)), this is the value around which your value are distributed. If you only subtract the mean from the distribution you end up with values around zero, to bring it to the same distance on the positive side you need to subtract another time that mean.
Dx(:,1) = - Dx(:,1) + 2 * mean(Dx(:,1))
Reading this line means that the mean(Dx) is the calculated before the reflection so it has a positive value...
Another solution would be to set the 'XDir' property of the axes to 'reverse':
set(gca,'XDir','reverse')
This flips the axes, so it increases to the left instead of to the right. The data plotted is still identical, it is just shown differently. This might or might not be what you're after.
fliplr can work, but not with your Dx array, as it is. fliplr operates on the data, not the image, so with an array of size 500 x 2, the function flips x-values for y-values, and vice versa, which is why you get a rotation instead of a reflection.
One way to get fliplr to work is to capture the image data after you plot it, then flip it:
scatter(Dx(:,1), Dx(:,2), 140, c, 'filled', 'LineWidth',1.5)
f = getframe;
flipped = fliplr(f.cdata);
imshow(flipped)

How to speed up the sliding window function in matlab for an image

I have an image on which I have to slide 81*81 window so as to find the the intensity distance(difference) between center pixel and each of the pixels present in surrounding 81*81 window, also I have to find out the location distance i.e.the difference between the center pixel co-ordinates and co-ordinates of the pixels present in surrounding window. If I use for loops then I'll have to use 4 nested for loops for this operation which takes almost a whole day for a single image. I want to store 81*81 intensity differences for each pixel present in an image and likewise for location distance, which tends to create a 4D variable. can anyone suggest me some efficient way of doing this. Here is how I have written this code:
I = imread('House.tiff');
YCbCr = rgb2ycbcr(I);
YCbCr = double(YCbCr);
YCbCr = imresize(YCbCr,[200 200]);
[m n v] = size(YCbCr);
Y = YCbCr(:,:,1); Cb = YCbCr(:,:,2); Cr =YCbCr(:,:,3);
Y1 = padarray(Y,[20,20]);
Cb1 = padarray(Cb,[20,20]);
Cr1 = padarray(Cr,[20,20]);
window_size = 41;
p = (window_size-1)/2;
Dl = zeros(m,n,41,41); Df = zeros(m,n,41,41);
for x = 1:1:m
for y = 1:1:n
for a = -p:1:p
for b = -p:1:p
Df(x,y,a+p+1,b+p+1)= abs(Y(x,y)-Y1(x+a+p,y+b+p))+ abs(Cb(x,y)- Cb1(x+a+p,y+b+p))+ abs(Cr(x,y)- Cr1(x+a+p,y+b+p));%% intensity distance
Dl(x,y,a+p+1,b+p+1) = max(abs((x+p)-(x+a+p)),abs((y+p)-(y+b+p)));%% location distance
end
end
end
end
For that 81 x 81 sliding window, you probably have one X and Y "initial" point.
Assuming that, create a new temporary window cropping the initial image between those centroids in a 80x80 window.
new_image = image(X-40:X+40,Y-40:Y+40);
this will create you the new temporary image in a fast way.
To store the value of the pixels all the way
for i=1:length(images)
currentImage=image(i,1);
new_image(i)=currentImage(X-40:X+40,Y-40:Y+40);
end
a) Your location difference matrix is always the same - it doesn't depend on Y. Just create a single 81x81 instance of it.
b) Why do you need to store all of these outputs? The long amount of time spend could easily be because of the RAM usage of such a large matrix means you are reading and writing to/from disk (pagefile) rather than RAM, which will slow things down a lot. It's probably better to go optimising at the "next stage" of this algorithm, reducing computations on what you are actually doing with this matrix. Depending on your next stage, using imfilter might be the way to go.

Hough Transform - Finding the center of the hough transform circles

I have been reading up quite a bit on Hough circles and how it can be used to detect the center and radius of a circular object. Although I understood how the transform works I am still not able to to understand how to get the center of a circle. The code snippet below is customized purely for coins.png in MATLAB. I tried it with a slightly more complex picture and it fails miserably. I am fixing the radius and then getting the accumulator matrix.
Code:
temp = accum;
temp(find(temp<40))=0; %Thresholding
imshow(temp,[0 max(temp(:))])
temp = imdilate(temp, ones(6,6)); %Dilating
imshow(temp,[0 max(max(temp))]);
temp = imerode(temp,ones(3,3)); %Eroding
imshow(temp,[0 max(max(temp))]);
s = regionprops(im2bw(temp),'centroid'); %Computing centroid
centroids = cat(1, s.Centroid);
I wanted to test the code out on a different picture and found this one on google. The Hough Transform produced a favorable result, but it's more overlapping than the previous case and my method fails.
Can someone suggest what the best method is to compute the centroid of the hough circle?
Original image:
Full Code:
%%Read and find edges using a filter
i = imread('coins4.jpg');
i = rgb2gray(i);
i = imresize(i,0.125);
i_f = edge(i, 'canny',[0.01 0.45]);
imshow(i_f)
%%
[x y] = find(i_f>0); % Finds where the edges are and stores the x and y coordinates
edge_index = size(x);
radius = 30; %Fixed radius value
theta = 0:0.01:2*pi;
accum = zeros(size(i,1), size(i,2)); %Create an accumulator matrix.
r_co = radius*cos(theta);
r_si = radius*sin(theta);
x1 = repmat(x, 1, length(r_co));
y1 = repmat(y, 1, length(r_si));
x_r_co = repmat(r_co, length(x),1);
y_r_si = repmat(r_si, length(y),1);
%% Filling the accumulator
a = x1 - x_r_co;
b = y1 - y_r_si;
for cnt = 1:numel(a)
a_cnt = round(a(cnt));
b_cnt = round(b(cnt));
if(a_cnt>0 && b_cnt>0 && a_cnt<=size(accum,1) && b_cnt<=size(accum,2))
accum(a_cnt,b_cnt) = accum(a_cnt,b_cnt) + 1;
end
end
imshow(accum,[0 max(max(accum))]);
%% Thresholding and get the center of the circle.
close all;
temp = accum;
temp(find(temp<40))=0;
imshow(temp,[0 max(temp(:))])
temp = imdilate(temp, ones(6,6));
imshow(temp,[0 max(max(temp))]);
temp = imerode(temp,ones(3,3));
imshow(temp,[0 max(max(temp))]);
s = regionprops(im2bw(temp),'centroid');
centroids = cat(1, s.Centroid);
imshow(i);
hold on;
plot(centroids(:,1), centroids(:,2),'*b')
If you want to stick with the 2D image you have now, this is a simple algorithm that might work for your first image since all the coins are about the same size and it isn't too cluttered:
find the global maximum of your accumulator array using [~,max_idx] = max(accum(:)); [i,j] = ind2sub(size(accum), max_idx];
Take a small neighbourhood around this pixel (maybe a square with a 10 pixel radius) and calculate its center of mass and get its index (this basically finds the middle of the bright rings you see)
add the center of mass pixel index to a list of circle centers
set all pixels in the small neighbourhood to zero (to prevent double-detection of the same pixel center)
repeat from step 1. until you reach approximately 70% or so of the original global max
If that doesn't work, I'd suggest taking the 3D accumulator approach, which is much better for coins of variable size.
The reason you are getting "donut" shapes for some of the coins rather than circles with intense bright points at the centers is because the radius you are assuming is a little off. If you were to explore the 3rd dimension of the accumulator (the radius dimension), you would find that these "donuts" taper to a point, which you could then detect as a local maximum.
This does require a lot more time and memory but it would lead to the most accurate result and isn't a big adjustment code-wise. You can detect the maxima using a method similar to the one I mentioned above, but in 3D and without the center-of-mass trick in step 2.
So, there is a function in matlab that does exactly what you want, called imfindcircles. You give an image and a range of possible radii, it outputs the computed radii and the centers of the circles. Some other parameters allow to tweak the computation (and in my experience, using them is necessary for good results).
Now, if you want to find the centers and radii yourself (not using matlab) then you want to use a maximum finder on the accumulation matrix. The concept of the Hough transform is that the maxima you find on the accumulation matrix each correspond to a circle (parametrized usually in (radius, x_center, y_center). The tough part here is that you have a gigantic searchspace and many many many many maxima that are not "real" circles but artefacts. I do not know how to reliably differentiate these fake circles of the real ones -- and I expect it's a rather complicated check to do.
Hope this helps!

Plot a peak with height

How can I properly plot a peak in Matlab with a given height? My current method:
x = linspace(0,500,500);
peaks = zeros(size(x));
peaks(50) = 5;
peaks(300) = 20;
peaks(302) = 17;
peaks(375) = 15;
plot(x,peaks)
which gives
But this is ugly, confusing when multiple lines are close and leads to problems when converting it to a log scale since log(0)=-Inf. Is there a proper way of plotting peaks?
Context: I'm trying to analyse a spectogram (EDXS) to identify which material I'm working with.
Scaling the scale to log, you are asking Matlab to draw a line between -Inf and a positive value. Such lines are skipped.
To get a usable log plot, use points instead of lines:
plot(x,peaks,'x')
Another nice possibility to plot peaks is a stem-plot
I think the reason why you only get single pixels in your log-plot is because most of your values are 0. As log(0) = -Inf, this can't be plotted.
I know nothing of spectogram analyses, but if you absolutely want to plot this with a logarithmic scale, is it possible to use a "base line" equal 1 (0 in a log scale). If so:
x = linspace(0,100);
peaks = ones(size(x));
peaks(10) = 5;
peaks(60) = 20;
peaks(75) = 15;
semilogy(x,peaks)
I guess that this will look more reasonable with your real data. Note: If you have values that are less than 1, I would not go with this approach!

Matlab - Trying to use vectors with grid coordinates and value at each point for a color plot

I'm trying to make a color plot in matlab using output data from another program. What I have are 3 vectors indicating the x-position, y-yposition (both in milliarcseconds, since this represents an image of the surroundings of a black hole), and value (which will be assigned a color) of every point in the desired image. I apparently can't use pcolor, because the values which indicate the color of each "pixel" are not in a matrix, and I don't know a way other than meshgrid to create a matrix out of the vectors, which didn't work due to the size of the vectors.
Thanks in advance for any help, I may not be able to reply immediately.
If we make no assumptions about the arrangement of the x,y coordinates (i.e. non-monotonic) and the sparsity of the data samples, the best way to get a nice image out of your vectors is to use TriScatteredInterp. Here is an example:
% samplesToGrid.m
function [vi,xi,yi] = samplesToGrid(x,y,v)
F = TriScatteredInterp(x,y,v);
[yi,xi] = ndgrid(min(y(:)):max(y(:)), min(x(:)):max(x(:)));
vi = F(xi,yi);
Here's an example of taking 500 "pixel" samples on a 100x100 grid and building a full image:
% exampleSparsePeakSamples.m
x = randi(100,[500 1]); y = randi(100,[500 1]);
v = exp(-(x-50).^2/50) .* exp(-(y-50).^2/50) + 1e-2*randn(size(x));
vi = samplesToGrid(x,y,v);
imagesc(vi); axis image
Gordon's answer will work if the coordinates are integer-valued, but the image will be spare.
You can assign your values to a matrix based on the x and y coordinates and then use imagesc (or a similar function).
% Assuming the X and Y coords start at 1
max_x = max(Xcoords);
max_y = max(Ycoords);
data = nan(max_y, max_x); % Note the order of y and x
indexes = sub2ind(size(data), max_y, max_x);
data(indexes) = Values;
imagesc(data); % note that NaN values will be colored with the minimum colormap value