Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
nn_model.h
Go to the documentation of this file.
1/*
2 This file is part of Ansel,
3 Copyright (C) 2026 Aurélien PIERRE.
4
5 Ansel is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 Ansel is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with Ansel. If not, see <http://www.gnu.org/licenses/>.
17*/
18#ifndef DT_COMMON_NN_MODEL_H
19#define DT_COMMON_NN_MODEL_H
20
21#include <stddef.h>
22
23/*
24 * Loader and CPU executor for the .anselnn neural denoising models trained by
25 * https://github.com/aurelienpierreeng/ansel-denoise.
26 *
27 * Deliberately self-contained: no darktable.h, no pipeline types. The only
28 * dependencies are json-glib (header parsing), libm and OpenMP. The pixelpipe
29 * integration lives in the IOP module; this translation unit owns the model
30 * data and the math, nothing else.
31 *
32 * File format (little-endian):
33 * 8 bytes magic "ANSELDN1"
34 * 4 bytes uint32 JSON header length N
35 * N bytes JSON: {"cfg": {"arch": "unet", "base", "depth",
36 * "in_channels", "out_channels"},
37 * "tensors": [{"name", "shape", "offset", "size"}, ...]}
38 * payload float32 tensor data, offsets relative to payload start
39 *
40 * Fixed topology ("unet"): depth encoder levels of two 3x3 conv + GELU with a
41 * 2x2 stride-2 downsampling conv between levels, a two-conv bottleneck, and a
42 * decoder of nearest x2 upsampling, 1x1 channel-reduction conv, skip
43 * concatenation and two 3x3 conv + GELU per level; a final 3x3 conv predicts
44 * the noise, subtracted from the input mosaic plane (residual output).
45 */
46
48
49/* Load a .anselnn file. Returns NULL on failure and, if err is non-NULL,
50 * writes a human-readable reason into err[0..err_len-1]. */
51/* Pixel-buffer allocator injection. The executors allocate their scratch
52 * (feature maps, skip connections) through these hooks so the buffers come
53 * from the application's pixelpipe cache arena — per the project rule that
54 * pixel buffers never come from bare malloc — WITHOUT this unit importing any
55 * pipeline header. Unset, they fall back to malloc/free (the standalone
56 * fixture test). Set once at module init, before any pipeline runs; not
57 * thread-safe against concurrent forwards. The model WEIGHTS blob stays on
58 * malloc: it is model data with session lifetime, not a pixel buffer. */
59/* long_lived: the block either survives across pipeline stages (a U-Net skip
60 * connection, held from its encoder level to its decoder level) or is a small
61 * helper that coexists with the largest tensors (the decoder's 1x1 output).
62 * A region-based arena places these at the opposite end from the big-tensor
63 * churn — simulated over the full forward, that layout reaches the ledger's
64 * live-set peak exactly, with no fragmentation slack. */
65typedef void *(*dt_nn_alloc_f)(size_t bytes, int long_lived);
66typedef void (*dt_nn_free_f)(void *ptr);
68
69dt_nn_model_t *dt_nn_model_load(const char *path, char *err, size_t err_len);
70
72
73/* Number of input planes the FINE net expects (5 for arch "unet": mosaic,
74 * R, G, B one-hot, sigma; 5 + coarse_out for arch "unet-ms") and output
75 * planes (1: denoised mosaic). */
78
79/* Multi-scale ("unet-ms") accessors. bin: the superpixel factor for the
80 * image's CFA family, 1 for a single-scale model (no coarse stage).
81 * coarse_in/out: the coarse net's plane counts, 0 without a coarse stage. */
82int dt_nn_model_bin(const dt_nn_model_t *model, const int is_xtrans);
85
86/* Low-band anchor scale (sensor px) carried in the model cfg: below it the
87 * module replaces the output's per-channel means with the noisy input's —
88 * the n-averaged measurement is the true diluted estimate there, while a
89 * denoiser's low band accumulates model error. 0 = no anchoring. */
91
92/* Scales of the low-band fusion pyramid, in sensor px — fixed by the training
93 * reference (cfa.fuse_low_bands, scales=(16, 32, 64)), not by the model file.
94 * A padded tile must divide by the coarsest one, or the pyramid loses a level
95 * and the fusion no longer matches what the model was trained against; see
96 * dt_nn_model_alignment(). */
97#define DT_NN_FUSION_FINEST 16
98#define DT_NN_FUSION_COARSEST 64
99
100/* Width and height passed to dt_nn_unet_apply must be multiples of this:
101 * 2^depth for arch "unet"; for "unet-ms" also every bin << coarse_depth so
102 * the binned planes stay aligned for either CFA. The caller pads (reflect)
103 * and crops. */
105
106/* Peak executor scratch as a DIMENSIONLESS factor of the input image size:
107 * floats of scratch per input pixel, i.e. multiples of a 1-float-per-pixel
108 * buffer. This is the form a tiling factor wants — never feed it absolute
109 * bytes. Includes the coarse net's share for a multi-scale model. */
111
112/* Same, for the OpenCL executor, whose sequence differs (it materializes the
113 * decoder concat and an upsample staging buffer where the CPU reads both in
114 * place): feed this one to factor_cl, the CPU one to factor. */
116
117/* Largest single host-side scratch tensor per input pixel (the contiguity
118 * requirement an arena allocation must satisfy, as opposed to the total). */
120
121/* Peak scratch in absolute bytes for a w x h input (diagnostics, messages). */
123
124/* Run the network. in: in_channels planar w*h float32 planes; out: one w*h
125 * plane (may not alias in). Returns 0 on success, non-zero on allocation
126 * failure or misaligned dimensions. Thread-safe for concurrent calls on the
127 * same model (weights are read-only; scratch is per-call). */
128int dt_nn_unet_apply(const dt_nn_model_t *model, const float *in, float *out, int width, int height);
129
130/* Run one stage of a multi-scale model. stage 0 = fine (identical to
131 * dt_nn_unet_apply); stage 1 = coarse: in is the 6-plane binned input
132 * [R, G, B, sigmaR, sigmaG, sigmaB] at (width, height) COARSE resolution,
133 * out receives the denoised coarse RGB (residual applied). */
134/* apply_residual: non-zero subtracts the head prediction from the matching
135 * input planes (what the torch reference does inside its forward); zero writes
136 * the RAW head output — which is what dt_nn_unet_apply_stage_cl always does,
137 * and what a caller wants when it applies the residual itself. */
138int dt_nn_unet_apply_stage(const dt_nn_model_t *model, int stage, const float *in, float *out, int width,
139 int height, int apply_residual);
140
141/* Superpixel-bin the assembled fine planes [mosaic, R, G, B one-hot, ...]:
142 * out_rgb (3 planes at pw/bin x ph/bin) receives the count-weighted mean of
143 * each block's same-channel sensels, out_cnt the per-channel sensel counts
144 * (for the analytic coarse sigma). The exact contract of the training
145 * repo's cfa.bin_mosaic_torch. */
146void dt_nn_bin_planes(const float *planes, int pw, int ph, int bin, float *out_rgb, float *out_cnt);
147
148/* Nearest-neighbour upsample of ch planar w*h planes by an integer factor
149 * (the coarse guide injection). */
150void dt_nn_upsample_nearest(const float *in, int ch, int w, int h, int factor, float *out);
151
152#ifdef HAVE_OPENCL
153#include "common/opencl.h"
154
155/* OpenCL kernel handles for the U-Net, created once per session from the
156 * rawdenoiseai.cl program. Opaque; owned by dt_nn_cl_create/destroy. */
157typedef struct dt_nn_cl_t dt_nn_cl_t;
158
159dt_nn_cl_t *dt_nn_cl_create(int program);
161
162/* GPU forward of one stage. Writes the RAW head output — stage 0 the predicted
163 * noise, stage 1 the coarse head — never the residual: subtracting it is the
164 * caller's business, on both devices, because "the net predicts what to
165 * remove" is the consumer's convention and not this runtime's. The CPU twin
166 * dt_nn_unet_apply_stage() takes apply_residual = 0 for the same contract. */
167int dt_nn_unet_apply_stage_cl(const dt_nn_model_t *model, int stage, dt_nn_cl_t *cl, int devid, cl_mem dev_in,
168 cl_mem dev_out, int width, int height);
169
170#endif
171#endif // DT_COMMON_NN_MODEL_H
typedef void((*dt_cache_allocate_t)(void *userdata, dt_cache_entry_t *entry))
const dt_colormatrix_t dt_aligned_pixel_t out
const char * model
float *const restrict const size_t const size_t ch
uint32_t width
Definition mipmap_cache.c:0
uint32_t height
Definition mipmap_cache.c:1
void dt_nn_cl_destroy(dt_nn_cl_t *cl)
Definition nn_model.c:1098
void *(* dt_nn_alloc_f)(size_t bytes, int long_lived)
Definition nn_model.h:65
int dt_nn_model_in_channels(const dt_nn_model_t *model)
Definition nn_model.c:416
float dt_nn_unet_scratch_maxblock_per_px(const dt_nn_model_t *model)
Definition nn_model.c:717
int dt_nn_model_out_channels(const dt_nn_model_t *model)
Definition nn_model.c:421
int dt_nn_model_bin(const dt_nn_model_t *model, const int is_xtrans)
Definition nn_model.c:426
void dt_nn_model_free(dt_nn_model_t *model)
Definition nn_model.c:405
int dt_nn_model_alignment(const dt_nn_model_t *model)
Definition nn_model.c:460
float dt_nn_unet_scratch_per_px(const dt_nn_model_t *model)
Definition nn_model.c:707
size_t dt_nn_unet_scratch_bytes(const dt_nn_model_t *model, int width, int height)
Definition nn_model.c:736
int dt_nn_model_anchor(const dt_nn_model_t *model)
Definition nn_model.c:442
void(* dt_nn_free_f)(void *ptr)
Definition nn_model.h:66
dt_nn_cl_t * dt_nn_cl_create(int program)
Definition nn_model.c:1088
int dt_nn_model_coarse_in_channels(const dt_nn_model_t *model)
Definition nn_model.c:432
int dt_nn_unet_apply_stage(const dt_nn_model_t *model, int stage, const float *in, float *out, int width, int height, int apply_residual)
Definition nn_model.c:1009
void dt_nn_bin_planes(const float *planes, int pw, int ph, int bin, float *out_rgb, float *out_cnt)
Definition nn_model.c:1022
float dt_nn_unet_scratch_per_px_cl(const dt_nn_model_t *model)
Definition nn_model.c:712
dt_nn_model_t * dt_nn_model_load(const char *path, char *err, size_t err_len)
Definition nn_model.c:237
int dt_nn_model_coarse_out_channels(const dt_nn_model_t *model)
Definition nn_model.c:437
int dt_nn_unet_apply_stage_cl(const dt_nn_model_t *model, int stage, dt_nn_cl_t *cl, int devid, cl_mem dev_in, cl_mem dev_out, int width, int height)
Definition nn_model.c:1338
int dt_nn_unet_apply(const dt_nn_model_t *model, const float *in, float *out, int width, int height)
Definition nn_model.c:1004
void dt_nn_upsample_nearest(const float *in, int ch, int w, int h, int factor, float *out)
Definition nn_model.c:1058
void dt_nn_set_allocator(dt_nn_alloc_f alloc_fn, dt_nn_free_f free_fn)
Definition nn_model.c:77
const float factor
Definition pdf.h:91