camera_calibrate/calibrate_extri.py

138 lines
5.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import os
from glob import glob
from os.path import join
import numpy as np
import cv2 as cv
import json
def read_json(input):
with open(input, "r") as f:
data = json.load(f)
return data
def solvePnP(k3d, k2d, K, dist, flag, tryextri=False):
k2d = np.ascontiguousarray(k2d[:, :2]) # 保留前两列
# try different initial values:
if tryextri: # 尝试不同的初始化外参
def closure(rvec, tvec):
ret, rvec, tvec = cv.solvePnP(k3d, k2d, K, dist, rvec, tvec, True, flags=flag)
points2d_repro, xxx = cv.projectPoints(k3d, rvec, tvec, K, dist)
kpts_repro = points2d_repro.squeeze()
err = np.linalg.norm(points2d_repro.squeeze() - k2d, axis=1).mean()
return err, rvec, tvec, kpts_repro
# create a series of extrinsic parameters looking at the origin
height_guess = 2.7 # 相机的初始高度猜测
radius_guess = 4. # 相机的初始水平距离猜测,圆的半径,需要根据自己的实际情况调整
infos = []
for theta in np.linspace(0, 2 * np.pi, 180):
st = np.sin(theta)
ct = np.cos(theta)
center = np.array([radius_guess * ct, radius_guess * st, height_guess]).reshape(3, 1)
R = np.array([
[-st, ct, 0],
[0, 0, -1],
[-ct, -st, 0]
])
tvec = - R @ center
rvec = cv.Rodrigues(R)[0]
err, rvec, tvec, kpts_repro = closure(rvec, tvec)
infos.append({
'err': err,
'repro': kpts_repro,
'rvec': rvec,
'tvec': tvec
})
infos.sort(key=lambda x: x['err'])
err, rvec, tvec, kpts_repro = infos[0]['err'], infos[0]['rvec'], infos[0]['tvec'], infos[0]['repro']
else:
# 直接求解的初值是零向量
ret, rvec, tvec = cv.solvePnP(k3d, k2d, K, dist, flags=flag)
points2d_repro, xxx = cv.projectPoints(k3d, rvec, tvec, K, dist)
kpts_repro = points2d_repro.squeeze()
err = np.linalg.norm(points2d_repro.squeeze() - k2d, axis=1).mean()
# print(err)
return err, rvec, tvec, kpts_repro
# 对单个相机进行外参标定
def _calibrate_extri(k3d, k2d, K, dist, flag, tryfocal=False):
extri = {}
methods = [cv.SOLVEPNP_ITERATIVE]
# 检查关键点数据的数量是否匹配
if k3d.shape[0] != k2d.shape[0]:
print('k3d {} doesnot match k2d {}'.format(k3d.shape, k2d.shape))
length = min(k3d.shape[0], k2d.shape[0])
k3d = k3d[:length]
k2d = k2d[:length]
valididx = k2d[:, 2] > 0 # k2d第三列是置信度检查是否大于0
if valididx.sum() < 4: # 筛选出有效的2D和3D关键点数量大于4
rvec = np.zeros((1, 3)) # 初始话旋转和平移为0并标记为失败
tvec = np.zeros((3, 1))
extri['Rvec'] = rvec
extri['R'] = cv.Rodrigues(rvec)[0]
extri['T'] = tvec
print('[ERROR] Failed to initialize the extrinsic parameters')
return extri
k3d = k3d[valididx]
k2d = k2d[valididx]
# 优化相机焦距
# 如果启用焦距优化
if tryfocal:
infos = []
for focal in range(500, 5000, 10): # 遍历焦距范围
# 设置焦距值
K[0, 0] = focal # 更新 K 的 fx
K[1, 1] = focal # 更新 K 的 fy
for method in methods:
# 调用 solvePnP
err, rvec, tvec, kpts_repro = solvePnP(k3d, k2d, K, dist, method)
# 保存结果
infos.append({
'focal': focal,
'err': err,
'rvec': rvec,
'tvec': tvec,
'repro': kpts_repro
})
# 根据重投影误差选择最佳焦距
infos.sort(key=lambda x: x['err'])
best_result = infos[0]
focal = best_result['focal']
err, rvec, tvec, kpts_repro = best_result['err'], best_result['rvec'], best_result['tvec'], best_result['repro']
# 更新内参中的焦距
K[0, 0] = focal
K[1, 1] = focal
print(f'[INFO] Optimal focal length found: {focal}, reprojection error: {err:.3f}')
else:
# 如果不优化焦距,直接调用 solvePnP
err, rvec, tvec, kpts_repro = solvePnP(k3d, k2d, K, dist, flag)
# 保存外参结果
extri['Rvec'] = rvec
extri['R'] = cv.Rodrigues(rvec)[0]
extri['T'] = tvec
center = - extri['R'].T @ tvec
print(f'[INFO] Camera center: {center.squeeze()}, reprojection error: {err:.3f}')
return extri
def calibrate_extri(kpts_path, intri_path, flag, tryfocal=False, tryextri=False):
extri = {}
intri_data = read_json(intri_path)
kpts_data = read_json(kpts_path)
# 获取内参
camnames = list(intri_data.keys())
for cam in camnames:
print(f'[INFO] Processing camera: {cam}')
K = np.array(intri_data[cam]['K'])
dist = np.array(intri_data[cam]['dist'])
k3d = np.array(kpts_data[cam]['keypoints3d'])
k2d = np.array(kpts_data[cam]['keypoints2d'])
extri[cam] = _calibrate_extri(k3d, k2d, K, dist, flag, tryfocal=tryfocal)
return extri
if __name__ == "__main__":
pass