본문 바로가기
실전 예제, 프로젝트

[실전 예제/변화 탐지/PyTorch] 변화 탐지 튜토리얼: LEVIR 데이터셋으로 PyTorch 데이터셋 만들기

by First Adventure 2025. 4. 19.
반응형

변화 탐지(Change Detection)란?

  변화 탐지(Change Detection)는 두 시점의 이미지를 비교하여 변화가 발생한 영역을 픽셀 단위로 분류하는 컴퓨터 비전 태스크입니다. 이번 시간에는 PyTorch를 이용하여 변화 탐지 데이터셋을 만드는 방법에 대해 알아보도록 하겠습니다.

 

변화 탐지 vs 의미론적 분할

  • 변화 탐지
    • 입력: 시점이 다른 두 이미지
    • 출력: 픽셀별 변화 여부(0: 동일, 1: 변화)
    • 활용 분야: 리모트 센싱, 감시, 재난 대응
  • 의미론적 분할 (Semantic)
    • 입력: 단일 이미지
    • 출력: 픽셀별 클래스
    • 활용 분야: 의료, 자율주행

 

대표 변화 탐지 데이터셋

LEVIR-CD

  • 고해상도 항공 이미지 (1024×1024)
  • 클래스: 변화 / 무변화 (2-class binary segmentation)
  • 건물의 증/개축 변화에 특화
  • 총 637 쌍 이미지 + 라벨

WHU-CD

  • 도시 지역 항공사진 기반, 약 3,000 쌍
  • 건물 변화 탐지에 활용
  • 배경이 복잡하고 다양한 변화 패턴 포함

 

PyTorch용 LEVIR-CD Dataset 만들기

디렉토리 구조 예시

LEVIR-CD/
  train/
    A/      # T1 시점 이미지
    B/      # T2 시점 이미지
    label/  # 정답 마스크 (0/1)

 

PyTorch 코드 예제

from PIL import Image
import os
import torch
from torch.utils.data import Dataset
import torchvision.transforms as T

class LEVIRDataset(Dataset):
    """
    Return: imgA (3,H,W), imgB (3,H,W), mask (1,H,W) float(0/1)
    """
    def __init__(self, root_dir, split="train", transforms=None):
        self.root_dir = root_dir
        self.split = split
        self.transforms = transforms

        self.dirA = os.path.join(root_dir, split, "A")
        self.dirB = os.path.join(root_dir, split, "B")
        self.dirL = os.path.join(root_dir, split, "label")

        if not (os.path.isdir(self.dirA) and os.path.isdir(self.dirB) and os.path.isdir(self.dirL)):
            raise FileNotFoundError(
                f"LEVIR 폴더 구조를 확인하세요.\n"
                f"- {self.dirA}\n- {self.dirB}\n- {self.dirL}"
            )

        # A 기준 파일명 목록
        self.names = sorted([os.path.basename(p) for p in glob(os.path.join(self.dirA, "*"))])
        if len(self.names) == 0:
            raise FileNotFoundError(f"'{self.dirA}' 안에 이미지가 없습니다.")

        # B/label 존재 체크(빠진 파일 방어)
        filtered = []
        for n in self.names:
            if os.path.exists(os.path.join(self.dirB, n)) and os.path.exists(os.path.join(self.dirL, n)):
                filtered.append(n)
        self.names = filtered

        if len(self.names) == 0:
            raise FileNotFoundError("A/B/label의 파일명이 일치하는 샘플이 없습니다. 파일명을 확인하세요.")

    def __len__(self):
        return len(self.names)

    def __getitem__(self, idx):
        name = self.names[idx]
        pA = os.path.join(self.dirA, name)
        pB = os.path.join(self.dirB, name)
        pL = os.path.join(self.dirL, name)

        imgA = Image.open(pA).convert("RGB")
        imgB = Image.open(pB).convert("RGB")
        mask = Image.open(pL).convert("L")  # 0/255

        if self.transforms:
            imgA, imgB, mask = self.transforms(imgA, imgB, mask)
        else:
            # 최소 기본값
            imgA, imgB, mask = CDToTensor()(imgA, imgB, mask)

        return imgA, imgB, mask

  변화 탐지는 A/B/Mask가 픽셀 단위로 정렬돼야 하므로, Resize/Flip/Crop 같은 변환은 반드시 동일하게 적용하고, 색감 변화(ColorJitter)처럼 라벨에 적용할 수 없는 변환은 이미지에만 적용합니다.

 

변화 탐지 모델 구조

변화 탐지 모델은 대부분 다음과 같은 구조를 따릅니다.

  1. 두 이미지를 각각 backbone (e.g., ResNet)으로 인코딩
  2. Feature 차이 계산 또는 융합
  3. 디코더로 변화 마스크 출력

대표 모델

  • Siamese U-Net
  • STANet
  • BIT (Bi-Temporal Transformer)
  • ChangeFormer (SOTA)

 

마무리

  PyTorch를 이용하여 변화 탐지 데이터셋을 어떻게 만드는지 살펴보았습니다. 다음 시간에는 모델 구성 및 학습 방법을 PyTorch로 작성하는 방법을 알아보도록 하겠습니다.

 

관련 내용

반응형