实验数据
标定图(黑白棋盘图)若干(不少于10张拍摄不同角度的图片,焦距不变)
过程
每张图片提取角点信息
每张图片提取亚像素角点信息
相机标定
畸变矫正
内参数矩阵、畸变系数
这里保存为json文件
{
"mtx": [
[
3096.855023641478,
0.0,
1029.4641305618723
],
[
0.0,
3091.0248015384295,
333.24889640636945
],
[
0.0,
0.0,
1.0
]
],
"dist": [
[
-0.102230276138444,
-6.080581251041141,
0.013564529498038387,
-0.0027957254737596593,
39.277124343630284
]
]
}
矫正前图像
矫正后图像
代码
包括图片矫正、视频矫正
import os
import cv2
import glob
import json
import numpy as np
class CameraCalibration:
def calibration_init(self, width, height, pic_path, save_parameters=True):
# 亚像素角点的参数
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
# 标定板角点的位置
objp = np.zeros((width * height, 3), np.float32)
objp[:, :2] = np.mgrid[0:width, 0:height].T.reshape(-1, 2)
# 存储点
obj_points, img_points = [], []
images = glob.glob(pic_path)
for i in images:
img = cv2.imread(i)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 棋盘格角点
ret, corners = cv2.findChessboardCorners(gray, (width, height), None)
# 存储角点
if ret:
cv2.cornerSubPix(gray, corners, (11, 11), (-1, -1), criteria)
obj_points.append(objp)
img_points.append(corners)
# 标定
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(obj_points, img_points, gray.shape[::-1], None, None)
if save_parameters:
self.__parameters_init(mtx, dist)
else:
print("ret:", ret)
print("mtx:\n", mtx) # 内参数矩阵
print("dist:\n", dist) # 畸变系数 distortion cofficients = (k_1,k_2,p_1,p_2,k_3)
print("rvecs:\n", rvecs) # 旋转向量 # 外参数
print("tvecs:\n", tvecs) # 平移向量 # 外参数
def __parameters_init(self, mtx, dist):
parameters_path = './parameters.json'
if not os.path.exists(parameters_path):
os.system(r'touch %s' % parameters_path)
self.__write_parameters(mtx, dist)
else:
self.__write_parameters(mtx, dist)
@staticmethod
def __write_parameters(mtx, dist):
with open("./parameters.json", "w", encoding='utf-8') as f:
content = {"mtx": mtx.tolist(), "dist": dist.tolist()}
f.write(json.dumps(content, ensure_ascii=False))
f.close()
class CameraCorrect:
def __init__(self):
self.__correct_init()
# 畸变参数初始化
def __correct_init(self, parameters_file_path: str = "./parameters.json"):
if not os.path.exists(parameters_file_path):
pass
else:
with open(parameters_file_path, "r", encoding='utf-8') as f:
json_data = json.load(f)
self.mtx = np.array(json_data["mtx"])
self.dist = np.array(json_data["dist"])
# 图片畸变校准
def correct_pic(self, origin_path: str, target_path: str = "./", file_name: str = "cor_pic",
file_format: str = "png"):
img = cv2.imread(origin_path)
pic_height, pic_width = img.shape[:2]
file_format = self.__calibration_pic_format(file_format.lower())
cv2.imwrite(target_path + file_name + "." + file_format,
self.__distortion_correction(img, pic_width, pic_height))
# 视频畸变校准(直接输出视频)
def correct_vid2vid(self, origin_path: str, target_path: str = "./", file_name: str = "cor_vid",
file_format: str = "mp4", file_coding: str = "mp4v"):
cap = cv2.VideoCapture(origin_path)
# 获取视频参数
fps, count, vid_width, vid_height = self.__get_capture_attribute(cap)
# 格式编码校对
if self.__calibration_format_coding(file_format, file_coding) is False:
file_format, file_coding = "mp4", "mp4v"
out = cv2.VideoWriter(target_path + file_name + "." + file_format,
cv2.VideoWriter_fourcc(*self.__get_vid_coding(file_coding)), fps,
(vid_width, vid_height))
for i in range(count):
ret, img = cap.read()
if ret:
img = cv2.resize(img, (vid_width, vid_height))
dst = self.__distortion_correction(img, vid_width, vid_height)
out.write(dst)
cap.release()
out.release()
# 视频畸变校准(校准抽取所有帧)
def correct_vid2pic(self, origin_path: str, target_path: str = "./", file_name: str = "cor_pic",
file_format: str = "png"):
file_format = self.__calibration_pic_format(file_format.lower())
cap = cv2.VideoCapture(origin_path)
fps, count, vid_width, vid_height = self.__get_capture_attribute(cap)
for i in range(count):
ret, img = cap.read()
if ret:
img = cv2.resize(img, (vid_width, vid_height))
cv2.imwrite(target_path + file_name + "_" + str(i) + "." + file_format,
self.__distortion_correction(img, vid_width, vid_height))
cap.release()
cv2.destroyAllWindows()
# 获取视频参数
@staticmethod
def __get_capture_attribute(vid: cv2.VideoCapture):
fps = int(vid.get(cv2.CAP_PROP_FPS)) # 视频帧率
count = int(vid.get(cv2.CAP_PROP_FRAME_COUNT)) # 视频帧数
width = int(vid.get(cv2.CAP_PROP_FRAME_WIDTH)) # 视频宽度 vid.shape[1]
height = int(vid.get(cv2.CAP_PROP_FRAME_HEIGHT)) # 视频高度 vid.shape[0]
return fps, count, width, height
# 图片格式校对
@staticmethod
def __calibration_pic_format(file_format: str):
if file_format == "jpg" or file_format == "png" or file_format == "bmp" or file_format == "tiff" or file_format == "tif":
return file_format
else:
return "png"
# 视频格式编码校对
@staticmethod
def __calibration_format_coding(file_format: str, file_coding: str):
file_format, file_coding = file_format.lower(), file_coding.lower()
if file_format == "mp4" and file_coding == "mp4v":
return True
elif file_format == "avi" and (file_coding == "yuv" or file_coding == "mpeg-1" or file_coding == "mpeg-4"):
return True
elif file_format == "ogv" and file_coding == "ogg vorbis":
return True
elif file_format == "flv" and file_coding == "flash":
return True
else:
return False
# 获取编码参数
@staticmethod
def __get_vid_coding(file_coding: str):
file_coding = file_coding.lower()
if file_coding == "mp4v":
return 'mp4v'
elif file_coding == "yuv":
return 'I420'
elif file_coding == "mpeg-1":
return 'PIMI'
elif file_coding == "mp4g-4":
return 'XVID'
elif file_coding == "ogg vorbis":
return 'THEO'
elif file_coding == "flash":
return 'FLV1'
else:
return 'mp4v'
# 畸变矫正
def __distortion_correction(self, img, origin_width, origin_height):
# 去畸变
new_camera_mtx, roi = cv2.getOptimalNewCameraMatrix(self.mtx, self.dist, (origin_width, origin_height), 0,
(origin_width, origin_height))
dst = cv2.undistort(img, self.mtx, self.dist, None, new_camera_mtx)
# 根据ROI区域裁剪图片
x, y, width, height = roi
dst = dst[y:y + height, x:x + width]
dst = cv2.resize(dst, (origin_width, origin_height))
return dst
if __name__ == '__main__':
cal = CameraCalibration()
cal.calibration_init(9, 6, "./pic/*.jpg")
cor = CameraCorrect()
cor.correct_pic("./pic/cm_flag_5.jpg", "./output_pic/")
cor.correct_vid2vid("./vid/vid_demo_1.mp4", "./output_vid/")
cor.correct_vid2pic("./vid/vid_demo_1.mp4", "./output_vid/")