Joonas' Note

Joonas' Note

[딥러닝 일지] 이미지 가지고 놀기 (변환하기) 본문

AI/딥러닝

[딥러닝 일지] 이미지 가지고 놀기 (변환하기)

2022. 3. 24. 01:27 joonas

    이전 글 - [딥러닝 일지] 다른 모델도 써보기 (Transfer Learning)


    오늘은 다음 주제를 다루는 과정에서, 이미지를 여러 방법으로 조작하는 것에 대해서 알아보았다.

    PIL

    먼저, 파이썬에서는 이미지 라이브러리로 PIL(Python Imaging Library) 패키지가 매우 많이 쓰이는 것 같다.

    많이 쓰이는 만큼, NumPy와 Tensor와도 호환되는 만들어주는 함수들이 있어서 자주 쓰는데 헷갈린다.
    그래서 아래처럼 정리했다.

    from torchvision.transforms.functional import to_pil_image
    
    def pil_to_tensor(pil_image):
        # PIL: [width, height]
        # -> NumPy: [width, height, channel]
        # -> Tensor: [channel, width, height]
        return torch.as_tensor(np.asarray(pil_image)).permute(2,0,1)
    
    def tensor_to_pil(tensor_image):
        return to_pil_image(tensor_image)
    
    def tensor_to_pltimg(tensor_image):
        return tensor_image.permute(1,2,0).numpy()

    PIL로 이미지를 로드한 후에, NumPy로 변환하면 [width, height, channel] 모양으로 된다. 

    그리고 torchvision의 transforms들은 PIL Image를 지원한다. 하지만 일부 transforms들이 Tensor를 넘겨야하기 때문에, PIL Image를 텐서로 변환해주는 함수도 있다. 바로 transforms.PILToTensor() 이다.

    아래에서 설명할 각 transforms들이 [height, width, channel] 순서의 텐서를 처리하기 때문에, PIL Image transforms.PILToTensor()를 쓰는 것이 좋을 것 같다.

    PIL로 이미지를 열어서, tensor로 바꿔보고, 다시 PIL 이미지로 바꾸면 정상적으로 보인다.

    pil_image = PIL.Image.open(image_pathes[0])
    tensor_to_pil(pil_to_tensor(pil_image))

    이미지의 출처는 https://www.kaggle.com/puneet6060/intel-image-classification 에서 train 데이터 중 buildings에 있는 것을 아무거나 사용했다.

    torchvision.transforms

    https://pytorch.org/vision/stable/_modules/torchvision/transforms/transforms.html

    PyTorch의 코드는 공개되어있어서 어떻게 구현되어있는지 볼 수 있는데, 오늘은 이 transforms에서 제공하는 Random 함수들에 대해서 알아볼까한다.

    • RandomRotation
    • RandomApply
    • RandomChoice
    • RandomOrder
    • RandomCrop
    • RandomResizedCrop
    • RandomGrayscale
    • RandomPerspective
    • RandomErasing
    • RandomInvert
    • RandomPosterize
    • RandomSolarize
    • RandomAdjustSharpness
    • RandomAutocontrast
    • RandomEqualize

    어떤 것인지 궁금하기도 하고, 나중에 기억이 안 나면 와서 볼 수 있게 결과까지 하나씩 살펴보자.

    적용하기 전에

    모든 transform들이 확률에 따라 랜덤하게 적용되기 때문에, 총 9개의 이미지에 적용시켜서 3x3 으로 확인할 것이다.

    import matplotlib.pyplot as plt
    import torchvision.transforms as transforms
    
    ## 여기를 바꿔가며 확인
    transform = ...
    #######
    
    plt.figure(figsize=(8, 8))
    for i in range(9):
        pil_image = PIL.Image.open(image_pathes[0])
        tensor = pil_to_tensor(pil_image)
        applied_image = transform(tensor)
        
        plt.subplot(3, 3, i+1)
        plt.imshow(tensor_to_pltimg(applied_image))
        plt.axis("off")
    plt.show()

    아무것도 적용하지 않은 상태는 이렇다.

    이미지의 크기는 (150, 150) 이다.

     

    RandomHorizontalFlip

    수평으로 뒤집는다. 즉, 좌우반전이다. 확률의 기본값은 0.5 (50%). p 옵션으로 확률을 조정할 수 있다.

    transform = transforms.RandomHorizontalFlip()

    RandomVerticalFlip

    수직으로 뒤집는다. 즉, 상하반전이다. 확률의 기본값은 0.5 (50%). p 옵션으로 확률을 조정할 수 있다.

    transform = transforms.RandomVerticalFlip()

    RandomRotation

    주어진 각도(degrees)에 랜덤하게 회전시킨다. 각도는 (-degree, +degree) 사이만큼 회전한다고 한다.
    center 옵션으로 회전의 중심점을 지정할 수 있다.

    transform = transforms.RandomRotation(180)

    RandomAffine

    RandomRotation과 같이 degrees를 받아서 회전을 시키지만, 아핀 공간(Affine space)에서 변형한다고 한다. 그래서 shear 같이 기울이는 옵션도 있다.

    transform = transforms.RandomAffine(180, shear=20)

     

    RandomChoice

    파라미터로 넘긴 transform 배열에서 하나만 랜덤으로 선택하여 적용한다.

    transform = transforms.RandomChoice([
        transforms.RandomHorizontalFlip(),
        transforms.RandomVerticalFlip(),
        transforms.RandomRotation(180),
    ])

    RandomChoice

    RandomApply

    RandomChoice와 비슷하지만, 확률에 따라 여러개가 적용될 수도 있다. 끝에 p 옵션으로 확률을 설정할 수 있다.

    transform = transforms.RandomApply([
        transforms.RandomHorizontalFlip(),
        transforms.RandomVerticalFlip(),
        transforms.RandomRotation(180),
    ], p=0.9)

    뒤집고 돌리는 것이 중첩으로 적용된 것이 몇 개 있다.

    RandomCrop

    (width, height)의 잘라낼 크기를 설정하면, 그 크기만큼 랜덤으로 잘라낸다.

    crop의 크기가 이미지의 크기보다 크면 에러가 난다.

    transform = transforms.RandomCrop((60, 60))

    RandomCrop((60, 60))

    RandomResizedCrop

    RandomCrop과 비슷하지만 다르다.

    이 경우에는 랜덤하게 Crop한 후에 주어진 크기만큼 Resize를 해준다. 그렇기때문에 크기를 이미지의 크기보다 크게 넘겨도 오류가 나지 않는다.

    transform = transforms.RandomResizedCrop((240, 240))

    RandomOrder

    주어진 transform들을 모두 적용하는데, 그 순서를 랜덤하게 설정한다.

    내부적으로 random.shuffle을 사용하고 있다.

    transform = transforms.RandomOrder([
        transforms.RandomHorizontalFlip(),
        transforms.RandomVerticalFlip(),
        transforms.RandomRotation(180),
        transforms.RandomCrop((60, 60)),
    ])

    RandomGrayscale

    랜덤으로 이미지 전체의 색상을 grayscale로 변경한다.

    transform = transforms.RandomGrayscale(p=0.75)

    RandomPerspective

    이미지를 Perspective View로 옮겨서 특정 시점에서 보는 효과를 준다. 따라서 원근감이 생긴다.

    fill 옵션으로 뒷 배경을 검은색이 아니라 다른 색으로 지정할 수 있다. int로 넘어가면 해당 값을 모든 채널에 곱하고, [255, 0, 255]처럼 array로 넘기면 RGB 색상으로 지정할 수 있다.

    # transform = transforms.RandomPerspective()
    # transform = transforms.RandomPerspective(fill=128)
    transform = transforms.RandomPerspective(fill=[255,0,255])

    RandomErasing

    랜덤한 부분을 박스 모양으로 지운다. 확률에 따라 지우는 부분이 없을 수도 있다.

    이 함수는 PIL image에 바로 적용할 수가 없기 때문에, 반드시 tensor로 변환해서 써야한다.

    scale 옵션으로 지울 크기를 조정할 수 있고, ratio 옵션으로 박스의 비율 범위를 조정한다. value 옵션은 RandomPerspective 함수의 fill과 같다.

    transform = transforms.RandomErasing()

    RandomInvert

    확률에 따라 이미지 전체의 색상을 반전시킨다.

    transform = transforms.RandomInvert()

    RandomPosterize

    파라미터로 bits를 넘겨야하는데, 0~8 사이의 값이다. 각 채널에서 몇 개의 비트를 남길 지를 의미한다.
    문서(주석)에는 int라고 되어있는데, float로 넘겨도 동작한다. (왜지? 어떻게?)

    transform = transforms.RandomPosterize(1.5)

    RandomSolarize

    파라미터로 임계값(threshold)이 들어간다. 어떤 픽셀이 그 값보다 크면 반전시키는데, 채널별로 적용한다.

    아래 예시는 R, G, B 채널마다 값이 200.5보다 크면 해당 채널값을 반전한 결과이다. 그래서 일부는 빨갛게 보이기도한다.

    transform = transforms.RandomSolarize(200.5)

    RandomAdjustSharpness

    포토샵의 sharp 효과와 같다. 선명하게 만들고 싶은 정도(sharpness_factor)가 파라미터로 넘어가는데, 0은 blurred, 1은 원본, 2부터는 선명한 정도를 2의 배수만큼 적용하는 것 같다.

    adjust_sharpness로 별도로 사용할 수도 있다.

    transform = transforms.Compose([
        transforms.CenterCrop((60, 60)),
        transforms.RandomAdjustSharpness(4)
    ])

    RandomAutocontrast

    대조를 자동으로 조정해주는 효과인데, 육안으로 크게 차이를 확인하기 힘들다.

    transform = transforms.RandomAutocontrast()

    RandomAutocontrast를 적용한 이미지는 위에 타이틀을 표시했는데도, 무슨 차이가 있는 지 확인하기가 어렵다.

    이런 이미지에서는 차이가 확연히 보인다.

    RandomEqualize​

    Histogram Eqaulization이라는 효과를 적용합니다. 색상 히스토그램을 분포가 균일하게 펼쳐주는 효과를 냅니다.

    transform = transforms.RandomEqualize()

    3개는 변형되지 않은 원본 이미지이다.

    AutoAugment

    파이토치 내부적으로 정의하고 있는 정책(IMAGENET 등)을 사용해서 자동으로 Augmentation 할 수 있다.

    transform = transforms.AutoAugment(transforms.AutoAugmentPolicy.IMAGENET)

    공식 문서

    다 만들고 보니까 문서에 정리가 되어있었다. 아래 링크에서 다른 transform 함수들의 결과도 볼 수 있다.

     

    Illustration of transforms — Torchvision main documentation

    Shortcuts

    pytorch.org

    노트북

    여기는 테스트하면서 사용한 캐글 노트북이다.

    https://www.kaggle.com/code/joonasyoon/image-transforming?scriptVersionId=91548288 

     

    Comments