From 26e50cfc433c042382cbfb4f420c35791eed4453 Mon Sep 17 00:00:00 2001 From: leonwanghui Date: Fri, 19 Feb 2021 15:45:15 +0800 Subject: [PATCH 1/3] Unify the vision transform module --- tinyms/data/transforms.py | 30 ----------- tinyms/vision/_transform_ops.py | 20 ++++++- tinyms/vision/transforms.py | 96 ++++++++++++++++----------------- 3 files changed, 65 insertions(+), 81 deletions(-) delete mode 100644 tinyms/data/transforms.py diff --git a/tinyms/data/transforms.py b/tinyms/data/transforms.py deleted file mode 100644 index 3fa2cf90..00000000 --- a/tinyms/data/transforms.py +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright 2021 Huawei Technologies Co., Ltd -# -# 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 mindspore.dataset.transforms.c_transforms import * - -__all__ = [ - 'Compose', - 'Concatenate', - 'Duplicate', - 'Fill', - 'Mask', - 'OneHot', - 'PadEnd', - 'RandomApply', - 'RandomChoice', - 'Slice', - 'TypeCast', - 'Unique', -] diff --git a/tinyms/vision/_transform_ops.py b/tinyms/vision/_transform_ops.py index 3b308d35..877e418f 100644 --- a/tinyms/vision/_transform_ops.py +++ b/tinyms/vision/_transform_ops.py @@ -14,8 +14,9 @@ # ============================================================================ from mindspore.dataset.vision.c_transforms import * +from mindspore.dataset.transforms.c_transforms import * -__all__ = [ +vision_trans = [ 'AutoContrast', 'BoundingBoxAugment', 'CenterCrop', @@ -55,6 +56,23 @@ 'UniformAugment', ] +common_trans = [ + 'Compose', + 'Concatenate', + 'Duplicate', + 'Fill', + 'Mask', + 'OneHot', + 'PadEnd', + 'RandomApply', + 'RandomChoice', + 'Slice', + 'TypeCast', + 'Unique', +] + +__all__ = vision_trans + common_trans + decode = Decode() hwc2chw = HWC2CHW() diff --git a/tinyms/vision/transforms.py b/tinyms/vision/transforms.py index ae3b5963..b2fb5339 100644 --- a/tinyms/vision/transforms.py +++ b/tinyms/vision/transforms.py @@ -20,7 +20,6 @@ from . import _transform_ops from ._transform_ops import * from ..data import MnistDataset, Cifar10Dataset, ImageFolderDataset -from ..data.transforms import TypeCast __all__ = [ 'mnist_transform', 'MnistTransform', @@ -29,32 +28,36 @@ ] __all__.extend(_transform_ops.__all__) -TRANSFORM_STRATEGY = ['TOP1_CLASS', 'TOP5_CLASS'] +class DatasetTransform(): + def __init__(self, labels=None): + self.labels = labels + self.transform_strategy = ['TOP1_CLASS', 'TOP5_CLASS'] -def _postprocess(input, labels, strategy=None): - if not isinstance(input, np.ndarray): - raise TypeError("Input should be NumPy, got {}.".format(type(input))) - if not input.ndim == 2: - raise TypeError("Input should be 2-D Numpy, got {}.".format(input.ndim)) - if strategy not in TRANSFORM_STRATEGY: - raise ValueError("Strategy should be one of {}, got {}.".format(TRANSFORM_STRATEGY, strategy)) - - if strategy == 'TOP1_CLASS': - return labels[input[0].argmax()] - else: - label_index = np.argsort(input[0])[::-1] - score_index = np.sort(input[0])[::-1] - top5_labels = [] - for i in range(5): - top5_labels.append(labels[label_index[i]]) - top5_scores = score_index[:5].tolist() - return {'label': top5_labels, 'score': top5_scores} + def postprocess(self, input, strategy='TOP1_CLASS'): + if not isinstance(input, np.ndarray): + raise TypeError("Input should be NumPy, got {}.".format(type(input))) + if not input.ndim == 2: + raise TypeError("Input should be 2-D Numpy, got {}.".format(input.ndim)) + if strategy not in self.transform_strategy: + raise ValueError("Strategy should be one of {}, got {}.".format(self.transform_strategy, strategy)) + + if strategy == 'TOP1_CLASS': + return self.labels[input[0].argmax()] + else: + label_index = np.argsort(input[0])[::-1] + score_index = np.sort(input[0])[::-1] + top5_labels = [] + for i in range(5): + top5_labels.append(self.labels[label_index[i]]) + top5_scores = score_index[:5].tolist() + return {'label': top5_labels, 'score': top5_scores} -class MnistTransform(): +class MnistTransform(DatasetTransform): def __init__(self): - self.labels = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + labels = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + super().__init__(labels=labels) self.resize = Resize((32, 32)) self.normalize = Rescale(1 / 0.3081, -1 * 0.1307 / 0.3081) self.rescale = Rescale(1.0 / 255.0, 0.0) @@ -98,14 +101,12 @@ def apply_ds(self, mnist_ds, repeat_size=1, batch_size=32, num_parallel_workers= return mnist_ds - def postprocess(self, input, strategy='TOP1_CLASS'): - return _postprocess(input, self.labels, strategy=strategy) - -class Cifar10Transform(): +class Cifar10Transform(DatasetTransform): def __init__(self): - self.labels = ['airplane', 'automobile', 'bird', 'cat', 'deer', - 'dog', 'frog', 'horse', 'ship', 'truck'] + labels = ['airplane', 'automobile', 'bird', 'cat', 'deer', + 'dog', 'frog', 'horse', 'ship', 'truck'] + super().__init__(labels=labels) self.random_crop = RandomCrop((32, 32), (4, 4, 4, 4)) self.random_horizontal_flip = RandomHorizontalFlip(prob=0.5) self.resize = Resize((224, 224)) @@ -153,22 +154,20 @@ def apply_ds(self, cifar10_ds, repeat_size=1, batch_size=32, return cifar10_ds - def postprocess(self, input, strategy='TOP1_CLASS'): - return _postprocess(input, self.labels, strategy=strategy) - -class ImageFolderTransform(): +class ImageFolderTransform(DatasetTransform): def __init__(self): - self.labels = ["Agaricus双孢蘑菇,伞菌目,蘑菇科,蘑菇属,广泛分布于北半球温带,无毒", - "Amanita毒蝇伞,伞菌目,鹅膏菌科,鹅膏菌属,主要分布于我国黑龙江、吉林、四川、西藏、云南等地,有毒", - "Boletus丽柄牛肝菌,伞菌目,牛肝菌科,牛肝菌属,分布于云南、陕西、甘肃、西藏等地,有毒", - "Cortinarius掷丝膜菌,伞菌目,丝膜菌科,丝膜菌属,分布于湖南等地(夏秋季在山毛等阔叶林地上生长)", - "Entoloma霍氏粉褶菌,伞菌目,粉褶菌科,粉褶菌属,主要分布于新西兰北岛和南岛西部,有毒", - "Hygrocybe浅黄褐湿伞,伞菌目,蜡伞科,湿伞属,分布于香港(见于松仔园),有毒", - "Lactarius松乳菇,红菇目,红菇科,乳菇属,广泛分布于亚热带松林地,无毒", - "Russula褪色红菇,伞菌目,红菇科,红菇属,分布于河北、吉林、四川、江苏、西藏等地,无毒", - "Suillus乳牛肝菌,牛肝菌目,乳牛肝菌科,乳牛肝菌属,分布于吉林、辽宁、山西、安徽、江西、浙江、湖南、四川、贵州等地,无毒", - ] + labels = ["Agaricus双孢蘑菇,伞菌目,蘑菇科,蘑菇属,广泛分布于北半球温带,无毒", + "Amanita毒蝇伞,伞菌目,鹅膏菌科,鹅膏菌属,主要分布于我国黑龙江、吉林、四川、西藏、云南等地,有毒", + "Boletus丽柄牛肝菌,伞菌目,牛肝菌科,牛肝菌属,分布于云南、陕西、甘肃、西藏等地,有毒", + "Cortinarius掷丝膜菌,伞菌目,丝膜菌科,丝膜菌属,分布于湖南等地(夏秋季在山毛等阔叶林地上生长)", + "Entoloma霍氏粉褶菌,伞菌目,粉褶菌科,粉褶菌属,主要分布于新西兰北岛和南岛西部,有毒", + "Hygrocybe浅黄褐湿伞,伞菌目,蜡伞科,湿伞属,分布于香港(见于松仔园),有毒", + "Lactarius松乳菇,红菇目,红菇科,乳菇属,广泛分布于亚热带松林地,无毒", + "Russula褪色红菇,伞菌目,红菇科,红菇属,分布于河北、吉林、四川、江苏、西藏等地,无毒", + "Suillus乳牛肝菌,牛肝菌目,乳牛肝菌科,乳牛肝菌属,分布于吉林、辽宁、山西、安徽、江西、浙江、湖南、四川、贵州等地,无毒", + ] + super().__init__(labels=labels) self.random_crop_decode_resize = RandomCropDecodeResize(224, scale=(0.08, 1.0), ratio=(0.75, 1.333)) self.random_horizontal_flip = RandomHorizontalFlip(prob=0.5) self.resize = Resize(256) @@ -177,11 +176,11 @@ def __init__(self): [0.229 * 255, 0.224 * 255, 0.225 * 255]) self.type_cast = TypeCast(ts.int32) - def _center_crop(self, img, cropx, cropy): + def _center_crop(self, img): y, x, _ = img.shape - startx = x // 2 - (cropx // 2) - starty = y // 2 - (cropy // 2) - return img[starty:starty + cropy, startx:startx + cropx, :] + startx = x // 2 - (224 // 2) + starty = y // 2 - (224 // 2) + return img[starty:starty + 224, startx:startx + 224, :] def __call__(self, img): """ @@ -196,7 +195,7 @@ def __call__(self, img): if not isinstance(img, (np.ndarray, Image.Image)): raise TypeError("Input should be NumPy or PIL image, got {}.".format(type(img))) img = self.resize(img) - img = self._center_crop(img, 224, 224) + img = self._center_crop(img) img = self.normalize(img) img = hwc2chw(img) @@ -224,9 +223,6 @@ def apply_ds(self, imagefolder_ds, repeat_size=1, batch_size=32, return imagefolder_ds - def postprocess(self, input, strategy='TOP1_CLASS'): - return _postprocess(input, self.labels, strategy=strategy) - mnist_transform = MnistTransform() cifar10_transform = Cifar10Transform() From db3364b719506117279bb823793c97a7dc92e514 Mon Sep 17 00:00:00 2001 From: leonwanghui Date: Fri, 19 Feb 2021 17:44:22 +0800 Subject: [PATCH 2/3] Add Grayscale transform operator Signed-off-by: leonwanghui --- setup.py | 2 +- tests/st/resnet50.py | 3 ++- tests/ut/vision/test_transforms.py | 8 ++++++++ tinyms/serving/client/client.py | 6 +----- tinyms/vision/_transform_ops.py | 2 ++ tinyms/vision/transforms.py | 6 ++++-- 6 files changed, 18 insertions(+), 9 deletions(-) diff --git a/setup.py b/setup.py index 5afb2e0c..34c1c13f 100644 --- a/setup.py +++ b/setup.py @@ -39,7 +39,7 @@ def _write_version(file): required_package = [ 'scipy >= 1.5.3', 'mindspore == 1.1.1', - 'opencv-python >= 4.4.0', + 'Pillow >= 6.2.0', 'requests >= 2.22.0', 'flask >= 1.1.1', 'wheel >= 0.32.0', diff --git a/tests/st/resnet50.py b/tests/st/resnet50.py index b659b475..672b51b3 100644 --- a/tests/st/resnet50.py +++ b/tests/st/resnet50.py @@ -45,7 +45,8 @@ def create_dataset(data_path, batch_size=32, repeat_size=1, num_parallel_workers cifar_ds = cifar10_transform.apply_ds(cifar_ds, repeat_size=repeat_size, batch_size=batch_size, - num_parallel_workers=num_parallel_workers) + num_parallel_workers=num_parallel_workers, + training=training) return cifar_ds diff --git a/tests/ut/vision/test_transforms.py b/tests/ut/vision/test_transforms.py index 2d2af7ad..24bf6698 100644 --- a/tests/ut/vision/test_transforms.py +++ b/tests/ut/vision/test_transforms.py @@ -22,6 +22,14 @@ def test_mnist_transform(): img = mnist_transform(img) print(img) + img = np.ones((32, 32, 1)) + img = mnist_transform(img) + print(img) + + img = np.ones((32, 32, 3)) + img = mnist_transform(img) + print(img) + def test_mnist_transform_postprocess(): input = np.array([[10, 1, 4, 2, 5, 18, -10, -4, 3, 7]]) diff --git a/tinyms/serving/client/client.py b/tinyms/serving/client/client.py index 7e481b8d..6d346700 100644 --- a/tinyms/serving/client/client.py +++ b/tinyms/serving/client/client.py @@ -15,9 +15,7 @@ import os import json import sys -import cv2 import requests -import numpy as np from PIL import Image from tinyms.vision import mnist_transform, cifar10_transform, imagefolder_transform @@ -45,14 +43,12 @@ def predict(img_path, servable_name, dataset_name="mnist"): print("The image path "+img_path+" not exist!") sys.exit(0) + img_data = Image.open(img_path) if dataset_name == "mnist": - img_data = np.asarray(cv2.imread(img_path, cv2.IMREAD_GRAYSCALE), dtype=np.float32) img_data = mnist_transform(img_data) elif dataset_name == "cifar10": - img_data = np.asarray(Image.open(img_path), dtype=np.float32) img_data = cifar10_transform(img_data) else: - img_data = np.asarray(Image.open(img_path), dtype=np.float32) img_data = imagefolder_transform(img_data) # Construct the request payload diff --git a/tinyms/vision/_transform_ops.py b/tinyms/vision/_transform_ops.py index 877e418f..d8aa48cb 100644 --- a/tinyms/vision/_transform_ops.py +++ b/tinyms/vision/_transform_ops.py @@ -13,6 +13,7 @@ # limitations under the License. # ============================================================================ +from mindspore.dataset.vision.py_transforms import Grayscale from mindspore.dataset.vision.c_transforms import * from mindspore.dataset.transforms.c_transforms import * @@ -24,6 +25,7 @@ 'CutOut', 'Decode', 'Equalize', + 'Grayscale', 'HWC2CHW', 'Invert', 'MixUpBatch', diff --git a/tinyms/vision/transforms.py b/tinyms/vision/transforms.py index b2fb5339..6c904f31 100644 --- a/tinyms/vision/transforms.py +++ b/tinyms/vision/transforms.py @@ -58,6 +58,7 @@ class MnistTransform(DatasetTransform): def __init__(self): labels = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] super().__init__(labels=labels) + self.grayscale = Grayscale() self.resize = Resize((32, 32)) self.normalize = Rescale(1 / 0.3081, -1 * 0.1307 / 0.3081) self.rescale = Rescale(1.0 / 255.0, 0.0) @@ -75,8 +76,9 @@ def __call__(self, img): """ if not isinstance(img, (np.ndarray, Image.Image)): raise TypeError("Input should be NumPy or PIL image, got {}.".format(type(img))) - if not img.ndim == 2: - raise TypeError("Input should be 2-D Numpy, got {}.".format(img.ndim)) + if isinstance(img, np.ndarray): + img = Image.fromarray(img, mode='RGB') + img = np.asarray(self.grayscale(img), dtype=np.float32) img = np.expand_dims(img, 2) img = self.resize(img) img = self.normalize(img) From 9c02ae6554d18ed4dac9dc9621798df469171c05 Mon Sep 17 00:00:00 2001 From: leonwanghui Date: Sat, 20 Feb 2021 10:24:50 +0800 Subject: [PATCH 3/3] Unify the apply_ds funtion to DatasetTransform --- tinyms/vision/transforms.py | 68 +++++++++++++++++-------------------- 1 file changed, 32 insertions(+), 36 deletions(-) diff --git a/tinyms/vision/transforms.py b/tinyms/vision/transforms.py index 6c904f31..5d710cc8 100644 --- a/tinyms/vision/transforms.py +++ b/tinyms/vision/transforms.py @@ -34,6 +34,22 @@ def __init__(self, labels=None): self.labels = labels self.transform_strategy = ['TOP1_CLASS', 'TOP5_CLASS'] + def apply_ds(self, ds, trans_func=None, repeat_size=1, batch_size=32, + num_parallel_workers=None): + if not isinstance(trans_func, list): + raise TypeError('trans_func must be list') + + # apply map operations on datasets + ds = ds.map(operations=TypeCast(ts.int32), input_columns="label", + num_parallel_workers=num_parallel_workers) + ds = ds.map(operations=trans_func, input_columns="image", num_parallel_workers=num_parallel_workers) + # apply batch operations + ds = ds.batch(batch_size, drop_remainder=True) + # apply repeat operations + ds = ds.repeat(repeat_size) + + return ds + def postprocess(self, input, strategy='TOP1_CLASS'): if not isinstance(input, np.ndarray): raise TypeError("Input should be NumPy, got {}.".format(type(input))) @@ -62,7 +78,6 @@ def __init__(self): self.resize = Resize((32, 32)) self.normalize = Rescale(1 / 0.3081, -1 * 0.1307 / 0.3081) self.rescale = Rescale(1.0 / 255.0, 0.0) - self.type_cast = TypeCast(ts.int32) def __call__(self, img): """ @@ -91,15 +106,10 @@ def apply_ds(self, mnist_ds, repeat_size=1, batch_size=32, num_parallel_workers= if not isinstance(mnist_ds, MnistDataset): raise TypeError("Input should be MnistDataset, got {}.".format(type(mnist_ds))) - c_trans = [self.resize, self.normalize, self.rescale, hwc2chw] - # apply map operations on images - mnist_ds = mnist_ds.map(operations=self.type_cast, input_columns="label", - num_parallel_workers=num_parallel_workers) - mnist_ds = mnist_ds.map(operations=c_trans, input_columns="image", num_parallel_workers=num_parallel_workers) - # apply batch operations - mnist_ds = mnist_ds.batch(batch_size, drop_remainder=True) - # apply repeat operations - mnist_ds = mnist_ds.repeat(repeat_size) + trans_func = [self.resize, self.normalize, self.rescale, hwc2chw] + # apply transform functions on mnist dataset + mnist_ds = super().apply_ds(mnist_ds, trans_func=trans_func, repeat_size=repeat_size, + batch_size=batch_size, num_parallel_workers=num_parallel_workers) return mnist_ds @@ -114,7 +124,6 @@ def __init__(self): self.resize = Resize((224, 224)) self.rescale = Rescale(1.0 / 255.0, 0.0) self.normalize = Normalize([0.4914, 0.4822, 0.4465], [0.2023, 0.1994, 0.2010]) - self.type_cast = TypeCast(ts.int32) def __call__(self, img): """ @@ -140,19 +149,13 @@ def apply_ds(self, cifar10_ds, repeat_size=1, batch_size=32, if not isinstance(cifar10_ds, Cifar10Dataset): raise TypeError("Input should be Cifar10Dataset, got {}.".format(type(cifar10_ds))) - c_trans = [] + trans_func = [] if training: - c_trans += [self.random_crop, self.random_horizontal_flip] - c_trans += [self.resize, self.rescale, self.normalize, hwc2chw] - # apply map operations on images - cifar10_ds = cifar10_ds.map(operations=self.type_cast, input_columns="label", - num_parallel_workers=num_parallel_workers) - cifar10_ds = cifar10_ds.map(operations=c_trans, input_columns="image", - num_parallel_workers=num_parallel_workers) - # apply batch operations - cifar10_ds = cifar10_ds.batch(batch_size, drop_remainder=True) - # apply repeat operations - cifar10_ds = cifar10_ds.repeat(repeat_size) + trans_func += [self.random_crop, self.random_horizontal_flip] + trans_func += [self.resize, self.rescale, self.normalize, hwc2chw] + # apply transform functions on cifar10 dataset + cifar10_ds = super().apply_ds(cifar10_ds, trans_func=trans_func, repeat_size=repeat_size, + batch_size=batch_size, num_parallel_workers=num_parallel_workers) return cifar10_ds @@ -176,7 +179,6 @@ def __init__(self): self.center_crop = CenterCrop(224) self.normalize = Normalize([0.485 * 255, 0.456 * 255, 0.406 * 255], [0.229 * 255, 0.224 * 255, 0.225 * 255]) - self.type_cast = TypeCast(ts.int32) def _center_crop(self, img): y, x, _ = img.shape @@ -209,19 +211,13 @@ def apply_ds(self, imagefolder_ds, repeat_size=1, batch_size=32, raise TypeError("Input should be ImageFolderDataset, got {}.".format(type(imagefolder_ds))) if training: - c_trans = [self.random_crop_decode_resize, self.random_horizontal_flip] + trans_func = [self.random_crop_decode_resize, self.random_horizontal_flip] else: - c_trans = [decode, self.resize, self.center_crop] - c_trans += [self.normalize, hwc2chw] - # apply map operations on images - imagefolder_ds = imagefolder_ds.map(operations=self.type_cast, input_columns="label", - num_parallel_workers=num_parallel_workers) - imagefolder_ds = imagefolder_ds.map(operations=c_trans, input_columns="image", - num_parallel_workers=num_parallel_workers) - # apply batch operations - imagefolder_ds = imagefolder_ds.batch(batch_size, drop_remainder=True) - # apply repeat operations - imagefolder_ds = imagefolder_ds.repeat(repeat_size) + trans_func = [decode, self.resize, self.center_crop] + trans_func += [self.normalize, hwc2chw] + # apply transform functions on imagefolder dataset + imagefolder_ds = super().apply_ds(imagefolder_ds, trans_func=trans_func, repeat_size=repeat_size, + batch_size=batch_size, num_parallel_workers=num_parallel_workers) return imagefolder_ds