FlyingQianMM 4 rokov pred
rodič
commit
4afdbdca12

+ 14 - 8
dygraph/paddlex/command.py

@@ -80,7 +80,7 @@ def arg_parser():
         "--fixed_input_shape",
         "-fs",
         default=None,
-        help="export inference model with fixed input shape:[w,h]")
+        help="export inference model with fixed input shape:[w,h] or [n,3,w,h]")
     parser.add_argument(
         "--split_dataset",
         "-sd",
@@ -136,15 +136,21 @@ def main():
 
         fixed_input_shape = None
         if args.fixed_input_shape is not None:
-            fixed_input_shape = eval(args.fixed_input_shape)
-            assert len(
-                fixed_input_shape
-            ) == 2, "len of fixed input shape must == 2, such as [224,224]"
+            fixed_input_shape = list(eval(args.fixed_input_shape))
+            assert len(fixed_input_shape) in [
+                2, 4
+            ], "fixed_input_shape must be a list/tuple with length 2 or 4, such as [224,224] or [1,3,224,244]"
+            if len(fixed_input_shape) == 4:
+                assert fixed_input_shape[
+                    1] == 3, "input channel in fixed_input_shape must be 3, but recieved is {}".format(
+                        fixed_input_shape[1])
+            assert fixed_input_shape[-2] > 0 and fixed_input_shape[
+                -1] > 0, "input width and height must be a positive integer, but recievied is {}".format(
+                    fixed_input_shape[-2:])
+
             # input fixed_input_shape is [w,h]
             # export_inference_model needs [h,w]
-            fixed_input_shape = fixed_input_shape[-1::-1]
-        else:
-            fixed_input_shape = [-1, -1]
+            fixed_input_shape[-2:] = fixed_input_shape[-1:-3:-1]
 
         os.environ['PADDLEX_EXPORT_STAGE'] = 'True'
         os.environ['PADDLESEG_EXPORT_STAGE'] = 'True'

+ 2 - 2
dygraph/paddlex/cv/models/base.py

@@ -423,10 +423,10 @@ class BaseModel:
             self.save_model(save_dir)
             logging.info("Pruned model is saved at {}".format(save_dir))
 
-    def _export_inference_model(self, save_dir, image_shape=[-1, -1]):
+    def _export_inference_model(self, save_dir, image_shape=None):
         save_dir = osp.join(save_dir, 'inference_model')
         self.net.eval()
-        self.test_inputs = self.get_test_inputs(image_shape)
+        self.test_inputs = self._get_test_inputs(image_shape)
         static_net = paddle.jit.to_static(
             self.net, input_spec=self.test_inputs)
         paddle.jit.save(static_net, osp.join(save_dir, 'model'))

+ 41 - 12
dygraph/paddlex/cv/models/classifier.py

@@ -24,6 +24,7 @@ from paddle.static import InputSpec
 from paddlex.utils import logging, TrainingStats, DisablePrint
 from paddlex.cv.models.base import BaseModel
 from paddlex.cv.transforms import arrange_transforms
+from paddlex.cv.transforms.operators import Resize
 
 with DisablePrint():
     from PaddleClas.ppcls.modeling import architectures
@@ -51,7 +52,8 @@ class BaseClassifier(BaseModel):
     def __init__(self, model_name='ResNet50', num_classes=1000, **params):
         self.init_params = locals()
         self.init_params.update(params)
-        del self.init_params['params']
+        if 'lr_mult_list' in self.init_params:
+            del self.init_params['lr_mult_list']
         super(BaseClassifier, self).__init__('classifier')
         if not hasattr(architectures, model_name):
             raise Exception("ERROR: There's no model named {}.".format(
@@ -70,10 +72,28 @@ class BaseClassifier(BaseModel):
                 class_dim=self.num_classes, **params)
         return net
 
-    def get_test_inputs(self, image_shape):
+    def _fix_transforms_shape(self, image_shape):
+        if hasattr(self, 'test_transforms'):
+            if self.test_transforms is not None:
+                normalize_op_idx = len(self.test_transforms.transforms)
+                for idx, op in enumerate(self.test_transforms.transforms):
+                    name = op.__class__.__name__
+                    if name == 'Normalize':
+                        normalize_op_idx = idx
+
+                self.test_transforms.transforms.insert(
+                    normalize_op_idx, Resize(target_size=image_shape))
+
+    def _get_test_inputs(self, image_shape):
+        if image_shape is not None:
+            if len(image_shape) == 2:
+                image_shape = [None, 3] + image_shape
+            self._fix_transforms_shape(image_shape[-2:])
+        else:
+            image_shape = [None, 3, -1, -1]
         input_spec = [
             InputSpec(
-                shape=[None, 3] + image_shape, name='image', dtype='float32')
+                shape=image_shape, name='image', dtype='float32')
         ]
         return input_spec
 
@@ -445,15 +465,18 @@ class AlexNet(BaseClassifier):
             model_name='AlexNet', num_classes=num_classes)
 
     def get_test_inputs(self, image_shape):
-        if image_shape == [-1, -1]:
+        if image_shape is not None:
+            if len(image_shape) == 2:
+                image_shape = [None, 3] + image_shape
+        else:
             image_shape = [224, 224]
             logging.info('When exporting inference model for {},'.format(
                 self.__class__.__name__
-            ) + ' if image_shape is [-1, -1], it will be forcibly set to [224, 224]'
+            ) + ' if fixed_input_shape is not set, it will be forcibly set to [None, 3, 224, 224]'
                          )
         input_spec = [
             InputSpec(
-                shape=[None, 3] + image_shape, name='image', dtype='float32')
+                shape=image_shape, name='image', dtype='float32')
         ]
         return input_spec
 
@@ -623,15 +646,18 @@ class ShuffleNetV2(BaseClassifier):
             model_name=model_name, num_classes=num_classes)
 
     def get_test_inputs(self, image_shape):
-        if image_shape == [-1, -1]:
+        if image_shape is not None:
+            if len(image_shape) == 2:
+                image_shape = [None, 3] + image_shape
+        else:
             image_shape = [224, 224]
             logging.info('When exporting inference model for {},'.format(
                 self.__class__.__name__
-            ) + ' if image_shape is [-1, -1], it will be forcibly set to [224, 224]'
+            ) + ' if fixed_input_shape is not set, it will be forcibly set to [None, 3, 224, 224]'
                          )
         input_spec = [
             InputSpec(
-                shape=[None, 3] + image_shape, name='image', dtype='float32')
+                shape=image_shape, name='image', dtype='float32')
         ]
         return input_spec
 
@@ -642,14 +668,17 @@ class ShuffleNetV2_swish(BaseClassifier):
             model_name='ShuffleNetV2_x1_5', num_classes=num_classes)
 
     def get_test_inputs(self, image_shape):
-        if image_shape == [-1, -1]:
+        if image_shape is not None:
+            if len(image_shape) == 2:
+                image_shape = [None, 3] + image_shape
+        else:
             image_shape = [224, 224]
             logging.info('When exporting inference model for {},'.format(
                 self.__class__.__name__
-            ) + ' if image_shape is [-1, -1], it will be forcibly set to [224, 224]'
+            ) + ' if fixed_input_shape is not set, it will be forcibly set to [None, 3, 224, 224]'
                          )
         input_spec = [
             InputSpec(
-                shape=[None, 3] + image_shape, name='image', dtype='float32')
+                shape=image_shape, name='image', dtype='float32')
         ]
         return input_spec

+ 99 - 6
dygraph/paddlex/cv/models/detector.py

@@ -26,7 +26,7 @@ import ppdet
 from ppdet.modeling.proposal_generator.target_layer import BBoxAssigner, MaskAssigner
 import paddlex
 import paddlex.utils.logging as logging
-from paddlex.cv.transforms.operators import _NormalizeBox, _PadBox, _BboxXYXY2XYWH
+from paddlex.cv.transforms.operators import _NormalizeBox, _PadBox, _BboxXYXY2XYWH, Resize, Padding
 from paddlex.cv.transforms.batch_operators import BatchCompose, BatchRandomResize, BatchRandomResizeByShort, _BatchPadding, _Gt2YoloTarget
 from paddlex.cv.transforms import arrange_transforms
 from .base import BaseModel
@@ -42,7 +42,6 @@ __all__ = [
 class BaseDetector(BaseModel):
     def __init__(self, model_name, num_classes=80, **params):
         self.init_params.update(locals())
-        del self.init_params['params']
         super(BaseDetector, self).__init__('detector')
         if not hasattr(ppdet.modeling, model_name):
             raise Exception("ERROR: There's no model named {}.".format(
@@ -58,15 +57,32 @@ class BaseDetector(BaseModel):
             net = ppdet.modeling.__dict__[self.model_name](**params)
         return net
 
-    def get_test_inputs(self, image_shape):
+    def _fix_transforms_shape(self, image_shape):
+        pass
+
+    def _get_test_inputs(self, image_shape):
+        if image_shape is not None:
+            if len(image_shape) == 2:
+                image_shape = [None, 3] + image_shape
+            if image_shape[-2] % 32 > 0 or image_shape[-1] % 32 > 0:
+                raise Exception(
+                    "Height and width in fixed_input_shape must be a multiple of 32, but recieved is {}.".
+                    format(image_shape[-2:]))
+            self._fix_transforms_shape(image_shape[-2:])
+        else:
+            image_shape = [None, 3, -1, -1]
+
         input_spec = [{
             "image": InputSpec(
-                shape=[None, 3] + image_shape, name='image', dtype='float32'),
+                shape=image_shape, name='image', dtype='float32'),
             "im_shape": InputSpec(
-                shape=[None, 2], name='im_shape', dtype='float32'),
+                shape=[image_shape[0], 2], name='im_shape', dtype='float32'),
             "scale_factor": InputSpec(
-                shape=[None, 2], name='scale_factor', dtype='float32')
+                shape=[image_shape[0], 2],
+                name='scale_factor',
+                dtype='float32')
         }]
+
         return input_spec
 
     def _get_backbone(self, backbone_name, **params):
@@ -610,6 +626,29 @@ class YOLOv3(BaseDetector):
 
         return batch_transforms
 
+    def _fix_transforms_shape(self, image_shape):
+        if hasattr(self, 'test_transforms'):
+            if self.test_transforms is not None:
+                has_resize_op = False
+                resize_op_idx = -1
+                normalize_op_idx = len(self.test_transforms.transforms)
+                for idx, op in enumerate(self.test_transforms.transforms):
+                    name = op.__class__.__name__
+                    if name == 'Resize':
+                        has_resize_op = True
+                        resize_op_idx = idx
+                    if name == 'Normalize':
+                        normalize_op_idx = idx
+
+                if not has_resize_op:
+                    self.test_transforms.transforms.insert(
+                        normalize_op_idx,
+                        Resize(
+                            target_size=image_shape, interp='CUBIC'))
+                else:
+                    self.test_transforms.transforms[
+                        resize_op_idx].target_size = image_shape
+
 
 class FasterRCNN(BaseDetector):
     def __init__(self,
@@ -833,6 +872,35 @@ class FasterRCNN(BaseDetector):
 
         return batch_transforms
 
+    def _fix_transforms_shape(self, image_shape):
+        if hasattr(self, 'test_transforms'):
+            if self.test_transforms is not None:
+                has_resize_op = False
+                resize_op_idx = -1
+                normalize_op_idx = len(self.test_transforms.transforms)
+                for idx, op in enumerate(self.test_transforms.transforms):
+                    name = op.__class__.__name__
+                    if name == 'ResizeByShort':
+                        has_resize_op = True
+                        resize_op_idx = idx
+                    if name == 'Normalize':
+                        normalize_op_idx = idx
+
+                if not has_resize_op:
+                    self.test_transforms.transforms.insert(
+                        normalize_op_idx,
+                        Resize(
+                            target_size=image_shape,
+                            keep_ratio=True,
+                            interp='CUBIC'))
+                else:
+                    self.test_transforms.transforms[resize_op_idx] = Resize(
+                        target_size=image_shape,
+                        keep_ratio=True,
+                        interp='CUBIC')
+                self.test_transforms.transforms.append(
+                    Padding(im_padding_value=[0., 0., 0.]))
+
 
 class PPYOLO(YOLOv3):
     def __init__(self,
@@ -1473,3 +1541,28 @@ class MaskRCNN(BaseDetector):
                                         default_batch_transforms)
 
         return batch_transforms
+
+    def _fix_transforms_shape(self, image_shape):
+        if hasattr(self, 'test_transforms'):
+            if self.test_transforms is not None:
+                has_resize_op = False
+                resize_op_idx = -1
+                normalize_op_idx = len(self.test_transforms.transforms)
+                for idx, op in enumerate(self.test_transforms.transforms):
+                    name = op.__class__.__name__
+                    if name == 'ResizeByShort':
+                        has_resize_op = True
+                        resize_op_idx = idx
+                    if name == 'Normalize':
+                        normalize_op_idx = idx
+
+                if not has_resize_op:
+                    self.test_transforms.transforms.insert(
+                        normalize_op_idx,
+                        Resize(
+                            target_size=image_shape, keep_ratio=True))
+                else:
+                    self.test_transforms.transforms[resize_op_idx] = Resize(
+                        target_size=image_shape, keep_ratio=True)
+                self.test_transforms.transforms.append(
+                    Padding(im_padding_value=[0., 0., 0.]))

+ 36 - 5
dygraph/paddlex/cv/models/load_model.py

@@ -43,7 +43,7 @@ def load_model(model_dir):
     if int(version.split('.')[0]) < 2:
         raise Exception(
             'Current version is {}, a model trained by PaddleX={} cannot be load.'.
-            format(paddlex.version, version))
+            format(paddlex.__version__, version))
 
     status = model_info['status']
 
@@ -78,10 +78,41 @@ def load_model(model_dir):
 
         if status == 'Infer':
             if model_info['Model'] in ['FasterRCNN', 'MaskRCNN']:
-                net_state_dict = paddle.load(
-                    model_dir,
-                    params_filename='model.pdiparams',
-                    model_filename='model.pdmodel')
+                #net_state_dict = paddle.load(
+                #    model_dir,
+                #    params_filename='model.pdiparams',
+                #    model_filename='model.pdmodel')
+
+                net = paddle.jit.load(osp.join(model_dir, 'model'))
+                #load_param_dict = paddle.load(osp.join(model_dir, 'model.pdiparams'))
+                #print(load_param_dict)
+
+                import pickle
+                var_info_path = osp.join(model_dir, 'model.pdiparams.info')
+                with open(var_info_path, 'rb') as f:
+                    extra_var_info = pickle.load(f)
+                net_state_dict = dict()
+                static_state_dict = dict()
+                for name, var in net.state_dict().items():
+                    print(name, var.name)
+                    static_state_dict[var.name] = var.numpy()
+                exit()
+                for var_name in static_state_dict:
+                    if var_name not in extra_var_info:
+                        print(var_name)
+                        continue
+                    structured_name = extra_var_info[var_name].get(
+                        'structured_name', None)
+                    if structured_name is None:
+                        continue
+                    net_state_dict[structured_name] = static_state_dict[
+                        var_name]
+
+                #model.net = paddle.jit.load(
+                #    model_dir,
+                #    params_filename='model.pdiparams',
+                #    model_filename='model.pdmodel')
+                #net_state_dict = paddle.load(osp.join(model_dir, 'model'))
             else:
                 net_state_dict = paddle.load(osp.join(model_dir, 'model'))
         else:

+ 30 - 3
dygraph/paddlex/cv/models/segmenter.py

@@ -27,7 +27,7 @@ import paddlex.utils.logging as logging
 from .base import BaseModel
 from .utils import seg_metrics as metrics
 from paddlex.utils.checkpoint import seg_pretrain_weights_dict
-from paddlex.cv.transforms import Decode
+from paddlex.cv.transforms import Decode, Resize
 
 __all__ = ["UNet", "DeepLabV3P", "FastSCNN", "HRNet", "BiSeNetV2"]
 
@@ -57,10 +57,37 @@ class BaseSegmenter(BaseModel):
             num_classes=self.num_classes, **params)
         return net
 
-    def get_test_inputs(self, image_shape):
+    def _fix_transforms_shape(self, image_shape):
+        if hasattr(self, 'test_transforms'):
+            if self.test_transforms is not None:
+                has_resize_op = False
+                resize_op_idx = -1
+                normalize_op_idx = len(self.test_transforms.transforms)
+                for idx, op in enumerate(self.test_transforms.transforms):
+                    name = op.__class__.__name__
+                    if name == 'Normalize':
+                        normalize_op_idx = idx
+                    if 'Resize' in name:
+                        has_resize_op = True
+                        resize_op_idx = idx
+
+                if not has_resize_op:
+                    self.test_transforms.transforms.insert(
+                        normalize_op_idx, Resize(target_size=image_shape))
+                else:
+                    self.test_transforms.transforms[resize_op_idx] = Resize(
+                        target_size=image_shape)
+
+    def _get_test_inputs(self, image_shape):
+        if image_shape is not None:
+            if len(image_shape) == 2:
+                image_shape = [None, 3] + image_shape
+            self._fix_transforms_shape(image_shape[-2:])
+        else:
+            image_shape = [None, 3, -1, -1]
         input_spec = [
             InputSpec(
-                shape=[None, 3] + image_shape, name='image', dtype='float32')
+                shape=image_shape, name='image', dtype='float32')
         ]
         return input_spec
 

+ 16 - 3
dygraph/paddlex/cv/transforms/operators.py

@@ -212,13 +212,15 @@ class Resize(Transform):
             Otherwise, target_size represents [target height, target width].
         interp ({'NEAREST', 'LINEAR', 'CUBIC', 'AREA', 'LANCZOS4', 'RANDOM'}, optional):
             Interpolation method of resize. Defaults to 'LINEAR'.
+        keep_ratio (bool): the resize scale of width/height is same and width/height after resized is not greater
+            than target width/height. Defaults to False.
 
     Raises:
         TypeError: Invalid type of target_size.
         ValueError: Invalid interpolation method.
     """
 
-    def __init__(self, target_size, interp='LINEAR'):
+    def __init__(self, target_size, interp='LINEAR', keep_ratio=False):
         super(Resize, self).__init__()
         if not (interp == "RANDOM" or interp in interp_dict):
             raise ValueError("interp should be one of {}".format(
@@ -234,6 +236,7 @@ class Resize(Transform):
         # (height, width)
         self.target_size = target_size
         self.interp = interp
+        self.keep_ratio = keep_ratio
 
     def apply_im(self, image, interp):
         image = cv2.resize(
@@ -281,6 +284,14 @@ class Resize(Transform):
 
         im_scale_y = self.target_size[0] / im_h
         im_scale_x = self.target_size[1] / im_w
+        target_size = list(self.target_size)
+        if self.keep_ratio:
+            scale = min(im_scale_y, im_scale_x)
+            target_w = int(round(im_w * scale))
+            target_h = int(round(im_h * scale))
+            target_size = [target_w, target_h]
+            im_scale_y = target_h / im_h
+            im_scale_x = target_w / im_w
 
         sample['image'] = self.apply_im(sample['image'], interp)
 
@@ -913,7 +924,8 @@ class Padding(Transform):
                  pad_mode=0,
                  offsets=None,
                  im_padding_value=(127.5, 127.5, 127.5),
-                 label_padding_value=255):
+                 label_padding_value=255,
+                 size_divisor=32):
         """
         Pad image to a specified size or multiple of size_divisor.
 
@@ -923,6 +935,7 @@ class Padding(Transform):
                 if 0, only pad to right and bottom. If 1, pad according to center. If 2, only pad left and top. Defaults to 0.
             im_padding_value(Sequence[float]): RGB value of pad area. Defaults to (127.5, 127.5, 127.5).
             label_padding_value(int, optional): Filling value for the mask. Defaults to 255.
+            size_divisor(int): Image width and height after padding is a multiple of size_divisor
         """
         super(Padding, self).__init__()
         if isinstance(target_size, (list, tuple)):
@@ -940,7 +953,7 @@ class Padding(Transform):
             assert offsets, 'if pad_mode is -1, offsets should not be None'
 
         self.target_size = target_size
-        self.size_divisor = 32
+        self.size_divisor = size_divisor
         self.pad_mode = pad_mode
         self.offsets = offsets
         self.im_padding_value = im_padding_value