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
Add in classical cv #204
Open
AshishA26
wants to merge
27
commits into
main
Choose a base branch
from
add_in_classical_cv
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Add in classical cv #204
Changes from all commits
Commits
Show all changes
27 commits
Select commit
Hold shift + click to select a range
c8b6228
Added Dylans code
AshishA26 1692f6f
Made fixes
AshishA26 dd9a29a
Added test_detect_target_contour.py and edited detect_target_contour:
achitaan bf8faf1
linters: reformatting code
achitaan 1a19e20
Added corrections(?):
achitaan c609f60
Updated code for the test (forgot to push earlier)
achitaan 15dff4e
Detect Target Contour working and check now detect all circular conto…
6a16444
black . change
cf82cf4
Linter Changes (again)
da7b5af
linter???
c0e772d
linter please
4d105f1
??
74497e9
pls
0742460
Corrected main_detect_target paths
b3dcfca
removed window
9b62cfa
Xierumeng changes (mostly?)
a6540e7
Last comma changes
8def299
Xierumeng Chnages 2
achitaan 14fcc41
small pylint changes
achitaan fa9b8d6
xierumeng changes v3
achitaan f873640
logger changes
achitaan 59c8d94
Xierumeng Changes
achitaan 8ce749c
Most changes made
achitaan d423026
Refactored + Rebase
achitaan 4b8aa3c
Removed create_detections
achitaan cc24a04
Updated compare_detections
achitaan e522279
black . space??
achitaan 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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,139 @@ | ||
| """ | ||
| Detects objects using the provided model. | ||
| """ | ||
|
|
||
| import time | ||
|
|
||
| import cv2 | ||
| import numpy as np | ||
|
|
||
| from . import base_detect_target | ||
| from .. import image_and_time | ||
| from .. import detections_and_time | ||
| from ..common.modules.logger import logger | ||
|
|
||
Xierumeng marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
Xierumeng marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| MIN_CONTOUR_AREA = 100 | ||
| MAX_CIRCULARITY = 1.3 | ||
| MIN_CIRCULARITY = 0.7 | ||
|
|
||
| UPPER_BLUE = np.array([130, 255, 255]) | ||
Xierumeng marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| LOWER_BLUE = np.array([100, 50, 50]) | ||
|
|
||
| CONFIDENCE = 1.0 | ||
Xierumeng marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| LABEL = 0 | ||
|
|
||
|
|
||
| class DetectTargetContour(base_detect_target.BaseDetectTarget): | ||
| """ | ||
| Predicts and locates landing pads using the classical computer vision methodology. | ||
| """ | ||
|
|
||
| def __init__( | ||
| self, image_logger: logger.Logger, show_annotations: bool = False, save_name: str = "" | ||
| ) -> None: | ||
| """ | ||
| image_logger: Log annotated images. | ||
| show_annotations: Display annotated images. | ||
Xierumeng marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| save_name: filename prefix for logging detections and annotated images. | ||
| """ | ||
| self.__counter = 0 | ||
| self.__show_annotations = show_annotations | ||
| self.__filename_prefix = "" | ||
| self.__logger = image_logger | ||
|
|
||
| if save_name != "": | ||
| self.__filename_prefix = save_name + "_" + str(int(time.time())) + "_" | ||
|
|
||
| def detect_landing_pads_contours( | ||
| self, image_and_time_data: image_and_time.ImageAndTime | ||
| ) -> tuple[True, detections_and_time.DetectionsAndTime, np.ndarray] | tuple[False, None, None]: | ||
| """ | ||
| Detects landing pads using contours/classical CV. | ||
|
|
||
| image_and_time_data: Data for the current image and time. | ||
| timestamp: Timestamp for the detections. | ||
|
|
||
| Return: Success, the DetectionsAndTime object, and the annotated image. | ||
Xierumeng marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| """ | ||
| image = image_and_time_data.image | ||
| timestamp = image_and_time_data.timestamp | ||
|
|
||
| hsv_image = cv2.cvtColor(image, cv2.COLOR_BGR2HSV) | ||
| mask = cv2.inRange(hsv_image, LOWER_BLUE, UPPER_BLUE) | ||
|
|
||
| contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) | ||
|
|
||
| if len(contours) == 0: | ||
| return False, None, None | ||
|
|
||
| result, detections = detections_and_time.DetectionsAndTime.create(timestamp) | ||
| if not result: | ||
| return False, None, None | ||
|
|
||
| image_annotated = image | ||
| for i, contour in enumerate(contours): | ||
| contour_area = cv2.contourArea(contour) | ||
|
|
||
| if contour_area < MIN_CONTOUR_AREA: | ||
| continue | ||
|
|
||
| (x, y), radius = cv2.minEnclosingCircle(contour) | ||
|
|
||
| enclosing_area = np.pi * (radius**2) | ||
|
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 spaces around |
||
| circularity = contour_area / enclosing_area | ||
|
|
||
| if circularity < MIN_CIRCULARITY or circularity > MAX_CIRCULARITY: | ||
| continue | ||
|
|
||
| x, y, w, h = cv2.boundingRect(contour) | ||
| bounds = np.array([x, y, x + w, y + h]) | ||
|
|
||
| # Create a Detection object and append it to detections | ||
| result, detection = detections_and_time.Detection.create(bounds, LABEL, CONFIDENCE) | ||
|
|
||
| if not result: | ||
| return False, None, None | ||
|
|
||
| detections.append(detection) | ||
|
|
||
| # Annotate the image | ||
| cv2.rectangle(image_annotated, (x, y), (x + w, y + h), (0, 0, 255), 2) | ||
| cv2.putText( | ||
| image_annotated, | ||
| f"landing-pad {i+1}", | ||
| (x, y - 10), | ||
| cv2.FONT_HERSHEY_SIMPLEX, | ||
| 0.9, | ||
| (0, 0, 255), | ||
| 2, | ||
| ) | ||
|
|
||
| return True, detections, image_annotated | ||
|
|
||
| def run( | ||
| self, data: image_and_time.ImageAndTime | ||
| ) -> tuple[True, detections_and_time.DetectionsAndTime] | tuple[False, None]: | ||
| """ | ||
| Runs object detection on the provided image and returns the detections. | ||
|
|
||
| data: Image with a timestamp. | ||
Xierumeng marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| Return: Success and the detections. | ||
Xierumeng marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| """ | ||
|
|
||
| result, detections, image_annotated = self.detect_landing_pads_contours(data) | ||
|
|
||
| if not result: | ||
| return False, None | ||
|
|
||
| # Logging | ||
| if self.__filename_prefix != "": | ||
| filename = self.__filename_prefix + str(self.__counter) | ||
| self.__logger.save_image(image_annotated, filename) | ||
| self.__counter += 1 | ||
|
|
||
| if self.__show_annotations: | ||
Xierumeng marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| cv2.imshow("Annotated", image_annotated) # type: ignore | ||
|
|
||
| return True, detections | ||
Xierumeng marked this conversation as resolved.
Show resolved
Hide resolved
|
||
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,203 @@ | ||
| """ | ||
| Helper functions for `test_detect_target_contour.py`. | ||
|
|
||
| """ | ||
|
|
||
| import cv2 | ||
| import math | ||
| import numpy as np | ||
|
|
||
Xierumeng marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| from modules import detections_and_time | ||
| from modules import image_and_time | ||
|
|
||
Xierumeng marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| LANDING_PAD_COLOUR_BLUE = (100, 50, 50) # BGR | ||
|
|
||
|
|
||
| class LandingPadImageConfig: | ||
Xierumeng marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| """ | ||
| Represents the data required to define and generate a landing pad. | ||
| """ | ||
|
|
||
| def __init__( | ||
Xierumeng marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| self, | ||
| centre: tuple[int, int], | ||
| axis: tuple[int, int], | ||
| blur: bool, | ||
| angle: float, | ||
| ): | ||
| """ | ||
| centre: The pixel coordinates representing the centre of the landing pad. | ||
| axis: The pixel lengths of the semi-major axes of the ellipse. | ||
| blur: Indicates whether the landing pad should have a blur effect. | ||
| angle: The rotation angle of the landing pad in degrees clockwise, where 0.0 degrees | ||
| is where both semi major and minor are aligned with the x and y-axis respectively (0.0 <= angle <= 360.0). | ||
|
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. Mention only the semi-major axis, since the orientation of the semi-minor axis is a derived value. |
||
| """ | ||
| self.centre = centre | ||
| self.axis = axis | ||
| self.blur = blur | ||
| self.angle = angle | ||
|
|
||
|
|
||
| class NumpyImage: | ||
Xierumeng marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| """ | ||
| Holds the numpy array which represents an image. | ||
| """ | ||
|
|
||
| def __init__(self, image: np.ndarray): | ||
Xierumeng marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| """ | ||
| image: A numpy array that represents the image. | ||
| """ | ||
| self.image = image | ||
|
|
||
|
|
||
| class BoundingBox: | ||
Xierumeng marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| """ | ||
| Holds the data that define the generated bounding boxes. | ||
| """ | ||
|
|
||
| def __init__(self, top_left: tuple[float, float], bottom_right: tuple[float, float]): | ||
| """ | ||
| top_left: The pixel coordinates representing the top left corner of the bounding box on an image. | ||
| bottom_right: pixel coordinates representing the bottom right corner of the bounding box on an image. | ||
| """ | ||
| self.top_left = top_left | ||
| self.bottom_right = bottom_right | ||
|
|
||
|
|
||
| class InputImageAndTimeAndExpectedBoundingBoxes: | ||
| """ | ||
| Struct to hold the data needed to perform the tests. | ||
| """ | ||
|
|
||
| def __init__(self, image_and_time_data: image_and_time.ImageAndTime, bounding_box_list: list): | ||
| """ | ||
| image_and_time_data: ImageAndTime object containing the image and timestamp | ||
| bounding_box_list: A list that holds expected bounding box coordinates. | ||
| Given in the following format: | ||
| [conf, label, top_left_x, top_left_y, bottom_right_x, bottom_right_y] | ||
| """ | ||
| self.image_and_time_data = image_and_time_data | ||
| self.bounding_box_list = bounding_box_list | ||
|
|
||
|
|
||
| def add_blurred_landing_pad( | ||
| background: np.ndarray, landing_data: LandingPadImageConfig | ||
| ) -> NumpyImage: | ||
| """ | ||
| Blurs an image and adds a singular landing pad to the background. | ||
|
|
||
Xierumeng marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| background: A numpy image. | ||
| landing_data: Landing pad data for the landing pad to be blurred and added. | ||
|
|
||
| Returns: Image with the landing pad. | ||
| """ | ||
| x, y = background.shape[:2] | ||
|
|
||
| mask = np.zeros((x, y), np.uint8) | ||
| mask = cv2.ellipse( | ||
| mask, | ||
| landing_data.centre, | ||
| landing_data.axis, | ||
| landing_data.angle, | ||
| 0, | ||
| 360, | ||
| 255, | ||
| -1, | ||
| ) | ||
|
|
||
| mask = cv2.blur(mask, (25, 25), 7) | ||
|
|
||
| alpha = mask[:, :, np.newaxis] / 255.0 | ||
| # Brings the image back to its original colour. | ||
| fg = np.full(background.shape, LANDING_PAD_COLOUR_BLUE, dtype=np.uint8) | ||
|
|
||
| blended = (background * (1 - alpha) + fg * alpha).astype(np.uint8) | ||
| return NumpyImage(blended) | ||
|
|
||
|
|
||
| def draw_landing_pad( | ||
| image: np.ndarray, landing_data: LandingPadImageConfig | ||
| ) -> tuple[NumpyImage, BoundingBox]: | ||
| """ | ||
| Draws a single landing pad on the provided image and saves the bounding box coordinates to a text file. | ||
|
|
||
| image: The image to add a landing pad to. | ||
| landing_data: Landing pad data for the landing pad to be added. | ||
Xierumeng marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| Returns: Image with landing pad and the bounding box for the drawn landing pad. | ||
| """ | ||
| centre_x, centre_y = landing_data.centre | ||
| axis_x, axis_y = landing_data.axis | ||
| angle_in_rad = math.radians(landing_data.angle) | ||
|
|
||
| ux = axis_x * math.cos(angle_in_rad) | ||
| uy = axis_x * math.sin(angle_in_rad) | ||
|
|
||
| vx = axis_y * math.sin(angle_in_rad) | ||
| vy = axis_y * math.cos(angle_in_rad) | ||
|
|
||
| width = 2 * math.sqrt(ux**2 + vx**2) | ||
| height = 2 * math.sqrt(uy**2 + vy**2) | ||
|
|
||
| top_left = (max(centre_x - (0.5) * width, 0), max(centre_y - (0.5) * height, 0)) | ||
| bottom_right = ( | ||
| min(centre_x + (0.5) * width, image.shape[1]), | ||
| min(centre_y + (0.5) * height, image.shape[0]), | ||
| ) | ||
|
|
||
| bounding_box = BoundingBox(top_left, bottom_right) | ||
|
|
||
| if landing_data.blur: | ||
| image = add_blurred_landing_pad(image, landing_data) | ||
| return image, bounding_box | ||
|
|
||
| image = cv2.ellipse( | ||
| image, | ||
| landing_data.centre, | ||
| landing_data.axis, | ||
| landing_data.angle, | ||
| 0, | ||
| 360, | ||
| LANDING_PAD_COLOUR_BLUE, | ||
| -1, | ||
| ) | ||
| return NumpyImage(image), bounding_box | ||
|
|
||
|
|
||
| def create_test( | ||
| landing_list: list[LandingPadImageConfig], | ||
| ) -> InputImageAndTimeAndExpectedBoundingBoxes: | ||
| """ | ||
| Generates test cases given a data set. | ||
Xierumeng marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| landing_list: List of landing pad data to be generated. | ||
|
|
||
| Returns: The image and expected bounding box. | ||
| """ | ||
| image = np.full(shape=(1000, 2000, 3), fill_value=255, dtype=np.uint8) | ||
| confidence_and_label = [1, 0] | ||
|
|
||
| # List to hold the bounding boxes. | ||
| # boxes_list = [confidence, label, top_left_x, top_left_y, bottom_right_x, bottom_right_y] | ||
|
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. This is not true! |
||
| boxes_list = [] | ||
Xierumeng marked this conversation as resolved.
Show resolved
Hide resolved
Xierumeng marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| for landing_data in landing_list: | ||
| image_wrapper, bounding_box = draw_landing_pad(image, landing_data) | ||
| image = image_wrapper.image | ||
| boxes_list.append( | ||
| confidence_and_label + list(bounding_box.top_left + bounding_box.bottom_right) | ||
| ) | ||
|
|
||
| # Sorts by the area of the bounding box | ||
| boxes_list = sorted( | ||
| boxes_list, reverse=True, key=lambda box: abs((box[4] - box[2]) * (box[5] - box[3])) | ||
| ) | ||
|
|
||
| image = image.astype(np.uint8) | ||
| result, image_and_time_data = image_and_time.ImageAndTime.create(image) | ||
|
|
||
| assert result | ||
| assert image_and_time_data is not None | ||
|
|
||
| return InputImageAndTimeAndExpectedBoundingBoxes(image_and_time_data, boxes_list) | ||
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.