Skip to content

Add FastSAM-based oyster counting & measurement pipeline - #1

Open
sr320 wants to merge 1 commit into
mainfrom
add-oyster-vision-pipeline
Open

Add FastSAM-based oyster counting & measurement pipeline#1
sr320 wants to merge 1 commit into
mainfrom
add-oyster-vision-pipeline

Conversation

@sr320

@sr320 sr320 commented Jul 8, 2026

Copy link
Copy Markdown
Member

What & why

Adds a vision pipeline that counts oysters and measures each one's length and width in mm from overhead tray photos, automating the manual ImageJ workflow (place a point per oyster, measure with calipers) captured in the reference data.

Because the dataset is a single labelled image, the pipeline uses a pretrained FastSAM "segment everything" model (no task-specific training) plus a domain-specific filter, measurement, and calibration layer.

How it works (scripts/oyster_vision.py)

  1. Segment every object with FastSAM within the tray ROI.
  2. Filter to oysters by size / solidity / extent / aspect ratio, then de-duplicate overlapping masks with mask-IoU NMS.
  3. Measure: length = maximum Feret (caliper) diameter of the mask; width = short side of the min-area rectangle.
  4. Calibrate px→mm with a single constant fit (least squares through origin) against the human measurements.

CLIs

  • scripts/calibrate.py — extracts ground truth from the ImageJ TIFF (POINT overlay = 84 human crosshairs), matches detections, fits px_per_mm, writes outputs/calibration.json + outputs/metrics.json.
  • scripts/detect.py — runs any image → per-oyster predictions CSV + annotated overlay.

Results — reference image bag380 (84 oysters)

Metric Value
Counted 80 / 84
Detection P / R / F1 0.90 / 0.86 / 0.88
Length error MAE 4.6 mm (4.8%), r 0.82
Width error MAE 4.7 mm (7.9%), r 0.73
Calibration 2.435 px/mm

Reviewer notes

  • FastSAM weights (~24 MB) auto-download to models/ on first run and are git-ignored.
  • Calibration is scene-specific — valid for this camera height/tray; new photos need a fiducial or fixed geometry (see METHODS.md).
  • Known misses: ~12 mud-caked oysters that blend into the board; ~8 false positives (board texture + the 380 tag). Improvement paths documented in METHODS.md.
  • Also removed a stray macOS .DS_Store from tracking and added it to .gitignore.

🤖 Generated with Claude Code

Automates the manual ImageJ workflow (point per oyster + caliper
measurement) for overhead tray photos.

Pipeline (scripts/oyster_vision.py):
- Segment every object with pretrained FastSAM (no task-specific training,
  suits a single labelled image)
- Filter masks to oyster instances by size/solidity/extent/aspect within a
  board ROI, de-duplicate with mask-IoU NMS
- Measure length = max Feret diameter, width = min-area-rect short side
- Convert px->mm with a single calibration constant

CLIs:
- calibrate.py derives px/mm and validation metrics from the ImageJ-annotated
  reference image (POINT overlay + mm table)
- detect.py runs image -> predictions CSV + annotated overlay

Reference image (bag380, 84 oysters): counted 80, detection P/R/F1
0.90/0.86/0.88, length MAE 4.6mm (r 0.82), width MAE 4.7mm (r 0.73),
calibration 2.435 px/mm. Metrics and outputs written to outputs/.

Docs in README.md and METHODS.md, including the scene-specific calibration
caveat and next steps for recall/false-positive improvements.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant