Bildsegmentierung des RGB-Bildes durch K bedeutet Clustering in Python

8

Ich möchte RGB-Bilder für die Landbedeckung mit k Mitteln segmentieren, indem ich sie so gruppiere, dass die verschiedenen Bereiche des Bildes durch unterschiedliche Farben markiert werden und wenn möglich Grenzen zwischen verschiedenen Bereichen erstellt werden. Ich möchte so etwas wie:

Geben Sie hier die Bildbeschreibung ein

davon :

Geben Sie hier die Bildbeschreibung ein

Ist es möglich, dies durch K-Mittel-Clustering zu erreichen? Ich habe im ganzen Internet gesucht und viele Tutorials tun dies mit k bedeutet Clustering, aber erst nachdem das Bild in Graustufen konvertiert wurde. Ich möchte es nur mit einem RGB-Bild machen. Gibt es eine Quelle, die mir dabei helfen könnte? Bitte schlagen Sie etwas vor.

rach
quelle
Hallo, versuche diesen Link. Ich habe es vor einiger Zeit versucht, hatte aber nur begrenzten Erfolg. Vielleicht können Sie es ein bisschen besser zum Laufen bringen. Viel Glück. opencv-python-tutroals.readthedocs.org/en/latest/py_tutorials/…
Jcstay
Hallo, danke für deinen Vorschlag @Jcstay, aber ich habe den Link bereits ausprobiert und es hat nicht geholfen. Trotzdem danke.
Rach
3
Ich möchte darauf hinweisen, dass der K-Means-Algorithmus, wie alle anderen Clustering-Methoden, eine optimale Anpassung von k benötigt. Da allen in den Referenzdaten eine Klasse zugewiesen wird, können die Ergebnisse fehlerhaft sein, wenn k nicht optimiert ist, ohne dass eine resultierende Klasse unterstützt wird. In diesen Fällen kann eine bestimmte Klasse nichts anderes als Rauschen oder Randeffekte in den Daten darstellen. Üblicherweise werden Rand-Silhouette-Werte verwendet, um ein optimales k auszuwählen.
Jeffrey Evans

Antworten:

9

Ich habe eine Lösung dafür gehackt und vor einiger Zeit einen Blog- Artikel zu einem sehr ähnlichen Thema geschrieben, den ich hier zusammenfassen werde. Das Skript soll einen Fluss aus einem 4-Band-NAIP-Bild unter Verwendung eines Bildsegmentierungs- und Klassifizierungsansatzes extrahieren.

  1. Konvertieren Sie das Bild in ein Numpy-Array
  2. Führen Sie eine schnelle Schichtsegmentierung durch (Bild 2)
  3. Konvertieren Sie Segmente in das Rasterformat
  4. Berechnen Sie den NDVI
  5. Führen Sie mittlere Zonenstatistiken mit Segmenten und NDVI durch, um NDVI-Werte auf Segmente zu übertragen (Bild 3).
  6. Klassifizieren Sie Segmente anhand von NDVI-Werten
  7. Ergebnisse auswerten (Bild 4)

In diesem Beispiel wird ein Bild mithilfe von Quickshift- Clustering im Farbraum (x, y) mit 4 Bändern (rot, grün, blau, NIR) segmentiert, anstatt K-Mittel-Clustering zu verwenden. Die Bildsegmentierung wurde mit dem Scikit-Image- Paket durchgeführt. Weitere Details zu einer Vielzahl von Bildsegmentierungsalgorithmen im Scikit-Bild finden Sie hier . Der Einfachheit halber habe ich einen arcpyGroßteil der GIS-Arbeit erledigt, obwohl dies ziemlich einfach auf GDAL zu portieren sein sollte.


Geben Sie hier die Bildbeschreibung ein


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
quelle
2
Dies ist eine fantastische Lösung, die einige Probleme mit k-means umgeht und ein optimales k findet.
Jeffrey Evans
Das ist sehr schöne, tolle Arbeit !!
Jcstay
7

Sie könnten sich das Clustering in Scikit-Learn ansehen . Sie müssen die Daten in numpy Arrays lesen (ich würde Rasterio vorschlagen ) und von dort aus können Sie die Daten so bearbeiten , dass jedes Band eine Variable für die Klassifizierung ist. Zum Beispiel, vorausgesetzt , Sie haben die drei Bänder in Python zu lesen wie red, greenund bluenumpy Arrays:

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()

Beachten Sie, dass das KMeans-Clustering die Konnektivität innerhalb des Datasets nicht berücksichtigt.

om_henners
quelle
+1 Tolle Antwort. Es wäre besonders schön, ein Beispiel für die Konvertierung von Farbbildern in numpy Arrays mit zu zeigen rasterio;)
Aaron
1
@ Aaron Danke! Ich habe ein etwas längeres Beispiel gepostet, einschließlich des Lesens von Daten mit rasterio.
om_henners
@om_henners Ihre Lösung ist wunderbar, aber ich habe eine Frage. Das von Ihrem Programm mit k zurückgegebene segmentierte Bild bedeutet, dass das Clustering 2D ist. Jetzt muss ich den Würfelähnlichkeitskoeffizienten zwischen dem Originalbild (3D-Bild vor dem Aufteilen in R-, G-, B-Bänder) und dem segmentierten Bild berechnen, aber dafür müssen beide die gleichen Abmessungen haben. Wie löse ich dieses Problem?
Rach