Skip to content

Commit a90ecdc

Browse files
committed
Add nanobind ImageSpec and ParamValue bindings
Signed-off-by: Aleksandr Motsjonov <soswow@gmail.com>
1 parent 614b9e0 commit a90ecdc

6 files changed

Lines changed: 1050 additions & 11 deletions

File tree

src/python-nanobind/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
set (nanobind_srcs
66
py_oiio.cpp
7+
py_paramvalue.cpp
78
py_roi.cpp
89
py_imagespec.cpp
910
py_typedesc.cpp)

src/python-nanobind/py_imagespec.cpp

Lines changed: 234 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,51 @@ namespace {
88

99
OIIO_NAMESPACE_USING
1010

11+
nb::tuple
12+
imagespec_get_channelformats(const ImageSpec& spec, bool allow_empty = true)
13+
{
14+
std::vector<TypeDesc> formats;
15+
if (spec.channelformats.size() || !allow_empty)
16+
spec.get_channelformats(formats);
17+
return PyOpenImageIO::C_to_tuple(cspan<TypeDesc>(formats));
18+
}
19+
20+
21+
void
22+
imagespec_set_channelformats(ImageSpec& spec, nb::handle py_channelformats)
23+
{
24+
spec.channelformats.clear();
25+
PyOpenImageIO::py_to_stdvector(spec.channelformats, py_channelformats);
26+
}
27+
28+
29+
nb::tuple
30+
imagespec_get_channelnames(const ImageSpec& spec)
31+
{
32+
return PyOpenImageIO::C_to_tuple(cspan<std::string>(spec.channelnames));
33+
}
34+
35+
36+
void
37+
imagespec_set_channelnames(ImageSpec& spec, nb::handle py_channelnames)
38+
{
39+
spec.channelnames.clear();
40+
PyOpenImageIO::py_to_stdvector(spec.channelnames, py_channelnames);
41+
}
42+
43+
44+
nb::object
45+
imagespec_getattribute_typed(const ImageSpec& spec, const std::string& name,
46+
TypeDesc type = TypeUnknown)
47+
{
48+
ParamValue tmpparam;
49+
const ParamValue* p = spec.find_attribute(name, tmpparam, type);
50+
if (!p)
51+
return nb::none();
52+
return PyOpenImageIO::make_pyobject(p->data(), p->type(), p->nvalues());
53+
}
54+
55+
1156
ROI
1257
imagespec_get_roi(const ImageSpec& spec)
1358
{
@@ -43,16 +88,7 @@ namespace PyOpenImageIO {
4388
void
4489
declare_imagespec(nb::module_& m)
4590
{
46-
// This is intentionally not a full ImageSpec port yet. It only exists
47-
// to support ROI parity until py_imagespec.cpp grows into the real
48-
// binding.
4991
nb::class_<ImageSpec>(m, "ImageSpec")
50-
.def(nb::init<>())
51-
.def("__init__",
52-
[](ImageSpec* self, int xres, int yres, int nchans,
53-
const TypeDesc& format) {
54-
new (self) ImageSpec(xres, yres, nchans, format);
55-
})
5692
.def_rw("x", &ImageSpec::x)
5793
.def_rw("y", &ImageSpec::y)
5894
.def_rw("z", &ImageSpec::z)
@@ -65,9 +101,196 @@ declare_imagespec(nb::module_& m)
65101
.def_rw("full_width", &ImageSpec::full_width)
66102
.def_rw("full_height", &ImageSpec::full_height)
67103
.def_rw("full_depth", &ImageSpec::full_depth)
104+
.def_rw("tile_width", &ImageSpec::tile_width)
105+
.def_rw("tile_height", &ImageSpec::tile_height)
106+
.def_rw("tile_depth", &ImageSpec::tile_depth)
68107
.def_rw("nchannels", &ImageSpec::nchannels)
69-
.def_prop_ro("roi", &imagespec_get_roi)
70-
.def_prop_ro("roi_full", &imagespec_get_roi_full);
108+
.def_rw("format", &ImageSpec::format)
109+
.def_prop_rw(
110+
"channelformats",
111+
[](const ImageSpec& spec) {
112+
return imagespec_get_channelformats(spec);
113+
},
114+
&imagespec_set_channelformats)
115+
.def_prop_rw("channelnames", &imagespec_get_channelnames,
116+
&imagespec_set_channelnames)
117+
.def_rw("alpha_channel", &ImageSpec::alpha_channel)
118+
.def_rw("z_channel", &ImageSpec::z_channel)
119+
.def_rw("deep", &ImageSpec::deep)
120+
.def_prop_ro("extra_attribs",
121+
[](const ImageSpec& spec) { return spec.extra_attribs; })
122+
.def_prop_rw("roi", &imagespec_get_roi, &imagespec_set_roi)
123+
.def_prop_rw("roi_full", &imagespec_get_roi_full,
124+
&imagespec_set_roi_full)
125+
.def(nb::init<>())
126+
.def(nb::init<int, int, int, TypeDesc>())
127+
.def(nb::init<const ROI&, TypeDesc>())
128+
.def(nb::init<TypeDesc>())
129+
.def(nb::init<const ImageSpec&>())
130+
.def("copy", [](const ImageSpec& self) { return ImageSpec(self); })
131+
.def("set_format",
132+
[](ImageSpec& self, TypeDesc t) { self.set_format(t); })
133+
.def("default_channel_names", &ImageSpec::default_channel_names)
134+
.def("channel_bytes",
135+
[](const ImageSpec& spec) { return spec.channel_bytes(); })
136+
.def(
137+
"channel_bytes",
138+
[](const ImageSpec& spec, int chan, bool native) {
139+
return spec.channel_bytes(chan, native);
140+
},
141+
"channel"_a, "native"_a = false)
142+
.def(
143+
"pixel_bytes",
144+
[](const ImageSpec& spec, bool native) {
145+
return spec.pixel_bytes(native);
146+
},
147+
"native"_a = false)
148+
.def(
149+
"pixel_bytes",
150+
[](const ImageSpec& spec, int chbegin, int chend, bool native) {
151+
return spec.pixel_bytes(chbegin, chend, native);
152+
},
153+
"chbegin"_a, "chend"_a, "native"_a = false)
154+
.def(
155+
"scanline_bytes",
156+
[](const ImageSpec& spec, bool native) {
157+
return spec.scanline_bytes(native);
158+
},
159+
"native"_a = false)
160+
.def("scanline_bytes",
161+
[](const ImageSpec& spec, TypeDesc type) {
162+
return spec.scanline_bytes(type);
163+
})
164+
.def(
165+
"tile_bytes",
166+
[](const ImageSpec& spec, bool native) {
167+
return spec.tile_bytes(native);
168+
},
169+
"native"_a = false)
170+
.def("tile_bytes", [](const ImageSpec& spec,
171+
TypeDesc type) { return spec.tile_bytes(type); })
172+
.def(
173+
"image_bytes",
174+
[](const ImageSpec& spec, bool native) {
175+
return spec.image_bytes(native);
176+
},
177+
"native"_a = false)
178+
.def("image_bytes",
179+
[](const ImageSpec& spec, TypeDesc type) {
180+
return spec.image_bytes(type);
181+
})
182+
.def("tile_pixels", &ImageSpec::tile_pixels)
183+
.def("image_pixels", &ImageSpec::image_pixels)
184+
.def("size_t_safe", &ImageSpec::size_t_safe)
185+
.def("channelformat", [](const ImageSpec& spec,
186+
int chan) { return spec.channelformat(chan); })
187+
.def("channel_name",
188+
[](const ImageSpec& spec, int chan) {
189+
return std::string(spec.channel_name(chan));
190+
})
191+
.def("channelindex",
192+
[](const ImageSpec& spec, const std::string& name) {
193+
return spec.channelindex(name);
194+
})
195+
.def("get_channelformats",
196+
[](const ImageSpec& spec) {
197+
return imagespec_get_channelformats(spec, false);
198+
})
199+
.def("attribute",
200+
[](ImageSpec& spec, const std::string& name, nb::handle obj) {
201+
attribute_onearg(spec, name, obj);
202+
})
203+
.def("attribute",
204+
[](ImageSpec& spec, const std::string& name, TypeDesc type,
205+
nb::handle obj) { attribute_typed(spec, name, type, obj); })
206+
.def(
207+
"get_int_attribute",
208+
[](const ImageSpec& spec, const std::string& name, int def) {
209+
return spec.get_int_attribute(name, def);
210+
},
211+
"name"_a, "defaultval"_a = 0)
212+
.def(
213+
"get_float_attribute",
214+
[](const ImageSpec& spec, const std::string& name, float def) {
215+
return spec.get_float_attribute(name, def);
216+
},
217+
"name"_a, "defaultval"_a = 0.0f)
218+
.def(
219+
"get_string_attribute",
220+
[](const ImageSpec& spec, const std::string& name,
221+
const std::string& def) {
222+
return std::string(spec.get_string_attribute(name, def));
223+
},
224+
"name"_a, "defaultval"_a = "")
225+
.def("getattribute", &imagespec_getattribute_typed, "name"_a,
226+
"type"_a = TypeUnknown)
227+
.def(
228+
"get",
229+
[](const ImageSpec& self, const std::string& key, nb::handle def) {
230+
ParamValue tmpparam;
231+
auto p = self.find_attribute(key, tmpparam);
232+
if (!p)
233+
return nb::borrow(def);
234+
return make_pyobject(p->data(), p->type(), 1, def);
235+
},
236+
"key"_a, "default"_a = nb::none())
237+
.def(
238+
"erase_attribute",
239+
[](ImageSpec& spec, const std::string& name, TypeDesc type,
240+
bool casesensitive) {
241+
return spec.erase_attribute(name, type, casesensitive);
242+
},
243+
"name"_a = "", "type"_a = TypeUnknown, "casesensitive"_a = false)
244+
.def_static(
245+
"metadata_val",
246+
[](const ParamValue& p, bool human) {
247+
return std::string(ImageSpec::metadata_val(p, human));
248+
},
249+
"param"_a, "human"_a = false)
250+
.def(
251+
"serialize",
252+
[](const ImageSpec& spec, const std::string& format,
253+
const std::string& verbose) {
254+
ImageSpec::SerialFormat fmt = ImageSpec::SerialText;
255+
if (Strutil::iequals(format, "xml"))
256+
fmt = ImageSpec::SerialXML;
257+
ImageSpec::SerialVerbose verb = ImageSpec::SerialDetailed;
258+
if (Strutil::iequals(verbose, "brief"))
259+
verb = ImageSpec::SerialBrief;
260+
else if (Strutil::iequals(verbose, "detailed"))
261+
verb = ImageSpec::SerialDetailed;
262+
else if (Strutil::iequals(verbose, "detailedhuman"))
263+
verb = ImageSpec::SerialDetailedHuman;
264+
return std::string(spec.serialize(fmt, verb));
265+
},
266+
"format"_a = "text", "verbose"_a = "detailed")
267+
.def(
268+
"set_colorspace",
269+
[](ImageSpec& self, const std::string& cs) {
270+
self.set_colorspace(cs);
271+
},
272+
"name"_a)
273+
.def("__getitem__",
274+
[](const ImageSpec& self, const std::string& key) {
275+
ParamValue tmpparam;
276+
auto p = self.find_attribute(key, tmpparam);
277+
if (p == nullptr) {
278+
std::string message = "key '" + key + "' does not exist";
279+
throw nb::key_error(message.c_str());
280+
}
281+
return make_pyobject(p->data(), p->type());
282+
})
283+
.def("__setitem__",
284+
[](ImageSpec& self, const std::string& key, nb::handle val) {
285+
delegate_setitem(self, key, val);
286+
})
287+
.def("__delitem__",
288+
[](ImageSpec& self, const std::string& key) {
289+
self.erase_attribute(key);
290+
})
291+
.def("__contains__", [](const ImageSpec& self, const std::string& key) {
292+
return self.extra_attribs.contains(key);
293+
});
71294

72295
m.def("get_roi", &imagespec_get_roi);
73296
m.def("get_roi_full", &imagespec_get_roi_full);

0 commit comments

Comments
 (0)