How can I make my code run faster? - matlab

I'm currently working on a piece of code which blurs an image by taking a circular sample of pixels around each pixel, finding their mean and then applying that to the central pixel. It works, but it takes ages, especially with large images and large radii.
Can anyone give me some tips on how to speed this up?
function [] = immean(IMAGE, r)
%Find the maximum width, nx, and maximum height, ny, of the image - so that
%it can be used to end the for-loop at the appropriate positions.
[nx, ny] = size(IMAGE);
%Create a completely black image of the same size as the subject image,
%into which the appropriate pixel values can be fed.
average = uint8(zeros(size(IMAGE)));
%Loop through all the pixels of the image.
for x = 1:nx
for y = 1:ny
%This next code takes a square sample of pixels, with dimensions of
%r x r.
%First, set the boundaries of this square, from which the circular
%sample of pixels will be taken.
if x-r <= 0
startx = 1;
else
startx = x-r;
end
if x+r > nx
endx = nx;
else
endx = x+r;
end
if y-r <= 0
starty = 1;
else
starty = y-r;
end
if y+r > ny
endy = ny;
else
endy = y+r;
end
%Loop through this square sample and, if the pixel is within the
%range of the circle, add its intensity to the total.
total = 0;
pixelcount = 0;
for xp = startx : endx
for yp = starty : endy
if (x-xp)^2 + (y-yp)^2 <= r^2
total = total + uint32(IMAGE(xp, yp));
pixelcount = pixelcount + 1;
end
end
end
mean = total / pixelcount;
average(x,y) = mean;
end
end
imshow(average)
I've tried changing things like uint32 but that hasn't worked. Beyond that, I'm a bit of a beginner, so I'm not sure what the best tricks are in this situation. Thanks for your time.

Loops are extremely slow in MATLAB. As a rule, you should always vectorize your code where possible. This is one of those cases. Looping through every pixel is painfully slow.
MATLAB has a function imfilter which does basically what you want. Since you are just taking the average intensity, a simple filter function will do this quite well and very fast. You can define the filter coefficients as a matrix:
% Define a 2D Filter with radius r:
d = 2*r+1;
h = zeros(d);
% Now make it a "circular" filter (keeping it square would be much easier
% and probably look the same but whatever):
[x, y] = meshgrid(1:d,1:d);
distance = sqrt((x-(r+1)).^2 + (y-(r+1)).^2);
h(distance<=r) = 1;
h = h / sum(h(:))
% Now pump it into imfilter and youre done:
average = imfilter(uint32(IMAGE), h);
Also, there are a TON of MATLAB image processing tools, so search around a bit and you'll probably find helpful tools for whatever your working on, no need to reinvent the wheel. I don't have an image in front of me to test this one but let me know if it works.

Related

Generate a Rectangular Pulse in MATLAB

I need to create a rectangular pulse with width = 7 and a range (-T/2, T/2) where T 59 msec.
I wrote this code but I'm not sure if that's correct.
w = 7;
T = 59;
t = -T/2:1:T/2;
rect = rectpuls(t, w);
plot(t, rect);
This code generates a rectangular pulse but I'm not sure if it's right. Also, I'm not quite sure what the t = -T/2:1:T/2; means. I mean the range is from -29.5 to 29.5 with step 1. When I set this to 0.1 or 0.01 my pulse is better. Why does this affect my output?
Note that the second thing I have to do is to create a periodic sequence of clock pulses. I don't know if this affects the way I must implement my initial rectangular pulse.
When you increase the number of increments a numerical function (such as Matlab rectpuls) uses in its process of discretizing the continuous, you'll have as consequence that the accuracy of said function is going to improve, at the expense (in this case, negligible) of added computational cost. You are doing exactly this, when you discretize employing smaller time-steps, from 1 to 0.1 to 0.01.
To create a periodic sequence of identical rectangular pulses, you can call the function in a loop:
w = 7;
T = 59;
t = -T/2:1:T/2;
t_size = size(t);
N = 10;
rect = zeros(N, t_size(2));
interval = 20;
figure
plot(t, rectpuls(t, w));
xlim([-20 (N + 1)*interval]);
ylim([0 1.1]);
hold on
for i = 1:N
t = (-T/2 + i*interval): 1 :(T/2 + i*interval);
rect(i,:) = rectpuls(t - i * interval, w);
plot(t, rect(i,:));
hold on
end
The above should generate identical rectangular pulses every interval = 20 ms, over a time length of interval * (N + 1) = 220 ms.

Fourier transform for fiber alignment

I'm working on an application to determine from an image the degree of alignment of a fiber network. I've read several papers on this issue and they basically do this:
Find the 2D discrete Fourier transform (DFT = F(u,v)) of the image (gray, range 0-255)
Find the Fourier Spectrum (FS = abs(F(u,v))) and the Power Spectrum (PS = FS^2)
Convert spectrum to polar coordinates and divide it into 1º intervals.
Calculate number-averaged line intensities (FI) for each interval (theta), that is, the average of all the intensities (pixels) forming "theta" degrees with respect to the horizontal axis.
Transform FI(theta) to cartesian coordinates
Cxy(theta) = [FI*cos(theta), FI*sin(theta)]
Find eigenvalues (lambda1 and lambda2) of the matrix Cxy'*Cxy
Find alignment index as alpha = 1 - lamda2/lambda1
I've implemented this in MATLAB (code below), but I'm not sure whether it is ok since point 3 and 4 are not really clear for me (I'm getting similar results to those of the papers, but not in all cases). For instance, in point 3, "spectrum" is referring to FS or to PS?. And in point 4, how should this average be done? are all the pixels considered? (even though there are more pixels in the diagonal).
rgb = imread('network.tif');%513x513 pixels
im = rgb2gray(rgb);
im = imrotate(im,-90);%since FFT space is rotated 90º
FT = fft2(im) ;
FS = abs(FT); %Fourier spectrum
PS = FS.^2; % Power spectrum
FS = fftshift(FS);
PS = fftshift(PS);
xoffset = (513-1)/2;
yoffset = (513-1)/2;
% Avoid low frequency points
x1 = 5;
y1 = 0;
% Maximum high frequency pixels
x2 = 255;
y2 = 0;
for theta = 0:pi/180:pi
% Transposed rotation matrix
Rt = [cos(theta) sin(theta);
-sin(theta) cos(theta)];
% Find radial lines necessary for improfile
xy1_rot = Rt * [x1; y1] + [xoffset; yoffset];
xy2_rot = Rt * [x2; y2] + [xoffset; yoffset];
plot([xy1_rot(1) xy2_rot(1)], ...
[xy1_rot(2) xy2_rot(2)], ...
'linestyle','none', ...
'marker','o', ...
'color','k');
prof = improfile(F,[xy1_rot(1) xy2_rot(1)],[xy1_rot(2) xy2_rot(2)]);
i = i + 1;
FI(i) = sum(prof(:))/length(prof);
Cxy(i,:) = [FI(i)*cos(theta), FI(i)*sin(theta)];
end
C = Cxy'*Cxy;
[V,D] = eig(C)
lambda2 = D(1,1);
lambda1 = D(2,2);
alpha = 1 - lambda2/lambda1
Figure: A) original image, B) plot of log(P+1), C) polar plot of FI.
My main concern is that when I choose an artificial image perfectly aligned (attached figure), I get alpha = 0.91, and it should be exactly 1.
Any help will be greatly appreciated.
PD: those black dots in the middle plot are just the points used by improfile.
I believe that there are a couple sources of potential error here that are leading to you not getting a perfect alpha value.
Discrete Fourier Transform
You have discrete imaging data which forces you to take a discrete Fourier transform which inevitably (depending on the resolution of the input data) have some accuracy issues.
Binning vs. Sampling Along a Line
The way that you have done the binning is that you literally drew a line (rotated by a particular angle) and sampled the image along that line using improfile. Using improfile performs interpolation of your data along that line introducing yet another potential source of error. The default is nearest neighbor interpolation which in the example shown below can cause multiple "profiles" to all pick up the same points.
This was with a rotation of 1-degree off-vertical when technically you'd want those peaks to only appear for a perfectly vertical line. It is clear to see how this sort of interpolation of the Fourier spectrum can lead to a spread around the "correct" answer.
Data Undersampling
Similar to Nyquist sampling in the Fourier domain, sampling in the spatial domain has some requirements as well.
Imagine for a second that you wanted to use 45-degree bin widths instead of the 1-degree. Your approach would still sample along a thin line and use that sample to represent 45-degrees worth or data. Clearly, this is a gross under-sampling of the data and you can imagine that the result wouldn't be very accurate.
It becomes more and more of an issue the further you get from the center of the image since the data in this "bin" is really pie wedge shaped and you're approximating it with a line.
A Potential Solution
A different approach to binning would be to determine the polar coordinates (r, theta) for all pixel centers in the image. Then to bin the theta components into 1-degree bins. Then sum all of the values that fall into that bin.
This has several advantages:
It removes the undersampling that we talked about and draws samples from the entire "pie wedge" regardless of the sampling angle.
It ensures that each pixel belongs to one and only one angular bin
I have implemented this alternate approach in the code below with some false horizontal line data and am able to achieve an alpha value of 0.988 which I'd say is pretty good given the discrete nature of the data.
% Draw a bunch of horizontal lines
data = zeros(101);
data([5:5:end],:) = 1;
fourier = fftshift(fft2(data));
FS = abs(fourier);
PS = FS.^2;
center = fliplr(size(FS)) / 2;
[xx,yy] = meshgrid(1:size(FS,2), 1:size(FS, 1));
coords = [xx(:), yy(:)];
% De-mean coordinates to center at the middle of the image
coords = bsxfun(#minus, coords, center);
[theta, R] = cart2pol(coords(:,1), coords(:,2));
% Convert to degrees and round them to the nearest degree
degrees = mod(round(rad2deg(theta)), 360);
degreeRange = 0:359;
% Band pass to ignore high and low frequency components;
lowfreq = 5;
highfreq = size(FS,1)/2;
% Now average everything with the same degrees (sum over PS and average by the number of pixels)
for k = degreeRange
ps_integral(k+1) = mean(PS(degrees == k & R > lowfreq & R < highfreq));
fs_integral(k+1) = mean(FS(degrees == k & R > lowfreq & R < highfreq));
end
thetas = deg2rad(degreeRange);
Cxy = [ps_integral.*cos(thetas);
ps_integral.*sin(thetas)]';
C = Cxy' * Cxy;
[V,D] = eig(C);
lambda2 = D(1,1);
lambda1 = D(2,2);
alpha = 1 - lambda2/lambda1;

affine2d in Octave

In MATLAB I make a cylinder r and shift it to the position I want which is Pos. In MATLAB I use affine2d and imwarp but unfortunately Octave doesn't have these functions.
Does anybody know how I can do this in Octave without affine2d and imwarp?
The MATLAB code is
% get image limits
limX = size(image,1)/2;
limY = size(image,2)/2;
steps = 1;
% add 30% to the inner diameter to make sure it covers the complete sparse area
largeradius = 1.5*diaStart/2;
smallradius = diaStart/2;
% generate x/y points
[x,y] = meshgrid(-limX:steps:limX,-limY:steps:limY);
% calculate the radius values:
r = sqrt(x.^2 + y.^2);
r(r>largeradius) = 0;
r(r<smallradius) = 0;
% Shift translate circle in place
r = im2bw(r);
xPos = (Pos(1)-limX);
yPos = Pos(2)-limY;
tform = affine2d([1 0 0; 0 1 0; xPos yPos 1]);
r_trans = imwarp(r,tform,'OutputView',imref2d(size(image)));
It should be easy to do. affine2d is not a problem, as it is a function that changes the data type, but doesn't modify anything.
imwarp does [x y 1] = [u v 1] * T (being T the affine transformation Matrix) for each of the pixels.
So, if you know that you want the values of the pixels in some specific locations in the transformed image, then its easy to know them
In other words: you have an image r (composed by [u v 1] pixels, a transformation tform and you know that you want to know how a new image is created by that. The new image r_trans is composed by [x y 1] pixels, and you know [x,y,1] values.
Basically, you want to get r(u,v) for each [u,v,1]=[x y 1]*T^(-1);.
x will be 1:size(r,1), and y=1:size(r,2).
Therefore computing [u,v,1]=[x y 1]*T^(-1); is not a problem.
Now, you want to access r(u,v), and u and v wont be integers, they will be floating point values. To be able to get ther` values you will need interpolation.
For that, you need to use this simple piece of code;
[X,Y]=meshgrid(1:size(r,1),1:size(r,2));
value=interp2(X,Y,r,ui,vi,method); %chose method from https://www.gnu.org/software/octave/doc/interpreter/Multi_002ddimensional-Interpolation.html
r_trans(xi,yi)=value;
I didn't give you the whole code, but hopefully you understand how to do it.

Plot a curve with each points specifc distance away (Matlab)

I am trying to draw a curve where each points are specific distance away from each other.
Now, blow shows pretty much what I wanted to do but I want sin like curve and not constant radius.
R = 50; %radius
Gap = 0.1; % gap between points
Curve = 180;
rad = 0;
n= pi*2*R*Curve/360/Gap; % n is length of arc
th = linspace( pi, rad ,n);
x = R*cos(th)+R;
y = R*sin(th)+100;
PathDB.Route1.x(1:1001,1)=0;
PathDB.Route1.y = (0:Gap:100)';
LengthY = length(PathDB.Route1.y);
PathDB.Route1.x(1001:1001+length(x)-1,1)=x ;
PathDB.Route1.y(LengthY:LengthY+length(y)-1) = y;
LengthX = length(PathDB.Route1.x);
LengthY = length(PathDB.Route1.y);
PathDB.Route1.x(LengthX:LengthX+1000,1)=PathDB.Route1.x(LengthX,1);
PathDB.Route1.y(LengthY:LengthY+1000,1)= (PathDB.Route1.y(LengthY,1):-Gap:0);
plot(PathDB.Route1.x, PathDB.Route1.y);
grid ;
axis equal
All I want to do is instead of perfect curve, I want to add sin like curve which are plotted by 0.1.
I'm sorry about my poor coding skills I hope you can understand and help me.
Any advice is appreciated!
Rui
If you want this to be robust, you'll probably need to use the Matlab Symbolic Toolbox. If you take a function f, and want to plot equidistant points along it's curve between a and b, you could do something like this (using sin(x) as the example function):
sym x;
f = sin(x);
g = diff(f); %derivative of f
b=5;
a=0;
%check syntax on this part
arc_length = int(sqrt(1+g^2)); %create function for arc_length
Sc = arc_length(b)-arc_length(a); %calculate total arc length
n = 30 %number of points to plot (not including b)
Sdist = Sc/n; %distance between points
xvals = zeros(1,(n+1)); %initialize array of x values, n+1 to include point at b
Next, we will want to iterate through the intervals to find equidistant points. To do this we take each S_ij where i represents the x value of the low end of the interval and j represents the high end. We start with i=a and stop when i=b. Using the inverse of our arc length equation we can solve for j. Sdist == S_ij => Sdist = arc_length(j) - arc_length(i) => Sdist+arc_length(i)=arc_length(j). Using this, we can compute: j = arc_length_inverse(Sdist+arc_length(i)). One way of implementing this would be as follows:
arc_length_inv = finverse(arc_length);
i=a
xvals(1)=i;
for ii=1:n
j = arc_length_inv(Sdist+arc_length(i)); %calculate high end
i = j %make high end new low end
xvals(ii+1)=i; %put new low end value in x array
end
With this array of x values, you can compute yvals=sin(xvals) and plot(xvals,yvals)
Some of this code probably needs tweaking but the math should be sound (the formula for arc length came from http://en.wikipedia.org/wiki/Arc_length). Mathematically, this should work for any function integrable on [a,b] and whose arc_length function has an inverse; that said, I do not know the capabilities of the int() and finverse() functions.
If you do not need robustness you can follow these steps for your specific function, doing the math by hand and entering the functions manually;

Roberts Operator just makes the image brighter

I posted another question about the Roberts operator, but I decided to post a new one since my code has changed significantly since that time.
My code runs, but it does not generate the correct image, instead the image becomes slightly brighter.
I have not found a mistake in the algorithm, but I know this is not the correct output. If I compare this program's output to edge(<image matrix>,'roberts',<threshold>);, or to images on wikipedia, it looks nothing like the effect of the roberts operator shown there.
code:
function [] = Robertize(filename)
Img = imread(filename);
NewImg = Img;
SI = size(Img);
I_W = SI(2)
I_H = SI(1)
Robertsx = [1,0;0,-1];
Robertsy = [0,-1;1,0];
M_W = 2; % do not need the + 1, I assume the for loop means while <less than or equal to>
% x and y are reversed...
for y=1 : I_H
for x=1 : I_W
S = 0;
for M_Y = 1 : M_W
for M_X = 1 : M_W
if (x + M_X - 1 < 1) || (x + M_X - 1 > I_W)
S = 0;
%disp('out of range, x');
continue
end
if (y + M_Y - 1 < 1) || (y + M_Y - 1 > I_H)
S = 0;
%disp('out of range, y');
continue
end
S = S + Img(y + M_Y - 1 , x + M_X - 1) * Robertsx(M_Y,M_X);
S = S + Img(y + M_Y - 1, x + M_X - 1) * Robertsy(M_Y,M_X);
% It is y + M_Y - 1 because you multiply Robertsx(1,1) *
% Img(y,x).
end
end
NewImg(y,x) = S;
end
end
imwrite(NewImg,'Roberts.bmp');
end
I think you may be misinterpreting how the Roberts Cross operator works. Use this page as a guide. Notice that it states that you convolve the original image separately with the X and Y operator. Then, you may calculate the final gradient (i.e. "total edge content") value by taking the square root of the sum of squares of the two (x and y) gradient values for a particular pixel. You're presently summing the x and y values into a single image, which will not give the correct results.
EDIT
I'll try to explain a bit better. The problem with summation instead of squaring/square root is that you can end up with negative values. Negative values are natural using this operator depending on the edge orientation. That may be why you think the image 'lightens' -- because when you display the image in MATLAB the negative values go to black, the zero values go to grey, and the positive values go to white. Here's the image I get when I run your code (with a few changes -- mostly setting NewImg to be zeros(size(Img)) so it's a double type instead of uint8. uint8 types don't allow negative values... Here's the image I get:.
You have to be very careful when trying to save files as well. Instead of calling imwrite, call imshow(NewImg,[]). That will automatically rescale the values in the double-valued image to show them correctly, with the most negative number being equal to black and most positive equal to white. Thus, in areas with little edge content (like the sky), we would expect grey and that's what we get!
I ran your code and got the effect you described. See how everything looks lighter:
Figure 1 - Original on the left, original roberts transformation on the right
The image on my system was actually saturated. My image was uint8 and the operations were pushing the image past 255 or under 0 (for the negative side) and everything became lighter.
By changing the line of code in the imread to convert to double as in
Img = double(rgb2gray( imread(filename)));
(note my image was color so I did an rgb conversion, too. You might use
Img = double(( imread(filename)));
I got the improved image:
Original on left, corrected code on right.
Note that I could also produce this result using 2d convolution rather than your loop:
Robertsx = [1,0;0,-1];
Robertsy = [0,-1;1,0];
dataR = conv2(data, Robertsx) + conv2(data, Robertsy);
figure(2);
imagesc(dataR);
colormap gray
axis image
For the following result:
Here is an example implementation. You could easily replace CONV2/IMFILTER with your own 2D convolution/correlation function:
%# convolve image with Roberts kernels
I = im2double(imread('lena512_gray.jpg')); %# double image, range [0,1]
hx = [+1 0;0 -1]; hy = [0 +1;-1 0];
%#Gx = conv2(I,hx);
%#Gy = conv2(I,hy);
Gx = imfilter(I,hx,'conv','same','replicate');
Gy = imfilter(I,hy,'conv','same','replicate');
%# gradient approximation
G = sqrt(Gx.^2+Gy.^2);
figure, imshow(G), colormap(gray), title('Gradient magnitude [0,1]')
%# direction of the gradient
Gdir = atan2(Gy,Gx);
figure, imshow(Gdir,[]), title('Gradient direction [-\pi,\pi]')
colormap(hot), colorbar%, caxis([-pi pi])
%# quiver plot
ySteps = 1:8:size(I,1);
xSteps = 1:8:size(I,2);
[X,Y] = meshgrid(xSteps,ySteps);
figure, imshow(G,[]), hold on
quiver(X, Y, Gx(ySteps,xSteps), Gy(ySteps,xSteps), 3)
axis image, hold off
%# binarize gradient, and compare against MATLAB EDGE function
BW = im2bw(G.^2, 6*mean(G(:).^2));
figure
subplot(121), imshow(BW)
subplot(122), imshow(edge(I,'roberts')) %# performs additional thinning step