camera_calibrate/detect_chessboard.py

186 lines
7.1 KiB
Python
Raw Normal View History

import os
import numpy as np
import cv2 as cv
import glob
import os.path as osp
import json
from tqdm import tqdm
# 先想清楚文件夹结构
# extri文件夹存放棋盘格照片命名规则是cam1.jpg, cam2.jpg, cam3.jpg, ...
# 另一种模式是只检测2d点不生成3d点需要指定文件夹和输出路径
def write_json(data, output_path):
with open(output_path, "w") as f:
json.dump(data, f)
def read_json(input):
with open(input, "r") as f:
data = json.load(f)
return data
def read_img_paths(imgFolder):
imgPaths = []
for extension in ["jpg", "png", "jpeg", "bmp"]:
imgPaths += glob.glob(osp.join(imgFolder, "*.{}".format(extension)))
return imgPaths
def create_output_folder(baseFolder, outputFolder):
folder = osp.join(baseFolder, outputFolder)
if not osp.exists(folder):
os.makedirs(folder)
return folder
base_path = "data"
extri_img_path = osp.join(base_path, "chessboard", "extri")
extri_vis_path = osp.join(base_path, "vis", "extri")
json_output_path = osp.join(base_path, 'output_json')
def _findChessboardCorners(img, pattern):
"basic function"
criteria = (cv.TERM_CRITERIA_EPS + cv.TERM_CRITERIA_MAX_ITER, 30, 0.001)
retval, corners = cv.findChessboardCorners(img, pattern,
flags=cv.CALIB_CB_ADAPTIVE_THRESH + cv.CALIB_CB_FAST_CHECK + cv.CALIB_CB_FILTER_QUADS)
if not retval:
return False, None
corners = cv.cornerSubPix(img, corners, (11, 11), (-1, -1), criteria)
corners = corners.squeeze()
return True, corners
def _findChessboardCornersAdapt(img, pattern):
"Adapt mode"
img = cv.adaptiveThreshold(img, 255, cv.ADAPTIVE_THRESH_GAUSSIAN_C, \
cv.THRESH_BINARY, 21, 2)
return _findChessboardCorners(img, pattern)
# 检测棋盘格角点并且可视化
def findChessboardCorners(img_path, pattern, show=False):
img = cv.imread(img_path)
if img is None:
raise FileNotFoundError(f"Image not found at {img_path}")
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
# Find the chess board corners
for func in [_findChessboardCorners, _findChessboardCornersAdapt]:
ret, corners = func(gray, pattern)
if ret: break
else:
return None
# 附加置信度 1.0 并返回
kpts2d = np.hstack([corners, np.ones((corners.shape[0], 1))])
if show:
# Draw and display the corners
img_with_corners = cv.drawChessboardCorners(img, pattern, corners, ret)
# 标出棋盘格的原点
origin = tuple(corners[0].astype(int)) # 原点的像素坐标
cv.circle(img_with_corners, origin, 10, (0, 0, 255), -1) # 绘制原点
cv.putText(img_with_corners, "Origin", (origin[0] + 10, origin[1] - 10),
cv.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 255), 2)
# 标出最后一个点
last_point = tuple(corners[-1].astype(int)) # 角点数组的最后一个点
cv.circle(img_with_corners, last_point, 10, (0, 255, 0), -1) # 绿色圆点
cv.putText(img_with_corners, "Last", (last_point[0] + 15, last_point[1] - 15),
cv.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2) # 添加文字 "Last"
# 显示图像
cv.imwrite(osp.join(extri_vis_path, osp.basename(img_path)), img_with_corners)
return kpts2d
# 根据棋盘格生成三维坐标,棋盘格坐标系原点在左上角(同时也是全局坐标原点)
# 设定标定板z轴朝上yx表示棋盘在yx平面上
# easymocap
# 注意采用11x8的棋盘格长边是y轴11短边是x轴8可以用opencv试一下
def getChessboard3d(pattern, gridSize, axis='yx'):
# 注意这里为了让标定板z轴朝上设定了短边是x长边是y
template = np.mgrid[0:pattern[0], 0:pattern[1]].T.reshape(-1, 2) # 棋盘格的坐标
object_points = np.zeros((pattern[1] * pattern[0], 3), np.float32) # 3d坐标默认向上的坐标轴为0
# 长边是x,短边是z
if axis == 'xz':
object_points[:, 0] = template[:, 0]
object_points[:, 2] = template[:, 1]
elif axis == 'yx':
object_points[:, 0] = template[:, 1]
object_points[:, 1] = template[:, 0]
else:
raise NotImplementedError
object_points = object_points * gridSize
return object_points
# 检测文件夹下的所有棋盘格图片生成3d点和2d点存入json文件
# 图片应该按照cam0.jpg, cam1.jpg, cam2.jpg, ...的命名方式,要和内参文件夹对应
def detect_chessboard(pattern, gridSize):
imgPaths = read_img_paths(extri_img_path)
if len(imgPaths) == 0:
print("No images found!")
return
data = {}
for imgPath in tqdm(imgPaths):
camname = osp.basename(imgPath).split(".")[0]
keypoints2d = findChessboardCorners(imgPath, pattern, show=True)
if keypoints2d is not None:
keypoints3d = getChessboard3d(pattern, gridSize)
data[camname] = {
"keypoints2d": keypoints2d.tolist(),
"keypoints3d": keypoints3d.tolist(),
"pattern": pattern,
"gridSize": gridSize
}
json_path = osp.join(json_output_path, "chessboard_keypoints.json")
write_json(data, json_path)
print(f"Saved keypoints to {json_path}")
# 只检测2d点存入json文件
def detect_chessboard_2d(imgFolder, pattern, outJsonPath):
pass
def test_findChessboardCorners(img_path, pattern, saveDir):
imgpaths = read_img_paths(img_path)
for imgpath in imgpaths:
img = cv.imread(imgpath)
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
ret, corners = cv.findChessboardCorners(gray, pattern)
if ret:
# 在棋盘格上绘制角点
img_with_corners = cv.drawChessboardCorners(img, pattern, corners, ret)
# 标出原点
origin = tuple(corners[0][0]) # 角点数组的第一个点作为原点
cv.circle(img_with_corners, (int(origin[0]), int(origin[1])), 10, (0, 0, 255), -1) # 红色圆点
cv.putText(img_with_corners, "Origin", (int(origin[0]) + 15, int(origin[1]) - 15),
cv.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 1) # 添加文字 "Origin"
# 标出最后一个点
last_point = tuple(corners[-1][0]) # 角点数组的最后一个点
cv.circle(img_with_corners, (int(last_point[0]), int(last_point[1])), 10, (0, 255, 0), -1) # 绿色圆点
cv.putText(img_with_corners, "Last", (int(last_point[0]) + 15, int(last_point[1]) - 15),
cv.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 1) # 添加文字 "Last"
# 保存带角点的图像
cv.imwrite(osp.join(saveDir, osp.basename(imgpath)), img_with_corners)
else:
print(f"Failed to detect chessboard corners in {imgpath}")
print(f"Saved images to {saveDir}")
if __name__ == '__main__':
# test1
img_path = "data/chessboard/extri"
pattern = (11, 8)
# saveDir = "data/test1"
# os.makedirs(saveDir, exist_ok=True)
# test_findChessboardCorners(img_path, pattern, saveDir)
detect_chessboard(pattern, 60)