Metadata-Version: 2.4
Name: fullseye
Version: 1.1.0
Summary: Numpy-native operator library for vision, 3-D and measurement (3,838 typed ops in the machine-readable index: imaging, point clouds, optics, tomography, metrology) with an evolutionary pipeline designer and honest holdout gating
Author-email: Kazufumi Furuse <kazufumi@furuse.work>
License: Apache-2.0
Project-URL: Homepage, https://github.com/furuse-kazufumi/fullseye
Project-URL: Repository, https://github.com/furuse-kazufumi/fullseye
Project-URL: Documentation, https://furuse.work/
Project-URL: Operator corpus (RAG), https://github.com/furuse-kazufumi/fullseye/tree/master/docs/ops
Project-URL: Bug tracker, https://github.com/furuse-kazufumi/fullseye/issues
Project-URL: Validation reports, https://github.com/furuse-kazufumi/fullseye/issues/new?template=real_validation_report.yml
Keywords: image-processing,computer-vision,evolutionary-algorithm,pipeline,operators
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Science/Research
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Scientific/Engineering :: Image Processing
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Classifier: Topic :: Scientific/Engineering :: Visualization
Classifier: Topic :: Multimedia :: Graphics :: 3D Modeling
Classifier: Typing :: Typed
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
License-File: NOTICE
License-File: AUTHORS
Requires-Dist: numpy>=1.23
Requires-Dist: scipy>=1.10
Requires-Dist: threadpoolctl>=3.1
Provides-Extra: opencv
Requires-Dist: opencv-python>=4.8.1.78; extra == "opencv"
Provides-Extra: skimage
Requires-Dist: scikit-image>=0.20; extra == "skimage"
Provides-Extra: pil
Requires-Dist: Pillow>=13.0; extra == "pil"
Provides-Extra: wavelets
Requires-Dist: PyWavelets>=1.4; extra == "wavelets"
Provides-Extra: gpu
Requires-Dist: torch>=2.13; extra == "gpu"
Requires-Dist: kornia>=0.9; extra == "gpu"
Requires-Dist: cupy-cuda12x>=14; platform_system != "Darwin" and extra == "gpu"
Provides-Extra: gi
Requires-Dist: mitsuba>=3.5; extra == "gi"
Provides-Extra: extra
Requires-Dist: mahotas>=1.4; extra == "extra"
Requires-Dist: SimpleITK>=2.2; extra == "extra"
Provides-Extra: gui
Requires-Dist: PySide6>=6.5; extra == "gui"
Provides-Extra: handpose
Requires-Dist: mediapipe>=0.10; extra == "handpose"
Provides-Extra: video
Requires-Dist: imageio>=2.28; extra == "video"
Requires-Dist: imageio-ffmpeg>=0.4; extra == "video"
Provides-Extra: audio
Requires-Dist: soundfile>=0.12; extra == "audio"
Provides-Extra: volume
Requires-Dist: SimpleITK>=2.2; extra == "volume"
Requires-Dist: tifffile>=2023.1; extra == "volume"
Provides-Extra: raster
Requires-Dist: imageio>=2.28; extra == "raster"
Requires-Dist: tifffile>=2023.1; extra == "raster"
Provides-Extra: gltf
Requires-Dist: pygltflib>=1.16; extra == "gltf"
Provides-Extra: lidar
Requires-Dist: laspy>=2.5; extra == "lidar"
Requires-Dist: lazrs>=0.5; extra == "lidar"
Provides-Extra: pcd
Requires-Dist: pypcd4>=1.4; extra == "pcd"
Provides-Extra: xlsx
Requires-Dist: openpyxl>=3.1; extra == "xlsx"
Provides-Extra: e57
Requires-Dist: pye57>=0.4; extra == "e57"
Provides-Extra: export-verify
Requires-Dist: pandas>=2.0; extra == "export-verify"
Requires-Dist: trimesh>=4.0; extra == "export-verify"
Requires-Dist: numpy-stl>=3.0; extra == "export-verify"
Requires-Dist: plyfile>=1.0; extra == "export-verify"
Requires-Dist: pye57>=0.4; extra == "export-verify"
Requires-Dist: astropy>=7.0; extra == "export-verify"
Requires-Dist: mrcfile>=1.5; extra == "export-verify"
Requires-Dist: pynrrd>=1.1; extra == "export-verify"
Requires-Dist: nibabel>=5.3; extra == "export-verify"
Requires-Dist: tifffile>=2023.1; extra == "export-verify"
Provides-Extra: polygon
Requires-Dist: matplotlib>=3.5; extra == "polygon"
Provides-Extra: threed
Requires-Dist: torch>=2.13; extra == "threed"
Requires-Dist: scikit-image>=0.20; extra == "threed"
Requires-Dist: SimpleITK>=2.2; extra == "threed"
Provides-Extra: serial
Requires-Dist: pyserial>=3.5; extra == "serial"
Provides-Extra: modbus
Requires-Dist: pymodbus>=3.0; extra == "modbus"
Provides-Extra: symbols
Requires-Dist: zxing-cpp>=2.2; extra == "symbols"
Provides-Extra: mqtt
Requires-Dist: paho-mqtt>=1.6; extra == "mqtt"
Provides-Extra: opcua
Requires-Dist: asyncua>=1.0; extra == "opcua"
Provides-Extra: dev
Requires-Dist: pytest>=9.0.3; extra == "dev"
Requires-Dist: pytest-cov>=4; extra == "dev"
Requires-Dist: ruff>=0.4; extra == "dev"
Requires-Dist: pytest-timeout>=2.3; extra == "dev"
Requires-Dist: hypothesis>=6.100; extra == "dev"
Requires-Dist: pytest-randomly>=3.15; extra == "dev"
Requires-Dist: pytest-xdist>=3.5; extra == "dev"
Requires-Dist: pip-audit>=2.7; extra == "dev"
Provides-Extra: all
Requires-Dist: opencv-python>=4.8.1.78; extra == "all"
Requires-Dist: scikit-image>=0.20; extra == "all"
Requires-Dist: Pillow>=13.0; extra == "all"
Requires-Dist: PyWavelets>=1.4; extra == "all"
Requires-Dist: torch>=2.13; extra == "all"
Requires-Dist: kornia>=0.9; extra == "all"
Requires-Dist: mahotas>=1.4; extra == "all"
Requires-Dist: SimpleITK>=2.2; extra == "all"
Requires-Dist: PySide6>=6.5; extra == "all"
Requires-Dist: imageio>=2.28; extra == "all"
Requires-Dist: imageio-ffmpeg>=0.4; extra == "all"
Requires-Dist: tifffile>=2023.1; extra == "all"
Dynamic: license-file

# Fullseye

[![CI](https://github.com/furuse-kazufumi/fullseye/actions/workflows/ci.yml/badge.svg)](https://github.com/furuse-kazufumi/fullseye/actions/workflows/ci.yml)
[![PyPI](https://img.shields.io/pypi/v/fullseye)](https://pypi.org/project/fullseye/)
[![License](https://img.shields.io/badge/license-Apache--2.0-blue)](LICENSE)
[![Docs](https://img.shields.io/badge/docs-furuse.work-informational)](https://furuse.work/)

**Documentation: [furuse.work](https://furuse.work/)** — the operator index (3,838
operators in the machine-readable index, `docs/OP_INDEX.json`: 947 single-input 2-D operators +
17 n-ary + 2,874 in the typed ledgers, each with a per-operator note), the family guides, and the
release notes.

**What it can do: [CAPABILITIES.en.md](docs/CAPABILITIES.en.md)** — an index organised by
*what you want to do*, every entry tied to operators that exist and an example that runs.
**What the PoCs hardened: [HARDENING.en.md](docs/HARDENING.en.md)** — the ledger of defects
the PoC series found, what changed, and which gate now stops each one coming back.

**Real RGB-D 6-DoF PoC:** [`poc_real_tless_6dof`](examples/poc_real_tless_6dof.py)
runs the depth-to-point-cloud, normal, table-removal, clustering, PPF and ICP chain on
BOP T-LESS (CC BY 4.0), scores it with symmetry-aware MSSD/MSPD, and compares it with
the public Drost and Vidal baselines. The dataset is not in the repository: on a machine
without it (`FULLSEYE_TLESS_DATA`, as in CI) the script prints the recorded 2026-11-01 run
instead of re-measuring, so CI checks the record, not the data, and the maturity ledger
does not count it as real-data validation. Dataset citation: Hodaň et al., T-LESS, WACV 2017,
[doi:10.1109/WACV.2017.8](https://doi.org/10.1109/WACV.2017.8).

> **Real-hardware and real-data results wanted.** Fullseye is developed without physical
> measurement rigs, so it is validated against virtual optical systems and simulated ground
> truth. Results from real hardware or real data are very welcome — **your data does not have
> to be shared**: results-only reports are accepted, and `python -m fullseye.validation_kit`
> writes only aggregate numbers from your local data. [Report form](https://github.com/furuse-kazufumi/fullseye/issues/new?template=real_validation_report.yml) ·
> [Discussions › Show and tell](https://github.com/furuse-kazufumi/fullseye/discussions/categories/show-and-tell) ·
> [how reports are judged](docs/VALIDATION_CONTRIBUTING.en.md)

<!-- Banner: real Fullseye outputs only (no mockups). Regenerate with
     `py -3.11 tools/gen_banner.py`. Absolute raw URL for PyPI compatibility. -->
<!-- Animated hero: 6 acts of real operator output (edge orientation, blob
     selection sweep, sub-pixel calipers, SDF -> marching cubes, LiDAR
     clustering, lens defocus). Regenerate with `py -3.11 tools/gen_hero_gif.py`
     (deterministic: same bytes on a re-run). A static first frame lives beside
     it as fullseye_hero_poster.png for renderers that do not animate. -->
[![Fullseye in motion — six acts of real operator output: edge orientation, connected-component selection, sub-pixel calipers, SDF marching cubes, LiDAR clustering, lens defocus](https://raw.githubusercontent.com/furuse-kazufumi/fullseye/master/docs/articles/assets/fullseye_hero.gif)](https://furuse.work/)

*Every frame is real operator output — no mockups. 960x360, 13.5 s, regenerated by `tools/gen_hero_gif.py`.*

![Fullseye banner — a mosaic of real outputs: Itokawa point-cloud curvature, edge orientation, sub-pixel metrology, watershed segmentation, defect heatmap, volumetric X-ray, Frangi filaments, distance transform, bin-picking grasps, event camera, elliptic Fourier fit, LiDAR clustering](https://raw.githubusercontent.com/furuse-kazufumi/fullseye/master/docs/articles/assets/fullseye_banner.png)

**An in-house, numpy-native image-processing operator library and evolutionary
pipeline designer.** Every operator is reimplemented from published algorithms and
open-source libraries (OpenCV, scikit-image, SciPy, Pillow, PyWavelets, SimpleITK,
mahotas, kornia/torch), given a single typed interface, and contract-tested
(finite, deterministic, sort-typed). On top of the operators sits an evolutionary
search that *designs* pipelines and gates them honestly on a held-out set.

Two ways to use it: **apply known operators** (most of the time), or **evolve a new
pipeline** when no single operator solves the task. It is `pip`-installable and works
on plain numpy arrays, so other projects can drop it into a vision pipeline directly.

<!-- Absolute raw URLs on purpose: this README doubles as the PyPI long description,
     where repo-relative image paths break. -->
![Fullseye's own renderer: SDF smooth-union sculpture with AO, soft shadows and ACES tonemap — pure numpy](https://raw.githubusercontent.com/furuse-kazufumi/fullseye/master/docs/articles/assets/render_beauty_hero.png)

*Rendered by Fullseye's numpy renderer (SDF → marching cubes → AO / soft shadows / ACES). More real outputs below.*

> **New here? Read the full introduction.** A single long-form article walks through
> the whole system — the three layers, the Studio IDE, the honest-evaluation
> discipline, and a museum of worked exhibits, all illustrated with real operator output.
> **[English](https://github.com/furuse-kazufumi/fullseye/blob/master/docs/articles/fullseye_overview_qiita_en.md)** ·
> **[日本語](https://github.com/furuse-kazufumi/fullseye/blob/master/docs/articles/fullseye_overview_qiita_ja.md)**

<!-- trust-block:start -->
<!-- generated by tools/gen_trust_block.py from docs/{test_count,gate_inventory,maturity,op_count_history}.json; edit the data, not this block -->

## How it is checked

None of these numbers is typed by hand: `tools/gen_trust_block.py` renders them from measurement files in `docs/` (`test_count.json`, `gate_inventory.json`, `maturity.json`, `op_count_history.json`), and `tests/test_trust_block.py` fails if this section drifts from them. Every number carries the version and date it was measured at.

**28,758 regression tests** — included in 1.1.0, commit `7df6a9be1f`, counted with `pytest --collect-only` on 2026-11-12 with every optional backend installed (without them, tests for the missing backends are not collected and the count is lower).

### If you add one operator, what checks it

For each tier, one deliberately broken operator was injected and the whole suite run; failing tests were counted (mutation probe). ✓ = a gate catches that breakage, ✗ = **nothing catches it yet**.

- Measurement ①: commit `e97e94bae2`, 2026-10-11, the tree after 0.6.0 (includes unreleased changes).

| tier | measured | bookkeeping gates | behavioural gates | raises | NaN | wrong type | nondeterministic | ignores parameters | no description | English-only description | accepts empty input |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 2-D core (`ops.REGISTRY`) | ① | 27 | 10 | ✓ | ✓ | ✓ | ✓ | ✗ | ✓ | ✓ | ✗ |
| n-ary | ① | 16 | 5 | ✓ | ✓ | ✓ | ✗ | ✗ | ✓ | ✓ | ✗ |
| colour | ① | 28 | 10 | ✓ | ✓ | ✓ | ✓ | ✗ | ✓ | ✓ | ✗ |
| typed ledger + `tb_` bridge | ① | 29 | 16 | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| typed ledger (no bridge) | ① | 17 | 5 | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| 3-D ledger | ① | 20 | 7 | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| facade only | ① | 1 | 1 | ✗ | ✗ | ✗ | ✗ | ✗ | ✓ | ✗ | ✗ |

Bookkeeping gates fire for any new operator, even a correct one, until its note and index entries are regenerated. Behavioural gates = the further distinct tests that fail because of how the operator is broken.

Not measured: plausible but wrong values (distance from ground truth), speed, OS-specific failures, two breakages at once. ✓ means at least one test failed, not that its message explains why.

### Verification stage

The 101 capabilities, split by how they are verified (`docs/maturity.json`, rebuilt by `tools/gen_maturity.py` from the current tree (pyproject version 1.1.0); stage definitions in [MATURITY.md](docs/MATURITY.md)).

| stage | meaning | capabilities |
|---|---|---|
| `research-prototype` | implemented; tests or a running example still missing | 0 |
| `verified-synthetic` | verified automatically against synthetic ground truth | 92 |
| `validated-public-real-data` | an example on public measured data runs in a gate | 9 |
| `validated-hardware` | validated on hardware (only from accepted external reports) | 0 |

Of 500 examples, 496 are actually run by a gate.

### Operator counts by version

| version | date | index total | of which typed ledger | HALCON | PoC | regression tests |
|---|---|---|---|---|---|---|
| 0.1.0 | 2026-08-30 | — | — | 323 | 0 | 6,246 |
| 0.1.8 | 2026-09-05 | — | — | 323 | 0 | 11,518 |
| 0.1.9 | 2026-09-05 | — | — | 979 | 0 | 11,593 |
| 0.1.11 | 2026-09-15 | 918 | 0 | 979 | 121 | 13,178 |
| 0.2.0 | 2026-09-15 | 1,942 | 1,024 | 979 | 121 | 13,272 |
| 0.3.0 | 2026-10-03 | 2,590 | 1,639 | 980 | 187 | 16,675 |
| 0.4.0 | 2026-10-06 | 3,057 | 2,104 | 980 | 214 | 17,424 |
| 0.5.0 | 2026-10-06 | 3,078 | 2,125 | 980 | 217 | 17,489 |
| 0.6.0 | 2026-10-11 | 3,083 | 2,130 | 981 | 218 | 18,109 |
| 0.7.0 | 2026-10-18 | 3,354 | 2,401 | 982 | 248 | 19,935 |
| 0.8.0 | 2026-10-22 | 3,529 | 2,572 | 982 | 275 | 22,073 |
| 0.9.0 | 2026-11-04 | 3,752 | 2,788 | 966 | 297 | 26,045 |
| 1.0.0 | 2026-11-06 | 3,811 | 2,847 | 1,017 | 299 | 27,660 |
| 1.1.0 | 2026-11-12 | 3,838 | 2,874 | 1,017 | 315 | 28,758 |

- The HALCON figure 323 for 0.1.0–0.1.8 is the value recorded at the time; from 0.1.9 it is 979 under a revised count (not 656 new implementations).
- 0.2.0 is the first version whose index includes the typed ledger: 918 → 1,942 is a change in what is counted, not new implementation (the ledger existed from 0.1.6 but sat outside the index).

— = not measured for that version. All versions and column definitions: [OP_COUNT_HISTORY.md](docs/OP_COUNT_HISTORY.md).

<!-- trust-block:end -->

## Three ways in

Fullseye is wide; the hard part is picking the first file to open. These are not new
demos — every command below is an example a gate already runs on each push, so if one
of them breaks, CI goes red.

| Course | For | Run this first (5 min) | Then read (30 min) |
|---|---|---|---|
| **Explainable inspection** | inspection / QA engineers | `py -3.11 examples/poc_solder_fillet_aoi.py` — solder-fillet AOI | `examples/poc_fabric_defect.py` — misses and false alarms counted separately |
| **3-D vision for robotics** | robotics / 3-D metrology | `py -3.11 examples/perception_pipeline.py` — stereo → depth → point cloud → traversability | `examples/grasp_pose.py` — register a cloud to a model, get 6-DoF pose and approach |
| **Physics-based NDT** | X-ray / optics / measurement | `py -3.11 examples/ct_reconstruction.py` — projections → reconstruction → dimensions in mm and a void count | `examples/poc_ct_void_morphology.py` — why one pass/fail number is blind to shape |

Every one of them carries a ground truth and prints the null (do-nothing) result beside
the method. How far each capability is actually verified is a generated ledger:
[`docs/MATURITY.md`](docs/MATURITY.md).

Two more doors, both new since 0.1.11 and both honest about their limits:

| Door | For | Run this first | Status |
|---|---|---|---|
| **From an LLM (MCP)** | Claude Code / Claude Desktop users | `py -3.11 -m fullseye.mcp --demo` — search an op, read its note, load a sample, run a pipeline, get a verdict | PoC. A `full` and a smaller `minimal` tool profile (the tool list is `tools/list`; see docs/MCP.md), strict by default, runs from the wheel (the catalog index and the notes' front matter ship as package data; note bodies fall back to the shipped help HTML). [`docs/MCP.md`](docs/MCP.md) |
| **From C / C++ / C# / Lua** (and Python `ctypes`) | embedding into an existing product | `cargo build --release` in `rust/fullseye_core`, then one example per language in [`rust/fullseye_core/examples/`](rust/fullseye_core/examples/README.md) — all four print the same five lines | A C ABI (`fullseye_abi.h`) with **one generic entry point, `fs_apply`, that reaches the whole 2-D registry** (947 single-input operators, by name + JSON parameters; 0.2.0, the registry runs in an embedded CPython, `cargo build --features embed` — the n-ary 2-D operators and the typed-ledger operators (counts: the index line at the top), whose inputs are not one image or region, are refused with a reason, not yet routed), plus a **5-operator contract** that also has a native Rust route. The contract's job is to find specification bugs by being a second implementation (nine found so far); `fs_apply` always reports which route ran |

## Install

```bash
pip install fullseye        # PyPI (numpy + scipy core)
pip install "fullseye[all]" # + opencv, scikit-image, Pillow, PyWavelets, SimpleITK, kornia/torch, PySide6
# from a checkout: pip install -e .   (per-backend extras: .[opencv] .[skimage] .[gpu] ...)
```

PyPI: <https://pypi.org/project/fullseye/> · Source / issues / operator corpus:
GitHub (linked from the PyPI sidebar). After installing, `fullseye-rag` sets up the
Claude Code RAG skill; `py -3.11 tools/update_fullseye.py` updates a checkout without
touching your environment (see `docs/AI_RAG_GUIDE.md`).

Only numpy and scipy are required; every other backend is optional and only its own
operators are affected when it is absent (graceful degradation). GPU is opt-in.

## Quickstart (programmatic API)

```python
import fullseye, numpy as np

frame = np.asarray(img, np.float64)                 # gray H×W in [0,1] (H×W×3 for color)
edges = fullseye.apply(frame, "sobel_amp")          # numpy in, numpy out
seg   = fullseye.apply(frame, "otsu")               # image → region (binary {0,1})
n     = fullseye.apply(seg,   "count_obj")          # region → feature → a float
out   = fullseye.run_pipeline(frame, ["gaussian", "sobel_amp", "otsu"])          # shared knobs
out   = fullseye.run_pipeline(frame, [("gaussian",0.3,0.5), ("otsu",0.4,0.5)])   # per-stage knobs
fullseye.list_ops(sort="region"); fullseye.op_names()   # discover
fullseye.apply([a, b], "add_image")                      # n-ary ops take a LIST of inputs
fullseye.apply(img, "ncc_locate", template=patch)        # match ops take the template here
fullseye.apply(frame, "otsu", on_error="raise")          # fail-closed (default: sort-valid fallback,
fullseye.fallbacks()                                     #   recorded + one warning per op)
```

> Argument order is `apply(image, name, a, b)` — array first, op name second. Swapped
> arguments raise `TypeError: ... arguments look swapped` (0.1.9+).

`apply(image, name, a=0.5, b=0.5)` and `run_pipeline` take an operator name and two
knobs in `[0, 1]`. Feature operators return a Python float; contour operators a dict.

## Operator library

**3,838 typed operators** (964 2-D in the registry, 17 of them n-ary, + 2,874 in the typed ledgers,
the 3-D ledger among them), covering denoising, smoothing, sharpening,
thresholding/segmentation, morphology, edge/corner/blob detection, distance
transforms, color-space conversion, texture/shape features, contours, and the 3-D
modality (point clouds / meshes / volumes / SDF / 6-DoF pose). Sorts: `image` (gray
`[0,1]`), `color` (RGB), `region` (binary), `feature` (scalar), `contour`, `volume`.

Every operator carries a machine-readable Markdown note under `docs/ops/`
(call form, type contract, HALCON counterpart, references, author/license/version
fingerprint) — a single source of truth that generates the Studio help pages and
doubles as a **retrieval (RAG) corpus for AI coding assistants**: an agent such as
Claude Code can look up operators by contract, chain them by sort, and inspect every
intermediate result. One command installs the bundled Claude Code skill and pins the
corpus path (`py -3.11 tools/setup_claude_rag.py` — see `docs/AI_RAG_GUIDE.md`).
Coverage against HALCON's 2313 operators is measured, not asserted
(`py -3.11 imgevolve.py coverage`).

```bash
py -3.11 imgevolve.py ops --search edge      # search implemented operators
py -3.11 imgevolve.py apply gaussian in.png out.png --a 0.6
py -3.11 imgevolve.py pipeline in.png out.png --ops "gaussian,sobel_amp,otsu"
py -3.11 imgevolve.py coverage               # honest coverage numbers
```

Adding one operator makes evolution, code generation, the catalog, and the
machine-readable index (`docs/OP_INDEX.json`) follow automatically.

**How to call one** — the 3,838 operators are not all reachable the same way. Measured on this
version (`fullseye.list_ops(include_ledger=True)`, then `hasattr` per name):

| way of calling | reaches | what it covers |
|---|---|---|
| `fullseye.apply(img, "name")` / `fullseye.run_pipeline` | the 2-D registry (947 single-input + 17 n-ary, by name) | the evolvable vocabulary |
| `fullseye.op.<name>(img)` | 947 (935 registry + 12 colour) | the same 2-D ops, by attribute |
| `fullseye.ledger.<name>(...)` | all 2,874 typed-ledger ops | everything outside the 2-D registry |
| `fullseye.<name>(...)` directly | only 770 of the 3,838 (767 ledger + 3 registry) | the rest raise `AttributeError` with a pointer to the right entry |

So `fullseye.<name>` is a convenience for a minority of names; when a name is not an attribute of
`fullseye`, call it with `fullseye.apply` (2-D ops, `fullseye.op.<name>`) or `fullseye.ledger.<name>` /
`fullseye.op_run("name")` (ledger ops). `fullseye.list_ops()` lists only the 964 2-D ops by default;
pass `include_ledger=True` for the full 3,838.

![Real outputs of classic 2-D vision operators — edges, segmentation, contour measurement](https://raw.githubusercontent.com/furuse-kazufumi/fullseye/master/docs/articles/assets/vision_ops_montage.png)

## Perception stack (robotics-friendly)

Building blocks that turn frames into geometry and objects — the pieces a robot
needs to perceive, measure, and act. A **sensor-simulation suite** (pseudo-LiDAR,
stereo, event camera / DVS, photometric stereo, TSDF fusion, polarization, focus
stacking) lets you develop and test perception pipelines without hardware:

![Physical-AI sensor simulation suite — pseudo-LiDAR, stereo depth, event camera (DVS), focus stacking, polarization, camera+IMU Kalman fusion](https://raw.githubusercontent.com/furuse-kazufumi/fullseye/master/docs/articles/assets/physical_ai_montage.png)

The **3-D side is where Fullseye differentiates most**: 377 typed 3-D operators
spanning point clouds / meshes / volumes / SDF — 3-D feature descriptors
(SHOT, FPFH, spin images), TSDF fusion, fringe projection, photometric stereo,
superquadric fitting, medial axis, geodesic distance, visual hull, and
boundary-preserving manifold-strict QEM decimation — all pure numpy behind one
typed registry. Real data, real numbers (asteroid 25143 Itokawa, JAXA
Hayabusa / Gaskell shape model):

![Asteroid 25143 Itokawa real point cloud — curvature analysis, ICP self-registration (rot err 0.027°), PCA canonical pose](https://raw.githubusercontent.com/furuse-kazufumi/fullseye/master/docs/articles/assets/itokawa_montage.png)

```python
import fullseye as fs
disp  = fs.disparity_map(left, right, max_disp=16)         # dense stereo (block matching)
Z     = fs.depth_from_disparity(disp, focal=f, baseline=B) # Z = f·B/d
pts   = fs.reproject_to_points(Z, fx=f, fy=f)              # point cloud (N,3)
grid,_= fs.elevation_map(world_pts, cell=0.05)            # 2.5-D terrain heightmap
ok    = fs.traversability(grid, cell=0.05, max_step=0.1)  # foothold / obstacle mask
objs  = fs.segment_objects(frame, threshold="otsu")       # per-object records (geometry + descriptors)
rgb   = fs.colorize_depth(Z); fs.save_ply("cloud.ply", pts)   # visualise / export (no matplotlib)
```

Motion, over time — feed it a real clip:

```python
frames = fs.read_frames("clip.mp4", gray=True, step=2)     # (T,H,W) float64 [0,1] (mp4/gif)
for a, b in fs.frame_pairs(frames):
    u, v = fs.optical_flow_lk(a, b)                         # dense flow; also track_points / motion_*
```

`fs.to_float01(x)` coerces uint8/uint16/bool/PIL/path inputs to float64 `[0,1]`;
`fs.read_frames` / `iter_frames` / `write_video` / `probe` handle video I/O (see
`docs/PERCEPTION_REALDATA.md` for measured results on real footage).

## Evolutionary pipeline design

When the task is "find an algorithm that maximizes metric *M* on my data", evolve one.
Fitness is measured on the **training** split only; a **held-out** split is tracked but
**never selected on**, so the reported generalization is honest rather than a fit to the
evaluation set.

```bash
py -3.11 baseline.py --problem denoise --workdir out/mine     # honest floor first
py -3.11 evolve.py   --problem denoise --workdir out/mine --gens 40 --pop 24
py -3.11 robust.py   --problem denoise --workdir out/mine --seeds 5   # best-of-N, train-selected
```

## Performance (optional GPU batch backend)

The default per-image path uses scipy/OpenCV. A batched `torch` fast path (`accel.py`,
`--device cuda`) accelerates the compute-heavy vectorizable operators. Honest note: on
CPU the batch path speeds up heavy operators (≈1.6–2.2×) but *loses* on trivial
pointwise ops (tensor-conversion overhead); the real win is on GPU, where that overhead
amortizes over large parallelism.

## Fullseye Studio (HDevelop-style IDE)

`fullseye-studio` (or `py -3.11 studio.py` from a checkout) opens the visual
workbench: operator browser with generated help
(2-D and 3-D), pipeline editor with per-stage timing, breakpoints, continue /
run-from-line execution control, a variable window with watch expressions and
right-click inspection, multi-window graphics scriptable from programs
(`dev_open_window` / `dev_set_window` / `dev_set_window_extents`), worked-example
galleries, and a tabbed **Python Editor** that opens any sample as editable, runnable
code (F5, subprocess). Combined with the RAG corpus this is aimed at being an
integrated environment for Physical-AI perception work: the AI writes and runs the
pipeline, and the human inspects what it "sees" in the same windows.

## Academic use

Fullseye is designed to be citable and reproducible: per-operator notes carry real
literature references (no fabricated DOIs), versions are pinned to the code by a
registry fingerprint with a CI drift test, and evaluation follows the honest
held-out discipline above. If you use it in academic work, please cite via
`CITATION.cff`.

**Archival status, stated plainly:** archived on Zenodo from v0.1.11 onwards.
Concept DOI (always resolves to the newest version):
[10.5281/zenodo.22761195](https://doi.org/10.5281/zenodo.22761195); the v0.2.1
version DOI is 10.5281/zenodo.22851588 (v0.2.0: 10.5281/zenodo.22772164). Each GitHub Release mints a new version
DOI (`.zenodo.json` describes the record and is kept in step with `CITATION.cff`
and `pyproject.toml` by a CI gate — see "Zenodo" in `CONTRIBUTING.md`). Versions
before 0.1.11 exist only as git tags and on PyPI, not as Zenodo records.

## Documentation map

Everything below lives in the repo — start at the guide that matches what you want to do:

| You want to… | Read |
|---|---|
| **Read the whole story end to end (long-form)** | [`docs/articles/fullseye_overview_qiita_en.md`](https://github.com/furuse-kazufumi/fullseye/blob/master/docs/articles/fullseye_overview_qiita_en.md) · [日本語](https://github.com/furuse-kazufumi/fullseye/blob/master/docs/articles/fullseye_overview_qiita_ja.md) |
| See what the operators produce (result gallery) | `docs/GALLERY.md` |
| Browse the paper exhibits (op demos and the PoC museum, en/ja; accession numbers in `exhibit_numbers.json`) | `docs/articles/exhibits/` |
| Look up any of the 3,838 operators in the machine-readable index | `docs/ops/INDEX.md` (full TOC) · `docs/OP_CATALOG.md` (one-page catalog) |
| Find real sample data (meshes / volumes / images, with licenses) | `docs/ops/SAMPLES.md` |
| Use Fullseye as an AI/RAG knowledge base | `docs/AI_RAG_GUIDE.md` (+ `fullseye-rag`) |
| Drive the Studio IDE | `docs/STUDIO_GUIDE.md` · `docs/HDEVELOP_DEV_OPS.md` (dev_* window ops) |
| Add an operator | `docs/ADDING_OPS.md` · `CONTRIBUTING.md` |
| Read the Studio and its operator help in your language | Studio ▸ Tools ▸ Language — **日本語 / English / 简体中文 / 繁體中文 / 한국어 / Deutsch** |
| Understand the language policy and what is *not* translated | `docs/I18N.md` |
| Update a checkout safely | `tools/update_fullseye.py --check` |
| **See what is actually verified (and how)** | `docs/MATURITY.md` + `docs/maturity.json` (generated, not written) |
| **Report results from real hardware or real data** (data may stay private) | `docs/VALIDATION_CONTRIBUTING.en.md` · [issue form](https://github.com/furuse-kazufumi/fullseye/issues/new?template=real_validation_report.yml) |
| **Read why it is built this way** | `docs/DESIGN_NOTES.md` — the load-bearing `★` comments collected from the source (generated; the count is `total` in `docs/design_notes.json`), in 6 languages (translated as we go; untranslated entries are shown as such) |
| Cite Fullseye | `CITATION.cff` |

## Design principles

- **Reimplemented from public knowledge** — published algorithms and open-source
  libraries, unified behind one typed interface; not derived from any proprietary product.
- **Honest by construction** — held-out data is never used for selection; coverage and
  benchmark numbers are measured, not asserted; limitations are disclosed, not hidden.
- **Optional heavy dependencies** — a numpy+scipy core always works; richer backends and
  GPU are opt-in.

## Scope

- **Out of scope: deep learning.** Fullseye is classical image processing plus physical models.
  It does not train or ship deep networks or pretrained weights (HALCON's Deep Learning chapter is
  not a target).
- **Classical trained classifiers: included.** Small classifiers that you train on your own feature
  vectors, in numpy (HALCON's Classification chapter): `train_class_gmm` / `train_class_knn`
  (`mvclass`), `train_class_mlp` (one hidden layer) / `train_class_svm` (linear, one-vs-rest),
  `fisher_lda_fit` (two-class, closed form). Nothing is pretrained; the model is whatever your
  labelled data makes it.
- **OCR: classical, included since 0.9.0.** `opssymbol` ships a classical OCR (`ocrclassic`, 8 ops:
  a built-in 5x7 font, line segmentation, features, training and classification, reading a line, scoring),
  alongside 1-D / 2-D code reading and print-quality grading. Its classifier is k-NN over feature
  templates rendered from the font, not a neural network.
- Some HALCON names are only **partial** analogues and are not counted as covered — see the
  "Partial analogues" table in [docs/HALCON_COVERAGE.md](docs/HALCON_COVERAGE.md).

## ViEW2026 入口メディアの検証(2026-11-01)

### `poc_interferometry_step` — `09_csi_scan_explained`

| 門 | 判定 |
|---|---|
| 解像度・コマ数・fps | 実測: 1280×720、48 コマ、12 fps |
| 文字のはみ出し | 描画前に全ラベルを `measure_text` で測定。はみ出し 0、最小 22 px |
| データ面の最近傍拡大 | なし。512→560、512→300/260 は双一次補間 |
| 画面の数字 = 実行の数字 | assert: 傾斜補正後の段差 0.661020 µm、真値 0.650000 µm、差 +11.020 nm |
| 追加の門 | assert: 差は 20 nm 以内。最終コマは有限な高さ 241,172 画素を全数着色 |
| 決定性 | 2 回描画した MP4/GIF の SHA-256 がそれぞれ一致 |
| `regen_all.py --check` | 緑 |

### `poc_polarization_specular` — `08_analyzer_sweep_explained`

| 門 | 判定 |
|---|---|
| 解像度・コマ数・fps | 実測: 1280×720、72 コマ、12 fps |
| 文字のはみ出し | 描画前に全ラベルを `measure_text` で測定。はみ出し 0、最小 22 px |
| データ面の最近傍拡大 | 1024→560 と 512→300/230 は双一次。DoFP の 24×24 crop だけ「拡大 12×」と明記して整数最近傍 |
| 画面の数字 = 実行の数字 | 同じ `r` から DoLP A/B/C = 0.900024 / 0.155788 / 0.000314 を取得 |
| 追加の門 | assert: 最大/最小比 A/B/C = 23.9563 / 1.35288 / 1.00000。A ≥ 5、C ≤ 1.1 |
| 決定性 | 2 回描画した MP4/GIF の SHA-256 がそれぞれ一致 |
| `regen_all.py --check` | 緑 |

## License

Apache-2.0.
