Java 如何消除扫描表单中的偏斜?

Java 如何消除扫描表单中的偏斜?,java,opencv,javacv,Java,Opencv,Javacv,我必须建立一个程序,以歪斜的形式(已扫描的图像)进行图像处理。第一步是消除偏斜。我成功地得到了图像的轮廓,我正试图做一个four\u point\u变换,如本文所示。但是,由于以下原因,我的代码失败: 错误 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:

我必须建立一个程序,以歪斜的形式(已扫描的图像)进行图像处理。第一步是消除偏斜。我成功地得到了图像的轮廓,我正试图做一个
four\u point\u变换
,如本文所示。但是,由于以下原因,我的代码失败:

错误

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

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

    }
我能得到一些帮助让它工作吗

示例图像:

根据建议答案的工作代码

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 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;
}
受保护的静态垫findBiggestContour(垫垫垫){
Mat mask=新Mat();
Mat灰色=新Mat();
去噪后的材料=新材料();
材料箱=新材料();
Mat层次结构=新Mat();
MatVector等高线=新MatVector();
//预处理图像
CVT颜色(垫子、灰色、颜色为灰色);
阈值(灰色,二进制,0,255,阈值二元,阈值大津);
findContours(箱子、轮廓、层次结构、检索树、链近似图和简单图);
双最大面积=0;
int maxAreaId=0;
对于(int i=0;i5000&&i!=0){
maxAreaId=i;
最大面积=面积;
}
}
//获取最小面积Rect并将其反转
RotatedRect=minareRect(轮廓.get(maxAreaId));
float newAngle=rect.angle();
如果(矩形角度()<45){
新角度=新角度+90;
}
旋转角度=矩形角度(新角度);
int h=材料尺寸()高度();
int w=材料尺寸()宽度();
int centerW=w/2;
int centerH=h/2;
//找到旋转矩阵并应用它
点2F中心=新点2F(中心W、中心H);
Mat m=getRotationMatrix2D(中心,角度,角度(),1.0);
旋转垫=新垫();
翘曲仿射(mat,旋转,m,新尺寸(w,h),中间立方,边界复制,新标量(10,10));
imwrite(“src/test/resources/tmp2/rotrated.png”,旋转);
返回旋转;
}

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

img=cv2.imread('images/form.png')
灰色=cv2.CVT颜色(img,cv2.COLOR\U BGR2GRAY)
#像你一样做一些预处理
#你的src图像非常清晰,如果他们都是这样的,
#我不会使用模糊,因为它使窗体边框不那么明显
#灰色=cv2。模糊(灰色,(5,5))
thresh=cv2.阈值(灰色,0,255,cv2.thresh\u二进制\u INV+cv2.thresh\u OTSU)[1]
#找到最大的轮廓,假设它是一个漂亮的矩形
ctrs,hier=cv2.查找到的对象(阈值,cv2.RETR\u列表,cv2.链近似值\u简单)
最大切圆切圆切圆切圆=最大(范围(透镜切圆),键=λi:cv2。切圆面积(切圆切圆[i]))
#获取轮廓的旋转角度
角度=cv2.Minareact(ctrs[最大ctr_idx])[-1]
如果角度<-45:
角度+=90
#找到旋转矩阵并应用它
h、 w=图像形状[:2]
中心=(w//2,h//2)
m=cv2.getRotationMatrix2D(中心,角度,1.0)
旋转=cv2。翘曲仿射(img,m,(w,h),标志=cv2。中间立方,
borderMode=cv2.BORDER(复制)
找到的轮廓:

倾斜图像:


OpenCV
getPerspectiveTransform()
在您尝试使用它时无法工作。第一个参数是由4个src(当前)点组成的数组,第二个参数是由4个点组成的数组,其中src点应为。该方法返回要传递给
wrapPerspective()
的变换矩阵。请看这里的用法:谢谢,我修复了它,但我没有得到输出,虽然这本身就不好,但我意识到我期望得到的轮廓没有被识别,因此我不得不后退一步,检测出更好的轮廓。我非常感谢你不知道!我非常感激。我用你的Java代码更新了我的帖子