I want to visualize all of the paths on 2D square in matlab.
This code gives me the following figure which consists of a 2D square and randomly distributed of 1 and 0.
https://i.hizliresim.com/Ey4G4D.png
Each 1's must connected with lines from top to bottom.
If there is 1, then there is a way and I have to plot line. Otherwise therre is no way and stop.
Without the boundary elements, there are 3 way for each elements. Each element can go left side, right side or down side.
The top left hand corner's element can go right and down direction.
The top right hand corner's element can go left and down direction.
This is the algorithm of the modelling.
https://i.hizliresim.com/Dy0z0y.jpg
How can I write this code ?
I am waiting your advise :)
Problem analysis
To get an information on the possible paths in your matrix/image you can use the diff function. It calculates the difference between two neighbouring matrix elements along the specified dimension.
The conditions for the existence of a path are:
The difference between the element and its neighbour must be 0
The element itself must be 1
Solution
The following matlab program will create 3 matrices containing the value 1 or true for each element with a path existing to its neighbour.
matrix = logical([1 1 1 1 0; ...
1 1 0 1 1; ...
0 0 0 1 0; ...
0 0 1 1 0])
hasPathtoRight = false(size(matrix));
hasPathtoRight(:,1:end-1) = (diff(matrix,1,2)==0) & (matrix(:,1:end-1)==1)
hasPathtoLeft = false(size(matrix));
hasPathtoLeft(:,2:end) = (diff(matrix,1,2)==0) & (matrix(:,2:end)==1)
hasPathDown = false(size(matrix));
hasPathDown(1:end-1,:) = (diff(matrix,1,1)==0) & (matrix(1:end-1,:)==1)
Result
The result for the example matrix is shown here:
matrix =
1 1 1 1 0
1 1 0 1 1
0 0 0 1 0
0 0 1 1 0
hasPathtoRight =
1 1 1 0 0
1 0 0 1 0
0 0 0 0 0
0 0 1 0 0
hasPathtoLeft =
0 1 1 1 0
0 1 0 0 1
0 0 0 0 0
0 0 0 1 0
hasPathDown =
1 1 0 1 0
0 0 0 1 0
0 0 0 1 0
0 0 0 0 0
You can use these matrices to draw the paths in a graphical display.
Related
I have a logical vector in which I would like to iterate over every n-elements. If in any given window at least 50% are 1's, then I change every element to 1, else I keep as is and move to the next window. For example.
n = 4;
input = [0 0 0 1 0 1 1 0 0 0 0 1 0 1 0 1 0 0 0 1];
output = func(input,4);
output = [0 0 0 1 1 1 1 1 0 0 0 1 1 1 1 1 0 0 0 1];
This function is trivial to implement but is it possible to apply a vectorized implementation using logical indexing?. I am trying to build up the intuition of applying this technique.
here's a one liner (that works for your input):
func = #(input,n) input | kron(sum(reshape(input ,n,[]))>=n/2,ones(1,n));
of course, there are cases to solve that this doesnt answer, what if the size of the input is not commensurate in n? etc...
i'm not sure if that's what you meant by vectorization, and I didnt benchmark it vs a for loop...
Here is one way of doing it. Once understood you can compact it in less lines but I'll details the intermediate steps for the sake of clarity.
%% The inputs
n = 4;
input = [0 0 0 1 0 1 1 0 0 0 0 1 0 1 0 1 0 0 0 1];
1) Split your input into blocks of size n (note that your final function will have to check that the number of elements in input is a integer multiple of n)
c = reshape(input,n,[]) ;
Gives you a matrix with your blocks organized in columns:
c =
0 0 0 0 0
0 1 0 1 0
0 1 0 0 0
1 0 1 1 1
2) Perform your test condition on each of the block. For this we'll take advantage that Matlab is working column wise for the sum function:
>> cr = sum(c) >= (n/2)
cr =
0 1 0 1 0
Now you have a logical vector cr containing as many elements as initial blocks. Each value is the result of the test condition over the block. The 0 blocks will be left unchanged, the 1 blocks will be forced to value 1.
3) Force 1 columns/block to value 1:
>> c(:,cr) = 1
c =
0 1 0 1 0
0 1 0 1 0
0 1 0 1 0
1 1 1 1 1
4) Now all is left is to unfold your matrix. You can do it several ways:
res = c(:) ; %% will give you a column vector
OR
>> res = reshape(c,1,[]) %% will give you a line vector
res =
0 0 0 1 1 1 1 1 0 0 0 1 1 1 1 1 0 0 0 1
I have a uint16 image of size 512*512. The problem is that the image is all black except there is a medium object of uniform intensity of value 40000. This object has a black hole and I need to extract the indexes of this hole.
So the matrix is in the form of all zeroes, and in some region of the matrix we have a submatrix filled with the value 40000, and in this submatrix we have another submatrix of zeroes. This zero submatrix is the required one to be extracted in terms of its indices. Any suggestions?
A very simple solution would be to use bwlabel. For example:
>> data = 40000.*[0 0 0 0 0; 0 1 1 1 0; 0 1 0 1 0; 0 1 1 1 0; 0 0 0 0 0]
data =
0 0 0 0 0
0 40000 40000 40000 0
0 40000 0 40000 0
0 40000 40000 40000 0
0 0 0 0 0
>> labelMatrix = bwlabel(~data)
labelMatrix =
1 1 1 1 1
1 0 0 0 1
1 0 2 0 1
1 0 0 0 1
1 1 1 1 1
>> holeIndex = find(labelMatrix == 2)
holeIndex =
13
The success of this is based on many assumptions, such as there only being one bright object and one hole in it. A more robust solution would require an example of the images you're analyzing, and might involve some preprocessing of the image and the use of functions such as regionprops to identify areas of the image.
I have got a 2D matrix. There is some region in the matrix where the elements are non-zero, in particular everywhere around the edge they are zero.
I plot the matrix using image as a colorplot and would like to add the curve that shows the boundary between non-zero values to zero values in the matrix. Is there any neat way to do this without loops?
This looks like a job for convhull :
To illustrate this code i'll take a dummy example :
A=zeros(10);
B=binornd(1,0.5,8,8);
A(2:end-1,2:end-1)=B
A =
0 0 0 0 0 0 0 0 0 0
0 0 0 1 0 0 0 0 0 0
0 0 1 1 1 1 1 1 0 0
0 0 1 1 0 0 0 0 1 0
0 0 0 1 0 0 0 1 0 0
0 1 0 0 0 0 0 1 0 0
0 0 0 1 1 1 1 1 1 0
0 0 1 0 1 1 1 1 0 0
0 1 0 1 1 1 1 0 1 0
0 0 0 0 0 0 0 0 0 0
1/ Find the locations of all non zero entries :
[row,col]=find(A);
2/ Take the convex hull of these locations
k=convhull(row,col);
3/ Plot the convex hull (I plot the non zero points aswell but in your problem it will be your image points)
plot(row(k),col(k),'r-',row,col,'b*')
Result :
Another option is using the image processing toolbox and the bwperim function. This will work if you know that your area is completely closed (i.e. has no holes in the boundary)
This is an example using a black and white image, and you have 2 options: fill the inner gaps before, or not. You can see in the result the differences.
A = imread('circles.png');
Afill=imfill(A,'holes'); % optional
Abound1=bwperim(Afill);
Abound2=bwperim(A);
imshow([A,Abound, Abound2])
You can plot one on top of the other with:
[x,y]= find(Abound2);
hold on
image(A*255) %// If A is logical, else use just A (not *255)
colormap('gray')
plot(y,x,'r.')
hold off
axis tight
If you have a gray-scale image (or a matrix with a single value in each position (2D matrix), then you can binarize it first by either:
If you know everything outside your object is EXACTLY zero
A=yourA>0;
If you want to separate your object from the background, and the background is not exactly zero by A=im2bw(yourA,level), by choosing your own level, or letting Otsu do it for you with level=graythresh(yourA)
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.
I am trying to find centroid of objects. I have already implemented connected components labeling and I have developed following code for centroid, it does give result but does not gives correct result:
I have following output matrix i.e matrix_img:
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 1 1 1 1 0 0
2 2 2 2 0 0 0 0 0 1 1 1 1 1 1 0
2 2 2 2 0 0 0 0 1 1 1 1 1 1 1 1
2 2 2 2 0 0 0 0 1 1 1 1 1 1 1 1
2 2 2 2 0 0 0 0 1 1 1 1 1 1 1 1
2 2 2 2 0 0 0 0 0 1 1 1 1 1 1 0
2 2 2 2 0 0 0 0 0 0 1 1 1 1 0 0
2 2 2 2 0 0 0 0 0 0 0 0 0 0 0 0
2 2 2 2 0 0 0 0 0 0 0 0 0 0 0 0
2 2 2 2 0 0 5 5 5 0 0 0 0 0 0 0
2 2 2 2 0 0 5 5 5 0 0 0 0 0 0 0
2 2 2 2 0 0 5 5 5 0 0 0 0 0 0 0
2 2 2 2 0 0 0 0 0 0 0 0 0 0 0 0
and following is the code
n= max(max(matrix_img));
for k=1:n
a(k)=length(find(matrix_img==k));
sx(k)=0;
sy(k)=0;
cx=0;
cy=0;
for i=1:1:r
for j=1:1:c
if(matrix_img(i,j)==k)
sx(k)=sx(k)+i;
sy(k)=sy(k)+j;
cx=sx(k)/a(k);
cy=sy(k)/a(k);
end
end
end
fprintf('Centroid of Object %d is %d and %d \n', k, cx, cy);
end
It gives result like :
Centroid of Object 1 is 7 and 1.250000e+001
Centroid of Object 2 is 1.050000e+001 and 2.500000e+000
Centroid of Object 3 is 0 and 0
Centroid of Object 4 is 0 and 0
Centroid of Object 5 is 14 and 8
Object 5 result is correct, object 2 is completely wrong and object 1 is partially wrong.. what shall I do?
The values you obtain are the exact centroids for those objects. So you might want to define what you'd expect to get as results.
To make this more clear, I've colored the objects in your matrix. By the symmetry on your cartesian grid, there should be an equal number of points to the left and right of your centroid and the same for above/below the centroid. I've drawn a figure with your objects colored in, together with lines to represent the horizontal and vertical center lines. Those are lines for which, we have an equal number of points to the left/right (or above/below) of them that are part of a certain object.
Their intersection is the centroid, so you can see for object 5 (blue one) that the centroid is at (8, 14). For the other two objects, these center lines do not lie on the integer grid you have: the red object (1) has its centroid at (12.5, 7) which is also the outcome of your code and the green object (2) is centered around (2.5, 10.5).
You will either have to live with inaccuracy introduced by rounding your centroids (e.g. round(cx)) or you will have to live with the non-integer coordinates of the centroids.
Next to that, I also recommend you vectorize your code as oli showed: this allows you to run your code faster and it is easier to understand when you are somewhat familiar with MATLAB than for loops.
Perhaps a little note with regard to your string representation: don't use %d for non-integers, as you see that will cause real numbers to be displayed in scientific notation. I think it's clearer if you use something like %0.2f as your format string.
When you use matlab, avoid using loops, it makes your code very slow, and it is much longer.
You can do the same thing by doing that:
[y x]=ndgrid(1:size(matrix_img,1),1:size(matrix_img,2));
n=max(matrix_img(:));
for k=1:n
cy=mean(y(matrix_img==k));
cx=mean(x(matrix_img==k));
fprintf('Centroid of Object %d is %2.2g and %2.2g \n', k, cx, cy);
end
(Maybe you want to swap x and y in that code)