diff --git a/python_scripts/DLM_Tree_Classification/DLM_training_script b/python_scripts/DLM_Tree_Classification/DLM_training_script new file mode 100644 index 0000000000000000000000000000000000000000..79b989092c30a42db3eb40d431861b7581ba7ccc --- /dev/null +++ b/python_scripts/DLM_Tree_Classification/DLM_training_script @@ -0,0 +1,287 @@ +import os +import gdown + +from glob import glob + +import cv2 +from PIL import Image, ImageDraw, ImageFont +from IPython.display import display + +from ultralytics import YOLO + +import matplotlib.pyplot as plt +import matplotlib.patches as patches + + + + +# Download Dataset - Download and unpack a dataset consisting of a train, val and test split. Trees are labeled (using the YOLO format). +gdown.cached_download("https://drive.google.com/file/d/1xkh8RYp15c0N4A9HFvcBeSmxhWaH5Ynw/view?usp=sharing", "sorted_images_YOLO_formatted.zip", fuzzy=True, postprocess=gdown.extractall) + + + + + + +# Setting up the Framework - create a setup file named yolov8.yaml containing required parameters +%%writefile sorted_images_YOLO_formatted/data/yolov8.yaml + +# Train/val/test sets +path: sorted_images_YOLO_formatted/data/ # dataset root dir +train: train # train images (relative to 'path') +val: val # val images (relative to 'path') +test: test # test images (optional) + +# number of classes +nc: 2 +# class names +names: ['Großer Laubbaum', + 'Kleiner Laubbaum'] + + + + + +# Training - Train the model on the train dataset. yolov8n.pt refers to the smallest model size +# Load a pretrained model +model = YOLO("yolov8n.pt") + +# Train the model +results = model.train(data="sorted_images_YOLO_formatted/data/yolov8.yaml", epochs=500, imgsz=200, cache=False) + + + +# Model evaluation - Evaluate the model performance on the validation dataset +# Validate the model +metrics = model.val(split='val') +metrics.box.map # map50-95 +metrics.box.map50 # map50 +metrics.box.map75 # map75 +metrics.box.maps # a list contains map50-95 of each category + + + + + +# Display performance metrics +display(Image.open('runs/detect/train/results.png')) +display(Image.open('runs/detect/train2/confusion_matrix_normalized.png')) +display(Image.open('runs/detect/train2/F1_curve.png')) +display(Image.open('runs/detect/train2/labels_correlogram.jpg')) +display(Image.open('runs/detect/train2/labels.jpg')) +display(Image.open('runs/detect/train/P_curve.png')) +display(Image.open('runs/detect/train/PR_curve.png')) +display(Image.open('runs/detect/train/R_curve.png')) + + + + +# Model Inference +# We use the model for inference on a list of images. +# First, we load the model checkpoint with the best performance. + +# Load trained model +model = YOLO('runs/detect/train2/weights/best.pt') # load a custom trained model + +# Export the model +#model.export(format='torchscript') +# model.export(format='onnx',simplify=True) + +# We define a list of images on which we will apply the model to identify trees. +#imagefiles = [ +# "/content/ObjectDetectionDataset/data/test/513278_5404358_Base_A62_Luftbild_2021_EPSG25832.png", +# "/content/ObjectDetectionDataset/data/test/513278_5404407_Base_A62_Luftbild_2021_EPSG25832.png", +# "/content/ObjectDetectionDataset/data/test/513278_5404553_Base_A62_Luftbild_2021_EPSG25832.png" +# ] + +# Run batched inference on a list of images +#results = model.predict(imagefiles) + + +# Neue Methodik um alle Bilder aus /content/ObjectDetectionDataset/data/test vom Modell untersuchen zu lassen +# Verzeichnis mit den Testbildern +test_directory = "sorted_images_YOLO_formatted/data/test/" +imagefiles = glob(os.path.join(test_directory, "*.png")) + +# Modellvorhersagen ausführen +results = model.predict(imagefiles) + +# Benutzerdefinierte Farben für die Klassen (basierend auf meinem ursprünglichen Skript) +class_colors = { + 'Großer Laubbaum': 'red', + 'Kleiner Laubbaum': 'orange' +} + +# Klassen-IDs und Klassennamen zuordnen +class_names = ['Großer Laubbaum', + 'Kleiner Laubbaum'] + + +# Following 2 Options of how to display the output: +# 1: Output with 200x200 pixels - save files to predictions/ directory - no readablie labels +from PIL import Image, ImageDraw, ImageFont +import os + +# Ergebnisverzeichnis für die Vorhersagen erstellen, falls es nicht existiert +os.makedirs('predictions/', exist_ok=True) + +# Benutzerdefinierte Farben für die Klassen +class_colors = { + 'Großer Laubbaum': 'red', + 'Kleiner Laubbaum': 'orange' +} + +# Vorhersagen plotten und Farben anpassen +for i, result in enumerate(results): + # Bild laden + img = Image.open(imagefiles[i]) + + # Bild um 50 % größer skalieren + img = img.resize((int(img.width * 1.5), int(img.height * 1.5))) + + draw = ImageDraw.Draw(img) + + # Bounding-Boxen und Klassen plotten + for box in result.boxes: + # Koordinaten der Bounding-Box + xmin, ymin, xmax, ymax = box.xyxy[0].numpy() + + # Anpassung der Koordinaten an das skalierte Bild + xmin, ymin, xmax, ymax = [coord * 1.5 for coord in [xmin, ymin, xmax, ymax]] + + # Klassennamen anhand der Klassen-ID holen + class_id = int(box.cls) + class_name = class_names[class_id] + + # Bounding-Box zeichnen mit der Farbe aus dem Farbschema + draw.rectangle([xmin, ymin, xmax, ymax], outline=class_colors[class_name], width=3) + + # Bild ohne weißen Hintergrund und Achsen speichern + img.save(os.path.join('predictions', os.path.split(imagefiles[i])[1])) + + print(f"Processed: {imagefiles[i]}") + + +# 2: Output with 200x200 pixels - save files to predictions/ directory - readable labels +from PIL import Image, ImageDraw, ImageFont +import os + +# Ergebnisverzeichnis für die Vorhersagen erstellen, falls es nicht existiert +os.makedirs('predictions/', exist_ok=True) + +# Benutzerdefinierte Farben für die Klassen +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' +} + +# Schriftart und -größe festlegen (falls verfügbar) +try: + font = ImageFont.truetype("arial.ttf", 40) # Verwende Arial mit Größe 20 +except IOError: + font = ImageFont.load_default() # Fallback auf Standard-Schriftart + +# Vorhersagen plotten und Farben anpassen +for i, result in enumerate(results): + # Bild laden + img = Image.open(imagefiles[i]) + + # Bild um 50 % größer skalieren + img = img.resize((int(img.width * 1.5), int(img.height * 1.5))) + + draw = ImageDraw.Draw(img) + + # Bounding-Boxen und Klassen plotten + for box in result.boxes: + # Koordinaten der Bounding-Box + xmin, ymin, xmax, ymax = box.xyxy[0].numpy() + + # Anpassung der Koordinaten an das skalierte Bild + xmin, ymin, xmax, ymax = [coord * 1.5 for coord in [xmin, ymin, xmax, ymax]] + + # Klassennamen anhand der Klassen-ID holen + class_id = int(box.cls) + class_name = class_names[class_id] + + # Bounding-Box zeichnen mit der Farbe aus dem Farbschema + draw.rectangle([xmin, ymin, xmax, ymax], outline=class_colors[class_name], width=2) + + # Größe des Textes berechnen mit textbbox (ersetzt textsize) + text_bbox = draw.textbbox((xmin, ymin), class_name, font=font) + text_width = text_bbox[2] - text_bbox[0] + text_height = text_bbox[3] - text_bbox[1] + + # Hintergrund für den Text (halbtransparent) + text_background = (xmin, ymin - text_height, xmin + text_width, ymin) + draw.rectangle(text_background, fill=(200, 200, 200, 128)) # Schwarzer halbtransparenter Hintergrund + + # Klassennamen über die Bounding-Box schreiben + draw.text((xmin, ymin - text_height), class_name, font=font, fill="red") # Weißer Text + + # Bild ohne weißen Hintergrund und Achsen speichern + img.save(os.path.join('predictions', os.path.split(imagefiles[i])[1])) + + print(f"Processed: {imagefiles[i]}") + + + + + + +# Export of labels as .txt +def yolo_format(class_id, xmin, ymin, xmax, ymax, img_width, img_height): + x_center = (xmin + xmax) / 2.0 / img_width + y_center = (ymin + ymax) / 2.0 / img_height + width = (xmax - xmin) / img_width + height = (ymax - ymin) / img_height + return f"{class_id} {x_center} {y_center} {width} {height}" + +def save_yolo_labels(results, output_dir): + if not os.path.exists(output_dir): + os.makedirs(output_dir) + + for i, result in enumerate(results): + boxes = result.boxes # Extract boxes from result + img_width, img_height = result.orig_shape # Get the original image shape + + image_path = result.path + image_id = os.path.splitext(os.path.basename(image_path))[0] + + with open(os.path.join(output_dir, f"{image_id}.txt"), 'w') as f: + for box in boxes: + class_id = int(box.cls) + xmin, ymin, xmax, ymax = box.xyxy[0].numpy() # Extract box coordinates + + yolo_line = yolo_format( + class_id, + xmin, + ymin, + xmax, + ymax, + img_width, + img_height + ) + f.write(yolo_line + '\n') + +# Run batched inference on all images in the test directory +results = model.predict(imagefiles) + +# Save the YOLO labels +output_dir = 'predictions' +save_yolo_labels(results, output_dir) + + +# Display results +#display all predictions +result_directory = "predictions" + +# Get a list of all image files in the result directory +resultimages = glob(os.path.join(result_directory, "*.png")) + +for image in resultimages: + display(Image.open(image))