RLE(Ren-Length Encoding)
# RLE 디코딩 함수
def rle_decode(mask_rle, 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
img = np.zeros(shape[0]*shape[1], dtype=np.uint8)
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)
➡ RLE 디코딩과 인코딩을 통해 이미지 데이터를 효율적으로 표현하고 저장할 수 있음. 이미지 마스크의 압축된 표현이나 객체 검출과 같은 작업에서 활용
Dataset Info.
train_img
TRAIN_0000.png ~ TRAIN_7139.png
1024 x 1024
test_img
TEST_00000.png ~ TEST_60639.png
224 x 224
train.csv
img_id : 학습 위성 이미지 샘플 ID
img_path : 학습 위성 이미지 경로 (상대 경로)
mask_rle : RLE 인코딩된 이진마스크(0 : 배경, 1 : 건물) 정보
학습 위성 이미지에는 반드시 건물이 포함되어 있습니다.
그러나 추론 위성 이미지에는 건물이 포함되어 있지 않을 수 있습니다.
학습 위성 이미지의 해상도는 0.5m/픽셀이며, 추론 위성 이미지의 해상도는 공개하지 않습니다.
test.csv
img_id : 추론 위성 이미지 샘플 ID
img_path : 추론 위성 이미지 경로 (상대 경로)
sample_submission.csv - 제출 양식
img_id : 추론 위성 이미지 샘플 ID
mask_rle : RLE 인코딩된 예측 이진마스크(0: 배경, 1 : 건물) 정보
단, 예측 결과에 건물이 없는 경우 반드시 -1 처리
Dice coefficient
Ground Truth (정답)에 건물이 없고, Prediction (예측) 또한 건물이 없다고 맞춘 경우에는 샘플들의 Dice Coefficient 평균 계산에서 '제외' 됩니다.
다른 케이스로, Ground Truth (정답)에 건물이 없으나, Prediction (예측)에는 건물이 있다고 하는 경우에는 해당 샘플의 Dice Coefficient는 0점이 됩니다.
import numpy as np
import pandas as pd
from typing import List, Union
from joblib import Parallel, delayed
def rle_decode(mask_rle: Union[str, int], shape=(224, 224)) -> np.array:
'''
mask_rle: run-length as string formatted (start length)
shape: (height,width) of array to return
Returns numpy array, 1 - mask, 0 - background
'''
if mask_rle == -1:
return np.zeros(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
img = np.zeros(shape[0]*shape[1], dtype=np.uint8)
for lo, hi in zip(starts, ends):
img[lo:hi] = 1
return img.reshape(shape)
def dice_score(prediction: np.array, ground_truth: np.array, smooth=1e-7) -> float:
'''
Calculate Dice Score between two binary masks.
'''
intersection = np.sum(prediction * ground_truth)
return (2.0 * intersection + smooth) / (np.sum(prediction) + np.sum(ground_truth) + smooth)
def calculate_dice_scores(ground_truth_df, prediction_df, img_shape=(224, 224)) -> List[float]:
'''
Calculate Dice scores for a dataset.
'''
# Keep only the rows in the prediction dataframe that have matching img_ids in the ground truth dataframe
prediction_df = prediction_df[prediction_df.iloc[:, 0].isin(ground_truth_df.iloc[:, 0])]
prediction_df.index = range(prediction_df.shape[0])
# Extract the mask_rle columns
pred_mask_rle = prediction_df.iloc[:, 1]
gt_mask_rle = ground_truth_df.iloc[:, 1]
def calculate_dice(pred_rle, gt_rle):
pred_mask = rle_decode(pred_rle, img_shape)
gt_mask = rle_decode(gt_rle, img_shape)
if np.sum(gt_mask) > 0 or np.sum(pred_mask) > 0:
return dice_score(pred_mask, gt_mask)
else:
return None # No valid masks found, return None
dice_scores = Parallel(n_jobs=-1)(
delayed(calculate_dice)(pred_rle, gt_rle) for pred_rle, gt_rle in zip(pred_mask_rle, gt_mask_rle)
)
dice_scores = [score for score in dice_scores if score is not None] # Exclude None values
return np.mean(dice_scores)
참고자료