使用 OpenCV 進行文件矯正

Icys發表於2024-03-26

使用 OpenCV 進行文件矯正

本文只釋出於部落格園pchar部落格

    std::vector<std::vector<cv::Point>> cvhelper::findCorners(const cv::Mat &image) {
        cv::Mat gaussImage;
        cv::GaussianBlur(image, gaussImage, cv::Size(5, 5), 0);
        cv::threshold(gaussImage, gaussImage, 0, 255, cv::THRESH_BINARY | cv::THRESH_OTSU);
        cv::Canny(gaussImage, gaussImage, 50, 200);
        cv::morphologyEx(gaussImage, gaussImage, cv::MORPH_CLOSE,
                         cv::Mat::ones(5, 5, CV_32F));
        std::vector<std::vector<cv::Point>> contours;
        std::vector<cv::Vec4i> hierarchy;
        cv::findContours(gaussImage, contours, hierarchy, cv::RETR_LIST, cv::CHAIN_APPROX_SIMPLE);


        std::vector<std::vector<cv::Point>> ret;

        for (auto &contour: contours) {
            double peri = cv::arcLength(contour, true);
            std::vector<cv::Point> approx;
            cv::approxPolyDP(contour, approx, 0.05 * peri, true);
            if (approx.size() == 4 && cv::isContourConvex(approx) && cv::contourArea(approx) > 1000) {
                ret.push_back(approx);
            }
        }

        return ret;
    }

    void cvhelper::GetDocumentRect(cv::Mat &src, std::vector<cv::Point> &ret_points) {
        int shrunkImageHeight = 500;
        cv::Mat shrunkImage;
        cv::resize(src, shrunkImage, cv::Size(shrunkImageHeight * src.cols / src.rows, shrunkImageHeight));

        cv::cvtColor(shrunkImage, shrunkImage, cv::COLOR_BGR2Luv);
        std::vector<cv::Mat> channels;
        cv::split(shrunkImage, channels);

        std::vector<std::vector<cv::Point>> documentCorners;

        for (const auto &channel: channels) {
            std::vector<std::vector<cv::Point>> corners = findCorners(channel);
            if (!corners.empty()) {
                double maxArea = 0.0;
                std::vector<cv::Point> maxAreaContourIt;
                for (const auto &contour: corners) {
                    double area = cv::contourArea(contour);
                    if (area > maxArea) {
                        maxArea = area;
                        maxAreaContourIt = contour;
                    }
                }

                if (maxArea > 0) {
                    std::vector<cv::Point> scaledContour;
                    for (const auto &point: maxAreaContourIt) {
                        cv::Point scaledPoint;
                        scaledPoint.x = static_cast<int>(point.x * static_cast<double>(src.rows) /
                                                         shrunkImageHeight);
                        scaledPoint.y = static_cast<int>(point.y * static_cast<double>(src.rows) /
                                                         shrunkImageHeight);
                        scaledContour.push_back(scaledPoint);
                    }
                    documentCorners.push_back(scaledContour);
                }
            }
        }

        if (documentCorners.size() == 0) {
            return;
        }

        std::vector<cv::Point> maxDocumentCorners;
        double maxDocumentArea = 0.0;
        for (const auto &documentCorner: documentCorners) {
            double area = cv::contourArea(documentCorner);
            if (area > maxDocumentArea) {
                maxDocumentArea = area;
                maxDocumentCorners = documentCorner;
            }
        }

        ret_points = maxDocumentCorners;
    }

    cv::Mat cvhelper::CutKeyPosition(cv::Mat &src, std::vector<cv::Point2f> &src_points) {
        // 透視變換
        cv::Point2f dst_points[4];
        dst_points[0] = cv::Point2f(src.cols, 0);
        dst_points[1] = cv::Point2f(0, 0);
        dst_points[2] = cv::Point2f(0, src.rows);
        dst_points[3] = cv::Point2f(src.cols, src.rows);

        cv::Point2f tL = src_points[1];
        cv::Point2f tR = src_points[0];
        cv::Point2f bR = src_points[3];
        cv::Point2f bL = src_points[2];

        int width = (std::min)(cv::norm(tR - tL), cv::norm(bR - bL));
        int height = (std::min)(cv::norm(tR - bR), cv::norm(tL - bL));


        cv::Mat M = cv::getPerspectiveTransform(src_points.data(), dst_points);
        cv::Mat dst;
        cv::warpPerspective(src, dst, M, src.size());
        // 縮放影像
        cv::resize(dst, dst, cv::Size(width, height));
        return dst;
    }

效果展示

使用 OpenCV 進行文件矯正

相關文章