bookmark/README.md
pyr0ball f9553373d6 docs: drop AI/ML terminology in README, document marker ordering
- README "How it works" step 3: "not by any AI/ML step" -> "not by any
  model inference step", per AGENTS.md's terminology rule (no generic
  "AI" when describing system capability).
- AppConfig gets a docstring spelling out that markers must be ordered
  smallest-bucket-boundary first, since determine_size_bucket trusts
  this order and a misordered config would silently produce a wrong
  (tax-relevant) size bucket. AppConfig.load() also now validates that
  marker area is non-decreasing across the list and raises ValueError
  if not, since this project's nested physical markers get larger as
  bucket size increases.
2026-07-13 11:22:22 -07:00

124 lines
5.2 KiB
Markdown

# Bookmark
A camera-driven book intake and checkout inventory system, built for
[Books in Hand](https://git.opensourcesolarpunk.com/books-in-hand), a
nonprofit that keeps books out of landfills and in people's hands.
Bookmark replaces manual book logging with a guided, camera-based workflow:
a volunteer places a book on a marked platform, the system photographs the
front and back, sorts it into a size category for tax/reporting purposes,
and later backfills the catalogue with title, author, and ISBN — all
running on local hardware, no cloud dependency.
This project is fully open source and is being built and donated as
volunteer/client work — it isn't part of any commercial product line.
## Status
**Phase 1 (intake station) is built, unit-tested, and ready for real-hardware validation.** Phase 2 (batch OCR/ISBN backfill) and Phase 3 (checkout station) are planned for later.
| Phase | What it does | Status |
|---|---|---|
| 1 — Intake station | Camera capture, guided front/back flow, deterministic size bucketing, CSV catalogue | Built, unit-tested — pending real-hardware validation |
| 2 — Batch OCR/ISBN backfill | Barcode decode + local vision-LLM fallback to fill in title/author/ISBN | Planned |
| 3 — Checkout station | Track books leaving inventory, reusing the intake identification pipeline | Planned |
## How it works (Phase 1)
1. A downward-facing webcam looks at a fixed platform with printed
nested size-boundary markers (small / medium / large).
2. A volunteer centers the book; capture fires automatically or on a
button press (configurable).
3. The system photographs the front, determines size — **deterministically**,
by checking which physical markers are covered, not by any model
inference step — then prompts the volunteer to flip the book and
photographs the back.
4. Every book gets a row in a CSV catalogue: images, size bucket, and a
status that later pipeline stages (OCR backfill, checkout) update.
Full design rationale lives in `AGENTS.md` (also readable as `CLAUDE.md`
same file, symlinked for compatibility with different AI coding tools).
## Why this matters for a nonprofit
- **No cloud costs, no vendor lock-in.** Everything runs on donated
hardware; no per-book or per-scan fees.
- **Deterministic where it counts.** Size-bucket counts feed Books in
Hand's year-end tax and 501(c)(3) reporting — that number is computed
from physical measurements, not inferred by a model, so it can't drift
or hallucinate.
- **Volunteer-friendly.** The intake flow is designed to be usable by
volunteers with no technical background — guided steps, big buttons,
no login required.
## Development
Requires Python 3.11+.
### Setup
1. Create and activate a virtual environment:
```bash
python3 -m venv .venv
.venv/bin/pip install -e ".[dev]"
```
2. Mount the USB webcam over the capture platform. See the design spec
(`circuitforge-plans/books-in-hand/superpowers/specs/2026-07-13-inventory-system-design.md`)
for physical setup details: nested size-boundary markers, guide box.
3. Run calibration once per physical install, with the platform empty:
```bash
.venv/bin/python calibrate.py <camera_device_index> reference.jpg
```
This captures a reference image of the empty platform for marker detection.
4. Copy `config.example.json` to `config.json` and fill in:
- `markers`: pixel coordinates (from the reference image)
- `guide_box`: capture region boundaries
- `trigger_strategy`: one of `manual`, `motion_stop`, or `stable_centered`
5. Run the backend:
```bash
.venv/bin/python run.py config.json
```
6. Open `http://<machine-ip>:8000` in a kiosk-mode browser on the station's monitor.
### Running tests
```bash
.venv/bin/pytest -v
```
All tests run without requiring real camera hardware — they use synthetic
NumPy frames and mocked `cv2.VideoCapture`.
### End-to-end smoke test (manual, requires real hardware)
This checklist is for whoever installs this system on real hardware. It
verifies that the intake workflow functions end-to-end.
1. Boot the station per Setup above with `trigger_strategy: "manual"`.
2. Place a book on the platform, click "Capture" — confirm the status
advances to "Front captured — flip it over" and a new row appears in
`catalogue.csv` with a `size_bucket` value and populated
`front_wide_path` / `front_cropped_path` columns pointing at real files
under `captures/<book_id>/`.
3. Flip the book, click "Capture" again — confirm status advances to
"Book complete!" and the same row now has `back_wide_path` /
`back_cropped_path` populated.
4. Click "Next Book" — confirm status returns to "Waiting for book..." and
the next capture gets a different `book_id`.
5. Repeat with a small book and a large book — confirm `size_bucket`
differs correctly between them, matching which physical markers each
book actually covers.
6. Switch `trigger_strategy` to `motion_stop` in `config.json`, restart,
and confirm placing a book and holding it still triggers capture
without pressing the button.
7. Switch to `stable_centered` and repeat, confirming capture fires once
the book is centered and steady in the guide box.
## License
MIT — see `LICENSE`.