Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
8b428f7
Add color space detection and conversion for iPhone videos
Geddart Jan 14, 2026
40edfeb
Address code review feedback for color space handling
Geddart Jan 14, 2026
d492060
Merge pull request #1 from Geddart/fix/colorspace-conversion
shrimbly Jul 14, 2026
e712afa
Fix color space conversion filters
shrimbly Jul 14, 2026
27465a2
fix: harden selection and processing pipelines
shrimbly Jul 14, 2026
63f6e0d
feat: add scrollable frame timeline
shrimbly Jul 14, 2026
7c670a3
fix: separate frame chart bars
shrimbly Jul 14, 2026
1b94875
fix: match chart canvas to app background
shrimbly Jul 14, 2026
707399f
prepare v0.4.0 release
shrimbly Jul 15, 2026
86dd1c6
fix Windows FFmpeg installation hint
shrimbly Jul 15, 2026
5499360
improve outlier detection defaults
shrimbly Jul 15, 2026
b5e444a
clarify Windows system PATH guidance
shrimbly Jul 15, 2026
1f44d30
preserve Windows PATH guidance contract
shrimbly Jul 15, 2026
1b8405b
make outlier detection trend aware
shrimbly Jul 15, 2026
34b8e83
refine chart resolution and parameter spacing
shrimbly Jul 15, 2026
f4ee4af
add click-to-inspect frame chart
shrimbly Jul 15, 2026
8fcf84d
add inline raster preview and responsive selection layout
shrimbly Jul 17, 2026
158af78
update release notes and changelog for full v0.4.0 scope
shrimbly Jul 17, 2026
c2a4e70
deflake title shimmer completion test
shrimbly Jul 17, 2026
37cc1b9
drive title shimmer from one interval timer
shrimbly Jul 17, 2026
80517a1
fix changelog URL to point at master branch
shrimbly Jul 18, 2026
6b02cb5
guard shimmer advance against a queued post-stop tick
shrimbly Jul 18, 2026
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
97 changes: 97 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
name: CI

on:
push:
branches: [master, develop]
pull_request:

permissions:
contents: read

jobs:
test:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
python-version: ["3.10", "3.11", "3.12", "3.13"]
runs-on: ${{ matrix.os }}

steps:
- uses: actions/checkout@v4

- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
cache: pip

- name: Install FFmpeg (Ubuntu)
if: runner.os == 'Linux'
run: sudo apt-get update && sudo apt-get install --yes ffmpeg

- name: Install FFmpeg (macOS)
if: runner.os == 'macOS'
run: brew install ffmpeg

- name: Install FFmpeg (Windows)
if: runner.os == 'Windows'
run: choco install ffmpeg --yes --no-progress

- name: Install package and test dependencies
run: python -m pip install --upgrade pip && python -m pip install -r requirements.txt

- name: Run tests
run: python -m pytest -q

- name: Build wheel
run: python -m pip wheel . --no-deps --wheel-dir dist

minimum-textual:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- uses: actions/setup-python@v5
with:
python-version: "3.10"
cache: pip

- name: Install FFmpeg
run: sudo apt-get update && sudo apt-get install --yes ffmpeg

- name: Install minimum supported Textual
run: >-
python -m pip install --upgrade pip &&
python -m pip install -e . -r tests/requirements.txt "textual==5.0.0" &&
python -m pip check

- name: Run tests at the minimum Textual version
run: python -m pytest -q

package:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- uses: actions/setup-python@v5
with:
python-version: "3.13"
cache: pip

- name: Install build tooling and FFmpeg
run: |
sudo apt-get update && sudo apt-get install --yes ffmpeg
python -m pip install --upgrade pip build

- name: Build distribution artifacts
run: python -m build

- name: Smoke test installed wheel outside the checkout
run: |
python -m venv "$RUNNER_TEMP/wheel-smoke"
"$RUNNER_TEMP/wheel-smoke/bin/python" -m pip install dist/*.whl
cd "$RUNNER_TEMP"
"$RUNNER_TEMP/wheel-smoke/bin/python" -c "import sharp_frames; from sharp_frames.ui.components.raster_preview import RasterImagePreview; print(sharp_frames.__version__)"
"$RUNNER_TEMP/wheel-smoke/bin/sharp-frames" --help
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,10 @@ pip-delete-this-directory.txt
htmlcov/
.tox/

# Editor files
# Editor and local agent files
.vscode/
.idea/
.codex/
*.swp
*.swo

Expand Down
31 changes: 30 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,35 @@ All notable changes to the Sharp Frames project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.4.0] - 2026-07-18

### Added
- Scrollable full-frame selection timeline with click-to-inspect and keyboard navigation
- Inline terminal frame preview via Sixel/Kitty raster graphics with an external-viewer fallback
- Automatic color space detection and conversion to sRGB for HDR and wide-gamut sources (BT.2020, HLG, PQ)
- Resolution-normalized focus scoring for more consistent comparisons across source sizes
- Trend-aware outlier detection with improved defaults
- Responsive selection layout that keeps the preview and chart visible on short terminals
- Shared alpha-preserving image decode/resize/encode helpers
- Cross-platform CI coverage for Python 3.10 through 3.13 on Linux, macOS, and Windows, plus the minimum supported Textual version
- Regression coverage for selection quality, output collisions, cancellation, and runtime cleanup

### Changed
- Selection methods now balance sharpness and temporal distribution more consistently
- Frame extraction, analysis, preview, and saving pipelines provide stricter validation and failure reporting
- Image-directory exports preserve content while correctly handling format conversion and case-insensitive filename collisions
- Configuration and selection screens provide clearer controls, status, and keyboard behavior; the processing screen is vertically centered
- Textual requirement raised to 5.0 or newer; new dependencies `textual-image` and `Pillow`

### Fixed
- FFmpeg and FFprobe process cleanup, cancellation, timeout, and pipe handling
- Partial or failed image reads being treated as valid zero-quality frames
- Selection previews disagreeing with the frames produced during processing
- Output metadata reporting frames that were not successfully saved
- Platform-specific dependency guidance and Windows-safe process startup
- Start Over after saving returning to the middle of setup instead of the first step
- Cancel during setup leaving the application running on an empty screen

## [0.3.1] - 2025-01-29

### Fixed
Expand Down Expand Up @@ -136,4 +165,4 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- outlier-removal: Remove outliers based on comparison with neighbors
- Interactive mode with guided prompts
- Proper Python package structure for pip and pipx installation
- Command-line interface with `sharp-frames` command
- Command-line interface with `sharp-frames` command
35 changes: 21 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,13 @@ Or with pipx for isolated installation:
pipx install sharp-frames
```

**IMPORTANT: Video Processing Requirement**: Install FFmpeg separately for video input support.
**IMPORTANT: Video Processing Requirement**: Install an FFmpeg distribution that includes both `ffmpeg` and `ffprobe`. Both executables must be on `PATH`; image-directory processing does not require them.
- **Windows**: Download from [FFmpeg website](https://ffmpeg.org/download.html) and add to PATH
- **macOS**: `brew install ffmpeg`
- **Linux**: `sudo apt install ffmpeg`

HDR-to-SDR extraction additionally requires an FFmpeg build with the `zscale` filter (`libzimg`). You can verify support with `ffmpeg -filters | grep zscale`. Non-HDR video processing does not require `zscale`.

## Quick Start

### Modern Interface (Default)
Expand All @@ -45,9 +47,9 @@ sharp-frames <input> <output> [options]
```

**Input Types:**
- Video files: `.mp4`, `.avi`, `.mov`, `.mkv`, `.wmv`, `.flv`, `.webm`, `.m4v`, etc.
- Video files: `.mp4`, `.avi`, `.mov`, `.mkv`, `.wmv`, `.flv`, `.webm`, `.m4v`, `.3gp`, `.3g2`, `.ogv`, `.ts`, `.mts`, `.m2ts`, `.mpg`, `.mpeg`, `.vob`
- Video directories: Processes all videos in a folder
- Image directories: `.jpg`, `.jpeg`, `.png`, `.bmp`, `.tiff`, `.webp`, etc.
- Image directories: `.jpg`, `.jpeg`, `.png`, `.bmp`, `.tif`, `.tiff`, `.webp`, `.ppm`, `.pgm`, `.pbm`

## Selection Methods

Expand All @@ -63,10 +65,10 @@ Divides content into batches and selects the sharpest frame from each batch.
--selection-method batched --batch-size 5 --batch-buffer 2
```

### Outlier Removal
### Outlier Detection
Removes unusually blurry frames by comparing each frame to its neighbors.
```bash
--selection-method outlier-removal --outlier-window-size 15 --outlier-sensitivity 50
--selection-method outlier-removal --outlier-window-size 15 --outlier-sensitivity 60
```

## Command Line Options
Expand All @@ -79,11 +81,11 @@ Removes unusually blurry frames by comparing each frame to its neighbors.

### Selection Method Parameters
- `--num-frames <int>`: Number of frames to select (best-n, default: 300)
- `--min-buffer <int>`: Minimum gap between selected frames (best-n, default: 3)
- `--min-buffer <int>`: Minimum number of intervening frames between selected frames (best-n, default: 3)
- `--batch-size <int>`: Frames per batch (batched, default: 5)
- `--batch-buffer <int>`: Frames to skip between batches (batched, default: 2)
- `--outlier-window-size <int>`: Neighbor comparison window (outlier-removal, default: 15)
- `--outlier-sensitivity <int>`: Removal aggressiveness 0-100 (outlier-removal, default: 50)
- `--outlier-window-size <int>`: Local comparison window, minimum 5 (outlier-removal, default: 15)
- `--outlier-sensitivity <int>`: Detection sensitivity 0-100 (outlier-removal, default: 60)

## Examples

Expand Down Expand Up @@ -125,23 +127,28 @@ sharp-frames photos selected --selection-method outlier-removal --outlier-sensit

## Requirements

- Python 3.7 or higher
- Dependencies installed automatically: `opencv-python`, `numpy`, `tqdm`, `textual`
- FFmpeg (for video processing only)
- Python 3.10 or higher
- Dependencies installed automatically: `opencv-python`, `numpy`, `tqdm`,
`textual`, `textual-image`
- FFmpeg and FFprobe (for video processing only)
- FFmpeg `zscale`/`libzimg` support (for HDR-to-SDR processing only)

## How It Works

1. **Validation**: Checks input paths, file formats, and system dependencies
2. **Extraction**: Videos are extracted to frames at specified FPS using FFmpeg
3. **Analysis**: Calculates sharpness scores using Laplacian variance in parallel
4. **Selection**: Applies chosen algorithm to select the best frames/images
3. **Analysis**: Normalizes analysis resolution, lightly denoises each image,
and combines Laplacian variance with Tenengrad focus scoring in parallel.
Unreadable inputs are excluded and reported.
4. **Selection**: Applies the chosen source-aware algorithm to select the best
naturally ordered frames/images.
5. **Output**: Saves selected content with metadata including scores and parameters

## Output

- Selected frames/images with descriptive filenames
- `selected_metadata.json` with processing details, parameters, and sharpness scores
- Preserves original formats for image directory input
- Transcodes selected images to the configured output format (`jpg` by default)
- Automatic output directory creation with permission validation

## Help & Support
Expand Down
60 changes: 21 additions & 39 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
@@ -1,53 +1,35 @@
# Sharp Frames v0.2.0 Release Notes
# Sharp Frames v0.4.0 Release Notes

## New Features
## Highlights

### Textual Interface (Default)
- Replaces command-line prompts with step-by-step wizard
- Real-time input validation and error feedback
- Enhanced error messages with specific guidance
- Explore the complete analyzed frame set in a scrollable timeline — click any bar to preview that frame directly in the terminal.
- iPhone and HDR sources (BT.2020, HLG, PQ) are detected and converted to sRGB automatically.
- Get more reliable selections from resolution-normalized focus scoring, trend-aware outlier detection, and improved temporal distribution.
- Preview and execution now share the same selection rules, so displayed counts and chosen frames agree.

### Video Directory Processing
- Process all videos in a folder with a single command
- Automatically detects and processes supported video formats
- Maintains individual processing settings for each video
## Interface Improvements

### Usage Changes
```bash
# New default behavior
sharp-frames # Launches textual interface

# Existing functionality unchanged
sharp-frames input.mp4 output/ # Direct processing
sharp-frames video_folder/ output/ # Process all videos in folder
sharp-frames --interactive # Legacy prompts
```

## Technical Changes
- Full-frame selection timeline with click-to-inspect, horizontal scrolling, and keyboard navigation (arrows, PgUp/PgDn through selected frames, Ctrl+PgUp/PgDn paging, Home/End).
- Inline frame preview rendered through Sixel or Kitty terminal graphics, with an external-viewer fallback (press `O`).
- Responsive layout keeps the preview and chart visible on short terminals (down to 80×24) while preserving the spacious layout on large ones.
- Start Over returns to the first setup step with a clean configuration, and Cancel exits the application.
- Clearer configuration controls and validation feedback.
- More actionable dependency and processing errors across supported platforms.

### Added
- Path validation with existence/permission checking
- Numeric field validation with range checking
- Thread-safe subprocess management
- UI component testing
- Enhanced error analysis and reporting
## Reliability and Compatibility

### Fixed
- Configuration parameter mapping
- Resource cleanup (temp files, subprocesses)
- Error message specificity
- HDR and wide-gamut color spaces are converted to sRGB/BT.709 through FFmpeg `zscale`, with a clear error for unsupported BT.2020 constant-luminance sources.
- Hardened FFmpeg and FFprobe cancellation, timeouts, output draining, and temporary-directory cleanup.
- Unreadable images are excluded and reported instead of silently receiving valid-looking scores.
- Image exports preserve alpha and handle format conversion, partial failures, metadata accuracy, and case-insensitive filename collisions.
- CI covers Linux, macOS, and Windows on Python 3.10, 3.11, 3.12, and 3.13, plus the minimum supported Textual version.

## Breaking Changes
- `sharp-frames` without arguments now launches UI instead of showing help
- Use `sharp-frames --help` for command-line help
## Upgrade

## Installation
```bash
pip install --upgrade sharp-frames
# or
pipx upgrade sharp-frames
```

## Dependencies
- Added: `textual>=0.41.0`
- All existing dependencies unchanged
New dependencies (`textual-image`, `Pillow`) install automatically. FFmpeg and FFprobe remain required for video processing and must be available on `PATH`; HDR-to-SDR conversion additionally requires FFmpeg built with `zscale`/`libzimg` support.
Loading
Loading