Android OpenCV 纸张检测

标签 android opencv object-recognition

我认为之前有人问过这个问题,但我没有找到解决我的问题的示例或解决方案。我是 opencv 的新手,我想使用 OpenCV CameraPreview 进行纸张检测。在我的示例应用程序中,我使用带有静态初始化的 opencv 3.0.0。 我知道对象识别可以通过以下步骤完成:

  1. 使输入图像Canny
  2. 模糊 Canny 图像
  3. 在模糊的 Canny 图像上找到轮廓
  4. 搜索矩形等
  5. 绘制线条或用半透明颜色填充矩形

我现在的问题是我可以巧妙地模糊图像,但我不知道如何找到轮廓和矩形并用半透明颜色填充它们。

这是我当前的 onCameraFrame 函数:

@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;
}

谁能帮我解决纸张检测的问题并有android/java的代码示例? 谢谢

最佳答案

以下代码来自Open Note Scanner我正在开发的应用程序,您可以使用它来查找更多信息。

函数 findDocument 将返回一个 Quadrilateral 对象,该对象封装了一个带有轮廓的 MatOfPoint 和一个带有各个点的 Point[]。您可以调用它并使用返回的对象调用 Imgproc.drawContours() 来完成您的图像。

所有代码都是以 this excellent tutorial from pyimagesearch 为基础编写的

注意:这是从我的代码中快速移植的方法,它没有语法错误,但我没有测试它。

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

}

关于Android OpenCV 纸张检测,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31310315/

相关文章:

java - 无限垂直世界

android - 我的应用程序图标未在某些设备上显示

Android TCP发送数据

opencv - 支持向量机预测函数总是返回一个大数字

opencv - 检测多个相同颜色的矩形并获取它们的坐标

android - 用于 ANDROID 图像比较的 OpenCV

image-processing - 如何用openCV检测圆圈组?

c++ - 我应该使用哪种对象识别算法?

java - 某些设备(如 oppo、vivo、MI 等)的前台服务在 Doze 模式下被杀死

python - 错误 : OpenCV(4. 1.0) 错误 : (-215:Assertion failed) ! ssize.empty() 函数 'cv::resize'