Определение зон перекрытия в растровом пакете R - PullRequest
8 голосов
/ 14 апреля 2011

Пакет:

Данные:

  • Растровый стек с 10 полосами.
  • Каждая из полос содержит область изображения, окруженную NA
  • Полосы являются логическими, то есть «1» для данных изображения и «0» / NA для окружающей области
  • «Области изображения»«каждой полосы не полностью совпадают друг с другом, хотя большинство из них имеют частичные перекрытия

Цель:

  • Написать быструю функцию, которая может возвращать либо rasterLayer, либо номера ячеекдля каждой «зоны», например, пиксель, содержащий данные только из полос 1 и 2, попадает в зону 1, пиксель, содержащий данные только из полос 3 и 4, попадает в зону 2 и т. д. Если возвращается rasterLayer, мне нужновозможность сопоставить значение зоны с номерами каналов позже.

Первая попытка:

# Possible band combinations
values = integer(0)
for(i in 1:nlayers(myraster)){
 combs = combn(1:nlayers(myraster), i)
 for(j in 1:ncol(combs)){
  values = c(values, list(combs[,j]))
 }
}

# Define the zone finding function
find_zones = function(bands){

 # The intersection of the bands of interest
 a = subset(myraster, 1)
 values(a) = TRUE
 for(i in bands){
  a = a & myraster[[i]]
 }

 # Union of the remaining bands
 b = subset(myraster, 1)
 values(b) = FALSE
 for(i in seq(1:nlayers(myraster))[-bands]){
  b = b | myraster[[i]]
 }

 #plot(a & !b)
 cells = Which(a & !b, cells=TRUE)
 return(cells)
}

# Applying the function
results = lapply(values, find_zones)

Моя текущая функция выполняется очень долго.Можете ли вы придумать лучший способ?Обратите внимание, что я не просто хочу знать, сколько полос имеет данные в каждом пикселе, мне также нужно знать, какие полосы.Целью этого является обработка различных областей по-разному впоследствии.

Обратите также внимание, что реальный сценарий представляет собой растр 3000 x 3000 или более с потенциально более чем 10 полосами.


РЕДАКТИРОВАТЬ

Некоторые примеры данных, состоящие из10 областей смещения изображения:

# Sample data
library(raster)    
for(i in 1:10) {
  start_line = i*10*1000
  end_line = 1000000 - 800*1000 - start_line
  offset = i * 10
  data = c(rep(0,start_line), rep(c(rep(0,offset), rep(1,800), rep(0,200-offset)), 800), rep(0, end_line))
  current_layer = raster(nrows=1000, ncols=1000)
  values(current_layer) = data
  if(i == 1) {
    myraster = stack(current_layer)
  } else {
    myraster = addLayer(myraster, current_layer)
  }
}
NAvalue(myraster) = 0  # You may not want to do this depending on your solution...

Showing what the sample data looks like

Ответы [ 4 ]

6 голосов
/ 18 апреля 2011

РЕДАКТИРОВАТЬ: Ответ обновлен с использованием трюка Ника и умножения матриц.


Вы можете попробовать следующую функцию, оптимизированную с помощью трюка Ника и умножения матриц. Узкое место теперь заполняет стопку отдельными слоями, но я думаю, что сейчас все в порядке. Использование памяти немного меньше, но, учитывая ваши данные и природу R, я не знаю, можете ли вы откусить немного, не снижая при этом производительность.

> system.time(T1 <- FindBands(myraster,return.stack=T))
   user  system elapsed 
   6.32    2.17    8.48 
> system.time(T2 <- FindBands(myraster,return.stack=F))
   user  system elapsed 
   1.58    0.02    1.59 
> system.time(results <- lapply(values, find_zones))
  Timing stopped at: 182.27 35.13 217.71

Функция возвращает либо rasterStack с различными комбинациями уровней, присутствующими на графике (это не все возможные комбинации уровней, поэтому у вас уже есть какой-то выигрыш), либо матрицу с номером уровня и именами уровней. Это позволяет вам сделать что-то вроде:

levelnames <- attr(T2,"levels")[T2]

, чтобы получить имена уровней для каждой точки ячейки. Как показано ниже, вы можете легко поместить эту матрицу в объект rasterLayer.

Функция:

 FindBands <- function(x,return.stack=F){
    dims <- dim(x)
    Values <- getValues(x)
    nn <- colnames(Values)

    vec <- 2^((1:dims[3])-1)
    #Get all combinations and the names
    id <- unlist(
                lapply(1:10,function(x) combn(1:10,x,simplify=F))
              ,recursive=F)

    nameid <- sapply(id,function(i){
      x <- sum(vec[i])
      names(x) <- paste(i,collapse="-")
      x
    })
    # Nicks approach
    layers <- Values %*% vec
    # Find out which levels we need
    LayerLevels <- unique(sort(layers))
    LayerNames <- c("No Layer",names(nameid[nameid %in% LayerLevels]))

    if(return.stack){
        myStack <- lapply(LayerLevels,function(i){
          r <- raster(nr=dims[1],nc=dims[2])
          r[] <- as.numeric(layers == i)
          r
          } )
        myStack <- stack(myStack)
        layerNames(myStack) <- LayerNames
        return(myStack)

    } else {

      LayerNumber <- match(layers,LayerLevels)
      LayerNumber <- matrix(LayerNumber,ncol=dims[2],byrow=T)
      attr(LayerNumber,"levels") <- LayerNames
      return(LayerNumber)
    }    
}

Подтверждение концепции, используя данные RobertH:

r <- raster(nr=10, nc=10)
r[]=0
r[c(20:60,90:93)] <- 1
s <- list(r)
r[]=0
r[c(40:70,93:98)] <- 1
s <- c(s, r)
r[]=0
r[50:95] <- 1
s <- (c(s, r))
aRaster <- stack(s)


> X <- FindBands(aRaster,return.stack=T)
> plot(X)

enter image description here

> X <- FindBands(aRaster,return.stack=F)
> X
      [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
 [1,]    1    1    1    1    1    1    1    1    1     1
 [2,]    1    1    1    1    1    1    1    1    1     2
 [3,]    2    2    2    2    2    2    2    2    2     2
 [4,]    2    2    2    2    2    2    2    2    2     4
 [5,]    4    4    4    4    4    4    4    4    4     8
 [6,]    8    8    8    8    8    8    8    8    8     8
 [7,]    7    7    7    7    7    7    7    7    7     7
 [8,]    5    5    5    5    5    5    5    5    5     5
 [9,]    5    5    5    5    5    5    5    5    5     6
[10,]    6    6    8    7    7    3    3    3    1     1
attr(,"levels")
[1] "No Layer" "1"        "2"        "3"        "1-2"      "1-3"
       "2-3"      "1-2-3"   

> XX <- raster(ncol=10,nrow=10)
> XX[] <- X
> plot(XX)

enter image description here

3 голосов
/ 18 апреля 2011

Я не знаком с растром, но из того, что я понял из вышесказанного, у вас, по сути, есть массив 10 *3000* 3000, верно?

Если так, для каждой позиции в растре (второйи третьи индексы, currow и curcol), вы можете вычислить уникальный идентификатор для его «зоны», используя двоичный файл: выполните i по «полосам» (первый индекс) и суммируйте r [i, currow, curcol] * 2 ^ (i-1).В зависимости от внутренней работы растра, это может быть возможно довольно быстро для реализации этого.

В результате получается новый «растр» размером 3000 * 3000, содержащий уникальные идентификаторы каждой позиции.Поиск там уникальных значений возвращает вам зоны, которые фактически присутствуют в ваших данных, а обращение бинарной логики должно дать вам полосы, принадлежащие данной зоне.

Извините, если моя интерпретация растра неверна: тогда, пожалуйста, игнорируйте мои размышления.В любом случае не полное решение.

3 голосов
/ 18 апреля 2011

Как насчет этого?

library(raster)
#setting up some data

r <- raster(nr=10, nc=10)
r[]=0
r[c(20:60,90:93)] <- 1
s <- list(r)
r[]=0
r[c(40:70,93:98)] <- 1
s <- c(s, r)
r[]=0
r[50:95] <- 1
s <- (c(s, r))
plot(stack(s))

# write a vectorized function that classifies the data
# 
fun=function(x,y,z)cbind(x+y+z==0, x==1&y+z==0, y==1&x+z==0, z==1&x+y==0, x==0&y+z==2, y==0&x+z==2, z==0&x+y==2,x+y+z==3)

z <- overlay(s[[1]], s[[2]], s[[3]], fun=fun)
# equivalent to
#s <- stack(s)
#z <- overlay(s[[1]], s[[2]], s[[3]], fun=fun)

ln <- c("x+y+z==0", "x==1&y+z==0", "y==1&x+z==0", "z==1&x+y==0", "x==0&y+z==2", "y==0&x+z==2", "z==0&x+y==2", "x+y+z==3")
layerNames(z) <- ln
x11()
plot(z)

более общий:

s <- stack(s)
fun=function(x)as.numeric(paste(which(x==1), collapse=""))
x <- calc(s,fun)

это не хорошо, когда nlayers (s) имеют двойные цифры («1», «2» - то же самое, что и «12»), и в этих случаях вы могли бы использовать функцию ниже (fun2) вместо:

fun2=function(x)as.numeric(paste(c(9, x), collapse=""))
x2 <- calc(s,fun2)

unique(x)
# [1]   1   2   3  12  13  23 123

unique(x2)
# [1] 9000 9001 9010 9011 9100 9101 9110 9111

только для игрушечного примера:

plot(x)
text(x)
p=rasterToPolygons(x)
plot(p, add=T)
1 голос
/ 19 апреля 2011

Я написал код для предложения @Nick Sabbe, который я считаю очень сжатым и относительно быстрым. Это предполагает, что входной rasterStack уже имеет логические данные 1 или 0:

# Set the channels to 2^i instead of 1
bands = nlayers(myraster)
a = stack()
for (i in 1:bands) {
  a = addLayer(a, myraster[[i]] * 2^i)
}
coded = sum(a)
#plot(coded)
values = unique(coded)[-1]
remove(a, myraster)

# Function to retrieve which coded value means which channels
which_bands = function(value) {
  single = numeric()
  for (i in bands:1) {
    if ((0 < value) & (value >= 2^i)) {
     value = value - 2^i
      single = c(single, i)
    }
  }
  return(single)
}
...