Ask Your Question

Revision history [back]

click to hide/show revision 1
initial version

I am making document scanner. It is detecting only edges and not corners.

I am making Document scanner. It have both option for Capture and Import from gallery.I am using AsynkTask for processing image. Image process flow- Image>Resize>Apply k-means with 2 cluster center>Identify cluster and binarize image>find contours and pick largest one>detect lines and corners>Transform> Result image.

Input Image

Image Result

It is detecting edges and displaying only white edges with black background. I want cropped color image. Edge is detecting and largest contour is also detected , but cannot sort corners properly.

getPage Fucntion

private void getPage() {
     new AsyncTask<Void, Void, Bitmap>() {
        ProgressDialog dialog;

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            dialog = ProgressDialog.show(MainActivity.this, "Processing Image", "please wait...");
        }

        @Override
        protected Bitmap doInBackground(Void... params) {
            Mat srcRes = new Mat(src.size(), src.type());
            Mat srcGray = new Mat();

            //Preparing image to perform k-means algo
            Mat samples = new Mat(src.rows() * src.cols(), 3, CvType.CV_32F);
            for (int y = 0; y < src.rows(); y++) {
                for (int x = 0; x < src.cols(); x++) {
                    for (int z = 0; z < 3; z++) {
                        samples.put(x + y * src.cols(), z, src.get(y, x)[z]);
                    }
                }
            }

            //k-means algorithm
            int clusterCount = 2;
            Mat labels = new Mat();
            int attempts = 5;
            Mat centers = new Mat();

            Core.kmeans(samples, clusterCount, labels, new TermCriteria(TermCriteria.MAX_ITER | TermCriteria.EPS, 10000, 0.0001), attempts, Core.KMEANS_PP_CENTERS, centers);
            double dstCenter0 = calcWhiteDist(centers.get(0, 0)[0], centers.get(0, 1)[0], centers.get(0, 2)[0]);
            double dstCenter1 = calcWhiteDist(centers.get(1, 0)[0], centers.get(1, 1)[0], centers.get(1, 2)[0]);

            int paperCluster = (dstCenter0 < dstCenter1) ? 0 : 1;
            for (int y = 0; y < src.rows(); y++) {
                for (int x = 0; x < src.cols(); x++) {
                    int cluster_idx = (int) labels.get(x + y * src.cols(), 0)[0];
                    if (cluster_idx != paperCluster) {
                        srcRes.put(y, x, 0, 0, 0, 255);
                    } else {
                        srcRes.put(y, x, 255, 255, 255, 255);
                    }
                }
            }
            Imgproc.cvtColor(src, srcGray, Imgproc.COLOR_BGR2GRAY);
            Imgproc.Canny(srcGray, srcGray, 50, 150);
            List<MatOfPoint> contours = new ArrayList<>();
            Mat hierarchy = new Mat();

         //Find ontours

                   Imgproc.findContours(srcGray, contours, hierarchy, Imgproc.RETR_TREE, Imgproc.CHAIN_APPROX_SIMPLE);

            int index = 0;
            double maxim = Imgproc.contourArea(contours.get(0));

            for (int contourIdx = 1; contourIdx < contours.size(); contourIdx++) {
                double temp;
                temp = Imgproc.contourArea(contours.get(contourIdx));
                if (maxim < temp) {
                    maxim = temp;
                    index = contourIdx;
                }
            }

            Mat drawing = Mat.zeros(srcRes.size(), CvType.CV_8UC1);
            Imgproc.drawContours(drawing, contours, index, new Scalar(255), 1);

            Mat lines = new Mat();
            Imgproc.HoughLinesP(drawing, lines, 1, Math.PI / 180, 70, 30, 10);

            ArrayList<Point> corners = new ArrayList<>();
            for (int i = 0; i < lines.cols(); i++) {
                for (int j = i + 1; j < lines.cols(); j++) {
                    double[] line1 = lines.get(0, i);
                    double[] line2 = lines.get(0, j);

                    Point pt = findIntersection(line1, line2);
                    if (pt.x >= 0 && pt.y >= 0 && pt.x <= drawing.cols() && pt.y <= drawing.rows()) {
                        if (!exists(corners, pt)) {
                            corners.add(pt);
                        }
                    }
                }
            }
            if (corners.size() != 4) {
                errorMessage = "Cannot detect perfect corners";
                Bitmap bitmap = Bitmap.createBitmap(drawing.cols(), drawing.rows(), Bitmap.Config.ARGB_8888);
                Utils.matToBitmap(drawing, bitmap);

                return bitmap;
            }

            sortCorners(corners);

            if (corners.size() == 0) {
                errorMessage = "Cannot sort corners";
                return null;
            }
            double top = Math.sqrt(Math.pow(corners.get(0).x - corners.get(1).x, 2) + Math.pow(corners.get(0).y - corners.get(1).y, 2));
            double right = Math.sqrt(Math.pow(corners.get(1).x - corners.get(2).x, 2) + Math.pow(corners.get(1).y - corners.get(2).y, 2));
            double bottom = Math.sqrt(Math.pow(corners.get(2).x - corners.get(3).x, 2) + Math.pow(corners.get(2).y - corners.get(3).y, 2));
            double left = Math.sqrt(Math.pow(corners.get(3).x - corners.get(1).x, 2) + Math.pow(corners.get(3).y - corners.get(1).y, 2));

            Mat quad = Mat.zeros(new Size(Math.max(top, bottom), Math.max(left, right)), CvType.CV_8UC3);

            ArrayList<Point> result_pts = new ArrayList<Point>();
            result_pts.add(new Point(0, 0));
            result_pts.add(new Point(quad.cols(), 0));
            result_pts.add(new Point(quad.cols(), quad.rows()));
            result_pts.add(new Point(0, quad.rows()));

            Mat cornerPts = Converters.vector_Point2f_to_Mat(corners);
            Mat resultPts = Converters.vector_Point2f_to_Mat(result_pts);


        //Mat to Bitmap transform

            Mat transformation = Imgproc.getPerspectiveTransform(cornerPts, resultPts);
            Imgproc.warpPerspective(srcOrig, quad, transformation, quad.size());
            Imgproc.cvtColor(quad, quad, Imgproc.COLOR_BGR2RGBA);
            Bitmap bitmap = Bitmap.createBitmap(quad.cols(), quad.rows(), Bitmap.Config.ARGB_8888);
            Utils.matToBitmap(quad, bitmap);

            return bitmap;
        }

        @Override
        protected void onPostExecute(Bitmap bitmap) {
            super.onPostExecute(bitmap);
            dialog.dismiss();
            if (bitmap != null) {
                imgview.setImageBitmap(bitmap);
            } else if (errorMessage != null) {
                Toast.makeText(getApplicationContext(), errorMessage, Toast.LENGTH_SHORT).show();
            }
        }

    }.execute();
}

CalcWhiteDist

static double calcWhiteDist(double r, double g, double b) {
    return Math.sqrt(Math.pow(255 - r, 2) + Math.pow(255 - g, 2) + Math.pow(255 - b, 2));
}

FindIntersection

static Point findIntersection(double[] line1, double[] line2) {
    double start_x1 = line1[0], start_y1 = line1[1], end_x1 = line1[2], end_y1 = line1[3], start_x2 = line2[0], start_y2 = line2[1], end_x2 = line2[2], end_y2 = line2[3];
    double denominator = ((start_x1 - end_x1) * (start_y2 - end_y2)) - ((start_y1 - end_y1) * (start_x2 - end_x2));

    if (denominator != 0) {
        Point pt = new Point();
        pt.x = ((start_x1 * end_y1 - start_y1 * end_x1) * (start_x2 - end_x2) - (start_x1 - end_x1) * (start_x2 * end_y2 - start_y2 * end_x2)) / denominator;
        pt.y = ((start_x1 * end_y1 - start_y1 * end_x1) * (start_y2 - end_y2) - (start_y1 - end_y1) * (start_x2 * end_y2 - start_y2 * end_x2)) / denominator;
        return pt;
    } else return new Point(-1, -1);
}

exists

static boolean exists(ArrayList<Point> corners, Point pt) {
    for (int i = 0; i < corners.size(); i++) {
        if (Math.sqrt(Math.pow(corners.get(i).x - pt.x, 2) + Math.pow(corners.get(i).y - pt.y, 2)) < 10) {
            return true;
        }
    }
    return false;
}

Sort Corners

static void sortCorners(ArrayList<Point> corners) {
    ArrayList<Point> top, bottom;

    top = new ArrayList<>();
    bottom = new ArrayList<>();

    Point center = new Point();

    for (int i = 0; i < corners.size(); i++) {
        center.x += corners.get(i).x / corners.size();
        center.y += corners.get(i).y / corners.size();
    }

    for (int i = 0; i < corners.size(); i++) {
        if (corners.get(i).y < center.y) top.add(corners.get(i));
        else bottom.add(corners.get(i));
    }
    corners.clear();

    if (top.size() == 2 && bottom.size() == 2) {
        Point top_left = top.get(0).x > top.get(1).x ? top.get(1) : top.get(0);
        Point top_right = top.get(0).x > top.get(1).x ? top.get(0) : top.get(1);
        Point bottom_left = bottom.get(0).x > bottom.get(1).x ? bottom.get(1) : bottom.get(0);
        Point bottom_right = bottom.get(0).x > bottom.get(1).x ? bottom.get(0) : bottom.get(1);

        top_left.x *= scaleFactor;
        top_left.y *= scaleFactor;

        top_right.x *= scaleFactor;
        top_right.y *= scaleFactor;

        bottom_left.x *= scaleFactor;
        bottom_left.y *= scaleFactor;

        bottom_right.x *= scaleFactor;
        bottom_right.y *= scaleFactor;

        corners.add(top_left);
        corners.add(top_right);
        corners.add(bottom_right);
        corners.add(bottom_left);
    }
}

CalcScaleFactor

private static int calcScaleFactor(int rows, int cols) {
    int idealRow, idealCol;
    if (rows < cols) {
        idealRow = 240;
        idealCol = 320;
    } else {
        idealCol = 240;
        idealRow = 320;
    }
    int val = Math.min(rows / idealRow, cols / idealCol);
    if (val <= 0) {
        return 1;
    } else {
        return val;
    }
}