【備忘録】OpenCVによるカメラキャリブレーション

# 参考

- 以下の記事内容を拝借しております。自分用のメモとしてまとめます。

- 実装については以下を参考にする
https://miyashinblog.com/camera-calibration/


- 行列計算については以下を参考にする

https://www.qoosky.io/techs/67a3d876c4

# 事前準備

- 以下より、サンプルコード＆サンプルデータをダウンロード

https://github.com/spmallick/learnopencv


/learnopencv-master/CameraCalibration の写真を確認
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/908963/81a7baa2-b52a-76dd-2f0e-3034cc166985.png)


- 画像を見ると広角による歪みを確認できる


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/908963/71e3e8c3-701f-f7c8-8a98-0a7ee3863205.png)



- カメラ内部＆外部パラメータを推測するために、チェスボードのような、歪がなかったらどのように見えるか分かっているパターンを何枚か撮影する

- チェスボードでは四角形ナー検出などで検出し、本来の位置からのズレ量などを用いて数学的な処理を行い歪みパラメータを推定

- カメラキャリブレーションには少なくとも10枚程度の画像が必要


- openCVのインストール (anacondaの場合)

```
conda install opencv
```


# 画像の歪みを確認

- 以下のコードを実行


``` python
import cv2
import numpy as np
import os
import glob
from matplotlib import pyplot as plt

# Defining the dimensions of checkerboard
CHECKERBOARD = (6,9)

# termination criteria
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)

# cv2.TERM_CRITERIA_EPS:指定された精度(epsilon)に到達したら繰り返し計算を終了する
# cv2.TERM_CRITERIA_MAX_ITER:指定された繰り返し回数(max_iter)に到達したら繰り返し計算を終了する
# cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER : 上記のどちらかの条件が満たされた時に繰り返し計算を終了する
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)

# Defining the world coordinates for 3D points
objp = np.zeros((1, CHECKERBOARD[0]*CHECKERBOARD[1], 3), np.float32)
objp[0,:,:2] = np.mgrid[0:CHECKERBOARD[0], 0:CHECKERBOARD[1]].T.reshape(-1, 2)
prev_img_shape = None

file_name = glob.glob('./learnopencv-master/CameraCalibration/images/*.jpg')[0]
img = cv2.imread(file_name)
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)

# Find the chess board corners
# If desired number of corners are found in the image then ret = true
ret, corners = cv2.findChessboardCorners(gray, CHECKERBOARD, cv2.CALIB_CB_ADAPTIVE_THRESH+cv2.CALIB_CB_FAST_CHECK+cv2.CALIB_CB_NORMALIZE_IMAGE)

# Creating vector to store vectors of 3D points for each checkerboard image
objpoints = []

# Creating vector to store vectors of 2D points for each checkerboard image
imgpoints = [] 

if ret == True:
    objpoints.append(objp)
    
    # refining pixel coordinates for given 2d points.
    corners2 = cv2.cornerSubPix(gray,corners,(11,11),(-1,-1),criteria)
    
    imgpoints.append(corners2)
    
    # Draw and display the corners
    img = cv2.drawChessboardCorners(img, CHECKERBOARD, corners2,ret)
    

plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB)) # OpenCV は色がGBR順なのでRGB順に並べ替える
plt.show()
```

- サンプル画像の1枚に検出した制御点を描画した結果が，以下になります:

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/908963/047c1912-adfd-5422-7dca-e441a4c31576.png)


# キャリブレーションを実行

- 制御点について，3次元空間中での位置(object points)と2次元画像上での位置（image points)が分かったので，キャリブレーションができるようになりました
- 以下のコードを実行
    - ret : 処理が上手くできた時にtrueを格納
    - mtx : カメラ行列(内部パラメータ)
    - rvecs : 回転ベクトル
    - tvecs : 並進ベクトル

``` python
"""
Performing camera calibration by 
passing the value of known 3D points (objpoints)
and corresponding pixel coordinates of the 
detected corners (imgpoints)
"""
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(objpoints, imgpoints, gray.shape[::-1],None,None)

```

- カメラ行列

```
[[694.50265261   0.         313.81893904]
 [  0.         577.05803359 226.4660374 ]
 [  0.           0.           1.        ]]
```

- 回転ベクトル

```
(array([[0.54342571],
       [0.81584567],
       [1.29195997]]),)
```

- 並進ベクトル

```
(array([[ 0.56790847],
       [-2.13991942],
       [12.12224481]]),)

```

- 行列計算より、キャリブレーションを実行
- 第4引数に「1」を入れているが、この場合はすべての元画像ピクセルを保持する。補正後の画像のエッジ部分を黒色で埋める

``` python
# Using the derived camera parameters to undistort the image

img = cv2.imread(file_name)

# Refining the camera matrix using parameters obtained by calibration
# ROI:Region Of Interest(対象領域)
# カメラ行列(内部パラメータ)を改善する関数
newcameramtx, roi = cv2.getOptimalNewCameraMatrix(mtx, dist, (w,h), 1, (w,h))

# Method 1 to undistort the image
# 歪み補正を行う関数
dst = cv2.undistort(img, mtx, dist, None, newcameramtx)

plt.imshow(cv2.cvtColor(dst, cv2.COLOR_BGR2RGB)) # OpenCV は色がGBR順なのでRGB順に並べ替える
plt.show()
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/908963/9931314e-f617-a8a9-ba05-9de453679fbd.png)


# 実環境へ適応するための手順
- 以下の実験が参考になる


https://tecsingularity.com/opencv/cameracalib/

- 適当なチェスボードの画像を用意する。これは歪みのないもの

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/908963/b74c3887-081f-9e84-fcf8-67e0d23a7bef.png)

- 上記の歪みのないチェスボードを印刷し、あらゆる画角で撮影する。最低でも10枚以上

以下、参考
https://tecsingularity.com/opencv/cameracalib/

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/908963/4195215a-69e2-da25-9472-dc1f75025dd4.png)


- openCVでキャリブレーションを行う。以上。