GPR v2.0: 41% smaller files, embedded-ready for Mission 1's 50MP sensor - #58
GPR v2.0: 41% smaller files, embedded-ready for Mission 1's 50MP sensor#58dcliftreaves wants to merge 35 commits into
Conversation
fixed 3 potential memory leaks when function PrepareCodebooks
Fixed file handle leaks in read_from_file() and write_to_file()
Mac build and run instructions
Removed an incorrect cast.
Add Linux instructions ('make' vs 'make .')
…ll' after 'make' so 'gpr_tools' executable is available system-wide.
…ke install' after 'make' so 'gpr_tools' executable is available system-wide." This reverts commit 7a3a1cb.
Add Hero5 Black GPR Sample Raw Photo
clean unused var and made for loop more readable
* make __attribute__((fallthrough)) more portable for GCC (>= 7) and CLANG (>= 10)
- PIXEL_FORMAT_RAW_RGGB_16 and GBRG_16 formats
- Updated wavelet, companding, and log curve for 16-bit range
- Prescale values {2,3,3} for 16-bit (vs {0,2,2} for 14-bit)
- Component clamping in decoder for Q6-Q8 overflow prevention
- PutBuffer overflow handling (assert → error return)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Noise model (noise_model.c/h): - Poisson-Gaussian noise estimation from raw pixels - noise_remove: quantize to noise floor (encoder, LUT-accelerated) - noise_restore: PRNG triangular noise reconstruction (decoder) - FPN polynomial model with row/column offsets and PRNU Wavelet denoise (denoise.c): - Phase 0.5: pre-transform signal-dependent MAD estimation - BayesShrink adaptive per-band thresholding - NoiseAwareRequantize: round coefficients to noise step size - Prescale-aware wavelet noise gain computation Tools: noise_analysis, calibrate, fpn_extract Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
ANS coder (ans.c, ans_joint.c): - Joint RLV: single symbol per coefficient (160 joint symbols) - 10 run classes × 16 magnitude classes with residual bits - 4-way interleaved rANS for reduced pipeline stalls - Packed decode table (sym+freq+cum_freq in one lookup) - Fast bitbuf_read: word-aligned reads instead of bit-by-bit - Per-band frequency tables for adaptive compression Modes: 3 (companded 14-bit), 4 (raw 16-bit) Backward compatible decoder for modes 1/2. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Encoder: - Phase 0.5: pre-transform noise estimation + adaptive quantization - Phase 1: parallel wavelet transform (4 threads) - Phase 1.8: parallel ANS pre-encoding (4 threads) - Phase 2: serial bitstream with per-band VLC/ANS auto-selection - Negative quant sentinel for 16-bit skip-uncompand path Decoder: - ANS mode dispatch (modes 1-4) with jans_decode_band_x4 - Negative quant → skip uncompanding in dequantization - Component clamping for Q6-Q8 overflow prevention - NEON-accelerated dequantization paths - Production hardening: assert(0) → proper error returns Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
SDK (gpr.cpp): - noise_remove/noise_restore pipeline with DNG unit conversion - Auto-triggers noise_restore on decode when noise seed present - 16-bit pixel format support in all conversion functions CLI tools: - gpr_tools: -A (ANS), -D (denoise), -R (noise replace), -F (FPN) flags - gpr_batch.sh: production batch encoder with parallel jobs - compare_quality: PSNR, SSIM, noise preservation, per-region analysis - ans_test: ANS round-trip unit test - fuzz_ans: libFuzzer target for ANS decode Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
CI (.github/workflows/ci.yml): - Ubuntu + macOS builds with smoke tests - ANS unit test and compare_quality build verification - VLC, ANS, and ANS+DN round-trip tests Test data (data/test_sets/): - 3-tier structure: smoke, medium, corner_cases - High/low ISO, high/low entropy test categories - Test suite script (data/tests/test_suite.sh) Calibration tools (tools/): - Phocus capture sequences for automated dark/flat frames - GoPro USB calibration script - Interactive calibration guide Build: ans_test and compare_quality added to CMake Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Review fixes: - Add GoPro dual license headers to 4 new files - Add -lm linkage for Linux builds (vc5_common, vc5_encoder) - Fix backward compat: route existing GPR→RAW through original decode path (not _ex which applies noise_restore to files with NoiseProfile) - Remove hardcoded personal paths from test scripts and docs - Fix batch_encode.sh: add nproc fallback for Linux - NEON auto-detect on any ARM64 (not just Apple) - Remove CI workflow (separate branch — not for upstream PR) Documentation: - docs/gotchas.md: 11 integration notes (LSB rounding, scope, embedded memory, division cost, malloc count, stack, FP, thread safety) - docs/compression-results.html: interactive charts with bar graphs, histogram of 1,231 Z8 compression ratios, speed tables Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Embedded mode (-E / --Embedded CLI flag): - Wavelet transform runs serially (no pthreads) - Phase 1.8 parallel ANS pre-encode skipped entirely - Bands encode inline in Phase 2, one at a time - Peak memory: 113 MB (vs 451 MB with 4 threads) - Output is byte-identical to normal mode Arena allocator: - Single malloc per band instead of 6 separate allocations - Bump-allocates tokens, residual, and rANS buffers from one block - 36 heap operations per image (was 216) Reciprocal frequency table (rcp_freq[]): - Precomputed for future division-free encode - Currently unused (32-bit approximation not exact for full state range) Also: - Remove Jetraw brand references (replaced with generic descriptions) - Remove Phocus capture sequences (vendor-specific tooling) - Rename PHOCUS env var to CALIBRATION_CAPTURES in test script Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Mission 1 Performance ProjectionsWith the newly announced Mission 1's 50MP 1-inch sensor (7680×5760) and 60fps burst mode, the ANS encoder directly addresses two hardware constraints: Burst Mode: SD Card Write PressureAt 50MP × 60fps, the camera generates ~3 GB/sec of raw sensor data.
At high ISO, the 31% reduction in sustained write bandwidth directly extends how long the camera can maintain 60fps burst before the SD card buffer fills. On-Device Decode (GP3 Processor)The GP3's 5nm process and estimated Cortex-A78-class cores are a good match for the embedded decode path:
This enables on-device gallery preview without sending files to a phone — a workflow advantage for Mission 1 Pro shooters using the ILS with manual RAW capture. 14-Stop Dynamic RangeThe Mission 1's 14-stop DR claim and manual ISO/shutter control mean photographers will push high ISO in challenging conditions. The noise-aware quantization is specifically designed for this: it estimates per-band noise sigma and raises quantization to the noise floor, removing noise entropy while preserving all signal information. The higher the ISO, the larger the compression advantage. Embedded Mode (
|
333655b to
911d8a6
Compare
5fc1c22 to
5ca9e54
Compare
Cross-Platform Build VerificationTested via Docker on all three platforms. Includes the endian fix from PR #53 (by @mckfarm) so Linux ARM64 builds succeed.
The Linux ARM64 GPR is smaller (455 KB vs 3.1 MB) because NEON is auto-enabled on aarch64 and the existing NEON inverse wavelet paths in LTO has been removed from CMakeLists.txt — it caused link failures on GCC ( Also includes the byte-order detection fix from #53 ( |
4ac1608 to
33187a2
Compare
Remove 15 files that are internal workflow artifacts, not core codec: - docs: calibration inventory, production audit, pipeline redesign, quality analysis, noise model design (5 planning/analysis docs) - tools: calibration guide, gopro-calibrate script, phocus sequences - apps: calibrate, fpn_extract, noise_analysis (3 standalone tools) - scripts: batch_encode, gpr_batch, test_suite, test_noise_compression What remains (20 new files): - Core codec: ANS coder, noise model, denoise, encoder/decoder changes - Validation: ans_test, fuzz_ans, compare_quality - Docs: architecture, format spec, gotchas, future ideas, compression results - Test data: gitignore + README for test set organization Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
33187a2 to
0620933
Compare
Fix: Linux ARM64 Encoder Producing Near-Zero CoefficientsRoot cause found and fixed. The Fix: Result: All three platforms now produce byte-identical output:
This was a pre-existing bug in the GoPro codebase (not introduced by this PR) that was latent because GoPro only tested on platforms where the stack happened to be zeroed. |
LTO Analysis: No Performance Benefit, Removed IntentionallyBenchmarked LTO vs no-LTO on macOS ARM64 (GoPro Hero6 12MP):
Zero measurable difference. The hot path functions ( LTO is disabled because:
Users who want to experiment can pass Also filed #60 — a standalone 1-line fix for the uninitialized |
a6e4638 to
d965047
Compare
d965047 to
1816919
Compare
Pre-existing GoPro issues fixed for MSVC/Windows: - expat_lib: replace __attribute((fallthrough)) with FALL_THROUGH macro (from gopro#51 by @keenanjohnson) - encoder.c, decoder.c, vlc.c: guard pthread.h with #ifndef _WIN32 - encoder.c: force embedded_mode on Windows (single-thread, no pthreads) - decoder.c: single-thread inverse transform on Windows - vlc.c: replace pthread_once with simple init check on Windows - main_c.c: guard strings.h, add _stricmp for MSVC (from #51) - CMakeLists: conditional -lm (not needed on MSVC) and Threads linkage Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Windows Build: Now WorkingFixed three pre-existing issues preventing Windows/MSVC builds:
Cross-Platform Results (all green)
Windows GPR is larger due to MSVC floating-point differences in the log curve computation. The file is valid. Windows runs in single-thread (embedded) mode automatically. CI: https://github.com/dcliftreaves/gpr/actions/runs/25342983810 |
The encoder/decoder log curves were computed at runtime using pow() and log10(). MSVC produces slightly different values for these transcendental functions than GCC/Clang, causing different wavelet coefficients and a 60% larger GPR file on Windows (4.96 MB vs 3.12 MB). Fix: precompute the 12-bit (4096 entries) and 14-bit (16384 entries) tables as static C arrays in logcurve_tables.h, generated from the canonical formulas using Python. The Setup functions now memcpy from these arrays instead of computing at runtime. 16-bit tables (65536 entries = 128KB each) are still computed at runtime since they're too large for a static header and are only used for 16-bit sensors (our new feature). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Update: Static Log Curve Tables + Windows Size ExplainedAdded precomputed 12-bit and 14-bit log curve tables ( Windows File Size AnalysisDetailed comparison with static log curve tables:
The log curve tables are now byte-identical across platforms. The remaining difference is in the existing GoPro encoder's quantization path — MSVC produces slightly different quantized coefficients, causing the per-band auto-selector to choose VLC for more bands. The file is valid and decodes correctly. The auto-selector guarantee holds: Windows output (4.96 MB) is smaller than pure VLC (5.33 MB). |
The precomputed tables ensured identical log curve values across compilers, but the Windows file size difference persists from elsewhere in GoPro's quantization path. Reverting to runtime computation to reduce diff size. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
0eff853 to
127bdd2
Compare
Minimal Codec-Only Changeset
This is a svelte PR containing only the core codec changes, validation tools, and essential documentation. Internal workflow scripts, calibration tools, and planning docs have been stripped to keep the review focused.
What's Included (20 new files)
Core codec (10 files):
ans_joint.c/h— Joint RLV ANS entropy coder with 4-way interleaved decode and arena allocatorans.c/h— Legacy separate-stream ANS (decoder backward compat)noise_model.c/h— Noise remove/restore, FPN model, PRNGdenoise.c/h— BayesShrink, MAD estimation, adaptive quantizationValidation tools (4 files):
ans_test.c— ANS round-trip unit testfuzz_ans.c— libFuzzer target for ANS decodecompare_quality.c— PSNR, SSIM, noise preservation metricsDocumentation (5 files):
architecture.md— Full technical writeup of the pipelineformat-spec-v2.md— Complete v2.0 bitstream specificationgotchas.md— 11 integration notes (LSB rounding, embedded memory, etc.)future-ideas.md— GPU accel, streaming, lossless modecompression-results.html— Interactive charts (open in browser)Test data (1 file):
.gitignorefor test image organizationWhat's NOT Included
Stripped to keep review minimal:
feature/ci-workflowbranch)These are available in the
feature/v2-final-2branch if wanted.Results
Per-band auto-selection: never larger than VLC. Validated on 1,231 images, zero failures.
Compression Distribution (1,231 images)
Embedded ARM Support
Decoder: Ready for On-Camera Use
Estimated: 0.67 seconds to decode a 27 MP frame on Cortex-A78.
Encoder: Embedded Mode (
-Eflag)Arena allocator: 1 malloc per band (was 6). Testable on desktop with
-Eflag.Technical Summary
See architecture.md for the full pipeline walkthrough, gotchas.md for integration notes.
Test Plan
🤖 Generated with Claude Code