# OpenCV 图像预处理—形态学:膨胀、腐蚀、开运算、闭运算 原理详解

devtools/2024/9/20 4:01:24/ 标签: opencv, 计算机视觉, 人工智能

文章目录

  • 形态学概念
  • 膨胀
    • 使用膨胀操作来修复裂痕
      • 示例代码
      • 关键解析:
  • 腐蚀
    • 使用腐蚀操作消除噪点
      • 示例代码:
  • 开运算—先腐蚀后膨胀
  • 闭运算—先膨胀后腐蚀

形态学概念

首先看这两张图片

在这里插入图片描述

一张图周围有大大小小的噪音和彩点,另一张图片中字母有间隙,这种效果影响了图片的质量,该如何处理图片,提高质量?

这就是形态学操作发挥作用的地方,形态学(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;
}

闭运算与开运算相反,先膨胀后腐蚀,它主要用于填补前景物体中的小孔和连接断开的物体。

闭运算效果:对比膨胀后的图像,边缘稍微细了些

在这里插入图片描述


http://www.ppmy.cn/devtools/85025.html

相关文章

Vue自定义指令与Vue插槽学习

文章目录 自定义指令1.指令介绍2.自定义指令3.自定义指令语法4.指令中的配置项 自定义指令-指令的值1.使用效果2.语法 插槽-默认插槽1.作用2.用处4.插槽的基本语法 插槽-具名插槽1.作用2.具名插槽语法3.v-slot的简写 插槽总结1.插槽分类2.作用3.场景4.使用步骤 自定义指令 1.指…

hcip学习 多实例生成树,VRRP工作原理

一、STP 和 RSTP 解决了什么问题 1、STP:解决了在冗余的二层网络中所出现的环路问题 2、RSTP:在 STP 的基础上,解决了 STP 收敛速度慢的问题,引入了一些 STP 保护机制,使其网络更加稳定 二、MSTP 针对 RSTP 的改进 …

进程概念(三)----- fork 初识

目录 前言1. pid && ppid2. forka. 为什么 fork 要给子进程返回 0, 给父进程返回子进程的 pid ?b. 一个函数是如何做到两次的?c. fork 函数在干什么?d. 一个变量怎么做到拥有不同的内容的?e. 拓展:…

Python爬虫技术 第15节 CSS选择器基础

在使用Python进行网页爬取时,CSS选择器是提取HTML文档中特定元素的常用方法之一。CSS选择器基于HTML元素的结构和属性来定位和选择页面中的元素。结合Python中的BeautifulSoup库或PyQuery库等,可以非常高效地解析和筛选出你想要的数据。 CSS选择器基础 …

MYSQL 七、mysql 日志与备份

一、其他数据库日志 千万不要小看日志。很多看似奇怪的问题,答案往往就藏在日志里。很多情况下,只有通过查看日志才 能发现问题的原因,真正解决问题。所以,一定要学会查看日志,养成检查日志的习惯,对提升你…

Android笔试面试题AI答之控件Views(6)

答案来着文心一言,仅供参考 目录 1.简述什么是RemoteViews?使用场景有哪些?RemoteViews的特性使用场景总结 2.获取View宽高的几种方法?1. 在onWindowFocusChanged方法中获取2. 使用ViewTreeObserver.OnGlobalLayoutListener3. 使用ViewTreeObserver.OnPreDrawLi…

QT:控件样式设置误区

当我设置不同控件格式样式,原先的代码如下 //设置MainWindow的背景R颜色this->setStyleSheet("QMainWindow{background-color:#F5F8FD;}");//设置菜单栏字体和背景颜色this->setStyleSheet("QMenuBar{color:#FFFFFF;background-color:#2A579A;…

浅聊一下编程!

在数字时代的浪潮中,编程已成为连接现实与虚拟世界的桥梁,它不仅是技术创新的基石,更是推动社会进步的强大动力。编程,这一看似深奥而复杂的技能,实则蕴含着无尽的创造力和可能性。本文将深入探讨编程的魅力所在&#…

C++ —— STL简介

1. 什么是STL STL(standard template libaray-标准模板库):是C标准库的重要组成部分,不仅是一个可复用的 组件库,而且是一个包罗数据结构与算法的软件框架 2.STL的版本 原始版本 Alexander Stepanov、Meng Lee 在惠普实验室完成的原始版本…

Golang | Leetcode Golang题解之第287题寻找重复数

题目: 题解: func findDuplicate(nums []int) int {slow, fast : 0, 0for slow, fast nums[slow], nums[nums[fast]]; slow ! fast; slow, fast nums[slow], nums[nums[fast]] { }slow 0for slow ! fast {slow nums[slow]fast nums[fast]}return s…

概率论三大分布

目录 基本概念 卡方分布(χ分布): t分布: F分布: 延伸 卡方分布在哪些具体情况下最适合用于数据分析? t分布在大样本情况下的表现与正态分布相比如何? F分布在进行方差比较时与t分布的区…

springSecurity学习之springSecurity web如何取得用户信息

web如何取得用户信息 之前说过SecurityContextHolder默认使用的是ThreadLocal来进行存储的,而且每次都会清除,但是web每次请求都会验证用户权限,这是如何做到的呢? 这是通过SecurityContextPersistenceFilter来实现的&#xff0…

Spring Boot入门指南:留言板

一.留言板 1.输⼊留⾔信息,点击提交.后端把数据存储起来. 2.⻚⾯展⽰输⼊的表⽩墙的信息 规范: 1.写一个类MessageInfo对象,添加构造方法 虽然有快捷键,但是还是不够偷懒 项目添加Lombok。 Lombok是⼀个Java⼯具库,通过添加注…

ElasticSearch(四)— 数据检索与查询

一、基本查询语法 所有的 REST 搜索请求使用_search 接口,既可以是 GET 请求,也可以是 POST请求,也可以通过在搜索 URL 中指定索引来限制范围。 _search 接口有两种请求方法,一种是基于 URI 的请求方式,另一种是基于…

IEC104转MQTT网关轻松将IEC104设备数据传输到Zabbix、阿里云、华为云、亚马逊AWS、ThingsBoard、Ignition云平台

随着工业4.0的深入发展和物联网技术的广泛应用,IEC 104(IEC 60870-5-104)作为电力系统中的重要通信协议,正逐步与各种现代监控、管理和云平台实现深度融合。IEC104转MQTT网关BE113作为这一融合过程中的关键设备,其能够…

构建坚不可摧的Memcached:高可用性策略全解析

🛡️ 构建坚不可摧的Memcached:高可用性策略全解析 Memcached是一个广泛使用的高性能分布式内存缓存系统,它通过减少对数据库的访问来加速数据检索。然而,为了确保服务的连续性和数据的可靠性,高可用性设计成为Memcac…

第13周 简历职位功能开发与Zookeeper实战

第13周 简历职位功能开发与Zookeeper实战 本章概述1. Mysql8窗口函数over使用1.1 演示表结构与数据1.2 案例1:获取男女总分数1.3 案例2****************************************************************************************本章概述 1. Mysql8窗口函数over使用 参考案例…

yarn 和 npm 的区别

yarn 和 npm 有以下区别: 工作原理: npm(Node Package Manager) 是 JavaScript 的包管理器,是随 Node.js 自带的工具。它通过访问 Node.js 包注册表来管理软件包的依赖关系和版本。yarn 也是 JavaScript 的包管理器&a…

力扣第三十题——串联所有单词的子串

内容介绍 给定一个字符串 s 和一个字符串数组 words。 words 中所有字符串 长度相同。 s 中的 串联子串 是指一个包含 words 中所有字符串以任意顺序排列连接起来的子串。 例如,如果 words ["ab","cd","ef"], 那么 &quo…

谷粒商城实战笔记-54-商品服务-API-三级分类-拖拽效果

文章目录 一,54-商品服务-API-三级分类-修改-拖拽效果1,el-tree控件加上允许拖拽的属性2,是否允许拖拽3,完整代码 一,54-商品服务-API-三级分类-修改-拖拽效果 本节的主要内容是给三级分类树形结构加上拖拽功能&#…