文章目录
- 形态学概念
- 膨胀
- 使用膨胀操作来修复裂痕
- 示例代码
- 关键解析:
- 腐蚀
- 使用腐蚀操作消除噪点
- 示例代码:
- 开运算—先腐蚀后膨胀
- 闭运算—先膨胀后腐蚀
形态学概念
首先看这两张图片
一张图周围有大大小小的噪音和彩点,另一张图片中字母有间隙,这种效果影响了图片的质量,该如何处理图片,提高质量?
这就是形态学操作发挥作用的地方,形态学(Morphology)是图像处理中的一种技术,主要用于分析和处理图像中的结构和形状。形态学操作基于图像的形状和结构,而不是像素的具体值。它通常应用于二值图像(黑白图像),但也可以用于灰度图像。形态学操作在许多图像处理任务中发挥着重要作用,如去噪声、分割、边缘检测等。
膨胀
膨胀操作可以理解为将图像中的前景对象扩展。其基本原理是用一个结构元素扫描图像,如果结构元素至少有一个与前景(白色)部分重叠,则图像中的中心元素被设置为前景。
与卷积类似,也有一个矩阵来扫描整张图片,比如下方这个3*3矩阵,
$$
1 & 1 & 1
1 & 1 & 1
1 & 1 & 1
$$
当这个矩阵在图片扫描的时候,如果矩阵的任何元素遇到图像的像素值“1”。则与内核中心元素重叠的像素将转换为“1”。如下图所示
整个扫描的过程动态如下:会将橙色部分进行扩张,这就是膨胀的过程。
使用膨胀操作来修复裂痕
示例代码
//形态学操作 膨胀 腐蚀 ,开操作, 闭操作
void morphology_op_demo(Mat &image){
Mat mask_image;
threshold(image,mask_image,150,255,THRESH_BINARY_INV); //图像进行二值化
// 创建结构元素 扫描核
int kernel_size =3;
cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT,
cv::Size(2 * kernel_size + 1, 2 * kernel_size + 1),
cv::Point(-1, -1));
// 执行膨胀操作
cv::Mat eroded_image;
cv::dilate(mask_image, eroded_image, element,Point(-1,-1),1);
// 显示结果图像
cv::imshow("Original Image", mask_image);
cv::imshow("Eroded Image", eroded_image);
waitKey();
}
关键解析:
threshold(image,mask_image,150,255,THRESH_BINARY); //图像进行二值化
对图像进行二值化,让图像非黑即白,当然膨胀操作也可以用于彩色图像,但是会有一个问题,中心点像素会累加,会提高原来图片的亮度,扫描核越大,会导致图片越亮,直到白色看不见为止。
cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT,
cv::Size(2 * erosion_size + 1, 2 * erosion_size + 1),
cv::Point(-1, -1));
创建扫描矩阵,矩阵可以任意大小,这里使用奇数n*n的矩阵 Point 取(-1,-1)代表使用中心点像素,扫描矩阵的size越大,膨胀效果越强。
cv::dilate(mask_image, eroded_image, element,Point(-1,-1),1);
膨胀操作API ,函数原型
CV_EXPORTS_W void dilate( InputArray src, OutputArray dst, InputArray kernel,
Point anchor = Point(-1,-1), int iterations = 1,
int borderType = BORDER_CONSTANT,
const Scalar& borderValue = morphologyDefaultBorderValue() );
一般只关心前三个参数即可,输入,输出,扫描核(扫描的矩阵),Point 取(-1,-1)代表使用中心点像素,这里迭代次数为1次,同理 迭代次数越多,膨胀效果越强
效果如图:
扫描核size为 3:有效果,但仍然有裂痕
扫描核 size 为 5:效果增强,边缘变粗,基本上添上空隙
腐蚀
与膨胀相反,删除元素,它腐蚀图像的方式就像水侵蚀河岸一样。在腐蚀操作中,它将结构元素从输入图像的左向右和从上到下滑动。如果结构元素内的所有像素都大于 0,则保留原始像素值。否则,像素设置为 0。腐蚀用于去除被视为噪声的小斑点。
同样的使用膨胀操作的扫描核 3*3 全为1 的矩阵
$$
1 & 1 & 1
1 & 1 & 1
1 & 1 & 1
$$
此内核遍历图像的每个像素。如果与内核重叠的所有像素恰好是“1”,则不会发生任何更改。但是,如果任何重叠的像素恰好为“0”,则与内核的 中心 元素重叠的像素将设置为“0”。
腐蚀操作可视化图:
随着迭代次数的增加,图像像素点慢慢被腐蚀。因此,如果您需要提取粗体且周围有很多噪点的时候,可以通过侵蚀图像来消除噪点。
使用腐蚀操作消除噪点
示例代码:
//形态学操作 膨胀 腐蚀 ,开操作, 闭操作
void morphology_op_demo(Mat &image){
Mat mask_image;
threshold(image,mask_image,150,255,THRESH_BINARY_INV); //图像进行二值化
// 创建结构元素 扫描核
int kernel_size =1;
cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT,
cv::Size(2 * kernel_size + 1, 2 * kernel_size + 1),
cv::Point(-1, -1));
// 执行腐蚀操作
cv::Mat eroded_image;
cv::erode(mask_image, eroded_image, element,Point(-1,-1),1);
// 显示结果图像
cv::imshow("Original Image", mask_image);
cv::imshow("Eroded Image", eroded_image);
waitKey();
}
int main()
{
string imagePath = "C:\\Users\\Marxist\\Pictures\\coco\\eroding_test.jpg";
string mix_image_path = "C:\\Users\\Marxist\\Pictures\\coco\\Linux.jpg";
Mat image = imread(imagePath,IMREAD_GRAYSCALE);//读取灰度图像
morphology_op_demo(image);
return 0;
}
关键解析:
cv::erode(mask_image, eroded_image, element,Point(-1,-1),1);
函数原型:
CV_EXPORTS_W void erode( InputArray src, OutputArray dst, InputArray kernel,
Point anchor = Point(-1,-1), int iterations = 1,
int borderType = BORDER_CONSTANT,
const Scalar& borderValue = morphologyDefaultBorderValue() );
与膨胀API参数一致,但效果相反
腐蚀操作—内核大小3*3 迭代1次,噪点明显消除
腐蚀操作—内核大小5*5 迭代1次,噪点完全消除,但是图像细节也跟着丢失了
面对图像丢失的情况,可以进行开闭运算了
开运算—先腐蚀后膨胀
开运算是先进行腐蚀操作,再进行膨胀操作。它主要用于去除小的噪点,并保持前景物体的整体形状。
关键函数:
morphologyEx(mask_image, opening_image, MORPH_OPEN, element);
本质上就是调用了腐蚀API和膨胀API,OpenCV为了代码简洁,将二个API 合成了一个
示例代码
//形态学操作 膨胀 腐蚀 ,开操作, 闭操作
void morphology_op_demo(Mat &image){
Mat mask_image;
threshold(image,mask_image,150,255,THRESH_BINARY_INV); //图像进行二值化
// 创建结构元素 扫描核
int kernel_size =2;
cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT,
cv::Size(2 * kernel_size + 1, 2 * kernel_size + 1),
cv::Point(-1, -1));
// 执行腐蚀操作
cv::Mat eroded_image;
cv::erode(mask_image, eroded_image, element,Point(-1,-1),1);
Mat final;
// 先执行 腐蚀 然后执行膨胀
morphologyEx(mask_image, final, MORPH_OPEN, element);
cv::imshow("Original Image", mask_image);
cv::imshow("Eroded Image", eroded_image);
cv::imshow("final Image", final);
waitKey();
}
int main()
{
string imagePath = "C:\\Users\\Marxist\\Pictures\\coco\\eroding_test.jpg";
string mix_image_path = "C:\\Users\\Marxist\\Pictures\\coco\\Linux.jpg";
Mat image = imread(imagePath,IMREAD_GRAYSCALE);//读取灰度图像
morphology_op_demo(image);
return 0;
}
开运算效果:对比腐蚀过后的图像,进行稍微膨胀 补充连接细节
闭运算—先膨胀后腐蚀
//形态学操作 膨胀 腐蚀 ,开操作, 闭操作
void morphology_op_demo(Mat &image){
Mat mask_image;
threshold(image,mask_image,150,255,THRESH_BINARY_INV); //图像进行二值化
// 创建结构元素 扫描核
int kernel_size =2;
cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT,
cv::Size(2 * kernel_size + 1, 2 * kernel_size + 1),
cv::Point(-1, -1));
// 执行膨胀操作
cv::Mat eroded_image;
cv::dilate(mask_image, eroded_image, element,Point(-1,-1),1);
Mat final;
// 先执行 腐蚀 然后执行膨胀
morphologyEx(mask_image, final, MORPH_CLOSE, element);
cv::imshow("Original Image", mask_image);
cv::imshow("Eroded Image", eroded_image);
cv::imshow("final Image", final);
waitKey();
}
int main()
{
string imagePath = "C:\\Users\\Marxist\\Pictures\\coco\\dilation_test.jpg";
string mix_image_path = "C:\\Users\\Marxist\\Pictures\\coco\\Linux.jpg";
Mat image = imread(imagePath,IMREAD_GRAYSCALE);//读取灰度图像
morphology_op_demo(image);
return 0;
}
闭运算与开运算相反,先膨胀后腐蚀,它主要用于填补前景物体中的小孔和连接断开的物体。
闭运算效果:对比膨胀后的图像,边缘稍微细了些