一. 什么是图像分割?
- 图像分割的目标是将图像中像素根据一定的规则分为若干个cluster集合,每个集合包含一类像素。
- 根据算法分为监督学习方法和无监督学习方法 ,图像分割的多数算法都属于无监督学习方法。
二. 距离变换常见算法:
- 不断膨胀/腐蚀
- 基于倒角距离
三. 分水岭变换常见算法:
(一).基于浸泡理论
API
1. 距离变换cv::distanceTransform
cv::distanceTransform(
InputArray src,
OutputArray dst,
OutputArray labels,
int distanceType,
int maskSize,
int labelType=DIST_LABEL_CCOMP
)
distanceType = DIST_LI/DIST_L2,
maskSize=3 X 3,
labels, 离散维诺图输出
dst, 输出8位或32位的浮点数,单一通道
2. 分水岭变换cv::watershed
cv::watershed(
InputArray image,
InputOutputArray markers
)
四. 代码处理思路:
- 将白色背景变为黑色,目的是为了后面的变换做准备
- 使用filter2D与拉普拉斯算子实现图像对比度的提高,sharp
- 通过threshold转为二值图像
- 距离变换
- 对距离变换结果归一化到0-1之间
- 使用阈值,再次二值化,得到标记
- 腐蚀得到每个Peak-erode
- 发现轮廓 - findContours
- 绘制轮廓 - drawContours
10.分水岭变换 - watershed,并对每个分割区域着色输出
/*****基于距离变换和分水岭的图像分割*****/
#include
#include
using namespace std;
int main(int argc, char** argv) {
cv::Mat src = cv::imread("rec_cir.png");
cv::namedWindow("in", cv::WINDOW_NORMAL);
cv::imshow("in", src);
//将背景提取,变为黑色
for (int row = 0; row < src.rows; ++row) {
for (int col = 0; col < src.cols; ++col) {
if (src.at(row, col) == cv::Vec3b(255, 255, 255)) {
src.at(row, col)[0] = 0;
src.at(row, col)[1] = 0;
src.at(row, col)[2] = 0;
}
}
}
//显示背景变色的图片
cv::namedWindow("back", cv::WINDOW_NORMAL);
cv::imshow("back", src);
//锐化
cv::Mat kernel = (cv::Mat_(3, 3) << 1, 1, 1, 1, -8, 1, 1, 1, 1);
cv::Mat imgLaplance; //Laplance变换后的结果
cv::Mat sharpenImg = src; //锐化后的结果
//Laplance变换
cv::filter2D(src, imgLaplance, CV_32F, kernel, cv::Point(-1, -1),
0, cv::BORDER_DEFAULT);
//锐化即为原图减去拉式变换后的图像
//两个图像相减,图像的类型深度必须相同
src.convertTo(sharpenImg, CV_32F);
cv::Mat resultImg = sharpenImg - imgLaplance;
//图像深度转为8UC3,3通道彩色图像显示
resultImg.convertTo(resultImg, CV_8UC3);
imgLaplance.convertTo(imgLaplance, CV_8UC3);
//显示锐化彩色结果
cv::namedWindow("sharp", cv::WINDOW_NORMAL);
cv::imshow("sharp", resultImg);
src = resultImg;
//二值化
cv::Mat binaryImg;
cv::cvtColor(src, resultImg, cv::COLOR_BGR2GRAY);
cv::threshold(resultImg, binaryImg, 40, 255, cv::THRESH_BINARY | cv::THRESH_OTSU);
cv::namedWindow("binary", cv::WINDOW_NORMAL);
cv::imshow("binary", binaryImg);
//距离变换
cv::Mat distImg;
cv::distanceTransform(binaryImg, distImg, cv::DIST_L1, 3, 5);
//归一化到0-1
cv::normalize(distImg, distImg, 0, 1, cv::NORM_MINMAX);
cv::namedWindow("dist_norm", cv::WINDOW_NORMAL);
cv::imshow("dist_norm", distImg);
//距离变换后再二值化
cv::threshold(distImg, distImg, 0.4, 1, cv::THRESH_BINARY);
//二值化后腐蚀
//腐蚀的结构体
cv::Mat k1 = cv::Mat::ones(3, 3, CV_8UC1);
cv::erode(distImg, distImg, k1, cv::Point(-1, -1));
cv::namedWindow("dist_erode", cv::WINDOW_NORMAL);
cv::imshow("dist_erode", distImg);
cv::Mat dist_8u;
//转换为8U深度
distImg.convertTo(dist_8u, CV_8U);
//找出轮廓
vector> contours;
cv::findContours(dist_8u, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE,
cv::Point(0, 0));
//标记 markerssr
cv::Mat markers = cv::Mat::zeros(src.size(), CV_32SC1);
for (size_t i = 0; i < contours.size(); ++i) {
//在markers上画出轮廓
cv::drawContours(markers, contours, static_cast(i),
cv::Scalar::all(static_cast(i)+1), -1);
}
cv::circle(markers, cv::Point(5, 6), 3, cv::Scalar(255, 255, 255));
cv::namedWindow("markers", cv::WINDOW_NORMAL);
//32S格式的图片显示有问题,需要转换成32F
//cv::imshow("markers", markers);
//分水岭算法,
cv::watershed(src, markers);
//转化到8UC1深度
cv::Mat mark = cv::Mat::zeros(markers.size(), CV_8UC1);
markers.convertTo(mark, CV_8UC1);
//图像取反
cv::bitwise_not(mark, mark, cv::Mat());
cv::namedWindow("watershed image", cv::WINDOW_NORMAL);
cv::imshow("watershed image", mark);
//随机着色
vector colors;
for (size_t i = 0; i < contours.size(); ++i) {
int r = cv::theRNG().uniform(0, 255);
int g = cv::theRNG().uniform(0, 255);
int b = cv::theRNG().uniform(0, 255);
colors.push_back(cv::Vec3b((uchar)b, (uchar)g, (uchar)r));
}
//填充每个图像
cv::Mat dst = cv::Mat::zeros(markers.size(), CV_8UC3);
//对markers标记进行处理,每个标记的颜色对应着相同的相同数字的索引
for (int row = 0; row < markers.rows; ++row) {
for (int col = 0; col < markers.cols; ++col) {
//markers中每个标记的索引就是对应的颜色
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) = cv::Vec3b(0, 0, 0);
}
}
}
cv::namedWindow("final", cv::WINDOW_NORMAL);
cv::imshow("final", dst);
cv::waitKey(0);
return 0;
}
关注公号【开发小鸽】,获取海量计算机视觉与深度学习资源,实战项目源码,最新论文下载,大厂面试经验!!!