mirror of
https://github.com/opencv/opencv.git
synced 2025-07-20 19:17:36 +08:00
![]() Add getClosestEllipsePoints() function to get the closest point on an ellipse #26299 Following https://github.com/opencv/opencv/issues/26078, I was thinking that a function to get for a considered 2d point the corresponding closest point (or maybe directly the distance?) on an ellipse could be useful. This would allow computing the fitting error with `fitEllipse()` for instance. Code is based from: - https://stackoverflow.com/questions/22959698/distance-from-given-point-to-given-ellipse/46007540#46007540 - https://blog.chatfield.io/simple-method-for-distance-to-ellipse/ - https://github.com/0xfaded/ellipse_demo --- Demo code: <details> <summary>code</summary> ```cpp #include <iostream> #include <opencv2/opencv.hpp> namespace { void scaleApplyColormap(const cv::Mat &img_float, cv::Mat &img) { cv::Mat img_scale = cv::Mat::zeros(img_float.size(), CV_8UC3); double min_val = 0, max_val = 0; cv::minMaxLoc(img_float, &min_val, &max_val); std::cout << "min_val=" << min_val << " ; max_val=" << max_val << std::endl; if (max_val - min_val > 1e-2) { float a = 255 / (max_val - min_val); float b = -a * min_val; cv::convertScaleAbs(img_float, img_scale, a, b); cv::applyColorMap(img_scale, img, cv::COLORMAP_TURBO); } else { std::cerr << "max_val - min_val <= 1e-2" << std::endl; } } cv::Mat drawEllipseDistanceMap(const cv::RotatedRect &ellipse_params) { float bb_rect_w = ellipse_params.center.x + ellipse_params.size.width; float bb_rect_h = ellipse_params.center.y + ellipse_params.size.height; std::vector<cv::Point2f> points_list; points_list.resize(1); cv::Mat pointsf; cv::Mat closest_pts; cv::Mat dist_map = cv::Mat::zeros(bb_rect_h*1.5, bb_rect_w*1.5, CV_32F); for (int i = 0; i < dist_map.rows; i++) { for (int j = 0; j < dist_map.cols; j++) { points_list[0].x = j; points_list[0].y = i; cv::Mat(points_list).convertTo(pointsf, CV_32F); cv::getClosestEllipsePoints(ellipse_params, pointsf, closest_pts); dist_map.at<float>(i, j) = std::hypot(closest_pts.at<cv::Point2f>(0).x-j, closest_pts.at<cv::Point2f>(0).y-i); } } cv::Mat dist_map_8u; scaleApplyColormap(dist_map, dist_map_8u); return dist_map_8u; } } int main() { std::vector<cv::Point2f> points_list; // [1434, 308], [1434, 309], [1433, 310], [1427, 310], [1427, 312], [1426, 313], [1422, 313], [1422, 314], points_list.push_back(cv::Point2f(1434, 308)); points_list.push_back(cv::Point2f(1434, 309)); points_list.push_back(cv::Point2f(1433, 310)); points_list.push_back(cv::Point2f(1427, 310)); points_list.push_back(cv::Point2f(1427, 312)); points_list.push_back(cv::Point2f(1426, 313)); points_list.push_back(cv::Point2f(1422, 313)); points_list.push_back(cv::Point2f(1422, 314)); // [1421, 315], [1415, 315], [1415, 316], [1414, 317], [1408, 317], [1408, 319], [1407, 320], [1403, 320], points_list.push_back(cv::Point2f(1421, 315)); points_list.push_back(cv::Point2f(1415, 315)); points_list.push_back(cv::Point2f(1415, 316)); points_list.push_back(cv::Point2f(1414, 317)); points_list.push_back(cv::Point2f(1408, 317)); points_list.push_back(cv::Point2f(1408, 319)); points_list.push_back(cv::Point2f(1407, 320)); points_list.push_back(cv::Point2f(1403, 320)); // [1403, 321], [1402, 322], [1396, 322], [1396, 323], [1395, 324], [1389, 324], [1389, 326], [1388, 327], points_list.push_back(cv::Point2f(1403, 321)); points_list.push_back(cv::Point2f(1402, 322)); points_list.push_back(cv::Point2f(1396, 322)); points_list.push_back(cv::Point2f(1396, 323)); points_list.push_back(cv::Point2f(1395, 324)); points_list.push_back(cv::Point2f(1389, 324)); points_list.push_back(cv::Point2f(1389, 326)); points_list.push_back(cv::Point2f(1388, 327)); // [1382, 327], [1382, 328], [1381, 329], [1376, 329], [1376, 330], [1375, 331], [1369, 331], [1369, 333], points_list.push_back(cv::Point2f(1382, 327)); points_list.push_back(cv::Point2f(1382, 328)); points_list.push_back(cv::Point2f(1381, 329)); points_list.push_back(cv::Point2f(1376, 329)); points_list.push_back(cv::Point2f(1376, 330)); points_list.push_back(cv::Point2f(1375, 331)); points_list.push_back(cv::Point2f(1369, 331)); points_list.push_back(cv::Point2f(1369, 333)); // [1368, 334], [1362, 334], [1362, 335], [1361, 336], [1359, 336], [1359, 1016], [1365, 1016], [1366, 1017], points_list.push_back(cv::Point2f(1368, 334)); points_list.push_back(cv::Point2f(1362, 334)); points_list.push_back(cv::Point2f(1362, 335)); points_list.push_back(cv::Point2f(1361, 336)); points_list.push_back(cv::Point2f(1359, 336)); points_list.push_back(cv::Point2f(1359, 1016)); points_list.push_back(cv::Point2f(1365, 1016)); points_list.push_back(cv::Point2f(1366, 1017)); // [1366, 1019], [1430, 1019], [1430, 1017], [1431, 1016], [1440, 1016], [1440, 308] points_list.push_back(cv::Point2f(1366, 1019)); points_list.push_back(cv::Point2f(1430, 1019)); points_list.push_back(cv::Point2f(1430, 1017)); points_list.push_back(cv::Point2f(1431, 1016)); points_list.push_back(cv::Point2f(1440, 1016)); points_list.push_back(cv::Point2f(1440, 308)); cv::Mat pointsf; cv::Mat(points_list).convertTo(pointsf, CV_32F); cv::RotatedRect ellipse_params = cv::fitEllipseAMS(pointsf); std::cout << "ellipse_params, center=" << ellipse_params.center << " ; size=" << ellipse_params.size << " ; angle=" << ellipse_params.angle << std::endl; cv::TickMeter tm; tm.start(); cv::Mat dist_map_8u = drawEllipseDistanceMap(ellipse_params); tm.stop(); std::cout << "Elapsed time: " << tm.getAvgTimeSec() << " sec" << std::endl; cv::Point center(ellipse_params.center.x, ellipse_params.center.y); cv::Point axis(ellipse_params.size.width/2, ellipse_params.size.height/2); std::vector<cv::Point> ellipse_pts_list; cv::ellipse2Poly(center, axis, ellipse_params.angle, 0, 360, 1, ellipse_pts_list); cv::polylines(dist_map_8u, ellipse_pts_list, false, cv::Scalar(0, 0, 0), 3); // Points to be fitted cv::Mat closest_pts; cv::getClosestEllipsePoints(ellipse_params, pointsf, closest_pts); for (int i = 0; i < closest_pts.rows; i++) { cv::Point pt; pt.x = closest_pts.at<cv::Point2f>(i).x; pt.y = closest_pts.at<cv::Point2f>(i).y; cv::circle(dist_map_8u, pt, 8, cv::Scalar(0, 0, 255), 2); } cv::imwrite("dist_map_8u.png", dist_map_8u); return EXIT_SUCCESS; } ``` </details>  --- ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [x] The PR is proposed to the proper branch - [x] There is a reference to the original bug report and related work - [x] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [x] The feature is well documented and sample code can be built with the project CMake |
||
---|---|---|
.. | ||
images | ||
js_tutorials | ||
pattern_tools | ||
py_tutorials | ||
tools | ||
tutorials | ||
acircles_pattern.png | ||
bodybg.png | ||
charuco_board_pattern.png | ||
CMakeLists.txt | ||
disabled_doc_warnings.txt | ||
Doxyfile.in | ||
DoxygenLayout.xml | ||
faq.markdown | ||
footer.html | ||
header.html | ||
LICENSE_BSD.txt | ||
LICENSE_CHANGE_NOTICE.txt | ||
mymath.js | ||
mymath.sty | ||
opencv-logo2.png | ||
opencv-logo-small.png | ||
opencv-logo-white.png | ||
opencv-logo.md | ||
opencv-logo.png | ||
opencv.bib | ||
opencv.ico | ||
opencv.jpg | ||
pattern.png | ||
root.markdown.in | ||
stylesheet.css | ||
tutorial-utils.js |