用于检测点的“簇”的算法

时间:2008-12-10 13:28:26

标签: algorithm data-structures image-processing

我在这个区域有一个带有“点”的2D区域。我现在正在尝试检测点的“簇”,即具有一定高密度点的区域。

对如何优雅地检测这些区域的任何想法(或链接到有关思考的文章)?

16 个答案:

答案 0 :(得分:24)

如何为您的空间定义任意分辨率,并计算该矩阵中的每个点,衡量从该点到所有点的距离,然后您可以制作“热图”并使用阈值来定义簇。

这是一个很好的处理练习,也许以后我会发布一个解决方案。

编辑:

这是:

//load the image
PImage sample;
sample = loadImage("test.png");
size(sample.width, sample.height);
image(sample, 0, 0);
int[][] heat = new int[width][height];

//parameters
int resolution = 5; //distance between points in the gridq
int distance = 8; //distance at wich two points are considered near
float threshold = 0.5;
int level = 240; //leven to detect the dots
int sensitivity = 1; //how much does each dot matters

//calculate the "heat" on each point of the grid
color black = color(0,0,0);
loadPixels();
for(int a=0; a<width; a+=resolution){
  for(int b=0; b<height; b+=resolution){
    for(int x=0; x<width; x++){
      for(int y=0; y<height; y++){
        color c = sample.pixels[y*sample.width+x];        
        /**
         * the heat should be a function of the brightness and the distance, 
         * but this works (tm)
         */
        if(brightness(c)<level && dist(x,y,a,b)<distance){
          heat[a][b] += sensitivity;
        }
      }
    }
  }
}

//render the output
for(int a=0; a<width; ++a){
  for(int b=0; b<height; ++b){
    pixels[b*sample.width+a] = color(heat[a][b],0,0);
  }
}
updatePixels();
filter(THRESHOLD,threshold);

编辑2(效率低下但代码输出相同):

//load the image
PImage sample;
sample = loadImage("test.png");
size(sample.width, sample.height);
image(sample, 0, 0);
int[][] heat = new int[width][height];
int dotQ = 0;
int[][] dots = new int[width*height][2];
int X = 0;
int Y = 1;


//parameters
int resolution = 1; //distance between points in the grid
int distance = 20; //distance at wich two points are considered near
float threshold = 0.6;
int level = 240; //minimum brightness to detect the dots
int sensitivity = 1; //how much does each dot matters

//detect all dots in the sample
loadPixels();
for(int x=0; x<width; x++){
 for(int y=0; y<height; y++){
   color c = pixels[y*sample.width+x];
   if(brightness(c)<level) {
       dots[dotQ][X] += x;
       dots[dotQ++][Y] += y;
   }
 }
}

//calculate heat
for(int x=0; x<width; x+=resolution){
 for(int y=0; y<height; y+=resolution){
   for(int d=0; d<dotQ; d++){
     if(dist(x,y,dots[d][X],dots[d][Y]) < distance)
       heat[x][y]+=sensitivity;
   }
 }
}

//render the output
for(int a=0; a<width; ++a){
 for(int b=0; b<height; ++b){
   pixels[b*sample.width+a] = color(heat[a][b],0,0);
 }
}
updatePixels();
filter(THRESHOLD,threshold);

/** This smooths the ouput with low resolutions
* for(int i=0; i<10; ++i) filter(DILATE);
* for(int i=0; i<3; ++i) filter(BLUR);
* filter(THRESHOLD);
*/

输出(减少的)肯特样本:

答案 1 :(得分:23)

我建议使用均值漂移内核来查找点的密度中心。

Mean-shift illustration http://cvr.yorku.ca/members/gradstudents/kosta/compvis/file_mean_shift_path.gif

此图显示了一个平均移位内核(最初以群集边缘为中心)收敛到群集的最高密度点。

理论上(简而言之):

这个问题的几个答案已经暗示了这种做法的平均转变方式:

您在动画人物中看到的是这两个建议的组合:它使用移动的“块”(即内核)来寻找当地最高的密度。

均值漂移是一种迭代方法,它使用一个名为内核的像素邻域(类似于this one)并使用它来计算均值底层图像数据。此上下文中的 mean 是内核坐标的像素加权平均值。

在每次迭代中,内核的平均值定义下一次迭代的中心坐标 - 这称为 shift 。因此名称​​ mean-shift 。迭代的停止条件是当移位距离下降到0时(即,我们处于邻域中最密集的点)。

可以在this ppt presentation.

中找到关于均值漂移(理论和应用)的全面介绍

在实践中:

OpenCV中提供了均值漂移的实现:

int cvMeanShift( const CvArr* prob_image, CvRect window,
                 CvTermCriteria criteria, CvConnectedComp* comp );

O'Reilly's Learning OpenCv (google book excerpts)对其工作方式也有很好的解释。基本上只是喂你的点图像(prob_image)。

在实践中,诀窍是选择足够的内核大小。内核越小,就越需要将其发送到集群。内核越大,初始位置就越随机。但是,如果图像中有多个点簇,则内核可能会在它们之间汇聚。

答案 2 :(得分:13)

为了向Trebs语句添加一点助手,我认为现实地首先定义群集的定义是肯定的,“点在一起”,这很重要。

拿这个我生成的样本集,我知道那里有一个簇形状,我创建了它。

但是,以编程方式识别此“群集”可能很难。

人类可能会认为是一个大的环形星团,但是你的自动程序更有可能决定它是一系列半近距离的小型星团。

另外,请注意,存在超高密度区域,这些区域在更大的背景下,仅仅是分心

您需要考虑这种行为,并可能将相似密度的簇链接在一起,这些簇仅由较低密度的微小空隙分开,具体取决于具体应用。

无论你开发什么,我至少会对它如何识别这一组中的数据感兴趣。

(我认为研究HDRI ToneMapping背后的技术可能是有序的,因为它们或多或少地对光密度起作用,并且有“本地”色调图和“全局”色调图,每个都会产生不同的结果)< / p>

答案 3 :(得分:12)

将模糊滤镜应用于2D区域的副本。像

这样的东西
1 2 3 2 1
2 4 6 4 2
3 6 9 6 3
2 4 6 4 2
1 2 3 2 1

“较暗”的区域现在可以识别出多个点。

答案 4 :(得分:10)

您可以尝试创建数据的Quadtree表示。图中较短的路径对应于高密度区域。

或者,更清楚地说明:给定四叉树和水平顺序遍历,由“点”组成的每个低级节点将代表高密度区域。随着节点级别的增加,这些节点代表“点”的低密度区域

答案 5 :(得分:5)

形态学方法怎么样?

将阈值图像扩展一些数字(取决于点的目标密度),然后群集中的点将显示为单个对象。

OpenCV支持形态学操作(与一系列图像处理库一样):

http://www.seas.upenn.edu/~bensapp/opencvdocs/ref/opencvref_cv.htm#cv_imgproc_morphology

答案 6 :(得分:4)

这听起来像是一个学术问题。

想到的解决方案涉及r *树。这会将您的总面积划分为单独尺寸和可能重叠的框。完成此操作后,您可以通过计算平均距离来确定每个框是否代表“群集”。

R* Trees

如果这种方法变得难以实现,最好将数据网格划分为相等大小的细分,并确定每个细分是否发生;尽管如此,你必须非常注意边缘条件。我建议在初始划分之后,您将通过并在定义边缘的特定阈值内重新组合具有数据点的区域。

答案 7 :(得分:4)

  1. 将概率密度函数拟合到数据中。我会使用“高斯混合物”并使用K-means算法引发的期望最大化学习来拟合它。没有EM,K-means本身有时就足够了。需要使用模型订单选择算法来启动集群本身的数量。
  2. 然后,可以使用模型用p(x)对每个点进行评分。即得到该点由模型生成的后验概率。
  3. 找到最大p(x)以查找群集质心。
  4. 这可以使用机器学习工具箱在Matlab等工具中快速编码。在网络/标准文本中广泛讨论了MoG / EM学习/ K-Means聚类。我最喜欢的文字是Duda / Hart的“模式分类”。

答案 8 :(得分:3)

“具有一定高密度的区域”意味着您大致了解每单位面积中您认为高的点数。这使我走向网格方法,您将总面积分成适当大小的子区域,然后计算每个区域中的点数。一旦找到靠近阈值的网格区域,您也可以搜索网格的相邻区域。

答案 9 :(得分:3)

让我把它作为研究论文组织起来

<强>一个。问题陈述

引用Epaga: “我在这个区域有一个带有”点“的2D区域。我现在正在尝试检测点的”簇“,即具有一定高密度点的区域。”

请注意,没有提到点是来自图像。 (虽然它们可以作为一个订购)。

<强>弘 情况1:如果点只是点(点= 2D空间中的点)。 在这种情况下,您已经拥有x&amp; y所有点的位置。问题减少到集中点之一。 Ivan在提出解决方案方面做得很好。他还总结了其他类似风味的答案。 我的帖子除了他的帖子之外,你考虑的是你是否知道先验集群的数量。算法(可以相应地选择监督与非监督聚类)。

案例2:如果这些点确实来自图像。这里需要澄清问题。让我解释一下使用这张图片alt text 如果没有对点的灰度值进行区分,则组1,2,3,4和4表示。 5都是“不同的集群”。但是,如果基于灰度值进行区分,则簇5很棘手,因为点具有不同的灰度值。

无论如何,通过光栅扫描图像并存储非零(非白色)像素的坐标,可以将该问题简化为情况1。然后,可以使用之前提出的聚类算法来计算聚类和聚类中心的数量。

答案 10 :(得分:2)

我认为这取决于点和簇之间的分离程度。如果距离很大且不规则,我最初会triangulate这些点,然后删除/隐藏具有统计上较大边长的所有三角形。剩余的子三角测量形成任意形状的簇。遍历这些子三角测量的边缘产生多边形,这些多边形可用于确定每个簇中的哪些特定点。根据需要,也可以将多边形与已知的形状进行比较,例如Kent Fredric的圆环。

IMO,网格方法适用于快速和脏的解决方案,但在稀疏数据上非常快速地变得非常饥饿。四棵树更好,但TIN是我个人最喜欢的任何更复杂的分析。

答案 11 :(得分:0)

我会计算从每个点到所有其他点的距离。然后对距离进行排序。彼此之间的距离低于阈值的点被视为 Near 附近的一组点是一个集群。

问题在于,人们在看到图表时可能会清楚 cluster ,但没有明确的数学定义。您需要定义 near 阈值,可能需要根据经验进行调整,直到您的算法结果(或多或少)等于您认为的群集结果。

答案 12 :(得分:0)

您可以在您的飞机上叠加逻辑网格。如果网格具有一定数量的包含点,则认为它是“密集的”,然后可以变薄。在处理群集容差时,这在GIS应用程序中完成了很多工作。使用网格有助于划分稀疏算法。

答案 13 :(得分:0)

你可以使用遗传算法。如果将“簇”定义为具有高点密度的矩形子区域,则可以创建一组初始的“解决方案”,每个“解决方案”由一些随机生成的非重叠矩形簇组成。然后,您将编写一个“适应度函数”来评估每个解决方案 - 在这种情况下,您希望适应度函数最小化聚类总数,同时最大化每个聚类中的点密度。

你最初的“解决方案”一切都很糟糕,很可能,但有些可能会比其他解决方案稍微糟糕一些。您使用适应度函数来消除最糟糕的解决方案,然后通过交叉培育上一代的“赢家”来创建下一代解决方案。通过逐代重复这个过程,你应该得到一个或多个解决这个问题的好方法。

为了使遗传算法起作用,问题空间的不同可能解决方案在解决问题的程度方面必须逐渐增加。点簇是完美的。

答案 14 :(得分:0)

Cluster 3.0包含一个用于进行统计聚类的C方法库。它有一些不同的方法,可能会或可能不会解决您的问题,加快您的点群集采取什么形式。该库在here可用,并在Python许可下发布。

答案 15 :(得分:0)

您是否尝试过简单的现成解决方案,例如Accusoft Pegasus的ImagXpress?

可以调整BlobRemoval方法的像素数和密度,以找到打孔,即使它们不连续。 (您也可以尝试使用Dilate函数来缩小间隙)

通过一点点游戏,您可以在绝大多数情况下获得所需的结果,只需很少的代码或科学。

C#:
public void DocumentBlobRemoval(    矩形区域,    int MinPixelCount,    int MaxPixelCount,    简短MinDensity )