diff --git a/modules/detect_target/detect_target_brightspot.py b/modules/detect_target/detect_target_brightspot.py index f217c614..06842f8a 100644 --- a/modules/detect_target/detect_target_brightspot.py +++ b/modules/detect_target/detect_target_brightspot.py @@ -1,5 +1,5 @@ """ -Detects bright spots generated by an IR camera. +Detects bright spots in images. """ import time @@ -8,14 +8,16 @@ import numpy as np from . import base_detect_target -from .. import image_and_time from .. import detections_and_time +from .. import image_and_time from ..common.modules.logger import logger BRIGHTSPOT_PERCENTILE = 99.9 + +# Label for brightspots DETECTION_LABEL = 1 -# Confidence set to 1 by default because SimpleBlobDetector does not compute a reponse value during keypoint detection +# SimpleBlobDetector is a binary detector, so a detection has confidence 1.0 by default CONFIDENCE = 1 @@ -45,7 +47,7 @@ def __init__( def run( self, data: image_and_time.ImageAndTime - ) -> "tuple[bool, detections_and_time.DetectionsAndTime | None]": + ) -> "tuple[True, detections_and_time.DetectionsAndTime] | tuple[False, None]": """ Runs brightspot detection on the provided image and returns the detections. @@ -57,19 +59,20 @@ def run( image = data.image try: - gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) + grey_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) # Catching all exceptions for library call # pylint: disable-next=broad-exception-caught except Exception as exception: - self.__local_logger.error(f"{time.time()}: Failed to convert to grayscale.") - self.__local_logger.error(f"{exception}") + self.__local_logger.error( + f"{time.time()}: Failed to convert to greyscale, exception: {exception}" + ) return False, None - brightspot_threshold = np.percentile(gray_image, BRIGHTSPOT_PERCENTILE) + brightspot_threshold = np.percentile(grey_image, BRIGHTSPOT_PERCENTILE) # Apply thresholding to isolate bright spots threshold_used, bw_image = cv2.threshold( - gray_image, brightspot_threshold, 255, cv2.THRESH_BINARY + grey_image, brightspot_threshold, 255, cv2.THRESH_BINARY ) if threshold_used == 0: self.__local_logger.error(f"{time.time()}: Failed to threshold image.") @@ -84,7 +87,7 @@ def run( params.minInertiaRatio = 0.2 params.filterByConvexity = False params.filterByArea = True - params.minArea = 50 + params.minArea = 50 # pixels detector = cv2.SimpleBlobDetector_create(params) keypoints = detector.detect(bw_image) if len(keypoints) == 0: @@ -93,12 +96,13 @@ def run( # Annotate the image (green circle) with detected keypoints image_annotated = cv2.drawKeypoints( - image, keypoints, np.array([]), (0, 255, 0), cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS + image, keypoints, None, (0, 255, 0), cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS ) # Process bright spot detection result, detections = detections_and_time.DetectionsAndTime.create(data.timestamp) if not result: + self.__local_logger.error(f"{time.time()}: Failed to create detections for image.") return False, None # Get Pylance to stop complaining @@ -112,12 +116,16 @@ def run( result, detection = detections_and_time.Detection.create( bounds, DETECTION_LABEL, CONFIDENCE ) - if result: - # Get Pylance to stop complaining - assert detections is not None + if not result: + self.__local_logger.error(f"{time.time()}: Failed to create bounding boxes.") + return False, None + + # Get Pylance to stop complaining + assert detections is not None - detections.append(detection) + detections.append(detection) + # Logging is identical to detect_target_ultralytics # pylint: disable=duplicate-code end_time = time.time() @@ -137,4 +145,6 @@ def run( if self.__show_annotations: cv2.imshow("Annotated", image_annotated) # type: ignore + # pylint: enable=duplicate-code + return True, detections diff --git a/tests/brightspot_example/generate_expected.py b/tests/brightspot_example/generate_expected.py index 3cb5ec69..f75e4530 100644 --- a/tests/brightspot_example/generate_expected.py +++ b/tests/brightspot_example/generate_expected.py @@ -7,14 +7,14 @@ import cv2 import numpy as np -from modules.detect_target import detect_target_brightspot from modules import image_and_time from modules.common.modules.logger import logger +from modules.detect_target import detect_target_brightspot NUMBER_OF_IMAGES = 7 TEST_PATH = pathlib.Path("tests", "brightspot_example") -IMAGE_FILES = [f"ir{i}.png" for i in range(1, NUMBER_OF_IMAGES + 1)] +IMAGE_FILES = [pathlib.Path(f"ir{i}.png") for i in range(1, NUMBER_OF_IMAGES + 1)] ANNOTATED_IMAGE_PATHS = [ pathlib.Path(TEST_PATH, f"ir{i}_annotated.png") for i in range(1, NUMBER_OF_IMAGES + 1) ] @@ -42,15 +42,21 @@ def main() -> int: image_path = pathlib.Path(TEST_PATH, image_file) image = cv2.imread(str(image_path)) # type: ignore result, image_data = image_and_time.ImageAndTime.create(image) - if not result or image_data is None: - print(f"Failed to load image {image_path}.") + if not result: + temp_logger.error(f"Failed to load image {image_path}.") continue - success, detections = detector.run(image_data) - if not success or detections is None: - print(f"Detection failed or returned no detections for {image_path}.") + # Get Pylance to stop complaining + assert image_data is not None + + result, detections = detector.run(image_data) + if not result: + temp_logger.error(f"Detection failed or returned no detections for {image_path}.") continue + # Get Pylance to stop complaining + assert detections is not None + detections_list = [] image_annotated = image.copy() for detection in detections.detections: @@ -67,10 +73,14 @@ def main() -> int: detections_array = np.array(detections_list) np.savetxt(expected_detections_path, detections_array, fmt="%.6f") - print(f"Expected detections saved to {expected_detections_path}") + temp_logger.info(f"Expected detections saved to {expected_detections_path}.") + + result = cv2.imwrite(str(annotated_image_path), image_annotated) # type: ignore + if not result: + temp_logger.error(f"Failed to wrtie image to {annotated_image_path}.") + continue - cv2.imwrite(str(annotated_image_path), image_annotated) # type: ignore - print(f"Annotated image saved to {annotated_image_path}") + temp_logger.info(f"Annotated image saved to {annotated_image_path}.") return 0