Skip to content

Commit 1a3b5ce

Browse files
committed
added a HDR demo executable (Python and C++)
1 parent 372a104 commit 1a3b5ce

File tree

4 files changed

+258
-0
lines changed

4 files changed

+258
-0
lines changed

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@ example_icons.bc
2424
example_icons.js
2525
example_icons.wasm
2626
example_icons.exe
27+
example_hdr
28+
example_hdr.bc
29+
example_hdr.js
30+
example_hdr.wasm
31+
example_hdr.exe
2732
icons
2833
*~
2934
nanogui*.so

CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -546,8 +546,10 @@ if (NANOGUI_BUILD_EXAMPLES)
546546
add_executable(example3 src/example3.cpp)
547547
add_executable(example4 src/example4.cpp)
548548
add_executable(example_icons src/example_icons.cpp)
549+
add_executable(example_hdr src/example_hdr.cpp)
549550

550551
target_link_libraries(example1 nanogui)
552+
target_link_libraries(example_hdr nanogui)
551553
target_link_libraries(example2 nanogui)
552554
target_link_libraries(example3 nanogui ${NANOGUI_LIBS}) # For OpenGL
553555
target_link_libraries(example4 nanogui)

src/example_hdr.cpp

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
#include <nanogui/screen.h>
2+
#include <nanogui/window.h>
3+
#include <nanogui/layout.h>
4+
#include <nanogui/label.h>
5+
#include <nanogui/combobox.h>
6+
#include <nanogui/imageview.h>
7+
#include <nanogui/texture.h>
8+
#include <nanogui/chroma.h>
9+
#include <algorithm>
10+
#include <cmath>
11+
#include <vector>
12+
13+
using namespace nanogui;
14+
using namespace nanogui::ituth273;
15+
16+
struct PrimaryEntry {
17+
ColorPrimaries primary;
18+
const char* name;
19+
};
20+
21+
static const PrimaryEntry primary_table[] = {
22+
{ ColorPrimaries::BT2020, "BT2020" },
23+
{ ColorPrimaries::BT470BG, "BT470BG" },
24+
{ ColorPrimaries::BT470M, "BT470M" },
25+
{ ColorPrimaries::BT709, "BT709" },
26+
{ ColorPrimaries::Film, "Film" },
27+
{ ColorPrimaries::SMPTE170M, "SMPTE170M" },
28+
{ ColorPrimaries::SMPTE240M, "SMPTE240M" },
29+
{ ColorPrimaries::SMPTE428, "SMPTE428" },
30+
{ ColorPrimaries::SMPTE431, "SMPTE431" },
31+
{ ColorPrimaries::SMPTE432, "SMPTE432" }
32+
};
33+
static const int num_primaries = sizeof(primary_table) / sizeof(primary_table[0]);
34+
35+
class HDRGamutTest : public Screen {
36+
public:
37+
HDRGamutTest() : Screen(Vector2i(800, 600), "NanoGUI test", /* resizable */ true, /* maximized */ false,
38+
/* fullscreen */ false, /* depth_buffer */ true, /* stencil_buffer */ false,
39+
/* float_buffer */ true, /* gl_major */ 3, /* gl_minor */ 2) {
40+
inc_ref();
41+
42+
Window *window = new Window(this, "HDR & Color gamut test");
43+
window->set_layout(new GroupLayout());
44+
45+
m_texture = new Texture(
46+
Texture::PixelFormat::RGBA,
47+
Texture::ComponentFormat::Float32,
48+
Vector2i(512, 400),
49+
Texture::InterpolationMode::Nearest,
50+
Texture::InterpolationMode::Nearest
51+
);
52+
53+
new Label(window, "Primaries");
54+
55+
std::vector<std::string> primary_names;
56+
for (int i = 0; i < num_primaries; ++i) {
57+
primary_names.push_back(primary_table[i].name);
58+
}
59+
60+
ColorPrimaries screen_primary = from_screen(this);
61+
int primary_index = 0;
62+
for (int i = 0; i < num_primaries; ++i) {
63+
if (primary_table[i].primary == screen_primary) {
64+
primary_index = i;
65+
break;
66+
}
67+
}
68+
69+
ComboBox *primaries_cbox = new ComboBox(window, primary_names);
70+
primaries_cbox->set_selected_index(primary_index);
71+
72+
primaries_cbox->set_callback([this](int index) {
73+
auto chroma_array = chroma(primary_table[index].primary);
74+
m_rec709_matrix = chroma_to_rec709_matrix(chroma_array);
75+
update_texture();
76+
});
77+
78+
// Initialize with current selection
79+
auto chroma_array = chroma(primary_table[primary_index].primary);
80+
m_rec709_matrix = chroma_to_rec709_matrix(chroma_array);
81+
82+
new Label(window, "Linear ramps (0..4), bars mark integer values. Top: LDR sRGB, bottom: HDR with chosen primaries.");
83+
84+
ImageView *img = new ImageView(window);
85+
img->set_image(m_texture);
86+
img->set_size(Vector2i(512, 400));
87+
img->set_scale(pixel_ratio());
88+
89+
perform_layout();
90+
window->set_position(Vector2i(5, 5));
91+
92+
update_texture();
93+
}
94+
95+
private:
96+
float to_srgb(float value) {
97+
float sign = value < 0 ? -1.0f : 1.0f;
98+
value = std::abs(value);
99+
return sign * (value < 0.0031308f ? value * 12.92f
100+
: 1.055f * std::pow(value, 1.0f/2.4f) - 0.055f);
101+
}
102+
103+
void update_texture() {
104+
const int width = 512, height = 400;
105+
std::vector<float> img(width * height * 4, 0.0f);
106+
107+
// Draw vertical grid lines at quarter positions
108+
for (int y = 0; y < height; ++y) {
109+
for (int x : {0, width/4, width/2, 3*width/4, width-1}) {
110+
float* p = &img[(y * width + x) * 4];
111+
p[0] = p[1] = p[2] = p[3] = 1.0f;
112+
}
113+
}
114+
115+
// Draw color bars: red, green, blue, white
116+
const struct { int y0, y1; Vector3f srgb, primary; } bars[] = {
117+
{10, 90, {1, 0, 0}, m_rec709_matrix[0]},
118+
{110, 190, {0, 1, 0}, m_rec709_matrix[1]},
119+
{210, 290, {0, 0, 1}, m_rec709_matrix[2]},
120+
{310, 390, {1, 1, 1}, {1, 1, 1}}
121+
};
122+
123+
for (auto& bar : bars) {
124+
int mid_y = (bar.y0 + bar.y1) / 2;
125+
for (int y = bar.y0; y < bar.y1; ++y) {
126+
bool is_srgb = (y < mid_y);
127+
for (int x = 0; x < width; ++x) {
128+
float ramp = x * 4.0f / (width - 1);
129+
float* p = &img[(y * width + x) * 4];
130+
for (int c = 0; c < 3; ++c) {
131+
float val = ramp * (is_srgb ? bar.srgb[c] : bar.primary[c]);
132+
p[c] = to_srgb(is_srgb ? std::min(val, 1.0f) : val);
133+
}
134+
p[3] = 1.0f;
135+
}
136+
}
137+
}
138+
139+
m_texture->upload((uint8_t*)img.data());
140+
}
141+
142+
private:
143+
ref<Texture> m_texture;
144+
Matrix3f m_rec709_matrix;
145+
};
146+
147+
int main(int /* argc */, char ** /* argv */) {
148+
nanogui::init();
149+
150+
{
151+
ref<HDRGamutTest> app = new HDRGamutTest();
152+
app->dec_ref();
153+
app->draw_all();
154+
app->set_visible(true);
155+
nanogui::run(RunMode::Lazy);
156+
}
157+
158+
nanogui::shutdown();
159+
return 0;
160+
}

src/python/example_hdr.py

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import nanogui as ng
2+
import numpy as np
3+
4+
class HDRGamutTest(ng.Screen):
5+
def __init__(self):
6+
super(HDRGamutTest, self).__init__(size=(800, 600), caption='NanoGUI test', float_buffer=True)
7+
window = ng.Window(self, 'HDR & Color gamut test')
8+
window.set_layout(ng.GroupLayout())
9+
10+
self.texture = ng.Texture(
11+
pixel_format=ng.Texture.PixelFormat.RGBA,
12+
component_format=ng.Texture.ComponentFormat.Float32,
13+
min_interpolation_mode=ng.Texture.InterpolationMode.Nearest,
14+
mag_interpolation_mode=ng.Texture.InterpolationMode.Nearest,
15+
size=[512, 400],
16+
)
17+
18+
ng.Label(window, "Primaries")
19+
primaries = sorted(ng.ituth273.ColorPrimaries, key=lambda x: x.__name__)
20+
primary_index = primaries.index(ng.ituth273.from_screen(self))
21+
primaries_cbox = ng.ComboBox(window, [p.__name__ for p in primaries])
22+
primaries_cbox.set_selected_index(primary_index)
23+
24+
def primary_cb(index):
25+
p = ng.ituth273.chroma(primaries[index])
26+
rec709_matrix = ng.ituth273.chroma_to_rec709_matrix(p)
27+
self.rec709_matrix = np.from_dlpack(rec709_matrix)
28+
self.update_texture()
29+
30+
primaries_cbox.set_callback(primary_cb)
31+
primary_cb(primary_index)
32+
33+
ng.Label(window, "Linear ramps (0..4), bars mark integer values. Top: LDR sRGB, bottom: HDR with chosen primaries.")
34+
img = ng.ImageView(window)
35+
img.set_image(self.texture)
36+
img.set_size((512, 400))
37+
img.set_scale(self.pixel_ratio())
38+
39+
self.perform_layout()
40+
window.set_position((5, 5))
41+
42+
def create_color_bar(self, y_start, y_end, primary_color, srgb_color, max_value=4):
43+
img = np.zeros((y_end - y_start, 512, 4), dtype=np.float32)
44+
mid_y = (y_end - y_start) // 2
45+
46+
ramp = np.linspace(0, max_value, 512)
47+
for i in range(3):
48+
img[:mid_y, :, i] = np.minimum(ramp * srgb_color[i], 1)
49+
img[mid_y:, :, i] = ramp * primary_color[i]
50+
img[..., 3] = 1
51+
52+
return img
53+
54+
def to_srgb(self, value):
55+
sign = np.sign(value)
56+
value = abs(value)
57+
return np.where(value < 0.0031308,
58+
value * 12.92,
59+
1.055 * value**(1.0/2.4) - 0.055) * sign
60+
61+
def update_texture(self):
62+
"""Update texture with color bars using rec709_matrix primaries and sRGB comparison"""
63+
img = np.zeros((400, 512, 4), dtype=np.float32)
64+
65+
# Vertical grid lines
66+
img[:, 0*512//4, :] = img[:, 1*512//4, :] = 1
67+
img[:, 2*512//4, :] = img[:, 3*512//4, :] = 1
68+
img[:, 511, :] = 1
69+
70+
bars = [
71+
(10, 90, (1, 0, 0), self.rec709_matrix[:, 0]),
72+
(110, 190, (0, 1, 0), self.rec709_matrix[:, 1]),
73+
(210, 290, (0, 0, 1), self.rec709_matrix[:, 2]),
74+
(310, 390, (1, 1, 1), (1, 1, 1)),
75+
]
76+
77+
for y_start, y_end, srgb, primary in bars:
78+
img[y_start:y_end, :, :] = self.create_color_bar(y_start, y_end, primary, srgb)
79+
80+
self.texture.upload(self.to_srgb(img))
81+
82+
def main():
83+
ng.init()
84+
app = HDRGamutTest()
85+
app.set_visible(True)
86+
ng.run(ng.RunMode.Lazy)
87+
del app
88+
ng.shutdown()
89+
90+
if __name__ == '__main__':
91+
main()

0 commit comments

Comments
 (0)