How to remove connected components from an image while retaining some - image

Let's say I have 5 connected components (labelled objects) in an image called labelledImage from bwlabel. How can I manipulate labelledImage so that the objects that are labelled as 1 and 4 only display, while removing the objects that are labelled as 2, 3 and 5. Then, how can I manipulate the original RGB image so that the connected components that are labelled as 1 and 4 only display.
I know how to retain a single connected component by using this line of code below. However, I don't know how to do this for multiple labelled regions.
Works.
connectedComponent1 = (labelledImage == 1);
imshow(connectedComponent1)
Doesn't work.
connectedComponent1and4 = (labelledImage == [1 4]);
imshow(connectedComponent1and4)

You can't do logical indexing that way. The simplest way is to perhaps use Boolean statements to combine things.
connectedCompoonent1and4 = labelledImage == 1 | labelledImage == 4;
In general, supposing you had a vector of elements that denote which components you want to keep, you could use bsxfun, permute and any to help you with that. Something like this should work:
components = [1 4];
connected = any(bsxfun(#eq, labelledImage, permute(components, [1 3 2])), 3);
The above code uses matrix broadcasting to create a temporary 3D matrix where each slice i contains the ith value of the vector components which contain the desired labels you want to keep. labelledImage is also replicated in the third dimension so the result using bsxfun creates a 3D matrix where each slice i segments out the ith object you want to keep. We then combine all of the objects together using any and looking in the third dimension.
If you don't like one-liners, you could even use a simple for loop:
components = [1 4];
connected = false(size(labelledImage, 1), size(labelledImage, 2));
for ind = 1 : numel(components)
connected = connected | labelledImage == components(ind);
end
This creates an output image that is all false, then we loop through each value in the vector of components you want to keep and append those results on top of the result. The end will give you all of the components you want to keep.
Lastly, you could use also use ismember and determine those values in your matrix that can be found between the label matrix and the components vector and simply create your mask that way:
connected = ismember(labelledImage, components);
Now that you have a mask of objects you want to extract out, to use this on the original image, simply multiply each channel with the mask. Another use of bsxfun can do that for you. Assuming your image in RGB is called img, simply do the following:
outImg = bsxfun(#times, img, cast(connected, class(img)));
To perform element-wise multiplication, you must ensure that both matrices that are being multiplied have the same type. I convert the mask into the same class as whatever the input image is and perform the multiplication.

Use ismember.
Ex:
A = randi(5,5); % your connected component matrix
B = [1 4] % list of components you want to keep
A =
4 2 1 3 5
2 4 2 5 1
3 4 5 1 4
1 4 1 3 5
4 3 5 1 5
A(~ismember(A,B)) = 0
A =
4 0 1 0 0
0 4 0 0 1
0 4 0 1 4
1 4 1 0 0
4 0 0 1 0

Related

Append matrix to another matrix in Matlab

I have a matrix M=[4 3 2 1;1 2 3 4]. I want to append different size matrices at each iteration:
M=[4 3 2 1;1 2 3 4];
for i=1:t
newM=createNewMatrix;
M=[M;newM];
end
newM can be [] or a Nx4 matrix. This is very slow though. What is the fastest way to do this?
Update
Pre-allocating would look like this?
M=zeros(200000,4)
start=1
M(1:2,:)=M=[4 3 2 1;1 2 3 4];
for i=1:t
newM=createNewMatrix;
size_of_newM=size(newM,1);
finish=start+size_of_newM-1;
M(start:finish,:)=newM;
start=finish;
end
Like suggested, preallocation gives the most boost.
Using cell arrays is another good approach and could be implemented like this:
M = cell(200000, 1);
M{1} = [4 3 2 1; 1 2 3 4];
for t=2:200000
i = randi(3)-1;
M{t}=rand(i,4);
end
MC = vertcat(M{:});
In principle you generate a cell array with arbitrary long arrays in each cell and then concatenate them afterwards.
This worked for me nearly twice as fast as your preallocation update. On the other hand, this still was only around one second for the example with 200k iterations...

Replace multiple pixels value in an image with a certain value Matlab

I have an image 640x480 img, and I want to replace pixels having values not in this list or array x=[1, 2, 3, 4, 5] with a certain value 10, so that any pixel in img which doesn't have the any of the values in x will be replaced with 10. I already know how to replace only one value using img(img~=1)=10 or multiple values using this img(img~=1 & img~=2 & img~=3 & img~=4 & img~=5)=10 but I when I tried this img(img~=x)=10 it gave an error saying Matrix dimensions must agree. So if anyone could please advise.
You can achieve this very easily with a combination of permute and bsxfun. We can create a 3D column vector that consists of the elements of [1,2,3,4,5], then use bsxfun with the not equals method (#ne) on your image (assuming grayscale) so that we thus create a 3D matrix of 5 slices. Each slice would tell you whether the locations in the image do not match an element in x. The first slice would give you the locations that don't match x = 1, the second slice would give you the locations that don't match x = 2, and so on.
Once you finish this, we can use an all call operating on the third dimension to consolidate the pixel locations that are not equal to all of 1, 2, 3, 4 or 5. The last step would be to take this logical map, which that tells you the locations that are none of 1, 2, 3, 4, or 5 and we'd set those locations to 10.
One thing we need to consider is that the image type and the vector x must be the same type. We can ensure this by casting the vector to be the same class as img.
As such, do something like this:
x = permute([1 2 3 4 5], [3 1 2]);
vals = bsxfun(#ne, img, cast(x, class(img)));
ind = all(vals, 3);
img(ind) = 10;
The advantage of the above method is that the list you want to use to check for the elements can be whatever you want. It prevents having messy logical indexing syntax, like img(img ~= 1 & img ~= 2 & ....). All you have to do is change the input list at the beginning line of the code, and bsxfun, permute and any should do the work for you.
Here's an example 5 x 5 image:
>> rng(123123);
>> img = randi(7, 5, 5)
img =
3 4 3 6 5
7 2 6 5 1
3 1 6 1 7
6 4 4 3 3
6 2 4 1 3
By using the code above, the output we get is:
img =
3 4 3 10 5
10 2 10 5 1
3 1 10 1 10
10 4 4 3 3
10 2 4 1 3
You can most certainly see that those elements that are neither 1, 2, 3, 4 or 5 get set to 10.
Aside
If you don't like the permute and bsxfun approach, one way would be to have a for loop and with an initially all true array, keep logical ANDing the final result with a logical map that consists of those locations which are not equal to each value in x. In the end, we will have a logical map where true are those locations that are neither equal to 1, 2, 3, 4 or 5.
Therefore, do something like this:
ind = true(size(img));
for idx = 1 : 5
ind = ind & img ~= idx;
end
img(ind) = 10;
If you do this instead, you'll see that we get the same answer.
Approach #1
You can use ismember,
which according to its official documentation for a case of ismember(A,B) would output a logical array of the same size as A and with 1's where
any element from B is present in A, 0's otherwise. Since, you are looking to detect "not in the list or array", you need to invert it afterwards, i.e. ~ismember().
In your case, you have img as A and x as B, so ~ismember(img,x) would give you those places where img~=any element in x
You can then map into img to set all those in it to 10 with this final solution -
img(~ismember(img,x)) = 10
Approach #2
Similar to rayryeng's solution, you can use bsxfun, but keep it in 2D which could be more efficient as it would also avoid permute. The implementation would look something like this -
img(reshape(all(bsxfun(#ne,img(:),x(:).'),2),size(img))) = 10

Convert non-zero image pixels to row-column coordinates and save the output to workspace

I'm having difficulties converting image pixels to coordinates and making them appear in my MATLAB workspace. For example, I have the image with pixel values as below (it's a binary image of size 4x4):
0 0 0 0
0 1 1 0
0 1 1 0
0 0 0 0
After getting the pixels, I want to read each value and if they're not equal to zero (which means 1), I want to read the coordinates of that value and save them in to my MATLAB workspace. For example, this is the idea that I thought of:
[x,y] = size(image)
for i=1:x
for j=1:y
if (image(i,j)~=0)
....
However, I am stuck. Can anyone give any suggestion on how to read the coordinates of the non-zero values and save them to my workspace?
Specifically, my expected result in the workspace:
2 2
2 3
3 2
3 3
Doing it with loops is probably not the most efficient way to do what you ask. Instead, use find. find determines the locations in a vector or matrix that are non-zero. In your case, all you have to do is:
[row,col] = find(image);
row and col would contain the row and column locations of the non-zero elements in your binary image. Therefore, with your example:
b = [0 0 0 0;
0 1 1 0;
0 1 1 0;
0 0 0 0];
We get:
>> disp([row, col]);
2 2
3 2
2 3
3 3
However, you'll see that the locations are not in the order you expect. This is because the locations are displayed in column-major order, meaning that the columns are traversed first. In your example, you are displaying them in row-major order. If you'd like to maintain this order, you would sort the results by the row coordinate:
>> sortrows([row, col])
ans =
2 2
2 3
3 2
3 3
However, if you really really really really... I mean really... want to use for loops, what you would do is keep two separate arrays that are initially empty, then loop through each pixel and determine whether it's non-zero. If it is, then you would add the x and y locations to these two separate arrays.
As such, you would do this:
row = []; col = [];
[x,y] = size(image);
for i=1:x
for j=1:y
if (image(i,j)~=0)
row = [row; i]; %// Concatenate row and column location if non-zero
col = [col; j];
end
end
end
This should give you the same results as find.
you can use meshgrid() to collect those coordinates. The function generates two outputs, first being x coordinates, second being y coordinates. you'd go like this:
[xcoord ycoord] = meshgrid( 1:x_size, 1:y_size);
zeros_coordsx = xcoord( image == 0);
zeros_coordsy = ycoord( image == 0);
this is way faster that nested looping and keeps you within matlab's natural vector operation space... these two outputs are in sync,meaning that
image( zeros_coordsy(1), zeros_coordsx(1))
is one of the zeros on the image

Decomposing uint32s into uint8s in a large matrix

For a project I am working on, I am loading in large image files, which Matlab inputs as LxWx3 arrays of uint8s. I have a function which concatenates these component-wise into a LxWx1 array of uint32s, but I can't find a way to do the reverse without using nested for loops, which is far too slow for the matrices I am working with.
Could anyone recommend a way to accomplish this effuciently? Basically, given a LxW Matrix of uint32s, I want to return a LxWx3 matrix of uint8s, where the (x, y,1:3) components are the three most significant bytes in the corresponding uint32.
You can do that with typecast:
A = uint32([2^16 2^30; 256 513]);
B = permute(reshape(typecast(A(:), 'uint8'), [], size(A,1), size(A,2)), [2 3 1]);
B = flipdim(B, 3); %// flip 3rd dim to bring MSB first, if needed (depends on computer)
B = B(:,:,2:4);
Example: for A = uint32([2^16 2^30; 256 513]);
A =
65536 1073741824
256 513
the result is
B(:,:,1) =
1 0
0 0
B(:,:,2) =
0 0
1 2
B(:,:,3) =
0 0
0 1

effective way of transformation from 2D to 1D vector

i want to create 1D vector in matlab from given matrix,for this i have implemented following algorithm ,which use trivial way
% create one dimensional vector from 2D matrix
function [x]=one_dimensional(b,m,n)
k=1;
for i=1:m
for t=1:n
x(k)=b(i,t);
k=k+1;
end
end
x;
end
when i run it using following example,it seems to do it's task fine
b=[2 1 3;4 2 3;1 5 4]
b =
2 1 3
4 2 3
1 5 4
>> one_dimensional(b,3,3)
ans =
2 1 3 4 2 3 1 5 4
but generally i know that,arrays are not good way to use in matlab,because it's performance,so what should be effective way for transformation matrix into row/column vector?i am just care about performance.thanks very much
You can use the (:) operator...But it works on columns not rows so you need to transpose using the 'operator before , for example:
b=b.';
b(:)'
ans=
2 1 3 4 2 3 1 5 4
and I transposed again to get a row output (otherwise it'll the same vector only in column form)
or also, this is an option (probably a slower one):
reshape(b.',1,[])

Resources