跳到主要内容

图像处理基础

本章节介绍 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')

下一步

掌握了图像处理基础后,下一章节我们将学习视频处理,了解如何处理视频流和摄像头数据。