FZGPUModules 2.0
GPU-accelerated modular compression pipelines
Loading...
Searching...
No Matches
roibin_split_stage.h File Reference

ROIBinSplitStage — split a detector field into a full-resolution region-of-interest stream and a (optionally binned) background stream. 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::RoiPeak
 One Bragg-peak record, matching the on-disk .roi layout exactly (8 bytes). More...
 
class  fz::ROIBinSplitStage< TData >
 

Namespaces

namespace  fz
 

Detailed Description

ROIBinSplitStage — split a detector field into a full-resolution region-of-interest stream and a (optionally binned) background stream.

Why this stage exists

Serial-crystallography detector frames are almost all background: the science lives in a few hundred Bragg peaks covering well under 1 % of the pixels. A single-bound compressor has to protect those peaks by applying the tight bound to the whole frame, and pays for the other 99 % at that same bound. ROIBIN-SZ (Underwood et al.) attacks this by splitting the frame into peak regions, kept at full resolution, and a background that is spatially binned; the two parts are then compressed separately.

FZGM's DAG can express that split directly: this stage is 1 → 3 forward and 3 → 1 inverse, so the two data streams become two independent branches that can each carry their own error bound and their own coder chain, converging again only at the archive (or at a MergeStage). No monolithic GPU compressor applies two error bounds in one pass — expressing it is the point.

┌── roi ──> Quantizer(eb_tight) ──> ... ──> coder ──┐
input ──> split ── bg ──> Quantizer(eb_loose) ──> ... ──> coder ─┤──> archive
└── peaks ────────────────────────────────────────────┘

Where the ROI comes from

The peak list is not derived from the data. It is the output of the experiment's own peak finder, which in a real light-source pipeline has already run upstream (this is also how ROIBIN-SZ obtains it). At compress time it is read from a .roi file via setPeaksFile(). It is then emitted on the peaks port so it is stored inside the archive and counted in the compressed size — the decompressor needs it and must not have to be handed it out of band. At 8 bytes/peak this is ~0.01 % of a frame.

Geometry and why there is no stream compaction

Each peak owns a fixed (2*hw+1)^2 box, and roi is simply those boxes concatenated in peak order. Boxes belonging to nearby peaks may overlap, and overlapping pixels are therefore stored more than once. That redundancy is deliberate: it makes the output size exactly npeaks * box * sizeof(T), known before the first kernel launch, so estimateOutputSizes() is exact and PREALLOCATE needs no slack. The alternative — a mask plus a device-wide exclusive scan — would need a 4-byte offset per pixel (1.2 GB for the 130-frame volume) to save a redundancy that measures well under 1 % of the ROI stream.

Duplicate pixels are safe on the inverse path because scatter is idempotent: every copy of a source pixel takes the same value, goes through the same quantizer, and therefore reconstructs to the same number, so the order in which the copies are written back does not matter.

Boxes are clamped at frame edges rather than truncated, which keeps the box size fixed. A clamped box reads the same border pixel several times and writes it back several times — again idempotent.

Binning, and what it does and does not bound

bin_factor = b replaces each b x b background block with its mean, so the background branch carries ceil(nx/b) * ceil(ny/b) * nz values. Binning is a resolution reduction, not an error bound. With b > 1 the background reconstruction error is the binning error plus the quantization error, and it is not bounded by the background branch's error bound. Only b = 1 gives a background that genuinely satisfies its stated bound pixel-wise.

Both are supported on purpose, and they answer different questions:

  • b = 1 — a true dual-error-bound pipeline; per-region bound verification is meaningful and must pass on both regions.
  • b > 1 — the ROIBIN configuration; higher ratio, but background fidelity may only be reported as a distortion metric (PSNR), never as a satisfied error bound. The ROI branch satisfies its bound in both cases; that is the invariant the science depends on.

Ports

Forward (1 → 3):

  • input 0: the field, float or double, nx*ny*nz elements
  • output roi : npeaks * (2*hw+1)^2 elements, same type
  • output bg : ceil(nx/b) * ceil(ny/b) * nz elements, same type
  • output peaks : npeaks * 8 bytes, the peak record table (UINT8) Inverse (3 → 1): the three above, in that order, back to the field.

Serialized config header

uint32 nx, ny, nz, npeaks; uint16 hw, bin; uint8 dtype, reserved