【问题标题】:Algorithm for detecting "clusters" of dots [closed]用于检测点“簇”的算法[关闭]
【发布时间】:2010-09-26 05:49:46
【问题描述】:

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

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

【问题讨论】:

  • 有一个很棒的聚类算法教程here,他们讨论了 K-means 和 K-gaussians。

标签: algorithm data-structures image-processing


【解决方案1】:

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

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

编辑:

这里是:

//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);

EDIT 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);
*/

以及带有(减少的)肯特样本的输出:

【讨论】:

  • 是的,对于 n x n 点的正方形,这是 o(n^4),或者对于较低的分辨率 r,o(n^2*((n/r)^2))。可以作为小图像的蛮力方法,但作为一般解决方案不好。
  • 当然我不会用这个来做任何严肃的事情,只是说明一点,如果方法有用,可以在很多方面进行优化。
  • @kent:有趣的部分是根据区域对点进行分组,这样更容易看出确实存在区域。 @smacl:你的评论让我感觉很糟糕,所以我将它更新为不那么难看。
  • @kent:我同意。留下了很酷的图像,但这个过程需要一段时间。
【解决方案2】:

我建议使用 mean-shift kernel 来找到点的密度中心。

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

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

理论上(简而言之):

这个问题的几个答案已经暗示了平均转移方式:

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

均值偏移是一种迭代方法,它使用称为内核的像素邻域(类似于this one)并使用它来计算底层的均值图像数据。在这种情况下,mean 是内核坐标的像素加权平均值。

在每次迭代中,kernel's mean 定义下一次迭代的中心坐标 - 这称为 shift。因此得名 mean-shift。迭代的停止条件是当移动距离下降到 0 时(即我们处于附近最密集的位置)。

mean-shift的全面介绍(理论和应用)可以在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)。

在实践中,诀窍是选择适当的内核大小。内核越小,您就越需要将其启动到集群。内核越大,您的初始位置就越随机。但是,如果图像中有多个点簇,内核可能会在它们之间收敛。

【讨论】:

  • 这听起来很有趣,但是(如果我错了,请纠正我)这种方法不是确定性的,因此在某些情况下是不合适的。写得不错。
  • 谢谢。如果确定性意味着算法总是为给定的输入提供相同的输出,那么它就是确定性的。
  • 不错的答案。但是,如果我错了,请纠正我,这只会在 OP 案例中为您提供“主集群”,他需要在其中获得不同的集群。不会吗?
【解决方案3】:

为了给Trebs 声明添加一点帮助,我认为首先要实际定义集群的定义是很重要的,当然,“点更靠近”,这是相当模糊的。

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

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

人类可能会认为这是一个大的环形星团,但您的自动化程序更可能会认为它是一系列半近距离的较小星团。

另外,请注意,有一些超高密度区域,在大局的背景下,只是分散注意力

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

无论您开发什么,我至少会对它如何识别该集合中的数据感兴趣。

(我认为研究 HDRI ToneMapping 背后的技术可能是正确的,因为这些技术或多或少在光密度上起作用,并且有“局部”色调贴图和“全局”色调贴图,每个都会产生不同的结果)

【讨论】:

  • 请参阅下面关于遗传算法的答案。在这种情况下,如果您提前知道环形集群(或任何不寻常的形状)是可能的,您可以简单地将这种可能性构建到您的解决方案生成机制和适应度函数中。
  • @Kent,如果您使用基于 TIN 的解决方案,您可以按最长边长的数量级对三角形进行分组来解决这个问题。因此,当我们看到一个圆环时,在你的超密集区域可能还有更多其他有趣的形状,值得他们自己分析。 Google 多分辨率 TIN 或 Voronoi。
【解决方案4】:

对 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

“较暗”区域现在可以识别点簇。

【讨论】:

  • 有趣。想知道这种方法是否可以扩展到超过 2 个维度...
  • 当然,但是您的矩阵也呈指数增长。例如3D 矩阵将使用 125 个元素而不是 25 个元素。为了在 2D 上实现与上述类似的效果(我们称之为 M),您将使用 M 其中 z=0 和 z=4,以及 M * 2 其中 z= 1和z=3,以及M * 3,其中z=2。更高的维度也是如此。
【解决方案5】:

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

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

【讨论】:

  • 我喜欢这个。我可以想到一些漂亮的算法来确定当前级别的其他四边形单元格是否属于当前“集群”,但不幸的是这个评论字段太小了......
  • 这是一个真的好主意,只要集群是凸的。
  • 有四叉树实现的链接吗?这会有多快?
【解决方案6】:

形态学方法怎么样?

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

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

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

【讨论】:

    【解决方案7】:

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

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

    R* Trees

    如果这种方法变得难以实施,您最好将数据网格分成大小相等的细分并确定每个细分中是否出现集群;不过,您必须非常注意这种方法的边缘条件。我建议在初始划分之后,您通过定义边缘的某个阈值内的数据点重新组合区域。

    【讨论】:

      【解决方案8】:
      1. 为数据拟合概率密度函数。我将使用“高斯混合”并使用由 K-means 算法启动的期望最大化学习来拟合它。如果没有 EM,K-means 本身有时就足够了。集群数量本身需要使用模型顺序选择算法进行初始化。
      2. 然后,可以使用模型用 p(x) 对每个点进行评分。 IE。获取该点由模型生成的后验概率。
      3. 找到最大 p(x) 以找到聚类质心。

      这可以使用机器学习工具箱在 Matlab 等工具中快速编码。 MoG/EM 学习/K-Means 聚类在网络/标准文本中得到广泛讨论。我最喜欢的文字是 Duda/Hart 的“模式分类”。

      【讨论】:

        【解决方案9】:

        “具有一定高密度的区域”意味着您大约知道每单位面积有多少点您认为高。这导致我采用网格方法,您可以将总区域分成适当大小的子区域,然后计算每个区域中的点数。找到阈值附近的网格区域后,您也可以搜索网格的相邻区域。

        【讨论】:

        • 与此方法相比,四叉树方法的优势在于四叉树不必提前定义单位面积,只需定义您认为“集群”的点数。
        • 我喜欢四叉树方法,我赞成,但单位面积是问题的一部分。一个簇不仅仅是一些点,它是一些彼此靠近的点,也就是在某个区域内。
        【解决方案10】:

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

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

        【讨论】:

        • “tringulate”是特定类型三角测量的专门术语,还是只是一个错字?
        【解决方案11】:

        让我把它整理成一篇研究论文

        一个。问题陈述

        引用Epaga: “我有一个二维区域,在这个区域上分布着‘点’。我现在正在尝试检测点的‘簇’,即具有一定高密度点的区域。”

        请注意,没有任何地方提到这些点来自图像。 (尽管它们可以作为一个订购)。

        b.方法 情况 1:如果点只是点(点 = 2D 空间中的点)。 在这种情况下,您将已经拥有所有点的 x 和 y 位置。问题归结为对点进行聚类之一。 Ivan 在提出解决方案方面做得很好。他还总结了其他类似的答案。 除了他的帖子之外,我的 2cts 是您考虑是否先验地知道集群的数量。算法(可以相应地选择有监督和无监督聚类)。

        案例 2:如果这些点确实来自图像。这里需要澄清问题。让我用这张图片解释一下 如果不区分点的灰度值,则组 1、2、3、4 和 5 都是“不同的簇”。但是,如果根据灰度值进行区分,第 5 类就很棘手,因为点的灰度值不同。

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

        【讨论】:

          【解决方案12】:

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

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

          【讨论】:

          • 那是 n * (n + log(n))!
          • @P 爸爸,如果使用了更快的算法但它没有返回正确的答案,那么它不是你想要使用的。有些问题只是NP完全。就是这样。
          • 这不是其中之一。
          • @P 爸爸。 Treb 的想法可以更快地完成。在“所有最近邻的集合”中查找任何计算几何书籍(例如 Shamos & Preparata、Hoey 或 O'Rourke)。
          • 很公平。我的意思是这个想法很好,只是实现很弱,可以改进。顺便说一句,是你开场白的最后一个字符,意思是阶乘,还是愤怒的迹象;)
          【解决方案13】:

          你可以在你的平面上覆盖一个逻辑网格。如果一个网格包含一定数量的点,则它被认为是“密集的”,然后可以被细化。这在 GIS 应用程序中使用集群容差时会做很多事情。使用网格有助于划分细化算法。

          【讨论】:

            【解决方案14】:

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

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

            要使遗传算法发挥作用,问题空间的不同可能解决方案在解决问题的能力方面必须彼此逐渐不同。点簇非常适合这种情况。

            【讨论】:

              【解决方案15】:

              Cluster 3.0 包含一个用于进行统计聚类的 C 方法库。它有几种不同的方法,可能会或可能不会解决您的问题,具体取决于您的点簇采用什么形式。该库可在此处here 获得,并在 Python 许可下分发。

              【讨论】:

                【解决方案16】:

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

                可以针对像素数和密度调整 BlobRemoval 方法以找到打孔,即使它们不是连续的。 (您也可以尝试使用扩张功能来缩小差距)

                在大多数情况下,只要稍微玩一下,您就可以获得所需的结果,而只需要很少的代码或科学知识。

                C#:
                公共无效 DocumentBlobRemoval( 矩形区域, int MinPixelCount, int MaxPixelCount, 短MinDensity )

                【讨论】:

                  猜你喜欢
                  • 2020-09-17
                  • 1970-01-01
                  • 2014-06-30
                  • 1970-01-01
                  • 2021-05-11
                  • 2018-01-08
                  • 2013-12-12
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多