Matlab - Plotting Specific Pixels (image treatment) - matlab

I'm currently struggling with an image treatment\ data plotting issue and was hoping to get some feedback from people with more experience than myself on this matter.
I'll try and breakdown the problem as to make it more understandable:
I have an original image (figureB - which is the blue chanel of the original image) of size NxM, from this image I select a specific area to study (NewfigureB), size 120x170;
I then divide this area into what I called macropixels which are 10x10 arrays of data points (pixels);
I then apply a mask to the selected area to select only the points meeting certain luminescence conditions;
So far so good. My problem comes when I try to plot a histogram of each of these macropixels when applying the luminescence mask. The final objective is to then find the peaks in these histograms.
so far this is what I've come up with. Any help would be greatly appreciated.
Many thanks
%Make the number of pixels in the matrix divisible
Macropixel = 10; %determine the size of the macropixel
[rows,columns] = size(figureB); %determine dimentions of the matrix used in the calculations
MacropixRows = floor(rows/Macropixel); %determine how many macropixels are in a row of the original matrix
MacropixColumn = floor(columns/Macropixel); %determine how many macropixels are in a column of the original matrix
%define new dim for the matrix
rows = MacropixRows * Macropixel;
columns = MacropixColumn * Macropixel;
NewfigureB = figureB(1:rows,1:columns); %divisible by the size of the macropixels created
%select area
NewfigureB = NewfigureB(1230:1349,2100:2269);
%create luminescence mask
Lmin=50;
hmax=80;
mask=false(size(NewfigureB));
mask(NewfigureB <Lmin)=true;
mask=mask & (NewfigureB<hmax);
%Apply mask
NewfigureB=NewfigureB(mask);
for jj = 1:Macropixel:120
for ii =1:Macropixel:170
histogram( NewfigureB(jj:jj+Macropixel-1, ii:ii+Macropixel-1))
end
end'''

The code you have posted has too many issues.
I tried to correct it the best I could.
I modified some parameters to feat the sample image I used.
I couldn't find your sample image, so I used the following
Here is a corrected code (please read the comments):
I = imread('Nikon-D810-Image-Sample-7.jpg');
figureB = I(:,:,3);
%Make the number of pixels in the matrix divisible
Macropixel = 10; %determine the size of the macropixel
[rows,columns] = size(figureB); %determine dimentions of the matrix used in the calculations
MacropixRows = floor(rows/Macropixel); %determine how many macropixels are in a row of the original matrix
MacropixColumn = floor(columns/Macropixel); %determine how many macropixels are in a column of the original matrix
%define new dim for the matrix
rows = MacropixRows * Macropixel;
columns = MacropixColumn * Macropixel;
NewfigureB = figureB(1:rows,1:columns); %divisible by the size of the macropixels created
%select area
NewfigureB = NewfigureB(1230:1349,2100:2269);
%create luminescence mask
Lmin=90;%50; %Change to 90 for testing
hmax=200;%80; %Change to 200 for testing
mask=false(size(NewfigureB));
mask(NewfigureB > Lmin)=true; %I think it should be > Lmin. %mask(NewfigureB <Lmin)=true;
mask=mask & (NewfigureB<hmax);
%This is not the right way to apply a mask, because the result is a vector (not a matrix).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%Apply mask
%NewfigureB=NewfigureB(mask);
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%Assuming there are no zeros in the image, you can set masked elements to zero:
NewfigureB(~mask) = 0;
for jj = 1:Macropixel:120
for ii =1:Macropixel:170
%Copy NewfigureB(jj:jj+Macropixel-1, ii:ii+Macropixel-1) into temporary matrix MB:
MB = NewfigureB(jj:jj+Macropixel-1, ii:ii+Macropixel-1);
%Remove the zeros from MB (zeros are masked elements).
%The result is a vector (not a matrix).
MB = MB(MB ~= 0);
%histogram( NewfigureB(jj:jj+Macropixel-1, ii:ii+Macropixel-1))
figure; %Open new figure for each histogram. (I don't know if it's a good idea).
histogram(MB); %Plot the histogram of vector MB.
end
end
It's probably not exactly matches intentions.
I hope it gives you a lead...

Related

Get pixel values in RGB images using PixelList in MATLAB

I am trying to get pixel intensity values from regions of interest in RGB images.
I segmented the image and saved the regions of interest (ROI) using regionprops 'PixelList' in MATLAB, as shown below:
In this example I am using "onion.png" image built in MATLAB. (But in reality I have hundreds of images, and each of them have several ROIs hence why I'm saving the ROIs separately.)
%SEGMENTATION PROGRAM:
a=imread('C:\Program Files\MATLAB\MATLAB Production Server\R2015a\toolbox\images\imdata\onion.png');warning('off', 'Images:initSize:adjustingMag');
figure; imshow(a,[]);
nrows = size(a,1);ncols = size(a,2);
zr=ones(nrows,ncols); %matrix of ones
r=a(:,:,1);g=a(:,:,2);b=a(:,:,3); %get RGB values
rr=double(r);gg=double(g);bb=double(b);% convert to double to avoid uint8 sums
bgd=(rr+bb)./(2*gg); %calculation RGB ratio of background
zr1=bgd>1.15; %matrix containing background as 1 and ROI as 0
% remake binary image for holes command which requires white object to fill % (this step is not relevant for this example, but it is for my data)
zr2=zr1<0.5;
zr3=imfill(zr2, 'holes');
figure;imshow(zr3); pause;
roi=regionprops(zr3,'Centroid','PixelList','Area');
ar=[roi.Area];
% find sort order , largest first
[as, ia]=sort(ar(1,:),'descend');
for w=1:length(roi); xy(w,:)=roi(w).Centroid;end
% use sort index to put cenrtoid list in same order
xy1=xy(ia,:);
%and pixel id list
for w=1:length(roi)
roi2(w).PixelList=roi(ia(w)).PixelList;
end
%extract centriod positions as two colums
%SAVE PIXEL LIST FOR EACH ROI IN A SEPARATE FILE
for ww=1:w;
k=roi2(ww).PixelList;
save('onion_PL','k');
end
How do I use this pixel list to get the intensity values in the original image? More specifically, I need to get the ratio of pixels in Green channel over Red ("grr=rdivide(gg,rr);"), but only in the region of interest labeled with PixelList. Here's my code so far:
%PL is the PixelList output we got above.
a=imread('C:\Program Files\MATLAB\MATLAB Production Server\R2015a\toolbox\images\imdata\onion.png');warning('off', 'Images:initSize:adjustingMag');
PL=dir(['*PL.mat']); %load file PixelList files. "dir" is a variable with directory path containing the pixelist files. In this example, we saved "onion_PL.mat"
for m=1:length(PL);
load(PL(m).name);
ex=[]; %empty matrix to hold the extracted values
for mm=1:length(k);
%INSERT ANSWER HERE
end
This next bit is wrong because it's based on the entire image ("a"), but it contains the calculations that I would like to perform in the ROIs
figure; imshow(a,[]);
pause;
nrows = size(a,1);ncols = size(a,2);
zr=ones(nrows,ncols); %matrix of ones
r=a(:,:,1);g=a(:,:,2);b=a(:,:,3); %get RGB values
rr=double(r);gg=double(g);bb=double(b);% convert to double to avoid uint8 sums
grr=rdivide(gg,rr);
I am brand new to MATLAB, so my code is not the greatest... Any suggestions will be greatly appreciated. Thank you in advance!
The loop you are looking for seems simple:
grr = zeros(nrows, ncols); % Initialize grr with zeros.
for mm = 1:length(k)
x = k(mm, 1); % Get the X (column) coordinate.
y = k(mm, 2); % Get the Y (row) coordinate.
grr(y, x) = gg(y, x) / rr(y, x);
end
A more efficient solution is using sub2ind for converting the x,y coordinates to linear indices:
% Convert k to linear indices.
kInd = sub2ind([nrows, ncols], k(:,2), k(:,1));
% Update only the indices in the PixelList.
grr(kInd) = rdivide(gg(kInd), rr(kInd));
In your given code sample there are 5 PixelLists.
I don't know how do you want to "arrange" the result.
In my code sample, I am saving the 5 results to 5 mat files.
Here is an executable code sample:
close all
%SEGMENTATION PROGRAM:
a=imread('onion.png');warning('off', 'Images:initSize:adjustingMag');
figure; imshow(a,[]);
nrows = size(a,1);ncols = size(a,2);
zr=ones(nrows,ncols); %matrix of ones
r=a(:,:,1);g=a(:,:,2);b=a(:,:,3); %get RGB values
rr=double(r);gg=double(g);bb=double(b);% convert to double to avoid uint8 sums
bgd=(rr+bb)./(2*gg); %calculation RGB ratio of background
zr1=bgd>1.15; %matrix containing background as 1 and ROI as 0
% remake binary image for holes command which requires white object to fill % (this step is not relevant for this example, but it is for my data)
zr2=zr1<0.5;
zr3=imfill(zr2, 'holes');
figure;imshow(zr3); %pause;
roi=regionprops(zr3,'Centroid','PixelList','Area');
ar=[roi.Area];
% find sort order , largest first
[as, ia]=sort(ar(1,:),'descend');
for w=1:length(roi); xy(w,:)=roi(w).Centroid;end
% use sort index to put cenrtoid list in same order
xy1=xy(ia,:);
%and pixel id list
for w=1:length(roi)
roi2(w).PixelList=roi(ia(w)).PixelList;
end
%extract centroid positions as two columns
%SAVE PIXEL LIST FOR EACH ROI IN A SEPARATE FILE
for ww=1:w
k=roi2(ww).PixelList;
%save('onion_PL', 'k');
save(['onion', num2str(ww), '_PL'], 'k'); % Store in multiple files - onion1_PL.mat, onion2_PL.mat, ... onion5_PL.mat
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
clear % Use clear for testing - the variables are going to be read from the mat file.
%PL is the PixelList output we got above.
a=imread('onion.png');warning('off', 'Images:initSize:adjustingMag');
nrows = size(a,1);ncols = size(a,2);
zr=ones(nrows,ncols); %matrix of ones
r=a(:,:,1);g=a(:,:,2);b=a(:,:,3); %get RGB values
rr=double(r);gg=double(g);bb=double(b);% convert to double to avoid uint8 sums
grr=rdivide(gg,rr);
PL=dir('*PL.mat'); %load file PixelList files. "dir" is a variable with directory path containing the pixelist files. In this example, we saved "onion_PL.mat"
for m = 1:length(PL)
load(PL(m).name);
ex=[]; %empty matrix to hold the extracted values
%for mm=1:length(k)
%INSERT ANSWER HERE
grr = zeros(nrows, ncols); % Initialize grr with zeros.
for mm = 1:length(k)
x = k(mm, 1); % Get the X (column) coordinate.
y = k(mm, 2); % Get the Y (row) coordinate.
grr(y, x) = gg(y, x) / rr(y, x);
end
% Instead of using a loop, it's more efficient to use sub2ind
if false
% Convert k to linear indices.
kInd = sub2ind([nrows, ncols], k(:,2), k(:,1));
% Update only the indices in the PixelList.
grr(kInd) = rdivide(gg(kInd), rr(kInd));
end
figure;imshow(grr);title(['grr of m=', num2str(m)]) % Show grr for testing.
save(['grr', num2str(m)], 'grr'); % Save grr for testing.
imwrite(imadjust(grr, stretchlim(grr)), ['grr', num2str(m), '.png']); % Store grr as image for testing
end
First two grr matrices as images (used for testing):
grr1.png:
grr2.png:

Enlarging models in MATLAB

Im trying to create a project in MATLAB where i project a heat map matrix on to a cylinder in MATLAB but i'm bumping in to all kinds of issues.
Im using MATLAB version R2019b(Latest version).
Now my first issue id like to adress is is the modells being absolutely tiny even when i zoom in to the max.
Is there any way to make these models larger or to display them in a separate window ??
Also my second question is the regarding the scaling. As you can see the scale on the Z-axis of the cylinder goes to 512. any way to get a larger more detailed image where i can see each point on that scale?
clear vars
filename =
['/Users/gomer/Documents/Datavisualisering/Projekt/data/day/filenames.txt'];
%This line simply gives us a table of all filenames in this file.
T = readtable(filename);
tsize = size(T);
%extracts the content of the table as a categorical array.
%%
%
% {rownumber,variablenumber}. {100,1} = row 100, variable 1.
%converts the categorical array into a string array.
%joins the string array across the column.
%string(T{100:105,1}); implies from row 100 to row 105 and use variable 1.
%This line simply adds the name of the file at row 100 to the path of the
%file. Hnece we get a full filepath.
filename = strcat('/Users/gomer/Documents/Datavisualisering/Projekt/data/day/', string(T{100,1}));
map100 = getHeatMap(filename);
%%
%
filename = strcat('/Users/gomer/Documents/Datavisualisering/Projekt/data/day/', string(T{1000,1}));
map1000 = getHeatMap(filename);
%creates a image
k=imshow(map100);
%creates a colormap.
%gca returns the current axes (or standalone visualization) in the current figure.
%hence the command just works top down and affects last image displayed.
colormap(gca, 'jet');
k=imshow(map1000);
colormap(gca, "jet");
function heat = getHeatMap(filename)
%Returns a struct with info about the file.
s = dir(filename);
%Opens a file for reading, hence the 'r'.
fin=fopen(filename,'r');
%A = fread(fileID,sizeA,precision)
%reads file data into an array, A, with dimensions, sizeA, and positions the file pointer after the last value read.
%fread populates A in column order.
I=fread(fin,s.bytes,'uint8=>uint8');
%The uint16 function converts a Input array, specified as a scalar, vector, matrix, or multidimensional array.
%Converts the values in to type uint16 and creates a Matrix with the values.
%Looks more like we are just calculating the size of values to be
%deducted from matrix size (no idea why).
w = uint16(I(1))+256*uint16(I(2));
h = uint16(I(3))+256*uint16(I(4));
skip = s.bytes - w*h + 1;
IN = I(skip:1:s.bytes);
%reshape(A,sz) reshapes A using the size vector, sz, to define size(B). For example, reshape(A,[2,3]) reshapes A into a 2-by-3 matrix.
%sz must contain at least 2 elements, and prod(sz) must be the same as numel(A).
%single(X) converts the values in X to single precision.
Z=single(reshape(IN,w,h));
%Interpolate the values.
%telling the system which points to interpolate in between.
Z=griddedInterpolant(Z');
y_range = linspace(1.0,single(h),360);
x_range = linspace(1.0,single(w),512);
%Used to apecify the query points (points in between which we want
%interpolation) as vectors (this is to get a more continues image).
%specifies the query points as grid vectors.
%Use this syntax to conserve memory when you want to query a large grid of points.
heat = uint8(Z({y_range, x_range}));
end
Thank you
Resizing Figure Window
Copying your script to a .m and running it instead of a live .mlx file will automatically create all the figures in a separate window by default. To adjust the size of the figures the following the position property can be modified. To adjust the scale functions xticks(), yticks() and zticks() can be used. These three scale functions take in an array representing all the line markers along the respective axis/scale.
Test Plot Script:
X_Position = 10;
Y_Position = 10;
Width = 1000;
Height = 500;
%Configuring the figure settings%
figure('Position', [X_Position Y_Position Width Height])
%Test plot (replace with your plot)%
Data = [1 2 3 4 5 6];
plot(Data);
Plotting Heatmaps on a Cylindrical Surfaces
Method 1: Using warp() Function
Haven't delved into which orientation the data should be set as but this is an option to use the warp() function to wrap the heatmap around a cylinder. They're most likely other 3D plotting options if specific points are of interest. The points of the cylinder to be plotted are generated using the cylinder() function which returns the xyz-coordinates of the cylinder's wireframe. The fourth input argument into the warp() function serves and a colormap in this case it is proportional to the heatmap values.
load('HeatMapMatrix.mat');
%Setting up the figure%
clf;
close all;
figure('Position', [10 10 1000 500])
%Creating the cylinder%
Cylinder_Radius = 360;
Cylinder_Height = 512;
[X,Y,Z] = cylinder(Cylinder_Radius,Cylinder_Height-1);
%Warping the heatmap along the cylinder%
subplot(1,2,1); warp(X,Y,Cylinder_Height.*Z,map100');
colormap(gca,'jet');
subplot(1,2,2); warp(X,Y,Cylinder_Height.*Z,map100);
colormap(gca,'jet');
Method 2: Plotting All Points and Using surf() Function:
In this case, the coordinates for a cylinder are generated by first finding the coordinates of the circumference of the cylinder. This is done by using the sin() and cos() relationships:
X-Points = Radius × cos(𝜃)
Y-Points = Radius × sin(𝜃)
This results in the xy-coordinates of the cylinder's circle. These xy-coordinates need to be duplicated using repmat() to be later used for the varying heights. The process can be described best with a diagram as follows:
Four matrices above are created to plot each Heat Data point corresponding to an xyz-coordinate. The x and y coordinates are repeated in every row of matrices X-Points and Y_Points since those are constant for the repeating circles. The columns of the matrix Z-Points are also duplicates of each other since the heights should be constant for each row corresponding to each circle.
load('HeatMapMatrix.mat');
Radius = 20;
Number_Of_Data_Points = 360;
Theta = linspace(0,2*pi,Number_Of_Data_Points);
%The xy values according to radius and number of points%
X_Points = Radius*cos(Theta);
Y_Points = Radius*sin(Theta);
map100 = rot90(map100);
Sample_Range = 255 - 0;
Temperature_Range = 450 - 50;
Multiplier = Temperature_Range/Sample_Range;
map100 = map100.*Multiplier + 50;
Height = 512;
X_Points = repmat(X_Points,Height,1);
Y_Points = repmat(Y_Points,Height,1);
Z_Points = (1:512)';
Z_Points = repmat(Z_Points,1,Number_Of_Data_Points);
clf;
close;
figure('Position', [10 10 800 500])
Offset = 200;
subplot(1,3,1:2); Surface = surf(Y_Points,X_Points,Z_Points,'Cdata',map100);
title("3D Heatmap Plot");
zlabel("Height");
shading interp
colorbar
% direction = [0 1 0];
% rotate(Surface,direction,90)
Maximum_Value = 450;
Minimum_Value = 50;
caxis([Minimum_Value Maximum_Value]);
subplot(1,3,3); imshow(rot90(rot90(map100)));
colormap(gca, 'default');
title("Flat Heatmap Plot");
caxis([Minimum_Value Maximum_Value]);
colorbar;
Ran using MATLAB R2019b

Efficient inpaint with neighbouring pixels

I am implementing a simple algorithm to do in-painting on a "damaged" image. I have a predefined mask that specifies the area which needs to be fixed. My strategy is to start at the border of the masked area and in-paint each pixel with the central mean of its neighboring non-zero pixels, repeating until there's no unknown pixels left.
function R = inPainting(I, mask)
H = [1 2 1; 2 0 2; 1 2 1];
R = I;
n = 1;
[row,col,~] = find(~mask); %Find zeros in mask (area to be inpainted)
unknown = horzcat(row, col)';
while size(unknown,2) > 0
new_unknown = [];
new_R = R;
for u = unknown
r = u(1);
c = u(2);
nb = R(max((r-n), 1):min((r+n), end), max((c-n),1):min((c+n),end));
nz = nb~=0;
nzs = sum(nz(:));
if nzs ~= 0 %We have non-zero neighbouring pixels. In-paint with average.
new_R(r,c) = sum(nb(:)) / nzs;
else
new_unknown = horzcat(new_unknown, u);
end
end
unknown = new_unknown;
R = new_R;
end
This works well, but it's not very efficient. Is it possible to vectorize such an approach, using mostly matrix operations? Does someone know of a more efficient way to implement this algorithm?
If I understand your problem statement, you are given a mask and you wish to fill in these pixels in this mask with the mean of the neighbourhood pixels that surround each pixel in the mask. Another constraint is that the image is defined such that any pixels that belong to the mask in the same spatial locations are zero in this mask. You are starting from the border of the mask and are propagating information towards the innards of the mask. Given this algorithm, there is unfortunately no way you can do this with standard filtering techniques as the current time step is dependent on the previous time step.
Image filtering mechanisms, like imfilter or conv2 can't work here because of this dependency.
As such, what I can do is help you speed up what is going on inside your loop and hopefully this will give you some speed up overall. I'm going to introduce you to a function called im2col. This is from the image processing toolbox, and given that you can use imfilter, we can use this function.
im2col creates a 2D matrix such that each column is a pixel neighbourhood unrolled into a single vector. How it works is that each pixel neighbourhood in column major order is grabbed, so we get a pixel neighbourhood at the top left corner of the image, then move down one row, and another row and we keep going until we reach the last row. We then move one column over and repeat the same process. For each pixel neighbourhood that we have, it gets unrolled into a single vector, and the output would be a MN x K matrix where you have a neighbourhood size of M x N for each pixel neighbourhood and there are K neighbourhoods.
Therefore, at each iteration of your loop, we can unroll the current inpainted image's pixel neighbourhoods into single vectors, determine which pixel neighborhoods are non-zero and from there, determine how many zero values there are for each of these selected pixel neighbourhood. After, we compute the mean for these non-zero columns disregarding the zero elements. Once we're done, we update the image and move to the next iteration.
What we're going to need to do first is pad the image with a 1 pixel border so that we're able to grab neighbourhoods that extend beyond the borders of the image. You can use padarray, also from the image processing toolbox.
Therefore, we can simply do this:
function R = inPainting(I, mask)
R = double(I); %// For precision
n = 1;
%// Change - column major indices
unknown = find(~mask); %Find zeros in mask (area to be inpainted)
%// Until we have searched all unknown pixels
while numel(unknown) ~= 0
new_R = R;
%// Change - take image at current iteration and
%// create columns of pixel neighbourhoods
padR = padarray(new_R, [n n], 'replicate');
cols = im2col(padR, [2*n+1 2*n+1], 'sliding');
%// Change - Access the right pixel neighbourhoods
%// denoted by unknown
nb = cols(:,unknown);
%// Get total sum of each neighbourhood
nbSum = sum(nb, 1);
%// Get total number of non-zero elements per pixel neighbourhood
nzs = sum(nb ~= 0, 1);
%// Replace the right pixels in the image with the mean
new_R(unknown(nzs ~= 0)) = nbSum(nzs ~= 0) ./ nzs(nzs ~= 0);
%// Find new unknown pixels to look at
unknown = unknown(nzs == 0);
%// Update image for next iteration
R = new_R;
end
%// Cast back to the right type
R = cast(R, class(I));

Calculate the pixel distance to three defined pixel in matlab

I want to classify pixels of one tiff image according to pixel's RGB colour. The input is an image and three predefined colours for water(r0,g0,b0), forest(r1,g1,b1) and building(r2,g2,c2). The classification is based on the distance between image pixel and these three colors. If a pixel is closet to the water, the pixel is water and changed it the water RGB. The distance is calculated as (one sample) sqrt((x-r0)^2+(y-g0)^2+(z0-b0)^2)
The sample implementation is:
a=imread(..);
[row col dim] = size(a); % dim =3
for i=1:row
for j=1:col
dis1=sqrtCal(a(i,j,:)-water)
dis2=sqrtCal(a(i,j,:)-forest)
dis3=sqrtCal(a(i,j,:)-build)
a(i,j,:) = water;
if dis2< dis1
dis1 = dis2
a(i,j,:) = forest
end
dis3=sqrt(a(i,j,:)-build)
if dis3<dis1
a(i,j,:) = build
end
end
end
This implementation should work. The problem is that the two for loops is not a good choice.
So is there any good alternatives in the Matlab? The
D = pdist(X,distance)
Seems not appliable for my case.
I think this does what you want. The number of reference colors is arbitrary (3 in your example). Each reference color is defined as a row of the matrix ref_colors. Let MxN denote the number of pixels in the original image. Thus the original image is an MxNx3 array.
result_index is an MxN array which for each original pixel contains the index of the closest reference color.
result is a MxNx3 array in which each pixel has been assigned the RBG values of the closest reference color.
im = rand(10,12,3); %// example image
ref_colors = [ .1 .1 .8; ... %// water
.3 .9 .2; ... %// forest
.6 .6 .6 ]; %// build: example reference colors
dist = sum(bsxfun(#minus, im, permute(ref_colors, [3 4 2 1])).^2, 3);
%// 4D array containing the distance from each pixel to each reference color.
%// Dimensions are: 1st: pixel row, 2nd: pixel col, 3rd: not used,
%// 4th: index of reference color
[~, result_index] = min(dist, [], 4); %// compute arg min along 4th dimension
result = reshape(ref_colors(result_index,:), size(im,1), size(im,2), 3);
This one is another bsxfun based solution, but stays in 3D and could be more efficient -
%// Concatenate water, forest, build as a 2D array for easy processing
wfb = cat(2,water(:),forest(:),build(:))
%// Calculate square root of squared distances & indices corresponding to min vals
[~,idx] = min(sum(bsxfun(#minus,reshape(a,[],3),permute(wfb,[3 1 2])).^2,2),[],3)
%// Get the output with the minimum from the set of water, forest & build
a_out = reshape(wfb(:,idx).',size(a))
If you have the statistics toolbox installed, you can use this version based on knnsearch, that scales well for a large number of colors.
result_index = knnsearch(ref_colors, reshape(im,[],3));
result = reshape(ref_colors(result_index,:), size(im));

Pixel subtraction

I am working on code that select set of pixels randomly from gray images, then comparing the intensity of each 2 pixels by subtracting the intensity of pixel in one location from another one in different location.
I have code do random selection, but I am not sure of this code and I do not know how to do pixels subtraction?
thank you in advance..
{
N = 100; % number of random pixels
im = imread('image.bmp');
[nRow,nCol,c] = size(im);
randRow = randi(nRow,[N,1]);
randCol = randi(nCol,[N,1]);
subplot(2,1,1)
imagesc(im(randRow,randCol,:))
subplot(2,1,2)
imagesc(im)
}
Parag basically gave you the answer. In order to achieve this vectorized, you need to use sub2ind. However, what I would do is generate two sets of rows and columns. The reason why is because you need one set for the first set of pixels and another set for the next set of pixels so you can subtract the two sets of intensities. Therefore, do something like this:
N = 100; % number of random pixels
im = imread('image.bmp');
[nRow,nCol,c] = size(im);
%// Generate two sets of locations
randRow1 = randi(nRow,[N,1]);
randCol1 = randi(nCol,[N,1]);
randRow2 = randi(nRow,[N,1]);
randCol2 = randi(nCol,[N,1]);
%// Convert each 2D location into a single linear index
%// for vectorization, then subtract
locs1 = sub2ind([nRow, nCol], randRow1, randCol1);
locs2 = sub2ind([nRow, nCol], randRow2, randCol2);
im_subtract = im(locs1) - im(locs2);
subplot(2,1,1)
imagesc(im_subtract);
subplot(2,1,2)
imagesc(im);
However, the above code only assumes that your image is grayscale. If you want to do this for colour, you'll have to do a bit more work. You need to access each channel and subtract on a channel basis. The linear indices that were defined above are just for a single channel. As such, you'll need to offset by nRow*nCol for each channel if you want to access the same corresponding locations in the next channels. As such, I would use sub2ind in combination with bsxfun to properly generate the right values for vectorizing the subtraction. This requires just a slight modification to the above code. Therefore:
N = 100; % number of random pixels
im = imread('image.bmp');
[nRow,nCol,c] = size(im);
%// Generate two sets of locations
randRow1 = randi(nRow,[N,1]);
randCol1 = randi(nCol,[N,1]);
randRow2 = randi(nRow,[N,1]);
randCol2 = randi(nCol,[N,1]);
%// Convert each 2D location into a single linear index
%// for vectorization
locs1 = sub2ind([nRow, nCol], randRow1, randCol1);
locs2 = sub2ind([nRow, nCol], randRow2, randCol2);
%// Extend to as many channels as we have
skip_ind = permute(0:nRow*nCol:(c-1)*nRow*nCol, [1 3 2]);
%// Create 3D linear indices
locs1 = bsxfun(#plus, locs1, skip_ind);
locs2 = bsxfun(#plus, locs2, skip_ind);
%// Now subtract the locations
im_subtract = im(locs1) - im(locs2);
subplot(2,1,1)
imagesc(im_subtract);
subplot(2,1,2)
imagesc(im);