基于OpenCV C++提取黑白图像中四边形的底部极端角点
嘿,我来帮你搞定这个提取四边形底部两个极端角点的问题!你之前的遍历方法效率低,其实可以借助OpenCV的轮廓处理工具来高效解决,下面是具体的思路和实现步骤:
核心思路
不用遍历整个像素矩阵,而是先提取四边形的轮廓,再通过轮廓近似得到四边形的四个顶点,最后从顶点中筛选出底部的两个角点——这样既能保证准确性,又能大幅提升效率。
具体步骤
1. 提取四边形的轮廓
首先从二值化Mat中提取目标四边形的轮廓,因为你的图像里只有一个四边形,所以取最外层的主轮廓即可:
std::vector<std::vector<cv::Point>> contours; std::vector<cv::Vec4i> hierarchy; // 提取最外层轮廓,RETR_EXTERNAL只取最外层,CHAIN_APPROX_SIMPLE压缩轮廓点 cv::findContours(binaryMat, contours, hierarchy, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); // 取第一个轮廓(假设只有一个四边形) std::vector<cv::Point> quadContour = contours[0];
2. 将轮廓近似为四边形顶点
因为原始轮廓可能有冗余点,用approxPolyDP把轮廓近似成4个顶点的多边形(也就是四边形本身):
std::vector<cv::Point> quadVertices; double epsilon = 0.02 * cv::arcLength(quadContour, true); // 轮廓周长的2%作为近似阈值 cv::approxPolyDP(quadContour, quadVertices, epsilon, true); // 确保得到的是4个顶点(如果不是,可能需要调整epsilon或先做形态学处理) assert(quadVertices.size() == 4);
3. 筛选底部的两个极端角点
OpenCV中图像的y轴是向下的,所以y值越大,位置越靠下。我们需要从4个顶点中找出y值最大的两个点,再区分左右:
// 先按y值从大到小排序,前两个就是最靠下的点 std::sort(quadVertices.begin(), quadVertices.end(), [](const cv::Point& a, const cv::Point& b) { return a.y > b.y; }); cv::Point bottomLeft, bottomRight; // 比较前两个点的x坐标,x小的是左下角,x大的是右下角 if (quadVertices[0].x < quadVertices[1].x) { bottomLeft = quadVertices[0]; bottomRight = quadVertices[1]; } else { bottomLeft = quadVertices[1]; bottomRight = quadVertices[0]; }
如果遇到两个点y值完全相同的情况(比如水平底边的四边形),这个逻辑也能正确区分左右。
4. 生成目标输出Mat
创建两个和原始Mat同尺寸、同类型的Mat,初始化为0,然后分别将两个角点的位置设为1:
// 初始化结果Mat,全0 cv::Mat bottomLeftMat = cv::Mat::zeros(binaryMat.size(), binaryMat.type()); cv::Mat bottomRightMat = cv::Mat::zeros(binaryMat.size(), binaryMat.type()); // 将目标点设为1 bottomLeftMat.at<uchar>(bottomLeft.y, bottomLeft.x) = 1; bottomRightMat.at<uchar>(bottomRight.y, bottomRight.x) = 1;
额外优化建议
- 如果二值化图像有小噪点,可以先做形态学闭运算(
cv::morphologyEx),填充小孔洞,确保轮廓提取的准确性; - 如果轮廓近似得到的顶点数不是4,可以调整
epsilon的值(比如改成0.01或0.03),或者检查二值化的效果,确保四边形是完整的连通区域。
这样处理下来,效率比遍历整个矩阵高太多了——毕竟轮廓的点数远少于图像的总像素数,尤其是大尺寸图像时差距会非常明显。
内容的提问来源于stack exchange,提问作者abdnChap
相关产品推荐
相关产品推荐

