A PyQt desktop application for pixel-level image annotation with Meta's Segment Anything Model (SAM). Generate masks from point, box, or circle prompts; refine them as polygons; and save annotations in Labelme-compatible JSON files.
This project uses the original SAM models (
vit_b,vit_l, andvit_h), not SAM 2.
- SAM-assisted segmentation using positive/negative points, boxes, and circles
- Manual polygon creation and vertex editing
- Multiple mask proposals
- Category and object ID annotation
- Merge and subtract polygon operations
- Polygon simplification
- Hide/show controls for all polygons
- Brightness and contrast preview controls
- Jump-to-image dialog and keyboard navigation
- Labelme-compatible JSON output
- Resumable annotation review sessions with timing and audit metadata
- Original/reviewed JSON, polygon geometry, and raster-mask comparison
- Duplicate-safe CSV review export
- Optional STCN-based video annotation workflow
- CUDA, Apple MPS, and CPU device selection
Python 3.8 or newer is required. A virtual environment is recommended.
Windows PowerShell:
python -m venv annotator_env
.\annotator_env\Scripts\Activate.ps1Linux or macOS:
python3 -m venv annotator_env
source annotator_env/bin/activateInstall the build appropriate for your operating system and accelerator using the official PyTorch installer.
For a CPU-only installation:
python -m pip install torch torchvisionpython -m pip install --upgrade pip
python -m pip install -r requirements.txtpython annotator.py --app_resolution 1000,1600 --model_type vit_b --keep_input_size True --max_size 720The --app_resolution value is height,width.
| Argument | Description |
|---|---|
--model_type |
SAM checkpoint type: vit_b, vit_l, or vit_h |
--keep_input_size |
Keep the original image size for SAM when True |
--max_size |
Maximum input dimension when resizing is enabled |
Click Load SAM to download the selected checkpoint automatically. The
checkpoint is saved in the repository root as vit_b.pth, vit_l.pth, or
vit_h.pth.
You can instead download a checkpoint manually from the official SAM repository and place it in the repository root using the corresponding filename.
vit_b is the smallest and fastest option. vit_l and vit_h require more
memory but use larger model backbones.
- Click Category File and select a text file containing one category per
line, such as
categories.txt. - Click Image Directory and select a folder containing
.jpgor.pngimages. - Click Save Directory and select the output folder.
- Click Load SAM.
- Select a prompt mode and annotate the image.
- Choose one of the mask proposals and click Accept.
- Edit the resulting polygon if needed, then save the annotation.
The application writes one Labelme-compatible JSON file per image.
- Point Prompt: left-click for positive points and right-click for negative points.
- Box Prompt: draw a bounding box around the object.
- Circle Prompt: draw a circular prompt around the object.
- Manual Polygons: click along the object boundary to create a polygon.
- Edit Polygons: select and move polygon vertices. Arrow keys nudge the selected polygon.
- Merge Polygons: select a source polygon, activate Merge, then select the target.
- Subtract Polygons: select the polygon to subtract, activate Subtract, then select the target.
Review mode reuses the image annotator and polygon canvas to inspect existing Labelme JSON annotations. Original annotations are treated as read-only input; all reviewed JSON files, session state, and audit exports are written to a separate output directory.
The application uses the original SAM models for interactive segmentation.
The SAM2 propagated frame option described below is provenance metadata for
annotations generated by a separate SAM2 propagation workflow.
Create three separate directories:
dataset/
|-- images/
| |-- sequence-1/
| | |-- 000001.jpg
| | |-- 000002.jpg
| | `-- 000003.jpg
|-- original-json/
| `-- sequence-1/
| |-- 000001.json
| `-- 000003.json
`-- reviewed-json/
The review output directory must already exist and must not be the original annotation directory or a directory inside it. Using a separate output directory per reviewer is recommended when reviewers work concurrently.
Images and annotations are discovered recursively. Supported images are
.jpg, .jpeg, and .png. They are matched by their case-insensitive relative
path without the extension; for example,
images/sequence-1/000001.jpg matches
original-json/sequence-1/000001.json.
Images without matching JSON files are reported as warnings and remain available as view-only temporal context. Orphan JSON files, duplicate relative keys, invalid JSON, malformed polygon points, unreadable images, and dimension mismatches are also reported during validation. Errors block the session; warnings can be reviewed and accepted.
python annotator.py --review --app_resolution 1000,1600 --model_type vit_b --keep_input_size True --max_size 720The startup dialog requests:
- reviewer ID (required);
- reviewer role (optional and remembered for the next session);
- default provenance for newly created review targets;
- image directory;
- original annotation directory;
- reviewed annotation output directory.
The fields can be prefilled from the command line:
python annotator.py --review \
--reviewer-id "reviewer-01" \
--reviewer-role "grader" \
--review-images "/path/to/images" \
--review-annotations "/path/to/original-json" \
--review-output "/path/to/reviewed-json"Use quoted Windows paths in PowerShell or Git Bash when a directory contains spaces.
| Review argument | Description |
|---|---|
--review |
Open the review-session configuration dialog |
--reviewer-id |
Prefill the required reviewer identifier |
--reviewer-role |
Prefill the optional reviewer role |
--review-images |
Prefill the image directory |
--review-annotations |
Prefill the read-only original JSON directory |
--review-output |
Prefill the reviewed JSON and session-output directory |
- Validate the directories and accept any understood warnings.
- Inspect the image and its loaded annotation. A previously reviewed JSON in the output directory takes precedence when a session is resumed.
- Optionally edit polygons manually or load SAM for prompt-assisted changes.
- Select the annotation provenance.
- Add an optional problem status or reviewer note.
- Record one decision:
- No change: discard unsaved edits and copy the original JSON exactly;
- Minor correction: save the current annotation as a reviewed JSON;
- Major correction: save the current annotation as a reviewed JSON;
- Unable to review: record the outcome without requiring a new JSON.
- The app saves the audit record and advances to the next unfinished review target.
- Use Finish review session only when the session should no longer resume.
Closing the application does not complete a session. Notes, provenance, timer state, decisions, and current progress are persisted in SQLite. Reopening the same reviewer and directory configuration resumes the active session at the first in-progress or unreviewed target. A completed session is never resumed; the same configuration starts a new session instead.
The active timer starts automatically on an annotated review target. Pause and Resume exclude interruptions, while Reset timer resets only the current target after confirmation. Context images pause the timer automatically and disable review decisions.
Decision shortcuts submit the displayed decision, save or copy the annotation, and advance. Navigation shortcuts never complete a target. Shortcuts are suppressed while typing in notes or another editable field.
| Control | Review-mode action |
|---|---|
Page Up / Last Image |
Previous image, including context images |
Page Down / Next Image |
Next image, including context images |
J / Jump |
Select any image by index or filename |
K |
No change / OK; save and advance |
M |
Minor correction; save and advance |
L |
Major correction; save and advance |
O |
Unable to review; record and advance |
Space |
Pause or resume the active review timer |
Alt+Left / Previous target |
Previous matched target; navigation only |
Alt+Right / Next target |
Next matched target; navigation only |
| Export review CSV | Rewrite the current session CSV from SQLite |
The progress display counts annotation targets only, not view-only context
images. Reviewed includes the four explicit decisions, Opened but undecided
means in_progress, and Not yet opened means unreviewed. After at least five
completed targets, the approximate remaining time uses the median completed
target time so long interruptions do not dominate the estimate.
| Category | Meaning |
|---|---|
Unknown / legacy |
Source provenance is unavailable |
Manual keyframe |
Annotation was created manually on a selected keyframe |
SAM2 propagated frame |
Annotation came from an external SAM2 propagation step |
Reviewed propagated frame |
A propagated annotation was reviewed or corrected |
Provenance is stored per review target and can be changed when revisiting an item. The configuration-dialog default is applied only to new targets; resumed targets preserve their stored per-item values.
Every completed decision records:
- decision status and active review time;
- reviewer ID, role, notes, and problem status;
- original and reviewed JSON SHA-256 hashes;
- whether the exact JSON file changed;
- whether polygon geometry changed;
- whether the rasterised foreground mask changed;
- annotation provenance;
- start, completion, creation, and update timestamps;
- original, reviewed, and image paths.
Geometry comparison ignores polygon-list order, polygon start-point rotation, and reversed polygon direction. The exact JSON hash still detects formatting, metadata, label, or path changes. Raster comparison detects changes to the combined foreground pixels independently of JSON formatting.
Review mode creates these files in the review output directory:
reviewed-json/
|-- review_session.sqlite3
|-- review_session_<session-uuid>.csv
`-- sequence-1/
|-- 000001.json
`-- 000003.json
SQLite is the authoritative resumable record. The session-specific CSV is rewritten atomically at session startup, after each decision, on manual export, and when the session is completed. Repeated exports update existing rows rather than appending duplicates. The CSV also retains inactive history if the dataset changes during an active session.
Close the CSV in Excel while reviewing. Windows may lock an open CSV and block replacement; the app will warn about the failed export while preserving the SQLite record.
- Never use the original JSON directory as the review output.
- Back up both the reviewed JSON files and
review_session.sqlite3. - Do not edit the SQLite database manually while the annotator is running.
- Keep reviewer output directories separate unless shared reviewed JSON files are explicitly intended.
- Use Finish review session deliberately; merely closing the app preserves the active session for resumption.
- Finishing a partial session leaves
in_progressandunreviewedstatuses unchanged but prevents that session from resuming automatically.
| Shortcut | Action |
|---|---|
Shift+I |
Choose image directory |
Shift+L |
Choose save directory |
Page Up |
Previous image |
Page Down |
Next image |
J |
Jump to an image |
N or Ctrl+W |
Manual polygon mode |
P |
Point prompt mode |
B |
Box prompt mode |
C |
Circle prompt mode |
A |
Accept the selected proposal |
R |
Reject proposals and clear prompts |
1–4 |
Select a mask proposal |
E |
Edit polygon mode |
| Arrow keys | Nudge a selected polygon |
D |
Delete selected polygons |
H |
Hide/show all polygons |
Shift+M |
Merge polygons |
Shift+S |
Subtract polygons |
Shift+R |
Reduce polygon points |
U |
Undo the last point |
Ctrl+U |
Undo the last shape edit |
S |
Save |
Alt+S |
Save as |
Ctrl + mouse wheel |
Zoom |
Esc |
Cancel an active merge or subtract operation |
Video annotation additionally requires
STCN and its stcn.pth checkpoint:
segment-anything-annotator-v2/
├── STCN/
├── stcn.pth
├── annotator_video.py
└── ...
Organize extracted video frames as:
video_folder/
├── video_1/
│ ├── 00000.jpg
│ ├── 00001.jpg
│ └── ...
└── video_2/
└── ...
Start the video annotator with:
python annotator_video.py --app_resolution 1000,1600 --model_type vit_b --keep_input_size True --max_size 720 --max_size_STCN 600Annotate the first frame with SAM, select objects with Ctrl + left-click, add
them to memory, and use Propagate on subsequent frames.
| Shortcut | Video action |
|---|---|
N |
Next frame |
B |
Previous frame |
E |
Edit mode |
A |
Accept proposal |
R |
Reject proposal |
D |
Delete |
S |
Save |
Space |
Propagate |
1–3 |
Select a mask proposal |
- Use a current graphics driver and the matching PyTorch build for CUDA.
- PowerShell may require permission to activate a virtual environment. See
Microsoft's documentation for
Set-ExecutionPolicyif activation is blocked. - The application also runs on CPU when CUDA is unavailable.
- Apple Silicon systems use PyTorch's MPS backend when it is available.
- The application falls back to CPU if MPS is unavailable.
This project uses opencv-python-headless because the GUI is provided by
PyQt5. The headless OpenCV package avoids a conflict between OpenCV's bundled
Qt plugins and PyQt5's xcb plugin.
Older versions of this project also installed metaseg, which depends on the
GUI-enabled OpenCV package. When updating an existing environment, remove that
unused dependency and both OpenCV variants before reinstalling:
python -m pip uninstall -y metaseg opencv-python opencv-python-headless
python -m pip install -r requirements.txtAs a temporary workaround without reinstalling, point Qt to PyQt5's plugins:
export QT_PLUGIN_PATH="$(python -c 'import PyQt5, pathlib; print(pathlib.Path(PyQt5.__file__).parent / "Qt5" / "plugins")')"If xcb still fails after using the headless OpenCV package, install the Qt/XCB
system libraries supplied by your Linux distribution.
Run the complete automated test suite from the repository root:
python -m unittest discover -s tests -vThe tests cover review path validation, resumable SQLite storage, JSON and geometry comparison, raster-mask comparison, provenance, duplicate-safe CSV export, application icon assets, and the complete review audit workflow.
If you use Segment Anything Annotator & Reviewer in your research, please cite the specific software version used. The citation for version 1.0.0 is:
@software{namour_2026_21726098,
author = {Namour, Adriana and
Bergeles, Christos},
title = {Segment Anything Annotator \& Reviewer},
month = jul,
year = 2026,
publisher = {Zenodo},
version = {v1.0.0},
doi = {10.5281/zenodo.21726098},
url = {https://doi.org/10.5281/zenodo.21726098},
}For other releases, use the repository's Cite this repository link or the Zenodo record for all versions.
This repository is based on haochenheheda/segment-anything-annotator. It builds on Meta Segment Anything, Labelme, and STCN.
See LICENSE for licensing information.
