9

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

enter image description here

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 ?

enter image description here

Jeru Luke
  • 20,118
  • 13
  • 80
  • 87
Gum Slashy
  • 385
  • 5
  • 20
  • I don't see any C++, so I removed the tag. I'm assuming the first example is Python. – Jesse Good Jul 09 '12 at 03:13
  • 1
    I have small question regarding this question. Please can some one explain the value of "cvSeq.total()" method after execute "cvFindContours()" method ? –  Jul 09 '12 at 05:17

3 Answers3

5

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.

zenpoy
  • 19,490
  • 9
  • 60
  • 87
  • In javacv cvBoundingRect() we can't pass pointer as parameter because It required CvArr so I think it can't be the problem. – Gum Slashy Jul 13 '12 at 03:23
  • @GumSlashy - see my edit. According to the README of javacv (http://code.google.com/p/javacv/source/browse/README.txt), this is a correct way to iterate over contours. I also found this question in SO http://stackoverflow.com/questions/9648482/ocr-with-javacv which uses the same method for getting contours, and drawing their bounding boxes. – zenpoy Jul 13 '12 at 09:31
  • I try to put cvBoundingRect(cvGetSeqElem(cvSeq, i),0); in the problem but it didn't work for me and I have put the answer that I have got correct results.anyway thank you very much for your reply. – Gum Slashy Jul 17 '12 at 12:51
  • @GumSlashy - I suggested you try `h_next()` for iterating (and also sketched the way to use it), and I also posted a links in the comments which shows a full example on how to use it. – zenpoy Jul 17 '12 at 13:29
3

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!

  • 1
    Thanks for your quick respond and I have tried your sugetion but it gave same out put. – Gum Slashy Jul 09 '12 at 03:36
  • I think it would help to update your code with your changes. Perhaps there's still a integer division in there? – Noremac Jul 12 '12 at 20:50
  • 1
    I have put the answer and in that also I try to cast the w/h in to float value but it did't give significant effect. But its true that it might be effecting significantly in some other cases. Thank you very much for your reply. – Gum Slashy Jul 17 '12 at 12:47
2

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);
        }
    }
Gum Slashy
  • 385
  • 5
  • 20
  • 2
    This is exactly the same method I suggested a few days ago - this is too bad you didn't try it: `// ... 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 }` – zenpoy Jul 17 '12 at 13:26