カニカニクラブライフ

Python系技術メモ、書評とか

今さら新しみのないサポートベクターマシン(SVM)のサンプルコード(Python,Scikit-learn)

・2年くらい前は、ネット上にあんまり日本語ドキュメントがなかったですが、今はたくさんあるサポートベクターマシン(Support Vector Machine,SVM)の回帰分析ver(SVR)をパッケージから呼び出すコードと実行結果を載せます。

f:id:russENG:20160320090508p:plain

1.SVMとは?
2.例題
3.いかに正しい予測器を選べば良いか
4.コードと結果

続きを読む

Python+OpenCVでのカメラキャリブレーション有りステレオビジョンのサンプルコードなど

russeng.hatenablog.jp

概要

1.上記過去記事での変数等の処理の流れ(フロー図)

2.サンプルコード

f:id:russENG:20160306223957p:plain

続きを読む

python+opencvでカメラキャリブレーション→ステレオマッチング→奥行き計測をする方法

今回でほぼパーペキにわかったのでまとめ

環境:python 2.7.6 OpenCV2.4.11

 
キャリブレーションをせずともステレオマッチングと、視差マップをもとにした奥行き計測はできる↓

 

russeng.hatenablog.jp

 

 


①事前
・左右カメラで同一のチェスボードを撮影した画像を数10枚程度(最低10枚くらい、確か)用意する(Zhangの方法)
・↑と同様のカメラの位置関係で撮影したステレオマッチング用の画像(奥行計測したい画像)を用意する


②cv2.findChessboardCorners(image, patternSize[, corners[, flags]]) → retval, corners
「チェスボードの交点を見つける」
・cornersには交点の画像内ピクセル座標が戻り値として与えられる
・撮影したキャリブレーション用画像すべてに対して行う
・cornersは、左右カメラ独立に取り出せるよう処理しておくのがよい


③cv2.calibrateCamera(objectPoints, imagePoints, imageSize[, cameraMatrix[, distCoeffs[, rvecs[, tvecs[,
flags[, criteria]]]]]]) → retval, cameraMatrix, distCoeffs, rvecs, tvecs
「カメラのキャリブレーション(カメラ行列と歪み係数を求める)」
・objectPointsには、キャリブレーションパターン内の座標値(パターン内の平面座標したがってz=0)
・imagePointsには、パターンを撮影した画像内の交点座標(先のcorners)を与える
・チェスボードはカメラに向かって水平にしなくてもよい
・1個のカメラのみにおける外部パラメータ(回転行列・並進ベクトル)も算出できるが、とりあえず使わない
・この関数を左右のカメラそれぞれにおいて独立に適用する


④cv2.stereoCalibrate(objectPoints, imagePoints1, imagePoints2, imageSize[, cameraMatrix1[, distCoeffs1[,
cameraMatrix2[, distCoeffs2[, R[, T[, E[, F[, criteria[, flags]]]]]]]]]]) → retval, cameraMatrix1,
distCoeffs1, cameraMatrix2, distCoeffs2, R, T, E, F
「撮影システムのキャリブレーション(左右のカメラを含めた外部パラメータを求める)」
・cameraMatrix1(,2)とdistCoeffs1(,2)には、先で求めたカメラ行列と歪み係数を入れる
・同様にcv2.findChessboardCornersで求めたピクセル座標と、パターン内の交点座標を入力する
・RとTは「左右のカメラ間の」回転行列と並進ベクトルであり、1で入力した方のカメラのカメラ座標系をそのままワールド座標系と考えて計算している
・カメラ行列、歪み係数も再度最適化(誤差を最小化するよう補正)される。
 つまり入力する必要は必ずしもないが、安定した解を素早く求めるために、先のcv2.calibrateCameraで求めておくことが推奨される。
    この"二度手間"の最適化を行わない条件で関数を動かすこともできる(flagsで制御)


⑤cv2.stereoRectify(cameraMatrix1, distCoeffs1, cameraMatrix2, distCoeffs2, imageSize, R, T[, R1[, R2[, P1[,
P2[, Q[, flags[, alpha[, newImageSize]]]]]]]]) → R1, R2, P1, P2, Q, validPixROI1, validPixRPI2
「それぞれのカメラの平行化のための回転行列・射影行列およびQを求める」
・前述のRとTを入力
・Qは視差-奥行のマッピング行列(ステレオマッチング後の3D座標の取得に必要)
・上記の3つの関数はこのドキュメント*1にリファレンスが載っている。以下に紹介する関数も特記しない限り載っている。この関数はなぜかpython2系の情報について書かれてないが、存在はする。


そもそも平行化とは?
ステレオマッチングとは、2つの画像間の対応点を探索し、対応点どうしのピクセル距離(視差)を求める作業である。その目的は、次式により、画像の取得過程(3次元→2次元)で失われた奥行情報を計算することにある。
    z=bf/(u-u')
ところでこの式は、以下のワールド座標-画像内座標の変換式を、左右のカメラが画像のx軸に平行であり、y方向・Z方向の位置は同一である(=平行ステレオ)と単純に仮定した場合に得られる変換式である。この仮定は、回転行列と並進ベクトルを用いて以下のように定義する。

しかし実際には、カメラのわずかな回転などにより、完全に平行にカメラを配置できるわけではない。
したがって平行化の操作とは、まるで"上記の仮定が成立する場合に得られた画像である"かのように、実際の画像を修正する作業である。
平行化の作業は、エピポーラ線を求めやすくもする、つまりステレオマッチングにおける対応点探索において解を求めやすくなる(書いてて思ったが、そっちの方の目的がメインじゃないのか?)


⑥cv2.initUndistortRectifyMap(cameraMatrix, distCoeffs, R, newCameraMatrix, size, m1type[, map1[, map2]]) →
map1, map2
「平行化と歪み補正を同時に行う"マップ"を求める」
・マップとは、OpenCVにおける画像の幾何変換のための位置指定情報を乗せた行列(関数Remapで使用する)
・左右のカメラ画像についてそれぞれ行う
・関数Rectifyで求めたRをRに、P1(P2)の左側の3×3行列をnewCameraMatrixに入力する
・sizeには歪み補正後の画像サイズを入力する
・cameraMatrixとdistCoeffsにはstereoCalibrate関数で求めたものを入力する


⑦cv2.remap(src, map1, map2, interpolation[, dst[, borderMode[, borderValue]]]) → dst
「マップを用いて画像を変換」
・initUndistort関数で求めたmap1,map2を入力する
・interpolationは補間方法の指定
・srcには変換したい元画像を入力し、distには変換後の画像が出力される
 (ここではステレオマッチングを行う用のペア画像を変換する)


マッチング関数(StereoBM関数、StereoSGBM関数等)を用い、remapで変換した画像に対しステレオマッチング
 →視差画像の取得


⑨cv2.reprojectImageTo3D(disparity, Q[, _3dImage[, handleMissingValues[, ddepth]]]) → _3dImage
「画像から3次元座標を得る」
・disparityにはマッチング関数で得た視差画像を、QにはstereoRectify関数で求めたものを入力する
・_3dImageは、dispartyと同じサイズでかつ3チャンネル(3次元座標)の構造体が入力されている


 
おわり
・CやC++でもほぼ同様の名前の関数を適用する(たぶん)
・コードはいずれまた
 

python+OpenCVでステレオマッチングするんだけど、キャリブレーション結果どうやって反映させるねん

python+OpenCVでステレオマッチング(左右カメラ画像の対応点探索)して、対象の3次元座標を計測する、というのを一応ゴールにやってきたんだけど

本家pythonチュートリアルDepth Map from Stereo Images — OpenCV-Python Tutorials 1 documentationは、今までの話(キャリブレーション、姿勢推定…云々)をすっとばして、いきなり「奥行マップをつくろう(唐突)」となるわけです。


視差disparity = B f / Z
となるわけだから、
奥行きZは = B f / disparityとなり、求められるのはわかる。

じゃあ基線長Bってどんな値なのか?単純にカメラ間の距離を定規ではかるだけでいいのだろうか?
そしてこの式が使えるのは平行ステレオの場合のみのはず(教科書((Amazon.co.jp: ディジタル画像処理: 本)))



今のところ僕の理解ではこんな流れのはず。
・内部パラメータをそれぞれのカメラごとに求める
・外部パラメータを左右のカメラ同時に求める
 ・solvePnP関数なるものによって、それぞれのカメラに対しての外部パラメータを求める方法は記載してある。これをどう合わせるのか目下謎。
 ・同時に求める(2枚の画像を読み込ませ)関数も存在するはず・・・。どこかに。
・わかった内部パラメータ、外部パラメータから、三次元座標への変換パラメータを導出する。
(あるいは、内部パラメーター、外部パラメーターを利用して歪み補正や平行化を行い、平行ステレオと同じ条件で得られた理想画像(っぽいの)に補正する)


よくわかんねーなーと思って調べてたら奥行きマップから3次元モデルを生成という記事があった。python-gazo.blog.jp


最終的にやりたいゴールをこれなので、とりあえずコードをパクリ、色々動かして見ながら調べてみることにした。コードはこれ、例によってmain関数は外す。視差画像を求めるまで。

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

import cv2

# 画像取得
gray_l = cv2.imread("left.png",0)
gray_r = cv2.imread("right.png",0)
# 画像のヒストグラム平坦化・平滑化
gray_l = cv2.GaussianBlur( cv2.equalizeHist(gray_l),(5,5), 0)
gray_r = cv2.GaussianBlur( cv2.equalizeHist(gray_r),(5,5), 0)
# BM法でステレオ対応点探索
stereo = cv2.StereoBM(cv2.STEREO_BM_BASIC_PRESET,ndisparities=32, SADWindowSize=21)
disp = stereo.compute(gray_l,gray_r)    # 視差を計算
# 視差データを8bitデータに変換(imshowで表示させるため)
disp = cv2.normalize(disp, alpha=0, beta=255, norm_type=cv2.NORM_MINMAX, dtype=cv2.CV_8U)
cv2.imshow("disp",disp)                 # 視差画像の表示
cv2.waitKey(0)
cv2.destroyAllWindows()


パクリ元python-gazo.blog.jp

・とりあえず動く。
ヒストグラム平坦化をなぜやるのかは不明。

Zhangの方法でチェスボード内の正方形のサイズを指定してどうなるの?

※注※ この記事での疑問の答えは出ていませんので読むのは時間の無駄です



russeng.hatenablog.jp


で、チェスボードの正方形のサイズを設定できる(square_sizeという変数がそれ)。


これに関して、tutorial*1では、

But if we know the square size, (say 30 mm), and we can pass the values as (0,0),(30,0),(60,0),..., we get the results in mm. (In this case, we don’t know square size since we didn’t take those images, so we pass in terms of square size).

とある。正方形サイズがわかっているとき、これをある単位で与えてやれば、その単位で結果を受け取ることができるよ、と書いてあるのだと思う。

実際に入力してみた。

  1. square_size = 1 のとき

f:id:russENG:20150616004511p:plain

  1. square_size = 23.0 のとき (実際に私がつかっているチェスボードの正方形サイズ(mm))

f:id:russENG:20150616234016p:plain


同じキャプチャじゃないよ!



とりあえず内部パラメータに違いは出ない(考えてみれば当たり前か)
外部パラメータや、奥行距離等に出てくるのかもれない。