膨胀与腐蚀是数学形态学在图像处理中最基础的操作。其卷积操作非常简单,对于图像的每个像素,取其一定的邻域,计算最大值/最小值作为新图像对应像素位置的像素值。其中,取最大值就是膨胀,取最小值就是腐蚀。膨胀与腐蚀能实现多种多样的功能,主要如下:
- 消除噪声
- 分割出独立的图像元素,在图像中连接相邻的元素。
- 寻找图像中的明显的极大值区域或极小值区域
- 求出图像的梯度
- 图像梯度计算的是图像变化的速度
形态学概述
形态学一词通常表示生物学的一个分支,该分支主要研究动植物的形态和结构。而我们图像处理中指的形态学,往往表示的是数学形态学。下面一起来了解数学形态学的概念。数学形态学是一门建立在格论和拓扑学基础之上的图像分析学科,是数学形态学图像处理的基本理论。其基本的运算包括:二值腐蚀和膨胀、二值开闭运算、骨架抽取、极限腐蚀、击中击不中变换、形态学梯度、Top-hat变换、颗粒分析、流域变换、灰值腐蚀和膨胀、灰值开闭运算、灰值形态学梯度等。简单来讲,形态学操作就是基于形状的一系列图像处理操作。opencv为进行图像的形态学变换提供了快捷、方便的函数。最基本的形态学操作有二种,他们是:膨胀与腐蚀
注意:在做图像的形态学处理的时候,需要对图像做二值化,且需要将处理的像素值改为255,因为图像的形态学处理操作都是基于白色像素上处理的
膨胀
膨胀操作是取每个位置领域内最大值,所以膨胀后输出图像的总体亮度的平均值比起原图会有所升高,图像中比较亮的区域的面积会变大,而较暗物体的尺寸会减小甚至消失。(增加高亮部分)
void dilate( InputArray src, OutputArray dst, InputArray kernel,Point anchor = Point(-1,-1), int iterations = 1,int borderType = BORDER_CONSTANT,const Scalar& borderValue = morphologyDefaultBorderValue() );
/*******************************************************************
* src: 输入图像
* dst: 输出图像
* kernel: 膨胀操作的核
* 参数为0:中心3*3的核
* anchor: 锚点
* (-1,-1):表示位于中心
* iterations: 迭代的次数
* borderType: 图像外部像素的某种边界模式
* borderValue: 边界值,一般不管
*********************************************************************/
腐蚀
膨胀和腐蚀是一对好基友,是相反的一对操作,所以腐蚀就是求局部最小值的操作。(减少高亮部分)
void erode( InputArray src, OutputArray dst, InputArray kernel,Point anchor = Point(-1,-1), int iterations = 1,int borderType = BORDER_CONSTANT,const Scalar& borderValue = morphologyDefaultBorderValue() );
/*******************************************************************
* src: 输入图像
* dst: 输出图像
* kernel: 腐蚀操作的核
* 参数为0:中心3*3的核
* anchor: 锚点
* (-1,-1):表示位于中心
* iterations: 迭代的次数
* borderType: 断图像外部像素的某种边界模式
* borderValue: 边界值,一般不管
*********************************************************************/
核操作
getStructuringElement函数可用于构造一个特定大小和形状的结构元素,用于图像形态学处理
综合代码
#include <iostream>
#include <string>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
class Img
{
public:
Img() :img(imread("text.jpg"))
{
result["原图"] = img;
}
void Show()
{
for (auto& v : result)
{
imshow(v.first, v.second);
}
waitKey(0);
}
void Testdilate()
{
Mat kernel = getStructuringElement(MORPH_RECT, Size(5, 5));
dilate(img, result["膨胀"], kernel);
}
void Testerode()
{
Mat kernel = getStructuringElement(MORPH_RECT, Size(5, 5));
erode(img, result["腐蚀"], kernel);
}
private:
Mat img;
map<string, Mat> result;
};
int main()
{
Img* pimg = new Img;
pimg->Testdilate();
pimg->Testerode();
pimg->Show();
return 0;
}
OpenCV图像开和闭运算
形态学处理
处理类型 | 操作 | 作用 |
开运算 | 先腐蚀再膨胀 | 可在纤细点出分离物体。有助于消除噪音 |
闭运算 | 先膨胀后腐蚀 | 用于排除前景对象中的小孔或对象上的小黑点 |
形态学梯度 | 膨胀图与腐蚀图之差 | 用于保留目标物体的边缘轮廓 |
顶帽 | 原图与开运算图之差 | 分离比邻近点亮的斑块,用于突出原图像中比周围亮的区域 |
黑帽 | 闭运算图与原图差 | 分离比邻近点暗的斑块,突出原图像中比周围暗的区域 |
形态学处理往往是在阈值处理之后才进行的,因为当图像被转化为二值图以后,这个时候只有黑白两者颜色,然后再进行形态学处理是效果最好的。
操作函数
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() );
/*******************************************************************
* src: 输入图像
* dst: 输出图像
* op: 操作类型
* kernel: 操作的核
* 参数为0:中心3*3的核
* anchor: 锚点
* (-1,-1):表示位于中心
* iterations: 迭代的次数
* borderType: 图像外部像素的某种边界模式
* borderValue: 边界值,一般不管
*********************************************************************/
//操作类型
enum MorphTypes{
MORPH_ERODE = 0, //腐蚀
MORPH_DILATE = 1, //膨胀
MORPH_OPEN = 2, //开操作
MORPH_CLOSE = 3, //闭操作
MORPH_GRADIENT = 4, //梯度操作
MORPH_TOPHAT = 5, //顶帽操作
MORPH_BLACKHAT = 6, //黑帽操作
MORPH_HITMISS = 7 //随机操作,只支持CV_8UC1模式
}
核操作
getStructuringElement函数可用于构造一个特定大小和形状的结构元素,用于图像形态学处理
Mat getStructuringElement(int shape, Size ksize, Point anchor = Point(-1,-1));
/*******************************************************************
* shape: 形状类型
* ksize: 大小
* anchor: 锚点
* (-1,-1):表示位于中心
*********************************************************************/
//shape取值
enum MorphShapes {
MORPH_RECT = 0, //矩形
MORPH_CROSS = 1, //十字交叉
MORPH_ELLIPSE = 2 //椭圆
};
综合代码
#include <iostream>
#include <map>
#include <string>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
class Morphology
{
public:
Morphology() :img(imread("20.jpg"))
{
result["原图"] = img;
}
void Show()
{
for (auto& v : result)
{
imshow(v.first, v.second);
}
waitKey(0);
}
void TestMorphology()
{
Mat kernel = getStructuringElement(MORPH_RECT, Size(5, 5));
morphologyEx(img, result["腐蚀"], MORPH_ERODE, kernel);
morphologyEx(img, result["膨胀"], MORPH_DILATE, kernel);
morphologyEx(img, result["开"], MORPH_OPEN, kernel);
morphologyEx(img, result["闭"], MORPH_CLOSE, kernel);
morphologyEx(img, result["梯度"], MORPH_GRADIENT, kernel);
morphologyEx(img, result["顶帽"], MORPH_TOPHAT, kernel);
morphologyEx(img, result["黑帽"], MORPH_BLACKHAT, kernel);
}
private:
Mat img;
map<string, Mat> result;
};
int main()
{
Morphology* pmorph = new Morphology;
pmorph->TestMorphology();
pmorph->Show();
return 0;
}