1. 鱼眼图像矫正到底在解决什么问题
第一次拿到全景鱼眼摄像机的原始画面时,很多人的反应都是"这能用?"——画面中心还算正常,越往边缘越像被揉皱的纸,直线全变成弧线,墙角扭曲得像哈哈镜。这不是摄像机坏了,而是鱼眼镜头为了获得超大视场角必须付出的代价。一颗典型的鱼眼镜头视场角可以做到180度甚至360度,代价就是极度的桶形畸变。图像矫正核心算法要干的事情,就是把这团"揉皱的画面"重新展开成符合人眼习惯的平面图像,或者映射成可以自由转动的虚拟PTZ画面。
这个领域涉及的技术栈其实相当深:从最基础的畸变模型建立,到病态矩阵的数值求解,再到虚拟PTZ的实时映射,每一环都有坑。我做过几个安防和会议场景的全景摄像机项目,从最初用OpenCV的initUndistortRectifyMap硬怼,到后来自己推导映射表做GPU加速,中间踩的坑足够写一本小册子。这篇文章就把整个链路拆开讲清楚,包括畸变模型怎么选、参数怎么标定、病态矩阵为什么会出现以及怎么绕过去、虚拟PTZ的映射表怎么生成、实时性怎么保证。
适合谁看?如果你正在做全景监控、视频会议、机器人视觉、或者任何需要处理鱼眼画面的项目,这篇文章能帮你少走至少两三个月的弯路。如果你只是好奇鱼眼矫正的原理,也能从里面搞明白那些"展开"按钮背后到底发生了什么。我会尽量用生活化的类比把数学讲清楚,同时给出可以直接抄的代码和参数。
先说一个核心认知:鱼眼矫正不是一个算法,而是一条流水线。标定、建模、映射、插值、加速,每一环都会影响最终画质和帧率。很多人只关注"用哪个函数",结果发现矫正完边缘模糊、锯齿严重、帧率掉到个位数,问题往往出在整条链路的配合上,而不是某一个函数用错了。
2. 鱼眼畸变模型与标定方案选型
2.1 为什么不能简单用径向畸变模型
普通镜头用Brown-Conrady模型就够了,两三个径向系数加两个切向系数,k1, k2, p1, p2基本能描述清楚。但鱼眼镜头不行,它的畸变太剧烈了,用多项式去拟合会在边缘产生严重的振荡。你可以这样理解:普通镜头的畸变像轻微驼背,用一根稍微弯的尺子就能量;鱼眼镜头的畸变像把一根弹簧压扁,你用低阶多项式去描述它,边缘必然失真。
所以鱼眼镜头通常用专门的模型,主流的有四种:等距投影(equidistant)、等立体角投影(equisolid angle)、正交投影(orthographic)、体视投影(stereographic)。它们描述的是入射角θ和成像半径r之间的关系。等距投影是r = f·θ,等立体角是r = 2f·sin(θ/2),正交是r = f·sin(θ),体视是r = 2f·tan(θ/2)。
选哪个取决于你的镜头设计。大部分安防鱼眼镜头用的是等距投影,因为它在整个视场内的分辨率分布相对均匀。我实测过几款主流180度鱼眼,用等距模型标定后重投影误差能控制在0.3像素以内,换成Brown-Conrady模型误差直接飙到2像素以上,边缘完全没法看。
2.2 标定实操:从棋盘格到参数
标定这一步是整个矫正的地基,地基歪了后面全白搭。我用的是张正友标定法的鱼眼扩展版,OpenCV里对应cv2.fisheye.calibrate。具体流程是这样的:
准备一块棋盘格标定板,建议用大尺寸的,比如10x7格、格子边长30mm以上,因为鱼眼视场大,小标定板在边缘区域占的像素太少,角点检测精度不够。拍摄15到25张不同角度的照片,关键是要覆盖整个视场,特别是边缘区域。我见过有人只拍中间区域,标定出来的参数在边缘完全失效。
拍摄时有个技巧:让标定板在画面边缘也保持清晰,因为鱼眼边缘的畸变最严重,那里的角点信息对参数求解最重要。同时要保证标定板有各种倾斜角度,不能全是正对镜头的。
标定代码大致长这样:
import cv2 import numpy as np # 棋盘格参数 pattern_size = (10, 7) square_size = 0.03 # 米 # 准备对象点 objp = np.zeros((1, pattern_size[0]*pattern_size[1], 3), np.float32) objp[0,:,:2] = np.mgrid[0:pattern_size[0], 0:pattern_size[1]].T.reshape(-1, 2) * square_size objpoints = [] imgpoints = [] # 读取所有标定图,检测角点 for fname in image_list: img = cv2.imread(fname) gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) ret, corners = cv2.findChessboardCorners(gray, pattern_size, cv2.CALIB_CB_ADAPTIVE_THRESH + cv2.CALIB_CB_FAST_CHECK) if ret: corners = cv2.cornerSubPix(gray, corners, (11,11), (-1,-1), (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.01)) objpoints.append(objp) imgpoints.append(corners) # 鱼眼标定 K = np.zeros((3, 3)) D = np.zeros((4, 1)) rvecs = [np.zeros((1, 1, 3), dtype=np.float64) for _ in range(len(objpoints))] tvecs = [np.zeros((1, 1, 3), dtype=np.float64) for _ in range(len(objpoints))] flags = cv2.fisheye.CALIB_RECOMPUTE_EXTRINSIC + cv2.fisheye.CALIB_FIX_SKEW + cv2.fisheye.CALIB_CHECK_COND criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 100, 1e-6) ret, K, D, rvecs, tvecs = cv2.fisheye.calibrate( objpoints, imgpoints, gray.shape[::-1], K, D, rvecs, tvecs, flags=flags, criteria=criteria)这里有个关键点:cv2.fisheye.CALIB_CHECK_COND这个标志一定要加。它会在求解时检查条件数,如果矩阵病态会直接报错,而不是给你一个看似正常实则完全错误的解。我早期做项目时没加这个标志,标定出来的参数在某些角度下矫正结果完全错乱,排查了两天才发现是病态矩阵导致的。
2.3 病态矩阵:鱼眼标定的隐形杀手
"病态矩阵"这个词最近在圈子里被提得很多,因为它确实是鱼眼标定最容易翻车的地方。什么是病态矩阵?简单说就是矩阵的条件数太大,输入有微小扰动,输出就剧烈变化。在鱼眼标定里,当标定板姿态过于单一、或者角点分布不均匀时,求解用的雅可比矩阵就会接近奇异,这时候求出来的参数看着数值正常,实际用起来完全不对。
怎么判断?看标定返回的ret(重投影误差)不够,还要看条件数。OpenCV的cv2.fisheye.calibrate在加了CALIB_CHECK_COND后,如果条件数超标会抛异常。更稳妥的做法是自己算一下雅可比矩阵的条件数,超过1e6就要警惕了。
规避病态矩阵的核心思路是让标定数据"多样化"。具体来说:标定板要覆盖整个视场,包括四个角和边缘;姿态要多样,有正对、有倾斜、有旋转;数量要够,至少15张有效图。我个人的经验是,如果条件数一直降不下来,往往是标定板太小或者拍摄距离太近,导致边缘区域的角点信息不足。
还有一个隐蔽的坑:鱼眼镜头的光心和机械中心往往不重合。如果标定时假设光心在图像正中心,而实际有偏移,也会导致矩阵病态。解决办法是在标定前先粗略估计光心位置,或者用CALIB_FIX_PRINCIPAL_POINT先固定主点,标定完再放开优化。
3. 从畸变图到平面图:映射表生成与插值
3.1 反向映射才是正解
矫正图像有两种思路:正向映射和反向映射。正向映射是把畸变图的每个像素算到矫正图的位置,问题是矫正图里会有空洞,因为畸变图边缘像素稀疏,映射过去覆盖不满。反向映射是遍历矫正图的每个像素,反算它在畸变图里的位置,然后采样。这样矫正图每个像素都有值,不会出现空洞。
所以工程上几乎都用反向映射。核心就是生成两张映射表:map_x和map_y,分别记录矫正图每个像素对应的畸变图坐标。生成一次可以反复用,只要相机参数不变。
生成映射表的逻辑是这样的:对于矫正图的每个像素(u, v),先把它转换到归一化平面坐标(x, y),然后计算入射角θ = atan(sqrt(x²+y²)),再根据鱼眼模型算出畸变图的半径r,最后结合主点得到畸变图坐标。
def generate_undistort_map(K, D, img_size, balance=0.0, fov_scale=1.0): h, w = img_size map_x = np.zeros((h, w), np.float32) map_y = np.zeros((h, w), np.float32) fx, fy = K[0,0], K[1,1] cx, cy = K[0,2], K[1,2] # 估计新的焦距,balance控制保留多少边缘 new_fx = fx * fov_scale new_fy = fy * fov_scale new_cx = w / 2 new_cy = h / 2 for v in range(h): for u in range(w): x = (u - new_cx) / new_fx y = (v - new_cy) / new_fy r = np.sqrt(x*x + y*y) theta = np.arctan(r) # 等距投影模型 theta_d = theta * (1 + D[0]*theta**2 + D[1]*theta**4 + D[2]*theta**6 + D[3]*theta**8) if r > 1e-8: scale = theta_d / r else: scale = 1.0 map_x[v, u] = x * scale * fx + cx map_y[v, u] = y * scale * fy + cy return map_x, map_y这段代码是CPU版本,实际用的时候要改成向量化或者GPU,不然1080P的图生成一次映射表要好几秒。向量化就是把双重循环改成numpy的矩阵运算,速度能提升几十倍。
3.2 balance参数:视野和画质的权衡
OpenCV的鱼眼矫正里有个balance参数,很多人不知道它是干嘛的。它控制的是矫正后图像保留多少原始视野。balance=0时只保留能完全填充矫正图的有效区域,画面没有黑边但视野损失大;balance=1时保留全部视野,但四周会有大量黑边。
实际项目里这个参数要根据场景调。监控场景通常希望视野尽量大,可以设balance=0.5左右,接受少量黑边。如果做虚拟PTZ,那必须保留全部视野,因为PTZ转动时会用到边缘区域,这时候balance=1,黑边在后续处理里裁掉。
我一般会先算一下不同balance下的有效视野占比,然后根据实际需求选。有个经验值:180度鱼眼在balance=0.5时,矫正后水平视野大概剩120度左右,垂直视野90度左右。这个数据对规划监控覆盖范围很有用。
3.3 插值方法的选择
反向映射算出来的坐标是浮点数,需要插值。OpenCV的remap支持最近邻和双线性插值。最近邻快但有锯齿,双线性平滑但边缘会糊。鱼眼矫正里我推荐用双线性,因为畸变图边缘像素稀疏,最近邻会产生明显的马赛克。
如果对画质要求极高,可以用双三次插值,但要自己实现,OpenCV的remap不支持。双三次在边缘区域的细节保留明显更好,代价是计算量大约是双线性的4倍。我做过对比,在4K分辨率下双三次比双线性PSNR高1.5dB左右,但帧率从60掉到25。所以除非是离线处理,实时场景还是双线性。
还有个细节:插值时要注意边界处理。畸变图边缘之外的坐标要返回黑色或者做边缘复制,不然会出现奇怪的条纹。remap默认用BORDER_CONSTANT,填0就是黑色,这个一般够用。
4. 虚拟PTZ:让全景画面动起来
4.1 虚拟PTZ的本质是坐标变换
虚拟PTZ(Pan-Tilt-Zoom)是全景摄像机最实用的功能。物理上摄像机不动,但通过算法从全景图里"切"出一块区域,模拟出转动和变焦的效果。它的本质是球面坐标到平面坐标的映射:全景图对应一个球面,PTZ参数决定视线方向,然后把视线锥体内的球面区域投影到平面上。
具体来说,给定PTZ的偏航角yaw、俯仰角pitch、视场角fov,可以构造一个虚拟相机。虚拟相机的内参由fov决定,外参由yaw和pitch决定。然后对于虚拟相机成像平面的每个像素,反算它在球面上的方向,再映射到全景图的对应位置。
这个映射可以预计算成查找表。因为PTZ参数是连续变化的,不可能每个角度都预计算,所以通常的做法是预计算一个高分辨率的球面映射表,然后根据PTZ参数做插值。或者用GPU实时计算,现代GPU算这个绰绰有余。
4.2 映射表的生成与优化
虚拟PTZ的映射表生成比矫正映射表复杂,因为它涉及三维旋转。核心步骤是:
- 根据
fov确定虚拟相机的焦距f = (W/2) / tan(fov/2) - 构造旋转矩阵
R,由yaw和pitch决定 - 对虚拟图像每个像素
(u, v),计算归一化坐标(x, y, 1) - 用
R旋转得到球面方向 - 把球面方向映射到全景图的像素坐标
def generate_ptz_map(yaw, pitch, fov, out_size, src_size): W, H = out_size src_w, src_h = src_size f = (W / 2) / np.tan(np.radians(fov / 2)) # 旋转矩阵 Ry = np.array([[np.cos(yaw), 0, np.sin(yaw)], [0, 1, 0], [-np.sin(yaw), 0, np.cos(yaw)]]) Rx = np.array([[1, 0, 0], [0, np.cos(pitch), -np.sin(pitch)], [0, np.sin(pitch), np.cos(pitch)]]) R = Rx @ Ry map_x = np.zeros((H, W), np.float32) map_y = np.zeros((H, W), np.float32) for v in range(H): for u in range(W): # 虚拟相机坐标 x = (u - W/2) / f y = (v - H/2) / f z = 1.0 # 旋转到世界坐标 vec = R @ np.array([x, y, z]) vec = vec / np.linalg.norm(vec) # 球面坐标转全景图像素 theta = np.arccos(vec[2]) phi = np.arctan2(vec[1], vec[0]) # 映射到全景图(假设全景图是等距投影) map_x[v, u] = (phi / (2*np.pi) + 0.5) * src_w map_y[v, u] = (theta / np.pi) * src_h return map_x, map_y这段代码是原理演示,实际用的时候要处理全景图的投影方式。如果全景图是矫正后的等距柱状投影(equirectangular),上面的映射就对了。如果全景图还是原始鱼眼图,那映射要复杂一些,需要先经过鱼眼模型。
4.3 实时性优化:从CPU到GPU
虚拟PTZ对实时性要求很高,用户拖动画面时延迟超过100ms就能感觉到卡顿。CPU版本生成一张1080P的PTZ映射表要几百毫秒,完全不够用。优化路径有几条:
第一条是用查找表加插值。预计算一组离散PTZ角度下的映射表,比如yaw每5度一张,pitch每5度一张,总共几千张。运行时根据实际角度找最近的两张做插值。这样每帧只需要做插值,速度很快。缺点是内存占用大,而且角度分辨率有限。
第二条是用GPU实时计算。把映射计算写成CUDA或者OpenGL shader,每帧在GPU上算。现代GPU算这个就是几毫秒的事。我用OpenGL做过,1080P输出在集显上也能跑到60帧。核心是把映射逻辑写成fragment shader,输入是PTZ参数,输出是采样坐标。
第三条是混合方案。低分辨率预览用CPU查找表,高分辨率输出用GPU。这样兼顾了响应速度和画质。
我实际项目里用的是GPU方案,因为现在的设备基本都有GPU,而且GPU方案最灵活,PTZ参数可以连续变化,没有角度分辨率限制。唯一要注意的是GPU和CPU之间的数据传输,映射表如果每帧都要从GPU读回CPU再传给显示,那带宽会成为瓶颈。解决办法是直接在GPU上完成采样和显示,不经过CPU。
5. 常见问题与排查技巧实录
5.1 矫正后边缘模糊怎么办
这是最常见的问题。原因通常有三个:一是畸变图边缘本身分辨率就低,鱼眼镜头边缘的像素密度远低于中心,矫正后相当于把稀疏的像素拉伸,自然模糊;二是插值方法太简单,双线性在拉伸比例大的地方会糊;三是映射表精度不够,用了float16或者整数坐标。
解决办法:首先接受一个事实,鱼眼边缘的画质天花板就在那里,不可能矫正得和中心一样清晰。能做的是尽量保留细节。用双三次插值,映射表用float32,如果可能的话用超分辨率算法补一下。我试过用ESRGAN对矫正后的边缘区域做增强,效果确实有提升,但计算量太大,实时场景不现实。
另一个思路是不要矫正到平面,而是矫正到柱面或者球面。柱面投影在水平方向的拉伸比平面投影小,边缘模糊会轻一些。很多全景摄像机默认输出就是柱面投影,就是这个原因。
5.2 画面出现奇怪的波纹或条纹
这通常是映射表的精度问题。如果映射表用整数存储,或者计算过程中有舍入误差,就会出现周期性的条纹。排查方法是把映射表可视化出来,看有没有明显的阶梯或者跳变。
还有一种可能是插值的边界处理不对。畸变图边缘之外的坐标如果没处理好,采样时会取到错误的像素,形成条纹。检查remap的borderMode参数,确保是BORDER_CONSTANT。
我遇到过一次特别隐蔽的:映射表生成时用了np.float32,但某个中间计算用了np.float64,结果精度不一致导致边缘出现摩尔纹。统一用float32就好了。这种问题很难从现象反推原因,只能靠经验。
5.3 虚拟PTZ转动时画面抖动
抖动通常来自两个方面:一是映射表插值的角度分辨率不够,PTZ参数连续变化但映射表是离散的,插值不连续就会抖;二是GPU计算的浮点精度问题,某些角度下旋转矩阵接近奇异。
解决办法:如果是查找表方案,提高角度分辨率,或者改用球面插值而不是线性插值。如果是GPU方案,检查旋转矩阵的构造,确保yaw和pitch的计算顺序正确。我遇到过一次是yaw和pitch的旋转顺序搞反了,导致某些角度下画面跳变,改成先yaw后pitch就正常了。
还有一个容易忽略的点:PTZ参数更新频率和显示刷新率不同步。如果PTZ参数每10ms更新一次,但显示每16ms刷新一次,就会出现一帧用新参数一帧用旧参数的情况,看起来就是抖动。解决办法是做参数平滑,或者用固定时间步长更新。
5.4 标定重投影误差很小但矫正效果很差
这种情况最让人抓狂,因为所有指标都正常,但结果就是不对。原因通常是标定时的假设和实际使用场景不一致。比如标定时假设镜头是等距投影,但实际镜头是等立体角投影,重投影误差可能还是很小,因为标定板覆盖的区域有限,两种模型在中心区域差异不大,但边缘区域差异巨大。
排查方法是把矫正后的图像和预期对比,特别是边缘区域。如果边缘的直线还是弯的,说明模型选错了。解决办法是换模型重新标定,或者用非参数化的方法,直接学习畸变映射。
还有一种可能是标定时的图像分辨率和实际使用的不一致。如果标定用1080P,实际用4K,内参需要按比例缩放。这个缩放不是简单乘个系数就行,因为主点位置也会变。正确做法是用相同分辨率标定,或者标定后重新计算内参。
5.5 常见问题速查表
| 问题现象 | 可能原因 | 排查方法 | 解决方案 |
|---|---|---|---|
| 边缘模糊 | 边缘像素密度低、插值简单 | 对比中心和边缘的清晰度 | 双三次插值、超分辨率增强 |
| 画面条纹 | 映射表精度不足、边界处理错误 | 可视化映射表 | 统一float32、检查borderMode |
| PTZ抖动 | 角度分辨率不足、旋转顺序错误 | 检查旋转矩阵构造 | 提高分辨率、修正旋转顺序 |
| 矫正效果差 | 模型选错、分辨率不一致 | 对比边缘直线 | 换模型、统一分辨率 |
| 帧率低 | CPU计算、映射表未预计算 | 性能分析 | GPU加速、预计算查找表 |
| 病态矩阵报错 | 标定数据单一、标定板太小 | 检查条件数 | 增加数据多样性、换大标定板 |
6. 工程落地中的几个关键决策
6.1 矫正放在前端还是后端
这是个架构问题。如果摄像机本身有算力,可以在前端做矫正,输出矫正后的画面,后端直接显示。优点是后端简单,带宽需求低(矫正后画面比原始鱼眼图小)。缺点是前端算力有限,矫正质量可能受限,而且一旦矫正参数需要调整,要重新烧录固件。
如果放在后端,前端输出原始鱼眼图,后端做矫正。优点是灵活,参数随时可调,可以用更强的算力做高质量矫正。缺点是带宽需求大,原始鱼眼图通常比矫正后大不少。
我一般推荐后端矫正,因为灵活性太重要了。实际项目里矫正参数经常需要微调,前端矫正的话每次都要重新部署,太麻烦。而且现在网络带宽普遍够用,多传一点原始数据不是大问题。
6.2 单目和双目怎么选
单目鱼眼能覆盖180度,双目背靠背能覆盖360度。如果只需要180度视野,单目就够了,成本和复杂度都低。如果需要360度,双目是标配,但要注意两个镜头的拼接。
拼接是个大坑。两个鱼眼镜头的光心不可能完全重合,拼接处会有视差。近距离物体在拼接处会出现重影。解决办法是拼接缝选在视差小的方向,或者用深度信息做补偿。我做过一个会议室的全景摄像机,拼接缝放在正后方,因为主要关注的是前方的人,后方重影影响不大。
如果预算允许,三目或者四目方案能进一步减少拼接问题,但成本和标定复杂度直线上升。一般场景双目够用。
6.3 分辨率怎么选
鱼眼矫正有个特点:矫正后的有效分辨率远低于原始分辨率。因为边缘区域被拉伸,原始像素被稀释。一个4K的鱼眼图,矫正后中心区域可能还有2K的有效分辨率,边缘区域可能只剩1K甚至更低。
所以选分辨率时要考虑矫正后的有效分辨率。如果最终输出需要1080P,那原始鱼眼图至少要4K,最好6K。我做过测试,4K鱼眼矫正到1080P输出,边缘区域的MTF(调制传递函数)已经明显下降,6K的话就好很多。
但分辨率越高,计算量越大。6K的鱼眼图做实时矫正,GPU压力不小。要在画质和性能之间找平衡。我的经验是,监控场景4K够用,视频会议6K起步,工业检测8K以上。
6.4 标定多久做一次
镜头参数会随温度、震动、老化漂移。高精度场景可能需要每天标定,普通监控场景几个月标定一次就行。有个简单的判断方法:如果矫正后的画面里,原本应该是直线的物体开始变弯,就该重新标定了。
自动标定是个方向。用场景中的直线特征(比如门框、窗框)做在线标定,不需要标定板。但实现难度大,对场景有要求。我试过用消失点做自动标定,在室内场景效果还行,室外场景就不太稳定。
7. 我踩过的几个印象深刻的坑
第一个坑是标定板的材质。我用过打印的纸质标定板,结果发现纸张不平整,角点检测精度很差。后来换成铝基板的标定板,精度立刻上去了。标定板一定要平整,这是基础中的基础。
第二个坑是镜头的红外截止滤光片。有些鱼眼镜头为了日夜两用,红外截止滤光片是可切换的。白天和夜间的光路不一样,畸变参数也不同。如果只标定白天,夜间矫正就会偏。解决办法是分别标定,或者用对红外不敏感的标定方法。
第三个坑是GPU的浮点精度。我用OpenGL做PTZ映射时,默认用的是mediump精度,结果在某些角度下画面出现明显的块状伪影。改成highp就好了。这个坑很隐蔽,因为大部分角度下mediump看起来没问题,只有特定角度才暴露。
第四个坑是映射表的缓存。我早期做的时候每次PTZ变化都重新生成映射表,结果帧率很低。后来改成预计算一组基础映射表,运行时插值,帧率立刻上去了。这个优化思路其实很通用:把能预计算的都预计算,运行时只做必要的计算。
第五个坑是色彩处理。矫正后的图像如果直接显示,色彩会偏暗,因为插值时边缘像素被平均了。需要在矫正后做一次色彩校正,或者用更保边的插值方法。我一般会在矫正后加一个轻微的锐化和色彩增强,视觉效果好很多。
8. 后续可以扩展的方向
鱼眼矫正这个领域还有很多可以深挖的地方。比如基于深度学习的矫正,用神经网络直接学习畸变映射,不需要显式的模型。这种方法在极端畸变下可能比传统方法更好,但需要大量训练数据,而且推理速度是个问题。
还有实时超分辨率结合矫正,在矫正的同时做超分,把边缘区域的细节补回来。这个方向学术界有不少论文,工程落地还不多,主要是计算量太大。
虚拟PTZ的智能化也是个方向。现在PTZ参数是人工控制的,未来可以根据画面内容自动调整,比如自动跟踪运动目标,或者根据场景自动选择最佳视角。这需要结合目标检测和跟踪算法。
最后一个方向是多相机拼接的优化。双目或者多目全景的拼接缝处理还有很多改进空间,特别是动态场景下的拼接。用光流做拼接缝的动态调整,能显著减少重影。
这些方向我都在关注,有些已经在小范围试了。等有成熟的结果再单独写文章分享。鱼眼矫正看起来是个老问题,但实际做起来每个环节都有新东西,值得持续投入。