How to Split Images into parts using MATLAB - matlab

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.

Related

Matlab: Vectorizing 4 nested for loops

So, I need to vectorize some for loops into a single line. I understand how vectorize one and two for-loops, but am really struggling to do more than that. Essentially, I am computing a "blur" matrix M2 of size (n-2)x(m-2) of an original matrix M of size nxm, where s = size(M):
for x = 0:1
for y = 0:1
m = zeros(1, 9);
k = 1;
for i = 1:(s(1) - 1)
for j = 1:(s(2) - 1)
m(1, k) = M(i+x,j+y);
k = k+1;
end
end
M2(x+1,y+1) = mean(m);
end
end
This is the closest I've gotten:
for x=0:1
for y=0:1
M2(x+1, y+1) = mean(mean(M((x+1):(3+x),(y+1):(3+y))))
end
end
To get any closer to a one-line solution, it seems like there has to be some kind of "communication" where I assign two variables (x,y) to index over M2 and index over M; I just don't see how it can be done otherwise, but I am assured there is a solution.
Is there a reason why you are not using MATLAB's convolution function to help you do this? You are performing a blur with a 3 x 3 averaging kernel with overlapping neighbourhoods. This is exactly what convolution is doing. You can perform this using conv2:
M2 = conv2(M, ones(3) / 9, 'valid');
The 'valid' flag ensures that you return a size(M) - 2 matrix in both dimensions as you have requested.
In your code, you have hardcoded this for a 4 x 4 matrix. To double-check to see if we have the right results, let's generate a random 4 x 4 matrix:
rng(123);
M = rand(4, 4);
s = size(M);
If we run this with your code, we get:
>> M2
M2 =
0.5054 0.4707
0.5130 0.5276
Doing this with conv2:
>> M2 = conv2(M, ones(3) / 9, 'valid')
M2 =
0.5054 0.4707
0.5130 0.5276
However, if you want to do this from first principles, the overlapping of the pixel neighbourhoods is very difficult to escape using loops. The two for loop approach you have is good enough and it tackles the problem appropriately. I would make the size of the input instead of being hard coded. Therefore, write a function that does something like this:
function M2 = blur_fp(M)
s = size(M);
M2 = zeros(s(1) - 2, s(2) - 2);
for ii = 2 : s(1) - 1
for jj = 2 : s(2) - 1
p = M(ii - 1 : ii + 1, jj - 1 : jj + 1);
M2(ii - 1, jj - 1) = mean(p(:));
end
end
The first line of code defines the function, which we will call blur_fp. The next couple lines of code determine the size of the input matrix as well as initialising a blank matrix to store out output. We then loop through each pixel location in the matrix that is possible without the kernel going outside of the boundaries of the image, we grab a 3 x 3 neighbourhood with each pixel location serving as the centre, we then unroll the matrix into a single column vector, find the average and store it in the appropriate output. For small kernels and relatively large matrices, this should perform OK.
To take this a little further, you can use user Divakar's im2col_sliding function which takes overlapping neighbourhoods and unrolls them into columns. Therefore, each column represents a neighbourhood which you can then blur the input using vector-matrix multiplication. You would then use reshape to reshape the result back into a matrix:
T = im2col_sliding(M, [3 3]);
V = ones(1, 9) / 9;
s = size(M);
M2 = reshape(V * T, s(1) - 2, s(2) - 2);
This unfortunately cannot be done in a single line unless you use built-in functions. I'm not sure what your intention is, but hopefully the gamut of approaches you have seen here have given you some insight on how to do this efficiently. BTW, using loops for small matrices (i.e. 4 x 4) may be better in efficiency. You will start to notice performance changes when you increase the size of the input... then again, I would argue that using loops are competitive as of R2015b when the JIT has significantly improved.

Loop through two variables in multidimensional array in Matlab

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)]);

Implementing convolution as a matrix multiplication

Pithy: Help with Matlab script that takes ImageData array and Convolution weights from Caffe and returns convolution. Please.
I am trying to recreate a convolution generated by Caffe in Matlab.
Let's make the following definitions
W**2 = Size of input
F**2 = Size of filter
P = Size of padding
S = Stride
K = Number of filters
The following text describes how to generalize the convolution as a matrix multiplication:
The local regions in the input image are stretched out into columns in an operation commonly called im2col. For example, if the input is [227x227x3] and it is to be convolved with 11x11x3 filters at stride 4, then we would take [11x11x3] blocks of pixels in the input and stretch each block into a column vector of size 11*11*3 = 363. Iterating this process in the input at stride of 4 gives (227-11)/4+1 = 55 locations along both width and height, leading to an output matrix X_col of im2col of size [363 x 3025], where every column is a stretched out receptive field and there are 55*55 = 3025 of them in total. Note that since the receptive fields overlap, every number in the input volume may be duplicated in multiple distinct columns.
From this, one could draw the conclusion that the im2col function call would look something like this:
input = im2col( input, [3*F*F, ((W-F)/S+1)**2)])
However, if I use the following parameter-values
W = 5
F = 3
P = 1
S = 2
K = 2
I get the following dimensions
>> size(input)
ans =
1 3 5 5
>> size(output)
ans =
1 2 3 3
>> size(filter)
ans =
2 3 3 3
And if I use the im2col function call from above, I end up with an empty matrix.
If I change the stride to 1 in the above example, the size of the input, the output and the filter remains the same. If I use Matlab's 'convn' command, the size is not the same as the actual output from Caffe.
>> size(convn(input,filter))
ans =
2 5 7 7
What would be the general way to resize your array for matrix multiplication?
You are using the second argument to im2col wrong, see the documentation.
You should give it the size of the filter window that you are trying to slide over the image, i.e.:
cols = im2col( input, [F, F])

RGB to YIQ conversion

I wrote code for rgb to yiq conversion.I get results but i don't know if this is correct.
%extract the red green blue elements
ImageGridRed = double(ImageRGB(:,:,1))';
ImageGridGreen = double(ImageRGB(:,:,2))';
ImageGridBlue = double(ImageRGB(:,:,3))';
%make the 300x300 matrices into 1x90000 matrices
flag = 1;
for i =1:1:300
for j = 1:1:300
imageGR(flag) = ImageGridRed(j,i);
imageGG(flag) = ImageGridGreen(j,i);
imageGB(flag) = ImageGridBlue(j,i);
flag = flag+1;
end
end
%put the 3 matrices into 1 matrix 90000x3
for j=1:1:300*300
colorRGB(j,1) = imageGR(j);
colorRGB(j,2) = imageGG(j);
colorRGB(j,3) = imageGB(j);
end
YIQ = rgb2ntsc([colorRGB(:,1) colorRGB(:,2) colorRGB(:,3)]);
I wrote this because the rgb2ntsc function needs mx3 matrix for input.I use the number 300 beacuse the picture is 300x300 pixels.I am going to seperate the picture in blocks in my project so dont give attention to the 300 number because i am going to change that, i put it just as an example.
thank you.
What you're doing is completely unnecessary. If you consult the documentation on rgb2ntsc, it also accepts a RGB image. Therefore, when you put in a RGB image, the output will be a 3 channel image, where the first channel is the luminance, or Y component and the second and third channels are the hue and saturation information (I and Q respectively). You don't need to decompose the image into a M x 3 matrix.
Therefore, simply do:
YIQ = rgb2ntsc(ImageRGB);
Make sure that ImageRGB is a RGB image where the first channel is red, second is green and third is blue.
Edit
With your comments, you want to take all of the pixels and place it into a M x 3 matrix where M is the total number of pixels. You would use this as input into rgb2ntsc. The function accepts a M x 3 matrix of RGB values where each row is a RGB tuple. The output in this case will be another M x 3 matrix where each row is its YIQ counterpart. Your code does do what you want it to do, but I would recommend that you do away with the for loops and replace it with:
colorRGB = reshape(permute(ImageRGB, [3 1 2]), 3, []).';,
After, do YIQ = rgb2ntsc(colorRGB);. colorRGB will already be a M x 3 matrix, so that column indexing you're doing is superfluous.
With the above using reshape and permute, it's very unnecessary to use the loops. In fact, I would argue that the for loop code is slower. Stick with the above code to get this done fast. Once you have your matrix in this fashion, then I suppose the code is doing what you want it to do.... however, I would personally just do a conversion on the image itself, then split it up into blocks or whatever you want to do after the fact.

Blockproc-like function for cell array output

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.