VS2017环境使用OpenCV 库中的分水岭算法实现对图像的分割

简介

分水岭算法在图像分割中是一种常见的算法,它可以将图像分成若干个区域,每个区域内的像素具有相似的特征。在本篇文章中,我们将使用 OpenCV 库中的分水岭算法实现对图像的分割。

一、算法原理

分水岭算法是一种基于图论的图像分割算法,它的基本思想是将图像看成一个地形图,图像中的像素点看成地形上的山峰,灰度值越大的像素点越像山峰,而灰度值越小的像素点越像山谷。然后在地形图上加入水,让水从山顶开始流动,当两个山峰之间的水流相遇时,就将它们分成两个区域。最终,所有的山峰都被水流分割成了若干个区域。

二、代码实现

下面是使用 OpenCV 库实现分水岭算法的代码,代码中使用的是一张名为 "4.jpg" 的图片,读取后进行了增强对比、二值化、距离变换、寻找标记、发现轮廓、分水岭算法等步骤,最终得到了分割后的图像。

#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);
}

三、实验结果

使用上述代码对 "4.jpg" 图片进行分割后,得到的最终结果如下图所示:

从上图中可以看出,分水岭算法成功地将图像分割成了若干个区域,每个区域内的像素具有相似的特征。

猜你喜欢

转载自blog.csdn.net/weixin_59276073/article/details/130514841