OPENCV系列博客主要记录自己学习OPENCV的历程,以及存储已经实现的代码,以备后续回顾使用,代码中包含了主要的备注。
1.距离变换和分水岭分割原理
2.距离变换和分水岭分割--处理流程
2.代码实现
#include
#include
#include
using namespace std;
using namespace cv;
/*
This program is used to verify the Watershed method, also as the final project of the OPENCV learning
*/
char input_win[] = "Input image";
char black_background[] = "Black background";
char watershed_win[] = "Water shed image";
char sharped_win[] = "Sharped image";
int main(int argc, char** argv) {
// Step 0: 读取image,并判断是否为空
Mat src;
src = imread("E:/OpenCVLearning/Project/source_image/card.png");
if (src.empty()) {
printf("Could not load this image, please check...");
return -1;
}
namedWindow(input_win,CV_WINDOW_AUTOSIZE);
imshow(input_win,src);
// Step 1: 将白色背景转化为黑色背景,为后续变换做准备
for (int row = 0; row < src.rows; row++) {
for (int col = 0; col < src.cols; col++) {
if (src.at(row,col) == Vec3b(255,255,255)) {
src.at(row, col)[0] = 0;
src.at(row, col)[1] = 0; //判断当前像素的RGB是否均为255-白色,若是,将其替换为黑色
src.at(row, col)[2] = 0; //!!注意:这个方法可以同样用在替换某一像素的特定颜色!
}
}
}
namedWindow(black_background, CV_WINDOW_AUTOSIZE);
imshow(black_background, src);
// Step 2: 使用Laplace算子,将图片进行锐化Sharp (注意:算子定义,filter2D, 图片格式转换等)
Mat kernal = (Mat_(3, 3) << 1, 1, 1, 1, -8, 1, 1, 1, 1); //创建Laplace算子
Mat laplaceImg;
Mat sharp = src;
filter2D(sharp, laplaceImg,CV_32F, kernal, Point(-1,-1),0); //进行滤波
src.convertTo(sharp,CV_32F);
Mat imgResult = sharp - laplaceImg;
//
imgResult.convertTo(imgResult, CV_8UC3);
laplaceImg.convertTo(laplaceImg, CV_8UC3); //转换图片格式
namedWindow(sharped_win, CV_WINDOW_AUTOSIZE);
imshow(sharped_win, imgResult);
// Step 3: 二值距离变换
Mat binImage;
cvtColor(src, binImage, CV_BGR2GRAY);
threshold(binImage, binImage,40,255, CV_THRESH_BINARY | CV_THRESH_OTSU); //使用自动方式查找二值化阈值
Mat dist;
distanceTransform(binImage,dist, CV_DIST_L1,3); //距离变化??得到的是什么??
normalize(dist,dist,0,1,NORM_MINMAX);
threshold(dist, dist, 0.4, 1, CV_THRESH_BINARY); //得到marker?
// Step 4: 二值腐蚀
Mat kernel1 = Mat::ones(13,13,CV_8UC1);
erode(dist, dist, kernel1);
imshow("Peaks",dist);
// Step5: 寻找轮廓,并且标记出来,以便后续处理
Mat dist_8u;
dist.convertTo(dist_8u, CV_8U);
vector> contours; //查找轮廓
findContours(dist_8u, contours, RETR_EXTERNAL,CHAIN_APPROX_SIMPLE,Point(0,0));
Mat markers = Mat::zeros(dist.size(),CV_32SC1); //创建一个marker图像,并在其上进行绘制轮廓
for (size_t i = 0; i < contours.size(); i++) {
drawContours(markers, contours, static_cast(i), Scalar::all(static_cast(i) + 1), -1); //注意,-1表示内部填充,>0表示填充轮廓
}
circle(markers, Point(5, 5), 3, Scalar(255, 255, 255), -1); //
imshow("my markers", markers * 1000);
// Step6: 使用分水岭变换,查找并显示
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 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(row, col);
if (index > 0 && index <= static_cast(contours.size())) {
dst.at(row, col) = colors[index - 1];
}
else {
dst.at(row, col) = Vec3b(0, 0, 0);
}
}
}
imshow("Final Result", dst);
waitKey(0);
return 0;
}
3.运行效果如下