图像分水岭

任何灰度图像都可以看做是地形表面,把高强度看做山峰和丘陵,低强度看做山谷。首先,你用不同显色的水(label)充满每个单独的山谷(局部最小值),随着水位的上升,根据山谷附近的山峰高度(梯度),那些来自不同山谷的不同颜色的水开始合并。但是我们要通过在这些合并的位置建立阻隔,来避免合并的发生。

我们不断地加水,并修建阻隔,直到所有的山峰被水覆盖。如此,我们创建的阻隔所分割出的结果即是分水岭其背后的“哲学”。一般用于分离相互接触的对象

OpenCV提供了基于marker的分水岭算法,但是我们需要将确定的背景和前景标记出来。

CMM的分水岭变换示意

下面左边的梯度图,可以描述为右边的地形图,地形的高度是由梯度图的梯度值决定,灰度为0对应地形图的地面,梯度值最大的像素对应地形图的最高点。

下面这张图就是以地形图的角度来看分水岭

示例代码

 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
/**
Created by kaijun on 10/17/19.
 图像分水岭
*/
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;

string path = "/home/kaijun/Pictures/wan.png";

Mat src = imread(path,IMREAD_COLOR);
int n=1;
void onMouse(int event, int x, int y, int flags, void* userdata){
    Mat markers = *(Mat*)userdata;
    // 当鼠标左键按下的时候
    if(event == EVENT_LBUTTONDOWN){

        circle(markers,Point(x,y),50,Scalar(n++),-1);

        imshow("markers",markers);
    }

    // 当鼠标右键按下的时候
    if(event == EVENT_RBUTTONDOWN){
        // 颜色列表
        vector<Vec3b> colors={Vec3b(255,255,0),Vec3b(0,255,255)};
        // 执行分水岭操作
        watershed(src,markers);

        // 定义一个用于显示结果的图像
        Mat result(markers.size(),CV_8UC3);
        for (int row = 0; row < markers.rows; ++row) {
            for (int col = 0; col < markers.cols; ++col) {
                int index = markers.at<int>(row,col);
                if(index>0 && index<n){
                    result.at<Vec3b>(row,col) = colors[index-1];
                    cout<<index<<n<<endl;
                }
            }
        }

        imshow("result",result);
    }

}

int main(int argc,char** argv){
    // 显示原图像
    imshow("src",src);

    // 彩色图像转成灰度图像
    Mat gray;
    cvtColor(src,gray,COLOR_BGR2GRAY);
    imshow("gray",gray);

    // 定义用于记录标记的图像
    Mat markers(src.size(),CV_32S);
    // 注册鼠标滑动时间
    setMouseCallback("gray",onMouse,&markers);

    Mat binary;
    threshold(gray,binary,0,255,THRESH_BINARY|THRESH_TRIANGLE);
    imshow("binary",binary);

    waitKey(0);
    return 0;
}