Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
name: CI

on:
push:
branches: [main]
pull_request:

permissions:
contents: read

jobs:
python:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.11"
cache: pip
- run: python -m pip install -e ".[dev]"
- run: ruff check .
- run: pytest

web:
runs-on: ubuntu-latest
defaults:
run:
working-directory: web
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with:
version: 11.9.0
- uses: actions/setup-node@v4
with:
node-version: "22"
cache: pnpm
cache-dependency-path: web/pnpm-lock.yaml
- run: pnpm install --frozen-lockfile
- run: pnpm test
- run: pnpm build
25 changes: 25 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ __pycache__/
htmlcov/
build/
dist/
web/node_modules/
web/dist/
.pnpm-store/
*.egg-info/
.reeftone_cache/
exports/
Expand All @@ -21,8 +24,30 @@ exports/
*.jpg
*.JPEG
*.jpeg
*.PNG
*.png
*.TIF
*.tif
*.TIFF
*.tiff
*.WEBP
*.webp
*.AVIF
*.avif
*.DNG
*.dng
*.XMP
*.xmp
*.MOV
*.mov
*.MP4
*.mp4
*.M4V
*.m4v
*.AVI
*.avi
*.MKV
*.mkv

# Public application artwork is reviewed and checked in deliberately.
!web/public/*.png
96 changes: 94 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,102 @@ small refinements.

## [Unreleased]

### Added

- Separate browser-local PWA alpha in `web/`
- Reliable device-local Canvas photo preview
- On-device JPEG/PNG opening, adjustments, comparison, and full-resolution export
- Browser capability reporting for GPU, Display P3, HDR display, OffscreenCanvas,
and WebAssembly support
- Automated web color-math tests and production build validation
- Web versions of the five curated looks and the full Light, Auto Restore,
White Balance, and Presence control groups
- Per-setting reset and bypass, undo/redo, copied edits, neutral eyedropper,
swipe/full-image comparison, magnifier zoom, and drag-to-pan
- Browser-side underwater scene analysis and the shared Python settings contract
- Web parity with the canonical desktop typography, spacing, toolbar, inspector,
Levels graph, disabled states, and responsive breakpoints
- Browser Green balance and five-point RGB/Red/Green/Blue Levels in preview
and full-resolution worker export
- Browser thresholded unsharp-mask Sharpening with Amount, Radius, Threshold,
reset/bypass, undo/redo, copied edits, and worker-export parity

### Fixed

- Hosted assets now use the deployment platform's expected client bundle
- The corrected preview no longer uses the silent-failing WebGPU shader path;
a reliable local Canvas renderer prevents black after-images
- Navigation now checks the network before an offline cache to prevent stale
alpha releases from remaining visible after deployment
- Web magnifier focal zoom now follows the desktop 25-percentage-point step
- Unsupported web formats and color workflows remain visible in their desktop
locations but are explicitly disabled instead of appearing functional
- Web balance labels now consistently read Red balance, Green balance, and Blue
balance while preserving their serialized keys
- Web typography, icons, control geometry, stacking, and responsive breakpoints
now consume the canonical desktop 1.5× stylesheet directly
- Whole-image Before/After is now the default web comparison mode; Swipe is an
explicit accessible opt-in
- Loaded photos now remain sticky beneath the web app header at mobile widths,
keeping the preview visible while adjustment controls scroll
- Mobile web editing now removes the floating Color information card and
low-priority preview metadata, keeps Before/After visible beside the looks,
reduces header space, and gives the photo a substantially larger sticky area
- Mobile adjustment labels, values, reset, and bypass actions now share one row
above each slider; section/control spacing and top look boxes are moderately
tighter while desktop geometry remains unchanged
- Mobile adjustment values now sit at the true center above their sliders,
independent of the right-aligned reset and bypass actions
- Web Sharpening now uses the standard Presence heading hierarchy without a
special card border, background, or inset padding
- Desktop web editing now keeps the image workspace sticky on the left while
the complete settings panel occupies and scrolls in the right column; the
mobile layout is unchanged

### Planned

- Native HDR gain-map editing and reconstruction
- Transfer-function-aware HLG and PQ processing
- Temporally stable HEVC video correction
- Hosted web edition after the local workflow is validated
- Shared color-managed WebAssembly core for Python/web algorithm parity

## [0.6.0] - 2026-07-28

### Added

- Output-neutral, float32 unsharp-mask Sharpening in Presence with Amount,
Radius, and Threshold controls
- Per-control and group reset/bypass, undo/redo, copied-settings, preview, API,
session, and full-resolution export support for Sharpening
- Sharpening coverage for neutral output, radius behavior, threshold noise
suppression, finite edge enhancement, clipping safety, and legacy settings

### Changed

- Visible underwater white-balance controls are now consistently named
Red balance, Green balance, and Blue balance while their serialized keys and
established adaptive processing remain backward compatible
- All desktop text and icon sizes, related control geometry, panel sizing, and
responsive breakpoints now resolve at exactly 1.5× the previous canonical scale
- Whole-image Before/After is now the default comparison mode when a photo opens
or adjustments reset; Swipe is an explicit opt-in and remains hidden until used
- The desktop UI/settings contract now records the exact 0.6.0 reference required
for the subsequent web parity pass

## [0.5.0] - 2026-07-28

### Added

- Neutral-by-default Green correction beside Red recovery and Blue balance
- Five-point monotonic Levels for RGB, Red, Green, and Blue, with a histogram,
channel reset/bypass, keyboard control, undo/redo, copied edits, and export parity
- A checked-in desktop UI/settings contract as the reference for web parity
- Representative identity, monotonicity, channel-isolation, and non-black
image-math coverage

### Changed

- Point-centered magnifier steps now move in 25% increments

## [0.4.1] - 2026-07-26

Expand Down Expand Up @@ -79,7 +169,9 @@ small refinements.
- Local FastAPI service and responsive editor
- Non-destructive sessions, presets, undo/redo, and before/after comparison

[Unreleased]: https://github.com/TomerSY/ReefTone/compare/v0.4.1...HEAD
[Unreleased]: https://github.com/TomerSY/ReefTone/compare/v0.6.0...HEAD
[0.6.0]: https://github.com/TomerSY/ReefTone/compare/v0.5.0...v0.6.0
[0.5.0]: https://github.com/TomerSY/ReefTone/compare/v0.4.1...v0.5.0
[0.4.1]: https://github.com/TomerSY/ReefTone/releases/tag/v0.4.1
[0.4.0]: https://github.com/TomerSY/ReefTone/compare/v0.3.0...v0.4.0
[0.3.0]: https://github.com/TomerSY/ReefTone/compare/v0.2.0...v0.3.0
Expand Down
44 changes: 36 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,14 @@ like a focused desktop editor today and can grow into a hosted product later.
- base bit-depth, ICC/P3, transfer-function, HDR gain-map, and auxiliary-image detection
- float32 processing from decode through correction
- scene analysis for red attenuation, cyan cast, haze, and low light
- underwater red compensation and confidence-gated white balance
- underwater-aware Red, Green, and Blue balance with confidence-gated white balance
- research-backed multiscale fusion of color-balanced and contrast-enhanced inputs
- exposure, contrast, black/white points, tone, vibrance, clarity, and denoise
- exposure, contrast, five-point RGB/channel Levels, tone, vibrance, clarity,
thresholded unsharp-mask sharpening, and denoise
- a neutral-point eyedropper plus per-slider reset and non-destructive bypass
- a real-time before/after editor with distinct presets, undo/redo, drag-and-drop,
and keyboard-accessible controls
- point-centered magnifier zoom, fixed pixel-ratio views, and drag-to-pan
- point-centered 25% magnifier zoom, fixed pixel-ratio views, and drag-to-pan
- collapsible source color-space and dynamic-range information
- full-resolution JPEG, PNG, and 16-bit TIFF export
- 10-bit HEIC export with source ICC, EXIF, and available XMP preservation
Expand Down Expand Up @@ -56,11 +57,16 @@ screen. You can also drag a file anywhere over the app.

1. Open a photo and start with **Natural**. Use **Dramatic** for a warmer,
darker, high-contrast treatment.
2. Use the comparison divider to check skin, coral, and open-water gradients.
3. Adjust **Red recovery** before adding saturation. This restores missing balance
more naturally than globally boosting color.
4. Use **Fusion clarity** to blend in the multiscale contrast branch.
5. Export a JPEG for sharing or a 16-bit TIFF as a high-quality editing master.
2. Hold **Before** for a whole-image check, or explicitly enable **Swipe** for a
draggable divider.
3. Adjust **Red balance** before adding saturation. It is an underwater-aware
recovery control, not a naive red-channel multiplier.
4. Use **Green balance** when green dominates after Red balance, then refine tonal
placement with the five Levels markers. **Blue balance** handles blue dominance.
5. Use **Fusion clarity** to blend in the multiscale contrast branch.
6. Add **Sharpening** only when needed: Amount controls strength, Radius controls
the edge scale in pixels, and Threshold protects fine noise.
7. Export a JPEG for sharing or a 16-bit TIFF as a high-quality editing master.

Use the eyedropper only on something that should be gray, white, or neutral.
ReefTone averages a small patch and balances it without changing any other slider.
Expand All @@ -84,6 +90,7 @@ src/reeftone/
app.py Local FastAPI application
session.py Bounded, private preview sessions
static/ Responsive editor UI
web/ Browser-local PWA alpha
tests/ Unit and API tests
docs/ Architecture and roadmap
```
Expand Down Expand Up @@ -118,12 +125,33 @@ JPEG photographs rather than camera RAW development. See
[the algorithm notes](docs/algorithm.md) for details and limitations. The measurements
behind the Dramatic look are recorded in
[the reference-style analysis](docs/reference-style-analysis.md).
The exact desktop labels, layout tokens, and serialized setting behavior are the
reference for web parity in [the desktop UI contract](docs/desktop-ui-contract.md).

Apple gain-map HDR is now detected and reported separately from the SDR base.
ReefTone does not copy an unchanged gain map onto edited pixels or falsely tag an
SDR result as HDR. The exact preservation matrix and native HDR roadmap are in
[the color-management notes](docs/color-management.md).

## Browser-local web alpha

The new [`web/`](web/) workspace is the beginning of a free public edition that
does its image work on the user's device. The first alpha opens JPEG/PNG files,
renders a reliable local Canvas preview, and exports locally. It has no photo upload
endpoint and does not change the Python editor.

This alpha is a product and performance foundation, not yet a color-management
replacement for the Python app. HEIC, embedded metadata preservation, Display P3,
HDR output, algorithm parity, and video remain disabled until they can be
validated end to end. See the [web alpha notes](web/README.md) for its exact scope.

```bash
cd web
pnpm install
pnpm test
pnpm dev
```

## Video roadmap

The correction function already accepts a single RGB array with no UI or file-system
Expand Down
33 changes: 25 additions & 8 deletions docs/algorithm.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
## Design boundary

Underwater restoration is ill-posed: attenuation varies with wavelength, range,
water type, illumination, and camera response. ReefTone 0.3 uses a deterministic
water type, illumination, and camera response. ReefTone uses a deterministic
single-image enhancement pipeline. It does not claim to recover physically measured
surface colors without range data.

Expand Down Expand Up @@ -35,7 +35,7 @@ Decode + orient → RGB float32
↓
Robust scene analysis
↓
Red compensation + adaptive gray-world balance
Underwater-aware Red + Green + Blue balance and adaptive gray-world balance
↓
Optional user-sampled neutral-point gains
↓
Expand All @@ -44,7 +44,9 @@ Contrast/CLAHE branch ─┼→ Gaussian/Laplacian pyramid fusion
↓ ┘
Exposure → black/white points → tone curve
↓
Color → clarity/denoise → non-destructive master mix
RGB Levels → Red → Green → Blue Levels
↓
Color → clarity/denoise → thresholded sharpening → non-destructive master mix
```

The same `correct_image()` function is used for preview, full-resolution export,
Expand All @@ -59,7 +61,7 @@ cast, low dynamic range, low light, and underwater confidence.

### 2. Underwater white balance

Red recovery follows the Ancuti-style compensation shape:
Red balance retains the established Ancuti-style compensation shape:

```text
R' = R + α(Ḡ - R̄)(1 - R)G
Expand All @@ -71,7 +73,11 @@ magenta. After compensation, robust channel means are measured again and a
confidence-weighted gray-world gain brings red closer to green without forcing the
entire water column to neutral gray.

Temperature and tint are direct creative gains and remain neutral at zero.
Green balance is a scene-relative artist control. Positive values move a
green-heavy channel toward the mean of red and blue; negative values can restore
green when it is deficient. Blue balance remains scene-adaptive, and Temperature
and Tint remain direct creative gains. The serialized keys remain
`red_recovery`, `green_correction`, and `blue_balance` for backward compatibility.

### 3. Neutral-point eyedropper

Expand Down Expand Up @@ -99,20 +105,31 @@ controls how strongly the fused result replaces the balanced base.
- shadows and highlights use luminance masks;
- black point and white point remap the endpoints before contrast;
- contrast uses an exponential mid-gray pivot so small changes are visible;
- Levels uses five fixed input anchors and monotone cubic interpolation, applying
RGB before the Red, Green, and Blue channel curves;
- vibrance preferentially affects low-saturation colors;
- clarity is a mid-frequency unsharp mask;
- denoise is an edge-preserving bilateral blend.
- denoise is an edge-preserving bilateral blend;
- Sharpening uses a final float32 unsharp mask. Amount is 0–200%, Radius is
0.3–5.0 pixels, and Threshold is a 0–20% luminance-difference gate that suppresses
small noise before detail is added. Amount defaults to zero, so legacy settings
and every curated preset remain output-neutral.

### 6. Non-destructive controls

Every slider has two local actions:

- reset sets only that setting to its neutral value of zero;
- bypass sends zero for that setting while preserving its chosen value.
- reset sets only that setting to its documented default;
- bypass sends its documented neutral value while preserving its chosen value.

Preset values are intentionally separated, and the master mix blends the entire
corrected result with the untouched source.

Levels has the same behavior at channel scope. Reset restores the identity
`0, 0.25, 0.5, 0.75, 1` curve, while bypass temporarily substitutes that identity
without discarding the edited markers. Marker ordering is enforced in the UI and
again when API settings are parsed.

## Precision and limitations

All processing arithmetic is float32. Export quantizes only in the encoder: JPEG
Expand Down
Loading
Loading