Position of median blocks - matlab

I have a problem in matrix (A) 9*9, I need to extract median for each block 3*3.
Also, I need to know the position of the median in each block.
Note:
I know how can extract the median by using
m=median(median(B));
and B is block in matrix
B = A(i:i+L-1, j:j+L-1);
thank you.

If you have the image processing toolbox installed you can use:
medianBlocks = blockproc(A,[3,3],#(X) ones(size(X.data))*median(X.data(:)))
A == medianBlocks

If you don't have the Image Processing Toolbox, I suggest transforming each neighbourhood so that it fits into a single column then you stack all of these columns together to create a single matrix. Once you have this new matrix, find the median of each column, then use this result for further processing.
The best way to do this would be to use im2col to create this temporary matrix and do the median on this matrix over the columns. The output will give you the median of each neighbourhood in your image.
Ironically, im2col is part of the Image Processing Toolbox. However, Divakar has made a vectorized and more efficient version of it that doesn't require the toolbox and only relies on native MATLAB commands.
See here: Efficient Implementation of `im2col` and `col2im`
As such, depending on whether you want overlapping blocks or sliding blocks, use whichever command suits you. Therefore:
out = im2col_distinct(A, [3 3]); %//OR
%out = im2col_sliding(A, [3 3]);
out_median = median(out);
out_median will contain a vector that has the median of each block in the image. Now, if you want to find which location in each window has the median, you can make a bsxfun call:
loc = bsxfun(#eq, out, out_median);
loc will contain the column-major locations of each block. If you want the row and column locations with respect to each block, you can use ind2sub:
[rows,cols] = ind2sub([3 3], loc);
rows and cols will give you the row and column locations of where the median was located in each block neighbourhood of your image.

Related

3D Convolution in MATLAB

I'm currently trying to use convolution to average blocks in my data and turn my current, 336x264x25x27, grid into a new, 100x100x27, grid.
To achieve this I've been trying to use convolution.
In a 2-D setting I've been able to convert a 336x264 matrix to 100x100 using the conv2 function in matlab. I'm now trying to use convn to accomplish a similar task in 4-D.
As stated, currently I'm using the convn function. I'm trying to average out cells over the first two dimensions so that I end up with a 100x100x27 matrix. My code is as follows:
A = rand(336,264,25,27); % Sample Data
A = A(:,:,13,:); % This line and the following line eliminate the third dimension (time) which will be constant throughout my output. Now "A" is 336x264x27 after using "squeeze".
A = squeeze(A);
B = ones(100,100,27); % This is the size of matrix that I would like to achieve. I was under the impression that "B" was the size matrix that you want to inevitably end up with but I believe I am mistaken.
C = convn(A,B); % C would hopefully by my 100x100x27 matrix.
Currently, this is resulting in a 435x363x53 matrix. If you could help me with my logic and show me how I might turn "A" into a 100x100x27 matrix using convolution it would be much appreciated!

How to compute histogram using three variables in MATLAB?

I have three variables, e.g., latitude, longitude and temperature. For each latitude and longitude, I have corresponding temperature value. I want to plot latitude v/s longitude plot in 5 degree x 5 degree grid , with mean temperature value inserted in that particular grid instead of occurring frequency.
Data= [latGrid,lonGrid] = meshgrid(25:45,125:145);
T = table(latGrid(:),lonGrid(:),randi([0,35],size(latGrid(:))),...
'VariableNames',{'lat','lon','temp'});
At the end, I need it somewhat like the following image:
Sounds to me like you want to scale your grid. The easiest way to do this is to smooth and downsample.
While 2d histograms also bin values into a grid, using a histogram is not the way to find the mean of datapoints in a smooth grid. A histogram counts the occurrence of values in a set of ranges. In a 2d example, a histogram would take the input measurements [1, 3, 3, 5] and count the number of ones, the number of threes, etc. A 2d histogram will count occurrences of pairs of numbers. (You might want to use histogram to help organize a measurements taken at irregular intervals, but that would be a different question)
How to smooth and downsample without the Image Processing Toolbox
Keep your data in the 2d matrix format rather than reshaping it into a table. This makes it easier to find the neighbors of each grid location.
%% Sample Data
[latGrid,lonGrid] = meshgrid(25:45,125:145);
temp = rand(size(latGrid));
There are many tools in Matlab for smoothing matrices. If you want to have the mean of a 5x5 window. You can write a for-loop, use a convolution, or use filter2. My example uses convolution. For more on convolutional filters, I suggest the wikipedia page.
%% Mean filter with conv2
M = ones(5) ./ 25; % 5x5 mean or box blur filter
C_temp = conv2(temp, M, 'valid');
C_temp is a blurry version of the original temperature variable with a slightly smaller size because we can't accurately take the mean of the edges. The border is reduced by a frame of 2 measurements. Now, we just need to take every fifth measurement from C_temp to scale down the grid.
%% Subsample result
C_temp = C_temp(1:5:end, 1:5:end);
% Because we removed a border from C_temp, we also need to remove a border from latGrid and lonGrid
[h, w] = size(latGrid)
latGrid = latGrid(5:5:h-5, 5:5:w-5);
lonGrid = lonGrid(5:5:h-5, 5:5,w-5);
Here's what the steps look like
If you use a slightly more organized, temp variable. It's easier to see that the result is correct.
With Image Processing Toolbox
imresize has a box filter method option that is equivalent to a mean filter. However, you have to do a little calculation to find the scaling factor that is equivalent to using a 5x5 window.
C_temp = imresize(temp, scale, 'box');

How to compute the mean value of all sub-imges in image I

I have a task that is compute the mean value of a sub-image that extract from input image I. Let explain my task. I have a image I (i.e, 9x9), and a window (i.e size 3x3). The window will be run from top-left to bottom-right of image. Hence, it will extract the input image into many subimage. I want to compute the mean value of these sub-images. Could you suggest to me some matlab code to compute it.
This is my solution. But it does not work.
First, I defined a window as Gaussian
Second, the Gaussian function will run from top-left to bottom-right using convolution function. (Note that, it must be use Gaussian Kernel)
Compute the mean value of each sub-window
%% Given Image I,Defined a Gaussian Kernel
sigma=3;
K=fspecial('gaussian',round(2*sigma)*2+1,sigma);
KI=conv2(I,K,'same');
%% mean value
mean(KI)
The problem in here is that mean value off all sub-image will have size similar image I. Because each pixel in image will made a sub-image. But my code returns only a value. What is problem?
If it is your desire to compute the average value in each sub-image once you filter your image with a Gaussian kernel, simply convolve your image with a mean or average filter. This will collect sub-images within your original image and for each output location, you will compute the average value.
Going with your initial assumption that the mask size is 3 x 3, simply use conv2 in conjunction with a 3 x 3 mask that has all 1/9 coefficients. In other words:
%// Your code
%% Given Image I,Defined a Gaussian Kernel
sigma=3;
K=fspecial('gaussian',round(2*sigma)*2+1,sigma);
KI=conv2(I,K,'same');
%// New code
mask = (1/9)*ones(3,3);
out = conv2(KI, mask, 'same');
Each location in out will give you what the average value was for each 3 x 3 sub-image in your Gaussian filtered result.
You can also create the averaging mask by using fspecial with the flag average and specifying the size / width of your mask. Given that you are already using it in your code, you already know of its existence. As such, you can also do:
mask = fspecial('average', 3);
The above code assumes the width and height of the mask are the same, and so it'll create a 3 x 3 mask of all 1/9 coefficients.
Aside
conv2 is designed for general 2D signals. If you are looking to filter an image, I recommend you use imfilter instead. You should have access to it, since fspecial is part of the Image Processing Toolbox, and so is imfilter. imfilter is known to be much more efficient than conv2, and also makes use of Intel Integrated Performance Primitives (Intel IPP) if available (basically if you are running MATLAB on a computer that has an Intel processor that supports IPP). Therefore, you should really perform your filtering this way:
%// Your code
%% Given Image I,Defined a Gaussian Kernel
sigma=3;
K=fspecial('gaussian',round(2*sigma)*2+1,sigma);
KI=imfilter(I,K,'replicate'); %// CHANGE
%// New code
mask = fspecial('average', 3);
out = imfilter(KI, mask, 'replicate'); %// CHANGE
The replicate flag is for handling the boundary conditions. When your mask goes out of bounds of the original image, replicate simply replicates the border of each side of your image so that the mask can fit comfortably within the image when performing your filtering.
Edit
Given your comment, you want to extract the subimages that are seen in KI. You can use the very powerful im2col function that's part of the Image Processing Toolbox. You call it like so:
B = im2col(A,[m n]);
A will be your input image, and B will be a matrix that is of size mn x L where L would be the total number of possible sub-images that exist in your image and m, n are the height and width of each sub-image respectively. How im2col works is that for each sub-image that exists in your image, it warps them so that it fits into a single column in B. Therefore, each column in B produces a single sub-image that is warped into a column. You can then use each column in B for your GMM modelling.
However, im2col only returns valid sub-images that don't go out of bounds. If you want to handle the edge and corner cases, you'll need to pad the image first. Use padarray to facilitate this padding. Therefore, to do what you're asking, we simply do:
Apad = padarray(KI, [1 1], 'replicate');
B = im2col(Apad, [3 3]);
The first line of code will pad the image so that you have a 1 pixel border that surrounds the image. This will allow you to extract 3 x 3 sub-images at the border locations. I use the replicate flag so that you can simply duplicate the border pixels. Next, we use im2col so that you get 3 x 3 sub-images that are then stored in B. As such, B will become a 9 x L matrix where each column gives you a 3 x 3 sub-image.
Be mindful that im2col warps these columns in column-major format. That means that for each sub-image that you have, it takes each column in the sub-image and stacks them on top of each other giving you a 9 x 1 column. You will have L total sub-images, and these are concatenated horizontally to produce a 9 x L matrix. Also, keep in mind that the sub-images are read top-to-bottom, then left-to-right as this is the nature of MATLAB operating in column-major order.

How to calculate per-page STD in matlab?

Suppose I have matrix A of 100x200x300. Third dimension is called "page" in Matlab and this matrix has 300 pages then.
Now I want to calculate standard deviation within each page and get a result matrix of 1x1x300.
I can't just do
std(std(A,0,1),0,2)
because normalization will be wrong as I think.
You need to collapse the first two dimensions into one (i.e. into columns) using reshape; and then compute std along each column:
Ar = reshape(A, size(A,1)*size(A,2), size(A,3));
result = std(Ar);
This will give you a 1x300 vector as result. If you really need it to be 1x1x300, use
result = shiftdim(result, -1);

Remove duplicates in correlations in matlab

Please see the following issue:
P=rand(4,4);
for i=1:size(P,2)
for j=1:size(P,2)
[r,p]=corr(P(:,i),P(:,j))
end
end
Clearly, the loop will cause the number of correlations to be doubled (i.e., corr(P(:,1),P(:,4)) and corr(P(:,4),P(:,1)). Does anyone have a suggestion on how to avoid this? Perhaps not using a loop?
Thanks!
I have four suggestions for you, depending on what exactly you are doing to compute your matrices. I'm assuming the example you gave is a simplified version of what needs to be done.
First Method - Adjusting the inner loop index
One thing you can do is change your j loop index so that it only goes from 1 up to i. This way, you get a lower triangular matrix and just concentrate on the values within the lower triangular half of your matrix. The upper half would essentially be all set to zero. In other words:
for i = 1 : size(P,2)
for j = 1 : i
%// Your code here
end
end
Second Method - Leave it unchanged, but then use unique
You can go ahead and use the same matrix like you did before with the full two for loops, but you can then filter the duplicates by using unique. In other words, you can do this:
[Y,indices] = unique(P);
Y will give you a list of unique values within the matrix P and indices will give you the locations of where these occurred within P. Note that these are column major indices, and so if you wanted to find the row and column locations of where these locations occur, you can do:
[rows,cols] = ind2sub(size(P), indices);
Third Method - Use pdist and squareform
Since you're looking for a solution that requires no loops, take a look at the pdist function. Given a M x N matrix, pdist will find distances between each pair of rows in a matrix. squareform will then transform these distances into a matrix like what you have seen above. In other words, do this:
dists = pdist(P.', 'correlation');
distMatrix = squareform(dists);
Fourth Method - Use the corr method straight out of the box
You can just use corr in the following way:
[rho, pvals] = corr(P);
corr in this case will produce a m x m matrix that contains the correlation coefficient between each pair of columns an n x m matrix stored in P.
Hopefully one of these will work!
this works ?
for i=1:size(P,2)
for j=1:i
Since you are just correlating each column with the other, then why not just use (straight from the documentation)
[Rho,Pval] = corr(P);
I don't have the Statistics Toolbox, but according to http://www.mathworks.com/help/stats/corr.html,
corr(X) returns a p-by-p matrix containing the pairwise linear correlation coefficient between each pair of columns in the n-by-p matrix X.