图像处理的对象是数字图像,它是由像素点阵列表示的图像。需要了解像素、图像分辨率、灰度级、RBG等图像表示方法。
用numpy数组表示,每个元素为像素值。例如RGB图像
import numpy as np
img = np.array([[[255,0,0], [0,255,0]], [[0,0,255], [255,255,255]]])
数字图像是通过采样和量化得到的,需要理解采样定理与频谱采样,和量化误差对图像质量的影响。
包括图像旋转、缩放、裁剪、变换等空间域操作,和灰度变换等强度域操作。这些操作会改变图像的 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)
用于图像平滑、去噪和边缘检测。常用滤波器有均值滤波器、高斯滤波器、中值滤波器、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)
用于增强图像的整体对比度,包括灰度变换、直方图均衡化、伽马变换等。可以改善图像的视觉质量。
阈值化:将大于阈值的像素置最大值,小于阈值的像素置最小值,可以将图像转化为二值图像。
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)
不同的像素值变换算法通过对图像的像素值进行变换,可以达到改变图像整体光照、对比度和细节等不同的效果。通过选择适当的算法或算法组合,我们可以有效地提高图像质量,为后续的图像分析和计算机视觉算法提供更清晰和信息丰富的输入。
用于图像的形变,包括平移变换、旋转变换、缩放变换等。这些变换没有改变像素值,只是改变位置或数量。
旋转变换:
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)
通过前面介绍的各种操作来改善数字图像的视觉效果和质量。增强后的图像更适合人类视觉观察或后续计算机分析。
使用像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. 阈值分割。根据像素灰度值设定一个阈值,大于阈值的像素属于前景,小于阈值的像素属于背景。例如:
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. 灰度图像。每个像素使用一个数值表示,通常范围是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等特征更适合对象检测和识别。通过不同表达方式,我们可以从不同角度理解和分析图像。
平均滤波是一种简单有效的平滑滤波方法,用邻域内像素的平均值来替换中心像素,可以抑制图像噪声。它通过用图像某像素邻域内像素的平均值来代替该像素值,达到平滑图像的目的。平均滤波的原理很简单,对每个像素值,取其在网格中的邻域内像素值的平均,然后代替原来的像素值。这个算子可以有效地去除图像噪声,使得图像平滑化。平均滤波在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)
用邻域内像素的加权平均值来替换中心像素,加权中心更大,可以抑制高斯噪声。
在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)
用邻域内像素的中值来替换中心像素,可以抑制椒盐噪声。
中值滤波的原理很简单,对每个像素,取其在网格内邻域像素值的中值,然后代替原来的像素值。由于中值可以抵制最多一半的脉冲噪声,所以这种滤波对椒盐噪声有很好的去除效果。
在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)
双边滤波是一种非线性的图像平滑滤波方法,结合空间距离和像素差异度的加权平均,可以抑制噪声的同时保留图像边缘。 它通过考虑像素空间距离和颜色相似度两方面信息,来决定像素之间的连接,实现平滑图像的目的。双边滤波的原理是对图像进行平均权重,但权重不仅考虑像素空间距离,也考虑像素颜色差异。
在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)
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)
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算子更加灵敏,但是噪声也会更加突出,所以通常会与其他平滑滤波器一起使用来减弱噪声的影响。
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滤波器用于检测图像中的边缘等结构。