導入
ウォーターシェッド アルゴリズムは画像セグメンテーションにおける一般的なアルゴリズムであり、画像を複数の領域に分割することができ、各領域のピクセルは同様の特性を持ちます。この記事では、OpenCV ライブラリのウォーターシェッド アルゴリズムを使用して画像をセグメント化します。
1. アルゴリズム原理
分水界アルゴリズムは、グラフ理論に基づいた画像セグメンテーション アルゴリズムです。その基本的な考え方は、画像を地形図とみなすことです。画像内のピクセルは、地形上の山頂とみなされます。グレー値が大きいほど、より類似しています。グレーの値が小さいほど、谷のようになります。次に、地形図に水を追加し、山の頂上から水を流し、2 つの山頂の間の水の流れが合流すると、2 つのエリアに分割されます。やがて、すべての山は水の流れによっていくつかのエリアに分かれました。
2. コードの実装
以下は、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);
}
3. 実験結果
上記のコードを使用して「4.jpg」画像をセグメント化した後の最終結果は次のようになります。
上の図からわかるように、分水嶺アルゴリズムは画像をいくつかの領域に分割することに成功し、各領域のピクセルは同様の特性を持っています。