2012年8月19日日曜日

夏休みの工作(4) 〜手と指の認識〜

夏休み全て費やしてどうにか手の認識が出来るようになったぜ・・・(;;;)p`)b

ということで、pythonとopencvで詰まったところなどの備忘録、まとめ

参考文献: 
考え方はこちら↓の(デッド)コピー
http://ilab.cs.ucsb.edu/projects/taehee/HandyAR/HandyAR.html

http://render.s73.xrea.com/pipe_render/2008/07/artoolkit-15.html

あとYahooGroupとStackOveflow

 □処理の流れ

1、RGBorBGRの元画像をHSV変換

2、ノイズを消すためメディアンフィルタをかける(メディアンが一番良かった)

3、 〜肌色の検出〜でやったInRangeS()で肌色orNOTの二値化

4、 距離変換(重要)
 →資料にはあるが、いらないと思ってやらなかったらどうにもならなかった。詳細は上記資料などを参照のこと。

  →出力画像のビット数、チャンネル数に注意(ビット深度32Fじゃないとだめ)、
   ついでに8ビットの空き箱をもうひとつ作っておく
  dist=cv.CreateImage(cv.GetSize(masked),cv.IPL_DEPTH_32F,1)
  frame=cv.CreateImage(cv.GetSize(masked),cv.IPL_DEPTH_8U,1)

  →GTKで出力したいので(筆者の都合)出力を8ビットに変換する。GTKは8ビットのRGBしか読んでくれない。下の関数を使う。
 cv.ConvertScaleAbs(dist,出力画像)

5、輪郭(contour)を取り出す。cv.FindContoursにぶち込むだけ。opencvは神

  contours = cv.FindContours(
                               (出力画像),
                               storage,
                               cv.CV_RETR_LIST,
                               cv.CV_CHAIN_APPROX_NONE,
                               (0,0))

これで輪郭の座標群のリストが得られる・・・・が

6、手の領域を判断して取り出す
 →ここでかなり詰まった(3日くらい)。 ただ輪郭を取り出すだけだと、下の画像の青っぽいところみたいないらない領域も検出されてしまう。ノイズみたいに出たり出なかったりして領域のIDなんかも決め様がない。
















たいてい得られた領域の中で一番面積でかいのがほしい領域なので(参考資料による)、全部の領域の面積を比較、一番でかい領域だけに処理を行うようにする。

→ 領域(contours)オブジェクトから情報を取り出す。Cの情報(ポインタがどうだの)しか無かったのでここでも若干詰まった。海外版ヤフー知恵袋的なところを参考に解決。
いまさっきStackoverflowでもっとスマートそうなやり方を発見した気がするが、疲れているのでスルー

 maxarea=0
    maxidx=0
    idx=0
 ↑予め作っておく
        while contours != None: ←領域(勝手に命名。日本語注意)がなくなるまで
   contour=contours[:] 
   ↑今選択している領域をリストで取り出す。中身は [(座標),(座標)・・・・・・・]
     [:]がないと領域群のポインタ的な何かを取り出すだけ。
         <nantoka object  0x1254614>←こんなの

            if maxarea                maxarea=cv.ContourArea(contours)
                maxidx=idx


   ↑cv.ContourAreaで領域の面積を取得、比較。今の面積よりでかければmaxidxを交換
  
  ループを抜ける条件を記述、Nodetectのif文はいらなそうだが、無いと多分バグる
   if contour ==[]:
                print "No detect"
                break

   ↓contours.h_next() で次の領域群に処理対象を移す。対象をうつす前にNoneだったらループ抜ける処理してるのは地味に大事
            if contours.h_next() == None:
                break
            contours=contours.h_next()
   idx +=1 ← 領域群IDを増加しループのはじめにもどる

→上記のループが終わった時点で最大面積の領域を持つ領域群のID(maxidx)と総ID数(idx)が手に入る。領域群は現在最後のIDのやつが選択されている。

↓ 現在選択中のIDから最初のIDまで戻す
 for i in range(idx):
            contours=contours.h_prev()
↓ maxidxまで進む。どう考えてもfor文2つもいらないよね。でもとりあえず放置。
 for i in range(maxidx):
            contours=contours.h_next()
↓選んだ領域群の中身を取り出せるようにしておく。         
 contour=contours[:]

気が向いたらcv.DrawContoursで輪郭線を描画。
↓のような感じになる。(青い丸は適当に出した領域座標の平均値、こういうのがあると領域がちゃんと選択されてるかわかりやすい。)


















7、指の検出とか形の検出とか
 →参考資料のみなさんや高知工科大学さんの公開されている卒論(岡田浩臣,星野 孝総,「HMD を用いた仮想ガジェットの開発」,2012))ではここで取得した座標間の角度を計算しまくってその角度から指を検出しているが、とりあえずもっと楽そうな(遅いけど)ふつうなやり方でやってみた。

→凸包と凹状欠損の利用(詳細はwebで)
 cv.ConvexHull2で凸包を計算、最後の0と1で出力形式が変わる(ポインタ(オブジェクト)か座標のリストか。さっきのcontoursみたいなもの) 。ここは0で。

          convexhull= cv.ConvexHull2 (contours,storage,0,0)

cv.ConvexityDefectsで凹状欠損を取得。中身は(凸開始座標,凸終わり座標,凹の底座標,忘れた)の4つのタプルがたくさん入ったリスト
defects=cv.ConvexityDefects(contours,convexhull,storage)


気が向いたらcv.Line()でそれぞれの座標間の線でも引く、 cv.Circle()で座標のところに丸でも書くとそれっぽくなる。↓そんな感じの画像(解決していない諸問題によりこのアングルで撮影しないとちゃんと指全部認識してくれない( ;д;))、あと速度(fps)がやばい。遅い。

















以上でおわり。

おれの画像認識はまだ始まったばかりだ! 〜yamada先生の次回作にご期待ください〜

 ・・・・打ち切りにせずに頑張りたい。せめてハンドジェスチャまでは。

おまけ 座標の角度計算関数。 math と numpy が必要

def kakudo(a,b,c):それぞれ(x,y)の座標
    v1=(a[0]-b[0],a[1]-b[1])
    v2=(c[0]-b[0],c[1]-b[1])
   
    dot=numpy.dot(v1,v2)
    v1=math.sqrt(numpy.dot(v1,v1))
    v2=math.sqrt(numpy.dot(v2,v2))
    coth=dot/(v1*v2)
    kakudo=numpy.arccos(coth)
    kakudo=kakudo*180/math.pi ←degreeで出力
    return kakudo



4 件のコメント:

  1. 画像認識か・・・すごい。
    先月くらいのトラ技で、Kinect特集やってたから、
    俺も手を出そうとしてたところだよ。

    返信削除
  2. おつかれ(´д`)ノ、奇遇だな。

    kinectいいよね。ツールも充実してそうだし。
    開発費(お小遣いともいう)が尽きなければおれも500円のウェブカメラじゃなくてkinect
    買ってたんだが・・(つд`)

    返信削除
  3. はじめまして、高知工科大学の岡田浩臣です

    指認識の事で検索していて、自分の名前があったのでびっくりしました!

    自分の論文が少しでも他の人の参考になっていて嬉しかったです。
    学生冥利につきます。

    ありがとうございました。

    返信削除
  4. はじめまして、Yamadaと申します。

    適当な引用の仕方でなんだか申し訳ないです。
    岡田さんの論文はとてもわかりやすく、大変興味深く拝読させて頂きました。

    これからも研究の方、頑張ってください。

    返信削除

フォロワー