OpenCv形态学操作和漫水填充

OpenCv形态学操作

函数介绍


在常用的腐蚀和膨胀基础上,常使用**morphology()**函数.

该方法支持常用的灰度图或是彩色图像(会分单通道处理)

  " morphologyEx函数的参数声明 "
void morphologyEx( 
InputArray src,    //输入图像
OutputArray dst,   //输出图像
int op,    //表示形态学操作的标识
InputArray kernel,  //自定义核
Point anchor=Point(-1,-1),   //核锚点
int iterations=1,   //操作迭代次数
int borderType=BORDER_CONSTANT,
//图像边界像素填充类型(默认为常数,配合borderValue参数)
const Scalar& borderValue=morphologyDefaultBorderValue() 
);

op参数:

MORPH_ERODE   -->"腐蚀操作"
MORPH_DILATE  -->"膨胀操作"
MORPH_OPEN(MORPH_OPEN)    -->"开运算"
CV_MOP_CLOSE(MORPH_CLOSE)   -->"闭运算"
CV_MOP_GRADIENT(MORPH_GRADIENT)  -->"形态梯度"
CV_MOP_TOPHAT(MORPH_TOPHAT)   -->"礼帽"
CV_MOP_BLACKHAT(MORPH_BLACKHAT)  -->"黑帽"

kernel参数:
默认参数为NULL,表示使用系统提供的3×3内核,锚点为中心位置。
常常使用自定义内核,使用函数getStructuringElement()生成我们想要的核矩阵。

Mat getStructuringElement(
int shape,  //核矩阵形状
Size ksize,  //尺寸
Point anchor=Point(-1,-1) //锚点位置
);

**shape**参数:
      MORPH_RECT -->"矩形"
      MORPH_CROSS -->"十字形"
      MORPH_ELLIPSE -->"椭圆形"

**anchor**参数:
      默认为Point(-1,-1)即为核中心。
  对于"十字形"核中心决定核矩阵形状(十字形为单线宽)。


 可以自定义一个核矩阵:
    int kernel_size;  //根据实际情况赋值
    Mat kernel = 
 getStructuringElement(MORPH_RECT,
                     Size(kernel_size*2+1,kerner_size*2+1),
                       Point(kernel_size,kernel_size));

其他参数可使用默认值
所有操作支持in-place(原地输出)


形态运算


  1. 开运算

    先腐蚀-->再膨胀

    • 开运算常用与分割图片(除去小的明亮区域,剩余明亮区域被隔绝)
    • 灰度图中会消除高于其邻点的孤立点。

除去黑圈中的白点


闭运算
先膨胀-->再腐蚀

  • 闭运算常用消除噪声(亮的区域连接在一起,大小基本不变)
  • 灰度图中会消除低于其邻点的孤立点。

白色的面积随着核矩阵变大而扩大

对于开闭运算的迭代的情况下(例如2次开运算)
是执行 腐蚀–>腐蚀–>膨胀–>膨胀


形态梯度运算
膨胀的图像减去腐蚀的图像

  • 梯度运算应用于灰度图,凸显出灰度变化边界值
  • 灰度图中边缘高亮突出。

边缘突出


礼帽运算
这里写图片描述

  • 开运算是放大裂缝或局部低亮度区域,进行TOPHAT操作后,可以突出局部最大值周围的区域
  • 突出轮廓周围更亮的区域
  • 常用于分割大背景配合小图片(分割出背景)


黑帽运算
闭运算-->原图

  • 突出轮廓周围更暗的区域
    - 可以分割出图像的轮廓

这里写图片描述


代码

#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/core/types_c.h>
#include <iostream>
#include <opencv.hpp>

using namespace cv;
using namespace std;


void onBarChangeListener(int,void*); //trackbar回调

Mat src;
int kernel_size = 7;  //核心矩阵大小
int Shape_Type=0;    //核矩阵形态类型
int iterations = 1;  //迭代次数

int main()
{
    namedWindow("src",0);
    namedWindow("dst_open",0);
    namedWindow("dst_close",0);
    namedWindow("dst_gradient",0);
    namedWindow("dst_tophat",0);
    namedWindow("dst_blackhat",0);

    src = imread("H:\\road.png");

    createTrackbar("Shape","src",&Shape_Type,2,onBarChangeListener);
    createTrackbar("KernelSise","src",&kernel_size,10,onBarChangeListener);
    createTrackbar("Iterations","src",&iterations,14,onBarChangeListener);


    waitKey(0);
    return 0;
}



void onBarChangeListener(int,void*)
{
    Mat dst_open,dst_close,dst_gradient,dst_tophat,dst_blackhat;
    Mat kernel;
    int s_type;

    //保证kernel矩阵最小为3x3
    kernel_size = (kernel_size==0)?1:kernel_size;  

    //保证iterations迭代次数最小为1
    iterations = (iterations==0)?1:iterations;     

    switch (Shape_Type)
    {
    case 0:  s_type = MORPH_RECT; break;
    case 1:  s_type = MORPH_CROSS; break;
    case 2:  s_type = MORPH_ELLIPSE; break;
    default:  s_type = MORPH_RECT; break;
    }

 //锚点默认为中心
 kernel=getStructuringElement(s_type,Size(kernel_size*2+1,kernel_size*2+1)); 

    morphologyEx(src,dst_open,MORPH_OPEN,
                 kernel,Point(-1,-1),iterations);
    morphologyEx(src,dst_close,MORPH_CLOSE,
                 kernel,Point(-1,-1),iterations);
    morphologyEx(src,dst_gradient,MORPH_GRADIENT,
                 kernel,Point(-1,-1),iterations);
    morphologyEx(src,dst_tophat,MORPH_TOPHAT,
                 kernel,Point(-1,-1),iterations);
    morphologyEx(src,dst_blackhat,MORPH_BLACKHAT,
                 kernel,Point(-1,-1),iterations);


    imshow("src",src);
    imshow("dst_open",dst_open);
    imshow("dst_close",dst_close);
    imshow("dst_gradient",dst_gradient);
    imshow("dst_tophat",dst_tophat);
    imshow("dst_blackhat",dst_blackhat);
}

这里写图片描述

这里写图片描述





漫水填充算法

函数声明

    用来标记或分离图像的一部分,类似于PS内的魔术棒功能。
    在确定一个中心点情况下,利用一个下限间隔值和一个上限间隔值确定连通区域,对该区域做漫填充操作。

  • 使用low(R1,G1,B1)和high(R2,G2,B2)来确定像素点可接受域

  • 该函数可以彩色图片或者灰度图操作,对于彩色图如果在区域内,可以三个通道同时设置不同的间隔值,如果在接收范围内则会留下该点。

  • 函数的操作区域一定为连续区域。


不带掩码的填充函数说明

int floodFill( InputOutputArray image,  //输入图像
               Point seedPoint,     //中心点
               CV_OUT Rect* rect=0,  //设置边界区域最小矩阵
               Scalar loDiff=Scalar(),  //低像素间隔值
               Scalar upDiff=Scalar(),  //高像素间隔值
               int flags=4       //控制填充区域的连通性,相关性
               );
带掩码
int floodFill( InputOutputArray image,   //输入图像
               InputOutputArray mask,  //掩码
               Point seedPoint,   //中心点
               Scalar newVal,    //填充像素值
               CV_OUT Rect* rect=0,  //边界最小矩阵
               Scalar loDiff=Scalar(),   //低
               Scalar upDiff=Scalar(),    //高
               int flags=4    //标志
               );

----------
参数说明
mask:
    注意该Mat对象应该满足:
          1. 单通道  CV_8UC1
          2. 长宽比原图大2倍(2个像素点即可)   (src.rows+2,src.cols+2)
          3. floodFill函数"只操作mask内像素点为0的值"
          4. mask图像的(x+1,y+1)与原图的(x,y)点对应

 - 在使用过程中可以先划定并清空mask中ROI区域,再做floodFill操作。
 - 也可以在mask中标定原图的边界区域,防止floodFill填充到边界。


----------
seedPoint:
      floodFill操作的中心点(可以利用鼠标点击事件来获取到用户输入)


----------

newVal:
      标定区域后填充的颜色值。( 彩色:Scalar(r,g,b)或灰度:Scalar(d) )


----------
rect:
     默认值为0,设置floodFill函数将要填充的最小边界区域


----------
loDiff:
     低下限间隔值( 彩色:Scalar(r,g,b)  灰度:Scalar(d) )
upDiff:
     高上限间隔值( 彩色:Scalar(r,g,b)  灰度:Scalar(d) )


----------
flags:
    int型定义前24位。参数包含三个部分。

      1.对于低8位(0~7位)。控制填充算法的连通性。可以设置4或8。
          a.为4 -->填充算法只考虑当前像素点的左右和垂直方向的相邻点
          b.为8 -->填充算法还会考虑对角线方向的相邻点

      2.对于8~15位。  
           指定填充掩码图像的值。如果设置为0,则mask即会用1填充。 

      3.对于16~23位。
           a. FLOODFILL_FIXED_RANGE   只有当某个相邻点与中心点(seekPoint)像素差在范围内才填充
           b. FLOODFILL_MASK_ONLY   函数不填充原始图像,只填充mask图像

     flags可以通过OR操作连接起来。
 例如:想用8领域填充,并填充固定像素值范围,填充mask图像,填充值为47.
         则输入参数为: 
                        flags = FLOODFILL_FIXED_RANGE
                              | FLOODFILL_MASK_ONLY
                              | (47<<8);  

----------

程序实例

#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/core/types_c.h>
#include <iostream>
#include <opencv.hpp>

using namespace cv;
using namespace std;

void onMouseChangeListener(int event,int x,int y,int,void*);//鼠标回调
void onBarChangeLisener(int,void*);  //bar回调

Mat src,src_gray,temp,dst;
int lowDiff=5,highDiff=5,maxGap=30;
bool isSrc_Gray=false; //源图是否为灰度图
bool isMask=false;     //floodFill是否需要掩码阵

int main()
{
    src = imread("H:\\cat.jpg");

    temp.create(src.rows+2,src.cols+2,CV_8UC1);  //初始化掩码矩阵
    temp = Scalar::all(0);

    namedWindow("src");

setMouseCallback("src",onMouseChangeListener);
createTrackbar("low","src",&lowDiff,maxGap,onBarChangeLisener);
createTrackbar("high","src",&highDiff,maxGap,onBarChangeLisener);
    imshow("src",src);

    int key=0;
    while (1)
    {
        key=waitKey(0);
        char c=(char)key;

        if (key=='e')  //按下E -->退出
        {
            break;
        }
        switch (c)  
        {
        case 'g':   isSrc_Gray = true; break;  //按下g -->  灰度图
        case 'r':  isSrc_Gray = false;break;  //按下r -->  彩色图
        case 'm':  isMask=true; break;        //按下m -->  带掩码
        case 'n':  isMask=false; break;     //按下n -->  不带掩码
        default: break;         
        }
    }

    waitKey(0);
    return 0;
}



void onMouseChangeListener(int event,int x,int y,int,void*)
{
    if (event!=CV_EVENT_LBUTTONDOWN)
    {
        return;     // 只响应左击事件
    }

    Rect ccmp;
    Point seedPoint = Point(x,y);  //漫水填充原始点=鼠标点击点

    isSrc_Gray?(cvtColor(src,dst,CV_RGB2GRAY)):(src.copyTo(dst));  //确定目标图是否为灰度图


    if (isMask)  //需要掩码
    {
        threshold(temp,temp,1,128,CV_THRESH_BINARY);  //确定mask阵
        if (isSrc_Gray)
        {
            floodFill(dst,temp,seedPoint,Scalar(1),&ccmp,
                Scalar(lowDiff),Scalar(highDiff),4);
        }
        else //彩色通道
        {
            floodFill(dst,temp,seedPoint,Scalar(255,0,0),&ccmp,
                Scalar(lowDiff,lowDiff,lowDiff),Scalar(highDiff,highDiff,highDiff),4);
        }
    }

    else  //不带mask的floodFill函数
    {
        if (isSrc_Gray)  
        {
            floodFill(dst,seedPoint,Scalar(1),&ccmp,Scalar(lowDiff),Scalar(highDiff),4);
        }
        else
        {
            floodFill(dst,seedPoint,Scalar(255,0,0),&ccmp,
                Scalar(lowDiff,lowDiff,lowDiff),Scalar(highDiff,highDiff,highDiff),4);
        }
    }

    imshow("dst",dst);

}


void onBarChangeLisener(int,void*)
{
    lowDiff = lowDiff==0?1:lowDiff;
    highDiff = highDiff==0?1:highDiff;
}

上为彩色图,下为灰度图

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值