Como buffer pixels de varredura por seus valores?

28

Os pixels à esquerda representam os locais das árvores e seus raios de coroa associados (ou seja, valores de pixels que variam de 2 a 5). Gostaria de armazenar em buffer esses pixels de varredura pelo valor do raio da coroa. A imagem à direita é o que espero obter usando apenas métodos de processamento de varredura .

Inicialmente, eu pensaria em usar uma soma focal circular no ArcGIS, embora a configuração da vizinhança seja um valor fixo, o que não levaria em consideração o raio da coroa de tamanho variável.

Qual é um bom método para "buffer" de pixels por seus valores?

insira a descrição da imagem aqui

Aaron
fonte
2
Você tentou converter raster em pontos, em seguida, armazenar em buffer por campo e depois converter novamente em raster?
2
Isso ajuda a perceber que essa é uma operação não local , porque essa não localidade mostra que existem limites inerentes à forma como o cálculo pode ser realizado. Por exemplo, sua saída mudaria radicalmente em quase todos os lugares se apenas um pixel isolado na entrada mudasse para um valor grande. Portanto, se você conhece restrições sobre os valores de entrada, compartilhe-os, pois isso pode levar a soluções aprimoradas. Por exemplo, todos os seus valores de entrada sempre estarão no conjunto {2,3,4}?
whuber
@ Dan Patterson Foi assim que surgiu a imagem à direita. No entanto, estou tentando evitar completamente as operações com vetores e essas etapas.
Aaron
2
@whuber Este conjunto de dados representa árvores com diâmetros de copa variáveis. Dado isso, as medições do raio da copa da árvore podem variar realisticamente de 1 a 10. Devo acrescentar também que a saída em buffer precisa apenas de 0 para ausência de coroa e 1 para presença de coroa.
Aaron
1
OK obrigado. Parece que você criou o exemplo de saída ao unir os 3 buffers dos pontos com o valor 3, os 4 buffers dos pontos com o valor 4 e os 5 buffers dos pontos com o valor 5. (Você parece ter esquecido para processar os pontos com o valor 2.) Esse processo não apenas responde à sua pergunta, mas (acredito), é a solução mais simples usando as ferramentas disponíveis no Spatial Analyst.
whuber

Respostas:

14

Aqui está uma solução pura de varredura no Python 2.7uso numpye scipy:

import numpy as np
from scipy import ndimage
import matplotlib.pyplot as plt

#create tree location matrix with values indicating crown radius
A = np.zeros((120,320))
A[60,40] = 1
A[60,80] = 2
A[60,120] = 3
A[60,160] = 4
A[60,200] = 5
A[60,240] = 6
A[60,280] = 7

#plot tree locations
fig = plt.figure()
plt.imshow(A, interpolation='none')
plt.colorbar()

#find unique values
unique_vals = np.unique(A)
unique_vals = unique_vals[unique_vals > 0]

# create circular kernel
def createKernel(radius):
    kernel = np.zeros((2*radius+1, 2*radius+1))
    y,x = np.ogrid[-radius:radius+1, -radius:radius+1]
    mask = x**2 + y**2 <= radius**2
    kernel[mask] = 1
    return kernel

#apply binary dilation sequentially to each unique crown radius value 
C = np.zeros(A.shape).astype(bool)   
for k, radius in enumerate(unique_vals):  
    B = ndimage.morphology.binary_dilation(A == unique_vals[k], structure=createKernel(radius))
    C = C | B #combine masks

#plot resulting mask   
fig = plt.figure()
plt.imshow(C, interpolation='none')
plt.show()

Entrada: insira a descrição da imagem aqui

Saída: insira a descrição da imagem aqui

jatobat
fonte
1
+1 para a abordagem de dilatação! Também funciona com pontos próximos.
Antonio Falciano
Este é um ótimo exemplo de por que esse antigo esquema de cores a jato era terrível. Isso parece muito mais claro com os viridis.
naught101
8

Abordagem baseada em vetor

Esta tarefa pode ser realizada em três etapas:

Nota: o uso do campo buffer evita o cálculo de um buffer para cada valor do raio da coroa.


Abordagem baseada em varredura

Evitando a solução baseada em vetores, esse problema sugere o uso de um tipo de autômato celular baseado nos vizinhos mais próximos. Supondo que todos os pixels pretos sejam zeros, os pixels são quadrados e seu tamanho é igual a 1 (ou, como alternativa, são dimensionados oportunamente), as regras a serem adotadas são muito simples:

  1. Se o valor do pixel ( VALUE) for maior que 1, seu valor se tornará VALUE-1e, em seguida, considere os pixels adjacentes. Se seus valores forem menores que VALUE-1, esses pixels nascem ou crescem e seu valor se torna VALUE-1. Caso contrário, esses pixels sobreviverão e permanecerão inalterados.
  2. Se VALUE<=1não fizer nada (o pixel está morto!).

Essas regras devem ser aplicadas até que todos os pixels estejam mortos, ou seja, seus valores são iguais a 0 ou 1. Então N-1, vezes, onde Né o valor máximo que você tem na varredura de entrada. Essa abordagem pode ser facilmente implementada com um pouco de Python e numpy.

Antonio Falciano
fonte
1
Obrigado pela resposta afalciano. Este método é como eu criei a imagem à direita e use uma abordagem vetorial - uma que estou tentando evitar.
Aaron
1
Ok Aaron, aqui está uma abordagem baseada em varredura agora. Espero que isto ajude.
Antonio Falciano
7

Outra opção seria criar separadores separados para cada valor de pixel, neste caso 4 separadores, com uma condição. Em seguida, expanda os rasters por uma contagem de pixels correspondente ao valor da varredura (possivelmente repetindo uma lista de valores). Por fim, junte os rasters (algébricos ou espaciais), para criar uma varredura binária para as copas das árvores.

HDunn
fonte
1
Essa ideia é a certa. Os detalhes podem ser aprimorados: (1) a seleção cria um indicador binário (0,1) das árvores de um determinado raio da coroa. (2) Uma soma focal dessa seleção - usando uma vizinhança circular do raio fornecido - é rápida de calcular usando a FFT. (3) A adição das somas focais (ponto a ponto) e a comparação com 0 fornecem o buffer desejado.
whuber
7

É uma pergunta desafiadora fazer isso em varredura, porque você não tem a oportunidade de usar o valor do pixel para definir o tamanho do buffer. Portanto, você precisaria fazer o filtro focal para cada valor, como você já disse.

Aqui está uma resposta possível para fazer isso com apenas 3 filtros (não consegui encontrar menos), mas não perfeitamente como mencionado por Whuber: seus buffers serão truncados quando as árvores estiverem próximas umas das outras.

1) EDIT: Alocação euclidiana (isso não resolve completamente o problema, pois corta os buffers nas proximidades de árvores menores, mas é melhor que os artefatos da minha primeira solução).

2) distância euclidiana em torno de cada pixel

3) calculadora raster (álgebra de mapa) com uma declaração condicional

Con("allocation_result" > ("distance_result" / pixel_size) , 1 , 0)

Observe que você pode ajustar a condição dependendo das suas necessidades em termos de raio (com ou sem o pixel central)

radouxju
fonte
+1 Esta é uma abordagem criativa. Vou testar para ver se é possível aumentar a escala usando essa abordagem.
Aaron
2
A abordagem da distância euclidiana não funcionará, porque calcula apenas a distância da árvore mais próxima , que não é necessariamente a distância de uma árvore cuja coroa cobre o ponto.
whuber
2

Pergunto por que você não usa a ferramenta de expansão do ArcGIS ?

import arcpy
from arcpy.sa import *

raster_in  = r'c:\test.tif'
raster_out = r'c:\test_out.tif'

outExpand1 = Expand(raster_in, 2, 2)
outExpand2 = Expand(outExpand1, 3, 3)
outExpand3 = Expand(outExpand3, 4, 4)
outExpand4 = Expand(outExpand4, 5, 5)

outExpand4.save(raster_out)

Em caso de sobreposição: o último expandcomando cobrirá os anteriores.

Mr. Che
fonte
2

Se você tiver a posição do pixel, o raio e o algoritmo do círculo do ponto médio (uma variante do algoritmo Bresenham) fornecem uma pista. Na IMO, é fácil criar um polígono a partir dessa abordagem e acho fácil implementá-lo no Python. A união desse conjunto de polígonos fornece a área de cobertura.

huckfinn
fonte
Sei que não é uma questão de pergunta, mas você quer saber mais sobre primitivas gráficas e preenchimento de polígono de linha de varredura? Para cirles é muito fácil. Combinação convexa é um chavão e assim por diante ....
huckfinn
Como isso seria aplicado usando operações básicas de varredura?
whuber
Se você tentar lidar com isso no espaço raster, determine os pontos do círculo, classificando-os por y ou x e preencha o espaço por uma linha reta (linha de varredura), a caminho de preencher o círculo. Na abordagem triangular, se você construir o círculo por uma aproximação de setores tringulares e tentar preencher o triângulo, precisará de um teste se o ponto está dentro ou fora (combinação convexa) e é o contrário. E na abordagem "GIS", a construção de polígonos (polígonos orientados a relógio) e a união é a terceira (OMI é a mais cara e computacional).
21714 Huckfinn
Para ser claro: e na abordagem "GIS" ... faça uma operação algebrárica como união, interseção, toque ... é a terceira IMO a mais cara e computacional.
huckfinn