From 70c35faf86ff49f7e8cc1ae93c29e822bd0eae0d Mon Sep 17 00:00:00 2001 From: KNSO Date: Sat, 12 Sep 2026 19:22:25 +0200 Subject: [PATCH] test(extract): Verschmelzungsfall und Schwellenwert von drop_map_labels absichern Ruling 43: beruehren sich Schriftzug und echte Flaeche (z.B. eine Mole), bilden sie eine Komponente und bleiben beide stehen - dieselbe vorsichtige Richtung wie bei Beschriftungen auf dem Festland, lieber ein Buchstabe zu viel als eine Mole zu wenig. Ruling 44: ein Tippfehler in LABEL_MAX_NATURAL (0.2 statt 0.02) faellt jetzt in einem Test auf, der knapp oberhalb der Schwelle liegt. MAINLAND_MIN_SIZE-Kommentar auf zwei Saetze gekuerzt (Stil wie LAND_REACH_PX/PALM_VERGE), Wortdopplung im Docstring entfernt. Kein Verhalten geaendert: 41 statt 39 Tests, beide neuen Tests waren vor jeder Aenderung bereits gruen. Co-Authored-By: Claude Sonnet 5 --- tools/extract/leonida/classify.py | 14 +++++++------- tools/extract/tests/test_classify.py | 20 ++++++++++++++++++++ 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/tools/extract/leonida/classify.py b/tools/extract/leonida/classify.py index 80014e5..9b19559 100644 --- a/tools/extract/leonida/classify.py +++ b/tools/extract/leonida/classify.py @@ -45,11 +45,11 @@ def class_shares(labels: np.ndarray) -> dict[str, float]: NATURAL_CLASSES = ("gruen", "sand", "land") LABEL_MAX_NATURAL = 0.02 # Anteil natuerlicher Merkmale, ab dem eine Flaeche als echt gilt -# Groesser als jeder gemessene Buchstabe (4.876-14.760 px am realen Mosaik) und weit kleiner -# als die Hauptlandmasse (5.852.263 px): ohne diese Untergrenze waere in einer Szene ohne -# echte Hauptlandmasse die groesste - und einzige - Komponente automatisch "Festland" und -# damit nie loeschbar, auch wenn sie selbst ein Schriftzug ist (siehe -# test_letter_shaped_island_becomes_water, wo genau das die einzige Komponente ist). +# Ab dieser Groesse zaehlt die groesste Komponente als Festland; die Ausnahme gilt nur ihr, +# ein Buchstabe kann sie also nur erben, wenn die Szene sonst gar kein Land enthaelt. Jeder +# Wert zwischen dem groessten gemessenen Buchstaben (14.760 px) und der echten Hauptlandmasse +# (5.852.263 px) aendert am realen Mosaik nichts; 15.000 liegt in dieser Spanne und bleibt +# zugleich unter den 16.200 px aus test_mainland_is_never_dropped. MAINLAND_MIN_SIZE = 15_000 @@ -61,8 +61,8 @@ def drop_map_labels(labels: np.ndarray) -> np.ndarray: Region: ein Schriftzug im Wasser bildet eine eigene, vom Festland getrennte Nicht-Wasser- Komponente ohne jedes natuerliche Merkmal, waehrend eine echte Insel Gruen oder Sand traegt. Die groesste Komponente ist das Festland und bleibt unangetastet, sofern sie - ueberhaupt gross genug ist, um ueberhaupt als Festland infrage zu kommen (MAINLAND_MIN_SIZE); - sonst wird auch sie nur ueber den Anteil natuerlicher Merkmale beurteilt. + ueberhaupt gross genug ist, um als Festland infrage zu kommen (MAINLAND_MIN_SIZE); sonst + wird auch sie nur ueber den Anteil natuerlicher Merkmale beurteilt. """ solid = labels != CLASSES["wasser"] if not solid.any(): diff --git a/tools/extract/tests/test_classify.py b/tools/extract/tests/test_classify.py index 3e60a5d..637529c 100644 --- a/tools/extract/tests/test_classify.py +++ b/tools/extract/tests/test_classify.py @@ -58,3 +58,23 @@ def test_mainland_is_never_dropped(): labels[10:190, 10:100] = CLASSES["stadt"] out = drop_map_labels(labels) assert (out[10:190, 10:100] == CLASSES["stadt"]).all() + +def test_lettering_touching_a_real_area_is_kept(): + """Beruehren sich Schriftzug und echte Flaeche, bilden sie eine Komponente, und es bleibt + beides stehen. Die vorsichtige Richtung: lieber ein Buchstabe zu viel als eine Mole zu + wenig. Dieselbe Entscheidung wie bei den Beschriftungen auf dem Festland.""" + labels = np.full((200, 200), CLASSES["wasser"], dtype=np.uint8) + labels[40:60, 40:120] = CLASSES["strasse"] # Schriftzug, fuer sich genommen 0 % natuerlich + labels[60:90, 100:140] = CLASSES["stadt"] # Mole, beruehrt ihn an der Kante + labels[70:85, 110:135] = CLASSES["gruen"] # ... und traegt Gruen: echte Flaeche + out = drop_map_labels(labels) + assert (out[40:60, 40:120] == CLASSES["strasse"]).all() + +def test_five_percent_of_green_already_counts_as_a_real_area(): + """Die Grenze liegt bei 2 %. Mit 5 % Gruen muss die Flaeche bleiben - daran faellt auch + ein Tippfehler in LABEL_MAX_NATURAL auf (0.2 statt 0.02), der sonst durchginge.""" + labels = np.full((200, 200), CLASSES["wasser"], dtype=np.uint8) + labels[40:140, 40:140] = CLASSES["stadt"] # 10.000 px, unter MAINLAND_MIN_SIZE + labels[40:62, 40:63] = CLASSES["gruen"] # 506 px, also gut 5 % + out = drop_map_labels(labels) + assert (out[40:140, 40:140] != CLASSES["wasser"]).all()