一、畸变基础:什么是广角镜头畸变?

大家好,我是你们这堂课的主讲人。做了十几年光学设计和计算机视觉,我经手的镜头没有一千也有八百了。今天咱们聊一个老生常谈、但又绕不开的话题——广角镜头畸变。

说白了,畸变就是镜头“撒谎”了。你明明拍的是笔直的墙缝,照片里它却弯了。你明明拍的是方方正正的棋盘格,照片里它却鼓起来了或者瘪下去了。这不是镜头坏了,这是物理规律在作祟。

核心定义:畸变(Distortion)是光学系统对物体成像时,因放大率随视场变化而产生的几何形变。它不影响图像的清晰度,但会改变物体的形状和位置关系。

1.1 桶形畸变:为什么会“鼓”起来?

桶形畸变,英文叫 Barrel Distortion。你想想看,一个木桶是什么形状?中间鼓、两头收。没错,桶形畸变就是图像中心区域被“撑大”了,边缘区域被“压缩”了。

成因:这主要跟广角镜头的结构有关。为了把更宽的视野塞进小小的传感器,镜头前组镜片必须做得非常“弯”。光线经过这些镜片时,边缘视场的光线偏折角度比中心更大,导致边缘的放大率小于中心。结果就是——中心的像素被“挤”得更大,边缘的像素被“拉”得更小。

我记得有一次做车载环视系统,用了四颗超广角镜头。拼接出来的全景图,地面上的停车线全是弯的,像一朵花。嗯,那就是典型的桶形畸变在捣乱。

视觉特征:

  • 直线向外弯曲,像“)(”的形状
  • 图像中心区域看起来“凸”出来
  • 边缘物体被压缩,显得更小、更远
  • 常见于广角镜头、鱼眼镜头、手机前置摄像头

我的小经验:判断桶形畸变有个土办法——拍一堵砖墙。如果墙缝在画面中间是直的,到了两边却向外弯,那就是桶形畸变。我曾经用这个办法帮一个学生快速定位了他手机镜头的畸变问题,省去了复杂的仪器测量。

1.2 枕形畸变:为什么会“瘪”下去?

枕形畸变,Pincushion Distortion,顾名思义,像枕头一样——四个角被“拉”出去了,中间反而“瘪”了。

成因:枕形畸变通常出现在长焦镜头或变焦镜头的长焦端。它的成因跟桶形畸变正好相反——边缘视场的放大率大于中心视场。说白了,边缘的像素被“拉”得更大了,中心的像素相对被“压缩”了。

为什么会这样?这跟镜头的后组结构有关。长焦镜头为了校正色差和球差,后组镜片往往设计得比较复杂。光线经过这些镜片时,边缘光线被过度“拉伸”,就产生了枕形畸变。

我个人习惯把这两种畸变记成一句话:“广角桶,长焦枕”。你想想看,广角镜头视野宽,像桶一样能装;长焦镜头视野窄,像枕头一样扁。虽然不严谨,但好记。

视觉特征:

  • 直线向内弯曲,像“)(”的反向——其实是“)(”的镜像
  • 图像四角被“拉”向画面中心
  • 边缘物体被拉伸,显得更大、更近
  • 常见于长焦镜头、变焦镜头的长焦端、某些望远镜

注意:枕形畸变在视觉上比桶形畸变更“隐蔽”。因为人眼对画面边缘的变形不太敏感,但一旦你把它跟真实场景对比,就会发现四角的直线全都弯了。我曾经在无人机测绘项目中吃过这个亏——拍出来的正射影像,四角的建筑物全都变形了,导致三维重建精度严重下降。

1.3 两种畸变的对比

为了让你更直观地理解,我整理了一个对比表。这个表在我做镜头选型时经常用到,分享给你:

对比项 桶形畸变 枕形畸变
英文名 Barrel Distortion Pincushion Distortion
形状比喻 木桶(中间鼓) 枕头(四角鼓)
直线弯曲方向 向外弯曲(凸) 向内弯曲(凹)
放大率变化 中心 > 边缘 边缘 > 中心
常见镜头 广角、鱼眼、手机前置 长焦、变焦长焦端
视觉影响 边缘物体被压缩 边缘物体被拉伸
校正难度 相对容易(参数少) 相对复杂(参数多)

1.4 畸变的知识体系

下面这张图是我自己画的,帮你理清这一章的核心逻辑。你看完应该能明白:畸变不是孤立的问题,它跟镜头类型、视场大小、校正方法都紧密相关。

镜头畸变 按弯曲方向分类 桶形畸变 中心放大率 > 边缘 枕形畸变 边缘放大率 > 中心 常见:广角、鱼眼、手机前置 常见:长焦、变焦长焦端 校正方法:光学设计 + 软件算法

1.5 一个简单的畸变检测代码

光说不练假把式。我写了一个简单的 Python 代码,用 OpenCV 检测一张棋盘格图像的畸变程度。你可以在自己的电脑上跑一下试试。

import cv2
import numpy as np

# 读取棋盘格图像
img = cv2.imread('chessboard.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# 寻找棋盘格角点
ret, corners = cv2.findChessboardCorners(gray, (9, 6), None)

if ret:
    # 绘制角点
    cv2.drawChessboardCorners(img, (9, 6), corners, ret)
    
    # 计算畸变系数(这里只是演示,实际需要多张图标定)
    # 假设我们已经有了相机矩阵和畸变系数
    # 这里用一组示例数据
    mtx = np.array([[800, 0, 320],
                    [0, 800, 240],
                    [0, 0, 1]], dtype=np.float32)
    dist = np.array([-0.3, 0.1, 0, 0, 0], dtype=np.float32)  # 桶形畸变
    
    # 去畸变
    h, w = img.shape[:2]
    newcameramtx, roi = cv2.getOptimalNewCameraMatrix(mtx, dist, (w, h), 1, (w, h))
    dst = cv2.undistort(img, mtx, dist, None, newcameramtx)
    
    # 显示结果
    cv2.imshow('Original', img)
    cv2.imshow('Undistorted', dst)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
else:
    print("未找到棋盘格角点,请检查图像")

避坑指南:我曾经在标定相机时,只用了一张棋盘格图像就跑去算畸变系数,结果校正出来的图像比原来还歪。后来才意识到——单张图像无法解出完整的畸变参数,至少需要10-20张不同角度、不同位置的棋盘格图像。嗯,这个坑我替你踩过了。

1.6 小结

这一章我们聊了畸变的基础知识。说白了,桶形畸变和枕形畸变就是镜头“放大率不均匀”导致的两种典型变形。一个像桶,一个像枕。记住那句口诀——“广角桶,长焦枕”,以后看到照片变形,你就能快速判断是哪种畸变了。

下一章我们会深入聊畸变的数学模型和标定方法。到时候我会手把手教你用张正友标定法算畸变系数。嗯,那才是真正硬核的东西。


公众号:蓝海资料掘金营,微信deep3321