距离变换:
distanceTransform方法用于计算图像中每一个非零点距离离自己最近的零点的距离,distanceTransform的第二个Mat矩阵参数dst保存了每一个点与最近的零点的距离信息,图像上越亮的点,代表了离零点的距离越远。
https://blog.csdn.net/u012566751/article/details/54286391
分水岭算法:
分水岭算法是一种图像区域分割法,在分割的过程中,它会把跟临近像素间的相似性作为重要的参考依据,从而将在空间位置上相近并且灰度值相近的像素点互相连接起来构成一个封闭的轮廓,封闭性是分水岭算法的一个重要特征。
https://blog.csdn.net/xiachong27/article/details/80410384
两种算法对应API:
代码实现:
#include
#include
#include
using namespace cv;
using namespace std;
Mat src,dst,gray_src,temp,dst1;
const char* output_win = "output_img";
RNG rng(12345);
int threshold_v = 100;
int threshold_max = 255;
void Demo_Moments(int, void*);
int main(int argc, char** argv) {
src = imread("C:/Users/18929/Desktop/博客项目/项目图片/1.png");
if (src.empty()) {
printf("could not load image");
return -1;
}
imshow("input_img", 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", WINDOW_AUTOSIZE);
imshow("black background", src);
//2.提高图像对比度,将原图减去用拉普拉斯算子提取的轮廓,从而得到黑边轮廓
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;
//将图片转回8UC3才可以进行下面操作
resultImg.convertTo(resultImg, CV_8UC3);
imgLaplance.convertTo(imgLaplance, CV_8UC3);
imshow("sharpen image", resultImg);
//3.转化为二值图像
Mat binaryImg;
//先转为灰度图像
cvtColor(resultImg, resultImg, CV_BGR2GRAY);
threshold(resultImg, binaryImg, 40, 255, THRESH_BINARY | THRESH_OTSU);
imshow("binary image", binaryImg);
//4.进行距离变化算法
//distanceTransform方法用于计算图像中每一个非零点距离离自己最近的零点的距离,
//distanceTransform的第二个Mat矩阵参数dst保存了每一个点与最近的零点的距离信息,
//图像上越亮的点,代表了离零点的距离越远。
//因此要先将图片二值化且背景图变为黑色
Mat distImg;
distanceTransform(binaryImg, distImg, DIST_L1, 3,5);
//5.将距离图片归一化--令显示更清晰
normalize(distImg, distImg, 0, 1, NORM_MINMAX);
imshow("distance result", distImg);
//6.将图片再次二值化--将各个对象分为各个块,分隔开来--得到标记快
threshold(distImg, distImg, 0.4, 1, THRESH_BINARY);
imshow("distance result1", distImg);
//7.腐蚀操作--腐蚀掉比较小的块,得到peak--为获取轮廓做准备
Mat enrodeImg;
Mat k1 = Mat::ones(13, 13, CV_8UC1);
erode(distImg, enrodeImg, k1, Point(-1, -1));
imshow("enrode_img", enrodeImg);
//8.获取轮廓
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));
//9,绘制边框
Mat markers = Mat::zeros(src.size(), CV_32SC1);
for (size_t i = 0; i < contours.size(); i++)
{
//i+1表示每个对象之间的灰度值图像都差一
drawContours(markers, contours, static_cast<int>(i), Scalar::all(static_cast<int>(i) + 1), -1);//-1代表直接填充整个图像
}
circle(markers, Point(5, 5), 3, Scalar(255, 255, 255), -1);
imshow("markers", markers * 1000);//*1000是因为图像灰度值很低,所以要变亮
//10.分水岭变换
//分水岭算法是一种图像区域分割法,在分割的过程中,它会把跟临近像素间的相似性作为重要的参考依据,
//从而将在空间位置上相近并且灰度值相近的像素点互相连接起来构成一个封闭的轮廓,
//封闭性是分水岭算法的一个重要特征。
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);
//11.采用随机颜色对图像着色
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));
}
//着色
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];//从0开始
}
else {
dst.at<Vec3b>(row, col) = Vec3b(0, 0, 0);
}
}
}
imshow("Final_img", dst);
waitKey(0);
return 0;
}