2013/02/19

OpenCV-javaで輪郭検出

メインのデスクトップが使えない時はMacBook AirのVirtualBox上にUbuntu12.04を入れて開発をしています。仮想PCバンザイ。

さて、年賀状のお年玉抽選チェックアプリの開発をサボっていましたが、
OpenCVのJavaバインディングも入れたところなのでJavaで続きをやってみます。

SVMの部分は後にして、数字を検出して1個ずつ切り出す部分を作りましょう。

切り出す処理は、
  • 輪郭を検出する
  • 輪郭の点列を囲む四角形を計算する
  • 四角形を切り出す
といった処理でできます。

OpenCVの関数を使ってサクッと実装してみましょう。

import java.util.ArrayList;
import java.util.List;
import org.opencv.core.*;
import org.opencv.highgui.Highgui;
import org.opencv.imgproc.Imgproc;

public class Main {

    /**
     * 輪郭を抽出して、輪郭を囲む四角形を描画します。
     */
    public static void main(String[] args)
    {
        System.loadLibrary("opencv_java");
        Mat src= Highgui.imread("num.png",0);
        Mat hierarchy=Mat.zeros(new Size(5,5), CvType.CV_8UC1);
        Mat invsrc=src.clone();
        Core.bitwise_not(src, invsrc);
        
        List<MatOfPoint> contours=new ArrayList<MatOfPoint>();
        
        //一番外側のみでOK
        Imgproc.findContours(invsrc, contours, hierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_TC89_L1);
        Mat dst=Mat.zeros(new Size(src.width(),src.height()),CvType.CV_8UC3);
        Scalar color=new Scalar(255,255,255);
        
        Imgproc.drawContours(dst, contours, -1, color,1);
        
        int i=0;
        for(i=0;i<contours.size();i++)
        {
            MatOfPoint ptmat= contours.get(i);

            //頂点描画
            /*
             int k=0;
             for(k=0;k<ptmat.height();k++)
            {
                double[] m=ptmat.get(k, 0);
                vertex.x=m[0];
                vertex.y=m[1];
                Core.circle(dst, vertex, 2, color,-1);
            }*/
            
            color=new Scalar(255,0,0);
            MatOfPoint2f ptmat2 = new MatOfPoint2f( ptmat.toArray() );
            RotatedRect bbox=Imgproc.minAreaRect(ptmat2);
            Rect box=bbox.boundingRect();
            Core.circle(dst, bbox.center, 5, color,-1);
            color=new Scalar(0,255,0);
            Core.rectangle(dst,box.tl(),box.br(),color,2);
            
        }
        Highgui.imwrite("test.png",dst);
    }
}

サンプル画像:
結果:

これで切り出す領域を指定できるようになりました。
ただし、輪郭グループの順番は左側から始まるわけではないので、領域の中心点のX座標を元にしてソートをかけてやる必要があります。

Cだと輪郭がCvSeq型でちょっと使いにくかったのですが、JavaだとMatOfPoint型になりました。
MatOfPoint型はN行1列の行列で、要素一つにdoubleの配列(x,yに相当)が格納されていました。CvSeqよりも操作しやすくなった気がします。

次は以前作った数字のSVMファイルを読み込んで、文字認識を行うところもJavaで作ってみましょう。

ではまた。

0 件のコメント:

コメントを投稿