Skip to content

Sharp Frames v0.4.0 - #3

Merged
shrimbly merged 22 commits into
masterfrom
develop
Jul 18, 2026
Merged

shrimbly merged 22 commits into
masterfrom
develop

Conversation

@shrimbly

@shrimbly shrimbly commented Jul 17, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Promotes develop to master for the Sharp Frames v0.4.0 release: 17 commits, 53 files changed (+8,374 / −3,773) since v0.3.1.

New features

  • Full-frame interactive timeline — the selection chart now shows every analyzed frame (previously capped at 100) in a horizontally scrollable, keyboard-accessible timeline with fractional-block resolution. Click any bar to inspect that frame; PgUp/PgDn steps through selected frames, Ctrl+PgUp/PgDn pages, Home/End jumps.
  • Inline frame preview — clicking a chart bar (or auto-following the first selected frame) renders the original frame directly in the terminal via Sixel or Kitty graphics (textual-image), with a graceful fallback and O to open the frame in the system viewer.
  • Color space detection and conversion — iPhone/HDR sources (BT.2020, HLG, PQ) are detected via ffprobe and converted to sRGB/BT.709 through FFmpeg zscale, with tone mapping, width-aware filter chains, and an explicit error for unsupported BT.2020 constant-luminance video.
  • Resolution-normalized sharpness scoring — focus scores are comparable across source resolutions, improving selection consistency.
  • Trend-aware outlier detection — outlier removal accounts for local sharpness trends with improved defaults and sensitivity mapping.
  • Responsive TUI layout — a compact layout tier keeps the frame preview and chart in the viewport on short terminals (down to 80×24) while preserving the spacious layout on large ones; the processing screen is vertically centered; controls and parameter steppers were tightened and polished.
  • Shared image output helpers — a single alpha-preserving decode/resize/encode path used by both frame extraction and image-directory processing.

Reliability and correctness

  • Hardened FFmpeg/FFprobe subprocess handling: cancellation, timeouts, output draining, Windows-safe startup, and temp-directory cleanup.
  • Preview and execution share the same selection rules, so displayed counts match saved frames.
  • Unreadable or partially-read images are excluded and reported instead of scoring as valid zero-sharpness frames.
  • Image exports handle format conversion, partial failures, metadata accuracy, and case-insensitive filename collisions.
  • Start Over after saving returns to the first setup step with cleared configuration; Cancel during setup fully exits the TUI with a cancelled status.
  • Clearer, platform-aware dependency guidance (including Windows FFmpeg PATH instructions).

Dependencies and CI

  • Textual raised to >=5.0.0; new dependencies textual-image (inline raster preview) and Pillow.
  • CI runs Linux, macOS, and Windows across Python 3.10–3.13, plus a job pinned to the minimum supported Textual version.

Validation

  • python -m pytest -q — 393 passed (also green against textual==5.0.0)
  • Headless layout verification of the selection screen at terminal sizes from 200×60 down to 80×24

Release checklist

  • CI passes on this PR
  • Merge to master
  • Create and push annotated tag v0.4.0 from the release commit
  • Publish the GitHub release using RELEASE_NOTES.md
  • Publish sharp-frames==0.4.0 to the package index, if applicable

🤖 Generated with Claude Code

Geddart and others added 22 commits January 14, 2026 17:26
iPhones record video in Display P3, BT.2020, or HDR (HLG/Dolby Vision),
but sharp_frames extracted frames without any color space conversion,
resulting in incorrect colors.

This commit adds:
- New colorspace.py module for detecting video color space via ffprobe
- Automatic conversion to sRGB/BT.709 for wide gamut SDR content
- HDR to SDR tone mapping using Hable algorithm (via zscale filter)
- Graceful fallback when zscale is unavailable

The fix applies to all three extraction code paths:
- FrameExtractor (new modular extractor)
- SharpFrames processor (legacy)
- MinimalProgressSharpFrames (TUI)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Fixes:
- Remove unused List import from colorspace.py
- Add __all__ export list for clear public API
- Fix type hint: Optional[Any] -> Optional['VideoColorInfo']
- HDR filter now explicitly specifies input transfer/primaries/matrix
  for more reliable conversion
- Optimize ffprobe calls: extract color info from existing video_info
  instead of making a separate subprocess call
- Add parse_color_info_from_stream() as public API for reuse

Tests:
- Add comprehensive unit tests for colorspace module
- Test VideoColorInfo.needs_conversion for all color space types
- Test parse_color_info_from_stream with various ffprobe outputs
- Test build_colorspace_filter for SDR, wide gamut, and HDR
- Test zscale availability detection
- Mock subprocess calls for isolated testing

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add color space detection and conversion for iPhone videos
- inline Sixel/Kitty source frame preview with chart click-to-inspect
- shared image transcode helpers that preserve alpha; HDR filter refinements
- compact layout tier keeps the preview and chart in the viewport on short
  terminals
- vertically center the processing screen
- Start Over resets setup to step 1 and Cancel fully exits the TUI
- require textual>=5.0 with textual-image and Pillow; CI matrix updates

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The test slept for the nominal animation duration plus a fixed grace
period, which slow Windows CI runners exceed. Completion is now observed
through the recorded frame callbacks with a polling deadline.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Per-frame one-shot timers whose deadlines sit closer together than the
platform timer resolution fire out of order on Windows, which could end
the shimmer pass on a highlighted frame instead of the original title.
A single interval timer advances the frame index, making the order and
final frame structural. The completion test now asserts the exact frame
sequence.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
An interval tick already queued when the timer stops can invoke the
callback once more on a stalled event loop, re-showing the final frame.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@shrimbly
shrimbly merged commit 95fca3c into master Jul 18, 2026
28 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants