《OpenCV 2 Computer Vision Application Programming Cookbook》
  Chapter 5:Segmenting images using watersheds

断断续续地学习着OpenCV;书本已经看到了第五章,是关于分水岭算法(Watersheds algorithm)的;花了挺长的一段时间在这算法和代码的理解上,也查看了很多相关的资料介绍;不过还是不能很好的去理解该算法的使用原理和方法。

函数介绍:
C++: void watershed(InputArray image, InputOutputArray markers)
下面内容来自:OpenCV官方文档介绍
The function implements one of the variants of watershed, non-parametric marker-based segmentation algorithm, described in [Meyer92].
Before passing the image to the function, you have to roughly outline the desired regions in the image markers with positive (>0) indices. So, every region is represented as one or more connected components with the pixel values 1, 2, 3, and so on. Such markers can be retrieved from a binary mask using findContours() and drawContours() (see the watershed.cpp demo). The markers are “seeds” of the future image regions. All the other pixels in markers , whose relation to the outlined regions is not known and should be defined by the algorithm, should be set to 0’s. In the function output, each pixel in markers is set to a value of the “seed” components or to -1 at boundaries between the regions.


大致解释一下:该算法借鉴了地理学上的一些概念,将图像看成是一幅地图,根据图像中物体梯度的大小,将高梯度的看成是山脉,低梯度的看成是盆地或者是山谷,这样有助于分割目标;通过模拟浸入的过程,山谷中水越来越多,梯度越来越高,就会流过低些的山脉,从而流到别的山谷中,那么他们就连成了一片区域。区域分割的要求是把不同的标记分割成不同的地方。所以如果一直注水,可能就会覆盖别的区域了。这时算法就采取某种方法使修大坝(-1作为分界线)使标记的不同区域不会因为注水而相连,他们会互不相干的扩张领地,直到把整个领地都扩张完为止。

下面是一个简单的应用
  • 首先加载一张图片,之后将图片转换成二进制图片,并进行相应的阈值化处理,使图片前后景更加分明清楚
1
2
3
4
5
6
7
8
cv::Mat image=cv::imread("D:\\opencvStudy\\pic\\watersheds.jpg");
if(!image.data)
     std::cout<<"Error loading the picture"<<std::endl;
//convert to binary image,
cv::Mat binary ;
cv::cvtColor(image,binary,CV_BGR2GRAY);
cv::threshold(binary,binary,100,255,THRESH_BINARY);
    
  • 二进制图处理,主要包括膨胀和腐蚀,用来去除噪声,同时合并一些小区域,获得bg和fg
1
2
3
4
5
6
7
cv::Mat fg;//foreground
cv::erode(binary,fg,cv::Mat(),cv::Point(-1,-1),2);
 
//idenify image pixeles without objects
cv::Mat bg;//background
cv::dilate(binary,bg,cv::Mat(),cv::Point(-1,-1),3);
cv::threshold(bg,bg,1,128,cv::THRESH_BINARY_INV);
  • 制作marker,注意marker大小要求和待处理图像,即binary一样,然后对“+”重载,获得markers
1
2
3
4
//create markers image
cv::Mat markers(binary.size(),CV_8U,cv::Scalar(0));
markers=bg+fg;
cv::imshow("markers",markers);
  • 将markers转化成32整形,扩大其数值范围,之后调用watersheds即可。
1
2
3
4
//create watershed segmentation object
WatershedSegment segmenter;
segmenter.setMarkers(markers);
cv::Mat result=segmenter.process(image);
下面是完整代码和结果图
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
// CookBook-Chapter 5:Segmenting images using watersheds
// By Steven 2012.11.7 
 
#include "opencv2/core/core.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <iostream>
 
using namespace cv;
using namespace std;
 
class WatershedSegment{
private:
    cv::Mat markers;
public:
    void setMarkers(cv::Mat &markerImage)
    {
        markerImage.convertTo(markers,CV_32S);//convert to image of ints
 
    }
 
    cv::Mat process(cv::Mat &image)
    {
        cv::watershed(image,markers);
        markers.convertTo(markers,CV_8U);
        return markers;
    }
};
 
int main()
{
    cv::Mat image=cv::imread("D:\\opencvStudy\\pic\\watersheds.jpg");
    if(!image.data)
        std::cout<<"Error loading the picture"<<std::endl;
 
    //convert to binary image,
    cv::Mat binary ;
    cv::cvtColor(image,binary,CV_BGR2GRAY);
    cv::threshold(binary,binary,100,255,THRESH_BINARY);
 
    //display them
    cv::imshow("OriginalImage",image);
    cv::imshow("OriginalBinary",binary);
 
    //obviously,in the binaryImage,there is so much noise around the object;
    //so next is to eliminate noise and smaller objects
    cv::Mat fg;//foreground
    cv::erode(binary,fg,cv::Mat(),cv::Point(-1,-1),2);
    cv::imshow("fg",fg);
 
    //idenify image pixeles without objects
    cv::Mat bg;//background
    cv::dilate(binary,bg,cv::Mat(),cv::Point(-1,-1),3);
    cv::threshold(bg,bg,1,128,cv::THRESH_BINARY_INV);
    cv::imshow("bg",bg);
 
    //create markers image
    cv::Mat markers(binary.size(),CV_8U,cv::Scalar(0));
    markers=bg+fg;
    cv::imshow("markers",markers);
 
    //create watershed segmentation object
    WatershedSegment segmenter;
    segmenter.setMarkers(markers);
 
    cv::Mat result=segmenter.process(image);
    cv::threshold(result,result,0,255,cv::THRESH_BINARY);
    //result=result&1;
    imshow("final_result",result);
     
        waitKey(0);
    return 0;
}



Leave a Reply.