使用OpenCV扫描文档

时间:2018-06-05 12:16:44

标签: java android opencv image-processing

我正在开发类似于this的Android文档扫描程序。

我搜索了这个,发现这可以用OpenCV实现,所以从OpenCV开始。

我尝试了很多例子来检测图像中的文档,但是无法检测图像是否具有浅色背景。检查样品图像以进行测试。

sample image

我正在使用OpenCV Android SDK并使用java代码进行图像处理。 这是代码:

public void scanDocument(Bitmap mBitmap)
{
    Mat mOriginalMat = convertToMat(mBitmap);
    int mRatio = getRadio(mOriginalMat);
    Size mSize = getImageFitSize(mOriginalMat, mRatio);

    Mat resizedMat = resizeMat(mOriginalMat, mSize);
    Mat colorMat = grayMat(resizedMat, mSize);
    Mat blurMat = medianBlurMat(colorMat, mSize);
    Mat thresholdMat = cannyEdgeMat(blurMat, mSize);

    ArrayList<MatOfPoint> contourList = findContours(thresholdMat, Imgproc.RETR_LIST, Imgproc.CHAIN_APPROX_SIMPLE);
    double maxArea = 0.0;
    int maxAreaIdx = -1;
    Collections.sort(contourList, new Comparator<MatOfPoint>() {
        @Override
        public int compare(MatOfPoint lhs, MatOfPoint rhs)
        {
        return Double.valueOf(Imgproc.contourArea(rhs)).compareTo(Imgproc.contourArea(lhs));
        }
    });

    ArrayList<MatOfPoint> contourListMax = new ArrayList<>();
    for (int idx = 0; idx < contourList.size(); idx++)
    {
        MatOfPoint contour = contourList.get(idx);

        MatOfPoint2f c2f = new MatOfPoint2f(contour.toArray());
        MatOfPoint2f approx = new MatOfPoint2f();
        double epsilon = Imgproc.arcLength(c2f, true);
        Imgproc.approxPolyDP(c2f, approx, epsilon * 0.02, true);

        Point[] points = approx.toArray();
        MatOfPoint approxTemp = new MatOfPoint(approx.toArray());

        if (points.length == 4 && Imgproc.isContourConvex(approxTemp) && maxArea < Imgproc.contourArea(approxTemp))
        {
            maxArea = Imgproc.contourArea(approxTemp);
            maxAreaIdx = idx;
            Point[] foundPoints = sortPoints(points);

            contourListMax.add(approxTemp);

            mPointFMap = new HashMap<>();
            mPointFMap.put(0, new PointF((float) foundPoints[0].x + xGap, (float) foundPoints[0].y + yGap));
            mPointFMap.put(1, new PointF((float) foundPoints[1].x + xGap, (float) foundPoints[1].y + yGap));
            mPointFMap.put(2, new PointF((float) foundPoints[3].x + xGap, (float) foundPoints[3].y + yGap));
            mPointFMap.put(3, new PointF((float) foundPoints[2].x + xGap, (float) foundPoints[2].y + yGap));
            break;
        }
    }

    Imgproc.drawContours(resizedMat, contourListMax, -1, new Scalar(255, 165, 0), 2);
    showMatToImageView(resizedMat);
}

private Mat convertToMat(Bitmap bitmap)
{
    Mat mat = Imgcodecs.imread(mFilePath);// new Mat(bitmap.getWidth(), bitmap.getHeight(), CvType.CV_8UC1);
    Imgproc.cvtColor(mat, mat, Imgproc.COLOR_BGR2RGB);
    return mat;
}

private double getRadio(Mat mat)
{
    double ratio;
    if (mat.size().width > mat.size().height)
        ratio = mat.size().height / mMainLayout.getHeight();
    else
        ratio = mat.size().width / mMainLayout.getWidth();
    return ratio;
}

private Size getImageFitSize(Mat mat, double ratio)
{
    int height = Double.valueOf(mat.size().height / ratio).intValue();
    int width = Double.valueOf(mat.size().width / ratio).intValue();
    return new Size(width, height);
}

private void showMatToImageView(Mat mat)
{
    final Bitmap bitmap = Bitmap.createBitmap(mat.width(), mat.height(), Bitmap.Config.ARGB_8888);
    Utils.matToBitmap(mat, bitmap);
    runOnUiThread(new Runnable()
    {
        @Override
        public void run()
        {
            mSourceImageView.setImageBitmap(bitmap);
            mProgressBar.setVisibility(View.GONE);
        }
    });
}

private Mat resizeMat(Mat mat, Size size)
{
    Mat resizedMat = new Mat(size, CvType.CV_8UC4);
    Imgproc.resize(mat, resizedMat, size);
    return resizedMat;
}

private Mat grayMat(Mat mat, Size size)
{
    Mat grayMat = new Mat(size, CvType.CV_8UC4);
    Imgproc.cvtColor(mat, grayMat, Imgproc.COLOR_RGB2GRAY, 4);
    return grayMat;
}

private Mat medianBlurMat(Mat mat, Size size)
{
    Mat blurMat = new Mat(size, CvType.CV_8UC4);
    Imgproc.medianBlur(mat, blurMat, 3);
    return blurMat;
}

private Mat cannyEdgeMat(Mat mat, Size size)
{
    if (thresholdVal <= 0)
        thresholdVal = 200;
    Mat cannyEdgeMat = new Mat(size, CvType.CV_8UC1);
    Imgproc.Canny(mat, cannyEdgeMat, thresholdVal * 0.5, thresholdVal, 3, true);
    return cannyEdgeMat;
}

private ArrayList<MatOfPoint> findContours(Mat mat, int retrievalMode, int approximationMode)
{
    ArrayList<MatOfPoint> contourList = new ArrayList<>();
    Mat hierarchy = new Mat();
    Imgproc.findContours(mat, contourList, hierarchy, retrievalMode, approximationMode);
    hierarchy.release();

    return contourList;
}

我希望在透视变换的帮助下检测文档点我会将文档转换为直接并进行其他图像过滤。

获得此结果图片。

result image

请帮我解决此问题。

1 个答案:

答案 0 :(得分:0)

您的问题是在资源非常有限的领域中。我实际上已经复制了上面粘贴的代码,因为我什至都不知道从哪里开始。但是,您Point[] foundPoints = sortPoints(points);的行中也没有包含 sortPoints()函数,也没有包含 thresholdVal xGap yGap 整数。他们是如何初始化的?通过共享其余代码,您将为我带来极大的帮助。谢谢。