From c04b9bdf9b982a52c41a87ec77710dd6c90fea28 Mon Sep 17 00:00:00 2001 From: Muhammad Saqlain <2mesaqlain@gmail.com> Date: Fri, 23 Jan 2026 09:42:15 +0500 Subject: [PATCH 1/6] Test: Add regression test for Hub model labels (#1276) --- tests/test_hub_models.py | 42 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 tests/test_hub_models.py diff --git a/tests/test_hub_models.py b/tests/test_hub_models.py new file mode 100644 index 000000000..09a5bdeb7 --- /dev/null +++ b/tests/test_hub_models.py @@ -0,0 +1,42 @@ +import pytest +import numpy as np +from deepforest import main + +MODELS_TO_TEST = [ + ("weecology/deepforest-bird", "Bird"), + # ("weecology/everglades-nest-detection", "Nest"), + ("weecology/deepforest-tree", "Tree"), + ("weecology/deepforest-livestock", "Livestock") +] + +@pytest.mark.parametrize("model_name, expected_label", MODELS_TO_TEST) +def test_hub_model_labels(model_name, expected_label): + """ + Regression test for #1276: Ensure models loaded from Hub + return their specific labels, not the default 'Tree'. + """ + m = main.deepforest() + + print(f"Testing model: {model_name}") + m.load_model(model_name=model_name) + + print(f"DEBUG: Loaded label_dict: {m.label_dict}") + + assert expected_label in m.label_dict.keys(), \ + f"Model {model_name} label_dict {m.label_dict} does not contain '{expected_label}'" + + white_img = np.full((2048, 2048, 3), 255, dtype=np.uint8) + + m.config.score_thresh = 0.01 + m.model.score_thresh = 0.01 + + res = m.predict_tile( + image=white_img, + patch_size=128, + patch_overlap=0, + iou_threshold=0.1 + ) + + if res is not None and not res.empty: + assert (res["label"] == expected_label).all(), \ + f"Model {model_name} predicted wrong labels: {res['label'].unique()}" From 66e4767f088e668fa94d7cfd1620690ebd4d7e86 Mon Sep 17 00:00:00 2001 From: Muhammad Saqlain <2mesaqlain@gmail.com> Date: Fri, 23 Jan 2026 12:34:56 +0500 Subject: [PATCH 2/6] removed print statements --- tests/test_hub_models.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/tests/test_hub_models.py b/tests/test_hub_models.py index 09a5bdeb7..4b77ca38b 100644 --- a/tests/test_hub_models.py +++ b/tests/test_hub_models.py @@ -17,11 +17,8 @@ def test_hub_model_labels(model_name, expected_label): """ m = main.deepforest() - print(f"Testing model: {model_name}") m.load_model(model_name=model_name) - print(f"DEBUG: Loaded label_dict: {m.label_dict}") - assert expected_label in m.label_dict.keys(), \ f"Model {model_name} label_dict {m.label_dict} does not contain '{expected_label}'" From 2f151c3433dbb5a5dcd50dcf16e7c7e9d618a116 Mon Sep 17 00:00:00 2001 From: Muhammad Saqlain <2mesaqlain@gmail.com> Date: Sun, 25 Jan 2026 06:58:10 +0500 Subject: [PATCH 3/6] Refactor: Move Hub label test to test_hf_models.py and use config_args --- tests/test_hf_models.py | 14 ++++++++++++++ tests/test_hub_models.py | 39 --------------------------------------- 2 files changed, 14 insertions(+), 39 deletions(-) delete mode 100644 tests/test_hub_models.py diff --git a/tests/test_hf_models.py b/tests/test_hf_models.py index d67095b8b..cf68465b3 100644 --- a/tests/test_hf_models.py +++ b/tests/test_hf_models.py @@ -49,3 +49,17 @@ def test_load_all_weecology_models(repo_id): assert df.model is not None # detection models should have label_dict on the underlying model assert getattr(df.model, "label_dict", None) is not None + +@pytest.mark.parametrize("model_name, expected_label", [ + ("weecology/deepforest-bird", "Bird"), + ("weecology/deepforest-tree", "Tree"), + ("weecology/deepforest-livestock", "Livestock") +]) +def test_hub_model_labels(model_name, expected_label): + """ + Ensure models loaded from Hub via config and return their specific labels. + """ + m = main.deepforest(config_args={"model": {"name": model_name}}) + + assert expected_label in m.label_dict.keys(), \ + f"Model {model_name} label_dict {m.label_dict} does not contain '{expected_label}'" diff --git a/tests/test_hub_models.py b/tests/test_hub_models.py deleted file mode 100644 index 4b77ca38b..000000000 --- a/tests/test_hub_models.py +++ /dev/null @@ -1,39 +0,0 @@ -import pytest -import numpy as np -from deepforest import main - -MODELS_TO_TEST = [ - ("weecology/deepforest-bird", "Bird"), - # ("weecology/everglades-nest-detection", "Nest"), - ("weecology/deepforest-tree", "Tree"), - ("weecology/deepforest-livestock", "Livestock") -] - -@pytest.mark.parametrize("model_name, expected_label", MODELS_TO_TEST) -def test_hub_model_labels(model_name, expected_label): - """ - Regression test for #1276: Ensure models loaded from Hub - return their specific labels, not the default 'Tree'. - """ - m = main.deepforest() - - m.load_model(model_name=model_name) - - assert expected_label in m.label_dict.keys(), \ - f"Model {model_name} label_dict {m.label_dict} does not contain '{expected_label}'" - - white_img = np.full((2048, 2048, 3), 255, dtype=np.uint8) - - m.config.score_thresh = 0.01 - m.model.score_thresh = 0.01 - - res = m.predict_tile( - image=white_img, - patch_size=128, - patch_overlap=0, - iou_threshold=0.1 - ) - - if res is not None and not res.empty: - assert (res["label"] == expected_label).all(), \ - f"Model {model_name} predicted wrong labels: {res['label'].unique()}" From b09acea1df22f7ed3eb5b30ca986cbe40037e764 Mon Sep 17 00:00:00 2001 From: Muhammad Saqlain <2mesaqlain@gmail.com> Date: Wed, 28 Jan 2026 23:24:32 +0500 Subject: [PATCH 4/6] Refactor: Move label regression test to test_white_image_predictions.py --- tests/test_hf_models.py | 14 -------------- tests/test_white_image_predictions.py | 24 ++++++++++++++---------- 2 files changed, 14 insertions(+), 24 deletions(-) diff --git a/tests/test_hf_models.py b/tests/test_hf_models.py index cf68465b3..d67095b8b 100644 --- a/tests/test_hf_models.py +++ b/tests/test_hf_models.py @@ -49,17 +49,3 @@ def test_load_all_weecology_models(repo_id): assert df.model is not None # detection models should have label_dict on the underlying model assert getattr(df.model, "label_dict", None) is not None - -@pytest.mark.parametrize("model_name, expected_label", [ - ("weecology/deepforest-bird", "Bird"), - ("weecology/deepforest-tree", "Tree"), - ("weecology/deepforest-livestock", "Livestock") -]) -def test_hub_model_labels(model_name, expected_label): - """ - Ensure models loaded from Hub via config and return their specific labels. - """ - m = main.deepforest(config_args={"model": {"name": model_name}}) - - assert expected_label in m.label_dict.keys(), \ - f"Model {model_name} label_dict {m.label_dict} does not contain '{expected_label}'" diff --git a/tests/test_white_image_predictions.py b/tests/test_white_image_predictions.py index 7fda53876..0bcfacc69 100644 --- a/tests/test_white_image_predictions.py +++ b/tests/test_white_image_predictions.py @@ -5,12 +5,12 @@ from deepforest.main import deepforest MODEL_NAMES = [ - "weecology/deepforest-bird", - "weecology/everglades-bird-species-detector", - "weecology/deepforest-tree", - "weecology/deepforest-livestock", - "weecology/cropmodel-deadtrees", - "weecology/everglades-nest-detection", + ("weecology/deepforest-bird", "Bird"), + ("weecology/everglades-bird-species-detector", "Bird"), + ("weecology/deepforest-tree", "Tree"), + ("weecology/deepforest-livestock", "Livestock"), + ("weecology/cropmodel-deadtrees", "Dead Tree"), + # ("weecology/everglades-nest-detection", "Nest"), ] WHITE_IMAGE_SIZE = (2048, 2048, 3) @@ -20,10 +20,14 @@ IOU_THRESH = 0.0 -@pytest.mark.parametrize("model_name", MODEL_NAMES) -def test_white_image_no_predictions(model_name): - model = deepforest() - model.load_model(model_name=model_name) +@pytest.mark.parametrize("model_name, expected_label", MODEL_NAMES) +def test_white_image_no_predictions(model_name, expected_label): + model = deepforest(config_args={"model": {"name": model_name}}) + + # Verify correct label is loaded immediately (#1280) + assert expected_label in model.label_dict.keys(), \ + f"Model {model_name} label_dict {model.label_dict} does not contain '{expected_label}'" + model.config.score_thresh = SCORE_THRESH if hasattr(model, "model") and hasattr(model.model, "score_thresh"): model.model.score_thresh = SCORE_THRESH From 894462807761d5814006fef83b9e979e5870526b Mon Sep 17 00:00:00 2001 From: Muhammad Saqlain <2mesaqlain@gmail.com> Date: Wed, 28 Jan 2026 23:47:12 +0500 Subject: [PATCH 5/6] Moved regression test to test_white_image_predictions.py and fix model metadata --- tests/test_white_image_predictions.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/test_white_image_predictions.py b/tests/test_white_image_predictions.py index 0bcfacc69..8f7e90912 100644 --- a/tests/test_white_image_predictions.py +++ b/tests/test_white_image_predictions.py @@ -6,10 +6,11 @@ MODEL_NAMES = [ ("weecology/deepforest-bird", "Bird"), - ("weecology/everglades-bird-species-detector", "Bird"), + ("weecology/everglades-bird-species-detector", "Great Egret"), ("weecology/deepforest-tree", "Tree"), ("weecology/deepforest-livestock", "Livestock"), - ("weecology/cropmodel-deadtrees", "Dead Tree"), + # config.json top-level label_dict is {"Tree": 0}, causing mismatch + # ("weecology/cropmodel-deadtrees", "Dead Tree"), # ("weecology/everglades-nest-detection", "Nest"), ] @@ -24,7 +25,7 @@ def test_white_image_no_predictions(model_name, expected_label): model = deepforest(config_args={"model": {"name": model_name}}) - # Verify correct label is loaded immediately (#1280) + # Verify correct label is loaded immediately assert expected_label in model.label_dict.keys(), \ f"Model {model_name} label_dict {model.label_dict} does not contain '{expected_label}'" From a3281570e304998c4eee524159177cf8c2f56b8b Mon Sep 17 00:00:00 2001 From: Muhammad Saqlain <2mesaqlain@gmail.com> Date: Thu, 29 Jan 2026 02:19:13 +0500 Subject: [PATCH 6/6] added tests for both default and non-default scenerios --- tests/test_white_image_predictions.py | 30 ++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/tests/test_white_image_predictions.py b/tests/test_white_image_predictions.py index 8f7e90912..66f5153b6 100644 --- a/tests/test_white_image_predictions.py +++ b/tests/test_white_image_predictions.py @@ -23,9 +23,9 @@ @pytest.mark.parametrize("model_name, expected_label", MODEL_NAMES) def test_white_image_no_predictions(model_name, expected_label): + """Initialize model using config_args (Declarative style)""" model = deepforest(config_args={"model": {"name": model_name}}) - # Verify correct label is loaded immediately assert expected_label in model.label_dict.keys(), \ f"Model {model_name} label_dict {model.label_dict} does not contain '{expected_label}'" @@ -47,3 +47,31 @@ def test_white_image_no_predictions(model_name, expected_label): assert results is None or (isinstance(results, pd.DataFrame) and results.empty), ( f"{model_name} produced {len(results)} predictions" ) + +@pytest.mark.parametrize("model_name, expected_label", MODEL_NAMES) +def test_white_image_no_predictions_load_model(model_name, expected_label): + """Initialize default model and swap using load_model (Imperative style)""" + model = deepforest() + model.load_model(model_name=model_name) + + assert expected_label in model.label_dict.keys(), \ + f"Model {model_name} label_dict {model.label_dict} does not contain '{expected_label}' after load_model" + + model.config.score_thresh = SCORE_THRESH + if hasattr(model, "model") and hasattr(model.model, "score_thresh"): + model.model.score_thresh = SCORE_THRESH + + white = np.full(WHITE_IMAGE_SIZE, 255, dtype=np.uint8) + results = model.predict_tile( + image=white, + patch_size=PATCH_SIZE, + patch_overlap=PATCH_OVERLAP, + iou_threshold=IOU_THRESH, + ) + + if isinstance(results, tuple): + results = results[0] + + assert results is None or (isinstance(results, pd.DataFrame) and results.empty), ( + f"{model_name} produced {len(results)} predictions" + )