File size: 5,177 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
from __future__ import absolute_import, division

import numpy as np
from PIL import ImageDraw


def _pascal_color_map(N=256, normalized=False):
    """
    Python implementation of the color map function for the PASCAL VOC data set.
    Official Matlab version can be found in the PASCAL VOC devkit
    http://host.robots.ox.ac.uk/pascal/VOC/voc2012/index.html#devkit
    """

    def bitget(byteval, idx):
        return (byteval & (1 << idx)) != 0

    dtype = 'float32' if normalized else 'uint8'
    cmap = np.zeros((N, 3), dtype=dtype)
    for i in range(N):
        r = g = b = 0
        c = i
        for j in range(8):
            r = r | (bitget(c, 0) << 7 - j)
            g = g | (bitget(c, 1) << 7 - j)
            b = b | (bitget(c, 2) << 7 - j)
            c = c >> 3

        cmap[i] = np.array([r, g, b])

    cmap = cmap / 255 if normalized else cmap
    return cmap


def plot_scribble(ax, scribble, frame, output_size=None, **kwargs):
    """ Plot scribbles into an axis.

    # Arguments
        ax: Matplotlib Axis. Axis where to plot the scribble lines.
        scribbles: Scribble. Scribble to plot.
        frame: Integer. Frame of the scribble to plot.
        output_size: Tuple. Image size to scale the scribble points `(H, W)`.
        **kwargs: Dictionary. Additional parameters to pass at the
            `ax.plot(**kwargs)` method.

    # Returns
        matplotlib.axis: Returns the given axis with the scribbles plotted on
            it.
    """
    scribbles = scribble['scribbles']
    if frame >= len(scribbles):
        raise ValueError('Frame value not valid')

    cmap = _pascal_color_map(normalized=True)

    frame_scribbles = scribbles[frame]

    for line in frame_scribbles:
        path, obj_id = line['path'], line['object_id']
        path = np.asarray(path, dtype=np.float32)
        color = cmap[obj_id]

        if output_size:
            img_size = np.asarray(output_size, dtype=np.float32)
            img_size -= 1
            path *= img_size

        ax.plot(*path.T, color=color, **kwargs)

    return ax


def draw_scribble(img, scribble, frame, output_size=None, width=5):
    """ Draw scribbles into a PIL Image.

    # Arguments
        img: PIL Image. Image where to draw the scribbles.
        scribbles: Scribble. Scribble to plot.
        frame: Integer. Frame of the scribble to plot.
        output_size: Tuple. Image size to scale the scribble points `(H, W)`.
        width: Integer. Width of the drawed lines.

    # Returns
        PIL Image: Returns the original image `img` with the scribble draw on
            it.
    """
    scribbles = scribble['scribbles']
    if frame >= len(scribbles):
        raise ValueError('Frame value not valid')

    cmap = _pascal_color_map(normalized=False)

    frame_scribbles = scribbles[frame]

    draw = ImageDraw.Draw(img)

    for line in frame_scribbles:
        path, obj_id = line['path'], line['object_id']
        path = np.asarray(path, dtype=np.float32)
        color = cmap[obj_id]

        if output_size:
            img_size = np.asarray(output_size, dtype=np.float32)
            img_size -= 1
            path *= img_size
        path = path.ravel().tolist()
        draw.line(path, fill=tuple(color), width=width)

    del draw

    return img


def overlay_mask(im, ann, alpha=0.5, colors=None, contour_thickness=None):
    """ Overlay mask over image.

    This function allows you to overlay a mask over an image with some
    transparency.

    # Arguments
        im: Numpy Array. Array with the image. The shape must be (H, W, 3) and
            the pixels must be represented as `np.uint8` data type.
        ann: Numpy Array. Array with the mask. The shape must be (H, W) and the
            values must be intergers
        alpha: Float. Proportion of alpha to apply at the overlaid mask.
        colors: Numpy Array. Optional custom colormap. It must have shape (N, 3)
            being N the maximum number of colors to represent.
        contour_thickness: Integer. Thickness of each object index contour draw
            over the overlay. This function requires to have installed the
            package `opencv-python`.

    # Returns
        Numpy Array: Image of the overlay with shape (H, W, 3) and data type
            `np.uint8`.
    """
    im, ann = np.asarray(im, dtype=np.uint8), np.asarray(ann, dtype=np.int)
    if im.shape[:-1] != ann.shape:
        raise ValueError('First two dimensions of `im` and `ann` must match')
    if im.shape[-1] != 3:
        raise ValueError('im must have three channels at the 3 dimension')

    colors = colors or _pascal_color_map()
    colors = np.asarray(colors, dtype=np.uint8)

    mask = colors[ann]
    fg = im * alpha + (1 - alpha) * mask

    img = im.copy()
    img[ann > 0] = fg[ann > 0]

    if contour_thickness:  # pragma: no cover
        import cv2
        for obj_id in np.unique(ann[ann > 0]):
            contours = cv2.findContours((ann == obj_id).astype(
                np.uint8), cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)[-2:]
            cv2.drawContours(img, contours[0], -1, colors[obj_id].tolist(),
                             contour_thickness)
    return img