您的位置:首页 > 运维架构

OpenCv形态学操作和漫水填充

2016-09-08 19:48 399 查看

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(原地输出)

形态运算

开运算


再膨胀" title="">

开运算常用与分割图片(除去小的明亮区域,剩余明亮区域被隔绝)

灰度图中会消除高于其邻点的孤立点。



闭运算


再腐蚀" title="">

闭运算常用消除噪声(亮的区域连接在一起,大小基本不变)

灰度图中会消除低于其邻点的孤立点。



对于开闭运算的迭代的情况下(例如2次开运算)

是执行 腐蚀–>腐蚀–>膨胀–>膨胀

形态梯度运算



梯度运算应用于灰度图,凸显出灰度变化边界值

灰度图中边缘高亮突出。



礼帽运算



开运算是放大裂缝或局部低亮度区域,进行TOPHAT操作后,可以突出局部最大值周围的区域

突出轮廓周围更亮的区域

常用于分割大背景配合小图片(分割出背景)



黑帽运算


原图" title="">

突出轮廓周围更暗的区域

- 可以分割出图像的轮廓



代码

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


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: