文章目录
- 距离变换
- 分水岭算法
- distanceTransform 距离变换
- watershed 分水岭算法
- 示例
距离变换
分水岭算法
distanceTransform 距离变换
void cv::distanceTransform (InputArray src,OutputArray dst,int distanceType,int maskSize,int dstType = CV_32F)
src:输入图像,数据类型为CV8U的单通道图像
dst:输出图像,与输入图像具有相同的尺寸,数据类型为CV_8U或者CV_32F的单通道图像distanceType:选择计算两个像素之间距离方法的标志, 常用的有DIST_L1、DIST_L2和DIST_C,分别表示街区距离、欧式距离和棋盘距离
maskSize: 距离变换掩码矩阵大小,可以选择尺寸为DIST_MASK_3(3×3)、DIST_MASK_ 5(5X5)
dstType:输出图像的数据类型,可以是CV_8U或者CV_32F
watershed 分水岭算法
void cv::watershed ( InputArray image,InputOutputArray markers)
mage:输入图像,数据类型为CV_8U的三通道图像
markers:输入/输出CV_32S的单通道图像的标记结果,与原图像具有相同的尺寸
示例
示例流程
1.将白色背景变成黑色-目的是为后面的变换做准备
2. 使用filter2D与拉普拉斯算子实现图像对比度提高,sharp
3. 转为二值图像通过threshold
4. 距离变换
5. 对距离变换结果进行归一化到[0~1]之间
6. 使用阈值,再次二值化,得到标记
7. 腐蚀得到每个Peak - erode
8.发现轮廓 – findContours
9. 绘制轮廓- drawContours
10.分水岭变换 watershed
11. 对每个分割区域着色输出结果
#include <opencv2/opencv.hpp>
#include <iostream>
#include <math.h>using namespace std;
using namespace cv;int main(int argc, char** argv) {char input_win[] = "input image";char watershed_win[] = "watershed segmentation demo";Mat src = imread("D:/vcprojects/images/cards.png");// Mat src = imread("D:/kuaidi.jpg");if (src.empty()) {printf("could not load image...\n");return -1;}namedWindow(input_win, CV_WINDOW_AUTOSIZE);imshow(input_win, src);// 1. 将白色背景变成黑色-目的是为后面的变换做准备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", CV_WINDOW_AUTOSIZE);imshow("black background", src);//2.使用filter2D与拉普拉斯算子实现图像对比度提高,sharpMat 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// 3. 转为二值图像通过thresholdMat binaryImg;cvtColor(src, resultImg, CV_BGR2GRAY);threshold(resultImg, binaryImg, 40, 255, THRESH_BINARY | THRESH_OTSU);imshow("binary image", binaryImg);//4. 距离变换Mat distImg;distanceTransform(binaryImg, distImg, DIST_L1, 3, 5);//5. 对距离变换结果进行归一化到[0~1]之间normalize(distImg, distImg, 0, 1, NORM_MINMAX);imshow("distance result", distImg);// 6. 使用阈值,再次二值化,得到标记threshold(distImg, distImg, .4, 1, THRESH_BINARY);Mat k1 = Mat::ones(13, 13, CV_8UC1);//7. 腐蚀得到每个Peak - erodeerode(distImg, distImg, k1, Point(-1, -1));imshow("distance binary image", distImg);//8.发现轮廓 – findContoursMat dist_8u;distImg.convertTo(dist_8u, CV_8U);vector<vector<Point>> contours;findContours(dist_8u, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE, Point(0, 0));// 9. 绘制轮廓- drawContoursMat 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*1000);//10.分水岭变换 watershedwatershed(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 colorvector<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));}// 11. 对每个分割区域着色输出结果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);waitKey(0);return 0;
}