I have an image and a pixellist of several large objects in that image defined with regionprops. Now I want to generate a new image that only contains one object which is resized to the size of the object. So what I do is
rawimage = imread('testfile.tif');
processimage = squeeze((sum(rawimage,3))');
IMAGE_labeled = bwlabel(processimage,8);
shapedata=regionprops (IMAGE_labeled,'Area','PixelList');
index = find ([shapedata.Area]>5000);
for i = 1:length(index)
ix = shapedata(index(i)).PixelList(:,1);
iy = shapedata(index(i)).PixelList(:,2);
newimage = zeros(max(ix)-min(ix)+1,max(iy)-min(iy)+1,3);
for im = 1:length(ix)
newimage(ix(im)-min(ix)+1,iy(im)-min(iy)+1,:) = rawimage(ix(im),iy(im),:);
end
newimage = uint8(newimage);
imwrite(newimage,'myimage.tif','tif')
end
Has anyone an idea how I can vectorize the second for loop to speed the whole code up? In the end the question is how can I use two vectors as index in a matrix.
I found
Vc = num2cell([ix,iy])
rawimage(sub2ind(size(rawimage),Vc{i,:}))
but this again requires a for loop to go through all indices of Vc as I cannot use
rawimage(sub2ind(size(rawimage),Vc))
Thanks for your suggestions
Because your image has a third dimension, you'll need to reshape it a bit into an [M*N x 3] array first. You can then use sub2ind as you've shown.
for k = 1:numel(shapedata)
% Flip the PixelList so it's row/column rather than x/y
list = fliplr(shapedata(k).PixelList);
% Figure out the extents of the PixelList values
minima = min(list, [], 1);
maxima = max(list, [], 1);
% Grab a square of the image containing the object of interest
subimage = rawimage(minima(1):maxima(1), minima(2):maxima(2),:);
% Store the size
sz = size(subimage);
% Convert the PixelList values to linear indices within this sub-image
inds = sub2ind(sz(1:2), bsxfun(#minus, list, minima - 1));
% Reshape so that we can index into the first two dimensions at the same time
subimage = reshape(subimage, [], size(subimage, 3));
% Now create the output which is all zeros, but then we grab the values that are
% in PixelList and set them within the new matrix.
newimage = zeros(size(subimage));
newimage(inds,:) = subimage(inds,:);
% Reshape the result to be the expected dimension
newimage = reshape(newimage, sz);
end
Related
I've been working on bilinear interpolation based on wiki example in matlab. I followed the example to the T, but when comparing the outputs from my function and the in-built matlab function, the results are vastly different and I can't figure out why or how that happens.
Using inbuilt matlab function:
Result of my function below:
function T = bilinear(X,h,w)
%pre-allocating the output size
T = uint8(zeros(h,w));
%padding the original image with 0 so i don't go out of bounds
X = padarray(X,[2,2],'both');
%calculating dimension ratios
hr = h/size(X,1);
wr = w/size(X,2);
for row = 3:h-3
for col = 3:w-3
%for calculating equivalent position on the original image
o_row = ceil(row/hr);
o_col = ceil(col/wr);
%getting the intensity values from horizontal neighbors
Q12=X(o_row+1,o_col-1);
Q22=X(o_row+1,o_col+1);
Q11=X(o_row-1,o_col-1);
Q21=X(o_row-1,o_col+1);
%calculating the relative positions to the enlarged image
y2=round((o_row-1)*hr);
y=round(o_row*hr);
y1=round((o_row+1)*hr);
x1=round((o_col-1)*wr);
x=round(o_col*wr);
x2=round((o_col+1)*wr);
%interpolating on 2 first axis and the result between them
R1=((x2-x)/(x2-x1))*Q11+((x-x1)/(x2-x1))*Q21;
R2=((x2-x)/(x2-x1))*Q12+((x-x1)/(x2-x1))*Q22;
P=round(((y2-y)/(y2-y1))*R1+((y-y1)/(y2-y1))*R2);
T(row,col) = P;
T = uint8(T);
end
end
end
The arguments passed to the function are step4 = bilinear(Igray,1668,1836); (scale factor of 3).
You are finding the pixel nearest to the point you want to interpolate, then find 4 of this pixel’s neighbors and interpolate between them:
o_row = ceil(row/hr);
o_col = ceil(col/wr);
Q12=X(o_row+1,o_col-1);
Q22=X(o_row+1,o_col+1);
Q11=X(o_row-1,o_col-1);
Q21=X(o_row-1,o_col+1);
Instead, find the 4 pixels nearest the point you want to interpolate:
o_row = ceil(row/hr);
o_col = ceil(col/wr);
Q12=X(o_row,o_col-1);
Q22=X(o_row,o_col);
Q11=X(o_row-1,o_col-1);
Q21=X(o_row-1,o_col);
The same pixel’s coordinates then need to be used when computing distances. The easiest way to do that is to separate out the floating-point coordinates of the output pixel ((row,col)) in the input image (o_row,o_col), and the location of the nearest pixel in the input image (fo_row,fo_col). Then, the distances are simply d_row = o_row - fo_row and 1-d_row, etc.
This is how I would write this function:
function T = bilinear(X,h,w)
% Pre-allocating the output size
T = zeros(h,w,'uint8'); % Create the matrix in the right type, rather than cast !!
% Calculating dimension ratios
hr = h/size(X,1); % Not with the padded sizes!!
wr = w/size(X,2);
% Padding the original image with 0 so I don't go out of bounds
pad = 2;
X = padarray(X,[pad,pad],'both');
% Loop
for col = 1:w % Looping over the row in the inner loop is faster!!
for row = 1:h
% For calculating equivalent position on the original image
o_row = row/hr;
o_col = col/wr;
fo_row = floor(o_row); % Code is simpler when using floor here !!
fo_col = floor(o_col);
% Getting the intensity values from horizontal neighbors
Q11 = double(X(fo_row +pad, fo_col +pad)); % Indexing taking padding into account !!
Q21 = double(X(fo_row+1+pad, fo_col +pad)); % Casting to double might not be necessary, but MATLAB does weird things with integer computation !!
Q12 = double(X(fo_row +pad, fo_col+1+pad));
Q22 = double(X(fo_row+1+pad, fo_col+1+pad));
% Calculating the relative positions to the enlarged image
d_row = o_row - fo_row;
d_col = o_col - fo_col;
% Interpolating on 2 first axis and the result between them
R1 = (1-d_row)*Q11 + d_row*Q21;
R2 = (1-d_row)*Q12 + d_row*Q22;
T(row,col) = round((1-d_col)*R1 + d_col*R2);
end
end
end
for i = [I, J, K];
imshow(i);
end
I, J, K are 16-bit images.
The script keeps trying to pump out the images (but doesn't) and gets into an infinite loop.
Is there something I'm missing?
You can store multiple images in a matrix if they have the same sizes.
However, You should store the images in a cell if they have different sizes. This method is less messier because you don't need to worry about how to extract them later.
define a cell with the size equal to the number of images.
numImages = 3;
Images = cell(1,numImage);
Store an image I into a cell:
Images{1,1} = I;
Now go over the images and show them
for ii = 1:3
imshow(Images{1,ii});
end
Example:
I = imread('cameraman.tif');
J = imread('peppers.png');
K = imread('snowflakes.png');
Images = cell(1,3);
Images{1,1} = I;
Images{1,2} = J;
Images{1,3} = K;
for ii=1:numel(Images)
figure;imshow(Images{1,ii});
end
For better understanding as to which point exactly you are missing/what is happening here: Using square brackets works like a concatenation here. So the line
i = [I, J, K] % separated with commas or spaces for horzcat
i = [I; J; K] % separated with semi-colons for vertcat
does the same as horzcat or vertcat:
i = horzcat(I, J, K);
i = vertcat(I, J, K);
Let's say I, J, K are 64x64 gray valued images. A (horizontal) concatenation will create a 64x192 matrix. The for loop will go through your matrix column-wise, which means it will extract a 64x1 vector 192-times (or much more often for larger images, which might feel like "infinite"). Displaying only a vector with imshow() won't show anything.
As already pointed out, using cells is a more flexible way to store images. When using arrays you have to handle each dimension (and this only works if your images are equal in size):
sizeImage = size(I); % assume all img are same size (as I)
numImages = 3; % needed for allocating array
% init array for imgs and fill images into array: e.g. 64x64x3
imageArray = zeros([sizeImage numImages]);
imageArray(:,:,1) = I; % :,: selects all elements of a dimension
imageArray(:,:,2) = J;
imageArray(:,:,3) = K;
for n = 1:numImages % iterate over image index
figure; imshow(imageArray(:,:,n)); % n = 1, 2 ... , numImages
end % is used for position in imageArray
Using the colon : when accessing arrays/cells selects all elements of a dimension. E.g. imageArray(:,:,n) will select all elements of first and second dimension, corresponding to a 64x64 image. For RGB images an array with 3 images will be 64x64x3x3 and you'll have to use imageArray(:,:,:,n) to select all three color channels.
Note that using for i = img_array won't work, as this will give vectors again.
E.g. for img_array: 64x64x5 (five 64x64 gray-valued images), this will iterate over all but one dimensions (and assign the remaining dim to i): img_array(:,1,1), img_array(:,2,1), img_array(:,3,1), ..., img_array(:,1,2), img_array(:,2,2) ..., img_array(:,64,5) and will again produce 64*3 = 192 vectors for i.
As was already pointed out, if you have variable image sizes, using cell arrays is the way. You may want to consult: Difference between cell and matrix in matlab?
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 have written a matlab function that performs some cpu intensive operations on images. I would like to improve the speed of this function, but cannot think of anymore ways to optimize it. Does anyone know how to further optimise the function below?
function imageMedoid(imageList, resizeFolder, outputFolder, x, y)
% local variables
medoidImage = zeros([1, y*x, 3]);
alphaImage = zeros([y x]);
medoidContainer = zeros([y*x, length(imageList), 3]);
% loop through all images in the resizeFolder
for i=1:length(imageList)
% get filename and load image
fname = imageList(i).name;
container = im2double(imread([resizeFolder fname]));
% load alpha channel, convert to zeros and ones, add to alphaImage
[~,~,alpha] = imread([resizeFolder fname]);
alpha = double(alpha) / 255;
alphaImage = alphaImage + alpha;
% add (r,g,b) values to medoidContainer and reshape to single line
medoidContainer(:, i, :) = reshape(container, [y*x 3]);
end
% loop through every pixel in medoidContainer
for i=1:length(medoidContainer)
% calculate distances between all values for current pixel
distances = pdist(squeeze(medoidContainer(i,:,1:3)));
% convert found distances to matrix of distances
distanceMatrix = squareform(distances);
% find index of image with the medoid value
[~, j] = min(mean(distanceMatrix,2));
% write found medoid value to medoidImage
medoidImage(1, i, 1:3) = medoidContainer(i, j, 1:3);
end
% replace values larger than one in alpha channel
alphaImage(alphaImage > 1) = 1;
% reshape image to original proportions
medoidImage = reshape(medoidImage, y, x, 3);
% save medoid image
imwrite(medoidImage, [outputFolder 'medoid.png'], 'Alpha', alphaImage);
Any suggestions would be greatly appreciated!
I would suggest that you use the built-in profiler of MATLAB in order to have a better idea on what are the most time consuming operations and then try to optimize there.
Tools -> Open Profiler -> imageMedoid(...)
You are calling
imread([resizeFolder fname])
twice in your first for-loop. Call it once as
[A,~,alpha] = imread([resizeFolder fname]);
This should result in some improvement.
I have the following error in my code
Assignment has more non-singleton rhs
dimensions than non-singleton
This is the code:
string =['d:\face\face\ffw' int2str(r) '_' int2str(Sel(nSelRow,t)) '.bmp'];
A = imread(string);
B = im2double(A);
Train_Dat(:,:,s)=B;
Update 1:
When I updated the code we got new error in the next row
my code
for r=1:Class_Num
for t=1:Class_Train_Num
%string=['e:\face_lib\feret_80\ff' int2str(r) '_' int2str(t) '.tif'];
string =['d:\face\face\ffw' int2str(r) '_' int2str(Sel(nSelRow,t)) '.bmp'];
A=imread(string);
B=im2double(A);
Train_Dat(:,:,:,s)=B;
Train_Dat_Vector(:,s)=B(:); %here new error Subscripted assignment dimension mismatch.
s=s+1;
end
Update 2:
my define for
nImgW = 40;
nImgH = 40;
nImgSize = nImgW*nImgH;
Train_Dat_Vector = zeros( nImgSize, Train_Num );
A=imread(string);
B=im2double(A);
Train_Dat(:,:,:,s)=B;
Train_Dat_Vector(:,s)=B(:);%here i want convert matrix to 40x40,Train_Num
s=s+1;
I think the problem is that B is most likely a 3-D RGB image, and you're trying to assign it to a single 2-D plane of your 3-D matrix Train_Dat. If you're trying to collect a set of 3-D images to use as training data, you will have to make Train_Dat either a 4-D matrix (if all of your images have the exact same dimensions) or a cell array (with one image per cell):
Example #1: a 4-D matrix...
nRows = 100; %# The number of rows in the images
nCols = 100; %# The number of columns in the images
nDepth = 3; %# The depth of the images (3 color planes for RGB images)
nImages = 5; %# The number of images you will use
Train_Dat = zeros(nRows,nCols,nDepth,nImages); %# Initialize to zeros
Train_Dat(:,:,:,1) = B; %# Assign B as the first image
If you want to use this option, but all of your images are not the same size, you will have to resize them all to a given size. One way to do this, if you have the Image Processing Toolbox, is to use the function IMRESIZE:
newSize = [40 40]; %# The new size the image will be
C = imresize(B,newSize); %# Resize image B
If you don't have access to the Image Processing Toolbox, one alternative is to use the function INTERP2 to resize your image. Here's one example of resizing a 3-D RGB image of type UINT8:
B = double(B); %# Convert B to double (needed to use INTERP2)
[nRows,nCols,nDepth] = size(B); %# Get the old image size
C = zeros(40,40,3,'uint8'); %# Initialize the new 3-D 40-by-40 uint8 image
xi = linspace(1,nCols,40); %# Create a down-sampled set of x points
yi = linspace(1,nRows,40); %# Create a down-sampled set of y points
[X,Y] = meshgrid(xi,yi); %# Create 40-by-40 grids of x and y points
C(:,:,1) = interp2(B(:,:,1),X,Y,'spline'); %# Interpolate the red color plane
C(:,:,2) = interp2(B(:,:,2),X,Y,'spline'); %# Interpolate the green color plane
C(:,:,3) = interp2(B(:,:,3),X,Y,'spline'); %# Interpolate the blue color plane
The image C will now be a down-sampled 40-by-40 version of B.
Example #2: a cell array...
nImages = 5; %# The number of images you will use
Train_Dat = cell(1,nImages); %# Initialize the cell array
Train_Dat{1} = B; %# Assign B as the first image
In this case, the images you add to each cell can be different sizes and types, so no resizing is needed.