我需要计算二进制图像中对象的长度(对象内部像素之间的最大距离)。由于它是二进制图像,因此我们可能会将其视为值为0(白色)和1(黑色)的2D数组。我需要的是一个聪明的(最好是简单的)算法来执行这个操作。请记住,图像中有许多对象。
要澄清的图片:
alt text http://cl.ly/489019a048c1bf20c6bb/content
示例输入图像:
答案 0 :(得分:3)
我认为问题很简单,如果一个物体的边界是凸的并且一条线上没有三个顶点(即没有改变多边形就不能去除顶点):那么你可以随机选择两个点并使用一个简单的梯度下降类型搜索找到最长的行:
Start with random vertices A, B
See if the line A' - B is longer than A - B where A' is the point left of A; if so, replace A with A'
See if the line A' - B is longer than A - B where A' is the point right of A; if so, replace A with A'
Do the same for B
repeat until convergence
所以我建议找到每个种子blob的凸包,删除所有“superfluos”顶点(以确保收敛)并运行上面的算法。
构造凸包是O(n log n)运算IIRC,其中n是边界像素的数量。对于像这样的小物体应该非常有效。 编辑:我只记得凸包船体算法的O(n log n)需要排序点。如果边界点是连通分量分析的结果,则它们已经排序。所以整个算法应该在O(n)时间内运行,其中n是边界点的数量。 (但这是很多工作,因为你可能必须编写自己的凸壳算法或修改一个以跳过排序。)
添加:回复评论
如果你不需要100%的准确度,你可以简单地将椭圆拟合到每个blob并计算长轴的长度:这可以从central moments计算出来(IIRC它只是平方根,如果协方差矩阵的最大特征值),因此它是O(n)运算,并且可以在图像上的单次扫描中有效地计算。它还有一个额外的好处,就是它会考虑一个斑点的所有像素,而不仅仅是两个极值点,即它受噪声的影响要小得多。
答案 1 :(得分:2)
找到椭圆的长轴长度,该椭圆具有与区域相同的归一化第二中心矩。在MATLAB中,您可以使用regionprops
。
答案 2 :(得分:1)
非常粗糙的强力方法是首先识别所有边缘像素(与非黑色像素相邻的对象中的任何黑色像素)并计算所有可能的边缘像素对之间的距离。这些距离中最长的将给出物体的长度。
如果对象的形状总是与样本中的对象相同,则可以通过仅评估对象中具有最高和最低x和y值的像素来加快速度。
答案 3 :(得分:1)
我建议尝试“反向”距离变换。在数学形态学的神奇世界中(抱歉无法抗拒头韵),距离变换为您提供每个像素与其最近边界像素的最近距离。在你的情况下,你感兴趣的是到边界像素的最远距离,因此我巧妙地应用了“反向”前缀。
您可以找到有关距离变换here和here的信息。我相信matlab按照here实现了距离变换。这将使我相信您可以在octave中找到距离变换的开源实现。此外,如果opencv实施它,我至少不会感到惊讶。
我没有多想过,但对我来说直观,你应该能够反转距离变换并在与原始距离变换大致相同的时间内计算它。
答案 4 :(得分:1)
我认为您可以考虑使用breadth first search算法。
基本思想是循环遍历图像中的每一行和一列,如果您还没有访问过该节点(一个节点是一个带有彩色像素的行和列),那么您将首先运行广度搜索。您可以访问每个节点,并跟踪对象的最大和最小点。
这是一些C ++示例代码(未经测试):
#include <vector>
#include <queue>
#include <cmath>
using namespace std;
// used to transition from given row, col to each of the
// 8 different directions
int dr[] = { -1, 0, 1, -1, 1, -1, 0, 1 };
int dc[] = { -1, -1, -1, 0, 0, 1, 1, 1 };
// WHITE or COLORED cells
const int WHITE = 0;
const int COLORED = 1;
// number of rows and columns
int nrows = 2000;
int ncols = 2000;
// assume G is the image
int G[2000][2000];
// the "visited array"
bool vis[2000][2000];
// get distance between 2 points
inline double getdist(double x1, double y1, double x2, double y2) {
double d1 = x1 - x2;
double d2 = y1 - y2;
return sqrt(d1*d1+d2*d2);
}
// this function performs the breadth first search
double bfs(int startRow, int startCol) {
queue< int > q;
q.push(startRow);
q.push(startCol);
vector< pair< int, int > > points;
while(!q.empty()) {
int r = q.front();
q.pop();
int c = q.front();
q.pop();
// already visited?
if (vis[r][c])
continue;
points.push_back(make_pair(r,c));
vis[r][c] = true;
// try all eight directions
for(int i = 0; i < 8; ++i) {
int nr = r + dr[i];
int nc = c + dc[i];
if (nr < 0 || nr >= nrows || nc < 0 || nc >= ncols)
continue; // out of bounds
// push next node on queue
q.push(nr);
q.push(nc);
}
}
// the distance is maximum difference between any 2 points encountered in the BFS
double diff = 0;
for(int i = 0; i < (int)points.size(); ++i) {
for(int j = i+1; j < (int)points.size(); ++j) {
diff = max(diff,getdist(points[i].first,points[i].second,points[j].first,points[j].second));
}
}
return diff;
}
int main() {
vector< double > lengths;
memset(vis,false,sizeof vis);
for(int r = 0; r < nrows; ++r) {
for(int c = 0; c < ncols; ++c) {
if (G[r][c] == WHITE)
continue; // we don't care about cells without objects
if (vis[r][c])
continue; // we've already processed this object
// find the length of this object
double len = bfs(r,c);
lengths.push_back(len);
}
}
return 0;
}