holmesのメモ帳

始めなければ始まらない。頭の中で考えているだけなのは終わりにして手を動かして色々と作っていこう。

OpenCVを使って顔を抜き出したりするやつやってみた。VBで。

画像を指定すると顔の部分だけ抜き出すという処理

やったこともないのにお客さんにできると言ってしまったので作ってみた。

VisualBasic2010 + OpenCV + OpenCvSharp(ラッパー)

f:id:holmes:20140320071734p:plain



環境などはこの辺を参考にさせて頂きました。

【PC】OpenCVをVisualStudio2012から使う(OpenCVSharp)

OpenCV(OpenCVSharp)をVB.NETで使う


OpenCvSharpを使った処理の辺りはこの辺

OpenCVを使った顔認識自作ソフトの部屋 - 開発日記 | 自作ソフトの部屋 - 開発日記




そのままだと顔認識されてくる顔の範囲はこんな感じになってしまう

f:id:holmes:20140320073604p:plain

Dim g As Graphics = Graphics.FromImage(image)

For i As Integer = 0 To faces.Total - 1


    '顔(輪郭)の位置を取得
    Dim r As CvRect = faces(i).Value.Rect

    '赤で描く
    Dim p As New Pen(Color.Red, 3)

    '顔(輪郭)を赤で囲う
    g.DrawRectangle(p, r.X, r.Y, r.Width, r.Height)

Next


なので拾ってきた範囲を広げてあげます。

この範囲が元の画像の範囲を超えてしまうとエラーになってしまうようなので
そうなった場合は画像の範囲内に収まるようにしてみました。

具体的にはこんな感じ(修正しました)

Dim g As Graphics = Graphics.FromImage(image)

'---------------------------------------------------
'線の色
'---------------------------------------------------
Dim p1 As New Pen(Color.Red, 3)
Dim p2 As New Pen(Color.Yellow, 3)

For i As Integer = 0 To faces.Total - 1

    '---------------------------------------------------
    '顔の位置を取得
    '---------------------------------------------------
    Dim r As CvRect = faces(i).Value.Rect

    '---------------------------------------------------
    '顔の位置の左上の座標と巾と高さ
    '---------------------------------------------------
    '左上の座標(左)
    Dim l1 As Integer = CInt(IIf(r.X < 0, 0, r.X))
    '左上の座標(上)
    Dim t1 As Integer = CInt(IIf(r.Y < 0, 0, r.Y))
    '範囲(巾)
    Dim w1 As Integer = CInt(IIf(r.Width > image.Width, image.Width - l1, r.Width))
    '範囲(高さ)
    Dim h1 As Integer = CInt(IIf(r.Height > image.Height, image.Height - t1, r.Height))

    '---------------------------------------------------
    '拡大した範囲の計算
    '---------------------------------------------------
    '左上の座標は拾ってきた輪郭の巾の半分だけ左から始める
    Dim l2 As Integer = CInt(IIf(r.X - (r.Width / 2) < 0, 0, r.X - (r.Width / 2)))
    '左上の座標は拾ってきた輪郭の高さの半分だけ上から始める
    Dim t2 As Integer = CInt(IIf(r.Y - (r.Height / 2) < 0, 0, r.Y - (r.Height / 2)))
    '範囲(巾)を倍に広げる
    Dim w2 As Integer = CInt(IIf((l2 + (r.Width * 2)) > image.Width, image.Width - l2, (r.Width * 2)))
    '範囲(高さ)を倍に広げる
    Dim h2 As Integer = CInt(IIf((t2 + (r.Height * 2)) > image.Height, image.Height - t2, (r.Height * 2)))


    '---------------------------------------------------
    '画像内の顔を囲う
    '---------------------------------------------------
    '顔(輪郭)を赤で囲う
    g.DrawRectangle(p1, l1, t1, w1, h1)

    '広げた範囲で顔を黄色で囲う
    g.DrawRectangle(p2, l2, t2, w2, h2)

Next


するとこんな感じになる。

f:id:holmes:20140320075511p:plain

ここまでできれば座標も解っているので
後は好きな様に画像を切り取っていけば良い。


ただこれだけではまだ問題があって
範囲を広げて巾と高さを倍に広げた結果画像をはみ出してしまう範囲になった場合に
エラーとなって「メモリ不足」と言われてしまう。
(未解決。気が向いたら直します..)

範囲を広げた左上の座標のとこをIIfを使ってマイナス値だったら0にしているのも同じ理由。



最初Webサービスを利用しようとしたけど顧客管理システムとかで使う場合には
外部に写真を出すのをお客さんが嫌がりそうなのでこの方法で行こうと思う。