segmentação de imagem da imagem RGB por K significa agrupamento em python

8

Desejo segmentar imagens RGB para cobertura do solo usando k significa agrupar de forma que as diferentes regiões da imagem sejam marcadas por cores diferentes e, se possível, limites sejam criados separando regiões diferentes. Eu quero algo como:

insira a descrição da imagem aqui

disso:

insira a descrição da imagem aqui

É possível conseguir isso por meio do cluster K-means? Eu tenho pesquisado por toda a Internet e muitos tutoriais fazem isso por k, agrupando, mas somente depois de converter a imagem em escala de cinza. Quero fazê-lo apenas com uma imagem RGB. Existe alguma fonte que possa me ajudar a começar com isso? Por favor, sugira algo.

rach
fonte
Olá, tente este link. Eu tentei há algum tempo, mas só tive sucesso limitado. Talvez você consiga que funcione um pouco melhor. Boa sorte. opencv-python-tutroals.readthedocs.org/en/latest/py_tutorials/...
Jcstay
Olá, obrigado pela sua sugestão @Jcstay, mas eu já tentei o link e não ajudou. Obrigado mesmo assim.
Rach1 de
3
Eu apontaria que o algoritmo K-means, como todos os outros métodos de agrupamento, necessidades e ajuste ideal de k. Como tudo nos dados de referência receberá uma classe atribuída, se k não for otimizado, os resultados poderão ser errados, sem suporte para a classe resultante. Nesses casos, uma determinada classe pode representar nada além de ruído ou efeito marginal nos dados. Geralmente, os valores da silhueta da margem são usados ​​para selecionar um k ideal.
111315 Jeffrey Evans

Respostas:

9

Eu criei uma solução para isso e escrevi um artigo no blog sobre um tópico muito semelhante, que vou resumir aqui. O script tem como objetivo extrair um rio de uma imagem NAIP de 4 bandas usando uma abordagem de classificação e segmentação de imagens.

  1. Converter imagem em uma matriz numpy
  2. Realize uma segmentação de troca rápida (Imagem 2)
  3. Converter segmentos em formato raster
  4. Calcular NDVI
  5. Realize estatísticas zonais médias usando segmentos e NDVI para transferir valores de NDVI para segmentos (Imagem 3)
  6. Classifique os segmentos com base nos valores NDVI
  7. Avalie os resultados (Imagem 4)

Este exemplo segmenta uma imagem usando o agrupamento de troca rápida no espaço de cores (x, y) com 4 bandas (vermelho, verde, azul, NIR) em vez de usar o agrupamento K-means. A segmentação da imagem foi realizada usando o pacote scikit-image . Mais detalhes sobre uma variedade de algoritmos de segmentação de imagem em scikit-image aqui . Por uma questão de conveniência, eu costumava arcpyfazer grande parte do trabalho de GIS, embora isso deva ser bastante fácil de transportar para o GDAL.


insira a descrição da imagem aqui


from __future__ import print_function

import arcpy
arcpy.CheckOutExtension("Spatial")

import matplotlib.pyplot as plt
import numpy as np
from skimage import io
from skimage.segmentation import quickshift

# The input 4-band NAIP image
river = r'C:\path\to\naip_image.tif'

# Convert image to numpy array
img = io.imread(river)

# Run the quick shift segmentation
segments = quickshift(img, kernel_size=3, convert2lab=False, max_dist=6, ratio=0.5)
print("Quickshift number of segments: %d" % len(np.unique(segments)))

# View the segments via Python
plt.imshow(segments)

# Get raster metrics for coordinate info
myRaster = arcpy.sa.Raster(river)

# Lower left coordinate of block (in map units)
mx = myRaster.extent.XMin
my = myRaster.extent.YMin
sr = myRaster.spatialReference

# Note the use of arcpy to convert numpy array to raster
seg = arcpy.NumPyArrayToRaster(segments, arcpy.Point(mx, my),
                               myRaster.meanCellWidth,
                               myRaster.meanCellHeight)

outRaster = r'C:\path\to\segments.tif'
seg_temp = seg.save(outRaster)
arcpy.DefineProjection_management(outRaster, sr)

# Calculate NDVI from bands 4 and 3
b4 = arcpy.sa.Raster(r'C:\path\to\naip_image.tif\Band_4')
b3 = arcpy.sa.Raster(r'C:\path\to\naip_image.tif\Band_3')
ndvi = arcpy.sa.Float(b4-b3) / arcpy.sa.Float(b4+b3)

# Extract NDVI values based on image object boundaries
zones = arcpy.sa.ZonalStatistics(outRaster, "VALUE", ndvi, "MEAN")
zones.save(r'C:\path\to\zones.tif')

# Classify the segments based on NDVI values
binary = arcpy.sa.Con(zones < 20, 1, 0)
binary.save(r'C:\path\to\classified_image_objects.tif')
Aaron
fonte
2
Esta é uma solução fantástica e evita alguns dos problemas com k-means e encontra um k ideal.
21415 Jeffrey Evans1
Isso é muito bom, ótimo trabalho !!
Jcstay
7

Você pode observar o agrupamento no scikit-learn . Você precisará ler os dados em matrizes numpy (eu sugiro rasterio ) e a partir daí você pode manipular os dados para que cada banda seja uma variável para classificação. Por exemplo, supondo que você tem as três bandas lido em python como red, greene bluematrizes numpy:

import numpy as np
import sklearn.cluster

original_shape = red.shape # so we can reshape the labels later

samples = np.column_stack([red.flatten(), green.flatten(), blue.flatten()])

clf = sklearn.cluster.KMeans(n_clusters=5)
labels = clf.fit_predict(samples).reshape(original_shape)

import matplotlib.pyplot as plt

plt.imshow(labels)
plt.show()

Observe que o cluster do KMeans não leva em consideração a conectividade dentro do conjunto de dados.

om_henners
fonte
+1 ótima resposta. Seria especialmente bom para mostrar um exemplo de conversão de imagens coloridas em matrizes numpy usando rasterio;)
Aaron
1
@Aaron Thanks! Publiquei um exemplo um pouco mais longo, incluindo a leitura de dados usando rasterio.
Om_henners
@om_henners sua solução é maravilhosa, mas eu tenho uma pergunta. A imagem segmentada retornada pelo seu programa usando k significa que o cluster é 2D. Agora eu preciso calcular o coeficiente de similaridade dos dados entre a imagem original (imagem 3D antes de dividir em bandas R, G, B) e a imagem segmentada, mas isso precisa que as duas tenham as mesmas dimensões. Como eu resolvo este problema?
Rach4