Skip to content

Instantly share code, notes, and snippets.

@mattak
Created July 15, 2025 11:00
Show Gist options
  • Save mattak/aaf2665fd3a0720e124df10f90d99d18 to your computer and use it in GitHub Desktop.
Save mattak/aaf2665fd3a0720e124df10f90d99d18 to your computer and use it in GitHub Desktop.

Revisions

  1. mattak created this gist Jul 15, 2025.
    101 changes: 101 additions & 0 deletions calibrate_fisheye.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,101 @@
    import cv2
    import numpy as np
    import glob
    import os
    import argparse

    def calibrate_fisheye(folder_path, pattern_size=(9, 6), square_size=1.0, save_path='fisheye_camera_params.npz'):
    # 3D座標の準備(Z=0平面)
    objp = np.zeros((1, pattern_size[0] * pattern_size[1], 3), np.float32)
    objp[0, :, :2] = np.mgrid[0:pattern_size[0], 0:pattern_size[1]].T.reshape(-1, 2)
    objp *= square_size

    objpoints = [] # 3D点
    imgpoints = [] # 2D点

    # 画像取得(再帰的に)
    image_paths = glob.glob(os.path.join(folder_path, '**', '*.jpg'), recursive=True)
    image_paths += glob.glob(os.path.join(folder_path, '**', '*.png'), recursive=True)

    print(f"{len(image_paths)} 枚の画像が見つかりました")
    if not image_paths:
    print("画像が見つかりませんでした")
    return

    for path in image_paths:
    img = cv2.imread(path)
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    ret, corners = cv2.findChessboardCorners(
    gray, pattern_size,
    cv2.CALIB_CB_ADAPTIVE_THRESH + cv2.CALIB_CB_FAST_CHECK + cv2.CALIB_CB_NORMALIZE_IMAGE
    )

    if ret:
    corners2 = cv2.cornerSubPix(
    gray, corners, (3, 3), (-1, -1),
    (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.01)
    )
    imgpoints.append(corners2)
    objpoints.append(objp)
    print(f"OK: {path}")
    else:
    print(f"NG: {path}")

    if len(objpoints) < 3:
    print("十分な有効な画像がありません。最低3枚以上必要です。")
    return

    N_OK = len(objpoints)
    img_shape = gray.shape[::-1]

    K = np.zeros((3, 3))
    D = np.zeros((4, 1))
    rvecs = [np.zeros((1, 1, 3)) for _ in range(N_OK)]
    tvecs = [np.zeros((1, 1, 3)) for _ in range(N_OK)]

    flags = (
    cv2.fisheye.CALIB_RECOMPUTE_EXTRINSIC +
    cv2.fisheye.CALIB_CHECK_COND +
    cv2.fisheye.CALIB_FIX_SKEW
    )

    rms, K, D, rvecs, tvecs = cv2.fisheye.calibrate(
    objpoints,
    imgpoints,
    img_shape,
    K,
    D,
    rvecs,
    tvecs,
    flags,
    (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 100, 1e-6)
    )

    print("\n=== FishEye キャリブレーション結果 ===")
    print("RMS 誤差:", rms)
    print("Camera Matrix (K):\n", K)
    print("Distortion Coefficients (D):\n", D.ravel())

    # 保存
    np.savez(save_path, K=K, D=D)
    print(f"\n保存しました: {save_path}")


    if __name__ == '__main__':
    parser = argparse.ArgumentParser(description='OpenCV FishEye カメラキャリブレーションスクリプト')
    parser.add_argument('--folder', type=str, required=True, help='チェスボード画像のフォルダパス')
    parser.add_argument('--cols', type=int, default=9, help='チェスボードの列数(交点数)')
    parser.add_argument('--rows', type=int, default=6, help='チェスボードの行数(交点数)')
    parser.add_argument('--square-size', type=float, default=1.0, help='1マスのサイズ(mm)')
    parser.add_argument('--output', type=str, default='fisheye_camera_params.npz', help='出力ファイル名')

    args = parser.parse_args()
    pattern_size = (args.cols, args.rows)

    calibrate_fisheye(
    folder_path=args.folder,
    pattern_size=pattern_size,
    square_size=args.square_size,
    save_path=args.output
    )