Design feature matrix of 8-neighborhood elements - matlab

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{:}).';

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 image translation matrix

I'm very new to Matlab. I'm learning some image manipulation basics, and I'm a bit confused on how to write a translation without using imtranslate.
this is my code but it just displays a black background. Thank you.
img = imread('name2.png');
figure(1);
% pixel matrix
[orig_x, orig_y,z] = size(img);
final_x = 600;
final_y = 600;
% define the final array with calculated dimensions and fill the array with zeros ie.,black
final_img = uint8(zeros([final_x final_y 3 ]));
for i = 1 : size(final_img, 1)
for j = 1 : size(final_img, 2)
new_x = img(i) + 5;
new_y = img(j) + 5;
% fprintf('X: %f\n',new_x); % prints 255
final_img(i) = new_x;
final_img(j) = new_y;
end
end
imshow(final_img);
This is one solution for 'translation only' transformation.
I = imread('Lenna.png');
shiftX = 5; % shift columns
shiftY = 5; % shift rows
% Assigning empty matrix for result, expected to be shiftX-1 larger in rows and shiftY-1 larger in columns
nI = uint8( zeros(size(I,1)+shiftY-1, size(I,2)+shiftX-1, size(I,3));
% Translate
nI(shiftY:end, shiftX:end, :) = I;
imshow(nI)
Now the image will start from (x,y) = (5,5) instead of (1,1). Also note that in matlab image coordinate system, x and y axis start from upper left corner (documentation).
final_img:
You've defined "final_img" with new x and new y but you haven't replaced the zeros in the red/green/blue values. It's all black because of your initialisation filling the final_img with all zeros.
Maybe try this instead of what you've written:
%{
[X,map] = imread('name2.png');
figure(1);
% X should be 600 by 600
%Translate X however you wish, e.g.:
X = X +5;
%Verify that the colormap, map, is not empty, and convert
%the data in X to RGB and store as your final_img.
if ~isempty(map)
final_img = ind2rgb(X,map);
end
%}
I am also not sure if you want to be indexing img with just a single i without the the other dimensions like you have:
new_x = img(i) + 5;
?
For the problems in your specific code, I wrote in the comments some of them.
A short way to achieve image translation is by 2D convolution with a filter of zeros and just one 1, that will preserve the values of the image, but relocate them according to the size of the filter and the position of the 1 in it.
That seems you want to move the image but preserve the size of the total image, if I get it right. So:
r=3; c=5; % number of rows and columns to move
filt=zeros(r*2+1, c*2+1); filt(end)=1; % the filetr
img2=conv2(img,filt,'same'); % the translated image
Just for the example, lets translate "cameraman" with 20 rows and columns:
img=imread('cameraman.tif');
imshow(img)
r=20; c=20;
filt=zeros(r*2+1, c*2+1); filt(end)=1;
img2=conv2(img,filt,'same');
figure; imshow(img2,[])

Is there a way to vectorize this loop in MATLAB?

I wish to vectorize this for loop. This loop is about getting the coordinates of image pixels and form an array in a row by row order.
rows = 812; % 812x650 image
cols = 650;
n=rows*cols; % total number of pixels
index = zeros(n,2); % n coordinates of image pixels
pt_homo = zeros(3,1,n); % [x,y,1]'
k=1;
for r=1:rows
for c=1:cols
index(k,1)=c;
index(k,2)=r;
pt_homo(1,1,k) = c;
pt_homo(2,1,k) = r;
pt_homo(3,1,k) = 1;
k=k+1;
end
end
So if i understand your question correctly this should solve it
c = 1:cols;
r = 1:rows;
[X Y] = meshgrid(r,c);
index = [Y(:) X(:)];
pt_homo_ = permute([index ones(size(index,1),1)],[2 3 1]);
Basically what i did is create the index vectors and create a matrix of indexes using meshgrid and then reorder it to be in the format you wanted.

Sorting two column vectors into 3D matrix based on position

Using the imfindcircles function in MATLAB to track circles in two images. I start with approximately a grid of circles which deforms. I am trying to sort the two column vector from imfindcircles into matrices so that neighbouring circles are neighbouring elements in the matrices. The first image the circles conform to a grid and the following code works:
[centXsort,IX] = sortrows(centres1,1); %sort by x
centYsort =zeros(289,2); %preallocate
for i = 1:17:289
[sortedY,IY] = sortrows(centXsort(i:i+16,:),2); %sort by y within individual column
centYsort(i:i+16,:) = sortedY;
end
cent1mat = reshape(centYsort,17,17,2); %reshape into centre matrices
This doesn't work for the second image as some of the circles overlap in the x or y direction, but neighbouring circles never overlap. This means that in the second set of matrices the neighbouring circles aren't neighbouring elements after sorting.
Is there a way to approximate a scatter of points into a matrix?
This answer doesn't work in every single case, but it seems good enough for situations where the points don't vary too wildly.
My idea is to start at the grid corners and work our way along the outside diagonals of the matrix, trying to "grab" the nearest points that seem like they fit into the grid-points based any surrounding points we've already captured.
You will need to provide:
The number of rows (rows) and columns (cols) in the grid.
Your data points P arranged in a N x 2 array, rescaled to the unit square on [0,1] x [0,1]. (I assume the you can do this through visual inspection of the corner points of your original data.)
A weight parameter edge_weight to tell the algorithm how much the border points should be attracted to the grid border. Some tests show that 3-5 or so are good values.
The code, with a test case included:
%// input parameters
rows = 11;
cols = 11;
edge_weight = 4;
%// function for getting squared errors between the points list P and a specific point pt
getErr =#(P,pt) sqrt( sum( bsxfun(#minus,P,pt(:)').^2, 2 ) ); %'
output_grid = zeros(rows,cols,2); %// output grid matrix
check_grid = zeros(rows,cols); %// matrix flagging the gridpoints we have covered
[ROW,COL] = meshgrid(... %// coordinate points of an "ideal grid"
linspace(0,1,rows),...
linspace(0,1,cols));
%// create a test case
G = [ROW(:),COL(:)]; %// the actual grid-points
noise_factor = 0.35; %// noise radius allowed
rn = noise_factor/rows;
cn = noise_factor/cols;
row_noise = -rn + 2*rn*rand(numel(ROW),1);
col_noise = -cn + 2*cn*rand(numel(ROW),1);
P = G + [row_noise,col_noise]; %// add noise to get points
%// MAIN LOOP
d = 0;
while ~isempty(P) %// while points remain...
d = d+1; %// increase diagonal depth (d=1 are the outer corners)
for ii = max(d-rows+1,1):min(d,rows)%// for every row number i...
i = ii;
j = d-i+1; %// on the dth diagonal, we have d=i+j-1
for c = 1:4 %// repeat for all 4 corners
if i<rows & j<cols & ~check_grid(i,j) %// check for out-of-bounds/repetitions
check_grid(i,j) = true; %// flag gridpoint
current_gridpoint = [ROW(i,j),COL(i,j)];
%// get error between all remaining points and the next gridpoint's neighbours
if i>1
errI = getErr(P,output_grid(i-1,j,:));
else
errI = edge_weight*getErr(P,current_gridpoint);
end
if check_grid(i+1,j)
errI = errI + edge_weight*getErr(P,current_gridpoint);
end
if j>1
errJ = getErr(P,output_grid(i,j-1,:));
else
errJ = edge_weight*getErr(P,current_gridpoint);
end
if check_grid(i,j+1)
errJ = errJ + edge_weight*getErr(P,current_gridpoint);
end
err = errI.^2 + errJ.^2;
%// find the point with minimal error, add it to the grid,
%// and delete it from the points list
[~,idx] = min(err);
output_grid(i,j,:) = permute( P(idx,:), [1 3 2] );
P(idx,:) = [];
end
%// rotate the grid 90 degrees and repeat for next corner
output_grid = cat(3, rot90(output_grid(:,:,1)), rot90(output_grid(:,:,2)));
check_grid = rot90(check_grid);
ROW = rot90(ROW);
COL = rot90(COL);
end
end
end
Code for plotting the resulting points with edges:
%// plotting code
figure(1); clf; hold on;
axis([-0.1 1.1 -0.1 1.1])
for i = 1:size(output_grid,1)
for j = 1:size(output_grid,2)
scatter(output_grid(i,j,1),output_grid(i,j,2),'b')
if i < size(output_grid,1)
plot( [output_grid(i,j,1),output_grid(i+1,j,1)],...
[output_grid(i,j,2),output_grid(i+1,j,2)],...
'r');
end
if j < size(output_grid,2)
plot( [output_grid(i,j,1),output_grid(i,j+1,1)],...
[output_grid(i,j,2),output_grid(i,j+1,2)],...
'r');
end
end
end
I've developed a solution, which works for my case but might not be as robust as required for some. It requires a known number of dots in a 'square' grid and a rough idea of the spacing between the dots. I find the 'AlphaShape' of the dots and all the points that lie along the edge. The edge vector is shifted to start at the minimum and then wrapped around a matrix with the corresponding points are discarded from the list of vertices. Probably not the best idea for large point clouds but good enough for me.
R = 50; % search radius
xy = centres2;
x = centres2(:,1);
y = centres2(:,2);
for i = 1:8
T = delaunay(xy); % delaunay
[~,r] = circumcenter(triangulation(T,x,y)); % circumcenters
T = T(r < R,:); % points within radius
B = freeBoundary(triangulation(T,x,y)); % find edge vertices
A = B(:,1);
EdgeList = [x(A) y(A) x(A)+y(A)]; % find point closest to origin and rotate vector
[~,I] = min(EdgeList);
EdgeList = circshift(EdgeList,-I(3)+1);
n = sqrt(length(xy)); % define zeros matrix
matX = zeros(n); % wrap x vector around zeros matrix
matX(1,1:n) = EdgeList(1:n,1);
matX(2:n-1,n) = EdgeList(n+1:(2*n)-2,1);
matX(n,n:-1:1) = EdgeList((2*n)-1:(3*n)-2,1);
matX(n-1:-1:2,1) = EdgeList((3*n)-1:(4*n)-4,1);
matY = zeros(n); % wrap y vector around zeros matrix
matY(1,1:n) = EdgeList(1:n,2);
matY(2:n-1,n) = EdgeList(n+1:(2*n)-2,2);
matY(n,n:-1:1) = EdgeList((2*n)-1:(3*n)-2,2);
matY(n-1:-1:2,1) = EdgeList((3*n)-1:(4*n)-4,2);
centreMatX(i:n+i-1,i:n+i-1) = matX; % paste into main matrix
centreMatY(i:n+i-1,i:n+i-1) = matY;
xy(B(:,1),:) = 0; % discard values
xy = xy(all(xy,2),:);
x = xy(:,1);
y = xy(:,2);
end
centreMatX(centreMatX == 0) = x;
centreMatY(centreMatY == 0) = y;