Related
I'm working on a filtered back projection algorithm using the central slice theorem for a homework assignment and while I understand the theory on paper, I've run into an issue implementing it in Matlab. I was provided with a skeleton to follow to do it but there is a step that I think I'm maybe misunderstanding. Here is what I have:
function img = sampleFBP(sino,angs)
% This step is necessary so that frequency information is preserved: we pad
% the sinogram with zeros so that this is ensured.
sino = padarray(sino, floor(size(sino,1)/2), 'both');
% diagDim should be the length of an individual row of the sinogram - don't
% hardcode this!
diagDim = size(sino, 2);
% The 2DFT (2D Fourier transform) of our image will start as a matrix of
% all zeros.
fimg = zeros(diagDim);
% Design your 1-d ramp filter.
rampFilter_1d = abs(linspace(-1, 1, diagDim))';
rowIndex = 1;
for nn = angs
% Each contribution to the image's 2DFT will also begin as all zero.
imContrib = zeros(diagDim);
% Get the current row of the sinogram - use rowIndex.
curRow = sino(rowIndex,:);
% Take the 1D Fourier transform the current row - be careful, as it's
% necessary to perform ifftshift and fftshift as Matlab tends to
% place zero-frequency components of a spectrum at the edges.
fourierCurRow = fftshift(fft(ifftshift(curRow)));
% Place the Fourier-transformed sino row and place it at the center of
% the next image contribution. Add the ramp filter in Fourier domain.
imContrib(floor(diagDim/2), :) = fourierCurRow;
imContrib = imContrib * fft(rampFilter_1d);
% Rotate the current image contribution to be at the correct angle on
% the 2D Fourier-space image.
imContrib = imrotate(imContrib, nn, 'crop');
% Add the current image contribution to the running representation of
% the image in Fourier space!
fimg = fimg + imContrib;
rowIndex = rowIndex + 1;
end
% Finally, just take the inverse 2D Fourier transform of the image! Don't
% forget - you may need an fftshift or ifftshift here.
rcon = fftshift(ifft2(ifftshift(fimg)));
The sinogram I'm inputting is just the output of the radon function on a Shepp-Logan phantom from 0 to 179 degrees. Running the code as it is now gives me a black image. I think I'm missing something in the loop where I add the FTs of rows to the image. From my understanding of the central slice theorem, what I think should be happening is this:
Initialize an array the same size as the what the 2DFT will be (i.e., diagDim x diagDim). This is the Fourier space.
Take a row of the sinogram which corresponds to the line integral information from a single angle and apply a 1D FT to it
According to the Central Slice Theorem, the FT of this line integral is a line through the Fourier domain that passes through the origin at an angle that corresponds to the angle at which the projection was taken. So to emulate that, I take the FT of that line integral and place it in the center row of the diagDim x diagDim matrix I created
Next I take the FT of the 1D ramp filter I created and multiply it with the FT of the line integral. Multiplication in the Fourier domain is equivalent to a convolution in the spatial domain so this convolves the line integral with the filter.
Now I rotate the entire matrix by the angle the projection was taken at. This should give me a diagDim x diagDim matrix with a single line of information passing through the center at an angle. Matlab increases the size of the matrix when it is rotated but since the sinogram was padded at the beginning, no information is lost and the matrices can still be added
If all of these empty matrices with a single line through the center are added up together, it should give me the complete 2D FT of the image. All that needs to be done is take the inverse 2D FT and the original image should be the result.
If the problem I'm running into is something conceptual, I'd be grateful if someone could point out where I messed up. If instead this is a Matlab thing (I'm still kind of new to Matlab), I'd appreciate learning what it is I missed.
The code that you have posted is a pretty good example of filtered backprojection (FBP) and I believe could be useful to people who wanted to learn the basis of FBP. One can use the function iradon(...) in MATLAB (see here) to perform FBP using a variety of filters. In your case of course, the point is to learn the basis of the central slice theorem and so finding a short cut is not the point. I have also learned a lot and refreshed my knowledge through answering to your question!
Now your code has been perfectly commented and describes the steps that need to be taken. There are a couple of subtle [programming] issues that need to be fixed so that the code works just fine.
First, your image representation in Fourier domain may end up having a missing array due to floor(diagDim/2) depending on the size of the sinogram. I would change this to round(diagDim/2) to have complete dataset in fimg. Be aware that this may lead to an error for certain sinogram sizes if not handled correctly. I would encourage you to visualize fimg to understand what that missing array is and why it matters.
Second issue is that your sinogram needs to be transposed to be consistent with your algorithm. Hence an addition of sino = sino'. Again, I do encourage you to try the code without this to see what happens! Note that zero padding must be happened along the views to avoid artifacts due to aliasing. I will demonstrate an example for this in this answer.
Third and most importantly, imContrib is a temporary holder for an array along fimg. Therefore, it must maintain the same size as fimg, so
imContrib = imContrib * fft(rampFilter_1d);
should be replaced with
imContrib(floor(diagDim/2), :) = imContrib(floor(diagDim/2), :)' .* rampFilter_1d;
Note that the Ramp filter is linear in frequency domain (thanks to #Cris Luengo for correcting this error). Therefore, you should drop the fft in fft(rampFilter_1d) as this filter is applied in the frequency domain (remember fft(x) decomposes the domain of x, such as time, space, etc to its frequency content).
Now a complete example to show how it works using the modified Shepp-Logan phantom:
angs = 0:359; % angles of rotation 0, 1, 2... 359
init_img = phantom('Modified Shepp-Logan', 100); % Initial image 2D [100 x 100]
sino = radon(init_img, angs); % Create a sinogram using radon transform
% Here is your function ....
% This step is necessary so that frequency information is preserved: we pad
% the sinogram with zeros so that this is ensured.
sino = padarray(sino, floor(size(sino,1)/2), 'both');
% Rotate the sinogram 90-degree to be compatible with your codes definition of view and radial positions
% dim 1 -> view
% dim 2 -> Radial position
sino = sino';
% diagDim should be the length of an individual row of the sinogram - don't
% hardcode this!
diagDim = size(sino, 2);
% The 2DFT (2D Fourier transform) of our image will start as a matrix of
% all zeros.
fimg = zeros(diagDim);
% Design your 1-d ramp filter.
rampFilter_1d = abs(linspace(-1, 1, diagDim))';
rowIndex = 1;
for nn = angs
% fprintf('rowIndex = %g => nn = %g\n', rowIndex, nn);
% Each contribution to the image's 2DFT will also begin as all zero.
imContrib = zeros(diagDim);
% Get the current row of the sinogram - use rowIndex.
curRow = sino(rowIndex,:);
% Take the 1D Fourier transform the current row - be careful, as it's
% necessary to perform ifftshift and fftshift as Matlab tends to
% place zero-frequency components of a spectrum at the edges.
fourierCurRow = fftshift(fft(ifftshift(curRow)));
% Place the Fourier-transformed sino row and place it at the center of
% the next image contribution. Add the ramp filter in Fourier domain.
imContrib(round(diagDim/2), :) = fourierCurRow;
imContrib(round(diagDim/2), :) = imContrib(round(diagDim/2), :)' .* rampFilter_1d; % <-- NOT fft(rampFilter_1d)
% Rotate the current image contribution to be at the correct angle on
% the 2D Fourier-space image.
imContrib = imrotate(imContrib, nn, 'crop');
% Add the current image contribution to the running representation of
% the image in Fourier space!
fimg = fimg + imContrib;
rowIndex = rowIndex + 1;
end
% Finally, just take the inverse 2D Fourier transform of the image! Don't
% forget - you may need an fftshift or ifftshift here.
rcon = fftshift(ifft2(ifftshift(fimg)));
Note that your image has complex value. So, I use imshow(abs(rcon),[]) to show the image. A couple of helpful images (food for thought) with the final reconstructed image rcon:
And here is the same image if you comment out the zero padding step (i.e. comment out sino = padarray(sino, floor(size(sino,1)/2), 'both');):
Note the different object size in the reconstructed images with and without zero padding. The object shrinks when the sinogram is zero padded since the radial contents are compressed.
I am attempting to extract the Radon Signature in order to recognize patterns of clothing (striped,plaid, irregular and patternless) as done in 1.
Algorithm to be implemented :
1. Use sobel operator to compute the gradient map as f(x,y).
2. Perform Radon transform based on maximum disk area.
3. Compute the variance of r under all theta directions.
4. Employ L2-norm to normalize the feature vector.
5. Plot Radon Signature as a bar chart of var(r) for all theta values.
I have done the following :
img = imread('plaid.jpg');
grey = rgb2gray(img);
img2 = edge(grey, 'sobel');
vararray=zeros(1,size(theta,2));
theta = -89:90;
for j = 1: size(theta,2)
[R3,xp3] = radon (img2,theta(j));
vararray(j) = var(R3);
end
vararray = vararray/norm(vararray);
figure(1), bar(theta,vararray),title('Radon Signature');
I believe that my error lies in the first 2 steps. I am unsure how to perform Radon only on the maximum disk area.
My results are shown on the right, while from the article (referenced below) is shown on the left.
However, my results should at least show 2 distinct peaks as shown in the acticle's results, but they do not.
Any assistance is appreciated.
Source of Algorithm : "Assistive Clothing Pattern Recognition for Visually Impaired People" by Xiaodong Yang, Student Member, IEEE, Shuai Yuan, and YingLi Tian, Senior Member, IEEE
Maximum disk area is, as #beaker thought, defined by the maximum filled circle that fits inside the bounding box of the image. That you can observe from the Fig.3 b) of the article.
Another thing you did wrong, is using edge detector edge(grey, 'sobel') while you should use gradient map or more formally gradient magnitude. Here's a code which produces a curve close to what is shown in Fig 3d. How to quantify it to six peaks, remains a question.
A = imread( 'Layer-5.png' ); % image from the article
A = double(rgb2gray( A ));
% gradient magnitude
dx = imfilter(A,fspecial('sobel') ); % x, 3x3 kernel
dy = imfilter(A,fspecial('sobel')'); % y
gradmag = sqrt( dx.^2 + dy.^2 );
% mask by disk
R = min( size(A)/2 ); % radius
disk = insertShape(zeros(size(A)),'FilledCircle', [size(A)/2,R] );
mask = double(rgb2gray(disk)~=0);
gradmag = mask.*gradmag;
% radon transform
theta = linspace(0,180,180);
vars = zeros(size(theta));
for u = 1:length(theta)
[rad,xp] =radon( gradmag, theta(u) );
indices = find( abs(xp)<R );
% ignore radii outside the maximum disk area
% so you don't sum up zeroes into variance
vars(u) = var( rad( indices ) );
end
vars = vars/norm(vars);
figure; plot( vars );
Bear in mind, images copied from the article appear with jpg artefacts. After good denoising (a tad too much here), e.g.,
you get much more prominent results.
I wish to create a limit cycle in Matlab. A limit cycle looks something like this:
I have no idea how to do it though, I've never done anything like this in Matlab.
The equations to describe the limit cycle are the following:
x_1d=x_2
x_2d=-x_1+x_2-2*(x_1+2*x_2)x_2^2
It is to be centered around the equilibrium which is (0,0)
Can any of you help me?
If you use the partial derivatives of your function to make a vector field, you can then use streamlines to visualize the cycle that you are describing.
For example, the function f = x^2+y^2
Gives me partial derivatives dx = 2x, dy=2y For the visualization, I sample from the partial derivatives over a grid.
[x,y] = meshgrid(0:0.1:1,0:0.1:1);
dx = 2*x;
dy = 2*y;
To visualize the vector field, I use quiver;
figure;
quiver(x, y, dx, dy);
Using streamline, I can visualize the path a particle injected into the vector field would take. In my example, I inject the particle at (0.1, 0.1)
streamline(x,y, dx, dy, 0.1, 0.1);
This produces the following visualization
In your case, you can omit the quiver step to remove the hedgehog arrows at every grid point.
Here's another example that shows the particle converging to an orbit.
Edit: Your function specifically.
So as knedlsepp points out, the function you are interested in is a bit ambiguously stated. In Matlab, * represents the matrix product while .* represents the element-wise multiplication between matrices. Similarly, '^2' represents MM for a matrix M, while .^2 represents taking the element-wise power.
So,
[x_1,x_2] = meshgrid(-4:0.1:4,-4:0.1:4);
dx_1 = x_2;
dx_2 = -x_1+x_2-2*(x_1+2*x_2)*(x_2)^2;
figure; streamline(x_1,x_2, dx_1, dx_2, 0:0.1:4, 0:0.1:4);
Looks like
This function will not show convergence because it doesn't converge.
knedlsepp suggests that the function you are actually interested in is
dx_1 = -1 * x_2;
dx_2 = -1 * -x_1+x_2-2*(x_1+2*x_2).*(x_2).^2;
His post has a nice description of the rest.
This post shows the code to produce the integral lines of your vector field defined by:
dx/dt = y
dy/dt = -x+y-2*(x+2*y)*y^2.
It is important to properly vectorize this function. (i.e. Introducing dots at all the important places)
dxdt = #(x,y) y;
dydt = #(x,y) -x+y-2*(x+2*y).*y.^2;
[X,Y] = meshgrid(linspace(-4,4,100));
[sx,sy] = meshgrid(linspace(-3,3,20));
streamline(stream2(X, Y, ... % Points
dxdt(X,Y), dydt(X,Y),... % Derivatives
sx, sy)); % Starting points
axis equal tight
To get a picture more similar to yours, change the grid size and starting points:
[X,Y] = meshgrid(linspace(-1,1,100));
[sx,sy] = meshgrid(linspace(0,0.75,20),0.2);
I am trying to achieve 3d reconstruction from 2 images. Steps I followed are,
1. Found corresponding points between 2 images using SURF.
2. Implemented eight point algo to find "Fundamental matrix"
3. Then, I implemented triangulation.
I have got Fundamental matrix and results of triangulation till now. How do i proceed further to get 3d reconstruction? I'm confused reading all the material available on internet.
Also, This is code. Let me know if this is correct or not.
Ia=imread('1.jpg');
Ib=imread('2.jpg');
Ia=rgb2gray(Ia);
Ib=rgb2gray(Ib);
% My surf addition
% collect Interest Points from Each Image
blobs1 = detectSURFFeatures(Ia);
blobs2 = detectSURFFeatures(Ib);
figure;
imshow(Ia);
hold on;
plot(selectStrongest(blobs1, 36));
figure;
imshow(Ib);
hold on;
plot(selectStrongest(blobs2, 36));
title('Thirty strongest SURF features in I2');
[features1, validBlobs1] = extractFeatures(Ia, blobs1);
[features2, validBlobs2] = extractFeatures(Ib, blobs2);
indexPairs = matchFeatures(features1, features2);
matchedPoints1 = validBlobs1(indexPairs(:,1),:);
matchedPoints2 = validBlobs2(indexPairs(:,2),:);
figure;
showMatchedFeatures(Ia, Ib, matchedPoints1, matchedPoints2);
legend('Putatively matched points in I1', 'Putatively matched points in I2');
for i=1:matchedPoints1.Count
xa(i,:)=matchedPoints1.Location(i);
ya(i,:)=matchedPoints1.Location(i,2);
xb(i,:)=matchedPoints2.Location(i);
yb(i,:)=matchedPoints2.Location(i,2);
end
matchedPoints1.Count
figure(1) ; clf ;
imshow(cat(2, Ia, Ib)) ;
axis image off ;
hold on ;
xbb=xb+size(Ia,2);
set=[1:matchedPoints1.Count];
h = line([xa(set)' ; xbb(set)'], [ya(set)' ; yb(set)']) ;
pts1=[xa,ya];
pts2=[xb,yb];
pts11=pts1;pts11(:,3)=1;
pts11=pts11';
pts22=pts2;pts22(:,3)=1;pts22=pts22';
width=size(Ia,2);
height=size(Ib,1);
F=eightpoint(pts1,pts2,width,height);
[P1new,P2new]=compute2Pmatrix(F);
XP = triangulate(pts11, pts22,P2new);
eightpoint()
function [ F ] = eightpoint( pts1, pts2,width,height)
X = 1:width;
Y = 1:height;
[X, Y] = meshgrid(X, Y);
x0 = [mean(X(:)); mean(Y(:))];
X = X - x0(1);
Y = Y - x0(2);
denom = sqrt(mean(mean(X.^2+Y.^2)));
N = size(pts1, 1);
%Normalized data
T = sqrt(2)/denom*[1 0 -x0(1); 0 1 -x0(2); 0 0 denom/sqrt(2)];
norm_x = T*[pts1(:,1)'; pts1(:,2)'; ones(1, N)];
norm_x_ = T*[pts2(:,1)';pts2(:,2)'; ones(1, N)];
x1 = norm_x(1, :)';
y1= norm_x(2, :)';
x2 = norm_x_(1, :)';
y2 = norm_x_(2, :)';
A = [x1.*x2, y1.*x2, x2, ...
x1.*y2, y1.*y2, y2, ...
x1, y1, ones(N,1)];
% compute the SVD
[~, ~, V] = svd(A);
F = reshape(V(:,9), 3, 3)';
[FU, FS, FV] = svd(F);
FS(3,3) = 0; %rank 2 constrains
F = FU*FS*FV';
% rescale fundamental matrix
F = T' * F * T;
end
triangulate()
function [ XP ] = triangulate( pts1,pts2,P2 )
n=size(pts1,2);
X=zeros(4,n);
for i=1:n
A=[-1,0,pts1(1,i),0;
0,-1,pts1(2,i),0;
pts2(1,i)*P2(3,:)-P2(1,:);
pts2(2,i)*P2(3,:)-P2(2,:)];
[~,~,va] = svd(A);
X(:,i) = va(:,4);
end
XP(:,:,1) = [X(1,:)./X(4,:);X(2,:)./X(4,:);X(3,:)./X(4,:); X(4,:)./X(4,:)];
end
function [ P1,P2 ] = compute2Pmatrix( F )
P1=[1,0,0,0;0,1,0,0;0,0,1,0];
[~, ~, V] = svd(F');
ep = V(:,3)/V(3,3);
P2 = [skew(ep)*F,ep];
end
From a quick look, it looks correct. Some notes are as follows:
You normalized code in eightpoint() is no ideal.
It is best done on the points involved. Each set of points will have its scaling matrix. That is:
[pts1_n, T1] = normalize_pts(pts1);
[pts2_n, T2] = normalize-pts(pts2);
% ... code
% solution
F = T2' * F * T
As a side note (for efficiency) you should do
[~,~,V] = svd(A, 0);
You also want to enforce the constraint that the fundamental matrix has rank-2. After you compute F, you can do:
[U,D,v] = svd(F);
F = U * diag([D(1,1),D(2,2), 0]) * V';
In either case, normalization is not the only key to make the algorithm work. You'll want to wrap the estimation of the fundamental matrix in a robust estimation scheme like RANSAC.
Estimation problems like this are very sensitive to non Gaussian noise and outliers. If you have a small number of wrong correspondence, or points with high error, the algorithm will break.
Finally, In 'triangulate' you want to make sure that the points are not at infinity prior to the homogeneous division.
I'd recommend testing the code with 'synthetic' data. That is, generate your own camera matrices and correspondences. Feed them to the estimate routine with varying levels of noise. With zero noise, you should get an exact solution up to floating point accuracy. As you increase the noise, your estimation error increases.
In its current form, running this on real data will probably not do well unless you 'robustify' the algorithm with RANSAC, or some other robust estimator.
Good luck.
Good luck.
Which version of MATLAB do you have?
There is a function called estimateFundamentalMatrix in the Computer Vision System Toolbox, which will give you the fundamental matrix. It may give you better results than your code, because it is using RANSAC under the hood, which makes it robust to spurious matches. There is also a triangulate function, as of version R2014b.
What you are getting is sparse 3D reconstruction. You can plot the resulting 3D points, and you can map the color of the corresponding pixel to each one. However, for what you want, you would have to fit a surface or a triangular mesh to the points. Unfortunately, I can't help you there.
If what you're asking is how to I proceed from fundamental Matrix + corresponding points to a dense model then you still have a lot of work ahead of you.
relative camera locations (R,T) can be calculated from a fundamental matrix assuming you know the internal camera params (up to scale, rotation, translation). To get a full dense matrix there are a few ways to go. you can try using an existing library (PMVS for example). I'd look into OpenMVG but I'm not sure about matlab interface.
Another way to go, you can compute a dense optical flow (many available for matlab). Look for a epipolar OF (It takes a fundamental matrix and restricts the solution to lie on the epipolar lines). Then you can triangulate every pixel to get a depthmap.
Finally you will have to play with format conversions to get from a depthmap to VRML (You can look at meshlab)
Sorry my answer isn't more Matlab oriented.
I am trying to create some Gaussian distributions and put them on an image. The Gaussians have randomly created parameter (amplitude, position and standard deviation). First I put the parameters into vectors or matrices, then I am using ngrid() function to get a 2d space to create the Gaussians, however i get an error (since possibly mathematical operations with ngrid values is not trivial...). The error is:
??? Error using ==> minus
Integers can only be combined
with integers of the same class,
or scalar doubles.
Error in ==> ss_gauss_fit at 23
gauss = amp(i)*
exp(-((x-xc).^2 +
(y-yc).^2)./(2*std(i)));
the code is here:
clear all;
image = uint8(zeros([300 300]));
imsize=size(image);
noOfGauss=10;
maxAmpGauss=160;
stdMax=15;
stdMin=3;
for i=1:noOfGauss
posn(:,:,i)=[ uint8(imsize(1)*rand()) uint8(imsize(2)*rand()) ];
std(i)=stdMin+uint8((stdMax-stdMin)*rand());
amp(i)= uint8(rand()* maxAmpGauss);
end
% draw the gaussians on blank image
for i=1:noOfGauss
[x,y] = ndgrid(1:imsize(1), 1:imsize(2));
xc = posn(1,1,i);
yc = posn(1,2,i);
gauss = amp(i)* exp(-((x-xc).^2 + (y-yc).^2)./(2*std(i)));
image = image + gauss;
end
Please tell me how fix this, plot 2d Gaussians with parameter vectors...
Thanks in advance
Apart from the craziness about "drawing on an image", which I don't really understand, I think you are trying to add up a bunch of separate gaussian distributions on a grid. Here's what I did with your code. Note that your bivariate gaussians are not normalized properly and you were using the variance and not standard deviation before. I fixed the latter; however, I didn't bother with the normalization because you are multiplying each by an amplitude value anyway.
clear all;
xmax = 50;
ymax = 50;
noOfGauss=10;
maxAmpGauss=160;
stdMax=10;
stdMin=3;
posn = zeros(noOfGauss, 2);
std = zeros(noOfGauss, 1);
amp = zeros(noOfGauss, 1);
for i=1:noOfGauss
posn(i,:)=[ xmax*rand() ymax*rand() ];
std(i)=stdMin+(stdMax-stdMin)*rand();
amp(i)= rand()* maxAmpGauss;
end
% draw the gaussians
[x,y] = ndgrid(1:xmax, 1:ymax);
z = zeros(xmax, ymax);
for i=1:noOfGauss
xc = posn(i,1);
yc = posn(i,2);
z = z + amp(i)* exp(-((x-xc).^2 + (y-yc).^2)./(2*std(i)^2));
end
surf(x, y, z);
Random output: