Lightweight WSI tile extraction and preprocessing. Use for basic slide processing, tissue detection, tile extraction, and stain normalization for H&E images. Best for simple pipelines, dataset preparation, and quick tile-based analysis. For advanced spatial proteomics, multiplexed imaging, or deep learning pipelines use pathml.
uv pip install histolab
histolab.data, also install pooch:uv pip install pooch
from histolab.slide import Slide
from histolab.tiler import RandomTiler
# Load slide
slide = Slide("slide.svs", processed_path="output/")
# Configure tiler
tiler = RandomTiler(
tile_size=(512, 512),
n_tiles=100,
level=0,
seed=42
)
# Preview tile locations
tiler.locate_tiles(slide, n_tiles=20)
# Extract tiles
tiler.extract(slide)
Slidereferences/slide_management.md contains comprehensive documentation on:from histolab.slide import Slide
from histolab.data import prostate_tissue
# Load sample data
prostate_svs, prostate_path = prostate_tissue()
# Initialize slide
slide = Slide(prostate_path, processed_path="output/")
# Inspect properties
print(f"Dimensions: {slide.dimensions}")
print(f"Levels: {slide.levels}")
print(f"Magnification: {slide.properties.get('openslide.objective-power')}")
# Save thumbnail to processed_path
from pathlib import Path
Path(slide.processed_path).mkdir(parents=True, exist_ok=True)
slide.thumbnail.save(Path(slide.processed_path) / f"{slide.name}_thumbnail.png")
TissueMask, BiggestTissueBoxMask, BinaryMaskreferences/tissue_masks.md contains comprehensive documentation on:locate_mask()from histolab.masks import TissueMask, BiggestTissueBoxMask
# Create tissue mask for all tissue regions
tissue_mask = TissueMask()
# Visualize mask on slide
slide.locate_mask(tissue_mask)
# Get mask array
mask_array = tissue_mask(slide)
# Use largest tissue region (default for most extractors)
biggest_mask = BiggestTissueBoxMask()
TissueMask: Multiple tissue sections, comprehensive analysisBiggestTissueBoxMask: Single main tissue section, exclude artifacts (default)BinaryMask: Specific ROI, exclude annotations, custom segmentationn_tiles, seed for reproducibilitypixel_overlap for sliding windowsscorer (NucleiScorer, CellularityScorer, custom)tile_size: Tile dimensions (e.g., (512, 512))level: Pyramid level for extraction (0 = highest resolution)check_tissue: Filter tiles by tissue contenttissue_percent: Minimum tissue coverage (default 80%)extraction_mask: Mask defining extraction regionreferences/tile_extraction.md contains comprehensive documentation on:locate_tiles()from histolab.tiler import RandomTiler, GridTiler, ScoreTiler
from histolab.scorer import NucleiScorer
# Random sampling (fast, diverse)
random_tiler = RandomTiler(
tile_size=(512, 512),
n_tiles=100,
level=0,
seed=42,
check_tissue=True,
tissue_percent=80.0
)
random_tiler.extract(slide)
# Grid coverage (comprehensive)
grid_tiler = GridTiler(
tile_size=(512, 512),
level=0,
pixel_overlap=0,
check_tissue=True
)
grid_tiler.extract(slide)
# Score-based selection (most informative)
score_tiler = ScoreTiler(
tile_size=(512, 512),
n_tiles=50,
scorer=NucleiScorer(),
level=0
)
score_tiler.extract(slide, report_path="tiles_report.csv")
# Preview tile locations on thumbnail
tiler.locate_tiles(slide, n_tiles=20)
RgbToGrayscale, RgbToHsv, RgbToHedOtsuThreshold, AdaptiveThresholdStretchContrast, HistogramEqualizationBinaryDilation, BinaryErosionBinaryOpening, BinaryClosingRemoveSmallObjects, RemoveSmallHolesCompose: Create filter pipelinesreferences/filters_preprocessing.md contains comprehensive documentation on:from histolab.filters.compositions import Compose
from histolab.filters.image_filters import RgbToGrayscale, OtsuThreshold
from histolab.filters.morphological_filters import (
BinaryDilation, RemoveSmallHoles, RemoveSmallObjects
)
# Standard tissue detection pipeline
tissue_detection = Compose([
RgbToGrayscale(),
OtsuThreshold(),
BinaryDilation(disk_size=5),
RemoveSmallHoles(area_threshold=1000),
RemoveSmallObjects(area_threshold=500)
])
# Use with custom mask
from histolab.masks import TissueMask
custom_mask = TissueMask(filters=tissue_detection)
# Apply filters to tile
from histolab.tile import Tile
filtered_tile = tile.apply_filters(tissue_detection)
MacenkoStainNormalizer, ReinhardStainNormalizerfrom histolab.stain_normalizer import MacenkoStainNormalizer, ReinhardStainNormalizer
from PIL import Image
target = Image.open("reference_stain.png") # Style reference slide/tile
source = Image.open("slide_to_normalize.png")
normalizer = MacenkoStainNormalizer()
normalizer.fit(target)
normalized = normalizer.transform(source)
normalized.save("normalized.png")
ReinhardStainNormalizer() for Reinhard color transfer. Fit on a representative target image, then transform source tiles or thumbnails. See references/filters_preprocessing.md for filter-based alternatives.references/visualization.md contains comprehensive documentation on:locate_mask()locate_tiles()import matplotlib.pyplot as plt
from histolab.masks import TissueMask
# Display slide thumbnail
plt.figure(figsize=(10, 10))
plt.imshow(slide.thumbnail)
plt.title(f"Slide: {slide.name}")
plt.axis('off')
plt.show()
# Visualize tissue mask
tissue_mask = TissueMask()
slide.locate_mask(tissue_mask)
# Preview tile locations
tiler = RandomTiler(tile_size=(512, 512), n_tiles=50)
tiler.locate_tiles(slide, n_tiles=20)
# Display extracted tiles in grid
from pathlib import Path
from PIL import Image
tile_paths = list(Path("output/tiles/").glob("*.png"))[:16]
fig, axes = plt.subplots(4, 4, figsize=(12, 12))
axes = axes.ravel()
for idx, tile_path in enumerate(tile_paths):
tile_img = Image.open(tile_path)
axes[idx].imshow(tile_img)
axes[idx].set_title(tile_path.stem, fontsize=8)
axes[idx].axis('off')
plt.tight_layout()
plt.show()
from histolab.slide import Slide
from histolab.tiler import RandomTiler
from pathlib import Path
import logging
# Enable logging for progress tracking
logging.basicConfig(level=logging.INFO)
# Load slide
slide = Slide("slide.svs", processed_path="output/random_tiles/")
# Inspect slide
print(f"Dimensions: {slide.dimensions}")
print(f"Levels: {slide.levels}")
Path(slide.processed_path).mkdir(parents=True, exist_ok=True)
slide.thumbnail.save(Path(slide.processed_path) / f"{slide.name}_thumbnail.png")
# Configure random tiler
random_tiler = RandomTiler(
tile_size=(512, 512),
n_tiles=100,
level=0,
seed=42,
check_tissue=True,
tissue_percent=80.0
)
# Preview locations
random_tiler.locate_tiles(slide, n_tiles=20)
# Extract tiles
random_tiler.extract(slide)
from histolab.slide import Slide
from histolab.tiler import GridTiler
from histolab.masks import TissueMask
# Load slide
slide = Slide("slide.svs", processed_path="output/grid_tiles/")
# Use TissueMask for all tissue sections
tissue_mask = TissueMask()
slide.locate_mask(tissue_mask)
# Configure grid tiler
grid_tiler = GridTiler(
tile_size=(512, 512),
level=1, # Use level 1 for faster extraction
pixel_overlap=0,
check_tissue=True,
tissue_percent=70.0
)
# Preview grid
grid_tiler.locate_tiles(slide)
# Extract all tiles
grid_tiler.extract(slide, extraction_mask=tissue_mask)
from histolab.slide import Slide
from histolab.tiler import ScoreTiler
from histolab.scorer import NucleiScorer
import pandas as pd
import matplotlib.pyplot as plt
# Load slide
slide = Slide("slide.svs", processed_path="output/scored_tiles/")
# Configure score tiler
score_tiler = ScoreTiler(
tile_size=(512, 512),
n_tiles=50,
level=0,
scorer=NucleiScorer(),
check_tissue=True
)
# Preview top tiles
score_tiler.locate_tiles(slide, n_tiles=15)
# Extract with report
score_tiler.extract(slide, report_path="tiles_report.csv")
# Analyze scores
report_df = pd.read_csv("tiles_report.csv")
plt.hist(report_df['score'], bins=20, edgecolor='black')
plt.xlabel('Tile Score')
plt.ylabel('Frequency')
plt.title('Distribution of Tile Scores')
plt.show()
from pathlib import Path
from histolab.slide import Slide
from histolab.tiler import RandomTiler
import logging
logging.basicConfig(level=logging.INFO)
# Configure tiler once
tiler = RandomTiler(
tile_size=(512, 512),
n_tiles=50,
level=0,
seed=42,
check_tissue=True
)
# Process all slides
slide_dir = Path("slides/")
output_base = Path("output/")
for slide_path in slide_dir.glob("*.svs"):
print(f"\nProcessing: {slide_path.name}")
# Create slide-specific output directory
output_dir = output_base / slide_path.stem
output_dir.mkdir(parents=True, exist_ok=True)
# Load and process slide
slide = Slide(slide_path, processed_path=output_dir)
# Save thumbnail for review
Path(slide.processed_path).mkdir(parents=True, exist_ok=True)
slide.thumbnail.save(Path(slide.processed_path) / f"{slide.name}_thumbnail.png")
# Extract tiles
tiler.extract(slide)
print(f"Completed: {slide_path.name}")
from histolab.slide import Slide
from histolab.masks import TissueMask
from histolab.tiler import RandomTiler
from histolab.filters.compositions import Compose
from histolab.filters.image_filters import RgbToGrayscale, OtsuThreshold
from histolab.filters.morphological_filters import (
BinaryDilation, RemoveSmallObjects, RemoveSmallHoles
)
# Define custom filter pipeline for aggressive artifact removal
aggressive_filters = Compose([
RgbToGrayscale(),
OtsuThreshold(),
BinaryDilation(disk_size=10),
RemoveSmallHoles(area_threshold=5000),
RemoveSmallObjects(area_threshold=3000) # Remove larger artifacts
])
# Create custom mask
custom_mask = TissueMask(filters=aggressive_filters)
# Load slide and visualize mask
slide = Slide("slide.svs", processed_path="output/")
slide.locate_mask(custom_mask)
# Extract with custom mask
tiler = RandomTiler(tile_size=(512, 512), n_tiles=100)
tiler.extract(slide, extraction_mask=custom_mask)
slide.thumbnail.save() for quick visual reviewlocate_mask() before extractionTissueMask for multiple sections, BiggestTissueBoxMask for single sectionslocate_tiles() before extractingtissue_percent threshold (70-90% typical)BiggestTissueBoxMask over TissueMask when appropriatetissue_percent to reduce invalid tile attemptsn_tiles for initial explorationpixel_overlap=0 for non-overlapping gridspixel_overlap for sliding window approachestissue_percent thresholdcheck_tissue=Truetissue_percent thresholdn_tiles for RandomTiler/ScoreTilerMacenkoStainNormalizer or ReinhardStainNormalizertissue_percent per staining qualityreferences/ directory: