[SW 중심대학 AI 경진대회 2023] 데이터 전처리 및 업샘플링

LONGNEW·2023년 7월 24일
0

연구

목록 보기
3/10

대회에서의 train, test set 데이터 크기가 달라서 train 데이터의 이미지 크기를 조정하였다.

RLE

이전에 영상처리 강의자료에서 보았듯이 해당 예제에서는 0, 1만을 가지고 인코딩을 수행했지만 이 범위는 더 넓을 수 있고 형태는 "위치, 횟수" 자료구조가 연속적으로 나오는 것이라 할 수 있다.

제작된 수도코드

1. 입력받은 이미지 크기 만큼의 행렬을 만들어 모두 0으로 초기화한다.
2. 현재 mask가 0으로만 이루어져 있다면 반환한다.
3. mask 값을 split() 연산하여 "위치, 횟수"의 구조로 인식할 수 있게 한다.
4. 0으로 초기화된 배열의 해당 위치에서 횟수만큼 값들을 1로 변경한다.

해당 수도코드는 0, 1에 대해서만 처리한 것이다.

IDEA

  • 주어진 것 : RLE 인코딩, 디코딩, 1024 * 1024 이미지, train.csv



  • 할 수 있는 것 : 224 * 224 이미지로 분할 하기
  1. 1024 * 1024 이미지에 대해서 RLE 디코드를 수행한다.
  2. image, mask 2개의 배열에 대해 224 * 224의 형태로 분할 한다.
  3. 분할된 image를 새로운 idx와 함께 저장한다.
  4. 저장된 idx에 맞게 mask를 인코딩 하여 새로운 train.csv를 제작한다.

주의

데이터의 수

  • 1024 * 1024 이미지를 224 * 224로 분할 한다면 대략 16개 정도가 된다.
  • 그러나 겹치는 부분을 추가하며 데이터를 생산하도록 하여 데이터를 약 9배 더 늘리게 하였다.

데이터의 통일성

  • 범위를 계산하기 때문에 오버플로가 날 수 있다.
  • 이를 제거하기 위한 범위 제한이 필요하다.

새로운 데이터 nan

  • 주어진 데이터는 모두 건물이 존재한다.
  • 분할을 하게 된다면 배경만 존재하는 경우도 있어 주어진 RLE 디코딩 함수의 변형이 필요하다.
import os
import cv2
import pandas as pd
import numpy as np
from PIL import Image

# RLE 디코딩 함수
def rle_decode(mask_rle, shape):
    # upsample된 경우는 사진에 건물이 없는 경우도 있어 nan을 판단하여 반환한다.
    img = np.zeros(shape[0] * shape[1], dtype=np.uint8)
    if pd.isnull(mask_rle):
        return img.reshape(shape)

    s = mask_rle.split()
    starts, lengths = [np.asarray(x, dtype=int) for x in (s[0:][::2], s[1:][::2])]
    starts -= 1
    ends = starts + lengths
    for lo, hi in zip(starts, ends):
        img[lo:hi] = 1
    return img.reshape(shape)

# RLE 인코딩 함수
def rle_encode(mask):
    pixels = mask.flatten()
    pixels = np.concatenate([[0], pixels, [0]])
    runs = np.where(pixels[1:] != pixels[:-1])[0] + 1
    runs[1::2] -= runs[::2]
    return ' '.join(str(x) for x in runs)

# 저장하기 위한 경로로 upsample이란 폴더가 존재해야함
relative_folder_path = 'upsample'
absolute_folder_path = os.path.join(os.getcwd(), relative_folder_path)
if not os.path.exists(absolute_folder_path):
    os.makedirs(absolute_folder_path)

relative_folder_path = 'upsample/train_img'
absolute_folder_path = os.path.join(os.getcwd(), relative_folder_path)
if not os.path.exists(absolute_folder_path):
    os.makedirs(absolute_folder_path)

upsample_csv = "./upsample/train.csv"

data = pd.read_csv("./train.csv")
cnt = 0

columns = ['img_id', 'img_path', 'mask_rle']
df_upsample_csv = pd.DataFrame(columns=columns)

for idx in range(len(data)):
    # image 개수를 제한해서 upsample함.
    if idx == 3:
        break

    img_id, img_path, mask_rle = data.iloc[idx]
    image = cv2.imread(img_path)
    image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
    mask = rle_decode(mask_rle, (image.shape[0], image.shape[1]))

    # 나누기의 값을 바꾸는 것으로 데이터의 개수를 더 늘릴 수 있다.
    for row in range(0, image.shape[0], (224 // 3)):
        for col in range(0, image.shape[1], (224 // 3)):
            # 크기를 제한하기 위한 조건이다.
            if row + (224 * 1) >= image.shape[0] or col + (224 * 1) >= image.shape[1]:
                continue

            temp_img = image[row:row + (224 * 1), col:col + (224 * 1)]
            temp_mask = mask[row:row + (224 * 1), col:col + (224 * 1)]

            # 제작된 img, mask를 다시 이미지 타입과 인코딩된 형태로 변경한다.
            temp_img = Image.fromarray(temp_img)
            temp_encode_mask = rle_encode(temp_mask)

            # 이미지를 저장하기 위해선 train_img라는 폴더가 upsample내부에 있어야 한다.
            temp_id = f"train{cnt}"
            image_path = f"./upsample/train_img/{temp_id}.png"
            temp_img.save(image_path, format="PNG")
            cnt += 1

            new_data = {
                columns[0] : temp_id,
                columns[1] : image_path,
                columns[2] : temp_encode_mask
            }
            df_upsample_csv = df_upsample_csv.append(new_data, ignore_index=True)

df_upsample_csv.to_csv(upsample_csv, index=False)

0개의 댓글