I have a three-dimensional array (hyperspectral image) and I would like to resample for each element of the two first dimensions (each pixel) the third dimension (wavelength).
I have been trying a nested for loop without success and I think i am understanding wrongly the concept. Here what I am trying:
T_Ire = zeros(size(I)); % initialize array
for i = 1:numel(I(:,1,1))
for t = 1:numel(I(1,:,1))
step=squeeze(I(i,t,:));
step=double(step);
step=step';
step=resample(step',2,3);
T_Ire(i,t,:)=step;
end
end
I is the 3D array (hyperspectral image).
There are two issues here:
When you call step=resample(step',2,3);, your array will be two thirds the size it was. Then, when you try to put it back on the cubic matrix, it will not fit because it has fewer elements. You could solve that by initializing T_Ire with the appropriate size.
Like this:
T_Ire = zeros(size(I, 1), size(I, 2), size(I, 3) * 2 / 3);
When you take the array I(i,t,:), it's size will be 1x1xN (where N = the size of the third dimension). But when you call step=squeeze(I(i,t,:));, now it is Nx1. To add it back, you should call permute or reshape to resize it back to 1x1xN.
Using permute:
T_Ire(i,t,:) = permute(step, [3 2 1]);
Using reshape:
T_Ire(i,t,:) = reshape(step, [1 1 numel(step)]);
Related
The sort() function sorts the elements row/column wise but how to sort the elements absolutely? The result should be another matrix with smallest element in (1,1) , second smallest in (1,2) and so on.
Take some random input
input = rand(5,10);
If you want the output to be a row vector, simply use
sortedRow = sort(input(:)).';
If you want the result to be the same shape as the input, then use
sortedOriginalShape = reshape(sort(input(:)), size(input,2), size(input,1)).';
Note that when maintaining the shape, we must use the reversed size dimensions and then transpose. This is because otherwise the result is column-wise i.e. smallest element in (1,1), next in (2,1) etc, which is the opposite of what you requested.
You can use the column operator (:) to vectorize all elements of 'nxm' matrix as a vector of 'nxm' elements and sort this vector. Then you can use direct assignement or 'reshape' function to store elements as matricial form.
All you need to know is that matlab use column-major-ordering to vectorize/iterate elements:
A = rand(3, 5);
A(:) = sort(A(:);
Will preserve colum-major-ordering, or as you said you prefer row-major ordering:
A = rand(3, 5);
A = reshape(sort(A(:)), fliplr(size(A)).';
Note the fliplr to store columnwise with reversed dimension and then the .' operator to transpose again the result.
EDIT
Even if matlab uses column-major-ordering for storing elements in memory, here below are two generic routines to work with row-major-order whatever the number of dimension of your array (i.e. no limited to 2D):
function [vector] = VectorizeWithRowMajorOrdering(array)
%[
axisCount = length(size(array)); % Squeezed size of original array
permutation = fliplr(1:(axisCount + 2)); % +2 ==> Trick to vectorize data in correct order
vector = permute(array, permutation);
vector = vector(:);
%]
end
function [array] = ReshapeFromRowMajorOrdering(vector, siz)
%[
siz = [siz( : ).' 1]; % Fix size if only one dim
array = NaN(siz); % Init
axisCount = length(size(array)); % Squeezed size!
permutation = fliplr(1:(axisCount + 2)); % +2 ==> Trick to vectorize data in correct order
array = reshape(vector, [1 1 fliplr(size(array))]);
array = ipermute(array, permutation);
%]
end
This can be useful when working with data coming from C/C++ (these languages use row-major-ordering). In your case this can be used this way:
A = rand(3, 5);
A = ReshapeFromRowMajorOrdering(sort(A(:)), size(A));
I wish to append a row-vector (and later, also a column vector) to an existing x by y by z matrix. So basically "Add a new row (at the "bottom") for each z in the original 3d matrix. Consider the following short Matlab program
appendVector = [1 2 3 4 5]; % Small matrix for brevity. Actual matrices used are much larger.
origMatrix = ones(5,5,3);
appendMatrix = [origMatrix( ... ); appendVector];
My question is: How do I adress (using Matlab-style matrix adressing, not a "manual" C-like loop) origMatrix( ... ) in order to append the vector above? Feel free to also include a suggestion on how to do the same operation for a column-vector (I am thinking that the correct way to do the latter is to simply use the '-operator in Matlab).
A "row" in a 3D matrix is actually a multi-dimensional array.
size(origMatrix(1,:,:))
% 5 3
So to append a row, you would need to append a 5 x 3 array.
toAppend = rand(5, 3);
appendMatrix = cat(1, origMatrix, toAppend);
You could append just a 5 element vector and specify an index for the third dimension. In this case, the value for the "row" for all other indices in the third dimension would be filled with zeros.
appendVector = [1 2 3 4 5];
origMatrix = ones(5,5,3);
appendMatrix = origMatrix;
appendMatrix(end+1, :, 1) = appendVector;
If instead, you want to append the same vector along the third dimension, you could use repmat to turn your vector into a 1 x 5 x 3 array and then append that.
appendVector = repmat([1 2 3 4 5], 1, 1, size(origMatrix, 3));
appendMatrix = cat(1, origMatrix, appendVector);
I got a satellite image of size [17935 10968] pixels, I want to cut image equally and process my required algorithm on individual parts (eg: I need to cut above pixel range into 4 equal parts).
How can I split image without loosing intermediate pixels? My requirement is like (1 to 5600 and 5601 to the end pixel).
And anybody got any idea how to split images that are this big in MATLAB?
Method 1
If you have the Image Processing Toolbox, this is the preferred and most efficient method. It utilizes the extremely useful blockproc function which is designed exactly for processing large image in blocks. For instance, it takes care of padding when your image does not divide equally into same size blocks and concatenates results from the processing of blocks into one result matrix.
Best you take a look at the official documentation, but here's how it would look like in your case:
vSize = [17935 10968];
imBig = rand([vSize 3]);
nParts = [2 2]; %means divide into 4 parts, 2 horizontal, 2 vertical
blockproc(imBig, ceil(vSize ./ nParts), #yourAlgorithm);
function res = yourAlgorithm(blockStruct)
%do your processing of the block here and
%optionally return a result in 'res'
%for example, just return the RGB vector of the first pixel
res = blockStruct.data(1,1,:);
end
Method 2
If you don't have the Image Processing Toolbox you can use the mat2cell function instead. Fisrt you figure out the required block sizes and then you get a cell array containing the different blocks. For such large images though, speed and memory may become an issue. The code is borrowed from this Matlab Central answer.
vSize = [17935 10968];
imBig = rand([vSize 3]);
nParts = [2 2]; %means divide into 4 parts, 2 horizontal, 2 vertical
%figure out the size of "regular" block and the last block
vRegBlockSize = ceil(vSize ./ nParts);
vLastBlockSize = vSize - vRegBlockSize .* (nParts - 1);
%put the sizes into a vector
vSplitR = [vRegBlockSize(1)*ones(1,nParts(1)-1), vLastBlockSize(1)];
vSplitC = [vRegBlockSize(2)*ones(1,nParts(2)-1), vLastBlockSize(2)];
%split the image
C = mat2cell(imBig, vSplitR, vSplitC, 3);
%access RGB pixel (x,y) in top left {1,1} block
p = C{1,1}(x, y, :);
upperLeft = theImage(1:5600, 1:5600, :);
upperRight = theImage(1:5600, 5601:end, :);
lowerLeft = theImage(5601:end, 1:5600, :);
lowerLeft = theImage(5601:end, 1:5601:end, :);
you can use reshape to make 4 matrices from the image:
A=reshape(Img, 17935 , 10968/4,[]);
then process A(:,:,1) , etc...
Use the following code to divide the image into 4 different images:
A=reshape(Img, 17935 , 10968/4, 3, []);
then A(:,:,:,1) is the first image.
Suppose A is your 17935x10968x3 matrix, I think you can do:
B = reshape(A, 17935, 10968 / 4, 4, 3);
In this way the last dimension still represents RGB. Only difference is that it becomes 4-D array.
I have a 3 dimensional (or higher) array that I want to aggregate by another vector. The specific application is to take daily observations of spatial data and average them to get monthly values. So, I have an array with dimensions <Lat, Lon, Day> and I want to create an array with dimensions <Lat, Lon, Month>.
Here is a mock example of what I want. Currently, I can get the correct output using a loop, but in practice, my data is very large, so I was hoping for a more efficient solution than the second loop:
% Make the mock data
A = [1 2 3; 4 5 6];
X = zeros(2, 3, 9);
for j = 1:9
X(:, :, j) = A;
A = A + 1;
end
% Aggregate the X values in groups of 3 -- This is the part I would like help on
T = [1 1 1 2 2 2 3 3 3];
X_agg = zeros(2, 3, 3);
for i = 1:3
X_agg(:,:,i) = mean(X(:,:,T==i),3);
end
In 2 dimensions, I would use accumarray, but that does not accept higher dimension inputs.
Before getting to your answer let's first rewrite your code in a more general way:
ag = 3; % or agg_size
X_agg = zeros(size(X)./[1 1 ag]);
for i = 1:ag
X_agg(:,:,i) = mean(X(:,:,(i-1)*ag+1:i*ag), 3);
end
To avoid using the for loop one idea is to reshape your X matrix to something that you can use the mean function directly on.
splited_X = reshape(X(:), [size(X_agg), ag]);
So now splited_X(:,:,:,i) is the i-th part
that contains all the matrices that should be aggregated which is X(:,:,(i-1)*ag+1:i*ag)) (like above)
Now you just need to find the mean in the 3rd dimension of splited_X:
temp = mean(splited_X, 3);
However this results in a 4D matrix (where its 3rd dimension size is 1). You can again turn it into 3D matrix using reshape function:
X_agg = reshape(temp, size(X_agg))
I have not tried it to see how much more efficient it is, but it should do better for large matrices since it doesn't use for loops.
I like blockproc, it makes working with large (very large) images easily. However, as far as I understand, it is limited to working with functions that output a matrix of the same size as the input they take.
So I was wondering if there is a way of replicating/simulating what blockproc does but for functions that output a cell array. We can either assume that the output array from the processing function is of the same dimensions of the input matrix, or that it just outputs one cell element, in which case the final output from the total processing would be a cell array with M x N elements, with M and N specifying the tiling for the processing.
I believe I can build this myself using cellfun, but I was wondering if there is are any other builtins or libraries (maybe third-party?) that I can use for this, and maybe even completely avoid reinventing the wheel.
More specifically, I am looking for something that has the same strengths as blockproc:
Can load a large image from disk progressively tile-by-tile to minimize the memory footprint of the processing
Takes care of the final concatenation of results for building the final cell array
Has an interface similar to blockproc (e.g. # of tiles, etc.)
Below is a solution that satisfies your criteria except for the first point
Use the IM2COL function to arrange distinct image blocks from the image into columns, then apply your function to each column storing the result in a cell array.
Of course this only works if all blocks fit into memory, otherwise you would have to manually write code that extracts one block at a time and process it in that way...
%# read image
img = im2double(imread('tire.tif'));
%# blocks params
sizBlk = [8 8];
numBlk = ceil( size(img) ./ sizBlk );
%# extract blocks
B = im2col(img, sizBlk, 'distinct');
B = reshape(B, [sizBlk size(B,2)]); %# put blocks on the 3rd dimension
B = squeeze( num2cell(B,[1 2]) ); %# convert to cell array
B = reshape(B, numBlk); %# reshape as blocks overlayed on image
%# process blocks
myFcn = #(blk) [mean2(blk) std2(blk)]; %# or any other processing function
I = cellfun(myFcn, B, 'UniformOutput',false);
%# in this example, we can show each component separately
subplot(121), imshow( cellfun(#(c)c(1),I) ), title('mean')
subplot(122), imshow( cellfun(#(c)c(2),I) ), title('std')
Alternatively, you could still use the BLOCKPROC function, but you have to call it multiple times, each time computing a single feature:
%# compute one feature at a time
b1 = blockproc(img, sizBlk, #(b)mean2(b.data), 'PadPartialBlocks',true);
b2 = blockproc(img, sizBlk, #(b)std2(b.data), 'PadPartialBlocks',true);
%# combine into cellarray of features
II = arrayfun(#(varargin)[varargin{:}], b1, b2, 'UniformOutput',false);
%# compare to previous results
isequal(I,II)
I've been doing something similar, although with numeric values rather than cell.
Something like this should work :
I = imread('pout.tif');
G = blockproc(I, [8 8], #(b) shiftdim(imhist(b.data)', -1), 'PadPartialBlocks', true);
G = reshape(G, size(G, 1) * size(G, 2), size(G, 3));
pout.tif is a greyscale image but I'm sure this can be changed up for RGB.
Also take care when using shiftdim, imhist returns a row vector so I transpose it to a column.