Skip to content

image_load

data.transform.image_load

IMAGE_MODES module-attribute

IMAGE_MODES = ('RGB', 'L')

ImageLoader

ImageLoader(image_mode: Union[str, List[str]] = 'RGB', to_numpy: bool = True)

여러 형태의 데이터를 입력으로 받아, 전처리 과정을 거쳐 PIL Image 혹은 np.ndarray 로 return 합니다. 현재 지원하는 데이터는 다음과 같습니다. - 데이터 타입: [image path, np.ndarray, PIL Image] - color: ["RGB", "Gray"] - bit: [8, 16]

입력으로 받은 데이터에 따른 출력값은 다음과 같습니다. | image path | np.ndarray | PIL | RGB; 8 | PIL(RGB;8) | PIL(RGB;8) | PIL(RGB;8) | RGB;16 | PIL(RGB;8) | PIL(RGB;8) | - | Gray; 8 | PIL(L;8) | PIL(L;8) | PIL(L;8) | Gray;16 | PIL(L;8) | PIL(L;8) | PIL(L;8) |

Note1

PIL은 RGB;16을 지원하지 않습니다. 따라서 PIL(RGB;16)은 입력으로 들어올 수 없습니다.

Note2

PIL은 "I;16" 모드로 Gray;16을 지원하지만, PIL 내부의 convert 함수를 써서 Gray;8로 변환시 값이 overflow 나는 issue가 있습니다.

Note3

이미지는 기본적으로 np.ndarray로 로드되며, PIL.Image.Image로 로드하려면 to_numpy=False를 세팅하세요.

Note4

image_mode는 "RGB", "L" 중 하나를 지원하며, multipage (이미지 리스트) 인 경우 각 페이지의 모드를 리스트로 입력하세요. 예시: 1, 3번째 페이지는 RGB 이고 2번째 페이지는 L 인 경우 image_mode = ["RGB", "L", "RGB"]

Source code in SaigeToolkit/data/transform/image_load.py
def __init__(self, image_mode: Union[str, List[str]] = "RGB", to_numpy: bool = True) -> None:
    if isinstance(image_mode, str):
        image_mode = [image_mode]
    elif not set(image_mode).issubset(set(IMAGE_MODES)):
        raise ValueError
    self.image_mode = image_mode
    self.to_numpy = to_numpy

call_method

call_method(image: Union[Image, ndarray, str, List], **data) -> Dict

make [PIL Image or np.ndarray] from PIL.Image, np.ndarray, or path string

Parameters:

  • image (Union[Image, ndarray, str, List]) –

    PIL.Image, array, path string or list of it.

Returns:

  • Dict ( Dict ) –

    { "image": Union[PIL.Image.Image, np.ndarray, List[PIL.Image.Image], List[np.ndarray]], **input_dict, }

Source code in SaigeToolkit/data/transform/image_load.py
def call_method(self, image: Union[Image.Image, np.ndarray, str, List], **data) -> Dict:
    """make [PIL Image or np.ndarray] from PIL.Image, np.ndarray, or path string

    Args:
        image (Union[Image.Image, np.ndarray, str, List]): PIL.Image, array, path string or list of it.

    Returns:
        Dict:
            {
                "image": Union[PIL.Image.Image, np.ndarray, List[PIL.Image.Image], List[np.ndarray]],
                **input_dict,
            }
    """
    multipage = isinstance(image, List)
    if not multipage:
        image = [image]

    if len(image) != len(self.image_mode):
        raise ValueError("number of images should match len(image_mode)")

    # 이미지들을 각자의 모드로 로드 (RGB 또는 L).
    loaded_images = []
    for image_i, image_mode_i in zip(image, self.image_mode):
        image_i = self.load(image_i)
        image_i = convert_image_mode(image=image_i, mode=image_mode_i, copy=False)
        loaded_images.append(image_i)

    if multipage:
        # multipage 이미지들의 사이즈가 모두 동일해야함.
        image_sizes = set(read_image_size(item) for item in loaded_images)
        if len(image_sizes) > 1:
            raise ValueError("sizef of images in multipage should be identical")
    else:
        loaded_images = loaded_images[0]

    data["image"] = loaded_images
    return data

load_from_path

load_from_path(path: str) -> Union[Image, ndarray]

Loading function using PIL.Image library. This function is introduced because {exif_transpose} must be processed. {exif_transpose} fix rotated image binary data using {EXIF} information. Without {exif_transpose}, network would accidently learn randomly rotated image data.

Parameters:

  • path (str) –

    path to image file

Returns:

  • Union[Image, ndarray]

    Union[Image.Image, np.ndarray]: image

Source code in SaigeToolkit/data/transform/image_load.py
def load_from_path(self, path: str) -> Union[Image.Image, np.ndarray]:
    """Loading function using PIL.Image library.
    This function is introduced because {exif_transpose} must be processed.
    {exif_transpose} fix rotated image binary data using {EXIF} information.
    Without {exif_transpose}, network would accidently learn randomly rotated image data.

    Args:
        path (str): path to image file

    Returns:
        Union[Image.Image, np.ndarray]: image
    """
    with open(path, "rb") as f:
        image = Image.open(f)
        image = ImageOps.exif_transpose(image)

    if self.to_numpy:
        image = np.array(image, dtype=np.uint16 if self._is_16bit(image) else np.uint8)

    if self._is_16bit(image):
        image = self._convert_16_to_8(image)

    return image

_convert_16_to_8 staticmethod

_convert_16_to_8(image: Union[Image, ndarray]) -> Union[Image, ndarray]

이미지 픽셀당 비트수를 16에서 8로 변화합니다. 입출력 데이터 타입이 같습니다. (pil로 받으면 pil을 ndarray로 받을 시 ndarray를 출력합니다.)

Source code in SaigeToolkit/data/transform/image_load.py
@staticmethod
def _convert_16_to_8(image: Union[Image.Image, np.ndarray]) -> Union[Image.Image, np.ndarray]:
    """
    이미지 픽셀당 비트수를 16에서 8로 변화합니다.
    입출력 데이터 타입이 같습니다. (pil로 받으면 pil을 ndarray로 받을 시 ndarray를 출력합니다.)
    """

    def _convert_16_to_8_np(array: np.ndarray) -> np.ndarray:
        return (array >> 8).astype(np.uint8)

    if isinstance(image, Image.Image):
        assert "I" in image.mode
        array_16bit = np.array(image, dtype=np.uint16)
        array_8bit = _convert_16_to_8_np(array_16bit)
        ret_image = Image.fromarray(array_8bit)
    elif isinstance(image, np.ndarray):
        assert image.dtype == np.uint16
        ret_image = _convert_16_to_8_np(image)
    else:
        raise NotImplementedError

    return ret_image

convert_image_mode

convert_image_mode(image: Union[Image, ndarray], mode: str, copy: bool = False) -> Union[Image, ndarray]

convert image mode

Parameters:

  • image (Union[Image, ndarray]) –

    image

  • mode (str) –

    "RGB" or "L"

  • copy (bool, default: False ) –

    to copy data. Defaults to False.

Returns:

  • Union[Image, ndarray]

    Union[Image.Image, np.ndarray]: converted image

Note

RGB -> L 변환 시 Image.Image와 np.ndarray 연산 결과가 다를 수 있음. (PIL과 cv2 에서 변환식은 L = R * 299/1000 + G * 587/1000 + B * 114/1000 로 동일하나 소숫점 처리 방식이 다름)

Source code in SaigeToolkit/data/transform/image_function.py
def convert_image_mode(
    image: Union[Image.Image, np.ndarray], mode: str, copy: bool = False
) -> Union[Image.Image, np.ndarray]:
    """convert image mode

    Args:
        image (Union[Image.Image, np.ndarray]): image
        mode (str): "RGB" or "L"
        copy (bool, optional): to copy data. Defaults to False.

    Returns:
        Union[Image.Image, np.ndarray]: converted image

    Note:
        RGB -> L 변환 시 Image.Image와 np.ndarray 연산 결과가 다를 수 있음.
        (PIL과 cv2 에서 변환식은 L = R * 299/1000 + G * 587/1000 + B * 114/1000 로 동일하나 소숫점 처리 방식이 다름)
    """
    if mode not in IMAGE_MODES:
        raise NotImplementedError

    if isinstance(image, Image.Image):
        if image.mode != mode:
            image = image.convert(mode)
        elif copy:
            image = image.copy()
    elif isinstance(image, np.ndarray):
        # check original mode & regularize (RGB: HW3 / L: HW)
        if image.ndim == 3:
            if image.shape[2] == 3:
                original_mode = "RGB"
            elif image.shape[2] == 1:
                original_mode = "L"
                image = image[:, :, 0]  # HW1 -> HW (no copy)
            elif image.shape[2] == 4:
                original_mode = "RGB"
                image = image[:, :, :3]  # RGBA -> RGB (no copy)
            else:
                raise NotImplementedError
        elif image.ndim == 2:
            original_mode = "L"
        else:
            raise NotImplementedError
        # convert
        if original_mode == "RGB" and mode == "L":
            image = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
        elif original_mode == "L" and mode == "RGB":
            image = cv2.cvtColor(image, cv2.COLOR_GRAY2RGB)
        elif copy:
            image = image.copy()
    else:
        raise NotImplementedError
    return image

read_image_size

read_image_size(image: Union[Image, Tensor, ndarray]) -> ImageSizeType

image size: (W, H)

Source code in SaigeToolkit/data/transform/image_function.py
def read_image_size(image: Union[Image.Image, torch.Tensor, np.ndarray]) -> ImageSizeType:
    """image size: (W, H)"""
    if isinstance(image, Image.Image):
        return image.size
    elif isinstance(image, torch.Tensor) and image.ndim in (2, 3, 4):  # HW, CHW, BCHW
        return (image.shape[-1], image.shape[-2])
    elif isinstance(image, np.ndarray) and image.ndim in (2, 3):  # HW, HWC
        return (image.shape[1], image.shape[0])
    else:
        raise NotImplementedError