OpenCV中的图像处理3.4-3.6(四)平滑化、形态学、图像梯度

news2024/11/23 10:06:30

目录

  • 3.4 平滑化图像
      • 目标
      • 二维卷积 ( 图像滤波 )
      • 图像模糊(图像平滑)
      • 其他资源
  • 3.5 形态学转换
      • 目标
      • 理论
      • 结构化元素
  • 3.6 图像梯度
      • 目标
      • 理论
      • 代码
      • 一个重要的问题!

翻译及二次校对:cvtutorials.com

编辑者:廿瓶鲸(和鲸社区Siby团队成员)

3.4 平滑化图像

目标

学习:

1.用各种低通滤波器模糊图像
2.在图像上应用定制的滤波器(二维卷积)

二维卷积 ( 图像滤波 )

与一维信号一样,图像也可以用各种低通滤波器(LPF)、高通滤波器(HPF)等进行过滤。LPF有助于去除噪音、模糊图像等。HPF滤波器有助于寻找图像的边缘。

OpenCV提供了一个函数cv.filter2D()来将一个核与图像进行融合。作为一个例子,我们将在一个图像上尝试一个平均滤波器。一个5x5的平均滤波核看起来就像下面这样。
K = 1 25 [ 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 ] K = \frac{1}{25} \begin{bmatrix} 1 & 1 & 1 & 1 & 1 \\ 1 & 1 & 1 & 1 & 1 \\ 1 & 1 & 1 & 1 & 1 \\ 1 & 1 & 1 & 1 & 1 \\ 1 & 1 & 1 & 1 & 1 \end{bmatrix} K=251 1111111111111111111111111

这个操作是这样的:在一个像素上面保持这个核,把这个核下面的所有25个像素加起来,取平均值,然后用新的平均值替换中心像素。图像中的所有像素都施加这个操作。试试这段代码并检查结果。

import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('opencv_logo.png')
kernel = np.ones((5,5),np.float32)/25
dst = cv.filter2D(img,-1,kernel)
plt.subplot(121),plt.imshow(img),plt.title('Original')
plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(dst),plt.title('Averaging')
plt.xticks([]), plt.yticks([])
plt.show()

结果如下:

Image Name

图像模糊(图像平滑)

图像模糊是通过用低通滤波器核对图像进行卷积实现的。它对去除噪音很有用。它实际上是从图像中去除高频内容(如:噪声、边缘)。因此,在这个操作中,边缘会被模糊一些(也有一些模糊技术是不模糊边缘的)。OpenCV提供了四种主要的模糊技术。

1.均值模糊

这是通过用一个归一化的盒式滤波器对图像进行卷积来完成的。它只是取核区下所有像素的平均值,并替换中心元素。这是由函数cv.blur()或cv.boxFilter()完成的。查看文档以了解关于核的更多细节。我们应该指定核的宽度和高度。一个3x3的归一化盒式滤波器看起来就像下面这样。
K = 1 9 [ 1 1 1 1 1 1 1 1 1 ] K = \frac{1}{9} \begin{bmatrix} 1 & 1 & 1 \\ 1 & 1 & 1 \\ 1 & 1 & 1 \end{bmatrix} K=91 111111111

注意:如果你不想使用规范化的盒子过滤器,请使用cv.boxFilter()。向该函数传递一个参数normalize=False。

请看下面的样本演示,核大小为5x5。

import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
img = cv.imread('opencv-logo-white.png')
blur = cv.blur(img,(5,5))
plt.subplot(121),plt.imshow(img),plt.title('Original')
plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(blur),plt.title('Blurred')
plt.xticks([]), plt.yticks([])
plt.show()

结果如下:

Image Name

2.高斯模糊

在这个方法中,使用了高斯核而不是盒式滤波器。它是通过函数cv.GaussianBlur()完成的。我们应该指定核的宽度和高度,应该是正数和奇数。我们还应该指定X和Y方向的标准偏差,分别为sigmaX和sigmaY。如果只指定sigmaX,sigmaY将被视为与sigmaX相同。如果两者都是零,则根据核大小计算。高斯模糊对去除图像中的高斯噪声非常有效。

如果你愿意,你可以用函数cv.getGaussianKernel()创建一个高斯核。

上面的代码可以为高斯模糊进行修改。

blur = cv.GaussianBlur(img,(5,5),0)

结果如下:

Image Name

3.中值模糊

在这里,函数cv.medianBlur()取核区下所有像素的中值,中心元素被替换成这个中值。这对图像中的椒盐噪声非常有效。有趣的是,在上述过滤器中,中心元素是一个新的计算值,可能是图像中的一个像素值或一个新值。但在中值模糊中,中心元素总是被图像中的某个像素值所取代。它能有效地减少噪音。它的核大小应该是一个正奇数的整数。

在这个演示中,我给我们的原始图像添加了50%的噪声,并应用中值模糊。检查一下结果。

median = cv.medianBlur(img,5)

结果:

Image Name

4.双边滤波

cv.bilateralFilter()在去除噪声的同时保持边缘的清晰度方面非常有效。但与其他过滤器相比,其操作速度较慢。我们已经看到,高斯滤波器取像素周围的邻域并找到其高斯加权平均值。这个高斯滤波器是一个单独的空间函数,也就是说,在过滤时考虑附近的像素。它不考虑像素是否有几乎相同的灰度。它不考虑一个像素是否是一个边缘像素。因此,它也模糊了边缘,这是我们不希望看到的。

双边滤波也需要一个空间的高斯滤波,但多了一个高斯滤波,这是一个像素差异的函数。空间的高斯函数确保只有附近的像素被考虑用于模糊处理,而灰度差的高斯函数则确保只有那些与中心像素灰度相似的像素被考虑用于模糊处理。所以它保留了边缘,因为边缘的像素会有很大的灰度变化。

下面的例子显示了双边滤波器的使用(关于参数的细节,请访问文档)。

blur = cv.bilateralFilter(img,9,75,75)

结果:

Image Name

看,表面的纹理已经消失了,但边缘仍然保存着。

其他资源

  • 关于双边过滤的细节

3.5 形态学转换

目标

在本章中:

  • 我们将学习不同的形态学操作,如腐蚀、膨胀、开运算、闭运算等。
  • 我们将看到不同的函数,如:cv.erode(), cv.dilate(), cv.morphologyEx() 等。

理论

形态学变换是基于图像形状的一些简单操作。它通常是在二进制图像上进行的。它需要两个输入,一个是我们的原始图像,第二个被称为结构化元素或核,它决定了操作的性质。两个基本的形态学运算符是腐蚀和膨胀。然后,它的变体形式,如开运算、闭运算、梯度等也开始发挥作用。我们将在以下图片的帮助下逐一看到它们。

Image Name

1.腐蚀

腐蚀的基本概念就像土壤腐蚀一样,只是它腐蚀了前景物体的边界(总是尽量保持前景为白色)。那么它是怎么做的呢?核在图像中滑动(如二维卷积)。原始图像中的一个像素(1或0)只有在核下的所有像素都是1时才会被认为是1,否则就会被腐蚀(变成0)。

因此,发生的情况是,根据核的大小,所有靠近边界的像素将被丢弃。因此,前景物体的厚度或大小减少了,或者说是图像中的白色区域减少了。这对于去除小的白色噪音(正如我们在色彩空间一章中所看到的),分离两个相连的物体等很有用。

这里,作为一个例子,我将使用一个充满1的5x5核。让我们看看它是如何工作的。

import cv2 as cv
import numpy as np
img = cv.imread('j.png',0)
kernel = np.ones((5,5),np.uint8)
erosion = cv.erode(img,kernel,iterations = 1)

结果如下:

Image Name

2.膨胀

它与腐蚀刚好相反。这里,一个像素元素是 “1”,如果核心下至少有一个像素是 “1”。因此,它增加了图像中的白色区域或增加了前景物体的大小。通常情况下,在去除噪声等情况下,腐蚀之后是膨胀。因为,腐蚀可以去除白色噪音,但它也会缩小我们的物体。所以我们要膨胀它。由于噪音已经消失,它们不会再出现,但我们的对象面积会增加。它在连接一个物体的破碎部分时也很有用。

dilation = cv.dilate(img,kernel,iterations = 1)

结果如下:

Image Name

3.开运算

开运算只是腐蚀和膨胀的另一个名称。正如我们上面解释的那样,它在去除噪音方面很有用。在这里,我们使用函数cv.morphologyEx()

opening = cv.morphologyEx(img, cv.MORPH_OPEN, kernel)

结果如下:

Image Name

4.闭运算

闭运算是开运算的反面,膨胀之后是腐蚀。它在关闭前景物体内部的小孔,或物体上的小黑点时很有用。

closing = cv.morphologyEx(img, cv.MORPH_CLOSE, kernel)

结果如下:

Image Name

5.形态学梯度

它是图像的膨胀和腐蚀之间的区别。

其结果将看起来像物体的轮廓。

gradient = cv.morphologyEx(img, cv.MORPH_GRADIENT, kernel)

结果如下:

Image Name

6.高帽

它是输入图像和图像开运算之间的差异。下面的例子是针对一个9x9的核做的。

tophat = cv.morphologyEx(img, cv.MORPH_TOPHAT, kernel)

结果如下:

Image Name

7.黑帽

它是指输入图像和输入图像的闭运算度之差。

blackhat = cv.morphologyEx(img, cv.MORPH_BLACKHAT, kernel)

结果如下:

Image Name

结构化元素

在前面的例子中,我们在Numpy的帮助下手动创建了一个结构化元素:矩阵。但在某些情况下,你可能需要椭圆/圆形的核。因此,为了这个目的,OpenCV有一个函数,cv.getStructuringElement()。你可以通过核的形状和大小去定义一个核。

# Rectangular Kernel
>>> cv.getStructuringElement(cv.MORPH_RECT,(5,5))
array([[1, 1, 1, 1, 1],
       [1, 1, 1, 1, 1],
       [1, 1, 1, 1, 1],
       [1, 1, 1, 1, 1],
       [1, 1, 1, 1, 1]], dtype=uint8)
# Elliptical Kernel
>>> cv.getStructuringElement(cv.MORPH_ELLIPSE,(5,5))
array([[0, 0, 1, 0, 0],[0, 0, 1, 0, 0]], dtype=uint8)

其他资源

  • HIPR2的形态学操作

3.6 图像梯度

目标

在本章中,我们将学习:

  • 寻找图像梯度和边缘等
  • 我们将看到以下函数:cv.Sobel()、cv.Scharr()、cv.Laplacian()等。

理论

OpenCV提供了三种类型的梯度滤波器或高通滤波器,Sobel, Scharr和Laplacian。

1.Sobel和Scharr梯度

Sobel运算符是一种高斯平滑加微分的联合运算,所以它对噪声的抵抗力更强。你可以指定要取的导数的方向,垂直或水平(分别通过参数yorder和xorder)。你还可以通过参数ksize指定核的大小。如果ksize = -1,则使用3x3 Scharr滤波器,它比3x3 Sobel滤波器的结果更好。请看文档中核的用法。

2.拉普拉斯导数

它计算由以下关系式给出的图像拉普拉斯系数。结果如下:
Δ s r c = ∂ 2 s r c ∂ x 2 + ∂ 2 s r c ∂ y 2 \Delta src = \frac{\partial ^2{src}}{\partial x^2} + \frac{\partial ^2{src}}{\partial y^2} Δsrc=x22src+y22src

其中每个导数都是用Sobel导数找到的。如果ksize=1,则使用以下核进行过滤。
k e r n e l = [ 0 1 0 1 − 4 1 0 1 0 ] kernel = \begin{bmatrix} 0 & 1 & 0 \\ 1 & -4 & 1 \\ 0 & 1 & 0 \end{bmatrix} kernel= 010141010

代码

下面的代码在一个图中显示了所有的运算符。所有核都是5x5大小。输出图像的深度是通过-1来获得np.uint8类型的结果。

import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('dave.jpg',0)
laplacian = cv.Laplacian(img,cv.CV_64F)
sobelx = cv.Sobel(img,cv.CV_64F,1,0,ksize=5)
sobely = cv.Sobel(img,cv.CV_64F,0,1,ksize=5)
plt.subplot(2,2,1),plt.imshow(img,cmap = 'gray')
plt.title('Original'), plt.xticks([]), plt.yticks([])
plt.subplot(2,2,2),plt.imshow(laplacian,cmap = 'gray')
plt.title('Laplacian'), plt.xticks([]), plt.yticks([])
plt.subplot(2,2,3),plt.imshow(sobelx,cmap = 'gray')
plt.title('Sobel X'), plt.xticks([]), plt.yticks([])
plt.subplot(2,2,4),plt.imshow(sobely,cmap = 'gray')
plt.title('Sobel Y'), plt.xticks([]), plt.yticks([])
plt.show()

结果如下:

Image Name

一个重要的问题!

在我们最后一个例子中,输出数据类型是cv.CV_8U或np.uint8。但是这里面有一个小问题。黑到白的过渡被认为是正斜率(它有一个正值),而白到黑的过渡被认为是负斜率(它有负值)。所以当你把数据转换成np.uint8时,所有的负斜率都变成了0。简单地说,你会丢失这个边缘。

如果你想检测两个边缘,更好的选择是将输出数据类型保持为更高的形式,如cv.CV_16S,cv.CV_64F等,取其绝对值,然后转换回cv.CV_8U。下面的代码演示了水平Sobel滤波器的这个过程和结果的差异。

import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('box.png',0)
# Output dtype = cv.CV_8U
sobelx8u = cv.Sobel(img,cv.CV_8U,1,0,ksize=5)
# Output dtype = cv.CV_64F. Then take its absolute and convert to cv.CV_8U
sobelx64f = cv.Sobel(img,cv.CV_64F,1,0,ksize=5)
abs_sobel64f = np.absolute(sobelx64f)
sobel_8u = np.uint8(abs_sobel64f)
plt.subplot(1,3,1),plt.imshow(img,cmap = 'gray')
plt.title('Original'), plt.xticks([]), plt.yticks([])
plt.subplot(1,3,2),plt.imshow(sobelx8u,cmap = 'gray')
plt.title('Sobel CV_8U'), plt.xticks([]), plt.yticks([])
plt.subplot(1,3,3),plt.imshow(sobel_8u,cmap = 'gray')
plt.title('Sobel abs(CV_64F)'), plt.xticks([]), plt.yticks([])
plt.show()

结果如下:

Image Name

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/494883.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

用 Bitmap 实现亿级海量数据统计

在移动应用的业务场景中,我们需要保存这样的信息:一个 key 关联了一个数据集合。 常见的场景如下: 给一个 userId ,判断用户登陆状态; 显示用户某个月的签到次数和首次签到时间; 两亿用户最近 7 天的签到…

【C++:模块3-------数组】

C:模块3-------数组 数组概念:数组特点:数组访问方法: 一维数组:1.1三种定义形式:(和C语言中一样)1.2一维数组数组名的作用:(1) 统计数组在内存中…

日常的性能测试工作

日常的性能测试工作 (1)在日常的测试工作时如何进行性能测试 日常的性能测试主要是从业务功能点中抽取具有并发特点的,高风险的、大数据量处理的业务,整理成测试用例,制定相应的指标,然后用工具或者手工进行性能测试并分析&#x…

MySQL HeatWave 被添加了机器学习,甲骨文认真了

开头还是介绍一下群,如果感兴趣polardb ,mongodb ,mysql ,postgresql ,redis 等有问题,有需求都可以加群群内有各大数据库行业大咖,CTO,可以解决你的问题。加群请联系 liuaustin3 ,在新加的朋友会分到2群(共…

基于javaweb的学生就业管理系统

一、简介 学生基业管理系统有三个角色:管理员、企业、学生 对学生信息管理、企业信息管理、求职信息管理 后端架构:spring springmvc mybatis 前端架构:jsp layui 系统环境:jdk1.8 | maven | mysql 二、主要功能 1. 登录…

jvm之垃圾回收相关概念解读

目录 System.gc()的理解 内存溢出与内存泄露 内存溢出(OOM) 内存泄漏(Memory Leak) Stop The World 垃圾回收的并行与并发 程序中的并发(Concurrent) 程序中的并发(Parallel&#xff09…

揭秘镭速传输点对点传输技术,NAT+Raysync强强组合

点对点传输是一种文件即时传输方式用于实现数据的快速联动,为所有客户端提供资源,包括带宽、存储空间、计算能力。点对点传输技术有很多应用,包括共享各种格式音频、视频、数据等。 在5G重新定义带宽,信息技术不断发展的今天&…

Python:概念解析:基础语法

Python基础语法 1. 列表,元组,字典1.1 列表1.2. tuple元组1.3 字典dict 2. 数据类型推断2.1 在Python中有一个函数 isinstance(data,type) , 可以用来判断某个数据是什么类型。 3. Python 基本数据类型3.1 整型 int 本章节介绍Python的一些基础语法 1. 列…

《可穿戴式血压测量设备与高血压管理的新方法:数字时代》阅读笔记

目录 一、论文摘要 二、论文十问 Q1:论文试图解决什么问题? Q2:这是否是一个新的问题? Q3:这篇文章要验证一个什么科学假设? Q4:有哪些相关研究?如何归类?谁是这一课…

PySide6/PyQT多线程之 线程安全:互斥锁条件变量的最佳实践

前言 在PySide6/PyQT中使用多线程时,线程锁和线程安全是非常重要的概念。本文将介绍线程锁和线程安全的基本概念,以及如何在PySide6/PyQT中使用它们。 使用PySide6/PyQT开发GUI应用程序,在多个线程同时访问同一个共享对象时候,如果…

单词词义、词性、例句查询python代码

单词发音、词义、词性、例句查询、输出结果更简洁,一次可查多个单词 运行该代码,命令窗口输入单词,单词用“/”分开,例如:noisy/problem/community/neighbor 可以更多。先安装两个python包requests、 beautifulsoup4&…

Eureka详解

Eureka概述和架构 Eureka Spring Cloud Eureka 是Netflix 开发的注册发现组件,本身是一个基于 REST 的服务。提供注册与发现,同时还提供了负载均衡、故障转移等能力 Eureka3个角色 服务中心,服务提供者,服务消费者 Eureka Se…

Win11的两个实用技巧系列之蓝屏死循环解决办法、调高进程的优先级方法

Win11蓝屏死循环怎么办?Win11蓝屏死循环解决办法 有用户安装Win11系统的时候,重新启动电脑的时候,会一直进入蓝屏的错误循环中,本文就为大家带来详细的解决方法,需要的朋友一起看看吧 Win11蓝屏死循环解决办法分享。有用户安装W…

Ubuntu18.04系统及相关软件安装恢复过程

Ubuntu18.04系统及相关软件安装恢复过程 一、常用软件安装1. [系统安装](https://blog.csdn.net/qq_43309940/article/details/116656810)2. [显卡驱动安装](https://blog.csdn.net/qq_43309940/article/details/126898929)3. [ROS Melodic安装](https://ismango.blog.csdn.net…

[Python]爬虫基础——urllib库

urllib目录 一、简介二、发送请求1、urlopen()函数2、Request()函数 三、异常处理四、解析URL五、分析Robots协议 一、简介 urllib库是Python内置的标准库。包含以下四个模块: 1、request:模拟发送HTTP请求; 2、error:处理HTTP请…

实验六 UML建模工具应用

一、实验目的 1.掌握面向对象开发思想及实现机制 2.理解并掌握UML常用图(重点:类、对象图、用例图) 3.掌握并常见UML建模工具,并绘制UML各种图 二、实验准备与要求 1.StarUML(简称SU),是一种创建UML类图&#xff0c…

洛谷P8597 [蓝桥杯 2013 省 B] 翻硬币C语言/C++

[蓝桥杯 2013 省 B] 翻硬币 题目背景 小明正在玩一个“翻硬币”的游戏。 题目描述 桌上放着排成一排的若干硬币。我们用 * 表示正面,用 o 表示反面(是小写字母,不是零),比如可能情形是 **oo***oooo,如果…

ideaSSM医院挂号管理系统VS开发mysql数据库web结构java编程计算机网页源码maven项目

一、源码特点 SSM医院挂号管理系统是一套完善的完整医院类型系统,结合SSM框架和bootstrap完成本系统SpringMVC spring mybatis ,对理解JSP java编程开发语言有帮助系统采用SSM框架(MVC模式 开发),系统具有完整的源代…

leetcode 1143. 最长公共子序列

1. dp 数组的定义 下标: 以 i - 1 和 j - 1 为结尾的子序列 值:以 i - 1 和 j - 1 为结尾的最长公共子序列的长度 2. 递推公式 if(text1[i - 1] text2[j - 1]) // 相等 dp[i][j] dp[i - 1][j - 1] 1 ; elsedp[i][j] max(dp[i - 1][j],…

【问题记录】flask开发blog

文章目录 小知识点问题1. 文章标签显示错误2. 文章状态无法回显(open)3. 用户管理页面,图标无法显示4. BuildError5. 用户管理添加用户,使用重复的用户名会报错(open)6. 添加用户,不上传头像会报错(open)7. 部分标签删除时报错&am…