OpenCv形態學操作和漫水填充
阿新 • • 發佈:2019-01-05
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(原地輸出)
形態運算
開運算
- 開運算常用與分割圖片(除去小的明亮區域,剩餘明亮區域被隔絕)
- 灰度圖中會消除高於其鄰點的孤立點。
閉運算
- 閉運算常用消除噪聲(亮的區域連線在一起,大小基本不變)
- 灰度圖中會消除低於其鄰點的孤立點。
對於開閉運算的迭代的情況下(例如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;
}