Skip to content

Latest commit

ย 

History

History
993 lines (737 loc) ยท 36.6 KB

tiatoolbox_tutorial.rst

File metadata and controls

993 lines (737 loc) ยท 36.6 KB

Pytorch์™€ TIAToolbox๋ฅผ ์‚ฌ์šฉํ•œ ์ „์ฒด ์Šฌ๋ผ์ด๋“œ ์ด๋ฏธ์ง€(Whole Slide Image) ๋ถ„๋ฅ˜

๋ฒˆ์—ญ: ๋ฐ•์ฃผํ™˜

Tip

์ด ํŠœํ† ๋ฆฌ์–ผ์„ ์ตœ๋Œ€ํ•œ ํ™œ์šฉํ•˜๋ ค๋ฉด, ์ด Colab ๋ฒ„์ „ ์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์„ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ์•„๋ž˜์˜ ์ž๋ฃŒ๋ฅผ ์‹คํ—˜ํ•ด ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ฐœ์š”

๋ณธ ํŠœํ† ๋ฆฌ์–ผ์—์„œ๋Š” TIAToolbox๋ฅผ ์‚ฌ์šฉํ•œ PyTorch ๋ชจ๋ธ์„ ํ†ตํ•ด ์ „์ฒด ์Šฌ๋ผ์ด๋“œ ์ด๋ฏธ์ง€๋“ค(Whole Slide Images, WSIs)์„ ๋ถ„๋ฅ˜ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ์•„๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. WSI๋ž€ ์ˆ˜์ˆ ์ด๋‚˜ ์ƒ๊ฒ€์„ ํ†ตํ•ด ์ฑ„์ทจ๋œ ์ธ๊ฐ„ ์กฐ์ง ์ƒ˜ํ”Œ์˜ ์ด๋ฏธ์ง€์ด๋ฉฐ, ์ด๋Ÿฌํ•œ ์ด๋ฏธ์ง€๋Š” ์ „๋ฌธ ์Šค์บ๋„ˆ๋ฅผ ์ด์šฉํ•ด ์Šค์บ” ๋ฉ๋‹ˆ๋‹ค. ์ด ๋ฐ์ดํ„ฐ๋Š” ๋ณ‘๋ฆฌํ•™์ž์™€ ์ „์‚ฐ ๋ณ‘๋ฆฌํ•™์ž๋“ค์ด ์ข…์–‘ ์„ฑ์žฅ์— ๋Œ€ํ•œ ์ดํ•ด๋ฅผ ๋†’์ด๊ณ  ํ™˜์ž ์น˜๋ฃŒ๋ฅผ ๊ฐœ์„ ํ•˜๊ธฐ ์œ„ํ•ด ์•”๊ณผ ๊ฐ™์€ ์งˆ๋ณ‘์„ ๋ฏธ์‹œ์  ์ˆ˜์ค€์—์„œ ์—ฐ๊ตฌ ํ•˜๋Š”๋ฐ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.

WSIs๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ฒƒ์ด ์–ด๋ ค์šด ์ด์œ ๋Š” ์—„์ฒญ๋‚œ ํฌ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ์˜ˆ์ปจ๋Œ€, ์ผ๋ฐ˜์ ์ธ ์Šฌ๋ผ์ด๋“œ ์ด๋ฏธ์ง€๊ฐ€ 100,000x100,000 ํ”ฝ์…€ ์ •๋„์˜ ํฌ๊ธฐ๋ฅผ ๊ฐ€์ง€๋ฉฐ, ๊ฐ ํ”ฝ์…€์€ ์Šฌ๋ผ์ด๋“œ ์ƒ์—์„œ ์•ฝ 0.25x0.25 ๋งˆ์ดํฌ๋กœ๋ฏธํ„ฐ์— ํ•ด๋‹นํ•ฉ๋‹ˆ๋‹ค. ์ด ๋•Œ๋ฌธ์— ์ด๋ฏธ์ง€๋ฅผ ๋กœ๋“œํ•˜๊ณ  ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐ ์–ด๋ ค์›€์„ ์•ผ๊ธฐํ•˜๋ฉฐ, ํ•œ ์—ฐ๊ตฌ์—์„œ ์ˆ˜๋ฐฑ ๊ฐœ๋‚˜ ์ˆ˜์ฒœ ๊ฐœ์˜ WSIs๊ฐ€ ํฌํ•จ๋˜๋Š” ๊ฒฝ์šฐ๋Š” ๋งํ•  ๊ฒƒ๋„ ์—†์Šต๋‹ˆ๋‹ค ๋” ํฐ ์—ฐ๊ตฌ๊ฐ€ ๋” ๋‚˜์€ ๊ฒฐ๊ณผ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค!

์ „ํ†ต์ ์ธ ์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ ํŒŒ์ดํ”„๋ผ์ธ์€ WSIs ์ฒ˜๋ฆฌ์— ์ ํ•ฉํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ ๋” ๋‚˜์€ ๋„๊ตฌ๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ์ด๋•Œ, TIAToolbox ๊ฐ€ ๋„์›€์ด ๋  ์ˆ˜ ์žˆ๋Š”๋ฐ, ์ด๋Š” ์กฐ์ง ์Šฌ๋ผ์ด๋“œ(tissue slides)๋ฅผ ๋น ๋ฅด๊ณ  ํšจ์œจ์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š” ์œ ์šฉํ•œ ๋„๊ตฌ๋“ค์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ์ผ๋ฐ˜์ ์œผ๋กœ WSIs๋Š” ์‹œ๊ฐํ™”์— ์ตœ์ ํ™”๋œ ๋‹ค์–‘ํ•œ ๋ฐฐ์œจ์—์„œ ๋™์ผํ•œ ์ด๋ฏธ์ง€์˜ ์—ฌ๋Ÿฌ ๋ณต์‚ฌ๋ณธ์ด ํ”ผ๋ผ๋ฏธ๋“œ ๊ตฌ์กฐ๋กœ ์ €์žฅ๋ฉ๋‹ˆ๋‹ค. ํ”ผ๋ผ๋ฏธ๋“œ์˜ ๋ ˆ๋ฒจ 0(๋˜๋Š” ๊ฐ€์žฅ ์•„๋ž˜ ๋‹จ๊ณ„)์—๋Š” ๊ฐ€์žฅ ๋†’์€ ๋ฐฐ์œจ ๋˜๋Š” ์คŒ ์ˆ˜์ค€์˜ ์ด๋ฏธ์ง€๋ฅผ ํฌํ•จํ•˜๋ฉฐ, ํ”ผ๋ผ๋ฏธ๋“œ์˜ ์ƒ์œ„ ๋‹จ๊ณ„๋กœ ๊ฐˆ์ˆ˜๋ก ๊ธฐ๋ณธ ์ด๋ฏธ์ง€์˜ ์ € ํ•ด์ƒ๋„ ์‚ฌ๋ณธ์ด ์žˆ์Šต๋‹ˆ๋‹ค. ํ•˜๋‹จ์— ํ”ผ๋ผ๋ฏธ๋“œ์˜ ๊ตฌ์กฐ๊ฐ€ ๊ทธ๋ ค์ ธ ์žˆ์Šต๋‹ˆ๋‹ค.

WSI pyramid stack WSI ํ”ผ๋ผ๋ฏธ๋“œ stack (source)

TIAToolbox๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์กฐ์ง ๋ถ„๋ฅ˜ ์™€ ๊ฐ™์€ ์ผ๋ฐ˜์ ์ธ ํ›„์† ๋ถ„์„ ์ž‘์—…์„ ์ž๋™ํ™”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ณธ ํŠœํ† ๋ฆฌ์–ผ์—์„œ๋Š” ๋‹ค์Œ์„ ์ˆ˜ํ–‰ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค: 1. TIAToolbox๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ WSI(Whole Slide Image)๋ฅผ ์ด๋ฏธ์ง€๋ฅผ ๋กœ๋“œํ•˜๋Š” ๋ฐฉ๋ฒ• 2. ์„œ๋กœ ๋‹ค๋ฅธ Pytorch ๋ชจ๋ธ์„ ์‚ฌ์šฉํ•˜์—ฌ ์Šฌ๋ผ์ด๋“œ๋ฅผ ํŒจ์น˜ ๋ ˆ๋ฒจ(patch-level)์—์„œ ๋ถ„๋ฅ˜ํ•˜๋Š” ๋ฐฉ๋ฒ•. ๋ณธ ํŠœํ† ๋ฆฌ์–ผ์—์„œ๋Š” TorchVision์˜ ResNet18 ๋ชจ๋ธ๊ณผ ์ปค์Šคํ…€(custom) HistoEncoder ๋ชจ๋ธ์„ ์‚ฌ์šฉํ•˜๋Š” ์˜ˆ์ œ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

์‹œ์ž‘ํ•ด๋ณด์ž!

ํ™˜๊ฒฝ์„ค์ •

๋ณธ ํŠœํ† ๋ฆฌ์–ผ์—์„œ ์ œ๊ณตํ•˜๋Š” ์˜ˆ์ œ๋ฅผ ์‹คํ–‰ํ•˜๋ ค๋ฉด, ๋‹ค์Œ๊ณผ ๊ฐ™์€ ํŒจํ‚ค์ง€(packages)๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

  1. OpenJpeg
  2. OpenSlide
  3. Pixman
  4. TIAToolbox
  5. HistoEncoder (for a custom model example)

์ด ํŒจํ‚ค์ง€๋“ค์„ ์„ค์น˜ํ•˜๊ธฐ ์œ„ํ•ด ํ„ฐ๋ฏธ๋„(terminal)์— ์•„๋ž˜์˜ ๋ช…๋ น์–ด๋ฅผ ์‹คํ–‰ํ•ด์ฃผ์„ธ์š”:

  • apt-get -y -qq install libopenjp2-7-dev libopenjp2-tools openslide-tools libpixman-1-dev
  • pip install -q 'tiatoolbox<1.5' histoencoder && echo "Installation is done."

๋˜ํ•œ, MacOS์—์„œ๋Š” apt-get ๋Œ€์‹ ์— brew install openjpeg openslide ๋ช…๋ น์–ด๋ฅผ ์‹คํ–‰ํ•˜์—ฌ ํ•„์ˆ˜ ํŒจํ‚ค์ง€๋“ค์„ ์„ค์น˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์„ค์น˜์— ๋Œ€ํ•œ ์ถ”๊ฐ€ ์ •๋ณด๋Š” ์—ฌ๊ธฐ ๋ฅผ ์ฐธ์กฐํ•˜์„ธ์š”.

๊ด€๋ จ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ

"""Jupyter ๋…ธํŠธ๋ถ์„ ์‹คํ–‰ํ•˜๋Š” ๋ฐ ํ•„์š”ํ•œ ๋ชจ๋“ˆ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ."""
from __future__ import annotations

# ๋กœ๊น…(logging) ์„ค์ •ํ•˜๊ธฐ
import logging
import warnings
if logging.getLogger().hasHandlers():
    logging.getLogger().handlers.clear()
warnings.filterwarnings("ignore", message=".*The 'nopython' keyword.*")

# ๋ฐ์ดํ„ฐ์™€ ํŒŒ์ผ ๋‹ค์šด๋กœ๋“œํ•˜๊ธฐ
import shutil
from pathlib import Path
from zipfile import ZipFile

# ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ ๋ฐ ์‹œ๊ฐํ™”
import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from matplotlib import cm
import PIL
import contextlib
import io
from sklearn.metrics import accuracy_score, confusion_matrix

# WSI ๋กœ๋”ฉ ๋ฐ ์ฒ˜๋ฆฌ๋ฅผ ์œ„ํ•œ TIAToolbox
from tiatoolbox import logger
from tiatoolbox.models.architecture import vanilla
from tiatoolbox.models.engine.patch_predictor import (
    IOPatchPredictorConfig,
    PatchPredictor,
)
from tiatoolbox.utils.misc import download_data, grab_files_from_dir
from tiatoolbox.utils.visualization import overlay_prediction_mask
from tiatoolbox.wsicore.wsireader import WSIReader

# Torch-๊ด€๋ จ
import torch
from torchvision import transforms

# ๊ทธ๋ž˜ํ”„ ์„ค์ •
mpl.rcParams["figure.dpi"] = 160  # for high resolution figure in notebook
mpl.rcParams["figure.facecolor"] = "white"  # To make sure text is visible in dark mode

# ๋งŒ์•ฝ GPU๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค๋ฉด, ON_GPU๋ฅผ False๋กœ ๋ณ€๊ฒฝํ•˜์„ธ์š”.
ON_GPU = True

# ์žฅํ™ฉํ•œ ์ฝ”๋“œ ๋ธ”๋ก์˜ ์ฝ˜์†” ์ถœ๋ ฅ์„ ์–ต์ œํ•˜๋Š” ํ•จ์ˆ˜
def suppress_console_output():
    return contextlib.redirect_stderr(io.StringIO())

์‹คํ–‰ ์ „ ์ •๋ฆฌ(clean-up)

์ ์ ˆํ•œ ์ •๋ฆฌ๋ฅผ ๋ณด์žฅํ•˜๊ธฐ ์œ„ํ•ด(์˜ˆ์ปจ๋Œ€, ๋น„์ •์ƒ ์ข…๋ฃŒ), ์ด๋ฒˆ ์‹คํ–‰์—์„œ ๋‹ค์šด๋กœ๋“œ๋˜๊ฑฐ๋‚˜ ์ƒ์„ฑ๋œ ๋ชจ๋“  ํŒŒ์ผ์€ global_save_dir ์ด๋ผ๋Š” ํ•˜๋‚˜์˜ ๋””๋ ‰ํ† ๋ฆฌ์— ์ €์žฅ๋˜๋ฉฐ, ์ด ๋””๋ ‰ํ† ๋ฆฌ๋Š” โ€œ./tmp/โ€๋กœ ์„ค์ •๋ฉ๋‹ˆ๋‹ค. ์œ ์ง€๋ณด์ˆ˜๋ฅผ ์‰ฝ๊ฒŒ ํ•˜๊ธฐ ์œ„ํ•ด ๋””๋ ‰ํ† ๋ฆฌ ์ด๋ฆ„์€ ์ด ํ•œ ๊ณณ์—์„œ๋งŒ ์„ค์ •๋˜๋ฏ€๋กœ, ํ•„์š”ํ•˜๋ฉด ๊ฐ„ํŽธํ•˜๊ฒŒ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

warnings.filterwarnings("ignore")
global_save_dir = Path("./tmp/")


def rmdir(dir_path: str | Path) -> None:
    """๋””๋ ‰ํ† ๋ฆฌ๋ฅผ ์ง€์šฐ๊ธฐ ์œ„ํ•œ ๋„์šฐ๋ฏธ ํ•จ์ˆ˜"""
    if Path(dir_path).is_dir():
        shutil.rmtree(dir_path)
        logger.info("Removing directory %s", dir_path)


rmdir(global_save_dir)  # ์ด์ „ ์‹คํ–‰์—์„œ ๋””๋ ‰ํ† ๋ฆฌ๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ ์‚ญ์ œ
global_save_dir.mkdir()
logger.info("Creating new directory %s", global_save_dir)

๋ฐ์ดํ„ฐ ๋‹ค์šด๋กœ๋“œ

์ƒ˜ํ”Œ ๋ฐ์ดํ„ฐ๋กœ๋Š” ์ „์ฒด ์Šฌ๋ผ์ด๋“œ ์ด๋ฏธ์ง€(whole-slide image)๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  Kather 100k ๋ฐ์ดํ„ฐ์…‹์˜ ๊ฒ€์ฆ(validation) ํ•˜์œ„ ์ง‘๋‹จ(subset)์—์„œ ์ถ”์ถœํ•œ ํŒจ์น˜๋“ค์„ ์‚ฌ์šฉํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

wsi_path = global_save_dir / "sample_wsi.svs"
patches_path = global_save_dir / "kather100k-validation-sample.zip"
weights_path = global_save_dir / "resnet18-kather100k.pth"

logger.info("Download has started. Please wait...")

# ์ „์ฒด ์Šฌ๋ผ์ด๋“œ ์ด๋ฏธ์ง€(whole-slide image) ์ƒ˜ํ”Œ์„ ๋‹ค์šด๋กœ๋“œ ํ•˜๊ณ  ์••์ถ•์„ ํ•ด์ œํ•˜๊ธฐ
download_data(
    "https://tiatoolbox.dcs.warwick.ac.uk/sample_wsis/TCGA-3L-AA1B-01Z-00-DX1.8923A151-A690-40B7-9E5A-FCBEDFC2394F.svs",
    wsi_path,
)

# Kather 100K ๋ฐ์ดํ„ฐ์…‹์„ ํ›ˆ๋ จํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ๋œ ๊ฒ€์ฆ ์„ธํŠธ(validation set) ์ƒ˜ํ”Œ์„ ๋‹ค์šด๋กœ๋“œํ•˜๊ณ  ์••์ถ•์„ ํ•ด์ œํ•˜๊ธฐ
download_data(
    "https://tiatoolbox.dcs.warwick.ac.uk/datasets/kather100k-validation-sample.zip",
    patches_path,
)
with ZipFile(patches_path, "r") as zipfile:
    zipfile.extractall(path=global_save_dir)

# ResNet18 ์•„ํ‚คํ…์ฒ˜๋กœ WSI(์ „์ฒด ์Šฌ๋ผ์ด๋“œ ์ด๋ฏธ์ง€) ๋ถ„๋ฅ˜๋ฅผ ์œ„ํ•ด ์‚ฌ์ „ ํ•™์Šต๋œ ๋ชจ๋ธ ๊ฐ€์ค‘์น˜๋ฅผ ๋‹ค์šด๋กœ๋“œํ•˜๊ธฐ
download_data(
    "https://tiatoolbox.dcs.warwick.ac.uk/models/pc/resnet18-kather100k.pth",
    weights_path,
)

logger.info("Download is complete.")

๋ฐ์ดํ„ฐ ์ฝ๊ธฐ

ํŒจ์น˜ ๋ชฉ๋ก๊ณผ ํ•ด๋‹น๋˜๋Š” ๋ผ๋ฒจ ๋ชฉ๋ก์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, label_list ์˜ ์ฒซ ๋ฒˆ์งธ ๋ผ๋ฒจ์€ patch_list ์˜ ์ฒซ ๋ฒˆ์งธ ์ด๋ฏธ์ง€ ํŒจ์น˜์˜ ํด๋ž˜์Šค๋ฅผ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค.

# ํŒจ์น˜ ๋ฐ์ดํ„ฐ๋ฅผ ์ฝ๊ณ  ํŒจ์น˜ ๋ชฉ๋ก๊ณผ ํ•ด๋‹น ๋ผ๋ฒจ ๋ชฉ๋ก์„ ์ƒ์„ฑ
dataset_path = global_save_dir / "kather100k-validation-sample"

# ๋ฐ์ดํ„ฐ์…‹ ๊ฒฝ๋กœ ์„ค์ •
image_ext = ".tif"  # ๊ฐ ์ด๋ฏธ์ง€์˜ ํŒŒ์ผ ํ™•์žฅ์ž

# ๋ผ๋ฒจ ID์™€ ํด๋ž˜์Šค ์ด๋ฆ„ ๊ฐ„์˜ ๋งคํ•‘
label_dict = {
    "BACK": 0, # Background (empty glass region)  # ์ด ๋ถ€๋ถ„์€ ๋ฐ‘์—์„œ ์ž์„ธํžˆ ์„ค๋ช…
    "NORM": 1, # Normal colon mucosa
    "DEB": 2,  # Debris
    "TUM": 3,  # Colorectal adenocarcinoma epithelium
    "ADI": 4,  # Adipose
    "MUC": 5,  # Mucus
    "MUS": 6,  # Smooth muscle
    "STR": 7,  # Cancer-associated stroma
    "LYM": 8,  # Lymphocytes
}

class_names = list(label_dict.keys())
class_labels = list(label_dict.values())

# ํŒจ์น˜ ๋ชฉ๋ก ์ƒ์„ฑ ๋ฐ ํŒŒ์ผ ์ด๋ฆ„์—์„œ ๋ผ๋ฒจ ์ถ”์ถœํ•˜๊ธฐ
patch_list = []
label_list = []
for class_name, label in label_dict.items():
    dataset_class_path = dataset_path / class_name
    patch_list_single_class = grab_files_from_dir(
        dataset_class_path,
        file_types="*" + image_ext,
    )
    patch_list.extend(patch_list_single_class)
    label_list.extend([label] * len(patch_list_single_class))

# ๋ฐ์ดํ„ฐ์…‹ ํ†ต๊ณ„ ํ‘œ๊ธฐ
plt.bar(class_names, [label_list.count(label) for label in class_labels])
plt.xlabel("Patch types")
plt.ylabel("Number of patches")

# ํด๋ž˜์Šค๋ณ„ ์˜ˆ์‹œ ๊ฐœ์ˆ˜ ์ง‘๊ณ„
for class_name, label in label_dict.items():
    logger.info(
        "Class ID: %d -- Class Name: %s -- Number of images: %d",
        label,
        class_name,
        label_list.count(label),
    )

# ์ „์ฒด ๋ฐ์ดํ„ฐ์…‹ ํ†ต๊ณ„
logger.info("Total number of patches: %d", (len(patch_list)))
.. image-sg:: ../_static/img/tiatoolbox_tutorial/tiatoolbox_tutorial_001.png
   :alt: tiatoolbox tutorial
   :srcset: ../_static/img/tiatoolbox_tutorial/tiatoolbox_tutorial_001.png
   :class: sphx-glr-single-img


.. rst-class:: sphx-glr-script-out

 .. code-block:: none

    |2023-11-14|13:15:59.299| [INFO] Class ID: 0 -- Class Name: BACK -- Number of images: 211
    |2023-11-14|13:15:59.299| [INFO] Class ID: 1 -- Class Name: NORM -- Number of images: 176
    |2023-11-14|13:15:59.299| [INFO] Class ID: 2 -- Class Name: DEB -- Number of images: 230
    |2023-11-14|13:15:59.299| [INFO] Class ID: 3 -- Class Name: TUM -- Number of images: 286
    |2023-11-14|13:15:59.299| [INFO] Class ID: 4 -- Class Name: ADI -- Number of images: 208
    |2023-11-14|13:15:59.299| [INFO] Class ID: 5 -- Class Name: MUC -- Number of images: 178
    |2023-11-14|13:15:59.299| [INFO] Class ID: 6 -- Class Name: MUS -- Number of images: 270
    |2023-11-14|13:15:59.299| [INFO] Class ID: 7 -- Class Name: STR -- Number of images: 209
    |2023-11-14|13:15:59.299| [INFO] Class ID: 8 -- Class Name: LYM -- Number of images: 232
    |2023-11-14|13:15:59.299| [INFO] Total number of patches: 2000



์ด ํŒจ์น˜ ๋ฐ์ดํ„ฐ์…‹์—์„œ ๋ณผ ์ˆ˜ ์žˆ๋“ฏ์ด, 0๋ถ€ํ„ฐ 8๊นŒ์ง€์˜ ID๋ฅผ ๊ฐ€์ง„ 9๊ฐœ์˜ ํด๋ž˜์Šค์™€ ๋ผ๋ฒจ์ด ์žˆ์œผ๋ฉฐ, ๊ฐ ํด๋ž˜์Šค๋Š” ํ•ด๋‹น ํŒจ์น˜์—์„œ ์ฃผ๋กœ ๋‚˜ํƒ€๋‚˜๋Š” ์กฐ์ง ์œ ํ˜•์„ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค:

  • BACK โŸถ ๋ฐฐ๊ฒฝ(Background)(๋น„์–ด ์žˆ๋Š” ์˜์—ญ)
  • LYM โŸถ ๋ฆผํ”„๊ตฌ(Lymphocytes)
  • NORM โŸถ ์ •์ƒ ๋Œ€์žฅ ์ ๋ง‰(Normal colon mucosa)
  • DEB โŸถ ์กฐ์ง ํŒŒํŽธ(Debris)
  • MUS โŸถ ํ‰ํ™œ๊ทผ(Smooth muscle)
  • STR โŸถ ์•” ๊ด€๋ จ ๊ธฐ์งˆ(Cancer-associated stroma)
  • ADI โŸถ ์ง€๋ฐฉ ์กฐ์ง(Adipose)
  • MUC โŸถ ์ ์•ก(Mucus)
  • TUM โŸถ ๋Œ€์žฅ์„ ์•” ์ƒ(Colorectal adenocarcinoma epithelium)

์ด๋ฏธ์ง€ ํŒจ์น˜ ๋ถ„๋ฅ˜

๋จผ์ € patch ๋ชจ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋””์ง€ํ„ธ ์Šฌ๋ผ์ด๋“œ ๋‚ด์˜ ๊ฐ ํŒจ์น˜์— ๋Œ€ํ•œ ์˜ˆ์ธก์„ ๊ตฌํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์‹œ์—ฐํ•œ ํ›„, wsi ๋ชจ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํฐ(large) ์Šฌ๋ผ์ด๋“œ์— ๋Œ€ํ•ด ์˜ˆ์ธก์„ ์ˆ˜ํ–‰ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค.

PatchPredictor ๋ชจ๋ธ ์ •์˜ํ•˜๊ธฐ

PatchPredictor ํด๋ž˜์Šค๋Š” PyTorch๋กœ ์ž‘์„ฑ๋œ CNN ๊ธฐ๋ฐ˜ ๋ถ„๋ฅ˜๊ธฐ๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค

  • ๋ชจ๋ธ ์€ tiatoolbox.models.abc.ModelABC `(๋ฌธ์„œ)
    <https://tia-toolbox.readthedocs.io/en/latest/_autosummary/tiatoolbox.models.models_abc.ModelABC.html>`__ ํด๋ž˜์Šค ๊ตฌ์กฐ๋ฅผ ๋”ฐ๋ฅด๋Š” ๋ชจ๋“  PyTorch๋กœ ํ›ˆ๋ จ๋œ ๋ชจ๋ธ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด์— ๋Œ€ํ•œ ์ž์„ธํ•œ ๋‚ด์šฉ์€ ๊ณ ๊ธ‰ ๋ชจ๋ธ ๊ธฐ์ˆ ์— ๊ด€ํ•œ ์˜ˆ์ œ ๋…ธํŠธ๋ถ(notebook). ์„ ์ฐธ์กฐํ•˜์‹ญ์‹œ์˜ค. ์ปค์Šคํ…€ ๋ชจ๋ธ์„ ๋กœ๋“œํ•˜๋ ค๋ฉด, preproc_func(img) ์™€ ๊ฐ™์€ ์ „์ฒ˜๋ฆฌ ํ•จ์ˆ˜๋ฅผ ์ž‘์„ฑํ•ด์•ผ ํ•˜๋ฉฐ, ์ด ํ•จ์ˆ˜๋Š” ์ž…๋ ฅ tensor๊ฐ€ ๋กœ๋“œ๋œ ๋„คํŠธ์›Œํฌ์— ์ ํ•ฉํ•œ ํ˜•์‹์œผ๋กœ ๋˜์–ด ์žˆ๋Š”์ง€ ํ™•์ธํ•ด์ค๋‹ˆ๋‹ค.
  • ๋˜ํ•œ, ์‚ฌ์ „ ํ•™์Šต๋œ ๋ชจ๋ธ(pretrained_model) ์„ ๋ฌธ์ž์—ด ์ธ์ˆ˜๋กœ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Š” ์˜ˆ์ธก์„ ์ˆ˜ํ–‰ํ•  CNN ๋ชจ๋ธ์„ ์ง€์ •ํ•˜๋ฉฐ, ํ•ด๋‹น ๋ชจ๋ธ์€ ์—ฌ๊ธฐ ๋‚˜์—ด๋œ ๋ชจ๋ธ ์ค‘ ํ•˜๋‚˜์ด์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋ช…๋ น์–ด๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค: predictor = PatchPredictor(pretrained_model='resnet18-kather100k', pretrained_weights=weights_path, batch_size=32) .
  • pretrained_weights : ์‚ฌ์ „ ํ•™์Šต๋œ ๋ชจ๋ธ(pretrained_model) ์„ ์‚ฌ์šฉํ•  ๋•Œ, ํ•ด๋‹น ๋ชจ๋ธ์˜ ์‚ฌ์ „ ํ•™์Šต๋œ ๊ฐ€์ค‘์น˜๋„ ๊ธฐ๋ณธ์ ์œผ๋กœ ๋‹ค์šด๋กœ๋“œ ๋ฉ๋‹ˆ๋‹ค. ๊ธฐ๋ณธ์œผ๋กœ ์ œ๊ณต๋˜๋Š” ๊ฐ€์ค‘์น˜๋ฅผ ๋ฎ์–ด์“ฐ๊ณ  ์ž์‹ ๋งŒ์˜ ๊ฐ€์ค‘์น˜๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๋ฉด pretrained_weight ์ธ์ˆ˜๋ฅผ ํ†ตํ•ด ๊ฐ€์ค‘์น˜๋ฅผ ์ง€์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • batch_size : ๋ชจ๋ธ์— ํ•œ ๋ฒˆ์— ์ž…๋ ฅ๋˜๋Š” ์ด๋ฏธ์ง€์˜ ๊ฐœ์ˆ˜๋ฅผ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค. ์ด ๊ฐ’์ด ํด์ˆ˜๋ก ๋” ๋งŽ์€ (GPU)๋ฉ”๋ชจ๋ฆฌ ์šฉ๋Ÿ‰์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.
# TIAToolbox์—์„œ ์‚ฌ์ „ ํ•™์Šต๋œ PyTorch ๋ชจ๋ธ ๊ฐ€์ ธ์˜ค๊ธฐ
predictor = PatchPredictor(pretrained_model='resnet18-kather100k', batch_size=32)

# ์‚ฌ์šฉ์ž๋Š” ์•„๋ž˜ ์Šคํฌ๋ฆฝํŠธ๋ฅผ ํ†ตํ•ด ์›ํ•˜๋Š” PyTorch ๋ชจ๋ธ ์•„ํ‚คํ…์ฒ˜๋ฅผ ๋ถˆ๋Ÿฌ์˜ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
model = vanilla.CNNModel(backbone="resnet18", num_classes=9) # torchvision.models.resnet18์—์„œ ๋ชจ๋ธ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ
model.load_state_dict(torch.load(weights_path, map_location="cpu"), strict=True)
def preproc_func(img):
    img = PIL.Image.fromarray(img)
    img = transforms.ToTensor()(img)
    return img.permute(1, 2, 0)
model.preproc_func = preproc_func
predictor = PatchPredictor(model=model, batch_size=32)

ํŒจ์น˜ ๋ผ๋ฒจ ์˜ˆ์ธกํ•˜๊ธฐ

์˜ˆ์ธก๊ธฐ(predictor) ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•œ ํ›„ patch ๋ชจ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ predict ๋ฉ”์†Œ๋“œ๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ ๋‹ค์Œ, ๋ถ„๋ฅ˜ ์ •ํ™•๋„์™€ ์˜ค์ฐจ ํ–‰๋ ฌ(confusion matrix)์„ ๊ณ„์‚ฐํ•ฉ๋‹ˆ๋‹ค.

with suppress_console_output():
    output = predictor.predict(imgs=patch_list, mode="patch", on_gpu=ON_GPU)

acc = accuracy_score(label_list, output["predictions"])
logger.info("Classification accuracy: %f", acc)

# ํŒจ์น˜ ๋ถ„๋ฅ˜ ๊ฒฐ๊ณผ๋ฅผ ์œ„ํ•œ ์˜ค์ฐจ ํ–‰๋ ฌ(confusion_matrix) ์ƒ์„ฑ ๋ฐ ์‹œ๊ฐํ™”
conf = confusion_matrix(label_list, output["predictions"], normalize="true")
df_cm = pd.DataFrame(conf, index=class_names, columns=class_names)
df_cm
.. rst-class:: sphx-glr-script-out

 .. code-block:: none

    |2023-11-14|13:16:03.215| [INFO] Classification accuracy: 0.993000


.dataframe tbody tr th:only-of-type { vertical-align: middle; } .dataframe tbody tr th { vertical-align: top; } .dataframe thead th { text-align: right; }
BACK NORM DEB TUM ADI MUC MUS STR LYM
BACK 1.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.00000
NORM 0.000000 0.988636 0.000000 0.011364 0.000000 0.000000 0.000000 0.000000 0.00000
DEB 0.000000 0.000000 0.991304 0.000000 0.000000 0.000000 0.000000 0.008696 0.00000
TUM 0.000000 0.000000 0.000000 0.996503 0.000000 0.003497 0.000000 0.000000 0.00000
ADI 0.004808 0.000000 0.000000 0.000000 0.990385 0.000000 0.004808 0.000000 0.00000
MUC 0.000000 0.000000 0.000000 0.000000 0.000000 0.988764 0.000000 0.011236 0.00000
MUS 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.996296 0.003704 0.00000
STR 0.000000 0.000000 0.004785 0.000000 0.000000 0.004785 0.004785 0.985646 0.00000
LYM 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.004310 0.99569


์ „์ฒด ์Šฌ๋ผ์ด๋“œ(whole slide)์— ๋Œ€ํ•œ ํŒจ์น˜ ๋ผ๋ฒจ ์˜ˆ์ธก

IOPatchPredictorConfig ํด๋ž˜์Šค๋ฅผ ์†Œ๊ฐœํ•ฉ๋‹ˆ๋‹ค. ์ด ํด๋ž˜์Šค๋Š” ๋ชจ๋ธ ์˜ˆ์ธก ์—”์ง„์„ ์œ„ํ•œ ์ด๋ฏธ์ง€ ์ฝ๊ธฐ ๋ฐ ์˜ˆ์ธก ๊ฒฐ๊ณผ ์“ฐ๊ธฐ ๊ตฌ์„ฑ ์„ค์ •์„ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค. ์ด ์„ค์ •์€ ๋ถ„๋ฅ˜๊ธฐ(classifier)์—๊ฒŒ WSI ํ”ผ๋ผ๋ฏธ๋“œ์˜ ์–ด๋Š ๋ ˆ๋ฒจ์„ ์ฝ๊ณ , ๋ฐ์ดํ„ฐ๋ฅผ ์ฒ˜๋ฆฌํ•˜๋ฉฐ, ์ถœ๋ ฅ์„ ์ƒ์„ฑํ•ด์•ผ ํ•˜๋Š”์ง€ ์•Œ๋ ค์ฃผ๋Š” ๋ฐ ํ•„์ˆ˜์ ์ž…๋‹ˆ๋‹ค.

IOPatchPredictorConfig ์˜ ๋งค๊ฐœ๋ณ€์ˆ˜๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ •์˜๋ฉ๋‹ˆ๋‹ค.

  • input_resolutions: ์ž…๋ ฅ์˜ ํ•ด์ƒ๋„๋ฅผ ์ง€์ •ํ•˜๋Š” ๋”•์…”๋„ˆ๋ฆฌ ํ˜•ํƒœ์˜ ๋ฆฌ์ŠคํŠธ๋กœ, ๊ฐ ์ž…๋ ฅ์˜ ํ•ด์ƒ๋„๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. ๋ฆฌ์ŠคํŠธ์˜ ์š”์†Œ๋Š” model.forward() ์˜ ์ˆœ์„œ์™€ ๊ฐ™์•„์•ผํ•ฉ๋‹ˆ๋‹ค. ๋งŒ์•ฝ ๋ชจ๋ธ์ด ํ•˜๋‚˜์˜ ์ž…๋ ฅ๋งŒ ๋ฐ›๋Š” ๊ฒฝ์šฐ, ํ•˜๋‚˜์˜ ๋”•์…”๋„ˆ๋ฆฌ๋กœ 'units' ์™€ 'resolution' ์„ ์ง€์ •ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค. TIAToolbox๋Š” ํ•˜๋‚˜ ์ด์ƒ์˜ ์ž…๋ ฅ์„ ๋ฐ›๋Š” ๋ชจ๋ธ์„ ์ง€์›ํ•˜๋ฏ€๋กœ, ์—ฌ๋Ÿฌ ์ž…๋ ฅ์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ์—๋„ ๋ฌธ์ œ์—†์ด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์œ ๋‹›(units)๊ณผ ํ•ด์ƒ๋„(resolution)์— ๋Œ€ํ•œ ์ž์„ธํ•œ ๋‚ด์šฉ์€ TIAToolbox ๋ฌธ์„œ ๋ฅผ ์ฐธ๊ณ ํ•˜์‹ญ์‹œ์˜ค.
  • patch_input_shape: ๊ฐ€์žฅ ํฐ ์ž…๋ ฅ์˜ ํฌ๊ธฐ๋ฅผ (๋†’์ด, ๋„ˆ๋น„) ํ˜•์‹์œผ๋กœ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค.
  • stride_shape: ์—ฐ์†๋œ ๋‘ ํŒจ์น˜ ์‚ฌ์ด์˜ ๊ฐ„๊ฒฉ(๋‹จ๊ณ„) ํฌ๊ธฐ๋ฅผ ์ง€์ •ํ•˜๋ฉฐ, ํŒจ์น˜ ์ถ”์ถœ ๊ณผ์ •์—์„œ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž๊ฐ€ stride_shape ๋ฅผ patch_input_shape ์™€ ๋™์ผํ•˜๊ฒŒ ์„ค์ •ํ•˜๋ฉด, ํŒจ์น˜๋“ค์ด ์ค‘์ฒฉ์—†์ด ์ถ”์ถœ๋˜๊ณ , ์ฒ˜๋ฆฌ๋ฉ๋‹ˆ๋‹ค.
wsi_ioconfig = IOPatchPredictorConfig(
    input_resolutions=[{"units": "mpp", "resolution": 0.5}],
    patch_input_shape=[224, 224],
    stride_shape=[224, 224],
)

predict ๋ฉ”์†Œ๋“œ๋Š” ์ž…๋ ฅ ํŒจ์น˜์— CNN์„ ์ ์šฉํ•˜์—ฌ ๊ฒฐ๊ณผ๋ฅผ ์–ป์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ์€ ํ•ด๋‹น ๋ฉ”์†Œ๋“œ์˜ ์ธ์ˆ˜์™€ ์„ค๋ช…์ž…๋‹ˆ๋‹ค:

  • mode: ์ฒ˜๋ฆฌํ•  ์ž…๋ ฅ์˜ ์œ ํ˜•์„ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค. ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ์— ๋”ฐ๋ผ patch, tile ๋˜๋Š” wsi ์ค‘์—์„œ ์„ ํƒํ•ฉ๋‹ˆ๋‹ค.
  • imgs: ์ž…๋ ฅ ํŒŒ์ผ๋“ค์˜ ๊ฒฝ๋กœ ๋ฆฌ์ŠคํŠธ๋กœ, ์ž…๋ ฅ ํƒ€์ผ(input tiles) ๋˜๋Š” WSIs ๊ฒฝ๋กœ์˜ ๋ชฉ๋ก์ด์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • return_probabilities: ์ž…๋ ฅ ํŒจ์น˜์˜ ์˜ˆ์ธก๋œ ๋ผ๋ฒจ๊ณผ ํ•จ๊ป˜ ํด๋ž˜์Šค๋ณ„ ํ™•๋ฅ ์„ ์–ป์œผ๋ ค๋ฉด True ๋กœ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. tile ๋˜๋Š” wsi ๋ชจ๋“œ์—์„œ ์˜ˆ์ธก ๊ฒฐ๊ณผ๋ฅผ ๋ณ‘ํ•ฉํ•˜์—ฌ ์˜ˆ์ธก ๋งต์„ ์ƒ์„ฑํ•˜๋ ค๋ฉด return_probabilities=True ๋กœ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ioconfig: IOPatchPredictorConfig ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ IO ๊ตฌ์„ฑ ์ •๋ณด๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
  • resolution ๊ณผ unit (์•„๋ž˜์— ํ‘œ์‹œ๋˜์ง€ ์•Š์Œ): ์ถ”์ถœํ•  ํŒจ์น˜์˜ WSI ๋ ˆ๋ฒจ ๋˜๋Š” ๋งˆ์ดํฌ๋ก ๋‹น ํ”ฝ์…€ ํ•ด์ƒ๋„๋ฅผ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ioconfig ๋Œ€์‹  ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ WSI ๋ ˆ๋ฒจ์€ 'baseline' ์œผ๋กœ ์ง€์ •ํ•˜๋ฉฐ, ์ด๋Š” ์ผ๋ฐ˜์ ์œผ๋กœ ๋ ˆ๋ฒจ 0์— ์ด ๊ฒฝ์šฐ ์ด๋ฏธ์ง€๋Š” ํ•˜๋‚˜์˜ ๋ ˆ๋ฒจ๋งŒ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๋” ์ž์„ธํ•œ ๋‚ด์šฉ์€ ๋ฌธ์„œ ์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • masks: imgs ๋ฆฌ์ŠคํŠธ์— ์žˆ๋Š” WSI์˜ ๋งˆ์Šคํฌ ๊ฒฝ๋กœ ๋ฆฌ์ŠคํŠธ์ž…๋‹ˆ๋‹ค. ์ด ๋งˆ์Šคํฌ๋Š” ์›๋ณธ WSI์—์„œ ํŒจ์น˜๋ฅผ ์ถ”์ถœํ•˜๊ณ ์ž ํ•˜๋Š” ์˜์—ญ์„ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค. ํŠน์ • WSI์˜ ๋งˆ์Šคํฌ๊ฐ€ None ์œผ๋กœ ์ง€์ •๋˜๋ฉด, ํ•ด๋‹น WSI์˜ ๋ชจ๋“  ํŒจ์น˜(๋ฐฐ๊ฒฝ ์˜์—ญ ํฌํ•จ)์— ๋Œ€ํ•œ ๋ผ๋ฒจ์ด ์˜ˆ์ธก๋ฉ๋‹ˆ๋‹ค. ์ด๋Š” ๋ถˆํ•„์š”ํ•œ ๊ณ„์‚ฐ์„ ์œ ๋ฐœํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • merge_predictions: ํŒจ์น˜ ๋ถ„๋ฅ˜ ๊ฒฐ๊ณผ๋ฅผ 2D ๋งต์œผ๋กœ ์ƒ์„ฑํ•ด์•ผํ•˜๋Š” ๊ฒฝ์šฐ True ๋กœ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ํฐ WSI์˜ ๊ฒฝ์šฐ ๋งŽ์€ ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ํ•„์š”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋Œ€์•ˆ์ ์ธ ํ•ด๊ฒฐ์ฑ…์œผ๋กœ๋Š” merge_predictions=False ๋กœ(๊ธฐ๋ณธ๊ฐ’) ์„ค์ •ํ•˜์—ฌ, ์ถ”ํ›„์— merge_predictions ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•ด 2D ์˜ˆ์ธก ๋งต์„ ์ƒ์„ฑํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ํฐ WSI๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ํŒจ์น˜ ์ถ”์ถœ ๋ฐ ์˜ˆ์ธก ๊ณผ์ •์— ์‹œ๊ฐ„์ด ๋‹ค์†Œ ๊ฑธ๋ฆด ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. (๋งŒ์•ฝ Cuda๊ฐ€ ํ™œ์„ฑํ™”๋œ GPU๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค๋ฉด ON_GPU=True ๋กœ ์„ค์ •ํ•˜์—ฌ PyTorch์™€ Cuda๋ฅผ ํ™œ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค).

with suppress_console_output():
    wsi_output = predictor.predict(
        imgs=[wsi_path],
        masks=None,
        mode="wsi",
        merge_predictions=False,
        ioconfig=wsi_ioconfig,
        return_probabilities=True,
        save_dir=global_save_dir / "wsi_predictions",
        on_gpu=ON_GPU,
    )

wsi_output ์„ ์‹œ๊ฐํ™”ํ•˜์—ฌ ์˜ˆ์ธก ๋ชจ๋ธ์ด ์ „์ฒด ์Šฌ๋ผ์ด๋“œ ์ด๋ฏธ์ง€(WSI)์—์„œ ์–ด๋–ป๊ฒŒ ์ž‘๋™ํ•˜๋Š”์ง€ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋จผ์ € ํŒจ์น˜ ์˜ˆ์ธก ๊ฒฐ๊ณผ๋ฅผ ๋ณ‘ํ•ฉํ•œ ํ›„, ์ด๋ฅผ ์›๋ณธ ์ด๋ฏธ์ง€ ์œ„์— ์˜ค๋ฒ„๋ ˆ์ด๋กœ ์‹œ๊ฐํ™”ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด์ „๊ณผ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ merge_predictions ๋ฉ”์†Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํŒจ์น˜ ์˜ˆ์ธก์„ ๋ณ‘ํ•ฉํ•ฉ๋‹ˆ๋‹ค. ์ด๋•Œ, 1.25x ๋งŒํผ ํ™•๋Œ€๋œ ์˜ˆ์ธก ๋งต์„ ์ƒ์„ฑํ•˜๊ธฐ ์œ„ํ•ด resolution=1.25, units='power' ๋กœ ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. ๋งŒ์•ฝ ๋” ๋†’์€/๋‚ฎ์€ ํ•ด์ƒ๋„ (๋” ํฐ/์ž‘์€) ์˜ˆ์ธก ๋งต์„ ์›ํ•œ๋‹ค๋ฉด, ์ด ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ์ ์ ˆํžˆ ๋ณ€๊ฒฝํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ์ธก์ด ๋ณ‘ํ•ฉ๋˜๋ฉด overlay_patch_prediction ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์˜ˆ์ธก ๋งต์„ WSI ์ธ๋„ค์ผ์— ์˜ค๋ฒ„๋ ˆ์ดํ•ฉ๋‹ˆ๋‹ค. ์ด๋•Œ ์‚ฌ์šฉ๋œ ํ•ด์ƒ๋„๋Š” ์˜ˆ์ธก ๋ณ‘ํ•ฉ์— ์‚ฌ์šฉ๋œ ํ•ด์ƒ๋„์™€ ์ผ์น˜ํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค.

overview_resolution = (
    4  # ํŒจ์น˜ ์˜ˆ์ธก์„ ๋ณ‘ํ•ฉํ•˜๊ณ  ์‹œ๊ฐํ™”ํ•˜๋Š” ํ•ด์ƒ๋„ ์„ค์ •
)
# 'ํ•ด์ƒ๋„' ๋งค๊ฐœ๋ณ€์ˆ˜์˜ ๋‹จ์œ„ ์„ค์ •. "power", "level", "mpp", ๋˜๋Š” "baseline" ์ค‘์—์„œ ์„ ํƒ ๊ฐ€๋Šฅ
overview_unit = "mpp"
wsi = WSIReader.open(wsi_path)
wsi_overview = wsi.slide_thumbnail(resolution=overview_resolution, units=overview_unit)
plt.figure(), plt.imshow(wsi_overview)
plt.axis("off")
.. image-sg:: ../_static/img/tiatoolbox_tutorial/tiatoolbox_tutorial_002.png
   :alt: tiatoolbox tutorial
   :srcset: ../_static/img/tiatoolbox_tutorial/tiatoolbox_tutorial_002.png
   :class: sphx-glr-single-img



์˜ˆ์ธก ๋งต์„ ์ด ์ด๋ฏธ์ง€์— ์˜ค๋ฒ„๋ ˆ์ดํ•œ ๊ฒฐ๊ณผ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค:

# ์ „์ฒด ์Šฌ๋ผ์ด๋“œ ์ด๋ฏธ์ง€(Whole slide image)์˜ ํŒจ์น˜ ๋ ˆ๋ฒจ ์˜ˆ์ธก ์‹œ๊ฐํ™”
# ๋จผ์ € ๋ผ๋ฒจ ์ƒ‰์ƒ์˜ ๋งคํ•‘ ์„ค์ •
label_color_dict = {}
label_color_dict[0] = ("empty", (0, 0, 0))
colors = cm.get_cmap("Set1").colors
for class_name, label in label_dict.items():
    label_color_dict[label + 1] = (class_name, 255 * np.array(colors[label]))

pred_map = predictor.merge_predictions(
    wsi_path,
    wsi_output[0],
    resolution=overview_resolution,
    units=overview_unit,
)
overlay = overlay_prediction_mask(
    wsi_overview,
    pred_map,
    alpha=0.5,
    label_info=label_color_dict,
    return_ax=True,
)
plt.show()
.. image-sg:: ../_static/img/tiatoolbox_tutorial/tiatoolbox_tutorial_003.png
   :alt: tiatoolbox tutorial
   :srcset: ../_static/img/tiatoolbox_tutorial/tiatoolbox_tutorial_003.png
   :class: sphx-glr-single-img



๋ณ‘๋ฆฌํ•™์— ํŠนํ™”๋œ ๋ชจ๋ธ์„ ์‚ฌ์šฉํ•œ ํŠน์ง• ์ถ”์ถœ

์ด ๋ถ€๋ถ„์—์„œ๋Š” TIAToolbox ์™ธ๋ถ€์— ์กด์žฌํ•˜๋Š” ์‚ฌ์ „ ํ•™์Šต๋œ PyTorch ๋ชจ๋ธ์—์„œ ํŠน์ง•์„ ์ถ”์ถœํ•˜๋Š” ๋ฐฉ๋ฒ•์„ TIAToolbox์—์„œ ์ œ๊ณตํ•˜๋Š” WSI ์ถ”๋ก  ์—”์ง„์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค. ์ด๋ฅผ ์„ค๋ช…ํ•˜๊ธฐ ์œ„ํ•ด, HistoEncoder๋ผ๋Š” ๋ณ‘๋ฆฌํ•™์  ์ด๋ฏธ์ง€์— ํŠนํ™”๋œ ๋ชจ๋ธ์„ ์‚ฌ์šฉํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. HistoEncoder๋Š” ์กฐ์งํ•™ ์ด๋ฏธ์ง€์—์„œ ํŠน์ง•์„ ์ถ”์ถœํ•˜๋„๋ก ์ž๊ฐ€ ์ง€๋„ ํ•™์Šต ๋ฐฉ์‹(self-supervised) ์œผ๋กœ ํ•™์Šต๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์ด ๋ชจ๋ธ์€ ๋‹ค์Œ์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

โ€˜HistoEncoder: ๋””์ง€ํ„ธ ๋ณ‘๋ฆฌํ•™์„ ์œ„ํ•œ ๊ธฐ๋ณธ ๋ชจ๋ธโ€™ (https://github.com/jopo666/HistoEncoder) ํ—ฌ์‹ฑํ‚ค ๋Œ€ํ•™๊ต Pohjonen, Joona ํŒ€.

ํŠน์ง• ๋งต์˜ umap ์ฐจ์› ์ถ•์†Œ๋ฅผ 3D(RGB)๋กœ ์‹œ๊ฐํ™”ํ•˜์—ฌ, ์œ„์—์„œ ์–ธ๊ธ‰ํ•œ ์—ฌ๋Ÿฌ ์กฐ์ง ์œ ํ˜• ๊ฐ„์˜ ์ฐจ์ด๋ฅผ ํŠน์ง•๋“ค์ด ์–ด๋–ป๊ฒŒ ํฌ์ฐฉํ•˜๋Š”์ง€ ๋ณด์—ฌ์ค„ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

# ์ถ”๊ฐ€ module ๊ฐ€์ ธ์˜ค๊ธฐ
import histoencoder.functional as F
import torch.nn as nn

from tiatoolbox.models.engine.semantic_segmentor import DeepFeatureExtractor, IOSegmentorConfig
from tiatoolbox.models.models_abc import ModelABC
import umap

TIAToolbox๋Š” PyTorch์˜ nn.Module ์„ ์ƒ์†ํ•˜๋Š” ModelABC ํด๋ž˜์Šค๋ฅผ ์ •์˜ํ•˜๋ฉฐ, ์ด๋Š” TIAToolbox์˜ ์ถ”๋ก  ์—”์ง„์—์„œ ์‚ฌ์šฉ๋  ๋ชจ๋ธ์˜ ๊ตฌ์กฐ๋ฅผ ๊ทœ์ •ํ•ฉ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ histoencoder ๋ชจ๋ธ์€ ์ด ๊ตฌ์กฐ๋ฅผ ๋”ฐ๋ฅด์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์—, TIAToolbox ์—”์ง„์ด ๊ธฐ๋Œ€ํ•˜๋Š” ์ถœ๋ ฅ๊ณผ ๋ฉ”์†Œ๋“œ๋ฅผ ์ œ๊ณตํ•˜๋Š” ํด๋ž˜์Šค๋กœ HistoEncoder๋ฅผ ๋ž˜ํ•‘(wrap)ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

class HistoEncWrapper(ModelABC):
    """tiatoolbox์˜ ModelABC ์ธํ„ฐํŽ˜์ด์Šค์— ๋งž์ถ˜ HistoEncW๋ชจ๋ธ์˜ ๋ ˆํผ ์ƒ์„ฑ"""

    def __init__(self: HistoEncWrapper, encoder) -> None:
        super().__init__()
        self.feat_extract = encoder

    def forward(self: HistoEncWrapper, imgs: torch.Tensor) -> torch.Tensor:
        """์ž…๋ ฅ ๋ฐ์ดํ„ฐ๋ฅผ ๋ชจ๋ธ์„ ํ†ตํ•ด ์ „๋‹ฌ

        Args:
            imgs (torch.Tensor):
                Model input.

        """
        out = F.extract_features(self.feat_extract, imgs, num_blocks=2, avg_pool=True)
        return out

    @staticmethod
    def infer_batch(
        model: nn.Module,
        batch_data: torch.Tensor,
        *,
        on_gpu: bool,
    ) -> list[np.ndarray]:
        """์ž…๋ ฅ ๋ฐฐ์น˜์— ๋Œ€ํ•œ ์ถ”๋ก  ์‹คํ–‰

        ์ˆœ๋ฐฉํ–ฅ ์—ฐ์‚ฐ๊ณผ ์ž…์ถœ๋ ฅ ์ง‘๊ณ„๋ฅผ ์œ„ํ•œ ๋กœ์ง์ด ํฌํ•จ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

        Args:
            model (nn.Module):
                ์ •์˜๋œ PyTorch ๋ชจ๋ธ.
            batch_data (torch.Tensor):
                `torch.utils.data.DataLoader`์—
                ์˜ํ•ด ์ƒ์„ฑ๋œ ๋ฐ์ดํ„ฐ์˜ ๋ฐฐ์น˜(batch).
            on_gpu (bool):
                ์ถ”๋ก  ์—ฐ์‚ฐ์„ GPU์—์„œ ํ•  ๊ฒƒ์ธ์ง€.

        """
        img_patches_device = batch_data.to('cuda') if on_gpu else batch_data
        model.eval()
        # ๊ธฐ์šธ๊ธฐ๋ฅผ ๊ณ„์‚ฐํ•˜์ง€ ์•Š์Œ(ํ›ˆ๋ จ์ด ์•„๋‹˜)
        with torch.inference_mode():
            output = model(img_patches_device)
        return [output.cpu().numpy()]

์ด์ œ ๋ž˜ํผ(wrapper)๋ฅผ ๋งŒ๋“ค์—ˆ์œผ๋‹ˆ, ํŠน์ง• ์ถ”์ถœ ๋ชจ๋ธ์„ ์ƒ์„ฑํ•˜๊ณ  DeepFeatureExtractor ๋ฅผ ์ธ์Šคํ„ด์Šคํ™” ํ•˜์—ฌ ์ด ๋ชจ๋ธ์„ WSI์— ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค. ์ด์ „์— ์‚ฌ์šฉํ–ˆ๋˜ ๋™์ผํ•œ WSI๋ฅผ ์‚ฌ์šฉํ•˜์ง€๋งŒ, ์ด๋ฒˆ์—๋Š” ๊ฐ ํŒจ์น˜์— ๋Œ€ํ•œ ๋ผ๋ฒจ์„ ์˜ˆ์ธกํ•˜๋Š” ๋Œ€์‹  HistoEncoder ๋ชจ๋ธ์„ ์‚ฌ์šฉํ•˜์—ฌ, WSI์˜ ํŒจ์น˜์—์„œ ํŠน์ง•์„ ์ถ”์ถœํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

# ๋ชจ๋ธ ๋งŒ๋“ค๊ธฐ
encoder = F.create_encoder("prostate_medium")
model = HistoEncWrapper(encoder)

# ์ „์ฒ˜๋ฆฌ ํ•จ์ˆ˜ ์„ค์ •
norm=transforms.Normalize(mean=[0.662, 0.446, 0.605],std=[0.169, 0.190, 0.155])
trans = [
    transforms.ToTensor(),
    norm,
]
model.preproc_func = transforms.Compose(trans)

wsi_ioconfig = IOSegmentorConfig(
    input_resolutions=[{"units": "mpp", "resolution": 0.5}],
    patch_input_shape=[224, 224],
    output_resolutions=[{"units": "mpp", "resolution": 0.5}],
    patch_output_shape=[224, 224],
    stride_shape=[224, 224],
)

DeepFeatureExtractor ๋ฅผ ์ƒ์„ฑํ•  ๋•Œ auto_generate_mask=True ์ธ์ˆ˜๋ฅผ ์ „๋‹ฌํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด๋Š” otsu ์ž„๊ณ„๊ฐ’ ์•Œ๊ณ ๋ฆฌ์ฆ˜์„ ์‚ฌ์šฉํ•˜์—ฌ ์ž๋™์œผ๋กœ ์กฐ์ง ์˜์—ญ์˜ ๋งˆ์Šคํฌ๋ฅผ ์ƒ์„ฑํ•˜์—ฌ, ์ถ”์ถœ๊ธฐ๊ฐ€ ์กฐ์ง์ด ํฌํ•จ๋œ ํŒจ์น˜์—๋งŒ ์ฒ˜๋ฆฌํ•˜๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.

# ํŠน์ง• ์ถ”์ถœ๊ธฐ ์ƒ์„ฑ ๋ฐ WSI์—์„œ ์‹คํ–‰ํ•˜๊ธฐ
extractor = DeepFeatureExtractor(model=model, auto_generate_mask=True, batch_size=32, num_loader_workers=4, num_postproc_workers=4)
with suppress_console_output():
    out = extractor.predict(imgs=[wsi_path], mode="wsi", ioconfig=wsi_ioconfig, save_dir=global_save_dir / "wsi_features",)

์ด๋Ÿฌํ•œ ํŠน์ง•๋“ค์€ ๋‹ค์šด์ŠคํŠธ๋ฆผ ๋ชจ๋ธ์„ ํ›ˆ๋ จํ•˜๋Š” ๋ฐ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, ์—ฌ๊ธฐ์„œ๋Š” ํŠน์ง•์ด ๋ฌด์—‡์„ ๋‚˜ํƒ€๋‚ด๋Š”์ง€ ์ง๊ด€์ ์œผ๋กœ ์ดํ•ดํ•˜๊ธฐ ์œ„ํ•ด UMAP ์ฐจ์› ์ถ•์†Œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํŠน์ง•์„ RGB ๊ณต๊ฐ„์—์„œ ์‹œ๊ฐํ™”ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์œ ์‚ฌํ•œ ์ƒ‰์ƒ์œผ๋กœ ๋ผ๋ฒจ๋ง๋œ ํฌ์ธํŠธ๋“ค์€ ๋น„์Šทํ•œ ํŠน์ง•์„ ๊ฐ€์ง€๊ณ  ์žˆ์–ด์•ผ ํ•˜๋ฏ€๋กœ, UMAP ์ถ•์†Œ ๊ฒฐ๊ณผ๋ฅผ WSI ์ธ๋„ค์ผ์— ์˜ค๋ฒ„๋ ˆ์ดํ•˜์—ฌ ํŠน์ง•๋“ค์ด ์„œ๋กœ ๋‹ค๋ฅธ ์กฐ์ง ์˜์—ญ์œผ๋กœ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ๋ถ„๋ฆฌ๋˜๋Š”์ง€ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ดํ›„, ์œ„์—์„œ ์ƒ์„ฑํ•œ ํŒจ์น˜ ์ˆ˜์ค€์˜ ์˜ˆ์ธก ๋งต๊ณผ ํ•จ๊ป˜ ์ด๋ฅผ ์‹œ๊ฐํ™”ํ•˜์—ฌ, ํŠน์ง•๊ณผ ํŒจ์น˜ ์ˆ˜์ค€ ์˜ˆ์ธก ๊ฐ„์˜ ์ฐจ์ด๋ฅผ ๋น„๊ตํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

# ๋จผ์ €, UMAP ์ถ•์†Œ ๊ณ„์‚ฐ์„ ์œ„ํ•œ ํ•จ์ˆ˜ ์ •์˜
def umap_reducer(x, dims=3, nns=10):
    """์ž…๋ ฅ ๋ฐ์ดํ„ฐ์˜ UMAP ์ถ•์†Œ"""
    reducer = umap.UMAP(n_neighbors=nns, n_components=dims, metric="manhattan", spread=0.5, random_state=2)
    reduced = reducer.fit_transform(x)
    reduced -= reduced.min(axis=0)
    reduced /= reduced.max(axis=0)
    return reduced

# ํŠน์ง• ์ถ”์ถœ๊ธฐ์—์„œ ์ถœ๋ ฅ๋œ ํŠน์ง• ๋ถˆ๋Ÿฌ์˜ค๊ธฐ
pos = np.load(global_save_dir / "wsi_features" / "0.position.npy")
feats = np.load(global_save_dir / "wsi_features" / "0.features.0.npy")
pos = pos / 8 # 0.5mpp์—์„œ ํŠน์ง•์„ ์ถ”์ถœํ•˜๊ณ , 4mpp์—์„œ ์ธ๋„ค์ผ์— ์˜ค๋ฒ„๋ ˆ์ดํ•˜๊ธฐ

# ํŠน์ง•์„ 3์ฐจ์›(RGB) ๊ณต๊ฐ„์œผ๋กœ ์ถ•์†Œํ•˜๊ธฐ
reduced = umap_reducer(feats)

# ๋ถ„๋ฅ˜๊ธฐ์˜ ์˜ˆ์ธก ๋งต์„ ๋‹ค์‹œ ๊ทธ๋ฆฌ๊ธฐ
overlay = overlay_prediction_mask(
    wsi_overview,
    pred_map,
    alpha=0.5,
    label_info=label_color_dict,
    return_ax=True,
)

# ํŠน์ง• ๋งต ์ถ•์†Œ๋ฅผ ์‹œ๊ฐํ™”ํ•˜๊ธฐ
plt.figure()
plt.imshow(wsi_overview)
plt.scatter(pos[:,0], pos[:,1], c=reduced, s=1, alpha=0.5)
plt.axis("off")
plt.title("UMAP reduction of HistoEnc features")
plt.show()
.. rst-class:: sphx-glr-horizontal


    *

      .. image-sg:: ../_static/img/tiatoolbox_tutorial/tiatoolbox_tutorial_004.png
         :alt: tiatoolbox tutorial
         :srcset: ../_static/img/tiatoolbox_tutorial/tiatoolbox_tutorial_004.png
         :class: sphx-glr-multi-img

    *

      .. image-sg:: ../_static/img/tiatoolbox_tutorial/tiatoolbox_tutorial_005.png
         :alt: UMAP reduction of HistoEnc features
         :srcset: ../_static/img/tiatoolbox_tutorial/tiatoolbox_tutorial_005.png
         :class: sphx-glr-multi-img




ํŒจ์น˜ ์ˆ˜์ค€ ์˜ˆ์ธก๊ธฐ(patch-level predictor)์—์„œ ์ƒ์„ฑ๋œ ์˜ˆ์ธก ๋งต๊ณผ ์ž๊ฐ€ ์ง€๋„ ํ•™์Šต(self-supervised) ์ธ์ฝ”๋”๋ฅผ ํ†ตํ•ด ํŠน์ง•์„ ์ถ”์ถœํ•œ ํŠน์ง• ๋งต์ด WSI์—์„œ ์กฐ์ง ์œ ํ˜•์— ๋Œ€ํ•œ ์œ ์‚ฌํ•œ ์ •๋ณด๋ฅผ ํฌ์ฐฉํ•˜๊ณ  ์žˆ์Œ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Š” ๋ชจ๋ธ์ด ์˜ˆ์ƒ๋Œ€๋กœ ์ž‘๋™ํ•˜๊ณ  ์žˆ์Œ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋Š” ์ข‹์€ ๊ฒ€์ฆ ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค. ๋˜ํ•œ, HistoEncoder ๋ชจ๋ธ์ด ์ถ”์ถœํ•œ ํŠน์ง•๋“ค์ด ์กฐ์ง ์œ ํ˜• ๊ฐ„์˜ ์ฐจ์ด๋ฅผ ์ž˜ ํฌ์ฐฉํ•˜๊ณ  ์žˆ์œผ๋ฉฐ, ๋”ฐ๋ผ์„œ ์ด ํŠน์ง•๋“ค์ด ์กฐ์งํ•™์ ์œผ๋กœ ์ค‘์š”ํ•œ ์ •๋ณด๋ฅผ ์ธ์ฝ”๋”ฉํ•˜๊ณ  ์žˆ์Œ์„ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค.

์•ž์œผ๋กœ ํ•ด์•ผํ•  ๊ฒƒ

์ด ๋…ธํŠธ๋ถ์—์„œ๋Š” PatchPredictor ์™€ DeepFeatureExtractor ํด๋ž˜์Šค์˜ predict ๋ฉ”์†Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํฐ ํƒ€์ผ(tiles)๊ณผ WSI์˜ ํŒจ์น˜์— ๋Œ€ํ•ด ๋ผ๋ฒจ์„ ์˜ˆ์ธกํ•˜๊ฑฐ๋‚˜ ํŠน์ง•์„ ์ถ”์ถœํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค. ๋˜ํ•œ, ํŒจ์น˜ ์˜ˆ์ธก ๊ฒฐ๊ณผ๋ฅผ ๋ณ‘ํ•ฉํ•˜๊ณ  ์ž…๋ ฅ ์ด๋ฏธ์ง€/WSI์— ์˜ˆ์ธก ๋งต์„ ์˜ค๋ฒ„๋ ˆ์ด๋กœ ์‹œ๊ฐํ™”ํ•˜๋Š” merge_predictions ์™€ overlay_prediction_mask ๋ณด์กฐ ํ•จ์ˆ˜๋„ ์†Œ๊ฐœํ•ฉ๋‹ˆ๋‹ค.

๋ชจ๋“  ๊ณผ์ •์€ TIAToolbox ๋‚ด์—์„œ ์ด๋ฃจ์–ด์ง€๋ฉฐ, ์˜ˆ์ œ ์ฝ”๋“œ๋ฅผ ๋”ฐ๋ผ ์‰ฝ๊ฒŒ ๊ตฌ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ž…๋ ฅ๊ฐ’๊ณผ ์˜ต์…˜์„ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์„ค์ •ํ•˜๋Š” ๊ฒƒ์„ ๊ผญ ํ™•์ธํ•˜์„ธ์š”. ๋˜ํ•œ predict ํ•จ์ˆ˜์˜ ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ๋ณ€๊ฒฝํ–ˆ์„ ๋•Œ ์˜ˆ์ธก ๊ฒฐ๊ณผ์— ๋ฏธ์น˜๋Š” ์˜ํ–ฅ์„ ํƒ๊ตฌํ•˜๋Š” ๊ฒƒ๋„ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค. TIAToolbox ํ”„๋ ˆ์ž„์›Œํฌ์—์„œ ์ œ๊ณตํ•˜๋Š” ์ปค๋ฎค๋‹ˆํ‹ฐ์˜ ๋ชจ๋ธ ๋˜๋Š” ์‚ฌ์šฉ์ž ์ •์˜ ์‚ฌ์ „ ํ•™์Šต๋œ ๋ชจ๋ธ์„ ์‚ฌ์šฉํ•˜์—ฌ, TIAToolbox ๋ชจ๋ธ ํด๋ž˜์Šค์— ์ •์˜๋˜์ง€ ์•Š์€ ๊ตฌ์กฐ์˜ ๋ชจ๋ธ์ด๋ผ๋„ ๋Œ€ํ˜• WSI์— ๋Œ€ํ•ด ์ถ”๋ก ์„ ์ˆ˜ํ–‰ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์‹œ์—ฐํ–ˆ์Šต๋‹ˆ๋‹ค.

๋‹ค์Œ ์ž๋ฃŒ๋ฅผ ํ†ตํ•ด ๋” ๋งŽ์€ ๋‚ด์šฉ์„ ๋ฐฐ์šธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค: