Quad Refine::operator()( const cv::Mat &inputImage, const Quad &quad, const float proximityRatio) { cv::Mat_<cv::Point2f> refinedQuad(quad); // Taking a ROI around the raw corners with some margin static const float GROWTH_RATIO = 1.2f/10.0f; cv::Rect roi = chilitags::growRoi(inputImage, refinedQuad, GROWTH_RATIO); static const int MIN_ROI_SIZE = 10; if (roi.width < MIN_ROI_SIZE || roi.height < MIN_ROI_SIZE) return quad; cv::Point2f roiOffset = roi.tl(); for (int i : {0,1,2,3}) refinedQuad(i) -= roiOffset; float averageSideLength = cv::arcLength(refinedQuad, true) / 4.0f; float cornerNeighbourhood = proximityRatio*averageSideLength; // ensure the cornerSubPixel search window is smaller that the ROI cornerNeighbourhood = cv::min(cornerNeighbourhood, (roi.width - 5.0f)/2.0f); cornerNeighbourhood = cv::min(cornerNeighbourhood, (roi.height - 5.0f)/2.0f); cornerNeighbourhood = cv::max(cornerNeighbourhood, 1.0f); cv::cornerSubPix(inputImage(roi), refinedQuad, cv::Size(cornerNeighbourhood, cornerNeighbourhood), cv::Size(-1, -1), cv::TermCriteria( cv::TermCriteria::MAX_ITER + cv::TermCriteria::EPS, 5, 0.01f)); #ifdef DEBUG_Refine cv::Mat debugImage = inputImage(roi).clone(); for(int i=0; i<4; ++i) { cv::circle(debugImage, refinedQuad(i), 3, cv::Scalar::all(128), 2); cv::line(debugImage, refinedQuad(i), refinedQuad(i), cv::Scalar::all(255), 5); cv::rectangle(debugImage, refinedQuad(i)-cv::Point2f(cornerNeighbourhood, cornerNeighbourhood), refinedQuad(i)+cv::Point2f(cornerNeighbourhood, cornerNeighbourhood), cv::Scalar::all(255)); printf("%1.1f %1.1f ", refinedQuad(i).x, refinedQuad(i).y); } printf("\n"); cv::imshow("Refine", debugImage); cv::waitKey(0); #endif for (int i : {0,1,2,3}) refinedQuad(i) += roiOffset; return refinedQuad.reshape(1); }
chilitags::Quad chilitags::Refine::operator()(const cv::Mat &inputImage, const Quad &quad) { cv::Mat_<cv::Point2f> refinedQuad(quad); // Taking a ROI around the raw corners with some margin static const float GROWTH_RATIO = 1.2f/10.0f; cv::Rect roi = cv::boundingRect(refinedQuad); int xGrowth = (int)(GROWTH_RATIO*roi.width); int yGrowth = (int)(GROWTH_RATIO*roi.height); roi.x -= xGrowth; roi.y -= yGrowth; roi.width += 2*xGrowth; roi.height += 2*yGrowth; // Making sure the ROI is still in the image int previousRoiX = roi.x; int previousRoiY = roi.y; roi.x = std::max(roi.x, 0); roi.y = std::max(roi.y, 0); roi.width -= roi.x - previousRoiX; roi.height -= roi.y - previousRoiY; roi.width = cv::min(roi.x+roi.width, inputImage.cols)-roi.x; roi.height = cv::min(roi.y+roi.height, inputImage.rows)-roi.y; static const int MIN_ROI_SIZE = 10; if (roi.width < MIN_ROI_SIZE || roi.height < MIN_ROI_SIZE) return quad; cv::Point2f roiOffset = roi.tl(); for (int i : {0,1,2,3}) refinedQuad(i) -= roiOffset; static const double PROXIMITYRATIO = 1.5/10.0; double averageSideLength = cv::arcLength(refinedQuad, true) / 4.0; double cornerNeighbourhood = PROXIMITYRATIO*averageSideLength; // ensure the cornerSubPixel search window is smaller that the ROI cornerNeighbourhood = cv::min(cornerNeighbourhood, ((double) roi.width-5)/2); cornerNeighbourhood = cv::min(cornerNeighbourhood, ((double) roi.height-5)/2); cv::cornerSubPix(inputImage(roi), refinedQuad, cv::Size(cornerNeighbourhood, cornerNeighbourhood), cv::Size(-1, -1), cv::TermCriteria( cv::TermCriteria::MAX_ITER + cv::TermCriteria::EPS, 5, 0.01)); for (int i : {0,1,2,3}) refinedQuad(i) += roiOffset; #ifdef DEBUG_Refine cv::Mat debugImage = inputImage(roi).clone(); for(int i=0; i<4; ++i) { cv::circle(debugImage, refinedQuad(i)-roiOffset, 3, cv::Scalar::all(128), 2); cv::line(debugImage, refinedQuad(i)-roiOffset, refinedQuad(i)-roiOffset, cv::Scalar::all(255), 5); cv::rectangle(debugImage, refinedQuad(i)-roiOffset-cv::Point2f(cornerNeighbourhood, cornerNeighbourhood), refinedQuad(i)-roiOffset+cv::Point2f(cornerNeighbourhood, cornerNeighbourhood), cv::Scalar::all(255)); printf("%1.1f %1.1f ", refinedQuad(i).x, refinedQuad(i).y); } printf("\n"); cv::imshow("Refine", debugImage); cv::waitKey(0); #endif // Sometimes, the corners are refined into a concave quadrilateral // which makes ReadBits crash cv::Mat convexHull; cv::convexHull(refinedQuad, convexHull, false); if (convexHull.rows == 4) return convexHull.reshape(1); return quad; }