jittor.transform 源代码

# ***************************************************************
# 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)