Fundamental 16

2022. 1. 17. 11:20

1. 디지털 이미지

디지털 화면은 수많은 점들로 이루어져 있으며, 색상을 가지는 점 하나를 화소(pixel, picture element)라고 한다. 각 화소는 RGB(Red, Green, Blue) 세 개의 단일 색의 강도를 각각 조절하여 색상을 표현한다.

왜 RGB 3개의 영역으로 이루어져 있을까?

그 이유는 인간의 눈의 망막에 있는 시세포가 인간의 경우 대부분 세 가지로 이루어져 있기 때문이다.

💡 래스터(raster) or 비트맵(bitmap) 방식
빨강, 초록, 파랑 세 가지 색의 강도로 표현되는 점들로 구성된 디지털 화면에 표시될 이미지를 저장하는 방법, 보통 한 점마다 각 색상별로 8비트를 사용하여 0~255 사이의 값으로 해당 생의 감도를 표시한다.
💡 벡터(vector) 방식
상대적인 점과 선의 위치를 방정식으로써 기록해 두었다가, 확대 및 축소에 따라 디지털 화면의 각 화소에 어떻게 표현될지를 재계산한다.

2. Pillow 사용법

💡 Pillow란?
간단한 이미지 작업에 Pillow는 Numpy와 결합하여 간편하게 사용할 수 있는 도구
# Numpy배열을 만들고 PIL롤 이미지 객체 변환하기
import numpy as np
from PIL import Image

data = np.zeros([32, 32, 3], dtype=np.uint8)
image = Image.fromarray(data, 'RGB')
image

연습문제

# 1. 가로 세로 각 128 픽셀짜리 흰색 이미지를 만들어 화면에 표시해 봅시다
data = np.ones([128, 128, 3], dtype=np.uint8)
data *= 255 
image = Image.fromarray(data, 'RGB')
image

# 2. 연습용 이미지를 열어 width와 height를 출력하고, .save()를 이용하여 jpg 파일 포맷으로 저장해 봅시다.
#- 문제 2 -#
from PIL import Image
import os

# 연습용 파일 경로
image_path = os.getenv('HOME')+'/aiffel/python_image_proc/data/pillow_practice.png'

# 이미지 열기
img = Image.open(image_path)
img

# width와 height 출력
print(img.width)
print(img.height)

# JPG 파일 형식으로 저장해보기
new_image_path = os.getenv('HOME')+'/aiffel/python_image_proc/data/jpg_pillow_practice.jpg'
img = img.convert('RGB')
img.save(new_image_path)

# 3. .resize()를 이용하여 이미지 크기를 100X200으로 변경하여 저장해봅시다.
resized_image = img.resize((100,200))

resized_image_path = os.getenv('HOME')+'/aiffel/python_image_proc/data/pillow_practice_resized.png'
resized_image.save(resized_image_path)
resized_image

# 4. .crop()을 이용하여 눈 부분만 잘라내어 저장해 봅시다.
box = (300, 100, 600, 400)
region = img.crop(box)

cropped_image_path = os.getenv('HOME')+'/aiffel/python_image_proc/data/pillow_practice_cropped.png'
region.save(cropped_image_path)
region

3. Pillow를 활용한 데이터 전처리

CIFAR-100 데이터를 받아 개별 이미지 파일로 추출하기

CIFAR-100데이터

  • 32 x 32화소 해상도
  • 100개의 class 당 600장(각 학습용 500장, 테스트용 100장) 총 60000장
import os
import pickle
from PIL import Image

dir_path = os.getenv('HOME')+'/aiffel/python_image_proc/data/cifar-100-python'
train_file_path = os.path.join(dir_path, 'train')

with open(train_file_path, 'rb') as f:
    train = pickle.load(f, encoding='bytes')

데이터 확인

# dictionary의 key확인 
train.keys()
'''
dict_keys([b'filenames', b'batch_label', b'fine_labels', b'coarse_labels', b'data'])
'''
# filename확인 
train[b'filenames'][0:5]
'''
[b'bos_taurus_s_000507.png',
 b'stegosaurus_s_000125.png',
 b'mcintosh_s_000643.png',
 b'altar_boy_s_001435.png',
 b'cichlid_s_000031.png']
'''
# 데이터 확인 
train[b'data'][0:5]
'''
array([[255, 255, 255, ...,  10,  59,  79],
       [255, 253, 253, ..., 253, 253, 255],
       [250, 248, 247, ..., 194, 207, 228],
       [124, 131, 135, ..., 232, 236, 231],
       [ 43,  32,  87, ...,  60,  29,  37]], dtype=uint8)
'''
# 데이터의 shape확인
'''
(3072,) # 3072는 3channel x 1024(32x32)
'''

데이터 전처리

# 데이터 reshape 
image_data = train[b'data'][0].reshape([32, 32, 3], order='F')   # order를 주의하세요!!
image = Image.fromarray(image_data)  

# 이미지의 축 바꾸기
image_data = image_data.swapaxes(0, 1)
image = Image.fromarray(image_data)

# 전체 데이터의 적용
import os
import pickle
from PIL import Image
import numpy
from tqdm import tqdm

dir_path = os.getenv('HOME')+'/aiffel/python_image_proc/data/cifar-100-python'
train_file_path = os.path.join(dir_path, 'train')

# image를 저장할 cifar-100-python의 하위 디렉토리(images)를 생성합니다. 
images_dir_path = os.getenv('HOME')+'/aiffel/python_image_proc/cifar-images'
if not os.path.exists(images_dir_path):
    os.mkdir(images_dir_path)  # images 디렉토리 생성

# 32X32의 이미지 파일 50000개를 생성합니다. 
with open(train_file_path, 'rb') as f:
    train = pickle.load(f, encoding='bytes')
    for i in tqdm(range(len(train[b'filenames']))):
        filename = train[b'filenames'][i].decode()
        data = train[b'data'][i].reshape([32, 32, 3], order='F')
        image = Image.fromarray(data.swapaxes(0, 1))
        image.save(os.path.join(images_dir_path, filename))

4. OpenCV

💡 OpenCV는 오픈소스로 제공되는 컴퓨터 비전용 라이브러리이다. C++, Python, Java, MATLAB 등 다양한 언어에서 호출하여 사용할 수 있으며, 영상 처리에 대한 다양한 고급 기능들이 사용하기 쉽도록 구현되어 있다.
import os
import cv2 as cv
import numpy as np
from  matplotlib import pyplot as plt
%matplotlib inline

img_path = os.getenv('HOME')+'/aiffel/python_image_proc/data/cv_practice.png'
img = cv.imread(img_path)

# Convert BGR to HSV
hsv = cv.cvtColor(img, cv.COLOR_BGR2HSV)

# define range of blue color in HSV
lower_blue = np.array([100,100,100])
upper_blue = np.array([130,255,255])

# Threshold the HSV image to get only blue colors
mask = cv.inRange(hsv, lower_blue, upper_blue)

# Bitwise-AND mask and original image
res = cv.bitwise_and(img, img, mask=mask)

plt.imshow(cv.cvtColor(img, cv.COLOR_BGR2RGB))
plt.show()
plt.imshow(cv.cvtColor(mask, cv.COLOR_BGR2RGB))
plt.show()
plt.imshow(cv.cvtColor(res, cv.COLOR_BGR2RGB))
plt.show()

5. 실습

파일 load

import os
import pickle
import cv2
import numpy as np
from matplotlib import pyplot as plt
from tqdm import tqdm
from PIL import Image

# 전처리 시 생성했던 디렉토리 구조
dir_path = os.getenv('HOME')+'/aiffel/python_image_proc/'
train_file_path = os.path.join(dir_path, 'train')
images_dir_path = os.path.join(dir_path, 'cifar-images')

histogram

# 파일명을 인자로 받아 해당 이미지 파일과 히스토그램을 출력해 주는 함수
def draw_color_histogram_from_image(file_name):
    image_path = os.path.join(images_dir_path, file_name)
    # 이미지 열기
    img = Image.open(image_path)
    cv_image = cv2.imread(image_path)

    # Image와 Histogram 그려보기
    f=plt.figure(figsize=(10,3))
    im1 = f.add_subplot(1,2,1)
    im1.imshow(img)
    im1.set_title("Image")

    im2 = f.add_subplot(1,2,2)
    color = ('b','g','r')
    for i,col in enumerate(color):
        # image에서 i번째 채널의 히스토그램을 뽑아서(0:blue, 1:green, 2:red)
        histr = cv2.calcHist([cv_image],[i],None,[256],[0,256])   
        im2.plot(histr,color = col)   # 그래프를 그릴 때 채널 색상과 맞춰서 그립니다.
    im2.set_title("Histogram")

draw_color_histogram_from_image('adriatic_s_001807.png')

Idea step

  • 프로그램이 실행된다.build_histogram_db()
    ◦ CIFAR-100 이미지들을 불러온다.
    ◦ CIFAR-100 이미지들을 하나하나 히스토그램으로 만든다.
    ◦ 이미지 이름을 키로 하고, 히스토그램을 값으로 하는 딕셔너리 histogram_db를 반환한다.
  • CIFAR-100 히스토그램 중 입력된 이미지 이름에 해당하는 히스토그램을 입력 이미지로 선택하여 target_histogram이라는 변수명으로 지정한다.
  • search()
    ◦ 입력 이미지 히스토그램 target_histogram와 전체 검색 대상 이미지들의 히스토그램을 가진 딕셔너리 histogram_db를 입력으로 받는다.
    ◦ OpenCV의 compareHist() 함수를 사용하여 입력 이미지와 검색 대상 이미지 하나하나의 히스토그램 간 유사도를 계산한다. 결과는 result라는 이름의 딕셔너리로, 키는 이미지 이름, 값은 유사도로 한다.
    ◦ 계산된 유사도를 기준으로 정렬하여 순서를 매긴다.
    ◦ 유사도 순서상으로 상위 5개 이미지만 골라서 result에 남긴다.
  • 고른 이미지들을 표시한다.
  • 프로그램이 종료된다.

code

def get_histogram(image):
    histogram = []

    # Create histograms per channels, in 4 bins each.
    for i in range(3):
        # [[YOUR CODE]]


    histogram = np.concatenate(histogram)
    histogram = cv2.normalize(histogram, histogram)

    return histogram
import os
import pickle
import cv2
import numpy as np
from matplotlib import pyplot as plt
from tqdm import tqdm

def build_histogram_db():
    histogram_db = {}

    #디렉토리에 모아 둔 이미지 파일들을 전부 리스트업합니다. 
    path = images_dir_path
    file_list = os.listdir(images_dir_path)

    # [[YOUR CODE]]
        for file_name in tqdm(file_list):
            file_path = os.path.join(images_dir_path, file_name)
            image = cv2.imread(file_path)

            histogram = get_histogram(image)

            histogram_db[file_name] = histogram
    return histogram_db

histogram_db = build_histogram_db()
def get_target_histogram():
    filename = input("이미지 파일명을 입력하세요: ")
    if filename not in histogram_db:
        print('유효하지 않은 이미지 파일명입니다.')
        return None
    return histogram_db[filename]

target_histogram = get_target_histogram()
def search(histogram_db, target_histogram, top_k=5):
    results = {}

    # Calculate similarity distance by comparing histograms.
    # [[YOUR CODE]
    for file_name, histogram in tqdm(histogram_db.items()):
        distance = cv2.compareHist(H1=target_histogram,
                                       H2=histogram,
                                       method=cv2.HISTCMP_CHISQR)
        results[file_name] = distance

    results = dict(sorted(results.items(), key=lambda item: item[1])[:top_k])
    return results
def show_result(result):
    f=plt.figure(figsize=(10,3))
    for idx, filename in enumerate(result.keys()):    
        # [[YOUR CODE]]
        img_path = os.path.join(images_dir_path, filename)
        im = f.add_subplot(1,len(result),idx+1)
        img = Image.open(img_path)
        im.imshow(img)
target_histogram = get_target_histogram()
result = search(histogram_db, target_histogram)
show_result(result)

'AIFFEL > fundametal' 카테고리의 다른 글

Fundamental 18  (0) 2022.01.21
Fundamental 17  (0) 2022.01.19
Fundamental 15  (0) 2022.01.14
Fundamental 14  (0) 2022.01.12
Fundamental 05  (0) 2022.01.10

BELATED ARTICLES

more