Matlab, why is strel non-semetrical with respect to the angle? - matlab

I was attempting to apply a closing operation to an image using a line structuring element at 8 different directions. Initially I wanted to apply it to angles in the range [0 .. 360] but I later realised that my stucturing element is symmetrical so I thought of using the range [0 .. 180] instead. However, I later realized that Matlab's structuring element function (strel) does not produce symmetrical results for angles that are 180 degrees apart. Consider:
>> strel('line', 11, 120)
ans =
Flat STREL object containing 9 neighbors.
Neighborhood:
1 0 0 0 0
1 0 0 0 0
0 1 0 0 0
0 1 0 0 0
0 0 1 0 0
0 0 0 1 0
0 0 0 1 0
0 0 0 0 1
0 0 0 0 1
>>
And:
>> strel('line', 11, 300)
ans =
Flat STREL object containing 9 neighbors.
Neighborhood:
1 0 0 0 0 0 0
0 1 0 0 0 0 0
0 1 0 0 0 0 0
0 0 1 0 0 0 0
0 0 0 1 0 0 0
0 0 0 0 1 0 0
0 0 0 0 0 1 0
0 0 0 0 0 1 0
0 0 0 0 0 0 1
I expect that the 2 structuring elements above should be symmetrical, since 300 = 120 + 180. Why is this not the case for Matlab's strel function?

Potatoes:
This is an interesting observation. If you happen to look into the implementation of the strel object in MATLAB, the lines to observe are these (in the MakeLineStrel sub-function):
theta = theta_d * pi / 180;
x = round((len-1)/2 * cos(theta));
y = -round((len-1)/2 * sin(theta));
The theta_d value is the angle in degrees you've specified, and len is the length of the line you've requested for.
The values for x and y define the integer valued "end point" of the line, and the structuring element is constructed such that it is symmetric with respect to the origin.
Due to the rounding operation here, when you specify 120 for theta_d, the pair (x,y) will be (-2, -4), but when you specify 300, (x,y) will be (3, 4). This is the root cause for the discretized line being different due to the way the angles have been represented.
Given this understanding, it would be safest to actually print the neighborhood of the structuring element and ensure that it looks right, before using it in your operation.
Hope this helps.

Related

How to perform inverse in GF(2) and multiply in GF(256) in Matlab?

I have a binary matrix A (only 1 and 0), and a vector D in Galois field (256). The vector C is calculated as:
C = (A^^-1)*D
where A^^-1 denotes the inverse matrix of matrix A in GF(2), * is multiply operation. The result vector C must be in GF(256). I tried to do it in Matlab.
A= [ 1 0 0 1 1 0 0 0 0 0 0 0 0 0;
1 1 0 0 0 1 0 0 0 0 0 0 0 0;
1 1 1 0 0 0 1 0 0 0 0 0 0 0;
0 1 1 1 0 0 0 1 0 0 0 0 0 0;
0 0 1 1 0 0 0 0 1 0 0 0 0 0;
1 1 0 1 1 0 0 1 0 1 0 0 0 0;
1 0 1 1 0 1 0 0 1 0 1 0 0 0;
1 1 1 0 0 0 1 1 1 0 0 1 0 0;
0 1 1 1 1 1 1 0 0 0 0 0 1 0;
0 0 0 0 1 1 1 1 1 0 0 0 0 1;
0 1 1 1 1 0 1 1 1 0 1 1 1 0;
0 0 0 1 0 0 0 1 0 0 0 0 0 0;
0 0 1 0 0 0 0 1 0 0 0 0 0 0;
1 1 1 1 0 0 0 0 0 0 0 0 0 0]
D=[0;0;0;0;0;0;0;0;0;0;103;198;105;115]
A=gf(A,1);
D=gf(D,8); %%2^8=256
C=inv(A)*D
%% The corrected result must be
%%C=[103;187;125;210;181;220;161;20;175;175;187;187;220;115]
However, for above code, I cannot achieved as my expected result
C=[103;187;125;210;181;220;161;20;175;175;187;187;220;115]
It produces an error as
Error using * (line 14)
Orders must match.
Could you help me achieve my expected result?
The error
Error using * (line 14)
Orders must match.
arises because Matlab does not support applying standard mathematical operations (+, *, .*, .^, \, etc.) to Galois Fields of different order, GF(2) and GF(256). The inverse operation, inv(A) is a valid operation and if you perform it on its own as shown by #Ander Biguri it will succeed and generate the inverse of A in GF(2). Your calculation breaks down when you attempt to multiply inv(A) and D as the orders of these Galois Fields do not match.
In this example it is unnecessary to explicitly define A as a Galois Field of order 2, GF(2) as multiplication in GF(256) takes place in GF(2). That is 1 + 1 = 0.
If we thus modify the code that creates the Galois Field for A to
A = gf(A, 8);
we can then perform
C = inv(A)*D
which produces
C = GF(2^8) array. Primitive polynomial = D^8+D^4+D^3+D^2+1 (285 decimal)
Array elements =
103
187
125
210
181
220
161
20
175
175
187
187
220
115
C is thus in GF(256) and produces the expected result.
It seems there is either
A) a theoretical fault in your computation or
B) something that MATLAB doesn't support.
As per the documentation in Galois field arithmetic (emphasis mine):
Section Overview. You can perform arithmetic operations on Galois
arrays by using familiar MATLAB operators, listed in the table below.
Whenever you operate on a pair of Galois arrays, both arrays must be
in the same Galois field.
And your matrices aren't.
I cant know which one is it as I have no idea how Galois fields work

Measure how spread out the data in an array is

I have an array of zeros and ones and I need to know if the data is spread out across the columns or concentrated in clumps.
For example:
If I have array x and it has these values:
Column 1 values: 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1
Column 2 values: 1 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 1 0 0 1 0 0 0 1
if we counted the number of ones we can know that it is the same number but the ones are more well spread out and distributed in column 2 compared with column 1.
I am trying to make a score that gives me a high value if the spreading is good and low value if the spreading is bad... any ideas??
Sample of Data:
1 0 0 0 5 0 -2 -3 0 0 1
1 0 0 0 0 0 0 0 0 0 1
2 0 0 0 0 0 0 3 -3 1 0
1 2 3 0 5 0 2 13 4 5 1
1 0 0 0 0 0 -4 34 0 0 1
I think what you're trying to measure is the variance of the distribution of the number of 0s between the 1s, i.e:
f = #(x)std(diff(find(x)))
So for you data:
a = [1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1]
b = [1 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 1 0 0 1 0 0 0 1]
f(a)
= 8.0498
f(b)
= 2.0736
But I still think you're essentially trying to measure the disorder of the system which is what I imagine entropy measures but I don't know how
Note that this gives a low value if the "spreading" is good and a high value if it is bad (i.e. the opposite of your request).
Also if you want it per column then it becomes a little more complicated:
f = #(x)arrayfun(#(y)std(diff(find(x(:,y)))), 1:size(x,2))
data = [a', b'];
f(data)
WARNING: This method pretty much does not consider trailing and leading 0s. I don't know if that's a problem or not. but basically f([0; 0; 0; 1; 1; 1; 0; 0; 0]) returns 0 where as f([1; 0; 0; 1; 0; 1; 0; 0; 0]) returns a positive indicating (incorrectly) that first case is more distributed. One possible fix might be to prepend and append a row of ones to the matrix...
I think you would need an interval to find the "spreadness" locally, otherwise the sample 1 (which is named as Column 1 in the question) would appear as spread too between the 2nd and 3rd ones.
So, following that theory and assuming input_array to be the input array, you can try this approach -
intv = 10; %// Interval
diff_loc = diff(find(input_array))
spread_factor = sum(diff_loc(diff_loc<=intv)) %// desired output/score
For sample 1, spread_factor gives 4 and for sample 2 it is 23.
Another theory that you can employ would be if you assume an interval such that distance between consecutive ones must be greater than or equal to that interval. This theory would lead us to a code like this -
intv = 3; %// Interval
diff_loc = diff(find(input_array))
spread_factor = sum(diff_loc>=intv)
With this new approach - For sample 1, spread_factor is 1 and for sample 2 it is 5.

Find Maximum Coordinate Point where Pixel value is 1 on x-axis, in 2-D image (Binary) (Matlab)

I'm using Matlab and I have a 2-D Array (image), like this
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 1 1 1 1 1 0 0
0 0 1 1 1 1 1 1 1 1 0
0 0 0 1 1 1 1 1 1 1 1
0 0 0 0 1 1 1 1 1 1 0
0 0 0 0 0 0 0 1 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
in the following array, I want to get very first position (with respect to X-axis) where X and Y have value "1" and very last position (with respect to X-axis) where X and Y have value "1".
Does anyone know the simplest way to do this?
A simple way using any and find. I assume that your image is called image
minimumX = find(any(image,1),1,'first')
maximumX = find(any(image,1),1,'last')
An alternative way is to use BoundingBox from regionprops:
stats = regionprops(image,'BoundingBox');
minimumX = stats.BoundingBox(1);
maximumX = sum(stats.BoundingBox([1 3]));
You can achieve it with max, exploting the facts that
[Y,I] = MAX(X,[],DIM) operates along the dimension DIM.
and that
If the values [...] contain more than one maximal element, the index of the first one is returned.
Let img denote your 2D array. I'm assuming your x-axis is the row index. Otherwise change ,2 to ,1 (three times) and fliplr to flipud (once).
[valid, first] = max(img,[],2);
first(~valid) = NaN;
[~, last] = max(fliplr(img),[],2);
last = size(img,2)-last+1;
last(~valid) = NaN;
In your example:
first =
NaN
NaN
5
3
4
5
8
NaN
NaN
last =
NaN
NaN
9
10
11
10
8
NaN
NaN

Check the surrounding neighbors of a matrix element in Matlab

I have a 480-by-640 matrix A. For each pixel, I want to check its neighbors. The neighbors of the pixel are determined by a value N. For example, this is a part of matrix A where all the zeros are the neighbours of pixel X when N=3:
0 0 0 0 0 0 0
0 0 0 0 0 0 0
0 0 0 0 0 0 0
0 0 0 X 0 0 0
0 0 0 0 0 0 0
0 0 0 0 0 0 0
0 0 0 0 0 0 0
As shown, because N=3, all these zeros are pixel X's neighbors. The problem is if X is located before the index N=3. Here the neighbors will be pixels with one values:
X 1 1 1 0 0 0
1 1 1 1 0 0 0
1 1 1 1 0 0 0
1 1 1 1 0 0 0
0 0 0 0 0 0 0
0 0 0 0 0 0 0
0 0 0 0 0 0 0
Could anyone advise on how to handle this?
The simplest way to proceed is just to pad your array with with values that do not return true for whatever you are checking (say, if you're looking for nonzeros, pad with zeros, or if you're looking for finite values, pad with NaN.) The padarray function can do this for you, but requires the Image Processing Toolbox*. Otherwise, you can pad arrays yourself. For example, an unoptimized way to proceed might be
A = rand(m,n);
Apadded = [zeros(N,2*N+n); [zeros(m,N), A, zeros(m,N)]; zeros(N,2*N+n)];
for i = N+1:N+m+1
for j = N+1:N+n+1
% Process neighborhood of A(i,j)
end
end
*Also note that these sorts of "sliding neighborhood" operations, being common in image processing, are implemented for you in the Image Processing Toolbox.

Get the indexes of the boundary cells of a subset of a matrix. Matlab

Given a matrix where 1 is the current subset
test =
0 0 0 0 0 0
0 0 0 0 0 0
0 0 1 1 0 0
0 0 1 1 0 0
0 0 0 0 0 0
0 0 0 0 0 0
Is there a function, or quick method to get change the subset to the boundary of the current subset?
Eg. Get this subset from 'test' above
test =
0 0 0 0 0 0
0 1 1 1 1 0
0 1 0 0 1 0
0 1 0 0 1 0
0 1 1 1 1 0
0 0 0 0 0 0
In the end I just want to get the minimum of the cells surrounding a subset of a matrix. Sure I could loop through and get the minimum of the boundary (cell by cell), but there must be a way to do it with the method i've shown above.
Note the subset WILL be connected, but may not be rectangular. This may be the big catch.
This is a possible subset.... (Would pad this with a NaN border)
test =
0 0 0 0 0 0
0 0 0 0 0 0
0 0 1 1 0 0
0 0 1 1 0 0
0 0 1 1 1 1
0 0 1 1 1 1
Ideas?
The basic steps I'd use are:
Perform a dilation on the shape to get a new area which is the shape plus its boundary
Subtract the original shape from the dilated shape to leave just the boundary
Use the boundary to index your data matrix, then take the minimum.
Dilation
What I want to do here is pass a 3x3 window over each cell and take the maximum value in that window:
[m, n] = size(A); % assuming A is your original shape matrix
APadded = zeros(m + 2, n + 2);
APadded(2:end-1, 2:end-1) = A; % pad A with zeroes on each side
ADilated = zeros(m + 2, n + 2); % this will hold the dilated shape.
for i = 1:m
for j = 1:n
mask = zeros(size(APadded));
mask(i:i+2, j:j+2) = 1; % this places a 3x3 square of 1's around (i, j)
ADilated(i + 1, j + 1) = max(APadded(mask));
end
end
Shape subtraction
This is basically a logical AND and a logical NOT to remove the intersection:
ABoundary = ADilated & (~APadded);
At this stage you may want to remove the border we added to do the dilation, since we don't need it any more.
ABoundary = ABoundary(2:end-1, 2:end-1);
Find the minimum data point along the boundary
We can use our logical boundary to index the original data into a vector, then just take the minimum of that vector.
dataMinimum = min(data(ABoundary));
You should look at this as morphology problem, not set theory. This can be solved pretty easily with imdilate() (requires the image package). You basically only need to subtract the image to its dilation with a 3x3 matrix of 1.
octave> test = logical ([0 0 0 0 0 0
0 0 0 0 0 0
0 0 1 1 0 0
0 0 1 1 0 0
0 0 1 1 1 1
0 0 1 1 1 1]);
octave> imdilate (test, true (3)) - test
ans =
0 0 0 0 0 0
0 1 1 1 1 0
0 1 0 0 1 0
0 1 0 0 1 1
0 1 0 0 0 0
0 1 0 0 0 0
It does not, however, pads with NaN. If you really want that, you could pad your original matrix with false, do the operation, and then check if there's any true values in the border.
Note that you don't have to use logical() in which case you'll have to use ones() instead of true(). But that takes more memory and has worse performance.
EDIT: since you are trying to do it without using any matlab toolbox, take a look at the source of imdilate() in Octave. For the case of logical matrices (which is your case) it's a simple usage of filter2() which belongs to matlab core. That said, the following one line should work fine and be much faster
octave> (filter2 (true (3), test) > 0) - test
ans =
0 0 0 0 0 0
0 1 1 1 1 0
0 1 0 0 1 0
0 1 0 0 1 1
0 1 0 0 0 0
0 1 0 0 0 0
One possible solution is to take the subset and add it to the original matrix, but ensure that each time you add it, you offset its position by +1 row, -1 row and +1 column, -1 column. The result will then be expanded by one row and column all around the original subset. You then use the original matrix to mask the original subet to zero.
Like this:
test_new = test + ...
[[test(2:end,2:end);zeros(1,size(test,1)-1)],zeros(size(test,1),1)] + ... %move subset up-left
[[zeros(1,size(test,1)-1);test(1:end-1,2:end)],zeros(size(test,1),1)] + ... %move down-left
[zeros(size(test,1),1),[test(2:end,1:end-1);zeros(1,size(test,1)-1)]] + ... %move subset up-right
[zeros(size(test,1),1),[zeros(1,size(test,1)-1);test(1:end-1,1:end-1)]]; %move subset down-right
test_masked = test_new.*~test; %mask with original matrix
result = test_masked;
result(result>1)=1; % ensure that there is only 1's, not 2, 3, etc.
The result for this on your test matrix is:
result =
0 0 0 0 0 0
0 1 1 1 1 0
0 1 0 0 1 0
0 1 0 0 1 1
0 1 0 0 0 0
0 1 0 0 0 0
Edited - it now grabs the corners as well, by moving the subset up and to the left, up and to the right, down then left and down then right.
I expect this would be a very quick way to achieve this - it doesn't have any loops, nor functions - just matrix operations.