sunrgbd_utils.py 8.38 KB
Newer Older
1
2
# Modified from
# https://github.com/facebookresearch/votenet/blob/master/sunrgbd/sunrgbd_utils.py
3
4
5
6
# Copyright (c) Facebook, Inc. and its affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
7
"""Provides Python helper function to read My SUNRGBD dataset.
yinchimaoliang's avatar
yinchimaoliang committed
8
9
10
11
12
13
14

Author: Charles R. Qi
Date: October, 2017

Updated by Charles R. Qi
Date: December, 2018
Note: removed basis loading.
15
"""
yinchimaoliang's avatar
yinchimaoliang committed
16
17
import cv2
import numpy as np
wangtai's avatar
wangtai committed
18
from scipy import io as sio
yinchimaoliang's avatar
yinchimaoliang committed
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

type2class = {
    'bed': 0,
    'table': 1,
    'sofa': 2,
    'chair': 3,
    'toilet': 4,
    'desk': 5,
    'dresser': 6,
    'night_stand': 7,
    'bookshelf': 8,
    'bathtub': 9
}
class2type = {type2class[t]: t for t in type2class}


def flip_axis_to_camera(pc):
36
37
38
39
40
    """Flip axis to camera.

    Flip X-right,Y-forward,Z-up to X-right,Y-down,Z-forward.

    Args:
liyinhao's avatar
liyinhao committed
41
        pc (np.ndarray): points in depth axis.
42

43
    Returns:
liyinhao's avatar
liyinhao committed
44
        np.ndarray: points in camera  axis.
45
    """
yinchimaoliang's avatar
yinchimaoliang committed
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
    pc2 = np.copy(pc)
    pc2[:, [0, 1, 2]] = pc2[:, [0, 2, 1]]  # cam X,Y,Z = depth X,-Z,Y
    pc2[:, 1] *= -1
    return pc2


def flip_axis_to_depth(pc):
    pc2 = np.copy(pc)
    pc2[:, [0, 1, 2]] = pc2[:, [0, 2, 1]]  # depth X,Y,Z = cam X,Z,-Y
    pc2[:, 2] *= -1
    return pc2


class SUNObject3d(object):

    def __init__(self, line):
        data = line.split(' ')
        data[1:] = [float(x) for x in data[1:]]
        self.classname = data[0]
        self.xmin = data[1]
        self.ymin = data[2]
        self.xmax = data[1] + data[3]
        self.ymax = data[2] + data[4]
        self.box2d = np.array([self.xmin, self.ymin, self.xmax, self.ymax])
        self.centroid = np.array([data[5], data[6], data[7]])
        self.unused_dimension = np.array([data[8], data[9], data[10]])
        self.width = data[8]
        self.length = data[9]
        self.height = data[10]
        self.orientation = np.zeros((3, ))
        self.orientation[0] = data[11]
        self.orientation[1] = data[12]
        self.heading_angle = -1 * np.arctan2(self.orientation[1],
                                             self.orientation[0])


class SUNRGBD_Calibration(object):
wangtai's avatar
wangtai committed
83
    """Calibration matrices and utils.
84
85

    We define five coordinate system in SUN RGBD dataset:
yinchimaoliang's avatar
yinchimaoliang committed
86
87

        camera coodinate:
88
            Z is forward, Y is downward, X is rightward.
yinchimaoliang's avatar
yinchimaoliang committed
89
90

        depth coordinate:
91
            Just change axis order and flip up-down axis from camera coord.
yinchimaoliang's avatar
yinchimaoliang committed
92
93

        upright depth coordinate: tilted depth coordinate by Rtilt such that
94
95
            Z is gravity direction, Z is up-axis, Y is forward,
            X is right-ward.
yinchimaoliang's avatar
yinchimaoliang committed
96
97

        upright camera coordinate:
98
            Just change axis order and flip up-down axis from upright.
yinchimaoliang's avatar
yinchimaoliang committed
99
100
101
102
103
104
105
106
107
108
109
110
111
                depth coordinate

        image coordinate:
            ----> x-axis (u)
           |
           v
            y-axis (v)

        depth points are stored in upright depth coordinate.
        labels for 3d box (basis, centroid, size) are in upright
            depth coordinate.
        2d boxes are in image coordinate

112
113
114
115
116
117
        We generate frustum point cloud and 3d box
        in upright camera coordinate.

    Args:
        calib_filepath(str): Path of the calib file.
    """
yinchimaoliang's avatar
yinchimaoliang committed
118
119
120
121
122
123
124
125
126
127
128
129
130

    def __init__(self, calib_filepath):
        lines = [line.rstrip() for line in open(calib_filepath)]
        Rtilt = np.array([float(x) for x in lines[0].split(' ')])
        self.Rtilt = np.reshape(Rtilt, (3, 3), order='F')
        K = np.array([float(x) for x in lines[1].split(' ')])
        self.K = np.reshape(K, (3, 3), order='F')
        self.f_u = self.K[0, 0]
        self.f_v = self.K[1, 1]
        self.c_u = self.K[0, 2]
        self.c_v = self.K[1, 2]

    def project_upright_depth_to_camera(self, pc):
131
132
133
        """Convert pc coordinate from depth to image.

        Args:
liyinhao's avatar
liyinhao committed
134
            pc (np.ndarray): Point cloud in depth coordinate.
135
136

        Returns:
liyinhao's avatar
liyinhao committed
137
            pc (np.ndarray): Point cloud in camera coordinate.
138
        """
yinchimaoliang's avatar
yinchimaoliang committed
139
140
141
142
143
144
        # Project upright depth to depth coordinate
        pc2 = np.dot(np.transpose(self.Rtilt), np.transpose(pc[:,
                                                               0:3]))  # (3,n)
        return flip_axis_to_camera(np.transpose(pc2))

    def project_upright_depth_to_image(self, pc):
145
146
147
        """Convert pc coordinate from depth to image.

        Args:
liyinhao's avatar
liyinhao committed
148
            pc (np.ndarray): Point cloud in depth coordinate.
149
150

        Returns:
liyinhao's avatar
liyinhao committed
151
152
            np.ndarray: [N, 2] uv.
            np.ndarray: [n,] depth.
153
        """
yinchimaoliang's avatar
yinchimaoliang committed
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
        pc2 = self.project_upright_depth_to_camera(pc)
        uv = np.dot(pc2, np.transpose(self.K))  # (n,3)
        uv[:, 0] /= uv[:, 2]
        uv[:, 1] /= uv[:, 2]
        return uv[:, 0:2], pc2[:, 2]

    def project_image_to_camera(self, uv_depth):
        n = uv_depth.shape[0]
        x = ((uv_depth[:, 0] - self.c_u) * uv_depth[:, 2]) / self.f_u
        y = ((uv_depth[:, 1] - self.c_v) * uv_depth[:, 2]) / self.f_v
        pts_3d_camera = np.zeros((n, 3))
        pts_3d_camera[:, 0] = x
        pts_3d_camera[:, 1] = y
        pts_3d_camera[:, 2] = uv_depth[:, 2]
        return pts_3d_camera


def rotz(t):
liyinhao's avatar
liyinhao committed
172
173
174
    """Rotation about the z-axis.

    Args:
liyinhao's avatar
liyinhao committed
175
        t (float): Heading angle.
liyinhao's avatar
liyinhao committed
176
177

    Returns:
liyinhao's avatar
liyinhao committed
178
        np.ndarray: Transforation matrix
liyinhao's avatar
liyinhao committed
179
    """
yinchimaoliang's avatar
yinchimaoliang committed
180
181
182
183
184
185
    c = np.cos(t)
    s = np.sin(t)
    return np.array([[c, -s, 0], [s, c, 0], [0, 0, 1]])


def transform_from_rot_trans(R, t):
liyinhao's avatar
liyinhao committed
186
187
188
    """Transforation matrix from rotation matrix and translation vector.

    Args:
liyinhao's avatar
liyinhao committed
189
190
        R (np.ndarray): Rotation matrix.
        t (np.ndarray): Translation vector.
liyinhao's avatar
liyinhao committed
191
192

    Returns:
liyinhao's avatar
liyinhao committed
193
        np.ndarray: Transforation matrix.
liyinhao's avatar
liyinhao committed
194
    """
yinchimaoliang's avatar
yinchimaoliang committed
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
    R = R.reshape(3, 3)
    t = t.reshape(3, 1)
    return np.vstack((np.hstack([R, t]), [0, 0, 0, 1]))


def read_sunrgbd_label(label_filename):
    lines = [line.rstrip() for line in open(label_filename)]
    objects = [SUNObject3d(line) for line in lines]
    return objects


def load_image(img_filename):
    return cv2.imread(img_filename)


def load_depth_points(depth_filename):
    depth = np.loadtxt(depth_filename)
    return depth


def load_depth_points_mat(depth_filename):
    depth = sio.loadmat(depth_filename)['instance']
    return depth


def in_hull(p, hull):
    from scipy.spatial import Delaunay
    if not isinstance(hull, Delaunay):
        hull = Delaunay(hull)
    return hull.find_simplex(p) >= 0


def extract_pc_in_box3d(pc, box3d):
228
229
230
    """Extract point cloud in box3d.

    Args:
liyinhao's avatar
liyinhao committed
231
232
        pc (np.ndarray): [N, 3] Point cloud.
        box3d (np.ndarray): [8,3] 3d box.
233
234

    Returns:
liyinhao's avatar
liyinhao committed
235
236
        np.ndarray: Selected point cloud.
        np.ndarray: Indices of selected point cloud.
237
    """
yinchimaoliang's avatar
yinchimaoliang committed
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
    box3d_roi_inds = in_hull(pc[:, 0:3], box3d)
    return pc[box3d_roi_inds, :], box3d_roi_inds


def my_compute_box_3d(center, size, heading_angle):
    R = rotz(-1 * heading_angle)
    l, w, h = size
    x_corners = [-l, l, l, -l, -l, l, l, -l]
    y_corners = [w, w, -w, -w, w, w, -w, -w]
    z_corners = [h, h, h, h, -h, -h, -h, -h]
    corners_3d = np.dot(R, np.vstack([x_corners, y_corners, z_corners]))
    corners_3d[0, :] += center[0]
    corners_3d[1, :] += center[1]
    corners_3d[2, :] += center[2]
    return np.transpose(corners_3d)


def compute_box_3d(obj, calib):
wangtai's avatar
wangtai committed
256
257
    """Takes an object and a projection matrix (P) and projects the 3d bounding
    box into the image plane.
258
259

    Args:
liyinhao's avatar
liyinhao committed
260
261
        obj (SUNObject3d): Instance of SUNObject3d.
        calib (SUNRGBD_Calibration): Instance of SUNRGBD_Calibration.
262
263

    Returns:
liyinhao's avatar
liyinhao committed
264
        np.ndarray: [8,2] array in image coord.
265
266
        corners_3d: [8,3] array in in upright depth coord.
    """
yinchimaoliang's avatar
yinchimaoliang committed
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
    center = obj.centroid

    # compute rotational matrix around yaw axis
    R = rotz(-1 * obj.heading_angle)

    # 3d bounding box dimensions
    length = obj.length  # along heading arrow
    width = obj.width  # perpendicular to heading arrow
    height = obj.height

    # rotate and translate 3d bounding box
    x_corners = [
        -length, length, length, -length, -length, length, length, -length
    ]
    y_corners = [width, width, -width, -width, width, width, -width, -width]
    z_corners = [
        height, height, height, height, -height, -height, -height, -height
    ]
    corners_3d = np.dot(R, np.vstack([x_corners, y_corners, z_corners]))
    corners_3d[0, :] += center[0]
    corners_3d[1, :] += center[1]
    corners_3d[2, :] += center[2]

    # project the 3d bounding box into the image plane
    corners_2d, _ = calib.project_upright_depth_to_image(
        np.transpose(corners_3d))
    return corners_2d, np.transpose(corners_3d)