# Copyright (c) 2019 PaddlePaddle Authors. All Rights Reserved. # # 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. from __future__ import absolute_import from __future__ import division from __future__ import print_function from collections import OrderedDict from paddle import fluid from paddle.fluid.param_attr import ParamAttr from paddle.fluid.initializer import Xavier from paddle.fluid.regularizer import L2Decay from ppdet.core.workspace import register __all__ = ['HRFPN'] @register class HRFPN(object): """ HRNet, see https://arxiv.org/abs/1908.07919 Args: num_chan (int): number of feature channels pooling_type (str): pooling type of downsampling share_conv (bool): whethet to share conv for different layers' reduction spatial_scale (list): feature map scaling factor """ def __init__(self, num_chan=256, pooling_type="avg", share_conv=False, spatial_scale=[1./64, 1./32, 1./16, 1./8, 1./4], ): self.num_chan = num_chan self.pooling_type = pooling_type self.share_conv = share_conv self.spatial_scale = spatial_scale return def get_output(self, body_dict): num_out = len(self.spatial_scale) body_name_list = list(body_dict.keys()) num_backbone_stages = len(body_name_list) outs = [] outs.append(body_dict[body_name_list[0]]) # resize for i in range(1, len(body_dict)): resized = self.resize_input_tensor(body_dict[body_name_list[i]], outs[0], 2**i) outs.append( resized ) # concat out = fluid.layers.concat( outs, axis=1 ) # reduction out = fluid.layers.conv2d( input=out, num_filters=self.num_chan, filter_size=1, stride=1, padding=0, param_attr=ParamAttr(name='hrfpn_reduction_weights'), bias_attr=False) # conv outs = [out] for i in range(1, num_out): outs.append(self.pooling(out, size=2**i, stride=2**i, pooling_type=self.pooling_type)) outputs = [] for i in range(num_out): conv_name = "shared_fpn_conv" if self.share_conv else "shared_fpn_conv_"+str(i) conv = fluid.layers.conv2d( input=outs[i], num_filters=self.num_chan, filter_size=3, stride=1, padding=1, param_attr=ParamAttr(name=conv_name+"_weights"), bias_attr=False) outputs.append( conv ) for idx in range(0, num_out-len(body_name_list)): body_name_list.append("fpn_res5_sum_subsampled_{}x".format( 2**(idx+1) )) outputs = outputs[::-1] body_name_list = body_name_list[::-1] res_dict = OrderedDict([(body_name_list[k], outputs[k]) for k in range(len(body_name_list))]) return res_dict, self.spatial_scale def resize_input_tensor(self, body_input, ref_output, scale): shape = fluid.layers.shape(ref_output) shape_hw = fluid.layers.slice(shape, axes=[0], starts=[2], ends=[4]) out_shape_ = shape_hw out_shape = fluid.layers.cast(out_shape_, dtype='int32') out_shape.stop_gradient = True body_output = fluid.layers.resize_bilinear( body_input, scale=scale, actual_shape=out_shape) return body_output def pooling(self, input, size, stride, pooling_type): pool = fluid.layers.pool2d(input=input, pool_size=size, pool_stride=stride, pool_type=pooling_type) return pool