hdrviz 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- hdrviz-0.1.0/.gitignore +39 -0
- hdrviz-0.1.0/LICENSE +21 -0
- hdrviz-0.1.0/PKG-INFO +170 -0
- hdrviz-0.1.0/README.md +137 -0
- hdrviz-0.1.0/hdrviz.py +544 -0
- hdrviz-0.1.0/pyproject.toml +66 -0
hdrviz-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
*.so
|
|
6
|
+
.Python
|
|
7
|
+
|
|
8
|
+
# Build
|
|
9
|
+
build/
|
|
10
|
+
dist/
|
|
11
|
+
*.egg-info/
|
|
12
|
+
.eggs/
|
|
13
|
+
*.egg
|
|
14
|
+
|
|
15
|
+
# Tests
|
|
16
|
+
.pytest_cache/
|
|
17
|
+
.coverage
|
|
18
|
+
htmlcov/
|
|
19
|
+
.tox/
|
|
20
|
+
|
|
21
|
+
# Environments
|
|
22
|
+
.venv/
|
|
23
|
+
venv/
|
|
24
|
+
env/
|
|
25
|
+
.env
|
|
26
|
+
|
|
27
|
+
# Editors
|
|
28
|
+
.idea/
|
|
29
|
+
.vscode/
|
|
30
|
+
*.swp
|
|
31
|
+
*.swo
|
|
32
|
+
*~
|
|
33
|
+
|
|
34
|
+
# OS
|
|
35
|
+
.DS_Store
|
|
36
|
+
Thumbs.db
|
|
37
|
+
|
|
38
|
+
# uv
|
|
39
|
+
uv.lock
|
hdrviz-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Konstantin Taletskiy
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
hdrviz-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: hdrviz
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: HDR data visualization for the browser, from numpy arrays.
|
|
5
|
+
Project-URL: Homepage, https://github.com/ktaletsk/hdrviz
|
|
6
|
+
Project-URL: Repository, https://github.com/ktaletsk/hdrviz
|
|
7
|
+
Project-URL: Issues, https://github.com/ktaletsk/hdrviz/issues
|
|
8
|
+
Author-email: Konstantin Taletskiy <konstantin@taletskiy.com>
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: anywidget,hdr,jupyter,marimo,numpy,pq,rec2020,smpte-2084,visualization
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Framework :: Jupyter
|
|
14
|
+
Classifier: Intended Audience :: Science/Research
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
22
|
+
Classifier: Topic :: Multimedia :: Graphics
|
|
23
|
+
Classifier: Topic :: Scientific/Engineering :: Visualization
|
|
24
|
+
Requires-Python: >=3.11
|
|
25
|
+
Requires-Dist: anywidget>=0.9
|
|
26
|
+
Requires-Dist: colour-science>=0.4
|
|
27
|
+
Requires-Dist: numpy>=1.24
|
|
28
|
+
Requires-Dist: pillow>=10
|
|
29
|
+
Requires-Dist: traitlets>=5
|
|
30
|
+
Provides-Extra: dev
|
|
31
|
+
Requires-Dist: pytest>=7; extra == 'dev'
|
|
32
|
+
Description-Content-Type: text/markdown
|
|
33
|
+
|
|
34
|
+
<h1>
|
|
35
|
+
<p align="center">
|
|
36
|
+
<img width="150" height="150" alt="logo" src="https://github.com/user-attachments/assets/b82d7db9-6378-45cd-bb12-d3aec6645938" />
|
|
37
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1000 1000" role="img" aria-label="hdrviz logo">
|
|
38
|
+
<rect width="1000" height="1000" fill="#a83817"/>
|
|
39
|
+
<path d="M 208.74 -0.28 L 213.71 3.93 L 217.84 8.82 L 221.23 14.32 L 223.99 20.35 L 226.22 26.83 L 228.02 33.68 L 229.50 40.83 L 230.75 48.20 L 231.89 55.71 L 233.01 63.28 L 234.21 70.83 L 235.60 78.28 L 237.28 85.56 L 239.36 92.59 L 241.93 99.29 L 245.09 105.59 L 248.85 111.46 L 253.17 116.90 L 257.99 121.93 L 263.29 126.54 L 269.01 130.73 L 275.11 134.53 L 281.54 137.92 L 288.27 140.92 L 295.25 143.53 L 302.44 145.75 L 309.78 147.59 L 317.25 149.06 L 324.78 150.15 L 332.35 150.87 L 339.91 151.24 L 347.42 151.25 L 354.88 150.95 L 362.30 150.37 L 369.69 149.56 L 377.06 148.54 L 384.40 147.37 L 391.72 146.08 L 399.04 144.70 L 406.35 143.28 L 413.67 141.86 L 420.99 140.47 L 428.33 139.16 L 435.69 137.96 L 443.07 136.90 L 450.49 136.04 L 457.93 135.36 L 465.40 134.87 L 472.89 134.54 L 480.38 134.39 L 487.89 134.40 L 495.40 134.56 L 502.91 134.87 L 510.41 135.31 L 517.90 135.90 L 525.37 136.60 L 532.82 137.43 L 540.23 138.37 L 547.62 139.42 L 554.97 140.56 L 562.27 141.83 L 569.51 143.23 L 576.68 144.80 L 583.78 146.55 L 590.79 148.51 L 597.71 150.69 L 604.53 153.12 L 611.23 155.83 L 617.80 158.83 L 624.25 162.14 L 630.55 165.79 L 636.69 169.80 L 642.68 174.19 L 648.50 178.98 L 654.13 184.20 L 659.62 189.75 L 665.10 195.24 L 670.77 200.22 L 676.78 204.22 L 683.29 206.83 L 690.32 208.07 L 697.76 208.26 L 705.52 207.74 L 713.44 206.91 L 721.20 206.49 L 728.42 207.23 L 734.73 209.88 L 739.85 214.53 L 743.58 220.70 L 745.72 227.89 L 746.14 235.61 L 745.54 243.45 L 745.14 251.07 L 746.12 258.12 L 749.21 264.42 L 753.78 270.20 L 759.03 275.77 L 764.13 281.42 L 768.34 287.45 L 771.52 293.88 L 773.85 300.65 L 775.54 307.70 L 776.75 314.94 L 777.67 322.31 L 778.50 329.74 L 779.40 337.16 L 780.49 344.52 L 781.75 351.83 L 783.16 359.09 L 784.72 366.31 L 786.40 373.48 L 788.19 380.61 L 790.06 387.70 L 792.02 394.75 L 794.03 401.78 L 796.09 408.77 L 798.17 415.74 L 800.26 422.68 L 802.35 429.60 L 804.42 436.51 L 806.45 443.39 L 808.47 450.26 L 810.53 457.08 L 812.68 463.86 L 814.98 470.56 L 817.47 477.18 L 820.20 483.71 L 823.24 490.12 L 826.62 496.41 L 830.30 502.59 L 834.18 508.72 L 838.17 514.84 L 842.17 521.00 L 846.08 527.24 L 849.81 533.60 L 853.26 540.14 L 856.34 546.88 L 858.92 553.79 L 860.86 560.75 L 862.03 567.63 L 862.28 574.31 L 861.47 580.68 L 859.46 586.60 L 856.11 591.96 L 851.42 596.72 L 845.66 601.01 L 839.15 605.00 L 832.19 608.84 L 825.10 612.69 L 818.20 616.71 L 811.80 621.07 L 806.21 625.92 L 801.68 631.28 L 797.55 635.13 L 792.48 634.06 L 786.25 628.69 L 779.50 622.78 L 772.74 619.13 L 766.27 619.23 L 760.38 622.98 L 755.36 628.59 L 751.17 634.76 L 747.32 640.96 L 743.31 646.76 L 738.61 651.69 L 732.95 655.56 L 726.64 658.75 L 720.07 661.74 L 713.65 665.01 L 707.61 668.84 L 702.00 673.21 L 696.84 678.05 L 692.14 683.33 L 687.93 688.99 L 684.21 694.98 L 681.02 701.25 L 678.36 707.74 L 676.24 714.41 L 674.57 721.22 L 673.19 728.14 L 671.98 735.13 L 670.79 742.15 L 669.50 749.18 L 667.95 756.18 L 666.04 763.12 L 663.71 769.97 L 661.00 776.74 L 657.92 783.41 L 654.49 789.98 L 650.73 796.43 L 646.66 802.76 L 642.29 808.95 L 637.65 815.01 L 632.75 820.84 L 627.62 826.30 L 622.28 831.25 L 616.76 835.54 L 611.07 839.01 L 605.23 841.51 L 599.28 842.90 L 593.23 843.03 L 587.09 841.85 L 580.87 839.56 L 574.55 836.39 L 568.13 832.57 L 561.60 828.33 L 554.95 823.90 L 548.19 819.52 L 541.30 815.40 L 534.29 811.76 L 527.21 808.68 L 520.13 806.25 L 513.11 804.56 L 506.21 803.68 L 499.49 803.71 L 493.01 804.72 L 486.83 806.80 L 480.96 809.89 L 475.37 813.86 L 470.02 818.53 L 464.87 823.75 L 459.88 829.38 L 455.02 835.24 L 450.25 841.19 L 445.54 847.07 L 440.87 852.78 L 436.31 858.39 L 431.93 863.97 L 427.80 869.60 L 423.99 875.35 L 420.57 881.29 L 417.60 887.51 L 415.16 894.08 L 413.28 901.05 L 411.81 908.32 L 410.55 915.78 L 409.30 923.29 L 407.85 930.74 L 406.00 937.99 L 403.56 944.91 L 400.33 951.41 L 396.40 957.50 L 392.00 963.35 L 387.38 969.08 L 382.76 974.84 L 378.41 980.78 L 374.55 987.03 L 371.42 993.74 L 369.28 1001.05 L 1000.00 1001.05 L 1000.00 0 Z" fill="#e8a020"/>
|
|
40
|
+
<circle cx="435.4" cy="59.6" r="9.6" fill="#ffffff"/>
|
|
41
|
+
<circle cx="314.8" cy="109.7" r="8.5" fill="#ffffff"/>
|
|
42
|
+
<circle cx="557.0" cy="91.6" r="10.9" fill="#ffffff"/>
|
|
43
|
+
<circle cx="671.7" cy="63.5" r="9.3" fill="#ffffff"/>
|
|
44
|
+
<circle cx="750.5" cy="55.3" r="8.7" fill="#ffffff"/>
|
|
45
|
+
<circle cx="943.9" cy="250.1" r="9.0" fill="#ffffff"/>
|
|
46
|
+
<circle cx="807.4" cy="657.9" r="10.1" fill="#ffffff"/>
|
|
47
|
+
<circle cx="908.9" cy="821.6" r="8.3" fill="#ffffff"/>
|
|
48
|
+
<circle cx="932.1" cy="599.6" r="9.3" fill="#ffffff"/>
|
|
49
|
+
<circle cx="979.5" cy="504.5" r="9.7" fill="#ffffff"/>
|
|
50
|
+
<circle cx="923.0" cy="914.5" r="10.3" fill="#ffffff"/>
|
|
51
|
+
<circle cx="854.1" cy="434.8" r="9.7" fill="#ffffff"/>
|
|
52
|
+
<circle cx="550.2" cy="925.4" r="7.7" fill="#ffffff"/>
|
|
53
|
+
<circle cx="885.7" cy="885.5" r="8.0" fill="#ffffff"/>
|
|
54
|
+
<circle cx="715.9" cy="974.6" r="7.3" fill="#ffffff"/>
|
|
55
|
+
<circle cx="937.5" cy="968.3" r="10.5" fill="#ffffff"/>
|
|
56
|
+
<circle cx="618.0" cy="415.4" r="7.4" fill="#0a0202"/>
|
|
57
|
+
<circle cx="787.6" cy="605.2" r="6.6" fill="#0a0202"/>
|
|
58
|
+
<circle cx="589.3" cy="351.2" r="6.9" fill="#0a0202"/>
|
|
59
|
+
<circle cx="675.8" cy="476.2" r="7.1" fill="#0a0202"/>
|
|
60
|
+
<circle cx="541.1" cy="416.6" r="7.5" fill="#0a0202"/>
|
|
61
|
+
<circle cx="698.6" cy="603.6" r="8.8" fill="#0a0202"/>
|
|
62
|
+
<circle cx="729.3" cy="288.9" r="9.6" fill="#0a0202"/>
|
|
63
|
+
<circle cx="763.4" cy="407.3" r="7.6" fill="#0a0202"/>
|
|
64
|
+
<circle cx="569.0" cy="492.0" r="6.2" fill="#0a0202"/>
|
|
65
|
+
<circle cx="554.7" cy="270.1" r="6.6" fill="#0a0202"/>
|
|
66
|
+
<circle cx="547.1" cy="576.0" r="8.5" fill="#0a0202"/>
|
|
67
|
+
<circle cx="473.1" cy="598.0" r="7.4" fill="#0a0202"/>
|
|
68
|
+
<circle cx="535.6" cy="568.3" r="9.4" fill="#0a0202"/>
|
|
69
|
+
<circle cx="718.0" cy="647.2" r="7.9" fill="#0a0202"/>
|
|
70
|
+
<circle cx="670.4" cy="577.1" r="6.1" fill="#0a0202"/>
|
|
71
|
+
<circle cx="472.5" cy="664.9" r="6.1" fill="#0a0202"/>
|
|
72
|
+
<circle cx="583.2" cy="765.1" r="9.5" fill="#0a0202"/>
|
|
73
|
+
<circle cx="653.9" cy="662.5" r="9.1" fill="#0a0202"/>
|
|
74
|
+
<path d="M615.0,48.7 L624.2,90.8 L666.3,100.0 L624.2,109.2 L615.0,151.3 L605.8,109.2 L563.7,100.0 L605.8,90.8 Z" fill="#ffffff"/>
|
|
75
|
+
<path d="M910.0,199.8 L922.6,257.4 L980.2,270.0 L922.6,282.6 L910.0,340.2 L897.4,282.6 L839.8,270.0 L897.4,257.4 Z" fill="#ffffff"/>
|
|
76
|
+
<path d="M965.0,379.9 L971.3,408.7 L1000.1,415.0 L971.3,421.3 L965.0,450.1 L958.7,421.3 L929.9,415.0 L958.7,408.7 Z" fill="#ffffff"/>
|
|
77
|
+
<path d="M590.0,611.4 L598.7,651.3 L638.6,660.0 L598.7,668.7 L590.0,708.6 L581.3,668.7 L541.4,660.0 L581.3,651.3 Z" fill="#ffffff"/>
|
|
78
|
+
<image x="500" y="0" width="500" height="1000" preserveAspectRatio="none" href="data:image/png;base64,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"/>
|
|
79
|
+
<line x1="500" y1="0" x2="500" y2="1000" stroke="#ffffff" stroke-width="4" opacity="0.95"/>
|
|
80
|
+
</svg>
|
|
81
|
+
hdrviz
|
|
82
|
+
</p>
|
|
83
|
+
</h1>
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
<p align="center">Data visualization in HDR.</p>
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
## About
|
|
90
|
+
|
|
91
|
+
`hdrviz` is a minimal Python library (~250 lines) that renders 2D numpy arrays as PQ Rec2020-tagged PNGs for HDR-capable browsers.
|
|
92
|
+
|
|
93
|
+
If your data has more dynamic range than 8-bit color can show — astrophotography, fluorescence microscopy, fractals, log-magnitude FFTs, density maps — `hdrviz` lets displays render the better contrast that's actually in the data.
|
|
94
|
+
|
|
95
|
+
This library is an encoder plus a reference notebook widget. For axes, colorbars, channel mixing, pan/zoom — compose with matplotlib, plotly, or viv.
|
|
96
|
+
|
|
97
|
+
## Install
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
pip install hdrviz
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Quick start
|
|
104
|
+
|
|
105
|
+
```python
|
|
106
|
+
import numpy as np
|
|
107
|
+
import hdrviz as hv
|
|
108
|
+
|
|
109
|
+
data = np.random.RandomState(0).rand(400, 600)
|
|
110
|
+
widget = hv.imshow(data, cmap="inferno-hdr", peak_nits=4000)
|
|
111
|
+
widget # display in a notebook
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Lower-level API
|
|
115
|
+
|
|
116
|
+
`imshow` is a convenience wrapper over a three-step pipeline: apply a colormap to map normalized data to RGB linear-light luminance in cd/m² (nits), run the SMPTE ST 2084 inverse EOTF (the "PQ encoding," via [colour-science](https://www.colour-science.org/)), and write a PNG with an embedded PQ Rec2020 ICC profile that browsers know how to composite in extended dynamic range.
|
|
117
|
+
|
|
118
|
+
```python
|
|
119
|
+
from hdrviz import hdr_colormap, encode_hdr_png
|
|
120
|
+
|
|
121
|
+
norm = (arr - arr.min()) / (arr.max() - arr.min())
|
|
122
|
+
rgb_nits = hdr_colormap(norm, cmap_name="inferno-hdr", peak_nits=4000)
|
|
123
|
+
png_bytes = encode_hdr_png(rgb_nits) # PQ Rec2020-tagged PNG
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
PNG bytes are the deliverable. Serve them from a backend, write them to disk, embed them in custom HTML — anywhere that wants "an HDR image from a numpy array."
|
|
127
|
+
|
|
128
|
+
## API surface
|
|
129
|
+
|
|
130
|
+
| Symbol | Purpose |
|
|
131
|
+
|---|---|
|
|
132
|
+
| `encode_hdr_png(rgb_nits, icc_profile)` | PNG encoding from linear-light RGB nits |
|
|
133
|
+
| `linear_nits_to_pq(rgb_nits)` | SMPTE ST 2084 inverse EOTF (wraps `colour-science`) |
|
|
134
|
+
| `hdr_colormap(norm, cmap_name, peak_nits)` | apply a named HDR colormap |
|
|
135
|
+
| `extract_icc_from_png(png_bytes)` | pull an ICC profile from a PNG's `iCCP` chunk |
|
|
136
|
+
| `to_data_url(png_bytes)` | inline embedding helper |
|
|
137
|
+
| `COLORMAP_LIBRARY` | seven HDR-aware colormaps (`fire-purple`, `ice`, `twilight-burst`, `matrix-green`, `ember`, `viridis-hdr`, `inferno-hdr`) |
|
|
138
|
+
| `DEFAULT_PQ_REC2020_ICC` | bundled ICC profile (~9 KB, "Rec2020 Gamut with PQ Transfer") |
|
|
139
|
+
| `imshow(arr, cmap, peak_nits, ...)` | quickstart wrapper that returns an `HDRImage` |
|
|
140
|
+
| `class HDRImage(anywidget.AnyWidget)` | reference display widget with an SDR-clamp toggle |
|
|
141
|
+
|
|
142
|
+
## What hdrviz isn't
|
|
143
|
+
|
|
144
|
+
- **Plotting framework** with axes, ticks, labels, colorbars — compose with matplotlib
|
|
145
|
+
- **Multi-channel mixing, contrast sliders, pan/zoom, multi-resolution tiling** — compose with [viv](https://github.com/hms-dbmi/viv), ideally with HDR PNG tiles encoded by `hdrviz`
|
|
146
|
+
- **Animated or video HDR** — waits for `configureHighDynamicRange()` to ship in stable Chromium
|
|
147
|
+
|
|
148
|
+
## Demo notebook
|
|
149
|
+
|
|
150
|
+
[`notebook.py`](./notebook.py) is a marimo notebook with introducing the HDR data visualization idea:
|
|
151
|
+
|
|
152
|
+
- A widget that checks your browser's HDR capabilities
|
|
153
|
+
- An interactive Mandelbrot explorer with HDR colormaps and click-to-zoom
|
|
154
|
+
- The Horsehead Nebula photographic plate from the astropy tutorials archive
|
|
155
|
+
- A fluorescence-microscopy frame from `scikit-image.data.cells3d`, where the membrane channel's ~150× native dynamic range is the showstopper
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
git clone https://github.com/ktaletsk/hdrviz
|
|
159
|
+
cd hdrviz
|
|
160
|
+
marimo edit notebook.py --sandbox
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## Browser support
|
|
164
|
+
|
|
165
|
+
Tested and works in Chromium-based browsers.
|
|
166
|
+
Safari mostly works, but had issues with some images not rendering in HDR.
|
|
167
|
+
|
|
168
|
+
## License
|
|
169
|
+
|
|
170
|
+
MIT — see [LICENSE](./LICENSE).
|
hdrviz-0.1.0/README.md
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
<h1>
|
|
2
|
+
<p align="center">
|
|
3
|
+
<img width="150" height="150" alt="logo" src="https://github.com/user-attachments/assets/b82d7db9-6378-45cd-bb12-d3aec6645938" />
|
|
4
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1000 1000" role="img" aria-label="hdrviz logo">
|
|
5
|
+
<rect width="1000" height="1000" fill="#a83817"/>
|
|
6
|
+
<path d="M 208.74 -0.28 L 213.71 3.93 L 217.84 8.82 L 221.23 14.32 L 223.99 20.35 L 226.22 26.83 L 228.02 33.68 L 229.50 40.83 L 230.75 48.20 L 231.89 55.71 L 233.01 63.28 L 234.21 70.83 L 235.60 78.28 L 237.28 85.56 L 239.36 92.59 L 241.93 99.29 L 245.09 105.59 L 248.85 111.46 L 253.17 116.90 L 257.99 121.93 L 263.29 126.54 L 269.01 130.73 L 275.11 134.53 L 281.54 137.92 L 288.27 140.92 L 295.25 143.53 L 302.44 145.75 L 309.78 147.59 L 317.25 149.06 L 324.78 150.15 L 332.35 150.87 L 339.91 151.24 L 347.42 151.25 L 354.88 150.95 L 362.30 150.37 L 369.69 149.56 L 377.06 148.54 L 384.40 147.37 L 391.72 146.08 L 399.04 144.70 L 406.35 143.28 L 413.67 141.86 L 420.99 140.47 L 428.33 139.16 L 435.69 137.96 L 443.07 136.90 L 450.49 136.04 L 457.93 135.36 L 465.40 134.87 L 472.89 134.54 L 480.38 134.39 L 487.89 134.40 L 495.40 134.56 L 502.91 134.87 L 510.41 135.31 L 517.90 135.90 L 525.37 136.60 L 532.82 137.43 L 540.23 138.37 L 547.62 139.42 L 554.97 140.56 L 562.27 141.83 L 569.51 143.23 L 576.68 144.80 L 583.78 146.55 L 590.79 148.51 L 597.71 150.69 L 604.53 153.12 L 611.23 155.83 L 617.80 158.83 L 624.25 162.14 L 630.55 165.79 L 636.69 169.80 L 642.68 174.19 L 648.50 178.98 L 654.13 184.20 L 659.62 189.75 L 665.10 195.24 L 670.77 200.22 L 676.78 204.22 L 683.29 206.83 L 690.32 208.07 L 697.76 208.26 L 705.52 207.74 L 713.44 206.91 L 721.20 206.49 L 728.42 207.23 L 734.73 209.88 L 739.85 214.53 L 743.58 220.70 L 745.72 227.89 L 746.14 235.61 L 745.54 243.45 L 745.14 251.07 L 746.12 258.12 L 749.21 264.42 L 753.78 270.20 L 759.03 275.77 L 764.13 281.42 L 768.34 287.45 L 771.52 293.88 L 773.85 300.65 L 775.54 307.70 L 776.75 314.94 L 777.67 322.31 L 778.50 329.74 L 779.40 337.16 L 780.49 344.52 L 781.75 351.83 L 783.16 359.09 L 784.72 366.31 L 786.40 373.48 L 788.19 380.61 L 790.06 387.70 L 792.02 394.75 L 794.03 401.78 L 796.09 408.77 L 798.17 415.74 L 800.26 422.68 L 802.35 429.60 L 804.42 436.51 L 806.45 443.39 L 808.47 450.26 L 810.53 457.08 L 812.68 463.86 L 814.98 470.56 L 817.47 477.18 L 820.20 483.71 L 823.24 490.12 L 826.62 496.41 L 830.30 502.59 L 834.18 508.72 L 838.17 514.84 L 842.17 521.00 L 846.08 527.24 L 849.81 533.60 L 853.26 540.14 L 856.34 546.88 L 858.92 553.79 L 860.86 560.75 L 862.03 567.63 L 862.28 574.31 L 861.47 580.68 L 859.46 586.60 L 856.11 591.96 L 851.42 596.72 L 845.66 601.01 L 839.15 605.00 L 832.19 608.84 L 825.10 612.69 L 818.20 616.71 L 811.80 621.07 L 806.21 625.92 L 801.68 631.28 L 797.55 635.13 L 792.48 634.06 L 786.25 628.69 L 779.50 622.78 L 772.74 619.13 L 766.27 619.23 L 760.38 622.98 L 755.36 628.59 L 751.17 634.76 L 747.32 640.96 L 743.31 646.76 L 738.61 651.69 L 732.95 655.56 L 726.64 658.75 L 720.07 661.74 L 713.65 665.01 L 707.61 668.84 L 702.00 673.21 L 696.84 678.05 L 692.14 683.33 L 687.93 688.99 L 684.21 694.98 L 681.02 701.25 L 678.36 707.74 L 676.24 714.41 L 674.57 721.22 L 673.19 728.14 L 671.98 735.13 L 670.79 742.15 L 669.50 749.18 L 667.95 756.18 L 666.04 763.12 L 663.71 769.97 L 661.00 776.74 L 657.92 783.41 L 654.49 789.98 L 650.73 796.43 L 646.66 802.76 L 642.29 808.95 L 637.65 815.01 L 632.75 820.84 L 627.62 826.30 L 622.28 831.25 L 616.76 835.54 L 611.07 839.01 L 605.23 841.51 L 599.28 842.90 L 593.23 843.03 L 587.09 841.85 L 580.87 839.56 L 574.55 836.39 L 568.13 832.57 L 561.60 828.33 L 554.95 823.90 L 548.19 819.52 L 541.30 815.40 L 534.29 811.76 L 527.21 808.68 L 520.13 806.25 L 513.11 804.56 L 506.21 803.68 L 499.49 803.71 L 493.01 804.72 L 486.83 806.80 L 480.96 809.89 L 475.37 813.86 L 470.02 818.53 L 464.87 823.75 L 459.88 829.38 L 455.02 835.24 L 450.25 841.19 L 445.54 847.07 L 440.87 852.78 L 436.31 858.39 L 431.93 863.97 L 427.80 869.60 L 423.99 875.35 L 420.57 881.29 L 417.60 887.51 L 415.16 894.08 L 413.28 901.05 L 411.81 908.32 L 410.55 915.78 L 409.30 923.29 L 407.85 930.74 L 406.00 937.99 L 403.56 944.91 L 400.33 951.41 L 396.40 957.50 L 392.00 963.35 L 387.38 969.08 L 382.76 974.84 L 378.41 980.78 L 374.55 987.03 L 371.42 993.74 L 369.28 1001.05 L 1000.00 1001.05 L 1000.00 0 Z" fill="#e8a020"/>
|
|
7
|
+
<circle cx="435.4" cy="59.6" r="9.6" fill="#ffffff"/>
|
|
8
|
+
<circle cx="314.8" cy="109.7" r="8.5" fill="#ffffff"/>
|
|
9
|
+
<circle cx="557.0" cy="91.6" r="10.9" fill="#ffffff"/>
|
|
10
|
+
<circle cx="671.7" cy="63.5" r="9.3" fill="#ffffff"/>
|
|
11
|
+
<circle cx="750.5" cy="55.3" r="8.7" fill="#ffffff"/>
|
|
12
|
+
<circle cx="943.9" cy="250.1" r="9.0" fill="#ffffff"/>
|
|
13
|
+
<circle cx="807.4" cy="657.9" r="10.1" fill="#ffffff"/>
|
|
14
|
+
<circle cx="908.9" cy="821.6" r="8.3" fill="#ffffff"/>
|
|
15
|
+
<circle cx="932.1" cy="599.6" r="9.3" fill="#ffffff"/>
|
|
16
|
+
<circle cx="979.5" cy="504.5" r="9.7" fill="#ffffff"/>
|
|
17
|
+
<circle cx="923.0" cy="914.5" r="10.3" fill="#ffffff"/>
|
|
18
|
+
<circle cx="854.1" cy="434.8" r="9.7" fill="#ffffff"/>
|
|
19
|
+
<circle cx="550.2" cy="925.4" r="7.7" fill="#ffffff"/>
|
|
20
|
+
<circle cx="885.7" cy="885.5" r="8.0" fill="#ffffff"/>
|
|
21
|
+
<circle cx="715.9" cy="974.6" r="7.3" fill="#ffffff"/>
|
|
22
|
+
<circle cx="937.5" cy="968.3" r="10.5" fill="#ffffff"/>
|
|
23
|
+
<circle cx="618.0" cy="415.4" r="7.4" fill="#0a0202"/>
|
|
24
|
+
<circle cx="787.6" cy="605.2" r="6.6" fill="#0a0202"/>
|
|
25
|
+
<circle cx="589.3" cy="351.2" r="6.9" fill="#0a0202"/>
|
|
26
|
+
<circle cx="675.8" cy="476.2" r="7.1" fill="#0a0202"/>
|
|
27
|
+
<circle cx="541.1" cy="416.6" r="7.5" fill="#0a0202"/>
|
|
28
|
+
<circle cx="698.6" cy="603.6" r="8.8" fill="#0a0202"/>
|
|
29
|
+
<circle cx="729.3" cy="288.9" r="9.6" fill="#0a0202"/>
|
|
30
|
+
<circle cx="763.4" cy="407.3" r="7.6" fill="#0a0202"/>
|
|
31
|
+
<circle cx="569.0" cy="492.0" r="6.2" fill="#0a0202"/>
|
|
32
|
+
<circle cx="554.7" cy="270.1" r="6.6" fill="#0a0202"/>
|
|
33
|
+
<circle cx="547.1" cy="576.0" r="8.5" fill="#0a0202"/>
|
|
34
|
+
<circle cx="473.1" cy="598.0" r="7.4" fill="#0a0202"/>
|
|
35
|
+
<circle cx="535.6" cy="568.3" r="9.4" fill="#0a0202"/>
|
|
36
|
+
<circle cx="718.0" cy="647.2" r="7.9" fill="#0a0202"/>
|
|
37
|
+
<circle cx="670.4" cy="577.1" r="6.1" fill="#0a0202"/>
|
|
38
|
+
<circle cx="472.5" cy="664.9" r="6.1" fill="#0a0202"/>
|
|
39
|
+
<circle cx="583.2" cy="765.1" r="9.5" fill="#0a0202"/>
|
|
40
|
+
<circle cx="653.9" cy="662.5" r="9.1" fill="#0a0202"/>
|
|
41
|
+
<path d="M615.0,48.7 L624.2,90.8 L666.3,100.0 L624.2,109.2 L615.0,151.3 L605.8,109.2 L563.7,100.0 L605.8,90.8 Z" fill="#ffffff"/>
|
|
42
|
+
<path d="M910.0,199.8 L922.6,257.4 L980.2,270.0 L922.6,282.6 L910.0,340.2 L897.4,282.6 L839.8,270.0 L897.4,257.4 Z" fill="#ffffff"/>
|
|
43
|
+
<path d="M965.0,379.9 L971.3,408.7 L1000.1,415.0 L971.3,421.3 L965.0,450.1 L958.7,421.3 L929.9,415.0 L958.7,408.7 Z" fill="#ffffff"/>
|
|
44
|
+
<path d="M590.0,611.4 L598.7,651.3 L638.6,660.0 L598.7,668.7 L590.0,708.6 L581.3,668.7 L541.4,660.0 L581.3,651.3 Z" fill="#ffffff"/>
|
|
45
|
+
<image x="500" y="0" width="500" height="1000" preserveAspectRatio="none" href="data:image/png;base64,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"/>
|
|
46
|
+
<line x1="500" y1="0" x2="500" y2="1000" stroke="#ffffff" stroke-width="4" opacity="0.95"/>
|
|
47
|
+
</svg>
|
|
48
|
+
hdrviz
|
|
49
|
+
</p>
|
|
50
|
+
</h1>
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
<p align="center">Data visualization in HDR.</p>
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
## About
|
|
57
|
+
|
|
58
|
+
`hdrviz` is a minimal Python library (~250 lines) that renders 2D numpy arrays as PQ Rec2020-tagged PNGs for HDR-capable browsers.
|
|
59
|
+
|
|
60
|
+
If your data has more dynamic range than 8-bit color can show — astrophotography, fluorescence microscopy, fractals, log-magnitude FFTs, density maps — `hdrviz` lets displays render the better contrast that's actually in the data.
|
|
61
|
+
|
|
62
|
+
This library is an encoder plus a reference notebook widget. For axes, colorbars, channel mixing, pan/zoom — compose with matplotlib, plotly, or viv.
|
|
63
|
+
|
|
64
|
+
## Install
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
pip install hdrviz
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Quick start
|
|
71
|
+
|
|
72
|
+
```python
|
|
73
|
+
import numpy as np
|
|
74
|
+
import hdrviz as hv
|
|
75
|
+
|
|
76
|
+
data = np.random.RandomState(0).rand(400, 600)
|
|
77
|
+
widget = hv.imshow(data, cmap="inferno-hdr", peak_nits=4000)
|
|
78
|
+
widget # display in a notebook
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Lower-level API
|
|
82
|
+
|
|
83
|
+
`imshow` is a convenience wrapper over a three-step pipeline: apply a colormap to map normalized data to RGB linear-light luminance in cd/m² (nits), run the SMPTE ST 2084 inverse EOTF (the "PQ encoding," via [colour-science](https://www.colour-science.org/)), and write a PNG with an embedded PQ Rec2020 ICC profile that browsers know how to composite in extended dynamic range.
|
|
84
|
+
|
|
85
|
+
```python
|
|
86
|
+
from hdrviz import hdr_colormap, encode_hdr_png
|
|
87
|
+
|
|
88
|
+
norm = (arr - arr.min()) / (arr.max() - arr.min())
|
|
89
|
+
rgb_nits = hdr_colormap(norm, cmap_name="inferno-hdr", peak_nits=4000)
|
|
90
|
+
png_bytes = encode_hdr_png(rgb_nits) # PQ Rec2020-tagged PNG
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
PNG bytes are the deliverable. Serve them from a backend, write them to disk, embed them in custom HTML — anywhere that wants "an HDR image from a numpy array."
|
|
94
|
+
|
|
95
|
+
## API surface
|
|
96
|
+
|
|
97
|
+
| Symbol | Purpose |
|
|
98
|
+
|---|---|
|
|
99
|
+
| `encode_hdr_png(rgb_nits, icc_profile)` | PNG encoding from linear-light RGB nits |
|
|
100
|
+
| `linear_nits_to_pq(rgb_nits)` | SMPTE ST 2084 inverse EOTF (wraps `colour-science`) |
|
|
101
|
+
| `hdr_colormap(norm, cmap_name, peak_nits)` | apply a named HDR colormap |
|
|
102
|
+
| `extract_icc_from_png(png_bytes)` | pull an ICC profile from a PNG's `iCCP` chunk |
|
|
103
|
+
| `to_data_url(png_bytes)` | inline embedding helper |
|
|
104
|
+
| `COLORMAP_LIBRARY` | seven HDR-aware colormaps (`fire-purple`, `ice`, `twilight-burst`, `matrix-green`, `ember`, `viridis-hdr`, `inferno-hdr`) |
|
|
105
|
+
| `DEFAULT_PQ_REC2020_ICC` | bundled ICC profile (~9 KB, "Rec2020 Gamut with PQ Transfer") |
|
|
106
|
+
| `imshow(arr, cmap, peak_nits, ...)` | quickstart wrapper that returns an `HDRImage` |
|
|
107
|
+
| `class HDRImage(anywidget.AnyWidget)` | reference display widget with an SDR-clamp toggle |
|
|
108
|
+
|
|
109
|
+
## What hdrviz isn't
|
|
110
|
+
|
|
111
|
+
- **Plotting framework** with axes, ticks, labels, colorbars — compose with matplotlib
|
|
112
|
+
- **Multi-channel mixing, contrast sliders, pan/zoom, multi-resolution tiling** — compose with [viv](https://github.com/hms-dbmi/viv), ideally with HDR PNG tiles encoded by `hdrviz`
|
|
113
|
+
- **Animated or video HDR** — waits for `configureHighDynamicRange()` to ship in stable Chromium
|
|
114
|
+
|
|
115
|
+
## Demo notebook
|
|
116
|
+
|
|
117
|
+
[`notebook.py`](./notebook.py) is a marimo notebook with introducing the HDR data visualization idea:
|
|
118
|
+
|
|
119
|
+
- A widget that checks your browser's HDR capabilities
|
|
120
|
+
- An interactive Mandelbrot explorer with HDR colormaps and click-to-zoom
|
|
121
|
+
- The Horsehead Nebula photographic plate from the astropy tutorials archive
|
|
122
|
+
- A fluorescence-microscopy frame from `scikit-image.data.cells3d`, where the membrane channel's ~150× native dynamic range is the showstopper
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
git clone https://github.com/ktaletsk/hdrviz
|
|
126
|
+
cd hdrviz
|
|
127
|
+
marimo edit notebook.py --sandbox
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## Browser support
|
|
131
|
+
|
|
132
|
+
Tested and works in Chromium-based browsers.
|
|
133
|
+
Safari mostly works, but had issues with some images not rendering in HDR.
|
|
134
|
+
|
|
135
|
+
## License
|
|
136
|
+
|
|
137
|
+
MIT — see [LICENSE](./LICENSE).
|
hdrviz-0.1.0/hdrviz.py
ADDED
|
@@ -0,0 +1,544 @@
|
|
|
1
|
+
"""HDR data visualization for the browser, from numpy arrays.
|
|
2
|
+
|
|
3
|
+
Renders 2D numpy arrays as PQ Rec2020-tagged PNGs that HDR-capable browsers
|
|
4
|
+
composite in extended dynamic range. Includes a small library of HDR-aware
|
|
5
|
+
colormaps and an ``imshow``-style helper.
|
|
6
|
+
|
|
7
|
+
Tested in Chromium-based browsers (Chrome, Brave, Edge) on macOS with HDR
|
|
8
|
+
displays. On non-HDR displays the PNG renders correctly in standard range.
|
|
9
|
+
|
|
10
|
+
Quick start::
|
|
11
|
+
|
|
12
|
+
import numpy as np
|
|
13
|
+
from hdrviz import imshow
|
|
14
|
+
|
|
15
|
+
data = np.random.RandomState(0).rand(400, 600)
|
|
16
|
+
widget = imshow(data, cmap="inferno-hdr", peak_nits=4000)
|
|
17
|
+
widget # display in Jupyter / marimo
|
|
18
|
+
|
|
19
|
+
To toggle HDR rendering on or off (same pixels), set CSS
|
|
20
|
+
``dynamic-range-limit: standard`` on the ``<img>`` element. The :class:`HDRImage`
|
|
21
|
+
widget exposes a ``clamp_to_sdr`` traitlet for this.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
# MIT License
|
|
25
|
+
#
|
|
26
|
+
# Copyright (c) 2026 Konstantin Taletskiy
|
|
27
|
+
#
|
|
28
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
29
|
+
# of this software and associated documentation files (the "Software"), to deal
|
|
30
|
+
# in the Software without restriction, including without limitation the rights
|
|
31
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
32
|
+
# copies of the Software, and to permit persons to whom the Software is
|
|
33
|
+
# furnished to do so, subject to the following conditions:
|
|
34
|
+
#
|
|
35
|
+
# The above copyright notice and this permission notice shall be included in
|
|
36
|
+
# all copies or substantial portions of the Software.
|
|
37
|
+
#
|
|
38
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND.
|
|
39
|
+
|
|
40
|
+
from __future__ import annotations
|
|
41
|
+
|
|
42
|
+
import base64
|
|
43
|
+
import io
|
|
44
|
+
import struct
|
|
45
|
+
import zlib
|
|
46
|
+
|
|
47
|
+
import anywidget
|
|
48
|
+
import colour
|
|
49
|
+
import numpy as np
|
|
50
|
+
import traitlets
|
|
51
|
+
from PIL import Image
|
|
52
|
+
|
|
53
|
+
__version__ = "0.1.0"
|
|
54
|
+
__all__ = [
|
|
55
|
+
"DEFAULT_PQ_REC2020_ICC",
|
|
56
|
+
"COLORMAP_LIBRARY",
|
|
57
|
+
"extract_icc_from_png",
|
|
58
|
+
"linear_nits_to_pq",
|
|
59
|
+
"encode_hdr_png",
|
|
60
|
+
"to_data_url",
|
|
61
|
+
"hdr_colormap",
|
|
62
|
+
"imshow",
|
|
63
|
+
"hdr_imshow", # deprecated alias for imshow, removed in 0.2.0
|
|
64
|
+
"HDRImage",
|
|
65
|
+
]
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
# Bundled ICC profile: "Rec2020 Gamut with PQ Transfer", 9176 bytes raw.
|
|
69
|
+
# This is the standard color space for HDR10 / HDR PNG / UltraHDR carriers.
|
|
70
|
+
# Pass icc_profile=... to encode_hdr_png to use a different one (e.g. HLG).
|
|
71
|
+
_DEFAULT_ICC_B64 = (
|
|
72
|
+
"AAAj2AAAAAAEQAAAbW50clJHQiBYWVogB+AAAQABAAAAAAAAYWNzcAAAAAAAAAAAAAAAAAAAAAAA"
|
|
73
|
+
"AAAAAAAAAAAAAAEAAPbWAAEAAAAA0y0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
|
|
74
|
+
"AAAAAAAAAAAAAAAAAAAAAAAJZGVzYwAAAPAAAABYclhZWgAAAUgAAAAUZ1hZWgAAAVwAAAAUYlhZ"
|
|
75
|
+
"WgAAAXAAAAAUd3RwdAAAAYQAAAAUY2ljcAAAAZgAAAAMQTJCMAAAAaQAACGoQjJBMAAAI0wAAABQ"
|
|
76
|
+
"Y3BydAAAI5wAAAA8bWx1YwAAAAAAAAABAAAADGVuVVMAAAA8AAAAHABSAGUAYwAyADAAMgAwACAA"
|
|
77
|
+
"RwBhAG0AdQB0ACAAdwBpAHQAaAAgAFAAUQAgAFQAcgBhAG4AcwBmAGUAclhZWiAAAAAAAACsaAAA"
|
|
78
|
+
"R2////+BWFlaIAAAAAAAACppAACs4wAAB61YWVogAAAAAAAAIAcAAAuuAADME1hZWiAAAAAAAAD2"
|
|
79
|
+
"1gABAAAAANMtY2ljcAAAAAAJEAABbUFCIAAAAAADAwAAAAAAIAAAIUgAACF4AAAAUAAAH5hwYXJh"
|
|
80
|
+
"AAAAAAAAAAAAAQAAcGFyYQAAAAAAAAAAAAEAAHBhcmEAAAAAAAAAAAABAAALCwsAAAAAAAAAAAAA"
|
|
81
|
+
"AAAAAgAAAAAAAAAAAAAAAAAMzQAAAAAZmgAAAAAmZgAAAAAzMwAAAABAAAAAAABMzQAAAABZmgAA"
|
|
82
|
+
"AABmZgAAAABzMwAAAACAAAAADM0AAAAADM0MzQAADBgZmgAAC1AmZgAACnQzMwAACYNAAAAACH5M"
|
|
83
|
+
"zQAAB2tZmgAABldmZgAABVNzMwAABHCAAAAAGZoAAAAAGZoMGAAAGZoZmgAAGAomZgAAFk0zMwAA"
|
|
84
|
+
"FGJAAAAAEkpMzQAAEA9ZmgAADctmZgAAC6RzMwAACcCAAAAAJmYAAAAAJmYLUAAAJmYYCgAAJmYm"
|
|
85
|
+
"ZgAAI8ozMwAAIOFAAAAAHatMzQAAGjZZmgAAFqdmZgAAEzlzMwAAEC+AAAAAMzMAAAAAMzMKdAAA"
|
|
86
|
+
"MzMWTQAAMzMjygAAMzMzMwAAL09AAAAAKvdMzQAAJjlZmgAAIUZmZgAAHHBzMwAAGBeAAAAAQAAA"
|
|
87
|
+
"AAAAQAAJgwAAQAAUYgAAQAAg4QAAQAAvTwAAQABAAAAAOo5MzQAANIVZmgAALhxmZgAAJ75zMwAA"
|
|
88
|
+
"Ie2AAAAATM0AAAAATM0IfgAATM0SSgAATM0dqwAATM0q9wAATM06jgAATM1MzQAARYpZmgAAPa1m"
|
|
89
|
+
"ZgAANbJzMwAALkSAAAAAWZoAAAAAWZoHawAAWZoQDwAAWZoaNgAAWZomOQAAWZo0hQAAWZpFigAA"
|
|
90
|
+
"WZpZmgAAUGhmZgAARtlzMwAAPb2AAAAAZmYAAAAAZmYGVwAAZmYNywAAZmYWpwAAZmYhRgAAZmYu"
|
|
91
|
+
"HAAAZmY9rQAAZmZQaAAAZmZmZgAAW31zMwAAUMqAAAAAczMAAAAAczMFUwAAczMLpAAAczMTOQAA"
|
|
92
|
+
"czMccAAAczMnvgAAczM1sgAAczNG2QAAczNbfQAAczNzMwAAZz6AAAAAgAAAAAAAgAAEcAAAgAAJ"
|
|
93
|
+
"wAAAgAAQLwAAgAAYFwAAgAAh7QAAgAAuRAAAgAA9vQAAgABQygAAgABnPgAAgACAAAzNAAAAAAzN"
|
|
94
|
+
"AAAMzQwYAAAZmgtQAAAmZgp0AAAzMwmDAABAAAh+AABMzQdrAABZmgZXAABmZgVTAABzMwRwAACA"
|
|
95
|
+
"AAzNDM0AAAzNDM0MzQwYDBgZmgtQC1AmZgp0CnQzMwmDCYNAAAh+CH5MzQdrB2tZmgZXBldmZgVT"
|
|
96
|
+
"BVNzMwRwBHCAAAwYGZoAAAwYGZoMGAwYGZoZmgtQGAomZgp0Fk0zMwmDFGJAAAh+EkpMzQdrEA9Z"
|
|
97
|
+
"mgZXDctmZgVTC6RzMwRwCcCAAAtQJmYAAAtQJmYLUAtQJmYYCgtQJmYmZgp0I8ozMwmDIOFAAAh+"
|
|
98
|
+
"HatMzQdrGjZZmgZXFqdmZgVTEzlzMwRwEC+AAAp0MzMAAAp0MzMKdAp0MzMWTQp0MzMjygp0MzMz"
|
|
99
|
+
"MwmDL09AAAh+KvdMzQdrJjlZmgZXIUZmZgVTHHBzMwRwGBeAAAmDQAAAAAmDQAAJgwmDQAAUYgmD"
|
|
100
|
+
"QAAg4QmDQAAvTwmDQABAAAh+Oo5MzQdrNIVZmgZXLhxmZgVTJ75zMwRwIe2AAAh+TM0AAAh+TM0I"
|
|
101
|
+
"fgh+TM0SSgh+TM0dqwh+TM0q9wh+TM06jgh+TM1MzQdrRYpZmgZXPa1mZgVTNbJzMwRwLkSAAAdr"
|
|
102
|
+
"WZoAAAdrWZoHawdrWZoQDwdrWZoaNgdrWZomOQdrWZo0hQdrWZpFigdrWZpZmgZXUGhmZgVTRtlz"
|
|
103
|
+
"MwRwPb2AAAZXZmYAAAZXZmYGVwZXZmYNywZXZmYWpwZXZmYhRgZXZmYuHAZXZmY9rQZXZmZQaAZX"
|
|
104
|
+
"ZmZmZgVTW31zMwRwUMqAAAVTczMAAAVTczMFUwVTczMLpAVTczMTOQVTczMccAVTczMnvgVTczM1"
|
|
105
|
+
"sgVTczNG2QVTczNbfQVTczNzMwRwZz6AAARwgAAAAARwgAAEcARwgAAJwARwgAAQLwRwgAAYFwRw"
|
|
106
|
+
"gAAh7QRwgAAuRARwgAA9vQRwgABQygRwgABnPgRwgACAABmaAAAAABmaAAAMGBmaAAAZmhgKAAAm"
|
|
107
|
+
"ZhZNAAAzMxRiAABAABJKAABMzRAPAABZmg3LAABmZgukAABzMwnAAACAABmaDBgAABmaDBgMGBma"
|
|
108
|
+
"DBgZmhgKC1AmZhZNCnQzMxRiCYNAABJKCH5MzRAPB2tZmg3LBldmZgukBVNzMwnABHCAABmaGZoA"
|
|
109
|
+
"ABmaGZoMGBmaGZoZmhgKGAomZhZNFk0zMxRiFGJAABJKEkpMzRAPEA9Zmg3LDctmZgukC6RzMwnA"
|
|
110
|
+
"CcCAABgKJmYAABgKJmYLUBgKJmYYChgKJmYmZhZNI8ozMxRiIOFAABJKHatMzRAPGjZZmg3LFqdm"
|
|
111
|
+
"ZgukEzlzMwnAEC+AABZNMzMAABZNMzMKdBZNMzMWTRZNMzMjyhZNMzMzMxRiL09AABJKKvdMzRAP"
|
|
112
|
+
"JjlZmg3LIUZmZgukHHBzMwnAGBeAABRiQAAAABRiQAAJgxRiQAAUYhRiQAAg4RRiQAAvTxRiQABA"
|
|
113
|
+
"ABJKOo5MzRAPNIVZmg3LLhxmZgukJ75zMwnAIe2AABJKTM0AABJKTM0IfhJKTM0SShJKTM0dqxJK"
|
|
114
|
+
"TM0q9xJKTM06jhJKTM1MzRAPRYpZmg3LPa1mZgukNbJzMwnALkSAABAPWZoAABAPWZoHaxAPWZoQ"
|
|
115
|
+
"DxAPWZoaNhAPWZomORAPWZo0hRAPWZpFihAPWZpZmg3LUGhmZgukRtlzMwnAPb2AAA3LZmYAAA3L"
|
|
116
|
+
"ZmYGVw3LZmYNyw3LZmYWpw3LZmYhRg3LZmYuHA3LZmY9rQ3LZmZQaA3LZmZmZgukW31zMwnAUMqA"
|
|
117
|
+
"AAukczMAAAukczMFUwukczMLpAukczMTOQukczMccAukczMnvgukczM1sgukczNG2QukczNbfQuk"
|
|
118
|
+
"czNzMwnAZz6AAAnAgAAAAAnAgAAEcAnAgAAJwAnAgAAQLwnAgAAYFwnAgAAh7QnAgAAuRAnAgAA9"
|
|
119
|
+
"vQnAgABQygnAgABnPgnAgACAACZmAAAAACZmAAALUCZmAAAYCiZmAAAmZiPKAAAzMyDhAABAAB2r"
|
|
120
|
+
"AABMzRo2AABZmhanAABmZhM5AABzMxAvAACAACZmC1AAACZmC1ALUCZmC1AYCiZmC1AmZiPKCnQz"
|
|
121
|
+
"MyDhCYNAAB2rCH5MzRo2B2tZmhanBldmZhM5BVNzMxAvBHCAACZmGAoAACZmGAoLUCZmGAoYCiZm"
|
|
122
|
+
"GAomZiPKFk0zMyDhFGJAAB2rEkpMzRo2EA9ZmhanDctmZhM5C6RzMxAvCcCAACZmJmYAACZmJmYL"
|
|
123
|
+
"UCZmJmYYCiZmJmYmZiPKI8ozMyDhIOFAAB2rHatMzRo2GjZZmhanFqdmZhM5EzlzMxAvEC+AACPK"
|
|
124
|
+
"MzMAACPKMzMKdCPKMzMWTSPKMzMjyiPKMzMzMyDhL09AAB2rKvdMzRo2JjlZmhanIUZmZhM5HHBz"
|
|
125
|
+
"MxAvGBeAACDhQAAAACDhQAAJgyDhQAAUYiDhQAAg4SDhQAAvTyDhQABAAB2rOo5MzRo2NIVZmhan"
|
|
126
|
+
"LhxmZhM5J75zMxAvIe2AAB2rTM0AAB2rTM0Ifh2rTM0SSh2rTM0dqx2rTM0q9x2rTM06jh2rTM1M"
|
|
127
|
+
"zRo2RYpZmhanPa1mZhM5NbJzMxAvLkSAABo2WZoAABo2WZoHaxo2WZoQDxo2WZoaNho2WZomORo2"
|
|
128
|
+
"WZo0hRo2WZpFiho2WZpZmhanUGhmZhM5RtlzMxAvPb2AABanZmYAABanZmYGVxanZmYNyxanZmYW"
|
|
129
|
+
"pxanZmYhRhanZmYuHBanZmY9rRanZmZQaBanZmZmZhM5W31zMxAvUMqAABM5czMAABM5czMFUxM5"
|
|
130
|
+
"czMLpBM5czMTORM5czMccBM5czMnvhM5czM1shM5czNG2RM5czNbfRM5czNzMxAvZz6AABAvgAAA"
|
|
131
|
+
"ABAvgAAEcBAvgAAJwBAvgAAQLxAvgAAYFxAvgAAh7RAvgAAuRBAvgAA9vRAvgABQyhAvgABnPhAv"
|
|
132
|
+
"gACAADMzAAAAADMzAAAKdDMzAAAWTTMzAAAjyjMzAAAzMy9PAABAACr3AABMzSY5AABZmiFGAABm"
|
|
133
|
+
"ZhxwAABzMxgXAACAADMzCnQAADMzCnQKdDMzCnQWTTMzCnQjyjMzCnQzMy9PCYNAACr3CH5MzSY5"
|
|
134
|
+
"B2tZmiFGBldmZhxwBVNzMxgXBHCAADMzFk0AADMzFk0KdDMzFk0WTTMzFk0jyjMzFk0zMy9PFGJA"
|
|
135
|
+
"ACr3EkpMzSY5EA9ZmiFGDctmZhxwC6RzMxgXCcCAADMzI8oAADMzI8oKdDMzI8oWTTMzI8ojyjMz"
|
|
136
|
+
"I8ozMy9PIOFAACr3HatMzSY5GjZZmiFGFqdmZhxwEzlzMxgXEC+AADMzMzMAADMzMzMKdDMzMzMW"
|
|
137
|
+
"TTMzMzMjyjMzMzMzMy9PL09AACr3KvdMzSY5JjlZmiFGIUZmZhxwHHBzMxgXGBeAAC9PQAAAAC9P"
|
|
138
|
+
"QAAJgy9PQAAUYi9PQAAg4S9PQAAvTy9PQABAACr3Oo5MzSY5NIVZmiFGLhxmZhxwJ75zMxgXIe2A"
|
|
139
|
+
"ACr3TM0AACr3TM0Ifir3TM0SSir3TM0dqyr3TM0q9yr3TM06jir3TM1MzSY5RYpZmiFGPa1mZhxw"
|
|
140
|
+
"NbJzMxgXLkSAACY5WZoAACY5WZoHayY5WZoQDyY5WZoaNiY5WZomOSY5WZo0hSY5WZpFiiY5WZpZ"
|
|
141
|
+
"miFGUGhmZhxwRtlzMxgXPb2AACFGZmYAACFGZmYGVyFGZmYNyyFGZmYWpyFGZmYhRiFGZmYuHCFG"
|
|
142
|
+
"ZmY9rSFGZmZQaCFGZmZmZhxwW31zMxgXUMqAABxwczMAABxwczMFUxxwczMLpBxwczMTORxwczMc"
|
|
143
|
+
"cBxwczMnvhxwczM1shxwczNG2RxwczNbfRxwczNzMxgXZz6AABgXgAAAABgXgAAEcBgXgAAJwBgX"
|
|
144
|
+
"gAAQLxgXgAAYFxgXgAAh7RgXgAAuRBgXgAA9vRgXgABQyhgXgABnPhgXgACAAEAAAAAAAEAAAAAJ"
|
|
145
|
+
"g0AAAAAUYkAAAAAg4UAAAAAvT0AAAABAADqOAABMzTSFAABZmi4cAABmZie+AABzMyHtAACAAEAA"
|
|
146
|
+
"CYMAAEAACYMJg0AACYMUYkAACYMg4UAACYMvT0AACYNAADqOCH5MzTSFB2tZmi4cBldmZie+BVNz"
|
|
147
|
+
"MyHtBHCAAEAAFGIAAEAAFGIJg0AAFGIUYkAAFGIg4UAAFGIvT0AAFGJAADqOEkpMzTSFEA9Zmi4c"
|
|
148
|
+
"DctmZie+C6RzMyHtCcCAAEAAIOEAAEAAIOEJg0AAIOEUYkAAIOEg4UAAIOEvT0AAIOFAADqOHatM"
|
|
149
|
+
"zTSFGjZZmi4cFqdmZie+EzlzMyHtEC+AAEAAL08AAEAAL08Jg0AAL08UYkAAL08g4UAAL08vT0AA"
|
|
150
|
+
"L09AADqOKvdMzTSFJjlZmi4cIUZmZie+HHBzMyHtGBeAAEAAQAAAAEAAQAAJg0AAQAAUYkAAQAAg"
|
|
151
|
+
"4UAAQAAvT0AAQABAADqOOo5MzTSFNIVZmi4cLhxmZie+J75zMyHtIe2AADqOTM0AADqOTM0IfjqO"
|
|
152
|
+
"TM0SSjqOTM0dqzqOTM0q9zqOTM06jjqOTM1MzTSFRYpZmi4cPa1mZie+NbJzMyHtLkSAADSFWZoA"
|
|
153
|
+
"ADSFWZoHazSFWZoQDzSFWZoaNjSFWZomOTSFWZo0hTSFWZpFijSFWZpZmi4cUGhmZie+RtlzMyHt"
|
|
154
|
+
"Pb2AAC4cZmYAAC4cZmYGVy4cZmYNyy4cZmYWpy4cZmYhRi4cZmYuHC4cZmY9rS4cZmZQaC4cZmZm"
|
|
155
|
+
"Zie+W31zMyHtUMqAACe+czMAACe+czMFUye+czMLpCe+czMTOSe+czMccCe+czMnvie+czM1sie+"
|
|
156
|
+
"czNG2Se+czNbfSe+czNzMyHtZz6AACHtgAAAACHtgAAEcCHtgAAJwCHtgAAQLyHtgAAYFyHtgAAh"
|
|
157
|
+
"7SHtgAAuRCHtgAA9vSHtgABQyiHtgABnPiHtgACAAEzNAAAAAEzNAAAIfkzNAAASSkzNAAAdq0zN"
|
|
158
|
+
"AAAq90zNAAA6jkzNAABMzUWKAABZmj2tAABmZjWyAABzMy5EAACAAEzNCH4AAEzNCH4IfkzNCH4S"
|
|
159
|
+
"SkzNCH4dq0zNCH4q90zNCH46jkzNCH5MzUWKB2tZmj2tBldmZjWyBVNzMy5EBHCAAEzNEkoAAEzN"
|
|
160
|
+
"EkoIfkzNEkoSSkzNEkodq0zNEkoq90zNEko6jkzNEkpMzUWKEA9Zmj2tDctmZjWyC6RzMy5ECcCA"
|
|
161
|
+
"AEzNHasAAEzNHasIfkzNHasSSkzNHasdq0zNHasq90zNHas6jkzNHatMzUWKGjZZmj2tFqdmZjWy"
|
|
162
|
+
"EzlzMy5EEC+AAEzNKvcAAEzNKvcIfkzNKvcSSkzNKvcdq0zNKvcq90zNKvc6jkzNKvdMzUWKJjlZ"
|
|
163
|
+
"mj2tIUZmZjWyHHBzMy5EGBeAAEzNOo4AAEzNOo4IfkzNOo4SSkzNOo4dq0zNOo4q90zNOo46jkzN"
|
|
164
|
+
"Oo5MzUWKNIVZmj2tLhxmZjWyJ75zMy5EIe2AAEzNTM0AAEzNTM0IfkzNTM0SSkzNTM0dq0zNTM0q"
|
|
165
|
+
"90zNTM06jkzNTM1MzUWKRYpZmj2tPa1mZjWyNbJzMy5ELkSAAEWKWZoAAEWKWZoHa0WKWZoQD0WK"
|
|
166
|
+
"WZoaNkWKWZomOUWKWZo0hUWKWZpFikWKWZpZmj2tUGhmZjWyRtlzMy5EPb2AAD2tZmYAAD2tZmYG"
|
|
167
|
+
"Vz2tZmYNyz2tZmYWpz2tZmYhRj2tZmYuHD2tZmY9rT2tZmZQaD2tZmZmZjWyW31zMy5EUMqAADWy"
|
|
168
|
+
"czMAADWyczMFUzWyczMLpDWyczMTOTWyczMccDWyczMnvjWyczM1sjWyczNG2TWyczNbfTWyczNz"
|
|
169
|
+
"My5EZz6AAC5EgAAAAC5EgAAEcC5EgAAJwC5EgAAQLy5EgAAYFy5EgAAh7S5EgAAuRC5EgAA9vS5E"
|
|
170
|
+
"gABQyi5EgABnPi5EgACAAFmaAAAAAFmaAAAHa1maAAAQD1maAAAaNlmaAAAmOVmaAAA0hVmaAABF"
|
|
171
|
+
"ilmaAABZmlBoAABmZkbZAABzMz29AACAAFmaB2sAAFmaB2sHa1maB2sQD1maB2saNlmaB2smOVma"
|
|
172
|
+
"B2s0hVmaB2tFilmaB2tZmlBoBldmZkbZBVNzMz29BHCAAFmaEA8AAFmaEA8Ha1maEA8QD1maEA8a"
|
|
173
|
+
"NlmaEA8mOVmaEA80hVmaEA9FilmaEA9ZmlBoDctmZkbZC6RzMz29CcCAAFmaGjYAAFmaGjYHa1ma"
|
|
174
|
+
"GjYQD1maGjYaNlmaGjYmOVmaGjY0hVmaGjZFilmaGjZZmlBoFqdmZkbZEzlzMz29EC+AAFmaJjkA"
|
|
175
|
+
"AFmaJjkHa1maJjkQD1maJjkaNlmaJjkmOVmaJjk0hVmaJjlFilmaJjlZmlBoIUZmZkbZHHBzMz29"
|
|
176
|
+
"GBeAAFmaNIUAAFmaNIUHa1maNIUQD1maNIUaNlmaNIUmOVmaNIU0hVmaNIVFilmaNIVZmlBoLhxm"
|
|
177
|
+
"ZkbZJ75zMz29Ie2AAFmaRYoAAFmaRYoHa1maRYoQD1maRYoaNlmaRYomOVmaRYo0hVmaRYpFilma"
|
|
178
|
+
"RYpZmlBoPa1mZkbZNbJzMz29LkSAAFmaWZoAAFmaWZoHa1maWZoQD1maWZoaNlmaWZomOVmaWZo0"
|
|
179
|
+
"hVmaWZpFilmaWZpZmlBoUGhmZkbZRtlzMz29Pb2AAFBoZmYAAFBoZmYGV1BoZmYNy1BoZmYWp1Bo"
|
|
180
|
+
"ZmYhRlBoZmYuHFBoZmY9rVBoZmZQaFBoZmZmZkbZW31zMz29UMqAAEbZczMAAEbZczMFU0bZczML"
|
|
181
|
+
"pEbZczMTOUbZczMccEbZczMnvkbZczM1skbZczNG2UbZczNbfUbZczNzMz29Zz6AAD29gAAAAD29"
|
|
182
|
+
"gAAEcD29gAAJwD29gAAQLz29gAAYFz29gAAh7T29gAAuRD29gAA9vT29gABQyj29gABnPj29gACA"
|
|
183
|
+
"AGZmAAAAAGZmAAAGV2ZmAAANy2ZmAAAWp2ZmAAAhRmZmAAAuHGZmAAA9rWZmAABQaGZmAABmZlt9"
|
|
184
|
+
"AABzM1DKAACAAGZmBlcAAGZmBlcGV2ZmBlcNy2ZmBlcWp2ZmBlchRmZmBlcuHGZmBlc9rWZmBldQ"
|
|
185
|
+
"aGZmBldmZlt9BVNzM1DKBHCAAGZmDcsAAGZmDcsGV2ZmDcsNy2ZmDcsWp2ZmDcshRmZmDcsuHGZm"
|
|
186
|
+
"Dcs9rWZmDctQaGZmDctmZlt9C6RzM1DKCcCAAGZmFqcAAGZmFqcGV2ZmFqcNy2ZmFqcWp2ZmFqch"
|
|
187
|
+
"RmZmFqcuHGZmFqc9rWZmFqdQaGZmFqdmZlt9EzlzM1DKEC+AAGZmIUYAAGZmIUYGV2ZmIUYNy2Zm"
|
|
188
|
+
"IUYWp2ZmIUYhRmZmIUYuHGZmIUY9rWZmIUZQaGZmIUZmZlt9HHBzM1DKGBeAAGZmLhwAAGZmLhwG"
|
|
189
|
+
"V2ZmLhwNy2ZmLhwWp2ZmLhwhRmZmLhwuHGZmLhw9rWZmLhxQaGZmLhxmZlt9J75zM1DKIe2AAGZm"
|
|
190
|
+
"Pa0AAGZmPa0GV2ZmPa0Ny2ZmPa0Wp2ZmPa0hRmZmPa0uHGZmPa09rWZmPa1QaGZmPa1mZlt9NbJz"
|
|
191
|
+
"M1DKLkSAAGZmUGgAAGZmUGgGV2ZmUGgNy2ZmUGgWp2ZmUGghRmZmUGguHGZmUGg9rWZmUGhQaGZm"
|
|
192
|
+
"UGhmZlt9RtlzM1DKPb2AAGZmZmYAAGZmZmYGV2ZmZmYNy2ZmZmYWp2ZmZmYhRmZmZmYuHGZmZmY9"
|
|
193
|
+
"rWZmZmZQaGZmZmZmZlt9W31zM1DKUMqAAFt9czMAAFt9czMFU1t9czMLpFt9czMTOVt9czMccFt9"
|
|
194
|
+
"czMnvlt9czM1slt9czNG2Vt9czNbfVt9czNzM1DKZz6AAFDKgAAAAFDKgAAEcFDKgAAJwFDKgAAQ"
|
|
195
|
+
"L1DKgAAYF1DKgAAh7VDKgAAuRFDKgAA9vVDKgABQylDKgABnPlDKgACAAHMzAAAAAHMzAAAFU3Mz"
|
|
196
|
+
"AAALpHMzAAATOXMzAAAccHMzAAAnvnMzAAA1snMzAABG2XMzAABbfXMzAABzM2c+AACAAHMzBVMA"
|
|
197
|
+
"AHMzBVMFU3MzBVMLpHMzBVMTOXMzBVMccHMzBVMnvnMzBVM1snMzBVNG2XMzBVNbfXMzBVNzM2c+"
|
|
198
|
+
"BHCAAHMzC6QAAHMzC6QFU3MzC6QLpHMzC6QTOXMzC6QccHMzC6QnvnMzC6Q1snMzC6RG2XMzC6Rb"
|
|
199
|
+
"fXMzC6RzM2c+CcCAAHMzEzkAAHMzEzkFU3MzEzkLpHMzEzkTOXMzEzkccHMzEzknvnMzEzk1snMz"
|
|
200
|
+
"EzlG2XMzEzlbfXMzEzlzM2c+EC+AAHMzHHAAAHMzHHAFU3MzHHALpHMzHHATOXMzHHAccHMzHHAn"
|
|
201
|
+
"vnMzHHA1snMzHHBG2XMzHHBbfXMzHHBzM2c+GBeAAHMzJ74AAHMzJ74FU3MzJ74LpHMzJ74TOXMz"
|
|
202
|
+
"J74ccHMzJ74nvnMzJ741snMzJ75G2XMzJ75bfXMzJ75zM2c+Ie2AAHMzNbIAAHMzNbIFU3MzNbIL"
|
|
203
|
+
"pHMzNbITOXMzNbIccHMzNbInvnMzNbI1snMzNbJG2XMzNbJbfXMzNbJzM2c+LkSAAHMzRtkAAHMz"
|
|
204
|
+
"RtkFU3MzRtkLpHMzRtkTOXMzRtkccHMzRtknvnMzRtk1snMzRtlG2XMzRtlbfXMzRtlzM2c+Pb2A"
|
|
205
|
+
"AHMzW30AAHMzW30FU3MzW30LpHMzW30TOXMzW30ccHMzW30nvnMzW301snMzW31G2XMzW31bfXMz"
|
|
206
|
+
"W31zM2c+UMqAAHMzczMAAHMzczMFU3MzczMLpHMzczMTOXMzczMccHMzczMnvnMzczM1snMzczNG"
|
|
207
|
+
"2XMzczNbfXMzczNzM2c+Zz6AAGc+gAAAAGc+gAAEcGc+gAAJwGc+gAAQL2c+gAAYF2c+gAAh7Wc+"
|
|
208
|
+
"gAAuRGc+gAA9vWc+gABQymc+gABnPmc+gACAAIAAAAAAAIAAAAAEcIAAAAAJwIAAAAAQL4AAAAAY"
|
|
209
|
+
"F4AAAAAh7YAAAAAuRIAAAAA9vYAAAABQyoAAAABnPoAAAACAAIAABHAAAIAABHAEcIAABHAJwIAA"
|
|
210
|
+
"BHAQL4AABHAYF4AABHAh7YAABHAuRIAABHA9vYAABHBQyoAABHBnPoAABHCAAIAACcAAAIAACcAE"
|
|
211
|
+
"cIAACcAJwIAACcAQL4AACcAYF4AACcAh7YAACcAuRIAACcA9vYAACcBQyoAACcBnPoAACcCAAIAA"
|
|
212
|
+
"EC8AAIAAEC8EcIAAEC8JwIAAEC8QL4AAEC8YF4AAEC8h7YAAEC8uRIAAEC89vYAAEC9QyoAAEC9n"
|
|
213
|
+
"PoAAEC+AAIAAGBcAAIAAGBcEcIAAGBcJwIAAGBcQL4AAGBcYF4AAGBch7YAAGBcuRIAAGBc9vYAA"
|
|
214
|
+
"GBdQyoAAGBdnPoAAGBeAAIAAIe0AAIAAIe0EcIAAIe0JwIAAIe0QL4AAIe0YF4AAIe0h7YAAIe0u"
|
|
215
|
+
"RIAAIe09vYAAIe1QyoAAIe1nPoAAIe2AAIAALkQAAIAALkQEcIAALkQJwIAALkQQL4AALkQYF4AA"
|
|
216
|
+
"LkQh7YAALkQuRIAALkQ9vYAALkRQyoAALkRnPoAALkSAAIAAPb0AAIAAPb0EcIAAPb0JwIAAPb0Q"
|
|
217
|
+
"L4AAPb0YF4AAPb0h7YAAPb0uRIAAPb09vYAAPb1QyoAAPb1nPoAAPb2AAIAAUMoAAIAAUMoEcIAA"
|
|
218
|
+
"UMoJwIAAUMoQL4AAUMoYF4AAUMoh7YAAUMouRIAAUMo9vYAAUMpQyoAAUMpnPoAAUMqAAIAAZz4A"
|
|
219
|
+
"AIAAZz4EcIAAZz4JwIAAZz4QL4AAZz4YF4AAZz4h7YAAZz4uRIAAZz49vYAAZz5QyoAAZz5nPoAA"
|
|
220
|
+
"Zz6AAIAAgAAAAIAAgAAEcIAAgAAJwIAAgAAQL4AAgAAYF4AAgAAh7YAAgAAuRIAAgAA9vYAAgABQ"
|
|
221
|
+
"yoAAgABnPoAAgACAAAAAY3VydgAAAAAAAABBAAAAAgAHABEAIAA4AFgAhAC/AQoBaQHhAnYDLQQK"
|
|
222
|
+
"BRUGVAfPCY8LnA4DEMQT+hefG8UgZSWeK3cx5DjQQIRItlF+WrdkfG56eNuDio5UmUOkSK8yukPF"
|
|
223
|
+
"VNBn27HnHPK9/t7//////////////////////////////////////////wAAY3VydgAAAAAAAABB"
|
|
224
|
+
"AAAAAgAHABEAIAA4AFgAhAC/AQoBaQHhAnYDLQQKBRUGVAfPCY8LnA4DEMQT+hefG8UgZSWeK3cx"
|
|
225
|
+
"5DjQQIRItlF+WrdkfG56eNuDio5UmUOkSK8yukPFVNBn27HnHPK9/t7/////////////////////"
|
|
226
|
+
"/////////////////////wAAY3VydgAAAAAAAABBAAAAAgAHABEAIAA4AFgAhAC/AQoBaQHhAnYD"
|
|
227
|
+
"LQQKBRUGVAfPCY8LnA4DEMQT+hefG8UgZSWeK3cx5DjQQIRItlF+WrdkfG56eNuDio5UmUOkSK8y"
|
|
228
|
+
"ukPFVNBn27HnHPK9/t7//////////////////////////////////////////wAAAACsaAAAKmkA"
|
|
229
|
+
"ACAHAABHbwAArOMAAAuu////gQAAB60AAMwTAAAAAAAAAAAAAAAAcGFyYQAAAAAAAAAAAAEAAHBh"
|
|
230
|
+
"cmEAAAAAAAAAAAABAABwYXJhAAAAAAAAAAAAAQAAbUJBIAAAAAADAwAAAAAAIAAAAAAAAAAAAAAA"
|
|
231
|
+
"AAAAAABwYXJhAAAAAAAAAAAAAQAAcGFyYQAAAAAAAAAAAAEAAHBhcmEAAAAAAAAAAAABAABtbHVj"
|
|
232
|
+
"AAAAAAAAAAEAAAAMZW5VUwAAACAAAAAcAEcAbwBvAGcAbABlACAASQBuAGMALgAgADIAMAAxADY="
|
|
233
|
+
)
|
|
234
|
+
DEFAULT_PQ_REC2020_ICC: bytes = base64.b64decode("".join(_DEFAULT_ICC_B64))
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
def extract_icc_from_png(png_bytes: bytes) -> bytes | None:
|
|
238
|
+
"""Return the decompressed ICC profile from a PNG's iCCP chunk, or ``None``."""
|
|
239
|
+
if png_bytes[:8] != b"\x89PNG\r\n\x1a\n":
|
|
240
|
+
return None
|
|
241
|
+
i = 8
|
|
242
|
+
while i < len(png_bytes):
|
|
243
|
+
length = struct.unpack(">I", png_bytes[i : i + 4])[0]
|
|
244
|
+
ctype = png_bytes[i + 4 : i + 8]
|
|
245
|
+
payload = png_bytes[i + 8 : i + 8 + length]
|
|
246
|
+
if ctype == b"iCCP":
|
|
247
|
+
nul = payload.index(b"\x00")
|
|
248
|
+
return zlib.decompress(payload[nul + 2 :])
|
|
249
|
+
if ctype == b"IEND":
|
|
250
|
+
return None
|
|
251
|
+
i += 8 + length + 4
|
|
252
|
+
return None
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
def linear_nits_to_pq(rgb_nits: np.ndarray) -> np.ndarray:
|
|
256
|
+
"""Inverse PQ EOTF (SMPTE ST 2084).
|
|
257
|
+
|
|
258
|
+
Args:
|
|
259
|
+
rgb_nits: Linear luminance in cd/m^2 (nits). Any shape; clipped to [0, 10000].
|
|
260
|
+
|
|
261
|
+
Returns:
|
|
262
|
+
PQ-encoded code values in [0, 1], same shape as input.
|
|
263
|
+
"""
|
|
264
|
+
return colour.models.eotf_inverse_ST2084(np.clip(rgb_nits, 0.0, 10000.0))
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
def encode_hdr_png(
|
|
268
|
+
rgb_nits: np.ndarray,
|
|
269
|
+
icc_profile: bytes = DEFAULT_PQ_REC2020_ICC,
|
|
270
|
+
) -> bytes:
|
|
271
|
+
"""Encode a (H, W, 3) array of linear-light RGB nits as a PQ-tagged PNG.
|
|
272
|
+
|
|
273
|
+
The returned bytes are an 8-bit RGB PNG with an embedded ICC profile (Rec2020
|
|
274
|
+
primaries, PQ transfer by default). HDR-capable browsers will composite this
|
|
275
|
+
in extended dynamic range; SDR browsers render it tone-mapped.
|
|
276
|
+
"""
|
|
277
|
+
pq = linear_nits_to_pq(rgb_nits)
|
|
278
|
+
pq8 = np.clip(pq * 255.0 + 0.5, 0, 255).astype(np.uint8)
|
|
279
|
+
img = Image.fromarray(pq8, mode="RGB")
|
|
280
|
+
buf = io.BytesIO()
|
|
281
|
+
img.save(buf, format="PNG", icc_profile=icc_profile)
|
|
282
|
+
return buf.getvalue()
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
def to_data_url(png_bytes: bytes) -> str:
|
|
286
|
+
"""Wrap PNG bytes in a ``data:`` URL suitable for an ``<img src=...>``."""
|
|
287
|
+
return "data:image/png;base64," + base64.b64encode(png_bytes).decode("ascii")
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
# HDR-aware colormaps. (norm, R, G, B) control points; channel values can
|
|
291
|
+
# exceed 1.0 to push above ``peak_nits`` — that is how the visible glow happens.
|
|
292
|
+
COLORMAP_LIBRARY: dict[str, np.ndarray] = {
|
|
293
|
+
"fire-purple": np.array([
|
|
294
|
+
[0.00, 0.000, 0.000, 0.000],
|
|
295
|
+
[0.05, 0.020, 0.000, 0.060],
|
|
296
|
+
[0.20, 0.080, 0.000, 0.180],
|
|
297
|
+
[0.40, 0.350, 0.020, 0.280],
|
|
298
|
+
[0.55, 0.700, 0.150, 0.150],
|
|
299
|
+
[0.70, 0.950, 0.450, 0.080],
|
|
300
|
+
[0.85, 1.000, 0.850, 0.250],
|
|
301
|
+
[0.95, 1.000, 1.000, 0.700],
|
|
302
|
+
[1.00, 1.000, 1.000, 0.950],
|
|
303
|
+
]),
|
|
304
|
+
"ice": np.array([
|
|
305
|
+
[0.00, 0.000, 0.000, 0.000],
|
|
306
|
+
[0.10, 0.000, 0.020, 0.080],
|
|
307
|
+
[0.30, 0.020, 0.150, 0.400],
|
|
308
|
+
[0.55, 0.100, 0.500, 0.900],
|
|
309
|
+
[0.75, 0.500, 0.900, 1.000],
|
|
310
|
+
[0.90, 1.000, 1.100, 1.150],
|
|
311
|
+
[1.00, 1.300, 1.300, 1.250],
|
|
312
|
+
]),
|
|
313
|
+
"twilight-burst": np.array([
|
|
314
|
+
[0.00, 0.000, 0.000, 0.000],
|
|
315
|
+
[0.10, 0.080, 0.000, 0.150],
|
|
316
|
+
[0.30, 0.400, 0.040, 0.500],
|
|
317
|
+
[0.55, 0.900, 0.300, 0.300],
|
|
318
|
+
[0.75, 1.100, 0.700, 0.150],
|
|
319
|
+
[0.90, 1.250, 1.100, 0.450],
|
|
320
|
+
[1.00, 1.300, 1.300, 1.000],
|
|
321
|
+
]),
|
|
322
|
+
"matrix-green": np.array([
|
|
323
|
+
[0.00, 0.000, 0.000, 0.000],
|
|
324
|
+
[0.20, 0.000, 0.080, 0.020],
|
|
325
|
+
[0.50, 0.040, 0.500, 0.080],
|
|
326
|
+
[0.75, 0.150, 1.000, 0.250],
|
|
327
|
+
[0.90, 0.500, 1.300, 0.500],
|
|
328
|
+
[1.00, 1.000, 1.500, 1.000],
|
|
329
|
+
]),
|
|
330
|
+
"ember": np.array([
|
|
331
|
+
[0.00, 0.000, 0.000, 0.000],
|
|
332
|
+
[0.30, 0.300, 0.020, 0.000],
|
|
333
|
+
[0.55, 0.700, 0.080, 0.000],
|
|
334
|
+
[0.75, 1.000, 0.350, 0.020],
|
|
335
|
+
[0.88, 1.150, 0.700, 0.150],
|
|
336
|
+
[0.97, 1.250, 1.100, 0.500],
|
|
337
|
+
[1.00, 1.300, 1.250, 0.900],
|
|
338
|
+
]),
|
|
339
|
+
"viridis-hdr": np.array([
|
|
340
|
+
[0.00, 0.267, 0.005, 0.329],
|
|
341
|
+
[0.20, 0.281, 0.165, 0.476],
|
|
342
|
+
[0.40, 0.254, 0.265, 0.530],
|
|
343
|
+
[0.60, 0.207, 0.372, 0.553],
|
|
344
|
+
[0.80, 0.435, 0.643, 0.422],
|
|
345
|
+
[0.90, 0.770, 0.902, 0.245],
|
|
346
|
+
[0.97, 1.050, 1.150, 0.450],
|
|
347
|
+
[1.00, 1.250, 1.350, 0.700],
|
|
348
|
+
]),
|
|
349
|
+
"inferno-hdr": np.array([
|
|
350
|
+
[0.00, 0.001, 0.000, 0.014],
|
|
351
|
+
[0.20, 0.218, 0.041, 0.378],
|
|
352
|
+
[0.40, 0.580, 0.149, 0.404],
|
|
353
|
+
[0.60, 0.866, 0.317, 0.226],
|
|
354
|
+
[0.75, 0.988, 0.557, 0.094],
|
|
355
|
+
[0.88, 1.000, 0.860, 0.250],
|
|
356
|
+
[0.97, 1.150, 1.150, 0.620],
|
|
357
|
+
[1.00, 1.300, 1.300, 0.950],
|
|
358
|
+
]),
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
def hdr_colormap(
|
|
363
|
+
norm: np.ndarray,
|
|
364
|
+
cmap_name: str = "fire-purple",
|
|
365
|
+
peak_nits: float = 4000.0,
|
|
366
|
+
) -> np.ndarray:
|
|
367
|
+
"""Map normalized [0, 1] values to RGB linear nits via a named colormap.
|
|
368
|
+
|
|
369
|
+
Args:
|
|
370
|
+
norm: Array of values in [0, 1]. Any shape.
|
|
371
|
+
cmap_name: Key from :data:`COLORMAP_LIBRARY`.
|
|
372
|
+
peak_nits: Linear-light scale. A colormap channel value of 1.0 maps to
|
|
373
|
+
this many nits; values above 1.0 push proportionally higher.
|
|
374
|
+
|
|
375
|
+
Returns:
|
|
376
|
+
An array shaped ``norm.shape + (3,)`` in linear cd/m^2 (nits).
|
|
377
|
+
"""
|
|
378
|
+
pts = COLORMAP_LIBRARY[cmap_name]
|
|
379
|
+
R = np.interp(norm, pts[:, 0], pts[:, 1]) * peak_nits
|
|
380
|
+
G = np.interp(norm, pts[:, 0], pts[:, 2]) * peak_nits
|
|
381
|
+
B = np.interp(norm, pts[:, 0], pts[:, 3]) * peak_nits
|
|
382
|
+
return np.stack([R, G, B], axis=-1)
|
|
383
|
+
|
|
384
|
+
|
|
385
|
+
def imshow(
|
|
386
|
+
arr: np.ndarray,
|
|
387
|
+
cmap: str = "fire-purple",
|
|
388
|
+
peak_nits: float = 4000.0,
|
|
389
|
+
normalize: str = "linear",
|
|
390
|
+
vmin: float | None = None,
|
|
391
|
+
vmax: float | None = None,
|
|
392
|
+
clip_percentile: tuple[float, float] | None = None,
|
|
393
|
+
interior_mask: np.ndarray | None = None,
|
|
394
|
+
label: str = "",
|
|
395
|
+
) -> "HDRImage":
|
|
396
|
+
"""Render a 2D numpy array as an HDR image. Returns an :class:`HDRImage` widget.
|
|
397
|
+
|
|
398
|
+
Conceptually a tiny ``matplotlib.imshow`` analogue, but pixels are PQ Rec2020-
|
|
399
|
+
encoded so a capable browser composites them in extended dynamic range.
|
|
400
|
+
|
|
401
|
+
Args:
|
|
402
|
+
arr: ``(H, W)`` float-like array. ``NaN``/``Inf`` values are treated as
|
|
403
|
+
interior (rendered black).
|
|
404
|
+
cmap: Key from :data:`COLORMAP_LIBRARY`.
|
|
405
|
+
peak_nits: Luminance scale; cmap channel value 1.0 maps to this.
|
|
406
|
+
normalize: How to map values to [0, 1]:
|
|
407
|
+
|
|
408
|
+
- ``"linear"``: ``(v - vmin) / (vmax - vmin)``
|
|
409
|
+
- ``"log"``: ``log1p((v - vmin) + tiny) / log1p((vmax - vmin) + tiny)``
|
|
410
|
+
- ``"sqrt"``: square root of linear
|
|
411
|
+
|
|
412
|
+
Default is linear; for HDR data viz, prefer linear or pre-stretch your
|
|
413
|
+
data with ``np.log1p``/``np.sqrt`` rather than reaching for log here.
|
|
414
|
+
vmin, vmax: Explicit data range. If unset, derived from data.
|
|
415
|
+
clip_percentile: Optional ``(lo, hi)`` percentile pair to derive
|
|
416
|
+
``vmin``/``vmax`` robustly (e.g. ``(0.5, 99.5)`` to ignore outliers).
|
|
417
|
+
interior_mask: Optional bool array same shape as ``arr``; ``True`` pixels
|
|
418
|
+
render as black. Useful for fractal interiors, NaN regions, etc.
|
|
419
|
+
label: Caption shown above the rendered image in the widget.
|
|
420
|
+
"""
|
|
421
|
+
if arr.ndim != 2:
|
|
422
|
+
raise ValueError(f"hdr_imshow expects a 2D array, got shape {arr.shape}")
|
|
423
|
+
if cmap not in COLORMAP_LIBRARY:
|
|
424
|
+
raise ValueError(
|
|
425
|
+
f"unknown cmap {cmap!r}; choose from {list(COLORMAP_LIBRARY)}"
|
|
426
|
+
)
|
|
427
|
+
|
|
428
|
+
A = np.asarray(arr, dtype=np.float64).copy()
|
|
429
|
+
finite = np.isfinite(A)
|
|
430
|
+
if interior_mask is None:
|
|
431
|
+
interior_mask = np.zeros_like(A, dtype=bool)
|
|
432
|
+
else:
|
|
433
|
+
interior_mask = np.asarray(interior_mask, dtype=bool)
|
|
434
|
+
if interior_mask.shape != A.shape:
|
|
435
|
+
raise ValueError("interior_mask must match arr shape")
|
|
436
|
+
|
|
437
|
+
treat_as_interior = interior_mask | ~finite
|
|
438
|
+
A[~finite] = 0.0
|
|
439
|
+
|
|
440
|
+
sample = A[finite & ~interior_mask]
|
|
441
|
+
if sample.size == 0:
|
|
442
|
+
vmin_eff = 0.0 if vmin is None else float(vmin)
|
|
443
|
+
vmax_eff = 1.0 if vmax is None else float(vmax)
|
|
444
|
+
elif clip_percentile is not None:
|
|
445
|
+
lo, hi = clip_percentile
|
|
446
|
+
vmin_eff = float(np.percentile(sample, lo)) if vmin is None else float(vmin)
|
|
447
|
+
vmax_eff = float(np.percentile(sample, hi)) if vmax is None else float(vmax)
|
|
448
|
+
else:
|
|
449
|
+
vmin_eff = float(sample.min()) if vmin is None else float(vmin)
|
|
450
|
+
vmax_eff = float(sample.max()) if vmax is None else float(vmax)
|
|
451
|
+
|
|
452
|
+
if not np.isfinite(vmin_eff) or not np.isfinite(vmax_eff) or vmax_eff <= vmin_eff:
|
|
453
|
+
vmin_eff, vmax_eff = 0.0, 1.0
|
|
454
|
+
|
|
455
|
+
A_clipped = np.clip(A, vmin_eff, vmax_eff)
|
|
456
|
+
span = vmax_eff - vmin_eff
|
|
457
|
+
|
|
458
|
+
if normalize == "linear":
|
|
459
|
+
norm = (A_clipped - vmin_eff) / span
|
|
460
|
+
elif normalize == "log":
|
|
461
|
+
eps = 1e-9 * span
|
|
462
|
+
norm = np.log1p(A_clipped - vmin_eff + eps) / np.log1p(span + eps)
|
|
463
|
+
elif normalize == "sqrt":
|
|
464
|
+
norm = np.sqrt((A_clipped - vmin_eff) / span)
|
|
465
|
+
else:
|
|
466
|
+
raise ValueError(f"unknown normalize={normalize!r}")
|
|
467
|
+
|
|
468
|
+
norm = np.clip(norm, 0.0, 1.0)
|
|
469
|
+
norm[treat_as_interior] = 0.0
|
|
470
|
+
|
|
471
|
+
rgb_nits = hdr_colormap(norm, cmap_name=cmap, peak_nits=peak_nits)
|
|
472
|
+
rgb_nits[treat_as_interior] = 0.0
|
|
473
|
+
|
|
474
|
+
png = encode_hdr_png(rgb_nits)
|
|
475
|
+
return HDRImage(image_data_url=to_data_url(png), label=label)
|
|
476
|
+
|
|
477
|
+
|
|
478
|
+
|
|
479
|
+
def hdr_imshow(*args, **kwargs):
|
|
480
|
+
"""Deprecated alias for :func:`imshow`. Will be removed in 0.2.0.
|
|
481
|
+
|
|
482
|
+
Emits :class:`DeprecationWarning` (silent by default in CPython; surface
|
|
483
|
+
via ``python -W error::DeprecationWarning`` or pytest).
|
|
484
|
+
"""
|
|
485
|
+
import warnings
|
|
486
|
+
warnings.warn(
|
|
487
|
+
"hdr_imshow is deprecated, use imshow instead. "
|
|
488
|
+
"This alias will be removed in v0.2.0.",
|
|
489
|
+
DeprecationWarning,
|
|
490
|
+
stacklevel=2,
|
|
491
|
+
)
|
|
492
|
+
return imshow(*args, **kwargs)
|
|
493
|
+
|
|
494
|
+
|
|
495
|
+
class HDRImage(anywidget.AnyWidget):
|
|
496
|
+
"""Anywidget that displays an HDR-encoded image with a CSS-clamp toggle.
|
|
497
|
+
|
|
498
|
+
Setting ``clamp_to_sdr=True`` applies ``dynamic-range-limit: standard`` to
|
|
499
|
+
the ``<img>``, making the same pixels render in SDR. This is the cleanest
|
|
500
|
+
A/B for showing HDR's contribution: same image, same display, one CSS
|
|
501
|
+
property apart.
|
|
502
|
+
"""
|
|
503
|
+
|
|
504
|
+
image_data_url = traitlets.Unicode("").tag(sync=True)
|
|
505
|
+
label = traitlets.Unicode("").tag(sync=True)
|
|
506
|
+
clamp_to_sdr = traitlets.Bool(False).tag(sync=True)
|
|
507
|
+
|
|
508
|
+
_esm = r'''
|
|
509
|
+
function render({ model, el }) {
|
|
510
|
+
function rebuild() {
|
|
511
|
+
const url = model.get("image_data_url");
|
|
512
|
+
const label = model.get("label");
|
|
513
|
+
const clamp = model.get("clamp_to_sdr");
|
|
514
|
+
el.innerHTML = `
|
|
515
|
+
<style>
|
|
516
|
+
.hi-card { font-family: system-ui, -apple-system, sans-serif; color: inherit;
|
|
517
|
+
border: 1px solid color-mix(in srgb, currentColor 20%, transparent);
|
|
518
|
+
border-radius: 12px; padding: 12px; display: grid; gap: 8px; }
|
|
519
|
+
.hi-label { font-size: 12px; opacity: 0.8; display:flex; gap:8px; align-items:center; }
|
|
520
|
+
.hi-label .tag { font-family: ui-monospace, SFMono-Regular, monospace; font-size: 11px;
|
|
521
|
+
padding: 2px 6px; border-radius: 4px;
|
|
522
|
+
background: color-mix(in srgb, currentColor 10%, transparent); }
|
|
523
|
+
.hi-img-wrap { background:#000; border-radius:8px; overflow:hidden; display:flex;
|
|
524
|
+
justify-content:center; align-items:center; }
|
|
525
|
+
.hi-img-wrap img { max-width:100%; height:auto; display:block; }
|
|
526
|
+
</style>
|
|
527
|
+
<div class="hi-card">
|
|
528
|
+
<div class="hi-label">${label}
|
|
529
|
+
<span class="tag">dynamic-range-limit: ${clamp ? "standard" : "no-limit"}</span>
|
|
530
|
+
</div>
|
|
531
|
+
<div class="hi-img-wrap">
|
|
532
|
+
<img src="${url}" alt="${label}"
|
|
533
|
+
style="${clamp ? "dynamic-range-limit: standard;" : ""}">
|
|
534
|
+
</div>
|
|
535
|
+
</div>
|
|
536
|
+
`;
|
|
537
|
+
}
|
|
538
|
+
rebuild();
|
|
539
|
+
model.on("change:clamp_to_sdr", rebuild);
|
|
540
|
+
model.on("change:image_data_url", rebuild);
|
|
541
|
+
model.on("change:label", rebuild);
|
|
542
|
+
}
|
|
543
|
+
export default { render };
|
|
544
|
+
'''
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "hdrviz"
|
|
3
|
+
dynamic = ["version"]
|
|
4
|
+
description = "HDR data visualization for the browser, from numpy arrays."
|
|
5
|
+
authors = [
|
|
6
|
+
{name = "Konstantin Taletskiy", email = "konstantin@taletskiy.com"},
|
|
7
|
+
]
|
|
8
|
+
license = "MIT"
|
|
9
|
+
license-files = ["LICENSE"]
|
|
10
|
+
readme = "README.md"
|
|
11
|
+
requires-python = ">=3.11"
|
|
12
|
+
keywords = [
|
|
13
|
+
"hdr",
|
|
14
|
+
"visualization",
|
|
15
|
+
"numpy",
|
|
16
|
+
"anywidget",
|
|
17
|
+
"jupyter",
|
|
18
|
+
"marimo",
|
|
19
|
+
"rec2020",
|
|
20
|
+
"pq",
|
|
21
|
+
"smpte-2084",
|
|
22
|
+
]
|
|
23
|
+
classifiers = [
|
|
24
|
+
"Development Status :: 4 - Beta",
|
|
25
|
+
"Intended Audience :: Science/Research",
|
|
26
|
+
"License :: OSI Approved :: MIT License",
|
|
27
|
+
"Operating System :: OS Independent",
|
|
28
|
+
"Programming Language :: Python :: 3",
|
|
29
|
+
"Programming Language :: Python :: 3.11",
|
|
30
|
+
"Programming Language :: Python :: 3.12",
|
|
31
|
+
"Programming Language :: Python :: 3.13",
|
|
32
|
+
"Programming Language :: Python :: 3.14",
|
|
33
|
+
"Topic :: Scientific/Engineering :: Visualization",
|
|
34
|
+
"Topic :: Multimedia :: Graphics",
|
|
35
|
+
"Framework :: Jupyter",
|
|
36
|
+
]
|
|
37
|
+
dependencies = [
|
|
38
|
+
"anywidget>=0.9",
|
|
39
|
+
"colour-science>=0.4",
|
|
40
|
+
"numpy>=1.24",
|
|
41
|
+
"pillow>=10",
|
|
42
|
+
"traitlets>=5",
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
[project.optional-dependencies]
|
|
46
|
+
dev = [
|
|
47
|
+
"pytest>=7",
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
[project.urls]
|
|
51
|
+
Homepage = "https://github.com/ktaletsk/hdrviz"
|
|
52
|
+
Repository = "https://github.com/ktaletsk/hdrviz"
|
|
53
|
+
Issues = "https://github.com/ktaletsk/hdrviz/issues"
|
|
54
|
+
|
|
55
|
+
[build-system]
|
|
56
|
+
requires = ["hatchling"]
|
|
57
|
+
build-backend = "hatchling.build"
|
|
58
|
+
|
|
59
|
+
[tool.hatch.version]
|
|
60
|
+
path = "hdrviz.py"
|
|
61
|
+
|
|
62
|
+
[tool.hatch.build.targets.wheel]
|
|
63
|
+
only-include = ["hdrviz.py"]
|
|
64
|
+
|
|
65
|
+
[tool.hatch.build.targets.sdist]
|
|
66
|
+
only-include = ["hdrviz.py", "README.md", "LICENSE", "pyproject.toml"]
|