图像处理基础
本章节介绍 OpenCV 中常用的图像处理技术,包括几何变换、图像滤波、边缘检测和图像阈值化等内容。
几何变换
几何变换是图像处理中最基本的操作之一,包括缩放、旋转、平移和仿射变换等。
图像缩放
使用 cv2.resize() 函数缩放图像:
import cv2
image = cv2.imread('image.jpg')
# 指定目标尺寸
resized = cv2.resize(image, (400, 300)) # 宽度400,高度300
# 按比例缩放
scale_percent = 50 # 缩放比例
width = int(image.shape[1] * scale_percent / 100)
height = int(image.shape[0] * scale_percent / 100)
resized = cv2.resize(image, (width, height))
# 使用不同的插值方法
resized_linear = cv2.resize(image, None, fx=2, fy=2, interpolation=cv2.INTER_LINEAR)
resized_nearest = cv2.resize(image, None, fx=2, fy=2, interpolation=cv2.INTER_NEAREST)
resized_cubic = cv2.resize(image, None, fx=2, fy=2, interpolation=cv2.INTER_CUBIC)
插值方法说明:
| 方法 | 适用场景 | 特点 |
|---|---|---|
INTER_NEAREST | 最近邻插值 | 速度最快,质量最低 |
INTER_LINEAR | 双线性插值(默认) | 速度和质量平衡 |
INTER_CUBIC | 双三次插值 | 放大时质量较好 |
INTER_AREA | 区域插值 | 缩小时效果最好 |
图像翻转
使用 cv2.flip() 函数翻转图像:
import cv2
image = cv2.imread('image.jpg')
# 水平翻转
flipped_horizontal = cv2.flip(image, 1)
# 垂直翻转
flipped_vertical = cv2.flip(image, 0)
# 同时水平和垂直翻转
flipped_both = cv2.flip(image, -1)
图像旋转
使用 cv2.getRotationMatrix2D() 和 cv2.warpAffine() 实现图像旋转:
import cv2
image = cv2.imread('image.jpg')
height, width = image.shape[:2]
# 获取旋转矩阵
center = (width // 2, height // 2) # 旋转中心
angle = 45 # 旋转角度(逆时针为正)
scale = 1.0 # 缩放比例
rotation_matrix = cv2.getRotationMatrix2D(center, angle, scale)
# 执行旋转
rotated = cv2.warpAffine(image, rotation_matrix, (width, height))
cv2.imshow('旋转图像', rotated)
cv2.waitKey(0)
图像平移
使用仿射变换实现图像平移:
import cv2
import numpy as np
image = cv2.imread('image.jpg')
height, width = image.shape[:2]
# 平移矩阵 [[1, 0, tx], [0, 1, ty]]
tx, ty = 100, 50 # x方向平移100,y方向平移50
translation_matrix = np.float32([[1, 0, tx], [0, 1, ty]])
# 执行平移
translated = cv2.warpAffine(image, translation_matrix, (width, height))
cv2.imshow('平移图像', translated)
cv2.waitKey(0)
仿射变换
仿射变换可以保持图像的平行性和直线性质。通过三个点可以确定一个仿射变换:
import cv2
import numpy as np
image = cv2.imread('image.jpg')
height, width = image.shape[:2]
# 原图像中的三个点
pts1 = np.float32([[50, 50], [200, 50], [50, 200]])
# 变换后的三个点
pts2 = np.float32([[10, 100], [200, 50], [100, 250]])
# 获取仿射变换矩阵
affine_matrix = cv2.getAffineTransform(pts1, pts2)
# 执行仿射变换
result = cv2.warpAffine(image, affine_matrix, (width, height))
cv2.imshow('仿射变换', result)
cv2.waitKey(0)
透视变换
透视变换可以将一个四边形区域变换为另一个四边形区域,常用于文档矫正:
import cv2
import numpy as np
image = cv2.imread('document.jpg')
height, width = image.shape[:2]
# 原图像中四个角点(按顺序:左上、右上、左下、右下)
pts1 = np.float32([[56, 65], [368, 52], [28, 387], [389, 390]])
# 目标四个角点
pts2 = np.float32([[0, 0], [300, 0], [0, 400], [300, 400]])
# 获取透视变换矩阵
perspective_matrix = cv2.getPerspectiveTransform(pts1, pts2)
# 执行透视变换
result = cv2.warpPerspective(image, perspective_matrix, (300, 400))
cv2.imshow('透视变换', result)
cv2.waitKey(0)
图像滤波
图像滤波用于去除噪声、平滑图像或提取特征。
均值滤波
均值滤波使用邻域像素的平均值替换当前像素:
import cv2
image = cv2.imread('image.jpg')
# 3x3 均值滤波
blurred = cv2.blur(image, (3, 3))
# 5x5 均值滤波
blurred = cv2.blur(image, (5, 5))
cv2.imshow('均值滤波', blurred)
cv2.waitKey(0)
均值滤波简单快速,但会使图像变得模糊。
高斯滤波
高斯滤波使用高斯核进行加权平均,效果比均值滤波更自然:
import cv2
image = cv2.imread('image.jpg')
# 高斯滤波,核大小 (5, 5),标准差为 0(自动计算)
gaussian = cv2.GaussianBlur(image, (5, 5), 0)
# 指定标准差
gaussian = cv2.GaussianBlur(image, (5, 5), sigmaX=1.5)
cv2.imshow('高斯滤波', gaussian)
cv2.waitKey(0)
高斯滤波是最常用的平滑滤波器,适合去除高斯噪声。
中值滤波
中值滤波使用邻域像素的中值替换当前像素,对椒盐噪声效果最好:
import cv2
image = cv2.imread('noisy_image.jpg')
# 中值滤波,核大小必须为奇数
median = cv2.medianBlur(image, 5)
cv2.imshow('中值滤波', median)
cv2.waitKey(0)
中值滤波能有效去除椒盐噪声,同时保持边缘清晰。
双边滤波
双边滤波在平滑的同时保持边缘,是效果最好的平滑滤波器:
import cv2
image = cv2.imread('image.jpg')
# 双边滤波
# d: 邻域直径
# sigmaColor: 颜色空间标准差
# sigmaSpace: 坐标空间标准差
bilateral = cv2.bilateralFilter(image, d=9, sigmaColor=75, sigmaSpace=75)
cv2.imshow('双边滤波', bilateral)
cv2.waitKey(0)
双边滤波计算量较大,但能实现"美颜"效果,平滑皮肤同时保持五官清晰。
自定义卷积核
使用 cv2.filter2D() 应用自定义卷积核:
import cv2
import numpy as np
image = cv2.imread('image.jpg')
# 锐化核
sharpen_kernel = np.array([
[0, -1, 0],
[-1, 5, -1],
[0, -1, 0]
])
# 边缘检测核(拉普拉斯)
laplacian_kernel = np.array([
[0, 1, 0],
[1, -4, 1],
[0, 1, 0]
])
# 应用卷积核
sharpened = cv2.filter2D(image, -1, sharpen_kernel)
edges = cv2.filter2D(image, -1, laplacian_kernel)
cv2.imshow('锐化', sharpened)
cv2.imshow('边缘', edges)
cv2.waitKey(0)
边缘检测
边缘检测是计算机视觉中的基础操作,用于提取图像中的轮廓信息。
Sobel 算子
Sobel 算子结合了高斯平滑和微分运算:
import cv2
import numpy as np
image = cv2.imread('image.jpg', cv2.IMREAD_GRAYSCALE)
# 计算 x 方向梯度
sobel_x = cv2.Sobel(image, cv2.CV_64F, 1, 0, ksize=3)
# 计算 y 方向梯度
sobel_y = cv2.Sobel(image, cv2.CV_64F, 0, 1, ksize=3)
# 合并梯度
sobel_combined = cv2.magnitude(sobel_x, sobel_y)
# 转换为 8 位图像
sobel_result = np.uint8(sobel_combined / sobel_combined.max() * 255)
cv2.imshow('Sobel X', np.abs(sobel_x).astype(np.uint8))
cv2.imshow('Sobel Y', np.abs(sobel_y).astype(np.uint8))
cv2.imshow('Sobel Combined', sobel_result)
cv2.waitKey(0)
Laplacian 算子
Laplacian 算子计算二阶导数,对噪声敏感:
import cv2
image = cv2.imread('image.jpg', cv2.IMREAD_GRAYSCALE)
# Laplacian 边缘检测
laplacian = cv2.Laplacian(image, cv2.CV_64F)
# 转换为 8 位图像
laplacian = np.uint8(np.absolute(laplacian))
cv2.imshow('Laplacian', laplacian)
cv2.waitKey(0)
Canny 边缘检测
Canny 是最常用的边缘检测算法,效果最好:
import cv2
image = cv2.imread('image.jpg', cv2.IMREAD_GRAYSCALE)
# Canny 边缘检测
# threshold1: 低阈值
# threshold2: 高阈值
edges = cv2.Canny(image, threshold1=100, threshold2=200)
cv2.imshow('Canny 边缘', edges)
cv2.waitKey(0)
Canny 算法的阈值选择原则:
- 高阈值用于确定边缘的起点
- 低阈值用于边缘的延伸
- 推荐比例:高阈值是低阈值的 2-3 倍
图像阈值化
阈值化将灰度图像转换为二值图像,是图像分割的基础。
简单阈值化
import cv2
image = cv2.imread('image.jpg', cv2.IMREAD_GRAYSCALE)
# 简单阈值化
# 阈值 127,最大值 255
ret, binary = cv2.threshold(image, 127, 255, cv2.THRESH_BINARY)
# 反向二值化
ret, binary_inv = cv2.threshold(image, 127, 255, cv2.THRESH_BINARY_INV)
# 截断阈值化
ret, trunc = cv2.threshold(image, 127, 255, cv2.THRESH_TRUNC)
# 阈值归零
ret, tozero = cv2.threshold(image, 127, 255, cv2.THRESH_TOZERO)
# 反向阈值归零
ret, tozero_inv = cv2.threshold(image, 127, 255, cv2.THRESH_TOZERO_INV)
阈值化类型说明:
| 类型 | 说明 |
|---|---|
THRESH_BINARY | 大于阈值为最大值,否则为 0 |
THRESH_BINARY_INV | 大于阈值为 0,否则为最大值 |
THRESH_TRUNC | 大于阈值为阈值,否则不变 |
THRESH_TOZERO | 大于阈值不变,否则为 0 |
THRESH_TOZERO_INV | 大于阈值为 0,否则不变 |
自适应阈值化
自适应阈值根据局部区域计算阈值,适合光照不均匀的图像:
import cv2
image = cv2.imread('image.jpg', cv2.IMREAD_GRAYSCALE)
# 自适应均值阈值
adaptive_mean = cv2.adaptiveThreshold(
image, 255,
cv2.ADAPTIVE_THRESH_MEAN_C,
cv2.THRESH_BINARY,
blockSize=11, # 邻域大小
C=2 # 常数
)
# 自适应高斯阈值
adaptive_gaussian = cv2.adaptiveThreshold(
image, 255,
cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
cv2.THRESH_BINARY,
blockSize=11,
C=2
)
cv2.imshow('自适应均值', adaptive_mean)
cv2.imshow('自适应高斯', adaptive_gaussian)
cv2.waitKey(0)
Otsu 阈值化
Otsu 方法自动计算最优阈值:
import cv2
image = cv2.imread('image.jpg', cv2.IMREAD_GRAYSCALE)
# 先进行高斯滤波去噪
blurred = cv2.GaussianBlur(image, (5, 5), 0)
# Otsu 阈值化
ret, otsu = cv2.threshold(
blurred, 0, 255,
cv2.THRESH_BINARY + cv2.THRESH_OTSU
)
print(f"Otsu 计算的阈值: {ret}")
cv2.imshow('Otsu 阈值化', otsu)
cv2.waitKey(0)
形态学操作
形态学操作基于图像形状进行处理,常用于二值图像的处理。
腐蚀与膨胀
import cv2
import numpy as np
image = cv2.imread('binary.png', cv2.IMREAD_GRAYSCALE)
# 定义结构元素
kernel = np.ones((5, 5), np.uint8)
# 腐蚀:去除边界点
erosion = cv2.erode(image, kernel, iterations=1)
# 膨胀:扩展边界点
dilation = cv2.dilate(image, kernel, iterations=1)
cv2.imshow('原图', image)
cv2.imshow('腐蚀', erosion)
cv2.imshow('膨胀', dilation)
cv2.waitKey(0)
腐蚀会缩小前景区域,膨胀会扩大前景区域。
开运算与闭运算
import cv2
import numpy as np
image = cv2.imread('binary.png', cv2.IMREAD_GRAYSCALE)
kernel = np.ones((5, 5), np.uint8)
# 开运算:先腐蚀后膨胀,去除噪点
opening = cv2.morphologyEx(image, cv2.MORPH_OPEN, kernel)
# 闭运算:先膨胀后腐蚀,填充孔洞
closing = cv2.morphologyEx(image, cv2.MORPH_CLOSE, kernel)
cv2.imshow('开运算', opening)
cv2.imshow('闭运算', closing)
cv2.waitKey(0)
形态学梯度
import cv2
import numpy as np
image = cv2.imread('binary.png', cv2.IMREAD_GRAYSCALE)
kernel = np.ones((5, 5), np.uint8)
# 形态学梯度 = 膨胀 - 腐蚀
gradient = cv2.morphologyEx(image, cv2.MORPH_GRADIENT, kernel)
cv2.imshow('形态学梯度', gradient)
cv2.waitKey(0)
完整示例:图像预处理流水线
import cv2
import numpy as np
def preprocess_image(image_path):
# 读取图像
image = cv2.imread(image_path)
if image is None:
print("无法读取图像")
return
# 转换为灰度图
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# 高斯滤波去噪
blurred = cv2.GaussianBlur(gray, (5, 5), 0)
# Canny 边缘检测
edges = cv2.Canny(blurred, 50, 150)
# 显示结果
cv2.imshow('原图', image)
cv2.imshow('灰度图', gray)
cv2.imshow('滤波后', blurred)
cv2.imshow('边缘检测', edges)
cv2.waitKey(0)
cv2.destroyAllWindows()
preprocess_image('image.jpg')
下一步
掌握了图像处理基础后,下一章节我们将学习视频处理,了解如何处理视频流和摄像头数据。