# ***************************************************************
# Copyright (c) 2023 Jittor.
# All Rights Reserved.
# Maintainers:
# Dun Liang <randonlang@gmail.com>.
#
# Contributors:
# Xin Yao <yaox12@outlook.com>
#
# This file is subject to the terms and conditions defined in
# file 'LICENSE.txt', which is part of this source code package.
# ***************************************************************
from PIL import Image
import random
import math
import numpy as np
import warnings
from collections.abc import Sequence, Mapping
import numbers
import jittor as jt
from . import function_pil as F_pil
def _get_image_size(img):
'''
返回图像大小(w, h)
'''
return F_pil._get_image_size(img)
def _get_image_num_channels(img):
return F_pil._get_image_num_channels(img)
def _is_numpy(img):
'''
检查输入的图像是否为 NumPy ndarray 类型。
参数:
- img (object): 待检查的图像。
返回值:
- 如果图像是 NumPy ndarray 类型,则返回 True,否则返回 False。
代码示例:
>>> from jittor import transform
>>> import numpy as np
>>> img = np.array([1, 2, 3])
>>> transform._is_numpy(img)
True
'''
return isinstance(img, np.ndarray)
def _is_numpy_image(img):
'''
判断输入的图片是否为 NumPy 格式的图片。NumPy 格式的图片具有 2 或 3 个维度。
参数:
- img (np.ndarray): 待判断的图片。这应是一个 NumPy ndarray 类型的对象。
返回值:
- 如果图片是 NumPy 格式的(维度为 2 或 3),则返回 True,否则返回 False。
代码示例:
>>> from jittor import transform
>>> import numpy as np
>>> img = np.random.rand(100, 100, 3)
>>> transform._is_numpy_image(img)
True
>>> img = np.random.rand(100, 100)
>>> transform._is_numpy_image(img)
True
>>> img = np.random.rand(100)
>>> transform._is_numpy_image(img)
False
'''
return img.ndim in {2, 3}
[文档]
def crop(img, top, left, height, width):
'''
对图像进行裁剪,根据给定的边界参数从原始图像中裁剪出一个矩形区域。
参数:
- `img` (Image.Image): 输入的 PIL 图像。
- `top` (int): 裁剪框的上边界。
- `left` (int): 裁剪框的左边界。
- `height` (int): 裁剪框的高度。
- `width` (int): 裁剪框的宽度。
返回值:
- 返回裁剪后的图像。
代码示例:
>>> img = np.ones((100, 100, 3)).astype(np.uint8)
>>> img = Image.fromarray(img)
>>> img_cropped = crop(img, 10, 10, 100, 100)
'''
return img.crop((left, top, left + width, top + height))
[文档]
def resize(img, size, interpolation=Image.BILINEAR):
'''
调整图像的大小。
参数:
- img (PIL.Image.Image): 输入图像。
- size (list): 调整后的大小,格式为 [高, 宽]
- interpolation (int,optional): 调整大小的插值方法类型。默认值: PIL.Image.BILINEAR
返回值:
PIL.Image.Image: 调整后的图像
代码示例:
>>> import jittor as jt
>>> import numpy as np
>>> from PIL import Image
>>> data = np.random.rand(3,4)
>>> img = Image.fromarray(data, 'L')
>>> img.size
(3, 4)
>>> jt.transform.resize(img, [2,3]).size
(3, 2)
'''
if isinstance(size, Sequence):
return img.resize(size[::-1], interpolation)
else:
w, h = img.size
if (h > w):
return img.resize((size, int(round(size * h / w))), interpolation)
else:
return img.resize((int(round(size * w / h)), size), interpolation)
[文档]
def gray(img, num_output_channels):
"""
将任意模式(如 RGB、HSV、LAB 等)的 PIL 图片转换为灰度版本。该函数支持将灰度图像转换为单通道或三通道图像。
参数:
- `img` (PIL.Image.Image): 输入的 PIL 图片。
- `num_output_channels` (int, 可选): 输出图片的通道数。值可以为 1 或 3。默认值: 1。
返回值:
- 返回图片的灰度版本。如果 `num_output_channels` 为 1,返回单通道图像;如果为 3,返回三通道图像,其中 R、G、B 值相同。
代码示例:
>>> import numpy as np
>>> from PIL import Image
>>> from jittor.transform import gray
>>> img = np.ones((100, 100, 3)).astype(np.uint8)
>>> img = Image.fromarray(img)
>>> img_gray_1_channel = gray(img, 1)
>>> print(np.array(img_gray_1_channel).shape)
(100, 100)
>>> img_gray_3_channel = gray(img, 3)
>>> print(np.array(img_gray_3_channel).shape)
(100, 100, 3)
"""
return F_pil.gray(img, num_output_channels)
[文档]
def center_crop(img, output_size):
'''
对输入图像在中心处进行裁剪。
参数:
- img (PIL.Image.Image): 输入图像
- output_size (int, list) :裁剪框的(高度、宽度)。如果为整数或只有单个整数的序列,则高度和宽度都使用该整数
返回值:
PIL.Image.Image: 裁剪后的图像
代码示例:
>>> import jittor as jt
>>> import numpy as np
>>> from PIL import Image
>>> data = np.random.rand(200,200)
>>> img = Image.fromarray(data, 'L')
>>> img.size
(200, 200)
>>> jt.transform.center_crop(img, 100).size
(100, 100)
'''
output_size = _setup_size(output_size, error_msg="If size is a sequence, it should have 2 values")
image_width, image_height = _get_image_size(img)
crop_height, crop_width = output_size
crop_top = int(round((image_height - crop_height) / 2.))
crop_left = int(round((image_width - crop_width) / 2.))
return crop(img, crop_top, crop_left, crop_height, crop_width)
[文档]
def crop_and_resize(img, top, left, height, width, size, interpolation=Image.BILINEAR):
'''
裁剪后调整图像大小。
参数:
- img (PIL.Image.Image): 输入图像
- top (int): 裁剪框的顶部边界
- left (int): 裁剪框的左边界
- height (int): 裁剪框的高度
- width (int): 裁剪框的宽度
- size (list):裁剪之后图像要调整到的(高度, 宽度)
- interpolation (int, optional): 调整大小所用的插值方法。默认值: PIL.Image.BILINEAR
返回值:
PIL.Image.Image: 裁剪并且调整大小之后的图像
代码示例:
>>> import jittor as jt
>>> import numpy as np
>>> from PIL import Image
>>> data = np.random.rand(200,200)
>>> img = Image.fromarray(data, 'L')
>>> img.size
(200, 200)
>>> # 截取坐标为[10,90]x[10,90]的正方形并且缩小0.5倍
>>> jt.transform.crop_and_resize(img, 10, 10, 80, 80, [40, 40]).size
(40, 40)
'''
img = crop(img, top, left, height, width)
img = resize(img, size, interpolation)
return img
[文档]
class Crop:
'''
根据指定大小裁剪PIL图像。在初始化时需要指定裁剪区域的边界,之后可以作为一个可调用对象直接应用在图像上。
参数:
- top (int): 裁剪区域顶部边界的像素索引
- left (int): 裁剪区域左侧边界的像素索引
- height (int): 裁剪区域高度
- width (int): 裁剪区域宽度
代码示例:
>>> from jittor import transform
>>> cropper = transform.Crop(top=100, left=100, height=200, width=200)
>>> cropped_img = cropper(img)
'''
def __init__(self, top, left, height, width):
self.top = top
self.left = left
self.height = height
self.width = width
def __call__(self, img:Image.Image):
if not isinstance(img, Image.Image):
img = to_pil_image(img)
return crop(img, self.top, self.left, self.height, self.width)
[文档]
class RandomCropAndResize:
'''
对给定的PIL图像进行随机裁剪和尺寸调整。
参数:
- size (int, tuple): 输出图像的[高度,宽度]
- scale (tuple): 裁剪区域的面积比例范围。默认值: (0.08, 1.0)
- ratio (tuple): 裁剪图像的长宽比范围。默认值: (3.0/4.0, 4.0/3.0)
- interpolation (InterpolationMode): 图像大小调整时采用的插值类型。默认使用双线性插值。默认值: PIL.Image.BILINEAR
代码示例:
>>> from jittor import transform
>>> import numpy as np
>>> from PIL import Image
>>> data = np.random.rand(200, 200, 3)
>>> img = Image.fromarray(data, 'RGB')
>>> transform = transform.RandomCropAndResize(128)
>>> transform(img).size
(133, 128)
'''
def __init__(self, size, scale:tuple=(0.08, 1.0), ratio:tuple=(3. / 4., 4. / 3.), interpolation=Image.BILINEAR):
self.size = _setup_size(size, error_msg="If size is a sequence, it should have 2 values")
assert scale[0] <= scale[1] and ratio[0] <= ratio[1]
self.size = size
self.scale = scale
self.ratio = ratio
self.interpolation = interpolation
def __call__(self, img:Image.Image):
if not isinstance(img, Image.Image):
img = to_pil_image(img)
width, height = img.size
scale = self.scale
ratio = self.ratio
area = height * width
for _ in range(10):
target_area = random.uniform(*scale) * area
log_ratio = (math.log(ratio[0]), math.log(ratio[1]))
aspect_ratio = math.exp(random.uniform(*log_ratio))
w = int(round(math.sqrt(target_area * aspect_ratio)))
h = int(round(math.sqrt(target_area / aspect_ratio)))
if 0 < w <= width and 0 < h <= height:
i = random.randint(0, height - h)
j = random.randint(0, width - w)
break
else:
# Fallback to central crop
in_ratio = float(width) / float(height)
if in_ratio < min(ratio):
w = width
h = int(round(w / min(ratio)))
elif in_ratio > max(ratio):
h = height
w = int(round(h * max(ratio)))
else:
w = width
h = height
i = (height - h) // 2
j = (width - w) // 2
return crop_and_resize(img, i, j, h, w, self.size, self.interpolation)
[文档]
def hflip(img):
'''
对给定的图像进行水平翻转。
参数:
- img (PIL.Image.Image): 输入图像
返回值:
PIL.Image.Image: 水平翻转后的图像
代码示例:
>>> import jittor as jt
>>> import numpy as np
>>> from PIL import Image
>>> data = np.random.rand(200,200)
>>> img = Image.fromarray(data, 'L')
>>> img.size
(200, 200)
>>> img_flipped = jt.transform.hflip(img) # 水平翻转
'''
return F_pil.hflip(img)
[文档]
def vflip(img):
'''
对给定的图像进行垂直翻转。
参数:
- img (PIL.Image.Image): 输入图像
返回值:
PIL.Image.Image: 垂直翻转后的图像
代码示例:
>>> import jittor as jt
>>> import numpy as np
>>> from PIL import Image
>>> data = np.random.rand(200,200)
>>> img = Image.fromarray(data, 'L')
>>> img.size
(200, 200)
>>> img_vflipped = jt.transform.vflip(img) # 垂直翻转
'''
return F_pil.vflip(img)
[文档]
def adjust_brightness(img, brightness_factor):
'''
调整RGB图像的亮度。
参数:
- img (PIL.Image.Image): 输入图像
- brightness_factor (float): 调整亮度的程度。可以为任何非负数。0会得到一个黑色的图像,1会得到原始图像,2则会将亮度提高一倍
返回值:
PIL.Image.Image: 亮度调整后的图像
代码示例:
>>> import jittor as jt
>>> import numpy as np
>>> from PIL import Image
>>> data = np.random.rand(200,200, 3)
>>> img = Image.fromarray(data, 'RGB')
>>> img.show() # RGB噪声图
>>> img_black = jt.transform.adjust_brightness(img, 0)
>>> img_black.show() # 亮度调整为0,即全黑图
'''
return F_pil.adjust_brightness(img, brightness_factor)
[文档]
def adjust_contrast(img, contrast_factor):
"""
调整 RGB 图像的对比度。
参数:
- img (Image.Image): 需要调整的图像。
- contrast_factor (float): 调整对比度的程度。可以是任意非负数。0 会将图像转换为纯灰色,1 保持原图,2 增加两倍对比度。
返回值:
返回调整对比度后的图像。
代码示例:
>>> import numpy as np
>>> from PIL import Image
>>> from jittor import transform
>>> img = np.ones((100, 100, 3)).astype(np.uint8)
>>> img = Image.fromarray(img)
>>> img_adjusted = transform.adjust_contrast(img, 0.5)
"""
return F_pil.adjust_contrast(img, contrast_factor)
[文档]
def adjust_saturation(img, saturation_factor):
'''
调整图像饱和度。
参数:
- img (PIL.Image.Image): 输入图像
- saturation_factor (float): 调整饱和度的程度。0将产生黑白图像,1将给出原始图像,2将增强2倍的饱和度
返回值:
PIL.Image.Image: 饱和度调整后的图像
代码示例:
>>> import jittor as jt
>>> import numpy as np
>>> from PIL import Image
>>> data = np.random.rand(200,200, 3)
>>> img = Image.fromarray(data, 'RGB')
>>> img.show() # RGB噪声图
>>> img_blackwhite = jt.transform.adjust_saturation(img, 0)
>>> img_blackwhite.show() # 饱和度调整为0,即黑白图
'''
return F_pil.adjust_saturation(img, saturation_factor)
[文档]
def adjust_hue(img, hue_factor):
'''
调整图像的色调。先将图像转换为HSV空间,并在色调通道(H)中循环移动强度来调整的;然后,再转换回原始图像模式。 `hue_factor` 是H通道中的移动量,必须在`[-0.5,0.5]`内。更多详细信息,请参见 `Hue <https://en.wikipedia.org/wiki/Hue>`__
参数:
- img (PIL.Image.Image): 输入图像
- hue_factor (float): 色调通道移动量。取值应在[-0.5, 0.5]之间。0.5和-0.5分别在HSV空间中以正和负方向给出色调通道的完全反转。0表示无移动。因此,-0.5和0.5都会给出具有互补色的图像,而0则给出原始图像。
返回值:
PIL.Image.Image: 色调调整后的图像
代码示例:
>>> import jittor as jt
>>> import numpy as np
>>> from PIL import Image
>>> data = np.random.rand(200,200, 3)
>>> img = Image.fromarray(data, 'RGB')
>>> img.show() # RGB噪声图
>>> img_complementary = jt.transform.adjust_hue(img, 0.5)
>>> img_complementary.show() # 色调反转,即互补色图
'''
return F_pil.adjust_hue(img, hue_factor)
[文档]
def adjust_gamma(img, gamma, gain=1):
'''
图像的伽玛校正,也被称为幂律转换。更多详细信息,请参见 `WIKI <https://en.wikipedia.org/wiki/Gamma_correction>`__。基于以下公式对RGB模式的强度进行调整:
.. math::
I_{\\text{out}} = 255 \\times \\text{gain} \\times (\\frac{I_{\\text{in}}}{255})^{\\gamma}
参数:
- img (PIL.Image.Image): 输入图像
- gamma (float): 非负实数,即上述公式中的 :math:`\\gamma` 。 gamma大于1使阴影变暗,gamma小于1使暗区变亮。
- gain (float, optional): 常数乘数,默认值: 1
返回值:
PIL.Image.Image: 伽马校正后的图像
代码示例:
>>> import jittor as jt
>>> import numpy as np
>>> from PIL import Image
>>> data = np.random.rand(200,200, 3)
>>> img = Image.fromarray(data, 'RGB')
>>> img.show() # RGB噪声图
>>> jt.transform.adjust_gamma(img, 0.2).show() # 暗区变亮
'''
return F_pil.adjust_gamma(img, gamma, gain)
[文档]
class RandomHorizontalFlip:
'''
对图像进行随机水平翻转的变换类,以一定的概率水平翻转输入的图像。
参数:
- p (float): 图像翻转的概率。默认值为0.5。
代码示例:
>>> from jittor import transform
>>> random_hflip = transform.RandomHorizontalFlip(0.6)
>>> img_ = random_hflip(img)
'''
def __init__(self, p=0.5):
self.p = p
def __call__(self, img:Image.Image):
if not isinstance(img, Image.Image):
img = to_pil_image(img)
if random.random() < self.p:
return img.transpose(Image.FLIP_LEFT_RIGHT)
return img
[文档]
class CenterCrop:
'''
中心裁剪图像的类。本类用于创建一个中心裁剪变换。当使用此类的实例调用一张图像时,它将图像裁剪为指定的大小,并且裁剪的中心与图像的中心对齐。
参数:
- size(int, tuple): 裁剪后的图像尺寸。
代码示例:
>>> from jittor import transform
>>> import numpy as np
>>> from PIL import Image
>>> center_crop = transform.CenterCrop(16)
>>> data = np.random.rand(200, 200, 3)
>>> img = Image.fromarray(data, 'RGB')
>>> center_crop(img).size
(16, 16)
'''
def __init__(self, size):
self.size = _setup_size(size, error_msg="If size is a sequence, it should have 2 values")
def __call__(self, img:Image.Image):
if not isinstance(img, Image.Image):
img = to_pil_image(img)
width, height = img.size
return crop(img, (height - self.size[0]) / 2, (width - self.size[1]) / 2, self.size[0], self.size[1])
[文档]
def to_tensor(pic):
'''
将Image.Image对象转换为格式为CHW的np.array
参数:
- pic (PIL.Image.Image): 输入图像
返回值:
经格式转换、规范化等操作后的np.array对象
代码示例:
>>> import jittor as jt
>>> import numpy as np
>>> from PIL import Image
>>> data = np.random.rand(200, 200, 3)
>>> img = Image.fromarray(data, 'RGB')
>>> arr = jt.transform.to_tensor(img)
>>> type(arr), arr.shape
(<class 'numpy.ndarray'>, (3, 200, 200))
'''
if isinstance(pic, jt.Var):
return pic
if isinstance(pic, tuple):
# try convert ten crop tuple
pic = ( to_tensor(pic) for p in pic )
pic = np.array(pic)
return pic
if not(F_pil._is_pil_image(pic) or _is_numpy(pic)):
raise TypeError(f'img should be PIL Image or ndarray. Got {type(pic)}.')
if _is_numpy(pic) and not _is_numpy_image(pic):
raise ValueError(f'img should be 2/3 dimensional. Got {pic.ndim} dimensions.')
if _is_numpy(pic):
# handle numpy array
if pic.ndim == 2:
pic = pic[None, :, :]
# backward compatibility
if pic.dtype == 'uint8':
return np.float32(pic) * np.float32(1/255.0)
else:
return pic
# handle PIL Image
if pic.mode == 'I':
img = np.array(pic, np.int32, copy=False)
elif pic.mode == 'I;16':
img = np.array(pic, np.int16, copy=False)
elif pic.mode == 'F':
img = np.array(pic, np.float32, copy=False)
elif pic.mode == '1':
img = np.array(pic, np.uint8, copy=False) * 255
else:
img = np.array(pic, np.uint8, copy=False)
# put it from HWC to CHW format
img = img.reshape(pic.size[1], pic.size[0], len(pic.getbands()))
img = img.transpose(2, 0, 1)
if img.dtype == 'uint8':
return np.float32(img) * np.float32(1/255.0)
else:
return img
pil_to_tensor = to_tensor
def _to_jittor_array(pic):
'''
将Image.Image对象或者形状为[H,W,C]的np.ndarray转换为形状为[C,H,W]的jt.Var
参数:
- pic (PIL.Image.Image或numpy.ndarray): 输入图像。如果输入类型为np.ndarray,那么其形状应该是[H,W,C]
返回值:
jt.Var: 转换后得到的形状为[C,H,W]的jittor张量
代码示例:
>>> import jittor as jt
>>> import numpy as np
>>> from PIL import Image
>>> data = np.random.rand(200,200, 3)
>>> img = Image.fromarray(data, 'RGB')
>>> jt.transform._to_jittor_array(img).shape
[3,200,200,]
>>> jt.transform._to_jittor_array(data).shape
[3,200,200,]
'''
if not(F_pil._is_pil_image(pic) or _is_numpy(pic)):
raise TypeError(f'img should be PIL Image or ndarray. Got {type(pic)}.')
if _is_numpy(pic) and not _is_numpy_image(pic):
raise ValueError(f'img should be 2/3 dimensional. Got {pic.ndim} dimensions.')
if _is_numpy(pic):
# handle numpy array
if pic.ndim == 2:
pic = pic[:, :, None]
img = jt.array(pic.transpose((2, 0, 1)))
# backward compatibility
if img.dtype == 'uint8':
return img.float().divide(255)
else:
return img
# handle PIL Image
if pic.mode == 'I':
img = jt.array(np.array(pic, np.int32, copy=False))
elif pic.mode == 'I;16':
img = jt.array(np.array(pic, np.int16, copy=False))
elif pic.mode == 'F':
img = jt.array(np.array(pic, np.float32, copy=False))
elif pic.mode == '1':
img = jt.array(np.array(pic, np.uint8, copy=False) * 255, dtype='uint8')
else:
img = jt.array(np.array(pic, np.uint8, copy=False))
# put it from HWC to CHW format
img = img.reshape(pic.size[1], pic.size[0], len(pic.getbands()))
img = img.permute((2, 0, 1))
if img.dtype == 'uint8':
return img.float().divide(255)
else:
return img
[文档]
def to_pil_image(pic, mode=None):
'''
将一个jt.Var或np.ndarray转换为PIL Image对象
参数:
- pic (Var, numpy.ndarray): 需要被转换为PIL Image的图像,应为HWC格式
- mode (`PIL.Image mode`, optional): 输入数据的颜色空间和像素深度。默认值:None
返回值:
PIL Image: 转换后的PIL Image
代码示例:
>>> import jittor as jt
>>> import numpy as np
>>> from PIL import Image
>>> data = np.random.rand(200,200, 3)
>>> img = jt.transform.to_pil_image(data)
>>> img.show()
'''
if isinstance(pic, jt.Var):
pic = pic.data
if not isinstance(pic, np.ndarray):
raise TypeError('pic should be Tensor or ndarray. Got {}.'.format(type(pic)))
else:
if pic.ndim not in {2, 3}:
raise ValueError('pic should be 2/3 dimensional. Got {} dimensions.'.format(pic.ndim))
elif pic.ndim == 2:
# if 2D image, add channel dimension (HWC)
pic = np.expand_dims(pic, 2)
npimg = pic
if 'float' in str(pic.dtype) and mode != 'F' and npimg.shape[2] != 1:
npimg = np.uint8(pic * 255)
# npimg = np.transpose(pic, (1, 2, 0))
if not isinstance(npimg, np.ndarray):
raise TypeError('Input pic must be a jt.Var or NumPy ndarray, ' +
'not {}'.format(type(npimg)))
if npimg.shape[2] == 1:
expected_mode = None
npimg = npimg[:, :, 0]
if npimg.dtype == np.uint8:
expected_mode = 'L'
elif npimg.dtype == np.int16:
expected_mode = 'I;16'
elif npimg.dtype == np.int32:
expected_mode = 'I'
elif npimg.dtype == np.float32:
expected_mode = 'F'
if mode is not None and mode != expected_mode:
raise ValueError("Incorrect mode ({}) supplied for input type {}. Should be {}"
.format(mode, np.dtype, expected_mode))
mode = expected_mode
elif npimg.shape[2] == 2:
permitted_2_channel_modes = ['LA']
if mode is not None and mode not in permitted_2_channel_modes:
raise ValueError("Only modes {} are supported for 2D inputs".format(permitted_2_channel_modes))
if mode is None and npimg.dtype == np.uint8:
mode = 'LA'
elif npimg.shape[2] == 4:
permitted_4_channel_modes = ['RGBA', 'CMYK', 'RGBX']
if mode is not None and mode not in permitted_4_channel_modes:
raise ValueError("Only modes {} are supported for 4D inputs".format(permitted_4_channel_modes))
if mode is None and npimg.dtype == np.uint8:
mode = 'RGBA'
else:
permitted_3_channel_modes = ['RGB', 'YCbCr', 'HSV']
if mode is not None and mode not in permitted_3_channel_modes:
raise ValueError("Only modes {} are supported for 3D inputs".format(permitted_3_channel_modes))
if mode is None and npimg.dtype == np.uint8:
mode = 'RGB'
if mode is None:
raise TypeError('Input type {} is not supported'.format(npimg.dtype))
return Image.fromarray(npimg, mode=mode)
[文档]
def image_normalize(img, mean, std):
'''
对图像进行归一化。
参数:
- img (PIL Image.Image, jt.Var, np.ndarray): 输入的图像。如果输入的图像类型是 np.ndarray,则它应该具有形状 (C, H, W)
- mean (list): 归一化的平均值
- std (list): 归一化的标准差值
返回值:
归一化后的图像
代码示例:
>>> import jittor as jt
>>> import numpy as np
>>> from PIL import Image
>>> data = np.random.rand(200,200, 3)
>>> img = Image.fromarray(data, 'RGB')
>>> mean = jt.ones(200,200) / 2
>>> std = jt.ones(200,200) / 2
>>> jt.transform.image_normalize(img, mean, std)
'''
if not isinstance(img, (Image.Image, jt.Var, np.ndarray)):
raise TypeError(f'Input type should be in (PIL Image, jt.Var, np.ndarray). Got {type(img)}.')
elif isinstance(img, Image.Image):
assert img.mode == 'RGB', f"input image mode should be 'RGB'. Got {img.mode}."
img = (np.array(img).transpose((2, 0, 1)) \
- mean * np.float32(255.)) \
/ (std * np.float32(255.))
else:
if img.ndim < 3:
raise ValueError(f'Expected input to be a array image of size (..., C, H, W). Got {img.shape}.')
if isinstance(img, jt.Var):
mean = jt.array(mean)
std = jt.array(std)
if (std.data == 0).any():
raise ValueError('std cannot be zero.')
else:
mean = np.asarray(mean)
std = np.asarray(std)
if (std == 0).any():
raise ValueError('std cannot be zero.')
if mean.ndim == 1:
mean = mean.reshape(-1, 1, 1)
if std.ndim == 1:
std = std.reshape(-1, 1, 1)
img = (img - mean) / std
return img
[文档]
class ImageNormalize:
'''
对图像进行标准化处理的类。给定n个通道的均值(mean[1], ..., mean[n])和标准差(std[1], .., std[n]),此转换将标准化输入张量的每个通道,即:
.. math::
output[channel] = (input[channel] - mean[channel]) / std[channel]
参数:
- mean(list): 标准化使用的平均值列表
- std(list): 标准化使用的标准差列表
代码示例:
>>> from jittor import transform
>>> img_normalize = transform.ImageNormalize(mean=[0.5], std=[0.5])
>>> img_ = img_normalize(img)
'''
def __init__(self, mean, std):
self.mean = np.float32(mean).reshape(-1,1,1)
self.std = np.float32(std).reshape(-1,1,1)
def __call__(self, img):
if isinstance(img, Image.Image):
img = (np.array(img).transpose((2,0,1)) \
- self.mean*np.float32(255.)) \
* (np.float32(1./255.)/self.std)
else:
img = (img - self.mean) / self.std
return img
[文档]
class Compose:
'''
将多个图像变换组合在一起的类。该类将输入的变换列表依次应用到输入数据上。如果输入数据为单个对象,会依次通过每个变换;如果输入为多个对象(例如图像和对应标签),则每个变换都会接收相同的参数列表,并依次处理。
参数:
- transforms (list): 需要组合的变换列表。
代码示例:
>>> from jittor import transform
>>> compose = transform.Compose([
transform.Resize(224),
transform.Gray(),
transform.ImageNormalize(mean=[0.5], std=[0.5]),
])
>>> img_ = compose(img)
'''
def __init__(self, transforms):
self.transforms = transforms
def __call__(self, *data):
if len(data) == 1:
data = data[0]
for t in self.transforms:
data = t(data)
else:
for t in self.transforms:
data = t(*data)
return data
[文档]
class Resize:
'''
调整图像尺寸的类。
参数:
- size (int或tuple): 想要调整到的目标尺寸。
- mode (int, 可选): 调整尺寸时采用的插值方式。默认为 `Image.BILINEAR`,即双线性插值。
代码示例:
>>> from jittor import transform
>>> resize_transform = transform.Resize(224)
>>> img_ = resize_transform(img)
'''
def __init__(self, size, mode=Image.BILINEAR):
self.size = _setup_size(size, error_msg="If size is a sequence, it should have 2 values")
self.mode = mode
def __call__(self, img:Image.Image):
if not isinstance(img, Image.Image):
img = to_pil_image(img)
return resize(img, self.size, self.mode)
[文档]
class Gray:
'''
将图像转换为灰度图的类。
参数:
- num_output_channels (int, 可选): 输出通道数(1表示单通道灰度图,3表示三通道灰度图),默认值为1。
代码示例:
>>> from jittor import transform
>>> gray = transform.Gray()
>>> img_ = gray(img)
'''
def __init__(self, num_output_channels=1):
self.num_output_channels = num_output_channels
def __call__(self, img:Image.Image):
if not isinstance(img, Image.Image):
img = to_pil_image(img)
img = np.float32(img.convert('L')) / np.float32(255.0)
if self.num_output_channels == 1:
return img[np.newaxis, :]
else:
return np.dstack([img, img, img])
[文档]
class RandomGray:
'''
随机将图片转换为灰度图。该类用于以给定的概率p随机将输入图像转换为灰度图像,转换后的图像可能保持不变或则转换为灰度图,这取决于一个随机事件。输出灰度图与输入图的通道数保持一致。
参数:
- p (float): 图像被转换为灰度图的概率。默认值: 0.1
代码示例:
>>> from jittor import transform
>>> random_gray = transform.RandomGray()
>>> img_ = random_gray(img)
'''
def __init__(self, p=0.1):
self.p = p
def __call__(self, img:Image.Image):
if not isinstance(img, Image.Image):
img = to_pil_image(img)
num_output_channels = _get_image_num_channels(img)
if random.random() < self.p:
return gray(img, num_output_channels=num_output_channels)
return img
[文档]
class RandomCrop:
'''
随机裁剪输入图像的类。
参数:
- size (tuple, int): 欲裁剪的目标大小。如果是int,将裁剪出一个正方形;如果是tuple,则按照(tuple_height, tuple_width)的形式给出。
代码示例:
>>> from jittor import transform
>>> import numpy as np
>>> from PIL import Image
>>> data = np.random.rand(200, 200, 3)
>>> img = Image.fromarray(data, 'RGB')
>>> random_crop = transform.RandomCrop(128)
>>> random_crop(img).size
(128, 128)
'''
def __init__(self, size):
self.size = _setup_size(size, error_msg="If size is a sequence, it should have 2 values")
def __call__(self, img:Image.Image):
if not isinstance(img, Image.Image):
img = to_pil_image(img)
width, height = img.size
assert self.size[0] <= height and self.size[1] <= width, f"crop size exceeds the input image in RandomCrop, {(self.size, height, width)}"
top = np.random.randint(0,height-self.size[0]+1)
left = np.random.randint(0,width-self.size[1]+1)
return crop(img, top, left, self.size[0], self.size[1])
[文档]
class Lambda:
'''
此类用于将用户定义的lambda函数作为变换应用到对象上。
参数:
- lambd (function): 用于变换的lambda函数。
代码示例:
>>> from jittor import transform
>>> lambda = transform.Lambda(lambda x: x + 10)
>>> result = lambda(5)
'''
def __init__(self, lambd):
assert callable(lambd), repr(type(lambd).__name__) + " object is not callable"
self.lambd = lambd
def __call__(self, img):
return self.lambd(img)
def __repr__(self):
return self.__class__.__name__ + '()'
[文档]
class RandomApply:
'''
随机应用具有给定概率的变换列表。
参数:
- transforms (list, tuple): 需要随机应用的变换操作列表
- p (float): 概率,默认值是 0.5
代码示例:
>>> from jittor import transform
>>> transform_list = [transform.RandomHorizontalFlip(), transform.RandomCrop(size=(32, 32))]
>>> random_apply = transform.RandomApply(transform_list, p=0.5)
>>> img_= random_apply(img)
'''
def __init__(self, transforms, p=0.5):
assert isinstance(transforms, (list, tuple))
self.transforms = transforms
self.p = p
def __call__(self, img):
if self.p < random.random():
return img
for t in self.transforms:
img = t(img)
return img
[文档]
class RandomOrder:
'''
对一系列的变换操作以随机的顺序进行应用。
参数:
- transforms (list): 需要应用的变换操作的列表
代码示例:
>>> from jittor import transform
>>> transform_list = [transform.RandomHorizontalFlip(), transform.RandomCrop(size=(32, 32))]
>>> random_order = transform.RandomOrder(transform_list)
>>> img_ = random_order(img)
'''
def __init__(self, transforms):
assert isinstance(transforms, (list, tuple))
self.transforms = transforms
def __call__(self, img):
order = list(range(len(self.transforms)))
random.shuffle(order)
for i in order:
img = self.transforms[i](img)
return img
[文档]
class RandomChoice:
'''
从给定的变换列表中随机选择一个变换应用于图像。
参数:
- transforms (list, tuple): 变换列表。
代码示例:
>>> from jittor import transform
>>> transform_list = [transform.RandomHorizontalFlip(), transform.RandomCrop(size=(32, 32))]
>>> random_choice = transform.RandomChoice(transform_list)
>>> img_ = random_choice(img)
'''
def __init__(self, transforms):
assert isinstance(transforms, (list, tuple))
self.transforms = transforms
def __call__(self, img):
t = random.choice(self.transforms)
return t(img)
[文档]
class RandomVerticalFlip:
'''
随机垂直翻转图像的类。该类以一定概率沿垂直方向翻转给定的图像。这是数据增强过程中的一个常用技术,旨在提高模型对图像方向变化的鲁棒性。
参数:
- p (float, 可选): 图像翻转的概率。默认值: 0.5。
代码示例:
>>> from jittor.transform import RandomVerticalFlip
>>> from PIL import Image
>>> random_vflip = RandomVerticalFlip(0.6)
>>> img = Image.open('path/to/image.jpg')
>>> img_transformed = random_vflip(img)
'''
def __init__(self, p=0.5):
self.p = p
def __call__(self, img:Image.Image):
if not isinstance(img, Image.Image):
img = to_pil_image(img)
if random.random() < self.p:
return vflip(img)
return img
[文档]
class ColorJitter:
'''
随机改变图像的亮度、对比度、饱和度和色调。该类用于对图像进行随机的颜色变换,以进行数据增强。这些变换包括亮度、对比度、饱和度和色调的调整。
参数:
- brightness (float, tuple(min, max)): 要改变的亮度范围。亮度因子从 :math:`[\\max(0, 1 - brightness), 1 + brightness]` 或给定的 :math:`[min, max]` 中均匀选择。应为非负数。默认值为 0,表示不改变亮度。
- contrast (float, tuple(min, max)): 要改变的对比度范围。对比度因子从 :math:`[\\max(0, 1 - contrast), 1 + contrast]` 或给定的 :math:`[min, max]` 中均匀选择。应为非负数。默认值为 0,表示不改变对比度。
- saturation (float, tuple(min, max)): 要改变的饱和度范围。饱和度因子从 :math:`[\\max(0, 1 - saturation), 1 + saturation]` 或给定的 :math:`[min, max]` 中均匀选择。应为非负数。默认值为 0,表示不改变饱和度。
- hue (float, tuple(min, max)): 要改变的色调范围。色调因子从 :math:`[-hue, hue]` 或给定的 :math:`[min, max]` 中均匀选择。应满足 :math:`0\\leq hue\\leq 0.5` 或 :math:`-0.5 \\leq min \\leq max \\leq 0.5`。默认值为 0,表示不改变色调。
代码示例:
>>> from jittor.transform import ColorJitter
>>> from PIL import Image
>>> img = Image.open(\'''path_to_image.jpg\
''')
>>> transform = ColorJitter(brightness=0.2, contrast=0.3, saturation=0.4, hue=0.1)
>>> img_transformed = transform(img)
'''
def __init__(self, brightness=0, contrast=0, saturation=0, hue=0):
self.brightness = self._check_input(brightness, 'brightness')
self.contrast = self._check_input(contrast, 'contrast')
self.saturation = self._check_input(saturation, 'saturation')
self.hue = self._check_input(hue, 'hue', center=0, bound=(-0.5, 0.5),
clip_first_on_zero=False)
@staticmethod
def _check_input(value, name, center=1, bound=(0, float('inf')), clip_first_on_zero=True):
if isinstance(value, numbers.Number):
if value < 0:
raise ValueError(f"If {name} is a single number, it must be non negative.")
value = [center - float(value), center + float(value)]
if clip_first_on_zero:
value[0] = max(value[0], 0.0)
elif isinstance(value, (tuple, list)) and len(value) == 2:
if not bound[0] <= value[0] <= value[1] <= bound[1]:
raise ValueError(f"{name} values should be between {bound}")
else:
raise TypeError(f"{name} should be a single number or a list/tuple with length 2.")
# if value is 0 or (1., 1.) for brightness/contrast/saturation
# or (0., 0.) for hue, do nothing
if value[0] == value[1] == center:
value = None
return value
@staticmethod
def _get_transform(brightness, contrast, saturation, hue):
"""
Get a randomized transform to be applied on image.
Arguments are same as that of __init__.
Returns::
Transform which randomly adjusts brightness, contrast, saturation
and hue in a random order.
"""
transforms = []
if brightness is not None:
brightness_factor = random.uniform(brightness[0], brightness[1])
transforms.append(Lambda(lambda img: adjust_brightness(img, brightness_factor)))
if contrast is not None:
contrast_factor = random.uniform(contrast[0], contrast[1])
transforms.append(Lambda(lambda img: adjust_contrast(img, contrast_factor)))
if saturation is not None:
saturation_factor = random.uniform(saturation[0], saturation[1])
transforms.append(Lambda(lambda img: adjust_saturation(img, saturation_factor)))
if hue is not None:
hue_factor = random.uniform(hue[0], hue[1])
transforms.append(Lambda(lambda img: adjust_hue(img, hue_factor)))
random.shuffle(transforms)
transform = Compose(transforms)
return transform
def __call__(self, img:Image.Image):
"""
Args::
[in] img (PIL Image): Input image.
Returns::
[out] PIL Image: Color jittered image.
"""
if not isinstance(img, Image.Image):
img = to_pil_image(img)
transform = self._get_transform(self.brightness, self.contrast, self.saturation, self.hue)
return transform(img)
def _setup_size(size, error_msg):
'''
这个函数用于设置元素的尺寸。如果给出尺寸是数字类型的大小,例如5,则将返回尺寸(5,5)。如果给出尺寸是长度为1的序列,例如[5],那么返回的尺寸也是(5,5)。如果给出尺寸是长度为2的序列,例如[5,7],那么返回的尺寸就是该序列(5,7)。
参数:
- size (numbers.Number 或 Sequence): 尺寸。它可以是一个数字,或者可以是一个长度为1或2的序列。如果是数字,则返回的是两个相同的整数。如果是长度为1的序列,则返回的是两个相同的元素。如果是长度为2的序列,则直接返回该序列。其他情况将抛出一个错误。
- perror_msg (str): 当尺寸不符合规定时返回的错误信息。
返回值:
output (Sequence): 双元素序列,表示尺寸。
代码示例:
>>> output = transform._setup_size(5, \'''Invalid size\''')
>>> print(output)
(5, 5)
>>> output = transform._setup_size([5], \'''Invalid size\''')
>>> print(output)
(5, 5)
>>> output = transform._setup_size([5, 7], \'''Invalid size\''')
>>> print(output)
[5,7]
'''
if isinstance(size, numbers.Number):
return int(size), int(size)
if isinstance(size, Sequence) and len(size) == 1:
return size[0], size[0]
if len(size) != 2:
raise ValueError(error_msg)
return size
[文档]
class ToTensor:
'''
将 PIL Image 或 numpy.ndarray 格式的图像转换为 Tensor。
`ToTensor` 类的实例是一个可调用对象,它可以将 PIL 库中的图像或者 numpy.ndarray 类型的数组转换为 Jittor 的 Tensor 格式。此操作通常用于数据预处理阶段。转换后的 Tensor 数据格式为 C x H x W,并且数据范围被归一化到 [0, 1]。
参数:
- pic (PIL.Image, numpy.ndarray): 需要转换为 Tensor 的图像对象。如果是 PIL 图像,则应为 L, LA, RGB, RGBA, P, PA, I, I;16, I;16L, I;16B, I;16P, F 等模式之一。如果是 numpy.ndarray,则应为形状为 H x W x C,数据类型为 np.uint8 的数组。
返回值:
- Var: 转换后的 Tensor,形状为 C x H x W,范围 [0.0, 1.0]。
代码示例:
>>> from jittor import transform
>>> import numpy as np
>>> from PIL import Image
>>> data = np.random.rand(200, 200, 3)
>>> img = Image.fromarray(data, 'RGB')
>>> to_tensor = transform.ToTensor()
>>> tensor = to_tensor(img)
>>> print(tensor.dtype, tensor.shape)
float32 (3, 200, 200)
'''
def __call__(self, pic):
"""
Args:
pic (PIL Image, numpy.ndarray): Image to be converted to tensor.
Returns:
Tensor: Converted image.
"""
return to_tensor(pic)
def __repr__(self):
return self.__class__.__name__ + '()'
[文档]
class ToPILImage(object):
'''
将张量或ndarray转换为PIL图像。该类将一个C x H x W 的张量或者一个H x W x C 型的numpy ndarray转换成 PIL Image ,同时根据mode调整value的范围。
初始化参数:
- mode (`PIL.Image mode` , 可选): PIL图像模式,指定输入数据的颜色空间和像素深度。支持的模式参见PIL文档中 `关于图像模式的介绍 <https://pillow.readthedocs.io/en/latest/handbook/concepts.html#concept-modes>`_ 。默认值: None
执行参数:
- pic (Var, numpy.ndarray): 将被转换成PIL image 的 HWC 格式的 image。
代码示例:
>>> from jittor import transform
>>> transform = transform.ToPILImage('RGB')
>>> img_ = transform(img_var)
'''
def __init__(self, mode=None):
self.mode = mode
def __call__(self, pic):
"""
Args:
pic (jittor.Var, numpy.ndarray): Image to be converted to PIL Image.
Returns:
PIL Image: Image converted to PIL Image.
"""
return to_pil_image(pic, self.mode)
def __repr__(self):
format_string = self.__class__.__name__ + '('
if self.mode is not None:
format_string += 'mode={0}'.format(self.mode)
format_string += ')'
return format_string
[文档]
class RandomPerspective(object):
'''
对图像以一定的概率进行随机透视变换。
参数:
- distortion_scale(float, 可选): 控制变形程度的比例系数,值域为 [0, 1]。默认值: 0.5
- p (float, 可选): 图片被透视变换的概率。默认值: 0.5
- interpolation (PIL.Image.Interpolation method, 可选): 插值方法。默认值: PIL.Image.BICUBIC
代码示例:
>>> from jittor import transform
>>> random_perspective = transform.RandomPerspective(distortion_scale=0.5, p=0.5)
>>> img_ = random_perspective(img)
'''
def __init__(self, distortion_scale=0.5, p=0.5, interpolation=Image.BICUBIC):
self.p = p
self.interpolation = interpolation
self.distortion_scale = distortion_scale
def __call__(self, img:Image.Image):
"""
Args:
img (PIL Image): Image to be Perspectively transformed.
Returns:
PIL Image: Random perspectivley transformed image.
"""
if not isinstance(img, Image.Image):
img = to_pil_image(img)
if random.random() < self.p:
width, height = img.size
startpoints, endpoints = self.get_params(width, height, self.distortion_scale)
return F_pil.perspective(img, startpoints, endpoints, self.interpolation)
return img
@staticmethod
def get_params(width, height, distortion_scale):
"""Get parameters for ``perspective`` for a random perspective transform.
Args:
width : width of the image.
height : height of the image.
Returns:
List containing [top-left, top-right, bottom-right, bottom-left] of the original image,
List containing [top-left, top-right, bottom-right, bottom-left] of the transformed image.
"""
half_height = int(height / 2)
half_width = int(width / 2)
topleft = (random.randint(0, int(distortion_scale * half_width)),
random.randint(0, int(distortion_scale * half_height)))
topright = (random.randint(width - int(distortion_scale * half_width) - 1, width - 1),
random.randint(0, int(distortion_scale * half_height)))
botright = (random.randint(width - int(distortion_scale * half_width) - 1, width - 1),
random.randint(height - int(distortion_scale * half_height) - 1, height - 1))
botleft = (random.randint(0, int(distortion_scale * half_width)),
random.randint(height - int(distortion_scale * half_height) - 1, height - 1))
startpoints = [(0, 0), (width - 1, 0), (width - 1, height - 1), (0, height - 1)]
endpoints = [topleft, topright, botright, botleft]
return startpoints, endpoints
def __repr__(self):
return self.__class__.__name__ + '(p={})'.format(self.p)
[文档]
class RandomResizedCrop(object):
'''
对给定的PIL图像执行随机尺寸与宽高比的裁剪并将其调整为给定大小。
同 ``jittor.transform.RandomSizedCrop``
参数:
- size (int, tuple[int]): 裁剪后每边的期望输出尺寸。若为单一整数,输出将是正方形;若为(int, int)元组,则输出为指定的宽高。
- scale (tuple[float], 可选): 原始尺寸裁剪范围。默认值: (0.08, 1.0)。
- ratio (tuple[float], 可选): 原始宽高比裁剪范围。默认值: (3/4, 4/3)。
- interpolation (PIL.Image.Interpolation method, 可选): 图像插值方法。默认值: PIL.Image.BILINEAR
代码示例:
>>> from jittor import transform
>>> random_resized_crop = transform.RandomResizedCrop(size=(224, 224))
>>> img_ = random_resized_crop(img)
'''
def __init__(self, size, scale=(0.08, 1.0), ratio=(3. / 4., 4. / 3.), interpolation=Image.BILINEAR):
if isinstance(size, tuple):
self.size = size
else:
self.size = (size, size)
if (scale[0] > scale[1]) or (ratio[0] > ratio[1]):
warnings.warn("range should be of kind (min, max)")
self.interpolation = interpolation
self.scale = scale
self.ratio = ratio
@staticmethod
def get_params(img, scale, ratio):
"""Get parameters for ``crop`` for a random sized crop.
Args:
img (PIL Image): Image to be cropped.
scale (tuple): range of size of the origin size cropped
ratio (tuple): range of aspect ratio of the origin aspect ratio cropped
Returns:
tuple: params (i, j, h, w) to be passed to ``crop`` for a random
sized crop.
"""
width, height = _get_image_size(img)
area = height * width
for attempt in range(10):
target_area = random.uniform(*scale) * area
log_ratio = (math.log(ratio[0]), math.log(ratio[1]))
aspect_ratio = math.exp(random.uniform(*log_ratio))
w = int(round(math.sqrt(target_area * aspect_ratio)))
h = int(round(math.sqrt(target_area / aspect_ratio)))
if 0 < w <= width and 0 < h <= height:
i = random.randint(0, height - h)
j = random.randint(0, width - w)
return i, j, h, w
# Fallback to central crop
in_ratio = float(width) / float(height)
if (in_ratio < min(ratio)):
w = width
h = int(round(w / min(ratio)))
elif (in_ratio > max(ratio)):
h = height
w = int(round(h * max(ratio)))
else: # whole image
w = width
h = height
i = (height - h) // 2
j = (width - w) // 2
return i, j, h, w
def __call__(self, img:Image.Image):
"""
Args:
img (PIL Image): Image to be cropped and resized.
Returns:
PIL Image: Randomly cropped and resized image.
"""
if not isinstance(img, Image.Image):
img = to_pil_image(img)
i, j, h, w = self.get_params(img, self.scale, self.ratio)
return F_pil.resized_crop(img, i, j, h, w, self.size, self.interpolation)
def __repr__(self):
interpolate_str = str(self.interpolation)
format_string = self.__class__.__name__ + '(size={0}'.format(self.size)
format_string += ', scale={0}'.format(tuple(round(s, 4) for s in self.scale))
format_string += ', ratio={0}'.format(tuple(round(r, 4) for r in self.ratio))
format_string += ', interpolation={0})'.format(interpolate_str)
return format_string
RandomSizedCrop = RandomResizedCrop
[文档]
class FiveCrop(object):
'''
在给定的PIL图像四个角和中心各截取一幅大小为 size 的图片
参数:
- size (sequence, int): 裁剪的期望输出大小。如果size是一个整数而不是像(h, w)这样的序列,会进行一个(size, size)的正方形裁剪
代码示例:
>>> from jittor import transform
>>> import numpy as np
>>> from PIL import Image
>>> data = np.random.rand(200, 200, 3)
>>> img = Image.fromarray(data, 'RGB')
>>> five_crop = transform.FiveCrop(16)
>>> imgs = five_crop(img)
>>> imgs[0].size, imgs[1].size, imgs[2].size, imgs[3].size, imgs[4].size
((16, 16), (16, 16), (16, 16), (16, 16), (16, 16))
'''
def __init__(self, size):
self.size = size
if isinstance(size, numbers.Number):
self.size = (int(size), int(size))
else:
assert len(size) == 2, "Please provide only two dimensions (h, w) for size."
self.size = size
def __call__(self, img:Image.Image):
if not isinstance(img, Image.Image):
img = to_pil_image(img)
return F_pil.five_crop(img, self.size)
def __repr__(self):
return self.__class__.__name__ + '(size={0})'.format(self.size)
[文档]
class TenCrop(object):
'''
将给定图像裁剪成四个角落和中心部分,以及这些部分的翻转版本(默认为水平翻转,可选择垂直翻转),共十张图片。
参数:
- size (sequence, int): 裁剪的期望输出大小。如果size是一个整数而不是像(h, w)这样的序列,会进行一个(size, size)的正方形裁剪
- vertical_flip (bool, 可选): 若为True,则使用垂直翻转。否则使用水平翻转。默认值: False
代码示例:
>>> from jittor import transform
>>> import numpy as np
>>> from PIL import Image
>>> data = np.random.rand(200, 200, 3)
>>> img = Image.fromarray(data, 'RGB')
>>> ten_crop = transform.TenCrop(16)
>>> imgs = ten_crop(img)
>>> imgs[0].size, imgs[1].size, imgs[2].size, imgs[8].size, imgs[9].size
((16, 16), (16, 16), (16, 16), (16, 16), (16, 16))
'''
def __init__(self, size, vertical_flip=False):
self.size = size
if isinstance(size, numbers.Number):
self.size = (int(size), int(size))
else:
assert len(size) == 2, "Please provide only two dimensions (h, w) for size."
self.size = size
self.vertical_flip = vertical_flip
def __call__(self, img:Image.Image):
if not isinstance(img, Image.Image):
img = to_pil_image(img)
return F_pil.ten_crop(img, self.size, self.vertical_flip)
def __repr__(self):
return self.__class__.__name__ + '(size={0}, vertical_flip={1})'.format(self.size, self.vertical_flip)
[文档]
class RandomRotation(object):
'''
对图像进行随机旋转。
参数:
- degrees (sequence, float, int): 选择的角度范围,如果degrees是一个数字而不是像(min, max)这样的序列,角度的范围将为(-degrees, +degrees)。
- resample ({PIL.Image.NEAREST, PIL.Image.BILINEAR, PIL.Image.BICUBIC}, 可选):可选的重采样滤波器。请参阅 `链接 <https://pillow.readthedocs.io/en/latest/handbook/concepts.html#filters>`__ 以获取更多信息。如果省略或图像的模式为1或P,则设置为 `PIL.Image.NEAREST` 。默认值: False
- expand (bool, 可选): 可选的扩展标志。若为真,则扩展输出以使其足够大以容纳整个旋转后的图像;为假或省略,则使输出图像与输入图像的尺寸相同。请注意,扩展标志假定围绕中心旋转且不进行平移。默认值: False
- center (2-tuple, 可选): 可选的旋转中心,原点是左上角。默认值: 图像的中心。默认值: None
- fill (n-tuple, int, float, 可选): 旋转后图像之外区域的像素填充值。如果是整数或浮点数,分别用于所有通道。默认情况下,所有通道的值都为0。此选项仅在`pillow>=5.2.0`版本中可用。默认值: None
代码示例:
>>> from jittor import transform
>>> random_rotation = transform.RandomRotation(degrees=45)
>>> rotated_image = random_rotation(input_image)
'''
def __init__(self, degrees, resample=False, expand=False, center=None, fill=None):
if isinstance(degrees, numbers.Number):
if degrees < 0:
raise ValueError("If degrees is a single number, it must be positive.")
self.degrees = (-degrees, degrees)
else:
if len(degrees) != 2:
raise ValueError("If degrees is a sequence, it must be of len 2.")
self.degrees = degrees
self.resample = resample
self.expand = expand
self.center = center
self.fill = fill
@staticmethod
def get_params(degrees):
"""Get parameters for ``rotate`` for a random rotation.
Returns:
sequence: params to be passed to ``rotate`` for random rotation.
"""
angle = random.uniform(degrees[0], degrees[1])
return angle
def __call__(self, img:Image.Image):
"""
Args:
img (PIL Image): Image to be rotated.
Returns:
PIL Image: Rotated image.
"""
if not isinstance(img, Image.Image):
img = to_pil_image(img)
angle = self.get_params(self.degrees)
return F_pil.rotate(img, angle, self.resample, self.expand, self.center, self.fill)
def __repr__(self):
format_string = self.__class__.__name__ + '(degrees={0}'.format(self.degrees)
format_string += ', resample={0}'.format(self.resample)
format_string += ', expand={0}'.format(self.expand)
if self.center is not None:
format_string += ', center={0}'.format(self.center)
format_string += ')'
return format_string
[文档]
class RandomAffine(object):
'''
随机仿射变换,输入保持中心不变。
参数:
- degrees (序列或int) : 可供选择的度数范围。如果度数是数字而不是序列(min, max) ,则度数范围将为 (-degrees, +degrees)。
- translate (元组, 可选): 例如 translate=(a, b),那么水平平移将在 img_width * a < dx < img_width * a 的范围内随机采样,垂直平移将在 -img_height * b < dy < img_height * b 的范围内随机采样。默认情况下不进行平移。默认值: None
- scale (元组, 可选): 缩放因子区间,例如 (a, b),则缩放比例在 a <= scale <= b 的范围内随机采样。默认值: None
- shear (序列, float或int, 可选): 从中选取剪切角度的范围。 如果 shear 是一个数字,则将应用范围是 (-shear, +shear) 并且平行于 x 轴的剪切;若是一个元组或列表且包含2个值,则应用范围是 (shear[0], shear[1]) 并且平行于 x 轴的剪切; 若是一个元组或列表且包含4个值,则应用 x 轴剪切范围 (shear[0], shear[1]) 以及y 轴剪切范围 (shear[2], shear[3])。默认情况下不应用剪切。默认值: None
- resample ({PIL.Image.NEAREST, PIL.Image.BILINEAR, PIL.Image.BICUBIC}, 可选): 可选的重采样滤波器。请参阅 `这里 <https://pillow.readthedocs.io/en/latest/handbook/concepts.html#filters>`__ 以获取更多信息。默认值: False
- fillcolor (元组或int, 可选): 对输出图像中变换区域外的区域填充的颜色(RGB 图像用元组表示,灰度图像用整数表示)。默认值: 0
代码示例:
>>> from jittor import transform
>>> random_affine = transform.RandomAffine(degrees=45)
>>> img_ = random_affine(img)
'''
def __init__(self, degrees, translate=None, scale=None, shear=None, resample=False, fillcolor=0):
if isinstance(degrees, numbers.Number):
if degrees < 0:
raise ValueError("If degrees is a single number, it must be positive.")
self.degrees = (-degrees, degrees)
else:
assert isinstance(degrees, (tuple, list)) and len(degrees) == 2, \
"degrees should be a list or tuple and it must be of length 2."
self.degrees = degrees
if translate is not None:
assert isinstance(translate, (tuple, list)) and len(translate) == 2, \
"translate should be a list or tuple and it must be of length 2."
for t in translate:
if not (0.0 <= t <= 1.0):
raise ValueError("translation values should be between 0 and 1")
self.translate = translate
if scale is not None:
assert isinstance(scale, (tuple, list)) and len(scale) == 2, \
"scale should be a list or tuple and it must be of length 2."
for s in scale:
if s <= 0:
raise ValueError("scale values should be positive")
self.scale = scale
if shear is not None:
if isinstance(shear, numbers.Number):
if shear < 0:
raise ValueError("If shear is a single number, it must be positive.")
self.shear = (-shear, shear)
else:
assert isinstance(shear, (tuple, list)) and \
(len(shear) == 2 or len(shear) == 4), \
"shear should be a list or tuple and it must be of length 2 or 4."
# X-Axis shear with [min, max]
if len(shear) == 2:
self.shear = [shear[0], shear[1], 0., 0.]
elif len(shear) == 4:
self.shear = [s for s in shear]
else:
self.shear = shear
self.resample = resample
self.fillcolor = fillcolor
@staticmethod
def get_params(degrees, translate, scale_ranges, shears, img_size):
"""Get parameters for affine transformation
Returns:
sequence: params to be passed to the affine transformation
"""
angle = random.uniform(degrees[0], degrees[1])
if translate is not None:
max_dx = translate[0] * img_size[0]
max_dy = translate[1] * img_size[1]
translations = (np.round(random.uniform(-max_dx, max_dx)),
np.round(random.uniform(-max_dy, max_dy)))
else:
translations = (0, 0)
if scale_ranges is not None:
scale = random.uniform(scale_ranges[0], scale_ranges[1])
else:
scale = 1.0
if shears is not None:
if len(shears) == 2:
shear = [random.uniform(shears[0], shears[1]), 0.]
elif len(shears) == 4:
shear = [random.uniform(shears[0], shears[1]),
random.uniform(shears[2], shears[3])]
else:
shear = 0.0
return angle, translations, scale, shear
def __call__(self, img:Image.Image):
"""
img (PIL Image): Image to be transformed.
Returns:
PIL Image: Affine transformed image.
"""
if not isinstance(img, Image.Image):
img = to_pil_image(img)
ret = self.get_params(self.degrees, self.translate, self.scale, self.shear, img.size)
return F_pil.affine(img, *ret, resample=self.resample, fillcolor=self.fillcolor)
def __repr__(self):
s = '{name}(degrees={degrees}'
if self.translate is not None:
s += ', translate={translate}'
if self.scale is not None:
s += ', scale={scale}'
if self.shear is not None:
s += ', shear={shear}'
if self.resample > 0:
s += ', resample={resample}'
if self.fillcolor != 0:
s += ', fillcolor={fillcolor}'
s += ')'
d = dict(self.__dict__)
d['resample'] = str(d['resample'])
return s.format(name=self.__class__.__name__, **d)