Saturday, 28 September 2013

Segmenting leaf by openCV2--01 : By watershed and contours

In the last post(recognize leaf--00) I use threshold and contours to cut the leafs from the images, this time I will give watershed a try.


graph_00
graph_01
graph_02


Step 0

  Read the images(graph_00~graph_02) and convert it to gray image.


cv::Mat color_image = cv::imread(Folder + "leaf" + num + ".png");
if(color_image.empty()){
  std::cerr<<"input is empty"<<std::endl;
  return -1;
}

cv::Mat image;
cv::cvtColor(color_image, image, CV_BGR2GRAY);

Step 1

Binarize the image.



cv::Mat binary;
cv::threshold(image, binary, 140, 255, cv::THRESH_BINARY); 

Step 2

Remove small objects and noise.

auto const structure = cv::getStructuringElement(cv::MORPH_ELLIPSE, 
                       cv::Size(5, 5));
cv::Mat fore_ground;
cv::erode(binary, fore_ground, structure, cv::Point(-1, -1), 4);

We severely erode the images in order to retain only pixels belonging to the foreground.

Step 3

Some of the objects can't removed by erosion, so we find out the contours of the image and remove unimportant objects from the images in order to get the foreground(255), they will be considered to correspond to objects of interest.


std::vector<std::vector<cv::Point>> contours;
cv::findContours(fore_ground, contours, CV_RETR_EXTERNAL, 
      CV_CHAIN_APPROX_NONE);
OCV::remove_contours(contours, 8000, 50000);
fore_ground.setTo(0);
cv::drawContours(fore_ground, contours, -1, cv::Scalar(255), CV_FILLED);

remove_contours was explained in the previous post.

graph_03
graph_04
graph_05



Step 4

  Mark the background as 128 and mark unknown pixels(neither foreground nor background) as 0.


cv::Mat back_ground;
cv::dilate(binary, back_ground, structure, cv::Point(-1, -1), 4);
cv::threshold(back_ground, back_ground, 1, 128, cv::THRESH_BINARY_INV);
graph_06
graph_07
graph_08

Step 5

  Combined foreground and background to get marker and use the marker to get the mask we need.


cv::Mat markers = back_ground + fore_ground;       
cv::Mat mask;
markers.convertTo(mask, CV_32S);
cv::watershed(color_image, mask);
mask.convertTo(mask, CV_8U);
cv::threshold(mask, mask, 150, 255, CV_THRESH_BINARY);

graph_09
graph_10
graph_11

Step 6

Generate final result.


cv::Mat result;
color_image.copyTo(result, mask);

graph_12
graph_13
graph_14
As usual, the codes can download from github.

Next post of Recognize leaf by openCV2

 Next post I will try to use K-means to cluster the images. I decided to put k-means as a new topic, next post of this series I will try to segment the leafs into independent leaf.