opencv::基于距离变换与分水岭的图像分割

什么是图像分割
    图像分割(Image Segmentation)是图像处理最重要的处理手段之一
    图像分割的目标是将图像中像素根据一定的规则分为若干(N)个cluster集合,每个集合包含一类像素。
    根据算法分为监督学习方法和无监督学习方法,图像分割的算法多数都是无监督学习方法 - KMeans

距离变换常见算法有两种
    - 不断膨胀/腐蚀得到
    - 基于倒角距离

分水岭变换常见的算法
    - 基于浸泡理论实现 
cv::distanceTransform(
    InputArray  src,
    OutputArray dst,
    OutputArray  labels,  //离散维诺图输出
    int  distanceType,    // DIST_L1/DIST_L2,
    int maskSize,            // 3x3,最新的支持5x5,推荐3x3、
    int labelType=DIST_LABEL_CCOMP //dst输出8位或者32位的浮点数,单一通道,大小与输入图像一致
)

cv::watershed(
    InputArray image,
    InputOutputArray  markers
)
处理流程
    1. 将白色背景变成黑色-目的是为后面的变换做准备
    2. 使用filter2D与拉普拉斯算子实现图像对比度提高,sharp
    3. 转为二值图像通过threshold
    4. 距离变换
    5. 对距离变换结果进行归一化到[0~1]之间
    6. 使用阈值,再次二值化,得到标记
    7. 腐蚀得到每个Peak - erode
    8. 发现轮廓 – findContours
    9. 绘制轮廓- drawContours
    10. 分水岭变换 watershed
    11. 对每个分割区域着色输出结果
int main(int argc, char** argv) {
    char input_win[] = "input image";
    char watershed_win[] = "watershed segmentation demo";
    Mat src = imread(STRPAHT2);
    if (src.empty()) {
        printf("could not load image...\n");
        return -1;
    }
    namedWindow(input_win, CV_WINDOW_AUTOSIZE);
    imshow(input_win, 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;
            }
        }
    }
    //namedWindow("black background", CV_WINDOW_AUTOSIZE);
    //imshow("black background", src);

    // sharpen
    Mat kernel = (Mat_<float>(3, 3) << 1, 1, 1, 1, -8, 1, 1, 1, 1);
    Mat imgLaplance;
    Mat sharpenImg = src;
    //使用filter2D与拉普拉斯算子实现图像对比度提高,sharp
    filter2D(src, imgLaplance, CV_32F, kernel, Point(-1, -1), 0, BORDER_DEFAULT);
    src.convertTo(sharpenImg, CV_32F);
    Mat resultImg = sharpenImg - imgLaplance;

    resultImg.convertTo(resultImg, CV_8UC3);
    imgLaplance.convertTo(imgLaplance, CV_8UC3);
    imshow("sharpen image", resultImg);

    // convert to binary
    Mat binaryImg;
    cvtColor(src, resultImg, CV_BGR2GRAY);
    // 转为二值图像通过threshold
    threshold(resultImg, binaryImg, 40, 255, THRESH_BINARY | THRESH_OTSU);
    imshow("binary image", binaryImg);

    Mat distImg;
    // 每一个非零点距离离自己最近的零点的距离
    distanceTransform(binaryImg, distImg, DIST_L1, CV_DIST_C, 5);

    // 归一化
    normalize(distImg, distImg, 0, 1, NORM_MINMAX);
    imshow("distance result", distImg);

    // 使用阈值,再次二值化,得到标记
    threshold(distImg, distImg, .4, 1, THRESH_BINARY);
    Mat k1 = Mat::ones(13, 13, CV_8UC1);
    // 膨胀/腐蚀
    erode(distImg, distImg, k1, Point(-1, -1));
    imshow("distance binary image", distImg);

    // markers
    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));

    // 绘制轮廓
    Mat markers = Mat::zeros(src.size(), CV_32SC1);
    for (size_t i = 0; i < contours.size(); i++) {
        drawContours(markers, contours, static_cast<int>(i), Scalar::all(static_cast<int>(i) + 1), -1);
    }
    circle(markers, Point(5, 5), 3, Scalar(255, 255, 255), -1);
    imshow("my markers", markers * 1000);

    // 分水岭变换
    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);

    // 对每个分割区域着色输出结果
    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];
            }
            else {
                dst.at<Vec3b>(row, col) = Vec3b(0, 0, 0);
            }
        }
    }
    imshow("Final Result", dst);

    waitKey(0);
    return 0;
}

原文地址:https://www.cnblogs.com/osbreak/p/11505002.html

时间: 2024-10-11 09:33:20

opencv::基于距离变换与分水岭的图像分割的相关文章

基于Matlab的标记分水岭分割算法

转自:http://blog.sina.com.cn/lyqmath 1 综述 Separating touching objects in an image is one of the more difficult image processing operations. The watershed transform is often applied to this problem. The watershed transform finds "catchment basins"(

图像处理之倒角距离变换

图像处理之倒角距离变换 图像处理中的倒角距离变换(Chamfer Distance Transform)在对象匹配识别中经常用到, 算法基本上是基于3x3的窗口来生成每个像素的距离值,分为两步完成距离变换,第一 步从左上角开始,从左向右.从上到下移动窗口扫描每个像素,检测在中心像素x的周 围0.1.2.3四个像素,保存最小距离与位置作为结果,图示如下: 第二步从底向上.从右向左,对每个像素,检测相邻像素4.5.6.7保存最小距离与 位置作为结果,如图示所: 完成这两步以后,得到的结果输出即为倒角

3.3 距离变换

3.3.4 距离变换-扫描 1 //////https://blog.csdn.net/gone_huilin/article/details/53223026 2 #include <opencv2/imgproc/imgproc.hpp> 3 #include <opencv2/core/core.hpp> 4 #include <opencv2/highgui/highgui.hpp> 5 #include <iostream> 6 // 计算欧式距离

OpenCV仿射变换+投射变换+单应性矩阵

本来想用单应性求解小规模运动的物体的位移,但是后来发现即使是很微小的位移也会带来超级大的误差甚至错误求解,看起来这个方法各种行不通,还是要匹配知道深度了以后才能从三维仿射变换来入手了,纠结~ estimateRigidTransform():计算多个二维点对或者图像之间的最优仿射变换矩阵 (2行x3列),H可以是部分自由度,比如各向一致的切变. getAffineTransform():计算3个二维点对之间的仿射变换矩阵H(2行x3列),自由度为6. warpAffine():对输入图像进行仿射

基于Mat变换的骨架提取Java

针对一副二值图像,区域内的点只有背景点(白点,0值)和前景点(黑点,1值).对于给定区域的像素点逐次应用两个基本步骤,以提取骨架: step1,如果一个像素点满足下列4个条件,那么将它标记为要删除的点: (1)2<=N(p1)<=6,其中N(p1)=p2+p3+p4+...+p8+p9; (2)T(p1)=1,其中T(p1)是以p2,p3,...,p8,p9,p2的次序旋转,从0到1的变换次数; (3)p2*p4*p6=0; (4)p4*p6*p8=0. step2,条件(1)(2)不变,但是

KINECT+opencv基于骨骼信息对视频进行动作识别

KINECT+opencv基于骨骼信息对视频进行动作识别 环境:kinect1.7+opencv2.4+vc2015 使用kinect获取并按批处理三维空间内的骨骼信息 基于视频帧差计算各关节运动向量并与本地模板匹配 目录 KINECTopencv基于骨骼信息对视频进行动作识别 目录 写在前面 对当前帧处理并匹配 kinect对帧的处理 与模板的向量余弦计算 根据动态时间规划法匹配 记录并保存模板到本地 使用opencv的FileStorage类生成xml文件 写在前面 自前一篇过去一周了.这次

距离变换DT

距离变换:计算区域中的每个点与最接近的区域外的点之间距离,把二值图象变换为灰度图象. 对于目标中一个点,距离变换的定义为改点与目标边界最近的距离. 目标点离边界约近则值越小,转换的点越暗:越远,值越大,转换的点约亮.  a是原图,b是以图像边缘看做B,c是以两个白点看做B 计算方法: 1.串行实现: 模板:  将a分成b.c两个模板.做一次从左上角到右下角的前向扫描,做一次右下角到左上角的反向扫描. 扫描方案类似于卷积: 在扫到某个像素时,将模板系数值和图像的对应值加起来,将所得和中最小值赋给对

为什么说K-Means是基于距离的聚类算法?

K-means算法是很典型的基于距离的聚类算法,采用距离作为相似性的评价指标,两个对象的距离越近,其相似度就越大.K-means算法认为簇是由距离靠近的对象组成的,因此把得到紧凑且独立的簇作为最终目标.k-means聚类,需要用户设定一个聚类个数k作为输入数据.k个初始类聚类中心点的选取,对聚类结果具有较大的.为了用k-means达到高质量的聚类,需要估计k值.可根据需要的聚类个数,估计k值.比如一百万篇文章,如果平均500篇分为一类,k值可以取2000(1百万/500). 算法步骤1)随机选取

基于GraphCuts图割算法的图像分割----OpenCV代码与实现

部分代码与文档是早些时候收集的,出处找不到了,还请原作者看到后联系注明. 图切算法是组合图论的经典算法之一.近年来,许多学者将其应用到图像和视频分割中,取得了很好的效果.本文简单介绍了图切算法和交互式图像分割技术,以及图切算法在交互式图像分割中的应用. 图像分割指图像分成各具特性的区域并提取出感兴趣目标的技术和过程,它是由图像处理到图像分析的关键步骤,是一种基本的计算机视觉技术.只有在图像分割的基础上才能对目标进行特征提取和参数测量,使得更高层的图像分析和理解成为可能.因此对图像分割方法的研究具