Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,16 @@

PrePATH is a comprehensive preprocessing toolkit for whole slide images (WSI), built upon [CLAM](https://github.com/mahmoodlab/CLAM) and [ASlide](https://github.com/MrPeterJin/ASlide).

## TODO
## TODO

- H0-mini
- OpenMidnight
- TITAN (Slide level)

## Installation

### Prerequisites

- Anaconda or Miniconda
- `openslide-tools` (system dependency)

Expand All @@ -49,6 +51,7 @@ wget https://github.com/birkhoffkiki/GPFM/releases/download/ckpt/GPFM.pth
```

**Notes:**

- ASlide should be installed as a Python package from [GitHub](https://github.com/MrPeterJin/ASlide) and is included in `requirements/gpfm.txt`.
- Environment configurations for other foundation models should be referenced from their respective repositories.

Expand All @@ -63,6 +66,10 @@ Extract coordinates of foreground patches from whole slide images:
bash scripts/get_coors/SAL/sal.sh
```

> ⚠ Note: The get_coord script provides an auto_size option to automatically adapt to different level-0 WSI magnifications, generating coordinates equivalent to a 256×256 FOV at 20× (see PrePATH/configs/resolution.py for customization).

> ⚠ Note: The same option is also available for crop_image scripts. Both are disabled by default. If enabled (by appending the `--auto_size` sign), make sure to turn it on in both scripts to ensure consistent coordinate–crop alignment.

### Step 2: Feature Extraction

Extract patch-level features using the selected foundation model:
Expand Down Expand Up @@ -106,7 +113,7 @@ models="resnet50 gpfm"
| MUSK | `musk` | [HuggingFace](https://huggingface.co/xiangjx/musk) |
| OmiCLIP | `omiclip` | [Github](https://github.com/GuangyuWangLab2021/Loki) |
| PathoCLIP | `pathoclip` | [Github](https://github.com/wenchuan-zhang/patho-r1) |
---
-------------------------
Copy link

Copilot AI Feb 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The table separator has inconsistent formatting. The row contains only dashes with no pipe characters, which breaks the markdown table structure. It should be removed or properly formatted as a standard table row with pipe delimiters.

Suggested change
-------------------------

Copilot uses AI. Check for mistakes.

## Supported WSI Formats

Expand All @@ -116,4 +123,3 @@ PrePATH supports the following whole slide image formats:
- **SDPC** (.sdpc)
- **TRON** (.tron)
- All formats supported by OpenSlide (including .svs, .tiff, .ndpi, .vms, .vmu, .scn, .mrxs, .tif, .bif, and others)

62 changes: 47 additions & 15 deletions create_patches_fp.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ def seg_and_patch(
stitch=False,
patch=False,
auto_skip=True,
auto_size=False,
process_list=None,
wsi_format="svs",
):
Expand Down Expand Up @@ -152,20 +153,26 @@ def seg_and_patch(
full_path = os.path.join(source, slide)
try:
WSI_object = wsi_slide_image(full_path)
mpp = WSI_object.mpp
if mpp is None:
# Fallback: assume 40x magnification (mpp=0.25) for files without MPP metadata
print(f"WARNING: MPP information not available for {slide}. Assuming 40x magnification (mpp=0.25).")
mpp = 0.25
object_power = 40
if auto_size:
mpp = WSI_object.mpp
if mpp is None:
# Fallback: assume 40x magnification (mpp=0.25) for files without MPP metadata
print(f"WARNING: MPP information not available for {slide}. Assuming 40x magnification (mpp=0.25).")
mpp = 0.25
object_power = 40
else:
# Convert mpp to magnification
object_power = int(round(10.0 / mpp))
patch_size, step_size = adjust_size(object_power)
print("#" * 100)
print("levels:", WSI_object.wsi.level_dimensions)
print("mpp: {:.3f}, object_power: {}x, patch_size: {}, step_size: {} (auto-adjusted)".format(mpp, object_power, patch_size, step_size))
print("#" * 100)
else:
# Convert mpp to magnification
object_power = int(round(10.0 / mpp))
patch_size, step_size = adjust_size(object_power)
print("#" * 100)
print("levels:", WSI_object.wsi.level_dimensions)
print("mpp: {:.3f}, object_power: {}x, patch_size: {}, step_size: {}".format(mpp, object_power, patch_size, step_size))
print("#" * 100)
print("#" * 100)
print("levels:", WSI_object.wsi.level_dimensions)
print("Using fixed patch_size: {}, step_size: {}".format(patch_size, step_size))
print("#" * 100)
except Exception as e:
print("Failed to reading:", full_path)
print('Exception:', e)
Expand Down Expand Up @@ -321,6 +328,7 @@ def mp_seg_and_patch(
stitch=False,
patch=False,
auto_skip=True,
auto_size=False,
process_list=None,
wsi_format="svs",
):
Expand Down Expand Up @@ -373,6 +381,29 @@ def func(i):
full_path = os.path.join(source, slide)
try:
WSI_object = wsi_slide_image(full_path)
# Auto-adjust patch_size and step_size based on mpp if auto_size is enabled
current_patch_size = patch_size
current_step_size = step_size
if auto_size:
mpp = WSI_object.mpp
if mpp is None:
# Fallback: assume 40x magnification (mpp=0.25) for files without MPP metadata
print(f"WARNING: MPP information not available for {slide}. Assuming 40x magnification (mpp=0.25).")
mpp = 0.25
object_power = 40
else:
# Convert mpp to magnification
object_power = int(round(10.0 / mpp))
current_patch_size, current_step_size = adjust_size(object_power)
print("#" * 100)
print("levels:", WSI_object.wsi.level_dimensions)
print("mpp: {:.3f}, object_power: {}x, patch_size: {}, step_size: {} (auto-adjusted)".format(mpp, object_power, current_patch_size, current_step_size))
print("#" * 100)
else:
print("#" * 100)
print("levels:", WSI_object.wsi.level_dimensions)
print("Using fixed patch_size: {}, step_size: {}".format(current_patch_size, current_step_size))
print("#" * 100)
except:
print("Failed to reading:", full_path)
return
Expand Down Expand Up @@ -472,7 +503,7 @@ def func(i):

patch_time_elapsed = -1 # Default time
if patch:
current_patch_params.update({"patch_level": patch_level, "patch_size": patch_size, "step_size": step_size, "save_path": patch_save_dir})
current_patch_params.update({"patch_level": patch_level, "patch_size": current_patch_size, "step_size": current_step_size, "save_path": patch_save_dir})
file_path, patch_time_elapsed = patching(
WSI_object=WSI_object,
**current_patch_params,
Expand Down Expand Up @@ -510,6 +541,7 @@ def func(i):
parser.add_argument("--patch_level", type=int, default=0, help="downsample level at which to patch")
parser.add_argument("--process_list", type=str, default=None, help="name of list of images to process with parameters (.csv)")
parser.add_argument("--wsi_format", type=str, default="svs")
parser.add_argument("--auto_size", default=False, action="store_true", help="automatically adjust patch_size and step_size based on WSI magnification (mpp)")
parser.add_argument("--use_mp", default=False, action="store_true")


Expand Down Expand Up @@ -566,4 +598,4 @@ def func(i):
fn = mp_seg_and_patch
else:
fn = seg_and_patch
seg_times, patch_times = fn(**directories, **parameters, patch_size=args.patch_size, step_size=args.step_size, seg=args.seg, use_default_params=False, save_mask=True, stitch=args.stitch, patch_level=args.patch_level, patch=args.patch, process_list=process_list, auto_skip=args.no_auto_skip, wsi_format=wsi_format)
seg_times, patch_times = fn(**directories, **parameters, patch_size=args.patch_size, step_size=args.step_size, seg=args.seg, use_default_params=False, save_mask=True, stitch=args.stitch, patch_level=args.patch_level, patch=args.patch, process_list=process_list, auto_skip=args.no_auto_skip, auto_size=args.auto_size, wsi_format=wsi_format)
Copy link

Copilot AI Feb 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When use_mp is True, mp_seg_and_patch is called but it does not return any values. The function is missing a return statement (similar to seg_and_patch which returns seg_times and patch_times at line 309). This will cause a TypeError when trying to unpack None into seg_times and patch_times. The mp_seg_and_patch function needs to be updated to return timing values.

Copilot uses AI. Check for mistakes.
Loading