5

javacv パッケージ (Opencv) を使用したコンポーネント識別に関するプロジェクトを開発しています。画像上の長方形のセットを「CvSeq」として返すメソッドを使用しました。私が知る必要があるのは、次のことを行う方法です

  • メソッドの出力 (CvSeq から) から各長方形を取得するにはどうすればよいですか?
  • 長方形の長さと幅にアクセスするには?

これは長方形を返すメソッドです

public static CvSeq findSquares( final IplImage src,  CvMemStorage storage)
{

CvSeq squares = new CvContour();
squares = cvCreateSeq(0, sizeof(CvContour.class), sizeof(CvSeq.class), storage);

IplImage pyr = null, timg = null, gray = null, tgray;
timg = cvCloneImage(src);

CvSize sz = cvSize(src.width() & -2, src.height() & -2);
tgray = cvCreateImage(sz, src.depth(), 1);
gray = cvCreateImage(sz, src.depth(), 1);
pyr = cvCreateImage(cvSize(sz.width()/2, sz.height()/2), src.depth(), src.nChannels());

// down-scale and upscale the image to filter out the noise
cvPyrDown(timg, pyr, CV_GAUSSIAN_5x5);
cvPyrUp(pyr, timg, CV_GAUSSIAN_5x5);
cvSaveImage("ha.jpg",   timg);
CvSeq contours = new CvContour();
// request closing of the application when the image window is closed
// show image on window
// find squares in every color plane of the image
for( int c = 0; c < 3; c++ )
{
    IplImage channels[] = {cvCreateImage(sz, 8, 1), cvCreateImage(sz, 8, 1), cvCreateImage(sz, 8, 1)};
    channels[c] = cvCreateImage(sz, 8, 1);
    if(src.nChannels() > 1){
        cvSplit(timg, channels[0], channels[1], channels[2], null);
    }else{
        tgray = cvCloneImage(timg);
    }
    tgray = channels[c]; // try several threshold levels
    for( int l = 0; l < N; l++ )
    {
    //             hack: use Canny instead of zero threshold level.
    //             Canny helps to catch squares with gradient shading
                   if( l == 0 )
                {
    //                apply Canny. Take the upper threshold from slider
    //                and set the lower to 0 (which forces edges merging)
                      cvCanny(tgray, gray, 0, thresh, 5);
   //                 dilate canny output to remove potential
   //                // holes between edge segments
                      cvDilate(gray, gray, null, 1);
                 }
                 else
                 {
    //                apply threshold if l!=0:
                      cvThreshold(tgray, gray, (l+1)*255/N, 255, CV_THRESH_BINARY);
                 }
    //            find contours and store them all as a list
                cvFindContours(gray, storage, contours, sizeof(CvContour.class), CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE);

                CvSeq approx;

  //            test each contour
                while (contours != null && !contours.isNull()) {
                       if (contours.elem_size() > 0) {
                            approx = cvApproxPoly(contours, Loader.sizeof(CvContour.class),storage, CV_POLY_APPROX_DP, cvContourPerimeter(contours)*0.02, 0);
                    if( approx.total() == 4
                            &&
                            Math.abs(cvContourArea(approx, CV_WHOLE_SEQ, 0)) > 1000 &&
                        cvCheckContourConvexity(approx) != 0
                        ){
                        double maxCosine = 0;
                        //
                        for( int j = 2; j < 5; j++ )
                        {
           //         find the maximum cosine of the angle between joint edges
                      double cosine = Math.abs(angle(new CvPoint(cvGetSeqElem(approx, j%4)), new CvPoint(cvGetSeqElem(approx, j-2)), new CvPoint(cvGetSeqElem(approx, j-1))));
                       maxCosine = Math.max(maxCosine, cosine);
                         }
                         if( maxCosine < 0.2 ){
                             cvSeqPush(squares, approx);
                         }
                    }
                }
                contours = contours.h_next();
            }
        contours = new CvContour();
    }
}
return squares;
}

これは私が使用したサンプルの元の画像です

ここに画像の説明を入力

そして、これは、一致する長方形の周りに線を引いた後に得た画像です

ここに画像の説明を入力

実際、上記の画像では、これらの大きな四角形を削除しようとしていますが、他の四角形を特定する必要があるだけなので、上記の目標をアーカイブする方法を理解するにはコード例が必要です。あなたの経験を私と共有するのに十分親切にしてください。ありがとう !

4

2 に答える 2

4

OpenCV は、黒い背景に白いオブジェクトの輪郭を見つけます。あなたの場合は逆で、オブジェクトは黒です。そういう意味では、画像の境界線もオブジェクトです。それを避けるには、背景が黒になるように画像を反転させます。

以下にそれを示しました(OpenCV-Pythonを使用):

import numpy as np
import cv2

im = cv2.imread('sofsqr.png')
img = cv2.cvtColor(im,cv2.COLOR_BGR2GRAY)

ret,thresh = cv2.threshold(img,127,255,1)

反転に別の関数を使用する代わりに、しきい値で使用したことを思い出してください。しきい値タイプを BINARY_INV (つまり「1」) に変換するだけです。

これで、次のような画像ができました。

ここに画像の説明を入力

これで輪郭が見つかりました。次に、各輪郭を概算し、概算された輪郭の長さ (長方形の場合は 4 である必要があります) を見て、それが長方形であるかどうかを確認します。

描画すると、次のようになります。

ここに画像の説明を入力

同時に、各輪郭の境界矩形も見つけます。境界 rect は次のような形状をしています: [始点 x、始点 y、四角形の幅、四角形の高さ]

したがって、幅と高さを取得します。

以下はコードです:

contours,hierarchy = cv2.findContours(thresh,cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE)

for cnt in contours:
    approx = cv2.approxPolyDP(cnt,cv2.arcLength(cnt,True)*0.02,True)
    if len(approx)==4:
        cv2.drawContours(im,[approx],0,(0,0,255),2)
        x,y,w,h = cv2.boundingRect(cnt)

編集 :

いくつかのコメントの後、質問の本当の目的は、大きな長方形を避け、小さな長方形のみを選択することであることがわかりました。

これは、取得したバウンディング rect 値を使用して実行できます。つまり、長さがしきい値、幅、または面積よりも小さい長方形のみを選択します。例として、この画像では、面積を 10000 未満にする必要があります (概算)。10000 未満の場合は選択する必要があり、赤色で示します。それ以外の場合は偽の候補であり、青色で表示します (視覚化のため)。

for cnt in contours:
    approx = cv2.approxPolyDP(cnt,cv2.arcLength(cnt,True)*0.02,True)
    if len(approx)==4:
        x,y,w,h = cv2.boundingRect(approx)
        if w*h < 10000:
            cv2.drawContours(im,[approx],0,(0,0,255),-1)
        else:
            cv2.drawContours(im,[approx],0,(255,0,0),-1)

以下は私が得た出力です:

ここに画像の説明を入力

そのしきい値を取得する方法は?:

それはあなたとあなたのアプリケーションに完全に依存します。または、試行錯誤の方法で見つけることができます。(私はそうしました)。

問題が解決することを願っています。すべての関数は標準の opencv 関数です。したがって、JavaCV に変換しても問題はないと思います。

于 2012-06-21T06:44:41.220 に答える
2

質問で提供されたコードにバグがあることに気付きました:

IplImage channels[] = {cvCreateImage(sz, 8, 1), cvCreateImage(sz, 8, 1), cvCreateImage(sz, 8, 1)};
channels[c] = cvCreateImage(sz, 8, 1);
if(src.nChannels() > 1){
    cvSplit(timg, channels[0], channels[1], channels[2], null);
}else{
    tgray = cvCloneImage(timg);
}
tgray = channels[c];

これは、チャネルが 1 つの場合、tgray が空のイメージになることを意味します。それは読むべきです:

IplImage channels[] = {cvCreateImage(sz, 8, 1), cvCreateImage(sz, 8, 1), cvCreateImage(sz, 8, 1)};
channels[c] = cvCreateImage(sz, 8, 1);
if(src.nChannels() > 1){
    cvSplit(timg, channels[0], channels[1], channels[2], null);
    tgray = channels[c];
}else{
    tgray = cvCloneImage(timg);
}
于 2012-10-18T07:29:36.110 に答える