From 11b38b12d64bfdcc58de30d357ef51038b85d0aa Mon Sep 17 00:00:00 2001
From: liukuikun <24622904+Harold-lkk@users.noreply.github.com>
Date: Sat, 5 Mar 2022 15:02:54 +0800
Subject: [PATCH] [Fix] fix random may generate same value (#85)

---
 mmengine/data/base_data_element.py  |  71 ++++++++---------
 mmengine/data/base_data_sample.py   | 119 +++++++++++++---------------
 tests/test_data/test_data_sample.py |   8 ++
 3 files changed, 99 insertions(+), 99 deletions(-)

diff --git a/mmengine/data/base_data_element.py b/mmengine/data/base_data_element.py
index 5ab96f6f..ac5870bc 100644
--- a/mmengine/data/base_data_element.py
+++ b/mmengine/data/base_data_element.py
@@ -25,27 +25,26 @@ class BaseDataElement:
     types of ground truth labels or predictions.
     They are used as interfaces between different commopenets.
 
-
     The attributes in ``BaseDataElement`` are divided into two parts,
     the ``metainfo`` and the ``data`` respectively.
 
-        - ``metainfo``: Usually contains the
-          information about the image such as filename,
-          image_shape, pad_shape, etc. The attributes can be accessed or
-          modified by dict-like or object-like operations, such as
-          ``.``(for data access and modification) , ``in``, ``del``,
-          ``pop(str)``, ``get(str)``, ``metainfo_keys()``,
-          ``metainfo_values()``, ``metainfo_items()``, ``set_metainfo()``(for
-          set or change key-value pairs in metainfo).
-
-        - ``data``: Annotations or model predictions are
-          stored. The attributes can be accessed or modified by
-          dict-like or object-like operations, such as
-          ``.`` , ``in``, ``del``, ``pop(str)`` ``get(str)``, ``data_keys()``,
-          ``data_values()``, ``data_items()``. Users can also apply tensor-like
-          methods to all obj:``torch.Tensor`` in the ``data_fileds``,
-          such as ``.cuda()``, ``.cpu()``, ``.numpy()``, , ``.to()``
-          ``to_tensor()``, ``.detach()``, ``.numpy()``
+      - ``metainfo``: Usually contains the
+        information about the image such as filename,
+        image_shape, pad_shape, etc. The attributes can be accessed or
+        modified by dict-like or object-like operations, such as
+        ``.``(for data access and modification) , ``in``, ``del``,
+        ``pop(str)``, ``get(str)``, ``metainfo_keys()``,
+        ``metainfo_values()``, ``metainfo_items()``, ``set_metainfo()``(for
+        set or change key-value pairs in metainfo).
+
+      - ``data``: Annotations or model predictions are
+        stored. The attributes can be accessed or modified by
+        dict-like or object-like operations, such as
+        ``.`` , ``in``, ``del``, ``pop(str)`` ``get(str)``, ``data_keys()``,
+        ``data_values()``, ``data_items()``. Users can also apply tensor-like
+        methods to all obj:``torch.Tensor`` in the ``data_fileds``,
+        such as ``.cuda()``, ``.cpu()``, ``.numpy()``, , ``.to()``
+        ``to_tensor()``, ``.detach()``, ``.numpy()``
 
     Args:
         meta_info (dict, optional): A dict contains the meta information
@@ -57,24 +56,24 @@ class BaseDataElement:
     Examples:
         >>> from mmengine.data import BaseDataElement
         >>> gt_instances = BaseDataElement()
-
         >>> bboxes = torch.rand((5, 4))
         >>> scores = torch.rand((5,))
         >>> img_id = 0
         >>> img_shape = (800, 1333)
         >>> gt_instances = BaseDataElement(
-                metainfo=dict(img_id=img_id, img_shape=img_shape),
-                data=dict(bboxes=bboxes, scores=scores))
+        ...     metainfo=dict(img_id=img_id, img_shape=img_shape),
+        ...     data=dict(bboxes=bboxes, scores=scores))
         >>> gt_instances = BaseDataElement(dict(img_id=img_id,
-                                                img_shape=(H, W)))
-        # new
+        ...                                     img_shape=(H, W)))
+
+        >>> # new
         >>> gt_instances1 = gt_instance.new(
-                                metainfo=dict(img_id=1, img_shape=(640, 640)),
-                                data=dict(bboxes=torch.rand((5, 4)),
-                                          scores=torch.rand((5,))))
+        ...                     metainfo=dict(img_id=1, img_shape=(640, 640)),
+        ...                     data=dict(bboxes=torch.rand((5, 4)),
+        ...                               scores=torch.rand((5,))))
         >>> gt_instances2 = gt_instances1.new()
 
-        # add and process property
+        >>> # add and process property
         >>> gt_instances = BaseDataElement()
         >>> gt_instances.set_metainfo(dict(img_id=9, img_shape=(100, 100))
         >>> assert 'img_shape' in gt_instances.metainfo_keys()
@@ -82,14 +81,12 @@ class BaseDataElement:
         >>> assert 'img_shape' not in gt_instances.data_keys()
         >>> assert 'img_shape' in gt_instances.keys()
         >>> print(gt_instances.img_shape)
-
         >>> gt_instances.scores = torch.rand((5,))
         >>> assert 'scores' in gt_instances.data_keys()
         >>> assert 'scores' in gt_instances
         >>> assert 'scores' in gt_instances.keys()
         >>> assert 'scores' not in gt_instances.metainfo_keys()
         >>> print(gt_instances.scores)
-
         >>> gt_instances.bboxes = torch.rand((5, 4))
         >>> assert 'bboxes' in gt_instances.data_keys()
         >>> assert 'bboxes' in gt_instances
@@ -97,14 +94,14 @@ class BaseDataElement:
         >>> assert 'bboxes' not in gt_instances.metainfo_keys()
         >>> print(gt_instances.bboxes)
 
-        # delete and change property
+        >>> # delete and change property
         >>> gt_instances = BaseDataElement(
-             metainfo=dict(img_id=0, img_shape=(640, 640)),
-             data=dict(bboxes=torch.rand((6, 4)), scores=torch.rand((6,))))
+        ...  metainfo=dict(img_id=0, img_shape=(640, 640)),
+        ...  data=dict(bboxes=torch.rand((6, 4)), scores=torch.rand((6,))))
         >>> gt_instances.img_shape = (1280, 1280)
         >>> gt_instances.img_shape  # (1280, 1280)
         >>> gt_instances.bboxes = gt_instances.bboxes * 2
-        >>> gt_instances.get('img_shape', None)  # (640, 640)
+        >>> gt_instances.get('img_shape', None)  # (640, 640)
         >>> gt_instances.get('bboxes', None)    # 6x4 tensor
         >>> del gt_instances.img_shape
         >>> del gt_instances.bboxes
@@ -113,18 +110,18 @@ class BaseDataElement:
         >>> gt_instances.pop('img_shape', None)  # None
         >>> gt_instances.pop('bboxes', None)  # None
 
-        # Tensor-like
+        >>> # Tensor-like
         >>> cuda_instances = gt_instances.cuda()
         >>> cuda_instances = gt_instancess.to('cuda:0')
         >>> cpu_instances = cuda_instances.cpu()
         >>> cpu_instances = cuda_instances.to('cpu')
         >>> fp16_instances = cuda_instances.to(
-             device=None, dtype=torch.float16, non_blocking=False, copy=False,
-             memory_format=torch.preserve_format)
+        ...  device=None, dtype=torch.float16, non_blocking=False, copy=False,
+        ...  memory_format=torch.preserve_format)
         >>> cpu_instances = cuda_instances.detach()
         >>> np_instances = cpu_instances.numpy()
 
-        # print
+        >>> # print
         >>> img_meta = dict(img_shape=(800, 1196, 3), pad_shape=(800, 1216, 3))
         >>> instance_data = BaseDataElement(metainfo=img_meta)
         >>> instance_data.det_labels = torch.LongTensor([0, 1, 2, 3])
diff --git a/mmengine/data/base_data_sample.py b/mmengine/data/base_data_sample.py
index 4c6a2607..dd6fafd1 100644
--- a/mmengine/data/base_data_sample.py
+++ b/mmengine/data/base_data_sample.py
@@ -54,55 +54,52 @@ class BaseDataSample:
     Examples:
         >>> from mmengine.data import BaseDataElement, BaseDataSample
         >>> gt_instances = BaseDataSample()
-
         >>> bboxes = torch.rand((5, 4))
         >>> scores = torch.rand((5,))
         >>> img_id = 0
         >>> img_shape = (800, 1333)
         >>> gt_instances = BaseDataElement(
-                metainfo=dict(img_id=img_id, img_shape=img_shape),
-                data=dict(bboxes=bboxes, scores=scores))
+        ...     metainfo=dict(img_id=img_id, img_shape=img_shape),
+        ...     data=dict(bboxes=bboxes, scores=scores))
         >>> data = dict(gt_instances=gt_instances)
         >>> sample = BaseDataSample(
-                        metainfo=dict(img_id=img_id, img_shape=img_shape),
-                        data=data)
+        ...             metainfo=dict(img_id=img_id, img_shape=img_shape),
+        ...             data=data)
         >>> sample = BaseDataSample(dict(img_id=img_id,
-                                          img_shape=(H, W)))
-        # new
+        ...                               img_shape=(H, W)))
+
+        >>> # new
         >>> data1 = dict(bboxes=torch.rand((5, 4)),
                       scores=torch.rand((5,)))
         >>> metainfo1 = dict(img_id=1, img_shape=(640, 640)),
         >>> gt_instances1 = BaseDataElement(
-                metainfo=metainfo1,
-                data=data1)
+        ...     metainfo=metainfo1,
+        ...     data=data1)
         >>> sample1 = sample.new(
-                            metainfo=metainfo1
-                            data=dict(gt_instances1=gt_instances1)),
-
+        ...                 metainfo=metainfo1
+        ...                 data=dict(gt_instances1=gt_instances1)),
         >>> gt_instances2 = gt_instances1.new()
 
-        # property add and access
+        >>> # property add and access
         >>> sample = BaseDataSample()
         >>> gt_instances = BaseDataElement(
-                metainfo=dict(img_id=9, img_shape=(100, 100)),
-                data=dict(bboxes=torch.rand((5, 4)), scores=torch.rand((5,)))
+        ...     metainfo=dict(img_id=9, img_shape=(100, 100)),
+        ...     data=dict(bboxes=torch.rand((5, 4)), scores=torch.rand((5,)))
         >>> sample.set_metainfo(dict(img_id=9, img_shape=(100, 100))
         >>> assert 'img_shape' in sample.metainfo_keys()
         >>> assert 'img_shape' in sample
         >>> assert 'img_shape' not in sample.data_keys()
         >>> assert 'img_shape' in sample.keys()
         >>> print(sample.img_shape)
-
         >>> gt_instances.gt_instances = gt_instances
         >>> assert 'gt_instances' in sample.data_keys()
         >>> assert 'gt_instances' in sample
         >>> assert 'gt_instances' in sample.keys()
         >>> assert 'gt_instances' not in sample.metainfo_keys()
         >>> print(sample.gt_instances)
-
         >>> pred_instances = BaseDataElement(
-                metainfo=dict(img_id=9, img_shape=(100, 100)),
-                data=dict(bboxes=torch.rand((5, 4)), scores=torch.rand((5,))
+        ...     metainfo=dict(img_id=9, img_shape=(100, 100)),
+        ...     data=dict(bboxes=torch.rand((5, 4)), scores=torch.rand((5,))
         >>> sample.pred_instances = pred_instances
         >>> assert 'pred_instances' in sample.data_keys()
         >>> assert 'pred_instances' in sample
@@ -110,17 +107,17 @@ class BaseDataSample:
         >>> assert 'pred_instances' not in sample.metainfo_keys()
         >>> print(sample.pred_instances)
 
-        # property delete and change
+        >>> # property delete and change
         >>> metainfo=dict(img_id=0, img_shape=(640, 640)
         >>> gt_instances = BaseDataElement(
-             metainfo=metainfo),
-             data=dict(bboxes=torch.rand((6, 4)), scores=torch.rand((6,))))
+        ...  metainfo=metainfo),
+        ...  data=dict(bboxes=torch.rand((6, 4)), scores=torch.rand((6,))))
         >>> sample = BaseDataSample(metainfo=metainfo,
-                                    data=dict(gt_instances=gt_instances))
+        ...                         data=dict(gt_instances=gt_instances))
         >>> sample.img_shape = (1280, 1280)
         >>> sample.img_shape  # (1280, 1280)
         >>> sample.gt_instances = gt_instances
-        >>> sample.get('img_shape', None)  # (640, 640)
+        >>> sample.get('img_shape', None)  # (640, 640)
         >>> sample.get('gt_instances', None)
         >>> del sample.img_shape
         >>> del sample.gt_instances
@@ -129,23 +126,22 @@ class BaseDataSample:
         >>> sample.pop('img_shape', None)  # None
         >>> sample.pop('gt_instances', None)  # None
 
-        # Tensor-like
+        >>> # Tensor-like
         >>> cuda_sample = gt_instasamplences.cuda()
         >>> cuda_sample = gt_sample.to('cuda:0')
         >>> cpu_sample = cuda_sample.cpu()
         >>> cpu_sample = cuda_sample.to('cpu')
         >>> fp16_sample = cuda_sample.to(
-             device=None, dtype=torch.float16, non_blocking=False, copy=False,
-             memory_format=torch.preserve_format)
+        ...  device=None, dtype=torch.float16, non_blocking=False, copy=False,
+        ...  memory_format=torch.preserve_format)
         >>> cpu_sample = cuda_sample.detach()
         >>> np_sample = cpu_sample.numpy()
 
-        # print
+        >>> # print
         >>> metainfo = dict(img_shape=(800, 1196, 3))
         >>> gt_instances = BaseDataElement(
-             metainfo=metainfo,
-             data=dict(det_labels=torch.LongTensor([0, 1, 2, 3])))
-
+        ...  metainfo=metainfo,
+        ...  data=dict(det_labels=torch.LongTensor([0, 1, 2, 3])))
         >>> data = dict(gt_instances=gt_instances)
         >>> sample = BaseDataSample(metainfo=metainfo, data=data)
         >>> print(sample)
@@ -161,37 +157,36 @@ class BaseDataSample:
         ) at 0x7f9705daecd0>'
         ) at 0x7f981e41c550>'
 
-        # inheritance
+        >>> # inheritance
         >>> class DetDataSample(BaseDataSample):
-        >>>     proposals = property(
-        >>>         fget=partial(BaseDataSample.get_field, name='_proposals'),
-        >>>         fset=partial(
-        >>>             BaseDataSample.set_field,
-        >>>             name='_proposals',
-        >>>             dtype=BaseDataElement),
-        >>>         fdel=partial(BaseDataSample.del_field, name='_proposals'),
-        >>>         doc='Region proposals of an image')
-        >>>     gt_instances = property(
-        >>>         fget=partial(BaseDataSample.get_field,
-                                 name='_gt_instances'),
-        >>>         fset=partial(
-        >>>             BaseDataSample.set_field,
-        >>>             name='_gt_instances',
-        >>>             dtype=BaseDataElement),
-        >>>         fdel=partial(BaseDataSample.del_field,
-                                 name='_gt_instances'),
-        >>>         doc='Ground truth instances of an image')
-        >>>     pred_instances = property(
-        >>>         fget=partial(
-        >>>             BaseDataSample.get_field, name='_pred_instances'),
-        >>>         fset=partial(
-        >>>             BaseDataSample.set_field,
-        >>>             name='_pred_instances',
-        >>>             dtype=BaseDataElement),
-        >>>         fdel=partial(
-        >>>             BaseDataSample.del_field, name='_pred_instances'),
-        >>>         doc='Predicted instances of an image')
-
+        ...     proposals = property(
+        ...         fget=partial(BaseDataSample.get_field, name='_proposals'),
+        ...         fset=partial(
+        ...             BaseDataSample.set_field,
+        ...             name='_proposals',
+        ...             dtype=BaseDataElement),
+        ...         fdel=partial(BaseDataSample.del_field, name='_proposals'),
+        ...         doc='Region proposals of an image')
+        ...     gt_instances = property(
+        ...         fget=partial(BaseDataSample.get_field,
+        ...                      name='_gt_instances'),
+        ...         fset=partial(
+        ...             BaseDataSample.set_field,
+        ...             name='_gt_instances',
+        ...             dtype=BaseDataElement),
+        ...         fdel=partial(BaseDataSample.del_field,
+        ...                      name='_gt_instances'),
+        ...         doc='Ground truth instances of an image')
+        ...     pred_instances = property(
+        ...         fget=partial(
+        ...             BaseDataSample.get_field, name='_pred_instances'),
+        ...         fset=partial(
+        ...             BaseDataSample.set_field,
+        ...             name='_pred_instances',
+        ...             dtype=BaseDataElement),
+        ...         fdel=partial(
+        ...             BaseDataSample.del_field, name='_pred_instances'),
+        ...         doc='Predicted instances of an image')
         >>> det_sample = DetDataSample()
         >>> proposals = BaseDataElement(data=dict(bboxes=torch.rand((5, 4))))
         >>> det_sample.proposals = proposals
@@ -200,7 +195,7 @@ class BaseDataSample:
         >>> del det_sample.proposals
         >>> assert 'proposals' not in det_sample
         >>> with self.assertRaises(AssertionError):
-                det_sample.proposals = torch.rand((5, 4))
+        ...     det_sample.proposals = torch.rand((5, 4))
     """
 
     def __init__(self,
diff --git a/tests/test_data/test_data_sample.py b/tests/test_data/test_data_sample.py
index cc940073..e03508fb 100644
--- a/tests/test_data/test_data_sample.py
+++ b/tests/test_data/test_data_sample.py
@@ -185,6 +185,14 @@ class TestBaseDataSample(TestCase):
         instances = BaseDataSample(metainfo, data)
 
         new_metainfo, new_data = self.setup_data()
+        # avoid generating same metainfo
+        while True:
+            if new_metainfo['img_id'] == metainfo['img_id'] or new_metainfo[
+                    'img_shape'] == metainfo['img_shape']:
+                new_metainfo, new_data = self.setup_data()
+            else:
+                break
+
         instances.gt_instances = new_data['gt_instances']
         instances.pred_instances = new_data['pred_instances']
 
-- 
GitLab