计算机视觉之旅-进阶-图像滤波处理

1.基本概念

1.1. 数字图像

图像处理的对象是数字图像,它是由像素点阵列表示的图像。需要了解像素、图像分辨率、灰度级、RBG等图像表示方法。

用numpy数组表示,每个元素为像素值。例如RGB图像

 import numpy as np
 img = np.array([[[255,0,0], [0,255,0]], [[0,0,255], [255,255,255]]]) 

1.2. 采样和量化

数字图像是通过采样和量化得到的,需要理解采样定理与频谱采样,和量化误差对图像质量的影响。

1.3. 图像操作

包括图像旋转、缩放、裁剪、变换等空间域操作,和灰度变换等强度域操作。这些操作会改变图像的 Geometry 或光学特性。

- 旋转:用旋转矩阵乘以像素坐标实现。逆时针90°

 M = np.array([[0, -1], [1, 0]]) 
 img_rot = np.zeros_like(img)
 h, w = img.shape[:2]
 for i in range(h):
     for j in range(w):
         img_rot[i, j] = img[int(M[0,0]*i+M[0,1]*j), int(M[1,0]*i+M[1,1]*j)]


- 缩放:用双线性插值实现。放大2倍
 

 h, w = img.shape[:2]
 img_scale = np.zeros((2*h, 2*w, 3))
 for i in range(2*h):
     for j in range(2*w):
         img_scale[i, j] = (1-a)*(1-b)*img[i/2, j/2] + a*(1-b)*img[i/2, j/2+1] + (1-a)*b*img[i/2+1, j/2] + a*b*img[i/2+1, j/2+1]
         a = i/2 - int(i/2)
         b = j/2 - int(j/2)

1.4. 图像滤波

用于图像平滑、去噪和边缘检测。常用滤波器有均值滤波器、高斯滤波器、中值滤波器、Sobel、Laplace 等。滤波通过卷积实现,需要理解卷积运算。

- 均值滤波: 

import cv2
import numpy as np

img = cv2.imread('noisy.png')
kernel = np.ones((5,5),np.float32)/25
dst = cv2.filter2D(img,-1,kernel)

cv2.imshow('src',img)
cv2.imshow('dst',dst)
cv2.waitKey(0)


- 高斯滤波: 

import cv2 
import numpy as np

img = cv2.imread('noisy.png')
kernel = cv2.getGaussianKernel(5,10)
dst = cv2.sepFilter2D(img, -1, kernel, kernel)

cv2.imshow('src',img)  
cv2.imshow('dst',dst)
cv2.waitKey(0)

- 中值滤波:

import cv2
import numpy as np

img = cv2.imread('noisy.png')  
dst = cv2.medianBlur(img, 5)  

cv2.imshow('src',img)  
cv2.imshow('dst',dst)
cv2.waitKey(0)

- 拉普拉斯滤波:

import cv2
import numpy as np

img = cv2.imread('noisy.png')
kernel = np.array([[0, -1, 0], [-1, 5, -1], [0, -1, 0]])
dst = cv2.filter2D(img, -1, kernel)

cv2.imshow('src',img)  
cv2.imshow('dst',dst)
cv2.waitKey(0)


- Sobel 边缘检测: 

import cv2
import numpy as np

img = cv2.imread('edge.png',0)

x = cv2.Sobel(img,cv2.CV_16S,1,0)
y = cv2.Sobel(img,cv2.CV_16S,0,1)

absX = cv2.convertScaleAbs(x)  
absY = cv2.convertScaleAbs(y)

dst = cv2.addWeighted(absX,0.5,absY,0.5,0)

cv2.imshow('src',img)
cv2.imshow('dst',dst)
cv2.waitKey(0)

1.5. 图像像素值变换

用于增强图像的整体对比度,包括灰度变换、直方图均衡化、伽马变换等。可以改善图像的视觉质量。

阈值化:将大于阈值的像素置最大值,小于阈值的像素置最小值,可以将图像转化为二值图像。

import cv2

img = cv2.imread('threshold.png',0)
ret,dst = cv2.threshold(img,127,255,cv2.THRESH_BINARY)

cv2.imshow('src',img)
cv2.imshow('dst',dst)
cv2.waitKey(0)


伽马变换:通过非线性变换来改变图像的对比度。γ<1可以增强对比度,γ>1可以减弱对比度。

import cv2
import numpy as np  

img = cv2.imread('gamma.png')
gamma=0.5
gamma_table = [np.power(x/255.0,gamma)*255.0 for x in range(256)]  
gamma_table = np.round(np.array(gamma_table)).astype(np.uint8)

dst = cv2.LUT(img,gamma_table)

cv2.imshow('src',img)
cv2.imshow('dst',dst) 
cv2.waitKey(0)


对数变换:也是一种非线性变换,可以对图像的暗部细节进行提高,压缩高亮部分的动态范围。

import cv2
import numpy as np

img = cv2.imread('log.png')
c = 255 / np.log(1 + np.max(img))
log_table = c * np.log(1 + img)
log_table = np.array(log_table,dtype=np.uint8)
dst = cv2.LUT(img,log_table)

cv2.imshow('src',img)
cv2.imshow('dst',dst)
cv2.waitKey(0) 


不同的像素值变换算法通过对图像的像素值进行变换,可以达到改变图像整体光照、对比度和细节等不同的效果。通过选择适当的算法或算法组合,我们可以有效地提高图像质量,为后续的图像分析和计算机视觉算法提供更清晰和信息丰富的输入。

1.6. 图像几何变换

用于图像的形变,包括平移变换、旋转变换、缩放变换等。这些变换没有改变像素值,只是改变位置或数量。

旋转变换:

import cv2

img = cv2.imread('rotate.png')
rows,cols = img.shape[:2]

# 旋转30°
M = cv2.getRotationMatrix2D((cols/2,rows/2),30,1)
dst = cv2.warpAffine(img,M,(cols,rows))

# 旋转90°
M = cv2.getRotationMatrix2D((cols/2,rows/2),90,1) 
dst = cv2.warpAffine(img,M,(cols,rows))

cv2.imshow('src',img)
cv2.imshow('30`,dst)  
cv2.imshow('90',dst)
cv2.waitKey(0)

 缩放变换:

import cv2

img = cv2.imread('scale.png')

# 放大1.5倍
dst = cv2.resize(img,None,fx=1.5,fy=1.5)  

# 缩小1.5倍
dst = cv2.resize(img,None,fx=0.667,fy=0.667)  

cv2.imshow('src',img)
cv2.imshow('scale up',dst)
cv2.imshow('scale down',dst)
cv2.waitKey(0)

1.7. 图像增强

通过前面介绍的各种操作来改善数字图像的视觉效果和质量。增强后的图像更适合人类视觉观察或后续计算机分析。

1.8. 图像压缩

使用像JPEG、PNG、GIF等算法来减少图像所需要的存储空间或数据量,通过压缩可以有效减少图像占用的存储空间和提高传输速度。 JPEG、PNG是常用的有损压缩方法,小波变换以及矢量量化是无损压缩的选择。通过控制压缩率,可以在压缩效果和重构图像质量之间取得平衡。

1. JPEG压缩。使用DCT变换将图像分解为频域,然后丢弃高频系数来达到压缩效果。例如:

import cv2
img = cv2.imread('input.png')
cv2.imshow('Original', img)

# JPEG压缩,质量为5%
_, encoded = cv2.imencode('.jpg', img, [int(cv2.IMWRITE_JPEG_QUALITY), 5]) 
decoded = cv2.imdecode(encoded, 1) 
cv2.imshow('JPEG Quality=5%', decoded)
cv2.waitKey(0)

2. PNG压缩。无损压缩格式,使用LZ77和Huffman编码来压缩数据,可以用于图像的无损存储。

3. 矢量量化。将图像空间划分为有限的量化单元,并使用代表每个单元的量化值来代替原始像素值,达到压缩效果。

4. 小波变换。使用小波分解将图像分解为不同频率子带,然后选择性地丢弃较高频率子带和准确重构低频子带,实现压缩。例如:

import pywt
import cv2

# 小波变换,使用Haar波lets,保留HH, LH, HL系数
coeff = pywt.dwt2(img, 'haar') 
LL, (LH, HL, HH) = coeff

# 重构图像
rec_img = pywt.idwt2((LL, (LH, HL, HH)), 'haar')
cv2.imshow('DWT Reconstructed', rec_img.astype(np.uint8))
cv2.waitKey(0)

1.9. 图像分割

将图像分解为有意义的区域或对象,是理解图像语义及计算机视觉的基础。常用方法有阈值法、边缘检测法、区域生长法等。

1. 阈值分割。根据像素灰度值设定一个阈值,大于阈值的像素属于前景,小于阈值的像素属于背景。例如:

import cv2
import numpy as np

img = cv2.imread('coins.png',0)
ret, thresh = cv2.threshold(img, 127, 255, 0) 
cv2.imshow('Binary Threshold', thresh)
cv2.waitKey(0)

2. 边缘检测。使用一阶或二阶导数检测图像强度变化的地方,即边缘。常用的边缘检测算子有Sobel、Canny等。例如:

edges = cv2.Canny(img, 100, 200)  
cv2.imshow('Canny Edge', edges)
cv2.waitKey(0)

3. 形态学分割。通过一系列的腐蚀和膨胀操作来分割图像。例如:

kernel = np.ones((5,5),np.uint8)
opening = cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel)
closing = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel)

4. 水平集分割。根据图像的灰度函数 seeking出相对平坦的区域,将其作为分割结果。

5. 聚类算法。将具有相似特征的像素聚类,形成不同的区域。常用的聚类方法有K-Means, Mean-Shift等。

1.10. 图像表示

使用像素矩阵、几何形状、边界等表达和描述图像特征及对象。这种表达方法决定后续算法的选择。

1. 灰度图像。每个像素使用一个数值表示,通常范围是0-255。例如:

img = cv2.imread('input.jpg', 0)

2. 彩色图像。每个像素使用三个通道(RGB)表示颜色,每个通道的值范围是0-255。例如:

img = cv2.imread('input.jpg')

3. HSV图像。将RGB图像转换到HSV空间,其中H表示色调,S表示饱和度,V表示明度。例如:

hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
4. YCrCb图像。将RGB图像转换到YCrCb空间,Y表示亮度,Cr和Cb表示红色和蓝色的色度分量。例如:
ycrcb = cv2.cvtColor(img, cv2.COLOR_BGR2YCrCb)
5. 小波变换。使用小波变换将图像分解为不同的频率子带,可以得到更低维的表达。例如:
coeff = pywt.dwt2(img, 'haar')  
LL, (LH, HL, HH) = coeff
 
6. HOG特征。计算图像局部区域的梯度方向直方图(Histogram of Oriented Gradients)作为特征表达。
7. SIFT特征。检测图像关键点,并计算每个关键点的位置、尺度和旋转不变特征, 获取对图像具有鲁棒性的特征表达。
不同的图像表示方式各有优势,我们可以根据实际应用选择使用。灰度图和RGB图适合直接分析像素信息;HSV和YCrCb空间更适合颜色分割和量化;Fourier变换等更适合频域分析;SIFT、HOG等特征更适合对象检测和识别。通过不同表达方式,我们可以从不同角度理解和分析图像。

2. 常用的图像滤波方法

2.1. 平均滤波

平均滤波是一种简单有效的平滑滤波方法,用邻域内像素的平均值来替换中心像素,可以抑制图像噪声。它通过用图像某像素邻域内像素的平均值来代替该像素值,达到平滑图像的目的。平均滤波的原理很简单,对每个像素值,取其在网格中的邻域内像素值的平均,然后代替原来的像素值。这个算子可以有效地去除图像噪声,使得图像平滑化。平均滤波在OpenCV中可以这样实现:

blur = cv2.blur(img, ksize)

- img:输入图像
- ksize:滤波器的大小,以(width, height)的形式指定例如,一个5x5的平均滤波器:

blur = cv2.blur(img, (5, 5))  

这个滤波器会取每个像素周围5x5范围内的像素值计算平均,然后代替中心像素的值。示例:

import cv2
import numpy as np

# 加载图像
img = cv2.imread('input.jpg')

# 5x5平均滤波器
blur = cv2.blur(img, (5, 5))  

# 展示输入图像和滤波结果
res = np.hstack((img, blur))
cv2.imshow('Average Blur', res)
cv2.waitKey(0)

2.2. 高斯滤波

用邻域内像素的加权平均值来替换中心像素,加权中心更大,可以抑制高斯噪声。

在OpenCV中可以这样实现高斯滤波:

g_blur = cv2.GaussianBlur(img, ksize, sigmaX)

- img:输入图像
- ksize:高斯核的大小,以(width, height)形式指定,width和height必须是奇数
- sigmaX:高斯函数的标准差,sigmaY默认为sigmaX例如,一个5x5高斯滤波器,标准差为1:

g_blur = cv2.GaussianBlur(img, (5, 5), 1)

示例:

import cv2
import numpy as np

# 加载图像 
img = cv2.imread('input.jpg')

# 5x5高斯滤波,标准差为1
g_blur = cv2.GaussianBlur(img, (5, 5), 1)  

# 展示输入图像和滤波结果
res = np.hstack((img, g_blur))
cv2.imshow('Gaussian Blur', res)
cv2.waitKey(0)

2.3. 中值滤波

用邻域内像素的中值来替换中心像素,可以抑制椒盐噪声。

中值滤波的原理很简单,对每个像素,取其在网格内邻域像素值的中值,然后代替原来的像素值。由于中值可以抵制最多一半的脉冲噪声,所以这种滤波对椒盐噪声有很好的去除效果。

在OpenCV中可以这样实现中值滤波:

median = cv2.medianBlur(img, ksize)

- img:输入图像
- ksize:滤波器的大小,必须是大于1的奇数,如3、5、7等例如,一个5x5的中值滤波器:

median = cv2.medianBlur(img, 5)  

这个滤波器会取每个像素周围5x5范围内的像素值,求其中值,然后替换中心像素的值。示例:

import cv2
import numpy as np

# 加载输入图像
img = cv2.imread('input_noise.jpg')

# 5x5中值滤波 
median = cv2.medianBlur(img, 5)

# 展示输入图像和滤波结果
res = np.hstack((img, median))
cv2.imshow('Median Blur', res)  
cv2.waitKey(0)

2.4. 双边滤波

双边滤波是一种非线性的图像平滑滤波方法,结合空间距离和像素差异度的加权平均,可以抑制噪声的同时保留图像边缘。 它通过考虑像素空间距离和颜色相似度两方面信息,来决定像素之间的连接,实现平滑图像的目的。双边滤波的原理是对图像进行平均权重,但权重不仅考虑像素空间距离,也考虑像素颜色差异。

在OpenCV中可以这样实现双边滤波:

bilateral = cv2.bilateralFilter(img, d, sigmaColor, sigmaSpace)

- img:输入图像
- d:半径,控制空间范围
- sigmaColor:颜色标准差,大值表示在更广泛的颜色范围内进行平滑
- sigmaSpace:坐标空间标准差,大值表示更长距离的像素会产生影响示例:

import cv2
import numpy as np

# 加载输入图像
img = cv2.imread('input.jpg')

# 双边滤波 d=9, sigmaColor=75, sigmaSpace=75
bilateral = cv2.bilateralFilter(img, 9, 75, 75)   

# 展示输入图像和滤波结果
res = np.hstack((img, bilateral))  
cv2.imshow('Bilateral Blur', res)
cv2.waitKey(0)

2.5. Sobel算子

Sobel算子是一种用于边缘检测的图像滤波操作,一阶求导算子,用于边缘检测。它通过计算图像亮度相近区域的梯度来检测边缘。

Sobel算子使用两个3x3的滤波器来计算图像的水平方向梯度Gx和垂直方向梯度Gy,然后通过组合它们来获得图像梯度的绝对值和方向。具体的滤波器如下:

Gx:

-1  0  1
-2  0  2 
-1  0  1

Gy:

-1 -2 -1
 0  0  0
 1  2  1

在OpenCV中可以这样实现Sobel算子:

x = cv2.Sobel(img, cv2.CV_64F, 1, 0, ksize=3)
y = cv2.Sobel(img, cv2.CV_64F, 0, 1, ksize=3)

- img:输入图像
- ddepth:输出图像的深度,-1表示与输入图像相同
- dx:x方向的导数阶数,这里为1表示一阶导数
- dy:y方向的导数阶数
- ksize:Sobel kernel的大小,必须为1, 3, 5或7通过计算Gx和Gy我们可以得到图像梯度的绝对值和方向:

abs_sobel = cv2.convertScaleAbs(x)   # 转为 unit8 类型 
sobel_deg = cv2.degrees(cv2.arctan2(y, x))  #计算梯度方向

示例:

import cv2
import numpy as np

# 加载输入图像  
img = cv2.imread('input.jpg', 0)

# Sobel算子
x = cv2.Sobel(img, cv2.CV_64F, 1, 0, ksize=3)
y = cv2.Sobel(img, cv2.CV_64F, 0, 1, ksize=3)

# 梯度的绝对值  
abs_sobel = cv2.convertScaleAbs(x)  

# 梯度方向 
sobel_deg = cv2.degrees(cv2.arctan2(y, x))  

# 展示结果
res = np.vstack((abs_sobel, sobel_deg))
cv2.imshow('Sobel', res)
cv2.waitKey(0) 

2.6. Laplacian算子

Laplacian算子是一种用于检测图像中二阶导数的滤波操作,主要用于边缘检测。它通过计算图像灰度之间的二阶差分来检测边缘。Laplacian算子使用以下核来计算图像的二阶导数:

0  1  0
1 -4  1
0  1  0

这个核可以检测图像的二阶导数,从而突出图像的边缘特征。在OpenCV中可以这样实现Laplacian算子:

laplacian = cv2.Laplacian(img, ddepth, ksize=3)

- img:输入图像 
- ddepth:输出图像的深度,-1表示与输入图像相同
- ksize:滤波器的大小,必须为1、3、5、7。示例:

import cv2
import numpy as np

# 加载输入图像  
img = cv2.imread('input.jpg', 0)

# Laplacian算子
laplacian = cv2.Laplacian(img, cv2.CV_64F, ksize=3)

# 展示结果
result = np.hstack((img, laplacian))
cv2.imshow('Laplacian', result)
cv2.waitKey(0)

相比Sobel算子(一阶导数),Laplacian算子在图像边缘检测上可以得到更加清晰的结果,所以在实际应用中会根据需要选择使用Sobel算子或Laplacian算子。Laplacian算子更加灵敏,但是噪声也会更加突出,所以通常会与其他平滑滤波器一起使用来减弱噪声的影响。 

2.7. Scharr滤波器

Scharr滤波器是一种用于边缘检测的图像滤波操作,更加精确的一阶导数滤波器,用于边缘检测。它是Sobel滤波器的一种优化,可以计算更加精确的图像梯度。

Scharr滤波器使用两个3x3的滤波器来计算图像的水平方向梯度Gx和垂直方向梯度Gy。与Sobel滤波器不同的是,Scharr滤波器的横向和纵向模板具有更高的对称性,且权重的绝对值求和为1,这可以产生更加精确的梯度估计。具体的滤波器模板如下:Gx:

-3  0  3 
-10 0 10
-3  0  3

  Gy:

-3 -10 -3
 0   0  0
 3  10  3

在OpenCV中可以这样实现Scharr滤波器:

x = cv2.Scharr(img,cv2.CV_64F,1,0)  
y = cv2.Scharr(img,cv2.CV_64F,0,1)

- img:输入图像
- ddepth:输出图像的深度,-1表示与输入图像相同
- dx:x方向的导数阶数,这里为1表示一阶导数 
- dy:y方向的导数阶数通过计算Gx和Gy我们可以得到图像梯度的绝对值和方向,就像Sobel算子一样:

abs_scharr = cv2.convertScaleAbs(x)   
scharr_deg = cv2.degrees(cv2.arctan2(y, x))  

示例:

import cv2
import numpy as np

# 加载输入图像  
img = cv2.imread('input.jpg', 0)

# Scharr滤波器  
x = cv2.Scharr(img,cv2.CV_64F,1,0)  
y = cv2.Scharr(img,cv2.CV_64F,0,1)   

# 梯度的绝对值
abs_scharr = cv2.convertScaleAbs(x)   

# 梯度方向 
scharr_deg = cv2.degrees(cv2.arctan2(y, x))  

# 展示结果
res = np.vstack((abs_scharr, scharr_deg))
cv2.imshow('Scharr', res)
cv2.waitKey(0)

通过对图像进行不同的滤波,我们可以达到去噪、模糊、边缘检测等效果。平均滤波、高斯滤波以及中值滤波主要用于去除噪声。Sobel以及Laplacian滤波器用于检测图像中的边缘等结构。

你可能感兴趣的:(计算机视觉,图像处理,人工智能)