Commit b19fe1de authored by Christoph Lassner's avatar Christoph Lassner Committed by Facebook GitHub Bot
Browse files

pulsar integration.

Summary:
This diff integrates the pulsar renderer source code into PyTorch3D as an alternative backend for the PyTorch3D point renderer. This diff is the first of a series of three diffs to complete that migration and focuses on the packaging and integration of the source code.

For more information about the pulsar backend, see the release notes and the paper (https://arxiv.org/abs/2004.07484). For information on how to use the backend, see the point cloud rendering notebook and the examples in the folder `docs/examples`.

Tasks addressed in the following diffs:
* Add the PyTorch3D interface,
* Add notebook examples and documentation (or adapt the existing ones to feature both interfaces).

Reviewed By: nikhilaravi

Differential Revision: D23947736

fbshipit-source-id: a5e77b53e6750334db22aefa89b4c079cda1b443
parent d5650323
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
#include "./renderer.forward.device.h"
namespace pulsar {
namespace Renderer {
template void forward<ISONDEVICE>(
Renderer* self,
const float* vert_pos,
const float* vert_col,
const float* vert_rad,
const CamInfo& cam,
const float& gamma,
float percent_allowed_difference,
const uint& max_n_hits,
const float* bg_col_d,
const float* opacity_d,
const size_t& num_balls,
const uint& mode,
cudaStream_t stream);
} // namespace Renderer
} // namespace pulsar
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
#ifndef PULSAR_NATIVE_INCLUDE_RENDERER_GET_SCREEN_AREA_DEVICE_H_
#define PULSAR_NATIVE_INCLUDE_RENDERER_GET_SCREEN_AREA_DEVICE_H_
#include "../global.h"
#include "./camera.device.h"
#include "./commands.h"
#include "./math.h"
namespace pulsar {
namespace Renderer {
/**
* Find the closest enclosing screen area rectangle in pixels that encloses a
* ball.
*
* The method returns the two x and the two y values of the boundaries. They
* are not ordered yet and you need to find min and max for the left/right and
* lower/upper boundary.
*
* The return values are floats and need to be rounded appropriately.
*/
INLINE DEVICE bool get_screen_area(
const float3& ball_center_cam,
const float3& ray_center_norm,
const float& vert_rad,
const CamInfo& cam,
const uint& idx,
/* Out variables. */
float* x_1,
float* x_2,
float* y_1,
float* y_2) {
float cos_alpha = dot(cam.sensor_dir_z, ray_center_norm);
float2 o__c_, alpha, theta;
if (cos_alpha < EPS) {
PULSAR_LOG_DEV(
PULSAR_LOG_CALC_SIGNATURE,
"signature %d|ball not visible. cos_alpha: %.9f.\n",
idx,
cos_alpha);
// No intersection, ball won't be visible.
return false;
}
// Multiply the direction vector with the camera rotation matrix
// to have the optical axis being the canonical z vector (0, 0, 1).
// TODO: optimize.
const float3 ball_center_cam_rot = rotate(
ball_center_cam,
cam.pixel_dir_x / length(cam.pixel_dir_x),
cam.pixel_dir_y / length(cam.pixel_dir_y),
cam.sensor_dir_z);
PULSAR_LOG_DEV(
PULSAR_LOG_CALC_SIGNATURE,
"signature %d|ball_center_cam_rot: %f, %f, %f.\n",
idx,
ball_center_cam.x,
ball_center_cam.y,
ball_center_cam.z);
const float pixel_size_norm_fac = FRCP(2.f * cam.half_pixel_size);
const float optical_offset_x =
(static_cast<float>(cam.aperture_width) - 1.f) * .5f;
const float optical_offset_y =
(static_cast<float>(cam.aperture_height) - 1.f) * .5f;
if (cam.orthogonal_projection) {
*x_1 =
FMA(ball_center_cam_rot.x - vert_rad,
pixel_size_norm_fac,
optical_offset_x);
*x_2 =
FMA(ball_center_cam_rot.x + vert_rad,
pixel_size_norm_fac,
optical_offset_x);
*y_1 =
FMA(ball_center_cam_rot.y - vert_rad,
pixel_size_norm_fac,
optical_offset_y);
*y_2 =
FMA(ball_center_cam_rot.y + vert_rad,
pixel_size_norm_fac,
optical_offset_y);
return true;
} else {
o__c_.x = FMAX(
FSQRT(
ball_center_cam_rot.x * ball_center_cam_rot.x +
ball_center_cam_rot.z * ball_center_cam_rot.z),
FEPS);
o__c_.y = FMAX(
FSQRT(
ball_center_cam_rot.y * ball_center_cam_rot.y +
ball_center_cam_rot.z * ball_center_cam_rot.z),
FEPS);
PULSAR_LOG_DEV(
PULSAR_LOG_CALC_SIGNATURE,
"signature %d|o__c_: %f, %f.\n",
idx,
o__c_.x,
o__c_.y);
alpha.x = sign_dir(ball_center_cam_rot.x) *
acos(FMIN(FMAX(ball_center_cam_rot.z / o__c_.x, -1.f), 1.f));
alpha.y = -sign_dir(ball_center_cam_rot.y) *
acos(FMIN(FMAX(ball_center_cam_rot.z / o__c_.y, -1.f), 1.f));
theta.x = asin(FMIN(FMAX(vert_rad / o__c_.x, -1.f), 1.f));
theta.y = asin(FMIN(FMAX(vert_rad / o__c_.y, -1.f), 1.f));
PULSAR_LOG_DEV(
PULSAR_LOG_CALC_SIGNATURE,
"signature %d|alpha.x: %f, alpha.y: %f, theta.x: %f, theta.y: %f.\n",
idx,
alpha.x,
alpha.y,
theta.x,
theta.y);
*x_1 = tan(alpha.x - theta.x) * cam.focal_length;
*x_2 = tan(alpha.x + theta.x) * cam.focal_length;
*y_1 = tan(alpha.y - theta.y) * cam.focal_length;
*y_2 = tan(alpha.y + theta.y) * cam.focal_length;
PULSAR_LOG_DEV(
PULSAR_LOG_CALC_SIGNATURE,
"signature %d|in sensor plane: x_1: %f, x_2: %f, y_1: %f, y_2: %f.\n",
idx,
*x_1,
*x_2,
*y_1,
*y_2);
*x_1 = FMA(*x_1, pixel_size_norm_fac, optical_offset_x);
*x_2 = FMA(*x_2, pixel_size_norm_fac, optical_offset_x);
*y_1 = FMA(*y_1, -pixel_size_norm_fac, optical_offset_y);
*y_2 = FMA(*y_2, -pixel_size_norm_fac, optical_offset_y);
return true;
}
};
} // namespace Renderer
} // namespace pulsar
#endif
This diff is collapsed.
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
#ifndef PULSAR_NATIVE_INCLUDE_RENDERER_NORM_CAM_GRADIENTS_DEVICE_H_
#define PULSAR_NATIVE_INCLUDE_RENDERER_NORM_CAM_GRADIENTS_DEVICE_H_
#include "../global.h"
#include "./camera.device.h"
#include "./commands.h"
#include "./math.h"
#include "./renderer.h"
namespace pulsar {
namespace Renderer {
/**
* Normalize the camera gradients by the number of spheres that contributed.
*/
template <bool DEV>
GLOBAL void norm_cam_gradients(Renderer renderer) {
GET_PARALLEL_IDX_1D(idx, 1);
CamGradInfo* cgi = reinterpret_cast<CamGradInfo*>(renderer.grad_cam_d);
*cgi = *cgi * FRCP(static_cast<float>(*renderer.n_grad_contributions_d));
END_PARALLEL_NORET();
};
} // namespace Renderer
} // namespace pulsar
#endif
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
#include "./renderer.norm_cam_gradients.device.h"
namespace pulsar {
namespace Renderer {
template GLOBAL void norm_cam_gradients<ISONDEVICE>(Renderer renderer);
} // namespace Renderer
} // namespace pulsar
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
#ifndef PULSAR_NATIVE_INCLUDE_RENDERER_NORM_SPHERE_GRADIENTS_H_
#define PULSAR_NATIVE_INCLUDE_RENDERER_NORM_SPHERE_GRADIENTS_H_
#include "../global.h"
#include "./commands.h"
#include "./math.h"
#include "./renderer.h"
namespace pulsar {
namespace Renderer {
/**
* Normalize the sphere gradients.
*
* We're assuming that the samples originate from a Monte Carlo
* sampling process and normalize by number and sphere area.
*/
template <bool DEV>
GLOBAL void norm_sphere_gradients(Renderer renderer, const int num_balls) {
GET_PARALLEL_IDX_1D(idx, num_balls);
float norm_fac = 0.f;
IntersectInfo ii;
if (renderer.ids_sorted_d[idx] > 0) {
ii = renderer.ii_d[idx];
// Normalize the sphere gradients as averages.
// This avoids the case that there are small spheres in a scene with still
// un-converged colors whereas the big spheres already converged, just
// because their integrated learning rate is 'higher'.
norm_fac = FRCP(static_cast<float>(renderer.ids_sorted_d[idx]));
}
PULSAR_LOG_DEV_NODE(
PULSAR_LOG_NORMALIZE,
"ids_sorted_d[idx]: %d, norm_fac: %.9f.\n",
renderer.ids_sorted_d[idx],
norm_fac);
renderer.grad_rad_d[idx] *= norm_fac;
for (uint c_idx = 0; c_idx < renderer.cam.n_channels; ++c_idx) {
renderer.grad_col_d[idx * renderer.cam.n_channels + c_idx] *= norm_fac;
}
renderer.grad_pos_d[idx] *= norm_fac;
renderer.grad_opy_d[idx] *= norm_fac;
if (renderer.ids_sorted_d[idx] > 0) {
// For the camera, we need to be more correct and have the gradients
// be proportional to the area they cover in the image.
// This leads to a formulation very much like in monte carlo integration:
norm_fac = FRCP(static_cast<float>(renderer.ids_sorted_d[idx])) *
(static_cast<float>(ii.max.x) - static_cast<float>(ii.min.x)) *
(static_cast<float>(ii.max.y) - static_cast<float>(ii.min.y)) *
1e-3f; // for better numerics.
}
renderer.grad_cam_buf_d[idx].cam_pos *= norm_fac;
renderer.grad_cam_buf_d[idx].pixel_0_0_center *= norm_fac;
renderer.grad_cam_buf_d[idx].pixel_dir_x *= norm_fac;
renderer.grad_cam_buf_d[idx].pixel_dir_y *= norm_fac;
// The sphere only contributes to the camera gradients if it is
// large enough in screen space.
if (renderer.ids_sorted_d[idx] > 0 && ii.max.x >= ii.min.x + 3 &&
ii.max.y >= ii.min.y + 3)
renderer.ids_sorted_d[idx] = 1;
END_PARALLEL_NORET();
};
} // namespace Renderer
} // namespace pulsar
#endif
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
#include "./renderer.norm_sphere_gradients.device.h"
namespace pulsar {
namespace Renderer {
template GLOBAL void norm_sphere_gradients<ISONDEVICE>(
Renderer renderer,
const int num_balls);
} // namespace Renderer
} // namespace pulsar
This diff is collapsed.
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
#ifndef PULSAR_NATIVE_INCLUDE_RENDERER_RENDER_INSTANTIATE_H_
#define PULSAR_NATIVE_INCLUDE_RENDERER_RENDER_INSTANTIATE_H_
#include "./renderer.render.device.h"
namespace pulsar {
namespace Renderer {
template GLOBAL void render<ISONDEVICE>(
size_t const* const RESTRICT
num_balls, /** Number of balls relevant for this pass. */
IntersectInfo const* const RESTRICT ii_d, /** Intersect information. */
DrawInfo const* const RESTRICT di_d, /** Draw information. */
float const* const RESTRICT min_depth_d, /** Minimum depth per sphere. */
int const* const RESTRICT id_d, /** IDs. */
float const* const RESTRICT op_d, /** Opacity. */
const CamInfo cam_norm, /** Camera normalized with all vectors to be in the
* camera coordinate system.
*/
const float gamma, /** Transparency parameter. **/
const float percent_allowed_difference, /** Maximum allowed
error in color. */
const uint max_n_hits,
const float* bg_col_d,
const uint mode,
const int x_min,
const int y_min,
const int x_step,
const int y_step,
// Out variables.
float* const RESTRICT result_d, /** The result image. */
float* const RESTRICT forw_info_d, /** Additional information needed for the
grad computation. */
const int n_track /** The number of spheres to track for backprop. */
);
}
} // namespace pulsar
#endif
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
#ifndef PULSAR_LOGGING_H_
#define PULSAR_LOGGING_H_
// #define PULSAR_LOGGING_ENABLED
/**
* Enable detailed per-operation timings.
*
* This timing scheme is not appropriate to measure batched calculations.
* Use `PULSAR_TIMINGS_BATCHED_ENABLED` for that.
*/
// #define PULSAR_TIMINGS_ENABLED
/**
* Time batched operations.
*/
// #define PULSAR_TIMINGS_BATCHED_ENABLED
#if defined(PULSAR_TIMINGS_BATCHED_ENABLED) && defined(PULSAR_TIMINGS_ENABLED)
#pragma message("Pulsar|batched and unbatched timings enabled. This will not")
#pragma message("Pulsar|create meaningful results.")
#endif
#ifdef PULSAR_LOGGING_ENABLED
// Control logging.
// 0: INFO, 1: WARNING, 2: ERROR, 3: FATAL (Abort after logging).
#define CAFFE2_LOG_THRESHOLD 0
#define PULSAR_LOG_INIT false
#define PULSAR_LOG_FORWARD false
#define PULSAR_LOG_CALC_SIGNATURE false
#define PULSAR_LOG_RENDER false
#define PULSAR_LOG_RENDER_PIX false
#define PULSAR_LOG_RENDER_PIX_X 428
#define PULSAR_LOG_RENDER_PIX_Y 669
#define PULSAR_LOG_RENDER_PIX_ALL false
#define PULSAR_LOG_TRACKER_PIX false
#define PULSAR_LOG_TRACKER_PIX_X 428
#define PULSAR_LOG_TRACKER_PIX_Y 669
#define PULSAR_LOG_TRACKER_PIX_ALL false
#define PULSAR_LOG_DRAW_PIX false
#define PULSAR_LOG_DRAW_PIX_X 428
#define PULSAR_LOG_DRAW_PIX_Y 669
#define PULSAR_LOG_DRAW_PIX_ALL false
#define PULSAR_LOG_BACKWARD false
#define PULSAR_LOG_GRAD false
#define PULSAR_LOG_GRAD_X 509
#define PULSAR_LOG_GRAD_Y 489
#define PULSAR_LOG_GRAD_ALL false
#define PULSAR_LOG_NORMALIZE false
#define PULSAR_LOG_NORMALIZE_X 0
#define PULSAR_LOG_NORMALIZE_ALL false
#define PULSAR_LOG_DEV(ID, ...) \
if ((ID)) { \
printf(__VA_ARGS__); \
}
#define PULSAR_LOG_DEV_APIX(ID, MSG, ...) \
if ((ID) && (film_coord_x == (ID##_X) && film_coord_y == (ID##_Y)) || \
ID##_ALL) { \
printf( \
"%u %u (ap %u %u)|" MSG, \
film_coord_x, \
film_coord_y, \
ap_coord_x, \
ap_coord_y, \
__VA_ARGS__); \
}
#define PULSAR_LOG_DEV_PIX(ID, MSG, ...) \
if ((ID) && (coord_x == (ID##_X) && coord_y == (ID##_Y)) || ID##_ALL) { \
printf("%u %u|" MSG, coord_x, coord_y, __VA_ARGS__); \
}
#ifdef __CUDACC__
#define PULSAR_LOG_DEV_PIXB(ID, MSG, ...) \
if ((ID) && static_cast<int>(block_area.min.x) <= (ID##_X) && \
static_cast<int>(block_area.max.x) > (ID##_X) && \
static_cast<int>(block_area.min.y) <= (ID##_Y) && \
static_cast<int>(block_area.max.y) > (ID##_Y)) { \
printf("%u %u|" MSG, coord_x, coord_y, __VA_ARGS__); \
}
#else
#define PULSAR_LOG_DEV_PIXB(ID, MSG, ...) \
if ((ID) && coord_x == (ID##_X) && coord_y == (ID##_Y)) { \
printf("%u %u|" MSG, coord_x, coord_y, __VA_ARGS__); \
}
#endif
#define PULSAR_LOG_DEV_NODE(ID, MSG, ...) \
if ((ID) && idx == (ID##_X) || (ID##_ALL)) { \
printf("%u|" MSG, idx, __VA_ARGS__); \
}
#else
#define CAFFE2_LOG_THRESHOLD 2
#define PULSAR_LOG_RENDER false
#define PULSAR_LOG_INIT false
#define PULSAR_LOG_FORWARD false
#define PULSAR_LOG_BACKWARD false
#define PULSAR_LOG_TRACKER_PIX false
#define PULSAR_LOG_DEV(...)
#define PULSAR_LOG_DEV_APIX(...)
#define PULSAR_LOG_DEV_PIX(...)
#define PULSAR_LOG_DEV_PIXB(...)
#define PULSAR_LOG_DEV_NODE(...)
#endif
#endif
// Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.
#include "./camera.h"
#include "../include/math.h"
namespace pulsar {
namespace pytorch {
CamInfo cam_info_from_params(
const torch::Tensor& cam_pos,
const torch::Tensor& pixel_0_0_center,
const torch::Tensor& pixel_vec_x,
const torch::Tensor& pixel_vec_y,
const torch::Tensor& principal_point_offset,
const float& focal_length,
const uint& width,
const uint& height,
const float& min_dist,
const float& max_dist,
const bool& right_handed) {
CamInfo res;
fill_cam_vecs(
cam_pos.detach().cpu(),
pixel_0_0_center.detach().cpu(),
pixel_vec_x.detach().cpu(),
pixel_vec_y.detach().cpu(),
principal_point_offset.detach().cpu(),
right_handed,
&res);
res.half_pixel_size = 0.5f * length(res.pixel_dir_x);
if (length(res.pixel_dir_y) * 0.5f - res.half_pixel_size > EPS) {
throw std::runtime_error("Pixel sizes must agree in x and y direction!");
}
res.focal_length = focal_length;
res.aperture_width =
width + 2u * static_cast<uint>(abs(res.principal_point_offset_x));
res.aperture_height =
height + 2u * static_cast<uint>(abs(res.principal_point_offset_y));
res.pixel_0_0_center -=
res.pixel_dir_x * static_cast<float>(abs(res.principal_point_offset_x));
res.pixel_0_0_center -=
res.pixel_dir_y * static_cast<float>(abs(res.principal_point_offset_y));
res.film_width = width;
res.film_height = height;
res.film_border_left =
static_cast<uint>(std::max(0, 2 * res.principal_point_offset_x));
res.film_border_top =
static_cast<uint>(std::max(0, 2 * res.principal_point_offset_y));
LOG_IF(INFO, PULSAR_LOG_INIT)
<< "Aperture width, height: " << res.aperture_width << ", "
<< res.aperture_height;
LOG_IF(INFO, PULSAR_LOG_INIT)
<< "Film width, height: " << res.film_width << ", " << res.film_height;
LOG_IF(INFO, PULSAR_LOG_INIT)
<< "Film border left, top: " << res.film_border_left << ", "
<< res.film_border_top;
res.min_dist = min_dist;
res.max_dist = max_dist;
res.norm_fac = 1.f / (max_dist - min_dist);
return res;
};
} // namespace pytorch
} // namespace pulsar
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment