# Copyright (c) 2020 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. import collections.abc from itertools import combinations import numpy as np import cv2 import paddle import paddle.nn.functional as F def get_reverse_list(ori_shape, transforms): """ get reverse list of transform. Args: ori_shape (list): Origin shape of image. transforms (list): List of transform. Returns: list: List of tuple, there are two format: ('resize', (h, w)) The image shape before resize, ('padding', (h, w)) The image shape before padding. """ reverse_list = [] h, w = ori_shape[0], ori_shape[1] for op in transforms: if op.__class__.__name__ in ['Resize']: reverse_list.append(('resize', (h, w))) h, w = op.target_size[0], op.target_size[1] if op.__class__.__name__ in ['SubImgCrop']: reverse_list.append(('SubImgCrop', (op.offset_top, op.offset_bottom), (op.offset_left, op.offset_right))) h = h - op.offset_top h = h - op.offset_bottom w = w - op.offset_left w = w - op.offset_right if op.__class__.__name__ in ['ResizeByLong']: reverse_list.append(('resize', (h, w))) long_edge = max(h, w) short_edge = min(h, w) short_edge = int(round(short_edge * op.long_size / long_edge)) long_edge = op.long_size if h > w: h = long_edge w = short_edge else: w = long_edge h = short_edge if op.__class__.__name__ in ['ResizeByShort']: reverse_list.append(('resize', (h, w))) long_edge = max(h, w) short_edge = min(h, w) long_edge = int(round(long_edge * op.short_size / short_edge)) short_edge = op.short_size if h > w: h = long_edge w = short_edge else: w = long_edge h = short_edge if op.__class__.__name__ in ['Padding']: reverse_list.append(('padding', (h, w))) w, h = op.target_size[0], op.target_size[1] if op.__class__.__name__ in ['PaddingByAspectRatio']: reverse_list.append(('padding', (h, w))) ratio = w / h if ratio == op.aspect_ratio: pass elif ratio > op.aspect_ratio: h = int(w / op.aspect_ratio) else: w = int(h * op.aspect_ratio) if op.__class__.__name__ in ['LimitLong']: long_edge = max(h, w) short_edge = min(h, w) if ((op.max_long is not None) and (long_edge > op.max_long)): reverse_list.append(('resize', (h, w))) long_edge = op.max_long short_edge = int(round(short_edge * op.max_long / long_edge)) elif ((op.min_long is not None) and (long_edge < op.min_long)): reverse_list.append(('resize', (h, w))) long_edge = op.min_long short_edge = int(round(short_edge * op.min_long / long_edge)) if h > w: h = long_edge w = short_edge else: w = long_edge h = short_edge return reverse_list def reverse_transform(pred, ori_shape, transforms, mode='nearest'): """recover pred to origin shape""" reverse_list = get_reverse_list(ori_shape, transforms) for item in reverse_list[::-1]: if item[0] == 'resize': h, w = item[1][0], item[1][1] if paddle.get_device() == 'cpu': pred = paddle.cast(pred, 'uint8') pred = F.interpolate(pred, (h, w), mode=mode) pred = paddle.cast(pred, 'int32') else: pred = F.interpolate(pred, (h, w), mode=mode) elif item[0] == 'SubImgCrop': offset_top, offset_bottom = item[1][0], item[1][1] offset_left, offset_right = item[2][0], item[2][1] pred = F.pad( pred, [offset_left, offset_right, offset_top, offset_bottom], value=0, mode='constant', data_format="NCHW") elif item[0] == 'padding': h, w = item[1][0], item[1][1] pred = pred[:, :, 0:h, 0:w] else: raise Exception("Unexpected info '{}' in im_info".format(item[0])) return pred def inference(model, im, ori_shape=None, transforms=None): """ Inference for image. Args: model (paddle.nn.Layer): model to get logits of image. im (Tensor): the input image. ori_shape (list): Origin shape of image. transforms (list): Transforms for image. Returns: Tensor: If ori_shape is not None, a prediction with shape (1, 1, h, w) is returned. If ori_shape is None, a logit with shape (1, num_classes, h, w) is returned. """ if hasattr(model, 'data_format') and model.data_format == 'NHWC': im = im.transpose((0, 2, 3, 1)) logits = model(im) if not isinstance(logits, collections.abc.Sequence): raise TypeError( "The type of logits must be one of collections.abc.Sequence, e.g. list, tuple. But received {}" .format(type(logits))) logit = logits[0] if hasattr(model, 'data_format') and model.data_format == 'NHWC': logit = logit.transpose((0, 3, 1, 2)) if ori_shape is not None: pred = reverse_transform(logit, ori_shape, transforms, mode='bilinear') pred = paddle.argmax(pred, axis=1, keepdim=True, dtype='int32') return pred, logits else: return logit, logits