Search code examples
androidopencvobject-recognition

Android OpenCV Paper Sheet detection


I think this question is asked before but i didnt find a sample or solution for my problem. I am new to opencv and i want to use the OpenCV CameraPreview for paper sheet detection. In my sample app i use opencv 3.0.0 with static initialization. I understand that object recognition can done with these steps:

  1. Make the input image Canny
  2. Blur the Canny Image
  3. Find Contours on the blurred Canny Image
  4. Search for rectangles etc
  5. Draw lines or fill the rectangle with half transparent color

My problem is now that i can canny and blur the image but i dont know how to find contours and rectangles and fill them with a half transparent color.

Here is my current onCameraFrame Function:

@Override
public Mat onCameraFrame(CameraBridgeViewBase.CvCameraViewFrame inputFrame) {
    Mat input = inputFrame.rgba();
    Mat output = input.clone();
    Imgproc.Canny(input, output, 50, 50);
    Imgproc.blur(output, output,new Size(5,5));
    //Find Contours
    //Search for biggest Contour/Rectangle
    //Fill Rectangle with half transparent Color
    return output;
}

Can anybody help me to solve the problem of paper sheet detection and has a code sample for android/java? Thank you


Solution

  • The following code is from the Open Note Scanner app I am developing, you can use it to look for more information.

    the function findDocument will return a Quadrilateral object that encapsulates a MatOfPoint with the contour and a Point[] with individual points. You can call it and with the returned object call the Imgproc.drawContours() to finish your image.

    All the code was written using as basis this excellent tutorial from pyimagesearch

    NOTICE: this was a quick transplant of the methods from my code, it doesn't have syntax errors but I didn't tested it.

    package com.todobom.opennotescanner.views;
    
    import org.opencv.core.CvType;
    import org.opencv.core.Mat;
    import org.opencv.core.MatOfPoint;
    import org.opencv.core.MatOfPoint2f;
    import org.opencv.core.Point;
    import org.opencv.core.Size;
    import org.opencv.imgproc.Imgproc;
    
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.Collections;
    import java.util.Comparator;
    
    public class detectDocument {
    
        /**
         *  Object that encapsulates the contour and 4 points that makes the larger
         *  rectangle on the image
         */
        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;
        }
    
    }