1. 相关理论
-
什么是图像分割(Image Segmentation)
- 图像分割(Image Segmentation)是图像处理最重要的处理手段之一
- 图像分割的目标是将图像中像素根据一定的规则分为若干(N)个cluster集合,每个集合包含一类像素。
- 根据算法分为监督学习方法和无监督学习方法,图像分割的算法多数都是无监督学习方法 - KMeans
-
距离变换与分水岭介绍
- 距离变换常见算法有两种
- 不断膨胀/ 腐蚀得到
- 基于倒角距离
- 分水岭变换常见的算法
- 基于浸泡理论实现
- 距离变换常见算法有两种
-
相关API
cv::distanceTransform( InputArray src, OutputArray dst, OutputArray labels, int distanceType, int maskSize, int labelType=DIST_LABEL_CCOMP) distanceType = DIST_L1/DIST_L2, maskSize = 3x3,最新的支持5x5,推荐3x3、 labels 离散维诺图输出 dst 输出8位或者32位的浮点数,单一通道,大小与输入图像一致 cv::watershed(InputArray image, InputOutputArray markers)
2. 代码 & 效果展示
-
处理流程
- 将白色背景变成黑色-目的是为后面的变换做准备
- 使用filter2D与拉普拉斯算子实现图像对比度提高,sharp
- 转为二值图像通过threshold
- 距离变换
- 对距离变换结果进行归一化到[0~1]之间
- 使用阈值,再次二值化,得到标记
- 腐蚀得到每个Peak - erode
- 发现轮廓 – findContours
- 绘制轮廓- drawContours
- 分水岭变换 watershed
- 对每个分割区域着色输出结果
-
代码:
#include <iostream> #include <opencv2/opencv.hpp> #include <opencv2/imgproc/types_c.h> using namespace std; using namespace cv; #ifndef P33 #define P33 33 #endif int main() { std::string path = "../circle.JPG"; cv::Mat img = cv::imread(path, 5); string str_input = "input image"; string str_output = "output image"; if (img.empty()) { std::cout << "open file failed" << std::endl; return -1; } namedWindow(str_input, WINDOW_AUTOSIZE); namedWindow(str_output, WINDOW_AUTOSIZE); imshow(str_input, img); #if P34 Mat src = img; // 1. change background for (int row = 0; row < src.rows; row++) { for (int col = 0; col < src.cols; col++) { if (src.at<Vec3b>(row, col) == Vec3b(255, 255, 255)) { src.at<Vec3b>(row, col)[0] = 0; src.at<Vec3b>(row, col)[1] = 0; src.at<Vec3b>(row, col)[2] = 0; } } } namedWindow("black background", WINDOW_AUTOSIZE); imshow("black background", src); // sharpen Mat kernel = (Mat_<float>(3, 3) << 1, 1, 1, 1, -8, 1, 1, 1, 1); Mat imgLaplance; Mat sharpenImg = src; filter2D(src, imgLaplance, CV_32F, kernel, Point(-1, -1), 0, BORDER_DEFAULT); src.convertTo(sharpenImg, CV_32F); Mat resultImg = sharpenImg - imgLaplance; resultImg.convertTo(resultImg, CV_8UC3); imgLaplance.convertTo(imgLaplance, CV_8UC3); imshow("sharpen image", resultImg); // src = resultImg; // copy back // convert to binary Mat binaryImg; cvtColor(src, resultImg, CV_BGR2GRAY); threshold(resultImg, binaryImg, 40, 255, THRESH_BINARY | THRESH_OTSU); imshow("binary image", binaryImg); Mat distImg; distanceTransform(binaryImg, distImg, DIST_L1, 3, 5); normalize(distImg, distImg, 0, 1, NORM_MINMAX); imshow("distance result", distImg); // binary again threshold(distImg, distImg, .4, 1, THRESH_BINARY); Mat k1 = Mat::ones(13, 13, CV_8UC1); erode(distImg, distImg, k1, Point(-1, -1)); imshow("distance binary image", distImg); // markers Mat dist_8u; distImg.convertTo(dist_8u, CV_8U); vector<vector<Point>> contours; findContours(dist_8u, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE, Point(0, 0)); // create makers Mat markers = Mat::zeros(src.size(), CV_32SC1); for (size_t i = 0; i < contours.size(); i++) { drawContours(markers, contours, static_cast<int>(i), Scalar::all(static_cast<int>(i) + 1), -1); } circle(markers, Point(5, 5), 3, Scalar(255, 255, 255), -1); //imshow("my markers", markers); //imshow("my markers", markers*1000); // perform watershed watershed(src, markers); Mat mark = Mat::zeros(markers.size(), CV_8UC1); markers.convertTo(mark, CV_8UC1); bitwise_not(mark, mark, Mat()); imshow("watershed image", mark); // generate random color 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)); } // fill with color and display final result 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("Final Result", dst); #endif cv::waitKey(0); cv::destroyAllWindows(); return 0; }
效果展示:
图像比较多,只展示原图和经过分水岭处理后的图形: