Browse Source

add BasicTransforms

jiangjiajun 5 years ago
parent
commit
421266a79a

+ 7 - 4
paddlex/cv/datasets/dataset.py

@@ -23,6 +23,7 @@ import random
 import platform
 import chardet
 import paddlex.utils.logging as logging
+from paddlex.cv.transforms.template import TemplateTransforms
 
 
 class EndSignal():
@@ -209,8 +210,8 @@ def GenerateMiniBatch(batch_data):
     padding_batch = []
     for data in batch_data:
         im_c, im_h, im_w = data[0].shape[:]
-        padding_im = np.zeros((im_c, max_shape[1], max_shape[2]),
-                              dtype=np.float32)
+        padding_im = np.zeros(
+            (im_c, max_shape[1], max_shape[2]), dtype=np.float32)
         padding_im[:, :im_h, :im_w] = data[0]
         padding_batch.append((padding_im, ) + data[1:])
     return padding_batch
@@ -226,12 +227,14 @@ class Dataset:
         if num_workers == 'auto':
             import multiprocessing as mp
             num_workers = mp.cpu_count() // 2 if mp.cpu_count() // 2 < 8 else 8
-        if platform.platform().startswith(
-                "Darwin") or platform.platform().startswith("Windows"):
+        if platform.platform().startswith("Darwin") or platform.platform(
+        ).startswith("Windows"):
             parallel_method = 'thread'
         if transforms is None:
             raise Exception("transform should be defined.")
         self.transforms = transforms
+        if isinstance(transforms, TemplateTransforms):
+            self.transforms = transforms.transforms
         self.num_workers = num_workers
         self.buffer_size = buffer_size
         self.parallel_method = parallel_method

+ 1 - 0
paddlex/cv/transforms/__init__.py

@@ -15,3 +15,4 @@
 from . import cls_transforms
 from . import det_transforms
 from . import seg_transforms
+from . import template

+ 53 - 0
paddlex/cv/transforms/cls_transforms.py

@@ -18,6 +18,7 @@ import random
 import os.path as osp
 import numpy as np
 from PIL import Image, ImageEnhance
+from .template import TemplateTransforms
 
 
 class ClsTransform:
@@ -461,3 +462,55 @@ class ArrangeClassifier(ClsTransform):
         else:
             outputs = (im, )
         return outputs
+
+
+class BasicClsTransforms(TemplateTransforms):
+    """ 分类模型的基础Transforms流程,具体如下
+        训练阶段:
+        1. 随机从图像中crop一块子图,并resize成crop_size大小
+        2. 将1的输出按0.5的概率随机进行水平翻转
+        3. 将图像进行归一化
+        验证/预测阶段:
+        1. 将图像按比例Resize,使得最小边长度为crop_size[0] * 1.14
+        2. 从图像中心crop出一个大小为crop_size的图像
+        3. 将图像进行归一化
+
+        Args:
+            mode(str): 图像处理流程所处阶段,训练/验证/预测,分别对应'train', 'eval', 'test'
+            crop_size(int|list): 输入模型里的图像大小
+            mean(list): 图像均值
+            std(list): 图像方差
+    """
+
+    def __init__(self,
+                 mode,
+                 crop_size=[224, 224],
+                 mean=[0.485, 0.456, 0.406],
+                 std=[0.229, 0.224, 0.225]):
+        super(TemplateClsTransforms, self).__init__(mode=mode)
+        width = crop_size
+        if isinstance(crop_size, list):
+            if shape[0] != shape[1]:
+                raise Exception(
+                    "In classifier model, width and height should be equal")
+            width = crop_size[0]
+        if width % 32 != 0:
+            raise Exception(
+                "In classifier model, width and height should be multiple of 32, e.g 224、256、320...."
+            )
+
+        if self.mode == 'train':
+            # 训练时的transforms,包含数据增强
+            self.transforms = transforms.Compose([
+                transforms.RandomCrop(crop_size=width),
+                transforms.RandomHorizontalFlip(prob=0.5),
+                transforms.Normalize(
+                    mean=mean, std=std)
+            ])
+        else:
+            # 验证/预测时的transforms
+            self.transforms = transforms.Compose([
+                transforms.ReiszeByShort(short_size=int(width * 1.14)),
+                transforms.CenterCrop(crop_size=width), transforms.Normalize(
+                    mean=mean, std=std)
+            ])

+ 107 - 0
paddlex/cv/transforms/det_transforms.py

@@ -27,6 +27,7 @@ from PIL import Image, ImageEnhance
 from .imgaug_support import execute_imgaug
 from .ops import *
 from .box_utils import *
+from .template import TemplateTransforms
 
 
 class DetTransform:
@@ -1227,3 +1228,109 @@ class ArrangeYOLOv3(DetTransform):
             im_shape = im_info['image_shape']
             outputs = (im, im_shape)
         return outputs
+
+
+class BasicRCNNTransforms(TemplateTransforms):
+    """ RCNN模型(faster-rcnn/mask-rcnn)图像处理流程,具体如下,
+        训练阶段:
+        1. 随机以0.5的概率将图像水平翻转
+        2. 图像归一化
+        3. 图像按比例Resize,scale计算方式如下
+            scale = min_max_size[0] / short_size_of_image
+            if max_size_of_image * scale > min_max_size[1]:
+                scale = min_max_size[1] / max_size_of_image
+        4. 将3步骤的长宽进行padding,使得长宽为32的倍数
+        验证阶段:
+        1. 图像归一化
+        2. 图像按比例Resize,scale计算方式同上训练阶段
+        3. 将2步骤的长宽进行padding,使得长宽为32的倍数
+
+        Args:
+            mode(str): 图像处理流程所处阶段,训练/验证/预测,分别对应'train', 'eval', 'test'
+            min_max_size(list): 图像在缩放时,最小边和最大边的约束条件
+            mean(list): 图像均值
+            std(list): 图像方差
+    """
+
+    def __init__(self,
+                 mode,
+                 min_max_size=[800, 1333],
+                 mean=[0.485, 0.456, 0.406],
+                 std=[0.229, 0.224, 0.225]):
+        super(RCNNTransforms, self).__init__(mode=mode)
+        if self.mode == 'train':
+            # 训练时的transforms,包含数据增强
+            self.transforms = transforms.Compose([
+                transforms.RandomHorizontalFlip(prob=0.5),
+                transforms.Normalize(
+                    mean=mean, std=std), transforms.ResizeByShort(
+                        short_size=min_max_size[0], max_size=min_max_size[1]),
+                transforms.Padding(coarsest_stride=32)
+            ])
+        else:
+            # 验证/预测时的transforms
+            self.transforms = transforms.Compose([
+                transforms.Normalize(
+                    mean=mean, std=std), transforms.ResizeByShort(
+                        short_size=min_max_size[0], max_size=min_max_size[1]),
+                transforms.Padding(coarsest_stride=32)
+            ])
+
+
+class BasicYOLOTransforms(TemplateTransforms):
+    """YOLOv3模型的图像预处理流程,具体如下,
+        训练阶段:
+        1. 在前mixup_epoch轮迭代中,使用MixupImage策略,见https://paddlex.readthedocs.io/zh_CN/latest/apis/transforms/det_transforms.html#mixupimage
+        2. 对图像进行随机扰动,包括亮度,对比度,饱和度和色调
+        3. 随机扩充图像,见https://paddlex.readthedocs.io/zh_CN/latest/apis/transforms/det_transforms.html#randomexpand
+        4. 随机裁剪图像
+        5. 将4步骤的输出图像Resize成shape参数的大小
+        6. 随机0.5的概率水平翻转图像
+        7. 图像归一化
+        验证/预测阶段:
+        1. 将图像Resize成shape参数大小
+        2. 图像归一化
+
+        Args:
+            mode(str): 图像处理流程所处阶段,训练/验证/预测,分别对应'train', 'eval', 'test'
+            shape(list): 输入模型中图像的大小,输入模型的图像会被Resize成此大小
+            mixup_epoch(int): 模型训练过程中,前mixup_epoch会使用mixup策略
+            mean(list): 图像均值
+            std(list): 图像方差
+    """
+
+    def __init__(self,
+                 mode,
+                 shape=[608, 608],
+                 mixup_epoch=250,
+                 mean=[0.485, 0.456, 0.406],
+                 std=[0.229, 0.224, 0.225]):
+        super(YOLOTransforms, self).__init__(mode=mode)
+        width = shape
+        if isinstance(shape, list):
+            if shape[0] != shape[1]:
+                raise Exception(
+                    "In YOLOv3 model, width and height should be equal")
+            width = shape[0]
+        if width % 32 != 0:
+            raise Exception(
+                "In YOLOv3 model, width and height should be multiple of 32, e.g 224、256、320...."
+            )
+
+        if self.mode == 'train':
+            # 训练时的transforms,包含数据增强
+            self.transforms = transforms.Compose([
+                transforms.MixupImage(mixup_epoch=mixup_epoch),
+                transforms.RandomDistort(), transforms.RandomExpand(),
+                transforms.RandomCrop(), transforms.Resize(
+                    target_size=width, interp='RANDOM'),
+                transforms.RandomHorizontalFlip(), transforms.Normalize(
+                    mean=mean, std=std)
+            ])
+        else:
+            # 验证/预测时的transforms
+            self.transforms = transforms.Compose([
+                transforms.Resize(
+                    target_size=width, interp='CUBIC'), transforms.Normalize(
+                        mean=mean, std=std)
+            ])

+ 40 - 0
paddlex/cv/transforms/seg_transforms.py

@@ -21,6 +21,7 @@ import numpy as np
 from PIL import Image
 import cv2
 from collections import OrderedDict
+from .template import TemplateTransforms
 
 
 class SegTransform:
@@ -1088,3 +1089,42 @@ class ArrangeSegmenter(SegTransform):
             return (im, im_info)
         else:
             return (im, )
+
+
+class BasicSegTransforms(TemplateTransforms):
+    """ 语义分割模型(UNet/DeepLabv3p)的图像处理流程,具体如下
+        训练阶段:
+        1. 随机对图像以0.5的概率水平翻转
+        2. 按不同的比例随机Resize原图
+        3. 从原图中随机crop出大小为train_crop_size大小的子图,如若crop出来的图小于train_crop_size,则会将图padding到对应大小
+        4. 图像归一化
+        预测阶段:
+        1. 图像归一化
+
+        Args:
+            mode(str): 图像处理所处阶段,训练/验证/预测,分别对应'train', 'eval', 'test'
+            train_crop_size(list): 模型训练阶段,随机从原图crop的大小
+            mean(list): 图像均值
+            std(list): 图像方差
+    """
+
+    def __init__(self,
+                 mode,
+                 train_crop_size=[769, 769],
+                 mean=[0.5, 0.5, 0.5],
+                 std=[0.5, 0.5, 0.5]):
+        super(TemplateSegTransforms, self).__init__(mode=mode)
+        if self.mode == 'train':
+            # 训练时的transforms,包含数据增强
+            self.transforms = transforms.Compose([
+                transforms.RandomHorizontalFlip(),
+                transforms.ResizeStepScaling(),
+                transforms.RandomPaddingCrop(crop_size=train_crop_size),
+                transforms.Normalize(
+                    mean=mean, std=std)
+            ])
+        else:
+            # 验证/预测时的transforms
+            self.transforms = transforms.Compose(
+                [transforms.Normalize(
+                    mean=mean, std=std)])

+ 28 - 0
paddlex/cv/transforms/template.py

@@ -0,0 +1,28 @@
+# copyright (c) 2020 PaddlePaddle Authors. All Rights Reserve.
+#
+# 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.
+
+
+class TemplateTransforms:
+    def __init__(self, mode):
+        assert mode in [
+            'train', 'eval', 'test'
+        ], "Parameter mode in TemplateTransforms should be one of ['train', 'eval', 'test']"
+        self.mode = mode
+
+    def add_augmenters(self, augmenters):
+        if not isinstance(augmenters, list):
+            raise Exception(
+                "augmenters should be list type in func add_augmenters()")
+        assert mode == 'train', "There should be exists augmenters while on train mode"
+        self.transforms = augmenters + self.transforms.transforms

+ 0 - 2
paddlex/deploy.py

@@ -97,8 +97,6 @@ class Predictor:
             config.disable_glog_info()
         if memory_optimize:
             config.enable_memory_optim()
-        else:
-            config.diable_memory_optim()
 
         # 开启计算图分析优化,包括OP融合等
         config.switch_ir_optim(True)