히비스서커스의 블로그

[Numpy] numpy로 이미지 처리하기 2 본문

Programming/Python

[Numpy] numpy로 이미지 처리하기 2

HibisCircus 2021. 9. 9. 11:41
728x90

binary semantic segmentation을 keras framework로 이미지 처리를 numpy로 진행할 시 마주친 여러 문제들을 정리해보았다. 개인적인 견해와 상황이므로 정확하지 않을 수 있으니 참고만 해주길 바란다. 

 

https://numpy.org/

 

keras semantic segmentation 시 generator에서 주의해야 할 사항

 

먼저 이미지 데이터 타입으로 unit8, uint16, uint32, float32 등 여러 가지 타입이 존재하지만 가장 많이 쓰는 것은 unit8 (min=0, max=255)과 float32(8bit의 지수, 23bit의 소수)인 듯 하다. 주어진 이미지와 마스크는 데이터 타입이 uint8이었다. 또한, binary segmentation을 시도하는 중이었기에 mask의 최대값은 1이었다.

 

1. numpy 데이터 타입을 float 32로 해줄 것

2. 이미지 데이터는 255로 나누어줄 것

 

class generator(keras.utils.Sequence):
    
    def __init__(self, batch_size, img_size, zip_path_list, is_train):
        self.batch_size = batch_size
        self.img_size = img_size
        self.zip_path_list = zip_path_list
        self.augment = train_aug() if is_train == True else test_aug()

    def __len__(self):
        return len(self.zip_path_list) // self.batch_size

    def __getitem__(self, idx):
        i = idx * self.batch_size
        batch_patch_pairs = self.zip_path_list[i : i + self.batch_size]

        batch_x = np.zeros((self.batch_size,) + self.img_size + (3,), dtype='float32')
        batch_y = np.zeros((self.batch_size,) + self.img_size + (1,), dtype='float32')
        for j, path in enumerate(batch_patch_pairs):
            img_path = path[0]
            mask_path = path[1]
            batch_x[j] = cv2.imread(img_path)
            batch_y[j] = np.expand_dims(cv2.imread(mask_path, 0),2)
            batch_aug = self.augment(image=batch_x[j], mask=batch_y[j])
            batch_x[j] = batch_aug['image']/255
            batch_y[j] = batch_aug['mask']

        return batch_x, batch_y

 

먼저 dtype=float32로 해주어야 하는 이유는 keras의 segmentation model을 쓰기 위해서는 데이터 타입이 float32이어야 한다. uint8로 그대로 해주었을 경우 에러가 발생하였다. 

 

이미지 데이터를 255로 나누어주는 이유는 데이터 자체를 matplotlib.pyplot에서 불러올 때 imshow에서 바로 볼 수 있게 하기 위함이다. 

 

 

 

loss function이 negative로 나오는 경우

 

loss function을 분명 양의 값만을 가질 수 밖에 없는 diceloss와 binarycrossentropy를 사용하였음에도 불구하고 에러가 음의 값을 가진 경우가 나오게 되었다. 이유를 찾아보니 mask의 값이 0에서 1의 값을 가지는지 다시 확인을 해보라고 stackoverflow에 나와있었다.

 

https://stackoverflow.com/questions/49785133/keras-dice-coefficient-loss-function-is-negative-and-increasing-with-epochs 

 

Keras: Dice coefficient loss function is negative and increasing with epochs

According to this Keras implementation of Dice Co-eff loss function, the loss is minus of calculated value of dice coefficient. Loss should decrease with epochs but with this implementation I am ,

stackoverflow.com

 

따라서, 이때 다시 점검해봐야할 사항은 데이터와 이미지 generator를 거치고 나온 데이터 두 가지이다.

 

데이터 점검

 

데이터에서 이미지와 마스크의 값을 다시 한 번 점검해보아야 한다.

이때 주로 점검할 때 유용한 방법은 먼저 shape과 dtype 한번 더 살펴보고 numpy의 값들을 딕셔너리로 변환하여 값을 카운트 해보는 것이다.

 

import numpy as np
import glob
import cv2

mask_path_list = sorted(glob.glob('mask_path/*.png')) # mask path를 변경
for mask_path in mask_path_list:
    mask = cv2.imread(mask_path)
    print(f'mask_path : {mask_path}')
    print(f'mask shape : {mask.shape}, mask dtype : {mask.dtype}')
    unique, counts = np.unique(cv2.imread(mask_path, 0), return_counts = True)
    uniq_cnt_dict = dict(zip(unique, counts))
    print(f'mask element count : {uniq_cnt_dict}', '\n')

 

generator 나온 데이터 점검

 

generator를 거치며 __getitem__에서 새로운 numpy 배열을 만들고 기존의 값을 삽입하는데 이 과정에서 데이터의 값이 달라질 수 있다. 이를 유념하여 generator를 거친 후 만들어진 데이터를 __getitem__으로 몇 개만 가져와서 위와 동일한 작업을 해주면 이상이 있는지 확인하면 용이하다.

 

# BATCH_SIZE, INPUT_SHAPE, TRAIN_ZIP은 미리 설정해줄 것
train_gen = generator(BATCH_SIZE, INPUT_SHAPE, TRAIN_ZIP, is_train=True)

batch_x, batch_y = train_gen.__getitem__(0)
for idx in range(0, 4):
    print('mask shape : ', batch_y[idx].shape, 'mask dtype : ', batch_y[idx].dtype)
    unique, counts = np.unique(batch_y[idx], return_counts = True)
    uniq_cnt_dict = dict(zip(unique, counts))   
    print(f'mask element count : {uniq_cnt_dict}', '\n')

 

이와 같이 점검을 통해 이미지, 마스크 numpy의 shape과 dtype, 그리고 내부 원소들의 값들을 확인하여 에러가 발생할 만한 요소가 있는지 확인하고 수정해주면 해결이 될 것이다.

 

 

 

 

-히비스서커스-

 

728x90