From ca30cc4a21831515b7fadfca685ef30bbbea6ad8 Mon Sep 17 00:00:00 2001 From: skarakovski Date: Sat, 17 Oct 2026 20:45:15 +0700 Subject: [PATCH 01/38] plan: 32-photo-trace-ai (optional in-browser AI silhouettes, classic stays baseline, v1.3.0) Co-Authored-By: Claude Sonnet 5.5 --- openspec/ROADMAP.md | 1 + .../changes/32-photo-trace-ai/.openspec.yaml | 2 + openspec/changes/32-photo-trace-ai/design.md | 59 ++++++++ .../changes/32-photo-trace-ai/proposal.md | 34 +++++ .../specs/cutout-photo-ai/spec.md | 126 ++++++++++++++++++ openspec/changes/32-photo-trace-ai/tasks.md | 37 +++++ 6 files changed, 259 insertions(+) create mode 100644 openspec/changes/32-photo-trace-ai/.openspec.yaml create mode 100644 openspec/changes/32-photo-trace-ai/design.md create mode 100644 openspec/changes/32-photo-trace-ai/proposal.md create mode 100644 openspec/changes/32-photo-trace-ai/specs/cutout-photo-ai/spec.md create mode 100644 openspec/changes/32-photo-trace-ai/tasks.md diff --git a/openspec/ROADMAP.md b/openspec/ROADMAP.md index 8a8c112..5ffe1ce 100644 --- a/openspec/ROADMAP.md +++ b/openspec/ROADMAP.md @@ -41,6 +41,7 @@ | 29 | `29-beta-label` | 22a | Метка beta и дисклеймер; версия 1.0.0 из package.json на фронтенде; правило semver: каждый change поднимает версию (config.yaml, CLAUDE.md, CHANGELOG.md) | | 30 | `30-rack-front-plate` | 28, 29 | v1.1.0. Передняя панель корпуса: крепление keystone по моделям (гнездо с рампой и защёлкой, обойма за панелью), группы отверстий (колонка, ряд, сетка, зоны вокруг корпуса), «отверстия» вместо «кабельных», окно по умолчанию, ссылки на референсы в README | | 31 | `31-rack-shelf` | 26, 29 | v1.2.0. Простая полка: передняя панель с ушами и плоская полка (глубина, толщина, зазор до реек, бортики по бокам и сзади), печать полкой на столе | +| 32 | `32-photo-trace-ai` | 25a, 29 | v1.3.0. Фото-вырезы: альтернативный нейросетевой движок силуэтов (в браузере, веса по согласию, гибридный точный край), классический остаётся по умолчанию как baseline; сравнение движков | ```mermaid gantt diff --git a/openspec/changes/32-photo-trace-ai/.openspec.yaml b/openspec/changes/32-photo-trace-ai/.openspec.yaml new file mode 100644 index 0000000..b699b88 --- /dev/null +++ b/openspec/changes/32-photo-trace-ai/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-10-17 diff --git a/openspec/changes/32-photo-trace-ai/design.md b/openspec/changes/32-photo-trace-ai/design.md new file mode 100644 index 0000000..e2877fd --- /dev/null +++ b/openspec/changes/32-photo-trace-ai/design.md @@ -0,0 +1,59 @@ +# Design + +## Context + +Today `usePhotoTrace` builds `rectified = rectify(photo, corners, paper, rgb)` (4 px/mm, ≤ 2000 px) and `segmentation = segment(rectified, {margin, sensitivity})`. A `Segmentation` is `{width, height, scale, labels (Int32 per pixel, 0 = background), silhouettes[{id, areaMm2, widthMm, heightMm, bbox}], sensitivity, defaultSensitivity}`. Everything downstream (`traceSilhouette`, `tracedCutouts`, the silhouette list, click selection, the dialog's preview) reads only that structure. This is the seam: an AI engine has to produce a `Segmentation`, nothing else changes. + +Why the straightened sheet is a good input for a network: it is a top view at true scale with the table cropped away, so the network sees only paper and objects, the foreground is by construction "everything that is not the sheet", and mm ↔ pixel is known (4 px/mm). + +## Goals / Non-Goals + +**Goals:** +- An alternative engine behind the same `Segmentation`, selectable per session, default unchanged. +- The baseline is provably untouched and is the yardstick. +- No photo leaves the browser; weights are downloaded only after consent. +- A decision on the model backed by numbers on the fixtures. + +**Non-Goals:** +- Replacing or tuning the classic segmenter. Click prompts. Server inference. Non-commercial weights. + +## Decisions + +- **Engine as data, not as a fork.** `type SegEngine = 'classic' | 'ai'` lives in `PhotoSettings.engine` (default `'classic'`). `usePhotoTrace` keeps `segmentation` (classic, as now) and adds `aiSegmentation` computed only when the engine is `ai` or the compare view is on; the exposed `active` segmentation is chosen by the engine. `segment.ts`, `colour.ts`, `outline.ts` are not modified, only imported. Alternative: an `if` inside `segment()`. Rejected: it would touch the baseline and make its regression harder to prove. +- **Input and resolution.** The rectified RGB is resampled to the model's input (typically 512 or 1024 px for BiRefNet-lite, 320 for U²-Net-P), run, and the alpha is resampled back to the 4 px/mm raster (bilinear). A4 at 1024 px is ≈ 0.29 mm/px, so the network edge is only good to about ±0.5 mm: this is why the edge is refined (next point) and why the result is never used raw for the default. +- **Model choice by measurement (task 1).** Candidates from the research, all permissive: BiRefNet-lite fp16 (MIT, 114 MB, sharpest edges, WebGPU best), ISNet general / U²-Net-P (Apache-2.0 / MIT, 5–45 MB, WASM-friendly), MODNet is portrait-only and excluded. Excluded for licence: RMBG-1.4/2.0 (non-commercial), which `PREPROP_CUTOUT_GOOGLE.md` recommends, and `finegrain-box-segmenter` (396 MB, the author advises against client use). The default AI engine uses the best candidate that reaches the targets in the table below; a second, smaller model may be offered as "lighter". If none reaches them the change stops after task 1 and records the numbers (the change is then archived as a negative result without a version bump, via `/opsx:update`). + + | Target on the two fixtures | Value | + |---|---| + | Tools found | 3 per photo, none merged or split | + | Size of each silhouette vs the baseline | within 1.0 mm (raw network), within 0.5 mm (hybrid) | + | Silhouette IoU vs the baseline | ≥ 0.97 (hybrid) | + | Time for one A4 on a laptop, WASM / WebGPU | ≤ 15 s / ≤ 3 s (informational) | + +- **Salient-object networks and shadows.** These networks were trained to cut out objects and usually leave a cast shadow out (the argument in `PREPROP_CUTOUT_GOOGLE.md`), but "usually" is not a guarantee, and thin dark parts (pliers jaws) can be eaten. The harness therefore reports, per fixture, the area difference to the baseline split into "network larger" (shadow leaked) and "network smaller" (part lost). +- **Mask → silhouettes.** Threshold the alpha at 0.5, then the same cleanup steps the classic path uses, imported from `geometry/raster` (`close`, `fillHoles`, `removeSpecks`, `labelComponents`, edge margin, `minSize`/`minArea` from `defaultSegmentParams`). The numbering rule is the classic one (top to bottom), so list order and colours are identical between engines for the same objects. Pure function `aiSegmentation(rectified, alpha, params) → Segmentation`, unit-tested on stored alphas. +- **Hybrid edge.** For each silhouette: band = mask dilated by `band` mm (default 1.5) minus mask eroded by `band`. Inside the band the classic paper-colour deviation (`colourMask` from the paper surface fitted by `paperSurface`; `paperSurface` and `colourMask` are called, not changed) classifies pixels; the refined mask is `eroded core ∪ (band ∩ classic object pixels)`. If the classic test marks the whole band as paper or as object on a stretch longer than a few mm (the dark-part-in-shadow case), that stretch keeps the network edge. The refined labels go through the same `traceAlpha` as today. A setting `refine: on|off` (default on) exists in the compare view so the raw network edge can be inspected; it is not a persisted user option beyond the session. +- **Compare view.** `CompareOverlay` draws the other engine's silhouette outlines (already traced by `traceSilhouette`) as dashed lines over the straightened view, plus a table "tool: classic mm / AI mm / Δ". It costs one extra segmentation when open, and is the way the user, and the owner while developing, sees where the engines disagree. +- **ML runtime.** `src/ml/` is React-free. `matte(rgb, w, h) → Float32 alpha` is the only adapter this change needs. `ml.worker.ts` owns the inference session; `mlClient.ts` mirrors `modelClient.ts` (latest request wins, stale replies dropped); the worker is created lazily so classic users pay nothing, not even the library chunk (dynamic import). Backend: try WebGPU, fall back to WASM single-threaded (no COOP/COEP on Pages); report the backend in the model manager. +- **Model manager and consent.** First use of AI opens `ModelDialog`: model name, size, source host (`huggingface.co`), the statement that the request reveals the user's IP to that host while the photo and the result stay in the browser, buttons Download / Cancel. Download shows progress and can be cancelled. Weights are cached with the Cache API under a versioned key (revision hash), listed with size and a Delete button in the same dialog (opened from the engine switch and from the footer's settings). If the cache is unavailable the model is used for the session and a notice says it will be downloaded again. Nothing downloads without the click. +- **Pinned weights.** The registry (`src/ml/models.ts`) holds per model: id, URL with a commit hash (not `main`), byte size, SHA-256 (verified after download), input size and normalisation, SPDX licence, source. A model enters the registry only with MIT / Apache-2.0 / BSD / CC0. The References page and README list them from this registry (a test checks every registry entry appears). +- **Mobile and weak devices.** The AI option is available at any width, but the dialog states the size, and on narrow screens or without WebGPU the lighter model is suggested first. The classic engine is always one click away and is what runs if the AI path fails (error banner, `active` falls back to classic). +- **Testing.** Unit tests never run a network. Frozen model outputs (alpha PNGs produced by the harness for each fixture and each chosen model, a few hundred KB) are committed under `geometry/photo/fixtures/ai/` and drive the post-processing tests. Component tests mock `mlClient`. The classic baseline has a new frozen-output test (silhouette count, bbox, area per fixture) next to the existing `photoFixtures.test.ts`, so any accidental change to it fails. +- **Rollout.** The engine switch is always visible, Classic selected. No flag. + +## Risks / Trade-offs + +- [The network does not beat the baseline on the owner's photos] → task 1 is a gate; the hybrid edge and the compare view are built only after it passes. +- [Network edge is "puffy" at 1024 px] → hybrid refinement; targets measured on the refined result. +- [Shadow leaks into the mask or a dark part is lost] → the band search clamps leaks to the classic edge where classic is reliable; lost parts are visible in the compare view and the user can switch engine. +- [100–300 MB download on mobile] → explicit consent with the size, the lighter model offered first on mobile, cancel, cache. +- [Library size and WASM files bloat the bundle] → dynamic import in the worker only on first AI use; the classic start-up path does not load them (checked in the build output size and the network panel). +- [Hugging Face availability, revision changes] → pinned hashes and SHA-256; on a failed download the error says which host failed and classic keeps working. +- [Single-threaded WASM is slow] → progress indicator, `computing` flag as for the classic path; measured in task 1, the lighter model is the WASM default. +- [Licence drift] → registry test refuses entries without an allowed SPDX id. + +## Open Questions + +- Is a request to huggingface.co acceptable at all (it reveals the IP), or must the weights be self-hosted (Cloudflare R2)? The design assumes huggingface.co with consent; self-hosting only changes the URLs in the registry. +- Which devices are the minimum target (an iPhone, a weak Android)? It decides whether the WASM path of BiRefNet-lite is shipped or only the light model. +- Click-to-select with SlimSAM / SAM 2.1-tiny is the strongest answer to touching and merged tools. It reuses the same runtime and the `Segmentation` seam and is proposed as the next change after the automatic path is measured. diff --git a/openspec/changes/32-photo-trace-ai/proposal.md b/openspec/changes/32-photo-trace-ai/proposal.md new file mode 100644 index 0000000..28ef0ec --- /dev/null +++ b/openspec/changes/32-photo-trace-ai/proposal.md @@ -0,0 +1,34 @@ +# Proposal + +## Why + +The photo-to-cutout flow (`25`, `25a`) finds tools on a sheet of paper with a hand-tuned colour segmenter: a paper-colour model, a shadow-region growth and a hole fill. On the owner's two reference photos (`geometry/photo/fixtures/{flash,noflash}.jpg`) it works, but it is a pile of heuristics with known weak spots: dark parts that merge with their own shadow, objects close to the paper's colour, tools that touch and merge into one silhouette, glossy plastic and glass. Every new photo style needs new thresholds. + +Neural segmentation answers the question the heuristics approximate: "which pixels are the object". Models for salient-object matting and promptable segmentation run in the browser (ONNX Runtime Web / Transformers.js, WASM or WebGPU) and do not send the photo anywhere. The research is in `tmp/ai/BROWSER_AI_RESEARCH.md` (model catalogue) and `tmp/ai/BROWSER_AI_APPLICATION.md` (where to apply it); the preprocessing notes in `tmp/ai/PREPROP_CUTOUT_*.md` come to the same conclusion: use the network for "what", and keep a precise edge from classical code, because the requirement is 0.5 mm on size. + +The AI path is an **alternative**, not a replacement. The current segmenter stays exactly as it is and stays the default (the baseline): it works offline, needs no download and is the reference the AI result is measured against. + +## What Changes + +- **Engine choice on the silhouette step.** A control "Detection: Classic | AI" in the photo dialog. Classic is the default and is today's behaviour, untouched. AI is optional and needs a one-time model download with the user's consent. The choice is kept for the session with the rest of the dialog state. +- **AI silhouettes (automatic).** The straightened sheet (`rectify`) is run through a background-removal network; the foreground mask is cleaned (threshold, close, fill holes, edge margin, specks, connected components) into the same `Segmentation` the classic path produces, so selection, outline tracing, cutout creation and everything after it are shared. +- **Hybrid edge.** The network decides which pixels belong to an object; the exact edge comes from the classical code: a band of 1–2 mm around the network's mask is searched with the existing paper-colour test and sub-pixel `traceAlpha`. Where the classic test finds nothing in the band (a dark part merged with its shadow) the network's own edge is used. Result: the 0.5 mm scale accuracy of the baseline wherever the baseline is accurate, and the network's judgement where it is not. +- **Compare view.** A switch that shows the classic and the AI silhouettes over the straightened sheet (outline of the other engine as a dashed line), with the size of each silhouette in mm for both, so the user can pick the better result per photo. +- **ML runtime.** A separate worker (`ml.worker.ts`, not the Manifold worker), a client with "latest request wins" like `modelClient.ts`, a model manager (consent dialog, download with progress, cache in the browser, delete, status), backend choice WebGPU → WASM, weights from a pinned Hugging Face revision. All UI text in en and ru. +- **Evaluation harness.** A spike script that runs the baseline and every candidate model on the fixtures and prints per-silhouette IoU and size error in mm; the model for the default AI engine is chosen from its output (task 1), not from the catalogue. + +## Capabilities + +### New Capabilities +- `cutout-photo-ai`: the optional neural silhouette engine for photo cutouts, the unchanged classic baseline, the hybrid edge, the compare view, and the model download and privacy rules. + +### Modified Capabilities + +## Impact + +- New `src/ml/` (React-free adapters `matte(image) → alpha`, runtime, model registry with pinned revisions and licences, cache), `ml.worker.ts` and `mlClient.ts`; new pure code `geometry/photo/ai.ts` (mask → `Segmentation`, hybrid edge band) next to `segment.ts`, which is **not edited**; `features/cutout/photo/` gets `EngineSwitch`, `ModelDialog`, `CompareOverlay` and `usePhotoTrace` gains the engine state and an AI segmentation next to `segmentation`; `app/` or settings gets the model manager entry; strings in en and ru; the References page lists every model with its SPDX licence and source; tests (post-processing on stored model outputs, baseline regression, component tests with a mocked `mlClient`). +- New dependency on an inference library (`onnxruntime-web` or `@huggingface/transformers`, chosen in task 1); weights are not committed and not bundled (100 MB limit of GitHub Pages), they are fetched on demand. +- The classic path's outputs on both fixtures stay byte-identical (checked by an existing test and a new frozen-output test). +- Out of scope: click-to-select / positive and negative points with a promptable model (SAM), a follow-up change once the automatic path is measured; depth maps, background removal for Keychain, prompt-to-parameters and other ideas from `BROWSER_AI_APPLICATION.md`; any server-side inference; non-commercial weights (RMBG-1.4/2.0, Depth Anything Base/Large) and AGPL code (Ultralytics); multi-threaded WASM (needs COOP/COEP headers that GitHub Pages does not send); offline pre-download of models; changing the classic segmenter. +- Version 1.3.0 (minor: a new option). +- Depends on: `25a-photo-trace-improvements`, `29-beta-label` (the versioning rule). diff --git a/openspec/changes/32-photo-trace-ai/specs/cutout-photo-ai/spec.md b/openspec/changes/32-photo-trace-ai/specs/cutout-photo-ai/spec.md new file mode 100644 index 0000000..26e9135 --- /dev/null +++ b/openspec/changes/32-photo-trace-ai/specs/cutout-photo-ai/spec.md @@ -0,0 +1,126 @@ +# Spec Delta + +## Purpose +Defines the optional neural silhouette engine for photo cutouts and the guarantee that the classic detection stays as the unchanged baseline. + +## ADDED Requirements + +### Requirement: Classic baseline unchanged +The classic silhouette detection described in `cutout-photo-trace` SHALL remain available, SHALL be the default engine, SHALL work without any download or network access, and SHALL produce exactly the same silhouettes as before this change for the same photo, corners, paper and settings. + +#### Scenario: Default engine +- **WHEN** the photo dialog is opened and a photo is chosen +- **THEN** the classic engine is selected and no model is requested + +#### Scenario: Unchanged result +- **WHEN** the two reference photos (with and without flash) are processed with the classic engine +- **THEN** each gives the same three silhouettes, with the same sizes and areas, as the recorded baseline + +#### Scenario: Offline +- **WHEN** the device has no network and the classic engine is used +- **THEN** detection works as before + +### Requirement: Engine choice +On the silhouette step the user SHALL be able to choose the detection engine, Classic or AI. The choice SHALL apply to the current photo and SHALL be kept while the dialog state lives (closing and reopening the dialog keeps it). Selection by click, the list with sizes, the sensitivity-independent steps (margin, depth, clearance) and the creation of cutouts SHALL work the same for both engines. + +#### Scenario: Switching engines +- **WHEN** the user switches from Classic to AI and the model is ready +- **THEN** the silhouettes are recomputed by the AI engine and shown in the same view and list, numbered top to bottom + +#### Scenario: Shared downstream +- **WHEN** silhouettes found by the AI engine are kept and Create is pressed +- **THEN** one cutout per kept silhouette is created exactly as for the classic engine, in true scale and position, with the bin footprint fitted + +#### Scenario: Sensitivity +- **WHEN** the AI engine is active +- **THEN** the sensitivity control, which belongs to the classic detector, is not offered for it, and the margin still applies + +### Requirement: Model download and consent +The AI engine SHALL need a neural model that is downloaded on demand. Before the first download the app SHALL show the model's name, size, source host and licence and SHALL download only after the user confirms. It SHALL show progress, allow cancelling, keep the model in the browser's storage for later sessions when storage is available, and let the user see its size and delete it. If the download or the model fails, the classic engine SHALL stay usable and the failure SHALL be shown. + +#### Scenario: Consent first +- **WHEN** the user chooses AI for the first time +- **THEN** a dialog states the download size and that the source host sees the user's address, and nothing is downloaded until the user confirms + +#### Scenario: Cached +- **WHEN** the model was downloaded earlier and the storage is intact +- **THEN** choosing AI starts at once without a new download + +#### Scenario: Delete +- **WHEN** the user deletes the model in the model dialog +- **THEN** its stored data is removed and choosing AI asks for consent again + +#### Scenario: Failure +- **WHEN** the download is interrupted or the checksum of the file does not match +- **THEN** an error says so, the file is not used, and the dialog stays on the classic engine + +#### Scenario: Storage unavailable +- **WHEN** the browser refuses persistent storage +- **THEN** the model works for the session and a notice says it will be downloaded again next time + +### Requirement: Privacy +The photo, the straightened image and the masks SHALL be processed in the browser only and SHALL NOT be sent anywhere by the AI engine. The only network traffic added by the AI engine SHALL be the download of the model files, from a fixed, versioned location, after consent. The user interface and the References page SHALL say so, and SHALL list every model with its licence and source. Only models under MIT, Apache-2.0, BSD or CC0 licences SHALL be used. + +#### Scenario: No image upload +- **WHEN** a photo is processed by the AI engine +- **THEN** no network request carries image data + +#### Scenario: Licence list +- **WHEN** the References page is opened +- **THEN** each model used by the app is listed with its licence and source + +### Requirement: Silhouettes from the network +The AI engine SHALL run the straightened sheet through the model, turn its foreground mask into silhouettes with the same cleanup as the classic path (enclosed gaps filled, small specks and tiny silhouettes dropped, a margin along the paper edge ignored, one silhouette per separate object), and SHALL return them in the same form as the classic engine. Soft shadows beside a tool SHALL NOT be part of its silhouette. + +#### Scenario: Reference photos +- **WHEN** each of the two reference photos is processed by the AI engine +- **THEN** three silhouettes are found, and each tool's width and height are within 1 mm of the baseline's + +#### Scenario: Shadow +- **WHEN** a tool casts a soft shadow beside it +- **THEN** the silhouette follows the tool and not its shadow + +#### Scenario: Margin +- **WHEN** the margin is 3 mm +- **THEN** nothing within 3 mm of the paper edge is a silhouette + +#### Scenario: No objects +- **WHEN** the model finds nothing +- **THEN** the message for no silhouettes is shown and Create is disabled + +### Requirement: Accurate edge +The AI engine SHALL place the silhouette edge by the paper-colour test within a narrow band around the network's mask and SHALL use the network's own edge only where that test does not separate the object from the paper. The sizes it reports SHALL stay within 0.5 mm of the classic result wherever the classic result is accurate. + +#### Scenario: Edge from the colour test +- **WHEN** a tool contrasts with the paper +- **THEN** its silhouette edge follows the colour edge, not the coarser edge of the network's mask + +#### Scenario: Dark part in shadow +- **WHEN** a dark part of a tool merges with its own shadow so that the colour test cannot find its edge +- **THEN** the network's edge is used along that stretch and the silhouette is still one piece + +#### Scenario: True scale +- **WHEN** a 40 × 20 mm rectangle lies on the paper and the corners are placed correctly +- **THEN** the AI silhouette measures 40 × 20 mm within 0.5 mm + +### Requirement: Compare engines +The user SHALL be able to compare the two engines on the straightened view: the silhouettes of the inactive engine are drawn as outlines over the view, and a table lists each tool's width and height for both engines and the difference. + +#### Scenario: Outlines +- **WHEN** compare is switched on +- **THEN** the outlines of the other engine appear as dashed lines over the straightened sheet + +#### Scenario: Sizes +- **WHEN** compare is on +- **THEN** each silhouette's size is shown for both engines with the difference in mm + +#### Scenario: Off by default +- **WHEN** the dialog is opened +- **THEN** compare is off and costs nothing + +### Requirement: Language +All text of the AI engine, the model dialog and the compare view SHALL be available in every language of the app. + +#### Scenario: Russian +- **WHEN** the interface language is Russian +- **THEN** the engine switch, the consent dialog, progress, errors and the compare table are in Russian diff --git a/openspec/changes/32-photo-trace-ai/tasks.md b/openspec/changes/32-photo-trace-ai/tasks.md new file mode 100644 index 0000000..61b7ad0 --- /dev/null +++ b/openspec/changes/32-photo-trace-ai/tasks.md @@ -0,0 +1,37 @@ +# Tasks + +## 1. Spike: do the models beat the baseline? (gate) + +- [ ] 1.1 Evaluation harness `scripts/eval-photo-ai.ts` (not shipped, not run by CI): for each fixture in `geometry/photo/fixtures` and for the owner's originals in `tmp/ai/photo_cutout_*.jpeg`, rectify, run the classic `segment` and each candidate model (BiRefNet-lite fp16, ISNet general, U²-Net-P; Node with `onnxruntime-node`), and print per silhouette: size in mm, IoU against the classic result, "network larger" and "network smaller" area, and time. Write each model's alpha for the fixtures to `geometry/photo/fixtures/ai/-.png`. Verify: the script prints the table for both fixtures. +- [ ] 1.2 Record the table and the decision in `design.md` (Decisions → Model choice): the default AI model, the lighter model, the pinned revisions and licences, the measured sizes of the files. Verify against the targets in `design.md`. If no model reaches the raw targets (3 tools, ≤ 1 mm) stop here, update the artifacts with `/opsx:update` and close the change as a negative result. + +## 2. Baseline guard + +- [ ] 2.1 Frozen-output test for the classic path (`photoBaseline.test.ts`): per fixture the silhouette count, each silhouette's bbox, area and size, recorded from the current code; fails on any change. Verify: passes on the current code before anything else is touched; `git diff` shows `segment.ts`, `colour.ts`, `outline.ts` untouched at the end of the change. + +## 3. Pure AI post-processing + +- [ ] 3.1 `geometry/photo/ai.ts`: `aiSegmentation(rectified, alpha, params) → Segmentation` (threshold, close, fill holes, margin, specks, components, numbering top to bottom, minimum size and area as the classic path). Tests on the stored alphas from 1.1: three silhouettes per fixture, sizes within 1 mm of the baseline, nothing in the edge margin, empty alpha gives no silhouettes, two touching blobs stay one silhouette. +- [ ] 3.2 Hybrid edge in `geometry/photo/ai.ts`: band around the mask, classic paper-colour test inside the band (calling `paperSurface` and `colourMask`), network edge kept where the test is uninformative, refined labels. Tests: sizes within 0.5 mm of the baseline and IoU ≥ 0.97 on both fixtures; a synthetic 40 × 20 mm rectangle (`synthetic.ts`) with a deliberately puffy mask measures 40 × 20 mm within 0.5 mm; a synthetic dark part merged into a shadow keeps the network edge and stays one piece; `refine: off` returns the raw mask result. + +## 4. ML runtime + +- [ ] 4.1 `src/ml/models.ts` registry (id, URL with commit hash, bytes, SHA-256, input size, normalisation, SPDX, source) and a test that refuses entries without an allowed SPDX id or without a pinned revision. Verify: test green. +- [ ] 4.2 `src/ml/runtime.ts` and `ml.worker.ts`: lazy session creation by dynamic import, WebGPU then WASM single-thread, `matte(rgb, w, h) → Float32Array`, resize to the model input and back. `mlClient.ts`: latest request wins, stale replies dropped, progress and cancel. Tests with a fake worker for ordering and cancel; pre/post-processing tested on a tiny fixed array. Verify in the browser that a classic-only session loads no ML chunk (network panel and build output). +- [ ] 4.3 `src/ml/modelStore.ts`: download with progress and cancel, SHA-256 check, Cache API storage under the revision key, list, delete, `persistent` flag with memory fallback. Tests with a mocked `fetch` and cache: checksum mismatch rejects, delete removes, interrupted download leaves nothing, no storage falls back to memory. + +## 5. UI + +- [ ] 5.1 `usePhotoTrace`: `engine` in `PhotoSettings` (default `classic`), `aiSegmentation` computed only when needed, an `active` segmentation, AI error falls back to classic with an error state, `computing` covers the AI run. Tests: default is classic and calls no model; switching uses the mocked client; a failing client leaves classic active; closing and reopening keeps the engine. +- [ ] 5.2 `EngineSwitch` on the silhouette step (SegmentedControl Classic | AI, sensitivity hidden for AI) and `ModelDialog` (consent text with size, host, licence and the IP note, progress, cancel, delete, backend, cached state). No artboard exists: built from `ui/` components and `theme.css` tokens, said in the commit; compare with the dialog in en and ru and at 390 px. Component tests: consent before any download, progress, error banner, delete asks again for consent, steppers not needed (no numeric inputs). +- [ ] 5.3 `CompareOverlay` and the table of sizes (classic, AI, Δ) in the silhouette step; dashed outlines of the inactive engine over the straightened view; off by default. Tests: toggling draws the second outline set and the table values equal `silhouettes` of both engines; off costs no AI run. +- [ ] 5.4 Strings in `en.json` and `ru.json` (engine, consent, progress, errors, compare, References licence list); the References page and README list the registry models. Tests: key parity, a registry-to-References test, the `PhotoTips` and consent text exist in both languages. + +## 6. Version + +- [ ] 6.1 Bump to 1.3.0: `package.json` and a `CHANGELOG.md` entry `1.3.0 — 32-photo-trace-ai`. Test: the existing version test passes with the new number. + +## 7. Verification + +- [ ] 7.1 `npm test` and `npm run build` are green, the dev server starts, no console errors; `git diff main -- src/geometry/photo/segment.ts src/geometry/photo/colour.ts src/geometry/photo/outline.ts` is empty. +- [ ] 7.2 In the browser with the two reference photos, en and ru and at 390 px: Classic is the default and gives today's three silhouettes; AI asks for consent, downloads with progress, gives three silhouettes; compare shows both sets and sizes within the targets; deleting the model asks again; with the network blocked after the download AI still works and with it blocked before, the error appears and Classic still works; no request carries image data; cutouts created from AI silhouettes have the right size and position in the bin. From 2a8d0a08e92c0976cfb8895d111580c45e3452b4 Mon Sep 17 00:00:00 2001 From: skarakovski Date: Sat, 17 Oct 2026 21:13:37 +0700 Subject: [PATCH 02/38] poc: browser AI silhouettes (ORT Web) on the owner's photos, findings Co-Authored-By: Claude Sonnet 5.5 --- package-lock.json | 163 ++++++++++++++++++++++++++++++- package.json | 4 +- poc/ai/FINDINGS.md | 19 ++++ poc/ai/index.html | 29 ++++++ poc/ai/main.ts | 188 ++++++++++++++++++++++++++++++++++++ poc/ai/models.ts | 28 ++++++ poc/ai/samples/flash.jpeg | Bin 0 -> 146570 bytes poc/ai/samples/noflash.jpeg | Bin 0 -> 140704 bytes poc/ai/vite.config.ts | 14 +++ 9 files changed, 442 insertions(+), 3 deletions(-) create mode 100644 poc/ai/FINDINGS.md create mode 100644 poc/ai/index.html create mode 100644 poc/ai/main.ts create mode 100644 poc/ai/models.ts create mode 100644 poc/ai/samples/flash.jpeg create mode 100644 poc/ai/samples/noflash.jpeg create mode 100644 poc/ai/vite.config.ts diff --git a/package-lock.json b/package-lock.json index 2a13d61..5e91eb6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "printkit", - "version": "0.1.0", + "version": "1.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "printkit", - "version": "0.1.0", + "version": "1.0.0", "license": "MIT", "dependencies": { "@fontsource-variable/caveat": "^5.3.0", @@ -61,6 +61,7 @@ "fake-indexeddb": "^6.2.5", "jpeg-js": "^0.4.4", "jsdom": "^29.1.1", + "onnxruntime-web": "^1.30.0", "tsx": "^4.23.15", "typescript": "^7.0.2", "vite": "^8.3.4", @@ -1625,6 +1626,72 @@ "url": "https://github.com/sponsors/oxc-project" } }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.5.tgz", + "integrity": "sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.1.tgz", + "integrity": "sha512-vW1GmwMZNnL+gMRaovlh9yZX74kc+TTU3FObkkurpMaRtBfLP3ldjS9KQWlwZgraRE0+dheEEoAxdzcJQ8eXZg==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.1.tgz", + "integrity": "sha512-GpptLrs57adMSuHi3VNj0mAF8dwh36LMaYF6XyJ6JMWlVsc+t42tm1HSEDmOs3A8fC9yyeisgLhsTVQokOZ0zw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.2.tgz", + "integrity": "sha512-b1UQwcEZ4yCnMCD8DAL1VlbvBJE9/IX4FTIp7BG1xYpf29SLazLSrqUkj4w7Y5y7cCVP6E5tcqqcI0xemPkHug==", + "dev": true, + "license": "BSD-3-Clause" + }, "node_modules/@remix-run/route-pattern": { "version": "0.22.1", "resolved": "https://registry.npmjs.org/@remix-run/route-pattern/-/route-pattern-0.22.1.tgz", @@ -2553,6 +2620,17 @@ "integrity": "sha512-go+C6vHBUuO9jH6Tj6qEZ63JEGoMSxFdmd+6QAtthGD6xumBfIz3RbNDDs2aoRVLf9naH8lk5jnWy2UHHKgYQQ==", "license": "MIT" }, + "node_modules/@types/node": { + "version": "26.6.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-26.6.5.tgz", + "integrity": "sha512-uMzsv9stDdofIZBx4UGh4Z9ZgXnGC9seRDscilBcTHAeOFk67MZg/k9bWVfO+bk4qZmG+l52hRnLwUiH2LnJTg==", + "devOptional": true, + "license": "MIT", + "peer": true, + "dependencies": { + "undici-types": "~8.9.0" + } + }, "node_modules/@types/react": { "version": "19.3.0", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.3.0.tgz", @@ -3382,6 +3460,13 @@ "integrity": "sha512-tbZNuJrLwGUp3zshBtdy4W+ORxZuIh8a5ilyIEQDC5rY1f3U20JMry0Ll3WBzU58EZKsEuJFXhb5gwv8CsPvgA==", "license": "MIT" }, + "node_modules/flatbuffers": { + "version": "25.9.23", + "resolved": "https://registry.npmjs.org/flatbuffers/-/flatbuffers-25.9.23.tgz", + "integrity": "sha512-MI1qs7Lo4Syw0EOzUl0xjs2lsoeqFku44KpngfIduHBYvzm8h2+7K8YMQh1JtVVVrUvhLpNwqVi4DERegUJhPQ==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -3402,6 +3487,13 @@ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "license": "ISC" }, + "node_modules/guid-typescript": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/guid-typescript/-/guid-typescript-1.0.9.tgz", + "integrity": "sha512-Y8T4vYhEfwJOTbouREvG+3XDsjr8E3kIr7uf+JZ0BYloFsttiHU0WfvANVsR7TxNUJa/WpCnw/Ino/p+DeBhBQ==", + "dev": true, + "license": "ISC" + }, "node_modules/html-encoding-sniffer": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-6.0.0.tgz", @@ -3823,6 +3915,13 @@ "url": "https://opencollective.com/parcel" } }, + "node_modules/long": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", + "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/lru-cache": { "version": "11.5.3", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.3.tgz", @@ -3977,6 +4076,28 @@ "node": ">=12.20.0" } }, + "node_modules/onnxruntime-common": { + "version": "1.30.0", + "resolved": "https://registry.npmjs.org/onnxruntime-common/-/onnxruntime-common-1.30.0.tgz", + "integrity": "sha512-7fdVWjAID1dVhH/G8qK3APARunV4VkBFoCQAP7qp4Wkab0mrorvmc+sqiT+mKXOzDqdjN5j+/Z9nb4gzNPWcyA==", + "dev": true, + "license": "MIT" + }, + "node_modules/onnxruntime-web": { + "version": "1.30.0", + "resolved": "https://registry.npmjs.org/onnxruntime-web/-/onnxruntime-web-1.30.0.tgz", + "integrity": "sha512-q0y+JrrtukXSzsBWEMccVfqX25LRmosXHF+CaRJmg8pZClzcV7svNc4rKY3jL02Vb7QmRMDs1SigqR4CXAfKYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatbuffers": "^25.1.24", + "guid-typescript": "^1.0.9", + "long": "^5.2.3", + "onnxruntime-common": "1.30.0", + "platform": "^1.3.6", + "protobufjs": "^7.2.4" + } + }, "node_modules/parse5": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.1.tgz", @@ -4023,6 +4144,13 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/platform": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/platform/-/platform-1.3.6.tgz", + "integrity": "sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==", + "dev": true, + "license": "MIT" + }, "node_modules/postcss": { "version": "8.5.29", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.29.tgz", @@ -4072,6 +4200,30 @@ "integrity": "sha512-AvPcP7XECNWy4LGmFQ77k7un4lSKM4eS29PTvW4ck95uYeLxXPWJM7hLuBqK91FaHqCcgJvIUCuNJjjxKE7VKQ==", "license": "MIT" }, + "node_modules/protobufjs": { + "version": "7.6.6", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.6.6.tgz", + "integrity": "sha512-dYDWdjSl5RNb7SgPxGQcRU+GtvP7s2fpkrY0r432PcOIaZ0/rBcxEZnQN67iJhFuQiVw754JDoPruPCNdGsbjg==", + "dev": true, + "hasInstallScript": true, + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.5", + "@protobufjs/eventemitter": "^1.1.1", + "@protobufjs/fetch": "^1.1.1", + "@protobufjs/float": "^1.0.2", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.1", + "@types/node": ">=13.7.0", + "long": "^5.3.2" + }, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -4535,6 +4687,13 @@ "node": ">=20.18.1" } }, + "node_modules/undici-types": { + "version": "8.9.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.9.0.tgz", + "integrity": "sha512-KTDyRTYX8sWmKXAikPHHSyc63CRPETMctyjKFupcC6OBLXT3xsN0e9aF7m+mIXutFWpUXuedtowG7iLOzp0kQg==", + "devOptional": true, + "license": "MIT" + }, "node_modules/uniq": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", diff --git a/package.json b/package.json index b641f68..8f7986d 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,8 @@ "dev": "vite", "build": "tsc --noEmit && vite build", "test": "vitest run", - "art": "tsx scripts/render-art.ts" + "art": "tsx scripts/render-art.ts", + "poc:ai": "vite --config poc/ai/vite.config.ts" }, "dependencies": { "@fontsource-variable/caveat": "^5.3.0", @@ -66,6 +67,7 @@ "fake-indexeddb": "^6.2.5", "jpeg-js": "^0.4.4", "jsdom": "^29.1.1", + "onnxruntime-web": "^1.30.0", "tsx": "^4.23.15", "typescript": "^7.0.2", "vite": "^8.3.4", diff --git a/poc/ai/FINDINGS.md b/poc/ai/FINDINGS.md new file mode 100644 index 0000000..f2afd02 --- /dev/null +++ b/poc/ai/FINDINGS.md @@ -0,0 +1,19 @@ +# PoC нейросетевых силуэтов: результаты (2026-10-17) + +Запуск: `npm run poc:ai` → http://localhost:5198 (`POC_COI=1` включает COOP/COEP). Снимки владельца 960×1280 (`samples/`), лист находится `detectPaper`, выпрямление 4 px/мм (840×1188), ONNX Runtime Web 1.30 в воркере (`wasm.proxy`), одна нить, машина 10 ядер, WebGPU доступен. Эталон сравнения — классический `segment()` (3 силуэта на обоих фото). + +| Модель (лицензия, вес) | Бэкенд | Фото | Силуэтов | Δ размеров vs классика | IoU | Инференс | +|---|---|---|---|---|---|---| +| U²-Net-P (Apache-2.0, 4.6 МБ), вход 320 | WASM 1 нить | flash | 3 | ≤ 0.25 мм | 0.984 | ~0.85 с | +| U²-Net-P | WASM 1 нить | noflash | 3 | кусачки: ширина −27 мм (потеряна часть синей ручки), остальные ≤ 0.75 мм | 0.832 | ~0.85 с | +| BiRefNet-lite fp16/fp32 (MIT), вход 1024 (фиксированный) | WebGPU и WASM | flash | — | `std::bad_alloc` в `OrtRun` на обоих бэкендах | — | не получено | +| ISNet general int8 (Apache-2.0, 46 МБ), вход 1024 | WASM 1 нить | flash | — | инференс > 4 мин, прерван | — | не получено | +| RMBG-1.4 int8 (Bria, non-commercial) | — | — | — | не запускалась (лицензия) | — | — | + +## Выводы +1. Бэкенд: ORT Web, один воркер, WebGPU → WASM. Для лёгкой модели WASM в одну нить достаточен (< 1 с на лист), COOP/COEP не нужны. +2. BiRefNet-lite с входом 1024 в этом окружении не запустился ни на WebGPU, ни на WASM (нехватка памяти). Вход у ONNX зафиксирован 1024, уменьшить нельзя. Либо искать/конвертировать вариант с входом 512, либо проверять на другом устройстве в обычном Chrome (память Browser pane могла быть причиной). +3. ISNet int8 на 1024 px и одной нити непригоден по скорости на WASM. +4. U²-Net-P: на снимке со вспышкой почти идентичен классике, на снимке без вспышки теряет часть тёмной/синей ручки: именно тот случай, где нужен гибридный край и сравнение с классикой, и где важна модель посильнее. +5. Нюансы реализации: при `wasm.proxy` тензоры и буфер модели передаются (transfer) и становятся detached: тензор создаётся заново на каждый запуск, буфер модели нельзя использовать повторно; без proxy инференс блокирует страницу. +6. Лицензия RMBG: карточки Hugging Face — `license: other` (bria-rmbg-1.4/2.0, «source-available for non-commercial use»), RMBG-2.0 к тому же gated. diff --git a/poc/ai/index.html b/poc/ai/index.html new file mode 100644 index 0000000..5c0e237 --- /dev/null +++ b/poc/ai/index.html @@ -0,0 +1,29 @@ + +PoC: AI silhouettes + +

PoC: нейросетевые силуэты на выпрямленном листе

+
Снимок + + +
+
Модель
+
Бэкенд + + потоки + вход +
+
Крайпорог поле мм
+ +
cross-origin isolated: , WebGPU: , cores:
+
+
+
+ + diff --git a/poc/ai/main.ts b/poc/ai/main.ts new file mode 100644 index 0000000..2c4ac86 --- /dev/null +++ b/poc/ai/main.ts @@ -0,0 +1,188 @@ +import * as ort from 'onnxruntime-web' +import { toColorImage, toGrayImage } from '../../src/features/keychain/decodeImage' +import { detectPaper } from '../../src/geometry/photo/detect' +import { guessPaper } from '../../src/geometry/photo/aspect' +import { paperSize } from '../../src/geometry/photo/paper' +import { rectify, type Rectified } from '../../src/geometry/photo/homography' +import { segment } from '../../src/geometry/photo/segment' +import { fillHoles, labelComponents, removeSpecks } from '../../src/geometry/raster/components' +import { CANDIDATES, type Candidate } from './models' + +const $ = (id: string) => document.getElementById(id) as T +const log = (s: string) => { $('log').textContent += s + '\n'; console.log(s) } +$('coi').textContent = String(self.crossOriginIsolated) +$('cores').textContent = String(navigator.hardwareConcurrency) +$('gpu').textContent = 'gpu' in navigator ? 'yes' : 'no' +for (const c of CANDIDATES) $('model').append(new Option(c.label, c.id)) + +const sessions = new Map() +ort.env.wasm.proxy = true // вывод в воркере ORT: страница не замирает на время инференса +ort.env.wasm.wasmPaths = 'https://cdn.jsdelivr.net/npm/onnxruntime-web@' + (ort.env.versions?.web ?? 'latest') + '/dist/' + +async function fetchModel(c: Candidate): Promise { + const cache = await caches.open('poc-ai-models') + let r = await cache.match(c.url) + const t0 = performance.now() + if (!r) { + const net = await fetch(c.url) + if (!net.ok) throw new Error(`HTTP ${net.status} for ${c.url}`) + await cache.put(c.url, net.clone()) + r = net + log(`скачано ${(c.bytes / 1e6).toFixed(0)} МБ за ${((performance.now() - t0) / 1000).toFixed(1)} с`) + } else log('модель из Cache API') + const buf = await r.arrayBuffer() + if (buf.byteLength !== c.bytes) log(`ВНИМАНИЕ: размер ${buf.byteLength} ≠ ${c.bytes}`) + return buf +} + +async function loadImage(): Promise { + const f = $('file').files?.[0] + const src = f ? URL.createObjectURL(f) : `./samples/${$('sample').value}.jpeg` + const img = new Image(); img.src = src; await img.decode() + const cv = document.createElement('canvas'); cv.width = img.naturalWidth; cv.height = img.naturalHeight + const g = cv.getContext('2d')!; g.drawImage(img, 0, 0) + return g.getImageData(0, 0, cv.width, cv.height) +} + +/** Нормализованный вход NCHW float32: лист из rgb растра, масштабированный до s × s. */ +function toInput(rect: Rectified, c: Candidate, s: number): Float32Array { + const w = rect.image.width, h = rect.image.height + const src = new ImageData(w, h) + for (let i = 0; i < w * h; i++) { src.data[i * 4] = rect.rgb![i * 3]; src.data[i * 4 + 1] = rect.rgb![i * 3 + 1]; src.data[i * 4 + 2] = rect.rgb![i * 3 + 2]; src.data[i * 4 + 3] = 255 } + const a = document.createElement('canvas'); a.width = w; a.height = h; a.getContext('2d')!.putImageData(src, 0, 0) + const b = document.createElement('canvas'); b.width = s; b.height = s + const bg = b.getContext('2d')!; bg.imageSmoothingQuality = 'high'; bg.drawImage(a, 0, 0, s, s) + const d = bg.getImageData(0, 0, s, s).data + const out = new Float32Array(3 * s * s) + for (let i = 0; i < s * s; i++) for (let k = 0; k < 3; k++) out[k * s * s + i] = (d[i * 4 + k] / 255 - c.mean[k]) / c.std[k] + return out +} + +/** Выход модели s × s → альфа w × h (билинейно), 0…1. Модели с несколькими выходами: берётся первый. */ +function toAlpha(o: Float32Array, s: number, w: number, h: number, sigmoid: boolean): Float32Array { + let v = o + if (sigmoid) v = o.map((x) => 1 / (1 + Math.exp(-x))) + else { let lo = Infinity, hi = -Infinity; for (const x of o) { if (x < lo) lo = x; if (x > hi) hi = x }; if (lo < 0 || hi > 1.0001) { const r = hi - lo || 1; v = o.map((x) => (x - lo) / r) } } + const out = new Float32Array(w * h) + for (let j = 0; j < h; j++) { + const fy = Math.min(s - 1.001, Math.max(0, (j + 0.5) * s / h - 0.5)), y0 = Math.floor(fy), ty = fy - y0 + for (let i = 0; i < w; i++) { + const fx = Math.min(s - 1.001, Math.max(0, (i + 0.5) * s / w - 0.5)), x0 = Math.floor(fx), tx = fx - x0 + const a = v[y0 * s + x0], b = v[y0 * s + x0 + 1], cc = v[(y0 + 1) * s + x0], d = v[(y0 + 1) * s + x0 + 1] + out[j * w + i] = (a * (1 - tx) + b * tx) * (1 - ty) + (cc * (1 - tx) + d * tx) * ty + } + } + return out +} + +interface Sil { id: number; wMm: number; hMm: number; areaMm2: number; bbox: [number, number, number, number] } +function components(mask: Uint8Array, w: number, h: number, scale: number, marginMm: number): { labels: Int32Array; sils: Sil[] } { + const m = new Uint8Array(mask), mp = Math.round(marginMm * scale) + for (let j = 0; j < h; j++) for (let i = 0; i < w; i++) if (i < mp || j < mp || i >= w - mp || j >= h - mp) m[j * w + i] = 0 + const clean = removeSpecks(fillHoles(m, w, h).mask, w, h, Math.round(2 * scale * scale)) + const cc = labelComponents(clean, w, h) + const sils: Sil[] = [] + const bb: [number, number, number, number][] = Array.from({ length: cc.count + 1 }, () => [w, h, -1, -1]) + for (let j = 0; j < h; j++) for (let i = 0; i < w; i++) { const k = cc.labels[j * w + i]; if (k) { const b = bb[k]; if (i < b[0]) b[0] = i; if (j < b[1]) b[1] = j; if (i > b[2]) b[2] = i; if (j > b[3]) b[3] = j } } + const keep = new Int32Array(cc.count + 1); let n = 0 + for (let k = 1; k <= cc.count; k++) { + const b = bb[k], wm = (b[2] - b[0] + 1) / scale, hm = (b[3] - b[1] + 1) / scale, area = cc.sizes[k] / (scale * scale) + if (area >= 60 && wm >= 3 && hm >= 3) { keep[k] = ++n; sils.push({ id: n, wMm: wm, hMm: hm, areaMm2: area, bbox: b }) } + } + const labels = new Int32Array(w * h); for (let i = 0; i < labels.length; i++) labels[i] = keep[cc.labels[i]] + return { labels, sils } +} + +function draw(rect: Rectified, classic: Int32Array, ai: Int32Array) { + const w = rect.image.width, h = rect.image.height + for (const [id, lab, col] of [['c1', classic, [255, 40, 40]], ['c2', ai, [0, 200, 80]]] as const) { + const cv = $(id); cv.width = w; cv.height = h + const g = cv.getContext('2d')!, im = g.createImageData(w, h) + for (let i = 0; i < w * h; i++) { + const edge = lab[i] && (!lab[i - 1] || !lab[i + 1] || !lab[i - w] || !lab[i + w]) + for (let k = 0; k < 3; k++) im.data[i * 4 + k] = edge ? col[k] : lab[i] ? rect.rgb![i * 3 + k] * 0.5 + col[k] * 0.5 : rect.rgb![i * 3 + k] + im.data[i * 4 + 3] = 255 + } + g.putImageData(im, 0, 0) + } +} + +async function run() { + $('log').textContent = ''; $('status').textContent = 'работаю…' + const c = CANDIDATES.find((x) => x.id === $('model').value)! + const ep = $('ep').value, threads = +$('threads').value + const s = +($('size').value || c.size), thr = +$('thr').value, margin = +$('margin').value + log(`модель ${c.id}, лицензия ${c.license}, вход ${s}, бэкенд ${ep}, потоков ${threads}, isolated=${self.crossOriginIsolated}`) + + const im = await loadImage() + const gray = toGrayImage(im.data, im.width, im.height), color = toColorImage(im.data, im.width, im.height) + const found = detectPaper(color) + if (!found) { log('лист не найден'); return } + const guess = guessPaper(found.corners, im.width, im.height) + const paper = paperSize(guess?.preset ?? 'a4', guess?.orientation ?? 'portrait') + const rect = rectify(gray, found.corners, paper, color.rgb)! + const W = rect.image.width, H = rect.image.height + log(`снимок ${im.width}×${im.height}, лист ${paper.width}×${paper.height} мм (${guess?.preset}/${guess?.orientation}), растр ${W}×${H}, ${rect.scale.toFixed(2)} px/мм`) + + let t = performance.now() + const seg = segment(rect) + log(`классика: ${seg.silhouettes.length} силуэтов за ${(performance.now() - t).toFixed(0)} мс`) + + const key = `${c.id}|${ep}|${threads}` + let session = sessions.get(key) + if (!session) { + const buf = await fetchModel(c) + ort.env.wasm.numThreads = threads + t = performance.now() + session = await ort.InferenceSession.create(buf, { executionProviders: [ep], graphOptimizationLevel: 'all' }) + log(`сессия создана за ${(performance.now() - t).toFixed(0)} мс; входы ${session.inputNames}, выходы ${session.outputNames}`) + sessions.set(key, session) + } + const inName = session.inputNames[0] + const data = toInput(rect, c, s) + const runs: number[] = []; let out!: ort.InferenceSession.OnnxValueMapType + for (let i = 0; i < 2; i++) { t = performance.now(); out = await session.run({ [inName]: new ort.Tensor('float32', data.slice(), [1, 3, s, s]) }); runs.push(performance.now() - t) } + log(`инференс: 1-й ${runs[0].toFixed(0)} мс (прогрев), 2-й ${runs[1].toFixed(0)} мс`) + const o = out[session.outputNames[0]] + const alpha = toAlpha(o.data as Float32Array, s, W, H, c.sigmoid) + + const mask = new Uint8Array(W * H); for (let i = 0; i < mask.length; i++) mask[i] = alpha[i] >= thr ? 1 : 0 + const ai = components(mask, W, H, rect.scale, margin) + draw(rect, seg.labels, ai.labels) + + // сравнение: общий IoU и по силуэтам (сопоставление по наибольшему пересечению) + let inter = 0, uni = 0; for (let i = 0; i < W * H; i++) { const a = seg.labels[i] > 0, b = ai.labels[i] > 0; if (a && b) inter++; if (a || b) uni++ } + const rows: string[] = ['классика ммAI ммΔ ширинаΔ высотаIoUAI больше (тень?)AI меньше (потеря?)'] + for (const cs of seg.silhouettes) { + const ov = new Map(); let cArea = 0 + for (let i = 0; i < W * H; i++) if (seg.labels[i] === cs.id) { cArea++; const k = ai.labels[i]; if (k) ov.set(k, (ov.get(k) ?? 0) + 1) } + let best = 0, bo = 0; for (const [k, v] of ov) if (v > bo) { bo = v; best = k } + const a = ai.sils.find((x) => x.id === best) + if (!a) { rows.push(`${cs.widthMm.toFixed(1)}×${cs.heightMm.toFixed(1)}НЕ НАЙДЕН`); continue } + let i2 = 0, u2 = 0, big = 0, small = 0 + for (let i = 0; i < W * H; i++) { const x = seg.labels[i] === cs.id, y = ai.labels[i] === best; if (x && y) i2++; if (x || y) u2++; if (y && !x) big++; if (x && !y) small++ } + const px = scaleMm(rect.scale) + rows.push(`${cs.widthMm.toFixed(1)}×${cs.heightMm.toFixed(1)}${a.wMm.toFixed(1)}×${a.hMm.toFixed(1)}${(a.wMm - cs.widthMm).toFixed(2)}${(a.hMm - cs.heightMm).toFixed(2)}${(i2 / u2).toFixed(3)}${(big * px).toFixed(0)} мм²${(small * px).toFixed(0)} мм²`) + } + const extra = ai.sils.length - seg.silhouettes.length + $('tbl').innerHTML = rows.join('') + `всего: классика ${seg.silhouettes.length}, AI ${ai.sils.length}${extra ? ' (лишние/слитые!)' : ''}; общий IoU ${(inter / uni).toFixed(3)}; инференс ${runs[1].toFixed(0)} мс` + ;(window as unknown as { __poc: unknown }).__poc = { model: c.id, ep, threads, size: s, classic: seg.silhouettes.map((x) => [x.widthMm, x.heightMm, x.areaMm2]), ai: ai.sils.map((x) => [x.wMm, x.hMm, x.areaMm2]), iou: inter / uni, ms: runs } + $('status').textContent = 'готово' +} +const scaleMm = (scale: number) => 1 / (scale * scale) + +$('run').addEventListener('click', () => { run().catch((e) => { log('ОШИБКА: ' + (e?.stack ?? e)); $('status').textContent = 'ошибка' }) }) + +/** Серия прогонов без участия человека: window.__all[ключ] = результат, window.__done = true. */ +;(window as unknown as { runAll: (jobs: [string, string, number, string][]) => void }).runAll = (jobs) => { + const w = window as unknown as { __all: Record; __done: boolean } + w.__all = {}; w.__done = false + ;(async () => { + for (const [m, ep, th, sample] of jobs) { + $('model').value = m; $('ep').value = ep; $('threads').value = String(th); $('sample').value = sample + try { await run(); w.__all[`${m}/${ep}/${th}/${sample}`] = { ...(window as unknown as { __poc: object }).__poc, table: [...document.querySelectorAll('#tbl tr')].slice(1).map((r) => [...r.children].map((c) => c.textContent).join(' | ')), log: $('log').textContent } } + catch (e) { w.__all[`${m}/${ep}/${th}/${sample}`] = { error: String(e) } } + } + w.__done = true + })() +} diff --git a/poc/ai/models.ts b/poc/ai/models.ts new file mode 100644 index 0000000..395c546 --- /dev/null +++ b/poc/ai/models.ts @@ -0,0 +1,28 @@ +/** Кандидаты PoC. Ревизии закреплены (commit hash); размеры из API Hugging Face. */ +export interface Candidate { + id: string + label: string + url: string + bytes: number + license: string + size: number + mean: [number, number, number] + std: [number, number, number] + /** Нужно ли применять sigmoid к выходу (логиты). */ + sigmoid: boolean + /** Имя входа/выхода (null — первый). */ + input?: string + /** Только для сравнения качества: лицензия не позволяет включить в проект. */ + referenceOnly?: boolean +} +const hf = (repo: string, rev: string, file: string) => `https://huggingface.co/${repo}/resolve/${rev}/${file}` +const IMNET = { mean: [0.485, 0.456, 0.406] as [number, number, number], std: [0.229, 0.224, 0.225] as [number, number, number] } +const HALF = { mean: [0.5, 0.5, 0.5] as [number, number, number], std: [1, 1, 1] as [number, number, number] } + +export const CANDIDATES: Candidate[] = [ + { id: 'birefnet-lite-fp16', label: 'BiRefNet-lite fp16 (MIT, 114 МБ)', url: hf('onnx-community/BiRefNet_lite-ONNX', 'de15b22ba131738a16dff04aab8bdf8dc32e3ac1', 'onnx/model_fp16.onnx'), bytes: 114538221, license: 'MIT', size: 1024, ...IMNET, sigmoid: true }, + { id: 'birefnet-lite-fp32', label: 'BiRefNet-lite fp32 (MIT, 224 МБ)', url: hf('onnx-community/BiRefNet_lite-ONNX', 'de15b22ba131738a16dff04aab8bdf8dc32e3ac1', 'onnx/model.onnx'), bytes: 224005088, license: 'MIT', size: 1024, ...IMNET, sigmoid: true }, + { id: 'isnet-general-q', label: 'ISNet general int8 (Apache-2.0, 46 МБ)', url: hf('Ko033/isnet-general-use-onnx', '5349b617911fd60c619b52f32e2b593517b78df3', 'onnx/model_quantized.onnx'), bytes: 45902969, license: 'Apache-2.0', size: 1024, ...HALF, sigmoid: false }, + { id: 'u2netp', label: 'U²-Net-P (Apache-2.0, 4.6 МБ)', url: hf('Heliosoph/u2net-onnx', '7fc34deee10329bc039c10a73b98090d0c6f5c59', 'u2netp.onnx'), bytes: 4574861, license: 'Apache-2.0', size: 320, ...IMNET, sigmoid: false }, + { id: 'rmbg14-q', label: 'RMBG-1.4 int8 (Bria, NON-COMMERCIAL, 44 МБ) — эталон качества', url: hf('briaai/RMBG-1.4', '2ceba5a5efaec153162aedea169f76caf9b46cf8', 'onnx/model_quantized.onnx'), bytes: 44403226, license: 'bria-rmbg-1.4 (non-commercial)', size: 1024, ...HALF, sigmoid: false, referenceOnly: true }, +] diff --git a/poc/ai/samples/flash.jpeg b/poc/ai/samples/flash.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..26b7bbd5983ddc27ef91fff017f0aacf73b97d81 GIT binary patch literal 146570 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 KVFAZ&25PjXuI#$k~ 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

{DC-|P={;#hF&<~OJxDFsuud5ad?0IL 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 ChleZx literal 0 HcmV?d00001 diff --git a/poc/ai/samples/noflash.jpeg b/poc/ai/samples/noflash.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..a19645beac942ae414d52e1b9a84aae653759ad1 GIT binary patch literal 140704 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�rFV5LlNrw=hT!0T=Tv zEZMqQDbWs=W7-(W9ntjxSLi_dVCKQA3@E_r{zw)fITD`i*!O@FPmU>Ig4F34M4c40 z`DU1PG6L&~ZJi;z;$#@8n#T}Cxp`n$z9xq;vA9)-#6Z^(Z%`#d%rdHj?NMS6`w(+I zmlGiYK(H*#Lc7fvi#5clo|5r7EXj<;%3{3cnbIyViCSt067Z2LXAyiGZeJrZ@k}nB zA%Zx-nIJe>kh1J52x_6zu3_7V7(2?*$p_Y6VMQ=tUab`4$#meFgVqpPt z^q5U`gHK5Aq3JkcK9c3sc4ARk#m3L_0;`Bo2(eH{60b6-F$S8BiknP{W9&v#t(RaUArU8>-Y5)Z^&%~w-=>FW@^5tnCTR9#la(% z@iBMh8#{u{Lp|mmxD|H-0>LWgF6D@yrUE&l=7;RZSx7AxO!Z+aSJwCJ&LFX>afD7F zHY5y1u3=ew#Iba!2V6{b)a0C02}Wp_l64}zN~+AQCsf-q&Ba2X<&Cov%t}eBLnffY z{4~Po;g?WQx~O2LT_C6t3?}@dD|4Jjr!t-=TZp+#Gdr^V(OtuE&ZbC+s_`msh~`l- zxto=7>T%Lys$_5K4s$Y8=@x03xUnOwqq19qzkxyahC9`(59Ag_-x+9NGw}?-Qu9Gs z?-&SOh99%sjc5$e2gRQeD$uz>?M!$hy-&HHH02M27kEwDYAsPOoU)ys7?zjM0>T!< zbh8*z`BUX!z@TU-ex@f3V=vS-P`20gfnkGpU)WkSn0ov{pChsULFva|^kJw1V5nJG zjjBmrl`M|)s?0N4hF8B>CaO7T>jo+SE@W3})s`Bl()TPd<$8kV;HKbVaHX8)R`e}L zhwvqcR=J2M%w@1g@(T*QVltC;_Hpv2bRzDYu{-JkRZ|ZD!`+l+CN~`Q%)Ak0^@u>G zUDQn9F*8*Ho2iGK!gLA>!UrP@u1n4Hd%@E-6!BQBbZXmL5a z%)G}2dc@D%;Dz65j=7bqgAUU0#Ijapv8jQXSk$_ge9p+(b2aS)S&ndk-gW01QHtl6 z2=qB2B_Q2t?1Vv9Z@=;^pt>~V{zSNocFHbaRxM$u8(6WwSjDwD3oZar=s!jnP9tFZ z;g>5BoTPhh4ZY(yft3x2)Lm&%pk*v+ z)Nvjx-~46<&|h&p@hQI*zaUar^xVMGXPAcrFEub|ZpnUV3dV@i4yRNYdW33FKaLhCmYOg8$W#o5q+f4bG zG-?cP23uyySgx~!nV=$0a|rGR63WaQ%yR$bb z%D^*kND~SPgE15U@OX0)y7iqPhj!iwlDlAk*kAd65B45CD--NO9YDBykS0tHDh5_o z&UNV-!%5_i@(PtVgb1FDL&}q=VE35wH+a+>uSDOHx=@*|p0KPU-dPm*aiipvqS1fw zq64>o;lv2r<0tGHR`zeV5x86CY~WX2RJtx)H?N3n$a95sm*pHltM59{+p0O03oIbx zm~NSi%)<_1(JZ@)*!hcUota(S45Jimhh|m00wV5Qd=G+jLI@B86gZ7Zue4LMGBxx= z^?tJQ%`+4#WK&RVij2<4XLAFY)fKEe$2AiXc+@+~%)4U6TPd?PxE!-;;K+AL^v^c6z#(`)|hGRBCNkjUZTq2G#6CDLxaeEMuHQ) zexZXx7eX#rD1#c$+!`7BtHH~q)kM}Cn=xLf^~QX_?d_}kDP~`<^2O7JWoZ14C4V6b zsM5P$2|-2EF#N;5D{&tU@vYcl2TgSvRcQ2h{TO25Uc>GGG=?tR#%nF=|YC%$;c;Z$ggsL9%O?WL!7`7qd ztj5J&ehqfTgP1N*U7*ysF5XsvfZ{1wj;QChh%Ia*qfADt25;&^EQN^So1z%pU||_t zL2*`Ur%6;)L)KMN%fiZnT~D$-L_QN zTY#V=wzUI8q?;b&F37r)JJZf>QD>4-e((!%$^-5xv%xF(LN!b7kGPuipur1*^@)uWTmXgQh(;A`*B+5tmFu{RJKexW`pPNXgUKA{QL`ptfLNppkjw~qWdOFtHR%S|&Kp-rdA0E|`yWWTW27$( z9J=u>>ozb6mEv4_qp6kjj#NmD$YK_e@ODSJStxe|cQZ-~W53)bII zzgI&VjAM$0q20S!HQZ_d7jOx_X4uq5srQFTf@a)xRWK(ER>lOuh`G4eAFNi(j^z+> z@-oF?lgz`x2%_KogmnP5r}-ISYt*wFf5cqS9>n~gGNIBA?3@)ZUUQ#GXVL|xKM`Da zh`LL-#K(oiRJF2!m5yagn5E_!%yu`FlIs1ZPV&|*>z91cPpRyfX{vZGi{1^z;& zgmitvTQE09A?*}kw`G0nGa;DaFWM|jz^%Se%dO!lpJhnz zo=Wu?+lRU~{D_eju+OuPkbA>3-8YsEH90y(%BD^u6RaH|(*$N-Y%Lp(;HrAZO?Vy| zNaiB4#w|+nnLf$$9UguXrQ6kYm__i~SLdhd3(FL!mrzkx43dO%3wlM&+47h3ahC_Q7{fc{ER2B0vtPf$oi9;XD`l(mL5A3m49npU1y!}OQZgW_AM1R zG)A4n7=ws1_X&gxZZ;2Cc4q1bBAKYSX&L3x9Jdh+8C~W&=2<9?FzsHETLvHs)xDqv zVBm;;LOe{RLVS9V@Z1m=)Ek&% z2kbuKO|5B6e8Ec(B4Kc%Q-cU;)$If}W-=^&N@3kGGdSKrmjO!C&pEOhYO^Yp;#Jrg8BaOY>An}dph23ZQKl=g}Xhf9tPb2LLZg=C2|#Jcch z3kNfFu#7G&%o8+TfSNMEP7EDQ3$sKz)*&C`5rhNi{{TpZu;!+u7WHpt5}NO)yt4e7 zBasWslpVDxzybqXwMZ1i^M56UqpHIOd zixF`Uc8VqsVkIMVa|^Pb9CYahP7ST+nM$|pRL9JF%Vn3VznQf|!^;Gmx;utPCa1Et<7MG zFQmS*>Ji)=!-I29u>rg@wB zA2YIdn}K!KWW#aP6&GwX7~BQtnC>k&hgbtJUT3f&v6wFzOo7y#S&*3+Oxr-;laJYW zhO+|YQ8LXpH&I+v1#Fh9DQi;7?JYCbY=*J9Ua{r8%vMsRV7To@6BNNRq8doSvke?} zkBB$C>e!hjmQ*;3103&Blww;`M-0A*<4^WW!#Y~_VtFCOMg?6!Pq^9 zGtLMyVX=ag-f1_FGjzs|=a`bm8iq$7!a2mGmwSdt30a6kamwM=Dk+AuxrtO<;}Ovv zGbKzcS;vI8E<$7okhzu!#Bb&%6m-NE26Ge%>XpLB+bE~dgJ@4n$5IZte83{D)+0sD zvM~I_j^%RqCA->$%SuHpf0AGdoF9mD0PiJNq`?sfM9d9kJrNLN4z(Jn2a$%uBW0TQ zm1sU`3Q#%1KPg@y)XckSVrSN9yuwU${EWI> z6P(3O(EY%uYjAADF~q*7lm$%V=FjR~8J@6iaE4Z*d`8uT?Z<=9XeDEDX$Yp_pLlOj zUCu<^HasuGv>TLq=!DYD~^ zY>w0x*`y;e0BMG=LUS=Hi(+cX7F#_fODOswmlt)STUwI?`bR6w-|1u_R5if-SrEuS zGjl|*Rf!Z2Xt^3Unm-V#tR7eUa{^C7zmmW=4HW!B)cN{Z@ixbqr^K^G4d$RG3c05L z01-Z9=LgM77;d;F=vm}`KngDCeu$#LyxLRZV9&0}WR>3&_Q8=yT2tCytBtkO-e512 zQ=5Db1iCY!4m!t7pht!{xV_~BSXOS1DA$4WI1u&%0bY`k^qN%QFw*;qQ*aO5a8qT> z3o|S|C2ug##Iwpr$&>VzWOqK}xGOh1pv=&jM;LKT`nE%W3vUjnKxG=}|Eg*?9M0ZIo#yLG=YOHDK<^ zLL72AA(uJf{6$$tT*>h%MUF6hnP3)iK{sVxj3@x<0bP;Ouj(RJWI22g8W)J`aDh<* z-3Yln;Ac9EStYA4bp;H2A;x9mp}CogqmH5~IGjR~;l!?bK)%Sb-G{BkP0gRER$j4V zQN!M&RDkquG*(7BC6c*@QfpA7h^&g-bA~Pvbt)xha}cb@i)6X39SxU9REFO|7r;0US0({{Ubvgiyq72ViDdCLM6~E()bOsQ^np z<>Sv|6;QBxCRF4-MG*$qwPm7(T9shbbf+Z8b2KvT72#!%lt&ZI7jsq=Rx#pcMRCrF z+Ch|AP*`PBv*j`hns*2)`7?QxA?AF_RKu9BQpz4lfOa($PS?>Kd_EZEmz$i~(ksMw z6BA-Pix`V=2&HoxuSrh;*~f;+f|w!95yMMB@!FveOu$894_S5K6&T)rAaf6>=Q6TJ z4r1dm@{0_MYbePxnC|1;xv7zKnkMq&DD=%DtUXz!98J6n978LO+!EDef-3?n=>Em2 zVaf~^T)1vacIOz_qloQGml=I&`yfq&bTr(%7h9sy)Lam$`vz{jXcc&m6n|hRHE=(O zH~{oji>%S$7wl+lk>MEkisVOzm2B+YkgW&n{=r*T3eSKal9pv`$@v%Lu8R>gzKpAt z$X_`~xsA4>mKPP>b%Wth!I?%XR^e+c#6`?DvjM2E$DD70MP6JE15on=7gH8?B{7aA zW-b8j2qR$2hYZTAP&hCZFPTMsZ{Aogk5;4CwLtR~IfX%zIH|(~wS?^!Ij;u?cr-I8 zd6&xuTtRt*9v)LcnSvP+j+seYReEdTouR)29}eAN)+7!y3)@ge?kOzwBj9Z{{V}C8aH~r2qKfgK>WhDZ=4?CDc%=AeUP4f9i=W8qd<>mfU6IJSV)^s z=>%vT341fxNL}gIAlUP{J?5gzANoYgFli9`l+;D?Rt*--f5H|8_ZRsX#yAv=)3rx~ zw4`&X&iv)dSesb0FBPw8@JCVMm>QNTPZNi#N=L3v@mAs8i*HDEc}+X*5v6;vs9|na zUT)^u?5%v_S2gfe`9Cz6NHk&0>NsA)OD-d!-x~Q9u zi1UfHLvsTtTupHV5apYtW?eN6%23cRhx5VLoXycRXK;)h%gpM*%)_lg3`#0(9IRP# z%(6MUa-~-An8IIaO_xLDh!h+`C`EIah8QY6h20s}38|Ns=|3aDOnLtR1RMp`Y^cFI zwEAV48>KJeDA>oTKV-32cxpYyTsd{+?*X-^Ek7c~2hmgJJq~_S!xSx-R?odmhq1v> zCH8>&Wr#kYt_vMF3-=ELClI$tcl@JVA65^7oo@VtA3$HMG@Hmi*!BTj*YP>2VNCo* z;;t9$M7ncvE!H7ZOuKU%Z%FG91I}SIsOs5?nP7ACU!op1=5m@b4HcL0?!%-9Yi zKvL?WFB!~k4ujTH+O%E2FH!?RDzMB&tEi}YkJ&32m~L4)xEqTEX=6psu`o_1k}S=l zwdFLM!FOcD%#08)&AcArC}6}HVYu`sAINQRSj#S4xR9eSfry|nuM4jL^DHIoRWM2} z1<}?D9va-Dnwc?#zeE23CeyNc&XqZdn}Lgt)SMU4?J62eWegxZIaR{q(cL0YE$@GF zqR8|y?^20PHVN`W_G1h;!vGEBSL8;ERM4No6&l_T;c(kmfj_W@zxeAuiL*If!v6rX zU^vC8E7z*u$XGQyE%_e60r}5mM24Jtgqhrv*Yi-oqu2bDwH5Jek9bW*J;EouyP`b3 zAwGcF^CF;{Mq>=>Hdd#Lioaqy>aWeqqg0`0kFM+84&-U(^ntf43x zg8_xo+&gv)(U?3<8TN=B7LTjRlvk)@!0Nv-rX^G}FB5~o)ki0kak<1C)aef6O|0av z4VMJ4W-K72L!SuGfyUsDNl^jVY=&PPEs--sAm(!Tm2j&wnB$9=f?Zs?>SDy8n=>p} z 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 ady>ilYNDTaY$=B6#1BZar Date: Sat, 17 Oct 2026 21:38:11 +0700 Subject: [PATCH 03/38] poc: owner's Chrome results (RMBG-1.4 best, BiRefNet fails, WebGPU no gain) Co-Authored-By: Claude Sonnet 5.5 --- poc/ai/FINDINGS.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/poc/ai/FINDINGS.md b/poc/ai/FINDINGS.md index f2afd02..7323b2a 100644 --- a/poc/ai/FINDINGS.md +++ b/poc/ai/FINDINGS.md @@ -17,3 +17,20 @@ 4. U²-Net-P: на снимке со вспышкой почти идентичен классике, на снимке без вспышки теряет часть тёмной/синей ручки: именно тот случай, где нужен гибридный край и сравнение с классикой, и где важна модель посильнее. 5. Нюансы реализации: при `wasm.proxy` тензоры и буфер модели передаются (transfer) и становятся detached: тензор создаётся заново на каждый запуск, буфер модели нельзя использовать повторно; без proxy инференс блокирует страницу. 6. Лицензия RMBG: карточки Hugging Face — `license: other` (bria-rmbg-1.4/2.0, «source-available for non-commercial use»), RMBG-2.0 к тому же gated. + +## Прогон владельца в обычном Chrome (flash, 1 нить) + +| Модель | Бэкенд | Результат | Инференс | +|---|---|---|---| +| BiRefNet-lite fp16 / fp32 | WebGPU и WASM | `std::bad_alloc` и в обычном Chrome: не пригодна через ORT Web на 1024 | — | +| RMBG-1.4 int8 (non-commercial) | WASM | «выглядит хорошо» (лучшее качество) | 6.3 с | +| RMBG-1.4 int8 | WebGPU | то же | 14.7 с (медленнее WASM) | +| ISNet general int8 | WASM / WebGPU | кусачки потеряны, 2 из 3 предметов хорошо | 6.4 с / 6.6 с | +| U²-Net-P | WASM | быстро, качество не лучше классики | 0.82 с | +| U²-Net-P | WebGPU | ошибка: MaxPool `ceil_mode` не реализован в WebGPU-ядре | — | + +## Что отсюда следует +1. BiRefNet-lite отпадает как основной вариант (ORT Web, вход 1024, не хватает памяти на обоих бэкендах). +2. WebGPU не даёт выигрыша: для int8-моделей он медленнее или равен WASM, а часть операторов не поддержана. Бэкенд по умолчанию: WASM; WebGPU только как опция после замера. +3. Единственная модель с хорошим результатом, RMBG-1.4, имеет non-commercial лицензию. Разрешённые (MIT/Apache) модели либо не запускаются (BiRefNet), либо хуже классики (ISNet теряет предмет, U²-Net-P не лучше). +4. Потеря целого предмета (ISNet, кусачки) не лечится гибридным краем, потому что тот только уточняет границу уже найденной маски. Если ИИ-движок остаётся, объединение «классика ∪ сеть» или проверка классических силуэтов сетью надёжнее, чем сеть как единственный источник. From baceab22ffe0117bc737abf2085ba3976da90d0b Mon Sep 17 00:00:00 2001 From: skarakovski Date: Sat, 17 Oct 2026 22:00:55 +0700 Subject: [PATCH 04/38] poc: more permissive candidates (BiRefNet-lite 512, ormbg, MVANet, BEN2) Co-Authored-By: Claude Sonnet 5.5 --- poc/ai/main.ts | 6 +++--- poc/ai/models.ts | 9 +++++++++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/poc/ai/main.ts b/poc/ai/main.ts index 2c4ac86..ac4ece0 100644 --- a/poc/ai/main.ts +++ b/poc/ai/main.ts @@ -60,9 +60,9 @@ function toInput(rect: Rectified, c: Candidate, s: number): Float32Array { /** Выход модели s × s → альфа w × h (билинейно), 0…1. Модели с несколькими выходами: берётся первый. */ function toAlpha(o: Float32Array, s: number, w: number, h: number, sigmoid: boolean): Float32Array { - let v = o - if (sigmoid) v = o.map((x) => 1 / (1 + Math.exp(-x))) - else { let lo = Infinity, hi = -Infinity; for (const x of o) { if (x < lo) lo = x; if (x > hi) hi = x }; if (lo < 0 || hi > 1.0001) { const r = hi - lo || 1; v = o.map((x) => (x - lo) / r) } } + // значения вне 0…1 — логиты: sigmoid (признак из реестра подстраховывается самим диапазоном выхода) + let lo = Infinity, hi = -Infinity; for (const x of o) { if (x < lo) lo = x; if (x > hi) hi = x } + const v = sigmoid || lo < -0.001 || hi > 1.001 ? o.map((x) => 1 / (1 + Math.exp(-x))) : o const out = new Float32Array(w * h) for (let j = 0; j < h; j++) { const fy = Math.min(s - 1.001, Math.max(0, (j + 0.5) * s / h - 0.5)), y0 = Math.floor(fy), ty = fy - y0 diff --git a/poc/ai/models.ts b/poc/ai/models.ts index 395c546..6122eee 100644 --- a/poc/ai/models.ts +++ b/poc/ai/models.ts @@ -17,6 +17,7 @@ export interface Candidate { } const hf = (repo: string, rev: string, file: string) => `https://huggingface.co/${repo}/resolve/${rev}/${file}` const IMNET = { mean: [0.485, 0.456, 0.406] as [number, number, number], std: [0.229, 0.224, 0.225] as [number, number, number] } +const ZERO = { mean: [0, 0, 0] as [number, number, number], std: [1, 1, 1] as [number, number, number] } const HALF = { mean: [0.5, 0.5, 0.5] as [number, number, number], std: [1, 1, 1] as [number, number, number] } export const CANDIDATES: Candidate[] = [ @@ -25,4 +26,12 @@ export const CANDIDATES: Candidate[] = [ { id: 'isnet-general-q', label: 'ISNet general int8 (Apache-2.0, 46 МБ)', url: hf('Ko033/isnet-general-use-onnx', '5349b617911fd60c619b52f32e2b593517b78df3', 'onnx/model_quantized.onnx'), bytes: 45902969, license: 'Apache-2.0', size: 1024, ...HALF, sigmoid: false }, { id: 'u2netp', label: 'U²-Net-P (Apache-2.0, 4.6 МБ)', url: hf('Heliosoph/u2net-onnx', '7fc34deee10329bc039c10a73b98090d0c6f5c59', 'u2netp.onnx'), bytes: 4574861, license: 'Apache-2.0', size: 320, ...IMNET, sigmoid: false }, { id: 'rmbg14-q', label: 'RMBG-1.4 int8 (Bria, NON-COMMERCIAL, 44 МБ) — эталон качества', url: hf('briaai/RMBG-1.4', '2ceba5a5efaec153162aedea169f76caf9b46cf8', 'onnx/model_quantized.onnx'), bytes: 44403226, license: 'bria-rmbg-1.4 (non-commercial)', size: 1024, ...HALF, sigmoid: false, referenceOnly: true }, + // второй раунд поиска (permissive-лицензии, ревизии закреплены) + { id: 'birefnet-lite-512-fp16', label: 'BiRefNet-lite 512 fp16 (MIT, 98 МБ)', url: hf('studioludens/birefnet-lite-512', '962e67969ec6acec9c19fab76acee4f5e22e990c', 'onnx/model_fp16.onnx'), bytes: 98484532, license: 'MIT', size: 512, ...IMNET, sigmoid: true }, + { id: 'birefnet-lite-512-fp32', label: 'BiRefNet-lite 512 fp32 (MIT, 192 МБ)', url: hf('studioludens/birefnet-lite-512', '962e67969ec6acec9c19fab76acee4f5e22e990c', 'onnx/model.onnx'), bytes: 191877254, license: 'MIT', size: 512, ...IMNET, sigmoid: true }, + { id: 'ormbg-int8', label: 'ormbg int8 (Apache-2.0, 44 МБ)', url: hf('onnx-community/ormbg-ONNX', '034e2d884afbab897e10e78fc5bb566b29533fd6', 'onnx/model_int8.onnx'), bytes: 44315136, license: 'Apache-2.0', size: 1024, ...ZERO, sigmoid: false }, + { id: 'ormbg-fp16', label: 'ormbg fp16 (Apache-2.0, 88 МБ)', url: hf('onnx-community/ormbg-ONNX', '034e2d884afbab897e10e78fc5bb566b29533fd6', 'onnx/model_fp16.onnx'), bytes: 88117930, license: 'Apache-2.0', size: 1024, ...ZERO, sigmoid: false }, + { id: 'mvanet-q4f16', label: 'MVANet q4f16 (MIT, 85 МБ)', url: hf('onnx-community/MVANet-ONNX', '43ec3427514b8d9164eea02df93ca2f1b036bb7b', 'onnx/model_q4f16.onnx'), bytes: 85027081, license: 'MIT', size: 1024, ...IMNET, sigmoid: true }, + { id: 'mvanet-int8', label: 'MVANet int8 (MIT, 141 МБ)', url: hf('onnx-community/MVANet-ONNX', '43ec3427514b8d9164eea02df93ca2f1b036bb7b', 'onnx/model_int8.onnx'), bytes: 141076192, license: 'MIT', size: 1024, ...IMNET, sigmoid: true }, + { id: 'ben2-fp16', label: 'BEN2 fp16 (MIT, 219 МБ)', url: hf('onnx-community/BEN2-ONNX', 'c552aa82688edce09f0ac9d2e31ad53d9d629010', 'onnx/model_fp16.onnx'), bytes: 219121675, license: 'MIT', size: 1024, ...IMNET, sigmoid: true }, ] From 97a134c25d3412347a0ce9c5970bf71d0b81e42b Mon Sep 17 00:00:00 2001 From: skarakovski Date: Sat, 17 Oct 2026 22:12:48 +0700 Subject: [PATCH 05/38] poc: drop BiRefNet-lite 1024, add ISNet variants, IS-Net, U2Net, InSPyReNet Co-Authored-By: Claude Sonnet 5.5 --- poc/ai/models.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/poc/ai/models.ts b/poc/ai/models.ts index 6122eee..6e9fe38 100644 --- a/poc/ai/models.ts +++ b/poc/ai/models.ts @@ -21,8 +21,6 @@ const ZERO = { mean: [0, 0, 0] as [number, number, number], std: [1, 1, 1] as [n const HALF = { mean: [0.5, 0.5, 0.5] as [number, number, number], std: [1, 1, 1] as [number, number, number] } export const CANDIDATES: Candidate[] = [ - { id: 'birefnet-lite-fp16', label: 'BiRefNet-lite fp16 (MIT, 114 МБ)', url: hf('onnx-community/BiRefNet_lite-ONNX', 'de15b22ba131738a16dff04aab8bdf8dc32e3ac1', 'onnx/model_fp16.onnx'), bytes: 114538221, license: 'MIT', size: 1024, ...IMNET, sigmoid: true }, - { id: 'birefnet-lite-fp32', label: 'BiRefNet-lite fp32 (MIT, 224 МБ)', url: hf('onnx-community/BiRefNet_lite-ONNX', 'de15b22ba131738a16dff04aab8bdf8dc32e3ac1', 'onnx/model.onnx'), bytes: 224005088, license: 'MIT', size: 1024, ...IMNET, sigmoid: true }, { id: 'isnet-general-q', label: 'ISNet general int8 (Apache-2.0, 46 МБ)', url: hf('Ko033/isnet-general-use-onnx', '5349b617911fd60c619b52f32e2b593517b78df3', 'onnx/model_quantized.onnx'), bytes: 45902969, license: 'Apache-2.0', size: 1024, ...HALF, sigmoid: false }, { id: 'u2netp', label: 'U²-Net-P (Apache-2.0, 4.6 МБ)', url: hf('Heliosoph/u2net-onnx', '7fc34deee10329bc039c10a73b98090d0c6f5c59', 'u2netp.onnx'), bytes: 4574861, license: 'Apache-2.0', size: 320, ...IMNET, sigmoid: false }, { id: 'rmbg14-q', label: 'RMBG-1.4 int8 (Bria, NON-COMMERCIAL, 44 МБ) — эталон качества', url: hf('briaai/RMBG-1.4', '2ceba5a5efaec153162aedea169f76caf9b46cf8', 'onnx/model_quantized.onnx'), bytes: 44403226, license: 'bria-rmbg-1.4 (non-commercial)', size: 1024, ...HALF, sigmoid: false, referenceOnly: true }, @@ -34,4 +32,11 @@ export const CANDIDATES: Candidate[] = [ { id: 'mvanet-q4f16', label: 'MVANet q4f16 (MIT, 85 МБ)', url: hf('onnx-community/MVANet-ONNX', '43ec3427514b8d9164eea02df93ca2f1b036bb7b', 'onnx/model_q4f16.onnx'), bytes: 85027081, license: 'MIT', size: 1024, ...IMNET, sigmoid: true }, { id: 'mvanet-int8', label: 'MVANet int8 (MIT, 141 МБ)', url: hf('onnx-community/MVANet-ONNX', '43ec3427514b8d9164eea02df93ca2f1b036bb7b', 'onnx/model_int8.onnx'), bytes: 141076192, license: 'MIT', size: 1024, ...IMNET, sigmoid: true }, { id: 'ben2-fp16', label: 'BEN2 fp16 (MIT, 219 МБ)', url: hf('onnx-community/BEN2-ONNX', 'c552aa82688edce09f0ac9d2e31ad53d9d629010', 'onnx/model_fp16.onnx'), bytes: 219121675, license: 'MIT', size: 1024, ...IMNET, sigmoid: true }, + // третий раунд: убраны BiRefNet-lite 1024 (std::bad_alloc на обоих бэкендах у владельца) + { id: 'isnet-imgly-fp16', label: 'ISNet general imgly fp16 (MIT, 88 МБ)', url: hf('imgly/isnet-general-onnx', '440dea96dd4a3b06bbbf5abec3e26569dd7ec49f', 'onnx/model_fp16.onnx'), bytes: 88152708, license: 'MIT', size: 1024, mean: [0.502, 0.502, 0.502], std: [1.004, 1.004, 1.004], sigmoid: false }, + { id: 'isnet-imgly-fp32', label: 'ISNet general imgly fp32 (MIT, 176 МБ)', url: hf('imgly/isnet-general-onnx', '440dea96dd4a3b06bbbf5abec3e26569dd7ec49f', 'onnx/model.onnx'), bytes: 176149806, license: 'MIT', size: 1024, mean: [0.502, 0.502, 0.502], std: [1.004, 1.004, 1.004], sigmoid: false }, + { id: 'isnet-pim-q', label: 'ISNet pim-rmbg int8 (Apache-2.0, 44 МБ)', url: hf('drodriguezgdu/pim-rmbg-isnet', 'b6422ea5d8a184f0340f7a8dbf5c3a64eee95239', 'onnx/model_quantized.onnx'), bytes: 44229662, license: 'Apache-2.0', size: 1024, mean: [0.502, 0.502, 0.502], std: [1.004, 1.004, 1.004], sigmoid: false }, + { id: 'isnet-bw-fp32', label: 'IS-Net BritishWerewolf fp32 (Apache-2.0, 179 МБ)', url: hf('BritishWerewolf/IS-Net', '9783722d9f964c0286a411e7e8e6fede947d5a53', 'onnx/model.onnx'), bytes: 178648008, license: 'Apache-2.0', size: 1024, mean: [0.485, 0.456, 0.406], std: [1, 1, 1], sigmoid: false }, + { id: 'u2net-bw-fp32', label: 'U²-Net полная (Apache-2.0, 176 МБ)', url: hf('BritishWerewolf/U-2-Net', 'c08042b462485d8597c89d3ce831d00be8582c5c', 'onnx/model.onnx'), bytes: 175997641, license: 'Apache-2.0', size: 320, ...IMNET, sigmoid: false }, + { id: 'inspyrenet-fp16', label: 'InSPyReNet SwinB Plus Ultra fp16 (MIT, 199 МБ)', url: hf('OS-Software/InSPyReNet-SwinB-Plus-Ultra-ONNX', 'fe628d2d1465f775c707368a80248a2d5b5ad71f', 'onnx/model_fp16.onnx'), bytes: 199133027, license: 'MIT', size: 1024, ...IMNET, sigmoid: false }, ] From faf37d9684f4b61d27dfd88d39352a4cdce6d20a Mon Sep 17 00:00:00 2001 From: skarakovski Date: Sat, 17 Oct 2026 22:13:21 +0700 Subject: [PATCH 06/38] plan: 32-photo-trace-ai model manager window, licence classes, PoC outcome Co-Authored-By: Claude Sonnet 5.5 --- openspec/changes/32-photo-trace-ai/design.md | 10 +++-- .../specs/cutout-photo-ai/spec.md | 43 ++++++++++++++----- openspec/changes/32-photo-trace-ai/tasks.md | 9 ++-- 3 files changed, 44 insertions(+), 18 deletions(-) diff --git a/openspec/changes/32-photo-trace-ai/design.md b/openspec/changes/32-photo-trace-ai/design.md index e2877fd..869a96b 100644 --- a/openspec/changes/32-photo-trace-ai/design.md +++ b/openspec/changes/32-photo-trace-ai/design.md @@ -21,7 +21,7 @@ Why the straightened sheet is a good input for a network: it is a top view at tr - **Engine as data, not as a fork.** `type SegEngine = 'classic' | 'ai'` lives in `PhotoSettings.engine` (default `'classic'`). `usePhotoTrace` keeps `segmentation` (classic, as now) and adds `aiSegmentation` computed only when the engine is `ai` or the compare view is on; the exposed `active` segmentation is chosen by the engine. `segment.ts`, `colour.ts`, `outline.ts` are not modified, only imported. Alternative: an `if` inside `segment()`. Rejected: it would touch the baseline and make its regression harder to prove. - **Input and resolution.** The rectified RGB is resampled to the model's input (typically 512 or 1024 px for BiRefNet-lite, 320 for U²-Net-P), run, and the alpha is resampled back to the 4 px/mm raster (bilinear). A4 at 1024 px is ≈ 0.29 mm/px, so the network edge is only good to about ±0.5 mm: this is why the edge is refined (next point) and why the result is never used raw for the default. -- **Model choice by measurement (task 1).** Candidates from the research, all permissive: BiRefNet-lite fp16 (MIT, 114 MB, sharpest edges, WebGPU best), ISNet general / U²-Net-P (Apache-2.0 / MIT, 5–45 MB, WASM-friendly), MODNet is portrait-only and excluded. Excluded for licence: RMBG-1.4/2.0 (non-commercial), which `PREPROP_CUTOUT_GOOGLE.md` recommends, and `finegrain-box-segmenter` (396 MB, the author advises against client use). The default AI engine uses the best candidate that reaches the targets in the table below; a second, smaller model may be offered as "lighter". If none reaches them the change stops after task 1 and records the numbers (the change is then archived as a negative result without a version bump, via `/opsx:update`). +- **Model choice by measurement (task 1).** The PoC (`poc/ai/`, results in `poc/ai/FINDINGS.md`) ran the candidates on the owner's photos: BiRefNet-lite at 1024 fails with `std::bad_alloc` on both backends and is dropped; RMBG-1.4 (non-commercial) gave the best result in ~6 s on WASM; ISNet loses a whole tool; U²-Net-P is fast but not better than the classic path; WebGPU brings no gain over WASM. Several models can be offered to the user in the manager; the set is fixed by the PoC rounds (BiRefNet-lite 512, ormbg, MVANet, ISNet and IS-Net variants, InSPyReNet are the next candidates). MODNet is portrait-only and excluded; `finegrain-box-segmenter` (396 MB) is excluded for size. The default AI engine uses the best candidate that reaches the targets in the table below; a second, smaller model may be offered as "lighter". If none reaches them the change stops after task 1 and records the numbers (the change is then archived as a negative result without a version bump, via `/opsx:update`). | Target on the two fixtures | Value | |---|---| @@ -35,8 +35,10 @@ Why the straightened sheet is a good input for a network: it is a top view at tr - **Hybrid edge.** For each silhouette: band = mask dilated by `band` mm (default 1.5) minus mask eroded by `band`. Inside the band the classic paper-colour deviation (`colourMask` from the paper surface fitted by `paperSurface`; `paperSurface` and `colourMask` are called, not changed) classifies pixels; the refined mask is `eroded core ∪ (band ∩ classic object pixels)`. If the classic test marks the whole band as paper or as object on a stretch longer than a few mm (the dark-part-in-shadow case), that stretch keeps the network edge. The refined labels go through the same `traceAlpha` as today. A setting `refine: on|off` (default on) exists in the compare view so the raw network edge can be inspected; it is not a persisted user option beyond the session. - **Compare view.** `CompareOverlay` draws the other engine's silhouette outlines (already traced by `traceSilhouette`) as dashed lines over the straightened view, plus a table "tool: classic mm / AI mm / Δ". It costs one extra segmentation when open, and is the way the user, and the owner while developing, sees where the engines disagree. - **ML runtime.** `src/ml/` is React-free. `matte(rgb, w, h) → Float32 alpha` is the only adapter this change needs. `ml.worker.ts` owns the inference session; `mlClient.ts` mirrors `modelClient.ts` (latest request wins, stale replies dropped); the worker is created lazily so classic users pay nothing, not even the library chunk (dynamic import). Backend: try WebGPU, fall back to WASM single-threaded (no COOP/COEP on Pages); report the backend in the model manager. -- **Model manager and consent.** First use of AI opens `ModelDialog`: model name, size, source host (`huggingface.co`), the statement that the request reveals the user's IP to that host while the photo and the result stay in the browser, buttons Download / Cancel. Download shows progress and can be cancelled. Weights are cached with the Cache API under a versioned key (revision hash), listed with size and a Delete button in the same dialog (opened from the engine switch and from the footer's settings). If the cache is unavailable the model is used for the session and a notice says it will be downloaded again. Nothing downloads without the click. -- **Pinned weights.** The registry (`src/ml/models.ts`) holds per model: id, URL with a commit hash (not `main`), byte size, SHA-256 (verified after download), input size and normalisation, SPDX licence, source. A model enters the registry only with MIT / Apache-2.0 / BSD / CC0. The References page and README list them from this registry (a test checks every registry entry appears). +- **Model manager (a window of its own).** `ModelManager` is a dialog reachable from the engine switch and from the app footer. It lists every model of the registry with: name, what it is good for (fast / accurate), download size, licence class, status (not downloaded / downloading with progress / ready / failed), and the buttons Download, Cancel, Delete, plus the total space used and a "Delete all". Several models can be downloaded at the same time as separate entries; the user picks the active one for the AI engine (a radio in the list). Weights live on the client only: Cache API under a versioned key (revision hash), with a memory fallback and a notice when the browser refuses storage. Nothing downloads without the click; choosing AI with no model ready opens the manager. The first download of a model shows its consent text (below). +- **Licence classes and consent.** Every registry entry has `licenseClass`: `permissive` (MIT / Apache-2.0 / BSD / CC0), `noncommercial` (e.g. RMBG-1.4, Bria) or `copyleft` (AGPL/GPL). The consent text is per class: `permissive` shows name, size, source host and licence; `noncommercial` additionally requires ticking "I use this model for non-commercial purposes and accept its licence" (link to the licence); `copyleft` states the licence and that the model is not part of PrintKit. Non-permissive models are never the default, never bundled, never mirrored or hosted by PrintKit: they are fetched straight from the rights holder's repository at a pinned revision, and a separate "Optional third-party models" section on the References page and in the README lists them as outside the project's MIT licence. Removing one is a one-line change in the registry. Why this is allowed: the app ships generic inference code and never redistributes the weights; the licence terms apply to the user's own use of the model, which the user accepts explicitly. This is a technical arrangement, not legal advice. AGPL models are admissible in the same way (the model file is data for a generic runtime, no AGPL code is linked or shipped), but no AGPL model is a candidate today: the YOLO segmentation weights only know COCO classes and cannot outline pliers, and the AGPL tag on `onnx-community/ISNet-ONNX` duplicates an Apache-2.0 model that is already in the list. +- **Pinned weights.** The registry (`src/ml/models.ts`) holds per model: id, URL with a commit hash (not `main`), byte size, SHA-256 (verified after download), input size and normalisation, SPDX licence, source. A model enters the registry with its `licenseClass` stated; the default AI model must be `permissive`, non-permissive ones are optional extras. The References page and README list them from this registry (a test checks every registry entry appears). +- **Default backend.** WASM in the ML worker (single thread); WebGPU is an option only for models that were measured to gain from it, and falls back to WASM on an unsupported operator. - **Mobile and weak devices.** The AI option is available at any width, but the dialog states the size, and on narrow screens or without WebGPU the lighter model is suggested first. The classic engine is always one click away and is what runs if the AI path fails (error banner, `active` falls back to classic). - **Testing.** Unit tests never run a network. Frozen model outputs (alpha PNGs produced by the harness for each fixture and each chosen model, a few hundred KB) are committed under `geometry/photo/fixtures/ai/` and drive the post-processing tests. Component tests mock `mlClient`. The classic baseline has a new frozen-output test (silhouette count, bbox, area per fixture) next to the existing `photoFixtures.test.ts`, so any accidental change to it fails. - **Rollout.** The engine switch is always visible, Classic selected. No flag. @@ -50,7 +52,7 @@ Why the straightened sheet is a good input for a network: it is a top view at tr - [Library size and WASM files bloat the bundle] → dynamic import in the worker only on first AI use; the classic start-up path does not load them (checked in the build output size and the network panel). - [Hugging Face availability, revision changes] → pinned hashes and SHA-256; on a failed download the error says which host failed and classic keeps working. - [Single-threaded WASM is slow] → progress indicator, `computing` flag as for the classic path; measured in task 1, the lighter model is the WASM default. -- [Licence drift] → registry test refuses entries without an allowed SPDX id. +- [Licence drift] → registry test refuses entries without a stated SPDX id, class and pinned revision; a non-`permissive` entry can never be marked default. ## Open Questions diff --git a/openspec/changes/32-photo-trace-ai/specs/cutout-photo-ai/spec.md b/openspec/changes/32-photo-trace-ai/specs/cutout-photo-ai/spec.md index 26e9135..ede78ca 100644 --- a/openspec/changes/32-photo-trace-ai/specs/cutout-photo-ai/spec.md +++ b/openspec/changes/32-photo-trace-ai/specs/cutout-photo-ai/spec.md @@ -35,31 +35,54 @@ On the silhouette step the user SHALL be able to choose the detection engine, Cl - **WHEN** the AI engine is active - **THEN** the sensitivity control, which belongs to the classic detector, is not offered for it, and the margin still applies +### Requirement: Model manager +The app SHALL have a model manager window, reachable from the engine switch and from the footer, that lists the available models with their name, purpose, size, licence class and status, and lets the user download, cancel, delete and choose the model used by the AI engine. Several models SHALL be downloadable and stored side by side. The models SHALL be kept in the browser's storage on the device; the window SHALL show the space they use and offer to delete all of them. + +#### Scenario: List +- **WHEN** the manager is opened +- **THEN** every model is listed with its size, licence class and status + +#### Scenario: Download and delete +- **WHEN** the user downloads a model and later deletes it +- **THEN** progress is shown while downloading, the model is then ready for use, and after deleting its stored data is removed and it shows as not downloaded + +#### Scenario: Choice +- **WHEN** two models are downloaded +- **THEN** the user picks which one the AI engine uses + +#### Scenario: No model yet +- **WHEN** the user chooses the AI engine and no model is ready +- **THEN** the manager opens, and nothing is downloaded until the user starts a download + ### Requirement: Model download and consent -The AI engine SHALL need a neural model that is downloaded on demand. Before the first download the app SHALL show the model's name, size, source host and licence and SHALL download only after the user confirms. It SHALL show progress, allow cancelling, keep the model in the browser's storage for later sessions when storage is available, and let the user see its size and delete it. If the download or the model fails, the classic engine SHALL stay usable and the failure SHALL be shown. +A model SHALL be downloaded only after the user confirms. Before the first download of a model the app SHALL show its name, size, source host and licence. For a model whose licence restricts use to non-commercial purposes, the app SHALL additionally require the user to confirm that the model is used for non-commercial purposes and that its licence is accepted, and SHALL NOT start the download without it. Such models, and models under copyleft licences, SHALL never be the default, SHALL NOT be bundled with or hosted by the app, SHALL be fetched from the rights holder's repository at a fixed revision, and SHALL be listed on the References page as third-party models outside the app's own licence. Downloads SHALL show progress and be cancellable. If the download or the model fails, or the file's checksum does not match, the classic engine SHALL stay usable and the failure SHALL be shown. #### Scenario: Consent first -- **WHEN** the user chooses AI for the first time -- **THEN** a dialog states the download size and that the source host sees the user's address, and nothing is downloaded until the user confirms +- **WHEN** the user starts the first download of a model +- **THEN** a dialog states the size and that the source host sees the user's address, and nothing is downloaded until the user confirms + +#### Scenario: Non-commercial model +- **WHEN** the user starts the download of a model with a non-commercial licence +- **THEN** the Download button stays disabled until the user ticks the non-commercial-use and licence confirmation, and the dialog links the licence + +#### Scenario: Third-party label +- **WHEN** the References page is opened +- **THEN** models that are not under a permissive licence appear in a separate section as optional third-party models not covered by the app's licence #### Scenario: Cached - **WHEN** the model was downloaded earlier and the storage is intact -- **THEN** choosing AI starts at once without a new download - -#### Scenario: Delete -- **WHEN** the user deletes the model in the model dialog -- **THEN** its stored data is removed and choosing AI asks for consent again +- **THEN** choosing it starts at once without a new download #### Scenario: Failure - **WHEN** the download is interrupted or the checksum of the file does not match -- **THEN** an error says so, the file is not used, and the dialog stays on the classic engine +- **THEN** an error says so, the file is not used, and the app stays on the classic engine #### Scenario: Storage unavailable - **WHEN** the browser refuses persistent storage - **THEN** the model works for the session and a notice says it will be downloaded again next time ### Requirement: Privacy -The photo, the straightened image and the masks SHALL be processed in the browser only and SHALL NOT be sent anywhere by the AI engine. The only network traffic added by the AI engine SHALL be the download of the model files, from a fixed, versioned location, after consent. The user interface and the References page SHALL say so, and SHALL list every model with its licence and source. Only models under MIT, Apache-2.0, BSD or CC0 licences SHALL be used. +The photo, the straightened image and the masks SHALL be processed in the browser only and SHALL NOT be sent anywhere by the AI engine. The only network traffic added by the AI engine SHALL be the download of the model files, from a fixed, versioned location, after consent. The user interface and the References page SHALL say so, and SHALL list every model with its licence and source. The default AI model SHALL be under a permissive licence (MIT, Apache-2.0, BSD or CC0). #### Scenario: No image upload - **WHEN** a photo is processed by the AI engine diff --git a/openspec/changes/32-photo-trace-ai/tasks.md b/openspec/changes/32-photo-trace-ai/tasks.md index 61b7ad0..1492062 100644 --- a/openspec/changes/32-photo-trace-ai/tasks.md +++ b/openspec/changes/32-photo-trace-ai/tasks.md @@ -2,6 +2,7 @@ ## 1. Spike: do the models beat the baseline? (gate) +- [x] 1.0 PoC page `poc/ai/` (`npm run poc:ai`): candidates run in the owner's browser on both photos; results in `poc/ai/FINDINGS.md`. BiRefNet-lite 1024 dropped (bad_alloc). - [ ] 1.1 Evaluation harness `scripts/eval-photo-ai.ts` (not shipped, not run by CI): for each fixture in `geometry/photo/fixtures` and for the owner's originals in `tmp/ai/photo_cutout_*.jpeg`, rectify, run the classic `segment` and each candidate model (BiRefNet-lite fp16, ISNet general, U²-Net-P; Node with `onnxruntime-node`), and print per silhouette: size in mm, IoU against the classic result, "network larger" and "network smaller" area, and time. Write each model's alpha for the fixtures to `geometry/photo/fixtures/ai/-.png`. Verify: the script prints the table for both fixtures. - [ ] 1.2 Record the table and the decision in `design.md` (Decisions → Model choice): the default AI model, the lighter model, the pinned revisions and licences, the measured sizes of the files. Verify against the targets in `design.md`. If no model reaches the raw targets (3 tools, ≤ 1 mm) stop here, update the artifacts with `/opsx:update` and close the change as a negative result. @@ -16,16 +17,16 @@ ## 4. ML runtime -- [ ] 4.1 `src/ml/models.ts` registry (id, URL with commit hash, bytes, SHA-256, input size, normalisation, SPDX, source) and a test that refuses entries without an allowed SPDX id or without a pinned revision. Verify: test green. +- [ ] 4.1 `src/ml/models.ts` registry (id, URL with commit hash, bytes, SHA-256, input size, normalisation, SPDX, `licenseClass` permissive | noncommercial | copyleft, purpose, source) and a test that refuses entries without SPDX, class or pinned revision and a non-permissive default. Verify: test green. - [ ] 4.2 `src/ml/runtime.ts` and `ml.worker.ts`: lazy session creation by dynamic import, WebGPU then WASM single-thread, `matte(rgb, w, h) → Float32Array`, resize to the model input and back. `mlClient.ts`: latest request wins, stale replies dropped, progress and cancel. Tests with a fake worker for ordering and cancel; pre/post-processing tested on a tiny fixed array. Verify in the browser that a classic-only session loads no ML chunk (network panel and build output). -- [ ] 4.3 `src/ml/modelStore.ts`: download with progress and cancel, SHA-256 check, Cache API storage under the revision key, list, delete, `persistent` flag with memory fallback. Tests with a mocked `fetch` and cache: checksum mismatch rejects, delete removes, interrupted download leaves nothing, no storage falls back to memory. +- [ ] 4.3 `src/ml/modelStore.ts`: download with progress and cancel, SHA-256 check, Cache API storage under the revision key, list, delete, `persistent` flag with memory fallback. Tests with a mocked `fetch` and cache, several models side by side: checksum mismatch rejects, delete removes, interrupted download leaves nothing, no storage falls back to memory. ## 5. UI - [ ] 5.1 `usePhotoTrace`: `engine` in `PhotoSettings` (default `classic`), `aiSegmentation` computed only when needed, an `active` segmentation, AI error falls back to classic with an error state, `computing` covers the AI run. Tests: default is classic and calls no model; switching uses the mocked client; a failing client leaves classic active; closing and reopening keeps the engine. -- [ ] 5.2 `EngineSwitch` on the silhouette step (SegmentedControl Classic | AI, sensitivity hidden for AI) and `ModelDialog` (consent text with size, host, licence and the IP note, progress, cancel, delete, backend, cached state). No artboard exists: built from `ui/` components and `theme.css` tokens, said in the commit; compare with the dialog in en and ru and at 390 px. Component tests: consent before any download, progress, error banner, delete asks again for consent, steppers not needed (no numeric inputs). +- [ ] 5.2 `EngineSwitch` on the silhouette step (SegmentedControl Classic | AI, sensitivity hidden for AI) and `ModelManager` dialog (list with name, purpose, size, licence class, status; Download / Cancel / Delete / Delete all, active-model radio, used space, backend; per-class consent: the non-commercial confirmation checkbox gates Download), reachable from the switch and the footer. No artboard exists: built from `ui/` components and `theme.css` tokens, said in the commit; compare in en and ru and at 390 px. Component tests: consent before any download, the non-commercial checkbox disables and enables Download, progress, error banner, delete returns to "not downloaded" and asks for consent again, two models side by side, choosing the active one. - [ ] 5.3 `CompareOverlay` and the table of sizes (classic, AI, Δ) in the silhouette step; dashed outlines of the inactive engine over the straightened view; off by default. Tests: toggling draws the second outline set and the table values equal `silhouettes` of both engines; off costs no AI run. -- [ ] 5.4 Strings in `en.json` and `ru.json` (engine, consent, progress, errors, compare, References licence list); the References page and README list the registry models. Tests: key parity, a registry-to-References test, the `PhotoTips` and consent text exist in both languages. +- [ ] 5.4 Strings in `en.json` and `ru.json` (engine, consent, progress, errors, compare, References licence list); the References page and README list the registry models, non-permissive ones in a separate section. Tests: key parity, a registry-to-References test, the `PhotoTips` and consent text exist in both languages. ## 6. Version From 4498367c0833f143b8e6f7d4680a431c03b93a17 Mon Sep 17 00:00:00 2001 From: skarakovski Date: Sun, 18 Oct 2026 01:33:26 +0700 Subject: [PATCH 07/38] poc: round 2 results, drop MVANet/BEN2; plan: per-model backends Co-Authored-By: Claude Sonnet 5.5 --- openspec/changes/32-photo-trace-ai/design.md | 4 ++-- openspec/changes/32-photo-trace-ai/tasks.md | 3 ++- poc/ai/FINDINGS.md | 18 ++++++++++++++++++ poc/ai/models.ts | 3 --- 4 files changed, 22 insertions(+), 6 deletions(-) diff --git a/openspec/changes/32-photo-trace-ai/design.md b/openspec/changes/32-photo-trace-ai/design.md index 869a96b..d18db32 100644 --- a/openspec/changes/32-photo-trace-ai/design.md +++ b/openspec/changes/32-photo-trace-ai/design.md @@ -21,7 +21,7 @@ Why the straightened sheet is a good input for a network: it is a top view at tr - **Engine as data, not as a fork.** `type SegEngine = 'classic' | 'ai'` lives in `PhotoSettings.engine` (default `'classic'`). `usePhotoTrace` keeps `segmentation` (classic, as now) and adds `aiSegmentation` computed only when the engine is `ai` or the compare view is on; the exposed `active` segmentation is chosen by the engine. `segment.ts`, `colour.ts`, `outline.ts` are not modified, only imported. Alternative: an `if` inside `segment()`. Rejected: it would touch the baseline and make its regression harder to prove. - **Input and resolution.** The rectified RGB is resampled to the model's input (typically 512 or 1024 px for BiRefNet-lite, 320 for U²-Net-P), run, and the alpha is resampled back to the 4 px/mm raster (bilinear). A4 at 1024 px is ≈ 0.29 mm/px, so the network edge is only good to about ±0.5 mm: this is why the edge is refined (next point) and why the result is never used raw for the default. -- **Model choice by measurement (task 1).** The PoC (`poc/ai/`, results in `poc/ai/FINDINGS.md`) ran the candidates on the owner's photos: BiRefNet-lite at 1024 fails with `std::bad_alloc` on both backends and is dropped; RMBG-1.4 (non-commercial) gave the best result in ~6 s on WASM; ISNet loses a whole tool; U²-Net-P is fast but not better than the classic path; WebGPU brings no gain over WASM. Several models can be offered to the user in the manager; the set is fixed by the PoC rounds (BiRefNet-lite 512, ormbg, MVANet, ISNet and IS-Net variants, InSPyReNet are the next candidates). MODNet is portrait-only and excluded; `finegrain-box-segmenter` (396 MB) is excluded for size. The default AI engine uses the best candidate that reaches the targets in the table below; a second, smaller model may be offered as "lighter". If none reaches them the change stops after task 1 and records the numbers (the change is then archived as a negative result without a version bump, via `/opsx:update`). +- **Model choice by measurement (task 1).** The PoC (`poc/ai/`, results in `poc/ai/FINDINGS.md`) ran the candidates on the owner's photos: BiRefNet-lite at 1024 fails with `std::bad_alloc` on both backends and is dropped; RMBG-1.4 (non-commercial) gave the best result in ~6 s on WASM; ISNet loses a whole tool; U²-Net-P is fast but not better than the classic path; WebGPU brings no gain over WASM. Second round: InSPyReNet fp16 (MIT, WebGPU ~2 s, good), ormbg (Apache-2.0, WASM 15–20 s, good), ISNet imgly fp16 (MIT, WebGPU 0.25 s, misses part of the cutters), U²-Net full (shadow leaks); MVANet and BEN2 mark the whole sheet and are dropped. The set offered in the manager comes from these results: InSPyReNet fp16 and ormbg as the permissive recommendations, ISNet imgly fp16 as the fast option, RMBG-1.4 as the optional non-commercial one. A note: most networks lose part of the cutters, which the classic path finds, so the AI engine is an alternative for hard photos, not a better default. MODNet is portrait-only and excluded; `finegrain-box-segmenter` (396 MB) is excluded for size. The default AI engine uses the best candidate that reaches the targets in the table below; a second, smaller model may be offered as "lighter". If none reaches them the change stops after task 1 and records the numbers (the change is then archived as a negative result without a version bump, via `/opsx:update`). | Target on the two fixtures | Value | |---|---| @@ -38,7 +38,7 @@ Why the straightened sheet is a good input for a network: it is a top view at tr - **Model manager (a window of its own).** `ModelManager` is a dialog reachable from the engine switch and from the app footer. It lists every model of the registry with: name, what it is good for (fast / accurate), download size, licence class, status (not downloaded / downloading with progress / ready / failed), and the buttons Download, Cancel, Delete, plus the total space used and a "Delete all". Several models can be downloaded at the same time as separate entries; the user picks the active one for the AI engine (a radio in the list). Weights live on the client only: Cache API under a versioned key (revision hash), with a memory fallback and a notice when the browser refuses storage. Nothing downloads without the click; choosing AI with no model ready opens the manager. The first download of a model shows its consent text (below). - **Licence classes and consent.** Every registry entry has `licenseClass`: `permissive` (MIT / Apache-2.0 / BSD / CC0), `noncommercial` (e.g. RMBG-1.4, Bria) or `copyleft` (AGPL/GPL). The consent text is per class: `permissive` shows name, size, source host and licence; `noncommercial` additionally requires ticking "I use this model for non-commercial purposes and accept its licence" (link to the licence); `copyleft` states the licence and that the model is not part of PrintKit. Non-permissive models are never the default, never bundled, never mirrored or hosted by PrintKit: they are fetched straight from the rights holder's repository at a pinned revision, and a separate "Optional third-party models" section on the References page and in the README lists them as outside the project's MIT licence. Removing one is a one-line change in the registry. Why this is allowed: the app ships generic inference code and never redistributes the weights; the licence terms apply to the user's own use of the model, which the user accepts explicitly. This is a technical arrangement, not legal advice. AGPL models are admissible in the same way (the model file is data for a generic runtime, no AGPL code is linked or shipped), but no AGPL model is a candidate today: the YOLO segmentation weights only know COCO classes and cannot outline pliers, and the AGPL tag on `onnx-community/ISNet-ONNX` duplicates an Apache-2.0 model that is already in the list. - **Pinned weights.** The registry (`src/ml/models.ts`) holds per model: id, URL with a commit hash (not `main`), byte size, SHA-256 (verified after download), input size and normalisation, SPDX licence, source. A model enters the registry with its `licenseClass` stated; the default AI model must be `permissive`, non-permissive ones are optional extras. The References page and README list them from this registry (a test checks every registry entry appears). -- **Default backend.** WASM in the ML worker (single thread); WebGPU is an option only for models that were measured to gain from it, and falls back to WASM on an unsupported operator. +- **Backend per model.** The registry lists for each model the backends it works on and the preferred one, from the PoC: InSPyReNet fp16 and ISNet imgly fp16 are WebGPU-only (fp16 on WASM gives an empty mask or minutes), ormbg and U²-Net fail on WebGPU (`ceil_mode` in MaxPool) and run on WASM, RMBG-1.4 int8 runs on WASM faster than on WebGPU. The manager shows only models that the device can run (WebGPU and `shader-f16` detected at start) and marks the others as unavailable with the reason; a run that fails on the preferred backend is not silently retried on another unless the registry says it works there. - **Mobile and weak devices.** The AI option is available at any width, but the dialog states the size, and on narrow screens or without WebGPU the lighter model is suggested first. The classic engine is always one click away and is what runs if the AI path fails (error banner, `active` falls back to classic). - **Testing.** Unit tests never run a network. Frozen model outputs (alpha PNGs produced by the harness for each fixture and each chosen model, a few hundred KB) are committed under `geometry/photo/fixtures/ai/` and drive the post-processing tests. Component tests mock `mlClient`. The classic baseline has a new frozen-output test (silhouette count, bbox, area per fixture) next to the existing `photoFixtures.test.ts`, so any accidental change to it fails. - **Rollout.** The engine switch is always visible, Classic selected. No flag. diff --git a/openspec/changes/32-photo-trace-ai/tasks.md b/openspec/changes/32-photo-trace-ai/tasks.md index 1492062..48c3781 100644 --- a/openspec/changes/32-photo-trace-ai/tasks.md +++ b/openspec/changes/32-photo-trace-ai/tasks.md @@ -2,7 +2,7 @@ ## 1. Spike: do the models beat the baseline? (gate) -- [x] 1.0 PoC page `poc/ai/` (`npm run poc:ai`): candidates run in the owner's browser on both photos; results in `poc/ai/FINDINGS.md`. BiRefNet-lite 1024 dropped (bad_alloc). +- [x] 1.0 PoC page `poc/ai/` (`npm run poc:ai`): candidates run in the owner's browser on both photos; results in `poc/ai/FINDINGS.md`. BiRefNet-lite 1024 dropped (bad_alloc); round 2 dropped MVANet and BEN2 (mark the whole sheet). - [ ] 1.1 Evaluation harness `scripts/eval-photo-ai.ts` (not shipped, not run by CI): for each fixture in `geometry/photo/fixtures` and for the owner's originals in `tmp/ai/photo_cutout_*.jpeg`, rectify, run the classic `segment` and each candidate model (BiRefNet-lite fp16, ISNet general, U²-Net-P; Node with `onnxruntime-node`), and print per silhouette: size in mm, IoU against the classic result, "network larger" and "network smaller" area, and time. Write each model's alpha for the fixtures to `geometry/photo/fixtures/ai/-.png`. Verify: the script prints the table for both fixtures. - [ ] 1.2 Record the table and the decision in `design.md` (Decisions → Model choice): the default AI model, the lighter model, the pinned revisions and licences, the measured sizes of the files. Verify against the targets in `design.md`. If no model reaches the raw targets (3 tools, ≤ 1 mm) stop here, update the artifacts with `/opsx:update` and close the change as a negative result. @@ -19,6 +19,7 @@ - [ ] 4.1 `src/ml/models.ts` registry (id, URL with commit hash, bytes, SHA-256, input size, normalisation, SPDX, `licenseClass` permissive | noncommercial | copyleft, purpose, source) and a test that refuses entries without SPDX, class or pinned revision and a non-permissive default. Verify: test green. - [ ] 4.2 `src/ml/runtime.ts` and `ml.worker.ts`: lazy session creation by dynamic import, WebGPU then WASM single-thread, `matte(rgb, w, h) → Float32Array`, resize to the model input and back. `mlClient.ts`: latest request wins, stale replies dropped, progress and cancel. Tests with a fake worker for ordering and cancel; pre/post-processing tested on a tiny fixed array. Verify in the browser that a classic-only session loads no ML chunk (network panel and build output). +- [ ] 4.2a Capability detection (`src/ml/capabilities.ts`): WebGPU adapter and `shader-f16`, used by the manager to mark models as unavailable with a reason. Tests with a mocked `navigator.gpu`: no WebGPU hides WebGPU-only models, no `shader-f16` hides fp16 ones. - [ ] 4.3 `src/ml/modelStore.ts`: download with progress and cancel, SHA-256 check, Cache API storage under the revision key, list, delete, `persistent` flag with memory fallback. Tests with a mocked `fetch` and cache, several models side by side: checksum mismatch rejects, delete removes, interrupted download leaves nothing, no storage falls back to memory. ## 5. UI diff --git a/poc/ai/FINDINGS.md b/poc/ai/FINDINGS.md index 7323b2a..6965d3b 100644 --- a/poc/ai/FINDINGS.md +++ b/poc/ai/FINDINGS.md @@ -34,3 +34,21 @@ 2. WebGPU не даёт выигрыша: для int8-моделей он медленнее или равен WASM, а часть операторов не поддержана. Бэкенд по умолчанию: WASM; WebGPU только как опция после замера. 3. Единственная модель с хорошим результатом, RMBG-1.4, имеет non-commercial лицензию. Разрешённые (MIT/Apache) модели либо не запускаются (BiRefNet), либо хуже классики (ISNet теряет предмет, U²-Net-P не лучше). 4. Потеря целого предмета (ISNet, кусачки) не лечится гибридным краем, потому что тот только уточняет границу уже найденной маски. Если ИИ-движок остаётся, объединение «классика ∪ сеть» или проверка классических силуэтов сетью надёжнее, чем сеть как единственный источник. + +## Второй раунд, прогон владельца (flash, 1 нить) + +| Модель (лицензия) | WebGPU | WASM | Качество | +|---|---|---|---| +| InSPyReNet SwinB fp16 (MIT, 199 МБ) | **1.9 с** | 78 с, **пустая маска** (fp16 на CPU не работает) | хорошо | +| ormbg int8 / fp16 (Apache-2.0, 44 / 88 МБ) | ошибка `ceil_mode` MaxPool | 19.7 с / 15.1 с | хорошо | +| ISNet imgly fp16 / fp32 (MIT, 88 / 176 МБ) | **0.23 с / 0.30 с** | — | 2 из 3 хорошо, кусачки ловит не полностью | +| ISNet pim int8, IS-Net BW fp32 (Apache-2.0) | — | 15 с | как другие ISNet: 2 из 3 | +| U²-Net полная fp32 (Apache-2.0, 176 МБ) | ошибка `ceil_mode` | 5.5 с | 2 из 3, захватывает тень у плоскогубцев | +| MVANet q4f16 / int8 (MIT) | 5.5 с | 87 с / 129 с | **размечает весь лист**: непригодна | +| BEN2 fp16 (MIT, 219 МБ) | 3.3 с | — | **размечает весь лист**: непригодна | + +## Выводы второго раунда +1. WebGPU полезен не всегда, но для моделей без `ceil_mode` MaxPool даёт ускорение в десятки раз (ISNet imgly 0.25 с, InSPyReNet 1.9 с против 15–78 с на WASM). Значит, выбор бэкенда делается **для каждой модели отдельно**: в реестре у модели список поддерживаемых бэкендов и предпочтительный; fp16-модели работают только на WebGPU (на WASM — пустая маска или медленно). +2. Кандидаты по разрешённым лицензиям: **InSPyReNet fp16 (MIT)** — хорошее качество и ~2 с на WebGPU; **ormbg (Apache-2.0)** — хорошее качество, но только WASM, 15–20 с; **ISNet imgly fp16 (MIT)** — мгновенно, но теряет часть кусачек. Из non-commercial: **RMBG-1.4** — лучшее качество, 6 с на WASM. +3. Без WebGPU разрешённых быстрых вариантов с хорошим качеством нет: остаётся ormbg (15 с) или RMBG-1.4 (6 с). +4. Остаётся проверить: снимок noflash на этих моделях, BiRefNet-lite 512, многопоточный WASM (`POC_COI=1`) для ormbg и RMBG-1.4. diff --git a/poc/ai/models.ts b/poc/ai/models.ts index 6e9fe38..66a3726 100644 --- a/poc/ai/models.ts +++ b/poc/ai/models.ts @@ -29,9 +29,6 @@ export const CANDIDATES: Candidate[] = [ { id: 'birefnet-lite-512-fp32', label: 'BiRefNet-lite 512 fp32 (MIT, 192 МБ)', url: hf('studioludens/birefnet-lite-512', '962e67969ec6acec9c19fab76acee4f5e22e990c', 'onnx/model.onnx'), bytes: 191877254, license: 'MIT', size: 512, ...IMNET, sigmoid: true }, { id: 'ormbg-int8', label: 'ormbg int8 (Apache-2.0, 44 МБ)', url: hf('onnx-community/ormbg-ONNX', '034e2d884afbab897e10e78fc5bb566b29533fd6', 'onnx/model_int8.onnx'), bytes: 44315136, license: 'Apache-2.0', size: 1024, ...ZERO, sigmoid: false }, { id: 'ormbg-fp16', label: 'ormbg fp16 (Apache-2.0, 88 МБ)', url: hf('onnx-community/ormbg-ONNX', '034e2d884afbab897e10e78fc5bb566b29533fd6', 'onnx/model_fp16.onnx'), bytes: 88117930, license: 'Apache-2.0', size: 1024, ...ZERO, sigmoid: false }, - { id: 'mvanet-q4f16', label: 'MVANet q4f16 (MIT, 85 МБ)', url: hf('onnx-community/MVANet-ONNX', '43ec3427514b8d9164eea02df93ca2f1b036bb7b', 'onnx/model_q4f16.onnx'), bytes: 85027081, license: 'MIT', size: 1024, ...IMNET, sigmoid: true }, - { id: 'mvanet-int8', label: 'MVANet int8 (MIT, 141 МБ)', url: hf('onnx-community/MVANet-ONNX', '43ec3427514b8d9164eea02df93ca2f1b036bb7b', 'onnx/model_int8.onnx'), bytes: 141076192, license: 'MIT', size: 1024, ...IMNET, sigmoid: true }, - { id: 'ben2-fp16', label: 'BEN2 fp16 (MIT, 219 МБ)', url: hf('onnx-community/BEN2-ONNX', 'c552aa82688edce09f0ac9d2e31ad53d9d629010', 'onnx/model_fp16.onnx'), bytes: 219121675, license: 'MIT', size: 1024, ...IMNET, sigmoid: true }, // третий раунд: убраны BiRefNet-lite 1024 (std::bad_alloc на обоих бэкендах у владельца) { id: 'isnet-imgly-fp16', label: 'ISNet general imgly fp16 (MIT, 88 МБ)', url: hf('imgly/isnet-general-onnx', '440dea96dd4a3b06bbbf5abec3e26569dd7ec49f', 'onnx/model_fp16.onnx'), bytes: 88152708, license: 'MIT', size: 1024, mean: [0.502, 0.502, 0.502], std: [1.004, 1.004, 1.004], sigmoid: false }, { id: 'isnet-imgly-fp32', label: 'ISNet general imgly fp32 (MIT, 176 МБ)', url: hf('imgly/isnet-general-onnx', '440dea96dd4a3b06bbbf5abec3e26569dd7ec49f', 'onnx/model.onnx'), bytes: 176149806, license: 'MIT', size: 1024, mean: [0.502, 0.502, 0.502], std: [1.004, 1.004, 1.004], sigmoid: false }, From 7844bce48de8038e0b28d70259c0a7894e0ad5d1 Mon Sep 17 00:00:00 2001 From: skarakovski Date: Sun, 18 Oct 2026 13:44:23 +0700 Subject: [PATCH 08/38] poc: BiRefNet-lite 512 results (good on WASM ~9 s, empty on WebGPU); plan updated Co-Authored-By: Claude Sonnet 5.5 --- openspec/changes/32-photo-trace-ai/design.md | 2 +- poc/ai/FINDINGS.md | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/openspec/changes/32-photo-trace-ai/design.md b/openspec/changes/32-photo-trace-ai/design.md index d18db32..b9a193d 100644 --- a/openspec/changes/32-photo-trace-ai/design.md +++ b/openspec/changes/32-photo-trace-ai/design.md @@ -21,7 +21,7 @@ Why the straightened sheet is a good input for a network: it is a top view at tr - **Engine as data, not as a fork.** `type SegEngine = 'classic' | 'ai'` lives in `PhotoSettings.engine` (default `'classic'`). `usePhotoTrace` keeps `segmentation` (classic, as now) and adds `aiSegmentation` computed only when the engine is `ai` or the compare view is on; the exposed `active` segmentation is chosen by the engine. `segment.ts`, `colour.ts`, `outline.ts` are not modified, only imported. Alternative: an `if` inside `segment()`. Rejected: it would touch the baseline and make its regression harder to prove. - **Input and resolution.** The rectified RGB is resampled to the model's input (typically 512 or 1024 px for BiRefNet-lite, 320 for U²-Net-P), run, and the alpha is resampled back to the 4 px/mm raster (bilinear). A4 at 1024 px is ≈ 0.29 mm/px, so the network edge is only good to about ±0.5 mm: this is why the edge is refined (next point) and why the result is never used raw for the default. -- **Model choice by measurement (task 1).** The PoC (`poc/ai/`, results in `poc/ai/FINDINGS.md`) ran the candidates on the owner's photos: BiRefNet-lite at 1024 fails with `std::bad_alloc` on both backends and is dropped; RMBG-1.4 (non-commercial) gave the best result in ~6 s on WASM; ISNet loses a whole tool; U²-Net-P is fast but not better than the classic path; WebGPU brings no gain over WASM. Second round: InSPyReNet fp16 (MIT, WebGPU ~2 s, good), ormbg (Apache-2.0, WASM 15–20 s, good), ISNet imgly fp16 (MIT, WebGPU 0.25 s, misses part of the cutters), U²-Net full (shadow leaks); MVANet and BEN2 mark the whole sheet and are dropped. The set offered in the manager comes from these results: InSPyReNet fp16 and ormbg as the permissive recommendations, ISNet imgly fp16 as the fast option, RMBG-1.4 as the optional non-commercial one. A note: most networks lose part of the cutters, which the classic path finds, so the AI engine is an alternative for hard photos, not a better default. MODNet is portrait-only and excluded; `finegrain-box-segmenter` (396 MB) is excluded for size. The default AI engine uses the best candidate that reaches the targets in the table below; a second, smaller model may be offered as "lighter". If none reaches them the change stops after task 1 and records the numbers (the change is then archived as a negative result without a version bump, via `/opsx:update`). +- **Model choice by measurement (task 1).** The PoC (`poc/ai/`, results in `poc/ai/FINDINGS.md`) ran the candidates on the owner's photos: BiRefNet-lite at 1024 fails with `std::bad_alloc` on both backends and is dropped; RMBG-1.4 (non-commercial) gave the best result in ~6 s on WASM; ISNet loses a whole tool; U²-Net-P is fast but not better than the classic path; WebGPU brings no gain over WASM. Second round: InSPyReNet fp16 (MIT, WebGPU ~2 s, good), ormbg (Apache-2.0, WASM 15–20 s, good), ISNet imgly fp16 (MIT, WebGPU 0.25 s, misses part of the cutters), BiRefNet-lite 512 fp16 (MIT, 98 MB, good, WASM ~9 s, empty mask on WebGPU), U²-Net full (shadow leaks); MVANet and BEN2 mark the whole sheet and are dropped. The set offered in the manager comes from these results: InSPyReNet fp16 (WebGPU), BiRefNet-lite 512 fp16 and ormbg (WASM) as the permissive recommendations, ISNet imgly fp16 as the fast option, RMBG-1.4 as the optional non-commercial one. A note: most networks lose part of the cutters, which the classic path finds, so the AI engine is an alternative for hard photos, not a better default. MODNet is portrait-only and excluded; `finegrain-box-segmenter` (396 MB) is excluded for size. The default AI engine uses the best candidate that reaches the targets in the table below; a second, smaller model may be offered as "lighter". If none reaches them the change stops after task 1 and records the numbers (the change is then archived as a negative result without a version bump, via `/opsx:update`). | Target on the two fixtures | Value | |---|---| diff --git a/poc/ai/FINDINGS.md b/poc/ai/FINDINGS.md index 6965d3b..4513d34 100644 --- a/poc/ai/FINDINGS.md +++ b/poc/ai/FINDINGS.md @@ -52,3 +52,12 @@ 2. Кандидаты по разрешённым лицензиям: **InSPyReNet fp16 (MIT)** — хорошее качество и ~2 с на WebGPU; **ormbg (Apache-2.0)** — хорошее качество, но только WASM, 15–20 с; **ISNet imgly fp16 (MIT)** — мгновенно, но теряет часть кусачек. Из non-commercial: **RMBG-1.4** — лучшее качество, 6 с на WASM. 3. Без WebGPU разрешённых быстрых вариантов с хорошим качеством нет: остаётся ormbg (15 с) или RMBG-1.4 (6 с). 4. Остаётся проверить: снимок noflash на этих моделях, BiRefNet-lite 512, многопоточный WASM (`POC_COI=1`) для ormbg и RMBG-1.4. + +## BiRefNet-lite 512 (MIT), прогон владельца (flash, 1 нить) + +| Вариант | WebGPU | WASM | +|---|---|---| +| fp16 (98 МБ) | пустая маска | хорошо, 9.4 с | +| fp32 (192 МБ) | пустая маска | хорошо, 9.1 с | + +Вывод: на входе 512 модель запускается и даёт хороший результат, но только на WASM (~9 с); WebGPU даёт пустую маску даже в fp32, значит дело в самом WebGPU-пути для этой модели, а не в fp16. Это первый разрешённый вариант с хорошим качеством, работающий без WebGPU. fp16 не быстрее fp32 на WASM, поэтому для WASM берётся меньший по размеру файл (fp16, 98 МБ), если качество совпадёт. From a37ee72da5ae0fcbec8e9c68fa17dc8374c793de Mon Sep 17 00:00:00 2001 From: skarakovski Date: Sun, 18 Oct 2026 13:51:30 +0700 Subject: [PATCH 09/38] poc: live threshold/smoothing of the soft mask, alpha view; plan: mask tuning requirement Co-Authored-By: Claude Sonnet 5.5 --- openspec/changes/32-photo-trace-ai/design.md | 1 + .../specs/cutout-photo-ai/spec.md | 23 +++++++++++++ openspec/changes/32-photo-trace-ai/tasks.md | 2 ++ poc/ai/index.html | 6 ++-- poc/ai/main.ts | 32 ++++++++++++++++++- 5 files changed, 60 insertions(+), 4 deletions(-) diff --git a/openspec/changes/32-photo-trace-ai/design.md b/openspec/changes/32-photo-trace-ai/design.md index b9a193d..8858475 100644 --- a/openspec/changes/32-photo-trace-ai/design.md +++ b/openspec/changes/32-photo-trace-ai/design.md @@ -32,6 +32,7 @@ Why the straightened sheet is a good input for a network: it is a top view at tr - **Salient-object networks and shadows.** These networks were trained to cut out objects and usually leave a cast shadow out (the argument in `PREPROP_CUTOUT_GOOGLE.md`), but "usually" is not a guarantee, and thin dark parts (pliers jaws) can be eaten. The harness therefore reports, per fixture, the area difference to the baseline split into "network larger" (shadow leaked) and "network smaller" (part lost). - **Mask → silhouettes.** Threshold the alpha at 0.5, then the same cleanup steps the classic path uses, imported from `geometry/raster` (`close`, `fillHoles`, `removeSpecks`, `labelComponents`, edge margin, `minSize`/`minArea` from `defaultSegmentParams`). The numbering rule is the classic one (top to bottom), so list order and colours are identical between engines for the same objects. Pure function `aiSegmentation(rectified, alpha, params) → Segmentation`, unit-tested on stored alphas. +- **Mask tuning (threshold and smoothing).** The network returns a soft alpha (0…1) with a soft zone along every edge. Two controls turn it into a silhouette, both applied to the cached alpha so the network is not re-run: a **threshold** (default 0.5, range 0.05…0.95; lower grows the outline into the soft zone, higher shrinks it) and a **smoothing radius** in mm (default 0.3, range 0…3; masked Gaussian from `geometry/raster/blur.ts` on the alpha before the threshold, σ = radius × scale, never below `MIN_SIGMA`). Smoothing evens out wavy and stair-stepped edges (the 1024 px output is stretched to 4 px/mm) and merges hairline cracks; too much removes thin parts, so the list shows each silhouette's size change and a hint appears when a silhouette shrinks by more than 1 mm. The PoC page already has both sliders with live recalculation, a greyscale view of the soft alpha, the outline length (shorter = smoother) and the area of the soft zone; the defaults and the size of the soft zone in mm are taken from its measurements. The values are session state of the dialog (`PhotoSettings.ai = {threshold, smooth}`), per photo not persisted, with a reset. With the hybrid edge the classic colour test places the final edge inside a band around this mask, so the two settings mainly decide which object parts count and how the band is placed; the traced outline is then smoothed by `traceSilhouette` as for the classic engine (σ = 1 px, 0.15 mm simplification). - **Hybrid edge.** For each silhouette: band = mask dilated by `band` mm (default 1.5) minus mask eroded by `band`. Inside the band the classic paper-colour deviation (`colourMask` from the paper surface fitted by `paperSurface`; `paperSurface` and `colourMask` are called, not changed) classifies pixels; the refined mask is `eroded core ∪ (band ∩ classic object pixels)`. If the classic test marks the whole band as paper or as object on a stretch longer than a few mm (the dark-part-in-shadow case), that stretch keeps the network edge. The refined labels go through the same `traceAlpha` as today. A setting `refine: on|off` (default on) exists in the compare view so the raw network edge can be inspected; it is not a persisted user option beyond the session. - **Compare view.** `CompareOverlay` draws the other engine's silhouette outlines (already traced by `traceSilhouette`) as dashed lines over the straightened view, plus a table "tool: classic mm / AI mm / Δ". It costs one extra segmentation when open, and is the way the user, and the owner while developing, sees where the engines disagree. - **ML runtime.** `src/ml/` is React-free. `matte(rgb, w, h) → Float32 alpha` is the only adapter this change needs. `ml.worker.ts` owns the inference session; `mlClient.ts` mirrors `modelClient.ts` (latest request wins, stale replies dropped); the worker is created lazily so classic users pay nothing, not even the library chunk (dynamic import). Backend: try WebGPU, fall back to WASM single-threaded (no COOP/COEP on Pages); report the backend in the model manager. diff --git a/openspec/changes/32-photo-trace-ai/specs/cutout-photo-ai/spec.md b/openspec/changes/32-photo-trace-ai/specs/cutout-photo-ai/spec.md index ede78ca..c8b60dc 100644 --- a/openspec/changes/32-photo-trace-ai/specs/cutout-photo-ai/spec.md +++ b/openspec/changes/32-photo-trace-ai/specs/cutout-photo-ai/spec.md @@ -111,6 +111,29 @@ The AI engine SHALL run the straightened sheet through the model, turn its foreg - **WHEN** the model finds nothing - **THEN** the message for no silhouettes is shown and Create is disabled +### Requirement: Mask tuning +With the AI engine the user SHALL be able to tune how the network's soft mask becomes a silhouette: a threshold (where inside the soft edge the outline is drawn, moving it outward or inward) and a smoothing radius in mm (how much the soft mask is blurred before the threshold, which evens out a jagged or wavy outline). Changing either SHALL update the silhouettes without running the network again, and the effect on each silhouette's size in mm SHALL be visible. The defaults SHALL be a threshold of 0.5 and a small smoothing radius, and a reset SHALL restore them. The soft mask SHALL be viewable as a greyscale image next to the silhouettes. + +#### Scenario: Live update +- **WHEN** the user moves the threshold or the smoothing slider +- **THEN** the silhouettes and their sizes update at once and the network is not run again + +#### Scenario: Threshold direction +- **WHEN** the threshold is lowered +- **THEN** the outlines grow outward into the soft edge, and when it is raised they shrink + +#### Scenario: Smoother outline +- **WHEN** the smoothing radius is raised +- **THEN** a wavy or jagged outline becomes smoother and the outline length shown does not grow + +#### Scenario: Thin parts +- **WHEN** the smoothing radius is large enough to erase a thin part of a tool +- **THEN** the size change is visible in the list and a hint says that a smaller radius keeps thin parts + +#### Scenario: Reset +- **WHEN** the user resets the mask settings +- **THEN** threshold and smoothing return to their defaults + ### Requirement: Accurate edge The AI engine SHALL place the silhouette edge by the paper-colour test within a narrow band around the network's mask and SHALL use the network's own edge only where that test does not separate the object from the paper. The sizes it reports SHALL stay within 0.5 mm of the classic result wherever the classic result is accurate. diff --git a/openspec/changes/32-photo-trace-ai/tasks.md b/openspec/changes/32-photo-trace-ai/tasks.md index 48c3781..356c873 100644 --- a/openspec/changes/32-photo-trace-ai/tasks.md +++ b/openspec/changes/32-photo-trace-ai/tasks.md @@ -13,6 +13,7 @@ ## 3. Pure AI post-processing - [ ] 3.1 `geometry/photo/ai.ts`: `aiSegmentation(rectified, alpha, params) → Segmentation` (threshold, close, fill holes, margin, specks, components, numbering top to bottom, minimum size and area as the classic path). Tests on the stored alphas from 1.1: three silhouettes per fixture, sizes within 1 mm of the baseline, nothing in the edge margin, empty alpha gives no silhouettes, two touching blobs stay one silhouette. +- [ ] 3.1a Mask tuning in `geometry/photo/ai.ts`: `aiSegmentation(rectified, alpha, {threshold, smooth, …})` smooths the alpha (`gaussianBlur`, masked by the full raster) and thresholds it; the alpha is cached by the caller. Tests on the stored alphas: lowering the threshold never shrinks a silhouette and raising it never grows one (monotone), smoothing does not lengthen the outline, smoothing 0 equals the unsmoothed result, a thin synthetic part (1 mm) disappears at a large radius and survives at a small one, defaults give the sizes of 3.1. - [ ] 3.2 Hybrid edge in `geometry/photo/ai.ts`: band around the mask, classic paper-colour test inside the band (calling `paperSurface` and `colourMask`), network edge kept where the test is uninformative, refined labels. Tests: sizes within 0.5 mm of the baseline and IoU ≥ 0.97 on both fixtures; a synthetic 40 × 20 mm rectangle (`synthetic.ts`) with a deliberately puffy mask measures 40 × 20 mm within 0.5 mm; a synthetic dark part merged into a shadow keeps the network edge and stays one piece; `refine: off` returns the raw mask result. ## 4. ML runtime @@ -26,6 +27,7 @@ - [ ] 5.1 `usePhotoTrace`: `engine` in `PhotoSettings` (default `classic`), `aiSegmentation` computed only when needed, an `active` segmentation, AI error falls back to classic with an error state, `computing` covers the AI run. Tests: default is classic and calls no model; switching uses the mocked client; a failing client leaves classic active; closing and reopening keeps the engine. - [ ] 5.2 `EngineSwitch` on the silhouette step (SegmentedControl Classic | AI, sensitivity hidden for AI) and `ModelManager` dialog (list with name, purpose, size, licence class, status; Download / Cancel / Delete / Delete all, active-model radio, used space, backend; per-class consent: the non-commercial confirmation checkbox gates Download), reachable from the switch and the footer. No artboard exists: built from `ui/` components and `theme.css` tokens, said in the commit; compare in en and ru and at 390 px. Component tests: consent before any download, the non-commercial checkbox disables and enables Download, progress, error banner, delete returns to "not downloaded" and asks for consent again, two models side by side, choosing the active one. +- [ ] 5.2a `MaskTuning` block under the AI engine on the silhouette step: threshold and smoothing sliders (smoothing is a length: `LengthField`), reset, greyscale view of the soft mask, size change per silhouette and the thin-part hint; updates without a model run (cached alpha). Component tests with a mocked `mlClient`: moving a slider changes the silhouette sizes and the mocked client is called once, reset restores defaults, the hint appears when a silhouette shrinks by more than 1 mm, both languages. - [ ] 5.3 `CompareOverlay` and the table of sizes (classic, AI, Δ) in the silhouette step; dashed outlines of the inactive engine over the straightened view; off by default. Tests: toggling draws the second outline set and the table values equal `silhouettes` of both engines; off costs no AI run. - [ ] 5.4 Strings in `en.json` and `ru.json` (engine, consent, progress, errors, compare, References licence list); the References page and README list the registry models, non-permissive ones in a separate section. Tests: key parity, a registry-to-References test, the `PhotoTips` and consent text exist in both languages. diff --git a/poc/ai/index.html b/poc/ai/index.html index 5c0e237..1782942 100644 --- a/poc/ai/index.html +++ b/poc/ai/index.html @@ -5,7 +5,7 @@ fieldset{display:inline-block;vertical-align:top;margin:0 8px 8px 0} button{padding:4px 10px} table{border-collapse:collapse;margin:8px 0}td,th{border:1px solid #bbb;padding:2px 8px;text-align:right}th{background:#eee} -canvas{max-width:48%;border:1px solid #888;margin:4px} +canvas{max-width:32%;border:1px solid #888;margin:4px} #log{white-space:pre-wrap;font:12px ui-monospace,monospace;background:#fff;border:1px solid #ccc;padding:6px;max-height:220px;overflow:auto}

PoC: нейросетевые силуэты на выпрямленном листе

@@ -19,11 +19,11 @@

PoC: нейросетевые силуэты на выпрямленном потоки вход -
Крайпорог поле мм
+
Край (пересчёт без модели)порог 0.5 сглаживание мм 0 поле мм
cross-origin isolated: , WebGPU: , cores:
-
+
diff --git a/poc/ai/main.ts b/poc/ai/main.ts index ac4ece0..6500685 100644 --- a/poc/ai/main.ts +++ b/poc/ai/main.ts @@ -5,6 +5,7 @@ import { guessPaper } from '../../src/geometry/photo/aspect' import { paperSize } from '../../src/geometry/photo/paper' import { rectify, type Rectified } from '../../src/geometry/photo/homography' import { segment } from '../../src/geometry/photo/segment' +import { gaussianBlur, MIN_SIGMA } from '../../src/geometry/raster/blur' import { fillHoles, labelComponents, removeSpecks } from '../../src/geometry/raster/components' import { CANDIDATES, type Candidate } from './models' @@ -146,6 +147,23 @@ async function run() { const o = out[session.outputNames[0]] const alpha = toAlpha(o.data as Float32Array, s, W, H, c.sigmoid) + state = { rect, seg, alpha, runs, c, ep, threads, s } + evaluate() + $('status').textContent = 'готово' +} + +interface State { rect: Rectified; seg: ReturnType; alpha: Float32Array; runs: number[]; c: Candidate; ep: string; threads: number; s: number } +let state: State | null = null + +/** Всё после инференса: сглаживание альфы, порог, компоненты, сравнение. Модель не запускается, поэтому ползунки работают мгновенно. */ +function evaluate() { + if (!state) return + const { rect, seg, runs, c, ep, threads, s } = state + const W = rect.image.width, H = rect.image.height + const thr = +$('thr').value, margin = +$('margin').value, smoothMm = +$('smooth').value + let alpha = state.alpha + if (smoothMm > 0) alpha = gaussianBlur(alpha, W, H, Math.max(MIN_SIGMA, smoothMm * rect.scale), new Uint8Array(W * H).fill(1)) + drawAlpha(alpha, W, H) const mask = new Uint8Array(W * H); for (let i = 0; i < mask.length; i++) mask[i] = alpha[i] >= thr ? 1 : 0 const ai = components(mask, W, H, rect.scale, margin) draw(rect, seg.labels, ai.labels) @@ -167,8 +185,20 @@ async function run() { const extra = ai.sils.length - seg.silhouettes.length $('tbl').innerHTML = rows.join('') + `всего: классика ${seg.silhouettes.length}, AI ${ai.sils.length}${extra ? ' (лишние/слитые!)' : ''}; общий IoU ${(inter / uni).toFixed(3)}; инференс ${runs[1].toFixed(0)} мс` ;(window as unknown as { __poc: unknown }).__poc = { model: c.id, ep, threads, size: s, classic: seg.silhouettes.map((x) => [x.widthMm, x.heightMm, x.areaMm2]), ai: ai.sils.map((x) => [x.wMm, x.hMm, x.areaMm2]), iou: inter / uni, ms: runs } - $('status').textContent = 'готово' + // длина границы: меньше — гладче контур (дрожащий край = много лишних угловых пикселей) + let edgePx = 0; for (let i = 1; i < W * H - W; i++) if (ai.labels[i] && (!ai.labels[i - 1] || !ai.labels[i + 1] || !ai.labels[i - W] || !ai.labels[i + W])) edgePx++ + $('edge').textContent = `граница AI: ${(edgePx / rect.scale).toFixed(0)} мм, мягкая зона альфы (0.1…0.9): ${(softPx(alpha) / rect.scale / rect.scale).toFixed(0)} мм²` +} + +function softPx(alpha: Float32Array): number { let n = 0; for (const a of alpha) if (a > 0.1 && a < 0.9) n++; return n } + +function drawAlpha(alpha: Float32Array, w: number, h: number) { + const cv = $('c3'); cv.width = w; cv.height = h + const g = cv.getContext('2d')!, im = g.createImageData(w, h) + for (let i = 0; i < w * h; i++) { const v = Math.round(alpha[i] * 255); im.data[i * 4] = im.data[i * 4 + 1] = im.data[i * 4 + 2] = v; im.data[i * 4 + 3] = 255 } + g.putImageData(im, 0, 0) } +for (const id of ['thr', 'smooth', 'margin']) $(id).addEventListener('input', () => evaluate()) const scaleMm = (scale: number) => 1 / (scale * scale) $('run').addEventListener('click', () => { run().catch((e) => { log('ОШИБКА: ' + (e?.stack ?? e)); $('status').textContent = 'ошибка' }) }) From a0eb71bf814288bc02a1bfa00a4361fcee591c4e Mon Sep 17 00:00:00 2001 From: skarakovski Date: Sun, 18 Oct 2026 20:21:57 +0700 Subject: [PATCH 10/38] plan: 32-photo-trace-ai permissive licences only (RMBG-1.4 stays a PoC reference); mask tuning makes them sufficient Co-Authored-By: Claude Sonnet 5.5 --- openspec/changes/32-photo-trace-ai/design.md | 10 +++++----- .../specs/cutout-photo-ai/spec.md | 16 ++++------------ openspec/changes/32-photo-trace-ai/tasks.md | 6 +++--- poc/ai/FINDINGS.md | 3 +++ 4 files changed, 15 insertions(+), 20 deletions(-) diff --git a/openspec/changes/32-photo-trace-ai/design.md b/openspec/changes/32-photo-trace-ai/design.md index 8858475..7398d5e 100644 --- a/openspec/changes/32-photo-trace-ai/design.md +++ b/openspec/changes/32-photo-trace-ai/design.md @@ -21,7 +21,7 @@ Why the straightened sheet is a good input for a network: it is a top view at tr - **Engine as data, not as a fork.** `type SegEngine = 'classic' | 'ai'` lives in `PhotoSettings.engine` (default `'classic'`). `usePhotoTrace` keeps `segmentation` (classic, as now) and adds `aiSegmentation` computed only when the engine is `ai` or the compare view is on; the exposed `active` segmentation is chosen by the engine. `segment.ts`, `colour.ts`, `outline.ts` are not modified, only imported. Alternative: an `if` inside `segment()`. Rejected: it would touch the baseline and make its regression harder to prove. - **Input and resolution.** The rectified RGB is resampled to the model's input (typically 512 or 1024 px for BiRefNet-lite, 320 for U²-Net-P), run, and the alpha is resampled back to the 4 px/mm raster (bilinear). A4 at 1024 px is ≈ 0.29 mm/px, so the network edge is only good to about ±0.5 mm: this is why the edge is refined (next point) and why the result is never used raw for the default. -- **Model choice by measurement (task 1).** The PoC (`poc/ai/`, results in `poc/ai/FINDINGS.md`) ran the candidates on the owner's photos: BiRefNet-lite at 1024 fails with `std::bad_alloc` on both backends and is dropped; RMBG-1.4 (non-commercial) gave the best result in ~6 s on WASM; ISNet loses a whole tool; U²-Net-P is fast but not better than the classic path; WebGPU brings no gain over WASM. Second round: InSPyReNet fp16 (MIT, WebGPU ~2 s, good), ormbg (Apache-2.0, WASM 15–20 s, good), ISNet imgly fp16 (MIT, WebGPU 0.25 s, misses part of the cutters), BiRefNet-lite 512 fp16 (MIT, 98 MB, good, WASM ~9 s, empty mask on WebGPU), U²-Net full (shadow leaks); MVANet and BEN2 mark the whole sheet and are dropped. The set offered in the manager comes from these results: InSPyReNet fp16 (WebGPU), BiRefNet-lite 512 fp16 and ormbg (WASM) as the permissive recommendations, ISNet imgly fp16 as the fast option, RMBG-1.4 as the optional non-commercial one. A note: most networks lose part of the cutters, which the classic path finds, so the AI engine is an alternative for hard photos, not a better default. MODNet is portrait-only and excluded; `finegrain-box-segmenter` (396 MB) is excluded for size. The default AI engine uses the best candidate that reaches the targets in the table below; a second, smaller model may be offered as "lighter". If none reaches them the change stops after task 1 and records the numbers (the change is then archived as a negative result without a version bump, via `/opsx:update`). +- **Model choice by measurement (task 1).** The PoC (`poc/ai/`, results in `poc/ai/FINDINGS.md`) ran the candidates on the owner's photos: BiRefNet-lite at 1024 fails with `std::bad_alloc` on both backends and is dropped; RMBG-1.4 (non-commercial) gave the best result in ~6 s on WASM; ISNet loses a whole tool; U²-Net-P is fast but not better than the classic path; WebGPU brings no gain over WASM. Second round: InSPyReNet fp16 (MIT, WebGPU ~2 s, good), ormbg (Apache-2.0, WASM 15–20 s, good), ISNet imgly fp16 (MIT, WebGPU 0.25 s, misses part of the cutters), BiRefNet-lite 512 fp16 (MIT, 98 MB, good, WASM ~9 s, empty mask on WebGPU), U²-Net full (shadow leaks); MVANet and BEN2 mark the whole sheet and are dropped. The set offered in the manager comes from these results: InSPyReNet fp16 (WebGPU), BiRefNet-lite 512 fp16 and ormbg (WASM) as the recommendations, ISNet imgly fp16 as the fast option (its missed cutter parts are expected to come back with a lower threshold; to be confirmed on the PoC). A note: most networks lose part of the cutters, which the classic path finds, so the AI engine is an alternative for hard photos, not a better default. MODNet is portrait-only and excluded; `finegrain-box-segmenter` (396 MB) is excluded for size. The default AI engine uses the best candidate that reaches the targets in the table below; a second, smaller model may be offered as "lighter". If none reaches them the change stops after task 1 and records the numbers (the change is then archived as a negative result without a version bump, via `/opsx:update`). | Target on the two fixtures | Value | |---|---| @@ -36,9 +36,9 @@ Why the straightened sheet is a good input for a network: it is a top view at tr - **Hybrid edge.** For each silhouette: band = mask dilated by `band` mm (default 1.5) minus mask eroded by `band`. Inside the band the classic paper-colour deviation (`colourMask` from the paper surface fitted by `paperSurface`; `paperSurface` and `colourMask` are called, not changed) classifies pixels; the refined mask is `eroded core ∪ (band ∩ classic object pixels)`. If the classic test marks the whole band as paper or as object on a stretch longer than a few mm (the dark-part-in-shadow case), that stretch keeps the network edge. The refined labels go through the same `traceAlpha` as today. A setting `refine: on|off` (default on) exists in the compare view so the raw network edge can be inspected; it is not a persisted user option beyond the session. - **Compare view.** `CompareOverlay` draws the other engine's silhouette outlines (already traced by `traceSilhouette`) as dashed lines over the straightened view, plus a table "tool: classic mm / AI mm / Δ". It costs one extra segmentation when open, and is the way the user, and the owner while developing, sees where the engines disagree. - **ML runtime.** `src/ml/` is React-free. `matte(rgb, w, h) → Float32 alpha` is the only adapter this change needs. `ml.worker.ts` owns the inference session; `mlClient.ts` mirrors `modelClient.ts` (latest request wins, stale replies dropped); the worker is created lazily so classic users pay nothing, not even the library chunk (dynamic import). Backend: try WebGPU, fall back to WASM single-threaded (no COOP/COEP on Pages); report the backend in the model manager. -- **Model manager (a window of its own).** `ModelManager` is a dialog reachable from the engine switch and from the app footer. It lists every model of the registry with: name, what it is good for (fast / accurate), download size, licence class, status (not downloaded / downloading with progress / ready / failed), and the buttons Download, Cancel, Delete, plus the total space used and a "Delete all". Several models can be downloaded at the same time as separate entries; the user picks the active one for the AI engine (a radio in the list). Weights live on the client only: Cache API under a versioned key (revision hash), with a memory fallback and a notice when the browser refuses storage. Nothing downloads without the click; choosing AI with no model ready opens the manager. The first download of a model shows its consent text (below). -- **Licence classes and consent.** Every registry entry has `licenseClass`: `permissive` (MIT / Apache-2.0 / BSD / CC0), `noncommercial` (e.g. RMBG-1.4, Bria) or `copyleft` (AGPL/GPL). The consent text is per class: `permissive` shows name, size, source host and licence; `noncommercial` additionally requires ticking "I use this model for non-commercial purposes and accept its licence" (link to the licence); `copyleft` states the licence and that the model is not part of PrintKit. Non-permissive models are never the default, never bundled, never mirrored or hosted by PrintKit: they are fetched straight from the rights holder's repository at a pinned revision, and a separate "Optional third-party models" section on the References page and in the README lists them as outside the project's MIT licence. Removing one is a one-line change in the registry. Why this is allowed: the app ships generic inference code and never redistributes the weights; the licence terms apply to the user's own use of the model, which the user accepts explicitly. This is a technical arrangement, not legal advice. AGPL models are admissible in the same way (the model file is data for a generic runtime, no AGPL code is linked or shipped), but no AGPL model is a candidate today: the YOLO segmentation weights only know COCO classes and cannot outline pliers, and the AGPL tag on `onnx-community/ISNet-ONNX` duplicates an Apache-2.0 model that is already in the list. -- **Pinned weights.** The registry (`src/ml/models.ts`) holds per model: id, URL with a commit hash (not `main`), byte size, SHA-256 (verified after download), input size and normalisation, SPDX licence, source. A model enters the registry with its `licenseClass` stated; the default AI model must be `permissive`, non-permissive ones are optional extras. The References page and README list them from this registry (a test checks every registry entry appears). +- **Model manager (a window of its own).** `ModelManager` is a dialog reachable from the engine switch and from the app footer. It lists every model of the registry with: name, what it is good for (fast / accurate), download size, licence, status (not downloaded / downloading with progress / ready / failed), and the buttons Download, Cancel, Delete, plus the total space used and a "Delete all". Several models can be downloaded at the same time as separate entries; the user picks the active one for the AI engine (a radio in the list). Weights live on the client only: Cache API under a versioned key (revision hash), with a memory fallback and a notice when the browser refuses storage. Nothing downloads without the click; choosing AI with no model ready opens the manager. The first download of a model shows its consent text (below). +- **Licences.** Only models under permissive licences (MIT, Apache-2.0, BSD, CC0) are offered. Non-commercial and copyleft weights are excluded: RMBG-1.4 (Bria, non-commercial) gave the best raw result in the PoC but is left out, because with the mask tuning below the permissive models reach a good result too (decision of the owner after the PoC), and the project stays free of licence caveats. The first download of any model shows its name, size, source host and licence and starts only after the click; the References page and the README list every model with its SPDX id and source. The PoC page keeps RMBG-1.4 marked `referenceOnly` as a quality yardstick; it is not part of the product registry. +- **Pinned weights.** The registry (`src/ml/models.ts`) holds per model: id, URL with a commit hash (not `main`), byte size, SHA-256 (verified after download), input size and normalisation, SPDX licence, source. A model enters the registry only with MIT / Apache-2.0 / BSD / CC0. The References page and README list them from this registry (a test checks every registry entry appears). - **Backend per model.** The registry lists for each model the backends it works on and the preferred one, from the PoC: InSPyReNet fp16 and ISNet imgly fp16 are WebGPU-only (fp16 on WASM gives an empty mask or minutes), ormbg and U²-Net fail on WebGPU (`ceil_mode` in MaxPool) and run on WASM, RMBG-1.4 int8 runs on WASM faster than on WebGPU. The manager shows only models that the device can run (WebGPU and `shader-f16` detected at start) and marks the others as unavailable with the reason; a run that fails on the preferred backend is not silently retried on another unless the registry says it works there. - **Mobile and weak devices.** The AI option is available at any width, but the dialog states the size, and on narrow screens or without WebGPU the lighter model is suggested first. The classic engine is always one click away and is what runs if the AI path fails (error banner, `active` falls back to classic). - **Testing.** Unit tests never run a network. Frozen model outputs (alpha PNGs produced by the harness for each fixture and each chosen model, a few hundred KB) are committed under `geometry/photo/fixtures/ai/` and drive the post-processing tests. Component tests mock `mlClient`. The classic baseline has a new frozen-output test (silhouette count, bbox, area per fixture) next to the existing `photoFixtures.test.ts`, so any accidental change to it fails. @@ -53,7 +53,7 @@ Why the straightened sheet is a good input for a network: it is a top view at tr - [Library size and WASM files bloat the bundle] → dynamic import in the worker only on first AI use; the classic start-up path does not load them (checked in the build output size and the network panel). - [Hugging Face availability, revision changes] → pinned hashes and SHA-256; on a failed download the error says which host failed and classic keeps working. - [Single-threaded WASM is slow] → progress indicator, `computing` flag as for the classic path; measured in task 1, the lighter model is the WASM default. -- [Licence drift] → registry test refuses entries without a stated SPDX id, class and pinned revision; a non-`permissive` entry can never be marked default. +- [Licence drift] → registry test refuses entries without an allowed SPDX id or without a pinned revision. ## Open Questions diff --git a/openspec/changes/32-photo-trace-ai/specs/cutout-photo-ai/spec.md b/openspec/changes/32-photo-trace-ai/specs/cutout-photo-ai/spec.md index c8b60dc..263af25 100644 --- a/openspec/changes/32-photo-trace-ai/specs/cutout-photo-ai/spec.md +++ b/openspec/changes/32-photo-trace-ai/specs/cutout-photo-ai/spec.md @@ -36,11 +36,11 @@ On the silhouette step the user SHALL be able to choose the detection engine, Cl - **THEN** the sensitivity control, which belongs to the classic detector, is not offered for it, and the margin still applies ### Requirement: Model manager -The app SHALL have a model manager window, reachable from the engine switch and from the footer, that lists the available models with their name, purpose, size, licence class and status, and lets the user download, cancel, delete and choose the model used by the AI engine. Several models SHALL be downloadable and stored side by side. The models SHALL be kept in the browser's storage on the device; the window SHALL show the space they use and offer to delete all of them. +The app SHALL have a model manager window, reachable from the engine switch and from the footer, that lists the available models with their name, purpose, size, licence and status, and lets the user download, cancel, delete and choose the model used by the AI engine. Several models SHALL be downloadable and stored side by side. The models SHALL be kept in the browser's storage on the device; the window SHALL show the space they use and offer to delete all of them. #### Scenario: List - **WHEN** the manager is opened -- **THEN** every model is listed with its size, licence class and status +- **THEN** every model is listed with its size, licence and status #### Scenario: Download and delete - **WHEN** the user downloads a model and later deletes it @@ -55,20 +55,12 @@ The app SHALL have a model manager window, reachable from the engine switch and - **THEN** the manager opens, and nothing is downloaded until the user starts a download ### Requirement: Model download and consent -A model SHALL be downloaded only after the user confirms. Before the first download of a model the app SHALL show its name, size, source host and licence. For a model whose licence restricts use to non-commercial purposes, the app SHALL additionally require the user to confirm that the model is used for non-commercial purposes and that its licence is accepted, and SHALL NOT start the download without it. Such models, and models under copyleft licences, SHALL never be the default, SHALL NOT be bundled with or hosted by the app, SHALL be fetched from the rights holder's repository at a fixed revision, and SHALL be listed on the References page as third-party models outside the app's own licence. Downloads SHALL show progress and be cancellable. If the download or the model fails, or the file's checksum does not match, the classic engine SHALL stay usable and the failure SHALL be shown. +A model SHALL be downloaded only after the user confirms. Before the first download of a model the app SHALL show its name, size, source host and licence, and the models offered SHALL be under permissive licences (MIT, Apache-2.0, BSD or CC0). Downloads SHALL show progress and be cancellable. If the download or the model fails, or the file's checksum does not match, the classic engine SHALL stay usable and the failure SHALL be shown. #### Scenario: Consent first - **WHEN** the user starts the first download of a model - **THEN** a dialog states the size and that the source host sees the user's address, and nothing is downloaded until the user confirms -#### Scenario: Non-commercial model -- **WHEN** the user starts the download of a model with a non-commercial licence -- **THEN** the Download button stays disabled until the user ticks the non-commercial-use and licence confirmation, and the dialog links the licence - -#### Scenario: Third-party label -- **WHEN** the References page is opened -- **THEN** models that are not under a permissive licence appear in a separate section as optional third-party models not covered by the app's licence - #### Scenario: Cached - **WHEN** the model was downloaded earlier and the storage is intact - **THEN** choosing it starts at once without a new download @@ -82,7 +74,7 @@ A model SHALL be downloaded only after the user confirms. Before the first downl - **THEN** the model works for the session and a notice says it will be downloaded again next time ### Requirement: Privacy -The photo, the straightened image and the masks SHALL be processed in the browser only and SHALL NOT be sent anywhere by the AI engine. The only network traffic added by the AI engine SHALL be the download of the model files, from a fixed, versioned location, after consent. The user interface and the References page SHALL say so, and SHALL list every model with its licence and source. The default AI model SHALL be under a permissive licence (MIT, Apache-2.0, BSD or CC0). +The photo, the straightened image and the masks SHALL be processed in the browser only and SHALL NOT be sent anywhere by the AI engine. The only network traffic added by the AI engine SHALL be the download of the model files, from a fixed, versioned location, after consent. The user interface and the References page SHALL say so, and SHALL list every model with its licence and source. Only models under permissive licences (MIT, Apache-2.0, BSD or CC0) SHALL be offered. #### Scenario: No image upload - **WHEN** a photo is processed by the AI engine diff --git a/openspec/changes/32-photo-trace-ai/tasks.md b/openspec/changes/32-photo-trace-ai/tasks.md index 356c873..bf4a31f 100644 --- a/openspec/changes/32-photo-trace-ai/tasks.md +++ b/openspec/changes/32-photo-trace-ai/tasks.md @@ -18,7 +18,7 @@ ## 4. ML runtime -- [ ] 4.1 `src/ml/models.ts` registry (id, URL with commit hash, bytes, SHA-256, input size, normalisation, SPDX, `licenseClass` permissive | noncommercial | copyleft, purpose, source) and a test that refuses entries without SPDX, class or pinned revision and a non-permissive default. Verify: test green. +- [ ] 4.1 `src/ml/models.ts` registry (id, URL with commit hash, bytes, SHA-256, input size, normalisation, SPDX, `licenseClass` permissive | noncommercial | copyleft, purpose, source) and a test that refuses entries without an allowed SPDX id (MIT / Apache-2.0 / BSD / CC0) or without a pinned revision. Verify: test green. - [ ] 4.2 `src/ml/runtime.ts` and `ml.worker.ts`: lazy session creation by dynamic import, WebGPU then WASM single-thread, `matte(rgb, w, h) → Float32Array`, resize to the model input and back. `mlClient.ts`: latest request wins, stale replies dropped, progress and cancel. Tests with a fake worker for ordering and cancel; pre/post-processing tested on a tiny fixed array. Verify in the browser that a classic-only session loads no ML chunk (network panel and build output). - [ ] 4.2a Capability detection (`src/ml/capabilities.ts`): WebGPU adapter and `shader-f16`, used by the manager to mark models as unavailable with a reason. Tests with a mocked `navigator.gpu`: no WebGPU hides WebGPU-only models, no `shader-f16` hides fp16 ones. - [ ] 4.3 `src/ml/modelStore.ts`: download with progress and cancel, SHA-256 check, Cache API storage under the revision key, list, delete, `persistent` flag with memory fallback. Tests with a mocked `fetch` and cache, several models side by side: checksum mismatch rejects, delete removes, interrupted download leaves nothing, no storage falls back to memory. @@ -26,10 +26,10 @@ ## 5. UI - [ ] 5.1 `usePhotoTrace`: `engine` in `PhotoSettings` (default `classic`), `aiSegmentation` computed only when needed, an `active` segmentation, AI error falls back to classic with an error state, `computing` covers the AI run. Tests: default is classic and calls no model; switching uses the mocked client; a failing client leaves classic active; closing and reopening keeps the engine. -- [ ] 5.2 `EngineSwitch` on the silhouette step (SegmentedControl Classic | AI, sensitivity hidden for AI) and `ModelManager` dialog (list with name, purpose, size, licence class, status; Download / Cancel / Delete / Delete all, active-model radio, used space, backend; per-class consent: the non-commercial confirmation checkbox gates Download), reachable from the switch and the footer. No artboard exists: built from `ui/` components and `theme.css` tokens, said in the commit; compare in en and ru and at 390 px. Component tests: consent before any download, the non-commercial checkbox disables and enables Download, progress, error banner, delete returns to "not downloaded" and asks for consent again, two models side by side, choosing the active one. +- [ ] 5.2 `EngineSwitch` on the silhouette step (SegmentedControl Classic | AI, sensitivity hidden for AI) and `ModelManager` dialog (list with name, purpose, size, licence, status; Download / Cancel / Delete / Delete all, active-model radio, used space, backend; consent before the first download), reachable from the switch and the footer. No artboard exists: built from `ui/` components and `theme.css` tokens, said in the commit; compare in en and ru and at 390 px. Component tests: consent before any download, progress, error banner, delete returns to "not downloaded" and asks for consent again, two models side by side, choosing the active one. - [ ] 5.2a `MaskTuning` block under the AI engine on the silhouette step: threshold and smoothing sliders (smoothing is a length: `LengthField`), reset, greyscale view of the soft mask, size change per silhouette and the thin-part hint; updates without a model run (cached alpha). Component tests with a mocked `mlClient`: moving a slider changes the silhouette sizes and the mocked client is called once, reset restores defaults, the hint appears when a silhouette shrinks by more than 1 mm, both languages. - [ ] 5.3 `CompareOverlay` and the table of sizes (classic, AI, Δ) in the silhouette step; dashed outlines of the inactive engine over the straightened view; off by default. Tests: toggling draws the second outline set and the table values equal `silhouettes` of both engines; off costs no AI run. -- [ ] 5.4 Strings in `en.json` and `ru.json` (engine, consent, progress, errors, compare, References licence list); the References page and README list the registry models, non-permissive ones in a separate section. Tests: key parity, a registry-to-References test, the `PhotoTips` and consent text exist in both languages. +- [ ] 5.4 Strings in `en.json` and `ru.json` (engine, consent, progress, errors, compare, References licence list); the References page and README list the registry models. Tests: key parity, a registry-to-References test, the `PhotoTips` and consent text exist in both languages. ## 6. Version diff --git a/poc/ai/FINDINGS.md b/poc/ai/FINDINGS.md index 4513d34..0d9473b 100644 --- a/poc/ai/FINDINGS.md +++ b/poc/ai/FINDINGS.md @@ -61,3 +61,6 @@ | fp32 (192 МБ) | пустая маска | хорошо, 9.1 с | Вывод: на входе 512 модель запускается и даёт хороший результат, но только на WASM (~9 с); WebGPU даёт пустую маску даже в fp32, значит дело в самом WebGPU-пути для этой модели, а не в fp16. Это первый разрешённый вариант с хорошим качеством, работающий без WebGPU. fp16 не быстрее fp32 на WASM, поэтому для WASM берётся меньший по размеру файл (fp16, 98 МБ), если качество совпадёт. + +## Решение владельца +С регулировкой порога и сглаживания маски достаточно моделей с разрешёнными лицензиями (даже ISNet даёт хороший результат), поэтому RMBG-1.4 в продукт не берётся и остаётся в PoC только как эталон качества (`referenceOnly`). Классы лицензий и согласие на non-commercial из плана убраны. From 49ade92e6d53c111cb0c2235622e6675c2de4f60 Mon Sep 17 00:00:00 2001 From: skarakovski Date: Sun, 18 Oct 2026 20:22:09 +0700 Subject: [PATCH 11/38] plan: 32-photo-trace-ai registry task without licence classes Co-Authored-By: Claude Sonnet 5.5 --- openspec/changes/32-photo-trace-ai/tasks.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openspec/changes/32-photo-trace-ai/tasks.md b/openspec/changes/32-photo-trace-ai/tasks.md index bf4a31f..cc20629 100644 --- a/openspec/changes/32-photo-trace-ai/tasks.md +++ b/openspec/changes/32-photo-trace-ai/tasks.md @@ -18,7 +18,7 @@ ## 4. ML runtime -- [ ] 4.1 `src/ml/models.ts` registry (id, URL with commit hash, bytes, SHA-256, input size, normalisation, SPDX, `licenseClass` permissive | noncommercial | copyleft, purpose, source) and a test that refuses entries without an allowed SPDX id (MIT / Apache-2.0 / BSD / CC0) or without a pinned revision. Verify: test green. +- [ ] 4.1 `src/ml/models.ts` registry (id, URL with commit hash, bytes, SHA-256, input size, normalisation, SPDX, supported and preferred backends, purpose, source) and a test that refuses entries without an allowed SPDX id (MIT / Apache-2.0 / BSD / CC0) or without a pinned revision. Verify: test green. - [ ] 4.2 `src/ml/runtime.ts` and `ml.worker.ts`: lazy session creation by dynamic import, WebGPU then WASM single-thread, `matte(rgb, w, h) → Float32Array`, resize to the model input and back. `mlClient.ts`: latest request wins, stale replies dropped, progress and cancel. Tests with a fake worker for ordering and cancel; pre/post-processing tested on a tiny fixed array. Verify in the browser that a classic-only session loads no ML chunk (network panel and build output). - [ ] 4.2a Capability detection (`src/ml/capabilities.ts`): WebGPU adapter and `shader-f16`, used by the manager to mark models as unavailable with a reason. Tests with a mocked `navigator.gpu`: no WebGPU hides WebGPU-only models, no `shader-f16` hides fp16 ones. - [ ] 4.3 `src/ml/modelStore.ts`: download with progress and cancel, SHA-256 check, Cache API storage under the revision key, list, delete, `persistent` flag with memory fallback. Tests with a mocked `fetch` and cache, several models side by side: checksum mismatch rejects, delete removes, interrupted download leaves nothing, no storage falls back to memory. From 4ee9a5ef855f93f3a80b94d72e54429b347b16e7 Mon Sep 17 00:00:00 2001 From: skarakovski Date: Sun, 18 Oct 2026 20:25:05 +0700 Subject: [PATCH 12/38] plan: 32-photo-trace-ai user-facing mask controls with per-model defaults Co-Authored-By: Claude Sonnet 5.5 --- openspec/changes/32-photo-trace-ai/design.md | 2 +- .../32-photo-trace-ai/specs/cutout-photo-ai/spec.md | 12 ++++++++++-- openspec/changes/32-photo-trace-ai/tasks.md | 4 ++-- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/openspec/changes/32-photo-trace-ai/design.md b/openspec/changes/32-photo-trace-ai/design.md index 7398d5e..8c8b844 100644 --- a/openspec/changes/32-photo-trace-ai/design.md +++ b/openspec/changes/32-photo-trace-ai/design.md @@ -32,7 +32,7 @@ Why the straightened sheet is a good input for a network: it is a top view at tr - **Salient-object networks and shadows.** These networks were trained to cut out objects and usually leave a cast shadow out (the argument in `PREPROP_CUTOUT_GOOGLE.md`), but "usually" is not a guarantee, and thin dark parts (pliers jaws) can be eaten. The harness therefore reports, per fixture, the area difference to the baseline split into "network larger" (shadow leaked) and "network smaller" (part lost). - **Mask → silhouettes.** Threshold the alpha at 0.5, then the same cleanup steps the classic path uses, imported from `geometry/raster` (`close`, `fillHoles`, `removeSpecks`, `labelComponents`, edge margin, `minSize`/`minArea` from `defaultSegmentParams`). The numbering rule is the classic one (top to bottom), so list order and colours are identical between engines for the same objects. Pure function `aiSegmentation(rectified, alpha, params) → Segmentation`, unit-tested on stored alphas. -- **Mask tuning (threshold and smoothing).** The network returns a soft alpha (0…1) with a soft zone along every edge. Two controls turn it into a silhouette, both applied to the cached alpha so the network is not re-run: a **threshold** (default 0.5, range 0.05…0.95; lower grows the outline into the soft zone, higher shrinks it) and a **smoothing radius** in mm (default 0.3, range 0…3; masked Gaussian from `geometry/raster/blur.ts` on the alpha before the threshold, σ = radius × scale, never below `MIN_SIGMA`). Smoothing evens out wavy and stair-stepped edges (the 1024 px output is stretched to 4 px/mm) and merges hairline cracks; too much removes thin parts, so the list shows each silhouette's size change and a hint appears when a silhouette shrinks by more than 1 mm. The PoC page already has both sliders with live recalculation, a greyscale view of the soft alpha, the outline length (shorter = smoother) and the area of the soft zone; the defaults and the size of the soft zone in mm are taken from its measurements. The values are session state of the dialog (`PhotoSettings.ai = {threshold, smooth}`), per photo not persisted, with a reset. With the hybrid edge the classic colour test places the final edge inside a band around this mask, so the two settings mainly decide which object parts count and how the band is placed; the traced outline is then smoothed by `traceSilhouette` as for the classic engine (σ = 1 px, 0.15 mm simplification). +- **Mask tuning (threshold and smoothing).** The network returns a soft alpha (0…1) with a soft zone along every edge. Two controls turn it into a silhouette, both applied to the cached alpha so the network is not re-run: a **threshold** (default 0.5, range 0.05…0.95; lower grows the outline into the soft zone, higher shrinks it) and a **smoothing radius** in mm (default 0.3, range 0…3; masked Gaussian from `geometry/raster/blur.ts` on the alpha before the threshold, σ = radius × scale, never below `MIN_SIGMA`). Smoothing evens out wavy and stair-stepped edges (the 1024 px output is stretched to 4 px/mm) and merges hairline cracks; too much removes thin parts, so the list shows each silhouette's size change and a hint appears when a silhouette shrinks by more than 1 mm. The PoC page already has both sliders with live recalculation, a greyscale view of the soft alpha, the outline length (shorter = smoother) and the area of the soft zone; the defaults and the size of the soft zone in mm are taken from its measurements. The two controls are user-facing and always visible under the AI engine (labels "Edge reach" / "Outline smoothness" with a one-line hint each and the numeric value; the technical names stay in code). Defaults are per model: the registry has `maskDefaults: {threshold, smooth}` for each entry (e.g. a lower threshold for ISNet, whose mask is tighter), found with the PoC sliders; switching the model loads them unless the user has touched the controls for the photo, and Reset returns to them. The values are session state of the dialog (`PhotoSettings.ai = {threshold, smooth}`), per photo not persisted, with a reset. With the hybrid edge the classic colour test places the final edge inside a band around this mask, so the two settings mainly decide which object parts count and how the band is placed; the traced outline is then smoothed by `traceSilhouette` as for the classic engine (σ = 1 px, 0.15 mm simplification). - **Hybrid edge.** For each silhouette: band = mask dilated by `band` mm (default 1.5) minus mask eroded by `band`. Inside the band the classic paper-colour deviation (`colourMask` from the paper surface fitted by `paperSurface`; `paperSurface` and `colourMask` are called, not changed) classifies pixels; the refined mask is `eroded core ∪ (band ∩ classic object pixels)`. If the classic test marks the whole band as paper or as object on a stretch longer than a few mm (the dark-part-in-shadow case), that stretch keeps the network edge. The refined labels go through the same `traceAlpha` as today. A setting `refine: on|off` (default on) exists in the compare view so the raw network edge can be inspected; it is not a persisted user option beyond the session. - **Compare view.** `CompareOverlay` draws the other engine's silhouette outlines (already traced by `traceSilhouette`) as dashed lines over the straightened view, plus a table "tool: classic mm / AI mm / Δ". It costs one extra segmentation when open, and is the way the user, and the owner while developing, sees where the engines disagree. - **ML runtime.** `src/ml/` is React-free. `matte(rgb, w, h) → Float32 alpha` is the only adapter this change needs. `ml.worker.ts` owns the inference session; `mlClient.ts` mirrors `modelClient.ts` (latest request wins, stale replies dropped); the worker is created lazily so classic users pay nothing, not even the library chunk (dynamic import). Backend: try WebGPU, fall back to WASM single-threaded (no COOP/COEP on Pages); report the backend in the model manager. diff --git a/openspec/changes/32-photo-trace-ai/specs/cutout-photo-ai/spec.md b/openspec/changes/32-photo-trace-ai/specs/cutout-photo-ai/spec.md index 263af25..6a9860d 100644 --- a/openspec/changes/32-photo-trace-ai/specs/cutout-photo-ai/spec.md +++ b/openspec/changes/32-photo-trace-ai/specs/cutout-photo-ai/spec.md @@ -104,7 +104,7 @@ The AI engine SHALL run the straightened sheet through the model, turn its foreg - **THEN** the message for no silhouettes is shown and Create is disabled ### Requirement: Mask tuning -With the AI engine the user SHALL be able to tune how the network's soft mask becomes a silhouette: a threshold (where inside the soft edge the outline is drawn, moving it outward or inward) and a smoothing radius in mm (how much the soft mask is blurred before the threshold, which evens out a jagged or wavy outline). Changing either SHALL update the silhouettes without running the network again, and the effect on each silhouette's size in mm SHALL be visible. The defaults SHALL be a threshold of 0.5 and a small smoothing radius, and a reset SHALL restore them. The soft mask SHALL be viewable as a greyscale image next to the silhouettes. +With the AI engine the user SHALL be able to tune how the network's soft mask becomes a silhouette: a threshold (where inside the soft edge the outline is drawn, moving it outward or inward) and a smoothing radius in mm (how much the soft mask is blurred before the threshold, which evens out a jagged or wavy outline). Changing either SHALL update the silhouettes without running the network again, and the effect on each silhouette's size in mm SHALL be visible. Both controls SHALL be shown to the user on the silhouette step whenever the AI engine is active, with plain-language labels and a short hint each (the threshold as how far the outline reaches into the blurry edge of the object, the smoothing as how evenly the outline runs), not as technical names. Each model SHALL have its own default threshold and smoothing, since models differ in how sharp their mask is, and a reset SHALL restore the defaults of the model in use. Changing the model SHALL load that model's defaults unless the user has changed the controls for this photo. The soft mask SHALL be viewable as a greyscale image next to the silhouettes. #### Scenario: Live update - **WHEN** the user moves the threshold or the smoothing slider @@ -124,7 +124,15 @@ With the AI engine the user SHALL be able to tune how the network's soft mask be #### Scenario: Reset - **WHEN** the user resets the mask settings -- **THEN** threshold and smoothing return to their defaults +- **THEN** threshold and smoothing return to the defaults of the model in use + +#### Scenario: Per-model defaults +- **WHEN** the user switches from one model to another without touching the controls +- **THEN** the controls show the new model's defaults + +#### Scenario: Visible to the user +- **WHEN** the AI engine is active on the silhouette step +- **THEN** both controls are visible with their labels and hints, in the interface language ### Requirement: Accurate edge The AI engine SHALL place the silhouette edge by the paper-colour test within a narrow band around the network's mask and SHALL use the network's own edge only where that test does not separate the object from the paper. The sizes it reports SHALL stay within 0.5 mm of the classic result wherever the classic result is accurate. diff --git a/openspec/changes/32-photo-trace-ai/tasks.md b/openspec/changes/32-photo-trace-ai/tasks.md index cc20629..7c7a418 100644 --- a/openspec/changes/32-photo-trace-ai/tasks.md +++ b/openspec/changes/32-photo-trace-ai/tasks.md @@ -18,7 +18,7 @@ ## 4. ML runtime -- [ ] 4.1 `src/ml/models.ts` registry (id, URL with commit hash, bytes, SHA-256, input size, normalisation, SPDX, supported and preferred backends, purpose, source) and a test that refuses entries without an allowed SPDX id (MIT / Apache-2.0 / BSD / CC0) or without a pinned revision. Verify: test green. +- [ ] 4.1 `src/ml/models.ts` registry (id, URL with commit hash, bytes, SHA-256, input size, normalisation, SPDX, supported and preferred backends, `maskDefaults` (threshold, smooth) found with the PoC, purpose, source) and a test that refuses entries without an allowed SPDX id (MIT / Apache-2.0 / BSD / CC0) or without a pinned revision. Verify: test green. - [ ] 4.2 `src/ml/runtime.ts` and `ml.worker.ts`: lazy session creation by dynamic import, WebGPU then WASM single-thread, `matte(rgb, w, h) → Float32Array`, resize to the model input and back. `mlClient.ts`: latest request wins, stale replies dropped, progress and cancel. Tests with a fake worker for ordering and cancel; pre/post-processing tested on a tiny fixed array. Verify in the browser that a classic-only session loads no ML chunk (network panel and build output). - [ ] 4.2a Capability detection (`src/ml/capabilities.ts`): WebGPU adapter and `shader-f16`, used by the manager to mark models as unavailable with a reason. Tests with a mocked `navigator.gpu`: no WebGPU hides WebGPU-only models, no `shader-f16` hides fp16 ones. - [ ] 4.3 `src/ml/modelStore.ts`: download with progress and cancel, SHA-256 check, Cache API storage under the revision key, list, delete, `persistent` flag with memory fallback. Tests with a mocked `fetch` and cache, several models side by side: checksum mismatch rejects, delete removes, interrupted download leaves nothing, no storage falls back to memory. @@ -27,7 +27,7 @@ - [ ] 5.1 `usePhotoTrace`: `engine` in `PhotoSettings` (default `classic`), `aiSegmentation` computed only when needed, an `active` segmentation, AI error falls back to classic with an error state, `computing` covers the AI run. Tests: default is classic and calls no model; switching uses the mocked client; a failing client leaves classic active; closing and reopening keeps the engine. - [ ] 5.2 `EngineSwitch` on the silhouette step (SegmentedControl Classic | AI, sensitivity hidden for AI) and `ModelManager` dialog (list with name, purpose, size, licence, status; Download / Cancel / Delete / Delete all, active-model radio, used space, backend; consent before the first download), reachable from the switch and the footer. No artboard exists: built from `ui/` components and `theme.css` tokens, said in the commit; compare in en and ru and at 390 px. Component tests: consent before any download, progress, error banner, delete returns to "not downloaded" and asks for consent again, two models side by side, choosing the active one. -- [ ] 5.2a `MaskTuning` block under the AI engine on the silhouette step: threshold and smoothing sliders (smoothing is a length: `LengthField`), reset, greyscale view of the soft mask, size change per silhouette and the thin-part hint; updates without a model run (cached alpha). Component tests with a mocked `mlClient`: moving a slider changes the silhouette sizes and the mocked client is called once, reset restores defaults, the hint appears when a silhouette shrinks by more than 1 mm, both languages. +- [ ] 5.2a `MaskTuning` block, always visible under the AI engine on the silhouette step: two sliders with plain-language labels and hints ("Edge reach", "Outline smoothness"; threshold and smoothing in code) (smoothing is a length: `LengthField`), per-model defaults from the registry (`maskDefaults`), reset to them, greyscale view of the soft mask, size change per silhouette and the thin-part hint; updates without a model run (cached alpha). Component tests with a mocked `mlClient`: moving a slider changes the silhouette sizes and the mocked client is called once, reset restores the model's defaults, switching model loads its defaults unless the controls were touched, the hint appears when a silhouette shrinks by more than 1 mm, both languages. - [ ] 5.3 `CompareOverlay` and the table of sizes (classic, AI, Δ) in the silhouette step; dashed outlines of the inactive engine over the straightened view; off by default. Tests: toggling draws the second outline set and the table values equal `silhouettes` of both engines; off costs no AI run. - [ ] 5.4 Strings in `en.json` and `ru.json` (engine, consent, progress, errors, compare, References licence list); the References page and README list the registry models. Tests: key parity, a registry-to-References test, the `PhotoTips` and consent text exist in both languages. From c1c9426dcda22f689a00a37059bf0aa735010279 Mon Sep 17 00:00:00 2001 From: skarakovski Date: Sun, 18 Oct 2026 20:28:51 +0700 Subject: [PATCH 13/38] plan: 33-keychain-ai-silhouette (AI silhouette source for Keychain Image, v1.4.0); poc: whole-image mode Co-Authored-By: Claude Sonnet 5.5 --- openspec/ROADMAP.md | 1 + .../33-keychain-ai-silhouette/.openspec.yaml | 2 + .../33-keychain-ai-silhouette/design.md | 45 +++++++ .../33-keychain-ai-silhouette/proposal.md | 32 +++++ .../specs/keychain-ai-silhouette/spec.md | 122 ++++++++++++++++++ .../33-keychain-ai-silhouette/tasks.md | 30 +++++ poc/ai/index.html | 2 +- poc/ai/main.ts | 64 +++++---- 8 files changed, 273 insertions(+), 25 deletions(-) create mode 100644 openspec/changes/33-keychain-ai-silhouette/.openspec.yaml create mode 100644 openspec/changes/33-keychain-ai-silhouette/design.md create mode 100644 openspec/changes/33-keychain-ai-silhouette/proposal.md create mode 100644 openspec/changes/33-keychain-ai-silhouette/specs/keychain-ai-silhouette/spec.md create mode 100644 openspec/changes/33-keychain-ai-silhouette/tasks.md diff --git a/openspec/ROADMAP.md b/openspec/ROADMAP.md index f5bfe0f..f2fe2ac 100644 --- a/openspec/ROADMAP.md +++ b/openspec/ROADMAP.md @@ -42,6 +42,7 @@ | 30 | `30-rack-front-plate` | 28, 29 | v1.1.0. Передняя панель корпуса: крепление keystone по моделям (гнездо с рампой и защёлкой, обойма за панелью), группы отверстий (колонка, ряд, сетка, зоны вокруг корпуса), «отверстия» вместо «кабельных», окно по умолчанию, ссылки на референсы в README | | 31 | `31-rack-shelf` | 26, 29 | v1.2.0. Полка: передняя панель с ушами лицом на столе, пол с вентиляцией (щели, можно глухой), боковые стенки с прямым скосом, вентиляция передней панели опционально | | 32 | `32-photo-trace-ai` | 25a, 29 | v1.3.0. Фото-вырезы: альтернативный нейросетевой движок силуэтов (в браузере, веса по согласию, гибридный точный край), классический остаётся по умолчанию как baseline; сравнение движков | +| 33 | `33-keychain-ai-silhouette` | 32, 11, 29 | v1.4.0. Keychain Image: силуэт по ИИ (удаление фона на браузерной модели), те же ручки порога и сглаживания, один главный объект, результат хранится вместе с картинкой; яркость остаётся по умолчанию | ```mermaid gantt diff --git a/openspec/changes/33-keychain-ai-silhouette/.openspec.yaml b/openspec/changes/33-keychain-ai-silhouette/.openspec.yaml new file mode 100644 index 0000000..5c881ba --- /dev/null +++ b/openspec/changes/33-keychain-ai-silhouette/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-10-18 diff --git a/openspec/changes/33-keychain-ai-silhouette/design.md b/openspec/changes/33-keychain-ai-silhouette/design.md new file mode 100644 index 0000000..b64b3df --- /dev/null +++ b/openspec/changes/33-keychain-ai-silhouette/design.md @@ -0,0 +1,45 @@ +# Design + +## Context + +`decodeImage` returns `GrayImage {gray, alpha | null}`; the image is stored in IndexedDB and registered once in the worker (`imageRegistry`), params reference it by `imageId`. `buildSilhouette(img, s)` calls `toMask`, which uses `alpha ≥ 128` when the image has transparency, otherwise a brightness threshold; closing, area detection, specks and grow follow. `32-photo-trace-ai` provides `src/ml/` (worker, client, registry with pinned models, per-model backend and `maskDefaults`), the model manager and the mask-tuning maths (smoothing then threshold on a soft alpha). + +A keychain differs from a photo cutout in three ways that shape the design: the input is an arbitrary image (any aspect ratio, up to 4096 px) and not a rectified sheet; the result is one subject for one piece of printed plastic; and the silhouette is later sampled on a grid of 0.2–0.5 mm cells, so edge accuracy is far less critical than for a pocket that must fit a tool (a 50 mm keychain over a 1024 px matte is 0.05 mm per pixel). + +## Goals / Non-Goals + +**Goals:** +- Photo in, clean silhouette out, without leaving the app, with the same two controls and models as `32`. +- Brightness stays the default and unchanged; no image or mask leaves the browser. +- Reloading restores the same silhouette without running the network. + +**Non-Goals:** +- Editing the mask by brush, selecting objects by click, depth relief, Shape mode. + +## Decisions + +- **Soft matte as a stored plane, silhouette computed in the worker.** The main thread runs the network (the `32` client) and gets a soft alpha at the image's resolution (0–255, `Uint8Array`). It is registered once in the worker (`registerMatte`, registry `matteRegistry` like `textRegistry`) and params carry `silhouette.ai.matteId`. In `buildSilhouette`, when `source === 'ai'` and the matte exists, the image's alpha is replaced by `smoothThreshold(matte, ai.smooth, ai.threshold)` and the existing path (`toMask` alpha branch, close, areas, specks, grow) runs unchanged. Moving a slider therefore only rebuilds the silhouette in the worker, never re-runs the network, and the matte is not re-sent. Alternative: send a ready hard mask on each slider move. Rejected: re-registers megabytes per move and breaks the "params reference ids" convention. +- **Input to the network.** The whole image, long side capped at 2048 for inference (the stored image keeps its own size, ≤ 4096), fitted into the model's square input with the aspect ratio kept and the borders filled with the model's mean colour (the PoC "whole image" mode does exactly this); the output is cropped back and resampled bilinearly to the image size. The paper-sheet stretch used in `32` is not appropriate here. No hybrid edge: the resolution argument above makes it unnecessary, and the brightness image is not a reliable edge cue for photos. +- **Soft alpha semantics.** The matte keeps the network's probability, so the two controls mean the same as in `32`: threshold decides how far the outline reaches into the soft edge (default per model, 0.5 as a fallback), smoothing radius is a length in image pixels converted from mm of the keychain size (`LengthField`, mm of the final keychain, so the same value gives the same visual smoothness at any resolution). Defaults come from `maskDefaults` of the model. +- **Keep only the largest object.** A component filter after the threshold keeps the connected component with the largest area (8-connected, `labelComponents`) and drops the rest; enclosed gaps are untouched and become areas as before. Default on for AI, off for Brightness (where several shapes can be intentional, e.g. a logo with a separate dot). Stored as `ai.keepLargest`. +- **Persistence.** `imageStore` records get a second kind of entry, `matte`, keyed by `imageId` + `model` + `revision`, with the plane and its size; stored together with the original in the same DB (`gridfinity-keychain`), memory fallback with the existing `persistent` warning. The params store `source`, `ai.model`, `threshold`, `smooth`, `keepLargest`; on load the matte is read and registered. Deleting the model in the manager does not delete the matte; deleting or replacing the image does. If the matte is missing (cleared storage) the section shows "run again" and the silhouette falls back to Brightness until it is run, never to an empty mask. +- **Staleness.** The matte is tied to the image and the model: replacing the image drops it; choosing a different model keeps the old matte until the user runs the new one (shown as "matte from "), so a switch never silently re-runs a download-sized job. +- **Relation to existing alpha.** If the image already has transparency, the section shows the note "this image has transparency, it is used as is" with AI still selectable (the user may want a different cut), and the AI run ignores the image's alpha as input. SVG is rasterised by the existing path and is not offered AI. +- **UI.** `SilhouetteSection` gets `SegmentedControl` From: Brightness | AI. Under AI: model picker (from the manager, with a link to open it), the Remove-background button with progress and cancel, the shared `MaskTuning` block (edge reach, smoothness, per-model defaults, reset, soft-matte greyscale view, which can also open in the enlarged mask dialog), the keep-largest toggle and the hints. Brightness controls (threshold, invert) are hidden under AI because they have no effect there; close, fill holes, specks and grow stay, since they work on the resulting mask. The mask preview, areas and everything downstream read the resulting silhouette as today. +- **Hints.** After a run, if the image has more than 2 distinct dominant colours in the border band the hint is silent; if the image is nearly two-tone (a logo on a clean background: Otsu separability high, a border band of one colour) a hint says Brightness is usually better and quicker. Pure text, never blocks. +- **Performance.** The run is a few seconds to ~20 s depending on model and backend (see `FINDINGS.md`); the dialog shows progress, the panel stays usable, a new run cancels an old one (latest wins via the client). Huge images are capped for inference only. +- **Versioning of params.** `migrateKeychain` fills `source: 'brightness'` and the `ai` defaults for old records, so stored keychains are unchanged; with `source: 'brightness'` the pipeline output equals the current one (regression volumes in the existing tests). + +## Risks / Trade-offs + +- [The network picks the wrong subject or several subjects] → keep-largest handles stray bits; a different model or the controls help; click-to-select is a later change. +- [Fine details (fur, hair, thin chains) are lost or merged] → smoothing 0 and a lower threshold keep them; the grid cell limits the final detail anyway; the soft matte view shows what the network saw. +- [A 1024 px matte of a 4000 px photo is soft] → fine for a 0.2–0.5 mm grid on a keychain-sized part; the matte is upsampled bilinearly, not with fake detail. +- [Large downloads] → handled by the shared model manager and consent of `32`; a keychain run uses a model that may already be there from Cutout. +- [WebGPU-only models on devices without WebGPU] → the picker marks them unavailable with the reason (capability detection of `32`) and offers WASM models. +- [Storage growth] → a matte is about the size of the image's gray plane (≤ 16 MB at 4096²); stored once per image and model, removed with the image; memory fallback if storage is refused. + +## Open Questions + +- Whether the default model for Keychain should differ from the Cutout default (photos of animals and people versus tools on paper); the PoC "whole image" mode and a few pet and portrait photos from the owner decide it. +- Whether to offer a one-click "AI remove background" on import for images that look like photos (no transparency, busy border), or keep it manual. Starting manual. diff --git a/openspec/changes/33-keychain-ai-silhouette/proposal.md b/openspec/changes/33-keychain-ai-silhouette/proposal.md new file mode 100644 index 0000000..dd4a129 --- /dev/null +++ b/openspec/changes/33-keychain-ai-silhouette/proposal.md @@ -0,0 +1,32 @@ +# Proposal + +## Why + +Keychain Image mode builds its silhouette from a threshold (Otsu or manual) on brightness, or from the alpha channel when the image has one. A logo on a white background works. A photo (a pet, a face, an object on a table) does not: brightness cannot tell the subject from the background, and the user fights the threshold, closing and areas without getting a clean outline. The usual answer is to remove the background first in some other tool and import a PNG with alpha. + +`32-photo-trace-ai` adds a browser-side neural engine for photo cutouts: an ML worker, a model manager, permissive-licence models and the two mask controls (threshold and smoothing). The same machinery produces exactly what Keychain needs, a soft alpha of the main subject, and the pipeline already has a place for it ("alpha wins"). This change connects them, so a photo becomes a keychain without leaving the app. + +## What Changes + +- **Silhouette source.** In Image mode the Silhouette section gets a choice "From: Brightness | AI". Brightness is today's behaviour, untouched and the default. AI removes the background with a model from the model manager of `32`. +- **Remove background (AI).** A button runs the network once on the whole image (aspect kept, padded to the model's input), shows progress, and stores the soft alpha ("matte") next to the image. The original image is kept; switching back to Brightness needs no new image. +- **Mask controls.** The same two user-facing controls as in `32` (edge reach = threshold, outline smoothness = smoothing radius) turn the stored soft matte into the silhouette. Changing them re-evaluates the mask without running the network. Defaults are per model (`maskDefaults` of the registry). +- **Keep the main object.** An option "Keep only the largest object" (on by default for AI) drops the stray bits a network sometimes leaves in the background; enclosed gaps stay and remain *areas* as before. +- **Persistence.** The matte is stored in IndexedDB with the image (`imageStore`), keyed by image, model and revision, so a reload restores the same silhouette without the network, and removing the model from the manager does not break an already computed matte. +- **Hints.** A transparent PNG keeps using its own alpha (AI is offered but not suggested); a logo on a clean background gets a hint that Brightness is usually better. SVG is not offered AI. +- **PoC.** `poc/ai/` has a "whole image (Keychain)" mode with padding, so models can be checked on keychain pictures before the work starts. + +## Capabilities + +### New Capabilities +- `keychain-ai-silhouette`: the AI silhouette source for Keychain Image mode, its mask controls, persistence and hints. + +### Modified Capabilities + +## Impact + +- `geometry/keychain.ts` (`SilhouetteParams` gets `source: 'brightness' | 'ai'` and `ai {model, threshold, smooth, keepLargest}`; `buildSilhouette` builds the alpha from the registered matte, then the unchanged pipeline), a matte registry in the worker next to `imageRegistry` (`ModelClient.registerMatte`, params carry `ai.matteId`), `features/keychain/` (`SilhouetteSection` source switch, `AiSilhouette` block, `useMatte`, `imageStore` matte records, migration normalising the new fields), reuse of `32`'s `src/ml/`, `ModelManager`, `MaskTuning`; strings in en and ru; tests. +- With `source: 'brightness'` the output equals the current one (regression volumes kept). +- Out of scope: Shape mode (no image), depth maps as a relief source, click-to-select or erase/restore brush on the mask, several subjects as separate keychains, background replacement or colour output, upscaling small images, any new model beyond the registry of `32`, Rack and other products. +- Version 1.4.0 (minor: a new option). +- Depends on: `32-photo-trace-ai` (ML runtime, model manager, mask tuning, registry), `11-keychain-relief`, `29-beta-label`. diff --git a/openspec/changes/33-keychain-ai-silhouette/specs/keychain-ai-silhouette/spec.md b/openspec/changes/33-keychain-ai-silhouette/specs/keychain-ai-silhouette/spec.md new file mode 100644 index 0000000..cd9d591 --- /dev/null +++ b/openspec/changes/33-keychain-ai-silhouette/specs/keychain-ai-silhouette/spec.md @@ -0,0 +1,122 @@ +# Spec Delta + +## Purpose +Defines the AI silhouette source for Keychain Image mode: removing the background of a photo with a model that runs in the browser, tuning the result and keeping it across reloads. + +## ADDED Requirements + +### Requirement: Silhouette source +In Image mode the user SHALL be able to choose where the silhouette comes from: Brightness (the existing threshold, invert and alpha behaviour) or AI (background removal by a model). Brightness SHALL remain the default and SHALL behave exactly as before. Choosing a source SHALL NOT change the image, and switching back and forth SHALL need no new import. + +#### Scenario: Default +- **WHEN** an image is imported +- **THEN** the source is Brightness and the silhouette and relief are the same as without this feature + +#### Scenario: Switching +- **WHEN** the user switches from AI to Brightness and back +- **THEN** each source shows its own silhouette from the same image, and an already computed AI result is not computed again + +#### Scenario: Controls of the source +- **WHEN** the source is AI +- **THEN** the brightness threshold and invert are not shown, and close gap, fill areas, remove specks and grow are still available + +### Requirement: Remove background with AI +With the AI source the user SHALL be able to run background removal on the whole image with a model chosen from the model manager. The run SHALL show progress and be cancellable, SHALL work on any image aspect ratio without distorting it, and SHALL keep the original image. The image and the result SHALL stay in the browser. If no model is ready, the model manager SHALL open and nothing SHALL be downloaded without the user's confirmation. + +#### Scenario: Run +- **WHEN** the user presses Remove background with a model ready +- **THEN** progress is shown, and on completion the silhouette preview and the relief show the subject without the background + +#### Scenario: Any proportions +- **WHEN** the image is a wide landscape or a tall portrait +- **THEN** the subject's outline is not stretched compared with the image + +#### Scenario: No model +- **WHEN** the user presses Remove background and no model is downloaded +- **THEN** the model manager opens, and the run starts only after a model is downloaded and chosen + +#### Scenario: Cancel +- **WHEN** the user cancels a run or starts another +- **THEN** the cancelled run's result is discarded and does not replace the current silhouette + +#### Scenario: No upload +- **WHEN** a run is made +- **THEN** no network request carries image data + +### Requirement: Mask controls +With the AI source the user SHALL have the same two controls as for photo cutouts: edge reach (how far the outline reaches into the soft edge of the subject) and outline smoothness (a length in mm of the keychain). Changing either SHALL update the silhouette and the relief without running the model again. Each model SHALL have its own defaults, a reset SHALL restore them, and the soft result SHALL be viewable as a greyscale image. + +#### Scenario: Live +- **WHEN** the user moves edge reach or smoothness +- **THEN** the mask preview and the relief update and the model is not run again + +#### Scenario: Smoothness in mm +- **WHEN** the same keychain size and smoothness value are used on a small and a large image +- **THEN** the outline is smoothed by the same distance in mm on the part + +#### Scenario: Model defaults +- **WHEN** another model is chosen and its result is computed +- **THEN** the controls show that model's defaults unless the user has changed them for this image + +### Requirement: Keep the main object +With the AI source the user SHALL be able to keep only the largest connected object of the silhouette, on by default, so that stray fragments of the background are dropped. Enclosed gaps inside the kept object SHALL remain areas with their modes as before. + +#### Scenario: Stray fragments +- **WHEN** the model leaves a small speck of background away from the subject and the option is on +- **THEN** the speck is not part of the silhouette + +#### Scenario: Off +- **WHEN** the option is turned off +- **THEN** all objects the mask contains are kept + +#### Scenario: Areas +- **WHEN** the kept object has an enclosed gap such as a ring handle +- **THEN** it appears in the area list with a numeric id and the usual modes + +### Requirement: Persistence of the AI result +The AI result SHALL be stored with the image in the browser's storage and restored after a reload together with the source, model and controls, without running the model again. Deleting the model from the model manager SHALL NOT remove an existing result. Replacing or removing the image SHALL remove its result. If the stored result is missing, the app SHALL say so, offer to run again and use Brightness meanwhile, never an empty silhouette. If storage is unavailable the AI source SHALL work for the session and the user SHALL be warned that the result will not persist. + +#### Scenario: Reload +- **WHEN** the user runs AI background removal, adjusts smoothness and reloads +- **THEN** the same silhouette is shown without running the model + +#### Scenario: Model deleted +- **WHEN** the user deletes the used model in the model manager +- **THEN** the silhouette from the stored result remains + +#### Scenario: Result lost +- **WHEN** the stored result is gone but the AI source is selected +- **THEN** a message offers to run again, and the silhouette follows Brightness until then + +#### Scenario: Storage unavailable +- **WHEN** persistent storage cannot be used +- **THEN** the AI result works in memory and a warning is shown + +### Requirement: Hints +The app SHALL tell the user when an image has its own transparency (it is used as is, AI can still be chosen) and when an image looks like a two-tone logo on a plain background (Brightness is usually quicker and sharper). SVG images SHALL NOT offer the AI source. Hints SHALL never block an action. + +#### Scenario: Transparent image +- **WHEN** a PNG with transparency is imported +- **THEN** a note says its transparency is used and the AI source remains selectable + +#### Scenario: Logo +- **WHEN** the image is a black logo on a white background +- **THEN** a hint suggests Brightness + +#### Scenario: SVG +- **WHEN** the image is an SVG +- **THEN** the AI source is not offered + +### Requirement: Compatibility +Keychains saved before this feature SHALL load with the Brightness source and unchanged settings, and with the Brightness source the relief SHALL be identical to what it was before this feature. + +#### Scenario: Old record +- **WHEN** a record without the new fields is loaded +- **THEN** it opens with the Brightness source and produces the same relief as before + +### Requirement: Language +All text of the AI silhouette source, its controls and hints SHALL be available in every language of the app. + +#### Scenario: Russian +- **WHEN** the interface language is Russian +- **THEN** the source choice, button, controls, progress, hints and warnings are in Russian diff --git a/openspec/changes/33-keychain-ai-silhouette/tasks.md b/openspec/changes/33-keychain-ai-silhouette/tasks.md new file mode 100644 index 0000000..663490a --- /dev/null +++ b/openspec/changes/33-keychain-ai-silhouette/tasks.md @@ -0,0 +1,30 @@ +# Tasks + +## 1. Check on keychain pictures (spike) + +- [ ] 1.1 With `poc/ai/` in "whole image (Keychain)" mode, run the models of `32`'s shortlist on 6–10 pictures: a pet, a portrait, an object on a table, a plush toy, a logo with transparency, a two-tone logo, a wide landscape-format photo. Record per picture and model: subject found whole, stray fragments, edge quality at the threshold and smoothing that look best, time. Write the table and the per-model `maskDefaults` for keychain pictures into `design.md` (Open Questions) and `poc/ai/FINDINGS.md`. If a model cannot be used on keychain pictures say so and stop for `/opsx:update`. + +## 2. Geometry (React-free) + +- [ ] 2.1 `geometry/raster/matte.ts`: `smoothThreshold(matte, w, h, radiusPx, threshold) → Uint8Array alpha`, `keepLargest(mask, w, h)`. Tests: threshold monotone (lower never shrinks), radius 0 equals plain threshold, smoothing does not lengthen the outline, keepLargest drops a speck and keeps an enclosed gap, ties and empty mask are safe. +- [ ] 2.2 `SilhouetteParams.source` and `ai {model, threshold, smooth, keepLargest, matteId}`, `buildSilhouette` builds the alpha from the registered matte (`matteRegistry` in `geometry/model.ts`, `ModelClient.registerMatte`) and runs the unchanged pipeline; missing matte under `ai` falls back to Brightness and reports it in `SilhouetteResult`. Tests: with `source: 'brightness'` the existing regression volumes and bounding boxes are unchanged; a synthetic image plus matte gives the expected mask and the same relief as the equivalent alpha PNG; slider changes do not touch the registered matte; a missing matte reports the fallback. +- [ ] 2.3 `migrateKeychain` fills `source: 'brightness'` and the `ai` defaults for old records and normalises partial values. Tests: old record round-trip, partial and corrupt `ai` data fall back to defaults. + +## 3. Image side + +- [ ] 3.1 `features/keychain/matteStore.ts` (or `imageStore` records of kind `matte`): put/get/delete by `imageId` + model + revision in the existing DB, memory fallback with the `persistent` flag, removal with the image. Tests with `fake-indexeddb`: round trip, replacing the image drops its matte, deleting a model does not, storage refusal falls back to memory. +- [ ] 3.2 `features/keychain/matte.ts`: whole-image run through the `32` client (aspect kept, padded input, inference cap 2048, output cropped and resampled to the image size, progress, cancel, latest wins) and `useMatte` (state, restore on load, registration in the worker). Tests with a mocked client: padded input geometry for a wide and a tall image, output mapped back without distortion, cancel discards, a stale result does not replace a newer one, restore registers the stored matte without calling the client. + +## 4. UI + +- [ ] 4.1 `SilhouetteSection`: source switch Brightness | AI (hides brightness threshold and invert under AI), `AiSilhouette` block (model picker with a link to the manager, Remove background button with progress and cancel, shared `MaskTuning` with per-model defaults and the soft-matte view, keep-largest toggle, lost-result message, hints for transparent, logo and SVG). Strings in `en.json` and `ru.json`. No artboard exists: built from `ui/` components and `theme.css` tokens, said in the commit; compare in en and ru and at 390 px. Component tests: Brightness default and unchanged, controls hide and show per source, the button opens the manager when no model is ready, sliders update the silhouette without a client call, keep-largest, hints, SVG has no AI, lost result falls back with a message, key parity. +- [ ] 4.2 The enlarged mask dialog and `MaskPreview` show the AI silhouette and can switch to the soft matte view; areas, selection and everything downstream work on the AI silhouette. Tests: preview follows the source, the area list is built from the AI mask. + +## 5. Version + +- [ ] 5.1 Bump to 1.4.0: `package.json` and a `CHANGELOG.md` entry `1.4.0 — 33-keychain-ai-silhouette`. Test: the existing version test passes with the new number. + +## 6. Verification + +- [ ] 6.1 `npm test` and `npm run build` are green, the dev server starts, no console errors; with the Brightness source the existing keychain tests and volumes are unchanged. +- [ ] 6.2 In the browser, en and ru and at 390 px, with a pet photo, a portrait and a logo: AI run with progress, silhouette and relief appear, sliders update without a model run (network panel shows no model activity), keep-largest drops a stray bit, a ring-shaped gap becomes an area, reload restores the same relief without a run, deleting the model keeps the silhouette, switching to Brightness and back works, a transparent PNG and an SVG behave as described, the exported STL is one valid piece, no request carries image data. diff --git a/poc/ai/index.html b/poc/ai/index.html index 1782942..1442506 100644 --- a/poc/ai/index.html +++ b/poc/ai/index.html @@ -11,7 +11,7 @@

PoC: нейросетевые силуэты на выпрямленном листе

Снимок - +
Модель
Бэкенд diff --git a/poc/ai/main.ts b/poc/ai/main.ts index 6500685..3715cfd 100644 --- a/poc/ai/main.ts +++ b/poc/ai/main.ts @@ -36,23 +36,28 @@ async function fetchModel(c: Candidate): Promise { return buf } -async function loadImage(): Promise { +async function loadImage(maxSide = 0): Promise { const f = $('file').files?.[0] const src = f ? URL.createObjectURL(f) : `./samples/${$('sample').value}.jpeg` const img = new Image(); img.src = src; await img.decode() - const cv = document.createElement('canvas'); cv.width = img.naturalWidth; cv.height = img.naturalHeight - const g = cv.getContext('2d')!; g.drawImage(img, 0, 0) + const k = maxSide ? Math.min(1, maxSide / Math.max(img.naturalWidth, img.naturalHeight)) : 1 + const cv = document.createElement('canvas'); cv.width = Math.round(img.naturalWidth * k); cv.height = Math.round(img.naturalHeight * k) + const g = cv.getContext('2d')!; g.drawImage(img, 0, 0, cv.width, cv.height) return g.getImageData(0, 0, cv.width, cv.height) } /** Нормализованный вход NCHW float32: лист из rgb растра, масштабированный до s × s. */ -function toInput(rect: Rectified, c: Candidate, s: number): Float32Array { +function toInput(rect: Rectified, c: Candidate, s: number, pad = false): Float32Array { const w = rect.image.width, h = rect.image.height const src = new ImageData(w, h) for (let i = 0; i < w * h; i++) { src.data[i * 4] = rect.rgb![i * 3]; src.data[i * 4 + 1] = rect.rgb![i * 3 + 1]; src.data[i * 4 + 2] = rect.rgb![i * 3 + 2]; src.data[i * 4 + 3] = 255 } const a = document.createElement('canvas'); a.width = w; a.height = h; a.getContext('2d')!.putImageData(src, 0, 0) const b = document.createElement('canvas'); b.width = s; b.height = s - const bg = b.getContext('2d')!; bg.imageSmoothingQuality = 'high'; bg.drawImage(a, 0, 0, s, s) + const bg = b.getContext('2d')!; bg.imageSmoothingQuality = 'high' + if (pad) { // вписать с сохранением пропорций, поля — цвет, нейтральный для модели (среднее по ImageNet) + bg.fillStyle = `rgb(${c.mean.map((m) => Math.round(m * 255)).join(',')})`; bg.fillRect(0, 0, s, s) + const r = Math.min(s / w, s / h); bg.drawImage(a, (s - w * r) / 2, (s - h * r) / 2, w * r, h * r) + } else bg.drawImage(a, 0, 0, s, s) const d = bg.getImageData(0, 0, s, s).data const out = new Float32Array(3 * s * s) for (let i = 0; i < s * s; i++) for (let k = 0; k < 3; k++) out[k * s * s + i] = (d[i * 4 + k] / 255 - c.mean[k]) / c.std[k] @@ -60,15 +65,17 @@ function toInput(rect: Rectified, c: Candidate, s: number): Float32Array { } /** Выход модели s × s → альфа w × h (билинейно), 0…1. Модели с несколькими выходами: берётся первый. */ -function toAlpha(o: Float32Array, s: number, w: number, h: number, sigmoid: boolean): Float32Array { +function toAlpha(o: Float32Array, s: number, w: number, h: number, sigmoid: boolean, pad = false): Float32Array { // значения вне 0…1 — логиты: sigmoid (признак из реестра подстраховывается самим диапазоном выхода) let lo = Infinity, hi = -Infinity; for (const x of o) { if (x < lo) lo = x; if (x > hi) hi = x } const v = sigmoid || lo < -0.001 || hi > 1.001 ? o.map((x) => 1 / (1 + Math.exp(-x))) : o const out = new Float32Array(w * h) + const r = pad ? Math.min(s / w, s / h) : 0 + const kx = pad ? r : s / w, ky = pad ? r : s / h, ox = pad ? (s - w * r) / 2 : 0, oy = pad ? (s - h * r) / 2 : 0 for (let j = 0; j < h; j++) { - const fy = Math.min(s - 1.001, Math.max(0, (j + 0.5) * s / h - 0.5)), y0 = Math.floor(fy), ty = fy - y0 + const fy = Math.min(s - 1.001, Math.max(0, (j + 0.5) * ky + oy - 0.5)), y0 = Math.floor(fy), ty = fy - y0 for (let i = 0; i < w; i++) { - const fx = Math.min(s - 1.001, Math.max(0, (i + 0.5) * s / w - 0.5)), x0 = Math.floor(fx), tx = fx - x0 + const fx = Math.min(s - 1.001, Math.max(0, (i + 0.5) * kx + ox - 0.5)), x0 = Math.floor(fx), tx = fx - x0 const a = v[y0 * s + x0], b = v[y0 * s + x0 + 1], cc = v[(y0 + 1) * s + x0], d = v[(y0 + 1) * s + x0 + 1] out[j * w + i] = (a * (1 - tx) + b * tx) * (1 - ty) + (cc * (1 - tx) + d * tx) * ty } @@ -115,19 +122,27 @@ async function run() { const s = +($('size').value || c.size), thr = +$('thr').value, margin = +$('margin').value log(`модель ${c.id}, лицензия ${c.license}, вход ${s}, бэкенд ${ep}, потоков ${threads}, isolated=${self.crossOriginIsolated}`) - const im = await loadImage() + const whole = $('whole').checked + const im = await loadImage(whole ? 2048 : 0) const gray = toGrayImage(im.data, im.width, im.height), color = toColorImage(im.data, im.width, im.height) - const found = detectPaper(color) - if (!found) { log('лист не найден'); return } - const guess = guessPaper(found.corners, im.width, im.height) - const paper = paperSize(guess?.preset ?? 'a4', guess?.orientation ?? 'portrait') - const rect = rectify(gray, found.corners, paper, color.rgb)! + let rect: Rectified, seg: ReturnType + if (whole) { + // режим Keychain: вся картинка как есть, без листа; масштаб 1 пиксель = 1 «мм» (размеры в таблице — в пикселях) + rect = { image: gray, rgb: color.rgb, scale: 1, paper: { width: im.width, height: im.height } } + seg = { width: im.width, height: im.height, scale: 1, labels: new Int32Array(im.width * im.height), silhouettes: [], sensitivity: 0, defaultSensitivity: 0 } + log(`режим Keychain: картинка ${im.width}×${im.height}, вписывание с полями до ${s}×${s}`) + } else { + const found = detectPaper(color) + if (!found) { log('лист не найден'); return } + const guess = guessPaper(found.corners, im.width, im.height) + const paper = paperSize(guess?.preset ?? 'a4', guess?.orientation ?? 'portrait') + rect = rectify(gray, found.corners, paper, color.rgb)! + log(`снимок ${im.width}×${im.height}, лист ${paper.width}×${paper.height} мм (${guess?.preset}/${guess?.orientation}), растр ${rect.image.width}×${rect.image.height}, ${rect.scale.toFixed(2)} px/мм`) + const t0 = performance.now(); seg = segment(rect) + log(`классика: ${seg.silhouettes.length} силуэтов за ${(performance.now() - t0).toFixed(0)} мс`) + } const W = rect.image.width, H = rect.image.height - log(`снимок ${im.width}×${im.height}, лист ${paper.width}×${paper.height} мм (${guess?.preset}/${guess?.orientation}), растр ${W}×${H}, ${rect.scale.toFixed(2)} px/мм`) - let t = performance.now() - const seg = segment(rect) - log(`классика: ${seg.silhouettes.length} силуэтов за ${(performance.now() - t).toFixed(0)} мс`) const key = `${c.id}|${ep}|${threads}` let session = sessions.get(key) @@ -140,25 +155,25 @@ async function run() { sessions.set(key, session) } const inName = session.inputNames[0] - const data = toInput(rect, c, s) + const data = toInput(rect, c, s, whole) const runs: number[] = []; let out!: ort.InferenceSession.OnnxValueMapType for (let i = 0; i < 2; i++) { t = performance.now(); out = await session.run({ [inName]: new ort.Tensor('float32', data.slice(), [1, 3, s, s]) }); runs.push(performance.now() - t) } log(`инференс: 1-й ${runs[0].toFixed(0)} мс (прогрев), 2-й ${runs[1].toFixed(0)} мс`) const o = out[session.outputNames[0]] - const alpha = toAlpha(o.data as Float32Array, s, W, H, c.sigmoid) + const alpha = toAlpha(o.data as Float32Array, s, W, H, c.sigmoid, whole) - state = { rect, seg, alpha, runs, c, ep, threads, s } + state = { rect, seg, alpha, runs, c, ep, threads, s, whole } evaluate() $('status').textContent = 'готово' } -interface State { rect: Rectified; seg: ReturnType; alpha: Float32Array; runs: number[]; c: Candidate; ep: string; threads: number; s: number } +interface State { rect: Rectified; seg: ReturnType; alpha: Float32Array; runs: number[]; c: Candidate; ep: string; threads: number; s: number; whole: boolean } let state: State | null = null /** Всё после инференса: сглаживание альфы, порог, компоненты, сравнение. Модель не запускается, поэтому ползунки работают мгновенно. */ function evaluate() { if (!state) return - const { rect, seg, runs, c, ep, threads, s } = state + const { rect, seg, runs, c, ep, threads, s, whole } = state const W = rect.image.width, H = rect.image.height const thr = +$('thr').value, margin = +$('margin').value, smoothMm = +$('smooth').value let alpha = state.alpha @@ -182,7 +197,8 @@ function evaluate() { const px = scaleMm(rect.scale) rows.push(`${cs.widthMm.toFixed(1)}×${cs.heightMm.toFixed(1)}${a.wMm.toFixed(1)}×${a.hMm.toFixed(1)}${(a.wMm - cs.widthMm).toFixed(2)}${(a.hMm - cs.heightMm).toFixed(2)}${(i2 / u2).toFixed(3)}${(big * px).toFixed(0)} мм²${(small * px).toFixed(0)} мм²`) } - const extra = ai.sils.length - seg.silhouettes.length + if (whole) for (const a of ai.sils) rows.push(`объект ${a.id}${a.wMm.toFixed(0)}×${a.hMm.toFixed(0)} px, площадь ${a.areaMm2.toFixed(0)} px²`) + const extra = whole ? 0 : ai.sils.length - seg.silhouettes.length $('tbl').innerHTML = rows.join('') + `всего: классика ${seg.silhouettes.length}, AI ${ai.sils.length}${extra ? ' (лишние/слитые!)' : ''}; общий IoU ${(inter / uni).toFixed(3)}; инференс ${runs[1].toFixed(0)} мс` ;(window as unknown as { __poc: unknown }).__poc = { model: c.id, ep, threads, size: s, classic: seg.silhouettes.map((x) => [x.widthMm, x.heightMm, x.areaMm2]), ai: ai.sils.map((x) => [x.wMm, x.hMm, x.areaMm2]), iou: inter / uni, ms: runs } // длина границы: меньше — гладче контур (дрожащий край = много лишних угловых пикселей) From 08ef2c01494ea0efa70faad7278dbccf71742bca Mon Sep 17 00:00:00 2001 From: skarakovski Date: Sun, 18 Oct 2026 21:22:55 +0700 Subject: [PATCH 14/38] plan: 34-keychain-photo-relief (depth relief from a photo, v1.5.0); poc: depth models and cat sample Co-Authored-By: Claude Sonnet 5.5 --- openspec/ROADMAP.md | 1 + .../34-keychain-photo-relief/.openspec.yaml | 2 + .../34-keychain-photo-relief/design.md | 46 +++++++ .../34-keychain-photo-relief/proposal.md | 31 +++++ .../specs/keychain-photo-relief/spec.md | 113 ++++++++++++++++++ .../changes/34-keychain-photo-relief/tasks.md | 29 +++++ poc/ai/index.html | 2 +- poc/ai/main.ts | 30 +++-- poc/ai/models.ts | 7 ++ poc/ai/samples/cat.jpeg | Bin 0 -> 243033 bytes 10 files changed, 249 insertions(+), 12 deletions(-) create mode 100644 openspec/changes/34-keychain-photo-relief/.openspec.yaml create mode 100644 openspec/changes/34-keychain-photo-relief/design.md create mode 100644 openspec/changes/34-keychain-photo-relief/proposal.md create mode 100644 openspec/changes/34-keychain-photo-relief/specs/keychain-photo-relief/spec.md create mode 100644 openspec/changes/34-keychain-photo-relief/tasks.md create mode 100644 poc/ai/samples/cat.jpeg diff --git a/openspec/ROADMAP.md b/openspec/ROADMAP.md index f2fe2ac..721810f 100644 --- a/openspec/ROADMAP.md +++ b/openspec/ROADMAP.md @@ -43,6 +43,7 @@ | 31 | `31-rack-shelf` | 26, 29 | v1.2.0. Полка: передняя панель с ушами лицом на столе, пол с вентиляцией (щели, можно глухой), боковые стенки с прямым скосом, вентиляция передней панели опционально | | 32 | `32-photo-trace-ai` | 25a, 29 | v1.3.0. Фото-вырезы: альтернативный нейросетевой движок силуэтов (в браузере, веса по согласию, гибридный точный край), классический остаётся по умолчанию как baseline; сравнение движков | | 33 | `33-keychain-ai-silhouette` | 32, 11, 29 | v1.4.0. Keychain Image: силуэт по ИИ (удаление фона на браузерной модели), те же ручки порога и сглаживания, один главный объект, результат хранится вместе с картинкой; яркость остаётся по умолчанию | +| 34 | `34-keychain-photo-relief` | 33, 32, 12, 17, 29 | v1.5.0. Keychain Image: рельеф лица/объекта из фото через карту глубины (Depth Anything V2 Small), смешивание с яркостью, усиление локального контраста, пресеты глубины; результат хранится с картинкой | ```mermaid gantt diff --git a/openspec/changes/34-keychain-photo-relief/.openspec.yaml b/openspec/changes/34-keychain-photo-relief/.openspec.yaml new file mode 100644 index 0000000..5c881ba --- /dev/null +++ b/openspec/changes/34-keychain-photo-relief/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-10-18 diff --git a/openspec/changes/34-keychain-photo-relief/design.md b/openspec/changes/34-keychain-photo-relief/design.md new file mode 100644 index 0000000..a8a90b5 --- /dev/null +++ b/openspec/changes/34-keychain-photo-relief/design.md @@ -0,0 +1,46 @@ +# Design + +## Context + +`SourceParams = {kind: 'distance' | 'brightness', contrast, gamma, invert, smooth, despeckle}`. In `buildGrid` the brightness source samples the image's gray plane at the grid nodes inside the silhouette (`gray`), maps it with `brightnessValue` and turns it into heights (`floor + value × (hmax − floor)`), with the rim ceiling, smoothing (masked Gaussian), profile, detail lines and patterns after it. `33` adds the AI silhouette and the matte registry and uses `src/ml/` of `32`. + +The photo-relief problem is not the silhouette but the interior. A depth map answers "what is closer to the camera", which for a face is the form. It is relative and smooth: it will not carry whiskers or fur texture, and it has soft errors at the subject's edge, which the matte mask covers. + +## Goals / Non-Goals + +**Goals:** +- A recognisable bas-relief of a face or object from one photo, with controls the user can understand. +- No change for existing keychains; no data leaves the browser. + +**Non-Goals:** +- Metric depth or 3D reconstruction, sculpting, face-specific models, multi-colour. + +## Decisions + +- **Depth plane like the text mask and the matte.** The main thread runs the depth model through the `32` client and gets a `Float32` plane normalised to 0…1 (2nd–98th percentile, farther = 0, nearer = 1) at the image's resolution; it is registered once in the worker (`registerDepth`, `depthRegistry`) and params carry `source.depthId`. `buildGrid` samples it where it samples brightness. Sliders (contrast, gamma, invert, smoothing, mix) then only rebuild in the worker. Alternative: bake the depth into the image's gray plane. Rejected: loses the original brightness needed for the mix and for switching sources. +- **Input and aspect.** The whole image, long side capped at 2048, padded or resized to the model's multiple of 14 (518 for Depth Anything V2 Small) keeping the aspect ratio; output resampled bilinearly back to the image size, as for the matte in `33`. +- **Model.** Depth Anything V2 Small ONNX (`onnx-community/depth-anything-v2-small-ONNX`, Apache-2.0): q4f16 20 MB, fp16 50 MB, weights in a separate `.onnx_data` file, so the ML runtime needs external-data support (path and bytes of the data file in the registry entry, SHA-256 for both files). The PoC runs it; Base and Large are non-commercial and not offered; Depth Anything 3 Small (Apache-2.0, newer) is a candidate to compare. The registry gets `kind: 'depth'` next to `matte`; the manager shows the kind and filters the picker by it. +- **Relief from depth.** Inside the AI silhouette the height is `floor + value × (hmax − floor)` with `value` = the mix of depth and local-contrast brightness. Because depth inside the silhouette may be nearly flat across the body, the depth is renormalised over the silhouette pixels only (robust min/max inside the mask), otherwise the background range would waste the height budget. The silhouette edge still goes through the bevel ceiling of the existing relief (the rim rolls off), so the edge of the cat does not stand as a cliff. +- **Mix with brightness.** `depthMix` (0–1, default 0.25 brightness): `value = (1 − m)·depth + m·brightnessDetail`, where `brightnessDetail` is the local-contrast brightness (below) re-centred to zero mean so it adds relief around the depth form instead of lifting the whole image. It brings fine structure (eye outline, whiskers, fur streaks) that depth smooths out. +- **Local contrast ("Detail boost").** `localContrast(gray, w, h, radiusPx, amount)`: subtract a large-radius Gaussian (≈ 1/12 of the shorter side), divide by a local standard deviation, soft-clip, `amount` 0 = off. Available for Brightness and Depth sources; default 0 for Brightness (unchanged output), 0.5 under Depth. It is what lets a flat-lit photo use the whole height range. +- **Range presets.** `Relief depth` presets Subtle / Portrait / Deep set `hmax − floor` and the profile bevel; they are data like the profile presets and show Custom when edited. Facial relief reads best with a modest range (about 1.5–3 mm over the plate) and a steep rim; the presets start there. +- **Persistence.** Like the matte of `33`: a `depth` record in `imageStore` keyed by `imageId` + model + revision, restored on load, kept when the model is deleted, dropped with the image; memory fallback with the `persistent` warning. If it is missing under `kind: 'depth'` the relief falls back to Distance with a "run again" message, never to an empty relief. +- **Not for shapes.** Shape mode has no image; the Photo-depth option appears only in Image mode, and only when the silhouette source is AI or the image otherwise has a usable mask (a depth relief outside a good mask is meaningless). +- **UI.** `ProfileSection`/Relief source: Distance | Brightness | Photo depth. Under Photo depth: model picker (depth models), Compute depth button with progress, the mix slider (Depth ↔ Detail), Detail boost, the existing contrast, gamma, invert and smoothing, the Relief depth presets, and a depth greyscale view (also in the enlarged mask dialog). Under Brightness: Detail boost appears. +- **Resolution warnings.** The existing `smoothResolution` and pattern resolution checks apply; a depth relief on a coarse grid gets a hint that a finer quality setting is needed for facial detail. +- **Performance.** Depth Anything V2 Small q4f16 is small; the PoC measures time and the backend it works on (WebGPU/WASM) per device. The run is cancellable and latest-wins like the matte. + +## Risks / Trade-offs + +- [Depth of a cat's fur and face is soft or wrong at edges] → the matte mask clips the edge, the rim roll-off hides the cliff, the mix adds detail; the PoC judges quality on the owner's cat photo before building. +- [Relative depth varies between photos] → normalised inside the silhouette; the user adjusts contrast, gamma, invert and range. +- [Fine detail is lost to the grid] → the quality setting and the existing resolution warning; a keychain-sized part cannot carry hair detail anyway, the portrait relief is about shape. +- [A second model (~20–50 MB) on top of the matte model] → shared manager and consent of `32`; both small. +- [External-data ONNX files need runtime support] → covered in the registry and tested in `32`'s runtime tasks (this change adds the case). +- [Printing a deep relief needs supports on steep faces] → the relief grows from a flat back (existing design), so it prints face up without supports; steep overhangs do not exist in a heightfield. + +## Open Questions + +- Whether depth alone is already good for a face or the brightness mix is essential: the PoC on the cat photo decides the default mix and boost. +- Depth Anything V2 Small versus V3 Small (Apache-2.0, 2026): compare quality and size in the PoC. +- Whether a face crop option (relief only inside a user-chosen window, with the rest flat) is wanted; left out for now. diff --git a/openspec/changes/34-keychain-photo-relief/proposal.md b/openspec/changes/34-keychain-photo-relief/proposal.md new file mode 100644 index 0000000..42e06cf --- /dev/null +++ b/openspec/changes/34-keychain-photo-relief/proposal.md @@ -0,0 +1,31 @@ +# Proposal + +## Why + +With `33-keychain-ai-silhouette` a photo gives a clean outline, but inside the outline the keychain is still a plain bevelled plate. A portrait of a cat (or a person, or a pet's face) is only recognisable by what is inside the outline: the eyes, the nose, the ears, the fur pattern. The owner tried exactly this: the outline of the cat is good, but the face cannot be seen on the keychain, so there is little point in working from photos. + +The relief already has a "brightness" height source, but it does not solve this: the brightness of a photo is lighting, not shape (a dark stripe on fur becomes a groove, a bright cheek a bump, a shadow a hole), and a flat-lit photo has too little tonal range inside the subject. What a face needs is shape: the nose proud of the cheeks, the eyes sunk, the ears leaning away. A monocular depth network gives a relative depth map from a single photo and runs in the browser; Depth Anything V2 Small (Apache-2.0) is 20–50 MB. The research and application notes (`tmp/ai/BROWSER_AI_APPLICATION.md`, idea A3) planned it for this purpose. Together with the soft matte of `33` this gives a bas-relief portrait. + +## What Changes + +- **Depth as a height source.** In Image mode the Relief source gets a third option, "Photo depth" next to Distance and Brightness. The depth map is computed once by a model from the model manager of `32`, stored with the image, and used inside the AI silhouette as the height field, with the existing contrast, gamma, invert and smoothing controls. +- **Mix with brightness.** A slider mixes depth with local-contrast brightness (0 % = depth only, 100 % = brightness only), because depth gives the form and brightness the fine detail (whiskers, fur, eye outline). The default favours depth. +- **Local contrast.** The brightness source gets an optional "Detail boost" (local contrast normalisation) so that a flat-lit photo reaches the tonal range the height needs; also useful for the plain Brightness source. +- **Range and base.** The height range of the photo relief, its floor and the rim stay the existing relief controls; a "Relief depth" preset (Subtle, Portrait, Deep) sets sensible range values. Printing notes: very fine detail below the grid cell is limited by the quality setting, as the existing resolution warning says. +- **Persistence.** The depth plane is stored with the image like the matte of `33`, keyed by image, model and revision, so a reload restores the relief without running the network. +- **PoC.** `poc/ai/` can run the depth model (kind `depth`, with external weights) on the owner's cat photo and shows the depth map next to the image, so the quality is judged before the work starts. + +## Capabilities + +### New Capabilities +- `keychain-photo-relief`: depth-based relief from a photo, its mix with brightness, local contrast and persistence. + +### Modified Capabilities + +## Impact + +- `geometry/keychain.ts` (`SourceParams.kind` gets `'depth'`, plus `depthMix`, `boost`; `buildGrid` takes the depth plane through the same path as brightness: resample to the grid, contrast/gamma/invert/smoothing, clamp to the silhouette; `registerDepth` and `depthRegistry` in the worker), `geometry/raster/` (`localContrast`, depth normalisation), `features/keychain/` (`ProfileSection` source choice, `PhotoReliefBlock`, `useDepth`, depth records in the store, migration), reuse of `src/ml/` and the model manager of `32` (a second model kind in the registry, `kind: 'depth'`, with external data support), strings in en and ru, tests. +- With `kind: 'distance'` or `'brightness'` and `boost: 0` the output equals the current one. +- Out of scope: real metric depth, 3D reconstruction, per-region editing of the relief or a sculpt brush, normal maps, colour output or multi-colour printing, face-specific models, Shape mode. +- Version 1.5.0 (minor: a new relief source). +- Depends on: `33-keychain-ai-silhouette`, `32-photo-trace-ai`, `12-keychain-patterns`, `17-keychain-smoothing`, `29-beta-label`. diff --git a/openspec/changes/34-keychain-photo-relief/specs/keychain-photo-relief/spec.md b/openspec/changes/34-keychain-photo-relief/specs/keychain-photo-relief/spec.md new file mode 100644 index 0000000..69e485e --- /dev/null +++ b/openspec/changes/34-keychain-photo-relief/specs/keychain-photo-relief/spec.md @@ -0,0 +1,113 @@ +# Spec Delta + +## Purpose +Defines a relief height source derived from the depth of a photo, so that the inside of a photographed subject, such as a face, shows as form on the keychain and not only as an outline. + +## ADDED Requirements + +### Requirement: Photo depth source +In Image mode the user SHALL be able to choose Photo depth as the relief height source, next to Distance and Brightness. The depth SHALL be computed once from the image by a model chosen in the model manager, run in the browser, and kept with the image. With this source, parts of the subject nearer to the camera SHALL be higher than parts farther from it, inside the silhouette, and the existing contrast, gamma, invert and smoothing controls SHALL apply to it. The image and the result SHALL stay in the browser. If no model is ready, the model manager SHALL open and nothing SHALL be downloaded without the user's confirmation. + +#### Scenario: Face form +- **WHEN** the depth of a photo of an animal's face is computed and Photo depth is the source +- **THEN** the nose and the front of the muzzle are higher than the cheeks and the surroundings of the face, inside the silhouette + +#### Scenario: Invert +- **WHEN** the user turns Invert on +- **THEN** the nearer parts become the lower ones + +#### Scenario: No model +- **WHEN** the user presses Compute depth and no depth model is downloaded +- **THEN** the model manager opens and the run starts only after a model is downloaded and chosen + +#### Scenario: Live controls +- **WHEN** the user changes contrast, gamma, smoothing or the mix +- **THEN** the relief updates without running the model again + +#### Scenario: No upload +- **WHEN** the depth is computed +- **THEN** no network request carries image data + +### Requirement: Use of the silhouette +The depth relief SHALL be limited to the silhouette. The depth range used for the height SHALL be taken from the pixels inside the silhouette, so that the background does not use up the height range, and the edge of the silhouette SHALL roll off with the relief's rim as for the other sources. + +#### Scenario: Range inside the subject +- **WHEN** the background is far away and the subject is shallow +- **THEN** the subject's relief spans the configured height range and does not stay almost flat + +#### Scenario: Edge +- **WHEN** the silhouette edge lies on a nearer part of the subject +- **THEN** the edge still rolls off to the rim height and does not stand as a vertical cliff + +### Requirement: Mix with brightness +The user SHALL be able to mix the depth with the brightness detail of the photo with a slider from depth only to detail only, so that fine structure that depth smooths out can be added. The brightness part SHALL add relief around the depth form and SHALL NOT raise the whole image by its mean brightness. + +#### Scenario: Depth only +- **WHEN** the mix is at depth only +- **THEN** the relief follows the depth alone + +#### Scenario: Detail added +- **WHEN** the mix is raised +- **THEN** local structure such as an eye outline or whisker lines appears on top of the depth form + +#### Scenario: Mean brightness +- **WHEN** a photo is uniformly brighter or darker +- **THEN** the relief form does not rise or sink as a whole because of it + +### Requirement: Detail boost +The brightness source and the mix SHALL offer a detail boost that evens out the lighting (local contrast), so that a flat-lit photo can use the available height range. Boost at zero SHALL leave the brightness source unchanged. + +#### Scenario: Flat photo +- **WHEN** a photo has a narrow tonal range inside the subject and the boost is raised +- **THEN** the relief shows more local structure + +#### Scenario: Off +- **WHEN** the boost is zero +- **THEN** the Brightness source gives the same relief as before this feature + +### Requirement: Relief depth presets +The app SHALL offer presets for the height range of a photo relief (Subtle, Portrait, Deep) that set the range and rim roll-off. Each value SHALL stay editable and the panel SHALL then show Custom. + +#### Scenario: Preset +- **WHEN** the user chooses Portrait +- **THEN** the range and rim values are set to the preset's values + +#### Scenario: Edited +- **WHEN** the user edits the range after choosing a preset +- **THEN** the preset indicator shows Custom and the value is kept + +### Requirement: Persistence of the depth result +The depth SHALL be stored with the image in the browser's storage and restored after a reload with the source and controls, without running the model again. Deleting the model from the model manager SHALL NOT remove an existing result; replacing or removing the image SHALL. If the stored result is missing, the app SHALL say so, offer to run again and use the Distance source meanwhile, never an empty relief. If storage is unavailable the result SHALL work for the session and the user SHALL be warned that it will not persist. + +#### Scenario: Reload +- **WHEN** the user computes the depth, sets the mix and reloads +- **THEN** the same relief is shown without running the model + +#### Scenario: Result lost +- **WHEN** the stored depth is gone but Photo depth is selected +- **THEN** a message offers to run again and the relief follows Distance until then + +### Requirement: Availability and hints +Photo depth SHALL be offered only in Image mode and SHALL be unavailable for SVG images and when the image has no silhouette mask to limit it. When the grid is too coarse for facial detail, the app SHALL say that a finer quality setting is needed. Hints SHALL never block an action. + +#### Scenario: Shape mode +- **WHEN** the mode is Shape +- **THEN** Photo depth is not offered + +#### Scenario: Coarse grid +- **WHEN** the quality setting is too coarse for the detail of the depth relief +- **THEN** a hint suggests a finer quality + +### Requirement: Compatibility +Keychains saved before this feature SHALL load unchanged. With the Distance or Brightness source and the boost at zero, the relief SHALL be identical to what it was before this feature. + +#### Scenario: Old record +- **WHEN** a record without the new fields is loaded +- **THEN** it opens with its former source and produces the same relief as before + +### Requirement: Language +All text of the photo relief, its controls, presets and hints SHALL be available in every language of the app. + +#### Scenario: Russian +- **WHEN** the interface language is Russian +- **THEN** the source choice, button, mix, boost, presets, hints and warnings are in Russian diff --git a/openspec/changes/34-keychain-photo-relief/tasks.md b/openspec/changes/34-keychain-photo-relief/tasks.md new file mode 100644 index 0000000..4a720ab --- /dev/null +++ b/openspec/changes/34-keychain-photo-relief/tasks.md @@ -0,0 +1,29 @@ +# Tasks + +## 1. Check on the owner's photos (spike) + +- [ ] 1.1 With `poc/ai/` run the depth models (Depth Anything V2 Small q4f16 and fp16, and V3 Small if an ONNX exists) on the cat photo and 3–5 other photos (a portrait, a plush toy, an object on a table): record whether the face forms read in the depth map, edge errors, time per backend. Then build a quick relief from the depth inside the AI matte (a throwaway script or the existing `buildGrid` with a test plane) and look at the 3D result; record the defaults for mix, boost and range in `design.md` (Open Questions) and `poc/ai/FINDINGS.md`. If a face is not recognisable in the relief with any setting, stop and update the artifacts with `/opsx:update`. + +## 2. Geometry (React-free) + +- [ ] 2.1 `geometry/raster/depth.ts`: `normaliseDepth(plane, mask?)` (2nd–98th percentile, over the mask when given, clamped 0…1) and `localContrast(gray, w, h, radiusPx, amount)`. Tests: monotone mapping, a constant plane gives a flat result without NaN, percentile clipping ignores outliers, mask limits the range, `amount` 0 returns the input, a gradient plus a small bump keeps the bump after boost. +- [ ] 2.2 `SourceParams.kind: 'depth'`, `depthId`, `depthMix`, `boost`; `registerDepth` / `depthRegistry`; `buildGrid` takes the depth plane through the brightness path (contrast, gamma, invert, smoothing, rim ceiling, clamp to silhouette), mixes the zero-mean local-contrast brightness, falls back to Distance when the plane is missing and reports it. Tests: with `distance` or `brightness` and `boost: 0` the existing regression volumes and bounding boxes are unchanged; a synthetic dome depth gives a dome relief inside the mask with the rim rolling off; the depth range is taken inside the mask (a far background does not flatten the subject); the mix adds structure without raising the mean; missing plane reports the fallback. +- [ ] 2.3 `migrateKeychain` fills the new source fields for old records. Tests: round trip, partial and corrupt values fall back to defaults. + +## 3. ML and image side + +- [ ] 3.1 `src/ml/` registry gets `kind: 'depth'` and external-data support (the data file's path, bytes and SHA-256, passed to the runtime as external data); the manager shows the kind and the picker filters by it. Tests: registry validation of the data file, download of both files with progress and cancel, a missing or mismatched data file fails the model, the manager lists depth models separately. +- [ ] 3.2 `features/keychain/depth.ts` and `useDepth`: whole-image run through the client (aspect kept, multiple of 14, inference cap 2048, resample back, normalise), store in `imageStore` as a `depth` record keyed by image, model and revision, restore on load, registration in the worker. Tests with a mocked client: input geometry for wide and tall images, output mapped back, cancel and latest-wins, restore does not call the client, replacing the image drops the record, deleting the model keeps it. + +## 4. UI + +- [ ] 4.1 Relief source choice gets Photo depth (Image mode only, hidden for SVG and without a mask): `PhotoReliefBlock` with the depth model picker, Compute depth with progress and cancel, mix slider (Depth ↔ Detail), Detail boost, the existing contrast, gamma, invert, smoothing, the Relief depth presets (Subtle, Portrait, Deep; Custom when edited), the depth greyscale view and the coarse-grid hint; Detail boost also under Brightness. Strings in `en.json` and `ru.json`. No artboard exists: built from `ui/` components and `theme.css` tokens, said in the commit; compare in en and ru and at 390 px. Component tests: option availability per mode, no model opens the manager, sliders update the relief without a client call, presets and Custom, lost result falls back with a message, hints, key parity, steppers on every numeric input. + +## 5. Version + +- [ ] 5.1 Bump to 1.5.0: `package.json` and a `CHANGELOG.md` entry `1.5.0 — 34-keychain-photo-relief`. Test: the existing version test passes with the new number. + +## 6. Verification + +- [ ] 6.1 `npm test` and `npm run build` are green, the dev server starts, no console errors; with the Distance and Brightness sources the existing keychain tests and volumes are unchanged. +- [ ] 6.2 In the browser, en and ru and at 390 px, with the cat photo and a portrait: AI silhouette, then Compute depth with progress, the face forms (nose, eyes, ears) are visible in the 3D relief, sliders update without a model run (no model activity in the network panel), reload restores the relief without a run, deleting the model keeps the relief, the STL is one valid piece that prints face up, no request carries image data. diff --git a/poc/ai/index.html b/poc/ai/index.html index 1442506..44e4904 100644 --- a/poc/ai/index.html +++ b/poc/ai/index.html @@ -10,7 +10,7 @@

PoC: нейросетевые силуэты на выпрямленном листе

Снимок - +
Модель
diff --git a/poc/ai/main.ts b/poc/ai/main.ts index 3715cfd..b26a3ef 100644 --- a/poc/ai/main.ts +++ b/poc/ai/main.ts @@ -20,21 +20,22 @@ const sessions = new Map() ort.env.wasm.proxy = true // вывод в воркере ORT: страница не замирает на время инференса ort.env.wasm.wasmPaths = 'https://cdn.jsdelivr.net/npm/onnxruntime-web@' + (ort.env.versions?.web ?? 'latest') + '/dist/' -async function fetchModel(c: Candidate): Promise { +async function fetchUrl(url: string, bytes: number): Promise { const cache = await caches.open('poc-ai-models') - let r = await cache.match(c.url) + let r = await cache.match(url) const t0 = performance.now() if (!r) { - const net = await fetch(c.url) - if (!net.ok) throw new Error(`HTTP ${net.status} for ${c.url}`) - await cache.put(c.url, net.clone()) + const net = await fetch(url) + if (!net.ok) throw new Error(`HTTP ${net.status} for ${url}`) + await cache.put(url, net.clone()) r = net - log(`скачано ${(c.bytes / 1e6).toFixed(0)} МБ за ${((performance.now() - t0) / 1000).toFixed(1)} с`) + log(`скачано ${(bytes / 1e6).toFixed(0)} МБ за ${((performance.now() - t0) / 1000).toFixed(1)} с`) } else log('модель из Cache API') const buf = await r.arrayBuffer() - if (buf.byteLength !== c.bytes) log(`ВНИМАНИЕ: размер ${buf.byteLength} ≠ ${c.bytes}`) + if (buf.byteLength !== bytes) log(`ВНИМАНИЕ: размер ${buf.byteLength} ≠ ${bytes}`) return buf } +const fetchModel = (c: Candidate) => fetchUrl(c.url, c.bytes) async function loadImage(maxSide = 0): Promise { const f = $('file').files?.[0] @@ -150,7 +151,8 @@ async function run() { const buf = await fetchModel(c) ort.env.wasm.numThreads = threads t = performance.now() - session = await ort.InferenceSession.create(buf, { executionProviders: [ep], graphOptimizationLevel: 'all' }) + const externalData = c.data ? [{ path: c.data.path, data: new Uint8Array(await fetchUrl(c.data.url, c.data.bytes)) }] : undefined + session = await ort.InferenceSession.create(buf, { executionProviders: [ep], graphOptimizationLevel: 'all', ...(externalData ? { externalData } : {}) }) log(`сессия создана за ${(performance.now() - t).toFixed(0)} мс; входы ${session.inputNames}, выходы ${session.outputNames}`) sessions.set(key, session) } @@ -160,14 +162,19 @@ async function run() { for (let i = 0; i < 2; i++) { t = performance.now(); out = await session.run({ [inName]: new ort.Tensor('float32', data.slice(), [1, 3, s, s]) }); runs.push(performance.now() - t) } log(`инференс: 1-й ${runs[0].toFixed(0)} мс (прогрев), 2-й ${runs[1].toFixed(0)} мс`) const o = out[session.outputNames[0]] - const alpha = toAlpha(o.data as Float32Array, s, W, H, c.sigmoid, whole) + let raw = o.data as Float32Array + if (c.kind === 'depth') { // относительная глубина → 0…1 по 2–98 перцентилям (дальше — ближе = выше, как в рельефе) + const sorted = Float32Array.from(raw).sort(); const lo = sorted[Math.floor(sorted.length * 0.02)], hi = sorted[Math.floor(sorted.length * 0.98)] + raw = raw.map((x) => Math.min(1, Math.max(0, (x - lo) / (hi - lo || 1)))) + } + const alpha = toAlpha(raw, s, W, H, c.sigmoid, whole) - state = { rect, seg, alpha, runs, c, ep, threads, s, whole } + state = { rect, seg, alpha, runs, c, ep, threads, s, whole, depth: c.kind === 'depth' } evaluate() $('status').textContent = 'готово' } -interface State { rect: Rectified; seg: ReturnType; alpha: Float32Array; runs: number[]; c: Candidate; ep: string; threads: number; s: number; whole: boolean } +interface State { rect: Rectified; seg: ReturnType; alpha: Float32Array; runs: number[]; c: Candidate; ep: string; threads: number; s: number; whole: boolean; depth: boolean } let state: State | null = null /** Всё после инференса: сглаживание альфы, порог, компоненты, сравнение. Модель не запускается, поэтому ползунки работают мгновенно. */ @@ -179,6 +186,7 @@ function evaluate() { let alpha = state.alpha if (smoothMm > 0) alpha = gaussianBlur(alpha, W, H, Math.max(MIN_SIGMA, smoothMm * rect.scale), new Uint8Array(W * H).fill(1)) drawAlpha(alpha, W, H) + if (state.depth) { $('edge').textContent = 'карта глубины (светлее = ближе): справа — глубина, силуэт не считается'; $('tbl').innerHTML = ''; $('status').textContent = 'готово'; return } const mask = new Uint8Array(W * H); for (let i = 0; i < mask.length; i++) mask[i] = alpha[i] >= thr ? 1 : 0 const ai = components(mask, W, H, rect.scale, margin) draw(rect, seg.labels, ai.labels) diff --git a/poc/ai/models.ts b/poc/ai/models.ts index 66a3726..2931456 100644 --- a/poc/ai/models.ts +++ b/poc/ai/models.ts @@ -14,6 +14,10 @@ export interface Candidate { input?: string /** Только для сравнения качества: лицензия не позволяет включить в проект. */ referenceOnly?: boolean + /** matte — маска объекта (по умолчанию), depth — относительная глубина (рельеф лица, а не силуэт). */ + kind?: 'matte' | 'depth' + /** Внешние веса (.onnx_data) рядом с моделью. */ + data?: { path: string; url: string; bytes: number } } const hf = (repo: string, rev: string, file: string) => `https://huggingface.co/${repo}/resolve/${rev}/${file}` const IMNET = { mean: [0.485, 0.456, 0.406] as [number, number, number], std: [0.229, 0.224, 0.225] as [number, number, number] } @@ -36,4 +40,7 @@ export const CANDIDATES: Candidate[] = [ { id: 'isnet-bw-fp32', label: 'IS-Net BritishWerewolf fp32 (Apache-2.0, 179 МБ)', url: hf('BritishWerewolf/IS-Net', '9783722d9f964c0286a411e7e8e6fede947d5a53', 'onnx/model.onnx'), bytes: 178648008, license: 'Apache-2.0', size: 1024, mean: [0.485, 0.456, 0.406], std: [1, 1, 1], sigmoid: false }, { id: 'u2net-bw-fp32', label: 'U²-Net полная (Apache-2.0, 176 МБ)', url: hf('BritishWerewolf/U-2-Net', 'c08042b462485d8597c89d3ce831d00be8582c5c', 'onnx/model.onnx'), bytes: 175997641, license: 'Apache-2.0', size: 320, ...IMNET, sigmoid: false }, { id: 'inspyrenet-fp16', label: 'InSPyReNet SwinB Plus Ultra fp16 (MIT, 199 МБ)', url: hf('OS-Software/InSPyReNet-SwinB-Plus-Ultra-ONNX', 'fe628d2d1465f775c707368a80248a2d5b5ad71f', 'onnx/model_fp16.onnx'), bytes: 199133027, license: 'MIT', size: 1024, ...IMNET, sigmoid: false }, + // карта глубины для рельефа (change 34): выход — относительная глубина, а не маска + { id: 'depth-v2-small-q4f16', kind: 'depth', label: 'Depth Anything V2 Small q4f16 (Apache-2.0, 20 МБ) — глубина', url: hf('onnx-community/depth-anything-v2-small-ONNX', 'c3b67641fd837b2368757101311e5d21e511441e', 'onnx/model_q4f16.onnx'), bytes: 212397, data: { path: 'model_q4f16.onnx_data', url: hf('onnx-community/depth-anything-v2-small-ONNX', 'c3b67641fd837b2368757101311e5d21e511441e', 'onnx/model_q4f16.onnx_data'), bytes: 20200448 }, license: 'Apache-2.0', size: 518, ...IMNET, sigmoid: false }, + { id: 'depth-v2-small-fp16', kind: 'depth', label: 'Depth Anything V2 Small fp16 (Apache-2.0, 50 МБ) — глубина', url: hf('onnx-community/depth-anything-v2-small-ONNX', 'c3b67641fd837b2368757101311e5d21e511441e', 'onnx/model_fp16.onnx'), bytes: 180471, data: { path: 'model_fp16.onnx_data', url: hf('onnx-community/depth-anything-v2-small-ONNX', 'c3b67641fd837b2368757101311e5d21e511441e', 'onnx/model_fp16.onnx_data'), bytes: 50392064 }, license: 'Apache-2.0', size: 518, ...IMNET, sigmoid: false }, ] diff --git a/poc/ai/samples/cat.jpeg b/poc/ai/samples/cat.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..da385ff78994793eefa0c26ce442aaff0211562f GIT binary patch literal 243033 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≤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�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 B)d&Cp literal 0 HcmV?d00001 From 3f98f4882c18fd3ff3836e11a4c03ec8959dc4ab Mon Sep 17 00:00:00 2001 From: skarakovski Date: Wed, 21 Oct 2026 21:00:09 +0700 Subject: [PATCH 15/38] plan: 34-keychain-photo-relief as a mix of contour, depth and detail with crop and whole-frame plaque; poc: shape combiner, experiment images Co-Authored-By: Claude Sonnet 5.5 --- openspec/ROADMAP.md | 2 +- .../34-keychain-photo-relief/design.md | 59 ++++--- .../34-keychain-photo-relief/proposal.md | 32 ++-- .../specs/keychain-photo-relief/spec.md | 157 ++++++++++++------ .../changes/34-keychain-photo-relief/tasks.md | 20 ++- poc/ai/FINDINGS.md | 11 ++ poc/ai/findings/cat_face_4_levels.jpg | Bin 0 -> 75334 bytes poc/ai/findings/cat_face_local_contrast.jpg | Bin 0 -> 61459 bytes poc/ai/findings/cat_whole_local_contrast.jpg | Bin 0 -> 439063 bytes poc/ai/index.html | 12 +- poc/ai/main.ts | 65 +++++++- poc/ai/samples/cat_face.jpeg | Bin 0 -> 43421 bytes 12 files changed, 252 insertions(+), 106 deletions(-) create mode 100644 poc/ai/findings/cat_face_4_levels.jpg create mode 100644 poc/ai/findings/cat_face_local_contrast.jpg create mode 100644 poc/ai/findings/cat_whole_local_contrast.jpg create mode 100644 poc/ai/samples/cat_face.jpeg diff --git a/openspec/ROADMAP.md b/openspec/ROADMAP.md index 721810f..c403203 100644 --- a/openspec/ROADMAP.md +++ b/openspec/ROADMAP.md @@ -43,7 +43,7 @@ | 31 | `31-rack-shelf` | 26, 29 | v1.2.0. Полка: передняя панель с ушами лицом на столе, пол с вентиляцией (щели, можно глухой), боковые стенки с прямым скосом, вентиляция передней панели опционально | | 32 | `32-photo-trace-ai` | 25a, 29 | v1.3.0. Фото-вырезы: альтернативный нейросетевой движок силуэтов (в браузере, веса по согласию, гибридный точный край), классический остаётся по умолчанию как baseline; сравнение движков | | 33 | `33-keychain-ai-silhouette` | 32, 11, 29 | v1.4.0. Keychain Image: силуэт по ИИ (удаление фона на браузерной модели), те же ручки порога и сглаживания, один главный объект, результат хранится вместе с картинкой; яркость остаётся по умолчанию | -| 34 | `34-keychain-photo-relief` | 33, 32, 12, 17, 29 | v1.5.0. Keychain Image: рельеф лица/объекта из фото через карту глубины (Depth Anything V2 Small), смешивание с яркостью, усиление локального контраста, пресеты глубины; результат хранится с картинкой | +| 34 | `34-keychain-photo-relief` | 33, 32, 12, 17, 29 | v1.5.0. Keychain Image: рельеф из фото как смесь трёх источников формы (контур, глубина Depth Anything, детали — локальный контраст), кроп области (лицо), силуэт «весь кадр» (плашка), пресеты; результат хранится с картинкой | ```mermaid gantt diff --git a/openspec/changes/34-keychain-photo-relief/design.md b/openspec/changes/34-keychain-photo-relief/design.md index a8a90b5..539ebc2 100644 --- a/openspec/changes/34-keychain-photo-relief/design.md +++ b/openspec/changes/34-keychain-photo-relief/design.md @@ -2,45 +2,52 @@ ## Context -`SourceParams = {kind: 'distance' | 'brightness', contrast, gamma, invert, smooth, despeckle}`. In `buildGrid` the brightness source samples the image's gray plane at the grid nodes inside the silhouette (`gray`), maps it with `brightnessValue` and turns it into heights (`floor + value × (hmax − floor)`), with the rim ceiling, smoothing (masked Gaussian), profile, detail lines and patterns after it. `33` adds the AI silhouette and the matte registry and uses `src/ml/` of `32`. +`SourceParams = {kind: 'distance' | 'brightness', contrast, gamma, invert, smooth, despeckle}`. In `buildGrid` the brightness source samples the gray plane at the grid nodes inside the silhouette and maps it with `brightnessValue` to heights `floor + value × (hmax − floor)`; then the rim ceiling, smoothing (masked Gaussian), profile (bevel, dome, terrace, flat, engraved), detail lines and patterns. `buildSilhouette` makes the mask from brightness or alpha; `33` adds the AI matte and its registry; `32` the ML runtime and model manager. -The photo-relief problem is not the silhouette but the interior. A depth map answers "what is closer to the camera", which for a face is the form. It is relative and smooth: it will not carry whiskers or fur texture, and it has soft errors at the subject's edge, which the matte mask covers. +## What the PoC showed (cat photo) + +Depth V2 Small gives a smooth depth map (the owner found it good as a form); on its own it does not carry the facial features. Local contrast of the brightness on a face crop (400 × 300 px of a 960 × 1280 photo) makes the eyes, nose, whiskers, stripes and ear clear, and 4 tone levels keep the face recognisable; on the whole frame the face is too small (`poc/ai/findings/`). The contour bevel is the existing form. The three sources therefore play different roles, and the PoC combiner is the tool to pick the mix. ## Goals / Non-Goals **Goals:** -- A recognisable bas-relief of a face or object from one photo, with controls the user can understand. -- No change for existing keychains; no data leaves the browser. +- A recognisable portrait relief from a photo by mixing contour, depth and detail, with controls the user understands; nothing changes for existing keychains. +- Everything stays in the browser. **Non-Goals:** -- Metric depth or 3D reconstruction, sculpting, face-specific models, multi-colour. +- Face detection, sculpting, line extraction, colour. ## Decisions -- **Depth plane like the text mask and the matte.** The main thread runs the depth model through the `32` client and gets a `Float32` plane normalised to 0…1 (2nd–98th percentile, farther = 0, nearer = 1) at the image's resolution; it is registered once in the worker (`registerDepth`, `depthRegistry`) and params carry `source.depthId`. `buildGrid` samples it where it samples brightness. Sliders (contrast, gamma, invert, smoothing, mix) then only rebuild in the worker. Alternative: bake the depth into the image's gray plane. Rejected: loses the original brightness needed for the mix and for switching sources. -- **Input and aspect.** The whole image, long side capped at 2048, padded or resized to the model's multiple of 14 (518 for Depth Anything V2 Small) keeping the aspect ratio; output resampled bilinearly back to the image size, as for the matte in `33`. -- **Model.** Depth Anything V2 Small ONNX (`onnx-community/depth-anything-v2-small-ONNX`, Apache-2.0): q4f16 20 MB, fp16 50 MB, weights in a separate `.onnx_data` file, so the ML runtime needs external-data support (path and bytes of the data file in the registry entry, SHA-256 for both files). The PoC runs it; Base and Large are non-commercial and not offered; Depth Anything 3 Small (Apache-2.0, newer) is a candidate to compare. The registry gets `kind: 'depth'` next to `matte`; the manager shows the kind and filters the picker by it. -- **Relief from depth.** Inside the AI silhouette the height is `floor + value × (hmax − floor)` with `value` = the mix of depth and local-contrast brightness. Because depth inside the silhouette may be nearly flat across the body, the depth is renormalised over the silhouette pixels only (robust min/max inside the mask), otherwise the background range would waste the height budget. The silhouette edge still goes through the bevel ceiling of the existing relief (the rim rolls off), so the edge of the cat does not stand as a cliff. -- **Mix with brightness.** `depthMix` (0–1, default 0.25 brightness): `value = (1 − m)·depth + m·brightnessDetail`, where `brightnessDetail` is the local-contrast brightness (below) re-centred to zero mean so it adds relief around the depth form instead of lifting the whole image. It brings fine structure (eye outline, whiskers, fur streaks) that depth smooths out. -- **Local contrast ("Detail boost").** `localContrast(gray, w, h, radiusPx, amount)`: subtract a large-radius Gaussian (≈ 1/12 of the shorter side), divide by a local standard deviation, soft-clip, `amount` 0 = off. Available for Brightness and Depth sources; default 0 for Brightness (unchanged output), 0.5 under Depth. It is what lets a flat-lit photo use the whole height range. -- **Range presets.** `Relief depth` presets Subtle / Portrait / Deep set `hmax − floor` and the profile bevel; they are data like the profile presets and show Custom when edited. Facial relief reads best with a modest range (about 1.5–3 mm over the plate) and a steep rim; the presets start there. -- **Persistence.** Like the matte of `33`: a `depth` record in `imageStore` keyed by `imageId` + model + revision, restored on load, kept when the model is deleted, dropped with the image; memory fallback with the `persistent` warning. If it is missing under `kind: 'depth'` the relief falls back to Distance with a "run again" message, never to an empty relief. -- **Not for shapes.** Shape mode has no image; the Photo-depth option appears only in Image mode, and only when the silhouette source is AI or the image otherwise has a usable mask (a depth relief outside a good mask is meaningless). -- **UI.** `ProfileSection`/Relief source: Distance | Brightness | Photo depth. Under Photo depth: model picker (depth models), Compute depth button with progress, the mix slider (Depth ↔ Detail), Detail boost, the existing contrast, gamma, invert and smoothing, the Relief depth presets, and a depth greyscale view (also in the enlarged mask dialog). Under Brightness: Detail boost appears. -- **Resolution warnings.** The existing `smoothResolution` and pattern resolution checks apply; a depth relief on a coarse grid gets a hint that a finer quality setting is needed for facial detail. -- **Performance.** Depth Anything V2 Small q4f16 is small; the PoC measures time and the backend it works on (WebGPU/WASM) per device. The run is cancellable and latest-wins like the matte. +- **One mix, three shares.** In `buildGrid`, with `source.kind = 'photo'`, the height value at a node inside the silhouette is + `value = (wC·contour + wD·depth + wT·detail) / (wC + wD + wT)`, all three terms in 0…1, then the existing contrast, gamma, invert and smoothing, then `floor + value × (hmax − floor)`, then the rim ceiling, profile and patterns as for any source. `contour` is the existing bevel value (`reliefValue` of the distance to the edge, so wC alone equals the Distance source), `depth` the depth plane normalised inside the silhouette, `detail` the boosted brightness. A share is stored in 0…100 and normalised by the sum, so the user sets proportions and the relief always uses the full height range; sum zero falls back to Contour. Shares of unavailable sources (no depth computed yet) count as zero and the panel says so. +- **Why normalised shares and not an additive detail.** Three sliders that always add up to the whole are easy to read ("40 % depth, 30 % detail") and cannot overflow the range. Detail is zero-centred (mean 0.5), so raising its share changes structure, not the mean height. The bevel ceiling keeps the edge rolling off, so a high detail share never produces a cliff at the edge. +- **Depth plane like the matte of `33`.** The main thread runs the depth model through the `32` client and gets a plane at the image's resolution, normalised 0…1 (2nd–98th percentile; farther 0, nearer 1), registered once in the worker (`registerDepth`, `depthRegistry`); params carry `source.depthId`. In `buildGrid` it is sampled like brightness and renormalised over the silhouette pixels (robust min/max inside the mask) so a far background does not waste the range. Sliders only rebuild in the worker. +- **Depth model and input.** Depth Anything V2 Small ONNX (`onnx-community/depth-anything-v2-small-ONNX`, Apache-2.0): q4f16 20 MB, fp16 50 MB, weights in a separate `.onnx_data` file, so the runtime needs external-data support (path, bytes and SHA-256 of both files in the registry entry). Registry kind `depth` next to `matte`; the manager shows the kind and the picker filters by it. Input: the working image (the crop when set, long side capped at 2048), resized to a multiple of 14 with the aspect kept (518 for this model), output resampled bilinearly back. Depth Anything 3 Small (Apache-2.0, newer) is compared in the spike; Base and Large are non-commercial and not offered. +- **Depth and the crop.** Depth computed on the crop has more pixels on the face. The depth record stores the crop it was computed for; when the crop changes the depth is kept and resampled (stale but usable) and marked "computed for another crop, recompute for better detail"; the user decides to re-run (a few seconds). Contour and Detail follow the crop live. +- **Detail boost.** `localContrast(gray, w, h, radiusPx, amount)`: subtract a Gaussian-blurred copy, divide by the local standard deviation (blurred squared deviation with a floor against noise), `0.5 + 0.5·tanh(amount·v)`. The radius is stored in **mm of the part** (`boostRadius`, default ≈ 1.4 mm, found with the PoC at about 14 px on a 400 px face crop printed 40 mm wide) and converted to pixels from the crop and the keychain size, so the same value looks the same at any resolution. Amount 0 = off (detail is then a flat 0.5, the same as no detail). +- **Tone levels.** `levels` 0 = continuous (default), 2–8 = quantise the detail value, with `levelSoftness` (0 hard steps, 1 smooth ramps) so the steps are printable slopes; only the detail term is quantised, so contour and depth stay smooth. The Terraced profile still works on the final height. +- **Crop.** `crop = {x, y, w, h}` fractions of the image (default full). `buildSilhouette` and `buildGrid` read the planes (gray, alpha, matte, depth) through a cropped view made once per change in the worker; size, text and mount fractions stay relative to the silhouette box. `CropEditor` over the image: drag, corner and edge handles, aspect lock (free, 1:1, 4:3, 3:4), fit to subject from the AI mask box, reset; minimum 5 % of a side; live update of the preview; also in the enlarged mask dialog. Changing the crop never re-runs the matte model. +- **Whole frame silhouette.** `silhouette.source: 'frame'`: every pixel of the crop is material; threshold, close, areas and specks are hidden. With the Base plate (rounded rectangle, circle) this is a portrait plaque; the relief edge still rolls off through the bevel ceiling and rim. +- **Presets.** Subtle / Portrait / Deep set the three shares, `hmax − floor`, boost radius and amount and levels (Portrait: about 30 / 40 / 30, 2 mm range, radius 1.4 mm, 4 soft levels; to be confirmed with the spike) and show Custom when edited; data next to the profile presets. +- **Heights and printing.** A height field grows from a flat back (existing design), prints face up without supports. Detail finer than the grid cell is lost: `quality` and the resolution warnings apply, with a hint when the cell is larger than about a third of the boost radius. +- **Persistence and migration.** Depth planes are stored in `imageStore` as `depth` records keyed by image, model, revision and crop, restored on load, kept when the model is deleted, dropped with the image, memory fallback with the `persistent` warning. `crop`, mix, boost and levels are keychain params (`migrateKeychain` fills defaults: full crop, `kind` unchanged, boost 0, levels 0). If the stored depth is missing the Depth share counts as zero with a "run again" message, never an empty relief. +- **Availability.** The Photo source appears only in Image mode, not for SVG images, and Depth only when a depth model exists; Contour and Detail work without any model. +- **UI.** Relief source: Distance | Brightness | Photo. Under Photo: three share sliders with the mix as a stacked bar, the model picker and Compute depth (progress, cancel, "for another crop" note), Detail boost (amount, radius as `LengthField`), Tone levels (count, softness), presets, the existing contrast, gamma, invert and smoothing, and a height greyscale view next to the mask preview. `CropEditor` in the Image section; the Silhouette source gains "Whole frame". Strings in en and ru. +- **Performance.** The depth run is a few seconds and cancellable (latest wins); everything else is arithmetic in the worker. ## Risks / Trade-offs -- [Depth of a cat's fur and face is soft or wrong at edges] → the matte mask clips the edge, the rim roll-off hides the cliff, the mix adds detail; the PoC judges quality on the owner's cat photo before building. -- [Relative depth varies between photos] → normalised inside the silhouette; the user adjusts contrast, gamma, invert and range. -- [Fine detail is lost to the grid] → the quality setting and the existing resolution warning; a keychain-sized part cannot carry hair detail anyway, the portrait relief is about shape. -- [A second model (~20–50 MB) on top of the matte model] → shared manager and consent of `32`; both small. -- [External-data ONNX files need runtime support] → covered in the registry and tested in `32`'s runtime tasks (this change adds the case). -- [Printing a deep relief needs supports on steep faces] → the relief grows from a flat back (existing design), so it prints face up without supports; steep overhangs do not exist in a heightfield. +- [Depth is soft or wrong at fur and at the subject's edge] → the mask clips the edge, the bevel ceiling hides the cliff, Detail adds the fine structure; the shares let the user lean on whichever source works for the photo. +- [Local contrast amplifies noise and JPEG artefacts] → floor in the divisor, despeckle and smoothing, the radius control. +- [A face is too small in the part to read] → the crop, the hint and the quality warning; a keychain-sized part cannot carry hair, the portrait is about eyes, nose and stripes. +- [A second model on top of the matte model] → shared manager and consent of `32`; both small, results are stored. +- [External-data ONNX needs runtime support] → registry and runtime tasks of this change (tested), PoC already loads it. +- [Quantised levels make jagged edges] → softness and the existing smoothing; hard steps are a choice. +- [Three shares are many controls] → presets first; the shares collapse into one stacked bar; a single "Photo" source keeps the panel short. ## Open Questions -- Whether depth alone is already good for a face or the brightness mix is essential: the PoC on the cat photo decides the default mix and boost. -- Depth Anything V2 Small versus V3 Small (Apache-2.0, 2026): compare quality and size in the PoC. -- Whether a face crop option (relief only inside a user-chosen window, with the rest flat) is wanted; left out for now. +- Defaults for the shares, radius, amount, levels and range (the spike picks them with the PoC combiner and real keychains). +- Whether depth on the face crop is clearly better than on the whole frame (`cat_face` sample in the PoC); decides if the crop-linked recompute prompt is shown by default. +- Whether to add a face detector for an automatic crop later. diff --git a/openspec/changes/34-keychain-photo-relief/proposal.md b/openspec/changes/34-keychain-photo-relief/proposal.md index 42e06cf..bd63ee4 100644 --- a/openspec/changes/34-keychain-photo-relief/proposal.md +++ b/openspec/changes/34-keychain-photo-relief/proposal.md @@ -2,30 +2,36 @@ ## Why -With `33-keychain-ai-silhouette` a photo gives a clean outline, but inside the outline the keychain is still a plain bevelled plate. A portrait of a cat (or a person, or a pet's face) is only recognisable by what is inside the outline: the eyes, the nose, the ears, the fur pattern. The owner tried exactly this: the outline of the cat is good, but the face cannot be seen on the keychain, so there is little point in working from photos. +With `33-keychain-ai-silhouette` a photo gives a clean outline, but inside the outline the keychain is still a plain bevelled plate. A portrait of a cat (or a person, or a pet's face) is only recognisable by what is inside the outline: the eyes, the nose, the ears, the fur pattern. The owner tried this on a cat photo: the outline is good, the face cannot be seen on the keychain. -The relief already has a "brightness" height source, but it does not solve this: the brightness of a photo is lighting, not shape (a dark stripe on fur becomes a groove, a bright cheek a bump, a shadow a hole), and a flat-lit photo has too little tonal range inside the subject. What a face needs is shape: the nose proud of the cheeks, the eyes sunk, the ears leaning away. A monocular depth network gives a relative depth map from a single photo and runs in the browser; Depth Anything V2 Small (Apache-2.0) is 20–50 MB. The research and application notes (`tmp/ai/BROWSER_AI_APPLICATION.md`, idea A3) planned it for this purpose. Together with the soft matte of `33` this gives a bas-relief portrait. +No single source of height does it. On the owner's cat photo (`poc/ai/FINDINGS.md`, images in `poc/ai/findings/`): + +- **Contour** (the existing bevel from the silhouette edge) gives the body of the keychain and a clean edge, but is the same for every photo. +- **Depth** (Depth Anything V2 Small) gives the overall form: the head raised over the body, the muzzle over the cheeks. It is smooth and soft on fine features. +- **Detail** (local contrast of the brightness, on a crop of the face) gives exactly the features depth smooths out: the eyes, the nose, the whiskers, the forehead stripes, the ear; on the whole frame the face is too small for it, on a face crop it reads well and four tone levels still keep the face recognisable. + +The three complement each other, so the relief is built as a **mix of the three**, with a share for each that the user controls, plus a crop so that the face has enough pixels. ## What Changes -- **Depth as a height source.** In Image mode the Relief source gets a third option, "Photo depth" next to Distance and Brightness. The depth map is computed once by a model from the model manager of `32`, stored with the image, and used inside the AI silhouette as the height field, with the existing contrast, gamma, invert and smoothing controls. -- **Mix with brightness.** A slider mixes depth with local-contrast brightness (0 % = depth only, 100 % = brightness only), because depth gives the form and brightness the fine detail (whiskers, fur, eye outline). The default favours depth. -- **Local contrast.** The brightness source gets an optional "Detail boost" (local contrast normalisation) so that a flat-lit photo reaches the tonal range the height needs; also useful for the plain Brightness source. -- **Range and base.** The height range of the photo relief, its floor and the rim stay the existing relief controls; a "Relief depth" preset (Subtle, Portrait, Deep) sets sensible range values. Printing notes: very fine detail below the grid cell is limited by the quality setting, as the existing resolution warning says. -- **Persistence.** The depth plane is stored with the image like the matte of `33`, keyed by image, model and revision, so a reload restores the relief without running the network. -- **PoC.** `poc/ai/` can run the depth model (kind `depth`, with external weights) on the owner's cat photo and shows the depth map next to the image, so the quality is judged before the work starts. +- **Form mix.** The Relief source gets "Photo" next to Distance and Brightness. Under it three shares (Contour, Depth, Detail, in percent, normalised) are mixed into one height field; presets Subtle, Portrait and Deep set the shares, the height range and the detail settings, and edit to Custom. A share at zero switches that source off, and Contour alone is today's Distance source. +- **Depth.** Computed once by a model of kind `depth` from the model manager of `32` (Depth Anything V2 Small, Apache-2.0, 20–50 MB), stored with the image, normalised inside the silhouette so the background does not use up the height range. +- **Detail.** "Detail boost" (local contrast, radius in mm of the part so it means the same at any resolution) and optional "Tone levels" (2–8, soft or hard steps). Detail is zero-centred, so it adds structure around the form and does not lift the part by the photo's mean brightness. +- **Crop.** A non-destructive crop rectangle over the image (aspect lock, "fit to subject" from the AI mask box, reset). Everything after it works on the crop; the AI matte is cropped without re-running the network. Depth can be recomputed for the crop (more pixels on the face); a depth computed for another crop stays usable and is marked as such. +- **Whole frame silhouette.** A third silhouette source, "Whole frame", makes the crop a plaque (shape from the Base section) for a face portrait; the AI matte of `33` stays for cut-out heads and whole animals. +- **PoC.** `poc/ai/` has a "shape combiner": it takes the contour from a matte run, the depth from a depth run and the brightness of the same image, and shows the mixed height and a shaded relief with sliders for the three shares, the detail radius and levels, so the combination can be chosen before the work starts. ## Capabilities ### New Capabilities -- `keychain-photo-relief`: depth-based relief from a photo, its mix with brightness, local contrast and persistence. +- `keychain-photo-relief`: a relief built from contour, depth and detail, with crop and a whole-frame plaque. ### Modified Capabilities ## Impact -- `geometry/keychain.ts` (`SourceParams.kind` gets `'depth'`, plus `depthMix`, `boost`; `buildGrid` takes the depth plane through the same path as brightness: resample to the grid, contrast/gamma/invert/smoothing, clamp to the silhouette; `registerDepth` and `depthRegistry` in the worker), `geometry/raster/` (`localContrast`, depth normalisation), `features/keychain/` (`ProfileSection` source choice, `PhotoReliefBlock`, `useDepth`, depth records in the store, migration), reuse of `src/ml/` and the model manager of `32` (a second model kind in the registry, `kind: 'depth'`, with external data support), strings in en and ru, tests. -- With `kind: 'distance'` or `'brightness'` and `boost: 0` the output equals the current one. -- Out of scope: real metric depth, 3D reconstruction, per-region editing of the relief or a sculpt brush, normal maps, colour output or multi-colour printing, face-specific models, Shape mode. -- Version 1.5.0 (minor: a new relief source). +- `geometry/keychain.ts` (`KeychainParams.crop {x, y, w, h}` fractions applied to the image planes before the silhouette; `silhouette.source` gets `'frame'` (extends `33`); `SourceParams.kind` gets `'photo'` with `mix {contour, depth, detail}`, `depthId`, `boost`, `boostRadius` (mm), `levels`, `levelSoftness`; `registerDepth` and `depthRegistry` in the worker; `buildGrid` samples the planes through the brightness path), `geometry/raster/` (`localContrast`, `quantise`, `normaliseDepth`), `src/ml/` registry gets `kind: 'depth'` with external-data support, `features/keychain/` (`CropEditor`, `PhotoReliefBlock`, `useDepth`, depth records in `imageStore`, migration), strings in en and ru, tests. +- With the Distance or Brightness source, no crop, a traced silhouette, boost 0 and levels 0, the output equals the current one. +- Out of scope: real metric depth or 3D reconstruction, face detection for an automatic crop, line or edge extraction, sculpt or brush editing of the relief, multi-colour output, larger non-permissive depth models, Shape mode. +- Version 1.5.0 (minor: new options). - Depends on: `33-keychain-ai-silhouette`, `32-photo-trace-ai`, `12-keychain-patterns`, `17-keychain-smoothing`, `29-beta-label`. diff --git a/openspec/changes/34-keychain-photo-relief/specs/keychain-photo-relief/spec.md b/openspec/changes/34-keychain-photo-relief/specs/keychain-photo-relief/spec.md index 69e485e..606efff 100644 --- a/openspec/changes/34-keychain-photo-relief/specs/keychain-photo-relief/spec.md +++ b/openspec/changes/34-keychain-photo-relief/specs/keychain-photo-relief/spec.md @@ -1,113 +1,160 @@ # Spec Delta ## Purpose -Defines a relief height source derived from the depth of a photo, so that the inside of a photographed subject, such as a face, shows as form on the keychain and not only as an outline. +Defines how a photo becomes a recognisable portrait relief on a keychain by mixing three sources of form, the contour, the depth and the detail of the photo, with a crop and an optional whole-frame plaque. ## ADDED Requirements -### Requirement: Photo depth source -In Image mode the user SHALL be able to choose Photo depth as the relief height source, next to Distance and Brightness. The depth SHALL be computed once from the image by a model chosen in the model manager, run in the browser, and kept with the image. With this source, parts of the subject nearer to the camera SHALL be higher than parts farther from it, inside the silhouette, and the existing contrast, gamma, invert and smoothing controls SHALL apply to it. The image and the result SHALL stay in the browser. If no model is ready, the model manager SHALL open and nothing SHALL be downloaded without the user's confirmation. +### Requirement: Photo relief source +In Image mode the user SHALL be able to choose Photo as the relief height source, next to Distance and Brightness. The Photo source SHALL build the height inside the silhouette from three sources of form, each with a share the user sets: Contour (the distance from the silhouette edge, as in the Distance source), Depth (the depth of the photo computed by a model) and Detail (the local contrast of the photo's brightness). The shares SHALL be treated as proportions, so that the relief always uses the full height range. A share of zero SHALL switch that source off, and Contour alone SHALL equal the Distance source. -#### Scenario: Face form -- **WHEN** the depth of a photo of an animal's face is computed and Photo depth is the source -- **THEN** the nose and the front of the muzzle are higher than the cheeks and the surroundings of the face, inside the silhouette +#### Scenario: Mix +- **WHEN** the shares are Contour 30, Depth 40 and Detail 30 +- **THEN** the relief shows the keychain's edge roll-off, the overall form of the subject and the fine structure together -#### Scenario: Invert -- **WHEN** the user turns Invert on -- **THEN** the nearer parts become the lower ones +#### Scenario: Contour only +- **WHEN** Depth and Detail are zero +- **THEN** the relief is the same as with the Distance source + +#### Scenario: Unavailable source +- **WHEN** no depth has been computed and the Depth share is above zero +- **THEN** the relief is built from the other sources, and the panel says that depth is missing + +#### Scenario: Proportions +- **WHEN** all shares are doubled +- **THEN** the relief is unchanged + +#### Scenario: Live +- **WHEN** a share changes +- **THEN** the preview and the relief update without running any model + +### Requirement: Depth source +The depth of the photo SHALL be computed once from the image, or from its crop, by a model of the depth kind chosen in the model manager and run in the browser, and kept with the image. Parts of the subject nearer to the camera SHALL be higher than parts farther from it. The depth range used SHALL be taken from the pixels inside the silhouette, so a far background does not use up the height range. The image and the result SHALL stay in the browser. If no depth model is ready, the model manager SHALL open and nothing SHALL be downloaded without the user's confirmation. + +#### Scenario: Form +- **WHEN** the depth of a photo of an animal is computed and the Depth share is raised +- **THEN** the head and the front of the muzzle are higher than the surroundings of the face, inside the silhouette + +#### Scenario: Range inside the subject +- **WHEN** the background is far away and the subject is shallow +- **THEN** the subject's relief still spans the configured height range #### Scenario: No model - **WHEN** the user presses Compute depth and no depth model is downloaded - **THEN** the model manager opens and the run starts only after a model is downloaded and chosen -#### Scenario: Live controls -- **WHEN** the user changes contrast, gamma, smoothing or the mix -- **THEN** the relief updates without running the model again +#### Scenario: Crop +- **WHEN** the crop has changed since the depth was computed +- **THEN** the depth stays in use and is marked as computed for another crop, and the user can recompute it for the current crop #### Scenario: No upload - **WHEN** the depth is computed - **THEN** no network request carries image data -### Requirement: Use of the silhouette -The depth relief SHALL be limited to the silhouette. The depth range used for the height SHALL be taken from the pixels inside the silhouette, so that the background does not use up the height range, and the edge of the silhouette SHALL roll off with the relief's rim as for the other sources. +### Requirement: Detail +The Detail source SHALL even out the lighting of the photo locally, so that fine structure such as eyes, a nose and stripes becomes visible as relief even in a flat-lit photo. Its radius SHALL be set as a length in mm of the finished part, so the same value gives the same result at any image resolution and size. Detail SHALL add structure around the form and SHALL NOT raise or lower the part as a whole because a photo is brighter or darker. The user SHALL be able to reduce the detail to a chosen number of levels (2 to 8), with a softness from hard steps to smooth ramps; zero levels SHALL mean continuous tones. Detail at zero amount SHALL add no structure. -#### Scenario: Range inside the subject -- **WHEN** the background is far away and the subject is shallow -- **THEN** the subject's relief spans the configured height range and does not stay almost flat +#### Scenario: Face detail +- **WHEN** the Detail share is raised on a face crop +- **THEN** the eyes, the nose and the stripes appear as relief around the face form -#### Scenario: Edge -- **WHEN** the silhouette edge lies on a nearer part of the subject -- **THEN** the edge still rolls off to the rim height and does not stand as a vertical cliff +#### Scenario: Radius in mm +- **WHEN** the same photo is used at two resolutions with the same radius in mm +- **THEN** the detail appears at the same scale on the part -### Requirement: Mix with brightness -The user SHALL be able to mix the depth with the brightness detail of the photo with a slider from depth only to detail only, so that fine structure that depth smooths out can be added. The brightness part SHALL add relief around the depth form and SHALL NOT raise the whole image by its mean brightness. +#### Scenario: Brightness +- **WHEN** a photo is uniformly brighter or darker +- **THEN** the height of the part does not change because of it -#### Scenario: Depth only -- **WHEN** the mix is at depth only -- **THEN** the relief follows the depth alone +#### Scenario: Four levels +- **WHEN** the levels are set to 4 +- **THEN** the detail uses four distinct heights, and the face stays recognisable -#### Scenario: Detail added -- **WHEN** the mix is raised -- **THEN** local structure such as an eye outline or whisker lines appears on top of the depth form +#### Scenario: Softness +- **WHEN** the softness is raised +- **THEN** the steps between levels become gradual slopes -#### Scenario: Mean brightness -- **WHEN** a photo is uniformly brighter or darker -- **THEN** the relief form does not rise or sink as a whole because of it +### Requirement: Crop +In Image mode the user SHALL be able to crop the image to a rectangle by dragging it over the image, with handles on the corners and edges, an aspect ratio lock, a button that fits the crop to the subject when an AI mask exists, and a reset. The crop SHALL be non-destructive (the image stays whole) and SHALL apply to the silhouette, the relief, the preview and everything built from them. Changing the crop SHALL NOT run a matte model again. + +#### Scenario: Crop to a face +- **WHEN** the user drags the crop over the face in a photo +- **THEN** the mask preview and the relief show only that region, with the face filling the keychain + +#### Scenario: Non-destructive +- **WHEN** the crop is changed or reset +- **THEN** the original image is unchanged and resetting restores the full frame -### Requirement: Detail boost -The brightness source and the mix SHALL offer a detail boost that evens out the lighting (local contrast), so that a flat-lit photo can use the available height range. Boost at zero SHALL leave the brightness source unchanged. +#### Scenario: Fit to subject +- **WHEN** an AI mask exists and the user presses Fit to subject +- **THEN** the crop becomes the mask's bounding box with a small margin -#### Scenario: Flat photo -- **WHEN** a photo has a narrow tonal range inside the subject and the boost is raised -- **THEN** the relief shows more local structure +#### Scenario: Aspect lock +- **WHEN** an aspect ratio is locked and a handle is dragged +- **THEN** the crop keeps that ratio -#### Scenario: Off -- **WHEN** the boost is zero -- **THEN** the Brightness source gives the same relief as before this feature +#### Scenario: No model run +- **WHEN** the crop changes and the silhouette comes from an AI mask +- **THEN** the mask is cropped without running the model again -### Requirement: Relief depth presets -The app SHALL offer presets for the height range of a photo relief (Subtle, Portrait, Deep) that set the range and rim roll-off. Each value SHALL stay editable and the panel SHALL then show Custom. +### Requirement: Whole frame silhouette +The user SHALL be able to choose Whole frame as the silhouette source, which makes the whole cropped image the material, so the keychain is a plaque whose shape comes from the base plate options. Controls that only matter for a traced silhouette (threshold, close gap, fill areas, specks) SHALL NOT be shown for it. -#### Scenario: Preset +#### Scenario: Plaque +- **WHEN** Whole frame is chosen with a rounded rectangle plate +- **THEN** the keychain is a rounded rectangular plaque carrying the photo relief + +#### Scenario: Edge +- **WHEN** the relief reaches the plaque's edge +- **THEN** it rolls off to the rim height and does not end as a vertical wall + +### Requirement: Photo presets +The app SHALL offer presets for the Photo source (Subtle, Portrait, Deep) that set the three shares, the height range, and the detail radius, amount and levels. Each value SHALL stay editable and the panel SHALL then show Custom. + +#### Scenario: Portrait - **WHEN** the user chooses Portrait -- **THEN** the range and rim values are set to the preset's values +- **THEN** the shares, range and detail values are set to the preset's values #### Scenario: Edited -- **WHEN** the user edits the range after choosing a preset +- **WHEN** the user edits any of them afterwards - **THEN** the preset indicator shows Custom and the value is kept ### Requirement: Persistence of the depth result -The depth SHALL be stored with the image in the browser's storage and restored after a reload with the source and controls, without running the model again. Deleting the model from the model manager SHALL NOT remove an existing result; replacing or removing the image SHALL. If the stored result is missing, the app SHALL say so, offer to run again and use the Distance source meanwhile, never an empty relief. If storage is unavailable the result SHALL work for the session and the user SHALL be warned that it will not persist. +The depth SHALL be stored with the image in the browser's storage and restored after a reload with the source and controls, without running the model again. Deleting the model from the model manager SHALL NOT remove an existing result; replacing or removing the image SHALL. If the stored result is missing, the app SHALL say so, offer to run again and build the relief without the Depth share meanwhile, never an empty relief. If storage is unavailable the result SHALL work for the session and the user SHALL be warned that it will not persist. #### Scenario: Reload -- **WHEN** the user computes the depth, sets the mix and reloads +- **WHEN** the user computes the depth, sets the shares and reloads - **THEN** the same relief is shown without running the model #### Scenario: Result lost -- **WHEN** the stored depth is gone but Photo depth is selected -- **THEN** a message offers to run again and the relief follows Distance until then +- **WHEN** the stored depth is gone but the Depth share is above zero +- **THEN** a message offers to run again and the relief uses the other sources until then ### Requirement: Availability and hints -Photo depth SHALL be offered only in Image mode and SHALL be unavailable for SVG images and when the image has no silhouette mask to limit it. When the grid is too coarse for facial detail, the app SHALL say that a finer quality setting is needed. Hints SHALL never block an action. +The Photo source SHALL be offered only in Image mode and SHALL NOT be offered for SVG images. The app SHALL tell the user when the detail asked for is finer than the grid can carry (a finer quality setting is needed) and when the subject's face is too small in the part to read (crop closer or use Whole frame). Hints SHALL never block an action. #### Scenario: Shape mode - **WHEN** the mode is Shape -- **THEN** Photo depth is not offered +- **THEN** the Photo source is not offered #### Scenario: Coarse grid -- **WHEN** the quality setting is too coarse for the detail of the depth relief +- **WHEN** the grid cell is larger than about a third of the detail radius - **THEN** a hint suggests a finer quality +#### Scenario: Small face +- **WHEN** the detail radius is only a few grid cells at the current crop +- **THEN** a hint suggests cropping closer or using Whole frame + ### Requirement: Compatibility -Keychains saved before this feature SHALL load unchanged. With the Distance or Brightness source and the boost at zero, the relief SHALL be identical to what it was before this feature. +Keychains saved before this feature SHALL load unchanged. With the Distance or Brightness source, no crop and a traced silhouette, the relief SHALL be identical to what it was before this feature. #### Scenario: Old record - **WHEN** a record without the new fields is loaded -- **THEN** it opens with its former source and produces the same relief as before +- **THEN** it opens with its former source, the full frame, and produces the same relief as before ### Requirement: Language -All text of the photo relief, its controls, presets and hints SHALL be available in every language of the app. +All text of the Photo source, the crop, the whole-frame silhouette, the presets and the hints SHALL be available in every language of the app. #### Scenario: Russian - **WHEN** the interface language is Russian -- **THEN** the source choice, button, mix, boost, presets, hints and warnings are in Russian +- **THEN** the source choice, shares, crop controls, presets and hints are in Russian diff --git a/openspec/changes/34-keychain-photo-relief/tasks.md b/openspec/changes/34-keychain-photo-relief/tasks.md index 4a720ab..9601ba0 100644 --- a/openspec/changes/34-keychain-photo-relief/tasks.md +++ b/openspec/changes/34-keychain-photo-relief/tasks.md @@ -1,23 +1,25 @@ # Tasks -## 1. Check on the owner's photos (spike) +## 1. Pick the mix on the owner's photos (spike) -- [ ] 1.1 With `poc/ai/` run the depth models (Depth Anything V2 Small q4f16 and fp16, and V3 Small if an ONNX exists) on the cat photo and 3–5 other photos (a portrait, a plush toy, an object on a table): record whether the face forms read in the depth map, edge errors, time per backend. Then build a quick relief from the depth inside the AI matte (a throwaway script or the existing `buildGrid` with a test plane) and look at the 3D result; record the defaults for mix, boost and range in `design.md` (Open Questions) and `poc/ai/FINDINGS.md`. If a face is not recognisable in the relief with any setting, stop and update the artifacts with `/opsx:update`. +- [ ] 1.1 With the PoC combiner (`poc/ai/`, "Shape combiner") on the cat photo, the face crop `cat_face`, a human portrait and a plush toy: run a matte model and a depth model on the same image, then choose the shares of Contour, Depth and Detail, the detail radius, amount and levels, and the contour bevel that make the face readable in the shaded relief. Record the chosen values for the Portrait, Subtle and Deep presets in `design.md` (Open Questions) and `poc/ai/FINDINGS.md`; record whether depth on the crop beats depth on the whole frame, and compare Depth Anything 3 Small if an ONNX exists. If no mix makes a face recognisable, stop and update the artifacts with `/opsx:update`. ## 2. Geometry (React-free) -- [ ] 2.1 `geometry/raster/depth.ts`: `normaliseDepth(plane, mask?)` (2nd–98th percentile, over the mask when given, clamped 0…1) and `localContrast(gray, w, h, radiusPx, amount)`. Tests: monotone mapping, a constant plane gives a flat result without NaN, percentile clipping ignores outliers, mask limits the range, `amount` 0 returns the input, a gradient plus a small bump keeps the bump after boost. -- [ ] 2.2 `SourceParams.kind: 'depth'`, `depthId`, `depthMix`, `boost`; `registerDepth` / `depthRegistry`; `buildGrid` takes the depth plane through the brightness path (contrast, gamma, invert, smoothing, rim ceiling, clamp to silhouette), mixes the zero-mean local-contrast brightness, falls back to Distance when the plane is missing and reports it. Tests: with `distance` or `brightness` and `boost: 0` the existing regression volumes and bounding boxes are unchanged; a synthetic dome depth gives a dome relief inside the mask with the rim rolling off; the depth range is taken inside the mask (a far background does not flatten the subject); the mix adds structure without raising the mean; missing plane reports the fallback. -- [ ] 2.3 `migrateKeychain` fills the new source fields for old records. Tests: round trip, partial and corrupt values fall back to defaults. +- [ ] 2.1 `geometry/raster/tone.ts`: `localContrast(gray, w, h, radiusPx, amount)`, `quantise(values, levels, softness)` and `normaliseDepth(plane, mask)` (2nd–98th percentile inside the mask, clamped 0…1). Tests: amount 0 gives a flat 0.5, a constant plane gives no NaN, a small bump on a gradient survives the boost and the mean stays about 0.5, quantise gives exactly `levels` distinct values at softness 0 and monotone ramps at softness 1, depth normalisation ignores outliers and uses only the mask, a constant depth gives a flat result. +- [ ] 2.2 `SourceParams.kind: 'photo'` with `mix {contour, depth, detail}`, `depthId`, `boost`, `boostRadius`, `levels`, `levelSoftness`; `registerDepth` and `depthRegistry`; `buildGrid` builds `value = (wC·contour + wD·depth + wT·detail) / Σw` through the brightness path (contrast, gamma, invert, smoothing, rim ceiling, clamp); missing depth counts as zero share and is reported in `KeychainInfo`; radius mm → pixels from the crop and part size. Tests: with `distance` or `brightness` the existing regression volumes and bounding boxes are unchanged; Contour only equals the Distance source; shares are proportions (doubling changes nothing); a synthetic dome depth gives a dome relief inside the mask and the edge still rolls off; a far background does not flatten a shallow subject; Detail does not change the mean height; the same radius in mm gives the same detail scale at two resolutions; missing depth reports and uses the other shares; sum zero falls back to Contour. +- [ ] 2.3 `KeychainParams.crop` and `silhouette.source: 'frame'` (full mask): the cropped view of the planes (gray, alpha, matte, depth) in `buildSilhouette` and `buildGrid`. Tests: no crop leaves volumes unchanged; a half-width crop halves the part box and keeps the relief of that region; the frame source makes the whole crop material and hides tracing steps; the matte is cropped without re-registration. +- [ ] 2.4 `migrateKeychain` fills the new fields for old records and normalises partial values. Tests: round trip, partial and corrupt values fall back to defaults, old records keep their source. ## 3. ML and image side -- [ ] 3.1 `src/ml/` registry gets `kind: 'depth'` and external-data support (the data file's path, bytes and SHA-256, passed to the runtime as external data); the manager shows the kind and the picker filters by it. Tests: registry validation of the data file, download of both files with progress and cancel, a missing or mismatched data file fails the model, the manager lists depth models separately. -- [ ] 3.2 `features/keychain/depth.ts` and `useDepth`: whole-image run through the client (aspect kept, multiple of 14, inference cap 2048, resample back, normalise), store in `imageStore` as a `depth` record keyed by image, model and revision, restore on load, registration in the worker. Tests with a mocked client: input geometry for wide and tall images, output mapped back, cancel and latest-wins, restore does not call the client, replacing the image drops the record, deleting the model keeps it. +- [ ] 3.1 `src/ml/` registry gets `kind: 'depth'` and external-data support (path, bytes and SHA-256 of the data file passed to the runtime as external data); the manager shows the kind and the picker filters by it. Tests: registry validation of the data file, download of both files with progress and cancel, a missing or mismatched data file fails the model, depth models are listed separately. +- [ ] 3.2 `features/keychain/depth.ts` and `useDepth`: run through the client on the crop (aspect kept, multiple of 14, inference cap 2048, resample back, normalise), store in `imageStore` as a `depth` record keyed by image, model, revision and crop, restore on load, mark "for another crop" when the crop differs, register in the worker. Tests with a mocked client: input geometry for wide and tall crops, output mapped back, cancel and latest-wins, restore does not call the client, a changed crop keeps the record and marks it, replacing the image drops it, deleting the model keeps it. ## 4. UI -- [ ] 4.1 Relief source choice gets Photo depth (Image mode only, hidden for SVG and without a mask): `PhotoReliefBlock` with the depth model picker, Compute depth with progress and cancel, mix slider (Depth ↔ Detail), Detail boost, the existing contrast, gamma, invert, smoothing, the Relief depth presets (Subtle, Portrait, Deep; Custom when edited), the depth greyscale view and the coarse-grid hint; Detail boost also under Brightness. Strings in `en.json` and `ru.json`. No artboard exists: built from `ui/` components and `theme.css` tokens, said in the commit; compare in en and ru and at 390 px. Component tests: option availability per mode, no model opens the manager, sliders update the relief without a client call, presets and Custom, lost result falls back with a message, hints, key parity, steppers on every numeric input. +- [ ] 4.1 `CropEditor` (drag, handles, aspect lock, fit to subject, reset), live update of the mask preview and relief, also reachable from the enlarged mask dialog. Strings in `en.json` and `ru.json`. No artboard exists: built from `ui/` components and `theme.css` tokens, said in the commit; compare in en and ru and at 390 px. Component tests: dragging and handles change the crop, aspect lock, fit to subject uses the mask box, reset, keyboard nudging, minimum size, key parity. +- [ ] 4.2 Relief source Photo and `PhotoReliefBlock`: three share sliders with a stacked bar, depth model picker and Compute depth (progress, cancel, "for another crop" note, missing-depth message), Detail boost (amount, radius as `LengthField`), Tone levels (count, softness), presets Subtle / Portrait / Deep with Custom, existing contrast, gamma, invert and smoothing, height greyscale view, hints (coarse grid, small face); silhouette source "Whole frame" hiding the tracing controls. Component tests: availability per mode and for SVG, shares update the relief with no client call, no depth model opens the manager, presets and Custom, lost depth falls back with a message, hints, key parity, steppers on every numeric input. ## 5. Version @@ -26,4 +28,4 @@ ## 6. Verification - [ ] 6.1 `npm test` and `npm run build` are green, the dev server starts, no console errors; with the Distance and Brightness sources the existing keychain tests and volumes are unchanged. -- [ ] 6.2 In the browser, en and ru and at 390 px, with the cat photo and a portrait: AI silhouette, then Compute depth with progress, the face forms (nose, eyes, ears) are visible in the 3D relief, sliders update without a model run (no model activity in the network panel), reload restores the relief without a run, deleting the model keeps the relief, the STL is one valid piece that prints face up, no request carries image data. +- [ ] 6.2 In the browser, en and ru and at 390 px, with the cat photo and a portrait: AI silhouette, crop to the face, Compute depth, the Portrait preset, the eyes, nose and ears are visible in the 3D relief, the shares update live without a model run (network panel), Whole frame gives a plaque, reload restores crop, shares and depth without a run, deleting the model keeps the relief, the STL is one valid piece that prints face up, no request carries image data. diff --git a/poc/ai/FINDINGS.md b/poc/ai/FINDINGS.md index 0d9473b..9bcd067 100644 --- a/poc/ai/FINDINGS.md +++ b/poc/ai/FINDINGS.md @@ -64,3 +64,14 @@ ## Решение владельца С регулировкой порога и сглаживания маски достаточно моделей с разрешёнными лицензиями (даже ISNet даёт хороший результат), поэтому RMBG-1.4 в продукт не берётся и остаётся в PoC только как эталон качества (`referenceOnly`). Классы лицензий и согласие на non-commercial из плана убраны. + +## Лицо кота: глубина против локального контраста (2026-10) +- **Depth Anything V2 Small (518 px)** на фото кота показывает глубину, но морду, глаза и т. п. не считывает (прогон владельца): лицо занимает малую часть кадра, глубина мягкая. +- **Локальный контраст яркости** (вычесть размытие, разделить на локальное отклонение, tanh), картинки в `poc/ai/findings/`: + - весь кадр (`cat_whole_local_contrast.jpg`): тело и шерсть читаются, лицо мелкое; + - **кроп морды** (`cat_face_local_contrast.jpg`): глаза, нос, усы, полосы на лбу и ухо читаются отлично; + - **4 уровня** (`cat_face_4_levels.jpg`): тот же кроп, постеризованный на 4 тона: подходит под террасный рельеф и гравировку, лицо узнаётся. +- Вывод: для портретного брелка нужны (1) кроп области лица (масштаб), (2) тональный рельеф из локального контраста (радиус задаётся в мм детали), (3) террасы/уровни. Глубина остаётся необязательным экспериментом; стоит проверить её на кропе (`cat_face` в образцах). + +### Решение владельца (2026-10-21) +Глубина полезна, поэтому в плане `34` форма строится как смесь трёх источников (контур, глубина, детали) с долями, которые задаёт пользователь, плюс кроп области. В PoC добавлен «Комбинатор формы»: берёт маску и глубину из запусков моделей на одном снимке (режим «весь снимок») и яркость, показывает смешанную высоту и затенённый рельеф. diff --git a/poc/ai/findings/cat_face_4_levels.jpg b/poc/ai/findings/cat_face_4_levels.jpg new file mode 100644 index 0000000000000000000000000000000000000000..57c1608f1fed78d346d466da9ab51b03918926c8 GIT binary patch literal 75334 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πntm~N8|U#VxfmFSD8g%8tPwW4o6iC~0i z$OCct)XDyU+D8D>(K^sZB>k!`Ddu%NOt5@Qp8K_KAn}B1H~#a8pbWEr`G8&7t_p5v z@&uK2UF(;hkG{hCW?7ht^N^(cd|wJPS%q81Ct!Iz^tR?J5;NbW<}h`#|-f1{46s z^QznL(81BmW%kfUTPK{UJRkM$-(NhKzmzwP$+W@IZrAfJ___Fphc#L;M4IcF zQRdV==NKRg5R)BQjX^mEiD$N!qx>H%!Y5_D+=-fC(gvqIvVjM%?l&-E@L=B7z=$^x zq$IDAqCT5HKEZ13H0+?aj1+rf8<#*ZK=|tP?}+bPkQ?%2dDsp4uO2&R3n9AA`bdjm z{tp3BmUE07t7vie^#DHTa~scOC|fg-8W?f14wD~!?&<7FD-mRw=fp=!cz28obVggr z^!ZnYBRkvQOy}7&f4=#6lT6E2hj~til5#$lf#hdb$hTjsMMzg!f6>=S)2)u??9tY4 zgahe`h9s3gR=6wq6q6LhP|TM(WF_tpay@LUYTBq=t*E&2izkYp=hWIP!MJ!a%U^L$ zq`Ms%_m)X91;ag=6^)IoHnxrUs}FyAn}7t*a!8GPXPKF>6xL0MqTyZUN_@Te@29OS8(Vq5baF?N0-_^yu0GvpdV?@r=XAExxY9AediwE6unc zCp^8|n5?G$u;!U?li|3n>$~5Js^cMMGcIwj7mFCaQcwk$%maiTIRA!mtoaqXZVh&O zqotQsVfV8xL|CLDxXfxQ$Be6az-%SnqZae{rG1nVpmF74oFF68S z6VpH^E#{qB5Ue>(C_*%MrR8uqxyjtt+Siu;NMJ#~+Eu|gj))E^$-IZdW)5N=Usq$m zYWCcLYg_Mn8=I6`UcBUfT=CYcn*t{m6 zG0TI`DuX`8xv+wKSBY83k9YWRP{z3a=C}IYz5L;&!-6Z44;$$ zO06?`wg*btR6WH!$WRz~beo`-c<=oS&TZ`HSXOA>m^wjWjAKo9cpGIayP~Uvo}AIq zRVPCT`0!bX;(MA&33wmyDOtFF={Ky2P-%4xb2SJF>^%aI9Q*LxE37W##XJ0>)D zH4QX$+uBMUt+WQztIbSgNJ)vj>VzKm3mM%_^??8a!uVJwfFI^V@S`2O)d-A?_wVSu z06BdvGWXGy{n*Vuag+J!1HLX!i6^%@5O2^mH%ZxvD$Bi`@|e$n zz%e~(H%3Z)42+})_pDIL7O?JL3EKHlPnn$*G*!8<2aw06y0at@yg~sX1})c$dr0Fb z_O_Ub-STRiTzuZIPg)6SNds`kUxDd>)U4q~>pr4UV&i!m)W;^+lSe1hS+7obo!S;h z5#~T@2Tx>)-lw$bY z5w)bmtsJt~K=WPL?E#hWS+6k4Q!e2W!1=~UxRg~$2OzMOyl&%V`aQlT+Xsy~Rw;Z0 zCkrtIa`MZ7vv3AM^KE;=@ksYc&5|qQ(H1X%U=>bG=A#(t7NB|h3VjYQ7EXn&MtiR4 z=E-xGY#KgH8qJm52Hi>)6z~_kiU&3FJF_&3MyudofL%I0`l^Y0z3yq5HkHGfqpCM z9VPMlZO8Yczx>|;xuHGMwM++o>`7%$>m!vD=0wwJxy<8k$3yxLeAMx;$&l~Pw;1eD z=Rh6~ib6;q+n9*0^p061CIyNG+wXo;dXXy&wi8{S^xQ=G?9*LwmKdbSo&@kcy-ky( zTZ_H7yk|sK5%dmyxJc&ju>5Q)&nkTWkzc|I6A1jp8YHquuiM(2w*o}*=?x-Hetos2gam_v)qsF9O5Fs7-2 zR9T0V-MqYhaRZ&?=HO%+3C6pFtCL<%4qL67A1iqE01-(cRgSqk03XC_>q#7=r&|#o zf;!FJ!2!e##K5&ITw_~niha6_!~6xmt;L6K<-8oW6H;*i9fj&)ul=3e#7y;zjx1xQ zc{19yFKn9QScF0j6fi8aX!LP!ojctH0wY&Xw#?8=fHrnGy zJFTfW^%R3&wi4bf&ECZzS8wfVTO{D{4Xr{{P0~)e>aMe9<>-1)0CrESnvs$^10Wf{e}}BQ#PNNd3^8AHR@4N1|veZ|@G`yI?tt2Qn=uja15zr_{F#uD)-CjW=_~aW1PxkA!wm{8; zlHY$iN!NP@khzor|Czil_Bpq-#Xk_$zUY;EcVjnS&x@sgCv@X7r~Rlp{c{V*U2Xtb zg|vy39wC$z+oUyFX5BVXh~dd|F8KDl(vTybPBtdUqqFrJKue*ek(B3ODsR6_TOU9C zw>%<}1 zyBz?)7Hq+OG+pN}oR{?_&9=;uq>ru^nUrW$ai>oqKIq=0r%h+OV9fz|(pC^gI;7bZ zbMNF@zVWVa%Dshe=fr5mAbRZnQ%k;utciXKIdD10f5TDI8v%5NH2vRlilseuJ**F) zPk4J|<3;_@=>JJeC8hTFDh+f&Lb+!B1q#vKYke~2=2gz{p4-cpMOtVM^c`Q)Jb4vI zhM9f+P}&%q^bTG)-$;E@yel zz<00p?}v=Q@#`%7-NwN2*n6k4Q)hPnK%vJ)YR=9!A=|36)w_Bc29&9_lz}d}U(J(v zfwJdscKc`~{b-OMGZKld9AvnCx=9jYC-P2j_7MD-##6m)()o_v6o^~mxRyik{l<7i z+zF$f-;JECSxlcPl@|1rJilJJk6b;C5%&Xu5Ng0Yqx_+wq$Zch=_=k+X zWB@mubmY3zh@&?2eN&t*Ccz@@VQFpv3d{%~20l7t?*3uS&@{_h_@TvDj?B6TC36@6 zp0ciGEYRgCEP7X`7bHj+g=bzklmQU`XG{579!T!Y#rU$XA`auJvbX1bEWwi2 z_X40ow*}D02BjJ*_+zHh!}o)6y>!@;VC{cFWFrAk^)aDC`EI?O$+IC-cebdXRC2mQ z{$zkv)-U|7FZ;Jg4J2H8jT>_qH|aKgSz7(K{<8i$A5|v>C~2P~B@Camza=)Cle-tJ zHOf?k+t>Qs?nKwtJKaDiYJqUMl!j;*uzlu`MclE$1DO=r()!CYigF*fTT_CX28AW0?*tv9Gs>Szbdzd{{iGxmTMC=Us zCp);E@eB_StcK7k%phE}SL>ecK(7mb6q#xUFr4x}4sjc$zIj-M^+HKGEYB*Uw?j4G zyIMycD)Kye$uVfarZBkjqQOe?=(-_?ED0egDOv;SVXcyAuTk;-vuQY?KTIJfRb%Ro}W?QPN4& zn0K<%ZTXd%^uL5Ss$yFjB=;>c^UzvSUzTusJzl4L5X1;61X3&N?>bj#&Q^e)tS
d@cRMM7e$0MnAvY16cv~H0^@YAD!-9CGK}S6-=sx>o_ zqm3W(?*sCKfLBTU#{V|;E6D)E>l&o(;QNV$3>gN35l~YYDd=V7(t0`T=;0H+1a=m zxO1GRXg}Ff`hH}D!e+r4Nk`gCtSbxa?_+@|x6RwvM3%7(5CrGX?P34|%jSV{(WC67 z7{B-NCvLQi>fnrhsMHj<5Cf4T9#HeB73)+-Qr(_?dXpH;o*K%K3hQrYEy^K_YUQ_G zc?d(rs?Q47bHjMdwM5PZifnNPwE4=FvtI57WvQ_+leoKFXj7lu{2zQr24`y^c)NO1-$~vgxI!2{;NF@LANb{Pip~Om zPOBSf^hL-W%MCPT^}OttJaxfOTk6@_$81Afxt}+iOtmCq&x>&9?l`b3Z*PInBUx8F z0EUNs*YB`(EM&DXP-VyA^S)_+r+5aSM^H}Scln5QzLCY)^?489WkBS!-}!dr-41v( zhIfMCVub#<$1)UqS1Ku;5PF2dxTF-lEfTgl0nUeLtb{@&Ay z_9QABAg(GsNeR9$CZ+`x^?+YM7zLRxSI1hz3XOJWvSH}ISq)4#dPxoV)4`3G__4Az z*|CWKcrdL=KMFlvxKFzg-_+k{LiIw`>6%mc`=kQ2LW2 ziEMtK`MRN_^4)xzPI4#Fn?dEu@}nse6UGZkVnA&DL%$u`X7djOLAYQX8ot|9*eg7tvxZnUQ)^--)C36+_%kFDtP;8G8rnx6r)CyEag zxok$`CgcP@m`nS#KEvITMryAXTy&0t5%k!)HTbVH-M^8~9=B8`5gVzTv3NPw{m#VY zV>*9%909tTTodi?##jfJB207Tnd|abY)fG4vG)p@-vq?Xs>_LiAGdyNr^N&5B;d#z zOjO&Y)SVpjx*I~hlxv@wjE_7-@he4H@Pe$f8i0`(^=%4ZOklKg{v=AYsXxS;5@@Dx zwP;Q!MGC|sGbfr}bHer2Vvdl5Gd(8oC99AVsY?#V+{3ZEuQ(I?*#v$}Sn1AQ2_@RY z83{{mEB@d_x_-9P72r%}mDSwRblwS6O@42j&ttv)F1mEWdMNQhRD1iE;(sBAKwK&6 z_(u6{Mq1o&w`Bcd{372UGNipuYA?@dnZE}>ed_8lYCXfMOk|2x*}7BP^-rIzX`UW+ za4=O=X8}R4e#Liv#udz;(Mqxt!Ykwpvc>RfxzoXsretb|*8%XspeWQYAR<}m(Dn^^ zB^is!>Qg@X`W({NsP27nSEsrc!*Plos9%2H9^r(dNCcHI%W3C69T98q2AuMeSZQf* zc5qR=Nc~kr7HzYBn0>*uxLgUYTpk7G3iQ7`3f}k0`+p2yNP)W7nGy`ED0H#`y@Joz z+E9d^q^44$i%(&?{Ub}3i=1GTC(u-CIMV9ffFpee_4hy34ct6I+#O^uWkWmsa?6yPZXTz3o9aNIz$DMC z^m*{?@)a#G$+!~zaU+B7JG5N0lctBi6xpAN$D4MG{3eq%K(^eO#iS@33V9CBdIh>y zuv`wKs@0+_+@roxrlV^HB*`zNBlr7MUtX!-TR><;3~2_cI3DV`cb~F1{I346Y-u91 zL6&L`mXbRHJaRxJ30V)rz9SwNfByKN3NIU_@CW>Ki;5c$1^Xqf0;D1V@y8xPcTK+n z59*u|y<^6xwtdg{)XuNzK0R4!e!J9*t1!4|&I*X$qiyDnM99b`ZS{wXw%T>)6MaQ1 z2Jct}ejTxe%d|T@sxUt02KmqgRjcsoVRUP2Ftkky1^kL!3HLJ%eh;3Aqxm!bI=%y4 z4EuEIQ5A+{b?IL-Ev0I*rMNy)W4%iwz5^P6z;GOS?t*pnT94GVn1$Jw9tHJ0N-(ew zAHof1_>CWt#Rq-g15z;sM*O>27{;~ZoId-DaPLG7ZLW?3HQn3c=1DmP+eh21&PILA zQ#fbR-AgoD=#+y@l&=6%kh#D-->~8#?)7+}@*t_&i~Ak_1#^OLDC^dGQX>2~ZI_>S zSBw^Ng%Ykjf5Khh3tx`%tKR{R)OUF1O(yS0ef=O9m)7qEiP*1lO_6jshh5A-g@)cf z&z(7L*2Wxnd(uZhY8YxmBr9M;z;*4b4UPfE0nj>Z#kCIHzcah?IOK+i9Jr1T&TQ=2 zCbu=pP5DBHv`i;ocy9PN#mhjRBS>cb=7CXS<58aATifq8r;7$q7Z3SInJu(Q##kmG zdGeZUSn4PjqqLsffXnlFLor|JNn#{?!##fR;_<_0qv~XOpjGMB+?8rrdGB$!mN;(+ z?`8wKB{PF`-28z9<>*W1dPQJDw=;X7hHhP0yExchBl0>#gtnbJ@(6ap5|1dnPjbm> zk45w{_h#DB>`RC@4t-1MyB`zAx*ZiRdc%L_* z?X3v|2X1HauRcZxFoa2U5WCK1n+A|&H5>ncORv4Qxzef;gUP;4TPvvj8pIeY0^g_4z!YAs%+QAF_ zC-9rp+08YmeG(&6j*FlS!~sEnTg>BcqrbpQ+UG5#Ut&wUB5(EJ8w~N=%LzvgKf{Fl zLIvdZ9xE-2^+czBx?|G4dB0XB{UPRPf+)o^VQo5WkuGNI#Xc?GUxv^994wG*={o=J zq2nlL18iFly>H(ahcq3bQ4%b?iK|Y_>6_BB!DBH7BSk7g+~fLfIu>U4>*w%o19Wy& z8IQ?~YQkq_BlGxhIp`WVkPMicUF&zYN{`hLI_Rw*{;?IY{HA01l|Pv?XO01ckDY~p!loD9B#Wl); zOs|EBv8*O-W&c-!)vY9QY1RRbUYf9gIj_1?r=Pww&bYmspcIZw^0DDxNqzAgi~G=h88emB$A z>zdbJnh&lrx&x69Z$UoGpVtjJ*9k)G-F=jN!je94=J1$vNM_thzXOzmn3XE4y0!kN z>0NsES2Y>&&1GKqe*86$DcOt-N13Y?iYg;@a}9A+8{we=^vyKh5%)f?d6MsKH`JWC%P zU6xoFm#*y8sr*elWiSkfcGblrDo_S$GY({&?u-A(E80zahxNDbwK>}lu!#R*wygY| z{>17ZNH>X*RBTsssO% zX%FYGRvp-VXMqfdexn!IF6mGNEPq&}iYI@@J71vRfXXu1n5##Q55=zb3qXtk%36M% zESz$GjL4Z)DB%@t+~E?7i%6i)A#inO<|3#j5!`6cwb9VO3=)Sjws8xQ^?7ks3eNT6 z(k&0oUKynV-4Cg!E`}!H4sS=iwY(HkHHX~OKct)qxPB2Ez1G$lF~K;FgK|O+eL>! z#77zW0$`PUAoCYmzgx#@($jtH?kl&MeT!V^CnGHljS}?-R0kl;s{G*ZwK39fYMDy^ zQOK+shR&6syimc@OTC_TSjG!JIw)1gc%DOwI`_IwUBzU_ZWCKh-!qx|!q8z#I5o=kp(&s}=u&(myaIq~@$hkz} z)u=Qa2gq`QajgC=y_1-hi+r%K6T;S*z`@;8YHo5=GNQUA^Ti)9zfIjS1Iiw-nISd| zLdGRjAet5v>$=Y29`!6nBGpr1m9(e<7dY555KchD0oSxau&14~C;ExGLq(|^S!#{B zd7 z8jg^iZ1%r?ZMQPtl%w<p3N>%DH?>6j-D zJ=#a7iCMsApj(`51)xT|UULX7T=?{RS!(Y-{kGp~#VWO#ulJoB&RVM!mbC~Tq?FRl zx2GlUR{+DC@ishxaT2q@UY*{_R!P^Jpcd`Uy!z^Y1&5`4y|*wY86zBI0nXm}Nn&4Fhar z;2z`ZwyqT%gaGHdG5y|EY&S2pF<6H?8b~h2d+>E#65*5)EjX*?rbDKZg;o4|& zuuC6FfE=g~z)RGSsPnMUwdCj`#8a#_vA9FZkN#x6ZCOFCY|&^v{O1b|cN`dR@=tJPu1X7mhWKc$`VLyr<)QPRT7O`!kI-7DNU3qIk|29GQ z9;a81?|6a)VkHxV@W*7IOHAERT8re~hW^~+|2f=xtQbM+$@WLWOy|_m zvZy2ZFF7^8>@tv_FIGvR?e?c|m+kuD8(pkf=aK%-A=UXeJ?o9D3fw@~|GJhd!BsWl zoivUv=RB+ia=A}Af+hyP4%Z6JpCz6VV}pxvJa?Hm8#X^5tPbrJi8q=fF_Cw-^YHh$ z%+w^y`bp%#f_{Ap6k&ya<9ajS+>7*gE0FU8#OHy8oBV+?^3tB;IQFFT1OS^XZC?f* z?O0|UFMX^7?CH_=@Z>MTH;ZMc^rcIMU`aq;ZJnu-uh7Lek5Pv>?4g=bf?w(GJKuL9 zcJZ&=QMjo9B<@)R5vC@)yDeCpCQRtMx4CA@gpzgOV$7mg_HH8X)kO}mBmE8yz3|_v zISGS!vzhUds8UzTEwAX~8UW2CK912_MQ?X1ExmuOoP18?q1$WFcOrN1s)`2KzIMq0 zjTSzv%`+QY?EBf8=To%Uty_nj&*u*)b?x2K2DcH(FsEM&!M~rrZuGUXv#rT|&1n^! z{OYTX;gz>Z>mcZ_V}Ramc^E?F`_meM-3wsb0_@L77ZDe~5#c-`Bgt4*lz&ZD&O@9X zO#`^!-HK8r`e`6Vm*%uZ^Y38oK>@jhr1*flBhptnWbofpXZvjgUPy!hQ4{Uc5L&fC zj}l_a-GqGWNua`#7T|_5Vk6HhZwt1r5tX`nvVLQRMuW|zFAijA`_Ruoq=24sb-gx9 z+LvcTt;ZbJNdjeXe6j4Q(MhF#ZzC~r<-f2!2=*FiY)5Kl-I=V700(gEc+s+i3Y#b^ z`<8iqMo{~R3<}#)gu6*1d#;E%4yh@!5~rJXL%;VdV!>kx@K+uKPXX?|W~OzrFUG`Y zhdg!|+ITjui=Yi+*OS1Pv?P6>`%Mw$AvA#u)Rg!7x=L$9xwbHG%{!>cDyN0VHm#&OSOsVSJYXWg=Xh3VltD6C zk45K5NMFm5jYe)nMhXjT<=ysQ;5`3?!B1-TdtN_d&s7~hR{UB=Ld=g~d^QBA#7~Sv z@9tw+f9zvqOW|GWZFLsW^a`begd3*GmfKrMxtEY0;3#P#c&F|j?p|e8k%e;e{yI`* z9c=z0;KcWuG6A%@eOvATk_uTsafDPy(@`(Tlk1m~FqTsUbh|Ai_U|JDp52W~KQu-6 zbw@kCX=pVYexzL+xAI3vs-()RNzLqA)K%sb(EJ|`=Y9eny8kDVPcu?lQhF0!PiBR# zBg2vg^YbeUUlYn{8NUVrpSU;UZT6!Pz*k?*$G>E+Pf<|c-*R`%HW}0LXisok@kx^8 zO+PLV|0%F5=d`uOQKW+G>O*e1vguN4Q1iu0CN&_9)>pbG5$is^W}8Z|u}YzE)(@@5 zIfJyfMgH(LLo)3W|7z#X4`BM^I>7M*kQENfarZ>Ulgl0PCuNEE3M%Rzd^=m1E%XNc z&3xB;zP!FH;Zt%iCPzBs(L++#IwvXMN48dw9ILrAXA8Fqw0<+zZ%I>dNT)}{z$8%@ zOgp$+C8PLAI7c`6>$F5RcEmz)euMx~Y$H`bf3H4k)P_2^7A~PN?&f9k21DJ~6f`Vq zu>l~mlt(x7+p%joWJRqM@QaU*B(}*H^wSM(X`806n;uCbZsu@t(ZKW39J}#3Z{VH# zM8rj%LV{PpS&Sd*=LMYM$0#M3T}K`zi2dd89s#dF<$~6ZZj_f3Y?FeO@;3P zAZ6v?hOrfAOsK`YhunBiL~r=#iV_|PGu|vMCqI8chw6xci$@k9Q_^%_g@)XSqLlV8 z-TQ?vx2sIX7l*3lHb7yIOsu0!NEgSz(woEU>BAyyW1`qFS+Q;@xtN(W(w zy{XqWpvXvhp1ameuZ{DvXU*!&G~YJ~e%vZDZm73A;mD4(gtXkB{fgR@Xe0({V!P0k z@3BE^Ux0w5ngwHep%S*}e3>lO{MQY{Gw8Thdl}O3Q({tvmmr9k?J^{^TaPC%fA_+F z?xg3p(fl6G&JQn7^m~On3I@&rgV@v!0Q`n@wGrwU21&kY%!;=y9k!#Z(LE&pMg92X zq_iG6&$e9wP*=2#Z);Pt(b-r*^{ExrYeoGq*0vT)8c~YJU5Zbgx*x@sO9C-5(6ZYU zKdK*9t8IAjL835=Ol}y=>@%G*OYc5Io7%TOPPt1{<}D!a@;byLL9H?bEHt$s%p_ zt=#5uSp-E})7Q-uXr4RB2dImo;$%#_#U-Ce{brepK34@w)*yfW1j`Vpf-j;$h<~7K zHGRVyb>N9Fz@X19YGaiM6lp0~G$lWW-+%z^xdFpdW4dY_s^z4@bq|0=mMC!rVDIaK zW{+a8_ocjY$V&f#R+FGF8ZaE~qYwL@0Zd3WhNQn*b-a6*;gES@{vSvjboE`|O1x#b zc{uecCl7!`82ure0tUpV7l-WY$5{vZZ}=K8 zsx52&%(q7}1NdS0bRROhKF0eiRRQSvF(;=eNa)|lXftUFJBsB};?u#D*pptd z4Qu9VoFn5Ze}G?c&Rb>zuSccm#e6l_fgN#wHx_t0aJ_V&5g3MCfy-^PAhXRhb|pLX zU+6c#C|o4!rD(Yu2CR}UwP16|q_NxuaDAPrzubGM$%RG4lWCj|;m^@?)|EuHTQvpZu5_(zO!b z6KqzAZ1UDIyka}yj{sq3*U#I2Vi)?vM?;Z)?`L&}o+w(Gd)gV!(S2m}I0G&ksXgh+ zwrF4>@iQZT;=Oo2_XGy_S}SgdH2*%ZpVe0wJ=6iLjQx_Fd^%Jtk^08DA*}dxL@l9j zF{=f>4|up-(bivJf4S-Dj)l5Dbu6k7Jv}T?=*+k}lDt+Yus(FZhVJ>JL$ax{EVtQ) zlO~E;%aWWz)}WtRHx8FJxLtm{ccW02+X8=UW4s&r&y;r*v1GfB%#~ItB-~LNqQg~P`?b{ABl>WY*{w<>OzDh4n1bf zsZu{^C?`so+uS`A!~*Nq%BkV7!|B?-iu35fR2CoL(Y=)7RHv6(7cw9GW&jaR-3DO2 z25GDE81dx1b33I=REyz0Z4xU^{L6ke;tUMX4MFE;s_TU_p)-3koYqb`y(x6CzA?&4 zG|;f|V4#t}nzRrmi4WDGD$OVC+=T-yCzc)yH5ZWd=)z!@|MHeD6H#QJ9o-BGHaY6T zcE_O%8i6#=?n?$cuJ4W(0x#4?7Ab~xr!gw+OIEoXO zE(1cSPivh2**#Y&(a+}VEus1Pfze^0SN>HLGamr!jr-rfd$ybGp@}rwg_uOpP%M5^ z?`H)&aX1fb90K+$$I9k|@#8E!`9#5RyV?Bht#cAL2#wvjyZ%>5%g zHZGnqG=4sm4H0l9401EZZT@J!#-yqFyD>&Tmdu!{rX}Y1Px4|vjWg*_4}mI5U{yU) zHhJr_VGP;p2^d$I5Cp7))jN@^nG~W$K8^R}^osHh##aNK=%|CG9Sh12Sw+L#NjTI3 zx7Vqvd3^{{9I~y`yUE{3du_g0B`OMGOAx*+kUhCK(2oyT==0V?3d|hNI^2E4KM~dL z4RUZc|K0xG4{X|DP$f{vAx#9dt6(UT!G3a8kLS8H$x!hiB9G11muRRChvE^=U`E{^ zQSx3VXPHN>_x;9y~U#OlNJWI-Z((?-gK9{HinQnGiT zYvc1Y4B3rl%8a;V;zBkhlI_J>$b)3szFdw?>0@j$Rw&#!G@b1@u7+&+T*zFBgV@z-#eu!2Bhh-i)HnAq z?xUfISB~77(}~q@$WO@s$I*EQQvLmZ{Bq@HM`d#rq3pdc4J)fO?7hde$Hg@wE1OV+ zBvDrO-kYrKYm>e1m2q?N{hiP6Z|l16`#$e;p0DTg@!ToMqhxP6CJMYl^poqj^M7-S z82EDcTH9{y8Gv%IdzPYN^zmVZs+%Ab@)a@=L{(`PU4_2W1P{jgcE4#QVv@dC%5gG3s}h*1$I*{~ z@RKe5m6wKOkl`xB_VQ0p_SVg|djXM-L-cI9>P5p=Bk*B*Z z544nD|ASZ{X(dMDG)tq9M(TB9q>!+P1SU4w)euEx!4}At|JRNK4RMj-P{~La+H|hJ z2|Xc-@`}jTP(#;_kr>Pihn++N$95b2JecDV5fleRwu4}rV7nYRielL(ANOd<_`#a?Io&TA3_X>#%YG#uJ$q|3?2`qj!igth(;m`uRj=o2l zd#!o>{ZUVGrlz3Y21_FwP-gr<S_MO2d`8~l>l)Ps+3~Zf# zwt!kYZb}W|wG9q0i{4|f)k(m?I&~;_YYRu53&#fH+aXZ5e%Ou&FR_B&lkpO-+1$ip zkE#!`$LDtcUveOcM&WyZIGo!6+SNAlpt@0)j>Si-W936=f%Fw5GCtCm16WO3XSI(} ztt-CK@*+jE9nG@4A&;Qi70iEz1*oD>_nVjB{_*}za{jX%Mo(Z9=v2NKeHu(P$URbM+bDapTO6F(vb*RZ{a`jE{ zKtip7CvfHJNTXUG9 z0Rm_sBLsQ$qjInh3}AFNN99I}7^@3YYS65L8siZ#$2#U~r7)U7l~NfZBi=U^ zmcCODKtjMCJHOqKUBBk!q(f<+Hr5>+;F9Bz#-_Y9WHM{n>&w_C&5r+1p`XT0M)oLT z(mN|rVx*=lSX4KWK;fAmFK`lqGX{*|| z>>a1aOf|tGYUYFOEFtY|k~xNul(sAO%+A;6S-&u6VcrWaED=v8B+}EIK0rw=IaKd4 zs|e1g@<@Y=c(eBRv^I0eub=>dnD=Bq3`m`8_DiJK-lWY=7Xe3l`btpVYP0OAmV$Sj zoEEn9tI0-v)01~&3rZv^d&N(3gdgO^%YgpC9Eujm5YA3(>?FNud}GwxDJJt}qt&S& zQjk;^!(frMXdOMsI6SoS6J-cYuXLyvJz$~|Y$2a^7Q`|oV5jlzu^_iblmYHR3nMHx zFRP0#D9BJOl;~LR?dNfXY-b_xL9EU>DBkcok!s=n?S+5x4GU3Rb+U7TENoZ{bU{Lk z+p$dV2Z%Nsa=Ny>6Btk$Piw^-DZW+wJo+LY+ocFZvxF5$-}A*%@MQT^tlFdo-wjVl3dw_AT%z&B5rRPLhWBC z%THyzE^>`YEc}%q%0`&6BO&2>GwS@A<^%9?UuvA%;YWZ4>6&BOnVSpoYI`NC*poI` zypkNT1`SyEbJ->EwAkI#SuFL(Rr;kHStLcCj<;w)8WU|lpM z_%u`%g;j}tVT=Df9rQsu`K|NX|8^)gU(l>m;&WP7&px$eA>NB#nfH)!Ng^`!gYJeX zS}Iy|*Nz9C;w2Zi(IU;sq8aYPqrBDUe##7l>=QbiEro;V^YxHLDstu zdo^##2UqVakzs%7{J>us?M5NPn_QiBr)*3r%XR5QdZFY?nxO9w|p>G%?E7 zjdjU(_vzx3YjaXZ9@lgAf_=98nkyp)MX?fo+K4cZm>Mg!<~iggOUVHQ+6&U0RYXxC z`c)I|)ybe%Z~%H*SF<-;KUpCXph%&|@W)g9h`VT-mT~&uO!41cxg2{m;le%bD*l(N z(w}LBE+ejJoffZ8c?aLxrLS;E(PUAtuFXRDAlv%p0!hctM`24DBVD}ma-YRhQmK&a zNBVE-;!$TDqp*#`E&&a_%ZgSY6H#t8XbRoSZ%Q&e2(jyXtKOjMvc$^?T6BY&P>r3; zrXR5l!IYxm<4amd@g60)vAiSPftolGekj=Goef1G;H=;`gLSYu~M5mn* z&m>@@FH~l2Qc+11&fy+U@B}{^f1!T)L$FD1C2jmmU5a6=Q3~B~TEX`d4y*O6DxzPH z*K}^|X0rt>yq5O5^jJh?FkxnID|zU7mNWGz{#P)>@NL&nwXHSM@F2yiq_Y^cre7g! zfH6{I8Q?E|)h^TV-LfmY4#?ok?8M`?F>3D?UdvgzGAciM^WmCONSdO^aOVVQ<-QQZ z>NfQkKBY4_+v|6xqcN3be^0c8B0LG(J9B%KPW#U%cC2ErQw@R2Pmux9Yf~W#lC4F z@~A|~i9}wGgXe9%tZQJ#J991-)AU4$e11vk^r&Y;j#tT&$-UoFin;~Nw`3!6k_p8o zDS-{AC}*zjh=u-askHr^##q7{V0bOXUeKHNVN4`)M!8Dz2OZ-q6d`d)=;&!iw z)b;&(kUQX!RViA{crU$mej8(I21dqSKyk>vi~UxFO`g-p zRIRC)60iHLp7MPB-d&50lXzWf?FEq85+_Q<9vmrnu4d;T3SgK`qn`JMz-?adZ3B73 z0#CK#;Zg9<1|5SZ)4f7)jAP5q@x!~#8gY&%Q)FI+hqm?6cQljP^{j@~2p_h=Iok@y z+QK_g<&;UGrS~{~A<>b{;xfC)Th6nt`;OM1LTDcKmk?WD{%LOovFF&cs3$=tlWB?KbCs2U=$h1qaU47w@2x1GM;}0h zTR4Wk1dS~|s2=y)_J)(ARen2PD@_8NCr-8}E_0_8AAM5^k0F$LL^b_QMa#=K01*-S z%`q4zuMxb;^4Rw>FMs~SJ^R5sRoP;Mt$9jUbB!WSDATn}7*`@WoL-ztN)HE*MDXH+ z<}}Bf?)}gZsJ?84^}s13E34~; zLy^y;dx=)$d4;6nwrYdCx=P5HM9?%H*bb!U5uD0Li`D%X8pua4>+7P?MZWKErC#s5 z=RTZ$4)4%t}?gTPfv={+Ycn)Oy_)f1FQ%C&Tw)X(bkQnIU)wKxi<)u`bPy^YIT?{4!9v zwHE6R2!OLG;>5GHl=o<~Ac|w=Dr9@7|s_}aMOk)3o5cPrH?`NZ4frVONMuh#QPt1x(bdAC2PsB3R6sKKkNg;kgY??AdHB6w@2j1fH5(hkNb>4)Dgc zMtq$(G5Sz?VG&F}xciUM&t5DmiH->RCS_t7NFQVbb>PmHT`=B8P`1=e+Lc>Q>-~7F zdK03gyr(ZocDx2+lVS)Ve9M|KJ{me0B=Lw_C1W}6o4SFXpY$PE)h6BgsZ$V95=JAk zn=k4s;_s&hPd=!)$Zv?f$m7iZ*9SsG!PR!%1d&y_Mg>8;Pg*Xgp2WR_EJkN8u)zLG z4jfRzQqT`qoi0^%(!^8qq92hxs$JqsCcCsJo(@NFo9a6W1Ss_1RQoLdc7GngAp>Ec z^8u67!)9klYvgdWu*2VN?V!T+nC8Ge^{f{t9X_VwxT#$KZ6xp5o(c1PLkHirAkj92 zTaf7l5k}<{!DLrEh1M!WeTw2#eY5V)`ldpOk@H{;j3XXR2i@N~#rS+=PjKQNlIwKl zL37nB`uPU#QBofh?f7$t7k|tryCu1Gor~A`ot$15yOg9{o%1eR{s%Yq*wwTY}X>#^KpGsNyAOhXV4Gg_<3 zFWBg6>{K4xyi~G$M8HQMqr0lLKBs3(VAk2+s5lgn)X_yN0~VD$D!no;UnI#fMQ!Z-bq|c#t6O6T=d@khqbY z9GuE@sODw7q_Vsgt^`FNO8&ZYpQmCQ$|T zcv3gyAvUrrh_pQ513Iu^u++eGVR>O@;m5+9G}HeeFI8AY~4&PPdG>Q`7^P8jLe zv`h#fP50U@kj2+8#^^=Tm3dH*_)aEJ5|xLbz$ZMyx`vx|Ua?1L1?X3WT>jYTz%1%7 zO3GCf2@gi~$&mbU0eNdwYnK4G4l}CvB~nGTF)r_BT19R0qJ+te0GEF%!B=t4RP4-#qKw-8@E z8Lu+Cjf(6r<+MQ%<4DNtN_sLa8fuOfFf6<1gbX8FWN*ZLX1m@UWr*8SHLTO*#fnr^ zWTZEgsZ?U{~;DXA_ZRey5-!)(tIdei;sTl033%oxkWwdOS{QP^lk zSyYuG8|v{(S(m)vyuojsV)KT>^-0esB4-7vUb;gY+=nRJ_8U-(Boli8lwhdlt zylP1{w{y##^Dz1%L{78uA95ojxN7NLK7NFtiVxM|7vKHv$ajl2mrG5BR%h+pHbfD4 zJ!~H}z}igsG~dh4*(Xy3&XDximTAvQ!=FO-B68^)e>h4OxMpqD<<9zk@sAS zNMMLUu;XzVi}$r!;~AXMe^t8p97=Tg@G~_4)9`@S*RGtK^3v(PYF_zuZ|p)H7~DP@ zFikVUSYzS9nOL9&(nFf4VWTC%L!+ANs8obwgcrFUPb~Bk7!g&Vg}|3z;DtJ+e5My= zr>&<1QX>fFOLnSk5FQz(zO9E~fO)e!xT>I|byldemYo*wp5MDlWjo*-eN3Q!;^BT( z^Gwc;ZGR$Gx$6$9m@M&0&>tsKh#sS3}u#jF}8^ zC1E1@_``#zb>`2(+clD2*$<#lPls~EkbTI4kY2C}C>D^n0ii<{ok^QVuRC6_Jm8fx zv%T%U9~L%!^qGblAD-*P+`Qj?x&8?^H2CVrw*0w+Xm4rth6c9Di6M_^-I@I@k(3dOUQi5ak~DE)wRU;4GsXc(88gPb%eIe0$wo@Sb$4A( zfbQ1#gr6u@JrZKeKq5p-Z$}6ddVh&!e+1j8-76Z^pUq?9DL)Epa)ulhh%Cw9&%~-~ zuD5b3u@5PHG93D}dj8zG{PT5ErM6*eEeP6`po!OATpqfeR(y(imCWSA8A=&x*F$kb zG-&109=fW?Jky+^>P{4A>9JzUHqgtsL!%9B9zq>0ck8R`p5F-kc|mwat@9lil`d)V zJgY22zMUBMqJqVKakq}|?so3XY~ig@HachOV6!n(ygKSF;Jvpga1R?b&-Ky9;49oW z(I+lDi~0b>x!e`$1&(uW4BA)~iqlI1q7~)e&KmV;R>84$c{{}7G0|oV7v7{O=Czij z%$J`rXPBA~g7o_P0uBGRv+IdkUbN5s4v`)TV&m(c*7;&MJF?PBIfUHk78icH>sEgJ zp6kgq+y1_D03f-&9LMmyar%U;1U;xFrCCj|&GVribAp|HNPdXMevkJ+t%Xp_>L^qF&go`}#KFiL`nsv8aB>yzNKFGcP@H?SUG>vEIP9T=$ZpNqYb zucPo+iG9#}jbaIEYTts0d0m_S{M~|!EtP$xx<^G4t6OCR*B!q$WbBkEUrRm8wCXS+vo&{4Od2=-oc}N7kMg$pQT~?oC4hdcAxU z$y#{78roFRRCXZv>p#p(CW$Y3n}xfU%UrML`7@CG%BRX44##h%P2HG3rOhFuo|*+VQ5Eu2-UH zA?rnPbKP!=5{v0gTzb?ME!ueELI_8_cusg4XB#e5Jdt!)G_jT_lE;m+l}(Nkf^zK* zrc^If#5f-lVqB?5xOa^{TFx`yIf|T@h5Yi!V_Z;TwR_V-;+P)9XTtZq=95|X15%J4 zT+dCs+K>G4KS&%Tuyu{4+2}L?FO`dbih-A#%wq=qymaL|C?shnjdId@A1}ckT6h;JU2#naSUo?@?BQb9_9=Cf^8x3(htvC4Ljo=0Ws-|?bV@(wAFbs= zf;qt|{T>rCx=~+u)I^vIr_*Fo8VQnKV+4tYO`2FZZSlpf(@g15lM~+~f;MG%<{h8- zI+^{&5Cx5uKPlUaREwuajN-e=OkZeYE6bi~d(OeLAm;c|#ajEnCf7}u{rGs&%H#Dn zxjqGh>D78ph#?{$2R+UEc#vvxM@5LsJw%v@Vibs$my}^0&+sg270F{-lgE2wH5*(U z&bBH*(g65SfYuc6nmQaxkE%L}vjc@Foqj1BOYO@ijK>=xvX_=fmrWff4RI~q`)q3j zl`B=P5Q3P6wk#=V!$U9N9_%;;d>cgy{{E}$^TfftkMIayzag{$bD(P5<)jV`K`7(< z?IcRSdCk;NY-PU?7tLw}aJwKdVsB7h=rT<1za{#Uyz_nt;DH9#69jiF=kDfzdSqZa zGH?_=${#=5{@_1fZ(#|j!|rTA%^o?2OMV^LMN+SSQ^c5hNq|vxen(27S@(!yr3cP3gm2b{bM{#ad1u z&yp2}f0xL}eb_Jy^X0%PF}+6Ip1hD-sVhCsxz+FB;Kb_d;fHS8&|S24VUc_3=ST{H z@-PBlJmEA1TYN=SGhSsvqg)r$`FA1M!Eu_(cK@<~Pjs)&hnGnCa#=rU=TCAy5KkiT zn_XBZ#DmFukGX2%xCgy`EjuKjgM_X`85fw&K*+r@iTmPH?>Z#Q$Ax&JzBjhMCy8cZ zvIrL(YU}weGExEwnC>itJDQTbGF$yVc#)kbj-?lmO*B2l_ym!>E{0bwj0 zzRn#Yl_7_G(X+oQmlJtj8iy?CvTSX_2LAEDXo-dfjdsA~d^vMPQmX!7-Rz0YNKqZ< zyBZT{DeS6(HVv7`UvH|X2M2O8Jo8=u?2SDxB#GB#K%pkHh?Ais`dMR2`{{-OPfdiz z*OrQa_k>5J|Nevs3%+#eV?}qeZ9yORWN3Y>T`epFA)P(82P_mK-;FVG0s_m)kNZDb z%80-hXmgtX0&)PJ2i&ZYZ{-a}_l`|e<%$g6g#^bhj86}a#TuO6xlhvh&7QV}L z#NEevsFrpH?ZlWYxmV?h3i%f-6_xG0!zUK8Wt39HN?;vVCrG0Hw!^DjLTjGsn>a9^ zCb2U!S<^I0E&QuMx6vqdGJ-lr>eIi=%JD@j3*3uHNle#4=03CKH{@x0dyhznHp~AY zlS*_z8UL2Py#M$abF9j)Q~RmWcO|Oz+<~62J|rg~eCO5!7>pf4cau09Nh_r8iF?fFk zHt}Cx?_3?%xVAqX3N{lI^IJFj>EjBWs~C3NG;P_9;$MZ4LILULCO)%UHYfILZA75@ z=F6zdtONK4W`^I*-ws*;xMVb)|AR!luGM>8JADek6~9kz2pllaFNYmv`>a9Vj4uJ1 zIc!Yc{mX?6_Hrp=&za=s>{Gj=FCEbgiZVRk<>LX#F`N7Hn~Qv4*(@r^!Mvt6{%{XV zl}lnDyk4rb+xDN@R#alt8GO^LS>e#S(v^H$B&rr6;_Z)uL4RUU+aq8+OoG$ft82Z6 zR(4w35-dJgyjuU0@!MXi_XgJuhzz_dz&)N9x~CY1xR2n$#x;d%ygKTCrKV^9P>Jo+ z6^9c~{Gv`K$B#rQqJPI=8Lw$BjN!Et?)t5Di64FloA6o%oky!Q-4 zN}tB|POoo+^)wNQc5A(X;4`SG3%FgUchqqoO7bU_lMTG;0HOE{z|D`L>%nQBhJ2u> z5qWOJxEo8G&ZrTuj zpT~ZDxXv8$IBl8z-=#44piE!gHJft~lP9D((O!?+q}!i?mzl~@gU6N`Bg)gY3(37Y z)v@_(yky>wLDut2t`8@>JrtHI%>%AP{FuaGEBWxK@p|=jOS(ZvB^&bD_J5E+D-B?S z&stjUn!8dqA9*b~S69ohc3mkqHG@h@m6#J%W%(;fHk@u!_0 z?6}oTiQSKvqByGvo}E_&_RWsJs}7Tbc@cquUJfmz*dZIbxQb-Gh_$S&=&OI9$xZL$ zOB5qo?zR^=lDF{c3$UB~H0%nR? z$Tc>`iFGqf*Ji>}kw4}-qJ9MZ^_#-Q&2T29#Da|}DmYrvu353|yvY#wG>n!9$vE7Y zJ9;+ObekS2Hqt$*aB0_t-k{bYp@xXG+?hj}buLoXkBRF<>NN~E`Y)Qa#x%VyP5;dY z&l#+}_UKwixPsrwXL7S^LQP12ud)c-PDXc40Lms zV!u0zmRb&ecW#rFo#ma6S23XHwZ?so<20OS4V_WtRpYoVeZ`rgL@&=~i{`-zPMvU1 zpkJM)WlLrc(^>mUldo|>n)XK+sQ@}eF|Nu)uIqcgeF{o&T*q(Eh=4dh*v?C~TeC)% z2if+&mYdm>2C}0Xz8tGwg|ibZSrg#Xn)pgASL9%_z?mX*wB%^vUaumSFc6{;aO#FH zv%+$7el`KYK>vb>(iG;A$WrHOmZ-zK3Wgk(yvfKlzEruN%PVd~7%-!JD^G))Rtsjj z;G2@5l6#s}$(^q7w!FD+*x=&(qsA|x?$zop#0Ox}wxsP6u=D^le0H-~uGt~k|>2;hxaodsf92j3Pa-)-^MaO=1 zpGbK#4s2XFiO7ifoVB&w-Eg>FGcR-&O|#nHB&YDLAm60;T=N%y zL8!UdoniTz3D5W@Kwm?$=#DpO1Q%|dOT0f1|2u46(R395IQ8G#kIU4kbD%W-(_2&Q zY*Y+K@YOm zB)x0}0$+iTS8^~Licu}hP59uo2`%yiC$|i{D9%SySB}u|6q$DCHyMy)q1n7%!zTLw zL1unmW;47^;{V}iy9tAp|Ca}RzvKBWyF|3jj_>am*5rZ~K&QSy`hGWU`H}HzeW*0a zi8g!ZT*eEFYh}I;V-9Pp-*gzE<}kj46wdEO)G!1@Yg!xarh( z10QBK*`yO_nf~($$%Eu4ELIjj=Ya)&dx^f)WfZ(0Y2V&P|0|GmslS-shv%>ewd_Z&C1rZQO_5a_<%^t&KU{@|6Z z-oG=}oXqhSV8LjRNT9cuJqu^%60zMpbJGxad^qd}iDhfD`|qi4Xe^+BiIb(UaI)`_ zv7}wGu0H$1z-;avs}y*H^NE5Hc=YOYw%#(B?GTjU%D8`{D5hWZ zaHCxQyP}tySS?XhK(&1?XhX>8DsQ2v>oyGTpx52)#XlMB2%x3-E^wC^gNieoUm6#z^Va9KQ7K1BVzWaMst)!?hqokEPt{lHN0v={}r=}~0LxMb= zTB~4P?%v*?x6bJBJlbiGcr>(RoG9g8B9#ecBz;iZ}p=Yec!eC^%DGFI2rUdbY-&WP=#3OFUcXm;RG9vC@AhM;>IUy{AFW0dQ8*=&~Za+dcLTJKT%X?$4*n>tF zp~-^=znfgKBgPFv4AI9U*^BzCuiq=X!x2X1v6OV3{KB&&5Sp)7GDa6pIMq|?TFJM_evG0RmmQO;yj3m8u_jRNo|^Jvv6X=;Q-QjenNIK9#cygfzw+TG zJe6)A{|^Eae&>5lcTt(wm?jf^t_I35*_Zd4hX!7{UldIWrF7_%DGoRy){k7Z`2OWVKOor;!Z)CT^vQ6}Hv$`ehrae}A^!o12G>Sr>CSXV%l^nF1NwtH^(r5Dh z`7UR@&>8jS3*_sVR+%(;ByrMDq7!@33m1No(!2SI65_xAWp;~R5{rH`+I<#rn6-R0 zF`hd|N!OnsfQS6CNZ3?jm1njs!iuf_l6^Vtv53)<9kM7SGx(A*d$p6g(-q*E4AO+h zXYGcb;9tIPQRtLT_HCxh@5m;WzgT)LTj>%6t+Ik0bLb>C;sjIjnToyc)9Fa{BU@Y- zde$bWo1pW+HwTOU@(yY1!9|^0&Z-2)G~1CEH#3g@cD9lpO58u*$_q6x4*bG|aHctg z_6CD&Ny}({#q_==@w~e8@Jd!6*YU{(Sinc-jFzjLM>@9!y^6TrU3VeLyaC;iQVLWN z4UG5uuPDQ;ohHoiD*Ce#>Wz4%yCTM#bZq@7wH-s|oHWi#bf|ReA{a;0#eN%CB`M-4 zy09(zomVn#Sxq1|)Azo)*zj0<04r#*Qo_3vp6p=>SvF;F>F-!TwWIO`k9WE40 z&&f^my{>`NAB?@RRss_z3Lrhy+@9HfZcDIb;NPvFsS!kDRe!yNA6ztHRqUaj?JYdD z<5fetYtM`VN_2mZh;SAEmfYYF>`*5(;SR)<*jz))gbt2NG~;A<&R%3J$Ec=$;O-_j zxCw(E3cBxux9`}sQ_DtP572CbZ~t;m8y8qiW9017vwuw;LE>me3f zh5(2DAV;*`WgdbTMd)xNr%QMIf4X!LM<4UG*bCV?PS7>$za!Fh*KDwma%A0Mm z+B?w6tyLncd;DoD-~IO2ef-+O)FA4=EnzP)XI9}<%Sbc3L1ZXx#U;t6#v!S# z)&smG2dz+qfN}wzqMJc7!!+-QvVBYuDoL?wtv23AhJ0C=Tjk%=sE8W}a_=wdAlpqW zYjPu)iZ!#}&ItDYl_6U3{^I-=C9e2sPnC6Fwjc2^EaniSvtCAMB8~j$ahA`x>I9nU z%i^M=_M-)$X1C-C*F<1m%Pdf@{Cvb6?N>G`pk9lYA%b*{A z*Ky~}PpVn$w<1q`3t{leHwyU;4eJ@Dp(w}STx&KmnW|!ApG4b{{Hz3G(3Bo|Lgtt5H@nmkCh@aY^WdH{p**0U0x(i)i@56qgVw|ImIH zw|nX{9mKv%y7G2W4EySPC7n$F{Nn^cZTIBgG75mMdTJgC0y89tyIZClqrvuUH@mNh zCT6+=^LA1)SqKL3OXF5sAGwdlyaGp`ZuS7_1fH;X2bUUq#oma0k2P}eVc4gyMtU=U zu8*ET^8RrPa*n0V23tNeVnv%kot3g~#T)qJ-M!O><01FUtmG&5YL;YNq3T=&bAK=a z`1|B;)Pq9l;agt)8zG8Jt@f=OGa;??w#a|vTAkQf0nRg6X2x3N z@!wZk$Q>z2{BuaJ;&VBq!R&(~e6<=&2Y^F^`tdWzqt zYrGjs#*r`xzHvU-ZY1<f!(&}_fDyjD9u2ZKgt_Ixxm|zmj|Dr@ z83&zmwsFz%HhosL(~RlCLP8@kO3CI1&-XfneA$%87W-y&KLuJ+ykg63N`kiUnWS|mKr86Xhhv=0R_ zvnk}WK2XnPpSAz8!u(jho)KzPX#gQ7y1`^AUf#DL(JM15tK9bn0rzX5)9qQkDVdC` zBS3Dw3lhfhbkzpTNxj>B@&=t55b%ui#y9m>A}Psjtn{1GOyJ-bh0W<$V#e1TiDM7q zemG8NhR{0H{MKB~IeQ3tWH0G2-?z|%j$oe@2RmH+?{QUZz(Dq+U02G=o*86oBa^~^ z(?03FYmk7PeTfwlc-s}*Xp{Sj2~2Bxb8MY{$AxqLXWkkex59PUr?ptC3`i%oAVz}b zT}Du*Uod)j>dneW)_tlw?UrJ5EdFjHU@nKan$$b8pJI4@MSQRU462AQ7qfDm~Pu6VI7w(gmSqA2;V~MP)j9Z3YKb6+m zqLLobyl3?sZ&3suJp_;A-Ty(JA{ESZ;z>Myoh8(WeR2!Dg!w~`KENWwW0ZXziga8l ze+)r+qi3U6;zizqmn6Kk6p%k&T#_bOHuxXek1lQrc_W~3iTSZ=GbYz(=`~o z{$bN&O@WlCADqcR^&&1s6?d9!H-31iyj~LahU&96 zU5D$rWOcFTz>;~QOwsG61r3wEyC2XO2|F?CCLOaLZ9|_RDPd0`ONW%4m7wTp<1<2{T*geATK?os>;yJ-ms;<)7GF zM#hm9CH4|0hm|bO7h(F7)fxhD{Z#I%znFU>YbVtjOpUHd>3Xl=jls!ifok|qCbsk zr%8PN+h?7f=0E>7|FG0vHYe&!&E!$-)X0Otc+7^pOYCh3z}Paeo@ckQQ++jUdLciA zJdOTH&S!ej)$*{94b(I$!F7T=bXJz&s6g8Z2?eny)2lVGK8;9yK?`zN+HO{3KMcl_ zAddx-A6mT-mEPvBuwHEFv7ILS=B6Nrx*>1-oyT-!L@`($d0=kIMN4IxWorHwk1_FI z)1E9rj9unY<4X~Ny8jmLBk9auf;;WFl)=je#zqZcOlaLI(AC2IKxES=}1St#eUtG@k|SFL1>br~BXA*TM(a zl6L~BoHr9DpJ0*BV6Lf<-kyWiH6b5hu@{mTSuG(~*xppO`4MNjXuaui{otQw{2Luj z(0v&km2<1rG9fm;p*Bi9OSRLmFnr9VDvsNT>2dP)#D5$;2f}VfZiK}NqGPFWS9u14 zNzlj(lv5}ChU!ZRb!{yRiWGzDN8OtFipr+y%Z zLh(_TuSCUG;8Z`=Ct2sa{2y@5w#~?QeHOXL%v)-bY$9IuVLU_Sp26C4YIFATeC_;7 z(9f}79~>&wBVW&yDM8pB6HGsz+f9Wa$R{rJ3K#9u%5zhMKi?b}>|`H6g#8;Ol{e){ z=1p>YQh%pkW{m^*m99mjt;EymWIPNaf7ug9;}HMOXzLqTpTS-&Z#LuXVwPy>6Kkc-iu>t!)eiE0xffk26MxV(3?Ab#|X;GExf zbsgumcq8X!P5JcWx^l40g_E!s%epO4LC{nLC45+C@a-$a%d3N}l{Ux<}CX5TFjdxF&qO%A-jK4FrE>uC{?Vwc8udY-jXPbp?(O z!11l#`=zYL(HGSmgdF=YtK*E^L`)FvVzh9-o0MGxuyd!XEgH4aE(boR-+4~gv*^knp5z9R&uMP|Z<+GNot;{NxBH#= zXRkReIlV-!SU#2FzF?wQD=_3q5el|r)9bh?gb`N{nx^X4h`y(6qpLPoMNN%xzPd$p z&bF)wF~s|}`?H7Jd3|?FYw?=7Tr~Kp;-2tWiG8&41G4I*q0Y+vEd6*cirE5Yybt81tFqs?P$XDp1k^jIP^ zAo&eGYt!OHgZ6N8#GS2-jMlIwWry#NTg)nFdxcZh%}Fw6{g^u9b*zBYx7R=uH{G7b zJ>4x;gVC;ZbzYf8M$OFAMsOVV4?z^<@3Yq(p&B;mTjK)WTQNx9uqMbHADcl@Pgqt6M-DkeN-W*zrdrLSKHj16+iWL8JV}oDQ}rYB(cT)7`=?Kv z#39mV40x&TE1n4>H&_?T;udh^+v#YvJd40wKUSVh!k4e;-Cb2Ejiul z<_T}Lw`HuBvvlHS??0tN=fHv(=(@^Oio9b&VK(|@q6+y^{i?j{n+)l~2Ng!nWE4kS z9@7W}o*B&SO#``5#w|8~!N>G4Cxm-9i+{ag=4kk`)g~*CU;evgY}Gqbm7Y5Zzy9qj zvHC_R+JOO!p`SR#5JlIbNuzOevCx-~fR$VtR&ysVMbS4rSMw+QRkQ09bF>d?+TSVp3D$Ek9xVJpQ!d;0KGHBnnj%i zL-0?p&h)XeJLgZW+sl^Ju=(>WVao5_q=Y$=2dAaaLjqLy#&J?wIO;|}Z^050DcRVk zH9ZFJx+%M(X}NWDrQ&6u!5DC2D>gm3IMtWQY)^YTna!L0ywy*{I2<-?7oYNOXp$}y zL;Aq2CWj;5GW5xH%iy9xxA!59bQ1<%OSj)=lzD!#3j&3xvS|6+3e|hHK6<)~d(Qug z*exjm(m4{$xzxX*6=}S0?!GYFIZyr`yoZ#~kXKW^sQz$Kp0f2h#}P6S(@8IxqI?r? z^;GmLDpIqtE^TK!bKDyF$!oV3RBQw$5SWa2g0G_=^6h5BM=$Pub`t*TJK#b;6}YAT zPDdcxwr2z4-Y!mb=JVZ7925UP2shi~$Bl2(NL*w%e0s=NLs-5z`1n78+l@cB43||2 zvC#P@_lz{6=*!%02C_SDHM-=0Q=~6elsvl$=o3AM@xd@|ycLe8JBX<+VGr}fSJc>u zVV}jkH*6_hksR`G>2*!$BB4K$YIuJ$_ZE@2DE2$8k}<0 zuclnI$XJnFG$5yK6t)A~`$)KM-%5P2ij%Cr>%{(mg7Do$G;N_p=g~t>5DvR{vL9al zsH1bNbl+TEi&J{5PeDtksz^J;{JW_Kl~rP|saMM}KuiW3^~4l@P6g$mF6uq`}H zC#PhhA-@-SC~Wm+bf2e|gK6p7h2K}aj>r?BRnZz*Zf4!D}xlfNZi_ z`yYgsDbGPOBk;XXG}9WsMx`x#rVmU5HP+yBk)7u?xnCFU*=Z+FSc*KDWzae$1BNQB zG%23c>YI%JgFwiWuND<)nU;#9EZx97FdC%h+AwmSma9e2kU z?U)Q8GNv#~Js?Q7?$4Yo-I!PSmC0fBoTVW-S^S-T;j=n6mYj|d1+(_^$)TQ))|iet zgTDT%p8b!+)E~hh?IRBUG^e}8yU7{rK+IY-h5%l32H4;8cfb@G1&4Yyi|zUEhj*p< zGSIIJt|Uv$DE0hqvc8#;0!|@PQxY5F&c&2Jjg1{JUbFEe?2f~iA8W1Abs=US?L$fa|S{JYgaVo*qYDEzO)XCVcSbrXe;K`1?&K8c9_H4{~P zv|r{km#st#Z030(*__#(8NABIGC~Udw6!ynzr;ggeXo`k(jvy8j-EY@SPJFkxHr`y z>3c%{x3pvS47c`s@yT}@C047tTa6yO3Q}QHXgOVCbzBY##^|d&Y1I0)E&FK5`^2{3 zLTc`mMCDbErAfH71zSW!{(I=K@9}w5Bj&o*$9ZFw?`gQRyvm|0BIamL`E%CAz_<@-}RpnC4?3*w@KhwJ=>llX=`6OXk*VT^$JuN7ZWIRgV`QuvN!(lZ=e~<-f0<$-+j@mCLq_t24RXOQYD5c2-oA{YH)?Qc~3}EH+6?Oc}QQ#mtIt!_QT?or>6{>VYG4TrxP2~Bvxpg z-;y{eG*82=ZV2C~jS`aVWF1VYaI&+d_VIu%*|xsRJVCm-zLOqb*kZG8A(>F1>_l>5 zO)i$!s;NH8f2OlLnO&)$N>ip#6Pg&#a!k}Kvn z4q&x<_{3nWB}{#iOysTjlrDjS(De_aoq~9aE)1c+UDSrAe%4#l@y~y!2W(&xJ7M+~ zH_%}x+tz^V6AtsDyfrwr>g#?nEOyWZ>x^yucQ)DOSHq=~ufF-9Q}2bXN52IIsM}pP zb;t^+KTvih-oea$I#ny%&k9K0B^zr~OObm6*JZLe0u7Q(IQn1BuP*AA8_wPPnnfPd z9VflOv^*MF>Cc=TOYQyMLjo`NB>{IT+9NM)XiZ6LVg0>MWv+c5BLeW;TAJ zQF^W07Q_(3P%3*vTt^puT!%QOX2zV#FpK~CPD`v#?S6H**pc|Qu<18dRq-*=aflu! zq(6zymG+U>rk0KYmO9o?^Cj=F++o5k9{k--H{2}4U0jHXTw=sTi zD0372!EG84jAVS6r~g4VNn)xy>`@PF3tl4c_#PMPO6$;Yenx*bFSx!0!OW~VBgj|g z4Jm?C__0JqpROYtUeZ7pY&aCjnjGcu63(M zcHBrqsix9c?cf?;IBVV2MY#|C~zZFJ$0xZI5=88EwKAb z_cXY<9bXA>TP&{l2`+`g#|}yQ+KN}TQ1a#|_Q@RKEEe-6uKL=p{&#Ah392EMdoW@J zAt#P|j%VKk$-T;tPB~2BcYK?|lD@>GgJ>?ffFW3Y`9KIEv*hOn@_sfCO`b{Osnl=7 zKa}7@G6X%ngAk94eCfG{gK9}RW7JT~Oi}0{+lkUjEgv*mJZb&AH$C*vB>ooinkKbY z1nHVR_Bt7m5^`drtc2)%DXOlnGw4P)=7q5X^890Gh6}gfkpN;_H!>VzT54d=cKiR+ zfW&dG=Dsy=E`I3qPf)FaRHifQb5lkUN9-edkItVzi`*Rfp*BfheF&0ufDekg=q!kM z^7&7p(tv;jS>Mf9EiKt11f!1#%gdxr6RA{y$6fL#{XXL>fA-1qD7@$sI#jG|m|&>y zVai;nj4Of3L6g7Oh?k6(kBgLKgBUa9_0_=9gu*e?MIpJFT=Ky{FnvdTW7Kh#N<(74 zDmtjY;*GofS3iJ-+Fc14*~bVTtq%}CFc7pkty1Y?=jHVdH|9a_?g0{8r}yIWIL6D^ zxJ7`6CF4x<-$%B2>T_exV0;=J&U*cfVrvH|8bFPDSzi~`_6i5+RdK7Z;7=a zZ~djiE9vNt{C>fC#85VD88xjf={4{ZdFo-x~s&9$+C%=t^o>cad}Vid#C23s^KjKo#W z^KT42w6ZjKbe(!_a|k^cEY{hS@?xDgDzExeKU3S=qTH^PTto`_yN3!5(8axwIzT8; z;BwU3X600Bam|KxA@##l`FeT`mSdxk9x@v1)OFws)5FB{9miaV9f&RAe10zl-b(PR zZDclGI!yPuPk>?ML(0aKLY&v|0+`{r!nvyBz!%89rNIu}yLoh+>iEuW1P`6!oHzGF z^g|{{(Umf;{uM?mdtdMS1;bm`*Bzp8bs@D6xOmQT_Z55S+n2^ZJP3N|$@(xgvr}7n z?EHWNZT$KROw#Y)7noIL7gnuKvTnzwkA@Mdx_-|Lsff46EG(cB$8^6g%f6xHALz&Q zXO|`-ANvR_$x6=jMfm#hrOw)0SVnS&wbp~xI;J3o7ihv1#Qu?8Wp>Hx9hYTwzVdyR zn(>e`^r5cyu?u+fJnfGx1*oqYpD0*O9H1p~Q$AGMyU2$EWY98D8aIrBvENNXA$&D9 zf6XmOFnBMCr085JZ;{`9AyE}ze_tfXfuQMMg`%Bn)KXUgs#=vFFNz7?x7;3q{?Q!(SqnzQr5gd0Q7qog zH3!a)WkkucJZ*PhQ=utBHCuBtpSL8-fC$?iGf^a72jmYZSL*Wn? zIOOm3i3jSSDGlyzHbnN3=HK8|2Tb2x;XQP6{or{X=U&qU)^PMduy06YUPy-c+&qV% z{5hBiYw{3AGgXOr}su9kl0SOiEMb;VnTl^s2H(*mtI|NMjyLiJ=^9AD{h~}F-JU5DYgFpfecvk{}*8eJB zqeMHK=Fcdf%37Z&56P7jq7_B|JlsNpSy*!Y)LF5CCt45PUpRHXYY#x9uf2Cv|!S>{sxVAZiDM@d(m2;X3EQ1#x zT0;ADacKQGs)uhMEVYQPJJ<6t+Uq-Gf+-1j6}KvftXrS*)O1pnHr#r*y3HJ* z;Q502_M5VTU)FT`l*rjIm1s!!kxukRiJ7%$_tst+gtLNL5LE zUX5@k1CnFB!|YOJqp^J!)m@*tDXs2WGy1i~QR?Uc3%>OkRVDrfwAdDvX<5(#aZcT|6cv=dNxPMppIdTNan>D5i)lywQ(OOFLPJy`ZGBlSDpXjuG~ z_F6vq1Gq_ha~3OH=COYF%pYA`+46@}8!PuR(I_TPYluD2BgW4tFgp3jR8E{{ zNuA%~nHTA;cpDZim0{(3u)GYuSznxCp~I41bNg(~n?Ep`>s{e`9oD~&+%6~EH^c}K z5X>T5Jml=kJZ?!iR-c_?f^~Mz(-_?_E7;(JE9Am#Bt0;YhpJ+R+U9ObEg66L6C$;*01byw?V(vAr~FL>=A}T7S{SFi$Hv zDjsWc3Svkh*6Bk)=hSu2Da(DqxGToCHpG5dU@m%YZO_`!-ZUq7D-A#+Mi4aenl4Du zI7J`16GZ+b0%>ua6(oT-GH7$mF}m{Dld`|BOZjld4I*5xbmH^x)laWKOVG=8%<&G& zadY_li*!;R3#Rg4T@1cz*L0OQgt=zHz$+XcB)by!jQrkozzW_*(-9WO(dN?&&iKD*Qms|_u6LYkoB+nUjj^x^wAYGODe0Qtg8)joXP&xv!oiWA7ZFcpmhy^Mn1KUV$o* z4nrN0wA);=urU0FY(TJhQJ|zo8THmU8OW$8HP185AqSU@<^A7nBUD<{@s$Y|j=XOf zS)1wHW<$Y-J0TZ>6&_F!Xc9F!FiLdhv0P&O*zI?a>&5XAAg%Xi1K}KvKGYvsH3dSs zqRg(lKz=ZfdBn-2-R=`-3$dbIm~U-4T8ZA1@@^2h0oMy8&Y`K&lF`&cPaeVmpLg1P=`b zb9W-gSR%rZa{jme0L(RMLoqp@5|?USW`e7(?l`deYPbT@9U@b5qxDJm;l~A*N*rNW zSTEyESgaR2!-(W~J}gNhAtvW-?2yTk#de%j9Caldc$}neJA-36H|*F;WNBwzQ>?)l zkA6Kn_Mv`Zc@gh|$9F=Y*|@wwJ@a0AJ&?z$Ip=DhLo90bSZ&AH$?dbD!1c?Ce%8tx z{<**~^%s3jR_cd3#;JEu+S^j62a&I;NWe!cc;EK#_ky-Y8~@x;zk=T*?Cr+Y{Y&zv z1N+JG5pen8kbV#+zFp8S1ytW@`wV5q@M=31Uju$RwPZ$;>GnUs>FBdbS=Jo1Y5ZdL z2j<{R2N8N5MD!H{EVK`b)qMdCd|FBkZ} zH+arZ_}3^c?Q^y3;SO5uFq20-Vb;3Azy1cFi>!dZfl6^$Ch4e%iA)_A3i`0*zl}SRk|w-h(Uq`&?QO z+vXF7-Gw{W9JK6TB03O!8|AJP-0h;R0iB&*3y zk*G-JFb-U?bF0m|dQsmp{17MYw#*xDT#V?;e5vn)`0XxrHANgg$>t<6I;^O!Z)%7KM|X zy4MyIkdPEj_!d&xjSa|1)mE6`Ty^WUTs9i&jQ6u6?)+P4{Z1a*%BrLN_J1X5#OK+$ z(w9;RHC;ueI&N%_e>5eM+p#wDk8TqjT!Sw?Ul5~~C{Ry-&%S)z9)99#L#8qcYkOjW zAo3gOnLy=kSP=9oaN-CZ-1mh81pL>kE4Z~NKejvQ@xuJ|q0B?56M(Q5g=~BZ@7Kd5 zim7?9VT{bwh)ZvT9$L(BOEyjuWDunyr_g08kU%07PFU#7RCP|>=%~-e_@f>p7xP>1 zhkWmJ#@~@n5PO6vv6amAF%m=!E3_7pXPZXOENi;+oTS<$Q?9TvPKb3HmxD6p!0VB2 za-jx6nUhVVc^y_G6KRu8FJqD5(Y)$t=hhn9%S1xp_8s{BKzAK#^)S-=+rD&MfW>J? zY|}U1x@As+(N_-|{#sM0?;D2x5(O3;>@_U{jrCIYYyt znRZ<(dWuq^H#4k> zxwet9UwSYyz}Rt;Lbqb^EQoG)n2-}S;`yYOtQcJ@?>vJ)d!>4+L@KU6Ya9N;sp zbzewr&#Rn0W5$EQ%XOnE;a}?}69-M#a|S~j%fx@OfYUsu525rt)E`>W@w_xkw`VNy z3n|FuI#Z9^KTIQWX+;B>D&w#Yy~)Arw|k(UiaMr4xLiCaU8U()jjyAvyqOh*_d~9I zqK6WjVr0WewSDp?uj@m~>2DK{soq0D!ak)UNLh>h4?^YTEH8#MOw$shs}<~=V1KI0 zoM`dx=@CkU4SXS{^?=|gz$K_4@20<;K5PsqOW%IsD!|7U5jFuRZSdnsA+8~@inLW6 zW17*+{c+-f>-KhHh+aZsdGY*NXrFKTfOI||a(v>mkDGk|7mZzwdrTJX+KNK&p+A-v z;f*(FI_{Bm`do8>reA?x)T;K$4u^Wl2^qzx5>MDH8RSfhq}Ti1)70fOm)nG&cMIRx zSD=xwYRDVsB}nts+NCmbd5B5<3q6gttaWl8nDJBC)~k%7i`baeSynRaIhz4k4&SKe zVtvvyD}Q)J+gt7xvv8Tiaj)6ZM5 z=havctbmozn0_I}(mCGPLf&jQzwqckNSESsC3c-2jXxpe&Xj&!qymRiwqqhe4lCMw981hjM0^o?(VwzajhHP3wd z7s7W?Ae6MTg?YUtUa^*&B0i4kqW2BZnszz7+brTV@yY6q!ZR70&jLZx95QVGqsO`1 zus;*u-p#R=K+rqzwY8-7qzOOxXPsB*F4lc-V;if)28-(m**L!a$j2w}Ji99E)rpco zhO%qqZELBW-d9%fzT8Lu90<_D?19QHU2#jSt8Gu_#Z8TuiC)nCAhaAN;N&NTu(72l zgDct}ApGleHPjNCI263o(jxZVbQF|R*}i6SeQ%$*RrUzP6etkv*A_XEXkt3rqU${` zXo|YT_%3Zq6mySeY`-(<7O4+EWCH{fUIAzBfi*XqEuWKfu9W)bTrozpo5No79hE>> zWgNeOcKjP7=T%4{9M?7+ClDzA>DM*Ww;Y4CnY#fAwlFybW0bUg!3P55Raq#!lkq%4 zW%`ex{Xjn3y4&Z5SMv-$ul#5_SUZ}O3u&*vqVwIr&d(u@6b=>*Wy}2YCs7p&BdrWZ(bQy4Onl<1}SOJXW2R zBrm!<(12`w5GK<0ra<&$(RMlNm)m3`#P4FLJ0`1bK91qc>OebPcvQB42>Au@26ZaEd$Lf+-SM8~D79QUzii_Do168m2ujlF; zXR1+}>O}&3jUR4t)9#9kC`x&ULpN+TD5ePSZ4A%aH5d!d*O`8QK|1{*3KrCIHVWh> zXOq9Duf+T+!7*!eGHha%`wv2#Fb)S5ajg=u~$CzdsY z&{5EQprQwC%R}Y~KxnbzQoxjJPA{i&w|kTIxYv4WXr!le<>=My?Zd(FO`e2_Pi$q>lCN)OSw}%yig7 z>1Y?W?=c_rp!bwWhJY`LyeEX?P4ff;~lpIozGv*9^)x1*a30%3|WqNiqs<)&T9lNp(zqI!&M<5?67okUnXl%G4cGS({ZU_Ru<0#=)VoO{O7G6%Rq2- z8CTMXHzTWS)IeT2_00(;iAyB0h!ds%cyn~hn5^ z$11@Lahj;{dika-ggr&JOjU{`XgJBWmUWv-0W|3tDv4^jH<)O)k^Gm70IKsvXdku# zWo_Ea)4z>hp4{fYvEFxlV@YN6?(8x1A7@uVv(;2G+5D$Rdlv!F;U6ALp;8^TTUVxZ zyLk0)DirqyOP2)%xi5$MGl!W8w=%#T0}hITlkV)a)Xf+j82wDU>toQbCM9hF$>ld4 zmJ3{Do_GlP+q^lK=^5m|-|M=r(UKy~sQ=jMhVjsPP@H9YC^5a;2nbE^(8m0z!8EK&A`42K?C36o^&S`x#MN*B2<2aXuiV^aIN!1t8ETq{8Wvj=le2NY) z;iDVBnVE*vz|lE-X3A<(kJT#8XpTW#PX|;iQ_^WE?qtgno=QWgA)%u_UspUv0Ml?8 z(#x`zf&y0onv&N(^GM%-@vhnG#m6$4Fs^Uh-4m*cZJ?VXP~)@OW=>p<+>?t}g6#c= zok&_MM+A+Ff`t+>bsiH5Gb<~h%{D>*pUK+;p77tcNH0@OEk{da_1bsU8W$gRTSs^4 zz)`CnGll1g_V1{c`^833m4xPa4qY0h}-e+qcH#87L;|959Ft1=?dG zVRVwraw;A~L%G>r**TSfY`SYh;>d^V{+ZtiY^zeHxW@*~v%p<`Q^MI;Lr6-^?|z9h z3!fe*K_;B(yF7I#1i;)F=|L8?0e z!yyWpn3Vn$DqoeeRO`~g^JPs(VbrY6WPX|$Vn~&~VjpT&-U6Z*Wl?`Z8(UkP;buen zGXw(A$G;8qA91L|O=nu=Cz<(fgo5MzC$?ZPm;<{)@@zJEAgiM3`D2#r-veLRqmeyA z>MN`2x=?!P1*``|x*q7ouNmfT<*)o0HQAs^rQn}{!1==F!QNVhD?kYfU1@c8<^SYu z3%|R_U13L(ybX94sUW&RHz!UmX7Lukl=M(pwEQ^4cX(iua#0OYsW5X$NV${M`{4UI zIB0i_Y^&UG?!QziRGNo=lxLc@_7+lS;iYL>W?f6O(7qqf!3E_#bT61GiFd}vUKj1~ zMjFnih*Dyaj%2dQyi(l@WV z3HD#LdFK&*iw>qptMp4~xVP~cwgwjCc%UR8_fNI3HyaXOJ+>QAosw^`HPlQ@+~VFR zm4Kf4^sgsnAID2ls*WBl^>W55)2pze3Ab>$XXO9O|GKr+O>gcSvzh<7cum5!e5tL( z?F-~>z^&OcFg(t9u19g)o~ja(BoE>sZcz>NYqe#&w};Dy9Da&MzQA-Qb&f|BFa1Tt z5_0nML2{Ela8o4tzQ@Xq%~OBc9XbZ0*VfjULOo6j#c0HWZpwkJ4Dl~PPRDnRY))dB zR{qPRYsS5?k8*=n3kQA4GN6qN#uNOLgK#>S-gkX~24{u5lA=C@KUJaxZbxiF2j$L#Rnll^;rAO=ynt=yzR}> zvk^xhkrqdxpBfqX5{+8|I*QWpeSeV276dIlNOxRgsmBuPJ@Eq8lkDAnLurLQuCS*x z8a&X3w^RVM+jLzFc~p;e4f`W#t)DV6D@pm(xOGRDJ}dP@3kfrs`UG@K@flE|qF_XO z{!&r58*_CC^}fC%@&Ic0BYpzs&9V&2=#LIlBX1QGoV9-}LgLs8aK4x787OAPBDmzvoEzT5a&Wo-MpNk_Wik0C=13k4s0feqq^6x!lS z958H~N1<=Rlj*7p-i%LDxnFGE%@&P_Y2mA<6zQk1f?StED(=A$Mz4S%kg0PnFS$`a zpY3p%y$~soTJh{%Z_Dyxw7W;X2(%Ov?0lTk#emtFPn0x1=>6h!`ALUR+|S`Nn_gXs zJ!>(u+@XawF_5ujee(65KFhn4ceJ`aGjuZVo_DzWoj_zYT!{dZ7~t-3qp2LlN*$9q z$3=9#5c9IGH9H{4?BPz5%_ELsm6xWaK25+6NZqFxvm}&b(KeC8zV=b_J1)w8U`K$&~4lP`vPAg;FsujQBe^Q(H$_D*!G<}cJ7qeAt52TYqyl-F6mto5>j$f z(lWAp_UzdSm)|QVyLY$j9$5$!e7}gOxTvVOtfYjb?Emp=>nk9=T@-+Rg+io(ZPE~^ zG-Rs@@(vuMDCB=$!2kP#Yy-z92HP&aLqZDJ27yAii9r7wE%?r8@c#f2Y0=#Wbx(@P zT)GWA6eN4%LCV|hDyK^7_gwzPI;?l+evJ4IIr+W&6ppB>sUJP2Z(wL-Y+`D4`pjAD zb2jI%T)lSP@rIML$K89LUfw>w!6Bhx;h2cX*oTke;vXkGNqzp}Wm@{H*BS5Lf5^$r z%m4VPw2VwCr&d%}H8eIgx3qry{-c}T)7#fSFgV0uPEP%vo|*mgm%Y5Qy2e@OZfrsT z=>M7y_~-wc(En#X;KU)@L`0w>u>a-**%l7|hDwWw9@G`veex3Qc96`W6A!k_o=SOJ zQZKHecbT>4&i!9I_R| zceIK^YT-g$b@V5Kh0COB3c8^95xM%lR=^fO-U5Er^=|<>`jCDb#;}XliSE9J?tw%X ze|%0C>>F*Le@9+|fmtFmqG}6B{_Fs=;O#o8O5vU25}9`G-a{8(iWD4u_|MK*--&1l z3Equ}S7>sM`HT0t@%Bl#uI>si$PyuYfyT<@7F_PX#k2cT*;7G=36c5G=7qJrT6G7# zyUye#-ZFp$2^0JCN$_f+n2+4DebI8 zkHn=5D9kND(YI9nu%<^xir)9bYkYO+Y@6)^CUNNg`qc`-JW{E2SZW<5tO9D;lT9kN zbhXrTWt{wysV>-W`6i#C$s4cZ_+GWZeY3iyHcKV<*m<{Uj=|CN;BEry7XCKz(M$t! zyJ=2aM7q&26vR?y3#hkTYb(^72p)8TkNe6W4djLjUqDu<^Lf0zogbr#O=HKd#*JQS zk5QXyTl9u5AmlLPb}V8X+CQY^#W{94g0YQ&ImWXq>Z)x|fCfBs+x?6xLAi)1Rw5*Sb=Q4XxX`PQ_W=! zY!j9YnHr)!ZgAno*^h2qPZPQeD5(T<=*?fA?CXUh{qB`9PWb2tmivpF1<1W2i5>Fi zl`kN}H#dP*;(!<9m$E@0-!Y`+UI|$)rKZ*+)&BKYKER*dw$wbnZ}_iiaze2dD}@`SNu?-FA9$4)%BIiBE*F-54|n0|XYV{c6TX|K&L*f5H9e*9tN zqYuuV$;JIYN|M_t6t{VdrZ;pPslxp`d9t=5EXil8BF4#*5b~7-Hx)bxO^kDY93Fn` z?(<8vBk8W`KSVs%Nbr+=ldS1Sd6?`UEkERI`k2eb+PR4WEJlUsMuv1?XcL8Gnuz{V z{FqEdMOo_fOh*I*RrUD1jV>|38`^+Jk3;>&5Ui!f;k_3eFZo;#D>pKX^k$mXghIzD z07ia6h9w9@`M`H^t?c4lk=@fuhV_TD%{>k}q6z_AIR!E=CU3Jb~!D=mAXSilPwU&dtP z|5zgI-*f%yo%gG(YcmPv9k8tG3ib{G`9hE> z6dl75ov9UuQ!ROKG5~=L*NZjXU!Sd%Vf`g-XiC69Tkfk<>*qiNC3`8g9kfmR!GLD~ z_Bq?Wc^!B7S5XnJ4b8D%&{>ULvfMBK$mLpvU!j99(L-VjQ0}LOtwM(R8temK*pEhQ zzf)?|><$*geSy^|9Yt57%#icV(JkOzW{u}p_5%3b&09c%Gt@29&H#PRrG#?_Hp!1NR1@Ji9m{ke?a5AAhghFT48c-7VleFoipT zx&DLaSXT3`taWn_iKpfs39Og}#;#|K77CaT!m2FF1By?A#dG|E1B{A^n#e8KA8C;D zi1Osqkl>5aAS$t&V1h_Al*jF15jh55$mFUPG5qgNHVOWw<|M;q^aN)`AXzlu&n-qD z_l6d+hP=Nb7VPBbZw;>P@P^7!fax(i@NM0pd*8-JLZ^%x?=v?)d4X6tG0gm=dX&;7qn>z&c`uriK>vVbdd!3;q ziKQS;nO^|R0{H^+K73C{|B!ar2x2vv%!^jvM)xi6>nlqxs}Br7E#D!Sx*mMXGna~B zHR|hyR?Hay;qe!n{z;;pXA4NX3c266Y3uX$aWao*sf5eIEjP6| zbv2i@+b9$CL%3-^2Up(itfm@vF6IDd-Guux7Lhpgm^I4*=5)J2;xQo=rHZDVNc&?c zcPr)18zw<{*?+_OLS&anXXG_tuQ6$7pp<)I9r3`iI*+I7;Ig^P#2V`7tVZ=AyN-`(5bJabR-? zP=L;(6i(3XeprkCg}v5m2Ern*)l?y$ee}_-@FAI`&Eq?DHhyn(i516?uf~3j{8_MT zr(gJv4fmC{VtvRj?&i=?e@j}1Yfh8e>4z+DXpgzx+s<6f^F}Jln|B)F-Od~$9K&9$ zs;p%Hl5<3O9@+u~`!^K;PRI3?#z=cYz_azm-6Qy>hW`02fW5-W0D8F|f&`MQbWUbw zCR*|A7I2PIHP=TlDNuBUGhs~~yO!s!RK)<56%~eD5zY(+h!*t=p8%)#7pK-uzw}~c zPHuYeOpW=2m|e!g5XcHVF(lE2_RRTOd6qDtz&Q&4&H>2VWLXq42($Nv-ouN_> z6`yC@Gy}oJ(xpcRl4=tzS)m`Dzv?VfR6dU19NQJN1^gvRE+rrj&<$vET;r?@Eru$vt|Pcs)6e5x%i5HVvr*gt_~ra}27*g8Q{WG=siEhA!VX>4(Z`-yH;hqK4g)$OCL4gDchOnlQvjEpGVU1!J%7# zb`N9|F`;-PWD9`1)Lc6IO#gNr2&+1u8P&Nbv`<7dK)$V#;CfwwGo}`NsgVd70d>7^ zdIzwqGpyEJzMHR}J&!0EGjWNsIUqdvCS}6`hTZ~1nto}m+Q(8O3q+ElFD_r1&aQ9} zLuCPxh_4oM(Y8(57JUJCj|6I)zp7yjIEcl!(XI!CQGwj~A zlstNHv6lqz#B-0~o59kigt2Yl7@e?^e4^O)U|(x{tHdUBqYKuviGO^U%!0>i`@dVB ztm%IDA_{(30oZ_X9&_IoIb&iJjm`Vxf2`2@x?nAMsZgg-yZfI1E)BMh|NH?=kzQW@ z?XtK9lvyI~2^)u8eCx~;zwb-PHU}}TW{5lT-Jw$-mqkcBvXx4chQ_h@R!d1X zdaUiBf9+lS41zhVW$d_uecW9q&FMFzu2P~j0SNTg9AG=@j&A|hvGe6OYHPNDYnJ=m z)VmGrJX$XQxqot}5vFsTd}tmE|M*OCf^&;_nP#J|1B`$T z<`&)|N|_rKjC{Q?UhG%W$@)B@2nllZvo{oEj(r`XeFH=Rc3oKFv(D}TuP6LS_;>Az;7=9nQ~7@(T|ohbEYMiw%ay_=iDw1;A0{i_yUHg$ex1#3^b!xIIPE(P%5R$pKx zS!%pI{hrrHiTqrb3jT;yPqa7mEvq$oVO7#laVt(p3)|r9+t|J?C?vKETgBw`uyGa;F-iH04q5GuUusC z?3kU5o%@$RAEwW>Ue||ATH)TSZU32*52a(JS@D;82ilIyeKTq2?*P_lF#ge`nd{|r zZOsgc%w%Uku${YybuA=HKeUYEVteJ`ww*GT%JFQqW{$E1eaOcd^H5i2YBAS3hh+}e}_ESc*+$hTq}_i4Rx`eSMdLY`8x1xTEn$9^ietFJ<4}#)C0NcU#YI@(|+}u|j?0xYGpyUnBU`ukIXgasM zISr0Y=4>p@S<2SfFh&vfzZA2kh^w}9h>iPTnd3&kb2e&<-V?$guGaT-!A9If+0oN4 zCEz8aqb1cU^Z52IG5f~`XwOBnw(lLI5im z%f2sID%sZ$qzO4)gJVvGkEaGg4iS)FpEqTMKdM3H4W8DP{L-K^80QWBVeZ7XI!EjG zpQ$u*zl&h6-AlreA>W6vT6T`i-E)y3>^w6mL~d*Wb6v1n?v;sE+;&!I%kz6uPy7;W z4LYbl?niY&Fr0Pg?&m=>Cj*7az&c3+iAZZ$ax~j%BsD2?!Jh|~iy!Nb+FFc~SX)49 z#kW9bpuUT8wyxK|N)E+d)!gU=i|iI4bwoiq+j&5ux*0JW4}w=x2u-HB7>g|F{*2Ll zaB`KjQz!A~UAloHBRK9FSf>IvI7l*tQR-`0)iJI*fPc3ItfFtfUnOm|rM^7kyrR(sM%XyAp7_jWlw@bt3^W zTp)qHgfxqg7wTm@NXr8?{~(-x%g_!{pD{zI-NOO)t46< z=<(i1`g>4NsQxQyk^QMrfSx5H*}c4Y!4=Q5<6EAaS3XxG^WpsXpo8p?=X}84$TuPL zC_&s*#$Ksa&_+c7>6d5qhCfhbT|WJ1vJO_WO%j`ovaiV){(cLf+Iq7=Ln<#>u$ zytUSp)3*^!@c=ie#YHXyt*qgar8@q`V|@#-|3N@HuwGI|XBK24(42S7{!6GoRzg<{ z3GOebdPnZ7IeI&)ket`(EW&d8p7Z@{Pf`%oqJ-E-(5sOKzWAs(svSk@HC5z+KE$`&EEIx-jnX}GxUdHY~8=I z9vJ|i!}W>W0;EyC9i|e+tBHxw-~bz95!m%Qq6U_J(S6`dDyQ2Y`#cQJ9qZ;n)W|v{G%pbn`3#5`E*F zqr-CgZmlddy&KLWk>K)`Y%k8Jfb%@>CQish{gDUj4Ng!Q@Q&3P$5&xorP@Y4 zid~?h4fvd;j~kcqXl7NLved;67u3*8Os zPzg>ReIf(uwNt!m@q7BuWKilj$G3VNV16aI6m?X(TvP}m^WaOL%J|jzF%rB8e+Ld} zE9Rf27dD+b=diCc6V%7<+)mI$t0ai^JUazyK+VNm_0}hND&D6fKv$JJ&qx}Hx_uvP zL%2u6_DebmTY%@dh}+16fBeZMJMhCg>EO}m*aDRJT6_8*g$BxW3KRFAJqcf|>VnPm zl~ga-6Sn|gf`-eUNAE}1X+tDsFf({_UlggaCp1JT{J$Zio{!#E4p?Z*qEpq-fa{Twa zh)G&dGk~pTc0T>LBifj70(XijImzy*G6{FbcyOiHa&PZ?X+Jrw5%`roNq+$ z^%|wOEmUG^6=mpaj!H+E;KooAKfuJ+H_}P zT}XyZ(8$&e<8j#N z=GDZMd}y1wJA)|w$5Bf4ZGbCs%1?X+)^&$qfRq^;_H~yvH}T8A5W@4SwOqF0tLQ^k zbrxdT>F2qxnJID)W6WF+X~i&gnEH_A5QUKb{zum&p08ViyI%;wb)LYycgXf(tQ;x_ zRfA+xmB(P?T}#SjafTZe_Rvz%3k#iBER|sM$rEYZl*wmSP4w-!w%m4fAz^s~gwU@g#| za_WM0qz(xRY!p7ss4`~|cX`&T1YJgNp-rV!bne)&&{fzI=>W54h1d7H>GtKV6e`#q zzbV<576MttB6et*-`N80R(A5wF9iF&SB@cAG;#t38RSt7`>5eLGn?Bm>Abw|2kVHd-gMKbo zLU65}Uj*vVGOl>8VyB!$TcTmL@Y_b=nG|pvIb%-gd;#f{_QN-mXuyRXJwAZdhzRF2 za@uICDDIjGY{UUZ{ELHDlUcP{%)#UmHpdk;pJlNH90TTYlH7}H0cafqTMaX7D4`Y< zk_&50(d6-RTQixNfa75#I1zWy+QW!T8{lFwX_6fl7uQ?g`Y51m~s1e~+(W=)ahJJB~esv45l9$_;{%Truj1qFru!)81O z*emPFLievo^-6N(u%8ziTs>tCOQqZ#wx;i<#USDy>fYX#;7(^?a)DxN`@6bky033I zPe~o>+&4Vm7nY9lDX-=kq1NUsWo9pg+EbLuM?g|^f|rdt%10PM+SG{%KVEWL3rq{6DBa8E*pCRJ!g%*joRBt^dpQ3k^A$ zce-G=83lOmIf^lw*d}E)r3KeYEAT@NNiXw(gjWyWbWtyK=^0d=N_n zcaJrzCHNjAYGbF2OzxW|>p~!;4#rA)S-@BC!=AeU4#bJ2D++|uAY>&=E@5QMB{O0R z_%lY(#>6dMA+v-Ozv#dKbg0Br5TRe}fy~MLwX!SeGQS>D9hG(VtMvU~@EJCYn~-)c zA$%i?(hD)qU8IH^1!dl%wFRM65I6tHKs=<_1y7U{QSn6w%Gi*nRvNB$DQIwM^&wXp z)6+FrI!=Acbbv9@PHdGj}$x$~c|;N4GMelhLBSt+T<-x!;M4 zD2_0EMU`uz3#5WU3CD;P-VFWfrbnkJ79KzLts1?tV5$6;=VrSF>{ww~`O#)sZu+-H zE0sWsgE$PDTqn$xkq3l*azjP$h&~E#>B0Hsi;qUOw+f%B5>U263+^%;&Q)o0ep=_R zJN!0I6W`{k0^*f+l*pVTQiNL!e~kUZ-?$(t78Pr$oc_^Okmp{>KF%|sMJ3vAZjdB9 z4;=9Coj8k95KM-FL6~@D1Mkrf1(; zM$hj{2JxG^kK2Jt6kNZ%pV{c}2Ji2d_zJ%SYVKYS0<_JoCQD2RAV%qlCON>`w}A4n zVx^^~xlBL5PHKGsHh(IhLKd=lnev~B2`yD1Ua|(&%`Zj9|FPO75*uE4X3K${Xwu-pYLdA zf?l!3+kwoEv&OxNpmhObb;14`@n!lMQyKmm(V8khk6E=}k%g?oo~&K^;Nxgy%-q`^ z&mzI=5kp2zXeR9$PP50A-+4mNW0Vhrf=_$SejaHzKF@e(6`$l@_DvqOqyVgJ0YmQA zFLAPeoll*SEwzq4d=;`0RB+=}TZeF5z_yh2k6&V}7z&os2&nU0pmw0DuAdUIFY)kZ zgeL(xPNhFAzn?pvHM?zeK4{2lzcf+(-HN61B{uNGBTY-_c?Y#jmxZi6`uWVTx_G-b z4Lm32_+U%KDNHa!@FMcshxOwUm|N3FE!3e#6XEgidxUCpM)S0aCnt|R7ffy7L5}am z!((ryw<;rh(6?tU((`yqAE8U;*b+Vn>Ql{KCXM;5Hp=u(zBH3JF;_cJa@Q$6PZ&aE0iecFK_9xC{2w9rFpKfJp!F6JiUUBP5X$ zwrXMU$J2j;HQM9bVdmhEVF)t$dl=(zzF8lrM!?RO^{Qp)tRCQRsDQ}(2qSP1e~L?0 zEs06)|EM`V8Yyt>zYEL^5cLzQLmd6E`Qh8H?ZSen;E~KEVJ75n`#_hjg0w z8c%S$`B7TtOlDlg>{&5yXd*qNfR-dQ0zplnRi%-_b)%c9Ah5FMUE|6%G7KI@>YvPD z{v8+Wr?dod9biAlc9o*tHpd2B6bqhrd4X=j5s*6X9K%Q~IHvW5-_3OAE(^S&pZe?6 ze3bX{z>>%F8tOEeoPPn~7=1-35AdzI{-LhN_W94h_e|RJ?|sl&eS=|K0882|CV>0@AqDrTE zIBD0FiTGr6jUA{FUAiYs1<>d4@}$d}g|S)txdz zeyU~Jx&=5Bj2FbU*Pe%L%cQoT>%OBq0DoW8E12*`!|;kK-t~gu%qwALyS9KrAe;!* zUdxoQD(?(UyKlQo9r`zUyPsVAO^tjfX7|s|kBFIWg6X)}DVj9J_JrZstXyA3mO}rw z!CDh=f4t)^T`Vo@Uhx0@@TB=l9Hm^TgVv6E2?V>r=nIZEha_l}Bve-?zbY!q0hZ@H zn|g*Z=VxzO76ssn7G0-^z(-z{@+MNx}0&Usa0PW1fTBoTX$XTdC;fr?X!EZZJDvHO8HQko9si}gn;=X(Q?smbKQg!SgqgOlajriLxo+5m(ZAz zX@YTaVjXx+Dax8u6#4+XY!?~wfT6R;dkYwdwRs&7zj1g58Ky5usX`jezy*c?Et%Hw^JMu|8!WHg)uU57z0^LYc z2hl8@n>`_I#Ig=_5Cxaqy-@7u2a zYu&k|4z2#Rh9}?ICp~2M8g&G?(s%tvVfLJT?80u^Nt!I=&d`|{<8J#DPIMDVY*hcb zpWL~}PYjWQRS1Uvq<#3_M!Cbb-TX*A)lzE2SKMQ7s$)~sf81r*p7Yv1h6vZba-s4l zbK%xr{a0T*|D`SWxnF4Qiuw&KszXcqwHU0t`lb12CIQ--q1#bp935;MWvoEv8Co0` z^3hizosV_7!4@W4DwW5F1~G{{F`B+HV{<&)1q6HUf84EElmv;+KW9MO;gPik84!$A5VrEDBeHdMm#DDVY-)iCVs?JHw+bJ7AlP?|Dq(X+6JPGjb~US36$&y z)>4u=+V3rQ;qrJxo%A6W4GCNK(zHY%nvlG~c;t50cpJg|ZzDq>h_7Etux@ZW6yI1U z-m`f2J>#1}=7Xv!mqpb^5jI_!tGo(%9UX=LjG=P;djj?678HIgFZX&?RKMF;lT^Cg@U?QfimkYI;Ua{mmnJe5eO4X%6E%@)s1?YLZ#~ zBXV4WKgocS74iiz9(R4+os6rFumCe!z;}|lumsjTC64dAla|& znbnOR)zP5bnFoLEfM6QS!F})d344hljP}q>XH~80-xZ564p>$b1qnFW)&j2K%+=d7 z7xvm7;DGS*s*i(nI7N=~RVbR)G1 z-mUn!-v{Gul_*K_EB+CNJ$yDS(|K&HLGfJb#flpQ&~}Krk)(v|K>mCt+Uf5Ad&+HU zdw=7v?wsD4Lv8Yl#kavM0N8fz+0=8f7uheFpV50yyf%Lw*_z!0G5Si|(NVxnm3UNc z^UHne3Mg%Z$sOExVFv@-^Gws%@NM-_CPTPpObJe} z!CYb`Ld*Pj!X_BA0TE|R$k@mn0y?=eB1?Vw)?H_qIA^JdAWtXDI=lX+Hy7%?%wW%2(Yyl45P}@mr%cx9aMm)#NX17bTT&rMK39R>`ECloPb(&b6I%Y^5`)7i3 z+{N?4^3|7!b@~o~Kg3R>Aq(}NyZ&&#i}b|)Btxb*ALjoK)A??x{EUaf9;o2HpR7I9 zC2sy&yEp7}#NaqoV zke&h|Y!LT7!#LDF(aV;6ywTG>OMewuIJA91ktcs)8M~1?Hzx_;-UvIgm6GdG;#!wI zMSJu*4lX)A1$WDYyONqhUp!M_lNV?G@6nj{WFx^X@TjYBELposk=jMh3`C) z-v0z>71Z+aXs^kmAw5+quhf5l&GhT$Gxp1o=hH^(-rEW(1!L_Z-bMHdb?CSd;LCzQ z=H2YbxRW@;ZCDbBwq+vXs0w{qd6=`?&yWrIGkh=tmrL_39(;t89Td=T4Q8?jt~>!#G6@Akc1Jo!vr>5Q!LXsiB~|(`A1q|Q5M^$! zbD*4#(~{$NdH3=LHsQ_yr&xT}Fk3C@?}|eSg~IbZ0gL=^MJ2e=w^Kf!`7|8;UWi0? zbajaaK8}>+m-nY1Zv+#FW{Gz`fv!X{J`(s{r-V5>$#XuZU={LiA~VyaF6fV?GLfxv zE%P+%!SaQmI^!F(-yc`O7HE1sw|~sSNKdlr8tKpsl1Z>|zsP--)5fSS4R}0dR^5A3=5}bfpO;T_VIBpD zT%#pwFMeLIp_B{h0YP4Y$>c6Ec6T#F*iCQ&q2L~}PMND9_ZpRFT}T1^y|X1H7>tz6 z0W#Bm4FX&XM9m%MRT9a`9JkoX~KrUUT^2;c_0rusBx-+F;q!;p6U z_%ZL=vIAcrlzai%4z6?|j}}9ah;Z|pi@Wt$c-a9OSaeyPGk*?W!$TzzRP<_ae&5Df zsHHC06F!c+1-!+ouzIIUKA$VP<$zw0U%J>zl7(!*+3w6F3cH$V(3wC<_Xzsu+O3Cg z9=}I~T7Mz!oO`82u{%3@<-@Vo-@*mJr_-mr^FrmdjU8B!4tBxp86TZ_j@6Xp*w6rU zK9tNo#5j1>@H*}0|z?wVL&sP#; zNq&}FM_Rk2SnhX=&sB7FWC&jyz7NiGaha1xc~()V%|JAi5x9NM+5VY+d{jZSo!6^Z z0l3Z>F|euq%dOqlBRoiHu9k=@uw9#4C&8_IhEND^q++kTGinxwmGpv*8IyLoGwq)( zlE)=6eoauj^4R9%(hm*!H#Z+x?j>%k@j~ZfPxh0K7KRz{0_5IPWg)G&E<8MdQc!q* zuXX@@u+R#UO@>%qt15Sob%WE3o^?<;s>t5EsNZu#1T&&2ra&m)`SkN0wkFasp4_Ef zxPa>vJ`?R)v*2~XGO@N?{5woRgS%|aC1ny;k} zS`ZE>tB33#fxgv1*oYvm(I%c}wt((;P!l5Y3IL6_V^-H2wTps!tWcOxR`%qO z=9ou&Ns=uX9N<<@t~a}kCnDrR3(w%Aq=@&v5KEr`0;nL3Z;{J46TI01=m#(5M?d{; zl+v^5AZjn~r#Fm&UaAr_e`~es2ICQkrp=$}Y!}XRu-8WUbPzVkuumRfQDg5>%BWP-ofrZb16O)~lYc{y%UAQvPu)jR zx0QV3e@c9ERvo$?bO3GK@art$H0a0$DeLwbQhIcSoIs5{K4^0uOSH9@_xY&Fw$+Th ziVKMyBcM!B-cb?eE^LoWRdEM@yViN!u->Hk0-Ls+(_BA4uib7nPO3MzU%=0KLs#Eh zKMHXbIXaZQ--QUK>vR_0vY+)gWFIbVS#ENf)&^??N>?Hxy?m|`=OsiiKzITlK%J&` zqr8+3O&>YRA+=*Wi`1d%f*QV~_5)V?(EG!FA<(fGE^dhXD2JnM|5@*@_C zF)uvWU+DUN;E>6GLcs)SF zxHktt^>)F2W}oPzeF@pfP2=pVSp+-mpD~ns{J3OJ599fd>eT%mN5Q@hDzauc9jsTc z`IX|E_?&ND5f2|6eocaZ!9s4CL&5OgZ@jAT z*}UFJixUscVKy(SL676xd;u<=DW6nS<5O_+Z+MNp?l&+N#@xH{Ot6$eu#jPZF#K-s z%;WhKbNb_-rXVnv7i8Bd+xg{M{`FeEfU2N^5zaJO$m-HVb7=1DbL88CM(GP14Ng;? z&%xl~Q(XTzxh67zkmkH+BJM(rn2A1Qr95+zCdEI=LYBR^GHA&O4(h0YrGN!?!Zw&e zd~}^dN02dKrcHn!!v7uvPqRyW)4FLI^|PYR+B%HJui4oJ`{Q0ugi*h40fbG9r*aKS zQP(K)@+T}$sJwi!8^06GP-}4Gc{YA^itOue3x=ZK_5av7l}!|YN)mCP>|!$WS$qP8 z_SjG%fbzc0<8bMSn+tU6S?fP0O2@_d>JZ#PTnj>s!OChW%LDJDU?Nv8#cBvLgDWBh z^lWwLOot3dlI@xE!4Id(4P-{^xzKCg9f%PgO7}LMh}jN9oCnw+g}brmxzUV26dfr( zs%L>r(+{Xl>-quvPpC3OAcQ@sb2JgG4kJ;cFVUgwVc}plWF_)eml!M&sbR1fpKF&l z(rD1rHyjW*5=>kHm$DC03UAiZW5hSSGB6ulJlRtW&$dT-Bj1B?WeZ^HB;w?_c1#zP z)Zg@NTGmZz6Bo>dKa7n@@P~XY=BQU|c!sd}e(QYRWs$LPku3o1NX5zgbDi-ry=9O#k8bd2d62U^&w(L_73$rr&N;@QxI79I%_nGCM;@_l)_`TUKs< zi{R;29@bSJ{03fQ>Z{TE&@tgI?tEj|MUabGV6W*z+PRa>Rd1a2T%4>eT0X|Wh6=fz zwSXA||3phWWZ{(GE#2JuxJmw|gP413r();ri9~%zp8n*C06b9%@-TU<|W-IM|ym<&4h zUWg7_3yluc+Kb@wOaFR9FGpM5sH-iLNBQyO?rVO|hlJFII87+(td{w46@XO@bX4fV zq<5du^>pjEt)NV^$T#pQ7~uM7Z~RFve*ijZFCeoSo@HUf0US0b+3g&5MN@k4Z1A;F~E^t0YN9^zOZPmcOX( z20Ps`K#etGl6oU2b#LqI+jDV?f=EE^wEHH1IETsd*Tc#{SJm zO;h!GE*qr}zCQ(T&V92_LhU!eCynV>*42h3UVQds!0AEL(Sc}h=u-mPJ4a&rOj#h0 z4516Mv==Pk-6HJic5o~Nf@(6`m8GG@+;Ih!Ix=VBm*ftbhe9H7YwX$ogc2!kzJOxpnsvW8rgI=ry!$r!u zLj;q+$A*N#A@AeP_}1@Rz*#ccA|Lu$kVby3k48CnFFAfQ?BFHejQa0VCAIZgk-k8q zA$vE~D@nAXvNC3GlI+o21z_V|0152)*FK^w?Ot-cLVqF?9-*9_Qg$ln<%0p2nGpaE zhD%ep#LKL}#}s^mvMx6kgEiY#H9io=sk)+X_t7>0|7%Seb~#!v1@s(Ss0LoA)tKEap9|diZVy@I^|GGje)m>0-cc|*ce7QpNiKI%U^GAW ziUiN5c^>`HUuT9{YR;(io9gz@7}9ubYU!ow5X3bC>9*Be_I~OYI&RoYWM-isF{HMR zSZ*L7y#+bglLqO8(DZ1l1*7y6iD81DCt%>UAk@T5+z$6WLVDsSyXqgmK5Oe#ERG+8 z3w9AuHNEv*V8=%R%s^8t|?n4dNZ&$okuM=<#acf%9OMTxc>N&wQ7_CTSx;t~oyAW-F$tKe0y)n| z7HZ;pCSy)XcX7zfWE!s}l65bYfGi=OTaDVj;PBZuR>T38=I5pwqobgC;qbqc>ug2)EVnU>lpiEiZ>d* zPpzsFkk=y8IBfg%;YA97%fn*3f3xi83nuV8H>BFDE@lQEW)V03P9oaD9AsCwesB@y zZR01Al(r0@OF_Dl{fzSN^!WlwcGH_K*nH=_71%K>k&@A${_bU@hHF*gpIHML;Po)8 zV5O;WXgkWdbuk$fPjGOf@IH1M?-g20yXFnP6ZDyhd%2&YvQSr_{8;~9+Pe7!;7+y* z4^=;R?lZKX3GO2pqP#lp1t=^8vN?OxE_-HzRIQC0(172Kc_Mk-POEROCSnz`8O%fQ zb!=}aHidur;!XcfS&96#t`AvKHVicUr3SgHTYlP$+uuz<$_hTNNh0bu#onsEkJuj> zSE7Az5_tiP>Nzfmv$Mxrj^57k@!2049kXGXyx|AB9VlCkvXJXw15i>JEDnev3d9J= zCaw~z)MWs@dwa(2Rw42hc&A{~473Twtr&sOuj!|BUY2@8>rp=TLkdx5=V&D}89y5a z`RV_FK#?2VJR~ffEIbvp!OWi2!_E8`x9P)|SjR5ru{EpOvRKft(x0k75pk3-_4oHQ`++fkn`*0$V-5`F?U`X}*Cfl#b_?{8L74#UsRt=U9o zurZ!&@zWag@(>ER79tFHXfDm!s8P$_fh8>A#fB9d4Qpxj#+#V@T;j@sDNQbDD~Swq z+umYI8}Jf-ev=v+XQ<29if|o=k(XQcgq5KIwDEz}r>h{YJHX9GYK~#rF4FnI@BG}o z={duJ3eG9x+;e-uaN$QrV1gf$xc|m1$``HI(4mU_mZa4K54Kb=T&`yp)SevzQRDKe zwJ*T)5{l0;)>uCuOzQ0Z9waWV@Ns2gn7lK)?ggX~8_2%+Y|^i-EiqAtyCUMOr~;a= z7*^;$&*V9W_R*sM$Xo2hQml4ijoD84j>cSPr^DCN;v}mc^oLzA^n~3hEgf6dvd{XV(S1R_AD|%q7iKFx@nZ*RZu3jlX_uFUoR{?7 z9-vCCz?if9#!-K9WXMWqi@l=D$@~jxYN4!3=zMesa38sseJ?GM5#-y((IJmM53UwQ zn}Ja|emJ)_f*GaJd-+UOv0-^!XK^J7egqyB{oZIO;OEa*t+jw>UR+UM%i@Z?*gz<1 zcGM2IGxAj0Cn(3&*%>NK6$Nh4KReu-HaCGhQOz;2yqgB3zRw>GDMWu_8 z+;ZFKCP|djg}GD`!|Vtd#*`4kDU~9ZaxJ-=xi9x3cZSV<<$l>*X6(}M-S1zI+U2wN z<@I{rUUTF5TWm7bvCi%yyrZRFMsj?ql$oeRDmf}|AQgUrbWoAu-_UD8s*$9Z}K z7&YV0oMMakMRAS57{ms?5wh*ZAM0~}cEji}89he?meUzqliy|tI@%Ahn7-nex?^Xv z%JtV#LTmQ2CG0_IH2HYUALz_kAoogw&IsuvnCEE;y6~6X!G|?2I39@mnYzLN7S;F4}Xm zZuIh{vj&G|-ae+_4*Sv#_4Ci%b$B&vmU8ASbtlx*twR3a>YWnnPwHaj#PQ#x zRvT#10EyRr!{S-JgK?@aEx9baz_Ph}vf;bt6i!gz8FSPM-YJ%q&%11)hHKV-aeXVb z?u%F+T?aTWyt(EuN|fuIj%oG6bCS+6&o<)$6ummviZK@*O9_91c9sd2M%%B7L`&+_ zFTuEYyKb_EmwVaD0^V>MMtAoR_N8*8u0IR>a6rQY2b$dW=|{G14qM2>_c@Eq1^-Ab z3VjL5=ET%TLUZ2lJn%tBxsL;F=x{QDbWsQGR%&1?#f0?$3C?JJ?Cv_$so7U<(iG|4 z^q(GN`B5K?90If`>kYX3%Ok6FC*f%PHivn#5t!@fj>7W9)Ix)2&0IZ17yGfaiCoHRx?L{#G$VeE)b0*WSF43lOqO{|92Ue8YYI+`)({jaTf_b4r4O`R~n?tcPg%_ zme!%0=PnMTT2ur{N0g5TdqYfRS}+>JS7sH}*ZN<2pGT-8bZodc-n{)Vzy#{J5hsO93S+@D)^MuJS zMbimaL$S&fdmDWa_cis|W&f~8QwZ*FDA11$Ydk{@RqNg_ zS&HtpFhUpzJEY^g2*sB1mXxJBOhRi=*z86kNUsK+bED?S}v!BMH=tD!>k>MqlAX($)m`?at^r zK5an^A20!H6R4T-8D-_$jRADCA2J>f(D0kkC*0J5pO#38hDgR#(Ojo);s3Z-=z%ql zX8t?nnE4pED%E0x_EGw)jESu-wwDS`-c_unio-(ymH3e*UvEmRdtB3KV_5>F+^~ZlKEB+ql(V_j!95E;LSstCeu$ zJ(!bOH{3TgMbNeA zZNmiK#KX|ci?}0ft)KV*O*;4BA<$t0hXhXlddm2Pb+_-!HSVMyB_l*K>VP`7pSG@J z`a6FK{W}TrbtsOn|6Fr~@BZ9cB^D=naNj!Upy~1!*DU_Wm1;F9iiUO`7V8AG?u2Z1 z$y&ngGmTyx4+BtXAdkap`3R;8AN{sp5csXKK^s`ze^U1v_~X~y7S0?$T6nNy@i^?K zR!K=wPw?{wzt?_L;QCY@oXs6y67R^ezPO{LD(d}DgN+iARo{6pmPZGsf)c-|J?@tOWmjS2b!1Pi~Yyr5Ge;N>?XAsg`<5$E9R;9 zT6T9vIu16!nVk9oTIjufX^c}@fKv@6=a_0sW#Sbd|T-Bn-q9Q~V0hKNdR=cW|*@~5ry z7=zI@ns8Ob%%mIDzb*8c&hEgptU>T?S4o^(EDOG3klba1cf~5Z(a34VIzj; zZi0>@Unob|!l8cslP%KA#GZR~ul$UI!V zL}_WfvOGu>pgQ5h=%Neq%-H>IHCn)sX^Zt5y!78tGd}dOa!Rv&Bjip5iqoffzD`jo zds}?d}Mo99>B?m z>V0!RGO%g$0w7XNwV^ERcN!;cb>Z~;-txCE{*0}=!wh&5dKN6d_iJ$J*yjqzyCf|y zn&o0{%U+xw+tn-5=la9x3(<}>74CgQ<*k043(dYh&In;o;5k&WOhQ$*RS#F`d=|gF zpmx4f(ntXi&@pu1aK}Cm57pPMK=z>!F|ssZDf+=-Hb|yFcUzoTN9l{Tl9@=TFpIda zqOezG$yaQwlsa8{yR>R2G{(oV>hJU!SX5pwt}2mAYSslE3=%zNINZ+kajCZ0l+J}* z=)@g;9^@p&5m~d-rRRkPMV`KMpn^+KuEmIab-u%zAT_6oiYf-HVS&rNKTO#zp!Jmc z=fJFBoVqXyyq>1uUGAr%N}evm%L_5+>zw)rQVc#ZN8RP2Ii7*J64V-#u5f&d`Suoi z>#eGZ0Zd?y0lY&~BwzxF;W9GPfa{<+?_&SpaabgaoYY3Q3w-)Bca&5LG;fr8*n*AD zU>2EYUftLXThJ7lK^w@K=V7tWMti6>=hi8DshtNKUHmNfY-(aV4U zdRx;gGA(m6oWa-14oo;h$v+ea#E#pO8aVO7!`+HcPatz|FxQiBD+3)T(4nn%Wh8<( zKY8cVTZbdS<&b_X{G-k2G$nX+W1zZN(G?mfWTor*)+R34%RcqcsvIpA^8^7q)GUIt z30tR8Hc)SZ3GU6Oh+;uzuw-+*^l5H$eT8-wUjqoH9l@~?3i;6Hhow|2lk5+L7OgbeA7&dCV{L-odpSMS zTtKaAn|DqzQUS?(YW!VX{bPHbzN+h4HbWR^PboW~JWdJHpqCah#mvtP2r}D$1^x%x zUFt4{soIe(Rg`iH&tPWzBVEu<_vS6AE+Ms6+)9{2v|+Uitc!T~W0Zxst(%e}t8*@Zz*yQAWCRL9~?SAE@Tsln@3|%1E`BwcwK6s0%F06QVBG z(sVw#;c{l3fw=zCb;xXP^KiOtGN5e_FJRlIP#5LO0=* z_^+%4E-=Q!o~ClmQVk1X$gho27sX?t zm0u5T)yvD;W9BuZfN}sxqY}}GtwJX^z4>AjOEFFhVuEBmuJpN@8g9mf*vR3Y!%@%y z>hE}P2*Zhir++XPt{N3xbdUY!3bB*y)f647`RK>NT0yd94*PAsw=BY9lt3C&XGhq5 zAC_pCCBI0_>|YZ>@jj8?sH10wxrsL-ckF!s5K$eKy*v$TB6?+y+`+EL*Z3~EFY=er z;Dc^c+b!(0vYGMs?sP3s>7uvptK&r!gKU*8fR(#6WR_{&deU39~Crw{!dE3&UWhu zfv+S-g3oA)zTzC(B4`?@lnO@Cz-{SAJkd)XKZU#giBL5ixBe(bM$r%WB(oN%UX(g1 z6e30!N1G&m2cQhUvtLZ`X?f%z@!Rr-XER8kqOz_nJ*hueNa@eNzlDdzUQerio*V)c zR-!LF8fg@#1A`T?a<4ff1*?hbN3wwk%Tjf}va*Jvzkz>wYb2con!4@7krs}}e#wh>M`Dj4;@P)iX$&LRkR#%b!b5?MUakS(d#WyOV-m7yXN}5;g8+b16+E-1B# zkC#Uq9u3T_(7)>38u11-{WJu)-Mv&`V0}iJINZA^x_Ok`fxiOr;A7@7+0F$ClXd?? zrr!8;aPx2Z-B)A46*B7`cKz~wC5C+vC7ws>^91Te?jrST*sOSU%E_0@^{E9~cJCV1mTR&zLd-2P5nmh;w_pdYm0 z(=|GT$LrQznt{smBsZFxDiD0l3+084l5;j$VI;C*5>Bz?*y5D$PfP)LGAUvF6X{&Z zOEIn2p6Z+3x$XMp#kT96mZAxS99 zewuoREw@oie>2h7jSdE(z}~3?iOt&*uOY+JZ z!rWUhm47|9z7Jg8&HR14NKq;K;;fPnl;Fj!^IISOf%H$Wqo`M)NhHv$s&TD@P_b9K z?vmq7pVXLTUEOPV8Q@_kT~^!Mw8WbwGFn-sJAg~dQq*q@c(Xnk-dJYc8J2A=x|op> zaTY8Y|NhVnGB+e=rqfNz!3mp%!Tg2`tf0LYaZ{q>4ujj=#H7S}jDW>rKeHrMdLy0Y^)loD9dX%i%S;N3S z)f)ogE$VqCBA;jJj@FFhTSoH-vtjxZdl_^B=338v-s$3}qXK0^2&8N2`7{r@FBr^O zkpIm2OFKnd{R6Yh zdY)Ag0lTbu;E3L?bZI&Fdmu>x&)qeQ&(BRQo4vF;ybdx#RxMx=@fPK;)YSGGgk9Y< zX@g7zN!o>Z7qK$J`PRv*^TLEq(ZJ6(fuBayitW!E=((eDiGc2yPQZ zgw4-QUK;J4xm7^8T|aWLFo8ZX8K5Lc1Au1cJ`v~UTWEU1O_)Me}f)r&?l~W^ClF~(Gaa{Ucy!p9uxNWEJc5pxnpG{b7t;87lk8uX%~86TYGrc zbg3}#D-%e}W{mc5i*B;ovq%#iyR+K z`lquhmr!k?_5i90K_M^*2@=*trqm<1NlV|XKF$?fP1Abq;YXLg32wDdTz7m(zn8M0 z2!8Zjb{vp_qTE@d;dw+awtx5RK;Wx>vtQPe!KjF`;Pg}y*O}2Cbt5?YA6KQU-Y7i8aQAWApdfobwx4gs z{sT32e3{enoyo8tv!1y%6be)?>!k2=J>$#M=a>iGJiLU_M)JvD(PZ3$f%&#Rati}C zMH#FNJPw=8r>~pF1W5YgfgYif>Jz)zxvhhH^jZ`mjBm!w@sY95?0Z zni*c4n>+!O%B=)chmkxb!oW!oH=c9gYO1ZPjc=UF7Q~ty(0(-s0Choxdr4W|;XlxS z_)YvGfCksUfA0#CvL0#FS;{L}LZ1eYbcM-zxtwQUc9hN##I^m`91ElXf(flHYme2H z@N7#_E0(W$BxxUZ!NC4stkIp)DIb9IX}s+zF_by}bUtRQ!;j;z)`z{c?bS6ags7h@ z1_MCfvlLl#v>M#BYF1%1_=4%f1jHHwuqY8~lZjSr^In_KA3nAAWGRymHV^8 zvbt4X*k3i}4TJ->qvM&@yLgMDNQ?xnG3bsCCyF86Q<@_|6BfQr71F>B8GO>Pub0FufLi=*S#0;&gsLH;GZJ^&|lTz1hL$; zDK$sg5@E?m+^;I$-a7Qu3j;VP%7C4I+cvg*c4aZyVtD94S}dxeQ?%Y*qAQIiJTx$n zeBMr^`5=%mx}zijD6y66h(FVS?2CJR9Hw{Pkd}ta)+`c*3$s9ES0HzR`HrYV`nPyw zLx$MB!xXXH6pX@mk$D1phHdh3J#)@;7gz(){A(N5%U7bTA^G+z;w;n)B%hd4c2nq) zj;X+eQQtVQmgX(Ok`ENIsKHfp7|-pc52rc+bW(tlq1!VEhH9!ev}!6nwya(N-h^V4 zk zR*2cQd=`LSrUrOQW1peLkGw&uRlQrf-41hC1`^2YH6q+|UBbP_d(R`22RtVgN<_?z zdfa`?Z(*X$1{{~~H*FmTG0Q3RxQSe0;lxIjMi$7etQpM?DNhyU@93H;?LPDjGbC}| z5=dSsf4OD@R|%9Kw%c7OUPtcvTKTS>d`6N-)AP? zPxV#S6!|p@p$@wGM>ke=zI;^ds(U{$8@7a#5_7{u*=e~?V8pCBCCx&c1L25#z*)oz z(R@rX`&!n-iACH6J*TI~pDZ?uj{;q9aRW*jcWz{Ml4->$El~v3vHV!dZUU!!UgziS zlp_BRmZIGVi6B`Uq7GZ%i@X(UsTRhtm%ZDL?|BWb&fvk@ky=v|% zWC^S5Wz}JH`UpIE^2}Lz(__J?2i-Lqvo(8YT4qtl$}X>IRG?395s`a9iiYq6w~5@z zN_RHVsMFi5@^o35TcUeZN~*_kSOmL&K8>gAmhXF_E6>}`3@n0JH=M#HQS^NNAz&l! zB-I-59UQY}gAQ>mm<*)NporMkt`g}7B;St*fx>U9Lz=&b+sFzriA3xn=>J10ysx^g zt%~Ggy!JpvlxZZBvIV62g8983&ZB6h0Id%g5$zNUZC>;M?UWn;ei8Y^ap-_ z;<3b{A)H|#`U>=tI0>S%+sd;XzRqzI4p?|umv?MU6ZY@xZ+ zBxS2)$97K-{myNknFFp!on)YR0{FC?Z)_DJyUj3N8%Q#-T5Lv=lCZe>PQIbKIf`q_DK!F2384y)n&yP$Hps6d%)rMhYu0K;hP7qP3kA6CPquUaC3>P$ z#Ff4dS&Hfc<)^mI&4z-DP7<|p08MBC=4nT(nq=z>MZh4o;17*gxv`I(veTP2RZ~tL ztJCWgl^l~0Ye?Cx`XJuCcxB=blq_@x0N|$M_#fyS&q7SZH^*#$gYdRMkP1Iv#g@-Z z`d{Qvzw76vH{Su{TdgI@rO_+!?DAJf12Y{Ph#w$G<+}mPjS|!M#oF|wj?|-(s$1;= zj3y$$8QFax#09w{?69oP;{U*n_>q-yT>KSDF`B2i=`EV);e!`$XX3s*c@!E4(k_&C~_24)AGLUw)FsuBo3rUHkQ% zr>E$Yk+isfU`_Rx`t0_gM!|bH$C@HQ=R*Fu)tr~^K2an)vA=!|4JLp@#r+=kR%v*p zx!KF~)X;VS8Zgc6R*kw$e1s@{VGRI*@!GVF($rmmiMSY`< z(YJ;Ujhhwj?ENVdLG4;}Ft0Fp@T9X-^feK|dXxEiLMhcv`nLZ)ImVsgWQ&c7VEsQ( z^douSh6?$SL|>ej&&QbsyiW~7`va3Y-W{wBlBxHO-ek3c2;2o9Os<(vmsuWg)-kL> zfC|?+bGAQWB-5wW?A9{!yId6BDkd}7Aiinj7+TyXG1u%Oi}rCs^0@}?BS6ejN}TgV z!0!AP6ut!SRIw3hZpKG0Wg;(K)2!GH|0ya-b+UVQ6k6$D*&b}rVZ~NeQ!R-Sx1P-c zUd<~?LU2L%fMW-5&RVY{;I6$xmxNN5dVRsEO&xnF+-pu^IyWrI+qOJyzFf3ydz!c4 zdFnLU=n(!VI6A3hyI&5Hs!K|}>B;R9h;{6PV>@qlutDKK0Y0Mq8EtoRBL=0I7Su8 z4@NEq^>}TG)t|2imqMF@E|(?LWrx?1V51lSxV%N8ZNkj%X8tyBeig{Q$ONYE#T1Bm zxSPqX%Bsu6iJG&HMjD}?8^FImi|g1-229zn^`Jd$TB2x!K6U5xBYOhww=@XnO0PHQ za{S=Ck_~IsQj646B4EDh^alml+L;+h4*(n-h8Flkt7tl+@z!JXUjE@U8;jo&x~F#y zv5ge4Pw z(BxX|&MNIWt#5lamOv*8u!>;AR?eNC)q#7j0>1w?y^w~re=V{$Nlv#RTx!~}$~$q7 zty-L2>)4-C2m+2taPK*J$@+Y!%;&UI-lL~MjkCe#*%3m0%>5Tl53e{`8v)(X%OGIK zfFI#Tu>-D|8VtPBk1bJG=oQ)AV7i^kF&8uInx7PVUV0{Z3_1?$;2YARt! za{m22@QZ-g9Oqr-#;i0ZtCtO`SPl$&N>tSatM^+d)sd!)&B3YO>bVU%$yX+ov~4~A zm*cD9nvc(&dX5^_L>Wom2d*2F+KLA!!(u|e0`7u zSU+yhZ9R|ohj8g7&L=Oqz}Ur{3U+W`m0Rvm&@a@O;%R?N(I3Sv-K1|Is8e1v#M@eJ z?ZDGDRc&C5(D~-@Y#D?ksbyYj{;r&&53nuNI7<_RaD^s!9}Sk(zgw9)1NutKK97_Q1?GCM` zAXY|#wU=->p`sgS^g*y7d4x1NgaMNRxL3SV36E&UkAxk$6Pl5|&KR zMzy)&@$Mdld=SJ2eD>xKo9jl@K^j$3phRQi&y1Q@&-LJDygyXQzCF=GkXdQ4be+EV zRT75tHUtS8d@h&4{)cZ#qtLT2$YJOhSR-W5bz*rSBweLSy=={q^^f{}C4-Tmu|U^@ zl8Tv~%^o zIA!XkZ?o}&k+cd*dB9D^A$*k)0+P@Ufu+4qq@HiWb}1?lHuARRB&XafT%v(P{~XIO zE9SY5VoRg@;`ibnkdC%Ke7!9nLyr2@1)eOVqWVDYDtUEN-`GvJ_D0-T9$~^=dkVS?TMun{PC*JRI5B}-io(5WZg>bNd zp1KN^`l?LWL9@So+-ibttR2)g_kUai_T5TC4~M{YU`Ls=hAEN!gZG{{F@H7Pb7C$o zUX`;HZ5@>c0_YmavT8GGc(QV@0b1n@pFN5opQA1o)Wg=mHo3&kpHE|l*UGGFOO<+Z z6AoOvn{x4krt{4DWl(3r>kzVQqv7^P0xWh94p|@Ab*u+}S5X(9*7UCnsFcJ1K;+Qy zh^6_3BeZSrj=+nI_uSMJSrAHbb2x&BwAs;{C3@zmayD=-4|I|tYR_v1u~;|GN1V6l zra4W|ayus~f3nL190U14X@)Z}k_Zd%X@jg@x+80PG^;m00MTE4AOD=(g_1eM8ixz&kK~ukz+=m#;u*G| zG62h?aoLzwH#fPF+wp3~MhZS`t9l!g2=DtGn&%OAU=MTn2RO_W0L``Am z{8qgqd#ne~!xg;$5y|%<`T7|Yd{-<1m}{>{@i<)Myr1{6vV&1_TR_OL=zboaZx=p> z|JKUGsUSes>6#;XwZo%h@^l@cnx+j}0@+?U{)l^cQt4XD)>PTbT_@ z?X*9*8Zq`hNv2c(+$iu16dTE3e3Mc7@*{l?K{uK`qtoFksvc_tE%D*IK4Qlit)(%z z8?7Ig11z^CqT%#WZmC$Hrr*j{Lf8%$SSf2g_MXi0pqu5!Ey;hyV!2YtnV9~4>i~;RJNZ4OcFoW9$IfU4jbA2WRi0?eZrl<)qc*&i; z$6=j83S8=Zf5K!+6rO8>#5J~qS{SYHTBL#Nb-ALi=HSSVwgPhRni}`*@F0llkRM$* z_{r^U#jz(|V#b1u)EVUV3?Ce0r|MZw3)od|V+Xp3c8|28uh8%dI5Tsp0(*=vC?d~y z2WzhCRvBh+W35=`BX7JyaFxdxApkH|4H+91C%o!0>%oyLtKrwnDn#dfRdnnGubElD zL6!K4bu&MbzvDCDKxWN9)l1A7dwJ)^h{{`x8z~qvqcHh9h&PG>bKIq=C<{HM+3It3V@VW&^xEKh3ed#afvgVV~jKH_|wP=5b>;q zcK6I7oS#^h&?6`ny#0>(mQ}a}U_I8Y?g4un*MsG}tLtY|fL<~vjOgAw$?B+O?|SLB z&$XrWw34?i>o}~wNMZlZiKPI;_9hJQIyDe*?w+1y3+RMB(c`Hi10u=Xnb zyjcfUZdk}<+oZ&W=9IG!cY&r+x>0=`{(OvZZs|hU=+zaFR6WVqZLxK9g#lJrD>sDW zUd|qEOwzf@GKpwh=cqgeJ5X9Cgjm;KQr{m;Itj7;a`_j>HuAG$;f!vwUR z_2iU&OoU@4U-nQlb6({r`o@-!dD&cl0g#clsT7A;to1&BKJ*mV@41pWC|x z6~C4ak$SGVz#21JZO_Eo?t}yupw_ym2Bs_;*2)bp2!*0~;VvDA%{siAjSzti6q!Zds8;K^Wr0>%5_ z*q|0X(rTETsnoM4X-b``G9+Sza3ET+;|2(&_qMe}EU;zHE1v_;;dEa{Z@&9gN_WKF z@UL6I*o9`!ds#2#PUa>H z;=eu{Dg^6V#MCIGWID<6roUs(=BCv~Q-6aE9;6)9sc=ZhVEcTxPvrwoDYSg2r_Cq! zH*W}-FhzN12}q(bQ(mU!?28$Lu{}nJ0y-izl|>1?7b%7}OpW>(BLkU>T@UNVozqpg zydzkvDFBp|e;}FML#*|%61TbXD&Fw53G8n?T8;@=y$RH=K2>Fq3qjvjT{Z`DNzA!! zX1(6{okMr9(z_kzWb!oE)}jA0`IyE)x8^yT{W@S1{5#w{@ogjYnkj zZWC>Ls{+|+4W`bAwLKjIW9W3o2j&ajE3ha3Ku?5n@9*~D)m{%Z4UNe_JTXoXnOsy+ zO;3411}8ivrV8tXiXhlbBgy1a3x+BvBOIQHK)o_G?#e1 zOKsk#l|hvpOl+Mq5VLwAh-Cm6Qwhx25o=utTH$G3dEyJ6$Yau$RkY*?mUFvbTl*s3 zW97pmAVk!vclv~-gp%Qyg{Zk{ms!1)8AdPQ<4a)u9j zJ4k5d4=i{@4`-@mqhzFSQ647e{Ti^;GqTJGYerMBqv3#@CfzU=z7DN>+$Z zi*q7mcHEZRFE0v^jMLr=E^hJhzWq8RT#;zhEEGGAd-JxE`H-q!iqi`$kOPgFj>1Vg zMVIn)h_Q}k1KV<<+Vi}cem|dWQ}jB9=fl4uJ1)$qXU*<@-MaO&i^w;%yrZp_+hU*F zZ(~VEf9qMQQqP=wtho1~2179VuRl0))F!6*lFi>Jk4*f=#pb9ILq8uO|9j8m@6bg6 zF01)7e;+^>6Wkc)C#{!0zPRr)hMQ3P0)pzbs&sfZ#PeKdXRO|NG@8 z@Q+u5B!_Cg+Lv0qb55=HJ1L1w(=3&8{x%Si(%c*?e(aqOQq2K?Z?-g#QKu3%iKp)rL)(a;SFcUp77YF&eY z7z(O+Epn3RKqjFr$7U7b#kS&>+aIKm`3mx353II&p6^))1$$$aDbM?|@=Fzu5tBU& zE@twtQoxX}r2a-6$+ov%c$8O^CDMhY9_q$juO z+6k&EnNR&bMH&3{^v`*d;BKcy;4e>ryo$exZwRqznb!sIlL+1)$VegS6Zg#I=8t>j zXAS`SD;QQ36P_6uc1+?A6f_NEr*VF9DL!m7&eQ(Geoiu%LQiz(;)aVPTl60&&5p+c zaRf5GF;Ohba3A-1lGfR0Ul*8|_VdI5q|198)+9ds8U%hU-4 z?i>~>4VNc~zai{yVT>psi<8kEVC$jK?6bX@?57S4=PSMBQ)Y58Am65V+}n+V)9W!V zRYab?Kb#Ne2kwNzMjJUnx_yiDOJ8H+eXvo51lo`t;5JqzP;Ll08?4Qc)SMvp-oG}M z9IkeQ<#tk}flY$p@(>q)9*{gS^*4HSFgDGPAan|dUW?)uUlR_}wv7&Yd|d=b^OZ)( zlnLj4upegFS~KUV%OH+3SPyt_nYAG%>|5llA8rwE#}Wf-Rz-9g4poHaN3CK}ZB7{j zCjVYabUAI_bY=&C1r&>b`*kTT38_Wz&x0Drd)+7e&@=y;?i7MiJPeO26Te(8Auzc`6rpizJhokj=c(dB-oHKCK{rpc3@7)fu?XOJ2m8i zuXZQM>hw!lXklYz1hLOor;?=rifI<**<1#)Yy}Ahrg&^%yXUsX0`O;li08$8oUxOy z1tR~lLC45@-E6GJ*pBom=c`z_k*QQT}zVvFAh}tUg|fxo|8< zRRhS-IxM0S0^cUF^Oj7D-pI5xP5rnfe;U+wtpIv=&2k>MjR)ul$j*>YfInYe4g_c`(UW(I`)Fw_HXP^|<0iLX@#qD~hSg4AY%?D#Qlt<@!uqaj9{HK)f)kO#2f$^Du zt=YNORFOR4##ILr9Z($YVP}Sa=5{pe<&m)`l3RXm>;^cBIT$Z$U_BlHW_U*A=PN}E ze+C)pnHV9k9d6>n-JMkz?}@$&Y8IaoCT^^Xu<(u1c5xHea!+{?t@aqKqu-_j$Zc>X`lALXoCr{=cNeDS^@V|}a zc;QC#l##;u2{__Mo&z`Jc3HBlx6e*whdqdqtg@QenAgNEeWXnl))b`(v?t&KP*3Su zFiing-eo@vFLBTutpMv{d7xADGyg1`FY~bQi!t{8;UaWdI8f(o%a@(iolf1=b4mF!TPr>ksqxF&8hnWg)PdroF6Zs zLmSSVEcUV;@htm*!PdKggKu7vo83FgeY$LV`7!bX2wHIIbP92AS=?}eEvXGS*X^mz z$F3LEf3TA~0Pden&uqp8`h%ZSccY7`f)mbF6xw*6&#~h2Q`d zbP{|rFwWu|cik&156$9~hmtfIUZR2_ye&R6zAF7?Ht_W|$P zN4PK)jr5xVU=ny5(YQMHX3yPjQkN=~*=r{b>TbmUgzff9eLiy$S;kYXUKAlp7l>Ic zXHvGT&0#c)dvSfM0q(fs4!(bIWX0+;M-Z#^ z-@&&r;`)8fuM0%AH?}1VCNuNFAi`ix$;NeieKxM?W44VAePF;gip)FpfFl1_r}dNj zp1r-NNzbF8;^v$_eDxGY|I`grU=sXEJQ0W;bE+z=C^$4?9p_6t4$IM@(4_RHHt`IV zTOC3)P||aP4Hlah{GV4BmKJ;~L;#gdRt)G9L{m+i#z;E`1@G}l)y7PZAG7d%;3tta zxFcx^%jTMAoJ!Wg|_6rWU`&_$w92NL3h}kkEG9mG0pn!U&FvO zC6Q!RFb*m_+#4gCDt&$K+f?as5zUVMgIb}{|J`b4pgUK3K!}NSJzZj*uM*<-7`g4g zIZjXG8_54)bx}d5``6qOea2i@4-XZrEtKJ27;&TGN@*I8-37pn>K2$aCtWTzF+Dgu zB{aCCF1!e-7Lszfr)qsE@7kuobk*)I)R9vz6=s;Hs*K#EDGb<$aaWAlfPPUf^drqIM6k>cO*brV{>zDO1Jw|7K7TguDeU*(>s7T9zBs2 zsyVv<@?lz)+V)IAg8m~wlUYpo&Kr&o-yd1BV%3*MYs!)H%(BMJK}be6QG^>h{@|6H z8Foo=&*DA&U#Zo}dbV$)P`X0f|5Mw8I>QXr!}TJon$RfMwjU?9aog%^Ci2T5n7IW! zmZm|+-L_5KrhZ`LUql-1f51)rd=Pf2(K_hY6JTC6%i?x4_9!t|Pxf(1}nsfDd)HeaR`)=&~$KK_C10E6Sp8YlO=|MLU88w{Qf z)(b$3)nX6DoCSr-+iu1@hsi}VQpIkI|AFrM;v$woQVPjXsQ%V$bido}N6s1!KSy;w zKXTdfQ$10Zbf~E4>X&*PWO2}^fD$5`*gguvj*S#vj?Wz_?j{>rGJdz-MQltNiH3Yt zEM;*%-Ex#lUx5_Y_KA#MoRGyxT(xVS3{k1MNpl3m@;@To271W#2+(JJRJyW4^}cWx zzBeJ3Ox_=^~|dd>QZ*d{Q@(Cf;N%-KgaH zrOh2;%+LwI1I;^;3hLBtgttrJqS@~8@trMW_Iwn6l_&$vcmw~)vr}WczHn$|ln=5w z|4IO5gEoEc7WdS6lIxFLkNn3|yng^olV1&a47%vqwUzgiN*5M1fa&)#`-Oi3>*bhZJ-c~_cey8M&;!+%!reHlw6YHx zeS%U7lx^IkhLRtqoGYZQ965!A2e@qv=TAHUip{GZIn8xd|f_PQeV&f>MfOj;v9Va?Sx&p1{sx^evmijw= zsJ%x6ES6~*dDuq>^$8D2Mq)pG3DOQ-O)u2LM=iop*9ZiZ0imu=o|ZsL8ir&ScP{vpU7&dZ zVy!G{cl-^m?{>1Urqu4hlp`q{xgbFZ`TClGHd#S ztpfmXo7*7&js}LC#a7}0P@(?76heD#75?LOinf#nNtbo2s%RNcMa@j|E*OxqQ{hxH z@`|-+SY~WXWIO4UP}nJ2i!O0)4q=UQlKaTV#{&kv8(y8Yj$N$k*84k4l)^bfV{$+9 zY-Zrh%W-z4y}!za;y+KO-_R>Gu_PRDzaNY?6)F=ASPu1pCwcYsYp-7o6G|tw%3^f2 z(;uzyro*uTY$Lh;ru@)k-R?!y9{ZU#0c8Td(-xD?d5aBAZqW_<2?O?3Vqx6UK|r$K zZai*a3Pya5DEBO_GF`Q9zQyT$k859P0*%F5HmoH-NKu!2Fvpcsk-n}2e}rU!)9f=5 z#K2c`MRE9?pSNZ?V++Y5-+@Me=(0}H3+!v`Wt-{5|7wMUft5>75&Q14O=_(*^>cvc zV)ek)TTEd<2>8kLx$$Yk*kolqS=_WhI(1jnH%h%xA5JUhhvMZ0=|fjFj1Zq3SY`pH zc?Jk(pkLIF?P*_#MLI-P3t^~{!f$XdHZNpyFSb2-^YF!vszNDKz&r2f`miP}_7g3O z#q7poC#^3|afD+g}13#O>mQvlA0yPoypZ zDAN2B_xw53p@N#ymH7*j9aFU+X9skjs1sP)CM_DDH)882rQo}WoQBm1K4@-Z<6n4? zb+zE7COa=W$(f|5iUX|l{w)2Op32+DR}$|>9=k7DcwhjX1U*_GIi_8q{n1w=VvG0& zX?7a6(#%QX4tizT44-#WFR{C^=M5-pA&2;yazFWgH6wX^i1%7KXy}r)U0S{50?nbI zH^SzGBb)ay(__0C1x<%j;bm)OO0gG9WA43bASQzyG?xmGq`D+sF|=x4nb|TH_^JzL zf&*9oq;#)`4|TUI=hz=e2PQBPfSNUB+&;sNRexP&t4#Cr7l>&DHbfvZ3KG$n6aIGC zWC`H_Z5x%Ihub6pi9V4x&Nv^yY)kdqT8A5>fReQv9G+Bh6%fm*UW)1ZUyTcK?lUwy zYz~wj3kYWHuIVlYHUf_mf6J1S&EI&;KMrFWDou!^CQ_s< z@?9;9?Oc7WwlwSl>h!N%hfj0{q5g_YMsD@cY zui_mzOqyzCr*7+1B`2QJXS%AGM@;{33P^T?cGB=A7nJNuq_Tvhu)k3m9l)617(LLG z#=%q{dLfIN3_dZFe1bJnB0BgQi&&Y9vUh=Lskx7Ey4&fq?VO$k=M2bq(~!dOi6vv` zV#yjfKMsD?!XJ7Ngs3cb%QfTO7@1`*9PAhjr-P>EmfHMBXK!T;xPj+@i%eGSg*UuZ zw#MTLsF{K045dh*TrDjDw%U%(>lFFo1nB=by7qXc|L;F3DzPp?ZtJeTg_KLVY^2CX zQu*Y5Ns`OVlG|J-au1(Ul*L4-+>*`QmwP35hRt;E3}-d^w5Ip=wv zmo2*~r9@YjR-EHG+R%r-i8t{_u+wE=dwQ;ZK)sdxI=64i50E@u+|u=BNKzmrYW+I5 zYDfx<6#GK8>nYx^f=p8^^cC}5nRA9dBC&g-z4!2!m>nX9Hr04PEULI5(v>8D5+Q+kdKcBWKP>j9MR0GbfS}5;KI}r!;rz8`iOr&!^a&I&NLwzVtH56D zsTW2D&`tWjZ7-9VZrt!?@sp#R@Yxxz%mfv%1##qqTF-Q$9~RQsftnS;)7@ON;vsMa zm|6g+UpggR+IB7=FvBaIseo{(skos;JU;NcKDQWx^c<5EbPae<^e>xzm-VlvIog8S zGjZi80OTO;9CXn*r?shdTY3m}g5sBp{y0aCcW6A=zKVaa8}T$-%u%Ah)_{%*RuKI+ zi_&LFAdc^%KA)}7&zA6=V3*XNWK#0bUYAf_KDed~2(7pFa;O1amH78FTxm3xk2`jH zA>wi-26ljF+`qI8@AL3*zbFoI_FyhF84VC{xN^+(!ELn`$P*az<{1|fXOL^)@onqA zlO04ajXr?|6pdrWlz5R(qjwG_KF4nbF!U8ixo)9Ga2bx? zM@)!eNDxB`vBZ7uPm%#3RR-c~bkri20Dm?C?+Zy=P5pJ^BpcMlFJy(^m9YKlUFUq; z6kZz3-`VEbXl3V z4VkM~;`~NBN|3+AOl@v)bu}B&91aLO*{7?jni%RK{(*gI&%Xw~2Py#+F+SyVu)(Ed z*btVfD;u~y4|Hp3hd_5QW6qG%=p+^c7m5Cp&D7|%laIHB0EouC6{ewqrVcjreEDW_ z>P^^PO*D9x;=wVQzR)06uNW@wYqjm{|AMRiy7(LVQp6RR78J{Jd#(zJJa^DFK`r&w z74sPYviU(%Z10ht`I{g1WalroK>gaCb-Tklzg-1Ddsn?GZ#Ty|kEcOqN#!f`5wViZ zO72Bue{r1u_bAY9lH0IC_5TY!%Q4J0%X4U=@AlicNHZ?@bV=P}Y7^6X^Bs`;U}lpw z77OtS)Vs)F@$qOsTC9aeOu+&At=XM>A@f*gJJT@a6e&JKu(CwCU_GL{fQ>mH;l87k zA;hGQnfRw1u(401$q)l7M#0@-0ILEJMO!G!nGJ*5naaQP`tQx}IQH=fge`gEU9g=8 z?CY(R;QOY&ff~&tfpR|qpH3lLocvsyQ5sV#TC_+ds9pTLb>|cKa>pVgbnVzS%wOYD z;g=2QHSHc=1iVB;)kRF!y)6U8O%OzBPHUTLmKr;oywE%2bh@X$vpZS00;IBphjGSR zwFJip%o=%-H%Q)KH7n*}v3St>+d~XJZQ>jr$VrDNt@R^m?LFu!8D)(&~w}eTOLS4LYR(|?>jBQBB z+h50><8`wD{@q4SLL5BF3QSzikUsk$))Tt&w+Q*5ceV8ATz(SB@2LO-5I>Flh>vXx z-pE|95I_B@;5-8+5`nlYkmYGtFl{}m&7S)hdn+6sZ~^JyU}ID~_33V(X-4Shuq@4V zrHTQd5z(&2>XG86>jsDlE<%sq0r3g|1OH)(#w*1-X3?civa=%eLK?zlwY@(TEQriN zRF1;t5gX$PkYWD&4P|4y0k^&mg4Ff}5OH2N0BqUtyU(wTw<$^<6ic+3(6!%E0QNA# zt<_VWFmXRx&8IR~?Zg;87Raog*=$RROSjD@rtWHlZn0~kWU`@qZI(AOZExfrQQ|*3 zkqs>f7YaR8oiKH?=FP>9Czu*8Jp8A-@F-81DSf+N7pYl;bk?=3Mo}OkK5IT;xTxn8 z=GZv2xt#wYGWgXm9lMDEr*39NYM39@kbAU5#TV(OuZX0ZU1f4Cmp2`D?#K3=j~-MP zn%>4HsG^BD(+gx2pvI&ZQQbh;*+ywYG4y5+WvW>^Kwr87=H|r;^i4MB09PQ^p>O$M z_g;5thP}#sF1HBGIifRsL- zvcAaBX?D6#wA110f`StBw`V@umyEk_^ij2*>gD?=fF7Z)O6YSbpxRK8X_i)a6zst_ zu+1e1;U84T#uB(?((g;C z0&6bKa0%vmd_dEakU!v$ElV9nr&#ONvS#NqQmCUf-J+1W zCwY=Y#EU2EtH|CsrOQ{A;Z?E+p=Za5b*! z5|Y}?+`3?ZrU3%Q{1YC6EAkpGn)&+=ga+GC7y2=8;W)%|cr$8n_?-pzGgvCki-~K` z{*xw$(?8CB2A=P!cwT)u`f&4|rC`V$AxXaR&YZB(@^@{WOKxZ`7*nwo#)(!9P$uXxWFRuh%J1!%xub>n|*jnC* zN1w7$Q4Fm=ar3P>1jUI7+Z=z$|Lq}HkcODWZ}CSVfuBCKP5!vM&}lwq|6?XTKlLZb z{`?oB`9l5W+{@sXg0_mEb&{Pn){Cg6{%}3Qn>M z=~}jCqZMi49sYc<%hT1iyDeIRX%&lK|GWJMDBN%b8-%7u*wWl+36N?HGmF-3{>)n! z_MH#*v@jgbD+7XU6U!`}(31#xcb*j4`#TJ$`OKvb5&)B0PdPBo>AD(ghSbwC?4v&& zTVBh00$LU1BqguN#>u`kQ8{vfoFt<_D#H|EtKzt3ajL zCqNceHgwbiKMVPL7A%b#WbZ58HEiRANfaCuN8Cf!ysQLern&?22$Mji-5x0F+BSjL z0eJu@j%ZP%r)B|yfx$VV#XPgr`${KVr4b(PDobs= zkSL#IEC9_+v4LP`<`jh}FH+}u%SaoOiXlO;=JB1QKU_VKdfByHm0-Vh&=*Z>ZfXN# ze6*D3e_`pCz2WU_Qwz3>ARhQIG&NV%Fk{GpqSTU(inrjvrv)OMz_75%4ysM28BP6vQ4M1S*&mgGp?~0{LP8u8KFiNW3thhL;y@O0pbpK}cz!j*myjeN(OjrIA1! z)iL166(DVmV^3WVfNb1gUkqwft3+f{9%kyqPGi^~&^ zyA`A^G$}Y!-VZ$Jx#n46x+7bMzy$;(4$wgj8sD^qIox@4e^h)iF9R}89w_qmPZT>S zadZ{PRko$s{;Y@XEKB;>8AQ*D7ikATbl(%!ioJDB=&JkoOnedafie02V-Wg9bf}5Q)AyLJe=8#Igr2W&n%xc>JtO=h{P0|bETZ9is71+c@AF<@ z0Ng3FnJpnb6&UBubI1-wFzaebWa>Dz*$nNY1uPBygck5p;i`@pt)@FRhFaTUAG*W- zbK#poJUD2O^uiS6v5@p@Qg`Pfj~L+!#u{c(>9kc~F#G0Zz669#egK?f12bar}qV%>>l${!z?udK%y%Y$hYIY>W*qZcuZg&gh zHs=o5;lsqu=^;34X9CgJ<3lrfwMXb1uWL1XFLpmMMFImD^o|3RQ_&MWGcl?`!Ssm# zg&yW)!XmM5a$+rVC90**&me}B2SE$C*_3fVv9ZZ!;6Z|9>uod8x!mQK)Sqf{$uO?W2E5XNKDr6ZTa7eIAAutJRiyND?}0pscq`CTmGw4Dd(pl9{pHRKPSd_x>a>>0Y<*r@J->P zFvASE0ZLKJ?zG=!&2mSI;=#{~qdhjB9-()siG5KwzAObiEdm;2%HQt zr=4DhY%6N3$zQcHzEz0;)RKV#5s$O+p(yu^=(LplOiEfs)m?9}BU#7dXZ2;^r&-yo zuEg_}L&I7_OZB9_NQ!3-J+Mo{*(l;e4+z5NkQZhnF52e5>31 z|7LF2N>fW0ZNqf2{ieK4RM(NI1V{@Do6wp`7nf)r3af5fvJvY$Q3pZ8B!(8Z7ZNWZ zrrIqbB z%G-v@QXEAkh2HhdDP67ODvu_;o(K4cGD7~gh!mb4hP+zK4T#1uJU8)6=UCst&C+Mi z8JsXrj2Ny@4Vi=;v}O4|#OQ_q!?{Bf9k3?F{cGMDI;dQHxnmZ%&eh5&tyRTC)M)7n zCzpjQe;}tLbj0PFoS3PnvBY}HrBv7FPZ|1>MDnx6VjQjpS!in79#TB0HKe32glJq!sx{dz+0mQjSAhnb|{mJeu~*So}d$3xz88M^#jFx!An1{u+P>vKw# zX=CFIMdRg5AZK-5D4K3s2N#Ab&sOdaTm!Z9bvDOyszrMo?~g{LLU}d7q5=4P*l`RS zg*%!LIU4HkV_x&McUkJbc{*DHtnRu?HO`iw`K4F80vI)ib?<>ux&1oWxZos}b4Jo) z(#a6Hkg!XR+R?Qa zZmE+;9_@v+^m?t(EI5H5TDXZx|HBf*LhvQPx+gGPjLEM9&`eR-7XxqOZS&!{8Nj75 z@yC@ucRWccGNw9?nY)P$`3V`%{Lm4~DM`;SW!8v{(;4RQ-{N*o1~w^#?= zgExw7aSz=CtqNawv|ZJC-Yo)q<~J?@ynMxFrK!pfrE-6AogCi*5CeU1&XSZRJ3zE^ z)e6M3<^?gp@(S_29|Gdk6f9$EGg8dloY{Aj~%orCrIZHUIB|*67Nu_ z3<~QBOOlN=WB;2WFn#!Zb5w@}hBF_|vkrQf&}f$)PGg`LkYQ|Tvw>S3AjMC1*sq{F zjXL46Z8aQ0v+1& zUO<<@WJ`gSp_>?iR`tELCjPR;Np)FkYR_GN0J!i6OU2r%F*vEeRVH8h>z0~2_^y6H zsTU_t)%MY?Cux$9lmGc0kI<7A?0N4#h$kdxJe!e>G&!l54fGnXSe>7MQ_gv&@%hJz zudKgV=#nLcR;63sVu^wyi{Vz8(liZ>XetE6F&XCZ)YDt>V(WF%=5;acH)WrnR@Id; z=WD?_BQBKku1t3A;{D4Uea+`-=fBVI;I16s7bO4K3}VHJF5<{pu{y}K`&l7s0`LJd zWMTp14$#a)fj2VRlnckpia(9n83nwMfH(+ItMSQU2-9~b#-#Wf`)co*$;2xcv&ean zFk@E8oy1A=r@Uhm#m8Ej3o&a8m+%{!@u%r7@VCm}fY_1M#<;}7-lWtkV|*LGdRQWu zNp?RT`d>a7yAw}8BLDm>pvURDaF5Pa7d+?5(KChb;tXT;pXt^UdUDy`wQnq;Aafh zrmW(U?w1jRwAc^bF61e&cjC%$E-&K635iMJrq2?7aykCG+}<%uKrbuYz;P1Z*GtF+(-{=aC)f* z5&~}_T*ikDCVSSyT&#&NP9WxmY~g@w4Xt)%=4Ih0hl)N#A9}u9xpha_8DW?;>sHEy z-o-N?9>45-d@c4xU z!C^Gf^w;Anf61_5*%cJ%#x{i;!3RPX_2rE|das~WO4XOO@#`nR=!~D06e?yG+{KE{ zS(p%TrXhE|nc7kMvV)FiI!S>ZY`OB*%L)ka00qqDDp%+1ums8)|1yicYl@~$EP z>E_1nTV(H3tONFjJUu#1T(UT6I$71A6Fb{RjkxS2jR8f{NsB%JAAcBzXpGR^{;B8P z-Yg{@oP&drB`>Oz5c?BuvobtyzV8of(!R<>jHa| z1(FMPG7*t0$L@~F*M6;ast69qmdI#rx9n!9l-@7rJ_1mOo*Bg7QIX|e5UZ?my<y3h-dr7N5FIxqkIqpF$;t_n&egbrlqbW(FWGUR^zXE^|RtLp#d6HbrEA!M>m zl!yR9x@DB;J(CSRQbQ@_>9Zn236A?EB7s|-s_2L#>_S`uw#(+$E=Yi_QsnO!_p2@d z>F&@QKoKgzSTuaWg(P|~80|35=1E{)!9h1x5&A;$gcqpJT@b&)v2&z8zjow1yKS+; z5@0b~ugppO=$U?XBuEDAEnttpbK;+QYwS>+7V#MZog#iG=IYqiQ8j~EfF&wF6Xjor zSh4tHD~Ifw`~@EbbXmwksNM%NAMSt0Vqja0-`%1mb8x8LZhiDi)h+i^*?V7Ng#1B# zN5_k?F@f6e-+a)bMra9S%K8Z8a?>LTH^{!zCIDCBWJ`3~9SeGX{4lzD9ny|icXR(R zQYsjQczo3^JV)ZQ^kE6(By@8X{6u)4;_+zLHNP%sDf`;)sQbGR6raUf5`PsqqZOal z6m;uLbaYfiXzO_&lFD|5g~`EAK?1WBYtAbiATE=I(pUe>o-uy4#P!BPzrj z#*L=&GixopHLTk=)SPe!?{G=`N6sp9;fuX}Dq{QawMnkJb*zfp-d&D2z^J^S`ld zHK95$SDMND)oaWs)a3L6_Z<2Yoifl?SsA7OgH2*P$_}i9fXv;t{V)2T0_*TGF$yse zD!+)>7kVe(%s;as6wC4bB*WrX^&r8)XfSb@p)b~UB4ttk zAP>$dFXiByRQV}+^A(0S!3j=juT1JzQUo#*d)v&c7tBHf%@6$lY{hDj<+Kz*9mLE< zm3KQZ<6ceJ*X1R*J^}or@Lh3MvKm8^d;Wkru(I0FUC_3-X|g8>}-Z)Hk<2$U@Q{RxlmkKfc~w zRq+@9xLZ4XxEX_}TiT;Ii+j$g~l$(1Y zvl>a3w0k`*wc95BjOf8(_)j3C0c?Od?UYCc{1_*kX5Pz4p~;pJ!uy6 zio3bj>0QMRiCfEl{C{S~5iW}WOuHobwOu#y7l3j|H_>@Oj~l?(3msd0cydW34e zGh{zcjAz6S-v{$bFeG;Jcg42bGYuiBG9NaUvqchgM2SRP(L3=Cd|tD|197K+W%@dr z*-Y9R7x0!uI4&PN9A!CS;w7(2y^r?&d;Ki9IHKfbA**QWqGK6zyn*(XDC2^=lkxM^ z4psYZ(ccAadUwqXoA0Qt<{IvxUk;zkh8F&TOr&P*kS%S_v~zq7eeH&vF5JEZ69Db# z8y+%E2PJJ@agA$Vd`3*-*K#&A1?!QL;5(P$ko-(m?(BxaOW*F>!f?s!sE-^t>Xu-N zd$;R)*9!nF@+xqO*CCknxZ1mZ=r~zI`)@;Z76t=6!RRJT~VP*L1B}HUv8nTE{3Ju&-WRTp8{&r0ZiI2>Jw%fzB|DP7_LP0pc)Jm??>T6E;=()YFAuu)6T0$7=!)$;Y zHW|;vPxf*wsmHygTz+>9fqU0{(&2xN*qunNn)+Y1?Uxorup5T}msA7cxgx8TSnDtU zn0nn~*XJOyU8SR7|C)_(9v`OCnr}CJ9=ASR&ApN}GIIU3?i5(m-&`tKu0_QsqU&8KEey={=*m`f*tLW-mzRRuF4}E3p1RTEwj^vS#dXC~c4CciEIRc2Pz>*gzE~=O1>Q+j zb)g}T3<@!iqjintT`b793#Ws%wjn1r#5XL&=VK??ErKP;1*2owG4Q^Rb1VvxSz6!( zzq~sWWc-!%Wx`Q;7r>U3wyNzV zaBnEDDCbv6OLyCuioVD)H=ox(f|m`yL~{qBykA>(5vx{Tnn(zt4fJ6Wp@yt8EO;rq zstrv{A$E(t?x5GCezw0rBx+J}3p^pkfu;7G@_SVmpkHk_AizH6UiW$RlqH;wX=2S) zlbP>S81OqI#?`mKN7@ejOgowEF5Fe?@|osNRZMxP&vhEUqKO3v^DZapF>0#+#$ z0m=xcReOEFokqmBt1RE*`+wFltHmneh8(timC$_$@;Hfku)(}yN-d2n;=F*NPqX3 zE_}aW=u1oH2LC-MQoQ>vy4|9xSeDfHu(*p@FMf)^9zM9z0Wr%B@6+54jEznz(hLa1 zaAi(mr+#7Ib??fQo)UJn9#)ABjNvLFypI48Tn%Ax#q2d>u1@QXOnJ5l)p2-eSYtfi z@2)zw!cCwD1xq?$r~Zh|ZK^)?IHPp51uG9B{7VX(-3<8g zxI!?XbOR2nvp@f}*L`BeWze0cSO6)iLwYO<7=b`5?8HKRefoeo!lo4~2KOG9h=F%L zLHGch*y;Z5m;%3}IMQEyY%d2j>#$ES0o)pHo)Mk{oWv`nuYJusp(DUbaCe!$TrfT} zf8N68+D;f7XuAu|MZ}H&^;w%RSK!7^^8kfHU`7+g!B=-J%)zLAT`%P(Gv(g9X7yBv0R)~4TI&vMx^ZJF&> z`GWSz?PTg!2BdCM%-JV2?q|!d+#Jv*mfEYvO}r^zN^q{sg9MADy1LL+45UGMI&-*;uRyi&FY*J<0yP+vM+5mSe%b@2bJnU2aE^|TFGjNw{s9|ew7W%NDj zrSdqZ{dNr=`4011PV-tZv)L;k38roG1C^W%Kh{z>JZkGWG;}c0=knGBNiVjF9&wRZ zXFFw8J5YjFuW^vK9i^|X7300Y@ZZI1drGtC==lH_^soX;VkNPLP^Eyn(T0cx6mtJf z>P&8a{kz^ylA$rk0(nCOhtGEb7>fLc?`%U(;-je36jGqCySGXPm60|3U+`lr=rY7K z2hjC$9Hpj-L_@X(?h;re#gzF3kPi?#R1VWz{n;)6q1Au{W-vC{#P?J+Q27-<8~{5H zN8t)$H7BJy)Zq^#XG9>M0Qtl{>v3ToF2t$9BD794S7%MJv8MjO(1EM%WY^A;SeQ}1 zI(_UCjLK@0pZbZPp0bce zd6HeVTmXIO+y?_bXiM_KGJ2PL7qtS&4`vzv3QfpneNZKY{hMfNXIEopA+T@T$QEt- zXiLSUn@CYC2h)QKhB390LK|8wr~n7_Wcbrlqp!A}e4si=1p9$@HGK)F<}ohKiYL2I zAQO>9%Jt?=r?vGfK#b_t60Dg%fjDf-Y4w3rnCW;)RYZNN1mOT@_*D-F{2hF+zxkma zESdH{N&utB7=RWF*l1h85fdjnr|fiU{jZIm`#EUIJhv54c&7*;QUlJC-0wmC@2|CP z1vmUCR~h2h16+&ZLe*LDcOXNtqm6g-ehV2`hzi5^VJx{VcyTAjTEhp`)RrD#9c9&& z7r{xtBj~KGtn6V-@LS3Na|s;o#gy3Un*tc;UvJgraH`{F>Y?Nz834B78OC%u^da}~ z6J>U9OJ>U|qkfXsSA+kmoOzQhK>@#@aF0>9rVtSu3&g|5h>2u&{CUB-^ZPG6g_AMe zqJ#KDJTu?V_METX#*!BEY&(Ya=1E1Mxs75cvbqQAk9%0O_=>Ms9k5?urf&R|Ovm+V&}5^*x)YF64WI`71d6 zxnGTYP8eE z4$T765?qG=)PjK}J`Awkd50hM4=J5t(IF+ns<;^(I+P79BD;g$t#Bcu87*>xG?wC% z`~r0(8+w>?$2XfJJvky!?^n@{U#VT(9o~xtkr=vwqKc5L#m?g5j571a!gBdam5~v8 zxQU-8Qz?nzs)NP*`brPdMu6ZQFIoF|TnH|@Xj}aJOGP zs-LHf%!+0}mWgO8)HsTbWEl~i&+m9txjKO2TmkV)Eg)>GIs6#Yf2Da3sy0*A`)GLc zEI3x1DNBm=N-V2L<@3sS-uD!}iLFf5^@c}Az=wK6RSVqT^I8L5;B{O9Mz)8agLMaU zGsmsD%-Vdnsf^&K9HR9)By^msx)?cSRx_ydjz+@IHX*(LKXjvliGOO<6kv651;NQZ^z1|;cJ$96(i*T>(E6#_xaWBk{fwjr-*UPY0l9qOTmiPUv zahRvN(1vJ?GSxhGGvjw!K_}UP5**gdKe_}m2)0t1dKVmzRrZ(YS4vw_QEkJ;Al`4V zUXmw0*!NA^a_nq%Wu%Tidx0dwE#E+ZO>!Gc$!xU94&&HJnJ{1{lQQRo*O3!e(MLHa zxQLUJ$C5WDE`FKY_sj*hNpTl$)4k4GUA)fPmG?{2(RGPuFLo#6FHw+8&=?wMUi86T zujD#Zi1KQ|FABIhEviX_^_M8{0iVZXPQ%Re%PJRv4RF!eNfeMj(CGmA45k4Fz4$^v z!}4sE#ba;f?I@(>ef-p{iMVw<{-ptfCr@_sk=5d&%8bDlbGQAs2!C2B4J*tLiJ z$c$orxw?)(?C8yUn=SDcerVVlFD{%jh8zFt8~4cb91`uJfvIdRX<-!@^D@-vMQaPPVSWo7E}l8yZ~ zwrvnBqI|@B3TfxW40jX3v*6=D?x=X1ZNKo}!JLVw0v5iM^=PeT-p_P?GKttDwS#gks6WL8{r*%{N zu*4E~;ba!R6%XaOkJ;D`hh+?7+J@bQr&1EgiW@K~hnuvw$5tyEyp+y=JR{t!lr?py zYHzPpsvsCPku6Sxik|!SRc-BL`bXGmJMayR&c&Q5j(sf&;?@?Q>cj40U=7qqDIch# za-}%HFkJ{n#Hy=8u^&2o*)ROCJsqPEl~44PbS>Gm@p90}<<7FQp)gi#vt6-`8Da2( z7$~i(52pTNH>qDLD*K71PTP}h!?D128tTbGF*Al|Mb`Morv!F1IiR;I)|t$F<~!7} zo5oi8@3yF^Zd$NMfe|?F?F~K)bmh>fK`8NI| zfH|k$N$$}!{;CaW>npzAGE2Su;s9ssjBsB4al7{2S9ve(o28jv-MjRj44XHb-H2Iz z({~f|QJA|HMPWL)E*mous znJHp>a#MCWE9Q0XSr`7xKq1~gF#kKcT@epzpI}dt)EeVrnUD*@$5&sPF#jmU+AUJD z-iX_MBl9j;iCuj;c%yscdw#`P|MPe3sQ{Q#_v2q&G4DsIb99HMsw)ya9{d$S>tvqm zjBU)9dmB1A3q2j7#nbUXPa&c+-pj<^i?ji=Ny)lUP5#31#Yu;BT!DfSQU>e8ecrt* z>B;K2EtB$eN@4F9ecbvBdFrp4Y*|;0UJPHH63HyG@z~U)VtQ3nRFpxjRonzmaIOI^ zQ&G(<9{c1PQw>bAiiIsgP<8sj>_h^7=Xl9-W`|1u&BEvQdSS+ zt7gfS`HdYBx`VCZqSkPTN#UX_eDQ}H6z%Hb)2Eb-|C<6xNu-k@i(}AAt%}$NtuJ=h zgB7QCYHB~<2Kt+>*gud1qdZ{O?r?88iO3j3h5rEavSQ(Le{f9Ns0!PHpHJKaD0xI@ zOgTfU?iLTMEvBX1{^_Yzo7Abh+70w6{KCAs{iu` zw41V--I>LrywFsA`A5;7p&Bd~DPVXAE;B|@KTH2G*^_*T<~WME)Wr0($Kf8GM^(zR z^c5SX$WrF{ny)86DW{uPIsDbgJ29r82CCId~Qv}e4Aqxw` z-G%8X)V}6)YHad!_sWO(`xSH%^DR#(0Bi1H3R_bbv0w1dcIBg12#EdE;gRrJzJawh z$SZAH%ZBdb0Hz7_z$neYrNCoBcMj3>a$Eoh^YB;vZB|OkGHO|g)JG<$<3d75vF&7G z0iY)K3Z&=j&#?ZLlHJvc`vZ}zg{`jnQuadFCU7wu%(({A?UXM=4S_*>l0*GsPk?z< zHK95!7Cl*~*>=0m{iR8cdshG2|E?MQ&7L{V`9z2AbD}K()0;2c&^1Q^2Y|*iX{9=z zvb9B$F@XqWNO(P1|DFq1aKadV3uae^9f*;8)8n%1!i^&*yG7ktB6Evr;i{N|jEQ!o zVS0rV@QZI_=|zM7T9T;ak!2^>u5{u09=K2JPx>+Zx7(rwjObm~NpX3VV`OusrOk*x zkUk*L8MomE{I}EcLI)UrmB4Mbc3VUuGat6sQ#vdBt-6v-0#rLf2S41_U|X6bILis{ zZ9JPNrN++*Sgnk}z5|O}=}6vLw@Fp8dFpk-=^o|dGPemrO#+IeS}F*-S@BZNJw3C! z=gy46F^9iBZrk{mU74iDKagZnutT$j%KIXZWB5RW7!ja>peMCN*OflMPdN=tch_V< zdKBh9u+DP=qg8V85>I>*zi&8dPwzB5h@sYI3#sBB#oRLAKd(Ko(SUQ0_4;sIbW|Y1 z{LTz)5*vLaW{N419b5uloz@pWPMU!0fg{Xb9cF4~e*xfaPC{#ZLGv}IrmM!OSzmzEU>b2`wp&+kqP8{CHXD16 z{(%_zbJZ${m%*ogWMP}nIS3n42y#bvQSb%JfQD$wMsS>;cnvmFzwhh)b@D6x*1jCr zD-B5SU~^KiRkyAoTH%*w0z2PX&Jgr{WHHXAo2hMz3IqPFvN507Qz-DS+lLno@pmF= zG4|we#G1?}98RzN=tmuk>az0)c$1rYmb6%oZwBEn#LecA_CoMgolDjDqz6WBX~Efg z*}AG^nMYjR;n)O-*ArH-=UNUWe{=iB}fyU=d#-KM&gj7m3fyi!xeR)~x=KYUR}-kC~r?OX*RtIsAP<462^25scOhP}amA zA^N1Y*HV5)=;CZyKf0HQSOPZRVa%TvC;;fiDpqR2cAqqPe$(n5&1DJi+5+*hEr?|- zqZk=K0WRFe;{u831|p_fd+vkhc1KG`7Zp22`C)7U*l&xnr;|MBb%8g^X*dmGEego9 z$&zD^rbl}xr@E6}xt2HdKvY+}_TW&?9leH17$`p}T>!K_%W0=SdbO=Qoij4#7>SMR z7VQ|{UmFxePr6FC!^QWc*aIp!)3t-@#R=;2%Clgb*nj-aeRR?gau-up!9_E zC8aX=9qep9YH7B%_J5T#kS7)kwl8t_QG#|eBkV=CsKa4=i-n_3^^IRYcLW3JW7cuj_0m`r z!**`v<+YJrkcTOW0{NtC^;I@*{RY`L`ER;Qj}~jB{`wgW*Bm z;5gQuUnp?+_}BB$XvKa|*{{xXj6Ooc=B5M#Y5htJ&u*gb^voy^-<47Ih<8SrQEGoHLV5UU8&lWGNyV#}AL41aZcU|d0Gj7rB3PS-OFMWc; z5J}2jA`Pnel_A+Rc7`b6@T?2YPE|27!?B91>ABrfk2_4L&V4Z}=4C%BztLt7f}qXh ztYdBXnb>mw%m6@)bT`AbyJJKPB!$W*-mcWD=Gb`HPCu9J%&2~R#m-xqAGe;pCtSwU z)-7IFjvlgUsJopiSNN&+xy+MVC@9qFn5otsZLP&0Jf+h$(<-*_AdyE!l_(Kvp$1%a zdqThpbJm8$63;N^=OW)bX${Ykl-}>t3uEa^`tKnWYwIbmsLXcr913g`JqSe7ckOt2 z*gue{e_8&-vHu2biTSj`(sgr&^Pr?qigS_ekUKSRv_iu$AGU7S1XdKr4R~>bNqlVh z`T9y}DeCJnqVS=4c4RK>Q?>|_v}qZx+Zz+9TvGP8sl~d{LFT6mEB*OKojE&#CSl)Dpa#i_Xt-TW_tq z&uO@1&_Ut_XE5i&3rea-ZK?M2B(0m5Mgp?MY~3Ps@6Zv^0x8n~LRIf9sj6DbF&2dT z0mxiH`cz+>W7}}HtnXmoQeP*E^-}w`aQ!jPqm^6umL(Nb_o&z%RKRs0{INB~LOo3L zoiy^s3fO2)x|jiw1xWC-2|v7R4bh)X@-`k(NJgsjB+%ONDgo3#ejnog&m zAG^GIv7UTiQs{TMSdUw*Il12%FTFpU-VPqWh$B9EC7`@n<@ zs(5nxhg+0lfAp0NQnq-CqBSW=g-|GGH!5TFc%GgPr`^qk-7>W9R2D zw3rMhZLiRr2)H#rhwwI3Z8FmcDSrCMZ=}BLtRM`?6_BkC{u<{pfAVgNGSZhqSg_jz z2p2I2qu<|2n!Tov`V?z5g#gp#;mLNNN$f$jZ_mtUJA8pOt_up(JCJZU;O$|L5$J3^ z9D0;xqztsf06F>s-|L~Nsi@4o($eH)w>1$2SV};r&Db!1s@aW%pX+xnizs>fy@Cg5 zDk+K>^|nA)*TVo+ajysO=foy(XvBoKkataU{3kgXAvF$H3}nNLv6rPuCrXFsgr~^VT$?_BhVgaO@zpEJ%8YA5qP(&;Phd^L zj|dfo7c-U2vnC7`UahL<-v}mM&W3t(1`12!C)&joTW=^cyl=!A1)~5D?|TY$@m@_0 zt1JEZQvAQDRPcL(uPV|k^h$LP?`$b1VR}k)&`xU9*qZeY$bP7T;rdH;kw2%J*#Ddg zA&~!?y1F}5CC@SLRW(+{~~laz&YngPT7s$l_uY3?BAv8D`J_5 zrcK%LmiCQ0LfY&S<=ah0AL!-Z^H8k#p7TZCv#Y)j^G<-y`~ZUQ31K-6}aGn=^NGBZB2@LWxLA2?n41_ zLz{Pn|1wmV&IOLF99rX`R))GXds92KI=ExC^xc(BgmAeLY(ScBt!*4?@4;?lUI zS^8eTM3E!VP$ohf^4lMgn5D%r;cCn&%CVH5`kU{gUJqk?sHz-2SM%daD-_Gj7B5v0 zPy?Wobq7!?j&kiaXLK7yNA>1}f526F!Rhv_8pj*??!6q?(qdNBcil%3m(BO;QR?V* zN*4Y3iAxA~#Sq0e+0ZAf*tUnv&UdFGIkj3RmNpR{AW{tUslietuAMQneNqsgeUfWb zlM!BpTmA;6-vB0xtd$l8UjQHwQOMg>mqr&3ZEfy+d`ewNJi0zl-OcMDel%d|jBpMo zY{J2?#df+X>CrQ{bYZwL5ILp^L|jTfcOZQqdy5)SB2=$W+<*@vkrmU#2iU1Tu$5uU zK_g?J61E1;?r7Txy_(6`2BZ#D!e;bVK6O@DGd&<1#JJ~^VsQ-107_cACE4Sz@0v6) z=5|VCDC^!SDt=uzomXJa;kSXLJ8YA0F{n+twpY!TG2?6-9`3R^LJghjJRJR1(#v$grfGW|ojSY*TVd=tvP; z$th=caGabg$@#Q7j-1C9X3W;_)$hN0)I*u~;eK8Bbv>^}L8TGm;~lh5feqqhl`Rw+ zk_WZ`m%nJ`16nI|{pP3%c<%v@%20J#saOa|NyCj>MAbjcLE70?~bfkWjPDhf=0@zSF0z|I)wy92>6O$LsTp&~($|A|0?2m@7Yc(UTVa#@KXzUbqQTK&- zI||dn{^wD%H4}GvSTd3|rL0Ep1(`DwqdHURs-kym4d)+E1c|I6!3PVXshpN=&loxe z1rm_s8B}86fx<~Q7YH4x-T%o!6#w+h}Q*IUMA?n zPw`nKsD-as%4-VLufm68MNQ+^z1d2 z;eZB8^kx4jo38PYEQLVOFwX@pnnQGMQfz?EwxKg2Epu6k)+2RNqj>Rzy0HCk&;Lxh z3;!Ph?9v#XO~WQztzIb`b;1(E462OAjAF>B%6E)2#?&Pxla0(DkXf?UStQw0V^4YX zaNg8>pTc?ITH2@quBc$8*B>1`^+no$Hge+>)a!#%{vURNWXJDePD)qN-HY7$a91y;%f@4tr=hCh-5ifn4jRQ#X=J69) zi?M<@lhK?*2Rt0JcSJ;OAI<&XB056`51|r?LzMOP3fd7 zVBIFQj((%0*la!KyK3&p3Nr+!)MTR&)Dlb9aI&crv8{b**imMCyk#+I>xgV9+;^<- zvCVyn)?YJ@FXr*x^~Bz=GnHqwDU0eefyub)f^t7lonvAr~8HB4>)d8?MRybI_-_Ue$b(6%e_dqs3 zLz@AqzYv)|`e{tmp{cempJ5aLO9>OQ*3W>!!B$e0UwNZv<(i*)GE^T`NZ>A!d7i&D(C~IpotKjK%VV)jkDJ zc_6{A|4#IuSdu^w`?63s^=vQhBu@Lc3v47Qd*s9jYA(Q71sn%oi;e%;A=bp#Jl_?7l!49PMUzEIa5s{CCVe+Q4x?wKX0LFx zMfT{G#KHF;YrpLW6ID~_B~}Ys{=YKXQe$;vWAi{3Np7hw`PavMOIO}SG(~EZ-Y_{L z!rDsOG~=|v36`KTzZ{dbv>blt%B_*5hV^!6gor6=BL_Y+2aB9Rn?Q|srO3Qyq3!#q zSAZb2vDl>^A!5^A+H4#k!VKX%a`ZBxU-?`-Pd#$t`=h%nKN}L-HihYg-t!36#AV&5{$&Yuga=YoPnE*x#na z6R%N9qaCZT{T;#Vy_l`@4#N9Yo^x~%r?a#!~iA&Fc4DG@VZ|F7M%)ch_+07+5 zSD2~9aMX~q0Xpt$ksdH^9_+GXk+bG`BhxG~AHw)>7>s)nms^jNj~j|&sEX=ErhY(W z2t`p9LoGtEt^(uhm;ThIy%7^=364+e)1~=4$AtSj?pe7-?DCmQ1g{Pgfz^oTI84dzv$)Al(jRv`eoqSGjMzDAbf$Ec8hT_4K_Om zLof3m=X&U`!l%DpJ);2vbOP0&5qSzm(VlRJoK$z*x_!Y2DAY)X!z5A~-`X`prC;jS zd%wHvg!``}O9mQc*UWaoiyyrWAcdMBaAz;M5cKzHF}z0so@C<=zdpxvH8$M=xzqif zr1@c4$SO(Rm7BT{$Fhpksbe(eo%C#U4T%lOwlF_DU&RHexbTge!bBixx5ap6TI=_n zk8}LgHnw)FZ+_U$F-$)J=xpCLG=-17jS*aDS%WcR{Pp=a0;}fSkDL141wYOJ3+B(2 zgaOpJWj)~MDz#&SF^UdAvM;RY_$)Yk-1Oa)X@4su6cF=s0#eYhf>GEpX0WGuX))XX&Hk2fDN~l`>taCkek}z_;z&M`kOJbz5F7 zsiam#ii=(u0;G$L#H^vx+c5<0r0MPawbP5q5FT37s?#40yWI0A6)9BCGyKw;iouq~pdhykb?<*TbC*=oy%8b)8TQ&D*ZnQBZdT-zb0 zkt+H2ZnaiK0lwc{S8OskL-bZy94Jin^l+F7Ca(E|&}~#5wN^@1XTZDphI>4qH|TKx z$FwWPlpu!EKCl*H7G}HUh~tP>)t*p+4TXUKuILOI|5nIdXa4$P!#X7+GarTGIr4S@ zG)z05)P!k698tpbM@F~>0b&A2J%{)doQXq&<|CCp9StaA~_-|SjOJ?e|z4_X0 z{a^J_URb7?^cN;-m2u6`>Ak2+9h~1tvX!^b4^6)E@fQ66)~`z?O4XoiaR@l=Y`$}~ zhB_*#6;)IUgmIV^wvXRdIMkn?ks(6opAf}*-UqkM32t9ZG~JJP2i_^A2^H=KN*(2G zYExx)Q0Hx%hvq-O{_Ejbw;6-h9i3AX*p*W#v~jitJGBvlgK<{tD?am}`qU@wj+1== zGBi(@X`kxsnqS(b?JKHsG#8~mgUl34#D64IXs0KT<=0%yb~i`s0araj;t7K_xLQ&6 zFFp=Qyw&dLz;qoQ-1@B_HFL6Qkm2a-`B9WyWvZ~Mu@Xui8JLv zb?^m~>`k%;4Ji@brH#CD%k?i1ru7A<3m(rk?pkpYGxx-o!2iQ<&71TY`}z zvJgjZi4L#eoefwR*26eAY+~BRMxN^9<%^QglQjQB<=a;&v7`+IW&&7_bn2g8%GRFW z$7VFQ+e$ExzIO37yzr&-vc4ps=P!RlTG3EJFE0fwxdPmlnt$htw0mjQnhyI6Nt!4( zUqeeikbJE0{p7dMpP+y({v}HSzGYE0$e4X$b^&>G6+B9(E9gXnjXffXaE&V?bETUB z3|tP3Yp%6l2G2<}>73B3+2D|*RbfxZddd9(ar;|Q)m_b}k zIZ2Lrmh%Xxrd!$~bQdW&;x$^C;AZv6#zr7Xc8gA-c8v$lu=gusroydDNCG>5n4>M! zCSw-b;Go=~mb1)6l9_}0Z!rO$-Avc=4Qq(UhH@2$ol%f7I)pg4-7=ezsPs_VTz$z* zBLVs}ABjx5M7L65W7yxGcpO?Q9u7N>-+ntK1+$CqYELa&B1>)ZF;E%a@@;=t2IL`r z8~3N3NUXa`JsYt5gjd~St<6z?m`9qlE-qN=isf`aX~-|9988E$Nq+fbW<`<1ToB+M8( zx>X%I_80QZU(n)`^`UkG0raQBNTsv&*Lo;(x%TLmfiNHUS)dAq9#Osp->#S0wQ}Z` z(VX6$mk={uF}r3sCkCjFcxY#+6G=)c`LIhlZ~@6$6sD4HGZ8u8dPf}W&(0PXVS4E!C1}n_f@{1k=VPFA<~as1B@Zy7FwAW zucOONue&GIcvdd%M5WynCAm#npGG{;c8B>BkU-36<9`qM#0WjxqqTxiiXZacn}n;|*SUs+A7h=c3Re{xR z_q^2=`w-s)egn^d_=&c24rPtmK=_HhA+f+3T-yBtc`)Y%KDs1!#R&!2Ck$YN@LrGU?V*N^R8V|r#?X`XkCR`BG8VD zhIeWuti#8F+!}6-%A0m@PEA3?;!e*tI64v98o~AnxqT%B=jX(g3R{w>1(!646HQJMwAfDsU$*{B z!`xMqMnW#RpZRmHR4Vj2_@tt|b%7YSVNsN(=HlzZOW>~_2k$aq!PXF`5^uM!BtU1{ z<(I|#+DP(uIE!_{;DKf4#hv%#YPBT#0HP!G(pU=4=5`6WtTJAOz3*ExFboZP1ur{m zl*Ti!ZokZeCrW`TlAyL&{h+AcQ!?BA{XRwVi2K~T-C$&;dx%fiD9k14WMNSkyUS{*G}R!m zyaKD5liWKyC5S){h@{hAsiq zY9ae-jZZ~&x5Jr27t2lCsuMue$8|=j6KwBy>+&^*umADdY8(xPhk)eF;P?H=ts7dL z$)rlLi>LelG$mAV0j>FrX2XJL~lBHQt5U4-Gei zkzgewk(wO$r|3flN~@BSyBA=8fCGZ~Or&33T=m=>T}J=V(SVy4Z!nd)&6w4<1EhaU zmSd2tZ;s7lpvX6Eg{+JWAjT$h@=y-M=PULNxB9}rQ^@c-5->tX@rt_hSJVpS`Ggb3 zCyz!w{x}3eCLrLT!&R(LgS1alMtHGIR6re=xc;DY{5j~y2>mEH z=_T6Np_huf-FclgD`GISQboFAt4#^n$PE+z@z8U3l^m#LS`*c6r`;f61-aH%ON0U8 zH%4O%_d)^-wiQ^;a|%vvvcc5j@M+zUHV}j=j;gbPh3?==<`hnsIG}ug&FZ+Mj(*#< z-|KZ4d@E==d^W1xgWs{RK17$BT~o zYAV^`*5O|ra)mSbQavhZ?7K?xIY;^T_M5QM?Y7W{Vcn?4VJYs;BCce3_B8FXpjYc^ zYr1P&x>zzKry%r#sJ-t{TIZ?F2Sl?fa_scZFeCWZ(0hr42Tqai?|u3OTv@Jg_Wo>? zstwOM_0;Vz#6buUUTs(fWbT&6$&A4Egk`W8tnY=*ADW(= zQkPm>YnM>VHjD){;DX3M^L{9^omHvV`8hCm3Kg2tf&hmmLj6mpX!ChWZ7T&}Qh}|yrP}Qh`js=dtirQ!>GO>hqu>FI+yZxxY_~V*q52d^%BHeumm%x*^}`Zf z$MW*oO(|%aIcJr*hFf)XqP;c%gwUn6zqN8 zs5enF2YOKh5~7EXalKFC(qM-=!}fGmDSnx~VXj*hVe|i1;`36~x z-z7kXp2eu*yVqAJUHOd>$%H#iDrM2z%;6jsQ$T9!yTLt_&P&1MoB>kPbTWmS@-ftclUaR+^6%(-|NCy>3^u6q z{BHx*a}4dHy^l;eJ2=j;uAKC=Eo zB1Nzs-Aa^IEE{GoZ<3MD#XK<=A9T;E21Rv7*``_>=Q@^y@&=ndH!I6@n%$ZBz_Iy5 zl3H2r6lrd_bZZ?8*e{tg7OQcS@S9Ta-_`|qOGX{}(|Fl8t-F=|OkV!+AKS2we@~Ks zAyW2O-J;WgbT8|afdju;CNN@Q>Xw)&(Uz*N<*Bf|P1}V;UuHkZBaLP6>B4LCF}5_~ z{P7WlV^A->W@_DbGhyfAZ%)f}pWU65IdwbsHb;bx&;XEFB3OJ)8kbj!mf^e$IO>8$ ze_I~c75n8u2W?DGyP9}uyZZ9c0R&l3UeTbn=fjV5pN4R-lOFdA-N8u!O=KGuRh+D7 zZDR>_lIrJR>n4Q}cD?2!_|ynptOO|fcXLT|g|daTnLoU*-~clDUIieB&0OW9Q<1YzFWx zC=6(9#kVn-o^;r2%UfHD6wJfm4J06PZLEqs= z5{i@NIk+@2!psAYP1Z-7FSMOqTJGTC%pA>metu>OGl`GZ-7;`f%t6G=I+;KI&oMDW ze5$dzOLydHPKaNSu`+F&Tc@~h*Ef*m5-CoDYmMu*bh~mXGk(i|h}Z4-QMWKtJFxzY z#wPw)1&`Q)b!^%W>+6S-kCgR~xU=B(U{(CK#tkkF7De~Dg|!uDZu}me?d8qagC@Y& zpo(>2vEfX5Ox}+*y!{QSHo@+8Xqc!=bRf!^LwTcA+|b^fUBCT9S|rHn{#o4QFw>`@ z-K{%)OiR>8a23Q7*Lb8Y#%kk67zjUkvL~YU&mMuXva+t&vgKpr`)HROuZK>KTlRGF ziN4_S3gj%ZB340?_x75UVN>aDz~l*lZ!1WHMF-jrK<>sEM-%2oL7`O0kj*VFCIVi> zHOcQ@&jb+^u_X*!$`zG?t*9Fw9u5gfJ+#W+Jf2OG|4eYnM_lV8xD_f{2K^2yn)Qni z_U(FkvmACySByhsON2~phN}$Y+PAxAJZLCtA+^aPd-|rPSciQ z;G1(R1SZ^1VG?uVA3fb&Dvzl#AiK3TKbd2eSr zD$I0bcv41~G8|@zc`8yV?v87Yn23}%(HQ=EY!?1|%0%==jGcCzOLmqe$L+b;bN6wI zw^LQIpx6wyZzF)85o~(Evf$*~XGX|n7l2~XthbSwOFP;FO|uyQhpX}Fcn@)Q;S*&lwdGQZEn8-F^jC< zZm)p8q9OqCAX9t3VqOa7c??=A{3@l_^ajt914uJCQ3oAHPZ+Z%M$T(SE=-~hfAmnx zd-eQI!nbxPG4y~iO}W;g=T}6b7FpH%)~2AdX;~Q%&pG(cDp;mHl5b%aIrRKbO6*#` zp?1&#@Rp47;LKI5HpfXW99hUe->?QE4(i$B^T>GTGu_5I;~4qRUbi~X_m>X*p#?o1 z|IGtepK=ga$E=&a-@$oKiMQI9n6AiNVS+5(O@sP`$1t@jtAGP(JD&Y|MD#wIm3uo9 zX8zc0w|oo$2YkRQoSY3bw23PqTbpi2t?o~wG`g7aO4AZQ=J?u*_q8q~6p}xN3BPF+EcVf@E3%yM(H-qSU#zt`O@*KRB z>6BuI=Tpbq*4rhAQ@Nf~R8w4@q_u#U|1>4kSO_!7*X#y7V|m7{K9OmV!2QZ!$Ol{K&5(juG&uJZ zScJmqe6;Xw;J-TLAzoQrqjeW%;b9X_VS`P)9M%?){47n*44H`Jb}Lgm~Xx1to<2Iz{GxU_WdX>MR+LeG;<`{VJ;Sv-0vjz zoM9^&u=qSb-zumNwz6opD+~E0}En+p()u;F{lCfv6R8c@+>h~=e11uDMHIGurYs+ zRN@yl9j}!mieltN@gUw>N08l1t$wmFDlEaQu?;}8doP)-Y4gScg^{uN`4@r^e1U+; zQVy%6u7A`MA{+&2Tu)I!m?3j!aXDXgf*H9-6L4k%82+op{k85P-iZ$wUtxPD{1{=1 zwZU#JIR)qLsHC*It&n60(W1Syj`VJf&w)Q$^B;pgzi!{>76uoqa$%{2-ee6SknXO& z1f154W_LMe&P+8quC$;gr{zR#Sv2z8&!NDAF<`AW?NvFTbgppt^=~i8iiu2PHb1dx z_|J~B_x?84z%;vO7{$sbyEWInIg6#9{*S64?E8oKQV!dKmn`YJE{t3|Q< zXgO|j_h|m1$s+~J+=&*kBvFv9SRwY4x=n*f{?K+^{aa5wFTlasG{@`p3|>=p7nK(E zop{{b7qB*HB(YGV&)C@77sy$MP$kJ>!k{!^4P&;`RC(r>#(}N^?K@PZ^~UPeb(5Yp zl!L%d6A99w?P+$+2Uawr6iyxK*RJ*B%7~MDO`gPM5T|mhuFwY zfMe&n*3wnGmRll44S>Q)|Dhd1c=4~N*%$Yv!l@QEfL^gajeGA6JfSduv6%|(9Q0VF z&Ne<3<+=P@Uu?Le54id1kk|ZnNlaa#{KyPVw6C(8JZFTk5}q)v4Y+4mAm_LC3{pis z+kByW!_GaMY>2MIh+3%tJ!(^zAmePmam(O(LJ8x$hc+-gB)!76^(8}6#v&uKy$IW@ z?z2))GK_qKZ-PL!%J-S>QTH8UH3b%dzT>Ixf=3HW7M#Ynq{kPdB|=w#1zh6@36Tl| zqxPj^heqc8g7$^!SLtPXz3%t&`^*|vbpJ|w5_U$YF^q%R7p1TP%SmG|tqc_3*moTZ zy_V%gJr(nDv!B^60F-#pWqfU{ZHHy_9>KBo+XAzdDELp16{*Xp*4Px)c*EXYGCkLM zA*BrlB62^fZaxgP%-%Rp>&!aU>VAJJ_Tecoa(Ssv=8h$73e}7c`rwJVlb?M%57K!2 zPoFpwwEvk{jjh((L?e|}tjc@GYddkzH|b_cVRDZwxHRjE{lI38EyfwcIpfyl(SWR1 zAN(IPLKMHH+XqyMb5r@&!qA6eyW}mVYk(GPS`@})6OuCsGK7jvF}tGvFX*C5?HSe zfd3M(swC8Qe#NUyE33I^V0RDNg0-fmXWwxw13Ns+xOo4`4PAZVp?@8&z=&Ua*+(m{ z%7<~EeE0M#o+mJKg9vys{wrO&yXv;!B33W&I#T=B8s<=<&X_&jmDr94aO6cUyg?^@ z#vm}Sr1kLY?=g1eBzXnlr+eXMoSKN`L(hg)FvMSP2K`xKrx)c>4)}%N-z+>{zAeuw$M1} z4b^dEEyjq+j_fD2_|5@;!1nmx0kyt4F$>xf;BY6=&Nfq`OV2q()}UA>mTZ{EyV)j| zSO8G|HI~K3N5~P0>4crUk9yS&bs^8?*RjBta0g7hf!IjY$W!DF@*HePkN8rr{DiB- zQzCH)=$>#7fm*i6kpD^Q)ArdqZ!%RV_4VH=wXS!)tmUI%+T$zb~oXP@GR^A!l;GGwiEeCnAV?n1AK&DPc|trojr2&OvA(f5W4 zZ1K;-t(#X(ue@ggs=3gF-=g&fuM#xueYbslhuBih^KpS*K2cljIDw zRGC)#^n*`9Be=-KL)?{T{SM0=3?;k;=QaC*zq(n^e9c>hxi#mT>ki6U<>^eY781FkWX(7A5!|%`|u0}1i`Cz z2Iyyus##x{uIE)ovmPc+{odjWw8km;ZocGPv-AGyw7n=E3c>Xgy*UL$ei$PJsG&b( z<$g@|pD1c$R38?v1&6hxHqt-=-mNQ(M0T_LE)~(VFyiL5-rL{+n$^dD$;}ce7!7GS z>2KLbE55D0z3_Aku;m%cHxZNqQVkELT1(%Na0Rugm-!T@O4~F=YgSf1K zCLeQfXON7KWP-~{{@8L4Rrp6+q6jFmuD1Nd@>TN=MBZ2<97ZN_*%ca!>6 z9YySo`c7+4zlJ0w{n`S2RADfLlMoAB&xQ4f>T`%`XJvgRX?Rr?SAXFJ?7^C3nDG$Y z^(bLm!x9!{*NfJCI`zG#tECy%dk)}Qj0J$2Qi5rO$?3k*kME;$1K!$sY^3ZEn>vYg zZ4ACy`^-sU%^r3*&zFV-@EWa9b3d>7r&Zp;3%lDT+W1y(@67W5=N-QAu)G^++Un;6Ns|fI+of0d{N$i6 z^W>51O!Sj2#;Oq3#tm+Dz;_?^BKk44+yv?c`Qu3;7CWv1?S}r8&>{{BCH=&o5Q21#@ZE`@imq|FvR$7G$ z_k{C-*b%^O5=km#)Osk3Oz2&01N1jyE%jW?jRc_udA}&7H5syppOaIIk{~Cf_{t61 zO2$n!qP}Zyd0!w4l&Bl1Gzo}SbHm}ZUesMF$=$Y_4TcYN?Ru^SxmLm5aihN7eCm8F zIi7SD$=N|8!}z^r_#rQ1^63g76}Q8`sD0XZ+3t2Oe>-%8PA$#VaD(SKu)Y@vu8l){LIL)=gU-ookxE#~sd=J^}o z127+}#1zdu`^-^%2H^`tTcO;;*!`pO&onRG2+S|*)T*XUoBJ_{+@Tg3# z|FkvaQF4ABmya~7fg3R9jVXgeug(%X!w%2%9Wc*A%rBigdBTq-=qJfraM)42q?p7` zyX{Y{*ItFGkLhfq$Fg**-v~cRAzMxWOA~22(as?%VMWV&ej<(0u-3^C*h;R3VcaxM z)b!?m{6p^&QGeP(&$FDis;S9uvqnP~EMLp?{e>(6^fp|^W8FDod@fF5LFHqYT#DfJ z6rlyPFU;_0F3>Kv@|9X_UX8F<*MzH!(;yS{guSAEqu$!&ZPhvsut|N*6gRF@%1sXl z^qC=#g+u|jAG|y-aZp#NK*(mI>8*iTh*84le@$4M#d0?ss=McX@Lhhw^fo4N+rT}v zDhk=+pAJxCGp}ev%21WhwLst)cXS+Wh`TZNY{rjak@FXVDriD@{xeyR?(M9)pBoA_ zz;C5X{?=d6-@iP>8DxQ$ z!Ypt+Oi(+)7Zj$<&$h2?q=Ve}qrZ@Po3W54wATG7yP#s(7MTR_v8Wx9*o5<<# z$;({IFYb7}OL$nk@fdpD!q-(>YoGAvvRHA(;83f?P_jH|>1;uc5OPRzsCj?^sJo>5 z2fr={c8=KV>IMyod)ZNM13v`Si{7h(rxK(70)Sq0-=p3h6R6LhItN_`UTBZJJTm-= zNTakf|5LIGIoB%J%5QnIEi`&#k3gf6`?|Tf1(N{3`bU0&0EYB*emXMgX1noTlI|1F zW$Jdvweh??u5e(LNZ4iaT;oFiPR<`4k^uNF5~!8kx=b6gXGIkc-~fa|N;SkjVZ1t$ z!fmM)?ciEQqVcuT49f2f3%uO7vOu1iiT9 z{w)%p+m|o}B&D%Mg{;R_mSEtkbNM*yyxZ^t#FM5t5Iz;8QA3$g6_Tj9XRW*l=I#d)TKFx?*e?=c4Sc@8bz7v%~|L zZC9LLYLtUm+Z>)O#ygbC-D-_&=+mb`TlG)5&M#S*Y3K=DyzExt+wwD>j9TzyPab`D zXT>NE@(`c`eLfZpI(mT`k*TXSe<9cW0!$#muxIq0o_%-B5Q`oWeTDBc z_=WYordTR?YA1vcMKxn)>Qi-si#220l5PWmbt2rA=OGooizHEA+RKnNuvOL5NKWQt3U)#pA^aBJ)@~$CSFy3?Gjt%p|St~jI0sRZZQ!~%;Ah#B}zq*p2 zo`*H~+S~r?db6#-=GET65ZhCr)TW)d8#`rI+>_@&?b8b_jr}e-KCVKN-^-mJVWi1$ zDZu6@I1Ik27g_%{BOE#k6P28p%BuVe*>e@c?j2_z_XmsbmBr@;T@9vk2Ax*NlgEXY z3}8P6G_+m(%3GBk+M4Ccs@^=_s4$T!GO4hYBs2m?(wGJ!P9dalzFKE~02tnJ0{A5o zqkeOmde?%MKU;FJP!mdArN7*QAjLJ-(VQ=!a*4oBGux0QLy+G*zp(~tM=OklxJQ(E$>U!0<2Xz6``jP@~YzJWn7`U#peafN%Bx@5zM4@fhQBHtX9=EigH@a_yQ7 zG`1F%7lqpjt{fNR`FfuL>)_ukw)+)KspE*J+w26B(A-NH?*>Op1X#7u&@3e<4MyjY zEU>k-3I&vRkut}*kPEeF_qUcjYNK%5fyjUFT--d?3PBBG<*hpDd)k6R{LopBo)tb4 zAVl(>V#T&$!5U;O=T&1WQ11+ZLtwF%GlP@~wM69%dOgZq6gf?c|1!pnzs`0Zbo@yj z6$cH*e!yXx?rA&Z^*@OQam}0=Xf*^MhyPWGCSg(Y`6!;#W!C5faLql$@8=2&qw#wN z9XvPnE^uTJ&bSm%(5qb0`|y(!)1Q)M6q+`}0YV_KB4w$1$+M!a} z{9sVEkbmbXAssARR1@lTw)_Zm^wdwXGr5We?@47Ad_XY9?#ZYo55u~$n#I}r8_Wct zW^ds?7RX|Ej=Gl?Dbe16`^=uU1pfY0FR(s;l;RsQ;Yqu})DV2O`&Xo=Bx+vojljtm zKFSi3cHcYx_zkG{(6b41KH1^AltHm6U0#v`4BM0hnYoGw76*&^M;!Xq}*rW??%WaAc?5ahdSY~;mNQ=gl#_pS+)Zt3BvYqA|QjdR@rW{ zEG#h{KXOedS{8%yicg|1N$(MqlVmly2TJEPg5sba(-6}GuMbDJ{7zVNk3YO=CwKGX zz_n(hQZwazy;X%tWqYLHCH*iY#o6Welp?S`9Q$Fr*_xFS)6{T59FHYQCsyQt6Li)L zf_P>n0(bS#!r{Brx6f;;w{HO-g2hLyE28JR9<6IqA%2wbg(Pp=j#qLTByW!;=zseQ z$xrk8rZG;7dfQRaZCj|f@fntV;(2n(lf$sQm#~#LVx^yE zRDV-w?+?9kFkf{YTAda%RYW<6Dzi~($GdgQz5Qd=d4~Qc#g=v&-G8kf6tI8#uy{$Y zj%Imv=P~`l6rJ^Rl|Yt#6#TTV+46MLXxUeA|Gz?VZo*=&&C(4yBVqXm5r248eT7==O7UFOV z52vnH3GK!7&jbYP+qRz*vPIi7){3z3tnI9t7 z2JZA3fTwao+-wJrhOVo`dwXcdK%q(zNzU#oW3w_$j?F$6FgRy-%KayJ$WQx)!of@{ z#?>wAZ-EY&enX@C-mj5ltfbI3ytjD}kBazIUt&pY!pyXXcxgyCSEqiFI_M~Kk$%O% z*|Jcj5n^f5l$(V`UPr}pP2O#;_47UsS{AOk2NE!uGl09<8S@lbNs>NwTly5Q(svt0 zIDfre(vwP!&VNRDIcv~L zb#(2vP5r=!gGMoHmdyaM7O&66#04a58EP9Hy3qFVeOtN(a{!~AZo9X zH$7y(p%zhPouQVCI3q$0?Jbol8W;M>b&C}Xc48~KL_3=s5~5uHixkXLPs|zWRgG^# z9)kg$NkaEpF30bFC#(Mtw(iOtfbn4~2Tu`t}z0j+3hyn4FZIAly9XiSqe4S|({sG##<@gk7kN%u_F zkINdkGcCHHwFFRTwi53{@0(DGx&78CUx})|-nld7K(|GGClmPgkJ`V1@ zx6)4hN?&0(dTdCE$u@jM1ks-hTOrF%FqV4T^ZaE*h_(JGf{@OHl@eNjXE9hl%DO3s zD>r3X*%i@~5Bh3r1(pD=2z?>yP0oD$8N2^_^zfQN-!*yt6Ho_8KHvhEbQT6+A_%Za z@n47*BP(px@GFBPzZZnXkHAnFzk%*J79c#~r(1MMcar71<=bSKNg+?Kyldm=c-&=R zqVtF-ZjU5Nl}Eumx=+eMf17EPqsOcgqJvq3a}M`-%_4-?*$Htp=pn~6If>Ufi}QVA z-R5A)&=;ho2X`jq!G_VB<6iH^FV4mWYyFpMWKq_`RJ{l$W&$vnC$a7^G*Ys^v32{D z691<)-ldpnLxG@7X&KDW6A2B<+swUh9*TO;|MSoi`6J9!jsKeqj$O?n&2mms#5^=w zhaz9fwDy;@+733dFZtCWnziyz(-irHtVVP|{%kMaxdJ`OxY#dFYySZ9c5oxn-b=xH zA>Vc}2X)>GUVEPgT@rycTXbs_9I=OfvA!v4r0*q5f-l9nO8Lk~)CIro{X!sbn8AN{ z{xfQA;}@WfSjE;S5y%qHBuxWNHn?Q1zQSbked2x*$40g}sc)@=z|{zVV2{&d5!D9y z@?RaC-ye8T8-U6P7rli2(9Z^SH%ZU+q!D^#GFOXKS*bKYC6jf7ZNd%q-JBff!5GcGL8+w(vB$YL4iwuIYh3cIIgTe1P-7 z1`ePo;oQ{5rpM|n`~(>1HSQgb1R#^##fV zlCdLrflFd5zGk%3l7~m&?&?}xGkK2Ali`q7{omx4+{&{L@IQbpp$rc)INjJA*UAyE zG2TH`P-QF!RM9pvOl&*P2tyfi;I<8DfwhqiWzNb(=W^H8EZ^6@qURyW5Z2;Cp&r^{ zat{39FH=^gg#QaOQT#f*gW7@b@zqa91Jx=KoLWIbhJ$}Bmu{NDyJXze%y5uVG?VS2 z7{QEgQmJLh!*joczrep}iY#S1TJuaS+i0*^vB|L0>|3*wzd$7^?{&QCca)!*A~p(` zUtnMayjaFNKxhGc!K{EnrX$0FIZJd`Y8uH5ZkG@$Ug`=^mqP)|Ekc;A_#Fh)fvz3r_AJYtP%oJxXH-U5O#tlx0RTu-XlkY1-!yojMnh}LpCd;$Uy89RybSp? ztWMQHE?6ERR(Nc>Hu~P@`uX_&y+C=-M{`ncly~8~kA)%afpUQ`=U+NF;cL=J+V=j@ z>tsj;UX6*c*l}%MfF3#b>|`i0AEN4Bm;0Uc%vV9TS!8daVGZ#I>z5?DRxIivBlZcYRy-4MQ^(l)4`~aNRGrCG=QBz<_eS?M9@#yk3zL_Zeh5^aQ+LW66Je5_YKbl98=g zhG%;lDx<1{dr0_3Y}Q$+TPtr-%k|y)Y|mHCyxT7Og*PEyR=cShCb71~iASyybrz~- zA#7c&PC%vX^@SE|@-t6Dmz!|W3o^*J!W#$53+Fv&g&h{PRRr`z(BgGZ>q&cPQY-e`d~K3|ICFMpV|G~NyswJ0VH+Sl)bnxK zn^qGRo(BJJPh-y4O`vDM{P&+2Wz<@;?vaAm_!SCWHI7c?z4Q6-&>WfpvFoHPDq`L1 z4Fj4o9wY}eGs<_3&x0l?^)L>ps?R4t9curCH^vm4MIqA6O^V||eNCtusg5rD+}^3q zKlU*%BHH?8cpLIEB!0y`s{Z)U3`(N=l=VB)HvP?@&x^j()W7&g>~AN8ubB*YpR}}^ zAP|7GpMDp#9r_<6e*E0)+nnEducV|Rb(S&fC^L2Kk6CK1r!H1sGR^^rzK~~-ak{3b zva<=_v%s@=yj9?ou<+?mW&37ANkI+^jJHPV& z;0uSh4Yz#HH$v|7Utd<70~;}e2QA7eO2+(sBru}Le^pX>T4WKVvr(cemWul{38Oa3 z6j^n(Ek6r0wqOYhiGDd2X#MF!vB!bGAr-Z?65jW{1w=Wz1k{*;^Tz~_Hr`Iq4x07Q{1wdKnbMyu5tD{LNz zT4j0#-~S6)_jb5YW}MS3e2^Swj1&OdW__Akm-b5j)}`~2h|-L<+&1}z(1m3&Ht>zZ zBbP^@s3OlLi!ZgVDbKf?!4C74Cg&Oq6JjV<9+^*>`UCDX(3K(Iq4UJx6`#oS?OfV@ zL$_%`-jJ|%f9W_$zNL*JxLQ%duR&!xOZ}wwzHFous`m^JipXN?mSA%E&JR?MqRXQy zs$P6n@Cx*L7Q((czH$h-Cs_ySNg1Z;fhMVW2MSL$$^*X{*kPKr&%$LK)U2>J{TF|V z05UdFrc!MAXur!BmH<{=fv^+VnPYEv^7XQF~PE`)pprudK zjyufhi?iX3|HN3lUc=Okp6aSwy_3vLdJ`l1wIE3AerD>c8;Y1)6`aEP za&Eo7rC`^S5Pe#?$p=Z%xk5k{)7Z;*EpTi@V|@L5@pH{uGp}SIOpPZDC8||Gh6^ z^{<9E4EXLCzZ`Rnmk&u`ArTHr=f;w!8qlX2^ede5HZho#))5!L-oiAYRCL>(CR;wP zI;iz_vhquPb>Tw)-JP?@*$`k0hm|~vO5ZT%%C0^O$aXp7LI%O}okl|;V9Ei{W{bw32EcjFF(biz*t>h+F zm=ctL)-ahAxw9ydT&3_>f`i@!EzUTvG zm91a$gj0=>N678|fL3x|_&$q?a0JMI0I6$--sGf@(lfp3{eAFz2i$r&jsYO`j`kjH z$M23s#Qe(8T_QBo!MU=TE0@l@y`r;km@%S}zVR3G(oDmEEcJe)qn-j{;MU1}zXib2iM#s8H2`@9$-Y9O&&c&@Tr*J(8U1)cC=|0r{7HIZVk# z-eXa$*)DyU;sV7tK=j(r*YBanrl@GseRI87GL16B{{Q!aQY?#Y>mW}CEz32O;AN6T z&&K_Lz+6ysYobLMp9t>_JBoeXY-dOGqEgoZwzb}zizRd3W+sVV^ANGTwWO`id%NMqee=D4Av5}4fd_2oC$0|9X{@|lpVv$69cU$9hKyBR z8m~mPpb*izKb>${`t_ilV!Q#aAXAY_JzxFp|46#_c&7LNzonXTnOo$#qN0S{i`nQV zNu-;*O76{)+gzucYY0UVQ*Nn5xy*gZt%!+`xs18rH5J>o9&o=3oFfbtcUB=*SAoS-4gpW2O)R13;T(X5q&77OH1w*Gv*P0) z@(O=|l@)GI*RA=boF+OGjKufKK;|&z9vIG#vrd(CLF%Q;73a)-zV3{S}zK|s^5 z<0*&Bi?!Ouos#|HN6y{YwACbTS821WNMm<>Q0?G+THB{N6(Zun=X_CET{Kteao0R# zAx!)D_6y>+{HNXanVY5$O`UZ^G`Oo45c5O6LnA{)u5r59OfPP$GjN=r(TU+nhi3hp zMpE&34P$xzhi=5Av%SSLDb&SPhgEQ~rTIgD&*?L3U~z|gN{W?UAsgv!xnct z7)rK1VhDrt&ptU}iy=XJ5r@ChBZ_k~U!mG|QI`YuBQqcs*aGtS(J*J-Pc#Afiyd5H zTBQ6K>-?8|o2~;b8%f#@!GO&H=a4@^oLoSemuU_<0p6%V=8-*1#q}P1zYaxTu4R}$ z4stNT+{aIh0Z$PSAcj(0ES35CewUf$Eb?Cc?*)TtDH|zz7 ztarr_57vxLAG}#j1soA$V|uKCxnwH%zr5N=?sNhN_aL8-9x@awMOdt zpbk0=u{O{4toWDZF5Hipu&S*@%sv__aRxD&+kM=EiV5@LW8R+2KE>tuiqHqZ*yNU_ z6XH||cprs*RaK>=Qpv>|x72U7MmQiCIYJtbD<8DcdANhwl8+o-D)mI3Qca2)RjTg& zy!c7i#B-d4=F0riFqs28g%v$~FvapHhosuUoje_>kF*DSjo8Et(8k>axr{D3LNT9^ zC9qb~f_~CQvK7n_my!jWNzu^9r^`b;S-0#hkvAqbBTa#L66wM;y3rdzh;xG0Sss(M zFIvutr#PX31JEs{f1;^B?aJ}wd!ify*g(Wt#-+~Egx<8`BmC2$_}yyen2ou$tIl-$X3%WhyQ~Vw_t&L{aK?fDr-*|%Ovs9Vj&&By~Oqxbl94oZh5<5}=xXu)n@Ve-H3 zv8nPCKq7UG;b1KUYeWeULqkUlR-Ltgd#39e82VHr#|e9nBus=W>cdfmJHJ^9Yd;I!0D%vKD7ml)bZRu- zy;| zb!v3e#$HdoN29&h6GyLKfCcRZ(gx@lNfH?p>W-($y4UaB7h-9wpsA6N)Jzp-Map`| zf;&fv2QKG$M+8sV2|q-lUCHBSQ@OGL8A+Z9h0-jef7>+TQK>soXX&Nj75xkB_4f2s z^GXtfS1~yho;?~01$kCe2k7a8$e{N_qnaw4>uKdjUo3MgniGZ(i8KISu>-%?HQ>daOK&c937nq{iYEXH zZ*N2|Ld*wg`>AtZMQ2Mz-Wb4{)yAC_?B^+2p#Ad!#=nZ6Z8~V`1z}5Bq+VheWz~;- zF62ftL%qZGs91#CzvAmOU7>LQ68RC2I|9k$0^YTmC8*xif2rq%&DjE#jGKv=a&SDH zuhd8WGcI0)j71#cAbGz6*bh?Oj&PfTwriR6(^Kmg#Gf6yba1p zd1`dazY!Wv&T}0*t4)$2jS|pYwh1fOtT#-vV2SivgMuz=71_uUKQPsVwgVard5(E?t;NNDDtAAqVamhj;=xIN*TmOzd_{Ofzz@UB z#SB02;Db4^(gX6j1p|_Q<(EvsfwSb-nvpgA0rO5@ai|xlx#HFAuU-l83uk)2^J^I) za>BZICp;R?vZE}AdV71{`wx-_{^{O{xK~lb*6nMf;Clc93Al!OrHehw3_s>4#8x2a zJc znUA^lbrmx+Rro6vO08gDJgEXV%_NC>^vmi?OAOw-NCQJ>XhK@rP~i^Bgpx@2?&0h` zXS{&quZ5HxgX%O^P@O0~;1yEHbd6=B42JBwcV^46Ouh5M^QNi00sbE4!S@~z8BHHu zv-yR7R#&zSZao6s2^!<01b45qt=^A;CaF#b8i?L8M}x8nC>;8agM)>!EVDu3p*pWk za0fqRRxw9NJm-w0;!4s638aRocI2KDw2qlteY+9|p_D&5nHzR4X#`v{IeQ!P)DAuW z`PFg*NL|SWkn%P3@^4-+f@k|5BqH)iM^jQmQrS$R4*LZ(y~q(b_dCCE%7Fk8dx@b< z`7yZ4`H5zTOIv})3d}5X zql%21gDy6hA3^L7DtvS_GnsLCCJre4;D~ws0U^Kpl=^1F;q_||;z-64@b8gG8tTIS zPKZ<4i;wC(XN2EH>id73{_@9~rhK<5=HsLl>}r5o1M%=^?#|F(g~4Z?5{&oV$(p$K zkf;lG3)?+Wy%)b`={ef&`-`8+^M?{k<^u~`cyd=xCA>{kiw2M1l2-q~6;^6d@FT}F z9m*gdjbM#mG8FjfT8E|>IMkWwIC}6^(!1(4Vj@Hx9h+}ETJw=t>Ig`y-6f40VkRF( zO!CaxMW2=1{vsFojQ6;_2Pz=lVq8@4G$@d2DAgN0@lVlh-@A@o90}Q>-q7i{tHB>E zQD_~Yxzi(6^>EZF0UJMhG5@4IlN4WLxy?lqzQ~JGu2=W67xy1T2BaW3PpAb*3=i1} z6kqJ0fk6Ln9SP(uaxSxu`q40Z2XD68ffuZSeVC;83x2?z5^3Np%=*wv>q;-QOGSqC zRdo53$2WGi5Vu+&hSqoif-J?WAEhPZ*pTkaO}TeHO50dItt@DIR_(oK+V*1yS?4nQ z`H*W9YZB0><0bo?I106?mqQI}hrF3o-5Pr^>=6`300}s%)VEw*$M@OE0*EiLvkfMOD9z>**1vHhav=HCkC@Zk3Z35(QK7IqRDMk~mGFk~5=GC}~qdtLVhrls=e0E1#!u!MhFLe?{X9ic# zgXkGRx18JAsFb7;hO;~tc_O#>P}D-lDn3AtP?sZcb+*6ZUTyQ2@B(D;6)PA4CrnuZ zl42>bX|ua}$+34PgPlCnB8AQmu{D|pLXuL{FrV|dVB*OJJTFg>;jb4GH|S`t5O8)# z*pOi?hi;pcG1`n?&aL22C0BsV9(2;Ebn10FGg+xo{oQSwcL(qt;1pgNTro%OWBs@t zW4;q*_GlTd2kH~a=j(}#gcmpUv%@4G@FwxSz@G6R2g=XYnHI!gNh5{OEO3?HaW!1up{H~eMbyv2>bSgx?T%FA9Ar^xACn%UPO@Bb zDZ{wJ=FxrCGKmTAJ$?sw7!|$}{IX7#z84bJ=3}f~hiAxMWWb}#Gw0l*S5Mua*fyL_$=TwI^~kq&C*BMJi2 z-yYE>Hi3(C2@g~Mq{G!)xKZBI-g*3VyK3z8&s!>d33b2E6F(%J|0NI_bD(wQBBYoB zZ`uTf*AeOm*q7)oi~YU8H{23-ntt=CT9^`+`A1($itZ3jSR7Gf<-oHWR2K}4{%JV< z6Ho%Nn5n9fJ=pEBU6AliHrgXy(z%qG;H~qUtn*!Y1lyZ-30@4^^fc&RtkgCcpSMNv z7%vLP#T7VW<=vy!g^u(#svNGcUGb-fWrk~j{o0=1(QM6XGHobVb1~X@GPSo5Wdepl zbKnDg2cr0DY`&DS&8qa#ZfsrRRbzw^$ChRBVc0Vgs3-I04SWbcK}Lfw;2GWMJH^m( zl=f+d(`^|Lwth2JRA)t)bF5Nq#C~M{9e|%_--KsGqC)>v6q`E^=qxZRv%gOZ+Ph7| zS>U&P9|32n&$OXVc28=*QuD9%j#QUeyJrUcf7l69bo#gK-Q1;LlklxAVkac9L68BQ zMD)Gw(m9V$|0&KTUcDb83<8H{NkSW)+K#;&=*3rs+06Zs*|f<39VpF?Q%R{WZhMaE z3n*{^M*@{{Do3zHm~*jbsXWtSZ_{FblLc7p9(wlxVH2Dd-I8V59HEGyzi@Dn7X}MT zF2xSf6=|RW>I-6|?4lDm=L)uDZV@}{E45RN6$03&!@SCCvoQw09Cohm*nyvk&WGH0 zhL!V&Sp*2@>oUbH(Hc{#gw*F3jpk;A%{?h(ID}@ENm|2By0ar_O*= zY{cM?SYIBA5_urKZJv@(+oSksq*(8kun@!*=iMrk81Lb{)XMLM3Ou~*!8g<(5@uFe z@u#>V!79ZXzaEMOjJ-XXVtAQxqrT9y*P*e%GjKR{U`YoW%Ye7q`ILMZ#@`-}w|63V zm(K%^Y+}I22iWA24P1$w(0FHHUA@L|styQ&rTbz}uC4LnG>xU_ismH;f~2%Tv^n}; z(HOZNa2w z7}ksgw8JZe`Q`>?BtV?$`?C^gSBwRKit35oxys`uOEb+|?CbCUL9V+9BCNg&TEqw$ zq{x#h$`|aNDp^KglZRhrjQo)j*|Xfx>thgk_(yu4@;d0%(;g2F@oV|ji zr1TFBrFQRytSoaBEA`PH(L9@`hsILtOJWCIXF7FmmVw|q7h_pfcfk`KQZJRcfgr{f zwdYAtnLuEiP4xEPa24pPm%@ci<@WtR8Vn*pkwm{v5JXl5-qwO^PUKa4+6ep1t0_} zJES+#Q#4D1@rNP7)L0)49YtO5l2Q9}{VMPIOq&?PERwTK{}*;`@PYpzXDfh+FDV@0 zUq0w1ftX8rA{VY=J5Sj`y#D^oo6Gq8r6`N>cc|mwRxKbV-}7y4_g5(QZ7i8u;Yk60 zhZgtn*2F>8(*C=H2304oY+LP$Idt>I^ZU(oKj6%545%LR-AR-N*nqIts;AG_=D*Lv zzBU6E1q>;eQC3`=<3NfnhSoc#)X`Ek4o*FD&~n@n5VzzsY8C+tJCRsrGim1wElLg;Q?sM=m(hiZ(MX+da~Vow|(X1)i?1lFm$cmGrrNCI7xoU@e!=b=VjYUi`SP!yOh0i8wj>)2zI8lMk& zZYmBnSN+th;}%4o!tcTL&kr;^9bliTWFHZt4xuL&5HBIN3}!8@WHJ0k@A)#6LOJ-{ zYd>}vdO?;r@FRuq9m7ujdE2vGqK-+=a-y$2;BSEhWE;_^2#D)aZ8EB1H*BU z68a|X4IYjnk1bhVS=CtxuB+F+;T;A8&9m}{h&2KIRmfz&;DULV#XNl5kBKd-ohTJX z2AGB8J2=?pQ^LgF62oVT+1pc{rAcXZox_)E_UEYszL-Wq&s+Ji5s z5p|Hzl&#f=+!L;N!3k|>o?<$UBRv5hx>|ZnZgtvr5A&3oF8>jHw&jemOi$C!8+*!? z>WPDRVq z4xnjkqX@N-;#^*vR9BVbCZ3!2UoqbE!86x6Vaf&{RA|lv12UL+X#OK^MoP`wIt`Y~ z?^4^zT!8uqFdE{q6WFM6iC|(#=yH_hc?oFd1jL;c^w#U}M8*^bx=NY>x`#;x1!8$7 zZD&xNro?FteO_j8IrKLxj<0~#(5|UG=)a=fp56tvjVoa6h{ZB1f%Wxu;UjSXGK2s4 z2fBK5QGR^(GfxW_wX7*Slv5Qq6vSCKLSKY%_pw@V>J8Gfo4f9!&i%%!1fXjH8Uy>T zu&=+)RwF^fCC+mtqKz2=)JZWBEe!^QvCYJah5j0w@rDY*r4bEt&%6U45iK5kD^+#- zfa24+jM!9!^c(ls@YPEz@(uEWXUZifzu6z$cp&>EC^)547iWay8P%C%#_b3*dUc6|oI* z$5buM&FdJdoiV%qPLGsz0mNk)?J^hU$WUzystlpn&ej+`@wwZJN$2{tHkytBC#;Fh zY>nGf^;`-51A<{|{PEl%7U<6*NqqjnINX|BFfU^7U_S(~dFRz-kjiA)ETjGPDWptS};&z8?Z?)7IMqf;MY+=*XheBXq>qw;L-{s#f)(sIcl_!1VWUy>f) zQhHrs;>}CqHIunYIk1JA@P@*|)F321SOE_Lp1e(>uk@;;%L>tR=+za>O!sb5~FA;lr?dQmxdMj94_KE)6eHGG| zeDbx^Jq><<5}^M)@^s9?+XjQOS(FI}WqcRV16Hu^Yyob@#vtZt=SESl zTCH28GNbT&N3pVr6dEi;v&{b>tE)aLd;&Lm5}%G2pWqhID3c(*tF{}qPXbTT7Xa!g zQ?j4_Mab-uP8qXc?TNnjD>R;I2l@S_r<`lGMT3_O|4TN)*s$)B1L zm=;jVveKruJ58rsZ5jM;(p^BH*6q-VlMU@^w91E;W}gp$A?#^i$%H3|in1>$h8PSq zmmRl$g2Z>Ay&wUESPi9dWvz2yY4qST(RDn}_?C#?a*c`1;m2wO@awAl{H2X2AA?lO zI&y3EMYnoEUog19eybPm-XE4-y0sr%Q%=dWFh`rAU+(OaghN)a->AJkK6RJXKELfP zg>h=FU?iOQcvyEEjB=rh%M8wedjmk{iCX!f;D=;BkOoE0u^S1WO0hf9e(oru7ipGeo!1L-V}k5 z;T1Ap7xTzFqaK$!etST9PVn&}!#qx4i{2;%@G%m{qr&z3Cw_8Dy=dS9Sls z%<7;?X}#1=<3yu*oU9|i`1tzJ& zJ^nT;fUU)1UvvLEaWRde&7C{U{e%AqO84Z5y)u4HF8{zX_z%vfH!jI>kb&O1eQI68 z^pk)q`-Z*97_Ub~`#*7%Ch!7tY3Z9sFn_CimqGMRG6+P_Vi1`nvFhs{It%uZ*fG7C zOGH3n%xA#j11uED&9rcB{To!q67y{jB~nXG%ACGASaEcN?F}&Y?8iKCDs(Kyf;}!2L>yBz53mVATOA=l339u*c znhP2|%HB5xHXOuNW0|y!sLb(&`q1wU$Gd$#mlt2A`mzsyKKC9~l_S90I}m^t2Guy; zS;t2`gQN~|KPg-=n%xn^kc^+`2uFW2sObEN%6jxI2-Fna3sd5jBi-qBjo9P9-ZgjK z)jR`~gqpa^z91)xg4l!iCP2b$%uimulGKzjcQRb{ny(Cg=;-A$;zN?p>``F`{>AD2wNi3~rQ1bvF?$n3G;Hy9u<8LqkP#0Q~KL0S1DfOS2w3b}<}V zQ;Wk1)^X^vpS;gD@A;}<4q5#^V!lUay8oP1tZ6N(`p zLV|rpU=zj)=`{JH2+ucO-RKX&Mjg$r(8*R0X03-!~6N*%0$YA<^LdoFQDUi`Qu5^ z1@<@}WRvc!X1fchV=_iuVx69z#EDJI_M|xQZ@z|wK-}`NyK~3t)hQ+lQ@)7d9AQ4^ zUbQ+++Wwiu3xE<5l|x;J7`A0gR}HLx#Mn4Uy}oqy>>cpyUxOx02%M}~fn`~3Reovl z&OW(zHwV50AS1rB4L9m(9Y1e!ucee^8Y8Z8oT~ijpnwtSoQaU&P(j{kgi5iZB`hi)6ORf{`U0HKlk z$z;;O)2_brqXt{-q79I8UKX08Ti0?Bi82IwOpBjV5lK9IL5O(xm1~)zpT;26&ogIcz`n0u)+t=z# zY8>`epMM1Y4??OXQ>?yZ`@FkJ=0z#h4zpK0bKkn8V@Mh z-kh-(Z`qYK4q6G_a)f|iqNexE2Dl_+q9?t za};JZ{7wQ@*d>^9!=&9|YAGE&`<+_Bj_;J;Siydfq}DCGZ!#o+gk>;r`#B#Mp$vk~ zcWk!S%KNO1cUETZYS1N!WTE;692gj55yDOB44&Si(KrmeZcigZA1G5lf!z3cTy@9l={Zu5u&%5Y``P}WWH zGzLnr0T(*NCqHcMQ236x2%&ALN&C2n3$`9k73I7D=41Qs`Zd|F!+r17*2O#khJS#n z>#RH;NcSw7mwMz|sJOK{Ji!GGZm`Cc!657Yu%j57K>X3C_07Z3XA=Vw&_xoQ8pr3r z*k1^)s&OF7wx~?b#W`Zumy8`sjnsWWtNDoa_Ka}5(KBaH>v)-*1^`zy_zzM|GCXiJ#q6{f}!k3#~Z1H_x$-h)vt4R9iXPI8Yeh?Fk}B%nHZHf`y^bcUVG z5sYT+IH&)@Em03W-*^!+rCTE5s8iaWZKg0Csfje3kSZP{cx<8YLyqAkt{>3vQtb!yIc+?T08>_PpL*Z|{?}yj4Z}LJ=|AEi+IwbVS zm*CK1vwgWqM@Ez=SA(cILRk1*Lg`&h4m_ST_K6?@5~yXXN7GOY5tpKTd`PNkG_^ zVik!Ms502|?%~wwq2%w6DUmuFoC}n+hUb&^2}3dGlf&eh_s*3p-AD07Kn3YkBxo# zGD-1``yUX(^A#t+EM*Xz!WGmGuH^Nt|CqE@jGK28`);}^C-;!oaTh3Ld!6}6Ue5fi z^tKzQ1M%1J0=TadtCZF)rrrD@=5f?HG;q)LTY>zmY&2CW{YZL{2DNu3ca;JI*I1;A zy*(~<+;!W+1Xc|N9sVPa3-PkI$L9=MtX{}O_glf@!tgzq4TPZSaZR`B_DgM2 z93U}HZ`}Tsj)v=W#P}$+^K;1Akw#kNj=uG1lVgVvjey3Bv!+dtnfp$0-%$yw0Oslk zaa|TFgLOFD@zpOf;qAbYwBL*y3ywYKFaJTTk*QOF=?`ZMdNSs+1z8V^zM(?~Af{{* z;8Va{C%(j4fwK%?++_?9Th@(Q* zlO}@nrkSQV$T}&CGFZ(BZ9H_uUJ-rY9SrgN?zwksoF}@#KrlP=XiL>}B+8qW{?0oB z!m7{3#kF2>)yUe_EG2IO7{st3nkQ%1*oe=>PxHYIk%3F8=cXUshrI+_=i1H02GOvC zjG(6+be7mcQ?}9{*tXGh;>-yI&hJ?SA^{X*oaMfXP@V13Vp%o^j;hnwlh1zf?CoR* zkD=H8Di9L!dm*f~R?4Ra$zJ4{cdXvVk^&sbqRRGm;@ERBaSFm`3vm2@BlsBayqv$d zf2%*er5Lw5VN(^?)dP+E@wKap?=RF?&ixt3JfO&F*xuyTt`^c@nKy`MUWaz$2EH@X z5Q0c?DNd+f5!UESb;aX9Ry#weP4&8UC)5^as;jFcSygN`h~e07B6gfCEIYTpf|_o9 zZ~&~h8WPZ;3F!(@kfvB)T~)2bGhtS>0igaGbD=Au1>_oTD!&FET~KKgc>?jf1Qa z;t>+Zk7Lp=g|2p=;eTV77|{jv+;D-gbG3hv-i3z=9sE^0`HU43=u+QOakb0@EiYo; zcjB(k>S5Xc^uKuabr#XQY#>W#LPzq(Sg_s*BnO_fqL%-MbLREVymovapr5YMqrcbI z+o@_Cp)T!K`HEq8!+>d%yjYay$dZ&dz52+8a+Ucs^CXBlL;wmwxEwEpbCcH_)bmsn zw9w1}j?SeU$^iI(2^&k|A>opdr_4*@ z(?ZnoqJ{{r^(qM@vr&mehEoK?Px>5t$`=S%eYOua;Zh2s=nR+xBzV z@N}f?!8DVp!i$ie-cS1p%0FKM9Rl#E&y9+*EJIO)Z+jD1gokmE?S*lv=+^Wc`ysE8 z@Ro9;h@M+H!kgDwHw(No&~NWA6u**cmj^hg1T;)c|I;g z^`wabXrFmf}&eti44yf!jthJNk5Ys7Sb2c zzgEab>YXWl6sf7+pzP?lAKB3BCJy9R2>Q!mOVl(fc&!Y4_Pux!!byAwzu~{Q)Lm%S zWgq>m{x%rTHex0pa*bI1C%}yJz6%s$!X8HKAH~IOJC{dHfYMJYlmjnM9R2m$FA?tt zxWtL?Xn|Y5b1G~|eNfO^*nrxtqV?7z9 zH>cc%%=MHk56l>^gM2!H(rOg+_oA-h2?j-D*(ZGk6p@sN$I_n3R*Bt32cETqe0^tl z;mk`42_*QnqFE~N);&BWUXKhgZ~yH@o+J$hPU2;cawo!r#8AMqHuFk;d1}oSvpk|7 zVzrB^?ezh~l0ZoXjv}2EOkGIGjRZxn=O);JNuU!pmesd&e;qSP)Z}_V8J@l^%47tm zY(*a8vhaBED>t`e;#ZEagTtuTO@#ZU4#exVZrIK7dPA%5J1X~9ZNS|RvH_MTgojsc z>BBY3u^EO>LM{3izpVLy1C&vXzRU0W?2K!1d;Rdwd-#A2kTJzB85C&E2rKb*HA*W? zwAM|X#(wLD$&jGDLvvDJ&6S^MWJ|HMQNTI-t{4lv#kD#r-K^96 z^?mfl$!GpL&`~dEaiP{+Phafu2M)Hj0ZCvG)gWDpylQR+Z#ct;13;=Vtepaj zc6RVjJoq{uB9RSRWLO`YT$(Ztr;zzt0y%knEU?}LyN+Z!QZMA(I@k($@0GXa@^vOJ zXlLGtf@r>X^rY4jxiIY%75{o4UvJUj${^FOrx9_3LmLh44^MH*j1iZ^PO&P+Kv;{|VR9tW)a8TMmA2{dhxsBSF6?Z3C}3VgEThA& zN)qs)zP?`F<;n4(L#laO>em~#{)6C&oE?!`_Y{*Z|KXAEj@}jJG0+QH5P#mG23*)_ z`J5rS?!4d}AtU8$=Jw&qJ2PjtltLiUPNlKB_VypcnZNb(A$G(8QrYL}ueFwSuX)q$ zkPcSy+v4S9abx}GA@_2GkJ}^(vLMaB&E!sFw}1!|aDH#;H!58#Lxc7c6aI;?FiwDj z@tU)CJj>XcgLS=tc3a#{51f zIT29{%?*6EPJ1pFy)_qaL!qZ)5fjD;7hcn-Vs8MPZ*FsW$NYm2_ETdlnsh3yi(h4)EJcH-*qoWhA_ve37ZS>gvKOw{i_1Cq3fclL;s+0I z79PM>4YM8yJBQ&Ge)LkbEFGPvb6J6rXyCOeWbX+(MJF!pqVrv)cjX4Z_iWInyA>T? zCg8pO$B(Bh`NzNXsf~WIC$+h0PyXw^ekT* z_jMgU2-dcX{xrXhP%tn4{5<~teGn|<$zXZDPowY+8WW#G#klZ%NZ4)0nX9}WjQ;t! zP#o%)rcsy5j)KSUy$qs?jG|r}ktslarCZ+#jBvlP4+Du#QgtA5uf5xT_f_91|B=Z! z2x;Q|q1#36Ut^tAS%PzJC@KgKXtkY`9ae(evy=zae&-7eh*DAHV7N&FQG&S1L7mJ}o?*EkGzF41C+X;kkR zGs*Wjb9%B9-0^>)YlU$UN=^Ivcxs<~l+m+eTlMex+A8XOhHSAZ{Mi~8Ku4v?_(CX~ zeC(8Q&K}E$g|G7y2V5ubAgB3fci>sqYnp|QYk7WlvssG)0%H3nDonZG{iP55@05H- z6lr_=0?!1&zUun{jRamE0OD7!!OtWUjKoif$lw}oJWy|;!fU*oVA zW!mP>BMACWld=T#PpeWHclh$G3MAu{*yvfaZZpnqJ4iN-fqvvK3*ZrQjAEkFm}P*sq&shIf*<=6fuuS)>`tzW#19O`)^zVy zFJmNzTfN!Z&y;|^E(){F@DBfa`CoNqLR+GGHvcsXxhrSG=1v%7Kym{nLNnjzi)}r4 z3nogNoV|NKclz@3XlM;FsIk<&rR5UEv?~huU;rn1@VWinIk{(=t&XU@rx2 zbAWrT@Y_(<`Ou@wZfRDQEA{6fp#zV>!Dj#MNSfg5P`5}UDc-)vltxd_!ZZA7W)JWS z>02V*u=levN^`$m+7neKz-Iam0`>RdxdfbfEdD0Ug?_$9pc+PM7Q0>}_ z2ln6UggI>n<^=lif^{An6^N!_pPJ9hX2TCgRGJ=5JUQcH9xVa+-~tG7&Yni0@BIX*n94d zTZ;1`e6P(cUp@Um8J(ADG{;?}uC_)Rl7%xvGhL9qWaL^P7%d0%@2d^#bvGF`0y%;g zglb?`(`(ZtI6X>5y8M&%7j^5f@)GK?WvG% zSS)K{oc#1@rrwwQrilItE~4WxKxU^2QXI+dBLjD;AB~z@-CZptmUAf|LlOo7As<5i z2BHjW=R@SsluECmZe^;#Us|N5GTky36Zli_;d|Tp=jPGGl-&aQW1(!&sE{YK@~mWK zbnP^+b5)HP97vbA#wVWuv5s#$zt$MIjZACF{@KxTzqqs&eg(`hL%5820Wz}v@>7ddOHImeKy8%)avaN`K#d80X97rqw}X1@u^!u*FsT3*V*OZ&mS_^AIKp5vDA2 z?YN`xviEFZWpMG7se=8t7U!UQ zqTpHS$J z%lFAyq_MawRy$Zsc^J&0CwdPKtOsipM}}YiEvjtEMAsY3eU6^6Rm(S5yQ3U>6rq4k z-&6phu5zAN7G%`wKOH<&k+~T^1#;|+<&LCdeBXI(*0{EmwI52_bc@slS4*0CxMw;0 zW6UHNyMll%OXv>Va53FWeDf*772@YCvHsR5`z`jsvu||i6s;^U1ZXTScw|d44o`J< zzN|6qI{VaA(+bvu5M7@gC&tnCreVDy0+Q;(KA48H}zNNr!VG&5q!rn2m@o@ zl&qFre2aF}rBCr!w!ck|@JFWHA8RmaId>nHUTbF=(kFUh}loLeE zbAQo{jJE6_AxjJ*Q6b#?3OLIq!mxK8cT3J<@{$Kgyw^J&roqx1Rc_oebgP zac8m7RNa?Zf1+qz$Dd_hel6-=O`b4T&@SY4fm||e_GOkm`}8obav&@nD-{Lo3Fd#H zT_%mNL->S$`T8UZ`~rhl{F&P6{vPLU_1l%bGb7>)JmtRS_Odp)0uok=JXh#7s@}VK z0eifJvBOQ_JPxRW)-|3m8z8Ktcb)^^M~?IzdGZXHA>Oxv*r$O;rvtsN^v-vO)k4`lEJ zz>a8P!JAUBp?`=hEVm0i!{nu8P9KvE?*FkbsqN82kX2$8AI4u zhpxXM2~JnX+hj;eiaOmI92T}*R@w2Lv%gy>boKz{h`8+?AN&-l7!sQFo$%PeJ#yXk z;7%=vx=YWSdX5K5Kw(Qai@^%8=WS&)5M;Q$6%(Qe=!5bDqO{p2vZr5G9yOtI>d-MdW_76Z(=N@kBv)xAfq z3`YPl+a8p#7xAX{>1Bbvkl8v-_Ay+?jo#fw5?W_m3CbY-0@g(OI!*FEB=Y4E|D9;< zEqXWXBL~gR$g6m4&@6Hq92N>db+Zy3A=-xPrJCNA z>vr9NvZHZ2DFFLP!`Fg_t77Cqq}Cm`&Z;G2Dbm-8%FHgyQ9N1GSW>BPB2Hz3jZUB9 z&GObj*p174wr+XplUMmE7r=;McQ;2+9%xh>vI<`>PCv{Szhh;2*@KU-8L_|kVEoS) z-{AOvvB+>WMs(}?m$h#s;wnrUe1N(@I`18p#CkRCGVmh@uHF}M3SK^pFL=2V-%$!K zn~Z_hRCQB=pZ?{tZW!zX$25ECRJQYmeK3%pE63V7t7<(5qMDq&0yOEzrblLy6rv&z zRu8CaOlcV`p4ns=i^fi8qkX80RyzrIvMn{1{fuR|@lTaQ8zqu2C<5Qg+gZMna{5Mb z-j2IZUO$E0JMK4{bVLR33*?e55dRxgxLt>3)b>0+oo62`0;s(kz?(ZdakF7n?Go>N zFktpV`##k;KYDgT0O-9+d%`dU^$pJL3Zpl-Ga!^w#*LS9O;VO}B#^0y_{d^l)DFK? z&rNSnLV^r|kDRMj4cKEx(E(uVoN56%y{Yy?7dFeJ)iv${$YdV`a2|IoXdkOmoYgOo zrt-iWc%T0CMaYAOS60*J;T=iW!De6p$x}O*R_z(F@5_ViYov|eA$U8;(w$w!*OR;; z52wavwJtFV=_*{8hqg>D}!RQS?V}$;@dzV>89#HjHiXYsG zz$7}pl-#Ai+y=0&9<<|gsS-|E4&14NR1y6v7Wtku_1VvOAIM(w??CHw zJ;5gg=!JQTT#((b0iv6s4Qc9)6Uaoe|EWg`7RvK|RSdE@_CW#-B9h!)1zYu2!BdmH z6Zp8D))tw(b49DS&{Y8R3~l@FjbND}mG5Zp4M@;g z!!m(EV5BE+l56$Y++X5$Q*-b)qK? z@_u{yEWs6laQy^QeRncGAQgP@K8_J2P}*c9b=kbILHSDkRS)z;Z#EjpqoS|ZXxWYq zI?O3DdrSU)x{L?J=zoxXVF&5gTiQFAHs!&~n4n|9Flmm*4ol&PUjPC|TDa@J9DVBR zse@kF2sMvVA<@NKAP;KDi$`;vJx#6YGqKCA(d@FJq-h~YFgl*(vJx916R20C?X@5m zN@bti$~Uc&>xP-q&!1c;h}jKq2@*w~#lF^3%Gv8Isw-V*8fwHKzTB79wdsW5QXoqp zkQ4MCGP9K!0?uufnxd3s!asA}VMypqD~aE65a)C)P9XIC()j9crGJ)s1OePHo$X#? zk9|rDM@M(>f?Vld=bve~kazgu$@XqoA4wnrCO{Gd&x!2$waSQPol6`petj=wtl0H)jG7kz#Q-0O&*%<<0An&p0r4c}2MKNz|E637Mn)F34 zmvc2QibraggBpg9HlWpu{1Q+mzKN6=AqlxbM9Ve4b{O2T$Ugg1nE}jAa&w)1|n@^%e~ z+JZ~j;g*My&p?W8%3?MUV#Wf6-yfvjwmQ+*PrmkBHvUx#;DgXGR)dhqcrVt?g1Xu= z!drqkF?cVquDw3LiNk{zV~^oW!A}13)29mfUXF`urHZ&UCL8k)3DLRW-+AO*wa7v4 z-`Amctze1Nxp&K!DzO@O){wu<5!)2VDpeF0(KFO~$1o=B;~C?9NTu8n7L4X%?m=Q` z^q~XPAmQie4cx_LY@P1?-@*E`u3w-3oL0S1cY=5RpG~MM9-F&hP0=y;DMhDy3Vyk3 zwaeVxv&7%xVn^p1_DFdS{0b{TI)41kjqS&#(qE!?Yit2tLHR5(z12w~^!+6JaDF*y zwzNr7g-_Aw^u%fUC2e5a4A7`>ZwCvZ0!!N?ES&Lmdw%vDQq%UA`hSp600xe5d>DUw zIeS1--XZN#s=1B`7y(zmN21KRsT@;K6OUgq9W!6Yfe^d1_7@9zwz2yQz_i0Ip%o-3 zL^DdR#hJO<+o!8Pyz@gyD>4|Q*l$qxa!vGfJTMMufZLwRE#>``J?y z&%SIOP1Hx^``*#9#=~+cm;?=XR4clmapzO2??+#&GwgR?1gfIqq2OqW$&Le6uR)}z zvh=``PU$m(O@U46j(f6oz>aUZ z%q)hWS=@-63f(m47;lNU92@`oV%*Z~z_T61wv^wYF3t|Xa_Sv*77HcpdEdCyO&E-k z_)ihOfEo;%s7lVEx(C}8G%EE})L^rku(~Hs8flW^2mB5=v!ZjYrWA8x38PN%55Ec0 zdkxi(l7M4?Z~GL6bR-|1OHqh$E82p69E}!q+n2b1FTe(58&}t6{Hmz+r*7=N&vJl* z!@%^{e484UzgTtRsj~Y_`h9g4aUb>^TGVAPB5{I`XW^Rspk&&s_=+*ZtLB`sHkRP2 z800-hqgF+F0}zfI7@#WG0X(<*h3+Ed$%<;a!PcRGL)KsmLgxp|_Ifz>B8T_$y$J?y zNe~P0H8vCIus|XHP9NPO4jHY7sLcUDSR5R0B3lnOeK_*0S>%<_^+2!(5`eYOaW;`3 zv*#`e8JznwYR6^Kws!{Hbu(?Ok65m{!jo23t~{~^vSE=xl>zpIkyv2BA=W3$bY$;W z5gaL!|C#8K?Hn1KgZd(Cs_yPTaY&D*9!SNMu(#G z*}tYZ9cz1_3wE4XqEm*|VjP!3qWk|di?#qGgq|wJiRi?Q-5Pi?q6qX&^rv8usd`Jf zvUE?*uHvoNxTF*OWdo(?^OM{#+!sI4r&(AkIPyb)?Ms|IvI0;aISx((t-f#tCq*Q`EV)h?aL42QgRFcHyu)(viI0r%!k zKz&_X#N)Ytx-S^+6?Jj+M^4iF@w-Pm0S2R6{Zx}HJZ$wIKZ5o^U#egNA35%}`BNFV z?*IiB43ddHm`y~d1i50y@iPCEbp4@HR2?pDYzN=#GoXi+q-R(OuIbe0ZrdnD{AfVg zKacL_sOw}^;USwVG6EMRkPoqE&&4BJF2mAtNw(722eP|0Z48-@PBW3jZ4FqTGoE+f zaJ%Oj4iwFiUJGsUh&Np+YjYMnGluH__7YHxQsfpLZzCu8<&$q+i|<{<5sIFQ&#{C7 zkZ(KSHc+)(apdc3aeB>J)ygkObDBb`@m?;Exx14qb~`>p330&Is2(>{G1O<8;k&GdGrbH~BF1pI??F zp|9mL4ZQOywt(TmG+DAYTQ{>gzM&w6U?Wv&?%V!H=^}Uz0I9&H0^as1JmWGVP?762 z;I{_Kt}Vx}9f(iPFZl0o@BuxY8%vu|W>=tPr24eDRY$X{E86u5CnJR430^~PNCrVXMSSH}@SZN9v1@hx z3-Lr$7A3Qn@D!IQ2Lbjd^wdJ(6t9S2bIq>&=tU+2x=OE>09+r<*HQ2Y8xG~lIiA-n%)4#r+mRyJsM1(qOc*G zp+}N^u<_wW$KnNFj3Zs8f(TObuV~lTJHe;Sj-(+^ys$E1^BTi+1c2ECgdfb=$_~tA zjj|2PS3G9+=Ge1fRam`0%(w;#%zdgI-I67=sd6(4fb1R&C$dTfyiL zWELb{x(NHbs1{h#Yzvl0E}aubV4}dOeov}=S+kq zYQUs@OkH0pS*3t4y5UH?p}(^m%b7r&Z~(;3uPxf!swC6RHnu02{A5-@7l%SiJ+FAU zNn2!RJ%$DlBV}YQG%?lpFh%+Fl2LAD0x+ungMIh)W$ED!euJd)&<B~%XPR5}2Dkno+P-;U(@FtnT? zXpV0KuOJ(l6j{k9PF?$TQry{QCjIEKuwp3mXRz%v-IEi)@VN$D#T8nfm%(%Ekh=~2 z3&1&Kk>vic*(l3Ow7pvu6XLFqFmSe<8M-(~{d`L=82q4Pbu}KR2pDv`u)SY3z>;7q zo7`5VbEoVvrTS0tT5;p4l7JigXT%_iY7D;It|=Ns^)8op6!^P?7U9Gq~y3gn# z`h?MdZ?PjS;cLdt#U+7&NHgh;;w9<96kBM^~V_5N=<^RT(IQoa2N~Q!ucOHPnytBY5 z(e-+p+rv@BUi+m?#eMyG#TG5dcS<(`k{+Hvp3PmXZT%7}BhV@WUL-B-u`iV3th56j z`ruWd58 z!c%TWIOSk29tDKfB`O*nvJq{+83Mu)q{OHv9|X(?r1Cdc|IUYh?A7CUT19irec2@G z^})KGF>Feprbob-<1(h(BRiYRiy(p<^*GgjcXY1NmCog@yfHD7df&FJ01fLz!229ufMmxOng^MH=b)PC}7)y#Rm z7ho^lpa6t-Pc{5ORR!G|zf`6#sTEVzB#O+ZZY=EUE8Zy`l}!XyQMFK*S#5%`9?26e z;m86!KiB+~iZ50EQ@D=ogXT_@i|!JhLtNl$&bokA9d@FOXpPHNq1W}r`+YqvPw;<4 z{mDMChE8p7`%wYI{Z}j%>-8j_{+$;vYs_?L)kpvC?W)wU`JvCNQtvqTy5L(8-aYq> z>Ca1+fSiwY@GvDpbziV}LyvUEv4=cTQHUnYrIX%aQLT>}=qMOV*^DYT>s!bx)&Jx<`JVm=>d)3ck^WR;l0Snzj z=zc1j6sDURlV_0)BR0k#@W0Kz(Tr)N@NZ1QV`NG)(0$d z{cg94J0qC~H>)pMihca03Pr3%JXZKug}GqI?UDj&5$lP6-b(ocD|jH3H?7KL7W4rG zV$5xTeUJ9ew%4O*RaK*Dr>Yvf!)>{cSG5%Q>OXh9S4j{VtXn4ufne;z)a+W_-`O=4 zwH089F#cRV4-k!l1wENcVl3Sp|Jf2x^G3}xrm%ccxYZ1<7b?Z3qs8A0-X3)XE>}>f z5LtES=;%o0+w$LK86cNXSD{HvKG{BJD4_Pvuv+IQZ2+BxGxW>KKh>AhFCwZOStuL| zSiC*=BB~vBPAY>gq)uWb0IOOs9f{d|&Q!nbr*8)bCp__$gFYapEQY1D+%x81vT^3& z!y>vMZ#M{+)fduGJ*W^-$)h)I6YVjpO0T;RYSi#Aw3x0ll(Ehx_p^1(HLHfj^X6X! zO$+@!868&o_*dthTjPABU62ba@R4z3ce!(AjV<`0O;-O%e!P?)4iyYS>eWgV}r}zF{e@ zwYKtmU2Wov2L~qXfmkf2uOXza3nnXmjZ*`L7;Hx-m_k-Kvy)P0pcqZ5Ht58@^Wyn% zt0r6d0VE=1r2V1$2#D3;p#yrFzp}9am#;8fww;!A=?Q{&}ONC z|1Dd>=Voa`*(bpiAO_Qcrq_cdz4BX6wK1P$^LbJEiW;ZTk?WH|bDIjixTM0PGlmQe z*z8SzZ>pJXhEdHihbjX7LN%yk?VaK%S9zIicsTCyk`d8QsRAM$9Cb-;_ZaAH7qm8h zeGCl5C#f|#ovN|h=kBpxzxQ*x7`o9@e7}smb-G>S)aa0@$e`IPV5Tk_9bZHpSXY&@ zm<F%ixceK!Ho6Y(2`Uoe+vER9r-IZ?guyy8px^1eD-@6eL zJ`Tmv&-k2XeW_wUFRA3*MG~8>g#hguz)5CHhuW@!OfZ8lF~-4vB0;AN%HEE9k%gc< zv9k_c022diM-;rBJEh-jyuOo%Q|`&>-T%Y6xJUDbP1*43EW$fpey+<< zuHtK$Hlr+??;tZ@PkvF&sX?r#MJi-Aw-iqTf0QQU1U0OH+OhMIOpMYL?U^#Ee+WO_ z**!hNfjR*(c_^5b?Fl|nh7rYz`tCyI@7E7*K2qjyYR1|3gW43_AFbg_RO9?59qkw& zJX^;TqFeHnnCV~DH#kRi4*;c91QkCq-nJfTDIfpQa@^9j?6Z4SOux;5dzR<-b_Ng7 zxfxS;DY1<`tqxNt&LvAuDf3rv9Y(j#G=g@6W=!R7Bwv%2F0Zj?!O(xoJDKwff##ea zL)-0yHQZfW2gF)C*OOg^F3-&!Cyj1=QwRUy=S5V7VZ__sNBth_x8?fAe}-Ru3Db~5 z0`TSfJt>QK5pzl%+6+nzR;iI9%@YtG|FYZa&jGa^k``8uf^X$fwfP;MKF8bZ-NZzp>1wDqOl*J?A(0O8Mv4Gpx}s0?5CboS)-G#T#%M&=g$VfXFKYtHwx z5N9sZ@#E$k?(;Ye@UXQm4DGfPP#a?h^5H4H4QB$R{MPg8GfF|6%3?gAUdAhYT1lC{ zD6g|^88iR2_n^UeDO9f{=&v)8(f$iWO zO_CjQ`s1(Vno_-a|0tM^vSS-lcePr6jRYKt82&C{&YsbUFt2&Wry%+_^n@9jbk-*E!36{lx8!(TND$m&#zZP zvC&ceMO+X00VSPltDz6u&Cz_7A?a6N@Bx3JJ48Pp$RxjMeKO-u@xY_3>u`_@5n&Y@ zRHw>2Hk`6yd1y9_*^>zv2!~2)Wp{~W!f&~BEJ}>%L5#zbEA`Hd{JBS(b80|zzUR*5 z&SN?=sNfxF*=kF$-wf`oUR%xPy4+-y_vjW0d{lM%qN{G*TI0-pSRLJc?Y|>#qE^f~ zbE$!UzS$qA6KRW}v}6H3;2r&ezj?=AX?$x44J3k+y!f0k*UO?F!QB8`?m}vQU`Ff@ z{sm+eAiCNtMm!1?#Hafg^3fMd9kSjz6WM+UK86AClW1+<&E-Q@BOx(d@T~V;oa*ea zI>&N7Oc2)QoK*`$SIJg!AvsMLFW@cWx!j&U&J^=k__V>7#RK_Aw&1y(t6yZitUKWT zX07gDux}s;6up^B7hC#WawH^f)S&^CvJl8VIj(m^@C*3Gu*6`L3i$1|iuE?lDhRc; zl^{_vJQ>rL3HheXSi~A==d6s50_kkMBHXEa}f z3pL0OFAO~Z4W~{?C`Ohlk`+>Tn- z7oP5Ii9E}Ivboe2PG7TfyYxxCs*7VTW76ccR+!tSXJUmpTlzN~*M2RSIQyvldR-g2 zi$nQaq-GMWl#B1&8v9xWY|uZbx!J;EWqPFK!deLN304-V?y4)feA<%BOn{aJ4OqKh zPQNHuveqiWlMUt=&VH<3ELEHWw{SJJyAY5?abJCApyAG{LebU{z~scI&%}G%ljm;@ zAnx{Mma0xZd52k3ITcDqYygelVkYFNt#@vCDyU6c%7&OZXDA5;afAnPWFx6nXHL;x z!ZM0V2Uk7jgD$;5(-;5`LEg(Vnd2HgJ$K6p90s`kSy*|p=ITH=(!@Pb=*VM>mIkXX z&QkyN#uUr?AO6_O$vSPbzw$sir5uGf^C$x@o^h`a27B{X8FDHo7({B70UiEB>IO+nz)NodRJxQ+g)l;w6eNZ_V^SI zM+b144z$CC=a;qJuZ@lk54G(LqJ~;y%cqVNc&eoV)}D=-xy8iu+sBxk$&Fpf z@^t{RoM1;FcsOvd)mipQZV{&8|3QvTWXb2dL%apKE*Flxm+f%f5B4h{RyX@5g~+fZ zkEI>0jrrkvE9$!^K>JT25uT18+VZ{#l9Vf+8_aC#O9fG4zX`bP?G^pECWDiI03N6; zbvEDD#j?XR5SfGWkpOGSJ$?SrAGZ9-Ef`70nKYQ?f?ARp=(==*@0b{pbBhKPbP4v)O7dwyP^jP7? z;oNc?WOPJhlh z^9ALF(1mW-P~YVntp&Hu0uch7qOu&i{rI`B0aBp{E2D=>H`+;(+_$1{m7UG08gF?w zIPTbDIaLvol8*mn(y#};)9e{DrW4ctLhR5Lfd z7hZ*b7{^xqSg%-L3Ut;9_xFc43c}3z&!gwJ!0@FnDY%dhny=G74n zz-y>4e#a6Kp&YqK=H>_V3U6Msf^p4tBpZ#BWit;L5H`oA{_(;840b#d!dU(oz?}%E zim6pc{>b_n144em2)&q0Bb!9k;934%9C7vr^=Bn$cAMW$K-Cgr_mXt8?!F8m_II(j zbt#v;yp-HFYxu{U{g0Azzp9FM0#?QqCX!L!Ap{fDenqcNI5VAu73!405HPA80D73s zgl-V~A69ORyc{o|&*s)OY$iMXo~w*gv+c>hAcsq-tBs+N@|x%=Cp1jfaKIn z+&^XfnpXOg7pcNs`x-IW(=R}SNpL3JC3HXs(}aG0V?j)JZ-iQX@9s0jHf6U=S;wuD zU3_3!481X^JyiV~;f>o=M@(tJx~vRl-=gcP!5kC#6+Oxjf~YG}OJuc3=AQ+4UUzYD zO>hMc2pmdY!bY9CO@zko4iVl;c*BMk(zDd&LNUN!uM2Osl})qeI+GXU3`Eu`tbYQS z?GVnZ5p?wc)+N>_b564dlZp<%k2(F+bL*n1pI_D&hnv!Qo<2ADiU4G@rb6%0KIM6A zGyNILi=GaBX%Kq$ZOmWU>v+0Ez)Tms_X?di6V0&@{1>88Wn!S&(7^(6R~w{ttSbA7_VdgT?uRlaKr;J_-&iMDoQA+gs`?(_%RDeu52h~YEy3-a^Z ziKI*69-pnx<_jYK%74yGnA+G}vGHg}P-x5$pz(7@udIaUj^P9KnqAIW0_7<#xkcNT zKV!XWlo?&q<27Q=vAIn2Aio{+Wqu}|5yn&I|1~gDfBj-`of{6C@cES*7>ajT)j!PO znQ&djT>CtlKgh*JqW!lmRRP<&`)vDF_m*5`WqF10gHWr(x;#+mEmMb}qN^TjiBna+ zcj>6z6s%qhGm~HlAP>{Li&a8T1Aioiu3=GED^f(#1ro%o0wndE1+i!B#Li3MbC-n4 zBGq5&a3QTfomn4*K(FoTU_mk`Ll)2P;nT6uW5EIqS_&g(V2c1fZFJ0JXohequ!Kpt4ec{(8rrQpGVb&O#6)&N z;8BzebWyYu2? z9Ro#X{{EbqbPZ3TlrtS5&y;bl61-{hjk@rw0w?4Ms{L1>uVv(0wx%R56ps2(1`ObJncztTqO)7jcs~R*IBZmu)1{~cy#JUHDFOgWcHO;eY zJ_o9vb-oY**wD4KY~Ja~(l%A`VA+NQ49{}%(6>m`qYS~}>e990qmF&|0o>9MtjVma zlvHSmMn${J)pwV~%CXWOsaw4mwd9}oeiWpZ&6l@TD&%k9GA8kbq8FauUQBg8cW3So zqQVkEn^#1FDPhr;o$^Ng55~#48id7FZTy7-0D%HRZ+1-KmSI+(Hr|R4g3nH0w0h`~ z4%f@N`0PUrnh>n9@@G|<30lIgHhH&f{@>qF#5hi|W}WQL5^s5NvgVmX@<+tKV1{^| zmLZjL=F6rF?IMXS0W>}!d?_ZsJD)UsjqY;b$s^``CV*@f;Mo-2?W(f-?(eJO#sZIQ z>Wf;kSEZ}6NP(yTS&TzuzE7zSCDh*?z~s@OtHr&MTrk+z4#JTRy9`d z^ur{czg(4af`@hah!!C|Uep(Ti9?)<%7<4|Dx%q$ebC9>9F< z_xeHPF5w~&(PxozmMnk28sD=^ba2+j1DsROhdMBMHg{m?y7P%vgi#cbz+GmwAUJq& zpzh_)TIbQ4FJJ>L!;ANgjG%$S z>X!N}K;uw{8q}+!e7+_BOqHz3=DjmyLK4g+?2y~56la(V*?gZd575uj<0GRrihY-_ zC9(T+$DdRH8s+T3^@!h$Gdoci4oLrLB!)Y|e|X$HPes2qWzQr$@OW@eA(;qf9Z%{2 z#iaSh=%R0-&|Sn}6)gZWp9iG4UemgE7bu)j^TZ$vDfxLmP^wXk<7@rzip5*LU4FGG z?3v;3M_<3r34do-TL@a1R8nQ*LT`X^Sjk|rBO2jSK-%@v7g;47z<8bbmUu3CL2)0Q zq1xAcWJpxdjy4B+Gru?RxNsNEDLlGX0&-DOeI^dPHUIDKQXlL^))7LCc~`V{LRRsHri9oGcd` z&Q{cF0S0=`;P=~AY8QJu0YN!7j@h}DvN(q+j+$^>1_*%J0mg0m*;7s%ZQQy>=b!5H zNbRQ<{Zt6ywaRCVt?;3M+iwSy@P%qyTeQwP3BHVc;RmvKxL)`lnr_T=yL&#>n-VoGUFHdS}n_rL8vce5OX2x z$el=PuFzvwQ0uOSTMvl>t4nqlM>u{pZ^}k{_1sp_vZ4a883}*q4)umTY2KNqu*czD z(%Y`1cGeW(r2ufA$VoC5P4NfP`$6H2wnQ9=meH|e2;KQ9zg${wqV65RF4N91vxF9l zP!riYl632){)HUXZ4oIyOK7YI<-X247ImR8`uogJ4*}yL zyr?f;)o`M$E>6iWF^x2m&7)Xa>>+V=K(PWAsQd6IvExZ3ntlt051G#3(*3b}(UC3$ zo7YoizO7i5V+*t{8yySeq`!o103#p$iB&MlWT9d?-)CHWB;Tf6ZNrVJzF7Sk_YI5E zVVs{Tw)&Pn`mMNV+ysO9@5#oGLDcyL-N=1drNy_Yejso&!=xtdOb0-s9l{}BOV?J7 zC~iffb_bX(?b+XdF6lG+3(e=X1D|uClGmt#5a%vf5s!6onjymMPc_d1?7+-@;+WtCabL~Z z-A(Q^^c{@tkY*oUb7OrC1Q$%czgg4WG5+#CRXSk$$D zn*2kcfc(&>nz7Hqx6WC+p-{-bP_;g1Yfu34rk7TR%iO@PBQ>uMy-dKK9cTBy)KNI( ze8*RsWLAwH55R16{rh3W)rJ1!4)>F7On!fMi@e>uj3>4tYJ#-*WI#(s=0d2V8F- zNbSi4%u77|hG~tP_c@dOEiguu>>zO_0jqb8a{0CJ8xs6#wTNPG&BFTg23`(`Jd6C%bk`d(zc$8Kg+1Fgj3pr* zJC0H6{3TXLnGwjF#Owb00X22A_$}`(-&`jQDlq5)-mq07XM@ntQtX_|f8wq;(o6zS zv(@#`9p-z+ai6J^%Gymj$LOYW?-bVeNoH9K!D#dJ3;vS9>i+BrgUat{TDXa}4zNkh z2c>z+crz6DeZt9OC8X!D=RmuG0KYGq^BTpMP2rMV-EgZ8!tM|jfK{gS>pa(`1F z;mP_=tWxgL!uivFj%UYw=I|MJbiTS_Rk_J!u6GToHb8QaeWc zJjHLvZ7JH_n|=gCQvYWg&UA7a7b;m-mNlzb=#UWMSGXIuITs2#iLQ|SBDQ`<{b>x` z__oL@cYpxQ8DGL1HWl&g*YfHL?zQy}NZgXj_u=DTw2bo%Na?QGL;)1WFMXS!t*4!J zntn8i#EAiH!C#1Kn2x`f%=Ckh{5h7#Msc zf8ZIc{^Yt(vE9z*d$*+s{GmG zlz$3<@?0)m-XHtjrs7VRMYcjWDW-9It264+YDl|{4i;b!j!(MMO12~|)sdd#0iXZ= zD`HQ139CnkFzY8luK;7YQ&aCE81;&PqB6QI{Tx6AsX7m1qcETy~ zukct8kCX`|^nnM8e?D5h#~iukakZl)(T`Py)xBxNOxpcnqW{G3 zR5Ad8&H*2843Cb|5eAhwU;Rp7Sko*#-6LM3!Qs8_-L-r`V_dCu|C;q{z%-jzYhdy= zt6;y{Mesgi=HnsnK*N=JnV*kps*Yj?PC!fExWQes=gsQQ-3AA?6R=vvp$DAMH)?8O>o*biwkch zBz^mAD1cg)6EJ5>)sq?p2?fG*uAy0 zrfoR&U(|hm8GF{oZby^+vWj&nRHC}-I6$|h9_V2J&l}_iQk!<&JtZqd`rpvZS6)gv z?X$o`P&D!?f9G~CLKKLZ`r|pRz>C_Gi{7v9v ztfJ~OhJynV@$mQ6@vX7_|Gcd0*ih+Fu2O22fhv$K0*ax$m%ue09u-hqWcXt%0juGS zI6$22u-PL#p?R`4ETIX4K@eEg z=B&7)1+~qnG+aFt%sI8ni{YWBUhnmQ2UKmIP8u~@;+s<37hnDWz(9A-D(;){1js|x zO%2!CHy!{h>VH7*4sb ziV-N9jm&?2B#ZMX9FJZdg`su-NU!5D%*RN%?8T>{AEeHUQT>mu2mIh z2yAX%S<*ckH{aS04iDW+#?hVtnTBUM)n)UmtrW4m2i>}CD>26Hoz0CFIA0u#_>^I{ zDh>kxN}7ZP7_(7Vpn@q~e%tYTc8g*-VHp%ftrOH@C)o&Sit`)_*~m(byk0IU1Z!FV z+8Oi!w$k1{1h=JvneG6YHR~|y?aIkemCi(z8y)IX2NR1S;~&7h1)x423ovl#oX-6$ z{QJ9=$@1neqiHZqtDk>!1)Vd8>@V8KK}Om!B2+)w!5h5et`$x>}6vOgvnwezA{hsL0Cqn{PoEY zvzD=oCIznZ!FH;@%1$x=fJ@^)j2J{i@1)oEB9HXnk$d;0FXN}GmjdGxNSft)7JOr4 z)cVI&ZsY*Xw{$$4SH(&^4AmhHj%pXI7DI3Rfsd?G9MLZjn1#pLw@IHqlav&R3Tx8< z!B`|k)M^iD6t22#b|iA+>k_a!T(0*Bl9Qv+Y)+5yKY=6^tPAs*AbvzoGQ`qpruLb3 zzi%iV*b9K5cJ$APJU&+!0CQf{8(10gw-k5pPP-PJkNZ_hZaM)ukY^X?T>xTD3`wV}wLp}

t6^v0o+65T$qXX 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ð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

    PoC: нейросетевые силуэты на выпрямленном листе

    Снимок - +
    Модель
    @@ -23,6 +23,16 @@

    PoC: нейросетевые силуэты на выпрямленном
    cross-origin isolated: , WebGPU: , cores:
    +
    Комбинатор формы (нужны запуски: маска-модель и/или глубина в режиме «весь снимок») + контур 30 + глубина 40 + детали 30
    + ширина детали мм радиус деталей мм + усиление уровни тонов (0=нет) + скос контура мм свет + +
    +
    diff --git a/poc/ai/main.ts b/poc/ai/main.ts index b26a3ef..5089612 100644 --- a/poc/ai/main.ts +++ b/poc/ai/main.ts @@ -5,6 +5,7 @@ import { guessPaper } from '../../src/geometry/photo/aspect' import { paperSize } from '../../src/geometry/photo/paper' import { rectify, type Rectified } from '../../src/geometry/photo/homography' import { segment } from '../../src/geometry/photo/segment' +import { edt } from '../../src/geometry/raster/edt' import { gaussianBlur, MIN_SIGMA } from '../../src/geometry/raster/blur' import { fillHoles, labelComponents, removeSpecks } from '../../src/geometry/raster/components' import { CANDIDATES, type Candidate } from './models' @@ -186,7 +187,8 @@ function evaluate() { let alpha = state.alpha if (smoothMm > 0) alpha = gaussianBlur(alpha, W, H, Math.max(MIN_SIGMA, smoothMm * rect.scale), new Uint8Array(W * H).fill(1)) drawAlpha(alpha, W, H) - if (state.depth) { $('edge').textContent = 'карта глубины (светлее = ближе): справа — глубина, силуэт не считается'; $('tbl').innerHTML = ''; $('status').textContent = 'готово'; return } + capture(state.depth, alpha, rect, W, H, thr) + if (state.depth) { combine(); $('edge').textContent = 'карта глубины (светлее = ближе): справа — глубина, силуэт не считается'; $('tbl').innerHTML = ''; $('status').textContent = 'готово'; return } const mask = new Uint8Array(W * H); for (let i = 0; i < mask.length; i++) mask[i] = alpha[i] >= thr ? 1 : 0 const ai = components(mask, W, H, rect.scale, margin) draw(rect, seg.labels, ai.labels) @@ -223,6 +225,67 @@ function drawAlpha(alpha: Float32Array, w: number, h: number) { g.putImageData(im, 0, 0) } for (const id of ['thr', 'smooth', 'margin']) $(id).addEventListener('input', () => evaluate()) +/** Плоскости последнего запуска для комбинатора: маска (контур), глубина, яркость. Размеры совпадают, пока снимок один. */ +const planes: { w: number; h: number; mask?: Uint8Array; depth?: Float32Array; gray?: Float32Array; name?: string } = { w: 0, h: 0 } +function capture(isDepth: boolean, alpha: Float32Array, rect: Rectified, W: number, H: number, thr: number) { + if (planes.w !== W || planes.h !== H) { planes.mask = undefined; planes.depth = undefined } + planes.w = W; planes.h = H + planes.gray = Float32Array.from(rect.image.gray, (v) => v / 255) + if (isDepth) planes.depth = alpha + else planes.mask = alpha.map((v) => (v >= thr ? 1 : 0)) as unknown as Uint8Array + $('planes').textContent = ` плоскости: маска ${planes.mask ? 'есть' : 'нет'}, глубина ${planes.depth ? 'есть' : 'нет'}` + combine() +} +const ones = (n: number) => new Uint8Array(n).fill(1) +function localContrast(g: Float32Array, w: number, h: number, sigma: number, amount: number): Float32Array { + if (amount <= 0) return g.map(() => 0.5) + const mean = gaussianBlur(g, w, h, Math.max(MIN_SIGMA, sigma), ones(w * h)) + const dev = g.map((v, i) => v - mean[i]) + const sd = gaussianBlur(dev.map((v) => v * v), w, h, Math.max(MIN_SIGMA, sigma), ones(w * h)).map((v) => Math.sqrt(v) + 0.02) + return dev.map((v, i) => 0.5 + 0.5 * Math.tanh(amount * v / sd[i])) +} +const smooth01 = (t: number) => { const x = Math.min(1, Math.max(0, t)); return x * x * (3 - 2 * x) } +/** Форма = нормированная смесь трёх источников: контур (скос от края маски), глубина (внутри маски), детали (локальный контраст яркости). */ +function combine() { + const { w, h, mask, depth, gray } = planes + if (!gray || !w) return + const num = (id: string) => +$(id).value + const wc = num('wc'), wd = num('wd'), wt = num('wt') + const pxMm = w / Math.max(1, num('partw')) // пикселей на мм детали (кадр = вся деталь) + const inside = mask ?? ones(w * h) + // контур: скос шириной bevel мм от края маски + const dist = mask ? edt(mask, w, h) : null + const bevelPx = Math.max(1, num('bevel') * pxMm) + const contour = Float32Array.from({ length: w * h }, (_, i) => (dist ? smooth01(dist[i] / bevelPx) : 1)) + // глубина: нормировка по перцентилям внутри маски + let dnorm = new Float32Array(w * h).fill(0.5) + if (depth) { + const vals: number[] = []; for (let i = 0; i < w * h; i++) if (inside[i]) vals.push(depth[i]) + vals.sort((a, b) => a - b); const lo = vals[Math.floor(vals.length * 0.02)] ?? 0, hi = vals[Math.floor(vals.length * 0.98)] ?? 1 + dnorm = depth.map((v) => Math.min(1, Math.max(0, (v - lo) / (hi - lo || 1)))) + } + let tone = localContrast(gray, w, h, num('lcr') * pxMm, num('lca')) + const L = num('lvl'); if (L >= 2) tone = tone.map((v) => Math.round(v * (L - 1)) / (L - 1)) + const wsum = (mask || !depth ? wc : wc) + wd * (depth ? 1 : 0) + wt || 1 + const wcE = wc, wdE = depth ? wd : 0 + const sum = wcE + wdE + wt || 1 + const height = new Float32Array(w * h) + for (let i = 0; i < w * h; i++) height[i] = inside[i] ? (wcE * contour[i] + wdE * dnorm[i] + wt * tone[i]) / sum : 0 + void wsum + // рисуем высоту и «затенённый» рельеф (свет сверху слева), чтобы оценить, читается ли форма + const cv4 = $('c4'), cv5 = $('c5'); cv4.width = cv5.width = w; cv4.height = cv5.height = h + const g4 = cv4.getContext('2d')!.createImageData(w, h), g5 = cv5.getContext('2d')!.createImageData(w, h), k = num('light') + for (let j = 0; j < h; j++) for (let i = 0; i < w; i++) { + const p = j * w + i, v = Math.round(height[p] * 255) + g4.data[p * 4] = g4.data[p * 4 + 1] = g4.data[p * 4 + 2] = v; g4.data[p * 4 + 3] = 255 + const a = height[Math.max(0, j - 1) * w + Math.max(0, i - 1)], b = height[Math.min(h - 1, j + 1) * w + Math.min(w - 1, i + 1)] + const sh = Math.round(Math.min(1, Math.max(0, 0.5 + k * (a - b))) * 255) + const sv = inside[p] ? sh : 40 + g5.data[p * 4] = g5.data[p * 4 + 1] = g5.data[p * 4 + 2] = sv; g5.data[p * 4 + 3] = 255 + } + cv4.getContext('2d')!.putImageData(g4, 0, 0); cv5.getContext('2d')!.putImageData(g5, 0, 0) +} +for (const id of ['wc', 'wd', 'wt', 'partw', 'lcr', 'lca', 'lvl', 'bevel', 'light']) $(id).addEventListener('input', () => combine()) const scaleMm = (scale: number) => 1 / (scale * scale) $('run').addEventListener('click', () => { run().catch((e) => { log('ОШИБКА: ' + (e?.stack ?? e)); $('status').textContent = 'ошибка' }) }) diff --git a/poc/ai/samples/cat_face.jpeg b/poc/ai/samples/cat_face.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..c7507feba70e5bdf293dfcb72d609919ac8b4a59 GIT binary patch literal 43421 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%0DVa7K+x8O@$!R#%Pw<) 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

    YR4UW;kdNfzR!E8>Bz`&<;NpRaj0l! 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 literal 0 HcmV?d00001 From bb0bf6e5bebf68fe678598fab37ecc4c81022f89 Mon Sep 17 00:00:00 2001 From: skarakovski Date: Wed, 21 Oct 2026 21:15:17 +0700 Subject: [PATCH 16/38] poc: Keychain pipeline page (stages: crop, silhouette model, depth model, detail, mix, 3D relief); cutout PoC moved to cutout.html Co-Authored-By: Claude Sonnet 5.5 --- poc/ai/FINDINGS.md | 3 + poc/ai/cutout.html | 39 +++++ poc/ai/{main.ts => cutout.ts} | 0 poc/ai/engine.ts | 86 +++++++++++ poc/ai/index.html | 120 ++++++++++----- poc/ai/keychain.ts | 274 ++++++++++++++++++++++++++++++++++ 6 files changed, 487 insertions(+), 35 deletions(-) create mode 100644 poc/ai/cutout.html rename poc/ai/{main.ts => cutout.ts} (100%) create mode 100644 poc/ai/engine.ts create mode 100644 poc/ai/keychain.ts diff --git a/poc/ai/FINDINGS.md b/poc/ai/FINDINGS.md index 9bcd067..c55f63e 100644 --- a/poc/ai/FINDINGS.md +++ b/poc/ai/FINDINGS.md @@ -75,3 +75,6 @@ ### Решение владельца (2026-10-21) Глубина полезна, поэтому в плане `34` форма строится как смесь трёх источников (контур, глубина, детали) с долями, которые задаёт пользователь, плюс кроп области. В PoC добавлен «Комбинатор формы»: берёт маску и глубину из запусков моделей на одном снимке (режим «весь снимок») и яркость, показывает смешанную высоту и затенённый рельеф. + +### PoC пайплайна Keychain (index.html) и PoC фото-вырезов (cutout.html) +`npm run poc:ai`: `/` — Keychain по этапам (1 изображение и кроп → 2 силуэт: модель маски, яркость или весь кадр → 3 глубина: модель глубины, считается по кропу → 4 детали: локальный контраст → 5 смесь контура, глубины и деталей → 3D-рельеф на three.js с орбитой и подсветкой); `/cutout.html` — сравнение с классикой для change 32. Модели силуэта и глубины выбираются отдельно и считаются независимо; результаты хранятся до смены снимка. diff --git a/poc/ai/cutout.html b/poc/ai/cutout.html new file mode 100644 index 0000000..794ed94 --- /dev/null +++ b/poc/ai/cutout.html @@ -0,0 +1,39 @@ + +PoC: AI silhouettes + +

    +
    Снимок + + +
    +
    Модель
    +
    Бэкенд + + потоки + вход +
    +
    Край (пересчёт без модели)порог 0.5 сглаживание мм 0 поле мм
    + +
    cross-origin isolated: , WebGPU: , cores:
    +
    +
    Комбинатор формы (нужны запуски: маска-модель и/или глубина в режиме «весь снимок») + контур 30 + глубина 40 + детали 30
    + ширина детали мм радиус деталей мм + усиление уровни тонов (0=нет) + скос контура мм свет + +
    +
    +
    +
    + + diff --git a/poc/ai/main.ts b/poc/ai/cutout.ts similarity index 100% rename from poc/ai/main.ts rename to poc/ai/cutout.ts diff --git a/poc/ai/engine.ts b/poc/ai/engine.ts new file mode 100644 index 0000000..8b87cf5 --- /dev/null +++ b/poc/ai/engine.ts @@ -0,0 +1,86 @@ +/** Запуск моделей PoC: загрузка (Cache API), сессии ORT Web, подготовка входа и разворот выхода. Не зависит от страницы. */ +import * as ort from 'onnxruntime-web' +import type { Candidate } from './models' + +ort.env.wasm.proxy = true // вывод в воркере ORT: страница не замирает +ort.env.wasm.wasmPaths = 'https://cdn.jsdelivr.net/npm/onnxruntime-web@' + (ort.env.versions?.web ?? 'latest') + '/dist/' + +const sessions = new Map() +export type Log = (s: string) => void + +async function fetchUrl(url: string, bytes: number, log: Log): Promise { + const cache = await caches.open('poc-ai-models') + let r = await cache.match(url) + const t0 = performance.now() + if (!r) { + const net = await fetch(url) + if (!net.ok) throw new Error(`HTTP ${net.status} for ${url}`) + await cache.put(url, net.clone()); r = net + log(`скачано ${(bytes / 1e6).toFixed(0)} МБ за ${((performance.now() - t0) / 1000).toFixed(1)} с`) + } + const buf = await r.arrayBuffer() + if (buf.byteLength !== bytes) log(`ВНИМАНИЕ: размер ${buf.byteLength} ≠ ${bytes}`) + return buf +} + +async function session(c: Candidate, ep: string, threads: number, log: Log): Promise { + const key = `${c.id}|${ep}|${threads}` + let s = sessions.get(key) + if (s) return s + const buf = await fetchUrl(c.url, c.bytes, log) + const externalData = c.data ? [{ path: c.data.path, data: new Uint8Array(await fetchUrl(c.data.url, c.data.bytes, log)) }] : undefined + ort.env.wasm.numThreads = threads + const t = performance.now() + s = await ort.InferenceSession.create(buf, { executionProviders: [ep], graphOptimizationLevel: 'all', ...(externalData ? { externalData } : {}) }) + log(`сессия ${c.id} (${ep}) за ${(performance.now() - t).toFixed(0)} мс`) + sessions.set(key, s) + return s +} + +/** Вход NCHW float32: картинка вписывается в s × s с сохранением пропорций, поля — среднее модели. */ +function toInput(rgb: Uint8Array, w: number, h: number, c: Candidate, s: number): Float32Array { + const src = new ImageData(w, h) + for (let i = 0; i < w * h; i++) { src.data[i * 4] = rgb[i * 3]; src.data[i * 4 + 1] = rgb[i * 3 + 1]; src.data[i * 4 + 2] = rgb[i * 3 + 2]; src.data[i * 4 + 3] = 255 } + const a = document.createElement('canvas'); a.width = w; a.height = h; a.getContext('2d')!.putImageData(src, 0, 0) + const b = document.createElement('canvas'); b.width = s; b.height = s + const g = b.getContext('2d')!; g.imageSmoothingQuality = 'high' + g.fillStyle = `rgb(${c.mean.map((m) => Math.round(m * 255)).join(',')})`; g.fillRect(0, 0, s, s) + const r = Math.min(s / w, s / h); g.drawImage(a, (s - w * r) / 2, (s - h * r) / 2, w * r, h * r) + const d = g.getImageData(0, 0, s, s).data + const out = new Float32Array(3 * s * s) + for (let i = 0; i < s * s; i++) for (let k = 0; k < 3; k++) out[k * s * s + i] = (d[i * 4 + k] / 255 - c.mean[k]) / c.std[k] + return out +} + +/** Выход s × s → плоскость w × h (билинейно, поля вписывания отброшены). */ +function fromOutput(v: Float32Array, s: number, w: number, h: number): Float32Array { + const out = new Float32Array(w * h), r = Math.min(s / w, s / h), ox = (s - w * r) / 2, oy = (s - h * r) / 2 + for (let j = 0; j < h; j++) { + const fy = Math.min(s - 1.001, Math.max(0, (j + 0.5) * r + oy - 0.5)), y0 = Math.floor(fy), ty = fy - y0 + for (let i = 0; i < w; i++) { + const fx = Math.min(s - 1.001, Math.max(0, (i + 0.5) * r + ox - 0.5)), x0 = Math.floor(fx), tx = fx - x0 + out[j * w + i] = (v[y0 * s + x0] * (1 - tx) + v[y0 * s + x0 + 1] * tx) * (1 - ty) + (v[(y0 + 1) * s + x0] * (1 - tx) + v[(y0 + 1) * s + x0 + 1] * tx) * ty + } + } + return out +} + +/** Прогон модели на картинке rgb (w × h). matte → альфа 0…1, depth → глубина 0…1 по 2–98 перцентилям (ближе = больше). */ +export async function infer(c: Candidate, ep: string, threads: number, rgb: Uint8Array, w: number, h: number, log: Log): Promise<{ plane: Float32Array; ms: number }> { + const sess = await session(c, ep, threads, log) + const s = c.size, data = toInput(rgb, w, h, c, s) + const t = performance.now() + const out = await sess.run({ [sess.inputNames[0]]: new ort.Tensor('float32', data, [1, 3, s, s]) }) + const ms = performance.now() - t + let o = out[sess.outputNames[0]].data as Float32Array + if (c.kind === 'depth') { + const sorted = Float32Array.from(o).sort(), lo = sorted[Math.floor(sorted.length * 0.02)], hi = sorted[Math.floor(sorted.length * 0.98)] + o = o.map((x) => Math.min(1, Math.max(0, (x - lo) / (hi - lo || 1)))) + } else { + let lo = Infinity, hi = -Infinity; for (const x of o) { if (x < lo) lo = x; if (x > hi) hi = x } + if (c.sigmoid || lo < -0.001 || hi > 1.001) o = o.map((x) => 1 / (1 + Math.exp(-x))) + } + // выход может быть не s × s (некоторые модели): определяем сторону по длине + const side = Math.round(Math.sqrt(o.length)) + return { plane: fromOutput(o, side, w, h), ms } +} diff --git a/poc/ai/index.html b/poc/ai/index.html index 494e3e1..c9a1e16 100644 --- a/poc/ai/index.html +++ b/poc/ai/index.html @@ -1,39 +1,89 @@ -PoC: AI silhouettes +PoC: Keychain — пайплайн фото → рельеф -

    PoC: нейросетевые силуэты на выпрямленном листе

    -
    Снимок - - -
    -
    Модель
    -
    Бэкенд - - потоки - вход -
    -
    Край (пересчёт без модели)порог 0.5 сглаживание мм 0 поле мм
    - -
    cross-origin isolated: , WebGPU: , cores:
    -
    -
    Комбинатор формы (нужны запуски: маска-модель и/или глубина в режиме «весь снимок») - контур 30 - глубина 40 - детали 30
    - ширина детали мм радиус деталей мм - усиление уровни тонов (0=нет) - скос контура мм свет - -
    -
    -
    -
    - +
    PoC Keychain: фото → рельеф, пайплайн по этапам · ← PoC фото-вырезов (change 32) + · isolated: , cores:
    +
    +
    +

    1Изображение и кроп

    + + +
    + + + + + +
    + + +
    +

    2Силуэт

    + +
    + + +
    +
    + +
    + + + +
    +

    3Глубина

    + + + + +
    +

    4Детали (локальный контраст)

    + + + + +
    +

    5Смесь формы → высота

    +
    + +
    + +
    + + + +
    +
    +
    + +
    + diff --git a/poc/ai/keychain.ts b/poc/ai/keychain.ts new file mode 100644 index 0000000..c9d021a --- /dev/null +++ b/poc/ai/keychain.ts @@ -0,0 +1,274 @@ +/** + * PoC пайплайна Keychain (change 33 + 34): фото → кроп → силуэт (модель маски) → глубина (модель глубины) → детали + * (локальный контраст) → смесь → 3D-рельеф. Этапы независимы: у каждого своя модель/настройки и своё превью. + */ +import * as THREE from 'three' +import { OrbitControls } from 'three/addons/controls/OrbitControls.js' +import { edt } from '../../src/geometry/raster/edt' +import { gaussianBlur, MIN_SIGMA } from '../../src/geometry/raster/blur' +import { otsu } from '../../src/geometry/raster/threshold' +import { close } from '../../src/geometry/raster/morphology' +import { fillHoles, labelComponents, removeSpecks } from '../../src/geometry/raster/components' +import { CANDIDATES, type Candidate } from './models' +import { infer } from './engine' + +const $ = (id: string) => document.getElementById(id) as T +const num = (id: string) => +$(id).value +const chk = (id: string) => $(id).checked +const logEl = $('log') +const log = (s: string) => { logEl.textContent += s + '\n'; logEl.scrollTop = logEl.scrollHeight; console.log(s) } +$('coi').textContent = String(self.crossOriginIsolated); $('cores').textContent = String(navigator.hardwareConcurrency) + +for (const c of CANDIDATES.filter((x) => (x.kind ?? 'matte') === 'matte')) $('silmodelsel').append(new Option(c.label, c.id)) +for (const c of CANDIDATES.filter((x) => x.kind === 'depth')) $('depthsel').append(new Option(c.label, c.id)) +$('silmodelsel').value = 'birefnet-lite-512-fp16' + +// ---------- состояние ---------- +interface Img { w: number; h: number; rgb: Uint8Array } +interface Crop { x: number; y: number; w: number; h: number } // доли +let img: Img | null = null +let matte: Float32Array | null = null // мягкая альфа, размер картинки +let depthRec: { plane: Float32Array; w: number; h: number; crop: Crop } | null = null + +const cropNow = (): Crop => ({ x: num('cx') / 100, y: num('cy') / 100, w: Math.min(num('cw'), 100 - num('cx')) / 100, h: Math.min(num('ch'), 100 - num('cy')) / 100 }) +const cropPx = (c: Crop, im: Img) => ({ x0: Math.round(c.x * im.w), y0: Math.round(c.y * im.h), pw: Math.max(8, Math.round(c.w * im.w)), ph: Math.max(8, Math.round(c.h * im.h)) }) + +async function loadImage() { + const f = $('file').files?.[0] + const el = new Image(); el.src = f ? URL.createObjectURL(f) : `./samples/${$('sample').value}.jpeg`; await el.decode() + const k = Math.min(1, 2048 / Math.max(el.naturalWidth, el.naturalHeight)) + const cv = document.createElement('canvas'); cv.width = Math.round(el.naturalWidth * k); cv.height = Math.round(el.naturalHeight * k) + const g = cv.getContext('2d')!; g.drawImage(el, 0, 0, cv.width, cv.height) + const d = g.getImageData(0, 0, cv.width, cv.height).data, rgb = new Uint8Array(cv.width * cv.height * 3) + for (let i = 0; i < cv.width * cv.height; i++) { rgb[i * 3] = d[i * 4]; rgb[i * 3 + 1] = d[i * 4 + 1]; rgb[i * 3 + 2] = d[i * 4 + 2] } + img = { w: cv.width, h: cv.height, rgb }; matte = null; depthRec = null; work = null + log(`снимок ${img.w}×${img.h}`); schedule() +} + +// ---------- рабочий растр кропа ---------- +interface Work { key: string; ww: number; wh: number; gray: Float32Array; rgb: Uint8Array; matte: Float32Array | null; depth: Float32Array | null; k: number; x0: number; y0: number } +let work: Work | null = null +function bil(p: Float32Array, w: number, h: number, x: number, y: number): number { + x = Math.min(w - 1.001, Math.max(0, x - 0.5)); y = Math.min(h - 1.001, Math.max(0, y - 0.5)) + const x0 = Math.floor(x), y0 = Math.floor(y), tx = x - x0, ty = y - y0 + return (p[y0 * w + x0] * (1 - tx) + p[y0 * w + x0 + 1] * tx) * (1 - ty) + (p[(y0 + 1) * w + x0] * (1 - tx) + p[(y0 + 1) * w + x0 + 1] * tx) * ty +} +function getWork(): Work | null { + if (!img) return null + const c = cropNow(), { x0, y0, pw, ph } = cropPx(c, img) + const key = [x0, y0, pw, ph, matte ? 1 : 0, depthRec ? depthRec.crop.x + ',' + depthRec.crop.y + ',' + depthRec.crop.w + ',' + depthRec.crop.h : 0].join('|') + if (work && work.key === key) return work + const k = Math.min(1, 640 / Math.max(pw, ph)), ww = Math.max(8, Math.round(pw * k)), wh = Math.max(8, Math.round(ph * k)) + const gray = new Float32Array(ww * wh), rgb = new Uint8Array(ww * wh * 3) + const mw = matte ? new Float32Array(ww * wh) : null, dw = depthRec ? new Float32Array(ww * wh) : null + const dpx = depthRec ? cropPx(depthRec.crop, img) : null + for (let j = 0; j < wh; j++) for (let i = 0; i < ww; i++) { + const fx = x0 + (i + 0.5) / k, fy = y0 + (j + 0.5) / k // координаты в полном кадре + const ix = Math.min(img.w - 1, Math.floor(fx)), iy = Math.min(img.h - 1, Math.floor(fy)), q = (iy * img.w + ix) * 3, p = j * ww + i + rgb[p * 3] = img.rgb[q]; rgb[p * 3 + 1] = img.rgb[q + 1]; rgb[p * 3 + 2] = img.rgb[q + 2] + gray[p] = (0.299 * img.rgb[q] + 0.587 * img.rgb[q + 1] + 0.114 * img.rgb[q + 2]) / 255 + if (mw && matte) mw[p] = bil(matte, img.w, img.h, fx, fy) + if (dw && depthRec && dpx) dw[p] = bil(depthRec.plane, depthRec.w, depthRec.h, (fx - dpx.x0) * depthRec.w / dpx.pw, (fy - dpx.y0) * depthRec.h / dpx.ph) + } + work = { key, ww, wh, gray, rgb, matte: mw, depth: dw, k, x0, y0 } + return work +} + +// ---------- этапы ---------- +const ones = (n: number) => new Uint8Array(n).fill(1) +const smooth01 = (t: number) => { const x = Math.min(1, Math.max(0, t)); return x * x * (3 - 2 * x) } + +function silhouette(w: Work, pxMm: number): { mask: Uint8Array; note: string } { + const n = w.ww * w.wh, src = $('silsrc').value + let mask: Uint8Array, note = '' + if (src === 'frame') mask = ones(n) + else if (src === 'brightness') { + const g8 = Uint8Array.from(w.gray, (v) => Math.round(v * 255)), thr = otsu(g8), inv = chk('inv') + mask = new Uint8Array(n); for (let i = 0; i < n; i++) mask[i] = (g8[i] < thr) !== inv ? 1 : 0 + mask = close(mask, w.ww, w.wh, Math.max(1, Math.round(0.6 * pxMm))) + note = `порог Оцу ${thr}` + } else { + if (!w.matte) { note = 'маска не посчитана: запустите модель'; return { mask: ones(n), note } } + const s = num('smooth') * pxMm, a = s > 0 ? gaussianBlur(w.matte, w.ww, w.wh, Math.max(MIN_SIGMA, s), ones(n)) : w.matte + mask = new Uint8Array(n); const thr = num('thr'); for (let i = 0; i < n; i++) mask[i] = a[i] >= thr ? 1 : 0 + if (chk('largest')) { + const cc = labelComponents(mask, w.ww, w.wh); let best = 0 + for (let k = 1; k <= cc.count; k++) if (cc.sizes[k] > (cc.sizes[best] ?? 0)) best = k + mask = mask.map((_, i) => (cc.labels[i] === best && best ? 1 : 0)) as Uint8Array + } + } + if (src !== 'frame') { + if (chk('fill')) mask = fillHoles(mask, w.ww, w.wh).mask + mask = removeSpecks(mask, w.ww, w.wh, Math.round(2 * pxMm * pxMm)) + } + return { mask, note } +} + +function normDepth(w: Work, mask: Uint8Array): Float32Array | null { + if (!w.depth) return null + const v: number[] = []; for (let i = 0; i < mask.length; i++) if (mask[i]) v.push(w.depth[i]) + if (!v.length) return null + v.sort((a, b) => a - b); const lo = v[Math.floor(v.length * 0.02)], hi = v[Math.floor(v.length * 0.98)], inv = chk('dinv') + return w.depth.map((d) => { const t = Math.min(1, Math.max(0, (d - lo) / (hi - lo || 1))); return inv ? 1 - t : t }) +} + +function detail(w: Work, pxMm: number): Float32Array { + const amount = num('lca'), n = w.ww * w.wh + if (amount <= 0) return new Float32Array(n).fill(0.5) + const sg = Math.max(MIN_SIGMA, num('lcr') * pxMm), m = ones(n) + const mean = gaussianBlur(w.gray, w.ww, w.wh, sg, m), dev = w.gray.map((v, i) => v - mean[i]) + const sd = gaussianBlur(dev.map((v) => v * v), w.ww, w.wh, sg, m).map((v) => Math.sqrt(v) + 0.02) + let t = dev.map((v, i) => 0.5 + 0.5 * Math.tanh(amount * v / sd[i])) + const L = num('lvl'); if (L >= 2) t = t.map((v) => Math.round(v * (L - 1)) / (L - 1)) + return t +} + +// ---------- превью ---------- +function show(id: string, w: number, h: number, px: (i: number) => [number, number, number]) { + const cv = $(id); cv.width = w; cv.height = h + const g = cv.getContext('2d')!, im = g.createImageData(w, h) + for (let i = 0; i < w * h; i++) { const [r, gg, b] = px(i); im.data[i * 4] = r; im.data[i * 4 + 1] = gg; im.data[i * 4 + 2] = b; im.data[i * 4 + 3] = 255 } + g.putImageData(im, 0, 0) +} +const gr = (v: number): [number, number, number] => { const t = Math.round(Math.min(1, Math.max(0, v)) * 255); return [t, t, t] } + +function previewImage(c: Crop) { + if (!img) return + const cv = $('pv-img'), s = Math.min(1, 520 / img.w); cv.width = Math.round(img.w * s); cv.height = Math.round(img.h * s) + const g = cv.getContext('2d')!, im = g.createImageData(img.w, img.h) + for (let i = 0; i < img.w * img.h; i++) { im.data[i * 4] = img.rgb[i * 3]; im.data[i * 4 + 1] = img.rgb[i * 3 + 1]; im.data[i * 4 + 2] = img.rgb[i * 3 + 2]; im.data[i * 4 + 3] = 255 } + const t = document.createElement('canvas'); t.width = img.w; t.height = img.h; t.getContext('2d')!.putImageData(im, 0, 0) + g.drawImage(t, 0, 0, cv.width, cv.height) + g.fillStyle = 'rgba(0,0,0,0.45)'; g.beginPath(); g.rect(0, 0, cv.width, cv.height); g.rect(c.x * cv.width, c.y * cv.height, c.w * cv.width, c.h * cv.height); g.fill('evenodd') + g.strokeStyle = '#ff3'; g.lineWidth = 2; g.strokeRect(c.x * cv.width, c.y * cv.height, c.w * cv.width, c.h * cv.height) +} + +// ---------- обновление пайплайна ---------- +let timer = 0 +function schedule() { clearTimeout(timer); timer = window.setTimeout(update, 120) } +function update() { + for (const id of ['cx', 'cy', 'cw', 'ch', 'thr', 'smooth', 'wc', 'wd', 'wt', 'roll']) { const e = $(id); (e.nextElementSibling as HTMLElement).textContent = e.value } + $('silmodel').hidden = $('silsrc').value !== 'model' + $('silbright').hidden = $('silsrc').value !== 'brightness' + const c = cropNow(); previewImage(c) + const w = getWork(); if (!w || !img) return + const partW = num('partw'), pxMm = w.ww / partW, partH = partW * w.wh / w.ww, n = w.ww * w.wh + const { mask, note } = silhouette(w, pxMm) + $('st-sil').textContent = note + show('pv-mask', w.ww, w.wh, (i) => (mask[i] ? [w.rgb[i * 3], w.rgb[i * 3 + 1], w.rgb[i * 3 + 2]] : [30, 30, 40])) + const dn = normDepth(w, mask) + const same = depthRec && JSON.stringify(depthRec.crop) === JSON.stringify(c) + $('st-depth').textContent = depthRec ? (same ? 'посчитана для этого кропа' : 'посчитана для другого кропа (можно пересчитать)') : 'не посчитана' + show('pv-depth', w.ww, w.wh, (i) => (mask[i] ? (dn ? gr(dn[i]) : [90, 90, 90]) : [30, 30, 40])) + const dt = detail(w, pxMm) + show('pv-detail', w.ww, w.wh, (i) => (mask[i] ? gr(dt[i]) : [30, 30, 40])) + // смесь + const dist = edt(mask, w.ww, w.wh), bevelPx = Math.max(1, num('bevel') * pxMm), roll = num('roll') / 100 + const wc = num('wc'), wd = dn ? num('wd') : 0, wt = num('wt') + const value = new Float32Array(n) + for (let i = 0; i < n; i++) { + if (!mask[i]) continue + const contour = smooth01(dist[i] / bevelPx), tot = wc + wd + wt, mixed = tot === 0 ? contour : (wc * contour + wd * (dn ? dn[i] : 0) + wt * dt[i]) / tot + value[i] = mixed * ((1 - roll) + roll * contour) + } + show('pv-mix', w.ww, w.wh, (i) => (mask[i] ? gr(value[i]) : [30, 30, 40])) + build3D(w, mask, value, partW, partH) +} + +// ---------- 3D ---------- +const view = $('view') +const renderer = new THREE.WebGLRenderer({ antialias: true }); view.append(renderer.domElement) +const scene = new THREE.Scene(); scene.background = new THREE.Color(0xe8e8e4) +const camera = new THREE.PerspectiveCamera(35, 1, 1, 2000) +const controls = new OrbitControls(camera, renderer.domElement) +const hemi = new THREE.HemisphereLight(0xffffff, 0x666666, 0.55); scene.add(hemi) +const sun = new THREE.DirectionalLight(0xffffff, 2.2); scene.add(sun) +const fill = new THREE.DirectionalLight(0xffffff, 0.5); fill.position.set(60, -80, 40); scene.add(fill) +const mat = new THREE.MeshStandardMaterial({ color: 0xd8d2c4, roughness: 0.75, metalness: 0 }) +const group = new THREE.Group(); scene.add(group) +function setLight() { const a = (num('lightang') * Math.PI) / 180; sun.position.set(Math.cos(a) * 80, Math.sin(a) * 80, 70) } +function resize() { const w = view.clientWidth, h = view.clientHeight; renderer.setSize(w, h, false); camera.aspect = w / h; camera.updateProjectionMatrix() } +new ResizeObserver(resize).observe(view); resize(); setLight() +renderer.setAnimationLoop(() => { controls.update(); renderer.render(scene, camera) }) +let fitted = '' +function frame(partW: number, partH: number, preset: 'top' | 'iso' | null) { + const d = Math.max(partW, partH) * 1.9 + if (preset === 'top') camera.position.set(0, -0.001, d) + else if (preset === 'iso') camera.position.set(-d * 0.45, -d * 0.7, d * 0.75) + camera.up.set(0, 0, 1); if (preset === 'top') camera.up.set(0, 1, 0) + camera.lookAt(0, 0, 0); controls.target.set(0, 0, 0); controls.update() +} +$('vtop').addEventListener('click', () => frame(num('partw'), num('partw'), 'top')) +$('viso').addEventListener('click', () => frame(num('partw'), num('partw'), 'iso')) + +function build3D(w: Work, mask: Uint8Array, value: Float32Array, partW: number, partH: number) { + const nodes = Math.max(60, num('nodes')), s = Math.max(1, Math.ceil(Math.max(w.ww, w.wh) / nodes)) + const nx = Math.floor((w.ww - 1) / s) + 1, ny = Math.floor((w.wh - 1) / s) + 1 + const floor = num('floor'), range = num('range'), mmx = partW / w.ww, mmy = partH / w.wh + const X = (i: number) => (i * s + 0.5) * mmx - partW / 2, Y = (j: number) => partH / 2 - (j * s + 0.5) * mmy + const inside = (i: number, j: number) => mask[Math.min(w.wh - 1, j * s) * w.ww + Math.min(w.ww - 1, i * s)] === 1 + const top = (i: number, j: number) => floor + range * value[Math.min(w.wh - 1, j * s) * w.ww + Math.min(w.ww - 1, i * s)] + // верх: индексированная сетка, только ячейки, у которых все 4 узла внутри маски + const pos = new Float32Array(nx * ny * 3) + for (let j = 0; j < ny; j++) for (let i = 0; i < nx; i++) { const p = (j * nx + i) * 3; pos[p] = X(i); pos[p + 1] = Y(j); pos[p + 2] = inside(i, j) ? top(i, j) : 0 } + const idx: number[] = [], cell: boolean[] = new Array((nx - 1) * (ny - 1)).fill(false) + for (let j = 0; j < ny - 1; j++) for (let i = 0; i < nx - 1; i++) { + if (!(inside(i, j) && inside(i + 1, j) && inside(i, j + 1) && inside(i + 1, j + 1))) continue + cell[j * (nx - 1) + i] = true + const a = j * nx + i, b = a + 1, c = a + nx, d = c + 1 + idx.push(a, b, c, b, d, c) + } + const topGeo = new THREE.BufferGeometry(); topGeo.setAttribute('position', new THREE.BufferAttribute(pos, 3)); topGeo.setIndex(idx); topGeo.computeVertexNormals() + // стенки по границе ячеек и дно + const wp: number[] = [] + const quad = (a: number[], b: number[], c: number[], d: number[]) => wp.push(...a, ...b, ...c, ...b, ...d, ...c) + const isCell = (i: number, j: number) => i >= 0 && j >= 0 && i < nx - 1 && j < ny - 1 && cell[j * (nx - 1) + i] + for (let j = 0; j < ny - 1; j++) for (let i = 0; i < nx - 1; i++) { + if (!isCell(i, j)) continue + const x0 = X(i), x1 = X(i + 1), y0 = Y(j), y1 = Y(j + 1) + quad([x0, y0, 0], [x1, y0, 0], [x0, y1, 0], [x1, y1, 0]) // дно (нормаль вниз задаётся порядком ниже) + if (!isCell(i, j - 1)) quad([x1, y0, 0], [x0, y0, 0], [x1, y0, top(i + 1, j)], [x0, y0, top(i, j)]) + if (!isCell(i, j + 1)) quad([x0, y1, 0], [x1, y1, 0], [x0, y1, top(i, j + 1)], [x1, y1, top(i + 1, j + 1)]) + if (!isCell(i - 1, j)) quad([x0, y0, 0], [x0, y1, 0], [x0, y0, top(i, j)], [x0, y1, top(i, j + 1)]) + if (!isCell(i + 1, j)) quad([x1, y1, 0], [x1, y0, 0], [x1, y1, top(i + 1, j + 1)], [x1, y0, top(i + 1, j)]) + } + const skirt = new THREE.BufferGeometry(); skirt.setAttribute('position', new THREE.BufferAttribute(new Float32Array(wp), 3)); skirt.computeVertexNormals() + group.clear() + mat.wireframe = chk('wire'); mat.side = THREE.DoubleSide + group.add(new THREE.Mesh(topGeo, mat), new THREE.Mesh(skirt, mat)) + $('stat3d').textContent = `сетка ${nx}×${ny}, шаг ${(s * mmx).toFixed(2)} мм, ${(idx.length / 3 / 1000).toFixed(0)} тыс. треуг.; деталь ${partW.toFixed(0)}×${partH.toFixed(0)} мм, высота до ${(floor + range).toFixed(1)} мм` + const sig = `${partW}|${partH}`; if (sig !== fitted) { fitted = sig; frame(partW, partH, 'iso') } +} + +// ---------- запуск моделей ---------- +async function runModel(kind: 'sil' | 'depth') { + if (!img) return + const c: Candidate = CANDIDATES.find((x) => x.id === $(kind === 'sil' ? 'silmodelsel' : 'depthsel').value)! + const ep = $(kind === 'sil' ? 'silep' : 'depthep').value + const btn = $(kind === 'sil' ? 'runsil' : 'rundepth'); btn.disabled = true + try { + log(`${kind === 'sil' ? 'силуэт' : 'глубина'}: ${c.id}, ${ep}, лицензия ${c.license}`) + let rgb = img.rgb, w = img.w, h = img.h, crop: Crop | null = null + if (kind === 'depth') { // глубина считается по кропу: на лице больше пикселей + crop = cropNow(); const p = cropPx(crop, img); w = p.pw; h = p.ph; rgb = new Uint8Array(w * h * 3) + for (let j = 0; j < h; j++) for (let i = 0; i < w; i++) { const q = ((p.y0 + j) * img.w + p.x0 + i) * 3; rgb.set(img.rgb.subarray(q, q + 3), (j * w + i) * 3) } + } + const r = await infer(c, ep, 1, rgb, w, h, log) + log(`готово за ${r.ms.toFixed(0)} мс`) + if (kind === 'sil') matte = r.plane; else depthRec = { plane: r.plane, w, h, crop: crop! } + work = null; schedule() + } catch (e) { log('ОШИБКА: ' + ((e as Error)?.message ?? e)) } finally { btn.disabled = false } +} + +// ---------- события ---------- +$('runsil').addEventListener('click', () => runModel('sil')) +$('rundepth').addEventListener('click', () => runModel('depth')) +$('sample').addEventListener('change', () => { $('file').value = ''; loadImage() }) +$('file').addEventListener('change', () => loadImage()) +$('face').addEventListener('click', () => { for (const [id, v] of [['cx', 42], ['cy', 34], ['cw', 42], ['ch', 24]] as const) $(id).value = String(v); schedule() }) +$('resetcrop').addEventListener('click', () => { for (const [id, v] of [['cx', 0], ['cy', 0], ['cw', 100], ['ch', 100]] as const) $(id).value = String(v); schedule() }) +for (const el of document.querySelectorAll('input[type=range], input[type=number], input[type=checkbox], select')) { + if (['file', 'sample'].includes(el.id)) continue + el.addEventListener('input', () => { if (el.id === 'lightang') setLight(); schedule() }) +} +loadImage() From 6db0b091a380aa1b3236d11f077f1dd7d691da47 Mon Sep 17 00:00:00 2001 From: skarakovski Date: Wed, 21 Oct 2026 21:22:43 +0700 Subject: [PATCH 17/38] poc: log under the 3D view, layer checkboxes (mask, contour, depth, detail), backends per model from measurements Co-Authored-By: Claude Sonnet 5.5 --- poc/ai/cutout.ts | 10 ++++++++-- poc/ai/index.html | 11 +++++++++-- poc/ai/keychain.ts | 14 +++++++++++--- poc/ai/models.ts | 15 +++++++++++++++ 4 files changed, 43 insertions(+), 7 deletions(-) diff --git a/poc/ai/cutout.ts b/poc/ai/cutout.ts index 5089612..12e0bfc 100644 --- a/poc/ai/cutout.ts +++ b/poc/ai/cutout.ts @@ -8,14 +8,20 @@ import { segment } from '../../src/geometry/photo/segment' import { edt } from '../../src/geometry/raster/edt' import { gaussianBlur, MIN_SIGMA } from '../../src/geometry/raster/blur' import { fillHoles, labelComponents, removeSpecks } from '../../src/geometry/raster/components' -import { CANDIDATES, type Candidate } from './models' +import { backendsOf, CANDIDATES, type Candidate } from './models' const $ = (id: string) => document.getElementById(id) as T const log = (s: string) => { $('log').textContent += s + '\n'; console.log(s) } $('coi').textContent = String(self.crossOriginIsolated) $('cores').textContent = String(navigator.hardwareConcurrency) $('gpu').textContent = 'gpu' in navigator ? 'yes' : 'no' -for (const c of CANDIDATES) $('model').append(new Option(c.label, c.id)) +for (const c of CANDIDATES.filter((x) => (x.kind ?? 'matte') === 'matte')) $('model').append(new Option(c.label, c.id)) +function syncEp() { + const ep = $('ep'), list = backendsOf($('model').value), keep = ep.value + ep.innerHTML = ''; for (const b of list) ep.append(new Option(b, b)) + ep.value = list.includes(keep as 'wasm' | 'webgpu') ? keep : list[0]; ep.disabled = list.length === 1 +} +syncEp(); $('model').addEventListener('change', syncEp) const sessions = new Map() ort.env.wasm.proxy = true // вывод в воркере ORT: страница не замирает на время инференса diff --git a/poc/ai/index.html b/poc/ai/index.html index c9a1e16..08c464a 100644 --- a/poc/ai/index.html +++ b/poc/ai/index.html @@ -16,7 +16,7 @@ #view{width:100%;height:560px;background:#e8e8e4;border:1px solid #999;border-radius:6px;position:sticky;top:8px} #right{position:sticky;top:8px;min-width:0} #view canvas{display:block;width:100%;height:100%} -#log{white-space:pre-wrap;font:11px ui-monospace,monospace;background:#fff;border:1px solid #ccc;padding:5px;max-height:150px;overflow:auto} +#log{white-space:pre-wrap;font:11px ui-monospace,monospace;background:#fff;border:1px solid #ccc;padding:5px;height:150px;overflow:auto} .note{color:#666;font-size:12px}.ok{color:#080}.bad{color:#b00} button{padding:3px 10px} @@ -72,7 +72,6 @@ -
    +

    Слои рельефа (галочки пересчитывают 3D)

    + + + + +
    Выключенный слой даёт нулевую долю, остальные нормируются; без маски рельеф строится на всём кадре.
    +
    +
    diff --git a/poc/ai/keychain.ts b/poc/ai/keychain.ts index c9d021a..aaa3615 100644 --- a/poc/ai/keychain.ts +++ b/poc/ai/keychain.ts @@ -9,7 +9,7 @@ import { gaussianBlur, MIN_SIGMA } from '../../src/geometry/raster/blur' import { otsu } from '../../src/geometry/raster/threshold' import { close } from '../../src/geometry/raster/morphology' import { fillHoles, labelComponents, removeSpecks } from '../../src/geometry/raster/components' -import { CANDIDATES, type Candidate } from './models' +import { backendsOf, CANDIDATES, type Candidate } from './models' import { infer } from './engine' const $ = (id: string) => document.getElementById(id) as T @@ -22,6 +22,13 @@ $('coi').textContent = String(self.crossOriginIsolated); $('cores').textContent for (const c of CANDIDATES.filter((x) => (x.kind ?? 'matte') === 'matte')) $('silmodelsel').append(new Option(c.label, c.id)) for (const c of CANDIDATES.filter((x) => x.kind === 'depth')) $('depthsel').append(new Option(c.label, c.id)) $('silmodelsel').value = 'birefnet-lite-512-fp16' +/** Бэкенд подбирается под модель: показываются только те, на которых она работает. */ +function syncBackends(modelSel: string, epSel: string) { + const ep = $(epSel), list = backendsOf($(modelSel).value), keep = ep.value + ep.innerHTML = ''; for (const b of list) ep.append(new Option(b, b)) + ep.value = list.includes(keep as 'wasm' | 'webgpu') ? keep : list[0]; ep.disabled = list.length === 1 +} +for (const [m, e] of [['silmodelsel', 'silep'], ['depthsel', 'depthep']] as const) { syncBackends(m, e); $(m).addEventListener('change', () => syncBackends(m, e)) } // ---------- состояние ---------- interface Img { w: number; h: number; rgb: Uint8Array } @@ -153,7 +160,8 @@ function update() { const c = cropNow(); previewImage(c) const w = getWork(); if (!w || !img) return const partW = num('partw'), pxMm = w.ww / partW, partH = partW * w.wh / w.ww, n = w.ww * w.wh - const { mask, note } = silhouette(w, pxMm) + const sil = silhouette(w, pxMm), note = sil.note + const mask = chk('lm') ? sil.mask : ones(n) // слой «маска»: выключен — рельеф без вырезания по силуэту $('st-sil').textContent = note show('pv-mask', w.ww, w.wh, (i) => (mask[i] ? [w.rgb[i * 3], w.rgb[i * 3 + 1], w.rgb[i * 3 + 2]] : [30, 30, 40])) const dn = normDepth(w, mask) @@ -164,7 +172,7 @@ function update() { show('pv-detail', w.ww, w.wh, (i) => (mask[i] ? gr(dt[i]) : [30, 30, 40])) // смесь const dist = edt(mask, w.ww, w.wh), bevelPx = Math.max(1, num('bevel') * pxMm), roll = num('roll') / 100 - const wc = num('wc'), wd = dn ? num('wd') : 0, wt = num('wt') + const wc = chk('lc') ? num('wc') : 0, wd = dn && chk('ld') ? num('wd') : 0, wt = chk('lt') ? num('wt') : 0 const value = new Float32Array(n) for (let i = 0; i < n; i++) { if (!mask[i]) continue diff --git a/poc/ai/models.ts b/poc/ai/models.ts index 2931456..5bfe136 100644 --- a/poc/ai/models.ts +++ b/poc/ai/models.ts @@ -16,6 +16,8 @@ export interface Candidate { referenceOnly?: boolean /** matte — маска объекта (по умолчанию), depth — относительная глубина (рельеф лица, а не силуэт). */ kind?: 'matte' | 'depth' + /** Бэкенды, на которых модель реально работает (по замерам PoC); первый — предпочтительный. */ + backends?: ('wasm' | 'webgpu')[] /** Внешние веса (.onnx_data) рядом с моделью. */ data?: { path: string; url: string; bytes: number } } @@ -24,6 +26,19 @@ const IMNET = { mean: [0.485, 0.456, 0.406] as [number, number, number], std: [0 const ZERO = { mean: [0, 0, 0] as [number, number, number], std: [1, 1, 1] as [number, number, number] } const HALF = { mean: [0.5, 0.5, 0.5] as [number, number, number], std: [1, 1, 1] as [number, number, number] } +/** Где модель работает по замерам владельца: остальные бэкенды дают ошибку, пустую маску или минуты. depth — не проверено, оба. */ +export const BACKENDS: Record = { + 'birefnet-lite-512-fp16': ['wasm'], 'birefnet-lite-512-fp32': ['wasm'], + 'ormbg-int8': ['wasm'], 'ormbg-fp16': ['wasm'], + 'inspyrenet-fp16': ['webgpu'], + 'isnet-imgly-fp16': ['webgpu'], 'isnet-imgly-fp32': ['webgpu'], + 'isnet-general-q': ['wasm', 'webgpu'], 'isnet-pim-q': ['wasm'], 'isnet-bw-fp32': ['wasm'], + 'u2netp': ['wasm'], 'u2net-bw-fp32': ['wasm'], + 'rmbg14-q': ['wasm', 'webgpu'], + 'depth-v2-small-q4f16': ['wasm', 'webgpu'], 'depth-v2-small-fp16': ['webgpu', 'wasm'], +} +export const backendsOf = (id: string): ('wasm' | 'webgpu')[] => BACKENDS[id] ?? ['wasm', 'webgpu'] + export const CANDIDATES: Candidate[] = [ { id: 'isnet-general-q', label: 'ISNet general int8 (Apache-2.0, 46 МБ)', url: hf('Ko033/isnet-general-use-onnx', '5349b617911fd60c619b52f32e2b593517b78df3', 'onnx/model_quantized.onnx'), bytes: 45902969, license: 'Apache-2.0', size: 1024, ...HALF, sigmoid: false }, { id: 'u2netp', label: 'U²-Net-P (Apache-2.0, 4.6 МБ)', url: hf('Heliosoph/u2net-onnx', '7fc34deee10329bc039c10a73b98090d0c6f5c59', 'u2netp.onnx'), bytes: 4574861, license: 'Apache-2.0', size: 320, ...IMNET, sigmoid: false }, From 619727a9cb39444d02dce341889d51218c1f9b90 Mon Sep 17 00:00:00 2001 From: skarakovski Date: Wed, 21 Oct 2026 21:29:40 +0700 Subject: [PATCH 18/38] poc: soften relief (per-layer smoothing, depth gamma, slope limit, final smoothing, bilinear nodes) Co-Authored-By: Claude Sonnet 5.5 --- poc/ai/FINDINGS.md | 3 +++ poc/ai/index.html | 10 ++++++++-- poc/ai/keychain.ts | 35 ++++++++++++++++++++++++++++++++--- 3 files changed, 43 insertions(+), 5 deletions(-) diff --git a/poc/ai/FINDINGS.md b/poc/ai/FINDINGS.md index c55f63e..88961e4 100644 --- a/poc/ai/FINDINGS.md +++ b/poc/ai/FINDINGS.md @@ -78,3 +78,6 @@ ### PoC пайплайна Keychain (index.html) и PoC фото-вырезов (cutout.html) `npm run poc:ai`: `/` — Keychain по этапам (1 изображение и кроп → 2 силуэт: модель маски, яркость или весь кадр → 3 глубина: модель глубины, считается по кропу → 4 детали: локальный контраст → 5 смесь контура, глубины и деталей → 3D-рельеф на three.js с орбитой и подсветкой); `/cutout.html` — сравнение с классикой для change 32. Модели силуэта и глубины выбираются отдельно и считаются независимо; результаты хранятся до смены снимка. + +### Мягкость рельефа (PoC пайплайна) +Ступени и обрывы давали сама глубина (отвесные скачки на границах уха и головы, полосы от 4-битных весов q4f16) и бугристый локальный контраст. Смягчение, которое нужно перенести в `34`: сглаживание каждого слоя в мм (глубина 0.6, детали 0.3), гамма глубины, итоговое сглаживание (0.3 мм, внутри маски, нормированная свёртка), **предел наклона** (две огибающие конусом + среднее; 1.5 ≈ 56°) и билинейная выборка высоты в узлах сетки. Усиление деталей по умолчанию 0.7. diff --git a/poc/ai/index.html b/poc/ai/index.html index 08c464a..2b65c4c 100644 --- a/poc/ai/index.html +++ b/poc/ai/index.html @@ -54,12 +54,15 @@ + +

    4Детали (локальный контраст)

    - + +

    5Смесь формы → высота

    @@ -69,7 +72,10 @@
    - +
    + + + 1.5 ≈ 56° от горизонтали: отвесные ступени становятся склонами
    diff --git a/poc/ai/keychain.ts b/poc/ai/keychain.ts index aaa3615..ef0c213 100644 --- a/poc/ai/keychain.ts +++ b/poc/ai/keychain.ts @@ -85,6 +85,30 @@ function getWork(): Work | null { const ones = (n: number) => new Uint8Array(n).fill(1) const smooth01 = (t: number) => { const x = Math.min(1, Math.max(0, t)); return x * x * (3 - 2 * x) } +/** Гауссово сглаживание только внутри маски (нормированная свёртка): значения снаружи не просачиваются внутрь. */ +function blurIn(a: Float32Array, w: number, h: number, sigmaPx: number, mask: Uint8Array): Float32Array { + return sigmaPx < 0.4 ? a : gaussianBlur(a, w, h, Math.max(MIN_SIGMA, sigmaPx), mask) +} +/** Предел наклона: нижняя и верхняя огибающие конусом (две хамфер-проходки), результат — среднее. Отвесные ступени становятся склонами. */ +function limitSlope(v: Float32Array, w: number, h: number, L: number, mask: Uint8Array): Float32Array { + if (L <= 0) return v + const lo = Float32Array.from(v), hi = Float32Array.from(v), D = L * Math.SQRT2 + const pass = (fwd: boolean) => { + const y0 = fwd ? 0 : h - 1, y1 = fwd ? h : -1, dy = fwd ? 1 : -1, x0 = fwd ? 0 : w - 1, x1 = fwd ? w : -1, dx = fwd ? 1 : -1 + for (let y = y0; y !== y1; y += dy) for (let x = x0; x !== x1; x += dx) { + const i = y * w + x; if (!mask[i]) continue + for (const [ox, oy, d] of [[-dx, 0, L], [0, -dy, L], [-dx, -dy, D], [dx, -dy, D]] as const) { + const xx = x + ox, yy = y + oy; if (xx < 0 || yy < 0 || xx >= w || yy >= h) continue + const j = yy * w + xx; if (!mask[j]) continue + if (lo[j] + d < lo[i]) lo[i] = lo[j] + d + if (hi[j] - d > hi[i]) hi[i] = hi[j] - d + } + } + } + pass(true); pass(false) + return v.map((_, i) => 0.5 * (lo[i] + hi[i])) +} + function silhouette(w: Work, pxMm: number): { mask: Uint8Array; note: string } { const n = w.ww * w.wh, src = $('silsrc').value let mask: Uint8Array, note = '' @@ -104,6 +128,7 @@ function silhouette(w: Work, pxMm: number): { mask: Uint8Array; note: string } { mask = mask.map((_, i) => (cc.labels[i] === best && best ? 1 : 0)) as Uint8Array } } + if (src === 'brightness') { const b = blurIn(Float32Array.from(mask), w.ww, w.wh, 0.3 * pxMm, ones(n)); mask = b.map((v) => (v >= 0.5 ? 1 : 0)) as unknown as Uint8Array } // ровный край без пиксельной лесенки if (src !== 'frame') { if (chk('fill')) mask = fillHoles(mask, w.ww, w.wh).mask mask = removeSpecks(mask, w.ww, w.wh, Math.round(2 * pxMm * pxMm)) @@ -164,11 +189,12 @@ function update() { const mask = chk('lm') ? sil.mask : ones(n) // слой «маска»: выключен — рельеф без вырезания по силуэту $('st-sil').textContent = note show('pv-mask', w.ww, w.wh, (i) => (mask[i] ? [w.rgb[i * 3], w.rgb[i * 3 + 1], w.rgb[i * 3 + 2]] : [30, 30, 40])) - const dn = normDepth(w, mask) + let dn = normDepth(w, mask) + if (dn) { const g = num('dgam'); if (g !== 1) dn = dn.map((v) => Math.pow(v, g)); dn = blurIn(dn, w.ww, w.wh, num('dsm') * pxMm, mask) } const same = depthRec && JSON.stringify(depthRec.crop) === JSON.stringify(c) $('st-depth').textContent = depthRec ? (same ? 'посчитана для этого кропа' : 'посчитана для другого кропа (можно пересчитать)') : 'не посчитана' show('pv-depth', w.ww, w.wh, (i) => (mask[i] ? (dn ? gr(dn[i]) : [90, 90, 90]) : [30, 30, 40])) - const dt = detail(w, pxMm) + const dt = blurIn(detail(w, pxMm), w.ww, w.wh, num('tsm') * pxMm, mask) show('pv-detail', w.ww, w.wh, (i) => (mask[i] ? gr(dt[i]) : [30, 30, 40])) // смесь const dist = edt(mask, w.ww, w.wh), bevelPx = Math.max(1, num('bevel') * pxMm), roll = num('roll') / 100 @@ -179,6 +205,9 @@ function update() { const contour = smooth01(dist[i] / bevelPx), tot = wc + wd + wt, mixed = tot === 0 ? contour : (wc * contour + wd * (dn ? dn[i] : 0) + wt * dt[i]) / tot value[i] = mixed * ((1 - roll) + roll * contour) } + let final = blurIn(value, w.ww, w.wh, num('fsm') * pxMm, mask) + { const rng = Math.max(0.01, num('range')); final = limitSlope(final, w.ww, w.wh, num('slope') / pxMm / rng, mask) } // наклон: мм высоты на мм детали → доли диапазона на пиксель + value.set(final) show('pv-mix', w.ww, w.wh, (i) => (mask[i] ? gr(value[i]) : [30, 30, 40])) build3D(w, mask, value, partW, partH) } @@ -215,7 +244,7 @@ function build3D(w: Work, mask: Uint8Array, value: Float32Array, partW: number, const floor = num('floor'), range = num('range'), mmx = partW / w.ww, mmy = partH / w.wh const X = (i: number) => (i * s + 0.5) * mmx - partW / 2, Y = (j: number) => partH / 2 - (j * s + 0.5) * mmy const inside = (i: number, j: number) => mask[Math.min(w.wh - 1, j * s) * w.ww + Math.min(w.ww - 1, i * s)] === 1 - const top = (i: number, j: number) => floor + range * value[Math.min(w.wh - 1, j * s) * w.ww + Math.min(w.ww - 1, i * s)] + const top = (i: number, j: number) => floor + range * bil(value, w.ww, w.wh, Math.min(w.ww - 0.5, i * s + 0.5), Math.min(w.wh - 0.5, j * s + 0.5)) // верх: индексированная сетка, только ячейки, у которых все 4 узла внутри маски const pos = new Float32Array(nx * ny * 3) for (let j = 0; j < ny; j++) for (let i = 0; i < nx; i++) { const p = (j * nx + i) * 3; pos[p] = X(i); pos[p + 1] = Y(j); pos[p + 2] = inside(i, j) ? top(i, j) : 0 } From 93d4a0d2a6a379d3dd3c424e3f55d4fffd27b057 Mon Sep 17 00:00:00 2001 From: skarakovski Date: Wed, 21 Oct 2026 21:36:52 +0700 Subject: [PATCH 19/38] poc: checkerboard under the silhouette in previews, face/whole-animal presets, working raster size Co-Authored-By: Claude Sonnet 5.5 --- poc/ai/index.html | 2 ++ poc/ai/keychain.ts | 23 ++++++++++++++++------- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/poc/ai/index.html b/poc/ai/index.html index 2b65c4c..ae2e393 100644 --- a/poc/ai/index.html +++ b/poc/ai/index.html @@ -35,6 +35,7 @@ +

    2Силуэт

    @@ -69,6 +70,7 @@
    +
    пресет:

    diff --git a/poc/ai/keychain.ts b/poc/ai/keychain.ts index ef0c213..6f71adc 100644 --- a/poc/ai/keychain.ts +++ b/poc/ai/keychain.ts @@ -63,9 +63,9 @@ function bil(p: Float32Array, w: number, h: number, x: number, y: number): numbe function getWork(): Work | null { if (!img) return null const c = cropNow(), { x0, y0, pw, ph } = cropPx(c, img) - const key = [x0, y0, pw, ph, matte ? 1 : 0, depthRec ? depthRec.crop.x + ',' + depthRec.crop.y + ',' + depthRec.crop.w + ',' + depthRec.crop.h : 0].join('|') + const key = [num('res'), x0, y0, pw, ph, matte ? 1 : 0, depthRec ? depthRec.crop.x + ',' + depthRec.crop.y + ',' + depthRec.crop.w + ',' + depthRec.crop.h : 0].join('|') if (work && work.key === key) return work - const k = Math.min(1, 640 / Math.max(pw, ph)), ww = Math.max(8, Math.round(pw * k)), wh = Math.max(8, Math.round(ph * k)) + const k = Math.min(1, num('res') / Math.max(pw, ph)), ww = Math.max(8, Math.round(pw * k)), wh = Math.max(8, Math.round(ph * k)) const gray = new Float32Array(ww * wh), rgb = new Uint8Array(ww * wh * 3) const mw = matte ? new Float32Array(ww * wh) : null, dw = depthRec ? new Float32Array(ww * wh) : null const dpx = depthRec ? cropPx(depthRec.crop, img) : null @@ -162,6 +162,8 @@ function show(id: string, w: number, h: number, px: (i: number) => [number, numb for (let i = 0; i < w * h; i++) { const [r, gg, b] = px(i); im.data[i * 4] = r; im.data[i * 4 + 1] = gg; im.data[i * 4 + 2] = b; im.data[i * 4 + 3] = 255 } g.putImageData(im, 0, 0) } +/** Подложка-шашка (как «прозрачность» в графических редакторах) для всего, что вне силуэта: клетка 8 px. */ +const checker = (i: number, w: number): [number, number, number] => { const x = (i % w) >> 3, y = Math.floor(i / w) >> 3; return (x + y) & 1 ? [214, 214, 214] : [170, 170, 170] } const gr = (v: number): [number, number, number] => { const t = Math.round(Math.min(1, Math.max(0, v)) * 255); return [t, t, t] } function previewImage(c: Crop) { @@ -186,16 +188,17 @@ function update() { const w = getWork(); if (!w || !img) return const partW = num('partw'), pxMm = w.ww / partW, partH = partW * w.wh / w.ww, n = w.ww * w.wh const sil = silhouette(w, pxMm), note = sil.note - const mask = chk('lm') ? sil.mask : ones(n) // слой «маска»: выключен — рельеф без вырезания по силуэту + const silMask = sil.mask // силуэт всегда показывается в превью этапа 2, даже когда слой «маска» выключен для 3D + const mask = chk('lm') ? silMask : ones(n) // слой «маска»: выключен — рельеф без вырезания по силуэту $('st-sil').textContent = note - show('pv-mask', w.ww, w.wh, (i) => (mask[i] ? [w.rgb[i * 3], w.rgb[i * 3 + 1], w.rgb[i * 3 + 2]] : [30, 30, 40])) + show('pv-mask', w.ww, w.wh, (i) => (silMask[i] ? [w.rgb[i * 3], w.rgb[i * 3 + 1], w.rgb[i * 3 + 2]] : checker(i, w.ww))) let dn = normDepth(w, mask) if (dn) { const g = num('dgam'); if (g !== 1) dn = dn.map((v) => Math.pow(v, g)); dn = blurIn(dn, w.ww, w.wh, num('dsm') * pxMm, mask) } const same = depthRec && JSON.stringify(depthRec.crop) === JSON.stringify(c) $('st-depth').textContent = depthRec ? (same ? 'посчитана для этого кропа' : 'посчитана для другого кропа (можно пересчитать)') : 'не посчитана' - show('pv-depth', w.ww, w.wh, (i) => (mask[i] ? (dn ? gr(dn[i]) : [90, 90, 90]) : [30, 30, 40])) + show('pv-depth', w.ww, w.wh, (i) => (mask[i] ? (dn ? gr(dn[i]) : [90, 90, 90]) : checker(i, w.ww))) const dt = blurIn(detail(w, pxMm), w.ww, w.wh, num('tsm') * pxMm, mask) - show('pv-detail', w.ww, w.wh, (i) => (mask[i] ? gr(dt[i]) : [30, 30, 40])) + show('pv-detail', w.ww, w.wh, (i) => (mask[i] ? gr(dt[i]) : checker(i, w.ww))) // смесь const dist = edt(mask, w.ww, w.wh), bevelPx = Math.max(1, num('bevel') * pxMm), roll = num('roll') / 100 const wc = chk('lc') ? num('wc') : 0, wd = dn && chk('ld') ? num('wd') : 0, wt = chk('lt') ? num('wt') : 0 @@ -208,7 +211,7 @@ function update() { let final = blurIn(value, w.ww, w.wh, num('fsm') * pxMm, mask) { const rng = Math.max(0.01, num('range')); final = limitSlope(final, w.ww, w.wh, num('slope') / pxMm / rng, mask) } // наклон: мм высоты на мм детали → доли диапазона на пиксель value.set(final) - show('pv-mix', w.ww, w.wh, (i) => (mask[i] ? gr(value[i]) : [30, 30, 40])) + show('pv-mix', w.ww, w.wh, (i) => (mask[i] ? gr(value[i]) : checker(i, w.ww))) build3D(w, mask, value, partW, partH) } @@ -298,6 +301,12 @@ async function runModel(kind: 'sil' | 'depth') { } // ---------- события ---------- +const PRESETS: Record> = { + // значения в мм детали; лицо — кроп морды ~40 мм, целиком — всё животное, мелочь теряется, поэтому деталь шире и сглаживание меньше + face: { partw: 40, dsm: 0.6, tsm: 0.3, fsm: 0.3, lcr: 1.4, lca: 0.7, slope: 1.5, bevel: 3, range: 2 }, + whole: { partw: 70, dsm: 0.35, tsm: 0.15, fsm: 0.15, lcr: 0.9, lca: 0.6, slope: 2.5, bevel: 3, range: 2.5 }, +} +for (const k of Object.keys(PRESETS)) $('pre-' + k).addEventListener('click', () => { for (const [id, v] of Object.entries(PRESETS[k])) $(id).value = String(v); schedule() }) $('runsil').addEventListener('click', () => runModel('sil')) $('rundepth').addEventListener('click', () => runModel('depth')) $('sample').addEventListener('change', () => { $('file').value = ''; loadImage() }) From 973e16a3f6666f6b275d84057964aa4cb9ece839 Mon Sep 17 00:00:00 2001 From: skarakovski Date: Wed, 21 Oct 2026 21:46:54 +0700 Subject: [PATCH 20/38] poc: smooth outline in 3D (subpixel edge from soft mask), higher default resolution Co-Authored-By: Claude Sonnet 5.5 --- poc/ai/FINDINGS.md | 3 +++ poc/ai/index.html | 4 ++-- poc/ai/keychain.ts | 55 ++++++++++++++++++++++++++++++++-------------- 3 files changed, 44 insertions(+), 18 deletions(-) diff --git a/poc/ai/FINDINGS.md b/poc/ai/FINDINGS.md index 88961e4..2692f66 100644 --- a/poc/ai/FINDINGS.md +++ b/poc/ai/FINDINGS.md @@ -81,3 +81,6 @@ ### Мягкость рельефа (PoC пайплайна) Ступени и обрывы давали сама глубина (отвесные скачки на границах уха и головы, полосы от 4-битных весов q4f16) и бугристый локальный контраст. Смягчение, которое нужно перенести в `34`: сглаживание каждого слоя в мм (глубина 0.6, детали 0.3), гамма глубины, итоговое сглаживание (0.3 мм, внутри маски, нормированная свёртка), **предел наклона** (две огибающие конусом + среднее; 1.5 ≈ 56°) и билинейная выборка высоты в узлах сетки. Усиление деталей по умолчанию 0.7. + +### Контур без ступеней (3D) +Лесенка по краю шла от сетки: в 3D брались только ячейки, где все четыре узла внутри маски. Исправлено: край кладётся по мягкой маске (σ = 1 px) с субпиксельной точностью, внешние узлы краевых ячеек сдвигаются вдоль ребра к линии F = 0.5, стенки идут по этой линии. Это же нужно сделать в `34` при построении контура (`shapeCrossSection`-подобное сглаживание маски перед построением, а не ступенчатая граница узлов). Рабочий растр по умолчанию 800 px, узлов 320. diff --git a/poc/ai/index.html b/poc/ai/index.html index ae2e393..ff5e4da 100644 --- a/poc/ai/index.html +++ b/poc/ai/index.html @@ -35,7 +35,7 @@ - +

    2Силуэт

    @@ -84,7 +84,7 @@
    diff --git a/poc/ai/keychain.ts b/poc/ai/keychain.ts index 0e5015d..a30ed28 100644 --- a/poc/ai/keychain.ts +++ b/poc/ai/keychain.ts @@ -279,8 +279,8 @@ function build3D(w: Work, mask: Uint8Array, value: Float32Array, partW: number, const nodes = Math.max(60, num('nodes')), s = Math.max(1, Math.ceil(Math.max(w.ww, w.wh) / nodes)) const nx = Math.floor((w.ww - 1) / s) + 1, ny = Math.floor((w.wh - 1) / s) + 1 const floor = num('floor'), range = num('range'), mmx = partW / w.ww, mmy = partH / w.wh - // мягкая маска (σ = 1 px): по ней край кладётся с субпиксельной точностью, а не по ступеням сетки - const soft = gaussianBlur(Float32Array.from(mask), w.ww, w.wh, 1, ones(w.ww * w.wh)) + // мягкая маска: размытие в мм (в вырезах σ = 1 px при 4 px/мм ≈ 0.25 мм); по изолинии 0.5 край кладётся с субпиксельной точностью + const soft = gaussianBlur(Float32Array.from(mask), w.ww, w.wh, Math.max(1, num('osm') * (w.ww / partW)), ones(w.ww * w.wh)) // как в вырезах: σ ≈ 0.25 мм const px = (i: number) => Math.min(w.ww - 0.5, i * s + 0.5), py = (j: number) => Math.min(w.wh - 0.5, j * s + 0.5) const F = new Float32Array(nx * ny), isIn = new Uint8Array(nx * ny), Z = new Float32Array(nx * ny) for (let j = 0; j < ny; j++) for (let i = 0; i < nx; i++) { From dcd34e06c6e77d348736f91b73226f8d12812943 Mon Sep 17 00:00:00 2001 From: skarakovski Date: Wed, 21 Oct 2026 22:27:28 +0700 Subject: [PATCH 24/38] poc: engrave detail mode, vector outline with corner preservation, drawing preset auto-applied; plan 34 updated Co-Authored-By: Claude Sonnet 5.5 --- .../34-keychain-photo-relief/design.md | 3 + .../specs/keychain-photo-relief/spec.md | 17 +++- .../changes/34-keychain-photo-relief/tasks.md | 3 +- poc/ai/FINDINGS.md | 5 + poc/ai/index.html | 6 +- poc/ai/keychain.ts | 34 +++++-- poc/ai/outline.ts | 93 +++++++++++++++++++ 7 files changed, 151 insertions(+), 10 deletions(-) create mode 100644 poc/ai/outline.ts diff --git a/openspec/changes/34-keychain-photo-relief/design.md b/openspec/changes/34-keychain-photo-relief/design.md index e41e06e..45d3f20 100644 --- a/openspec/changes/34-keychain-photo-relief/design.md +++ b/openspec/changes/34-keychain-photo-relief/design.md @@ -30,6 +30,9 @@ Depth V2 Small gives a smooth depth map (the owner found it good as a form); on - **Crop.** `crop = {x, y, w, h}` fractions of the image (default full). `buildSilhouette` and `buildGrid` read the planes (gray, alpha, matte, depth) through a cropped view made once per change in the worker; size, text and mount fractions stay relative to the silhouette box. `CropEditor` over the image: drag, corner and edge handles, aspect lock (free, 1:1, 4:3, 3:4), fit to subject from the AI mask box, reset; minimum 5 % of a side; live update of the preview; also in the enlarged mask dialog. Changing the crop never re-runs the matte model. - **Whole frame silhouette.** `silhouette.source: 'frame'`: every pixel of the crop is material; threshold, close, areas and specks are hidden. With the Base plate (rounded rectangle, circle) this is a portrait plaque; the relief edge still rolls off through the bevel ceiling and rim. - **Outline without steps.** Stairs on the edge come from three places, and each is removed: (1) a binary mask quantises the edge to a pixel, so the outline is taken from a continuous field instead (the soft matte of `33`, the grey level for Brightness, a blurred mask otherwise, iso-line at 0.5, sub-pixel); (the smoothing is the one that already works for cutouts: `traceSilhouette` blurs the mask with σ = 1 px at 4 px/mm, that is about 0.25 mm, takes the iso-line at 128 and simplifies it by 0.15 mm; the outline here reuses that routine or its parameters, expressed in mm of the part and exposed as an "Outline smoothness" value, default 0.25 mm, so thin spikes can keep sharper tips with a smaller value); (2) cutting the relief by whole grid cells leaves a staircase of cells, so the outline is traced as a vector polygon (`traceAlpha`, marching squares with sub-pixel interpolation, as for cutouts and text), simplified with a small tolerance (0.03–0.05 mm) and smoothed (Chaikin or a curvature-limited smoothing that keeps real corners such as spike tips), and the relief is clipped by that polygon exactly as the Shape mode already does with `shapeCrossSection` (extrude the polygon as a `CrossSection`, intersect with the relief, so the wall is exact and independent of the grid cell); (3) the grid only supplies heights, so its cell size no longer limits the outline. The PoC shows the effect with marching-squares clipping of the grid cells along the iso-line (`poc/ai/keychain.ts`): the outline is smooth to well under the cell size. Large enclosed areas stay open and small ones between strokes are filled through the existing areas of change 18 (`findHoles`), not a new rule. +- **Detail as engraving, not bumps.** Mapping the tone of a photo straight to height turns fur and shading into random bumps. The default Detail mode is **Engrave**: only the darkest features (eyes, nose, mouth, stripes, whisker roots) are pressed in as grooves, `1 − groove` with `groove = smoothstep((dark − t) / (1 − t))`, `dark = 2·max(0, 0.5 − tone)` and a user "dark threshold" `t` (about 0.15 for a face crop), while mid-tones stay smooth; **Relief** (tone as height, the former behaviour) and **Both** remain as options. Grooves read well on a face at keychain scale and do not create noise on the body. The groove depth is the Detail share of the mix. +- **Corner-preserving outline.** The outline is a vector polygon traced from the soft field (resampled at ~0.7 px, corners found where the direction turns more than a threshold angle, default 50°, with local-maximum suppression), smoothed by a Gaussian along the curve with the corners held fixed (odd reflection between corners, ring smoothing when there are none), so curves become smooth while spike tips stay sharp; the mesh edge is cut exactly along it (signed distance to the polygon near the edge, marching squares on the cells). The PoC (`poc/ai/outline.ts`) implements this and shows crisp spike tips on the line-art samples. +- **Drawing preset.** Besides Face and Whole animal, a "Drawing / logo" preset (Brightness silhouette, contour only, 60 mm, bevel 1.2 mm, outline smoothness 0.15 mm, large areas open, small ones filled) is chosen automatically for images with few tones and thin strokes. - **Presets.** Subtle / Portrait / Deep set the three shares, `hmax − floor`, boost radius and amount and levels (Portrait: about 30 / 40 / 30, 2 mm range, radius 1.4 mm, 4 soft levels; to be confirmed with the spike) and show Custom when edited; data next to the profile presets. - **Heights and printing.** A height field grows from a flat back (existing design), prints face up without supports. Detail finer than the grid cell is lost: `quality` and the resolution warnings apply, with a hint when the cell is larger than about a third of the boost radius. - **Persistence and migration.** Depth planes are stored in `imageStore` as `depth` records keyed by image, model, revision and crop, restored on load, kept when the model is deleted, dropped with the image, memory fallback with the `persistent` warning. `crop`, mix, boost and levels are keychain params (`migrateKeychain` fills defaults: full crop, `kind` unchanged, boost 0, levels 0). If the stored depth is missing the Depth share counts as zero with a "run again" message, never an empty relief. diff --git a/openspec/changes/34-keychain-photo-relief/specs/keychain-photo-relief/spec.md b/openspec/changes/34-keychain-photo-relief/specs/keychain-photo-relief/spec.md index 7ae4aa4..685e017 100644 --- a/openspec/changes/34-keychain-photo-relief/specs/keychain-photo-relief/spec.md +++ b/openspec/changes/34-keychain-photo-relief/specs/keychain-photo-relief/spec.md @@ -108,6 +108,21 @@ The user SHALL be able to choose Whole frame as the silhouette source, which mak - **WHEN** the relief reaches the plaque's edge - **THEN** it rolls off to the rim height and does not end as a vertical wall +### Requirement: Detail as engraving +The Detail source SHALL offer three modes: Engrave (the darkest features of the photo are pressed in as grooves and the mid-tones stay smooth), Relief (the tone drives the height) and Both. Engrave SHALL be the default. The user SHALL be able to set how dark a feature must be to be engraved. + +#### Scenario: Engrave +- **WHEN** the mode is Engrave on a face crop +- **THEN** the eyes, the nose and the dark stripes are grooves and the rest of the surface is not bumpy + +#### Scenario: Threshold +- **WHEN** the dark threshold is lowered +- **THEN** more of the photo's darker features are engraved + +#### Scenario: Relief +- **WHEN** the mode is Relief +- **THEN** the height follows the tone as before + ### Requirement: Photo presets The app SHALL offer presets for the Photo source (Subtle, Portrait, Deep) that set the three shares, the height range, and the detail radius, amount and levels. Each value SHALL stay editable and the panel SHALL then show Custom. @@ -146,7 +161,7 @@ The Photo source SHALL be offered only in Image mode and SHALL NOT be offered fo - **THEN** a hint suggests cropping closer or using Whole frame ### Requirement: Smooth outline -The outline of a keychain made from an image SHALL be smooth: it SHALL NOT show steps from the pixels of the image, from the cells of the relief grid or from the resolution of the mask, and its position SHALL follow the edge of the subject to a fraction of a grid cell. Sharp corners that exist in the subject, such as the tip of a thin spike, SHALL stay sharp. +The outline of a keychain made from an image SHALL be smooth: it SHALL NOT show steps from the pixels of the image, from the cells of the relief grid or from the resolution of the mask, and its position SHALL follow the edge of the subject to a fraction of a grid cell. Sharp corners that exist in the subject, such as the tip of a thin spike, SHALL stay sharp while the curves between them are smoothed. The user SHALL be able to set the smoothness and the angle from which a vertex counts as a corner. #### Scenario: No steps - **WHEN** the silhouette of a photo or a drawing is made into a keychain at any quality setting diff --git a/openspec/changes/34-keychain-photo-relief/tasks.md b/openspec/changes/34-keychain-photo-relief/tasks.md index b5b1e8c..ff4fa6a 100644 --- a/openspec/changes/34-keychain-photo-relief/tasks.md +++ b/openspec/changes/34-keychain-photo-relief/tasks.md @@ -9,7 +9,8 @@ - [ ] 2.1 `geometry/raster/tone.ts`: `localContrast(gray, w, h, radiusPx, amount)`, `quantise(values, levels, softness)` and `normaliseDepth(plane, mask)` (2nd–98th percentile inside the mask, clamped 0…1). Tests: amount 0 gives a flat 0.5, a constant plane gives no NaN, a small bump on a gradient survives the boost and the mean stays about 0.5, quantise gives exactly `levels` distinct values at softness 0 and monotone ramps at softness 1, depth normalisation ignores outliers and uses only the mask, a constant depth gives a flat result. - [ ] 2.2 `SourceParams.kind: 'photo'` with `mix {contour, depth, detail}`, `depthId`, `boost`, `boostRadius`, `levels`, `levelSoftness`; `registerDepth` and `depthRegistry`; `buildGrid` builds `value = (wC·contour + wD·depth + wT·detail) / Σw` through the brightness path (contrast, gamma, invert, smoothing, rim ceiling, clamp); missing depth counts as zero share and is reported in `KeychainInfo`; radius mm → pixels from the crop and part size. Tests: with `distance` or `brightness` the existing regression volumes and bounding boxes are unchanged; Contour only equals the Distance source; shares are proportions (doubling changes nothing); a synthetic dome depth gives a dome relief inside the mask and the edge still rolls off; a far background does not flatten a shallow subject; Detail does not change the mean height; the same radius in mm gives the same detail scale at two resolutions; missing depth reports and uses the other shares; sum zero falls back to Contour. - [ ] 2.3 `KeychainParams.crop` and `silhouette.source: 'frame'` (full mask): the cropped view of the planes (gray, alpha, matte, depth) in `buildSilhouette` and `buildGrid`. Tests: no crop leaves volumes unchanged; a half-width crop halves the part box and keeps the relief of that region; the frame source makes the whole crop material and hides tracing steps; the matte is cropped without re-registration. -- [ ] 2.3a Exact outline for image silhouettes: the sub-pixel outline from the continuous field (`traceAlpha`), simplification and corner-preserving smoothing, the relief clipped by the polygon as `CrossSection` (as `shapeCrossSection` does in Shape mode), large areas open and small ones filled by the existing area rules. Tests: the outline of a synthetic disc and of a thin spike deviates from the true shape by less than 0.1 grid cell at two qualities, no vertex of the wall lies on a grid line pattern (no staircase: the number of direction changes of the outline stays close to the true shape's), a spike tip keeps its length within one cell, a coarser quality changes only the relief and not the outline, bounding box and volume regressions of the existing keychain tests stay within their tolerances for a traced silhouette. +- [ ] 2.3a Exact outline for image silhouettes: the sub-pixel outline from the continuous field (`traceAlpha`), simplification and corner-preserving smoothing, the relief clipped by the polygon as `CrossSection` (as `shapeCrossSection` does in Shape mode), large areas open and small ones filled by the existing area rules. The outline smoothing keeps sharp corners: resample, find corners by the turning angle, Gaussian along the curve with corners fixed. Tests: the outline of a synthetic disc and of a thin spike deviates from the true shape by less than 0.1 grid cell at two qualities, no vertex of the wall lies on a grid line pattern (no staircase: the number of direction changes of the outline stays close to the true shape's), a spike tip keeps its length within one cell, a coarser quality changes only the relief and not the outline, bounding box and volume regressions of the existing keychain tests stay within their tolerances for a traced silhouette. +- [ ] 2.3b Detail modes Engrave / Relief / Both with the dark threshold (`1 − groove`), default Engrave; presets Face, Whole animal and Drawing / logo (chosen automatically for few-tone thin-stroke images). Tests: Engrave leaves mid-tones at one level and lowers only pixels darker than the threshold, a lower threshold engraves a superset, Relief equals the former tone mapping, the Drawing preset leaves a one-tone image with contour only. - [ ] 2.4 `migrateKeychain` fills the new fields for old records and normalises partial values. Tests: round trip, partial and corrupt values fall back to defaults, old records keep their source. ## 3. ML and image side diff --git a/poc/ai/FINDINGS.md b/poc/ai/FINDINGS.md index ef6f1ec..50a195a 100644 --- a/poc/ai/FINDINGS.md +++ b/poc/ai/FINDINGS.md @@ -92,3 +92,8 @@ ### Контур совсем без ступеней Ступени давали три вещи: бинарная маска (край по пикселю), резка рельефа целыми ячейками сетки (лесенка ячеек) и сетка как предел точности края. В PoC край теперь режется marching squares по изолинии 0.5 мягкой маски (вершины контура на изолинии), на рисунке «галочка» шипы и кончики без ступеней. В приложении: контур как векторный полигон (`traceAlpha`, упрощение и сглаживание с сохранением углов) и обрезка рельефа этим полигоном как `CrossSection` (как `shapeCrossSection` в режиме Shape); сетка задаёт только высоты. Записано в `34` (design, spec, задача 2.3a). + +### Детали гравировкой и векторный контур +- Бугры давало прямое «тон → высота». Режим «гравировка»: продавливаются только самые тёмные черты (глаза, нос, рот, полосы), средние тона гладкие; порог тёмного ~0.15 для кропа лица. Режимы «рельеф» и «оба» сохранены. +- Контур «вау» на рисунках: полигон с передискретизацией, поиск острых углов по повороту (50°), гаусс вдоль кривой с закреплёнными углами, резка ячеек по изолинии знакового расстояния. Острия шипов остаются острыми, кривые плавные. +- Пресеты: лицо (кроп), животное целиком, рисунок/логотип; при выборе образца пресет применяется автоматически. diff --git a/poc/ai/index.html b/poc/ai/index.html index 426a554..844fd1f 100644 --- a/poc/ai/index.html +++ b/poc/ai/index.html @@ -49,6 +49,8 @@ + + @@ -66,6 +68,8 @@

    4Детали (локальный контраст)

    + + @@ -75,7 +79,7 @@
    -
    пресет:
    +
    пресет:

    diff --git a/poc/ai/keychain.ts b/poc/ai/keychain.ts index a30ed28..7078a09 100644 --- a/poc/ai/keychain.ts +++ b/poc/ai/keychain.ts @@ -11,6 +11,7 @@ import { close } from '../../src/geometry/raster/morphology' import { findHoles, fillHoles, labelComponents, removeSpecks } from '../../src/geometry/raster/components' import { backendsOf, CANDIDATES, type Candidate } from './models' import { infer } from './engine' +import { signedDistance, vectorOutline } from './outline' const $ = (id: string) => document.getElementById(id) as T const num = (id: string) => +$(id).value @@ -230,7 +231,13 @@ function update() { const same = depthRec && JSON.stringify(depthRec.crop) === JSON.stringify(c) $('st-depth').textContent = depthRec ? (same ? 'посчитана для этого кропа' : 'посчитана для другого кропа (можно пересчитать)') : 'не посчитана' show('pv-depth', w.ww, w.wh, (i) => (mask[i] ? (dn ? gr(dn[i]) : [90, 90, 90]) : checker(i, w.ww))) - const dt = blurIn(detail(w, pxMm), w.ww, w.wh, num('tsm') * pxMm, mask) + const tone = blurIn(detail(w, pxMm), w.ww, w.wh, num('tsm') * pxMm, mask) + // режим деталей: бугры (тон → высота), гравировка (только тёмные черты продавливаются канавками) или оба + const dmode = $('dmode').value, dth = Math.min(0.95, num('dth')) + const dt = tone.map((t) => { + const groove = smooth01((Math.max(0, 0.5 - t) * 2 - dth) / (1 - dth)) // 0 везде, кроме самых тёмных мест + return dmode === 'bumps' ? t : dmode === 'engrave' ? 1 - groove : 0.5 * t + 0.5 * (1 - groove) + }) show('pv-detail', w.ww, w.wh, (i) => (mask[i] ? gr(dt[i]) : checker(i, w.ww))) // смесь const dist = edt(mask, w.ww, w.wh), bevelPx = Math.max(1, num('bevel') * pxMm), roll = num('roll') / 100 @@ -275,6 +282,7 @@ function frame(partW: number, partH: number, preset: 'top' | 'iso' | null) { $('vtop').addEventListener('click', () => frame(num('partw'), num('partw'), 'top')) $('viso').addEventListener('click', () => frame(num('partw'), num('partw'), 'iso')) +const log2 = document.createElement('span'); log2.className = 'note'; $('stat3d').after(log2) function build3D(w: Work, mask: Uint8Array, value: Float32Array, partW: number, partH: number) { const nodes = Math.max(60, num('nodes')), s = Math.max(1, Math.ceil(Math.max(w.ww, w.wh) / nodes)) const nx = Math.floor((w.ww - 1) / s) + 1, ny = Math.floor((w.wh - 1) / s) + 1 @@ -283,8 +291,17 @@ function build3D(w: Work, mask: Uint8Array, value: Float32Array, partW: number, const soft = gaussianBlur(Float32Array.from(mask), w.ww, w.wh, Math.max(1, num('osm') * (w.ww / partW)), ones(w.ww * w.wh)) // как в вырезах: σ ≈ 0.25 мм const px = (i: number) => Math.min(w.ww - 0.5, i * s + 0.5), py = (j: number) => Math.min(w.wh - 0.5, j * s + 0.5) const F = new Float32Array(nx * ny), isIn = new Uint8Array(nx * ny), Z = new Float32Array(nx * ny) + // векторный контур: изолиния, сглаживание вдоль кривой, острые углы сохраняются; поле F у края заменяется знаковым расстоянием до кривой + let loops: ReturnType | null = null + if (chk('vec')) { + const f1 = gaussianBlur(Float32Array.from(mask), w.ww, w.wh, 0.8, ones(w.ww * w.wh)), pxMmL = w.ww / partW + loops = vectorOutline(Uint8Array.from(f1, (v) => Math.round(v * 255)), w.ww, w.wh, Math.max(0.5, num('osm') * pxMmL), num('cang')) + log2.textContent = `контур: ${loops.length} петель, ${loops.reduce((a, l) => a + l.length, 0)} вершин` + } else log2.textContent = '' for (let j = 0; j < ny; j++) for (let i = 0; i < nx; i++) { - const k = j * nx + i; F[k] = bil(soft, w.ww, w.wh, px(i), py(j)); isIn[k] = F[k] >= 0.5 ? 1 : 0 + const k = j * nx + i; F[k] = bil(soft, w.ww, w.wh, px(i), py(j)) + if (loops && F[k] > 0.003 && F[k] < 0.997) F[k] = Math.min(1, Math.max(0, 0.5 + signedDistance(loops, px(i), py(j)) / (2 * s))) // у края: расстояние до кривой в долях ячейки + isIn[k] = F[k] >= 0.5 ? 1 : 0 Z[k] = floor + range * bil(value, w.ww, w.wh, px(i), py(j)) } const NX = new Float32Array(nx * ny), NY = new Float32Array(nx * ny) @@ -352,15 +369,18 @@ async function runModel(kind: 'sil' | 'depth') { } // ---------- события ---------- -const PRESETS: Record> = { +const PRESETS: Record> = { // значения в мм детали; лицо — кроп морды ~40 мм, целиком — всё животное, мелочь теряется, поэтому деталь шире и сглаживание меньше - face: { wc: 30, wd: 40, wt: 30, partw: 40, dsm: 0.6, tsm: 0.3, fsm: 0.3, lcr: 1.4, lca: 0.7, slope: 1.5, bevel: 3, range: 2 }, - whole: { wc: 30, wd: 55, wt: 15, partw: 70, dsm: 0.35, tsm: 0.25, fsm: 0.2, lcr: 1.2, lca: 0.45, slope: 2.5, bevel: 3, range: 2.5 }, + face: { silsrc: 'frame', dmode: 'engrave', dth: 0.15, wc: 30, wd: 40, wt: 30, partw: 40, dsm: 0.6, tsm: 0.3, fsm: 0.3, lcr: 1.4, lca: 1.1, slope: 1.5, bevel: 3, range: 2.4 }, + whole: { silsrc: 'model', dmode: 'engrave', dth: 0.45, wc: 30, wd: 55, wt: 15, partw: 70, dsm: 0.35, tsm: 0.25, fsm: 0.2, lcr: 1.2, lca: 0.45, slope: 2.5, bevel: 3, range: 2.5 }, + // рисунок/логотип: только силуэт по яркости, контурный скос, без глубины и деталей; кончики шипов сохраняются малым сглаживанием + drawing: { silsrc: 'brightness', wc: 100, wd: 0, wt: 0, partw: 60, bevel: 1.2, roll: 40, range: 1.5, fsm: 0.1, slope: 3, osm: 0.15, openmm2: 150 }, } -for (const k of Object.keys(PRESETS)) $('pre-' + k).addEventListener('click', () => { for (const [id, v] of Object.entries(PRESETS[k])) $(id).value = String(v); schedule() }) +const applyPreset = (k: string) => { for (const [id, v] of Object.entries(PRESETS[k])) $(id).value = String(v); schedule() } +for (const k of Object.keys(PRESETS)) $('pre-' + k).addEventListener('click', () => applyPreset(k)) $('runsil').addEventListener('click', () => runModel('sil')) $('rundepth').addEventListener('click', () => runModel('depth')) -$('sample').addEventListener('change', () => { $('file').value = ''; loadImage() }) +$('sample').addEventListener('change', () => { $('file').value = ''; const v = $('sample').value; applyPreset(v === 'ring' || v === 'check' ? 'drawing' : v === 'cat_face' ? 'face' : 'whole'); loadImage() }) $('file').addEventListener('change', () => loadImage()) $('face').addEventListener('click', () => { for (const [id, v] of [['cx', 42], ['cy', 34], ['cw', 42], ['ch', 24]] as const) $(id).value = String(v); schedule() }) $('resetcrop').addEventListener('click', () => { for (const [id, v] of [['cx', 0], ['cy', 0], ['cw', 100], ['ch', 100]] as const) $(id).value = String(v); schedule() }) diff --git a/poc/ai/outline.ts b/poc/ai/outline.ts new file mode 100644 index 0000000..f892765 --- /dev/null +++ b/poc/ai/outline.ts @@ -0,0 +1,93 @@ +/** Векторный контур для PoC: трассировка, сглаживание вдоль кривой с сохранением острых углов, знаковое расстояние до полигонов. */ +import { traceAlpha } from '../../src/geometry/textOutline' + +export type Pt = [number, number] + +const len = (a: Pt, b: Pt) => Math.hypot(b[0] - a[0], b[1] - a[1]) + +/** Равномерная передискретизация замкнутой петли шагом h. */ +export function resample(loop: Pt[], h: number): Pt[] { + const n = loop.length, cum = [0] + for (let i = 0; i < n; i++) cum.push(cum[i] + len(loop[i], loop[(i + 1) % n])) + const L = cum[n], N = Math.max(8, Math.round(L / h)), out: Pt[] = [] + let k = 0 + for (let i = 0; i < N; i++) { + const d = (i / N) * L + while (cum[k + 1] < d) k++ + const t = (d - cum[k]) / Math.max(1e-9, cum[k + 1] - cum[k]), a = loop[k % n], b = loop[(k + 1) % n] + out.push([a[0] + (b[0] - a[0]) * t, a[1] + (b[1] - a[1]) * t]) + } + return out +} + +/** Углы: вершины, где направление кривой поворачивается больше cornerDeg на окне ±win (локальный максимум поворота). */ +export function findCorners(p: Pt[], win: number, cornerDeg: number): boolean[] { + const n = p.length, turn = new Float64Array(n) + for (let i = 0; i < n; i++) { + const a = p[(i - win + n) % n], b = p[i], c = p[(i + win) % n] + const v1x = b[0] - a[0], v1y = b[1] - a[1], v2x = c[0] - b[0], v2y = c[1] - b[1] + turn[i] = Math.abs(Math.atan2(v1x * v2y - v1y * v2x, v1x * v2x + v1y * v2y)) * 180 / Math.PI + } + const corner = new Array(n).fill(false) + for (let i = 0; i < n; i++) { + if (turn[i] < cornerDeg) continue + let max = true + for (let d = -win; d <= win; d++) if (turn[(i + d + n) % n] > turn[i] || (turn[(i + d + n) % n] === turn[i] && d < 0)) { max = false; break } + corner[i] = max + } + return corner +} + +const gaussKernel = (sigma: number) => { const r = Math.max(1, Math.ceil(3 * sigma)), k: number[] = []; let s = 0; for (let i = -r; i <= r; i++) { const v = Math.exp(-(i * i) / (2 * sigma * sigma)); k.push(v); s += v } return { r, k: k.map((v) => v / s) } } + +/** Сглаживание гауссом вдоль петли; между углами концы закреплены (нечётное отражение), без углов — по кольцу. */ +export function smoothLoop(p: Pt[], sigma: number, corner: boolean[]): Pt[] { + if (sigma < 0.3) return p + const n = p.length, { r, k } = gaussKernel(sigma), ids = corner.map((c, i) => (c ? i : -1)).filter((i) => i >= 0) + const out: Pt[] = p.map((q) => [q[0], q[1]]) + const smoothRun = (idx: number[], fixedEnds: boolean) => { // idx — индексы вершин участка по порядку + const m = idx.length + const at = (t: number, axis: 0 | 1): number => { + if (!fixedEnds) return p[idx[((t % m) + m) % m]][axis] + if (t < 0) return 2 * p[idx[0]][axis] - p[idx[Math.min(m - 1, -t)]][axis] + if (t > m - 1) return 2 * p[idx[m - 1]][axis] - p[idx[Math.max(0, 2 * (m - 1) - t)]][axis] + return p[idx[t]][axis] + } + for (let t = fixedEnds ? 1 : 0; t < (fixedEnds ? m - 1 : m); t++) { + let x = 0, y = 0 + for (let d = -r; d <= r; d++) { x += k[d + r] * at(t + d, 0); y += k[d + r] * at(t + d, 1) } + out[idx[t]] = [x, y] + } + } + if (!ids.length) { smoothRun(Array.from({ length: n }, (_, i) => i), false); return out } + for (let c = 0; c < ids.length; c++) { + const a = ids[c], b = ids[(c + 1) % ids.length], idx: number[] = [] + for (let i = a; ; i = (i + 1) % n) { idx.push(i); if (i === b && idx.length > 1) break; if (idx.length > n + 1) break } + if (idx.length > 2) smoothRun(idx, true) + } + return out +} + +/** Контуры мягкого поля (значения 0…255, уровень 128): внешние и внутренние петли, сглаженные с сохранением углов. Единицы — пиксели растра. */ +export function vectorOutline(field255: Uint8Array, w: number, h: number, sigmaPx: number, cornerDeg: number, stepPx = 0.7): Pt[][] { + const loops = traceAlpha(field255, w, h, 128) + return loops.map((l) => { + if (l.length < 4) return l as Pt[] + const rs = resample(l as Pt[], stepPx), win = Math.max(2, Math.round(1.5 / stepPx)) + return smoothLoop(rs, sigmaPx / stepPx, findCorners(rs, win, cornerDeg)) + }) +} + +/** Расстояние от точки до набора петель и чётность пересечений (внутри ли). */ +export function signedDistance(loops: Pt[][], x: number, y: number): number { + let best = Infinity, cross = 0 + for (const l of loops) { + const n = l.length + for (let i = 0; i < n; i++) { + const a = l[i], b = l[(i + 1) % n], dx = b[0] - a[0], dy = b[1] - a[1], t = Math.max(0, Math.min(1, ((x - a[0]) * dx + (y - a[1]) * dy) / (dx * dx + dy * dy || 1))) + const d = Math.hypot(x - (a[0] + dx * t), y - (a[1] + dy * t)); if (d < best) best = d + if ((a[1] > y) !== (b[1] > y) && x < a[0] + ((y - a[1]) / (b[1] - a[1])) * dx) cross++ + } + } + return cross & 1 ? best : -best +} From 0aaf5e46e3c10e7eec7ef27e95d26855ca41e114 Mon Sep 17 00:00:00 2001 From: skarakovski Date: Wed, 21 Oct 2026 22:37:53 +0700 Subject: [PATCH 25/38] poc: drawing preset engraves inner strokes, whole-animal preset with soft engrave threshold, auto-preset checkbox Co-Authored-By: Claude Sonnet 5.5 --- poc/ai/FINDINGS.md | 5 +++++ poc/ai/index.html | 2 +- poc/ai/keychain.ts | 6 +++--- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/poc/ai/FINDINGS.md b/poc/ai/FINDINGS.md index 50a195a..ed3c4a1 100644 --- a/poc/ai/FINDINGS.md +++ b/poc/ai/FINDINGS.md @@ -97,3 +97,8 @@ - Бугры давало прямое «тон → высота». Режим «гравировка»: продавливаются только самые тёмные черты (глаза, нос, рот, полосы), средние тона гладкие; порог тёмного ~0.15 для кропа лица. Режимы «рельеф» и «оба» сохранены. - Контур «вау» на рисунках: полигон с передискретизацией, поиск острых углов по повороту (50°), гаусс вдоль кривой с закреплёнными углами, резка ячеек по изолинии знакового расстояния. Острия шипов остаются острыми, кривые плавные. - Пресеты: лицо (кроп), животное целиком, рисунок/логотип; при выборе образца пресет применяется автоматически. + +### Пресеты по результатам прогонов +- **Рисунок/логотип** (`check`, `ring`): контур по яркости + гравировка внутренних штрихов (контур 55 / детали 45, режим «гравировка», порог 0.2, усиление 1.6, радиус 0.7 мм, диапазон 1.8 мм): внутренние линии и тени рисунка становятся рельефом, а не плоской плитой. +- **Животное целиком** (`cat`): гравировка с мягким порогом 0.18, усиление 0.9, радиус 0.9 мм, доли 25 / 45 / 30, диапазон 2.8 мм: полосы шерсти как узор, тело гладкое. Порог 0.45 давал пустой гладкий рельеф. +- **Лицо** (`cat_face`): порог 0.15, усиление 1.1, доли 30 / 40 / 30, диапазон 2.4 мм. diff --git a/poc/ai/index.html b/poc/ai/index.html index 844fd1f..6f997b0 100644 --- a/poc/ai/index.html +++ b/poc/ai/index.html @@ -79,7 +79,7 @@
    -
    пресет:
    +
    пресет:

    diff --git a/poc/ai/keychain.ts b/poc/ai/keychain.ts index 7078a09..2dbb334 100644 --- a/poc/ai/keychain.ts +++ b/poc/ai/keychain.ts @@ -372,15 +372,15 @@ async function runModel(kind: 'sil' | 'depth') { const PRESETS: Record> = { // значения в мм детали; лицо — кроп морды ~40 мм, целиком — всё животное, мелочь теряется, поэтому деталь шире и сглаживание меньше face: { silsrc: 'frame', dmode: 'engrave', dth: 0.15, wc: 30, wd: 40, wt: 30, partw: 40, dsm: 0.6, tsm: 0.3, fsm: 0.3, lcr: 1.4, lca: 1.1, slope: 1.5, bevel: 3, range: 2.4 }, - whole: { silsrc: 'model', dmode: 'engrave', dth: 0.45, wc: 30, wd: 55, wt: 15, partw: 70, dsm: 0.35, tsm: 0.25, fsm: 0.2, lcr: 1.2, lca: 0.45, slope: 2.5, bevel: 3, range: 2.5 }, + whole: { silsrc: 'model', dmode: 'engrave', dth: 0.18, wc: 25, wd: 45, wt: 30, partw: 70, dsm: 0.35, tsm: 0.15, fsm: 0.2, lcr: 0.9, lca: 0.9, slope: 2.5, bevel: 3, range: 2.8 }, // рисунок/логотип: только силуэт по яркости, контурный скос, без глубины и деталей; кончики шипов сохраняются малым сглаживанием - drawing: { silsrc: 'brightness', wc: 100, wd: 0, wt: 0, partw: 60, bevel: 1.2, roll: 40, range: 1.5, fsm: 0.1, slope: 3, osm: 0.15, openmm2: 150 }, + drawing: { silsrc: 'brightness', dmode: 'engrave', dth: 0.2, wc: 55, wd: 0, wt: 45, partw: 60, bevel: 1.2, roll: 40, range: 1.8, fsm: 0.1, slope: 3, osm: 0.15, openmm2: 150, lca: 1.6, lcr: 0.7, tpre: 0.05, tsm: 0.05 }, } const applyPreset = (k: string) => { for (const [id, v] of Object.entries(PRESETS[k])) $(id).value = String(v); schedule() } for (const k of Object.keys(PRESETS)) $('pre-' + k).addEventListener('click', () => applyPreset(k)) $('runsil').addEventListener('click', () => runModel('sil')) $('rundepth').addEventListener('click', () => runModel('depth')) -$('sample').addEventListener('change', () => { $('file').value = ''; const v = $('sample').value; applyPreset(v === 'ring' || v === 'check' ? 'drawing' : v === 'cat_face' ? 'face' : 'whole'); loadImage() }) +$('sample').addEventListener('change', () => { $('file').value = ''; const v = $('sample').value; if (chk('autopre')) applyPreset(v === 'ring' || v === 'check' ? 'drawing' : v === 'cat_face' ? 'face' : 'whole'); loadImage() }) $('file').addEventListener('change', () => loadImage()) $('face').addEventListener('click', () => { for (const [id, v] of [['cx', 42], ['cy', 34], ['cw', 42], ['ch', 24]] as const) $(id).value = String(v); schedule() }) $('resetcrop').addEventListener('click', () => { for (const [id, v] of [['cx', 0], ['cy', 0], ['cw', 100], ['ch', 100]] as const) $(id).value = String(v); schedule() }) From 21fe7b6ba61baf2fb4924d06405e7f67ad938887 Mon Sep 17 00:00:00 2001 From: skarakovski Date: Wed, 21 Oct 2026 23:11:11 +0700 Subject: [PATCH 26/38] poc: cutout PoC as a pipeline with 3D pockets (stages, engines, vector outline, bin fit, compare) Co-Authored-By: Claude Sonnet 5.5 --- poc/ai/FINDINGS.md | 3 + poc/ai/cutout.html | 107 ++++++--- poc/ai/cutout.ts | 529 ++++++++++++++++++++++----------------------- 3 files changed, 339 insertions(+), 300 deletions(-) diff --git a/poc/ai/FINDINGS.md b/poc/ai/FINDINGS.md index ed3c4a1..51f9ffc 100644 --- a/poc/ai/FINDINGS.md +++ b/poc/ai/FINDINGS.md @@ -102,3 +102,6 @@ - **Рисунок/логотип** (`check`, `ring`): контур по яркости + гравировка внутренних штрихов (контур 55 / детали 45, режим «гравировка», порог 0.2, усиление 1.6, радиус 0.7 мм, диапазон 1.8 мм): внутренние линии и тени рисунка становятся рельефом, а не плоской плитой. - **Животное целиком** (`cat`): гравировка с мягким порогом 0.18, усиление 0.9, радиус 0.9 мм, доли 25 / 45 / 30, диапазон 2.8 мм: полосы шерсти как узор, тело гладкое. Порог 0.45 давал пустой гладкий рельеф. - **Лицо** (`cat_face`): порог 0.15, усиление 1.1, доли 30 / 40 / 30, диапазон 2.4 мм. + +### PoC вырезов (cutout.html) переделан как пайплайн +Этапы: 1 фото и лист (авто-углы, формат, ориентация) → 2 силуэты (классика-baseline или модель маски с порогом и сглаживанием в мм; сравнение контуров двух движков и таблица размеров/IoU; галочки по силуэтам) → 3 контур (векторный, с сохранением углов, как в Keychain) → 4 вырез и корпус (глубина, зазор, поле, шаг юнита, высота; корпус целыми юнитами) → 3D с карманами по точному контуру (зазор сдвигает контур карманa по знаковому расстоянию), инструменты полупрозрачным слоем. Контуры карманов режутся по изолинии без ступеней. Старая страница сравнения (`runAll`) заменена этим пайплайном: сравнение осталось как галочка и таблица. diff --git a/poc/ai/cutout.html b/poc/ai/cutout.html index 794ed94..147d81e 100644 --- a/poc/ai/cutout.html +++ b/poc/ai/cutout.html @@ -1,39 +1,78 @@ -PoC: AI silhouettes +PoC: Вырезы — пайплайн фото → карманы -

    PoC (change 32): нейросетевые силуэты на выпрямленном листе · Keychain: пайплайн →

    -
    Снимок - - -
    -
    Модель
    -
    Бэкенд - - потоки - вход -
    -
    Край (пересчёт без модели)порог 0.5 сглаживание мм 0 поле мм
    - -
    cross-origin isolated: , WebGPU: , cores:
    -
    -
    Комбинатор формы (нужны запуски: маска-модель и/или глубина в режиме «весь снимок») - контур 30 - глубина 40 - детали 30
    - ширина детали мм радиус деталей мм - усиление уровни тонов (0=нет) - скос контура мм свет - -
    -
    -
    -
    +
    PoC Вырезы: фото инструментов на листе → карманы в корпусе · Keychain: пайплайн → + · isolated: , cores:
    +
    +
    +

    1Фото и лист

    + + + + + +
    +

    2Силуэты

    + + + +
    + + + +
    +
    +
    +

    3Контур (как в keychain)

    + + + +
    +

    4Вырез и корпус

    + + + + + + +
    +
    +
    + +
    diff --git a/poc/ai/cutout.ts b/poc/ai/cutout.ts index 12e0bfc..47341c1 100644 --- a/poc/ai/cutout.ts +++ b/poc/ai/cutout.ts @@ -1,311 +1,308 @@ -import * as ort from 'onnxruntime-web' +/** + * PoC пайплайна вырезов (change 25/25a/32): фото инструментов на листе → углы листа и выпрямление → силуэты (классика или модель маски) + * → векторный контур → карманы в корпусе → 3D. Контуры и сглаживание те же, что в PoC Keychain (outline.ts). + */ +import * as THREE from 'three' +import { OrbitControls } from 'three/addons/controls/OrbitControls.js' import { toColorImage, toGrayImage } from '../../src/features/keychain/decodeImage' import { detectPaper } from '../../src/geometry/photo/detect' import { guessPaper } from '../../src/geometry/photo/aspect' -import { paperSize } from '../../src/geometry/photo/paper' +import { paperSize, type Paper, type PaperOrientation } from '../../src/geometry/photo/paper' import { rectify, type Rectified } from '../../src/geometry/photo/homography' -import { segment } from '../../src/geometry/photo/segment' +import { segment, type Segmentation } from '../../src/geometry/photo/segment' import { edt } from '../../src/geometry/raster/edt' import { gaussianBlur, MIN_SIGMA } from '../../src/geometry/raster/blur' import { fillHoles, labelComponents, removeSpecks } from '../../src/geometry/raster/components' -import { backendsOf, CANDIDATES, type Candidate } from './models' +import { backendsOf, CANDIDATES } from './models' +import { infer } from './engine' +import { signedDistance, vectorOutline, type Pt } from './outline' const $ = (id: string) => document.getElementById(id) as T -const log = (s: string) => { $('log').textContent += s + '\n'; console.log(s) } -$('coi').textContent = String(self.crossOriginIsolated) -$('cores').textContent = String(navigator.hardwareConcurrency) -$('gpu').textContent = 'gpu' in navigator ? 'yes' : 'no' -for (const c of CANDIDATES.filter((x) => (x.kind ?? 'matte') === 'matte')) $('model').append(new Option(c.label, c.id)) +const num = (id: string) => +$(id).value +const chk = (id: string) => $(id).checked +const logEl = $('log') +const log = (s: string) => { logEl.textContent += s + '\n'; logEl.scrollTop = logEl.scrollHeight; console.log(s) } +$('coi').textContent = String(self.crossOriginIsolated); $('cores').textContent = String(navigator.hardwareConcurrency) +const ones = (n: number) => new Uint8Array(n).fill(1) + +for (const c of CANDIDATES.filter((x) => (x.kind ?? 'matte') === 'matte')) $('modelsel').append(new Option(c.label, c.id)) +$('modelsel').value = 'birefnet-lite-512-fp16' function syncEp() { - const ep = $('ep'), list = backendsOf($('model').value), keep = ep.value + const ep = $('ep'), list = backendsOf($('modelsel').value), keep = ep.value ep.innerHTML = ''; for (const b of list) ep.append(new Option(b, b)) ep.value = list.includes(keep as 'wasm' | 'webgpu') ? keep : list[0]; ep.disabled = list.length === 1 } -syncEp(); $('model').addEventListener('change', syncEp) +syncEp(); $('modelsel').addEventListener('change', syncEp) -const sessions = new Map() -ort.env.wasm.proxy = true // вывод в воркере ORT: страница не замирает на время инференса -ort.env.wasm.wasmPaths = 'https://cdn.jsdelivr.net/npm/onnxruntime-web@' + (ort.env.versions?.web ?? 'latest') + '/dist/' - -async function fetchUrl(url: string, bytes: number): Promise { - const cache = await caches.open('poc-ai-models') - let r = await cache.match(url) - const t0 = performance.now() - if (!r) { - const net = await fetch(url) - if (!net.ok) throw new Error(`HTTP ${net.status} for ${url}`) - await cache.put(url, net.clone()) - r = net - log(`скачано ${(bytes / 1e6).toFixed(0)} МБ за ${((performance.now() - t0) / 1000).toFixed(1)} с`) - } else log('модель из Cache API') - const buf = await r.arrayBuffer() - if (buf.byteLength !== bytes) log(`ВНИМАНИЕ: размер ${buf.byteLength} ≠ ${bytes}`) - return buf -} -const fetchModel = (c: Candidate) => fetchUrl(c.url, c.bytes) +// ---------- состояние ---------- +interface Sil { id: number; widthMm: number; heightMm: number; areaMm2: number; bbox: [number, number, number, number] } +interface Seg { labels: Int32Array; sils: Sil[] } +let photo: { w: number; h: number; rgb: Uint8Array; gray: ReturnType; color: ReturnType } | null = null +let detection: { corners: Pt[]; paper: Paper; info: string } | null = null +let rect: Rectified | null = null +let classic: Seg | null = null +let matte: Float32Array | null = null // мягкая альфа модели на выпрямленном растре +const excluded = new Set() -async function loadImage(maxSide = 0): Promise { +async function loadPhoto() { const f = $('file').files?.[0] - const src = f ? URL.createObjectURL(f) : `./samples/${$('sample').value}.jpeg` - const img = new Image(); img.src = src; await img.decode() - const k = maxSide ? Math.min(1, maxSide / Math.max(img.naturalWidth, img.naturalHeight)) : 1 - const cv = document.createElement('canvas'); cv.width = Math.round(img.naturalWidth * k); cv.height = Math.round(img.naturalHeight * k) - const g = cv.getContext('2d')!; g.drawImage(img, 0, 0, cv.width, cv.height) - return g.getImageData(0, 0, cv.width, cv.height) + const el = new Image(); el.src = f ? URL.createObjectURL(f) : `./samples/${$('sample').value}.jpeg`; await el.decode() + const cv = document.createElement('canvas'); cv.width = el.naturalWidth; cv.height = el.naturalHeight + const g = cv.getContext('2d')!; g.drawImage(el, 0, 0) + const d = g.getImageData(0, 0, cv.width, cv.height) + const gray = toGrayImage(d.data, d.width, d.height), color = toColorImage(d.data, d.width, d.height) + photo = { w: d.width, h: d.height, rgb: color.rgb, gray, color } + const found = detectPaper(color) + if (!found) { detection = null; log('лист не найден'); $('st-paper').textContent = 'лист не найден'; return } + const guess = guessPaper(found.corners, d.width, d.height) + detection = { corners: found.corners, paper: paperSize('a4', 'portrait'), info: '' } + detection.info = `найден лист, уверенность ${found.confidence.toFixed(2)}, формат ${guess?.preset ?? '?'} / ${guess?.orientation ?? '?'}` + ;(window as unknown as { __guess: unknown }).__guess = guess + log(`фото ${d.width}×${d.height}: ${detection.info}`) + matte = null; excluded.clear(); rebuildRect(); schedule() } -/** Нормализованный вход NCHW float32: лист из rgb растра, масштабированный до s × s. */ -function toInput(rect: Rectified, c: Candidate, s: number, pad = false): Float32Array { - const w = rect.image.width, h = rect.image.height - const src = new ImageData(w, h) - for (let i = 0; i < w * h; i++) { src.data[i * 4] = rect.rgb![i * 3]; src.data[i * 4 + 1] = rect.rgb![i * 3 + 1]; src.data[i * 4 + 2] = rect.rgb![i * 3 + 2]; src.data[i * 4 + 3] = 255 } - const a = document.createElement('canvas'); a.width = w; a.height = h; a.getContext('2d')!.putImageData(src, 0, 0) - const b = document.createElement('canvas'); b.width = s; b.height = s - const bg = b.getContext('2d')!; bg.imageSmoothingQuality = 'high' - if (pad) { // вписать с сохранением пропорций, поля — цвет, нейтральный для модели (среднее по ImageNet) - bg.fillStyle = `rgb(${c.mean.map((m) => Math.round(m * 255)).join(',')})`; bg.fillRect(0, 0, s, s) - const r = Math.min(s / w, s / h); bg.drawImage(a, (s - w * r) / 2, (s - h * r) / 2, w * r, h * r) - } else bg.drawImage(a, 0, 0, s, s) - const d = bg.getImageData(0, 0, s, s).data - const out = new Float32Array(3 * s * s) - for (let i = 0; i < s * s; i++) for (let k = 0; k < 3; k++) out[k * s * s + i] = (d[i * 4 + k] / 255 - c.mean[k]) / c.std[k] - return out +function currentPaper(): Paper { + const guess = (window as unknown as { __guess: ReturnType }).__guess + const pp = $('ppaper').value, po = $('porient').value + const preset = pp === 'auto' ? guess?.preset ?? 'a4' : (pp as 'a4' | 'letter'), orient = (po === 'auto' ? guess?.orientation ?? 'portrait' : po) as PaperOrientation + return paperSize(preset, orient) } -/** Выход модели s × s → альфа w × h (билинейно), 0…1. Модели с несколькими выходами: берётся первый. */ -function toAlpha(o: Float32Array, s: number, w: number, h: number, sigmoid: boolean, pad = false): Float32Array { - // значения вне 0…1 — логиты: sigmoid (признак из реестра подстраховывается самим диапазоном выхода) - let lo = Infinity, hi = -Infinity; for (const x of o) { if (x < lo) lo = x; if (x > hi) hi = x } - const v = sigmoid || lo < -0.001 || hi > 1.001 ? o.map((x) => 1 / (1 + Math.exp(-x))) : o - const out = new Float32Array(w * h) - const r = pad ? Math.min(s / w, s / h) : 0 - const kx = pad ? r : s / w, ky = pad ? r : s / h, ox = pad ? (s - w * r) / 2 : 0, oy = pad ? (s - h * r) / 2 : 0 - for (let j = 0; j < h; j++) { - const fy = Math.min(s - 1.001, Math.max(0, (j + 0.5) * ky + oy - 0.5)), y0 = Math.floor(fy), ty = fy - y0 - for (let i = 0; i < w; i++) { - const fx = Math.min(s - 1.001, Math.max(0, (i + 0.5) * kx + ox - 0.5)), x0 = Math.floor(fx), tx = fx - x0 - const a = v[y0 * s + x0], b = v[y0 * s + x0 + 1], cc = v[(y0 + 1) * s + x0], d = v[(y0 + 1) * s + x0 + 1] - out[j * w + i] = (a * (1 - tx) + b * tx) * (1 - ty) + (cc * (1 - tx) + d * tx) * ty - } - } - return out +function rebuildRect() { + if (!photo || !detection) return + const paper = currentPaper() + rect = rectify(photo.gray, detection.corners, paper, photo.color.rgb) + $('st-paper').textContent = `${detection.info}; лист ${paper.width}×${paper.height} мм` + if (!rect) { log('углы листа не дают выпрямления'); return } + const t = performance.now() + classic = segFromClassic(rect, segment(rect, { margin: num('margin'), ...(($('sens').value) ? { sensitivity: num('sens') } : {}) })) + log(`выпрямлено ${rect.image.width}×${rect.image.height} (${rect.scale} px/мм); классика: ${classic.sils.length} силуэтов за ${(performance.now() - t).toFixed(0)} мс`) + matte = null } +const segFromClassic = (r: Rectified, s: Segmentation): Seg => ({ labels: s.labels, sils: s.silhouettes.map((x) => ({ id: x.id, widthMm: x.widthMm, heightMm: x.heightMm, areaMm2: x.areaMm2, bbox: x.bbox })) }) -interface Sil { id: number; wMm: number; hMm: number; areaMm2: number; bbox: [number, number, number, number] } -function components(mask: Uint8Array, w: number, h: number, scale: number, marginMm: number): { labels: Int32Array; sils: Sil[] } { - const m = new Uint8Array(mask), mp = Math.round(marginMm * scale) - for (let j = 0; j < h; j++) for (let i = 0; i < w; i++) if (i < mp || j < mp || i >= w - mp || j >= h - mp) m[j * w + i] = 0 - const clean = removeSpecks(fillHoles(m, w, h).mask, w, h, Math.round(2 * scale * scale)) - const cc = labelComponents(clean, w, h) - const sils: Sil[] = [] - const bb: [number, number, number, number][] = Array.from({ length: cc.count + 1 }, () => [w, h, -1, -1]) +/** Силуэты по мягкой маске модели: сглаживание, порог, поле у края, заливка пустот, мелочь долой, компоненты по порядку обхода сверху вниз. */ +function aiSegmentation(r: Rectified, alpha: Float32Array): Seg { + const w = r.image.width, h = r.image.height, s = r.scale, n = w * h + const sg = num('smooth') * s, a = sg > 0 ? gaussianBlur(alpha, w, h, Math.max(MIN_SIGMA, sg), ones(n)) : alpha, thr = num('thr') + let mask: Uint8Array = new Uint8Array(n); for (let i = 0; i < n; i++) mask[i] = a[i] >= thr ? 1 : 0 + const mp = Math.round(num('margin') * s) + for (let j = 0; j < h; j++) for (let i = 0; i < w; i++) if (i < mp || j < mp || i >= w - mp || j >= h - mp) mask[j * w + i] = 0 + mask = removeSpecks(fillHoles(mask, w, h).mask, w, h, Math.round(2 * s * s)) + const cc = labelComponents(mask, w, h), bb: [number, number, number, number][] = Array.from({ length: cc.count + 1 }, () => [w, h, -1, -1]) for (let j = 0; j < h; j++) for (let i = 0; i < w; i++) { const k = cc.labels[j * w + i]; if (k) { const b = bb[k]; if (i < b[0]) b[0] = i; if (j < b[1]) b[1] = j; if (i > b[2]) b[2] = i; if (j > b[3]) b[3] = j } } - const keep = new Int32Array(cc.count + 1); let n = 0 + const keep = new Int32Array(cc.count + 1), sils: Sil[] = []; let id = 0 for (let k = 1; k <= cc.count; k++) { - const b = bb[k], wm = (b[2] - b[0] + 1) / scale, hm = (b[3] - b[1] + 1) / scale, area = cc.sizes[k] / (scale * scale) - if (area >= 60 && wm >= 3 && hm >= 3) { keep[k] = ++n; sils.push({ id: n, wMm: wm, hMm: hm, areaMm2: area, bbox: b }) } + const b = bb[k], wm = (b[2] - b[0] + 1) / s, hm = (b[3] - b[1] + 1) / s, area = cc.sizes[k] / (s * s) + if (area >= num('minarea') && wm >= 3 && hm >= 3) { keep[k] = ++id; sils.push({ id, widthMm: wm, heightMm: hm, areaMm2: area, bbox: b }) } } - const labels = new Int32Array(w * h); for (let i = 0; i < labels.length; i++) labels[i] = keep[cc.labels[i]] + const labels = new Int32Array(n); for (let i = 0; i < n; i++) labels[i] = keep[cc.labels[i]] return { labels, sils } } -function draw(rect: Rectified, classic: Int32Array, ai: Int32Array) { - const w = rect.image.width, h = rect.image.height - for (const [id, lab, col] of [['c1', classic, [255, 40, 40]], ['c2', ai, [0, 200, 80]]] as const) { - const cv = $(id); cv.width = w; cv.height = h - const g = cv.getContext('2d')!, im = g.createImageData(w, h) - for (let i = 0; i < w * h; i++) { - const edge = lab[i] && (!lab[i - 1] || !lab[i + 1] || !lab[i - w] || !lab[i + w]) - for (let k = 0; k < 3; k++) im.data[i * 4 + k] = edge ? col[k] : lab[i] ? rect.rgb![i * 3 + k] * 0.5 + col[k] * 0.5 : rect.rgb![i * 3 + k] - im.data[i * 4 + 3] = 255 - } - g.putImageData(im, 0, 0) - } +// ---------- превью ---------- +function show(id: string, w: number, h: number, px: (i: number) => [number, number, number]) { + const cv = $(id); cv.width = w; cv.height = h + const g = cv.getContext('2d')!, im = g.createImageData(w, h) + for (let i = 0; i < w * h; i++) { const [r, gg, b] = px(i); im.data[i * 4] = r; im.data[i * 4 + 1] = gg; im.data[i * 4 + 2] = b; im.data[i * 4 + 3] = 255 } + g.putImageData(im, 0, 0) +} +function previewPhoto() { + if (!photo) return + const cv = $('pv-photo'), s = Math.min(1, 480 / photo.w); cv.width = Math.round(photo.w * s); cv.height = Math.round(photo.h * s) + const t = document.createElement('canvas'); t.width = photo.w; t.height = photo.h + const im = t.getContext('2d')!.createImageData(photo.w, photo.h) + for (let i = 0; i < photo.w * photo.h; i++) { im.data[i * 4] = photo.rgb[i * 3]; im.data[i * 4 + 1] = photo.rgb[i * 3 + 1]; im.data[i * 4 + 2] = photo.rgb[i * 3 + 2]; im.data[i * 4 + 3] = 255 } + t.getContext('2d')!.putImageData(im, 0, 0) + const g = cv.getContext('2d')!; g.drawImage(t, 0, 0, cv.width, cv.height) + if (detection) { g.strokeStyle = '#ff3'; g.lineWidth = 2; g.beginPath(); detection.corners.forEach(([x, y], i) => (i ? g.lineTo(x * s, y * s) : g.moveTo(x * s, y * s))); g.closePath(); g.stroke() } } -async function run() { - $('log').textContent = ''; $('status').textContent = 'работаю…' - const c = CANDIDATES.find((x) => x.id === $('model').value)! - const ep = $('ep').value, threads = +$('threads').value - const s = +($('size').value || c.size), thr = +$('thr').value, margin = +$('margin').value - log(`модель ${c.id}, лицензия ${c.license}, вход ${s}, бэкенд ${ep}, потоков ${threads}, isolated=${self.crossOriginIsolated}`) - - const whole = $('whole').checked - const im = await loadImage(whole ? 2048 : 0) - const gray = toGrayImage(im.data, im.width, im.height), color = toColorImage(im.data, im.width, im.height) - let rect: Rectified, seg: ReturnType - if (whole) { - // режим Keychain: вся картинка как есть, без листа; масштаб 1 пиксель = 1 «мм» (размеры в таблице — в пикселях) - rect = { image: gray, rgb: color.rgb, scale: 1, paper: { width: im.width, height: im.height } } - seg = { width: im.width, height: im.height, scale: 1, labels: new Int32Array(im.width * im.height), silhouettes: [], sensitivity: 0, defaultSensitivity: 0 } - log(`режим Keychain: картинка ${im.width}×${im.height}, вписывание с полями до ${s}×${s}`) - } else { - const found = detectPaper(color) - if (!found) { log('лист не найден'); return } - const guess = guessPaper(found.corners, im.width, im.height) - const paper = paperSize(guess?.preset ?? 'a4', guess?.orientation ?? 'portrait') - rect = rectify(gray, found.corners, paper, color.rgb)! - log(`снимок ${im.width}×${im.height}, лист ${paper.width}×${paper.height} мм (${guess?.preset}/${guess?.orientation}), растр ${rect.image.width}×${rect.image.height}, ${rect.scale.toFixed(2)} px/мм`) - const t0 = performance.now(); seg = segment(rect) - log(`классика: ${seg.silhouettes.length} силуэтов за ${(performance.now() - t0).toFixed(0)} мс`) - } - const W = rect.image.width, H = rect.image.height - let t = performance.now() - - const key = `${c.id}|${ep}|${threads}` - let session = sessions.get(key) - if (!session) { - const buf = await fetchModel(c) - ort.env.wasm.numThreads = threads - t = performance.now() - const externalData = c.data ? [{ path: c.data.path, data: new Uint8Array(await fetchUrl(c.data.url, c.data.bytes)) }] : undefined - session = await ort.InferenceSession.create(buf, { executionProviders: [ep], graphOptimizationLevel: 'all', ...(externalData ? { externalData } : {}) }) - log(`сессия создана за ${(performance.now() - t).toFixed(0)} мс; входы ${session.inputNames}, выходы ${session.outputNames}`) - sessions.set(key, session) - } - const inName = session.inputNames[0] - const data = toInput(rect, c, s, whole) - const runs: number[] = []; let out!: ort.InferenceSession.OnnxValueMapType - for (let i = 0; i < 2; i++) { t = performance.now(); out = await session.run({ [inName]: new ort.Tensor('float32', data.slice(), [1, 3, s, s]) }); runs.push(performance.now() - t) } - log(`инференс: 1-й ${runs[0].toFixed(0)} мс (прогрев), 2-й ${runs[1].toFixed(0)} мс`) - const o = out[session.outputNames[0]] - let raw = o.data as Float32Array - if (c.kind === 'depth') { // относительная глубина → 0…1 по 2–98 перцентилям (дальше — ближе = выше, как в рельефе) - const sorted = Float32Array.from(raw).sort(); const lo = sorted[Math.floor(sorted.length * 0.02)], hi = sorted[Math.floor(sorted.length * 0.98)] - raw = raw.map((x) => Math.min(1, Math.max(0, (x - lo) / (hi - lo || 1)))) - } - const alpha = toAlpha(raw, s, W, H, c.sigmoid, whole) - - state = { rect, seg, alpha, runs, c, ep, threads, s, whole, depth: c.kind === 'depth' } - evaluate() - $('status').textContent = 'готово' +// ---------- контуры и поле ---------- +interface Outline { id: number; loops: Pt[][] } +function outlineOf(seg: Seg, sil: Sil, r: Rectified): Outline { + const w = r.image.width, h = r.image.height, PAD = 4 + const x0 = Math.max(0, sil.bbox[0] - PAD), y0 = Math.max(0, sil.bbox[1] - PAD), x1 = Math.min(w - 1, sil.bbox[2] + PAD), y1 = Math.min(h - 1, sil.bbox[3] + PAD), bw = x1 - x0 + 1, bh = y1 - y0 + 1 + const v = new Float32Array(bw * bh); for (let j = 0; j < bh; j++) for (let i = 0; i < bw; i++) if (seg.labels[(y0 + j) * w + x0 + i] === sil.id) v[j * bw + i] = 255 + const f = gaussianBlur(v, bw, bh, 0.8, ones(bw * bh)), sigma = chk('vec') ? Math.max(0, num('osm') * r.scale) : 0 + const loops = vectorOutline(Uint8Array.from(f, (x) => Math.round(x)), bw, bh, sigma, num('cang')).map((l) => l.map(([x, y]) => [x + x0, y + y0] as Pt)) + return { id: sil.id, loops } } -interface State { rect: Rectified; seg: ReturnType; alpha: Float32Array; runs: number[]; c: Candidate; ep: string; threads: number; s: number; whole: boolean; depth: boolean } -let state: State | null = null +interface Field { key: string; nx: number; ny: number; cell: number; sd: Float32Array; cx: number; cy: number; binW: number; binH: number; scale: number } +let fieldCache: Field | null = null -/** Всё после инференса: сглаживание альфы, порог, компоненты, сравнение. Модель не запускается, поэтому ползунки работают мгновенно. */ -function evaluate() { - if (!state) return - const { rect, seg, runs, c, ep, threads, s, whole } = state - const W = rect.image.width, H = rect.image.height - const thr = +$('thr').value, margin = +$('margin').value, smoothMm = +$('smooth').value - let alpha = state.alpha - if (smoothMm > 0) alpha = gaussianBlur(alpha, W, H, Math.max(MIN_SIGMA, smoothMm * rect.scale), new Uint8Array(W * H).fill(1)) - drawAlpha(alpha, W, H) - capture(state.depth, alpha, rect, W, H, thr) - if (state.depth) { combine(); $('edge').textContent = 'карта глубины (светлее = ближе): справа — глубина, силуэт не считается'; $('tbl').innerHTML = ''; $('status').textContent = 'готово'; return } - const mask = new Uint8Array(W * H); for (let i = 0; i < mask.length; i++) mask[i] = alpha[i] >= thr ? 1 : 0 - const ai = components(mask, W, H, rect.scale, margin) - draw(rect, seg.labels, ai.labels) +function activeSeg(): Seg | null { + if ($('eng').value === 'ai' && rect && matte) return aiSegmentation(rect, matte) + return classic +} +const otherSeg = (): Seg | null => ($('eng').value === 'ai' ? classic : rect && matte ? aiSegmentation(rect, matte) : null) - // сравнение: общий IoU и по силуэтам (сопоставление по наибольшему пересечению) - let inter = 0, uni = 0; for (let i = 0; i < W * H; i++) { const a = seg.labels[i] > 0, b = ai.labels[i] > 0; if (a && b) inter++; if (a || b) uni++ } - const rows: string[] = ['классика ммAI ммΔ ширинаΔ высотаIoUAI больше (тень?)AI меньше (потеря?)'] - for (const cs of seg.silhouettes) { - const ov = new Map(); let cArea = 0 - for (let i = 0; i < W * H; i++) if (seg.labels[i] === cs.id) { cArea++; const k = ai.labels[i]; if (k) ov.set(k, (ov.get(k) ?? 0) + 1) } +let timer = 0 +function schedule() { clearTimeout(timer); timer = window.setTimeout(update, 120) } +function update() { + for (const id of ['thr', 'smooth']) { const e = $(id); (e.nextElementSibling as HTMLElement).textContent = e.value } + $('aibox').hidden = $('eng').value !== 'ai'; $('classicbox').hidden = !$('aibox').hidden + previewPhoto() + const seg = activeSeg(); if (!rect || !seg) return + $('st-sil').textContent = `${seg.sils.length} силуэтов` + ($('eng').value === 'ai' && !matte ? ' (маска не посчитана: запустите модель)' : '') + // список силуэтов с галочками + const list = $('sils'); list.innerHTML = '' + for (const s of seg.sils) { + const l = document.createElement('label'), c = document.createElement('input'); c.type = 'checkbox'; c.checked = !excluded.has(s.id) + c.addEventListener('input', () => { c.checked ? excluded.delete(s.id) : excluded.add(s.id); schedule() }) + l.append(c, ` №${s.id}: ${s.widthMm.toFixed(1)} × ${s.heightMm.toFixed(1)} мм, ${s.areaMm2.toFixed(0)} мм²`); list.append(l, document.createElement('br')) + } + // превью силуэтов (+ контуры другого движка) + const W = rect.image.width, H = rect.image.height, other = chk('cmp') ? otherSeg() : null + show('pv-sil', W, H, (i) => { + const l = seg.labels[i], ex = excluded.has(l), o = other ? other.labels[i] > 0 : false + const gv = rect!.image.gray[i] + if (!l) return o ? [gv * 0.7 + 60, gv * 0.5, gv * 0.5] : [gv, gv, gv] + return ex ? [gv * 0.5, gv * 0.5, gv * 0.5] : [gv * 0.45, gv * 0.45 + 120, gv * 0.45 + 40] + }) + compareTable(seg, other) + build() +} +function compareTable(seg: Seg, other: Seg | null) { + const el = $('cmptab'); if (!other || !rect) { el.innerHTML = ''; return } + const n = seg.labels.length, rows = ['активный ммдругой ммΔ ширΔ высIoU'] + for (const a of seg.sils) { + const ov = new Map(); for (let i = 0; i < n; i++) if (seg.labels[i] === a.id) { const k = other.labels[i]; if (k) ov.set(k, (ov.get(k) ?? 0) + 1) } let best = 0, bo = 0; for (const [k, v] of ov) if (v > bo) { bo = v; best = k } - const a = ai.sils.find((x) => x.id === best) - if (!a) { rows.push(`${cs.widthMm.toFixed(1)}×${cs.heightMm.toFixed(1)}НЕ НАЙДЕН`); continue } - let i2 = 0, u2 = 0, big = 0, small = 0 - for (let i = 0; i < W * H; i++) { const x = seg.labels[i] === cs.id, y = ai.labels[i] === best; if (x && y) i2++; if (x || y) u2++; if (y && !x) big++; if (x && !y) small++ } - const px = scaleMm(rect.scale) - rows.push(`${cs.widthMm.toFixed(1)}×${cs.heightMm.toFixed(1)}${a.wMm.toFixed(1)}×${a.hMm.toFixed(1)}${(a.wMm - cs.widthMm).toFixed(2)}${(a.hMm - cs.heightMm).toFixed(2)}${(i2 / u2).toFixed(3)}${(big * px).toFixed(0)} мм²${(small * px).toFixed(0)} мм²`) + const b = other.sils.find((x) => x.id === best); if (!b) { rows.push(`${a.widthMm.toFixed(1)}×${a.heightMm.toFixed(1)}не найден`); continue } + let i2 = 0, u2 = 0; for (let i = 0; i < n; i++) { const x = seg.labels[i] === a.id, y = other.labels[i] === best; if (x && y) i2++; if (x || y) u2++ } + rows.push(`${a.widthMm.toFixed(1)}×${a.heightMm.toFixed(1)}${b.widthMm.toFixed(1)}×${b.heightMm.toFixed(1)}${(b.widthMm - a.widthMm).toFixed(2)}${(b.heightMm - a.heightMm).toFixed(2)}${(i2 / u2).toFixed(3)}`) } - if (whole) for (const a of ai.sils) rows.push(`объект ${a.id}${a.wMm.toFixed(0)}×${a.hMm.toFixed(0)} px, площадь ${a.areaMm2.toFixed(0)} px²`) - const extra = whole ? 0 : ai.sils.length - seg.silhouettes.length - $('tbl').innerHTML = rows.join('') + `всего: классика ${seg.silhouettes.length}, AI ${ai.sils.length}${extra ? ' (лишние/слитые!)' : ''}; общий IoU ${(inter / uni).toFixed(3)}; инференс ${runs[1].toFixed(0)} мс` - ;(window as unknown as { __poc: unknown }).__poc = { model: c.id, ep, threads, size: s, classic: seg.silhouettes.map((x) => [x.widthMm, x.heightMm, x.areaMm2]), ai: ai.sils.map((x) => [x.wMm, x.hMm, x.areaMm2]), iou: inter / uni, ms: runs } - // длина границы: меньше — гладче контур (дрожащий край = много лишних угловых пикселей) - let edgePx = 0; for (let i = 1; i < W * H - W; i++) if (ai.labels[i] && (!ai.labels[i - 1] || !ai.labels[i + 1] || !ai.labels[i - W] || !ai.labels[i + W])) edgePx++ - $('edge').textContent = `граница AI: ${(edgePx / rect.scale).toFixed(0)} мм, мягкая зона альфы (0.1…0.9): ${(softPx(alpha) / rect.scale / rect.scale).toFixed(0)} мм²` + el.innerHTML = '' + rows.join('') + '
    ' } -function softPx(alpha: Float32Array): number { let n = 0; for (const a of alpha) if (a > 0.1 && a < 0.9) n++; return n } - -function drawAlpha(alpha: Float32Array, w: number, h: number) { - const cv = $('c3'); cv.width = w; cv.height = h - const g = cv.getContext('2d')!, im = g.createImageData(w, h) - for (let i = 0; i < w * h; i++) { const v = Math.round(alpha[i] * 255); im.data[i * 4] = im.data[i * 4 + 1] = im.data[i * 4 + 2] = v; im.data[i * 4 + 3] = 255 } - g.putImageData(im, 0, 0) -} -for (const id of ['thr', 'smooth', 'margin']) $(id).addEventListener('input', () => evaluate()) -/** Плоскости последнего запуска для комбинатора: маска (контур), глубина, яркость. Размеры совпадают, пока снимок один. */ -const planes: { w: number; h: number; mask?: Uint8Array; depth?: Float32Array; gray?: Float32Array; name?: string } = { w: 0, h: 0 } -function capture(isDepth: boolean, alpha: Float32Array, rect: Rectified, W: number, H: number, thr: number) { - if (planes.w !== W || planes.h !== H) { planes.mask = undefined; planes.depth = undefined } - planes.w = W; planes.h = H - planes.gray = Float32Array.from(rect.image.gray, (v) => v / 255) - if (isDepth) planes.depth = alpha - else planes.mask = alpha.map((v) => (v >= thr ? 1 : 0)) as unknown as Uint8Array - $('planes').textContent = ` плоскости: маска ${planes.mask ? 'есть' : 'нет'}, глубина ${planes.depth ? 'есть' : 'нет'}` - combine() +// ---------- 3D ---------- +const view = $('view') +const renderer = new THREE.WebGLRenderer({ antialias: true }); view.append(renderer.domElement) +const scene = new THREE.Scene(); scene.background = new THREE.Color(0xe8e8e4) +const camera = new THREE.PerspectiveCamera(35, 1, 1, 3000) +const controls = new OrbitControls(camera, renderer.domElement) +scene.add(new THREE.HemisphereLight(0xffffff, 0x666666, 0.55)) +const sun = new THREE.DirectionalLight(0xffffff, 2.2); scene.add(sun) +const fillLight = new THREE.DirectionalLight(0xffffff, 0.5); fillLight.position.set(60, -80, 40); scene.add(fillLight) +const mat = new THREE.MeshStandardMaterial({ color: 0xd8d2c4, roughness: 0.75, metalness: 0, side: THREE.DoubleSide }) +const toolMat = new THREE.MeshStandardMaterial({ color: 0x1fb6c9, transparent: true, opacity: 0.4, roughness: 0.5 }) +const group = new THREE.Group(); scene.add(group) +function setLight() { const a = (num('lightang') * Math.PI) / 180; sun.position.set(Math.cos(a) * 120, Math.sin(a) * 120, 100) } +function resize() { const w = view.clientWidth, h = view.clientHeight; renderer.setSize(w, h, false); camera.aspect = w / h; camera.updateProjectionMatrix() } +new ResizeObserver(resize).observe(view); resize(); setLight() +renderer.setAnimationLoop(() => { controls.update(); renderer.render(scene, camera) }) +let fitted = '' +function frame(size: number, preset: 'top' | 'iso') { + const d = size * 1.7 + if (preset === 'top') { camera.up.set(0, 1, 0); camera.position.set(0, -0.001, d) } else { camera.up.set(0, 0, 1); camera.position.set(-d * 0.45, -d * 0.75, d * 0.7) } + camera.lookAt(0, 0, 0); controls.target.set(0, 0, 0); controls.update() } -const ones = (n: number) => new Uint8Array(n).fill(1) -function localContrast(g: Float32Array, w: number, h: number, sigma: number, amount: number): Float32Array { - if (amount <= 0) return g.map(() => 0.5) - const mean = gaussianBlur(g, w, h, Math.max(MIN_SIGMA, sigma), ones(w * h)) - const dev = g.map((v, i) => v - mean[i]) - const sd = gaussianBlur(dev.map((v) => v * v), w, h, Math.max(MIN_SIGMA, sigma), ones(w * h)).map((v) => Math.sqrt(v) + 0.02) - return dev.map((v, i) => 0.5 + 0.5 * Math.tanh(amount * v / sd[i])) -} -const smooth01 = (t: number) => { const x = Math.min(1, Math.max(0, t)); return x * x * (3 - 2 * x) } -/** Форма = нормированная смесь трёх источников: контур (скос от края маски), глубина (внутри маски), детали (локальный контраст яркости). */ -function combine() { - const { w, h, mask, depth, gray } = planes - if (!gray || !w) return - const num = (id: string) => +$(id).value - const wc = num('wc'), wd = num('wd'), wt = num('wt') - const pxMm = w / Math.max(1, num('partw')) // пикселей на мм детали (кадр = вся деталь) - const inside = mask ?? ones(w * h) - // контур: скос шириной bevel мм от края маски - const dist = mask ? edt(mask, w, h) : null - const bevelPx = Math.max(1, num('bevel') * pxMm) - const contour = Float32Array.from({ length: w * h }, (_, i) => (dist ? smooth01(dist[i] / bevelPx) : 1)) - // глубина: нормировка по перцентилям внутри маски - let dnorm = new Float32Array(w * h).fill(0.5) - if (depth) { - const vals: number[] = []; for (let i = 0; i < w * h; i++) if (inside[i]) vals.push(depth[i]) - vals.sort((a, b) => a - b); const lo = vals[Math.floor(vals.length * 0.02)] ?? 0, hi = vals[Math.floor(vals.length * 0.98)] ?? 1 - dnorm = depth.map((v) => Math.min(1, Math.max(0, (v - lo) / (hi - lo || 1)))) +$('vtop').addEventListener('click', () => frame(Math.max(binDims.w, binDims.h), 'top')) +$('viso').addEventListener('click', () => frame(Math.max(binDims.w, binDims.h), 'iso')) +const binDims = { w: 100, h: 100 } + +function build() { + const seg = activeSeg(); if (!rect || !seg) return + const sels = seg.sils.filter((s) => !excluded.has(s.id)) + group.clear() + if (!sels.length) { $('stat3d').textContent = 'нет выбранных силуэтов'; return } + const sc = rect.scale, cell0 = Math.max(0.15, num('cell')) + const outs = sels.map((s) => outlineOf(seg, s, rect!)) + $('st-out').textContent = `${outs.reduce((a, o) => a + o.loops.length, 0)} петель, ${outs.reduce((a, o) => a + o.loops.reduce((b, l) => b + l.length, 0), 0)} вершин` + // габарит всех инструментов (мм листа) → корпус целыми юнитами + let x0 = Infinity, y0 = Infinity, x1 = -Infinity, y1 = -Infinity + for (const o of outs) for (const l of o.loops) for (const [x, y] of l) { x0 = Math.min(x0, x); x1 = Math.max(x1, x); y0 = Math.min(y0, y); y1 = Math.max(y1, y) } + const cx = (x0 + x1) / 2 / sc, cy = (y0 + y1) / 2 / sc, ex = (x1 - x0) / sc, ey = (y1 - y0) / sc, pitch = num('pitch'), m = num('bmargin') + num('clear') + const ux = Math.max(1, Math.ceil((ex + 2 * m) / pitch - 1e-9)), uy = Math.max(1, Math.ceil((ey + 2 * m) / pitch - 1e-9)), binW = ux * pitch, binH = uy * pitch + binDims.w = binW; binDims.h = binH + $('st-bin').textContent = `инструменты ${ex.toFixed(0)}×${ey.toFixed(0)} мм → корпус ${ux}×${uy} юнитов (${binW}×${binH} мм)` + const nx = Math.round(binW / cell0) + 1, ny = Math.round(binH / cell0) + 1, cx3 = binW / (nx - 1), cy3 = binH / (ny - 1) + const key = [sels.map((s) => s.id).join(','), $('eng').value, chk('vec'), num('osm'), num('cang'), nx, ny, binW, binH, num('margin'), num('thr'), num('smooth'), num('minarea'), matte ? 1 : 0].join('|') + if (!fieldCache || fieldCache.key !== key) fieldCache = computeField(key, seg, sels, outs, nx, ny, cx, cy, binW, binH, sc, rect) + const F = fieldCache + const zTop = num('blockh'), zFloor = Math.max(1, zTop - num('depth')), clear = num('clear') * sc + // F' по знаковому расстоянию (px) со сдвигом на зазор; в ячейке-мосту: линейно по расстоянию → точный контур кармана + const Fv = new Float32Array(nx * ny), isIn = new Uint8Array(nx * ny) + for (let k = 0; k < nx * ny; k++) { const d = F.sd[k] + clear; Fv[k] = Math.min(1, Math.max(0, 0.5 + d / (2 * cell0 * sc))); isIn[k] = d >= 0 ? 1 : 0 } + const NXp = (i: number) => -binW / 2 + i * cx3, NYp = (j: number) => binH / 2 - j * cy3 + const tri: number[] = [], wall: number[] = [] + const emit = (poly: [number, number][], z: number) => { + let area = 0; for (let t = 0; t < poly.length; t++) { const a = poly[t], b = poly[(t + 1) % poly.length]; area += a[0] * b[1] - b[0] * a[1] } + const p = area < 0 ? [...poly].reverse() : poly + for (let t = 1; t + 1 < p.length; t++) tri.push(p[0][0], p[0][1], z, p[t][0], p[t][1], z, p[t + 1][0], p[t + 1][1], z) + } + for (let j = 0; j < ny - 1; j++) for (let i = 0; i < nx - 1; i++) { + const k0 = j * nx + i, ks = [k0, k0 + 1, k0 + nx + 1, k0 + nx], P = ks.map((k) => [NXp(k % nx), NYp(Math.floor(k / nx))] as [number, number]) + const flags = ks.map((k) => isIn[k] === 1), n = flags.filter(Boolean).length + if (n === 0) { emit(P, zTop); continue } + if (n === 4) { emit(P, zFloor); continue } + const outP: [number, number][] = [], inP: [number, number][] = [], segEnds: [number, number][] = [] + for (let e = 0; e < 4; e++) { + const a = e, b = (e + 1) % 4 + if (flags[a]) inP.push(P[a]); else outP.push(P[a]) + if (flags[a] !== flags[b]) { + const ia = flags[a] ? a : b, oa = flags[a] ? b : a, t = (Fv[ks[ia]] - 0.5) / Math.max(1e-6, Fv[ks[ia]] - Fv[ks[oa]]) + const c: [number, number] = [P[ia][0] + (P[oa][0] - P[ia][0]) * t, P[ia][1] + (P[oa][1] - P[ia][1]) * t] + inP.push(c); outP.push(c); segEnds.push(c) + } + } + emit(outP, zTop); emit(inP, zFloor) + if (segEnds.length >= 2) { const [a, b] = segEnds; wall.push(a[0], a[1], zFloor, b[0], b[1], zFloor, a[0], a[1], zTop, b[0], b[1], zFloor, b[0], b[1], zTop, a[0], a[1], zTop) } } - let tone = localContrast(gray, w, h, num('lcr') * pxMm, num('lca')) - const L = num('lvl'); if (L >= 2) tone = tone.map((v) => Math.round(v * (L - 1)) / (L - 1)) - const wsum = (mask || !depth ? wc : wc) + wd * (depth ? 1 : 0) + wt || 1 - const wcE = wc, wdE = depth ? wd : 0 - const sum = wcE + wdE + wt || 1 - const height = new Float32Array(w * h) - for (let i = 0; i < w * h; i++) height[i] = inside[i] ? (wcE * contour[i] + wdE * dnorm[i] + wt * tone[i]) / sum : 0 - void wsum - // рисуем высоту и «затенённый» рельеф (свет сверху слева), чтобы оценить, читается ли форма - const cv4 = $('c4'), cv5 = $('c5'); cv4.width = cv5.width = w; cv4.height = cv5.height = h - const g4 = cv4.getContext('2d')!.createImageData(w, h), g5 = cv5.getContext('2d')!.createImageData(w, h), k = num('light') - for (let j = 0; j < h; j++) for (let i = 0; i < w; i++) { - const p = j * w + i, v = Math.round(height[p] * 255) - g4.data[p * 4] = g4.data[p * 4 + 1] = g4.data[p * 4 + 2] = v; g4.data[p * 4 + 3] = 255 - const a = height[Math.max(0, j - 1) * w + Math.max(0, i - 1)], b = height[Math.min(h - 1, j + 1) * w + Math.min(w - 1, i + 1)] - const sh = Math.round(Math.min(1, Math.max(0, 0.5 + k * (a - b))) * 255) - const sv = inside[p] ? sh : 40 - g5.data[p * 4] = g5.data[p * 4 + 1] = g5.data[p * 4 + 2] = sv; g5.data[p * 4 + 3] = 255 + // боковые стенки корпуса и дно + const hx = binW / 2, hy = binH / 2 + const sideQuad = (a: [number, number], b: [number, number]) => wall.push(a[0], a[1], 0, b[0], b[1], 0, a[0], a[1], zTop, b[0], b[1], 0, b[0], b[1], zTop, a[0], a[1], zTop) + sideQuad([-hx, -hy], [hx, -hy]); sideQuad([hx, -hy], [hx, hy]); sideQuad([hx, hy], [-hx, hy]); sideQuad([-hx, hy], [-hx, -hy]) + tri.push(-hx, -hy, 0, -hx, hy, 0, hx, -hy, 0, hx, -hy, 0, -hx, hy, 0, hx, hy, 0) + const geo = new THREE.BufferGeometry(); geo.setAttribute('position', new THREE.BufferAttribute(new Float32Array([...tri, ...wall]), 3)); geo.computeVertexNormals() + mat.wireframe = chk('wire'); group.add(new THREE.Mesh(geo, mat)) + if (chk('tools')) for (const o of outs) { // инструменты: настоящий контур без зазора, стоят на дне кармана + const sh = new THREE.Shape(); const loop = o.loops.reduce((best, l) => (l.length > best.length ? l : best), o.loops[0]) + loop.forEach(([x, y], t) => { const X = x / sc - cx, Y = -(y / sc - cy); t ? sh.lineTo(X, Y) : sh.moveTo(X, Y) }) + const g2 = new THREE.ExtrudeGeometry(sh, { depth: Math.max(0.5, num('depth') - 0.6), bevelEnabled: false }); g2.translate(0, 0, zFloor + 0.05) + group.add(new THREE.Mesh(g2, toolMat)) } - cv4.getContext('2d')!.putImageData(g4, 0, 0); cv5.getContext('2d')!.putImageData(g5, 0, 0) + $('stat3d').textContent = `сетка ${nx}×${ny}, шаг ${cx3.toFixed(2)} мм, ${((tri.length + wall.length) / 9 / 1000).toFixed(0)} тыс. треуг.; карман ${(zTop - zFloor).toFixed(0)} мм, зазор ${num('clear')} мм` + const sig = `${binW}|${binH}`; if (sig !== fitted) { fitted = sig; frame(Math.max(binW, binH), 'iso') } } -for (const id of ['wc', 'wd', 'wt', 'partw', 'lcr', 'lca', 'lvl', 'bevel', 'light']) $(id).addEventListener('input', () => combine()) -const scaleMm = (scale: number) => 1 / (scale * scale) -$('run').addEventListener('click', () => { run().catch((e) => { log('ОШИБКА: ' + (e?.stack ?? e)); $('status').textContent = 'ошибка' }) }) +/** Знаковое расстояние (px листа, + внутри) в узлах сетки корпуса: точно у края (до контура), приближённо дальше (EDT). */ +function computeField(key: string, seg: Seg, sels: Sil[], outs: Outline[], nx: number, ny: number, cx: number, cy: number, binW: number, binH: number, sc: number, r: Rectified): Field { + const W = r.image.width, H = r.image.height, M = new Uint8Array(W * H), ids = new Set(sels.map((s) => s.id)) + for (let i = 0; i < M.length; i++) if (ids.has(seg.labels[i])) M[i] = 1 + const inv = M.map((v) => 1 - v) as Uint8Array + const dIn = edt(M, W, H), dOut = edt(inv, W, H) + const loops = outs.flatMap((o) => o.loops), sd = new Float32Array(nx * ny), cell = binW / (nx - 1), band = (num('clear') + 3 * cell) * sc + 2 + let exact = 0 + for (let j = 0; j < ny; j++) for (let i = 0; i < nx; i++) { + const X = (cx - binW / 2 + (i * binW) / (nx - 1)) * sc, Y = (cy - binH / 2 + (j * binH) / (ny - 1)) * sc // координаты листа (px), Y вниз + const xi = Math.min(W - 1, Math.max(0, Math.round(X))), yi = Math.min(H - 1, Math.max(0, Math.round(Y))), p = yi * W + xi + const inside = M[p] === 1, d = inside ? dIn[p] : dOut[p] + if (d <= band) { sd[j * nx + i] = signedDistance(loops, X, Y); exact++ } else sd[j * nx + i] = inside ? d : -d + } + log(`поле: ${nx}×${ny} узлов, точное расстояние в ${exact} узлах у края`) + return { key, nx, ny, cell, sd, cx, cy, binW, binH, scale: sc } +} -/** Серия прогонов без участия человека: window.__all[ключ] = результат, window.__done = true. */ -;(window as unknown as { runAll: (jobs: [string, string, number, string][]) => void }).runAll = (jobs) => { - const w = window as unknown as { __all: Record; __done: boolean } - w.__all = {}; w.__done = false - ;(async () => { - for (const [m, ep, th, sample] of jobs) { - $('model').value = m; $('ep').value = ep; $('threads').value = String(th); $('sample').value = sample - try { await run(); w.__all[`${m}/${ep}/${th}/${sample}`] = { ...(window as unknown as { __poc: object }).__poc, table: [...document.querySelectorAll('#tbl tr')].slice(1).map((r) => [...r.children].map((c) => c.textContent).join(' | ')), log: $('log').textContent } } - catch (e) { w.__all[`${m}/${ep}/${th}/${sample}`] = { error: String(e) } } - } - w.__done = true - })() +// ---------- запуск модели и события ---------- +$('runai').addEventListener('click', async () => { + if (!rect) return + const c = CANDIDATES.find((x) => x.id === $('modelsel').value)!, ep = $('ep').value, btn = $('runai'); btn.disabled = true + try { + log(`силуэты: ${c.id}, ${ep}, лицензия ${c.license}`) + const r = await infer(c, ep, 1, rect.rgb!, rect.image.width, rect.image.height, log) + log(`готово за ${r.ms.toFixed(0)} мс`); matte = r.plane; $('eng').value = 'ai'; fieldCache = null; schedule() + } catch (e) { log('ОШИБКА: ' + ((e as Error)?.message ?? e)) } finally { btn.disabled = false } +}) +$('sample').addEventListener('change', () => { $('file').value = ''; loadPhoto() }) +$('file').addEventListener('change', () => loadPhoto()) +for (const id of ['ppaper', 'porient']) $(id).addEventListener('change', () => { rebuildRect(); fieldCache = null; schedule() }) +for (const id of ['margin', 'sens']) $(id).addEventListener('change', () => { rebuildRect(); fieldCache = null; schedule() }) +for (const el of document.querySelectorAll('input[type=range], input[type=number], input[type=checkbox], select')) { + if (['file', 'sample', 'ppaper', 'porient', 'margin', 'sens', 'modelsel', 'ep'].includes(el.id) || el.closest('#sils')) continue + el.addEventListener('input', () => { if (el.id === 'lightang') setLight(); schedule() }) } +loadPhoto() From e10bbe25c7a1affedf884ef5e3a2cee8771934e3 Mon Sep 17 00:00:00 2001 From: skarakovski Date: Thu, 22 Oct 2026 09:31:41 +0700 Subject: [PATCH 27/38] poc: smooth AI outline (bicubic upsampling, per-object second pass, roughness metric); plan 32: smooth outline requirement and task Co-Authored-By: Claude Sonnet 5.5 --- openspec/changes/32-photo-trace-ai/design.md | 1 + .../specs/cutout-photo-ai/spec.md | 15 ++++++++ openspec/changes/32-photo-trace-ai/tasks.md | 1 + poc/ai/FINDINGS.md | 3 ++ poc/ai/cutout.html | 1 + poc/ai/cutout.ts | 34 +++++++++++++++++-- poc/ai/engine.ts | 24 ++++++++----- 7 files changed, 69 insertions(+), 10 deletions(-) diff --git a/openspec/changes/32-photo-trace-ai/design.md b/openspec/changes/32-photo-trace-ai/design.md index 8c8b844..cf1acf4 100644 --- a/openspec/changes/32-photo-trace-ai/design.md +++ b/openspec/changes/32-photo-trace-ai/design.md @@ -33,6 +33,7 @@ Why the straightened sheet is a good input for a network: it is a top view at tr - **Salient-object networks and shadows.** These networks were trained to cut out objects and usually leave a cast shadow out (the argument in `PREPROP_CUTOUT_GOOGLE.md`), but "usually" is not a guarantee, and thin dark parts (pliers jaws) can be eaten. The harness therefore reports, per fixture, the area difference to the baseline split into "network larger" (shadow leaked) and "network smaller" (part lost). - **Mask → silhouettes.** Threshold the alpha at 0.5, then the same cleanup steps the classic path uses, imported from `geometry/raster` (`close`, `fillHoles`, `removeSpecks`, `labelComponents`, edge margin, `minSize`/`minArea` from `defaultSegmentParams`). The numbering rule is the classic one (top to bottom), so list order and colours are identical between engines for the same objects. Pure function `aiSegmentation(rectified, alpha, params) → Segmentation`, unit-tested on stored alphas. - **Mask tuning (threshold and smoothing).** The network returns a soft alpha (0…1) with a soft zone along every edge. Two controls turn it into a silhouette, both applied to the cached alpha so the network is not re-run: a **threshold** (default 0.5, range 0.05…0.95; lower grows the outline into the soft zone, higher shrinks it) and a **smoothing radius** in mm (default 0.3, range 0…3; masked Gaussian from `geometry/raster/blur.ts` on the alpha before the threshold, σ = radius × scale, never below `MIN_SIGMA`). Smoothing evens out wavy and stair-stepped edges (the 1024 px output is stretched to 4 px/mm) and merges hairline cracks; too much removes thin parts, so the list shows each silhouette's size change and a hint appears when a silhouette shrinks by more than 1 mm. The PoC page already has both sliders with live recalculation, a greyscale view of the soft alpha, the outline length (shorter = smoother) and the area of the soft zone; the defaults and the size of the soft zone in mm are taken from its measurements. The two controls are user-facing and always visible under the AI engine (labels "Edge reach" / "Outline smoothness" with a one-line hint each and the numeric value; the technical names stay in code). Defaults are per model: the registry has `maskDefaults: {threshold, smooth}` for each entry (e.g. a lower threshold for ISNet, whose mask is tighter), found with the PoC sliders; switching the model loads them unless the user has touched the controls for the photo, and Reset returns to them. The values are session state of the dialog (`PhotoSettings.ai = {threshold, smooth}`), per photo not persisted, with a reset. With the hybrid edge the classic colour test places the final edge inside a band around this mask, so the two settings mainly decide which object parts count and how the band is placed; the traced outline is then smoothed by `traceSilhouette` as for the classic engine (σ = 1 px, 0.15 mm simplification). +- **Why the AI outline came out ribbed, and the fix.** The classic mask is made from the photo itself at 4 px/mm (0.25 mm per pixel) and `traceSilhouette` then blurs it (σ = 1 px), takes the 128 iso-line and simplifies it by 0.15 mm, so its outline is smooth. A model returns a fixed-size mask (512 × 512 for BiRefNet-lite) for the whole sheet, about 0.6 mm per output pixel, and stretching it with bilinear interpolation to 4 px/mm leaves a wobble with the period of the output pixel. Three measures, all measured in the PoC with a roughness metric (mean distance of the outline's vertices from the same outline smoothed with σ = 1.5 mm): (1) **bicubic (Catmull–Rom) upsampling** of the soft mask instead of bilinear; (2) a **second pass per object**: each object found by the first pass is cropped with a margin (12 mm) and run again, so the 512 px of the network cover the object only (about 0.3 mm instead of 0.6 mm per pixel), blended into the first result at the crop border; (3) the **vector outline smoothing** of the keychain PoC (resample, corner detection, Gaussian along the curve with corners fixed, default σ 0.25 mm) in place of the plain `simplifyLoop`. PoC result on the owner's flash photo: roughness of the classic outline 59 µm, AI with bicubic and vector smoothing 65 µm, AI with the second pass 58 µm, that is, the AI outline is as smooth as the classic one. The second pass costs one more model run per object (about 6 s each on WASM), so it is a toggle, on by default for the final result. - **Hybrid edge.** For each silhouette: band = mask dilated by `band` mm (default 1.5) minus mask eroded by `band`. Inside the band the classic paper-colour deviation (`colourMask` from the paper surface fitted by `paperSurface`; `paperSurface` and `colourMask` are called, not changed) classifies pixels; the refined mask is `eroded core ∪ (band ∩ classic object pixels)`. If the classic test marks the whole band as paper or as object on a stretch longer than a few mm (the dark-part-in-shadow case), that stretch keeps the network edge. The refined labels go through the same `traceAlpha` as today. A setting `refine: on|off` (default on) exists in the compare view so the raw network edge can be inspected; it is not a persisted user option beyond the session. - **Compare view.** `CompareOverlay` draws the other engine's silhouette outlines (already traced by `traceSilhouette`) as dashed lines over the straightened view, plus a table "tool: classic mm / AI mm / Δ". It costs one extra segmentation when open, and is the way the user, and the owner while developing, sees where the engines disagree. - **ML runtime.** `src/ml/` is React-free. `matte(rgb, w, h) → Float32 alpha` is the only adapter this change needs. `ml.worker.ts` owns the inference session; `mlClient.ts` mirrors `modelClient.ts` (latest request wins, stale replies dropped); the worker is created lazily so classic users pay nothing, not even the library chunk (dynamic import). Backend: try WebGPU, fall back to WASM single-threaded (no COOP/COEP on Pages); report the backend in the model manager. diff --git a/openspec/changes/32-photo-trace-ai/specs/cutout-photo-ai/spec.md b/openspec/changes/32-photo-trace-ai/specs/cutout-photo-ai/spec.md index 6a9860d..21a2a2c 100644 --- a/openspec/changes/32-photo-trace-ai/specs/cutout-photo-ai/spec.md +++ b/openspec/changes/32-photo-trace-ai/specs/cutout-photo-ai/spec.md @@ -149,6 +149,21 @@ The AI engine SHALL place the silhouette edge by the paper-colour test within a - **WHEN** a 40 × 20 mm rectangle lies on the paper and the corners are placed correctly - **THEN** the AI silhouette measures 40 × 20 mm within 0.5 mm +### Requirement: Smooth outline +The outline of a silhouette found by the AI engine SHALL be as smooth as the outline of the same object found by the classic engine: it SHALL NOT show a ribbed or wavy line that follows the resolution of the model's mask. The app SHALL upsample the model's soft mask smoothly, SHALL offer to refine each found object with a second model pass on its own crop, and SHALL smooth the traced outline along the curve while keeping sharp corners. + +#### Scenario: Comparable smoothness +- **WHEN** the same photo is processed by both engines +- **THEN** the roughness of the AI outline (mean distance of its vertices from the same outline smoothed by 1.5 mm) is within 25 % of the classic outline's + +#### Scenario: Second pass +- **WHEN** the second pass is on +- **THEN** each object is run again on its own crop and its outline is no rougher than without the second pass + +#### Scenario: Sharp tips +- **WHEN** a tool has a pointed tip +- **THEN** the smoothing does not round the tip away + ### Requirement: Compare engines The user SHALL be able to compare the two engines on the straightened view: the silhouettes of the inactive engine are drawn as outlines over the view, and a table lists each tool's width and height for both engines and the difference. diff --git a/openspec/changes/32-photo-trace-ai/tasks.md b/openspec/changes/32-photo-trace-ai/tasks.md index 7c7a418..652e6a2 100644 --- a/openspec/changes/32-photo-trace-ai/tasks.md +++ b/openspec/changes/32-photo-trace-ai/tasks.md @@ -14,6 +14,7 @@ - [ ] 3.1 `geometry/photo/ai.ts`: `aiSegmentation(rectified, alpha, params) → Segmentation` (threshold, close, fill holes, margin, specks, components, numbering top to bottom, minimum size and area as the classic path). Tests on the stored alphas from 1.1: three silhouettes per fixture, sizes within 1 mm of the baseline, nothing in the edge margin, empty alpha gives no silhouettes, two touching blobs stay one silhouette. - [ ] 3.1a Mask tuning in `geometry/photo/ai.ts`: `aiSegmentation(rectified, alpha, {threshold, smooth, …})` smooths the alpha (`gaussianBlur`, masked by the full raster) and thresholds it; the alpha is cached by the caller. Tests on the stored alphas: lowering the threshold never shrinks a silhouette and raising it never grows one (monotone), smoothing does not lengthen the outline, smoothing 0 equals the unsmoothed result, a thin synthetic part (1 mm) disappears at a large radius and survives at a small one, defaults give the sizes of 3.1. +- [ ] 3.1b Smooth AI outline: bicubic upsampling of the soft mask, the optional second pass per object (crop with a 12 mm margin, blended at the crop border), the corner-preserving vector smoothing, and a `roughness(outline)` helper. Tests on the stored alphas: the roughness of the AI outline of each fixture is within 25 % of the classic outline's, the second pass does not increase it, a synthetic spike keeps its tip within 0.3 mm, a bilinear upsampling of the same alpha is measurably rougher than the bicubic one. - [ ] 3.2 Hybrid edge in `geometry/photo/ai.ts`: band around the mask, classic paper-colour test inside the band (calling `paperSurface` and `colourMask`), network edge kept where the test is uninformative, refined labels. Tests: sizes within 0.5 mm of the baseline and IoU ≥ 0.97 on both fixtures; a synthetic 40 × 20 mm rectangle (`synthetic.ts`) with a deliberately puffy mask measures 40 × 20 mm within 0.5 mm; a synthetic dark part merged into a shadow keeps the network edge and stays one piece; `refine: off` returns the raw mask result. ## 4. ML runtime diff --git a/poc/ai/FINDINGS.md b/poc/ai/FINDINGS.md index 51f9ffc..efc3821 100644 --- a/poc/ai/FINDINGS.md +++ b/poc/ai/FINDINGS.md @@ -105,3 +105,6 @@ ### PoC вырезов (cutout.html) переделан как пайплайн Этапы: 1 фото и лист (авто-углы, формат, ориентация) → 2 силуэты (классика-baseline или модель маски с порогом и сглаживанием в мм; сравнение контуров двух движков и таблица размеров/IoU; галочки по силуэтам) → 3 контур (векторный, с сохранением углов, как в Keychain) → 4 вырез и корпус (глубина, зазор, поле, шаг юнита, высота; корпус целыми юнитами) → 3D с карманами по точному контуру (зазор сдвигает контур карманa по знаковому расстоянию), инструменты полупрозрачным слоем. Контуры карманов режутся по изолинии без ступеней. Старая страница сравнения (`runAll`) заменена этим пайплайном: сравнение осталось как галочка и таблица. + +### Почему AI-контур был ребристым и как исправлено (cutout.html) +Классика строит маску по самому фото при 4 px/мм (0.25 мм на пиксель) + размытие σ = 1 px + изолиния + упрощение 0.15 мм. Модель отдаёт 512 × 512 на весь лист (≈ 0.6 мм на пиксель), билинейное растяжение давало волну с периодом пикселя сети. Исправления: бикубическая интерполяция (Катмулл–Ром), второй проход по каждому объекту (кроп с полем 12 мм: ≈ 0.3 мм на пиксель), векторное сглаживание с сохранением углов. Метрика «шероховатость» (среднее отклонение вершин от контура, сглаженного σ = 1.5 мм): классика 59 мкм, AI без второго прохода 65 мкм, AI со вторым проходом 58 мкм (flash, birefnet-lite-512-fp16, WASM). Второй проход стоит ≈ 6 с на объект. diff --git a/poc/ai/cutout.html b/poc/ai/cutout.html index 147d81e..1c046bc 100644 --- a/poc/ai/cutout.html +++ b/poc/ai/cutout.html @@ -39,6 +39,7 @@
    diff --git a/poc/ai/cutout.ts b/poc/ai/cutout.ts index 47341c1..eee19d0 100644 --- a/poc/ai/cutout.ts +++ b/poc/ai/cutout.ts @@ -130,6 +130,18 @@ function outlineOf(seg: Seg, sil: Sil, r: Rectified): Outline { return { id: sil.id, loops } } +/** Шероховатость контура, мм: среднее отклонение вершин от того же контура, сглаженного гауссом σ = 1.5 мм (чем меньше, тем плавнее линия). */ +function roughness(loops: Pt[][], scale: number): number { + let sum = 0, cnt = 0 + for (const l of loops) { + if (l.length < 12) continue + const step = 0.7, n = l.length, sg = (1.5 * scale) / step, r = Math.ceil(3 * sg), k: number[] = []; let ks = 0 + for (let i = -r; i <= r; i++) { const v = Math.exp(-(i * i) / (2 * sg * sg)); k.push(v); ks += v } + for (let i = 0; i < n; i++) { let x = 0, y = 0; for (let d = -r; d <= r; d++) { const q = l[(((i + d) % n) + n) % n]; x += k[d + r] * q[0]; y += k[d + r] * q[1] } ; sum += Math.hypot(l[i][0] - x / ks, l[i][1] - y / ks); cnt++ } + } + return cnt ? sum / cnt / scale : 0 +} + interface Field { key: string; nx: number; ny: number; cell: number; sd: Float32Array; cx: number; cy: number; binW: number; binH: number; scale: number } let fieldCache: Field | null = null @@ -211,7 +223,7 @@ function build() { if (!sels.length) { $('stat3d').textContent = 'нет выбранных силуэтов'; return } const sc = rect.scale, cell0 = Math.max(0.15, num('cell')) const outs = sels.map((s) => outlineOf(seg, s, rect!)) - $('st-out').textContent = `${outs.reduce((a, o) => a + o.loops.length, 0)} петель, ${outs.reduce((a, o) => a + o.loops.reduce((b, l) => b + l.length, 0), 0)} вершин` + $('st-out').textContent = `${outs.reduce((a, o) => a + o.loops.length, 0)} петель, ${outs.reduce((a, o) => a + o.loops.reduce((b, l) => b + l.length, 0), 0)} вершин; шероховатость ${(roughness(outs.flatMap((o) => o.loops), sc) * 1000).toFixed(0)} мкм` // габарит всех инструментов (мм листа) → корпус целыми юнитами let x0 = Infinity, y0 = Infinity, x1 = -Infinity, y1 = -Infinity for (const o of outs) for (const l of o.loops) for (const [x, y] of l) { x0 = Math.min(x0, x); x1 = Math.max(x1, x); y0 = Math.min(y0, y); y1 = Math.max(y1, y) } @@ -288,13 +300,31 @@ function computeField(key: string, seg: Seg, sels: Sil[], outs: Outline[], nx: n } // ---------- запуск модели и события ---------- +/** Второй проход: каждый найденный объект (рамка + поле) прогоняется моделью отдельно, поэтому на объект приходятся все 512 px сети (≈0.3 мм вместо ≈0.6 мм на весь лист). */ +async function refine(c: (typeof CANDIDATES)[number], ep: string, base: Float32Array, r: Rectified): Promise { + const seg0 = aiSegmentation(r, base), W = r.image.width, H = r.image.height, out = Float32Array.from(base), pad = Math.round(12 * r.scale) + for (const s of seg0.sils) { + const x0 = Math.max(0, s.bbox[0] - pad), y0 = Math.max(0, s.bbox[1] - pad), x1 = Math.min(W - 1, s.bbox[2] + pad), y1 = Math.min(H - 1, s.bbox[3] + pad), cw = x1 - x0 + 1, ch = y1 - y0 + 1 + const crop = new Uint8Array(cw * ch * 3); for (let j = 0; j < ch; j++) for (let i = 0; i < cw; i++) { const q = ((y0 + j) * W + x0 + i) * 3; crop.set(r.rgb!.subarray(q, q + 3), (j * cw + i) * 3) } + const res = await infer(c, ep, 1, crop, cw, ch, () => {}) + log(` уточнение объекта №${s.id}: ${cw}×${ch} px, ${res.ms.toFixed(0)} мс`) + for (let j = 0; j < ch; j++) for (let i = 0; i < cw; i++) { // внутри рамки без самого края (край рамки остаётся от общего прохода) + const edge = Math.min(i, j, cw - 1 - i, ch - 1 - j), w2 = Math.min(1, edge / (pad * 0.5)) // плавный переход к общему проходу на краю рамки + out[(y0 + j) * W + x0 + i] = base[(y0 + j) * W + x0 + i] * (1 - w2) + res.plane[j * cw + i] * w2 + } + } + return out +} + $('runai').addEventListener('click', async () => { if (!rect) return const c = CANDIDATES.find((x) => x.id === $('modelsel').value)!, ep = $('ep').value, btn = $('runai'); btn.disabled = true try { log(`силуэты: ${c.id}, ${ep}, лицензия ${c.license}`) const r = await infer(c, ep, 1, rect.rgb!, rect.image.width, rect.image.height, log) - log(`готово за ${r.ms.toFixed(0)} мс`); matte = r.plane; $('eng').value = 'ai'; fieldCache = null; schedule() + log(`общий проход за ${r.ms.toFixed(0)} мс`); matte = r.plane + if (chk('refine')) { const t = performance.now(); matte = await refine(c, ep, matte, rect); log(`второй проход по объектам за ${((performance.now() - t) / 1000).toFixed(1)} с`) } + $('eng').value = 'ai'; fieldCache = null; schedule() } catch (e) { log('ОШИБКА: ' + ((e as Error)?.message ?? e)) } finally { btn.disabled = false } }) $('sample').addEventListener('change', () => { $('file').value = ''; loadPhoto() }) diff --git a/poc/ai/engine.ts b/poc/ai/engine.ts index 8b87cf5..62a45c2 100644 --- a/poc/ai/engine.ts +++ b/poc/ai/engine.ts @@ -52,16 +52,24 @@ function toInput(rgb: Uint8Array, w: number, h: number, c: Candidate, s: number) return out } -/** Выход s × s → плоскость w × h (билинейно, поля вписывания отброшены). */ +/** Бикубическая (Катмулл–Ром) интерполяция плоскости: гладкая изолиния при растяжении, без «ступеней» билинейной сетки. */ +export function cubicSample(v: Float32Array, w: number, h: number, x: number, y: number): number { + x -= 0.5; y -= 0.5 + const x0 = Math.floor(x), y0 = Math.floor(y), tx = x - x0, ty = y - y0 + const wgt = (t: number) => [(-t * t * t + 2 * t * t - t) / 2, (3 * t * t * t - 5 * t * t + 2) / 2, (-3 * t * t * t + 4 * t * t + t) / 2, (t * t * t - t * t) / 2] + const wx = wgt(tx), wy = wgt(ty); let acc = 0 + for (let j = 0; j < 4; j++) { + const yy = Math.min(h - 1, Math.max(0, y0 - 1 + j)); let row = 0 + for (let i = 0; i < 4; i++) row += wx[i] * v[yy * w + Math.min(w - 1, Math.max(0, x0 - 1 + i))] + acc += wy[j] * row + } + return acc +} + +/** Выход s × s → плоскость w × h (бикубически, поля вписывания отброшены), значения 0…1. */ function fromOutput(v: Float32Array, s: number, w: number, h: number): Float32Array { const out = new Float32Array(w * h), r = Math.min(s / w, s / h), ox = (s - w * r) / 2, oy = (s - h * r) / 2 - for (let j = 0; j < h; j++) { - const fy = Math.min(s - 1.001, Math.max(0, (j + 0.5) * r + oy - 0.5)), y0 = Math.floor(fy), ty = fy - y0 - for (let i = 0; i < w; i++) { - const fx = Math.min(s - 1.001, Math.max(0, (i + 0.5) * r + ox - 0.5)), x0 = Math.floor(fx), tx = fx - x0 - out[j * w + i] = (v[y0 * s + x0] * (1 - tx) + v[y0 * s + x0 + 1] * tx) * (1 - ty) + (v[(y0 + 1) * s + x0] * (1 - tx) + v[(y0 + 1) * s + x0 + 1] * tx) * ty - } - } + for (let j = 0; j < h; j++) for (let i = 0; i < w; i++) out[j * w + i] = Math.min(1, Math.max(0, cubicSample(v, s, s, (i + 0.5) * r + ox, (j + 0.5) * r + oy))) return out } From b55ebb71e12c36f88e611d2d386fe4475f4b4d21 Mon Sep 17 00:00:00 2001 From: skarakovski Date: Thu, 22 Oct 2026 09:37:31 +0700 Subject: [PATCH 28/38] poc: cutout 3D pockets from the exact polygon (smooth wall normals), no grid ribbing Co-Authored-By: Claude Sonnet 5.5 --- poc/ai/FINDINGS.md | 3 ++ poc/ai/cutout.html | 1 + poc/ai/cutout.ts | 111 +++++++++++++++++++++------------------------ 3 files changed, 55 insertions(+), 60 deletions(-) diff --git a/poc/ai/FINDINGS.md b/poc/ai/FINDINGS.md index efc3821..7de4c2e 100644 --- a/poc/ai/FINDINGS.md +++ b/poc/ai/FINDINGS.md @@ -108,3 +108,6 @@ ### Почему AI-контур был ребристым и как исправлено (cutout.html) Классика строит маску по самому фото при 4 px/мм (0.25 мм на пиксель) + размытие σ = 1 px + изолиния + упрощение 0.15 мм. Модель отдаёт 512 × 512 на весь лист (≈ 0.6 мм на пиксель), билинейное растяжение давало волну с периодом пикселя сети. Исправления: бикубическая интерполяция (Катмулл–Ром), второй проход по каждому объекту (кроп с полем 12 мм: ≈ 0.3 мм на пиксель), векторное сглаживание с сохранением углов. Метрика «шероховатость» (среднее отклонение вершин от контура, сглаженного σ = 1.5 мм): классика 59 мкм, AI без второго прохода 65 мкм, AI со вторым проходом 58 мкм (flash, birefnet-lite-512-fp16, WASM). Второй проход стоит ≈ 6 с на объект. + +### Рябь на стенках кармана в 3D PoC вырезов (исправлено) +Вертикальные полосы на стенках появлялись даже у классики и были артефактом построения 3D в PoC, а не контура: стенки собирались из точек пересечения с узлами сетки (0.7 мм) и имели плоские нормали по граням. Теперь, как в приложении (контур → смещение на зазор → выдавливание), стенка кармана строится вдоль самого полигона, верх — прямоугольник с отверстиями (`ShapeUtils.triangulateShape`), дно кармана — контур, а нормали вершин усредняются по окну ≈ 1 мм вдоль контура (поле «окно нормалей стенки»), чтобы шум контура в десятки мкм не давал полос. Число треугольников: 263 тыс. → 36 тыс. diff --git a/poc/ai/cutout.html b/poc/ai/cutout.html index 1c046bc..e7871a3 100644 --- a/poc/ai/cutout.html +++ b/poc/ai/cutout.html @@ -51,6 +51,7 @@

    3Контур (как в keychain)

    +

    4Вырез и корпус

    diff --git a/poc/ai/cutout.ts b/poc/ai/cutout.ts index eee19d0..da52cff 100644 --- a/poc/ai/cutout.ts +++ b/poc/ai/cutout.ts @@ -143,7 +143,19 @@ function roughness(loops: Pt[][], scale: number): number { } interface Field { key: string; nx: number; ny: number; cell: number; sd: Float32Array; cx: number; cy: number; binW: number; binH: number; scale: number } -let fieldCache: Field | null = null +/** Смещение замкнутого контура (против часовой стрелки) наружу на d мм по нормалям вершин с митрой (масштаб ≤ 2). */ +function offsetLoop(p: [number, number][], d: number): [number, number][] { + if (d <= 0) return p + const n = p.length + return p.map((b, i) => { + const a = p[(i - 1 + n) % n], c = p[(i + 1) % n] + const e1 = [b[0] - a[0], b[1] - a[1]], e2 = [c[0] - b[0], c[1] - b[1]], l1 = Math.hypot(e1[0], e1[1]) || 1, l2 = Math.hypot(e2[0], e2[1]) || 1 + const n1 = [e1[1] / l1, -e1[0] / l1], n2 = [e2[1] / l2, -e2[0] / l2], mx = n1[0] + n2[0], my = n1[1] + n2[1], ml = Math.hypot(mx, my) || 1 + const cos = (n1[0] * mx + n1[1] * my) / ml, k = d / Math.max(0.5, cos) + return [b[0] + (mx / ml) * k, b[1] + (my / ml) * k] as [number, number] + }) +} + function activeSeg(): Seg | null { if ($('eng').value === 'ai' && rect && matte) return aiSegmentation(rect, matte) @@ -231,45 +243,42 @@ function build() { const ux = Math.max(1, Math.ceil((ex + 2 * m) / pitch - 1e-9)), uy = Math.max(1, Math.ceil((ey + 2 * m) / pitch - 1e-9)), binW = ux * pitch, binH = uy * pitch binDims.w = binW; binDims.h = binH $('st-bin').textContent = `инструменты ${ex.toFixed(0)}×${ey.toFixed(0)} мм → корпус ${ux}×${uy} юнитов (${binW}×${binH} мм)` - const nx = Math.round(binW / cell0) + 1, ny = Math.round(binH / cell0) + 1, cx3 = binW / (nx - 1), cy3 = binH / (ny - 1) - const key = [sels.map((s) => s.id).join(','), $('eng').value, chk('vec'), num('osm'), num('cang'), nx, ny, binW, binH, num('margin'), num('thr'), num('smooth'), num('minarea'), matte ? 1 : 0].join('|') - if (!fieldCache || fieldCache.key !== key) fieldCache = computeField(key, seg, sels, outs, nx, ny, cx, cy, binW, binH, sc, rect) - const F = fieldCache - const zTop = num('blockh'), zFloor = Math.max(1, zTop - num('depth')), clear = num('clear') * sc - // F' по знаковому расстоянию (px) со сдвигом на зазор; в ячейке-мосту: линейно по расстоянию → точный контур кармана - const Fv = new Float32Array(nx * ny), isIn = new Uint8Array(nx * ny) - for (let k = 0; k < nx * ny; k++) { const d = F.sd[k] + clear; Fv[k] = Math.min(1, Math.max(0, 0.5 + d / (2 * cell0 * sc))); isIn[k] = d >= 0 ? 1 : 0 } - const NXp = (i: number) => -binW / 2 + i * cx3, NYp = (j: number) => binH / 2 - j * cy3 - const tri: number[] = [], wall: number[] = [] - const emit = (poly: [number, number][], z: number) => { - let area = 0; for (let t = 0; t < poly.length; t++) { const a = poly[t], b = poly[(t + 1) % poly.length]; area += a[0] * b[1] - b[0] * a[1] } - const p = area < 0 ? [...poly].reverse() : poly - for (let t = 1; t + 1 < p.length; t++) tri.push(p[0][0], p[0][1], z, p[t][0], p[t][1], z, p[t + 1][0], p[t + 1][1], z) - } - for (let j = 0; j < ny - 1; j++) for (let i = 0; i < nx - 1; i++) { - const k0 = j * nx + i, ks = [k0, k0 + 1, k0 + nx + 1, k0 + nx], P = ks.map((k) => [NXp(k % nx), NYp(Math.floor(k / nx))] as [number, number]) - const flags = ks.map((k) => isIn[k] === 1), n = flags.filter(Boolean).length - if (n === 0) { emit(P, zTop); continue } - if (n === 4) { emit(P, zFloor); continue } - const outP: [number, number][] = [], inP: [number, number][] = [], segEnds: [number, number][] = [] - for (let e = 0; e < 4; e++) { - const a = e, b = (e + 1) % 4 - if (flags[a]) inP.push(P[a]); else outP.push(P[a]) - if (flags[a] !== flags[b]) { - const ia = flags[a] ? a : b, oa = flags[a] ? b : a, t = (Fv[ks[ia]] - 0.5) / Math.max(1e-6, Fv[ks[ia]] - Fv[ks[oa]]) - const c: [number, number] = [P[ia][0] + (P[oa][0] - P[ia][0]) * t, P[ia][1] + (P[oa][1] - P[ia][1]) * t] - inP.push(c); outP.push(c); segEnds.push(c) - } + // Карманы строятся прямо по контуру (как в приложении: контур → смещение на зазор → выдавливание), а не по узлам сетки: + // стенки — вертикальные ленты вдоль полигона с гладкими нормалями, верх — прямоугольник с отверстиями-карманами, дно кармана — сам контур. + const zTop = num('blockh'), zFloor = Math.max(1, zTop - num('depth')), clearMm = num('clear') + const toMm = (l: Pt[]): [number, number][] => l.map(([x, y]) => [x / sc - cx, -(y / sc - cy)]) + const area = (l: [number, number][]) => l.reduce((a, [x, y], i) => { const [x2, y2] = l[(i + 1) % l.length]; return a + (x * y2 - x2 * y) / 2 }, 0) + const pockets = outs.map((o) => { + const outer = o.loops.reduce((best, l) => (Math.abs(area(toMm(l))) > Math.abs(area(toMm(best))) ? l : best), o.loops[0]) + let poly = toMm(outer); if (area(poly) < 0) poly = poly.reverse() // против часовой стрелки: наружу — вправо от хода + return offsetLoop(poly, clearMm) + }) + const hx = binW / 2, hy = binH / 2, pos: number[] = [], nor: number[] = [] + const tri = (a: number[], b: number[], c: number[], n: number[]) => { pos.push(...a, ...b, ...c); for (let t = 0; t < 3; t++) nor.push(...n) } + const flat = (poly: [number, number][], holes: [number, number][][], z: number, up: boolean) => { + const pts = [...poly, ...holes.flat()], faces = THREE.ShapeUtils.triangulateShape(poly.map((p) => new THREE.Vector2(p[0], p[1])), holes.map((h) => h.map((p) => new THREE.Vector2(p[0], p[1])))) + for (const [i, j, k] of faces) { + const A = pts[i], B = pts[j], C = pts[k], sa = (B[0] - A[0]) * (C[1] - A[1]) - (C[0] - A[0]) * (B[1] - A[1]), flip = (sa < 0) === up + const P = (q: [number, number]) => [q[0], q[1], z] + flip ? tri(P(A), P(C), P(B), [0, 0, up ? 1 : -1]) : tri(P(A), P(B), P(C), [0, 0, up ? 1 : -1]) } - emit(outP, zTop); emit(inP, zFloor) - if (segEnds.length >= 2) { const [a, b] = segEnds; wall.push(a[0], a[1], zFloor, b[0], b[1], zFloor, a[0], a[1], zTop, b[0], b[1], zFloor, b[0], b[1], zTop, a[0], a[1], zTop) } } - // боковые стенки корпуса и дно - const hx = binW / 2, hy = binH / 2 - const sideQuad = (a: [number, number], b: [number, number]) => wall.push(a[0], a[1], 0, b[0], b[1], 0, a[0], a[1], zTop, b[0], b[1], 0, b[0], b[1], zTop, a[0], a[1], zTop) - sideQuad([-hx, -hy], [hx, -hy]); sideQuad([hx, -hy], [hx, hy]); sideQuad([hx, hy], [-hx, hy]); sideQuad([-hx, hy], [-hx, -hy]) - tri.push(-hx, -hy, 0, -hx, hy, 0, hx, -hy, 0, hx, -hy, 0, -hx, hy, 0, hx, hy, 0) - const geo = new THREE.BufferGeometry(); geo.setAttribute('position', new THREE.BufferAttribute(new Float32Array([...tri, ...wall]), 3)); geo.computeVertexNormals() + flat([[-hx, -hy], [hx, -hy], [hx, hy], [-hx, hy]], pockets, zTop, true) // верх с отверстиями + for (const pk of pockets) flat(pk, [], zFloor, true) // дно карманов + flat([[-hx, -hy], [hx, -hy], [hx, hy], [-hx, hy]], [], 0, false) // дно корпуса + const quad = (a: number[], b: number[], c: number[], d: number[], na: number[], nb: number[]) => { // a,b снизу; c,d сверху (c над b, d над a) + pos.push(...a, ...b, ...d, ...b, ...c, ...d); nor.push(...na, ...nb, ...na, ...nb, ...nb, ...na) + } + for (const pk of pockets) { // стенки кармана: нормали в сторону кармана, сглаженные по вершинам + // нормаль вершины — среднее нормалей рёбер в окне ±nw мм вдоль контура: шум в десятки мкм не даёт полос на стенке + const n = pk.length, perim = pk.reduce((a, q, i) => a + Math.hypot(pk[(i + 1) % n][0] - q[0], pk[(i + 1) % n][1] - q[1]), 0), k = Math.max(1, Math.round((num('nwin') / perim) * n)) + const en = pk.map((q, i) => { const r = pk[(i + 1) % n], dx = r[0] - q[0], dy = r[1] - q[1], l = Math.hypot(dx, dy) || 1; return [dy / l, -dx / l] }) // наружу кармана (контур против часовой) + const vn = pk.map((_, i) => { let x = 0, y = 0; for (let d = -k; d <= k; d++) { const e = en[(((i + d) % n) + n) % n], w = 1 - Math.abs(d) / (k + 1); x += w * e[0]; y += w * e[1] } const l = Math.hypot(x, y) || 1; return [-x / l, -y / l, 0] }) + for (let i = 0; i < n; i++) { const a = pk[i], b = pk[(i + 1) % n]; quad([a[0], a[1], zFloor], [b[0], b[1], zFloor], [b[0], b[1], zTop], [a[0], a[1], zTop], vn[i], vn[(i + 1) % n]) } + } + const sideN = [[0, -1, 0], [1, 0, 0], [0, 1, 0], [-1, 0, 0]], corners: [number, number][] = [[-hx, -hy], [hx, -hy], [hx, hy], [-hx, hy]] + for (let i = 0; i < 4; i++) { const a = corners[i], b = corners[(i + 1) % 4]; quad([b[0], b[1], 0], [a[0], a[1], 0], [a[0], a[1], zTop], [b[0], b[1], zTop], sideN[i], sideN[i]) } + const geo = new THREE.BufferGeometry(); geo.setAttribute('position', new THREE.BufferAttribute(new Float32Array(pos), 3)); geo.setAttribute('normal', new THREE.BufferAttribute(new Float32Array(nor), 3)) mat.wireframe = chk('wire'); group.add(new THREE.Mesh(geo, mat)) if (chk('tools')) for (const o of outs) { // инструменты: настоящий контур без зазора, стоят на дне кармана const sh = new THREE.Shape(); const loop = o.loops.reduce((best, l) => (l.length > best.length ? l : best), o.loops[0]) @@ -277,28 +286,10 @@ function build() { const g2 = new THREE.ExtrudeGeometry(sh, { depth: Math.max(0.5, num('depth') - 0.6), bevelEnabled: false }); g2.translate(0, 0, zFloor + 0.05) group.add(new THREE.Mesh(g2, toolMat)) } - $('stat3d').textContent = `сетка ${nx}×${ny}, шаг ${cx3.toFixed(2)} мм, ${((tri.length + wall.length) / 9 / 1000).toFixed(0)} тыс. треуг.; карман ${(zTop - zFloor).toFixed(0)} мм, зазор ${num('clear')} мм` + $('stat3d').textContent = `${pockets.length} кармана, ${pockets.reduce((a, p) => a + p.length, 0)} вершин контура, ${(pos.length / 9 / 1000).toFixed(0)} тыс. треуг.; глубина ${(zTop - zFloor).toFixed(0)} мм, зазор ${clearMm} мм` const sig = `${binW}|${binH}`; if (sig !== fitted) { fitted = sig; frame(Math.max(binW, binH), 'iso') } } -/** Знаковое расстояние (px листа, + внутри) в узлах сетки корпуса: точно у края (до контура), приближённо дальше (EDT). */ -function computeField(key: string, seg: Seg, sels: Sil[], outs: Outline[], nx: number, ny: number, cx: number, cy: number, binW: number, binH: number, sc: number, r: Rectified): Field { - const W = r.image.width, H = r.image.height, M = new Uint8Array(W * H), ids = new Set(sels.map((s) => s.id)) - for (let i = 0; i < M.length; i++) if (ids.has(seg.labels[i])) M[i] = 1 - const inv = M.map((v) => 1 - v) as Uint8Array - const dIn = edt(M, W, H), dOut = edt(inv, W, H) - const loops = outs.flatMap((o) => o.loops), sd = new Float32Array(nx * ny), cell = binW / (nx - 1), band = (num('clear') + 3 * cell) * sc + 2 - let exact = 0 - for (let j = 0; j < ny; j++) for (let i = 0; i < nx; i++) { - const X = (cx - binW / 2 + (i * binW) / (nx - 1)) * sc, Y = (cy - binH / 2 + (j * binH) / (ny - 1)) * sc // координаты листа (px), Y вниз - const xi = Math.min(W - 1, Math.max(0, Math.round(X))), yi = Math.min(H - 1, Math.max(0, Math.round(Y))), p = yi * W + xi - const inside = M[p] === 1, d = inside ? dIn[p] : dOut[p] - if (d <= band) { sd[j * nx + i] = signedDistance(loops, X, Y); exact++ } else sd[j * nx + i] = inside ? d : -d - } - log(`поле: ${nx}×${ny} узлов, точное расстояние в ${exact} узлах у края`) - return { key, nx, ny, cell, sd, cx, cy, binW, binH, scale: sc } -} - // ---------- запуск модели и события ---------- /** Второй проход: каждый найденный объект (рамка + поле) прогоняется моделью отдельно, поэтому на объект приходятся все 512 px сети (≈0.3 мм вместо ≈0.6 мм на весь лист). */ async function refine(c: (typeof CANDIDATES)[number], ep: string, base: Float32Array, r: Rectified): Promise { @@ -324,13 +315,13 @@ $('runai').addEventListener('click', async () => { const r = await infer(c, ep, 1, rect.rgb!, rect.image.width, rect.image.height, log) log(`общий проход за ${r.ms.toFixed(0)} мс`); matte = r.plane if (chk('refine')) { const t = performance.now(); matte = await refine(c, ep, matte, rect); log(`второй проход по объектам за ${((performance.now() - t) / 1000).toFixed(1)} с`) } - $('eng').value = 'ai'; fieldCache = null; schedule() + $('eng').value = 'ai'; schedule() } catch (e) { log('ОШИБКА: ' + ((e as Error)?.message ?? e)) } finally { btn.disabled = false } }) $('sample').addEventListener('change', () => { $('file').value = ''; loadPhoto() }) $('file').addEventListener('change', () => loadPhoto()) -for (const id of ['ppaper', 'porient']) $(id).addEventListener('change', () => { rebuildRect(); fieldCache = null; schedule() }) -for (const id of ['margin', 'sens']) $(id).addEventListener('change', () => { rebuildRect(); fieldCache = null; schedule() }) +for (const id of ['ppaper', 'porient']) $(id).addEventListener('change', () => { rebuildRect(); schedule() }) +for (const id of ['margin', 'sens']) $(id).addEventListener('change', () => { rebuildRect(); schedule() }) for (const el of document.querySelectorAll('input[type=range], input[type=number], input[type=checkbox], select')) { if (['file', 'sample', 'ppaper', 'porient', 'margin', 'sens', 'modelsel', 'ep'].includes(el.id) || el.closest('#sils')) continue el.addEventListener('input', () => { if (el.id === 'lightang') setLight(); schedule() }) From c0147969aca896240e166c85ca7d36ae0c57836b Mon Sep 17 00:00:00 2001 From: skarakovski Date: Thu, 22 Oct 2026 09:48:13 +0700 Subject: [PATCH 29/38] poc: Bezier-fitted pockets for both engines; plan: 35-smooth-outline (shared outline stage, v1.6.0) Co-Authored-By: Claude Sonnet 5.5 --- openspec/ROADMAP.md | 1 + .../changes/35-smooth-outline/.openspec.yaml | 2 + openspec/changes/35-smooth-outline/design.md | 23 +++++ .../changes/35-smooth-outline/proposal.md | 29 ++++++ .../specs/smooth-outline/spec.md | 58 ++++++++++++ openspec/changes/35-smooth-outline/tasks.md | 21 +++++ poc/ai/FINDINGS.md | 3 + poc/ai/cutout.html | 5 +- poc/ai/cutout.ts | 56 +++++++++--- poc/ai/outline.ts | 91 +++++++++++++++++++ 10 files changed, 276 insertions(+), 13 deletions(-) create mode 100644 openspec/changes/35-smooth-outline/.openspec.yaml create mode 100644 openspec/changes/35-smooth-outline/design.md create mode 100644 openspec/changes/35-smooth-outline/proposal.md create mode 100644 openspec/changes/35-smooth-outline/specs/smooth-outline/spec.md create mode 100644 openspec/changes/35-smooth-outline/tasks.md diff --git a/openspec/ROADMAP.md b/openspec/ROADMAP.md index c403203..5846403 100644 --- a/openspec/ROADMAP.md +++ b/openspec/ROADMAP.md @@ -44,6 +44,7 @@ | 32 | `32-photo-trace-ai` | 25a, 29 | v1.3.0. Фото-вырезы: альтернативный нейросетевой движок силуэтов (в браузере, веса по согласию, гибридный точный край), классический остаётся по умолчанию как baseline; сравнение движков | | 33 | `33-keychain-ai-silhouette` | 32, 11, 29 | v1.4.0. Keychain Image: силуэт по ИИ (удаление фона на браузерной модели), те же ручки порога и сглаживания, один главный объект, результат хранится вместе с картинкой; яркость остаётся по умолчанию | | 34 | `34-keychain-photo-relief` | 33, 32, 12, 17, 29 | v1.5.0. Keychain Image: рельеф из фото как смесь трёх источников формы (контур, глубина Depth Anything, детали — локальный контраст), кроп области (лицо), силуэт «весь кадр» (плашка), пресеты; результат хранится с картинкой | +| 35 | `35-smooth-outline` | 25a, 29 | v1.6.0. Общий этап контура для вырезов (классика и ИИ) и Keychain: подгонка кубических кривых Безье с минимумом точек, острые углы сохраняются, зазор по кривой; стенки без ряби, детекция не меняется | ```mermaid gantt diff --git a/openspec/changes/35-smooth-outline/.openspec.yaml b/openspec/changes/35-smooth-outline/.openspec.yaml new file mode 100644 index 0000000..7cc41bb --- /dev/null +++ b/openspec/changes/35-smooth-outline/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-10-22 diff --git a/openspec/changes/35-smooth-outline/design.md b/openspec/changes/35-smooth-outline/design.md new file mode 100644 index 0000000..2a8fde3 --- /dev/null +++ b/openspec/changes/35-smooth-outline/design.md @@ -0,0 +1,23 @@ +# Design + +## Context + +`traceSilhouette(seg, id, paper)` blurs the label mask (σ = 1 px at 4 px/mm), traces the 128 iso-line (`traceAlpha`), keeps the largest outer loop and simplifies it (`simplifyLoop`, 0.15 mm). The polygon becomes an `OutlineSource` cutout; `buildCutout` grows it by the clearance with `CrossSection.offset` and extrudes it. The result is a faithful but noisy polygon of about 3000 vertices per tool. + +## Decisions + +- **Fit curves, then sample them.** Schneider's algorithm (the one used by Potrace-like and vector-editor tools): chord-length parametrisation, least-squares cubic with given end tangents, Newton–Raphson reparametrisation, split at the point of the largest error until the error is below the tolerance. Runs between corners; between runs the tangent is not shared (a real corner), inside a run the split point's tangent is shared (G1). A loop without corners is cut in two with central-difference tangents. Tolerance default 0.15 mm (the existing `OUTLINE_TOLERANCE`). +- **Noise first, then fit.** The loop is resampled uniformly (≈ 0.17 mm), smoothed along the curve with corners held fixed (Gaussian σ default 0.25 mm: the one tuned for cutouts), then fitted; the fit tolerance absorbs what is left of the noise. The corner detector (turning angle over a ±3-vertex window, local maxima, default 50°) keeps pointed tips sharp. +- **Sampling.** Each cubic is sampled at about 0.5 mm along its length (uniform in `t`), which for curvature radii above 2 mm gives a chord error below 0.03 mm. The sampled polygon is what goes to the geometry kernel (Manifold needs polygons) and to STL; the curve is an intermediate representation. +- **Offset on the curve.** The clearance is applied to the samples along the analytic normals; at a convex corner (left turn of a counter-clockwise loop) round-join points at 15° steps, at a concave corner a mitre on the bisector (scale limited). Where this would self-intersect (clearance larger than the local radius) the kernel's offset (`CrossSection.offset`) is the fallback, as today. +- **Same stage for every source.** The stage takes a label mask or a soft field, so classic, AI (with the bicubic soft mask and the optional second pass of `32`) and keychain masks all go through it; the baseline detection code is untouched. +- **Opt-out.** "Use the original polyline" skips the fit and smoothing, reproducing the previous polygon exactly (this is also what the regression tests compare against). +- **Stored data.** An outline cutout keeps `{points, size}`; the points are now the sampled fitted polygon. Old sessions have denser polygons and load unchanged. Optionally the control points are stored with the points for later editing (not used yet). +- **Evidence (PoC).** Owner's flash photo, A4: classic outlines 8902 polyline vertices → 248 Béziers / 747 control points / 3214 sampled vertices after offset with round joins; AI 304 Béziers. Walls without ripple at a grazing view; the previous polyline with window-averaged normals still showed ripple. + +## Risks / Trade-offs + +- [Fitting rounds a real small feature] → tolerance 0.15 mm and corner detection; the control is exposed; the polyline option remains. +- [A spike thinner than the smoothing disappears] → corner-preserving smoothing and a smaller σ; checked by a test on a thin spike. +- [Self-intersection of the offset at tight concave features] → fallback to the kernel's offset. +- [Fit time on large outlines] → the fit is O(n) per iteration over a few thousand points per loop, milliseconds; done once per silhouette and cached with the outline. diff --git a/openspec/changes/35-smooth-outline/proposal.md b/openspec/changes/35-smooth-outline/proposal.md new file mode 100644 index 0000000..edac3b4 --- /dev/null +++ b/openspec/changes/35-smooth-outline/proposal.md @@ -0,0 +1,29 @@ +# Proposal + +## Why + +A pocket in Cutout mode (and the outline of a keychain made from an image) is today a polygon of thousands of vertices traced from a pixel mask (`traceSilhouette`: blur, iso-line, simplification by 0.15 mm). The polygon follows every bit of noise of the detected edge, so the printed wall shows a fine vertical ripple, and the model carries thousands of needless vertices. The photo contour is also not "ideal" to begin with: what the user wants is the **shape** of the tool, not every wobble of the detection. + +In the PoC (`poc/ai/`) a pocket built from fitted curves instead of the raw polyline has no ripple, and the same fit works for the classic and the AI silhouettes: on the owner's photo the classic outlines (8902 polyline vertices) become 248 cubic Bézier segments (747 control points), the AI outlines 304. + +## What Changes + +- **One shared outline stage for every source of silhouette.** After the silhouette is found (classic detection, AI mask, or a keychain mask), the outline is traced, smoothed along the curve (sharp corners kept) and **fitted with cubic Bézier curves** (Schneider's algorithm) to a tolerance (default 0.15 mm), corners detected by the turning angle (default 50°) staying real corners with G1-continuous curves in between. +- **Compact and smooth geometry.** The fitted curves are sampled into a polygon with a chord error well below the tolerance (a step of about 0.5 mm along the curve), so the model gets a few hundred vertices instead of thousands and walls without ripple. Clearance offsets are made on the curve (round joins at convex corners, mitre at concave ones). +- **Applies to both engines.** The classic detection (the baseline) is not changed: only the stage after it, so the baseline silhouettes stay identical and its outline gets smoother. The AI engine of `32` uses the same stage. +- **Controls.** "Outline smoothness" (σ, default 0.25 mm) and "Fit tolerance" (default 0.15 mm) under the silhouette step, with the vertex count shown; "Use the original polyline" as an option that restores the previous behaviour. +- **Keychain.** The image and shape silhouettes of Keychain use the same stage for their outline (see `34`). + +## Capabilities + +### New Capabilities +- `smooth-outline`: fitting of smooth curves with few points to silhouette outlines, shared by Cutout (both engines) and Keychain. + +### Modified Capabilities + +## Impact + +- New pure module `geometry/outlineFit.ts` (resample, corner detection, along-curve smoothing, Schneider fitting, sampling, offset), used by `traceSilhouette` callers in `features/cutout/photo/createCutouts.ts` and by the keychain outline; `segment.ts`, `colour.ts` and the classic detection are not edited, their frozen-output test (`32`, task 2.1) stays green; the `OutlineSource` points of a cutout become the sampled fitted polygon (the stored `points` keep their shape, so saved sessions load unchanged); strings in en and ru, tests. +- Out of scope: changing the classic silhouette detection, editing the outline by hand (node dragging), exporting curves as such (STL and 3MF stay polygonal), smoothing of mesh cutouts (STL imports). +- Version 1.6.0 (minor: a new option that also improves existing output). +- Depends on: `25a-photo-trace-improvements`, `29-beta-label`; works with `32` and `34` (they use it) but does not require them. diff --git a/openspec/changes/35-smooth-outline/specs/smooth-outline/spec.md b/openspec/changes/35-smooth-outline/specs/smooth-outline/spec.md new file mode 100644 index 0000000..65dfcd6 --- /dev/null +++ b/openspec/changes/35-smooth-outline/specs/smooth-outline/spec.md @@ -0,0 +1,58 @@ +# Spec Delta + +## Purpose +Defines the shared stage that turns a detected silhouette into a smooth outline with few points, used by Cutout with either detection engine and by Keychain. + +## ADDED Requirements + +### Requirement: Smooth outline from any silhouette +The outline made from a silhouette, whether found by the classic detection, by the AI engine or from a keychain image or shape, SHALL be smooth: it SHALL be fitted with smooth curves instead of following every pixel and every wobble of the detected edge, and SHALL be turned into a polygon with a small number of vertices. Sharp corners of the subject, such as the tip of a tool, SHALL stay sharp, and the curves between corners SHALL be continuous in direction. The user SHALL be able to set the smoothness and the fit tolerance and to choose to keep the original polyline. + +#### Scenario: Smooth walls +- **WHEN** a pocket is made from a detected tool and viewed along its wall +- **THEN** the wall shows no ripple that follows the pixels or the noise of the detected edge + +#### Scenario: Fewer points +- **WHEN** the outline of a tool is made with the default tolerance +- **THEN** its polygon has at least five times fewer vertices than the original polyline, and no point of it is farther than the tolerance from the original polyline + +#### Scenario: Sharp tips +- **WHEN** a tool has a pointed tip +- **THEN** the tip stays pointed and its position is within the tolerance of the original + +#### Scenario: Both engines +- **WHEN** the same photo is processed by the classic and the AI engine +- **THEN** both outlines go through the same stage and both come out smooth + +#### Scenario: Original polyline +- **WHEN** the user chooses to keep the original polyline +- **THEN** the outline is the same as before this feature + +### Requirement: Clearance on the smooth outline +The clearance of a cutout SHALL be applied to the smooth outline so that the pocket stays smooth: rounded at convex corners, mitred at concave ones, and never smaller than the outline plus the clearance. + +#### Scenario: Convex corner +- **WHEN** the clearance is 0.8 mm and the outline has a convex tip +- **THEN** the pocket around the tip is rounded with a radius of the clearance + +#### Scenario: Containment +- **WHEN** a pocket is made with a clearance +- **THEN** the tool's outline lies inside the pocket everywhere + +### Requirement: Detection unchanged +Adding the smooth outline SHALL NOT change which silhouettes the classic detection finds, their sizes or their numbering; only the polygon made from each of them. + +#### Scenario: Same silhouettes +- **WHEN** the reference photos are processed +- **THEN** the number, sizes and areas of the silhouettes are identical to those before this feature + +### Requirement: Compatibility and language +Sessions saved before this feature SHALL load unchanged. All text of this feature SHALL be available in every language of the app. + +#### Scenario: Old session +- **WHEN** a session with outline cutouts saved before this feature is loaded +- **THEN** the cutouts are as they were + +#### Scenario: Russian +- **WHEN** the interface language is Russian +- **THEN** the smoothness, tolerance and polyline option are in Russian diff --git a/openspec/changes/35-smooth-outline/tasks.md b/openspec/changes/35-smooth-outline/tasks.md new file mode 100644 index 0000000..ca6062a --- /dev/null +++ b/openspec/changes/35-smooth-outline/tasks.md @@ -0,0 +1,21 @@ +# Tasks + +## 1. Geometry (React-free) + +- [ ] 1.1 `geometry/outlineFit.ts`: `resample`, `findCorners`, `smoothLoop` (Gaussian along the loop with corners fixed), `fitLoop` (Schneider: chord-length parametrisation, least squares, reparametrisation, split) and `sampleCurves`. Tests: the fit of a circle uses few segments and stays within the tolerance, of a square keeps four corners, of a noisy circle absorbs the noise within the tolerance, a thin spike keeps its tip within the tolerance, a loop without corners is closed with continuous direction, the number of vertices drops at least five times on a traced tool outline fixture and the maximum distance to the original stays below the tolerance. +- [ ] 1.2 `offsetCurve(samples, d)`: the clearance along the analytic normals, round join at convex corners, mitre at concave ones, fallback to the kernel's offset when the result self-intersects. Tests: the offset of a circle is a circle of radius + d, a convex tip is rounded with radius d, the tool's outline lies inside the offset everywhere, a tight concave notch falls back without a crash. +- [ ] 1.3 Use in `createCutouts` (`tracedCutouts`) with `traceSilhouette` kept as it is: the traced loop goes through the fit, `OutlineSource.points` is the sampled polygon, an option keeps the original polyline. Tests: with the polyline option the existing outline tests are unchanged; with the fit the vertex count drops and the polygon stays within the tolerance of the original; classic silhouettes of both fixtures are identical to the frozen baseline (`32` task 2.1); the AI engine of `32` goes through the same path. +- [ ] 1.4 Keychain outline (silhouette and shape plates) through the same stage as in `34`. Tests: the existing keychain regression volumes stay within their tolerances; the outline has fewer vertices and no ripple. + +## 2. UI + +- [ ] 2.1 Outline smoothness and fit tolerance under the silhouette step of the photo dialog (lengths, so `LengthField`), the vertex count, the option "original polyline"; strings in `en.json` and `ru.json`. No artboard exists: built from `ui/` components and `theme.css` tokens, said in the commit; compare in en and ru and at 390 px. Component tests: controls update the outline and the count, the polyline option restores the previous result, steppers on every numeric input, key parity. + +## 3. Version + +- [ ] 3.1 Bump to 1.6.0: `package.json` and a `CHANGELOG.md` entry `1.6.0 — 35-smooth-outline`. Test: the existing version test passes with the new number. + +## 4. Verification + +- [ ] 4.1 `npm test` and `npm run build` are green, no console errors; the classic detection output on both fixtures is unchanged. +- [ ] 4.2 In the browser, en and ru and at 390 px, with the owner's two photos and both engines: pockets show smooth walls at a grazing view in the 3D preview, the vertex count is several times lower, tips stay pointed, the clearance gives rounded convex corners, the exported STL of the bin is one valid solid, saved old sessions load unchanged. diff --git a/poc/ai/FINDINGS.md b/poc/ai/FINDINGS.md index 7de4c2e..568236d 100644 --- a/poc/ai/FINDINGS.md +++ b/poc/ai/FINDINGS.md @@ -111,3 +111,6 @@ ### Рябь на стенках кармана в 3D PoC вырезов (исправлено) Вертикальные полосы на стенках появлялись даже у классики и были артефактом построения 3D в PoC, а не контура: стенки собирались из точек пересечения с узлами сетки (0.7 мм) и имели плоские нормали по граням. Теперь, как в приложении (контур → смещение на зазор → выдавливание), стенка кармана строится вдоль самого полигона, верх — прямоугольник с отверстиями (`ShapeUtils.triangulateShape`), дно кармана — контур, а нормали вершин усредняются по окну ≈ 1 мм вдоль контура (поле «окно нормалей стенки»), чтобы шум контура в десятки мкм не давал полос. Число треугольников: 263 тыс. → 36 тыс. + +### Кривые Безье вместо полилинии (cutout.html) +Подгонка кубических кривых (Шнейдер) к контуру, допуск 0.15 мм, углы по повороту 50°, выборка кривой ≈ 0.5 мм, зазор по аналитическим нормалям (дуга на выпуклых углах, митра на вогнутых). Фото flash: классика 248 кривых (747 контрольных точек) вместо 8902 вершин полилинии, ИИ 304 кривые; в модели ≈ 3.2 тыс. вершин, 13 тыс. треугольников. Работает для обоих движков одинаково: этап стоит после детекции. Оформлено как change `35-smooth-outline`. diff --git a/poc/ai/cutout.html b/poc/ai/cutout.html index e7871a3..05114f6 100644 --- a/poc/ai/cutout.html +++ b/poc/ai/cutout.html @@ -51,7 +51,10 @@

    3Контур (как в keychain)

    - + + + +

    4Вырез и корпус

    diff --git a/poc/ai/cutout.ts b/poc/ai/cutout.ts index da52cff..6ed5ff6 100644 --- a/poc/ai/cutout.ts +++ b/poc/ai/cutout.ts @@ -15,7 +15,7 @@ import { gaussianBlur, MIN_SIGMA } from '../../src/geometry/raster/blur' import { fillHoles, labelComponents, removeSpecks } from '../../src/geometry/raster/components' import { backendsOf, CANDIDATES } from './models' import { infer } from './engine' -import { signedDistance, vectorOutline, type Pt } from './outline' +import { bezTan, findCorners, fitLoop, signedDistance, tessellate, vectorOutline, type Bez, type Pt } from './outline' const $ = (id: string) => document.getElementById(id) as T const num = (id: string) => +$(id).value @@ -248,10 +248,45 @@ function build() { const zTop = num('blockh'), zFloor = Math.max(1, zTop - num('depth')), clearMm = num('clear') const toMm = (l: Pt[]): [number, number][] => l.map(([x, y]) => [x / sc - cx, -(y / sc - cy)]) const area = (l: [number, number][]) => l.reduce((a, [x, y], i) => { const [x2, y2] = l[(i + 1) % l.length]; return a + (x * y2 - x2 * y) / 2 }, 0) - const pockets = outs.map((o) => { + interface Pocket { pts: [number, number][]; nrm: [number, number][] } // вершины (мм) и единичные нормали наружу кармана + let fitInfo = { segs: 0, ctrl: 0 } + const pockets: Pocket[] = outs.map((o) => { const outer = o.loops.reduce((best, l) => (Math.abs(area(toMm(l))) > Math.abs(area(toMm(best))) ? l : best), o.loops[0]) let poly = toMm(outer); if (area(poly) < 0) poly = poly.reverse() // против часовой стрелки: наружу — вправо от хода - return offsetLoop(poly, clearMm) + if (!chk('fit')) { // старый путь: полилиния со шумом, нормали усредняются по окну + const pts = offsetLoop(poly, clearMm), n = pts.length, perim = pts.reduce((a, q, i) => a + Math.hypot(pts[(i + 1) % n][0] - q[0], pts[(i + 1) % n][1] - q[1]), 0), k = Math.max(1, Math.round((num('nwin') / perim) * n)) + const en = pts.map((q, i) => { const r = pts[(i + 1) % n], dx = r[0] - q[0], dy = r[1] - q[1], l = Math.hypot(dx, dy) || 1; return [dy / l, -dx / l] as [number, number] }) + const nrm = pts.map((_, i) => { let x = 0, y = 0; for (let d = -k; d <= k; d++) { const e = en[(((i + d) % n) + n) % n], w = 1 - Math.abs(d) / (k + 1); x += w * e[0]; y += w * e[1] } const l = Math.hypot(x, y) || 1; return [x / l, y / l] as [number, number] }) + fitInfo.segs += n; fitInfo.ctrl += n + return { pts, nrm } + } + // гладкие кривые Безье: минимум точек, нормали аналитические; углы остаются углами, на выпуклых углах зазор даёт дугу, на вогнутых — митру + const { segs } = fitLoop(poly, findCorners(poly, 3, num('cang')), num('ftol')) + fitInfo.segs += segs.length; fitInfo.ctrl += 3 * segs.length + 1 + const samples = tessellate(segs, num('fstep')), pts: [number, number][] = [], nrm: [number, number][] = [] + const push = (p: [number, number], nv: [number, number]) => { pts.push([p[0] + nv[0] * clearMm, p[1] + nv[1] * clearMm]); nrm.push(nv) } + let si = -1 + for (let i = 0; i < samples.length; i++) { + const sm = samples[i] + if (sm.seg !== si) { // начало нового сегмента: стык с предыдущим + if (si >= 0 && clearMm > 0) { + const c = segs[si] as Bez, nEnd = (() => { const t = bezTan(c, 1); return [t[1], -t[0]] as [number, number] })(), nS = sm.n, cr = nEnd[1] * 0 // выпуклость по знаку векторного произведения касательных + const tE = bezTan(c, 1), tS: [number, number] = [-sm.n[1], sm.n[0]], cross = tE[0] * tS[1] - tE[1] * tS[0], dot = nEnd[0] * nS[0] + nEnd[1] * nS[1] + void cr + if (dot < 0.9986) { // угол > ~3° + const P = c[3] + if (cross > 0) { // выпуклый угол: дуга радиуса зазора от нормали к нормали + const a0 = Math.atan2(nEnd[1], nEnd[0]); let da = Math.atan2(nS[1], nS[0]) - a0; while (da < 0) da += 2 * Math.PI + const steps = Math.max(1, Math.ceil(da / (Math.PI / 12))) + for (let t = 1; t < steps; t++) { const a = a0 + (da * t) / steps, nv: [number, number] = [Math.cos(a), Math.sin(a)]; push(P, nv) } + } else { const bx = nEnd[0] + nS[0], by = nEnd[1] + nS[1], bl = Math.hypot(bx, by) || 1, k = clearMm / Math.max(0.4, bl / 2); pts.push([P[0] + (bx / bl) * k, P[1] + (by / bl) * k]); nrm.push([bx / bl, by / bl]) } + } + } + si = sm.seg + } + push(sm.p, sm.n) + } + return { pts, nrm } }) const hx = binW / 2, hy = binH / 2, pos: number[] = [], nor: number[] = [] const tri = (a: number[], b: number[], c: number[], n: number[]) => { pos.push(...a, ...b, ...c); for (let t = 0; t < 3; t++) nor.push(...n) } @@ -263,18 +298,15 @@ function build() { flip ? tri(P(A), P(C), P(B), [0, 0, up ? 1 : -1]) : tri(P(A), P(B), P(C), [0, 0, up ? 1 : -1]) } } - flat([[-hx, -hy], [hx, -hy], [hx, hy], [-hx, hy]], pockets, zTop, true) // верх с отверстиями - for (const pk of pockets) flat(pk, [], zFloor, true) // дно карманов + flat([[-hx, -hy], [hx, -hy], [hx, hy], [-hx, hy]], pockets.map((q) => q.pts), zTop, true) // верх с отверстиями + for (const pk of pockets) flat(pk.pts, [], zFloor, true) // дно карманов flat([[-hx, -hy], [hx, -hy], [hx, hy], [-hx, hy]], [], 0, false) // дно корпуса const quad = (a: number[], b: number[], c: number[], d: number[], na: number[], nb: number[]) => { // a,b снизу; c,d сверху (c над b, d над a) pos.push(...a, ...b, ...d, ...b, ...c, ...d); nor.push(...na, ...nb, ...na, ...nb, ...nb, ...na) } - for (const pk of pockets) { // стенки кармана: нормали в сторону кармана, сглаженные по вершинам - // нормаль вершины — среднее нормалей рёбер в окне ±nw мм вдоль контура: шум в десятки мкм не даёт полос на стенке - const n = pk.length, perim = pk.reduce((a, q, i) => a + Math.hypot(pk[(i + 1) % n][0] - q[0], pk[(i + 1) % n][1] - q[1]), 0), k = Math.max(1, Math.round((num('nwin') / perim) * n)) - const en = pk.map((q, i) => { const r = pk[(i + 1) % n], dx = r[0] - q[0], dy = r[1] - q[1], l = Math.hypot(dx, dy) || 1; return [dy / l, -dx / l] }) // наружу кармана (контур против часовой) - const vn = pk.map((_, i) => { let x = 0, y = 0; for (let d = -k; d <= k; d++) { const e = en[(((i + d) % n) + n) % n], w = 1 - Math.abs(d) / (k + 1); x += w * e[0]; y += w * e[1] } const l = Math.hypot(x, y) || 1; return [-x / l, -y / l, 0] }) - for (let i = 0; i < n; i++) { const a = pk[i], b = pk[(i + 1) % n]; quad([a[0], a[1], zFloor], [b[0], b[1], zFloor], [b[0], b[1], zTop], [a[0], a[1], zTop], vn[i], vn[(i + 1) % n]) } + for (const pk of pockets) { // стенки кармана: нормали в сторону кармана (аналитические или усреднённые) + const n = pk.pts.length, vn = pk.nrm.map((q) => [-q[0], -q[1], 0]) + for (let i = 0; i < n; i++) { const a = pk.pts[i], b = pk.pts[(i + 1) % n]; quad([a[0], a[1], zFloor], [b[0], b[1], zFloor], [b[0], b[1], zTop], [a[0], a[1], zTop], vn[i], vn[(i + 1) % n]) } } const sideN = [[0, -1, 0], [1, 0, 0], [0, 1, 0], [-1, 0, 0]], corners: [number, number][] = [[-hx, -hy], [hx, -hy], [hx, hy], [-hx, hy]] for (let i = 0; i < 4; i++) { const a = corners[i], b = corners[(i + 1) % 4]; quad([b[0], b[1], 0], [a[0], a[1], 0], [a[0], a[1], zTop], [b[0], b[1], zTop], sideN[i], sideN[i]) } @@ -286,7 +318,7 @@ function build() { const g2 = new THREE.ExtrudeGeometry(sh, { depth: Math.max(0.5, num('depth') - 0.6), bevelEnabled: false }); g2.translate(0, 0, zFloor + 0.05) group.add(new THREE.Mesh(g2, toolMat)) } - $('stat3d').textContent = `${pockets.length} кармана, ${pockets.reduce((a, p) => a + p.length, 0)} вершин контура, ${(pos.length / 9 / 1000).toFixed(0)} тыс. треуг.; глубина ${(zTop - zFloor).toFixed(0)} мм, зазор ${clearMm} мм` + $('stat3d').textContent = `${pockets.length} кармана; ${chk('fit') ? `кривых Безье ${fitInfo.segs} (контрольных точек ${fitInfo.ctrl})` : `полилиния ${fitInfo.segs} вершин`}; вершин в модели ${pockets.reduce((a, p) => a + p.pts.length, 0)}, ${(pos.length / 9 / 1000).toFixed(0)} тыс. треуг.; глубина ${(zTop - zFloor).toFixed(0)} мм, зазор ${clearMm} мм` const sig = `${binW}|${binH}`; if (sig !== fitted) { fitted = sig; frame(Math.max(binW, binH), 'iso') } } diff --git a/poc/ai/outline.ts b/poc/ai/outline.ts index f892765..899480c 100644 --- a/poc/ai/outline.ts +++ b/poc/ai/outline.ts @@ -91,3 +91,94 @@ export function signedDistance(loops: Pt[][], x: number, y: number): number { } return cross & 1 ? best : -best } + +// ---------- подгонка кубических кривых Безье (Шнейдер, «Graphics Gems»): минимум точек, гладкие стенки ---------- +export type V2 = [number, number] +export type Bez = [V2, V2, V2, V2] +const vadd = (a: V2, b: V2): V2 => [a[0] + b[0], a[1] + b[1]] +const vsub = (a: V2, b: V2): V2 => [a[0] - b[0], a[1] - b[1]] +const vmul = (a: V2, s: number): V2 => [a[0] * s, a[1] * s] +const vdot = (a: V2, b: V2) => a[0] * b[0] + a[1] * b[1] +const vlen = (a: V2) => Math.hypot(a[0], a[1]) +const vnorm = (a: V2): V2 => { const l = vlen(a) || 1; return [a[0] / l, a[1] / l] } +const B0 = (u: number) => (1 - u) ** 3, B1 = (u: number) => 3 * u * (1 - u) ** 2, B2 = (u: number) => 3 * u * u * (1 - u), B3 = (u: number) => u ** 3 +export const bezAt = (c: Bez, t: number): V2 => [B0(t) * c[0][0] + B1(t) * c[1][0] + B2(t) * c[2][0] + B3(t) * c[3][0], B0(t) * c[0][1] + B1(t) * c[1][1] + B2(t) * c[2][1] + B3(t) * c[3][1]] +export const bezTan = (c: Bez, t: number): V2 => { + const a = vmul(vsub(c[1], c[0]), 3 * (1 - t) ** 2), b = vmul(vsub(c[2], c[1]), 6 * (1 - t) * t), d = vmul(vsub(c[3], c[2]), 3 * t * t) + const r = vadd(vadd(a, b), d); return vlen(r) < 1e-9 ? vnorm(vsub(c[3], c[0])) : vnorm(r) +} +const bezD2 = (c: Bez, t: number): V2 => { + const a = vmul(vadd(vsub(c[2], vmul(c[1], 2)), c[0]), 6 * (1 - t)), b = vmul(vadd(vsub(c[3], vmul(c[2], 2)), c[1]), 6 * t); return vadd(a, b) +} +const bezD1 = (c: Bez, t: number): V2 => vadd(vadd(vmul(vsub(c[1], c[0]), 3 * (1 - t) ** 2), vmul(vsub(c[2], c[1]), 6 * (1 - t) * t)), vmul(vsub(c[3], c[2]), 3 * t * t)) + +function chordParam(d: V2[], a: number, b: number): number[] { + const u = [0]; for (let i = a + 1; i <= b; i++) u.push(u[u.length - 1] + vlen(vsub(d[i], d[i - 1]))) + const L = u[u.length - 1] || 1; return u.map((x) => x / L) +} +function genBezier(d: V2[], a: number, b: number, u: number[], t1: V2, t2: V2): Bez { + let c00 = 0, c01 = 0, c11 = 0, x0 = 0, x1 = 0 + for (let i = 0; i < u.length; i++) { + const A0 = vmul(t1, B1(u[i])), A1 = vmul(t2, B2(u[i])) + c00 += vdot(A0, A0); c01 += vdot(A0, A1); c11 += vdot(A1, A1) + const tmp = vsub(d[a + i], vadd(vadd(vmul(d[a], B0(u[i]) + B1(u[i])), vmul(d[b], B2(u[i]) + B3(u[i]))), [0, 0])) + x0 += vdot(A0, tmp); x1 += vdot(A1, tmp) + } + const det = c00 * c11 - c01 * c01, al = det === 0 ? 0 : (x0 * c11 - x1 * c01) / det, ar = det === 0 ? 0 : (c00 * x1 - c01 * x0) / det + const seg = vlen(vsub(d[b], d[a])), eps = 1e-6 * seg + if (al < eps || ar < eps) { const k = seg / 3; return [d[a], vadd(d[a], vmul(t1, k)), vadd(d[b], vmul(t2, k)), d[b]] } + return [d[a], vadd(d[a], vmul(t1, al)), vadd(d[b], vmul(t2, ar)), d[b]] +} +function maxErr(d: V2[], a: number, b: number, c: Bez, u: number[]): { e: number; split: number } { + let e = 0, split = Math.floor((a + b) / 2) + for (let i = 1; i < u.length - 1; i++) { const p = bezAt(c, u[i]), dd = vsub(p, d[a + i]), s = vdot(dd, dd); if (s >= e) { e = s; split = a + i } } + return { e, split } +} +function reparam(d: V2[], a: number, c: Bez, u: number[]): number[] { + return u.map((ui, i) => { + const p = bezAt(c, ui), d1 = bezD1(c, ui), d2 = bezD2(c, ui), diff = vsub(p, d[a + i]), num = vdot(diff, d1), den = vdot(d1, d1) + vdot(diff, d2) + return den === 0 ? ui : Math.min(1, Math.max(0, ui - num / den)) + }) +} +function fitCubic(d: V2[], a: number, b: number, t1: V2, t2: V2, err: number, out: Bez[]) { + if (b - a === 1) { const k = vlen(vsub(d[b], d[a])) / 3; out.push([d[a], vadd(d[a], vmul(t1, k)), vadd(d[b], vmul(t2, k)), d[b]]); return } + let u = chordParam(d, a, b), c = genBezier(d, a, b, u, t1, t2), { e, split } = maxErr(d, a, b, c, u) + if (e < err * err) { out.push(c); return } + if (e < err * err * 16) for (let k = 0; k < 6; k++) { + u = reparam(d, a, c, u); c = genBezier(d, a, b, u, t1, t2); const r = maxErr(d, a, b, c, u); e = r.e; split = r.split + if (e < err * err) { out.push(c); return } + } + const tc = vnorm(vsub(d[split - 1], d[split + 1])) + fitCubic(d, a, split, t1, tc, err, out); fitCubic(d, split, b, vmul(tc, -1), t2, err, out) +} + +/** Замкнутый контур (точки в мм) → цепочка кубических Безье с допуском tol; в острых углах (по `corner`) разрыв касательной, между ними G1. */ +export function fitLoop(pts: V2[], corner: boolean[], tol: number): { segs: Bez[]; cornerAt: boolean[] } { + const n = pts.length, ids = corner.map((c, i) => (c ? i : -1)).filter((i) => i >= 0) + const w = 3, tan = (i: number): V2 => vnorm(vsub(pts[(i + w) % n], pts[(i - w + n) % n])) + const cuts = ids.length ? ids : [0, Math.floor(n / 2)] + const segs: Bez[] = [], cornerAt: boolean[] = [] + for (let c = 0; c < cuts.length; c++) { + const a = cuts[c], b = cuts[(c + 1) % cuts.length], run: V2[] = [] + for (let i = a; ; i = (i + 1) % n) { run.push(pts[i]); if (i === b && run.length > 1) break; if (run.length > n + 1) break } + if (run.length < 2) continue + const m = run.length, k = Math.min(w, m - 1) + const isCorner = ids.length > 0 + const t1 = isCorner ? vnorm(vsub(run[k], run[0])) : tan(a), t2 = isCorner ? vnorm(vsub(run[m - 1 - k], run[m - 1])) : vmul(tan(b), -1) + const before = segs.length + fitCubic(run, 0, m - 1, t1, t2, tol, segs) + for (let s = before; s < segs.length; s++) cornerAt.push(s === before && isCorner) + } + return { segs, cornerAt } +} + +/** Выборка кривых с шагом ≈ step мм: точки и единичные нормали наружу (контур против часовой). cornerAt — начала сегментов с разрывом нормали. */ +export function tessellate(segs: Bez[], step: number): { p: V2; n: V2; start: boolean; seg: number }[] { + const out: { p: V2; n: V2; start: boolean; seg: number }[] = [] + segs.forEach((c, si) => { + let L = 0, prev = c[0]; for (let i = 1; i <= 16; i++) { const q = bezAt(c, i / 16); L += vlen(vsub(q, prev)); prev = q } + const n = Math.max(2, Math.ceil(L / step)) + for (let i = 0; i < n; i++) { const t = i / n, tg = bezTan(c, t); out.push({ p: bezAt(c, t), n: [tg[1], -tg[0]], start: i === 0, seg: si }) } + }) + return out +} From 442d77594d2d8237cbf99ebcf4b78cdc105bdecb Mon Sep 17 00:00:00 2001 From: skarakovski Date: Thu, 22 Oct 2026 09:58:15 +0700 Subject: [PATCH 30/38] poc: Bezier-fitted outline in keychain (analytic wall normals), defaults tolerance 0.25 / step 1 mm; plan 35 updated Co-Authored-By: Claude Sonnet 5.5 --- openspec/changes/35-smooth-outline/design.md | 5 ++- .../changes/35-smooth-outline/proposal.md | 6 +-- poc/ai/FINDINGS.md | 3 ++ poc/ai/cutout.html | 4 +- poc/ai/index.html | 3 ++ poc/ai/keychain.ts | 44 ++++++++++++++++--- 6 files changed, 52 insertions(+), 13 deletions(-) diff --git a/openspec/changes/35-smooth-outline/design.md b/openspec/changes/35-smooth-outline/design.md index 2a8fde3..d51a9f4 100644 --- a/openspec/changes/35-smooth-outline/design.md +++ b/openspec/changes/35-smooth-outline/design.md @@ -6,13 +6,14 @@ ## Decisions -- **Fit curves, then sample them.** Schneider's algorithm (the one used by Potrace-like and vector-editor tools): chord-length parametrisation, least-squares cubic with given end tangents, Newton–Raphson reparametrisation, split at the point of the largest error until the error is below the tolerance. Runs between corners; between runs the tangent is not shared (a real corner), inside a run the split point's tangent is shared (G1). A loop without corners is cut in two with central-difference tangents. Tolerance default 0.15 mm (the existing `OUTLINE_TOLERANCE`). +- **Fit curves, then sample them.** Schneider's algorithm (the one used by Potrace-like and vector-editor tools): chord-length parametrisation, least-squares cubic with given end tangents, Newton–Raphson reparametrisation, split at the point of the largest error until the error is below the tolerance. Runs between corners; between runs the tangent is not shared (a real corner), inside a run the split point's tangent is shared (G1). A loop without corners is cut in two with central-difference tangents. Tolerance default **0.25 mm** and sampling step **1 mm**, the values the owner judged best in the PoC (0.15 mm and 0.5 mm kept a faint ripple on a screwdriver neck; 0.25 and 1 gave the smoothest walls); the previous `OUTLINE_TOLERANCE` of 0.15 mm applies only to the "original polyline" option. - **Noise first, then fit.** The loop is resampled uniformly (≈ 0.17 mm), smoothed along the curve with corners held fixed (Gaussian σ default 0.25 mm: the one tuned for cutouts), then fitted; the fit tolerance absorbs what is left of the noise. The corner detector (turning angle over a ±3-vertex window, local maxima, default 50°) keeps pointed tips sharp. -- **Sampling.** Each cubic is sampled at about 0.5 mm along its length (uniform in `t`), which for curvature radii above 2 mm gives a chord error below 0.03 mm. The sampled polygon is what goes to the geometry kernel (Manifold needs polygons) and to STL; the curve is an intermediate representation. +- **Sampling.** Each cubic is sampled at about 1 mm along its length (uniform in `t`); the chord error is `step² / (8 R)`, below 0.06 mm for curvature radii above 2 mm, inside the 0.25 mm fit tolerance. Where the radius is smaller the sampling is refined (step ≤ `sqrt(8 R · 0.03)`). The sampled polygon is what goes to the geometry kernel (Manifold needs polygons) and to STL; the curve is an intermediate representation. - **Offset on the curve.** The clearance is applied to the samples along the analytic normals; at a convex corner (left turn of a counter-clockwise loop) round-join points at 15° steps, at a concave corner a mitre on the bisector (scale limited). Where this would self-intersect (clearance larger than the local radius) the kernel's offset (`CrossSection.offset`) is the fallback, as today. - **Same stage for every source.** The stage takes a label mask or a soft field, so classic, AI (with the bicubic soft mask and the optional second pass of `32`) and keychain masks all go through it; the baseline detection code is untouched. - **Opt-out.** "Use the original polyline" skips the fit and smoothing, reproducing the previous polygon exactly (this is also what the regression tests compare against). - **Stored data.** An outline cutout keeps `{points, size}`; the points are now the sampled fitted polygon. Old sessions have denser polygons and load unchanged. Optionally the control points are stored with the points for later editing (not used yet). +- **Keychain.** The same stage gives the outline of an image silhouette in Keychain: in the PoC the drawing `check` outline (3475 polyline vertices) became 40 Bézier segments (121 control points) and 262 vertices in the model, with wall normals taken analytically from the curve, so the wall shows no ripple either. The top surface of the keychain stays a height field cut along the fitted outline. - **Evidence (PoC).** Owner's flash photo, A4: classic outlines 8902 polyline vertices → 248 Béziers / 747 control points / 3214 sampled vertices after offset with round joins; AI 304 Béziers. Walls without ripple at a grazing view; the previous polyline with window-averaged normals still showed ripple. ## Risks / Trade-offs diff --git a/openspec/changes/35-smooth-outline/proposal.md b/openspec/changes/35-smooth-outline/proposal.md index edac3b4..adb2de1 100644 --- a/openspec/changes/35-smooth-outline/proposal.md +++ b/openspec/changes/35-smooth-outline/proposal.md @@ -8,10 +8,10 @@ In the PoC (`poc/ai/`) a pocket built from fitted curves instead of the raw poly ## What Changes -- **One shared outline stage for every source of silhouette.** After the silhouette is found (classic detection, AI mask, or a keychain mask), the outline is traced, smoothed along the curve (sharp corners kept) and **fitted with cubic Bézier curves** (Schneider's algorithm) to a tolerance (default 0.15 mm), corners detected by the turning angle (default 50°) staying real corners with G1-continuous curves in between. -- **Compact and smooth geometry.** The fitted curves are sampled into a polygon with a chord error well below the tolerance (a step of about 0.5 mm along the curve), so the model gets a few hundred vertices instead of thousands and walls without ripple. Clearance offsets are made on the curve (round joins at convex corners, mitre at concave ones). +- **One shared outline stage for every source of silhouette.** After the silhouette is found (classic detection, AI mask, or a keychain mask), the outline is traced, smoothed along the curve (sharp corners kept) and **fitted with cubic Bézier curves** (Schneider's algorithm) to a tolerance (default 0.25 mm), corners detected by the turning angle (default 50°) staying real corners with G1-continuous curves in between. +- **Compact and smooth geometry.** The fitted curves are sampled into a polygon with a chord error well below the tolerance (a step of about 1 mm along the curve), so the model gets a few hundred vertices instead of thousands and walls without ripple. Clearance offsets are made on the curve (round joins at convex corners, mitre at concave ones). - **Applies to both engines.** The classic detection (the baseline) is not changed: only the stage after it, so the baseline silhouettes stay identical and its outline gets smoother. The AI engine of `32` uses the same stage. -- **Controls.** "Outline smoothness" (σ, default 0.25 mm) and "Fit tolerance" (default 0.15 mm) under the silhouette step, with the vertex count shown; "Use the original polyline" as an option that restores the previous behaviour. +- **Controls.** "Outline smoothness" (σ, default 0.25 mm) and "Fit tolerance" (default 0.25 mm) and "Sampling step" (default 1 mm) under the silhouette step, with the vertex count shown; "Use the original polyline" as an option that restores the previous behaviour. - **Keychain.** The image and shape silhouettes of Keychain use the same stage for their outline (see `34`). ## Capabilities diff --git a/poc/ai/FINDINGS.md b/poc/ai/FINDINGS.md index 568236d..7a6947a 100644 --- a/poc/ai/FINDINGS.md +++ b/poc/ai/FINDINGS.md @@ -114,3 +114,6 @@ ### Кривые Безье вместо полилинии (cutout.html) Подгонка кубических кривых (Шнейдер) к контуру, допуск 0.15 мм, углы по повороту 50°, выборка кривой ≈ 0.5 мм, зазор по аналитическим нормалям (дуга на выпуклых углах, митра на вогнутых). Фото flash: классика 248 кривых (747 контрольных точек) вместо 8902 вершин полилинии, ИИ 304 кривые; в модели ≈ 3.2 тыс. вершин, 13 тыс. треугольников. Работает для обоих движков одинаково: этап стоит после детекции. Оформлено как change `35-smooth-outline`. + +### Допуск 0.25 мм и шаг 1 мм; контур Keychain +Владелец: допуск 0.25 и шаг 1 дают лучший результат (меньше всего ряби на шейке отвёртки) → значения по умолчанию в PoC вырезов и Keychain и в плане `35`. Контур Keychain в PoC (`index.html`) теперь тоже через кривые Безье: рисунок `check`: 3475 вершин полилинии → 40 кривых (121 контрольная точка), 262 вершины в модели; нормали стенки аналитические, поверхность остаётся полем высот, обрезанным по подогнанному контуру. diff --git a/poc/ai/cutout.html b/poc/ai/cutout.html index 05114f6..c28b057 100644 --- a/poc/ai/cutout.html +++ b/poc/ai/cutout.html @@ -52,8 +52,8 @@ - - + +
    diff --git a/poc/ai/index.html b/poc/ai/index.html index 6f997b0..240c385 100644 --- a/poc/ai/index.html +++ b/poc/ai/index.html @@ -50,6 +50,9 @@ + + + diff --git a/poc/ai/keychain.ts b/poc/ai/keychain.ts index 2dbb334..1d4f321 100644 --- a/poc/ai/keychain.ts +++ b/poc/ai/keychain.ts @@ -11,7 +11,7 @@ import { close } from '../../src/geometry/raster/morphology' import { findHoles, fillHoles, labelComponents, removeSpecks } from '../../src/geometry/raster/components' import { backendsOf, CANDIDATES, type Candidate } from './models' import { infer } from './engine' -import { signedDistance, vectorOutline } from './outline' +import { findCorners, fitLoop, signedDistance, tessellate, vectorOutline, type Pt } from './outline' const $ = (id: string) => document.getElementById(id) as T const num = (id: string) => +$(id).value @@ -293,10 +293,25 @@ function build3D(w: Work, mask: Uint8Array, value: Float32Array, partW: number, const F = new Float32Array(nx * ny), isIn = new Uint8Array(nx * ny), Z = new Float32Array(nx * ny) // векторный контур: изолиния, сглаживание вдоль кривой, острые углы сохраняются; поле F у края заменяется знаковым расстоянием до кривой let loops: ReturnType | null = null + let wallS: { x: number; y: number; nx: number; ny: number }[] = [] // выборка кривых (мм детали) с нормалями наружу: для гладких стенок if (chk('vec')) { const f1 = gaussianBlur(Float32Array.from(mask), w.ww, w.wh, 0.8, ones(w.ww * w.wh)), pxMmL = w.ww / partW loops = vectorOutline(Uint8Array.from(f1, (v) => Math.round(v * 255)), w.ww, w.wh, Math.max(0.5, num('osm') * pxMmL), num('cang')) log2.textContent = `контур: ${loops.length} петель, ${loops.reduce((a, l) => a + l.length, 0)} вершин` + if (chk('fit')) { // подгонка кубических Безье (как в вырезах): минимум точек, аналитические нормали стенки + const tolPx = num('ftol') * pxMmL, stepPx = Math.max(1, num('fstep') * pxMmL); let segs = 0, ctrl = 0, verts = 0 + const fitted: Pt[][] = []; wallS = [] + for (const l of loops) { + if (l.length < 12) { fitted.push(l); continue } + const { segs: sg } = fitLoop(l, findCorners(l, 3, num('cang')), tolPx), sm = tessellate(sg, stepPx) + // направление нормали: наружу — туда, где маска пуста + let inside = 0; for (const q of sm) { const x = q.p[0] + q.n[0] * 1.5, y = q.p[1] + q.n[1] * 1.5; if (bil(soft, w.ww, w.wh, x, y) > 0.5) inside++ } + const flip = inside > sm.length / 2 ? -1 : 1 + fitted.push(sm.map((q) => q.p as Pt)); for (const q of sm) wallS.push({ x: q.p[0] * mmx - partW / 2, y: partH / 2 - q.p[1] * mmy, nx: q.n[0] * flip, ny: -q.n[1] * flip }) + segs += sg.length; ctrl += 3 * sg.length + 1; verts += sm.length + } + loops = fitted; log2.textContent = `контур: кривых Безье ${segs} (контрольных точек ${ctrl}), вершин в модели ${verts}, допуск ${num('ftol')} мм, шаг ${num('fstep')} мм` + } } else log2.textContent = '' for (let j = 0; j < ny; j++) for (let i = 0; i < nx; i++) { const k = j * nx + i; F[k] = bil(soft, w.ww, w.wh, px(i), py(j)) @@ -335,12 +350,29 @@ function build3D(w: Work, mask: Uint8Array, value: Float32Array, partW: number, for (let v = 0; v < vx.length; v++) { pos[v * 3] = vx[v]; pos[v * 3 + 1] = vy[v]; pos[v * 3 + 2] = vz[v] } const topGeo = new THREE.BufferGeometry(); topGeo.setAttribute('position', new THREE.BufferAttribute(pos, 3)); topGeo.setIndex(idx); topGeo.computeVertexNormals() // Стенки по сегментам контура и дно: те же полигоны на z = 0 (зеркально) - const wp: number[] = [] - const quad = (a: number[], b: number[], c: number[], d: number[]) => wp.push(...a, ...b, ...c, ...b, ...d, ...c) + const wp: number[] = [], wn: number[] = [] const V = (v: number, z?: number): number[] => [vx[v], vy[v], z ?? vz[v]] - for (const [p0, p1] of segs) quad(V(p0, 0), V(p1, 0), V(p0), V(p1)) - for (let t = 0; t < idx.length; t += 3) wp.push(...V(idx[t], 0), ...V(idx[t + 2], 0), ...V(idx[t + 1], 0)) // дно - const skirt = new THREE.BufferGeometry(); skirt.setAttribute('position', new THREE.BufferAttribute(new Float32Array(wp), 3)); skirt.computeVertexNormals() + const nrmAt = new Map() + const wallN = (v: number): [number, number] => { // нормаль стенки в вершине контура: у ближайшей точки подогнанной кривой + let r = nrmAt.get(v); if (r) return r + let best = Infinity; r = [0, 0] + for (const q of wallS) { const d = (q.x - vx[v]) ** 2 + (q.y - vy[v]) ** 2; if (d < best) { best = d; r = [q.nx, q.ny] } } + nrmAt.set(v, r); return r + } + const tri3 = (a: number[], b: number[], c: number[], na: number[], nb: number[], nc: number[]) => { // порядок вершин так, чтобы лицевая сторона смотрела по нормали + const g = [(b[1] - a[1]) * (c[2] - a[2]) - (b[2] - a[2]) * (c[1] - a[1]), (b[2] - a[2]) * (c[0] - a[0]) - (b[0] - a[0]) * (c[2] - a[2]), (b[0] - a[0]) * (c[1] - a[1]) - (b[1] - a[1]) * (c[0] - a[0])] + const dn = g[0] * (na[0] + nb[0] + nc[0]) + g[1] * (na[1] + nb[1] + nc[1]) + g[2] * (na[2] + nb[2] + nc[2]) + if (dn >= 0) { wp.push(...a, ...b, ...c); wn.push(...na, ...nb, ...nc) } else { wp.push(...a, ...c, ...b); wn.push(...na, ...nc, ...nb) } + } + const curved = chk('vec') && chk('fit') && wallS.length > 0 + for (const [p0, p1] of segs) { + const n0 = curved ? [...wallN(p0), 0] : [0, 0, 0], n1 = curved ? [...wallN(p1), 0] : [0, 0, 0] + if (curved) { tri3(V(p0, 0), V(p1, 0), V(p0), n0, n1, n0); tri3(V(p1, 0), V(p1), V(p0), n1, n1, n0) } + else wp.push(...V(p0, 0), ...V(p1, 0), ...V(p0), ...V(p1, 0), ...V(p1), ...V(p0)) + } + for (let t = 0; t < idx.length; t += 3) { wp.push(...V(idx[t], 0), ...V(idx[t + 2], 0), ...V(idx[t + 1], 0)); if (curved) wn.push(0, 0, -1, 0, 0, -1, 0, 0, -1) } // дно + const skirt = new THREE.BufferGeometry(); skirt.setAttribute('position', new THREE.BufferAttribute(new Float32Array(wp), 3)) + if (curved) skirt.setAttribute('normal', new THREE.BufferAttribute(new Float32Array(wn), 3)); else skirt.computeVertexNormals() group.clear() mat.wireframe = chk('wire'); mat.side = THREE.DoubleSide group.add(new THREE.Mesh(topGeo, mat), new THREE.Mesh(skirt, mat)) From be2861e8b1d8bb3ef4571cd5c9bf8c878195c19e Mon Sep 17 00:00:00 2001 From: skarakovski Date: Thu, 22 Oct 2026 10:21:52 +0700 Subject: [PATCH 31/38] plan: model manager as a modal from the Header (sections by kind), no silent download, disabled run without a model, AI activity line and full log (32, 33, 34) Co-Authored-By: Claude Sonnet 5.5 --- openspec/changes/32-photo-trace-ai/design.md | 4 +- .../changes/32-photo-trace-ai/proposal.md | 4 +- .../specs/cutout-photo-ai/spec.md | 66 ++++++++++++++++--- openspec/changes/32-photo-trace-ai/tasks.md | 3 +- .../33-keychain-ai-silhouette/design.md | 2 +- .../specs/keychain-ai-silhouette/spec.md | 12 ++-- .../33-keychain-ai-silhouette/tasks.md | 2 +- .../34-keychain-photo-relief/design.md | 2 +- .../specs/keychain-photo-relief/spec.md | 10 ++- .../changes/34-keychain-photo-relief/tasks.md | 2 +- 10 files changed, 83 insertions(+), 24 deletions(-) diff --git a/openspec/changes/32-photo-trace-ai/design.md b/openspec/changes/32-photo-trace-ai/design.md index cf1acf4..d4441fe 100644 --- a/openspec/changes/32-photo-trace-ai/design.md +++ b/openspec/changes/32-photo-trace-ai/design.md @@ -37,7 +37,9 @@ Why the straightened sheet is a good input for a network: it is a top view at tr - **Hybrid edge.** For each silhouette: band = mask dilated by `band` mm (default 1.5) minus mask eroded by `band`. Inside the band the classic paper-colour deviation (`colourMask` from the paper surface fitted by `paperSurface`; `paperSurface` and `colourMask` are called, not changed) classifies pixels; the refined mask is `eroded core ∪ (band ∩ classic object pixels)`. If the classic test marks the whole band as paper or as object on a stretch longer than a few mm (the dark-part-in-shadow case), that stretch keeps the network edge. The refined labels go through the same `traceAlpha` as today. A setting `refine: on|off` (default on) exists in the compare view so the raw network edge can be inspected; it is not a persisted user option beyond the session. - **Compare view.** `CompareOverlay` draws the other engine's silhouette outlines (already traced by `traceSilhouette`) as dashed lines over the straightened view, plus a table "tool: classic mm / AI mm / Δ". It costs one extra segmentation when open, and is the way the user, and the owner while developing, sees where the engines disagree. - **ML runtime.** `src/ml/` is React-free. `matte(rgb, w, h) → Float32 alpha` is the only adapter this change needs. `ml.worker.ts` owns the inference session; `mlClient.ts` mirrors `modelClient.ts` (latest request wins, stale replies dropped); the worker is created lazily so classic users pay nothing, not even the library chunk (dynamic import). Backend: try WebGPU, fall back to WASM single-threaded (no COOP/COEP on Pages); report the backend in the model manager. -- **Model manager (a window of its own).** `ModelManager` is a dialog reachable from the engine switch and from the app footer. It lists every model of the registry with: name, what it is good for (fast / accurate), download size, licence, status (not downloaded / downloading with progress / ready / failed), and the buttons Download, Cancel, Delete, plus the total space used and a "Delete all". Several models can be downloaded at the same time as separate entries; the user picks the active one for the AI engine (a radio in the list). Weights live on the client only: Cache API under a versioned key (revision hash), with a memory fallback and a notice when the browser refuses storage. Nothing downloads without the click; choosing AI with no model ready opens the manager. The first download of a model shows its consent text (below). +- **Model manager (a modal opened from the top bar).** `ModelManager` is a modal dialog opened by an "AI models" button in the `Header` (next to the language and unit switches), visible on every page and independent of any mode; the AI blocks of Cutout and Keychain only link to it ("Models…"). It has **sections by the `kind` of the registry entry**: "Silhouette (mask)" and "Depth", each listing its models with: name, what it is good for (fast / accurate), download size, licence, status (not downloaded / downloading with progress / ready / failed / unavailable on this device with the reason), and the buttons Download, Cancel, Delete; the footer of the modal shows the total space used, "Delete all" and the full log (below). Several models can be downloaded at the same time as separate entries; the user picks the active one per kind (a radio in each section). Weights live on the client only: Cache API under a versioned key (revision hash), with a memory fallback and a notice when the browser refuses storage. The button in the `Header` shows a small indicator while a download or an AI run is in progress. +- **Explicit confirmation, no silent download.** There is one code path that starts a download, `requestDownload(model)`, and it always goes through a confirmation dialog in the manager (name, size, source host, licence; "Download" / "Cancel"); nothing prefetches, resumes in the background or downloads as a side effect of any other action. When the AI run button is pressed there is no download at all: **without a ready model of the needed kind the run button is disabled** and a hint under it says "Download a model to use AI" with a link that opens the manager; when the download finishes and a model is chosen, the button becomes active by itself (the state is derived from the model store, so it updates live). A test makes the downloader unreachable except through the confirmed path. +- **AI activity and log.** Every phase of a model download and of an AI run reports an event to a session log (`mlLog`: time, level, an i18n key with parameters, never a ready-made sentence, so the line follows the language of the interface). `AiStatus` is the one component that shows activity: a spinner and **only the current line** (the latest event of the active job, for example "Downloading model: 42 of 98 MB", "Starting the session (WASM)", "Object 2 of 3, second pass", "Building the outline"), with a Cancel button, placed next to the run button of each feature (photo dialog, Keychain silhouette, Keychain depth). While a job is active the run button is replaced by or accompanied by `AiStatus`; at the end the line shows the result ("Done in 5.2 s") or the error. The **full log of the session** (all lines, with time, scrollable, with "Copy" and "Clear") is shown in the model manager modal; downloads of models are logged there too. The log is memory only and never leaves the browser. - **Licences.** Only models under permissive licences (MIT, Apache-2.0, BSD, CC0) are offered. Non-commercial and copyleft weights are excluded: RMBG-1.4 (Bria, non-commercial) gave the best raw result in the PoC but is left out, because with the mask tuning below the permissive models reach a good result too (decision of the owner after the PoC), and the project stays free of licence caveats. The first download of any model shows its name, size, source host and licence and starts only after the click; the References page and the README list every model with its SPDX id and source. The PoC page keeps RMBG-1.4 marked `referenceOnly` as a quality yardstick; it is not part of the product registry. - **Pinned weights.** The registry (`src/ml/models.ts`) holds per model: id, URL with a commit hash (not `main`), byte size, SHA-256 (verified after download), input size and normalisation, SPDX licence, source. A model enters the registry only with MIT / Apache-2.0 / BSD / CC0. The References page and README list them from this registry (a test checks every registry entry appears). - **Backend per model.** The registry lists for each model the backends it works on and the preferred one, from the PoC: InSPyReNet fp16 and ISNet imgly fp16 are WebGPU-only (fp16 on WASM gives an empty mask or minutes), ormbg and U²-Net fail on WebGPU (`ceil_mode` in MaxPool) and run on WASM, RMBG-1.4 int8 runs on WASM faster than on WebGPU. The manager shows only models that the device can run (WebGPU and `shader-f16` detected at start) and marks the others as unavailable with the reason; a run that fails on the preferred backend is not silently retried on another unless the registry says it works there. diff --git a/openspec/changes/32-photo-trace-ai/proposal.md b/openspec/changes/32-photo-trace-ai/proposal.md index 28ef0ec..10e549a 100644 --- a/openspec/changes/32-photo-trace-ai/proposal.md +++ b/openspec/changes/32-photo-trace-ai/proposal.md @@ -14,7 +14,9 @@ The AI path is an **alternative**, not a replacement. The current segmenter stay - **AI silhouettes (automatic).** The straightened sheet (`rectify`) is run through a background-removal network; the foreground mask is cleaned (threshold, close, fill holes, edge margin, specks, connected components) into the same `Segmentation` the classic path produces, so selection, outline tracing, cutout creation and everything after it are shared. - **Hybrid edge.** The network decides which pixels belong to an object; the exact edge comes from the classical code: a band of 1–2 mm around the network's mask is searched with the existing paper-colour test and sub-pixel `traceAlpha`. Where the classic test finds nothing in the band (a dark part merged with its shadow) the network's own edge is used. Result: the 0.5 mm scale accuracy of the baseline wherever the baseline is accurate, and the network's judgement where it is not. - **Compare view.** A switch that shows the classic and the AI silhouettes over the straightened sheet (outline of the other engine as a dashed line), with the size of each silhouette in mm for both, so the user can pick the better result per photo. -- **ML runtime.** A separate worker (`ml.worker.ts`, not the Manifold worker), a client with "latest request wins" like `modelClient.ts`, a model manager (consent dialog, download with progress, cache in the browser, delete, status), backend choice WebGPU → WASM, weights from a pinned Hugging Face revision. All UI text in en and ru. +- **ML runtime.** A separate worker (`ml.worker.ts`, not the Manifold worker), a client with "latest request wins" like `modelClient.ts`, a model manager, backend choice WebGPU → WASM, weights from a pinned Hugging Face revision. All UI text in en and ru. +- **Model manager (a modal of its own).** Opened by a button in the top bar of the app, always available and independent of any mode, so models can be downloaded or deleted at any time. Models are in sections by kind (silhouette mask, depth); each shows size, licence and status, with Download and Delete. A model is **never downloaded silently**: only after an explicit confirmation in the manager. The AI run button is disabled while no suitable model is ready, with a hint to download one, and becomes active when the download finishes. +- **AI activity and log.** While a model downloads or an AI run is going on, a loader and the **current line of the log** (what is happening now, for example "Downloading model: 42 of 98 MB", "Starting session (WASM)", "Object 2 of 3, second pass") are shown next to the run button, and the model manager modal shows the **full log** of the session. - **Evaluation harness.** A spike script that runs the baseline and every candidate model on the fixtures and prints per-silhouette IoU and size error in mm; the model for the default AI engine is chosen from its output (task 1), not from the catalogue. ## Capabilities diff --git a/openspec/changes/32-photo-trace-ai/specs/cutout-photo-ai/spec.md b/openspec/changes/32-photo-trace-ai/specs/cutout-photo-ai/spec.md index 21a2a2c..0f941ac 100644 --- a/openspec/changes/32-photo-trace-ai/specs/cutout-photo-ai/spec.md +++ b/openspec/changes/32-photo-trace-ai/specs/cutout-photo-ai/spec.md @@ -36,31 +36,54 @@ On the silhouette step the user SHALL be able to choose the detection engine, Cl - **THEN** the sensitivity control, which belongs to the classic detector, is not offered for it, and the margin still applies ### Requirement: Model manager -The app SHALL have a model manager window, reachable from the engine switch and from the footer, that lists the available models with their name, purpose, size, licence and status, and lets the user download, cancel, delete and choose the model used by the AI engine. Several models SHALL be downloadable and stored side by side. The models SHALL be kept in the browser's storage on the device; the window SHALL show the space they use and offer to delete all of them. +The app SHALL have a model manager, a modal window opened by a button in the top bar of the app, available on every page and independent of any mode. It SHALL list the available models in sections by their kind (silhouette mask, depth) with their name, purpose, size, licence and status, and SHALL let the user download, cancel, delete and choose the model used for each kind. Several models SHALL be downloadable and stored side by side. The models SHALL be kept in the browser's storage on the device; the window SHALL show the space they use and offer to delete all of them, and SHALL show the full log of the session. -#### Scenario: List +#### Scenario: Always reachable +- **WHEN** the user is on any page of the app +- **THEN** the top bar has a button that opens the model manager, also when no AI feature is in use + +#### Scenario: Sections by kind - **WHEN** the manager is opened -- **THEN** every model is listed with its size, licence and status +- **THEN** the models of each kind are in their own section, each with its size, licence and status #### Scenario: Download and delete - **WHEN** the user downloads a model and later deletes it - **THEN** progress is shown while downloading, the model is then ready for use, and after deleting its stored data is removed and it shows as not downloaded #### Scenario: Choice -- **WHEN** two models are downloaded -- **THEN** the user picks which one the AI engine uses +- **WHEN** two models of one kind are downloaded +- **THEN** the user picks which one the AI engine uses for that kind + +#### Scenario: Activity indicator +- **WHEN** a download or an AI run is in progress and the manager is closed +- **THEN** the manager's button in the top bar shows that something is going on + +### Requirement: No AI run without a model +The button that starts an AI run SHALL be disabled while no suitable model is ready, and a hint next to it SHALL say that a model has to be downloaded and SHALL open the model manager. When the download finishes and a model is available, the button SHALL become active without any other action. A run SHALL NOT start a download. -#### Scenario: No model yet -- **WHEN** the user chooses the AI engine and no model is ready -- **THEN** the manager opens, and nothing is downloaded until the user starts a download +#### Scenario: Disabled without a model +- **WHEN** the AI engine is chosen and no silhouette model is downloaded +- **THEN** the run button is disabled and a hint offers to download a model, with a link to the manager + +#### Scenario: Active after the download +- **WHEN** the user downloads a model in the manager +- **THEN** when the download is complete the run button becomes active, with no other action + +#### Scenario: Deleting the model +- **WHEN** the user deletes the only model while the AI engine is chosen +- **THEN** the run button becomes disabled again, and an already computed result stays in use ### Requirement: Model download and consent -A model SHALL be downloaded only after the user confirms. Before the first download of a model the app SHALL show its name, size, source host and licence, and the models offered SHALL be under permissive licences (MIT, Apache-2.0, BSD or CC0). Downloads SHALL show progress and be cancellable. If the download or the model fails, or the file's checksum does not match, the classic engine SHALL stay usable and the failure SHALL be shown. +A model SHALL be downloaded only after the user explicitly confirms in the model manager, and never as a side effect of another action, in the background or in advance. Before a download the app SHALL show its name, size, source host and licence, and the models offered SHALL be under permissive licences (MIT, Apache-2.0, BSD or CC0). Downloads SHALL show progress and be cancellable. If the download or the model fails, or the file's checksum does not match, the classic engine SHALL stay usable and the failure SHALL be shown. #### Scenario: Consent first -- **WHEN** the user starts the first download of a model +- **WHEN** the user starts the download of a model - **THEN** a dialog states the size and that the source host sees the user's address, and nothing is downloaded until the user confirms +#### Scenario: Never silent +- **WHEN** the user uses any feature of the app without confirming a download +- **THEN** no model file is requested from the network + #### Scenario: Cached - **WHEN** the model was downloaded earlier and the storage is intact - **THEN** choosing it starts at once without a new download @@ -73,6 +96,29 @@ A model SHALL be downloaded only after the user confirms. Before the first downl - **WHEN** the browser refuses persistent storage - **THEN** the model works for the session and a notice says it will be downloaded again next time +### Requirement: AI activity and log +While a model is downloading or an AI run is in progress, the app SHALL show a loader and the current line of the log, describing what is happening now, next to the control that started it, with a way to cancel. When the job ends the line SHALL show the result or the error. The model manager SHALL show the full log of the session with the time of each line, and let the user copy and clear it. The lines SHALL be in the language of the interface, and the log SHALL stay in the browser. + +#### Scenario: Current line +- **WHEN** an AI run goes through its phases +- **THEN** next to the run button a spinner and one line show the current phase, replacing the previous line as the phase changes + +#### Scenario: Download progress +- **WHEN** a model is downloading +- **THEN** the line shows the downloaded and total size, and the manager shows the progress of that model + +#### Scenario: Full log +- **WHEN** the user opens the model manager after a run +- **THEN** all lines of the session are listed with their time, newest at the end, and Copy and Clear work + +#### Scenario: Error +- **WHEN** a run or a download fails +- **THEN** the line shows the error and the log keeps its details + +#### Scenario: Language +- **WHEN** the interface language is changed +- **THEN** the lines of the log are shown in the new language + ### Requirement: Privacy The photo, the straightened image and the masks SHALL be processed in the browser only and SHALL NOT be sent anywhere by the AI engine. The only network traffic added by the AI engine SHALL be the download of the model files, from a fixed, versioned location, after consent. The user interface and the References page SHALL say so, and SHALL list every model with its licence and source. Only models under permissive licences (MIT, Apache-2.0, BSD or CC0) SHALL be offered. diff --git a/openspec/changes/32-photo-trace-ai/tasks.md b/openspec/changes/32-photo-trace-ai/tasks.md index 652e6a2..6d7bc42 100644 --- a/openspec/changes/32-photo-trace-ai/tasks.md +++ b/openspec/changes/32-photo-trace-ai/tasks.md @@ -27,7 +27,8 @@ ## 5. UI - [ ] 5.1 `usePhotoTrace`: `engine` in `PhotoSettings` (default `classic`), `aiSegmentation` computed only when needed, an `active` segmentation, AI error falls back to classic with an error state, `computing` covers the AI run. Tests: default is classic and calls no model; switching uses the mocked client; a failing client leaves classic active; closing and reopening keeps the engine. -- [ ] 5.2 `EngineSwitch` on the silhouette step (SegmentedControl Classic | AI, sensitivity hidden for AI) and `ModelManager` dialog (list with name, purpose, size, licence, status; Download / Cancel / Delete / Delete all, active-model radio, used space, backend; consent before the first download), reachable from the switch and the footer. No artboard exists: built from `ui/` components and `theme.css` tokens, said in the commit; compare in en and ru and at 390 px. Component tests: consent before any download, progress, error banner, delete returns to "not downloaded" and asks for consent again, two models side by side, choosing the active one. +- [ ] 5.2 `EngineSwitch` on the silhouette step (SegmentedControl Classic | AI, sensitivity hidden for AI) and `ModelManager` modal opened by an "AI models" button in the `Header` (always visible, independent of the mode; with an activity indicator), sections by registry `kind` (Silhouette mask, Depth), each row with name, purpose, size, licence, status (including unavailable with the reason), Download / Cancel / Delete, an active-model radio per kind, used space, "Delete all", the full log at the bottom; a confirmation dialog before every download. The photo dialog links to it ("Models…"); the run button is disabled without a ready model, with the hint "Download a model to use AI" and a link, and becomes active by itself when the download ends. No artboard exists: built from `ui/` components and `theme.css` tokens, said in the commit; compare in en and ru and at 390 px. Component tests: consent before any download, progress, error banner, delete returns to "not downloaded" and asks for consent again, two models side by side, choosing the active one. +- [ ] 5.2b `mlLog` (session log of events: time, level, i18n key with parameters) and `AiStatus` (spinner, only the current line, Cancel) fed by `mlClient` and `modelStore` events for both downloads and runs; the modal shows the full log with Copy and Clear; strings for every event in `en.json` and `ru.json`. Tests: the line follows the events of a fake run in order and shows the result at the end, the log keeps every line with its time, Cancel stops the job and logs it, errors appear in the line and in the log, switching the language re-renders the lines, key parity for every event key, nothing outside the manager calls the downloader (the downloader is reachable only through the confirmed path), the run button follows the model store live (disabled, active after the mocked download, disabled after delete), the `Header` indicator appears while a job is active. - [ ] 5.2a `MaskTuning` block, always visible under the AI engine on the silhouette step: two sliders with plain-language labels and hints ("Edge reach", "Outline smoothness"; threshold and smoothing in code) (smoothing is a length: `LengthField`), per-model defaults from the registry (`maskDefaults`), reset to them, greyscale view of the soft mask, size change per silhouette and the thin-part hint; updates without a model run (cached alpha). Component tests with a mocked `mlClient`: moving a slider changes the silhouette sizes and the mocked client is called once, reset restores the model's defaults, switching model loads its defaults unless the controls were touched, the hint appears when a silhouette shrinks by more than 1 mm, both languages. - [ ] 5.3 `CompareOverlay` and the table of sizes (classic, AI, Δ) in the silhouette step; dashed outlines of the inactive engine over the straightened view; off by default. Tests: toggling draws the second outline set and the table values equal `silhouettes` of both engines; off costs no AI run. - [ ] 5.4 Strings in `en.json` and `ru.json` (engine, consent, progress, errors, compare, References licence list); the References page and README list the registry models. Tests: key parity, a registry-to-References test, the `PhotoTips` and consent text exist in both languages. diff --git a/openspec/changes/33-keychain-ai-silhouette/design.md b/openspec/changes/33-keychain-ai-silhouette/design.md index b64b3df..7b79e05 100644 --- a/openspec/changes/33-keychain-ai-silhouette/design.md +++ b/openspec/changes/33-keychain-ai-silhouette/design.md @@ -25,7 +25,7 @@ A keychain differs from a photo cutout in three ways that shape the design: the - **Persistence.** `imageStore` records get a second kind of entry, `matte`, keyed by `imageId` + `model` + `revision`, with the plane and its size; stored together with the original in the same DB (`gridfinity-keychain`), memory fallback with the existing `persistent` warning. The params store `source`, `ai.model`, `threshold`, `smooth`, `keepLargest`; on load the matte is read and registered. Deleting the model in the manager does not delete the matte; deleting or replacing the image does. If the matte is missing (cleared storage) the section shows "run again" and the silhouette falls back to Brightness until it is run, never to an empty mask. - **Staleness.** The matte is tied to the image and the model: replacing the image drops it; choosing a different model keeps the old matte until the user runs the new one (shown as "matte from "), so a switch never silently re-runs a download-sized job. - **Relation to existing alpha.** If the image already has transparency, the section shows the note "this image has transparency, it is used as is" with AI still selectable (the user may want a different cut), and the AI run ignores the image's alpha as input. SVG is rasterised by the existing path and is not offered AI. -- **UI.** `SilhouetteSection` gets `SegmentedControl` From: Brightness | AI. Under AI: model picker (from the manager, with a link to open it), the Remove-background button with progress and cancel, the shared `MaskTuning` block (edge reach, smoothness, per-model defaults, reset, soft-matte greyscale view, which can also open in the enlarged mask dialog), the keep-largest toggle and the hints. Brightness controls (threshold, invert) are hidden under AI because they have no effect there; close, fill holes, specks and grow stay, since they work on the resulting mask. The mask preview, areas and everything downstream read the resulting silhouette as today. +- **UI.** `SilhouetteSection` gets `SegmentedControl` From: Brightness | AI. Under AI: model picker (from the manager, with a link to open it), the Remove-background button (disabled with the hint "Download a model to use AI" and a link to the manager while no model is ready, active as soon as one is) with the shared `AiStatus` next to it (spinner, the current line of the log, Cancel; the full log is in the model manager modal), the shared `MaskTuning` block (edge reach, smoothness, per-model defaults, reset, soft-matte greyscale view, which can also open in the enlarged mask dialog), the keep-largest toggle and the hints. Brightness controls (threshold, invert) are hidden under AI because they have no effect there; close, fill holes, specks and grow stay, since they work on the resulting mask. The mask preview, areas and everything downstream read the resulting silhouette as today. - **Hints.** After a run, if the image has more than 2 distinct dominant colours in the border band the hint is silent; if the image is nearly two-tone (a logo on a clean background: Otsu separability high, a border band of one colour) a hint says Brightness is usually better and quicker. Pure text, never blocks. - **Performance.** The run is a few seconds to ~20 s depending on model and backend (see `FINDINGS.md`); the dialog shows progress, the panel stays usable, a new run cancels an old one (latest wins via the client). Huge images are capped for inference only. - **Versioning of params.** `migrateKeychain` fills `source: 'brightness'` and the `ai` defaults for old records, so stored keychains are unchanged; with `source: 'brightness'` the pipeline output equals the current one (regression volumes in the existing tests). diff --git a/openspec/changes/33-keychain-ai-silhouette/specs/keychain-ai-silhouette/spec.md b/openspec/changes/33-keychain-ai-silhouette/specs/keychain-ai-silhouette/spec.md index cd9d591..fe2439c 100644 --- a/openspec/changes/33-keychain-ai-silhouette/specs/keychain-ai-silhouette/spec.md +++ b/openspec/changes/33-keychain-ai-silhouette/specs/keychain-ai-silhouette/spec.md @@ -21,19 +21,23 @@ In Image mode the user SHALL be able to choose where the silhouette comes from: - **THEN** the brightness threshold and invert are not shown, and close gap, fill areas, remove specks and grow are still available ### Requirement: Remove background with AI -With the AI source the user SHALL be able to run background removal on the whole image with a model chosen from the model manager. The run SHALL show progress and be cancellable, SHALL work on any image aspect ratio without distorting it, and SHALL keep the original image. The image and the result SHALL stay in the browser. If no model is ready, the model manager SHALL open and nothing SHALL be downloaded without the user's confirmation. +With the AI source the user SHALL be able to run background removal on the whole image with a model chosen from the model manager. The run SHALL show a loader with the current line of the log (what is happening now) and be cancellable, SHALL work on any image aspect ratio without distorting it, and SHALL keep the original image. The image and the result SHALL stay in the browser. If no model is ready, the Remove background button SHALL be disabled with a hint to download a model (a link to the model manager), SHALL become active when a download finishes, and a run SHALL NOT download a model. #### Scenario: Run - **WHEN** the user presses Remove background with a model ready -- **THEN** progress is shown, and on completion the silhouette preview and the relief show the subject without the background +- **THEN** a loader and the current line of the log are shown next to the button, and on completion the silhouette preview and the relief show the subject without the background + +#### Scenario: Current line +- **WHEN** the run goes through its phases +- **THEN** the line shows what is happening now and changes as the phase changes, and shows the result or the error at the end #### Scenario: Any proportions - **WHEN** the image is a wide landscape or a tall portrait - **THEN** the subject's outline is not stretched compared with the image #### Scenario: No model -- **WHEN** the user presses Remove background and no model is downloaded -- **THEN** the model manager opens, and the run starts only after a model is downloaded and chosen +- **WHEN** no silhouette model is downloaded +- **THEN** the Remove background button is disabled and a hint offers to download a model with a link to the model manager, and when the download finishes the button becomes active with no other action #### Scenario: Cancel - **WHEN** the user cancels a run or starts another diff --git a/openspec/changes/33-keychain-ai-silhouette/tasks.md b/openspec/changes/33-keychain-ai-silhouette/tasks.md index 663490a..d66a99c 100644 --- a/openspec/changes/33-keychain-ai-silhouette/tasks.md +++ b/openspec/changes/33-keychain-ai-silhouette/tasks.md @@ -17,7 +17,7 @@ ## 4. UI -- [ ] 4.1 `SilhouetteSection`: source switch Brightness | AI (hides brightness threshold and invert under AI), `AiSilhouette` block (model picker with a link to the manager, Remove background button with progress and cancel, shared `MaskTuning` with per-model defaults and the soft-matte view, keep-largest toggle, lost-result message, hints for transparent, logo and SVG). Strings in `en.json` and `ru.json`. No artboard exists: built from `ui/` components and `theme.css` tokens, said in the commit; compare in en and ru and at 390 px. Component tests: Brightness default and unchanged, controls hide and show per source, the button opens the manager when no model is ready, sliders update the silhouette without a client call, keep-largest, hints, SVG has no AI, lost result falls back with a message, key parity. +- [ ] 4.1 `SilhouetteSection`: source switch Brightness | AI (hides brightness threshold and invert under AI), `AiSilhouette` block (model picker with a link to the manager, Remove background button (disabled with a hint and a link to the manager without a model, active by itself after the download) with `AiStatus` (loader, current line, Cancel), shared `MaskTuning` with per-model defaults and the soft-matte view, keep-largest toggle, lost-result message, hints for transparent, logo and SVG). Strings in `en.json` and `ru.json`. No artboard exists: built from `ui/` components and `theme.css` tokens, said in the commit; compare in en and ru and at 390 px. Component tests: Brightness default and unchanged, controls hide and show per source, the button is disabled with a hint and a manager link when no model is ready and becomes active when the model store reports a model, the loader and the current line follow the events of a fake run, sliders update the silhouette without a client call, keep-largest, hints, SVG has no AI, lost result falls back with a message, key parity. - [ ] 4.2 The enlarged mask dialog and `MaskPreview` show the AI silhouette and can switch to the soft matte view; areas, selection and everything downstream work on the AI silhouette. Tests: preview follows the source, the area list is built from the AI mask. ## 5. Version diff --git a/openspec/changes/34-keychain-photo-relief/design.md b/openspec/changes/34-keychain-photo-relief/design.md index 45d3f20..24b2ec3 100644 --- a/openspec/changes/34-keychain-photo-relief/design.md +++ b/openspec/changes/34-keychain-photo-relief/design.md @@ -37,7 +37,7 @@ Depth V2 Small gives a smooth depth map (the owner found it good as a form); on - **Heights and printing.** A height field grows from a flat back (existing design), prints face up without supports. Detail finer than the grid cell is lost: `quality` and the resolution warnings apply, with a hint when the cell is larger than about a third of the boost radius. - **Persistence and migration.** Depth planes are stored in `imageStore` as `depth` records keyed by image, model, revision and crop, restored on load, kept when the model is deleted, dropped with the image, memory fallback with the `persistent` warning. `crop`, mix, boost and levels are keychain params (`migrateKeychain` fills defaults: full crop, `kind` unchanged, boost 0, levels 0). If the stored depth is missing the Depth share counts as zero with a "run again" message, never an empty relief. - **Availability.** The Photo source appears only in Image mode, not for SVG images, and Depth only when a depth model exists; Contour and Detail work without any model. -- **UI.** Relief source: Distance | Brightness | Photo. Under Photo: three share sliders with the mix as a stacked bar, the model picker and Compute depth (progress, cancel, "for another crop" note), Detail boost (amount, radius as `LengthField`), Tone levels (count, softness), presets, the existing contrast, gamma, invert and smoothing, and a height greyscale view next to the mask preview. `CropEditor` in the Image section; the Silhouette source gains "Whole frame". Strings in en and ru. +- **UI.** Relief source: Distance | Brightness | Photo. Under Photo: three share sliders with the mix as a stacked bar, the model picker and Compute depth (disabled with a hint and a manager link without a depth model, active after the download; the shared `AiStatus` with a loader, the current line of the log and Cancel; "for another crop" note), Detail boost (amount, radius as `LengthField`), Tone levels (count, softness), presets, the existing contrast, gamma, invert and smoothing, and a height greyscale view next to the mask preview. `CropEditor` in the Image section; the Silhouette source gains "Whole frame". Strings in en and ru. - **Performance.** The depth run is a few seconds and cancellable (latest wins); everything else is arithmetic in the worker. ## Risks / Trade-offs diff --git a/openspec/changes/34-keychain-photo-relief/specs/keychain-photo-relief/spec.md b/openspec/changes/34-keychain-photo-relief/specs/keychain-photo-relief/spec.md index 685e017..ba25f98 100644 --- a/openspec/changes/34-keychain-photo-relief/specs/keychain-photo-relief/spec.md +++ b/openspec/changes/34-keychain-photo-relief/specs/keychain-photo-relief/spec.md @@ -29,7 +29,7 @@ In Image mode the user SHALL be able to choose Photo as the relief height source - **THEN** the preview and the relief update without running any model ### Requirement: Depth source -The depth of the photo SHALL be computed once from the image, or from its crop, by a model of the depth kind chosen in the model manager and run in the browser, and kept with the image. Parts of the subject nearer to the camera SHALL be higher than parts farther from it. The depth range used SHALL be taken from the pixels inside the silhouette, so a far background does not use up the height range. The image and the result SHALL stay in the browser. If no depth model is ready, the model manager SHALL open and nothing SHALL be downloaded without the user's confirmation. +The depth of the photo SHALL be computed once from the image, or from its crop, by a model of the depth kind chosen in the model manager and run in the browser, and kept with the image. Parts of the subject nearer to the camera SHALL be higher than parts farther from it. The depth range used SHALL be taken from the pixels inside the silhouette, so a far background does not use up the height range. The image and the result SHALL stay in the browser. While the depth is computed the app SHALL show a loader and the current line of the log next to the button, with a way to cancel. If no depth model is ready, the Compute depth button SHALL be disabled with a hint to download a model (a link to the model manager, depth section), SHALL become active when a download finishes, and a run SHALL NOT download a model. #### Scenario: Form - **WHEN** the depth of a photo of an animal is computed and the Depth share is raised @@ -40,8 +40,12 @@ The depth of the photo SHALL be computed once from the image, or from its crop, - **THEN** the subject's relief still spans the configured height range #### Scenario: No model -- **WHEN** the user presses Compute depth and no depth model is downloaded -- **THEN** the model manager opens and the run starts only after a model is downloaded and chosen +- **WHEN** no depth model is downloaded +- **THEN** the Compute depth button is disabled with a hint and a link to the model manager, and becomes active when the download finishes + +#### Scenario: Current line +- **WHEN** the depth is being computed +- **THEN** a loader and the current line of the log are shown next to the button, with the result or the error at the end #### Scenario: Crop - **WHEN** the crop has changed since the depth was computed diff --git a/openspec/changes/34-keychain-photo-relief/tasks.md b/openspec/changes/34-keychain-photo-relief/tasks.md index ff4fa6a..a1b5e2f 100644 --- a/openspec/changes/34-keychain-photo-relief/tasks.md +++ b/openspec/changes/34-keychain-photo-relief/tasks.md @@ -21,7 +21,7 @@ ## 4. UI - [ ] 4.1 `CropEditor` (drag, handles, aspect lock, fit to subject, reset), live update of the mask preview and relief, also reachable from the enlarged mask dialog. Strings in `en.json` and `ru.json`. No artboard exists: built from `ui/` components and `theme.css` tokens, said in the commit; compare in en and ru and at 390 px. Component tests: dragging and handles change the crop, aspect lock, fit to subject uses the mask box, reset, keyboard nudging, minimum size, key parity. -- [ ] 4.2 Relief source Photo and `PhotoReliefBlock`: three share sliders with a stacked bar, depth model picker and Compute depth (progress, cancel, "for another crop" note, missing-depth message), Detail boost (amount, radius as `LengthField`), Tone levels (count, softness), presets Subtle / Portrait / Deep with Custom, existing contrast, gamma, invert and smoothing, height greyscale view, hints (coarse grid, small face); silhouette source "Whole frame" hiding the tracing controls. Component tests: availability per mode and for SVG, shares update the relief with no client call, no depth model opens the manager, presets and Custom, lost depth falls back with a message, hints, key parity, steppers on every numeric input. +- [ ] 4.2 Relief source Photo and `PhotoReliefBlock`: three share sliders with a stacked bar, depth model picker and Compute depth (disabled with a hint and a manager link without a depth model, `AiStatus` with loader, current line and Cancel, "for another crop" note, missing-depth message), Detail boost (amount, radius as `LengthField`), Tone levels (count, softness), presets Subtle / Portrait / Deep with Custom, existing contrast, gamma, invert and smoothing, height greyscale view, hints (coarse grid, small face); silhouette source "Whole frame" hiding the tracing controls. Component tests: availability per mode and for SVG, shares update the relief with no client call, no depth model disables the button with a hint and a manager link, and it becomes active when the model store reports one; the loader and the current line follow the events of a fake run, presets and Custom, lost depth falls back with a message, hints, key parity, steppers on every numeric input. ## 5. Version From 6ecc841400c93e229611e53e3ee0e05928b80a9c Mon Sep 17 00:00:00 2001 From: skarakovski Date: Thu, 22 Oct 2026 10:28:51 +0700 Subject: [PATCH 32/38] =?UTF-8?q?plan:=2031a-status-log=20(shared=20activi?= =?UTF-8?q?ty=20log=20in=20the=20status=20bar);=2032-34=20report=20AI=20ev?= =?UTF-8?q?ents=20to=20it,=20model=20manager=20links=20to=20the=20log;=20v?= =?UTF-8?q?ersions=20shifted=20(32=201.4.0=20=E2=80=A6=2035=201.7.0)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Sonnet 5.5 --- openspec/ROADMAP.md | 9 +- .../changes/31a-status-log/.openspec.yaml | 2 + openspec/changes/31a-status-log/design.md | 38 ++++++ openspec/changes/31a-status-log/proposal.md | 29 +++++ .../31a-status-log/specs/activity-log/spec.md | 116 ++++++++++++++++++ openspec/changes/31a-status-log/tasks.md | 24 ++++ openspec/changes/32-photo-trace-ai/design.md | 4 +- .../changes/32-photo-trace-ai/proposal.md | 6 +- .../specs/cutout-photo-ai/spec.md | 8 +- openspec/changes/32-photo-trace-ai/tasks.md | 6 +- .../33-keychain-ai-silhouette/design.md | 2 +- .../33-keychain-ai-silhouette/proposal.md | 2 +- .../33-keychain-ai-silhouette/tasks.md | 2 +- .../34-keychain-photo-relief/proposal.md | 2 +- .../changes/34-keychain-photo-relief/tasks.md | 2 +- .../changes/35-smooth-outline/proposal.md | 2 +- openspec/changes/35-smooth-outline/tasks.md | 2 +- 17 files changed, 233 insertions(+), 23 deletions(-) create mode 100644 openspec/changes/31a-status-log/.openspec.yaml create mode 100644 openspec/changes/31a-status-log/design.md create mode 100644 openspec/changes/31a-status-log/proposal.md create mode 100644 openspec/changes/31a-status-log/specs/activity-log/spec.md create mode 100644 openspec/changes/31a-status-log/tasks.md diff --git a/openspec/ROADMAP.md b/openspec/ROADMAP.md index 5846403..ddb2f81 100644 --- a/openspec/ROADMAP.md +++ b/openspec/ROADMAP.md @@ -41,10 +41,11 @@ | 29 | `29-beta-label` | 22a | Метка beta и дисклеймер; версия 1.0.0 из package.json на фронтенде; правило semver: каждый change поднимает версию (config.yaml, CLAUDE.md, CHANGELOG.md) | | 30 | `30-rack-front-plate` | 28, 29 | v1.1.0. Передняя панель корпуса: крепление keystone по моделям (гнездо с рампой и защёлкой, обойма за панелью), группы отверстий (колонка, ряд, сетка, зоны вокруг корпуса), «отверстия» вместо «кабельных», окно по умолчанию, ссылки на референсы в README | | 31 | `31-rack-shelf` | 26, 29 | v1.2.0. Полка: передняя панель с ушами лицом на столе, пол с вентиляцией (щели, можно глухой), боковые стенки с прямым скосом, вентиляция передней панели опционально | -| 32 | `32-photo-trace-ai` | 25a, 29 | v1.3.0. Фото-вырезы: альтернативный нейросетевой движок силуэтов (в браузере, веса по согласию, гибридный точный край), классический остаётся по умолчанию как baseline; сравнение движков | -| 33 | `33-keychain-ai-silhouette` | 32, 11, 29 | v1.4.0. Keychain Image: силуэт по ИИ (удаление фона на браузерной модели), те же ручки порога и сглаживания, один главный объект, результат хранится вместе с картинкой; яркость остаётся по умолчанию | -| 34 | `34-keychain-photo-relief` | 33, 32, 12, 17, 29 | v1.5.0. Keychain Image: рельеф из фото как смесь трёх источников формы (контур, глубина Depth Anything, детали — локальный контраст), кроп области (лицо), силуэт «весь кадр» (плашка), пресеты; результат хранится с картинкой | -| 35 | `35-smooth-outline` | 25a, 29 | v1.6.0. Общий этап контура для вырезов (классика и ИИ) и Keychain: подгонка кубических кривых Безье с минимумом точек, острые углы сохраняются, зазор по кривой; стенки без ряби, детекция не меняется | +| 31a | `31a-status-log` | 29 | v1.3.0. Общий независимый лог приложения: статус-бар показывает лоадер и текущую строку, по клику открывается панель с полным логом (уровни, источники, фильтры, копирование); лог пишут генерация, импорт, хранилище, фото-диалог, экспорт | +| 32 | `32-photo-trace-ai` | 25a, 29, 31a | v1.4.0. Фото-вырезы: альтернативный нейросетевой движок силуэтов (в браузере, веса по согласию, гибридный точный край), классический остаётся по умолчанию как baseline; сравнение движков | +| 33 | `33-keychain-ai-silhouette` | 32, 11, 29 | v1.5.0. Keychain Image: силуэт по ИИ (удаление фона на браузерной модели), те же ручки порога и сглаживания, один главный объект, результат хранится вместе с картинкой; яркость остаётся по умолчанию | +| 34 | `34-keychain-photo-relief` | 33, 32, 12, 17, 29 | v1.6.0. Keychain Image: рельеф из фото как смесь трёх источников формы (контур, глубина Depth Anything, детали — локальный контраст), кроп области (лицо), силуэт «весь кадр» (плашка), пресеты; результат хранится с картинкой | +| 35 | `35-smooth-outline` | 25a, 29 | v1.7.0. Общий этап контура для вырезов (классика и ИИ) и Keychain: подгонка кубических кривых Безье с минимумом точек, острые углы сохраняются, зазор по кривой; стенки без ряби, детекция не меняется | ```mermaid gantt diff --git a/openspec/changes/31a-status-log/.openspec.yaml b/openspec/changes/31a-status-log/.openspec.yaml new file mode 100644 index 0000000..7cc41bb --- /dev/null +++ b/openspec/changes/31a-status-log/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-10-22 diff --git a/openspec/changes/31a-status-log/design.md b/openspec/changes/31a-status-log/design.md new file mode 100644 index 0000000..21f2a81 --- /dev/null +++ b/openspec/changes/31a-status-log/design.md @@ -0,0 +1,38 @@ +# Design + +## Context + +`MeasurementsBar` (in `features/viewport`) renders X, Y, Z of the model and, on the right, a spinner with "Generating…" when `busy` (from `useModel`, which sets it 200 ms after a request starts) or a check with "Ready". `Workspace` passes `busy` from each mode. There is no shared place to report anything else, and each feature shows its own banner. + +## Goals / Non-Goals + +**Goals:** +- One log for every part of the app, one place to look at it, the current line always visible. +- A small interface that features can call without knowing about the UI; the UI follows the interface language. + +**Non-Goals:** +- Persistence, telemetry, a debugger. + +## Decisions + +- **Module store, not React state.** `activityLog.ts` is a module-level store (array of entries, a list of jobs, subscribers) read by `useSyncExternalStore`, so workers' clients, hooks and plain functions can write to it without a provider, and logging never re-renders unrelated components. The panel and the bar subscribe to it; tests reset it between cases. +- **Entries carry keys, not sentences.** `{id, time, level, source, key, params, detail?, job?}`; the UI renders `t(key, params)`, so changing the language re-renders old lines. `detail` is raw technical text of an exception (message or short stack) shown inside a collapsed "Details" element on that line, never as UI text. Time is stored as a timestamp and shown as `HH:MM:SS.mmm`. +- **Jobs.** `startJob({source, key, params, cancel?})` returns `{update({key, params, progress}), done(key, params), fail(key, params, detail), cancelled()}`. Updates replace the job's current line (and progress) in place; the log keeps the job's start, the notable milestones (`done`/`fail`/`cancelled` and anything the producer logs explicitly) and does not store every progress tick. A job that runs more than 200 ms drives the status bar loader, which replaces the `busy` prop of `Workspace`. +- **Status bar.** Left: X, Y, Z as now. Right: with a running job, the spinner and the job's current line, plus a Cancel button if the job has one; with none, the latest entry for 4 s, then "Ready" (with the check) when a model exists; counters `⚠ n` and `✕ n` of the entries since the last open or clear. The whole right part is one button (`aria-label` "Open the log"): click or Enter/Space opens the panel. The line is an `aria-live="polite"` region, throttled to one announcement per second so a download does not flood a screen reader. At 390 px the X/Y/Z shorten and the line truncates with an ellipsis; the counters stay. +- **Panel.** `ActivityLogPanel` is rendered once by `AppShell` and opened through a tiny store (`openLog({source?})`), so any component, such as the model manager, can open it filtered to a source. Desktop: a resizable panel above the status bar covering about 40 % of the viewport area; below 768 px a bottom sheet over the parameter drawer. Content: a virtualised-free list (at most 500 lines), time, level icon, source label, text, optional Details; sticky filters (level: all / warnings / errors; source chips with counts); auto-scroll to the newest line that pauses when the user scrolls up and resumes with a "Jump to latest" button; Copy (plain text: app version, language, user agent, then the lines) and Clear with no confirmation (the log is disposable); Esc and a close button, focus returns to the status bar button. +- **Retention.** A ring buffer of 500 entries; the oldest drop first; running jobs are never dropped. Memory only: a reload starts a clean log. +- **Producers in this change.** Only what exists today: (1) model generation in `useModel` and `useKeychainModel` (a `generate` job per rebuild: start, duration and triangle count on done, the validation error key and the warnings of the `ModelResult` as warning lines, a failure as an error with detail); (2) image decoding, mesh and font import (name, size, downscaled, failure); (3) storage: IndexedDB or Cache API unavailable and the memory fallback, as a warning once per session; (4) the photo dialog: sheet found or not (confidence, paper), straightened, silhouettes found (count, sizes), cutouts created; (5) export: format, printer, plates, parts that overflow, file built. The AI changes add `ai` and `model` sources later. A slider move is not logged; only the rebuild it triggers is, and consecutive rebuilds within a debounce window update one job. +- **Keys.** Every message has `log..` keys in `en.json` and `ru.json`; the existing parity test covers them. +- **Privacy.** Entries never hold pixels, file contents or URLs of user files; names and sizes only; the Copy text states that it stays on the device. No network call is made by the log. +- **Compatibility.** The `busy` prop of `Workspace` and `MeasurementsBar` stays accepted for one change as an override that is ignored when jobs report; the modes switch to jobs in this change and the prop is removed in the same change (a task), since nothing outside the app uses it. + +## Risks / Trade-offs + +- [Log noise from frequent rebuilds] → jobs coalesce within the debounce window, progress updates are in place, a ring buffer bounds memory. +- [Screen reader flooding] → one polite announcement per second, only the status line is live, not the panel. +- [Duplicate sources of truth for "generating"] → the loader is derived from jobs only; tests check that no mode passes its own busy flag. +- [Technical detail shown in a non-translated language] → only inside "Details"; the line itself is a translated sentence. + +## Open Questions + +- Whether warnings from validation (`rack.error.*`, `baseplate.error.*`) should also keep their banners (yes for now: the banner is the call to action, the log is the record). diff --git a/openspec/changes/31a-status-log/proposal.md b/openspec/changes/31a-status-log/proposal.md new file mode 100644 index 0000000..2076959 --- /dev/null +++ b/openspec/changes/31a-status-log/proposal.md @@ -0,0 +1,29 @@ +# Proposal + +## Why + +The status bar under the 3D view (`MeasurementsBar`) shows the model's X, Y and Z and one indicator, "Generating…" or "Ready". That is all the app says about what it is doing. When a model does not appear, the reason is a banner or nothing; photo processing, image import, storage fallbacks and export have no visible trace; and the planned AI features (`32`–`34`) add long jobs (downloading a model, a run of several passes) that need a visible "what is happening now" and a place to read the whole story afterwards. + +Instead of a log hidden in one feature (the model manager), the status bar becomes a general, independent activity log of the whole app: one current line at a glance, and the full log one click away. + +## What Changes + +- **A shared activity log.** An app-level, memory-only log with levels (info, warning, error), a source (model generation, photo, image, storage, export, AI, app) and a text that follows the interface language. Long work is a *job* with a state, an optional progress and a Cancel; progress updates change the job's line in place instead of adding lines. +- **The status bar shows the activity.** X, Y and Z stay on the left. On the right a loader and the **current line** (the latest line of the running job, otherwise the last message for a few seconds, then "Ready"), with small counters of warnings and errors. Clicking it, or pressing Enter, opens the log. +- **The log panel.** Opens above the status bar (a bottom sheet on a phone): every line with time, level and source, filters by level and by source, Copy (plain text with the app version and language) and Clear, closes with Esc. It is reachable from anywhere that links to it, such as the "AI models" manager, not only from the status bar. +- **Existing work reports to it.** Model generation (start, duration, triangle count, validation errors and warnings with their messages, failures), image and font import, storage fallbacks (IndexedDB or Cache API unavailable), the photo dialog (sheet found, straightened, silhouettes found, cutouts created) and export (file built, plates, parts that do not fit). The AI changes (`32`–`34`) report through the same interface. +- **Not a telemetry.** The log lives in memory of the tab, holds names and sizes but never image data, and is never sent anywhere. + +## Capabilities + +### New Capabilities +- `activity-log`: the shared activity log, its status bar line and its panel. + +### Modified Capabilities + +## Impact + +- New `src/app/activityLog.ts` (store, `log.info/warn/error`, `startJob`, `useActivityLog` via `useSyncExternalStore`), `features/viewport/StatusBar.tsx` replacing the right part of `MeasurementsBar` (the X/Y/Z part stays), `app/ActivityLogPanel.tsx` rendered by `AppShell`; `useModel`, `useKeychainModel`, `imageStore`, `fontStore`, the photo dialog hook and the export panel emit events; the `busy` prop of `Workspace` is replaced by the job state; strings in en and ru; tests (the store, the line, the panel, producers, key parity, accessibility). +- Out of scope: persistence of the log across reloads, sending it anywhere or any telemetry, logging every slider move or render frame, a profiler, the AI events themselves (added by `32`–`34` through this interface), the Gridfinity or Rack calculations beyond their generation result. +- Version 1.3.0 (minor: a new capability). +- Depends on: `29-beta-label` (the versioning rule); `31-rack-shelf` is already archived. diff --git a/openspec/changes/31a-status-log/specs/activity-log/spec.md b/openspec/changes/31a-status-log/specs/activity-log/spec.md new file mode 100644 index 0000000..f70bf76 --- /dev/null +++ b/openspec/changes/31a-status-log/specs/activity-log/spec.md @@ -0,0 +1,116 @@ +# Spec Delta + +## Purpose +Defines the shared activity log of the app, the line that shows it in the status bar and the panel that shows all of it. + +## ADDED Requirements + +### Requirement: Shared activity log +The app SHALL keep one activity log for all its parts. Each entry SHALL have a time, a level (information, warning, error), a source and a message in the language of the interface, and MAY have technical details. Long work SHALL be a job with a state (running, done, failed, cancelled), an optional progress and an optional Cancel; progress updates SHALL change the job's line instead of adding lines. The log SHALL keep at most 500 entries, dropping the oldest first and never a running job, SHALL live in the memory of the page only, and SHALL NOT contain image data or file contents nor be sent anywhere. + +#### Scenario: Language follows the interface +- **WHEN** the interface language is changed +- **THEN** all lines, old and new, are shown in the new language + +#### Scenario: Progress in place +- **WHEN** a job reports its progress many times +- **THEN** its current line changes and the log does not gain a line for each report + +#### Scenario: Bounded +- **WHEN** more than 500 entries have been logged +- **THEN** the oldest finished entries are dropped and a running job stays + +#### Scenario: Not kept +- **WHEN** the page is reloaded +- **THEN** the log starts empty + +### Requirement: Status bar +The status bar under the 3D view SHALL keep showing the model's size, and SHALL show on its right a loader and the current line of the running job, with a Cancel when the job has one. When nothing runs it SHALL show the latest message for a few seconds and then that the model is ready. It SHALL show the number of warnings and errors, and the whole right part SHALL be one control that opens the log panel by click or by keyboard. A running job longer than a fraction of a second SHALL be what shows the loader, in place of any separate generating flag of a mode. + +#### Scenario: Current line +- **WHEN** a model is being generated or a photo is being processed +- **THEN** the status bar shows a loader and what is happening now + +#### Scenario: Ready +- **WHEN** no job is running and a model exists +- **THEN** the status bar shows that the model is ready + +#### Scenario: Counters +- **WHEN** a warning or an error has been logged +- **THEN** the status bar shows how many there are + +#### Scenario: Open +- **WHEN** the user clicks the right part of the status bar or presses Enter on it +- **THEN** the log panel opens + +#### Scenario: Cancel +- **WHEN** the running job has a Cancel and the user presses it +- **THEN** the job stops and its line says that it was cancelled + +#### Scenario: Narrow screen +- **WHEN** the screen is 390 px wide +- **THEN** the size shortens, the line is truncated with an ellipsis, and the counters and the loader stay visible + +### Requirement: Log panel +The log panel SHALL list every entry with its time, level, source and message, SHALL let the user filter by level and by source, SHALL keep the newest line in view until the user scrolls up and then offer to jump to the latest, SHALL show technical details of a line on request, and SHALL offer Copy (plain text starting with the app version, the language and the browser) and Clear. It SHALL close with a button and with Esc and return the focus to where it was opened. Other parts of the app, such as the model manager, SHALL be able to open it, also filtered to one source, from any page. + +#### Scenario: Filters +- **WHEN** the user chooses to show only errors, or only one source +- **THEN** only matching lines are shown, with the number of lines per source + +#### Scenario: Follow the newest +- **WHEN** new lines arrive and the user is at the end of the list +- **THEN** the list follows them, and when the user has scrolled up it stays and a control jumps to the latest + +#### Scenario: Copy +- **WHEN** the user presses Copy +- **THEN** the text of the shown lines, with the app version, the language and the browser, is put on the clipboard and the panel says so + +#### Scenario: Clear +- **WHEN** the user presses Clear +- **THEN** all finished entries are removed and the counters reset + +#### Scenario: Opened from elsewhere +- **WHEN** another part of the app opens the log for one source +- **THEN** the panel opens with that source selected, on any page, also where there is no status bar + +#### Scenario: Phone +- **WHEN** the screen is narrower than 768 px +- **THEN** the panel is a sheet from the bottom with the same content + +### Requirement: What the app reports +The app SHALL report to the log: each model generation (start, how long it took, the number of triangles, validation messages and warnings, failures with details), the import of images, meshes and fonts (name, size, whether the image was downscaled, failures), the fall-back to memory when the browser's storage is unavailable, the steps of the photo dialog (sheet found or not, paper, silhouettes found with their sizes, cutouts created) and the export (format, printer, plates, parts that do not fit, files built). Moving a control SHALL NOT add a line for each movement; only the rebuild it triggers SHALL, and rebuilds that follow each other within the delay of the model update SHALL be one job. + +#### Scenario: Generation +- **WHEN** a parameter changes and the model is rebuilt +- **THEN** one job reports the start and the result with its duration + +#### Scenario: Slider +- **WHEN** the user drags a slider +- **THEN** the log gains lines only for the rebuilds that actually run, not for every movement + +#### Scenario: Failure +- **WHEN** a generation fails or a parameter is invalid +- **THEN** an error or a warning line says why, with details on request, and the model that was shown stays + +#### Scenario: Storage +- **WHEN** persistent storage is unavailable +- **THEN** a warning says that results will not be kept, once per session + +### Requirement: Accessibility +The status line SHALL be announced politely to assistive technology at most about once a second, the control that opens the log SHALL have an accessible name, and the panel SHALL be reachable and closable with the keyboard. + +#### Scenario: Announcements +- **WHEN** a job reports progress many times a second +- **THEN** a screen reader announces at most about one line a second + +#### Scenario: Keyboard +- **WHEN** the user uses only the keyboard +- **THEN** they can open the log from the status bar, move through its controls and close it with Esc + +### Requirement: Language +All text of the status line, the panel and the messages the app logs SHALL be available in every language of the app. + +#### Scenario: Russian +- **WHEN** the interface language is Russian +- **THEN** the status line, the panel, its controls and the logged messages are in Russian diff --git a/openspec/changes/31a-status-log/tasks.md b/openspec/changes/31a-status-log/tasks.md new file mode 100644 index 0000000..811c3ba --- /dev/null +++ b/openspec/changes/31a-status-log/tasks.md @@ -0,0 +1,24 @@ +# Tasks + +## 1. Store and interface + +- [ ] 1.1 `app/activityLog.ts`: the store (entries, jobs, subscribers), `log.info/warn/error(source, key, params?, detail?)`, `startJob(...)` with `update / done / fail / cancelled`, a ring buffer of 500 entries that never drops a running job, `useActivityLog` via `useSyncExternalStore`, `openLog({source?})` and its state, a reset for tests. Tests: entries keep keys and params, progress updates change the job's line in place without adding lines, the buffer drops the oldest finished entries and keeps a running job, the loader state follows jobs longer than 200 ms, `cancelled` and `fail` set the state, subscribers are notified once per change. + +## 2. Status bar and panel + +- [ ] 2.1 `StatusBar` replacing the right part of `MeasurementsBar` (X, Y, Z stay): loader and the current line of the running job with Cancel, the latest message for 4 s then "Ready", counters of warnings and errors, one button that opens the log, a polite live region throttled to one announcement a second, truncation at 390 px; the `busy` prop of `Workspace` and the modes is removed in favour of jobs. Strings in `en.json` and `ru.json`. No artboard exists: built from `ui/` components and `theme.css` tokens, said in the commit; compare in en and ru and at 390 px. Component tests: the line follows a fake job, "Ready" after the message delay, counters, Enter and click open the panel, Cancel calls the job, the live region announces at most once a second, no mode passes its own busy flag. +- [ ] 2.2 `ActivityLogPanel` rendered by `AppShell`: list of entries with time, level, source and text, filters by level and by source with counts, follow and "Jump to latest", Details of a line, Copy (header with version, language, browser) and Clear, Esc and a close button with focus return, a bottom sheet below 768 px, openable from any page through `openLog({source})`. Component tests: filters, following and pausing on scroll, Copy writes the expected text, Clear resets the counters, Esc closes and returns the focus, opening with a source preselects it, the language switch re-renders old lines, key parity. + +## 3. Producers + +- [ ] 3.1 Model generation: `useModel` and `useKeychainModel` run one `generate` job per rebuild (rebuilds within the debounce are one job), report the duration and triangles on done, the validation key and warnings of the `ModelResult` as warning lines, a failure as an error with details. Tests: one job per debounced rebuild, the warning and error lines appear with their keys, a failed build keeps the previous mesh and logs the error. +- [ ] 3.2 Import, storage, photo and export: image, mesh and font import (name, size, downscaled, failure), the memory fallback of `imageStore`, `meshStore` and `fontStore` (a warning once per session), the photo dialog steps (sheet found with the confidence and paper, straightened, silhouettes found with sizes, cutouts created), export (format, printer, plates, overflow, files built). Tests: each producer emits its lines with the right source and level, the storage warning is logged once, no line holds pixels or file contents. + +## 4. Version + +- [ ] 4.1 Bump to 1.3.0: `package.json` and a `CHANGELOG.md` entry `1.3.0 — 31a-status-log`. Test: the existing version test passes with the new number. + +## 5. Verification + +- [ ] 5.1 `npm test` and `npm run build` are green, the dev server starts, no console errors. +- [ ] 5.2 In the browser, en and ru and at 390 px, in Box, Keychain and Cutout: the status bar shows "Generating…" lines while a model is built and "Ready" after, the log lists the generations with durations, an invalid parameter shows a warning with its message, importing an image and exporting a file are logged, the log opens by click and by keyboard, filters, Copy and Clear work, Esc returns the focus, a reload clears the log, nothing is sent to the network. diff --git a/openspec/changes/32-photo-trace-ai/design.md b/openspec/changes/32-photo-trace-ai/design.md index d4441fe..63bd316 100644 --- a/openspec/changes/32-photo-trace-ai/design.md +++ b/openspec/changes/32-photo-trace-ai/design.md @@ -37,9 +37,9 @@ Why the straightened sheet is a good input for a network: it is a top view at tr - **Hybrid edge.** For each silhouette: band = mask dilated by `band` mm (default 1.5) minus mask eroded by `band`. Inside the band the classic paper-colour deviation (`colourMask` from the paper surface fitted by `paperSurface`; `paperSurface` and `colourMask` are called, not changed) classifies pixels; the refined mask is `eroded core ∪ (band ∩ classic object pixels)`. If the classic test marks the whole band as paper or as object on a stretch longer than a few mm (the dark-part-in-shadow case), that stretch keeps the network edge. The refined labels go through the same `traceAlpha` as today. A setting `refine: on|off` (default on) exists in the compare view so the raw network edge can be inspected; it is not a persisted user option beyond the session. - **Compare view.** `CompareOverlay` draws the other engine's silhouette outlines (already traced by `traceSilhouette`) as dashed lines over the straightened view, plus a table "tool: classic mm / AI mm / Δ". It costs one extra segmentation when open, and is the way the user, and the owner while developing, sees where the engines disagree. - **ML runtime.** `src/ml/` is React-free. `matte(rgb, w, h) → Float32 alpha` is the only adapter this change needs. `ml.worker.ts` owns the inference session; `mlClient.ts` mirrors `modelClient.ts` (latest request wins, stale replies dropped); the worker is created lazily so classic users pay nothing, not even the library chunk (dynamic import). Backend: try WebGPU, fall back to WASM single-threaded (no COOP/COEP on Pages); report the backend in the model manager. -- **Model manager (a modal opened from the top bar).** `ModelManager` is a modal dialog opened by an "AI models" button in the `Header` (next to the language and unit switches), visible on every page and independent of any mode; the AI blocks of Cutout and Keychain only link to it ("Models…"). It has **sections by the `kind` of the registry entry**: "Silhouette (mask)" and "Depth", each listing its models with: name, what it is good for (fast / accurate), download size, licence, status (not downloaded / downloading with progress / ready / failed / unavailable on this device with the reason), and the buttons Download, Cancel, Delete; the footer of the modal shows the total space used, "Delete all" and the full log (below). Several models can be downloaded at the same time as separate entries; the user picks the active one per kind (a radio in each section). Weights live on the client only: Cache API under a versioned key (revision hash), with a memory fallback and a notice when the browser refuses storage. The button in the `Header` shows a small indicator while a download or an AI run is in progress. +- **Model manager (a modal opened from the top bar).** `ModelManager` is a modal dialog opened by an "AI models" button in the `Header` (next to the language and unit switches), visible on every page and independent of any mode; the AI blocks of Cutout and Keychain only link to it ("Models…"). It has **sections by the `kind` of the registry entry**: "Silhouette (mask)" and "Depth", each listing its models with: name, what it is good for (fast / accurate), download size, licence, status (not downloaded / downloading with progress / ready / failed / unavailable on this device with the reason), and the buttons Download, Cancel, Delete; the footer of the modal shows the total space used, "Delete all" and a link that opens the shared activity log filtered to AI and model downloads (below). Several models can be downloaded at the same time as separate entries; the user picks the active one per kind (a radio in each section). Weights live on the client only: Cache API under a versioned key (revision hash), with a memory fallback and a notice when the browser refuses storage. The button in the `Header` shows a small indicator while a download or an AI run is in progress. - **Explicit confirmation, no silent download.** There is one code path that starts a download, `requestDownload(model)`, and it always goes through a confirmation dialog in the manager (name, size, source host, licence; "Download" / "Cancel"); nothing prefetches, resumes in the background or downloads as a side effect of any other action. When the AI run button is pressed there is no download at all: **without a ready model of the needed kind the run button is disabled** and a hint under it says "Download a model to use AI" with a link that opens the manager; when the download finishes and a model is chosen, the button becomes active by itself (the state is derived from the model store, so it updates live). A test makes the downloader unreachable except through the confirmed path. -- **AI activity and log.** Every phase of a model download and of an AI run reports an event to a session log (`mlLog`: time, level, an i18n key with parameters, never a ready-made sentence, so the line follows the language of the interface). `AiStatus` is the one component that shows activity: a spinner and **only the current line** (the latest event of the active job, for example "Downloading model: 42 of 98 MB", "Starting the session (WASM)", "Object 2 of 3, second pass", "Building the outline"), with a Cancel button, placed next to the run button of each feature (photo dialog, Keychain silhouette, Keychain depth). While a job is active the run button is replaced by or accompanied by `AiStatus`; at the end the line shows the result ("Done in 5.2 s") or the error. The **full log of the session** (all lines, with time, scrollable, with "Copy" and "Clear") is shown in the model manager modal; downloads of models are logged there too. The log is memory only and never leaves the browser. +- **AI activity and the shared log.** The log itself is the shared activity log of `31a-status-log` (store, jobs, status bar line, log panel); this change only reports to it. Every phase of a model download and of an AI run is a job of source `model` (downloads) or `ai` (runs) with entries as i18n keys with parameters ("Downloading the model: 42 of 98 MB", "Starting the session (WASM)", "Object 2 of 3, second pass", "Building the outline"), progress updates change the line in place, `done` / `fail` / `cancelled` end it. `AiStatus` is a thin component that shows **only the current line of the feature's own job** with a spinner and Cancel next to the run button (photo dialog, Keychain silhouette, Keychain depth); the status bar shows the same line for any running job, and its panel shows the **full log** of the session, so the model manager has no log of its own: it only has a link "Open the log" that opens the shared panel filtered to the sources `model` and `ai`. A download started from the manager on a page without a status bar (a start page) is visible in its row of the manager and in the indicator of its Header button, and the log panel can be opened from the manager there. The log stays in memory and never leaves the browser. - **Licences.** Only models under permissive licences (MIT, Apache-2.0, BSD, CC0) are offered. Non-commercial and copyleft weights are excluded: RMBG-1.4 (Bria, non-commercial) gave the best raw result in the PoC but is left out, because with the mask tuning below the permissive models reach a good result too (decision of the owner after the PoC), and the project stays free of licence caveats. The first download of any model shows its name, size, source host and licence and starts only after the click; the References page and the README list every model with its SPDX id and source. The PoC page keeps RMBG-1.4 marked `referenceOnly` as a quality yardstick; it is not part of the product registry. - **Pinned weights.** The registry (`src/ml/models.ts`) holds per model: id, URL with a commit hash (not `main`), byte size, SHA-256 (verified after download), input size and normalisation, SPDX licence, source. A model enters the registry only with MIT / Apache-2.0 / BSD / CC0. The References page and README list them from this registry (a test checks every registry entry appears). - **Backend per model.** The registry lists for each model the backends it works on and the preferred one, from the PoC: InSPyReNet fp16 and ISNet imgly fp16 are WebGPU-only (fp16 on WASM gives an empty mask or minutes), ormbg and U²-Net fail on WebGPU (`ceil_mode` in MaxPool) and run on WASM, RMBG-1.4 int8 runs on WASM faster than on WebGPU. The manager shows only models that the device can run (WebGPU and `shader-f16` detected at start) and marks the others as unavailable with the reason; a run that fails on the preferred backend is not silently retried on another unless the registry says it works there. diff --git a/openspec/changes/32-photo-trace-ai/proposal.md b/openspec/changes/32-photo-trace-ai/proposal.md index 10e549a..75d7277 100644 --- a/openspec/changes/32-photo-trace-ai/proposal.md +++ b/openspec/changes/32-photo-trace-ai/proposal.md @@ -16,7 +16,7 @@ The AI path is an **alternative**, not a replacement. The current segmenter stay - **Compare view.** A switch that shows the classic and the AI silhouettes over the straightened sheet (outline of the other engine as a dashed line), with the size of each silhouette in mm for both, so the user can pick the better result per photo. - **ML runtime.** A separate worker (`ml.worker.ts`, not the Manifold worker), a client with "latest request wins" like `modelClient.ts`, a model manager, backend choice WebGPU → WASM, weights from a pinned Hugging Face revision. All UI text in en and ru. - **Model manager (a modal of its own).** Opened by a button in the top bar of the app, always available and independent of any mode, so models can be downloaded or deleted at any time. Models are in sections by kind (silhouette mask, depth); each shows size, licence and status, with Download and Delete. A model is **never downloaded silently**: only after an explicit confirmation in the manager. The AI run button is disabled while no suitable model is ready, with a hint to download one, and becomes active when the download finishes. -- **AI activity and log.** While a model downloads or an AI run is going on, a loader and the **current line of the log** (what is happening now, for example "Downloading model: 42 of 98 MB", "Starting session (WASM)", "Object 2 of 3, second pass") are shown next to the run button, and the model manager modal shows the **full log** of the session. +- **AI activity and log.** While a model downloads or an AI run is going on, a loader and the **current line of the log** (what is happening now, for example "Downloading model: 42 of 98 MB", "Starting session (WASM)", "Object 2 of 3, second pass") are shown next to the run button, and every line also goes to the **shared activity log of the app** (`31a-status-log`): the status bar shows the same current line and opens the full log, so there is no separate log in the model manager (it only has a link to it). - **Evaluation harness.** A spike script that runs the baseline and every candidate model on the fixtures and prints per-silhouette IoU and size error in mm; the model for the default AI engine is chosen from its output (task 1), not from the catalogue. ## Capabilities @@ -32,5 +32,5 @@ The AI path is an **alternative**, not a replacement. The current segmenter stay - New dependency on an inference library (`onnxruntime-web` or `@huggingface/transformers`, chosen in task 1); weights are not committed and not bundled (100 MB limit of GitHub Pages), they are fetched on demand. - The classic path's outputs on both fixtures stay byte-identical (checked by an existing test and a new frozen-output test). - Out of scope: click-to-select / positive and negative points with a promptable model (SAM), a follow-up change once the automatic path is measured; depth maps, background removal for Keychain, prompt-to-parameters and other ideas from `BROWSER_AI_APPLICATION.md`; any server-side inference; non-commercial weights (RMBG-1.4/2.0, Depth Anything Base/Large) and AGPL code (Ultralytics); multi-threaded WASM (needs COOP/COEP headers that GitHub Pages does not send); offline pre-download of models; changing the classic segmenter. -- Version 1.3.0 (minor: a new option). -- Depends on: `25a-photo-trace-improvements`, `29-beta-label` (the versioning rule). +- Version 1.4.0 (minor: a new option). +- Depends on: `25a-photo-trace-improvements`, `29-beta-label` (the versioning rule), `31a-status-log` (the shared activity log). diff --git a/openspec/changes/32-photo-trace-ai/specs/cutout-photo-ai/spec.md b/openspec/changes/32-photo-trace-ai/specs/cutout-photo-ai/spec.md index 0f941ac..0f17952 100644 --- a/openspec/changes/32-photo-trace-ai/specs/cutout-photo-ai/spec.md +++ b/openspec/changes/32-photo-trace-ai/specs/cutout-photo-ai/spec.md @@ -36,7 +36,7 @@ On the silhouette step the user SHALL be able to choose the detection engine, Cl - **THEN** the sensitivity control, which belongs to the classic detector, is not offered for it, and the margin still applies ### Requirement: Model manager -The app SHALL have a model manager, a modal window opened by a button in the top bar of the app, available on every page and independent of any mode. It SHALL list the available models in sections by their kind (silhouette mask, depth) with their name, purpose, size, licence and status, and SHALL let the user download, cancel, delete and choose the model used for each kind. Several models SHALL be downloadable and stored side by side. The models SHALL be kept in the browser's storage on the device; the window SHALL show the space they use and offer to delete all of them, and SHALL show the full log of the session. +The app SHALL have a model manager, a modal window opened by a button in the top bar of the app, available on every page and independent of any mode. It SHALL list the available models in sections by their kind (silhouette mask, depth) with their name, purpose, size, licence and status, and SHALL let the user download, cancel, delete and choose the model used for each kind. Several models SHALL be downloadable and stored side by side. The models SHALL be kept in the browser's storage on the device; the window SHALL show the space they use and offer to delete all of them, and SHALL link to the app's activity log for the details of downloads and runs. #### Scenario: Always reachable - **WHEN** the user is on any page of the app @@ -97,7 +97,7 @@ A model SHALL be downloaded only after the user explicitly confirms in the model - **THEN** the model works for the session and a notice says it will be downloaded again next time ### Requirement: AI activity and log -While a model is downloading or an AI run is in progress, the app SHALL show a loader and the current line of the log, describing what is happening now, next to the control that started it, with a way to cancel. When the job ends the line SHALL show the result or the error. The model manager SHALL show the full log of the session with the time of each line, and let the user copy and clear it. The lines SHALL be in the language of the interface, and the log SHALL stay in the browser. +While a model is downloading or an AI run is in progress, the app SHALL show a loader and the current line of the log, describing what is happening now, next to the control that started it, with a way to cancel. When the job ends the line SHALL show the result or the error. These lines SHALL be entries of the app's shared activity log, so the full log of downloads and runs, with the time of each line, is available in the log panel opened from the status bar, and the model manager SHALL link to it. The lines SHALL be in the language of the interface, and the log SHALL stay in the browser. #### Scenario: Current line - **WHEN** an AI run goes through its phases @@ -108,8 +108,8 @@ While a model is downloading or an AI run is in progress, the app SHALL show a l - **THEN** the line shows the downloaded and total size, and the manager shows the progress of that model #### Scenario: Full log -- **WHEN** the user opens the model manager after a run -- **THEN** all lines of the session are listed with their time, newest at the end, and Copy and Clear work +- **WHEN** the user opens the activity log from the status bar, or from the link in the model manager, after a run +- **THEN** the lines of the download and of the run are listed with their time, and the log can be filtered to them #### Scenario: Error - **WHEN** a run or a download fails diff --git a/openspec/changes/32-photo-trace-ai/tasks.md b/openspec/changes/32-photo-trace-ai/tasks.md index 6d7bc42..781fc4c 100644 --- a/openspec/changes/32-photo-trace-ai/tasks.md +++ b/openspec/changes/32-photo-trace-ai/tasks.md @@ -27,15 +27,15 @@ ## 5. UI - [ ] 5.1 `usePhotoTrace`: `engine` in `PhotoSettings` (default `classic`), `aiSegmentation` computed only when needed, an `active` segmentation, AI error falls back to classic with an error state, `computing` covers the AI run. Tests: default is classic and calls no model; switching uses the mocked client; a failing client leaves classic active; closing and reopening keeps the engine. -- [ ] 5.2 `EngineSwitch` on the silhouette step (SegmentedControl Classic | AI, sensitivity hidden for AI) and `ModelManager` modal opened by an "AI models" button in the `Header` (always visible, independent of the mode; with an activity indicator), sections by registry `kind` (Silhouette mask, Depth), each row with name, purpose, size, licence, status (including unavailable with the reason), Download / Cancel / Delete, an active-model radio per kind, used space, "Delete all", the full log at the bottom; a confirmation dialog before every download. The photo dialog links to it ("Models…"); the run button is disabled without a ready model, with the hint "Download a model to use AI" and a link, and becomes active by itself when the download ends. No artboard exists: built from `ui/` components and `theme.css` tokens, said in the commit; compare in en and ru and at 390 px. Component tests: consent before any download, progress, error banner, delete returns to "not downloaded" and asks for consent again, two models side by side, choosing the active one. -- [ ] 5.2b `mlLog` (session log of events: time, level, i18n key with parameters) and `AiStatus` (spinner, only the current line, Cancel) fed by `mlClient` and `modelStore` events for both downloads and runs; the modal shows the full log with Copy and Clear; strings for every event in `en.json` and `ru.json`. Tests: the line follows the events of a fake run in order and shows the result at the end, the log keeps every line with its time, Cancel stops the job and logs it, errors appear in the line and in the log, switching the language re-renders the lines, key parity for every event key, nothing outside the manager calls the downloader (the downloader is reachable only through the confirmed path), the run button follows the model store live (disabled, active after the mocked download, disabled after delete), the `Header` indicator appears while a job is active. +- [ ] 5.2 `EngineSwitch` on the silhouette step (SegmentedControl Classic | AI, sensitivity hidden for AI) and `ModelManager` modal opened by an "AI models" button in the `Header` (always visible, independent of the mode; with an activity indicator), sections by registry `kind` (Silhouette mask, Depth), each row with name, purpose, size, licence, status (including unavailable with the reason), Download / Cancel / Delete, an active-model radio per kind, used space, "Delete all", an "Open the log" link to the shared activity log filtered to AI; a confirmation dialog before every download. The photo dialog links to it ("Models…"); the run button is disabled without a ready model, with the hint "Download a model to use AI" and a link, and becomes active by itself when the download ends. No artboard exists: built from `ui/` components and `theme.css` tokens, said in the commit; compare in en and ru and at 390 px. Component tests: consent before any download, progress, error banner, delete returns to "not downloaded" and asks for consent again, two models side by side, choosing the active one. +- [ ] 5.2b `AiStatus` (spinner, only the current line of the feature's own job, Cancel) and the reporting of downloads and runs to the shared activity log of `31a-status-log` (jobs of source `model` and `ai`, progress in place, done / fail / cancelled), with the strings of every event in `en.json` and `ru.json`. Tests: the line follows the events of a fake run in order and shows the result at the end, the activity log gets the start, the milestones and the end of each job, Cancel stops the job and logs it, errors appear in the line and in the log, switching the language re-renders the lines, key parity for every event key, nothing outside the manager calls the downloader, the run button follows the model store live (disabled, active after the mocked download, disabled after delete), the `Header` indicator appears while a job is active, the manager's link opens the log filtered to AI. - [ ] 5.2a `MaskTuning` block, always visible under the AI engine on the silhouette step: two sliders with plain-language labels and hints ("Edge reach", "Outline smoothness"; threshold and smoothing in code) (smoothing is a length: `LengthField`), per-model defaults from the registry (`maskDefaults`), reset to them, greyscale view of the soft mask, size change per silhouette and the thin-part hint; updates without a model run (cached alpha). Component tests with a mocked `mlClient`: moving a slider changes the silhouette sizes and the mocked client is called once, reset restores the model's defaults, switching model loads its defaults unless the controls were touched, the hint appears when a silhouette shrinks by more than 1 mm, both languages. - [ ] 5.3 `CompareOverlay` and the table of sizes (classic, AI, Δ) in the silhouette step; dashed outlines of the inactive engine over the straightened view; off by default. Tests: toggling draws the second outline set and the table values equal `silhouettes` of both engines; off costs no AI run. - [ ] 5.4 Strings in `en.json` and `ru.json` (engine, consent, progress, errors, compare, References licence list); the References page and README list the registry models. Tests: key parity, a registry-to-References test, the `PhotoTips` and consent text exist in both languages. ## 6. Version -- [ ] 6.1 Bump to 1.3.0: `package.json` and a `CHANGELOG.md` entry `1.3.0 — 32-photo-trace-ai`. Test: the existing version test passes with the new number. +- [ ] 6.1 Bump to 1.4.0: `package.json` and a `CHANGELOG.md` entry `1.4.0 — 32-photo-trace-ai`. Test: the existing version test passes with the new number. ## 7. Verification diff --git a/openspec/changes/33-keychain-ai-silhouette/design.md b/openspec/changes/33-keychain-ai-silhouette/design.md index 7b79e05..a81228d 100644 --- a/openspec/changes/33-keychain-ai-silhouette/design.md +++ b/openspec/changes/33-keychain-ai-silhouette/design.md @@ -25,7 +25,7 @@ A keychain differs from a photo cutout in three ways that shape the design: the - **Persistence.** `imageStore` records get a second kind of entry, `matte`, keyed by `imageId` + `model` + `revision`, with the plane and its size; stored together with the original in the same DB (`gridfinity-keychain`), memory fallback with the existing `persistent` warning. The params store `source`, `ai.model`, `threshold`, `smooth`, `keepLargest`; on load the matte is read and registered. Deleting the model in the manager does not delete the matte; deleting or replacing the image does. If the matte is missing (cleared storage) the section shows "run again" and the silhouette falls back to Brightness until it is run, never to an empty mask. - **Staleness.** The matte is tied to the image and the model: replacing the image drops it; choosing a different model keeps the old matte until the user runs the new one (shown as "matte from "), so a switch never silently re-runs a download-sized job. - **Relation to existing alpha.** If the image already has transparency, the section shows the note "this image has transparency, it is used as is" with AI still selectable (the user may want a different cut), and the AI run ignores the image's alpha as input. SVG is rasterised by the existing path and is not offered AI. -- **UI.** `SilhouetteSection` gets `SegmentedControl` From: Brightness | AI. Under AI: model picker (from the manager, with a link to open it), the Remove-background button (disabled with the hint "Download a model to use AI" and a link to the manager while no model is ready, active as soon as one is) with the shared `AiStatus` next to it (spinner, the current line of the log, Cancel; the full log is in the model manager modal), the shared `MaskTuning` block (edge reach, smoothness, per-model defaults, reset, soft-matte greyscale view, which can also open in the enlarged mask dialog), the keep-largest toggle and the hints. Brightness controls (threshold, invert) are hidden under AI because they have no effect there; close, fill holes, specks and grow stay, since they work on the resulting mask. The mask preview, areas and everything downstream read the resulting silhouette as today. +- **UI.** `SilhouetteSection` gets `SegmentedControl` From: Brightness | AI. Under AI: model picker (from the manager, with a link to open it), the Remove-background button (disabled with the hint "Download a model to use AI" and a link to the manager while no model is ready, active as soon as one is) with the shared `AiStatus` next to it (spinner, the current line of the log, Cancel; the full log is in the shared activity log opened from the status bar), the shared `MaskTuning` block (edge reach, smoothness, per-model defaults, reset, soft-matte greyscale view, which can also open in the enlarged mask dialog), the keep-largest toggle and the hints. Brightness controls (threshold, invert) are hidden under AI because they have no effect there; close, fill holes, specks and grow stay, since they work on the resulting mask. The mask preview, areas and everything downstream read the resulting silhouette as today. - **Hints.** After a run, if the image has more than 2 distinct dominant colours in the border band the hint is silent; if the image is nearly two-tone (a logo on a clean background: Otsu separability high, a border band of one colour) a hint says Brightness is usually better and quicker. Pure text, never blocks. - **Performance.** The run is a few seconds to ~20 s depending on model and backend (see `FINDINGS.md`); the dialog shows progress, the panel stays usable, a new run cancels an old one (latest wins via the client). Huge images are capped for inference only. - **Versioning of params.** `migrateKeychain` fills `source: 'brightness'` and the `ai` defaults for old records, so stored keychains are unchanged; with `source: 'brightness'` the pipeline output equals the current one (regression volumes in the existing tests). diff --git a/openspec/changes/33-keychain-ai-silhouette/proposal.md b/openspec/changes/33-keychain-ai-silhouette/proposal.md index dd4a129..d96c004 100644 --- a/openspec/changes/33-keychain-ai-silhouette/proposal.md +++ b/openspec/changes/33-keychain-ai-silhouette/proposal.md @@ -28,5 +28,5 @@ Keychain Image mode builds its silhouette from a threshold (Otsu or manual) on b - `geometry/keychain.ts` (`SilhouetteParams` gets `source: 'brightness' | 'ai'` and `ai {model, threshold, smooth, keepLargest}`; `buildSilhouette` builds the alpha from the registered matte, then the unchanged pipeline), a matte registry in the worker next to `imageRegistry` (`ModelClient.registerMatte`, params carry `ai.matteId`), `features/keychain/` (`SilhouetteSection` source switch, `AiSilhouette` block, `useMatte`, `imageStore` matte records, migration normalising the new fields), reuse of `32`'s `src/ml/`, `ModelManager`, `MaskTuning`; strings in en and ru; tests. - With `source: 'brightness'` the output equals the current one (regression volumes kept). - Out of scope: Shape mode (no image), depth maps as a relief source, click-to-select or erase/restore brush on the mask, several subjects as separate keychains, background replacement or colour output, upscaling small images, any new model beyond the registry of `32`, Rack and other products. -- Version 1.4.0 (minor: a new option). +- Version 1.5.0 (minor: a new option). - Depends on: `32-photo-trace-ai` (ML runtime, model manager, mask tuning, registry), `11-keychain-relief`, `29-beta-label`. diff --git a/openspec/changes/33-keychain-ai-silhouette/tasks.md b/openspec/changes/33-keychain-ai-silhouette/tasks.md index d66a99c..3b87463 100644 --- a/openspec/changes/33-keychain-ai-silhouette/tasks.md +++ b/openspec/changes/33-keychain-ai-silhouette/tasks.md @@ -22,7 +22,7 @@ ## 5. Version -- [ ] 5.1 Bump to 1.4.0: `package.json` and a `CHANGELOG.md` entry `1.4.0 — 33-keychain-ai-silhouette`. Test: the existing version test passes with the new number. +- [ ] 5.1 Bump to 1.5.0: `package.json` and a `CHANGELOG.md` entry `1.5.0 — 33-keychain-ai-silhouette`. Test: the existing version test passes with the new number. ## 6. Verification diff --git a/openspec/changes/34-keychain-photo-relief/proposal.md b/openspec/changes/34-keychain-photo-relief/proposal.md index bd63ee4..f3e7fba 100644 --- a/openspec/changes/34-keychain-photo-relief/proposal.md +++ b/openspec/changes/34-keychain-photo-relief/proposal.md @@ -33,5 +33,5 @@ The three complement each other, so the relief is built as a **mix of the three* - `geometry/keychain.ts` (`KeychainParams.crop {x, y, w, h}` fractions applied to the image planes before the silhouette; `silhouette.source` gets `'frame'` (extends `33`); `SourceParams.kind` gets `'photo'` with `mix {contour, depth, detail}`, `depthId`, `boost`, `boostRadius` (mm), `levels`, `levelSoftness`; `registerDepth` and `depthRegistry` in the worker; `buildGrid` samples the planes through the brightness path), `geometry/raster/` (`localContrast`, `quantise`, `normaliseDepth`), `src/ml/` registry gets `kind: 'depth'` with external-data support, `features/keychain/` (`CropEditor`, `PhotoReliefBlock`, `useDepth`, depth records in `imageStore`, migration), strings in en and ru, tests. - With the Distance or Brightness source, no crop, a traced silhouette, boost 0 and levels 0, the output equals the current one. - Out of scope: real metric depth or 3D reconstruction, face detection for an automatic crop, line or edge extraction, sculpt or brush editing of the relief, multi-colour output, larger non-permissive depth models, Shape mode. -- Version 1.5.0 (minor: new options). +- Version 1.6.0 (minor: new options). - Depends on: `33-keychain-ai-silhouette`, `32-photo-trace-ai`, `12-keychain-patterns`, `17-keychain-smoothing`, `29-beta-label`. diff --git a/openspec/changes/34-keychain-photo-relief/tasks.md b/openspec/changes/34-keychain-photo-relief/tasks.md index a1b5e2f..7390717 100644 --- a/openspec/changes/34-keychain-photo-relief/tasks.md +++ b/openspec/changes/34-keychain-photo-relief/tasks.md @@ -25,7 +25,7 @@ ## 5. Version -- [ ] 5.1 Bump to 1.5.0: `package.json` and a `CHANGELOG.md` entry `1.5.0 — 34-keychain-photo-relief`. Test: the existing version test passes with the new number. +- [ ] 5.1 Bump to 1.6.0: `package.json` and a `CHANGELOG.md` entry `1.6.0 — 34-keychain-photo-relief`. Test: the existing version test passes with the new number. ## 6. Verification diff --git a/openspec/changes/35-smooth-outline/proposal.md b/openspec/changes/35-smooth-outline/proposal.md index adb2de1..62b9fbb 100644 --- a/openspec/changes/35-smooth-outline/proposal.md +++ b/openspec/changes/35-smooth-outline/proposal.md @@ -25,5 +25,5 @@ In the PoC (`poc/ai/`) a pocket built from fitted curves instead of the raw poly - New pure module `geometry/outlineFit.ts` (resample, corner detection, along-curve smoothing, Schneider fitting, sampling, offset), used by `traceSilhouette` callers in `features/cutout/photo/createCutouts.ts` and by the keychain outline; `segment.ts`, `colour.ts` and the classic detection are not edited, their frozen-output test (`32`, task 2.1) stays green; the `OutlineSource` points of a cutout become the sampled fitted polygon (the stored `points` keep their shape, so saved sessions load unchanged); strings in en and ru, tests. - Out of scope: changing the classic silhouette detection, editing the outline by hand (node dragging), exporting curves as such (STL and 3MF stay polygonal), smoothing of mesh cutouts (STL imports). -- Version 1.6.0 (minor: a new option that also improves existing output). +- Version 1.7.0 (minor: a new option that also improves existing output). - Depends on: `25a-photo-trace-improvements`, `29-beta-label`; works with `32` and `34` (they use it) but does not require them. diff --git a/openspec/changes/35-smooth-outline/tasks.md b/openspec/changes/35-smooth-outline/tasks.md index ca6062a..abc45f2 100644 --- a/openspec/changes/35-smooth-outline/tasks.md +++ b/openspec/changes/35-smooth-outline/tasks.md @@ -13,7 +13,7 @@ ## 3. Version -- [ ] 3.1 Bump to 1.6.0: `package.json` and a `CHANGELOG.md` entry `1.6.0 — 35-smooth-outline`. Test: the existing version test passes with the new number. +- [ ] 3.1 Bump to 1.7.0: `package.json` and a `CHANGELOG.md` entry `1.7.0 — 35-smooth-outline`. Test: the existing version test passes with the new number. ## 4. Verification From d322057d0580e6b5128a90bbb8bc695a88dbeed4 Mon Sep 17 00:00:00 2001 From: skarakovski Date: Thu, 22 Oct 2026 10:45:27 +0700 Subject: [PATCH 33/38] plan: 36-site-refresh (card texts and tags, README, Keychain ring card art, Rack cards at 1 U, margin check; v1.7.1) Co-Authored-By: Claude Sonnet 5.5 --- openspec/ROADMAP.md | 1 + .../changes/36-site-refresh/.openspec.yaml | 2 + openspec/changes/36-site-refresh/design.md | 48 +++++++++++ openspec/changes/36-site-refresh/proposal.md | 33 ++++++++ .../36-site-refresh/specs/app-shell/spec.md | 79 +++++++++++++++++++ .../specs/site-identity/spec.md | 22 ++++++ openspec/changes/36-site-refresh/tasks.md | 25 ++++++ 7 files changed, 210 insertions(+) create mode 100644 openspec/changes/36-site-refresh/.openspec.yaml create mode 100644 openspec/changes/36-site-refresh/design.md create mode 100644 openspec/changes/36-site-refresh/proposal.md create mode 100644 openspec/changes/36-site-refresh/specs/app-shell/spec.md create mode 100644 openspec/changes/36-site-refresh/specs/site-identity/spec.md create mode 100644 openspec/changes/36-site-refresh/tasks.md diff --git a/openspec/ROADMAP.md b/openspec/ROADMAP.md index ddb2f81..7140063 100644 --- a/openspec/ROADMAP.md +++ b/openspec/ROADMAP.md @@ -46,6 +46,7 @@ | 33 | `33-keychain-ai-silhouette` | 32, 11, 29 | v1.5.0. Keychain Image: силуэт по ИИ (удаление фона на браузерной модели), те же ручки порога и сглаживания, один главный объект, результат хранится вместе с картинкой; яркость остаётся по умолчанию | | 34 | `34-keychain-photo-relief` | 33, 32, 12, 17, 29 | v1.6.0. Keychain Image: рельеф из фото как смесь трёх источников формы (контур, глубина Depth Anything, детали — локальный контраст), кроп области (лицо), силуэт «весь кадр» (плашка), пресеты; результат хранится с картинкой | | 35 | `35-smooth-outline` | 25a, 29 | v1.7.0. Общий этап контура для вырезов (классика и ИИ) и Keychain: подгонка кубических кривых Безье с минимумом точек, острые углы сохраняются, зазор по кривой; стенки без ряби, детекция не меняется | +| 36 | `36-site-refresh` | 29 | v1.7.1. Актуализация сайта и README: тексты и теги карточек (без «скоро», «snap», «mask»), README по текущему продукту, превью Keychain из PoC-кольца с меньшим зумом, все превью Rack в 1 U, проверка отступов | ```mermaid gantt diff --git a/openspec/changes/36-site-refresh/.openspec.yaml b/openspec/changes/36-site-refresh/.openspec.yaml new file mode 100644 index 0000000..7cc41bb --- /dev/null +++ b/openspec/changes/36-site-refresh/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-10-22 diff --git a/openspec/changes/36-site-refresh/design.md b/openspec/changes/36-site-refresh/design.md new file mode 100644 index 0000000..d280751 --- /dev/null +++ b/openspec/changes/36-site-refresh/design.md @@ -0,0 +1,48 @@ +# Design + +## Context + +Card texts are i18n keys: `products..{name,desc,tag1,tag2}` for the root page and `start..{desc,tag1,tag2}` for the mode pages, with a parity test between `en.json` and `ru.json`. Illustrations are SVG files rendered by `scripts/render-art.ts` from `generate()` meshes in groups with their own scale (Gridfinity three with one scale, Keychain two, each Rack part alone); the Keychain card is aligned to the box by `BOX_ART_HEIGHT`. `ModeArt.test.tsx` checks the 60 KB budget. + +## Goals / Non-Goals + +**Goals:** +- Texts that say what each card is, in the same style, without placeholders or jargon, in both languages. +- A README that matches the product. +- Illustrations that sit on the same visual line, with the Gridfinity cards as the reference. + +**Non-Goals:** +- New copywriting for workspaces, screenshots, marketing pages. + +## Decisions + +- **Card text rules.** A description is one or two sentences: what you get, then the one thing that makes it useful; it names the input when there is a special one (a photo, an image, a device). Two tags per card, each at most two short words, nouns or noun phrases, no arrows except "image → relief" and "device → shell" where the arrow is the point; no status words (soon, new, beta). Russian: translated, sentence case, no Latin words except names; "keystone" is written as a name (`keystone`) in the same form on every card. +- **Proposed texts (to be confirmed by the owner in the first task).** + + | Card | EN description | EN tags | RU description | RU tags | + |---|---|---|---|---| + | Gridfinity Master | Parametric Gridfinity bins, baseplates and tool cutouts, with pockets traced from a photo. | bins · baseplates; tool cutouts | Параметрические контейнеры и плиты Gridfinity, вырезы под инструменты, в том числе по фото. | контейнеры · плиты; вырезы под инструмент | + | Keychain Master | Turn a picture or a plain shape into a relief keychain: silhouette, height pattern, text, ring mount, two halves with pins. | image → relief; shapes · text | Брелок-рельеф из картинки или простой фигуры: силуэт, узор высоты, надпись, крепление под кольцо, две половинки со штифтами. | картинка → рельеф; фигуры · надпись | + | Rack Master | Parts for a 10-inch rack: panels with patterns, enclosures around a device and shelves, with standard ear holes. | 1–10 U; panels · cases · shelves | Детали для 10-дюймовой стойки: панели с узорами, корпуса под устройство и полки со стандартными отверстиями в ушах. | 1–10 U; панели · корпуса · полки | + | Cutout | A bin with pockets shaped to your tools: from a photo on a sheet of paper, an STL, a circle or a rectangle. | from photo; STL import | Контейнер с гнёздами под ваши инструменты: по фото на листе бумаги, из STL, круг или прямоугольник. | по фото; импорт STL | + | Keychain Image | A relief keychain from a picture: a drawing, logo or emblem becomes a silhouette with a height pattern, text and a ring mount. | image → relief; text · mounts | Брелок-рельеф по картинке: рисунок, логотип или эмблема становятся силуэтом с узором высоты, надписью и креплением под кольцо. | картинка → рельеф; надпись · крепление | + | Rack Enclosure | A front plate with rack ears and a shell around your device: mini PC, switch or router. Window, `keystone` sockets, holes and vents. | device → shell; keystone sockets | Передняя панель с ушами и оболочка вокруг устройства: мини-ПК, коммутатор или роутер. Окно, гнёзда `keystone`, отверстия и вентиляция. | устройство → корпус; гнёзда keystone | + + Box, Baseplate, Keychain Shape, Rack Panel and Rack Shelf keep their descriptions and get tags in the same style ("grid × height", "compartments"; "fractional cells", "split to the bed"; "shapes", "rim"; "1–10 U", "patterns"; "plate + shelf", "ventilated"). The final wording is settled in task 1.1. +- **README structure.** Title and one-line pitch (unchanged); a status line (beta, current version, link to the changelog); Why this exists (the motivation text, identical to the site); What you get: the product table with every mode and its main options, the highlights; Privacy: the work stays in the browser, photos and files are never uploaded, visit counting only after consent and the repository link to its settings; Quick start and commands; Deploy; How it works; Project layout (listing the folders that exist, such as `poc/`); Roadmap and changes (`openspec/ROADMAP.md`, `CHANGELOG.md`); References; License. The statement "No server. No account." is kept for the app and followed by the privacy note. A test (or the existing `site.test.ts`) checks that the motivation text in the README equals the site's. +- **Keychain art from the ring emblem.** `scripts/art/ring.jpg` is the PoC picture; `npm run art` decodes it with `jpeg-js` (already a dev dependency), makes a `GrayImage`, runs the Keychain Image pipeline (`generate('keychain')`) with default relief and size about 50 mm, the large enclosed area opened (the emblem's hole) and the small ones between strokes filled (the same rule as the areas of change 18), and draws it like the other keychain art, with the existing smoothing. No eyelet is added: the emblem already has a through hole, and an eyelet on a spiky silhouette looks accidental; the alternative is a small eyelet at the top spike, rejected for the look. The paw module `geometry/art/paw.ts` stays for tests until it is no longer used, then is removed. +- **Scale and air.** The Gridfinity cards are the reference: their drawing keeps the margin that the owner is happy with. The Keychain Image card is scaled so its drawn height is a fixed fraction of the box drawing's (`KEYCHAIN_ART_RATIO`, about 0.8, set by eye against the cards in the browser and written down) instead of equal to it, and the width limit stays. Every illustration is measured by `projectedSize` in the script and written to `src/app/art/manifest.json` as fractions of the 250 × 165 field; the test asserts at least 6 % of free space on each side for every card and that the Keychain Image drawing is not taller than the Gridfinity box. +- **Rack at 1 U.** The panel is `defaultRackPanel` with `units: 1` and the Skadis pattern (the 1 U plate has less room, so the pattern's region is checked: a plate that shows no pattern element is not acceptable, the pattern pitch may be reduced for the art only); the enclosure uses a small device (about 25 mm high so that the shell fits 1 U) with one keystone; the shelf uses `units: 1` with the default shelf and side walls capped to what a 1 U plate allows. The three Rack drawings share one scale chosen so the widest of them (the 254 mm width) fits the field with the Gridfinity margin; they are drawn in one group of the script, which also makes their sizes comparable. +- **Weight and tokens.** The 60 KB budget and the colour tokens stay; if a 1 U drawing is heavier, the smoothing or the trace tolerance is raised for it. + +## Risks / Trade-offs + +- [The ring is a drawing, so the render is a flat plaque with the emblem's silhouette] → the relief profile (bevel) and the two tone passes give it volume; if it looks flat the default profile "dome" is used for the art only. +- [A 1 U panel is a very thin strip and looks small on the card] → the shared Rack scale and the isometric view keep it readable; the card height does not change; the check is by eye in the browser. +- [Texts drift again] → the style rules are in the spec; a test keeps the tags to two per card and at most two words each, and forbids the words "soon" and "скоро" on cards. +- [The README repeats the specs] → it stays a summary with links, not a copy. + +## Open Questions + +- The exact wording of the cards and the README is the owner's call; the table above is the proposal. +- Whether the ring eyelet question should be revisited after seeing the render. diff --git a/openspec/changes/36-site-refresh/proposal.md b/openspec/changes/36-site-refresh/proposal.md new file mode 100644 index 0000000..c91647c --- /dev/null +++ b/openspec/changes/36-site-refresh/proposal.md @@ -0,0 +1,33 @@ +# Proposal + +## Why + +The site and the README were written when PrintKit had three Gridfinity modes and a panel. Since then it has gained Cutout from a photo and a circle, Keychain Image and Shape with text, areas and two halves, Rack Enclosure, Shelf and the Kumiko patterns, a beta label and a versioning rule. The texts did not follow: + +- the Keychain product card still carries the tag "soon", the Rack product card describes panels only, the Cutout card tag "snap" and the Keychain Image tag "mask" mean nothing to a newcomer, and the Russian cards mix Latin terms ("keystone"); +- the README's product table misses most of the modes and options, says nothing about the beta status, the version, the changelog or the analytics banner, and "No server. No account." needs the honest footnote that visit counting is optional and asks consent; +- the card illustrations are uneven: the Keychain Image card shows a paw that fills the whole field while the Gridfinity cards leave air around the part, and all three Rack cards show a 2 U part although the common case, and the default of the shelf, is 1 U. + +## What Changes + +- **Texts of the start pages (en and ru).** Rewrite the descriptions and tags of the three product cards and of every mode card so that each states what the part is and what is special about it, with no placeholders ("soon") and no private jargon ("snap", "mask"); two short tags per card, in the same style everywhere; Russian terms translated, a Latin term kept only when it is the name of a thing (Gridfinity, Skadis, keystone) and then in the same form on every card. The tagline, the title and the motivation text are checked against the current products. +- **README.** Bring it up to date: what is in each product and mode, the beta status and where the version and the changelog are, a short privacy note (the work stays in the browser; visit counting only after consent), the commands, the layout including folders that exist (for example `poc/`), the link to the roadmap, the references, and the badges. The motivation text stays identical to the one on the site. +- **Keychain card illustration.** Replace the paw by the ring emblem from the PoC (`poc/ai/samples/ring.jpeg`, an outlined emblem with a large through hole and sharp spikes), rendered by `npm run art` from that picture through the real Keychain Image pipeline, and reduce its scale so that it leaves the same air as the Gridfinity illustrations (which are the reference) instead of filling the field. +- **Rack card illustrations.** All three at 1 U: a 1 U Skadis panel, a 1 U enclosure around a small device, a 1 U shelf; one scale for the three Rack cards (the rack is the same width in all of them) with margins like the Gridfinity cards. +- **A check for the margins.** `npm run art` records the drawn size of every illustration as a fraction of the card field in a small manifest, and a test keeps every illustration inside a margin and the Keychain illustration no larger than the Gridfinity box. + +## Capabilities + +### New Capabilities + +### Modified Capabilities +- `app-shell`: the card texts and the start page illustrations. +- `site-identity`: the README must be accurate and consistent with the site. + +## Impact + +- `src/i18n/en.json` and `ru.json` (the `start.*` and `products.*` keys of the cards), `README.md`, `scripts/render-art.ts` (the ring source, the Rack models at 1 U, the scales), `scripts/art/ring.jpg` (the source picture), `src/app/art/{keychainImage,rackPanel,rackCase,rackShelf}.svg`, a new `src/app/art/manifest.json`, `ModeArt.test.tsx` and a margin test; `CLAUDE.md` (the art paragraph). +- No change to geometry, modes or routes. +- Out of scope: new features, screenshots in the README, the product names, the footer and consent texts, texts of the workspaces (panels, sections), texts of the AI changes (they add their own when they land), redrawing the Gridfinity or Keychain Shape illustrations. +- Version 1.7.1 (patch: content and illustrations, no new capability). +- Depends on: `29-beta-label` (the versioning rule); the art uses geometry that is already in the app. diff --git a/openspec/changes/36-site-refresh/specs/app-shell/spec.md b/openspec/changes/36-site-refresh/specs/app-shell/spec.md new file mode 100644 index 0000000..aebf7aa --- /dev/null +++ b/openspec/changes/36-site-refresh/specs/app-shell/spec.md @@ -0,0 +1,79 @@ +# Spec Delta + +## MODIFIED Requirements + +### Requirement: Start page illustrations +Each mode card on the Gridfinity, Keychain and Rack start pages and each product card on the root start page SHALL show an isometric illustration that is a render of an actual model produced by the app (a small part with default settings), not a schematic drawing or an icon. The Gridfinity product card SHALL show the box; the Keychain product card SHALL show an image-mode keychain; the Keychain Image and Shape cards SHALL each show a render of their own mode. The image-mode keychain illustration SHALL show a keychain made from a drawing with a complex outline (the ring emblem with sharp spikes and a large through hole), not a circle or other plain geometric figure. The Rack illustrations SHALL show 1 U parts. Illustrations SHALL be static vector images, SHALL follow the app's colour tokens, and SHALL NOT require WebGL, geometry computation or network requests on the start page. + +#### Scenario: Renders match the editor +- **WHEN** the Baseplate card is shown +- **THEN** its illustration is a 2×2 baseplate with the standard cell pockets and the default corner fastener holes, as the editor produces by default + +#### Scenario: Product cards +- **WHEN** the root start page is shown +- **THEN** the Gridfinity card shows the box illustration and the Keychain card shows the image-mode keychain illustration + +#### Scenario: Keychain mode cards +- **WHEN** the Keychain start page is shown +- **THEN** the Image card shows a relief keychain made from the ring emblem, with its through hole open and its spikes sharp, and the Shape card shows a keychain with a geometric outline and rim, each a render of what that mode produces + +#### Scenario: Rack cards at 1 U +- **WHEN** the Rack start page is shown +- **THEN** the Panel, Enclosure and Shelf illustrations each show a part one rack unit high, drawn at one common scale + +#### Scenario: Smooth outline +- **WHEN** the Keychain Image illustration is viewed at 200 % zoom or on a high-density display +- **THEN** its outer outline and the edges between shaded areas are smooth curves, with no visible pixel stairs + +#### Scenario: Cards stay level +- **WHEN** the root start page is shown +- **THEN** the drawn height of the Keychain illustration is not greater than that of the Gridfinity box illustration, so the Keychain card does not look larger than its neighbour + +#### Scenario: Same air on every card +- **WHEN** the start pages are shown +- **THEN** every illustration leaves a margin of at least 6 % of the card's picture field on each side, the Keychain Image illustration is not taller than the Gridfinity box illustration, and none looks larger than its neighbours in the same row + +#### Scenario: No runtime geometry +- **WHEN** the start page is opened +- **THEN** no worker is started, no WASM is loaded and no WebGL context is created + +#### Scenario: Theme tokens +- **WHEN** a colour token of the theme changes +- **THEN** the illustrations change colour with it without regenerating the image files + +#### Scenario: Crisp on any display +- **WHEN** the page is shown on a high-density display or zoomed to 200 % +- **THEN** illustration edges stay sharp + +#### Scenario: Decorative +- **WHEN** a screen reader reads a card +- **THEN** the illustration is skipped and the card is announced by its title and description + +#### Scenario: Weight +- **WHEN** the production build is made +- **THEN** each illustration is at most 60 KB uncompressed + +## ADDED Requirements + +### Requirement: Card texts +Every product and mode card SHALL have a localized description and exactly two short tags. The description SHALL say what the part is and name the special input when there is one (a photo, an image, a device); each tag SHALL be at most two short words, a noun or a noun phrase. Cards SHALL NOT show status words such as "soon", "new" or "beta" as tags, and SHALL NOT use internal jargon. In Russian, terms SHALL be translated except for names of things (Gridfinity, Skadis, keystone), which SHALL be written the same way on every card. The descriptions of the product cards SHALL cover the modes that exist. + +#### Scenario: No placeholders +- **WHEN** the root start page is shown +- **THEN** no card carries a tag that says it is coming soon, and the Keychain card describes what it makes + +#### Scenario: Rack card covers its modes +- **WHEN** the root start page is shown +- **THEN** the Rack card's description mentions panels, enclosures and shelves + +#### Scenario: Cutout card +- **WHEN** the Gridfinity start page is shown +- **THEN** the Cutout card says that pockets can be made from a photo of the tools on a sheet of paper as well as from an STL, a circle or a rectangle + +#### Scenario: Two short tags +- **WHEN** any card is shown +- **THEN** it has exactly two tags of at most two words each, in English and in Russian + +#### Scenario: Same term everywhere +- **WHEN** the Russian cards are read +- **THEN** a name such as keystone is written the same way on every card where it occurs diff --git a/openspec/changes/36-site-refresh/specs/site-identity/spec.md b/openspec/changes/36-site-refresh/specs/site-identity/spec.md new file mode 100644 index 0000000..ddc1991 --- /dev/null +++ b/openspec/changes/36-site-refresh/specs/site-identity/spec.md @@ -0,0 +1,22 @@ +# Spec Delta + +## ADDED Requirements + +### Requirement: README accuracy +The README SHALL describe the app as it is: every product with its modes and their main options, the beta status with where the version and the changelog are, a privacy note saying that the user's work and files stay in the browser and that visit counting happens only after the user accepts it, the commands, the layout of the folders that exist, a link to the roadmap, the references and the licence. Its motivation text SHALL be identical to the one on the site. The statement that there is no server and no account SHALL be followed by the privacy note. + +#### Scenario: Products and modes +- **WHEN** the README is read +- **THEN** its product table lists every mode of the app, including Cutout from a photo, Keychain Image and Shape, and Rack Panel, Enclosure and Shelf + +#### Scenario: Status and privacy +- **WHEN** the README is read +- **THEN** it says that the app is in beta, where the version and the changelog are, and that visit counting needs consent while photos and files never leave the browser + +#### Scenario: Same motivation text +- **WHEN** the motivation text of the README and of the site are compared +- **THEN** they are identical + +#### Scenario: Existing folders only +- **WHEN** the layout section of the README is read +- **THEN** every folder it lists exists in the repository diff --git a/openspec/changes/36-site-refresh/tasks.md b/openspec/changes/36-site-refresh/tasks.md new file mode 100644 index 0000000..80046c9 --- /dev/null +++ b/openspec/changes/36-site-refresh/tasks.md @@ -0,0 +1,25 @@ +# Tasks + +## 1. Texts + +- [ ] 1.1 Settle the wording of the card descriptions and tags with the owner from the table in `design.md` (three product cards and every mode card, en and ru) and write the result into `design.md`. Verify: the owner confirms the table. +- [ ] 1.2 Update `products.*` and `start.*` in `en.json` and `ru.json` (descriptions and two tags each, no "soon", Russian terms translated, `keystone` written one way). Tests: key parity, every card has exactly two tags of at most two words, the words "soon" and "скоро" do not occur on cards, the Rack product description mentions panels, enclosures and shelves, the Cutout card mentions a photo. + +## 2. README + +- [ ] 2.1 Rewrite `README.md` after the structure in `design.md`: status line with the version and the changelog link, the motivation text identical to `site.motivation`, the full product and mode table, the privacy note, the commands, the layout of existing folders, the roadmap link, the references. Tests: the motivation text in the README equals the site's (extend `site.test.ts`), every folder named in the layout exists, the README names every mode of `app/modes.tsx`. + +## 3. Illustrations + +- [ ] 3.1 Keychain card: add `scripts/art/ring.jpg` (the PoC `ring.jpeg`), make `npm run art` render it through `generate('keychain')` (decoded with `jpeg-js`, size about 50 mm, the large area open and the small ones filled), drop the paw from the script (keep `paw.ts` until unused), and scale it with `KEYCHAIN_ART_RATIO` (about 0.8 of the box drawing's height, set by eye against the Gridfinity cards). Verify: `npm run art` writes `keychainImage.svg`, the weight is within 60 KB, the card looks level with the Gridfinity cards in the browser (en and ru, 390 px). +- [ ] 3.2 Rack cards at 1 U: panel `units: 1` with the Skadis pattern showing, a small device (about 25 mm high) in the enclosure with one keystone, shelf `units: 1`; the three drawn in one group at one scale with the Gridfinity margin. Verify: `npm run art` writes the three files within 60 KB, all three show a 1 U part, compared in the browser. +- [ ] 3.3 `npm run art` writes `src/app/art/manifest.json` (every illustration's drawn width and height as fractions of the 250 × 165 field); a test asserts at least 6 % free space on each side of every illustration and that the Keychain Image drawing is not taller than the Gridfinity box. Update the art paragraph of `CLAUDE.md`. Tests: the margin test, the existing weight test. + +## 4. Version + +- [ ] 4.1 Bump to 1.7.1: `package.json` and a `CHANGELOG.md` entry `1.7.1 — 36-site-refresh`. Test: the existing version test passes with the new number. + +## 5. Verification + +- [ ] 5.1 `npm test` and `npm run build` are green, the dev server starts, no console errors. +- [ ] 5.2 In the browser, en and ru and at 390 px: the root page and the three product start pages show the new texts and tags, no placeholder, the Keychain Image card shows the ring emblem with its hole open and the Rack cards show 1 U parts, the card rows look level and have the same air, the README reads correctly on GitHub. From 40faf607c4585073d834e10d344e01ac0586b72e Mon Sep 17 00:00:00 2001 From: skarakovski Date: Thu, 22 Oct 2026 12:11:03 +0700 Subject: [PATCH 34/38] plan: 37-resource-monitor (honest CPU/GPU/memory figures for rebuild and AI runs, diagnostics mode, no FPS; v1.8.0) Co-Authored-By: Claude Sonnet 5.5 --- openspec/ROADMAP.md | 1 + .../37-resource-monitor/.openspec.yaml | 2 + .../changes/37-resource-monitor/design.md | 45 +++++++ .../changes/37-resource-monitor/proposal.md | 32 +++++ .../specs/resource-monitor/spec.md | 125 ++++++++++++++++++ openspec/changes/37-resource-monitor/tasks.md | 28 ++++ 6 files changed, 233 insertions(+) create mode 100644 openspec/changes/37-resource-monitor/.openspec.yaml create mode 100644 openspec/changes/37-resource-monitor/design.md create mode 100644 openspec/changes/37-resource-monitor/proposal.md create mode 100644 openspec/changes/37-resource-monitor/specs/resource-monitor/spec.md create mode 100644 openspec/changes/37-resource-monitor/tasks.md diff --git a/openspec/ROADMAP.md b/openspec/ROADMAP.md index 7140063..13fd009 100644 --- a/openspec/ROADMAP.md +++ b/openspec/ROADMAP.md @@ -47,6 +47,7 @@ | 34 | `34-keychain-photo-relief` | 33, 32, 12, 17, 29 | v1.6.0. Keychain Image: рельеф из фото как смесь трёх источников формы (контур, глубина Depth Anything, детали — локальный контраст), кроп области (лицо), силуэт «весь кадр» (плашка), пресеты; результат хранится с картинкой | | 35 | `35-smooth-outline` | 25a, 29 | v1.7.0. Общий этап контура для вырезов (классика и ИИ) и Keychain: подгонка кубических кривых Безье с минимумом точек, острые углы сохраняются, зазор по кривой; стенки без ряби, детекция не меняется | | 36 | `36-site-refresh` | 29 | v1.7.1. Актуализация сайта и README: тексты и теги карточек (без «скоро», «snap», «mask»), README по текущему продукту, превью Keychain из PoC-кольца с меньшим зумом, все превью Rack в 1 U, проверка отступов | +| 37 | `37-resource-monitor` | 31a, 32, 29 | v1.8.0. Вкладка «Ресурсы» в панели лога: честные показатели CPU/GPU/памяти для ресурсоёмких случаев (перестроение и отрисовка модели, ИИ на WASM и WebGPU) с пометкой «измерено / оценка / недоступно», отчёт по каждой задаче, режим диагностики (профилирование GPU); без FPS | ```mermaid gantt diff --git a/openspec/changes/37-resource-monitor/.openspec.yaml b/openspec/changes/37-resource-monitor/.openspec.yaml new file mode 100644 index 0000000..f56fe13 --- /dev/null +++ b/openspec/changes/37-resource-monitor/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-10-23 diff --git a/openspec/changes/37-resource-monitor/design.md b/openspec/changes/37-resource-monitor/design.md new file mode 100644 index 0000000..2bea3be --- /dev/null +++ b/openspec/changes/37-resource-monitor/design.md @@ -0,0 +1,45 @@ +# Design + +## Context + +`31a` adds the shared activity log with jobs (`generate`, and later `ai` and `model` jobs from `32`), the status bar line and the log panel. `32` adds the ML runtime (a worker running ONNX Runtime Web on WASM or WebGPU), the registry with the backend of each model, the model store (Cache API) and the model manager. The viewport draws with three.js on WebGL, on demand. The geometry runs in a Manifold WASM worker. + +## What a page can and cannot know + +Checked in Chrome 152 on Apple Silicon (the owner's device): `PressureObserver` with source `cpu`; `longtask` and `long-animation-frame` entry types; the WebGPU adapter with `timestamp-query`, `shader-f16` and `subgroups` and `adapter.info` (vendor `apple`, architecture `metal-3`); `EXT_disjoint_timer_query_webgl2`; `performance.memory`; `navigator.hardwareConcurrency` (10) and `deviceMemory` (16). Not available: `measureUserAgentSpecificMemory` (needs cross-origin isolation, which GitHub Pages does not give), any GPU or per-core CPU utilisation, VRAM, temperature. Firefox and Safari lack Compute Pressure, timer queries and `performance.memory`, so the view degrades to what the app itself measures. + +## Goals / Non-Goals + +**Goals:** +- For the two heavy cases (rebuild and draw of a model, AI run on WASM or WebGPU) show honest numbers with their provenance. +- Help the user choose a backend and a model, and understand a slow run. + +**Non-Goals:** +- Real utilisation percentages, frame rate, automatic adaptation, telemetry. + +## Decisions + +- **Provenance on every value.** `ResourceValue = {value, unit, source: 'measured' | 'estimated' | 'unavailable', note?}`; the UI renders a small label and a tooltip ("estimated: threads × busy time"), and the copy text keeps them. A collector that the browser does not support returns `unavailable` with the reason; the view never hides a row silently. +- **Job accounting (all browsers).** `modelClient` and `mlClient` already know when a request starts and when its reply arrives; the jobs of `31a` carry `{start, end, backend, threads}` and the report is derived: duration, busy share of the worker over the job (busy time of the request over the wall time of the job, since a worker handles one request at a time) and, for WASM with `n` threads, an estimated core-equivalent load `busy × n` capped at `n` (labelled estimated: ORT's internal threads are not observable). For the editor: triangles of the result and the rebuild time. +- **Main thread.** A `PerformanceObserver` for `longtask` (and `long-animation-frame` where present) accumulates blocked time; the view shows "main thread blocked X % of the last 10 s" and the longest block, which tells whether the page stayed responsive while a job ran. Chrome and Edge only; elsewhere unavailable. +- **System CPU pressure.** `new PressureObserver(cb, {sampleInterval: 1000}).observe('cpu')` in secure contexts (HTTPS, localhost) shows the level and the time spent in each level during a job; it is a coarse, whole-system signal (the label says so) and is created only while the Resources view is open or a job runs, to avoid keeping an observer around. +- **GPU for AI on WebGPU.** The adapter's `info` and `limits` are read once (name, architecture, `maxBufferSize`). In diagnostics mode the runtime's WebGPU profiling (timestamp queries; ORT Web's `env.webgpu.profiling` with an `ondata` callback, to be verified against the pinned ORT version in task 1) accumulates kernel start and end times; the report shows the GPU time of the run and the five slowest kernels, and the ratio of GPU time to wall time. The profiling slows the run, so it is off by default; when the feature is missing the report says unavailable. GPU memory is an estimate: bytes of the weights plus the largest activations seen in the inputs and outputs of the model (labelled estimated), never a reading. +- **GPU for the editor render.** `renderer.info` (triangles, draw calls, geometries, textures) is exact. A timer query around the render (`EXT_disjoint_timer_query_webgl2`) gives the GPU time of the last draw where available (Chrome), read a few frames later and discarded when the disjoint flag is set; the CPU time to submit the draw is measured with `performance.now()`. Because the viewport renders on demand, these are "last render" values, not a running rate. +- **Memory.** `navigator.storage.estimate()` for the quota and the usage by the origin, with the model cache size taken from the model store; `performance.memory.usedJSHeapSize` where present; the byte length of the WASM memory of the modules we own when the runtime exposes it; the model manager of `32` shows the same cache numbers. +- **Diagnostics mode.** One persisted switch (`gridfinity.diagnostics`, key kept in the project's prefix), default off, that enables the costly collectors (kernel profiling, timer queries) and the continuous sampling of pressure and long tasks; its label says "slows down AI runs a little". +- **UI.** A "Resources" tab in the log panel of `31a`: groups CPU, GPU, Memory; a table "Last jobs" (what, backend, duration, busy share, GPU time when measured, triangles for rebuilds) taken from the job reports; values refresh once a second while a job runs and when the tab is opened; every number carries its provenance label; the browser support is summarised in one line ("Compute Pressure: not available in this browser"). On a phone the groups stack. The AI status line of `32` names the backend and threads ("WebGPU", "WASM ×4"). Copy of the log panel includes the resources report when the tab is shown. +- **Privacy.** Adapter name and architecture are shown to the user and included in the copied text only when the user presses Copy; nothing is sent anywhere, and no collector runs in the start pages. +- **Independent of the AI changes.** The job accounting, the viewport and memory parts work with `31a` alone; the GPU-for-AI part appears when `32` provides the runtime hook, so tasks are ordered to keep each part testable. + +## Risks / Trade-offs + +- [Proxies mistaken for utilisation] → provenance labels, no percentages unless measured, the wording "estimated" for core-equivalents and GPU memory. +- [Profiling changes what it measures] → diagnostics mode off by default, the label says it slows runs, kernel timing is aggregated per run. +- [APIs differ between browsers and versions] → each collector is feature-detected and returns `unavailable` with a reason; tests use fakes for both cases. +- [Observers leaking] → pressure and long-task observers exist only while needed and are disconnected on close; tested. +- [ORT's profiling hook changes between versions] → the ORT version is pinned in `32`; task 1 checks the hook on that version in the PoC before the work starts. + +## Open Questions + +- Whether to also offer a "benchmark" button that runs a fixed small inference on each backend and recommends one; left out of this change. +- Whether the AI status line should show GPU time live (needs profiling on); proposed only in the diagnostics mode. diff --git a/openspec/changes/37-resource-monitor/proposal.md b/openspec/changes/37-resource-monitor/proposal.md new file mode 100644 index 0000000..f5b023b --- /dev/null +++ b/openspec/changes/37-resource-monitor/proposal.md @@ -0,0 +1,32 @@ +# Proposal + +## Why + +Two kinds of work in PrintKit are heavy: rebuilding and drawing a model in the editor (CSG in a WebAssembly worker, then a three.js render), and running a neural model in the browser (ONNX Runtime Web on WASM threads or on WebGPU). The user cannot tell what the machine is doing: whether a slow AI run is slow because of the WASM backend, because the GPU is busy, because the browser throttles, or because the model is simply big; whether a heavy model makes the editor sluggish; how much memory the loaded models take. The choice between WASM and WebGPU models (`32`) is made blind. + +A web page cannot read the real load of the CPU or the GPU (there is no such API, on purpose). It can measure honest proxies for exactly these two cases, and label each as measured, estimated or unavailable. This change shows them. + +## What Changes + +- **A "Resources" view** in the activity log panel of `31a-status-log` (a tab next to the log) with three groups, CPU, GPU and Memory, plus the report of the last jobs. Each value says whether it is measured, estimated or not available in this browser. Nothing is shown as a percentage unless it is one. +- **Per-job report.** Every job of the log (model rebuild, AI run, model download) gets a short resource line when it ends: duration, backend (WASM threads or WebGPU), thread count, how much of the time the worker was busy, and for the editor the triangles drawn. The AI status line names the backend ("WebGPU", "WASM ×4"). +- **CPU.** Worker busy share over the job (an estimate of load: threads × busy time), main-thread blocked time (long tasks) to say whether the page stayed responsive, and the system CPU pressure level (nominal, fair, serious, critical) where the browser offers it (Chrome and Edge). No per-core percentages. +- **GPU.** For AI on WebGPU, GPU time of the run from the runtime's profiling (timestamp queries) in a **diagnostics mode**, with the slowest kernels; the adapter's name and architecture; an estimate of the GPU memory a model needs (weights plus tensors). For the editor's render, the cost of a draw (triangles, draw calls, CPU submit time and, where the browser supports it, GPU time from a timer query). No GPU load percentage. +- **Memory.** Size of the model files in the cache and the browser's quota (`storage.estimate`), the JS heap where the browser reports it, the memory of the WASM modules we own when the runtime exposes it. +- **Diagnostics mode.** A switch, off by default, that turns on the profiling that costs speed (kernel timing, timer queries) and says so next to the switch. +- **No FPS.** The editor does not animate, so frame rate is not shown. +- **Local only.** All values stay on the device; the report is copied only when the user presses Copy. + +## Capabilities + +### New Capabilities +- `resource-monitor`: the resources view, per-job resource reports and the diagnostics mode. + +### Modified Capabilities + +## Impact + +- New `src/app/resources/` (React-free collectors: `jobs.ts` busy accounting, `pressure.ts` Compute Pressure, `longTasks.ts`, `gpuInfo.ts`, `memory.ts`, `render.ts` for the viewport, a small `ResourceSnapshot` type with `measured | estimated | unavailable` per value), a `ResourcesTab` in the log panel of `31a`, the job interface of `31a` gets an optional `report` field, `modelClient` and `mlClient` report busy time and backend, the ML runtime of `32` gets an optional profiling hook, `Viewport` reports `renderer.info` and an optional timer query, strings in en and ru, tests. +- Out of scope: frame rate and anything about animation, real CPU or GPU load percentages (not available to a page), temperature, power and battery, per-process OS numbers, sending any measurement anywhere, adapting the app automatically to the load (choosing a model or a backend by itself), a benchmark suite. +- Version 1.8.0 (minor: a new capability). +- Depends on: `31a-status-log` (the log, jobs, status bar and panel), `32-photo-trace-ai` (the ML runtime, backends and model store), `29-beta-label` (the versioning rule). diff --git a/openspec/changes/37-resource-monitor/specs/resource-monitor/spec.md b/openspec/changes/37-resource-monitor/specs/resource-monitor/spec.md new file mode 100644 index 0000000..99a4260 --- /dev/null +++ b/openspec/changes/37-resource-monitor/specs/resource-monitor/spec.md @@ -0,0 +1,125 @@ +# Spec Delta + +## Purpose +Defines the resources view that shows, with honest labels, how the heavy work of the app, rebuilding and drawing a model and running AI models on WASM or WebGPU, uses the processor, the graphics card and the memory. + +## ADDED Requirements + +### Requirement: Resources view +The log panel SHALL have a Resources view with groups for the processor, the graphics card and the memory, and a list of the last jobs with what they were, the backend, the duration and their resource figures. The view SHALL refresh while a job runs and when it is opened. It SHALL NOT show the frame rate. + +#### Scenario: Open +- **WHEN** the user opens the log panel and chooses Resources +- **THEN** the processor, graphics and memory groups and the list of recent jobs are shown + +#### Scenario: Live while a job runs +- **WHEN** an AI run is in progress and the view is open +- **THEN** its values are refreshed about once a second + +#### Scenario: Recent jobs +- **WHEN** a model has been rebuilt and an AI run has finished +- **THEN** each appears in the list with its duration and backend, and the rebuild with its triangle count + +### Requirement: Honest figures +Every value in the view SHALL say whether it was measured, estimated or is not available in this browser, with a short reason for the last two. A value SHALL NOT be shown as a percentage of the processor or of the graphics card unless it is one that was measured. A source that the browser does not offer SHALL be listed as not available, not left out. + +#### Scenario: Measured and estimated +- **WHEN** the busy share of a WASM run with four threads is shown +- **THEN** it is labelled as an estimate and its tooltip says how it was derived + +#### Scenario: Not available +- **WHEN** the browser has no Compute Pressure +- **THEN** the system pressure row says that it is not available in this browser + +#### Scenario: No invented load +- **WHEN** the view is shown on any browser +- **THEN** it does not show a CPU or GPU load percentage that was not measured + +### Requirement: Job resource report +Every model rebuild, AI run and model download SHALL end with a short resource report in the activity log: its duration, the backend (WASM with its number of threads, or WebGPU), how much of the time the worker was busy, and for a rebuild the number of triangles. The AI status line SHALL name the backend and the number of threads. + +#### Scenario: Rebuild report +- **WHEN** a model rebuild ends +- **THEN** the log line shows how long it took and how many triangles the result has + +#### Scenario: AI report +- **WHEN** an AI run on WebGPU ends +- **THEN** the log line shows the duration and that the backend was WebGPU + +#### Scenario: Backend in the status line +- **WHEN** an AI run on WASM with four threads is in progress +- **THEN** its status line names WASM and four threads + +### Requirement: Processor figures +The view SHALL show, for the processor, the busy share of the worker over a job (an estimate of the load, counted with the number of threads), the time the main thread was blocked and the longest block, so the user can tell whether the page stayed responsive, and, where the browser offers it, the system's processor pressure level and the time spent at each level during a job, labelled as a whole-system signal. + +#### Scenario: Responsiveness +- **WHEN** a job has run for a while +- **THEN** the view shows how much of the last seconds the main thread was blocked and the longest block + +#### Scenario: System pressure +- **WHEN** the browser offers processor pressure and a job runs +- **THEN** the current level is shown and labelled as concerning the whole computer + +### Requirement: Graphics figures +For an AI run on WebGPU the view SHALL show the adapter's name and architecture and an estimate of the graphics memory the model needs; in diagnostics mode, where the browser supports timestamp queries, it SHALL also show the GPU time of the run, its share of the run's duration and the five slowest kernels. For the editor's drawing it SHALL show the number of triangles and draw calls of the last render, the time to submit it and, where the browser supports a timer query, the GPU time of the last render. + +#### Scenario: Adapter +- **WHEN** WebGPU is used +- **THEN** the view shows the adapter's name and architecture, and the memory figure is labelled as an estimate + +#### Scenario: GPU time of a run +- **WHEN** diagnostics mode is on, the browser supports timestamp queries and an AI run on WebGPU finishes +- **THEN** the report shows the GPU time and the slowest kernels + +#### Scenario: Last render +- **WHEN** the editor shows a model +- **THEN** the view shows the triangles and draw calls of its last render and the time it took to submit + +#### Scenario: Unsupported +- **WHEN** the browser does not support timestamp queries or timer queries +- **THEN** the corresponding rows are listed as not available + +### Requirement: Memory figures +The view SHALL show the space taken by the downloaded models in the browser's storage and the storage quota, the JavaScript heap where the browser reports it, and the memory of the WebAssembly modules when the runtime exposes it. + +#### Scenario: Storage +- **WHEN** two models are downloaded +- **THEN** the view shows the space they take and the quota of the browser's storage + +#### Scenario: Heap +- **WHEN** the browser reports the JavaScript heap +- **THEN** it is shown, and otherwise listed as not available + +### Requirement: Diagnostics mode +The costly measurements, kernel timing of WebGPU runs and timer queries, SHALL run only when the user turns on a diagnostics switch, which SHALL be off by default, SHALL say that it slows runs down a little, and SHALL be remembered. With it off the view SHALL still show the measurements that cost nothing. + +#### Scenario: Off by default +- **WHEN** the app is opened for the first time +- **THEN** diagnostics mode is off and no profiling runs + +#### Scenario: Warned +- **WHEN** the user looks at the diagnostics switch +- **THEN** its label says that turning it on slows AI runs a little + +#### Scenario: Remembered +- **WHEN** the user turns diagnostics on and reloads +- **THEN** it is still on + +### Requirement: Local only and tidy +All figures SHALL stay on the device and SHALL be copied only when the user presses Copy. Observers and queries SHALL run only while the view is open or a job is running and SHALL be released afterwards. Nothing SHALL run on the start pages. + +#### Scenario: No network +- **WHEN** the view is used +- **THEN** no network request carries any measurement + +#### Scenario: Released +- **WHEN** the view is closed and no job is running +- **THEN** the pressure and long-task observers are disconnected + +### Requirement: Language +All text of the view, the labels, the reasons and the report lines SHALL be available in every language of the app. + +#### Scenario: Russian +- **WHEN** the interface language is Russian +- **THEN** the groups, labels, reasons and job reports are in Russian diff --git a/openspec/changes/37-resource-monitor/tasks.md b/openspec/changes/37-resource-monitor/tasks.md new file mode 100644 index 0000000..ff7b6a4 --- /dev/null +++ b/openspec/changes/37-resource-monitor/tasks.md @@ -0,0 +1,28 @@ +# Tasks + +## 1. Check on the owner's device (spike) + +- [ ] 1.1 Extend the PoC (`poc/ai/`) with a throwaway resources panel that, for the AI models of the shortlist on WASM (1 and 4 threads) and on WebGPU, records: wall time, worker busy share, long-task blocked time, Compute Pressure levels, `adapter.info`, and with ORT Web's WebGPU profiling on the pinned version the GPU time and the slowest kernels, compared with the wall time; also the cost of the profiling (run time with and without it) and `renderer.info` plus a timer query for the keychain and cutout 3D views. Record the numbers and what is not available in `poc/ai/FINDINGS.md` and in `design.md` (Open Questions). If the profiling hook is not usable on the pinned version, say so and stop for `/opsx:update`. + +## 2. Collectors (React-free) + +- [ ] 2.1 `app/resources/types.ts` (`ResourceValue` with `measured | estimated | unavailable` and a note) and `app/resources/jobs.ts`: the report of a job from its start, end, backend and threads (duration, worker busy share, estimated core-equivalent `busy × threads` capped at the threads, triangles for a rebuild). Tests: a single-request job gives busy share 1, two requests with a gap give the right share, the core-equivalent is capped, an unknown backend is reported as such, values carry their provenance. +- [ ] 2.2 `longTasks.ts` (blocked time over a window and the longest block from `longtask` and `long-animation-frame`), `pressure.ts` (`PressureObserver` for `cpu`, time per level), `gpuInfo.ts` (adapter name, architecture, limits), `memory.ts` (`storage.estimate`, `performance.memory`, model cache size from the model store of `32`), each feature-detected and returning `unavailable` with a reason. Tests with fakes for the supported and the unsupported browser, observers are disconnected on `stop()`, no collector runs before `start()`. +- [ ] 2.3 GPU time: the profiling hook of the ML runtime of `32` (kernel start and end accumulated per run, five slowest kernels, ratio to wall time) enabled only in diagnostics mode, and the viewport's `render.ts` (`renderer.info`, submit time, optional `EXT_disjoint_timer_query_webgl2` with the disjoint check). Tests: the kernel aggregation on a recorded sample, profiling is off unless diagnostics is on, a disjoint timer result is discarded, the unsupported case is reported. + +## 3. Integration + +- [ ] 3.1 Job reports in the log of `31a`: `modelClient` and `mlClient` report start, end, backend and threads; a finished job writes its resource line; the AI status line of `32` names the backend and threads. Tests: a fake rebuild and a fake AI run produce the lines with duration, backend and (for rebuilds) triangles, the status line shows "WASM ×4" and "WebGPU", English and Russian keys. + +## 4. UI + +- [ ] 4.1 `ResourcesTab` in the log panel of `31a`: groups CPU, GPU and Memory, the table of the last jobs, a provenance label with a tooltip on every value, a one-line browser support summary, the diagnostics switch with its warning, refresh once a second while a job runs and when opened, collectors started on open and stopped on close, stacked groups on a phone, the resources report included in Copy. Strings in `en.json` and `ru.json`. No artboard exists: built from `ui/` components and `theme.css` tokens, said in the commit; compare in en and ru and at 390 px. Component tests: every value shows a label, an unsupported source is listed as not available, no percentage appears for estimated values, the switch persists in `gridfinity.diagnostics`, observers are disconnected on close, Copy contains the report, key parity. + +## 5. Version + +- [ ] 5.1 Bump to 1.8.0: `package.json` and a `CHANGELOG.md` entry `1.8.0 — 37-resource-monitor`. Test: the existing version test passes with the new number. + +## 6. Verification + +- [ ] 6.1 `npm test` and `npm run build` are green, the dev server starts, no console errors. +- [ ] 6.2 In the browser, en and ru and at 390 px: rebuild a heavy model and run an AI model on WASM and on WebGPU, check the Resources view shows the durations, the backend, the busy share labelled as an estimate, the blocked main-thread time, the pressure level where Chrome offers it, the adapter, the triangles and draw calls of the last render; turn diagnostics on and see the GPU time and the slowest kernels on WebGPU and the note that runs are slower; check Safari or Firefox (if available) lists the unsupported rows as not available; no network request carries a measurement; the observers stop when the view is closed. From 63341b43554daedbe953fc73000ed3be8b83fb75 Mon Sep 17 00:00:00 2001 From: skarakovski Date: Sun, 25 Oct 2026 10:38:54 +0700 Subject: [PATCH 35/38] plan: 38-webmcp (tools for browser agents: validated parameter changes, model summary, consent-based export, log and undo; v1.9.0) Co-Authored-By: Claude Sonnet 5.5 --- openspec/ROADMAP.md | 1 + openspec/changes/38-webmcp/.openspec.yaml | 2 + openspec/changes/38-webmcp/design.md | 54 ++++++++ openspec/changes/38-webmcp/proposal.md | 40 ++++++ .../changes/38-webmcp/specs/webmcp/spec.md | 122 ++++++++++++++++++ openspec/changes/38-webmcp/tasks.md | 31 +++++ 6 files changed, 250 insertions(+) create mode 100644 openspec/changes/38-webmcp/.openspec.yaml create mode 100644 openspec/changes/38-webmcp/design.md create mode 100644 openspec/changes/38-webmcp/proposal.md create mode 100644 openspec/changes/38-webmcp/specs/webmcp/spec.md create mode 100644 openspec/changes/38-webmcp/tasks.md diff --git a/openspec/ROADMAP.md b/openspec/ROADMAP.md index 13fd009..9cd3b8d 100644 --- a/openspec/ROADMAP.md +++ b/openspec/ROADMAP.md @@ -48,6 +48,7 @@ | 35 | `35-smooth-outline` | 25a, 29 | v1.7.0. Общий этап контура для вырезов (классика и ИИ) и Keychain: подгонка кубических кривых Безье с минимумом точек, острые углы сохраняются, зазор по кривой; стенки без ряби, детекция не меняется | | 36 | `36-site-refresh` | 29 | v1.7.1. Актуализация сайта и README: тексты и теги карточек (без «скоро», «snap», «mask»), README по текущему продукту, превью Keychain из PoC-кольца с меньшим зумом, все превью Rack в 1 U, проверка отступов | | 37 | `37-resource-monitor` | 31a, 32, 29 | v1.8.0. Вкладка «Ресурсы» в панели лога: честные показатели CPU/GPU/памяти для ресурсоёмких случаев (перестроение и отрисовка модели, ИИ на WASM и WebGPU) с пометкой «измерено / оценка / недоступно», отчёт по каждой задаче, режим диагностики (профилирование GPU); без FPS | +| 38 | `38-webmcp` | 31a, 29 | v1.9.0. Инструменты WebMCP для браузерных агентов: чтение и запись параметров активного режима (baseplate, box, rack, keychain shape) с валидацией, сводка модели, экспорт и сброс только с подтверждением пользователя, журнал и отмена действий агента, переключатель; без эффекта, если браузер не поддерживает WebMCP | ```mermaid gantt diff --git a/openspec/changes/38-webmcp/.openspec.yaml b/openspec/changes/38-webmcp/.openspec.yaml new file mode 100644 index 0000000..f56fe13 --- /dev/null +++ b/openspec/changes/38-webmcp/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-10-23 diff --git a/openspec/changes/38-webmcp/design.md b/openspec/changes/38-webmcp/design.md new file mode 100644 index 0000000..0979660 --- /dev/null +++ b/openspec/changes/38-webmcp/design.md @@ -0,0 +1,54 @@ +# Design + +## Context + +Each mode owns its state through `usePersistedState(key, defaults, migrate)` and builds its model with `useModel(mode, params, {enabled})` (debounce 150 ms, `busy`, `pending`, last good mesh kept). Validation is pure (`validateBaseplate`, `validateBox`, `validateRackPanel`, `rackCaseLayout` errors, …) and returns i18n keys. Export settings are a module store (`useExportSettings()`, key `gridfinity.export`) and `buildExport(parts, settings, name)` is pure. The activity log of `31a` takes entries and jobs. + +WebMCP (state of the art when this was planned, checked against the Chrome documentation): `document.modelContext.registerTool({name, description, inputSchema, execute(input, {signal}), annotations})` with annotations `readOnlyHint`, `untrustedContentHint`, `consequentialHint` (all default false); unregistering by an `AbortSignal` passed at registration; `getTools()`, `executeTool()` and a `toolchange` event for discovery and testing; cross-origin exposure is opt-in. The first preview used `navigator.modelContext`. Return shape and feature detection are not formally documented, and early-preview behaviour needs Chrome flags or an origin trial. + +## Goals / Non-Goals + +**Goals:** +- Let a browser agent configure any writable mode and read the result with real validation, without touching the DOM. +- Never do anything the user did not see or cannot undo; never download silently. +- Survive API churn: one adapter file knows the browser API. + +**Non-Goals:** +- Agents driving images, files, fonts, AI; server-side MCP; supporting every agent product. + +## Decisions + +- **One adapter, many tools.** `agent/registry.ts` is the product-level model: `AgentTool = {name, description, inputSchema, annotations, run(input, ctx): Promise}`; `ToolResult` is `{ok: true, data} | {ok: false, error: {code, message, details?}}`. `agent/webmcp.ts` is the only place that touches `document.modelContext` (falling back to `navigator.modelContext` while Chrome still has it): feature detection, `registerTool` with an `AbortController` per tool, converting a `ToolResult` to the shape the browser expects (a JSON text content; the exact shape is fixed by the spike), `toolchange`, and `dispose()`. If neither object exists, `webmcp.install()` returns `null` and the host renders nothing. Everything else is testable in Node with a fake `modelContext`. +- **Tools follow the active mode.** `AgentHost` (in `AppShell`) holds the always-present tools and the set contributed by the mounted mode. A mode contributes by calling `useAgentMode(descriptor, {state, setState, model, defaults})`; unmount removes its tools. The names do not carry the mode (`set_parameters`, not `set_baseplate_parameters`): the active mode is in `get_app_state`, and fewer tools make agents more reliable. A tool called while no mode is open answers `no_active_mode` with the list of modes and `open_mode`. +- **Descriptor per mode.** `AgentModeDescriptor = {id, writable, schema, normalise(raw) → T, validate(T) → keys, describe(T) → data}`. `schema` is a JSON Schema subset (object, number with min/max/step, integer, boolean, string enum, array with max items) written by hand for the documented fields only, with descriptions in English that include units ("mm", "cells"). `normalise` is the mode's existing `migrate` plus the same nested normalisers as `usePersistedState` (so a partial patch can never crash a mode); `validate` is the mode's existing validator. Fields not in the schema are not writable: this keeps files, images and internal ids out of reach. +- **Patch semantics.** `set_parameters({patch})`: (1) check the patch against the schema (unknown field, wrong type, out of range → error with the JSON path, nothing applied); (2) deep-merge onto the current state (arrays replaced whole, `AxisSize` objects merged by key); (3) `normalise`; (4) `validate`: validation keys are *returned* but the state **is** still applied when the keys are the mode's ordinary "invalid parameters" (the UI shows them the same way and generation is skipped), so the user sees what the agent did; hard schema violations are never applied; (5) one `setState` call, so one rebuild; (6) the result carries `applied`, `normalised` differences (e.g. a value clamped), `errors` (keys + English text from `en.json`) and `warnings`. Lengths are millimetres whatever the display unit. +- **Reading the result.** `get_model_info` waits for `!pending && !busy` with a 20 s limit and returns `{ready, parts: [{label, bbox, volumeMm3, triangles}], total, errors, warnings, plate: {printer, usableBed, plates, overflow}}`; it never returns the mesh. If the build is still running at the limit it answers `ready: false` and the caller may retry. +- **Export is a request, not an action.** `request_export({format?, name?})` validates (nothing to export, errors) and opens an `AgentExportDialog` ("An agent wants to save "), whose Download button runs the normal export path. The tool resolves when the user chooses or the dialog is dismissed (`saved | declined | timeout 120 s`), so an agent cannot download in the background and a hidden tab cannot save files. `reset_parameters` uses the same confirmation. This matches the rule of `32` (nothing is downloaded silently). +- **Log and undo.** Each call writes a log entry through the `31a` API with source `agent`, tool name and a short human summary built from the patch paths (not the whole object). `agent/history.ts` keeps the last 10 `{label, before, after}` snapshots of mode state per mode; "Undo agent change" restores `before` through the same `setState` (only if the current state still equals `after`, otherwise it offers to restore anyway). The status bar marker is the `31a` job type `agent` while a call runs. +- **Switch.** `gridfinity.agent` = `{enabled: true}`; off → `dispose()`; on → reinstall. The switch sits in the log panel header (where agent activity is visible) and the Header menu link opens it. The state is read at startup before registering. +- **Security.** Tool results contain only numbers, enums, booleans, parameter names, validation texts that we wrote, and user-entered strings of writable modes (Keychain text, export file name), the tools returning the last are annotated `untrustedContentHint`. Tool descriptions are static. Nothing in the page is executed from tool input, a string never reaches `innerHTML` or a file path unsanitised (the export name goes through the existing file-name normalisation). `exposedTo` is never set, so other origins' frames cannot call the tools. Inputs are size-limited (a patch over 8 KB is rejected). A call rate over 20 per second answers `rate_limited`. +- **Stability.** The adapter reads the flavour of the API at runtime (`document.modelContext` first), tolerates both argument shapes for `execute`, and everything is behind `webmcp.install()` returning `null`. The spike (task 1.1) pins what the current Chrome does; a version change of the browser API means editing one file. + +## Tool surface (v1) + +| Tool | Flags | Purpose | +|---|---|---| +| `get_app_state` | readOnly | product, mode, units, language, writable modes, which tools exist | +| `open_mode` | – | navigate to `{product, mode}` (hash route); unknown → error with valid ids | +| `get_parameters` | readOnly, untrusted | current parameters (mm), schema of the writable subset, defaults | +| `set_parameters` | consequential | patch, validate, apply; returns errors, warnings, normalised diff | +| `reset_parameters` | consequential | asks the user to confirm, then restores defaults | +| `get_model_info` | readOnly | build result summary and plate status | +| `get_export_settings` | readOnly | format, printer, margin, gap, multi, orientation, file name | +| `set_export_settings` | consequential | patch of the same fields (printer ids from `data/printers.ts`) | +| `request_export` | consequential | ask the user to save; resolves with `saved/declined/timeout` | + +## Risks / Trade-offs + +- The API may change or never leave preview → one adapter, no effect without it, a small change if it moves; the value now is mostly agent-driven testing and an early look. +- An agent can leave a mode in an invalid state → validation keys are returned and shown, undo is one click, the model keeps the last good mesh. +- A per-mode schema can drift from the UI → each descriptor has a test that the schema and defaults agree, and a test that every writable setting of a panel is either in the schema or listed as excluded. + +## Open Questions + +- Spike results go here: exact API name and flag in the current Chrome, the shape `execute` must return, whether images can be returned (a `capture_view` tool for the 3D view is deferred until they can), how to drive the tools in tests (the browser's `executeTool`), and whether a hidden tab keeps tools registered. diff --git a/openspec/changes/38-webmcp/proposal.md b/openspec/changes/38-webmcp/proposal.md new file mode 100644 index 0000000..cc5e761 --- /dev/null +++ b/openspec/changes/38-webmcp/proposal.md @@ -0,0 +1,40 @@ +# Proposal + +## Why + +Browsers are getting agents (Chrome with Gemini, extensions, assistants) that act on a page for the user. Today such an agent has to read the DOM and click: slow, fragile, and blind to what is valid (it does not know that a 5-unit-wide rack case exceeds 222.25 mm until the page shows an error). WebMCP is a web-platform proposal (W3C community group; early preview in Chrome) in which a page declares **tools**, each with a name, description, JSON input schema and an `execute` function, and the agent calls them directly. + +PrintKit is a good fit: it is client-only, every mode is a pure function of a small serialisable parameter object in millimetres, validation already returns keys instead of text, and the result (volume, bounding box, plate arrangement) is measurable without looking at the screen. "Baseplate 4×3 with corner magnets for a Bambu A1, give me the 3MF" becomes a few calls with real validation feedback. + +The API is still moving (the entry point is `document.modelContext` in current Chrome docs and was `navigator.modelContext` in the first preview; annotations and argument shapes change between versions). This change therefore isolates the API behind one thin adapter and does nothing at all when the browser has no WebMCP. + +## What Changes + +- **Agent bridge** (`src/agent/`): a React-free registry of *tools* (name, description, JSON schema, read-only/consequential flags, handler) and an adapter that registers them with the browser's WebMCP when it exists, re-registers when the active mode changes (`toolchange`), and removes them on unmount. No WebMCP → nothing is registered and nothing changes for the user. +- **Tools, always present:** `get_app_state` (product, mode, units, language, what is writable), `open_mode` (navigate to a product/mode), `get_export_settings`, `set_export_settings`. +- **Tools, per active mode:** + - `get_parameters` (current parameters in mm, the schema of the writable subset, defaults); + - `set_parameters` (a partial patch, deep-merged, validated against the mode's schema and normalised by the mode's own `migrate`, applied atomically; returns the validation keys with English texts and the new state, or the reasons it was rejected, without changing anything); + - `reset_parameters` (asks the user to confirm in the UI); + - `get_model_info` (waits for the build to settle: parts, bounding box, volume, triangles, validation errors and warnings, plate arrangement and overflow for the current printer); + - `request_export` (does not download by itself: opens a confirmation in the page, the file is saved only when the user presses Download). +- **Writable modes in this version:** Gridfinity baseplate and box, Rack panel, case and shelf, Keychain shape. **Read-only (state, info, export):** Gridfinity cutout and Keychain image (they depend on files the user chose, which an agent cannot supply), and anything AI (`32`–`34`). +- **Agent activity is visible and reversible.** Every call is written to the activity log of `31a` ("Agent: set_parameters baseplate.x = 4 units"), the status bar shows a short "Agent" marker while a call runs, and the last 10 agent changes can be undone one by one from the log line or the panel ("Undo agent change"). +- **A switch** "Allow browser agents" (Header menu or the log panel, persisted, default on) that unregisters everything when off. +- **Safety.** Tools expose only the parameters of the current mode and the export settings: never images, photos, meshes, model files, file names of the user's disk, or storage contents. Tool results are plain data, never instructions. Parameters that hold user text (Keychain text, file name) are marked untrusted in the annotations. +- **No new dependency, no network.** The schema check is a small local validator; nothing is sent anywhere by this change. + +## Capabilities + +### New Capabilities +- `webmcp`: tools for browser agents, the agent bridge, activity log entries, undo and the switch. + +### Modified Capabilities + +## Impact + +- New `src/agent/` (`registry.ts`, `webmcp.ts` adapter, `schema.ts` validator, `patch.ts` deep merge, `tools/{app,params,model,export}.ts`, per-mode descriptors `modes/*.ts` with schema + normalise + validate, `history.ts` undo stack), an `AgentHost` mounted in `AppShell`, a small hook `useAgentMode(descriptor, state, setState, model)` called by each writable mode, a "Allow browser agents" switch and an undo control in the log panel of `31a`, an export confirmation dialog, strings in en and ru, tests. +- Modes keep ownership of their state: the bridge calls the same setters and the same `migrate` and `validate*` functions the UI uses; geometry is untouched. +- Out of scope: tools for images, photos, STL import, fonts and AI models; running or managing AI from an agent; reading or writing the mesh store; cross-origin exposure of tools (`exposedTo`); the declarative (form attribute) WebMCP API; any server side; adapting to a particular agent product. +- Version 1.9.0 (minor: a new capability). +- Depends on: `31a-status-log` (agent entries in the log, status marker), `29-beta-label` (the versioning rule). Independent of `32`–`37`, but the tools never download models and never start AI, so they stay valid when those land. diff --git a/openspec/changes/38-webmcp/specs/webmcp/spec.md b/openspec/changes/38-webmcp/specs/webmcp/spec.md new file mode 100644 index 0000000..bb6d504 --- /dev/null +++ b/openspec/changes/38-webmcp/specs/webmcp/spec.md @@ -0,0 +1,122 @@ +# Spec Delta + +## Purpose +Defines how PrintKit exposes its parametric tools to browser agents through WebMCP, with visible, reversible and consent-based actions. + +## ADDED Requirements + +### Requirement: Registration only where the browser supports it +The app SHALL register its tools with the browser's WebMCP interface when the browser provides one, and SHALL do nothing, without errors or visible changes, when it does not. All knowledge of the browser API SHALL be in one adapter. + +#### Scenario: Supported browser +- **WHEN** the app starts in a browser with WebMCP and agents are allowed +- **THEN** the always-present tools are registered, and the tools of the open mode are added + +#### Scenario: Unsupported browser +- **WHEN** the browser has no WebMCP interface +- **THEN** no tool is registered, no error is logged, and the app behaves as before + +#### Scenario: Mode changes +- **WHEN** the user moves from one mode to another +- **THEN** the tools of the old mode are removed and those of the new mode registered, and the tool list change is announced to the browser + +### Requirement: Reading the app and the mode +The tools SHALL let an agent read the product, the mode, the units, the language, the writable modes, the current parameters in millimetres with their schema and defaults, and the export settings. They SHALL NOT return images, photos, meshes, model files, stored model data or the contents of browser storage. + +#### Scenario: App state +- **WHEN** an agent calls `get_app_state` in the Rack case mode +- **THEN** it receives the product and mode, whether the mode is writable and the list of tools available + +#### Scenario: Parameters in millimetres +- **WHEN** the user's display unit is inches and an agent calls `get_parameters` +- **THEN** all lengths are returned in millimetres, with the unit named in the schema + +#### Scenario: No tool in a read-only mode for writing +- **WHEN** the open mode is Gridfinity cutout or Keychain image +- **THEN** `set_parameters` and `reset_parameters` are not offered, and reading and export remain + +### Requirement: Validated, atomic parameter changes +`set_parameters` SHALL accept a partial patch of the writable fields of the active mode, check it against the mode's schema, normalise it with the mode's own rules and apply it as one change that causes one rebuild. A patch that breaks the schema (unknown field, wrong type, out of range, too large) SHALL be rejected with the JSON path of each problem and SHALL change nothing. The result SHALL report the values the normalisation changed and the mode's validation errors and warnings, with texts. + +#### Scenario: Valid patch +- **WHEN** an agent sets the baseplate width to 4 units and the corner fasteners to magnets +- **THEN** both are applied together, one rebuild runs, and the result lists no errors + +#### Scenario: Unknown field +- **WHEN** a patch contains a field outside the schema +- **THEN** the call fails with the path of that field and the state is unchanged + +#### Scenario: Parameters invalid for the mode +- **WHEN** a patch makes the rack case wider than the rack window +- **THEN** the change is applied as if made in the panel, the panel shows the same error, the result lists the error key and text, and the last valid model is kept + +#### Scenario: Value clamped +- **WHEN** a value is outside what the mode normalises to +- **THEN** the result names the field, the value sent and the value used + +### Requirement: Model summary +`get_model_info` SHALL wait for the current build to finish, up to a limit, and return for each part its label, bounding box, volume and triangle count, the totals, the validation errors and warnings, and the plate arrangement for the chosen printer including parts that do not fit. It SHALL NOT return mesh data. + +#### Scenario: After a change +- **WHEN** an agent changes a parameter and calls `get_model_info` +- **THEN** the answer describes the model built from the new parameters + +#### Scenario: Build still running +- **WHEN** the build does not finish within the limit +- **THEN** the answer says it is not ready and the agent can ask again + +#### Scenario: Part larger than the bed +- **WHEN** a part does not fit the printer's usable bed +- **THEN** it is listed as overflowing with the sizes + +### Requirement: Exports and resets need the user +`request_export` and `reset_parameters` SHALL show a confirmation in the page and SHALL act only when the user confirms. No tool SHALL save a file, download a model, start an AI model or delete stored data by itself. A request that is not answered within two minutes SHALL end as timed out without acting. + +#### Scenario: Export confirmed +- **WHEN** an agent calls `request_export` and the user presses Download in the dialog +- **THEN** the files are saved by the normal export path and the call returns `saved` + +#### Scenario: Export declined +- **WHEN** the user dismisses the dialog +- **THEN** nothing is saved and the call returns `declined` + +#### Scenario: Nothing to export +- **WHEN** the model has errors or no parts +- **THEN** no dialog opens and the call returns the reason + +### Requirement: Agent actions are visible and reversible +Every tool call SHALL be written to the activity log with the tool and a short summary of what changed, and the status bar SHALL show that an agent call is running. The user SHALL be able to undo each of the last ten agent changes to the parameters of a mode. + +#### Scenario: Logged +- **WHEN** an agent changes a parameter +- **THEN** the log has an entry marked as from an agent that names the tool and the changed fields + +#### Scenario: Undo +- **WHEN** the user presses "Undo agent change" after an agent set the width +- **THEN** the mode's parameters return to the values before that call + +#### Scenario: State changed since +- **WHEN** the user edited the parameters after the agent's change and presses Undo +- **THEN** the app says the parameters have changed since and asks before restoring + +### Requirement: Switch for agents +The app SHALL have a persisted switch "Allow browser agents", on by default, that removes all tools when turned off and registers them again when turned on. + +#### Scenario: Turn off +- **WHEN** the user turns the switch off +- **THEN** all tools are unregistered and a later reload keeps them off + +### Requirement: Safe tool data +Tool descriptions SHALL be static. Tool inputs SHALL be size-limited and rate-limited. Tools SHALL NOT be exposed to other origins. Results that include text entered by the user SHALL be marked as untrusted content. Tool input SHALL NOT be executed, inserted as markup or used as a file path. + +#### Scenario: Oversized patch +- **WHEN** a patch is larger than the limit +- **THEN** it is rejected and nothing changes + +#### Scenario: Too many calls +- **WHEN** an agent calls tools faster than the rate limit +- **THEN** the excess calls answer that they are rate limited and change nothing + +#### Scenario: File name +- **WHEN** an agent sets the export file name to a string with path separators or markup +- **THEN** it is normalised by the existing file-name rules before use diff --git a/openspec/changes/38-webmcp/tasks.md b/openspec/changes/38-webmcp/tasks.md new file mode 100644 index 0000000..b776d91 --- /dev/null +++ b/openspec/changes/38-webmcp/tasks.md @@ -0,0 +1,31 @@ +# Tasks + +## 1. Check the browser API (spike) + +- [ ] 1.1 In a throwaway page (or a branch of `poc/`) in the owner's Chrome, register one read-only and one writing tool and drive them with the browser's own `getTools()` / `executeTool()` and with a real agent if one is available. Record in `design.md` (Open Questions): the entry point (`document.modelContext` / `navigator.modelContext`), the flag or origin trial, the shape `execute` must return and how errors are returned, `AbortSignal` unregistering, `toolchange`, whether a background tab keeps the tools, whether images can be returned, and the Chrome version. If the API cannot be used on a normal build, stop for `/opsx:update`. + +## 2. Agent core (React-free) + +- [ ] 2.1 `agent/registry.ts` (`AgentTool`, `ToolResult`, error codes), `agent/schema.ts` (JSON-Schema subset validator returning JSON paths: object, number min/max, integer, boolean, enum, array with max items, size limit) and `agent/patch.ts` (deep merge, arrays replaced, the diff of normalisation). Tests: valid, unknown field, wrong type, out of range, oversized, arrays, merge of nested objects, diff reporting. +- [ ] 2.2 `agent/webmcp.ts`: feature detection, registration with an `AbortController` per tool, result conversion as fixed by the spike, `toolchange`, `dispose()`, call rate limit. Tests with a fake `modelContext` (both entry points): no API → `install()` is `null` and nothing throws; tools registered and removed; a thrown handler becomes an error result; rate limit. +- [ ] 2.3 `agent/history.ts`: per-mode stack of the last 10 `{label, before, after}`, undo, the changed-since check. Tests. + +## 3. Descriptors and tools + +- [ ] 3.1 Descriptors for the writable modes (`agent/modes/{baseplate,box,rackPanel,rackCase,rackShelf,keychainShape}.ts`): schema of the documented fields with unit descriptions, `normalise` (the mode's migrate), `validate`, `describe`. Tests per mode: defaults satisfy the schema, a patch round-trips through `normalise`, invalid sizes give the same keys as the validator, and a coverage test that every setting of the panel is in the schema or in an explicit exclusion list. +- [ ] 3.2 Tools: `get_app_state`, `open_mode`, `get_parameters`, `set_parameters`, `reset_parameters`, `get_model_info`, `get_export_settings`, `set_export_settings`, `request_export`. Tests with fake mode handles: unknown mode, read-only modes, patch rejected leaves state unchanged, invalid-but-applied returns keys with English texts, `get_model_info` waits for `pending`, times out with `ready: false`, never returns mesh data, printer ids checked, export file name normalised, lengths in mm regardless of unit. + +## 4. App integration + +- [ ] 4.1 `useAgentMode(descriptor, handle)` and `AgentHost` in `AppShell` (tool set follows the mounted mode); each writable mode calls the hook with its state, setter, defaults and model result; the read-only modes call it with `writable: false`. Tests: mounting a mode adds its tools, switching modes swaps them, unmount removes them, a tool called during a route change answers `no_active_mode`. +- [ ] 4.2 Activity log entries and the status marker through the API of `31a` (source `agent`, a summary built from the changed paths), the "Undo agent change" control in the log line and panel, the switch "Allow browser agents" (key `gridfinity.agent`) in the log panel header with its Header-menu entry. Strings in `en.json` and `ru.json`. No artboard exists: built from `ui/` and tokens, said in the commit. Component tests: log entry text, undo restores, changed-since prompt, switch persists and unregisters, key parity. +- [ ] 4.3 `AgentExportDialog` and the reset confirmation (`ConfirmDialog`): file list and sizes, Download runs the normal export, decline and the 120 s timeout resolve the call, one dialog at a time, focus handling and Esc. Tests: saved / declined / timeout, no download without a click, nothing opens when the model has errors. + +## 5. Version + +- [ ] 5.1 Bump to 1.9.0: `package.json` and a `CHANGELOG.md` entry `1.9.0 — 38-webmcp`. Test: the existing version test passes with the new number. + +## 6. Verification + +- [ ] 6.1 `npm test` and `npm run build` are green, the dev server starts, no console errors; in a browser without WebMCP the app behaves as before. +- [ ] 6.2 In Chrome with WebMCP enabled, en and ru: list the tools, change a baseplate (size, fasteners), a box, a rack case that becomes too wide (see the error in the panel), a keychain shape; check `get_model_info` against the STL bounding box and volume of the real export; ask for an export and confirm, decline and let it time out; undo an agent change; turn the switch off and check the tools disappear; check no tool returns images or file data and no network request is made by the tools. From 27b76d8a78261712e22dd09d98941fc0aefd7c7a Mon Sep 17 00:00:00 2001 From: skarakovski Date: Sun, 25 Oct 2026 10:39:05 +0700 Subject: [PATCH 36/38] plan: 38-webmcp creation date Co-Authored-By: Claude Sonnet 5.5 --- openspec/changes/38-webmcp/.openspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openspec/changes/38-webmcp/.openspec.yaml b/openspec/changes/38-webmcp/.openspec.yaml index f56fe13..7ccf7c3 100644 --- a/openspec/changes/38-webmcp/.openspec.yaml +++ b/openspec/changes/38-webmcp/.openspec.yaml @@ -1,2 +1,2 @@ schema: spec-driven -created: 2026-10-23 +created: 2026-10-25 From 88b288b383e1017886aec66d89d5988150b2d4c9 Mon Sep 17 00:00:00 2001 From: skarakovski Date: Sun, 25 Oct 2026 10:59:23 +0700 Subject: [PATCH 37/38] plan: 33/34/35 fill gaps (auto-open large areas, guided depth snapping, slope limit and final smoothing, subject presets incl. Drawing/logo, Advanced outline group); 35 now depends on 34 Co-Authored-By: Claude Sonnet 5.5 --- openspec/ROADMAP.md | 2 +- .../33-keychain-ai-silhouette/design.md | 1 + .../specs/keychain-ai-silhouette/spec.md | 23 ++++++++++ .../33-keychain-ai-silhouette/tasks.md | 5 ++- .../34-keychain-photo-relief/design.md | 6 ++- .../specs/keychain-photo-relief/spec.md | 42 +++++++++++++++++++ .../changes/34-keychain-photo-relief/tasks.md | 4 +- openspec/changes/35-smooth-outline/design.md | 1 + .../changes/35-smooth-outline/proposal.md | 2 +- .../specs/smooth-outline/spec.md | 19 +++++++++ openspec/changes/35-smooth-outline/tasks.md | 2 +- 11 files changed, 99 insertions(+), 8 deletions(-) diff --git a/openspec/ROADMAP.md b/openspec/ROADMAP.md index 9cd3b8d..dd82a2c 100644 --- a/openspec/ROADMAP.md +++ b/openspec/ROADMAP.md @@ -45,7 +45,7 @@ | 32 | `32-photo-trace-ai` | 25a, 29, 31a | v1.4.0. Фото-вырезы: альтернативный нейросетевой движок силуэтов (в браузере, веса по согласию, гибридный точный край), классический остаётся по умолчанию как baseline; сравнение движков | | 33 | `33-keychain-ai-silhouette` | 32, 11, 29 | v1.5.0. Keychain Image: силуэт по ИИ (удаление фона на браузерной модели), те же ручки порога и сглаживания, один главный объект, результат хранится вместе с картинкой; яркость остаётся по умолчанию | | 34 | `34-keychain-photo-relief` | 33, 32, 12, 17, 29 | v1.6.0. Keychain Image: рельеф из фото как смесь трёх источников формы (контур, глубина Depth Anything, детали — локальный контраст), кроп области (лицо), силуэт «весь кадр» (плашка), пресеты; результат хранится с картинкой | -| 35 | `35-smooth-outline` | 25a, 29 | v1.7.0. Общий этап контура для вырезов (классика и ИИ) и Keychain: подгонка кубических кривых Безье с минимумом точек, острые углы сохраняются, зазор по кривой; стенки без ряби, детекция не меняется | +| 35 | `35-smooth-outline` | 25a, 34, 29 | v1.7.0. Общий этап контура для вырезов (классика и ИИ) и Keychain: подгонка кубических кривых Безье с минимумом точек, острые углы сохраняются, зазор по кривой; стенки без ряби, детекция не меняется | | 36 | `36-site-refresh` | 29 | v1.7.1. Актуализация сайта и README: тексты и теги карточек (без «скоро», «snap», «mask»), README по текущему продукту, превью Keychain из PoC-кольца с меньшим зумом, все превью Rack в 1 U, проверка отступов | | 37 | `37-resource-monitor` | 31a, 32, 29 | v1.8.0. Вкладка «Ресурсы» в панели лога: честные показатели CPU/GPU/памяти для ресурсоёмких случаев (перестроение и отрисовка модели, ИИ на WASM и WebGPU) с пометкой «измерено / оценка / недоступно», отчёт по каждой задаче, режим диагностики (профилирование GPU); без FPS | | 38 | `38-webmcp` | 31a, 29 | v1.9.0. Инструменты WebMCP для браузерных агентов: чтение и запись параметров активного режима (baseplate, box, rack, keychain shape) с валидацией, сводка модели, экспорт и сброс только с подтверждением пользователя, журнал и отмена действий агента, переключатель; без эффекта, если браузер не поддерживает WebMCP | diff --git a/openspec/changes/33-keychain-ai-silhouette/design.md b/openspec/changes/33-keychain-ai-silhouette/design.md index a81228d..d231fe7 100644 --- a/openspec/changes/33-keychain-ai-silhouette/design.md +++ b/openspec/changes/33-keychain-ai-silhouette/design.md @@ -22,6 +22,7 @@ A keychain differs from a photo cutout in three ways that shape the design: the - **Input to the network.** The whole image, long side capped at 2048 for inference (the stored image keeps its own size, ≤ 4096), fitted into the model's square input with the aspect ratio kept and the borders filled with the model's mean colour (the PoC "whole image" mode does exactly this); the output is cropped back and resampled bilinearly to the image size. The paper-sheet stretch used in `32` is not appropriate here. No hybrid edge: the resolution argument above makes it unnecessary, and the brightness image is not a reliable edge cue for photos. - **Soft alpha semantics.** The matte keeps the network's probability, so the two controls mean the same as in `32`: threshold decides how far the outline reaches into the soft edge (default per model, 0.5 as a fallback), smoothing radius is a length in image pixels converted from mm of the keychain size (`LengthField`, mm of the final keychain, so the same value gives the same visual smoothness at any resolution). Defaults come from `maskDefaults` of the model. - **Keep only the largest object.** A component filter after the threshold keeps the connected component with the largest area (8-connected, `labelComponents`) and drops the rest; enclosed gaps are untouched and become areas as before. Default on for AI, off for Brightness (where several shapes can be intentional, e.g. a logo with a separate dot). Stored as `ai.keepLargest`. +- **Automatic areas.** A photo of a ring handle, a bag strap or an animal with a gap between legs leaves enclosed background regions, and the owner's rule (PoC) is: large ones are meant to be open (a keyring hole must not be filled), small ones between strokes are noise and are filled. `SilhouetteParams.autoOpen` is an area threshold in mm² of the part (default **150**, 0 = off, i.e. today's behaviour of `fillHoles`); in `buildSilhouette`, after the areas are found and sized (pixels × the mm² of a pixel, from the part size), an area with no stored mode of its own becomes `open` if its size is at least the threshold and filled (`follow` with fill) if smaller. A mode the user sets on an area (open, level, follow) always wins, and stable ids are unchanged. Default on (150) for the AI source; Brightness keeps 0 so existing keychains are unchanged, and the Drawing preset of `34` sets it. The control is an area, so it is a `NumberField` labelled mm² and is not converted by the display unit (like the other unitless counters). - **Persistence.** `imageStore` records get a second kind of entry, `matte`, keyed by `imageId` + `model` + `revision`, with the plane and its size; stored together with the original in the same DB (`gridfinity-keychain`), memory fallback with the existing `persistent` warning. The params store `source`, `ai.model`, `threshold`, `smooth`, `keepLargest`; on load the matte is read and registered. Deleting the model in the manager does not delete the matte; deleting or replacing the image does. If the matte is missing (cleared storage) the section shows "run again" and the silhouette falls back to Brightness until it is run, never to an empty mask. - **Staleness.** The matte is tied to the image and the model: replacing the image drops it; choosing a different model keeps the old matte until the user runs the new one (shown as "matte from "), so a switch never silently re-runs a download-sized job. - **Relation to existing alpha.** If the image already has transparency, the section shows the note "this image has transparency, it is used as is" with AI still selectable (the user may want a different cut), and the AI run ignores the image's alpha as input. SVG is rasterised by the existing path and is not offered AI. diff --git a/openspec/changes/33-keychain-ai-silhouette/specs/keychain-ai-silhouette/spec.md b/openspec/changes/33-keychain-ai-silhouette/specs/keychain-ai-silhouette/spec.md index fe2439c..fa3c467 100644 --- a/openspec/changes/33-keychain-ai-silhouette/specs/keychain-ai-silhouette/spec.md +++ b/openspec/changes/33-keychain-ai-silhouette/specs/keychain-ai-silhouette/spec.md @@ -77,6 +77,29 @@ With the AI source the user SHALL be able to keep only the largest connected obj - **WHEN** the kept object has an enclosed gap such as a ring handle - **THEN** it appears in the area list with a numeric id and the usual modes +### Requirement: Automatic areas +Enclosed background regions of a traced silhouette SHALL be opened or filled automatically by size: regions at least as large as a threshold in square millimetres of the part SHALL be open, smaller ones SHALL be filled. The threshold SHALL be set by the user, with 0 meaning no automatic rule. A mode the user has set on a region SHALL always win. The default SHALL be 150 mm² for the AI source and 0 for Brightness, so existing keychains do not change. + +#### Scenario: Ring handle +- **WHEN** the AI silhouette has a large enclosed gap such as a key ring hole +- **THEN** it is open without the user marking it, and the area list shows it as open + +#### Scenario: Small gap +- **WHEN** the silhouette has a small enclosed gap smaller than the threshold +- **THEN** it is filled + +#### Scenario: User wins +- **WHEN** the user sets a mode on an area +- **THEN** that mode is kept whatever the threshold says, also after the threshold changes + +#### Scenario: Off +- **WHEN** the threshold is 0 +- **THEN** the areas follow the existing fill and open rules + +#### Scenario: Existing keychain +- **WHEN** a keychain saved before this feature is loaded +- **THEN** its areas behave as before + ### Requirement: Persistence of the AI result The AI result SHALL be stored with the image in the browser's storage and restored after a reload together with the source, model and controls, without running the model again. Deleting the model from the model manager SHALL NOT remove an existing result. Replacing or removing the image SHALL remove its result. If the stored result is missing, the app SHALL say so, offer to run again and use Brightness meanwhile, never an empty silhouette. If storage is unavailable the AI source SHALL work for the session and the user SHALL be warned that the result will not persist. diff --git a/openspec/changes/33-keychain-ai-silhouette/tasks.md b/openspec/changes/33-keychain-ai-silhouette/tasks.md index 3b87463..2f5b946 100644 --- a/openspec/changes/33-keychain-ai-silhouette/tasks.md +++ b/openspec/changes/33-keychain-ai-silhouette/tasks.md @@ -8,6 +8,7 @@ - [ ] 2.1 `geometry/raster/matte.ts`: `smoothThreshold(matte, w, h, radiusPx, threshold) → Uint8Array alpha`, `keepLargest(mask, w, h)`. Tests: threshold monotone (lower never shrinks), radius 0 equals plain threshold, smoothing does not lengthen the outline, keepLargest drops a speck and keeps an enclosed gap, ties and empty mask are safe. - [ ] 2.2 `SilhouetteParams.source` and `ai {model, threshold, smooth, keepLargest, matteId}`, `buildSilhouette` builds the alpha from the registered matte (`matteRegistry` in `geometry/model.ts`, `ModelClient.registerMatte`) and runs the unchanged pipeline; missing matte under `ai` falls back to Brightness and reports it in `SilhouetteResult`. Tests: with `source: 'brightness'` the existing regression volumes and bounding boxes are unchanged; a synthetic image plus matte gives the expected mask and the same relief as the equivalent alpha PNG; slider changes do not touch the registered matte; a missing matte reports the fallback. +- [ ] 2.2a Automatic areas: `SilhouetteParams.autoOpen` (mm², default 150 for `ai`, 0 otherwise) and its use in `buildSilhouette` (size of each area in mm² from the part size; explicit per-area modes win; stable ids unchanged). Tests: a ring gap above the threshold is open and a speck gap below it is filled, an explicit mode survives a threshold change, 0 reproduces the existing behaviour and volumes, the size in mm² does not depend on the image resolution, the threshold works for a Brightness silhouette when set. - [ ] 2.3 `migrateKeychain` fills `source: 'brightness'` and the `ai` defaults for old records and normalises partial values. Tests: old record round-trip, partial and corrupt `ai` data fall back to defaults. ## 3. Image side @@ -17,7 +18,7 @@ ## 4. UI -- [ ] 4.1 `SilhouetteSection`: source switch Brightness | AI (hides brightness threshold and invert under AI), `AiSilhouette` block (model picker with a link to the manager, Remove background button (disabled with a hint and a link to the manager without a model, active by itself after the download) with `AiStatus` (loader, current line, Cancel), shared `MaskTuning` with per-model defaults and the soft-matte view, keep-largest toggle, lost-result message, hints for transparent, logo and SVG). Strings in `en.json` and `ru.json`. No artboard exists: built from `ui/` components and `theme.css` tokens, said in the commit; compare in en and ru and at 390 px. Component tests: Brightness default and unchanged, controls hide and show per source, the button is disabled with a hint and a manager link when no model is ready and becomes active when the model store reports a model, the loader and the current line follow the events of a fake run, sliders update the silhouette without a client call, keep-largest, hints, SVG has no AI, lost result falls back with a message, key parity. +- [ ] 4.1 `SilhouetteSection`: source switch Brightness | AI (hides brightness threshold and invert under AI), `AiSilhouette` block (model picker with a link to the manager, Remove background button (disabled with a hint and a link to the manager without a model, active by itself after the download) with `AiStatus` (loader, current line, Cancel), shared `MaskTuning` with per-model defaults and the soft-matte view, keep-largest toggle, Open-areas-larger-than field in mm² (`NumberField`, with a hint), lost-result message, hints for transparent, logo and SVG). Strings in `en.json` and `ru.json`. No artboard exists: built from `ui/` components and `theme.css` tokens, said in the commit; compare in en and ru and at 390 px. Component tests: Brightness default and unchanged, controls hide and show per source, the button is disabled with a hint and a manager link when no model is ready and becomes active when the model store reports a model, the loader and the current line follow the events of a fake run, sliders update the silhouette without a client call, keep-largest, the threshold field opens and fills areas and does not override a mode the user set, hints, SVG has no AI, lost result falls back with a message, key parity. - [ ] 4.2 The enlarged mask dialog and `MaskPreview` show the AI silhouette and can switch to the soft matte view; areas, selection and everything downstream work on the AI silhouette. Tests: preview follows the source, the area list is built from the AI mask. ## 5. Version @@ -27,4 +28,4 @@ ## 6. Verification - [ ] 6.1 `npm test` and `npm run build` are green, the dev server starts, no console errors; with the Brightness source the existing keychain tests and volumes are unchanged. -- [ ] 6.2 In the browser, en and ru and at 390 px, with a pet photo, a portrait and a logo: AI run with progress, silhouette and relief appear, sliders update without a model run (network panel shows no model activity), keep-largest drops a stray bit, a ring-shaped gap becomes an area, reload restores the same relief without a run, deleting the model keeps the silhouette, switching to Brightness and back works, a transparent PNG and an SVG behave as described, the exported STL is one valid piece, no request carries image data. +- [ ] 6.2 In the browser, en and ru and at 390 px, with a pet photo, a portrait and a logo: AI run with progress, silhouette and relief appear, sliders update without a model run (network panel shows no model activity), keep-largest drops a stray bit, a ring-shaped gap is open by itself and a small gap is filled, reload restores the same relief without a run, deleting the model keeps the silhouette, switching to Brightness and back works, a transparent PNG and an SVG behave as described, the exported STL is one valid piece, no request carries image data. diff --git a/openspec/changes/34-keychain-photo-relief/design.md b/openspec/changes/34-keychain-photo-relief/design.md index 24b2ec3..85a36fd 100644 --- a/openspec/changes/34-keychain-photo-relief/design.md +++ b/openspec/changes/34-keychain-photo-relief/design.md @@ -30,14 +30,16 @@ Depth V2 Small gives a smooth depth map (the owner found it good as a form); on - **Crop.** `crop = {x, y, w, h}` fractions of the image (default full). `buildSilhouette` and `buildGrid` read the planes (gray, alpha, matte, depth) through a cropped view made once per change in the worker; size, text and mount fractions stay relative to the silhouette box. `CropEditor` over the image: drag, corner and edge handles, aspect lock (free, 1:1, 4:3, 3:4), fit to subject from the AI mask box, reset; minimum 5 % of a side; live update of the preview; also in the enlarged mask dialog. Changing the crop never re-runs the matte model. - **Whole frame silhouette.** `silhouette.source: 'frame'`: every pixel of the crop is material; threshold, close, areas and specks are hidden. With the Base plate (rounded rectangle, circle) this is a portrait plaque; the relief edge still rolls off through the bevel ceiling and rim. - **Outline without steps.** Stairs on the edge come from three places, and each is removed: (1) a binary mask quantises the edge to a pixel, so the outline is taken from a continuous field instead (the soft matte of `33`, the grey level for Brightness, a blurred mask otherwise, iso-line at 0.5, sub-pixel); (the smoothing is the one that already works for cutouts: `traceSilhouette` blurs the mask with σ = 1 px at 4 px/mm, that is about 0.25 mm, takes the iso-line at 128 and simplifies it by 0.15 mm; the outline here reuses that routine or its parameters, expressed in mm of the part and exposed as an "Outline smoothness" value, default 0.25 mm, so thin spikes can keep sharper tips with a smaller value); (2) cutting the relief by whole grid cells leaves a staircase of cells, so the outline is traced as a vector polygon (`traceAlpha`, marching squares with sub-pixel interpolation, as for cutouts and text), simplified with a small tolerance (0.03–0.05 mm) and smoothed (Chaikin or a curvature-limited smoothing that keeps real corners such as spike tips), and the relief is clipped by that polygon exactly as the Shape mode already does with `shapeCrossSection` (extrude the polygon as a `CrossSection`, intersect with the relief, so the wall is exact and independent of the grid cell); (3) the grid only supplies heights, so its cell size no longer limits the outline. The PoC shows the effect with marching-squares clipping of the grid cells along the iso-line (`poc/ai/keychain.ts`): the outline is smooth to well under the cell size. Large enclosed areas stay open and small ones between strokes are filled through the existing areas of change 18 (`findHoles`), not a new rule. +- **Clean depth, printable slopes, final smoothing.** A monocular depth map is soft at the subject's edges and does not line up with the image, so the head "bleeds" into the background; and a sum of three sources can produce a wall steeper than a printer can build. Three steps, applied only to the Photo source: (1) **edge snapping**: the depth plane is passed through a guided filter (He et al.; guide = the grey image, box radius `snapRadius` in mm of the part, default 1 mm, regularisation `eps` derived from the user's 0–100 "Snap depth to edges" value, default 60, 0 = off), a linear-time operation of box filters that keeps depth steps where the image has an edge and flattens it where the image is flat; (2) **slope limit**: after the mix, contrast and gamma, the height field is relaxed so that no two neighbouring nodes differ by more than `tan(maxSlope) × cell`, by forward and backward sweeps (a Lipschitz clamp that only lowers peaks and keeps the base), default 50° (0 = off), not applied to `fixed` nodes (level areas) and never below the floor; (3) **final smoothing**: the existing masked Gaussian with a short radius (`finalSmooth`, default 0.2 mm, 0 = off) after the slope limit, so sweeps leave no creases. The order is depth → snap → mix → contrast and gamma → slope limit → final smoothing → bevel ceiling, profile, detail lines, patterns. Presets set the three values. The PoC combiner shows the effect (`poc/ai/keychain.ts`). - **Detail as engraving, not bumps.** Mapping the tone of a photo straight to height turns fur and shading into random bumps. The default Detail mode is **Engrave**: only the darkest features (eyes, nose, mouth, stripes, whisker roots) are pressed in as grooves, `1 − groove` with `groove = smoothstep((dark − t) / (1 − t))`, `dark = 2·max(0, 0.5 − tone)` and a user "dark threshold" `t` (about 0.15 for a face crop), while mid-tones stay smooth; **Relief** (tone as height, the former behaviour) and **Both** remain as options. Grooves read well on a face at keychain scale and do not create noise on the body. The groove depth is the Detail share of the mix. - **Corner-preserving outline.** The outline is a vector polygon traced from the soft field (resampled at ~0.7 px, corners found where the direction turns more than a threshold angle, default 50°, with local-maximum suppression), smoothed by a Gaussian along the curve with the corners held fixed (odd reflection between corners, ring smoothing when there are none), so curves become smooth while spike tips stay sharp; the mesh edge is cut exactly along it (signed distance to the polygon near the edge, marching squares on the cells). The PoC (`poc/ai/outline.ts`) implements this and shows crisp spike tips on the line-art samples. -- **Drawing preset.** Besides Face and Whole animal, a "Drawing / logo" preset (Brightness silhouette, contour only, 60 mm, bevel 1.2 mm, outline smoothness 0.15 mm, large areas open, small ones filled) is chosen automatically for images with few tones and thin strokes. +- **Subject presets (a visible choice).** A "Subject" selector with **Face**, **Whole animal** and **Drawing / logo** sets what the three Photo shares do not: the silhouette source and the detail mode with its dark threshold, the part size, and the outline. Face: AI silhouette, crop to the face, Engrave with dark threshold 0.15; Whole animal: AI silhouette, whole frame crop, Engrave with threshold 0.18, no tone levels; Drawing / logo: Brightness silhouette, contour only, 60 mm, bevel 1.2 mm, outline smoothness 0.15 mm, automatic area opening at 150 mm² (`33`). An "Choose automatically" switch (on for a new image) picks one from the picture (few tones and thin strokes → Drawing; a model-found subject whose box fills the frame → Whole animal; otherwise Face) and turns itself off as soon as the user edits any value, which shows Custom. Each choice is a data record next to the profile presets, and `presetOf` returns Custom when values differ. These are separate from the intensity presets (Subtle, Portrait, Deep). - **Presets.** Subtle / Portrait / Deep set the three shares, `hmax − floor`, boost radius and amount and levels (Portrait: about 30 / 40 / 30, 2 mm range, radius 1.4 mm, 4 soft levels; to be confirmed with the spike) and show Custom when edited; data next to the profile presets. - **Heights and printing.** A height field grows from a flat back (existing design), prints face up without supports. Detail finer than the grid cell is lost: `quality` and the resolution warnings apply, with a hint when the cell is larger than about a third of the boost radius. - **Persistence and migration.** Depth planes are stored in `imageStore` as `depth` records keyed by image, model, revision and crop, restored on load, kept when the model is deleted, dropped with the image, memory fallback with the `persistent` warning. `crop`, mix, boost and levels are keychain params (`migrateKeychain` fills defaults: full crop, `kind` unchanged, boost 0, levels 0). If the stored depth is missing the Depth share counts as zero with a "run again" message, never an empty relief. - **Availability.** The Photo source appears only in Image mode, not for SVG images, and Depth only when a depth model exists; Contour and Detail work without any model. -- **UI.** Relief source: Distance | Brightness | Photo. Under Photo: three share sliders with the mix as a stacked bar, the model picker and Compute depth (disabled with a hint and a manager link without a depth model, active after the download; the shared `AiStatus` with a loader, the current line of the log and Cancel; "for another crop" note), Detail boost (amount, radius as `LengthField`), Tone levels (count, softness), presets, the existing contrast, gamma, invert and smoothing, and a height greyscale view next to the mask preview. `CropEditor` in the Image section; the Silhouette source gains "Whole frame". Strings in en and ru. +- **Outline controls.** Under the Image section: Outline smoothness (`LengthField`, default 0.25 mm) and a collapsed Advanced group with Corner angle (degrees, default 50) and Sampling step (`LengthField`, default 1 mm); the fit tolerance stays at its 0.25 mm default here and `35` adds it with the original-polyline option for both products. The block is one component (`OutlineControls`) that `35` reuses in the photo dialog. +- **UI.** Relief source: Distance | Brightness | Photo. Under Photo: three share sliders with the mix as a stacked bar, the model picker and Compute depth (disabled with a hint and a manager link without a depth model, active after the download; the shared `AiStatus` with a loader, the current line of the log and Cancel; "for another crop" note), Detail boost (amount, radius as `LengthField`), Tone levels (count, softness), presets (Subtle / Portrait / Deep with Custom), the Subject selector (Face, Whole animal, Drawing / logo, with Choose automatically), Snap depth to edges, Max slope (degrees) and Final smoothing (`LengthField`), the existing contrast, gamma, invert and smoothing, and a height greyscale view next to the mask preview. `CropEditor` in the Image section; the Silhouette source gains "Whole frame". Strings in en and ru. - **Performance.** The depth run is a few seconds and cancellable (latest wins); everything else is arithmetic in the worker. ## Risks / Trade-offs diff --git a/openspec/changes/34-keychain-photo-relief/specs/keychain-photo-relief/spec.md b/openspec/changes/34-keychain-photo-relief/specs/keychain-photo-relief/spec.md index ba25f98..08657aa 100644 --- a/openspec/changes/34-keychain-photo-relief/specs/keychain-photo-relief/spec.md +++ b/openspec/changes/34-keychain-photo-relief/specs/keychain-photo-relief/spec.md @@ -138,6 +138,44 @@ The app SHALL offer presets for the Photo source (Subtle, Portrait, Deep) that s - **WHEN** the user edits any of them afterwards - **THEN** the preset indicator shows Custom and the value is kept +### Requirement: Clean depth and printable slopes +The Photo source SHALL snap the depth to the edges of the image so that the subject's depth does not bleed into the background, SHALL limit the steepness of the surface to a maximum slope, and SHALL smooth the result lightly. The user SHALL be able to set the snapping strength, the maximum slope and the final smoothing, and to turn each of them off. With the maximum slope on, no wall of the relief SHALL be steeper than the set angle, except the sides that the silhouette defines. + +#### Scenario: Edge snapping +- **WHEN** the subject has a sharp edge in the image and the depth map is soft there +- **THEN** with snapping on the depth steps where the image edge is, and with snapping off it does not + +#### Scenario: Slope limit +- **WHEN** the mix would make a wall steeper than the maximum slope +- **THEN** the surface is lowered so that the slope between neighbouring points does not exceed the angle + +#### Scenario: Slope off +- **WHEN** the maximum slope is 0 +- **THEN** the surface is not limited + +#### Scenario: Floor and level areas +- **WHEN** the slope limit is applied +- **THEN** no point goes below the minimum height and level areas keep their height + +### Requirement: Subject presets +The app SHALL offer subject presets Face, Whole animal and Drawing / logo, which set the silhouette source, the detail mode and its dark threshold, the part size and the outline for that kind of picture, and SHALL offer to choose one automatically from the picture. The automatic choice SHALL stop as soon as the user edits any of the values, which then show Custom. For Drawing / logo the preset SHALL use the contour only, a 60 mm part, a 1.2 mm bevel, an outline smoothness of 0.15 mm and automatic opening of large areas. + +#### Scenario: Automatic +- **WHEN** an image with a few tones and thin strokes is loaded and automatic choice is on +- **THEN** Drawing / logo is selected + +#### Scenario: Manual +- **WHEN** the user chooses Whole animal +- **THEN** its values are set and automatic choice is turned off + +#### Scenario: Edited +- **WHEN** the user changes any value of a preset +- **THEN** the selector shows Custom and the value is kept + +#### Scenario: Drawing +- **WHEN** Drawing / logo is chosen +- **THEN** the relief uses the contour only and enclosed regions larger than the area threshold are open + ### Requirement: Persistence of the depth result The depth SHALL be stored with the image in the browser's storage and restored after a reload with the source and controls, without running the model again. Deleting the model from the model manager SHALL NOT remove an existing result; replacing or removing the image SHALL. If the stored result is missing, the app SHALL say so, offer to run again and build the relief without the Depth share meanwhile, never an empty relief. If storage is unavailable the result SHALL work for the session and the user SHALL be warned that it will not persist. @@ -175,6 +213,10 @@ The outline of a keychain made from an image SHALL be smooth: it SHALL NOT show - **WHEN** the drawing has a thin pointed spike - **THEN** the spike keeps its tip and its length within the grid cell +#### Scenario: Advanced controls +- **WHEN** the user opens the Advanced group of the outline +- **THEN** the corner angle and the sampling step can be set, and their defaults are restored by reset + #### Scenario: Independent of the grid - **WHEN** the quality setting is made coarser - **THEN** the outline stays smooth and only the relief detail becomes coarser diff --git a/openspec/changes/34-keychain-photo-relief/tasks.md b/openspec/changes/34-keychain-photo-relief/tasks.md index 7390717..12de86b 100644 --- a/openspec/changes/34-keychain-photo-relief/tasks.md +++ b/openspec/changes/34-keychain-photo-relief/tasks.md @@ -11,6 +11,8 @@ - [ ] 2.3 `KeychainParams.crop` and `silhouette.source: 'frame'` (full mask): the cropped view of the planes (gray, alpha, matte, depth) in `buildSilhouette` and `buildGrid`. Tests: no crop leaves volumes unchanged; a half-width crop halves the part box and keeps the relief of that region; the frame source makes the whole crop material and hides tracing steps; the matte is cropped without re-registration. - [ ] 2.3a Exact outline for image silhouettes: the sub-pixel outline from the continuous field (`traceAlpha`), simplification and corner-preserving smoothing, the relief clipped by the polygon as `CrossSection` (as `shapeCrossSection` does in Shape mode), large areas open and small ones filled by the existing area rules. The outline smoothing keeps sharp corners: resample, find corners by the turning angle, Gaussian along the curve with corners fixed. Tests: the outline of a synthetic disc and of a thin spike deviates from the true shape by less than 0.1 grid cell at two qualities, no vertex of the wall lies on a grid line pattern (no staircase: the number of direction changes of the outline stays close to the true shape's), a spike tip keeps its length within one cell, a coarser quality changes only the relief and not the outline, bounding box and volume regressions of the existing keychain tests stay within their tolerances for a traced silhouette. - [ ] 2.3b Detail modes Engrave / Relief / Both with the dark threshold (`1 − groove`), default Engrave; presets Face, Whole animal and Drawing / logo (chosen automatically for few-tone thin-stroke images). Tests: Engrave leaves mid-tones at one level and lowers only pixels darker than the threshold, a lower threshold engraves a superset, Relief equals the former tone mapping, the Drawing preset leaves a one-tone image with contour only. +- [ ] 2.3c Clean depth and slopes in `geometry/raster/`: `guidedFilter(depth, guide, w, h, radiusPx, eps)`, `limitSlope(height, w, h, cell, maxSlopeDeg, fixed, floor)` (forward and backward sweeps) and their use in `buildGrid` in the order of the design (`snap`, `maxSlope`, `finalSmooth` params with defaults 60, 50°, 0.2 mm). Tests: the guided filter keeps a step that coincides with a guide edge and flattens noise where the guide is flat, strength 0 returns the input, a constant plane stays constant; the slope limit keeps every neighbour difference within the bound on a spike and on a cliff, never raises a node, never goes below the floor, leaves `fixed` nodes alone, angle 0 is the identity; the existing source regression volumes are unchanged; the result does not depend on the image resolution at the same mm values. +- [ ] 2.3d Subject presets Face, Whole animal and Drawing / logo as data, `presetOf` with Custom, and the automatic chooser (`chooseSubject(stats, mask box)`: tone count, stroke thinness, box fill). Tests: each preset sets exactly its listed values, a few-tone thin-stroke fixture selects Drawing, a frame-filling subject selects Whole animal, a face crop selects Face, editing any value gives Custom and turns the automatic choice off, defaults of an old record are unchanged. - [ ] 2.4 `migrateKeychain` fills the new fields for old records and normalises partial values. Tests: round trip, partial and corrupt values fall back to defaults, old records keep their source. ## 3. ML and image side @@ -21,7 +23,7 @@ ## 4. UI - [ ] 4.1 `CropEditor` (drag, handles, aspect lock, fit to subject, reset), live update of the mask preview and relief, also reachable from the enlarged mask dialog. Strings in `en.json` and `ru.json`. No artboard exists: built from `ui/` components and `theme.css` tokens, said in the commit; compare in en and ru and at 390 px. Component tests: dragging and handles change the crop, aspect lock, fit to subject uses the mask box, reset, keyboard nudging, minimum size, key parity. -- [ ] 4.2 Relief source Photo and `PhotoReliefBlock`: three share sliders with a stacked bar, depth model picker and Compute depth (disabled with a hint and a manager link without a depth model, `AiStatus` with loader, current line and Cancel, "for another crop" note, missing-depth message), Detail boost (amount, radius as `LengthField`), Tone levels (count, softness), presets Subtle / Portrait / Deep with Custom, existing contrast, gamma, invert and smoothing, height greyscale view, hints (coarse grid, small face); silhouette source "Whole frame" hiding the tracing controls. Component tests: availability per mode and for SVG, shares update the relief with no client call, no depth model disables the button with a hint and a manager link, and it becomes active when the model store reports one; the loader and the current line follow the events of a fake run, presets and Custom, lost depth falls back with a message, hints, key parity, steppers on every numeric input. +- [ ] 4.2 Relief source Photo and `PhotoReliefBlock`: three share sliders with a stacked bar, depth model picker and Compute depth (disabled with a hint and a manager link without a depth model, `AiStatus` with loader, current line and Cancel, "for another crop" note, missing-depth message), Detail boost (amount, radius as `LengthField`), Tone levels (count, softness), presets Subtle / Portrait / Deep with Custom, the Subject selector (Face, Whole animal, Drawing / logo) with Choose automatically, Snap depth to edges, Max slope (degrees) and Final smoothing (`LengthField`), `OutlineControls` in the Image section (smoothness, collapsed Advanced group with corner angle and sampling step), existing contrast, gamma, invert and smoothing, height greyscale view, hints (coarse grid, small face); silhouette source "Whole frame" hiding the tracing controls. Component tests: availability per mode and for SVG, shares update the relief with no client call, no depth model disables the button with a hint and a manager link, and it becomes active when the model store reports one; the loader and the current line follow the events of a fake run, presets, subject presets and Custom, the automatic choice stops on an edit, the Advanced group holds corner angle and step with reset, snapping, slope and smoothing controls update the relief with no client call, lost depth falls back with a message, hints, key parity, steppers on every numeric input. ## 5. Version diff --git a/openspec/changes/35-smooth-outline/design.md b/openspec/changes/35-smooth-outline/design.md index d51a9f4..802395b 100644 --- a/openspec/changes/35-smooth-outline/design.md +++ b/openspec/changes/35-smooth-outline/design.md @@ -11,6 +11,7 @@ - **Sampling.** Each cubic is sampled at about 1 mm along its length (uniform in `t`); the chord error is `step² / (8 R)`, below 0.06 mm for curvature radii above 2 mm, inside the 0.25 mm fit tolerance. Where the radius is smaller the sampling is refined (step ≤ `sqrt(8 R · 0.03)`). The sampled polygon is what goes to the geometry kernel (Manifold needs polygons) and to STL; the curve is an intermediate representation. - **Offset on the curve.** The clearance is applied to the samples along the analytic normals; at a convex corner (left turn of a counter-clockwise loop) round-join points at 15° steps, at a concave corner a mitre on the bisector (scale limited). Where this would self-intersect (clearance larger than the local radius) the kernel's offset (`CrossSection.offset`) is the fallback, as today. - **Same stage for every source.** The stage takes a label mask or a soft field, so classic, AI (with the bicubic soft mask and the optional second pass of `32`) and keychain masks all go through it; the baseline detection code is untouched. +- **Advanced group.** The user-facing controls are Outline smoothness and Fit tolerance; the **corner angle** (degrees, default 50) and the **sampling step** (`LengthField`, default 1 mm) are rarely needed and sit in a collapsed "Advanced" group under them, with a reset to the defaults. `OutlineControls` (introduced for Keychain in `34`) is the one component, extended here with the tolerance and the polyline option and reused by the photo dialog; a lower step or angle gives a finer outline, a higher one fewer vertices (the vertex count is shown). Values are stored with the cutout session or keychain params and migrated with defaults. - **Opt-out.** "Use the original polyline" skips the fit and smoothing, reproducing the previous polygon exactly (this is also what the regression tests compare against). - **Stored data.** An outline cutout keeps `{points, size}`; the points are now the sampled fitted polygon. Old sessions have denser polygons and load unchanged. Optionally the control points are stored with the points for later editing (not used yet). - **Keychain.** The same stage gives the outline of an image silhouette in Keychain: in the PoC the drawing `check` outline (3475 polyline vertices) became 40 Bézier segments (121 control points) and 262 vertices in the model, with wall normals taken analytically from the curve, so the wall shows no ripple either. The top surface of the keychain stays a height field cut along the fitted outline. diff --git a/openspec/changes/35-smooth-outline/proposal.md b/openspec/changes/35-smooth-outline/proposal.md index 62b9fbb..d85a489 100644 --- a/openspec/changes/35-smooth-outline/proposal.md +++ b/openspec/changes/35-smooth-outline/proposal.md @@ -26,4 +26,4 @@ In the PoC (`poc/ai/`) a pocket built from fitted curves instead of the raw poly - New pure module `geometry/outlineFit.ts` (resample, corner detection, along-curve smoothing, Schneider fitting, sampling, offset), used by `traceSilhouette` callers in `features/cutout/photo/createCutouts.ts` and by the keychain outline; `segment.ts`, `colour.ts` and the classic detection are not edited, their frozen-output test (`32`, task 2.1) stays green; the `OutlineSource` points of a cutout become the sampled fitted polygon (the stored `points` keep their shape, so saved sessions load unchanged); strings in en and ru, tests. - Out of scope: changing the classic silhouette detection, editing the outline by hand (node dragging), exporting curves as such (STL and 3MF stay polygonal), smoothing of mesh cutouts (STL imports). - Version 1.7.0 (minor: a new option that also improves existing output). -- Depends on: `25a-photo-trace-improvements`, `29-beta-label`; works with `32` and `34` (they use it) but does not require them. +- Depends on: `25a-photo-trace-improvements`, `29-beta-label`, `34-keychain-photo-relief` (the Keychain outline and `OutlineControls` start there; this change generalises them); `32` uses it but is not required. diff --git a/openspec/changes/35-smooth-outline/specs/smooth-outline/spec.md b/openspec/changes/35-smooth-outline/specs/smooth-outline/spec.md index 65dfcd6..54cc124 100644 --- a/openspec/changes/35-smooth-outline/specs/smooth-outline/spec.md +++ b/openspec/changes/35-smooth-outline/specs/smooth-outline/spec.md @@ -28,6 +28,25 @@ The outline made from a silhouette, whether found by the classic detection, by t - **WHEN** the user chooses to keep the original polyline - **THEN** the outline is the same as before this feature +### Requirement: Advanced outline settings +Besides the smoothness and the fit tolerance, the user SHALL be able to set the angle from which a vertex counts as a corner and the sampling step of the polygon, in an Advanced group that is collapsed by default and can be reset to the defaults. The same controls SHALL be offered for Cutout and for Keychain. + +#### Scenario: Collapsed +- **WHEN** the outline controls are first shown +- **THEN** smoothness and tolerance are visible and the corner angle and sampling step are in a collapsed Advanced group + +#### Scenario: Finer step +- **WHEN** the sampling step is made smaller +- **THEN** the polygon has more vertices and follows the curve more closely + +#### Scenario: Corner angle +- **WHEN** the corner angle is raised above the turning angle of a tip +- **THEN** that tip is smoothed instead of kept sharp + +#### Scenario: Reset +- **WHEN** the user resets the Advanced group +- **THEN** the angle returns to 50 degrees and the step to 1 mm + ### Requirement: Clearance on the smooth outline The clearance of a cutout SHALL be applied to the smooth outline so that the pocket stays smooth: rounded at convex corners, mitred at concave ones, and never smaller than the outline plus the clearance. diff --git a/openspec/changes/35-smooth-outline/tasks.md b/openspec/changes/35-smooth-outline/tasks.md index abc45f2..640a555 100644 --- a/openspec/changes/35-smooth-outline/tasks.md +++ b/openspec/changes/35-smooth-outline/tasks.md @@ -9,7 +9,7 @@ ## 2. UI -- [ ] 2.1 Outline smoothness and fit tolerance under the silhouette step of the photo dialog (lengths, so `LengthField`), the vertex count, the option "original polyline"; strings in `en.json` and `ru.json`. No artboard exists: built from `ui/` components and `theme.css` tokens, said in the commit; compare in en and ru and at 390 px. Component tests: controls update the outline and the count, the polyline option restores the previous result, steppers on every numeric input, key parity. +- [ ] 2.1 Extend `OutlineControls` (introduced for Keychain in `34`) and use it under the silhouette step of the photo dialog: outline smoothness and fit tolerance (lengths, so `LengthField`), the vertex count, the option "original polyline", and a collapsed Advanced group with corner angle (degrees) and sampling step (`LengthField`) and a reset; strings in `en.json` and `ru.json`. No artboard exists: built from `ui/` components and `theme.css` tokens, said in the commit; compare in en and ru and at 390 px. Component tests: controls update the outline and the count, the Advanced group is collapsed by default and its reset restores 50° and 1 mm, a finer step raises the vertex count, the polyline option restores the previous result, steppers on every numeric input, key parity. ## 3. Version From 3c10a74b67f0fb9313b99e85d43b164842b7ebbf Mon Sep 17 00:00:00 2001 From: skarakovski Date: Sun, 25 Oct 2026 11:01:53 +0700 Subject: [PATCH 38/38] plan: 36-site-refresh adds product hues for card art and logo (Gridfinity blue, Keychain green, Rack yellow; interface colours unchanged) Co-Authored-By: Claude Sonnet 5.5 --- openspec/changes/36-site-refresh/design.md | 4 ++++ openspec/changes/36-site-refresh/proposal.md | 3 ++- .../36-site-refresh/specs/app-shell/spec.md | 24 +++++++++++++++++++ openspec/changes/36-site-refresh/tasks.md | 3 ++- 4 files changed, 32 insertions(+), 2 deletions(-) diff --git a/openspec/changes/36-site-refresh/design.md b/openspec/changes/36-site-refresh/design.md index d280751..4fc352f 100644 --- a/openspec/changes/36-site-refresh/design.md +++ b/openspec/changes/36-site-refresh/design.md @@ -33,6 +33,9 @@ Card texts are i18n keys: `products..{name,desc,tag1,tag2}` for the root pag - **Keychain art from the ring emblem.** `scripts/art/ring.jpg` is the PoC picture; `npm run art` decodes it with `jpeg-js` (already a dev dependency), makes a `GrayImage`, runs the Keychain Image pipeline (`generate('keychain')`) with default relief and size about 50 mm, the large enclosed area opened (the emblem's hole) and the small ones between strokes filled (the same rule as the areas of change 18), and draws it like the other keychain art, with the existing smoothing. No eyelet is added: the emblem already has a through hole, and an eyelet on a spiky silhouette looks accidental; the alternative is a small eyelet at the top spike, rejected for the look. The paw module `geometry/art/paw.ts` stays for tests until it is no longer used, then is removed. - **Scale and air.** The Gridfinity cards are the reference: their drawing keeps the margin that the owner is happy with. The Keychain Image card is scaled so its drawn height is a fixed fraction of the box drawing's (`KEYCHAIN_ART_RATIO`, about 0.8, set by eye against the cards in the browser and written down) instead of equal to it, and the width limit stays. Every illustration is measured by `projectedSize` in the script and written to `src/app/art/manifest.json` as fractions of the 250 × 165 field; the test asserts at least 6 % of free space on each side for every card and that the Keychain Image drawing is not taller than the Gridfinity box. - **Rack at 1 U.** The panel is `defaultRackPanel` with `units: 1` and the Skadis pattern (the 1 U plate has less room, so the pattern's region is checked: a plate that shows no pattern element is not acceptable, the pattern pitch may be reduced for the art only); the enclosure uses a small device (about 25 mm high so that the shell fits 1 U) with one keystone; the shelf uses `units: 1` with the default shelf and side walls capped to what a 1 U plate allows. The three Rack drawings share one scale chosen so the widest of them (the 254 mm width) fits the field with the Gridfinity margin; they are drawn in one group of the script, which also makes their sizes comparable. +- **Hue per product.** `Product.hue: 'gridfinity' | 'keychain' | 'rack'` (the id doubles as the hue name). `theme.css` keeps `--color-art-0…6` for Gridfinity (blue, unchanged) and adds `--color-art-keychain-0…6` (green) and `--color-art-rack-0…6` (yellow), with the same lightness steps as the blue ladder so shading reads the same; the edge colour is shared. `ModeArt` gets the hue from the art id (`box`, `baseplate`, `cutout` → Gridfinity; `keychainImage`, `keychainShape` → Keychain; `rackPanel`, `rackCase`, `rackShelf` → Rack) and sets `data-hue` on its wrapper; the rules `[data-hue=keychain] .art-t0 { fill: var(--color-art-keychain-0) }` and so on override the fills, so the SVG files are untouched and the card art still follows the tokens. Starting values (to tune by eye against the dark surface): green `#1b2a22 #25402f #345a43 #437557 #58926d #78b08a #a1cfb0`, yellow `#2a2814 #45401a #625b21 #827927 #a39a33 #c5bb52 #e3dc86`. Yellow is kept warm-yellow, not orange, so it does not read as the Okabe–Ito orange of the cutout colours. +- **Logo.** `Logo` takes `hue` and draws its faces from tokens `--color-logo-{top,left,right}` per hue (blue `#7c9cff / #4a63b8 / #34478f` as now; green about `#7edc9d / #3f9a5e / #2e7547`; yellow about `#f0d95c / #b89e2a / #8a7620`), the dark outline unchanged. `Header` passes the active product's hue; on the root page and on routes without a product the logo is blue. The product name text, the buttons and everything else in the header keep their colours. +- **Not coloured.** The interface accent (`--color-accent`), focus rings, selected states, banners and the 3D viewport keep their colours in all products; only the pictures and the logo are coded by product. The colour is never the only cue: the product name and icon stay. - **Weight and tokens.** The 60 KB budget and the colour tokens stay; if a 1 U drawing is heavier, the smoothing or the trace tolerance is raised for it. ## Risks / Trade-offs @@ -40,6 +43,7 @@ Card texts are i18n keys: `products..{name,desc,tag1,tag2}` for the root pag - [The ring is a drawing, so the render is a flat plaque with the emblem's silhouette] → the relief profile (bevel) and the two tone passes give it volume; if it looks flat the default profile "dome" is used for the art only. - [A 1 U panel is a very thin strip and looks small on the card] → the shared Rack scale and the isometric view keep it readable; the card height does not change; the check is by eye in the browser. - [Texts drift again] → the style rules are in the spec; a test keeps the tags to two per card and at most two words each, and forbids the words "soon" and "скоро" on cards. +- [Green and yellow art looks muddy on the dark card] → the ladders keep the lightness steps of the blue one, values are tuned by eye in the browser; the 6 % margin and weight checks do not depend on colour. - [The README repeats the specs] → it stays a summary with links, not a copy. ## Open Questions diff --git a/openspec/changes/36-site-refresh/proposal.md b/openspec/changes/36-site-refresh/proposal.md index c91647c..c534a66 100644 --- a/openspec/changes/36-site-refresh/proposal.md +++ b/openspec/changes/36-site-refresh/proposal.md @@ -14,6 +14,7 @@ The site and the README were written when PrintKit had three Gridfinity modes an - **README.** Bring it up to date: what is in each product and mode, the beta status and where the version and the changelog are, a short privacy note (the work stays in the browser; visit counting only after consent), the commands, the layout including folders that exist (for example `poc/`), the link to the roadmap, the references, and the badges. The motivation text stays identical to the one on the site. - **Keychain card illustration.** Replace the paw by the ring emblem from the PoC (`poc/ai/samples/ring.jpeg`, an outlined emblem with a large through hole and sharp spikes), rendered by `npm run art` from that picture through the real Keychain Image pipeline, and reduce its scale so that it leaves the same air as the Gridfinity illustrations (which are the reference) instead of filling the field. - **Rack card illustrations.** All three at 1 U: a 1 U Skadis panel, a 1 U enclosure around a small device, a 1 U shelf; one scale for the three Rack cards (the rack is the same width in all of them) with margins like the Gridfinity cards. +- **Colour by product, in the pictures and the logo only.** The start-page illustrations and the logo in the header take the hue of their product: Gridfinity stays blue (as now), Keychain green, Rack yellow. Only the hue of the illustrations' tone ladder and of the logo's faces changes; the interface (accent, buttons, borders, focus rings, text) keeps its colours in every product. The illustration files do not change (they carry only classes); the hue comes from token sets chosen by the product. On the root page and the start pages the logo stays blue as today. - **A check for the margins.** `npm run art` records the drawn size of every illustration as a fraction of the card field in a small manifest, and a test keeps every illustration inside a margin and the Keychain illustration no larger than the Gridfinity box. ## Capabilities @@ -26,7 +27,7 @@ The site and the README were written when PrintKit had three Gridfinity modes an ## Impact -- `src/i18n/en.json` and `ru.json` (the `start.*` and `products.*` keys of the cards), `README.md`, `scripts/render-art.ts` (the ring source, the Rack models at 1 U, the scales), `scripts/art/ring.jpg` (the source picture), `src/app/art/{keychainImage,rackPanel,rackCase,rackShelf}.svg`, a new `src/app/art/manifest.json`, `ModeArt.test.tsx` and a margin test; `CLAUDE.md` (the art paragraph). +- `src/i18n/en.json` and `ru.json` (the `start.*` and `products.*` keys of the cards), `README.md`, `scripts/render-art.ts` (the ring source, the Rack models at 1 U, the scales), `scripts/art/ring.jpg` (the source picture), `src/app/art/{keychainImage,rackPanel,rackCase,rackShelf}.svg`, a new `src/app/art/manifest.json`, `ModeArt.test.tsx` and a margin test; `src/app/theme.css` (two more tone ladders and the logo colours per hue), `src/app/ModeArt.tsx` and `Logo.tsx` (a `hue` prop), `Header.tsx` (passes the product's hue); `CLAUDE.md` (the art paragraph). - No change to geometry, modes or routes. - Out of scope: new features, screenshots in the README, the product names, the footer and consent texts, texts of the workspaces (panels, sections), texts of the AI changes (they add their own when they land), redrawing the Gridfinity or Keychain Shape illustrations. - Version 1.7.1 (patch: content and illustrations, no new capability). diff --git a/openspec/changes/36-site-refresh/specs/app-shell/spec.md b/openspec/changes/36-site-refresh/specs/app-shell/spec.md index aebf7aa..0b50781 100644 --- a/openspec/changes/36-site-refresh/specs/app-shell/spec.md +++ b/openspec/changes/36-site-refresh/specs/app-shell/spec.md @@ -77,3 +77,27 @@ Every product and mode card SHALL have a localized description and exactly two s #### Scenario: Same term everywhere - **WHEN** the Russian cards are read - **THEN** a name such as keystone is written the same way on every card where it occurs + +### Requirement: Product colour in illustrations and logo +The start-page illustrations and the logo in the top bar SHALL take the hue of their product: blue for Gridfinity, green for Keychain and yellow for Rack. Only the hue of the illustrations and of the logo SHALL differ; the colours of the interface (accent, buttons, borders, focus, text, banners) and of the 3D viewport SHALL be the same in every product. The logo SHALL be blue where no product is active. The illustrations SHALL keep following colour tokens. + +#### Scenario: Cards by product +- **WHEN** the root start page is shown +- **THEN** the Gridfinity card illustration is blue, the Keychain one green and the Rack one yellow + +#### Scenario: Mode cards +- **WHEN** a product's start page is shown +- **THEN** all its mode illustrations have that product's hue + +#### Scenario: Logo +- **WHEN** the user opens a product +- **THEN** the logo in the top bar has that product's hue, and on the root page it is blue + +#### Scenario: Interface unchanged +- **WHEN** the user moves between products +- **THEN** buttons, selected states, focus rings and links keep the same colours + +#### Scenario: Token driven +- **WHEN** a hue token changes +- **THEN** the illustrations of that product change with it without regenerating the image files + diff --git a/openspec/changes/36-site-refresh/tasks.md b/openspec/changes/36-site-refresh/tasks.md index 80046c9..c567c16 100644 --- a/openspec/changes/36-site-refresh/tasks.md +++ b/openspec/changes/36-site-refresh/tasks.md @@ -13,6 +13,7 @@ - [ ] 3.1 Keychain card: add `scripts/art/ring.jpg` (the PoC `ring.jpeg`), make `npm run art` render it through `generate('keychain')` (decoded with `jpeg-js`, size about 50 mm, the large area open and the small ones filled), drop the paw from the script (keep `paw.ts` until unused), and scale it with `KEYCHAIN_ART_RATIO` (about 0.8 of the box drawing's height, set by eye against the Gridfinity cards). Verify: `npm run art` writes `keychainImage.svg`, the weight is within 60 KB, the card looks level with the Gridfinity cards in the browser (en and ru, 390 px). - [ ] 3.2 Rack cards at 1 U: panel `units: 1` with the Skadis pattern showing, a small device (about 25 mm high) in the enclosure with one keystone, shelf `units: 1`; the three drawn in one group at one scale with the Gridfinity margin. Verify: `npm run art` writes the three files within 60 KB, all three show a 1 U part, compared in the browser. +- [ ] 3.2a Colour by product: `Product.hue`, the two extra tone ladders and the logo colours in `theme.css`, `ModeArt` setting `data-hue` from the art id, `Logo` with a `hue` prop, `Header` passing the product's hue (blue on the root page). Tests: each art id maps to the right hue, the wrapper carries `data-hue`, the SVG files are unchanged (no colour literals, only classes), the logo gets the hue of the active route and blue without a product, the three ladders define all seven tones, the accent, focus and button tokens do not depend on the hue. Verify: compare the root page and the three start pages in the browser (Gridfinity blue, Keychain green, Rack yellow), the header logo on each product; tune the ladder values by eye. - [ ] 3.3 `npm run art` writes `src/app/art/manifest.json` (every illustration's drawn width and height as fractions of the 250 × 165 field); a test asserts at least 6 % free space on each side of every illustration and that the Keychain Image drawing is not taller than the Gridfinity box. Update the art paragraph of `CLAUDE.md`. Tests: the margin test, the existing weight test. ## 4. Version @@ -22,4 +23,4 @@ ## 5. Verification - [ ] 5.1 `npm test` and `npm run build` are green, the dev server starts, no console errors. -- [ ] 5.2 In the browser, en and ru and at 390 px: the root page and the three product start pages show the new texts and tags, no placeholder, the Keychain Image card shows the ring emblem with its hole open and the Rack cards show 1 U parts, the card rows look level and have the same air, the README reads correctly on GitHub. +- [ ] 5.2 In the browser, en and ru and at 390 px: the root page and the three product start pages show the new texts and tags, no placeholder, the Keychain Image card shows the ring emblem with its hole open and the Rack cards show 1 U parts, the card rows look level and have the same air, the pictures and the logo are blue, green and yellow for Gridfinity, Keychain and Rack while buttons, accents and focus rings keep their colours, the README reads correctly on GitHub.

    PoC (change 32): нейросетевые силуэты на выпрямленном листе · Keychain: пайплайн →

    Uhhgo*N#>bCS>fq&s%F&2VA;QH2>@upC}3^(-#t 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⩔(p< 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 PF{F9ZEg7H+{|5gDnxeEZ literal 0 HcmV?d00001 diff --git a/poc/ai/index.html b/poc/ai/index.html index 44e4904..494e3e1 100644 --- a/poc/ai/index.html +++ b/poc/ai/index.html @@ -10,7 +10,7 @@