|
FZGPUModules 2.0
GPU-accelerated modular compression pipelines
|
OutlierCorrectStage<Reconstructor> — transform-agnostic sparse outlier correction that turns a coefficient-domain quantization bound into an actually GUARANTEED reconstructed-domain pointwise error bound, for any reversible transform. More...
#include "stage/stage.h"#include "fzm_format.h"#include "backend/types.h"#include <array>#include <cstdint>#include <cstring>#include <stdexcept>#include <string>#include <unordered_map>#include <vector>Go to the source code of this file.
Classes | |
| struct | fz::OutlierCorrectConfig |
| Serialized OutlierCorrectStage config. 12 bytes. More... | |
Namespaces | |
| namespace | fz |
OutlierCorrectStage<Reconstructor> — transform-agnostic sparse outlier correction that turns a coefficient-domain quantization bound into an actually GUARANTEED reconstructed-domain pointwise error bound, for any reversible transform.
Transform -> QuantizerStage(linear/ABS) -> Coder quantizes transform COEFFICIENTS directly. For transforms whose synthesis gain varies (CDF 9/7 is the motivating case: gain differs by decomposition level), a uniform coefficient-domain threshold does NOT translate to a uniform bound on the RECONSTRUCTED FIELD's pointwise error – measured misses up to 2.7x the requested bound on real CDF 9/7 data (see memory/speck_gpu_design.md sec.9). A candidate fix – scale each coefficient's quantization step by its level's synthesis-filter gain – was tried and REJECTED for CDF 9/7: it makes the max error WORSE, because many coefficients across levels jointly influence any given pixel, so bounding each one's isolated worst case does not bound their sum. That reasoning is transform-shape-dependent in general, so this stage does not assume a scaling fix exists for whatever transform it's paired with either.
The fix that actually works, matching what native SPERR's own Outlier_Coder does for CDF 9/7 (and generalizes to any reversible transform): quantize normally; separately compute what the reconstruction WOULD be (dequantize + inverse-transform a copy); every pixel whose error exceeds the bound gets an EXACT correction value in a sparse (index, value) list, applied as the final step of decompress. This gives a mathematically exact guarantee, not a calibrated approximation.
Everything in this class – diffing, sparse pack/apply, config, serialization, port shape – is transform-agnostic. The ONLY transform-specific step is "given dequantized coefficients, produce the
trial reconstruction" – that's Reconstructor::applyInverseTransform(). A Reconstructor must provide:
Cdf97Reconstructor (modules/coders/cdf97_outlier_correct/) is the one instantiation that ships today; adding another reversible transform's bound-guarantee pipeline is writing one small policy struct like it, not a new stage. This is the split found while pushing back on TeeStage + Cdf97OutlierCorrectStage as too SPERR-specific to justify as Pipeline primitives (see memory/speck_gpu_design.md sec.9's design-flaw discussion) – reusability was the actual bar, not a naming change.
This stage needs BOTH the original raw field (to compute corrections against, at compress time) AND the dequantized codes (to reconstruct a trial value from, in both directions). The raw field is bound directly to input port 0 via Pipeline::bindExternalInput() – no duplicate-copy node needed; see Pipeline::bindExternalInput()'s doc comment for why a dedicated fan-out stage (TeeStage, now removed) turned out to be unnecessary scaffolding once buildInverseDAG()'s actual mirroring rules were traced precisely. Forward inputs: [raw_field, codes]. Forward outputs: [correction_stream (archived leaf), codes_passthrough -> coder]. Per the DAG's bijective inverse contract (inverse output k reconstructs forward input k), inverse outputs are: [corrected field, codes passthrough]; inverse inputs are: [archived correction stream, coder's decoded codes].
The inverse-transform that recovers the trial/candidate reconstruction runs identically in both directions (compress-time to detect outliers, decompress-time to reconstruct before applying corrections) – it is NOT a forward/inverse pair in the Stage sense, which is why it's called directly here via the Reconstructor policy rather than delegated to another DAG-wired stage's own inverse: buildInverseDAG()'s mirroring assumes a stage's inverse output reconstructs its OWN forward input, and "run the same forward-direction computation again in both pipeline
phases" doesn't fit that shape for any stage, transform-specific or not.
float coefficients only. ABS-mode linear quantization only (the only mode any current pipeline pairs this with); error_bound here MUST equal the paired QuantizerStage's own error_bound – set both from the same value when building the pipeline.
This header declares the class only – no CUDA device code, safe to include from a plain .cpp translation unit (fzgpumodules.h does). Member definitions (execute(), the pimpl Impl) live in outlier_correct_stage_impl.cuh, included only from .cu files that explicit-instantiate a concrete Reconstructor (see cdf97_outlier_correct_stage.cu).