Matlab - Applying a function in a neighborhood - matlab

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);

Related

Design feature matrix of 8-neighborhood elements

I have a matrix (image) of size m x n that I am trying to rearrange differently. I want to design a feature matrix of size (m x n) x 9 where each row corresponds to a matrix element centered around its 8-neighborhood elements. My attempt was to sequentially loop through each element of the original matrix to extract the neighborhood values, however this method is too computationally heavy and takes too long to perform as the matrix size is exhaustively large. Is there anyway to do this cost-beneficially?
Attempt
M_feat = nan(size(img,1)*size(img,2), 9);
temp = zeros(size(img)+2);
temp(2:end-1,2:end-1) = double(img);
for i = 2:size(img,1)+1
for j = 2:size(img,2)+1
neighbors = temp(i-1:i+1, j-1:j+1); % read 3-by-3 mini-matrix
neighbors = reshape(neighbors, 1, []);
M_feat((i-2)*size(img,1) + (j-1),:) = neighbors; % store row-wise
end
end
Got it!
new_img = zeros(size(img)+2);
new_img(2:end-1,2:end-1) = double(img);
% Image boundary coordinates without first/last row/column
inner_coord = [2 2; size(new_img,1)-1 size(new_img,2)-1];
% 9x2 matrix with 1 row for the relative shift to reach neighbors
[d2, d1] = meshgrid(-1:1, -1:1);
d = [d1(:) d2(:)];
% Cell array to store all 9 shifted images
temp = {};
for i = 1:size(d,1)
% x-indices of the submatrix when shifted by d(i,1)
coord_x = (inner_coord(1,1):inner_coord(2,1)) + d(i,1);
% y-indices of the submatrix when shifted by d(i,2)
coord_y = (inner_coord(1,2):inner_coord(2,2)) + d(i,2);
% image matrix resulting from shift by d(i,)
temp{i} = reshape(new_img(coord_x, coord_y), 1, []);
end
% Column-wise bind all 9 shifted images (as vectors) from the array
M_feat = vertcat(temp{:}).';

MATLAB: Sliding colfilt problem with custom function

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

MATLAB: Multiply 2D matrix with 3D matrix within cell arrays

I have a constant 2D double matrix mat1. I also have a 2D cell array mat2 where every cell contains a 2D or 3D double matrix. These double matrices have the same number of rows and columns as mat1. I need to dot multiply (.*) mat1 with every slice of each double matrix within mat2. The result needs to be another cell array results with the same size as mat2, whereby the contatining double matrices must equal the double matrices of mat2 in terms of size.
Here's my code to generate mat1 and mat2 for illustrating purposes. I am struggling at the point where the multiplication should take place.
rowCells = 5;
colCells = 3;
rowTimeSeries = 300;
colTimeSeries = 5;
slices = [1;10];
% Create 2D double matrix
mat1 = rand(rowTimeSeries, colTimeSeries);
% Create 2D cell matrix comprisiong 2D and/or 3D double matrices
mat2 = cell(rowCells,colCells);
for c = 1:colCells
for r = 1:rowCells
slice = randsample(slices, 1, true);
mat2{r,c} = rand(rowTimeSeries, colTimeSeries, slice);
end
end
% Multiply (.*) mat1 with mat2 (every slice)
results = cell(rowCells,colCells);
for c = 1:colCells
for r = 1:rowCells
results{r,c} = ... % I am struggling here!!!
end
end
You could use bsxfun to remove the need for your custom function multiply2D3D, it works in a similar way! Updated code:
results = cell(rowCells,colCells);
for c = 1:colCells
for r = 1:rowCells
results{r,c} = bsxfun(#times, mat1, mat2{r,c});
end
end
This will work for 2D and 3D matrices where the number of rows and cols is the same in each of your "slices", so it should work in your case.
You also don't need to loop over the rows and the columns of your cell array separately. This loop has the same number of iterations, but it is one loop not two, so the code is a little more streamlined:
results = cell(size(mat2));
for n = 1:numel(mat2) % Loop over every element of mat2. numel(mat2) = rowCells*colCells
results{n} = bsxfun(#times, mat1, mat2{n});
end
I had almost the exact same answer as Wolfie but he beat me to it.
Anyway, here is a one liner that I think is slightly nicer:
nR = rowCells; % Number of Rows
nC = colCells; % Number of Cols
results = arrayfun(#(I) bsxfun(#times, mat1, mat2{I}), reshape(1:nR*nC,[],nC), 'un',0);
This uses arrayfun to perform the loop indexing and bsxfun for the multiplications.
A few advantages
1) Specifying 'UniformOutput' ('un') in arrayfun returns a cell array so the results variable is also a cell array and doesn't need to be initialised (in contrast to using loops).
2) The dimensions of the indexes determine the dimensions of results at the output, so they can match what you like.
3) The single line can be used directly as an input argument to a function.
Disadvantage
1) Can run slower than using for loops as Wolfie pointed out in the comments.
One solution I came up with is to outsource the multiplication of a 2D with a 3D matrix into a function. However, I am curious to know whether this is the most efficient way to solve this problem?
rowCells = 5;
colCells = 3;
rowTimeSeries = 300;
colTimeSeries = 5;
slices = [1;10];
% Create 2D double matrix
mat1 = rand(rowTimeSeries, colTimeSeries);
% Create 2D cell matrix comprisiong 2D and/or 3D double matrices
mat2 = cell(rowCells,colCells);
for c = 1:colCells
for r = 1:rowCells
slice = randsample(slices, 1, true);
mat2{r,c} = rand(rowTimeSeries, colTimeSeries, slice);
end
end
% Multiply (.*) mat1 with mat2 (every slice)
results = cell(rowCells,colCells);
for c = 1:colCells
for r = 1:rowCells
results{r,c} = multiply2D3D(mat1, mat2{r,c});
end
end
function vout = multiply2D3D(mat2D, mat3D)
%MULTIPLY2D3D multiplies a 2D double matrix with every slice of a 3D
% double matrix.
%
% INPUTs:
% mat2D:
% 2D double matrix
%
% mat3D:
% 3D double matrix where the third dimension is equal or greater than 1.
%
% OUTPUT:
% vout:
% 3D double matrix with the same size as mat3D. Every slice in vout
% is the result of a multiplication of mat2D with every individual slice
% of mat3D.
[rows, cols, slices] = size(mat3D);
vout = zeros(rows, cols, slices);
for s = 1 : slices
vout(:,:,s) = mat2D .* mat3D(:,:,s);
end
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.

Filling in a Cell of Matrices in MATLAB

In Matlab I am trying to create a cell of size 16 x1 where each entry of this cell is a matrix. I have the following equation
$$W_g = exp^{\frac{j{2\pi m}{N}(n+\frac{g}{G}))} \,\,\,\,\,\,\, m,n=0,1,.....(N-1)$$
for this work assume $N=4$ and the index $g$ is the index that refers to the cell element i.e g=0:1:15
W=cell(16,1);
for g=1:16
for m=1:3
for n=1:3
W{g,m,n}= exp((2*pi*j*m/4)* n+(g-1)/16));
end
end
end
How can I make this work? I have two problems with this, you see g starts from 0 and MATLAB doesnt accept index of zero and how to actually define the matrices within the cell.
Thanks
So if I understand you have this equation:
And you just want the following code:
W=cell(16,1);
n = 1:3;
m = 1:3;
N = 4;
for g=1:16
W{g}= exp((2*pi*j.*m/4*N).*n+(g-1)/16);
end
%or the 1 line version:
W = cellfun(#(g) exp((2*pi*j.*m/4*N).*n+(g-1)/16),num2cell([1:16]),'UniformOutput',0);
With matlab you can use the Element-wise multiplication symbol .*
For example:
%A matrix multiplication
A = [2,3]
B = [1,3]';
result = A * B %result = 11
%An element wise multiplication
A = [2,3]
B = [1,3];
result = A .* B %result = [2,9]
First of all, i is the complex number in matlab (sqrt(-1)) not j, and you are correct, matlab is indexed in 1, so simply start counting g at 1, until 16.
Next, create a zero matrix, and calculate all indices accordingly. Something like this should work just fine :
clc
clear all
W=cell(16,1);
for g=1:16;
temp = zeros(3,3);
for m=1:3
for n=1:3
temp (m,n) = exp((2*pi*1i*m/4)* n+g/16);
end
end
W{g} = temp;
end
if you are considering doing much larger operations, consider using linspace to create your m and n indices and using matrix operations