22.01.19 CNN, 이미지 데이터

2022. 1. 19. 14:43작업/ComputerVision

CNN은 대표적인 딥러닝 모델 -> 이미지(JPG,PNG) 분류에 특화되어있다.

 

픽셀

이미지의 기본 단위 : 픽셀

픽셀 하나당 색깔, 밝기 정보를 가진다.

각 픽셀은 3가지 색 값을 저장할 수 있으므로 RGB 채널이 있다.

그 RGB 채널은 8비트(01001110)의 수로 이뤄져있다. R:0~255 , G:0~255 , B:0~255 가능

총 8*3 = 24 비트, 2^24개의 색을 표현 가능하다

한가지 값만 가지면 흑백, 밝기만 저장함.

 

딥러닝을 활용한 이미지처리 기술 분야

1. 사물인식(Object Detection)

센터 스테이지 : 사람 영상에서 사람얼굴을 인식해 화면 가운데에게 오게 해주는 apple의 기술

2. 이미지 캡셔닝 ( Image Captioning)

사진을 보고 설명하는 기술? -> 자연어처리도 필요함

3. 이미지 합성( Deep Fake)

4. 화질 개선( Super Resolution, DLSS)

4K 모니터 (고해상도) 는 게임을 돌릴 때는 애로사항이 있다. 고해상도 모니터는 픽셀 수가 많다보니 1번 그릴 때(FPS) 그려야하는 픽셀수가 많아서 FPS가 끊길 수 있다.

NVidia의 기술 : 일단 저해상도로 그리고(FPS가 높다), 실시간으로 딥러닝으로 고해상도로 바꾼다. 이 모든 작업을 그래픽카드 내부에 장착을 시킨다. -> FPS자체는 저해상도에서 그렸을 떄의 값을 고정하면서도 화면의 품질은 고해상도처럼 만든다.

 

5. 알파고

딥러닝 이전의 이미지 처리 기법

1. 형태 변환

2. 색상 변환

3. 필터 변환

 

1. 형태 변환

2. 색상변환

흑백변화 : RGB중 하나만(R)만 가져옴 그 색의 세기를 밝기로 바꿔버리면 흑백이 되는 것.

 

3. 필터 변환 -> CNN에서 핵심 역할을 수행

샤프닝 : noise까지도 포함, 더 선명한 이미지

블러: 인물사진(초점 같은 느낌, 모자이크)

경계선 감지 : 이미지분류(사물 구분)에도 쓰임

 

PIL ( Pillow) Python Imaging Library

img = Image.open('이미지.png')

img라는 변수에 Pillow의 Image 클래스 객체가 저장된다. 

img클래스는 size, mode 속성이 있다.

crop : 전체 이미지 안에서 특정 영역만 잘라내는 작업을 의미, Image클래스의 crop 메소드 사용

crop 메소드는 이미지 내의 4개의 좌표를 튜플 형태로 넣어준다.

 

이미지 좌표는 (x,y)로 이뤄지고 0,0은 왼쪽 위가 된다.

crop메소드는 잘라낼 영역의 왼쪽상단, 오른쪽 아래 총 x1,y1, x2,y2 두 좌표가 필요하다. 이 두 개를 튜플로 요구한다

img.crop((x1,y1,x2,y2)) 이렇게 하나의 튜플에 넣어서 주면 된다.

이미지 회전 img.rotate 메소드 사용 -> 시계방향 또는 반시계 방향으로 일정 각도만큼 회전

expand = True

크기/비율 변환

img.resize 메소드 : 크기 변환은 세로,가로 길이를 변화시키는 것이고 비율 변환은 기존의 이미지가 가지고 있던 가로와 세로 길이의 비율을 변환하는 작업. resize 혹은 rescale로 부르며 resize메소드로 통일해서 수행한다.

바꾸고자 하는 가로와 세로의 픽셀 길이를 튜플로 넣어주면 된다.

원본은 (512,512)였는데 (128,128)로 resize를 했다. 그리고 픽셀 수를 줄였기 떄문에 이미지의 선명도 또한 줄어들었다.

이것은 비율은 무시한 채 원하는 크기로 바꾸는 것이다. 128,256으로 했다

 

전단 변환(층 밀림 변환)

사각형의 이미지를 평행사변형 꼴로 바꾸는 변환

transform메소드를 사용한다. 전단변환 뿐 아니라 다른 형태의 변환기술들도 사용할 수 있다

img.size[0] 은 512, img.size[1]은 512일 듯

img.transform((픽셀,픽셀) , 변환방법)

이미지 색상 변환 - 밝기변화(Brightness), 대조변화(Contrast), 흑백변화(Grayscale)

PIL에서 ImageEnhance를 import한다.

 

밝기변화

ImageEnhance의 Brightness 클래스를 사용. 이 클래스는 객체 생성을 위해 image 객체를 파라미터로 요구한다.

객체.enhance(밝기 배)

 

대조 변화 - Contrast 클래스 사용

흑백 변화

이미지의 컬러 여부는 mode속성으로 확인하는데, 이걸 RGB에서 흑백 모드로 바꾸는 작업을 한다.

PIL에서 흑백 변화를 하려면 Image 클래스의 convert 메소드를 사용한다.

img.convert('L')

필터 변환 - 샤프닝, 블러, Edge detection

Image 클래스의 filter 메소드를 사용

블러

경계선 감지 edge detection

이미지 형태 변환하기

from elice_utils import EliceUtils

elice_utils = EliceUtils()

from PIL import Image

def crop(img, coordinates):
    # TODO: [지시사항 1번] 이미지를 자르는 코드를 완성하세요.
    img_crop = img.crop(coordinates)
    
    return img_crop
    
def rotate(img, angle, expand=False):
    # TODO: [지시사항 2번] 이미지를 회전하는 코드를 완성하세요.
    img_rotate = img.rotate(angle, expand=expand)
    
    return img_rotate
    
def resize(img, new_size):
    # TODO: [지시사항 3번] 이미지 크기를 변경하는 코드를 완성하세요.
    img_resize = img.resize(new_size)
    
    return img_resize
    
def shearing(img, shear_factor):
    # TODO: [지시사항 4번] 이미지를 전단 변환하는 코드를 완성하세요.
    img_shearing = img.transform((int(img.size[0] * (1 + shear_factor)), img.size[1]),
                            Image.AFFINE, (1, -shear_factor, 0, 0, 1, 0))
                                 
    return img_shearing
    
def show_image(img, name):
    img.save(name)
    elice_utils.send_image(name)

def main():
    img = Image.open("Lenna.png")
    
    # TODO: [지시사항 5번] 지시사항에 따라 적절한 이미지 변환을 수행하세요.
    
    # 이미지 자르기
    img_crop = crop(img,(150,200,450,400))
    
    # 이미지 회전하기
    img_rotate = rotate(img,160,expand=True)
    
    # 이미지 크기 바꾸기
    img_resize = resize(img,(640,360))
    
    # 이미지 전단 변환
    img_shearing = shearing(img,0.8)
    
    print("=" * 50, "Crop 결과", "=" * 50)
    show_image(img_crop, "crop.png")
    
    print("=" * 50, "Rotate 결과", "=" * 50)
    show_image(img_rotate, "rotate.png")
    
    print("=" * 50, "Resize 결과", "=" * 50)
    show_image(img_resize, "resize.png")
    
    print("=" * 50, "Shearing 결과", "=" * 50)
    show_image(img_shearing, "shearing.png")
    
    return img_crop, img_rotate, img_resize, img_shearing

if __name__ == "__main__":
    main()

from elice_utils import EliceUtils

elice_utils = EliceUtils()

from PIL import Image
from PIL import ImageEnhance

def change_brightness(img, factor):
    # TODO: [지시사항 1번] 이미지의 밝기를 변화시키는 코드를 완성하세요.
    bright_enhancer = ImageEnhance.Brightness(img)
    img_bright = bright_enhancer.enhance(factor)
    
    return img_bright
    
def change_contrast(img, factor):
    # TODO: [지시사항 2번] 이미지의 대조를 변화시키는 코드를 완성하세요.
    contrast_enhancer = ImageEnhance.Contrast(img)
    img_contrast = contrast_enhancer.enhance(factor)
    
    return img_contrast
    
def change_grayscale(img):
    # TODO: [지시사항 3번] 이미지를 흑백 이미지로 변경하는 코드를 완성하세요.
    img_gray = img.convert('L') # 모드를 RGB에서 L로 바꾼다.
    
    return img_gray
    
def show_image(img, name):
    img.save(name)
    elice_utils.send_image(name)

def main():
    img = Image.open("Lenna.png")
    
    # TODO: [지시사항 4번] 지시사항에 따라 적절한 이미지 변환을 수행하세요.
    
    # 이미지 밝게 하기
    img_bright = change_brightness(img, 1.5)
    
    # 이미지 어둡게 하기
    img_dark = change_brightness(img,0.2) # 5배 어둡게
    
    # 이미지 대조 늘리기
    img_high_contrast = change_contrast(img, 3)
    
    # 이미지 대조 줄이기
    img_low_contrast = change_contrast(img, 0.1)
    
    # 이미지 흑백 변환
    img_gray = change_grayscale(img)
    
    print("=" * 50, "밝은 이미지", "=" * 50)
    show_image(img_bright, "bright.png")
    
    print("=" * 50, "어두운 이미지", "=" * 50)
    show_image(img_dark, "dark.png")
    
    print("=" * 50, "강한 대조 이미지", "=" * 50)
    show_image(img_high_contrast, "high_contrast.png")
    
    print("=" * 50, "약한 대조 이미지", "=" * 50)
    show_image(img_low_contrast, "low_contrast.png")
    
    print("=" * 50, "흑백 이미지", "=" * 50)
    show_image(img_gray, "gray.png")
    
    return img_bright, img_dark, img_high_contrast, img_low_contrast, img_gray

if __name__ == "__main__":
    main()

from elice_utils import EliceUtils

elice_utils = EliceUtils()

from PIL import Image
from PIL import ImageFilter

def sharpening(img):
    # TODO: [지시사항 1번] 이미지에 샤프닝 필터를 적용시키는 코드를 완성하세요.
    img_sharpen = img.filter(ImageFilter.SHARPEN)
    
    return img_sharpen
    
def blur(img):
    # TODO: [지시사항 2번] 이미지에 블러 필터를 적용시키는 코드를 완성하세요.
    img_blur = img.filter(ImageFilter.BLUR)
    
    return img_blur
    
def detect_edge(img):
    # TODO: [지시사항 3번] 이미지의 경계선을 탐지하는 코드를 완성하세요.
    img_edge = img.filter(ImageFilter.FIND_EDGES)
    
    return img_edge
    
def show_image(img, name):
    img.save(name)
    elice_utils.send_image(name)

def main():
    img = Image.open("Lenna.png")
    
    # TODO: [지시사항 4번] 지시사항에 따라 적절한 이미지 변환을 수행하세요.
    
    # 이미지 샤프닝 한번 적용하기
    img_sharpen_1 = sharpening(img)
    
    # 이미지 샤프닝 5번 적용하기
    img_sharpen_5 = sharpening(img)
    img_sharpen_5 = sharpening(img_sharpen_5)
    img_sharpen_5 = sharpening(img_sharpen_5)
    img_sharpen_5 = sharpening(img_sharpen_5)
    img_sharpen_5 = sharpening(img_sharpen_5)
    
    # 이미지 블러 한번 적용하기
    img_blur_1 = blur(img)
    
    # 이미지 블러 5번 적용하기
    img_blur_5 = blur(img)
    img_blur_5 = blur(img_blur_5)
    img_blur_5 = blur(img_blur_5)
    img_blur_5 = blur(img_blur_5)
    img_blur_5 = blur(img_blur_5)
    
    
    # 이미지 경계선 찾기
    img_edge = detect_edge(img)
    
    print("=" * 50, "샤프닝 한번 적용한 이미지", "=" * 50)
    show_image(img_sharpen_1, "sharpen_1.png")
    
    print("=" * 50, "샤프닝 다섯번 적용한 이미지", "=" * 50)
    show_image(img_sharpen_5, "sharpen_5.png")
    
    print("=" * 50, "블러 한번 적용한 이미지", "=" * 50)
    show_image(img_blur_1, "blur_1.png")
    
    print("=" * 50, "블러 다섯번 적용한 이미지", "=" * 50)
    show_image(img_blur_5, "blur_5.png")
    
    print("=" * 50, "경계선 이미지", "=" * 50)
    show_image(img_edge, "edge.png")
    
    return img_sharpen_1, img_sharpen_5, img_blur_1, img_blur_5, img_edge


if __name__ == "__main__":
    main()

from elice_utils import EliceUtils

elice_utils = EliceUtils()

from PIL import Image
import matplotlib.pyplot as plt
import os
import numpy as np

def show_plot(img, title=" "): # 이미지 저장하고 보여주기
    plt.title(title)
    plt.imshow(img)
    plt.savefig("tmp.png")
    elice_utils.send_image("tmp.png")

def load_image(path, name):
    # TODO: [지시사항 1번] 이미지를 불러오는 함수를 완성하세요
    
    # path = 'dataset/val/dogs'
    # name = 'dog.0.jpg' # 'dataset/val/dogs/dog.0.jpg'로 만들어야함
    img = Image.open(os.path.join(path,name))
    return img
    
def main():
    data_path = "dataset/val/dogs"   
    
    
    # 이미지를 불러와 plt를 이용하여 출력합니다
    names = os.listdir(data_path)
    img = load_image(data_path, names[0])
    
    # 원본 이미지를 출력
    show_plot(img, "PIL original image")
    
    
    # TODO: [지시사항 2번] 지시사항에 따라 이미지의 크기를 확인하는 코드를 완성하세요.
    # PIL을 통해 이미지 크기 확인
    pil_size = img.size
    print("PIL을 통한 이미지 크기:", pil_size)
    
    # PIL 이미지를 numpy 배열로 변환
    np_img = np.array(img)
    
    # numpy 배열의 shape 확인
    np_shape = np_img.shape
    print("numpy 배열 shape:", np_shape)
    show_plot(np_img, "Numpy array image")
    
    # TODO: [지시사항 3번] PIL과 numpy를 이용하여 이미지를 다루는 코드를 완성하세요.
    # PIL.Image에서 x=10, y=20 의 픽셀값 가져오기
    pil_pix = img.load()[10, 20]
    
    # numpy 배열에서 x=10, y=20 의 픽셀값을 가져오세요
    np_pix = np_img[20,10] # numpy랑 pil이랑 가로세로 반대로 가져와야함!
    print("PIL의 픽셀값: {}, numpy의 픽셀값: {}".format(pil_pix, np_pix))
    
    # PIL을 이용하여 이미지의 크기를 (224,224)로 변형하세요.
    resized_img = img.resize((224,224))
    # 224,224인 이유 ImageNet이라는 유명한 딥러닝학습방법의 데이터셋이 224,224이기 때문이다.
    
    # resize된 이미지 출력
    show_plot(resized_img, "Resized image")
    print("resize 결과 사이즈:", resized_img.size)
    
    return pil_size, np_img, np_shape, np_pix, resized_img

if __name__ == "__main__":
    main()

import os
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'

import tensorflow as tf
from tensorflow.keras.preprocessing.image import ImageDataGenerator
data_path = "dataset"

batch_size = 2
img_height = 180
img_width = 180

# path의 데이터를 ImageDataGenerator로 불러와주는 함수
def get_dataset(path, datagen):
    data_set = datagen.flow_from_directory(path,
                                           target_size=(img_width, img_height),
                                           batch_size=batch_size, # 한 번에 이미지 두 장씩
                                           class_mode='categorical') # cats, dogs 두 개면 binary를 쓰기도 함.
    return data_set

def main():
    # TODO: [지시사항 1번] 정규화 과정이 없는 ImageDataGenerator를 만드세요.
    first_gen = ImageDataGenerator()
    first_set = get_dataset(os.path.join(data_path, "val"), first_gen)
    x, y = first_set.__next__()

    print("\n1. 데이터 제너레이터 만들기")
    print("first_set")
    print("x: {}, y: {}".format(x.shape, y.shape))
    print(x[0][0][0]) # 픽셀이 0~255의 값을 가짐

    # TODO: [지시사항 2번] 픽셀값을 0~1의 값으로 정규화 하는 ImageDataGenerator를 만드세요.
    second_gen = ImageDataGenerator(rescale=1/255)
    second_set = get_dataset(os.path.join(data_path, "val"), second_gen)
    x, y = second_set.__next__()    
    
    print("\n2. 데이터 제너레이터에 정규화 추가하기")
    print("second_set")
    print("x: {}, y: {}".format(x.shape, y.shape))
    print(x[0][0][0]) # 픽셀이 0~1의 값을 가지는 것을 확인하세요

    # TODO: [지시사항 3번] 실제 학습을 위한 ImageDataGenerator를 만드세요.
    # 학습 데이터를 위한 ImageDataGenerator를 만드세요.
    train_gen = ImageDataGenerator(rescale=1/255)
    
    # 학습 데이터셋을 불러오도록 경로명을 설정하세요.
    train_set = get_dataset(os.path.join(data_path,'train'), train_gen) #dataset/train

    # 검증 데이터를 위한 ImageDataGenerator를 만드세요.
    valid_gen = ImageDataGenerator(rescale=1/255)
    
    # 검증 데이터셋을 불러오도록 경로명을 설정하세요.
    valid_set = get_dataset(os.path.join(data_path,'val'), valid_gen)

    print("\n3. 실제 학습을 위한 데이터 제너레이터 작성")
    print("학습 데이터의 길이: ", len(train_set))
    print("검증 데이터의 길이: ", len(valid_set))
    
    return first_gen, second_gen, train_gen, train_set, valid_gen, valid_set

if __name__ == "__main__":
    main()