OpenCVSharpによるGrabCut

アバター
asd
記事: 319
登録日時: 14年前

OpenCVSharpによるGrabCut

投稿記事 by asd » 8年前

以下の質問トピで質問があったので返答してみたところかみ合ってない感が…。
http://dixq.net/forum/viewtopic.php?f=3&t=19476

質問者さんのリンクを見るとマウスで塗りぬりした範囲をざっくりマスキングして前景と背景に分けてくれる機能っぽい。
ちょっと面白そうかもと思ってOpenCV2.4.10とOpenCVSharp2.4.10(C#用ラッパ)を使ってC#でやってみました。
基本はOpenCVと同じように使えるものの、ラッパ独特の癖があってハマることが多かったです…。
質問者さんのリンクで紹介されてた画像に合わせて、鏡音リン、鏡音レンペアの画像も探してみたり(ぉ)

参考ページではGrabCut関数に与える引数のbgdModel、fgdModelは空のMatを渡しているものの、
OpenCVSharpでは空のCv2.Matクラスを渡すとぬるぽガッされ、適当なサイズのCv2.Matクラスのインスタンスを作るとサイズが違うとエラーが出てきてしまう始末。
OpenCVのソースを見てみると1行65列(modelSize*ComponentCount)だとわかったのでそれに設定。
他にもMatクラスの行と列の指定を逆にして入力画像とマスク画像のサイズが不一致のエラーになってたりと結構苦労してました。

参考にさせていただいたページ
http://www.cg.info.hiroshima-cu.ac.jp/~ ... ech89.html

利用させていただいた画像
https://commons.nicovideo.jp/material/nc29966

で、作ったのはこんな感じの画像が表示されるだけのシンプルなGUI。
grabcut1.png
左:元画像と前景・背景指定用フォーム
右:マスク適用結果画像
左のForm1に元画像が読み込まれ、この画像の上をマウスでドラッグして前景と背景をフリーハンドで指定する形。
左ボタンは前景で緑のラインが、右ボタンは背景で赤のラインがそれぞれ引かれます。

ドラッグをやめるとそれまでにドラッグされた軌跡を基にマスク画像を生成してGrabCut関数でマスク画像を更新、
更新されたマスク画像の前景のみを表示したのが右に表示されているtestウィンドウです。

で、実際に前景部分をドラッグするとこんな感じで緑色のラインが引かれます。
(線が細くて見ずらいですが…)
PictureBoxは2つ重ねていて、元画像表示用の下層部分とマウスドラッグによるライン描画部分の上層に分かれてます。
その方がマウスドラッグの軌跡だけを取り出しやすいかなと思いまして。
grabcut2.png
前景部分を指定中
処理部分はこんな感じ。

CODE:

            //軌跡からマスクを取得
            inputMask = getStrokeMat(inputMask, new Bitmap(pbStroke.Image), inStrokeColor, inKind);
            //マスク情報からLazySnapping実施
            bgdModel = Mat.Zeros(1, 65, MatType.CV_64FC1);
            fgdModel = Mat.Zeros(1, 65, MatType.CV_64FC1);
            rect = new Rect(0, 0, inputMask.Rows, inputMask.Cols);
            Cv2.GrabCut(inputImg, inputMask, rect, bgdModel, fgdModel, iterCount, GrabCutFlag.InitWithMask);

            //マスク適用画像の生成
            Mat _tmp = inputImg.Clone();
            for (int row = 0; row (row, col) == Cv2.GC_BGD || inputMask.Get(row, col) == Cv2.GC_PR_BGD)
                    {
                        _tmp.Set(row, col, new Vec3b(0, 0, 0));
                    }
                    else
                    {
                        _tmp.Set(row, col, inputImg.Get(row, col));
                    }
                }
            }
            Cv2.ImShow("test", _tmp);
pbStroke.Image(マウス軌跡を描画したPictureBoxのイメージ)からgetStrokeMatメソッドでMatクラスのマスクイメージinputMaskを作成、
それを元画像のInputImgと一緒にGrabCutメソッドに渡してマスク画像を更新します。

処理後のマスク適用結果がこれ。背景部分はマスクをかけて真っ黒になってます。
grabcut3.png
GrabCutメソッドでのマスクを適用した結果
ぉ、結構きれいにできてるかも(*´ヮ`)
マウスボタンを離すのと同時にGrabCut処理が動いちゃうので、複数個所の指定は何度かに分けてマウスドラッグが必要になります。
(好きな画像を指定読み込みできる機能と合わせて、別途処理ボタン等の押下までGrabCut処理を行わないように要改良ポイントです)

元の質問トピックでは質問者さん返ってこないっぽいけど、やりたいことは多分こういうことだよね。
あえてC#でやってしまったんで元のトピックには書かずこちらに(質問者さんはC++と念押ししていたので)

他にも処理速度とか使い勝手とか改良点は一杯あるけど、眠いのでとりあえずこの辺で…(´-`)zZ乙
最後に編集したユーザー asd on 2017年8月14日(月) 04:05 [ 編集 2 回目 ]

コメントはまだありません。