chrono_batch_render/
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
66 / 154
42.9%
Branches:
20 / 105
19.0%
Functions:
2 / 15
13.3%

python/src/io.cpp
Line Branch Exec Source
1 // SPDX-License-Identifier: BSD-3-Clause
2 #include <algorithm>
3 #include <filesystem>
4 #include <optional>
5 #include <vector>
6
7 #include <nanobind/ndarray.h>
8 #include <nanobind/stl/filesystem.h>
9 #include <nanobind/stl/optional.h>
10 #include <nanobind/stl/vector.h>
11
12 #include "bindings.h"
13
14 namespace nb = nanobind;
15
16 namespace chrono::batch_render::python {
17
18 namespace {
19
20 using HostArray = nb::ndarray<nb::ro, nb::device::cpu, nb::c_contig>;
21 template <class T> using TypedArray = nb::ndarray<const T, nb::device::cpu, nb::c_contig>;
22
23
0/6
chrono::batch_render::Pixels chrono::batch_render::python::(anonymous namespace)::ToPixels<nanobind::ndarray<float const, nanobind::device::cpu, nanobind::c_contig> >(nanobind::ndarray<float const, nanobind::device::cpu, nanobind::c_contig> const&):
✗ Branch 0 not taken.
✗ Branch 1 not taken.
chrono::batch_render::Pixels chrono::batch_render::python::(anonymous namespace)::ToPixels<nanobind::ndarray<nanobind::ro, nanobind::device::cpu, nanobind::c_contig> >(nanobind::ndarray<nanobind::ro, nanobind::device::cpu, nanobind::c_contig> const&):
✗ Branch 0 not taken.
✗ Branch 1 not taken.
chrono::batch_render::Pixels chrono::batch_render::python::(anonymous namespace)::ToPixels<nanobind::ndarray<unsigned int const, nanobind::device::cpu, nanobind::c_contig> >(nanobind::ndarray<unsigned int const, nanobind::device::cpu, nanobind::c_contig> const&):
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ template <class Array> Pixels ToPixels(const Array& array) {
24
0/12
chrono::batch_render::Pixels chrono::batch_render::python::(anonymous namespace)::ToPixels<nanobind::ndarray<float const, nanobind::device::cpu, nanobind::c_contig> >(nanobind::ndarray<float const, nanobind::device::cpu, nanobind::c_contig> const&):
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
chrono::batch_render::Pixels chrono::batch_render::python::(anonymous namespace)::ToPixels<nanobind::ndarray<nanobind::ro, nanobind::device::cpu, nanobind::c_contig> >(nanobind::ndarray<nanobind::ro, nanobind::device::cpu, nanobind::c_contig> const&):
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
chrono::batch_render::Pixels chrono::batch_render::python::(anonymous namespace)::ToPixels<nanobind::ndarray<unsigned int const, nanobind::device::cpu, nanobind::c_contig> >(nanobind::ndarray<unsigned int const, nanobind::device::cpu, nanobind::c_contig> const&):
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ if (array.ndim() != 2 && array.ndim() != 3) {
25
0/3
chrono::batch_render::Pixels chrono::batch_render::python::(anonymous namespace)::ToPixels<nanobind::ndarray<float const, nanobind::device::cpu, nanobind::c_contig> >(nanobind::ndarray<float const, nanobind::device::cpu, nanobind::c_contig> const&):
✗ Branch 2 not taken.
chrono::batch_render::Pixels chrono::batch_render::python::(anonymous namespace)::ToPixels<nanobind::ndarray<nanobind::ro, nanobind::device::cpu, nanobind::c_contig> >(nanobind::ndarray<nanobind::ro, nanobind::device::cpu, nanobind::c_contig> const&):
✗ Branch 2 not taken.
chrono::batch_render::Pixels chrono::batch_render::python::(anonymous namespace)::ToPixels<nanobind::ndarray<unsigned int const, nanobind::device::cpu, nanobind::c_contig> >(nanobind::ndarray<unsigned int const, nanobind::device::cpu, nanobind::c_contig> const&):
✗ Branch 2 not taken.
✗ throw nb::value_error("Images are (rows, cols[, channels]) arrays");
26 }
27 ✗ const auto rows = int64_t(array.shape(0));
28 ✗ const auto cols = int64_t(array.shape(1));
29
0/12
chrono::batch_render::Pixels chrono::batch_render::python::(anonymous namespace)::ToPixels<nanobind::ndarray<float const, nanobind::device::cpu, nanobind::c_contig> >(nanobind::ndarray<float const, nanobind::device::cpu, nanobind::c_contig> const&):
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
chrono::batch_render::Pixels chrono::batch_render::python::(anonymous namespace)::ToPixels<nanobind::ndarray<nanobind::ro, nanobind::device::cpu, nanobind::c_contig> >(nanobind::ndarray<nanobind::ro, nanobind::device::cpu, nanobind::c_contig> const&):
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
chrono::batch_render::Pixels chrono::batch_render::python::(anonymous namespace)::ToPixels<nanobind::ndarray<unsigned int const, nanobind::device::cpu, nanobind::c_contig> >(nanobind::ndarray<unsigned int const, nanobind::device::cpu, nanobind::c_contig> const&):
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ const int64_t channels = array.ndim() == 3 ? int64_t(array.shape(2)) : 1;
30
0/6
chrono::batch_render::Pixels chrono::batch_render::python::(anonymous namespace)::ToPixels<nanobind::ndarray<float const, nanobind::device::cpu, nanobind::c_contig> >(nanobind::ndarray<float const, nanobind::device::cpu, nanobind::c_contig> const&):
✗ Branch 0 not taken.
✗ Branch 1 not taken.
chrono::batch_render::Pixels chrono::batch_render::python::(anonymous namespace)::ToPixels<nanobind::ndarray<nanobind::ro, nanobind::device::cpu, nanobind::c_contig> >(nanobind::ndarray<nanobind::ro, nanobind::device::cpu, nanobind::c_contig> const&):
✗ Branch 0 not taken.
✗ Branch 1 not taken.
chrono::batch_render::Pixels chrono::batch_render::python::(anonymous namespace)::ToPixels<nanobind::ndarray<unsigned int const, nanobind::device::cpu, nanobind::c_contig> >(nanobind::ndarray<unsigned int const, nanobind::device::cpu, nanobind::c_contig> const&):
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ const void* data = array.data();
31 ✗ const nb::dlpack::dtype dtype = array.dtype();
32
0/6
chrono::batch_render::Pixels chrono::batch_render::python::(anonymous namespace)::ToPixels<nanobind::ndarray<float const, nanobind::device::cpu, nanobind::c_contig> >(nanobind::ndarray<float const, nanobind::device::cpu, nanobind::c_contig> const&):
✗ Branch 0 not taken.
✗ Branch 1 not taken.
chrono::batch_render::Pixels chrono::batch_render::python::(anonymous namespace)::ToPixels<nanobind::ndarray<nanobind::ro, nanobind::device::cpu, nanobind::c_contig> >(nanobind::ndarray<nanobind::ro, nanobind::device::cpu, nanobind::c_contig> const&):
✗ Branch 0 not taken.
✗ Branch 1 not taken.
chrono::batch_render::Pixels chrono::batch_render::python::(anonymous namespace)::ToPixels<nanobind::ndarray<unsigned int const, nanobind::device::cpu, nanobind::c_contig> >(nanobind::ndarray<unsigned int const, nanobind::device::cpu, nanobind::c_contig> const&):
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ if (dtype == nb::dtype<double>()) {
33 ✗ const auto* values = static_cast<const double*>(data);
34 ✗ std::vector<float> floats(array.size());
35 ✗ std::transform(values, values + array.size(), floats.begin(),
36 ✗ [](double value) { return float(value); });
37
0/3
chrono::batch_render::Pixels chrono::batch_render::python::(anonymous namespace)::ToPixels<nanobind::ndarray<float const, nanobind::device::cpu, nanobind::c_contig> >(nanobind::ndarray<float const, nanobind::device::cpu, nanobind::c_contig> const&):
✗ Branch 1 not taken.
chrono::batch_render::Pixels chrono::batch_render::python::(anonymous namespace)::ToPixels<nanobind::ndarray<nanobind::ro, nanobind::device::cpu, nanobind::c_contig> >(nanobind::ndarray<nanobind::ro, nanobind::device::cpu, nanobind::c_contig> const&):
✗ Branch 1 not taken.
chrono::batch_render::Pixels chrono::batch_render::python::(anonymous namespace)::ToPixels<nanobind::ndarray<unsigned int const, nanobind::device::cpu, nanobind::c_contig> >(nanobind::ndarray<unsigned int const, nanobind::device::cpu, nanobind::c_contig> const&):
✗ Branch 1 not taken.
✗ return Pixels(std::move(floats), rows, cols, channels);
38 ✗ }
39 ✗ const device::DType type =
40
0/6
chrono::batch_render::Pixels chrono::batch_render::python::(anonymous namespace)::ToPixels<nanobind::ndarray<float const, nanobind::device::cpu, nanobind::c_contig> >(nanobind::ndarray<float const, nanobind::device::cpu, nanobind::c_contig> const&):
✗ Branch 0 not taken.
✗ Branch 1 not taken.
chrono::batch_render::Pixels chrono::batch_render::python::(anonymous namespace)::ToPixels<nanobind::ndarray<nanobind::ro, nanobind::device::cpu, nanobind::c_contig> >(nanobind::ndarray<nanobind::ro, nanobind::device::cpu, nanobind::c_contig> const&):
✗ Branch 0 not taken.
✗ Branch 1 not taken.
chrono::batch_render::Pixels chrono::batch_render::python::(anonymous namespace)::ToPixels<nanobind::ndarray<unsigned int const, nanobind::device::cpu, nanobind::c_contig> >(nanobind::ndarray<unsigned int const, nanobind::device::cpu, nanobind::c_contig> const&):
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ dtype == nb::dtype<uint8_t>() ? device::DType::UInt8
41
0/6
chrono::batch_render::Pixels chrono::batch_render::python::(anonymous namespace)::ToPixels<nanobind::ndarray<float const, nanobind::device::cpu, nanobind::c_contig> >(nanobind::ndarray<float const, nanobind::device::cpu, nanobind::c_contig> const&):
✗ Branch 0 not taken.
✗ Branch 1 not taken.
chrono::batch_render::Pixels chrono::batch_render::python::(anonymous namespace)::ToPixels<nanobind::ndarray<nanobind::ro, nanobind::device::cpu, nanobind::c_contig> >(nanobind::ndarray<nanobind::ro, nanobind::device::cpu, nanobind::c_contig> const&):
✗ Branch 0 not taken.
✗ Branch 1 not taken.
chrono::batch_render::Pixels chrono::batch_render::python::(anonymous namespace)::ToPixels<nanobind::ndarray<unsigned int const, nanobind::device::cpu, nanobind::c_contig> >(nanobind::ndarray<unsigned int const, nanobind::device::cpu, nanobind::c_contig> const&):
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ : dtype == nb::dtype<uint32_t>() ? device::DType::UInt32
42
0/6
chrono::batch_render::Pixels chrono::batch_render::python::(anonymous namespace)::ToPixels<nanobind::ndarray<float const, nanobind::device::cpu, nanobind::c_contig> >(nanobind::ndarray<float const, nanobind::device::cpu, nanobind::c_contig> const&):
✗ Branch 0 not taken.
✗ Branch 1 not taken.
chrono::batch_render::Pixels chrono::batch_render::python::(anonymous namespace)::ToPixels<nanobind::ndarray<nanobind::ro, nanobind::device::cpu, nanobind::c_contig> >(nanobind::ndarray<nanobind::ro, nanobind::device::cpu, nanobind::c_contig> const&):
✗ Branch 0 not taken.
✗ Branch 1 not taken.
chrono::batch_render::Pixels chrono::batch_render::python::(anonymous namespace)::ToPixels<nanobind::ndarray<unsigned int const, nanobind::device::cpu, nanobind::c_contig> >(nanobind::ndarray<unsigned int const, nanobind::device::cpu, nanobind::c_contig> const&):
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ : dtype == nb::dtype<float>()
43 ? device::DType::Float32
44
0/3
chrono::batch_render::Pixels chrono::batch_render::python::(anonymous namespace)::ToPixels<nanobind::ndarray<float const, nanobind::device::cpu, nanobind::c_contig> >(nanobind::ndarray<float const, nanobind::device::cpu, nanobind::c_contig> const&):
✗ Branch 2 not taken.
chrono::batch_render::Pixels chrono::batch_render::python::(anonymous namespace)::ToPixels<nanobind::ndarray<nanobind::ro, nanobind::device::cpu, nanobind::c_contig> >(nanobind::ndarray<nanobind::ro, nanobind::device::cpu, nanobind::c_contig> const&):
✗ Branch 2 not taken.
chrono::batch_render::Pixels chrono::batch_render::python::(anonymous namespace)::ToPixels<nanobind::ndarray<unsigned int const, nanobind::device::cpu, nanobind::c_contig> >(nanobind::ndarray<unsigned int const, nanobind::device::cpu, nanobind::c_contig> const&):
✗ Branch 2 not taken.
✗ : throw nb::type_error("Images are uint8, uint32, float32 or float64 arrays");
45 return Pixels(Span<const std::byte>(static_cast<const std::byte*>(data), array.nbytes()), type,
46 ✗ rows, cols, channels);
47 }
48
49 ✗ nb::object ToNumpy(Pixels pixels) {
50 ✗ const std::vector<size_t> shape = {size_t(pixels.rows), size_t(pixels.cols),
51 ✗ size_t(pixels.channels)};
52
0/2
✗ Branch 1 not taken.
✗ Branch 4 not taken.
✗ return Numpy(std::move(pixels.bytes), shape, DataType(pixels.type));
53 ✗ }
54
55 }
56
57 2 void BindIo(nb::module_& module) {
58 2 module.def(
59 "write_image",
60 4 [](const std::filesystem::path& path, const device::Tensor& image) {
61
2/2
✓ Branch 2 taken 2 times.
✓ Branch 5 taken 2 times.
4 return WriteImage(path, image);
62 },
63 2 nb::arg("path"), nb::arg("image"), nb::call_guard<nb::gil_scoped_release>());
64 2 module.def(
65 "write_image",
66 2 [](const std::filesystem::path& path, const HostArray& image) {
67
0/2
✗ Branch 2 not taken.
✗ Branch 5 not taken.
✗ return WriteImage(path, ToPixels(image));
68 },
69 2 nb::arg("path"), nb::arg("image"));
70
71 2 nb::enum_<Colormap>(module, "Colormap")
72 2 .value("GRAYSCALE", Colormap::Grayscale)
73 2 .value("TURBO", Colormap::Turbo)
74 2 .value("VIRIDIS", Colormap::Viridis);
75
76 2 module.def(
77 "colorize_scalars",
78 4 [](const device::Tensor& values, Colormap colormap, std::optional<float> minimum,
79 std::optional<float> maximum, bool logarithmic, bool reversed, bool centered,
80 uint32_t channel) {
81 2 return ColorizeScalars(
82
1/1
✓ Branch 1 taken 2 times.
2 values, {colormap, minimum, maximum, logarithmic, reversed, centered, channel});
83 },
84
1/1
✓ Branch 1 taken 2 times.
4 nb::arg("values"), nb::kw_only(), nb::arg("colormap") = Colormap::Grayscale,
85
1/1
✓ Branch 1 taken 2 times.
4 nb::arg("minimum") = nb::none(), nb::arg("maximum") = nb::none(),
86
3/3
✓ Branch 1 taken 2 times.
✓ Branch 4 taken 2 times.
✓ Branch 7 taken 2 times.
8 nb::arg("logarithmic") = false, nb::arg("reversed") = false, nb::arg("centered") = false,
87
1/1
✓ Branch 2 taken 2 times.
4 nb::arg("channel") = 0, nb::call_guard<nb::gil_scoped_release>());
88 2 module.def(
89 "colorize_scalars",
90 2 [](const TypedArray<float>& values, Colormap colormap, std::optional<float> minimum,
91 std::optional<float> maximum, bool logarithmic, bool reversed, bool centered,
92 uint32_t channel) {
93 ✗ return ToNumpy(
94
0/1
✗ Branch 2 not taken.
✗ ColorizeScalars(ToPixels(values), {colormap, minimum, maximum, logarithmic,
95
0/1
✗ Branch 1 not taken.
✗ reversed, centered, channel}));
96 },
97
1/1
✓ Branch 1 taken 2 times.
4 nb::arg("values"), nb::kw_only(), nb::arg("colormap") = Colormap::Grayscale,
98
1/1
✓ Branch 1 taken 2 times.
4 nb::arg("minimum") = nb::none(), nb::arg("maximum") = nb::none(),
99
3/3
✓ Branch 1 taken 2 times.
✓ Branch 4 taken 2 times.
✓ Branch 7 taken 2 times.
8 nb::arg("logarithmic") = false, nb::arg("reversed") = false, nb::arg("centered") = false,
100
1/1
✓ Branch 2 taken 2 times.
4 nb::arg("channel") = 0);
101
102 2 module.def("colorize_hdr", nb::overload_cast<const device::Tensor&, float>(&ColorizeHdr),
103 4 nb::arg("color"), nb::arg("exposure") = 0.0f,
104 2 nb::call_guard<nb::gil_scoped_release>());
105 2 module.def(
106 "colorize_hdr",
107 2 [](const TypedArray<float>& color, float exposure) {
108
0/2
✗ Branch 2 not taken.
✗ Branch 5 not taken.
✗ return ToNumpy(ColorizeHdr(ToPixels(color), exposure));
109 },
110 4 nb::arg("color"), nb::arg("exposure") = 0.0f);
111
112 2 module.def("colorize_normals", nb::overload_cast<const device::Tensor&>(&ColorizeNormals),
113 2 nb::arg("normals"), nb::call_guard<nb::gil_scoped_release>());
114 2 module.def(
115 "colorize_normals",
116 2 [](const TypedArray<float>& normals) {
117
0/2
✗ Branch 2 not taken.
✗ Branch 5 not taken.
✗ return ToNumpy(ColorizeNormals(ToPixels(normals)));
118 },
119 2 nb::arg("normals"));
120
121 2 module.def("colorize_ids", nb::overload_cast<const device::Tensor&>(&ColorizeIds),
122 2 nb::arg("ids"), nb::call_guard<nb::gil_scoped_release>());
123 2 module.def(
124 "colorize_ids",
125
0/2
✗ Branch 2 not taken.
✗ Branch 5 not taken.
✗ [](const TypedArray<uint32_t>& ids) { return ToNumpy(ColorizeIds(ToPixels(ids))); },
126 2 nb::arg("ids"));
127
128 2 module.def(
129 "write_render_var",
130 2 [](const std::filesystem::path& path, const RenderVarOutput& renderVar, int64_t camera) {
131
0/1
✗ Branch 2 not taken.
✗ return WriteRenderVar(path, renderVar, camera);
132 },
133 4 nb::arg("path"), nb::arg("render_var"), nb::arg("camera") = 0,
134 2 nb::call_guard<nb::gil_scoped_release>());
135 2 module.def(
136 "write_frame",
137 3 [](const std::filesystem::path& directory, const FrameOutput& frame, bool raw) {
138
1/1
✓ Branch 1 taken 1 time.
1 return WriteFrame(directory, frame, raw);
139 },
140 4 nb::arg("directory"), nb::arg("frame"), nb::arg("raw") = false,
141 2 nb::call_guard<nb::gil_scoped_release>());
142
143 2 nb::class_<ImageWriter>(module, "ImageWriter")
144
2/2
✓ Branch 1 taken 2 times.
✓ Branch 4 taken 2 times.
10 .def(nb::init<uint32_t, uint32_t>(), nb::arg("threads") = 4, nb::arg("max_pending") = 64)
145 2 .def(
146 "write_image",
147 ✗ [](ImageWriter& writer, std::filesystem::path path, const device::Tensor& image) {
148
0/1
✗ Branch 2 not taken.
✗ writer.WriteImage(std::move(path), image);
149 ✗ },
150 2 nb::arg("path"), nb::arg("image"), nb::call_guard<nb::gil_scoped_release>())
151 2 .def(
152 "write_image",
153 ✗ [](ImageWriter& writer, std::filesystem::path path, const HostArray& image) {
154 ✗ Pixels pixels = ToPixels(image);
155 ✗ nb::gil_scoped_release release;
156
0/1
✗ Branch 2 not taken.
✗ writer.WriteImage(std::move(path), std::move(pixels));
157 ✗ },
158 2 nb::arg("path"), nb::arg("image"))
159 2 .def("write_render_var", &ImageWriter::WriteRenderVar, nb::arg("path"),
160
1/1
✓ Branch 1 taken 2 times.
6 nb::arg("render_var"), nb::arg("camera") = 0, nb::call_guard<nb::gil_scoped_release>())
161 2 .def("write_frame", &ImageWriter::WriteFrame, nb::arg("directory"), nb::arg("frame"),
162
1/1
✓ Branch 1 taken 2 times.
6 nb::arg("raw") = false, nb::call_guard<nb::gil_scoped_release>())
163 2 .def("wait", &ImageWriter::Wait, nb::call_guard<nb::gil_scoped_release>())
164 4 .def_prop_ro("pending", &ImageWriter::Pending)
165 2 .def(
166 "__enter__", [](ImageWriter& writer) -> ImageWriter& { return writer; },
167 nb::rv_policy::reference)
168 2 .def(
169 "__exit__",
170 ✗ [](ImageWriter& writer, nb::handle, nb::handle, nb::handle) {
171 ✗ nb::gil_scoped_release release;
172
0/1
✗ Branch 1 not taken.
✗ writer.Wait();
173 ✗ },
174 4 nb::arg("type").none(), nb::arg("value").none(), nb::arg("traceback").none());
175 2 }
176
177 }
178