MATLAB - Find contour of a binary bit map? - matlab

I have a 10x10 binary bit map as follows. I am looking for an efficient way of finding its contour in MATLAB. (I have tried letting every value "look around" its neighbors' values and decide, but it is too inefficient. I expect the algorithm to scale up.)
false false false false false false false false false false
false false true true true true true true false false
false true true true true true true true true false
false true true true true true true true true false
false true true true true true true true true false
false true true true true true true true true false
false true true true true true true true true false
false true true true true true true true true false
false false true true true true true true false false
false false false false false false false false false false
Let's assume each boolean value resembles a square, and the left-bottom one sits over x: 0-1; y: 0-1. The output should be the points that form the boundary. You may assume the inner true block is alway convex.

This is dead simple. Use the bwperim command in MATLAB, assuming you have the image processing toolbox.
You can call the function like so:
out = bwperim(A); %//or
out = bwperim(A,conn);
The first way assumes that the pixel connectivity is a 4-pixel neighbourhood. This will only look at the north, south, east and west directions.
If you specify an additional parameter called conn which is a single number, you can override this behaviour and specify the kind of behaviour you want when looking at neighbouring pixels. For example, if conn=8, you would look at 8-pixel neighbourhoods for 2D (so N, NE, E, SE, S, SW, W, NW), or you can go into 3D if you have a 3D binary image... but for now, I'm assuming it's just 2D. For the best accuracy, use 8.
As such, we have:
A = [false false false false false false false false false false
false false true true true true true true false false
false true true true true true true true true false
false true true true true true true true true false
false true true true true true true true true false
false true true true true true true true true false
false true true true true true true true true false
false true true true true true true true true false
false false true true true true true true false false
false false false false false false false false false false];
out = bwperim(A,8);
And it looks like:
out =
0 0 0 0 0 0 0 0 0 0
0 0 1 1 1 1 1 1 0 0
0 1 1 0 0 0 0 1 1 0
0 1 0 0 0 0 0 0 1 0
0 1 0 0 0 0 0 0 1 0
0 1 0 0 0 0 0 0 1 0
0 1 0 0 0 0 0 0 1 0
0 1 1 0 0 0 0 1 1 0
0 0 1 1 1 1 1 1 0 0
0 0 0 0 0 0 0 0 0 0
MATLAB outputs 1 for true and 0 for false.
As a bonus, this is what the shapes look like side by side:
Edit from the comments
Going with your comments, you wish to find the set of points that make the perimeter. As such, you can simply use the find command to do that for you.
[X,Y] = find(out == 1);
coords = [X Y];
What the find command does is that it searches your array and finds locations in the array that match the Boolean expression given in the parameter of find. In this case, we wish to find all co-ordinates that have a pixel in out equal to 1, and out is our perimeter image. As such, this effectively finds all pixels that are perimeter pixels.
We thus get:
coords =
3 2
4 2
5 2
6 2
7 2
8 2
2 3
3 3
8 3
9 3
2 4
9 4
2 5
9 5
2 6
9 6
2 7
9 7
2 8
3 8
8 8
9 8
3 9
4 9
5 9
6 9
7 9
8 9
X are the row co-ordinates, while Y are the column co-ordinates. I've placed X and Y into a single 2D array for better presentation, but you can take the X and Y variables by themselves for further processing.
Hope this helps!

Here's another option:
B = bwboundaries(A)
this will get the x-y coordinates of any boundary. see more info here...

Another option with image processing toolbox:
B = A - imerode(A,SE);
where SE is one of the kernels:
0 1 0 1 1 1
1 1 1 1 1 1
0 1 0 1 1 1
depending on which connectivity you want to use: first one for 8-connectivity, second one for 4-connectivity. For the difference between the two, recall that 8-connectivity allows diagonal neighbours.
With the image B, you can find all the points of the perimeters with the same technique displayed in another answer:
[Xp,Yp] = find(B);

Related

Cumsum in Postgres with nulling every partiton

I have this
device_id new_trip corridor_trip_id
a_10003750032338423796 true 1
a_10003750032338423796 false 1
a_10003750032338423796 false 1
a_1001349994099853586 true 3
a_1001349994099853586 false 3
a_1001349994099853586 false 3
a_1001349994099853586 false 3
a_1001349994099853586 true 3
a_1001349994099853586 false 3
a_1001349994099853586 true 3
a_1001349994099853586 false 3
I want to find the cumulative amount of corridor_trip_id as here
device_id new_trip corridor_trip_id
a_10003750032338423796 True 1
a_10003750032338423796 False 1
a_10003750032338423796 False 1
a_1001349994099853586 True 1
a_1001349994099853586 False 1
a_1001349994099853586 False 1
a_1001349994099853586 False 1
a_1001349994099853586 True 2
a_1001349994099853586 False 2
a_1001349994099853586 True 3
a_1001349994099853586 False 3

Julia dot operator and boolean

Suppose I have p = [true, true, false, false] and q = [true, false, true, false]. How can I logically "and" them, say like
p .&& q?
Use .& instead:
julia> p=[true, true, false, false]
4-element Array{Bool,1}:
1
1
0
0
julia> q=[true, false, true, false]
4-element Array{Bool,1}:
1
0
1
0
julia> p .& q
4-element BitArray{1}:
1
0
0
0
You have to be careful though as & works also on non-Bool elements:
julia> [11,12,13] .& [3,2,1]
3-element Array{Int64,1}:
3
0
1

How to create an error function comparing two matrices?

I have two matrices in MATLAB. Each one is filled with 1 and 0 at different positions. I want to compare each element:
If there is a 1 match, I want it to record as True Positive.
If there is a 0 match, I want it to record as True Negative.
If one says 1 and the other says 0, I want to record as False Positive.
If one says 0 and the other says 1, I want to record as False Negative.
I tried just comparing the two matrices:
idx = A == B
But, that gives me a simple match, not telling me when there is a True Positive or Negative, etc.
Is there any specific function I could use, or any alternative?
You could just add the matrices in a prescribed way....
a = [1 0 1 0
1 1 0 0
0 0 1 1];
b = [1 0 0 0
0 0 0 1
0 0 1 0];
C = a + 2*b;
% For pairs [a,b] we expect
% [0,0]: C = 0, true negative
% [1,0]: C = 1, false positive
% [0,1]: C = 2, false negative
% [1,1]: C = 3, true positive
% C =
% [ 3 0 1 0
% 1 1 0 2
% 0 0 3 1 ]
If you have the Statistics and Machine Learning toolbox and you only want a summary, you might just need the function confusionmat.
From the docs:
C = confusionmat(group,grouphat) returns the confusion matrix C determined by the known and predicted groups in group and grouphat. [...]. C is a square matrix with size equal to the total number of distinct elements in group and grouphat. C(i,j) is a count of observations known to be in group i but predicted to be in group j.
For example:
a = [1 0 1 0
1 1 0 0
0 0 1 1];
b = [1 0 0 0
0 0 0 1
0 0 1 0];
C = confusionmat( a(:), b(:) );
% C =
% [ 5 1
% 4 2]
% So for each pair [a,b], we have 5*[0,0], 2*[1,1], 4*[1,0], 1*[0,1]
A similar function for those with the Neural Network Toolbox instead would be confusion.
You could just use bitwise operators to produce the four different values:
bitor(bitshift(uint8(b),1),uint8(a))
Produces an array with
0 : True Negative
1 : False Negative (a is true but b is false)
2 : False Positive (a is false but b is true)
3 : True Positive
One naive approach would be four comparisons, case by case:
% Set up some artificial data
ground_truth = randi(2, 5) - 1
compare = randi(2, 5) - 1
% Determine true positives, false positives, etc.
tp = ground_truth & compare
fp = ~ground_truth & compare
tn = ~ground_truth & ~compare
fn = ground_truth & ~compare
Output:
ground_truth =
1 0 1 0 0
0 1 1 0 1
1 1 0 1 0
0 1 0 1 1
0 0 0 1 0
compare =
0 1 1 0 1
0 1 1 1 0
1 1 0 0 1
1 1 1 0 0
1 1 1 1 1
tp =
0 0 1 0 0
0 1 1 0 0
1 1 0 0 0
0 1 0 0 0
0 0 0 1 0
fp =
0 1 0 0 1
0 0 0 1 0
0 0 0 0 1
1 0 1 0 0
1 1 1 0 1
tn =
0 0 0 1 0
1 0 0 0 0
0 0 1 0 0
0 0 0 0 0
0 0 0 0 0
fn =
1 0 0 0 0
0 0 0 0 1
0 0 0 1 0
0 0 0 1 1
0 0 0 0 0
That works, because 0 and 1 (or any positive value) are alternative representations for true and false.
To keep your main code clean, set up a separate function, say my_stats.m
function [tp, fp, tn, fn] = my_stats(ground_truth, compare)
% Determine true positives, false positives, etc.
tp = ground_truth & compare;
fp = ~ground_truth & compare;
tn = ~ground_truth & ~compare;
fn = ground_truth & ~compare;
end
and call it in your main code:
% Set up some artificial data
ground_truth = randi(2, 5) - 1
compare = randi(2, 5) - 1
[tp, fp, tn, fn] = my_stats(ground_truth, compare)
Hope that helps!
I found that I can use the find method and set two conditions, then just find the numbers of the element in each variable
TruePositive = length(find(A==B & A==1))
TrueNegative = length(find(A==B & A==0))
FalsePositive = length(find(A~=B & A==1))
FalseNegative = length(find(A~=B & A==0))
The confusionmatrix() method suggested by #Wolfie is also really neat, especially if you use the confusionchart() which provides a nice visualisation.

SUM the same field on different CASE conditions

I have a table with the following fields.
code quantity active
1 23 true
1 35 true
1 25 false
1 37 false
2 30 true
2 43 true
2 43 false
2 45 true
3 49 false
3 26 false
3 30 true
3 46 false
3 46 false
3 27 true
4 42 false
4 23 false
4 48 true
4 35 false
4 45 true
and I have the following query:
SELECT code, CASE WHEN active THEN COUNT(*) ELSE 0 END AS "activeCodes",
CASE WHEN active=false THEN COUNT(*) ELSE 0 END AS "inactiveCodes"
FROM tbl_codes
GROUP BY code, active
ORDER BY code
and the result of it is the following table:
code activeCodes inactiveCodes
1 0 2
1 2 0
2 3 0
2 0 1
3 0 4
3 2 0
4 0 3
4 2 0
but the result that I expected is
code activeCodes inactiveCodes
1 2 2
2 3 1
3 4 2
4 2 3
which condition am I missing here? how can I get the result that I expected?
You have a sparse matrix which you can collapse by running a SUM. Probably something like this (not tested):
SELECT code
,SUM(CASE WHEN active THEN 1 ELSE 0 END) AS "activeCodes"
,SUM(CASE WHEN active=false THEN 1 ELSE 0 END) AS "inactiveCodes"
FROM tbl_codes
GROUP BY code
ORDER BY code

How to select element using boolean matrix in Matlab [duplicate]

This question already has an answer here:
Linear indexing, logical indexing, and all that
(1 answer)
Closed 6 years ago.
I'm trying to select some elements by using boolean operations in MATLAB.
I have A = [1 2 3; 4 5 6; 7 8 9]
A =
1 2 3
4 5 6
7 8 9
When using A([true true false; true true false]) I get:
1
4
7
2
Isn't it supposed to be?:
1
4
2
5
Does anyone know what's going on?
See this example for the documentation on logical indexing. It may not be explained as clearly as it should, but if you specify a logical index with fewer elements that then the indexed matrix (A) then the indexing matrix is linearized such that:
A = [1 2 3; 4 5 6; 7 8 9];
idx1 = [true true false; true true false];
A(idx1)
is equivalent to:
idx1 = [true true false; true true false];
A(idx1(:))
In other words, the index matrix (idx1) elements specify the output in column-wise order.
If you want what you though you should get, you can use:
idx2 = [true false true; true true false];
A(idx2)
or you can transform your original index array:
idx1 = [true true false; true true false];
idx2 = reshape(idx1.',2,3);
A(idx2)
or just use:
idx3 = [true true false true true].';
A(idx3)