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));
Related
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...
I need to extract image patches of size s x s x 3 around specified 2D locations from an image (3 channels).
How can I do this efficiently without a for loop? I know I can extract one patch around (x,y) location as:
apatch = I(y-s/2:y+s/2, x-s/2:x+s/2, :)
How can I do this for many patches? I know I can use MATLAB's function blockproc but I can't specify the locations.
You can use im2col from the image processing toolbox to transform each pixel neighbourhood into a single column. The pixel neighbourhoods are selected such that each block is chose on a column-basis, which means that the blocks are constructed by traversing down the rows first, then proceeding to the next column and getting the neighbourhoods there.
You call im2col this way:
B = im2col(A, [M N]);
I'm assuming you'll want sliding / overlapping neighbourhoods and not distinct neighbourhoods, which are what is normally used when performing any kind of image filtering. A is your image and you want to find M x N pixel neighbourhoods transformed as columns. B would be the output where each neighbourhood is a single column and horizontally-tiled together. However, you'll probably want to handle the case where you want to grab pixel neighbourhoods along the borders of the image. In this case, you'll want to pad the image first. We're going to assume that M and N are odd to allow the padding to be easier. Specifically, you want to be sure that there are floor(M/2) rows padded on top of the image as well as the bottom as well as floor(N/2) columns padded to the left of the image as well as the right. As such, we should pad A first by using padarray. Let's assume that the border pixels will be replicated, which means that the padded rows and columns will simply be those grabbed from the top or bottom row, or the left and right column, depending on where we need to pad. Therefore:
Apad = padarray(A, floor([M N]/2), 'replicate');
For the next part, if you want to choose specify neighbourhoods, you can use sub2ind to convert your 2D co-ordinates into linear indices so you can select the right columns to get the right pixel blocks. However, because you have a colour image, you'll want to perform im2col on each colour channel. Unfortunately, im2col only works on grayscale images, and so you'd have to repeat this for each channel in your image.
As such, to get ready for patch sampling, do something like this:
B = arrayfun(#(x) im2col(Apad(:,:,x), [M N]), 1:size(A,3), 'uni', 0);
B = cat(3, B{:});
The above code will create a 3D version of im2col, where each 3D slice would be what im2col produces for each colour channel. Now, we can use sub2ind to convert your (x,y) co-ordinates into linear indices so that we can choose which pixel neighbourhoods we want. Therefore, assuming your positions are stored in vectors x and y, you would do something like this:
%// Generate linear indices
ind = sub2ind([size(A,1) size(A,2)], y, x);
%// Select neighbourhoods
%// Should be shaped as a MN x len(ind) x 3 matrix
neigh = B(:,ind,:);
%// Create cell arrays for each patch
patches = arrayfun(#(x) reshape(B(:,x,:), [M N 3]), 1:numel(ind), 'uni', 0);
patches will be a cell array where each element contains your desired patch at each location of (x,y) that you specify. Therefore, patches{1} would be the patch located at (x(1), y(1)), patches{2} would be the patch located at (x(2), y(2)), etc. For your copying and pasting pleasure, this is what we have:
%// Define image, M and N here
%//...
%//...
Apad = padarray(A, floor([M N]/2), 'replicate');
B = arrayfun(#(x) im2col(Apad(:,:,x), [M N]), 1:size(A,3), 'uni', 0);
B = cat(3, B{:});
ind = sub2ind([size(A,1) size(A,2)], y, x);
neigh = B(:,ind,:);
patches = arrayfun(#(x) reshape(neigh(:,x,:), [M N 3]), 1:numel(ind), 'uni', 0);
As unexpected as this may seem, but for me the naive for-loop is actually the fastest. This might depend on your version of MATLAB though, as with newer versions they keep on improving the JIT compiler.
Common data:
A = rand(30, 30, 3); % Image
I = [5,2,3,21,24]; % I = y
J = [3,7,5,20,22]; % J = x
s = 3; % Block size
Naive approach: (faster than im2col and arrayfun!)
Patches = cell(size(I));
steps = -(s-1)/2:(s-1)/2;
for k = 1:numel(Patches);
Patches{k} = A(I(k)+steps, ...
J(k)+steps, ...
:);
end
Approach using arrayfun: (slower than the loop)
steps = -(s-1)/2:(s-1)/2;
Patches = arrayfun(#(ii,jj) A(ii+steps,jj+steps,:), I, J, 'UniformOutput', false);
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);
I'm trying to make a color plot in matlab using output data from another program. What I have are 3 vectors indicating the x-position, y-yposition (both in milliarcseconds, since this represents an image of the surroundings of a black hole), and value (which will be assigned a color) of every point in the desired image. I apparently can't use pcolor, because the values which indicate the color of each "pixel" are not in a matrix, and I don't know a way other than meshgrid to create a matrix out of the vectors, which didn't work due to the size of the vectors.
Thanks in advance for any help, I may not be able to reply immediately.
If we make no assumptions about the arrangement of the x,y coordinates (i.e. non-monotonic) and the sparsity of the data samples, the best way to get a nice image out of your vectors is to use TriScatteredInterp. Here is an example:
% samplesToGrid.m
function [vi,xi,yi] = samplesToGrid(x,y,v)
F = TriScatteredInterp(x,y,v);
[yi,xi] = ndgrid(min(y(:)):max(y(:)), min(x(:)):max(x(:)));
vi = F(xi,yi);
Here's an example of taking 500 "pixel" samples on a 100x100 grid and building a full image:
% exampleSparsePeakSamples.m
x = randi(100,[500 1]); y = randi(100,[500 1]);
v = exp(-(x-50).^2/50) .* exp(-(y-50).^2/50) + 1e-2*randn(size(x));
vi = samplesToGrid(x,y,v);
imagesc(vi); axis image
Gordon's answer will work if the coordinates are integer-valued, but the image will be spare.
You can assign your values to a matrix based on the x and y coordinates and then use imagesc (or a similar function).
% Assuming the X and Y coords start at 1
max_x = max(Xcoords);
max_y = max(Ycoords);
data = nan(max_y, max_x); % Note the order of y and x
indexes = sub2ind(size(data), max_y, max_x);
data(indexes) = Values;
imagesc(data); % note that NaN values will be colored with the minimum colormap value
I need to create an nth-order Hadamard matrix, row double it, within each row randomly permute the elements of the matrix, and then display it. So far, I have accomplished all of these things. What I end up with when I imshow(matrix) is a nice picture of black and white boxes. But I haven't figured out how to insert a fine line to divide each row. I can create something like the first image on the left, but not the image on the right (these are Figures 1 and 2 from this paper)
Any help or comments would be thoroughly appreciated.
I've found using vector approaches (e.g., patch and rectangle) for this sort of problem unnecessarily challenging. I think that it's more straightforward to build a new image. This avoids floating-point rounding issues and other things that crop up with vector graphics. My solution below relies on some functions in the Image Processing Toolbox, but is simple and fast:
% Create data similarly to #TryHard
H = hadamard(48);
C = (1+[H;-H])/2;
rng(0); % Set seed
C(:) = C(randperm(numel(C))); % For demo, just permute all values, not rows
% Scale image and lines
scl = 10; % Amount to vertically scale each row
pad = 2; % Number of pixels to add between each row
C = imresize(C,scl,'nearest');
C = blockproc(C,[scl size(C,2)],#(x)[x.data;zeros(pad,size(C,2))]);
C = C(1:end-pad,:); % Remove last line added
% Dispay image
imshow(C)
This results in an image like this
The scl and pad parameters can be easily adjusted to obtain different sizes and relative sizes. You can call imresize(...,'nearest') again after adding the lines to further scale the image if desired. The blocproc line could potentially be made more efficient with various options (see the help). It could also be replaced by calls to im2col and col2im, which possibly could be faster, if messier.
I did not try the code, but I think that something like that should work:
sizeOfACube = 6;
numberOfRows = 47;
RGB = imread('image.png');
RGB = imresize(A, [(numRows+numberOfRows) numCols]);
for i=1:1:NumberOfRows
RGB(i*6,:,:) = 0;
end
imagesc(RGB);
imwrite(RGB,'newImage.png');
with:
sizeOfAcube the size of one cube on the QRcode.
numRows and numCols the number of Rows and Column of the original image.
One solution is to use patches, for instance as follows:
% set up example array
xl = 24; yl = xl;
[X Y] = find(hadamard(xl)==1);
% generate figure
figure, hold on
for ii=1:length(X)
patch(X(ii) + [0 0 1 1],Y(ii) + [0.1 0.9 0.9 0.1],[1 1 1],'Edgecolor',[1 1 1])
end
axis([0 xl+1 0 yl+1])
axis('square')
The patch command patch(x,y, color) accepts the vertices of the polygon element as x and y. In this example you can modify the term [0.1 0.9 0.9 0.1] to set the thickness of the bounding black line.
This generates
Edited
For the particular instance provided by the OP:
H=Hadamard(48); %# now to row-double the matrix
A=(1+H)/2;
B=(1-H)/2;
C=[A; B]; %# the code below randomly permutes elements within the rows of the matrix
[nRows,nCols] = size(C);
[junk,idx] = sort(rand(nRows,nCols),2); %# convert column indices into linear indices
idx = (idx-1)*nRows + ndgrid(1:nRows,1:nCols); %# rearrange whatever matrix
E = C;
E(:) = E(idx);
[X Y] = find(logical(E));
xl = length(X);
yl = length(Y);
figure, hold on
for ii=1:xl
rectangle('Position',[X(ii) Y(ii)+.2 1 0.8],'facecolor',[1 1 1],'edgecolor',[1 1 1])
end
axis([0 max(X)+1 0 max(Y)+1])
axis('square')
set(gca,'color',[0 0 0])
set(gca,'XTickLabel',[],'YTickLabel',[],'XTick',[],'YTick',[])
This example uses rectangle instead of patch to generate sharp corners.
The image: