我有一个分布在二维区域上的“点”。我现在正在尝试检测“簇”(clusters),也就是具有一定高密度点的区域。
您有什么关于如何优雅地检测这些区域的想法(或者链接到有关想法的文章)?
我有一个分布在二维区域上的“点”。我现在正在尝试检测“簇”(clusters),也就是具有一定高密度点的区域。
您有什么关于如何优雅地检测这些区域的想法(或者链接到有关想法的文章)?
如何为您的空间定义一个任意分辨率,然后计算矩阵中每个点到所有点的距离度量,从而可以制作“热图”并使用阈值定义聚类。
这是一个很好的处理练习,也许以后我会发布解决方案。
编辑:
这就是解决方案:
//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样本的输出:
已经有几个回答暗示了使用均值漂移的方法:
j0rd4n 和 Bill the Lizard 都建议将空间离散成块并检查它们的密度。
你在动画中看到的是这两个建议的结合:它使用一个移动的“块”(即核)来寻找局部最高密度。
均值漂移是一种迭代方法,使用称为核的像素邻域并使用它来计算底层图像数据的平均值。在这种情况下,平均值是核坐标的像素加权平均值。int cvMeanShift( const CvArr* prob_image, CvRect window,
CvTermCriteria criteria, CvConnectedComp* comp );
O'Reilly's Learning OpenCv (google book excerpts)还对其工作原理进行了很好的解释。基本上只需将您的点图像(prob_image)输入即可。
实际上,诀窍在于选择适当的卷积核大小。卷积核越小,您需要将其初始化得越接近聚类。卷积核越大,您的初始位置可以更随意。但是,如果您的图像中有几个点簇,则卷积核可能会在它们之间收敛。
对您的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
“较暗”的区域现在标识出点的聚类。
这可以在像Matlab这样的工具中快速编写,使用机器学习工具箱。 MoG / EM学习/ K-Means聚类在Web /标准文本上广泛讨论。我的最爱是Duda / Hart的“模式分类”。
这听起来像是一道学术问题。
我想到的解决方案涉及到R*树。这将您的总面积划分为单独大小和可能重叠的盒子。在这样做之后,您可以通过计算平均距离来确定每个盒子是否代表了一个“聚类”。
如果这种方法难以实现,您最好将数据网格分成相等大小的子网格,并确定每个子网格中是否发生了聚类;但是您需要非常注意边缘条件。我建议在初始划分之后,您可以重新组合具有特定阈值内定义边缘上数据点的区域。