2012-07-09 103 views
9

我正在開發一個使用JavaCV的形狀識別項目,我發現了一些OpenCV代碼來識別特定圖像中的U形狀。我試圖將它轉換成JavaCV,但它不提供相同的輸出。你能幫我把這個OpenCV代碼轉換成JavaCV嗎?opencv/javacv:如何迭代輪廓以進行形狀識別?

這是OpenCV的代碼:

import cv2 
import numpy as np 

img = cv2.imread('sofud.jpg') 
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) 
ret,thresh = cv2.threshold(gray,127,255,1) 
contours,hierarchy = cv2.findContours(thresh,cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE) 

for cnt in contours: 
    x,y,w,h = cv2.boundingRect(cnt) 
    if 10 < w/float(h) or w/float(h) < 0.1: 
     cv2.rectangle(img,(x,y),(x+w,y+h),(0,0,255),2) 

cv2.imshow('res',img) 
cv2.waitKey(0) 
cv2.destroyAllWindows() 

這是預期的輸出

enter image description here

這是轉換後的代碼:

import com.googlecode.javacpp.Loader; 
import com.googlecode.javacv.CanvasFrame; 
import static com.googlecode.javacpp.Loader.*; 
import static com.googlecode.javacv.cpp.opencv_core.*; 
import static com.googlecode.javacv.cpp.opencv_imgproc.*; 
import static com.googlecode.javacv.cpp.opencv_highgui.*; 
import java.io.File; 
import javax.swing.JFileChooser; 

public class TestBeam { 
    public static void main(String[] args) { 
     CvMemStorage storage=CvMemStorage.create(); 
     CvSeq squares = new CvContour(); 
     squares = cvCreateSeq(0, sizeof(CvContour.class), sizeof(CvSeq.class), storage); 
     JFileChooser f=new JFileChooser(); 
     int result=f.showOpenDialog(f);//show dialog box to choose files 
      File myfile=null; 
      String path=""; 
     if(result==0){ 
      myfile=f.getSelectedFile();//selected file taken to myfile 
      path=myfile.getAbsolutePath();//get the path of the file 
     } 
     IplImage src = cvLoadImage(path);//hear path is actual path to image 
     IplImage grayImage = IplImage.create(src.width(), src.height(), IPL_DEPTH_8U, 1); 
     cvCvtColor(src, grayImage, CV_RGB2GRAY); 
     cvThreshold(grayImage, grayImage, 127, 255, CV_THRESH_BINARY); 
     CvSeq cvSeq=new CvSeq(); 
     CvMemStorage memory=CvMemStorage.create(); 
     cvFindContours(grayImage, memory, cvSeq, Loader.sizeof(CvContour.class), CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE); 
     System.out.println(cvSeq.total()); 
     for (int i = 0; i < cvSeq.total(); i++) { 
      CvRect rect=cvBoundingRect(cvSeq, i); 
      int x=rect.x(),y=rect.y(),h=rect.height(),w=rect.width(); 
      if (10 < (w/h) || (w/h) < 0.1){ 
       cvRectangle(src, cvPoint(x, y), cvPoint(x+w, y+h), CvScalar.RED, 1, CV_AA, 0); 
       //cvSeqPush(squares, rect); 
      } 
     } 
     CanvasFrame cnvs=new CanvasFrame("Beam"); 
     cnvs.setDefaultCloseOperation(javax.swing.JFrame.EXIT_ON_CLOSE); 
     cnvs.showImage(src); 
     //cvShowImage("Final ", src); 

    } 
} 

這是輸出,我得到。請有人可以幫我解決這個問題嗎?

enter image description here

+0

我沒有看到任何C++,所以我刪除了標籤。我假設第一個例子是Python。 – 2012-07-09 03:13:59

+1

我對這個問題有一個小問題。請在執行「cvFindContours()」方法後解釋一下「cvSeq.total()」方法的值嗎? – 2012-07-09 05:17:23

回答

5

編輯:這是最有趣的發現 - 我想你不通過輪廓正確地迭代 - 你應該這樣做:

CvRect rect = cvBoundingRect(cvGetSeqElem(cvSeq, i),0); //python default? 

或者:

// ... 
CvSeq contours = new CvSeq(); 
CvSeq ptr = new CvSeq(); 
CvRect rect = null; 
// ... 
cvFindContours(..., contours, ...); 

for (ptr = contours; ptr != null; ptr = ptr.h_next()) { 
    rect = cvBoundingRect(ptr, 0); 
    // ... Draw the box if meets criteria 
} 

首先,我認爲pst是正確的關於比率的計算 - 你必須把寬度浮動。

其次,我看到,當你在python中製作灰色圖像時,使用COLOR_BGR2GRAY,而在java中,你使用的是CV_RGB2GRAY,這可能會導致完全不同的灰色圖片。我會在兩個程序中添加一些調試步驟以保存臨時灰度圖像,並將它們作爲x,y,wh的值進行比較,當(10 < (w/h) || (w/h) < 0.1)爲真時。

另一件事是,在Java解決方案,您使用CV_RETR_CCOMP獲得的輪廓和在Python解決您根據文檔中使用CV_RETR_LIST

CV_RETR_LIST檢索所有的輪廓而沒有建立任何 層次關係CV_RETR_CCOMP檢索所有輪廓 並將它們組織爲兩級層次結構:頂層爲 組件的外部邊界,第二層爲 孔的邊界。如果連接的組件 的孔裏面還有另外一個輪廓,它仍然會在頂層放

所以首先我會仔細檢查所有的品種在這兩個方案的參數是相同的,那麼我想補充調試步驟以查看中間變量包含相同的數據。

+0

在javacv cvBoundingRect()中我們不能傳遞指針作爲參數,因爲它需要CvArr,所以我認爲它不能成爲問題。 – 2012-07-13 03:23:03

+0

@GumSlashy - 看我的編輯。根據javacv的README(http://code.google.com/p/javacv/source/browse/README.txt),這是迭代輪廓的正確方法。我還在SO http://stackoverflow.com/questions/9648482/ocr-with-javacv中發現了這個問題,它使用相同的方法獲取輪廓,並繪製邊界框。 – zenpoy 2012-07-13 09:31:06

+0

我嘗試把cvBoundingRect(cvGetSeqElem(cvSeq,i),0);在這個問題上,但它並不適用於我,我已經把答案放在了正確的結果上。非常感謝你的答覆。 – 2012-07-17 12:51:33

3

檢查類型促銷活動,例如:

if (10 < (w/h) || (w/h) < 0.1){ 

..是非常值得懷疑。要獲得浮點數除法,一個(或兩個)操作數必須至少爲float(同樣用於雙重除法的double)。否則,在這種情況下,它是一個整數除法。 (請注意,代碼具有推廣float爲好。)

例如:

float ratio = (float)w/h; // (float/int) => (float/float) -> float 
if (10 < ratio || ratio < 0.1) { 

(雖然我不能確定這是否是問題在這裏。)

快樂編碼!

+1

感謝您的快速回復,我已經嘗試了你的sugetion,但它給出了同樣的結果。 – 2012-07-09 03:36:19

+0

我認爲這將有助於更新您的代碼。那裏可能還有一個整數部分? – Noremac 2012-07-12 20:50:16

+1

我已經提出了答案,並且我也嘗試將w/h投入浮動值,但它沒有產生顯着的效果。但其確實在一些其他情況下可能會產生重大影響。非常感謝您的回覆。 – 2012-07-17 12:47:46

2

此代碼適用於我,我只是把cvSeq = cvSeq.h_next();輸入到程序中,並刪除for循環的for循環add。

package Beam; 
    import com.googlecode.javacpp.Loader; 
    import com.googlecode.javacv.CanvasFrame; 
    import static com.googlecode.javacpp.Loader.*; 
    import static com.googlecode.javacv.cpp.opencv_core.*; 
    import static com.googlecode.javacv.cpp.opencv_imgproc.*; 
    import static com.googlecode.javacv.cpp.opencv_highgui.*; 
    import java.io.File; 
    import javax.swing.JFileChooser; 

    public class TestBeam2 { 
     public static void main(String[] args) { 
      JFileChooser f=new JFileChooser(); 
      int result=f.showOpenDialog(f);//show dialog box to choose files 
       File myfile=null; 
       String path=""; 
      if(result==0){ 
       myfile=f.getSelectedFile();//selected file taken to myfile 
       path=myfile.getAbsolutePath();//get the path of the file 
      } 
      IplImage src = cvLoadImage(path);//hear path is actual path to image 
      IplImage grayImage = IplImage.create(src.width(), src.height(), IPL_DEPTH_8U, 1); 
      cvCvtColor(src, grayImage, CV_RGB2GRAY); 
      cvThreshold(grayImage, grayImage, 127, 255, CV_THRESH_BINARY); 
      CvSeq cvSeq=new CvSeq(); 
      CvMemStorage memory=CvMemStorage.create(); 
      cvFindContours(grayImage, memory, cvSeq, Loader.sizeof(CvContour.class), CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE); 

      while (cvSeq != null && !cvSeq.isNull()) { 
       CvRect rect=cvBoundingRect(cvSeq, 0); 
       int x=rect.x(),y=rect.y(),h=rect.height(),w=rect.width(); 
       if (10 < w/h || w/h < 0.1){ 
        cvRectangle(src, cvPoint(x, y), cvPoint(x+w, y+h), CvScalar.RED, 1, CV_AA, 0); 
       } 
       cvSeq=cvSeq.h_next(); 
      } 
      CanvasFrame cnvs=new CanvasFrame("Beam"); 
      cnvs.setDefaultCloseOperation(javax.swing.JFrame.EXIT_ON_CLOSE); 
      cnvs.showImage(src); 
      //cvShowImage("Final ", src); 
     } 
    } 
+2

這與我前幾天提出的方法完全相同 - 這太糟糕了,您沒有嘗試過:'// ... CvSeq輪廓= new CvSeq(); CvSeq ptr = new CvSeq(); CvRect rect = null; // ... cvFindContours(...,contour,...); (ptr =等高線; ptr!= null; ptr = ptr.h_next()){ rect = cvBoundingRect(ptr,0); // ...如果符合條件,請畫框 } – zenpoy 2012-07-17 13:26:14