1. 程式人生 > >OpenCv形態學操作和漫水填充

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;
}

上為彩色圖,下為灰度圖