2. 图像桶形畸变校正
任务说明
本案例实现图像桶形畸变的校正。桶形畸变是广角镜头常见的几何失真,画面中心区域向外膨胀,直线向外弯曲。给定一张存在桶形畸变的测试图像,通过建立畸变模型并反向映射,输出校正后的图像,使原本弯曲的直线恢复平直。
畸变模型与校正原理
采用常用的多项式畸变模型。设归一化坐标下无畸变点为 (xu, yu),畸变点为 (xd, yd),径向距离 r = √(xu² + yu²),则畸变关系为:
xd = xu · (1 + k₁·r² + k₂·r⁴)
yd = yu · (1 + k₁·r² + k₂·r⁴)
校正时,对输出图像中的每个像素,将其坐标归一化后作为无畸变点 (xu, yu),代入上式求得畸变坐标 (xd, yd),再映射回输入图像采样像素值。由于畸变系数为正时产生桶形畸变,校正需使用负系数抵消,因此本实现中 k₁、k₂ 取负值。
代码实现
import numpy as np
import cv2
import sys
def barrel_correction(img, k1, k2):
"""桶形畸变校正主函数
Args:
img: 输入畸变图像 (H, W, C)
k1, k2: 径向畸变系数(校正时取负值)
Returns:
校正后的图像
"""
h, w = img.shape[:2]
# 相机内参近似:主点位于图像中心,焦距取图像尺寸的一半
cx, cy = w / 2.0, h / 2.0
fx = fy = max(w, h) / 2.0
# 构建输出图像坐标网格
map_x = np.zeros((h, w), dtype=np.float32)
map_y = np.zeros((h, w), dtype=np.float32)
# 归一化坐标
u = (np.arange(w) - cx) / fx
v = (np.arange(h) - cy) / fy
U, V = np.meshgrid(u, v)
# 计算径向距离
r2 = U**2 + V**2
r4 = r2 * r2
# 畸变比例因子
scale = 1.0 + k1 * r2 + k2 * r4
# 畸变坐标(归一化)
xd = U * scale
yd = V * scale
# 映射回像素坐标
map_x = xd * fx + cx
map_y = yd * fy + cy
# 使用重映射进行像素采样
corrected = cv2.remap(img, map_x, map_y,
interpolation=cv2.INTER_LINEAR,
borderMode=cv2.BORDER_CONSTANT)
return corrected
def generate_test_pattern(size=600):
"""生成含直线网格的测试图案,用于验证校正效果"""
img = np.full((size, size, 3), 255, dtype=np.uint8)
step = 60
# 绘制水平和垂直线条
for i in range(0, size, step):
cv2.line(img, (i, 0), (i, size-1), (0, 0, 0), 2)
cv2.line(img, (0, i), (size-1, i), (0, 0, 0), 2)
# 绘制边框
cv2.rectangle(img, (0, 0), (size-1, size-1), (0, 0, 0), 3)
return img
def apply_barrel_distortion(img, k1, k2):
"""对图像施加桶形畸变(用于构造测试输入)"""
h, w = img.shape[:2]
cx, cy = w / 2.0, h / 2.0
fx = fy = max(w, h) / 2.0
u = (np.arange(w) - cx) / fx
v = (np.arange(h) - cy) / fy
U, V = np.meshgrid(u, v)
r2 = U**2 + V**2
r4 = r2 * r2
# 正向畸变:系数为正产生桶形
scale = 1.0 + k1 * r2 + k2 * r4
xd = U * scale
yd = V * scale
map_x = (xd * fx + cx).astype(np.float32)
map_y = (yd * fy + cy).astype(np.float32)
distorted = cv2.remap(img, map_x, map_y,
interpolation=cv2.INTER_LINEAR,
borderMode=cv2.BORDER_CONSTANT)
return distorted
if __name__ == "__main__":
# 畸变系数(模拟桶形畸变)
K1_DISTORT = 0.15
K2_DISTORT = 0.05
# 生成测试图案并施加畸变
pattern = generate_test_pattern(600)
distorted_img = apply_barrel_distortion(pattern, K1_DISTORT, K2_DISTORT)
# 校正:使用负系数抵消畸变
corrected_img = barrel_correction(distorted_img, -K1_DISTORT, -K2_DISTORT)
# 保存结果
cv2.imwrite("distorted.png", distorted_img)
cv2.imwrite("corrected.png", corrected_img)
cv2.imwrite("original_pattern.png", pattern)
print("处理完成,输出文件:")
print(" distorted.png - 畸变图像")
print(" corrected.png - 校正后图像")
print(" original_pattern.png - 原始网格")
运行与验证
依赖环境:Python 3,NumPy,OpenCV(pip install numpy opencv-python)。
运行方式:将上述代码保存为 barrel_correction.py,在终端执行 python barrel_correction.py。
验证方法:程序会生成三张图片。打开 distorted.png 可看到网格线向外弯曲(桶形畸变);打开 corrected.png 观察网格线是否恢复平直。判定标准:校正后图像中的直线网格应基本恢复为直线,且图像边缘区域无明显弯曲残留;与 original_pattern.png 对比,网格间距和位置应接近一致。
关键参数说明
畸变系数 k₁、k₂ 的取值决定了畸变强度。本案例中正向畸变使用 k₁=0.15、k₂=0.05 模拟桶形畸变,校正时取相反数。若处理真实相机图像,需通过标定获取准确的畸变系数和相机内参,替换代码中的 cx、cy、fx、fy 及 k₁、k₂ 即可。
实现要点
本实现的核心在于:对输出图像的每个像素,通过畸变模型反算出其在输入图像中的采样位置,再使用 cv2.remap 完成像素重采样。代码中同时提供了畸变施加函数 apply_barrel_distortion 用于构造测试输入,从而在无真实相机的情况下验证校正算法的正确性。校正系数取畸变系数的相反数,实现了畸变的逆向消除。