自学内容网 自学内容网

鱼眼摄像头-opencv校准(基于棋盘+畸变表)

一:主要参数说明

1:内参矩阵K

是3*3的矩阵,其类似格式

K=np.array([
[389.2109574522624, 0.0, 630.2525667489842], 
[0.0, 388.505701978078, 360.7886749292513], 
[0.0, 0.0, 1.0]])

2:畸变系数

针对鱼眼相机:为1*4的数组,格式如下:

D=np.array([[0.0590137867946409], [-0.030903466430950866], [0.002123587326450784], [-1.851242815594123e-05]])
rms 0.18562501602552903

二:畸变表

这个文摄像头的镜头场景

1:焦距

2:镜头尺寸

三:参考连接


https://github.com/792864625/AroundViewMonitor-China-Developer/tree/d664bf75a8c43bb52bf69a71c420b31cbefa5a07?tab=readme-ov-file
https://www.eet-china.com/mp/a213856.html
https://blog.csdn.net/Yong_Qi2015/article/details/130299413?utm_medium=distribute.pc_relevant.none-task-blog-2~default~baidujs_baidulandingword~default-4-130299413-blog-108095636.235^v43^pc_blog_bottom_relevance_base2&spm=1001.2101.3001.4242.3&utm_relevant_index=7

四:实际测试

import cv2
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
import os
from simple_gui import display_image, PointSelector

from math import radians

import argparse
# 在这里修改各参数值
parser = argparse.ArgumentParser(description="Fisheye Camera Undistortion")
parser.add_argument('-width', default=1280, type=int, help='Camera Frame Width')
parser.add_argument('-height', default=720, type=int, help='Camera Frame Height')
parser.add_argument('-load', default=False, type=bool, help='Load New Camera K/D Data (True/False)')

'''
parser.add_argument('-path_read', default='./data/ligong/', type=str, help='Original Image Read Path')
parser.add_argument('-path_save', default='./data/ligong/', type=str, help='Undistortion Image Save Path')
'''
parser.add_argument('-path_read', default='./data/orig_3_3_npy_data/', type=str, help='Original Image Read Path')
parser.add_argument('-path_save', default='./data/orig_3_3_npy_data/', type=str, help='Undistortion Image Save Path')


parser.add_argument('-path_k', default='./KD_01/camera_1_K.npy', type=str, help='Camera K File Path')
parser.add_argument('-path_d', default='./KD_01/camera_1_D.npy', type=str, help='Camera D File Path')
parser.add_argument('-focalscale', default=1, type=float, help='Camera Undistortion Focal Scale')
parser.add_argument('-sizescale', default=2, type=float, help='Camera Undistortion Size Scale')
parser.add_argument('-offset_h', default=0, type=float, help='Horizontal Offset of Optical Axis')
parser.add_argument('-offset_v', default=0, type=float, help='Vertical Offset of Optical Axis')
parser.add_argument('-srcformat', default='npy', type=str, help='Original Image Format (jpg/png)')
parser.add_argument('-dstformat', default='jpg', type=str, help='Final Image Format (jpg/png)')
parser.add_argument('-quality', default=100, type=int, help='Save Image Quality (jpg:0-100, png:9-0 (low-high))')
parser.add_argument('-name', default=None, type=str, help='Save Image Name')
args = parser.parse_args()

#https://github.com/792864625/AroundViewMonitor-China-Developer/tree/d664bf75a8c43bb52bf69a71c420b31cbefa5a07?tab=readme-ov-file
#https://www.eet-china.com/mp/a213856.html
#https://blog.csdn.net/Yong_Qi2015/article/details/130299413?utm_medium=distribute.pc_relevant.none-task-blog-2~default~baidujs_baidulandingword~default-4-130299413-blog-108095636.235^v43^pc_blog_bottom_relevance_base2&spm=1001.2101.3001.4242.3&utm_relevant_index=7

#用来描述每个像素在实际世界中代表多少毫米。换句话说,它告诉我们在图像中每个像素的大小对应于实际场景中多大的长度。
sensorRatio = 0.003 #;% 由厂家提供,单位 mm/pixel
sensorRatio=0.0029  #这个值应该是正确的值
sensorRatio_cm=1
sensorH=3.876/720
sensorW=5.138/1280
sensorW=sensorRatio
sensorW=sensorRatio
#下面的参数是当前可以的
sensorH=3.876/1080 #0.00358888888888888888888888888889
sensorW=5.138/1920 #0.00267604166666666666666666666667
sensorH=sensorRatio #0.00358888888888888888888888888889

'''
下面是通过棋盘格校准而来,在最终的校准参数中,使用给了棋盘校准的畸变系数参数
Found 541 valid images for calibration
DIM=(1280, 720)
K=np.array([
[389.2109574522624, 0.0, 630.2525667489842], 
[0.0, 388.505701978078, 361.7886749292513], 
[0.0, 0.0, 1.0]])
D=np.array([[0.0590137867946409], [-0.030903466430950866], [0.002123587326450784], [-1.851242815594123e-05]])
rms 0.18562501602552903

下面是通过畸变表计算而来
0.06244240163136363,-0.03908688528398631,0.006663506063585303,-0.0009067163647851509
[[479.34904735   0.         768.        ]
[  0.         642.86385797 432.        ]
[  0.           0.           1.        ]]

畸变焦距 = 1.71781
畸变焦距 = 1.72033
畸变焦距 = 1.71520
上面三个平均值为:1.71778


根据目前的测试结果,畸变系数从肉眼上面看不来有什么差异

1:配置参数

K=np.array([
[389.2109574522624, 0.0, 630.2525667489842], 
[0.0, 388.505701978078, 360.7886749292513], 
[0.0, 0.0, 1.0]])

其中focal=1.7,sensorW=sensorRatio=0.0029
K = np.array([[389.2109574522624, 0, w/2 ],
          [0, focal / sensorW,  h/2  ],
          [0, 0, 1]])   
          
opencvCoeffs = np.array([[0.09780962], [-0.07203037],  [0.01673093], [-0.0017944 ]], dtype=np.float64) # 来自畸变表


2:配置参数
K=np.array([
[389.2109574522624, 0.0, 630.2525667489842], 
[0.0, 388.505701978078, 360.7886749292513], 
[0.0, 0.0, 1.0]])
opencvCoeffs =  np.array([[0.0590137867946409], [-0.030903466430950866], [0.002123587326450784], [-1.851242815594123e-05]], dtype=np.float64) # 这个来自450张照片校准生成的。

3: 配置参数
newCameraMatrixK6=np.array([
[289.67227465, 0.0, w/2], 
[0.0, 558.505701978078, h/2], 
[0.0, 0.0, 1.0]])
opencvCoeffs = np.array([[0.09780962], [-0.07203037],  [0.01673093], [-0.0017944 ]], dtype=np.float64) # 来自畸变表

其实主要更改的参数是:内参矩阵的
k[0][0] 和 k[1][1]


'''

distortionTablePath = "./data/distorti_table/distortionTable.xlsx"
distortionTablePath = "./data/distorti_table/backupDistortionTable.xlsx"
distortionTablePath = "./data/distorti_table/3.xlsx"


def manual_select_point(img,cam_id):
    radius = 1
    color = (0, 0, 255)  # BGR格式,红色
    thickness = 2    
    pts_dir = args.path_read
    camera_file = os.path.join(pts_dir, 'cam'+cam_id + '_dst_point.npy')
    point_corners=[]
    if os.path.exists(camera_file):
        print("load dst point from ",camera_file)
        dst_corners = np.load(camera_file)  
        print("dst_corners",dst_corners)

    else:     
        gui = PointSelector(img, title="cam"+cam_id)
        choice = gui.loop()
        if choice > 0:
            point_corners = np.float32(gui.keypoints)
            print(point_corners)
            print(len(point_corners))
            cam_pts = np.array(point_corners, dtype=np.float32)
            np.save(camera_file, cam_pts)
            
        cnt=0
        for index in range(len(point_corners)): 
            x, y = point_corners[index]
            color = (0, 0, 255)  # BGR格式,红色
            cv2.putText(img, str(cnt), (int(x), int(y)), cv2.FONT_HERSHEY_SIMPLEX, 1,color, thickness)
            color = (0, 255, 255)  # BGR格式,红色
            cv2.circle(img,(int(x),int(y)), radius, color, thickness)







cameraData = pd.read_excel(distortionTablePath)
#print(cameraData)
#print(type(cameraData)) 


# 如果表格有多个 sheet,可以用 sheet_name='Sheet1' 指定具体的 sheet 名称

# 假设我们读取的是第一个 sheet,根据 MATLAB 代码推测,数据从第四行开始
cameraData = cameraData.iloc[0:101, :]  
print(cameraData)



angleIn = cameraData.iloc[:, 0].values  # 入射角列
print(angleIn)
theta_input = np.radians(angleIn)
focal = np.mean(cameraData.iloc[:, 2].values / np.tan(theta_input))  # 计算焦距
print("focal:",focal)


distortFrame = np.load("./data/orig_3_3_npy_data/img_cam4.npy")
def test_measure_distance():

    shift_h=1000
    shift_w=1000
    
    cam2_world_point = np.array([
    [0.+shift_h, 200.+shift_w],
    [200.+shift_h, 200.+shift_w],
    [ 0.+shift_h, 960.+shift_w],
    [ 200.+shift_h, 960.+shift_w],
    ]).astype(np.int32)  
    cam2_img_point = np.array([
    [1166.*sensorRatio_cm, 419.*sensorRatio_cm],
    [1233.*sensorRatio_cm, 530.*sensorRatio_cm],
    [1096.*sensorRatio_cm, 422.*sensorRatio_cm],
    [1191.*sensorRatio_cm, 578.*sensorRatio_cm],
    [ 349.*sensorRatio_cm, 381.*sensorRatio_cm],
    [ 216.*sensorRatio_cm, 505.*sensorRatio_cm],
    [ 271.*sensorRatio_cm,369.*sensorRatio_cm],
    [ 171.*sensorRatio_cm,456.*sensorRatio_cm]
    ]).astype(np.float64)      

    shift_h=200
    shift_w=200
    cam2_world_point = np.array([
    [0.+shift_h, 0.+shift_w],
    [200.+shift_h, 0.+shift_w],
    [0.+shift_h, 200.+shift_w],
    [200.+shift_h, 200.+shift_w],
    [ 0.+shift_h, 940.+shift_w],
    [ 200.+shift_h, 940.+shift_w],
    [ 0.+shift_h,1140.+shift_w],
    [ 200.+shift_h,1140.+shift_w]
    ]).astype(np.int32)  



    cam2_img_point = np.array([
    [1096.*sensorRatio_cm, 422.*sensorRatio_cm],
    [1191.*sensorRatio_cm, 578.*sensorRatio_cm],
    [ 349.*sensorRatio_cm, 381.*sensorRatio_cm],
    [ 216.*sensorRatio_cm, 505.*sensorRatio_cm],
    ]).astype(np.float64)      


    cam2_img_point = np.array([
    [[1167. , 419.],
     [1234. , 532.],
     [ 271. , 370.],
     [ 173. , 454.]]

    ]).astype(np.float64)    


    cam2_img_point = np.array([
    [[1137. , 402.],
     [1264. , 574.],
     [ 256. , 336.],
     [  10. , 477.]]
    ]).astype(np.float64)     

    cam2_undistortImg2 = cv2.imread("./data/ligong/cam2_undistortImg2.jpg")

    cam_id='2'
    manual_select_point(distortFrame,cam_id)

    print("distortFrame.shape",cam2_undistortImg2.shape)

    H, _ = cv2.findHomography(cam2_img_point, cam2_world_point,method = cv2.RANSAC)
    holog = cv2.warpPerspective(cam2_undistortImg2, H, (1140*4, 1080*3))

    print(holog.shape)
    surround_n=cv2.resize(holog,(720,1280))
    dstMap = cv2.rotate(surround_n, cv2.ROTATE_90_COUNTERCLOCKWISE)
    cv2.imshow("camera_homography_vir_1_H", dstMap)





h, w, _ = distortFrame.shape  # 获取图像高度和宽度

K = np.array([[focal / sensorRatio, 0, w / 2],
              [0, focal / sensorRatio, h / 2],
              [0, 0, 1]])

K = np.array([[focal / sensorH, 0,w *3/ 5 ],
              [0, focal / sensorW, h *3/ 5  ],
              [0, 0, 1]])


r_d = 1. / focal * cameraData.iloc[:, 1].values  # 求归一化平面上的 r_d

thetaRadian = np.radians(angleIn)  # 度数转为弧度

A = np.column_stack([thetaRadian**3, thetaRadian**5, thetaRadian**7, thetaRadian**9])
b = r_d - thetaRadian

opencvCoeffs = np.linalg.lstsq(A, b, rcond=None)[0]

focal=1.7

K = np.array([[focal / sensorH, 0,630.2525667489842 ],
              [0, focal / sensorW, 400.7886749292513 ],
              [0, 0, 1]])

K = np.array([[focal / sensorH, 0,w*2/5 ],
              [0, focal / sensorW, h*2/5 ],
              [0, 0, 1]])

K = np.array([[focal / sensorH, 0,730.2525667489842 ],
              [0, focal / sensorW, 730.7886749292513 ],
              [0, 0, 1]])
if 1==1:
    K=np.array([
    [389.2109574522624, 0.0, 630.2525667489842], 
    [0.0, 388.505701978078, 400.7886749292513], 
    [0.0, 0.0, 1.0]])


    K = np.array([[focal / sensorH, 0,w*3/5 ],
                  [0, focal / sensorW, h*3/5 ],
                  [0, 0, 1]])

    K = np.array([[focal / sensorH,0, w/2 ],
                  [0, focal / sensorW, h/2 ],
                  [0, 0, 1]])

    K = np.array([[focal / sensorH,0, w/2 ],
                  [0, focal / sensorW, h/2 ],
                  [0, 0, 1]])
    K = np.array([[focal / sensorH, 0,w/2 ],
                  [0, focal / sensorW, h*2/5 ],
                  [0, 0, 1]])



    K=np.array([
    [389.2109574522624, 0.0, 630.2525667489842], 
    [0.0, 388.505701978078, 360.7886749292513], 
    [0.0, 0.0, 1.0]])

    K = np.array([[focal / sensorH, 0,630.2525667489842 ],
                  [0, focal / sensorW, 360.7886749292513 ],
                  [0, 0, 1]])



    K = np.array([[389.2109574522624, 0, w/2 ],
              [0, focal / sensorW,  h/2  ],
              [0, 0, 1]])   
    opencvCoeffs = np.array([[0.09780962], [-0.07203037],  [0.01673093], [-0.0017944 ]], dtype=np.float64) # 来自畸变表
 

    newCameraMatrixK = K.copy()

    newImageSize = distortFrame.shape[:2]  # 假设 distortFrame 是已定义的畸变图像


    newCameraMatrixK[0][0]*=0.55 #控制X方向显示多少
    newCameraMatrixK[1][1]*=0.90

    newCameraMatrixK6=np.array([
    [289.67227465, 0.0, w/2], 
    [0.0, 558.505701978078, h/2], 
    [0.0, 0.0, 1.0]])


#===========================================================================================================
else:
    '''
下面的是中间压缩,类似之前程彪校准的,左右比较模糊,但是实际中进行透视变化后,比较直
    '''

    K=np.array([
    [389.2109574522624, 0.0, 630.2525667489842], 
    [0.0, 388.505701978078, 400.7886749292513], 
    [0.0, 0.0, 1.0]])


    K=np.array([
    [389.2109574522624, 0.0, w/2], 
    [0.0, 388.505701978078, h/2], 
    [0.0, 0.0, 1.0]])

######################################################################################

    K=np.array([
    [389.2109574522624, 0.0, 630.2525667489842], 
    [0.0, 388.505701978078, 360.7886749292513], 
    [0.0, 0.0, 1.0]])

    opencvCoeffs =  np.array([[0.0590137867946409], [-0.030903466430950866], [0.002123587326450784], [-1.851242815594123e-05]], dtype=np.float64) # 这个来自450张照片校准生成的。


# 假设接下来的部分中的函数和变量定义,需要根据具体情况修改
#newCameraMatrixK = K  # 假设 K 是已定义的新相机矩阵
    newCameraMatrixK = K.copy()
    newImageSize = distortFrame.shape[:2]  # 假设 distortFrame 是已定义的畸变图像
    newCameraMatrixK[0][0]*=0.35 #控制X方向显示多少,值越小,显示校准后的区域越大
    newCameraMatrixK[1][1]*=0.72


newImageSize = newImageSize[::-1]


print(opencvCoeffs)
print("最小二乘拟合 OpenCV 鱼眼模型畸变系数为(k1~k4):" + ",".join(map(str, opencvCoeffs)))
print("K",K)
print("newCameraMatrixK:",newCameraMatrixK)
print("newImageSize:",newImageSize)

balance=0
newCameraMatrixK1 = cv2.fisheye.estimateNewCameraMatrixForUndistortRectify(K, opencvCoeffs, newImageSize, np.eye(3), balance=balance)
print("newCameraMatrixK1:",newCameraMatrixK1)

balance=1
newCameraMatrixK2 = cv2.fisheye.estimateNewCameraMatrixForUndistortRectify(K, opencvCoeffs, newImageSize, np.eye(3), balance=balance)
print("newCameraMatrixK2:",newCameraMatrixK2)

balance=0.1
newCameraMatrixK3 = cv2.fisheye.estimateNewCameraMatrixForUndistortRectify(K, opencvCoeffs, newImageSize, np.eye(3), balance=balance)
print("newCameraMatrixK3:",newCameraMatrixK3)

balance=0.3
newCameraMatrixK4 = cv2.fisheye.estimateNewCameraMatrixForUndistortRectify(K, opencvCoeffs, newImageSize, np.eye(3), balance=balance)
print("newCameraMatrixK4:",newCameraMatrixK4)


balance=0.5
newCameraMatrixK5 = cv2.fisheye.estimateNewCameraMatrixForUndistortRectify(K, opencvCoeffs, newImageSize, np.eye(3), balance=balance)
print("newCameraMatrixK5:",newCameraMatrixK5)

balance=0.7
newCameraMatrixK6 = cv2.fisheye.estimateNewCameraMatrixForUndistortRectify(K, opencvCoeffs, newImageSize, np.eye(3), balance=balance)
print("newCameraMatrixK6:",newCameraMatrixK6)



base_newCameraMatrixK6=np.array([
[289.67227465, 0.0, w/2], 
[0.0, 458.505701978078, h/2], 
[0.0, 0.0, 1.0]])


newCameraMatrixK7=np.array([
[250.67227465, 0.0, w/2], 
[0.0, 558.505701978078, h/2], 
[0.0, 0.0, 1.0]])


print(newCameraMatrixK6)
R = np.eye(3, dtype=np.float64)
mapX, mapY = cv2.fisheye.initUndistortRectifyMap(K, opencvCoeffs, R, newCameraMatrixK, newImageSize, cv2.CV_16SC2)
mapX, mapY = cv2.fisheye.initUndistortRectifyMap(K, opencvCoeffs, R, newCameraMatrixK7, (w,h), cv2.CV_16SC2)


undistortImg2 = cv2.remap(distortFrame, mapX, mapY, cv2.INTER_LINEAR)
undistortImg2 = cv2.remap(distortFrame, mapX, mapY, cv2.INTER_LINEAR, borderMode=cv2.BORDER_CONSTANT, borderValue=255)
print(undistortImg2.shape)
cv2.imshow("img",distortFrame)
cv2.imshow("undistortImg2",undistortImg2)
cv2.imwrite('cam2_undistortImg2.jpg', undistortImg2)

cv2.waitKey(0) 

indices = np.indices((720, 1280), dtype=np.float32)
indices = np.stack((indices[1], indices[0], np.zeros((720, 1280))), axis=-1).astype(np.float64)
indices = cv2.remap(indices, mapX, mapY, interpolation=cv2.INTER_LINEAR,borderMode=cv2.BORDER_CONSTANT, borderValue=-1)
position=1
map_dict = np.array(indices[:, :, :-1], dtype=np.float32)

while True:
    if cv2.waitKey(10) & 0xFF == ord('q'):
        filenames = os.listdir(args.path_read)       # 在argparse中修改图片路径
        index = 1
        for filename in filenames:
            if filename[-4:] == '.' + args.srcformat:    

                all_name=args.path_read + filename
                img = np.load(all_name)
                cv2.imwrite(args.path_save + filename[:-4] + '_orig_distort.jpg', img, [cv2.IMWRITE_PNG_COMPRESSION, args.quality])
                print(filename)
                print(all_name)
                undistort_img = cv2.remap(img, mapX, mapY, cv2.INTER_LINEAR)
                np.save('%sF_cam%s.npy' % ("./fisheye_k_d/", index), map_dict)
                index+=1
                print(undistort_img.shape)   
                print(img.shape)   
                if args.dstformat == 'jpg':
                    print("save")
                    cv2.imwrite(args.path_save + filename[:-4] + '.jpg', undistort_img, [cv2.IMWRITE_JPEG_QUALITY, args.quality])
                elif args.dstformat == 'png':
                    cv2.imwrite(args.path_save + filename[:-4] + '.png', undistort_img, [cv2.IMWRITE_PNG_COMPRESSION, args.quality])
                else:
                    cv2.imwrite(filename[:-4] + '.' + args.dstformat, undistort_img) 
                        
        break
'''
if __name__ == "__main__":
    main()
'''

五:测试结果

1:原始图片

校准后的

不同的newCameraMatrixK7,有不同的结果


原文地址:https://blog.csdn.net/suiyuan19840208/article/details/140618846

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!