Menschen zählen im Bild

Ich lag im Krankenhaut. Vier Rippen gebrochen. Internet und mein neuer Surface Pro on ARM (+ Copilot) verfügbar. 64 GB RAM, GPU, NPU. Wollte mit lokalen Modellen experimentieren. Ollama, LM Studio und Windows local foundry. War ehrlich gesagt etwas enttäuschend. Irgendwo hakte es immer. Funktioniert zwar das meiste, aber nicht mit der erwarteten Leistung. ´
Aber das Thema ist visuelle KI. Es fand gerade eine Konferenz statt und ich wollte wissen, wie viele Teilnehmer da wohl im Raum sitzen. Erstversuch ChatGPT hingeworfen mit einem “zähl mal”. 290. Dann ein wenig hin und her diskutiert über Methode und es wurden immer weniger. Ja, die Tokens auch.

Dann Zielsetzung geändert. Mach mir ein Python Script, das  Menschen im Raum zählen kann. Ein ganz wichtiges Pattern im Kontext von AI. Den Prozess definieren automatisieren mit Hilfe von KI, aber dann ohne laufen lassen. Oder mit einer lokalen KI, was gerade bei Video und Bild dramatische Performance und Kostenersparnis bringt.

Es gibt Vision Modelle die sich per Ollama laden und lokal betreiben lassen. Das ist aber langsam und nur sinnvoll, wenn der Workload vom lokalen PC weg muss. Außerdem kann man so nicht Objekte markieren im Bild (Bounding Boxes).
Für Bilderkennung findet man unter Yolo verschieden geprägte Modelle. Ich landete bei Yolo11x. Nur rund 100MB groß.
Und dann habe ich mich für ein mehrstufiges Verfahren entschieden. Auch ein ganz wichtiges Erfolgskriterium. Die Aufgaben klein zu machen und je Schritt die passende Technologie. Mal ein Python Script, mal ein lokales Modell, mal ein API Call zu Open AI ChatGPT 5.6.
Stufe 1 markiert jeden Kopf mit einem roten Punkt. Stufe 2 zählt nur die Punkte.
teilnehmer_personen_markiert

Wenn man mit Modellen direkt arbeitet, bzw diese Aufgaben erledigen lässt, kann man sich nicht darauf verlassen, das das Ergebnis reproduzierbar und damit 100% richtig ist. Die großen Anbieter nutzen in der Zwischenzeit nur mehr ausschließlich Thinking und in einer Schleife (Loop). Damit kommt man sehr nah an Perfektion. Auch lokale Modelle wie Qwen 3.6 setzen auf Thinking Mode. Nachteil: dauert und verbrennt Token.

Für die Bilderkennung der Anzahl der Teilnehmer auf dem Foto, lasse ich einfach mehrfach Yolo mit verschiedenen Parametern laufen.

 1  from ultralytics import YOLO
 2  import cv2
 3  from pathlib import Path
 4  import csv
 5  
 6  # ==========================================
 7  # Einstellungen
 8  # ==========================================
 9  
10  input_file = Path("teilnehmer.jpg")
11  
12  model = YOLO("yolo11x.pt")
13  
14  conf_values = [0.08, 0.10, 0.12, 0.15, 0.20]
15  
16  output_dir = Path("output")
17  output_dir.mkdir(exist_ok=True)
18  
19  # ==========================================
20  
21  original = cv2.imread(str(input_file))
22  
23  if original is None:
24      raise FileNotFoundError(input_file)
25  
26  csv_rows = []
27  
28  for conf in conf_values:
29  
30      img = original.copy()
31  
32      results = model(
33          img,
34          conf=conf,
35          iou=0.35,
36          imgsz=1920,
37          classes=[0]
38      )
39  
40      count = 0
41  
42      for r in results:
43          for box in r.boxes:
44  
45              x1, y1, x2, y2 = map(int, box.xyxy[0])
46  
47              # Kopfposition
48              cx = int((x1 + x2) / 2)
49              cy = int(y1 + (y2 - y1) * 0.18)
50  
51              count += 1
52  
53              # roter Punkt
54              cv2.circle(img, (cx, cy), 5, (0, 0, 255), -1)
55  
56              # Nummer daneben
57              cv2.putText(
58                  img,
59                  str(count),
60                  (cx + 7, cy - 4),
61                  cv2.FONT_HERSHEY_SIMPLEX,
62                  0.35,
63                  (0, 0, 255),
64                  1,
65                  cv2.LINE_AA
66              )
67      cv2.putText(
68          img,
69          f"Personen: {count}",
70          (30, 55),
71          cv2.FONT_HERSHEY_SIMPLEX,
72          1.4,
73          (0, 0, 255),
74          3,
75          cv2.LINE_AA
76      )
77  
78      outfile = output_dir / f"{input_file.stem}_conf{int(conf*1000):03d}.jpg"
79  
80      cv2.imwrite(str(outfile), img)
81  
82      csv_rows.append([outfile.name, conf, count])
83  
84      print(f"{conf:.2f} -> {count}")
85  
86  # CSV schreiben
87  with open(output_dir / "statistik.csv", "w", newline="", encoding="utf-8") as f:
88  
89      writer = csv.writer(f, delimiter=";")
90      writer.writerow(["Datei", "Confidence", "Personen"])
91      writer.writerows(csv_rows)
92  
93  print("\nFertig.")
94  print(f"Ergebnisse in: {output_dir.resolve()}")
[sourcecode language='python'  padlinenumbers='true']
from ultralytics import YOLO
import cv2
from pathlib import Path
import csv

# ==========================================
# Einstellungen
# ==========================================

input_file = Path("teilnehmer.jpg")

model = YOLO("yolo11x.pt")

conf_values = [0.08, 0.10, 0.12, 0.15, 0.20]

output_dir = Path("output")
output_dir.mkdir(exist_ok=True)

# ==========================================

original = cv2.imread(str(input_file))

if original is None:
    raise FileNotFoundError(input_file)

csv_rows = []

for conf in conf_values:

    img = original.copy()

    results = model(
        img,
        conf=conf,
        iou=0.35,
        imgsz=1920,
        classes=[0]
    )

    count = 0

    for r in results:
        for box in r.boxes:

            x1, y1, x2, y2 = map(int, box.xyxy[0])

            # Kopfposition
            cx = int((x1 + x2) / 2)
            cy = int(y1 + (y2 - y1) * 0.18)

            count += 1

            # roter Punkt
            cv2.circle(img, (cx, cy), 5, (0, 0, 255), -1)

            # Nummer daneben
            cv2.putText(
                img,
                str(count),
                (cx + 7, cy - 4),
                cv2.FONT_HERSHEY_SIMPLEX,
                0.35,
                (0, 0, 255),
                1,
                cv2.LINE_AA
            )
    cv2.putText(
        img,
        f"Personen: {count}",
        (30, 55),
        cv2.FONT_HERSHEY_SIMPLEX,
        1.4,
        (0, 0, 255),
        3,
        cv2.LINE_AA
    )

    outfile = output_dir / f"{input_file.stem}_conf{int(conf*1000):03d}.jpg"

    cv2.imwrite(str(outfile), img)

    csv_rows.append([outfile.name, conf, count])

    print(f"{conf:.2f} -> {count}")

# CSV schreiben
with open(output_dir / "statistik.csv", "w", newline="", encoding="utf-8") as f:

    writer = csv.writer(f, delimiter=";")
    writer.writerow(["Datei", "Confidence", "Personen"])
    writer.writerows(csv_rows)

print("\nFertig.")
print(f"Ergebnisse in: {output_dir.resolve()}")
[/sourcecode]
Kommentare sind geschlossen