IT@程序员猿媛OpenCv图像处理

【图像处理】OpenCV系列九 --- 形态学处理

2019-04-23  本文已影响118人  307656af5a04

上一节我们学习了形态学的基本操作,即图像的腐蚀与膨胀,那么,本节我们一起来学习更高级的形态学操作。

一、形态学操作 --- 开运算

开运算(Opening Operation),其实就是先腐蚀后膨胀的过程。

其数学表达式如下:

开运算

实例:

#include <opencv2/opencv.hpp>
#include <iostream>

using namespace std;
using namespace cv;

//原始图和效果图
Mat g_srcImage, g_dstImage;

//0表示腐蚀erode, 1表示膨胀dilate
int g_nTrackbarNumer = 0;

//结构元素(内核矩阵)的尺寸
int g_nStructElementSize = 3; 

int main()
{
    //载入原图
    g_srcImage = imread("lena.png");

    if (!g_srcImage.data) 
    { 
        printf("image error!");
        return -1; 
    }

    //显示原始图
    namedWindow("【原始图】");
    imshow("【原始图】", g_srcImage);

    //进行初次腐蚀操作并显示效果图
    namedWindow("【开操作效果图】");

    //获取自定义核
    Mat element = getStructuringElement(
        MORPH_RECT, 
        Size(2 * g_nStructElementSize + 1, 
            2 * g_nStructElementSize + 1), 
        Point(g_nStructElementSize, 
            g_nStructElementSize));
    
    // 开操作 = 腐蚀+膨胀
    erode(g_srcImage, g_dstImage, 
        element);
    
    dilate(g_dstImage, g_dstImage,
        element);

    imshow("【开操作效果图】", g_dstImage);

    waitKey(0);
    return 0;
}

实验结果:

开操作原图(左)与效果图(右)

2 、形态学操作 --- 闭运算

先膨胀后腐蚀的过程称为闭运算(Closing Operation)。

其数学表达式如下:

闭运算

实例:

#include <opencv2/opencv.hpp>
#include <iostream>

using namespace std;
using namespace cv;

//原始图和效果图
Mat g_srcImage, g_dstImage;

//0表示腐蚀erode, 1表示膨胀dilate
int g_nTrackbarNumer = 0;

//结构元素(内核矩阵)的尺寸
int g_nStructElementSize = 3; 

int main()
{
    //载入原图
    g_srcImage = imread("lena.png");

    if (!g_srcImage.data) 
    { 
        printf("image error!");
        return -1; 
    }

    //显示原始图
    namedWindow("【原始图】");
    imshow("【原始图】", g_srcImage);

    //进行初次腐蚀操作并显示效果图
    namedWindow("【闭操作效果图】");

    //获取自定义核
    Mat element = getStructuringElement(
        MORPH_RECT, 
        Size(2 * g_nStructElementSize + 1, 
            2 * g_nStructElementSize + 1), 
        Point(g_nStructElementSize, 
            g_nStructElementSize));
    
    // 闭运算= 膨胀+腐蚀
    dilate(g_srcImage, g_dstImage,
        element);
    erode(g_dstImage, g_dstImage,
        element);
    
    imshow("【闭操作效果图】", g_dstImage);

    waitKey(0);
    return 0;
}

实验结果:

闭运算原图(左)与效果图(右)

三、形态学梯度

形态学梯度(Morphological Gradient)为膨胀图与腐蚀图之差。

数学表达式如下:

形态学梯度

对二值图像进行这一操作可以将团块(blob)的边缘突出出来,我们可以用形态学梯度来保留物体的边缘轮廓。

函数原型:

void morphologyEx(InputArray src, 
    OutputArray dst,
    int op, 
    InputArray kernel,
    Point anchor = Point(-1, -1), 
    int iterations = 1,
    int borderType = BORDER_CONSTANT,
    const Scalar& borderValue =
    morphologyDefaultBorderValue());

函数功能:

morphologyEx函数利用基本的膨胀和腐蚀技术,来执行更加高级形态学变换,如开闭运算,形态学梯度,“顶帽”、“黑帽”等等。

参数详解:

实例:

#include <opencv2/opencv.hpp>
#include <iostream>

using namespace std;
using namespace cv;

//原始图和效果图
Mat g_srcImage, g_dstImage;

//0表示腐蚀erode, 1表示膨胀dilate
int g_nTrackbarNumer = 0;

//结构元素(内核矩阵)的尺寸
int g_nStructElementSize = 3; 

int main()
{
    //载入原图
    g_srcImage = imread("lena.png");

    if (!g_srcImage.data) 
    { 
        printf("image error!");
        return -1; 
    }

    //显示原始图
    namedWindow("【原始图】");
    imshow("【原始图】", g_srcImage);

    //进行初次腐蚀操作并显示效果图
    namedWindow("【形态学梯度效果图】");

    //获取自定义核
    Mat element = getStructuringElement(
        MORPH_RECT, 
        Size(2 * g_nStructElementSize + 1, 
            2 * g_nStructElementSize + 1), 
        Point(g_nStructElementSize, 
            g_nStructElementSize));

    // 形态学梯度
    morphologyEx(g_srcImage, g_dstImage, 
        MORPH_GRADIENT, element);
    
    imshow("【形态学梯度效果图】", 
        g_dstImage);
    

    waitKey(0);
    return 0;
}

实验结果:

形态学梯度原图(左)与效果图(右)

四、形态学操作 --- 顶帽

顶帽运算(Top Hat)又常常被译为”礼帽“运算。为原图像与上文刚刚介绍的“开运算“的结果图之差,数学表达式如下:

顶帽

因为开运算带来的结果是放大了裂缝或者局部低亮度的区域,因此,从原图中减去开运算后的图,得到的效果图突出了比原图轮廓周围的区域更明亮的区域,且这一操作和选择的核的大小相关。

顶帽运算往往用来分离比邻近点亮一些的斑块。当一幅图像具有大幅的背景的时候,而微小物品比较有规律的情况下,可以使用顶帽运算进行背景提取。

实例--核心代码:

//获取自定义核
Mat element = getStructuringElement(
    MORPH_RECT,
    Size(2 * g_nStructElementSize + 1,
        2 * g_nStructElementSize + 1),
    Point(g_nStructElementSize,
        g_nStructElementSize));

// 形态学梯度
morphologyEx(g_srcImage, g_dstImage,
    MORPH_TOPHAT, element);

实验结果:

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

五、形态学操作 --- 黑帽

黑帽(Black Hat)运算为”闭运算“的结果图与原图像之差。数学表达式为:

黑帽

黑帽运算后的效果图突出了比原图轮廓周围的区域更暗的区域,且这一操作和选择的核的大小相关。

所以,黑帽运算用来分离比邻近点暗一些的斑块。

实例 - 核心代码:

Mat element = getStructuringElement(
    MORPH_RECT,
    Size(2 * g_nStructElementSize + 1,
        2 * g_nStructElementSize + 1),
    Point(g_nStructElementSize,
        g_nStructElementSize));

// 形态学梯度
morphologyEx(g_srcImage, g_dstImage,
    MORPH_BLACKHAT, element);

实验结果:

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

六、综合实例

#include <opencv2/opencv.hpp>

using namespace std;
using namespace cv;

//原始图和效果图
Mat g_srcImage, g_dstImage;

//元素结构的形状
int g_nElementShape = MORPH_RECT;

//变量接收的TrackBar位置参数
int g_nMaxIterationNum = 10;
int g_nOpenCloseNum = 0;
int g_nErodeDilateNum = 0;
int g_nTopBlackHatNum = 0;

//开/闭运算回调函数
static void on_OpenClose(int, void*);

//腐蚀/膨胀回调函数
static void on_ErodeDilate(int, void*);

//顶帽/黑帽回调函数
static void on_TopBlackHat(int, void*);

//帮助文字显示
static void ShowHelpText();


int main()
{
    ShowHelpText();

    //载入原图
    g_srcImage = imread("lena.png");
    if (!g_srcImage.data) 
    { 
        printf("image error!\n"); 
        return -1;
    }

    //显示原始图
    namedWindow("【原始图】");
    imshow("【原始图】", g_srcImage);

    //创建三个窗口
    namedWindow("【开运算/闭运算】", 1);
    namedWindow("【腐蚀/膨胀】", 1);
    namedWindow("【顶帽/黑帽】", 1);

    //参数赋值
    g_nOpenCloseNum = 9;
    g_nErodeDilateNum = 9;
    g_nTopBlackHatNum = 2;

    //分别为三个窗口创建滚动条
    createTrackbar("迭代值", 
        "【开运算/闭运算】", 
        &g_nOpenCloseNum, 
        g_nMaxIterationNum * 2 + 1, 
        on_OpenClose);

    createTrackbar("迭代值", 
        "【腐蚀/膨胀】",
        &g_nErodeDilateNum, 
        g_nMaxIterationNum * 2 + 1, 
        on_ErodeDilate);

    createTrackbar("迭代值", 
        "【顶帽/黑帽】", 
        &g_nTopBlackHatNum, 
        g_nMaxIterationNum * 2 + 1, 
        on_TopBlackHat);

    //轮询获取按键信息
    while (1)
    {
        int c;
        //执行回调函数
        on_OpenClose(g_nOpenCloseNum, 0);
        on_ErodeDilate(g_nErodeDilateNum, 0);
        on_TopBlackHat(g_nTopBlackHatNum, 0);

        //获取按键
        c = waitKey(0);

        //按下键盘按键Q或者ESC,程序退出
        if ((char)c == 'q' || (char)c == 27)
            break;

        //按下键盘按键1,使用椭圆(Elliptic)
        //结构元素结构元素MORPH_ELLIPSE
        //键盘按键1的ASII码为49
        if ((char)c == 49)
            g_nElementShape = MORPH_ELLIPSE;

        //按下键盘按键2,使用矩形(Rectangle)
        //结构元素MORPH_RECT
        //键盘按键2的ASII码为50
        else if ((char)c == 50)
            g_nElementShape = MORPH_RECT;

        //按下键盘按键3,使用十字形
        //结构元素MORPH_CROSS
        //键盘按键3的ASII码为51
        else if ((char)c == 51)
            g_nElementShape = MORPH_CROSS;

        //按下键盘按键space,在矩形、
        //椭圆、十字形结构元素中循环
        else if ((char)c == ' ')
            g_nElementShape = (g_nElementShape + 1) % 3;
    }

    return 0;
}

//开运算/闭运算
static void on_OpenClose(int, void*)
{
    //偏移量的定义
    int offset = g_nOpenCloseNum - 
        g_nMaxIterationNum;

    //偏移量绝对值
    int Absolute_offset = offset > 0 ? 
        offset : -offset;

    //自定义核                                                  
    Mat element = getStructuringElement(
        g_nElementShape, 
        Size(Absolute_offset * 2 + 1,
            Absolute_offset * 2 + 1), 
        Point(Absolute_offset, 
            Absolute_offset));
    
    //进行操作
    if (offset < 0)
        morphologyEx(g_srcImage, g_dstImage, 
            MORPH_OPEN, element);
    else
        morphologyEx(g_srcImage, g_dstImage, 
            MORPH_CLOSE, element);

    //显示图像
    imshow("【开运算/闭运算】", g_dstImage);
}

//腐蚀/膨胀
static void on_ErodeDilate(int, void*)
{
    //偏移量的定义
    //偏移量
    int offset = g_nErodeDilateNum - 
        g_nMaxIterationNum;

    //偏移量绝对值
    int Absolute_offset = offset > 0 ? 
        offset : -offset;
        
    //自定义核
    Mat element = getStructuringElement(
        g_nElementShape, 
        Size(Absolute_offset * 2 + 1, 
            Absolute_offset * 2 + 1), 
        Point(Absolute_offset, 
            Absolute_offset));

    //进行操作
    if (offset < 0)
        erode(g_srcImage, g_dstImage, 
            element);

    else
        dilate(g_srcImage, g_dstImage, 
            element);

    //显示图像
    imshow("【腐蚀/膨胀】", g_dstImage);
}

//顶帽运算/黑帽运算
static void on_TopBlackHat(int, void*)
{
    //偏移量的定义
    //偏移量
    int offset = g_nTopBlackHatNum - 
        g_nMaxIterationNum;

    //偏移量绝对值                                                    
    int Absolute_offset = offset > 0 ? 
        offset : -offset;
            
    //自定义核
    Mat element = getStructuringElement(
        g_nElementShape, 
        Size(Absolute_offset * 2 + 1, 
            Absolute_offset * 2 + 1), 
        Point(Absolute_offset, 
            Absolute_offset));
    
    //进行操作
    if (offset < 0)
        morphologyEx(g_srcImage, 
            g_dstImage, 
            MORPH_TOPHAT, 
            element);
    else
        morphologyEx(g_srcImage,
            g_dstImage, 
            MORPH_BLACKHAT, 
            element);

    //显示图像
    imshow("【顶帽/黑帽】", g_dstImage);
}

//输出一些帮助信息
static void ShowHelpText()
{
    //输出一些帮助信息
    printf("\n\n\n\t请调整滚动条观察图像效果~\n\n");
    printf("\n\n\t按键操作说明: \n\n"
        "\t\t键盘按键【ESC】或者【Q】- 退出程序\n"
        "\t\t键盘按键【1】- 使用椭圆(Elliptic)结构元素\n"
        "\t\t键盘按键【2】- 使用矩形(Rectangle )结构元素\n"
        "\t\t键盘按键【3】- 使用十字型(Cross-shaped)结构元素\n"
        "\t\t键盘按键【空格SPACE】- 在矩形、椭圆、十字形结构元素中循环\n"
    );
}

实验结果:

综合实例

好了,今天OpenCV形态学学习到这里就结束了,喜欢的朋友可以给我点个赞!!!

上一篇下一篇

猜你喜欢

热点阅读