| --- |
| license: mit |
| library_name: onnx |
| tags: |
| - image-denoising |
| - onnx |
| - drunet |
| - raw-photography |
| --- |
| |
| # DRUNet (colour) β ONNX export |
|
|
| An ONNX export of **DRUNet**, the plug-and-play denoiser from Kai Zhang's |
| [KAIR](https://github.com/cszn/KAIR) toolbox. Nothing here is retrained or modified: this is the |
| original `drunet_color.pth` weights, exported to ONNX so they can be run from ONNX Runtime. |
|
|
| Published for use in [PxlMonk](https://pxlmonk.com), a desktop raw photo editor, whose build fetches |
| this file at build time rather than committing a 130 MB binary. |
|
|
| ## Provenance |
|
|
| | | | |
| |---|---| |
| | Architecture | `UNetRes` from [cszn/KAIR](https://github.com/cszn/KAIR) β MIT | |
| | Weights | `drunet_color.pth`, from the KAIR v1.0 release β unmodified | |
| | Export | `torch.onnx.export`, opset 17, dynamic batch/height/width | |
| | Paper | Zhang et al., *Plug-and-Play Image Restoration with Deep Denoiser Prior*, TPAMI 2021 | |
|
|
| The weights were trained by the original author and are redistributed unchanged, in a different file |
| format. No training data is redistributed here. |
|
|
| ### A note on the training corpora |
|
|
| DRUNet was trained on `train400`, **DIV2K**, **Flickr2K** and the Waterloo Exploration Database. Some of |
| those carry their own terms β DIV2K states: |
|
|
| > This dataset is made available for academic research purpose only. All the images are collected from |
| > the Internet, and the copyright belongs to the original owners. |
|
|
| Whether such dataset terms reach the resulting model weights is unsettled, and this repository takes no |
| position on it. What it does do is state the provenance plainly, so anyone using these weights can make |
| that call with the facts in front of them rather than discovering them later. The MIT tag above reflects |
| the licence under which the original author published the weights. |
|
|
| ## Interface |
|
|
| Input `input`: `float32[batch, 4, height, width]`, NCHW. |
|
|
| - Channels 0β2: RGB, **sRGB display-referred**, range 0β¦1. The model was trained on display images β |
| feeding scene-linear data gives poor results. |
| - Channel 3: the **noise level map**, a constant `sigma / 255` across the plane. This is DRUNet's |
| strength control: sigma is in 0β¦255 units, so `sigma = 20` means a plane filled with `20/255`. |
|
|
| Output `output`: `float32[batch, 3, height, width]`, same convention, denoised. |
|
|
| Height and width must be divisible by 8 (three downsampling stages). Pad or tile accordingly. |
|
|
| ## Choosing sigma |
|
|
| There is no single right value β it is the strength dial. Measured on a 24 MP frame shot at ISO 16000, |
| using mean deviation from the local 3Γ3 mean as a noise figure (lower is cleaner): |
|
|
| | sigma | noise figure | | |
| |---|---|---| |
| | β (untouched) | 7.41 | | |
| | 10 | 1.67 | visible noise remains | |
| | **20** | **0.23** | clean, fine detail intact | |
| | 35 | 0.17 | over-smoothed β eyelashes go waxy | |
|
|
| ## Reproducing this export |
|
|
| The weights are stored **without bias terms**, so the network must be constructed with `bias=False`. |
| Loading with `strict=False` silently leaves 64 randomly-initialised bias tensors in place and produces |
| a model that runs but denoises badly β always load strictly. |
|
|
| ```python |
| from models.network_unet import UNetRes # from cszn/KAIR |
| import torch |
| |
| net = UNetRes(in_nc=4, out_nc=3, nc=[64, 128, 256, 512], nb=4, act_mode="R", |
| downsample_mode="strideconv", upsample_mode="convtranspose", bias=False) |
| net.load_state_dict(torch.load("drunet_color.pth", map_location="cpu", weights_only=True), strict=True) |
| net.eval() |
| |
| torch.onnx.export( |
| net, torch.randn(1, 4, 256, 256), "drunet_color.onnx", |
| input_names=["input"], output_names=["output"], |
| dynamic_axes={"input": {0: "b", 2: "h", 3: "w"}, "output": {0: "b", 2: "h", 3: "w"}}, |
| opset_version=17, |
| ) |
| ``` |
|
|
| ## Execution providers |
|
|
| Measured on Apple Silicon (10 cores), 256Γ256 tile, best of three after a warm-up: |
|
|
| | | | |
| |---|---| |
| | CPU | 560 ms | |
| | CoreML | **117 ms** | |
| | WebGPU | 154 ms | |
|
|
| DRUNet is a plain CNN, so it maps cleanly onto accelerators β unlike transformer-based denoisers, where |
| the graph fragments into hundreds of partitions and the copying between them costs more than the |
| compute saves. |
|
|
| ## Licence |
|
|
| MIT, matching KAIR. Include the original copyright notice when redistributing: |
|
|
| > Copyright (c) 2019 Kai Zhang |
|
|