Spaces:
No application file
No application file
| import numpy as np | |
| import pandas as pd | |
| from matplotlib import pyplot as plt | |
| from deepforest_agent.conf.config import Config | |
| from deepforest_agent.tools.deepforest_tool import DeepForestPredictor | |
| from deepforest_agent.utils.image_utils import load_image_as_np_array | |
| TEST_IMAGE_PATH_SMALL = "data/AWPE Pigeon Lake 2020 DJI_0005.JPG" | |
| TEST_IMAGE_PATH_LARGE = "data/OSBS_029.tif" | |
| deepforest_predictor = DeepForestPredictor() | |
| def display_image_for_test(image_array: np.ndarray, title: str = "Test Image"): | |
| """ | |
| Display an image using matplotlib for visual inspection during testing. | |
| Args: | |
| image_array: Image as numpy array | |
| title: Title for the plot | |
| """ | |
| plt.imshow(image_array) | |
| plt.axis('off') | |
| plt.title(title) | |
| plt.show() | |
| def test_deepforest_predict_objects_basic_detection_bird(): | |
| """Test basic bird detection with default parameters on a small image.""" | |
| image_array = load_image_as_np_array(TEST_IMAGE_PATH_SMALL) | |
| if image_array is None: | |
| return | |
| summary, annotated_image, detections_list = ( | |
| deepforest_predictor.predict_objects( | |
| image_data_array=image_array, | |
| model_names=["bird"] | |
| ) | |
| ) | |
| assert "DeepForest detected" in summary or "No objects detected" in summary | |
| assert ("bird" in summary or "No objects detected" in summary) | |
| assert annotated_image is not None | |
| assert isinstance(annotated_image, np.ndarray) | |
| assert annotated_image.shape[:2] == image_array.shape[:2] | |
| assert isinstance(detections_list, list) | |
| if detections_list: | |
| bird_labels_found = any( | |
| detection["label"] == "bird" for detection in detections_list if 'label' in detection | |
| ) | |
| assert bird_labels_found | |
| display_image_for_test(annotated_image, "Bird Detection Test") | |
| def test_deepforest_predict_objects_basic_detection_tree(): | |
| """Test basic tree detection with default parameters on a small image.""" | |
| image_array = load_image_as_np_array(TEST_IMAGE_PATH_SMALL) | |
| if image_array is None: | |
| return | |
| summary, annotated_image, detections_list = ( | |
| deepforest_predictor.predict_objects( | |
| image_data_array=image_array, | |
| model_names=["tree"] | |
| ) | |
| ) | |
| assert "DeepForest detected" in summary or "No objects detected" in summary | |
| assert "tree" in summary or "No objects detected" in summary | |
| assert annotated_image is not None | |
| assert isinstance(annotated_image, np.ndarray) | |
| assert annotated_image.shape[:2] == image_array.shape[:2] | |
| assert isinstance(detections_list, list) | |
| if detections_list: | |
| tree_labels_found = any( | |
| detection["label"] == "tree" for detection in detections_list if 'label' in detection | |
| ) | |
| assert tree_labels_found | |
| display_image_for_test(annotated_image, "Tree Detection Test") | |
| def test_deepforest_predict_objects_multiple_models(): | |
| """Test detection using multiple models simultaneously.""" | |
| image_array = load_image_as_np_array(TEST_IMAGE_PATH_SMALL) | |
| if image_array is None: | |
| return | |
| summary, annotated_image, detections_list = ( | |
| deepforest_predictor.predict_objects( | |
| image_data_array=image_array, | |
| model_names=["bird", "tree", "livestock"] | |
| ) | |
| ) | |
| assert "DeepForest detected" in summary or "No objects detected" in summary | |
| assert annotated_image is not None | |
| assert isinstance(annotated_image, np.ndarray) | |
| assert annotated_image.shape[:2] == image_array.shape[:2] | |
| assert isinstance(detections_list, list) | |
| if detections_list: | |
| labels = {detection['label'] for detection in detections_list if 'label' in detection} | |
| assert "bird" in labels or "tree" in labels or "livestock" in labels | |
| display_image_for_test(annotated_image, "Multiple Models Test") | |
| def test_deepforest_predict_objects_large_image_processing(): | |
| """Test processing of large images using tiled prediction.""" | |
| summary, annotated_image, detections_list = ( | |
| deepforest_predictor.predict_objects( | |
| image_file_path=TEST_IMAGE_PATH_LARGE, | |
| model_names=["tree"], | |
| patch_size=Config.DEEPFOREST_DEFAULTS["patch_size"], | |
| patch_overlap=Config.DEEPFOREST_DEFAULTS["patch_overlap"], | |
| iou_threshold=Config.DEEPFOREST_DEFAULTS["iou_threshold"], | |
| thresh=Config.DEEPFOREST_DEFAULTS["thresh"] | |
| ) | |
| ) | |
| assert "DeepForest detected" in summary or "No objects detected" in summary | |
| assert annotated_image is not None | |
| assert isinstance(annotated_image, np.ndarray) | |
| assert isinstance(detections_list, list) | |
| if detections_list: | |
| assert any(detection['label'] == 'tree' for detection in detections_list if 'label' in detection) | |
| display_image_for_test(annotated_image, "Large Image Processing Test") | |
| def test_deepforest_predict_objects_custom_patch_size(): | |
| """Test detection with custom patch size parameter.""" | |
| image_array = load_image_as_np_array(TEST_IMAGE_PATH_SMALL) | |
| if image_array is None: | |
| return | |
| summary, annotated_image, detections_list = ( | |
| deepforest_predictor.predict_objects( | |
| image_data_array=image_array, | |
| model_names=["tree"], | |
| patch_size=800, | |
| patch_overlap=Config.DEEPFOREST_DEFAULTS["patch_overlap"], | |
| iou_threshold=Config.DEEPFOREST_DEFAULTS["iou_threshold"], | |
| thresh=Config.DEEPFOREST_DEFAULTS["thresh"] | |
| ) | |
| ) | |
| assert "DeepForest detected" in summary or "No objects detected" in summary | |
| assert annotated_image is not None | |
| assert isinstance(annotated_image, np.ndarray) | |
| assert annotated_image.shape[:2] == image_array.shape[:2] | |
| assert isinstance(detections_list, list) | |
| if detections_list: | |
| assert any(detection['label'] == 'tree' for detection in detections_list if 'label' in detection) | |
| display_image_for_test(annotated_image, "Custom Patch Size Test") | |
| def test_deepforest_predict_objects_multiple_custom_parameters(): | |
| """Test detection with multiple custom parameters.""" | |
| image_array = load_image_as_np_array(TEST_IMAGE_PATH_SMALL) | |
| if image_array is None: | |
| return | |
| summary, annotated_image, detections_list = ( | |
| deepforest_predictor.predict_objects( | |
| image_data_array=image_array, | |
| model_names=["tree"], | |
| patch_size=600, | |
| patch_overlap=0.1, | |
| iou_threshold=0.3, | |
| thresh=0.3 | |
| ) | |
| ) | |
| assert "DeepForest detected" in summary or "No objects detected" in summary | |
| assert annotated_image is not None | |
| assert isinstance(annotated_image, np.ndarray) | |
| assert annotated_image.shape[:2] == image_array.shape[:2] | |
| assert isinstance(detections_list, list) | |
| if detections_list: | |
| assert any(detection['label'] == 'tree' for detection in detections_list if 'label' in detection) | |
| display_image_for_test(annotated_image, "Multiple Custom Parameters Test") | |
| def test_deepforest_predict_objects_alive_dead_trees(): | |
| """Test alive/dead tree classification detection.""" | |
| summary, annotated_image, detections_list = ( | |
| deepforest_predictor.predict_objects( | |
| image_file_path=TEST_IMAGE_PATH_LARGE, | |
| model_names=["tree"], | |
| alive_dead_trees=True | |
| ) | |
| ) | |
| assert "DeepForest detected" in summary or "No objects detected" in summary | |
| assert annotated_image is not None | |
| assert isinstance(annotated_image, np.ndarray) | |
| print(summary) | |
| assert isinstance(detections_list, list) | |
| if detections_list: | |
| tree_detections = [d for d in detections_list if d.get('label') == 'tree'] | |
| assert len(tree_detections) > 0, "Expected at least one tree detection" | |
| # Check for classification_label field in tree detections | |
| classification_labels = {d.get('classification_label') for d in tree_detections | |
| if 'classification_label' in d} | |
| assert ('alive_tree' in classification_labels or 'dead_tree' in classification_labels), \ | |
| f"Expected alive_tree or dead_tree in classification labels, got: {classification_labels}" | |
| # Check that summary mentions classification results | |
| assert (("alive" in summary and "tree" in summary) or | |
| ("dead" in summary and "tree" in summary) or | |
| ("No objects detected" in summary)), \ | |
| f"Summary should mention alive/dead classification: {summary}" | |
| display_image_for_test(annotated_image, "Alive/Dead Tree Detection Test") | |
| def test_deepforest_predict_objects_no_detections(): | |
| """Test the function gracefully handles cases with no detections.""" | |
| blank_image = np.zeros((100, 100, 3), dtype=np.uint8) | |
| summary, annotated_image, detections_list = ( | |
| deepforest_predictor.predict_objects( | |
| image_data_array=blank_image, | |
| model_names=["tree"], | |
| thresh=1.0 | |
| ) | |
| ) | |
| assert "No objects detected by DeepForest" in summary | |
| assert annotated_image is not None | |
| assert isinstance(annotated_image, np.ndarray) | |
| assert annotated_image.shape[:2] == blank_image.shape[:2] | |
| assert isinstance(detections_list, list) | |
| assert len(detections_list) == 0 | |
| display_image_for_test(annotated_image, "No Detections Test") | |
| def test_deepforest_predict_objects_custom_thresholds(): | |
| """Test detection with custom threshold parameters.""" | |
| image_array = load_image_as_np_array(TEST_IMAGE_PATH_SMALL) | |
| if image_array is None: | |
| return | |
| summary, annotated_image, detections_list = ( | |
| deepforest_predictor.predict_objects( | |
| image_data_array=image_array, | |
| model_names=["tree"], | |
| thresh=0.9, | |
| iou_threshold=0.5 | |
| ) | |
| ) | |
| assert ("DeepForest detected" in summary or | |
| "No objects detected" in summary) | |
| assert annotated_image is not None | |
| assert isinstance(annotated_image, np.ndarray) | |
| assert annotated_image.shape[:2] == image_array.shape[:2] | |
| assert isinstance(detections_list, list) | |
| if detections_list: | |
| assert any(detection['label'] == 'tree' for detection in detections_list if 'label' in detection) | |
| display_image_for_test(annotated_image, "Custom Thresholds Test") | |
| def test_deepforest_predict_objects_unsupported_model_name(): | |
| """Test behavior with an unsupported model name.""" | |
| image_array = load_image_as_np_array(TEST_IMAGE_PATH_SMALL) | |
| if image_array is None: | |
| return | |
| summary, annotated_image, detections_list = ( | |
| deepforest_predictor.predict_objects( | |
| image_data_array=image_array, | |
| model_names=["tree", "nonexistent_model"] | |
| ) | |
| ) | |
| assert ("DeepForest detected" in summary or | |
| "No objects detected" in summary) | |
| assert annotated_image is not None | |
| assert isinstance(annotated_image, np.ndarray) | |
| assert annotated_image.shape[:2] == image_array.shape[:2] | |
| assert isinstance(detections_list, list) | |
| if detections_list: | |
| labels = {detection['label'] for detection in detections_list if 'label' in detection} | |
| assert 'tree' in labels | |
| assert 'nonexistent_model' not in labels | |
| display_image_for_test(annotated_image, "Unsupported Model Test") | |
| def test_plot_boxes_basic(): | |
| """Test _plot_boxes with some sample bounding box data.""" | |
| img = np.zeros((100, 100, 3), dtype=np.uint8) + 255 | |
| predictions = pd.DataFrame([ | |
| {'xmin': 10, 'ymin': 10, 'xmax': 30, 'ymax': 30, | |
| 'label': 'bird', 'score': 0.9}, | |
| {'xmin': 50, 'ymin': 50, 'xmax': 70, 'ymax': 70, | |
| 'label': 'tree', 'score': 0.8} | |
| ]) | |
| annotated_img = DeepForestPredictor._plot_boxes( | |
| img, predictions, Config.COLORS | |
| ) | |
| assert annotated_img.shape == img.shape | |
| assert not np.array_equal(annotated_img, img) | |
| display_image_for_test(annotated_img, "Plot Boxes Basic Test") | |
| def test_plot_boxes_empty_predictions(): | |
| """Test _plot_boxes with empty predictions DataFrame.""" | |
| img = np.zeros((100, 100, 3), dtype=np.uint8) + 255 | |
| predictions = pd.DataFrame({ | |
| "xmin": pd.Series(dtype=float), | |
| "ymin": pd.Series(dtype=float), | |
| "xmax": pd.Series(dtype=float), | |
| "ymax": pd.Series(dtype=float), | |
| "label": pd.Series(dtype=str), | |
| "score": pd.Series(dtype=float) | |
| }) | |
| annotated_img = DeepForestPredictor._plot_boxes( | |
| img, predictions, Config.COLORS | |
| ) | |
| assert np.array_equal(annotated_img, img) | |
| display_image_for_test(annotated_img, "Empty Predictions Test") | |
| def test_deepforest_predict_objects_default_parameters(): | |
| """Test that default parameters work correctly with tiled prediction.""" | |
| image_array = load_image_as_np_array(TEST_IMAGE_PATH_SMALL) | |
| if image_array is None: | |
| return | |
| summary, annotated_image, detections_list = ( | |
| deepforest_predictor.predict_objects( | |
| image_data_array=image_array, | |
| model_names=["tree"] | |
| ) | |
| ) | |
| assert ("DeepForest detected" in summary or "No objects detected" in summary) | |
| assert annotated_image is not None | |
| assert isinstance(annotated_image, np.ndarray) | |
| assert annotated_image.shape[:2] == image_array.shape[:2] | |
| assert isinstance(detections_list, list) | |
| print("Default parameters test completed successfully") | |
| display_image_for_test(annotated_image, "Default Parameters Test") | |
| def test_generate_detection_summary(): | |
| """Test the _generate_detection_summary method directly.""" | |
| # Test with empty DataFrame | |
| empty_df = pd.DataFrame() | |
| summary = deepforest_predictor._generate_detection_summary(empty_df) | |
| assert "No objects detected" in summary | |
| # Test with basic detections | |
| predictions_df = pd.DataFrame([ | |
| {'label': 'tree', 'score': 0.9}, | |
| {'label': 'tree', 'score': 0.8}, | |
| {'label': 'bird', 'score': 0.7} | |
| ]) | |
| summary = deepforest_predictor._generate_detection_summary(predictions_df) | |
| assert "DeepForest detected" in summary | |
| assert "2 trees" in summary | |
| assert "1 bird" in summary | |
| print("Detection summary tests completed successfully") | |
| def test_detections_list_structure(): | |
| """Test that detections_list has the correct structure.""" | |
| image_array = load_image_as_np_array(TEST_IMAGE_PATH_SMALL) | |
| if image_array is None: | |
| return | |
| summary, annotated_image, detections_list = ( | |
| deepforest_predictor.predict_objects( | |
| image_data_array=image_array, | |
| model_names=["tree"] | |
| ) | |
| ) | |
| assert isinstance(detections_list, list) | |
| if detections_list: | |
| for detection in detections_list: | |
| assert isinstance(detection, dict) | |
| assert 'xmin' in detection | |
| assert 'ymin' in detection | |
| assert 'xmax' in detection | |
| assert 'ymax' in detection | |
| assert 'score' in detection | |
| assert 'label' in detection | |
| assert isinstance(detection['xmin'], int) | |
| assert isinstance(detection['ymin'], int) | |
| assert isinstance(detection['xmax'], int) | |
| assert isinstance(detection['ymax'], int) | |
| assert isinstance(detection['score'], float) | |
| assert isinstance(detection['label'], str) | |
| print("Detections list structure test completed successfully") | |
| def test_error_handling_invalid_model(): | |
| """Test error handling when all models are invalid.""" | |
| image_array = load_image_as_np_array(TEST_IMAGE_PATH_SMALL) | |
| if image_array is None: | |
| return | |
| summary, annotated_image, detections_list = ( | |
| deepforest_predictor.predict_objects( | |
| image_data_array=image_array, | |
| model_names=["invalid_model_1", "invalid_model_2"] | |
| ) | |
| ) | |
| assert "No objects detected" in summary | |
| assert annotated_image is not None | |
| assert isinstance(annotated_image, np.ndarray) | |
| assert isinstance(detections_list, list) | |
| assert len(detections_list) == 0 | |
| print("Error handling test completed successfully") | |
| def test_input_validation(): | |
| """Test input validation for the predict_objects method.""" | |
| # Test with neither image_data_array nor image_file_path provided | |
| try: | |
| deepforest_predictor.predict_objects( | |
| image_data_array=None, | |
| image_file_path=None, | |
| model_names=["tree"] | |
| ) | |
| assert False, "Should have raised ValueError" | |
| except ValueError as e: | |
| assert "Either image_data_array or image_file_path must be provided" in str(e) | |
| print("Input validation test completed successfully") |