当前位置:   article > 正文

鱼眼相机去畸变(图像拉直/展开/矫正)算法及实战总结_鱼眼矫正算法

鱼眼矫正算法

本文介绍两种方法

1、经纬度矫正法

2、棋盘格矫正法

一、经纬度矫正法

1、算法说明

经纬度矫正法, 可以把鱼眼图想象成半个地球, 然后将地球展开成地图,经纬度矫正法主要是利用几何原理, 对图像进行展开矫正。

        经过P点的入射光线没有透镜的话,本应交于相机成像平面的e点。然而,经过鱼眼相机的折射,光线会交于相机成像平面的d点,就产生了畸变,因此畸变图像整体上呈现出像素朝图像中心点聚集的态势。

        而去畸变,就是将折射到d点的点,重新映射回到e点,因此去畸变之后的图像与原始的鱼眼图像相比,仿佛是把向心聚集的像素又重新向四周铺展开来。

       详细的推导流程及公式见地址:AVM环视系统——鱼眼相机去畸变算法 - 知乎

2、 代码

  1. import math
  2. from PIL import Image
  3. im = Image.open("/Users/Fisheye_photo-600x600.jpg")
  4. im.show()
  5. width, high = im.size
  6. sqrt_len = min(width, high)
  7. im = im.transform((sqrt_len, sqrt_len),
  8. Image.EXTENT,
  9. ((width-sqrt_len)/2, (high-sqrt_len)/2,
  10. sqrt_len+(width-sqrt_len)/2, sqrt_len+(high-sqrt_len)/2)
  11. )
  12. width = high = sqrt_len
  13. idata = im.getdata()
  14. odata = []
  15. alpha = math.pi/2
  16. out_high = round(high * math.tan(alpha/2))
  17. out_width = round(width * math.tan(alpha/2))
  18. out_radius = round(high * math.tan(alpha/2))
  19. out_center_x = out_width / 2
  20. out_center_y = out_high / 2
  21. out_bl_x = 0
  22. out_br_x = out_width - 1
  23. out_bt_y = 0
  24. out_bb_y = out_high - 1
  25. out_bl_cx = out_bl_x - out_center_x
  26. out_br_cx = out_br_x - out_center_x
  27. out_bt_cy = out_bt_y - out_center_y
  28. out_bb_cy = out_bb_y - out_center_y
  29. src_radius = round(high * math.sin(alpha/2))
  30. src_center_x = width / 2
  31. src_center_y = high / 2
  32. for i in range(0, high * width):
  33. ox = math.floor(i / out_width)
  34. oy = i % out_high
  35. cx = ox - out_center_x;
  36. cy = oy - out_center_y;
  37. out_distance = round(math.sqrt(pow(cx, 2) + pow(cy, 2)))
  38. theta = math.atan2(cy, cx)
  39. if (-math.pi/4 <= theta <= math.pi/4):
  40. bx = out_radius * math.cos(math.pi/4)
  41. by = bx * math.tan(theta)
  42. elif (math.pi/4 <= theta <= math.pi*3/4):
  43. by = out_radius * math.sin(math.pi/4)
  44. bx = by / math.tan(theta)
  45. elif (-math.pi*3/4 <= theta <= -math.pi/4):
  46. by = out_radius * math.sin(-math.pi/4)
  47. bx = by / math.tan(theta)
  48. else:
  49. bx = out_radius * math.cos(-math.pi*3/4)
  50. by = bx * math.tan(theta)
  51. bdy_distance = round(math.sqrt(pow(cx, 2) + pow(cy, 2)))
  52. src_distance = src_radius * bdy_distance / out_radius
  53. src_x = round(src_center_x + math.cos(theta) * src_distance)
  54. src_y = round(src_center_y + math.sin(theta) * src_distance)
  55. src_idx = src_x*width + src_y
  56. if(0 < src_idx < high*width):
  57. odata.append(idata[src_idx])
  58. else:
  59. odata.append((0,0,0))
  60. om = Image.new("RGB", (high, width))
  61. om.putdata(odata)
  62. om.show()

3、代码及图片地址:GitHub - duducosmos/defisheye: Fast Corrects for fisheye distortion in an image.

二、棋盘格矫正方法

1、算法说明

利用棋盘格进行标定, 然后计算鱼眼镜头的畸变系数以及内参, opencv中自带有fisheye模块, 可以直接根据棋盘格标定结果,采用cv2.fisheye.calibrate计算畸变系数以及内参, 然后使用cv2.fisheye.initUndistortRectifyMap函数计算映射矩阵, 最后根据映射矩阵, 使用cv2.remap进行矫正。

2、代码

  1. import cv2
  2. import numpy as np
  3. import math
  4. import time
  5. # 鱼眼有效区域截取
  6. def cut(img):
  7. img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
  8. (_, thresh) = cv2.threshold(img_gray, 20, 255, cv2.THRESH_BINARY)
  9. contours, hierarchy = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
  10. cnts = sorted(contours, key=cv2.contourArea, reverse=True)[0]
  11. x,y,w,h = cv2.boundingRect(cnts)
  12. r = max(w/ 2, h/ 2)
  13. # 提取有效区域
  14. img_valid = img[y:y+h, x:x+w]
  15. return img_valid, int(r)
  16. # 鱼眼矫正
  17. def undistort(src,r):
  18. # r: 半径, R: 直径
  19. R = 2*r
  20. # Pi: 圆周率
  21. Pi = np.pi
  22. # 存储映射结果
  23. dst = np.zeros((R, R, 3))
  24. src_h, src_w, _ = src.shape
  25. # 圆心
  26. x0, y0 = src_w//2, src_h//2
  27. for dst_y in range(0, R):
  28. theta = Pi - (Pi/R)*dst_y
  29. temp_theta = math.tan(theta)**2
  30. for dst_x in range(0, R):
  31. # 取坐标点 p[i][j]
  32. # 计算 sita 和 fi
  33. phi = Pi - (Pi/R)*dst_x
  34. temp_phi = math.tan(phi)**2
  35. tempu = r/(temp_phi+ 1 + temp_phi/temp_theta)**0.5
  36. tempv = r/(temp_theta + 1 + temp_theta/temp_phi)**0.5
  37. if (phi < Pi/2):
  38. u = x0 + tempu
  39. else:
  40. u = x0 - tempu
  41. if (theta < Pi/2):
  42. v = y0 + tempv
  43. else:
  44. v = y0 - tempv
  45. if (u>=0 and v>=0 and u+0.5<src_w and v+0.5<src_h):
  46. dst[dst_y, dst_x, :] = src[int(v+0.5)][int(u+0.5)]
  47. # 计算在源图上四个近邻点的位置
  48. # src_x, src_y = u, v
  49. # src_x_0 = int(src_x)
  50. # src_y_0 = int(src_y)
  51. # src_x_1 = min(src_x_0 + 1, src_w - 1)
  52. # src_y_1 = min(src_y_0 + 1, src_h - 1)
  53. #
  54. # value0 = (src_x_1 - src_x) * src[src_y_0, src_x_0, :] + (src_x - src_x_0) * src[src_y_0, src_x_1, :]
  55. # value1 = (src_x_1 - src_x) * src[src_y_1, src_x_0, :] + (src_x - src_x_0) * src[src_y_1, src_x_1, :]
  56. # dst[dst_y, dst_x, :] = ((src_y_1 - src_y) * value0 + (src_y - src_y_0) * value1 + 0.5).astype('uint8')
  57. return dst
  58. if __name__ == "__main__":
  59. t = time.perf_counter()
  60. frame = cv2.imread('../imgs/pig.jpg')
  61. cut_img,R = cut(frame)
  62. result_img = undistort(cut_img,R)
  63. cv2.imwrite('../imgs/pig_nearest.jpg',result_img)
  64. print(time.perf_counter()-t)

效果图

3、代码地址

https://github.com/HLearning/fisheye

三、总结:比对两个算法

本人用两个算法对一张图像进行拉直,发现经过经纬度矫正算法生成的图像原作者裁剪掉了边缘部分,见下图效果图,中间黑框内的图像是经过“经纬度矫正法”得到的效果图,外面的大图是用“棋盘格矫正法”得到的效果图

为了更直观,更改了图像的透明度,可以看出两个算法的效果还是多少有些差别的。

其实,两个算法的边缘部分都被严重拉伸,丢不丢掉看适用场景和个人需要吧。

四、知识拓展

立体标定

算法说明

坐标映射建立,各区域的角点都有一维世界坐标为0,对应图5中三幅子图像分别为Y=0,X=0,Z=0。根据棋盘方格边长以及与世界坐标原点间隔的方格数,可得到所有角点的世界坐标。从而建立起二维图像坐标与三维世界坐标的一一映射,用于模型参数的求解。

 

参考地址:采用立体标定板的鱼眼相机快速标定方法_真空技术_新闻动态_深圳市鼎达信装备有限公司

基于双经度模型的鱼眼图像畸变矫正方法

基于双经度模型的鱼眼图像畸变矫正方法 - 百度文库

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/141550
推荐阅读
相关标签
  

闽ICP备14008679号