0 鱼眼图像
鱼眼图像是一种特殊的宽视角成像方式,广泛应用于监控、全景摄影、机器人视觉等领域。鱼眼镜头的视角通常在 180° 或更大,远超普通镜头,因此可以捕捉到更大范围的场景。要做到这一点,鱼眼图像通常以非线性方式将场景投影到图像平面上。直线物体在图像中通常被呈现为弯曲的曲线。
图1是一张普通的鱼眼图像,可以看出鱼眼图像通常具有显著的圆形边界,中心区域接近真实场景,边缘部分则畸变严重。(刘冲的博客blog.popkx.com原创,偷文可耻)
1 鱼眼镜头的投影模型
在鱼眼相机中,为了将光线成像于一个平面上,一般会使用包含凸透镜、凹透镜和滤光镜在内的多种光学透镜,以减小入射光线的入射角。图1展示了鱼眼透镜(美国专利4,412,726)的布局以及光线复杂的入射或折射关系。
可以看出,光线的入射角经过透镜组后变小了,因此鱼眼镜头可以拍摄视场角更大的图像。鱼眼镜头的投影模型总体可以按照图3(b)简化:
设
-
i. 透视投影
r = f*tan(\theta) ,针孔成像模型 - ii. 正交投影
r = f*sin(\theta) ,将光线沿着球面投影到平面,适合小视角 - iii. 等距投影
r = f * \theta ,投影的半径与光线的入射角度成比例 - iv. 立体投影
r = 2f*tan(\frac{\theta}{2}) ,常用于全景摄影和球面投影 - v. 等面积投影
r = 2f*sin(\frac{\theta}{2}) ,保持像素点的面积与球面面积成比例
在应用中,精心设计的镜头并不完全遵循设计的投影模型,实际成像过程可以看作理想投影的近似,既然是近似,我们可以找到一种更通用的近似:
r(\theta) = k_1\theta + k_2\theta ^ 3 + k_3 \theta ^5 + k_4 \theta ^7 + ...
2 鱼眼图像矫正
要对鱼眼图像矫正,需要先了解镜头的基本成像过程。我们回顾图3(b),在针孔成像过程中,点
可以想象,如果我们将成像点
设
其中
通常,无论是针孔成像过程,还是鱼眼成像过程,都可以认为是各向同性的,所谓各向同性,可以粗略理解为:“像素坐标沿横轴的变化量等于纵轴的变化量”。因此有
注意,图4并不是
xOy
平面,xOy
平面与成像平面平行。
俯视主平面,如图5所示:
公式(3) 的意义是显然的——点
结合公式 (1)(2)(4),可得以下关系:
公式(5)构建了成像平面与成像曲面之间像素坐标的关系,这是本文执行鱼眼图像矫正的理论基础。
3 工程实现
第2节推导出的公式(5)已经是非常具体的方案,在具体实现中,可以先构建矫正后的图像,图像的像素按照坐标依次根据公式(5)换算到源鱼眼图像像素坐标,查询填入,简单的 Python 实现代码如下:
import numpy as np
def decode_fisheye(src, size, plane_focal_len, func_fisheye_model, func_get_pixel):
h, w = src.shape[:2]
dst_w, dst_h = size
src_center_x, src_center_y = w / 2, h / 2
dst_center_x, dst_center_y = dst_w / 2, dst_h / 2
dst = np.zeros((dst_h, dst_w, src.shape[2]), dtype=src.dtype)
for dst_y in range(dst_h):
dst_row = dst[dst_y]
dst_dy = dst_y - dst_center_y
for dst_x in range(dst_w):
dst_dx = dst_x - dst_center_x
dst_l = np.sqrt(dst_dx**2 + dst_dy**2)
theta = np.arctan(dst_l / plane_focal_len)
src_l = func_fisheye_model(theta)
k = 0.0 if dst_l == 0 else src_l / dst_l
src_dx, src_dy = k * dst_dx, k * dst_dy
src_x = src_center_x + src_dx
src_y = src_center_y + src_dy
func_get_pixel(dst_row, dst_x, src, src_x, src_y)
return dst
func_fisheye_model()
为鱼眼成像的数学模型,例如选择第1节中介绍的几种模型之一。func_get_pixel()
是查询填入
过程使用的,可以使用最近邻方法或者双线性方法。为了方便使用,可以进一步封装:
class FisheyeCorrection(object):
def __init__(self, cfg_dict: dict):
self.plane_focal_len = cfg_dict['plane_focal_len']
self.fisheye_focal_len = cfg_dict['fisheye_focal_len']
def correct(self, img, output_size=(800, 600)):
corrected_image = decode_fisheye(
img, output_size, self.plane_focal_len, self.__fisheye_model, bilinear_interpolation)
return corrected_image
def __fisheye_model(self, theta):
# common models
# r = 2 * f * tan(theta / 2)
# r = f * theta
# r = 2 * f * sin(theta / 2)
# r = f * sin(theta)
return self.fisheye_focal_len * 2 * np.sin(theta / 2.0)
其中 bilinear_interpolation()
为双线性插值方法:
def bilinear_interpolation(dst_row, dst_x, src, src_x, src_y):
h, w, c = src.shape
if np.isnan(src_x) or np.isnan(src_y) or src_x < 0 or src_x >= w - 1 or src_y < 0 or src_y >= h - 1:
dst_row[dst_x] = 0
return
left = int(np.floor(src_x))
top = int(np.floor(src_y))
w_right = src_x - left
w_bottom = src_y - top
w_left = 1 - w_right
w_top = 1 - w_bottom
weights = np.array([[w_left * w_top, w_right * w_top],
[w_left * w_bottom, w_right * w_bottom]])
values = np.zeros(c)
for dy in range(2):
y = top + dy
for dx in range(2):
x = left + dx
weight = weights[dy, dx]
values += weight * src[y, x]
dst_row[dst_x] = values.astype(src.dtype)
编写测试代码:
import cv2
...
def test():
fisheye_correction_dict = {
'plane_focal_len': 120,
'fisheye_focal_len': 500
}
undistorted_img_size = (600, 400)
fe_corr = FisheyeCorrection(cfg_dict=fisheye_correction_dict)
img = cv2.imread('./test_fisheye.jpg')
if img is None:
print('img is empty.')
return
und_img = fe_corr.correct(img, undistorted_img_size)
cv2.imshow('corrected', und_img)
cv2.waitKey(0)
if __name__ == '__main__':
test()
运行结果如图6所示,可见,鱼眼图像总体被拉平了,调整成像平面焦距和成像曲面焦距可以调整矫正程度。但是无论怎样调整,靠近图像边缘的内容畸变较大,例如黄框中的猪明显变形,这说明我们使用的鱼眼成像模型不够精确。(刘冲的博客blog.popkx.com原创,偷文可耻)
针对矫正后鱼眼图像边缘畸变较大的问题,倒是可以做一些弥补工作:
输入邮箱或者手机号码,付款后可永久阅读隐藏的内容,请勿未经本站许可,擅自分享付费内容。
如果您已购买本页内容,输入购买时的手机号码或者邮箱,点击支付,即可查看内容。
电子商品,一经购买,不支持退款,特殊原因,请联系客服。 付费可读
运行后,结果如图7所示,可见,边缘畸变被抑制了。
参考
[1] Approximate model of fisheye camera based on the optical refraction
[2] A Generic Camera Model and Calibration Method for Conventional, Wide-Angle, and Fish-Eye Lenses
[3] 博客