Skip to main content

Python Tensorflow Bildsortierung

Hierfür braucht man relativ große Bilddatensätze als Beispiele für Tensorflow um zuverlässige Ergebnisse zu bekommen.

Situation

Ihr habt ein großen Ordner voller Bilder und mit diesen über https://teachablemachine.withgoogle.com ein Modell mit zwei Klassen erstellt und ALS QUANTITISIERT exportiert.

Script

python 3.9

pip install tensorflow pillow

#!/usr/bin/env python3
# sorter_tm.py

import os
import sys
import shutil
from PIL import Image
import numpy as np
import tensorflow as tf
tflite = tf.lite

def load_labels(label_path):
    """Liest die Klassennamen aus einer labels.txt im Format '0 Klasse1'."""
    labels = []
    with open(label_path, 'r', encoding='utf-8') as f:
        for line in f:
            line = line.strip()
            if not line:
                continue
            parts = line.split(' ', 1)
            if len(parts) < 2:
                raise ValueError(f"Ungültiges Format in labels.txt: '{line}'")
            labels.append(parts[1])
    if not labels:
        raise ValueError("Die labels.txt ist leer oder enthält keine gültigen Einträge.")
    return labels

def prepare_interpreter(model_path):
    """Initialisiert den TFLite-Interpreter."""
    interpreter = tflite.Interpreter(model_path=model_path)
    interpreter.allocate_tensors()
    input_details  = interpreter.get_input_details()[0]
    output_details = interpreter.get_output_details()[0]
    return interpreter, input_details, output_details

def classify_image(interpreter, input_details, output_details, image, target_size):
    input_shape = input_details['shape']
    input_dtype = input_details['dtype']

    # Resize des PIL-Bildes
    img = image.resize(target_size)
    arr = np.array(img)

    if input_dtype == np.float32:
        arr = arr.astype(np.float32) / 255.0
    elif input_dtype == np.uint8:
        arr = arr.astype(np.uint8)
    else:
        raise ValueError(f"Nicht unterstützter Eingabetyp: {input_dtype}")

    arr = np.expand_dims(arr, axis=0)
    interpreter.set_tensor(input_details['index'], arr)
    interpreter.invoke()

    output = interpreter.get_tensor(output_details['index'])[0]
    output = output.astype(np.float32)  # Konvertiere Ausgabe zu float32 für Softmax
    probabilities = tf.nn.softmax(output).numpy()
    idx = np.argmax(probabilities)
    confidence = probabilities[idx]
    return idx, confidence, probabilities

def main(model_path, label_path, src_dir, dst_root, image_size):
    # Labels und Interpreter laden
    labels = load_labels(label_path)
    interpreter, in_det, out_det = prepare_interpreter(model_path)

    # Ausgabeordnerstruktur anlegen
    for lbl in labels:
        os.makedirs(os.path.join(dst_root, lbl), exist_ok=True)

    # Jedes Bild klassifizieren und verschieben
    for filename in os.listdir(src_dir):
        if not filename.lower().endswith((".jpg", ".jpeg", ".png", ".bmp", ".gif")):
            continue

        image_path = os.path.join(src_dir, filename)
        try:
            with Image.open(image_path) as img:
                img = img.convert("RGB")
        except Exception as e:
            print(f"Bild konnte nicht geöffnet werden: {filename} – {e}")
            continue

        idx, confidence, probabilities = classify_image(interpreter, in_det, out_det, img, image_size)
        label = labels[idx]

        target_dir = os.path.join(dst_root, label)
        os.makedirs(target_dir, exist_ok=True)

        target_path = os.path.join(target_dir, filename)
        try:
            shutil.move(image_path, target_path)
            print(f"{filename} → {label} ({confidence:.2%}) – Verteilung: {probabilities}")
        except Exception as e:
            print(f"Fehler beim Verschieben von {filename}: {e}")

if __name__ == "__main__":
    if len(sys.argv) != 6:
        print("Usage: python sorter_tm.py <model.tflite> <labels.txt> "
              "<source_dir> <destination_root> <image_size>")
        print("Beispiel: python sorter_tm.py model/model.tflite model/labels.txt "
              "input_images sorted_images 224")
        sys.exit(1)

    model_file       = sys.argv[1]
    labels_file      = sys.argv[2]
    source_folder    = sys.argv[3]
    destination_root = sys.argv[4]
    size             = int(sys.argv[5])

    main(model_file, labels_file, source_folder, destination_root, (size, size))

 

Vorgängerversionen

V2: Kann noch nicht mit Tensorflow Labels umgehen und erwartet feste Klassen.

#!/usr/bin/env python3
# sorter_tm.py

import os
import sys
import shutil
from PIL import Image
import numpy as np
import tensorflow as tf
tflite = tf.lite

def load_labels(label_path):
    """Liest die Klassennamen aus einer labels.txt."""
    with open(label_path, 'r', encoding='utf-8') as f:
        labels = [line.strip() for line in f if line.strip()]
    if len(labels) != 2 or set(labels) != {"Good", "Bad"}:
        raise ValueError("labels.txt muss genau die Einträge 'Good' und 'Bad' enthalten.")
    return labels

def prepare_interpreter(model_path):
    """Initialisiert den TFLite-Interpreter."""
    interpreter = tflite.Interpreter(model_path=model_path)
    interpreter.allocate_tensors()
    input_details  = interpreter.get_input_details()[0]
    output_details = interpreter.get_output_details()[0]
    return interpreter, input_details, output_details

def classify_image(interpreter, input_details, output_details, image, target_size):
    input_shape = input_details['shape']
    input_dtype = input_details['dtype']

    # Resize des PIL-Bildes
    img = image.resize(target_size)
    arr = np.array(img)

    if input_dtype == np.float32:
        arr = arr.astype(np.float32) / 255.0
    elif input_dtype == np.uint8:
        arr = arr.astype(np.uint8)
    else:
        raise ValueError(f"Nicht unterstützter Eingabetyp: {input_dtype}")

    arr = np.expand_dims(arr, axis=0)
    interpreter.set_tensor(input_details['index'], arr)
    interpreter.invoke()

    output = interpreter.get_tensor(output_details['index'])[0]
    output = output.astype(np.float32)  # Konvertiere Ausgabe zu float32 für Softmax
    probabilities = tf.nn.softmax(output).numpy()
    idx = np.argmax(probabilities)
    confidence = probabilities[idx]
    return idx, confidence, probabilities

def main(model_path, label_path, src_dir, dst_root, image_size):
    # Labels und Interpreter laden
    labels = load_labels(label_path)
    interpreter, in_det, out_det = prepare_interpreter(model_path)

    # Ausgabeordnerstruktur anlegen
    for lbl in labels:
        os.makedirs(os.path.join(dst_root, lbl), exist_ok=True)

    # Jedes Bild klassifizieren und verschieben
    for filename in os.listdir(src_dir):
        if not filename.lower().endswith((".jpg", ".jpeg", ".png", ".bmp", ".gif")):
            continue

        image_path = os.path.join(src_dir, filename)
        try:
            with Image.open(image_path) as img:
                img = img.convert("RGB")
        except Exception as e:
            print(f"Bild konnte nicht geöffnet werden: {filename} – {e}")
            continue

        idx, confidence, probabilities = classify_image(interpreter, in_det, out_det, img, image_size)
        label = labels[idx]

        target_dir = os.path.join(dst_root, label)
        os.makedirs(target_dir, exist_ok=True)

        target_path = os.path.join(target_dir, filename)
        try:
            shutil.move(image_path, target_path)
            print(f"{filename} → {label} ({confidence:.2%}) – Verteilung: {probabilities}")
        except Exception as e:
            print(f"Fehler beim Verschieben von {filename}: {e}")

if __name__ == "__main__":
    if len(sys.argv) != 6:
        print("Usage: python sorter_tm.py <model.tflite> <labels.txt> "
              "<source_dir> <destination_root> <image_size>")
        print("Beispiel: python sorter_tm.py model/model.tflite model/labels.txt "
              "input_images sorted_images 224")
        sys.exit(1)

    model_file       = sys.argv[1]
    labels_file      = sys.argv[2]
    source_folder    = sys.argv[3]
    destination_root = sys.argv[4]
    size             = int(sys.argv[5])

    main(model_file, labels_file, source_folder, destination_root, (size, size))

Notige Struktur:
C:\BildSorter\
├── model\
│   ├── model.tflite     ← exportiertes Modell von Teachable Machine
│   └── labels.txt       ← labels
├── input_images\        ← HIER liegen Ihre zu sortierenden Bilder
├── sorted_images\       ← WIRD automatisch befüllt mit Unterordnern
├── sorter_tm.py         ← das Python-Skript

Run:

python sorter_tm.py model/model.tflite model/labels.txt input_images sorted_images 224