簡體   English   中英

如何在OpenCV Java中從HoughLines變換檢測矩形

[英]How to detect rectangle from HoughLines transform in OpenCV Java

我知道這是重復的帖子,但仍然會卡在實施上。 我遵循互聯網上的一些指南,以了解如何在OpenCV和Java中檢測圖像中的文檔。 我想到的第一個方法是在對圖像進行一些預處理(例如模糊,邊緣檢測)之后,使用findContours,在獲得所有輪廓之后,我可以找到最大的輪廓,並假設這是我要尋找的矩形,但是失敗了在某些情況下,例如文檔沒有像一個角一樣被完全拿走。 在嘗試了幾次並進行了一些新的處理后,但是根本無法正常工作之后,我發現HoughLine轉換使它變得更容易。 從現在開始,我將所有行都放在圖像中,但是仍然無法做什么來定義我想要的興趣矩形。 這是我到目前為止的實現代碼: 方法1:使用findContours

Mat grayImage = new Mat();
    Mat detectedEdges = new Mat();
    // convert to grayscale
    Imgproc.cvtColor(frame, grayImage, Imgproc.COLOR_BGR2GRAY);
    // reduce noise with a 3x3 kernel
    // Imgproc.blur(grayImage, detectedEdges, new Size(3, 3));
    Imgproc.medianBlur(grayImage, detectedEdges, 9);
    // Imgproc.equalizeHist(detectedEdges, detectedEdges);
    // Imgproc.GaussianBlur(detectedEdges, detectedEdges, new Size(5, 5), 0, 0, Core.BORDER_DEFAULT);
    Mat edges = new Mat();
    // canny detector, with ratio of lower:upper threshold of 3:1
    Imgproc.Canny(detectedEdges, edges, this.threshold.getValue(), this.threshold.getValue() * 3, 3, true);
    // makes the object in white bigger
    Imgproc.dilate(edges, edges, new Mat(), new Point(-1, -1), 1); // 1
    Image imageToShow = Utils.mat2Image(edges);
    updateImageView(cannyFrame, imageToShow);
    /// Find contours
    List<MatOfPoint> contours = new ArrayList<MatOfPoint>();
    Imgproc.findContours(edges, contours, new Mat(), Imgproc.RETR_LIST, Imgproc.CHAIN_APPROX_SIMPLE);
    // loop over the contours
    MatOfPoint2f approxCurve;
    double maxArea = 0;
    int maxId = -1;
    for (MatOfPoint contour : contours) {
        MatOfPoint2f temp = new MatOfPoint2f(contour.toArray());
        double area = Imgproc.contourArea(contour);
        approxCurve = new MatOfPoint2f();
        Imgproc.approxPolyDP(temp, approxCurve, Imgproc.arcLength(temp, true) * 0.02, true);
        if (approxCurve.total() == 4 && area >= maxArea) {
            double maxCosine = 0;
            List<Point> curves = approxCurve.toList();
            for (int j = 2; j < 5; j++) {
                double cosine = Math.abs(angle(curves.get(j % 4), curves.get(j - 2), curves.get(j - 1)));
                maxCosine = Math.max(maxCosine, cosine);
            }
            if (maxCosine < 0.3) {
                maxArea = area;
                maxId = contours.indexOf(contour);
            }
        }
    }
    MatOfPoint maxMatOfPoint = contours.get(maxId);
    MatOfPoint2f maxMatOfPoint2f = new MatOfPoint2f(maxMatOfPoint.toArray());
    RotatedRect rect = Imgproc.minAreaRect(maxMatOfPoint2f);
    System.out.println("Rect angle: " + rect.angle);
    Point points[] = new Point[4];
    rect.points(points);
    for (int i = 0; i < 4; ++i) {
        Imgproc.line(frame, points[i], points[(i + 1) % 4], new Scalar(255, 255, 25), 3);
    }

    Mat dest = new Mat();
    frame.copyTo(dest, frame);
    return dest;

Apparch 2:使用HoughLine變換

// STEP 1: Edge detection
    Mat grayImage = new Mat();
    Mat detectedEdges = new Mat();
    Vector<Point> start = new Vector<Point>();
    Vector<Point> end = new Vector<Point>();
    // convert to grayscale
    Imgproc.cvtColor(frame, grayImage, Imgproc.COLOR_BGR2GRAY);
    // reduce noise with a 3x3 kernel
    // Imgproc.blur(grayImage, detectedEdges, new Size(3, 3));
    Imgproc.medianBlur(grayImage, detectedEdges, 9);
    // Imgproc.equalizeHist(detectedEdges, detectedEdges);
    // Imgproc.GaussianBlur(detectedEdges, detectedEdges, new Size(5, 5), 0, 0, Core.BORDER_DEFAULT);
    // AdaptiveThreshold -> classify as either black or white
    // Imgproc.adaptiveThreshold(detectedEdges, detectedEdges, 255, Imgproc.ADAPTIVE_THRESH_MEAN_C, Imgproc.THRESH_BINARY, 5, 2);
    // Imgproc.Sobel(detectedEdges, detectedEdges, -1, 1, 0);
    Mat edges = new Mat();
    // canny detector, with ratio of lower:upper threshold of 3:1
    Imgproc.Canny(detectedEdges, edges, this.threshold.getValue(), this.threshold.getValue() * 3, 3, true);
    // apply gaussian blur to smoothen lines of dots
    Imgproc.GaussianBlur(edges, edges, new org.opencv.core.Size(5, 5), 5);
    // makes the object in white bigger
    Imgproc.dilate(edges, edges, new Mat(), new Point(-1, -1), 1); // 1
    Image imageToShow = Utils.mat2Image(edges);
    updateImageView(cannyFrame, imageToShow);
    // STEP 2: Line detection
    // Do Hough line
    Mat lines = new Mat();
    int minLineSize = 50;
    int lineGap = 10;
    Imgproc.HoughLinesP(edges, lines, 1, Math.PI / 720, (int) this.threshold.getValue(), this.minLineSize.getValue(), lineGap);
    System.out.println("MinLineSize: " + this.minLineSize.getValue());
    System.out.println(lines.rows());
    for (int i = 0; i < lines.rows(); i++) {
        double[] val = lines.get(i, 0);
        Point tmpStartP = new Point(val[0], val[1]);
        Point tmpEndP = new Point(val[2], val[3]);
        start.add(tmpStartP);
        end.add(tmpEndP);
        Imgproc.line(frame, tmpStartP, tmpEndP, new Scalar(255, 255, 0), 2);
    }

    Mat dest = new Mat();
    frame.copyTo(dest, frame);
    return dest;

HoughLine結果1 HoughLine結果2

如何從HoughLine結果中檢測所需的矩形? 有人可以給我下一步完成HoughLine轉換方法的步驟。 任何幫助都適用。 我堅持了一段時間。

感謝您閱讀本文。

這個答案幾乎是我發布的其他兩個答案( 此處此處 )的混合。 但是根據您的情況,我用於其他答案的管道可能會有所改善。 因此,我認為值得發布一個新的答案。

有很多方法可以實現您想要的。 但是,我認為這里不需要使用HoughLinesP線檢測。 這是我在樣本上使用的管道:

步驟1:檢測egdes

  • 如果輸入圖像太大,則調整大小 (我注意到該管道在縮小給定輸入圖像的版本上效果更好)
  • 使用Canny濾鏡 模糊灰度輸入並檢測邊緣

第2步:找到卡的角

  • 計算輪廓
  • 長度對輪廓進行排序 ,僅保留最大的輪廓
  • 生成此輪廓的凸包
  • 使用approxPolyDP簡化凸包(應該給出四邊形
  • 從近似多邊形創建蒙版
  • 返回四邊形的4點

步驟3:單應性

  • 使用findHomography查找紙張的仿射變換(在步驟2中找到4個角點)
  • 使用計算的單應矩陣扭曲輸入圖像

注意 :當然,一旦在輸入圖像的縮小版本上找到了紙張的角,就可以輕松計算出全尺寸輸入圖像上角的位置。 這是為了使翹曲的紙張具有最佳分辨率。

結果如下: 在此處輸入圖片說明

vector<Point> getQuadrilateral(Mat & grayscale, Mat& output)
{
    Mat approxPoly_mask(grayscale.rows, grayscale.cols, CV_8UC1);
    approxPoly_mask = Scalar(0);

    vector<vector<Point>> contours;
    findContours(grayscale, contours, RETR_EXTERNAL, CHAIN_APPROX_NONE);

    vector<int> indices(contours.size());
    iota(indices.begin(), indices.end(), 0);

    sort(indices.begin(), indices.end(), [&contours](int lhs, int rhs) {
        return contours[lhs].size() > contours[rhs].size();
    });

    /// Find the convex hull object for each contour
    vector<vector<Point> >hull(1);
    convexHull(Mat(contours[indices[0]]), hull[0], false);

    vector<vector<Point>> polygon(1);
    approxPolyDP(hull[0], polygon[0], 20, true);
    drawContours(approxPoly_mask, polygon, 0, Scalar(255));
    imshow("approxPoly_mask", approxPoly_mask);

    if (polygon[0].size() >= 4) // we found the 4 corners
    {
        return(polygon[0]);
    }

    return(vector<Point>());
}


int main(int argc, char** argv)
{

    Mat input = imread("papersheet1.JPG");
    resize(input, input, Size(), 0.1, 0.1);
    Mat input_grey;
    cvtColor(input, input_grey, CV_BGR2GRAY);
    Mat threshold1;
    Mat edges;
    blur(input_grey, input_grey, Size(3, 3));
    Canny(input_grey, edges, 30, 100);


    vector<Point> card_corners = getQuadrilateral(edges, input);
    Mat warpedCard(400, 300, CV_8UC3);
    if (card_corners.size() == 4)
    {
        Mat homography = findHomography(card_corners, vector<Point>{Point(warpedCard.cols, warpedCard.rows), Point(0, warpedCard.rows), Point(0, 0), Point(warpedCard.cols, 0)});
        warpPerspective(input, warpedCard, homography, Size(warpedCard.cols, warpedCard.rows));
    }

    imshow("warped card", warpedCard);
    imshow("edges", edges);
    imshow("input", input);
    waitKey(0);

    return 0;
}

這是C ++代碼,但要翻譯成Java應該不難。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM