Skip to content

Commit bc27b61

Browse files
committed
copilot review. Consistent device use.
Switch to tbb parallel reduce, for export depth image, since AppleClang lacks execution policy.
1 parent b5d95a0 commit bc27b61

8 files changed

Lines changed: 106 additions & 85 deletions

File tree

cpp/open3d/core/Tensor.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1174,7 +1174,7 @@ Tensor Tensor::Cross(const Tensor& other, int64_t axis) const {
11741174
utility::LogError(
11751175
"[Tensor::Cross] Dim axis={} of both tensors must have shape "
11761176
"3. Got {} and {} instead.",
1177-
axis, GetShape(3), other.GetShape(3));
1177+
axis, GetShape(axis), other.GetShape(axis));
11781178
}
11791179
Tensor t_x = Slice(axis, 0, 1), t_y = Slice(axis, 1, 2),
11801180
t_z = Slice(axis, 2, 3);

cpp/open3d/core/Tensor.h

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -411,7 +411,6 @@ class Tensor : public IsDevice {
411411
/// t = np.empty((4, 5), dtype=np.float32)
412412
/// t[2, 0:4:2] = np.empty((2, 5), dtype=np.float32)
413413
/// ```
414-
/// Tensor Cross(const Tensor &b) const;
415414
/// The equivalent Open3D C++ calls:
416415
/// ```cpp
417416
/// Tensor t({4, 5}, core::Float32);
@@ -636,14 +635,15 @@ class Tensor : public IsDevice {
636635
/// tensor.
637636
///
638637
/// The cross product is computed along the specified axis. The tensors must
639-
/// have compatible
640-
// (i.e. broadcastable) shape, and the size of the specified axis must be 3.
641-
// If the axis is -1 (default), the last axis is used.
638+
/// have compatible (i.e. broadcastable) shape, and the size of the
639+
/// specified axis must be 3. If the axis is -1 (default), the last axis is
640+
/// used.
642641
///
643642
/// \param other The second input tensor to compute the cross product with.
644643
/// \param axis The axis along which to compute the cross product. Default
645-
/// is -1 (the last axis). \return A tensor containing the cross product of
646-
/// the two input tensors along the specified axis.
644+
/// is -1 (the last axis).
645+
/// \return A tensor containing the cross product of the two input tensors
646+
/// along the specified axis.
647647
Tensor Cross(const Tensor& other, int64_t axis = -1) const;
648648

649649
/// Helper function to return scalar value of a scalar Tensor, the Tensor
@@ -842,8 +842,8 @@ class Tensor : public IsDevice {
842842
/// with size 1.
843843
/// \param p The order of the norm. Default is 2.0 (L2 norm). Can be
844844
/// positive infinity (L-infinity norm).
845-
/// \return Tensor containing the computed norms. Dtype is
846-
/// preserved for Float32 and Float64 for L2 norm, It is onverted to Float32
845+
/// \return Tensor containing the computed norms. Dtype is preserved for
846+
/// Float32 and Float64 for L2 norm, It is converted to Float32
847847
/// for all other cases.
848848
Tensor Norm(const SizeVector& dims = {},
849849
bool keepdim = false,

cpp/open3d/t/geometry/TriangleMesh.cpp

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include <mutex>
2626
#include <string>
2727
#include <tuple>
28+
#include <unordered_map>
2829

2930
#include "open3d/core/CUDAUtils.h"
3031
#include "open3d/core/Device.h"
@@ -1945,8 +1946,7 @@ Image TriangleMesh::ComputeAmbientOcclusion(int tex_width,
19451946
}
19461947

19471948
// Bake positions and normals into textures.
1948-
const float margin =
1949-
(tex_width + 511.f) / 512.f; // = ceil(tex_width / 512)
1949+
const float margin = (tex_width + 511) / 512; // = ceil(tex_width / 512)
19501950
auto baked_textures =
19511951
BakeVertexAttrTextures(tex_width, {"positions", "normals"}, margin,
19521952
/*fill=*/0.0, /*update_material=*/false);
@@ -1957,7 +1957,7 @@ Image TriangleMesh::ComputeAmbientOcclusion(int tex_width,
19571957

19581958
RaycastingScene rcs;
19591959
// TODO: Reuse RCS from BakeVertexAttrTextures() if possible.
1960-
rcs.AddTriangles(*this);
1960+
rcs.AddTriangles(*this);
19611961

19621962
const int64_t n_pixels = tex_width * tex_width;
19631963

@@ -2234,12 +2234,15 @@ Image TriangleMesh::TransformNormalMap(const Image &normal_map,
22342234
"Use ComputeVertexNormals() and ComputeTangentSpace() to "
22352235
"compute them.");
22362236
}
2237-
22382237
if (normal_map.GetChannels() != 3) {
22392238
utility::LogError("Normal map must have 3 channels, but has {}.",
22402239
normal_map.GetChannels());
22412240
}
2242-
2241+
const core::Device device = GetDevice();
2242+
if (normal_map.GetDevice() != device) {
2243+
utility::LogError("Normal map device {} does not match mesh device {}.",
2244+
normal_map.GetDevice().ToString(), device.ToString());
2245+
}
22432246
int tex_width = normal_map.GetCols();
22442247
int tex_height = normal_map.GetRows();
22452248
if (tex_width != tex_height) {
@@ -2250,8 +2253,7 @@ Image TriangleMesh::TransformNormalMap(const Image &normal_map,
22502253
}
22512254

22522255
// Bake TBN vectors into textures.
2253-
const float margin =
2254-
(tex_width + 511.f) / 512.f; // = ceil(tex_width / 512)
2256+
const float margin = (tex_width + 511) / 512; // = ceil(tex_width / 512)
22552257
if (!HasMaterial()) {
22562258
SetMaterial(visualization::rendering::Material());
22572259
GetMaterial().SetDefaultProperties(); // defaultLit
@@ -2260,18 +2262,19 @@ Image TriangleMesh::TransformNormalMap(const Image &normal_map,
22602262
std::unordered_map<std::string, core::Tensor> baked_textures;
22612263
if (!(GetMaterial().HasTextureMap("normals") &&
22622264
GetMaterial().HasTextureMap("tangents"))) {
2265+
// BakeVertexAttrTextures requires CPU; move results back to the mesh
2266+
// device for all subsequent computation.
22632267
baked_textures = To(core::Device("CPU:0"))
22642268
.BakeVertexAttrTextures(
22652269
tex_width, {"normals", "tangents"},
22662270
margin, /*fill=*/0.0, false);
2271+
baked_textures["normals"] = baked_textures["normals"].To(device);
2272+
baked_textures["tangents"] = baked_textures["tangents"].To(device);
22672273
} else {
22682274
baked_textures["normals"] =
2269-
GetMaterial().GetTextureMap("normals").AsTensor().To(
2270-
core::Device("CPU:0"));
2271-
baked_textures["tangents"] = GetMaterial()
2272-
.GetTextureMap("tangents")
2273-
.AsTensor()
2274-
.To(core::Device("CPU:0"));
2275+
GetMaterial().GetTextureMap("normals").AsTensor().To(device);
2276+
baked_textures["tangents"] =
2277+
GetMaterial().GetTextureMap("tangents").AsTensor().To(device);
22752278
}
22762279

22772280
// Baked (Interpolated) TBN vectors are NOT normalized.
@@ -2314,8 +2317,8 @@ Image TriangleMesh::TransformNormalMap(const Image &normal_map,
23142317
2.0f -
23152318
1.0f;
23162319

2317-
core::Tensor tangent_normals_t =
2318-
core::Tensor::Empty({tex_height, tex_width, 3}, core::Float32);
2320+
core::Tensor tangent_normals_t = core::Tensor::Empty(
2321+
{tex_height, tex_width, 3}, core::Float32, device);
23192322
tangent_normals_t.Slice(2, 0, 1) =
23202323
fSign * (input_normals_t * row0).Sum({2}, true);
23212324
tangent_normals_t.Slice(2, 1, 2) =

cpp/open3d/t/geometry/TriangleMesh.h

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1163,29 +1163,38 @@ class TriangleMesh : public Geometry, public DrawableGeometry {
11631163
float max_hit_distance = INFINITY,
11641164
bool update_material = true);
11651165

1166-
/// Computes tangent space for the triangle mesh with MikkTSpace.
1167-
/// The mesh must have vertex positions, vertex normals, and texture UVs
1168-
/// (triangle attribute 'texture_uvs'). The computed tangents and bitangents
1169-
/// will be added as vertex attributes 'tangents' and 'bitangents'.
1166+
/// Computes tangent space for the triangle mesh with MikkTSpace. The mesh
1167+
/// must have vertex positions, vertex normals, and texture UVs (triangle
1168+
/// attribute 'texture_uvs'). The computed tangents will be added as vertex
1169+
/// attributes 'tangents' with shape {N, 4}, where the 4th element is the
1170+
/// sign. Bitangents are normally computed as needed with the formula:
1171+
/// \verbatim
1172+
/// vB = sign * cross(vN, vT);
1173+
/// \endverbatim
11701174
/// This function works on the CPU and will transfer data to the CPU if
11711175
/// necessary.
1172-
/// \param bake If true, the tangents, bitangents and normals will also be
1173-
/// baked to textures and saved to the material.
1176+
/// \param bake If true, the tangents and normals will also be
1177+
/// baked to textures (unnormalized, interpolated) and saved to the
1178+
/// material.
11741179
/// \param tex_width Baked texture size. Default 512.
11751180
void ComputeTangentSpace(bool bake = true, int tex_width = 512);
11761181

11771182
/// \brief Converts a normal map between world and tangent space.
11781183
///
11791184
/// The conversion is performed for each pixel of the map. The mesh must
1180-
/// have vertex normals, tangents, bitangents, and texture UVs.
1181-
/// The tangent space attributes can be computed with
1182-
/// `ComputeTangentSpace()`.
1185+
/// have vertex normals (shape {N, 3}), tangents with sign (shape {N, 4})
1186+
/// and texture UVs. The tangent space attributes can be computed with
1187+
/// `ComputeTangentSpace()`. Bitangents are normally computed as needed with
1188+
/// the formula:
1189+
/// \verbatim
1190+
/// vB = sign * cross(vN, vT);
1191+
/// \endverbatim
11831192
///
1184-
/// \param normal_map The normal map to convert.
1185-
/// When converting to tangent space, this is the world-space normal map.
1186-
/// When converting to world space, this is the tangent-space normal map.
1187-
/// It is expected to have 3 channels with Float32 or Float64 data type,
1188-
/// with values in range [-1, 1], or UInt8 data type in the range [0, 255].
1193+
/// \param normal_map The normal map to convert. When converting to tangent
1194+
/// space, this is the world-space normal map. When converting to world
1195+
/// space, this is the tangent-space normal map. It is expected to have 3
1196+
/// channels with Float32 or Float64 data type, with values in range [-1,
1197+
/// 1], or UInt8 data type in the range [0, 255].
11891198
/// \param to_tangent_space If true, converts from world to tangent space.
11901199
/// If false, converts from tangent to world space.
11911200
/// \param update_material If true and we are converting to tangent space,

cpp/open3d/visualization/visualizer/O3DVisualizer.cpp

Lines changed: 45 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
#include <json/json.h>
1111

12-
#include <execution>
12+
#include <cmath>
1313
#include <set>
1414
#include <unordered_map>
1515

@@ -2162,43 +2162,49 @@ Ctrl-alt-click to polygon select)";
21622162

21632163
void ExportCurrentDepthImage(const std::string &path) {
21642164
scene_->EnableSceneCaching(false);
2165-
scene_->GetScene()->GetScene()->RenderToDepthImage(
2166-
[this, path](std::shared_ptr<geometry::Image> image) mutable {
2167-
bool ok = false;
2168-
if (image && image->num_of_channels_ == 1 &&
2169-
image->bytes_per_channel_ == 4) {
2170-
// Depth export stores normalized depth in 16-bit PNG.
2171-
auto depth_float =
2172-
std::make_shared<geometry::Image>(*image);
2173-
float *depth_data = depth_float->PointerAs<float>();
2174-
size_t num_pixels = static_cast<size_t>(
2175-
depth_float->GetMaxBound().prod());
2176-
float max_val =
2177-
std::reduce(std::execution::par_unseq,
2178-
depth_data, depth_data + num_pixels,
2179-
0.0f, [](float a, float b) {
2180-
if (!std::isfinite(a)) return b;
2181-
if (!std::isfinite(b)) return a;
2182-
return std::max(a, b);
2183-
});
2184-
if (max_val > 0)
2185-
depth_float->LinearTransform(65535.0 / max_val,
2186-
0.0);
2187-
auto depth_u16 =
2188-
depth_float
2189-
->CreateImageFromFloatImage<uint16_t>();
2190-
ok = io::WriteImage(path, *depth_u16);
2191-
}
2192-
if (!ok) {
2193-
this->window_->ShowMessageBox(
2194-
"Error",
2195-
(std::string(
2196-
"Could not write depth image to ") +
2197-
path + ".")
2198-
.c_str());
2199-
}
2200-
scene_->EnableSceneCaching(true);
2201-
});
2165+
auto fs = scene_->GetScene()->GetScene();
2166+
fs->RenderToDepthImage([this,
2167+
path](std::shared_ptr<geometry::Image> image) {
2168+
bool ok = false;
2169+
auto finite_max = [](float a, float b) {
2170+
if (!std::isfinite(a)) return b;
2171+
if (!std::isfinite(b)) return a;
2172+
return std::max(a, b);
2173+
};
2174+
if (image && image->num_of_channels_ == 1 &&
2175+
image->bytes_per_channel_ == 4) {
2176+
// Depth export stores normalized depth in 16-bit PNG.
2177+
auto depth_float = std::make_shared<geometry::Image>(*image);
2178+
float *depth_data = depth_float->PointerAs<float>();
2179+
size_t num_pixels =
2180+
static_cast<size_t>(depth_float->GetMaxBound().prod());
2181+
auto depth_subrange_finite_max =
2182+
[depth_data](const tbb::blocked_range<size_t> &r,
2183+
float cmax) {
2184+
for (size_t i = r.begin(); i != r.end(); ++i) {
2185+
if (std::isfinite(depth_data[i])) {
2186+
cmax = std::max(cmax, depth_data[i]);
2187+
}
2188+
}
2189+
return cmax;
2190+
};
2191+
float max_val = tbb::parallel_reduce(
2192+
tbb::blocked_range<size_t>(0, num_pixels), -INFINITY,
2193+
depth_subrange_finite_max, finite_max);
2194+
if (max_val > 0)
2195+
depth_float->LinearTransform(65535.0 / max_val, 0.0);
2196+
auto depth_u16 =
2197+
depth_float->CreateImageFromFloatImage<uint16_t>();
2198+
ok = io::WriteImage(path, *depth_u16);
2199+
}
2200+
if (!ok) {
2201+
this->window_->ShowMessageBox(
2202+
"Error",
2203+
fmt::format("Could not write depth image to {}.", path)
2204+
.c_str());
2205+
}
2206+
scene_->EnableSceneCaching(true);
2207+
});
22022208
}
22032209

22042210
void OnAbout() {
@@ -2209,7 +2215,7 @@ Ctrl-alt-click to polygon select)";
22092215
(std::string("Open3D ") + OPEN3D_VERSION).c_str());
22102216
auto text = std::make_shared<gui::Label>(
22112217
"The MIT License (MIT)\n"
2212-
"Copyright (c) 2018-2023 www.open3d.org\n\n"
2218+
"Copyright (c) 2018-2026 www.open3d.org\n\n"
22132219

22142220
"Permission is hereby granted, free of charge, to any person "
22152221
"obtaining a copy of this software and associated "

cpp/pybind/core/tensor.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -785,8 +785,8 @@ tuple `output` tensor of shape {n,n} and `ipiv` tensor of shape {n}, where
785785
must be 3. If the axis is -1 (default), the last axis is used.
786786
787787
Args:
788-
other The second input tensor to compute the cross product with.
789-
axis The axis along which to compute the cross product. Default is -1 (the
788+
other: The second input tensor to compute the cross product with.
789+
axis: The axis along which to compute the cross product. Default is -1 (the
790790
last axis).
791791
792792
Returns:

cpp/pybind/t/geometry/trianglemesh.cpp

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1266,8 +1266,10 @@ Example::
12661266
R"(Computes tangent space for the triangle mesh with MikkTSpace.
12671267
The mesh must have vertex positions, vertex normals, and texture UVs
12681268
(triangle attribute 'texture_uvs'). The computed tangents will be added as
1269-
vertex attributes 'tangents' shape (N,4). This function works on the CPU and
1270-
will transfer data to the CPU if necessary.
1269+
vertex attributes 'tangents' shape (N,4), where the 4th element is the sign.
1270+
Bitangents are normally computed as needed with the formula:
1271+
``vB = sign * cross(vN, vT);``
1272+
This function works on the CPU and will transfer data to the CPU if necessary.
12711273
12721274
Args:
12731275
bake (bool): If true, the normals and tangents will also be baked to
@@ -1281,8 +1283,8 @@ Example::
12811283
R"(Converts a normal map between world and tangent space.
12821284
12831285
The conversion is performed for each pixel of the map. The mesh must
1284-
have vertex normals and tangents. The tangent space attributes can be
1285-
computed with `compute_tangent_space()`.
1286+
have vertex normals (shape (N, 3)) and tangents with sign (shape (N, 4)).
1287+
The tangent space attributes can be computed with `compute_tangent_space()`.
12861288
12871289
Args:
12881290
normal_map (o3d.t.geometry.Image): The normal map to convert.

cpp/tests/t/geometry/TriangleMesh.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1685,8 +1685,9 @@ TEST_P(TriangleMeshPermuteDevices, TangentSpace) {
16851685
world_map_t =
16861686
world_map_t / world_map_t.Norm({2}, true).Clip_(1e-6, INFINITY);
16871687
t::geometry::Image world_normal_map(world_map_t);
1688-
t::io::WriteImage("world_normal_map.png",
1689-
world_normal_map.To(core::UInt8, false, 127.5f, 127.5f));
1688+
// t::io::WriteImage("world_normal_map.png",
1689+
// world_normal_map.To(core::UInt8, false, 127.5f,
1690+
// 127.5f));
16901691

16911692
// 2. World to Tangent space.
16921693
auto tangent_normal_map = mesh.TransformNormalMap(world_normal_map,

0 commit comments

Comments
 (0)