距离变换
输出像素的值为,输入图像中,与最近的零像素的距离,可显示物体靠质心的位置。
一般先对灰度图像取反,即转化为黑色背景。
void distanceTransform( InputArray src, OutputArray dst,
int distanceType, int maskSize, int dstType=CV_32F);
//distanceType,输入DIST_C,DIST_L1,DIST_L2,分别表示计算四连通,八连通,和精准的欧氏距离
//markSize输入3/5.表示使用3x3/5x8的掩膜
分水岭算法
先通过距离变换求出物体质心后,将其想象成山顶,而其他地方为山谷,灌水后,山谷被填满,只剩下山顶,图像就被分割开来。
void watershed( InputArray image, InputOutputArray markers );
//输入8位三通道图像,输入cv_32SC1的Markers标记。
//输出给markers,0为不确定区域,-1为边界。
实战演练
处理流程
- 将白色背景转化为黑色
- sharp,使用拉普拉斯算子将图像对比度提高,突出边缘。
- 二值化
- 距离变换
- 归一化[0~1]之间
- 再次二值化
- 腐蚀
- 发现轮廓
- 绘制轮廓/轮廓编号
- 分水岭变换
- 着色输出
#include<opencv2/opencv.hpp>
#include<opencv2/core/mat.hpp>
#include<iostream>
#include<vector>
using namespace std;
using namespace cv;
int main()
{
Mat src0;
Mat src, dst;
src = imread("C:/Users/LBJ/Desktop/OpenCVTest/puke.png");
if (!src.data)
{
cout << "The iamge is empty" << endl;
return -1;
}
imshow("input_img", src);
src0 = src;
//背景转化为黑色
cvtColor(src, src, CV_BGR2GRAY);
threshold(src, src, 251, 255, THRESH_TOZERO_INV);
medianBlur(src, src, 5);
//锐化提高边缘对比度
Mat kernel = (Mat_<float>(3, 3) << 1, 1, 1,
1, -8, 1,
1, 1, 1);
Mat laplas, sharp = src;
filter2D(sharp, laplas, CV_32F, kernel, Point(-1, -1), 0);
src.convertTo(sharp, CV_32F);
sharp = sharp - laplas * 5;
sharp.convertTo(sharp, CV_8U);
threshold(sharp, sharp, 30, 255, THRESH_BINARY | THRESH_OTSU);
//距离变换0
Mat distan;
distanceTransform(sharp, distan, CV_DIST_L2, CV_DIST_MASK_PRECISE);
normalize(distan, distan, 0, 255, NORM_MINMAX);
threshold(distan, distan, 0.4 * 255, 255, THRESH_BINARY);
//腐蚀
Mat marks = Mat::zeros(src.size(), CV_32SC1);
Mat erod;
Mat ker = Mat::ones(Size(3, 3), CV_8U);
erode(distan, erod, ker, Point(-1, -1));
//发现轮廓 绘制轮廓
Mat dist_8u;
erod.convertTo(dist_8u, CV_8UC1);
vector<vector<Point>> contours;
vector<Vec4i> hierachy;
findContours(dist_8u, contours, hierachy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0));
for (int i = 0; i < contours.size(); i++)
{
drawContours(marks, contours, i, Scalar::all(i + 1), -1, 8, hierachy, 0, Point(0, 0));
}
circle(marks, Point(5, 5), 3, Scalar(255, 255, 255), -1);
//分水岭变换
cvtColor(src0, src0, CV_BGR2GRAY);
threshold(~src0, src0, 20, 255, THRESH_BINARY);
cvtColor(src0, src0, CV_GRAY2BGR);
watershed(src0, marks);
Mat marker = Mat::zeros(marks.size(), CV_8UC1);
marks.convertTo(marker, CV_8UC1);
bitwise_not(marker, marker, Mat());
dst = Mat::zeros(src0.size(), src0.type());
RNG rng;
vector<Vec3b> colors;
for (size_t i = 0; i < contours.size(); i++)
{
int r = rng.uniform(0, 255);
int g = rng.uniform(0, 255);
int b = rng.uniform(0, 255);
colors.push_back(Vec3b(uchar(b), uchar(g), uchar(r)));
}
for (size_t row = 0; row < marks.rows; row++)
{
for (size_t col = 0; col < marks.cols; col++)
{
if (marks.at<int>(row,col)==-1)
{
dst.at<Vec3b>(row, col) = Vec3b(255, 255, 255);
}
else if (marks.at<int>(row, col) >0 && marks.at<int>(row, col)<=static_cast<int>(contours.size()))
{
dst.at<Vec3b>(row, col) = colors[marks.at<int>(row, col)-1];
}
else
{
continue;
}
}
}
imshow("output", dst);
waitKey(0);
return 0;
}
感觉分割的效果还是有待提高的,后续再优化