如何用OpenCV实现的文档矫正?

摘要:使用 OpenCV 进行文档矫正 本文只发布于博客园与pchar博客 std::vector<std::vector<cv::Point>> cvhel
使用 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; } 效果展示