图像处理读书散文

【图像处理】OpenCV系列二十二 --- 分水岭算法(wate

2019-05-05  本文已影响22人  307656af5a04

上一节我们学习了用如何用cvtColor函数对一幅图像进行颜色空间的转换,相信大家学习之后,已理解如何使用颜色空间转换的用法,本节呢,我们在学习watershed函数的用法与原理,即分水岭算法如何使用。

1、函数原型

void watershed(InputArray image, 
    InputOutputArray markers);

2、函数功能
使用分水岭算法执行基于标记的图像分割;该函数实现了分水岭非参数标记分割算法的一个变;

在将图像传递给函数之前,您必须大致勾勒出图像标记中包含正索引的所需区域;

因此,每个区域被表示为具有像素值1、2、3等的一个或多个连通组件;

这样的标记可以从二进制掩码中检索到,可以使用findContours和drawContours对轮廓进行查找以及绘制出轮廓;

标记的区域是未来图像的“种子”;标记中的所有其他像素,其与所勾画的区域之间的关系是不知道的,需要通过算法来计算,一般开始设置为0;

在函数输出中,标记中的每个像素设置为“种子”组件的值或区域之间边界处的-1;

Note:
任何两个相邻的连接部件都不一定由分水岭边界(-1的像素)分隔;

例如,它们可以在传递给函数的初始标记图像中互相接触。

3、参数详解

4、实验案例

#include <opencv2/core/utility.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <cstdio>
#include <iostream>

using namespace cv;
using namespace std;

Mat markerMask, img;
Point prevPt(-1, -1);

// 鼠标移动事件
static void onMouse(int event, int x, int y, int flags, void*)
{
    // 越界判断
    if (x < 0 || x >= img.cols || y < 0 || y >= img.rows)
        return;

    // 左键松开消息
    if (event == EVENT_LBUTTONUP || !(flags & EVENT_FLAG_LBUTTON))
        // 清理鼠标按下的坐标
        prevPt = Point(-1, -1);

    // 左键按下消息
    else if (event == EVENT_LBUTTONDOWN)
        // 记录鼠标按下的坐标
        prevPt = Point(x, y);
    
    //鼠标移动消息
    else if (event == EVENT_MOUSEMOVE && (flags & EVENT_FLAG_LBUTTON))
    {
        // 鼠标当前的位置
        Point pt(x, y);

        // 判断是鼠标是否越界
        if (prevPt.x < 0)
            prevPt = pt;

        // 在标记图像上绘制线条
        line(markerMask, prevPt, pt, Scalar::all(255), 5, 8, 0);
        line(img, prevPt, pt, Scalar::all(255), 5, 8, 0);

        prevPt = pt;

        // 实时刷新图像
        imshow("image", img);
    }
}

int main(int argc, char** argv)
{
    // 打开图像
    Mat img0 = imread("lena.png", 1), imgGray;

    // 判断图像是否为空
    if (img0.empty())
    {
        cout << "image error !\n";
        return 0;
    }
    
    // 创建显示图像的窗口
    namedWindow("image", 1);

    // 备份图像
    img0.copyTo(img);

    // 彩色图像转换为灰度图像
    cvtColor(img, markerMask, COLOR_BGR2GRAY);

    // 将单通道的灰度图像,转换为三通道
    cvtColor(markerMask, imgGray, COLOR_GRAY2BGR);

    // 将标记图像全部像素置为0
    markerMask = Scalar::all(0);

    // 显示原图
    imshow("image", img);

    //imshow("imgGray", imgGray);

    // 调用鼠标事件
    setMouseCallback("image", onMouse, 0);

    for (;;)
    {
        // 接收键盘的输入
        char c = (char)waitKey(0);

        // esc键退出
        if (c == 27)
            break;

        // 重置图像为初始状态
        if (c == 'r')
        {
            markerMask = Scalar::all(0);
            img0.copyTo(img);
            imshow("image", img);
        }

        // 按w或者空格对图像进行分水岭算法处理
        if (c == 'w' || c == ' ')
        {
            int i, j, compCount = 0;
            vector<vector<Point> > contours;
            vector<Vec4i> hierarchy;

            // 查找图像的轮廓
            findContours(markerMask, contours, 
                hierarchy, RETR_CCOMP, 
                CHAIN_APPROX_SIMPLE);

            // 如果图像的轮廓为空,不处理
            if (contours.empty())
                continue;

            // 标记
            Mat markers(markerMask.size(), CV_32S);

            markers = Scalar::all(0);

            int idx = 0;
            // 绘制轮廓
            for (; idx >= 0; idx = hierarchy[idx][0], compCount++)
                drawContours(markers, contours, idx, 
                    Scalar::all(compCount + 1),
                    -1, 8, hierarchy, INT_MAX);
            
            if (compCount == 0) 
                continue;

            vector<Vec3b> colorTab;

            // 随机生成颜色
            for (i = 0; i < compCount; i++)
            {
                int b = theRNG().uniform(0, 255);
                int g = theRNG().uniform(0, 255);
                int r = theRNG().uniform(0, 255);
                colorTab.push_back(Vec3b((uchar)b, (uchar)g, (uchar)r));
            }

            double t = (double)getTickCount();

            // 分水岭算法处理
            watershed(img0, markers);

            t = (double)getTickCount() - t;

            // 一次分水岭算法所耗费时间
            printf("execution time = %gms\n", 
                t*1000. / getTickFrequency());

            Mat wshed(markers.size(), CV_8UC3);

            // 绘制经过分水岭处理之后的图像
            for (i = 0; i < markers.rows; i++)
                for (j = 0; j < markers.cols; j++)
                {
                    int index = markers.at<int>(i, j);

                    // 未找到的用白色填充
                    if (index == -1)
                        wshed.at<Vec3b>(i, j) = Vec3b(255, 255, 255);

                    // 不再处理范围的用黑色填充
                    else if (index <= 0 || index > compCount)
                        wshed.at<Vec3b>(i, j) = Vec3b(0, 0, 0);
                    else
                        // 每一块用不同的颜色绘制
                        wshed.at<Vec3b>(i, j) = colorTab[index - 1];
                }

            // 显示分水岭算法处理后的图像,分水岭占%50,灰度图像占%50
            wshed = wshed*0.5 + imgGray*0.5;

            imshow("watershed transform", wshed);
        }
    }
    return 0;
}

5、实验结果

原图(左)与效果图(右)

我是奕双,现在已经毕业将近两年了,从大学开始学编程,期间学习了C语言编程,C++语言编程,Win32编程,MFC编程,毕业之后进入一家图像处理相关领域的公司,掌握了用OpenCV对图像进行处理,如果大家对相关领域感兴趣的话,可以关注我,我这边会为大家进行解答哦!如果大家需要相关学习资料的话,可以私聊我哦!

上一篇下一篇

猜你喜欢

热点阅读