图像的形态学操作(Morphological Operations)是一种基于图像形状的处理方法,通常用于二值图像的分析和处理。形态学操作通过对图像中各个区域的结构进行改变或分析,来提取或增强图像中的形态特征(如边缘、物体、空洞等)。
这些操作在许多计算机视觉任务中非常常见,例如噪声去除、边缘检测、图像分割、物体识别等。它们主要基于图像的几何形状进行分析,通过设置形态学核(通常是小的二值结构元素)来操作图像。
常见的形态学操作有以下几种:
膨胀:膨胀操作会使图像中的白色区域变大,黑色区域缩小。它将卷积核(通常是一个小的矩形或圆形区域)放置在图像的每个像素上,然后对应位置相乘,并选取所得结果中的最小值。
腐蚀:腐蚀操作与膨胀相反,它会使图像中的白色区域缩小,黑色区域变大。它会将所得结果的最小值作为输出。
假设有一个简单的二值图像,其中前景是白色,背景是黑色,卷积核为3*3的全1矩阵。
原图:
1 1 1 1 1
1 0 0 0 1
1 0 0 0 1
1 1 1 1 1
腐蚀:
腐蚀后:
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
膨胀:
膨胀后:
1 1 1 1 1
1 1 1 1 1
1 1 1 1 1
1 1 1 1 1
在进行图像腐蚀等操作前,我们得先生成结构元素。
Mat cv::getStructuringElement(int shape, //生成结构元素的种类,比如MORPH_CROSS
cv::Size ksize, //尺寸,一般有3*3,5*5,7*7
cv::Point anchor = cv::Point(-1,-1) //中心的位置,默认为几何中心
)
其中shape可以为:
cv::MORPH_ELLIPSE
:圆形结构元素cv::MORPH_CROSS
:十字形结构元素cv::MORPH_RECT
:矩形结构元素结构元素决定了操作时如何对每个像素的邻域进行处理,进而影响图像处理的形态(即结果的边界形状)。
例如:
总结一下,矩形结构元素会让边缘保持直线形状(矩形或方形)。圆形结构元素会让边缘变得圆滑,适合对不规则形状的物体进行处理。十字形结构元素则在水平和垂直方向上扩展边界,适用于对有明确水平或垂直边缘的图像进行处理。
cv::dilate()
函数与cv::erode()
函数cv::dilate()
函数cv::dilate()
函数用于执行膨胀操作(dilation),其函数原型为:
void cv::dilate(
InputArray src, // 输入图像(通常是二值图像)
OutputArray dst, // 输出图像
InputArray kernel, // 结构元素(卷积核)
Point anchor = Point(-1, -1), // 锚点,默认为结构元素的中心
int iterations = 1, // 膨胀操作的迭代次数
int borderType = BORDER_CONSTANT, // 边界类型,默认为常数边界
const Scalar& borderValue = Scalar() // 边界值
);
cv::erode()
函数cv::erode()
函数用于执行腐蚀操作(erosion),其函数原型为:
void cv::erode(
InputArray src, // 输入图像(通常是二值图像)
OutputArray dst, // 输出图像
InputArray kernel, // 结构元素(卷积核)
Point anchor = Point(-1, -1), // 锚点,默认为结构元素的中心
int iterations = 1, // 腐蚀操作的迭代次数,默认为1
int borderType = BORDER_CONSTANT, // 边界类型,默认为常数边界
const Scalar& borderValue = Scalar() // 边界值
);
iterations
参数控制了膨胀和腐蚀操作的次数。迭代次数越多,效果越显著。
多次腐蚀:会使物体边界收缩得更厉害,甚至完全去除细小物体。
多次膨胀:会使物体边界扩展得更大,填补更多的空洞,连接更多的区域。
#include
#include
int main() {
// 读取输入图像(假设是二值图像)
cv::Mat inputImage = cv::imread("binary_image.png", cv::IMREAD_GRAYSCALE);
// 检查图像是否成功读取
if (inputImage.empty()) {
std::cerr << "Image not loaded!" << std::endl;
return -1;
}
// 创建一个3x3的矩形结构元素
cv::Mat kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3));
// 执行膨胀操作
cv::Mat dilatedImage;
cv::dilate(inputImage, dilatedImage, kernel, cv::Point(-1, -1), 1); // 迭代次数为1
// 显示原图和膨胀后的图像
cv::imshow("Original Image", inputImage);
cv::imshow("Dilated Image", dilatedImage);
cv::waitKey(0);
return 0;
}
#include
#include
int main() {
// 读取输入图像(假设是二值图像)
cv::Mat inputImage = cv::imread("binary_image.png", cv::IMREAD_GRAYSCALE);
// 检查图像是否成功读取
if (inputImage.empty()) {
std::cerr << "Image not loaded!" << std::endl;
return -1;
}
// 创建一个3x3的矩形结构元素
cv::Mat kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3));
// 执行腐蚀操作
cv::Mat erodedImage;
cv::erode(inputImage, erodedImage, kernel, cv::Point(-1, -1), 1); // 迭代次数为1
// 显示原图和腐蚀后的图像
cv::imshow("Original Image", inputImage);
cv::imshow("Eroded Image", erodedImage);
cv::waitKey(0);
return 0;
}
开运算(Opening)和闭运算(Closing)是形态学图像处理中的两种重要操作,它们都是基于膨胀和腐蚀操作的组合。这两种运算能够有效地去除噪声、平滑物体的边界、填补小的空洞等。开运算和闭运算在许多图像处理任务中被广泛使用,尤其是在二值图像的处理和分析中。
morphologyEx()
函数在介绍开运算和闭运算之前,首先我们要介绍一下morphologyEx()
函数,因为在OpenCV中开闭运算通过此函数实现。而且,上面提到的腐蚀和膨胀操作也可以通过这个函数完成。
void morphologyEx(
InputArray src,
OutputArray dst,
int op,
InputArray kernel,
Point anchor = Point(-1, -1),
int iterations = 1,
BorderTypes borderType = BORDER_CONSTANT,
const Scalar& borderValue = morphologyDefaultBorderValue()
);
src
: 输入图像,必须是单通道的图像,通常为灰度图。它是需要应用形态学操作的源图像。dst
: 输出图像,即形态学操作后的结果。op
: 操作类型。这个参数指定了执行哪种形态学操作,它是一个整数值。OpenCV 提供了几种不同的操作类型,通过以下常量来指定:
MORPH_ERODE
:腐蚀操作(erosion)MORPH_DILATE
:膨胀操作(dilation)MORPH_OPEN
:开运算(opening),先腐蚀再膨胀MORPH_CLOSE
:闭运算(closing),先膨胀再腐蚀MORPH_GRADIENT
:形态学梯度(morphological gradient),膨胀和腐蚀的差MORPH_TOPHAT
:顶帽运算(top hat),原图减去开运算结果MORPH_BLACKHAT
:黑帽运算(black hat),闭运算结果减去原图kernel
: 结构元素(kernel),和上文介绍的结构元素一样:
MORPH_RECT
:矩形结构元素MORPH_ELLIPSE
:椭圆形结构元素MORPH_CROSS
:十字形结构元素anchor
: 锚点位置,指定结构元素的中心点,默认值为 Point(-1, -1)
,表示结构元素的中心点自动设置为结构元素的几何中心。iterations
: 迭代次数,指定形态学操作执行的次数。通常,我们只需要执行一次操作,但有时需要多次迭代来得到期望的效果。borderType
: 边界类型,定义图像边界外的区域如何处理。常用的边界类型有:
BORDER_CONSTANT
:常量边界(默认值)BORDER_REFLECT
:边界反射BORDER_REPLICATE
:边界复制BORDER_WRAP
:边界环绕BORDER_REFLECT_101
:反射类型 101BORDER_TRANSPARENT
:透明边界borderValue
: 边界值,指定边界类型时,设置常量边界的值。默认使用 morphologyDefaultBorderValue()
开运算是腐蚀和膨胀操作的组合。具体来说,开运算先对图像进行腐蚀操作,再进行膨胀操作。开运算可以用来去除小的噪点,并平滑物体的边界。
这种操作可以去除图像中的小的白色噪点或小的连接区域,同时保留物体的主要结构。
#include
#include
int main() {
// 读取输入图像(假设是二值图像)
cv::Mat inputImage = cv::imread("binary_image.png", cv::IMREAD_GRAYSCALE);
// 检查图像是否成功读取
if (inputImage.empty()) {
std::cerr << "Image not loaded!" << std::endl;
return -1;
}
// 创建一个3x3的矩形结构元素
cv::Mat kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3));
// 执行开运算(腐蚀 + 膨胀)
cv::Mat openedImage;
cv::morphologyEx(inputImage, openedImage, cv::MORPH_OPEN, kernel);
// 显示原图和开运算后的图像
cv::imshow("Original Image", inputImage);
cv::imshow("Opened Image", openedImage);
cv::waitKey(0);
return 0;
}
闭运算是膨胀和腐蚀操作的组合。具体来说,闭运算先对图像进行膨胀操作,再进行腐蚀操作。闭运算可以用来填补物体中的小孔洞、连接邻近的物体,并平滑物体的边界。
闭运算通过膨胀填补空洞,通过腐蚀恢复边界的细节,常用于连接分离的物体或填补小的缺失区域。
#include
#include
int main() {
// 读取输入图像(假设是二值图像)
cv::Mat inputImage = cv::imread("binary_image.png", cv::IMREAD_GRAYSCALE);
// 检查图像是否成功读取
if (inputImage.empty()) {
std::cerr << "Image not loaded!" << std::endl;
return -1;
}
// 创建一个3x3的矩形结构元素
cv::Mat kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3));
// 执行闭运算(膨胀 + 腐蚀)
cv::Mat closedImage;
cv::morphologyEx(inputImage, closedImage, cv::MORPH_CLOSE, kernel);
// 显示原图和闭运算后的图像
cv::imshow("Original Image", inputImage);
cv::imshow("Closed Image", closedImage);
cv::waitKey(0);
return 0;
}
特性 | 开运算 | 闭运算 |
---|---|---|
操作顺序 | 先腐蚀,再膨胀 | 先膨胀,再腐蚀 |
作用 | 去除小的白色噪点,平滑物体的边界 | 填补小的黑色空洞,连接物体 |
主要应用 | 去噪、物体分割、平滑边界 | 填补小孔洞、物体连接、平滑边界 |
影响效果 | 物体边界收缩,细节被消除 | 物体边界扩展,细节被连接 |
顶帽运算(Top Hat)和黑帽运算(Black Hat)是形态学操作中的两种常见运算,它们都是通过开运算和闭运算的差值来处理图像的。它们能够帮助提取图像中某些特定区域的细节,常用于图像的特征提取、背景建模和物体检测等任务。
顶帽运算是原图像与其开运算结果的差值。开运算(Opening)是先腐蚀后膨胀,它通常用于去除小的噪声或细小的物体。顶帽运算则帮助我们提取出原图像中比背景亮的区域,也就是去除掉了背景后剩下的细节部分。
TopHat ( I ) = I − Opening ( I ) \text{TopHat}(I) = I - \text{Opening}(I) TopHat(I)=I−Opening(I)
I
是原图像。黑帽运算是闭运算结果与原图像的差值。闭运算(Closing)是先膨胀后腐蚀,它通常用于填补图像中的小黑洞或噪声。黑帽运算则帮助我们提取出原图像中比背景暗的区域,也就是去除掉物体后的背景部分。
BlackHat ( I ) = Closing ( I ) − I \text{BlackHat}(I) = \text{Closing}(I) - I BlackHat(I)=Closing(I)−I
其中:
I
是原图像。#include
#include
using namespace cv;
using namespace std;
int main()
{
// 1. 读取图像
Mat image = imread("example.jpg", IMREAD_GRAYSCALE); // 以灰度模式读取图像
if (image.empty()) {
cout << "Could not open or find the image!" << endl;
return -1;
}
// 2. 显示原始图像
imshow("Original Image", image);
waitKey(0); // 等待按键事件
// 3. 创建结构元素(Kernel)
Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3)); // 使用3x3矩形结构元素
// 4. 顶帽运算(原图像 - 开运算结果)
Mat tophat;
morphologyEx(image, tophat, MORPH_TOPHAT, kernel);
// 5. 黑帽运算(闭运算结果 - 原图像)
Mat blackhat;
morphologyEx(image, blackhat, MORPH_BLACKHAT, kernel);
// 6. 显示结果
imshow("Top Hat Image", tophat); // 显示顶帽运算结果
imshow("Black Hat Image", blackhat); // 显示黑帽运算结果
waitKey(0); // 等待按键事件
// 释放窗口
destroyAllWindows();
return 0;
}
相信读者也能看出来,morphologyEx()
是一个非常灵活的函数。它可以执行各种形态学操作,帮助我们处理图像中的形状、边界、噪声等。通过调整结构元素的大小和形状,morphologyEx()
可以应用于许多不同的图像处理任务,如去噪、物体分割、边缘检测、细节提取等。