Skip to content
cccskills
FreePublished on GitHub

bio-imaging-mass-cytometry-interactive-annotation

Label cell types in imaging mass cytometry images using napari, an image viewer, and marker overlays. Export labeled cells as training data, extend labels to similar cells, and generate image panels for annotation review.

OriginalShow the original description

Interactive cell type annotation for IMC data. Covers napari-based annotation, marker-guided labeling, training data generation, and annotation validation. Use when manually annotating cell types for training classifiers or validating automated phenotyping results.

Run score Not tested

See how to install

Good for

  • Manually labeling cell types in IMC images
  • Creating training data for cell classifiers
  • Extending manual labels to unlabeled cells
  • Reviewing automated cell classification results

Files included (3)

  • SKILL.md10.4 KB
  • examples/napari_annotation.py4.0 KB
  • usage-guide.md1.9 KB

SKILL.md (original)

Review the instructions the agent will receive before you install.

Version Compatibility

Reference examples tested with: matplotlib 3.8+, numpy 1.26+, pandas 2.2+, scikit-learn 1.4+

Before using code patterns, verify installed versions match. If versions differ:

  • Python: pip show <package> then help(module.function) to check signatures

If code throws ImportError, AttributeError, or TypeError, introspect the installed package and adapt the example to match the actual API rather than retrying.

Interactive Annotation

"Manually annotate cell types in my IMC data" → Interactively label cells using napari visualization with marker overlays for training classifiers or validating automated phenotyping results.

  • Python: napari.Viewer() with label layer for interactive annotation

Napari-Based Annotation

import napari
import numpy as np
from skimage import io
import pandas as pd

# Load IMC image stack
image_stack = io.imread('imc_image.tiff')  # (C, H, W)
segmentation_mask = io.imread('cell_segmentation.tiff')

# Create napari viewer
viewer = napari.Viewer()

# Add channels as separate layers for visualization
channel_names = ['CD45', 'CD3', 'CD68', 'panCK', 'DNA']
for i, name in enumerate(channel_names):
    viewer.add_image(image_stack[i], name=name, visible=False, colormap='gray', blending='additive')

# Add segmentation
viewer.add_labels(segmentation_mask, name='Cells')

# Add annotation layer (start empty)
annotation_layer = viewer.add_labels(
    np.zeros_like(segmentation_mask),
    name='Cell_Types'
)

# Define cell types
cell_type_mapping = {1: 'T_cell', 2: 'Macrophage', 3: 'Epithelial', 4: 'Stromal', 5: 'Other'}

Marker-Guided Annotation

def create_marker_overlay(image_stack, channel_indices, colors):
    '''Create RGB overlay of selected markers for easier annotation.'''
    h, w = image_stack.shape[1:]
    overlay = np.zeros((h, w, 3), dtype=np.float32)

    for idx, color in zip(channel_indices, colors):
        channel = image_stack[idx].astype(np.float32)
        channel = (channel - channel.min()) / (channel.max() - channel.min() + 1e-8)
        for c, weight in enumerate(color):
            overlay[:, :, c] += channel * weight

    overlay = np.clip(overlay, 0, 1)
    return overlay

# Create T cell overlay (CD3=green, CD45=blue)
t_cell_overlay = create_marker_overlay(
    image_stack,
    channel_indices=[0, 1],  # CD45, CD3
    colors=[[0, 0, 1], [0, 1, 0]]  # Blue, Green
)

# Create tumor overlay (panCK=red)
tumor_overlay = create_marker_overlay(
    image_stack,
    channel_indices=[3],  # panCK
    colors=[[1, 0, 0]]  # Red
)

# Add overlays to viewer
viewer.add_image(t_cell_overlay, name='T_cell_markers', visible=True)
viewer.add_image(tumor_overlay, name='Tumor_markers', visible=False)

Training Data Generation

def extract_training_data(image_stack, segmentation_mask, annotation_mask, channel_names):
    '''Extract mean marker intensities per cell with annotations.'''
    from skimage.measure import regionprops_table

    cells = []
    for cell_id in np.unique(segmentation_mask):
        if cell_id == 0:
            continue

        cell_mask = segmentation_mask == cell_id
        annotation = annotation_mask[cell_mask]
        annotation = annotation[annotation > 0]

        if len(annotation) == 0:
            continue

        cell_type = int(np.median(annotation))

        cell_data = {'cell_id': cell_id, 'cell_type': cell_type}
        for i, name in enumerate(channel_names):
            cell_data[name] = np.mean(image_stack[i][cell_mask])

        cells.append(cell_data)

    return pd.DataFrame(cells)

# After manual annotation in napari
annotation_data = annotation_layer.data
training_df = extract_training_data(image_stack, segmentation_mask, annotation_data, channel_names)
training_df.to_csv('training_annotations.csv', index=False)
print(f'Annotated {len(training_df)} cells')
print(training_df['cell_type'].value_counts())

Semi-Automated Annotation

Goal: Propagate a small set of manual cell type annotations to all unannotated cells using marker expression similarity.

Approach: Train a k-nearest-neighbors classifier on manually annotated cells' marker intensities, predict labels for remaining cells, and report classification confidence to flag uncertain assignments for review.

from sklearn.neighbors import KNeighborsClassifier
from sklearn.preprocessing import StandardScaler

def propagate_annotations(training_df, all_cells_df, marker_columns):
    '''Use annotated cells to classify unannotated cells.'''
    X_train = training_df[marker_columns].values
    y_train = training_df['cell_type'].values

    scaler = StandardScaler()
    X_train_scaled = scaler.fit_transform(X_train)

    knn = KNeighborsClassifier(n_neighbors=5)
    knn.fit(X_train_scaled, y_train)

    unannotated = all_cells_df[~all_cells_df['cell_id'].isin(training_df['cell_id'])]
    X_test = scaler.transform(unannotated[marker_columns].values)

    predictions = knn.predict(X_test)
    probabilities = knn.predict_proba(X_test)
    confidence = np.max(probabilities, axis=1)

    unannotated = unannotated.copy()
    unannotated['predicted_type'] = predictions
    unannotated['confidence'] = confidence

    return unannotated

marker_cols = ['CD45', 'CD3', 'CD68', 'panCK']
predictions = propagate_annotations(training_df, all_cells_df, marker_cols)

high_conf = predictions[predictions['confidence'] > 0.8]
print(f'{len(high_conf)} cells classified with high confidence')

Annotation Validation

def validate_annotations(annotation_df, image_stack, segmentation_mask, channel_names, output_dir):
    '''Generate validation plots for manual review.'''
    import matplotlib.pyplot as plt
    from pathlib import Path

    Path(output_dir).mkdir(exist_ok=True)

    cell_types = annotation_df['cell_type'].unique()

    for ct in cell_types:
        cells = annotation_df[annotation_df['cell_type'] == ct]
        n_sample = min(20, len(cells))
        sample_cells = cells.sample(n_sample)

        fig, axes = plt.subplots(n_sample, len(channel_names), figsize=(2*len(channel_names), 2*n_sample))

        for i, (_, cell) in enumerate(sample_cells.iterrows()):
            cell_mask = segmentation_mask == cell['cell_id']
            bbox = get_bounding_box(cell_mask, padding=10)

            for j, ch_name in enumerate(channel_names):
                ax = axes[i, j] if n_sample > 1 else axes[j]
                crop = image_stack[j][bbox[0]:bbox[1], bbox[2]:bbox[3]]
                ax.imshow(crop, cmap='gray')
                ax.axis('off')
                if i == 0:
                    ax.set_title(ch_name)

        plt.suptitle(f'Cell Type: {ct} (n={len(cells)})')
        plt.tight_layout()
        plt.savefig(f'{output_dir}/validation_type_{ct}.png', dpi=150)
        plt.close()

def get_bounding_box(mask, padding=10):
    rows = np.any(mask, axis=1)
    cols = np.any(mask, axis=0)
    rmin, rmax = np.where(rows)[0][[0, -1]]
    cmin, cmax = np.where(cols)[0][[0, -1]]
    rmin = max(0, rmin - padding)
    cmin = max(0, cmin - padding)
    rmax = min(mask.shape[0], rmax + padding)
    cmax = min(mask.shape[1], cmax + padding)
    return rmin, rmax, cmin, cmax

validate_annotations(training_df, image_stack, segmentation_mask, channel_names, 'validation/')

Napari Plugin Interface

from magicgui import magicgui
from napari.types import LabelsData

@magicgui(call_button='Apply Annotation')
def annotate_selected(viewer: napari.Viewer, cell_type: int = 1):
    '''Annotate selected cells with specified type.'''
    labels_layer = viewer.layers['Cells']
    annotation_layer = viewer.layers['Cell_Types']

    selected = labels_layer.selected_label
    if selected > 0:
        mask = labels_layer.data == selected
        annotation_layer.data[mask] = cell_type
        annotation_layer.refresh()
        print(f'Annotated cell {selected} as type {cell_type}')

@magicgui(call_button='Export Annotations')
def export_annotations(viewer: napari.Viewer, filename: str = 'annotations.csv'):
    '''Export current annotations to CSV.'''
    annotation_layer = viewer.layers['Cell_Types']
    segmentation_layer = viewer.layers['Cells']

    annotations = []
    for cell_id in np.unique(segmentation_layer.data):
        if cell_id == 0:
            continue
        cell_mask = segmentation_layer.data == cell_id
        cell_type = annotation_layer.data[cell_mask]
        cell_type = cell_type[cell_type > 0]
        if len(cell_type) > 0:
            annotations.append({'cell_id': cell_id, 'cell_type': int(np.median(cell_type))})

    pd.DataFrame(annotations).to_csv(filename, index=False)
    print(f'Exported {len(annotations)} annotations to {filename}')

# Add widgets to viewer
viewer.window.add_dock_widget(annotate_selected)
viewer.window.add_dock_widget(export_annotations)

Batch Annotation Workflow

def batch_annotation_session(image_files, seg_files, existing_annotations=None):
    '''Set up batch annotation session for multiple images.'''
    viewer = napari.Viewer()

    all_annotations = existing_annotations or {}

    for img_file, seg_file in zip(image_files, seg_files):
        image_stack = io.imread(img_file)
        seg_mask = io.imread(seg_file)

        sample_name = Path(img_file).stem

        for layer in list(viewer.layers):
            viewer.layers.remove(layer)

        for i, name in enumerate(channel_names):
            viewer.add_image(image_stack[i], name=name, visible=False)

        viewer.add_labels(seg_mask, name='Cells')

        if sample_name in all_annotations:
            viewer.add_labels(all_annotations[sample_name], name='Cell_Types')
        else:
            viewer.add_labels(np.zeros_like(seg_mask), name='Cell_Types')

        viewer.title = f'Annotating: {sample_name}'
        input('Press Enter when done annotating this image...')

        all_annotations[sample_name] = viewer.layers['Cell_Types'].data.copy()

    return all_annotations

Related Skills

  • cell-segmentation - Generate cell masks for annotation
  • phenotyping - Automated phenotyping as alternative
  • spatial-analysis - Use annotations for spatial analysis
  • quality-metrics - QC annotated data

Reviews

No reviews yet. Tell others what you thought.

Comments

No login needed. Share how it worked for you, ask a question, or note where it did not run.

No comments yet. Be the first to write one.

Comments are published right away. Do not include personal information such as email addresses or phone numbers. Up to 2 URLs.

0 / 1,000 characters

This form is protected by reCAPTCHA, and the Google Privacy Policy and Terms of Service apply.

More skills from this repository

adaptyv

FreeSummary

Support protein design with cloud laboratory testing for binding, expression, thermal stability, and enzyme activity. Covers sequence optimization, submitting experiments through the Adaptyv API, checking progress, and downloading experimental results.

  • Validate designed proteins with laboratory assays
  • Screen and redesign sequences for better solubility
  • Submit protein testing experiments through an API
FreedomIntelligence/OpenClaw-Medical-Skills3.1KUpdated Jul 21, 2026

Build flexible daily plans that account for ADHD-related difficulties with time estimation and getting started. Narrow priorities to three tasks, add transition buffers, and choose small next actions that match the energy available that day.

  • Choosing three priorities for today
  • Planning a schedule with transition buffers
  • Finding a small first step when starting feels difficult
FreedomIntelligence/OpenClaw-Medical-Skills3.1KUpdated Jul 21, 2026

aeon

FreeSummary

Build Python workflows for data recorded over time, including classification, forecasting, anomaly detection, and pattern search. Using aeon, the skill guides preprocessing, algorithm selection, feature extraction, and evaluation through interfaces compatible with scikit-learn.

  • Classify time series by their patterns
  • Forecast future values from historical data
  • Detect anomalies and change points
FreedomIntelligence/OpenClaw-Medical-Skills3.1KUpdated Jul 21, 2026

agent-browser

FreeSummary

Use a browser to research topics, extract page information, fill forms, and test websites. Guides agents through identifying page controls, interacting with web apps, saving screenshots or PDFs, and reusing saved login sessions.

  • Researching topics and reading web articles
  • Filling and submitting online forms
  • Extracting page text and links
FreedomIntelligence/OpenClaw-Medical-Skills3.1KUpdated Jul 21, 2026

ai-analyzer

FreeSummary

Combine health measurements, exercise, sleep, nutrition, and mental health records to examine trends, unusual changes, and estimated risks. The skill produces personalized suggestions and text or interactive HTML reports using local data without external API calls.

  • Review health records together in a comprehensive report
  • Explore links between sleep, mood, exercise, and weight
  • Summarize estimated disease and nutrition risks
FreedomIntelligence/OpenClaw-Medical-Skills3.1KUpdated Jul 21, 2026

alphafold

FreeSummary

Predict structures for designed protein sequences and protein complexes with AlphaFold2 or ColabFold. Inspect structure files and confidence scores to assess folding and chain interfaces, with guidance for local runs, Modal execution, and common prediction errors.

  • Check predicted folding of a designed protein sequence
  • Predict a binder–target protein complex
  • Review structure and interface confidence scores
FreedomIntelligence/OpenClaw-Medical-Skills3.1KUpdated Jul 21, 2026

See all skills by FreedomIntelligence

Report a problem with this skill