代码之家  ›  专栏  ›  技术社区  ›  Benjamin Cintix

rgdal高效读取大型多波段光栅

  •  2
  • Benjamin Cintix  · 技术社区  · 14 年前

    我正在使用rgdal包在R中编写一个图像分类脚本。所讨论的光栅是一个PCIDSK文件,有28个通道:一个训练数据通道、一个验证数据通道和26个光谱数据通道。目标是填充一个数据帧,该数据帧包含训练数据信道中不为零的每个像素的值,以及26个频带中的相关光谱值。

    在Python/Numpy中,我可以轻松地将整个图像的所有波段导入到多维数组中,但是,由于内存限制,R中的唯一选项似乎是逐块导入此数据,这非常缓慢:

    library(rgdal)
    
    raster = "image.pix"
    training_band = 2
    validation_band = 1
    BlockWidth = 500
    BlockHeight = 500
    
    # Get some metadata about the whole raster
    myinfo = GDALinfo(raster)
    ysize = myinfo[[1]]
    xsize = myinfo[[2]]
    numbands = myinfo[[3]]
    
    
    # Iterate through the image in blocks and retrieve the training data
    column = 0
    training_data = NULL
    while(column < xsize){
     if(column + BlockWidth > xsize){
      BlockWidth = xsize - column
     }
     row = 0
     while(row < ysize){
      if(row + BlockHeight > ysize){
       BlockHeight = ysize - row
      }
    
      # Do stuff here
      myblock = readGDAL(raster, region.dim = c(BlockHeight,BlockWidth), offset = c(row, column), band = c(training_band,3:numbands), silent = TRUE)
      blockdata = matrix(NA, dim(myblock)[1], dim(myblock)[2])
      for(i in 1:(dim(myblock)[2])){
       bandname = paste("myblock", names(myblock)[i], sep="$")
       blockdata[,i]= as.matrix(eval(parse(text=bandname)))
      }
      blockdata = as.data.frame(blockdata)
      blockdata = subset(blockdata, blockdata[,1] > 0)
      if (dim(blockdata)[1] > 0){
       training_data = rbind(training_data, blockdata)
      }
    
      row = row + BlockHeight
     }
     column = column + BlockWidth
    }
    remove(blockdata, myblock, BlockHeight, BlockWidth, row, column)
    

    有没有更快/更好的方法在不耗尽内存的情况下执行相同的操作?

    收集这些训练数据后的下一步是创建分类器(randomForest包),该分类器还需要大量内存,具体取决于请求的树的数量。这就引出了我的第二个问题,即考虑到训练数据已经占用的内存量,创建一个500棵树的森林是不可能的:

    myformula = formula(paste("as.factor(V1) ~ V3:V", dim(training_data)[2], sep=""))
    r_tree = randomForest(formula = myformula, data = training_data, ntree = 500, keep.forest=TRUE)
    

    有没有办法分配更多的内存?我遗漏了什么吗?谢谢。。。

    [编辑] 正如Jan所建议的,使用“光栅”软件包要快得多;但是据我所知,就收集训练数据而言,它并不能解决内存问题,因为它最终需要在一个数据帧中,在内存中:

    library(raster)
    library(randomForest)
    
    # Set some user variables
    fn = "image.pix"
    training_band = 2
    validation_band = 1
    
    # Get the training data
    myraster = stack(fn)
    training_class = subset(myraster, training_band)
    training_class[training_class == 0] = NA
    training_class = Which(training_class != 0, cells=TRUE)
    training_data = extract(myraster, training_class)
    training_data = as.data.frame(training_data)
    

    因此,虽然这要快得多(需要的代码也更少),但它仍然不能解决没有足够的空闲内存来创建分类器的问题。。。是否有一些“光栅”包功能,我没有发现,可以做到这一点?谢谢。。。

    2 回复  |  直到 14 年前
        1
  •  3
  •   Janvb    14 年前

    查看光栅软件包。光栅包为Rgdal提供了一个方便的包装器,而无需将其加载到内存中。

    http://raster.r-forge.r-project.org/

    希望能帮上忙。

    “光栅”软件包处理基本的 空间光栅(网格)数据访问和 操纵。它定义了光栅 类;可以处理非常大的 文件(存储在磁盘上);包括 标准光栅函数,如 覆盖、聚合和合并。

    “光栅”软件包的目的是 提供易于使用的功能 光栅操作和分析。 其中包括高级功能 例如叠加、合并、聚合, 投影,重采样,距离, 多边形到光栅的转换。全部 这些功能的作用非常大 无法加载的光栅数据集 进入记忆。另外,包裹 提供较低级别的功能,如 逐行读写 许多格式通过rgdal)用于构建 其他功能。

    通过使用光栅软件包,可以避免在使用randomForest之前填满内存。

    [编辑]要解决random forest的内存问题,如果您可以在子样本(大小为<<n)而不是引导样本(大小为n)上学习randomForest中的各个树,可能会有所帮助。

        2
  •  0
  •   mdsumner    14 年前

    我认为这里的关键是:“一个包含每个像素值的数据帧,它在训练数据通道中不为零”。如果生成的data.frame小到可以保存在内存中,则可以通过仅读取该条带,然后仅修剪到那些非零值,然后尝试创建一个包含所需行数和列总数的data.frame。

    你能管理这个吗?

     training_band = 2 
    
     df = readGDAL("image.pix",  band = training_band) 
     df = as.data.frame(df[!df[,1] == 0, ])
    

    然后,您可以通过分别读取每个波段并修剪训练波段来逐个填充data.frame的列。

    如果data.frame太大,那么您就卡住了-我不知道randomForest是否可以使用“ff”中的内存映射数据对象,但这可能值得尝试。

    编辑:一些示例代码,请注意,光栅为您提供内存映射访问,但问题是randomForest是否可以使用内存映射数据结构。您可能只能读入所需的数据,一次只能读入一个波段—您可能希望首先构建完整的data.frame,而不是附加列。

    另外,如果您可以从一开始就生成完整的data.frame,那么您将知道它是否应该工作。通过rbind()作为代码遍历,您需要越来越大的连续内存块,这可能是可以避免的。