Skip to content

Commit 3d840bd

Browse files
authored
Merge branch 'darktable-org:master' into skip-dupes
2 parents f1c4a43 + 3b88968 commit 3d840bd

6 files changed

Lines changed: 485 additions & 305 deletions

File tree

dev-doc/AI.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -485,15 +485,21 @@ dt_your_task_free(ctx);
485485
| Task | Key | API | Consumer |
486486
|------|-----|-----|----------|
487487
| Object Mask | `"mask"` | `src/common/ai/segmentation.h` | `src/develop/masks/object.c` |
488+
| Raw Denoise (Bayer) | `"rawdenoise"` | `src/common/ai/restore_raw_bayer.h` | `src/libs/neural_restore.c` |
489+
| Raw Denoise (Linear) | `"rawdenoise"` | `src/common/ai/restore_raw_linear.h` | `src/libs/neural_restore.c` |
488490
| Denoise | `"denoise"` | `src/common/ai/restore_rgb.h` | `src/libs/neural_restore.c` |
489491
| Upscale | `"upscale"` | `src/common/ai/restore_rgb.h` | `src/libs/neural_restore.c` |
490-
| Raw Denoise (Bayer) | `"rawdenoise"` | `src/common/ai/restore_raw_bayer.h` | `src/libs/neural_restore.c` |
491-
| Raw Denoise (Linear) | `"rawdenoise"` | `src/common/ai/restore_raw_linear.h` | `src/libs/neural_restore.c` |
492492
493493
For model requirements, I/O specifications, tiling strategies, color
494494
space conventions, ONNX export instructions, and config.json examples
495495
for each task, see **[AI_Tasks.md](AI_Tasks.md)**.
496496
497+
The neural-restore consumer (denoise / upscale / rawdenoise) writes
498+
its output to a sibling file (TIFF or DNG), then auto-imports it via
499+
`_import_image` in `neural_restore.c`. The new image is grouped with
500+
its source and inherits the source's user-applied tags so the output
501+
shows up in tag-based collections that contained the source.
502+
497503
---
498504
499505
## Adding a New Execution Provider

dev-doc/AI_Tasks.md

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

131246
Removes noise from developed images using neural network inference.
@@ -145,6 +260,10 @@ Removes noise from developed images using neural network inference.
145260
4. optionally, DWT-based detail recovery blends fine texture from the
146261
original back into the denoised result
147262
5. 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

Comments
 (0)