Skip to content
Draft
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
13 changes: 13 additions & 0 deletions docs/acknowledgements.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Acknowledgements

SST was developed by the [Imageomics Informatics Team](https://imageomics.org/).

The [Imageomics Institute](https://imageomics.org/) is supported by the National Science Foundation under [Award No. 2118240](https://www.nsf.gov/awardsearch/showAward?AWD_ID=2118240) "HDR Institute: Imageomics: A New Frontier of Biological Information Powered by Knowledge-Guided Machine Learning." Any opinions, findings and conclusions or recommendations expressed in this material are those of the author(s) and do not necessarily reflect the views of the National Science Foundation.

## Datasets

Test datasets used in development include:

- Ananda STRI Images (Ananda Browne)
- Cambridge WingSeg dataset
- Rosser-Lawrence multispectral dataset
33 changes: 33 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# SST — Static Segmentation by Tracking

## Frustratingly Label-Efficient Segmentation for Biological Specimens

[![GitHub](https://img.shields.io/badge/GitHub-Imageomics%2FSST-blue)](https://github.com/Imageomics/SST)

SST is a pipeline for propagating reference segmentation masks across a set of images using SAM2’s video tracking capabilities. It was designed for biological image analysis workflows where a small number of manually annotated reference masks can be propagated to a large set of target images.

## Package Purpose

SST addresses the challenge of segmenting recurring structures — such as butterfly wings — across large collections of specimen images. Rather than annotating every image individually, a researcher annotates a small number of reference frames and SST propagates those masks to all remaining images in the group.

The pipeline was developed as part of the [LepidopteraLens](https://github.com/Imageomics/LepidopteraLens) workflow for automated trait extraction from pinned Lepidoptera specimens.

## Key Concepts

- **Reference frames** — images for which you provide manually annotated masks
- **Target frames** — images SST propagates masks to automatically
- **Grouping** — images are grouped (e.g. by species) so propagation happens within visually similar sets; within-species grouping produces significantly better results than cross-species

## Limitations

- Reference mask quality is the upper bound on predicted mask quality — incomplete annotations propagate to all targets
- SAM2 automatic mask generation is the bottleneck for multi-specimen images
- Reference frame selection uses a fixed random seed internally and is not user-controlled

## Getting Started

To get started with SST, see the [Quick Reference](quickstart.md) guide.

---

> **Note:** Documentation should only be published after PyPI packaging is complete. See [issue #22](https://github.com/Imageomics/SST/issues/22).
23 changes: 23 additions & 0 deletions docs/installation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Installation

## Requirements

- Python 3.10+
- macOS or Linux (Apple Silicon supported)

## Install SST

```bash
git clone https://github.com/Imageomics/SST.git
cd SST
pip install -e .
```

## macOS-specific note

On Apple Silicon Macs, set `num_workers=0` in `inference.py` to avoid a multiprocessing pickling error:

```python
# inference.py
num_workers = 0
```
32 changes: 32 additions & 0 deletions docs/io.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Input/Output Format Specifications

## Inputs

### Image files
- Format: JPEG or PNG (8-bit); PPM also supported
- For calibrated specimen images, use files with the `_calibrated` suffix

### Reference masks (`--support_mask`)
- Format: greyscale PNG, 8-bit
- Same spatial dimensions as the corresponding image
- Pixel values:

| Value | Wing |
|-------|------|
| 0 | Background |
| 1 | Right forewing |
| 2 | Left forewing |
| 3 | Right hindwing |
| 4 | Left hindwing |

### CSV file
- Columns: `image_path`, `group`, `mask_path`
- `mask_path` is empty for target frames
- Minimum 2 reference masks per group required

## Outputs

### Predicted masks
- Location: `<output_dir>/pred_masks/`
- Format: greyscale PNG, same pixel value convention as input masks
- One file per target image, named `<input_filename>.png`
40 changes: 40 additions & 0 deletions docs/quickstart.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Quick Reference: Running SST from Existing Masks

This guide assumes you already have reference masks. If you need to create them, see [Generating Reference Masks](reference-mask-tools/index.md).

## 1. Prepare your CSV

Create a CSV file that groups your images. Each row is one image; the group column controls which images are propagated together. SST requires a minimum of 2 reference masks per group.

```
image_path,group,mask_path
images/110_BOQ_D.jpg,BOQ,masks/110_BOQ_D_mask.png
images/111_BOQ_D.jpg,BOQ,masks/111_BOQ_D_mask.png
images/113_BOQ_D.jpg,BOQ,
```

Rows with a `mask_path` are treated as reference frames; rows without are targets.

## 2. Run inference

```bash
python inference.py --csv your_data.csv --output_dir output/
```

## 3. Check outputs

Results are written to `output/pred_masks/`. Each predicted mask is a greyscale PNG where pixel values correspond to wing IDs:

| Value | Wing |
|-------|------|
| 1 | Right forewing |
| 2 | Left forewing |
| 3 | Right hindwing |
| 4 | Left hindwing |
| 0 | Background |

## Notes

- Within-species grouping produces significantly better results than cross-species
- Reference mask quality is the upper bound on predicted mask quality — incomplete annotations propagate to all targets
- Reference frame selection uses `seed=0` internally, so it is deterministic but not user-controlled
69 changes: 69 additions & 0 deletions docs/reference-mask-tools/fiji.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# Generating a Reference Mask with Fiji

Fiji is a distribution of ImageJ with plugins pre-installed. It uses the ROI Manager to collect polygon selections for each wing, then a macro fills them into a blank 8-bit image and saves it as PNG. No Python script is needed.

## Installation

1. Go to [https://imagej.net/software/fiji/downloads](https://imagej.net/software/fiji/downloads)
2. Download the **macOS arm64** version
3. Unzip and drag Fiji to your Applications folder
4. Launch Fiji from Applications

## Usage

### 1. Open and prepare the image

1. Open your butterfly image: **File → Open**
2. Convert to 8-bit: **Image → Type → 8-bit**

### 2. Trace wings with the ROI Manager

1. Open the ROI Manager: **Analyze → Tools → ROI Manager**
2. Select the **Polygon** tool from the Fiji toolbar
3. Trace the right forewing, then press **t** to add it to the ROI Manager
4. Double-click the ROI in the manager and rename it `right_forewing`
5. Repeat for each wing:
- `left_forewing`
- `right_hindwing`
- `left_hindwing`

### 3. Run the export macro

1. Open the macro editor: **Plugins → Macros → Edit**
2. Paste the following macro, updating the output path:

```
names = newArray("right_forewing", "left_forewing", "right_hindwing", "left_hindwing");
values = newArray(1, 2, 3, 4);

w = getWidth();
h = getHeight();
newImage("mask", "8-bit black", w, h, 1);

for (i = 0; i < names.length; i++) {
idx = -1;
for (j = 0; j < roiManager("count"); j++) {
roiManager("select", j);
if (Roi.getName() == names[i]) {
idx = j;
j = roiManager("count");
}
}
if (idx >= 0) {
roiManager("select", idx);
setForegroundColor(values[i], values[i], values[i]);
fill();
}
}

saveAs("PNG", "/Users/sahasra/SST/mask.png");
```

3. Update `/Users/sahasra/SST/mask.png` to your desired output path
4. Click **Run**

## Notes

- The output is a uint8 PNG with values 0–4, directly usable as SST's `--support_mask`
- No Python conversion step is needed — the macro handles everything natively
- SST results validated clean across all seven calibrated species using this workflow
20 changes: 20 additions & 0 deletions docs/reference-mask-tools/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Generating Reference Masks

SST requires at least 2 manually annotated reference masks per group before it can run inference. This section documents four tools you can use to create them.

All tools must produce a greyscale PNG where pixel values encode wing identity:

| Value | Wing |
|-------|------|
| 0 | Background |
| 1 | Right forewing |
| 2 | Left forewing |
| 3 | Right hindwing |
| 4 | Left hindwing |

| Tool | Approach | Export format |
|------|----------|---------------|
| [napari](napari.md) | Python script with interactive Labels layer | PNG (direct) |
| [X-AnyLabeling](x-anylabeling.md) | GUI polygon annotation | JSON converted to PNG |
| [Fiji](fiji.md) | ROI Manager + macro | PNG (direct via macro) |
| [Label Studio](label-studio.md) | Web UI with optional SAM2 backend | Brush annotation exported as PNG |
60 changes: 60 additions & 0 deletions docs/reference-mask-tools/label-studio.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Generating a Reference Mask with Label Studio

Label Studio provides a web-based annotation interface with optional SAM2 ML backend integration for interactive segmentation.

## Installation

```bash
pip install label-studio
label-studio start
```

Label Studio runs at `http://localhost:8080`.

## Project setup

Create a new project and use the following labeling interface config under **Settings → Labeling Interface → Custom template**:

```xml
<View>
<Image name="image" value="$image"/>
<BrushLabels name="tag" toName="image">
<Label value="right_forewing" background="#FF0000"/>
<Label value="left_forewing" background="#00FF00"/>
<Label value="right_hindwing" background="#0000FF"/>
<Label value="left_hindwing" background="#FFFF00"/>
</BrushLabels>
<KeyPointLabels name="tag2" toName="image" smart="true">
<Label value="Foreground" smart="true" background="#FFaa00" showInline="true"/>
<Label value="Background" smart="true" background="#00aaFF" showInline="true"/>
</KeyPointLabels>
<RectangleLabels name="tag3" toName="image" smart="true">
<Label value="Foreground" smart="true" background="#FFaa00" showInline="true"/>
</RectangleLabels>
</View>
```

## Manual brush annotation

Brush annotation works without any ML backend:

1. Open a task in your project
2. Select a wing label (e.g. `right_forewing`) from the label panel
3. Use the **Brush** tool to paint over that wing
4. Repeat for each of the four wings
5. Click **Submit** to save the annotation

## SAM2 ML backend (optional)

A SAM2 backend can be connected for interactive prompting:

1. Clone the backend repo: `git clone https://github.com/HumanSignal/label-studio-ml-backend`
2. Follow the SAM2 backend setup instructions in that repo
3. Start the backend on port 9090
4. In Label Studio go to **Settings → Machine Learning → Add Model** and enter `http://localhost:9090`

> **Known issue:** Label Studio Community Edition sends `"context": null` to ML backends instead of passing click coordinates. This means interactive prompting (keypoint/rectangle → SAM2 mask) does not currently work. Brush annotation works normally. Resolution requires either a Docker-based backend setup or a GPU backend on Cardinal.

## Current status

Setup complete. Manual brush annotation is functional. Interactive SAM2 prompting is pending resolution of the `context: null` issue.
60 changes: 60 additions & 0 deletions docs/reference-mask-tools/napari.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Generating a Reference Mask with napari

`napari_annotate.py` opens a butterfly specimen image as a napari Labels layer and lets you trace each wing using the polygon tool, assigning integers 1–4 for right forewing, left forewing, right hindwing, and left hindwing — matching the YOLO class convention. A docked Save button casts the Labels array from `int32` to `uint8` and writes it as a greyscale PNG via `imageio.imwrite`, producing exactly the format SST's `--support_mask` expects. The `PushButton` plus `add_dock_widget` pattern also makes this script a direct structural prototype for the future `napari-sst` plugin.

## Installation

```bash
pip install "napari[all]==0.7.0" imageio scikit-image magicgui
```

## Script

Save the following as `napari_annotate.py` in your SST directory:

```python
import numpy as np
import imageio.v3 as iio
from skimage import io
import napari
from magicgui.widgets import PushButton

IMAGE_PATH = "your_image.jpg" # change this
OUTPUT_PATH = "mask.png" # change this

image = io.imread(IMAGE_PATH)
viewer = napari.Viewer()
viewer.add_image(image, name="butterfly")
labels_layer = viewer.add_labels(
np.zeros(image.shape[:2], dtype=np.int32),
name="wings"
)

def save_mask():
mask = labels_layer.data.astype(np.uint8)
iio.imwrite(OUTPUT_PATH, mask)
print(f"Saved mask to {OUTPUT_PATH}")

btn = PushButton(label="Save Mask")
btn.clicked.connect(save_mask)
viewer.window.add_dock_widget(btn, area="right")
napari.run()
```

## Usage

1. Edit `IMAGE_PATH` and `OUTPUT_PATH` at the top of the script
2. Run: `python napari_annotate.py`
3. In the viewer, select the **Labels** layer
4. Choose the **polygon** tool from the toolbar
5. Set the label value to the wing you are tracing:
- `1` = right forewing
- `2` = left forewing
- `3` = right hindwing
- `4` = left hindwing
6. Trace each wing, then click **Save Mask** in the right dock panel

## Notes

- The Save button writes a uint8 PNG with values 0–4, directly usable as SST's `--support_mask`
- Within-species propagation (e.g. CAM→CAM) produces clean results; cross-species propagation shows expected degradation consistent with SST's general behaviour
Loading