【问题标题】:How to get rid of skewness in scanned form?如何摆脱扫描形式的偏斜?
【发布时间】:2020-09-26 22:02:57
【问题描述】:

我必须构建一个程序,该程序采用倾斜形式(已扫描的图像)进行图像处理。第一步是消除偏斜。我成功地获得了图像的轮廓,我正在尝试做一个four_point_transform,如这篇文章Remove top section of image above border line to detect text document 中所述。但是,由于以下原因,我的代码失败了:

错误

java.lang.RuntimeException: OpenCV(4.4.0) C:\projects\javacpp-presets\opencv\cppbuild\windows-x86_64\opencv-4.4.0\modules\imgproc\src\imgwarp.cpp:3391: error: (-215:Assertion failed) src.checkVector(2, CV_32F) == 4 && dst.checkVector(2, CV_32F) == 4 in function 'cv::getPerspectiveTransform

代码

  protected  static void fixSkeweness(Mat mat){

        Mat mask = new Mat();
        Mat gray = new Mat();
        Mat denoised = new Mat();
        Mat bin = new Mat();
        Mat hierarchy = new Mat();
        MatVector contours = new MatVector();

        cvtColor(mat, gray, COLOR_BGR2GRAY);
        //Normalize
        GaussianBlur(gray, denoised, new Size(5, 5), 0);
        threshold(denoised, mask, 0, 255, THRESH_BINARY_INV | THRESH_OTSU);
        normalize(gray, gray, 0, 255, NORM_MINMAX, -1, mask);
        // Convert image to binary
        threshold(gray, bin, 150, 255, THRESH_BINARY);
        // Find contours
        findContours(bin, contours, hierarchy, RETR_TREE, CHAIN_APPROX_NONE);
        long contourCount = contours.size();
        System.out.println("Countour count " + contourCount);

        double maxArea = 0;
        int maxAreaId = 0;
        for (int i = 0; i < contourCount; ++i) {
            // Calculate the area of each contour
            Mat contour = contours.get(i);
            double area = contourArea(contour);
            if(area > maxArea){
                maxAreaId = i;
                maxArea = area;
            }

        }


        Double peri = arcLength(contours.get(maxAreaId), true);
        Mat newcontour = new Mat();
        approxPolyDP(contours.get(maxAreaId), newcontour,0.02 * peri, true);
        Mat result = new Mat();
        getPerspectiveTransform(newcontour.reshape(4,2), result);
        imwrite("src/test/resources/isDataPage/fourPointTransform.jpg", result);

    }

失败的代码行是:

getPerspectiveTransform(newcontour.reshape(4,2), result);

请问我可以得到一些帮助吗?

示例图片:

根据建议答案的工作代码

protected static Mat findBiggestContour(Mat mat){
    Mat mask = new Mat();
    Mat gray = new Mat();
    Mat denoised = new Mat();
    Mat bin = new Mat();
    Mat hierarchy = new Mat();
    MatVector contours = new MatVector();

    //Pre-process image
    cvtColor(mat, gray, COLOR_BGR2GRAY);
    threshold(gray, bin, 0, 255, THRESH_BINARY_INV + THRESH_OTSU);
    findContours(bin, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE);


    double maxArea = 0;
    int maxAreaId = 0;

    for (int i = 0; i < contours.size(); ++i) {

        // Calculate the area of each contour
        Mat contour = contours.get(i);
        double area = contourArea(contour);

        if(area > 5000 && i!=0){
            maxAreaId = i;
            maxArea = area;
        }

    }

    //Get Min Area Rect and inverse it
    RotatedRect rect = minAreaRect(contours.get(maxAreaId));
    float newAngle = rect.angle();

    if (rect.angle() < 45){
        newAngle = newAngle + 90;
    }
    RotatedRect angle =rect.angle( newAngle);

    int h = mat.size().height();
    int w = mat.size().width();

    int centerW =  w/2;
    int centerH = h/2;
    
    //find rotation matrix and apply it woohoo
    Point2f center = new Point2f(centerW, centerH);
    Mat m = getRotationMatrix2D(center, angle.angle(), 1.0);
    Mat rotated = new Mat();

    warpAffine(mat,rotated,m, new Size(w, h),INTER_CUBIC,BORDER_REPLICATE,new Scalar(10,10));
    imwrite("src/test/resources/tmp2/rotrated.png",rotated);

    return rotated;
}

【问题讨论】:

  • OpenCV getPerspectiveTransform() 在您尝试使用它时不起作用。第一个参数是 4 个 src(当前)点的数组,第二个是 src 点应该在的 4 个点的数组。该方法返回您要传递给wrapPerspective() 的变换矩阵。在此处查看用法:stackoverflow.com/a/45163886/9152984
  • 谢谢你,我修好了,但我没有输出,虽然这本身很糟糕,但我意识到我期望得到的轮廓没有被识别,因此我不得不退后一步检测更好的轮廓。

标签: java opencv javacv


【解决方案1】:

getPerspectiveTransform() 正在以其他方式工作(请参阅我的评论)。但是,我在这里发现minAreaRect() 是更合适的方法。我没有准备好的java环境,所以这里是python代码。我希望您在转换时不会遇到任何困难。

img = cv2.imread('images/form.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# some preprocessing as you did
# your src image is pretty clean though, and if they all are like that,
#   I wouldn't use blur as it makes form borders less obvious
# gray = cv2.blur(gray, (5, 5))
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]

# find the largest contour assuming it will be some nice rectangle
ctrs, hier = cv2.findContours(thresh, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
largest_ctr_idx = max(range(len(ctrs)), key=lambda i: cv2.contourArea(ctrs[i]))

# get the contour's rotation angle
angle = cv2.minAreaRect(ctrs[largest_ctr_idx])[-1]
if angle < -45:
    angle += 90

# find rotation matrix and apply it woohoo
h, w = img.shape[:2]
center = (w // 2, h // 2)
m = cv2.getRotationMatrix2D(center, angle, 1.0)
rotated = cv2.warpAffine(img, m, (w, h), flags=cv2.INTER_CUBIC,
    borderMode=cv2.BORDER_REPLICATE)

找到轮廓:

歪斜的图像:

【讨论】:

  • 非常感谢您不知道!我很感激。我用你的 Java 代码更新了我的帖子
猜你喜欢
  • 2011-03-12
  • 1970-01-01
  • 1970-01-01
  • 2021-08-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-28
  • 2021-05-20
相关资源
最近更新 更多