Nearest-neighbor interpolation algorithm in MATLAB - matlab

I am trying to write my own function for scaling up an input image by using the Nearest-neighbor interpolation algorithm. The bad part is I am able to see how it works but cannot find the algorithm itself. I will be grateful for any help.
Here's what I tried for scaling up the input image by a factor of 2:
function output = nearest(input)
[x,y]=size(input);
output = repmat(uint8(0),x*2,y*2);
[newwidth,newheight]=size(output);
for i=1:y
for j=1:x
xloc = round ((j * (newwidth+1)) / (x+1));
yloc = round ((i * (newheight+1)) / (y+1));
output(xloc,yloc) = input(j,i);
end
end
Here is the output after Mark's suggestion

This answer is more explanatory than trying to be concise and efficient. I think gnovice's solution is best in that regard. In case you are trying to understand how it works, keep reading...
Now the problem with your code is that you are mapping locations from the input image to the output image, which is why you are getting the spotty output. Consider an example where input image is all white and output initialized to black, we get the following:
What you should be doing is the opposite (from output to input). To illustrate, consider the following notation:
1 c 1 scaleC*c
+-----------+ 1 +----------------------+ 1
| | | | | |
|----o | <=== | | |
| (ii,jj) | |--------o |
+-----------+ r | (i,j) |
inputImage | |
| |
+----------------------+ scaleR*r
ouputImage
Note: I am using matrix notation (row/col), so:
i ranges on [1,scaleR*r] , and j on [1,scaleC*c]
and ii on [1,r], jj on [1,c]
The idea is that for each location (i,j) in the output image, we want to map it to the "nearest" location in the input image coordinates. Since this is a simple mapping we use the formula that maps a given x to y (given all the other params):
x-minX y-minY
--------- = ---------
maxX-minX maxY-minY
in our case, x is the i/j coordinate and y is the ii/jj coordinate. Therefore substituting for each gives us:
jj = (j-1)*(c-1)/(scaleC*c-1) + 1
ii = (i-1)*(r-1)/(scaleR*r-1) + 1
Putting pieces together, we get the following code:
% read a sample image
inputI = imread('coins.png');
[r,c] = size(inputI);
scale = [2 2]; % you could scale each dimension differently
outputI = zeros(scale(1)*r,scale(2)*c, class(inputI));
for i=1:scale(1)*r
for j=1:scale(2)*c
% map from output image location to input image location
ii = round( (i-1)*(r-1)/(scale(1)*r-1)+1 );
jj = round( (j-1)*(c-1)/(scale(2)*c-1)+1 );
% assign value
outputI(i,j) = inputI(ii,jj);
end
end
figure(1), imshow(inputI)
figure(2), imshow(outputI)

A while back I went through the code of the imresize function in the MATLAB Image Processing Toolbox to create a simplified version for just nearest neighbor interpolation of images. Here's how it would be applied to your problem:
%# Initializations:
scale = [2 2]; %# The resolution scale factors: [rows columns]
oldSize = size(inputImage); %# Get the size of your image
newSize = max(floor(scale.*oldSize(1:2)),1); %# Compute the new image size
%# Compute an upsampled set of indices:
rowIndex = min(round(((1:newSize(1))-0.5)./scale(1)+0.5),oldSize(1));
colIndex = min(round(((1:newSize(2))-0.5)./scale(2)+0.5),oldSize(2));
%# Index old image to get new image:
outputImage = inputImage(rowIndex,colIndex,:);
Another option would be to use the built-in interp2 function, although you mentioned not wanting to use built-in functions in one of your comments.
EDIT: EXPLANATION
In case anyone is interested, I thought I'd explain how the solution above works...
newSize = max(floor(scale.*oldSize(1:2)),1);
First, to get the new row and column sizes the old row and column sizes are multiplied by the scale factor. This result is rounded down to the nearest integer with floor. If the scale factor is less than 1 you could end up with a weird case of one of the size values being 0, which is why the call to max is there to replace anything less than 1 with 1.
rowIndex = min(round(((1:newSize(1))-0.5)./scale(1)+0.5),oldSize(1));
colIndex = min(round(((1:newSize(2))-0.5)./scale(2)+0.5),oldSize(2));
Next, a new set of indices is computed for both the rows and columns. First, a set of indices for the upsampled image is computed: 1:newSize(...). Each image pixel is considered as having a given width, such that pixel 1 spans from 0 to 1, pixel 2 spans from 1 to 2, etc.. The "coordinate" of the pixel is thus treated as the center, which is why 0.5 is subtracted from the indices. These coordinates are then divided by the scale factor to give a set of pixel-center coordinates for the original image, which then have 0.5 added to them and are rounded off to get a set of integer indices for the original image. The call to min ensures that none of these indices are larger than the original image size oldSize(...).
outputImage = inputImage(rowIndex,colIndex,:);
Finally, the new upsampled image is created by simply indexing into the original image.

MATLAB has already done it for you. Use imresize:
output = imresize(input,size(input)*2,'nearest');
or if you want to scale both x & y equally,
output = imresize(input,2,'nearest');

You just need a more generalized formula for calculating xloc and yloc.
xloc = (j * (newwidth+1)) / (x+1);
yloc = (i * (newheight+1)) / (y+1);
This assumes your variables have enough range for the multiplication results.

Related

How do I write a 3D histogram code without the Matlab built in functions?

I want to creat a histogram code, knowing that it'll be counting the number of occurence of 3 values of a pixel.
The idea is I have 3 matrices (L1im, L2im, L3im) representing information extracted from an image, size of each of them is 256*226, and I want to compute how many times a combination of let's say (52,6,40) occurs (each number correspends to a matrix/component but they're all of the same pixel).
I have tried this, but it doesn’t produce the right result:
for i = 1 : 256
for j = 1 : 256
for k = 1 : 256
if (L1im == i) & (L2im == j) & (L3im == k)
myhist(i,j,k)= myhist(i,j,k)+1;
end
end
end
end
Colour Triplets Histogram
Keeping in mind doing an entire RGB triplet histogram is a large task since you can have 256 × 256 × 256 = 16,777,216 combinations of possible unique colours. A slightly more manageable task I believe is to compute the histogram for the unique RGB values in the image (since the rest will be zero anyways). This is still a fairly large task but might be reasonable if the image is fairly small. Below I believe a decent alternative to binning is to resize the image to a smaller number of pixels. This can be done by using the imresize function. I believe this will decrease fidelity of the image and almost act as a rounding function which can "kinda" simulate the behaviour of binning. In this example I convert the matrices string arrays an concatenate the channels, L1im, L2im and L3im of the image. Below is a demo where I use the image built into MATLAB named saturn.png. A Resize_Factor
of 1 will result in the highest amount of bins and the number of bins will decrease as the Resize_Factor increases. Keep in mind that the histogram might require scaling if the image is being resized with the Resize_Factor.
Resize_Factor = 200;
RGB_Image = imread("saturn.png");
[Image_Height,Image_Width,Number_Of_Colour_Channels] = size(RGB_Image);
Number_Of_Pixels = Image_Height*Image_Width;
RGB_Image = imresize(RGB_Image,[Image_Height/Resize_Factor Image_Width/Resize_Factor]);
L1im = RGB_Image(:,:,1);
L2im = RGB_Image(:,:,2);
L3im = RGB_Image(:,:,3);
L1im_String = string(L1im);
L2im_String = string(L2im);
L3im_String = string(L3im);
RGB_Triplets = L1im_String + "," + L2im_String + "," + L3im_String;
Unique_RGB_Triplets = unique(RGB_Triplets);
for Colour_Index = 1: length(Unique_RGB_Triplets)
RGB_Colour = Unique_RGB_Triplets(Colour_Index);
Unique_RGB_Triplets(Colour_Index,2) = nnz(RGB_Colour == RGB_Triplets);
end
Counts = str2double(Unique_RGB_Triplets(:,2));
Scaling_Factor = Number_Of_Pixels/sum(Counts);
Counts = Counts.*Scaling_Factor;
if sum(Counts) == Number_Of_Pixels
disp("Sum of histogram is equal to the number of pixels");
end
bar(Counts);
title("RGB Triplet Histogram");
xlabel("RGB Triplets"); ylabel("Counts");
Current_Axis = gca;
Scale = (1:length(Unique_RGB_Triplets));
set(Current_Axis,'xtick',Scale,'xticklabel',Unique_RGB_Triplets);
Angle = 90;
xtickangle(Current_Axis,Angle);

correlation coefficient data driven approach possible?

I have a matrix 64x64x32x90 which stands for pixels at x,y,z, at time t.
I have a reference signal 1x90 which stands for the behavior I expect for a pixel at some point (x,y,z).
I am constructing a new image of the correlation between each pixel versus my reference.
load('DATA.mat');
ON = ones(1,10);
OFF = zeros(1,10);
taskRef = [OFF ON OFF ON OFF ON OFF ON OFF];
corrImage = zeros(64,64,36);
for i=1:64,
for j=1:63,
for k=1:36
signal = squeeze(DATA(i,j,k,:));
coef = corrcoef(signal',taskRef);
corrImage(i,j,k) = coef(2);
end
end
end
My process is too slow. Is there a way to get rid of my loops or adjust the code to have a better runtime?
Reshape your data so that its first three dimensions are collapsed into one (so now there are 64*64*32 rows and 90 columns).
Then use pdist2 (with 'correlation' option) to compute the correlation of each row with the expected pattern.
Finally, reshape result into the desired shape.
DATA2 = reshape(DATA, [],90);
corrImage = 1 - pdist2(DATA2, taskRef, 'correlation');
corrImage = reshape(corrImage, 64,64,32);

sum of absolute differences of two images

I want to find how similar a picture is to some samples that I have (black and white).
I use the sum of absolute difference code, but because I'm new to MATLAB I didn't find out how to use it. How does this algorithm work? Does it give a measure of how similar the pics are?
I= imread('img1.jpg');
image2= imread('img2.jpg');
% J = uint8(filter2(fspecial('gaussian'), I));
K = imabsdiff(I,image2);
figure, imshow(K,[])
Well I think you pretty much answered your question yourself. It is the sum of the absolute difference. So let say you have img1 and img2 which are the same size and type.
To find the difference, do subtraction
img1-img2
To find the absolute difference, use the absolute value function abs
abs(img1-img2)
To find the sum, use the sum function. Note that you will need to do this for each "dimension" your image has. If you are not sure, type size(img1) and see if there are 2 or 3 numbers that show up, this corresponds to how many sum(...) you need to use.
For a color image (3 dimensions):
sum(sum(sum(abs(img1-img2))))
^^ Is the sum of the absolute differences. Whichever has the smallest sum can be considered the closest image.
If you have different sized images, you need to use the normxcorr2 function. This function will return a matrix of the same size with how well the template (small) image fits into the big image at each different point. Find the maximum value of that matrix and that is how well that image fits.
For instance:
correlation = normxcorr2(smallImg, bigImg);
compareMe = max(correlation(:))
It is best practice to use MATLAB's build-in function imabsdiff. In contrast to the other suggested answers, it takes care of the range boundaries if your image is formatted as uint8. Consider:
img1 = uint8(10);
img2 = uint8(20);
sum(abs(img1(:)-img2(:)))
gives you 0, whereas
imabsdiff(img1(:),img2(:))
correctly gives 10.
You should use the command im2col in MATLAB you will be able to do so in Vectorized manner.
Just arrange each neighborhood in columns (For each frame).
Put them in 3D Matrix and apply you operation on the 3rd dimension.
Code Snippet
I used Wikipedia's definition of "Sum of Absolute Differences".
The demo script:
```
% Sum of Absolute Differences Demo
numRows = 10;
numCols = 10;
refBlockRadius = 1;
refBlockLength = (2 * refBlockRadius) + 1;
mImgSrc = randi([0, 255], [numRows, numCols]);
mRefBlock = randi([0, 255], [refBlockLength, refBlockLength]);
mSumAbsDiff = SumAbsoluteDifferences(mImgSrc, mRefBlock);
```
The Function SumAbsoluteDifferences:
```
function [ mSumAbsDiff ] = SumAbsoluteDifferences( mInputImage, mRefBlock )
%UNTITLED2 Summary of this function goes here
% Detailed explanation goes here
numRows = size(mInputImage, 1);
numCols = size(mInputImage, 2);
blockLength = size(mRefBlock, 1);
blockRadius = (blockLength - 1) / 2;
mInputImagePadded = padarray(mInputImage, [blockRadius, blockRadius], 'replicate', 'both');
mBlockCol = im2col(mInputImagePadded, [blockLength, blockLength], 'sliding');
mSumAbsDiff = sum(abs(bsxfun(#minus, mBlockCol, mRefBlock(:))));
mSumAbsDiff = col2im(mSumAbsDiff, [blockLength, blockLength], [(numRows + blockLength - 1), (numCols + blockLength - 1)]);
end
```
Enjoy...

How do I resize a Matlab matrix with a 3rd dimension?

So I'd like to resize a matrix that is of size 72x144x156 into a 180x360x156 grid. I can try to do it with this command: resizem(precip,2.5). The first two dimensions are latitude and longitude, while the last dimension is time. I don't want time to be resized.
This works if the matrix is of size 72x144. But it doesn't work for size 72x144x156. Is there a way to resize the first two dimensions without resizing the third?
Also, what is the fastest way to do this (preferably without a for loop). If a for loop is necessary, then that's fine.
I hinted in my comment, but could use interp3 like this:
outSize = [180 360 156];
[nrows,ncols,ntimes] = size(data);
scales = [nrows ncols ntimes] ./ outSize;
xq = (1:outSize(2))*scales(2) + 0.5 * (1 - scales(2));
yq = (1:outSize(1))*scales(1) + 0.5 * (1 - scales(1));
zq = (1:outSize(3))*scales(3) + 0.5 * (1 - scales(3));
[Xq,Yq,Zq] = meshgrid(xq,yq,zq);
dataLarge = interp3(data,Xq,Yq,Zq);
But the problem is simplified if you know you don't want to interpolate between time points, so you can loop as in Daniel R's answer. Although, this answer will not increase the number of time points.
D= %existing matrix
scale=2.5;
E=zeros(size(D,1)*2.5,size(D,2)*2.5,size(D,3))
for depth=1:size(D,3)
E(:,:,depth)=resizem(D(:,:,depth),scale)
end
This should provide the expected output.
% s = zeros(72, 144, 156);
% whos s;
% news = resize2D(s, 2.5);
% whos news;
function [result] = resize2D(input, multiply)
[d1, d2, d3] = size(input);
result = zeros(d1*multiply, d2*multiply, d3);
end

Eigenfaces shows emptyblack image in matlab [duplicate]

This question already has answers here:
eigenfaces are not showing correctly and are very dark
(2 answers)
Closed 3 years ago.
i have a set of 17 face grayscale pictures..and when try to view it i get a black images instead of ghost like pictures.
input_dir = 'images';
image_dims = [60, 60];
filenames = dir(fullfile(input_dir, '*.jpg'));
num_images = numel(filenames);
images = [];
for n = 1:num_images
filename = fullfile(input_dir, filenames(n).name);
img = imresize(imread(filename),[60,60]);
if n == 1
images = zeros(prod(image_dims), num_images);
end
images(:, n) = img(:);
end
% Trainig
% steps 1 and 2: find the mean image and the mean-shifted input images
mean_face = mean(images, 2);
shifted_images = images - repmat(mean_face, 1, num_images);
% steps 3 and 4: calculate the ordered eigenvectors and eigenvalues
[evectors, score, evalues] = princomp(images');
% step 5: only retain the top 'num_eigenfaces' eigenvectors (i.e. the principal components)
num_eigenfaces = 20;
evectors = evectors(:, 1:num_eigenfaces);
% step 6: project the images into the subspace to generate the feature vectors
features = evectors' * shifted_images;
and to see the eignevalues i used this code
figure;
for n = 1:num_eigenfaces
subplot(2, ceil(num_eigenfaces/2), n);
evector = reshape(evectors(:,n), image_dims);
imshow(evector);
end
i dont think it was suppose to be like this. can someone point out what i did wrong?
You should check each step in the code and make sure they pass sanity checks. My guess is this
features = evectors' * shifted_images;
Should be this
features = shifted_images * evectors;
Which makes me wonder if shifted_images has the correct dimensions. The evectors should be a matrix where each column represents a component vector. The matrix will be [pics x n]. The shifted images should be a [pixcount x pics] matrix. "pixcount" is the amount of pixels in each picture and "pics" is the number of pictures. If evectors' * shifted_images works without a dimensions error, I wonder if one quantity isn't being calculated correctly. I think this transpose is the culprit:
princomp(images');
Try scaling the image:
for i=1:num_eigenfaces
subplot(1,7,i);
image=reshape(evectors(:,i), image_dims);
image=image';
%scale image to full scale
imshow(image, []);
end