opencv/javacv: How to iterate over contours for shape identification?

穿精又带淫゛_ 提交于 2019-12-03 17:42:02

问题


I'm developing a shape identification project using JavaCV and I have found some OpenCV code to identify U shapes in a particular image. I have tried to convert it into JavaCV but it doesn't give the same output. Can you please help me to convert this OpenCV code into JavaCV?

This is the OpenCV code:

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

This is the expected output

This is the converted code:

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

    }
}

This is the output that I got. Please can someone help me to solve this problem ?


回答1:


EDIT: Here is the most interesting finding - I think you are not iterating correctly through the contours - you should do something like:

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

Or:

// ... 
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
}

First, I think pst is right regarding the calculation of the ratio - you have to cast the width to float.

Secondly, I see that when you are making the gray image in python you use COLOR_BGR2GRAY and in java you are using CV_RGB2GRAY that could lead to a totally different gray picture. I would add some debug steps on both programs to save the temp gray images and compare them as also print outs for the values of x,y,w and h when (10 < (w/h) || (w/h) < 0.1) is true.

Another thing is that in the java solution you use CV_RETR_CCOMP to get the contours and in the python solution you use CV_RETR_LIST according to the documentation:

CV_RETR_LIST retrieves all of the contours without establishing any hierarchical relationships CV_RETR_CCOMP retrieves all of the contours and organizes them into a two-level hierarchy: on the top level are the external boundaries of the components, on the second level are the boundaries of the holes. If inside a hole of a connected component there is another contour, it will still be put on the top level

So first I would double check that all cv's parameters in both programs are the same, then I would add debug steps to see that the intermediate variables contains the same data.




回答2:


Check your type promotions, e.g.:

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

.. is highly suspect. To get a floating point division, one (or both) of the operands must at least be a float (and likewise a double for double division). Otherwise, as in this case, it is an integer division. (Note that the original code has promotion to float as well.)

For instance:

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

(Although I am unsure if this is the issue here.)

Happy coding!




回答3:


This code work for me and I just put cvSeq=cvSeq.h_next(); line in to the program and remove the for loop add while loop 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);
        }
    }


来源:https://stackoverflow.com/questions/11388683/opencv-javacv-how-to-iterate-over-contours-for-shape-identification

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!