距离变换的定义 :计算图像中像素点到最近零像素点的距离,也就是零像素点的最短距离。
距离变换的常见算法
距离变换常用应用:
API
C++: void distanceTransform(
InputArray src,
OutputArray dst,
OutputArray labels,
int distanceType,
int maskSize,
int abelType=DIST_LABEL_CCOMP)
参数说明
将图像中的边缘转换成”山脉”,将均匀区域转化为“山谷”,这样有助于分割目标
算法: 基于浸泡理论 ------- 简单来说就是输出图像的极大值
//关键:画一个圆作为标记这个不理解
# include <opencv2\opencv.hpp>
# include <iostream>
using namespace std;
using namespace cv;
Mat src, src_gray;
int main(int argc, char**argv) {
src = imread("H:\\tuku\\jiuwei.jpg");
if (!src.data)
{
cout << "can't find the picture...";
return -1;
}
imshow("sourceimg", src);
//换背景为黑色
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;
}
}
imshow("blackground src", src);
Mat kernel = (Mat_<float>(3, 3) << 1, 1, 1, 1, -8, 1, 1, 1, 1);
//sharpen
Mat imgLaplance;
Mat sharpenimg = src;
filter2D(src, imgLaplance, CV_32F, kernel, Point(-1, -1),0,BORDER_DEFAULT);
//拉普拉斯有浮点数计算,位数要提高到32
src.convertTo(sharpenimg, CV_32F);
//原图减边缘(白色)实现边缘增强
Mat resultImg = sharpenimg- imgLaplance;
resultImg.convertTo(resultImg, CV_8UC3);
imshow("sharpen Image", resultImg);
src = resultImg;
//转化成2值图像
Mat binary;
cvtColor(resultImg, resultImg, CV_BGR2GRAY);
threshold(resultImg, binary, 80, 255, THRESH_BINARY | THRESH_OTSU);
imshow("binary", binary);
//距离变换
Mat distImg;
distanceTransform(binary, distImg, DIST_L1, 5,CV_32F);
//归一化
normalize(distImg, distImg, 0, 1, NORM_MINMAX);
imshow("distance chang", distImg);
//again 二值化
threshold(distImg, distImg, 0.55, 1, THRESH_BINARY);
imshow("again binary threshold", distImg);
//腐蚀(使连在一起的分开)
Mat k1 = Mat::ones(7,7, CV_8UC1);
erode(distImg, distImg, k1);
imshow("erode", distImg);
//标记
Mat dist_8u;
distImg.convertTo(dist_8u, CV_8U);
vector<vector<Point>> contours;
vector<Vec4i> hierachy;
findContours(dist_8u, contours, hierachy,RETR_EXTERNAL, CHAIN_APPROX_SIMPLE, Point(0, 0)); //finContours只支持CV_8UC1的格式,所以要进行通道转换
//创建标记
Mat marsker = Mat::zeros(src.size(), CV_32SC1);// 如果使用 CV_8UC1 ,watershed 函数会报错
//因为masker最后的边缘存储是-1,所以必须使用有符号的
//话标记
for (size_t i = 0; i < contours.size(); i++) {
drawContours(marsker, contours, static_cast<int>(i), Scalar(static_cast<int>(i) + 1), -1);
}
circle(marsker, Point(5, 5), 3, Scalar(255, 255, 255), -1);
//关键代码!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// 创建marker,标记的位置如果在要分割的图像块上会影响分割的结果,如果不创建,分水岭变换会无效
imshow("marsker", marsker*1000); //执行后,dist_m的像素值十分的小,扩大了1000倍,才看出来了轮廓
//分水岭变换
watershed(src, marsker); //根据距离变换的标记,在原图上分离
Mat water = Mat::zeros(marsker.size(), CV_8UC1);
marsker.convertTo(water, CV_8UC1);
bitwise_not(water, water, Mat());
imshow("water Image", water);
// 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(marsker.size(), CV_8UC3);
for (int row = 0; row < marsker.rows; row++) {
for (int col = 0; col < marsker.cols; col++) {
int index = marsker.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;
waitKey(0);
return 0;
}