I'm doing a piece of Matlab code for reconstruction of radial 3D NMR data. However, since the radon functions built into Matlab are only 2D, I will have to apply them twice. After the first iradon transform in my code I get out projections of the imaged object as they are supposed to look (from different angles). But after the second iradon transform I do not see a correct reconstruction of the object (just a lot of noise and some blurry stuff where the object should be).
My attempt at a solution is shown below.
The input data is a free induction decay or fid: fid(NP,nv,nv2) where NP is the number of projections, nv is the number of theta angle increments and nv2 is the number of phi angle increments.
Doing the ifft on the FID will give a sinogram of dimensions proj(NP,phi) for each theta angle.
Doing the first iradon gives filtered and unfiltered backprojections with dimensions I(r,x) for each theta angle. (so that I3 and I4 have dimensions (r,z,theta) )
Doing the last iradon transform should then render the reconstructed 3D image with dimensions I(x,y,z)
I3=[];
I4=[];
I5=[];
I6=[];
for k=1:1:nv2
FID = squeeze(fid(:,:,k));
proj=abs(fftshift(ifft(ifftshift(FID),[],1)));
I1 = imrotate(iradon(proj,theta,'v5cubic','none',1,2*NP),-90);
I2 = imrotate(iradon(proj,theta,'v5cubic','Ram-Lak',1,2*NP),-90);
I3(:,:,k) = I1;
I4(:,:,k) = I2;
end
for k=1:size(I3,2)
I5(:,:,k) = iradon(squeeze(I3(:,k,:)),phi,'v5cubic','none',1,2*NP);
I6(:,:,k) = iradon(squeeze(I4(:,k,:)),phi,'v5cubic','Ram-Lak',1,2*NP);
end
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 trying to compute the convolution of a curve with a scaled wavelet. Using the MATLAB convolution function https://www.mathworks.com/help/matlab/ref/conv.html, the result has a large number of 'NaN' values in the beginning, and I'd like to understand where these are coming from.
My instinct with convolution is that this should not be the case as I have defined upper bounds at each point of the convolution. The way I visualize this convolution is that the wavelet is flipped, and then moved from left to right over the curve. As it moves, the area below the intersection of the wavelet with the curve is what is stored in the output. If this is the case, the resulting output should exist and be positive at all points of the convolution except where there is no overlap and the result is 0.
% The curve is a set of responses to presented stimuli,
% and the following is an example:
curve = [0.0500, 0.1000, 0.1500, 0.2000, 0.3000, 0.5000, 0.8000;
11.6465, 14.8354, 5.0695, 0.4856, 0.5858, 0.2863, 0.3864];
% The scaled Mexican hat wavelet is as follows:
acuteness = 1000;
[mexh_y, mexh_x] = mexihat(-5,5,acuteness);
max_response = max(curve(2,:));
wav_x = mexh_x/100;
wav_y = mexh_y * (max_response+1);
% Here wav_x are the wavelet’s x-values and wav_y are the wavelet’s y-values.
% I interpolate the original curve to have more values as follows:
max_presentation = max(curve(1,:));
step = max_presentation/acuteness;
sf_vals_for_interpolation = [step:step:max_presentation];
interp_curve = interp1(curve(1,:),curve(2,:),sf_vals_for_interpolation);
% Now I’d like to perform a convolution:
conv_res = conv(interp_curve, wav_y);
As noted in the summary, the resulting convolution contains a number of 'NaN' values that I would like to understand.
This is the first time I do the image processing. So I have a lot of questions:
I have two pictures which are taken from different position, one from the left and the other one from the right like the picture below.[![enter image description here][1]][1]
Step 1: Read images by using imread function
I1 = imread('DSC01063.jpg');
I2 = imread('DSC01064.jpg');
Step 2: Using camera calibrator app in matlab to get the cameraParameters
load cameraParams.mat
Step 3: Remove Lens Distortion by using undistortImage function
[I1, newOrigin1] = undistortImage(I1, cameraParams, 'OutputView', 'same');
[I2, newOrigin2] = undistortImage(I2, cameraParams, 'OutputView', 'same');
Step 4: Detect feature points by using detectSURFFeatures function
imagePoints1 = detectSURFFeatures(rgb2gray(I1), 'MetricThreshold', 600);
imagePoints2 = detectSURFFeatures(rgb2gray(I2), 'MetricThreshold', 600);
Step 5: Extract feature descriptors by using extractFeatures function
features1 = extractFeatures(rgb2gray(I1), imagePoints1);
features2 = extractFeatures(rgb2gray(I2), imagePoints2);
Step 6: Match Features by using matchFeatures function
indexPairs = matchFeatures(features1, features2, 'MaxRatio', 1);
matchedPoints1 = imagePoints1(indexPairs(:, 1));
matchedPoints2 = imagePoints2(indexPairs(:, 2));
From there, how can I construct the 3D point cloud ??? In step 2, I used the checkerboard as in the picture attach to calibrate the camera[![enter image description here][2]][2]
The square size is 23 mm and from the cameraParams.mat I know the intrinsic matrix (or camera calibration matrix K) which has the form K=[alphax 0 x0; 0 alphay y0; 0 0 1].
I need to compute the Fundamental matrix F, Essential matrix E in order to calculate the camera matrices P1 and P2, right ???
After that when I have the camera matrices P1 and P2, I use the linear triangulation methods to estimate 3D point cloud. Is it the correct way??
I appreciate if you have any suggestion for me?
Thanks!
To triangulate the points you need the so called "camera matrices" and the points in 2D in each of the images (that you already have).
In Matlab you have the function triangulate, that does the job for you.
If you have calibrated the cameras, you shoudl have this information already. Anyways, you have here an example of how to create the "stereoParams" object needed for the triangulation.
Yes, that is the correct way. Now that you have matched points, you can use estimateFundamentalMatrix to compute the fundamental matrix F. Then you get the essential matrix E by multiplying F by extrinsics. Be careful about the order of multiplication, because the intrinsic matrix in cameraParameters is transposed relative to what you see in most textbooks.
Now, you have to decompose E into a rotation and a translation, from which you can construct the camera matrix for the second camera using cameraMatrix. You also need the camera matrix for the first camera, for which the rotation would be a 3x3 identity matrix, and translation will be a 3-element 0 vector.
Edit: there is now a cameraPose function in MATLAB, which computes an up-to-scale relative pose ('R' and 't') given the Fundamental matrix and the camera parameters.
I am working on a project involving video motion magnification algorithms. Currently I am trying to understand phase based motion magnification using a riesz pyramid. My main source of information is this document:
Riesz Pyramids for Fast Phase-Based Video Magnification
\
I have performed the following steps to attempt to reproduce some of the results in the paper:
Decompose an image into multiple scales using the provided matlab code for the riesz pyramid
Generate the images Riesz1 and Riesz2 by convolving one subband of the pyramid with [-0.5, 0, 0.5] and [-0.5, 0, 0.5]' using the approximate riesz transform introduced in the paper.
Determine the dominant local orientation in every pixel of the subband by calculating atan(R2/R1). This calculation is derived from equation 3 in the paper.
Steer the transform to the dominant local orientation and calculate the resulting quadrature pair
Use the quadrature pair to generate a complex number (I + iQ) whose phase I then used to determine the local phase in the specific pixel.
This is the Matlab code I created:
%Generate a circle image
img = zeros(512, 512);
img(:) = 128;
rad = 180;
for i = size(img, 1)/2 - rad : size(img,1)/2 + rad
for j = size(img, 2)/2 - rad : size(img,2)/2 + rad
deltaX = abs(size(img, 1)/2 - i);
deltaY = abs(size(img, 2)/2 - j);
if (sqrt(deltaX^2+deltaY^2) <= rad)
img(i, j) = 255;
end
end
end
%build riesz pyramid
[pyr, pind] = buildNewPyr(img);
%extract band2 from pyramid (no orientation information yet)
I = pyrBand(pyr,pind,3);
%convolve band2 with approximate riesz filter for first quadrature pair
%element
R1 = conv2(I, [0.5, 0, -0.5], 'same');
%convolve band2 with approximate riesz filter (rotated by 90°) for second
%quadrature pair element
R2 = conv2(I, [0.5, 0, -0.5]', 'same');
% show the resulting image containing orientation information!
% imshow(band2_r2, []);
%To extract the phase, we have to steer the pyramid to its dominant local
%orientation. Orientation is calculated as atan(R2/R1)
theta = atan(R2./R1);
theta(isnan(theta) | isinf(theta)) = 0;
%imshow(theta, []);
% create quadrature pair
Q = zeros(size(theta, 1), size(theta, 2));
for i = 1:size(theta, 1)
for j = 1:size(theta, 1)
if theta(i, j) ~= 0
%create rotation matrix
rot_mat = ...
[cos(theta(i, j)), sin(theta(i, j));...
-sin(theta(i, j)) cos(theta(i, j))];
%steer to dominant local orientation(theta) and set Q
resultPair = rot_mat*[R1(i, j), R2(i,j)]';
Q(i,j) = resultPair(1);
end
end
end
% create amplitude and phase image
A = abs(complex(I, Q));
Phi = angle(complex(I, Q));
The generated images look like this:
Now my questions:
When calculating theta using atan(R2/R1) I get a lot of artifacts in the result (see image "dominant orientation"). Is there something obvious I miss here/do wrong?
Assuming what my results are correct thus far. To magnify motion I need to not only be able to determine the local phase, but also to change it. I seem to miss something obvious, but how would I go about that? Do I need to somehow change the phase of the pyramid subband pixels and then collapse the pyramid? If yes, how?
I am (obviously) quite new to this topic and only have a rudimentary understanding of image processing. I would be very thankful for any answer, be it a solution to my problems or just a referral to an other useful source of information.
Sincerely
I have got a functioning implementation of this algorithm. Here are the steps I took to successfully motion-magnify a video using this method.
These steps should be applied to each channel of a video sequence that you (I have tried it for RGB video, you could probably get away with doing it for just luminance, in a YUV video).
Create an image pyramid of each frame. The original paper has a recommended pyramid structure to allow greater magnification values, although it works fairly well with a Laplacian pyramid.
For each pyramid level of each video channel, calculate the Riesz transform (see The Riesz transform and simultaneous representations of phase, energy and orientation in spatial vision for an overview of the transform, and see the paper in the original question for an efficient approximate implementation).
Using the Riesz transform, calculate the local amplitude, orientation and phase for each pixel of each pyramid level of each video frame. The following Matlab code will calculate the local orientation, phase and amplitude of a (double format) image using the approximate Riesz transform:
function [orientation, phase, amplitude] = riesz(image)
[imHeight, imWidth] = size(image);
%approx riesz, from Riesz Pyramids for Fast Phase-Based Video Magnification
dxkernel = zeros(size(image));
dxkernel(1, 2)=-0.5;
dxkernel(1,imWidth) = 0.5;
dykernel = zeros(size(image));
dykernel(2, 1) = -0.5;
dykernel(imHeight, 1) = 0.5;
R1 = ifft2(fft2(image) .* fft2(dxkernel));
R2 = ifft2(fft2(image) .* fft2(dykernel));
orientation = zeros(imHeight, imWidth);
phase = zeros(imHeight, imWidth);
orientation = (atan2(-R2, R1));
phase = ((unwrap(atan2(sqrt(R1.^2 + R2.^2) , image))));
amplitude = sqrt(image.^2 + R1.^2 + R2.^2);
end
For each pyramid level, temporally filter the phase values of each pixel using a bandpass filter set to a frequency appropriate for the motion that you wish to magnify. Note that this removes the DC component from the phase value.
Calculate the amplified phase value by
amplifiedPhase = phase + (requiredGain * filteredPhase);
Use the amplified phase to calculate new pixel values for each pyramid level by
amplifiedSequence = amplitude .* cos(amplifiedPhase);
Collapse the pyramids to generate the new, amplified video channel.
Recombine your amplified channels into a new video frame.
There are some other steps in the original paper to improve noise performance, but the sequence above produces motion amplified video quite nicely.
I have fully implemented the methodology of Riesz Pyramids for fast phase based video motion magnification. I felt the papers did not clearly describe the appropriate steps required to correctly filter phase. It is important to realise that mulitple mathematically correct expressions for phase and orientation may in fact not be suitable using MATLAB's acos(), asin() and atan() functions. Here is my implementation:
% R1, R2 are Riesz transforms of the image I and Q is the Quadrature pair
Q = sqrt((R1.^2) + (R2.^2));
phase = atan2(Q,I);
The phase should be wrapped to be between -pi and +pi, i.e. if phase is greater than +pi, phase = phase - 2*pi, if phase is smaller than -pi,
phase = phase + 2*pi.
amplitude = sqrt((I.^2) + (R1.^2) + (R2.^2));
Furthermore, it is essential that the change in phase between successive frames is filtered, not the phase directly.
phase_diff = phase(t+1) - phase(t);
This quantity "phase_diff" is temporally filtered, and amplified by multiplication with an amplification factor. The filtered, amplified, variation in phase is then used to phase shift the input.
magnified output = amplitude.*cos(phase_diff_filtered_amplified + original phase).
whilst DrMcCleod's answer didn't directly provide a solution, it seems he was on the right track.
The complex representation of the image can be constructed from the input and the quadrature pair with
complexImg = complex(I, Q);
A phase shifted reconstruction of the image can then be generated by multiplying the complex representation with e^(-i*shift) which removes the complex part of the representation and results in the original image plus the introduced phase shift.
reconstructed = complexImg*exp(-sqrt(-1) * shift);
I'll have to experiment a little, but this seems to produce the expected results.
Thanks for the help!
I need radon transform of an image,but I am not permitted to use radon function of MATLAB.So I have to write my own.
I have done some research for that but couldn't find any satisfying example.
Is there any other way to get projections of an image without
using radon?
If not,how can I write my own radon function(at least a little
clue)
The radon transform converts an image in 2D/pseudo-3D to a 1D intensity equivalent. Basically, we calculate the sum of all intensities across one row of pixels for all angles theta.
Original code by Justin K. Romberg (Rice University) on https://www.clear.rice.edu/elec431/projects96/DSP/projections.html
projections.m
%%This MATLAB function takes an image matrix and vector of angles and
%%then finds the 1D projection (Radon transform) at each of the angles.
%%It returns a matrix whose columns are the projections at each angle.
%% Written by : Justin K. Romberg
function PR = projections(IMG, THETA)
% pad the image with zeros so we don't lose anything when we rotate.
[iLength, iWidth] = size(IMG);
iDiag = sqrt(iLength^2 + iWidth^2);
LengthPad = ceil(iDiag - iLength) + 2;
WidthPad = ceil(iDiag - iWidth) + 2;
padIMG = zeros(iLength+LengthPad, iWidth+WidthPad);
padIMG(ceil(LengthPad/2):(ceil(LengthPad/2)+iLength-1), ...
ceil(WidthPad/2):(ceil(WidthPad/2)+iWidth-1)) = IMG;
% loop over the number of angles, rotate 90-theta (because we can easily sum
% if we look at stuff from the top), and then add up. Don't perform any
% interpolation on the rotating.
n = length(THETA);
PR = zeros(size(padIMG,2), n);
for i = 1:n
tic
tmpimg = imrotate(padIMG, 90-THETA(i), 'bilinear', 'crop');
PR(:,i) = (sum(tmpimg))';
THETA(i)
toc
end
I don't know if it what you search for but have a look to that :
Matlab code at the end.