[메디컬] 의료 인공지능을 위한 신호 처리 개념 정리 및 전처리

2026. 4. 23. 12:33·개념 정리 step2/메디컬 이미지

오늘 공부할 내용은 의료 인공지능 모델을 개발할 때 가장 중요한 의료 데이터 전처리(Signal Processing) 개념과 파이토치(PyTorch) 기반 프레임워크인 MONAI의 활용법입니다. MRI, CT, X-ray 등 다양한 영상 데이터의 특징을 짚어보고, 코드를 통해 직접 증강(Augmentation) 기법들을 적용해 보겠습니다.


1. Signal Processing (신호 처리)

의료 영상 데이터(MRI, CT, 초음파, ECG 등)를 AI가 분석하기 좋은 형태로 정제하고 변환하는 핵심 전처리 과정입니다.
노이즈 제거, 해상도 보정, 정규화, 주파수 변환, 아티팩트 제거 등을 통해 불필요한 정보는 줄이고 질병과 관련된 중요한 특징(Signal)을 명확하게 강조하여 모델의 진단 및 예측 정확도를 높입니다.

1) Spatial Domain (공간 영역)

이미지나 신호를 각 위치(픽셀 또는 시간 지점)의 값 그대로 표현하고 처리하는 방식입니다.

  • 특징: 데이터의 실제 형태를 직관적으로 다룰 수 있습니다.
  • 활용: 밝기 조절, 필터링, 블러, 샤프닝, 엣지 검출 등 위치 값에 직접 연산을 적용합니다. 의료 영상에서는 원본 구조를 유지한 채 노이즈를 제거하거나 품질을 보정할 때 주로 쓰입니다.

2) Frequency Domain (주파수 영역)

신호나 이미지를 구성하는 주파수 성분(저주파/고주파)으로 분해하여 분석하는 방식입니다.

  • 저주파 (Low Frequency): 이미지의 "큰 구조", 전체 밝기 (예: 장기의 전체적인 형태)
  • 고주파 (High Frequency): 이미지의 "디테일", 조직 차이, 텍스처, 세밀한 경계선
  • 활용: 푸리에 변환을 통해 특정 주파수를 강조하거나 필터링합니다. MRI의 k-space처럼 애초에 데이터가 주파수 영역에서 수집되는 경우도 많아 영상 재구성과 압축, 노이즈 제거에 핵심적인 역할을 합니다.

 

3) Quantization (양자화)

Sampling을 통해 연속적인 영상을 일정 간격으로 나눈 뒤, 각 위치의 값을 이산적인 수치로 변환하는 과정입니다.

  • 연속적인 밝기 값을 0~255 (8bit) 또는 0~65535 (16bit)와 같은 정해진 단계의 디지털 값으로 변환하여 컴퓨터가 처리할 수 있도록 만듭니다.

2. 모달리티별 특징 (X-ray, CT, MRI)

X-ray

방사선을 이용해 인체 내부 구조를 2D 영상으로 표현합니다. 조직의 밀도 차이에 따라 빛 투과 정도가 달라집니다.

  • 특징: 밀도가 높을수록 밝게 보임 (뼈=밝음, 공기=어두움).
  • 데이터 형태: 픽셀 기반 그레이스케일 데이터.
  • 장점: 빠르고 비용이 저렴해 폐 질환 진단, 골절 탐지 등 AI 분야에서 가장 널리 쓰입니다.

CT (Computed Tomography)

X-ray를 여러 각도에서 촬영한 뒤 컴퓨터로 재구성하여 3차원 단면(슬라이스) 영상으로 표현합니다.

  • 특징: Hounsfield Unit(HU) 값을 기반으로 뼈, 장기, 혈관 등을 정량적으로 구분합니다.
  • 데이터 형태: 다수의 2D 슬라이스가 쌓인 3D 데이터.
  • 주의점: 장비 제조사나 촬영 프로토콜에 따라 방사선 Dose(흡수량)가 달라져 영상 품질(노이즈, 해상도 등)에 편차가 생길 수 있습니다. 따라서 AI 적용 시 정규화, 윈도잉, 데이터 증강을 통한 표준화 과정이 필수입니다.

MRI (Magnetic Resonance Imaging)

강한 자기장과 라디오파를 이용해 인체 내 수소(H) 원자 신호를 측정하여 영상으로 재구성합니다.

  • 특징: 방사선 노출이 없으며 연부조직(뇌, 근육, 장기 등)을 매우 선명하게 보여줍니다.
  • 원리: 우리 몸의 대부분을 차지하는 물(수소)을 흔들어서 이미지를 만듭니다. T1, T2, FLAIR 등 촬영 방식에 따라 조직 특성이 완전히 다르게 보입니다.
  • 데이터 수집: 원본 데이터는 주파수 영역(k-space)에서 수집된 후 푸리에 변환을 거쳐 3D 데이터(여러 장의 2D 슬라이스)로 변환됩니다.

3. MONAI 프레임워크 실습

MONAI는 의료 영상 분석을 위해 개발된 PyTorch 기반의 오픈소스 딥러닝 프레임워크입니다.
의료 데이터의 특성(DICOM/NIfTI 포맷 지원, 3D 텐서 처리, Spacing 등)을 쉽게 다룰 수 있는 특화된 전처리 및 증강 파이프라인을 제공합니다.

데이터 준비 및 환경 세팅

!pip install monai
import numpy as np
import matplotlib.pyplot as plt
from glob import glob
from pathlib import Path
import os
import monai
from monai.apps import DecathlonDataset, download_and_extract
from monai.data import DataLoader, Dataset
from monai.transforms import (
    EnsureChannelFirstd,    # tesor로 변경하기 위해 channel로 옮김
    LoadImaged,             # nifiti, dicom image 읽어오기
    Spacingd,               # 이미지의 pixel간의 spacing을 interpolationd을 쉽게 해줌
    Orientationd,           # 이미지의 위치를 다시 정렬  Left (L), Right (R), Posterior (P), Anterior (A), Inferior (I), Superior (S).
    ScaleIntensityRanged,   # min, max범위를 and scales to (0, 1) 하는 방법
    Compose,                # 해당 augmentationd을 list로 묶어서 사용
    OneOf,                  # 여러 Augmenation중에 하나를 선택하기
    CropForegroundd,        # Foreground부분만 남기는 방법
    Rand3DElasticd,         # 3D 모양으로 Elastic Transform
    RandAffined,            # 이미지의 크기는고정인 상태로 augmenation 함수
    RandRotated,            # random roatate
    RandFlipd,

)
import tempfile
from monai.visualize.utils import (
    blend_images,           # label과 Image를 합친 영상
    matshow3d               # 3d image의 visulization
)
import matplotlib.pyplot as plt

1) 기본 데이터 로드 (LoadImaged & EnsureChannelFirstd)

의료 영상은 보통 (H, W, D) 형태를 가지므로 파이토치 모델에 넣기 위해 채널 축을 앞으로 보내 (C, H, W, D) 형태로 만들어 줍니다.

transform = Compose([
    LoadImaged(keys=["image", "label"]),
    EnsureChannelFirstd(keys=['image', 'label'])
])

dataset = Dataset(data=val_data_dicts, transform=transform)
print(f"image shape: {dataset[0]['image'].shape}")
print(f"label shape: {dataset[0]['label'].shape}")
print(f"pixel spacing: {dataset[0]['image'].pixdim}")

# 시각화 출력부
plt = matshow3d(volume=dataset[0]['image'][..., 1::20], fig=None, title='input image', frame_dim=-1, show=True, cmap='gray')

2) 재샘플링 (Spacingd)

각기 다른 해상도로 촬영된 영상들의 픽셀 간격(spacing)을 통일시켜 줍니다.

transform = Compose([
    LoadImaged(keys=["image", "label"]),
    EnsureChannelFirstd(keys=['image', 'label']),
    # pixdim 값으로 재샘플링. image는 bilinear로 부드럽게, label은 nearest로 값 보존
    Spacingd(keys=['image', 'label'], pixdim=(2, 2, 3), mode=("bilinear", "nearest"))
])

dataset = Dataset(data=val_data_dicts, transform=transform)
plt = matshow3d(volume=dataset[0]['image'][..., 1::20], fig=None, title='spacing image', frame_dim=-1, show=True, cmap='gray')

3) 배경 자르기 (CropForegroundd)

분석에 불필요한 배경(검은 여백)을 잘라내어 연산 효율을 높입니다.

transform = Compose([
    LoadImaged(keys=["image", "label"]),
    EnsureChannelFirstd(keys=['image', 'label']),
    CropForegroundd(keys=['image', 'label'], source_key='image')
])

dataset = Dataset(data=val_data_dicts, transform=transform)
plt = matshow3d(volume=dataset[0]['image'][..., 1::20], fig=None, title='cropped image', frame_dim=-1, show=True, cmap='gray')

4) 정규화 (NormalizeIntensityd)

데이터의 밝기 분포를 평균 0, 표준편차 1로 맞춰줍니다.

transform = Compose([
    LoadImaged(keys=["image", "label"]),
    EnsureChannelFirstd(keys=['image', 'label']),
    # channel_wise: 각 채널별로 따로 정규화
    NormalizeIntensityd(keys='image', channel_wise=True)
])

dataset = Dataset(data=val_data_dicts, transform=transform)
plt = matshow3d(volume=dataset[0]['image'][..., 1::20], fig=None, title='normalized image', frame_dim=-1, show=True, cmap='gray')

5) 데이터 증강: Flip & Rotate

from monai.transforms import (
    EnsureChannelFirstd,    # tesor로 변경하기 위해 channel로 옮김
    LoadImaged,             # nifiti, dicom image 읽어오기
    Spacingd,               # 이미지의 pixel간의 spacing을 interpolationd을 쉽게 해줌
    Orientationd,           # 이미지의 위치를 다시 정렬  Left (L), Right (R), Posterior (P), Anterior (A), Inferior (I), Superior (S).
    ScaleIntensityRanged,   # min, max범위를 and scales to (0, 1) 하는 방법
    Compose,                # 해당 augmentationd을 list로 묶어서 사용
    OneOf,                  # 여러 Augmenation중에 하나를 선택하기
    CropForegroundd,        # Foreground부분만 남기는 방법
    Rand3DElasticd,         # 3D 모양으로 Elastic Transform
    RandAffined,            # 이미지의 크기는고정인 상태로 augmenation 함수
    RandRotated,            # random roatate
    RandFlipd,
    RandRotate90d,
)
transform = Compose([
    LoadImaged(keys=["image", "label"]),
    EnsureChannelFirstd(keys=['image', 'label']),
    CropForegroundd(keys=['image', 'label'], source_key='image'),
    # 폭(width) 방향으로 뒤집기
    RandFlipd(keys=['image', 'label'], spatial_axis=[2], prob=1),
    # 최대 3번(90, 180, 270도) 랜덤 회전
    RandRotate90d(keys=['image', 'label'], prob=1, max_k=3)
])

dataset = Dataset(data=val_data_dicts, transform=transform)
plt = matshow3d(volume=dataset[0]['image'][..., 1::20], fig=None, title='flip & rotate image', frame_dim=-1, show=True, cmap='gray')

6) 데이터 증강: Affine Transform (RandAffined)

transform = Compose([
    LoadImaged(keys=["image", "label"]),
    EnsureChannelFirstd(keys=['image', 'label']),
    CropForegroundd(keys=['image', 'label'], source_key='image'),
    RandAffined(keys=['image', 'label'], shear_range=(0.5, 0.5), mode=('bilinear', 'nearest'), padding_mode='zeros', prob=1)
])

dataset = Dataset(data=val_data_dicts, transform=transform)
plt = matshow3d(volume=dataset[0]['image'][..., 1::20], fig=None, title='affine image', frame_dim=-1, show=True, cmap='gray')

7) 시각화 응용: Image와 Label 오버레이 (blend_images)

원본 영상 위에 분할(Segmentation)된 라벨을 겹쳐서 확인하는 방법입니다.

# image와 label을 겹쳐서 시각화 (alpha 조절로 투명도 설정)
ret = blend_images(image=dataset[0]["image"], label=dataset[0]["label"], alpha=0.5, cmap="hsv", rescale_arrays=True)
fig, axs = plt.subplots(1, 3)

slice_index = 50
axs[0].set_title(f"image slice {slice_index}")
axs[0].imshow(dataset[0]["image"][0, :, :, slice_index], cmap="gray")

axs[1].set_title(f"label slice {slice_index}")
axs[1].imshow(dataset[0]["label"][0, :, :, slice_index])

axs[2].set_title(f"blend slice {slice_index}")
# imshow는 (H,W,C) 포맷을 요구하므로 채널(C)을 맨 뒤로 이동
axs[2].imshow(torch.moveaxis(ret[:, :, :, slice_index], 0, -1))

8) 데이터 증강: K-Space Noise (RandKSpaceSpikeNoised)

from monai.transforms import (
    RandKSpaceSpikeNoised,  # 주파수 영역의 이미지에서 noise를 추가하여준다 .
    AdjustContrastd,        # 영상의 밝기 조절
    GaussianSmoothd,        # 이미지에 Gaussian을 적용하여 smooth하게 만듬
    RandCoarseDropoutd,     # Cutout처럼 이미지에 특정영역에 box를 적용
    HistogramNormalized,    # 이미지의 histogram을 normalize함으로써 영상의 밝기 향상
)

MRI처럼 주파수 영역(K-Space)에서 발생할 수 있는 노이즈를 인위적으로 추가하는 기법입니다.

transform = Compose([
    LoadImaged(keys=["image", "label"]),
    EnsureChannelFirstd(keys=["image", "label"]),
    ScaleIntensityRanged(keys=["image"], a_min=0, a_max=1800, b_min=0.0, b_max=1.0, clip=True),
    CropForegroundd(keys=["image", "label"], source_key="image"),
    RandKSpaceSpikeNoised(keys=["image"], prob=1, intensity_range=(13, 15), channel_wise=True),
])

dataset = Dataset(data=train_data_dicts, transform=transform)
plt = matshow3d(volume=dataset[0]["image"][...,1::20], fig=None, title="k-space noise image", frame_dim=-1, show=True, cmap="gray")

9) 히스토그램 평활화 (HistogramNormalized)

이미지의 픽셀 밝기 분포를 균등하게 펴주어 전체적인 명암비(Contrast)를 개선합니다.

transform = Compose([
    LoadImaged(keys=["image", "label"]),
    EnsureChannelFirstd(keys=["image", "label"]),
    ScaleIntensityRanged(keys=["image"], a_min=0, a_max=1800, b_min=0.0, b_max=1.0, clip=True),
    CropForegroundd(keys=["image", "label"], source_key="image"),
    HistogramNormalized(keys=["image"], num_bins=10),
])

dataset = Dataset(data=val_data_dicts, transform=transform)
plt = matshow3d(volume=dataset[0]["image"][...,1::20], fig=None, title="histogram normalized image", frame_dim=-1, show=True, cmap="gray")

'개념 정리 step2 > 메디컬 이미지' 카테고리의 다른 글

[딥러닝 전처리] DICOM MRI 데이터를 NIfTI로 변환하고 3D 볼륨을 GIF로 시각화하기  (0) 2026.04.06
[Technical Deep Dive] 메디컬 이미지 데이터의 이해와 Python DICOM 실습  (0) 2026.04.05
'개념 정리 step2/메디컬 이미지' 카테고리의 다른 글
  • [딥러닝 전처리] DICOM MRI 데이터를 NIfTI로 변환하고 3D 볼륨을 GIF로 시각화하기
  • [Technical Deep Dive] 메디컬 이미지 데이터의 이해와 Python DICOM 실습
고니3000원
고니3000원
프로젝트의 구현 과정과 기술적 노하우를 담았습니다. AI 모델 연구와 매일의 학습 기록을 차곡차곡 공유하고 있습니다.
  • 고니3000원
    곤이의 공부 블로그
    고니3000원
  • 전체
    오늘
    어제
    • 분류 전체보기 (218) N
      • 1. AI 논문 + 모델 분석 (21)
        • AI 논문 분석 (13)
        • AI 모델 분석 (8)
      • 2. 자료구조와 알고리즘 (16)
        • 2-1 자료구조와 알고리즘 (13)
        • 2-2 강화학습 알고리즘 (3)
      • 3. 자습 & 메모(실전, 실습, 프로젝트) (27)
        • 3-1 문제 해석 (4)
        • 3-2 메모(실전, 프로젝트) (14)
        • 3-3 배포 실전 공부 (7)
        • 3-4 최신 기술 분석 (2)
      • 4. [팀] 프로젝트 및 공모전 (31)
        • 4-1 팀 프로젝트(메모, 공부) (1)
        • 4-2 Meat-A-Eye (6)
        • 4-3 RL-Tycoon-Agent (3)
        • 4-4 구조물 안정성 물리 추론 AI 경진대회(D.. (4)
        • 4-5 AgentShield(보안 플랫폼) (17)
      • 5. [개인] 프로젝트 및 공모전 (25) N
        • 4-1 귀멸의칼날디펜스(자바스크립트 활용) (5)
        • 4-2 바탕화면 AI 펫 프로그램 (4)
        • 4-3 개인 프로젝트(기타) (3)
        • 4-4 공모전 (9) N
        • 4-5 나만의 로컬 LLM 멀티 에이전트 구축 (4)
      • 6. 경진 대회 (kaggle, dacon 등) (1) N
      • 개념 정리 step1 (32)
        • Python 기초 (7)
        • DBMS (1)
        • HTML | CSS (3)
        • Git | GitHub (1)
        • JavaScript (5)
        • Node.js (5)
        • React (1)
        • 데이터 분석 (6)
        • Python Engineering (3)
      • 개념 정리 step2 (60)
        • Machine | Deep Learning (15)
        • 멀티모달(Multi-modal) (23)
        • 강화 학습 (10)
        • AI Agent (9)
        • 메디컬 이미지 (3)
      • 개인 공부 - 내가 공부하고 싶은 모든 것 (3) N
        • 1. 인프라 (2)
        • 2. 자율주행 (0)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

    • Notion-포트폴리오
    • Github
  • 공지사항

    • ‘박영곤’ 나의 핵심 가치
  • 인기 글

  • 태그

    Ai
    인공지능
    데이터분석
    공모전
    RAG
    paddleocr
    파이썬
    논문 리뷰
    llm
    보안
    pandas
    강화학습
    OCR
    자바스크립트
    알고리즘
    Python
    ViT
    Ollama
    github
    프로젝트
    Vision
    파인튜닝
    학습
    전처리
    자료구조
    구현
    transformer
    API
    Lora
    Agent
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.5
고니3000원
[메디컬] 의료 인공지능을 위한 신호 처리 개념 정리 및 전처리
상단으로

티스토리툴바