Downsize a matrix in which some entries are unknown - image

I have a 2D grid (G= 250x250) and just about 100 points of this is known and the rest is unknown (NaN). I want to resize this matrix. My problem is that imresize cannot do it for me in MATLAB, because it deletes the known values for me and just gives a NaN matrix.
Anyone know about a method that can do it for me? A suggestion is to use an interpolation method (e.g. by using inverse distance weighting), but I am not sure whether it works or not or even is there any better method?
G = NaN(250,250);
a = ceil(rand(1,50)*250*250);
b = ceil(rand(1,50)*250*250);
G (a) = 1; G (b) = 0;

How about this:
% find the non-NaN entries in G
idx = ~isnan(G);
% find their corresponding row/column indices
[i,j] = find(idx);
% resize your matrix as desired, i.e. scale the row/column indices
i = ceil(i*100/250);
j = ceil(j*100/250);
% write the old non-NaN entries to Gnew using accumarray
% you have to set the correct size of Gnew explicitly
% maximum value is chosen if many entries share the same scaled i/j indices
% NaNs are used as the fill
Gnew = accumarray([i, j], G(idx), [100 100], #max, NaN);
You can also choose a different accumulation function for accumarray if max is not suitable for you. And you can change the fill value from NaN to something else if it is not what you need.

Related

How to create a mask or detect image section based on the intensity value?

I have a matrix named figmat from which I obtain the following pcolor plot (Matlab-Version R 2016b).
Basically I only want to extract the bottom red high intensity line from this plot.
I thought of doing it in some way of extracting the maximum values from the matrix and creating some sort of mask on the main matrix. But I'm not understanding a possible way to achieve this. Can it be accomplished with the help of any edge/image detection algorithms?
I was trying something like this with the following code to create a mask
A=max(figmat);
figmat(figmat~=A)=0;
imagesc(figmat);
But this gives only the boundary of maximum values. I also need the entire red color band.
Okay, I assume that the red line is linear and its values can uniquely be separated from the rest of the picture. Let's generate some test data...
[x,y] = meshgrid(-5:.2:5, -5:.2:5);
n = size(x,1)*size(x,2);
z = -0.2*(y-(0.2*x+1)).^2 + 5 + randn(size(x))*0.1;
figure
surf(x,y,z);
This script generates a surface function. Its set of maximum values (x,y) can be described by a linear function y = 0.2*x+1. I added a bit of noise to it to make it a bit more realistic.
We now select all points where z is smaller than, let's say, 95 % of the maximum value. Therefore find can be used. Later, we want to use one-dimensional data, so we reshape everything.
thresh = min(min(z)) + (max(max(z))-min(min(z)))*0.95;
mask = reshape(z > thresh,1,n);
idx = find(mask>0);
xvec = reshape(x,1,n);
yvec = reshape(y,1,n);
xvec and yvec now contain the coordinates of all values > thresh.
The last step is to do some linear polynomial over all points.
pp = polyfit(xvec(idx),yvec(idx),1)
pp =
0.1946 1.0134
Obviously these are roughly the coefficients of y = 0.2*x+1 as it should be.
I do not know, if this also works with your data, since I made some assumptions. The threshold level must be chosen carefully. Maybe some preprocessing must be done to dynamically detect this level if you really want to process your images automatically. There might also be a simpler way to do it... but for me this one was straight forward without the need of any toolboxes.
By assuming:
There is only one band to extract.
It always has the maximum values.
It is linear.
I can adopt my previous answer to this case as well, with few minor changes:
First, we get the distribution of the values in the matrix and look for a population in the top values, that can be distinguished from the smaller values. This is done by finding the maximum value x(i) on the histogram that:
Is a local maximum (its bin is higher than that of x(i+1) and x(i-1))
Has more values above it than within it (the sum of the height of bins x(i+1) to x(end) < the height of bin x):
This is how it is done:
[h,x] = histcounts(figmat); % get the distribution of intesities
d = diff(fliplr(h)); % The diffrence in bin height from large x to small x
band_min_ind = find(cumsum(d)>size(figmat,2) & d<0, 1); % 1st bin that fit the conditions
flp_val = fliplr(x); % the value of x from large to small
band_min = flp_val(band_min_ind); % the value of x that fit the conditions
Now we continue as before. Mask all the unwanted values, interpolate the linear line:
mA = figmat>band_min; % mask all values below the top value mode
[y1,x1] = find(mA,1); % find the first nonzero row
[y2,x2] = find(mA,1,'last'); % find the last nonzero row
m = (y1-y2)/(x1-x2); % the line slope
n = y1-m*x1; % the intercept
f_line = #(x) m.*x+n; % the line function
And if we plot it we can see the red line where the band for detection was:
Next, we can make this line thicker for a better representation of this line:
thick = max(sum(mA)); % mode thickness of the line
tmp = (1:thick)-ceil(thick/2); % helper vector for expanding
rows = bsxfun(#plus,tmp.',floor(f_line(1:size(A,2)))); % all the rows for each column
rows(rows<1) = 1; % make sure to not get out of range
rows(rows>size(A,1)) = size(A,1); % make sure to not get out of range
inds = sub2ind(size(A),rows,repmat(1:size(A,2),thick,1)); % convert to linear indecies
mA(inds) = true; % add the interpolation to the mask
result = figmat.*mA; % apply the mask on figmat
Finally, we can plot that result after masking, excluding the unwanted areas:
imagesc(result(any(result,2),:))

Histogram of an image but without considering the first k pixels

I would like to create a histogram of an image but without considering the first k pixels.
Eg: 50x70 image and k = 40, the histogram is calculated on the last 3460 pixels. The first 40 pixels of the image are ignored.
The order to scan the k pixels is a raster scan order (starting from the top left and proceeds by lines).
Another example is this, where k=3:
Obviously I can't assign a value to those k pixels otherwise the histogram would be incorrect.
Honestly I have no idea how to start.
How can I do that?
Thanks so much
The vectorized solution to your problem would be
function [trimmedHist]=histKtoEnd(image,k)
imageVec=reshape(image.',[],1); % Transform the image into a vector. Note that the image has to be transposed in order to achieve the correct order for your counting
imageWithoutKPixels=imageVec(k+1:end); % Create vector without first k pixels
trimmedHist=accumarray(imageWithoutKPixels,1); % Create the histogram using accumarray
If you got that function on your workingdirectory you can use
image=randi(4,4,4)
k=6;
trimmedHistogram=histKtoEnd(image,k)
to try it.
EDIT: If you just need the plot you can also use histogram(imageWithoutKPixels) in the 4th row of the function I wrote
One of the way can be this:
histogram = zeros(1,256);
skipcount = 0;
for i = 1:size(image,1)
for j = 1:size(image,2)
skipcount = skipcount + 1;
if (skipcount > 40)
histogram(1,image(i,j)+1) = histogram(1,image(i,j)+1) + 1;
end
end
end
If you need to skip some exact number of top lines, then you can skip the costly conditional check and just start the outer loop from appropriate index.
Vec = image(:).';
Vec = Vec(k+1:end);
Hist = zeros(1, 256);
for i=0:255
grayI = (Vec == i);
Hist(1, i+1) = sum(grayI(:));
end
First two lines drop the first k pixels so they are not considered in the computation.
Then you check how many 0's you have and save it in the array. The same for all gray levels.
In the hist vector, in the i-th cell you will have the number of occurance of gray level (i-1).

How to construct a loop with reducing iterations

In MATLAB, I have a 256x256 RGB image and a 3x3 kernel that passes over it. The 3x3 kernel computes the colour-euclidean distance between every pair combination of the 9 pixels in the kernel, and stores the maximum value in an array. It then moves by 1 pixel and performs the same computation, and so on.
I can easily code the movement of the kernel over the image, as well as the extraction of the RGB values from the pixels in the kernel.
HOWEVER, I do have trouble efficiently computing the colour-euclidean distance operation for every pair combination of pixels.
For example if I had a 3x3 matrix with the following values:
[55 12 5; 77 15 99; 124 87 2]
I need to code a loop such that the 1st element performs an operation with the 2nd,3rd...9th element. Then the 2nd element performs the operation with the 3rd,4th...9th element and so on until finally the 8th element performs the operation with the 9th element. Preferrably, the same pixel combination shouldn't compute again (like if you computed 2nd with 7th, don't compute 7th with 2nd).
Thank you in advance.
EDIT: My code so far
K=3;
s=1; %If S=0, don't reject, If S=1 Reject first max distance pixel pair
OI=imread('onion.png');
Rch = im2col(OI(:,:,1),[K,K],'sliding')
Gch = im2col(OI(:,:,2),[K,K],'sliding')
Bch = im2col(OI(:,:,3),[K,K],'sliding')
indexes = bsxfun(#gt,(1:K^2)',1:K^2)
a=find(indexes);
[idx1,idx2] = find(indexes);
Rsqdiff = (Rch(idx2,:) - Rch(idx1,:)).^2
Gsqdiff = (Gch(idx2,:) - Gch(idx1,:)).^2
Bsqdiff = (Bch(idx2,:) - Bch(idx1,:)).^2
dists = sqrt(double(Rsqdiff + Gsqdiff + Bsqdiff)) %Distance values for all 36 combinations in 1 column
[maxdist,idx3] = max(dists,[],1) %idx3 is each column's index of max value
if s==0
y = reshape(maxdist,size(OI,1)-K+1,[]) %max value of each column (each column has 36 values)
elseif s==1
[~,I]=max(maxdist);
idx3=idx3(I);
n=size(idx3,2);
for i=1:1:n
idx3(i)=a(idx3(i));
end
[I,J]=ind2sub([K*K K*K],idx3);
for j=1:1:a
[M,N]=ind2sub([K*K K*K],dists(j,:));
M(I,:)=0;
N(:,J)=0;
dists(j,:)=sub2ind; %Incomplete line, don't know what to do here
end
[maxdist,idx3] = max(dists,[],1);
y = reshape(maxdist,size(OI,1)-K+1,[]);
end
If I understood the question correctly, you are looking to form unique pairwise combinations within a sliding 3x3 window, perform euclidean distance calculations consider all three channels, which we are calling as colour-euclidean distances and finally picking out the largest of all distances for each sliding window. So, for a 3x3 window that has 9 elements, you would have 36 unique pairs. If the image size is MxN, because of the sliding nature, you would have (M-3+1)*(N-3+1) = 64516 (for 256x256 case) such sliding windows with 36 pairs each, and therefore the distances array would be 36x64516 sized and the output array of maximum distances would be of size 254x254. The implementation suggested here involves im2col to extract sliding windowed elements as columns, nchoosek to form the pairs and finally performing the square-root of squared differences between three channels of such pairs and would look something like this -
K = 3; %// Kernel size
Rch = im2col(img(:,:,1),[K,K],'sliding')
Gch = im2col(img(:,:,2),[K,K],'sliding')
Bch = im2col(img(:,:,3),[K,K],'sliding')
[idx1,idx2] = find(bsxfun(#gt,(1:K^2)',1:K^2)); %//'
Rsqdiff = (Rch(idx2,:) - Rch(idx1,:)).^2
Gsqdiff = (Gch(idx2,:) - Gch(idx1,:)).^2
Bsqdiff = (Bch(idx2,:) - Bch(idx1,:)).^2
dists = sqrt(Rsqdiff + Gsqdiff + Bsqdiff)
out = reshape(max(dists,[],1),size(img,1)-K+1,[])
Your question is interesting and caught my attention. As far as I understood, you need to calculate euclidean distance between RGB color values of all cells inside 3x3 kernel and to find the largest one. I suggest a possible way to do this by using circshift function and 4D array operations:
Firstly, we pad the input array and create 8 shifted versions of it for each direction:
DIM = 256;
A = zeros(DIM,DIM,3,9);
A(:,:,:,1) = round(255*rand(DIM,DIM,3));%// random 256x256 array (suppose it is your image)
A = padarray(A,[1,1]);%// add zeros on each side of image
%// compute shifted versions of the input array
%// and write them as 4th dimension starting from shifted up clockwise:
A(:,:,:,2) = circshift(A(:,:,:,1),[-1, 0]);
A(:,:,:,3) = circshift(A(:,:,:,1),[-1, 1]);
A(:,:,:,4) = circshift(A(:,:,:,1),[ 0, 1]);
A(:,:,:,5) = circshift(A(:,:,:,1),[ 1, 1]);
A(:,:,:,6) = circshift(A(:,:,:,1),[ 1, 0]);
A(:,:,:,7) = circshift(A(:,:,:,1),[ 1,-1]);
A(:,:,:,8) = circshift(A(:,:,:,1),[ 0,-1]);
A(:,:,:,9) = circshift(A(:,:,:,1),[-1,-1]);
Next, we create an array that calculates the difference for all the possible combinations between all the above arrays:
q = nchoosek(1:9,2);
B = zeros(DIM+2,DIM+2,3,size(q,1));
for i = 1:size(q,1)
B(:,:,:,i) = (A(:,:,:,q(i,1)) - A(:,:,:,q(i,2))).^2;
end
C = sqrt(sum(B,3));
Finally, what we have is all the euclidean distances between all possible pairs within a 3x3 kernel. All we have to do is to extract the maximum values. As far as I understood, you do not consider image edges, so:
C = sqrt(sum(B,3));
D = zeros(DIM-2);
for i = 3:DIM
for j = 3:DIM
temp = C(i-1:i+1,j-1:j+1);
D(i-2,j-2) = max(temp(:));
end
end
D is the 254x254 array with maximum Euclidean distances for A(2:255,2:255), i.e. we exclude image edges.
Hope that helps.
P.S. I am amazed by the shortness of the code provided by #Divakar.

Speed-efficient classification in Matlab

I have an image of size as RGB uint8(576,720,3) where I want to classify each pixel to a set of colors. I have transformed using rgb2lab from RGB to LAB space, and then removed the L layer so it is now a double(576,720,2) consisting of AB.
Now, I want to classify this to some colors that I have trained on another image, and calculated their respective AB-representations as:
Cluster 1: -17.7903 -13.1170
Cluster 2: -30.1957 40.3520
Cluster 3: -4.4608 47.2543
Cluster 4: 46.3738 36.5225
Cluster 5: 43.3134 -17.6443
Cluster 6: -0.9003 1.4042
Cluster 7: 7.3884 11.5584
Now, in order to classify/label each pixel to a cluster 1-7, I currently do the following (pseudo-code):
clusters;
for each x
for each y
ab = im(x,y,2:3);
dist = norm(ab - clusters); // norm of dist between ab and each cluster
[~, idx] = min(dist);
end
end
However, this is terribly slow (52 seconds) because of the image resolution and that I manually loop through each x and y.
Are there some built-in functions I can use that performs the same job? There must be.
To summarize: I need a classification method that classifies pixel images to an already defined set of clusters.
Approach #1
For a N x 2 sized points/pixels array, you can avoid permute as suggested in the other solution by Luis, which could slow down things a bit, to have a kind of "permute-unrolled" version of it and also let's bsxfun work towards a 2D array instead of a 3D array, which must be better with performance.
Thus, assuming clusters to be ordered as a N x 2 sized array, you may try this other bsxfun based approach -
%// Get a's and b's
im_a = im(:,:,2);
im_b = im(:,:,3);
%// Get the minimum indices that correspond to the cluster IDs
[~,idx] = min(bsxfun(#minus,im_a(:),clusters(:,1).').^2 + ...
bsxfun(#minus,im_b(:),clusters(:,2).').^2,[],2);
idx = reshape(idx,size(im,1),[]);
Approach #2
You can try out another approach that leverages fast matrix multiplication in MATLAB and is based on this smart solution -
d = 2; %// dimension of the problem size
im23 = reshape(im(:,:,2:3),[],2);
numA = size(im23,1);
numB = size(clusters,1);
A_ext = zeros(numA,3*d);
B_ext = zeros(numB,3*d);
for id = 1:d
A_ext(:,3*id-2:3*id) = [ones(numA,1), -2*im23(:,id), im23(:,id).^2 ];
B_ext(:,3*id-2:3*id) = [clusters(:,id).^2 , clusters(:,id), ones(numB,1)];
end
[~, idx] = min(A_ext * B_ext',[],2); %//'
idx = reshape(idx, size(im,1),[]); %// Desired IDs
What’s going on with the matrix multiplication based distance matrix calculation?
Let us consider two matrices A and B between whom we want to calculate the distance matrix. For the sake of an easier explanation that follows next, let us consider A as 3 x 2 and B as 4 x 2 sized arrays, thus indicating that we are working with X-Y points. If we had A as N x 3 and B as M x 3 sized arrays, then those would be X-Y-Z points.
Now, if we have to manually calculate the first element of the square of distance matrix, it would look like this –
first_element = ( A(1,1) – B(1,1) )^2 + ( A(1,2) – B(1,2) )^2
which would be –
first_element = A(1,1)^2 + B(1,1)^2 -2*A(1,1)* B(1,1) + ...
A(1,2)^2 + B(1,2)^2 -2*A(1,2)* B(1,2) … Equation (1)
Now, according to our proposed matrix multiplication, if you check the output of A_ext and B_ext after the loop in the earlier code ends, they would look like the following –
So, if you perform matrix multiplication between A_ext and transpose of B_ext, the first element of the product would be the sum of elementwise multiplication between the first rows of A_ext and B_ext, i.e. sum of these –
The result would be identical to the result obtained from Equation (1) earlier. This would continue for all the elements of A against all the elements of B that are in the same column as in A. Thus, we would end up with the complete squared distance matrix. That’s all there is!!
Vectorized Variations
Vectorized variations of the matrix multiplication based distance matrix calculations are possible, though there weren't any big performance improvements seen with them. Two such variations are listed next.
Variation #1
[nA,dim] = size(A);
nB = size(B,1);
A_ext = ones(nA,dim*3);
A_ext(:,2:3:end) = -2*A;
A_ext(:,3:3:end) = A.^2;
B_ext = ones(nB,dim*3);
B_ext(:,1:3:end) = B.^2;
B_ext(:,2:3:end) = B;
distmat = A_ext * B_ext.';
Variation #2
[nA,dim] = size(A);
nB = size(B,1);
A_ext = [ones(nA*dim,1) -2*A(:) A(:).^2];
B_ext = [B(:).^2 B(:) ones(nB*dim,1)];
A_ext = reshape(permute(reshape(A_ext,nA,dim,[]),[1 3 2]),nA,[]);
B_ext = reshape(permute(reshape(B_ext,nB,dim,[]),[1 3 2]),nB,[]);
distmat = A_ext * B_ext.';
So, these could be considered as experimental versions too.
Use pdist2 (Statistics Toolbox) to compute the distances in a vectorized manner:
ab = im(:,:,2:3); % // get A, B components
ab = reshape(ab, [size(im,1)*size(im,2) 2]); % // reshape into 2-column
dist = pdist2(clusters, ab); % // compute distances
[~, idx] = min(dist); % // find minimizer for each pixel
idx = reshape(idx, size(im,1), size(im,2)); % // reshape result
If you don't have the Statistics Toolbox, you can replace the third line by
dist = squeeze(sum(bsxfun(#minus, clusters, permute(ab, [3 2 1])).^2, 2));
This gives squared distance instead of distance, but for the purposes of minimizing it doesn't matter.

Sparse matrix plot matlab

I have a 5000 *5000 sparse matrix with 4 different values. I want to visualise the nonzero elements with 4 different colors such that I can recognise the ratio of this values and the relationships between them,I use imagesc but I can not recognise very well among different values, especially the values with smaller ratio.I think if I use some symboles for each value , it works but I don't know how is it in Matlab. Any suggestion? The result of Dan code is in figure below.
You could reform the matrix to be a set of [X, Y, F] coordinates (re-using my answer from Resampling Matrix and restoring in one single Matrix):
Assuming your matrix is M
[X, Y] = meshgrid(1:size(M,1), 1:size(M,2));
Mf = M(:); %used again later, hence stored
V = [X(:), Y(:), Mf];
get rid of the zero elements
V(Mf == 0, :) = [];
At this point, if you have access to the statistics toolbox you can just go gscatter(V(:,1), V(:,2), V(:,3)) to get the correct plot otherwise continue with the following if you don't have the toolbox:
Find a list of the unique values in M
Vu = unique(V(:,3));
For each such value, plot the points as an xy scatter plot, note hold all makes sure the colour changes each time a new plot is added i.e. each new iteration of the loop
hold all;
for g = 1:length(Vu)
Vg = V(V(:,3)==Vu(g),:)
plot(Vg(:,1), Vg(:,2), '*');
a{g}=num2str(Vu(g));
end
legend(a);
Example M:
M = zeros(1000);
M(200,30) = 7;
M(100, 900) = 10;
M(150, 901) = 13;
M(600, 600) = 13;
Result:
Now i can answer the first part of the question. I suppose you need to do something like
sum(histc(A, unique(A)),2)
to count the number of unique values in the matrix.
temp = histc(A, unique(A)) "is a matrix of column histogram counts." So you get the counts of all values of unique(A) as they appear in A columns.
I'm doing stat = sum(temp,2) to get counts of all values of unique(A) in the whole matrix.
Then you can use the code proposed from #Dan to visualize the result.
hold all;
u=unique(A);
for i = 1:length(stat)
plot(u(i), stat(i)/max(stat), '*');
end
Please clarify what kind of relationship between the values do you mean?

Resources