【问题标题】:opencv/javacv: How to iterate over contours for shape identification?opencv/javacv:如何迭代轮廓以进行形状识别?
【发布时间】:2012-07-18 18:35:21
【问题描述】:

我正在使用 JavaCV 开发一个形状识别项目,并且我找到了一些 OpenCV 代码来识别特定图像中的 U 形状。我试图将它转换为 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()

这是预期的输出

这是转换后的代码:

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

    }
}

这是我得到的输出。请问有人可以帮我解决这个问题吗?

【问题讨论】:

  • 我没有看到任何 C++,所以我删除了标签。我假设第一个例子是 Python。
  • 我对这个问题有一个小问题。请有人解释一下执行“cvFindContours()”方法后“cvSeq.total()”方法的值吗?

标签: java image-processing opencv javacv


【解决方案1】:

编辑:这是最有趣的发现 - 我认为您没有正确地遍历轮廓-您应该执行以下操作:

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,这可能会导致完全不同的灰度图像。当(10 &lt; (w/h) || (w/h) &lt; 0.1) 为真时,我将在这两个程序上添加一些调试步骤以保存临时灰度图像并将它们与x,y,wh 的值进行比较。

另一件事是,根据文档,在 java 解决方案中使用 CV_RETR_CCOMP 获取轮廓,在 python 解决方案中使用 CV_RETR_LIST

CV_RETR_LIST 检索所有轮廓而不建立任何轮廓 层次关系 CV_RETR_CCOMP 检索所有轮廓 并将它们组织成两级层次结构:在顶层是 组件的外部边界,在第二层是 孔的边界。如果在连接组件的孔内 还有一个轮廓,它仍然会放在顶层

所以首先我会仔细检查两个程序中所有 cv 的参数是否相同,然后我会添加调试步骤以查看中间变量是否包含相同的数据。

【讨论】:

  • 在 javacv cvBoundingRect() 中,我们不能将指针作为参数传递,因为它需要 CvArr,所以我认为这不是问题。
  • @GumSlashy - 查看我的编辑。根据 javacv (code.google.com/p/javacv/source/browse/README.txt) 的自述文件,这是迭代轮廓的正确方法。我还在 SO stackoverflow.com/questions/9648482/ocr-with-javacv 中发现了这个问题,它使用相同的方法来获取轮廓并绘制它们的边界框。
  • 我试着把 cvBoundingRect(cvGetSeqElem(cvSeq, i),0);在问题中,但它对我不起作用,我已经给出了正确结果的答案。无论如何,非常感谢您的回复。
  • @GumSlashy - 我建议你尝试使用h_next() 进行迭代(并勾勒出使用它的方法),我还在 cmets 中发布了一个链接,其中显示了如何使用它的完整示例.
【解决方案2】:

检查您的类型促销,例如:

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 ) { 

(虽然我不确定这是否是 这里的问题。)

编码愉快!

【讨论】:

  • 感谢您的快速回复,我已经尝试过您的建议,但结果相同。
  • 我认为通过您的更改更新您的代码会有所帮助。也许那里还有一个整数除法?
  • 我已经给出了答案,并且我也尝试将 w/h 转换为浮点值,但它没有产生显着的效果。但它确实在其他一些情况下可能会产生重大影响。非常感谢您的回复。
【解决方案3】:

这段代码对我有用,我只需将 cvSeq=cvSeq.h_next(); 行放入程序并删除 for 循环添加 while 循环 for thet。

    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);
        }
    }

【讨论】:

  • 这和我几天前建议的方法一模一样——可惜你没有尝试:// ... 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 }
猜你喜欢
  • 2012-07-10
  • 2019-03-15
  • 2012-07-12
  • 1970-01-01
  • 2021-08-21
  • 2011-09-13
  • 2012-07-15
  • 2014-04-05
  • 1970-01-01
相关资源
最近更新 更多