-
Notifications
You must be signed in to change notification settings - Fork 37
implemented label in cluster estimation #231
base: main
Are you sure you want to change the base?
Changes from 18 commits
3205913
dabc8cb
a101d17
571cc13
61dcfa1
9c70b00
0b89706
f5c4b90
f687063
c0ea3e0
50ef6ff
42c9fbe
6c8e724
b702a76
34224fe
6c02dc8
2106bbe
1692e16
769ba6e
8618361
d3c0c89
d12aa34
b8155cb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,6 +4,8 @@ | |
| covariance of each landing pad estimation. | ||
| """ | ||
|
|
||
| # pylint: disable=duplicate-code | ||
|
|
||
| import numpy as np | ||
| import sklearn | ||
| import sklearn.datasets | ||
|
|
@@ -20,6 +22,20 @@ class ClusterEstimation: | |
| works by predicting 'cluster centres' from groups of closely placed landing pad | ||
| detections. | ||
|
|
||
| ATTRIBUTES | ||
| ---------- | ||
| min_activation_threshold: int | ||
| Minimum total data points before model runs. | ||
|
|
||
| min_new_points_to_run: int | ||
| Minimum number of new data points that must be collected before running model. | ||
|
|
||
| random_state: int | ||
| Seed for randomizer, to get consistent results. | ||
|
|
||
| local_logger: Logger | ||
| For logging error and debug messages. | ||
|
|
||
Xierumeng marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| METHODS | ||
| ------- | ||
| run() | ||
|
|
@@ -42,6 +58,10 @@ class ClusterEstimation: | |
| Removes any cluster with covariances much higher than the lowest covariance value. | ||
| """ | ||
|
|
||
| # pylint: disable=too-many-instance-attributes | ||
|
|
||
| # pylint: disable=too-many-instance-attributes | ||
|
|
||
Xierumeng marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| __create_key = object() | ||
|
|
||
| # VGMM Hyperparameters | ||
|
|
@@ -167,6 +187,10 @@ def run( | |
| List containing ObjectInWorld objects, containing position and covariance value. | ||
| None if conditions not met and model not ran or model failed to converge. | ||
| """ | ||
| # in use, all detections will have the same label, so the | ||
Xierumeng marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| # first element's label was arbitrarily selected | ||
| label = detections[0].label | ||
|
|
||
| # Store new input data | ||
| self.__current_bucket += self.__convert_detections_to_point(detections) | ||
|
|
||
|
|
@@ -199,6 +223,7 @@ def run( | |
|
|
||
| # Filter out all clusters after __WEIGHT_DROP_THRESHOLD weight drop occurs | ||
| viable_clusters = [model_output[0]] | ||
| print(f"len(model_output) = {len(model_output)}") | ||
Xierumeng marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| for i in range(1, len(model_output)): | ||
| if model_output[i][1] / model_output[i - 1][1] < self.__WEIGHT_DROP_THRESHOLD: | ||
| break | ||
|
|
@@ -211,21 +236,18 @@ def run( | |
| model_output = self.__filter_by_covariances(model_output) | ||
|
|
||
| # Create output list of remaining valid clusters | ||
| detections_in_world = [] | ||
| objects_in_world = [] | ||
| for cluster in model_output: | ||
| result, landing_pad = object_in_world.ObjectInWorld.create( | ||
| cluster[0][0], | ||
| cluster[0][1], | ||
| cluster[2], | ||
| cluster[0][0], cluster[0][1], cluster[2], label | ||
| ) | ||
|
|
||
| if result: | ||
| detections_in_world.append(landing_pad) | ||
| objects_in_world.append(landing_pad) | ||
| else: | ||
| self.__logger.warning("Failed to create ObjectInWorld object") | ||
|
|
||
| self.__logger.info(detections_in_world) | ||
| return True, detections_in_world | ||
| self.__logger.error("Failed to create ObjectInWorld object") | ||
| return False, None | ||
| return True, objects_in_world | ||
Xierumeng marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| def __decide_to_run(self, run_override: bool) -> bool: | ||
| """ | ||
|
|
@@ -288,7 +310,7 @@ def __sort_by_weights( | |
| @staticmethod | ||
| def __convert_detections_to_point( | ||
| detections: "list[detection_in_world.DetectionInWorld]", | ||
| ) -> "list[tuple[float, float]]": | ||
| ) -> "list[tuple[float, float, int]]": | ||
|
||
| """ | ||
| Convert DetectionInWorld input object to a list of points- (x,y) positions, to store. | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,177 @@ | ||
| """ | ||
| Take in bounding box coordinates from Geolocation and use to estimate landing pad locations. | ||
| Returns an array of classes, each containing the x coordinate, y coordinate, and spherical | ||
| covariance of each landing pad estimation. | ||
Xierumeng marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| """ | ||
|
|
||
| from .. import detection_in_world | ||
| from .. import object_in_world | ||
| from ..common.modules.logger import logger | ||
| from . import cluster_estimation | ||
Xierumeng marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
|
|
||
| class ClusterEstimationByLabel: | ||
| """ | ||
| Estimate landing pad locations based on landing pad ground detection. Estimation | ||
| works by predicting 'cluster centres' from groups of closely placed landing pad | ||
| detections. | ||
| ATTRIBUTES | ||
| ---------- | ||
| min_activation_threshold: int | ||
| Minimum total data points before model runs. Must be at least max_num_components. | ||
| min_new_points_to_run: int | ||
| Minimum number of new data points that must be collected before running model. | ||
| max_num_components: int | ||
| Max number of real landing pads. Must be at least 1. | ||
| random_state: int | ||
| Seed for randomizer, to get consistent results. | ||
| local_logger: Logger | ||
| For logging error and debug messages. | ||
| METHODS | ||
| ------- | ||
| run() | ||
| Take in list of object detections and return dictionary of labels to | ||
| to corresponging clusters of estimated object locations if number of | ||
| detections is sufficient, or if manually forced to run. | ||
Xierumeng marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| """ | ||
|
|
||
| # pylint: disable=too-many-instance-attributes | ||
Xierumeng marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| __create_key = object() | ||
|
|
||
| @classmethod | ||
| def create( | ||
| cls, | ||
| min_activation_threshold: int, | ||
| min_new_points_to_run: int, | ||
| max_num_components: int, | ||
| random_state: int, | ||
Aleksa-M marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| local_logger: logger.Logger, | ||
| ) -> "tuple[bool, ClusterEstimationByLabel | None]": | ||
Xierumeng marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| """ | ||
| Data requirement conditions for estimation model to run. | ||
Xierumeng marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| """ | ||
|
|
||
| # At least 1 point for model to fit | ||
| if min_activation_threshold < max_num_components: | ||
| return False, None | ||
|
|
||
| if min_new_points_to_run < 0: | ||
| return False, None | ||
|
|
||
| if max_num_components < 1: | ||
| return False, None | ||
|
|
||
| if random_state < 0: | ||
| return False, None | ||
Xierumeng marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| return True, ClusterEstimationByLabel( | ||
|
Member
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. Please check |
||
| cls.__create_key, | ||
| min_activation_threshold, | ||
| min_new_points_to_run, | ||
| max_num_components, | ||
| random_state, | ||
| local_logger, | ||
| ) | ||
|
|
||
| def __init__( | ||
| self, | ||
| class_private_create_key: object, | ||
| min_activation_threshold: int, | ||
| min_new_points_to_run: int, | ||
| max_num_components: int, | ||
| random_state: int, | ||
| local_logger: logger.Logger, | ||
| ) -> None: | ||
| """ | ||
| Private constructor, use create() method. | ||
| """ | ||
| assert ( | ||
| class_private_create_key is ClusterEstimationByLabel.__create_key | ||
| ), "Use create() method" | ||
|
|
||
| # Requirements to decide to run | ||
Xierumeng marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| self.__min_activation_threshold = min_activation_threshold | ||
| self.__min_new_points_to_run = min_new_points_to_run | ||
| self.__max_num_components = max_num_components | ||
| self.__random_state = random_state | ||
| self.__local_logger = local_logger | ||
|
|
||
| # Cluster model corresponding to each label | ||
| # Each cluster estimation object stores the detections given to in its __all_points bucket across runs | ||
| self.__label_to_cluster_estimation_model: dict[ | ||
| int, cluster_estimation.ClusterEstimation | ||
| ] = {} | ||
|
|
||
| def run( | ||
| self, | ||
| input_detections: "list[detection_in_world.DetectionInWorld]", | ||
| run_override: bool, | ||
| ) -> "tuple[True, dict[int, list[object_in_world.ObjectInWorld]]] | tuple[False, None]": | ||
Xierumeng marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| """ | ||
| Take in list of detections and return list of estimated object locations | ||
| if number of detections is sufficient, or if manually forced to run. | ||
| PARAMETERS | ||
| ---------- | ||
| input_detections: list[DetectionInWorld] | ||
| List containing DetectionInWorld objects which holds real-world positioning data to run | ||
| clustering on. | ||
| run_override: bool | ||
| Forces ClusterEstimation to predict if data is available, regardless of any other | ||
| requirements. | ||
| RETURNS | ||
| ------- | ||
| model_ran: bool | ||
| True if ClusterEstimation object successfully ran its estimation model, False otherwise. | ||
| labels_to_object_clusters: dict[int, list[object_in_world.ObjectInWorld] or None. | ||
| Dictionary where the key is a label and the value is a list of all cluster detections with that label | ||
Xierumeng marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| """ | ||
| label_to_detections: dict[int, list[detection_in_world.DetectionInWorld]] = {} | ||
|
Member
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 comment saying sorting detections by label |
||
| # Sorting detections by label | ||
Xierumeng marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| for detection in input_detections: | ||
| if not detection.label in label_to_detections: | ||
| label_to_detections[detection.label] = [] | ||
| label_to_detections[detection.label].append(detection) | ||
Xierumeng marked this conversation as resolved.
Show resolved
Hide resolved
Xierumeng marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| labels_to_object_clusters: dict[int, list[object_in_world.ObjectInWorld]] = {} | ||
Xierumeng marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| for label, detections in label_to_detections.items(): | ||
| # create cluster estimation for label if it doesn't exist | ||
Xierumeng marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| if not label in self.__label_to_cluster_estimation_model: | ||
Aleksa-M marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| result, cluster_model = cluster_estimation.ClusterEstimation.create( | ||
| self.__min_activation_threshold, | ||
| self.__min_new_points_to_run, | ||
| self.__max_num_components, | ||
| self.__random_state, | ||
| self.__local_logger, | ||
| ) | ||
| if not result: | ||
| self.__local_logger.error( | ||
| f"Failed to create cluster estimation for label {label}" | ||
| ) | ||
| return False, None | ||
| self.__label_to_cluster_estimation_model[label] = cluster_model | ||
Xierumeng marked this conversation as resolved.
Show resolved
Hide resolved
Xierumeng marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| # runs cluster estimation for specific label | ||
Xierumeng marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
Xierumeng marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
Xierumeng marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| result, clusters = self.__label_to_cluster_estimation_model[label].run( | ||
Aleksa-M marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| detections, | ||
| run_override, | ||
| ) | ||
| if not result: | ||
| self.__local_logger.error( | ||
| f"Failed to run cluster estimation model for label {label}" | ||
| ) | ||
| return False, None | ||
| if not label in labels_to_object_clusters: | ||
Xierumeng marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| labels_to_object_clusters[label] = [] | ||
| labels_to_object_clusters[label] += clusters | ||
|
||
|
|
||
| return True, labels_to_object_clusters | ||
Aleksa-M marked this conversation as resolved.
Show resolved
Hide resolved
Xierumeng marked this conversation as resolved.
Show resolved
Hide resolved
Xierumeng marked this conversation as resolved.
Show resolved
Hide resolved
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. Change this worker to use the new |
Uh oh!
There was an error while loading. Please reload this page.