#本記事はopenCVで手を認識する方法を調べた、まとめです。
Leap MotionやKinectが使えるのであれば、そちらを使った方が簡単です。
はじめに
OpenCVのhaar cascadeを用いた画像認識では、学習済みのxmlファイルを使い、顔などの位置を知ることが出来ます。
デフォルトでは、顔や目、上半身などを認識できますが、手の学習済みファイルはありません。そこで、手の認識について調べました。
Convex Hull
OpenCVの参考書では、グーチョキパーの認識のために、ConvexHullを用いた方法が紹介されています。ConvexHullについては、wikiやyoutubeに紹介があります。
https://en.wikipedia.org/wiki/Convex_hull
https://www.youtube.com/watch?v=YNyULRrydVI
原理はわかりますが、実装には、難しそうです。
また、processingのOpenCVライブラリではConvexHullは使えなさそうでした。
processingのサンプルコードも見つけましたが、シンプルではなかったので、紹介するのはやめます。
学習済みデータを探す
OpenCVでは、学習データはxmlファイルとして保存されています。
例:haarcascade_frontalface_default.xml
中身は1MBくらいのテキストファイルです。
このxmlファイルで、手があったらと思って探した結果が、以下です。
https://github.com/Aravindlivewire/Opencv/blob/master/haarcascade/aGest.xml
https://github.com/Aravindlivewire/Opencv/blob/master/haarcascade/closed_frontal_palm.xml
https://github.com/Aravindlivewire/Opencv/blob/master/haarcascade/fist.xml
https://github.com/Aravindlivewire/Opencv/blob/master/haarcascade/palm.xml
https://github.com/Balaje/OpenCV/blob/master/haarcascades/hand.xml
RAWボタンを押した後、全選択してコピーして、テキストエディタでxmlファイルを作れば、とりあえず使えます。
どのファイルが、何を学習させたかよくわかりませんので、試してみます。
実行する
ダウンロードしたxmlファイルを、パソコンの以下の場所にコピーします。(windowsの場合)
C:/Users/??????/Documents/Processing/src/libraries/opencv_processing/library/cascade-files/??????.xml
プログラムの中で、ファイル名を指定します。
うまくxmlを読み込めた場合は、
Cascade loaded: hand.xml
のようにコンソールに表示されます。
うまく読み込めない場合は、
Cascade failed to load
と表示されます。
ファイル名 | 結果 |
---|---|
aGest.xml | 右手のグーを認識します |
closed_frontal_palm.xml | 右手のグーを認識します |
fist.xml | 右手のグーを認識します |
palm.xml | パーを認識します(微妙) |
hand.xml | グーを認識します |
グーの認識ではチョキの握っている部分でも認識します。
左手のグーの認識が弱いのは、学習させてないからだと思います。
グーの傾きの許容角度は90度ぐらいですが、パーは30度ぐらいです。
まとめ
これが一番まともでした。
https://github.com/Balaje/OpenCV/blob/master/haarcascades/hand.xml
グーの認識率は使えるレベルですが、パーの認識には実用上、無理があります。
チョキは学習データが見当たりませんでした。
グー以外は、肌色識別ラベリング等を組み合わせる必要がありそうです。
手の認識
import gab.opencv.*;
import processing.video.*;
import java.awt.*;
Capture video;
OpenCV opencv;
void setup() {
size(640, 480);
video = new Capture(this, width, height);
opencv = new OpenCV(this, width, height);
opencv.loadCascade("hand.xml");
video.start();
}
void draw() {
opencv.loadImage(video);
image(video, 0, 0);
noFill();
stroke(0, 255, 0);
strokeWeight(3);
Rectangle[] hand = opencv.detect();
for (int i = 0; i < hand.length; i++) {
rect(hand[i].x, hand[i].y, hand[i].width, hand[i].height);
}
}
void captureEvent(Capture c) {
c.read();
}