0. 内容
可用于相机标定图片筛选
相机标定
三维点重投影会二维图像
绘制Z平面以及连接线
1. 图片筛选
利用opencv中findChessboardCorners判断图片是否可以检测到对应角点,若检测到角点ret值为True,当检测不到角点时删除对应图片。
import os import cv2 import glob def delete_pic(inter_corner_shape, img_dir, img_type): images = glob.glob(img_dir + os.sep + '**.' + img_type) for fname in images: img = cv2.imread(fname) gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) ret, cp_img = cv2.findChessboardCorners(gray_img, (inter_corner_shape[0], inter_corner_shape[1]), None) if not ret: os.remove(fname) if __name__ == '__main__': inter_corner_shape = (9, 6) img_dir = "./data" img_type = "jpg" delete_pic(inter_corner_shape, img_dir, img_type)
2. 线条和平面绘制辅助函数(含详细注释)
# 二维图片, 图片角点坐标, 所需投影的三维点在图像中的投影点坐标 def draw(img, corners, imgpts): # 获取第一个图像角点信息 corner = tuple(corners[0].ravel().astype(np.int64)) # 利用第一个图像角点坐标和投影点的坐标绘制想要的直线和平面 # np.ravel()函数为多维数组的维度降为一维 # 新版本的opencv在绘制直线和平面时需手动将坐标信息转为整型,老版本绘制直线时不需要(测试发现的) img = cv2.drawContours(img, [np.array([list(corners[0].ravel()), list(imgpts[1].ravel()), list(imgpts[2].ravel()), list(imgpts[3].ravel())]).astype(np.int64)], -1, (175, 0, 175), -3) img = cv2.line(img, corner, tuple(imgpts[0].ravel().astype(np.int64)), (255, 0, 0), 5) img = cv2.line(img, corner, tuple(imgpts[1].ravel().astype(np.int64)), (0, 255, 0), 5) # img = cv2.line(img, corner, tuple(imgpts[2].ravel().astype(np.int64)), (0, 0, 255), 5) img = cv2.line(img, corner, tuple(imgpts[3].ravel().astype(np.int64)), (0, 0, 255), 5) return img
3. 主函数(相机标定:获取内参、畸变系数,外参<标定阶段没太大作用>)
# 定义角点数量, 棋盘格尺寸(应该可以随意写,不影响参数标定),图像路径, 图像类型 def calib(inter_corner_shape, size_per_grid, img_dir, img_type): # 生成角点的世界坐标系坐标 # np.mgrid() 返回多维结构,具体功能建议自行尝试 w, h = inter_corner_shape cp_int = np.zeros((w * h, 3), np.float32) cp_int[:, :2] = np.mgrid[0:w, 0:h].T.reshape(-1, 2) cp_world = cp_int * size_per_grid obj_points = [] img_points = [] # 角点提取 images = glob.glob(img_dir + os.sep + '**.' + img_type) for fname in images: img = cv2.imread(fname) gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 得到是否有角点以及角点 ret, cp_img = cv2.findChessboardCorners(gray_img, (w, h), None) if ret: # 得到优化后的角点 cp_img = cv2.cornerSubPix(gray_img, cp_img, (11, 11), (-1, -1), criteria) obj_points.append(cp_world) img_points.append(cp_img) # 绘制角点,可以检查角点提取是否准确 cv2.drawChessboardCorners(img, (w, h), cp_img, ret) cv2.imshow('FoundCorners', img) cv2.waitKey(100) cv2.destroyAllWindows() # 相机标定 # 参数:世界坐标系坐标,角点坐标,图像,None,None(标定方法,默认) # 需要注意:opencv中图像为宽、高,与numpy的读取方式不同 # 传参数应依次传numpy的1, 0两个维度 _, mat_inter, coff_dis, v_rot, v_trans = cv2.calibrateCamera(obj_points, img_points, gray_img.shape[::-1], None, None) # 内参 print("internal matrix:n", mat_inter) # 畸变系数 print("distortion cofficients:n", coff_dis) # 旋转向量 # print("rotation vectors:n", v_rot) # 平移向量 # print("translation vectors:n", v_trans) # 将世界坐标系的点投影回二维平面,计算投影点与角点的距离误差 total_error = 0 for i in range(len(obj_points)): img_points_repro, _ = cv2.projectPoints(obj_points[i], v_rot[i], v_trans[i], mat_inter, coff_dis) error = cv2.norm(img_points[i], img_points_repro, cv2.NORM_L2) / len(img_points_repro) total_error += error print(("Average Error of Reproject: "), total_error / len(obj_points)) return mat_inter, coff_dis, cp_world
4. 实现(含详细注释)
if __name__ == '__main__': # 定义角点数量 inter_corner_shape = (9, 6) # 定义棋盘格尺寸 size_per_grid = 0.02 # 定义图片路径 img_dir = "./data" # 定义图片格式 img_type = "jpg" # 相机标定 mat_inter, coff_dis, cp_world = calib(inter_corner_shape, size_per_grid, img_dir, img_type) # 读取图片测试标定效果 img = cv2.imread("./data/test01.jpg") h, w = img.shape[:2] # new为优化参数矩阵,roi为opencv认为的感兴趣区域,可用于去畸变后的黑色边缘裁剪 # 当参数1改为0时,opencv会对图像自动裁剪 # 注意w和h new, roi = cv2.getOptimalNewCameraMatrix(mat_inter, coff_dis, (w, h), 1, (w, h)) x, y, w, h = roi print(roi) # 图像去畸变,方法一 dst = cv2.undistort(img, mat_inter, coff_dis, None, new) # 图像去畸变,方法二,有点点问题,可以自己尝试 # mapx, mapy = cv2.initUndistortRectifyMap(mat_inter, coff_dis, None, new, (w, h), 5) # dst = cv2.remap(img, mapx, mapy, cv2.INTER_LINEAR) cv2.namedWindow('origin', cv2.WINDOW_NORMAL) cv2.namedWindow('no_cut', cv2.WINDOW_NORMAL) cv2.namedWindow('cut', cv2.WINDOW_NORMAL) cv2.namedWindow('img_with_line', cv2.WINDOW_NORMAL) cv2.imshow('origin', img) cv2.imshow('cut', dst[y: y+h, x: x+w]) cv2.imshow('no_cut', dst) # 可视化连线,流程如下 gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY) # 找角点 -> 优化角点 -> 获取外参 -> 外参矩阵变换 -> 选择要绘制的世界坐标系点 -> 得到世界坐标系点在图像上的投影点 -> 绘制连线 ok, corners = cv2.findChessboardCorners(gray, (inter_corner_shape[0], inter_corner_shape[1]), ) if ok: exact_corners = cv2.cornerSubPix(gray, corners, (11, 11), (-1, -1), criteria) _, rvec, tvec, inliers = cv2.solvePnPRansac(cp_world, exact_corners, mat_inter, coff_dis) axis = 0.02 * np.float32([[0, 0, -8], [8, 0, 0], [8, 5, 0], [0, 5, 0]]).reshape(-1, 3) imgpts, _ = cv2.projectPoints(axis, rvec, tvec, mat_inter, coff_dis) img = draw(img, corners, imgpts) cv2.imshow('img_with_line', img) cv2.waitKey(0)
5. 结果展示
6. 代码链接
360sorround/calibration at main · liuweixue001/360sorround (github.com)
7. 后续更新
1. 图像去畸变、鸟瞰图生成、图像拼接、图像融合:畸变处理->俯视变换->图像拼接 (github.com)">liuweixue001/360sorround: 360环视--相机校正->畸变处理->俯视变换->图像拼接 (github.com)
2. C++版本相关工作:
liuweixue001/360surround-2.0-: 一个简单案例,包含:图像去畸变、俯视变换、图像拼接和融合 (github.com)
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)