@@ -126,6 +126,121 @@ repository. Requirements for the decoder export:
126126
127127---
128128
129+ ## Raw Denoise
130+
131+ Sensor-level denoise on the raw CFA mosaic, before darktable's pipeline.
132+ Output is a DNG that re-imports as a normal raw and is graded with the
133+ full pipeline as usual. Two pipeline variants share the task key:
134+
135+ | Variant | API | Sensors | Output |
136+ | ---------| -----| ---------| --------|
137+ | Bayer | ` src/common/ai/restore_raw_bayer.h ` | RGGB / BGGR / GRBG / GBRG (force-cropped to RGGB origin) | CFA Bayer DNG (uint16, same CFA pattern) |
138+ | Linear | ` src/common/ai/restore_raw_linear.h ` | X-Trans, Foveon, monochrome-CFA, anything that can't be packed as 4ch Bayer | LinearRaw DNG (3ch float demosaicked) |
139+
140+ ** Task key** : ` "rawdenoise" `
141+ ** Shared API** : ` src/common/ai/restore.h ` (loaders: ` dt_restore_load_rawdenoise_bayer ` , ` dt_restore_load_rawdenoise_xtrans ` , ` dt_restore_load_rawdenoise_linear ` )
142+ ** Consumer** : ` src/libs/neural_restore.c `
143+
144+ The X-Trans loader currently falls through to the linear variant; it
145+ exists so a future dedicated X-Trans model can be picked up via a
146+ manifest-only change.
147+
148+ ### How It Works
149+
150+ ** Bayer variant** (RGGB family):
151+
152+ 1 . raw CFA mosaic is loaded straight from rawspeed (no demosaic, no
153+ darktable pre-processing other than rawprepare)
154+ 2 . preprocessing: per-channel black-level subtract, per-channel WB
155+ normalisation (default: daylight derived from ` adobe_XYZ_to_CAM ` ),
156+ normalise by per-site range
157+ 3 . pack the 2T × 2T CFA tile into a 4-channel T × T tensor (R, G1, G2, B
158+ in RGGB order; non-RGGB sensors are force-cropped to an RGGB origin)
159+ 4 . tiled inference; the model demosaics internally via PixelShuffle and
160+ returns a 3-channel 2T × 2T tile in camRGB
161+ 5 . postprocessing: invert the normalisation and WB, optional scalar
162+ mean-match (` match_gain ` ) to keep tile gain consistent
163+ 6 . re-mosaic back to the same CFA layout, write uint16 mosaic to a
164+ CFA Bayer DNG via ` dt_imageio_dng_write_cfa_bayer `
165+
166+ ** Linear variant** (X-Trans, Foveon, etc.):
167+
168+ 1 . run a minimal darktable pipeline (`rawprepare → highlights →
169+ demosaic`) with temperature disabled, producing a 3ch float buffer
170+ at full sensor resolution in camRGB raw-ADC units. this reuses
171+ darktable's sensor-aware demosaic (AMaZE / VNG / Markesteijn / …)
172+ instead of rolling our own
173+ 2 . apply daylight WB and the ` camRGB → lin_rec2020 ` matrix
174+ 3 . optional scalar exposure boost to ` target_mean ` (default 0.30 for
175+ the training distribution)
176+ 4 . tiled inference with per-tile ` match_gain `
177+ 5 . invert the exposure boost, matrix, and WB to recover a camRGB raw
178+ 6 . write a LinearRaw DNG via ` dt_imageio_dng_write_linear `
179+
180+ Both variants then auto-import into the library, group with the source
181+ image, and inherit user tags (same ` _import_image ` path as denoise /
182+ upscale).
183+
184+ ### Model Requirements
185+
186+ ** Bayer (` input_kind: bayer_v1 ` ):**
187+
188+ | Tensor | Name | Shape | Type | Description |
189+ | --------| ------| -------| ------| -------------|
190+ | Input 0 | ` input ` | ` [1, 4, T, T] ` | float32 | packed CFA half-resolution tile, channel order R G1 G2 B, values ` (raw - black) / range * wb_norm ` |
191+ | Output 0 | ` output ` | ` [1, 3, 2T, 2T] ` | float32 | demosaicked camRGB in same WB/exposure frame |
192+
193+ ** Linear (` input_kind: linear_v1 ` ):**
194+
195+ | Tensor | Name | Shape | Type | Description |
196+ | --------| ------| -------| ------| -------------|
197+ | Input 0 | ` input ` | ` [1, 3, T, T] ` | float32 | planar 3-channel tile, default colorspace ` lin_rec2020 ` |
198+ | Output 0 | ` output ` | ` [1, 3, T, T] ` | float32 | denoised tile in same input space |
199+
200+ A declared-but-mismatched ` input_kind ` is a hard load error (no silent
201+ fallback). Manifests predating the contract label are treated as
202+ ` bayer_v1 ` for back-compat.
203+
204+ ### Tiling
205+
206+ - tile sizes (tried in order, half-resolution for bayer): 512, 384, 256, 192
207+ - overlap: 16 packed pixels (= 32 sensor pixels) on each edge
208+ - corner tiles in the Bayer path are mirror-padded * inside* the
209+ effective-RGGB-cropped rectangle (`variants.bayer.edge_pad:
210+ mirror_cropped` — matches RawNIND training)
211+
212+ ### config.json Example
213+
214+ ``` json
215+ {
216+ "id" : " rawdenoise-nind" ,
217+ "name" : " raw denoise NIND" ,
218+ "description" : " RawNIND raw-domain denoise (Bayer + Linear)" ,
219+ "task" : " rawdenoise" ,
220+ "github_asset" : " rawdenoise-nind.dtmodel" ,
221+ "default" : true ,
222+ "variants" : {
223+ "bayer" : { "input_kind" : " bayer_v1" , "onnx" : " model_bayer.onnx" ,
224+ "bayer_orientation" : " force_rggb" , "wb_norm" : " daylight" ,
225+ "edge_pad" : " mirror_cropped" },
226+ "linear" : { "input_kind" : " linear_v1" , "onnx" : " model_linear.onnx" ,
227+ "input_colorspace" : " lin_rec2020" , "wb_norm" : " as_shot" ,
228+ "target_mean" : 0.30 }
229+ }
230+ }
231+ ```
232+
233+ ### Directory Layout
234+
235+ ```
236+ rawdenoise-nind/
237+ config.json
238+ model_bayer.onnx
239+ model_linear.onnx
240+ ```
241+
242+ ---
243+
129244## Denoise
130245
131246Removes noise from developed images using neural network inference.
@@ -145,6 +260,10 @@ Removes noise from developed images using neural network inference.
1452604 . optionally, DWT-based detail recovery blends fine texture from the
146261 original back into the denoised result
1472625 . the result is written as TIFF with embedded ICC profile and EXIF
263+ 6 . the TIFF is auto-imported into the library, grouped with the source
264+ image, and inherits the source's user-applied tags (internal
265+ ` darktable|* ` auto-tags are skipped) so the output stays visible in
266+ tag-based collections
148267
149268### Model Requirements
150269
0 commit comments