# Labelling 2.0 (ohne Skala 0-200 und weißen Hintergrund) ACHTUNG - Es wurden mehrere Verarbeitungsmöglichkeiten implementiert mit minimalen Unterschieden. Ungewollte Verarbeitung sollte auskommentiert werden: # Möglichkeit 1: import pandas as pd import geopandas as gpd import os import rasterio import numpy as np from shapely.geometry import box import matplotlib.pyplot as plt import matplotlib.patches as patches city_name = 'konstanz' gdf = gpd.read_file('baumkataster_stadt_' + city_name + '.geojson') gdf.loc[:, 'id'] = list(gdf.index) gdf = gdf.to_crs('EPSG:31468') df = pd.read_csv('baumkataster_classified_' + city_name + '.csv') tree_class = df.Klasse # Importverzeichnis für die rgbi Bilder datadir = city_name + '_aerial_images_rgbi' # Exportverzeichnis für die rgb Bilder rgbdir = city_name + '_aerial_images_rgb' # Exportverzeichnis für die gelabelten Bilder labeldir = city_name + '_labeled' # Verzeichnisse erstellen, falls noch nicht existent if not os.path.exists(labeldir): os.makedirs(labeldir) if not os.path.exists(rgbdir): os.makedirs(rgbdir) filenames = os.listdir(datadir) # Distanz pro Pixel festlegen gsd = 0.2 # m/px # Klassifizierungssystem und Farbgebung definieren class_id = {'Großer Laubbaum': 0, 'Kleiner Laubbaum': 1, 'Großer Nadelbaum': 2, 'Kleiner Nadelbaum': 3, 'Busch/Hecke (Laub/Hartlaub)': 4, 'Busch/Hecke (Nadel)': 5, 'Unbekannt': 6} class_colors = {'Großer Laubbaum': 'red', 'Kleiner Laubbaum': 'orange', 'Großer Nadelbaum': 'darkgreen', 'Kleiner Nadelbaum': 'lightgreen', 'Busch/Hecke (Laub/Hartlaub)': 'purple', 'Busch/Hecke (Nadel)': 'cornflowerblue', 'Unbekannt': 'white'} # Liste zum Speichern problematischer Dateien problematic_files = [] # Schleife für gesamten Ordner for filename in filenames: try: # Überprüfen, ob die Datei eine Bilddatei ist (z.B. .tif oder .png) if filename.endswith('.tif') or filename.endswith('.png'): # Öffnet Bilddatei dataset = rasterio.open(os.path.join(datadir, filename)) # Erstellung BBox für Bildgrenzen geom = box(*dataset.bounds) # Filtern der Bäume aus dem GeoDataFrame, die sich innerhalb der Bildgrenzen befinden trees = gdf[gdf.intersects(geom)] # Vorbereitung der Bilddarstellung f, ax = plt.subplots() ax.axis('off') # Skalen und Achsen ausblenden # Erstellen eines RGB-Bildes für die Visualisierung img = np.dstack(dataset.read()[:3]) ax.imshow(img) # Speichern des Originalbilds ohne Bounding-Boxen plt.savefig(os.path.join(rgbdir, filename.replace('.tif', '.png'))) # Erstellen und Speichern der Bounding-Boxen, wenn Bäume im Bild gefunden wurden if len(trees) > 0: for i, tree in trees.iterrows(): if not np.isnan(tree.kronenbrei): # Berechnen der Pixelkoordinaten des Baumes im Bild xy = dataset.index(tree.geometry.x, tree.geometry.y) # Bestimmen der oberen linken Ecke der Bounding-Box basierend auf der Baumkrone xy_center = (xy[0] - int(tree.kronenbrei/gsd/2), xy[1] - int(tree.kronenbrei/gsd/2)) # Speichern der Bounding-Box-Koordinaten und der Klassen-ID in einer .txt-Datei with open(os.path.join(labeldir, filename.replace('.tif', '.txt')), 'a') as bboxf: bboxf.write('{:d} {} {} {} {}\n'.format(class_id[tree_class.iloc[tree.id]], # Klassen-ID basierend auf dem Baumtyp xy[1]/img.shape[1], # Normalisierte x-Koordinate (xy[1] + int(tree.kronenbrei/gsd/2))/img.shape[1], # Normalisierte Breite xy[0]/img.shape[0], # Normalisierte y-Koordinate (xy[0] + int(tree.kronenbrei/gsd/2))/img.shape[1])) # Normalisierte Höhe # Erstellen eines Rechtecks (Bounding-Box) zur Visualisierung im Bild rect = patches.Rectangle(np.array(xy_center)[::-1], # Position der Bounding-Box tree.kronenbrei/gsd, # Breite der Bounding-Box tree.kronenbrei/gsd, # Höhe der Bounding-Box linewidth=1, # Linienstärke der BBox edgecolor=class_colors[tree_class.iloc[tree.id]], facecolor='none') # Farbe der Bounding-Box basierend auf der Klasse ax.add_patch(rect) # Speichern des Bildes mit den Bounding-Boxen als PNG-Datei plt.savefig(os.path.join(labeldir, filename.replace('.tif', '_bbox.png'))) plt.close() print(f"Processed: {filename}") except Exception as e: print(f"Error processing {filename}: {e}") problematic_files.append(filename) # Problematische Dateien anzeigen if problematic_files: print("\nProblematic files:") for problem_file in problematic_files: print(problem_file) # Möglichkeit 2: (Bilder unskaliert lassen) import pandas as pd import geopandas as gpd import os import rasterio import numpy as np from shapely.geometry import box import matplotlib.pyplot as plt import matplotlib.patches as patches city_name = 'wuerzburg' gdf = gpd.read_file('baumkataster_stadt_' + city_name + '.geojson') gdf.loc[:, 'id'] = list(gdf.index) gdf = gdf.to_crs('EPSG:31468') df = pd.read_csv('baumkataster_classified_' + city_name + '.csv') tree_class = df.Klasse # Importverzeichnis für die rgbi Bilder datadir = city_name + '_aerial_images_rgbi' # Exportverzeichnis für die rgb Bilder (ohne Bounding-Boxen) rgbdir = city_name + '_aerial_images_rgb' # Exportverzeichnis für die gelabelten Bilder (mit oder ohne Bounding-Boxen) labeldir = city_name + '_labeled' # Verzeichnisse erstellen, falls noch nicht existent if not os.path.exists(labeldir): os.makedirs(labeldir) if not os.path.exists(rgbdir): os.makedirs(rgbdir) filenames = os.listdir(datadir) # Distanz pro Pixel festlegen gsd = 0.2 # m/px # Klassifizierungssystem und Farbgebung definieren class_id = {'Großer Laubbaum': 0, 'Kleiner Laubbaum': 1, 'Großer Nadelbaum': 2, 'Kleiner Nadelbaum': 3, 'Busch/Hecke (Laub/Hartlaub)': 4, 'Busch/Hecke (Nadel)': 5, 'Unbekannt': 6} class_colors = {'Großer Laubbaum': 'red', 'Kleiner Laubbaum': 'orange', 'Großer Nadelbaum': 'darkgreen', 'Kleiner Nadelbaum': 'lightgreen', 'Busch/Hecke (Laub/Hartlaub)': 'purple', 'Busch/Hecke (Nadel)': 'cornflowerblue', 'Unbekannt': 'white'} # Liste zum Speichern problematischer Dateien problematic_files = [] # Schleife für gesamten Ordner for filename in filenames: try: # Überprüfen, ob die Datei eine Bilddatei ist (z.B. .tif oder .png) if filename.endswith('.tif') or filename.endswith('.png'): # Öffnet Bilddatei dataset = rasterio.open(os.path.join(datadir, filename)) # Erstellung BBox für Bildgrenzen geom = box(*dataset.bounds) # Filtern der Bäume aus dem GeoDataFrame, die sich innerhalb der Bildgrenzen befinden trees = gdf[gdf.intersects(geom)] # Erstellen eines RGB-Bildes für die Visualisierung img = np.dstack(dataset.read()[:3]) # Verwende nur die RGB-Kanäle # Speichern des Originalbilds im 'rgbdir'-Ordner (ohne Bounding-Boxen) plt.imsave(os.path.join(rgbdir, filename.replace('.tif', '.png')), img) # Falls keine Bäume gefunden wurden, speichere das Bild dennoch im 'labeldir'-Ordner mit der Endung '_bbox.png' if len(trees) == 0: plt.imsave(os.path.join(labeldir, filename.replace('.tif', '_bbox.png')), img) continue # Keine Bounding-Boxen, also überspringe den Rest des Loops # Bild und Achsen für Bounding-Box vorbereiten fig, ax = plt.subplots(figsize=(img.shape[1] / 100, img.shape[0] / 100), dpi=100) # Bildgröße exakt festlegen ax.imshow(img) ax.axis('off') # Skalen und Achsen ausblenden # Erstellen und Speichern der Bounding-Boxen, wenn Bäume im Bild gefunden wurden for i, tree in trees.iterrows(): if not np.isnan(tree.kronenbrei): # Berechnen der Pixelkoordinaten des Baumes im Bild xy = dataset.index(tree.geometry.x, tree.geometry.y) # Bestimmen der oberen linken Ecke der Bounding-Box basierend auf der Baumkrone xy_center = (xy[0] - int(tree.kronenbrei/gsd/2), xy[1] - int(tree.kronenbrei/gsd/2)) # Speichern der Bounding-Box-Koordinaten und der Klassen-ID in einer .txt-Datei with open(os.path.join(labeldir, filename.replace('.tif', '.txt')), 'a') as bboxf: bboxf.write('{:d} {} {} {} {}\n'.format(class_id[tree_class.iloc[tree.id]], # Klassen-ID basierend auf dem Baumtyp xy[1]/img.shape[1], # Normalisierte x-Koordinate (xy[1] + int(tree.kronenbrei/gsd/2))/img.shape[1], # Normalisierte Breite xy[0]/img.shape[0], # Normalisierte y-Koordinate (xy[0] + int(tree.kronenbrei/gsd/2))/img.shape[1])) # Normalisierte Höhe # Erstellen eines Rechtecks (Bounding-Box) zur Visualisierung im Bild rect = patches.Rectangle(np.array(xy_center)[::-1], # Position der Bounding-Box tree.kronenbrei/gsd, # Breite der Bounding-Box tree.kronenbrei/gsd, # Höhe der Bounding-Box linewidth=1, # Linienstärke der BBox edgecolor=class_colors[tree_class.iloc[tree.id]], facecolor='none') # Farbe der Bounding-Box basierend auf der Klasse ax.add_patch(rect) # Speichern des Bildes mit den Bounding-Boxen als PNG-Datei plt.savefig(os.path.join(labeldir, filename.replace('.tif', '_bbox.png')), bbox_inches='tight', pad_inches=0) plt.close() print(f"Processed: {filename}") except Exception as e: print(f"Error processing {filename}: {e}") problematic_files.append(filename) # Problematische Dateien anzeigen if problematic_files: print("\nProblematic files:") for problem_file in problematic_files: print(problem_file) # Möglichkeit 3: (Das Gleiche noch einmal aber mittels Pillow) import pandas as pd import geopandas as gpd import os import rasterio import numpy as np from shapely.geometry import box from PIL import Image, ImageDraw city_name = 'memmingen' gdf = gpd.read_file('baumkataster_stadt_' + city_name + '.geojson') gdf.loc[:, 'id'] = list(gdf.index) gdf = gdf.to_crs('EPSG:31468') df = pd.read_csv('baumkataster_classified_' + city_name + '.csv') tree_class = df.Klasse # Importverzeichnis für die rgbi Bilder datadir = city_name + '_aerial_images_rgbi' # Exportverzeichnis für die rgb Bilder (ohne Bounding-Boxen) rgbdir = city_name + '_aerial_images_rgb' # Exportverzeichnis für die gelabelten Bilder (mit oder ohne Bounding-Boxen) labeldir = city_name + '_labeled' # Verzeichnisse erstellen, falls noch nicht existent if not os.path.exists(labeldir): os.makedirs(labeldir) if not os.path.exists(rgbdir): os.makedirs(rgbdir) filenames = os.listdir(datadir) # Distanz pro Pixel festlegen gsd = 0.2 # m/px # Klassifizierungssystem und Farbgebung definieren class_id = {'Großer Laubbaum': 0, 'Kleiner Laubbaum': 1, 'Großer Nadelbaum': 2, 'Kleiner Nadelbaum': 3, 'Busch/Hecke (Laub/Hartlaub)': 4, 'Busch/Hecke (Nadel)': 5, 'Unbekannt': 6} class_colors = {'Großer Laubbaum': 'red', 'Kleiner Laubbaum': 'orange', 'Großer Nadelbaum': 'darkgreen', 'Kleiner Nadelbaum': 'lightgreen', 'Busch/Hecke (Laub/Hartlaub)': 'purple', 'Busch/Hecke (Nadel)': 'cornflowerblue', 'Unbekannt': 'white'} # Liste zum Speichern problematischer Dateien problematic_files = [] # Schleife für gesamten Ordner for filename in filenames: try: # Überprüfen, ob die Datei eine Bilddatei ist (z.B. .tif oder .png) if filename.endswith('.tif') or filename.endswith('.png'): # Öffnet Bilddatei dataset = rasterio.open(os.path.join(datadir, filename)) # Erstellung BBox für Bildgrenzen geom = box(*dataset.bounds) # Filtern der Bäume aus dem GeoDataFrame, die sich innerhalb der Bildgrenzen befinden trees = gdf[gdf.intersects(geom)] # Erstellen eines RGB-Bildes für die Visualisierung img = np.dstack(dataset.read()[:3]) # Verwende nur die RGB-Kanäle img_pil = Image.fromarray(img.astype(np.uint8)) # Speichern des Originalbilds im 'rgbdir'-Ordner (ohne Bounding-Boxen) img_pil.save(os.path.join(rgbdir, filename.replace('.tif', '.png'))) # Falls keine Bäume gefunden wurden, speichere das Bild dennoch im 'labeldir'-Ordner mit der Endung '_bbox.png' if len(trees) == 0: img_pil.save(os.path.join(labeldir, filename.replace('.tif', '_bbox.png'))) continue # Keine Bounding-Boxen, also überspringe den Rest des Loops # Erstellen des Zeichenwerkzeugs für Bounding-Boxen draw = ImageDraw.Draw(img_pil) # Erstellen und Speichern der Bounding-Boxen, wenn Bäume im Bild gefunden wurden for i, tree in trees.iterrows(): if not np.isnan(tree.kronenbrei): # Berechnen der Pixelkoordinaten des Baumes im Bild xy = dataset.index(tree.geometry.x, tree.geometry.y) # Bestimmen der oberen linken Ecke der Bounding-Box basierend auf der Baumkrone xy_center = (xy[0] - int(tree.kronenbrei/gsd/2), xy[1] - int(tree.kronenbrei/gsd/2)) # Speichern der Bounding-Box-Koordinaten und der Klassen-ID in einer .txt-Datei with open(os.path.join(labeldir, filename.replace('.tif', '.txt')), 'a') as bboxf: bboxf.write('{:d} {} {} {} {}\n'.format(class_id[tree_class.iloc[tree.id]], # Klassen-ID basierend auf dem Baumtyp xy[1] / img.shape[1], # Normalisierte x-Koordinate (xy[1] + int(tree.kronenbrei / gsd / 2)) / img.shape[1], # Normalisierte Breite xy[0] / img.shape[0], # Normalisierte y-Koordinate (xy[0] + int(tree.kronenbrei / gsd / 2)) / img.shape[0])) # Normalisierte Höhe # Zeichnen der Bounding-Box rect_xy = [xy_center[1], xy_center[0], xy_center[1] + tree.kronenbrei / gsd, xy_center[0] + tree.kronenbrei / gsd] draw.rectangle(rect_xy, outline=class_colors[tree_class.iloc[tree.id]], width=2) # Speichern des Bildes mit den Bounding-Boxen als PNG-Datei img_pil.save(os.path.join(labeldir, filename.replace('.tif', '_bbox.png'))) print(f"Processed: {filename}") except Exception as e: print(f"Error processing {filename}: {e}") problematic_files.append(filename) # Problematische Dateien anzeigen if problematic_files: print("\nProblematic files:") for problem_file in problematic_files: print(problem_file)