我有一个2D区域,在这个区域上分布着“点”。我现在正在尝试检测点的“簇”,即具有特定高密度点的区域。
有没有关于如何优雅地检测这些区域的想法(或者链接到文章的链接)?
发布于 2008-12-10 13:40:44
如何为你的空间定义一个任意的分辨率,并为矩阵中的每个点计算从该点到所有点的距离,然后你可以制作一个“热图”,并使用一个阈值来定义集群。
这是一个很好的处理练习,也许稍后我会发布一个解决方案。
编辑:
这就是它:
//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);
*/以及使用(简化的) Kent样本的输出:
发布于 2009-01-05 18:28:05
我建议使用mean-shift内核来找到点的密度中心。
Mean-shift illustration http://cvr.yorku.ca/members/gradstudents/kosta/compvis/file_mean_shift_path.gif
该图显示了mean-shift内核(最初以集群边缘为中心)向集群的最高密度点收敛。
理论上(简而言之):
这个问题的几个答案已经暗示了mean-shift的方法:
您在动画图形中看到的是这两个建议的组合:它使用移动的“块”(即内核)来寻求局部最高密度。
均值偏移是一种迭代方法,它使用称为内核的像素邻域(类似于this one),并使用它来计算底层图像数据的平均值。在此上下文中的平均值是内核坐标的像素加权平均值。
在每次迭代中,内核的平均值定义了下一次迭代的中心坐标-这称为移位。因此得名mean-shift。迭代的停止条件是当移位距离降为0时(即,我们处于邻域中最密集的点)。
有关均值漂移的全面介绍(包括理论和应用),请参阅this ppt presentation.
在实践中:
在OpenCV中提供了mean-shift的实现
int cvMeanShift( const CvArr* prob_image, CvRect window,
CvTermCriteria criteria, CvConnectedComp* comp );O'Reilly's Learning OpenCv (google book excerpts)对它的工作原理也有一个很好的解释。基本上就是给它提供你的点图像(prob_image)。
在实践中,诀窍是选择适当的内核大小。内核越小,就越需要将其初始化到集群中。内核越大,初始位置的随机性就越大。但是,如果您的图像中有几个点的群集,内核可能会在它们之间收敛。
发布于 2008-12-10 13:56:31
为了给Trebs语句增加一点帮助,我认为重要的是首先实际地定义集群的定义是什么,当然,“更紧密的点在一起”,这是相当有意义的。
以我生成的样本集为例,我知道那里有一个簇形状,我创建了它。
然而,以编程方式识别这个“集群”可能很困难。
人类可能会认为这是一个大的环形星团,但你的自动程序更有可能决定它是一系列半近距离的较小星团。
此外,请注意,有一些超高密度的区域,这些区域处于更大的背景下,只是分散了注意力。
您将需要考虑此行为,并可能将类似密度的集群链接在一起,仅由较低密度的无关紧要的空洞分隔,这取决于特定的应用程序。
无论您开发什么,我至少会对它如何识别这个集合中的数据感兴趣。
(我认为研究HDRI ToneMapping背后的技术可能是合适的,因为这些技术或多或少地作用于光密度,并且有“局部”色调映射和“全局”色调映射,每个色调映射产生不同的结果)
https://stackoverflow.com/questions/356035
复制相似问题