Skip to content

Commit ba11c0b

Browse files
nikhilaravifacebook-github-bot
authored andcommitted
Blending fixes and test updates
Summary: Changed `torch.cumprod` to `torch.prod` in blending functions and added more tests and benchmark tests. This should fix the issue raised on GitHub. Reviewed By: gkioxari Differential Revision: D20163073 fbshipit-source-id: 4569fd37be11aa4435a3ce8736b55622c00ec718
1 parent ff19c64 commit ba11c0b

File tree

3 files changed

+311
-94
lines changed

3 files changed

+311
-94
lines changed

pytorch3d/renderer/blending.py

+15-28
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ def sigmoid_alpha_blend(colors, fragments, blend_params) -> torch.Tensor:
4545
"""
4646
Silhouette blending to return an RGBA image
4747
- **RGB** - choose color of the closest point.
48-
- **A** - blend based on the 2D distance based probability map [0].
48+
- **A** - blend based on the 2D distance based probability map [1].
4949
5050
Args:
5151
colors: (N, H, W, K, 3) RGB color for each of the top K faces per pixel.
@@ -60,7 +60,7 @@ def sigmoid_alpha_blend(colors, fragments, blend_params) -> torch.Tensor:
6060
Returns:
6161
RGBA pixel_colors: (N, H, W, 4)
6262
63-
[0] Liu et al, 'Soft Rasterizer: A Differentiable Renderer for Image-based
63+
[1] Liu et al, 'Soft Rasterizer: A Differentiable Renderer for Image-based
6464
3D Reasoning', ICCV 2019
6565
"""
6666
N, H, W, K = fragments.pix_to_face.shape
@@ -73,20 +73,13 @@ def sigmoid_alpha_blend(colors, fragments, blend_params) -> torch.Tensor:
7373
# the face. Therefore use -1.0 * fragments.dists to get the correct sign.
7474
prob = torch.sigmoid(-fragments.dists / blend_params.sigma) * mask
7575

76-
# The cumulative product ensures that alpha will be 1 if at least 1 face
77-
# fully covers the pixel as for that face prob will be 1.0
78-
# TODO: investigate why torch.cumprod backwards is very slow for large
79-
# values of K.
80-
# Temporarily replace this with exp(sum(log))) using the fact that
81-
# a*b = exp(log(a*b)) = exp(log(a) + log(b))
82-
# alpha = 1.0 - torch.cumprod((1.0 - prob), dim=-1)[..., -1]
83-
84-
alpha = 1.0 - torch.exp(torch.log((1.0 - prob)).sum(dim=-1))
85-
76+
# The cumulative product ensures that alpha will be 0.0 if at least 1
77+
# face fully covers the pixel as for that face, prob will be 1.0.
78+
# This results in a multiplication by 0.0 because of the (1.0 - prob)
79+
# term. Therefore 1.0 - alpha will be 1.0.
80+
alpha = torch.prod((1.0 - prob), dim=-1)
8681
pixel_colors[..., :3] = colors[..., 0, :] # Hard assign for RGB
87-
pixel_colors[..., 3] = alpha
88-
89-
pixel_colors = torch.clamp(pixel_colors, min=0, max=1.0)
82+
pixel_colors[..., 3] = 1.0 - alpha
9083
return torch.flip(pixel_colors, [1])
9184

9285

@@ -95,7 +88,7 @@ def softmax_rgb_blend(
9588
) -> torch.Tensor:
9689
"""
9790
RGB and alpha channel blending to return an RGBA image based on the method
98-
proposed in [0]
91+
proposed in [1]
9992
- **RGB** - blend the colors based on the 2D distance based probability map and
10093
relative z distances.
10194
- **A** - blend based on the 2D distance based probability map.
@@ -151,15 +144,11 @@ def softmax_rgb_blend(
151144
# Sigmoid probability map based on the distance of the pixel to the face.
152145
prob_map = torch.sigmoid(-fragments.dists / blend_params.sigma) * mask
153146

154-
# The cumulative product ensures that alpha will be 1 if at least 1 face
155-
# fully covers the pixel as for that face prob will be 1.0
156-
# TODO: investigate why torch.cumprod backwards is very slow for large
157-
# values of K.
158-
# Temporarily replace this with exp(sum(log))) using the fact that
159-
# a*b = exp(log(a*b)) = exp(log(a) + log(b))
160-
# alpha = 1.0 - torch.cumprod((1.0 - prob), dim=-1)[..., -1]
161-
162-
alpha = 1.0 - torch.exp(torch.log((1.0 - prob_map)).sum(dim=-1))
147+
# The cumulative product ensures that alpha will be 0.0 if at least 1
148+
# face fully covers the pixel as for that face, prob will be 1.0.
149+
# This results in a multiplication by 0.0 because of the (1.0 - prob)
150+
# term. Therefore 1.0 - alpha will be 1.0.
151+
alpha = torch.prod((1.0 - prob_map), dim=-1)
163152

164153
# Weights for each face. Adjust the exponential by the max z to prevent
165154
# overflow. zbuf shape (N, H, W, K), find max over K.
@@ -178,8 +167,6 @@ def softmax_rgb_blend(
178167
weighted_colors = (weights[..., None] * colors).sum(dim=-2)
179168
weighted_background = (delta / denom) * background
180169
pix_colors[..., :3] = weighted_colors + weighted_background
181-
pix_colors[..., 3] = alpha
170+
pix_colors[..., 3] = 1.0 - alpha
182171

183-
# Clamp colors to the range 0-1 and flip y axis.
184-
pix_colors = torch.clamp(pix_colors, min=0, max=1.0)
185172
return torch.flip(pix_colors, [1])

tests/bm_blending.py

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
#!/usr/bin/env python3
2+
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
3+
4+
5+
from itertools import product
6+
from fvcore.common.benchmark import benchmark
7+
8+
from test_blending import TestBlending
9+
10+
11+
def bm_blending() -> None:
12+
devices = ["cpu", "cuda"]
13+
kwargs_list = []
14+
num_meshes = [16]
15+
image_size = [128, 256]
16+
faces_per_pixel = [50, 100]
17+
test_cases = product(num_meshes, image_size, faces_per_pixel, devices)
18+
19+
for case in test_cases:
20+
n, s, k, d = case
21+
kwargs_list.append(
22+
{
23+
"num_meshes": n,
24+
"image_size": s,
25+
"faces_per_pixel": k,
26+
"device": d,
27+
}
28+
)
29+
30+
benchmark(
31+
TestBlending.bm_sigmoid_alpha_blending,
32+
"SIGMOID_ALPHA_BLENDING_PYTORCH",
33+
kwargs_list,
34+
warmup_iters=1,
35+
)
36+
37+
benchmark(
38+
TestBlending.bm_softmax_blending,
39+
"SOFTMAX_BLENDING_PYTORCH",
40+
kwargs_list,
41+
warmup_iters=1,
42+
)

0 commit comments

Comments
 (0)