5

I am trying to implement Paper detection through OpenCV. I am able to understand the concept of how can I get it,

Input-> Canny-> Blur-> Find Conture-> Search (closed)Quadrilateral-> Draw Conture

but still, I am new to OpenCV programming. So having issues in implementing it. I was able to find help through this answer

Android OpenCV Paper Sheet detection

but it's drawing contour on every possible lining. Here is the code I am trying to implement.

public Mat onCameraFrame(CvCameraViewFrame inputFrame) {

    mRgba = inputFrame.rgba(); 
    Imgproc.drawContours(mRgba,findContours(mRgba), 0, new Scalar(0 , 255, 0), 5);
    return mRgba; 
}

public static class Quadrilateral {
    public MatOfPoint contour;
    public Point[] points;

    public Quadrilateral(MatOfPoint contour, Point[] points) {
        this.contour = contour;
        this.points = points;
    }
}

public static Quadrilateral findDocument( Mat inputRgba ) {
    ArrayList<MatOfPoint> contours = findContours(inputRgba);
    Quadrilateral quad = getQuadrilateral(contours);
    return quad;
}

private static ArrayList<MatOfPoint> findContours(Mat src) {

    double ratio = src.size().height / 500;
    int height = Double.valueOf(src.size().height / ratio).intValue();
    int width = Double.valueOf(src.size().width / ratio).intValue();
    Size size = new Size(width,height);

    Mat resizedImage = new Mat(size, CvType.CV_8UC4);
    Mat grayImage = new Mat(size, CvType.CV_8UC4);
    Mat cannedImage = new Mat(size, CvType.CV_8UC1);

    Imgproc.resize(src,resizedImage,size);
    Imgproc.cvtColor(resizedImage, grayImage, Imgproc.COLOR_RGBA2GRAY, 4);
    Imgproc.GaussianBlur(grayImage, grayImage, new Size(5, 5), 0);
    Imgproc.Canny(grayImage, cannedImage, 75, 200);

    ArrayList<MatOfPoint> contours = new ArrayList<MatOfPoint>();
    Mat hierarchy = new Mat();

    Imgproc.findContours(cannedImage, contours, hierarchy, Imgproc.RETR_LIST, Imgproc.CHAIN_APPROX_SIMPLE);

    hierarchy.release();

    Collections.sort(contours, new Comparator<MatOfPoint>() {

        @Override
        public int compare(MatOfPoint lhs, MatOfPoint rhs) {
            return Double.valueOf(Imgproc.contourArea(rhs)).compareTo(Imgproc.contourArea(lhs));
        }
    });

    resizedImage.release();
    grayImage.release();
    cannedImage.release();

    return contours;
}

private static Quadrilateral getQuadrilateral(ArrayList<MatOfPoint> contours) {

    for ( MatOfPoint c: contours ) {
        MatOfPoint2f c2f = new MatOfPoint2f(c.toArray());
        double peri = Imgproc.arcLength(c2f, true);
        MatOfPoint2f approx = new MatOfPoint2f();
        Imgproc.approxPolyDP(c2f, approx, 0.02 * peri, true);

        Point[] points = approx.toArray();

        // select biggest 4 angles polygon
        if (points.length == 4) {
            Point[] foundPoints = sortPoints(points);

            return new Quadrilateral(c, foundPoints);
        }
    }

    return null;
}

private static Point[] sortPoints(Point[] src) {

    ArrayList<Point> srcPoints = new ArrayList<>(Arrays.asList(src));

    Point[] result = { null , null , null , null };

    Comparator<Point> sumComparator = new Comparator<Point>() {
        @Override
        public int compare(Point lhs, Point rhs) {
            return Double.valueOf(lhs.y + lhs.x).compareTo(rhs.y + rhs.x);
        }
    };

    Comparator<Point> diffComparator = new Comparator<Point>() {

        @Override
        public int compare(Point lhs, Point rhs) {
            return Double.valueOf(lhs.y - lhs.x).compareTo(rhs.y - rhs.x);
        }
    };

    // top-left corner = minimal sum
    result[0] = Collections.min(srcPoints, sumComparator);

    // bottom-right corner = maximal sum
    result[2] = Collections.max(srcPoints, sumComparator);

    // top-right corner = minimal diference
    result[1] = Collections.min(srcPoints, diffComparator);

    // bottom-left corner = maximal diference
    result[3] = Collections.max(srcPoints, diffComparator);

    return result;
}

The answer suggests that I should use Quadrilateral Object and call it with Imgproc.drawContours(), but this function takes in ArrayList as argument where as Quadrilateral object contains MatofPoint and Point[]. Can someone help me through this..I am using OpenCV(3.3) and Android (1.5.1)?

Here is the sample what it should look like Expected Output

Ap00rv
  • 128
  • 1
  • 11
  • Please attach some sample input test cases ? – ZdaR Aug 24 '17 at 11:50
  • @ZdaR I am sorry I didn't understand. I am trying it out in real time and just wanting to draw a green line all along a paper in the real time frame. – Ap00rv Aug 24 '17 at 12:05
  • take a look at [this](http://answers.opencv.org/question/170615) – sturkmen Aug 24 '17 at 12:19
  • As your desired object is of white color, you may try with binary threshold the `white` color from the input image. – ZdaR Aug 24 '17 at 12:21
  • @ZdaR no that is not the case every time, that is why I am more focused on finding closed quadrilateral. – Ap00rv Aug 24 '17 at 13:11
  • @sturkmen I think this pretty much I have done but seems to have missed out something in the implementing them. Can you take a closer look at my code? – Ap00rv Aug 24 '17 at 13:16
  • What issues do you have with using the `Quadrilateral`? – Rick M. Aug 24 '17 at 16:19
  • @RickM. If you see my code in onCameraFrame() method I used Imgproc.drawContours() to draw contours in the frame but it takes in ArrayList where as my Quadrilateral gives me MatofPoint and Point[]. So couldn't figure out how to use Quadrilateral object to draw contour. – Ap00rv Aug 24 '17 at 19:39
  • Looking at the sample input I guess it should return one contour. Why don't you make an array list and add the `MatofPoint` to this list which you pass to `drawContour`. – Rick M. Aug 24 '17 at 20:28

0 Answers0