形态学操作
主要针对
二值图像
进行处理
图像形态学操作是 基于形状的一系列图像处理操作的合集,主要是基于集合论基础上的形态学数学。
形态学有四个基本操作:膨胀、腐蚀、开、闭。
膨胀与
腐蚀是图像处理中最常用的形态学操作手段,常常被组合起来一起使用实现一些复杂的图像形态学操作。
膨胀与腐蚀能实现各种各样的功能,主要如下:
1、消除噪声。
2、分割出独立的图像元素,在图像中连接相邻的元素。
3、寻找图像中的明显的极大值区域或极小值区域。
求出图像的梯度。
膨胀与腐蚀都是对于白色部分即高亮部分(不是黑色部分)进行操作。膨胀是图像中的高亮部分进行膨胀,类似于“领域扩大”,而腐蚀是原图像中的高亮部分被腐蚀,类似于“领域被吞噬”。
膨胀(dilation)
跟卷积操作类似,假设有图像A和结构元素B,结构元素B在A上面移动,其中B定义其中心为锚点,计算B覆盖下A的最大像素值来替换锚点的像素,其中B作为结构体可以是任意形状。
OpenCV提供的API:
void
dilate(InputArray src,
OutputArray dst,
InputArray kernel,//第三个参数表示膨胀操作的核,当为NULL时,表示使用参考点位于中心3*3的核。
Point anchor=Point(-
1
,-
1
),
int
iterations=
1
,
int
borderType=BORDER_CONSTANT,
const
Scalar& borderValue=morphologyDefaultBorderValue())
一般在调用erode以及dilate函数之前,先定义一个Mat类型的变量来获得函数getStructuringElemen返回的指定形状和尺寸的结构元素(内核矩阵)。
Mat getStructuringElement(int shape,
Size ksize, //内核的尺寸
Point anchor=Point(-1,-1))//锚点的位置
第一个参数表示内核的形状,有如下三种形状可以选择.
- 矩形:MORPH_RECT;
- 交叉形:MORPH_CROSS;
- 椭圆形:MORPH_ELLIPSE;
处理结果:
腐蚀
(erode)
腐蚀操作和膨胀操作的过程类似,唯一不同的是一最小值替换锚点重叠下图像的像素值。
OpenCV提供的API:
void erode(InputArray src,
OutputArray dst,
InputArray kernel,
Point anchor=Point(-1,-1),
int iterations=1,
int borderType=BORDER_CONSTANT,
const Scalar& borderValue=morphologyDefaultBorderValue())
处理结果:
很好的消除了小白点的干扰,但是主体还是有损失。
创建滑动条:
滑动条
依附于窗口(window)
而存在。
它往往会和一个
回调函数配合
起来用。
函数原型:
int createTrackbar(
conststring& trackbarname,//轨迹条的名字
conststring& winname,//依附在哪一个窗口
int* value,//表示滑块的初始位置,当操作滑动条的时候,值小于初始值的话,程序中断。
int count,//表示滑块可以达到的最大值
TrackbarCallback onChange=0,//指定回调函数,在轨迹改变的时候会自动调用这个回调函数
void* userdata=0);
步骤:
1、声明相关变量。(这里我定义的是全局变量)
int dilateval = 1;
int dilatecst = 20;
2、定义一个回调函数,这个回调函数
在轨迹改变的时候会自动调用
。
void CallBack_Demo_Dilate(int, void*) //回调函数的格式
{
//获取自定义核
Size ksize(dilateval, dilateval);
Mat element = getStructuringElement(MORPH_RECT, ksize);//获取内核的形状
//膨胀
dilate(src, DilatePic, element);
imshow("膨胀后", DilatePic);
}
3、创建滚动条,并依附于相应窗口,代码写在一起好看一点。
namedWindow(
"膨胀后",WINDOW_AUTOSIZE);
createTrackbar("dilate-size:", "膨胀后",&dilateval, dilatecst,CallBack_Demo_Dilate);
CallBack_Demo_Dilate(0, 0); //先执行一次代码让图像在程序Run时就显示。
开操作
先腐蚀后膨胀
在上面的腐蚀操作中,使用腐蚀虽然使白(亮)点消失,但是也使主体损失,这时我们可以再使用膨胀进行弥补。这一过程就是开操作。开操作是一般使对象的轮廓变得光滑,断开狭窄的间断和消除细的突出物。
OpenCV提供的API:
void morphologyEx(
InputArray src,
OutputArray dst,
int op,
InputArray kernel,
Point anchor=Point(-1,-1),
int iterations=1,
int borderType=BORDER_CONSTANT,
const Scalar& borderValue=morphologyDefaultBorderValue())
/*
部分参数介绍:
op:选择使用什么形态学操作
MORPH_OPEN - 开操作
MORPH_CLOSE - 闭操作
MORPH_GRADIENT - 形态学梯度
MORPH_TOPHAT - 顶帽
MORPH_BLACKHAT - 黑帽
MORPH_HITMISS - 击中与击不中
*/
处理结果:
很好的消除了噪点,也使主体损失减小。
闭操作
先膨胀后腐蚀
闭操作可使轮廓线更光滑,但与开操作相反的是,闭操作通常消弥狭窄的间断和长细的鸿沟,消除小的空洞,并填补轮廓线中的断裂。
代码几乎相同,只是改动一个参数(op)罢了。
处理结果:
形态学梯度
1、基本梯度
基本梯度是用膨胀后的图像减去腐蚀后的图像得到差值图像,称为梯度图像也是OpenCV中支持的计算形态学梯度的方法,而此方法得到梯度有被称为基本梯度。
2、内部梯度
是用原图像减去腐蚀之后的图像得到差值图像,称为图像的内部梯度
3、外部梯度
图像膨胀之后再减去原来的图像得到的差值图像,称为图像的外部梯度。
4、方向梯度
方向梯度是使用X方向与Y方向的直线作为结构元素之后得到图像梯度,X的结构元素分布膨胀与腐蚀得到图像之后求差值得到称为X方向梯度,用Y方向直线做结构分别膨胀与腐蚀之后得到图像求差值之后称为Y方向梯度。
顶帽
顶帽是原图像与开操作之间的差值图像。
处理结果:
黑帽
黑帽是闭操作减去原图像
处理结果:
提取水平与垂直线(形态学操作应用)
图像形态学操作的时候,可以通过自定义的结构元素实现结构元素对输入图像一些对象敏感,另外一些对象不敏感,这样就会让敏感的对象改变而不敏感的对象保留输出。通过使用两个最基本的形态学操作–膨胀与腐蚀,使用不同的结构元素实现对输入图像的操作,得到想要的结果。
操作过程:
- 1、读取原图;
- 2、转为灰度图像;
- 3、转为二值图像;(像逻辑电路一样,要么0要么1的图像)
- 采用自适应二值化adaptiveThreshold(属于阈值操作部分)
_src 要二值化的灰度图
_dst 二值化后的图
maxValue 二值化后要设置的那个值
method 块计算的方法(ADAPTIVE_THRESH_MEAN_C 平均值,ADAPTIVE_THRESH_GAUSSIAN_C 高斯分布加权和)
type 二值化类型(CV_THRESH_BINARY 大于为最大值,CV_THRESH_BINARY_INV 小于为最大值)
blockSize 块大小(奇数,大于1)
delta 差值(负值也可以)
- 4、定义结构元素;
- 5、开操作。
实例代码:
void
GetLine(){
Mat src,grayImg,binImg,dest;
//①、读取图片
src
=
imread(
"line.png"
);
if
(
!
src
.data
){
cout
<<
"图片打开失败!"
<<
endl;
return
;
}
namedWindow(
"原图像"
,CV_WINDOW_AUTOSIZE);
imshow(
"原图像"
,src);
//②、转为灰度图
cvtColor(src,grayImg,CV_RGB2GRAY);
namedWindow(
"灰度图"
,CV_WINDOW_AUTOSIZE);
imshow(
"灰度图"
,grayImg);
//③、转为二值图像
adaptiveThreshold(~grayImg,binImg,
255
,ADAPTIVE_THRESH_MEAN_C,THRESH_BINARY ,
15
,
-
2
);
namedWindow(
"二值图像"
,CV_WINDOW_AUTOSIZE);
imshow(
"二值图像"
,binImg);
//④、定义结构元素
Mat kernel
=
getStructuringElement(MORPH_RECT,Size(
1
,
20
));
//⑤、开操作
morphologyEx(binImg,dest,MORPH_OPEN,kernel);
namedWindow(
"最终结果"
,CV_WINDOW_AUTOSIZE);
imshow(
"最终结果"
,~dest);//图像的取反操作
cvWaitKey();
}
提取结果:
去除横线,提取字母:
实例代码:
void
GetChar(){
Mat src,grayImg,binImg,dest;
//①、读取图片
src
=
imread(
"char.png"
);
if
(
!
src
.data
){
cout
<<
"图片打开失败!"
<<
endl;
return
;
}
namedWindow(
"原图像"
,CV_WINDOW_AUTOSIZE);
imshow(
"原图像"
,src);
//②、转为灰度图
cvtColor(src,grayImg,CV_RGB2GRAY);
namedWindow(
"灰度图"
,CV_WINDOW_AUTOSIZE);
imshow(
"灰度图"
,grayImg);
//③、转为二值图像
/*
void adaptiveThreshold(InputArray src, OutputArray dst, double maxValue, int adaptiveMethod, int thresholdType, int blockSize, double C)
*/
adaptiveThreshold(~grayImg,binImg,
255
,ADAPTIVE_THRESH_MEAN_C,THRESH_BINARY ,
15
,
-
2
);
namedWindow(
"二值图像"
,CV_WINDOW_AUTOSIZE);
imshow(
"二值图像"
,binImg);
//④、定义结构元素
Mat kernel
=
getStructuringElement(MORPH_RECT,Size(
3
,
3
));
//⑤、开操作
morphologyEx(binImg,dest,MORPH_OPEN,kernel);
namedWindow(
"最终结果"
,CV_WINDOW_AUTOSIZE);
imshow(
"最终结果"
,~dest);
cvWaitKey();
}
处理结果:
代码示例如下:
#include <opencv2/opencv.hpp>
using namespace std; using namespace cv; void CallBack_Demo_Erode(int, void*); void CallBack_Demo_Dilate(int, void*); //全局变量 Mat src = imread("helloworld.png"); Mat DilatePic = Mat(src.size(), src.type()); int dilateval = 1; int dilatecst = 20; Mat ErodePic = Mat(src.size(), src.type()); int erodeval = 1; int erodecst = 20; int main(int argc, char** argv) { if (!src.data) { printf("could not find picture .../n"); return -1; } namedWindow("原图像",WINDOW_AUTOSIZE); namedWindow("膨胀后",WINDOW_AUTOSIZE); createTrackbar("dilate-size:", "膨胀后",&dilateval, dilatecst,CallBack_Demo_Dilate); namedWindow("腐蚀后",WINDOW_AUTOSIZE); createTrackbar("erode-size:", "腐蚀后",&erodeval,erodecst,CallBack_Demo_Erode); //这个只是让回调函数执行一次把图像显示出来而已,可以不要。 CallBack_Demo_Dilate(0, 0); CallBack_Demo_Erode(0, 0); imshow("原图像", src); waitKey(0); return 0; } void CallBack_Demo_Dilate(int, void*) { //获取自定义核 Size ksize(dilateval, dilateval); Mat element = getStructuringElement(MORPH_RECT, ksize); //膨胀 dilate(src, DilatePic, element); imshow("膨胀后", DilatePic); } void CallBack_Demo_Erode(int, void*) { //获取自定义核 Size ksize(erodeval, erodeval); Mat element = getStructuringElement(MORPH_RECT, ksize); //腐蚀 erode(src, ErodePic, element); imshow("腐蚀后", ErodePic); }