From ce04d9fd676167d1c9bb14c8cc5731f24e287316 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Grzegorz=20G=C5=82owacki?= Date: Thu, 21 Nov 2024 13:56:13 +0000 Subject: [PATCH] deepcopy with sharing --- .../perception_eval/common/deepcopy.py | 38 +++++++++++++++++++ .../evaluation/result/object_result.py | 4 ++ .../result/perception_frame_result.py | 19 +--------- perception_eval/perception_eval/tool/utils.py | 3 +- 4 files changed, 44 insertions(+), 20 deletions(-) create mode 100644 perception_eval/perception_eval/common/deepcopy.py diff --git a/perception_eval/perception_eval/common/deepcopy.py b/perception_eval/perception_eval/common/deepcopy.py new file mode 100644 index 00000000..5ab4cbe4 --- /dev/null +++ b/perception_eval/perception_eval/common/deepcopy.py @@ -0,0 +1,38 @@ +from copy import deepcopy + + +# https://stackoverflow.com/a/24621200/4732868 +def deepcopy_with_sharing(obj, shared_attribute_names, memo=None): + """ + Deepcopy an object, except for a given list of attributes, which should + be shared between the original object and its copy. + + obj is some object + shared_attribute_names: A list of strings identifying the attributes that + should be shared between the original and its copy. + memo is the dictionary passed into __deepcopy__. Ignore this argument if + not calling from within __deepcopy__. + """ + assert isinstance(shared_attribute_names, (list, tuple)) + shared_attributes = {k: getattr(obj, k) for k in shared_attribute_names} + + if hasattr(obj, "__deepcopy__"): + # Do hack to prevent infinite recursion in call to deepcopy + deepcopy_method = obj.__deepcopy__ + obj.__deepcopy__ = None + + for attr in shared_attribute_names: + del obj.__dict__[attr] + + clone = deepcopy(obj) + + for attr, val in shared_attributes.items(): + setattr(obj, attr, val) + setattr(clone, attr, val) + + if hasattr(obj, "__deepcopy__"): + # Undo hack + obj.__deepcopy__ = deepcopy_method + del clone.__deepcopy__ + + return clone diff --git a/perception_eval/perception_eval/evaluation/result/object_result.py b/perception_eval/perception_eval/evaluation/result/object_result.py index d4c6e9df..6adf39d2 100644 --- a/perception_eval/perception_eval/evaluation/result/object_result.py +++ b/perception_eval/perception_eval/evaluation/result/object_result.py @@ -25,6 +25,7 @@ from perception_eval.common import DynamicObject from perception_eval.common import DynamicObject2D from perception_eval.common import ObjectType +from perception_eval.common.deepcopy import deepcopy_with_sharing from perception_eval.common.evaluation_task import EvaluationTask from perception_eval.common.label import LabelType from perception_eval.common.label import TrafficLightLabel @@ -106,6 +107,9 @@ def __init__( self.iou_3d = None self.plane_distance = None + def __deepcopy__(self, memo): + return deepcopy_with_sharing(self, shared_attribute_names = ['estimated_object', 'ground_truth_object'], memo=memo) + def get_status( self, matching_mode: MatchingMode, diff --git a/perception_eval/perception_eval/evaluation/result/perception_frame_result.py b/perception_eval/perception_eval/evaluation/result/perception_frame_result.py index 9925fa01..373b64a3 100644 --- a/perception_eval/perception_eval/evaluation/result/perception_frame_result.py +++ b/perception_eval/perception_eval/evaluation/result/perception_frame_result.py @@ -13,7 +13,7 @@ # limitations under the License. from __future__ import annotations -from copy import copy +from copy import copy, deepcopy from typing import Dict from typing import List from typing import Optional @@ -89,23 +89,6 @@ def __init__( transforms=frame_ground_truth.transforms, ) - def copy_with_shallow_results(self) -> PerceptionFrameResult: - """ - Create a shallow copy of the current PerceptionFrameResult instance. - - This method creates a new instance of PerceptionFrameResult by performing a shallow copy - of the current instance. The `metrics_score` and `pass_fail_result` attributes are also - shallow copied to the new instance. - - Returns: - PerceptionFrameResult: A new instance of PerceptionFrameResult with shallow copied attributes. - """ - - new_instance = copy(self) - new_instance.metrics_score = copy(self.metrics_score) - new_instance.pass_fail_result = copy(self.pass_fail_result) - return new_instance - def evaluate_frame( self, previous_result: Optional[PerceptionFrameResult] = None, diff --git a/perception_eval/perception_eval/tool/utils.py b/perception_eval/perception_eval/tool/utils.py index 32cc6a70..3c7ab2df 100644 --- a/perception_eval/perception_eval/tool/utils.py +++ b/perception_eval/perception_eval/tool/utils.py @@ -475,8 +475,7 @@ def filter_frame_by_distance( Returns: PerceptionFrameResult: Filtered frame results. """ - ret_frame = frame.copy_with_shallow_results() - + ret_frame = deepcopy(frame) if min_distance is not None: min_distance_list = [min_distance] * len(ret_frame.target_labels) else: