The VS2017 environment uses the watershed algorithm in the OpenCV library to segment images.

Introduction

The watershed algorithm is a common algorithm in image segmentation. It can divide the image into several regions, and the pixels in each region have similar characteristics. In this article, we will use the watershed algorithm in the OpenCV library to segment images.

1. Algorithm Principle

The watershed algorithm is an image segmentation algorithm based on graph theory. Its basic idea is to regard the image as a terrain map. The pixels in the image are regarded as mountain peaks on the terrain. The larger the gray value, the more like the mountain peaks. The smaller the gray value, the more like a valley. Then add water to the terrain map, let the water flow from the top of the mountain, and when the water flow between the two peaks meets, it divides them into two areas. Eventually, all the mountains were divided into several areas by the water flow.

2. Code implementation

The following is the code that uses the OpenCV library to implement the watershed algorithm. The code uses a picture named "4.jpg". After reading it, enhanced contrast, binarization, distance transformation, finding markers, finding contours, and watershed are performed. algorithm and other steps to finally obtain the segmented image.

#pragma once
#include <opencv2\opencv.hpp>
#include <iostream>

using namespace std;
using namespace cv;

int main()
{
    // 定义函数原型
    Mat watershedCluster(Mat &srcImg, int &numSegments);

    // 读取图像
    Mat m1 = imread("D:/4.jpg", 1);

    // Laplace算子增强对比
    Mat imgLp;
    Laplacian(m1, imgLp, -1, 3);
    Mat sharpImg = m1 - imgLp;

    // 显示原图和增强后的图像
    imshow("原图", m1);
    imshow("增强", sharpImg);

    // 图像二值化
    Mat binaryImg;
    cvtColor(sharpImg, binaryImg, COLOR_BGR2GRAY);
    threshold(binaryImg, binaryImg, 130, 255, THRESH_BINARY);
    imshow("二值化", binaryImg);

    // 距离变换
    Mat distanceImg;
    distanceTransform(binaryImg, distanceImg, DIST_L1, 3, 5);

    // 归一化距离变换结果
    imshow("距离变换", distanceImg);
    normalize(distanceImg, distanceImg, 0, 1, NORM_MINMAX);
    imshow("归一化后", distanceImg);

    // 再次二值化,寻找标记
    threshold(distanceImg, distanceImg, 0.3, 1, THRESH_BINARY);

    // 腐蚀标记
    Mat kernel = getStructuringElement(MORPH_RECT, Size(13, 13), Point(-1, -1));
    Mat erodeImg;
    morphologyEx(distanceImg, erodeImg, MORPH_ERODE, kernel);
    imshow("腐蚀标记", erodeImg);

    // 发现轮廓
    Mat erodeU8;
    erodeImg.convertTo(erodeU8, CV_8U);
    vector<vector<Point>> contours;
    vector<Vec4i> hierarchy;
    findContours(erodeU8, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE, Point(0, 0));

    // 绘制轮廓
    Mat markers = Mat::zeros(m1.size(), CV_8UC1);
    for (size_t i = 0; i < contours.size(); i++) {
        drawContours(markers, contours, i, Scalar::all(static_cast<int>(i) + 1), FILLED);
    }
    imshow("轮廓", markers);

    // 分水岭算法
    markers.convertTo(markers, CV_32SC1);
    watershed(m1 - imgLp, markers);
    Mat mark = Mat::zeros(markers.size(), CV_8UC1);
    markers.convertTo(mark, CV_8UC1);
    bitwise_not(mark, mark, Mat());
    imshow("分水岭", mark);

    // 为每个轮廓生成随机颜色
    vector<Vec3b> colors;
    for (size_t i = 0; i < contours.size(); i++) {
        int r = theRNG().uniform(0, 255);
        int g = theRNG().uniform(0, 255);
        int b = theRNG().uniform(0, 255);
        colors.push_back(Vec3b((uchar)b, (uchar)g, (uchar)r));
    }

    // 填充颜色并显示最终结果
    Mat dst = Mat::zeros(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 <= static_cast<int>(contours.size())) {
                dst.at<Vec3b>(row, col) = colors[index - 1];
            }
            else {
                dst.at<Vec3b>(row, col) = Vec3b(0, 0, 0);
            }
        }
    }
    imshow("最终结果", dst);

    waitKey(0);
}

3. Experimental results

After using the above code to segment the "4.jpg" image, the final result is as shown below:

As can be seen from the above figure, the watershed algorithm successfully divides the image into several regions, and the pixels in each region have similar characteristics.

 

Guess you like

Origin blog.csdn.net/weixin_59276073/article/details/130514841