カニカニクラブライフ

Python系技術メモ、書評とか

Zhangのカメラキャリブレーションで、きちんとチェスボードの交点を検出できているか確かめるコード

russeng.hatenablog.jp


でパクったキャリブレーションのコードですが、


OpenCV を利用した非接触 3 次元座標測定技術に関する研究

この報告によれば、チェスボードの角度がカメラの視線方向に対して斜め15度を超えると、

"検出した交点座標が画像上の交点位置と異なる"

"全ての交点座標が検出できない"             などの問題が発生するそう。


そこで、

  • 目視により検出したポイントを表示
  • ボタンにより画像を使用するか否かを人の目で判別する   という機能をつけます。
上記コードにつけたし
# -*- coding: utf-8 -*-

import numpy
import cv2
from glob import glob
import Tkinter
import tkMessageBox


square_size = 23.0      # 正方形のサイズ
pattern_size = (10, 7)  # 模様のサイズ
pattern_points = numpy.zeros( (numpy.prod(pattern_size), 3), numpy.float32 ) #チェスボード(X,Y,Z)座標の指定 (Z=0)
pattern_points[:,:2] = numpy.indices(pattern_size).T.reshape(-1, 2)
pattern_points *= square_size
obj_points = []
img_points = []
 
for fn in glob("*.jpg"):
    # 画像の取得
    im = cv2.imread(fn, 0)
    print "loading..." + fn
    # チェスボードのコーナーを検出
    found, corner = cv2.findChessboardCorners(im, pattern_size)
    # コーナーがあれば
    if found:
        term = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_COUNT, 30, 0.1)
        cv2.cornerSubPix(im, corner, (5,5), (-1,-1), term)    #よくわからないがサブピクセル処理(小数点以下のピクセル単位まで精度を求める)
        cv2.drawChessboardCorners(im, pattern_size, corner,found)
        cv2.imshow('found corners in' + fn,im)
    # コーナーがない場合のエラー処理
    if not found:
        print 'chessboard not found'
        continue
    # 選択ボタンを表示
    root = Tkinter.Tk()
    root.withdraw()
    if tkMessageBox.askyesno('askyesno','この画像の値を採用しますか?'):
         img_points.append(corner.reshape(-1, 2))   #appendメソッド:リストの最後に因数のオブジェクトを追加 #corner.reshape(-1, 2) : 検出したコーナーの画像内座標値(x, y)
         obj_points.append(pattern_points)
         print 'found corners in ' + fn + ' is adopted'
    else:
         print 'found corners in ' + fn + ' is not adopted'        
    cv2.destroyAllWindows()
    
    
# 内部パラメータを計算
rms, K, d, r, t = cv2.calibrateCamera(obj_points,img_points,(im.shape[1],im.shape[0]))
# 計算結果を表示
print "RMS = ", rms
print "K = \n", K
print "d = ", d.ravel()
# 計算結果を保存
numpy.savetxt("K.csv", K, delimiter =',',fmt="%0.14f") #カメラ行列の保存
numpy.savetxt("d.csv", d, delimiter =',',fmt="%0.14f") #歪み係数の保存

コーナーの描画には、opencv出来合いの関数を使用*1

めんどくさいので、main関数にする表記も外しました*2



結果はこんな。

f:id:russENG:20150620120548p:plain

*1:tutorialにあるように代入表記すると僕の環境(opencv2系)だと戻り値はNoneなのでエラ―を吐きます。

*2:main関数にするメリット、いまだによくわからない

pythonとOpenCVでカメラキャリブレーション(1個のカメラの内部パラメータと歪み係数を求める)するコード(パクリ)

パクりました。

# -*- coding: utf-8 -*-

import numpy
import cv2
from glob import glob
 
def main():
 
    square_size = 1.0      # 正方形のサイズ
    pattern_size = (10, 7)  # 模様のサイズ
    pattern_points = numpy.zeros( (numpy.prod(pattern_size), 3), numpy.float32 ) #チェスボード(X,Y,Z)座標の指定 (Z=0)
    pattern_points[:,:2] = numpy.indices(pattern_size).T.reshape(-1, 2)
    pattern_points *= square_size
    obj_points = []
    img_points = []
 
    for fn in glob("*.jpg"):
        # 画像の取得
        im = cv2.imread(fn, 0)
        print "loading..." + fn
        # チェスボードのコーナーを検出
        found, corner = cv2.findChessboardCorners(im, pattern_size)
        # コーナーがあれば
        if found:
            term = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_COUNT, 30, 0.1)
            cv2.cornerSubPix(im, corner, (5,5), (-1,-1), term)
        # コーナーがない場合のエラー処理
        if not found:
            print 'chessboard not found'
            continue
        img_points.append(corner.reshape(-1, 2))   #appendメソッド:リストの最後に因数のオブジェクトを追加
        obj_points.append(pattern_points)
        #corner.reshape(-1, 2) : 検出したコーナーの画像内座標値(x, y)
 
    # 内部パラメータを計算
    rms, K, d, r, t = cv2.calibrateCamera(obj_points,img_points,(im.shape[1],im.shape[0]))
    # 計算結果を表示
    print "RMS = ", rms
    print "K = \n", K
    print "d = ", d.ravel()
    # 計算結果を保存
    numpy.savetxt("rms.csv", rms, delimiter =',',fmt="%0.14f")
    numpy.savetxt("K.csv", K, delimiter =',',fmt="%0.14f")
 
if __name__ == '__main__':
    main()


ついでにコードに要らないコメントを書き込んで見にくくしてやりました*1

画像取得は、中で何やってるかよくわからんですが、ソースがあるフォルダ内の画像をfor文で
順番に呼んでくれる感じです。
f:id:russENG:20150616004218p:plain

結果はこうなりました

f:id:russENG:20150616004511p:plain



うん、ちゃんと出来てるのかすらよくわからんな。

これを使って歪み補正します ⇒







以下からソースを拝借しました。python-gazo.blog.jp

*1:ゲヘー

カメラの内部パラメータと歪み係数を求めるキャリブレーションって、チェスボードを視線に水平にしなくちゃダメなんじゃないの?

 

実験して確かめる。

 

何の話?

ステレオ視の話。

・カメラキャリブレーションにより、内部パラメータ・歪み係数と、外部パラメータを求める必要がある。 (過去ブログ記事参照)

・これにはよくZhangの方法と呼ばれる、チェスボードを撮影し、マスの交点を自動探索させる方法が用いられる。

・内部パラメータ・歪み係数は、左右のカメラに対し、それぞれ個別に求める必要がある。

・カメラ位置と対象物の関係、つまり奥行きに関する直接的な情報を決めるのは、2つのステレオカメラの位置についての外部パラメータである。 

・tutorial*1でも、内部パラメータ・歪み係数を求める際には、zは一定で入力する、という記述があるし、実際のコードも、z=0を入力している。

What about the 3D points from real world space? Those images are taken from a static camera and chess boards are placed at different locations and orientations. So we need to know (X,Y,Z) values. But for simplicity, we can say chess board was kept stationary at XY plane, (so Z=0 always) and camera was moved accordingly. This consideration helps us to find only X,Y values.

・実際、内部パラメータと歪み係数を得てできる画像への操作は、レンズ方向の歪みの補正である。

 

疑問 

では、奥行き情報はひとつのカメラではわからないのだから、チェスボード内のすべての点で奥行距離を一致させなくてはいけないのでは?

→つまり、カメラに対して水平にチェスボードを設置させなくてはいけないのじゃないの?

 

・しかしながら既存のいろんな資料*2で、チェスボードを「いろんな角度で」撮影することとしている。

・そして元のZhangの論文は英語なので読む気にならない。

・そうは言っても、チェスボードのマスの大きさは与えているわけだし、マスが小さく映っているか、大きく映っているかで、カメラからの距離と、チェスボードの傾きはわかっているのじゃないの?

 

よくわからないので実験します。

方法:「チェスボードをわざと傾ける」グループと、「チェスボードまあまあ水平にした」グループで、各パラメータや補正結果に違いが出るのか。

 

●準備作業

 ・ステレオカメラ、つまり2台のカメラを用意する*3

 ・2台のカメラをなんらかの方法で固定した計測システムをつくる

 ・カメラの自動明るさ調整などのスイッチを切る*4

 ・カメラを2台接続し、同時に映像を記録するためのコードを用意する*5

 

●手順1 : 内部パラメータと歪み係数を求めて比較する

 ・キャリブレーションするコードはこんな感じ

russeng.hatenablog.jp

 

・「斜めにチェスボードを撮影した場合、交点が正確に検出できない場合がある*6」現象が起こるらしいので、目視で確認するコードを作ったりする。

 

●手順2 : 外部パラメータを求めて比較する

 ・まだやってません。

 

結果

 

 

 

 

ステレオマッチング 自分メモ

とある目的のためにステレオマッチングをやりたい。

python + OpenCVでやる予定だけど、原理の話がわからなければ、関数を使えるわけもなし。

ざっくりと資料を集めて読んでいたらなんとなくわかってきたので、自分メモ。

 

メニュー

進行・わけかたは、tutorial*1になんとなくだいたい依拠します。

  1. カメラのキャリブレーション
  2. 姿勢推定
  3. エピポーラ線を求める
  4. ステレオマッチング

 

 1.カメラキャリブレーション

 以下の3つを求める作業。*2

 

・内部パラメータ

camera \; matrix = \left [ \begin{matrix}   f_x & 0 & c_x \\  0 & f_y & c_y \\   0 & 0 & 1 \end{matrix} \right ]*3 

f:ピント c:画像中心。(下記の式のA)

 

・歪み係数

Distortion \; coefficients=(k_1 \hspace{10pt} k_2 \hspace{10pt} p_1 \hspace{10pt} p_2 \hspace{10pt} k_3)*4 

レンズの歪みで線がまるくなるのをまっすぐに直すためのパラメ。カメラに固有の値。(下記の式にはない。でもtutorialにはある。)

 

・外部パラメータ

カメラの姿勢に関するパラメ。回転行列Rと並進ベクトルtからなる。カメラに固有の値ではない。が、画像1枚1枚に依存する値なのかもよくわからない。

 

内部パラメータと歪み係数は、カメラに固有の値なので、ステレオマッチングの際も、カメラ1個ごとにそれぞれ求める(たぶん)。

外部パラメータは、ステレオカメラの場合は、2個同時に(2個を含んだ世界の座標系として)求める必要がある(たぶん)。

 

tutorialでキャリブレーションと呼んでいるのは、内部パラメータと歪み係数を求めることのみである。

 

画像内の座標(u, v)をベクトルm、

THE☆ワールド(実世界)の座標(x, y, z)をベクトルXとすると、

この両者の関係を内部パラメータと外部パラメータを使って以下のように表せられる。(by 教科書*5

m ~ A(R|t) X      (~:両辺がその定数倍を許して等しい*6

 

つまり3つのパラメータを求めることで、画像から3次元空間の測量ができる下地が出揃う。

 

具体的なキャリブレーションの方法

Zhangの方法とよばれる方法が一般的。他には、Tsaiの方法という3次元の方法もある*7

 

サイズが既知のチェックボードを撮影し、その交点を自動的に探索させ、対応する画素と、現実の座標系上の点のペアを何個も何個も求める。

そして最小二乗法的なサムシングを使って求める。*8*9

 

外部パラメータの同定にも、このチェックボードを使った方法を用いるはずなんだが、ステレオカメラを同時にする方法について説明しているドキュメントは少ない。この記事くらい。*10

これのpython版をつくる(よてい)

 

3つのパラメータ同定を同時にやっていいのか、

それとも内部パラメータだけ先に求めたりするのかも今のところ不明。

 

2.姿勢推定

たぶん外部パラメータを求めることを指す。

3.エピポーラ線を求める

求めない(え。

4のステレオマッチングに使う技術らしい。直接原理を理解して関数を書くのではなさそう。

外部パラメータの回転行列を求めて画像の平行化処理を行うと、マッチングがしやすくなるらしい。*11

 

 

4.ステレオマッチング

 

・ステレオカメラの設置の方法2種類

 ・トーイン撮影

 ・平行ステレオ

 

・平行ステレオだと、基線長(2つのカメラの物理的距離)と焦点距離の2つの定数を使った、簡単な式で、奥行が求められる。(じゃあ今までのキャリブレーションの話はなんだったんじゃい!)

 

・マッチングは自動処理。アルゴリズムにも、ブロックマッチングやグラフカットなど種類が。

*1:Camera Calibration and 3D Reconstruction — OpenCV-Python Tutorials 1 documentation

*2:と、ここではする。tutorialのキャリブレーションについてのドキュメントでは、内部パラメータと歪み係数を求めることのみの記述。

*3:Camera Calibration — OpenCV-Python Tutorials 1 documentation

*4:Camera Calibration — OpenCV-Python Tutorials 1 documentation

*5:
ディジタル画像処理

*6:そして正しくは、式のmとXは、ベクトルではない。ベクトルが表す同次座標であり、そのことを示す記法(記号の上に~がつく)を教科書ではしている。同次座標がなんなのかは知らない。

*7:http://www.eyedeal.co.jp/doc/stereo_algorithm.pdf

*8:http://www.eyedeal.co.jp/doc/stereo_algorithm.pdf

*9:
ディジタル画像処理

*10:JugglerYou日記 :: ステレオカメラの歪み補正&平行化

*11:JugglerYou日記 :: ステレオカメラの歪み補正&平行化