R中kmeans的无监督分类


10

我有一个按时间顺序排列的卫星图像(5个波段),并想按R中的kmeans对其进行分类。我的脚本运行良好(循环浏览我的图像,将图像转换为data.frame,将它们聚类,然后将其转换回一个栅格):

for (n in files) {
image <- stack(n)    
image <- clip(image,subset)

###classify raster
image.df <- as.data.frame(image)  
cluster.image <- kmeans(na.omit(image.df), 10, iter.max = 10, nstart = 25) ### kmeans, with 10 clusters

#add back NAs using the NAs in band 1 (identic NA positions in all bands), see http://stackoverflow.com/questions/12006366/add-back-nas-after-removing-them/12006502#12006502
image.df.factor <- rep(NA, length(image.df[,1]))
image.df.factor[!is.na(image.df[,1])] <- cluster.image$cluster

#create raster output
clusters <- raster(image)   ## create an empty raster with same extent than "image"  
clusters <- setValues(clusters, image.df.factor) ## fill the empty raster with the class results  
plot(clusters)
}

我的问题是:我无法将分类结果相互比较,因为群集分配器因图像而异。例如,“水”在第一个图像簇编号1中,在下一个图像簇编号2中以及在第三个图像簇中,因此无法比较日期之间的水结果。

如何解决群集分配?

是否可以为所有图像指定一个固定的起点(希望总是先检测到水,然后分类为1)?

如果是的话,怎么办?

Answers:


6

我认为您不能...首先必须标记每个类以进行比较。Kmean无监督地进行分类,因此没有任何先验信息,因此无法定义任何种类的类。

如果您有参考图层,则可以通过多数投票进行标记。与使用“ raster”包功能相比,这是用于多数表决的更有效的代码zonal

require (data.table)
fun <- match.fun(modal)
vals <- getValues(ref) 
zones <- round(getValues(class_file), digits = 0) 
rDT <- data.table(vals, z=zones) 
setkey(rDT, z) 
zr<-rDT[, lapply(.SD, modal,na.rm=T), by=z]

ref栅格类参考文件在哪里,class_file是kmeans结果。

zr 在第一栏为您提供“区域”编号,在第二栏为您提供课程标签。


我担心这是不可能的。感谢您提供多数表决代码!
Iris

4

要在图像堆栈上实现群集,您不必按波段进行,而是同时在整个图像堆栈上进行。否则,如@nmatton所指出的,该统计没有太大意义。

但是,我不同意这是不可能的,只是占用大量内存。在真实的卫星数据上,这将是一个巨大的问题,对于高分辨率数据可能是不可能的,但是您可以通过将栅格强制为一个可以传递给聚类函数的对象来在内存中进行处理。您将需要跟踪整个栅格中的NA值,因为在聚类期间将删除它们,并且您需要知道栅格中的位置,以便可以将聚类值分配给正确的像元。

我们可以在这里通过一种方法。让我们添加所需的库和一些示例数据(RGB R徽标使我们可以使用3个频段)。

library(raster)
library(cluster)
r <- stack(system.file("external/rlogo.grd", package="raster")) 
  plot(r)

首先,我们可以使用getValues将多波段栅格堆栈对象强制为data.frame。请注意,我在第1行第3列添加了NA值,因此我可以说明如何不处理数据。

r.vals <- getValues(r[[1:3]])
  r.vals[1,][3] <- NA

在这里,我们可以开始工作,并创建将用于分配聚类结果的非NA值的单元格索引。

idx <- 1:ncell(r)
idx <- idx[-unique(which(is.na(r.vals), arr.ind=TRUE)[,1])]  

现在,我们从3波段RGB值(k = 4)创建一个群集对象。我正在使用clara K-Medoids方法,因为它适用于大数据,适用于奇数分布。它与K-Means非常相似。

clus <- cluster::clara(na.omit(scale(r.vals)), k=4)

为简单起见,我们可以通过从原始栅格堆栈对象中拉出其中一个栅格带并为其分配NA值来创建空栅格。

r.clust <- r[[1]]
r.clust[] <- NA

最后,使用索引,将聚类值分配给空栅格中的适当像元并绘制结果。

r.clust[idx] <- clus$clustering
plot(r.clust) 

对于庞大的栅格,您可能需要研究bigmemory软件包,该软件包将矩阵写入磁盘并在块上进行操作,并且提供了k均值函数。另外,请记住,这不完全是R设计的目的,并且图像处理或GIS软件可能更合适。我知道SAGA和Orfeo工具箱都是免费软件,具有可用于图像堆栈的k均值聚类。甚至还有一个RSAGA库,允许从R调用该软件。


如果所有图像都一次堆叠并聚类,那么结果就是一个聚类图像,对吗?
虹膜

@Iris,是的,这就是这种图像聚类的工作方式,并且遵循遥感软件中的实现。一个明显的和有关的例子是在ArcGIS(在isocluster实施desktop.arcgis.com/en/arcmap/10.3/tools/spatial-analyst-toolbox/...
杰弗里·埃文斯

然后,这完全没有帮助。我的问题是,我尝试根据几种无监督的图像分类随时间进行更改检测,但由于类别分配不同,因此我可以比较不同的结果。
虹膜

无监督分类不是执行更改检测的可行方法。给定图像中的微小变化最终都可能导致像素被分配到不同的类别中。即使您为K-Means提供了群集中心,情况也是如此。我在spatialEco软件包中有一个熵函数,可用于更改检测。您可以在NxN窗口中计算熵,然后在每个时间步长导出增量。负熵表示损失,正熵表示在最大熵下给定大小内景观分量的增益。
杰弗里·埃文斯

这是一个老问题,我早就放弃了使用k-means的想法。但是很高兴下次知道spacespaceEco软件包;)
Iris
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.