File size: 6,268 Bytes
5fd3fc6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
from __future__ import absolute_import, division

import numpy as np

from .operations import bezier_curve
from .operations import bresenham as bresenham_function


def scribbles2mask(scribbles,
                   output_resolution,
                   bezier_curve_sampling=False,
                   nb_points=1000,
                   bresenham=True,
                   default_value=-1,
                   single_object=False):
    """ Convert the scribbles data into a mask.

    # Arguments
        scribbles: Dictionary. Scribbles in the default format.
        output_resolution: Tuple. Output resolution (H, W).
        bezier_curve_sampling: Boolean. Weather to sample first the returned
            scribbles using bezier curve or not.
        nb_points: Integer. If `bezier_curve_sampling` is `True` set the number
            of points to sample from the bezier curve.
        bresenham: Boolean. Whether to compute bresenham algorithm for the
            scribbles lines.
        default_value: Integer. Default value for the pixels which do not belong
            to any scribble.

    # Returns
        ndarray: Array with the mask of the scribbles with the index of the
            object ids. The shape of the returned array is (B x H x W) by
            default or (H x W) if `only_annotated_frame==True`.
    """
    if len(output_resolution) != 2:
        raise ValueError(
            'Invalid output resolution: {}'.format(output_resolution))
    for r in output_resolution:
        if r < 1:
            raise ValueError(
                'Invalid output resolution: {}'.format(output_resolution))

    nb_frames = len(scribbles['scribbles'])
    masks = np.full(
        (nb_frames,) + output_resolution, default_value, dtype=np.int)

    size_array = np.asarray(output_resolution[::-1], dtype=np.float) - 1

    for f in range(nb_frames):
        sp = scribbles['scribbles'][f]
        for p in sp:
            path = p['path']
            if single_object:
                # Simulate having only one object
                obj_id = 1
            else:
                obj_id = p['object_id']
            path = np.asarray(path, dtype=np.float)
            if bezier_curve_sampling:
                path = bezier_curve(path, nb_points=nb_points)
            path *= size_array
            path = path.astype(np.int)

            if bresenham:
                path = bresenham_function(path)
            m = masks[f]

            m[path[:, 1], path[:, 0]] = obj_id
            masks[f] = m

    return masks


def scribbles2points(scribbles_data, output_resolution=None):
    """ Convert the given scribbles into a list of points and object ids.

    # Arguments
        scribbles_data: Dictionary. Scribbles in the default format
        output_resolution: Tuple. Output resolution (H, W) to scale the
            points.
            If None given, the points will be floats as a fraction of height
            and width.

    # Returns
        (ndarray, ndarray): Returns (X, Y) where X is a list of points from the
            scribbles represented in the output_resolution with shape (N x 3)
            with N being the total number of points on all the scribbles. The three
            coordinates given correspond the the frame number, height and width,
            respectively.
            Y is the object id for each given point with shape (N,).
    """
    scribbles = scribbles_data['scribbles']

    paths, object_ids = [], []

    for frame, s in enumerate(scribbles):
        for l in s:
            # p = l['path']
            coordinates = [[frame] + point for point in l['path']]
            paths += coordinates
            object_ids += [l['object_id']] * len(l['path'])

    paths = np.asarray(paths, dtype=np.float)
    object_ids = np.asarray(object_ids, dtype=np.int)

    if output_resolution:
        h, w = output_resolution
        img_size = np.asarray([1, h - 1, w - 1], dtype=np.float)
        paths *= img_size
        paths = paths.astype(np.int)

    return paths, object_ids


def fuse_scribbles(scribbles_a, scribbles_b):
    """ Fuse two scribbles in the default format.

    # Arguments
        scribbles_a: Dictionary. Default representation of scribbles A.
        scribbles_b: Dictionary. Default representation of scribbles B.

    # Returns
        dict: Returns a dictionary with scribbles A and B fused.
    """

    if scribbles_a['sequence'] != scribbles_b['sequence']:
        raise ValueError('Scribbles to fuse are not from the same sequence')
    if len(scribbles_a['scribbles']) != len(scribbles_b['scribbles']):
        raise ValueError('Scribbles does not have the same number of frames')

    scribbles = dict(scribbles_a)
    nb_frames = len(scribbles['scribbles'])

    for i in range(nb_frames):
        scribbles['scribbles'][i] += scribbles_b['scribbles'][i]

    return scribbles


def is_empty(scribbles_data):
    """ Checks whether the given scribble has any non-empty line.

    # Arguments
        scribbles_data (dict): Scribble in the default format

    # Returns
        bool: Whether the scribble is empty or not.
    """
    scribbles = scribbles_data['scribbles']
    has_lines = [len(s) > 0 for s in scribbles]
    return not any(has_lines)


def annotated_frames(scribbles_data):
    """ Finds which frames have a scribble.

    # Arguments
        scribbles_data (dict): Scribble in the default format.

    # Returns
        list: Number of the frames that contain at least one scribble.
    """
    scribbles = scribbles_data['scribbles']
    frames_list = [i for i, s in enumerate(scribbles) if s]
    return frames_list


def annotated_frames_object(scribbles_data, object_id):
    """ Computes which frames have a scribble for a certain object.

    # Arguments
        scribbles_data (dict): Scribble in the default format.
        object_id (int): Id of the object of interest.

    # Returns
        dict: Number of the frames that contain at least one scribble.
    """
    frames_list = []
    scribbles = scribbles_data['scribbles']
    for ii, scribble_frame in enumerate(scribbles):
        for scribble in scribble_frame:
            if scribble['object_id'] == object_id:
                frames_list.append(ii)
                break
    return frames_list