MATLAB: Sliding colfilt problem with custom function - matlab

I'm trying to do a custom Matlab function using colfilt where if the value of the pixel is black or white (0 or 255) the value will be the median of neighbours. Since I'm using colfilt, that means that the neighbours values are present in the same column and therefore I did this function:
function [Y] = Lab3_2(X)
n = X(5)
if(n ==255 || n ==0)
Y = median(X)
else
Y = n
end
end
And the function gets called with:
Y = uint8(colfilt(Isp,[3 3],'sliding',#Lab3_2))
Where Isp is an image with salt&pepper noise.
The problem is that I get this error:
Error using reshape To RESHAPE the number of elements must not change.
Error in colfilt (line 182)
reshape(feval(fun,x,params{:}),block(1),block(2));
I read the documentation and it says that the function must return a row vector containing a single value for each column in the temporary matrix.
I think i'm not doing this correctly (I'm refering to my output variable Y) and also I'm not sure if the line n = X(5) is correct.
Does anyone know how I can fix it?

If you had a loop iterating over each column of the input matrix X (or, if colfilt passed the columns to your function one by one), your approach would work. To implement the loop, you'd do something like this:
function [Y] = Lab3_2(X)
num_cols = size(X,2); % get number of columns in X
Y = zeros(1,num_cols); % preallocate row vector Y
for c = 1:size(X,2); % iterate over each column of X
Xcol = X(c);
n = Xcol(5) % check whether center pixel is 0 or 255
% assumes a 3x3 neighborhood
if(n ==255 || n ==0)
Y(c) = median(X) % yes, replace with median of column values
else
Y(c) = n % no, use original value
end
end
end
But looping over the columns is unnecessary when median and the comparison operators already work column-wise over the entire matrix. A more concise way to do the same thing would be:
function [Y] = Lab3_2(X)
Y = X(5,:) % initialize Y to current pixel
bw_indices = (Y == 255 | Y == 0); % get indices of 0,255 values
X_median = median(X); % take medians of all columns
% replace 0,255 values with their corresponding medians
Y(bw_indices) = X_median(bw_indices);
end

Related

How do I create a matrix in MATLAB surrounding the center element?

I want to create a matrix (n by n, n being odd) in MATLAB that has its central element fixed, and its surrounding elements increasing/decreasing by some constant value. For example:
where my center element is 0 and the surrounding elements are decrementing by 0.1. I am pretty much blank from where to start exactly. Your time and help is highly appreciated.
This alternative seems a bit faster than the for loop.
n = 7; % size
vector = -abs((1-n)/2:(n-1)/2)/10; % entries in middle row/column
x = min(vector,vector.') % final result
% works for only odd numbers as your requirement
n = 5; % matrix size
r = (n-1)/2; % surrounding rows
x = zeros(n); % array initialization
c = r-1:-1:0;
% assigning values
for i = 1:r
x([1+c(i), end-c(i)], :) = -i/10;
x(:,[1+c(i), end-c(i)]) = -i/10;
end
x % final matrix

Matlab - Applying a function in a neighborhood

Lets say that I have a 250*250 matrix. What I want to do is select a [3 3] neighborhood around every pixel and apply a function to it. Now the problem is that the function will output a 2*2 matrix for every pixel in the neighborhood and then I have to add the result of every pixel and finally get a 2*2 matrix for the selected pixel. So in the end I will get 62500 2*2 matrices. Also, I have to save the 2*2 matrix for every pixel in a 250*250 cell. Because these matrices will be used for further calculations. So any idea how I go about doing this because I cannot use nfilter or colfilt because in those the function must return a scalar. Any advice or suggestions are highly welcome.
You can use nlfilter with a function that returns a cell so the result will be a cell matrix.:
a = rand(10);
result = nlfilter(a,[3 3],#(x){x(1:2,1:2)});
Here's one pattern of how to do this:
% define matrix
N = 250; % dimensionality
M = rand(N); % random square N-by-N matrix
% initialize output cell array
C = cell(N);
% apply the function (assume the function is called your_function)
for row = 1 : N
for col = 1 : N
% determine a 3x3 neighborhood (if on edge of matrix, 2x2)
row_index = max(1, row - 1) : min(N, row + 1);
col_index = max(1, col - 1) : min(N, col + 1);
neighborhood = mat(row_index, col_index);
% apply the function and save to cell
C{row, col} = your_function(neighborhood);
end
end
And here is a simple example of your_function so you can test the above code:
function mat = your_function(mat)
S = size(mat);
if S(1) < 2 || S(2) < 2, error('Bad input'); end
mat = mat(1:2, 1:2);

Matlab rref() function precision error after 12th column of hilbert matrices

My question may be a simple one but I could not think of a logical explanation for my question:
When I use
rref(hilb(8)), rref(hilb(9)), rref(hilb(10)), rref(hilb(11))
it gives me the result that I expected, a unit matrix.
However when it comes to the
rref(hilb(12))
it does not give a nonsingular matrix as expected. I used Wolfram and it gives the unit matrix for the same case so I am sure that it should have given a unit matrix. There may be a round off error or something like that but then 1/11 or 1/7 have also some troublesome decimals
so why does Matlab behave like this when it comes to 12?
It indeed seems like a precision error. This makes sense as the determinant of Hilbert matrix of order n tends to 0 as n tends to infinity (see here). However, you can use rref with tol parameter:
[R,jb] = rref(A,tol)
and take tol to be very small to get more precise results. For example, rref(hilb(12),1e-20)
will give you identity matrix.
EDIT- more details regarding the role of the tol parameter.
The source code of rref is provided at the bottom of the answer. The tol is used when we search for a maximal element in absolute value in a certain part of a column, to find the pivot row.
% Find value and index of largest element in the remainder of column j.
[p,k] = max(abs(A(i:m,j))); k = k+i-1;
if (p <= tol)
% The column is negligible, zero it out.
A(i:m,j) = zeros(m-i+1,1);
j = j + 1;
If all the elements are smaller than tol in absolute value, the relevant part of the column is filled by zeros. This seems to be where the precision error for rref(hilb(12)) occurs. By reducing the tol we avoid this issue in rref(hilb(12),1e-20).
source code:
function [A,jb] = rref(A,tol)
%RREF Reduced row echelon form.
% R = RREF(A) produces the reduced row echelon form of A.
%
% [R,jb] = RREF(A) also returns a vector, jb, so that:
% r = length(jb) is this algorithm's idea of the rank of A,
% x(jb) are the bound variables in a linear system, Ax = b,
% A(:,jb) is a basis for the range of A,
% R(1:r,jb) is the r-by-r identity matrix.
%
% [R,jb] = RREF(A,TOL) uses the given tolerance in the rank tests.
%
% Roundoff errors may cause this algorithm to compute a different
% value for the rank than RANK, ORTH and NULL.
%
% Class support for input A:
% float: double, single
%
% See also RANK, ORTH, NULL, QR, SVD.
% Copyright 1984-2005 The MathWorks, Inc.
% $Revision: 5.9.4.3 $ $Date: 2006/01/18 21:58:54 $
[m,n] = size(A);
% Does it appear that elements of A are ratios of small integers?
[num, den] = rat(A);
rats = isequal(A,num./den);
% Compute the default tolerance if none was provided.
if (nargin < 2), tol = max(m,n)*eps(class(A))*norm(A,'inf'); end
% Loop over the entire matrix.
i = 1;
j = 1;
jb = [];
while (i <= m) && (j <= n)
% Find value and index of largest element in the remainder of column j.
[p,k] = max(abs(A(i:m,j))); k = k+i-1;
if (p <= tol)
% The column is negligible, zero it out.
A(i:m,j) = zeros(m-i+1,1);
j = j + 1;
else
% Remember column index
jb = [jb j];
% Swap i-th and k-th rows.
A([i k],j:n) = A([k i],j:n);
% Divide the pivot row by the pivot element.
A(i,j:n) = A(i,j:n)/A(i,j);
% Subtract multiples of the pivot row from all the other rows.
for k = [1:i-1 i+1:m]
A(k,j:n) = A(k,j:n) - A(k,j)*A(i,j:n);
end
i = i + 1;
j = j + 1;
end
end
% Return "rational" numbers if appropriate.
if rats
[num,den] = rat(A);
A=num./den;
end

How can all dimensions left after the specified one be preserved, without explicitly listing them?

Or equivalently, "what is the equivalent of NumPy's ellipsis indexing in Matlab"
Say I have some high-dimensional array:
x = zeros(3, 4, 5, 6);
I want to write a function that takes an array of size (3, ...), and does some computation. In NumPy, I could write this:
def fun(x):
return x[0]*x[1] + x[2]
However, the equivalent in MATLAB doesn't work, because indexing with one integer flattens the array to 1d
function y = fun_bad(x)
y = x(1)*x(2) + x(3)
I can make this work for up to 3-dimensional arrays with
function y = fun_ok3d(x)
y = x(1,:,:)*x(2,:,:) + x(3,:,:)
If I want this to work for up to 10-dimensional arrays, I can write
function y = fun_ok10d(x)
y = x(1,:,:,:,:,:,:,:,:,:)*x(2,:,:,:,:,:,:,:,:,:) + x(3,:,:,:,:,:,:,:,:,:)
How can I avoid writing stupid numbers of colons here, and just make this work for any dimension? Is there some x(1,...) syntax that implies this?
NumPy can use the ... (Ellipsis) literal in an indexing expression to mean ": as many times as needed", which would solve this problem.
Approach 1: using a comma-separated list with ':'
I don't know a way to specify
: as many times as needed
while preserving shape. But you can specify
: an arbitrary number of times
where that number of times is defined at run-time. With this method you can preserve shape, provided that the number of indices coincides with the number of dimensions.
This is done using a comma-separated list generated from a cell array, and exploiting the fact that the string ':' can be used as an index instead of ::
function y = fun(x)
colons = repmat({':'}, 1, ndims(x)-1); % row cell array containing the string ':'
% repeated the required number of times
y = x(1,colons{:}).*x(2,colons{:}) + x(3,colons{:});
This approach can be easily generalized to indexing along any dimension, not just the first:
function y = fun(x, dim)
% Input argument dim is the dimension along which to index
colons_pre = repmat({':'}, 1, dim-1);
colons_post = repmat({':'}, 1, ndims(x)-dim);
y = x(colons_pre{:}, 1, colons_post{:}) ...
.*x(colons_pre{:}, 2, colons_post{:}) ...
+ x(colons_pre{:}, 3, colons_post{:});
Approach 2: splitting the array
You can split the array along the first dimension using num2cell, and then apply the operation to the resulting subarrays. Of course this uses more memory; and as noted by #Adriaan it is slower.
function y = fun(x)
xs = num2cell(x, [2:ndims(x)]); % x split along the first dimension
y = xs{1}.*xs{2} + xs{3};
Or, for indexing along any dimension:
function y = fun(x, dim)
xs = num2cell(x, [1:dim-1 dim+1:ndims(x)]); % x split along dimension dim
y = xs{1}.*xs{2} + xs{3};
MATLAB flattens all trailing dimensions when using a single colon, so you can use that to get from your N-D array to a 2D array, which you can reshape back into the original N dimensions after the calculation.
Along the first dimension
If you want to use the first dimension you can use a relatively simple and short piece of code:
function y = MyMultiDimensional(x)
x_size = size(x); % Get input size
yflat = x(1,:) .* x(2,:) + x(3,:); % Calculate "flattened" 2D function
y = reshape(yflat, [1 x_size(2:end)]); % Reshape output back to original size
end
Along an arbitrary dimension, now featuring N-D permute.
When you want your function to act along the n-th dimension out of a total of N, you can permute that dimension to the front first:
function y = MyMultiDimensional(x,n)
x_size = size(x); % Get input size
Order = 1:numel(x_size);
Order(n)=[]; % Remove n-th dimension
Order2 = [n, Order]; % Prepend n-th dimension
xPermuted = permute(x,Order2); % permute the n-th dimension to the front
yTmp = xPermuted (1,:) .* xPermuted (2,:) + xPermuted (3,:); % Calculate "flattened" 2D function
y = reshape(yTmp, x_size(Order)); % Reshape output back to original size
end
I timed the results of the two methods of Luis' and my methods:
function timeMultiDim()
x = rand(1e1,1e1,1e1,1e1,1e1,1e1,1e1,1e1);
function y = Luis1(x)
colons = repmat({':'}, 1, ndims(x)-1); % row cell array containing the string ':'
% repeated the required number of times
y = x(1,colons{:}).*x(2,colons{:}) + x(3,colons{:});
end
function y = Luis2(x)
xs = num2cell(x, [2:ndims(x)]); % x split along the first dimension
y = xs{1}.*xs{2} + xs{3};
end
function y = Adriaan(x)
x_size = size(x); % Get input size
yflat = x(1,:) .* x(2,:) + x(3,:); % Calculate "flattened" 2D function
y = reshape(yflat, [1 x_size(2:end)]); % Reshape output back to original size
end
n=1;
function y = Adriaan2(x,n)
x_size = size(x); % Get input size
Order = 1:numel(x_size);
Order(n)=[]; % Remove n-th dimension
Order2 = [n, Order]; % Prepend n-th dimension
xPermuted = permute(x,Order2); % permute the n-th dimension to the front
yTmp = xPermuted (1,:) .* xPermuted (2,:) + xPermuted (3,:); % Calculate "flattened" 2D function
y = reshape(yTmp, x_size(Order)); % Reshape output back to original size
end
t1 = timeit(#() Luis1(x));
t2 = timeit(#() Luis2(x));
t3 = timeit(#() Adriaan(x));
t4 = timeit(#() Adriaan2(x,n));
format long g;
fprintf('Luis 1: %f seconds\n', t1);
fprintf('Luis 2: %f seconds\n', t2);
fprintf('Adriaan 1: %f seconds\n', t3);
fprintf('Adriaan 2: %f seconds\n', t4);
end
Luis 1: 0.698139 seconds
Luis 2: 4.082378 seconds
Adriaan 1: 0.696034 seconds
Adriaan 2: 0.691597 seconds
So, going to a cell is bad, it takes more than 5 times as long, reshape and ':' are barely apart, so that'd come down to preference.

Differentiation from FFT finding extrema

I'm trying to find zeros of a function. See my code below.
Because fft expects a numerical array, I didn't define the symbolic function to use fzero.
However, this approach is not accurate and depend on step. Do you have a better idea?
step=2000;
x=0:pi/step:2*pi;
y= 4+5*cos(10*x)+20*cos(40*x)+cos(100*x);
fy = fft(y');
fy(1:8) =0;
fy(12:end-10)=0;
fy(end-6:end)=0;
ffy = ifft(fy);
t=diff(ffy);
x=0:pi/step:2*pi-pi/step;
plot(x,t)
indices= find(t<5e-4 & t>-5e-4);
You could proceed along the array t and look for points where the values change sign. That would indicate the presence of a zero.
Actaully, MATLAB's fzero function uses a similar method. You said you didn't use it because you required an array, rather than an anonymous function, but you could convert the array into an anonymous function using simple linear interpolation like so:
func = #(k) interp1(x,t,k); % value of t(x) interpolated at x=k
fzero(func,initial_value);
EDIT : Just to clarify what I mean. If you have an array t and you want to find its zeros...
f = 5; % frequency of wave in Hz
x = 0:0.01:1; % time index
t = cos( 2*pi*f*x ); % cosine wave of frequency f
zeroList = []; % start with an empty list of zeros
for i = 2:length(t) % loop through the array t
current_t = t(i); % current value of t
previous_t = t(i-1); % previous value of t
if current_t == 0 % the case where the zero is exact
newZero = x(i);
zeroList = [zeroList,newZero];
elseif current_t*previous_t < 0 % a and b have opposite sign if a*b is -ve
% do a linear interpolation to find the zero (solve y=mx+b)
slope = (current_t-previous_t)/(x(i)-x(i-1));
newZero = x(i) - current_t/slope;
zeroList = [zeroList,newZero];
end
end
figure(1); hold on;
axis([ min(x) max(x) -(max(abs(t))) (max(abs(t))) ]);
plot(x,t,'b');
plot(x,zeros(length(x)),'k-.');
scatter(zeroList,zeros(size(zeroList)),'ro');
The zeros I get are correct: