utils.py 5.08 KB
Newer Older
dingchang's avatar
dingchang committed
1
# Copyright (c) OpenMMLab. All rights reserved.
VVsssssk's avatar
VVsssssk committed
2
import numpy as np
3
from mmcv.transforms import LoadImageFromFile
VVsssssk's avatar
VVsssssk committed
4
from pyquaternion import Quaternion
5

6
# yapf: disable
zhangshilong's avatar
zhangshilong committed
7
8
9
10
11
12
13
from mmdet3d.datasets.transforms import (LoadAnnotations3D,
                                         LoadImageFromFileMono3D,
                                         LoadMultiViewImageFromFiles,
                                         LoadPointsFromFile,
                                         LoadPointsFromMultiSweeps,
                                         MultiScaleFlipAug3D, Pack3DDetInputs,
                                         PointSegClassMapping)
14
# yapf: enable
15
from mmdet3d.registry import TRANSFORMS
16
17


18
19
20
def is_loading_function(transform):
    """Judge whether a transform function is a loading function.

21
22
23
    Note: `MultiScaleFlipAug3D` is a wrapper for multiple pipeline functions,
    so we need to search if its inner transforms contain any loading function.

24
25
26
27
    Args:
        transform (dict | :obj:`Pipeline`): A transform config or a function.

    Returns:
28
        bool: Whether it is a loading function. None means can't judge.
29
30
31
32
33
            When transform is `MultiScaleFlipAug3D`, we return None.
    """
    # TODO: use more elegant way to distinguish loading modules
    loading_functions = (LoadImageFromFile, LoadPointsFromFile,
                         LoadAnnotations3D, LoadMultiViewImageFromFiles,
jshilong's avatar
jshilong committed
34
35
                         LoadPointsFromMultiSweeps, Pack3DDetInputs,
                         LoadImageFromFileMono3D, PointSegClassMapping)
36
    if isinstance(transform, dict):
37
        obj_cls = TRANSFORMS.get(transform['type'])
38
39
40
41
        if obj_cls is None:
            return False
        if obj_cls in loading_functions:
            return True
zhangshilong's avatar
zhangshilong committed
42
        if obj_cls in (MultiScaleFlipAug3D, ):
43
44
45
46
            return None
    elif callable(transform):
        if isinstance(transform, loading_functions):
            return True
zhangshilong's avatar
zhangshilong committed
47
        if isinstance(transform, (MultiScaleFlipAug3D)):
48
49
50
51
            return None
    return False


52
53
54
55
def get_loading_pipeline(pipeline):
    """Only keep loading image, points and annotations related configuration.

    Args:
56
57
        pipeline (list[dict] | list[:obj:`Pipeline`]):
            Data pipeline configs or list of pipeline functions.
58
59

    Returns:
60
61
        list[dict] | list[:obj:`Pipeline`]): The new pipeline list with only
            keep loading image, points and annotations related configuration.
62
63

    Examples:
zhangshilong's avatar
zhangshilong committed
64
        >>> transforms = [
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
        ...    dict(type='LoadPointsFromFile',
        ...         coord_type='LIDAR', load_dim=4, use_dim=4),
        ...    dict(type='LoadImageFromFile'),
        ...    dict(type='LoadAnnotations3D',
        ...         with_bbox=True, with_label_3d=True),
        ...    dict(type='Resize',
        ...         img_scale=[(640, 192), (2560, 768)], keep_ratio=True),
        ...    dict(type='RandomFlip3D', flip_ratio_bev_horizontal=0.5),
        ...    dict(type='PointsRangeFilter',
        ...         point_cloud_range=point_cloud_range),
        ...    dict(type='ObjectRangeFilter',
        ...         point_cloud_range=point_cloud_range),
        ...    dict(type='PointShuffle'),
        ...    dict(type='Normalize', **img_norm_cfg),
        ...    dict(type='Pad', size_divisor=32),
        ...    dict(type='DefaultFormatBundle3D', class_names=class_names),
        ...    dict(type='Collect3D',
        ...         keys=['points', 'img', 'gt_bboxes_3d', 'gt_labels_3d'])
        ...    ]
        >>> expected_pipelines = [
        ...    dict(type='LoadPointsFromFile',
        ...         coord_type='LIDAR', load_dim=4, use_dim=4),
        ...    dict(type='LoadImageFromFile'),
        ...    dict(type='LoadAnnotations3D',
        ...         with_bbox=True, with_label_3d=True),
        ...    dict(type='DefaultFormatBundle3D', class_names=class_names),
        ...    dict(type='Collect3D',
        ...         keys=['points', 'img', 'gt_bboxes_3d', 'gt_labels_3d'])
        ...    ]
94
        >>> assert expected_pipelines == \
zhangshilong's avatar
zhangshilong committed
95
        ...        get_loading_pipeline(transforms)
96
    """
97
98
99
100
101
102
103
104
105
106
107
108
109
    loading_pipeline = []
    for transform in pipeline:
        is_loading = is_loading_function(transform)
        if is_loading is None:  # MultiScaleFlipAug3D
            # extract its inner pipeline
            if isinstance(transform, dict):
                inner_pipeline = transform.get('transforms', [])
            else:
                inner_pipeline = transform.transforms.transforms
            loading_pipeline.extend(get_loading_pipeline(inner_pipeline))
        elif is_loading:
            loading_pipeline.append(transform)
    assert len(loading_pipeline) > 0, \
110
111
        'The data pipeline in your config file must include ' \
        'loading step.'
112
    return loading_pipeline
113
114


VVsssssk's avatar
VVsssssk committed
115
116
117
118
119
120
121
122
123
def convert_quaternion_to_matrix(quaternion: list,
                                 translation: list = None) -> list:
    """Compute a transform matrix by given quaternion and translation
    vector."""
    result = np.eye(4)
    result[:3, :3] = Quaternion(quaternion).rotation_matrix
    if translation is not None:
        result[:3, 3] = np.array(translation)
    return result.astype(np.float32).tolist()