【追記】
元投稿がPython2 & OpenCV2系?(Pythonにfisheyeモジュールもomnidirモジュールも無い)状態で古かったので、Python3およびOpenCV4系でカメラキャリブレーションのサンプルを書き直しました。
元の投稿は、通常のカメラキャリブレーションモジュールで魚眼レンズのキャリブレーションをトライした記録として残しておきます。
最近Pythonのコードの書きやすさに魅力を感じて、結構Pythonでサンプルプログラムとか書いてます。
で、表題の件。
この先仕事で魚眼とか超広角レンズを利用するかもしれないので、お試しサンプルを作ろうとしたら、
C++版OpenCVに存在する cv::fisheye系のI/FがPython版には無いらしい。。。
しかたなく、色々諦めきれなかったので、Pythonで通常のキャリブレーションの手番を踏んで、
魚眼レンズをキャリブレーションしてみました。
(Python版で出来たら、C++版でちゃんとした実装すれば技術的には問題無いでしょう理論
以下、YouTube動画↓
https://www.youtube.com/watch?v=WGtZeyfzve4
あれ?なんか普通に出来てる? 理由要確認。
あと、気持ち切り取られている箇所多いような気がしないでもない。
たぶん広角130度くらい確保出来ている。
ちなみに使用したカメラは以下↓
https://www.amazon.co.jp/ELP-USB2-0-Ominivison-OV2710-%E5%BA%A6%E3%83%A1%E3%82%AC%E3%83%94%E3%82%AF%E3%82%BB%E3%83%AB%E9%AD%9A%E7%9C%BC%E3%83%AC%E3%83%B3%E3%82%BA/dp/B017R02JLI
→商品ページ切れてました(2021/04/18時点) 多分これと同じ。https://www.amazon.co.jp/ELP-Android%E3%81%AELinux%E7%94%A8-USB2-0-OV2710-%E5%BA%A6%E3%83%A1%E3%82%AC%E3%83%94%E3%82%AF%E3%82%BB%E3%83%AB%E9%AD%9A%E7%9C%BC%E3%83%AC%E3%83%B3%E3%82%BA/dp/B08CBSG5DV?th=1
コードも以下に貼り付け↓
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import numpy as np
import cv2
import Tkinter
import tkMessageBox
square_side_length = 23.0 # チェスボード内の正方形の1辺のサイズ(mm)
grid_intersection_size = (10, 7) # チェスボード内の格子数
pattern_points = np.zeros( (np.prod(grid_intersection_size), 3), np.float32 )
pattern_points[:,:2] = np.indices(grid_intersection_size).T.reshape(-1, 2)
pattern_points *= square_side_length
object_points = []
image_points = []
root = Tkinter.Tk()
root.withdraw()
video_input = cv2.VideoCapture(1)
if (video_input.isOpened() == False):
exit()
camera_mat, dist_coef = [], []
if tkMessageBox.askyesno('askyesno','キャリブレーションデータ(K.csv, d.csv)を読み込みますか?'):
# キャリブレーションデータの読み込み
camera_mat = np.loadtxt('K.csv', delimiter=',')
dist_coef = np.loadtxt('d.csv', delimiter=',')
print "K = \n", camera_mat
print "d = ", dist_coef.ravel()
else:
# チェスボードの撮影
capture_count = 0
while(True):
ret, frame = video_input.read()
# チェスボード検出用にグレースケール画像へ変換
#grayscale_image = cv2.cvtColor(frame, cv2.COLOR_RGB2GRAY)
# チェスボードのコーナーを検出
#found, corner = cv2.findChessboardCorners(grayscale_image, grid_intersection_size)
found, corner = cv2.findChessboardCorners(frame, grid_intersection_size)
if found == True:
print 'findChessboardCorners : True'
# 現在のOpenCVではfindChessboardCorners()内で、cornerSubPix()相当の処理が実施されている?要確認
#term = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_COUNT, 30, 0.1)
#cv2.cornerSubPix(grayscale_image, corner, (5,5), (-1,-1), term)
#cv2.drawChessboardCorners(grayscale_image, grid_intersection_size, corner, found)
cv2.drawChessboardCorners(frame, grid_intersection_size, corner, found)
if found == False:
print 'findChessboardCorners : False'
cv2.putText(frame, "Enter:Capture Chessboard(" + str(capture_count) + ")", (100, 50), cv2.FONT_HERSHEY_SIMPLEX, 0.4, (0,0,255), 1)
cv2.putText(frame, "N :Completes Calibration Photographing", (100, 75), cv2.FONT_HERSHEY_SIMPLEX, 0.4, (0,0,255), 1)
cv2.putText(frame, "ESC :terminate program", (100, 100), cv2.FONT_HERSHEY_SIMPLEX, 0.4, (0,0,255), 1)
#cv2.putText(grayscale_image, "Enter:Capture Chessboard(" + str(capture_count) + ")", (100, 50), cv2.FONT_HERSHEY_SIMPLEX, 0.4, (0,0,255), 1)
#cv2.putText(grayscale_image, "ESC :Completes Calibration Photographing.", (100, 75), cv2.FONT_HERSHEY_SIMPLEX, 0.4, (0,0,255), 1)
cv2.imshow('original', frame)
#cv2.imshow('findChessboardCorners', grayscale_image)
c = cv2.waitKey(50) & 0xFF
if c == 13 and found == True: # Enter
# チェスボードコーナー検出情報を追加
image_points.append(corner)
object_points.append(pattern_points)
capture_count += 1
if c == 110: # N
if tkMessageBox.askyesno('askyesno','チェスボード撮影を終了し、カメラ内部パラメータを求めますか?'):
cv2.destroyAllWindows()
break
if c == 27: # ESC
if tkMessageBox.askyesno('askyesno','プログラムを終了しますか?'):
video_input.release()
cv2.destroyAllWindows()
exit()
if len(image_points) > 0:
# カメラ内部パラメータを計算
print 'calibrateCamera() start'
rms, K, d, r, t = cv2.calibrateCamera(object_points,image_points,(frame.shape[1],frame.shape[0]),None,None)
print "RMS = ", rms
print "K = \n", K
print "d = ", d.ravel()
np.savetxt("K.csv", K, delimiter =',',fmt="%0.14f") #カメラ行列の保存
np.savetxt("d.csv", d, delimiter =',',fmt="%0.14f") #歪み係数の保存
camera_mat = K
dist_coef = d
# 再投影誤差による評価
mean_error = 0
for i in xrange(len(object_points)):
image_points2, _ = cv2.projectPoints(object_points[i], r[i], t[i], camera_mat, dist_coef)
error = cv2.norm(image_points[i], image_points2, cv2.NORM_L2) / len(image_points2)
mean_error += error
print "total error: ", mean_error/len(object_points) # 0に近い値が望ましい(魚眼レンズの評価には不適?)
else:
print "findChessboardCorners() not be successful once"
# 歪み補正画像表示
if camera_mat != []:
while(True):
ret, frame = video_input.read()
undistort_image = cv2.undistort(frame, camera_mat, dist_coef)
cv2.imshow('original', frame)
cv2.imshow('undistort', undistort_image)
c = cv2.waitKey(50) & 0xFF
if c==27: # ESC
break
video_input.release()
cv2.destroyAllWindows()
以上。