天天看點

OpenCV 相機校正

1. 相機标定

根據張正友校正算法,利用棋盤格資料校正對車載相機進行校正,計算其内參矩陣,外參矩陣和畸變系數。

标定的流程是:

  • 準備棋盤格資料,即用于标定的圖檔
  • 對每一張圖檔提取角點資訊
  • 在棋盤上繪制提取到的角點(非必須,隻是為了顯示結果)
  • 利用提取的角點對相機進行标定
  • 擷取相機的參數資訊

2.關于相機校正用到的幾個API:

  1. 尋找棋盤圖中的棋盤角點
rect, corners = cv2.findChessboardCorners(image, pattern_size, flags)      

參數:

  • Image: 輸入的棋盤圖,必須是8位的灰階或者彩色圖像
  • Pattern_size:棋盤圖中每行每列的角點個數(内角點)。
  • flags: 用來定義額外的濾波步驟以有助于尋找棋盤角點。所有的變量都可以單獨或者以邏輯或的方式組合使用。取值主要有:

    CV_CALIB_CB_ADAPTIVE_THRESH :使用自适應門檻值(通過平均圖像亮度計算得到)将圖像轉換為黑白圖,而不是一個固定的門檻值。

    CV_CALIB_CB_NORMALIZE_IMAGE :在利用固定門檻值或者自适應的門檻值進行二值化之前,先使用cvNormalizeHist來均衡化圖像亮度。

    CV_CALIB_CB_FILTER_QUADS :使用其他的準則(如輪廓面積,周長,方形形狀)來去除在輪廓檢測階段檢測到的錯誤方塊。

傳回:

  • Corners:檢測到的角點
  • rect: 輸出是否找到角點,找到角點傳回1,否則傳回0
  1. 檢測完角點之後可以将測到的角點繪制在圖像上,使用的API是:
cv2.drawChessboardCorners(img, pattern_size, corners, rect)      

參數:

  • Img: 預繪制檢測角點的圖像
  • pattern_size : 預繪制的角點的形狀
  • corners: 角點矩陣
  • rect: 表示是否所有的棋盤角點被找到,可以設定為findChessboardCorners的傳回值

注意:如果發現了所有的角點,那麼角點将用不同顔色繪制(每行使用單獨的顔色繪制),并且把角點以一定順序用線連接配接起來。

  1. 利用定标的結果計算内外參數
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(object_points, image_points, image_size, None, None)      

參數:

  • Object_points:世界坐标系中的點,在使用棋盤的場合,令z的坐标值為0,而x,y坐标用裡面來度量,選用英寸機關,那麼所有參數計算的結果也是用英寸表示。最簡單的方式是定義棋盤的每一個方塊為一個機關。
  • image_points:在圖像中尋找到的角點的坐标,包含object_points所提供的所有點
  • image_size: 圖像的大小,以像素為衡量機關

傳回:

  • ret: 傳回值
  • mtx: 相機的内參矩陣,大小為3*3的矩陣
  • dist: 畸變系數,為5*1大小的矢量
  • rvecs: 旋轉變量
  • tvecs: 平移變量
2.1 圖像去畸變

上一步中得到相機的内參及畸變系數,利用其進行圖像的去畸變,最直接的方法就是調用opencv中的函數得到去畸變的圖像:

def img_undistort(img, mtx, dist):
    dst = cv2.undistort(img, mtx, dist, None, mtx)
    return dst      

求畸變的API:

dst = cv2.undistort(img, mtx, dist, None, mtx)      

參數:

  • Img: 要進行校正的圖像
  • mtx: 相機的内參
  • dist: 相機的畸變系數
  • dst: 圖像校正後的結果

3. 相機校正

import cv2
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
import glob

def plot_contrast_imgs(origin_img, converted_img, origin_img_title="origin_img", converted_img_title="converted_img", converted_img_gray=False):
    """
    用于對比顯示兩幅圖像
    """
    fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(15, 20))
    ax1.set_title(origin_img_title)
    ax1.imshow(origin_img)
    ax2.set_title(converted_img_title)
    if converted_img_gray==True:
        ax2.imshow(converted_img, cmap="gray")
    else:
        ax2.imshow(converted_img)
    plt.show()

# 1. 參數設定:定義棋盤橫向和縱向的角點個數并指定校正圖像的位置
nx = 9
ny = 6
file_paths = glob.glob("./camera_cal/calibration*.jpg")
# 2. 計算相機的内外參數及畸變系數
def cal_calibrate_params(file_paths):
    object_points = []  # 三維空間中的點:3D
    image_points = []   # 圖像空間中的點:2d
    # 2.1 生成真實的交點坐标:類似(0,0,0), (1,0,0), (2,0,0) ....,(6,5,0)的三維點
    objp = np.zeros((nx * ny, 3), np.float32)
    objp[:, :2] = np.mgrid[0:nx, 0:ny].T.reshape(-1, 2)
    # 2.2 檢測每幅圖像角點坐标
    for file_path in file_paths:
        img = cv2.imread(file_path)
        # 将圖像轉換為灰階圖
        gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
        # 自動檢測棋盤格内4個棋盤格的角點(2白2黑的交點)
        rect, corners = cv2.findChessboardCorners(gray, (nx, ny), None)
        # 若檢測到角點,則将其存儲到object_points和image_points
        if rect == True:
            object_points.append(objp)
            image_points.append(corners)
    # 2.3 擷取相機參數
    ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(object_points, image_points, gray.shape[::-1], None, None)
    return ret, mtx, dist, rvecs, tvecs


def img_undistort(img, mtx, dist):
    """
    圖像去畸變
    """
    return cv2.undistort(img, mtx, dist, None, mtx)

# 測試去畸變函數的效果
file_paths = glob.glob("./camera_cal/calibration*.jpg")
ret, mtx, dist, rvecs, tvecs = cal_calibrate_params(file_paths)
if mtx.any() != None:  # a.any() or a.all()
    img = mpimg.imread("./camera_cal/calibration1.jpg")
    undistort_img = img_undistort(img, mtx, dist)
    plot_contrast_imgs(img, undistort_img)
    print("done!")
else:
    print("failed")