2

enter image description here

In my progress work, I have to detect a parasite. I have found the parasite using HSV and later made it into a grey image. Now I have done edge detection too. I need some code which tells MATLAB to find the largest contour (parasite) and make the rest of the area as black pixels.

rayryeng
  • 102,964
  • 22
  • 184
  • 193
Jae
  • 85
  • 1
  • 7
  • That's quite vague, can you elaborate/provide sample image/code please? Otherwise you might be interested in [regionprops](http://www.mathworks.com/help/images/ref/regionprops.html) – Benoit_11 Feb 19 '15 at 18:32
  • I have added the image now @Benoit_11. I haven't written any code. I want to have only the the large area covered line ( like a cashew shape ) there and to make the rest into black pixels. I by reading the other related question at stack overflow learnt that I should use contour and if I can write any code in order to choose the large contour I can make it work. But I have no clue how to do that. – Jae Feb 19 '15 at 18:38

3 Answers3

3

You can select the "largest" contour by filling in the holes that each contour surrounds, figure out which shape gives you the largest area, then use the locations of the largest area and copy that over to a final image. As what Benoit_11 suggested, use regionprops - specifically the Area and PixelList flags. Something like this:

im = imclearborder(im2bw(imread('https://i.stack.imgur.com/a5Yi7.jpg')));
im_fill = imfill(im, 'holes');
s = regionprops(im_fill, 'Area', 'PixelList');
[~,ind] = max([s.Area]);
pix = sub2ind(size(im), s(ind).PixelList(:,2), s(ind).PixelList(:,1));
out = zeros(size(im));
out(pix) = im(pix);
imshow(out);

The first line of code reads in your image from StackOverflow directly. The image is also a RGB image for some reason, and so I convert this into binary through im2bw. There is also a white border that surrounds the image. You most likely had this image open in a figure and saved the image from the figure. I got rid of this by using imclearborder to remove the white border.

Next, we need to fill in the areas that the contour surround, so use imfill with the holes flag. Next, use regionprops to analyze the different filled objects in the image - specifically the Area and which pixels belong to each object in the filled image. Once we obtain these attributes, find the filled contour that gives you the biggest area, then access the correct regionprops element, extract out the pixel locations that belong to the object, then use these and copy over the pixels to an output image and display the results.

We get:

enter image description here

Alternatively, you can use the Perimeter flag (as what Benoit_11) suggested, and simply find the maximum perimeter which will correspond to the largest contour. This should still give you what you want. As such, simply replace the Area flag with Perimeter in the third and fourth lines of code and you should still get the same results.

rayryeng
  • 102,964
  • 22
  • 184
  • 193
  • Haha I was writing the exact same answer but with the `Perimeter` flag :) Well done and +1 of course. – Benoit_11 Feb 19 '15 at 18:59
  • @Benoit_11 - Oh yeah! Perimeter would work here too. D'oh lol. You should still put your answer in :) I'll +1 it too. – rayryeng Feb 19 '15 at 19:02
3

This could be one approach -

%// Read in image as binary
im = im2bw(imread('https://i.stack.imgur.com/a5Yi7.jpg'));
im = im(40:320,90:375); %// clear out the whitish border you have
figure, imshow(im), title('Original image')

%// Fill all contours to get us filled blobs and then select the biggest one
outer_blob = imfill(im,'holes');
figure, imshow(outer_blob), title('Filled Blobs')

%// Select the biggest blob that will correspond to the biggest contour
outer_blob = biggest_blob(outer_blob); 

%// Get the biggest contour from the biggest filled blob
out = outer_blob & im;
figure, imshow(out), title('Final output: Biggest Contour')

The function biggest_blob that is based on bsxfun is an alternative to what other answers posted here perform with regionprops. From my experience, I have found out this bsxfun based technique to be faster than regionprops. Here are few benchmarks comparing these two techniques for runtime performances on one of my previous answers.

Associated function -

function out = biggest_blob(BW)

%// Find and labels blobs in the binary image BW
[L, num] = bwlabel(BW, 8); 

%// Count of pixels in each blob, basically should give area of each blob
counts = sum(bsxfun(@eq,L(:),1:num)); 

%// Get the label(ind) cooresponding to blob with the maximum area 
%// which would be the biggest blob
[~,ind] = max(counts);

%// Get only the logical mask of the biggest blob by comparing all labels 
%// to the label(ind) of the biggest blob
out = (L==ind);

return;

Debug images -

enter image description here

enter image description here

enter image description here

Community
  • 1
  • 1
Divakar
  • 218,885
  • 19
  • 262
  • 358
  • Ah. `bwlabel` with `bsxfun`. Another valid approach. It doesn't rely on `regionprops`. +1. – rayryeng Feb 19 '15 at 19:14
  • @rayryeng yeah that's what I could do! ;) – Divakar Feb 19 '15 at 19:14
  • I do like the fact that you used that to count how many pixels belonged to each object. Curious: How do you think `accumarray` would compare to `bsxfun` in this case? Comparable? Faster? Slower? – rayryeng Feb 19 '15 at 19:18
  • @rayryeng Just as you spoke, added few runtimes stuffs! – Divakar Feb 19 '15 at 19:21
  • 1
    Yep very clever approach I like it! Of course `bsxfun` FTW! – Benoit_11 Feb 19 '15 at 19:22
  • @rayryeng Actually accumarray could be interesting! In fact since you would use this function so many times on IP problems, give it a try?? How about `histc`? – Divakar Feb 19 '15 at 19:24
  • @Divakar - I know for sure `histc` would be slower. I'm gonna try some stuff right now and get back to you :) I'll do some `timeit` with your code. I'd simply be replacing the line that counts up how many pixels belongs to each object. – rayryeng Feb 19 '15 at 19:26
  • @Divakar - Wow... I'm very surprised. I did some simple `tic` `toc` testing and over the 10 times I ran it, `bsxfun` ran for: 0.001058126 seconds, `accumarray`: 0.002268824 seconds and `histc`: 0.000631483 seconds. I only benchmarked the counting labels part. I'm really surprised how `histc` did better. – rayryeng Feb 19 '15 at 19:32
  • 1
    @rayryeng Told you!! At first glance looked perfect for `histc`, because that's what histc is made for (to count) which is what we are doing here! – Divakar Feb 19 '15 at 19:32
  • @Divakar - lol of course. This was a really interesting test. Thanks! – rayryeng Feb 19 '15 at 19:42
3

Since my answer was pretty much all written out I'll give it to you anyway, but the idea is similar to @rayryeng's answer.

Basically I use the Perimeter and PixelIdxList flags during the call to regionprops and therefore get the linear indices of the pixels forming the largest contour, once the image border has been removed using imclearborder.

Here is the code:

clc
clear


BW = imclearborder(im2bw(imread('https://i.stack.imgur.com/a5Yi7.jpg')));

S= regionprops(BW, 'Perimeter','PixelIdxList');

[~,idx] = max([S.Perimeter]);

Indices = S(idx).PixelIdxList;

NewIm = false(size(BW));

NewIm(Indices) = 1;

imshow(NewIm)

And the output:

enter image description here

As you see there are many ways to achieve the same result haha.

Benoit_11
  • 13,905
  • 2
  • 24
  • 35
  • 1
    Ah `PixelIdxList`. Smart. It removes the unnecessary `sub2ind` call I made. +1. – rayryeng Feb 19 '15 at 19:12
  • Why does it give this error everytime i run it?: ??? [~,idx] = max([S.Perimeter]); | Error: Expression or statement is incorrect--possibly unbalanced (, {, or [. – Apurv Jul 03 '15 at 05:59
  • Mhh it shouldn't...it seems ok. You use exactly this code? – Benoit_11 Jul 06 '15 at 10:34