Release v3.0: selected sub-4M semantic colorizer, inference and verified ONNX
Browse files- DEPLOYMENT.md +18 -64
- PIPELINE_EVAL.json +1196 -0
- QA.json +55 -0
- README.md +50 -53
- RELEASE_MANIFEST.json +16 -0
- RELEASE_REPORT.md +63 -0
- SHA256SUMS.json +21 -42
- app.py +47 -0
- colorization_project_log.md +5 -0
- colorize_image.py +2 -176
- colorize_onnx.py +15 -23
- colorizer.json +27 -51
- colorizer.onnx +2 -2
- config.json +5 -954
- export_onnx.py +25 -44
- inference.py +101 -53
- legacy/v2/ARCHIVE.md +1 -0
- legacy/v2/DEPLOYMENT.md +80 -0
- legacy/v2/README.md +80 -0
- colorize_eval.py → legacy/v2/colorize_eval.py +0 -0
- legacy/v2/colorize_image.py +176 -0
- legacy/v2/colorize_onnx.py +32 -0
- colorize_train.py → legacy/v2/colorize_train.py +0 -0
- legacy/v2/colorizer.json +54 -0
- eval_grid.png → legacy/v2/eval_grid.png +0 -0
- eval_grid_temp038.png → legacy/v2/eval_grid_temp038.png +0 -0
- eval_grid_temp075.png → legacy/v2/eval_grid_temp075.png +0 -0
- legacy/v2/export_onnx.py +48 -0
- legacy/v2/inference.py +63 -0
- legacy/v2/model.py +122 -0
- legacy/v2/requirements-onnx.txt +4 -0
- legacy/v2/requirements.txt +12 -0
- sample.png → legacy/v2/sample.png +0 -0
- upload_main.py → legacy/v2/upload_main.py +0 -0
- mini-colorizer-v3.zip +3 -0
- model.py +6 -120
- model.safetensors +2 -2
- requirements-onnx.txt +5 -4
- requirements-space.txt +9 -0
- requirements.txt +7 -12
- semantic_model.py +94 -0
DEPLOYMENT.md
CHANGED
|
@@ -1,80 +1,34 @@
|
|
| 1 |
-
#
|
| 2 |
|
| 3 |
-
|
| 4 |
-
improvement. The `.safetensors` file contains the learned colorizer; guided
|
| 5 |
-
decoding is implemented by `spatial.py` and is included in the ONNX graph.
|
| 6 |
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
Run from the extracted release directory:
|
| 10 |
-
|
| 11 |
-
```bash
|
| 12 |
-
python -m pip install -r requirements.txt
|
| 13 |
-
python inference.py --model . --output-dir colorized photo.jpg
|
| 14 |
-
```
|
| 15 |
-
|
| 16 |
-
The default guided radius is 8 at the model's input resolution. Use
|
| 17 |
-
`--guided-radius 0` for raw predictions or `--guided-radius 16` for stronger
|
| 18 |
-
smoothing. Stronger smoothing can remove legitimate small color details.
|
| 19 |
-
`--flip-tta --guided-radius 4` is an optional two-pass mode. The released
|
| 20 |
-
ONNX graph is the single-pass radius-8 mode.
|
| 21 |
|
| 22 |
```python
|
| 23 |
-
from
|
| 24 |
-
|
| 25 |
-
from inference import colorize
|
| 26 |
-
|
| 27 |
-
model = load_model('.')
|
| 28 |
-
result = colorize(model, Image.open('photo.jpg'))
|
| 29 |
-
result.save('colorized.png')
|
| 30 |
```
|
| 31 |
|
| 32 |
-
The
|
| 33 |
-
longest network-input side to 256 pixels, upsamples chroma to the original
|
| 34 |
-
oriented image dimensions and combines it with the original Lab luminance.
|
| 35 |
-
Final RGB conversion can clip colors outside the display gamut. Very large
|
| 36 |
-
inputs still require memory for full-resolution color conversion. There is
|
| 37 |
-
no video temporal-consistency guarantee.
|
| 38 |
|
| 39 |
-
##
|
| 40 |
|
| 41 |
-
```
|
| 42 |
-
python -m pip install -r requirements-onnx.txt
|
| 43 |
-
python colorize_onnx.py --model colorizer.onnx --output-dir colorized photo.jpg
|
| 44 |
-
```
|
| 45 |
|
| 46 |
-
|
| 47 |
-
Output name: `chroma`, float32, shape `N x 2 x H x W`, Lab a and b values.
|
| 48 |
-
Height and width must each be at least 8. Dynamic shapes and batches are
|
| 49 |
-
supported. Prefer a longest input side of 256 to match the evaluated
|
| 50 |
-
operating point. Ordinary RGB values are not valid graph inputs.
|
| 51 |
|
| 52 |
-
The
|
| 53 |
-
with epsilon 0.001. It does not include file loading, EXIF handling, Lab
|
| 54 |
-
conversion, aspect-ratio resizing or final chroma upsampling; these are
|
| 55 |
-
implemented in `colorize_onnx.py`. The ONNX wrapper uses Pillow resizing,
|
| 56 |
-
whereas the PyTorch wrapper uses PyTorch interpolation. Their image-level
|
| 57 |
-
comparison is recorded in the release checks.
|
| 58 |
|
| 59 |
-
##
|
| 60 |
|
| 61 |
-
|
| 62 |
|
| 63 |
-
```
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
```
|
| 67 |
|
| 68 |
-
|
| 69 |
-
It also verifies that `stable` retains its pre-upload revision. The app can
|
| 70 |
-
keep using `stable` until you choose to switch it to the tested release.
|
| 71 |
-
The current chat connection has Jobs/read access but no repository write
|
| 72 |
-
scope; the downloadable release is the publication fallback.
|
| 73 |
|
| 74 |
-
##
|
| 75 |
|
| 76 |
-
|
| 77 |
-
and smoothing cannot infer an object's unknown original color. Review the
|
| 78 |
-
included failure examples on your app's real input photos before describing
|
| 79 |
-
it as generally production-ready. Browser/mobile performance and real-time
|
| 80 |
-
video have not been validated.
|
|
|
|
| 1 |
+
# v3 deployment
|
| 2 |
|
| 3 |
+
## Local Python
|
|
|
|
|
|
|
| 4 |
|
| 5 |
+
Use Python 3.10 or 3.11 with the supplied requirements. CPU inference works without a GPU. Install a CUDA-compatible PyTorch build for GPU execution. Load once, put the model in evaluation mode, and reuse it across requests. The supplied loader does this automatically.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 6 |
|
| 7 |
```python
|
| 8 |
+
from inference import load_colorizer, colorize
|
| 9 |
+
model = load_colorizer('.', 'cpu')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 10 |
```
|
| 11 |
|
| 12 |
+
The runtime loads a single 3,994,676-parameter network. The training-only teacher and discriminator are not required or downloaded.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 13 |
|
| 14 |
+
## Hugging Face Space
|
| 15 |
|
| 16 |
+
Copy `app.py`, `inference.py`, `semantic_model.py`, and `requirements-space.txt` into a Gradio Space. Rename the latter to `requirements.txt`. Use Gradio 6.28.0 in the Space README and choose ZeroGPU hardware.
|
|
|
|
|
|
|
|
|
|
| 17 |
|
| 18 |
+
The released app pins `MODEL_REVISION` to the verified model commit. Setting this environment variable deliberately overrides that pin. `MODEL_ID` can be a local directory for offline use or a Hub repository ID.
|
|
|
|
|
|
|
|
|
|
|
|
|
| 19 |
|
| 20 |
+
Import `spaces` before PyTorch. The model is placed on CUDA once at startup, and only the network stage is inside `@spaces.GPU`. Image preparation and full-resolution rendering happen on CPU. `ZEROGPU_CPU_TEST=1` enables the local CPU path for verification.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 21 |
|
| 22 |
+
## Input handling
|
| 23 |
|
| 24 |
+
The image pipeline applies EXIF orientation, preserves alpha, rejects images over 12 megapixels, and restores colour at the original output dimensions. Network processing preserves aspect ratio with a maximum 256-pixel side. Errors are returned explicitly rather than replaced by a blank image.
|
| 25 |
|
| 26 |
+
The default API `/colorize` takes an image, colour strength (0–1.5), and smoothing (`Gentle`, `Balanced`, or `Strong`). It returns a PNG. Requests are serialized to bound memory usage.
|
| 27 |
+
|
| 28 |
+
## Versioning and rollback
|
|
|
|
| 29 |
|
| 30 |
+
Model and source are committed atomically. The app pins the model commit rather than following moving main. `RELEASE_MANIFEST.json` records selection and previous commits. To roll back, use the recorded prior Space commit and model revision. The user-managed stable branch is not modified.
|
|
|
|
|
|
|
|
|
|
|
|
|
| 31 |
|
| 32 |
+
## Verification scope
|
| 33 |
|
| 34 |
+
`QA.json` records tests for strict reload, parameter count, finite outputs, dimensions, transparency, black/white inputs, grayscale strength zero, input validation, a Gradio HTTP request, and ONNX parity. `RELEASE_REPORT.md` records visual selection and the separate live Space check. These checks do not imply factual colour reconstruction or eliminate every visual failure case.
|
|
|
|
|
|
|
|
|
|
|
|
PIPELINE_EVAL.json
ADDED
|
@@ -0,0 +1,1196 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"n_color": 78,
|
| 3 |
+
"n_total": 80,
|
| 4 |
+
"pipeline": "actual aspect-preserving release vs prior Space 256/radius8/temp0.38; metrics measured after output resize to256",
|
| 5 |
+
"summary": {
|
| 6 |
+
"old_space": {
|
| 7 |
+
"ab_error": 16.144625015747852,
|
| 8 |
+
"patch_excess": 1.8169478370020022,
|
| 9 |
+
"color_coverage": 0.47298392271384215,
|
| 10 |
+
"missed_color": 0.24084079671968467
|
| 11 |
+
},
|
| 12 |
+
"release": {
|
| 13 |
+
"ab_error": 14.243927399317423,
|
| 14 |
+
"patch_excess": 0.6932277647444071,
|
| 15 |
+
"color_coverage": 0.479247068747496,
|
| 16 |
+
"missed_color": 0.1663338435453805
|
| 17 |
+
}
|
| 18 |
+
},
|
| 19 |
+
"rows": [
|
| 20 |
+
{
|
| 21 |
+
"index": 1616,
|
| 22 |
+
"old_space": {
|
| 23 |
+
"ab_error": 26.323200225830078,
|
| 24 |
+
"patch_excess": 4.660755157470703,
|
| 25 |
+
"color_coverage": 0.823211669921875,
|
| 26 |
+
"missed_color": 0.00838758732119945
|
| 27 |
+
},
|
| 28 |
+
"release": {
|
| 29 |
+
"ab_error": 16.94342803955078,
|
| 30 |
+
"patch_excess": 0.8918601274490356,
|
| 31 |
+
"color_coverage": 0.4547271728515625,
|
| 32 |
+
"missed_color": 0.1244119184526921
|
| 33 |
+
}
|
| 34 |
+
},
|
| 35 |
+
{
|
| 36 |
+
"index": 1890,
|
| 37 |
+
"old_space": {
|
| 38 |
+
"ab_error": 8.562353134155273,
|
| 39 |
+
"patch_excess": 0.4099482297897339,
|
| 40 |
+
"color_coverage": 0.081634521484375,
|
| 41 |
+
"missed_color": 0.5827405612444305
|
| 42 |
+
},
|
| 43 |
+
"release": {
|
| 44 |
+
"ab_error": 8.610569953918457,
|
| 45 |
+
"patch_excess": 0.49974769353866577,
|
| 46 |
+
"color_coverage": 0.0383148193359375,
|
| 47 |
+
"missed_color": 0.44008442116782615
|
| 48 |
+
}
|
| 49 |
+
},
|
| 50 |
+
{
|
| 51 |
+
"index": 801,
|
| 52 |
+
"old_space": {
|
| 53 |
+
"ab_error": 22.603069305419922,
|
| 54 |
+
"patch_excess": 2.7419018745422363,
|
| 55 |
+
"color_coverage": 0.364776611328125,
|
| 56 |
+
"missed_color": 0.3159938757982001
|
| 57 |
+
},
|
| 58 |
+
"release": {
|
| 59 |
+
"ab_error": 16.933408737182617,
|
| 60 |
+
"patch_excess": 0.5424809455871582,
|
| 61 |
+
"color_coverage": 0.7138671875,
|
| 62 |
+
"missed_color": 0.01211770417117891
|
| 63 |
+
}
|
| 64 |
+
},
|
| 65 |
+
{
|
| 66 |
+
"index": 1350,
|
| 67 |
+
"old_space": {
|
| 68 |
+
"ab_error": 19.007339477539062,
|
| 69 |
+
"patch_excess": 1.4587503671646118,
|
| 70 |
+
"color_coverage": 0.2160186767578125,
|
| 71 |
+
"missed_color": 0.8389771099393459
|
| 72 |
+
},
|
| 73 |
+
"release": {
|
| 74 |
+
"ab_error": 16.581514358520508,
|
| 75 |
+
"patch_excess": 0.950056791305542,
|
| 76 |
+
"color_coverage": 0.64739990234375,
|
| 77 |
+
"missed_color": 0.000883903806882258
|
| 78 |
+
}
|
| 79 |
+
},
|
| 80 |
+
{
|
| 81 |
+
"index": 1452,
|
| 82 |
+
"old_space": {
|
| 83 |
+
"ab_error": 11.529170036315918,
|
| 84 |
+
"patch_excess": 1.2069720029830933,
|
| 85 |
+
"color_coverage": 0.402740478515625,
|
| 86 |
+
"missed_color": 0.35575881190643127
|
| 87 |
+
},
|
| 88 |
+
"release": {
|
| 89 |
+
"ab_error": 9.721474647521973,
|
| 90 |
+
"patch_excess": 0.5086259245872498,
|
| 91 |
+
"color_coverage": 0.2576904296875,
|
| 92 |
+
"missed_color": 0.18481101408630252
|
| 93 |
+
}
|
| 94 |
+
},
|
| 95 |
+
{
|
| 96 |
+
"index": 2287,
|
| 97 |
+
"old_space": {
|
| 98 |
+
"ab_error": 15.350773811340332,
|
| 99 |
+
"patch_excess": 1.9994475841522217,
|
| 100 |
+
"color_coverage": 0.5487213134765625,
|
| 101 |
+
"missed_color": 0.08166368855263659
|
| 102 |
+
},
|
| 103 |
+
"release": {
|
| 104 |
+
"ab_error": 14.244087219238281,
|
| 105 |
+
"patch_excess": 1.2213833332061768,
|
| 106 |
+
"color_coverage": 0.6115264892578125,
|
| 107 |
+
"missed_color": 0.009846785537771357
|
| 108 |
+
}
|
| 109 |
+
},
|
| 110 |
+
{
|
| 111 |
+
"index": 1064,
|
| 112 |
+
"old_space": {
|
| 113 |
+
"ab_error": 21.012693405151367,
|
| 114 |
+
"patch_excess": 1.974622130393982,
|
| 115 |
+
"color_coverage": 0.4133453369140625,
|
| 116 |
+
"missed_color": 0.06151211801896733
|
| 117 |
+
},
|
| 118 |
+
"release": {
|
| 119 |
+
"ab_error": 15.226168632507324,
|
| 120 |
+
"patch_excess": 0.29835397005081177,
|
| 121 |
+
"color_coverage": 0.716217041015625,
|
| 122 |
+
"missed_color": 0.0
|
| 123 |
+
}
|
| 124 |
+
},
|
| 125 |
+
{
|
| 126 |
+
"index": 1342,
|
| 127 |
+
"old_space": {
|
| 128 |
+
"ab_error": 25.887615203857422,
|
| 129 |
+
"patch_excess": 2.6944580078125,
|
| 130 |
+
"color_coverage": 0.3334808349609375,
|
| 131 |
+
"missed_color": 0.17779714872198635
|
| 132 |
+
},
|
| 133 |
+
"release": {
|
| 134 |
+
"ab_error": 29.920133590698242,
|
| 135 |
+
"patch_excess": 0.9985980987548828,
|
| 136 |
+
"color_coverage": 0.80023193359375,
|
| 137 |
+
"missed_color": 0.01242864775187695
|
| 138 |
+
}
|
| 139 |
+
},
|
| 140 |
+
{
|
| 141 |
+
"index": 1974,
|
| 142 |
+
"old_space": {
|
| 143 |
+
"ab_error": 9.522749900817871,
|
| 144 |
+
"patch_excess": 1.2533193826675415,
|
| 145 |
+
"color_coverage": 0.207183837890625,
|
| 146 |
+
"missed_color": 0.5120499211017071
|
| 147 |
+
},
|
| 148 |
+
"release": {
|
| 149 |
+
"ab_error": 10.576430320739746,
|
| 150 |
+
"patch_excess": 2.0199408531188965,
|
| 151 |
+
"color_coverage": 0.503326416015625,
|
| 152 |
+
"missed_color": 0.03285038014632047
|
| 153 |
+
}
|
| 154 |
+
},
|
| 155 |
+
{
|
| 156 |
+
"index": 1324,
|
| 157 |
+
"old_space": {
|
| 158 |
+
"ab_error": 26.204675674438477,
|
| 159 |
+
"patch_excess": 2.7856273651123047,
|
| 160 |
+
"color_coverage": 0.9070587158203125,
|
| 161 |
+
"missed_color": 0.0
|
| 162 |
+
},
|
| 163 |
+
"release": {
|
| 164 |
+
"ab_error": 33.15073776245117,
|
| 165 |
+
"patch_excess": 1.4181828498840332,
|
| 166 |
+
"color_coverage": 0.987152099609375,
|
| 167 |
+
"missed_color": 0.0
|
| 168 |
+
}
|
| 169 |
+
},
|
| 170 |
+
{
|
| 171 |
+
"index": 1241,
|
| 172 |
+
"old_space": {
|
| 173 |
+
"ab_error": 12.267753601074219,
|
| 174 |
+
"patch_excess": 2.981765031814575,
|
| 175 |
+
"color_coverage": 0.386505126953125,
|
| 176 |
+
"missed_color": 0.2614813208263298
|
| 177 |
+
},
|
| 178 |
+
"release": {
|
| 179 |
+
"ab_error": 10.905852317810059,
|
| 180 |
+
"patch_excess": 1.2132973670959473,
|
| 181 |
+
"color_coverage": 0.6724700927734375,
|
| 182 |
+
"missed_color": 0.052052269153608416
|
| 183 |
+
}
|
| 184 |
+
},
|
| 185 |
+
{
|
| 186 |
+
"index": 1076,
|
| 187 |
+
"old_space": {
|
| 188 |
+
"ab_error": 32.03386306762695,
|
| 189 |
+
"patch_excess": 1.8285555839538574,
|
| 190 |
+
"color_coverage": 0.5383453369140625,
|
| 191 |
+
"missed_color": 0.18550559192337193
|
| 192 |
+
},
|
| 193 |
+
"release": {
|
| 194 |
+
"ab_error": 25.441709518432617,
|
| 195 |
+
"patch_excess": 0.3919871747493744,
|
| 196 |
+
"color_coverage": 0.240875244140625,
|
| 197 |
+
"missed_color": 0.4045627240782691
|
| 198 |
+
}
|
| 199 |
+
},
|
| 200 |
+
{
|
| 201 |
+
"index": 1592,
|
| 202 |
+
"old_space": {
|
| 203 |
+
"ab_error": 14.867029190063477,
|
| 204 |
+
"patch_excess": 1.3357399702072144,
|
| 205 |
+
"color_coverage": 0.639739990234375,
|
| 206 |
+
"missed_color": 0.15845956598129066
|
| 207 |
+
},
|
| 208 |
+
"release": {
|
| 209 |
+
"ab_error": 14.02211856842041,
|
| 210 |
+
"patch_excess": 0.548101544380188,
|
| 211 |
+
"color_coverage": 0.6025848388671875,
|
| 212 |
+
"missed_color": 0.09786789496177406
|
| 213 |
+
}
|
| 214 |
+
},
|
| 215 |
+
{
|
| 216 |
+
"index": 894,
|
| 217 |
+
"old_space": {
|
| 218 |
+
"ab_error": 19.769493103027344,
|
| 219 |
+
"patch_excess": 1.7964528799057007,
|
| 220 |
+
"color_coverage": 0.419647216796875,
|
| 221 |
+
"missed_color": 0.21247737003936895
|
| 222 |
+
},
|
| 223 |
+
"release": {
|
| 224 |
+
"ab_error": 18.06083106994629,
|
| 225 |
+
"patch_excess": 0.9287981390953064,
|
| 226 |
+
"color_coverage": 0.205474853515625,
|
| 227 |
+
"missed_color": 0.29653150952613583
|
| 228 |
+
}
|
| 229 |
+
},
|
| 230 |
+
{
|
| 231 |
+
"index": 2148,
|
| 232 |
+
"old_space": {
|
| 233 |
+
"ab_error": 7.6851372718811035,
|
| 234 |
+
"patch_excess": 0.2038302719593048,
|
| 235 |
+
"color_coverage": 0.357147216796875,
|
| 236 |
+
"missed_color": 0.06188071827189552
|
| 237 |
+
},
|
| 238 |
+
"release": {
|
| 239 |
+
"ab_error": 8.425639152526855,
|
| 240 |
+
"patch_excess": 0.2777884006500244,
|
| 241 |
+
"color_coverage": 0.5030364990234375,
|
| 242 |
+
"missed_color": 0.029231995748073347
|
| 243 |
+
}
|
| 244 |
+
},
|
| 245 |
+
{
|
| 246 |
+
"index": 499,
|
| 247 |
+
"old_space": {
|
| 248 |
+
"ab_error": 12.139986038208008,
|
| 249 |
+
"patch_excess": 2.4101762771606445,
|
| 250 |
+
"color_coverage": 0.259033203125,
|
| 251 |
+
"missed_color": 0.5720882503848127
|
| 252 |
+
},
|
| 253 |
+
"release": {
|
| 254 |
+
"ab_error": 10.870327949523926,
|
| 255 |
+
"patch_excess": 1.7260417938232422,
|
| 256 |
+
"color_coverage": 0.6872406005859375,
|
| 257 |
+
"missed_color": 0.028860954335556695
|
| 258 |
+
}
|
| 259 |
+
},
|
| 260 |
+
{
|
| 261 |
+
"index": 1091,
|
| 262 |
+
"old_space": {
|
| 263 |
+
"ab_error": 7.706239700317383,
|
| 264 |
+
"patch_excess": 0.9490010738372803,
|
| 265 |
+
"color_coverage": 0.70660400390625,
|
| 266 |
+
"missed_color": 0.04750045695485286
|
| 267 |
+
},
|
| 268 |
+
"release": {
|
| 269 |
+
"ab_error": 8.351296424865723,
|
| 270 |
+
"patch_excess": 0.41664353013038635,
|
| 271 |
+
"color_coverage": 0.585662841796875,
|
| 272 |
+
"missed_color": 0.03813288247121185
|
| 273 |
+
}
|
| 274 |
+
},
|
| 275 |
+
{
|
| 276 |
+
"index": 643,
|
| 277 |
+
"old_space": {
|
| 278 |
+
"ab_error": 12.94747543334961,
|
| 279 |
+
"patch_excess": 1.5321152210235596,
|
| 280 |
+
"color_coverage": 0.28607177734375,
|
| 281 |
+
"missed_color": 0.27395094734394976
|
| 282 |
+
},
|
| 283 |
+
"release": {
|
| 284 |
+
"ab_error": 10.66858959197998,
|
| 285 |
+
"patch_excess": 0.8456946015357971,
|
| 286 |
+
"color_coverage": 0.5540924072265625,
|
| 287 |
+
"missed_color": 0.05258630710217366
|
| 288 |
+
}
|
| 289 |
+
},
|
| 290 |
+
{
|
| 291 |
+
"index": 2199,
|
| 292 |
+
"old_space": {
|
| 293 |
+
"ab_error": 30.45133399963379,
|
| 294 |
+
"patch_excess": 0.11608487367630005,
|
| 295 |
+
"color_coverage": 0.0208892822265625,
|
| 296 |
+
"missed_color": 0.9190106905012267
|
| 297 |
+
},
|
| 298 |
+
"release": {
|
| 299 |
+
"ab_error": 20.48551368713379,
|
| 300 |
+
"patch_excess": 0.5652980804443359,
|
| 301 |
+
"color_coverage": 0.413116455078125,
|
| 302 |
+
"missed_color": 0.29834822993340343
|
| 303 |
+
}
|
| 304 |
+
},
|
| 305 |
+
{
|
| 306 |
+
"index": 28,
|
| 307 |
+
"old_space": {
|
| 308 |
+
"ab_error": 9.457603454589844,
|
| 309 |
+
"patch_excess": 0.390183687210083,
|
| 310 |
+
"color_coverage": 0.058074951171875,
|
| 311 |
+
"missed_color": 0.667576025034101
|
| 312 |
+
},
|
| 313 |
+
"release": {
|
| 314 |
+
"ab_error": 15.951315879821777,
|
| 315 |
+
"patch_excess": 0.9229965209960938,
|
| 316 |
+
"color_coverage": 0.7261199951171875,
|
| 317 |
+
"missed_color": 0.0
|
| 318 |
+
}
|
| 319 |
+
},
|
| 320 |
+
{
|
| 321 |
+
"index": 193,
|
| 322 |
+
"old_space": {
|
| 323 |
+
"ab_error": 8.022804260253906,
|
| 324 |
+
"patch_excess": 0.6247572898864746,
|
| 325 |
+
"color_coverage": 0.52301025390625,
|
| 326 |
+
"missed_color": 0.018376565601259325
|
| 327 |
+
},
|
| 328 |
+
"release": {
|
| 329 |
+
"ab_error": 10.262919425964355,
|
| 330 |
+
"patch_excess": 0.3918754458427429,
|
| 331 |
+
"color_coverage": 0.5903778076171875,
|
| 332 |
+
"missed_color": 0.0
|
| 333 |
+
}
|
| 334 |
+
},
|
| 335 |
+
{
|
| 336 |
+
"index": 1572,
|
| 337 |
+
"old_space": {
|
| 338 |
+
"ab_error": 13.333690643310547,
|
| 339 |
+
"patch_excess": 0.11408320814371109,
|
| 340 |
+
"color_coverage": 0.109130859375,
|
| 341 |
+
"missed_color": 0.43947592309001193
|
| 342 |
+
},
|
| 343 |
+
"release": {
|
| 344 |
+
"ab_error": 12.270377159118652,
|
| 345 |
+
"patch_excess": 0.104813352227211,
|
| 346 |
+
"color_coverage": 0.170135498046875,
|
| 347 |
+
"missed_color": 0.3524247064828994
|
| 348 |
+
}
|
| 349 |
+
},
|
| 350 |
+
{
|
| 351 |
+
"index": 2130,
|
| 352 |
+
"old_space": {
|
| 353 |
+
"ab_error": 16.954084396362305,
|
| 354 |
+
"patch_excess": 1.4006093740463257,
|
| 355 |
+
"color_coverage": 0.595916748046875,
|
| 356 |
+
"missed_color": 0.25908369733414294
|
| 357 |
+
},
|
| 358 |
+
"release": {
|
| 359 |
+
"ab_error": 9.94679069519043,
|
| 360 |
+
"patch_excess": 0.05882778391242027,
|
| 361 |
+
"color_coverage": 0.43670654296875,
|
| 362 |
+
"missed_color": 0.13325367755078057
|
| 363 |
+
}
|
| 364 |
+
},
|
| 365 |
+
{
|
| 366 |
+
"index": 1865,
|
| 367 |
+
"old_space": {
|
| 368 |
+
"ab_error": 7.544837951660156,
|
| 369 |
+
"patch_excess": 0.5778899788856506,
|
| 370 |
+
"color_coverage": 0.1146240234375,
|
| 371 |
+
"missed_color": 0.33362932893568437
|
| 372 |
+
},
|
| 373 |
+
"release": {
|
| 374 |
+
"ab_error": 8.306941986083984,
|
| 375 |
+
"patch_excess": 0.6006697416305542,
|
| 376 |
+
"color_coverage": 0.4653778076171875,
|
| 377 |
+
"missed_color": 0.30293035646327543
|
| 378 |
+
}
|
| 379 |
+
},
|
| 380 |
+
{
|
| 381 |
+
"index": 1802,
|
| 382 |
+
"old_space": {
|
| 383 |
+
"ab_error": 18.950891494750977,
|
| 384 |
+
"patch_excess": 2.2092673778533936,
|
| 385 |
+
"color_coverage": 0.5519561767578125,
|
| 386 |
+
"missed_color": 0.18835859782093795
|
| 387 |
+
},
|
| 388 |
+
"release": {
|
| 389 |
+
"ab_error": 15.6486177444458,
|
| 390 |
+
"patch_excess": 0.3804606795310974,
|
| 391 |
+
"color_coverage": 0.32672119140625,
|
| 392 |
+
"missed_color": 0.17589412600663193
|
| 393 |
+
}
|
| 394 |
+
},
|
| 395 |
+
{
|
| 396 |
+
"index": 1843,
|
| 397 |
+
"old_space": {
|
| 398 |
+
"ab_error": 11.737858772277832,
|
| 399 |
+
"patch_excess": 1.105494499206543,
|
| 400 |
+
"color_coverage": 0.6038665771484375,
|
| 401 |
+
"missed_color": 0.02552606103836552
|
| 402 |
+
},
|
| 403 |
+
"release": {
|
| 404 |
+
"ab_error": 14.591524124145508,
|
| 405 |
+
"patch_excess": 0.31478655338287354,
|
| 406 |
+
"color_coverage": 0.5148162841796875,
|
| 407 |
+
"missed_color": 0.10567075966780455
|
| 408 |
+
}
|
| 409 |
+
},
|
| 410 |
+
{
|
| 411 |
+
"index": 1310,
|
| 412 |
+
"old_space": {
|
| 413 |
+
"ab_error": 23.062294006347656,
|
| 414 |
+
"patch_excess": 0.9265129566192627,
|
| 415 |
+
"color_coverage": 0.88275146484375,
|
| 416 |
+
"missed_color": 0.007003060940543549
|
| 417 |
+
},
|
| 418 |
+
"release": {
|
| 419 |
+
"ab_error": 8.736886024475098,
|
| 420 |
+
"patch_excess": 0.38463208079338074,
|
| 421 |
+
"color_coverage": 0.2847442626953125,
|
| 422 |
+
"missed_color": 0.11650125220294963
|
| 423 |
+
}
|
| 424 |
+
},
|
| 425 |
+
{
|
| 426 |
+
"index": 760,
|
| 427 |
+
"old_space": {
|
| 428 |
+
"ab_error": 13.43350601196289,
|
| 429 |
+
"patch_excess": 0.9921309947967529,
|
| 430 |
+
"color_coverage": 0.2053070068359375,
|
| 431 |
+
"missed_color": 0.9260222323343951
|
| 432 |
+
},
|
| 433 |
+
"release": {
|
| 434 |
+
"ab_error": 16.876251220703125,
|
| 435 |
+
"patch_excess": 1.1696078777313232,
|
| 436 |
+
"color_coverage": 0.6178131103515625,
|
| 437 |
+
"missed_color": 0.44172138074497824
|
| 438 |
+
}
|
| 439 |
+
},
|
| 440 |
+
{
|
| 441 |
+
"index": 1547,
|
| 442 |
+
"old_space": {
|
| 443 |
+
"ab_error": 10.504715919494629,
|
| 444 |
+
"patch_excess": 0.8381835222244263,
|
| 445 |
+
"color_coverage": 0.14837646484375,
|
| 446 |
+
"missed_color": 0.39370380186706716
|
| 447 |
+
},
|
| 448 |
+
"release": {
|
| 449 |
+
"ab_error": 8.540243148803711,
|
| 450 |
+
"patch_excess": 0.3024168610572815,
|
| 451 |
+
"color_coverage": 0.0621490478515625,
|
| 452 |
+
"missed_color": 0.17516270577495066
|
| 453 |
+
}
|
| 454 |
+
},
|
| 455 |
+
{
|
| 456 |
+
"index": 1984,
|
| 457 |
+
"old_space": {
|
| 458 |
+
"ab_error": 27.105270385742188,
|
| 459 |
+
"patch_excess": 3.5282702445983887,
|
| 460 |
+
"color_coverage": 0.7821044921875,
|
| 461 |
+
"missed_color": 0.013363312404547769
|
| 462 |
+
},
|
| 463 |
+
"release": {
|
| 464 |
+
"ab_error": 28.28919792175293,
|
| 465 |
+
"patch_excess": 0.48088812828063965,
|
| 466 |
+
"color_coverage": 0.1480865478515625,
|
| 467 |
+
"missed_color": 0.258590700831495
|
| 468 |
+
}
|
| 469 |
+
},
|
| 470 |
+
{
|
| 471 |
+
"index": 20,
|
| 472 |
+
"old_space": {
|
| 473 |
+
"ab_error": 15.894635200500488,
|
| 474 |
+
"patch_excess": 1.3054187297821045,
|
| 475 |
+
"color_coverage": 0.531951904296875,
|
| 476 |
+
"missed_color": 0.07939068100358423
|
| 477 |
+
},
|
| 478 |
+
"release": {
|
| 479 |
+
"ab_error": 17.26842498779297,
|
| 480 |
+
"patch_excess": 0.5532290935516357,
|
| 481 |
+
"color_coverage": 0.55316162109375,
|
| 482 |
+
"missed_color": 0.08535586277521762
|
| 483 |
+
}
|
| 484 |
+
},
|
| 485 |
+
{
|
| 486 |
+
"index": 995,
|
| 487 |
+
"old_space": {
|
| 488 |
+
"ab_error": 15.624862670898438,
|
| 489 |
+
"patch_excess": 2.2529406547546387,
|
| 490 |
+
"color_coverage": 0.259613037109375,
|
| 491 |
+
"missed_color": 0.4021378891845213
|
| 492 |
+
},
|
| 493 |
+
"release": {
|
| 494 |
+
"ab_error": 13.449739456176758,
|
| 495 |
+
"patch_excess": 0.6617642641067505,
|
| 496 |
+
"color_coverage": 0.57403564453125,
|
| 497 |
+
"missed_color": 0.05704149745600523
|
| 498 |
+
}
|
| 499 |
+
},
|
| 500 |
+
{
|
| 501 |
+
"index": 719,
|
| 502 |
+
"old_space": {
|
| 503 |
+
"ab_error": 17.673622131347656,
|
| 504 |
+
"patch_excess": 6.900073051452637,
|
| 505 |
+
"color_coverage": 0.7959136962890625,
|
| 506 |
+
"missed_color": 0.03709780024776509
|
| 507 |
+
},
|
| 508 |
+
"release": {
|
| 509 |
+
"ab_error": 10.906693458557129,
|
| 510 |
+
"patch_excess": 0.8480014801025391,
|
| 511 |
+
"color_coverage": 0.5038604736328125,
|
| 512 |
+
"missed_color": 0.0922087923125858
|
| 513 |
+
}
|
| 514 |
+
},
|
| 515 |
+
{
|
| 516 |
+
"index": 1494,
|
| 517 |
+
"old_space": {
|
| 518 |
+
"ab_error": 8.360665321350098,
|
| 519 |
+
"patch_excess": 0.411716490983963,
|
| 520 |
+
"color_coverage": 0.1339569091796875,
|
| 521 |
+
"missed_color": 0.5299967917869747
|
| 522 |
+
},
|
| 523 |
+
"release": {
|
| 524 |
+
"ab_error": 7.159595966339111,
|
| 525 |
+
"patch_excess": 0.266187459230423,
|
| 526 |
+
"color_coverage": 0.0975189208984375,
|
| 527 |
+
"missed_color": 0.6700352903432788
|
| 528 |
+
}
|
| 529 |
+
},
|
| 530 |
+
{
|
| 531 |
+
"index": 842,
|
| 532 |
+
"old_space": {
|
| 533 |
+
"ab_error": 11.821839332580566,
|
| 534 |
+
"patch_excess": 2.703233003616333,
|
| 535 |
+
"color_coverage": 0.928680419921875,
|
| 536 |
+
"missed_color": 0.0
|
| 537 |
+
},
|
| 538 |
+
"release": {
|
| 539 |
+
"ab_error": 11.372722625732422,
|
| 540 |
+
"patch_excess": 0.5155807137489319,
|
| 541 |
+
"color_coverage": 0.907684326171875,
|
| 542 |
+
"missed_color": 7.853608733212911e-05
|
| 543 |
+
}
|
| 544 |
+
},
|
| 545 |
+
{
|
| 546 |
+
"index": 514,
|
| 547 |
+
"old_space": {
|
| 548 |
+
"ab_error": 9.687101364135742,
|
| 549 |
+
"patch_excess": 0.6117255687713623,
|
| 550 |
+
"color_coverage": 0.211181640625,
|
| 551 |
+
"missed_color": 0.5444506483032446
|
| 552 |
+
},
|
| 553 |
+
"release": {
|
| 554 |
+
"ab_error": 9.208121299743652,
|
| 555 |
+
"patch_excess": 0.18699583411216736,
|
| 556 |
+
"color_coverage": 0.1870574951171875,
|
| 557 |
+
"missed_color": 0.7229356659842422
|
| 558 |
+
}
|
| 559 |
+
},
|
| 560 |
+
{
|
| 561 |
+
"index": 1700,
|
| 562 |
+
"old_space": {
|
| 563 |
+
"ab_error": 15.625609397888184,
|
| 564 |
+
"patch_excess": 2.0040969848632812,
|
| 565 |
+
"color_coverage": 0.3100738525390625,
|
| 566 |
+
"missed_color": 0.4106577942620268
|
| 567 |
+
},
|
| 568 |
+
"release": {
|
| 569 |
+
"ab_error": 12.55972957611084,
|
| 570 |
+
"patch_excess": 0.6425396203994751,
|
| 571 |
+
"color_coverage": 0.3192138671875,
|
| 572 |
+
"missed_color": 0.323370059270572
|
| 573 |
+
}
|
| 574 |
+
},
|
| 575 |
+
{
|
| 576 |
+
"index": 81,
|
| 577 |
+
"old_space": {
|
| 578 |
+
"ab_error": 16.702062606811523,
|
| 579 |
+
"patch_excess": 3.883439064025879,
|
| 580 |
+
"color_coverage": 0.579315185546875,
|
| 581 |
+
"missed_color": 0.027358006001459816
|
| 582 |
+
},
|
| 583 |
+
"release": {
|
| 584 |
+
"ab_error": 16.94745445251465,
|
| 585 |
+
"patch_excess": 0.9333924651145935,
|
| 586 |
+
"color_coverage": 0.3153076171875,
|
| 587 |
+
"missed_color": 0.003919872401394934
|
| 588 |
+
}
|
| 589 |
+
},
|
| 590 |
+
{
|
| 591 |
+
"index": 669,
|
| 592 |
+
"old_space": {
|
| 593 |
+
"ab_error": 16.509859085083008,
|
| 594 |
+
"patch_excess": 0.4290950298309326,
|
| 595 |
+
"color_coverage": 0.1493682861328125,
|
| 596 |
+
"missed_color": 0.43240032314784016
|
| 597 |
+
},
|
| 598 |
+
"release": {
|
| 599 |
+
"ab_error": 15.242349624633789,
|
| 600 |
+
"patch_excess": 0.8886958360671997,
|
| 601 |
+
"color_coverage": 0.548095703125,
|
| 602 |
+
"missed_color": 0.007888609038635176
|
| 603 |
+
}
|
| 604 |
+
},
|
| 605 |
+
{
|
| 606 |
+
"index": 1863,
|
| 607 |
+
"old_space": {
|
| 608 |
+
"ab_error": 14.619690895080566,
|
| 609 |
+
"patch_excess": 1.6612411737442017,
|
| 610 |
+
"color_coverage": 0.47161865234375,
|
| 611 |
+
"missed_color": 0.12886517030293518
|
| 612 |
+
},
|
| 613 |
+
"release": {
|
| 614 |
+
"ab_error": 15.573603630065918,
|
| 615 |
+
"patch_excess": 2.386152505874634,
|
| 616 |
+
"color_coverage": 0.613861083984375,
|
| 617 |
+
"missed_color": 0.11965677810914038
|
| 618 |
+
}
|
| 619 |
+
},
|
| 620 |
+
{
|
| 621 |
+
"index": 118,
|
| 622 |
+
"old_space": {
|
| 623 |
+
"ab_error": 12.41354751586914,
|
| 624 |
+
"patch_excess": 2.3019068241119385,
|
| 625 |
+
"color_coverage": 0.42901611328125,
|
| 626 |
+
"missed_color": 0.13236981670440096
|
| 627 |
+
},
|
| 628 |
+
"release": {
|
| 629 |
+
"ab_error": 9.036662101745605,
|
| 630 |
+
"patch_excess": 1.469712257385254,
|
| 631 |
+
"color_coverage": 0.6489715576171875,
|
| 632 |
+
"missed_color": 0.0034115932140309523
|
| 633 |
+
}
|
| 634 |
+
},
|
| 635 |
+
{
|
| 636 |
+
"index": 247,
|
| 637 |
+
"old_space": {
|
| 638 |
+
"ab_error": 22.328853607177734,
|
| 639 |
+
"patch_excess": 1.0756666660308838,
|
| 640 |
+
"color_coverage": 0.4027252197265625,
|
| 641 |
+
"missed_color": 0.29081412325978323
|
| 642 |
+
},
|
| 643 |
+
"release": {
|
| 644 |
+
"ab_error": 23.825349807739258,
|
| 645 |
+
"patch_excess": 0.08932070434093475,
|
| 646 |
+
"color_coverage": 0.217254638671875,
|
| 647 |
+
"missed_color": 0.20771556067367208
|
| 648 |
+
}
|
| 649 |
+
},
|
| 650 |
+
{
|
| 651 |
+
"index": 718,
|
| 652 |
+
"old_space": {
|
| 653 |
+
"ab_error": 14.24968433380127,
|
| 654 |
+
"patch_excess": 1.4334428310394287,
|
| 655 |
+
"color_coverage": 0.6019744873046875,
|
| 656 |
+
"missed_color": 0.1952375935096407
|
| 657 |
+
},
|
| 658 |
+
"release": {
|
| 659 |
+
"ab_error": 9.221932411193848,
|
| 660 |
+
"patch_excess": 0.7787426114082336,
|
| 661 |
+
"color_coverage": 0.536376953125,
|
| 662 |
+
"missed_color": 0.23814666526182698
|
| 663 |
+
}
|
| 664 |
+
},
|
| 665 |
+
{
|
| 666 |
+
"index": 875,
|
| 667 |
+
"old_space": {
|
| 668 |
+
"ab_error": 12.56160831451416,
|
| 669 |
+
"patch_excess": 0.5671428442001343,
|
| 670 |
+
"color_coverage": 0.9713592529296875,
|
| 671 |
+
"missed_color": 0.003802414009628131
|
| 672 |
+
},
|
| 673 |
+
"release": {
|
| 674 |
+
"ab_error": 14.186704635620117,
|
| 675 |
+
"patch_excess": 0.3387293219566345,
|
| 676 |
+
"color_coverage": 0.8649749755859375,
|
| 677 |
+
"missed_color": 0.03068094606851322
|
| 678 |
+
}
|
| 679 |
+
},
|
| 680 |
+
{
|
| 681 |
+
"index": 2311,
|
| 682 |
+
"old_space": {
|
| 683 |
+
"ab_error": 22.329435348510742,
|
| 684 |
+
"patch_excess": 0.5803585052490234,
|
| 685 |
+
"color_coverage": 0.1852569580078125,
|
| 686 |
+
"missed_color": 0.4449274087109547
|
| 687 |
+
},
|
| 688 |
+
"release": {
|
| 689 |
+
"ab_error": 23.35008430480957,
|
| 690 |
+
"patch_excess": 0.3492544889450073,
|
| 691 |
+
"color_coverage": 0.12896728515625,
|
| 692 |
+
"missed_color": 0.3160756709194897
|
| 693 |
+
}
|
| 694 |
+
},
|
| 695 |
+
{
|
| 696 |
+
"index": 272,
|
| 697 |
+
"old_space": {
|
| 698 |
+
"ab_error": 50.092628479003906,
|
| 699 |
+
"patch_excess": 2.4126620292663574,
|
| 700 |
+
"color_coverage": 0.942230224609375,
|
| 701 |
+
"missed_color": 0.01947199203006838
|
| 702 |
+
},
|
| 703 |
+
"release": {
|
| 704 |
+
"ab_error": 13.429896354675293,
|
| 705 |
+
"patch_excess": 0.572797417640686,
|
| 706 |
+
"color_coverage": 0.0374298095703125,
|
| 707 |
+
"missed_color": 0.5612462074899244
|
| 708 |
+
}
|
| 709 |
+
},
|
| 710 |
+
{
|
| 711 |
+
"index": 1947,
|
| 712 |
+
"old_space": {
|
| 713 |
+
"ab_error": 13.949646949768066,
|
| 714 |
+
"patch_excess": 1.9255857467651367,
|
| 715 |
+
"color_coverage": 0.27593994140625,
|
| 716 |
+
"missed_color": 0.4522735813603908
|
| 717 |
+
},
|
| 718 |
+
"release": {
|
| 719 |
+
"ab_error": 14.641658782958984,
|
| 720 |
+
"patch_excess": 0.21683070063591003,
|
| 721 |
+
"color_coverage": 0.1020965576171875,
|
| 722 |
+
"missed_color": 0.6970526654855854
|
| 723 |
+
}
|
| 724 |
+
},
|
| 725 |
+
{
|
| 726 |
+
"index": 2307,
|
| 727 |
+
"old_space": {
|
| 728 |
+
"ab_error": 14.67344856262207,
|
| 729 |
+
"patch_excess": 1.292840838432312,
|
| 730 |
+
"color_coverage": 0.1552886962890625,
|
| 731 |
+
"missed_color": 0.40270344180958706
|
| 732 |
+
},
|
| 733 |
+
"release": {
|
| 734 |
+
"ab_error": 13.693403244018555,
|
| 735 |
+
"patch_excess": 0.16015203297138214,
|
| 736 |
+
"color_coverage": 0.4368133544921875,
|
| 737 |
+
"missed_color": 0.07055514318084151
|
| 738 |
+
}
|
| 739 |
+
},
|
| 740 |
+
{
|
| 741 |
+
"index": 1936,
|
| 742 |
+
"old_space": {
|
| 743 |
+
"ab_error": 6.898637771606445,
|
| 744 |
+
"patch_excess": 0.40601056814193726,
|
| 745 |
+
"color_coverage": 0.3411102294921875,
|
| 746 |
+
"missed_color": 0.24669745462356352
|
| 747 |
+
},
|
| 748 |
+
"release": {
|
| 749 |
+
"ab_error": 7.870009422302246,
|
| 750 |
+
"patch_excess": 0.27915287017822266,
|
| 751 |
+
"color_coverage": 0.320526123046875,
|
| 752 |
+
"missed_color": 0.16385637060108116
|
| 753 |
+
}
|
| 754 |
+
},
|
| 755 |
+
{
|
| 756 |
+
"index": 1935,
|
| 757 |
+
"old_space": {
|
| 758 |
+
"ab_error": 19.05333137512207,
|
| 759 |
+
"patch_excess": 0.9947030544281006,
|
| 760 |
+
"color_coverage": 0.8654022216796875,
|
| 761 |
+
"missed_color": 0.010393743440430646
|
| 762 |
+
},
|
| 763 |
+
"release": {
|
| 764 |
+
"ab_error": 21.285797119140625,
|
| 765 |
+
"patch_excess": 1.0532053709030151,
|
| 766 |
+
"color_coverage": 0.874114990234375,
|
| 767 |
+
"missed_color": 0.01535362426786742
|
| 768 |
+
}
|
| 769 |
+
},
|
| 770 |
+
{
|
| 771 |
+
"index": 406,
|
| 772 |
+
"old_space": {
|
| 773 |
+
"ab_error": 11.752641677856445,
|
| 774 |
+
"patch_excess": 2.6542296409606934,
|
| 775 |
+
"color_coverage": 0.50799560546875,
|
| 776 |
+
"missed_color": 0.2511842190643047
|
| 777 |
+
},
|
| 778 |
+
"release": {
|
| 779 |
+
"ab_error": 14.617018699645996,
|
| 780 |
+
"patch_excess": 1.5975663661956787,
|
| 781 |
+
"color_coverage": 0.750701904296875,
|
| 782 |
+
"missed_color": 0.01739017584841996
|
| 783 |
+
}
|
| 784 |
+
},
|
| 785 |
+
{
|
| 786 |
+
"index": 630,
|
| 787 |
+
"old_space": {
|
| 788 |
+
"ab_error": 9.711251258850098,
|
| 789 |
+
"patch_excess": 0.9617525339126587,
|
| 790 |
+
"color_coverage": 0.1025543212890625,
|
| 791 |
+
"missed_color": 0.411119306656832
|
| 792 |
+
},
|
| 793 |
+
"release": {
|
| 794 |
+
"ab_error": 7.769914627075195,
|
| 795 |
+
"patch_excess": 0.10242865979671478,
|
| 796 |
+
"color_coverage": 0.0,
|
| 797 |
+
"missed_color": 0.8865019361976766
|
| 798 |
+
}
|
| 799 |
+
},
|
| 800 |
+
{
|
| 801 |
+
"index": 1023,
|
| 802 |
+
"old_space": {
|
| 803 |
+
"ab_error": 12.51215934753418,
|
| 804 |
+
"patch_excess": 3.8293511867523193,
|
| 805 |
+
"color_coverage": 0.8065338134765625,
|
| 806 |
+
"missed_color": 0.0007825250966977441
|
| 807 |
+
},
|
| 808 |
+
"release": {
|
| 809 |
+
"ab_error": 12.0440673828125,
|
| 810 |
+
"patch_excess": 0.339896023273468,
|
| 811 |
+
"color_coverage": 0.067352294921875,
|
| 812 |
+
"missed_color": 0.3461667449191763
|
| 813 |
+
}
|
| 814 |
+
},
|
| 815 |
+
{
|
| 816 |
+
"index": 941,
|
| 817 |
+
"old_space": {
|
| 818 |
+
"ab_error": 11.803794860839844,
|
| 819 |
+
"patch_excess": 1.020538091659546,
|
| 820 |
+
"color_coverage": 0.15338134765625,
|
| 821 |
+
"missed_color": 0.613625304136253
|
| 822 |
+
},
|
| 823 |
+
"release": {
|
| 824 |
+
"ab_error": 11.58432674407959,
|
| 825 |
+
"patch_excess": 0.5445202589035034,
|
| 826 |
+
"color_coverage": 0.40020751953125,
|
| 827 |
+
"missed_color": 0.032907542579075424
|
| 828 |
+
}
|
| 829 |
+
},
|
| 830 |
+
{
|
| 831 |
+
"index": 1746,
|
| 832 |
+
"old_space": {
|
| 833 |
+
"ab_error": 21.40471076965332,
|
| 834 |
+
"patch_excess": 2.445641279220581,
|
| 835 |
+
"color_coverage": 0.553466796875,
|
| 836 |
+
"missed_color": 0.26700241575564015
|
| 837 |
+
},
|
| 838 |
+
"release": {
|
| 839 |
+
"ab_error": 16.30540657043457,
|
| 840 |
+
"patch_excess": 0.5714777708053589,
|
| 841 |
+
"color_coverage": 0.1327056884765625,
|
| 842 |
+
"missed_color": 0.39769889039020595
|
| 843 |
+
}
|
| 844 |
+
},
|
| 845 |
+
{
|
| 846 |
+
"index": 584,
|
| 847 |
+
"old_space": {
|
| 848 |
+
"ab_error": 20.1906681060791,
|
| 849 |
+
"patch_excess": 1.2620389461517334,
|
| 850 |
+
"color_coverage": 0.69354248046875,
|
| 851 |
+
"missed_color": 0.09630933556105857
|
| 852 |
+
},
|
| 853 |
+
"release": {
|
| 854 |
+
"ab_error": 22.57025909423828,
|
| 855 |
+
"patch_excess": 1.746338963508606,
|
| 856 |
+
"color_coverage": 0.646820068359375,
|
| 857 |
+
"missed_color": 0.1843243727063878
|
| 858 |
+
}
|
| 859 |
+
},
|
| 860 |
+
{
|
| 861 |
+
"index": 1435,
|
| 862 |
+
"old_space": {
|
| 863 |
+
"ab_error": 21.27211570739746,
|
| 864 |
+
"patch_excess": 5.1058244705200195,
|
| 865 |
+
"color_coverage": 0.6849822998046875,
|
| 866 |
+
"missed_color": 0.35429673620628394
|
| 867 |
+
},
|
| 868 |
+
"release": {
|
| 869 |
+
"ab_error": 11.184368133544922,
|
| 870 |
+
"patch_excess": 0.9250662922859192,
|
| 871 |
+
"color_coverage": 0.50732421875,
|
| 872 |
+
"missed_color": 0.014263235368763007
|
| 873 |
+
}
|
| 874 |
+
},
|
| 875 |
+
{
|
| 876 |
+
"index": 1955,
|
| 877 |
+
"old_space": {
|
| 878 |
+
"ab_error": 7.787717819213867,
|
| 879 |
+
"patch_excess": 1.0435097217559814,
|
| 880 |
+
"color_coverage": 0.270172119140625,
|
| 881 |
+
"missed_color": 0.2432694031188385
|
| 882 |
+
},
|
| 883 |
+
"release": {
|
| 884 |
+
"ab_error": 12.496732711791992,
|
| 885 |
+
"patch_excess": 0.31601670384407043,
|
| 886 |
+
"color_coverage": 0.7804718017578125,
|
| 887 |
+
"missed_color": 0.0
|
| 888 |
+
}
|
| 889 |
+
},
|
| 890 |
+
{
|
| 891 |
+
"index": 1047,
|
| 892 |
+
"old_space": {
|
| 893 |
+
"ab_error": 12.725871086120605,
|
| 894 |
+
"patch_excess": 1.604668140411377,
|
| 895 |
+
"color_coverage": 0.195526123046875,
|
| 896 |
+
"missed_color": 0.4522170570355802
|
| 897 |
+
},
|
| 898 |
+
"release": {
|
| 899 |
+
"ab_error": 12.001117706298828,
|
| 900 |
+
"patch_excess": 0.2767108678817749,
|
| 901 |
+
"color_coverage": 0.17236328125,
|
| 902 |
+
"missed_color": 0.46093330949401035
|
| 903 |
+
}
|
| 904 |
+
},
|
| 905 |
+
{
|
| 906 |
+
"index": 1689,
|
| 907 |
+
"old_space": {
|
| 908 |
+
"ab_error": 26.23225212097168,
|
| 909 |
+
"patch_excess": 1.7044408321380615,
|
| 910 |
+
"color_coverage": 0.58978271484375,
|
| 911 |
+
"missed_color": 0.15641351282702565
|
| 912 |
+
},
|
| 913 |
+
"release": {
|
| 914 |
+
"ab_error": 17.07444953918457,
|
| 915 |
+
"patch_excess": 0.4091215431690216,
|
| 916 |
+
"color_coverage": 0.90069580078125,
|
| 917 |
+
"missed_color": 0.0014901363136059606
|
| 918 |
+
}
|
| 919 |
+
},
|
| 920 |
+
{
|
| 921 |
+
"index": 772,
|
| 922 |
+
"old_space": {
|
| 923 |
+
"ab_error": 6.426523208618164,
|
| 924 |
+
"patch_excess": 0.38687315583229065,
|
| 925 |
+
"color_coverage": 0.258514404296875,
|
| 926 |
+
"missed_color": 0.26649261974093785
|
| 927 |
+
},
|
| 928 |
+
"release": {
|
| 929 |
+
"ab_error": 6.300887107849121,
|
| 930 |
+
"patch_excess": 0.348586767911911,
|
| 931 |
+
"color_coverage": 0.2451171875,
|
| 932 |
+
"missed_color": 0.32392810523144894
|
| 933 |
+
}
|
| 934 |
+
},
|
| 935 |
+
{
|
| 936 |
+
"index": 618,
|
| 937 |
+
"old_space": {
|
| 938 |
+
"ab_error": 13.832884788513184,
|
| 939 |
+
"patch_excess": 1.9853154420852661,
|
| 940 |
+
"color_coverage": 0.485137939453125,
|
| 941 |
+
"missed_color": 0.20293154973672975
|
| 942 |
+
},
|
| 943 |
+
"release": {
|
| 944 |
+
"ab_error": 14.936275482177734,
|
| 945 |
+
"patch_excess": 0.5301600694656372,
|
| 946 |
+
"color_coverage": 0.8293914794921875,
|
| 947 |
+
"missed_color": 0.0007115411982353779
|
| 948 |
+
}
|
| 949 |
+
},
|
| 950 |
+
{
|
| 951 |
+
"index": 336,
|
| 952 |
+
"old_space": {
|
| 953 |
+
"ab_error": 16.60566520690918,
|
| 954 |
+
"patch_excess": 1.2380704879760742,
|
| 955 |
+
"color_coverage": 0.4294891357421875,
|
| 956 |
+
"missed_color": 0.08609339996553507
|
| 957 |
+
},
|
| 958 |
+
"release": {
|
| 959 |
+
"ab_error": 14.536388397216797,
|
| 960 |
+
"patch_excess": 1.1959733963012695,
|
| 961 |
+
"color_coverage": 0.43182373046875,
|
| 962 |
+
"missed_color": 0.1851456143374117
|
| 963 |
+
}
|
| 964 |
+
},
|
| 965 |
+
{
|
| 966 |
+
"index": 1086,
|
| 967 |
+
"old_space": {
|
| 968 |
+
"ab_error": 17.58598518371582,
|
| 969 |
+
"patch_excess": 1.7815990447998047,
|
| 970 |
+
"color_coverage": 0.70526123046875,
|
| 971 |
+
"missed_color": 0.0451198129748685
|
| 972 |
+
},
|
| 973 |
+
"release": {
|
| 974 |
+
"ab_error": 8.847654342651367,
|
| 975 |
+
"patch_excess": 0.4222590923309326,
|
| 976 |
+
"color_coverage": 0.053924560546875,
|
| 977 |
+
"missed_color": 0.43115137346580945
|
| 978 |
+
}
|
| 979 |
+
},
|
| 980 |
+
{
|
| 981 |
+
"index": 2140,
|
| 982 |
+
"old_space": {
|
| 983 |
+
"ab_error": 7.6119256019592285,
|
| 984 |
+
"patch_excess": 0.15633070468902588,
|
| 985 |
+
"color_coverage": 0.7803955078125,
|
| 986 |
+
"missed_color": 0.0044853635505193576
|
| 987 |
+
},
|
| 988 |
+
"release": {
|
| 989 |
+
"ab_error": 9.672757148742676,
|
| 990 |
+
"patch_excess": 0.08361931890249252,
|
| 991 |
+
"color_coverage": 0.7842254638671875,
|
| 992 |
+
"missed_color": 0.001731192949323261
|
| 993 |
+
}
|
| 994 |
+
},
|
| 995 |
+
{
|
| 996 |
+
"index": 1994,
|
| 997 |
+
"old_space": {
|
| 998 |
+
"ab_error": 23.052637100219727,
|
| 999 |
+
"patch_excess": 4.873438835144043,
|
| 1000 |
+
"color_coverage": 0.569427490234375,
|
| 1001 |
+
"missed_color": 0.049524982406755805
|
| 1002 |
+
},
|
| 1003 |
+
"release": {
|
| 1004 |
+
"ab_error": 17.308002471923828,
|
| 1005 |
+
"patch_excess": 1.5104169845581055,
|
| 1006 |
+
"color_coverage": 0.5511016845703125,
|
| 1007 |
+
"missed_color": 0.014250527797325828
|
| 1008 |
+
}
|
| 1009 |
+
},
|
| 1010 |
+
{
|
| 1011 |
+
"index": 451,
|
| 1012 |
+
"old_space": {
|
| 1013 |
+
"ab_error": 10.520956993103027,
|
| 1014 |
+
"patch_excess": 1.2838455438613892,
|
| 1015 |
+
"color_coverage": 0.65618896484375,
|
| 1016 |
+
"missed_color": 0.016560718085831993
|
| 1017 |
+
},
|
| 1018 |
+
"release": {
|
| 1019 |
+
"ab_error": 9.030373573303223,
|
| 1020 |
+
"patch_excess": 0.754244327545166,
|
| 1021 |
+
"color_coverage": 0.6208953857421875,
|
| 1022 |
+
"missed_color": 0.035385916568902956
|
| 1023 |
+
}
|
| 1024 |
+
},
|
| 1025 |
+
{
|
| 1026 |
+
"index": 1807,
|
| 1027 |
+
"old_space": {
|
| 1028 |
+
"ab_error": 17.55420684814453,
|
| 1029 |
+
"patch_excess": 3.6267919540405273,
|
| 1030 |
+
"color_coverage": 0.8811798095703125,
|
| 1031 |
+
"missed_color": 0.03126752664049355
|
| 1032 |
+
},
|
| 1033 |
+
"release": {
|
| 1034 |
+
"ab_error": 16.857818603515625,
|
| 1035 |
+
"patch_excess": 0.4508571922779083,
|
| 1036 |
+
"color_coverage": 0.965179443359375,
|
| 1037 |
+
"missed_color": 0.0
|
| 1038 |
+
}
|
| 1039 |
+
},
|
| 1040 |
+
{
|
| 1041 |
+
"index": 194,
|
| 1042 |
+
"old_space": {
|
| 1043 |
+
"ab_error": 21.62356185913086,
|
| 1044 |
+
"patch_excess": 3.4757049083709717,
|
| 1045 |
+
"color_coverage": 0.4154815673828125,
|
| 1046 |
+
"missed_color": 0.3036251799502683
|
| 1047 |
+
},
|
| 1048 |
+
"release": {
|
| 1049 |
+
"ab_error": 17.168527603149414,
|
| 1050 |
+
"patch_excess": 1.7961444854736328,
|
| 1051 |
+
"color_coverage": 0.7869110107421875,
|
| 1052 |
+
"missed_color": 0.006255725690354666
|
| 1053 |
+
}
|
| 1054 |
+
},
|
| 1055 |
+
{
|
| 1056 |
+
"index": 1636,
|
| 1057 |
+
"old_space": {
|
| 1058 |
+
"ab_error": 18.11170768737793,
|
| 1059 |
+
"patch_excess": 2.316526174545288,
|
| 1060 |
+
"color_coverage": 0.4608001708984375,
|
| 1061 |
+
"missed_color": 0.2738500960922486
|
| 1062 |
+
},
|
| 1063 |
+
"release": {
|
| 1064 |
+
"ab_error": 17.05701446533203,
|
| 1065 |
+
"patch_excess": 0.8802337646484375,
|
| 1066 |
+
"color_coverage": 0.3453216552734375,
|
| 1067 |
+
"missed_color": 0.262780269058296
|
| 1068 |
+
}
|
| 1069 |
+
},
|
| 1070 |
+
{
|
| 1071 |
+
"index": 409,
|
| 1072 |
+
"old_space": {
|
| 1073 |
+
"ab_error": 21.841514587402344,
|
| 1074 |
+
"patch_excess": 2.8149540424346924,
|
| 1075 |
+
"color_coverage": 0.5799102783203125,
|
| 1076 |
+
"missed_color": 0.07750204292288504
|
| 1077 |
+
},
|
| 1078 |
+
"release": {
|
| 1079 |
+
"ab_error": 21.5656795501709,
|
| 1080 |
+
"patch_excess": 0.5761880278587341,
|
| 1081 |
+
"color_coverage": 0.636871337890625,
|
| 1082 |
+
"missed_color": 0.043051911745731364
|
| 1083 |
+
}
|
| 1084 |
+
},
|
| 1085 |
+
{
|
| 1086 |
+
"index": 2471,
|
| 1087 |
+
"old_space": {
|
| 1088 |
+
"ab_error": 16.08145523071289,
|
| 1089 |
+
"patch_excess": 2.2105460166931152,
|
| 1090 |
+
"color_coverage": 0.7911224365234375,
|
| 1091 |
+
"missed_color": 0.02534622878306507
|
| 1092 |
+
},
|
| 1093 |
+
"release": {
|
| 1094 |
+
"ab_error": 17.141895294189453,
|
| 1095 |
+
"patch_excess": 0.7329339981079102,
|
| 1096 |
+
"color_coverage": 0.7151947021484375,
|
| 1097 |
+
"missed_color": 0.037562970634182305
|
| 1098 |
+
}
|
| 1099 |
+
},
|
| 1100 |
+
{
|
| 1101 |
+
"index": 665,
|
| 1102 |
+
"old_space": {
|
| 1103 |
+
"ab_error": 19.746185302734375,
|
| 1104 |
+
"patch_excess": 2.317701578140259,
|
| 1105 |
+
"color_coverage": 0.7936553955078125,
|
| 1106 |
+
"missed_color": 0.09188258199790325
|
| 1107 |
+
},
|
| 1108 |
+
"release": {
|
| 1109 |
+
"ab_error": 14.18701457977295,
|
| 1110 |
+
"patch_excess": 0.9515336751937866,
|
| 1111 |
+
"color_coverage": 0.8133697509765625,
|
| 1112 |
+
"missed_color": 0.05256851879586641
|
| 1113 |
+
}
|
| 1114 |
+
},
|
| 1115 |
+
{
|
| 1116 |
+
"index": 2151,
|
| 1117 |
+
"old_space": {
|
| 1118 |
+
"ab_error": 17.301462173461914,
|
| 1119 |
+
"patch_excess": 0.5416412353515625,
|
| 1120 |
+
"color_coverage": 0.3140869140625,
|
| 1121 |
+
"missed_color": 0.5995582777385642
|
| 1122 |
+
},
|
| 1123 |
+
"release": {
|
| 1124 |
+
"ab_error": 22.86566162109375,
|
| 1125 |
+
"patch_excess": 0.31695353984832764,
|
| 1126 |
+
"color_coverage": 0.8281402587890625,
|
| 1127 |
+
"missed_color": 0.020811038502698147
|
| 1128 |
+
}
|
| 1129 |
+
},
|
| 1130 |
+
{
|
| 1131 |
+
"index": 1377,
|
| 1132 |
+
"old_space": {
|
| 1133 |
+
"ab_error": 12.53142261505127,
|
| 1134 |
+
"patch_excess": 2.1505064964294434,
|
| 1135 |
+
"color_coverage": 0.8076019287109375,
|
| 1136 |
+
"missed_color": 0.01162006310521162
|
| 1137 |
+
},
|
| 1138 |
+
"release": {
|
| 1139 |
+
"ab_error": 14.857101440429688,
|
| 1140 |
+
"patch_excess": 1.1797388792037964,
|
| 1141 |
+
"color_coverage": 0.5686492919921875,
|
| 1142 |
+
"missed_color": 0.08688934827548689
|
| 1143 |
+
}
|
| 1144 |
+
},
|
| 1145 |
+
{
|
| 1146 |
+
"index": 246,
|
| 1147 |
+
"old_space": {
|
| 1148 |
+
"ab_error": 16.83350372314453,
|
| 1149 |
+
"patch_excess": 1.9284921884536743,
|
| 1150 |
+
"color_coverage": 0.5128631591796875,
|
| 1151 |
+
"missed_color": 0.0004520182615377661
|
| 1152 |
+
},
|
| 1153 |
+
"release": {
|
| 1154 |
+
"ab_error": 9.959344863891602,
|
| 1155 |
+
"patch_excess": 0.281173974275589,
|
| 1156 |
+
"color_coverage": 0.2016754150390625,
|
| 1157 |
+
"missed_color": 0.1439678162997785
|
| 1158 |
+
}
|
| 1159 |
+
},
|
| 1160 |
+
{
|
| 1161 |
+
"index": 1679,
|
| 1162 |
+
"old_space": {
|
| 1163 |
+
"ab_error": 5.608643531799316,
|
| 1164 |
+
"patch_excess": 1.0197176933288574,
|
| 1165 |
+
"color_coverage": 0.28912353515625,
|
| 1166 |
+
"missed_color": 0.13227684346701166
|
| 1167 |
+
},
|
| 1168 |
+
"release": {
|
| 1169 |
+
"ab_error": 4.4221906661987305,
|
| 1170 |
+
"patch_excess": 0.4705710709095001,
|
| 1171 |
+
"color_coverage": 0.210845947265625,
|
| 1172 |
+
"missed_color": 0.023673997412677877
|
| 1173 |
+
}
|
| 1174 |
+
},
|
| 1175 |
+
{
|
| 1176 |
+
"index": 2332,
|
| 1177 |
+
"old_space": {
|
| 1178 |
+
"ab_error": 13.967047691345215,
|
| 1179 |
+
"patch_excess": 3.781674861907959,
|
| 1180 |
+
"color_coverage": 0.5753173828125,
|
| 1181 |
+
"missed_color": 0.03037200832466181
|
| 1182 |
+
},
|
| 1183 |
+
"release": {
|
| 1184 |
+
"ab_error": 7.873258590698242,
|
| 1185 |
+
"patch_excess": 0.19574034214019775,
|
| 1186 |
+
"color_coverage": 0.108612060546875,
|
| 1187 |
+
"missed_color": 0.39815296566077
|
| 1188 |
+
}
|
| 1189 |
+
}
|
| 1190 |
+
],
|
| 1191 |
+
"median_pipeline_seconds": 0.06851194500001156,
|
| 1192 |
+
"torch": "2.8.0+cu128",
|
| 1193 |
+
"device": "NVIDIA L4",
|
| 1194 |
+
"checkpoint": "round6 palette9000",
|
| 1195 |
+
"limitations": "Previously inspected development/reporting set, no independent human ratings. Exact hue error is auxiliary."
|
| 1196 |
+
}
|
QA.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"parameters": 3994676,
|
| 3 |
+
"torch": "2.8.0+cu128",
|
| 4 |
+
"tests": [
|
| 5 |
+
{
|
| 6 |
+
"size": [
|
| 7 |
+
1,
|
| 8 |
+
1
|
| 9 |
+
],
|
| 10 |
+
"alpha": "exact",
|
| 11 |
+
"max_L_error": 0.0753021240234375,
|
| 12 |
+
"seconds": 0.03764470492023975
|
| 13 |
+
},
|
| 14 |
+
{
|
| 15 |
+
"size": [
|
| 16 |
+
17,
|
| 17 |
+
23
|
| 18 |
+
],
|
| 19 |
+
"alpha": "exact",
|
| 20 |
+
"max_L_error": 0.17739105224609375,
|
| 21 |
+
"seconds": 0.019733823952265084
|
| 22 |
+
},
|
| 23 |
+
{
|
| 24 |
+
"size": [
|
| 25 |
+
320,
|
| 26 |
+
191
|
| 27 |
+
],
|
| 28 |
+
"alpha": "exact",
|
| 29 |
+
"max_L_error": 0.2049694061279297,
|
| 30 |
+
"seconds": 0.15181890805251896
|
| 31 |
+
},
|
| 32 |
+
{
|
| 33 |
+
"size": [
|
| 34 |
+
193,
|
| 35 |
+
320
|
| 36 |
+
],
|
| 37 |
+
"alpha": "exact",
|
| 38 |
+
"max_L_error": 0.21184539794921875,
|
| 39 |
+
"seconds": 0.20581824099645019
|
| 40 |
+
},
|
| 41 |
+
{
|
| 42 |
+
"size": [
|
| 43 |
+
1024,
|
| 44 |
+
768
|
| 45 |
+
],
|
| 46 |
+
"alpha": "exact",
|
| 47 |
+
"max_L_error": 0.2049694061279297,
|
| 48 |
+
"seconds": 1.8695425690384582
|
| 49 |
+
}
|
| 50 |
+
],
|
| 51 |
+
"gradio_config_components": 11,
|
| 52 |
+
"ui_function_all_modes": "passed",
|
| 53 |
+
"gradio_http_request": "passed",
|
| 54 |
+
"onnx_max_abs_error": 0.000911712646484375
|
| 55 |
+
}
|
README.md
CHANGED
|
@@ -3,78 +3,75 @@ license: apache-2.0
|
|
| 3 |
pipeline_tag: image-to-image
|
| 4 |
tags:
|
| 5 |
- colorization
|
| 6 |
-
-
|
| 7 |
- pytorch
|
| 8 |
-
- safetensors
|
| 9 |
- onnx
|
| 10 |
-
|
| 11 |
-
-
|
| 12 |
-
|
| 13 |
---
|
| 14 |
-
# Mini U-Net Colorizer — broader-data trained candidate
|
| 15 |
|
| 16 |
-
|
| 17 |
-
incorrect object hues remain. Predicted colors are not evidence of original
|
| 18 |
-
historical colors.
|
| 19 |
|
| 20 |
-
|
| 21 |
-
It starts from the audited bin-mapping repair of main commit
|
| 22 |
-
`6c47ea40724d8fcd67d4f36ce837dc1cb5b1b2a8` and changes all 65 learned parameter
|
| 23 |
-
tensors. The color vocabulary is unchanged. The selected weights are update
|
| 24 |
-
748 of a completed 1,122-update BF16 L4 run using a 17,325-photo mixed training
|
| 25 |
-
pool (11,943 Imagenette plus 5,382 COCO), batch 32, initial learning rate 1e-5,
|
| 26 |
-
weighted classification loss and frozen BatchNorm running statistics.
|
| 27 |
|
| 28 |
-
|
| 29 |
-
The selected model retained color strength better than spatial-loss candidates.
|
| 30 |
-
It was then scored on separate Imagenette200 and COCO-val100 checks.
|
| 31 |
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
|
|
|
|
|
|
| 36 |
|
| 37 |
-
|
| 38 |
-
raw learned weights alone improve error by 1.97% and 3.25%, respectively.
|
| 39 |
-
Excess-edge reductions are proxies, not counts of visible blotches removed.
|
| 40 |
-
COCO is a convenience sample; older upstream training exposure is unknown.
|
| 41 |
|
| 42 |
-
|
| 43 |
|
| 44 |
```bash
|
| 45 |
-
|
| 46 |
-
python inference.py --model . --
|
| 47 |
```
|
| 48 |
|
|
|
|
|
|
|
| 49 |
```python
|
| 50 |
from PIL import Image
|
| 51 |
-
from
|
| 52 |
-
|
| 53 |
-
model =
|
| 54 |
-
colorize(model, Image.open('
|
|
|
|
| 55 |
```
|
| 56 |
|
| 57 |
-
|
| 58 |
-
The wrapper preserves aspect ratio and original luminance. Old app code that
|
| 59 |
-
only loads safetensors will not automatically gain guided filtering.
|
| 60 |
|
| 61 |
-
|
| 62 |
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 67 |
|
| 68 |
-
The
|
| 69 |
-
batch/spatial sizes and verified PyTorch parity. See `DEPLOYMENT.md` for the
|
| 70 |
-
Lab input contract, CPU timings, publication commands and integration limits.
|
| 71 |
-
See `RESEARCH_ROUND2.md` and `reports/round2/` for complete measured evidence.
|
| 72 |
|
| 73 |
-
##
|
| 74 |
|
| 75 |
-
|
| 76 |
-
details, especially without luminance boundaries. Semantically wrong hues
|
| 77 |
-
remain. The coffee and rocket failure examples are retained. Browser/mobile
|
| 78 |
-
performance, video consistency and general production quality are unvalidated.
|
| 79 |
-
The separate experiment bundle contains all runs and reproduction code; GPU
|
| 80 |
-
optimizer state is not included. This is a weights-only continuation point.
|
|
|
|
| 3 |
pipeline_tag: image-to-image
|
| 4 |
tags:
|
| 5 |
- colorization
|
| 6 |
+
- image-colorization
|
| 7 |
- pytorch
|
|
|
|
| 8 |
- onnx
|
| 9 |
+
- mobilenet-v3
|
| 10 |
+
- small-model
|
| 11 |
+
library_name: pytorch
|
| 12 |
---
|
|
|
|
| 13 |
|
| 14 |
+
# Mini Photo Colorizer — v3.0
|
|
|
|
|
|
|
| 15 |
|
| 16 |
+
A compact automatic photo colouriser with **3,994,676 learned parameters in total**, including the complete deployed MobileNetV3 encoder. It predicts plausible colours from grayscale photographs. No teacher, critic, external semantic model, retrieval service or ensemble is needed at inference.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 17 |
|
| 18 |
+
## Release contents
|
|
|
|
|
|
|
| 19 |
|
| 20 |
+
- `model.safetensors` and `config.json`: the selected checkpoint.
|
| 21 |
+
- `semantic_model.py`: the complete architecture and strict checkpoint loader.
|
| 22 |
+
- `inference.py`: Python API and command-line image colourisation.
|
| 23 |
+
- `colorizer.onnx`: equivalent fixed 256×256 network graph, with float Lab `ab` output.
|
| 24 |
+
- `app.py` and `requirements-space.txt`: the tested Gradio / ZeroGPU application.
|
| 25 |
+
- `RELEASE_REPORT.md`, `QA.json` and `SHA256SUMS.json`: selection evidence, runtime checks and file hashes.
|
| 26 |
|
| 27 |
+
## Use
|
|
|
|
|
|
|
|
|
|
| 28 |
|
| 29 |
+
Download this repository, then:
|
| 30 |
|
| 31 |
```bash
|
| 32 |
+
pip install -r requirements.txt
|
| 33 |
+
python inference.py input.jpg output.png --model . --device cpu
|
| 34 |
```
|
| 35 |
|
| 36 |
+
For CUDA, use `--device cuda`. Python API:
|
| 37 |
+
|
| 38 |
```python
|
| 39 |
from PIL import Image
|
| 40 |
+
from inference import load_colorizer, colorize
|
| 41 |
+
|
| 42 |
+
model = load_colorizer('.', 'cpu')
|
| 43 |
+
output = colorize(model, Image.open('input.jpg'))
|
| 44 |
+
output.save('output.png')
|
| 45 |
```
|
| 46 |
|
| 47 |
+
Default processing uses a 256-pixel maximum network side and retains aspect ratio. The output keeps the input resolution, orientation and alpha, up to 12 megapixels. Original Lab lightness is retained; colour is upsampled with a lightness-guided local linear model and compressed into the sRGB gamut. Quantisation can cause small lightness differences.
|
|
|
|
|
|
|
| 48 |
|
| 49 |
+
`saturation=1.0` is the default. Values between 0 and 1.5 are supported. Smoothing radius defaults to 8 at network resolution; 4 and 16 are available for gentler or stronger smoothing.
|
| 50 |
|
| 51 |
+
## Architecture and training
|
| 52 |
+
|
| 53 |
+
MobileNetV3 Large feature encoder, 128-channel feature pyramid, 16 colour queries and two attention blocks. The decoder produces a shared palette and spatial assignment masks with a bounded local residual. The ImageNet classifier is discarded, and every remaining learned parameter is included in the count above. There are 5,324 parameters of headroom below the strict four-million limit.
|
| 54 |
+
|
| 55 |
+
Training uses 16,230 filtered photographs from pinned Imagenette and COCO parquet files, with ImageNet pretrained encoder features and DDColor artistic targets. The final selection and any fine-tuning are documented in `RELEASE_REPORT.md`. Data revisions, training scripts and historical experiments are retained under `experiments/`.
|
| 56 |
+
|
| 57 |
+
Teacher source and checkpoint references:
|
| 58 |
+
|
| 59 |
+
- [DDColor](https://arxiv.org/abs/2212.11613), artistic checkpoint revision `aa10f72fffc89a6658e37b48556050b4d9a26f63`.
|
| 60 |
+
- [TorchVision MobileNetV3 Large](https://docs.pytorch.org/vision/0.21/models/generated/torchvision.models.mobilenet_v3_large.html), ImageNet V2 encoder initialization.
|
| 61 |
+
- [PalGAN](https://arxiv.org/abs/2210.11204) informed palette and realism experiments; this implementation is not a reproduction.
|
| 62 |
+
|
| 63 |
+
## Intended use and limits
|
| 64 |
+
|
| 65 |
+
Intended for adding plausible colour to ordinary photographs and consumer photo applications. A grayscale image can admit several equally plausible colours. Outputs must not be presented as recovered historical fact.
|
| 66 |
+
|
| 67 |
+
Known limitations include muted or warm/sepia colour choices, incorrect clothing and object hues, residual colour bleeding around small objects, and poor results on unusual surfaces or scenes. Severe scan damage, astronomical images and illustrations are outside the principal evaluation domain. The model does not repair scratches or reconstruct lost detail. Surface consistency is not the same as assigning a uniform colour to an entire patterned object.
|
| 68 |
+
|
| 69 |
+
Metrics against an original colour image are auxiliary because that original may not be inferable. The report includes visual review and colour-retention diagnostics. Upstream pretraining overlap is possible; the evaluation is not a guarantee of unseen content or universal quality. No claim is made that further improvements below four million parameters are impossible.
|
| 70 |
+
|
| 71 |
+
## Migration from the old U-Net
|
| 72 |
|
| 73 |
+
This release changes the architecture. The old U-Net class and colour-bin decoder cannot load these weights. Use `semantic_model.py` and `inference.py` together with the new checkpoint. The network directly returns Lab `ab`; do not apply the old bin softmax or temperature decoder. Prior main commits and the user-managed `stable` branch remain available. Old root-level training and ONNX helpers are archived under `legacy/v2/` to avoid silently mixing architectures.
|
|
|
|
|
|
|
|
|
|
| 74 |
|
| 75 |
+
## ONNX
|
| 76 |
|
| 77 |
+
The ONNX graph accepts float32 `L` shaped `[1,1,256,256]`, normalized as `L_lab / 50 - 1`, and returns float32 Lab `ab` shaped `[1,2,256,256]`. It contains only the compact network. Image loading, resizing, guided upsampling and gamut compression are handled by the Python pipeline, not embedded in the graph. See `colorizer.json` for its contract. The fixed-size export is numerically compared with PyTorch before release.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
RELEASE_MANIFEST.json
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"release": "v3.0.0",
|
| 3 |
+
"selected": "round6 palette step9000",
|
| 4 |
+
"selected_source_revision": "7b10485ee9d205ce2f7b660dfc6a90012d78b1cb",
|
| 5 |
+
"parameters": 3994676,
|
| 6 |
+
"previous_main_commit": "45011cd907b845d44f627db4e387678800261257",
|
| 7 |
+
"previous_space_commit": "ab8faf4771154605d5dbecf084f2a01f32704efb",
|
| 8 |
+
"model_sha256": "ec1f27d74533adc83f7ab3639a091fc4d8738a434dafc7d172c7873c28a9e715",
|
| 9 |
+
"engineering_qa": "QA.json",
|
| 10 |
+
"pipeline_eval": "PIPELINE_EVAL.json",
|
| 11 |
+
"critic_runs_rejected": [
|
| 12 |
+
"realism_low_v2",
|
| 13 |
+
"realism_high_v2"
|
| 14 |
+
],
|
| 15 |
+
"scope": "Final tested release from this project; not a proof of optimality or historical colour truth."
|
| 16 |
+
}
|
RELEASE_REPORT.md
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Final selection — Mini Photo Colorizer v3.0
|
| 2 |
+
|
| 3 |
+
## Decision
|
| 4 |
+
Release the round6 palette checkpoint at step9000 with the new, verified image pipeline. All 3,994,676 learned parameters, including the encoder, are in model.safetensors. No discriminator, teacher, second model or weight ensemble is loaded in production.
|
| 5 |
+
|
| 6 |
+
The final two training runs tested conditional colour critics with strengths0.04 and0.12. Both completed 2,500 fine-tuning updates, with checkpoints saved and verified every500 updates. They produced stronger colours in some images but reintroduced local colour patches. They were rejected after visual review and matched reporting. Two earlier startup attempts failed on a typed optimiser argument before training; that argument was corrected. Every successful run is retained under experiments/final-20260928.
|
| 7 |
+
|
| 8 |
+
## Matched 80-image reporting set
|
| 9 |
+
78 images had appreciable original colour. These are repeated reporting/development images, not an independent unseen benchmark.
|
| 10 |
+
|
| 11 |
+
| Checkpoint | Blotch proxy (lower) | Missed colour (lower) | Colour coverage |
|
| 12 |
+
|---|---:|---:|---:|
|
| 13 |
+
| Selected round6 palette9000 | 0.779 | 0.169 | 0.474 |
|
| 14 |
+
| Gentle colour critic | 1.070 | 0.170 | 0.487 |
|
| 15 |
+
| Strong colour critic | 1.413 | 0.171 | 0.497 |
|
| 16 |
+
|
| 17 |
+
The critics increased the patchiness measure by about37% and81%. On reviewed images, this corresponded to red patches on faces, small coloured areas within otherwise consistent objects, and colour bleeding. More saturation did not make them better releases.
|
| 18 |
+
|
| 19 |
+
Round7's four objective/degradation variants were also rejected: they were visually almost unchanged and did not improve blotches.
|
| 20 |
+
|
| 21 |
+
## What improves over the previously deployed app
|
| 22 |
+
The previous Space used the old v2 U-Net. The release uses pretrained semantic features and a shared palette. In the prior pinned300-image fresh COCO comparison (272 chromatic images), selected round6 lowered the patch-excess proxy from1.714 to0.747 and missed-colour fraction from0.223 to0.159. Those are approximately56% and29% reductions under that evaluation pipeline, not universal guarantees.
|
| 23 |
+
|
| 24 |
+
The production image pipeline additionally retains aspect ratio, uses original-resolution lightness to guide chroma upsampling, preserves EXIF orientation and alpha, checks the image-size limit, compresses out-of-gamut chroma, and reports input errors. See the actual pipeline comparison appended below for its distinct measurements.
|
| 25 |
+
|
| 26 |
+
## Verification
|
| 27 |
+
QA.json records CPU inference on tiny, portrait, landscape and1024×768 images; exact alpha retention; worst observed lightness difference below0.22 Lab L after8-bit quantisation; black/white and saturation-zero behaviour; Gradio function and HTTP calls; and ONNX parity with maximum observed difference0.000912 Lab units. These are engineering tests, not evidence of historical colour accuracy.
|
| 28 |
+
|
| 29 |
+
The Space pins the model release commit and uses the same tested source. ZeroGPU GPU operations are isolated in a decorated function; full-resolution rendering runs on CPU. Separate live Space verification is recorded in LIVE_SPACE_CHECK.json when completed.
|
| 30 |
+
|
| 31 |
+
## Evidence and provenance
|
| 32 |
+
- [Final low-critic training](experiments/final-20260928/realism_low_v2/)
|
| 33 |
+
- [Final high-critic training](experiments/final-20260928/realism_high_v2/)
|
| 34 |
+
- [Round7 review](experiments/round7-20260928/REVIEW.md)
|
| 35 |
+
- [Round6 selected checkpoint and evaluation](experiments/round6-20260928/RESULTS.md)
|
| 36 |
+
- [Actual release pipeline comparisons](experiments/final-20260928/pipeline_eval/)
|
| 37 |
+
|
| 38 |
+
## Limits
|
| 39 |
+
The release may choose muted, warm or incorrect colours and can still show residual bleeding. Real archival evaluation is limited, and two familiar historical examples are not representative of the entire domain. The moon remains an out-of-domain failure. There is no basis to claim that all further improvement below4M parameters is impossible. This is the selected, tested final release from these experiments; it is not a guarantee of perfect colourisation.
|
| 40 |
+
|
| 41 |
+
## Actual production image pipeline comparison
|
| 42 |
+
|
| 43 |
+
{
|
| 44 |
+
"n_total": 80,
|
| 45 |
+
"n_color": 78,
|
| 46 |
+
"summary": {
|
| 47 |
+
"old_space": {
|
| 48 |
+
"ab_error": 16.144625015747852,
|
| 49 |
+
"patch_excess": 1.8169478370020022,
|
| 50 |
+
"color_coverage": 0.47298392271384215,
|
| 51 |
+
"missed_color": 0.24084079671968467
|
| 52 |
+
},
|
| 53 |
+
"release": {
|
| 54 |
+
"ab_error": 14.243927399317423,
|
| 55 |
+
"patch_excess": 0.6932277647444071,
|
| 56 |
+
"color_coverage": 0.479247068747496,
|
| 57 |
+
"missed_color": 0.1663338435453805
|
| 58 |
+
}
|
| 59 |
+
},
|
| 60 |
+
"median_pipeline_seconds_L4": 0.06851194500001156
|
| 61 |
+
}
|
| 62 |
+
|
| 63 |
+
This comparison preserves aspect ratio and original output resolution before measuring the resized results. Median pipeline time excludes ZeroGPU queue/allocation and network transfer.
|
SHA256SUMS.json
CHANGED
|
@@ -1,44 +1,23 @@
|
|
| 1 |
{
|
| 2 |
-
"DEPLOYMENT.md": "
|
| 3 |
-
"
|
| 4 |
-
"
|
| 5 |
-
"
|
| 6 |
-
"
|
| 7 |
-
"
|
| 8 |
-
"
|
| 9 |
-
"
|
| 10 |
-
"
|
| 11 |
-
"
|
| 12 |
-
"
|
| 13 |
-
"
|
| 14 |
-
"
|
| 15 |
-
"
|
| 16 |
-
"
|
| 17 |
-
"
|
| 18 |
-
"
|
| 19 |
-
"
|
| 20 |
-
"
|
| 21 |
-
"
|
| 22 |
-
"
|
| 23 |
-
"reports/round2/final_test200.json": "d525d24e8706ff3f7ee3478c1ab95b8d5875d0d8242f6389dc29b9416449ce63",
|
| 24 |
-
"reports/round2/final_test_comparison.png": "9681e45700aa5def0f69f67a49a45cadfddb06c1dc4b321995e09641eebf7f73",
|
| 25 |
-
"reports/round2/final_visual.json": "0ca89a7d8017046c085c7983374706df0c1970284d95a8519cf16bf4d65065a8",
|
| 26 |
-
"reports/round2/final_visual.png": "df2b3c271150059ab2f0d0a10f7fe889f1d3878c6096e14e0fd5df13edd7022a",
|
| 27 |
-
"reports/round2/hub_refs.json": "e714d59b825a333abbea48270353a8dffd5c1b1b8b629028a729fac063f7cbd8",
|
| 28 |
-
"reports/round2/jobs.json": "aa8dc41c57ae6ee41b5e51e8aff09055dafb536424413240cbeff3fcbabcbacc",
|
| 29 |
-
"reports/round2/manifest.json": "adaf3a771a4c1cd3e1c58e6d74eae2a164e1d00ddbca8232067711111caa62ea",
|
| 30 |
-
"reports/round2/onnx_benchmark.json": "2afbcbd29f9619dcc0cad1954f8fe2af3fdd11b94235b53883e8e0db53ef67f0",
|
| 31 |
-
"reports/round2/onnx_export.json": "9f1db0072e91747bd6e9d6de1e2387440a4a2e0669554e499bbf29288a9758cd",
|
| 32 |
-
"reports/round2/selection_protocol.md": "c297f6141a6c5419c2d6bba4ea1ecb5ce7295a78f9d16abec9e38224d5000a7b",
|
| 33 |
-
"reports/round2/tests-final.txt": "60e907edbabdfcc838d135fbd18b7733ab87c401bbf608c734f589abcc4687f8",
|
| 34 |
-
"reports/round2/weight_selection.json": "5ca680f16fab8fedb1ca1a14f59a127ee5d5a71e21fb3560f78417b530201df0",
|
| 35 |
-
"requirements-onnx.txt": "c243944cabecd501038b8027dc5fd08f6d2ce74bcc47cc121f5c8df1b8c4445c",
|
| 36 |
-
"requirements.txt": "a5b6a1804d983178099da00bad2f92a4196009384e431119cc73c5433d249622",
|
| 37 |
-
"spatial.py": "d13abe399ef049e21a6459a7003461afaae0c00e0c560262c6cf375de4c9884a",
|
| 38 |
-
"training/color_statistics.npz": "1d14920885324137169e63f908a413ebf083e8db1e604d2333847fd2094f36a6",
|
| 39 |
-
"training/environment.txt": "ee1e54f3b86a977f74fbc0283ce0cfd2fcf54cc4c8ec4b9096634746eadcc18b",
|
| 40 |
-
"training/history.json": "714ff0c8364e33e75cff21b6abe94f78db6c4536faf016ad1f591eb0ebb7dc72",
|
| 41 |
-
"training/run_config.json": "6cdd4da9e0ae293e8f1895dd03d28bfb1f45c3abf19fc147cd8c18e6dfd36559",
|
| 42 |
-
"training/split_manifest.json": "dc5901733751e8585f959918e46ce75b520c6e241a79d26ab9b3df943a12ac38",
|
| 43 |
-
"upload_main.py": "d59fe5ebfc27f6051b7e7c6d8ad66b23092fa61b3db9fc87b33bff0fad2b483a"
|
| 44 |
}
|
|
|
|
| 1 |
{
|
| 2 |
+
"DEPLOYMENT.md": "201879644b48093363f025589641bc2a264ec8097512b4b4c30b621f3b9d9ee6",
|
| 3 |
+
"PIPELINE_EVAL.json": "595be88704d580c01a3c4dd642409b3dc5dd25e86ca1c7eb40a696605f181a19",
|
| 4 |
+
"QA.json": "b249b1d49045e31b0bf85f9e01515b91a72c4dd973963751c30d14272d262b90",
|
| 5 |
+
"README.md": "5ade5fd9022a03937b91fbd3b0fa653ae93c068034b54b35677c3db47a9ccc59",
|
| 6 |
+
"RELEASE_MANIFEST.json": "872eebfb322c8990b5e6ac493e4e4068b87f8daac0ddf219b8b55299514ca03c",
|
| 7 |
+
"RELEASE_REPORT.md": "69677ec5e13f5c1249f59b01c31ed1b0f1588bddc216bce9167b5d31423a5574",
|
| 8 |
+
"app.py": "58be79b62642c2f3393860791a1a7b39d8f2e83ac79caf6545839124ec787404",
|
| 9 |
+
"colorization_project_log.md": "699238b4078fca93b15f4f2d951e439651502338dac4bf3a5b5a6d0b0449de21",
|
| 10 |
+
"colorize_image.py": "9a85b7633bdf8a0d63f740ae69ed76644bb2087da4b21f8c1e01ca804cd4e6b2",
|
| 11 |
+
"colorize_onnx.py": "cc106f066878a7372f4260279620238d46a3841baf09fbbe954f61a684199d05",
|
| 12 |
+
"colorizer.json": "7d444d5f560b2ea6a070f73750f75f66aa2c61d3dc21a8e4300ed5f5196da179",
|
| 13 |
+
"colorizer.onnx": "911fb777328fcfbeeb89cc25dca70092cf2de4686ede1edd44a8ccebc89ea341",
|
| 14 |
+
"config.json": "eaeb49b39f9116bfed0d954e81852dc93476298752787dba88a63c0eec97ccec",
|
| 15 |
+
"export_onnx.py": "f46a413c2b1b2a6e130cb7d2ad8d6d69c16a16955bf830efdc4c04c30d47ca60",
|
| 16 |
+
"inference.py": "0ac1f382205b42cef0afaa9173017fa660e6859194a65a61b9b9052c437cd476",
|
| 17 |
+
"model.py": "31628744fb5d362d91342063c90eb38b2b97fda8c91e303e4a58b6f388f53c55",
|
| 18 |
+
"model.safetensors": "ec1f27d74533adc83f7ab3639a091fc4d8738a434dafc7d172c7873c28a9e715",
|
| 19 |
+
"requirements-onnx.txt": "9bcca098a9da63b31fb9d803dfd7f99b753ad125ee9bce3a5bbe52e368739e11",
|
| 20 |
+
"requirements-space.txt": "4c7f918aadc4afb31ec6808131570f9978cf0bcf9e2af562e93adc749e3245cf",
|
| 21 |
+
"requirements.txt": "c855eb70020dc89f71fd38e44a0be2cfb80f2fe3d442d5ba45c91398c336f83f",
|
| 22 |
+
"semantic_model.py": "b019358cbcaa214743cbd244d20eb6e59eeff0ab5264093ec0927be8529b25a2"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 23 |
}
|
app.py
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Gradio and ZeroGPU entry point. The model revision is pinned at release."""
|
| 2 |
+
import os
|
| 3 |
+
from pathlib import Path
|
| 4 |
+
import spaces
|
| 5 |
+
import torch
|
| 6 |
+
import gradio as gr
|
| 7 |
+
from huggingface_hub import snapshot_download
|
| 8 |
+
from inference import load_colorizer, prepare, chroma_coefficients, render
|
| 9 |
+
|
| 10 |
+
REPO=os.getenv('MODEL_ID','User-2468/mini-unet-colorizer')
|
| 11 |
+
REV=os.getenv('MODEL_REVISION','v3.0.0')
|
| 12 |
+
CPU=os.getenv('ZEROGPU_CPU_TEST')=='1'
|
| 13 |
+
DEVICE='cpu' if CPU else 'cuda'
|
| 14 |
+
if CPU:torch.set_num_threads(min(4,os.cpu_count() or 1))
|
| 15 |
+
path=Path(REPO)
|
| 16 |
+
if not path.is_dir():path=Path(snapshot_download(REPO,revision=REV,allow_patterns=['model.safetensors','config.json']))
|
| 17 |
+
MODEL=load_colorizer(path,DEVICE)
|
| 18 |
+
|
| 19 |
+
@spaces.GPU(duration=20)
|
| 20 |
+
def infer(small,radius):
|
| 21 |
+
return chroma_coefficients(MODEL,small,int(radius))
|
| 22 |
+
|
| 23 |
+
def run(image,strength,smoothing):
|
| 24 |
+
if image is None:raise gr.Error('Upload a photograph first.')
|
| 25 |
+
radius={'Balanced':8,'Gentle':4,'Strong':16}[smoothing]
|
| 26 |
+
try:
|
| 27 |
+
light,alpha,small=prepare(image,256)
|
| 28 |
+
coefficients=infer(small,radius)
|
| 29 |
+
result=render(light,alpha,coefficients,strength)
|
| 30 |
+
except (ValueError,RuntimeError,TypeError) as exc:
|
| 31 |
+
raise gr.Error(str(exc)) from exc
|
| 32 |
+
return result
|
| 33 |
+
|
| 34 |
+
with gr.Blocks(title='Mini Photo Colorizer') as demo:
|
| 35 |
+
gr.Markdown('# Mini Photo Colorizer\nAdd plausible colour to a black-and-white photograph. Original colours may be unknowable.')
|
| 36 |
+
with gr.Row():
|
| 37 |
+
source=gr.Image(type='pil',image_mode='RGBA',label='Original photograph',sources=['upload','clipboard'],height=440)
|
| 38 |
+
result=gr.Image(type='pil',label='Colourised photograph',format='png',height=440)
|
| 39 |
+
with gr.Row():
|
| 40 |
+
strength=gr.Slider(0,1.5,value=1.,step=.05,label='Colour strength')
|
| 41 |
+
smoothing=gr.Radio(['Gentle','Balanced','Strong'],value='Balanced',label='Colour smoothing')
|
| 42 |
+
button=gr.Button('Colourise',variant='primary')
|
| 43 |
+
button.click(run,[source,strength,smoothing],result,api_name='colorize',concurrency_limit=1)
|
| 44 |
+
gr.ClearButton([source,result])
|
| 45 |
+
gr.Markdown('3.995 million parameters · Full-resolution output, up to 12 megapixels · PNG download\n\nBest suited to ordinary photographs. Colours can be muted or inaccurate on unusual scenes, tiny objects and heavily damaged scans.')
|
| 46 |
+
demo.queue(default_concurrency_limit=1,max_size=12)
|
| 47 |
+
if __name__=='__main__':demo.launch()
|
colorization_project_log.md
CHANGED
|
@@ -249,3 +249,8 @@ All GPU weights were recovered through checksum-verified artifact transport;
|
|
| 249 |
best/last weights and experiment records are retained. Main and stable remain
|
| 250 |
unchanged. The saved release includes a guarded uploader targeting main.
|
| 251 |
The current candidate is release-v2; release/ preserves the earlier repair.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 249 |
best/last weights and experiment records are retained. Main and stable remain
|
| 250 |
unchanged. The saved release includes a guarded uploader targeting main.
|
| 251 |
The current candidate is release-v2; release/ preserves the earlier repair.
|
| 252 |
+
|
| 253 |
+
|
| 254 |
+
## Final release — 28 September 2026
|
| 255 |
+
|
| 256 |
+
Selected round6 palette9000 after rejecting round7 and final critic variants. All3,994,676 parameters include the encoder. Root files now use SemanticColorizer, with aspect-preserving guided colour upsampling, gamut compression, CPU/CUDA support, fixed256 ONNX and Gradio/ZeroGPU app. See RELEASE_REPORT.md, RELEASE_MANIFEST.json and QA.json. Stable branch unchanged.
|
colorize_image.py
CHANGED
|
@@ -1,176 +1,2 @@
|
|
| 1 |
-
|
| 2 |
-
|
| 3 |
-
# dependencies = [
|
| 4 |
-
# "torch>=2.3",
|
| 5 |
-
# "torchvision>=0.18",
|
| 6 |
-
# "huggingface_hub>=0.24",
|
| 7 |
-
# "safetensors>=0.4",
|
| 8 |
-
# "scikit-image>=0.22",
|
| 9 |
-
# "pillow>=10.0",
|
| 10 |
-
# "numpy",
|
| 11 |
-
# ]
|
| 12 |
-
# ///
|
| 13 |
-
"""
|
| 14 |
-
Colorize photos with a SmallUNetColorizer checkpoint trained by colorize_train.py
|
| 15 |
-
(classification-head version: predicts a distribution over quantized Lab ab
|
| 16 |
-
bins per pixel, decoded with an annealed mean).
|
| 17 |
-
|
| 18 |
-
Usage:
|
| 19 |
-
uv run colorize_image.py --model User-2468/mini-unet-colorizer photo.jpg
|
| 20 |
-
uv run colorize_image.py --model ./local_checkpoint --temperature 0.2 a.jpg b.jpg
|
| 21 |
-
|
| 22 |
-
--temperature is the main "vividness" knob: lower values weight the decode
|
| 23 |
-
toward the most likely color bin (more saturated, can be a bit blotchy);
|
| 24 |
-
higher values move toward the full expectation over the distribution
|
| 25 |
-
(smoother, but can drift back toward desaturated -- the same hedging effect
|
| 26 |
-
plain regression had). 0.38 (the default) is a reasonable middle ground.
|
| 27 |
-
--saturation-boost is an optional *additional* post-decode multiplier on top
|
| 28 |
-
of that, for further hand-tuning after picking a temperature.
|
| 29 |
-
"""
|
| 30 |
-
import argparse
|
| 31 |
-
from pathlib import Path
|
| 32 |
-
|
| 33 |
-
import numpy as np
|
| 34 |
-
import torch
|
| 35 |
-
import torch.nn as nn
|
| 36 |
-
import torch.nn.functional as F
|
| 37 |
-
from huggingface_hub import PyTorchModelHubMixin
|
| 38 |
-
from PIL import Image
|
| 39 |
-
from skimage.color import lab2rgb, rgb2lab
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
def double_conv(in_ch, out_ch):
|
| 43 |
-
return nn.Sequential(
|
| 44 |
-
nn.Conv2d(in_ch, out_ch, 3, padding=1, bias=False),
|
| 45 |
-
nn.BatchNorm2d(out_ch),
|
| 46 |
-
nn.ReLU(inplace=True),
|
| 47 |
-
nn.Conv2d(out_ch, out_ch, 3, padding=1, bias=False),
|
| 48 |
-
nn.BatchNorm2d(out_ch),
|
| 49 |
-
nn.ReLU(inplace=True),
|
| 50 |
-
)
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
class DilatedContextBlock(nn.Module):
|
| 54 |
-
def __init__(self, channels, mid_ch=96, dilations=(2, 4, 8)):
|
| 55 |
-
super().__init__()
|
| 56 |
-
self.proj_in = nn.Sequential(
|
| 57 |
-
nn.Conv2d(channels, mid_ch, 1, bias=False),
|
| 58 |
-
nn.BatchNorm2d(mid_ch),
|
| 59 |
-
nn.ReLU(inplace=True),
|
| 60 |
-
)
|
| 61 |
-
layers = []
|
| 62 |
-
for d in dilations:
|
| 63 |
-
layers += [
|
| 64 |
-
nn.Conv2d(mid_ch, mid_ch, 3, padding=d, dilation=d, bias=False),
|
| 65 |
-
nn.BatchNorm2d(mid_ch),
|
| 66 |
-
nn.ReLU(inplace=True),
|
| 67 |
-
]
|
| 68 |
-
self.dilated = nn.Sequential(*layers)
|
| 69 |
-
self.proj_out = nn.Sequential(
|
| 70 |
-
nn.Conv2d(mid_ch, channels, 1, bias=False),
|
| 71 |
-
nn.BatchNorm2d(channels),
|
| 72 |
-
)
|
| 73 |
-
self.relu = nn.ReLU(inplace=True)
|
| 74 |
-
|
| 75 |
-
def forward(self, x):
|
| 76 |
-
y = self.proj_in(x)
|
| 77 |
-
y = self.dilated(y)
|
| 78 |
-
y = self.proj_out(y)
|
| 79 |
-
return self.relu(x + y)
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
class SmallUNetColorizer(
|
| 83 |
-
nn.Module,
|
| 84 |
-
PyTorchModelHubMixin,
|
| 85 |
-
pipeline_tag="image-to-image",
|
| 86 |
-
license="apache-2.0",
|
| 87 |
-
tags=["colorization", "unet", "image-to-image", "classification"],
|
| 88 |
-
):
|
| 89 |
-
def __init__(self, bin_centers, in_ch: int = 1, base: int = 44,
|
| 90 |
-
context_mid_ch: int = 96, context_dilations=(2, 4, 8)):
|
| 91 |
-
super().__init__()
|
| 92 |
-
self.in_ch, self.base = in_ch, base
|
| 93 |
-
num_bins = len(bin_centers)
|
| 94 |
-
self.num_bins = num_bins
|
| 95 |
-
self.register_buffer("bin_centers", torch.tensor(bin_centers, dtype=torch.float32))
|
| 96 |
-
|
| 97 |
-
self.enc1 = double_conv(in_ch, base)
|
| 98 |
-
self.enc2 = double_conv(base, base * 2)
|
| 99 |
-
self.enc3 = double_conv(base * 2, base * 4)
|
| 100 |
-
self.enc4 = double_conv(base * 4, base * 8)
|
| 101 |
-
self.pool = nn.MaxPool2d(2)
|
| 102 |
-
self.context = DilatedContextBlock(base * 8, mid_ch=context_mid_ch,
|
| 103 |
-
dilations=tuple(context_dilations))
|
| 104 |
-
self.up3 = nn.ConvTranspose2d(base * 8, base * 4, 2, stride=2)
|
| 105 |
-
self.dec3 = double_conv(base * 8, base * 4)
|
| 106 |
-
self.up2 = nn.ConvTranspose2d(base * 4, base * 2, 2, stride=2)
|
| 107 |
-
self.dec2 = double_conv(base * 4, base * 2)
|
| 108 |
-
self.up1 = nn.ConvTranspose2d(base * 2, base, 2, stride=2)
|
| 109 |
-
self.dec1 = double_conv(base * 2, base)
|
| 110 |
-
self.out_conv = nn.Conv2d(base, num_bins, 1)
|
| 111 |
-
|
| 112 |
-
def forward(self, x):
|
| 113 |
-
e1 = self.enc1(x)
|
| 114 |
-
e2 = self.enc2(self.pool(e1))
|
| 115 |
-
e3 = self.enc3(self.pool(e2))
|
| 116 |
-
e4 = self.context(self.enc4(self.pool(e3)))
|
| 117 |
-
d3 = self.dec3(torch.cat([self.up3(e4), e3], dim=1))
|
| 118 |
-
d2 = self.dec2(torch.cat([self.up2(d3), e2], dim=1))
|
| 119 |
-
d1 = self.dec1(torch.cat([self.up1(d2), e1], dim=1))
|
| 120 |
-
return self.out_conv(d1)
|
| 121 |
-
|
| 122 |
-
def decode(self, logits, temperature: float = 0.38):
|
| 123 |
-
logp = F.log_softmax(logits, dim=1)
|
| 124 |
-
probs_t = F.softmax(logp / temperature, dim=1)
|
| 125 |
-
return torch.einsum("bqhw,qc->bchw", probs_t, self.bin_centers)
|
| 126 |
-
|
| 127 |
-
|
| 128 |
-
def colorize(model, img, size, temperature, saturation_boost, device):
|
| 129 |
-
img = img.convert("RGB").resize((size, size))
|
| 130 |
-
arr = np.asarray(img).astype(np.float32) / 255.0
|
| 131 |
-
lab = rgb2lab(arr).astype(np.float32)
|
| 132 |
-
L = torch.from_numpy(lab[:, :, 0:1] / 50.0 - 1.0).permute(2, 0, 1)[None].to(device)
|
| 133 |
-
|
| 134 |
-
with torch.no_grad():
|
| 135 |
-
logits = model(L)
|
| 136 |
-
ab = model.decode(logits, temperature=temperature)[0].permute(1, 2, 0).cpu().numpy()
|
| 137 |
-
|
| 138 |
-
ab = np.clip(ab * saturation_boost, -128, 127)
|
| 139 |
-
L_out = (L[0, 0].cpu().numpy() + 1.0) * 50.0
|
| 140 |
-
lab_out = np.concatenate([L_out[:, :, None], ab], axis=-1)
|
| 141 |
-
rgb_out = np.clip(lab2rgb(lab_out), 0, 1)
|
| 142 |
-
return Image.fromarray((rgb_out * 255).astype(np.uint8))
|
| 143 |
-
|
| 144 |
-
|
| 145 |
-
def main():
|
| 146 |
-
p = argparse.ArgumentParser(description=__doc__)
|
| 147 |
-
p.add_argument("images", nargs="+", help="Path(s) to input photo(s)")
|
| 148 |
-
p.add_argument("--model", required=True, help="Hub model id or local checkpoint path")
|
| 149 |
-
p.add_argument("--size", type=int, default=256, help="Resize input to this square size")
|
| 150 |
-
p.add_argument("--temperature", type=float, default=0.38,
|
| 151 |
-
help="Annealed-mean decode temperature. Lower = more vivid/mode-like, "
|
| 152 |
-
"higher = smoother but can desaturate again. Try 0.15-0.6.")
|
| 153 |
-
p.add_argument("--saturation-boost", type=float, default=1.0,
|
| 154 |
-
help="Extra multiplier on the decoded ab, applied after temperature. "
|
| 155 |
-
"1.0 = no extra boost.")
|
| 156 |
-
p.add_argument("--output-dir", default="./colorized")
|
| 157 |
-
args = p.parse_args()
|
| 158 |
-
|
| 159 |
-
device = "cuda" if torch.cuda.is_available() else "cpu"
|
| 160 |
-
print(f"Loading {args.model} on {device} ...")
|
| 161 |
-
model = SmallUNetColorizer.from_pretrained(args.model).to(device).eval()
|
| 162 |
-
print(f"({model.num_bins} color bins)")
|
| 163 |
-
|
| 164 |
-
out_dir = Path(args.output_dir)
|
| 165 |
-
out_dir.mkdir(parents=True, exist_ok=True)
|
| 166 |
-
|
| 167 |
-
for path in args.images:
|
| 168 |
-
img = Image.open(path)
|
| 169 |
-
result = colorize(model, img, args.size, args.temperature, args.saturation_boost, device)
|
| 170 |
-
out_path = out_dir / f"{Path(path).stem}_colorized.png"
|
| 171 |
-
result.save(out_path)
|
| 172 |
-
print(f"{path} -> {out_path}")
|
| 173 |
-
|
| 174 |
-
|
| 175 |
-
if __name__ == "__main__":
|
| 176 |
-
main()
|
|
|
|
| 1 |
+
from inference import main
|
| 2 |
+
if __name__ == "__main__": main()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
colorize_onnx.py
CHANGED
|
@@ -1,32 +1,24 @@
|
|
| 1 |
-
"""
|
|
|
|
|
|
|
|
|
|
| 2 |
import argparse
|
| 3 |
-
from pathlib import Path
|
| 4 |
import numpy as np
|
| 5 |
import onnxruntime as ort
|
| 6 |
from PIL import Image,ImageOps
|
| 7 |
from skimage.color import rgb2lab,lab2rgb
|
| 8 |
|
| 9 |
-
def
|
| 10 |
-
opts=ort.SessionOptions();opts.intra_op_num_threads=threads;opts.inter_op_num_threads=1
|
| 11 |
-
return ort.InferenceSession(str(path),sess_options=opts,providers=['CPUExecutionProvider'])
|
| 12 |
-
|
| 13 |
-
def colorize(session,image,size=256):
|
| 14 |
-
if size<8:raise ValueError('size must be at least8')
|
| 15 |
image=ImageOps.exif_transpose(image).convert('RGB')
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
ab=
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
return Image.fromarray(np.rint(rgb*255).astype(np.uint8))
|
| 25 |
|
| 26 |
if __name__=='__main__':
|
| 27 |
-
p=argparse.ArgumentParser(
|
| 28 |
-
|
| 29 |
-
a=p.parse_args();session=load_session(a.model,a.threads);out=Path(a.output_dir);out.mkdir(parents=True,exist_ok=True)
|
| 30 |
-
for source in a.images:
|
| 31 |
-
with Image.open(source) as im:result=colorize(session,im,a.size)
|
| 32 |
-
dest=out/(Path(source).stem+'_colorized.png');result.save(dest);print(dest)
|
|
|
|
| 1 |
+
"""Minimal ONNX-only image example; fixed square network input.
|
| 2 |
+
|
| 3 |
+
For aspect-preserving guided upsampling and gamut compression use inference.py.
|
| 4 |
+
"""
|
| 5 |
import argparse
|
|
|
|
| 6 |
import numpy as np
|
| 7 |
import onnxruntime as ort
|
| 8 |
from PIL import Image,ImageOps
|
| 9 |
from skimage.color import rgb2lab,lab2rgb
|
| 10 |
|
| 11 |
+
def colorize(image,model='colorizer.onnx'):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 12 |
image=ImageOps.exif_transpose(image).convert('RGB')
|
| 13 |
+
if image.width*image.height>12_000_000:raise ValueError('Maximum 12 megapixels')
|
| 14 |
+
light=rgb2lab(np.asarray(image,np.float32)/255)[...,0].astype(np.float32)
|
| 15 |
+
resized=np.asarray(Image.fromarray(light).resize((256,256),Image.Resampling.BILINEAR))
|
| 16 |
+
session=ort.InferenceSession(model,providers=['CPUExecutionProvider'])
|
| 17 |
+
ab=session.run(['ab'],{'L':(resized[None,None]/50-1).astype(np.float32)})[0][0]
|
| 18 |
+
ab=np.stack([np.asarray(Image.fromarray(channel).resize(image.size,Image.Resampling.BILINEAR)) for channel in ab],-1)
|
| 19 |
+
rgb=np.clip(lab2rgb(np.concatenate([light[...,None],ab],-1)),0,1)
|
| 20 |
+
return Image.fromarray(np.uint8(np.rint(rgb*255)))
|
|
|
|
| 21 |
|
| 22 |
if __name__=='__main__':
|
| 23 |
+
p=argparse.ArgumentParser();p.add_argument('input');p.add_argument('output');p.add_argument('--model',default='colorizer.onnx');a=p.parse_args()
|
| 24 |
+
colorize(Image.open(a.input),a.model).save(a.output)
|
|
|
|
|
|
|
|
|
|
|
|
colorizer.json
CHANGED
|
@@ -1,54 +1,30 @@
|
|
| 1 |
{
|
| 2 |
-
"
|
| 3 |
-
"
|
| 4 |
-
"
|
| 5 |
-
"
|
| 6 |
-
"temperature": 0.38,
|
| 7 |
"opset": 17,
|
| 8 |
-
"
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
2,
|
| 32 |
-
1,
|
| 33 |
-
64,
|
| 34 |
-
80
|
| 35 |
-
],
|
| 36 |
-
"max_ab_difference": 7.152557373046875e-06,
|
| 37 |
-
"mean_ab_difference": 9.707116532808868e-07
|
| 38 |
-
},
|
| 39 |
-
{
|
| 40 |
-
"shape": [
|
| 41 |
-
1,
|
| 42 |
-
1,
|
| 43 |
-
8,
|
| 44 |
-
9
|
| 45 |
-
],
|
| 46 |
-
"max_ab_difference": 1.5497207641601562e-06,
|
| 47 |
-
"mean_ab_difference": 3.6218099808138504e-07
|
| 48 |
-
}
|
| 49 |
-
],
|
| 50 |
-
"parameters": 3968892,
|
| 51 |
-
"weights_source": "release-v2",
|
| 52 |
-
"runtime": "1.30.0",
|
| 53 |
-
"bytes": 15885757
|
| 54 |
}
|
|
|
|
| 1 |
{
|
| 2 |
+
"release": "v3.0.0",
|
| 3 |
+
"architecture": "SemanticColorizer",
|
| 4 |
+
"parameters": 3994676,
|
| 5 |
+
"format": "onnx",
|
|
|
|
| 6 |
"opset": 17,
|
| 7 |
+
"input": {
|
| 8 |
+
"name": "L",
|
| 9 |
+
"shape": [
|
| 10 |
+
1,
|
| 11 |
+
1,
|
| 12 |
+
256,
|
| 13 |
+
256
|
| 14 |
+
],
|
| 15 |
+
"dtype": "float32",
|
| 16 |
+
"normalization": "Lab L /50 -1"
|
| 17 |
+
},
|
| 18 |
+
"output": {
|
| 19 |
+
"name": "ab",
|
| 20 |
+
"shape": [
|
| 21 |
+
1,
|
| 22 |
+
2,
|
| 23 |
+
256,
|
| 24 |
+
256
|
| 25 |
+
],
|
| 26 |
+
"dtype": "float32",
|
| 27 |
+
"units": "unscaled Lab ab"
|
| 28 |
+
},
|
| 29 |
+
"postprocessing": "See inference.py; no colour bins or temperature decode."
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 30 |
}
|
colorizer.onnx
CHANGED
|
@@ -1,3 +1,3 @@
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
-
oid sha256:
|
| 3 |
-
size
|
|
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:911fb777328fcfbeeb89cc25dca70092cf2de4686ede1edd44a8ccebc89ea341
|
| 3 |
+
size 16039257
|
config.json
CHANGED
|
@@ -1,956 +1,7 @@
|
|
| 1 |
{
|
| 2 |
-
"
|
| 3 |
-
"
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
],
|
| 8 |
-
[
|
| 9 |
-
-75.0,
|
| 10 |
-
35.0
|
| 11 |
-
],
|
| 12 |
-
[
|
| 13 |
-
-75.0,
|
| 14 |
-
45.0
|
| 15 |
-
],
|
| 16 |
-
[
|
| 17 |
-
-75.0,
|
| 18 |
-
55.0
|
| 19 |
-
],
|
| 20 |
-
[
|
| 21 |
-
-75.0,
|
| 22 |
-
65.0
|
| 23 |
-
],
|
| 24 |
-
[
|
| 25 |
-
-75.0,
|
| 26 |
-
75.0
|
| 27 |
-
],
|
| 28 |
-
[
|
| 29 |
-
-65.0,
|
| 30 |
-
25.0
|
| 31 |
-
],
|
| 32 |
-
[
|
| 33 |
-
-65.0,
|
| 34 |
-
35.0
|
| 35 |
-
],
|
| 36 |
-
[
|
| 37 |
-
-65.0,
|
| 38 |
-
45.0
|
| 39 |
-
],
|
| 40 |
-
[
|
| 41 |
-
-65.0,
|
| 42 |
-
55.0
|
| 43 |
-
],
|
| 44 |
-
[
|
| 45 |
-
-65.0,
|
| 46 |
-
65.0
|
| 47 |
-
],
|
| 48 |
-
[
|
| 49 |
-
-65.0,
|
| 50 |
-
75.0
|
| 51 |
-
],
|
| 52 |
-
[
|
| 53 |
-
-55.0,
|
| 54 |
-
5.0
|
| 55 |
-
],
|
| 56 |
-
[
|
| 57 |
-
-55.0,
|
| 58 |
-
15.0
|
| 59 |
-
],
|
| 60 |
-
[
|
| 61 |
-
-55.0,
|
| 62 |
-
25.0
|
| 63 |
-
],
|
| 64 |
-
[
|
| 65 |
-
-55.0,
|
| 66 |
-
35.0
|
| 67 |
-
],
|
| 68 |
-
[
|
| 69 |
-
-55.0,
|
| 70 |
-
45.0
|
| 71 |
-
],
|
| 72 |
-
[
|
| 73 |
-
-55.0,
|
| 74 |
-
55.0
|
| 75 |
-
],
|
| 76 |
-
[
|
| 77 |
-
-55.0,
|
| 78 |
-
65.0
|
| 79 |
-
],
|
| 80 |
-
[
|
| 81 |
-
-55.0,
|
| 82 |
-
75.0
|
| 83 |
-
],
|
| 84 |
-
[
|
| 85 |
-
-45.0,
|
| 86 |
-
-15.0
|
| 87 |
-
],
|
| 88 |
-
[
|
| 89 |
-
-45.0,
|
| 90 |
-
-5.0
|
| 91 |
-
],
|
| 92 |
-
[
|
| 93 |
-
-45.0,
|
| 94 |
-
5.0
|
| 95 |
-
],
|
| 96 |
-
[
|
| 97 |
-
-45.0,
|
| 98 |
-
15.0
|
| 99 |
-
],
|
| 100 |
-
[
|
| 101 |
-
-45.0,
|
| 102 |
-
25.0
|
| 103 |
-
],
|
| 104 |
-
[
|
| 105 |
-
-45.0,
|
| 106 |
-
35.0
|
| 107 |
-
],
|
| 108 |
-
[
|
| 109 |
-
-45.0,
|
| 110 |
-
45.0
|
| 111 |
-
],
|
| 112 |
-
[
|
| 113 |
-
-45.0,
|
| 114 |
-
55.0
|
| 115 |
-
],
|
| 116 |
-
[
|
| 117 |
-
-45.0,
|
| 118 |
-
65.0
|
| 119 |
-
],
|
| 120 |
-
[
|
| 121 |
-
-45.0,
|
| 122 |
-
75.0
|
| 123 |
-
],
|
| 124 |
-
[
|
| 125 |
-
-45.0,
|
| 126 |
-
85.0
|
| 127 |
-
],
|
| 128 |
-
[
|
| 129 |
-
-35.0,
|
| 130 |
-
-35.0
|
| 131 |
-
],
|
| 132 |
-
[
|
| 133 |
-
-35.0,
|
| 134 |
-
-25.0
|
| 135 |
-
],
|
| 136 |
-
[
|
| 137 |
-
-35.0,
|
| 138 |
-
-15.0
|
| 139 |
-
],
|
| 140 |
-
[
|
| 141 |
-
-35.0,
|
| 142 |
-
-5.0
|
| 143 |
-
],
|
| 144 |
-
[
|
| 145 |
-
-35.0,
|
| 146 |
-
5.0
|
| 147 |
-
],
|
| 148 |
-
[
|
| 149 |
-
-35.0,
|
| 150 |
-
15.0
|
| 151 |
-
],
|
| 152 |
-
[
|
| 153 |
-
-35.0,
|
| 154 |
-
25.0
|
| 155 |
-
],
|
| 156 |
-
[
|
| 157 |
-
-35.0,
|
| 158 |
-
35.0
|
| 159 |
-
],
|
| 160 |
-
[
|
| 161 |
-
-35.0,
|
| 162 |
-
45.0
|
| 163 |
-
],
|
| 164 |
-
[
|
| 165 |
-
-35.0,
|
| 166 |
-
55.0
|
| 167 |
-
],
|
| 168 |
-
[
|
| 169 |
-
-35.0,
|
| 170 |
-
65.0
|
| 171 |
-
],
|
| 172 |
-
[
|
| 173 |
-
-35.0,
|
| 174 |
-
75.0
|
| 175 |
-
],
|
| 176 |
-
[
|
| 177 |
-
-35.0,
|
| 178 |
-
85.0
|
| 179 |
-
],
|
| 180 |
-
[
|
| 181 |
-
-35.0,
|
| 182 |
-
95.0
|
| 183 |
-
],
|
| 184 |
-
[
|
| 185 |
-
-25.0,
|
| 186 |
-
-35.0
|
| 187 |
-
],
|
| 188 |
-
[
|
| 189 |
-
-25.0,
|
| 190 |
-
-25.0
|
| 191 |
-
],
|
| 192 |
-
[
|
| 193 |
-
-25.0,
|
| 194 |
-
-15.0
|
| 195 |
-
],
|
| 196 |
-
[
|
| 197 |
-
-25.0,
|
| 198 |
-
-5.0
|
| 199 |
-
],
|
| 200 |
-
[
|
| 201 |
-
-25.0,
|
| 202 |
-
5.0
|
| 203 |
-
],
|
| 204 |
-
[
|
| 205 |
-
-25.0,
|
| 206 |
-
15.0
|
| 207 |
-
],
|
| 208 |
-
[
|
| 209 |
-
-25.0,
|
| 210 |
-
25.0
|
| 211 |
-
],
|
| 212 |
-
[
|
| 213 |
-
-25.0,
|
| 214 |
-
35.0
|
| 215 |
-
],
|
| 216 |
-
[
|
| 217 |
-
-25.0,
|
| 218 |
-
45.0
|
| 219 |
-
],
|
| 220 |
-
[
|
| 221 |
-
-25.0,
|
| 222 |
-
55.0
|
| 223 |
-
],
|
| 224 |
-
[
|
| 225 |
-
-25.0,
|
| 226 |
-
65.0
|
| 227 |
-
],
|
| 228 |
-
[
|
| 229 |
-
-25.0,
|
| 230 |
-
75.0
|
| 231 |
-
],
|
| 232 |
-
[
|
| 233 |
-
-25.0,
|
| 234 |
-
85.0
|
| 235 |
-
],
|
| 236 |
-
[
|
| 237 |
-
-25.0,
|
| 238 |
-
95.0
|
| 239 |
-
],
|
| 240 |
-
[
|
| 241 |
-
-15.0,
|
| 242 |
-
-45.0
|
| 243 |
-
],
|
| 244 |
-
[
|
| 245 |
-
-15.0,
|
| 246 |
-
-35.0
|
| 247 |
-
],
|
| 248 |
-
[
|
| 249 |
-
-15.0,
|
| 250 |
-
-25.0
|
| 251 |
-
],
|
| 252 |
-
[
|
| 253 |
-
-15.0,
|
| 254 |
-
-15.0
|
| 255 |
-
],
|
| 256 |
-
[
|
| 257 |
-
-15.0,
|
| 258 |
-
-5.0
|
| 259 |
-
],
|
| 260 |
-
[
|
| 261 |
-
-15.0,
|
| 262 |
-
5.0
|
| 263 |
-
],
|
| 264 |
-
[
|
| 265 |
-
-15.0,
|
| 266 |
-
15.0
|
| 267 |
-
],
|
| 268 |
-
[
|
| 269 |
-
-15.0,
|
| 270 |
-
25.0
|
| 271 |
-
],
|
| 272 |
-
[
|
| 273 |
-
-15.0,
|
| 274 |
-
35.0
|
| 275 |
-
],
|
| 276 |
-
[
|
| 277 |
-
-15.0,
|
| 278 |
-
45.0
|
| 279 |
-
],
|
| 280 |
-
[
|
| 281 |
-
-15.0,
|
| 282 |
-
55.0
|
| 283 |
-
],
|
| 284 |
-
[
|
| 285 |
-
-15.0,
|
| 286 |
-
65.0
|
| 287 |
-
],
|
| 288 |
-
[
|
| 289 |
-
-15.0,
|
| 290 |
-
75.0
|
| 291 |
-
],
|
| 292 |
-
[
|
| 293 |
-
-15.0,
|
| 294 |
-
85.0
|
| 295 |
-
],
|
| 296 |
-
[
|
| 297 |
-
-15.0,
|
| 298 |
-
95.0
|
| 299 |
-
],
|
| 300 |
-
[
|
| 301 |
-
-5.0,
|
| 302 |
-
-55.0
|
| 303 |
-
],
|
| 304 |
-
[
|
| 305 |
-
-5.0,
|
| 306 |
-
-45.0
|
| 307 |
-
],
|
| 308 |
-
[
|
| 309 |
-
-5.0,
|
| 310 |
-
-35.0
|
| 311 |
-
],
|
| 312 |
-
[
|
| 313 |
-
-5.0,
|
| 314 |
-
-25.0
|
| 315 |
-
],
|
| 316 |
-
[
|
| 317 |
-
-5.0,
|
| 318 |
-
-15.0
|
| 319 |
-
],
|
| 320 |
-
[
|
| 321 |
-
-5.0,
|
| 322 |
-
-5.0
|
| 323 |
-
],
|
| 324 |
-
[
|
| 325 |
-
-5.0,
|
| 326 |
-
5.0
|
| 327 |
-
],
|
| 328 |
-
[
|
| 329 |
-
-5.0,
|
| 330 |
-
15.0
|
| 331 |
-
],
|
| 332 |
-
[
|
| 333 |
-
-5.0,
|
| 334 |
-
25.0
|
| 335 |
-
],
|
| 336 |
-
[
|
| 337 |
-
-5.0,
|
| 338 |
-
35.0
|
| 339 |
-
],
|
| 340 |
-
[
|
| 341 |
-
-5.0,
|
| 342 |
-
45.0
|
| 343 |
-
],
|
| 344 |
-
[
|
| 345 |
-
-5.0,
|
| 346 |
-
55.0
|
| 347 |
-
],
|
| 348 |
-
[
|
| 349 |
-
-5.0,
|
| 350 |
-
65.0
|
| 351 |
-
],
|
| 352 |
-
[
|
| 353 |
-
-5.0,
|
| 354 |
-
75.0
|
| 355 |
-
],
|
| 356 |
-
[
|
| 357 |
-
-5.0,
|
| 358 |
-
85.0
|
| 359 |
-
],
|
| 360 |
-
[
|
| 361 |
-
5.0,
|
| 362 |
-
-65.0
|
| 363 |
-
],
|
| 364 |
-
[
|
| 365 |
-
5.0,
|
| 366 |
-
-55.0
|
| 367 |
-
],
|
| 368 |
-
[
|
| 369 |
-
5.0,
|
| 370 |
-
-45.0
|
| 371 |
-
],
|
| 372 |
-
[
|
| 373 |
-
5.0,
|
| 374 |
-
-35.0
|
| 375 |
-
],
|
| 376 |
-
[
|
| 377 |
-
5.0,
|
| 378 |
-
-25.0
|
| 379 |
-
],
|
| 380 |
-
[
|
| 381 |
-
5.0,
|
| 382 |
-
-15.0
|
| 383 |
-
],
|
| 384 |
-
[
|
| 385 |
-
5.0,
|
| 386 |
-
-5.0
|
| 387 |
-
],
|
| 388 |
-
[
|
| 389 |
-
5.0,
|
| 390 |
-
5.0
|
| 391 |
-
],
|
| 392 |
-
[
|
| 393 |
-
5.0,
|
| 394 |
-
15.0
|
| 395 |
-
],
|
| 396 |
-
[
|
| 397 |
-
5.0,
|
| 398 |
-
25.0
|
| 399 |
-
],
|
| 400 |
-
[
|
| 401 |
-
5.0,
|
| 402 |
-
35.0
|
| 403 |
-
],
|
| 404 |
-
[
|
| 405 |
-
5.0,
|
| 406 |
-
45.0
|
| 407 |
-
],
|
| 408 |
-
[
|
| 409 |
-
5.0,
|
| 410 |
-
55.0
|
| 411 |
-
],
|
| 412 |
-
[
|
| 413 |
-
5.0,
|
| 414 |
-
65.0
|
| 415 |
-
],
|
| 416 |
-
[
|
| 417 |
-
5.0,
|
| 418 |
-
75.0
|
| 419 |
-
],
|
| 420 |
-
[
|
| 421 |
-
5.0,
|
| 422 |
-
85.0
|
| 423 |
-
],
|
| 424 |
-
[
|
| 425 |
-
15.0,
|
| 426 |
-
-75.0
|
| 427 |
-
],
|
| 428 |
-
[
|
| 429 |
-
15.0,
|
| 430 |
-
-65.0
|
| 431 |
-
],
|
| 432 |
-
[
|
| 433 |
-
15.0,
|
| 434 |
-
-55.0
|
| 435 |
-
],
|
| 436 |
-
[
|
| 437 |
-
15.0,
|
| 438 |
-
-45.0
|
| 439 |
-
],
|
| 440 |
-
[
|
| 441 |
-
15.0,
|
| 442 |
-
-35.0
|
| 443 |
-
],
|
| 444 |
-
[
|
| 445 |
-
15.0,
|
| 446 |
-
-25.0
|
| 447 |
-
],
|
| 448 |
-
[
|
| 449 |
-
15.0,
|
| 450 |
-
-15.0
|
| 451 |
-
],
|
| 452 |
-
[
|
| 453 |
-
15.0,
|
| 454 |
-
-5.0
|
| 455 |
-
],
|
| 456 |
-
[
|
| 457 |
-
15.0,
|
| 458 |
-
5.0
|
| 459 |
-
],
|
| 460 |
-
[
|
| 461 |
-
15.0,
|
| 462 |
-
15.0
|
| 463 |
-
],
|
| 464 |
-
[
|
| 465 |
-
15.0,
|
| 466 |
-
25.0
|
| 467 |
-
],
|
| 468 |
-
[
|
| 469 |
-
15.0,
|
| 470 |
-
35.0
|
| 471 |
-
],
|
| 472 |
-
[
|
| 473 |
-
15.0,
|
| 474 |
-
45.0
|
| 475 |
-
],
|
| 476 |
-
[
|
| 477 |
-
15.0,
|
| 478 |
-
55.0
|
| 479 |
-
],
|
| 480 |
-
[
|
| 481 |
-
15.0,
|
| 482 |
-
65.0
|
| 483 |
-
],
|
| 484 |
-
[
|
| 485 |
-
15.0,
|
| 486 |
-
75.0
|
| 487 |
-
],
|
| 488 |
-
[
|
| 489 |
-
15.0,
|
| 490 |
-
85.0
|
| 491 |
-
],
|
| 492 |
-
[
|
| 493 |
-
25.0,
|
| 494 |
-
-75.0
|
| 495 |
-
],
|
| 496 |
-
[
|
| 497 |
-
25.0,
|
| 498 |
-
-65.0
|
| 499 |
-
],
|
| 500 |
-
[
|
| 501 |
-
25.0,
|
| 502 |
-
-55.0
|
| 503 |
-
],
|
| 504 |
-
[
|
| 505 |
-
25.0,
|
| 506 |
-
-45.0
|
| 507 |
-
],
|
| 508 |
-
[
|
| 509 |
-
25.0,
|
| 510 |
-
-35.0
|
| 511 |
-
],
|
| 512 |
-
[
|
| 513 |
-
25.0,
|
| 514 |
-
-25.0
|
| 515 |
-
],
|
| 516 |
-
[
|
| 517 |
-
25.0,
|
| 518 |
-
-15.0
|
| 519 |
-
],
|
| 520 |
-
[
|
| 521 |
-
25.0,
|
| 522 |
-
-5.0
|
| 523 |
-
],
|
| 524 |
-
[
|
| 525 |
-
25.0,
|
| 526 |
-
5.0
|
| 527 |
-
],
|
| 528 |
-
[
|
| 529 |
-
25.0,
|
| 530 |
-
15.0
|
| 531 |
-
],
|
| 532 |
-
[
|
| 533 |
-
25.0,
|
| 534 |
-
25.0
|
| 535 |
-
],
|
| 536 |
-
[
|
| 537 |
-
25.0,
|
| 538 |
-
35.0
|
| 539 |
-
],
|
| 540 |
-
[
|
| 541 |
-
25.0,
|
| 542 |
-
45.0
|
| 543 |
-
],
|
| 544 |
-
[
|
| 545 |
-
25.0,
|
| 546 |
-
55.0
|
| 547 |
-
],
|
| 548 |
-
[
|
| 549 |
-
25.0,
|
| 550 |
-
65.0
|
| 551 |
-
],
|
| 552 |
-
[
|
| 553 |
-
25.0,
|
| 554 |
-
75.0
|
| 555 |
-
],
|
| 556 |
-
[
|
| 557 |
-
25.0,
|
| 558 |
-
85.0
|
| 559 |
-
],
|
| 560 |
-
[
|
| 561 |
-
35.0,
|
| 562 |
-
-85.0
|
| 563 |
-
],
|
| 564 |
-
[
|
| 565 |
-
35.0,
|
| 566 |
-
-75.0
|
| 567 |
-
],
|
| 568 |
-
[
|
| 569 |
-
35.0,
|
| 570 |
-
-65.0
|
| 571 |
-
],
|
| 572 |
-
[
|
| 573 |
-
35.0,
|
| 574 |
-
-55.0
|
| 575 |
-
],
|
| 576 |
-
[
|
| 577 |
-
35.0,
|
| 578 |
-
-45.0
|
| 579 |
-
],
|
| 580 |
-
[
|
| 581 |
-
35.0,
|
| 582 |
-
-35.0
|
| 583 |
-
],
|
| 584 |
-
[
|
| 585 |
-
35.0,
|
| 586 |
-
-25.0
|
| 587 |
-
],
|
| 588 |
-
[
|
| 589 |
-
35.0,
|
| 590 |
-
-15.0
|
| 591 |
-
],
|
| 592 |
-
[
|
| 593 |
-
35.0,
|
| 594 |
-
-5.0
|
| 595 |
-
],
|
| 596 |
-
[
|
| 597 |
-
35.0,
|
| 598 |
-
5.0
|
| 599 |
-
],
|
| 600 |
-
[
|
| 601 |
-
35.0,
|
| 602 |
-
15.0
|
| 603 |
-
],
|
| 604 |
-
[
|
| 605 |
-
35.0,
|
| 606 |
-
25.0
|
| 607 |
-
],
|
| 608 |
-
[
|
| 609 |
-
35.0,
|
| 610 |
-
35.0
|
| 611 |
-
],
|
| 612 |
-
[
|
| 613 |
-
35.0,
|
| 614 |
-
45.0
|
| 615 |
-
],
|
| 616 |
-
[
|
| 617 |
-
35.0,
|
| 618 |
-
55.0
|
| 619 |
-
],
|
| 620 |
-
[
|
| 621 |
-
35.0,
|
| 622 |
-
65.0
|
| 623 |
-
],
|
| 624 |
-
[
|
| 625 |
-
35.0,
|
| 626 |
-
75.0
|
| 627 |
-
],
|
| 628 |
-
[
|
| 629 |
-
45.0,
|
| 630 |
-
-95.0
|
| 631 |
-
],
|
| 632 |
-
[
|
| 633 |
-
45.0,
|
| 634 |
-
-85.0
|
| 635 |
-
],
|
| 636 |
-
[
|
| 637 |
-
45.0,
|
| 638 |
-
-75.0
|
| 639 |
-
],
|
| 640 |
-
[
|
| 641 |
-
45.0,
|
| 642 |
-
-65.0
|
| 643 |
-
],
|
| 644 |
-
[
|
| 645 |
-
45.0,
|
| 646 |
-
-55.0
|
| 647 |
-
],
|
| 648 |
-
[
|
| 649 |
-
45.0,
|
| 650 |
-
-45.0
|
| 651 |
-
],
|
| 652 |
-
[
|
| 653 |
-
45.0,
|
| 654 |
-
-35.0
|
| 655 |
-
],
|
| 656 |
-
[
|
| 657 |
-
45.0,
|
| 658 |
-
-25.0
|
| 659 |
-
],
|
| 660 |
-
[
|
| 661 |
-
45.0,
|
| 662 |
-
-15.0
|
| 663 |
-
],
|
| 664 |
-
[
|
| 665 |
-
45.0,
|
| 666 |
-
-5.0
|
| 667 |
-
],
|
| 668 |
-
[
|
| 669 |
-
45.0,
|
| 670 |
-
5.0
|
| 671 |
-
],
|
| 672 |
-
[
|
| 673 |
-
45.0,
|
| 674 |
-
15.0
|
| 675 |
-
],
|
| 676 |
-
[
|
| 677 |
-
45.0,
|
| 678 |
-
25.0
|
| 679 |
-
],
|
| 680 |
-
[
|
| 681 |
-
45.0,
|
| 682 |
-
35.0
|
| 683 |
-
],
|
| 684 |
-
[
|
| 685 |
-
45.0,
|
| 686 |
-
45.0
|
| 687 |
-
],
|
| 688 |
-
[
|
| 689 |
-
45.0,
|
| 690 |
-
55.0
|
| 691 |
-
],
|
| 692 |
-
[
|
| 693 |
-
45.0,
|
| 694 |
-
65.0
|
| 695 |
-
],
|
| 696 |
-
[
|
| 697 |
-
45.0,
|
| 698 |
-
75.0
|
| 699 |
-
],
|
| 700 |
-
[
|
| 701 |
-
55.0,
|
| 702 |
-
-95.0
|
| 703 |
-
],
|
| 704 |
-
[
|
| 705 |
-
55.0,
|
| 706 |
-
-85.0
|
| 707 |
-
],
|
| 708 |
-
[
|
| 709 |
-
55.0,
|
| 710 |
-
-75.0
|
| 711 |
-
],
|
| 712 |
-
[
|
| 713 |
-
55.0,
|
| 714 |
-
-65.0
|
| 715 |
-
],
|
| 716 |
-
[
|
| 717 |
-
55.0,
|
| 718 |
-
-55.0
|
| 719 |
-
],
|
| 720 |
-
[
|
| 721 |
-
55.0,
|
| 722 |
-
-45.0
|
| 723 |
-
],
|
| 724 |
-
[
|
| 725 |
-
55.0,
|
| 726 |
-
-35.0
|
| 727 |
-
],
|
| 728 |
-
[
|
| 729 |
-
55.0,
|
| 730 |
-
-25.0
|
| 731 |
-
],
|
| 732 |
-
[
|
| 733 |
-
55.0,
|
| 734 |
-
-15.0
|
| 735 |
-
],
|
| 736 |
-
[
|
| 737 |
-
55.0,
|
| 738 |
-
-5.0
|
| 739 |
-
],
|
| 740 |
-
[
|
| 741 |
-
55.0,
|
| 742 |
-
5.0
|
| 743 |
-
],
|
| 744 |
-
[
|
| 745 |
-
55.0,
|
| 746 |
-
15.0
|
| 747 |
-
],
|
| 748 |
-
[
|
| 749 |
-
55.0,
|
| 750 |
-
25.0
|
| 751 |
-
],
|
| 752 |
-
[
|
| 753 |
-
55.0,
|
| 754 |
-
35.0
|
| 755 |
-
],
|
| 756 |
-
[
|
| 757 |
-
55.0,
|
| 758 |
-
45.0
|
| 759 |
-
],
|
| 760 |
-
[
|
| 761 |
-
55.0,
|
| 762 |
-
55.0
|
| 763 |
-
],
|
| 764 |
-
[
|
| 765 |
-
55.0,
|
| 766 |
-
65.0
|
| 767 |
-
],
|
| 768 |
-
[
|
| 769 |
-
55.0,
|
| 770 |
-
75.0
|
| 771 |
-
],
|
| 772 |
-
[
|
| 773 |
-
65.0,
|
| 774 |
-
-105.0
|
| 775 |
-
],
|
| 776 |
-
[
|
| 777 |
-
65.0,
|
| 778 |
-
-95.0
|
| 779 |
-
],
|
| 780 |
-
[
|
| 781 |
-
65.0,
|
| 782 |
-
-85.0
|
| 783 |
-
],
|
| 784 |
-
[
|
| 785 |
-
65.0,
|
| 786 |
-
-75.0
|
| 787 |
-
],
|
| 788 |
-
[
|
| 789 |
-
65.0,
|
| 790 |
-
-65.0
|
| 791 |
-
],
|
| 792 |
-
[
|
| 793 |
-
65.0,
|
| 794 |
-
-55.0
|
| 795 |
-
],
|
| 796 |
-
[
|
| 797 |
-
65.0,
|
| 798 |
-
-45.0
|
| 799 |
-
],
|
| 800 |
-
[
|
| 801 |
-
65.0,
|
| 802 |
-
-35.0
|
| 803 |
-
],
|
| 804 |
-
[
|
| 805 |
-
65.0,
|
| 806 |
-
-25.0
|
| 807 |
-
],
|
| 808 |
-
[
|
| 809 |
-
65.0,
|
| 810 |
-
-15.0
|
| 811 |
-
],
|
| 812 |
-
[
|
| 813 |
-
65.0,
|
| 814 |
-
-5.0
|
| 815 |
-
],
|
| 816 |
-
[
|
| 817 |
-
65.0,
|
| 818 |
-
5.0
|
| 819 |
-
],
|
| 820 |
-
[
|
| 821 |
-
65.0,
|
| 822 |
-
15.0
|
| 823 |
-
],
|
| 824 |
-
[
|
| 825 |
-
65.0,
|
| 826 |
-
25.0
|
| 827 |
-
],
|
| 828 |
-
[
|
| 829 |
-
65.0,
|
| 830 |
-
35.0
|
| 831 |
-
],
|
| 832 |
-
[
|
| 833 |
-
65.0,
|
| 834 |
-
45.0
|
| 835 |
-
],
|
| 836 |
-
[
|
| 837 |
-
65.0,
|
| 838 |
-
55.0
|
| 839 |
-
],
|
| 840 |
-
[
|
| 841 |
-
65.0,
|
| 842 |
-
65.0
|
| 843 |
-
],
|
| 844 |
-
[
|
| 845 |
-
75.0,
|
| 846 |
-
-105.0
|
| 847 |
-
],
|
| 848 |
-
[
|
| 849 |
-
75.0,
|
| 850 |
-
-95.0
|
| 851 |
-
],
|
| 852 |
-
[
|
| 853 |
-
75.0,
|
| 854 |
-
-45.0
|
| 855 |
-
],
|
| 856 |
-
[
|
| 857 |
-
75.0,
|
| 858 |
-
-35.0
|
| 859 |
-
],
|
| 860 |
-
[
|
| 861 |
-
75.0,
|
| 862 |
-
-25.0
|
| 863 |
-
],
|
| 864 |
-
[
|
| 865 |
-
75.0,
|
| 866 |
-
-15.0
|
| 867 |
-
],
|
| 868 |
-
[
|
| 869 |
-
75.0,
|
| 870 |
-
-5.0
|
| 871 |
-
],
|
| 872 |
-
[
|
| 873 |
-
75.0,
|
| 874 |
-
5.0
|
| 875 |
-
],
|
| 876 |
-
[
|
| 877 |
-
75.0,
|
| 878 |
-
15.0
|
| 879 |
-
],
|
| 880 |
-
[
|
| 881 |
-
75.0,
|
| 882 |
-
25.0
|
| 883 |
-
],
|
| 884 |
-
[
|
| 885 |
-
75.0,
|
| 886 |
-
35.0
|
| 887 |
-
],
|
| 888 |
-
[
|
| 889 |
-
75.0,
|
| 890 |
-
45.0
|
| 891 |
-
],
|
| 892 |
-
[
|
| 893 |
-
75.0,
|
| 894 |
-
55.0
|
| 895 |
-
],
|
| 896 |
-
[
|
| 897 |
-
75.0,
|
| 898 |
-
65.0
|
| 899 |
-
],
|
| 900 |
-
[
|
| 901 |
-
85.0,
|
| 902 |
-
-45.0
|
| 903 |
-
],
|
| 904 |
-
[
|
| 905 |
-
85.0,
|
| 906 |
-
-35.0
|
| 907 |
-
],
|
| 908 |
-
[
|
| 909 |
-
85.0,
|
| 910 |
-
-25.0
|
| 911 |
-
],
|
| 912 |
-
[
|
| 913 |
-
85.0,
|
| 914 |
-
-15.0
|
| 915 |
-
],
|
| 916 |
-
[
|
| 917 |
-
85.0,
|
| 918 |
-
-5.0
|
| 919 |
-
],
|
| 920 |
-
[
|
| 921 |
-
85.0,
|
| 922 |
-
5.0
|
| 923 |
-
],
|
| 924 |
-
[
|
| 925 |
-
85.0,
|
| 926 |
-
35.0
|
| 927 |
-
],
|
| 928 |
-
[
|
| 929 |
-
85.0,
|
| 930 |
-
45.0
|
| 931 |
-
],
|
| 932 |
-
[
|
| 933 |
-
85.0,
|
| 934 |
-
65.0
|
| 935 |
-
],
|
| 936 |
-
[
|
| 937 |
-
95.0,
|
| 938 |
-
-55.0
|
| 939 |
-
],
|
| 940 |
-
[
|
| 941 |
-
95.0,
|
| 942 |
-
-45.0
|
| 943 |
-
],
|
| 944 |
-
[
|
| 945 |
-
95.0,
|
| 946 |
-
-35.0
|
| 947 |
-
]
|
| 948 |
-
],
|
| 949 |
-
"context_dilations": [
|
| 950 |
-
2,
|
| 951 |
-
4,
|
| 952 |
-
8
|
| 953 |
-
],
|
| 954 |
-
"context_mid_ch": 96,
|
| 955 |
-
"in_ch": 1
|
| 956 |
}
|
|
|
|
| 1 |
{
|
| 2 |
+
"architecture": "SemanticColorizer",
|
| 3 |
+
"head": "palette",
|
| 4 |
+
"width": 128,
|
| 5 |
+
"queries": 16,
|
| 6 |
+
"format_version": 1
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7 |
}
|
export_onnx.py
CHANGED
|
@@ -1,48 +1,29 @@
|
|
| 1 |
-
"""Export the
|
| 2 |
-
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
"""
|
| 6 |
-
import argparse,json
|
| 7 |
from pathlib import Path
|
| 8 |
import numpy as np
|
| 9 |
import torch
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
class ChromaPipeline(torch.nn.Module):
|
| 14 |
-
def __init__(self,model,radius=8,temperature=.38):
|
| 15 |
-
super().__init__();self.model=model;self.radius=radius;self.temperature=temperature
|
| 16 |
-
def forward(self,L):
|
| 17 |
-
return guided_chroma(L,self.model.decode(self.model(L),self.temperature),self.radius)
|
| 18 |
-
|
| 19 |
-
def export(a):
|
| 20 |
-
import onnx,onnxruntime as ort
|
| 21 |
-
torch.set_num_threads(2);torch.manual_seed(2026)
|
| 22 |
-
model=ChromaPipeline(load_model(a.model),a.radius).eval()
|
| 23 |
-
out=Path(a.output);out.parent.mkdir(parents=True,exist_ok=True)
|
| 24 |
-
with torch.inference_mode():
|
| 25 |
-
torch.onnx.export(model,torch.zeros(1,1,256,256),str(out),opset_version=17,
|
| 26 |
-
input_names=['luminance'],output_names=['chroma'],dynamo=False,
|
| 27 |
-
dynamic_axes={'luminance':{0:'batch',2:'height',3:'width'},'chroma':{0:'batch',2:'height',3:'width'}})
|
| 28 |
-
onnx.checker.check_model(str(out))
|
| 29 |
-
opts=ort.SessionOptions();opts.intra_op_num_threads=2;opts.inter_op_num_threads=1
|
| 30 |
-
session=ort.InferenceSession(str(out),sess_options=opts,providers=['CPUExecutionProvider'])
|
| 31 |
-
checks=[]
|
| 32 |
-
for shape in [(1,1,256,256),(1,1,173,241),(2,1,64,80),(1,1,8,9)]:
|
| 33 |
-
x=torch.rand(shape)*2-1
|
| 34 |
-
with torch.inference_mode():expected=model(x).numpy()
|
| 35 |
-
actual=session.run(None,{'luminance':x.numpy()})[0]
|
| 36 |
-
error=np.abs(expected-actual)
|
| 37 |
-
assert actual.shape==expected.shape and np.isfinite(actual).all()
|
| 38 |
-
np.testing.assert_allclose(actual,expected,atol=.01,rtol=.001)
|
| 39 |
-
checks.append({'shape':list(shape),'max_ab_difference':float(error.max()),'mean_ab_difference':float(error.mean())})
|
| 40 |
-
result={'input':'N,1,H,W Lab L*/50-1; H,W>=8','output':'N,2,H,W Lab chroma a,b',
|
| 41 |
-
'guided_radius':a.radius,'guided_epsilon':.001,'temperature':.38,
|
| 42 |
-
'opset':17,'checks':checks,'parameters':sum(p.numel() for p in model.parameters()),
|
| 43 |
-
'weights_source':a.model,'runtime':ort.__version__,'bytes':out.stat().st_size}
|
| 44 |
-
out.with_suffix('.json').write_text(json.dumps(result,indent=2));print(json.dumps(result,indent=2))
|
| 45 |
|
| 46 |
-
|
| 47 |
-
p=argparse.ArgumentParser(
|
| 48 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Export the release network at fixed 256x256, with numerical verification."""
|
| 2 |
+
import argparse
|
| 3 |
+
import importlib.util
|
| 4 |
+
import tempfile
|
|
|
|
|
|
|
| 5 |
from pathlib import Path
|
| 6 |
import numpy as np
|
| 7 |
import torch
|
| 8 |
+
import onnx
|
| 9 |
+
import onnxruntime as ort
|
| 10 |
+
from semantic_model import load_semantic
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 11 |
|
| 12 |
+
def main():
|
| 13 |
+
p=argparse.ArgumentParser();p.add_argument('--model',default='.');p.add_argument('--output',default='colorizer.onnx');args=p.parse_args()
|
| 14 |
+
source=Path(__file__).with_name('semantic_model.py').read_text()
|
| 15 |
+
source=source.replace("x=F.pad(self.neutral_rgb(L),(0,(-w)%32,0,(-h)%32),mode='replicate')","x=self.neutral_rgb(L)")
|
| 16 |
+
source=source.replace("F.adaptive_avg_pool2d(f,(8,8)).flatten(2).transpose(1,2) for f in projected[1:]","F.avg_pool2d(f,k).flatten(2).transpose(1,2) for f,k in zip(projected[1:],[4,2,1])")
|
| 17 |
+
with tempfile.TemporaryDirectory() as tmp:
|
| 18 |
+
path=Path(tmp)/'export_model.py';path.write_text(source)
|
| 19 |
+
spec=importlib.util.spec_from_file_location('export_model',path);module=importlib.util.module_from_spec(spec);spec.loader.exec_module(module)
|
| 20 |
+
model=module.load_semantic(args.model,'cpu');reference=load_semantic(args.model,'cpu')
|
| 21 |
+
x=torch.linspace(-1,1,256*256).reshape(1,1,256,256)
|
| 22 |
+
with torch.no_grad():expected=reference(x).numpy();assert np.max(np.abs(expected-model(x).numpy()))<1e-5
|
| 23 |
+
torch.onnx.export(model,x,args.output,input_names=['L'],output_names=['ab'],opset_version=17,do_constant_folding=True,dynamo=False)
|
| 24 |
+
onnx.checker.check_model(args.output)
|
| 25 |
+
session=ort.InferenceSession(args.output,providers=['CPUExecutionProvider'])
|
| 26 |
+
error=float(np.max(np.abs(session.run(None,{'L':x.numpy()})[0]-expected)))
|
| 27 |
+
if error>=.005:raise RuntimeError(f'ONNX parity failed: {error}')
|
| 28 |
+
print(f'Exported {args.output}; maximum Lab error {error:.6f}')
|
| 29 |
+
if __name__=='__main__':main()
|
inference.py
CHANGED
|
@@ -1,63 +1,111 @@
|
|
| 1 |
-
"""Aspect-preserving
|
| 2 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3 |
import math
|
| 4 |
from pathlib import Path
|
| 5 |
import numpy as np
|
| 6 |
-
from PIL import Image, ImageOps
|
| 7 |
import torch
|
| 8 |
import torch.nn.functional as F
|
| 9 |
-
from
|
| 10 |
-
from
|
| 11 |
-
from
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 12 |
|
| 13 |
@torch.inference_mode()
|
| 14 |
-
def
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
raise ValueError("size must be >=8 and saturation finite and nonnegative")
|
| 18 |
-
image = ImageOps.exif_transpose(image).convert("RGB")
|
| 19 |
-
rgb = np.asarray(image, dtype=np.float32) / 255.0
|
| 20 |
-
luminance = rgb2lab(rgb)[..., 0].astype(np.float32)
|
| 21 |
-
h, w = luminance.shape
|
| 22 |
-
scale = min(size / max(h, w), 1.0)
|
| 23 |
-
target = (max(8, round(h*scale)), max(8, round(w*scale)))
|
| 24 |
device = next(model.parameters()).device
|
| 25 |
-
L =
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 37 |
|
| 38 |
def main():
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
p.add_argument("--device", default="cuda" if torch.cuda.is_available() else "cpu")
|
| 51 |
-
a = p.parse_args()
|
| 52 |
-
torch.set_num_threads(min(torch.get_num_threads(),4))
|
| 53 |
-
model = load_model(a.model, a.revision, a.device)
|
| 54 |
-
out = Path(a.output_dir); out.mkdir(parents=True, exist_ok=True)
|
| 55 |
-
for file in a.images:
|
| 56 |
-
with Image.open(file) as im:
|
| 57 |
-
result = colorize(model, im, a.size, a.temperature, a.saturation, a.flip_tta,
|
| 58 |
-
a.guided_radius,a.guided_epsilon)
|
| 59 |
-
dest = out / (Path(file).stem + "_colorized.png")
|
| 60 |
-
result.save(dest); print(dest)
|
| 61 |
-
|
| 62 |
-
if __name__ == "__main__":
|
| 63 |
-
main()
|
|
|
|
| 1 |
+
"""Aspect-preserving colourisation with a compact semantic model.
|
| 2 |
+
|
| 3 |
+
Input and output are PIL images. No teacher, critic or second learned model is
|
| 4 |
+
loaded. Original lightness and alpha are retained; out-of-gamut chroma is reduced.
|
| 5 |
+
"""
|
| 6 |
+
import json
|
| 7 |
import math
|
| 8 |
from pathlib import Path
|
| 9 |
import numpy as np
|
|
|
|
| 10 |
import torch
|
| 11 |
import torch.nn.functional as F
|
| 12 |
+
from PIL import Image, ImageOps
|
| 13 |
+
from skimage.color import rgb2lab
|
| 14 |
+
from semantic_model import load_semantic
|
| 15 |
+
|
| 16 |
+
MAX_PIXELS = 12_000_000
|
| 17 |
+
|
| 18 |
+
def load_colorizer(path, device='cpu'):
|
| 19 |
+
model = load_semantic(path, device)
|
| 20 |
+
count = sum(p.numel() for p in model.parameters())
|
| 21 |
+
if count >= 4_000_000:
|
| 22 |
+
raise ValueError('Model exceeds the four-million-parameter limit')
|
| 23 |
+
return model
|
| 24 |
+
|
| 25 |
+
def box_mean(x, r):
|
| 26 |
+
return F.avg_pool2d(x, 2*r+1, 1, r, count_include_pad=False)
|
| 27 |
+
|
| 28 |
+
def prepare(image, size=256):
|
| 29 |
+
if not isinstance(image, Image.Image):
|
| 30 |
+
raise TypeError('Expected a PIL image')
|
| 31 |
+
if not 128 <= int(size) <= 512:
|
| 32 |
+
raise ValueError('Input size must be between 128 and 512')
|
| 33 |
+
image = ImageOps.exif_transpose(image)
|
| 34 |
+
if image.width * image.height > MAX_PIXELS:
|
| 35 |
+
raise ValueError('Please resize the image to at most 12 megapixels')
|
| 36 |
+
alpha = np.asarray(image.getchannel('A')).copy() if 'A' in image.getbands() else None
|
| 37 |
+
rgb = np.asarray(image.convert('RGB'), dtype=np.float32) / 255
|
| 38 |
+
light = rgb2lab(rgb)[..., 0].astype(np.float32)
|
| 39 |
+
scale = min(int(size)/max(image.size), 1.)
|
| 40 |
+
shape = (max(8, round(image.height*scale)), max(8, round(image.width*scale)))
|
| 41 |
+
x = torch.from_numpy(light)[None,None]/50-1
|
| 42 |
+
small = F.interpolate(x, size=shape, mode='bilinear', align_corners=False, antialias=True)
|
| 43 |
+
return light, alpha, small
|
| 44 |
|
| 45 |
@torch.inference_mode()
|
| 46 |
+
def chroma_coefficients(model, small, radius=8):
|
| 47 |
+
if radius not in (0,4,8,12,16):
|
| 48 |
+
raise ValueError('Unsupported smoothing radius')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 49 |
device = next(model.parameters()).device
|
| 50 |
+
L = small.to(device)
|
| 51 |
+
ab = model(L).float()
|
| 52 |
+
if not torch.isfinite(ab).all():
|
| 53 |
+
raise RuntimeError('Model returned non-finite colours')
|
| 54 |
+
if radius == 0:
|
| 55 |
+
return torch.zeros_like(ab).cpu(), ab.cpu()
|
| 56 |
+
guide = (L.float()+1)/2
|
| 57 |
+
mi, mp = box_mean(guide,radius), box_mean(ab,radius)
|
| 58 |
+
var = (box_mean(guide*guide,radius)-mi*mi).clamp_min(0)
|
| 59 |
+
cov = box_mean(guide*ab,radius)-mi*mp
|
| 60 |
+
a = cov/(var+.001)
|
| 61 |
+
b = mp-a*mi
|
| 62 |
+
# Coefficients are upsampled, then evaluated against original-resolution L.
|
| 63 |
+
return box_mean(a,radius).cpu(), box_mean(b,radius).cpu()
|
| 64 |
+
|
| 65 |
+
def _linear_rgb(light, ab):
|
| 66 |
+
fy = (light+16)/116
|
| 67 |
+
f = np.stack([fy+ab[...,0]/500,fy,fy-ab[...,1]/200],axis=-1)
|
| 68 |
+
xyz = np.where(f>6/29,f**3,(f-4/29)*(3*(6/29)**2))
|
| 69 |
+
xyz *= np.array([.95047,1.,1.08883],np.float32)
|
| 70 |
+
matrix = np.array([[3.24048134,-1.53715152,-.49853633],[-.96925495,1.87599,.04155593],[.05564664,-.20404134,1.05731107]],np.float32)
|
| 71 |
+
return xyz @ matrix.T
|
| 72 |
+
|
| 73 |
+
def render(light, alpha, coefficients, saturation=1.):
|
| 74 |
+
saturation = float(saturation)
|
| 75 |
+
if not math.isfinite(saturation) or not 0 <= saturation <= 1.5:
|
| 76 |
+
raise ValueError('Colour strength must be between 0 and 1.5')
|
| 77 |
+
a,b = [F.interpolate(v.float(),size=light.shape,mode='bilinear',align_corners=False)[0].permute(1,2,0).numpy() for v in coefficients]
|
| 78 |
+
ab = (a*(light[...,None]/100)+b)*saturation
|
| 79 |
+
# Binary-search chroma compression retains Lab hue and lightness.
|
| 80 |
+
linear = _linear_rgb(light,ab)
|
| 81 |
+
invalid = ((linear < -1e-5)|(linear > 1+1e-5)).any(-1)
|
| 82 |
+
if invalid.any():
|
| 83 |
+
L = light[invalid]; colors=ab[invalid];lo=np.zeros(len(L),np.float32);hi=np.ones(len(L),np.float32)
|
| 84 |
+
for _ in range(9):
|
| 85 |
+
mid=(lo+hi)/2; candidate=_linear_rgb(L,colors*mid[:,None])
|
| 86 |
+
valid=((candidate>=-1e-5)&(candidate<=1+1e-5)).all(-1)
|
| 87 |
+
lo=np.where(valid,mid,lo);hi=np.where(valid,hi,mid)
|
| 88 |
+
ab[invalid]=colors*lo[:,None]
|
| 89 |
+
linear[invalid]=_linear_rgb(L,ab[invalid])
|
| 90 |
+
linear=np.clip(linear,0,1)
|
| 91 |
+
rgb=np.where(linear<=.0031308,12.92*linear,1.055*np.power(linear,1/2.4)-.055)
|
| 92 |
+
pixels=np.uint8(np.clip(np.rint(rgb*255),0,255))
|
| 93 |
+
if alpha is not None: pixels=np.concatenate([pixels,alpha[...,None]],axis=-1)
|
| 94 |
+
return Image.fromarray(pixels)
|
| 95 |
+
|
| 96 |
+
def colorize(model, image, size=256, radius=8, saturation=1.):
|
| 97 |
+
light,alpha,small=prepare(image,size)
|
| 98 |
+
return render(light,alpha,chroma_coefficients(model,small,radius),saturation)
|
| 99 |
|
| 100 |
def main():
|
| 101 |
+
import argparse
|
| 102 |
+
parser=argparse.ArgumentParser(description='Compact photo colouriser')
|
| 103 |
+
parser.add_argument('input');parser.add_argument('output')
|
| 104 |
+
parser.add_argument('--model',default='.');parser.add_argument('--device',default='cpu')
|
| 105 |
+
parser.add_argument('--size',type=int,default=256);parser.add_argument('--saturation',type=float,default=1.)
|
| 106 |
+
args=parser.parse_args()
|
| 107 |
+
model=load_colorizer(args.model,args.device)
|
| 108 |
+
with Image.open(args.input) as image:
|
| 109 |
+
colorize(model,image,args.size,saturation=args.saturation).save(args.output)
|
| 110 |
+
|
| 111 |
+
if __name__=='__main__':main()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
legacy/v2/ARCHIVE.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
Historical v2 helpers. Load weights/config from immutable model revision 704fa80d792c3d759db91daa00b2dcfe6f0f6412. Do not combine these helpers with the new semantic checkpoint.
|
legacy/v2/DEPLOYMENT.md
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Colorizer deployment
|
| 2 |
+
|
| 3 |
+
Use the release's inference wrapper or its ONNX graph to obtain the complete
|
| 4 |
+
improvement. The `.safetensors` file contains the learned colorizer; guided
|
| 5 |
+
decoding is implemented by `spatial.py` and is included in the ONNX graph.
|
| 6 |
+
|
| 7 |
+
## Python / PyTorch
|
| 8 |
+
|
| 9 |
+
Run from the extracted release directory:
|
| 10 |
+
|
| 11 |
+
```bash
|
| 12 |
+
python -m pip install -r requirements.txt
|
| 13 |
+
python inference.py --model . --output-dir colorized photo.jpg
|
| 14 |
+
```
|
| 15 |
+
|
| 16 |
+
The default guided radius is 8 at the model's input resolution. Use
|
| 17 |
+
`--guided-radius 0` for raw predictions or `--guided-radius 16` for stronger
|
| 18 |
+
smoothing. Stronger smoothing can remove legitimate small color details.
|
| 19 |
+
`--flip-tta --guided-radius 4` is an optional two-pass mode. The released
|
| 20 |
+
ONNX graph is the single-pass radius-8 mode.
|
| 21 |
+
|
| 22 |
+
```python
|
| 23 |
+
from PIL import Image
|
| 24 |
+
from model import load_model
|
| 25 |
+
from inference import colorize
|
| 26 |
+
|
| 27 |
+
model = load_model('.')
|
| 28 |
+
result = colorize(model, Image.open('photo.jpg'))
|
| 29 |
+
result.save('colorized.png')
|
| 30 |
+
```
|
| 31 |
+
|
| 32 |
+
The wrapper handles EXIF orientation, preserves aspect ratio, bounds the
|
| 33 |
+
longest network-input side to 256 pixels, upsamples chroma to the original
|
| 34 |
+
oriented image dimensions and combines it with the original Lab luminance.
|
| 35 |
+
Final RGB conversion can clip colors outside the display gamut. Very large
|
| 36 |
+
inputs still require memory for full-resolution color conversion. There is
|
| 37 |
+
no video temporal-consistency guarantee.
|
| 38 |
+
|
| 39 |
+
## ONNX without PyTorch
|
| 40 |
+
|
| 41 |
+
```bash
|
| 42 |
+
python -m pip install -r requirements-onnx.txt
|
| 43 |
+
python colorize_onnx.py --model colorizer.onnx --output-dir colorized photo.jpg
|
| 44 |
+
```
|
| 45 |
+
|
| 46 |
+
Input name: `luminance`, float32, shape `N x 1 x H x W`, values `L*/50 - 1`.
|
| 47 |
+
Output name: `chroma`, float32, shape `N x 2 x H x W`, Lab a and b values.
|
| 48 |
+
Height and width must each be at least 8. Dynamic shapes and batches are
|
| 49 |
+
supported. Prefer a longest input side of 256 to match the evaluated
|
| 50 |
+
operating point. Ordinary RGB values are not valid graph inputs.
|
| 51 |
+
|
| 52 |
+
The graph includes temperature-0.38 decoding and radius-8 guided filtering
|
| 53 |
+
with epsilon 0.001. It does not include file loading, EXIF handling, Lab
|
| 54 |
+
conversion, aspect-ratio resizing or final chroma upsampling; these are
|
| 55 |
+
implemented in `colorize_onnx.py`. The ONNX wrapper uses Pillow resizing,
|
| 56 |
+
whereas the PyTorch wrapper uses PyTorch interpolation. Their image-level
|
| 57 |
+
comparison is recorded in the release checks.
|
| 58 |
+
|
| 59 |
+
## Publish to main
|
| 60 |
+
|
| 61 |
+
Authenticate normally on your own computer with repository write access:
|
| 62 |
+
|
| 63 |
+
```bash
|
| 64 |
+
hf auth login
|
| 65 |
+
python upload_main.py --folder . --repo User-2468/mini-unet-colorizer
|
| 66 |
+
```
|
| 67 |
+
|
| 68 |
+
This performs one upload to `main` with an optimistic-concurrency guard.
|
| 69 |
+
It also verifies that `stable` retains its pre-upload revision. The app can
|
| 70 |
+
keep using `stable` until you choose to switch it to the tested release.
|
| 71 |
+
The current chat connection has Jobs/read access but no repository write
|
| 72 |
+
scope; the downloadable release is the publication fallback.
|
| 73 |
+
|
| 74 |
+
## Deployment limits
|
| 75 |
+
|
| 76 |
+
This is a measured app-testing candidate. Semantic color mistakes remain,
|
| 77 |
+
and smoothing cannot infer an object's unknown original color. Review the
|
| 78 |
+
included failure examples on your app's real input photos before describing
|
| 79 |
+
it as generally production-ready. Browser/mobile performance and real-time
|
| 80 |
+
video have not been validated.
|
legacy/v2/README.md
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
license: apache-2.0
|
| 3 |
+
pipeline_tag: image-to-image
|
| 4 |
+
tags:
|
| 5 |
+
- colorization
|
| 6 |
+
- unet
|
| 7 |
+
- pytorch
|
| 8 |
+
- safetensors
|
| 9 |
+
- onnx
|
| 10 |
+
datasets:
|
| 11 |
+
- johnowhitaker/imagenette2-320
|
| 12 |
+
- detection-datasets/coco
|
| 13 |
+
---
|
| 14 |
+
# Mini U-Net Colorizer — broader-data trained candidate
|
| 15 |
+
|
| 16 |
+
**Status: evaluated app-testing candidate.** Some broad color patches and
|
| 17 |
+
incorrect object hues remain. Predicted colors are not evidence of original
|
| 18 |
+
historical colors.
|
| 19 |
+
|
| 20 |
+
This checkpoint has 3,968,892 learned parameters and 236 fixed color bins.
|
| 21 |
+
It starts from the audited bin-mapping repair of main commit
|
| 22 |
+
`6c47ea40724d8fcd67d4f36ce837dc1cb5b1b2a8` and changes all 65 learned parameter
|
| 23 |
+
tensors. The color vocabulary is unchanged. The selected weights are update
|
| 24 |
+
748 of a completed 1,122-update BF16 L4 run using a 17,325-photo mixed training
|
| 25 |
+
pool (11,943 Imagenette plus 5,382 COCO), batch 32, initial learning rate 1e-5,
|
| 26 |
+
weighted classification loss and frozen BatchNorm running statistics.
|
| 27 |
+
|
| 28 |
+
Selection compared Imagenette50 and reserved COCO200 validation images.
|
| 29 |
+
The selected model retained color strength better than spatial-loss candidates.
|
| 30 |
+
It was then scored on separate Imagenette200 and COCO-val100 checks.
|
| 31 |
+
|
| 32 |
+
| Test sample | Previous repaired error | This release error | Fine excess-edge reduction |
|
| 33 |
+
|---|---:|---:|---:|
|
| 34 |
+
| Imagenette 200 | 13.318 | 12.847 | 85.8% |
|
| 35 |
+
| COCO-val 100 | 15.266 | 14.532 | 84.3% |
|
| 36 |
+
|
| 37 |
+
Error is mean Lab chroma distance. The release includes guided8 decoding;
|
| 38 |
+
raw learned weights alone improve error by 1.97% and 3.25%, respectively.
|
| 39 |
+
Excess-edge reductions are proxies, not counts of visible blotches removed.
|
| 40 |
+
COCO is a convenience sample; older upstream training exposure is unknown.
|
| 41 |
+
|
| 42 |
+
## Use the complete pipeline
|
| 43 |
+
|
| 44 |
+
```bash
|
| 45 |
+
python -m pip install -r requirements.txt
|
| 46 |
+
python inference.py --model . --output-dir colorized photo.jpg
|
| 47 |
+
```
|
| 48 |
+
|
| 49 |
+
```python
|
| 50 |
+
from PIL import Image
|
| 51 |
+
from model import load_model
|
| 52 |
+
from inference import colorize
|
| 53 |
+
model = load_model('.')
|
| 54 |
+
colorize(model, Image.open('photo.jpg')).save('colorized.png')
|
| 55 |
+
```
|
| 56 |
+
|
| 57 |
+
Defaults: temperature 0.38, guided radius 8, epsilon 0.001, one network pass.
|
| 58 |
+
The wrapper preserves aspect ratio and original luminance. Old app code that
|
| 59 |
+
only loads safetensors will not automatically gain guided filtering.
|
| 60 |
+
|
| 61 |
+
For ONNX without PyTorch:
|
| 62 |
+
|
| 63 |
+
```bash
|
| 64 |
+
python -m pip install -r requirements-onnx.txt
|
| 65 |
+
python colorize_onnx.py --model colorizer.onnx --output-dir colorized photo.jpg
|
| 66 |
+
```
|
| 67 |
+
|
| 68 |
+
The 15.9MB ONNX graph includes the model and guided decoder, with dynamic
|
| 69 |
+
batch/spatial sizes and verified PyTorch parity. See `DEPLOYMENT.md` for the
|
| 70 |
+
Lab input contract, CPU timings, publication commands and integration limits.
|
| 71 |
+
See `RESEARCH_ROUND2.md` and `reports/round2/` for complete measured evidence.
|
| 72 |
+
|
| 73 |
+
## Limitations
|
| 74 |
+
|
| 75 |
+
Smoothing removes fine color fluctuations but can suppress true small color
|
| 76 |
+
details, especially without luminance boundaries. Semantically wrong hues
|
| 77 |
+
remain. The coffee and rocket failure examples are retained. Browser/mobile
|
| 78 |
+
performance, video consistency and general production quality are unvalidated.
|
| 79 |
+
The separate experiment bundle contains all runs and reproduction code; GPU
|
| 80 |
+
optimizer state is not included. This is a weights-only continuation point.
|
colorize_eval.py → legacy/v2/colorize_eval.py
RENAMED
|
File without changes
|
legacy/v2/colorize_image.py
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# /// script
|
| 2 |
+
# requires-python = ">=3.10"
|
| 3 |
+
# dependencies = [
|
| 4 |
+
# "torch>=2.3",
|
| 5 |
+
# "torchvision>=0.18",
|
| 6 |
+
# "huggingface_hub>=0.24",
|
| 7 |
+
# "safetensors>=0.4",
|
| 8 |
+
# "scikit-image>=0.22",
|
| 9 |
+
# "pillow>=10.0",
|
| 10 |
+
# "numpy",
|
| 11 |
+
# ]
|
| 12 |
+
# ///
|
| 13 |
+
"""
|
| 14 |
+
Colorize photos with a SmallUNetColorizer checkpoint trained by colorize_train.py
|
| 15 |
+
(classification-head version: predicts a distribution over quantized Lab ab
|
| 16 |
+
bins per pixel, decoded with an annealed mean).
|
| 17 |
+
|
| 18 |
+
Usage:
|
| 19 |
+
uv run colorize_image.py --model User-2468/mini-unet-colorizer photo.jpg
|
| 20 |
+
uv run colorize_image.py --model ./local_checkpoint --temperature 0.2 a.jpg b.jpg
|
| 21 |
+
|
| 22 |
+
--temperature is the main "vividness" knob: lower values weight the decode
|
| 23 |
+
toward the most likely color bin (more saturated, can be a bit blotchy);
|
| 24 |
+
higher values move toward the full expectation over the distribution
|
| 25 |
+
(smoother, but can drift back toward desaturated -- the same hedging effect
|
| 26 |
+
plain regression had). 0.38 (the default) is a reasonable middle ground.
|
| 27 |
+
--saturation-boost is an optional *additional* post-decode multiplier on top
|
| 28 |
+
of that, for further hand-tuning after picking a temperature.
|
| 29 |
+
"""
|
| 30 |
+
import argparse
|
| 31 |
+
from pathlib import Path
|
| 32 |
+
|
| 33 |
+
import numpy as np
|
| 34 |
+
import torch
|
| 35 |
+
import torch.nn as nn
|
| 36 |
+
import torch.nn.functional as F
|
| 37 |
+
from huggingface_hub import PyTorchModelHubMixin
|
| 38 |
+
from PIL import Image
|
| 39 |
+
from skimage.color import lab2rgb, rgb2lab
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
def double_conv(in_ch, out_ch):
|
| 43 |
+
return nn.Sequential(
|
| 44 |
+
nn.Conv2d(in_ch, out_ch, 3, padding=1, bias=False),
|
| 45 |
+
nn.BatchNorm2d(out_ch),
|
| 46 |
+
nn.ReLU(inplace=True),
|
| 47 |
+
nn.Conv2d(out_ch, out_ch, 3, padding=1, bias=False),
|
| 48 |
+
nn.BatchNorm2d(out_ch),
|
| 49 |
+
nn.ReLU(inplace=True),
|
| 50 |
+
)
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
class DilatedContextBlock(nn.Module):
|
| 54 |
+
def __init__(self, channels, mid_ch=96, dilations=(2, 4, 8)):
|
| 55 |
+
super().__init__()
|
| 56 |
+
self.proj_in = nn.Sequential(
|
| 57 |
+
nn.Conv2d(channels, mid_ch, 1, bias=False),
|
| 58 |
+
nn.BatchNorm2d(mid_ch),
|
| 59 |
+
nn.ReLU(inplace=True),
|
| 60 |
+
)
|
| 61 |
+
layers = []
|
| 62 |
+
for d in dilations:
|
| 63 |
+
layers += [
|
| 64 |
+
nn.Conv2d(mid_ch, mid_ch, 3, padding=d, dilation=d, bias=False),
|
| 65 |
+
nn.BatchNorm2d(mid_ch),
|
| 66 |
+
nn.ReLU(inplace=True),
|
| 67 |
+
]
|
| 68 |
+
self.dilated = nn.Sequential(*layers)
|
| 69 |
+
self.proj_out = nn.Sequential(
|
| 70 |
+
nn.Conv2d(mid_ch, channels, 1, bias=False),
|
| 71 |
+
nn.BatchNorm2d(channels),
|
| 72 |
+
)
|
| 73 |
+
self.relu = nn.ReLU(inplace=True)
|
| 74 |
+
|
| 75 |
+
def forward(self, x):
|
| 76 |
+
y = self.proj_in(x)
|
| 77 |
+
y = self.dilated(y)
|
| 78 |
+
y = self.proj_out(y)
|
| 79 |
+
return self.relu(x + y)
|
| 80 |
+
|
| 81 |
+
|
| 82 |
+
class SmallUNetColorizer(
|
| 83 |
+
nn.Module,
|
| 84 |
+
PyTorchModelHubMixin,
|
| 85 |
+
pipeline_tag="image-to-image",
|
| 86 |
+
license="apache-2.0",
|
| 87 |
+
tags=["colorization", "unet", "image-to-image", "classification"],
|
| 88 |
+
):
|
| 89 |
+
def __init__(self, bin_centers, in_ch: int = 1, base: int = 44,
|
| 90 |
+
context_mid_ch: int = 96, context_dilations=(2, 4, 8)):
|
| 91 |
+
super().__init__()
|
| 92 |
+
self.in_ch, self.base = in_ch, base
|
| 93 |
+
num_bins = len(bin_centers)
|
| 94 |
+
self.num_bins = num_bins
|
| 95 |
+
self.register_buffer("bin_centers", torch.tensor(bin_centers, dtype=torch.float32))
|
| 96 |
+
|
| 97 |
+
self.enc1 = double_conv(in_ch, base)
|
| 98 |
+
self.enc2 = double_conv(base, base * 2)
|
| 99 |
+
self.enc3 = double_conv(base * 2, base * 4)
|
| 100 |
+
self.enc4 = double_conv(base * 4, base * 8)
|
| 101 |
+
self.pool = nn.MaxPool2d(2)
|
| 102 |
+
self.context = DilatedContextBlock(base * 8, mid_ch=context_mid_ch,
|
| 103 |
+
dilations=tuple(context_dilations))
|
| 104 |
+
self.up3 = nn.ConvTranspose2d(base * 8, base * 4, 2, stride=2)
|
| 105 |
+
self.dec3 = double_conv(base * 8, base * 4)
|
| 106 |
+
self.up2 = nn.ConvTranspose2d(base * 4, base * 2, 2, stride=2)
|
| 107 |
+
self.dec2 = double_conv(base * 4, base * 2)
|
| 108 |
+
self.up1 = nn.ConvTranspose2d(base * 2, base, 2, stride=2)
|
| 109 |
+
self.dec1 = double_conv(base * 2, base)
|
| 110 |
+
self.out_conv = nn.Conv2d(base, num_bins, 1)
|
| 111 |
+
|
| 112 |
+
def forward(self, x):
|
| 113 |
+
e1 = self.enc1(x)
|
| 114 |
+
e2 = self.enc2(self.pool(e1))
|
| 115 |
+
e3 = self.enc3(self.pool(e2))
|
| 116 |
+
e4 = self.context(self.enc4(self.pool(e3)))
|
| 117 |
+
d3 = self.dec3(torch.cat([self.up3(e4), e3], dim=1))
|
| 118 |
+
d2 = self.dec2(torch.cat([self.up2(d3), e2], dim=1))
|
| 119 |
+
d1 = self.dec1(torch.cat([self.up1(d2), e1], dim=1))
|
| 120 |
+
return self.out_conv(d1)
|
| 121 |
+
|
| 122 |
+
def decode(self, logits, temperature: float = 0.38):
|
| 123 |
+
logp = F.log_softmax(logits, dim=1)
|
| 124 |
+
probs_t = F.softmax(logp / temperature, dim=1)
|
| 125 |
+
return torch.einsum("bqhw,qc->bchw", probs_t, self.bin_centers)
|
| 126 |
+
|
| 127 |
+
|
| 128 |
+
def colorize(model, img, size, temperature, saturation_boost, device):
|
| 129 |
+
img = img.convert("RGB").resize((size, size))
|
| 130 |
+
arr = np.asarray(img).astype(np.float32) / 255.0
|
| 131 |
+
lab = rgb2lab(arr).astype(np.float32)
|
| 132 |
+
L = torch.from_numpy(lab[:, :, 0:1] / 50.0 - 1.0).permute(2, 0, 1)[None].to(device)
|
| 133 |
+
|
| 134 |
+
with torch.no_grad():
|
| 135 |
+
logits = model(L)
|
| 136 |
+
ab = model.decode(logits, temperature=temperature)[0].permute(1, 2, 0).cpu().numpy()
|
| 137 |
+
|
| 138 |
+
ab = np.clip(ab * saturation_boost, -128, 127)
|
| 139 |
+
L_out = (L[0, 0].cpu().numpy() + 1.0) * 50.0
|
| 140 |
+
lab_out = np.concatenate([L_out[:, :, None], ab], axis=-1)
|
| 141 |
+
rgb_out = np.clip(lab2rgb(lab_out), 0, 1)
|
| 142 |
+
return Image.fromarray((rgb_out * 255).astype(np.uint8))
|
| 143 |
+
|
| 144 |
+
|
| 145 |
+
def main():
|
| 146 |
+
p = argparse.ArgumentParser(description=__doc__)
|
| 147 |
+
p.add_argument("images", nargs="+", help="Path(s) to input photo(s)")
|
| 148 |
+
p.add_argument("--model", required=True, help="Hub model id or local checkpoint path")
|
| 149 |
+
p.add_argument("--size", type=int, default=256, help="Resize input to this square size")
|
| 150 |
+
p.add_argument("--temperature", type=float, default=0.38,
|
| 151 |
+
help="Annealed-mean decode temperature. Lower = more vivid/mode-like, "
|
| 152 |
+
"higher = smoother but can desaturate again. Try 0.15-0.6.")
|
| 153 |
+
p.add_argument("--saturation-boost", type=float, default=1.0,
|
| 154 |
+
help="Extra multiplier on the decoded ab, applied after temperature. "
|
| 155 |
+
"1.0 = no extra boost.")
|
| 156 |
+
p.add_argument("--output-dir", default="./colorized")
|
| 157 |
+
args = p.parse_args()
|
| 158 |
+
|
| 159 |
+
device = "cuda" if torch.cuda.is_available() else "cpu"
|
| 160 |
+
print(f"Loading {args.model} on {device} ...")
|
| 161 |
+
model = SmallUNetColorizer.from_pretrained(args.model).to(device).eval()
|
| 162 |
+
print(f"({model.num_bins} color bins)")
|
| 163 |
+
|
| 164 |
+
out_dir = Path(args.output_dir)
|
| 165 |
+
out_dir.mkdir(parents=True, exist_ok=True)
|
| 166 |
+
|
| 167 |
+
for path in args.images:
|
| 168 |
+
img = Image.open(path)
|
| 169 |
+
result = colorize(model, img, args.size, args.temperature, args.saturation_boost, device)
|
| 170 |
+
out_path = out_dir / f"{Path(path).stem}_colorized.png"
|
| 171 |
+
result.save(out_path)
|
| 172 |
+
print(f"{path} -> {out_path}")
|
| 173 |
+
|
| 174 |
+
|
| 175 |
+
if __name__ == "__main__":
|
| 176 |
+
main()
|
legacy/v2/colorize_onnx.py
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Run the exported chroma pipeline without importing PyTorch."""
|
| 2 |
+
import argparse
|
| 3 |
+
from pathlib import Path
|
| 4 |
+
import numpy as np
|
| 5 |
+
import onnxruntime as ort
|
| 6 |
+
from PIL import Image,ImageOps
|
| 7 |
+
from skimage.color import rgb2lab,lab2rgb
|
| 8 |
+
|
| 9 |
+
def load_session(path,threads=2):
|
| 10 |
+
opts=ort.SessionOptions();opts.intra_op_num_threads=threads;opts.inter_op_num_threads=1
|
| 11 |
+
return ort.InferenceSession(str(path),sess_options=opts,providers=['CPUExecutionProvider'])
|
| 12 |
+
|
| 13 |
+
def colorize(session,image,size=256):
|
| 14 |
+
if size<8:raise ValueError('size must be at least8')
|
| 15 |
+
image=ImageOps.exif_transpose(image).convert('RGB')
|
| 16 |
+
L=rgb2lab(np.asarray(image,dtype=np.float32)/255)[...,0].astype(np.float32)
|
| 17 |
+
h,w=L.shape;scale=min(size/max(h,w),1)
|
| 18 |
+
target=(max(8,round(w*scale)),max(8,round(h*scale)))
|
| 19 |
+
small=np.asarray(Image.fromarray(L).resize(target,Image.Resampling.BILINEAR),dtype=np.float32)
|
| 20 |
+
x=(small[None,None]/50-1).copy()
|
| 21 |
+
ab=session.run(['chroma'],{'luminance':x})[0][0]
|
| 22 |
+
full=np.stack([np.asarray(Image.fromarray(c).resize((w,h),Image.Resampling.BILINEAR)) for c in ab],axis=-1)
|
| 23 |
+
rgb=np.clip(lab2rgb(np.concatenate([L[...,None],full],axis=-1)),0,1)
|
| 24 |
+
return Image.fromarray(np.rint(rgb*255).astype(np.uint8))
|
| 25 |
+
|
| 26 |
+
if __name__=='__main__':
|
| 27 |
+
p=argparse.ArgumentParser(__doc__);p.add_argument('images',nargs='+');p.add_argument('--model',required=True)
|
| 28 |
+
p.add_argument('--output-dir',default='colorized');p.add_argument('--size',type=int,default=256);p.add_argument('--threads',type=int,default=2)
|
| 29 |
+
a=p.parse_args();session=load_session(a.model,a.threads);out=Path(a.output_dir);out.mkdir(parents=True,exist_ok=True)
|
| 30 |
+
for source in a.images:
|
| 31 |
+
with Image.open(source) as im:result=colorize(session,im,a.size)
|
| 32 |
+
dest=out/(Path(source).stem+'_colorized.png');result.save(dest);print(dest)
|
colorize_train.py → legacy/v2/colorize_train.py
RENAMED
|
File without changes
|
legacy/v2/colorizer.json
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"input": "N,1,H,W Lab L*/50-1; H,W>=8",
|
| 3 |
+
"output": "N,2,H,W Lab chroma a,b",
|
| 4 |
+
"guided_radius": 8,
|
| 5 |
+
"guided_epsilon": 0.001,
|
| 6 |
+
"temperature": 0.38,
|
| 7 |
+
"opset": 17,
|
| 8 |
+
"checks": [
|
| 9 |
+
{
|
| 10 |
+
"shape": [
|
| 11 |
+
1,
|
| 12 |
+
1,
|
| 13 |
+
256,
|
| 14 |
+
256
|
| 15 |
+
],
|
| 16 |
+
"max_ab_difference": 8.58306884765625e-06,
|
| 17 |
+
"mean_ab_difference": 1.0279118214384653e-06
|
| 18 |
+
},
|
| 19 |
+
{
|
| 20 |
+
"shape": [
|
| 21 |
+
1,
|
| 22 |
+
1,
|
| 23 |
+
173,
|
| 24 |
+
241
|
| 25 |
+
],
|
| 26 |
+
"max_ab_difference": 7.3909759521484375e-06,
|
| 27 |
+
"mean_ab_difference": 1.0783561492644367e-06
|
| 28 |
+
},
|
| 29 |
+
{
|
| 30 |
+
"shape": [
|
| 31 |
+
2,
|
| 32 |
+
1,
|
| 33 |
+
64,
|
| 34 |
+
80
|
| 35 |
+
],
|
| 36 |
+
"max_ab_difference": 7.152557373046875e-06,
|
| 37 |
+
"mean_ab_difference": 9.707116532808868e-07
|
| 38 |
+
},
|
| 39 |
+
{
|
| 40 |
+
"shape": [
|
| 41 |
+
1,
|
| 42 |
+
1,
|
| 43 |
+
8,
|
| 44 |
+
9
|
| 45 |
+
],
|
| 46 |
+
"max_ab_difference": 1.5497207641601562e-06,
|
| 47 |
+
"mean_ab_difference": 3.6218099808138504e-07
|
| 48 |
+
}
|
| 49 |
+
],
|
| 50 |
+
"parameters": 3968892,
|
| 51 |
+
"weights_source": "release-v2",
|
| 52 |
+
"runtime": "1.30.0",
|
| 53 |
+
"bytes": 15885757
|
| 54 |
+
}
|
eval_grid.png → legacy/v2/eval_grid.png
RENAMED
|
File without changes
|
eval_grid_temp038.png → legacy/v2/eval_grid_temp038.png
RENAMED
|
File without changes
|
eval_grid_temp075.png → legacy/v2/eval_grid_temp075.png
RENAMED
|
File without changes
|
legacy/v2/export_onnx.py
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Export the colorizer AND luminance-guided chroma decoder as one ONNX graph.
|
| 2 |
+
|
| 3 |
+
Input: N,1,H,W normalized Lab luminance (L*/50-1), minimum8px per side.
|
| 4 |
+
Output: N,2,H,W Lab a,b values. Resize/preserve original L* in the app.
|
| 5 |
+
"""
|
| 6 |
+
import argparse,json
|
| 7 |
+
from pathlib import Path
|
| 8 |
+
import numpy as np
|
| 9 |
+
import torch
|
| 10 |
+
from model import load_model
|
| 11 |
+
from spatial import guided_chroma
|
| 12 |
+
|
| 13 |
+
class ChromaPipeline(torch.nn.Module):
|
| 14 |
+
def __init__(self,model,radius=8,temperature=.38):
|
| 15 |
+
super().__init__();self.model=model;self.radius=radius;self.temperature=temperature
|
| 16 |
+
def forward(self,L):
|
| 17 |
+
return guided_chroma(L,self.model.decode(self.model(L),self.temperature),self.radius)
|
| 18 |
+
|
| 19 |
+
def export(a):
|
| 20 |
+
import onnx,onnxruntime as ort
|
| 21 |
+
torch.set_num_threads(2);torch.manual_seed(2026)
|
| 22 |
+
model=ChromaPipeline(load_model(a.model),a.radius).eval()
|
| 23 |
+
out=Path(a.output);out.parent.mkdir(parents=True,exist_ok=True)
|
| 24 |
+
with torch.inference_mode():
|
| 25 |
+
torch.onnx.export(model,torch.zeros(1,1,256,256),str(out),opset_version=17,
|
| 26 |
+
input_names=['luminance'],output_names=['chroma'],dynamo=False,
|
| 27 |
+
dynamic_axes={'luminance':{0:'batch',2:'height',3:'width'},'chroma':{0:'batch',2:'height',3:'width'}})
|
| 28 |
+
onnx.checker.check_model(str(out))
|
| 29 |
+
opts=ort.SessionOptions();opts.intra_op_num_threads=2;opts.inter_op_num_threads=1
|
| 30 |
+
session=ort.InferenceSession(str(out),sess_options=opts,providers=['CPUExecutionProvider'])
|
| 31 |
+
checks=[]
|
| 32 |
+
for shape in [(1,1,256,256),(1,1,173,241),(2,1,64,80),(1,1,8,9)]:
|
| 33 |
+
x=torch.rand(shape)*2-1
|
| 34 |
+
with torch.inference_mode():expected=model(x).numpy()
|
| 35 |
+
actual=session.run(None,{'luminance':x.numpy()})[0]
|
| 36 |
+
error=np.abs(expected-actual)
|
| 37 |
+
assert actual.shape==expected.shape and np.isfinite(actual).all()
|
| 38 |
+
np.testing.assert_allclose(actual,expected,atol=.01,rtol=.001)
|
| 39 |
+
checks.append({'shape':list(shape),'max_ab_difference':float(error.max()),'mean_ab_difference':float(error.mean())})
|
| 40 |
+
result={'input':'N,1,H,W Lab L*/50-1; H,W>=8','output':'N,2,H,W Lab chroma a,b',
|
| 41 |
+
'guided_radius':a.radius,'guided_epsilon':.001,'temperature':.38,
|
| 42 |
+
'opset':17,'checks':checks,'parameters':sum(p.numel() for p in model.parameters()),
|
| 43 |
+
'weights_source':a.model,'runtime':ort.__version__,'bytes':out.stat().st_size}
|
| 44 |
+
out.with_suffix('.json').write_text(json.dumps(result,indent=2));print(json.dumps(result,indent=2))
|
| 45 |
+
|
| 46 |
+
if __name__=='__main__':
|
| 47 |
+
p=argparse.ArgumentParser(__doc__);p.add_argument('--model',required=True);p.add_argument('--output',required=True);p.add_argument('--radius',type=int,default=8)
|
| 48 |
+
export(p.parse_args())
|
legacy/v2/inference.py
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Aspect-preserving inference; infer chroma globally and retain original luminance."""
|
| 2 |
+
import argparse
|
| 3 |
+
import math
|
| 4 |
+
from pathlib import Path
|
| 5 |
+
import numpy as np
|
| 6 |
+
from PIL import Image, ImageOps
|
| 7 |
+
import torch
|
| 8 |
+
import torch.nn.functional as F
|
| 9 |
+
from skimage.color import rgb2lab, lab2rgb
|
| 10 |
+
from model import load_model
|
| 11 |
+
from spatial import guided_chroma
|
| 12 |
+
|
| 13 |
+
@torch.inference_mode()
|
| 14 |
+
def colorize(model, image, size=256, temperature=0.38, saturation=1.0, flip_tta=False,
|
| 15 |
+
guided_radius=8, guided_epsilon=.001):
|
| 16 |
+
if size < 8 or not math.isfinite(saturation) or saturation < 0:
|
| 17 |
+
raise ValueError("size must be >=8 and saturation finite and nonnegative")
|
| 18 |
+
image = ImageOps.exif_transpose(image).convert("RGB")
|
| 19 |
+
rgb = np.asarray(image, dtype=np.float32) / 255.0
|
| 20 |
+
luminance = rgb2lab(rgb)[..., 0].astype(np.float32)
|
| 21 |
+
h, w = luminance.shape
|
| 22 |
+
scale = min(size / max(h, w), 1.0)
|
| 23 |
+
target = (max(8, round(h*scale)), max(8, round(w*scale)))
|
| 24 |
+
device = next(model.parameters()).device
|
| 25 |
+
L = torch.from_numpy(luminance)[None, None].to(device) / 50 - 1
|
| 26 |
+
small = F.interpolate(L, size=target, mode="bilinear", align_corners=False, antialias=True)
|
| 27 |
+
logits = model(small)
|
| 28 |
+
if flip_tta:
|
| 29 |
+
logits = (logits + model(small.flip(-1)).flip(-1)) * 0.5
|
| 30 |
+
ab = model.decode(logits, temperature)
|
| 31 |
+
ab = guided_chroma(small, ab, guided_radius, guided_epsilon)
|
| 32 |
+
ab = F.interpolate(ab, size=(h,w), mode="bilinear", align_corners=False)
|
| 33 |
+
ab = ab[0].permute(1,2,0).cpu().numpy() * saturation
|
| 34 |
+
lab = np.concatenate([luminance[...,None], ab], axis=-1)
|
| 35 |
+
result = np.clip(lab2rgb(lab), 0, 1)
|
| 36 |
+
return Image.fromarray(np.rint(result * 255).astype(np.uint8))
|
| 37 |
+
|
| 38 |
+
def main():
|
| 39 |
+
p = argparse.ArgumentParser(__doc__)
|
| 40 |
+
p.add_argument("images", nargs="+")
|
| 41 |
+
p.add_argument("--model", required=True)
|
| 42 |
+
p.add_argument("--revision", default=None)
|
| 43 |
+
p.add_argument("--size", type=int, default=256)
|
| 44 |
+
p.add_argument("--temperature", type=float, default=.38)
|
| 45 |
+
p.add_argument("--saturation", type=float, default=1)
|
| 46 |
+
p.add_argument("--flip-tta", action="store_true")
|
| 47 |
+
p.add_argument("--guided-radius",type=int,default=8,help="Chroma smoothing radius at model resolution;0 disables")
|
| 48 |
+
p.add_argument("--guided-epsilon",type=float,default=.001)
|
| 49 |
+
p.add_argument("--output-dir", default="colorized")
|
| 50 |
+
p.add_argument("--device", default="cuda" if torch.cuda.is_available() else "cpu")
|
| 51 |
+
a = p.parse_args()
|
| 52 |
+
torch.set_num_threads(min(torch.get_num_threads(),4))
|
| 53 |
+
model = load_model(a.model, a.revision, a.device)
|
| 54 |
+
out = Path(a.output_dir); out.mkdir(parents=True, exist_ok=True)
|
| 55 |
+
for file in a.images:
|
| 56 |
+
with Image.open(file) as im:
|
| 57 |
+
result = colorize(model, im, a.size, a.temperature, a.saturation, a.flip_tta,
|
| 58 |
+
a.guided_radius,a.guided_epsilon)
|
| 59 |
+
dest = out / (Path(file).stem + "_colorized.png")
|
| 60 |
+
result.save(dest); print(dest)
|
| 61 |
+
|
| 62 |
+
if __name__ == "__main__":
|
| 63 |
+
main()
|
legacy/v2/model.py
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Shared, checkpoint-compatible Mini U-Net. Parameters remain under 4M."""
|
| 2 |
+
import json
|
| 3 |
+
import math
|
| 4 |
+
from pathlib import Path
|
| 5 |
+
import numpy as np
|
| 6 |
+
import torch
|
| 7 |
+
from torch import nn
|
| 8 |
+
import torch.nn.functional as F
|
| 9 |
+
from huggingface_hub import PyTorchModelHubMixin, snapshot_download
|
| 10 |
+
from safetensors.torch import load_file, save_file
|
| 11 |
+
def double_conv(in_ch, out_ch):
|
| 12 |
+
return nn.Sequential(
|
| 13 |
+
nn.Conv2d(in_ch, out_ch, 3, padding=1, bias=False),
|
| 14 |
+
nn.BatchNorm2d(out_ch),
|
| 15 |
+
nn.ReLU(inplace=True),
|
| 16 |
+
nn.Conv2d(out_ch, out_ch, 3, padding=1, bias=False),
|
| 17 |
+
nn.BatchNorm2d(out_ch),
|
| 18 |
+
nn.ReLU(inplace=True),
|
| 19 |
+
)
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
class DilatedContextBlock(nn.Module):
|
| 23 |
+
def __init__(self, channels, mid_ch=96, dilations=(2, 4, 8)):
|
| 24 |
+
super().__init__()
|
| 25 |
+
self.proj_in = nn.Sequential(
|
| 26 |
+
nn.Conv2d(channels, mid_ch, 1, bias=False),
|
| 27 |
+
nn.BatchNorm2d(mid_ch),
|
| 28 |
+
nn.ReLU(inplace=True),
|
| 29 |
+
)
|
| 30 |
+
layers = []
|
| 31 |
+
for d in dilations:
|
| 32 |
+
layers += [
|
| 33 |
+
nn.Conv2d(mid_ch, mid_ch, 3, padding=d, dilation=d, bias=False),
|
| 34 |
+
nn.BatchNorm2d(mid_ch),
|
| 35 |
+
nn.ReLU(inplace=True),
|
| 36 |
+
]
|
| 37 |
+
self.dilated = nn.Sequential(*layers)
|
| 38 |
+
self.proj_out = nn.Sequential(
|
| 39 |
+
nn.Conv2d(mid_ch, channels, 1, bias=False),
|
| 40 |
+
nn.BatchNorm2d(channels),
|
| 41 |
+
)
|
| 42 |
+
nn.init.zeros_(self.proj_out[-1].weight)
|
| 43 |
+
self.relu = nn.ReLU(inplace=True)
|
| 44 |
+
|
| 45 |
+
def forward(self, x):
|
| 46 |
+
y = self.proj_in(x)
|
| 47 |
+
y = self.dilated(y)
|
| 48 |
+
y = self.proj_out(y)
|
| 49 |
+
return self.relu(x + y)
|
| 50 |
+
|
| 51 |
+
|
| 52 |
+
class SmallUNetColorizer(
|
| 53 |
+
nn.Module,
|
| 54 |
+
PyTorchModelHubMixin,
|
| 55 |
+
pipeline_tag="image-to-image",
|
| 56 |
+
license="apache-2.0",
|
| 57 |
+
tags=["colorization", "unet", "image-to-image", "classification"],
|
| 58 |
+
):
|
| 59 |
+
def __init__(self, bin_centers, in_ch: int = 1, base: int = 44,
|
| 60 |
+
context_mid_ch: int = 96, context_dilations=(2, 4, 8)):
|
| 61 |
+
super().__init__()
|
| 62 |
+
self.in_ch, self.base = in_ch, base
|
| 63 |
+
num_bins = len(bin_centers)
|
| 64 |
+
self.num_bins = num_bins
|
| 65 |
+
self.register_buffer("bin_centers", torch.tensor(bin_centers, dtype=torch.float32))
|
| 66 |
+
|
| 67 |
+
self.enc1 = double_conv(in_ch, base)
|
| 68 |
+
self.enc2 = double_conv(base, base * 2)
|
| 69 |
+
self.enc3 = double_conv(base * 2, base * 4)
|
| 70 |
+
self.enc4 = double_conv(base * 4, base * 8)
|
| 71 |
+
self.pool = nn.MaxPool2d(2)
|
| 72 |
+
self.context = DilatedContextBlock(base * 8, mid_ch=context_mid_ch,
|
| 73 |
+
dilations=tuple(context_dilations))
|
| 74 |
+
self.up3 = nn.ConvTranspose2d(base * 8, base * 4, 2, stride=2)
|
| 75 |
+
self.dec3 = double_conv(base * 8, base * 4)
|
| 76 |
+
self.up2 = nn.ConvTranspose2d(base * 4, base * 2, 2, stride=2)
|
| 77 |
+
self.dec2 = double_conv(base * 4, base * 2)
|
| 78 |
+
self.up1 = nn.ConvTranspose2d(base * 2, base, 2, stride=2)
|
| 79 |
+
self.dec1 = double_conv(base * 2, base)
|
| 80 |
+
self.out_conv = nn.Conv2d(base, num_bins, 1)
|
| 81 |
+
|
| 82 |
+
def forward(self, x):
|
| 83 |
+
h, w = x.shape[-2:]
|
| 84 |
+
x = F.pad(x, (0, (-w) % 8, 0, (-h) % 8), mode="replicate")
|
| 85 |
+
e1 = self.enc1(x)
|
| 86 |
+
e2 = self.enc2(self.pool(e1))
|
| 87 |
+
e3 = self.enc3(self.pool(e2))
|
| 88 |
+
e4 = self.context(self.enc4(self.pool(e3)))
|
| 89 |
+
d3 = self.dec3(torch.cat([self.up3(e4), e3], dim=1))
|
| 90 |
+
d2 = self.dec2(torch.cat([self.up2(d3), e2], dim=1))
|
| 91 |
+
d1 = self.dec1(torch.cat([self.up1(d2), e1], dim=1))
|
| 92 |
+
return self.out_conv(d1)[..., :h, :w]
|
| 93 |
+
|
| 94 |
+
def decode(self, logits, temperature: float = 0.38):
|
| 95 |
+
if not math.isfinite(temperature) or temperature <= 0:
|
| 96 |
+
raise ValueError("temperature must be finite and positive")
|
| 97 |
+
probs_t = F.softmax(logits.float() / temperature, dim=1)
|
| 98 |
+
return torch.einsum("bqhw,qc->bchw", probs_t, self.bin_centers)
|
| 99 |
+
|
| 100 |
+
|
| 101 |
+
def load_model(source, revision=None, device="cpu"):
|
| 102 |
+
path = Path(source)
|
| 103 |
+
if not path.is_dir():
|
| 104 |
+
path = Path(snapshot_download(source, revision=revision,
|
| 105 |
+
allow_patterns=["config.json", "model.safetensors"]))
|
| 106 |
+
config = json.loads((path / "config.json").read_text())
|
| 107 |
+
state = load_file(str(path / "model.safetensors"))
|
| 108 |
+
centers = torch.tensor(config["bin_centers"], dtype=torch.float32)
|
| 109 |
+
if not torch.equal(centers, state["bin_centers"]):
|
| 110 |
+
raise ValueError("Checkpoint config and state color bins differ; refusing ambiguous decode")
|
| 111 |
+
model = SmallUNetColorizer(**config)
|
| 112 |
+
model.load_state_dict(state, strict=True)
|
| 113 |
+
model.to(device).eval()
|
| 114 |
+
return model
|
| 115 |
+
|
| 116 |
+
def save_model(model, path):
|
| 117 |
+
path = Path(path); path.mkdir(parents=True, exist_ok=True)
|
| 118 |
+
model.save_pretrained(path)
|
| 119 |
+
# Mixin config can retain constructor bins; use the actual authoritative buffer.
|
| 120 |
+
cfg = json.loads((path / "config.json").read_text())
|
| 121 |
+
cfg["bin_centers"] = model.bin_centers.detach().cpu().tolist()
|
| 122 |
+
(path / "config.json").write_text(json.dumps(cfg, indent=2))
|
legacy/v2/requirements-onnx.txt
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
onnxruntime>=1.17
|
| 2 |
+
numpy
|
| 3 |
+
pillow
|
| 4 |
+
scikit-image
|
legacy/v2/requirements.txt
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
torch>=2.3
|
| 2 |
+
huggingface_hub>=0.24
|
| 3 |
+
safetensors>=0.4
|
| 4 |
+
scikit-image>=0.22
|
| 5 |
+
scipy>=1.11
|
| 6 |
+
pillow>=10
|
| 7 |
+
numpy
|
| 8 |
+
pyarrow
|
| 9 |
+
pytest
|
| 10 |
+
datasets
|
| 11 |
+
torchvision>=0.18
|
| 12 |
+
tqdm
|
sample.png → legacy/v2/sample.png
RENAMED
|
File without changes
|
upload_main.py → legacy/v2/upload_main.py
RENAMED
|
File without changes
|
mini-colorizer-v3.zip
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:ec6c3e507c15a74faf869d92d6b80e7374d24e990e95176187483905fba2afc6
|
| 3 |
+
size 29784576
|
model.py
CHANGED
|
@@ -1,122 +1,8 @@
|
|
| 1 |
-
"""Shared, checkpoint-compatible Mini U-Net. Parameters remain under 4M."""
|
| 2 |
-
import json
|
| 3 |
-
import math
|
| 4 |
from pathlib import Path
|
| 5 |
-
import
|
| 6 |
-
import torch
|
| 7 |
-
from torch import nn
|
| 8 |
-
import torch.nn.functional as F
|
| 9 |
-
from huggingface_hub import PyTorchModelHubMixin, snapshot_download
|
| 10 |
-
from safetensors.torch import load_file, save_file
|
| 11 |
-
def double_conv(in_ch, out_ch):
|
| 12 |
-
return nn.Sequential(
|
| 13 |
-
nn.Conv2d(in_ch, out_ch, 3, padding=1, bias=False),
|
| 14 |
-
nn.BatchNorm2d(out_ch),
|
| 15 |
-
nn.ReLU(inplace=True),
|
| 16 |
-
nn.Conv2d(out_ch, out_ch, 3, padding=1, bias=False),
|
| 17 |
-
nn.BatchNorm2d(out_ch),
|
| 18 |
-
nn.ReLU(inplace=True),
|
| 19 |
-
)
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
class DilatedContextBlock(nn.Module):
|
| 23 |
-
def __init__(self, channels, mid_ch=96, dilations=(2, 4, 8)):
|
| 24 |
-
super().__init__()
|
| 25 |
-
self.proj_in = nn.Sequential(
|
| 26 |
-
nn.Conv2d(channels, mid_ch, 1, bias=False),
|
| 27 |
-
nn.BatchNorm2d(mid_ch),
|
| 28 |
-
nn.ReLU(inplace=True),
|
| 29 |
-
)
|
| 30 |
-
layers = []
|
| 31 |
-
for d in dilations:
|
| 32 |
-
layers += [
|
| 33 |
-
nn.Conv2d(mid_ch, mid_ch, 3, padding=d, dilation=d, bias=False),
|
| 34 |
-
nn.BatchNorm2d(mid_ch),
|
| 35 |
-
nn.ReLU(inplace=True),
|
| 36 |
-
]
|
| 37 |
-
self.dilated = nn.Sequential(*layers)
|
| 38 |
-
self.proj_out = nn.Sequential(
|
| 39 |
-
nn.Conv2d(mid_ch, channels, 1, bias=False),
|
| 40 |
-
nn.BatchNorm2d(channels),
|
| 41 |
-
)
|
| 42 |
-
nn.init.zeros_(self.proj_out[-1].weight)
|
| 43 |
-
self.relu = nn.ReLU(inplace=True)
|
| 44 |
-
|
| 45 |
-
def forward(self, x):
|
| 46 |
-
y = self.proj_in(x)
|
| 47 |
-
y = self.dilated(y)
|
| 48 |
-
y = self.proj_out(y)
|
| 49 |
-
return self.relu(x + y)
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
class SmallUNetColorizer(
|
| 53 |
-
nn.Module,
|
| 54 |
-
PyTorchModelHubMixin,
|
| 55 |
-
pipeline_tag="image-to-image",
|
| 56 |
-
license="apache-2.0",
|
| 57 |
-
tags=["colorization", "unet", "image-to-image", "classification"],
|
| 58 |
-
):
|
| 59 |
-
def __init__(self, bin_centers, in_ch: int = 1, base: int = 44,
|
| 60 |
-
context_mid_ch: int = 96, context_dilations=(2, 4, 8)):
|
| 61 |
-
super().__init__()
|
| 62 |
-
self.in_ch, self.base = in_ch, base
|
| 63 |
-
num_bins = len(bin_centers)
|
| 64 |
-
self.num_bins = num_bins
|
| 65 |
-
self.register_buffer("bin_centers", torch.tensor(bin_centers, dtype=torch.float32))
|
| 66 |
-
|
| 67 |
-
self.enc1 = double_conv(in_ch, base)
|
| 68 |
-
self.enc2 = double_conv(base, base * 2)
|
| 69 |
-
self.enc3 = double_conv(base * 2, base * 4)
|
| 70 |
-
self.enc4 = double_conv(base * 4, base * 8)
|
| 71 |
-
self.pool = nn.MaxPool2d(2)
|
| 72 |
-
self.context = DilatedContextBlock(base * 8, mid_ch=context_mid_ch,
|
| 73 |
-
dilations=tuple(context_dilations))
|
| 74 |
-
self.up3 = nn.ConvTranspose2d(base * 8, base * 4, 2, stride=2)
|
| 75 |
-
self.dec3 = double_conv(base * 8, base * 4)
|
| 76 |
-
self.up2 = nn.ConvTranspose2d(base * 4, base * 2, 2, stride=2)
|
| 77 |
-
self.dec2 = double_conv(base * 4, base * 2)
|
| 78 |
-
self.up1 = nn.ConvTranspose2d(base * 2, base, 2, stride=2)
|
| 79 |
-
self.dec1 = double_conv(base * 2, base)
|
| 80 |
-
self.out_conv = nn.Conv2d(base, num_bins, 1)
|
| 81 |
-
|
| 82 |
-
def forward(self, x):
|
| 83 |
-
h, w = x.shape[-2:]
|
| 84 |
-
x = F.pad(x, (0, (-w) % 8, 0, (-h) % 8), mode="replicate")
|
| 85 |
-
e1 = self.enc1(x)
|
| 86 |
-
e2 = self.enc2(self.pool(e1))
|
| 87 |
-
e3 = self.enc3(self.pool(e2))
|
| 88 |
-
e4 = self.context(self.enc4(self.pool(e3)))
|
| 89 |
-
d3 = self.dec3(torch.cat([self.up3(e4), e3], dim=1))
|
| 90 |
-
d2 = self.dec2(torch.cat([self.up2(d3), e2], dim=1))
|
| 91 |
-
d1 = self.dec1(torch.cat([self.up1(d2), e1], dim=1))
|
| 92 |
-
return self.out_conv(d1)[..., :h, :w]
|
| 93 |
-
|
| 94 |
-
def decode(self, logits, temperature: float = 0.38):
|
| 95 |
-
if not math.isfinite(temperature) or temperature <= 0:
|
| 96 |
-
raise ValueError("temperature must be finite and positive")
|
| 97 |
-
probs_t = F.softmax(logits.float() / temperature, dim=1)
|
| 98 |
-
return torch.einsum("bqhw,qc->bchw", probs_t, self.bin_centers)
|
| 99 |
-
|
| 100 |
-
|
| 101 |
def load_model(source, revision=None, device="cpu"):
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
|
| 107 |
-
state = load_file(str(path / "model.safetensors"))
|
| 108 |
-
centers = torch.tensor(config["bin_centers"], dtype=torch.float32)
|
| 109 |
-
if not torch.equal(centers, state["bin_centers"]):
|
| 110 |
-
raise ValueError("Checkpoint config and state color bins differ; refusing ambiguous decode")
|
| 111 |
-
model = SmallUNetColorizer(**config)
|
| 112 |
-
model.load_state_dict(state, strict=True)
|
| 113 |
-
model.to(device).eval()
|
| 114 |
-
return model
|
| 115 |
-
|
| 116 |
-
def save_model(model, path):
|
| 117 |
-
path = Path(path); path.mkdir(parents=True, exist_ok=True)
|
| 118 |
-
model.save_pretrained(path)
|
| 119 |
-
# Mixin config can retain constructor bins; use the actual authoritative buffer.
|
| 120 |
-
cfg = json.loads((path / "config.json").read_text())
|
| 121 |
-
cfg["bin_centers"] = model.bin_centers.detach().cpu().tolist()
|
| 122 |
-
(path / "config.json").write_text(json.dumps(cfg, indent=2))
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
from pathlib import Path
|
| 2 |
+
from semantic_model import SemanticColorizer, load_semantic, save_semantic
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3 |
def load_model(source, revision=None, device="cpu"):
|
| 4 |
+
from huggingface_hub import snapshot_download
|
| 5 |
+
path=Path(source)
|
| 6 |
+
if not path.is_dir(): path=Path(snapshot_download(source,revision=revision,allow_patterns=["config.json","model.safetensors"]))
|
| 7 |
+
return load_semantic(path,device)
|
| 8 |
+
save_model=save_semantic
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
model.safetensors
CHANGED
|
@@ -1,3 +1,3 @@
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
-
oid sha256:
|
| 3 |
-
size
|
|
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:ec1f27d74533adc83f7ab3639a091fc4d8738a434dafc7d172c7873c28a9e715
|
| 3 |
+
size 16112432
|
requirements-onnx.txt
CHANGED
|
@@ -1,4 +1,5 @@
|
|
| 1 |
-
onnxruntime
|
| 2 |
-
|
| 3 |
-
|
| 4 |
-
|
|
|
|
|
|
| 1 |
+
onnxruntime==1.20.1
|
| 2 |
+
onnx==1.17.0
|
| 3 |
+
numpy==1.26.4
|
| 4 |
+
Pillow==11.1.0
|
| 5 |
+
scikit-image==0.25.2
|
requirements-space.txt
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
torch==2.8.0
|
| 2 |
+
torchvision==0.23.0
|
| 3 |
+
numpy==1.26.4
|
| 4 |
+
Pillow==11.1.0
|
| 5 |
+
scikit-image==0.25.2
|
| 6 |
+
safetensors==0.5.3
|
| 7 |
+
huggingface-hub>=1.0,<2
|
| 8 |
+
gradio==6.28.0
|
| 9 |
+
spaces==0.51.3
|
requirements.txt
CHANGED
|
@@ -1,12 +1,7 @@
|
|
| 1 |
-
torch
|
| 2 |
-
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
pyarrow
|
| 9 |
-
pytest
|
| 10 |
-
datasets
|
| 11 |
-
torchvision>=0.18
|
| 12 |
-
tqdm
|
|
|
|
| 1 |
+
torch==2.8.0
|
| 2 |
+
torchvision==0.23.0
|
| 3 |
+
numpy==1.26.4
|
| 4 |
+
Pillow==11.1.0
|
| 5 |
+
scikit-image==0.25.2
|
| 6 |
+
safetensors==0.5.3
|
| 7 |
+
huggingface-hub>=1.0,<2
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
semantic_model.py
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Compact pretrained semantic colorizer; dense and shared-palette variants."""
|
| 2 |
+
import json
|
| 3 |
+
from pathlib import Path
|
| 4 |
+
import torch
|
| 5 |
+
from torch import nn
|
| 6 |
+
import torch.nn.functional as F
|
| 7 |
+
from torchvision.models import mobilenet_v3_large, MobileNet_V3_Large_Weights
|
| 8 |
+
from safetensors.torch import save_file, load_file
|
| 9 |
+
|
| 10 |
+
class QueryBlock(nn.Module):
|
| 11 |
+
def __init__(self,d=96):
|
| 12 |
+
super().__init__()
|
| 13 |
+
self.self_attn=nn.MultiheadAttention(d,4,batch_first=True,dropout=0)
|
| 14 |
+
self.cross_attn=nn.MultiheadAttention(d,4,batch_first=True,dropout=0)
|
| 15 |
+
self.norms=nn.ModuleList([nn.LayerNorm(d) for _ in range(3)])
|
| 16 |
+
self.ff=nn.Sequential(nn.Linear(d,2*d),nn.GELU(),nn.Linear(2*d,d))
|
| 17 |
+
def forward(self,q,memory):
|
| 18 |
+
x=self.norms[0](q);q=q+self.self_attn(x,x,x,need_weights=False)[0]
|
| 19 |
+
x=self.norms[1](q);q=q+self.cross_attn(x,memory,memory,need_weights=False)[0]
|
| 20 |
+
return q+self.ff(self.norms[2](q))
|
| 21 |
+
|
| 22 |
+
def refine(d):
|
| 23 |
+
return nn.Sequential(nn.Conv2d(d,d,3,padding=1,bias=False),nn.GroupNorm(8,d),nn.SiLU())
|
| 24 |
+
|
| 25 |
+
class SemanticColorizer(nn.Module):
|
| 26 |
+
def __init__(self,head='palette',pretrained=False,width=128,queries=16):
|
| 27 |
+
super().__init__()
|
| 28 |
+
if head not in ['palette','dense']:raise ValueError(head)
|
| 29 |
+
self.config={'architecture':'SemanticColorizer','head':head,'width':width,'queries':queries,'format_version':1}
|
| 30 |
+
self.encoder=mobilenet_v3_large(weights=MobileNet_V3_Large_Weights.IMAGENET1K_V2 if pretrained else None,progress=False).features
|
| 31 |
+
self.lateral=nn.ModuleList([nn.Conv2d(c,width,1) for c in [24,40,112,960]])
|
| 32 |
+
self.refine=nn.ModuleList([refine(width) for _ in range(3)])
|
| 33 |
+
self.register_buffer('rgb_mean',torch.tensor([.485,.456,.406]).view(1,3,1,1))
|
| 34 |
+
self.register_buffer('rgb_std',torch.tensor([.229,.224,.225]).view(1,3,1,1))
|
| 35 |
+
if head=='palette':
|
| 36 |
+
self.queries=nn.Parameter(torch.randn(queries,width)*.2)
|
| 37 |
+
self.query_blocks=nn.ModuleList([QueryBlock(width) for _ in range(2)])
|
| 38 |
+
self.memory_norm=nn.LayerNorm(width)
|
| 39 |
+
self.query_norm=nn.LayerNorm(width)
|
| 40 |
+
self.pixel=nn.Conv2d(width,width,1)
|
| 41 |
+
self.palette=nn.Sequential(nn.Linear(width,width),nn.GELU(),nn.Linear(width,2))
|
| 42 |
+
self.residual=nn.Conv2d(width,2,1)
|
| 43 |
+
nn.init.normal_(self.palette[-1].weight,std=.01);nn.init.zeros_(self.palette[-1].bias)
|
| 44 |
+
nn.init.zeros_(self.residual.weight);nn.init.zeros_(self.residual.bias)
|
| 45 |
+
else:
|
| 46 |
+
self.dense=nn.Sequential(refine(width),nn.Conv2d(width,2,1))
|
| 47 |
+
nn.init.normal_(self.dense[-1].weight,std=.01);nn.init.zeros_(self.dense[-1].bias)
|
| 48 |
+
count=sum(p.numel() for p in self.parameters())
|
| 49 |
+
if count>=4_000_000:raise ValueError(f'Parameter budget exceeded: {count}')
|
| 50 |
+
|
| 51 |
+
@staticmethod
|
| 52 |
+
def neutral_rgb(L):
|
| 53 |
+
light=(L.float()*50+50).clamp(0,100)
|
| 54 |
+
y=torch.where(light>8,((light+16)/116)**3,light/903.296296)
|
| 55 |
+
g=torch.where(y<=.0031308,12.92*y,1.055*y.clamp_min(1e-8).pow(1/2.4)-.055)
|
| 56 |
+
return g.expand(-1,3,-1,-1)
|
| 57 |
+
|
| 58 |
+
def forward(self,L):
|
| 59 |
+
h,w=L.shape[-2:]
|
| 60 |
+
x=F.pad(self.neutral_rgb(L),(0,(-w)%32,0,(-h)%32),mode='replicate')
|
| 61 |
+
x=(x-self.rgb_mean)/self.rgb_std
|
| 62 |
+
features=[]
|
| 63 |
+
for i,layer in enumerate(self.encoder):
|
| 64 |
+
x=layer(x)
|
| 65 |
+
if i in [3,6,12,16]:features.append(x)
|
| 66 |
+
projected=[layer(f) for layer,f in zip(self.lateral,features)]
|
| 67 |
+
x=projected[-1]
|
| 68 |
+
for i in range(2,-1,-1):
|
| 69 |
+
x=self.refine[2-i](F.interpolate(x,size=projected[i].shape[-2:],mode='bilinear',align_corners=False)+projected[i])
|
| 70 |
+
if self.config['head']=='palette':
|
| 71 |
+
memory=torch.cat([F.adaptive_avg_pool2d(f,(8,8)).flatten(2).transpose(1,2) for f in projected[1:]],1)
|
| 72 |
+
memory=self.memory_norm(memory)
|
| 73 |
+
q=self.queries[None].expand(L.shape[0],-1,-1)
|
| 74 |
+
for block in self.query_blocks:q=block(q,memory)
|
| 75 |
+
q=self.query_norm(q)
|
| 76 |
+
palette=80*torch.tanh(self.palette(q))
|
| 77 |
+
masks=torch.einsum('bqd,bdhw->bqhw',q,self.pixel(x))/(self.config['width']**.5)
|
| 78 |
+
weights=F.softmax(masks.float(),dim=1)
|
| 79 |
+
ab=torch.einsum('bqhw,bqc->bchw',weights,palette.float())+2*torch.tanh(self.residual(x).float())
|
| 80 |
+
else:ab=80*torch.tanh(self.dense(x).float())
|
| 81 |
+
return F.interpolate(ab,size=(x.shape[-2]*4,x.shape[-1]*4),mode='bilinear',align_corners=False)[...,:h,:w]
|
| 82 |
+
def decode(self,z,temperature=.38):return z.float()
|
| 83 |
+
|
| 84 |
+
def save_semantic(model,path):
|
| 85 |
+
path=Path(path);path.mkdir(parents=True,exist_ok=True)
|
| 86 |
+
save_file({k:v.detach().cpu().contiguous() for k,v in model.state_dict().items()},str(path/'model.safetensors'))
|
| 87 |
+
(path/'config.json').write_text(json.dumps(model.config,indent=2))
|
| 88 |
+
(path/'README.md').write_text('# Experimental semantic colorizer\n\nHead: '+model.config['head']+'. Parameters: '+str(sum(p.numel() for p in model.parameters()))+'.\n\nNot approved for production. Requires semantic_model.py; incompatible with the old U-Net loader. Input is Lab lightness normalized to [-1,1]; output is Lab ab. See the run protocol, provenance, selection and visual comparisons. Predictions are plausible colors, not recovered historical truth.\n')
|
| 89 |
+
|
| 90 |
+
def load_semantic(path,device='cpu'):
|
| 91 |
+
path=Path(path);cfg=json.loads((path/'config.json').read_text())
|
| 92 |
+
model=SemanticColorizer(**{k:cfg[k] for k in ['head','width','queries']})
|
| 93 |
+
model.load_state_dict(load_file(str(path/'model.safetensors')),strict=True)
|
| 94 |
+
return model.to(device).eval()
|