opencv - 二值图像中磁盘的半径
问题描述
我有像这样的二值化图像:
我需要确定内部实心圆盘的中心和半径。如您所见,它被一个接触它的纹理区域包围,因此简单的连接组件检测不起作用。无论如何,周长的很大一部分都有空隙。
一种可能的解决方法是腐蚀直到所有纹理消失或与磁盘断开连接,但这可能很耗时并且迭代次数不确定。(此外,在某些不幸的情况下,圆盘上有小孔,会随着侵蚀而增大。)
有什么更好的建议来以稳健和快速的方式解决这个问题吗?(我标记了 OpenCV,但这不是强制性的,重要的是方法。)
解决方案
你可以:
- 反转图像
- 找到仅包含零的最大轴对齐矩形,(我使用了这个答案中的 C++ 代码)。该算法非常快。
- 从矩形中获取圆的中心和半径
代码:
#include <opencv2\opencv.hpp>
using namespace std;
using namespace cv;
// https://stackoverflow.com/a/30418912/5008845
cv::Rect findMaxRect(const cv::Mat1b& src)
{
cv::Mat1f W(src.rows, src.cols, float(0));
cv::Mat1f H(src.rows, src.cols, float(0));
cv::Rect maxRect(0,0,0,0);
float maxArea = 0.f;
for (int r = 0; r < src.rows; ++r)
{
for (int c = 0; c < src.cols; ++c)
{
if (src(r, c) == 0)
{
H(r, c) = 1.f + ((r>0) ? H(r-1, c) : 0);
W(r, c) = 1.f + ((c>0) ? W(r, c-1) : 0);
}
float minw = W(r,c);
for (int h = 0; h < H(r, c); ++h)
{
minw = std::min(minw, W(r-h, c));
float area = (h+1) * minw;
if (area > maxArea)
{
maxArea = area;
maxRect = cv::Rect(cv::Point(c - minw + 1, r - h), cv::Point(c+1, r+1));
}
}
}
}
return maxRect;
}
int main()
{
cv::Mat1b img = cv::imread("path/to/img", cv::IMREAD_GRAYSCALE);
// Correct image
img = img > 127;
cv::Rect r = findMaxRect(~img);
cv::Point center ( std::round(r.x + r.width / 2.f), std::round(r.y + r.height / 2.f));
int radius = std::sqrt(r.width*r.width + r.height*r.height) / 2;
cv::Mat3b out;
cv::cvtColor(img, out, cv::COLOR_GRAY2BGR);
cv::rectangle(out, r, cv::Scalar(0, 255, 0));
cv::circle(out, center, radius, cv::Scalar(0, 0, 255));
return 0;
}
推荐阅读
- mysql - 在 Management Studio 上使用 Workbench 连接到 SQL Server
- next.js - 这是哪个运算符?在下一个身份验证示例中?
- kotlin - JDK8 - 包 sun.security.provider.certpath.ldap 不存在 - 实施建议
- javascript - 如何处理基于 iframe 的 chrome 扩展中的权限?
- node.js - 如果 (msg.content == "Ash" || "ash") 为任何输入返回 true,即使为 false?
- angular - 以角度将路径值绑定到图像显示组件
- wordpress - 尝试在 WooCommerce 中有条件地隐藏 Square
- python - 如何确定 virtualenv 站点包中代码的“拥有包”?
- neo4j - 如何按指定顺序在neo4j中保存节点的多个标签?
- dart - 我不能使用“如果”