detection.py 18.9 KB
Newer Older
C
chengduoZH 已提交
1
#   Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserved.
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
All layers just related to the detection neural network.
"""

from ..layer_helper import LayerHelper
C
chengduoZH 已提交
19 20
from ..param_attr import ParamAttr
from ..framework import Variable
C
chengduoZH 已提交
21
from ..nets import img_conv_with_bn
C
chengduoZH 已提交
22 23 24 25
from tensor import concat
from ops import reshape
from nn import transpose
import math
26

C
chengduoZH 已提交
27 28
__all__ = [
    'detection_output',
C
chengduoZH 已提交
29
    'prior_box',
C
chengduoZH 已提交
30 31
    'multi_box_head',
]
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46


def detection_output(scores,
                     loc,
                     prior_box,
                     prior_box_var,
                     background_label=0,
                     nms_threshold=0.3,
                     nms_top_k=400,
                     keep_top_k=200,
                     score_threshold=0.01,
                     nms_eta=1.0):
    """
    **Detection Output Layer**

C
chengduoZH 已提交
47
    This layer applies the NMS to the output of network and computes the
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
    predict bounding box location. The output's shape of this layer could
    be zero if there is no valid bounding box.

    Args:
        scores(Variable): A 3-D Tensor with shape [N, C, M] represents the
            predicted confidence predictions. N is the batch size, C is the
            class number, M is number of bounding boxes. For each category
            there are total M scores which corresponding M bounding boxes.
        loc(Variable): A 3-D Tensor with shape [N, M, 4] represents the
            predicted locations of M bounding bboxes. N is the batch size,
            and each bounding box has four coordinate values and the layout
            is [xmin, ymin, xmax, ymax].
        prior_box(Variable): A 2-D Tensor with shape [M, 4] holds M boxes,
            each box is represented as [xmin, ymin, xmax, ymax],
            [xmin, ymin] is the left top coordinate of the anchor box,
            if the input is image feature map, they are close to the origin
            of the coordinate system. [xmax, ymax] is the right bottom
            coordinate of the anchor box.
        prior_box_var(Variable): A 2-D Tensor with shape [M, 4] holds M group
            of variance.
        background_label(float): The index of background label,
            the background label will be ignored. If set to -1, then all
            categories will be considered.
        nms_threshold(float): The threshold to be used in NMS.
        nms_top_k(int): Maximum number of detections to be kept according
            to the confidences aftern the filtering detections based on
            score_threshold.
        keep_top_k(int): Number of total bboxes to be kept per image after
            NMS step. -1 means keeping all bboxes after NMS step.
        score_threshold(float): Threshold to filter out bounding boxes with
            low confidence score. If not provided, consider all boxes.
        nms_eta(float): The parameter for adaptive NMS.

    Returns:
        The detected bounding boxes which are a Tensor.

    Examples:
        .. code-block:: python

        pb = layers.data(name='prior_box', shape=[10, 4],
                         append_batch_size=False, dtype='float32')
        pbv = layers.data(name='prior_box_var', shape=[10, 4],
                          append_batch_size=False, dtype='float32')
        loc = layers.data(name='target_box', shape=[21, 4],
                          append_batch_size=False, dtype='float32')
        scores = layers.data(name='scores', shape=[2, 21, 10],
                          append_batch_size=False, dtype='float32')
        nmsed_outs = fluid.layers.detection_output(scores=scores,
                                       loc=loc,
                                       prior_box=pb,
                                       prior_box_var=pbv)
    """

    helper = LayerHelper("detection_output", **locals())
    decoded_box = helper.create_tmp_variable(dtype=loc.dtype)
    helper.append_op(
        type="box_coder",
        inputs={
            'PriorBox': prior_box,
            'PriorBoxVar': prior_box_var,
            'TargetBox': loc
        },
        outputs={'OutputBox': decoded_box},
        attrs={'code_type': 'decode_center_size'})
    nmsed_outs = helper.create_tmp_variable(dtype=decoded_box.dtype)

    helper.append_op(
        type="multiclass_nms",
        inputs={'Scores': scores,
                'BBoxes': decoded_box},
        outputs={'Out': nmsed_outs},
        attrs={
            'background_label': 0,
            'nms_threshold': nms_threshold,
            'nms_top_k': nms_top_k,
            'keep_top_k': keep_top_k,
            'score_threshold': score_threshold,
            'nms_eta': 1.0
        })
    return nmsed_outs
C
chengduoZH 已提交
128 129


C
chengduoZH 已提交
130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
def prior_box(inputs,
              image,
              min_ratio,
              max_ratio,
              aspect_ratios,
              base_size,
              steps=None,
              step_w=None,
              step_h=None,
              offset=0.5,
              variance=[0.1, 0.1, 0.1, 0.1],
              flip=False,
              clip=False,
              min_sizes=None,
              max_sizes=None,
              name=None):
C
chengduoZH 已提交
146 147 148
    """
    **Prior_boxes**

149 150 151 152
    Generate prior boxes for SSD(Single Shot MultiBox Detector)
    algorithm. The details of this algorithm, please refer the
    section 2.2 of SSD paper (SSD: Single Shot MultiBox Detector)
    <https://arxiv.org/abs/1512.02325>`_ .
C
chengduoZH 已提交
153

C
chengduoZH 已提交
154
    Args:
155 156 157 158
       inputs(list): The list of input Variables, the format
            of all Variables is NCHW.
       image(Variable): The input image data of PriorBoxOp,
            the layout is NCHW.
C
chengduoZH 已提交
159 160
       min_ratio(int): the min ratio of generated prior boxes.
       max_ratio(int): the max ratio of generated prior boxes.
161 162 163 164 165 166 167 168 169 170
       aspect_ratios(list): the aspect ratios of generated prior
            boxes. The length of input and aspect_ratios must be equal.
       base_size(int): the base_size is used to get min_size
            and max_size according to min_ratio and max_ratio.
       step_w(list, optional, default=None): Prior boxes step
            across width. If step_w[i] == 0.0, the prior boxes step
            across width of the inputs[i] will be automatically calculated.
       step_h(list, optional, default=None): Prior boxes step
            across height, If step_h[i] == 0.0, the prior boxes
            step across height of the inputs[i] will be automatically calculated.
C
chengduoZH 已提交
171 172 173
       offset(float, optional, default=0.5): Prior boxes center offset.
       variance(list, optional, default=[0.1, 0.1, 0.1, 0.1]): the variances
            to be encoded in prior boxes.
174 175 176 177 178 179 180 181 182 183
       flip(bool, optional, default=False): Whether to flip
            aspect ratios.
       clip(bool, optional, default=False): Whether to clip
            out-of-boundary boxes.
       min_sizes(list, optional, default=None): If `len(inputs) <=2`,
            min_sizes must be set up, and the length of min_sizes
            should equal to the length of inputs.
       max_sizes(list, optional, default=None): If `len(inputs) <=2`,
            max_sizes must be set up, and the length of min_sizes
            should equal to the length of inputs.
C
chengduoZH 已提交
184
       name(str, optional, None): Name of the prior box layer.
C
chengduoZH 已提交
185

C
chengduoZH 已提交
186
    Returns:
187 188 189 190 191 192
        boxes(Variable): the output prior boxes of PriorBoxOp.
             The layout is [num_priors, 4]. num_priors is the total
             box count of each position of inputs.
        Variances(Variable): the expanded variances of PriorBoxOp.
             The layout is [num_priors, 4]. num_priors is the total
             box count of each position of inputs
C
chengduoZH 已提交
193

C
chengduoZH 已提交
194 195
    Examples:
        .. code-block:: python
C
chengduoZH 已提交
196

C
chengduoZH 已提交
197
          prior_box(
C
chengduoZH 已提交
198 199 200 201 202
             inputs = [conv1, conv2, conv3, conv4, conv5, conv6],
             image = data,
             min_ratio = 20, # 0.20
             max_ratio = 90, # 0.90
             offset = 0.5,
C
chengduoZH 已提交
203
             base_size = 300,
C
chengduoZH 已提交
204
             variance = [0.1,0.1,0.1,0.1],
C
chengduoZH 已提交
205
             aspect_ratios = [[2.], [2., 3.], [2., 3.], [2., 3.], [2.], [2.]],
C
chengduoZH 已提交
206 207 208
             flip=True,
             clip=True)
    """
C
chengduoZH 已提交
209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247

    def _prior_box_(input,
                    image,
                    min_sizes,
                    max_sizes,
                    aspect_ratios,
                    variance,
                    flip=False,
                    clip=False,
                    step_w=0.0,
                    step_h=0.0,
                    offset=0.5,
                    name=None):
        helper = LayerHelper("prior_box", **locals())
        dtype = helper.input_dtype()

        box = helper.create_tmp_variable(dtype)
        var = helper.create_tmp_variable(dtype)
        helper.append_op(
            type="prior_box",
            inputs={"Input": input,
                    "Image": image},
            outputs={"Boxes": box,
                     "Variances": var},
            attrs={
                'min_sizes': min_sizes,
                'max_sizes': max_sizes,
                'aspect_ratios': aspect_ratios,
                'variances': variance,
                'flip': flip,
                'clip': clip,
                'step_w': step_w,
                'step_h': step_h,
                'offset': offset
            })
        return box, var

    def _reshape_with_axis_(input, axis=1):
        if not (axis > 0 and axis < len(input.shape)):
248 249 250 251 252
            raise ValueError("The axis should be smaller than "
                             "the arity of input and bigger than 0.")
        new_shape = [
            -1, reduce(lambda x, y: x * y, input.shape[axis:len(input.shape)])
        ]
C
chengduoZH 已提交
253
        out = reshape(x=input, shape=new_shape)
C
chengduoZH 已提交
254 255
        return out

C
chengduoZH 已提交
256 257 258
    assert isinstance(inputs, list), 'inputs should be a list.'
    num_layer = len(inputs)

C
chengduoZH 已提交
259 260 261 262 263 264
    if num_layer <= 2:
        assert min_sizes is not None and max_sizes is not None
        assert len(min_sizes) == num_layer and len(max_sizes) == num_layer
    else:
        min_sizes = []
        max_sizes = []
C
chengduoZH 已提交
265 266 267 268 269 270 271
        step = int(math.floor(((max_ratio - min_ratio)) / (num_layer - 2)))
        for ratio in xrange(min_ratio, max_ratio + 1, step):
            min_sizes.append(base_size * ratio / 100.)
            max_sizes.append(base_size * (ratio + step) / 100.)
        min_sizes = [base_size * .10] + min_sizes
        max_sizes = [base_size * .20] + max_sizes

C
chengduoZH 已提交
272 273 274 275 276 277
    if aspect_ratios:
        if not (isinstance(aspect_ratios, list) and
                len(aspect_ratios) == num_layer):
            raise ValueError(
                'aspect_ratios should be list and the length of inputs '
                'and aspect_ratios should be the same.')
C
chengduoZH 已提交
278
    if step_h:
C
chengduoZH 已提交
279 280 281 282
        if not (isinstance(step_h, list) and len(step_h) == num_layer):
            raise ValueError(
                'step_h should be list and the length of inputs and '
                'step_h should be the same.')
C
chengduoZH 已提交
283
    if step_w:
C
chengduoZH 已提交
284 285 286 287
        if not (isinstance(step_w, list) and len(step_w) == num_layer):
            raise ValueError(
                'step_w should be list and the length of inputs and '
                'step_w should be the same.')
C
chengduoZH 已提交
288
    if steps:
C
chengduoZH 已提交
289 290 291 292
        if not (isinstance(steps, list) and len(steps) == num_layer):
            raise ValueError(
                'steps should be list and the length of inputs and '
                'step_w should be the same.')
C
chengduoZH 已提交
293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310
        step_w = steps
        step_h = steps

    box_results = []
    var_results = []
    for i, input in enumerate(inputs):
        min_size = min_sizes[i]
        max_size = max_sizes[i]
        aspect_ratio = []
        if not isinstance(min_size, list):
            min_size = [min_size]
        if not isinstance(max_size, list):
            max_size = [max_size]
        if aspect_ratios:
            aspect_ratio = aspect_ratios[i]
            if not isinstance(aspect_ratio, list):
                aspect_ratio = [aspect_ratio]

C
chengduoZH 已提交
311 312 313 314
        box, var = _prior_box_(input, image, min_size, max_size, aspect_ratio,
                               variance, flip, clip, step_w[i]
                               if step_w else 0.0, step_h[i]
                               if step_w else 0.0, offset)
C
chengduoZH 已提交
315 316 317 318 319 320 321 322 323 324 325

        box_results.append(box)
        var_results.append(var)

    if len(box_results) == 1:
        box = box_results[0]
        var = var_results[0]
    else:
        reshaped_boxes = []
        reshaped_vars = []
        for i in range(len(box_results)):
C
chengduoZH 已提交
326 327
            reshaped_boxes.append(_reshape_with_axis_(box_results[i], axis=3))
            reshaped_vars.append(_reshape_with_axis_(var_results[i], axis=3))
C
chengduoZH 已提交
328 329 330 331 332

        box = concat(reshaped_boxes)
        var = concat(reshaped_vars)

    return box, var
C
chengduoZH 已提交
333 334


C
chengduoZH 已提交
335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378
def multi_box_head(inputs,
                   num_classes,
                   min_sizes=None,
                   max_sizes=None,
                   min_ratio=None,
                   max_ratio=None,
                   aspect_ratios=None,
                   flip=False,
                   share_location=True,
                   kernel_size=1,
                   pad=1,
                   stride=1,
                   use_batchnorm=False,
                   base_size=None,
                   name=None):
    """
    **Multi Box Head**

    input many Variable, and return mbox_loc, mbox_conf

    Args:
       inputs(list): The list of input Variables, the format
            of all Variables is NCHW.
       num_classes(int): The number of calss.
       min_sizes(list, optional, default=None): The length of
            min_size is used to compute the the number of prior box.
            If the min_size is None, it will be computed according
            to min_ratio and max_ratio.
       max_sizes(list, optional, default=None): The length of max_size
            is used to compute the the number of prior box.
       min_ratio(int): If the min_sizes is None, min_ratio and min_ratio
            will be used to compute the min_sizes and max_sizes.
       max_ratio(int): If the min_sizes is None, min_ratio and min_ratio
            will be used to compute the min_sizes and max_sizes.
       aspect_ratios(list): The number of the aspect ratios is used to
            compute the number of prior box.
       base_size(int): the base_size is used to get min_size
            and max_size according to min_ratio and max_ratio.
       flip(bool, optional, default=False): Whether to flip
            aspect ratios.
       name(str, optional, None): Name of the prior box layer.

    Returns:

C
chengduoZH 已提交
379
        mbox_loc(list): the output prior boxes of PriorBoxOp. The layout is
C
chengduoZH 已提交
380 381
             [num_priors, 4]. num_priors is the total box count of each
              position of inputs.
C
chengduoZH 已提交
382
        mbox_conf(list): the expanded variances of PriorBoxOp. The layout
C
chengduoZH 已提交
383 384 385 386 387 388
             is [num_priors, 4]. num_priors is the total box count of each
             position of inputs

    Examples:
        .. code-block:: python

C
chengduoZH 已提交
389 390 391 392 393 394 395 396
            mbox_locs, mbox_confs = detection.multi_box_head(
                inputs=[conv1, conv2, conv3, conv4, conv5, conv5],
                num_classes=21,
                min_ratio=20,
                max_ratio=90,
                aspect_ratios=[[2.], [2., 3.], [2., 3.], [2., 3.], [2.], [2.]],
                base_size=300,
                flip=True)
C
chengduoZH 已提交
397 398
    """

C
chengduoZH 已提交
399 400
    if not (isinstance(inputs, list)):
        raise ValueError('inputs should be a list.')
C
chengduoZH 已提交
401 402

    if min_sizes is not None:
C
chengduoZH 已提交
403 404 405
        if not (len(inputs) == len(min_sizes)):
            raise ValueError('the length of min_sizes '
                             'and inputs should be the same.')
C
chengduoZH 已提交
406 407

    if max_sizes is not None:
C
chengduoZH 已提交
408 409 410 411 412 413 414 415
        if not (len(inputs) == len(max_sizes)):
            raise ValueError('the length of max_sizes '
                             'and inputs should be the same.')

    if aspect_ratios is not None:
        if not (len(inputs) == len(aspect_ratios)):
            raise ValueError('the length of aspect_ratios '
                             'and inputs should be the same.')
C
chengduoZH 已提交
416 417

    if min_sizes is None:
C
chengduoZH 已提交
418 419 420 421 422 423
        # If min_sizes is None, min_sizes and max_sizes
        #  will be set according to max_ratio and min_ratio.
        num_layer = len(inputs)
        assert max_ratio is not None and min_ratio is not None,\
            'max_ratio and min_ratio must be not None.'
        assert num_layer >= 3, 'The length of the input data is at least three.'
C
chengduoZH 已提交
424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444
        min_sizes = []
        max_sizes = []
        step = int(math.floor(((max_ratio - min_ratio)) / (num_layer - 2)))
        for ratio in xrange(min_ratio, max_ratio + 1, step):
            min_sizes.append(base_size * ratio / 100.)
            max_sizes.append(base_size * (ratio + step) / 100.)
        min_sizes = [base_size * .10] + min_sizes
        max_sizes = [base_size * .20] + max_sizes

    mbox_locs = []
    mbox_confs = []
    for i, input in enumerate(inputs):
        min_size = min_sizes[i]
        if type(min_size) is not list:
            min_size = [min_size]

        max_size = []
        if max_sizes is not None:
            max_size = max_sizes[i]
            if type(max_size) is not list:
                max_size = [max_size]
C
chengduoZH 已提交
445 446 447
            if not (len(max_size) == len(min_size)):
                raise ValueError(
                    'max_size and min_size should have same length.')
C
chengduoZH 已提交
448 449 450 451 452 453 454

        aspect_ratio = []
        if aspect_ratios is not None:
            aspect_ratio = aspect_ratios[i]
            if type(aspect_ratio) is not list:
                aspect_ratio = [aspect_ratio]

C
chengduoZH 已提交
455
        # get the number of prior box on each location
C
chengduoZH 已提交
456 457
        num_priors_per_location = 0
        if max_sizes is not None:
C
chengduoZH 已提交
458 459 460
            num_priors_per_location = len(min_size) + \
                                      len(aspect_ratio) * len(min_size) +\
                                      len(max_size)
C
chengduoZH 已提交
461
        else:
C
chengduoZH 已提交
462 463
            num_priors_per_location = len(min_size) +\
                                      len(aspect_ratio) * len(min_size)
C
chengduoZH 已提交
464 465 466
        if flip:
            num_priors_per_location += len(aspect_ratio) * len(min_size)

C
chengduoZH 已提交
467
        # get mbox_loc
C
chengduoZH 已提交
468 469 470 471 472 473 474 475 476 477 478 479 480 481
        num_loc_output = num_priors_per_location * 4
        if share_location:
            num_loc_output *= num_classes

        mbox_loc = img_conv_with_bn(
            input=input,
            conv_num_filter=num_loc_output,
            conv_padding=pad,
            conv_stride=stride,
            conv_filter_size=kernel_size,
            conv_with_batchnorm=use_batchnorm)
        mbox_loc = transpose(mbox_loc, perm=[0, 2, 3, 1])
        mbox_locs.append(mbox_loc)

C
chengduoZH 已提交
482
        # get conf_loc
C
chengduoZH 已提交
483 484 485 486 487 488 489 490 491 492 493 494
        num_conf_output = num_priors_per_location * num_classes
        conf_loc = img_conv_with_bn(
            input=input,
            conv_num_filter=num_conf_output,
            conv_padding=pad,
            conv_stride=stride,
            conv_filter_size=kernel_size,
            conv_with_batchnorm=use_batchnorm)
        conf_loc = transpose(conf_loc, perm=[0, 2, 3, 1])
        mbox_confs.append(conf_loc)

    return mbox_locs, mbox_confs