forked from pmeletis/panoptic_parts
-
Notifications
You must be signed in to change notification settings - Fork 0
/
format.py
198 lines (166 loc) · 7.07 KB
/
format.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
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
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
"""
Utility functions for reading and writing to our hierarchical panoptic format (see REAMDE).
Tensorflow and Pytorch are optional frameworks.
"""
from enum import Enum
import functools
import numpy as np
TENSORFLOW_IMPORTED = False
try:
import tensorflow as tf # pylint: disable=import-error
TENSORFLOW_IMPORTED = True
except ModuleNotFoundError:
pass
TORCH_IMPORTED = False
try:
import torch # pylint: disable=import-error
TORCH_IMPORTED = True
except ModuleNotFoundError:
pass
# Functions that start with underscore (_) should be considered as internal.
# All other functions belong to the public API.
# Arguments and functions defined with the preffix experimental_ may be changed
# and are not backward-compatible.
# PUBLIC_API = [decode_uids, encode_ids]
class Frameworks(Enum):
PYTHON = "python"
NUMPY = "numpy"
TENSORFLOW = "tensorflow"
TORCH = "torch"
def _decode_uids_functors_and_checking(uids):
# this functions makes the decode_uids more clear
# required frameworks: Python and NumPy
if isinstance(uids, np.ndarray):
if uids.dtype != np.int32:
raise TypeError(f'{uids.dtype} is an unsupported dtype of np.ndarray uids.')
where = np.where
ones_like = np.ones_like
divmod_ = np.divmod
maximum = np.maximum
dtype = np.int32
return where, ones_like, divmod_, maximum, dtype
if isinstance(uids, (int, np.int32)):
where = lambda cond, true_br, false_br: true_br if cond else false_br
ones_like = lambda the_like: type(the_like)(1)
divmod_ = divmod
maximum = max
dtype = (lambda x: x) if isinstance(uids, int) else np.int32
return where, ones_like, divmod_, maximum, dtype
# optional frameworks: Tensorflow and Pytorch
if TENSORFLOW_IMPORTED:
if isinstance(uids, tf.Tensor):
if uids.dtype != tf.int32:
raise TypeError(f'{uids.dtype} is an unsupported dtype of tf.Tensor uids.')
where = tf.where
ones_like = tf.ones_like
divmod_ = lambda x, y: (x // y, x % y)
maximum = tf.maximum
dtype = lambda x: x
return where, ones_like, divmod_, maximum, dtype
if TORCH_IMPORTED:
if isinstance(uids, torch.Tensor):
where = torch.where
ones_like = torch.ones_like
divmod_ = lambda x, y: (x // y, x % y)
maximum = torch.max
dtype = functools.partial(torch.tensor, dtype=torch.int32)
return where, ones_like, divmod_, maximum, dtype
raise TypeError(f'{type(uids)} is an unsupported type of uids.')
def decode_uids(uids,
experimental_return_sids_iids=False,
experimental_return_sids_pids=False):
"""
Given the universal ids `uids` according to the hierarchical format described
in README, this function returns element-wise
the semantic ids (sids), instance ids (iids), and part ids (pids).
Optionally (experimental for now), returns the sids_iids and sids_pids as well.
sids_iids represent the semantic-instance-level (two-level) labeling,
e.g., sids_iids(Cityscapes-Panoptic-Parts) = Cityscapes-Original.
sids_pids = sids * 100 + max(pids, 0) and represent the semantic-part-level labeling,
e.g., sids_pids(23_003_04) = 23_04.
Examples:
decode_uids(23) = (23, -1, -1)
decode_uids(23003) = (23, 3, -1)
decode_uids(2300304) = (23, 3, 4)
Args:
uids: tf.Tensor of dtype tf.int32 and arbitrary shape,
or np.ndarray of dtype np.int32 and arbitrary shape,
or torch.tensor of dtype np.int32 and arbitrary shape,
or Python int,
or np.int32 integer,
with elements according to hierarchical format (see README).
Return:
sids, iids, pids: same type and shape as uids, with -1 for not relevant pixels.
sids will have no -1.
iids will have -1 for pixels labeled with sids only.
pids will have -1 for pixels labeled with sids or sids_iids only.
if experimental_return_sids_iids:
sids_iids: same type and shape as uids, will have no -1.
if experimental_return_sids_pids:
sids_pids: same type and shape as uids, will have no -1.
"""
where, ones_like, divmod_, maximum, dtype = _decode_uids_functors_and_checking(uids)
# explanation for using dtype and np.asarray in this function:
# dtype: numpy implicitly converts Python int literals in np.int64, we need np.int32
# np.asarray: numpy implicitly converts ndarray with one element to np.int32 (which is not
# ndarray), moreover dtypes are implicitly converted to np.int64 for arithmetic operations
# pad uids to uniform 7-digit length
uids_padded = where(uids <= 99_999,
where(uids <= 99, uids * dtype(10**5), uids * dtype(10**2)),
uids)
# split uids to components (sids, iids, pids) from right to left
sids_iids, pids = divmod_(uids_padded, dtype(10**2))
sids, iids = divmod_(sids_iids, dtype(10**3))
invalid_ids = ones_like(uids) * dtype(-1)
# set invalid ids
iids = where(uids <= 99, invalid_ids, iids)
pids = where(uids <= 99_999, invalid_ids, pids)
if isinstance(uids, np.ndarray):
sids = np.asarray(sids, dtype=np.int32)
returns = (sids, iids, pids)
if experimental_return_sids_iids:
sids_iids = where(uids <= 99_999, uids, sids_iids)
returns += (sids_iids,)
if experimental_return_sids_pids:
sids_pids = sids * dtype(10**2) + maximum(pids, dtype(0))
if isinstance(uids, np.ndarray):
sids_pids = np.asarray(sids_pids, dtype=np.int32)
returns += (sids_pids,)
return returns
def encode_ids(sids, iids, pids):
"""
Given semantic ids (sids), instance ids (iids), and part ids (pids)
this function encodes them element-wise to uids
according to the hierarchical format described in README.
This function is the opposite of decode_uids, i.e.,
uids = encode_ids(decode_uids(uids)).
Note: this function is still not fully tested.
Args:
sids, iids, pids: all of the same type with -1 for non-relevant pixels with
elements according to hierarchical format (see README). Can be:
tf.Tensor of dtype tf.int32 and arbitrary shape,
tf.ndarray of dtype np.int32 and arbitrary shape,
Python int,
np.int32 int.
Return:
uids: same type and shape as the args according to hierarchical format (see README).
"""
if type(sids) is not type(iids) and type(iids) is not type(pids):
raise ValueError(
f"All arguments must have the same type, not {(*map(type, (sids, iids, pids)),)}")
if isinstance(sids, np.ndarray):
if sids.dtype != np.int32:
raise TypeError(f'{sids.dtype} is an unsupported dtype of np.ndarray uids.')
where = np.where
elif isinstance(sids, tf.Tensor):
if sids.dtype != tf.int32:
raise TypeError(f'{sids.dtype} is an unsupported dtype of tf.Tensor uids.')
where = tf.where
elif isinstance(sids, (int, np.int32, np.int64)):
# assert all([0 <= sids <= 99, 0 <= iids <= 999, 0 <= pids <= 99]), f"{(sids, iids, pids)}"
where = lambda cond, true_br, false_br: true_br if cond else false_br
else:
raise TypeError(f'{type(sids)} is an unsupported type of ids.')
sids_iids = where(iids < 0, sids, sids * 10**3 + iids)
uids = where(pids < 0, sids_iids, sids_iids * 10**2 + pids)
return uids