【opencv 基礎知識 #5】透視投影変換行列に関連する関数まとめ (getPerspectiveTransform, calibrateCamera, warpPerspective, perspectiveTransform, undistort, remap)
忘れやすい透視投影変換行列周りの関数をメモ。(python)
具体的に前回の投稿でも使っている。
motojapan.hateblo.jp
1. 透視投影変換行列を求める方法
1.1. 台形補正の場合 [getPerspectiveTransform]
#pts_src 変換前座標 ※台形補正であれば4点 #pts_dst 変換先座標 ※台形補正であれば4点 M = cv2.getPerspectiveTransform(pts_src, pts_dst)
1.2. レンズ歪み補正の場合 [calibrateCamera]
# object_points チェスボードのパターンの点群 # image_points チェスボードのコーナーの点群 rms, camera_M, dist_coef, r, t = cv2.calibrateCamera(object_points,image_points,(img.shape[1],img.shape[0]),None,None)
2. 透視投影変換行列による変換方法
2.1. 画像を変換する [warpPerspective]
#img 変換前画像 #img_trans 変換後画像 img_trans = cv2.warpPerspective(img, M, (w, h))
2.2. 座標を変換する [perspectiveTransform]
#pts 変換前座標 #pts_trans 変換後座標 pts_trains = cv2.perspectiveTransform(pts, M)
2.3. 画像を変換する1[undistort](レンズ歪み補正)
img_undistort = cv2.undistort(img, camera_M, dist_coef)
2.4. 画像を変換する2[remap](レンズ歪み補正)
cam_M_ = cv2.getOptimalNewCameraMatrix(camera_M, dist_coef, (img.shape[1], img.shape[0]), 1)[0] mapx, mapy = cv2.initUndistortRectifyMap(camera_M, dist_coef, np.eye(3), cam_M_, (img.shape[1], img.shape[0]), cv2.cv.CV_32FC1) img_undistort = cv2.remap(img, mapx, mapy, cv2.cv.CV_INTER_AREA)
おまけ
レンズ歪み補正について
- 歪み種類
- 放射状歪み
- 接線歪み
- この2つはdist_coefで補正する
- 補正に必要なパラメータ
- 上記dist_coef
- カメラの内部パラメータ
- カメラ固有の焦点距離(fx, fy)や光学中心(cx, cy)
- camera_Mで補正する
- カメラの外部パラメータ
- ある座標系における3次元点の座標を別の座標系での座標に変換するための回転と並進のパラメータ
- 補正に必要な画像数
- チェスボードが写った画像10枚程度
具体的な camera_M、dist_coefの中身の例
In [1]: camera_M Out[1]: array([[ 452.30622078, 0. , 332.44877655], [ 0. , 453.36588503, 267.73560929], [ 0. , 0. , 1. ]]) In [2]: dist_coef Out[2]: array([-0.19224447, 0.07401813, 0.0044831 , 0.00214658, -0.02052423])
以上。
【opencv 基礎知識 #4】動画の手ぶれ補正をpython実装 (AKAZE, KNN, RANSAC)
行動認識が多かったので、半日くらいで動画の手ぶれ補正を作ってみた。
実装は数多あるので、そのうちコードをリファクタリングしたらGithubに載せようかと思う。
(すぐほしい人がいたら、コメントください)
すぐ忘れることをメモ。
結果
動画の通り、チューニングしなくても結構いい感じになっている。
上が補正前/下が補正後。
www.youtube.com
今年GWに山登りした時に撮った動画ですが、手ぶれを気にせず雫が落ちていくのが見れます。(最高です)
これもっと精度良くして無限ループすると、湯水のように時間が費やせそうです。
アルゴリズム
これを実装すれば基本的には動く。
- 動画を読み込み、フレームを読み出す
- 特徴量抽出する(AKAZE)
- キーポイントマッチングする(Brute-Force, KNN)
- 透視投影変換行列を求める(RANSAC)
- 画像を回転する
メモは以下。
1. 動画を読み込み、フレームを読み出す
cap = cv2.VideoCapture(mov_path) ret, frame1 = cap.read()
2. 特徴量抽出する(AKAZE)
今回は権利が緩やかなAKAZEを用いる。
- その他の選択肢
- SHIFT, ORB, FAST, BRISK, KAZE, etc...
detector = cv2.AKAZE_create() gray1 = cv2.cvtColor(frame1, cv2.COLOR_BGR2GRAY) keypoints1, descriptors1 = detector.detectAndCompute(gray1, None) #frame2, keypoints2, descriptors2も同様に生成
frame1をquery画像、frame2をtrain画像とする。
query、trainの説明は、次に記載。
keypointsはkeypoint型である。
詳細は下記が分かりやすい。
Common Interfaces of Feature Detectors — OpenCV 2.4.13.3 documentation
3. キーポイントマッチングする(Brute-Force, KNN)
ここでハマったのは、queryとtrainの概念。
ウェブの記事を見てもなかなか説明無かったが、一般的にこういうことらしい。
説明 | 例 | |
---|---|---|
query | 探したいターゲット画像 | 犬のクロップ画像 |
train | queryを探し出したいシーン画像 | 犬を散歩している風景画像 |
今回はqueryを基準フレーム(初期フレームなど)、trainは毎フレームとしてみた。
ここからBrute-Force(総当たり)で最近傍の特徴量を検索し、キーポイントマッチングする。
- その他の選択肢
- FLANN: Fast Library for Approximate Nearest Neighbors (高速近似近傍探索法)
#Brute-Force bf = cv2.BFMatcher() #matchesの要素数は、queryDescriptorsの要素数に一致 #上位k個の特徴点を返す matches = bf.knnMatch(queryDescriptors = descriptors1, trainDescriptors = descriptors2, k=2)
matchesはDMatch型オブジェクトのリスト。
DMatch型の詳細は下記が分かりやすい。
特徴点のマッチング — OpenCV-Python Tutorials 1 documentation
(箸休め)
どんなマッチングをしているかは、下記コードでvisualizeして確認できる。
#good_matches = matchesから選び出したもの frame3 = cv2.drawMatchesKnn(frame1, keypoints1, frame2, keypoints2, good_matches, None, flags=2)
たとえばこんなの(左画像がquery、右画像がtrain)
4. 透視投影変換行列を求める(RANSAC)
マッチングが分かったので次のどういう一致になるか具体的に透視投影変換行列 Mを求める。
Mが分かればそれでtrain画像を回したり、query画像を回せば、互いに一致する画像が得られる。
具体的には、cv2.findHomographyで行列を得られるが、外れ値をいい感じに無視して変数推定をするRANSACを用いる。
#src_points/dst_pointsは、good_matchesからindexでアクセスしたquery/trainのkeypoint座標である #(求めたいMの順にsrc_points/dst_pointsの入力順を決める) M, mask = cv2.findHomography(src_points, dst_points, cv2.RANSAC, 5.0)
5. 画像を回転する
上記Mは、query画像座標系→train画像座標系とする行列である。
手ぶれ補正をするなら、Mの逆行列が必要であるので、linalg.inv(M)で得ると楽。
frame2_trans = cv2.warpPerspective(frame2, np.linalg.inv(M), size)
#frame1_trans = cv2.warpPerspective(frame1, M, size)
これで、frame2_transは、「train画像座標系がquery画像座標系にいい感じでキーポイントに一致する形で透視投影された画像」となる。
あとは各フレームごりごりやればうまくいく。
めでたしめでたし。
【行動認識 #6】tensorflowでCAE(Convolutional Auto-Encoder)を実装してみた
これの続き
【行動認識 #5】データ量別でCNNの性能差 - MotoJapan's Tech-Memo
今回はCAEの実装をしてみた。
CAEってなに?
CAEとは、Convolutional Auto-Encoderの略で、次元削減/圧縮テクニックの1つである。
AE(Auto-Encoder)のConvolutional版ということで、Convolution層を使い、情報量を少なくして特徴量を抽出する半教師学習。
特にCAEが画像などの多次元データを扱うにはそのままテンソル変換せずに使えるので筋が良い気がするのでこれで今回はやってみる。
構造はencoderとdecoderで構成され、
- encoderの入力は画像等のデータ、出力は圧縮特徴量
- decoderの入力は圧縮特徴量、出力は、画像等のデータ
- decoderの出力とencoderへの入力が一致するように学習していく
活用方法は、次などがある。
- 圧縮した特徴量を抽出できる
- データが少ない場合の事は前学習として用い、encoder部分を再利用する
- モデルの局所最適への落ち込みを回避できるという点。(最近だとデータが多いなら不要という話)
概要イメージは下記。
詳細はいろいろなとこで記事になっているので他に譲ります。
実践
今回は前回に引き続き6行動のCAEを行う。
入力は、6ODF(加速度3軸、角速度3軸)の復号化。
今回のモデルは、Convolution + maxpooling 1 layer (=encoder) + unpooling + Deconvolution (=decoder) 1 layerで実装。
tensorflowだと、decoderは、tf.nn.conv2d_transposeが使える。
注意としては、ピクセル毎に回帰するので最後はsigmoidで0~1に収める必要がある。
結果
結果は例えばWALKを学習してみると、次の正解信号に対して、
学習epochを回す毎にどんどん近づいていく。
(見てておもしろい)
100[epoch]目以降はかなりオーバフィットさせ過ぎている感じもあるのでちょうどよいところ、
(WALKで言えば周期性が見えてくるところで)で打ち切る必要がありそうだ。
他の行動についてもすべてが並べてみるとなかなかの光景。
左上から右下までの順は次の通り。(横方向)
WALKING, WALKING_UPSTAIRS, WALKING_DOWNSTAIRS, SITTING, STANDING, LAYING
学習曲線はこんな感じで学習できている。
今後やりたいこと
- 学習データが少ない状況で、CAEで事前学習
- これで精度が上がるのであればいいなと。
- 生成モデルとしてVAEの実装
以上。
【行動認識 #5】データ量別でCNNの性能差
これの続き
【行動認識 #4】Subjectを考慮してCNN層数別で性能差を確認 - MotoJapan's Tech-Memo
学習データ量別で確認してみる。
概要
もともとの総データ数は、7344件。
7344件 ⇒ ( 7344 x 128 [samples] / 50 [Hz] ) + 0.5 [overwrap rate] ⇒ 2.6 [hour]
これが100%⇒50%⇒25%と変化したときの精度差を確認。
結果
最終精度は下記
condition | accuracy | loss |
---|---|---|
full (100%) | 0.9276 | 0.9687 |
half (50%) | 0.9008 | 0.9108 |
quarter (25%) | 0.8597 | 1.2324 |
tensorboardで学習進捗を書き出すとこんな感じ
- 学習データ量が多いほど精度向上がわかる
- 大体80 [epoch]目でどれも精度的な収束は落ち着きがわかる
- 比較的データが多いほうが初期精度も高い
- データ量が少ないとOverfitによる乖離傾向が強くなる
confusion matrixはどうだろう。
testデータの左からfull/half/quarterの順
- 特に姿勢系のSITTING/LAYINGの精度劣化が著しい(これは結構面白い)
SITTING/LAYINGは、0.87->0.77に落ち込み、誤検出の漏れ先はほとんどが姿勢系
まとめ
- 学習データ量が多いほど、汎化性能は高くなっている
- いろいろな行動パターンが学習できている
- 学習データ量が多いほど、収束タイミングは早くなっている
- epochを回すのもいいがデータが多ければepochをそこまで回さなくてもよいかも
- 姿勢系は遷移系に比べてデータが必要
- 繰り返し動作と単発動作だと、繰り返しの方が特徴量抽出しやすいのかも
以上。
【行動認識 #4】Subjectを考慮してCNN層数別で性能差を確認
これの続き
【行動認識 #3】機械学習/深層学習で人間行動認識 ~CNNしてみる~ - MotoJapan's Tech-Memo
前回はtrainデータでSubject未考慮で精度を検証した。(trainデータ内)
その精度は、93.43%
今回はtrain/testのそれぞれを使って評価
加えて、Convolution+MaxPoolingのペア層の数を変えて評価
前回との差
学習データ | Subject | CNN層数 | |
---|---|---|---|
前回 | trainデータを7:3で分割して評価 (5136件:2194件) | 14名 分割時に未考慮 | (Conv + Pooling) x 2 |
今回 | trainデータ(7344件) / testデータ(2944件) | 20名 予めSubject単位で分割(14名/6名) | (Conv + Pooling) x 1~3 |
【行動認識 #3】機械学習/深層学習で人間行動認識 ~CNNしてみる~
これの続き
【行動認識 #2】機械学習/深層学習で人間行動認識 ~データ確認~ - MotoJapan's Tech-Memo
やりたいことの1つでもあるtensorflowを使った。
結果
1. 精度
下記条件で検証して、精度としてはCNN(Convolutional Neural Network)で93.43%程度でた。
testセットの結果(93.25%時点)
条件
- データは、trainデータをtrainセット/testセットに7:3で分割 (Subject未考慮)
- 精度は、testセットに対して分類した結果
2. tensorflowを使って良かった点
- 実行速度が速い(いちいちpython層まで帰ってこない)
- tensorboardがめちゃ使える
- tensorflow mobileにも使える(まだやってないけど)
- コミュニティが大きく、文献が多い
3. 逆に辛かった点
- chainer上がりなので計算グラフを先に作るという概念なくて最初辛かった(慣れればOK)
- 動的に計算グラフを更新できない(とりあえず簡単にはできなそう)
- 学習中のネットワークのデバックができない(やり方がわからない)
Data
入力データは、前回の通り、下記の計6軸。
# 重力未分離加速度情報
total_acc_x_train、total_acc_y_train、total_acc_z_train
# 角速度情報
body_gyro_x_train、body_gyro_y_train、body_gyro_z_train
ラベルデータは、次の6行動。
WALKING(0), WALKING_UPSTAIRS(1), WALKING_DOWNSTAIRS(2), SITTING(3), STANDING(4), LAYING(5)
1データは、特に前処理せずに1軸あたり128 [samples]ずつ突っ込む。
Network
まず下記の感じで、[Convolution層 + pooling層] x 2で作って回す。
tensorboardでvisualize!
忘れそうだらtensorboardの実行方法を記載。
>tensorboard --logdir=[target_logdir]
ちなみに精度やロスを出すときに、trainとtestのディレクトリを分けておくと、
tensorboardが色分けしてくれる。
Accuracy/loss
精度とロスはこんな感じになりました。
一応、学習はちゃんと進んでいる。
200 epoch 回したが、189 epoch目でtestがbestに。
epoch=189 | train | test |
accuracy | 0.9998 | 0.9343 |
loss | 0.0004 | 0.5572 |
学習曲線は以下。(ありがとうtensorboard)
(※ただ、結局細かいところは自分でダンプした方がいい。)
Accuracy graph
Loss graph
analysis
実行速度(throughput)
環境は、Ubuntu 16.04 LTS / RAM 4G / Core-i7 CPU
このnetworkで 314.039 frame/secでした。
混合行列 (confusion matrix)
200 epoch目の結果で考察
bestの189 epochと比較してもそこまで大きく違わないので。
trainから分かること
- ちゃんとOverfitするだけの表現力がある
testから分かること
- WALK系[WALKING(0), WALKING_UPSTAIRS(1), WALKING_DOWNSTAIRS(2)]と、WALK系以外[SITTING(3), STANDING(4), LAYING(5)]は分離できている。(予想通り)
- ちなみに、WALK系は遷移系統、SITTING/STANDING/LAYINGは姿勢系統の動き。
- WALKING_DOWNSTAIRSの推定はまず間違えない。
- WALKINGは、UPSTAIRS, DOWNSTAIRSの一部であるから、間違える可能性がある。
- 姿勢系統は、ぼちぼちお互いに勘違いするケースがあるが、0.87は精度が出せている。
- 姿勢は、動きが少なく、特徴的な変化が少ないかもしれない。
同一Subjectが含まれる学習でも上記のような結果。
他の考察もそのうち。
【行動認識 #2】機械学習/深層学習で人間行動認識 ~データ確認~
この続き
【行動認識 #1】機械学習/深層学習で人間行動認識 ~事始め~ - MotoJapan's Tech-Memo
データセットは下記
UCI Machine Learning Repository: Human Activity Recognition Using Smartphones Data Set
ライセンスは次の通り。
This dataset is distributed AS-IS and no responsibility implied or explicit can be addressed to the authors or their institutions for its use or misuse. Any commercial use is prohibited.
「Human Activity Recognition Using Smartphones Data Set」『UCI Machine Learning Repository』データセット内 README.txt より。
URL: https://archive.ics.uci.edu/ml/datasets/human+activity+recognition+using+smartphones
データセットについて構造確認
train側だけだとこんなイメージ(test側もあり)
- body_acc_x_train # 重力分離済み加速度情報 (★1)
- body_acc_y_train
- body_acc_z_train
- body_gyro_x_train # 角速度情報 (★1)
- body_gyro_y_train
- body_gyro_z_train
- total_acc_x_train # 重力未分離加速度情報 (★1)
- total_acc_y_train
- total_acc_z_train
- X_train # 特徴量 (★1)
- y_train # 行動ラベル (★2)
分類問題を解くのであれば、★2が正解ラベルであり、★1を駆使して分類する。
今回はより生に近い値でやりたいので、入力は重力未分離加速度情報(ax,ay,az)と角速度情報(gx, gy, gz)のみを利用。
ではデータの中身を眺める。
データセットの中身確認
trainデータだけでも、7352件/21名分あるのでまずこれを味見。
同一被験者/行動違いのデータを確認(時間領域)
被験者1(Subject1)内でのランダムに取り出した各Actionのノルム違い
total_norm_acc_train
body_norm_gyro_train
異なる被験者/同一行動のデータを確認
シンプルそうなSTANDINGで確認。
total_norm_acc_train
body_norm_gyro_train
- total_acc_trainは、Subject3,5はオフセットしているように見える
- これは最初から平均値等でキャンセルした方が良さそう。
- body_gyro_trainは、かなり小さい値なので、その辺りで安定している
- Subject11が持つ0.14[rad/s]付近のデータでも8.02[dps]と考えるとかなり小さい。
同一被験者/異なる動きのデータを確認(周波数領域)
周波数領域についても見たい。
・total_norm_acc_train
SITTING/STANDING/WALKING
[SITTING/STANDING] [WALKING]という分離ができそう
・body_norm_gyro_train
SITTING/STANDING/WALKING
[SITTING] [[STANDING] [WALKING]]という分離ができそう
・SITTINGはかなり限られた低周波が反応
・WALKが高周波の部分が良く反応している
他の分析
同一被験者/異なる試行のデータを確認
SITTING (周期性なし)
WALKING (周期性あり)
- 傾向が見えるものもあるが、たまに特異な動きをするものもある(個人内ばらつき)
- 目視だが、行動によって周期性の無い動き、周期性のある動き、その再現性が確認できる
以上。
簡単なデータ分析だが、組み合わせ次第で分離可能性は確認できる。
次回は学習。