2012-06-21 41 views
5

我正在使用javacv包(Opencv)開發組件識別項目。我使用的圖像作爲「CvSeq」上返回設置矩形的方法,我需要知道的是如何做以下的事情如何在javacv中提取輪廓的寬度和高度?

  • 我怎樣才能從方法輸出每個矩形(從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; 
} 

這是我用

enter image description here

樣品原始圖像的方法而這是我畫線後得到了圖像在匹配的矩形周圍

enter image description here

其實在上面的圖片中,我正在綁定刪除那些大矩形,只需要識別其他矩形,所以我需要一些代碼示例來了解如何存檔以上目標。請善待與我分享你的經驗。謝謝 !

回答

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')。

現在你有一個圖像,如下:

enter image description here

現在我們找到了輪廓。然後,對於每個輪廓,我們通過查看近似輪廓的長度來近似它並檢查它是否爲矩形,矩形應該是四個。

如果抽,你會得到這樣的:

enter image description here

,並在同一時間,我們也發現每個輪廓的邊界矩形。邊界矩形的形狀如下:[初始點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) 

編輯:

一些意見之後,我明白了,這個問題的真正目的是爲了避免大的矩形,並僅選擇較小的。

它可以使用我們獲得的邊界矩形值來完成。即只選擇那些長度小於閾值或寬度或面積的矩形。作爲一個例子,在這個圖像中,我拿的面積應該小於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) 

下面是輸出我得到:

enter image description here

如何獲得該閾值? :

它完全取決於你和你的應用程序。或者你可以通過試驗和錯誤的方法找到它。 (我這樣做了)。

希望能夠解決您的問題。所有功能都是標準的opencv功能。所以我認爲你不會找到任何問題轉換爲JavaCV。

+0

其實我需要刪除兩個大的矩形在圖像和其他小矩形應該被識別。請你可以解釋如何使用javacv來做到這一點。 –

+0

我很抱歉,我沒有得到它。也許你可以使用顏料繪製出你需要的綠色方塊,然後在某處上傳並在此處給出鏈接。 –

+2

K,我明白了..你只需要小矩形,避免大矩形,對吧?如果它們的長度或寬度大於某個閾值,則可以避免它們。 –

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]; 

這意味着,如果有一個單信道,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); 
}