chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,126 @@
|
||||
# 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 paddle # noqa: F401
|
||||
from paddle import nn # noqa: F401
|
||||
|
||||
from . import ( # noqa: F401
|
||||
datasets,
|
||||
models,
|
||||
ops,
|
||||
transforms,
|
||||
)
|
||||
from .datasets import ( # noqa: F401
|
||||
MNIST,
|
||||
VOC2012,
|
||||
Cifar10,
|
||||
Cifar100,
|
||||
DatasetFolder,
|
||||
FashionMNIST,
|
||||
Flowers,
|
||||
ImageFolder,
|
||||
)
|
||||
from .image import (
|
||||
get_image_backend,
|
||||
image_load,
|
||||
set_image_backend,
|
||||
)
|
||||
from .models import ( # noqa: F401
|
||||
VGG,
|
||||
AlexNet,
|
||||
DenseNet,
|
||||
GoogLeNet,
|
||||
InceptionV3,
|
||||
LeNet,
|
||||
MobileNetV1,
|
||||
MobileNetV2,
|
||||
MobileNetV3Large,
|
||||
MobileNetV3Small,
|
||||
ResNet,
|
||||
ShuffleNetV2,
|
||||
SqueezeNet,
|
||||
alexnet,
|
||||
densenet121,
|
||||
densenet161,
|
||||
densenet169,
|
||||
densenet201,
|
||||
densenet264,
|
||||
googlenet,
|
||||
inception_v3,
|
||||
mobilenet_v1,
|
||||
mobilenet_v2,
|
||||
mobilenet_v3_large,
|
||||
mobilenet_v3_small,
|
||||
resnet18,
|
||||
resnet34,
|
||||
resnet50,
|
||||
resnet101,
|
||||
resnet152,
|
||||
resnext50_32x4d,
|
||||
resnext50_64x4d,
|
||||
resnext101_32x4d,
|
||||
resnext101_64x4d,
|
||||
resnext152_32x4d,
|
||||
resnext152_64x4d,
|
||||
shufflenet_v2_swish,
|
||||
shufflenet_v2_x0_5,
|
||||
shufflenet_v2_x0_25,
|
||||
shufflenet_v2_x0_33,
|
||||
shufflenet_v2_x1_0,
|
||||
shufflenet_v2_x1_5,
|
||||
shufflenet_v2_x2_0,
|
||||
squeezenet1_0,
|
||||
squeezenet1_1,
|
||||
vgg11,
|
||||
vgg13,
|
||||
vgg16,
|
||||
vgg19,
|
||||
wide_resnet50_2,
|
||||
wide_resnet101_2,
|
||||
)
|
||||
from .transforms import ( # noqa: F401
|
||||
BaseTransform,
|
||||
BrightnessTransform,
|
||||
CenterCrop,
|
||||
ColorJitter,
|
||||
Compose,
|
||||
ContrastTransform,
|
||||
Grayscale,
|
||||
HueTransform,
|
||||
Normalize,
|
||||
Pad,
|
||||
RandomCrop,
|
||||
RandomHorizontalFlip,
|
||||
RandomResizedCrop,
|
||||
RandomRotation,
|
||||
RandomVerticalFlip,
|
||||
Resize,
|
||||
SaturationTransform,
|
||||
ToTensor,
|
||||
Transpose,
|
||||
adjust_brightness,
|
||||
adjust_contrast,
|
||||
adjust_hue,
|
||||
center_crop,
|
||||
crop,
|
||||
hflip,
|
||||
normalize,
|
||||
pad,
|
||||
resize,
|
||||
rotate,
|
||||
to_grayscale,
|
||||
to_tensor,
|
||||
vflip,
|
||||
)
|
||||
|
||||
__all__ = ['set_image_backend', 'get_image_backend', 'image_load']
|
||||
@@ -0,0 +1,30 @@
|
||||
# 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.
|
||||
|
||||
from .cifar import Cifar10, Cifar100
|
||||
from .flowers import Flowers
|
||||
from .folder import DatasetFolder, ImageFolder
|
||||
from .mnist import MNIST, FashionMNIST
|
||||
from .voc2012 import VOC2012
|
||||
|
||||
__all__ = [
|
||||
'DatasetFolder',
|
||||
'ImageFolder',
|
||||
'MNIST',
|
||||
'FashionMNIST',
|
||||
'Flowers',
|
||||
'Cifar10',
|
||||
'Cifar100',
|
||||
'VOC2012',
|
||||
]
|
||||
@@ -0,0 +1,314 @@
|
||||
# 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.
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import pickle
|
||||
import tarfile
|
||||
from functools import lru_cache
|
||||
from typing import TYPE_CHECKING, Any, Literal
|
||||
|
||||
import numpy as np
|
||||
import numpy.typing as npt
|
||||
from PIL import Image
|
||||
|
||||
import paddle
|
||||
from paddle.dataset.common import _check_exists_and_download, md5file
|
||||
from paddle.io import Dataset
|
||||
|
||||
if TYPE_CHECKING:
|
||||
import numpy.typing as npt
|
||||
|
||||
from paddle._typing.dtype_like import _DTypeLiteral
|
||||
from paddle.vision.transforms.transforms import _Transform
|
||||
|
||||
from ..image import _ImageBackend, _ImageDataType
|
||||
|
||||
_DatasetMode = Literal["train", "test"]
|
||||
|
||||
__all__ = []
|
||||
|
||||
URL_PREFIX = 'https://dataset.bj.bcebos.com/cifar/'
|
||||
CIFAR10_URL = URL_PREFIX + 'cifar-10-python.tar.gz'
|
||||
CIFAR10_MD5 = 'c58f30108f718f92721af3b95e74349a'
|
||||
CIFAR100_URL = URL_PREFIX + 'cifar-100-python.tar.gz'
|
||||
CIFAR100_MD5 = 'eb9058c3a382ffc7106e4002c42a8d85'
|
||||
|
||||
MODE_FLAG_MAP = {
|
||||
'train10': 'data_batch',
|
||||
'test10': 'test_batch',
|
||||
'train100': 'train',
|
||||
'test100': 'test',
|
||||
}
|
||||
|
||||
|
||||
@lru_cache(maxsize=8)
|
||||
def _cached_md5file(path, _mtime_ns, _size):
|
||||
return md5file(path)
|
||||
|
||||
|
||||
def _check_local_cifar_md5(path, expected_md5):
|
||||
path = os.path.abspath(path)
|
||||
stat = os.stat(path)
|
||||
file_md5 = _cached_md5file(path, stat.st_mtime_ns, stat.st_size)
|
||||
if file_md5 != expected_md5:
|
||||
raise ValueError(
|
||||
"Loading unverified local CIFAR pickle archive is disabled. "
|
||||
f"Please use the official archive with MD5 {expected_md5}."
|
||||
)
|
||||
|
||||
|
||||
class Cifar10(Dataset[tuple["_ImageDataType", "npt.NDArray[Any]"]]):
|
||||
"""
|
||||
Implementation of `Cifar-10 <https://www.cs.toronto.edu/~kriz/cifar.html>`_
|
||||
dataset, which has 10 categories.
|
||||
|
||||
Args:
|
||||
data_file (str|None, optional): Path to data file, can be set None if
|
||||
:attr:`download` is True. Default None, default data path: ~/.cache/paddle/dataset/cifar
|
||||
mode (str, optional): Either train or test mode. Default 'train'.
|
||||
transform (Callable|None, optional): transform to perform on image, None for no transform. Default: None.
|
||||
download (bool, optional): download dataset automatically if :attr:`data_file` is None. Default True.
|
||||
backend (str|None, optional): Specifies which type of image to be returned:
|
||||
PIL.Image or numpy.ndarray. Should be one of {'pil', 'cv2'}.
|
||||
If this option is not set, will get backend from :ref:`paddle.vision.get_image_backend <api_paddle_vision_get_image_backend>`,
|
||||
default backend is 'pil'. Default: None.
|
||||
|
||||
Returns:
|
||||
:ref:`api_paddle_io_Dataset`. An instance of Cifar10 dataset.
|
||||
|
||||
Examples:
|
||||
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> # doctest: +TIMEOUT(60)
|
||||
>>> import itertools
|
||||
>>> import paddle
|
||||
>>> import paddle.vision.transforms as T
|
||||
>>> from paddle.vision.datasets import Cifar10
|
||||
|
||||
>>> cifar10 = Cifar10()
|
||||
>>> print(len(cifar10))
|
||||
50000
|
||||
|
||||
>>> for i in range(5): # only show first 5 images
|
||||
... img, label = cifar10[i]
|
||||
... # do something with img and label
|
||||
... print(type(img), img.size, label)
|
||||
... # <class 'PIL.Image.Image'> (32, 32) 6
|
||||
|
||||
|
||||
>>> transform = T.Compose(
|
||||
... [
|
||||
... T.Resize(64),
|
||||
... T.ToTensor(),
|
||||
... T.Normalize(
|
||||
... mean=[0.5, 0.5, 0.5],
|
||||
... std=[0.5, 0.5, 0.5],
|
||||
... to_rgb=True,
|
||||
... ),
|
||||
... ]
|
||||
... )
|
||||
>>> cifar10_test = Cifar10(
|
||||
... mode="test",
|
||||
... transform=transform, # apply transform to every image
|
||||
... backend="cv2", # use OpenCV as image transform backend
|
||||
... )
|
||||
>>> print(len(cifar10_test))
|
||||
10000
|
||||
|
||||
>>> for img, label in itertools.islice(iter(cifar10_test), 5): # only show first 5 images
|
||||
... # do something with img and label
|
||||
... assert isinstance(img, paddle.Tensor)
|
||||
... print(type(img), img.shape, label)
|
||||
... # <class 'paddle.Tensor'> [3, 64, 64] 3
|
||||
|
||||
"""
|
||||
|
||||
mode: _DatasetMode
|
||||
backend: _ImageBackend
|
||||
data_file: str | None
|
||||
transform: _Transform[Any, Any] | None
|
||||
dtype: _DTypeLiteral
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
data_file: str | None = None,
|
||||
mode: _DatasetMode = 'train',
|
||||
transform: _Transform[Any, Any] | None = None,
|
||||
download: bool = True,
|
||||
backend: _ImageBackend | None = None,
|
||||
) -> None:
|
||||
assert mode.lower() in [
|
||||
'train',
|
||||
'test',
|
||||
], f"mode.lower() should be 'train' or 'test', but got {mode}"
|
||||
self.mode = mode.lower()
|
||||
|
||||
if backend is None:
|
||||
backend = paddle.vision.get_image_backend()
|
||||
if backend not in ['pil', 'cv2']:
|
||||
raise ValueError(
|
||||
f"Expected backend are one of ['pil', 'cv2'], but got {backend}"
|
||||
)
|
||||
self.backend = backend
|
||||
|
||||
self._init_url_md5_flag()
|
||||
|
||||
self.data_file = data_file
|
||||
if self.data_file is None:
|
||||
assert download, (
|
||||
"data_file is not set and downloading automatically is disabled"
|
||||
)
|
||||
self.data_file = _check_exists_and_download(
|
||||
data_file, self.data_url, self.data_md5, 'cifar', download
|
||||
)
|
||||
elif not os.path.exists(self.data_file):
|
||||
raise ValueError(
|
||||
f"Local CIFAR archive does not exist: {self.data_file}."
|
||||
)
|
||||
else:
|
||||
_check_local_cifar_md5(self.data_file, self.data_md5)
|
||||
|
||||
self.transform = transform
|
||||
|
||||
# read dataset into memory
|
||||
self._load_data()
|
||||
|
||||
self.dtype = paddle.get_default_dtype()
|
||||
|
||||
def _init_url_md5_flag(self):
|
||||
self.data_url = CIFAR10_URL
|
||||
self.data_md5 = CIFAR10_MD5
|
||||
self.flag = MODE_FLAG_MAP[self.mode + '10']
|
||||
|
||||
def _load_data(self):
|
||||
self.data = []
|
||||
with tarfile.open(self.data_file, mode='r') as f:
|
||||
names = (
|
||||
each_item.name for each_item in f if self.flag in each_item.name
|
||||
)
|
||||
|
||||
names = sorted(names)
|
||||
|
||||
for name in names:
|
||||
batch = pickle.load(f.extractfile(name), encoding='bytes')
|
||||
|
||||
data = batch[b'data']
|
||||
labels = batch.get(b'labels', batch.get(b'fine_labels', None))
|
||||
assert labels is not None
|
||||
for sample, label in zip(data, labels):
|
||||
self.data.append((sample, label))
|
||||
|
||||
def __getitem__(self, idx: int) -> tuple[_ImageDataType, npt.NDArray[Any]]:
|
||||
image, label = self.data[idx]
|
||||
image = np.reshape(image, [3, 32, 32])
|
||||
image = image.transpose([1, 2, 0])
|
||||
|
||||
if self.backend == 'pil':
|
||||
image = Image.fromarray(image.astype('uint8'))
|
||||
if self.transform is not None:
|
||||
image = self.transform(image)
|
||||
|
||||
if self.backend == 'pil':
|
||||
return image, np.array(label).astype('int64')
|
||||
|
||||
return image.astype(self.dtype), np.array(label).astype('int64')
|
||||
|
||||
def __len__(self):
|
||||
return len(self.data)
|
||||
|
||||
|
||||
class Cifar100(Cifar10):
|
||||
"""
|
||||
Implementation of `Cifar-100 <https://www.cs.toronto.edu/~kriz/cifar.html>`_
|
||||
dataset, which has 100 categories.
|
||||
|
||||
Args:
|
||||
data_file (str|None, optional): path to data file, can be set None if
|
||||
:attr:`download` is True. Default: None, default data path: ~/.cache/paddle/dataset/cifar
|
||||
mode (str, optional): Either train or test mode. Default 'train'.
|
||||
transform (Callable|None, optional): transform to perform on image, None for no transform. Default: None.
|
||||
download (bool, optional): download dataset automatically if :attr:`data_file` is None. Default True.
|
||||
backend (str|None, optional): Specifies which type of image to be returned:
|
||||
PIL.Image or numpy.ndarray. Should be one of {'pil', 'cv2'}.
|
||||
If this option is not set, will get backend from :ref:`paddle.vision.get_image_backend <api_paddle_vision_get_image_backend>`,
|
||||
default backend is 'pil'. Default: None.
|
||||
|
||||
Returns:
|
||||
:ref:`api_paddle_io_Dataset`. An instance of Cifar100 dataset.
|
||||
|
||||
Examples:
|
||||
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> # doctest: +TIMEOUT(60)
|
||||
>>> import itertools
|
||||
>>> import paddle
|
||||
>>> import paddle.vision.transforms as T
|
||||
>>> from paddle.vision.datasets import Cifar100
|
||||
|
||||
>>> cifar100 = Cifar100()
|
||||
>>> print(len(cifar100))
|
||||
50000
|
||||
|
||||
>>> for i in range(5): # only show first 5 images
|
||||
... img, label = cifar100[i]
|
||||
... # do something with img and label
|
||||
... print(type(img), img.size, label)
|
||||
... # <class 'PIL.Image.Image'> (32, 32) 19
|
||||
|
||||
|
||||
>>> transform = T.Compose(
|
||||
... [
|
||||
... T.Resize(64),
|
||||
... T.ToTensor(),
|
||||
... T.Normalize(
|
||||
... mean=[0.5, 0.5, 0.5],
|
||||
... std=[0.5, 0.5, 0.5],
|
||||
... to_rgb=True,
|
||||
... ),
|
||||
... ]
|
||||
... )
|
||||
|
||||
>>> cifar100_test = Cifar100(
|
||||
... mode="test",
|
||||
... transform=transform, # apply transform to every image
|
||||
... backend="cv2", # use OpenCV as image transform backend
|
||||
... )
|
||||
>>> print(len(cifar100_test))
|
||||
10000
|
||||
|
||||
>>> for img, label in itertools.islice(iter(cifar100_test), 5): # only show first 5 images
|
||||
... # do something with img and label
|
||||
... assert isinstance(img, paddle.Tensor)
|
||||
... print(type(img), img.shape, label)
|
||||
... # <class 'paddle.Tensor'> [3, 64, 64] 49
|
||||
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
data_file: str | None = None,
|
||||
mode: _DatasetMode = 'train',
|
||||
transform: _Transform[Any, Any] | None = None,
|
||||
download: bool = True,
|
||||
backend: _ImageBackend | None = None,
|
||||
) -> None:
|
||||
super().__init__(data_file, mode, transform, download, backend)
|
||||
|
||||
def _init_url_md5_flag(self):
|
||||
self.data_url = CIFAR100_URL
|
||||
self.data_md5 = CIFAR100_MD5
|
||||
self.flag = MODE_FLAG_MAP[self.mode + '100']
|
||||
@@ -0,0 +1,216 @@
|
||||
# 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.
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Any, Literal
|
||||
|
||||
if TYPE_CHECKING:
|
||||
import numpy.typing as npt
|
||||
|
||||
from paddle.vision.transforms.transforms import _Transform
|
||||
|
||||
from ..image import _ImageBackend, _ImageDataType
|
||||
|
||||
_DatasetMode = Literal["train", "valid", "test"]
|
||||
|
||||
import os
|
||||
import tarfile
|
||||
|
||||
import numpy as np
|
||||
from PIL import Image
|
||||
|
||||
import paddle
|
||||
from paddle.dataset.common import _check_exists_and_download
|
||||
from paddle.io import Dataset
|
||||
from paddle.utils import try_import
|
||||
from paddle.utils.download import _safe_extract_tar
|
||||
|
||||
__all__ = []
|
||||
|
||||
DATA_URL = 'http://paddlemodels.bj.bcebos.com/flowers/102flowers.tgz'
|
||||
LABEL_URL = 'http://paddlemodels.bj.bcebos.com/flowers/imagelabels.mat'
|
||||
SETID_URL = 'http://paddlemodels.bj.bcebos.com/flowers/setid.mat'
|
||||
DATA_MD5 = '52808999861908f626f3c1f4e79d11fa'
|
||||
LABEL_MD5 = 'e0620be6f572b9609742df49c70aed4d'
|
||||
SETID_MD5 = 'a5357ecc9cb78c4bef273ce3793fc85c'
|
||||
|
||||
# In official 'readme', tstid is the flag of test data
|
||||
# and trnid is the flag of train data. But test data is more than train data.
|
||||
# So we exchange the train data and test data.
|
||||
MODE_FLAG_MAP = {'train': 'tstid', 'test': 'trnid', 'valid': 'valid'}
|
||||
|
||||
|
||||
class Flowers(Dataset[tuple["_ImageDataType", "npt.NDArray[np.int64]"]]):
|
||||
"""
|
||||
Implementation of `Flowers102 <https://www.robots.ox.ac.uk/~vgg/data/flowers/>`_
|
||||
dataset.
|
||||
|
||||
Args:
|
||||
data_file (str|None, optional): Path to data file, can be set None if
|
||||
:attr:`download` is True. Default: None, default data path: ~/.cache/paddle/dataset/flowers/.
|
||||
label_file (str|None, optional): Path to label file, can be set None if
|
||||
:attr:`download` is True. Default: None, default data path: ~/.cache/paddle/dataset/flowers/.
|
||||
setid_file (str|None, optional): Path to subset index file, can be set
|
||||
None if :attr:`download` is True. Default: None, default data path: ~/.cache/paddle/dataset/flowers/.
|
||||
mode (str, optional): Either train or test mode. Default 'train'.
|
||||
transform (Callable|None, optional): transform to perform on image, None for no transform. Default: None.
|
||||
download (bool|None, optional): download dataset automatically if :attr:`data_file` is None. Default: True.
|
||||
backend (str|None, optional): Specifies which type of image to be returned:
|
||||
PIL.Image or numpy.ndarray. Should be one of {'pil', 'cv2'}.
|
||||
If this option is not set, will get backend from :ref:`paddle.vision.get_image_backend <api_paddle_vision_get_image_backend>`,
|
||||
default backend is 'pil'. Default: None.
|
||||
|
||||
Returns:
|
||||
:ref:`api_paddle_io_Dataset`. An instance of Flowers dataset.
|
||||
|
||||
Examples:
|
||||
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> # doctest: +TIMEOUT(60)
|
||||
>>> import itertools
|
||||
>>> import paddle
|
||||
>>> import paddle.vision.transforms as T
|
||||
>>> from paddle.vision.datasets import Flowers
|
||||
|
||||
>>> flowers = Flowers()
|
||||
>>> print(len(flowers))
|
||||
6149
|
||||
|
||||
>>> for i in range(5): # only show first 5 images
|
||||
... img, label = flowers[i]
|
||||
... # do something with img and label
|
||||
... print(type(img), img.size, label)
|
||||
... # <class 'PIL.JpegImagePlugin.JpegImageFile'> (523, 500) [1]
|
||||
|
||||
>>> transform = T.Compose(
|
||||
... [
|
||||
... T.Resize(64),
|
||||
... T.ToTensor(),
|
||||
... T.Normalize(
|
||||
... mean=[0.5, 0.5, 0.5],
|
||||
... std=[0.5, 0.5, 0.5],
|
||||
... to_rgb=True,
|
||||
... ),
|
||||
... ]
|
||||
... )
|
||||
>>> flowers_test = Flowers(
|
||||
... mode="test",
|
||||
... transform=transform, # apply transform to every image
|
||||
... backend="cv2", # use OpenCV as image transform backend
|
||||
... )
|
||||
>>> print(len(flowers_test))
|
||||
1020
|
||||
|
||||
>>> for img, label in itertools.islice(iter(flowers_test), 5): # only show first 5 images
|
||||
... # do something with img and label
|
||||
... assert isinstance(img, paddle.Tensor)
|
||||
... print(type(img), img.shape, label)
|
||||
... # <class 'paddle.Tensor'> [3, 64, 96] [1]
|
||||
"""
|
||||
|
||||
backend: _ImageBackend
|
||||
data_file: str | None
|
||||
label_file: str | None
|
||||
setid_file: str | None
|
||||
mode: _DatasetMode
|
||||
transform: _Transform[Any, Any] | None
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
data_file: str | None = None,
|
||||
label_file: str | None = None,
|
||||
setid_file: str | None = None,
|
||||
mode: _DatasetMode = 'train',
|
||||
transform: _Transform[Any, Any] | None = None,
|
||||
download: bool = True,
|
||||
backend: _ImageBackend | None = None,
|
||||
) -> None:
|
||||
assert mode.lower() in [
|
||||
'train',
|
||||
'valid',
|
||||
'test',
|
||||
], f"mode should be 'train', 'valid' or 'test', but got {mode}"
|
||||
|
||||
if backend is None:
|
||||
backend = paddle.vision.get_image_backend()
|
||||
if backend not in ['pil', 'cv2']:
|
||||
raise ValueError(
|
||||
f"Expected backend are one of ['pil', 'cv2'], but got {backend}"
|
||||
)
|
||||
self.backend = backend
|
||||
|
||||
flag = MODE_FLAG_MAP[mode.lower()]
|
||||
|
||||
if not data_file:
|
||||
assert download, (
|
||||
"data_file is not set and downloading automatically is disabled"
|
||||
)
|
||||
data_file = _check_exists_and_download(
|
||||
data_file, DATA_URL, DATA_MD5, 'flowers', download
|
||||
)
|
||||
|
||||
if not label_file:
|
||||
assert download, (
|
||||
"label_file is not set and downloading automatically is disabled"
|
||||
)
|
||||
label_file = _check_exists_and_download(
|
||||
label_file, LABEL_URL, LABEL_MD5, 'flowers', download
|
||||
)
|
||||
|
||||
if not setid_file:
|
||||
assert download, (
|
||||
"setid_file is not set and downloading automatically is disabled"
|
||||
)
|
||||
setid_file = _check_exists_and_download(
|
||||
setid_file, SETID_URL, SETID_MD5, 'flowers', download
|
||||
)
|
||||
|
||||
self.transform = transform
|
||||
|
||||
data_tar = tarfile.open(data_file)
|
||||
self.data_path = data_file.replace(".tgz", "/")
|
||||
if not os.path.exists(self.data_path):
|
||||
os.mkdir(self.data_path)
|
||||
jpg_path = os.path.join(self.data_path, "jpg")
|
||||
if not os.path.exists(jpg_path):
|
||||
_safe_extract_tar(data_tar, self.data_path, on_unsafe='raise')
|
||||
|
||||
scio = try_import('scipy.io')
|
||||
self.labels = scio.loadmat(label_file)['labels'][0]
|
||||
self.indexes = scio.loadmat(setid_file)[flag][0]
|
||||
|
||||
def __getitem__(
|
||||
self, idx: int
|
||||
) -> tuple[_ImageDataType, npt.NDArray[np.int64]]:
|
||||
index = self.indexes[idx]
|
||||
label = np.array([self.labels[index - 1]])
|
||||
img_name = f"jpg/image_{index:05}.jpg"
|
||||
image = os.path.join(self.data_path, img_name)
|
||||
if self.backend == 'pil':
|
||||
image = Image.open(image)
|
||||
elif self.backend == 'cv2':
|
||||
image = np.array(Image.open(image))
|
||||
|
||||
if self.transform is not None:
|
||||
image = self.transform(image)
|
||||
|
||||
if self.backend == 'pil':
|
||||
return image, label.astype('int64')
|
||||
|
||||
return image.astype(paddle.get_default_dtype()), label.astype('int64')
|
||||
|
||||
def __len__(self):
|
||||
return len(self.indexes)
|
||||
@@ -0,0 +1,538 @@
|
||||
# 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.
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Any, Literal, TypeAlias
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from collections.abc import Callable, Sequence
|
||||
|
||||
from paddle._typing.dtype_like import _DTypeLiteral
|
||||
from paddle.vision.transforms.transforms import _Transform
|
||||
|
||||
from ..image import _ImageDataType
|
||||
|
||||
_AllowedExtensions: TypeAlias = Literal[
|
||||
'.jpg',
|
||||
'.jpeg',
|
||||
'.png',
|
||||
'.ppm',
|
||||
'.bmp',
|
||||
'.pgm',
|
||||
'.tif',
|
||||
'.tiff',
|
||||
'.webp',
|
||||
]
|
||||
|
||||
import os
|
||||
|
||||
from PIL import Image
|
||||
|
||||
import paddle
|
||||
from paddle.io import Dataset
|
||||
from paddle.utils import try_import
|
||||
|
||||
__all__ = []
|
||||
|
||||
|
||||
def has_valid_extension(filename: str, extensions: Sequence[str]) -> bool:
|
||||
"""Checks if a file is a valid extension.
|
||||
|
||||
Args:
|
||||
filename (str): path to a file
|
||||
extensions (list[str]|tuple[str]): extensions to consider
|
||||
|
||||
Returns:
|
||||
bool: True if the filename ends with one of given extensions
|
||||
"""
|
||||
assert isinstance(extensions, (list, tuple)), (
|
||||
"`extensions` must be list or tuple."
|
||||
)
|
||||
extensions = tuple([x.lower() for x in extensions])
|
||||
return filename.lower().endswith(extensions)
|
||||
|
||||
|
||||
def make_dataset(dir, class_to_idx, extensions, is_valid_file=None):
|
||||
images = []
|
||||
dir = os.path.expanduser(dir)
|
||||
|
||||
if extensions is not None:
|
||||
|
||||
def is_valid_file(x):
|
||||
return has_valid_extension(x, extensions)
|
||||
|
||||
for target in sorted(class_to_idx.keys()):
|
||||
d = os.path.join(dir, target)
|
||||
if not os.path.isdir(d):
|
||||
continue
|
||||
for root, _, fnames in sorted(os.walk(d, followlinks=True)):
|
||||
for fname in sorted(fnames):
|
||||
path = os.path.join(root, fname)
|
||||
if is_valid_file(path):
|
||||
item = (path, class_to_idx[target])
|
||||
images.append(item)
|
||||
|
||||
return images
|
||||
|
||||
|
||||
class DatasetFolder(Dataset[tuple["_ImageDataType", int]]):
|
||||
"""A generic data loader where the samples are arranged in this way:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
root/class_a/1.ext
|
||||
root/class_a/2.ext
|
||||
root/class_a/3.ext
|
||||
|
||||
root/class_b/123.ext
|
||||
root/class_b/456.ext
|
||||
root/class_b/789.ext
|
||||
|
||||
Args:
|
||||
root (str): Root directory path.
|
||||
loader (Callable|None, optional): A function to load a sample given its path. Default: None.
|
||||
extensions (list[str]|tuple[str]|None, optional): A list of allowed extensions.
|
||||
Both :attr:`extensions` and :attr:`is_valid_file` should not be passed.
|
||||
If this value is not set, the default is to use ('.jpg', '.jpeg', '.png',
|
||||
'.ppm', '.bmp', '.pgm', '.tif', '.tiff', '.webp'). Default: None.
|
||||
transform (Callable|None, optional): A function/transform that takes in
|
||||
a sample and returns a transformed version. Default: None.
|
||||
is_valid_file (Callable|None, optional): A function that takes path of a file
|
||||
and check if the file is a valid file. Both :attr:`extensions` and
|
||||
:attr:`is_valid_file` should not be passed. Default: None.
|
||||
|
||||
Returns:
|
||||
:ref:`api_paddle_io_Dataset`. An instance of DatasetFolder.
|
||||
|
||||
Attributes:
|
||||
classes (list[str]): List of the class names.
|
||||
class_to_idx (dict[str, int]): Dict with items (class_name, class_index).
|
||||
samples (list[tuple[str, int]]): List of (sample_path, class_index) tuples.
|
||||
targets (list[int]): The class_index value for each image in the dataset.
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import shutil
|
||||
>>> import tempfile
|
||||
>>> import cv2
|
||||
>>> import numpy as np
|
||||
>>> import paddle
|
||||
>>> import paddle.vision.transforms as T
|
||||
>>> from pathlib import Path
|
||||
>>> from paddle.vision.datasets import DatasetFolder
|
||||
|
||||
>>> def make_fake_file(img_path: str):
|
||||
... if img_path.endswith((".jpg", ".png", ".jpeg")):
|
||||
... fake_img = np.random.randint(0, 256, (32, 32, 3), dtype=np.uint8)
|
||||
... cv2.imwrite(img_path, fake_img)
|
||||
... elif img_path.endswith(".txt"):
|
||||
... with open(img_path, "w") as f:
|
||||
... f.write("This is a fake file.")
|
||||
|
||||
>>> def make_directory(root, directory_hierarchy, file_maker=make_fake_file):
|
||||
... root = Path(root)
|
||||
... root.mkdir(parents=True, exist_ok=True)
|
||||
... for subpath in directory_hierarchy:
|
||||
... if isinstance(subpath, str):
|
||||
... filepath = root / subpath
|
||||
... file_maker(str(filepath))
|
||||
... else:
|
||||
... dirname = list(subpath.keys())[0]
|
||||
... make_directory(root / dirname, subpath[dirname])
|
||||
|
||||
>>> directory_hierarchy = [
|
||||
... {"class_0": [
|
||||
... "abc.jpg",
|
||||
... "def.png"]},
|
||||
... {"class_1": [
|
||||
... "ghi.jpeg",
|
||||
... "jkl.png",
|
||||
... {"mno": [
|
||||
... "pqr.jpeg",
|
||||
... "stu.jpg"]}]},
|
||||
... "this_will_be_ignored.txt",
|
||||
... ] # fmt: skip
|
||||
|
||||
>>> # You can replace this with any directory to explore the structure
|
||||
>>> # of generated data. e.g. fake_data_dir = "./temp_dir"
|
||||
>>> fake_data_dir = tempfile.mkdtemp()
|
||||
>>> make_directory(fake_data_dir, directory_hierarchy)
|
||||
>>> data_folder_1 = DatasetFolder(fake_data_dir)
|
||||
>>> print(data_folder_1.classes)
|
||||
['class_0', 'class_1']
|
||||
>>> print(data_folder_1.class_to_idx)
|
||||
{'class_0': 0, 'class_1': 1}
|
||||
>>> print(data_folder_1.samples)
|
||||
>>> # doctest: +SKIP(it's different with windows)
|
||||
[('./temp_dir/class_0/abc.jpg', 0), ('./temp_dir/class_0/def.png', 0),
|
||||
('./temp_dir/class_1/ghi.jpeg', 1), ('./temp_dir/class_1/jkl.png', 1),
|
||||
('./temp_dir/class_1/mno/pqr.jpeg', 1), ('./temp_dir/class_1/mno/stu.jpg', 1)]
|
||||
>>> # doctest: -SKIP
|
||||
>>> print(data_folder_1.targets)
|
||||
[0, 0, 1, 1, 1, 1]
|
||||
>>> print(len(data_folder_1))
|
||||
6
|
||||
|
||||
>>> for i in range(len(data_folder_1)):
|
||||
... img, label = data_folder_1[i]
|
||||
... # do something with img and label
|
||||
... print(type(img), img.size, label)
|
||||
... # <class 'PIL.Image.Image'> (32, 32) 0
|
||||
|
||||
|
||||
>>> transform = T.Compose(
|
||||
... [
|
||||
... T.Resize(64),
|
||||
... T.ToTensor(),
|
||||
... T.Normalize(
|
||||
... mean=[0.5, 0.5, 0.5],
|
||||
... std=[0.5, 0.5, 0.5],
|
||||
... to_rgb=True,
|
||||
... ),
|
||||
... ]
|
||||
... )
|
||||
|
||||
>>> def cv2_loader(path: str):
|
||||
... image = cv2.imread(path)
|
||||
... assert image is not None
|
||||
... return image
|
||||
|
||||
>>> data_folder_2 = DatasetFolder(
|
||||
... fake_data_dir,
|
||||
... loader=cv2_loader, # load image with OpenCV
|
||||
... extensions=(".jpg",), # only load *.jpg files
|
||||
... transform=transform, # apply transform to every image
|
||||
... )
|
||||
|
||||
>>> print([img_path for img_path, label in data_folder_2.samples])
|
||||
>>> # doctest: +SKIP(it's different with windows)
|
||||
['./temp_dir/class_0/abc.jpg', './temp_dir/class_1/mno/stu.jpg']
|
||||
>>> # doctest: -SKIP
|
||||
>>> print(len(data_folder_2))
|
||||
2
|
||||
|
||||
>>> for img, label in iter(data_folder_2):
|
||||
... # do something with img and label
|
||||
... assert isinstance(img, paddle.Tensor)
|
||||
... print(type(img), img.shape, label)
|
||||
... # <class 'paddle.Tensor'> [3, 64, 64] 0
|
||||
|
||||
>>> shutil.rmtree(fake_data_dir)
|
||||
"""
|
||||
|
||||
loader: Callable[..., _ImageDataType] | None
|
||||
extensions: Sequence[_AllowedExtensions] | None
|
||||
transform: _Transform[Any, Any] | None
|
||||
classes: list[str]
|
||||
class_to_idx: dict[str, int]
|
||||
samples: list[tuple[str, int]]
|
||||
targets: list[str]
|
||||
dtype: _DTypeLiteral
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
root: str,
|
||||
loader: Callable[..., _ImageDataType] | None = None,
|
||||
extensions: Sequence[_AllowedExtensions] | None = None,
|
||||
transform: _Transform[Any, Any] | None = None,
|
||||
is_valid_file: _ImageDataType | None = None,
|
||||
) -> None:
|
||||
self.root = root
|
||||
self.transform = transform
|
||||
if extensions is None:
|
||||
extensions = IMG_EXTENSIONS
|
||||
classes, class_to_idx = self._find_classes(self.root)
|
||||
samples = make_dataset(
|
||||
self.root, class_to_idx, extensions, is_valid_file
|
||||
)
|
||||
if len(samples) == 0:
|
||||
raise (
|
||||
RuntimeError(
|
||||
"Found 0 directories in subfolders of: " + self.root + "\n"
|
||||
"Supported extensions are: " + ",".join(extensions)
|
||||
)
|
||||
)
|
||||
|
||||
self.loader = default_loader if loader is None else loader
|
||||
self.extensions = extensions
|
||||
|
||||
self.classes = classes
|
||||
self.class_to_idx = class_to_idx
|
||||
self.samples = samples
|
||||
self.targets = [s[1] for s in samples]
|
||||
|
||||
self.dtype = paddle.get_default_dtype()
|
||||
|
||||
def _find_classes(self, dir: str) -> tuple[list[str], dict[str, int]]:
|
||||
"""
|
||||
Finds the class folders in a dataset.
|
||||
|
||||
Args:
|
||||
dir (string): Root directory path.
|
||||
|
||||
Returns:
|
||||
tuple: (classes, class_to_idx) where classes are relative to (dir),
|
||||
and class_to_idx is a dictionary.
|
||||
|
||||
"""
|
||||
classes = [d.name for d in os.scandir(dir) if d.is_dir()]
|
||||
classes.sort()
|
||||
class_to_idx = {classes[i]: i for i in range(len(classes))}
|
||||
return classes, class_to_idx
|
||||
|
||||
def __getitem__(self, index: int) -> tuple[_ImageDataType, int]:
|
||||
"""
|
||||
Args:
|
||||
index (int): Index
|
||||
|
||||
Returns:
|
||||
tuple: (sample, target) where target is class_index of the target class.
|
||||
"""
|
||||
path, target = self.samples[index]
|
||||
sample = self.loader(path)
|
||||
if self.transform is not None:
|
||||
sample = self.transform(sample)
|
||||
|
||||
return sample, target
|
||||
|
||||
def __len__(self):
|
||||
return len(self.samples)
|
||||
|
||||
|
||||
IMG_EXTENSIONS = (
|
||||
'.jpg',
|
||||
'.jpeg',
|
||||
'.png',
|
||||
'.ppm',
|
||||
'.bmp',
|
||||
'.pgm',
|
||||
'.tif',
|
||||
'.tiff',
|
||||
'.webp',
|
||||
)
|
||||
|
||||
|
||||
def pil_loader(path):
|
||||
with open(path, 'rb') as f:
|
||||
img = Image.open(f)
|
||||
return img.convert('RGB')
|
||||
|
||||
|
||||
def cv2_loader(path):
|
||||
cv2 = try_import('cv2')
|
||||
return cv2.cvtColor(cv2.imread(path), cv2.COLOR_BGR2RGB)
|
||||
|
||||
|
||||
def default_loader(path):
|
||||
from paddle.vision import get_image_backend
|
||||
|
||||
if get_image_backend() == 'cv2':
|
||||
return cv2_loader(path)
|
||||
else:
|
||||
return pil_loader(path)
|
||||
|
||||
|
||||
class ImageFolder(Dataset[list["_ImageDataType"]]):
|
||||
"""A generic data loader where the samples are arranged in this way:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
root/1.ext
|
||||
root/2.ext
|
||||
root/sub_dir/3.ext
|
||||
|
||||
Args:
|
||||
root (str): Root directory path.
|
||||
loader (Callable|None, optional): A function to load a sample given its path. Default: None.
|
||||
extensions (list[str]|tuple[str]|None, optional): A list of allowed extensions.
|
||||
Both :attr:`extensions` and :attr:`is_valid_file` should not be passed.
|
||||
If this value is not set, the default is to use ('.jpg', '.jpeg', '.png',
|
||||
'.ppm', '.bmp', '.pgm', '.tif', '.tiff', '.webp'). Default: None.
|
||||
transform (Callable|None, optional): A function/transform that takes in
|
||||
a sample and returns a transformed version. Default: None.
|
||||
is_valid_file (Callable|None, optional): A function that takes path of a file
|
||||
and check if the file is a valid file. Both :attr:`extensions` and
|
||||
:attr:`is_valid_file` should not be passed. Default: None.
|
||||
|
||||
Returns:
|
||||
:ref:`api_paddle_io_Dataset`. An instance of ImageFolder.
|
||||
|
||||
Attributes:
|
||||
samples (list[str]): List of sample path.
|
||||
|
||||
Example:
|
||||
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import shutil
|
||||
>>> import tempfile
|
||||
>>> import cv2
|
||||
>>> import numpy as np
|
||||
>>> import paddle
|
||||
>>> import paddle.vision.transforms as T
|
||||
>>> from pathlib import Path
|
||||
>>> from paddle.vision.datasets import ImageFolder
|
||||
|
||||
>>> def make_fake_file(img_path: str):
|
||||
... if img_path.endswith((".jpg", ".png", ".jpeg")):
|
||||
... fake_img = np.random.randint(0, 256, (32, 32, 3), dtype=np.uint8)
|
||||
... cv2.imwrite(img_path, fake_img)
|
||||
... elif img_path.endswith(".txt"):
|
||||
... with open(img_path, "w") as f:
|
||||
... f.write("This is a fake file.")
|
||||
|
||||
>>> def make_directory(root, directory_hierarchy, file_maker=make_fake_file):
|
||||
... root = Path(root)
|
||||
... root.mkdir(parents=True, exist_ok=True)
|
||||
... for subpath in directory_hierarchy:
|
||||
... if isinstance(subpath, str):
|
||||
... filepath = root / subpath
|
||||
... file_maker(str(filepath))
|
||||
... else:
|
||||
... dirname = list(subpath.keys())[0]
|
||||
... make_directory(root / dirname, subpath[dirname])
|
||||
|
||||
>>> directory_hierarchy = [
|
||||
... "abc.jpg",
|
||||
... "def.png",
|
||||
... {"ghi": [
|
||||
... "jkl.jpeg",
|
||||
... {"mno": [
|
||||
... "pqr.jpg"]}]},
|
||||
... "this_will_be_ignored.txt",
|
||||
... ] # fmt: skip
|
||||
|
||||
>>> # You can replace this with any directory to explore the structure
|
||||
>>> # of generated data. e.g. fake_data_dir = "./temp_dir"
|
||||
>>> fake_data_dir = tempfile.mkdtemp()
|
||||
>>> make_directory(fake_data_dir, directory_hierarchy)
|
||||
>>> image_folder_1 = ImageFolder(fake_data_dir)
|
||||
>>> print(image_folder_1.samples)
|
||||
>>> # doctest: +SKIP(it's different with windows)
|
||||
['./temp_dir/abc.jpg', './temp_dir/def.png',
|
||||
'./temp_dir/ghi/jkl.jpeg', './temp_dir/ghi/mno/pqr.jpg']
|
||||
>>> # doctest: -SKIP
|
||||
>>> print(len(image_folder_1))
|
||||
4
|
||||
|
||||
>>> for i in range(len(image_folder_1)):
|
||||
... (img,) = image_folder_1[i]
|
||||
... # do something with img
|
||||
... print(type(img), img.size)
|
||||
... # <class 'PIL.Image.Image'> (32, 32)
|
||||
|
||||
|
||||
>>> transform = T.Compose(
|
||||
... [
|
||||
... T.Resize(64),
|
||||
... T.ToTensor(),
|
||||
... T.Normalize(
|
||||
... mean=[0.5, 0.5, 0.5],
|
||||
... std=[0.5, 0.5, 0.5],
|
||||
... to_rgb=True,
|
||||
... ),
|
||||
... ]
|
||||
... )
|
||||
|
||||
>>> def cv2_loader(path: str):
|
||||
... image = cv2.imread(path)
|
||||
... assert image is not None
|
||||
... return image
|
||||
|
||||
>>> image_folder_2 = ImageFolder(
|
||||
... fake_data_dir,
|
||||
... loader=cv2_loader, # load image with OpenCV
|
||||
... extensions=(".jpg",), # only load *.jpg files
|
||||
... transform=transform, # apply transform to every image
|
||||
... )
|
||||
|
||||
>>> print(image_folder_2.samples)
|
||||
>>> # doctest: +SKIP(it's different with windows)
|
||||
['./temp_dir/abc.jpg', './temp_dir/ghi/mno/pqr.jpg']
|
||||
>>> # doctest: -SKIP
|
||||
>>> print(len(image_folder_2))
|
||||
2
|
||||
|
||||
>>> for (img,) in iter(image_folder_2):
|
||||
... # do something with img
|
||||
... assert isinstance(img, paddle.Tensor)
|
||||
... print(type(img), img.shape)
|
||||
... # <class 'paddle.Tensor'> [3, 64, 64]
|
||||
|
||||
>>> shutil.rmtree(fake_data_dir)
|
||||
"""
|
||||
|
||||
loader: Callable[..., _ImageDataType] | None
|
||||
extensions: Sequence[_AllowedExtensions] | None
|
||||
samples: list[str]
|
||||
transform: _Transform[Any, Any] | None
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
root: str,
|
||||
loader: Callable[..., _ImageDataType] | None = None,
|
||||
extensions: Sequence[_AllowedExtensions] | None = None,
|
||||
transform: _Transform[Any, Any] | None = None,
|
||||
is_valid_file: _ImageDataType | None = None,
|
||||
) -> None:
|
||||
self.root = root
|
||||
if extensions is None:
|
||||
extensions = IMG_EXTENSIONS
|
||||
|
||||
samples = []
|
||||
path = os.path.expanduser(root)
|
||||
|
||||
if extensions is not None:
|
||||
|
||||
def is_valid_file(x):
|
||||
return has_valid_extension(x, extensions)
|
||||
|
||||
for root, _, fnames in sorted(os.walk(path, followlinks=True)):
|
||||
for fname in sorted(fnames):
|
||||
f = os.path.join(root, fname)
|
||||
if is_valid_file(f):
|
||||
samples.append(f)
|
||||
|
||||
if len(samples) == 0:
|
||||
raise (
|
||||
RuntimeError(
|
||||
"Found 0 files in subfolders of: " + self.root + "\n"
|
||||
"Supported extensions are: " + ",".join(extensions)
|
||||
)
|
||||
)
|
||||
|
||||
self.loader = default_loader if loader is None else loader
|
||||
self.extensions = extensions
|
||||
self.samples = samples
|
||||
self.transform = transform
|
||||
|
||||
def __getitem__(self, index: int) -> list[_ImageDataType]:
|
||||
"""
|
||||
Args:
|
||||
index (int): Index
|
||||
|
||||
Returns:
|
||||
sample of specific index.
|
||||
"""
|
||||
path = self.samples[index]
|
||||
sample = self.loader(path)
|
||||
if self.transform is not None:
|
||||
sample = self.transform(sample)
|
||||
return [sample]
|
||||
|
||||
def __len__(self) -> int:
|
||||
return len(self.samples)
|
||||
@@ -0,0 +1,339 @@
|
||||
# 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.
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Any, Literal
|
||||
|
||||
if TYPE_CHECKING:
|
||||
import numpy.typing as npt
|
||||
|
||||
from paddle._typing.dtype_like import _DTypeLiteral
|
||||
from paddle.vision.transforms.transforms import _Transform
|
||||
|
||||
from ..image import _ImageBackend, _ImageDataType
|
||||
|
||||
_DatasetMode = Literal["train", "test"]
|
||||
|
||||
import gzip
|
||||
import struct
|
||||
|
||||
import numpy as np
|
||||
from PIL import Image
|
||||
|
||||
import paddle
|
||||
from paddle.dataset.common import _check_exists_and_download
|
||||
from paddle.io import Dataset
|
||||
|
||||
__all__ = []
|
||||
|
||||
|
||||
class MNIST(Dataset[tuple["_ImageDataType", "npt.NDArray[np.int64]"]]):
|
||||
"""
|
||||
Implementation of `MNIST <http://yann.lecun.com/exdb/mnist/>`_ dataset.
|
||||
|
||||
Args:
|
||||
image_path (str|None, optional): Path to image file, can be set None if
|
||||
:attr:`download` is True. Default: None, default data path: ~/.cache/paddle/dataset/mnist.
|
||||
label_path (str|None, optional): Path to label file, can be set None if
|
||||
:attr:`download` is True. Default: None, default data path: ~/.cache/paddle/dataset/mnist.
|
||||
mode (str, optional): Either train or test mode. Default 'train'.
|
||||
transform (Callable|None, optional): Transform to perform on image, None for no transform. Default: None.
|
||||
download (bool, optional): Download dataset automatically if
|
||||
:attr:`image_path` :attr:`label_path` is not set. Default: True.
|
||||
backend (str|None, optional): Specifies which type of image to be returned:
|
||||
PIL.Image or numpy.ndarray. Should be one of {'pil', 'cv2'}.
|
||||
If this option is not set, will get backend from :ref:`paddle.vision.get_image_backend <api_paddle_vision_get_image_backend>`,
|
||||
default backend is 'pil'. Default: None.
|
||||
|
||||
Returns:
|
||||
:ref:`api_paddle_io_Dataset`. An instance of MNIST dataset.
|
||||
|
||||
Examples:
|
||||
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import itertools
|
||||
>>> import paddle
|
||||
>>> import paddle.vision.transforms as T
|
||||
>>> from paddle.vision.datasets import MNIST
|
||||
|
||||
|
||||
>>> mnist = MNIST()
|
||||
>>> print(len(mnist))
|
||||
60000
|
||||
|
||||
>>> for i in range(5): # only show first 5 images
|
||||
... img, label = mnist[i]
|
||||
... # do something with img and label
|
||||
... print(type(img), img.size, label)
|
||||
... # <class 'PIL.Image.Image'> (28, 28) [5]
|
||||
|
||||
|
||||
>>> transform = T.Compose(
|
||||
... [
|
||||
... T.ToTensor(),
|
||||
... T.Normalize(
|
||||
... mean=[127.5],
|
||||
... std=[127.5],
|
||||
... ),
|
||||
... ]
|
||||
... )
|
||||
|
||||
>>> mnist_test = MNIST(
|
||||
... mode="test",
|
||||
... transform=transform, # apply transform to every image
|
||||
... backend="cv2", # use OpenCV as image transform backend
|
||||
... )
|
||||
>>> print(len(mnist_test))
|
||||
10000
|
||||
|
||||
>>> for img, label in itertools.islice(iter(mnist_test), 5): # only show first 5 images
|
||||
... # do something with img and label
|
||||
... assert isinstance(img, paddle.Tensor)
|
||||
... print(type(img), img.shape, label)
|
||||
... # <class 'paddle.Tensor'> [1, 28, 28] [7]
|
||||
"""
|
||||
|
||||
NAME = 'mnist'
|
||||
URL_PREFIX = 'https://dataset.bj.bcebos.com/mnist/'
|
||||
TEST_IMAGE_URL = URL_PREFIX + 't10k-images-idx3-ubyte.gz'
|
||||
TEST_IMAGE_MD5 = '9fb629c4189551a2d022fa330f9573f3'
|
||||
TEST_LABEL_URL = URL_PREFIX + 't10k-labels-idx1-ubyte.gz'
|
||||
TEST_LABEL_MD5 = 'ec29112dd5afa0611ce80d1b7f02629c'
|
||||
TRAIN_IMAGE_URL = URL_PREFIX + 'train-images-idx3-ubyte.gz'
|
||||
TRAIN_IMAGE_MD5 = 'f68b3c2dcbeaaa9fbdd348bbdeb94873'
|
||||
TRAIN_LABEL_URL = URL_PREFIX + 'train-labels-idx1-ubyte.gz'
|
||||
TRAIN_LABEL_MD5 = 'd53e105ee54ea40749a09fcbcd1e9432'
|
||||
|
||||
mode: _DatasetMode
|
||||
image_path: str | None
|
||||
label_path: str | None
|
||||
transform: _Transform[Any, Any] | None
|
||||
backend: _ImageBackend
|
||||
dtype: _DTypeLiteral
|
||||
labels: list
|
||||
images: list
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
image_path: str | None = None,
|
||||
label_path: str | None = None,
|
||||
mode: _DatasetMode = 'train',
|
||||
transform: _Transform[Any, Any] | None = None,
|
||||
download: bool = True,
|
||||
backend: _ImageBackend | None = None,
|
||||
) -> None:
|
||||
assert mode.lower() in [
|
||||
'train',
|
||||
'test',
|
||||
], f"mode should be 'train' or 'test', but got {mode}"
|
||||
|
||||
if backend is None:
|
||||
backend = paddle.vision.get_image_backend()
|
||||
if backend not in ['pil', 'cv2']:
|
||||
raise ValueError(
|
||||
f"Expected backend are one of ['pil', 'cv2'], but got {backend}"
|
||||
)
|
||||
self.backend = backend
|
||||
|
||||
self.mode = mode.lower()
|
||||
self.image_path = image_path
|
||||
if self.image_path is None:
|
||||
assert download, (
|
||||
"image_path is not set and downloading automatically is disabled"
|
||||
)
|
||||
image_url = (
|
||||
self.TRAIN_IMAGE_URL if mode == 'train' else self.TEST_IMAGE_URL
|
||||
)
|
||||
image_md5 = (
|
||||
self.TRAIN_IMAGE_MD5 if mode == 'train' else self.TEST_IMAGE_MD5
|
||||
)
|
||||
self.image_path = _check_exists_and_download(
|
||||
image_path, image_url, image_md5, self.NAME, download
|
||||
)
|
||||
|
||||
self.label_path = label_path
|
||||
if self.label_path is None:
|
||||
assert download, (
|
||||
"label_path is not set and downloading automatically is disabled"
|
||||
)
|
||||
label_url = (
|
||||
self.TRAIN_LABEL_URL
|
||||
if self.mode == 'train'
|
||||
else self.TEST_LABEL_URL
|
||||
)
|
||||
label_md5 = (
|
||||
self.TRAIN_LABEL_MD5
|
||||
if self.mode == 'train'
|
||||
else self.TEST_LABEL_MD5
|
||||
)
|
||||
self.label_path = _check_exists_and_download(
|
||||
label_path, label_url, label_md5, self.NAME, download
|
||||
)
|
||||
|
||||
self.transform = transform
|
||||
|
||||
# read dataset into memory
|
||||
self._parse_dataset()
|
||||
|
||||
self.dtype = paddle.get_default_dtype()
|
||||
|
||||
def _parse_dataset(self, buffer_size=100):
|
||||
self.images = []
|
||||
self.labels = []
|
||||
with gzip.GzipFile(self.image_path, 'rb') as image_file:
|
||||
img_buf = image_file.read()
|
||||
with gzip.GzipFile(self.label_path, 'rb') as label_file:
|
||||
lab_buf = label_file.read()
|
||||
|
||||
step_label = 0
|
||||
offset_img = 0
|
||||
# read from Big-endian
|
||||
# get file info from magic byte
|
||||
# image file : 16B
|
||||
magic_byte_img = '>IIII'
|
||||
magic_img, image_num, rows, cols = struct.unpack_from(
|
||||
magic_byte_img, img_buf, offset_img
|
||||
)
|
||||
offset_img += struct.calcsize(magic_byte_img)
|
||||
|
||||
offset_lab = 0
|
||||
# label file : 8B
|
||||
magic_byte_lab = '>II'
|
||||
magic_lab, label_num = struct.unpack_from(
|
||||
magic_byte_lab, lab_buf, offset_lab
|
||||
)
|
||||
offset_lab += struct.calcsize(magic_byte_lab)
|
||||
|
||||
while True:
|
||||
if step_label >= label_num:
|
||||
break
|
||||
fmt_label = '>' + str(buffer_size) + 'B'
|
||||
labels = struct.unpack_from(fmt_label, lab_buf, offset_lab)
|
||||
offset_lab += struct.calcsize(fmt_label)
|
||||
step_label += buffer_size
|
||||
|
||||
fmt_images = '>' + str(buffer_size * rows * cols) + 'B'
|
||||
images_temp = struct.unpack_from(
|
||||
fmt_images, img_buf, offset_img
|
||||
)
|
||||
images = np.reshape(
|
||||
images_temp, (buffer_size, rows * cols)
|
||||
).astype('float32')
|
||||
offset_img += struct.calcsize(fmt_images)
|
||||
|
||||
for i in range(buffer_size):
|
||||
self.images.append(images[i, :])
|
||||
self.labels.append(
|
||||
np.array([labels[i]]).astype('int64')
|
||||
)
|
||||
|
||||
def __getitem__(
|
||||
self, idx: int
|
||||
) -> tuple[_ImageDataType, npt.NDArray[np.int64]]:
|
||||
image, label = self.images[idx], self.labels[idx]
|
||||
image = np.reshape(image, [28, 28])
|
||||
|
||||
if self.backend == 'pil':
|
||||
image = Image.fromarray(image.astype('uint8'), mode='L')
|
||||
|
||||
if self.transform is not None:
|
||||
image = self.transform(image)
|
||||
|
||||
if self.backend == 'pil':
|
||||
return image, label.astype('int64')
|
||||
|
||||
return image.astype(self.dtype), label.astype('int64')
|
||||
|
||||
def __len__(self) -> int:
|
||||
return len(self.labels)
|
||||
|
||||
|
||||
class FashionMNIST(MNIST):
|
||||
"""
|
||||
Implementation of `Fashion-MNIST <https://github.com/zalandoresearch/fashion-mnist>`_ dataset.
|
||||
|
||||
Args:
|
||||
image_path (str, optional): Path to image file, can be set None if
|
||||
:attr:`download` is True. Default: None, default data path: ~/.cache/paddle/dataset/fashion-mnist.
|
||||
label_path (str, optional): Path to label file, can be set None if
|
||||
:attr:`download` is True. Default: None, default data path: ~/.cache/paddle/dataset/fashion-mnist.
|
||||
mode (str, optional): Either train or test mode. Default 'train'.
|
||||
transform (Callable, optional): Transform to perform on image, None for no transform. Default: None.
|
||||
download (bool, optional): Whether to download dataset automatically if
|
||||
:attr:`image_path` :attr:`label_path` is not set. Default: True.
|
||||
backend (str, optional): Specifies which type of image to be returned:
|
||||
PIL.Image or numpy.ndarray. Should be one of {'pil', 'cv2'}.
|
||||
If this option is not set, will get backend from :ref:`paddle.vision.get_image_backend <api_paddle_vision_get_image_backend>`,
|
||||
default backend is 'pil'. Default: None.
|
||||
|
||||
Returns:
|
||||
:ref:`api_paddle_io_Dataset`. An instance of FashionMNIST dataset.
|
||||
|
||||
Examples:
|
||||
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import itertools
|
||||
>>> import paddle
|
||||
>>> import paddle.vision.transforms as T
|
||||
>>> from paddle.vision.datasets import FashionMNIST
|
||||
|
||||
|
||||
>>> fashion_mnist = FashionMNIST()
|
||||
>>> print(len(fashion_mnist))
|
||||
60000
|
||||
|
||||
>>> for i in range(5): # only show first 5 images
|
||||
... img, label = fashion_mnist[i]
|
||||
... # do something with img and label
|
||||
... print(type(img), img.size, label)
|
||||
... # <class 'PIL.Image.Image'> (28, 28) [9]
|
||||
|
||||
|
||||
>>> transform = T.Compose(
|
||||
... [
|
||||
... T.ToTensor(),
|
||||
... T.Normalize(
|
||||
... mean=[127.5],
|
||||
... std=[127.5],
|
||||
... ),
|
||||
... ]
|
||||
... )
|
||||
|
||||
>>> fashion_mnist_test = FashionMNIST(
|
||||
... mode="test",
|
||||
... transform=transform, # apply transform to every image
|
||||
... backend="cv2", # use OpenCV as image transform backend
|
||||
... )
|
||||
>>> print(len(fashion_mnist_test))
|
||||
10000
|
||||
|
||||
>>> for img, label in itertools.islice(iter(fashion_mnist_test), 5): # only show first 5 images
|
||||
... # do something with img and label
|
||||
... assert isinstance(img, paddle.Tensor)
|
||||
... print(type(img), img.shape, label)
|
||||
... # <class 'paddle.Tensor'> [1, 28, 28] [9]
|
||||
"""
|
||||
|
||||
NAME = 'fashion-mnist'
|
||||
URL_PREFIX = 'https://dataset.bj.bcebos.com/fashion_mnist/'
|
||||
TEST_IMAGE_URL = URL_PREFIX + 't10k-images-idx3-ubyte.gz'
|
||||
TEST_IMAGE_MD5 = 'bef4ecab320f06d8554ea6380940ec79'
|
||||
TEST_LABEL_URL = URL_PREFIX + 't10k-labels-idx1-ubyte.gz'
|
||||
TEST_LABEL_MD5 = 'bb300cfdad3c16e7a12a480ee83cd310'
|
||||
TRAIN_IMAGE_URL = URL_PREFIX + 'train-images-idx3-ubyte.gz'
|
||||
TRAIN_IMAGE_MD5 = '8d4fb7e6c68d591d4c3dfef9ec88bf0d'
|
||||
TRAIN_LABEL_URL = URL_PREFIX + 'train-labels-idx1-ubyte.gz'
|
||||
TRAIN_LABEL_MD5 = '25c81989df183df01b3e8a0aad5dffbe'
|
||||
@@ -0,0 +1,215 @@
|
||||
# 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.
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import io
|
||||
import tarfile
|
||||
from typing import TYPE_CHECKING, Any, Literal
|
||||
|
||||
import numpy as np
|
||||
from PIL import Image
|
||||
|
||||
import paddle
|
||||
from paddle.dataset.common import _check_exists_and_download
|
||||
from paddle.io import Dataset
|
||||
|
||||
if TYPE_CHECKING:
|
||||
import numpy.typing as npt
|
||||
|
||||
from paddle._typing import DTypeLike
|
||||
from paddle.vision.transforms.transforms import _Transform
|
||||
|
||||
from ..image import _ImageDataType
|
||||
|
||||
_ImageBackend = Literal["cv2", "pil"]
|
||||
|
||||
_DatasetMode = Literal["train", "valid", "test"]
|
||||
|
||||
__all__ = []
|
||||
|
||||
VOC_URL = 'https://dataset.bj.bcebos.com/voc/VOCtrainval_11-May-2012.tar'
|
||||
|
||||
VOC_MD5 = '6cd6e144f989b92b3379bac3b3de84fd'
|
||||
SET_FILE = 'VOCdevkit/VOC2012/ImageSets/Segmentation/{}.txt'
|
||||
DATA_FILE = 'VOCdevkit/VOC2012/JPEGImages/{}.jpg'
|
||||
LABEL_FILE = 'VOCdevkit/VOC2012/SegmentationClass/{}.png'
|
||||
|
||||
CACHE_DIR = 'voc2012'
|
||||
|
||||
MODE_FLAG_MAP = {'train': 'trainval', 'test': 'train', 'valid': "val"}
|
||||
|
||||
|
||||
class VOC2012(Dataset[tuple["_ImageDataType", "npt.NDArray[Any]"]]):
|
||||
"""
|
||||
Implementation of `VOC2012 <http://host.robots.ox.ac.uk/pascal/VOC/voc2012/>`_ dataset.
|
||||
|
||||
Args:
|
||||
data_file (str|None, optional): Path to data file, can be set None if
|
||||
:attr:`download` is True. Default: None, default data path: ~/.cache/paddle/dataset/voc2012.
|
||||
mode (str, optional): Either train or test mode. Default 'train'.
|
||||
transform (Callable|None, optional): Transform to perform on image, None for no transform. Default: None.
|
||||
download (bool, optional): Download dataset automatically if :attr:`data_file` is None. Default: True.
|
||||
backend (str|None, optional): Specifies which type of image to be returned:
|
||||
PIL.Image or numpy.ndarray. Should be one of {'pil', 'cv2'}.
|
||||
If this option is not set, will get backend from :ref:`paddle.vision.get_image_backend <api_paddle_vision_get_image_backend>`,
|
||||
default backend is 'pil'. Default: None.
|
||||
|
||||
Returns:
|
||||
:ref:`api_paddle_io_Dataset`. An instance of VOC2012 dataset.
|
||||
|
||||
Examples:
|
||||
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> # doctest: +TIMEOUT(120)
|
||||
>>> import itertools
|
||||
>>> import paddle
|
||||
>>> import paddle.vision.transforms as T
|
||||
>>> from paddle.vision.datasets import VOC2012
|
||||
|
||||
|
||||
>>> voc2012 = VOC2012()
|
||||
>>> print(len(voc2012))
|
||||
2913
|
||||
|
||||
>>> for i in range(5): # only show first 5 images
|
||||
... img, label = voc2012[i]
|
||||
... # do something with img and label
|
||||
... print(type(img), img.size)
|
||||
... # <class 'PIL.JpegImagePlugin.JpegImageFile'> (500, 281)
|
||||
... print(type(label), label.size)
|
||||
... # <class 'PIL.PngImagePlugin.PngImageFile'> (500, 281)
|
||||
|
||||
|
||||
>>> transform = T.Compose(
|
||||
... [
|
||||
... T.ToTensor(),
|
||||
... T.Normalize(
|
||||
... mean=[0.5, 0.5, 0.5],
|
||||
... std=[0.5, 0.5, 0.5],
|
||||
... to_rgb=True,
|
||||
... ),
|
||||
... ]
|
||||
... )
|
||||
|
||||
>>> voc2012_test = VOC2012(
|
||||
... mode="test",
|
||||
... transform=transform, # apply transform to every image
|
||||
... backend="cv2", # use OpenCV as image transform backend
|
||||
... )
|
||||
>>> print(len(voc2012_test))
|
||||
1464
|
||||
|
||||
>>> for img, label in itertools.islice(iter(voc2012_test), 5): # only show first 5 images
|
||||
... # do something with img and label
|
||||
... assert isinstance(img, paddle.Tensor)
|
||||
... print(type(img), img.shape)
|
||||
... # <class 'paddle.Tensor'> [3, 281, 500]
|
||||
... print(type(label), label.shape)
|
||||
... # <class 'numpy.ndarray'> (281, 500)
|
||||
"""
|
||||
|
||||
data_file: str | None
|
||||
mode: _DatasetMode
|
||||
transform: _Transform[Any, Any] | None
|
||||
backend: _ImageBackend
|
||||
flag: str
|
||||
dtype: DTypeLike
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
data_file: str | None = None,
|
||||
mode: _DatasetMode = 'train',
|
||||
transform: _Transform[Any, Any] | None = None,
|
||||
download: bool = True,
|
||||
backend: _ImageBackend | None = None,
|
||||
) -> None:
|
||||
assert mode.lower() in [
|
||||
'train',
|
||||
'valid',
|
||||
'test',
|
||||
], f"mode should be 'train', 'valid' or 'test', but got {mode}"
|
||||
|
||||
if backend is None:
|
||||
backend = paddle.vision.get_image_backend()
|
||||
if backend not in ['pil', 'cv2']:
|
||||
raise ValueError(
|
||||
f"Expected backend are one of ['pil', 'cv2'], but got {backend}"
|
||||
)
|
||||
self.backend = backend
|
||||
|
||||
self.flag = MODE_FLAG_MAP[mode.lower()]
|
||||
|
||||
self.data_file = data_file
|
||||
if self.data_file is None:
|
||||
assert download, (
|
||||
"data_file is not set and downloading automatically is disabled"
|
||||
)
|
||||
self.data_file = _check_exists_and_download(
|
||||
data_file, VOC_URL, VOC_MD5, CACHE_DIR, download
|
||||
)
|
||||
self.transform = transform
|
||||
|
||||
# read dataset into memory
|
||||
self._load_anno()
|
||||
|
||||
self.dtype = paddle.get_default_dtype()
|
||||
|
||||
def _load_anno(self):
|
||||
self.name2mem = {}
|
||||
self.data_tar = tarfile.open(self.data_file)
|
||||
for ele in self.data_tar.getmembers():
|
||||
self.name2mem[ele.name] = ele
|
||||
|
||||
set_file = SET_FILE.format(self.flag)
|
||||
sets = self.data_tar.extractfile(self.name2mem[set_file])
|
||||
|
||||
self.data = []
|
||||
self.labels = []
|
||||
|
||||
for line in sets:
|
||||
line = line.strip()
|
||||
data = DATA_FILE.format(line.decode('utf-8'))
|
||||
label = LABEL_FILE.format(line.decode('utf-8'))
|
||||
self.data.append(data)
|
||||
self.labels.append(label)
|
||||
|
||||
def __getitem__(self, idx: int) -> tuple[_ImageDataType, npt.NDArray[Any]]:
|
||||
data_file = self.data[idx]
|
||||
label_file = self.labels[idx]
|
||||
|
||||
data = self.data_tar.extractfile(self.name2mem[data_file]).read()
|
||||
label = self.data_tar.extractfile(self.name2mem[label_file]).read()
|
||||
data = Image.open(io.BytesIO(data))
|
||||
label = Image.open(io.BytesIO(label))
|
||||
|
||||
if self.backend == 'cv2':
|
||||
data = np.array(data)
|
||||
label = np.array(label)
|
||||
|
||||
if self.transform is not None:
|
||||
data = self.transform(data)
|
||||
|
||||
if self.backend == 'cv2':
|
||||
return data.astype(self.dtype), label.astype(self.dtype)
|
||||
|
||||
return data, label
|
||||
|
||||
def __len__(self) -> int:
|
||||
return len(self.data)
|
||||
|
||||
def __del__(self) -> None:
|
||||
if self.data_tar:
|
||||
self.data_tar.close()
|
||||
@@ -0,0 +1,179 @@
|
||||
# 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.
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Any, Literal, TypeAlias
|
||||
|
||||
from PIL import Image
|
||||
|
||||
from paddle.utils import try_import
|
||||
|
||||
if TYPE_CHECKING:
|
||||
import numpy.typing as npt
|
||||
from PIL.Image import Image as PILImage
|
||||
|
||||
from paddle import Tensor
|
||||
|
||||
_ImageBackend: TypeAlias = Literal["pil", "cv2", "tensor"]
|
||||
_ImageDataType: TypeAlias = Tensor | PILImage | npt.NDArray[Any]
|
||||
|
||||
__all__ = []
|
||||
|
||||
_image_backend: _ImageBackend = 'pil'
|
||||
|
||||
|
||||
def set_image_backend(backend: _ImageBackend) -> None:
|
||||
"""
|
||||
Specifies the backend used to load images in class :ref:`api_paddle_datasets_ImageFolder`
|
||||
and :ref:`api_paddle_datasets_DatasetFolder` . Now support backends are pillow and opencv.
|
||||
If backend not set, will use 'pil' as default.
|
||||
|
||||
Args:
|
||||
backend (str): Name of the image load backend, should be one of {'pil', 'cv2'}.
|
||||
|
||||
Examples:
|
||||
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import os
|
||||
>>> import shutil
|
||||
>>> import tempfile
|
||||
>>> import numpy as np
|
||||
>>> from PIL import Image
|
||||
|
||||
>>> from paddle.vision import DatasetFolder
|
||||
>>> from paddle.vision import set_image_backend
|
||||
|
||||
>>> set_image_backend('pil')
|
||||
|
||||
>>> def make_fake_dir():
|
||||
... data_dir = tempfile.mkdtemp()
|
||||
...
|
||||
... for i in range(2):
|
||||
... sub_dir = os.path.join(data_dir, 'class_' + str(i))
|
||||
... if not os.path.exists(sub_dir):
|
||||
... os.makedirs(sub_dir)
|
||||
... for j in range(2):
|
||||
... fake_img = Image.fromarray((np.random.random((32, 32, 3)) * 255).astype('uint8'))
|
||||
... fake_img.save(os.path.join(sub_dir, str(j) + '.png'))
|
||||
... return data_dir
|
||||
|
||||
>>> temp_dir = make_fake_dir()
|
||||
|
||||
>>> pil_data_folder = DatasetFolder(temp_dir)
|
||||
|
||||
>>> for items in pil_data_folder:
|
||||
... break
|
||||
|
||||
>>> print(type(items[0]))
|
||||
<class 'PIL.Image.Image'>
|
||||
|
||||
>>> # use opencv as backend
|
||||
>>> set_image_backend('cv2')
|
||||
|
||||
>>> cv2_data_folder = DatasetFolder(temp_dir)
|
||||
|
||||
>>> for items in cv2_data_folder:
|
||||
... break
|
||||
|
||||
>>> print(type(items[0]))
|
||||
<class 'numpy.ndarray'>
|
||||
|
||||
>>> shutil.rmtree(temp_dir)
|
||||
"""
|
||||
global _image_backend
|
||||
if backend not in ['pil', 'cv2', 'tensor']:
|
||||
raise ValueError(
|
||||
f"Expected backend are one of ['pil', 'cv2', 'tensor'], but got {backend}"
|
||||
)
|
||||
_image_backend = backend
|
||||
|
||||
|
||||
def get_image_backend() -> _ImageBackend:
|
||||
"""
|
||||
Gets the name of the package used to load images
|
||||
|
||||
Returns:
|
||||
str: backend of image load.
|
||||
|
||||
Examples:
|
||||
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> from paddle.vision import get_image_backend
|
||||
|
||||
>>> backend = get_image_backend()
|
||||
>>> print(backend)
|
||||
pil
|
||||
|
||||
"""
|
||||
return _image_backend
|
||||
|
||||
|
||||
def image_load(
|
||||
path: str, backend: _ImageBackend | None = None
|
||||
) -> _ImageDataType | None:
|
||||
"""Load an image.
|
||||
|
||||
Args:
|
||||
path (str): Path of the image.
|
||||
backend (str, optional): The image decoding backend type. Options are
|
||||
`cv2`, `pil`, `None`. If backend is None, the global _imread_backend
|
||||
specified by :ref:`api_paddle_vision_set_image_backend` will be used. Default: None.
|
||||
|
||||
Returns:
|
||||
PIL.Image or np.array: Loaded image.
|
||||
|
||||
Examples:
|
||||
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import numpy as np
|
||||
>>> from PIL import Image
|
||||
>>> from paddle.vision import image_load, set_image_backend
|
||||
|
||||
>>> fake_img = Image.fromarray((np.random.random((32, 32, 3)) * 255).astype('uint8'))
|
||||
|
||||
>>> path = 'temp.png'
|
||||
>>> fake_img.save(path)
|
||||
|
||||
>>> set_image_backend('pil')
|
||||
|
||||
>>> pil_img = image_load(path).convert('RGB') # type: ignore
|
||||
|
||||
>>> print(type(pil_img))
|
||||
<class 'PIL.Image.Image'>
|
||||
|
||||
>>> # use opencv as backend
|
||||
>>> set_image_backend('cv2')
|
||||
|
||||
>>> np_img = image_load(path)
|
||||
>>> print(type(np_img))
|
||||
<class 'numpy.ndarray'>
|
||||
|
||||
"""
|
||||
|
||||
if backend is None:
|
||||
backend = _image_backend
|
||||
if backend not in ['pil', 'cv2', 'tensor']:
|
||||
raise ValueError(
|
||||
f"Expected backend are one of ['pil', 'cv2', 'tensor'], but got {backend}"
|
||||
)
|
||||
|
||||
if backend == 'pil':
|
||||
return Image.open(path)
|
||||
elif backend == 'cv2':
|
||||
cv2 = try_import('cv2')
|
||||
return cv2.imread(path)
|
||||
@@ -0,0 +1,116 @@
|
||||
# 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.
|
||||
|
||||
from .alexnet import AlexNet, alexnet
|
||||
from .densenet import (
|
||||
DenseNet,
|
||||
densenet121,
|
||||
densenet161,
|
||||
densenet169,
|
||||
densenet201,
|
||||
densenet264,
|
||||
)
|
||||
from .googlenet import GoogLeNet, googlenet
|
||||
from .inceptionv3 import InceptionV3, inception_v3
|
||||
from .lenet import LeNet
|
||||
from .mobilenetv1 import MobileNetV1, mobilenet_v1
|
||||
from .mobilenetv2 import MobileNetV2, mobilenet_v2
|
||||
from .mobilenetv3 import (
|
||||
MobileNetV3Large,
|
||||
MobileNetV3Small,
|
||||
mobilenet_v3_large,
|
||||
mobilenet_v3_small,
|
||||
)
|
||||
from .resnet import (
|
||||
ResNet,
|
||||
resnet18,
|
||||
resnet34,
|
||||
resnet50,
|
||||
resnet101,
|
||||
resnet152,
|
||||
resnext50_32x4d,
|
||||
resnext50_64x4d,
|
||||
resnext101_32x4d,
|
||||
resnext101_64x4d,
|
||||
resnext152_32x4d,
|
||||
resnext152_64x4d,
|
||||
wide_resnet50_2,
|
||||
wide_resnet101_2,
|
||||
)
|
||||
from .shufflenetv2 import (
|
||||
ShuffleNetV2,
|
||||
shufflenet_v2_swish,
|
||||
shufflenet_v2_x0_5,
|
||||
shufflenet_v2_x0_25,
|
||||
shufflenet_v2_x0_33,
|
||||
shufflenet_v2_x1_0,
|
||||
shufflenet_v2_x1_5,
|
||||
shufflenet_v2_x2_0,
|
||||
)
|
||||
from .squeezenet import SqueezeNet, squeezenet1_0, squeezenet1_1
|
||||
from .vgg import VGG, vgg11, vgg13, vgg16, vgg19
|
||||
|
||||
__all__ = [
|
||||
'ResNet',
|
||||
'resnet18',
|
||||
'resnet34',
|
||||
'resnet50',
|
||||
'resnet101',
|
||||
'resnet152',
|
||||
'resnext50_32x4d',
|
||||
'resnext50_64x4d',
|
||||
'resnext101_32x4d',
|
||||
'resnext101_64x4d',
|
||||
'resnext152_32x4d',
|
||||
'resnext152_64x4d',
|
||||
'wide_resnet50_2',
|
||||
'wide_resnet101_2',
|
||||
'VGG',
|
||||
'vgg11',
|
||||
'vgg13',
|
||||
'vgg16',
|
||||
'vgg19',
|
||||
'MobileNetV1',
|
||||
'mobilenet_v1',
|
||||
'MobileNetV2',
|
||||
'mobilenet_v2',
|
||||
'MobileNetV3Small',
|
||||
'MobileNetV3Large',
|
||||
'mobilenet_v3_small',
|
||||
'mobilenet_v3_large',
|
||||
'LeNet',
|
||||
'DenseNet',
|
||||
'densenet121',
|
||||
'densenet161',
|
||||
'densenet169',
|
||||
'densenet201',
|
||||
'densenet264',
|
||||
'AlexNet',
|
||||
'alexnet',
|
||||
'InceptionV3',
|
||||
'inception_v3',
|
||||
'SqueezeNet',
|
||||
'squeezenet1_0',
|
||||
'squeezenet1_1',
|
||||
'GoogLeNet',
|
||||
'googlenet',
|
||||
'ShuffleNetV2',
|
||||
'shufflenet_v2_x0_25',
|
||||
'shufflenet_v2_x0_33',
|
||||
'shufflenet_v2_x0_5',
|
||||
'shufflenet_v2_x1_0',
|
||||
'shufflenet_v2_x1_5',
|
||||
'shufflenet_v2_x2_0',
|
||||
'shufflenet_v2_swish',
|
||||
]
|
||||
@@ -0,0 +1,113 @@
|
||||
# Copyright (c) 2022 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 annotations
|
||||
|
||||
from collections import OrderedDict
|
||||
|
||||
import paddle
|
||||
from paddle import nn
|
||||
|
||||
|
||||
def _make_divisible(v, divisor=8, min_value=None):
|
||||
"""
|
||||
This function ensures that all layers have a channel number that is divisible by divisor.
|
||||
You can also see at https://github.com/keras-team/keras/blob/8ecef127f70db723c158dbe9ed3268b3d610ab55/keras/applications/mobilenet_v2.py#L505
|
||||
|
||||
Args:
|
||||
divisor (int, optional): The divisor for number of channels. Default: 8.
|
||||
min_value (int, optional): The minimum value of number of channels, if it is None,
|
||||
the default is divisor. Default: None.
|
||||
"""
|
||||
if min_value is None:
|
||||
min_value = divisor
|
||||
new_v = max(min_value, int(v + divisor / 2) // divisor * divisor)
|
||||
# Make sure that round down does not go down by more than 10%.
|
||||
if new_v < 0.9 * v:
|
||||
new_v += divisor
|
||||
return new_v
|
||||
|
||||
|
||||
class IntermediateLayerGetter(nn.LayerDict):
|
||||
"""
|
||||
Layer wrapper that returns intermediate layers from a model.
|
||||
|
||||
It has a strong assumption that the layers have been registered into the model in the
|
||||
same order as they are used. This means that one should **not** reuse the same nn.Layer
|
||||
twice in the forward if you want this to work.
|
||||
|
||||
Additionally, it is only able to query sublayer that are directly assigned to the model.
|
||||
So if `model` is passed, `model.feature1` can be returned, but not `model.feature1.layer2`.
|
||||
|
||||
Args:
|
||||
|
||||
model (nn.Layer): Model on which we will extract the features.
|
||||
return_layers (Dict[name, new_name]): A dict containing the names of the layers for
|
||||
which the activations will be returned as the key of the dict, and the value of the
|
||||
dict is the name of the returned activation (which the user can specify).
|
||||
|
||||
Examples:
|
||||
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
>>> m = paddle.vision.models.resnet18(pretrained=False)
|
||||
|
||||
>>> # extract layer1 and layer3, giving as names `feat1` and feat2`
|
||||
>>> new_m = paddle.vision.models._utils.IntermediateLayerGetter(
|
||||
... m,
|
||||
... {
|
||||
... 'layer1': 'feat1',
|
||||
... 'layer3': 'feat2',
|
||||
... },
|
||||
... )
|
||||
>>> out = new_m(paddle.rand([1, 3, 224, 224]))
|
||||
>>> print([(k, v.shape) for k, v in out.items()])
|
||||
[('feat1', [1, 64, 56, 56]), ('feat2', [1, 256, 14, 14])]
|
||||
"""
|
||||
|
||||
return_layers: dict[str, str]
|
||||
|
||||
def __init__(self, model: nn.Layer, return_layers: dict[str, str]) -> None:
|
||||
if not set(return_layers).issubset(
|
||||
[name for name, _ in model.named_children()]
|
||||
):
|
||||
raise ValueError("return_layers are not present in model")
|
||||
orig_return_layers = return_layers
|
||||
return_layers = {str(k): str(v) for k, v in return_layers.items()}
|
||||
layers = OrderedDict()
|
||||
for name, module in model.named_children():
|
||||
layers[name] = module
|
||||
if name in return_layers:
|
||||
del return_layers[name]
|
||||
if not return_layers:
|
||||
break
|
||||
|
||||
super().__init__(layers)
|
||||
self.return_layers = orig_return_layers
|
||||
|
||||
def forward(self, x):
|
||||
out = OrderedDict()
|
||||
for name, module in self.items():
|
||||
if (isinstance(module, nn.Linear) and x.ndim == 4) or (
|
||||
len(module.sublayers()) > 0
|
||||
and isinstance(module.sublayers()[0], nn.Linear)
|
||||
and x.ndim == 4
|
||||
):
|
||||
x = paddle.flatten(x, 1)
|
||||
|
||||
x = module(x)
|
||||
if name in self.return_layers:
|
||||
out_name = self.return_layers[name]
|
||||
out[out_name] = x
|
||||
return out
|
||||
@@ -0,0 +1,241 @@
|
||||
# copyright (c) 2022 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.
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import math
|
||||
from typing import (
|
||||
TYPE_CHECKING,
|
||||
TypedDict,
|
||||
)
|
||||
|
||||
from typing_extensions import NotRequired, Unpack
|
||||
|
||||
import paddle
|
||||
import paddle.nn.functional as F
|
||||
from paddle import nn
|
||||
from paddle.base.param_attr import ParamAttr
|
||||
from paddle.nn import Conv2D, Dropout, Linear, MaxPool2D, ReLU
|
||||
from paddle.nn.initializer import Uniform
|
||||
from paddle.utils.download import get_weights_path_from_url
|
||||
|
||||
model_urls = {
|
||||
"alexnet": (
|
||||
"https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/AlexNet_pretrained.pdparams",
|
||||
"7f0f9f737132e02732d75a1459d98a43",
|
||||
)
|
||||
}
|
||||
|
||||
__all__ = []
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from paddle import Tensor
|
||||
from paddle._typing import Size2
|
||||
|
||||
class _AlexNetOptions(TypedDict):
|
||||
num_classes: NotRequired[int]
|
||||
|
||||
|
||||
class ConvPoolLayer(nn.Layer):
|
||||
def __init__(
|
||||
self,
|
||||
input_channels: int,
|
||||
output_channels: int,
|
||||
filter_size: Size2,
|
||||
stride: Size2,
|
||||
padding: Size2,
|
||||
stdv: float,
|
||||
groups: int = 1,
|
||||
act: str | None = None,
|
||||
) -> None:
|
||||
super().__init__()
|
||||
|
||||
self.relu = ReLU() if act == "relu" else None
|
||||
|
||||
self._conv = Conv2D(
|
||||
in_channels=input_channels,
|
||||
out_channels=output_channels,
|
||||
kernel_size=filter_size,
|
||||
stride=stride,
|
||||
padding=padding,
|
||||
groups=groups,
|
||||
weight_attr=ParamAttr(initializer=Uniform(-stdv, stdv)),
|
||||
bias_attr=ParamAttr(initializer=Uniform(-stdv, stdv)),
|
||||
)
|
||||
self._pool = MaxPool2D(kernel_size=3, stride=2, padding=0)
|
||||
|
||||
def forward(self, inputs: Tensor) -> Tensor:
|
||||
x = self._conv(inputs)
|
||||
if self.relu is not None:
|
||||
x = self.relu(x)
|
||||
x = self._pool(x)
|
||||
return x
|
||||
|
||||
|
||||
class AlexNet(nn.Layer):
|
||||
"""AlexNet model from
|
||||
`"ImageNet Classification with Deep Convolutional Neural Networks"
|
||||
<https://proceedings.neurips.cc/paper/2012/file/c399862d3b9d6b76c8436e924a68c45b-Paper.pdf>`_.
|
||||
|
||||
Args:
|
||||
num_classes (int, optional): Output dim of last fc layer. If num_classes <= 0, last fc layer
|
||||
will not be defined. Default: 1000.
|
||||
|
||||
Returns:
|
||||
:ref:`api_paddle_nn_Layer`. An instance of AlexNet model.
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
>>> from paddle.vision.models import AlexNet
|
||||
|
||||
>>> alexnet = AlexNet()
|
||||
>>> x = paddle.rand([1, 3, 224, 224])
|
||||
>>> out = alexnet(x)
|
||||
>>> print(out.shape)
|
||||
paddle.Size([1, 1000])
|
||||
"""
|
||||
|
||||
num_classes: int
|
||||
|
||||
def __init__(self, num_classes: int = 1000) -> None:
|
||||
super().__init__()
|
||||
self.num_classes = num_classes
|
||||
stdv = 1.0 / math.sqrt(3 * 11 * 11)
|
||||
self._conv1 = ConvPoolLayer(3, 64, 11, 4, 2, stdv, act="relu")
|
||||
stdv = 1.0 / math.sqrt(64 * 5 * 5)
|
||||
self._conv2 = ConvPoolLayer(64, 192, 5, 1, 2, stdv, act="relu")
|
||||
stdv = 1.0 / math.sqrt(192 * 3 * 3)
|
||||
self._conv3 = Conv2D(
|
||||
192,
|
||||
384,
|
||||
3,
|
||||
stride=1,
|
||||
padding=1,
|
||||
weight_attr=ParamAttr(initializer=Uniform(-stdv, stdv)),
|
||||
bias_attr=ParamAttr(initializer=Uniform(-stdv, stdv)),
|
||||
)
|
||||
stdv = 1.0 / math.sqrt(384 * 3 * 3)
|
||||
self._conv4 = Conv2D(
|
||||
384,
|
||||
256,
|
||||
3,
|
||||
stride=1,
|
||||
padding=1,
|
||||
weight_attr=ParamAttr(initializer=Uniform(-stdv, stdv)),
|
||||
bias_attr=ParamAttr(initializer=Uniform(-stdv, stdv)),
|
||||
)
|
||||
stdv = 1.0 / math.sqrt(256 * 3 * 3)
|
||||
self._conv5 = ConvPoolLayer(256, 256, 3, 1, 1, stdv, act="relu")
|
||||
|
||||
if self.num_classes > 0:
|
||||
stdv = 1.0 / math.sqrt(256 * 6 * 6)
|
||||
self._drop1 = Dropout(p=0.5, mode="downscale_in_infer")
|
||||
self._fc6 = Linear(
|
||||
in_features=256 * 6 * 6,
|
||||
out_features=4096,
|
||||
weight_attr=ParamAttr(initializer=Uniform(-stdv, stdv)),
|
||||
bias_attr=ParamAttr(initializer=Uniform(-stdv, stdv)),
|
||||
)
|
||||
|
||||
self._drop2 = Dropout(p=0.5, mode="downscale_in_infer")
|
||||
self._fc7 = Linear(
|
||||
in_features=4096,
|
||||
out_features=4096,
|
||||
weight_attr=ParamAttr(initializer=Uniform(-stdv, stdv)),
|
||||
bias_attr=ParamAttr(initializer=Uniform(-stdv, stdv)),
|
||||
)
|
||||
self._fc8 = Linear(
|
||||
in_features=4096,
|
||||
out_features=num_classes,
|
||||
weight_attr=ParamAttr(initializer=Uniform(-stdv, stdv)),
|
||||
bias_attr=ParamAttr(initializer=Uniform(-stdv, stdv)),
|
||||
)
|
||||
|
||||
def forward(self, inputs: Tensor) -> Tensor:
|
||||
x = self._conv1(inputs)
|
||||
x = self._conv2(x)
|
||||
x = self._conv3(x)
|
||||
x = F.relu(x)
|
||||
x = self._conv4(x)
|
||||
x = F.relu(x)
|
||||
x = self._conv5(x)
|
||||
|
||||
if self.num_classes > 0:
|
||||
x = paddle.flatten(x, start_axis=1, stop_axis=-1)
|
||||
x = self._drop1(x)
|
||||
x = self._fc6(x)
|
||||
x = F.relu(x)
|
||||
x = self._drop2(x)
|
||||
x = self._fc7(x)
|
||||
x = F.relu(x)
|
||||
x = self._fc8(x)
|
||||
|
||||
return x
|
||||
|
||||
|
||||
def _alexnet(
|
||||
arch: str, pretrained: bool, **kwargs: Unpack[_AlexNetOptions]
|
||||
) -> AlexNet:
|
||||
model = AlexNet(**kwargs)
|
||||
|
||||
if pretrained:
|
||||
assert arch in model_urls, (
|
||||
f"{arch} model do not have a pretrained model now, you should set pretrained=False"
|
||||
)
|
||||
weight_path = get_weights_path_from_url(
|
||||
model_urls[arch][0], model_urls[arch][1]
|
||||
)
|
||||
|
||||
param = paddle.load(weight_path)
|
||||
model.load_dict(param)
|
||||
|
||||
return model
|
||||
|
||||
|
||||
def alexnet(
|
||||
pretrained: bool = False, **kwargs: Unpack[_AlexNetOptions]
|
||||
) -> AlexNet:
|
||||
"""AlexNet model from
|
||||
`"ImageNet Classification with Deep Convolutional Neural Networks"
|
||||
<https://proceedings.neurips.cc/paper/2012/file/c399862d3b9d6b76c8436e924a68c45b-Paper.pdf>`_.
|
||||
|
||||
Args:
|
||||
pretrained (bool, optional): Whether to load pre-trained weights. If True, returns a model pre-trained
|
||||
on ImageNet. Default: False.
|
||||
**kwargs (optional): Additional keyword arguments. For details, please refer to :ref:`AlexNet <api_paddle_vision_AlexNet>`.
|
||||
|
||||
Returns:
|
||||
:ref:`api_paddle_nn_Layer`. An instance of AlexNet model.
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
>>> from paddle.vision.models import alexnet
|
||||
|
||||
>>> # Build model
|
||||
>>> model = alexnet()
|
||||
|
||||
>>> # Build model and load imagenet pretrained weight
|
||||
>>> # model = alexnet(pretrained=True)
|
||||
|
||||
>>> x = paddle.rand([1, 3, 224, 224])
|
||||
>>> out = model(x)
|
||||
|
||||
>>> print(out.shape)
|
||||
paddle.Size([1, 1000])
|
||||
"""
|
||||
return _alexnet('alexnet', pretrained, **kwargs)
|
||||
@@ -0,0 +1,571 @@
|
||||
# copyright (c) 2021 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.
|
||||
from __future__ import annotations
|
||||
|
||||
import math
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from typing_extensions import Unpack
|
||||
|
||||
import paddle
|
||||
from paddle import nn
|
||||
from paddle.base.param_attr import ParamAttr
|
||||
from paddle.nn import (
|
||||
AdaptiveAvgPool2D,
|
||||
AvgPool2D,
|
||||
BatchNorm,
|
||||
Conv2D,
|
||||
Dropout,
|
||||
Linear,
|
||||
MaxPool2D,
|
||||
)
|
||||
from paddle.nn.initializer import Uniform
|
||||
from paddle.utils.download import get_weights_path_from_url
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing import Literal, TypedDict
|
||||
|
||||
from typing_extensions import NotRequired
|
||||
|
||||
from paddle import Tensor
|
||||
from paddle._typing import Size2
|
||||
|
||||
_DenseNetArch = Literal[
|
||||
"densenet121",
|
||||
"densenet161",
|
||||
"densenet169",
|
||||
"densenet201",
|
||||
"densenet264",
|
||||
]
|
||||
|
||||
class _DenseNetOptions(TypedDict):
|
||||
bn_size: NotRequired[int]
|
||||
dropout: NotRequired[float]
|
||||
num_classes: NotRequired[int]
|
||||
with_pool: NotRequired[bool]
|
||||
|
||||
|
||||
__all__ = []
|
||||
|
||||
model_urls: dict[str, tuple[str, str]] = {
|
||||
'densenet121': (
|
||||
'https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/DenseNet121_pretrained.pdparams',
|
||||
'db1b239ed80a905290fd8b01d3af08e4',
|
||||
),
|
||||
'densenet161': (
|
||||
'https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/DenseNet161_pretrained.pdparams',
|
||||
'62158869cb315098bd25ddbfd308a853',
|
||||
),
|
||||
'densenet169': (
|
||||
'https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/DenseNet169_pretrained.pdparams',
|
||||
'82cc7c635c3f19098c748850efb2d796',
|
||||
),
|
||||
'densenet201': (
|
||||
'https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/DenseNet201_pretrained.pdparams',
|
||||
'16ca29565a7712329cf9e36e02caaf58',
|
||||
),
|
||||
'densenet264': (
|
||||
'https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/DenseNet264_pretrained.pdparams',
|
||||
'3270ce516b85370bba88cfdd9f60bff4',
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
class BNACConvLayer(nn.Layer):
|
||||
def __init__(
|
||||
self,
|
||||
num_channels: int,
|
||||
num_filters: int,
|
||||
filter_size: Size2,
|
||||
stride: Size2 = 1,
|
||||
pad: Size2 = 0,
|
||||
groups: int = 1,
|
||||
act: str = "relu",
|
||||
) -> None:
|
||||
super().__init__()
|
||||
self._batch_norm = BatchNorm(num_channels, act=act)
|
||||
|
||||
self._conv = Conv2D(
|
||||
in_channels=num_channels,
|
||||
out_channels=num_filters,
|
||||
kernel_size=filter_size,
|
||||
stride=stride,
|
||||
padding=pad,
|
||||
groups=groups,
|
||||
weight_attr=ParamAttr(),
|
||||
bias_attr=False,
|
||||
)
|
||||
|
||||
def forward(self, input: Tensor) -> Tensor:
|
||||
y = self._batch_norm(input)
|
||||
y = self._conv(y)
|
||||
return y
|
||||
|
||||
|
||||
class DenseLayer(nn.Layer):
|
||||
dropout: float
|
||||
|
||||
def __init__(
|
||||
self, num_channels: int, growth_rate: int, bn_size: int, dropout: float
|
||||
) -> None:
|
||||
super().__init__()
|
||||
self.dropout = dropout
|
||||
|
||||
self.bn_ac_func1 = BNACConvLayer(
|
||||
num_channels=num_channels,
|
||||
num_filters=bn_size * growth_rate,
|
||||
filter_size=1,
|
||||
pad=0,
|
||||
stride=1,
|
||||
)
|
||||
|
||||
self.bn_ac_func2 = BNACConvLayer(
|
||||
num_channels=bn_size * growth_rate,
|
||||
num_filters=growth_rate,
|
||||
filter_size=3,
|
||||
pad=1,
|
||||
stride=1,
|
||||
)
|
||||
|
||||
if dropout:
|
||||
self.dropout_func = Dropout(p=dropout, mode="downscale_in_infer")
|
||||
|
||||
def forward(self, input: Tensor) -> Tensor:
|
||||
conv = self.bn_ac_func1(input)
|
||||
conv = self.bn_ac_func2(conv)
|
||||
if self.dropout:
|
||||
conv = self.dropout_func(conv)
|
||||
conv = paddle.concat([input, conv], axis=1)
|
||||
return conv
|
||||
|
||||
|
||||
class DenseBlock(nn.Layer):
|
||||
dropout: float
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
num_channels: int,
|
||||
num_layers: int,
|
||||
bn_size: int,
|
||||
growth_rate: int,
|
||||
dropout: float,
|
||||
name: str | None = None,
|
||||
) -> None:
|
||||
super().__init__()
|
||||
self.dropout = dropout
|
||||
self.dense_layer_func = []
|
||||
|
||||
pre_channel = num_channels
|
||||
for layer in range(num_layers):
|
||||
self.dense_layer_func.append(
|
||||
self.add_sublayer(
|
||||
f"{name}_{layer + 1}",
|
||||
DenseLayer(
|
||||
num_channels=pre_channel,
|
||||
growth_rate=growth_rate,
|
||||
bn_size=bn_size,
|
||||
dropout=dropout,
|
||||
),
|
||||
)
|
||||
)
|
||||
pre_channel = pre_channel + growth_rate
|
||||
|
||||
def forward(self, input: Tensor) -> Tensor:
|
||||
conv = input
|
||||
for func in self.dense_layer_func:
|
||||
conv = func(conv)
|
||||
return conv
|
||||
|
||||
|
||||
class TransitionLayer(nn.Layer):
|
||||
def __init__(self, num_channels: int, num_output_features: int) -> None:
|
||||
super().__init__()
|
||||
|
||||
self.conv_ac_func = BNACConvLayer(
|
||||
num_channels=num_channels,
|
||||
num_filters=num_output_features,
|
||||
filter_size=1,
|
||||
pad=0,
|
||||
stride=1,
|
||||
)
|
||||
|
||||
self.pool2d_avg = AvgPool2D(kernel_size=2, stride=2, padding=0)
|
||||
|
||||
def forward(self, input: Tensor) -> Tensor:
|
||||
y = self.conv_ac_func(input)
|
||||
y = self.pool2d_avg(y)
|
||||
return y
|
||||
|
||||
|
||||
class ConvBNLayer(nn.Layer):
|
||||
def __init__(
|
||||
self,
|
||||
num_channels: int,
|
||||
num_filters: int,
|
||||
filter_size: Size2,
|
||||
stride: Size2 = 1,
|
||||
pad: Size2 = 0,
|
||||
groups: int = 1,
|
||||
act: str = "relu",
|
||||
) -> None:
|
||||
super().__init__()
|
||||
|
||||
self._conv = Conv2D(
|
||||
in_channels=num_channels,
|
||||
out_channels=num_filters,
|
||||
kernel_size=filter_size,
|
||||
stride=stride,
|
||||
padding=pad,
|
||||
groups=groups,
|
||||
weight_attr=ParamAttr(),
|
||||
bias_attr=False,
|
||||
)
|
||||
self._batch_norm = BatchNorm(num_filters, act=act)
|
||||
|
||||
def forward(self, input: Tensor) -> Tensor:
|
||||
y = self._conv(input)
|
||||
y = self._batch_norm(y)
|
||||
return y
|
||||
|
||||
|
||||
class DenseNet(nn.Layer):
|
||||
"""DenseNet model from
|
||||
`"Densely Connected Convolutional Networks" <https://arxiv.org/pdf/1608.06993.pdf>`_.
|
||||
|
||||
Args:
|
||||
layers (int, optional): Layers of DenseNet. Default: 121.
|
||||
bn_size (int, optional): Expansion of growth rate in the middle layer. Default: 4.
|
||||
dropout (float, optional): Dropout rate. Default: :math:`0.0`.
|
||||
num_classes (int, optional): Output dim of last fc layer. If num_classes <= 0, last fc layer
|
||||
will not be defined. Default: 1000.
|
||||
with_pool (bool, optional): Use pool before the last fc layer or not. Default: True.
|
||||
|
||||
Returns:
|
||||
:ref:`api_paddle_nn_Layer`. An instance of DenseNet model.
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
>>> from paddle.vision.models import DenseNet
|
||||
|
||||
>>> # Build model
|
||||
>>> densenet = DenseNet()
|
||||
|
||||
>>> x = paddle.rand([1, 3, 224, 224])
|
||||
>>> out = densenet(x)
|
||||
|
||||
>>> print(out.shape)
|
||||
paddle.Size([1, 1000])
|
||||
"""
|
||||
|
||||
num_classes: int
|
||||
with_pool: bool
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
layers: int = 121,
|
||||
bn_size: int = 4,
|
||||
dropout: float = 0.0,
|
||||
num_classes: int = 1000,
|
||||
with_pool: bool = True,
|
||||
) -> None:
|
||||
super().__init__()
|
||||
self.num_classes = num_classes
|
||||
self.with_pool = with_pool
|
||||
supported_layers = [121, 161, 169, 201, 264]
|
||||
assert layers in supported_layers, (
|
||||
f"supported layers are {supported_layers} but input layer is {layers}"
|
||||
)
|
||||
densenet_spec = {
|
||||
121: (64, 32, [6, 12, 24, 16]),
|
||||
161: (96, 48, [6, 12, 36, 24]),
|
||||
169: (64, 32, [6, 12, 32, 32]),
|
||||
201: (64, 32, [6, 12, 48, 32]),
|
||||
264: (64, 32, [6, 12, 64, 48]),
|
||||
}
|
||||
num_init_features, growth_rate, block_config = densenet_spec[layers]
|
||||
|
||||
self.conv1_func = ConvBNLayer(
|
||||
num_channels=3,
|
||||
num_filters=num_init_features,
|
||||
filter_size=7,
|
||||
stride=2,
|
||||
pad=3,
|
||||
act='relu',
|
||||
)
|
||||
self.pool2d_max = MaxPool2D(kernel_size=3, stride=2, padding=1)
|
||||
self.block_config = block_config
|
||||
self.dense_block_func_list = []
|
||||
self.transition_func_list = []
|
||||
pre_num_channels = num_init_features
|
||||
num_features = num_init_features
|
||||
for i, num_layers in enumerate(block_config):
|
||||
self.dense_block_func_list.append(
|
||||
self.add_sublayer(
|
||||
f"db_conv_{i + 2}",
|
||||
DenseBlock(
|
||||
num_channels=pre_num_channels,
|
||||
num_layers=num_layers,
|
||||
bn_size=bn_size,
|
||||
growth_rate=growth_rate,
|
||||
dropout=dropout,
|
||||
name='conv' + str(i + 2),
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
num_features = num_features + num_layers * growth_rate
|
||||
pre_num_channels = num_features
|
||||
|
||||
if i != len(block_config) - 1:
|
||||
self.transition_func_list.append(
|
||||
self.add_sublayer(
|
||||
f"tr_conv{i + 2}_blk",
|
||||
TransitionLayer(
|
||||
num_channels=pre_num_channels,
|
||||
num_output_features=num_features // 2,
|
||||
),
|
||||
)
|
||||
)
|
||||
pre_num_channels = num_features // 2
|
||||
num_features = num_features // 2
|
||||
|
||||
self.batch_norm = BatchNorm(num_features, act="relu")
|
||||
if self.with_pool:
|
||||
self.pool2d_avg = AdaptiveAvgPool2D(1)
|
||||
|
||||
if self.num_classes > 0:
|
||||
stdv = 1.0 / math.sqrt(num_features * 1.0)
|
||||
self.out = Linear(
|
||||
num_features,
|
||||
num_classes,
|
||||
weight_attr=ParamAttr(initializer=Uniform(-stdv, stdv)),
|
||||
bias_attr=ParamAttr(),
|
||||
)
|
||||
|
||||
def forward(self, input: Tensor) -> Tensor:
|
||||
conv = self.conv1_func(input)
|
||||
conv = self.pool2d_max(conv)
|
||||
|
||||
for i, num_layers in enumerate(self.block_config):
|
||||
conv = self.dense_block_func_list[i](conv)
|
||||
if i != len(self.block_config) - 1:
|
||||
conv = self.transition_func_list[i](conv)
|
||||
|
||||
conv = self.batch_norm(conv)
|
||||
|
||||
if self.with_pool:
|
||||
y = self.pool2d_avg(conv)
|
||||
|
||||
if self.num_classes > 0:
|
||||
y = paddle.flatten(y, start_axis=1, stop_axis=-1)
|
||||
y = self.out(y)
|
||||
|
||||
return y
|
||||
|
||||
|
||||
def _densenet(
|
||||
arch: _DenseNetArch,
|
||||
layers: int,
|
||||
pretrained: bool,
|
||||
**kwargs: Unpack[_DenseNetOptions],
|
||||
) -> DenseNet:
|
||||
model = DenseNet(layers=layers, **kwargs)
|
||||
if pretrained:
|
||||
assert arch in model_urls, (
|
||||
f"{arch} model do not have a pretrained model now, you should set pretrained=False"
|
||||
)
|
||||
weight_path = get_weights_path_from_url(
|
||||
model_urls[arch][0], model_urls[arch][1]
|
||||
)
|
||||
|
||||
param = paddle.load(weight_path)
|
||||
model.set_dict(param)
|
||||
|
||||
return model
|
||||
|
||||
|
||||
def densenet121(
|
||||
pretrained: bool = False, **kwargs: Unpack[_DenseNetOptions]
|
||||
) -> DenseNet:
|
||||
"""DenseNet 121-layer model from
|
||||
`"Densely Connected Convolutional Networks" <https://arxiv.org/pdf/1608.06993.pdf>`_.
|
||||
|
||||
Args:
|
||||
pretrained (bool, optional): Whether to load pre-trained weights. If True, returns a model pre-trained
|
||||
on ImageNet. Default: False.
|
||||
**kwargs (optional): Additional keyword arguments. For details, please refer to :ref:`DenseNet <api_paddle_vision_models_DenseNet>`.
|
||||
|
||||
Returns:
|
||||
:ref:`api_paddle_nn_Layer`. An instance of DenseNet 121-layer model.
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
>>> from paddle.vision.models import densenet121
|
||||
|
||||
>>> # Build model
|
||||
>>> model = densenet121()
|
||||
|
||||
>>> # Build model and load imagenet pretrained weight
|
||||
>>> # model = densenet121(pretrained=True)
|
||||
|
||||
>>> x = paddle.rand([1, 3, 224, 224])
|
||||
>>> out = model(x)
|
||||
|
||||
>>> print(out.shape)
|
||||
paddle.Size([1, 1000])
|
||||
"""
|
||||
return _densenet('densenet121', 121, pretrained, **kwargs)
|
||||
|
||||
|
||||
def densenet161(
|
||||
pretrained: bool = False, **kwargs: Unpack[_DenseNetOptions]
|
||||
) -> DenseNet:
|
||||
"""DenseNet 161-layer model from
|
||||
`"Densely Connected Convolutional Networks" <https://arxiv.org/pdf/1608.06993.pdf>`_.
|
||||
|
||||
Args:
|
||||
pretrained (bool, optional): Whether to load pre-trained weights. If True, returns a model pre-trained
|
||||
on ImageNet. Default: False.
|
||||
**kwargs (optional): Additional keyword arguments. For details, please refer to :ref:`DenseNet <api_paddle_vision_models_DenseNet>`.
|
||||
|
||||
Returns:
|
||||
:ref:`api_paddle_nn_Layer`. An instance of DenseNet 161-layer model.
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
>>> from paddle.vision.models import densenet161
|
||||
|
||||
>>> # Build model
|
||||
>>> model = densenet161()
|
||||
|
||||
>>> # Build model and load imagenet pretrained weight
|
||||
>>> # model = densenet161(pretrained=True)
|
||||
|
||||
>>> x = paddle.rand([1, 3, 224, 224])
|
||||
>>> out = model(x)
|
||||
|
||||
>>> print(out.shape)
|
||||
paddle.Size([1, 1000])
|
||||
"""
|
||||
return _densenet('densenet161', 161, pretrained, **kwargs)
|
||||
|
||||
|
||||
def densenet169(
|
||||
pretrained: bool = False, **kwargs: Unpack[_DenseNetOptions]
|
||||
) -> DenseNet:
|
||||
"""DenseNet 169-layer model from
|
||||
`"Densely Connected Convolutional Networks" <https://arxiv.org/pdf/1608.06993.pdf>`_.
|
||||
|
||||
Args:
|
||||
pretrained (bool, optional): Whether to load pre-trained weights. If True, returns a model pre-trained
|
||||
on ImageNet. Default: False.
|
||||
**kwargs (optional): Additional keyword arguments. For details, please refer to :ref:`DenseNet <api_paddle_vision_models_DenseNet>`.
|
||||
|
||||
Returns:
|
||||
:ref:`api_paddle_nn_Layer`. An instance of DenseNet 169-layer model.
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
>>> from paddle.vision.models import densenet169
|
||||
|
||||
>>> # Build model
|
||||
>>> model = densenet169()
|
||||
|
||||
>>> # Build model and load imagenet pretrained weight
|
||||
>>> # model = densenet169(pretrained=True)
|
||||
|
||||
>>> x = paddle.rand([1, 3, 224, 224])
|
||||
>>> out = model(x)
|
||||
|
||||
>>> print(out.shape)
|
||||
paddle.Size([1, 1000])
|
||||
"""
|
||||
return _densenet('densenet169', 169, pretrained, **kwargs)
|
||||
|
||||
|
||||
def densenet201(
|
||||
pretrained: bool = False, **kwargs: Unpack[_DenseNetOptions]
|
||||
) -> DenseNet:
|
||||
"""DenseNet 201-layer model from
|
||||
`"Densely Connected Convolutional Networks" <https://arxiv.org/pdf/1608.06993.pdf>`_.
|
||||
|
||||
Args:
|
||||
pretrained (bool, optional): Whether to load pre-trained weights. If True, returns a model pre-trained
|
||||
on ImageNet. Default: False.
|
||||
**kwargs (optional): Additional keyword arguments. For details, please refer to :ref:`DenseNet <api_paddle_vision_models_DenseNet>`.
|
||||
|
||||
Returns:
|
||||
:ref:`api_paddle_nn_Layer`. An instance of DenseNet 201-layer model.
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
>>> from paddle.vision.models import densenet201
|
||||
|
||||
>>> # Build model
|
||||
>>> model = densenet201()
|
||||
|
||||
>>> # Build model and load imagenet pretrained weight
|
||||
>>> # model = densenet201(pretrained=True)
|
||||
>>> x = paddle.rand([1, 3, 224, 224])
|
||||
>>> out = model(x)
|
||||
|
||||
>>> print(out.shape)
|
||||
paddle.Size([1, 1000])
|
||||
"""
|
||||
return _densenet('densenet201', 201, pretrained, **kwargs)
|
||||
|
||||
|
||||
def densenet264(
|
||||
pretrained: bool = False, **kwargs: Unpack[_DenseNetOptions]
|
||||
) -> DenseNet:
|
||||
"""DenseNet 264-layer model from
|
||||
`"Densely Connected Convolutional Networks" <https://arxiv.org/pdf/1608.06993.pdf>`_.
|
||||
|
||||
Args:
|
||||
pretrained (bool, optional): Whether to load pre-trained weights. If True, returns a model pre-trained
|
||||
on ImageNet. Default: False.
|
||||
**kwargs (optional): Additional keyword arguments. For details, please refer to :ref:`DenseNet <api_paddle_vision_models_DenseNet>`.
|
||||
|
||||
Returns:
|
||||
:ref:`api_paddle_nn_Layer`. An instance of DenseNet 264-layer model.
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
>>> from paddle.vision.models import densenet264
|
||||
|
||||
>>> # Build model
|
||||
>>> model = densenet264()
|
||||
|
||||
>>> # Build model and load imagenet pretrained weight
|
||||
>>> # model = densenet264(pretrained=True)
|
||||
|
||||
>>> x = paddle.rand([1, 3, 224, 224])
|
||||
>>> out = model(x)
|
||||
|
||||
>>> print(out.shape)
|
||||
paddle.Size([1, 1000])
|
||||
"""
|
||||
return _densenet('densenet264', 264, pretrained, **kwargs)
|
||||
@@ -0,0 +1,303 @@
|
||||
# Copyright (c) 2021 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 annotations
|
||||
|
||||
from typing import (
|
||||
TYPE_CHECKING,
|
||||
TypedDict,
|
||||
)
|
||||
|
||||
from typing_extensions import NotRequired, Unpack
|
||||
|
||||
import paddle
|
||||
import paddle.nn.functional as F
|
||||
from paddle import nn
|
||||
from paddle.base.param_attr import ParamAttr
|
||||
from paddle.nn import (
|
||||
AdaptiveAvgPool2D,
|
||||
AvgPool2D,
|
||||
Conv2D,
|
||||
Dropout,
|
||||
Linear,
|
||||
MaxPool2D,
|
||||
)
|
||||
from paddle.nn.initializer import Uniform
|
||||
from paddle.utils.download import get_weights_path_from_url
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from paddle import Tensor
|
||||
from paddle._typing import Size2
|
||||
|
||||
class _GoogLeNetOptions(TypedDict):
|
||||
num_classes: NotRequired[int]
|
||||
with_pool: NotRequired[bool]
|
||||
|
||||
|
||||
__all__ = []
|
||||
|
||||
model_urls = {
|
||||
"googlenet": (
|
||||
"https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/GoogLeNet_pretrained.pdparams",
|
||||
"80c06f038e905c53ab32c40eca6e26ae",
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
def xavier(channels: int, filter_size: int) -> ParamAttr:
|
||||
stdv = (3.0 / (filter_size**2 * channels)) ** 0.5
|
||||
param_attr = ParamAttr(initializer=Uniform(-stdv, stdv))
|
||||
return param_attr
|
||||
|
||||
|
||||
class ConvLayer(nn.Layer):
|
||||
def __init__(
|
||||
self,
|
||||
num_channels: int,
|
||||
num_filters: int,
|
||||
filter_size: int,
|
||||
stride: Size2 = 1,
|
||||
groups: int = 1,
|
||||
):
|
||||
super().__init__()
|
||||
|
||||
self._conv = Conv2D(
|
||||
in_channels=num_channels,
|
||||
out_channels=num_filters,
|
||||
kernel_size=filter_size,
|
||||
stride=stride,
|
||||
padding=(filter_size - 1) // 2,
|
||||
groups=groups,
|
||||
bias_attr=False,
|
||||
)
|
||||
|
||||
def forward(self, inputs: Tensor) -> Tensor:
|
||||
y = self._conv(inputs)
|
||||
return y
|
||||
|
||||
|
||||
class Inception(nn.Layer):
|
||||
def __init__(
|
||||
self,
|
||||
input_channels: int,
|
||||
output_channels: int,
|
||||
filter1: int,
|
||||
filter3R: int,
|
||||
filter3: int,
|
||||
filter5R: int,
|
||||
filter5: int,
|
||||
proj: int,
|
||||
):
|
||||
super().__init__()
|
||||
|
||||
self._conv1 = ConvLayer(input_channels, filter1, 1)
|
||||
self._conv3r = ConvLayer(input_channels, filter3R, 1)
|
||||
self._conv3 = ConvLayer(filter3R, filter3, 3)
|
||||
self._conv5r = ConvLayer(input_channels, filter5R, 1)
|
||||
self._conv5 = ConvLayer(filter5R, filter5, 5)
|
||||
self._pool = MaxPool2D(kernel_size=3, stride=1, padding=1)
|
||||
|
||||
self._convprj = ConvLayer(input_channels, proj, 1)
|
||||
|
||||
def forward(self, inputs: Tensor) -> Tensor:
|
||||
conv1 = self._conv1(inputs)
|
||||
|
||||
conv3r = self._conv3r(inputs)
|
||||
conv3 = self._conv3(conv3r)
|
||||
|
||||
conv5r = self._conv5r(inputs)
|
||||
conv5 = self._conv5(conv5r)
|
||||
|
||||
pool = self._pool(inputs)
|
||||
convprj = self._convprj(pool)
|
||||
|
||||
cat = paddle.concat([conv1, conv3, conv5, convprj], axis=1)
|
||||
cat = F.relu(cat)
|
||||
return cat
|
||||
|
||||
|
||||
class GoogLeNet(nn.Layer):
|
||||
"""GoogLeNet (Inception v1) model architecture from
|
||||
`"Going Deeper with Convolutions" <https://arxiv.org/pdf/1409.4842.pdf>`_.
|
||||
|
||||
Args:
|
||||
num_classes (int, optional): Output dim of last fc layer. If num_classes <= 0, last fc layer
|
||||
will not be defined. Default: 1000.
|
||||
with_pool (bool, optional): Use pool before the last fc layer or not. Default: True.
|
||||
|
||||
Returns:
|
||||
:ref:`api_paddle_nn_Layer`. An instance of GoogLeNet (Inception v1) model.
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
>>> from paddle.vision.models import GoogLeNet
|
||||
|
||||
>>> # Build model
|
||||
>>> model = GoogLeNet()
|
||||
|
||||
>>> x = paddle.rand([1, 3, 224, 224])
|
||||
>>> out, out1, out2 = model(x)
|
||||
|
||||
>>> print(out.shape, out1.shape, out2.shape)
|
||||
paddle.Size([1, 1000]) paddle.Size([1, 1000]) paddle.Size([1, 1000])
|
||||
"""
|
||||
|
||||
num_classes: int
|
||||
with_pool: bool
|
||||
|
||||
def __init__(self, num_classes: int = 1000, with_pool: bool = True) -> None:
|
||||
super().__init__()
|
||||
self.num_classes = num_classes
|
||||
self.with_pool = with_pool
|
||||
|
||||
self._conv = ConvLayer(3, 64, 7, 2)
|
||||
self._pool = MaxPool2D(kernel_size=3, stride=2)
|
||||
self._conv_1 = ConvLayer(64, 64, 1)
|
||||
self._conv_2 = ConvLayer(64, 192, 3)
|
||||
|
||||
self._ince3a = Inception(192, 192, 64, 96, 128, 16, 32, 32)
|
||||
self._ince3b = Inception(256, 256, 128, 128, 192, 32, 96, 64)
|
||||
|
||||
self._ince4a = Inception(480, 480, 192, 96, 208, 16, 48, 64)
|
||||
self._ince4b = Inception(512, 512, 160, 112, 224, 24, 64, 64)
|
||||
self._ince4c = Inception(512, 512, 128, 128, 256, 24, 64, 64)
|
||||
self._ince4d = Inception(512, 512, 112, 144, 288, 32, 64, 64)
|
||||
self._ince4e = Inception(528, 528, 256, 160, 320, 32, 128, 128)
|
||||
|
||||
self._ince5a = Inception(832, 832, 256, 160, 320, 32, 128, 128)
|
||||
self._ince5b = Inception(832, 832, 384, 192, 384, 48, 128, 128)
|
||||
|
||||
if with_pool:
|
||||
# out
|
||||
self._pool_5 = AdaptiveAvgPool2D(1)
|
||||
# out1
|
||||
self._pool_o1 = AvgPool2D(kernel_size=5, stride=3)
|
||||
# out2
|
||||
self._pool_o2 = AvgPool2D(kernel_size=5, stride=3)
|
||||
|
||||
if num_classes > 0:
|
||||
# out
|
||||
self._drop = Dropout(p=0.4, mode="downscale_in_infer")
|
||||
self._fc_out = Linear(
|
||||
1024, num_classes, weight_attr=xavier(1024, 1)
|
||||
)
|
||||
|
||||
# out1
|
||||
self._conv_o1 = ConvLayer(512, 128, 1)
|
||||
self._fc_o1 = Linear(1152, 1024, weight_attr=xavier(2048, 1))
|
||||
self._drop_o1 = Dropout(p=0.7, mode="downscale_in_infer")
|
||||
self._out1 = Linear(1024, num_classes, weight_attr=xavier(1024, 1))
|
||||
|
||||
# out2
|
||||
self._conv_o2 = ConvLayer(528, 128, 1)
|
||||
self._fc_o2 = Linear(1152, 1024, weight_attr=xavier(2048, 1))
|
||||
self._drop_o2 = Dropout(p=0.7, mode="downscale_in_infer")
|
||||
self._out2 = Linear(1024, num_classes, weight_attr=xavier(1024, 1))
|
||||
|
||||
def forward(self, inputs: Tensor) -> tuple[Tensor, Tensor, Tensor]:
|
||||
x = self._conv(inputs)
|
||||
x = self._pool(x)
|
||||
x = self._conv_1(x)
|
||||
x = self._conv_2(x)
|
||||
x = self._pool(x)
|
||||
|
||||
x = self._ince3a(x)
|
||||
x = self._ince3b(x)
|
||||
x = self._pool(x)
|
||||
|
||||
ince4a = self._ince4a(x)
|
||||
x = self._ince4b(ince4a)
|
||||
x = self._ince4c(x)
|
||||
ince4d = self._ince4d(x)
|
||||
x = self._ince4e(ince4d)
|
||||
x = self._pool(x)
|
||||
|
||||
x = self._ince5a(x)
|
||||
ince5b = self._ince5b(x)
|
||||
|
||||
out, out1, out2 = ince5b, ince4a, ince4d
|
||||
|
||||
if self.with_pool:
|
||||
out = self._pool_5(out)
|
||||
out1 = self._pool_o1(out1)
|
||||
out2 = self._pool_o2(out2)
|
||||
|
||||
if self.num_classes > 0:
|
||||
out = self._drop(out)
|
||||
out = paddle.squeeze(out, axis=[2, 3])
|
||||
out = self._fc_out(out)
|
||||
|
||||
out1 = self._conv_o1(out1)
|
||||
out1 = paddle.flatten(out1, start_axis=1, stop_axis=-1)
|
||||
out1 = self._fc_o1(out1)
|
||||
out1 = F.relu(out1)
|
||||
out1 = self._drop_o1(out1)
|
||||
out1 = self._out1(out1)
|
||||
|
||||
out2 = self._conv_o2(out2)
|
||||
out2 = paddle.flatten(out2, start_axis=1, stop_axis=-1)
|
||||
out2 = self._fc_o2(out2)
|
||||
out2 = self._drop_o2(out2)
|
||||
out2 = self._out2(out2)
|
||||
|
||||
return out, out1, out2
|
||||
|
||||
|
||||
def googlenet(
|
||||
pretrained: bool = False, **kwargs: Unpack[_GoogLeNetOptions]
|
||||
) -> GoogLeNet:
|
||||
"""GoogLeNet (Inception v1) model architecture from
|
||||
`"Going Deeper with Convolutions" <https://arxiv.org/pdf/1409.4842.pdf>`_.
|
||||
|
||||
Args:
|
||||
pretrained (bool, optional): Whether to load pre-trained weights. If True, returns a model pre-trained
|
||||
on ImageNet. Default: False.
|
||||
**kwargs (optional): Additional keyword arguments. For details, please refer to :ref:`GoogLeNet <api_paddle_vision_models_GoogLeNet>`.
|
||||
|
||||
Returns:
|
||||
:ref:`api_paddle_nn_Layer`. An instance of GoogLeNet (Inception v1) model.
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
>>> from paddle.vision.models import googlenet
|
||||
|
||||
>>> # Build model
|
||||
>>> model = googlenet()
|
||||
|
||||
>>> # Build model and load imagenet pretrained weight
|
||||
>>> # model = googlenet(pretrained=True)
|
||||
|
||||
>>> x = paddle.rand([1, 3, 224, 224])
|
||||
>>> out, out1, out2 = model(x)
|
||||
|
||||
>>> print(out.shape, out1.shape, out2.shape)
|
||||
paddle.Size([1, 1000]) paddle.Size([1, 1000]) paddle.Size([1, 1000])
|
||||
"""
|
||||
model = GoogLeNet(**kwargs)
|
||||
arch = "googlenet"
|
||||
if pretrained:
|
||||
assert arch in model_urls, (
|
||||
f"{arch} model do not have a pretrained model now, you should set pretrained=False"
|
||||
)
|
||||
weight_path = get_weights_path_from_url(
|
||||
model_urls[arch][0], model_urls[arch][1]
|
||||
)
|
||||
|
||||
param = paddle.load(weight_path)
|
||||
model.set_dict(param)
|
||||
return model
|
||||
@@ -0,0 +1,654 @@
|
||||
# Copyright (c) 2021 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 annotations
|
||||
|
||||
import math
|
||||
from typing import (
|
||||
TYPE_CHECKING,
|
||||
TypedDict,
|
||||
)
|
||||
|
||||
from typing_extensions import (
|
||||
NotRequired,
|
||||
Unpack,
|
||||
)
|
||||
|
||||
import paddle
|
||||
from paddle import nn
|
||||
from paddle.base.param_attr import ParamAttr
|
||||
from paddle.nn import AdaptiveAvgPool2D, AvgPool2D, Dropout, Linear, MaxPool2D
|
||||
from paddle.nn.initializer import Uniform
|
||||
from paddle.utils.download import get_weights_path_from_url
|
||||
|
||||
from ..ops import ConvNormActivation
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from paddle import Tensor
|
||||
|
||||
class _InceptionV3Options(TypedDict):
|
||||
num_classes: NotRequired[int]
|
||||
with_pool: NotRequired[bool]
|
||||
|
||||
|
||||
__all__ = []
|
||||
|
||||
model_urls = {
|
||||
"inception_v3": (
|
||||
"https://paddle-hapi.bj.bcebos.com/models/inception_v3.pdparams",
|
||||
"649a4547c3243e8b59c656f41fe330b8",
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
class InceptionStem(nn.Layer):
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
self.conv_1a_3x3 = ConvNormActivation(
|
||||
in_channels=3,
|
||||
out_channels=32,
|
||||
kernel_size=3,
|
||||
stride=2,
|
||||
padding=0,
|
||||
activation_layer=nn.ReLU,
|
||||
)
|
||||
self.conv_2a_3x3 = ConvNormActivation(
|
||||
in_channels=32,
|
||||
out_channels=32,
|
||||
kernel_size=3,
|
||||
stride=1,
|
||||
padding=0,
|
||||
activation_layer=nn.ReLU,
|
||||
)
|
||||
self.conv_2b_3x3 = ConvNormActivation(
|
||||
in_channels=32,
|
||||
out_channels=64,
|
||||
kernel_size=3,
|
||||
padding=1,
|
||||
activation_layer=nn.ReLU,
|
||||
)
|
||||
|
||||
self.max_pool = MaxPool2D(kernel_size=3, stride=2, padding=0)
|
||||
self.conv_3b_1x1 = ConvNormActivation(
|
||||
in_channels=64,
|
||||
out_channels=80,
|
||||
kernel_size=1,
|
||||
padding=0,
|
||||
activation_layer=nn.ReLU,
|
||||
)
|
||||
self.conv_4a_3x3 = ConvNormActivation(
|
||||
in_channels=80,
|
||||
out_channels=192,
|
||||
kernel_size=3,
|
||||
padding=0,
|
||||
activation_layer=nn.ReLU,
|
||||
)
|
||||
|
||||
def forward(self, x: Tensor) -> Tensor:
|
||||
x = self.conv_1a_3x3(x)
|
||||
x = self.conv_2a_3x3(x)
|
||||
x = self.conv_2b_3x3(x)
|
||||
x = self.max_pool(x)
|
||||
x = self.conv_3b_1x1(x)
|
||||
x = self.conv_4a_3x3(x)
|
||||
x = self.max_pool(x)
|
||||
return x
|
||||
|
||||
|
||||
class InceptionA(nn.Layer):
|
||||
def __init__(self, num_channels: int, pool_features: int) -> None:
|
||||
super().__init__()
|
||||
self.branch1x1 = ConvNormActivation(
|
||||
in_channels=num_channels,
|
||||
out_channels=64,
|
||||
kernel_size=1,
|
||||
padding=0,
|
||||
activation_layer=nn.ReLU,
|
||||
)
|
||||
|
||||
self.branch5x5_1 = ConvNormActivation(
|
||||
in_channels=num_channels,
|
||||
out_channels=48,
|
||||
kernel_size=1,
|
||||
padding=0,
|
||||
activation_layer=nn.ReLU,
|
||||
)
|
||||
self.branch5x5_2 = ConvNormActivation(
|
||||
in_channels=48,
|
||||
out_channels=64,
|
||||
kernel_size=5,
|
||||
padding=2,
|
||||
activation_layer=nn.ReLU,
|
||||
)
|
||||
|
||||
self.branch3x3dbl_1 = ConvNormActivation(
|
||||
in_channels=num_channels,
|
||||
out_channels=64,
|
||||
kernel_size=1,
|
||||
padding=0,
|
||||
activation_layer=nn.ReLU,
|
||||
)
|
||||
self.branch3x3dbl_2 = ConvNormActivation(
|
||||
in_channels=64,
|
||||
out_channels=96,
|
||||
kernel_size=3,
|
||||
padding=1,
|
||||
activation_layer=nn.ReLU,
|
||||
)
|
||||
self.branch3x3dbl_3 = ConvNormActivation(
|
||||
in_channels=96,
|
||||
out_channels=96,
|
||||
kernel_size=3,
|
||||
padding=1,
|
||||
activation_layer=nn.ReLU,
|
||||
)
|
||||
|
||||
self.branch_pool = AvgPool2D(
|
||||
kernel_size=3, stride=1, padding=1, exclusive=False
|
||||
)
|
||||
self.branch_pool_conv = ConvNormActivation(
|
||||
in_channels=num_channels,
|
||||
out_channels=pool_features,
|
||||
kernel_size=1,
|
||||
padding=0,
|
||||
activation_layer=nn.ReLU,
|
||||
)
|
||||
|
||||
def forward(self, x: Tensor) -> Tensor:
|
||||
branch1x1 = self.branch1x1(x)
|
||||
branch5x5 = self.branch5x5_1(x)
|
||||
branch5x5 = self.branch5x5_2(branch5x5)
|
||||
|
||||
branch3x3dbl = self.branch3x3dbl_1(x)
|
||||
branch3x3dbl = self.branch3x3dbl_2(branch3x3dbl)
|
||||
branch3x3dbl = self.branch3x3dbl_3(branch3x3dbl)
|
||||
|
||||
branch_pool = self.branch_pool(x)
|
||||
branch_pool = self.branch_pool_conv(branch_pool)
|
||||
x = paddle.concat(
|
||||
[branch1x1, branch5x5, branch3x3dbl, branch_pool], axis=1
|
||||
)
|
||||
return x
|
||||
|
||||
|
||||
class InceptionB(nn.Layer):
|
||||
def __init__(self, num_channels: int) -> None:
|
||||
super().__init__()
|
||||
self.branch3x3 = ConvNormActivation(
|
||||
in_channels=num_channels,
|
||||
out_channels=384,
|
||||
kernel_size=3,
|
||||
stride=2,
|
||||
padding=0,
|
||||
activation_layer=nn.ReLU,
|
||||
)
|
||||
|
||||
self.branch3x3dbl_1 = ConvNormActivation(
|
||||
in_channels=num_channels,
|
||||
out_channels=64,
|
||||
kernel_size=1,
|
||||
padding=0,
|
||||
activation_layer=nn.ReLU,
|
||||
)
|
||||
self.branch3x3dbl_2 = ConvNormActivation(
|
||||
in_channels=64,
|
||||
out_channels=96,
|
||||
kernel_size=3,
|
||||
padding=1,
|
||||
activation_layer=nn.ReLU,
|
||||
)
|
||||
self.branch3x3dbl_3 = ConvNormActivation(
|
||||
in_channels=96,
|
||||
out_channels=96,
|
||||
kernel_size=3,
|
||||
stride=2,
|
||||
padding=0,
|
||||
activation_layer=nn.ReLU,
|
||||
)
|
||||
|
||||
self.branch_pool = MaxPool2D(kernel_size=3, stride=2)
|
||||
|
||||
def forward(self, x: Tensor) -> Tensor:
|
||||
branch3x3 = self.branch3x3(x)
|
||||
|
||||
branch3x3dbl = self.branch3x3dbl_1(x)
|
||||
branch3x3dbl = self.branch3x3dbl_2(branch3x3dbl)
|
||||
branch3x3dbl = self.branch3x3dbl_3(branch3x3dbl)
|
||||
|
||||
branch_pool = self.branch_pool(x)
|
||||
|
||||
x = paddle.concat([branch3x3, branch3x3dbl, branch_pool], axis=1)
|
||||
|
||||
return x
|
||||
|
||||
|
||||
class InceptionC(nn.Layer):
|
||||
def __init__(self, num_channels: int, channels_7x7: int) -> None:
|
||||
super().__init__()
|
||||
self.branch1x1 = ConvNormActivation(
|
||||
in_channels=num_channels,
|
||||
out_channels=192,
|
||||
kernel_size=1,
|
||||
padding=0,
|
||||
activation_layer=nn.ReLU,
|
||||
)
|
||||
|
||||
self.branch7x7_1 = ConvNormActivation(
|
||||
in_channels=num_channels,
|
||||
out_channels=channels_7x7,
|
||||
kernel_size=1,
|
||||
stride=1,
|
||||
padding=0,
|
||||
activation_layer=nn.ReLU,
|
||||
)
|
||||
self.branch7x7_2 = ConvNormActivation(
|
||||
in_channels=channels_7x7,
|
||||
out_channels=channels_7x7,
|
||||
kernel_size=(1, 7),
|
||||
stride=1,
|
||||
padding=(0, 3),
|
||||
activation_layer=nn.ReLU,
|
||||
)
|
||||
self.branch7x7_3 = ConvNormActivation(
|
||||
in_channels=channels_7x7,
|
||||
out_channels=192,
|
||||
kernel_size=(7, 1),
|
||||
stride=1,
|
||||
padding=(3, 0),
|
||||
activation_layer=nn.ReLU,
|
||||
)
|
||||
|
||||
self.branch7x7dbl_1 = ConvNormActivation(
|
||||
in_channels=num_channels,
|
||||
out_channels=channels_7x7,
|
||||
kernel_size=1,
|
||||
padding=0,
|
||||
activation_layer=nn.ReLU,
|
||||
)
|
||||
self.branch7x7dbl_2 = ConvNormActivation(
|
||||
in_channels=channels_7x7,
|
||||
out_channels=channels_7x7,
|
||||
kernel_size=(7, 1),
|
||||
padding=(3, 0),
|
||||
activation_layer=nn.ReLU,
|
||||
)
|
||||
self.branch7x7dbl_3 = ConvNormActivation(
|
||||
in_channels=channels_7x7,
|
||||
out_channels=channels_7x7,
|
||||
kernel_size=(1, 7),
|
||||
padding=(0, 3),
|
||||
activation_layer=nn.ReLU,
|
||||
)
|
||||
self.branch7x7dbl_4 = ConvNormActivation(
|
||||
in_channels=channels_7x7,
|
||||
out_channels=channels_7x7,
|
||||
kernel_size=(7, 1),
|
||||
padding=(3, 0),
|
||||
activation_layer=nn.ReLU,
|
||||
)
|
||||
self.branch7x7dbl_5 = ConvNormActivation(
|
||||
in_channels=channels_7x7,
|
||||
out_channels=192,
|
||||
kernel_size=(1, 7),
|
||||
padding=(0, 3),
|
||||
activation_layer=nn.ReLU,
|
||||
)
|
||||
|
||||
self.branch_pool = AvgPool2D(
|
||||
kernel_size=3, stride=1, padding=1, exclusive=False
|
||||
)
|
||||
self.branch_pool_conv = ConvNormActivation(
|
||||
in_channels=num_channels,
|
||||
out_channels=192,
|
||||
kernel_size=1,
|
||||
padding=0,
|
||||
activation_layer=nn.ReLU,
|
||||
)
|
||||
|
||||
def forward(self, x: Tensor) -> Tensor:
|
||||
branch1x1 = self.branch1x1(x)
|
||||
|
||||
branch7x7 = self.branch7x7_1(x)
|
||||
branch7x7 = self.branch7x7_2(branch7x7)
|
||||
branch7x7 = self.branch7x7_3(branch7x7)
|
||||
|
||||
branch7x7dbl = self.branch7x7dbl_1(x)
|
||||
branch7x7dbl = self.branch7x7dbl_2(branch7x7dbl)
|
||||
branch7x7dbl = self.branch7x7dbl_3(branch7x7dbl)
|
||||
branch7x7dbl = self.branch7x7dbl_4(branch7x7dbl)
|
||||
branch7x7dbl = self.branch7x7dbl_5(branch7x7dbl)
|
||||
|
||||
branch_pool = self.branch_pool(x)
|
||||
branch_pool = self.branch_pool_conv(branch_pool)
|
||||
|
||||
x = paddle.concat(
|
||||
[branch1x1, branch7x7, branch7x7dbl, branch_pool], axis=1
|
||||
)
|
||||
|
||||
return x
|
||||
|
||||
|
||||
class InceptionD(nn.Layer):
|
||||
def __init__(self, num_channels: int) -> None:
|
||||
super().__init__()
|
||||
self.branch3x3_1 = ConvNormActivation(
|
||||
in_channels=num_channels,
|
||||
out_channels=192,
|
||||
kernel_size=1,
|
||||
padding=0,
|
||||
activation_layer=nn.ReLU,
|
||||
)
|
||||
self.branch3x3_2 = ConvNormActivation(
|
||||
in_channels=192,
|
||||
out_channels=320,
|
||||
kernel_size=3,
|
||||
stride=2,
|
||||
padding=0,
|
||||
activation_layer=nn.ReLU,
|
||||
)
|
||||
|
||||
self.branch7x7x3_1 = ConvNormActivation(
|
||||
in_channels=num_channels,
|
||||
out_channels=192,
|
||||
kernel_size=1,
|
||||
padding=0,
|
||||
activation_layer=nn.ReLU,
|
||||
)
|
||||
self.branch7x7x3_2 = ConvNormActivation(
|
||||
in_channels=192,
|
||||
out_channels=192,
|
||||
kernel_size=(1, 7),
|
||||
padding=(0, 3),
|
||||
activation_layer=nn.ReLU,
|
||||
)
|
||||
self.branch7x7x3_3 = ConvNormActivation(
|
||||
in_channels=192,
|
||||
out_channels=192,
|
||||
kernel_size=(7, 1),
|
||||
padding=(3, 0),
|
||||
activation_layer=nn.ReLU,
|
||||
)
|
||||
self.branch7x7x3_4 = ConvNormActivation(
|
||||
in_channels=192,
|
||||
out_channels=192,
|
||||
kernel_size=3,
|
||||
stride=2,
|
||||
padding=0,
|
||||
activation_layer=nn.ReLU,
|
||||
)
|
||||
|
||||
self.branch_pool = MaxPool2D(kernel_size=3, stride=2)
|
||||
|
||||
def forward(self, x: Tensor) -> Tensor:
|
||||
branch3x3 = self.branch3x3_1(x)
|
||||
branch3x3 = self.branch3x3_2(branch3x3)
|
||||
|
||||
branch7x7x3 = self.branch7x7x3_1(x)
|
||||
branch7x7x3 = self.branch7x7x3_2(branch7x7x3)
|
||||
branch7x7x3 = self.branch7x7x3_3(branch7x7x3)
|
||||
branch7x7x3 = self.branch7x7x3_4(branch7x7x3)
|
||||
|
||||
branch_pool = self.branch_pool(x)
|
||||
|
||||
x = paddle.concat([branch3x3, branch7x7x3, branch_pool], axis=1)
|
||||
return x
|
||||
|
||||
|
||||
class InceptionE(nn.Layer):
|
||||
def __init__(self, num_channels: int) -> None:
|
||||
super().__init__()
|
||||
self.branch1x1 = ConvNormActivation(
|
||||
in_channels=num_channels,
|
||||
out_channels=320,
|
||||
kernel_size=1,
|
||||
padding=0,
|
||||
activation_layer=nn.ReLU,
|
||||
)
|
||||
self.branch3x3_1 = ConvNormActivation(
|
||||
in_channels=num_channels,
|
||||
out_channels=384,
|
||||
kernel_size=1,
|
||||
padding=0,
|
||||
activation_layer=nn.ReLU,
|
||||
)
|
||||
self.branch3x3_2a = ConvNormActivation(
|
||||
in_channels=384,
|
||||
out_channels=384,
|
||||
kernel_size=(1, 3),
|
||||
padding=(0, 1),
|
||||
activation_layer=nn.ReLU,
|
||||
)
|
||||
self.branch3x3_2b = ConvNormActivation(
|
||||
in_channels=384,
|
||||
out_channels=384,
|
||||
kernel_size=(3, 1),
|
||||
padding=(1, 0),
|
||||
activation_layer=nn.ReLU,
|
||||
)
|
||||
|
||||
self.branch3x3dbl_1 = ConvNormActivation(
|
||||
in_channels=num_channels,
|
||||
out_channels=448,
|
||||
kernel_size=1,
|
||||
padding=0,
|
||||
activation_layer=nn.ReLU,
|
||||
)
|
||||
self.branch3x3dbl_2 = ConvNormActivation(
|
||||
in_channels=448,
|
||||
out_channels=384,
|
||||
kernel_size=3,
|
||||
padding=1,
|
||||
activation_layer=nn.ReLU,
|
||||
)
|
||||
self.branch3x3dbl_3a = ConvNormActivation(
|
||||
in_channels=384,
|
||||
out_channels=384,
|
||||
kernel_size=(1, 3),
|
||||
padding=(0, 1),
|
||||
activation_layer=nn.ReLU,
|
||||
)
|
||||
self.branch3x3dbl_3b = ConvNormActivation(
|
||||
in_channels=384,
|
||||
out_channels=384,
|
||||
kernel_size=(3, 1),
|
||||
padding=(1, 0),
|
||||
activation_layer=nn.ReLU,
|
||||
)
|
||||
|
||||
self.branch_pool = AvgPool2D(
|
||||
kernel_size=3, stride=1, padding=1, exclusive=False
|
||||
)
|
||||
self.branch_pool_conv = ConvNormActivation(
|
||||
in_channels=num_channels,
|
||||
out_channels=192,
|
||||
kernel_size=1,
|
||||
padding=0,
|
||||
activation_layer=nn.ReLU,
|
||||
)
|
||||
|
||||
def forward(self, x: Tensor) -> Tensor:
|
||||
branch1x1 = self.branch1x1(x)
|
||||
|
||||
branch3x3 = self.branch3x3_1(x)
|
||||
branch3x3 = [
|
||||
self.branch3x3_2a(branch3x3),
|
||||
self.branch3x3_2b(branch3x3),
|
||||
]
|
||||
branch3x3 = paddle.concat(branch3x3, axis=1)
|
||||
|
||||
branch3x3dbl = self.branch3x3dbl_1(x)
|
||||
branch3x3dbl = self.branch3x3dbl_2(branch3x3dbl)
|
||||
branch3x3dbl = [
|
||||
self.branch3x3dbl_3a(branch3x3dbl),
|
||||
self.branch3x3dbl_3b(branch3x3dbl),
|
||||
]
|
||||
branch3x3dbl = paddle.concat(branch3x3dbl, axis=1)
|
||||
|
||||
branch_pool = self.branch_pool(x)
|
||||
branch_pool = self.branch_pool_conv(branch_pool)
|
||||
|
||||
x = paddle.concat(
|
||||
[branch1x1, branch3x3, branch3x3dbl, branch_pool], axis=1
|
||||
)
|
||||
return x
|
||||
|
||||
|
||||
class InceptionV3(nn.Layer):
|
||||
"""Inception v3 model from
|
||||
`"Rethinking the Inception Architecture for Computer Vision" <https://arxiv.org/pdf/1512.00567.pdf>`_.
|
||||
|
||||
Args:
|
||||
num_classes (int, optional): Output dim of last fc layer. If num_classes <= 0, last fc layer
|
||||
will not be defined. Default: 1000.
|
||||
with_pool (bool, optional): Use pool before the last fc layer or not. Default: True.
|
||||
|
||||
Returns:
|
||||
:ref:`api_paddle_nn_Layer`. An instance of Inception v3 model.
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
>>> from paddle.vision.models import InceptionV3
|
||||
|
||||
>>> inception_v3 = InceptionV3()
|
||||
|
||||
>>> x = paddle.rand([1, 3, 299, 299])
|
||||
>>> out = inception_v3(x)
|
||||
|
||||
>>> print(out.shape)
|
||||
paddle.Size([1, 1000])
|
||||
"""
|
||||
|
||||
num_classes: int
|
||||
with_pool: bool
|
||||
|
||||
def __init__(self, num_classes: int = 1000, with_pool: bool = True) -> None:
|
||||
super().__init__()
|
||||
self.num_classes = num_classes
|
||||
self.with_pool = with_pool
|
||||
self.layers_config = {
|
||||
"inception_a": [[192, 256, 288], [32, 64, 64]],
|
||||
"inception_b": [288],
|
||||
"inception_c": [[768, 768, 768, 768], [128, 160, 160, 192]],
|
||||
"inception_d": [768],
|
||||
"inception_e": [1280, 2048],
|
||||
}
|
||||
|
||||
inception_a_list = self.layers_config["inception_a"]
|
||||
inception_c_list = self.layers_config["inception_c"]
|
||||
inception_b_list = self.layers_config["inception_b"]
|
||||
inception_d_list = self.layers_config["inception_d"]
|
||||
inception_e_list = self.layers_config["inception_e"]
|
||||
|
||||
self.inception_stem = InceptionStem()
|
||||
|
||||
self.inception_block_list = nn.LayerList()
|
||||
for i in range(len(inception_a_list[0])):
|
||||
inception_a = InceptionA(
|
||||
inception_a_list[0][i], inception_a_list[1][i]
|
||||
)
|
||||
self.inception_block_list.append(inception_a)
|
||||
|
||||
for i in range(len(inception_b_list)):
|
||||
inception_b = InceptionB(inception_b_list[i])
|
||||
self.inception_block_list.append(inception_b)
|
||||
|
||||
for i in range(len(inception_c_list[0])):
|
||||
inception_c = InceptionC(
|
||||
inception_c_list[0][i], inception_c_list[1][i]
|
||||
)
|
||||
self.inception_block_list.append(inception_c)
|
||||
|
||||
for i in range(len(inception_d_list)):
|
||||
inception_d = InceptionD(inception_d_list[i])
|
||||
self.inception_block_list.append(inception_d)
|
||||
|
||||
for i in range(len(inception_e_list)):
|
||||
inception_e = InceptionE(inception_e_list[i])
|
||||
self.inception_block_list.append(inception_e)
|
||||
|
||||
if with_pool:
|
||||
self.avg_pool = AdaptiveAvgPool2D(1)
|
||||
|
||||
if num_classes > 0:
|
||||
self.dropout = Dropout(p=0.2, mode="downscale_in_infer")
|
||||
stdv = 1.0 / math.sqrt(2048 * 1.0)
|
||||
self.fc = Linear(
|
||||
2048,
|
||||
num_classes,
|
||||
weight_attr=ParamAttr(initializer=Uniform(-stdv, stdv)),
|
||||
bias_attr=ParamAttr(),
|
||||
)
|
||||
|
||||
def forward(self, x: Tensor) -> Tensor:
|
||||
x = self.inception_stem(x)
|
||||
for inception_block in self.inception_block_list:
|
||||
x = inception_block(x)
|
||||
|
||||
if self.with_pool:
|
||||
x = self.avg_pool(x)
|
||||
|
||||
if self.num_classes > 0:
|
||||
x = paddle.reshape(x, shape=[-1, 2048])
|
||||
x = self.dropout(x)
|
||||
x = self.fc(x)
|
||||
return x
|
||||
|
||||
|
||||
def inception_v3(
|
||||
pretrained: bool = False, **kwargs: Unpack[_InceptionV3Options]
|
||||
) -> InceptionV3:
|
||||
"""Inception v3 model from
|
||||
`"Rethinking the Inception Architecture for Computer Vision" <https://arxiv.org/pdf/1512.00567.pdf>`_.
|
||||
|
||||
Args:
|
||||
pretrained (bool, optional): Whether to load pre-trained weights. If True, returns a model pre-trained
|
||||
on ImageNet. Default: False.
|
||||
**kwargs (optional): Additional keyword arguments. For details, please refer to :ref:`InceptionV3 <api_paddle_vision_models_InceptionV3>`.
|
||||
|
||||
Returns:
|
||||
:ref:`api_paddle_nn_Layer`. An instance of Inception v3 model.
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
>>> from paddle.vision.models import inception_v3
|
||||
|
||||
>>> # Build model
|
||||
>>> model = inception_v3()
|
||||
|
||||
>>> # Build model and load imagenet pretrained weight
|
||||
>>> # model = inception_v3(pretrained=True)
|
||||
|
||||
>>> x = paddle.rand([1, 3, 299, 299])
|
||||
>>> out = model(x)
|
||||
|
||||
>>> print(out.shape)
|
||||
paddle.Size([1, 1000])
|
||||
"""
|
||||
model = InceptionV3(**kwargs)
|
||||
arch = "inception_v3"
|
||||
if pretrained:
|
||||
assert arch in model_urls, (
|
||||
f"{arch} model do not have a pretrained model now, you should set pretrained=False"
|
||||
)
|
||||
weight_path = get_weights_path_from_url(
|
||||
model_urls[arch][0], model_urls[arch][1]
|
||||
)
|
||||
|
||||
param = paddle.load(weight_path)
|
||||
model.set_dict(param)
|
||||
return model
|
||||
@@ -0,0 +1,83 @@
|
||||
# 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.
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import (
|
||||
TYPE_CHECKING,
|
||||
)
|
||||
|
||||
import paddle
|
||||
from paddle import nn
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from paddle import Tensor
|
||||
|
||||
__all__ = []
|
||||
|
||||
|
||||
class LeNet(nn.Layer):
|
||||
"""LeNet model from
|
||||
`"Gradient-based learning applied to document recognition" <https://ieeexplore.ieee.org/document/726791>`_.
|
||||
|
||||
Args:
|
||||
num_classes (int, optional): Output dim of last fc layer. If num_classes <= 0, last fc layer
|
||||
will not be defined. Default: 10.
|
||||
|
||||
Returns:
|
||||
:ref:`api_paddle_nn_Layer`. An instance of LeNet model.
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
>>> from paddle.vision.models import LeNet
|
||||
|
||||
>>> model = LeNet()
|
||||
|
||||
>>> x = paddle.rand([1, 1, 28, 28])
|
||||
>>> out = model(x)
|
||||
|
||||
>>> print(out.shape)
|
||||
paddle.Size([1, 10])
|
||||
"""
|
||||
|
||||
num_classes: int
|
||||
|
||||
def __init__(self, num_classes: int = 10) -> None:
|
||||
super().__init__()
|
||||
self.num_classes = num_classes
|
||||
self.features = nn.Sequential(
|
||||
nn.Conv2D(1, 6, 3, stride=1, padding=1),
|
||||
nn.ReLU(),
|
||||
nn.MaxPool2D(2, 2),
|
||||
nn.Conv2D(6, 16, 5, stride=1, padding=0),
|
||||
nn.ReLU(),
|
||||
nn.MaxPool2D(2, 2),
|
||||
)
|
||||
|
||||
if num_classes > 0:
|
||||
self.fc = nn.Sequential(
|
||||
nn.Linear(400, 120),
|
||||
nn.Linear(120, 84),
|
||||
nn.Linear(84, num_classes),
|
||||
)
|
||||
|
||||
def forward(self, inputs: Tensor) -> Tensor:
|
||||
x = self.features(inputs)
|
||||
|
||||
if self.num_classes > 0:
|
||||
x = paddle.flatten(x, 1)
|
||||
x = self.fc(x)
|
||||
return x
|
||||
@@ -0,0 +1,334 @@
|
||||
# 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.
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import (
|
||||
TYPE_CHECKING,
|
||||
TypedDict,
|
||||
)
|
||||
|
||||
from typing_extensions import NotRequired, Unpack
|
||||
|
||||
import paddle
|
||||
from paddle import nn
|
||||
from paddle.utils.download import get_weights_path_from_url
|
||||
|
||||
from ..ops import ConvNormActivation
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from paddle import Tensor
|
||||
from paddle._typing import Size2
|
||||
|
||||
class _MobileNetV1Options(TypedDict):
|
||||
num_classes: NotRequired[int]
|
||||
with_pool: NotRequired[bool]
|
||||
|
||||
|
||||
__all__ = []
|
||||
|
||||
|
||||
model_urls = {
|
||||
'mobilenetv1_1.0': (
|
||||
'https://paddle-hapi.bj.bcebos.com/models/mobilenetv1_1.0.pdparams',
|
||||
'3033ab1975b1670bef51545feb65fc45',
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
class DepthwiseSeparable(nn.Layer):
|
||||
def __init__(
|
||||
self,
|
||||
in_channels: int,
|
||||
out_channels1: int,
|
||||
out_channels2: int,
|
||||
num_groups: int,
|
||||
stride: Size2,
|
||||
scale: float,
|
||||
) -> None:
|
||||
super().__init__()
|
||||
|
||||
self._depthwise_conv = ConvNormActivation(
|
||||
in_channels,
|
||||
int(out_channels1 * scale),
|
||||
kernel_size=3,
|
||||
stride=stride,
|
||||
padding=1,
|
||||
groups=int(num_groups * scale),
|
||||
)
|
||||
|
||||
self._pointwise_conv = ConvNormActivation(
|
||||
int(out_channels1 * scale),
|
||||
int(out_channels2 * scale),
|
||||
kernel_size=1,
|
||||
stride=1,
|
||||
padding=0,
|
||||
)
|
||||
|
||||
def forward(self, x: Tensor) -> Tensor:
|
||||
x = self._depthwise_conv(x)
|
||||
x = self._pointwise_conv(x)
|
||||
return x
|
||||
|
||||
|
||||
class MobileNetV1(nn.Layer):
|
||||
"""MobileNetV1 model from
|
||||
`"MobileNets: Efficient Convolutional Neural Networks for Mobile Vision Applications" <https://arxiv.org/abs/1704.04861>`_.
|
||||
|
||||
Args:
|
||||
scale (float, optional): Scale of channels in each layer. Default: 1.0.
|
||||
num_classes (int, optional): Output dim of last fc layer. If num_classes <= 0, last fc layer
|
||||
will not be defined. Default: 1000.
|
||||
with_pool (bool, optional): Use pool before the last fc layer or not. Default: True.
|
||||
|
||||
Returns:
|
||||
:ref:`api_paddle_nn_Layer`. An instance of MobileNetV1 model.
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
>>> from paddle.vision.models import MobileNetV1
|
||||
|
||||
>>> model = MobileNetV1()
|
||||
|
||||
>>> x = paddle.rand([1, 3, 224, 224])
|
||||
>>> out = model(x)
|
||||
|
||||
>>> print(out.shape)
|
||||
paddle.Size([1, 1000])
|
||||
"""
|
||||
|
||||
scale: float
|
||||
num_classes: int
|
||||
with_pool: bool
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
scale: float = 1.0,
|
||||
num_classes: int = 1000,
|
||||
with_pool: bool = True,
|
||||
) -> None:
|
||||
super().__init__()
|
||||
self.scale = scale
|
||||
self.dwsl = []
|
||||
self.num_classes = num_classes
|
||||
self.with_pool = with_pool
|
||||
|
||||
self.conv1 = ConvNormActivation(
|
||||
in_channels=3,
|
||||
out_channels=int(32 * scale),
|
||||
kernel_size=3,
|
||||
stride=2,
|
||||
padding=1,
|
||||
)
|
||||
|
||||
dws21 = self.add_sublayer(
|
||||
sublayer=DepthwiseSeparable(
|
||||
in_channels=int(32 * scale),
|
||||
out_channels1=32,
|
||||
out_channels2=64,
|
||||
num_groups=32,
|
||||
stride=1,
|
||||
scale=scale,
|
||||
),
|
||||
name="conv2_1",
|
||||
)
|
||||
self.dwsl.append(dws21)
|
||||
|
||||
dws22 = self.add_sublayer(
|
||||
sublayer=DepthwiseSeparable(
|
||||
in_channels=int(64 * scale),
|
||||
out_channels1=64,
|
||||
out_channels2=128,
|
||||
num_groups=64,
|
||||
stride=2,
|
||||
scale=scale,
|
||||
),
|
||||
name="conv2_2",
|
||||
)
|
||||
self.dwsl.append(dws22)
|
||||
|
||||
dws31 = self.add_sublayer(
|
||||
sublayer=DepthwiseSeparable(
|
||||
in_channels=int(128 * scale),
|
||||
out_channels1=128,
|
||||
out_channels2=128,
|
||||
num_groups=128,
|
||||
stride=1,
|
||||
scale=scale,
|
||||
),
|
||||
name="conv3_1",
|
||||
)
|
||||
self.dwsl.append(dws31)
|
||||
|
||||
dws32 = self.add_sublayer(
|
||||
sublayer=DepthwiseSeparable(
|
||||
in_channels=int(128 * scale),
|
||||
out_channels1=128,
|
||||
out_channels2=256,
|
||||
num_groups=128,
|
||||
stride=2,
|
||||
scale=scale,
|
||||
),
|
||||
name="conv3_2",
|
||||
)
|
||||
self.dwsl.append(dws32)
|
||||
|
||||
dws41 = self.add_sublayer(
|
||||
sublayer=DepthwiseSeparable(
|
||||
in_channels=int(256 * scale),
|
||||
out_channels1=256,
|
||||
out_channels2=256,
|
||||
num_groups=256,
|
||||
stride=1,
|
||||
scale=scale,
|
||||
),
|
||||
name="conv4_1",
|
||||
)
|
||||
self.dwsl.append(dws41)
|
||||
|
||||
dws42 = self.add_sublayer(
|
||||
sublayer=DepthwiseSeparable(
|
||||
in_channels=int(256 * scale),
|
||||
out_channels1=256,
|
||||
out_channels2=512,
|
||||
num_groups=256,
|
||||
stride=2,
|
||||
scale=scale,
|
||||
),
|
||||
name="conv4_2",
|
||||
)
|
||||
self.dwsl.append(dws42)
|
||||
|
||||
for i in range(5):
|
||||
tmp = self.add_sublayer(
|
||||
sublayer=DepthwiseSeparable(
|
||||
in_channels=int(512 * scale),
|
||||
out_channels1=512,
|
||||
out_channels2=512,
|
||||
num_groups=512,
|
||||
stride=1,
|
||||
scale=scale,
|
||||
),
|
||||
name="conv5_" + str(i + 1),
|
||||
)
|
||||
self.dwsl.append(tmp)
|
||||
|
||||
dws56 = self.add_sublayer(
|
||||
sublayer=DepthwiseSeparable(
|
||||
in_channels=int(512 * scale),
|
||||
out_channels1=512,
|
||||
out_channels2=1024,
|
||||
num_groups=512,
|
||||
stride=2,
|
||||
scale=scale,
|
||||
),
|
||||
name="conv5_6",
|
||||
)
|
||||
self.dwsl.append(dws56)
|
||||
|
||||
dws6 = self.add_sublayer(
|
||||
sublayer=DepthwiseSeparable(
|
||||
in_channels=int(1024 * scale),
|
||||
out_channels1=1024,
|
||||
out_channels2=1024,
|
||||
num_groups=1024,
|
||||
stride=1,
|
||||
scale=scale,
|
||||
),
|
||||
name="conv6",
|
||||
)
|
||||
self.dwsl.append(dws6)
|
||||
|
||||
if with_pool:
|
||||
self.pool2d_avg = nn.AdaptiveAvgPool2D(1)
|
||||
|
||||
if num_classes > 0:
|
||||
self.fc = nn.Linear(int(1024 * scale), num_classes)
|
||||
|
||||
def forward(self, x: Tensor) -> Tensor:
|
||||
x = self.conv1(x)
|
||||
for dws in self.dwsl:
|
||||
x = dws(x)
|
||||
|
||||
if self.with_pool:
|
||||
x = self.pool2d_avg(x)
|
||||
|
||||
if self.num_classes > 0:
|
||||
x = paddle.flatten(x, 1)
|
||||
x = self.fc(x)
|
||||
return x
|
||||
|
||||
|
||||
def _mobilenet(
|
||||
arch: str, pretrained: bool = False, **kwargs: Unpack[_MobileNetV1Options]
|
||||
) -> MobileNetV1:
|
||||
model = MobileNetV1(**kwargs)
|
||||
if pretrained:
|
||||
assert arch in model_urls, (
|
||||
f"{arch} model do not have a pretrained model now, you should set pretrained=False"
|
||||
)
|
||||
weight_path = get_weights_path_from_url(
|
||||
model_urls[arch][0], model_urls[arch][1]
|
||||
)
|
||||
|
||||
param = paddle.load(weight_path)
|
||||
model.load_dict(param)
|
||||
|
||||
return model
|
||||
|
||||
|
||||
def mobilenet_v1(
|
||||
pretrained: bool = False,
|
||||
scale: float = 1.0,
|
||||
**kwargs: Unpack[_MobileNetV1Options],
|
||||
) -> MobileNetV1:
|
||||
"""MobileNetV1 from
|
||||
`"MobileNets: Efficient Convolutional Neural Networks for Mobile Vision Applications" <https://arxiv.org/abs/1704.04861>`_.
|
||||
|
||||
Args:
|
||||
pretrained (bool, optional): Whether to load pre-trained weights. If True, returns a model pre-trained
|
||||
on ImageNet. Default: False.
|
||||
scale (float, optional): Scale of channels in each layer. Default: 1.0.
|
||||
**kwargs (optional): Additional keyword arguments. For details, please refer to :ref:`MobileNetV1 <api_paddle_vision_models_MobileNetV1>`.
|
||||
|
||||
Returns:
|
||||
:ref:`api_paddle_nn_Layer`. An instance of MobileNetV1 model.
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
>>> from paddle.vision.models import mobilenet_v1
|
||||
|
||||
>>> # Build model
|
||||
>>> model = mobilenet_v1()
|
||||
|
||||
>>> # Build model and load imagenet pretrained weight
|
||||
>>> # model = mobilenet_v1(pretrained=True)
|
||||
|
||||
>>> # build mobilenet v1 with scale=0.5
|
||||
>>> model_scale = mobilenet_v1(scale=0.5)
|
||||
|
||||
>>> x = paddle.rand([1, 3, 224, 224])
|
||||
>>> out = model(x)
|
||||
|
||||
>>> print(out.shape)
|
||||
paddle.Size([1, 1000])
|
||||
"""
|
||||
model = _mobilenet(
|
||||
'mobilenetv1_' + str(scale), pretrained, scale=scale, **kwargs
|
||||
)
|
||||
return model
|
||||
@@ -0,0 +1,276 @@
|
||||
# Copyright (c) 2022 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 annotations
|
||||
|
||||
from typing import (
|
||||
TYPE_CHECKING,
|
||||
TypedDict,
|
||||
)
|
||||
|
||||
from typing_extensions import NotRequired, Unpack
|
||||
|
||||
import paddle
|
||||
from paddle import nn
|
||||
from paddle.utils.download import get_weights_path_from_url
|
||||
|
||||
from ..ops import ConvNormActivation
|
||||
from ._utils import _make_divisible
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from collections.abc import Callable
|
||||
|
||||
from paddle import Tensor
|
||||
|
||||
class _MobileNetV2Options(TypedDict):
|
||||
num_classes: NotRequired[int]
|
||||
with_pool: NotRequired[bool]
|
||||
|
||||
|
||||
__all__ = []
|
||||
|
||||
model_urls = {
|
||||
'mobilenetv2_1.0': (
|
||||
'https://paddle-hapi.bj.bcebos.com/models/mobilenet_v2_x1.0.pdparams',
|
||||
'0340af0a901346c8d46f4529882fb63d',
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
class InvertedResidual(nn.Layer):
|
||||
def __init__(
|
||||
self,
|
||||
inp: int,
|
||||
oup: int,
|
||||
stride: int,
|
||||
expand_ratio: float,
|
||||
norm_layer: Callable[..., nn.Layer] = nn.BatchNorm2D,
|
||||
) -> None:
|
||||
super().__init__()
|
||||
self.stride = stride
|
||||
assert stride in [1, 2]
|
||||
|
||||
hidden_dim = int(round(inp * expand_ratio))
|
||||
self.use_res_connect = self.stride == 1 and inp == oup
|
||||
|
||||
layers = []
|
||||
if expand_ratio != 1:
|
||||
layers.append(
|
||||
ConvNormActivation(
|
||||
inp,
|
||||
hidden_dim,
|
||||
kernel_size=1,
|
||||
norm_layer=norm_layer,
|
||||
activation_layer=nn.ReLU6,
|
||||
)
|
||||
)
|
||||
layers.extend(
|
||||
[
|
||||
ConvNormActivation(
|
||||
hidden_dim,
|
||||
hidden_dim,
|
||||
stride=stride,
|
||||
groups=hidden_dim,
|
||||
norm_layer=norm_layer,
|
||||
activation_layer=nn.ReLU6,
|
||||
),
|
||||
nn.Conv2D(hidden_dim, oup, 1, 1, 0, bias_attr=False),
|
||||
norm_layer(oup),
|
||||
]
|
||||
)
|
||||
self.conv = nn.Sequential(*layers)
|
||||
|
||||
def forward(self, x: Tensor) -> Tensor:
|
||||
if self.use_res_connect:
|
||||
return x + self.conv(x)
|
||||
else:
|
||||
return self.conv(x)
|
||||
|
||||
|
||||
class MobileNetV2(nn.Layer):
|
||||
"""MobileNetV2 model from
|
||||
`"MobileNetV2: Inverted Residuals and Linear Bottlenecks" <https://arxiv.org/abs/1801.04381>`_.
|
||||
|
||||
Args:
|
||||
scale (float, optional): Scale of channels in each layer. Default: 1.0.
|
||||
num_classes (int, optional): Output dim of last fc layer. If num_classes <= 0, last fc layer
|
||||
will not be defined. Default: 1000.
|
||||
with_pool (bool, optional): Use pool before the last fc layer or not. Default: True.
|
||||
|
||||
Returns:
|
||||
:ref:`api_paddle_nn_Layer`. An instance of MobileNetV2 model.
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
>>> from paddle.vision.models import MobileNetV2
|
||||
|
||||
>>> model = MobileNetV2()
|
||||
|
||||
>>> x = paddle.rand([1, 3, 224, 224])
|
||||
>>> out = model(x)
|
||||
|
||||
>>> print(out.shape)
|
||||
paddle.Size([1, 1000])
|
||||
"""
|
||||
|
||||
num_classes: int
|
||||
with_pool: bool
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
scale: float = 1.0,
|
||||
num_classes: int = 1000,
|
||||
with_pool: bool = True,
|
||||
) -> None:
|
||||
super().__init__()
|
||||
self.num_classes = num_classes
|
||||
self.with_pool = with_pool
|
||||
input_channel = 32
|
||||
last_channel = 1280
|
||||
|
||||
block = InvertedResidual
|
||||
round_nearest = 8
|
||||
norm_layer = nn.BatchNorm2D
|
||||
inverted_residual_setting = [
|
||||
[1, 16, 1, 1],
|
||||
[6, 24, 2, 2],
|
||||
[6, 32, 3, 2],
|
||||
[6, 64, 4, 2],
|
||||
[6, 96, 3, 1],
|
||||
[6, 160, 3, 2],
|
||||
[6, 320, 1, 1],
|
||||
]
|
||||
|
||||
input_channel = _make_divisible(input_channel * scale, round_nearest)
|
||||
self.last_channel = _make_divisible(
|
||||
last_channel * max(1.0, scale), round_nearest
|
||||
)
|
||||
features = [
|
||||
ConvNormActivation(
|
||||
3,
|
||||
input_channel,
|
||||
stride=2,
|
||||
norm_layer=norm_layer,
|
||||
activation_layer=nn.ReLU6,
|
||||
)
|
||||
]
|
||||
|
||||
for t, c, n, s in inverted_residual_setting:
|
||||
output_channel = _make_divisible(c * scale, round_nearest)
|
||||
for i in range(n):
|
||||
stride = s if i == 0 else 1
|
||||
features.append(
|
||||
block(
|
||||
input_channel,
|
||||
output_channel,
|
||||
stride,
|
||||
expand_ratio=t,
|
||||
norm_layer=norm_layer,
|
||||
)
|
||||
)
|
||||
input_channel = output_channel
|
||||
|
||||
features.append(
|
||||
ConvNormActivation(
|
||||
input_channel,
|
||||
self.last_channel,
|
||||
kernel_size=1,
|
||||
norm_layer=norm_layer,
|
||||
activation_layer=nn.ReLU6,
|
||||
)
|
||||
)
|
||||
|
||||
self.features = nn.Sequential(*features)
|
||||
|
||||
if with_pool:
|
||||
self.pool2d_avg = nn.AdaptiveAvgPool2D(1)
|
||||
|
||||
if self.num_classes > 0:
|
||||
self.classifier = nn.Sequential(
|
||||
nn.Dropout(0.2), nn.Linear(self.last_channel, num_classes)
|
||||
)
|
||||
|
||||
def forward(self, x: Tensor) -> Tensor:
|
||||
x = self.features(x)
|
||||
|
||||
if self.with_pool:
|
||||
x = self.pool2d_avg(x)
|
||||
|
||||
if self.num_classes > 0:
|
||||
x = paddle.flatten(x, 1)
|
||||
x = self.classifier(x)
|
||||
return x
|
||||
|
||||
|
||||
def _mobilenet(
|
||||
arch: str, pretrained: bool = False, **kwargs: Unpack[_MobileNetV2Options]
|
||||
) -> MobileNetV2:
|
||||
model = MobileNetV2(**kwargs)
|
||||
if pretrained:
|
||||
assert arch in model_urls, (
|
||||
f"{arch} model do not have a pretrained model now, you should set pretrained=False"
|
||||
)
|
||||
weight_path = get_weights_path_from_url(
|
||||
model_urls[arch][0], model_urls[arch][1]
|
||||
)
|
||||
|
||||
param = paddle.load(weight_path)
|
||||
model.load_dict(param)
|
||||
|
||||
return model
|
||||
|
||||
|
||||
def mobilenet_v2(
|
||||
pretrained: bool = False,
|
||||
scale: float = 1.0,
|
||||
**kwargs: Unpack[_MobileNetV2Options],
|
||||
) -> MobileNetV2:
|
||||
"""MobileNetV2 from
|
||||
`"MobileNetV2: Inverted Residuals and Linear Bottlenecks" <https://arxiv.org/abs/1801.04381>`_.
|
||||
|
||||
Args:
|
||||
pretrained (bool, optional): Whether to load pre-trained weights. If True, returns a model pre-trained on ImageNet. Default: False.
|
||||
scale (float, optional): Scale of channels in each layer. Default: 1.0.
|
||||
**kwargs (optional): Additional keyword arguments. For details, please refer to :ref:`MobileNetV2 <api_paddle_vision_models_MobileNetV2>`.
|
||||
|
||||
Returns:
|
||||
:ref:`api_paddle_nn_Layer`. An instance of MobileNetV2 model.
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
>>> from paddle.vision.models import mobilenet_v2
|
||||
|
||||
>>> # Build model
|
||||
>>> model = mobilenet_v2()
|
||||
|
||||
>>> # Build model and load imagenet pretrained weight
|
||||
>>> # model = mobilenet_v2(pretrained=True)
|
||||
|
||||
>>> # Build mobilenet v2 with scale=0.5
|
||||
>>> model = mobilenet_v2(scale=0.5)
|
||||
|
||||
>>> x = paddle.rand([1, 3, 224, 224])
|
||||
>>> out = model(x)
|
||||
|
||||
>>> print(out.shape)
|
||||
paddle.Size([1, 1000])
|
||||
"""
|
||||
model = _mobilenet(
|
||||
'mobilenetv2_' + str(scale), pretrained, scale=scale, **kwargs
|
||||
)
|
||||
return model
|
||||
@@ -0,0 +1,547 @@
|
||||
# Copyright (c) 2022 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 annotations
|
||||
|
||||
from functools import partial
|
||||
from typing import (
|
||||
TYPE_CHECKING,
|
||||
TypedDict,
|
||||
)
|
||||
|
||||
from typing_extensions import NotRequired, Unpack
|
||||
|
||||
import paddle
|
||||
from paddle import nn
|
||||
from paddle.utils.download import get_weights_path_from_url
|
||||
|
||||
from ..ops import ConvNormActivation
|
||||
from ._utils import _make_divisible
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from collections.abc import Callable
|
||||
|
||||
from paddle import Tensor
|
||||
|
||||
class _MobileNetV3Options(TypedDict):
|
||||
num_classes: NotRequired[int]
|
||||
with_pool: NotRequired[bool]
|
||||
|
||||
|
||||
__all__ = []
|
||||
|
||||
model_urls = {
|
||||
"mobilenet_v3_small_x1.0": (
|
||||
"https://paddle-hapi.bj.bcebos.com/models/mobilenet_v3_small_x1.0.pdparams",
|
||||
"34fe0e7c1f8b00b2b056ad6788d0590c",
|
||||
),
|
||||
"mobilenet_v3_large_x1.0": (
|
||||
"https://paddle-hapi.bj.bcebos.com/models/mobilenet_v3_large_x1.0.pdparams",
|
||||
"118db5792b4e183b925d8e8e334db3df",
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
class SqueezeExcitation(nn.Layer):
|
||||
"""
|
||||
This block implements the Squeeze-and-Excitation block from https://arxiv.org/abs/1709.01507 (see Fig. 1).
|
||||
Parameters ``activation``, and ``scale_activation`` correspond to ``delta`` and ``sigma`` in eq. 3.
|
||||
This code is based on the torchvision code with modifications.
|
||||
You can also see at https://github.com/pytorch/vision/blob/main/torchvision/ops/misc.py#L127
|
||||
|
||||
Args:
|
||||
input_channels (int): Number of channels in the input image.
|
||||
squeeze_channels (int): Number of squeeze channels.
|
||||
activation (Callable[..., paddle.nn.Layer], optional): ``delta`` activation. Default: ``paddle.nn.ReLU``.
|
||||
scale_activation (Callable[..., paddle.nn.Layer]): ``sigma`` activation. Default: ``paddle.nn.Sigmoid``.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
input_channels: int,
|
||||
squeeze_channels: int,
|
||||
activation: Callable[..., nn.Layer] = nn.ReLU,
|
||||
scale_activation: Callable[..., nn.Layer] = nn.Sigmoid,
|
||||
) -> None:
|
||||
super().__init__()
|
||||
self.avgpool = nn.AdaptiveAvgPool2D(1)
|
||||
self.fc1 = nn.Conv2D(input_channels, squeeze_channels, 1)
|
||||
self.fc2 = nn.Conv2D(squeeze_channels, input_channels, 1)
|
||||
self.activation = activation()
|
||||
self.scale_activation = scale_activation()
|
||||
|
||||
def _scale(self, input: Tensor) -> Tensor:
|
||||
scale = self.avgpool(input)
|
||||
scale = self.fc1(scale)
|
||||
scale = self.activation(scale)
|
||||
scale = self.fc2(scale)
|
||||
return self.scale_activation(scale)
|
||||
|
||||
def forward(self, input: Tensor) -> Tensor:
|
||||
scale = self._scale(input)
|
||||
return scale * input
|
||||
|
||||
|
||||
class InvertedResidualConfig:
|
||||
def __init__(
|
||||
self,
|
||||
in_channels: int,
|
||||
kernel: int,
|
||||
expanded_channels: int,
|
||||
out_channels: int,
|
||||
use_se: bool,
|
||||
activation: str,
|
||||
stride: int,
|
||||
scale: float = 1.0,
|
||||
):
|
||||
self.in_channels = self.adjust_channels(in_channels, scale=scale)
|
||||
self.kernel = kernel
|
||||
self.expanded_channels = self.adjust_channels(
|
||||
expanded_channels, scale=scale
|
||||
)
|
||||
self.out_channels = self.adjust_channels(out_channels, scale=scale)
|
||||
self.use_se = use_se
|
||||
if activation is None:
|
||||
self.activation_layer = None
|
||||
elif activation == "relu":
|
||||
self.activation_layer = nn.ReLU
|
||||
elif activation == "hardswish":
|
||||
self.activation_layer = nn.Hardswish
|
||||
else:
|
||||
raise RuntimeError(
|
||||
f"The activation function is not supported: {activation}"
|
||||
)
|
||||
self.stride = stride
|
||||
|
||||
@staticmethod
|
||||
def adjust_channels(channels, scale=1.0):
|
||||
return _make_divisible(channels * scale, 8)
|
||||
|
||||
|
||||
class InvertedResidual(nn.Layer):
|
||||
def __init__(
|
||||
self,
|
||||
in_channels: int,
|
||||
expanded_channels: int,
|
||||
out_channels: int,
|
||||
filter_size: int,
|
||||
stride: int,
|
||||
use_se: bool,
|
||||
activation_layer: Callable[..., nn.Layer],
|
||||
norm_layer: Callable[..., nn.Layer],
|
||||
) -> None:
|
||||
super().__init__()
|
||||
self.use_res_connect = stride == 1 and in_channels == out_channels
|
||||
self.use_se = use_se
|
||||
self.expand = in_channels != expanded_channels
|
||||
|
||||
if self.expand:
|
||||
self.expand_conv = ConvNormActivation(
|
||||
in_channels=in_channels,
|
||||
out_channels=expanded_channels,
|
||||
kernel_size=1,
|
||||
stride=1,
|
||||
padding=0,
|
||||
norm_layer=norm_layer,
|
||||
activation_layer=activation_layer,
|
||||
)
|
||||
|
||||
self.bottleneck_conv = ConvNormActivation(
|
||||
in_channels=expanded_channels,
|
||||
out_channels=expanded_channels,
|
||||
kernel_size=filter_size,
|
||||
stride=stride,
|
||||
padding=int((filter_size - 1) // 2),
|
||||
groups=expanded_channels,
|
||||
norm_layer=norm_layer,
|
||||
activation_layer=activation_layer,
|
||||
)
|
||||
|
||||
if self.use_se:
|
||||
self.mid_se = SqueezeExcitation(
|
||||
expanded_channels,
|
||||
_make_divisible(expanded_channels // 4),
|
||||
scale_activation=nn.Hardsigmoid,
|
||||
)
|
||||
|
||||
self.linear_conv = ConvNormActivation(
|
||||
in_channels=expanded_channels,
|
||||
out_channels=out_channels,
|
||||
kernel_size=1,
|
||||
stride=1,
|
||||
padding=0,
|
||||
norm_layer=norm_layer,
|
||||
activation_layer=None,
|
||||
)
|
||||
|
||||
def forward(self, x: Tensor) -> Tensor:
|
||||
identity = x
|
||||
if self.expand:
|
||||
x = self.expand_conv(x)
|
||||
x = self.bottleneck_conv(x)
|
||||
if self.use_se:
|
||||
x = self.mid_se(x)
|
||||
x = self.linear_conv(x)
|
||||
if self.use_res_connect:
|
||||
x = paddle.add(identity, x)
|
||||
return x
|
||||
|
||||
|
||||
class MobileNetV3(nn.Layer):
|
||||
"""MobileNetV3 model from
|
||||
`"Searching for MobileNetV3" <https://arxiv.org/abs/1905.02244>`_.
|
||||
|
||||
Args:
|
||||
config (list[InvertedResidualConfig]): MobileNetV3 depthwise blocks config.
|
||||
last_channel (int): The number of channels on the penultimate layer.
|
||||
scale (float, optional): Scale of channels in each layer. Default: 1.0.
|
||||
num_classes (int, optional): Output dim of last fc layer. If num_classes <=0, last fc layer
|
||||
will not be defined. Default: 1000.
|
||||
with_pool (bool, optional): Use pool before the last fc layer or not. Default: True.
|
||||
"""
|
||||
|
||||
scale: float
|
||||
num_classes: int
|
||||
with_pool: bool
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
config: list[InvertedResidualConfig],
|
||||
last_channel: int,
|
||||
scale: float = 1.0,
|
||||
num_classes: int = 1000,
|
||||
with_pool: bool = True,
|
||||
) -> None:
|
||||
super().__init__()
|
||||
|
||||
self.config = config
|
||||
self.scale = scale
|
||||
self.last_channel = last_channel
|
||||
self.num_classes = num_classes
|
||||
self.with_pool = with_pool
|
||||
self.firstconv_in_channels = config[0].in_channels
|
||||
self.lastconv_in_channels = config[-1].in_channels
|
||||
self.lastconv_out_channels = self.lastconv_in_channels * 6
|
||||
norm_layer = partial(nn.BatchNorm2D, epsilon=0.001, momentum=0.99)
|
||||
|
||||
self.conv = ConvNormActivation(
|
||||
in_channels=3,
|
||||
out_channels=self.firstconv_in_channels,
|
||||
kernel_size=3,
|
||||
stride=2,
|
||||
padding=1,
|
||||
groups=1,
|
||||
activation_layer=nn.Hardswish,
|
||||
norm_layer=norm_layer,
|
||||
)
|
||||
|
||||
self.blocks = nn.Sequential(
|
||||
*[
|
||||
InvertedResidual(
|
||||
in_channels=cfg.in_channels,
|
||||
expanded_channels=cfg.expanded_channels,
|
||||
out_channels=cfg.out_channels,
|
||||
filter_size=cfg.kernel,
|
||||
stride=cfg.stride,
|
||||
use_se=cfg.use_se,
|
||||
activation_layer=cfg.activation_layer,
|
||||
norm_layer=norm_layer,
|
||||
)
|
||||
for cfg in self.config
|
||||
]
|
||||
)
|
||||
|
||||
self.lastconv = ConvNormActivation(
|
||||
in_channels=self.lastconv_in_channels,
|
||||
out_channels=self.lastconv_out_channels,
|
||||
kernel_size=1,
|
||||
stride=1,
|
||||
padding=0,
|
||||
groups=1,
|
||||
norm_layer=norm_layer,
|
||||
activation_layer=nn.Hardswish,
|
||||
)
|
||||
|
||||
if with_pool:
|
||||
self.avgpool = nn.AdaptiveAvgPool2D(1)
|
||||
|
||||
if num_classes > 0:
|
||||
self.classifier = nn.Sequential(
|
||||
nn.Linear(self.lastconv_out_channels, self.last_channel),
|
||||
nn.Hardswish(),
|
||||
nn.Dropout(p=0.2),
|
||||
nn.Linear(self.last_channel, num_classes),
|
||||
)
|
||||
|
||||
def forward(self, x: Tensor) -> Tensor:
|
||||
x = self.conv(x)
|
||||
x = self.blocks(x)
|
||||
x = self.lastconv(x)
|
||||
|
||||
if self.with_pool:
|
||||
x = self.avgpool(x)
|
||||
|
||||
if self.num_classes > 0:
|
||||
x = paddle.flatten(x, 1)
|
||||
x = self.classifier(x)
|
||||
|
||||
return x
|
||||
|
||||
|
||||
class MobileNetV3Small(MobileNetV3):
|
||||
"""MobileNetV3 Small architecture model from
|
||||
`"Searching for MobileNetV3" <https://arxiv.org/abs/1905.02244>`_.
|
||||
|
||||
Args:
|
||||
scale (float, optional): Scale of channels in each layer. Default: 1.0.
|
||||
num_classes (int, optional): Output dim of last fc layer. If num_classes <= 0, last fc layer
|
||||
will not be defined. Default: 1000.
|
||||
with_pool (bool, optional): Use pool before the last fc layer or not. Default: True.
|
||||
|
||||
Returns:
|
||||
:ref:`api_paddle_nn_Layer`. An instance of MobileNetV3 Small architecture model.
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
>>> from paddle.vision.models import MobileNetV3Small
|
||||
|
||||
>>> # Build model
|
||||
>>> model = MobileNetV3Small(scale=1.0)
|
||||
|
||||
>>> x = paddle.rand([1, 3, 224, 224])
|
||||
>>> out = model(x)
|
||||
|
||||
>>> print(out.shape)
|
||||
paddle.Size([1, 1000])
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
scale: float = 1.0,
|
||||
num_classes: int = 1000,
|
||||
with_pool: bool = True,
|
||||
) -> None:
|
||||
config = [
|
||||
InvertedResidualConfig(16, 3, 16, 16, True, "relu", 2, scale),
|
||||
InvertedResidualConfig(16, 3, 72, 24, False, "relu", 2, scale),
|
||||
InvertedResidualConfig(24, 3, 88, 24, False, "relu", 1, scale),
|
||||
InvertedResidualConfig(24, 5, 96, 40, True, "hardswish", 2, scale),
|
||||
InvertedResidualConfig(40, 5, 240, 40, True, "hardswish", 1, scale),
|
||||
InvertedResidualConfig(40, 5, 240, 40, True, "hardswish", 1, scale),
|
||||
InvertedResidualConfig(40, 5, 120, 48, True, "hardswish", 1, scale),
|
||||
InvertedResidualConfig(48, 5, 144, 48, True, "hardswish", 1, scale),
|
||||
InvertedResidualConfig(48, 5, 288, 96, True, "hardswish", 2, scale),
|
||||
InvertedResidualConfig(96, 5, 576, 96, True, "hardswish", 1, scale),
|
||||
InvertedResidualConfig(96, 5, 576, 96, True, "hardswish", 1, scale),
|
||||
]
|
||||
last_channel = _make_divisible(1024 * scale, 8)
|
||||
super().__init__(
|
||||
config,
|
||||
last_channel=last_channel,
|
||||
scale=scale,
|
||||
with_pool=with_pool,
|
||||
num_classes=num_classes,
|
||||
)
|
||||
|
||||
|
||||
class MobileNetV3Large(MobileNetV3):
|
||||
"""MobileNetV3 Large architecture model from
|
||||
`"Searching for MobileNetV3" <https://arxiv.org/abs/1905.02244>`_.
|
||||
|
||||
Args:
|
||||
scale (float, optional): Scale of channels in each layer. Default: 1.0.
|
||||
num_classes (int, optional): Output dim of last fc layer. If num_classes <= 0, last fc layer
|
||||
will not be defined. Default: 1000.
|
||||
with_pool (bool, optional): Use pool before the last fc layer or not. Default: True.
|
||||
|
||||
Returns:
|
||||
:ref:`api_paddle_nn_Layer`. An instance of MobileNetV3 Large architecture model.
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
>>> from paddle.vision.models import MobileNetV3Large
|
||||
|
||||
>>> # Build model
|
||||
>>> model = MobileNetV3Large(scale=1.0)
|
||||
|
||||
>>> x = paddle.rand([1, 3, 224, 224])
|
||||
>>> out = model(x)
|
||||
|
||||
>>> print(out.shape)
|
||||
paddle.Size([1, 1000])
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
scale: float = 1.0,
|
||||
num_classes: int = 1000,
|
||||
with_pool: bool = True,
|
||||
) -> None:
|
||||
config = [
|
||||
InvertedResidualConfig(16, 3, 16, 16, False, "relu", 1, scale),
|
||||
InvertedResidualConfig(16, 3, 64, 24, False, "relu", 2, scale),
|
||||
InvertedResidualConfig(24, 3, 72, 24, False, "relu", 1, scale),
|
||||
InvertedResidualConfig(24, 5, 72, 40, True, "relu", 2, scale),
|
||||
InvertedResidualConfig(40, 5, 120, 40, True, "relu", 1, scale),
|
||||
InvertedResidualConfig(40, 5, 120, 40, True, "relu", 1, scale),
|
||||
InvertedResidualConfig(
|
||||
40, 3, 240, 80, False, "hardswish", 2, scale
|
||||
),
|
||||
InvertedResidualConfig(
|
||||
80, 3, 200, 80, False, "hardswish", 1, scale
|
||||
),
|
||||
InvertedResidualConfig(
|
||||
80, 3, 184, 80, False, "hardswish", 1, scale
|
||||
),
|
||||
InvertedResidualConfig(
|
||||
80, 3, 184, 80, False, "hardswish", 1, scale
|
||||
),
|
||||
InvertedResidualConfig(
|
||||
80, 3, 480, 112, True, "hardswish", 1, scale
|
||||
),
|
||||
InvertedResidualConfig(
|
||||
112, 3, 672, 112, True, "hardswish", 1, scale
|
||||
),
|
||||
InvertedResidualConfig(
|
||||
112, 5, 672, 160, True, "hardswish", 2, scale
|
||||
),
|
||||
InvertedResidualConfig(
|
||||
160, 5, 960, 160, True, "hardswish", 1, scale
|
||||
),
|
||||
InvertedResidualConfig(
|
||||
160, 5, 960, 160, True, "hardswish", 1, scale
|
||||
),
|
||||
]
|
||||
last_channel = _make_divisible(1280 * scale, 8)
|
||||
super().__init__(
|
||||
config,
|
||||
last_channel=last_channel,
|
||||
scale=scale,
|
||||
with_pool=with_pool,
|
||||
num_classes=num_classes,
|
||||
)
|
||||
|
||||
|
||||
def _mobilenet_v3(
|
||||
arch: str,
|
||||
pretrained: bool = False,
|
||||
scale: float = 1.0,
|
||||
**kwargs: Unpack[_MobileNetV3Options],
|
||||
) -> MobileNetV3:
|
||||
if arch == "mobilenet_v3_large":
|
||||
model = MobileNetV3Large(scale=scale, **kwargs)
|
||||
else:
|
||||
model = MobileNetV3Small(scale=scale, **kwargs)
|
||||
if pretrained:
|
||||
arch = f"{arch}_x{scale}"
|
||||
assert arch in model_urls, (
|
||||
f"{arch} model do not have a pretrained model now, you should set pretrained=False"
|
||||
)
|
||||
weight_path = get_weights_path_from_url(
|
||||
model_urls[arch][0], model_urls[arch][1]
|
||||
)
|
||||
|
||||
param = paddle.load(weight_path)
|
||||
model.set_dict(param)
|
||||
return model
|
||||
|
||||
|
||||
def mobilenet_v3_small(
|
||||
pretrained: bool = False,
|
||||
scale: float = 1.0,
|
||||
**kwargs: Unpack[_MobileNetV3Options],
|
||||
) -> MobileNetV3Small:
|
||||
"""MobileNetV3 Small architecture model from
|
||||
`"Searching for MobileNetV3" <https://arxiv.org/abs/1905.02244>`_.
|
||||
|
||||
Args:
|
||||
pretrained (bool, optional): Whether to load pre-trained weights. If True, returns a model pre-trained on ImageNet. Default: False.
|
||||
scale (float, optional): Scale of channels in each layer. Default: 1.0.
|
||||
**kwargs (optional): Additional keyword arguments. For details, please refer to :ref:`MobileNetV3Small <api_paddle_vision_models_MobileNetV3Small>`.
|
||||
|
||||
Returns:
|
||||
:ref:`api_paddle_nn_Layer`. An instance of MobileNetV3 Small architecture model.
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
>>> from paddle.vision.models import mobilenet_v3_small
|
||||
|
||||
>>> # Build model
|
||||
>>> model = mobilenet_v3_small()
|
||||
|
||||
>>> # Build model and load imagenet pretrained weight
|
||||
>>> # model = mobilenet_v3_small(pretrained=True)
|
||||
|
||||
>>> # Build mobilenet v3 small model with scale=0.5
|
||||
>>> model = mobilenet_v3_small(scale=0.5)
|
||||
|
||||
>>> x = paddle.rand([1, 3, 224, 224])
|
||||
>>> out = model(x)
|
||||
|
||||
>>> print(out.shape)
|
||||
paddle.Size([1, 1000])
|
||||
"""
|
||||
model = _mobilenet_v3(
|
||||
"mobilenet_v3_small", scale=scale, pretrained=pretrained, **kwargs
|
||||
)
|
||||
return model
|
||||
|
||||
|
||||
def mobilenet_v3_large(
|
||||
pretrained: bool = False,
|
||||
scale: float = 1.0,
|
||||
**kwargs: Unpack[_MobileNetV3Options],
|
||||
) -> MobileNetV3Large:
|
||||
"""MobileNetV3 Large architecture model from
|
||||
`"Searching for MobileNetV3" <https://arxiv.org/abs/1905.02244>`_.
|
||||
|
||||
Args:
|
||||
pretrained (bool, optional): Whether to load pre-trained weights. If True, returns a model pre-trained on ImageNet. Default: False.
|
||||
scale (float, optional): Scale of channels in each layer. Default: 1.0.
|
||||
**kwargs (optional): Additional keyword arguments. For details, please refer to :ref:`MobileNetV3Large <api_paddle_vision_models_MobileNetV3Large>`.
|
||||
|
||||
Returns:
|
||||
:ref:`api_paddle_nn_Layer`. An instance of MobileNetV3 Large architecture model.
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
>>> from paddle.vision.models import mobilenet_v3_large
|
||||
|
||||
>>> # Build model
|
||||
>>> model = mobilenet_v3_large()
|
||||
|
||||
>>> # Build model and load imagenet pretrained weight
|
||||
>>> # model = mobilenet_v3_large(pretrained=True)
|
||||
|
||||
>>> # Build mobilenet v3 large model with scale=0.5
|
||||
>>> model = mobilenet_v3_large(scale=0.5)
|
||||
|
||||
>>> x = paddle.rand([1, 3, 224, 224])
|
||||
>>> out = model(x)
|
||||
|
||||
>>> print(out.shape)
|
||||
paddle.Size([1, 1000])
|
||||
"""
|
||||
model = _mobilenet_v3(
|
||||
"mobilenet_v3_large", scale=scale, pretrained=pretrained, **kwargs
|
||||
)
|
||||
return model
|
||||
@@ -0,0 +1,898 @@
|
||||
# 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.
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import paddle
|
||||
from paddle import nn
|
||||
from paddle.utils.download import get_weights_path_from_url
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from collections.abc import Callable
|
||||
from typing import Literal, TypedDict
|
||||
|
||||
from typing_extensions import NotRequired, Unpack
|
||||
|
||||
from paddle import Tensor
|
||||
from paddle._typing import Size2
|
||||
|
||||
_ResNetArch = Literal[
|
||||
'resnet18',
|
||||
'resnet34',
|
||||
'resnet50',
|
||||
'resnet101',
|
||||
'resnet152',
|
||||
'resnext50_32x4d',
|
||||
'resnext50_64x4d',
|
||||
'resnext101_32x4d',
|
||||
'resnext101_64x4d',
|
||||
'resnext152_32x4d',
|
||||
'resnext152_64x4d',
|
||||
'wide_resnet50_2',
|
||||
'wide_resnet101_2',
|
||||
]
|
||||
|
||||
class _ResNetOptions(TypedDict):
|
||||
width: NotRequired[int]
|
||||
num_classes: NotRequired[int]
|
||||
with_pool: NotRequired[bool]
|
||||
groups: NotRequired[int]
|
||||
|
||||
|
||||
__all__ = []
|
||||
|
||||
model_urls: dict[str, tuple[str, str]] = {
|
||||
'resnet18': (
|
||||
'https://paddle-hapi.bj.bcebos.com/models/resnet18.pdparams',
|
||||
'cf548f46534aa3560945be4b95cd11c4',
|
||||
),
|
||||
'resnet34': (
|
||||
'https://paddle-hapi.bj.bcebos.com/models/resnet34.pdparams',
|
||||
'8d2275cf8706028345f78ac0e1d31969',
|
||||
),
|
||||
'resnet50': (
|
||||
'https://paddle-hapi.bj.bcebos.com/models/resnet50.pdparams',
|
||||
'ca6f485ee1ab0492d38f323885b0ad80',
|
||||
),
|
||||
'resnet101': (
|
||||
'https://paddle-hapi.bj.bcebos.com/models/resnet101.pdparams',
|
||||
'02f35f034ca3858e1e54d4036443c92d',
|
||||
),
|
||||
'resnet152': (
|
||||
'https://paddle-hapi.bj.bcebos.com/models/resnet152.pdparams',
|
||||
'7ad16a2f1e7333859ff986138630fd7a',
|
||||
),
|
||||
'resnext50_32x4d': (
|
||||
'https://paddle-hapi.bj.bcebos.com/models/resnext50_32x4d.pdparams',
|
||||
'dc47483169be7d6f018fcbb7baf8775d',
|
||||
),
|
||||
"resnext50_64x4d": (
|
||||
'https://paddle-hapi.bj.bcebos.com/models/resnext50_64x4d.pdparams',
|
||||
'063d4b483e12b06388529450ad7576db',
|
||||
),
|
||||
'resnext101_32x4d': (
|
||||
'https://paddle-hapi.bj.bcebos.com/models/resnext101_32x4d.pdparams',
|
||||
'967b090039f9de2c8d06fe994fb9095f',
|
||||
),
|
||||
'resnext101_64x4d': (
|
||||
'https://paddle-hapi.bj.bcebos.com/models/resnext101_64x4d.pdparams',
|
||||
'98e04e7ca616a066699230d769d03008',
|
||||
),
|
||||
'resnext152_32x4d': (
|
||||
'https://paddle-hapi.bj.bcebos.com/models/resnext152_32x4d.pdparams',
|
||||
'18ff0beee21f2efc99c4b31786107121',
|
||||
),
|
||||
'resnext152_64x4d': (
|
||||
'https://paddle-hapi.bj.bcebos.com/models/resnext152_64x4d.pdparams',
|
||||
'77c4af00ca42c405fa7f841841959379',
|
||||
),
|
||||
'wide_resnet50_2': (
|
||||
'https://paddle-hapi.bj.bcebos.com/models/wide_resnet50_2.pdparams',
|
||||
'0282f804d73debdab289bd9fea3fa6dc',
|
||||
),
|
||||
'wide_resnet101_2': (
|
||||
'https://paddle-hapi.bj.bcebos.com/models/wide_resnet101_2.pdparams',
|
||||
'd4360a2d23657f059216f5d5a1a9ac93',
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
class BasicBlock(nn.Layer):
|
||||
expansion: int = 1
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
inplanes: int,
|
||||
planes: int,
|
||||
stride: Size2 = 1,
|
||||
downsample: nn.Layer | None = None,
|
||||
groups: int = 1,
|
||||
base_width: int = 64,
|
||||
dilation: int = 1,
|
||||
norm_layer: Callable[..., nn.Layer] | None = None,
|
||||
) -> None:
|
||||
super().__init__()
|
||||
if norm_layer is None:
|
||||
norm_layer: type[nn.BatchNorm2D] = nn.BatchNorm2D
|
||||
|
||||
if dilation > 1:
|
||||
raise NotImplementedError(
|
||||
"Dilation > 1 not supported in BasicBlock"
|
||||
)
|
||||
|
||||
self.conv1 = nn.Conv2D(
|
||||
inplanes, planes, 3, padding=1, stride=stride, bias_attr=False
|
||||
)
|
||||
self.bn1 = norm_layer(planes)
|
||||
self.relu = nn.ReLU()
|
||||
self.conv2 = nn.Conv2D(planes, planes, 3, padding=1, bias_attr=False)
|
||||
self.bn2 = norm_layer(planes)
|
||||
self.downsample = downsample
|
||||
self.stride = stride
|
||||
|
||||
def forward(self, x: Tensor) -> Tensor:
|
||||
identity = x
|
||||
|
||||
out = self.conv1(x)
|
||||
out = self.bn1(out)
|
||||
out = self.relu(out)
|
||||
|
||||
out = self.conv2(out)
|
||||
out = self.bn2(out)
|
||||
|
||||
if self.downsample is not None:
|
||||
identity = self.downsample(x)
|
||||
|
||||
out += identity
|
||||
out = self.relu(out)
|
||||
|
||||
return out
|
||||
|
||||
|
||||
class BottleneckBlock(nn.Layer):
|
||||
expansion: int = 4
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
inplanes: int,
|
||||
planes: int,
|
||||
stride: Size2 = 1,
|
||||
downsample: nn.Layer | None = None,
|
||||
groups: int = 1,
|
||||
base_width: int = 64,
|
||||
dilation: int = 1,
|
||||
norm_layer: Callable[..., nn.Layer] | None = None,
|
||||
) -> None:
|
||||
super().__init__()
|
||||
if norm_layer is None:
|
||||
norm_layer = nn.BatchNorm2D
|
||||
width = int(planes * (base_width / 64.0)) * groups
|
||||
|
||||
self.conv1 = nn.Conv2D(inplanes, width, 1, bias_attr=False)
|
||||
self.bn1 = norm_layer(width)
|
||||
|
||||
self.conv2 = nn.Conv2D(
|
||||
width,
|
||||
width,
|
||||
3,
|
||||
padding=dilation,
|
||||
stride=stride,
|
||||
groups=groups,
|
||||
dilation=dilation,
|
||||
bias_attr=False,
|
||||
)
|
||||
self.bn2 = norm_layer(width)
|
||||
|
||||
self.conv3 = nn.Conv2D(
|
||||
width, planes * self.expansion, 1, bias_attr=False
|
||||
)
|
||||
self.bn3 = norm_layer(planes * self.expansion)
|
||||
self.relu = nn.ReLU()
|
||||
self.downsample = downsample
|
||||
self.stride = stride
|
||||
|
||||
def forward(self, x: Tensor) -> Tensor:
|
||||
identity = x
|
||||
|
||||
out = self.conv1(x)
|
||||
out = self.bn1(out)
|
||||
out = self.relu(out)
|
||||
|
||||
out = self.conv2(out)
|
||||
out = self.bn2(out)
|
||||
out = self.relu(out)
|
||||
|
||||
out = self.conv3(out)
|
||||
out = self.bn3(out)
|
||||
|
||||
if self.downsample is not None:
|
||||
identity = self.downsample(x)
|
||||
|
||||
out += identity
|
||||
out = self.relu(out)
|
||||
|
||||
return out
|
||||
|
||||
|
||||
class ResNet(nn.Layer):
|
||||
"""ResNet model from
|
||||
`"Deep Residual Learning for Image Recognition" <https://arxiv.org/pdf/1512.03385.pdf>`_.
|
||||
|
||||
Args:
|
||||
Block (BasicBlock|BottleneckBlock): Block module of model.
|
||||
depth (int, optional): Layers of ResNet, Default: 50.
|
||||
width (int, optional): Base width per convolution group for each convolution block, Default: 64.
|
||||
num_classes (int, optional): Output dim of last fc layer. If num_classes <= 0, last fc layer
|
||||
will not be defined. Default: 1000.
|
||||
with_pool (bool, optional): Use pool before the last fc layer or not. Default: True.
|
||||
groups (int, optional): Number of groups for each convolution block, Default: 1.
|
||||
|
||||
Returns:
|
||||
:ref:`api_paddle_nn_Layer`. An instance of ResNet model.
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
>>> from paddle.vision.models import ResNet
|
||||
>>> from paddle.vision.models.resnet import (
|
||||
... BottleneckBlock,
|
||||
... BasicBlock,
|
||||
... )
|
||||
|
||||
>>> # build ResNet with 18 layers
|
||||
>>> resnet18 = ResNet(BasicBlock, 18)
|
||||
|
||||
>>> # build ResNet with 50 layers
|
||||
>>> resnet50 = ResNet(BottleneckBlock, 50)
|
||||
|
||||
>>> # build Wide ResNet model
|
||||
>>> wide_resnet50_2 = ResNet(BottleneckBlock, 50, width=64 * 2)
|
||||
|
||||
>>> # build ResNeXt model
|
||||
>>> resnext50_32x4d = ResNet(BottleneckBlock, 50, width=4, groups=32)
|
||||
|
||||
>>> x = paddle.rand([1, 3, 224, 224])
|
||||
>>> out = resnet18(x)
|
||||
|
||||
>>> print(out.shape)
|
||||
paddle.Size([1, 1000])
|
||||
"""
|
||||
|
||||
groups: int
|
||||
base_width: int
|
||||
num_classes: int
|
||||
with_pool: bool
|
||||
inplanes: int
|
||||
dilation: int
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
block: type[BasicBlock | BottleneckBlock],
|
||||
depth: int = 50,
|
||||
width: int = 64,
|
||||
num_classes: int = 1000,
|
||||
with_pool: bool = True,
|
||||
groups: int = 1,
|
||||
) -> None:
|
||||
super().__init__()
|
||||
layer_cfg = {
|
||||
18: [2, 2, 2, 2],
|
||||
34: [3, 4, 6, 3],
|
||||
50: [3, 4, 6, 3],
|
||||
101: [3, 4, 23, 3],
|
||||
152: [3, 8, 36, 3],
|
||||
}
|
||||
layers = layer_cfg[depth]
|
||||
self.groups = groups
|
||||
self.base_width = width
|
||||
self.num_classes = num_classes
|
||||
self.with_pool = with_pool
|
||||
self._norm_layer = nn.BatchNorm2D
|
||||
|
||||
self.inplanes = 64
|
||||
self.dilation = 1
|
||||
|
||||
self.conv1 = nn.Conv2D(
|
||||
3,
|
||||
self.inplanes,
|
||||
kernel_size=7,
|
||||
stride=2,
|
||||
padding=3,
|
||||
bias_attr=False,
|
||||
)
|
||||
self.bn1 = self._norm_layer(self.inplanes)
|
||||
self.relu = nn.ReLU()
|
||||
self.maxpool = nn.MaxPool2D(kernel_size=3, stride=2, padding=1)
|
||||
self.layer1 = self._make_layer(block, 64, layers[0])
|
||||
self.layer2 = self._make_layer(block, 128, layers[1], stride=2)
|
||||
self.layer3 = self._make_layer(block, 256, layers[2], stride=2)
|
||||
self.layer4 = self._make_layer(block, 512, layers[3], stride=2)
|
||||
if with_pool:
|
||||
self.avgpool = nn.AdaptiveAvgPool2D((1, 1))
|
||||
|
||||
if num_classes > 0:
|
||||
self.fc = nn.Linear(512 * block.expansion, num_classes)
|
||||
|
||||
def _make_layer(
|
||||
self,
|
||||
block: type[BasicBlock | BottleneckBlock],
|
||||
planes: int,
|
||||
blocks: int,
|
||||
stride: int = 1,
|
||||
dilate: bool = False,
|
||||
) -> nn.Sequential:
|
||||
norm_layer = self._norm_layer
|
||||
downsample = None
|
||||
previous_dilation = self.dilation
|
||||
if dilate:
|
||||
self.dilation *= stride
|
||||
stride = 1
|
||||
if stride != 1 or self.inplanes != planes * block.expansion:
|
||||
downsample = nn.Sequential(
|
||||
nn.Conv2D(
|
||||
self.inplanes,
|
||||
planes * block.expansion,
|
||||
1,
|
||||
stride=stride,
|
||||
bias_attr=False,
|
||||
),
|
||||
norm_layer(planes * block.expansion),
|
||||
)
|
||||
|
||||
layers = []
|
||||
layers.append(
|
||||
block(
|
||||
self.inplanes,
|
||||
planes,
|
||||
stride,
|
||||
downsample,
|
||||
self.groups,
|
||||
self.base_width,
|
||||
previous_dilation,
|
||||
norm_layer,
|
||||
)
|
||||
)
|
||||
self.inplanes = planes * block.expansion
|
||||
for _ in range(1, blocks):
|
||||
layers.append(
|
||||
block(
|
||||
self.inplanes,
|
||||
planes,
|
||||
groups=self.groups,
|
||||
base_width=self.base_width,
|
||||
norm_layer=norm_layer,
|
||||
)
|
||||
)
|
||||
|
||||
return nn.Sequential(*layers)
|
||||
|
||||
def forward(self, x: Tensor) -> Tensor:
|
||||
x = self.conv1(x)
|
||||
x = self.bn1(x)
|
||||
x = self.relu(x)
|
||||
x = self.maxpool(x)
|
||||
x = self.layer1(x)
|
||||
x = self.layer2(x)
|
||||
x = self.layer3(x)
|
||||
x = self.layer4(x)
|
||||
|
||||
if self.with_pool:
|
||||
x = self.avgpool(x)
|
||||
|
||||
if self.num_classes > 0:
|
||||
x = paddle.flatten(x, 1)
|
||||
x = self.fc(x)
|
||||
|
||||
return x
|
||||
|
||||
|
||||
def _resnet(
|
||||
arch: _ResNetArch,
|
||||
Block: type[BasicBlock | BottleneckBlock],
|
||||
depth: int,
|
||||
pretrained: bool,
|
||||
**kwargs: Unpack[_ResNetOptions],
|
||||
) -> ResNet:
|
||||
model = ResNet(Block, depth, **kwargs)
|
||||
if pretrained:
|
||||
assert arch in model_urls, (
|
||||
f"{arch} model do not have a pretrained model now, you should set pretrained=False"
|
||||
)
|
||||
weight_path = get_weights_path_from_url(
|
||||
model_urls[arch][0], model_urls[arch][1]
|
||||
)
|
||||
|
||||
param = paddle.load(weight_path)
|
||||
model.set_dict(param)
|
||||
|
||||
return model
|
||||
|
||||
|
||||
def resnet18(pretrained=False, **kwargs: Unpack[_ResNetOptions]) -> ResNet:
|
||||
"""ResNet 18-layer model from
|
||||
`"Deep Residual Learning for Image Recognition" <https://arxiv.org/pdf/1512.03385.pdf>`_.
|
||||
|
||||
Args:
|
||||
pretrained (bool, optional): Whether to load pre-trained weights. If True, returns a model pre-trained
|
||||
on ImageNet. Default: False.
|
||||
**kwargs (optional): Additional keyword arguments. For details, please refer to :ref:`ResNet <api_paddle_vision_models_ResNet>`.
|
||||
|
||||
Returns:
|
||||
:ref:`api_paddle_nn_Layer`. An instance of ResNet 18-layer model.
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
>>> from paddle.vision.models import resnet18
|
||||
|
||||
>>> # build model
|
||||
>>> model = resnet18()
|
||||
|
||||
>>> # build model and load imagenet pretrained weight
|
||||
>>> # model = resnet18(pretrained=True)
|
||||
|
||||
>>> x = paddle.rand([1, 3, 224, 224])
|
||||
>>> out = model(x)
|
||||
|
||||
>>> print(out.shape)
|
||||
paddle.Size([1, 1000])
|
||||
"""
|
||||
return _resnet('resnet18', BasicBlock, 18, pretrained, **kwargs)
|
||||
|
||||
|
||||
def resnet34(
|
||||
pretrained: bool = False, **kwargs: Unpack[_ResNetOptions]
|
||||
) -> ResNet:
|
||||
"""ResNet 34-layer model from
|
||||
`"Deep Residual Learning for Image Recognition" <https://arxiv.org/pdf/1512.03385.pdf>`_.
|
||||
|
||||
Args:
|
||||
pretrained (bool, optional): Whether to load pre-trained weights. If True, returns a model pre-trained
|
||||
on ImageNet. Default: False.
|
||||
**kwargs (optional): Additional keyword arguments. For details, please refer to :ref:`ResNet <api_paddle_vision_models_ResNet>`.
|
||||
|
||||
Returns:
|
||||
:ref:`api_paddle_nn_Layer`. An instance of ResNet 34-layer model.
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
>>> from paddle.vision.models import resnet34
|
||||
|
||||
>>> # build model
|
||||
>>> model = resnet34()
|
||||
|
||||
>>> # build model and load imagenet pretrained weight
|
||||
>>> # model = resnet34(pretrained=True)
|
||||
|
||||
>>> x = paddle.rand([1, 3, 224, 224])
|
||||
>>> out = model(x)
|
||||
|
||||
>>> print(out.shape)
|
||||
paddle.Size([1, 1000])
|
||||
"""
|
||||
return _resnet('resnet34', BasicBlock, 34, pretrained, **kwargs)
|
||||
|
||||
|
||||
def resnet50(
|
||||
pretrained: bool = False, **kwargs: Unpack[_ResNetOptions]
|
||||
) -> ResNet:
|
||||
"""ResNet 50-layer model from
|
||||
`"Deep Residual Learning for Image Recognition" <https://arxiv.org/pdf/1512.03385.pdf>`_.
|
||||
|
||||
Args:
|
||||
pretrained (bool, optional): Whether to load pre-trained weights. If True, returns a model pre-trained
|
||||
on ImageNet. Default: False.
|
||||
**kwargs (optional): Additional keyword arguments. For details, please refer to :ref:`ResNet <api_paddle_vision_models_ResNet>`.
|
||||
|
||||
Returns:
|
||||
:ref:`api_paddle_nn_Layer`. An instance of ResNet 50-layer model.
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
>>> from paddle.vision.models import resnet50
|
||||
|
||||
>>> # build model
|
||||
>>> model = resnet50()
|
||||
|
||||
>>> # build model and load imagenet pretrained weight
|
||||
>>> # model = resnet50(pretrained=True)
|
||||
|
||||
>>> x = paddle.rand([1, 3, 224, 224])
|
||||
>>> out = model(x)
|
||||
|
||||
>>> print(out.shape)
|
||||
paddle.Size([1, 1000])
|
||||
"""
|
||||
return _resnet('resnet50', BottleneckBlock, 50, pretrained, **kwargs)
|
||||
|
||||
|
||||
def resnet101(
|
||||
pretrained: bool = False, **kwargs: Unpack[_ResNetOptions]
|
||||
) -> ResNet:
|
||||
"""ResNet 101-layer model from
|
||||
`"Deep Residual Learning for Image Recognition" <https://arxiv.org/pdf/1512.03385.pdf>`_.
|
||||
|
||||
Args:
|
||||
pretrained (bool, optional): Whether to load pre-trained weights. If True, returns a model pre-trained
|
||||
on ImageNet. Default: False.
|
||||
**kwargs (optional): Additional keyword arguments. For details, please refer to :ref:`ResNet <api_paddle_vision_models_ResNet>`.
|
||||
|
||||
Returns:
|
||||
:ref:`api_paddle_nn_Layer`. An instance of ResNet 101-layer.
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
>>> from paddle.vision.models import resnet101
|
||||
|
||||
>>> # build model
|
||||
>>> model = resnet101()
|
||||
|
||||
>>> # build model and load imagenet pretrained weight
|
||||
>>> # model = resnet101(pretrained=True)
|
||||
|
||||
>>> x = paddle.rand([1, 3, 224, 224])
|
||||
>>> out = model(x)
|
||||
|
||||
>>> print(out.shape)
|
||||
paddle.Size([1, 1000])
|
||||
"""
|
||||
return _resnet('resnet101', BottleneckBlock, 101, pretrained, **kwargs)
|
||||
|
||||
|
||||
def resnet152(
|
||||
pretrained: bool = False, **kwargs: Unpack[_ResNetOptions]
|
||||
) -> ResNet:
|
||||
"""ResNet 152-layer model from
|
||||
`"Deep Residual Learning for Image Recognition" <https://arxiv.org/pdf/1512.03385.pdf>`_.
|
||||
|
||||
Args:
|
||||
pretrained (bool, optional): Whether to load pre-trained weights. If True, returns a model pre-trained
|
||||
on ImageNet. Default: False.
|
||||
**kwargs (optional): Additional keyword arguments. For details, please refer to :ref:`ResNet <api_paddle_vision_models_ResNet>`.
|
||||
|
||||
Returns:
|
||||
:ref:`api_paddle_nn_Layer`. An instance of ResNet 152-layer model.
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
>>> from paddle.vision.models import resnet152
|
||||
|
||||
>>> # build model
|
||||
>>> model = resnet152()
|
||||
|
||||
>>> # build model and load imagenet pretrained weight
|
||||
>>> # model = resnet152(pretrained=True)
|
||||
|
||||
>>> x = paddle.rand([1, 3, 224, 224])
|
||||
>>> out = model(x)
|
||||
|
||||
>>> print(out.shape)
|
||||
paddle.Size([1, 1000])
|
||||
"""
|
||||
return _resnet('resnet152', BottleneckBlock, 152, pretrained, **kwargs)
|
||||
|
||||
|
||||
def resnext50_32x4d(
|
||||
pretrained: bool = False, **kwargs: Unpack[_ResNetOptions]
|
||||
) -> ResNet:
|
||||
"""ResNeXt-50 32x4d model from
|
||||
`"Aggregated Residual Transformations for Deep Neural Networks" <https://arxiv.org/pdf/1611.05431.pdf>`_.
|
||||
|
||||
Args:
|
||||
pretrained (bool, optional): Whether to load pre-trained weights. If True, returns a model pre-trained
|
||||
on ImageNet. Default: False.
|
||||
**kwargs (optional): Additional keyword arguments. For details, please refer to :ref:`ResNet <api_paddle_vision_models_ResNet>`.
|
||||
|
||||
Returns:
|
||||
:ref:`api_paddle_nn_Layer`. An instance of ResNeXt-50 32x4d model.
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
>>> from paddle.vision.models import resnext50_32x4d
|
||||
|
||||
>>> # build model
|
||||
>>> model = resnext50_32x4d()
|
||||
|
||||
>>> # build model and load imagenet pretrained weight
|
||||
>>> # model = resnext50_32x4d(pretrained=True)
|
||||
|
||||
>>> x = paddle.rand([1, 3, 224, 224])
|
||||
>>> out = model(x)
|
||||
|
||||
>>> print(out.shape)
|
||||
paddle.Size([1, 1000])
|
||||
"""
|
||||
kwargs['groups'] = 32
|
||||
kwargs['width'] = 4
|
||||
return _resnet('resnext50_32x4d', BottleneckBlock, 50, pretrained, **kwargs)
|
||||
|
||||
|
||||
def resnext50_64x4d(
|
||||
pretrained: bool = False, **kwargs: Unpack[_ResNetOptions]
|
||||
) -> ResNet:
|
||||
"""ResNeXt-50 64x4d model from
|
||||
`"Aggregated Residual Transformations for Deep Neural Networks" <https://arxiv.org/pdf/1611.05431.pdf>`_.
|
||||
|
||||
Args:
|
||||
pretrained (bool, optional): Whether to load pre-trained weights. If True, returns a model pre-trained
|
||||
on ImageNet. Default: False.
|
||||
**kwargs (optional): Additional keyword arguments. For details, please refer to :ref:`ResNet <api_paddle_vision_models_ResNet>`.
|
||||
|
||||
Returns:
|
||||
:ref:`api_paddle_nn_Layer`. An instance of ResNeXt-50 64x4d model.
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
>>> from paddle.vision.models import resnext50_64x4d
|
||||
|
||||
>>> # build model
|
||||
>>> model = resnext50_64x4d()
|
||||
|
||||
>>> # build model and load imagenet pretrained weight
|
||||
>>> # model = resnext50_64x4d(pretrained=True)
|
||||
|
||||
>>> x = paddle.rand([1, 3, 224, 224])
|
||||
>>> out = model(x)
|
||||
|
||||
>>> print(out.shape)
|
||||
paddle.Size([1, 1000])
|
||||
"""
|
||||
kwargs['groups'] = 64
|
||||
kwargs['width'] = 4
|
||||
return _resnet('resnext50_64x4d', BottleneckBlock, 50, pretrained, **kwargs)
|
||||
|
||||
|
||||
def resnext101_32x4d(
|
||||
pretrained: bool = False, **kwargs: Unpack[_ResNetOptions]
|
||||
) -> ResNet:
|
||||
"""ResNeXt-101 32x4d model from
|
||||
`"Aggregated Residual Transformations for Deep Neural Networks" <https://arxiv.org/pdf/1611.05431.pdf>`_.
|
||||
|
||||
Args:
|
||||
pretrained (bool, optional): Whether to load pre-trained weights. If True, returns a model pre-trained
|
||||
on ImageNet. Default: False.
|
||||
**kwargs (optional): Additional keyword arguments. For details, please refer to :ref:`ResNet <api_paddle_vision_models_ResNet>`.
|
||||
|
||||
Returns:
|
||||
:ref:`api_paddle_nn_Layer`. An instance of ResNeXt-101 32x4d model.
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
>>> from paddle.vision.models import resnext101_32x4d
|
||||
|
||||
>>> # build model
|
||||
>>> model = resnext101_32x4d()
|
||||
|
||||
>>> # build model and load imagenet pretrained weight
|
||||
>>> # model = resnext101_32x4d(pretrained=True)
|
||||
|
||||
>>> x = paddle.rand([1, 3, 224, 224])
|
||||
>>> out = model(x)
|
||||
|
||||
>>> print(out.shape)
|
||||
paddle.Size([1, 1000])
|
||||
"""
|
||||
kwargs['groups'] = 32
|
||||
kwargs['width'] = 4
|
||||
return _resnet(
|
||||
'resnext101_32x4d', BottleneckBlock, 101, pretrained, **kwargs
|
||||
)
|
||||
|
||||
|
||||
def resnext101_64x4d(
|
||||
pretrained: bool = False, **kwargs: Unpack[_ResNetOptions]
|
||||
) -> ResNet:
|
||||
"""ResNeXt-101 64x4d model from
|
||||
`"Aggregated Residual Transformations for Deep Neural Networks" <https://arxiv.org/pdf/1611.05431.pdf>`_.
|
||||
|
||||
Args:
|
||||
pretrained (bool, optional): Whether to load pre-trained weights. If True, returns a model pre-trained
|
||||
on ImageNet. Default: False.
|
||||
**kwargs (optional): Additional keyword arguments. For details, please refer to :ref:`ResNet <api_paddle_vision_models_ResNet>`.
|
||||
|
||||
Returns:
|
||||
:ref:`api_paddle_nn_Layer`. An instance of ResNeXt-101 64x4d model.
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
>>> from paddle.vision.models import resnext101_64x4d
|
||||
|
||||
>>> # build model
|
||||
>>> model = resnext101_64x4d()
|
||||
|
||||
>>> # build model and load imagenet pretrained weight
|
||||
>>> # model = resnext101_64x4d(pretrained=True)
|
||||
|
||||
>>> x = paddle.rand([1, 3, 224, 224])
|
||||
>>> out = model(x)
|
||||
|
||||
>>> print(out.shape)
|
||||
paddle.Size([1, 1000])
|
||||
"""
|
||||
kwargs['groups'] = 64
|
||||
kwargs['width'] = 4
|
||||
return _resnet(
|
||||
'resnext101_64x4d', BottleneckBlock, 101, pretrained, **kwargs
|
||||
)
|
||||
|
||||
|
||||
def resnext152_32x4d(
|
||||
pretrained: bool = False, **kwargs: Unpack[_ResNetOptions]
|
||||
) -> ResNet:
|
||||
"""ResNeXt-152 32x4d model from
|
||||
`"Aggregated Residual Transformations for Deep Neural Networks" <https://arxiv.org/pdf/1611.05431.pdf>`_.
|
||||
|
||||
Args:
|
||||
pretrained (bool, optional): Whether to load pre-trained weights. If True, returns a model pre-trained
|
||||
on ImageNet. Default: False.
|
||||
**kwargs (optional): Additional keyword arguments. For details, please refer to :ref:`ResNet <api_paddle_vision_models_ResNet>`.
|
||||
|
||||
Returns:
|
||||
:ref:`api_paddle_nn_Layer`. An instance of ResNeXt-152 32x4d model.
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
>>> from paddle.vision.models import resnext152_32x4d
|
||||
|
||||
>>> # build model
|
||||
>>> model = resnext152_32x4d()
|
||||
|
||||
>>> # build model and load imagenet pretrained weight
|
||||
>>> # model = resnext152_32x4d(pretrained=True)
|
||||
|
||||
>>> x = paddle.rand([1, 3, 224, 224])
|
||||
>>> out = model(x)
|
||||
|
||||
>>> print(out.shape)
|
||||
paddle.Size([1, 1000])
|
||||
"""
|
||||
kwargs['groups'] = 32
|
||||
kwargs['width'] = 4
|
||||
return _resnet(
|
||||
'resnext152_32x4d', BottleneckBlock, 152, pretrained, **kwargs
|
||||
)
|
||||
|
||||
|
||||
def resnext152_64x4d(
|
||||
pretrained: bool = False, **kwargs: Unpack[_ResNetOptions]
|
||||
) -> ResNet:
|
||||
"""ResNeXt-152 64x4d model from
|
||||
`"Aggregated Residual Transformations for Deep Neural Networks" <https://arxiv.org/pdf/1611.05431.pdf>`_.
|
||||
|
||||
Args:
|
||||
pretrained (bool, optional): Whether to load pre-trained weights. If True, returns a model pre-trained
|
||||
on ImageNet. Default: False.
|
||||
**kwargs (optional): Additional keyword arguments. For details, please refer to :ref:`ResNet <api_paddle_vision_models_ResNet>`.
|
||||
|
||||
Returns:
|
||||
:ref:`api_paddle_nn_Layer`. An instance of ResNeXt-152 64x4d model.
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
>>> from paddle.vision.models import resnext152_64x4d
|
||||
|
||||
>>> # build model
|
||||
>>> model = resnext152_64x4d()
|
||||
|
||||
>>> # build model and load imagenet pretrained weight
|
||||
>>> # model = resnext152_64x4d(pretrained=True)
|
||||
|
||||
>>> x = paddle.rand([1, 3, 224, 224])
|
||||
>>> out = model(x)
|
||||
|
||||
>>> print(out.shape)
|
||||
paddle.Size([1, 1000])
|
||||
"""
|
||||
kwargs['groups'] = 64
|
||||
kwargs['width'] = 4
|
||||
return _resnet(
|
||||
'resnext152_64x4d', BottleneckBlock, 152, pretrained, **kwargs
|
||||
)
|
||||
|
||||
|
||||
def wide_resnet50_2(
|
||||
pretrained: bool = False, **kwargs: Unpack[_ResNetOptions]
|
||||
) -> ResNet:
|
||||
"""Wide ResNet-50-2 model from
|
||||
`"Wide Residual Networks" <https://arxiv.org/pdf/1605.07146.pdf>`_.
|
||||
|
||||
Args:
|
||||
pretrained (bool, optional): Whether to load pre-trained weights. If True, returns a model pre-trained
|
||||
on ImageNet. Default: False.
|
||||
**kwargs (optional): Additional keyword arguments. For details, please refer to :ref:`ResNet <api_paddle_vision_models_ResNet>`.
|
||||
|
||||
Returns:
|
||||
:ref:`api_paddle_nn_Layer`. An instance of Wide ResNet-50-2 model.
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
>>> from paddle.vision.models import wide_resnet50_2
|
||||
|
||||
>>> # build model
|
||||
>>> model = wide_resnet50_2()
|
||||
|
||||
>>> # build model and load imagenet pretrained weight
|
||||
>>> # model = wide_resnet50_2(pretrained=True)
|
||||
|
||||
>>> x = paddle.rand([1, 3, 224, 224])
|
||||
>>> out = model(x)
|
||||
|
||||
>>> print(out.shape)
|
||||
paddle.Size([1, 1000])
|
||||
"""
|
||||
kwargs['width'] = 64 * 2
|
||||
return _resnet('wide_resnet50_2', BottleneckBlock, 50, pretrained, **kwargs)
|
||||
|
||||
|
||||
def wide_resnet101_2(
|
||||
pretrained: bool = False, **kwargs: Unpack[_ResNetOptions]
|
||||
) -> ResNet:
|
||||
"""Wide ResNet-101-2 model from
|
||||
`"Wide Residual Networks" <https://arxiv.org/pdf/1605.07146.pdf>`_.
|
||||
|
||||
Args:
|
||||
pretrained (bool, optional): Whether to load pre-trained weights. If True, returns a model pre-trained
|
||||
on ImageNet. Default: False.
|
||||
**kwargs (optional): Additional keyword arguments. For details, please refer to :ref:`ResNet <api_paddle_vision_models_ResNet>`.
|
||||
|
||||
Returns:
|
||||
:ref:`api_paddle_nn_Layer`. An instance of Wide ResNet-101-2 model.
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
>>> from paddle.vision.models import wide_resnet101_2
|
||||
|
||||
>>> # build model
|
||||
>>> model = wide_resnet101_2()
|
||||
|
||||
>>> # build model and load imagenet pretrained weight
|
||||
>>> # model = wide_resnet101_2(pretrained=True)
|
||||
|
||||
>>> x = paddle.rand([1, 3, 224, 224])
|
||||
>>> out = model(x)
|
||||
|
||||
>>> print(out.shape)
|
||||
paddle.Size([1, 1000])
|
||||
"""
|
||||
kwargs['width'] = 64 * 2
|
||||
return _resnet(
|
||||
'wide_resnet101_2', BottleneckBlock, 101, pretrained, **kwargs
|
||||
)
|
||||
@@ -0,0 +1,649 @@
|
||||
# Copyright (c) 2021 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 annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import paddle
|
||||
from paddle import nn
|
||||
from paddle.nn import AdaptiveAvgPool2D, Linear, MaxPool2D
|
||||
from paddle.utils.download import get_weights_path_from_url
|
||||
|
||||
from ..ops import ConvNormActivation
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from collections.abc import Callable
|
||||
from typing import Literal, TypedDict
|
||||
|
||||
from typing_extensions import NotRequired, Unpack
|
||||
|
||||
from paddle import Tensor
|
||||
from paddle._typing import Size2
|
||||
|
||||
_ShuffleNetArch = Literal[
|
||||
'shufflenet_v2_x0_25',
|
||||
'shufflenet_v2_x0_33',
|
||||
'shufflenet_v2_x0_5',
|
||||
'shufflenet_v2_x1_0',
|
||||
'shufflenet_v2_x1_5',
|
||||
'shufflenet_v2_x2_0',
|
||||
'shufflenet_v2_swish',
|
||||
]
|
||||
|
||||
_ActivationType = Literal['relu', 'swish']
|
||||
|
||||
class _ShuffleNetOptions(TypedDict):
|
||||
act: NotRequired[_ActivationType | None]
|
||||
with_pool: NotRequired[bool]
|
||||
num_classes: NotRequired[int]
|
||||
|
||||
class _ShuffleNetSwishOptions(TypedDict):
|
||||
with_pool: NotRequired[bool]
|
||||
num_classes: NotRequired[int]
|
||||
|
||||
|
||||
__all__ = []
|
||||
|
||||
model_urls: dict[str, tuple[str, str]] = {
|
||||
"shufflenet_v2_x0_25": (
|
||||
"https://paddle-hapi.bj.bcebos.com/models/shufflenet_v2_x0_25.pdparams",
|
||||
"1e509b4c140eeb096bb16e214796d03b",
|
||||
),
|
||||
"shufflenet_v2_x0_33": (
|
||||
"https://paddle-hapi.bj.bcebos.com/models/shufflenet_v2_x0_33.pdparams",
|
||||
"3d7b3ab0eaa5c0927ff1026d31b729bd",
|
||||
),
|
||||
"shufflenet_v2_x0_5": (
|
||||
"https://paddle-hapi.bj.bcebos.com/models/shufflenet_v2_x0_5.pdparams",
|
||||
"5e5cee182a7793c4e4c73949b1a71bd4",
|
||||
),
|
||||
"shufflenet_v2_x1_0": (
|
||||
"https://paddle-hapi.bj.bcebos.com/models/shufflenet_v2_x1_0.pdparams",
|
||||
"122d42478b9e81eb49f8a9ede327b1a4",
|
||||
),
|
||||
"shufflenet_v2_x1_5": (
|
||||
"https://paddle-hapi.bj.bcebos.com/models/shufflenet_v2_x1_5.pdparams",
|
||||
"faced5827380d73531d0ee027c67826d",
|
||||
),
|
||||
"shufflenet_v2_x2_0": (
|
||||
"https://paddle-hapi.bj.bcebos.com/models/shufflenet_v2_x2_0.pdparams",
|
||||
"cd3dddcd8305e7bcd8ad14d1c69a5784",
|
||||
),
|
||||
"shufflenet_v2_swish": (
|
||||
"https://paddle-hapi.bj.bcebos.com/models/shufflenet_v2_swish.pdparams",
|
||||
"adde0aa3b023e5b0c94a68be1c394b84",
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
def create_activation_layer(act: _ActivationType | None) -> nn.Layer | None:
|
||||
if act == "swish":
|
||||
return nn.Swish
|
||||
elif act == "relu":
|
||||
return nn.ReLU
|
||||
elif act is None:
|
||||
return None
|
||||
else:
|
||||
raise RuntimeError(f"The activation function is not supported: {act}")
|
||||
|
||||
|
||||
def channel_shuffle(x: Tensor, groups: int) -> Tensor:
|
||||
batch_size, num_channels, height, width = x.shape[0:4]
|
||||
channels_per_group = num_channels // groups
|
||||
|
||||
# reshape
|
||||
x = paddle.reshape(
|
||||
x, shape=[batch_size, groups, channels_per_group, height, width]
|
||||
)
|
||||
|
||||
# transpose
|
||||
x = paddle.transpose(x, perm=[0, 2, 1, 3, 4])
|
||||
|
||||
# flatten
|
||||
x = paddle.reshape(x, shape=[batch_size, num_channels, height, width])
|
||||
return x
|
||||
|
||||
|
||||
class InvertedResidual(nn.Layer):
|
||||
def __init__(
|
||||
self,
|
||||
in_channels: int,
|
||||
out_channels: int,
|
||||
stride: Size2,
|
||||
activation_layer: Callable[..., nn.Layer] = nn.ReLU,
|
||||
) -> None:
|
||||
super().__init__()
|
||||
self._conv_pw = ConvNormActivation(
|
||||
in_channels=in_channels // 2,
|
||||
out_channels=out_channels // 2,
|
||||
kernel_size=1,
|
||||
stride=1,
|
||||
padding=0,
|
||||
groups=1,
|
||||
activation_layer=activation_layer,
|
||||
)
|
||||
self._conv_dw = ConvNormActivation(
|
||||
in_channels=out_channels // 2,
|
||||
out_channels=out_channels // 2,
|
||||
kernel_size=3,
|
||||
stride=stride,
|
||||
padding=1,
|
||||
groups=out_channels // 2,
|
||||
activation_layer=None,
|
||||
)
|
||||
self._conv_linear = ConvNormActivation(
|
||||
in_channels=out_channels // 2,
|
||||
out_channels=out_channels // 2,
|
||||
kernel_size=1,
|
||||
stride=1,
|
||||
padding=0,
|
||||
groups=1,
|
||||
activation_layer=activation_layer,
|
||||
)
|
||||
|
||||
def forward(self, inputs: Tensor) -> Tensor:
|
||||
x1, x2 = paddle.split(
|
||||
inputs,
|
||||
num_or_sections=[inputs.shape[1] // 2, inputs.shape[1] // 2],
|
||||
axis=1,
|
||||
)
|
||||
x2 = self._conv_pw(x2)
|
||||
x2 = self._conv_dw(x2)
|
||||
x2 = self._conv_linear(x2)
|
||||
out = paddle.concat([x1, x2], axis=1)
|
||||
return channel_shuffle(out, 2)
|
||||
|
||||
|
||||
class InvertedResidualDS(nn.Layer):
|
||||
def __init__(
|
||||
self,
|
||||
in_channels: int,
|
||||
out_channels: int,
|
||||
stride: Size2,
|
||||
activation_layer: Callable[..., nn.Layer] = nn.ReLU,
|
||||
) -> None:
|
||||
super().__init__()
|
||||
|
||||
# branch1
|
||||
self._conv_dw_1 = ConvNormActivation(
|
||||
in_channels=in_channels,
|
||||
out_channels=in_channels,
|
||||
kernel_size=3,
|
||||
stride=stride,
|
||||
padding=1,
|
||||
groups=in_channels,
|
||||
activation_layer=None,
|
||||
)
|
||||
self._conv_linear_1 = ConvNormActivation(
|
||||
in_channels=in_channels,
|
||||
out_channels=out_channels // 2,
|
||||
kernel_size=1,
|
||||
stride=1,
|
||||
padding=0,
|
||||
groups=1,
|
||||
activation_layer=activation_layer,
|
||||
)
|
||||
# branch2
|
||||
self._conv_pw_2 = ConvNormActivation(
|
||||
in_channels=in_channels,
|
||||
out_channels=out_channels // 2,
|
||||
kernel_size=1,
|
||||
stride=1,
|
||||
padding=0,
|
||||
groups=1,
|
||||
activation_layer=activation_layer,
|
||||
)
|
||||
self._conv_dw_2 = ConvNormActivation(
|
||||
in_channels=out_channels // 2,
|
||||
out_channels=out_channels // 2,
|
||||
kernel_size=3,
|
||||
stride=stride,
|
||||
padding=1,
|
||||
groups=out_channels // 2,
|
||||
activation_layer=None,
|
||||
)
|
||||
self._conv_linear_2 = ConvNormActivation(
|
||||
in_channels=out_channels // 2,
|
||||
out_channels=out_channels // 2,
|
||||
kernel_size=1,
|
||||
stride=1,
|
||||
padding=0,
|
||||
groups=1,
|
||||
activation_layer=activation_layer,
|
||||
)
|
||||
|
||||
def forward(self, inputs: Tensor) -> Tensor:
|
||||
x1 = self._conv_dw_1(inputs)
|
||||
x1 = self._conv_linear_1(x1)
|
||||
x2 = self._conv_pw_2(inputs)
|
||||
x2 = self._conv_dw_2(x2)
|
||||
x2 = self._conv_linear_2(x2)
|
||||
out = paddle.concat([x1, x2], axis=1)
|
||||
|
||||
return channel_shuffle(out, 2)
|
||||
|
||||
|
||||
class ShuffleNetV2(nn.Layer):
|
||||
"""ShuffleNetV2 model from
|
||||
`"ShuffleNet V2: Practical Guidelines for Efficient CNN Architecture Design" <https://arxiv.org/pdf/1807.11164.pdf>`_.
|
||||
|
||||
Args:
|
||||
scale (float, optional): Scale of output channels. Default: True.
|
||||
act (str, optional): Activation function of neural network. Default: "relu".
|
||||
num_classes (int, optional): Output dim of last fc layer. If num_classes <= 0, last fc layer
|
||||
will not be defined. Default: 1000.
|
||||
with_pool (bool, optional): Use pool before the last fc layer or not. Default: True.
|
||||
|
||||
Returns:
|
||||
:ref:`api_paddle_nn_Layer`. An instance of ShuffleNetV2 model.
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
>>> from paddle.vision.models import ShuffleNetV2
|
||||
|
||||
>>> shufflenet_v2_swish = ShuffleNetV2(scale=1.0, act="swish")
|
||||
>>> x = paddle.rand([1, 3, 224, 224])
|
||||
>>> out = shufflenet_v2_swish(x)
|
||||
>>> print(out.shape)
|
||||
paddle.Size([1, 1000])
|
||||
"""
|
||||
|
||||
scale: float
|
||||
num_classes: int
|
||||
with_pool: bool
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
scale: float = 1.0,
|
||||
act: _ActivationType | None = "relu",
|
||||
num_classes: int = 1000,
|
||||
with_pool: bool = True,
|
||||
) -> None:
|
||||
super().__init__()
|
||||
self.scale = scale
|
||||
self.num_classes = num_classes
|
||||
self.with_pool = with_pool
|
||||
stage_repeats = [4, 8, 4]
|
||||
activation_layer = create_activation_layer(act)
|
||||
|
||||
if scale == 0.25:
|
||||
stage_out_channels = [-1, 24, 24, 48, 96, 512]
|
||||
elif scale == 0.33:
|
||||
stage_out_channels = [-1, 24, 32, 64, 128, 512]
|
||||
elif scale == 0.5:
|
||||
stage_out_channels = [-1, 24, 48, 96, 192, 1024]
|
||||
elif scale == 1.0:
|
||||
stage_out_channels = [-1, 24, 116, 232, 464, 1024]
|
||||
elif scale == 1.5:
|
||||
stage_out_channels = [-1, 24, 176, 352, 704, 1024]
|
||||
elif scale == 2.0:
|
||||
stage_out_channels = [-1, 24, 224, 488, 976, 2048]
|
||||
else:
|
||||
raise NotImplementedError(
|
||||
"This scale size:[" + str(scale) + "] is not implemented!"
|
||||
)
|
||||
# 1. conv1
|
||||
self._conv1 = ConvNormActivation(
|
||||
in_channels=3,
|
||||
out_channels=stage_out_channels[1],
|
||||
kernel_size=3,
|
||||
stride=2,
|
||||
padding=1,
|
||||
activation_layer=activation_layer,
|
||||
)
|
||||
self._max_pool = MaxPool2D(kernel_size=3, stride=2, padding=1)
|
||||
|
||||
# 2. bottleneck sequences
|
||||
self._block_list = []
|
||||
for stage_id, num_repeat in enumerate(stage_repeats):
|
||||
for i in range(num_repeat):
|
||||
if i == 0:
|
||||
block = self.add_sublayer(
|
||||
sublayer=InvertedResidualDS(
|
||||
in_channels=stage_out_channels[stage_id + 1],
|
||||
out_channels=stage_out_channels[stage_id + 2],
|
||||
stride=2,
|
||||
activation_layer=activation_layer,
|
||||
),
|
||||
name=str(stage_id + 2) + "_" + str(i + 1),
|
||||
)
|
||||
else:
|
||||
block = self.add_sublayer(
|
||||
sublayer=InvertedResidual(
|
||||
in_channels=stage_out_channels[stage_id + 2],
|
||||
out_channels=stage_out_channels[stage_id + 2],
|
||||
stride=1,
|
||||
activation_layer=activation_layer,
|
||||
),
|
||||
name=str(stage_id + 2) + "_" + str(i + 1),
|
||||
)
|
||||
self._block_list.append(block)
|
||||
# 3. last_conv
|
||||
self._last_conv = ConvNormActivation(
|
||||
in_channels=stage_out_channels[-2],
|
||||
out_channels=stage_out_channels[-1],
|
||||
kernel_size=1,
|
||||
stride=1,
|
||||
padding=0,
|
||||
activation_layer=activation_layer,
|
||||
)
|
||||
# 4. pool
|
||||
if with_pool:
|
||||
self._pool2d_avg = AdaptiveAvgPool2D(1)
|
||||
|
||||
# 5. fc
|
||||
if num_classes > 0:
|
||||
self._out_c = stage_out_channels[-1]
|
||||
self._fc = Linear(stage_out_channels[-1], num_classes)
|
||||
|
||||
def forward(self, inputs: Tensor) -> Tensor:
|
||||
x = self._conv1(inputs)
|
||||
x = self._max_pool(x)
|
||||
for inv in self._block_list:
|
||||
x = inv(x)
|
||||
x = self._last_conv(x)
|
||||
|
||||
if self.with_pool:
|
||||
x = self._pool2d_avg(x)
|
||||
|
||||
if self.num_classes > 0:
|
||||
x = paddle.flatten(x, start_axis=1, stop_axis=-1)
|
||||
x = self._fc(x)
|
||||
return x
|
||||
|
||||
|
||||
def _shufflenet_v2(
|
||||
arch: _ShuffleNetArch,
|
||||
pretrained: bool = False,
|
||||
scale: float = 1.0,
|
||||
**kwargs: Unpack[_ShuffleNetOptions],
|
||||
) -> ShuffleNetV2:
|
||||
model = ShuffleNetV2(scale=scale, **kwargs)
|
||||
if pretrained:
|
||||
assert arch in model_urls, (
|
||||
f"{arch} model do not have a pretrained model now, you should set pretrained=False"
|
||||
)
|
||||
weight_path = get_weights_path_from_url(
|
||||
model_urls[arch][0], model_urls[arch][1]
|
||||
)
|
||||
|
||||
param = paddle.load(weight_path)
|
||||
model.set_dict(param)
|
||||
return model
|
||||
|
||||
|
||||
def shufflenet_v2_x0_25(
|
||||
pretrained: bool = False, **kwargs: Unpack[_ShuffleNetOptions]
|
||||
) -> ShuffleNetV2:
|
||||
"""ShuffleNetV2 with 0.25x output channels, as described in
|
||||
`"ShuffleNet V2: Practical Guidelines for Efficient CNN Architecture Design" <https://arxiv.org/pdf/1807.11164.pdf>`_.
|
||||
|
||||
Args:
|
||||
pretrained (bool, optional): Whether to load pre-trained weights. If True, returns a model pre-trained
|
||||
on ImageNet. Default: False.
|
||||
**kwargs (optional): Additional keyword arguments. For details, please refer to :ref:`ShuffleNetV2 <api_paddle_vision_models_ShuffleNetV2>`.
|
||||
|
||||
Returns:
|
||||
:ref:`api_paddle_nn_Layer`. An instance of ShuffleNetV2 with 0.25x output channels.
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
>>> from paddle.vision.models import shufflenet_v2_x0_25
|
||||
|
||||
>>> # build model
|
||||
>>> model = shufflenet_v2_x0_25()
|
||||
|
||||
>>> # build model and load imagenet pretrained weight
|
||||
>>> # model = shufflenet_v2_x0_25(pretrained=True)
|
||||
|
||||
>>> x = paddle.rand([1, 3, 224, 224])
|
||||
>>> out = model(x)
|
||||
|
||||
>>> print(out.shape)
|
||||
paddle.Size([1, 1000])
|
||||
"""
|
||||
return _shufflenet_v2(
|
||||
"shufflenet_v2_x0_25", scale=0.25, pretrained=pretrained, **kwargs
|
||||
)
|
||||
|
||||
|
||||
def shufflenet_v2_x0_33(
|
||||
pretrained: bool = False, **kwargs: Unpack[_ShuffleNetOptions]
|
||||
) -> ShuffleNetV2:
|
||||
"""ShuffleNetV2 with 0.33x output channels, as described in
|
||||
`"ShuffleNet V2: Practical Guidelines for Efficient CNN Architecture Design" <https://arxiv.org/pdf/1807.11164.pdf>`_.
|
||||
|
||||
Args:
|
||||
pretrained (bool, optional): Whether to load pre-trained weights. If True, returns a model pre-trained
|
||||
on ImageNet. Default: False.
|
||||
**kwargs (optional): Additional keyword arguments. For details, please refer to :ref:`ShuffleNetV2 <api_paddle_vision_models_ShuffleNetV2>`.
|
||||
|
||||
Returns:
|
||||
:ref:`api_paddle_nn_Layer`. An instance of ShuffleNetV2 with 0.33x output channels.
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
>>> from paddle.vision.models import shufflenet_v2_x0_33
|
||||
|
||||
>>> # build model
|
||||
>>> model = shufflenet_v2_x0_33()
|
||||
|
||||
>>> # build model and load imagenet pretrained weight
|
||||
>>> # model = shufflenet_v2_x0_33(pretrained=True)
|
||||
|
||||
>>> x = paddle.rand([1, 3, 224, 224])
|
||||
>>> out = model(x)
|
||||
|
||||
>>> print(out.shape)
|
||||
paddle.Size([1, 1000])
|
||||
"""
|
||||
return _shufflenet_v2(
|
||||
"shufflenet_v2_x0_33", scale=0.33, pretrained=pretrained, **kwargs
|
||||
)
|
||||
|
||||
|
||||
def shufflenet_v2_x0_5(
|
||||
pretrained: bool = False, **kwargs: Unpack[_ShuffleNetOptions]
|
||||
) -> ShuffleNetV2:
|
||||
"""ShuffleNetV2 with 0.5x output channels, as described in
|
||||
`"ShuffleNet V2: Practical Guidelines for Efficient CNN Architecture Design" <https://arxiv.org/pdf/1807.11164.pdf>`_.
|
||||
|
||||
Args:
|
||||
pretrained (bool, optional): Whether to load pre-trained weights. If True, returns a model pre-trained
|
||||
on ImageNet. Default: False.
|
||||
**kwargs (optional): Additional keyword arguments. For details, please refer to :ref:`ShuffleNetV2 <api_paddle_vision_models_ShuffleNetV2>`.
|
||||
|
||||
Returns:
|
||||
:ref:`api_paddle_nn_Layer`. An instance of ShuffleNetV2 with 0.5x output channels.
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
>>> from paddle.vision.models import shufflenet_v2_x0_5
|
||||
|
||||
>>> # build model
|
||||
>>> model = shufflenet_v2_x0_5()
|
||||
|
||||
>>> # build model and load imagenet pretrained weight
|
||||
>>> # model = shufflenet_v2_x0_5(pretrained=True)
|
||||
|
||||
>>> x = paddle.rand([1, 3, 224, 224])
|
||||
>>> out = model(x)
|
||||
|
||||
>>> print(out.shape)
|
||||
paddle.Size([1, 1000])
|
||||
"""
|
||||
return _shufflenet_v2(
|
||||
"shufflenet_v2_x0_5", scale=0.5, pretrained=pretrained, **kwargs
|
||||
)
|
||||
|
||||
|
||||
def shufflenet_v2_x1_0(
|
||||
pretrained: bool = False, **kwargs: Unpack[_ShuffleNetOptions]
|
||||
) -> ShuffleNetV2:
|
||||
"""ShuffleNetV2 with 1.0x output channels, as described in
|
||||
`"ShuffleNet V2: Practical Guidelines for Efficient CNN Architecture Design" <https://arxiv.org/pdf/1807.11164.pdf>`_.
|
||||
|
||||
Args:
|
||||
pretrained (bool, optional): Whether to load pre-trained weights. If True, returns a model pre-trained
|
||||
on ImageNet. Default: False.
|
||||
**kwargs (optional): Additional keyword arguments. For details, please refer to :ref:`ShuffleNetV2 <api_paddle_vision_models_ShuffleNetV2>`.
|
||||
|
||||
Returns:
|
||||
:ref:`api_paddle_nn_Layer`. An instance of ShuffleNetV2 with 1.0x output channels.
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
>>> from paddle.vision.models import shufflenet_v2_x1_0
|
||||
|
||||
>>> # build model
|
||||
>>> model = shufflenet_v2_x1_0()
|
||||
|
||||
>>> # build model and load imagenet pretrained weight
|
||||
>>> # model = shufflenet_v2_x1_0(pretrained=True)
|
||||
|
||||
>>> x = paddle.rand([1, 3, 224, 224])
|
||||
>>> out = model(x)
|
||||
|
||||
>>> print(out.shape)
|
||||
paddle.Size([1, 1000])
|
||||
"""
|
||||
return _shufflenet_v2(
|
||||
"shufflenet_v2_x1_0", scale=1.0, pretrained=pretrained, **kwargs
|
||||
)
|
||||
|
||||
|
||||
def shufflenet_v2_x1_5(
|
||||
pretrained: bool = False, **kwargs: Unpack[_ShuffleNetOptions]
|
||||
) -> ShuffleNetV2:
|
||||
"""ShuffleNetV2 with 1.5x output channels, as described in
|
||||
`"ShuffleNet V2: Practical Guidelines for Efficient CNN Architecture Design" <https://arxiv.org/pdf/1807.11164.pdf>`_.
|
||||
|
||||
Args:
|
||||
pretrained (bool, optional): Whether to load pre-trained weights. If True, returns a model pre-trained
|
||||
on ImageNet. Default: False.
|
||||
**kwargs (optional): Additional keyword arguments. For details, please refer to :ref:`ShuffleNetV2 <api_paddle_vision_models_ShuffleNetV2>`.
|
||||
|
||||
Returns:
|
||||
:ref:`api_paddle_nn_Layer`. An instance of ShuffleNetV2 with 1.5x output channels.
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
>>> from paddle.vision.models import shufflenet_v2_x1_5
|
||||
|
||||
>>> # build model
|
||||
>>> model = shufflenet_v2_x1_5()
|
||||
|
||||
>>> # build model and load imagenet pretrained weight
|
||||
>>> # model = shufflenet_v2_x1_5(pretrained=True)
|
||||
|
||||
>>> x = paddle.rand([1, 3, 224, 224])
|
||||
>>> out = model(x)
|
||||
|
||||
>>> print(out.shape)
|
||||
paddle.Size([1, 1000])
|
||||
"""
|
||||
return _shufflenet_v2(
|
||||
"shufflenet_v2_x1_5", scale=1.5, pretrained=pretrained, **kwargs
|
||||
)
|
||||
|
||||
|
||||
def shufflenet_v2_x2_0(
|
||||
pretrained: bool = False, **kwargs: Unpack[_ShuffleNetOptions]
|
||||
) -> ShuffleNetV2:
|
||||
"""ShuffleNetV2 with 2.0x output channels, as described in
|
||||
`"ShuffleNet V2: Practical Guidelines for Efficient CNN Architecture Design" <https://arxiv.org/pdf/1807.11164.pdf>`_.
|
||||
|
||||
Args:
|
||||
pretrained (bool, optional): Whether to load pre-trained weights. If True, returns a model pre-trained
|
||||
on ImageNet. Default: False.
|
||||
**kwargs (optional): Additional keyword arguments. For details, please refer to :ref:`ShuffleNetV2 <api_paddle_vision_models_ShuffleNetV2>`.
|
||||
|
||||
Returns:
|
||||
:ref:`api_paddle_nn_Layer`. An instance of ShuffleNetV2 with 2.0x output channels.
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
>>> from paddle.vision.models import shufflenet_v2_x2_0
|
||||
|
||||
>>> # build model
|
||||
>>> model = shufflenet_v2_x2_0()
|
||||
|
||||
>>> # build model and load imagenet pretrained weight
|
||||
>>> # model = shufflenet_v2_x2_0(pretrained=True)
|
||||
|
||||
>>> x = paddle.rand([1, 3, 224, 224])
|
||||
>>> out = model(x)
|
||||
|
||||
>>> print(out.shape)
|
||||
paddle.Size([1, 1000])
|
||||
"""
|
||||
return _shufflenet_v2(
|
||||
"shufflenet_v2_x2_0", scale=2.0, pretrained=pretrained, **kwargs
|
||||
)
|
||||
|
||||
|
||||
def shufflenet_v2_swish(
|
||||
pretrained: bool = False, **kwargs: Unpack[_ShuffleNetSwishOptions]
|
||||
) -> ShuffleNetV2:
|
||||
"""ShuffleNetV2 with swish activation function, as described in
|
||||
`"ShuffleNet V2: Practical Guidelines for Efficient CNN Architecture Design" <https://arxiv.org/pdf/1807.11164.pdf>`_.
|
||||
|
||||
Args:
|
||||
pretrained (bool, optional): Whether to load pre-trained weights. If True, returns a model pre-trained
|
||||
on ImageNet. Default: False.
|
||||
**kwargs (optional): Additional keyword arguments. For details, please refer to :ref:`ShuffleNetV2 <api_paddle_vision_models_ShuffleNetV2>`.
|
||||
|
||||
Returns:
|
||||
:ref:`api_paddle_nn_Layer`. An instance of ShuffleNetV2 with swish activation function.
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
>>> from paddle.vision.models import shufflenet_v2_swish
|
||||
|
||||
>>> # build model
|
||||
>>> model = shufflenet_v2_swish()
|
||||
|
||||
>>> # build model and load imagenet pretrained weight
|
||||
>>> # model = shufflenet_v2_swish(pretrained=True)
|
||||
|
||||
>>> x = paddle.rand([1, 3, 224, 224])
|
||||
>>> out = model(x)
|
||||
|
||||
>>> print(out.shape)
|
||||
paddle.Size([1, 1000])
|
||||
"""
|
||||
return _shufflenet_v2(
|
||||
"shufflenet_v2_swish",
|
||||
scale=1.0,
|
||||
act="swish",
|
||||
pretrained=pretrained,
|
||||
**kwargs,
|
||||
)
|
||||
@@ -0,0 +1,320 @@
|
||||
# copyright (c) 2021 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.
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import (
|
||||
TYPE_CHECKING,
|
||||
TypedDict,
|
||||
)
|
||||
|
||||
from typing_extensions import NotRequired, Unpack
|
||||
|
||||
import paddle
|
||||
import paddle.nn.functional as F
|
||||
from paddle import nn
|
||||
from paddle.base.param_attr import ParamAttr
|
||||
from paddle.nn import AdaptiveAvgPool2D, Conv2D, Dropout, MaxPool2D
|
||||
from paddle.utils.download import get_weights_path_from_url
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from paddle import Tensor
|
||||
from paddle._typing import Size2
|
||||
|
||||
class _SqueezeNetOptions(TypedDict):
|
||||
num_classes: NotRequired[int]
|
||||
with_pool: NotRequired[bool]
|
||||
|
||||
|
||||
__all__ = []
|
||||
|
||||
model_urls = {
|
||||
'squeezenet1_0': (
|
||||
'https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/SqueezeNet1_0_pretrained.pdparams',
|
||||
'30b95af60a2178f03cf9b66cd77e1db1',
|
||||
),
|
||||
'squeezenet1_1': (
|
||||
'https://paddle-imagenet-models-name.bj.bcebos.com/dygraph/SqueezeNet1_1_pretrained.pdparams',
|
||||
'a11250d3a1f91d7131fd095ebbf09eee',
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
class MakeFireConv(nn.Layer):
|
||||
def __init__(
|
||||
self,
|
||||
input_channels: int,
|
||||
output_channels: int,
|
||||
filter_size: Size2,
|
||||
padding: Size2 = 0,
|
||||
) -> None:
|
||||
super().__init__()
|
||||
self._conv = Conv2D(
|
||||
input_channels,
|
||||
output_channels,
|
||||
filter_size,
|
||||
padding=padding,
|
||||
weight_attr=ParamAttr(),
|
||||
bias_attr=ParamAttr(),
|
||||
)
|
||||
|
||||
def forward(self, x: Tensor) -> Tensor:
|
||||
x = self._conv(x)
|
||||
x = F.relu(x)
|
||||
return x
|
||||
|
||||
|
||||
class MakeFire(nn.Layer):
|
||||
def __init__(
|
||||
self,
|
||||
input_channels: int,
|
||||
squeeze_channels: int,
|
||||
expand1x1_channels: int,
|
||||
expand3x3_channels: int,
|
||||
) -> None:
|
||||
super().__init__()
|
||||
self._conv = MakeFireConv(input_channels, squeeze_channels, 1)
|
||||
self._conv_path1 = MakeFireConv(squeeze_channels, expand1x1_channels, 1)
|
||||
self._conv_path2 = MakeFireConv(
|
||||
squeeze_channels, expand3x3_channels, 3, padding=1
|
||||
)
|
||||
|
||||
def forward(self, inputs: Tensor) -> Tensor:
|
||||
x = self._conv(inputs)
|
||||
x1 = self._conv_path1(x)
|
||||
x2 = self._conv_path2(x)
|
||||
return paddle.concat([x1, x2], axis=1)
|
||||
|
||||
|
||||
class SqueezeNet(nn.Layer):
|
||||
"""SqueezeNet model from
|
||||
`"SqueezeNet: AlexNet-level accuracy with 50x fewer parameters and <0.5MB model size"
|
||||
<https://arxiv.org/pdf/1602.07360.pdf>`_.
|
||||
|
||||
Args:
|
||||
version (str): Version of SqueezeNet, which can be "1.0" or "1.1".
|
||||
num_classes (int, optional): Output dim of last fc layer. If num_classes <= 0, last fc layer
|
||||
will not be defined. Default: 1000.
|
||||
with_pool (bool, optional): Use pool before the last fc layer or not. Default: True.
|
||||
|
||||
Returns:
|
||||
:ref:`api_paddle_nn_Layer`. An instance of SqueezeNet model.
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
>>> from paddle.vision.models import SqueezeNet
|
||||
|
||||
>>> # build v1.0 model
|
||||
>>> model = SqueezeNet(version='1.0')
|
||||
|
||||
>>> # build v1.1 model
|
||||
>>> # model = SqueezeNet(version='1.1')
|
||||
|
||||
>>> x = paddle.rand([1, 3, 224, 224])
|
||||
>>> out = model(x)
|
||||
|
||||
>>> print(out.shape)
|
||||
paddle.Size([1, 1000])
|
||||
"""
|
||||
|
||||
version: str
|
||||
num_classes: int
|
||||
with_pool: bool
|
||||
|
||||
def __init__(
|
||||
self, version: str, num_classes: int = 1000, with_pool: bool = True
|
||||
) -> None:
|
||||
super().__init__()
|
||||
self.version = version
|
||||
self.num_classes = num_classes
|
||||
self.with_pool = with_pool
|
||||
|
||||
supported_versions = ['1.0', '1.1']
|
||||
assert version in supported_versions, (
|
||||
f"supported versions are {supported_versions} but input version is {version}"
|
||||
)
|
||||
|
||||
if self.version == "1.0":
|
||||
self._conv = Conv2D(
|
||||
3,
|
||||
96,
|
||||
7,
|
||||
stride=2,
|
||||
weight_attr=ParamAttr(),
|
||||
bias_attr=ParamAttr(),
|
||||
)
|
||||
self._pool = MaxPool2D(kernel_size=3, stride=2, padding=0)
|
||||
self._conv1 = MakeFire(96, 16, 64, 64)
|
||||
self._conv2 = MakeFire(128, 16, 64, 64)
|
||||
self._conv3 = MakeFire(128, 32, 128, 128)
|
||||
self._conv4 = MakeFire(256, 32, 128, 128)
|
||||
self._conv5 = MakeFire(256, 48, 192, 192)
|
||||
self._conv6 = MakeFire(384, 48, 192, 192)
|
||||
self._conv7 = MakeFire(384, 64, 256, 256)
|
||||
self._conv8 = MakeFire(512, 64, 256, 256)
|
||||
else:
|
||||
self._conv = Conv2D(
|
||||
3,
|
||||
64,
|
||||
3,
|
||||
stride=2,
|
||||
padding=1,
|
||||
weight_attr=ParamAttr(),
|
||||
bias_attr=ParamAttr(),
|
||||
)
|
||||
self._pool = MaxPool2D(kernel_size=3, stride=2, padding=0)
|
||||
self._conv1 = MakeFire(64, 16, 64, 64)
|
||||
self._conv2 = MakeFire(128, 16, 64, 64)
|
||||
self._conv3 = MakeFire(128, 32, 128, 128)
|
||||
self._conv4 = MakeFire(256, 32, 128, 128)
|
||||
self._conv5 = MakeFire(256, 48, 192, 192)
|
||||
self._conv6 = MakeFire(384, 48, 192, 192)
|
||||
self._conv7 = MakeFire(384, 64, 256, 256)
|
||||
self._conv8 = MakeFire(512, 64, 256, 256)
|
||||
|
||||
self._drop = Dropout(p=0.5, mode="downscale_in_infer")
|
||||
self._conv9 = Conv2D(
|
||||
512, num_classes, 1, weight_attr=ParamAttr(), bias_attr=ParamAttr()
|
||||
)
|
||||
self._avg_pool = AdaptiveAvgPool2D(1)
|
||||
|
||||
def forward(self, inputs: Tensor) -> Tensor:
|
||||
x = self._conv(inputs)
|
||||
x = F.relu(x)
|
||||
x = self._pool(x)
|
||||
if self.version == "1.0":
|
||||
x = self._conv1(x)
|
||||
x = self._conv2(x)
|
||||
x = self._conv3(x)
|
||||
x = self._pool(x)
|
||||
x = self._conv4(x)
|
||||
x = self._conv5(x)
|
||||
x = self._conv6(x)
|
||||
x = self._conv7(x)
|
||||
x = self._pool(x)
|
||||
x = self._conv8(x)
|
||||
else:
|
||||
x = self._conv1(x)
|
||||
x = self._conv2(x)
|
||||
x = self._pool(x)
|
||||
x = self._conv3(x)
|
||||
x = self._conv4(x)
|
||||
x = self._pool(x)
|
||||
x = self._conv5(x)
|
||||
x = self._conv6(x)
|
||||
x = self._conv7(x)
|
||||
x = self._conv8(x)
|
||||
if self.num_classes > 0:
|
||||
x = self._drop(x)
|
||||
x = self._conv9(x)
|
||||
if self.with_pool:
|
||||
x = F.relu(x)
|
||||
x = self._avg_pool(x)
|
||||
x = paddle.squeeze(x, axis=[2, 3])
|
||||
|
||||
return x
|
||||
|
||||
|
||||
def _squeezenet(
|
||||
arch: str,
|
||||
version: str,
|
||||
pretrained: bool,
|
||||
**kwargs: Unpack[_SqueezeNetOptions],
|
||||
) -> SqueezeNet:
|
||||
model = SqueezeNet(version, **kwargs)
|
||||
if pretrained:
|
||||
assert arch in model_urls, (
|
||||
f"{arch} model do not have a pretrained model now, you should set pretrained=False"
|
||||
)
|
||||
weight_path = get_weights_path_from_url(
|
||||
model_urls[arch][0], model_urls[arch][1]
|
||||
)
|
||||
param = paddle.load(weight_path)
|
||||
model.set_dict(param)
|
||||
|
||||
return model
|
||||
|
||||
|
||||
def squeezenet1_0(
|
||||
pretrained: bool = False, **kwargs: Unpack[_SqueezeNetOptions]
|
||||
) -> SqueezeNet:
|
||||
"""SqueezeNet v1.0 model from
|
||||
`"SqueezeNet: AlexNet-level accuracy with 50x fewer parameters and <0.5MB model size"
|
||||
<https://arxiv.org/pdf/1602.07360.pdf>`_.
|
||||
|
||||
Args:
|
||||
pretrained (bool, optional): Whether to load pre-trained weights. If True, returns a model pre-trained
|
||||
on ImageNet. Default: False.
|
||||
**kwargs (optional): Additional keyword arguments. For details, please refer to :ref:`SqueezeNet <api_paddle_vision_models_SqueezeNet>`.
|
||||
|
||||
Returns:
|
||||
:ref:`api_paddle_nn_Layer`. An instance of SqueezeNet v1.0 model.
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
>>> from paddle.vision.models import squeezenet1_0
|
||||
|
||||
>>> # build model
|
||||
>>> model = squeezenet1_0()
|
||||
|
||||
>>> # build model and load imagenet pretrained weight
|
||||
>>> # model = squeezenet1_0(pretrained=True)
|
||||
|
||||
>>> x = paddle.rand([1, 3, 224, 224])
|
||||
>>> out = model(x)
|
||||
|
||||
>>> print(out.shape)
|
||||
paddle.Size([1, 1000])
|
||||
"""
|
||||
return _squeezenet('squeezenet1_0', '1.0', pretrained, **kwargs)
|
||||
|
||||
|
||||
def squeezenet1_1(
|
||||
pretrained: bool = False, **kwargs: Unpack[_SqueezeNetOptions]
|
||||
) -> SqueezeNet:
|
||||
"""SqueezeNet v1.1 model from
|
||||
`"SqueezeNet: AlexNet-level accuracy with 50x fewer parameters and <0.5MB model size"
|
||||
<https://arxiv.org/pdf/1602.07360.pdf>`_.
|
||||
|
||||
Args:
|
||||
pretrained (bool, optional): Whether to load pre-trained weights. If True, returns a model pre-trained
|
||||
on ImageNet. Default: False.
|
||||
**kwargs (optional): Additional keyword arguments. For details, please refer to :ref:`SqueezeNet <api_paddle_vision_models_SqueezeNet>`.
|
||||
|
||||
Returns:
|
||||
:ref:`api_paddle_nn_Layer`. An instance of SqueezeNet v1.1 model.
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
>>> from paddle.vision.models import squeezenet1_1
|
||||
|
||||
>>> # build model
|
||||
>>> model = squeezenet1_1()
|
||||
|
||||
>>> # build model and load imagenet pretrained weight
|
||||
>>> # model = squeezenet1_1(pretrained=True)
|
||||
|
||||
>>> x = paddle.rand([1, 3, 224, 224])
|
||||
>>> out = model(x)
|
||||
|
||||
>>> print(out.shape)
|
||||
paddle.Size([1, 1000])
|
||||
"""
|
||||
return _squeezenet('squeezenet1_1', '1.1', pretrained, **kwargs)
|
||||
@@ -0,0 +1,371 @@
|
||||
# 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.
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import (
|
||||
TYPE_CHECKING,
|
||||
Literal,
|
||||
TypedDict,
|
||||
)
|
||||
|
||||
from typing_extensions import NotRequired, Unpack
|
||||
|
||||
import paddle
|
||||
from paddle import nn
|
||||
from paddle.utils.download import get_weights_path_from_url
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from paddle import Tensor
|
||||
from paddle.nn import Layer, Sequential
|
||||
|
||||
class _VGGOptions(TypedDict):
|
||||
num_classes: NotRequired[int]
|
||||
with_pool: NotRequired[bool]
|
||||
|
||||
|
||||
__all__ = []
|
||||
|
||||
model_urls = {
|
||||
'vgg16': (
|
||||
'https://paddle-hapi.bj.bcebos.com/models/vgg16.pdparams',
|
||||
'89bbffc0f87d260be9b8cdc169c991c4',
|
||||
),
|
||||
'vgg19': (
|
||||
'https://paddle-hapi.bj.bcebos.com/models/vgg19.pdparams',
|
||||
'23b18bb13d8894f60f54e642be79a0dd',
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
class VGG(nn.Layer):
|
||||
"""VGG model from
|
||||
`"Very Deep Convolutional Networks For Large-Scale Image Recognition" <https://arxiv.org/pdf/1409.1556.pdf>`_.
|
||||
|
||||
Args:
|
||||
features (nn.Layer): Vgg features create by function make_layers.
|
||||
num_classes (int, optional): Output dim of last fc layer. If num_classes <= 0, last fc layer
|
||||
will not be defined. Default: 1000.
|
||||
with_pool (bool, optional): Use pool before the last three fc layer or not. Default: True.
|
||||
|
||||
Returns:
|
||||
:ref:`api_paddle_nn_Layer`. An instance of VGG model.
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
>>> from paddle.vision.models import VGG
|
||||
>>> from paddle.vision.models.vgg import make_layers
|
||||
|
||||
>>> vgg11_cfg = [
|
||||
... 64,
|
||||
... 'M',
|
||||
... 128,
|
||||
... 'M',
|
||||
... 256,
|
||||
... 256,
|
||||
... 'M',
|
||||
... 512,
|
||||
... 512,
|
||||
... 'M',
|
||||
... 512,
|
||||
... 512,
|
||||
... 'M',
|
||||
... ]
|
||||
|
||||
>>> features = make_layers(vgg11_cfg) # type: ignore
|
||||
|
||||
>>> vgg11 = VGG(features)
|
||||
|
||||
>>> x = paddle.rand([1, 3, 224, 224])
|
||||
>>> out = vgg11(x)
|
||||
|
||||
>>> print(out.shape)
|
||||
paddle.Size([1, 1000])
|
||||
"""
|
||||
|
||||
num_classes: int
|
||||
with_pool: bool
|
||||
|
||||
def __init__(
|
||||
self, features: Layer, num_classes: int = 1000, with_pool: bool = True
|
||||
) -> None:
|
||||
super().__init__()
|
||||
self.features = features
|
||||
self.num_classes = num_classes
|
||||
self.with_pool = with_pool
|
||||
|
||||
if with_pool:
|
||||
self.avgpool = nn.AdaptiveAvgPool2D((7, 7))
|
||||
|
||||
if num_classes > 0:
|
||||
self.classifier = nn.Sequential(
|
||||
nn.Linear(512 * 7 * 7, 4096),
|
||||
nn.ReLU(),
|
||||
nn.Dropout(),
|
||||
nn.Linear(4096, 4096),
|
||||
nn.ReLU(),
|
||||
nn.Dropout(),
|
||||
nn.Linear(4096, num_classes),
|
||||
)
|
||||
|
||||
def forward(self, x: Tensor) -> Tensor:
|
||||
x = self.features(x)
|
||||
|
||||
if self.with_pool:
|
||||
x = self.avgpool(x)
|
||||
|
||||
if self.num_classes > 0:
|
||||
x = paddle.flatten(x, 1)
|
||||
x = self.classifier(x)
|
||||
|
||||
return x
|
||||
|
||||
|
||||
def make_layers(
|
||||
cfg: list[int | Literal['M']], batch_norm: bool = False
|
||||
) -> Sequential:
|
||||
layers = []
|
||||
in_channels = 3
|
||||
for v in cfg:
|
||||
if v == 'M':
|
||||
layers += [nn.MaxPool2D(kernel_size=2, stride=2)]
|
||||
else:
|
||||
conv2d = nn.Conv2D(in_channels, v, kernel_size=3, padding=1)
|
||||
if batch_norm:
|
||||
layers += [conv2d, nn.BatchNorm2D(v), nn.ReLU()]
|
||||
else:
|
||||
layers += [conv2d, nn.ReLU()]
|
||||
in_channels = v
|
||||
return nn.Sequential(*layers)
|
||||
|
||||
|
||||
cfgs = {
|
||||
'A': [
|
||||
64, 'M',
|
||||
128, 'M',
|
||||
256, 256, 'M',
|
||||
512, 512, 'M',
|
||||
512, 512, 'M',
|
||||
],
|
||||
'B': [
|
||||
64, 64, 'M',
|
||||
128, 128, 'M',
|
||||
256, 256, 'M',
|
||||
512, 512, 'M',
|
||||
512, 512, 'M',
|
||||
],
|
||||
'D': [
|
||||
64, 64, 'M',
|
||||
128, 128, 'M',
|
||||
256, 256, 256, 'M',
|
||||
512, 512, 512, 'M',
|
||||
512, 512, 512, 'M',
|
||||
],
|
||||
'E': [
|
||||
64, 64, 'M',
|
||||
128, 128, 'M',
|
||||
256, 256, 256, 256, 'M',
|
||||
512, 512, 512, 512, 'M',
|
||||
512, 512, 512, 512, 'M',
|
||||
],
|
||||
} # fmt: skip
|
||||
|
||||
|
||||
def _vgg(
|
||||
arch: str,
|
||||
cfg: Literal["A", "B", "D", "E"],
|
||||
batch_norm: bool,
|
||||
pretrained: bool,
|
||||
**kwargs: Unpack[_VGGOptions],
|
||||
) -> VGG:
|
||||
model = VGG(make_layers(cfgs[cfg], batch_norm=batch_norm), **kwargs)
|
||||
|
||||
if pretrained:
|
||||
assert arch in model_urls, (
|
||||
f"{arch} model do not have a pretrained model now, you should set pretrained=False"
|
||||
)
|
||||
weight_path = get_weights_path_from_url(
|
||||
model_urls[arch][0], model_urls[arch][1]
|
||||
)
|
||||
|
||||
param = paddle.load(weight_path)
|
||||
model.load_dict(param)
|
||||
|
||||
return model
|
||||
|
||||
|
||||
def vgg11(
|
||||
pretrained: bool = False,
|
||||
batch_norm: bool = False,
|
||||
**kwargs: Unpack[_VGGOptions],
|
||||
) -> VGG:
|
||||
"""VGG 11-layer model from
|
||||
`"Very Deep Convolutional Networks For Large-Scale Image Recognition" <https://arxiv.org/pdf/1409.1556.pdf>`_.
|
||||
|
||||
Args:
|
||||
pretrained (bool, optional): Whether to load pre-trained weights. If True, returns a model pre-trained
|
||||
on ImageNet. Default: False.
|
||||
batch_norm (bool, optional): If True, returns a model with batch_norm layer. Default: False.
|
||||
**kwargs (optional): Additional keyword arguments. For details, please refer to :ref:`VGG <api_paddle_vision_models_VGG>`.
|
||||
|
||||
Returns:
|
||||
:ref:`api_paddle_nn_Layer`. An instance of VGG 11-layer model.
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
>>> from paddle.vision.models import vgg11
|
||||
|
||||
>>> # build model
|
||||
>>> model = vgg11()
|
||||
|
||||
>>> # build vgg11 model with batch_norm
|
||||
>>> model = vgg11(batch_norm=True)
|
||||
|
||||
>>> x = paddle.rand([1, 3, 224, 224])
|
||||
>>> out = model(x)
|
||||
|
||||
>>> print(out.shape)
|
||||
paddle.Size([1, 1000])
|
||||
"""
|
||||
model_name = 'vgg11'
|
||||
if batch_norm:
|
||||
model_name += '_bn'
|
||||
return _vgg(model_name, 'A', batch_norm, pretrained, **kwargs)
|
||||
|
||||
|
||||
def vgg13(
|
||||
pretrained: bool = False,
|
||||
batch_norm: bool = False,
|
||||
**kwargs: Unpack[_VGGOptions],
|
||||
) -> VGG:
|
||||
"""VGG 13-layer model from
|
||||
`"Very Deep Convolutional Networks For Large-Scale Image Recognition" <https://arxiv.org/pdf/1409.1556.pdf>`_.
|
||||
|
||||
Args:
|
||||
pretrained (bool, optional): Whether to load pre-trained weights. If True, returns a model pre-trained
|
||||
on ImageNet. Default: False.
|
||||
batch_norm (bool): If True, returns a model with batch_norm layer. Default: False.
|
||||
**kwargs (optional): Additional keyword arguments. For details, please refer to :ref:`VGG <api_paddle_vision_models_VGG>`.
|
||||
|
||||
Returns:
|
||||
:ref:`api_paddle_nn_Layer`. An instance of VGG 13-layer model.
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
>>> from paddle.vision.models import vgg13
|
||||
|
||||
>>> # build model
|
||||
>>> model = vgg13()
|
||||
|
||||
>>> # build vgg13 model with batch_norm
|
||||
>>> model = vgg13(batch_norm=True)
|
||||
|
||||
>>> x = paddle.rand([1, 3, 224, 224])
|
||||
>>> out = model(x)
|
||||
|
||||
>>> print(out.shape)
|
||||
paddle.Size([1, 1000])
|
||||
"""
|
||||
model_name = 'vgg13'
|
||||
if batch_norm:
|
||||
model_name += '_bn'
|
||||
return _vgg(model_name, 'B', batch_norm, pretrained, **kwargs)
|
||||
|
||||
|
||||
def vgg16(
|
||||
pretrained: bool = False,
|
||||
batch_norm: bool = False,
|
||||
**kwargs: Unpack[_VGGOptions],
|
||||
) -> VGG:
|
||||
"""VGG 16-layer model from
|
||||
`"Very Deep Convolutional Networks For Large-Scale Image Recognition" <https://arxiv.org/pdf/1409.1556.pdf>`_.
|
||||
|
||||
Args:
|
||||
pretrained (bool, optional): Whether to load pre-trained weights. If True, returns a model pre-trained
|
||||
on ImageNet. Default: False.
|
||||
batch_norm (bool, optional): If True, returns a model with batch_norm layer. Default: False.
|
||||
**kwargs (optional): Additional keyword arguments. For details, please refer to :ref:`VGG <api_paddle_vision_models_VGG>`.
|
||||
|
||||
Returns:
|
||||
:ref:`api_paddle_nn_Layer`. An instance of VGG 16-layer model.
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
>>> from paddle.vision.models import vgg16
|
||||
|
||||
>>> # build model
|
||||
>>> model = vgg16()
|
||||
|
||||
>>> # build vgg16 model with batch_norm
|
||||
>>> model = vgg16(batch_norm=True)
|
||||
|
||||
>>> x = paddle.rand([1, 3, 224, 224])
|
||||
>>> out = model(x)
|
||||
|
||||
>>> print(out.shape)
|
||||
paddle.Size([1, 1000])
|
||||
"""
|
||||
model_name = 'vgg16'
|
||||
if batch_norm:
|
||||
model_name += '_bn'
|
||||
return _vgg(model_name, 'D', batch_norm, pretrained, **kwargs)
|
||||
|
||||
|
||||
def vgg19(
|
||||
pretrained: bool = False,
|
||||
batch_norm: bool = False,
|
||||
**kwargs: Unpack[_VGGOptions],
|
||||
) -> VGG:
|
||||
"""VGG 19-layer model from
|
||||
`"Very Deep Convolutional Networks For Large-Scale Image Recognition" <https://arxiv.org/pdf/1409.1556.pdf>`_.
|
||||
|
||||
Args:
|
||||
pretrained (bool, optional): Whether to load pre-trained weights. If True, returns a model pre-trained
|
||||
on ImageNet. Default: False.
|
||||
batch_norm (bool, optional): If True, returns a model with batch_norm layer. Default: False.
|
||||
**kwargs (optional): Additional keyword arguments. For details, please refer to :ref:`VGG <api_paddle_vision_models_VGG>`.
|
||||
|
||||
Returns:
|
||||
:ref:`api_paddle_nn_Layer`. An instance of VGG 19-layer model.
|
||||
|
||||
Examples:
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> import paddle
|
||||
>>> from paddle.vision.models import vgg19
|
||||
|
||||
>>> # build model
|
||||
>>> model = vgg19()
|
||||
|
||||
>>> # build vgg19 model with batch_norm
|
||||
>>> model = vgg19(batch_norm=True)
|
||||
|
||||
>>> x = paddle.rand([1, 3, 224, 224])
|
||||
>>> out = model(x)
|
||||
|
||||
>>> print(out.shape)
|
||||
paddle.Size([1, 1000])
|
||||
"""
|
||||
model_name = 'vgg19'
|
||||
if batch_norm:
|
||||
model_name += '_bn'
|
||||
return _vgg(model_name, 'E', batch_norm, pretrained, **kwargs)
|
||||
Executable
+2621
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,97 @@
|
||||
# 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.
|
||||
|
||||
from .functional import (
|
||||
adjust_brightness,
|
||||
adjust_contrast,
|
||||
adjust_hue,
|
||||
affine,
|
||||
center_crop,
|
||||
crop,
|
||||
erase,
|
||||
hflip,
|
||||
normalize,
|
||||
pad,
|
||||
perspective,
|
||||
resize,
|
||||
rotate,
|
||||
to_grayscale,
|
||||
to_tensor,
|
||||
vflip,
|
||||
)
|
||||
from .transforms import (
|
||||
BaseTransform,
|
||||
BrightnessTransform,
|
||||
CenterCrop,
|
||||
ColorJitter,
|
||||
Compose,
|
||||
ContrastTransform,
|
||||
Grayscale,
|
||||
HueTransform,
|
||||
Normalize,
|
||||
Pad,
|
||||
RandomAffine,
|
||||
RandomCrop,
|
||||
RandomErasing,
|
||||
RandomHorizontalFlip,
|
||||
RandomPerspective,
|
||||
RandomResizedCrop,
|
||||
RandomRotation,
|
||||
RandomVerticalFlip,
|
||||
Resize,
|
||||
SaturationTransform,
|
||||
ToTensor,
|
||||
Transpose,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
'BaseTransform',
|
||||
'Compose',
|
||||
'Resize',
|
||||
'RandomResizedCrop',
|
||||
'CenterCrop',
|
||||
'RandomHorizontalFlip',
|
||||
'RandomVerticalFlip',
|
||||
'Transpose',
|
||||
'Normalize',
|
||||
'BrightnessTransform',
|
||||
'SaturationTransform',
|
||||
'ContrastTransform',
|
||||
'HueTransform',
|
||||
'ColorJitter',
|
||||
'RandomCrop',
|
||||
'Pad',
|
||||
'RandomAffine',
|
||||
'RandomRotation',
|
||||
'RandomPerspective',
|
||||
'Grayscale',
|
||||
'ToTensor',
|
||||
'RandomErasing',
|
||||
'to_tensor',
|
||||
'hflip',
|
||||
'vflip',
|
||||
'resize',
|
||||
'pad',
|
||||
'affine',
|
||||
'rotate',
|
||||
'perspective',
|
||||
'to_grayscale',
|
||||
'crop',
|
||||
'center_crop',
|
||||
'adjust_brightness',
|
||||
'adjust_contrast',
|
||||
'adjust_hue',
|
||||
'normalize',
|
||||
'erase',
|
||||
]
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,732 @@
|
||||
# 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 math
|
||||
import numbers
|
||||
from collections.abc import Iterable, Sequence
|
||||
|
||||
import numpy as np
|
||||
|
||||
import paddle
|
||||
from paddle.utils import try_import
|
||||
|
||||
__all__ = []
|
||||
|
||||
|
||||
def to_tensor(pic, data_format='CHW'):
|
||||
"""Converts a ``numpy.ndarray`` to paddle.Tensor.
|
||||
|
||||
See ``ToTensor`` for more details.
|
||||
|
||||
Args:
|
||||
pic (np.ndarray): Image to be converted to tensor.
|
||||
data_format (str, optional): Data format of output tensor, should be 'HWC' or
|
||||
'CHW'. Default: 'CHW'.
|
||||
|
||||
Returns:
|
||||
Tensor: Converted image.
|
||||
|
||||
"""
|
||||
|
||||
if data_format not in ['CHW', 'HWC']:
|
||||
raise ValueError(f'data_format should be CHW or HWC. Got {data_format}')
|
||||
|
||||
if pic.ndim == 2:
|
||||
pic = pic[:, :, None]
|
||||
|
||||
if data_format == 'CHW':
|
||||
img = paddle.to_tensor(pic.transpose((2, 0, 1)))
|
||||
else:
|
||||
img = paddle.to_tensor(pic)
|
||||
|
||||
if paddle.base.data_feeder.convert_dtype(img.dtype) == 'uint8':
|
||||
return paddle.cast(img, np.float32) / 255.0
|
||||
else:
|
||||
return img
|
||||
|
||||
|
||||
def resize(img, size, interpolation='bilinear'):
|
||||
"""
|
||||
Resizes the image to given size
|
||||
|
||||
Args:
|
||||
input (np.ndarray): Image to be resized.
|
||||
size (int|list|tuple): Target size of input data, with (height, width) shape.
|
||||
interpolation (int|str, optional): Interpolation method. when use cv2 backend,
|
||||
support method are as following:
|
||||
- "nearest": cv2.INTER_NEAREST,
|
||||
- "bilinear": cv2.INTER_LINEAR,
|
||||
- "area": cv2.INTER_AREA,
|
||||
- "bicubic": cv2.INTER_CUBIC,
|
||||
- "lanczos": cv2.INTER_LANCZOS4
|
||||
|
||||
Returns:
|
||||
np.array: Resized image.
|
||||
|
||||
"""
|
||||
cv2 = try_import('cv2')
|
||||
_cv2_interp_from_str = {
|
||||
'nearest': cv2.INTER_NEAREST,
|
||||
'bilinear': cv2.INTER_LINEAR,
|
||||
'area': cv2.INTER_AREA,
|
||||
'bicubic': cv2.INTER_CUBIC,
|
||||
'lanczos': cv2.INTER_LANCZOS4,
|
||||
}
|
||||
|
||||
if not (
|
||||
isinstance(size, int) or (isinstance(size, Iterable) and len(size) == 2)
|
||||
):
|
||||
raise TypeError(f'Got inappropriate size arg: {size}')
|
||||
|
||||
h, w = img.shape[:2]
|
||||
|
||||
if isinstance(size, int):
|
||||
if (w <= h and w == size) or (h <= w and h == size):
|
||||
return img
|
||||
if w < h:
|
||||
ow = size
|
||||
oh = int(size * h / w)
|
||||
output = cv2.resize(
|
||||
img,
|
||||
dsize=(ow, oh),
|
||||
interpolation=_cv2_interp_from_str[interpolation],
|
||||
)
|
||||
else:
|
||||
oh = size
|
||||
ow = int(size * w / h)
|
||||
output = cv2.resize(
|
||||
img,
|
||||
dsize=(ow, oh),
|
||||
interpolation=_cv2_interp_from_str[interpolation],
|
||||
)
|
||||
else:
|
||||
output = cv2.resize(
|
||||
img,
|
||||
dsize=(size[1], size[0]),
|
||||
interpolation=_cv2_interp_from_str[interpolation],
|
||||
)
|
||||
if len(img.shape) == 3 and img.shape[2] == 1:
|
||||
return output[:, :, np.newaxis]
|
||||
else:
|
||||
return output
|
||||
|
||||
|
||||
def pad(img, padding, fill=0, padding_mode='constant'):
|
||||
"""
|
||||
Pads the given numpy.array on all sides with specified padding mode and fill value.
|
||||
|
||||
Args:
|
||||
img (np.array): Image to be padded.
|
||||
padding (int|list|tuple): Padding on each border. If a single int is provided this
|
||||
is used to pad all borders. If list/tuple of length 2 is provided this is the padding
|
||||
on left/right and top/bottom respectively. If a list/tuple of length 4 is provided
|
||||
this is the padding for the left, top, right and bottom borders
|
||||
respectively.
|
||||
fill (float, optional): Pixel fill value for constant fill. If a tuple of
|
||||
length 3, it is used to fill R, G, B channels respectively.
|
||||
This value is only used when the padding_mode is constant. Default: 0.
|
||||
padding_mode: Type of padding. Should be: constant, edge, reflect or symmetric. Default: 'constant'.
|
||||
|
||||
- constant: pads with a constant value, this value is specified with fill
|
||||
|
||||
- edge: pads with the last value on the edge of the image
|
||||
|
||||
- reflect: pads with reflection of image (without repeating the last value on the edge)
|
||||
|
||||
padding [1, 2, 3, 4] with 2 elements on both sides in reflect mode
|
||||
will result in [3, 2, 1, 2, 3, 4, 3, 2]
|
||||
|
||||
- symmetric: pads with reflection of image (repeating the last value on the edge)
|
||||
|
||||
padding [1, 2, 3, 4] with 2 elements on both sides in symmetric mode
|
||||
will result in [2, 1, 1, 2, 3, 4, 4, 3]
|
||||
|
||||
Returns:
|
||||
np.array: Padded image.
|
||||
|
||||
"""
|
||||
cv2 = try_import('cv2')
|
||||
_cv2_pad_from_str = {
|
||||
'constant': cv2.BORDER_CONSTANT,
|
||||
'edge': cv2.BORDER_REPLICATE,
|
||||
'reflect': cv2.BORDER_REFLECT_101,
|
||||
'symmetric': cv2.BORDER_REFLECT,
|
||||
}
|
||||
|
||||
if not isinstance(padding, (numbers.Number, list, tuple)):
|
||||
raise TypeError('Got inappropriate padding arg')
|
||||
if not isinstance(fill, (numbers.Number, str, list, tuple)):
|
||||
raise TypeError('Got inappropriate fill arg')
|
||||
if not isinstance(padding_mode, str):
|
||||
raise TypeError('Got inappropriate padding_mode arg')
|
||||
|
||||
if isinstance(padding, Sequence) and len(padding) not in [2, 4]:
|
||||
raise ValueError(
|
||||
"Padding must be an int or a 2, or 4 element tuple, not a "
|
||||
+ f"{len(padding)} element tuple"
|
||||
)
|
||||
|
||||
assert padding_mode in [
|
||||
'constant',
|
||||
'edge',
|
||||
'reflect',
|
||||
'symmetric',
|
||||
], 'Padding mode should be either constant, edge, reflect or symmetric'
|
||||
|
||||
if isinstance(padding, list):
|
||||
padding = tuple(padding)
|
||||
if isinstance(padding, int):
|
||||
pad_left = pad_right = pad_top = pad_bottom = padding
|
||||
if isinstance(padding, Sequence) and len(padding) == 2:
|
||||
pad_left = pad_right = padding[0]
|
||||
pad_top = pad_bottom = padding[1]
|
||||
if isinstance(padding, Sequence) and len(padding) == 4:
|
||||
pad_left = padding[0]
|
||||
pad_top = padding[1]
|
||||
pad_right = padding[2]
|
||||
pad_bottom = padding[3]
|
||||
|
||||
if len(img.shape) == 3 and img.shape[2] == 1:
|
||||
return cv2.copyMakeBorder(
|
||||
img,
|
||||
top=pad_top,
|
||||
bottom=pad_bottom,
|
||||
left=pad_left,
|
||||
right=pad_right,
|
||||
borderType=_cv2_pad_from_str[padding_mode],
|
||||
value=fill,
|
||||
)[:, :, np.newaxis]
|
||||
else:
|
||||
return cv2.copyMakeBorder(
|
||||
img,
|
||||
top=pad_top,
|
||||
bottom=pad_bottom,
|
||||
left=pad_left,
|
||||
right=pad_right,
|
||||
borderType=_cv2_pad_from_str[padding_mode],
|
||||
value=fill,
|
||||
)
|
||||
|
||||
|
||||
def crop(img, top, left, height, width):
|
||||
"""Crops the given image.
|
||||
|
||||
Args:
|
||||
img (np.array): Image to be cropped. (0,0) denotes the top left
|
||||
corner of the image.
|
||||
top (int): Vertical component of the top left corner of the crop box.
|
||||
left (int): Horizontal component of the top left corner of the crop box.
|
||||
height (int): Height of the crop box.
|
||||
width (int): Width of the crop box.
|
||||
|
||||
Returns:
|
||||
np.array: Cropped image.
|
||||
|
||||
"""
|
||||
|
||||
return img[top : top + height, left : left + width, :]
|
||||
|
||||
|
||||
def center_crop(img, output_size):
|
||||
"""Crops the given image and resize it to desired size.
|
||||
|
||||
Args:
|
||||
img (np.array): Image to be cropped. (0,0) denotes the top left corner of the image.
|
||||
output_size (sequence or int): (height, width) of the crop box. If int,
|
||||
it is used for both directions
|
||||
backend (str, optional): The image process backend type. Options are `pil`, `cv2`. Default: 'pil'.
|
||||
|
||||
Returns:
|
||||
np.array: Cropped image.
|
||||
|
||||
"""
|
||||
|
||||
if isinstance(output_size, numbers.Number):
|
||||
output_size = (int(output_size), int(output_size))
|
||||
|
||||
h, w = img.shape[0:2]
|
||||
th, tw = output_size
|
||||
i = int(round((h - th) / 2.0))
|
||||
j = int(round((w - tw) / 2.0))
|
||||
return crop(img, i, j, th, tw)
|
||||
|
||||
|
||||
def hflip(img):
|
||||
"""Horizontally flips the given image.
|
||||
|
||||
Args:
|
||||
img (np.array): Image to be flipped.
|
||||
|
||||
Returns:
|
||||
np.array: Horizontally flipped image.
|
||||
|
||||
"""
|
||||
cv2 = try_import('cv2')
|
||||
|
||||
return cv2.flip(img, 1)
|
||||
|
||||
|
||||
def vflip(img):
|
||||
"""Vertically flips the given np.array.
|
||||
|
||||
Args:
|
||||
img (np.array): Image to be flipped.
|
||||
|
||||
Returns:
|
||||
np.array: Vertically flipped image.
|
||||
|
||||
"""
|
||||
cv2 = try_import('cv2')
|
||||
|
||||
if len(img.shape) == 3 and img.shape[2] == 1:
|
||||
return cv2.flip(img, 0)[:, :, np.newaxis]
|
||||
else:
|
||||
return cv2.flip(img, 0)
|
||||
|
||||
|
||||
def adjust_brightness(img, brightness_factor):
|
||||
"""Adjusts brightness of an image.
|
||||
|
||||
Args:
|
||||
img (np.array): Image to be adjusted.
|
||||
brightness_factor (float): How much to adjust the brightness. Can be
|
||||
any non negative number. 0 gives a black image, 1 gives the
|
||||
original image while 2 increases the brightness by a factor of 2.
|
||||
|
||||
Returns:
|
||||
np.array: Brightness adjusted image.
|
||||
|
||||
"""
|
||||
cv2 = try_import('cv2')
|
||||
|
||||
table = (
|
||||
np.array([i * brightness_factor for i in range(0, 256)])
|
||||
.clip(0, 255)
|
||||
.astype('uint8')
|
||||
)
|
||||
|
||||
if len(img.shape) == 3 and img.shape[2] == 1:
|
||||
return cv2.LUT(img, table)[:, :, np.newaxis]
|
||||
else:
|
||||
return cv2.LUT(img, table)
|
||||
|
||||
|
||||
def adjust_contrast(img, contrast_factor):
|
||||
"""Adjusts contrast of an image.
|
||||
|
||||
Args:
|
||||
img (np.array): Image to be adjusted.
|
||||
contrast_factor (float): How much to adjust the contrast. Can be any
|
||||
non negative number. 0 gives a solid gray image, 1 gives the
|
||||
original image while 2 increases the contrast by a factor of 2.
|
||||
|
||||
Returns:
|
||||
np.array: Contrast adjusted image.
|
||||
|
||||
"""
|
||||
cv2 = try_import('cv2')
|
||||
|
||||
table = (
|
||||
np.array([(i - 74) * contrast_factor + 74 for i in range(0, 256)])
|
||||
.clip(0, 255)
|
||||
.astype('uint8')
|
||||
)
|
||||
if len(img.shape) == 3 and img.shape[2] == 1:
|
||||
return cv2.LUT(img, table)[:, :, np.newaxis]
|
||||
else:
|
||||
return cv2.LUT(img, table)
|
||||
|
||||
|
||||
def adjust_saturation(img, saturation_factor):
|
||||
"""Adjusts color saturation of an image.
|
||||
|
||||
Args:
|
||||
img (np.array): Image to be adjusted.
|
||||
saturation_factor (float): How much to adjust the saturation. 0 will
|
||||
give a black and white image, 1 will give the original image while
|
||||
2 will enhance the saturation by a factor of 2.
|
||||
|
||||
Returns:
|
||||
np.array: Saturation adjusted image.
|
||||
|
||||
"""
|
||||
cv2 = try_import('cv2')
|
||||
|
||||
dtype = img.dtype
|
||||
img = img.astype(np.float32)
|
||||
alpha = np.random.uniform(
|
||||
max(0, 1 - saturation_factor), 1 + saturation_factor
|
||||
)
|
||||
gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
|
||||
gray_img = gray_img[..., np.newaxis]
|
||||
img = img * alpha + gray_img * (1 - alpha)
|
||||
return img.clip(0, 255).astype(dtype)
|
||||
|
||||
|
||||
def adjust_hue(img, hue_factor):
|
||||
"""Adjusts hue of an image.
|
||||
|
||||
The image hue is adjusted by converting the image to HSV and
|
||||
cyclically shifting the intensities in the hue channel (H).
|
||||
The image is then converted back to original image mode.
|
||||
|
||||
`hue_factor` is the amount of shift in H channel and must be in the
|
||||
interval `[-0.5, 0.5]`.
|
||||
|
||||
Args:
|
||||
img (np.array): Image to be adjusted.
|
||||
hue_factor (float): How much to shift the hue channel. Should be in
|
||||
[-0.5, 0.5]. 0.5 and -0.5 give complete reversal of hue channel in
|
||||
HSV space in positive and negative direction respectively.
|
||||
0 means no shift. Therefore, both -0.5 and 0.5 will give an image
|
||||
with complementary colors while 0 gives the original image.
|
||||
|
||||
Returns:
|
||||
np.array: Hue adjusted image.
|
||||
|
||||
"""
|
||||
cv2 = try_import('cv2')
|
||||
|
||||
if not (-0.5 <= hue_factor <= 0.5):
|
||||
raise ValueError(f'hue_factor:{hue_factor} is not in [-0.5, 0.5].')
|
||||
|
||||
dtype = img.dtype
|
||||
img = img.astype(np.uint8)
|
||||
hsv_img = cv2.cvtColor(img, cv2.COLOR_BGR2HSV_FULL)
|
||||
h, s, v = cv2.split(hsv_img)
|
||||
alpha = hue_factor
|
||||
h = h.astype(np.int32) # Convert to int32 to prevent overflow
|
||||
# uint8 addition takes care of rotation across boundaries
|
||||
h = (h + int(alpha * 255)) % 256 # Ensure values are within [0, 255]
|
||||
h = h.astype(np.uint8) # Convert back to uint8
|
||||
hsv_img = cv2.merge([h, s, v])
|
||||
return cv2.cvtColor(hsv_img, cv2.COLOR_HSV2BGR_FULL).astype(dtype)
|
||||
|
||||
|
||||
def affine(
|
||||
img,
|
||||
angle,
|
||||
translate,
|
||||
scale,
|
||||
shear,
|
||||
interpolation='nearest',
|
||||
fill=0,
|
||||
center=None,
|
||||
):
|
||||
"""Affine the image by matrix.
|
||||
|
||||
Args:
|
||||
img (PIL.Image): Image to be affined.
|
||||
translate (sequence or int): horizontal and vertical translations
|
||||
scale (float): overall scale ratio
|
||||
shear (sequence or float): shear angle value in degrees between -180 to 180, clockwise direction.
|
||||
If a sequence is specified, the first value corresponds to a shear parallel to the x axis, while
|
||||
the second value corresponds to a shear parallel to the y axis.
|
||||
interpolation (int|str, optional): Interpolation method. If omitted, or if the
|
||||
image has only one channel, it is set to cv2.INTER_NEAREST.
|
||||
when use cv2 backend, support method are as following:
|
||||
- "nearest": cv2.INTER_NEAREST,
|
||||
- "bilinear": cv2.INTER_LINEAR,
|
||||
- "bicubic": cv2.INTER_CUBIC
|
||||
fill (3-tuple or int): RGB pixel fill value for area outside the affined image.
|
||||
If int, it is used for all channels respectively.
|
||||
center (sequence, optional): Optional center of rotation. Origin is the upper left corner.
|
||||
Default is the center of the image.
|
||||
|
||||
Returns:
|
||||
np.array: Affined image.
|
||||
|
||||
"""
|
||||
cv2 = try_import('cv2')
|
||||
_cv2_interp_from_str = {
|
||||
'nearest': cv2.INTER_NEAREST,
|
||||
'bilinear': cv2.INTER_LINEAR,
|
||||
'area': cv2.INTER_AREA,
|
||||
'bicubic': cv2.INTER_CUBIC,
|
||||
'lanczos': cv2.INTER_LANCZOS4,
|
||||
}
|
||||
|
||||
h, w = img.shape[0:2]
|
||||
|
||||
if isinstance(fill, int):
|
||||
fill = tuple([fill] * 3)
|
||||
|
||||
if center is None:
|
||||
center = (w / 2.0, h / 2.0)
|
||||
|
||||
M = np.ones([2, 3])
|
||||
# Rotate and Scale
|
||||
R = cv2.getRotationMatrix2D(angle=angle, center=center, scale=scale)
|
||||
|
||||
# Shear
|
||||
sx = math.tan(shear[0] * math.pi / 180)
|
||||
sy = math.tan(shear[1] * math.pi / 180)
|
||||
M[0] = R[0] + sy * R[1]
|
||||
M[1] = R[1] + sx * R[0]
|
||||
|
||||
# Translation
|
||||
tx, ty = translate
|
||||
M[0, 2] = tx
|
||||
M[1, 2] = ty
|
||||
|
||||
if len(img.shape) == 3 and img.shape[2] == 1:
|
||||
return cv2.warpAffine(
|
||||
img,
|
||||
M,
|
||||
dsize=(w, h),
|
||||
flags=_cv2_interp_from_str[interpolation],
|
||||
borderValue=fill,
|
||||
)[:, :, np.newaxis]
|
||||
else:
|
||||
return cv2.warpAffine(
|
||||
img,
|
||||
M,
|
||||
dsize=(w, h),
|
||||
flags=_cv2_interp_from_str[interpolation],
|
||||
borderValue=fill,
|
||||
)
|
||||
|
||||
|
||||
def rotate(
|
||||
img, angle, interpolation='nearest', expand=False, center=None, fill=0
|
||||
):
|
||||
"""Rotates the image by angle.
|
||||
|
||||
Args:
|
||||
img (np.array): Image to be rotated.
|
||||
angle (float or int): In degrees degrees counter clockwise order.
|
||||
interpolation (int|str, optional): Interpolation method. If omitted, or if the
|
||||
image has only one channel, it is set to cv2.INTER_NEAREST.
|
||||
when use cv2 backend, support method are as following:
|
||||
- "nearest": cv2.INTER_NEAREST,
|
||||
- "bilinear": cv2.INTER_LINEAR,
|
||||
- "bicubic": cv2.INTER_CUBIC
|
||||
expand (bool, optional): Optional expansion flag.
|
||||
If true, expands the output image to make it large enough to hold the entire rotated image.
|
||||
If false or omitted, make the output image the same size as the input image.
|
||||
Note that the expand flag assumes rotation around the center and no translation.
|
||||
center (2-tuple, optional): Optional center of rotation.
|
||||
Origin is the upper left corner.
|
||||
Default is the center of the image.
|
||||
fill (3-tuple or int): RGB pixel fill value for area outside the rotated image.
|
||||
If int, it is used for all channels respectively.
|
||||
|
||||
Returns:
|
||||
np.array: Rotated image.
|
||||
|
||||
"""
|
||||
cv2 = try_import('cv2')
|
||||
_cv2_interp_from_str = {
|
||||
'nearest': cv2.INTER_NEAREST,
|
||||
'bilinear': cv2.INTER_LINEAR,
|
||||
'area': cv2.INTER_AREA,
|
||||
'bicubic': cv2.INTER_CUBIC,
|
||||
'lanczos': cv2.INTER_LANCZOS4,
|
||||
}
|
||||
|
||||
h, w = img.shape[0:2]
|
||||
if center is None:
|
||||
center = (w / 2.0, h / 2.0)
|
||||
M = cv2.getRotationMatrix2D(center, angle, 1)
|
||||
|
||||
if expand:
|
||||
|
||||
def transform(x, y, matrix):
|
||||
(a, b, c, d, e, f) = matrix
|
||||
return a * x + b * y + c, d * x + e * y + f
|
||||
|
||||
# calculate output size
|
||||
xx = []
|
||||
yy = []
|
||||
|
||||
angle = -math.radians(angle)
|
||||
expand_matrix = [
|
||||
round(math.cos(angle), 15),
|
||||
round(math.sin(angle), 15),
|
||||
0.0,
|
||||
round(-math.sin(angle), 15),
|
||||
round(math.cos(angle), 15),
|
||||
0.0,
|
||||
]
|
||||
|
||||
post_trans = (0, 0)
|
||||
expand_matrix[2], expand_matrix[5] = transform(
|
||||
-center[0] - post_trans[0],
|
||||
-center[1] - post_trans[1],
|
||||
expand_matrix,
|
||||
)
|
||||
expand_matrix[2] += center[0]
|
||||
expand_matrix[5] += center[1]
|
||||
|
||||
for x, y in ((0, 0), (w, 0), (w, h), (0, h)):
|
||||
x, y = transform(x, y, expand_matrix)
|
||||
xx.append(x)
|
||||
yy.append(y)
|
||||
nw = math.ceil(max(xx)) - math.floor(min(xx))
|
||||
nh = math.ceil(max(yy)) - math.floor(min(yy))
|
||||
|
||||
M[0, 2] += (nw - w) * 0.5
|
||||
M[1, 2] += (nh - h) * 0.5
|
||||
|
||||
w, h = int(nw), int(nh)
|
||||
|
||||
if len(img.shape) == 3 and img.shape[2] == 1:
|
||||
return cv2.warpAffine(
|
||||
img,
|
||||
M,
|
||||
(w, h),
|
||||
flags=_cv2_interp_from_str[interpolation],
|
||||
borderValue=fill,
|
||||
)[:, :, np.newaxis]
|
||||
else:
|
||||
return cv2.warpAffine(
|
||||
img,
|
||||
M,
|
||||
(w, h),
|
||||
flags=_cv2_interp_from_str[interpolation],
|
||||
borderValue=fill,
|
||||
)
|
||||
|
||||
|
||||
def perspective(img, startpoints, endpoints, interpolation='nearest', fill=0):
|
||||
"""Perspective the image.
|
||||
|
||||
Args:
|
||||
img (np.array): Image to be perspectived.
|
||||
startpoints (list[list[int]]): [top-left, top-right, bottom-right, bottom-left] of the original image,
|
||||
endpoints (list[list[int]]): [top-left, top-right, bottom-right, bottom-left] of the transformed image.
|
||||
interpolation (int|str, optional): Interpolation method. If omitted, or if the
|
||||
image has only one channel, it is set to cv2.INTER_NEAREST.
|
||||
when use cv2 backend, support method are as following:
|
||||
- "nearest": cv2.INTER_NEAREST,
|
||||
- "bilinear": cv2.INTER_LINEAR,
|
||||
- "bicubic": cv2.INTER_CUBIC
|
||||
fill (3-tuple or int): RGB pixel fill value for area outside the rotated image.
|
||||
If int, it is used for all channels respectively.
|
||||
|
||||
Returns:
|
||||
np.array: Perspectived image.
|
||||
|
||||
"""
|
||||
cv2 = try_import('cv2')
|
||||
_cv2_interp_from_str = {
|
||||
'nearest': cv2.INTER_NEAREST,
|
||||
'bilinear': cv2.INTER_LINEAR,
|
||||
'area': cv2.INTER_AREA,
|
||||
'bicubic': cv2.INTER_CUBIC,
|
||||
'lanczos': cv2.INTER_LANCZOS4,
|
||||
}
|
||||
h, w = img.shape[0:2]
|
||||
|
||||
startpoints = np.array(startpoints, dtype="float32")
|
||||
endpoints = np.array(endpoints, dtype="float32")
|
||||
matrix = cv2.getPerspectiveTransform(startpoints, endpoints)
|
||||
|
||||
if len(img.shape) == 3 and img.shape[2] == 1:
|
||||
return cv2.warpPerspective(
|
||||
img,
|
||||
matrix,
|
||||
dsize=(w, h),
|
||||
flags=_cv2_interp_from_str[interpolation],
|
||||
borderValue=fill,
|
||||
)[:, :, np.newaxis]
|
||||
else:
|
||||
return cv2.warpPerspective(
|
||||
img,
|
||||
matrix,
|
||||
dsize=(w, h),
|
||||
flags=_cv2_interp_from_str[interpolation],
|
||||
borderValue=fill,
|
||||
)
|
||||
|
||||
|
||||
def to_grayscale(img, num_output_channels=1):
|
||||
"""Converts image to grayscale version of image.
|
||||
|
||||
Args:
|
||||
img (np.array): Image to be converted to grayscale.
|
||||
|
||||
Returns:
|
||||
np.array: Grayscale version of the image.
|
||||
if num_output_channels = 1 : returned image is single channel
|
||||
|
||||
if num_output_channels = 3 : returned image is 3 channel with r = g = b
|
||||
|
||||
"""
|
||||
cv2 = try_import('cv2')
|
||||
|
||||
if num_output_channels == 1:
|
||||
img = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)[:, :, np.newaxis]
|
||||
elif num_output_channels == 3:
|
||||
# much faster than doing cvtColor to go back to gray
|
||||
img = np.broadcast_to(
|
||||
cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)[:, :, np.newaxis], img.shape
|
||||
)
|
||||
else:
|
||||
raise ValueError('num_output_channels should be either 1 or 3')
|
||||
|
||||
return img
|
||||
|
||||
|
||||
def normalize(img, mean, std, data_format='CHW', to_rgb=False):
|
||||
"""Normalizes a ndarray image or image with mean and standard deviation.
|
||||
|
||||
Args:
|
||||
img (np.array): input data to be normalized.
|
||||
mean (list|tuple): Sequence of means for each channel.
|
||||
std (list|tuple): Sequence of standard deviations for each channel.
|
||||
data_format (str, optional): Data format of img, should be 'HWC' or
|
||||
'CHW'. Default: 'CHW'.
|
||||
to_rgb (bool, optional): Whether to convert to rgb. Default: False.
|
||||
|
||||
Returns:
|
||||
np.array: Normalized mage.
|
||||
|
||||
"""
|
||||
|
||||
if data_format == 'CHW':
|
||||
mean = np.float32(np.array(mean).reshape(-1, 1, 1))
|
||||
std = np.float32(np.array(std).reshape(-1, 1, 1))
|
||||
else:
|
||||
mean = np.float32(np.array(mean).reshape(1, 1, -1))
|
||||
std = np.float32(np.array(std).reshape(1, 1, -1))
|
||||
if to_rgb:
|
||||
# inplace
|
||||
img = img[..., ::-1]
|
||||
|
||||
img = (img - mean) / std
|
||||
return img
|
||||
|
||||
|
||||
def erase(img, i, j, h, w, v, inplace=False):
|
||||
"""Erase the pixels of selected area in input image array with given value.
|
||||
|
||||
Args:
|
||||
img (np.array): input image array, which shape is (H, W, C).
|
||||
i (int): y coordinate of the top-left point of erased region.
|
||||
j (int): x coordinate of the top-left point of erased region.
|
||||
h (int): Height of the erased region.
|
||||
w (int): Width of the erased region.
|
||||
v (np.array): value used to replace the pixels in erased region.
|
||||
inplace (bool, optional): Whether this transform is inplace. Default: False.
|
||||
|
||||
Returns:
|
||||
np.array: Erased image.
|
||||
|
||||
"""
|
||||
if not inplace:
|
||||
img = img.copy()
|
||||
|
||||
img[i : i + h, j : j + w, ...] = v
|
||||
return img
|
||||
@@ -0,0 +1,567 @@
|
||||
# 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 numbers
|
||||
from collections.abc import Iterable, Sequence
|
||||
|
||||
import numpy as np
|
||||
from PIL import Image, ImageEnhance, ImageOps
|
||||
|
||||
import paddle
|
||||
|
||||
try:
|
||||
# PIL version >= "9.1.0"
|
||||
_pil_interp_from_str = {
|
||||
'nearest': Image.Resampling.NEAREST,
|
||||
'bilinear': Image.Resampling.BILINEAR,
|
||||
'bicubic': Image.Resampling.BICUBIC,
|
||||
'box': Image.Resampling.BOX,
|
||||
'lanczos': Image.Resampling.LANCZOS,
|
||||
'hamming': Image.Resampling.HAMMING,
|
||||
}
|
||||
except:
|
||||
_pil_interp_from_str = {
|
||||
'nearest': Image.NEAREST,
|
||||
'bilinear': Image.BILINEAR,
|
||||
'bicubic': Image.BICUBIC,
|
||||
'box': Image.BOX,
|
||||
'lanczos': Image.LANCZOS,
|
||||
'hamming': Image.HAMMING,
|
||||
}
|
||||
|
||||
|
||||
__all__ = []
|
||||
|
||||
|
||||
def to_tensor(pic, data_format='CHW'):
|
||||
"""Converts a ``PIL.Image`` to paddle.Tensor.
|
||||
|
||||
See ``ToTensor`` for more details.
|
||||
|
||||
Args:
|
||||
pic (PIL.Image): Image to be converted to tensor.
|
||||
data_format (str, optional): Data format of output tensor, should be 'HWC' or
|
||||
'CHW'. Default: 'CHW'.
|
||||
|
||||
Returns:
|
||||
Tensor: Converted image.
|
||||
|
||||
"""
|
||||
|
||||
if data_format not in ['CHW', 'HWC']:
|
||||
raise ValueError(f'data_format should be CHW or HWC. Got {data_format}')
|
||||
|
||||
# PIL Image
|
||||
if pic.mode == 'I':
|
||||
img = paddle.to_tensor(np.asarray(pic, np.int32))
|
||||
elif pic.mode == 'I;16':
|
||||
# cast and reshape not support int16
|
||||
img = paddle.to_tensor(np.asarray(pic, np.int32))
|
||||
elif pic.mode == 'F':
|
||||
img = paddle.to_tensor(np.asarray(pic, np.float32))
|
||||
elif pic.mode == '1':
|
||||
img = 255 * paddle.to_tensor(np.asarray(pic, np.uint8))
|
||||
else:
|
||||
img = paddle.to_tensor(np.asarray(pic))
|
||||
|
||||
if pic.mode == 'YCbCr':
|
||||
nchannel = 3
|
||||
elif pic.mode == 'I;16':
|
||||
nchannel = 1
|
||||
else:
|
||||
nchannel = len(pic.mode)
|
||||
|
||||
dtype = paddle.base.data_feeder.convert_dtype(img.dtype)
|
||||
if dtype == 'uint8':
|
||||
img = paddle.cast(img, np.float32) / 255.0
|
||||
|
||||
img = img.reshape([pic.size[1], pic.size[0], nchannel])
|
||||
|
||||
if data_format == 'CHW':
|
||||
img = img.transpose([2, 0, 1])
|
||||
|
||||
return img
|
||||
|
||||
|
||||
def resize(img, size, interpolation='bilinear'):
|
||||
"""
|
||||
Resizes the image to given size
|
||||
|
||||
Args:
|
||||
input (PIL.Image): Image to be resized.
|
||||
size (int|list|tuple): Target size of input data, with (height, width) shape.
|
||||
interpolation (int|str, optional): Interpolation method. when use pil backend,
|
||||
support method are as following:
|
||||
- "nearest": Image.NEAREST,
|
||||
- "bilinear": Image.BILINEAR,
|
||||
- "bicubic": Image.BICUBIC,
|
||||
- "box": Image.BOX,
|
||||
- "lanczos": Image.LANCZOS,
|
||||
- "hamming": Image.HAMMING
|
||||
|
||||
Returns:
|
||||
PIL.Image: Resized image.
|
||||
|
||||
"""
|
||||
|
||||
if not (
|
||||
isinstance(size, int) or (isinstance(size, Iterable) and len(size) == 2)
|
||||
):
|
||||
raise TypeError(f'Got inappropriate size arg: {size}')
|
||||
|
||||
if isinstance(size, int):
|
||||
w, h = img.size
|
||||
if (w <= h and w == size) or (h <= w and h == size):
|
||||
return img
|
||||
if w < h:
|
||||
ow = size
|
||||
oh = int(size * h / w)
|
||||
return img.resize((ow, oh), _pil_interp_from_str[interpolation])
|
||||
else:
|
||||
oh = size
|
||||
ow = int(size * w / h)
|
||||
return img.resize((ow, oh), _pil_interp_from_str[interpolation])
|
||||
else:
|
||||
return img.resize(size[::-1], _pil_interp_from_str[interpolation])
|
||||
|
||||
|
||||
def pad(img, padding, fill=0, padding_mode='constant'):
|
||||
"""
|
||||
Pads the given PIL.Image on all sides with specified padding mode and fill value.
|
||||
|
||||
Args:
|
||||
img (PIL.Image): Image to be padded.
|
||||
padding (int|list|tuple): Padding on each border. If a single int is provided this
|
||||
is used to pad all borders. If list/tuple of length 2 is provided this is the padding
|
||||
on left/right and top/bottom respectively. If a list/tuple of length 4 is provided
|
||||
this is the padding for the left, top, right and bottom borders
|
||||
respectively.
|
||||
fill (float, optional): Pixel fill value for constant fill. If a tuple of
|
||||
length 3, it is used to fill R, G, B channels respectively.
|
||||
This value is only used when the padding_mode is constant. Default: 0.
|
||||
padding_mode: Type of padding. Should be: constant, edge, reflect or symmetric. Default: 'constant'.
|
||||
|
||||
- constant: pads with a constant value, this value is specified with fill
|
||||
|
||||
- edge: pads with the last value on the edge of the image
|
||||
|
||||
- reflect: pads with reflection of image (without repeating the last value on the edge)
|
||||
|
||||
padding [1, 2, 3, 4] with 2 elements on both sides in reflect mode
|
||||
will result in [3, 2, 1, 2, 3, 4, 3, 2]
|
||||
|
||||
- symmetric: pads with reflection of image (repeating the last value on the edge)
|
||||
|
||||
padding [1, 2, 3, 4] with 2 elements on both sides in symmetric mode
|
||||
will result in [2, 1, 1, 2, 3, 4, 4, 3]
|
||||
|
||||
Returns:
|
||||
PIL.Image: Padded image.
|
||||
|
||||
"""
|
||||
|
||||
if not isinstance(padding, (numbers.Number, list, tuple)):
|
||||
raise TypeError('Got inappropriate padding arg')
|
||||
if not isinstance(fill, (numbers.Number, str, list, tuple)):
|
||||
raise TypeError('Got inappropriate fill arg')
|
||||
if not isinstance(padding_mode, str):
|
||||
raise TypeError('Got inappropriate padding_mode arg')
|
||||
|
||||
if isinstance(padding, Sequence) and len(padding) not in [2, 4]:
|
||||
raise ValueError(
|
||||
"Padding must be an int or a 2, or 4 element tuple, not a "
|
||||
+ f"{len(padding)} element tuple"
|
||||
)
|
||||
|
||||
assert padding_mode in [
|
||||
'constant',
|
||||
'edge',
|
||||
'reflect',
|
||||
'symmetric',
|
||||
], 'Padding mode should be either constant, edge, reflect or symmetric'
|
||||
|
||||
if isinstance(padding, list):
|
||||
padding = tuple(padding)
|
||||
if isinstance(padding, int):
|
||||
pad_left = pad_right = pad_top = pad_bottom = padding
|
||||
if isinstance(padding, Sequence) and len(padding) == 2:
|
||||
pad_left = pad_right = padding[0]
|
||||
pad_top = pad_bottom = padding[1]
|
||||
if isinstance(padding, Sequence) and len(padding) == 4:
|
||||
pad_left = padding[0]
|
||||
pad_top = padding[1]
|
||||
pad_right = padding[2]
|
||||
pad_bottom = padding[3]
|
||||
|
||||
if padding_mode == 'constant':
|
||||
if img.mode == 'P':
|
||||
palette = img.getpalette()
|
||||
image = ImageOps.expand(img, border=padding, fill=fill)
|
||||
image.putpalette(palette)
|
||||
return image
|
||||
|
||||
return ImageOps.expand(img, border=padding, fill=fill)
|
||||
else:
|
||||
if img.mode == 'P':
|
||||
palette = img.getpalette()
|
||||
img = np.asarray(img)
|
||||
img = np.pad(
|
||||
img,
|
||||
((pad_top, pad_bottom), (pad_left, pad_right)),
|
||||
padding_mode,
|
||||
)
|
||||
img = Image.fromarray(img)
|
||||
img.putpalette(palette)
|
||||
return img
|
||||
|
||||
img = np.asarray(img)
|
||||
# RGB image
|
||||
if len(img.shape) == 3:
|
||||
img = np.pad(
|
||||
img,
|
||||
((pad_top, pad_bottom), (pad_left, pad_right), (0, 0)),
|
||||
padding_mode,
|
||||
)
|
||||
# Grayscale image
|
||||
if len(img.shape) == 2:
|
||||
img = np.pad(
|
||||
img,
|
||||
((pad_top, pad_bottom), (pad_left, pad_right)),
|
||||
padding_mode,
|
||||
)
|
||||
|
||||
return Image.fromarray(img)
|
||||
|
||||
|
||||
def crop(img, top, left, height, width):
|
||||
"""Crops the given PIL Image.
|
||||
|
||||
Args:
|
||||
img (PIL.Image): Image to be cropped. (0,0) denotes the top left
|
||||
corner of the image.
|
||||
top (int): Vertical component of the top left corner of the crop box.
|
||||
left (int): Horizontal component of the top left corner of the crop box.
|
||||
height (int): Height of the crop box.
|
||||
width (int): Width of the crop box.
|
||||
|
||||
Returns:
|
||||
PIL.Image: Cropped image.
|
||||
|
||||
"""
|
||||
return img.crop((left, top, left + width, top + height))
|
||||
|
||||
|
||||
def center_crop(img, output_size):
|
||||
"""Crops the given PIL Image and resize it to desired size.
|
||||
|
||||
Args:
|
||||
img (PIL.Image): Image to be cropped. (0,0) denotes the top left corner of the image.
|
||||
output_size (sequence or int): (height, width) of the crop box. If int,
|
||||
it is used for both directions
|
||||
backend (str, optional): The image process backend type. Options are `pil`, `cv2`. Default: 'pil'.
|
||||
|
||||
Returns:
|
||||
PIL.Image: Cropped image.
|
||||
|
||||
"""
|
||||
|
||||
if isinstance(output_size, numbers.Number):
|
||||
output_size = (int(output_size), int(output_size))
|
||||
|
||||
image_width, image_height = img.size
|
||||
crop_height, crop_width = output_size
|
||||
crop_top = int(round((image_height - crop_height) / 2.0))
|
||||
crop_left = int(round((image_width - crop_width) / 2.0))
|
||||
return crop(img, crop_top, crop_left, crop_height, crop_width)
|
||||
|
||||
|
||||
def hflip(img):
|
||||
"""Horizontally flips the given PIL Image.
|
||||
|
||||
Args:
|
||||
img (PIL.Image): Image to be flipped.
|
||||
|
||||
Returns:
|
||||
PIL.Image: Horizontally flipped image.
|
||||
|
||||
"""
|
||||
|
||||
return img.transpose(Image.FLIP_LEFT_RIGHT)
|
||||
|
||||
|
||||
def vflip(img):
|
||||
"""Vertically flips the given PIL Image.
|
||||
|
||||
Args:
|
||||
img (PIL.Image): Image to be flipped.
|
||||
|
||||
Returns:
|
||||
PIL.Image: Vertically flipped image.
|
||||
|
||||
"""
|
||||
|
||||
return img.transpose(Image.FLIP_TOP_BOTTOM)
|
||||
|
||||
|
||||
def adjust_brightness(img, brightness_factor):
|
||||
"""Adjusts brightness of an Image.
|
||||
|
||||
Args:
|
||||
img (PIL.Image): PIL Image to be adjusted.
|
||||
brightness_factor (float): How much to adjust the brightness. Can be
|
||||
any non negative number. 0 gives a black image, 1 gives the
|
||||
original image while 2 increases the brightness by a factor of 2.
|
||||
|
||||
Returns:
|
||||
PIL.Image: Brightness adjusted image.
|
||||
|
||||
"""
|
||||
|
||||
enhancer = ImageEnhance.Brightness(img)
|
||||
img = enhancer.enhance(brightness_factor)
|
||||
return img
|
||||
|
||||
|
||||
def adjust_contrast(img, contrast_factor):
|
||||
"""Adjusts contrast of an Image.
|
||||
|
||||
Args:
|
||||
img (PIL.Image): PIL Image to be adjusted.
|
||||
contrast_factor (float): How much to adjust the contrast. Can be any
|
||||
non negative number. 0 gives a solid gray image, 1 gives the
|
||||
original image while 2 increases the contrast by a factor of 2.
|
||||
|
||||
Returns:
|
||||
PIL.Image: Contrast adjusted image.
|
||||
|
||||
"""
|
||||
|
||||
enhancer = ImageEnhance.Contrast(img)
|
||||
img = enhancer.enhance(contrast_factor)
|
||||
return img
|
||||
|
||||
|
||||
def adjust_saturation(img, saturation_factor):
|
||||
"""Adjusts color saturation of an image.
|
||||
|
||||
Args:
|
||||
img (PIL.Image): PIL Image to be adjusted.
|
||||
saturation_factor (float): How much to adjust the saturation. 0 will
|
||||
give a black and white image, 1 will give the original image while
|
||||
2 will enhance the saturation by a factor of 2.
|
||||
|
||||
Returns:
|
||||
PIL.Image: Saturation adjusted image.
|
||||
|
||||
"""
|
||||
|
||||
enhancer = ImageEnhance.Color(img)
|
||||
img = enhancer.enhance(saturation_factor)
|
||||
return img
|
||||
|
||||
|
||||
def adjust_hue(img, hue_factor):
|
||||
"""Adjusts hue of an image.
|
||||
|
||||
The image hue is adjusted by converting the image to HSV and
|
||||
cyclically shifting the intensities in the hue channel (H).
|
||||
The image is then converted back to original image mode.
|
||||
|
||||
`hue_factor` is the amount of shift in H channel and must be in the
|
||||
interval `[-0.5, 0.5]`.
|
||||
|
||||
Args:
|
||||
img (PIL.Image): PIL Image to be adjusted.
|
||||
hue_factor (float): How much to shift the hue channel. Should be in
|
||||
[-0.5, 0.5]. 0.5 and -0.5 give complete reversal of hue channel in
|
||||
HSV space in positive and negative direction respectively.
|
||||
0 means no shift. Therefore, both -0.5 and 0.5 will give an image
|
||||
with complementary colors while 0 gives the original image.
|
||||
|
||||
Returns:
|
||||
PIL.Image: Hue adjusted image.
|
||||
|
||||
"""
|
||||
if not (-0.5 <= hue_factor <= 0.5):
|
||||
raise ValueError(f'hue_factor:{hue_factor} is not in [-0.5, 0.5].')
|
||||
|
||||
input_mode = img.mode
|
||||
if input_mode in {'L', '1', 'I', 'F'}:
|
||||
return img
|
||||
|
||||
h, s, v = img.convert('HSV').split()
|
||||
|
||||
np_h = np.array(h, dtype=np.uint8)
|
||||
np_h = np_h.astype(np.int16)
|
||||
np_h = (np_h + int(hue_factor * 255)) % 256
|
||||
np_h = np_h.astype(np.uint8)
|
||||
h = Image.fromarray(np_h, 'L')
|
||||
img = Image.merge('HSV', (h, s, v)).convert(input_mode)
|
||||
return img
|
||||
|
||||
|
||||
def affine(img, matrix, interpolation="nearest", fill=0):
|
||||
"""Affine the image by matrix.
|
||||
|
||||
Args:
|
||||
img (PIL.Image): Image to be affined.
|
||||
matrix (float or int): Affine matrix.
|
||||
interpolation (str, optional): Interpolation method. If omitted, or if the
|
||||
image has only one channel, it is set to PIL.Image.NEAREST . when use pil backend,
|
||||
support method are as following:
|
||||
- "nearest": Image.NEAREST,
|
||||
- "bilinear": Image.BILINEAR,
|
||||
- "bicubic": Image.BICUBIC
|
||||
fill (3-tuple or int): RGB pixel fill value for area outside the affined image.
|
||||
If int, it is used for all channels respectively.
|
||||
|
||||
Returns:
|
||||
PIL.Image: Affined image.
|
||||
|
||||
"""
|
||||
if isinstance(fill, int):
|
||||
fill = tuple([fill] * 3)
|
||||
|
||||
return img.transform(
|
||||
img.size,
|
||||
Image.AFFINE,
|
||||
matrix,
|
||||
_pil_interp_from_str[interpolation],
|
||||
fill,
|
||||
)
|
||||
|
||||
|
||||
def rotate(
|
||||
img, angle, interpolation="nearest", expand=False, center=None, fill=0
|
||||
):
|
||||
"""Rotates the image by angle.
|
||||
|
||||
Args:
|
||||
img (PIL.Image): Image to be rotated.
|
||||
angle (float or int): In degrees degrees counter clockwise order.
|
||||
interpolation (str, optional): Interpolation method. If omitted, or if the
|
||||
image has only one channel, it is set to PIL.Image.NEAREST . when use pil backend,
|
||||
support method are as following:
|
||||
- "nearest": Image.NEAREST,
|
||||
- "bilinear": Image.BILINEAR,
|
||||
- "bicubic": Image.BICUBIC
|
||||
expand (bool, optional): Optional expansion flag.
|
||||
If true, expands the output image to make it large enough to hold the entire rotated image.
|
||||
If false or omitted, make the output image the same size as the input image.
|
||||
Note that the expand flag assumes rotation around the center and no translation.
|
||||
center (2-tuple, optional): Optional center of rotation.
|
||||
Origin is the upper left corner.
|
||||
Default is the center of the image.
|
||||
fill (3-tuple or int): RGB pixel fill value for area outside the rotated image.
|
||||
If int, it is used for all channels respectively.
|
||||
|
||||
Returns:
|
||||
PIL.Image: Rotated image.
|
||||
|
||||
"""
|
||||
|
||||
if isinstance(fill, int):
|
||||
fill = tuple([fill] * 3)
|
||||
|
||||
return img.rotate(
|
||||
angle,
|
||||
_pil_interp_from_str[interpolation],
|
||||
expand,
|
||||
center,
|
||||
fillcolor=fill,
|
||||
)
|
||||
|
||||
|
||||
def perspective(img, coeffs, interpolation="nearest", fill=0):
|
||||
"""Perspective the image.
|
||||
|
||||
Args:
|
||||
img (PIL.Image): Image to be perspectived.
|
||||
coeffs (list[float]): coefficients (a, b, c, d, e, f, g, h) of the perspective transforms.
|
||||
interpolation (str, optional): Interpolation method. If omitted, or if the
|
||||
image has only one channel, it is set to PIL.Image.NEAREST . when use pil backend,
|
||||
support method are as following:
|
||||
- "nearest": Image.NEAREST,
|
||||
- "bilinear": Image.BILINEAR,
|
||||
- "bicubic": Image.BICUBIC
|
||||
fill (3-tuple or int): RGB pixel fill value for area outside the rotated image.
|
||||
If int, it is used for all channels respectively.
|
||||
|
||||
Returns:
|
||||
PIL.Image: Perspectived image.
|
||||
|
||||
"""
|
||||
|
||||
if isinstance(fill, int):
|
||||
fill = tuple([fill] * 3)
|
||||
|
||||
return img.transform(
|
||||
img.size,
|
||||
Image.PERSPECTIVE,
|
||||
coeffs,
|
||||
_pil_interp_from_str[interpolation],
|
||||
fill,
|
||||
)
|
||||
|
||||
|
||||
def to_grayscale(img, num_output_channels=1):
|
||||
"""Converts image to grayscale version of image.
|
||||
|
||||
Args:
|
||||
img (PIL.Image): Image to be converted to grayscale.
|
||||
backend (str, optional): The image process backend type. Options are `pil`,
|
||||
`cv2`. Default: 'pil'.
|
||||
|
||||
Returns:
|
||||
PIL.Image: Grayscale version of the image.
|
||||
if num_output_channels = 1 : returned image is single channel
|
||||
|
||||
if num_output_channels = 3 : returned image is 3 channel with r = g = b
|
||||
|
||||
"""
|
||||
|
||||
if num_output_channels == 1:
|
||||
img = img.convert('L')
|
||||
elif num_output_channels == 3:
|
||||
img = img.convert('L')
|
||||
np_img = np.array(img, dtype=np.uint8)
|
||||
np_img = np.dstack([np_img, np_img, np_img])
|
||||
img = Image.fromarray(np_img, 'RGB')
|
||||
else:
|
||||
raise ValueError('num_output_channels should be either 1 or 3')
|
||||
|
||||
return img
|
||||
|
||||
|
||||
def erase(img, i, j, h, w, v, inplace=False):
|
||||
"""Erase the pixels of selected area in input image with given value. PIL format is
|
||||
not support inplace.
|
||||
|
||||
Args:
|
||||
img (PIL.Image): input image, which shape is (C, H, W).
|
||||
i (int): y coordinate of the top-left point of erased region.
|
||||
j (int): x coordinate of the top-left point of erased region.
|
||||
h (int): Height of the erased region.
|
||||
w (int): Width of the erased region.
|
||||
v (np.array): value used to replace the pixels in erased region.
|
||||
inplace (bool, optional): Whether this transform is inplace. Default: False.
|
||||
|
||||
Returns:
|
||||
PIL.Image: Erased image.
|
||||
|
||||
"""
|
||||
np_img = np.array(img, dtype=np.uint8)
|
||||
np_img[i : i + h, j : j + w, ...] = v
|
||||
img = Image.fromarray(np_img, 'RGB')
|
||||
return img
|
||||
@@ -0,0 +1,956 @@
|
||||
# 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 math
|
||||
import numbers
|
||||
|
||||
import numpy as np
|
||||
|
||||
import paddle
|
||||
import paddle.nn.functional as F
|
||||
|
||||
from ...base.framework import Variable
|
||||
from ...base.libpaddle.pir import Value
|
||||
|
||||
__all__ = []
|
||||
|
||||
|
||||
def _assert_image_tensor(img, data_format):
|
||||
if (
|
||||
not isinstance(img, (paddle.Tensor, Variable, Value))
|
||||
or img.ndim < 3
|
||||
or img.ndim > 4
|
||||
or data_format.lower() not in ('chw', 'hwc')
|
||||
):
|
||||
raise RuntimeError(
|
||||
f'not support [type={type(img)}, ndim={img.ndim}, data_format={data_format}] paddle image'
|
||||
)
|
||||
|
||||
|
||||
def _get_image_h_axis(data_format):
|
||||
if data_format.lower() == 'chw':
|
||||
return -2
|
||||
elif data_format.lower() == 'hwc':
|
||||
return -3
|
||||
|
||||
|
||||
def _get_image_w_axis(data_format):
|
||||
if data_format.lower() == 'chw':
|
||||
return -1
|
||||
elif data_format.lower() == 'hwc':
|
||||
return -2
|
||||
|
||||
|
||||
def _get_image_c_axis(data_format):
|
||||
if data_format.lower() == 'chw':
|
||||
return -3
|
||||
elif data_format.lower() == 'hwc':
|
||||
return -1
|
||||
|
||||
|
||||
def _get_image_n_axis(data_format):
|
||||
if len(data_format) == 3:
|
||||
return None
|
||||
elif len(data_format) == 4:
|
||||
return 0
|
||||
|
||||
|
||||
def _is_channel_last(data_format):
|
||||
return _get_image_c_axis(data_format) == -1
|
||||
|
||||
|
||||
def _is_channel_first(data_format):
|
||||
return _get_image_c_axis(data_format) == -3
|
||||
|
||||
|
||||
def _get_image_num_batches(img, data_format):
|
||||
if _get_image_n_axis(data_format):
|
||||
return img.shape[_get_image_n_axis(data_format)]
|
||||
return None
|
||||
|
||||
|
||||
def _get_image_num_channels(img, data_format):
|
||||
return img.shape[_get_image_c_axis(data_format)]
|
||||
|
||||
|
||||
def _get_image_size(img, data_format):
|
||||
return (
|
||||
img.shape[_get_image_w_axis(data_format)],
|
||||
img.shape[_get_image_h_axis(data_format)],
|
||||
)
|
||||
|
||||
|
||||
def _rgb_to_hsv(img):
|
||||
"""Convert a image Tensor from RGB to HSV. This implementation is based on Pillow (
|
||||
https://github.com/python-pillow/Pillow/blob/main/src/libImaging/Convert.c)
|
||||
"""
|
||||
maxc = img.max(axis=-3)
|
||||
minc = img.min(axis=-3)
|
||||
|
||||
is_equal = paddle.equal(maxc, minc)
|
||||
one_divisor = paddle.ones_like(maxc)
|
||||
c_delta = maxc - minc
|
||||
# s is 0 when maxc == minc, set the divisor to 1 to avoid zero divide.
|
||||
s = c_delta / paddle.where(is_equal, one_divisor, maxc)
|
||||
|
||||
r, g, b = img.unbind(axis=-3)
|
||||
c_delta_divisor = paddle.where(is_equal, one_divisor, c_delta)
|
||||
# when maxc == minc, there is r == g == b, set the divisor to 1 to avoid zero divide.
|
||||
rc = (maxc - r) / c_delta_divisor
|
||||
gc = (maxc - g) / c_delta_divisor
|
||||
bc = (maxc - b) / c_delta_divisor
|
||||
|
||||
hr = (maxc == r).astype(maxc.dtype) * (bc - gc)
|
||||
hg = ((maxc == g) & (maxc != r)).astype(maxc.dtype) * (rc - bc + 2.0)
|
||||
hb = ((maxc != r) & (maxc != g)).astype(maxc.dtype) * (gc - rc + 4.0)
|
||||
h = (hr + hg + hb) / 6.0 + 1.0
|
||||
h = h - h.trunc()
|
||||
return paddle.stack([h, s, maxc], axis=-3)
|
||||
|
||||
|
||||
def _hsv_to_rgb(img):
|
||||
"""Convert a image Tensor from HSV to RGB."""
|
||||
h, s, v = img.unbind(axis=-3)
|
||||
f = h * 6.0
|
||||
i = paddle.floor(f)
|
||||
f = f - i
|
||||
i = i.astype(paddle.int32) % 6
|
||||
|
||||
p = paddle.clip(v * (1.0 - s), 0.0, 1.0)
|
||||
q = paddle.clip(v * (1.0 - s * f), 0.0, 1.0)
|
||||
t = paddle.clip(v * (1.0 - s * (1.0 - f)), 0.0, 1.0)
|
||||
|
||||
mask = paddle.equal(
|
||||
i.unsqueeze(axis=-3),
|
||||
paddle.arange(6, dtype=i.dtype).reshape((-1, 1, 1)),
|
||||
).astype(img.dtype)
|
||||
matrix = paddle.stack(
|
||||
[
|
||||
paddle.stack([v, q, p, p, t, v], axis=-3),
|
||||
paddle.stack([t, v, v, q, p, p], axis=-3),
|
||||
paddle.stack([p, p, t, v, v, q], axis=-3),
|
||||
],
|
||||
axis=-4,
|
||||
)
|
||||
return paddle.einsum("...ijk, ...xijk -> ...xjk", mask, matrix)
|
||||
|
||||
|
||||
def _blend_images(img1, img2, ratio):
|
||||
max_value = 1.0 if paddle.is_floating_point(img1) else 255.0
|
||||
return (
|
||||
paddle.lerp(img2, img1, float(ratio))
|
||||
.clip(0, max_value)
|
||||
.astype(img1.dtype)
|
||||
)
|
||||
|
||||
|
||||
def normalize(img, mean, std, data_format='CHW'):
|
||||
"""Normalizes a tensor image given mean and standard deviation.
|
||||
|
||||
Args:
|
||||
img (paddle.Tensor): input data to be normalized.
|
||||
mean (list|tuple): Sequence of means for each channel.
|
||||
std (list|tuple): Sequence of standard deviations for each channel.
|
||||
data_format (str, optional): Data format of img, should be 'HWC' or
|
||||
'CHW'. Default: 'CHW'.
|
||||
|
||||
Returns:
|
||||
Tensor: Normalized mage.
|
||||
|
||||
"""
|
||||
_assert_image_tensor(img, data_format)
|
||||
|
||||
mean = paddle.to_tensor(mean, place=img.place)
|
||||
std = paddle.to_tensor(std, place=img.place)
|
||||
|
||||
if _is_channel_first(data_format):
|
||||
mean = mean.reshape([-1, 1, 1])
|
||||
std = std.reshape([-1, 1, 1])
|
||||
|
||||
return (img - mean) / std
|
||||
|
||||
|
||||
def to_grayscale(img, num_output_channels=1, data_format='CHW'):
|
||||
"""Converts image to grayscale version of image.
|
||||
|
||||
Args:
|
||||
img (paddle.Tensor): Image to be converted to grayscale.
|
||||
num_output_channels (int, optional[1, 3]):
|
||||
if num_output_channels = 1 : returned image is single channel
|
||||
if num_output_channels = 3 : returned image is 3 channel
|
||||
data_format (str, optional): Data format of img, should be 'HWC' or
|
||||
'CHW'. Default: 'CHW'.
|
||||
|
||||
Returns:
|
||||
paddle.Tensor: Grayscale version of the image.
|
||||
"""
|
||||
_assert_image_tensor(img, data_format)
|
||||
|
||||
if num_output_channels not in (1, 3):
|
||||
raise ValueError('num_output_channels should be either 1 or 3')
|
||||
|
||||
rgb_weights = paddle.to_tensor(
|
||||
[0.2989, 0.5870, 0.1140], place=img.place
|
||||
).astype(img.dtype)
|
||||
|
||||
if _is_channel_first(data_format):
|
||||
rgb_weights = rgb_weights.reshape((-1, 1, 1))
|
||||
|
||||
_c_index = _get_image_c_axis(data_format)
|
||||
|
||||
img = (img * rgb_weights).sum(axis=_c_index, keepdim=True)
|
||||
_shape = img.shape
|
||||
_shape[_c_index] = num_output_channels
|
||||
|
||||
return img.expand(_shape)
|
||||
|
||||
|
||||
def _affine_grid(theta, w, h, ow, oh):
|
||||
d = 0.5
|
||||
base_grid = paddle.ones((1, oh, ow, 3), dtype=theta.dtype)
|
||||
|
||||
x_grid = paddle.linspace(-ow * 0.5 + d, ow * 0.5 + d - 1, ow)
|
||||
|
||||
if paddle.in_dynamic_mode():
|
||||
y_grid = paddle.linspace(
|
||||
-oh * 0.5 + d, oh * 0.5 + d - 1, oh
|
||||
).unsqueeze_(-1)
|
||||
base_grid[..., 0] = x_grid
|
||||
base_grid[..., 1] = y_grid
|
||||
tmp = paddle.to_tensor([0.5 * w, 0.5 * h])
|
||||
else:
|
||||
# To eliminate the warning:
|
||||
# In static mode, unsqueeze_() is the same as unsqueeze() and does not perform inplace operation.
|
||||
y_grid = paddle.linspace(-oh * 0.5 + d, oh * 0.5 + d - 1, oh).unsqueeze(
|
||||
-1
|
||||
)
|
||||
base_grid = paddle.static.setitem(base_grid, (..., 0), x_grid)
|
||||
base_grid = paddle.static.setitem(base_grid, (..., 1), y_grid)
|
||||
tmp = paddle.assign(np.array([0.5 * w, 0.5 * h], dtype="float32"))
|
||||
|
||||
scaled_theta = theta.transpose((0, 2, 1)) / tmp
|
||||
output_grid = base_grid.reshape((1, oh * ow, 3)).bmm(scaled_theta)
|
||||
|
||||
return output_grid.reshape((1, oh, ow, 2))
|
||||
|
||||
|
||||
def _grid_transform(img, grid, mode, fill):
|
||||
if img.shape[0] > 1:
|
||||
grid = grid.expand(
|
||||
shape=[img.shape[0], grid.shape[1], grid.shape[2], grid.shape[3]]
|
||||
)
|
||||
|
||||
if fill is not None:
|
||||
dummy = paddle.ones(
|
||||
(img.shape[0], 1, img.shape[2], img.shape[3]), dtype=img.dtype
|
||||
)
|
||||
img = paddle.concat((img, dummy), axis=1)
|
||||
|
||||
img = F.grid_sample(
|
||||
img, grid, mode=mode, padding_mode="zeros", align_corners=False
|
||||
)
|
||||
|
||||
# Fill with required color
|
||||
if fill is not None:
|
||||
mask = img[:, -1:, :, :] # n 1 h w
|
||||
img = img[:, :-1, :, :] # n c h w
|
||||
mask = mask.tile([1, img.shape[1], 1, 1])
|
||||
len_fill = len(fill) if isinstance(fill, (tuple, list)) else 1
|
||||
|
||||
if paddle.in_dynamic_mode():
|
||||
fill_img = (
|
||||
paddle.to_tensor(fill)
|
||||
.reshape((1, len_fill, 1, 1))
|
||||
.astype(img.dtype)
|
||||
.expand_as(img)
|
||||
)
|
||||
else:
|
||||
fill = np.array(fill).reshape(len_fill).astype("float32")
|
||||
fill_img = paddle.ones_like(img) * paddle.assign(fill).reshape(
|
||||
[1, len_fill, 1, 1]
|
||||
)
|
||||
|
||||
if mode == 'nearest':
|
||||
mask = paddle.cast(mask < 0.5, img.dtype)
|
||||
img = img * (1.0 - mask) + mask * fill_img
|
||||
else: # 'bilinear'
|
||||
img = img * mask + (1.0 - mask) * fill_img
|
||||
|
||||
return img
|
||||
|
||||
|
||||
def affine(img, matrix, interpolation="nearest", fill=None, data_format='CHW'):
|
||||
"""Affine to the image by matrix.
|
||||
|
||||
Args:
|
||||
img (paddle.Tensor): Image to be rotated.
|
||||
matrix (float or int): Affine matrix.
|
||||
interpolation (str, optional): Interpolation method. If omitted, or if the
|
||||
image has only one channel, it is set NEAREST . when use pil backend,
|
||||
support method are as following:
|
||||
- "nearest"
|
||||
- "bilinear"
|
||||
- "bicubic"
|
||||
fill (3-tuple or int): RGB pixel fill value for area outside the rotated image.
|
||||
If int, it is used for all channels respectively.
|
||||
data_format (str, optional): Data format of img, should be 'HWC' or
|
||||
'CHW'. Default: 'CHW'.
|
||||
|
||||
Returns:
|
||||
paddle.Tensor: Affined image.
|
||||
|
||||
"""
|
||||
ndim = len(img.shape)
|
||||
if ndim == 3:
|
||||
img = img.unsqueeze(0)
|
||||
|
||||
img = img if data_format.lower() == 'chw' else img.transpose((0, 3, 1, 2))
|
||||
|
||||
matrix = paddle.to_tensor(matrix, place=img.place)
|
||||
matrix = matrix.reshape((1, 2, 3))
|
||||
shape = img.shape
|
||||
|
||||
grid = _affine_grid(
|
||||
matrix, w=shape[-1], h=shape[-2], ow=shape[-1], oh=shape[-2]
|
||||
)
|
||||
|
||||
if isinstance(fill, int):
|
||||
fill = tuple([fill] * 3)
|
||||
|
||||
out = _grid_transform(img, grid, mode=interpolation, fill=fill)
|
||||
|
||||
out = out if data_format.lower() == 'chw' else out.transpose((0, 2, 3, 1))
|
||||
out = out.squeeze(0) if ndim == 3 else out
|
||||
|
||||
return out
|
||||
|
||||
|
||||
def rotate(
|
||||
img,
|
||||
angle,
|
||||
interpolation='nearest',
|
||||
expand=False,
|
||||
center=None,
|
||||
fill=None,
|
||||
data_format='CHW',
|
||||
):
|
||||
"""Rotates the image by angle.
|
||||
|
||||
Args:
|
||||
img (paddle.Tensor): Image to be rotated.
|
||||
angle (float or int): In degrees degrees counter clockwise order.
|
||||
interpolation (str, optional): Interpolation method. If omitted, or if the
|
||||
image has only one channel, it is set NEAREST . when use pil backend,
|
||||
support method are as following:
|
||||
- "nearest"
|
||||
- "bilinear"
|
||||
- "bicubic"
|
||||
expand (bool, optional): Optional expansion flag.
|
||||
If true, expands the output image to make it large enough to hold the entire rotated image.
|
||||
If false or omitted, make the output image the same size as the input image.
|
||||
Note that the expand flag assumes rotation around the center and no translation.
|
||||
center (2-tuple, optional): Optional center of rotation.
|
||||
Origin is the upper left corner.
|
||||
Default is the center of the image.
|
||||
fill (3-tuple or int): RGB pixel fill value for area outside the rotated image.
|
||||
If int, it is used for all channels respectively.
|
||||
|
||||
Returns:
|
||||
paddle.Tensor: Rotated image.
|
||||
|
||||
"""
|
||||
|
||||
angle = -angle % 360
|
||||
img = img.unsqueeze(0)
|
||||
|
||||
# n, c, h, w = img.shape
|
||||
w, h = _get_image_size(img, data_format=data_format)
|
||||
|
||||
img = img if data_format.lower() == 'chw' else img.transpose((0, 3, 1, 2))
|
||||
|
||||
post_trans = [0, 0]
|
||||
|
||||
if center is None:
|
||||
rotn_center = [0, 0]
|
||||
else:
|
||||
rotn_center = [(p - s * 0.5) for p, s in zip(center, [w, h])]
|
||||
|
||||
if paddle.in_dynamic_mode():
|
||||
angle = math.radians(angle)
|
||||
matrix = [
|
||||
math.cos(angle),
|
||||
math.sin(angle),
|
||||
0.0,
|
||||
-math.sin(angle),
|
||||
math.cos(angle),
|
||||
0.0,
|
||||
]
|
||||
matrix = paddle.to_tensor(matrix, place=img.place)
|
||||
|
||||
matrix[2] += (
|
||||
matrix[0] * (-rotn_center[0] - post_trans[0])
|
||||
+ matrix[1] * (-rotn_center[1] - post_trans[1])
|
||||
+ rotn_center[0]
|
||||
)
|
||||
matrix[5] += (
|
||||
matrix[3] * (-rotn_center[0] - post_trans[0])
|
||||
+ matrix[4] * (-rotn_center[1] - post_trans[1])
|
||||
+ rotn_center[1]
|
||||
)
|
||||
else:
|
||||
angle = angle / 180 * math.pi
|
||||
matrix = paddle.concat(
|
||||
[
|
||||
paddle.cos(angle),
|
||||
paddle.sin(angle),
|
||||
paddle.zeros([1]),
|
||||
-paddle.sin(angle),
|
||||
paddle.cos(angle),
|
||||
paddle.zeros([1]),
|
||||
]
|
||||
)
|
||||
matrix = paddle.static.setitem(
|
||||
matrix,
|
||||
2,
|
||||
matrix[2]
|
||||
+ matrix[0] * (-rotn_center[0] - post_trans[0])
|
||||
+ matrix[1] * (-rotn_center[1] - post_trans[1])
|
||||
+ rotn_center[0],
|
||||
)
|
||||
matrix = paddle.static.setitem(
|
||||
matrix,
|
||||
5,
|
||||
matrix[5]
|
||||
+ matrix[3] * (-rotn_center[0] - post_trans[0])
|
||||
+ matrix[4] * (-rotn_center[1] - post_trans[1])
|
||||
+ rotn_center[1],
|
||||
)
|
||||
|
||||
matrix = matrix.reshape((1, 2, 3))
|
||||
|
||||
if expand:
|
||||
# calculate output size
|
||||
if paddle.in_dynamic_mode():
|
||||
corners = paddle.to_tensor(
|
||||
[
|
||||
[-0.5 * w, -0.5 * h, 1.0],
|
||||
[-0.5 * w, 0.5 * h, 1.0],
|
||||
[0.5 * w, 0.5 * h, 1.0],
|
||||
[0.5 * w, -0.5 * h, 1.0],
|
||||
],
|
||||
place=matrix.place,
|
||||
).astype(matrix.dtype)
|
||||
else:
|
||||
corners = paddle.assign(
|
||||
[
|
||||
[-0.5 * w, -0.5 * h, 1.0],
|
||||
[-0.5 * w, 0.5 * h, 1.0],
|
||||
[0.5 * w, 0.5 * h, 1.0],
|
||||
[0.5 * w, -0.5 * h, 1.0],
|
||||
],
|
||||
).astype(matrix.dtype)
|
||||
|
||||
_pos = (
|
||||
corners.reshape((1, -1, 3))
|
||||
.bmm(matrix.transpose((0, 2, 1)))
|
||||
.reshape((1, -1, 2))
|
||||
)
|
||||
_min = _pos.min(axis=-2).floor()
|
||||
_max = _pos.max(axis=-2).ceil()
|
||||
|
||||
npos = _max - _min
|
||||
nw = npos[0][0]
|
||||
nh = npos[0][1]
|
||||
|
||||
if paddle.in_dynamic_mode():
|
||||
ow, oh = int(nw), int(nh)
|
||||
else:
|
||||
ow, oh = nw.astype("int32"), nh.astype("int32")
|
||||
|
||||
else:
|
||||
ow, oh = w, h
|
||||
|
||||
grid = _affine_grid(matrix, w, h, ow, oh)
|
||||
|
||||
out = _grid_transform(img, grid, mode=interpolation, fill=fill)
|
||||
|
||||
out = out if data_format.lower() == 'chw' else out.transpose((0, 2, 3, 1))
|
||||
|
||||
return out.squeeze(0)
|
||||
|
||||
|
||||
def _perspective_grid(img, coeffs, ow, oh, dtype):
|
||||
theta1 = coeffs[:6].reshape([1, 2, 3])
|
||||
tmp = paddle.tile(coeffs[6:].reshape([1, 2]), repeat_times=[2, 1])
|
||||
dummy = paddle.ones((2, 1), dtype=dtype)
|
||||
theta2 = paddle.concat((tmp, dummy), axis=1).unsqueeze(0)
|
||||
|
||||
d = 0.5
|
||||
base_grid = paddle.ones((1, oh, ow, 3), dtype=dtype)
|
||||
|
||||
x_grid = paddle.linspace(d, ow * 1.0 + d - 1.0, ow)
|
||||
base_grid[..., 0] = x_grid
|
||||
y_grid = paddle.linspace(d, oh * 1.0 + d - 1.0, oh).unsqueeze_(-1)
|
||||
base_grid[..., 1] = y_grid
|
||||
|
||||
scaled_theta1 = theta1.transpose((0, 2, 1)) / paddle.to_tensor(
|
||||
[0.5 * ow, 0.5 * oh]
|
||||
)
|
||||
output_grid1 = base_grid.reshape((1, oh * ow, 3)).bmm(scaled_theta1)
|
||||
output_grid2 = base_grid.reshape((1, oh * ow, 3)).bmm(
|
||||
theta2.transpose((0, 2, 1))
|
||||
)
|
||||
|
||||
output_grid = output_grid1 / output_grid2 - 1.0
|
||||
return output_grid.reshape((1, oh, ow, 2))
|
||||
|
||||
|
||||
def perspective(
|
||||
img, coeffs, interpolation="nearest", fill=None, data_format='CHW'
|
||||
):
|
||||
"""Perspective the image.
|
||||
|
||||
Args:
|
||||
img (paddle.Tensor): Image to be rotated.
|
||||
coeffs (list[float]): coefficients (a, b, c, d, e, f, g, h) of the perspective transforms.
|
||||
interpolation (str, optional): Interpolation method. If omitted, or if the
|
||||
image has only one channel, it is set NEAREST. When use pil backend,
|
||||
support method are as following:
|
||||
- "nearest"
|
||||
- "bilinear"
|
||||
- "bicubic"
|
||||
fill (3-tuple or int): RGB pixel fill value for area outside the rotated image.
|
||||
If int, it is used for all channels respectively.
|
||||
|
||||
Returns:
|
||||
paddle.Tensor: Perspectived image.
|
||||
|
||||
"""
|
||||
|
||||
ndim = len(img.shape)
|
||||
if ndim == 3:
|
||||
img = img.unsqueeze(0)
|
||||
|
||||
img = img if data_format.lower() == 'chw' else img.transpose((0, 3, 1, 2))
|
||||
ow, oh = img.shape[-1], img.shape[-2]
|
||||
dtype = img.dtype if paddle.is_floating_point(img) else paddle.float32
|
||||
|
||||
coeffs = paddle.to_tensor(coeffs, place=img.place)
|
||||
grid = _perspective_grid(img, coeffs, ow=ow, oh=oh, dtype=dtype)
|
||||
out = _grid_transform(img, grid, mode=interpolation, fill=fill)
|
||||
|
||||
out = out if data_format.lower() == 'chw' else out.transpose((0, 2, 3, 1))
|
||||
out = out.squeeze(0) if ndim == 3 else out
|
||||
|
||||
return out
|
||||
|
||||
|
||||
def vflip(img, data_format='CHW'):
|
||||
"""Vertically flips the given paddle tensor.
|
||||
|
||||
Args:
|
||||
img (paddle.Tensor): Image to be flipped.
|
||||
data_format (str, optional): Data format of img, should be 'HWC' or
|
||||
'CHW'. Default: 'CHW'.
|
||||
|
||||
Returns:
|
||||
paddle.Tensor: Vertically flipped image.
|
||||
|
||||
"""
|
||||
_assert_image_tensor(img, data_format)
|
||||
|
||||
h_axis = _get_image_h_axis(data_format)
|
||||
|
||||
return img.flip(axis=[h_axis])
|
||||
|
||||
|
||||
def hflip(img, data_format='CHW'):
|
||||
"""Horizontally flips the given paddle.Tensor Image.
|
||||
|
||||
Args:
|
||||
img (paddle.Tensor): Image to be flipped.
|
||||
data_format (str, optional): Data format of img, should be 'HWC' or
|
||||
'CHW'. Default: 'CHW'.
|
||||
|
||||
Returns:
|
||||
paddle.Tensor: Horizontally flipped image.
|
||||
|
||||
"""
|
||||
_assert_image_tensor(img, data_format)
|
||||
|
||||
w_axis = _get_image_w_axis(data_format)
|
||||
|
||||
return img.flip(axis=[w_axis])
|
||||
|
||||
|
||||
def crop(img, top, left, height, width, data_format='CHW'):
|
||||
"""Crops the given paddle.Tensor Image.
|
||||
|
||||
Args:
|
||||
img (paddle.Tensor): Image to be cropped. (0,0) denotes the top left
|
||||
corner of the image.
|
||||
top (int): Vertical component of the top left corner of the crop box.
|
||||
left (int): Horizontal component of the top left corner of the crop box.
|
||||
height (int): Height of the crop box.
|
||||
width (int): Width of the crop box.
|
||||
data_format (str, optional): Data format of img, should be 'HWC' or
|
||||
'CHW'. Default: 'CHW'.
|
||||
Returns:
|
||||
paddle.Tensor: Cropped image.
|
||||
|
||||
"""
|
||||
_assert_image_tensor(img, data_format)
|
||||
|
||||
if _is_channel_first(data_format):
|
||||
return img[:, top : top + height, left : left + width]
|
||||
else:
|
||||
return img[top : top + height, left : left + width, :]
|
||||
|
||||
|
||||
def erase(img, i, j, h, w, v, inplace=False):
|
||||
"""Erase the pixels of selected area in input Tensor image with given value.
|
||||
|
||||
Args:
|
||||
img (paddle.Tensor): input Tensor image.
|
||||
i (int): y coordinate of the top-left point of erased region.
|
||||
j (int): x coordinate of the top-left point of erased region.
|
||||
h (int): Height of the erased region.
|
||||
w (int): Width of the erased region.
|
||||
v (paddle.Tensor): value used to replace the pixels in erased region.
|
||||
inplace (bool, optional): Whether this transform is inplace. Default: False.
|
||||
|
||||
Returns:
|
||||
paddle.Tensor: Erased image.
|
||||
|
||||
"""
|
||||
_assert_image_tensor(img, 'CHW')
|
||||
if not inplace:
|
||||
img = img.clone()
|
||||
|
||||
if paddle.in_dynamic_mode():
|
||||
img[..., i : i + h, j : j + w] = v
|
||||
else:
|
||||
img = paddle.static.setitem(
|
||||
img, (..., slice(i, i + h), slice(j, j + w)), v
|
||||
)
|
||||
return img
|
||||
|
||||
|
||||
def center_crop(img, output_size, data_format='CHW'):
|
||||
"""Crops the given paddle.Tensor Image and resize it to desired size.
|
||||
|
||||
Args:
|
||||
img (paddle.Tensor): Image to be cropped. (0,0) denotes the top left corner of the image.
|
||||
output_size (sequence or int): (height, width) of the crop box. If int,
|
||||
it is used for both directions
|
||||
data_format (str, optional): Data format of img, should be 'HWC' or
|
||||
'CHW'. Default: 'CHW'.
|
||||
Returns:
|
||||
paddle.Tensor: Cropped image.
|
||||
|
||||
"""
|
||||
_assert_image_tensor(img, data_format)
|
||||
|
||||
if isinstance(output_size, numbers.Number):
|
||||
output_size = (int(output_size), int(output_size))
|
||||
|
||||
image_width, image_height = _get_image_size(img, data_format)
|
||||
crop_height, crop_width = output_size
|
||||
crop_top = int(round((image_height - crop_height) / 2.0))
|
||||
crop_left = int(round((image_width - crop_width) / 2.0))
|
||||
return crop(
|
||||
img,
|
||||
crop_top,
|
||||
crop_left,
|
||||
crop_height,
|
||||
crop_width,
|
||||
data_format=data_format,
|
||||
)
|
||||
|
||||
|
||||
def pad(img, padding, fill=0, padding_mode='constant', data_format='CHW'):
|
||||
"""
|
||||
Pads the given paddle.Tensor on all sides with specified padding mode and fill value.
|
||||
|
||||
Args:
|
||||
img (paddle.Tensor): Image to be padded.
|
||||
padding (int|list|tuple): Padding on each border. If a single int is provided this
|
||||
is used to pad all borders. If tuple of length 2 is provided this is the padding
|
||||
on left/right and top/bottom respectively. If a tuple of length 4 is provided
|
||||
this is the padding for the left, top, right and bottom borders
|
||||
respectively.
|
||||
fill (float, optional): Pixel fill value for constant fill. If a tuple of
|
||||
length 3, it is used to fill R, G, B channels respectively.
|
||||
This value is only used when the padding_mode is constant. Default: 0.
|
||||
padding_mode: Type of padding. Should be: constant, edge, reflect or symmetric. Default: 'constant'.
|
||||
|
||||
- constant: pads with a constant value, this value is specified with fill
|
||||
|
||||
- edge: pads with the last value on the edge of the image
|
||||
|
||||
- reflect: pads with reflection of image (without repeating the last value on the edge)
|
||||
|
||||
padding [1, 2, 3, 4] with 2 elements on both sides in reflect mode
|
||||
will result in [3, 2, 1, 2, 3, 4, 3, 2]
|
||||
|
||||
- symmetric: pads with reflection of image (repeating the last value on the edge)
|
||||
|
||||
padding [1, 2, 3, 4] with 2 elements on both sides in symmetric mode
|
||||
will result in [2, 1, 1, 2, 3, 4, 4, 3]
|
||||
|
||||
Returns:
|
||||
paddle.Tensor: Padded image.
|
||||
|
||||
"""
|
||||
_assert_image_tensor(img, data_format)
|
||||
|
||||
if not isinstance(padding, (numbers.Number, list, tuple)):
|
||||
raise TypeError('Got inappropriate padding arg')
|
||||
if not isinstance(fill, (numbers.Number, str, list, tuple)):
|
||||
raise TypeError('Got inappropriate fill arg')
|
||||
if not isinstance(padding_mode, str):
|
||||
raise TypeError('Got inappropriate padding_mode arg')
|
||||
|
||||
if isinstance(padding, (list, tuple)) and len(padding) not in [2, 4]:
|
||||
raise ValueError(
|
||||
"Padding must be an int or a 2, or 4 element tuple, not a "
|
||||
+ f"{len(padding)} element tuple"
|
||||
)
|
||||
|
||||
assert padding_mode in [
|
||||
'constant',
|
||||
'edge',
|
||||
'reflect',
|
||||
'symmetric',
|
||||
], 'Padding mode should be either constant, edge, reflect or symmetric'
|
||||
|
||||
if isinstance(padding, int):
|
||||
pad_left = pad_right = pad_top = pad_bottom = padding
|
||||
elif len(padding) == 2:
|
||||
pad_left = pad_right = padding[0]
|
||||
pad_top = pad_bottom = padding[1]
|
||||
else:
|
||||
pad_left = padding[0]
|
||||
pad_top = padding[1]
|
||||
pad_right = padding[2]
|
||||
pad_bottom = padding[3]
|
||||
|
||||
padding = [pad_left, pad_right, pad_top, pad_bottom]
|
||||
|
||||
if padding_mode == 'edge':
|
||||
padding_mode = 'replicate'
|
||||
elif padding_mode == 'symmetric':
|
||||
raise ValueError('Do not support symmetric mode')
|
||||
|
||||
img = img.unsqueeze(0)
|
||||
# 'constant', 'reflect', 'replicate', 'circular'
|
||||
img = F.pad(
|
||||
img,
|
||||
pad=padding,
|
||||
mode=padding_mode,
|
||||
value=float(fill),
|
||||
data_format='N' + data_format,
|
||||
)
|
||||
|
||||
return img.squeeze(0)
|
||||
|
||||
|
||||
def resize(img, size, interpolation='bilinear', data_format='CHW'):
|
||||
"""
|
||||
Resizes the image to given size
|
||||
|
||||
Args:
|
||||
input (paddle.Tensor): Image to be resized.
|
||||
size (int|list|tuple): Target size of input data, with (height, width) shape.
|
||||
interpolation (int|str, optional): Interpolation method. when use paddle backend,
|
||||
support method are as following:
|
||||
- "nearest"
|
||||
- "bilinear"
|
||||
- "bicubic"
|
||||
- "trilinear"
|
||||
- "area"
|
||||
- "linear"
|
||||
data_format (str, optional): paddle.Tensor format
|
||||
- 'CHW'
|
||||
- 'HWC'
|
||||
Returns:
|
||||
paddle.Tensor: Resized image.
|
||||
|
||||
"""
|
||||
_assert_image_tensor(img, data_format)
|
||||
|
||||
if not (
|
||||
isinstance(size, int)
|
||||
or (isinstance(size, (tuple, list)) and len(size) == 2)
|
||||
):
|
||||
raise TypeError(f'Got inappropriate size arg: {size}')
|
||||
|
||||
if isinstance(size, int):
|
||||
w, h = _get_image_size(img, data_format)
|
||||
# TODO(Aurelius84): In static graph mode, w and h will be -1 for dynamic shape.
|
||||
# We should consider to support this case in future.
|
||||
if w <= 0 or h <= 0:
|
||||
raise NotImplementedError(
|
||||
f"Not support while w<=0 or h<=0, but received w={w}, h={h}"
|
||||
)
|
||||
if (w <= h and w == size) or (h <= w and h == size):
|
||||
return img
|
||||
if w < h:
|
||||
ow = size
|
||||
oh = int(size * h / w)
|
||||
else:
|
||||
oh = size
|
||||
ow = int(size * w / h)
|
||||
else:
|
||||
oh, ow = size
|
||||
|
||||
img = img.unsqueeze(0)
|
||||
img = F.interpolate(
|
||||
img,
|
||||
size=(oh, ow),
|
||||
mode=interpolation.lower(),
|
||||
data_format='N' + data_format.upper(),
|
||||
)
|
||||
|
||||
return img.squeeze(0)
|
||||
|
||||
|
||||
def adjust_brightness(img, brightness_factor):
|
||||
"""Adjusts brightness of an Image.
|
||||
|
||||
Args:
|
||||
img (paddle.Tensor): Image to be adjusted.
|
||||
brightness_factor (float): How much to adjust the brightness. Can be
|
||||
any non negative number. 0 gives a black image, 1 gives the
|
||||
original image while 2 increases the brightness by a factor of 2.
|
||||
|
||||
Returns:
|
||||
paddle.Tensor: Brightness adjusted image.
|
||||
|
||||
"""
|
||||
_assert_image_tensor(img, 'CHW')
|
||||
assert brightness_factor >= 0, "brightness_factor should be non-negative."
|
||||
assert _get_image_num_channels(img, 'CHW') in [
|
||||
1,
|
||||
3,
|
||||
], "channels of input should be either 1 or 3."
|
||||
|
||||
extreme_target = paddle.zeros_like(img, img.dtype)
|
||||
return _blend_images(img, extreme_target, brightness_factor)
|
||||
|
||||
|
||||
def adjust_contrast(img, contrast_factor):
|
||||
"""Adjusts contrast of an image.
|
||||
|
||||
Args:
|
||||
img (paddle.Tensor): Image to be adjusted.
|
||||
contrast_factor (float): How much to adjust the contrast. Can be any
|
||||
non negative number. 0 gives a solid gray image, 1 gives the
|
||||
original image while 2 increases the contrast by a factor of 2.
|
||||
|
||||
Returns:
|
||||
paddle.Tensor: Contrast adjusted image.
|
||||
|
||||
"""
|
||||
_assert_image_tensor(img, 'chw')
|
||||
assert contrast_factor >= 0, "contrast_factor should be non-negative."
|
||||
|
||||
channels = _get_image_num_channels(img, 'CHW')
|
||||
dtype = img.dtype if paddle.is_floating_point(img) else paddle.float32
|
||||
if channels == 1:
|
||||
extreme_target = paddle.mean(
|
||||
img.astype(dtype), axis=(-3, -2, -1), keepdim=True
|
||||
)
|
||||
elif channels == 3:
|
||||
extreme_target = paddle.mean(
|
||||
to_grayscale(img).astype(dtype), axis=(-3, -2, -1), keepdim=True
|
||||
)
|
||||
else:
|
||||
raise ValueError("channels of input should be either 1 or 3.")
|
||||
|
||||
return _blend_images(img, extreme_target, contrast_factor)
|
||||
|
||||
|
||||
def adjust_saturation(img, saturation_factor):
|
||||
"""Adjusts color saturation of an image.
|
||||
|
||||
Args:
|
||||
img (paddle.Tensor): Image to be adjusted.
|
||||
saturation_factor (float): How much to adjust the saturation. 0 will
|
||||
give a black and white image, 1 will give the original image while
|
||||
2 will enhance the saturation by a factor of 2.
|
||||
|
||||
Returns:
|
||||
paddle.Tensor: Saturation adjusted image.
|
||||
|
||||
"""
|
||||
_assert_image_tensor(img, 'CHW')
|
||||
assert saturation_factor >= 0, "saturation_factor should be non-negative."
|
||||
channels = _get_image_num_channels(img, 'CHW')
|
||||
if channels == 1:
|
||||
return img
|
||||
elif channels == 3:
|
||||
extreme_target = to_grayscale(img)
|
||||
else:
|
||||
raise ValueError("channels of input should be either 1 or 3.")
|
||||
|
||||
return _blend_images(img, extreme_target, saturation_factor)
|
||||
|
||||
|
||||
def adjust_hue(img, hue_factor):
|
||||
"""Adjusts hue of an image.
|
||||
|
||||
The image hue is adjusted by converting the image to HSV and
|
||||
cyclically shifting the intensities in the hue channel (H).
|
||||
The image is then converted back to original image mode.
|
||||
|
||||
`hue_factor` is the amount of shift in H channel and must be in the
|
||||
interval `[-0.5, 0.5]`.
|
||||
|
||||
Args:
|
||||
img (paddle.Tensor): Image to be adjusted.
|
||||
hue_factor (float): How much to shift the hue channel. Should be in
|
||||
[-0.5, 0.5]. 0.5 and -0.5 give complete reversal of hue channel in
|
||||
HSV space in positive and negative direction respectively.
|
||||
0 means no shift. Therefore, both -0.5 and 0.5 will give an image
|
||||
with complementary colors while 0 gives the original image.
|
||||
|
||||
Returns:
|
||||
paddle.Tensor: Hue adjusted image.
|
||||
|
||||
"""
|
||||
_assert_image_tensor(img, 'CHW')
|
||||
assert hue_factor >= -0.5 and hue_factor <= 0.5, (
|
||||
"hue_factor should be in range [-0.5, 0.5]"
|
||||
)
|
||||
channels = _get_image_num_channels(img, 'CHW')
|
||||
if channels == 1:
|
||||
return img
|
||||
elif channels == 3:
|
||||
dtype = img.dtype
|
||||
if dtype == paddle.uint8:
|
||||
img = img.astype(paddle.float32) / 255.0
|
||||
|
||||
img_hsv = _rgb_to_hsv(img)
|
||||
h, s, v = img_hsv.unbind(axis=-3)
|
||||
h = h + hue_factor
|
||||
h = h - h.floor()
|
||||
img_adjusted = _hsv_to_rgb(paddle.stack([h, s, v], axis=-3))
|
||||
|
||||
if dtype == paddle.uint8:
|
||||
img_adjusted = (img_adjusted * 255.0).astype(dtype)
|
||||
else:
|
||||
raise ValueError("channels of input should be either 1 or 3.")
|
||||
|
||||
return img_adjusted
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user