Pytorch

[파이토치] 이미지 데이터 전처리/증강

왕초보코딩러 2024. 12. 28. 18:26
728x90

데이터 전처리/증강

torchvision의 transforms를 사용

이미지 데이터 전처리

데이터 다양성을 높이기 위해 증강

 

https://github.com/stonegyoung/Pytorch_Study/blob/master/%EB%8D%B0%EC%9D%B4%ED%84%B0%20%EC%A0%84%EC%B2%98%EB%A6%AC.ipynb

 

Pytorch_Study/데이터 전처리.ipynb at master · stonegyoung/Pytorch_Study

Pytorch 공부. Contribute to stonegyoung/Pytorch_Study development by creating an account on GitHub.

github.com


필요 라이브러리 임포트

import torch
from torchvision import transforms
from PIL import Image
import numpy as np
import matplotlib.pyplot as plt

 

 

먼저 원본 이미지(뉴진스 민지)를 PIL의 Image.open으로 확인해보겠습니다.

img = Image.open('./minji.png')
img

 

type 변경

PIL인 상태에서 numpy 형태로 바꿔주겠습니다.

원본 이미지의 넘파이는 numpy_img라는 변수로 받아놓겠습니다.

# PIL -> numpy
numpy_img = np.array(img)
numpy_img.shape # h, w, channel

numpy_img.shape

 

이미지 출력 함수

이제 numpy/tensor 상태의 배열을 이미지로 출력하는 함수를 만들겠습니다.

def imgshow(array, tensor=True): # 배열과 tensor인지 여부를 받는다
    if tensor: # tensor 배열이면
    	numpy_array = array.numpy() # 넘파이로 바꾼다
    	img = numpy_array.transpose(1,2,0) # tensor 형태(channel,h,w) -> numpy 형태(h,w,channel)
    else: # numpy 배열이면
    	img = array # 그대로
    plt.axis('off') # 눈금 제거
    plt.imshow(img) # 이미지 시각화

자주 쓰이는 속성

기본 변환

  • ToTensor()
  • Normalize(mean, std)

크기 변환

  • Resize((h,w))
  • CenterCrop(size)
  • RandomCrop(size)

데이터 증강

  • RandomHorizontalFlip(p), RandomVerticalFlip(p)
  • RandomRotation(degrees)
  • ColorJitter(brightness, contrast, saturation, hue)
  • RandomResizedCrop(size, scale, ratio)

transforms.Compose([변환할 전처리 리스트])로 묶어줍니다.


<기본 변환>

1. ToTensor()

이미지(PIL 이미지/Numpy 배열)를 텐서 형태로 변환

0~255 사이의 픽셀 값을 0~1 사이로 정규화(픽셀값 정규화)

tensor_transform = transforms.Compose([
    transforms.ToTensor()
])

 

원본 이미지 넘파이와 ToTensor()를 적용한 텐서와의 차이를 보겠습니다.

원본 넘파이 / ToTensor() 변환 텐서

shape 확인

 

  원본 ToTensor()
type numpy tensor
shape (h,w,channel) (channel,h,w)
픽셀 값 0~255 0~1

2. Normalize(mean, std)

각 채널(RGB / 흑백)의 값들을 정규화

각 채널의 값이 평균 0, 표준편차 1을 가지도록 하여 학습을 더 안정적이고 빠르게 만들어준다.

각 채널에서 mean을 빼고 std로 나눈다

  • 빨간색(R)은 평균이 0.485, 표준편차가 0.229
  • 초록색(G)은 평균이 0.456, 표준편차가 0.224
  • 파란색(B)은 평균이 0.406, 표준편차가 0.225
  • 흑백은 평균이 0, 표준편차가 1

 

민지 이미지는 컬러이기 때문에, RGB 값에 맞춰서 정규화해주겠습니다.

normalize_transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
])

 

 

ToTensor()만 한 것과 Normalize()를 한 것을 비교해보겠습니다.

ToTensor() / ToTensor() + Normalize()

 

 

확실히 보기 위해 이미지로 출력해보겠습니다

원본과 Normalize()의 차이를 살펴보겠습니다.

Normalize 전 / 후


<크기 변환>

1. Resize((h,w))

h,w 크기로 변경

Resize(size)처럼 하나만 쓰면 h, w 중 짧은 길이에 size를 맞춘다.

resize_transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.Resize((224,224))
])

 

ToTensor()만 했을 때와 Resize()를 적용했을 때의 shape을 보니 크기가 변환되었습니다.

 

 

이미지로 출력해보겠습니다.

Resize((224,224)
원본

 

이번에는 h,w 다 입력하지 않고 하나만 넣어보겠습니다.

resize_transform1 = transforms.Compose([
    transforms.ToTensor(),
    transforms.Resize(224)
])

 

원본(w가 더 짧음)
Resize((224,224)) / Resize(224)

h와 w 중 길이가 더 짧은 것에 224를 맞춥니다.


2. CenterCrop(size)

이미지 중앙 부분을 size x size 로 자른다.

CenterCrop((h,w))로도 적용 가능

centercrop_transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.CenterCrop(224)
])

 

Resize((224,224))와 shape은 똑같은데요. 이미지를 출력해보겠습니다.

 

Resize((224,224) / CenterCrop(224)

 


3. RandomCrop(size)

랜덤 위치에서 size x size 로 자른다

RandomCrop((h,w))로도 적용 가능

randomcrop_transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.RandomCrop(224)
])

랜덤으로 바뀝니다

 


<데이터 증강>

모델이 더 다양한 형태의 데이터를 학습할 수 있도록 원본 데이터를 변형하여 다양한 버전을 만들어 주는 과정


1. RandomHorizontalFlip(p) /  RandomVerticalFlip(p)

랜덤으로 이미지를 뒤집는다

p는 뒤집힐 확률(p = 0.5가 기본)

 

RandomHorizontalFlip(p)

랜덤으로 이미지를 좌우로 뒤집는다  

randomhorizontalflip_transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.RandomHorizontalFlip()
])

뒤집힐 확률이 0.5로 반반이라 한 번은 뒤집히고, 한 번은 안 뒤집혔습니다.

 

RandomVerticalFlip(p)

랜덤으로 이미지를 위아래로 뒤집는다  

randomverticalflip_transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.RandomVerticalFlip()
])


2. RandomRotation(degrees)

-degrees ~ +degrees 범위 내에서 랜덤하게 이미지 회전

randomrotation_transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.RandomRotation(30) # -30~30
])

 

-30~30도 사이 랜덤 값으로 각도가 회전됩니다

조금씩 각도가 다르게 회전됐습니다.


3. ColorJitter(brightness, contrast, saturation, hue)

이미지의 밝기, 대비, 채도, 색조를 랜덤하게 변경

  • brightness: 밝기를 변경. brightness=0.5는 밝기를 ±50% 범위 내에서 랜덤하게 변경
  • contrast: 대비를 변경. contrast=0.5는 대비를 ±50% 범위 내에서 랜덤하게 변경
  • saturation: 채도를 변경. saturation=0.5는 채도를 ±50% 범위 내에서 랜덤하게 변경
  • hue: 색조를 변경. hue=0.5는 색조를 ±50% 범위 내에서 랜덤하게 변경

변경 범위는 0이 기본

# 모두 변경
colorjitter_transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.ColorJitter(brightness=0.5, contrast=0.5, saturation=0.5, hue=0.5)
])

# 밝기만
bright_transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.ColorJitter(brightness=0.5)
])

# 대비만
contrast_transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.ColorJitter(contrast=0.5)
])

# 채도만
saturation_transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.ColorJitter(saturation=0.5)
])

# 색조만
hue_transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.ColorJitter(hue=0.5)
])

 

brightness

-50~50 범위 내에서 밝기 변경

밝기 변경1 / 밝기 변경2 / 원본

contrast

-50~50 범위 내에서 대비 변경

대비 변경1 / 대비 변경2 / 원본

saturation

-50~50 범위 내에서 채도 변경

채도 변경1 / 채도 변경2 / 원본

hue

-50~50 범위 내에서 색조 변경

 

brightness, contrast, saturation, hue

모두 변경

colorjitter_transform


4. RandomResizedCrop(size, scale, ratio)

이미지를 임의로 자르고, 지정된 크기로 리사이즈

scale과 ratio로 자를 영역의 크기와 비율을 조절 -> 자른 후에는 지정된 size로 리사이즈

 

scale=(0.3, 0.8)은 전체 이미지에서 30%에서 80% 사이에서 자르고,

ratio=(3/4, 4/3)은 자를 영역의 가로 세로 비율이 3:4에서 4:3 사이로 랜덤하게 선택

 

Resize와의 차이

Resize: 자르기 X

RandomResizedCrop: 자르기 있음

randomresizedcrop_transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.RandomResizedCrop(224, scale=(0.3,0.8), ratio=(3/4,4/3))
])

 

shape은 Resize((224, 224))와 동일하니 이미지를 출력해보겠습니다.

네 장 모두 RandomResizedCrop 결과

먼저 crop하고 resize를 하기 때문에 어떻게 crop하냐에 따라 결과가 달라집니다.

 

 


+ train과 test에 적용될 transform은 다를 수 있다.

train에서는 다양한 데이터를 위해 데이터 증강을 적용하지만,

test에서는 추론을 하기 때문에 증강하지 않기 때문(단순한 크기 변환같은 기본적인 전처리만 적용)