regionprops orientation behaves differently with logical and integers - matlab

It seems that regionprops 'orientation' behaves differentlty if we give a logical or an integer :
A = [ 0 0 0 1 1 0 ;
0 0 0 1 0 0 ;
1 1 0 0 0 0 ;
0 0 0 0 0 0 ;
0 0 0 0 1 0 ;
0 0 0 0 0 0 ]
a_orientation = regionprops(A,'Orientation')
% Orientation: 28.1550
B = logical(A)
b_orientation = regionprops(B,'Orientation')
% 3x1 struct array with fields:
% Orientation
b_orientation.Orientation
% ans =
% 0
%
% ans =
% 45.0000
%
% ans =
% 0
%
How could I make regionprops behave with logical the same way as with integers without changing the type ?

When you pass A (a double precision matrix) into regionprops, it is treated as a label matrix with only one labeled region corresponding to the ones. It essentially assumes the data you are giving to it is the output from bwlabel. The metrics are computed across all the pixels labeled 1.
When you pass B (a logical matrix), it is treated as a binary image, in which there are three distinct regions of ones (i.e. 8-connected components) found. Metrics are returned for each region.
I don't believe there is any way to get regionprops to treat a logical input differently without first converting its type. Logical inputs appear to always undergo a connected-component processing step that identifies each "island" of ones as separate regions to analyze.

Related

How do I populate matrix with a vector, considering Matrix as chart and vector as line

Consider following values
result=zeros(11,11);
line=(4:0.4:8);
Imagine result as a 11x11 X-Y chart paper. So initially we have a blank chart paper. As in a chart plot, I want to populate values of line in result matrix so that we get an upward sloping line when we display matrix.
Consider following figure which I want as result.
Here, result matrix can be visualized as chart paper with origin at bottom left corner. Now, for X=1, line(1)=4; for X=2, line(2)=4.4,.. and so on.
I have written following code which serves the purpose.
result=zeros(11,11);
line=(4:0.4:8);
for i=1:length(line)
temp=floor(line(i));
result(length(line)-temp+1,i)=line(i);
end
Is there a more efficient way to implement this solution? (I shall be working with 20000x20000 matrix, so method needs to be fast)
As suggested in comments, Problem Description is as follows:
I have lets say 1000 lines. All of these lines have different slopes and intercept. I know the x range of the lines and y range of the lines. There is not much I can infer from data if I plot these lines simultaneously on a single plot. The resulting image will be something like this:
Not much can be inferred about this plot. However, if I can get this information saved in a big matrix, then I can analyse where maximum lines are passing through at a particular X index and make further analysis accordingly.
Further Details
I am discretinizing Y axis into 1000 equally spaced interval:
sample code as follows:
range=max(data)-min(data);
percent=0.20;
outerRange= max(data)+range*percent - (min(data)-range*percent);
outerRangeValues=min(data)-range*percent:outerRange/1000:max(data)+range*percent;
Even though it is entirely possible that a particularly steep line will pass through 2 or more rows in a single column, I'll only select only one of the rows to be filled by line in a single column. This can be done by taking average of rows values for a particular column and assigning single row to be its value for that column
You can use sub2ind to keep things vectorized and avoid loops.
The idea is to find all the row and column indices which will have to be modified.
For X axis it is easy, it is simply one per column so the X indices will be 1,2,3,...,np.
For the Y axis, you have to bin the line values into the Y grid. Since indices have to be integers, you have to convert your floating point values into integers. For that you can choose between round, floor and ceil. Each will place some values slightly differently, it is up to you to define which rounding method makes sense for your problem.
Once you have your indices [row_indices,column_indices], you convert them to linear indices into the matrix by using sub2ind, then you assign the values of line into these linear indices.
In code:
line=(4:0.4:8); % your input (line vector)
np = numel(line) ; % determine size of matrix/chart
% identify column and row indices to modify
idCol = 1:np ;
idRow = fliplr( round( line ) ) ; % choose "round", "floor" or "ceil"
% build the result
result = zeros(np);
linearInd = sub2ind( [np,np], idRow, idCol ) ;
result(linearInd) = line ;
Gives you:
>> result
result =
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 7.2 7.6 8
0 0 0 0 0 0 6.4 6.8 0 0 0
0 0 0 5.2 5.6 6 0 0 0 0 0
0 4.4 4.8 0 0 0 0 0 0 0 0
4 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0

Creating gray-level co-occurrence matrix from 16-bit image

I have a data set of images that are 16-bit and I want to create GLCM matrix from them to extract GLCM features.
However, the resulting matrix shows one value (as shown in the picture below), I wonder why.
I tried using the same image but converted to 8-bit, the resulted GLCM show several values.
Note: I used the following Matlab function:
glcm_matrix = graycomatrix(image.tif);
Here is a cropped sample from the 16-bit image:
Note: The image used in the computations can be downloaded from here. The original image is very low contrast and looks totally dark. The image shown above has its contrast stretched and is intended only for visualization purposes.
EDIT:
I used
glcm_matrix = graycomatrix(image.tif, 'GrayLimits', []);
and it gives me the following results:
It was a binning/scaling problem.
Let's take a peek inside:
edit graycomatrix
In this case we're interested in the two options, 'NumLevels' and 'GrayLimits'
% 'NumLevels' An integer specifying the number of gray levels to use
% when scaling the grayscale values in I. For example,
% if 'NumLevels' is 8, GRAYCOMATRIX scales the values in
% I so they are integers between 1 and 8. The number of
% gray levels determines the size of the gray-level
% co-occurrence matrix (GLCM).
%
% 'NumLevels' must be an integer. 'NumLevels' must be 2
% if I is logical.
%
% Default: 8 for numeric
% 2 for logical
%
% 'GrayLimits' A two-element vector, [LOW HIGH], that specifies how
% the values in I are scaled into gray levels. If N is
% the number of gray levels (see parameter 'NumLevels')
% to use for scaling, the range [LOW HIGH] is divided
% into N equal width bins and values in a bin get mapped
% to a single gray level. Grayscale values less than or
% equal to LOW are scaled to 1. Grayscale values greater
% than or equal to HIGH are scaled to NumLevels. If
% 'GrayLimits' is set to [], GRAYCOMATRIX uses the
% minimum and maximum grayscale values in I as limits,
% [min(I(:)) max(I(:))].
So in other words the function was binning your data into 8x8 bins and assuming that the scaling range was the full uint16 range (0-65535). However that sample image I you gave has a minimum of 305 and a maximum of 769, making it fall into the first bin (0-8192 or so). When I call A = graycomatrix(I) it gives me the following matrix :
A =
6600 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
However when A = graycomatrix(I,'GrayLimits', []) is called the scaling range is taken as min(I) - max(I), and the function works as expected :
A =
4 2 1 0 0 0 0 0
1 1 2 2 0 0 0 0
2 2 4 7 1 0 0 0
0 1 7 142 72 1 0 0
0 0 0 65 1711 252 0 0
0 0 0 0 230 3055 178 0
0 0 0 0 0 178 654 8
0 0 0 0 0 0 8 9
In your original example the single value is in the middle of the 8x8 matrix most likely because your original images are int16 and not uint16, so the graycomatrix is symmetric to take into account the possibility of negative values.
You can also of course scale the original images to fit their datatypes. For example percentile scaling might be a good idea if you expect outliers etc.
I'd just like to build on #Tapio's excellent answer.
The GLCM yielded by graycomatrix when you use the name/value pair GrayLimits', [] in the function call looks good. However, this approach might not be valid for your application. If you compute the GLCMs for a set of images in this way, the same elements of two different GLCMs corresponding to two different images are likely to have a different meaning. Indeed, as the intensity is being rescaled differently for each image, the components of the GLCM are actually encoding different co-occurrences from one image to another.
To avoid this you could first calculate the minimum and maximum intensities over the whole image dataset (for example minImgs and maxImgs) and then use those values to rescale the intensity of all the images that make up the dataset in the exact same way:
glcm_matrix = graycomatrix(image_tif, 'GrayLimits', [minImgs maxImgs]);

How does Y = eye(K)(y, :); replace a "for" loop? Coursera

Working on an assignment from Coursera Machine Learning. I'm curious how this works... From an example, this much simpler code:
% K is the number of classes.
K = num_labels;
Y = eye(K)(y, :);
seems to be a substitute for the following:
I = eye(num_labels);
Y = zeros(m, num_labels);
for i=1:m
Y(i, :)= I(y(i), :);
end
and I have no idea how. I'm having some difficulty Googling this info as well.
Thanks!
Your variable y in this case must be an m-element vector containing integers in the range of 1 to num_labels. The goal of the code is to create a matrix Y that is m-by-num_labels where each row k will contain all zeros except for a 1 in column y(k).
A way to generate Y is to first create an identity matrix using the function eye. This is a square matrix of all zeroes except for ones along the main diagonal. Row k of the identity matrix will therefore have one non-zero element in column k. We can therefore build matrix Y out of rows indexed from the identity matrix, using y as the row index. We could do this with a for loop (as in your second code sample), but that's not as simple and efficient as using a single indexing operation (as in your first code sample).
Let's look at an example (in MATLAB):
>> num_labels = 5;
>> y = [2 3 3 1 5 4 4 4]; % The columns where the ones will be for each row
>> I = eye(num_labels)
I =
1 0 0 0 0
0 1 0 0 0
0 0 1 0 0
0 0 0 1 0
0 0 0 0 1
>> Y = I(y, :)
Y =
% 1 in column ...
0 1 0 0 0 % 2
0 0 1 0 0 % 3
0 0 1 0 0 % 3
1 0 0 0 0 % 1
0 0 0 0 1 % 5
0 0 0 1 0 % 4
0 0 0 1 0 % 4
0 0 0 1 0 % 4
NOTE: Octave allows you to index function return arguments without first placing them in a variable, but MATLAB does not (at least, not very easily). Therefore, the syntax:
Y = eye(num_labels)(y, :);
only works in Octave. In MATLAB, you have to do it as in my example above, or use one of the other options here.
The first set of code is Octave, which has some additional indexing functionality that MATLAB does not have. The second set of code is how the operation would be performed in MATLAB.
In both cases Y is a matrix generated by re-arranging the rows of an identity matrix. In both cases it may also be posible to calculate Y = T*y for a suitable linear transformation matrix T.
(The above assumes that y is a vector of integers that are being used as an indexing variables for the rows. If that's not the case then the code most likely throws an error.)

How to replace non-zero elements randomly with zero?

I have a matrix including 1 and 0 elements like below which is used as a network adjacency matrix.
A =
0 1 1 1
1 1 0 1
1 1 0 1
1 1 1 0
I want to simulate an attack on the network, so I must replace some specific percent of 1 elements randomly with 0. How can I do this in MATLAB?
I know how to replace a percentage of elements randomly with zeros, but I must be sure that the element that is replaced randomly, is one of the 1 elements of matrix not zeros.
If you want to change each 1 with a certain probability:
p = 0.1%; % desired probability of change
A_ones = find(A); % linear index of ones in A
A_ones_change = A_ones(rand(size(A_ones))<=p); % entries to be changed
A(A_ones_change) = 0; % apply changes in those entries
If you want to randomly change a fixed fraction of the 1 entries:
f = 0.1; % desired fraction
A_ones = find(A);
n = round(f*length(A_ones));
A_ones_change = randsample(A_ones,n);
A(A_ones_change) = 0;
Note that in this case the resulting fraction may be different to that intended, because of the need to round to an integer number of entries.
#horchler's point is a good one. However, if we keep it simple, then you can just multiple your input matrix to a mask matrix.
>> a1=randint(5,5,[0 1]) #before replacing 1->0
a1 =
1 1 1 0 1
0 1 1 1 0
0 1 0 0 1
0 0 1 0 1
1 0 1 0 1
>> a2=random('unif',0,1,5,5) #Assuming frequency distribution is uniform ('unif')
a2 =
0.7889 0.3200 0.2679 0.8392 0.6299
0.4387 0.9601 0.4399 0.6288 0.3705
0.4983 0.7266 0.9334 0.1338 0.5751
0.2140 0.4120 0.6833 0.2071 0.4514
0.6435 0.7446 0.2126 0.6072 0.0439
>> a1.*(a2>0.1) #And the replacement prob. is 0.1
ans =
1 1 1 0 1
0 1 1 1 0
0 1 0 0 1
0 0 1 0 1
1 0 1 0 0
And other trick can be added to the mask matrix (a2). Such as a different freq. distribution, or a structure (e.g. once a cell is replaced, the adjacent cells become less likely to be replaced and so on.)
Cheers.
The function find is your friend:
indices = find(A);
This will return an array of the indices of 1 elements in your matrix A and you can use your method of replacing a percent of elements with zero on a subset of this array. Then,
A(subsetIndices) = 0;
will replace the remaining indices of A with zero.

Trim Binary Matrix in MatLab

I have a binary matrix like this:
0 0 0 0 0 0
0 0 0 1 0 0
0 1 0 0 0 0
0 0 1 0 1 0
0 0 0 1 0 0
0 0 0 0 0 0
and I want to trim this matrix (in other words, remove zeroes at the boundaries) to be like:
0 0 1 0
1 0 0 0
0 1 0 1
0 0 1 0
How to do this the "Matlab" way? that's not to use conventional loops and conditions.
To be clearer, the matrix should be reduced to start from the first column which has at least one 1, and ends at the last column with the same condition, inclusive. Any column out of this range should be removed. Same rules apply for rows.
Thanks.
If you have the data in matrix M...
x = find(any(M,2),1,'first'):find(any(M,2),1,'last');
y = find(any(M),1,'first'):find(any(M),1,'last');
M(x, y)
Or, if you know that there will be a 1 in every row/col except the edges:
M(any(M,2), any(M))
Extension to higher dimensions:
Assuming a 3D matrix to be trimmed, this is more straightforward:
M=rand(3,3,3); % generating a random 3D matrix
M(2,:,:)=0; % just to make a check if it works in extreme case of having zeros in the middle
padded = padarray(M,[2 2 2]); % making some zero boundaries
[r,c,v]=ind2sub(size(padded),find(padded));
recoveredM=padded(min(r):max(r),min(c):max(c),min(v):max(v));
check=M==recoveredM % checking to see if M is successfully recovered
You could use the fact that find can return row and column indices:
[r1, c1] = find(x, 1, 'first')
[r2, c2] = find(x, 1, 'last')
x(r1:r2, c1:c2)