This repository was archived by the owner on Nov 13, 2025. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 37
showing annotated images for detect target message bug fix #230
Closed
Closed
Changes from all commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
1ad8ba8
showing annotated images for detect target message bug fix
arpanroy18 7315197
formatting attempt fix
arpanroy18 c5fc431
reviewed changes
arpanroy18 100919c
formatting fix
arpanroy18 915d367
formatting fixes again
arpanroy18 4dc6f99
removed ds_store files
arpanroy18 eef1307
fixed formatting
arpanroy18 2e89607
Implement brightspot detection (#228)
siddhp1 9d846bd
integrated camera_factory in video_input (#232)
Aleksa-M a980e4a
rebase attempt 2
arpanroy18 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,153 @@ | ||
| """ | ||
| Detects bright spots in images. | ||
| """ | ||
|
|
||
| import time | ||
|
|
||
| import cv2 | ||
| import numpy as np | ||
|
|
||
| from . import base_detect_target | ||
| from .. import detections_and_time | ||
| from .. import image_and_time | ||
| from ..common.modules.logger import logger | ||
|
|
||
|
|
||
| BRIGHTSPOT_PERCENTILE = 99.9 | ||
|
|
||
| # Label for brightspots; is 1 since 0 is used for blue landing pads | ||
| DETECTION_LABEL = 1 | ||
| # SimpleBlobDetector is a binary detector, so a detection has confidence 1.0 by default | ||
| CONFIDENCE = 1.0 | ||
|
|
||
|
|
||
| class DetectTargetBrightspot(base_detect_target.BaseDetectTarget): | ||
| """ | ||
| Detects bright spots in images. | ||
| """ | ||
|
|
||
| def __init__( | ||
| self, | ||
| local_logger: logger.Logger, | ||
| show_annotations: bool = False, | ||
| save_name: str = "", | ||
| ) -> None: | ||
| """ | ||
| Initializes the bright spot detector. | ||
|
|
||
| show_annotations: Display annotated images. | ||
| save_name: Filename prefix for logging detections and annotated images. | ||
| """ | ||
| self.__counter = 0 | ||
| self.__local_logger = local_logger | ||
| self.__show_annotations = show_annotations | ||
| self.__filename_prefix = "" | ||
| if save_name != "": | ||
| self.__filename_prefix = f"{save_name}_{int(time.time())}_" | ||
|
|
||
| def run( | ||
| self, data: image_and_time.ImageAndTime | ||
| ) -> tuple[True, detections_and_time.DetectionsAndTime] | tuple[False, None]: | ||
| """ | ||
| Runs brightspot detection on the provided image and returns the detections. | ||
|
|
||
| data: Image with a timestamp. | ||
|
|
||
| Return: Success, detections. | ||
| """ | ||
| start_time = time.time() | ||
|
|
||
| image = data.image | ||
| try: | ||
| 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 greyscale, exception: {exception}" | ||
| ) | ||
| return False, None | ||
|
|
||
| brightspot_threshold = np.percentile(grey_image, BRIGHTSPOT_PERCENTILE) | ||
|
|
||
| # Apply thresholding to isolate bright spots | ||
| threshold_used, bw_image = cv2.threshold( | ||
| grey_image, brightspot_threshold, 255, cv2.THRESH_BINARY | ||
| ) | ||
| if threshold_used == 0: | ||
| self.__local_logger.error(f"{time.time()}: Failed to threshold image.") | ||
| return False, None | ||
|
|
||
| # Set up SimpleBlobDetector | ||
| params = cv2.SimpleBlobDetector_Params() | ||
| params.filterByColor = True | ||
| params.blobColor = 255 | ||
| params.filterByCircularity = False | ||
| params.filterByInertia = True | ||
| params.minInertiaRatio = 0.2 | ||
| params.filterByConvexity = False | ||
| params.filterByArea = True | ||
| params.minArea = 50 # pixels | ||
|
|
||
| detector = cv2.SimpleBlobDetector_create(params) | ||
| keypoints = detector.detect(bw_image) | ||
|
|
||
| # A lack of detections is not an error, but should still not be forwarded | ||
| if len(keypoints) == 0: | ||
| self.__local_logger.info(f"{time.time()}: No brightspots detected.") | ||
| return False, None | ||
|
|
||
| # Annotate the image (green circle) with detected keypoints | ||
| image_annotated = cv2.drawKeypoints( | ||
| 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 | ||
| assert detections is not None | ||
|
|
||
| # Draw bounding boxes around detected keypoints | ||
| for keypoint in keypoints: | ||
| x, y = keypoint.pt | ||
| size = keypoint.size | ||
| bounds = np.array([x - size / 2, y - size / 2, x + size / 2, y + size / 2]) | ||
| result, detection = detections_and_time.Detection.create( | ||
| bounds, DETECTION_LABEL, CONFIDENCE | ||
| ) | ||
| 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) | ||
|
|
||
| # Logging is identical to detect_target_ultralytics.py | ||
| # pylint: disable=duplicate-code | ||
| end_time = time.time() | ||
|
|
||
| # Logging | ||
| self.__local_logger.info( | ||
| f"{time.time()}: Count: {self.__counter}. Target detection took {end_time - start_time} seconds. Objects detected: {detections}." | ||
| ) | ||
|
|
||
| if self.__filename_prefix != "": | ||
| filename = self.__filename_prefix + str(self.__counter) | ||
|
|
||
| # Annotated image | ||
| cv2.imwrite(filename + ".png", image_annotated) # type: ignore | ||
|
|
||
| self.__counter += 1 | ||
|
|
||
| if self.__show_annotations: | ||
| cv2.imshow("Annotated", image_annotated) # type: ignore | ||
|
|
||
| # pylint: enable=duplicate-code | ||
|
|
||
| return True, detections |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,8 +3,10 @@ | |
| """ | ||
|
|
||
| import enum | ||
| import torch | ||
|
|
||
| from . import base_detect_target | ||
| from . import detect_target_brightspot | ||
| from . import detect_target_ultralytics | ||
| from ..common.modules.logger import logger | ||
|
|
||
|
|
@@ -15,6 +17,7 @@ class DetectTargetOption(enum.Enum): | |
| """ | ||
|
|
||
| ML_ULTRALYTICS = 0 | ||
| CV_BRIGHTSPOT = 1 | ||
|
|
||
|
|
||
| def create_detect_target( | ||
|
|
@@ -27,8 +30,16 @@ def create_detect_target( | |
| save_name: str, | ||
| ) -> tuple[bool, base_detect_target.BaseDetectTarget | None]: | ||
| """ | ||
| Construct detect target class at runtime. | ||
| Factory function to create a detection target object. | ||
|
|
||
| Return: | ||
| Success, detect target object. | ||
| """ | ||
| # Fall back to CPU if no GPU is available | ||
| if device != "cpu" and not torch.cuda.is_available(): | ||
| local_logger.warning("CUDA not available. Falling back to CPU.") | ||
| device = "cpu" | ||
|
|
||
arpanroy18 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| match detect_target_option: | ||
| case DetectTargetOption.ML_ULTRALYTICS: | ||
| return True, detect_target_ultralytics.DetectTargetUltralytics( | ||
|
|
@@ -39,5 +50,11 @@ def create_detect_target( | |
| show_annotations, | ||
| save_name, | ||
| ) | ||
| case DetectTargetOption.CV_BRIGHTSPOT: | ||
| return True, detect_target_brightspot.DetectTargetBrightspot( | ||
| local_logger, | ||
| show_annotations, | ||
| save_name, | ||
| ) | ||
|
|
||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Restore empty line. |
||
| return False, None | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,8 +5,10 @@ | |
| import time | ||
|
|
||
| import cv2 | ||
arpanroy18 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| import torch | ||
arpanroy18 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| import ultralytics | ||
|
|
||
|
|
||
| from . import base_detect_target | ||
| from .. import image_and_time | ||
| from .. import detections_and_time | ||
|
|
@@ -37,14 +39,18 @@ def __init__( | |
| self.__device = device | ||
| self.__model = ultralytics.YOLO(model_path) | ||
| self.__counter = 0 | ||
| self.__enable_half_precision = not self.__device == "cpu" | ||
| self.__enable_half_precision = self.__device != "cpu" | ||
| self.__local_logger = local_logger | ||
| self.__show_annotations = show_annotations | ||
| if override_full: | ||
| self.__enable_half_precision = False | ||
| self.__filename_prefix = "" | ||
| if save_name != "": | ||
| self.__filename_prefix = save_name + "_" + str(int(time.time())) + "_" | ||
|
|
||
| if self.__device != "cpu" and not torch.cuda.is_available(): | ||
| self.__local_logger.warning("CUDA not available. Falling back to CPU.") | ||
| self.__device = "cpu" | ||
|
Comment on lines
+51
to
+53
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add empty line above this line. |
||
|
|
||
| def run( | ||
| self, data: image_and_time.ImageAndTime | ||
|
|
@@ -111,6 +117,13 @@ def run( | |
| self.__counter += 1 | ||
|
|
||
| if self.__show_annotations: | ||
| cv2.imshow("Annotated", image_annotated) # type: ignore | ||
| if image_annotated is None: | ||
| self.__local_logger.error("Annotated image is invalid.") | ||
| return False, detections | ||
|
|
||
|
|
||
| # Display the annotated image in a named window | ||
| cv2.imshow("Annotated", image_annotated) | ||
| cv2.waitKey(1) # Short delay to process GUI events | ||
|
|
||
| return True, detections | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.