形态学图像处理之凸壳

基本概念

        如果连接物体A内任意两点的直线段都在A的内部,则称A是凸的。任意物体A的凸壳是包含A的最小凸物体。像素化操作首先需找到二值图像中所有的连通区域,然后用这些区域质心作为这些连通分量的代表,即将一个连通 区域像素化位于区域质心的一个像素。我们总是希望像素化算法能够找到物体的质心来代表该物体,但在实际中,可能由于光照不均等原因导致图像在二值化,物体本身形状发生缺损,像素化算法就无法找到物体真正的质心。此时可适当进行凸壳处理,弥补凹损,算法会找到包含原始形状的最小凸多边形,如下图所示。
在这里插入图片描述
        下面介绍一种获得集合A的凸壳C(A)的算法。
在这里插入图片描述
在这里插入图片描述

        为了确保在上述生长过程中凸壳不会大幅度超过凸性所需的最小尺寸,可以限制其生长以便凸壳不会超出初始时包含物体A的最小矩形。

示例演示

        实现凸壳算法,用来计算嘴巴的凸壳。完整工程代码

/**********************************************
功能:3*3结构元素的二值图像的腐蚀
参数:src-输入图像
     dst-输出图像
     se-模板
***********************************************/
void  CustomErode(const Mat &src, Mat &dst,int se[3][3])
{
    if (src.empty())
    {
        return;
    }
    dst = Mat::zeros(src.size(), src.type());
    bool match = true; //结构元素是否与局部图像匹配
    //逐行扫描图像,为防止越界,四周留出一个像素的空边
    for (int i = 1; i < src.rows - 1; i++)
    {
        uchar* data = dst.ptr<uchar>(i);
        for (int j = 1; j < src.cols - 1; j++)
        {
            match = true;
            for (int m = 0; m < 3; m++)
            {
                for (int n = 0; n < 3; n++)
                {
                    if (se[m][n] == -1)//不关心
                        continue;
                    if (se[m][n] == 1) //前景
                    {
                        if (src.at<uchar>(i - 1 + m, j - 1 + n) != 255)
                        {
                            match = false;
                            break;
                        }
                    }
                    else if (se[m][n]==0)//背景
                    {
                        if (src.at<uchar>(i - 1 + m, j - 1 + n) != 0)
                        {
                            match = false;
                            break;
                        }
                    }
                    else
                    {
                        std::cout << "模板结构元素错误!!!" << std::endl;
                        return;
                    }
                } //for n
            } // for m
            if (match)
            {
                data[j] = 255;
            }
        } // for j
    } //for i
}

/*
 *only process binary image
 * white is foreground
*/
void Convex(const cv::Mat &src, cv::Mat &dst, bool constrain)
{
    //four struct elements to compute convex
    int se1[3][3] = {{1,-1,-1},
                     {1,0,-1},
                     {1,-1,1}};  //弥补右侧的凸缺
    int se2[3][3] = {{1,1,1},
                     {-1,0,-1},
                     {-1,-1,-1}}; //弥补下侧的凸缺
    int se3[3][3] = {{-1,-1,1},
                     {-1,0,1},
                     {-1,-1,1}}; //弥补左侧的凸缺
    int se4[3][3] = {{-1,-1,-1},
                     {-1,0,-1},
                     {1,1,1}}; //弥补上侧的凸缺

     src.copyTo(dst);
    //采用第一个结构元素
    Mat backupsrc1;
    src.copyTo(backupsrc1);
    while(true)
    {
        CustomErode(backupsrc1, dst, se1);
        bitwise_or(dst, backupsrc1, dst);
        if(cv::countNonZero(dst - backupsrc1) == 0)
            break;
        dst.copyTo(backupsrc1);
    }

    //采用第二个结构元素
    Mat backupsrc2;
    src.copyTo(backupsrc2);
    while(true)
    {
        CustomErode(backupsrc2, dst, se2);
        bitwise_or(dst, backupsrc2, dst);
        if(cv::countNonZero(dst - backupsrc2) == 0)
            break;
        dst.copyTo(backupsrc2);
    }

    //采用第三个结构元素
    Mat backupsrc3;
    src.copyTo(backupsrc3);
    while(true)
    {
        CustomErode(backupsrc3, dst, se3);
        bitwise_or(dst, backupsrc3, dst);
        if(cv::countNonZero(dst - backupsrc3) == 0)
            break;
        dst.copyTo(backupsrc3);
    }

    //采用第四个结构元素
    Mat backupsrc4;
    src.copyTo(backupsrc4);
    while(true)
    {
        CustomErode(backupsrc4, dst, se4);
        bitwise_or(dst, backupsrc4, dst);
        if(cv::countNonZero(dst - backupsrc4) == 0)
            break;
        dst.copyTo(backupsrc4);
    }

    dst = Mat::zeros(src.size(), src.type());
    for(int i = 0; i < src.rows; i++)
    {
        for(int j = 1; j < src.cols; j++)
        {
            if(backupsrc1.at<uchar>(i, j) == 255 || backupsrc2.at<uchar>(i, j) == 255 ||
                    backupsrc3.at<uchar>(i, j) == 255 ||backupsrc4.at<uchar>(i, j) == 255)
                dst.at<uchar>(i, j) = 255;
        }
    }

    //需要限制凸壳的生长
    //找到原始图像中物体的范围
    if(constrain)
    {
        int top = src.rows;
        int bottom = 0;
        int left = src.cols;
        int right = 0;
        for(int i = 0; i < src.rows; i++)
        {
            for(int j = 1; j < src.cols; j++)
            {
                if(src.at<uchar>(i, j) == 0) // background
                    continue;
                if(i < top)
                    top = i;
                if(i > bottom)
                    bottom = i;
                if(j < left)
                    left = j;
                if(j > right)
                    right = j;
            }
        }
        for(int i = 0; i < src.rows; i++)
        {
            for(int j = 1; j < src.cols; j++)
            {
                if( i < top ||  i > bottom || j < left || j > right)
                    dst.at<uchar>(i, j) = 0;
            }
        }
    }
}

运行结果

在这里插入图片描述

  • 5
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
形态学图像处理是一种基于数学形态学原理的图像处理方法,可以用于图像的特征提取、边缘检测、形状分析等。在Python中,有多种库可以进行形态学图像处理,如OpenCV和scikit-image。 在OpenCV中,可以使用cv2.morphologyEx函数进行形态学图像处理。其中,可以使用cv2.MORPH_GRADIENT参数来进行形态学梯度操作,即膨胀图与腐蚀图之差。下面是一个使用OpenCV进行形态学梯度操作的示例代码: ```python import numpy as np import cv2 # 读取输入图像 img = cv2.imread('g1.png') # 定义卷积核 kernel = np.ones((6,6), dtype="uint8")/9 # 进行形态学梯度操作 gradient = cv2.morphologyEx(img, cv2.MORPH_GRADIENT, kernel) # 保存结果图像 cv2.imwrite('gradient.jpg', gradient) ``` 另外,scikit-image库也提供了一些形态学图像处理的函数。可以使用skimage.morphology模块中的函数进行膨胀、腐蚀等操作。下面是一个使用scikit-image进行膨胀与腐蚀操作的示例代码: ```python from skimage.morphology import erosion, dilation from skimage.morphology import square from skimage.color import rgb2gray from skimage.io import imread # 读取输入图像并转为灰度图像 im = imread('zebras.jpg', as_gray=True) # 进行腐蚀操作 selem = square(5) eroded = erosion(im, selem) # 进行膨胀操作 dilated = dilation(im, selem) ``` 以上是使用OpenCV和scikit-image库进行形态学图像处理的示例代码,你可以根据自己的需求选择合适的库和函数进行图像处理

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值