opencv之图像预处理(三)

opencv之图像预处理(三)


一、图像掩膜

1.1 制作掩膜

掩膜(Mask)是一种在图像处理中常见的操作,它用于选择性地遮挡图像的某些部分,以实现特定任务的目标。掩膜通常是一个二值化图像,并且与原图像的大小相同,其中目标区域被设置为1(或白色),而其他区域被设置为0(或黑色),并且目标区域可以根据HSV的颜色范围进行修改,如下图就是制作红色掩膜的过程:

在这里插入图片描述
通过这个掩膜,我们就可以对掩膜中的白色区域所对应的原图中的区域进行处理与操作。

  • mask=cv.inRange(img,color_low,color_high)
    
    • cv2.inRange用于进行多通道图像(尤其是彩色图像)的阈值操作。

1.2 与运算

我们在高中时学过逻辑运算中的“与”运算,其规则是当两个命题都是真时,其结果才为真。而在图像处理中,“与”运算被用来对图像的像素值进行操作。具体来说,就是将两个图像中所有的对应像素值一一进行“与”运算,从而得到新的图像。从上面的图片我们可以看出,掩膜中有很多地方是黑色的,其像素值为0,那么在与原图像进行“与”运算的时候,得到的新图像的对应位置也是黑色的,如下图所示:
在这里插入图片描述
通过掩膜与原图的与运算,我们就可以提取出图像中被掩膜覆盖的区域(扣图)。

  • cv2.bitwise_and(src1,src2[,mask])
    
    • src1:第一个输入数组。通常是输入的原始图像。
    • src2:第二个输入数组。它可以是另一个图像、一个常数值或者与 src1 相同的图像。
      • 当应用掩膜时,这个参数经常就是src1本身;即对同一个图像进行操作。
      • 如果对两个不同的图像执行按位与操作(例如,将两张图片的某些部分组合在一起),可以分别将它们作为 src1src2 输入到 cv2.bitwise_and() 函数中,创建复杂的图像效果或进行图像合成。
    • mask:掩膜(可选)。输入数组元素只有在该掩膜非零时才被处理。是一个8位单通道的数组,尺寸必须与src1src2相同。
    • 返回值:输出数组,应用掩膜后的图像,与输入数组大小和类型相同。

1.3 颜色替换

前一个实验中,我们已经能够识别到图像中的某一种颜色,那么我们就可以对识别到的颜色进行一个操作,比如将其替换成别的颜色,其原理就是在得到原图的掩膜之后,对掩膜中的白色区域所对应的原图中的区域进行一个像素值的修改即可。

1.4 案例

import cv2 as cv
import numpy as np

img = cv.imread("../demo.png")
img = cv.resize(img,(400,400))
# 颜色空间转为HSV
hsv = cv.cvtColor(img,cv.COLOR_BGR2HSV)
# 创建掩模:cv2.inRange(img,(h,s,v)min,(h,s,v)max)
color_low = np.array([23,46,46])
color_high = np.array([34,255,255])
# 创建掩模,大小和原图一致
mask_yellow = cv.inRange(hsv,color_low,color_high)
# 与运算:把原图和掩膜进行运算
color_img = cv.bitwise_and(img,img,mask=mask_yellow)
# 替换颜色
"""
mask_yellow==255生成一个布尔数组,大小与掩膜一致,等于255的地方为True
筛选出来的赋值为B,G,R(0,255,0)
"""
img[mask_yellow==255] = (0,255,0)


cv.imshow("color_img",color_img)
cv.imshow("mask",mask_yellow)
cv.imshow("img",img)
cv.imshow("hsv",hsv)
cv.waitKey(0)
cv.destroyAllWindows()

在这里插入图片描述

二、 ROI切割

ROI:Region of Interest,翻译过来就是感兴趣的区域。什么意思呢?比如对于一个人的照片,假如我们要检测眼睛,因为眼睛肯定在脸上,所以我们感兴趣的只有脸这部分,其他都不care,所以可以单独把脸截取出来,这样就可以大大节省计算量,提高运行速度。
案例

import cv2 as cv
import numpy as np

cat = cv.imread('../cat1.png')
cat = cv.resize(cat,(480,480))
roi = cat[200:360,200:360]
# roi[:] = (0,0,255)
cv.imshow('cat',cat)
cv.imshow('roi',roi)
cv.waitKey(0)
cv.destroyAllWindows()

在这里插入图片描述

三、 图像添加水印

本实验中添加水印的概念其实可以理解为将一张图片中的某个物体或者图案提取出来,然后叠加到另一张图片上。具体的操作思想是通过将原始图片转换成灰度图,并进行二值化处理,去除背景部分,得到一个类似掩膜的图像。然后将这个二值化图像与另一张图片中要添加水印的区域进行“与”运算,使得目标物体的形状出现在要添加水印的区域。最后,将得到的目标物体图像与要添加水印的区域进行相加,就完成了添加水印的操作。这样可以实现将一个图像中的某个物体或图案叠加到另一个图像上,从而实现添加水印的效果。就本实验而言,会用到两个新的组件,一个是模板输入,一个是图像融合。

3.1 模板输入

其实,该组件起到的就是图片输入的功能,只不过使用模板输入所输入的图片其实是作为要添加的水印,有了水印的彩色图之后,我们需要用它来制作一个掩模,这就用到了灰度化和二值化,即先灰度化后二值化,这就得到了带有水印图案的掩模。

在这里插入图片描述

3.2 与运算

有了模板的掩膜之后(也就是二值化图),我们就可以在要添加水印的图像中,根据掩膜的大小切割出一个ROI区域,也就是我们要添加水印的区域,之后让其与模板的掩膜进行与运算,我们知道,与运算的过程中,只要有黑色像素,那么得到的结果图中的对应位置也会是黑色像素。由于模板的掩膜中目标物体的像素值为黑色,所以经过与运算后,就会在ROI区域中得到模板图的形状。

3.3 图像融合(图像位与操作)

将模板的形状添加到水印区域之后,就可以将该图像与原始的模板图进行图像融合。该组件的目的就是将图像对应的数组中的对应元素进行相加(一定要注意这里的两个数组是规格相同的,也就是说要么都是灰度图,要么都是彩图),其过程如下图所示。

在这里插入图片描述

3.4 案例

import cv2 as cv
import numpy as np

bj = cv.imread("../bg.png")
logo = cv.imread("../cat1.png")
logo = cv.resize(logo,(200,200))
# 获取logo的宽高
w, h = logo.shape[:2]
# 从背景中切割出和logo一样的区域
bj_roi = bj[0:w, 0:h]
# 将logo转灰度
logo_gray = cv.cvtColor(logo, cv.COLOR_BGR2GRAY)
# 创建掩膜:0为背景,255为前景:白色logo,目的是获取到logo的颜色,有logo没背景
mask_whitelogo = cv.threshold(logo_gray, 170, 255, cv.THRESH_BINARY_INV)[1]
# 与运算,提取logo
fg1 = cv.bitwise_and(logo,logo,mask = mask_whitelogo)
# 创建掩膜:255为背景,0为前景:黑色logo,目的是获取到logo的背景,有背景没logo
mask_blacklogo = cv.threshold(logo_gray, 170, 255, cv.THRESH_BINARY)[1]
# 与运算,提取背景
fg2 = cv.bitwise_and(bj_roi,bj_roi,mask = mask_blacklogo)
# 图像融合
# roi[:] = fg1 + fg2
bj_roi[:] = cv.add(fg1,fg2)
# 让融合后的图像赋值给切割出来的区域



cv.imshow("logo",logo)
cv.imshow("bj",bj)
cv.imshow("bj_roi",bj_roi)
cv.imshow("fg1",fg1)
cv.imshow("fg2",fg2)
cv.waitKey(0)
cv.destroyAllWindows()

在这里插入图片描述
在这里插入图片描述

四、图像噪点消除

噪声:指图像中的一些干扰因素,通常是由图像采集设备、传输信道等因素造成的,表现为图像中随机的亮度,也可以理解为有那么一些点的像素值与周围的像素值格格不入。常见的噪声类型包括高斯噪声和椒盐噪声。高斯噪声是一种分布符合正态分布的噪声,会使图像变得模糊或有噪点。椒盐噪声则是一些黑白色的像素值分布在原图像中。

在这里插入图片描述

滤波器:也可以叫做卷积核,与自适应二值化中的核一样,本身是一个小的区域,有着特定的核值,并且工作原理也是在原图上进行滑动并计算中心像素点的像素值。滤波器可分为线性滤波和非线性滤波,线性滤波对邻域中的像素进行线性运算,如在核的范围内进行加权求和,常见的线性滤波器有均值滤波、高斯滤波等。非线性滤波则是利用原始图像与模板之间的一种逻辑关系得到结果,常见的非线性滤波器中有中值滤波器、双边滤波器等。

滤波与模糊联系与区别:

  • 它们都属于卷积,不同滤波方法之间只是卷积核不同(对线性滤波而言)
  • 低通滤波器是模糊,高通滤波器是锐化
  • 低通滤波器就是允许低频信号通过,在图像中边缘和噪点都相当于高频部分,所以低通滤波器用于去除噪点、平滑和模糊图像。高通滤波器则反之,用来增强图像边缘,进行锐化处理。

注意:椒盐噪声可以理解为斑点,随机出现在图像中的黑点或白点;高斯噪声可以理解为拍摄图片时由于光照等原因造成的噪声。

4.1 均值滤波

均值滤波是一种最简单的滤波处理,它取的是卷积核区域内元素的均值,如3×3的卷积核:
k e r n e l = 1 9 [ 1 1 1 1 1 1 1 1 1 ] k e r n e l={\frac{1}{9}}{\Bigg[}\begin{array}{l l l}{1}&{1}&{1}\\{1}&{1}&{1}\\{1}&{1}&{1}\end{array}{\Bigg]} kernel=91[111111111]
在滤波算法组件中,当参数filtering_method选为均值滤波,参数component_param为ksize,代表卷积核的大小,eg:ksize=3,则代表使用3×3的卷积核。

  • 对于边界的像素点,则会进行边界填充,以确保卷积核的中心能够对准边界的像素点进行滤波操作。在OpenCV中,默认的是使用BORDER_REFLECT_101的方式进行填充,下面的滤波方法中除了中值滤波使用的是BORDER_REPLICATE进行填充之外,其他默认也是使用这个方式进行填充,因此下面就不再赘述。通过卷积核在原图上从左上角滑动计算到右下角,从而得到新的4*4的图像的像素值。

案例

img = cv.imread("../lvbo2.png")
# 均值滤波cv2.blur(src, ksize)
img_blur = cv.blur(img, (3,3))
cv.imshow("blur", img_blur)
cv.imshow("img", img)
cv.waitKey(0)
cv.destroyAllWindows()

在这里插入图片描述

4.2 方框滤波

方框滤波跟均值滤波很像,如3×3的滤波核如下:
k e r n e l = a [ 1 1 1 1 1 1 1 1 1 ] k e r n e l={a}{\Bigg[}\begin{array}{l l l}{1}&{1}&{1}\\{1}&{1}&{1}\\{1}&{1}&{1}\end{array}{\Bigg]} kernel=a[111111111]
在滤波算法组件中,当参数filtering_method选为方框滤波时,参数component_param为ksize,ddepth,normalize。下面讲解这3个参数的含义:

案例

import cv2 as cv
import numpy as np

img = cv.imread("../lvbo2.png")
img = cv.resize(img,(640,480))
# 方框滤波cv2.boxFilter(src, ddepth, ksize[, dst[, anchor[, normalize]]])
img_box1 = cv.boxFilter(img, -1, (3,3),normalize=True) # 相当于均值滤波
img_box2 = cv.boxFilter(img, -1, (3,3),normalize=False) # 把区域内的像素值全部相加

cv.imshow("img", img)
cv.imshow("img_box2",img_box2)
cv.waitKey(0)
cv.destroyAllWindows()

在这里插入图片描述

4.3 高斯滤波

  • 高斯滤波是一种常用的图像处理技术,主要用于平滑图像、去除噪声。它通过使用高斯函数(正态分布)作为卷积核来对图像进行模糊处理。

前面两种滤波方式,卷积核内的每个值都一样,也就是说图像区域中每个像素的权重也就一样。高斯滤波的卷积核权重并不相同:中间像素点权重最高,越远离中心的像素权重越小。还记得我们在自适应二值化里是怎么生成高斯核的吗?这里跟自适应二值化里生成高斯核的步骤是一样的,都是以核的中心位置为坐标原点,然后计算周围点的坐标,然后带入下面的高斯公式中。
g ( x , y ) = 1 2 π σ 2 e − ( x 2 + y 2 ) 2 σ 2 g(x,y)=\frac{1}{2\pi\sigma^{2}}e^{-\frac{(x^{2}+y^{2})}{2\sigma^{2}}} g(x,y)=2πσ21e2σ2(x2+y2)
其中,x和 y 是相对于中心点的坐标偏移量,σ 是标准差,控制着高斯函数的宽度和高度。较大的 σ 值会导致更广泛的平滑效果。

卷积核通常是一个方形矩阵,其元素值根据高斯函数计算得出,并且这些值加起来等于1,近似于正态分布,以确保输出图像的亮度保持不变。

其中的值也是与自适应二值化里的一样,当时会取固定的系数,当kernel大于7并且没有设置时,会使用固定的公式进行计算 σ \sigma σ的值:
σ = 0.3 ∗ ( ( k s i z e − 1 ) ∗ 0.5 − 1 ) + 0.8 \sigma=0.3*\left((k s i z e-1)*0.5-1\right)+0.8 σ=0.3((ksize1)0.51)+0.8
我们还是以3*3的卷积核为例,其核值如下所示:
  k e r n e l = [ 0.0625      0.125      0.0625 0.125      0.25      0.125 0.0625      0.125      0.0625 ] = [ 1 16     1 8     1 16 1 8     1 4     1 8 1 16     1 8     1 16 ] \ k e r n e l=\left[\begin{array}{c}{{0.0625~~~~0.125~~~~0.0625}}\\{{0.125~~~~0.25~~~~0.125}}\\{{0.0625~~~~0.125~~~~0.0625}} \end{array}\right]=\left[\begin{array}{c c c}{\frac{1}{16}~~~\frac{1}{8}~~~\frac{1}{16}}\\{\frac{1}{8}~~~\frac{1}{4}~~~\frac{1}{8}}\\{\frac{1}{16}~~~\frac{1}{8}~~~\frac{1}{16}}\end{array}\right]  kernel= 0.0625    0.125    0.06250.125    0.25    0.1250.0625    0.125    0.0625 = 161   81   16181   41   81161   81   161
得到了卷积核的核值之后,其滤波过程与上面两种滤波方式的滤波过程一样,都是用卷积核从图像左上角开始,逐个计算对应位置的像素值,并从左至右、从上至下滑动卷积核,直至到达图像右下角,唯一的区别就是核值不同。

案例

import cv2 as cv
import numpy as np

img = cv.imread("../lvbo2.png")
img = cv.resize(img,(640,480))
# 高斯滤波cv2.GaussianBlur(src, ksize, sigmaX, dst=None, sigmaY=None, borderType=None)
gaussian_blur = cv.GaussianBlur(img, (3, 3), 0)
cv.imshow("gaussian_blur", gaussian_blur)
cv.waitKey(0)
cv.destroyAllWindows()

在这里插入图片描述

4.4 中值滤波

中值又叫中位数,是所有数排序后取中间的值。中值滤波没有核值,而是在原图中从左上角开始,将卷积核区域内的像素值进行排序,并选取中值作为卷积核的中点的像素值
中值滤波就是用区域内的中值来代替本像素值,所以那种孤立的斑点,如0或255很容易消除掉,适用于去除椒盐噪声和斑点噪声。中值是一种非线性操作,效率相比前面几种线性滤波要慢。
比如下面这张斑点噪声图,用中值滤波显然更好:
在这里插入图片描述

案例

import cv2 as cv
import numpy as np

img = cv.imread("../lvbo2.png")
img = cv.resize(img,(640,480))
img_jy = cv.imread("../lvbo3.png")
img_jy = cv.resize(img_jy,(640,480))
# 中值滤波 cv2.medianBlur(src, ksize)
median = cv.medianBlur(img, 3)
median_jy = cv.medianBlur(img_jy, 3)
cv.imshow("median", median)
cv.imshow("median_jy", median_jy)
cv.imshow("img_jy", img_jy)
cv.waitKey(0)
cv.destroyAllWindows()

在这里插入图片描述

4.5 双边滤波

模糊操作基本都会损失掉图像细节信息,尤其前面介绍的线性滤波器,图像的边缘信息很难保留下来。然而,边缘(edge)信息是图像中很重要的一个特征,所以这才有了双边滤波。

在这里插入图片描述
可以看到,双边滤波明显保留了更多边缘信息,下面来介绍一下双边滤波。

双边滤波的基本思路是同时考虑将要被滤波的像素点的空域信息(周围像素点的位置的权重)和值域信息(周围像素点的像素值的权重)。为什么要添加值域信息呢?是因为假设图像在空间中是缓慢变化的话,那么临近的像素点会更相近,但是这个假设在图像的边缘处会不成立,因为图像的边缘处的像素点必不会相近。因此在边缘处如果只是使用空域信息来进行滤波的话,得到的结果必然是边缘被模糊了,这样我们就丢掉了边缘信息,因此添加了值域信息。

案例

import cv2 as cv
import numpy as np

img = cv.imread("../lvbo2.png")
img = cv.resize(img,(640,480))
# 双边滤波cv2.bilateralFilter(src, d, sigmaColor, sigmaSpace[, dst[, borderType]])
img_bilateral = cv.bilateralFilter(img, 9, 75, 75)
cv.imshow("img_bilateral", img_bilateral)
cv.waitKey(0)
cv.destroyAllWindows()

在这里插入图片描述

4.6 小结

在不知道用什么滤波器好的时候,优先高斯滤波,然后均值滤波。

斑点和椒盐噪声优先使用中值滤波。

要去除噪点的同时尽可能保留更多的边缘信息,使用双边滤波。

线性滤波方式:均值滤波、方框滤波、高斯滤波(速度相对快)。

非线性滤波方式:中值滤波、双边滤波(速度相对慢)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值