#include #include #include #include #include #include #include #include #include "src/constants.h" #include "src/flame_tracking.h" std::function create_byte_allocator(torch::Tensor& buffer) { return [&buffer](size_t num_bytes) -> void* { buffer.resize_({static_cast(num_bytes)}); return buffer.contiguous().data_ptr(); }; } int infer_supported_num_vertices(const int64_t num_vertices, const char* caller) { if (num_vertices == NUM_FLAME_VERTICES_BASE || num_vertices == NUM_FLAME_VERTICES_ORAL) { return static_cast(num_vertices); } throw std::runtime_error(std::string(caller) + ": unsupported num_vertices."); } std::tuple flame_calc_canonical( const torch::Tensor& identity, const torch::Tensor& v_template, const torch::Tensor& shape_dirs, const torch::Tensor& J_regressor_row, const torch::Tensor& J_regressor_col, const torch::Tensor& J_regressor_values ) { if (v_template.dim() != 2 || v_template.size(1) != 3) { throw std::runtime_error("flame_calc_canonical: v_template must be [V,3]."); } const int num_vertices = infer_supported_num_vertices(v_template.size(0), "flame_calc_canonical"); if (shape_dirs.dim() != 2 || shape_dirs.size(0) != NUM_FLAME_IDENTITY_BASIS || shape_dirs.size(1) != num_vertices * 3) { throw std::runtime_error("flame_calc_canonical: shape_dirs must be [300,V*3]."); } torch::Tensor v_canonical = torch::zeros_like(v_template); torch::Tensor j_canonical = torch::zeros({NUM_FLAME_JOINTS, 3}, v_template.options()); const int num_J_regressor_nonzero = J_regressor_values.size(0); FlameTracking::flame_calc_canonical( num_vertices, identity.contiguous().data_ptr(), reinterpret_cast(v_template.contiguous().data_ptr()), reinterpret_cast(shape_dirs.contiguous().data_ptr()), num_J_regressor_nonzero, J_regressor_row.contiguous().data_ptr(), J_regressor_col.contiguous().data_ptr(), J_regressor_values.contiguous().data_ptr(), reinterpret_cast(v_canonical.contiguous().data_ptr()), reinterpret_cast(j_canonical.contiguous().data_ptr()) ); return std::make_tuple(v_canonical, j_canonical); } std::tuple flame_expression_jacobian( const torch::Tensor& batched_x, // [B, dim_x] cuda const torch::Tensor& v_canonical, // [V, 3] cuda const torch::Tensor& j_canonical, // [5, 3] cuda const torch::Tensor& expr_dirs, // [num_expressions, 5023 * 3] cuda const torch::Tensor& pose_dirs, // [ 36, 5023 * 3] cuda const torch::Tensor& lbs_weights, // [5023, 5] cuda const int num_expressions ) { const int dim_x = dim_x_for(num_expressions); if (v_canonical.dim() != 2 || v_canonical.size(1) != 3) { throw std::runtime_error("flame_expression_jacobian: v_canonical must be [V,3]."); } const int num_vertices = infer_supported_num_vertices(v_canonical.size(0), "flame_expression_jacobian"); if ( expr_dirs.dim() != 2 || expr_dirs.size(0) != num_expressions || expr_dirs.size(1) != num_vertices * 3 || pose_dirs.dim() != 2 || pose_dirs.size(1) != num_vertices * 3 || lbs_weights.dim() != 2 || lbs_weights.size(0) != num_vertices || lbs_weights.size(1) != NUM_FLAME_JOINTS ) { throw std::runtime_error("flame_expression_jacobian: invalid FLAME asset tensor shape."); } int batch_size = 1.0; torch::Tensor batched_v_output, batched_j_output, batched_v_jacobian, batched_j_jacobian; if (batched_x.dim() == 1) { if (batched_x.size(0) != dim_x) { throw std::runtime_error("flame_expression_jacobian: batched_x size mismatch."); } batched_v_output = torch::zeros({num_vertices, 3}, v_canonical.options()); // [V, 3] batched_j_output = torch::zeros({NUM_FLAME_JOINTS, 3}, v_canonical.options()); // [J, 3] batched_v_jacobian = torch::zeros({num_vertices, dim_x, 3}, v_canonical.options()); // [V, dim_x, 3] batched_j_jacobian = torch::zeros({NUM_FLAME_JOINTS, dim_x, 3}, v_canonical.options()); // [J, dim_x, 3] } else if (batched_x.dim() == 2) { if (batched_x.size(1) != dim_x) { throw std::runtime_error("flame_expression_jacobian: batched_x size mismatch."); } batch_size = batched_x.size(0); batched_v_output = torch::zeros({batch_size, num_vertices, 3}, v_canonical.options()); // [B, V, 3] batched_j_output = torch::zeros({batch_size, NUM_FLAME_JOINTS, 3}, v_canonical.options()); // [B, J, 3] batched_v_jacobian = torch::zeros({batch_size, num_vertices, dim_x, 3}, v_canonical.options()); // [B, V, dim_x, 3] batched_j_jacobian = torch::zeros({batch_size, NUM_FLAME_JOINTS, dim_x, 3}, v_canonical.options()); // [B, J, dim_x, 3] } else { throw std::runtime_error("flame_expression_jacobian: invalid batched_x shape."); } const int work_size = batch_size * ( NUM_FLAME_POSEFEAT_BASIS + NUM_FLAME_JOINTS * 4 * 4 + NUM_FLAME_JOINTS * 3 * 3 + NUM_FLAME_JOINTS * NUM_FLAME_JOINTS * 4 * 4 + NUM_FLAME_JOINTS * 3 * 3 ); torch::Tensor work_tensor = torch::zeros({work_size}, v_canonical.options()); float* work_ptr = work_tensor.data_ptr(); int offset = 0; float* batched_pose_feat_ptr = work_ptr + offset; offset += batch_size * NUM_FLAME_POSEFEAT_BASIS; glm::mat4* batched_affine_mats_ptr = reinterpret_cast(work_ptr + offset); offset += batch_size * NUM_FLAME_JOINTS * 4 * 4; glm::mat3* batched_right_jacobian_ptr = reinterpret_cast(work_ptr + offset); offset += batch_size * NUM_FLAME_JOINTS * 3 * 3; glm::mat4* batched_B_masked_ptr = reinterpret_cast(work_ptr + offset); offset += batch_size * NUM_FLAME_JOINTS * NUM_FLAME_JOINTS * 4 * 4; glm::mat3* batched_Ar_ptr = reinterpret_cast(work_ptr + offset); FlameTracking::flame_expression_jacobian( num_vertices, num_expressions, batch_size, batched_x.contiguous().data_ptr(), reinterpret_cast(v_canonical.contiguous().data_ptr()), reinterpret_cast(j_canonical.contiguous().data_ptr()), reinterpret_cast(expr_dirs.contiguous().data_ptr()), reinterpret_cast(pose_dirs.contiguous().data_ptr()), lbs_weights.contiguous().data_ptr(), batched_pose_feat_ptr, batched_affine_mats_ptr, batched_right_jacobian_ptr, batched_B_masked_ptr, batched_Ar_ptr, reinterpret_cast(batched_v_output.contiguous().data_ptr()), reinterpret_cast(batched_j_output.contiguous().data_ptr()), batched_v_jacobian.contiguous().data_ptr(), batched_j_jacobian.contiguous().data_ptr() ); return std::make_tuple(batched_v_output, batched_j_output, batched_v_jacobian, batched_j_jacobian); } std::tuple flame_expression_forward( const torch::Tensor& batched_x, // [B, dim_x] cuda const torch::Tensor& v_canonical, // [V, 3] cuda const torch::Tensor& j_canonical, // [5, 3] cuda const torch::Tensor& expr_dirs, // [num_expressions, 5023 * 3] cuda const torch::Tensor& pose_dirs, // [ 36, 5023 * 3] cuda const torch::Tensor& lbs_weights, // [5023, 5] cuda const int num_expressions ) { const int dim_x = dim_x_for(num_expressions); if (v_canonical.dim() != 2 || v_canonical.size(1) != 3) { throw std::runtime_error("flame_expression_forward: v_canonical must be [V,3]."); } const int num_vertices = infer_supported_num_vertices(v_canonical.size(0), "flame_expression_forward"); if ( expr_dirs.dim() != 2 || expr_dirs.size(0) != num_expressions || expr_dirs.size(1) != num_vertices * 3 || pose_dirs.dim() != 2 || pose_dirs.size(1) != num_vertices * 3 || lbs_weights.dim() != 2 || lbs_weights.size(0) != num_vertices || lbs_weights.size(1) != NUM_FLAME_JOINTS ) { throw std::runtime_error("flame_expression_forward: invalid FLAME asset tensor shape."); } int batch_size = 1.0; torch::Tensor batched_v_output, batched_j_output; if (batched_x.dim() == 1) { if (batched_x.size(0) != dim_x) { throw std::runtime_error("flame_expression_forward: batched_x size mismatch."); } batched_v_output = torch::zeros({num_vertices, 3}, v_canonical.options()); batched_j_output = torch::zeros({NUM_FLAME_JOINTS, 3}, v_canonical.options()); } else if (batched_x.dim() == 2) { if (batched_x.size(1) != dim_x) { throw std::runtime_error("flame_expression_forward: batched_x size mismatch."); } batch_size = batched_x.size(0); batched_v_output = torch::zeros({batch_size, num_vertices, 3}, v_canonical.options()); batched_j_output = torch::zeros({batch_size, NUM_FLAME_JOINTS, 3}, v_canonical.options()); } else { throw std::runtime_error("flame_expression_forward: invalid batched_x shape."); } const int work_size = batch_size * ( NUM_FLAME_POSEFEAT_BASIS + NUM_FLAME_JOINTS * 4 * 4 ); torch::Tensor work_tensor = torch::zeros({work_size}, v_canonical.options()); float* work_ptr = work_tensor.data_ptr(); int offset = 0; float* batched_pose_feat_ptr = work_ptr + offset; offset += batch_size * NUM_FLAME_POSEFEAT_BASIS; glm::mat4* batched_affine_mats_ptr = reinterpret_cast(work_ptr + offset); FlameTracking::flame_expression_forward( num_vertices, num_expressions, batch_size, batched_x.contiguous().data_ptr(), reinterpret_cast(v_canonical.contiguous().data_ptr()), reinterpret_cast(j_canonical.contiguous().data_ptr()), reinterpret_cast(expr_dirs.contiguous().data_ptr()), reinterpret_cast(pose_dirs.contiguous().data_ptr()), lbs_weights.contiguous().data_ptr(), batched_pose_feat_ptr, batched_affine_mats_ptr, reinterpret_cast(batched_v_output.contiguous().data_ptr()), reinterpret_cast(batched_j_output.contiguous().data_ptr()) ); return std::make_tuple(batched_v_output, batched_j_output); } std::tuple flame_identity_jacobian( const torch::Tensor& batched_x, // [B, dim_x] or [dim_x] const torch::Tensor& v_canonical, // [V, 3] const torch::Tensor& j_canonical, // [5, 3] const torch::Tensor& ref_joints_dirs, // [300, 5, 3] const torch::Tensor& shape_dirs, // [300, 5023 * 3] const torch::Tensor& expr_dirs, // [num_expressions, 5023 * 3] const torch::Tensor& pose_dirs, // [ 36, 5023 * 3] const torch::Tensor& lbs_weights, // [5023, 5] const int num_expressions ) { const int dim_x = dim_x_for(num_expressions); if (v_canonical.dim() != 2 || v_canonical.size(1) != 3) { throw std::runtime_error("flame_identity_jacobian: v_canonical must be [V,3]."); } const int num_vertices = infer_supported_num_vertices(v_canonical.size(0), "flame_identity_jacobian"); if ( shape_dirs.dim() != 2 || shape_dirs.size(0) != NUM_FLAME_IDENTITY_BASIS || shape_dirs.size(1) != num_vertices * 3 || expr_dirs.dim() != 2 || expr_dirs.size(0) != num_expressions || expr_dirs.size(1) != num_vertices * 3 || pose_dirs.dim() != 2 || pose_dirs.size(1) != num_vertices * 3 || lbs_weights.dim() != 2 || lbs_weights.size(0) != num_vertices || lbs_weights.size(1) != NUM_FLAME_JOINTS ) { throw std::runtime_error("flame_identity_jacobian: invalid FLAME asset tensor shape."); } int batch_size = 1.0; torch::Tensor batched_v_output, batched_j_output, batched_v_jacobian, batched_j_jacobian; if (batched_x.dim() == 1) { if (batched_x.size(0) != dim_x) { throw std::runtime_error("flame_identity_jacobian: batched_x size mismatch."); } batched_v_output = torch::zeros({num_vertices, 3}, v_canonical.options()); // [V, 3] batched_j_output = torch::zeros({NUM_FLAME_JOINTS, 3}, v_canonical.options()); // [J, 3] batched_v_jacobian = torch::zeros({num_vertices, NUM_FLAME_IDENTITY_BASIS, 3}, v_canonical.options()); // [V, 300, 3] batched_j_jacobian = torch::zeros({NUM_FLAME_JOINTS, NUM_FLAME_IDENTITY_BASIS, 3}, v_canonical.options()); // [J, 300, 3] } else if (batched_x.dim() == 2) { if (batched_x.size(1) != dim_x) { throw std::runtime_error("flame_identity_jacobian: batched_x size mismatch."); } batch_size = batched_x.size(0); batched_v_output = torch::zeros({batch_size, num_vertices, 3}, v_canonical.options()); // [B, V, 3] batched_j_output = torch::zeros({batch_size, NUM_FLAME_JOINTS, 3}, v_canonical.options()); // [B, J, 3] batched_v_jacobian = torch::zeros({batch_size, num_vertices, NUM_FLAME_IDENTITY_BASIS, 3}, v_canonical.options()); // [B, V, 300, 3] batched_j_jacobian = torch::zeros({batch_size, NUM_FLAME_JOINTS, NUM_FLAME_IDENTITY_BASIS, 3}, v_canonical.options()); // [B, J, 300, 3] } else { throw std::runtime_error("flame_identity_jacobian: invalid batched_x shape."); } const int work_size = batch_size * ( NUM_FLAME_POSEFEAT_BASIS + NUM_FLAME_JOINTS * 4 * 4 + NUM_FLAME_JOINTS * 4 * 4 ); torch::Tensor work_tensor = torch::zeros({work_size}, v_canonical.options()); float* work_ptr = work_tensor.data_ptr(); int offset = 0; float* batched_pose_feat_ptr = work_ptr + offset; offset += batch_size * NUM_FLAME_POSEFEAT_BASIS; glm::mat4* batched_rel_affine_mats = reinterpret_cast(work_ptr + offset); offset += batch_size * NUM_FLAME_JOINTS * 4 * 4; glm::mat4* batched_affine_mats = reinterpret_cast(work_ptr + offset); FlameTracking::flame_identity_jacobian( num_vertices, num_expressions, batch_size, batched_x.contiguous().data_ptr(), reinterpret_cast(v_canonical.contiguous().data_ptr()), reinterpret_cast(j_canonical.contiguous().data_ptr()), reinterpret_cast(ref_joints_dirs.contiguous().data_ptr()), reinterpret_cast(shape_dirs.contiguous().data_ptr()), reinterpret_cast(expr_dirs.contiguous().data_ptr()), reinterpret_cast(pose_dirs.contiguous().data_ptr()), lbs_weights.contiguous().data_ptr(), batched_pose_feat_ptr, batched_rel_affine_mats, batched_affine_mats, reinterpret_cast(batched_v_output.contiguous().data_ptr()), reinterpret_cast(batched_j_output.contiguous().data_ptr()), batched_v_jacobian.contiguous().data_ptr(), batched_j_jacobian.contiguous().data_ptr() ); return std::make_tuple(batched_v_output, batched_j_output, batched_v_jacobian, batched_j_jacobian); } std::tuple assemble_x_jacobian( const torch::Tensor& view_mat, const torch::Tensor& viewproj_mat, const torch::Tensor& batched_vertices, const torch::Tensor& batched_joints, const torch::Tensor& gt_projected_vertices, const torch::Tensor& gt_vertex_rel_depths, const torch::Tensor& gt_projected_log_vars, const torch::Tensor& gt_vertex_rel_depth_log_vars, const torch::Tensor& batched_x, const torch::Tensor& batched_v_jacobian, const torch::Tensor& batched_j_jacobian, const float alignment_weight, const float rel_depth_weight, const float exp_reg_weight, const float pose_reg_weight, const torch::Tensor& vertex_indices, const int num_expressions ) { const int dim_x = dim_x_for(num_expressions); if (batched_vertices.dim() != 3 || batched_vertices.size(2) != 3) { throw std::runtime_error("assemble_x_jacobian: batched_vertices must be [T,V,3]."); } const int num_vertices = infer_supported_num_vertices(batched_vertices.size(1), "assemble_x_jacobian"); // Camera matrices: [C, 4, 4] (shared) or [T, C, 4, 4] (per-batch) int num_views, camera_stride; torch::Tensor view_mat_flat, viewproj_mat_flat; if (view_mat.dim() == 4) { num_views = view_mat.size(1); camera_stride = num_views; view_mat_flat = view_mat.reshape({-1, 4, 4}).contiguous(); viewproj_mat_flat = viewproj_mat.reshape({-1, 4, 4}).contiguous(); } else if (view_mat.dim() == 3) { num_views = view_mat.size(0); camera_stride = 0; view_mat_flat = view_mat.contiguous(); viewproj_mat_flat = viewproj_mat.contiguous(); } else { throw std::runtime_error("assemble_x_jacobian: view_mat must be [C,4,4] or [T,C,4,4]."); } if ( view_mat.size(-2) != 4 || view_mat.size(-1) != 4 || viewproj_mat.size(-2) != 4 || viewproj_mat.size(-1) != 4 || batched_vertices.dim() != 3 || batched_vertices.size(1) != num_vertices || batched_vertices.size(2) != 3 || batched_joints.dim() != 3 || batched_joints.size(1) != NUM_FLAME_JOINTS || batched_joints.size(2) != 3 || gt_projected_vertices.dim() != 4 || gt_projected_vertices.size(3) != 2 || gt_vertex_rel_depths.dim() != 4 || gt_vertex_rel_depths.size(3) != 1 || batched_x.dim() != 2 || batched_x.size(1) != dim_x || batched_v_jacobian.dim() != 4 || batched_v_jacobian.size(1) != num_vertices || batched_v_jacobian.size(2) != dim_x || batched_v_jacobian.size(3) != 3 || batched_j_jacobian.dim() != 4 || batched_j_jacobian.size(1) != NUM_FLAME_JOINTS || batched_j_jacobian.size(2) != dim_x || batched_j_jacobian.size(3) != 3 || vertex_indices.dim() != 1 ) { throw std::runtime_error("assemble_x_jacobian: invalid tensor shape."); } const int batch_size = batched_x.size(0); if (view_mat.dim() == 4 && view_mat.size(0) != batch_size) { throw std::runtime_error("assemble_x_jacobian: view_mat [T,C,4,4] T must match batch_size."); } const bool has_projected_log_var = gt_projected_log_vars.numel() > 0; const bool has_depth_log_var = gt_vertex_rel_depth_log_vars.numel() > 0; if (has_projected_log_var != has_depth_log_var) { throw std::runtime_error("assemble_x_jacobian: projected/depth log_var tensors must both be empty or both be non-empty."); } if ( has_projected_log_var && ( gt_projected_log_vars.dim() != 4 || gt_projected_log_vars.size(3) != 1 || gt_projected_log_vars.size(0) != batch_size || gt_projected_log_vars.size(1) != num_views || gt_projected_log_vars.size(2) != num_vertices ) ) { throw std::runtime_error("assemble_x_jacobian: gt_projected_log_vars must be [T,C,V,1] when provided."); } if ( has_depth_log_var && ( gt_vertex_rel_depth_log_vars.dim() != 4 || gt_vertex_rel_depth_log_vars.size(3) != 1 || gt_vertex_rel_depth_log_vars.size(0) != batch_size || gt_vertex_rel_depth_log_vars.size(1) != num_views || gt_vertex_rel_depth_log_vars.size(2) != num_vertices ) ) { throw std::runtime_error("assemble_x_jacobian: gt_vertex_rel_depth_log_vars must be [T,C,V,1] when provided."); } if ( batched_vertices.size(0) != batch_size || batched_joints.size(0) != batch_size || gt_projected_vertices.size(0) != batch_size || gt_projected_vertices.size(1) != num_views || gt_projected_vertices.size(2) != num_vertices || gt_vertex_rel_depths.size(0) != batch_size || gt_vertex_rel_depths.size(1) != num_views || gt_vertex_rel_depths.size(2) != num_vertices || batched_v_jacobian.size(0) != batch_size || batched_j_jacobian.size(0) != batch_size ) { throw std::runtime_error("assemble_x_jacobian: inconsistent tensor shape."); } const int num_items = vertex_indices.numel() > 0 ? vertex_indices.numel() : num_vertices; const bool use_rel_depth = rel_depth_weight > 0.0f; const int obs_dim = use_rel_depth ? 3 : 2; const int num_residuals = num_views * num_items * obs_dim + num_expressions + (NUM_FLAME_JOINTS - 1) * 3; torch::Tensor residual = torch::zeros({batch_size, num_residuals}, batched_vertices.options()); torch::Tensor jacobian = torch::zeros({batch_size, num_residuals, dim_x}, batched_vertices.options()); FlameTracking::assemble_x_jacobian( num_vertices, num_expressions, batch_size, num_views, num_items, camera_stride, vertex_indices.numel() > 0 ? vertex_indices.contiguous().data_ptr() : nullptr, alignment_weight, rel_depth_weight, batched_x.contiguous().data_ptr(), exp_reg_weight, pose_reg_weight, reinterpret_cast(view_mat_flat.data_ptr()), reinterpret_cast(viewproj_mat_flat.data_ptr()), reinterpret_cast(batched_vertices.contiguous().data_ptr()), reinterpret_cast(batched_joints.contiguous().data_ptr()), reinterpret_cast(gt_projected_vertices.contiguous().data_ptr()), gt_vertex_rel_depths.contiguous().data_ptr(), has_projected_log_var ? gt_projected_log_vars.contiguous().data_ptr() : nullptr, has_depth_log_var ? gt_vertex_rel_depth_log_vars.contiguous().data_ptr() : nullptr, reinterpret_cast(batched_v_jacobian.contiguous().data_ptr()), reinterpret_cast(batched_j_jacobian.contiguous().data_ptr()), residual.contiguous().data_ptr(), jacobian.contiguous().data_ptr() ); return std::make_tuple(residual, jacobian); } std::tuple assemble_identity_jacobian( const torch::Tensor& view_mat, const torch::Tensor& viewproj_mat, const torch::Tensor& batched_vertices, const torch::Tensor& batched_joints, const torch::Tensor& gt_projected_vertices, const torch::Tensor& gt_vertex_rel_depths, const torch::Tensor& gt_projected_log_vars, const torch::Tensor& gt_vertex_rel_depth_log_vars, const torch::Tensor& identity, const torch::Tensor& batched_v_jacobian, const torch::Tensor& batched_j_jacobian, const float alignment_weight, const float rel_depth_weight, const float identity_reg_weight, const torch::Tensor& vertex_indices ) { if (batched_vertices.dim() != 3 || batched_vertices.size(2) != 3) { throw std::runtime_error("assemble_identity_jacobian: batched_vertices must be [T,V,3]."); } const int num_vertices = infer_supported_num_vertices(batched_vertices.size(1), "assemble_identity_jacobian"); // Camera matrices: [C, 4, 4] (shared) or [T, C, 4, 4] (per-batch) int num_views, camera_stride; torch::Tensor view_mat_flat, viewproj_mat_flat; if (view_mat.dim() == 4) { num_views = view_mat.size(1); camera_stride = num_views; view_mat_flat = view_mat.reshape({-1, 4, 4}).contiguous(); viewproj_mat_flat = viewproj_mat.reshape({-1, 4, 4}).contiguous(); } else if (view_mat.dim() == 3) { num_views = view_mat.size(0); camera_stride = 0; view_mat_flat = view_mat.contiguous(); viewproj_mat_flat = viewproj_mat.contiguous(); } else { throw std::runtime_error("assemble_identity_jacobian: view_mat must be [C,4,4] or [T,C,4,4]."); } if ( view_mat.size(-2) != 4 || view_mat.size(-1) != 4 || viewproj_mat.size(-2) != 4 || viewproj_mat.size(-1) != 4 || batched_vertices.dim() != 3 || batched_vertices.size(1) != num_vertices || batched_vertices.size(2) != 3 || batched_joints.dim() != 3 || batched_joints.size(1) != NUM_FLAME_JOINTS || batched_joints.size(2) != 3 || gt_projected_vertices.dim() != 4 || gt_projected_vertices.size(3) != 2 || gt_vertex_rel_depths.dim() != 4 || gt_vertex_rel_depths.size(3) != 1 || identity.dim() != 1 || identity.size(0) != NUM_FLAME_IDENTITY_BASIS || batched_v_jacobian.dim() != 4 || batched_v_jacobian.size(1) != num_vertices || batched_v_jacobian.size(2) != NUM_FLAME_IDENTITY_BASIS || batched_v_jacobian.size(3) != 3 || batched_j_jacobian.dim() != 4 || batched_j_jacobian.size(1) != NUM_FLAME_JOINTS || batched_j_jacobian.size(2) != NUM_FLAME_IDENTITY_BASIS || batched_j_jacobian.size(3) != 3 || vertex_indices.dim() != 1 ) { throw std::runtime_error("assemble_identity_jacobian: invalid tensor shape."); } const int batch_size = batched_vertices.size(0); if (view_mat.dim() == 4 && view_mat.size(0) != batch_size) { throw std::runtime_error("assemble_identity_jacobian: view_mat [T,C,4,4] T must match batch_size."); } const bool has_projected_log_var = gt_projected_log_vars.numel() > 0; const bool has_depth_log_var = gt_vertex_rel_depth_log_vars.numel() > 0; if (has_projected_log_var != has_depth_log_var) { throw std::runtime_error("assemble_identity_jacobian: projected/depth log_var tensors must both be empty or both be non-empty."); } if ( has_projected_log_var && ( gt_projected_log_vars.dim() != 4 || gt_projected_log_vars.size(3) != 1 || gt_projected_log_vars.size(0) != batch_size || gt_projected_log_vars.size(1) != num_views || gt_projected_log_vars.size(2) != num_vertices ) ) { throw std::runtime_error("assemble_identity_jacobian: gt_projected_log_vars must be [T,C,V,1] when provided."); } if ( has_depth_log_var && ( gt_vertex_rel_depth_log_vars.dim() != 4 || gt_vertex_rel_depth_log_vars.size(3) != 1 || gt_vertex_rel_depth_log_vars.size(0) != batch_size || gt_vertex_rel_depth_log_vars.size(1) != num_views || gt_vertex_rel_depth_log_vars.size(2) != num_vertices ) ) { throw std::runtime_error("assemble_identity_jacobian: gt_vertex_rel_depth_log_vars must be [T,C,V,1] when provided."); } if ( batched_joints.size(0) != batch_size || gt_projected_vertices.size(0) != batch_size || gt_projected_vertices.size(1) != num_views || gt_projected_vertices.size(2) != num_vertices || gt_vertex_rel_depths.size(0) != batch_size || gt_vertex_rel_depths.size(1) != num_views || gt_vertex_rel_depths.size(2) != num_vertices || batched_v_jacobian.size(0) != batch_size || batched_j_jacobian.size(0) != batch_size ) { throw std::runtime_error("assemble_identity_jacobian: inconsistent tensor shape."); } const int num_items = vertex_indices.numel() > 0 ? vertex_indices.numel() : num_vertices; const bool use_rel_depth = rel_depth_weight > 0.0f; const int obs_dim = use_rel_depth ? 3 : 2; const int num_residuals = batch_size * num_views * num_items * obs_dim + NUM_FLAME_IDENTITY_BASIS; torch::Tensor residual = torch::zeros({num_residuals}, batched_vertices.options()); torch::Tensor jacobian = torch::zeros({num_residuals, NUM_FLAME_IDENTITY_BASIS}, batched_vertices.options()); FlameTracking::assemble_identity_jacobian( num_vertices, batch_size, num_views, num_items, camera_stride, vertex_indices.numel() > 0 ? vertex_indices.contiguous().data_ptr() : nullptr, alignment_weight, rel_depth_weight, identity.contiguous().data_ptr(), identity_reg_weight, reinterpret_cast(view_mat_flat.data_ptr()), reinterpret_cast(viewproj_mat_flat.data_ptr()), reinterpret_cast(batched_vertices.contiguous().data_ptr()), reinterpret_cast(batched_joints.contiguous().data_ptr()), reinterpret_cast(gt_projected_vertices.contiguous().data_ptr()), gt_vertex_rel_depths.contiguous().data_ptr(), has_projected_log_var ? gt_projected_log_vars.contiguous().data_ptr() : nullptr, has_depth_log_var ? gt_vertex_rel_depth_log_vars.contiguous().data_ptr() : nullptr, reinterpret_cast(batched_v_jacobian.contiguous().data_ptr()), reinterpret_cast(batched_j_jacobian.contiguous().data_ptr()), residual.contiguous().data_ptr(), jacobian.contiguous().data_ptr() ); return std::make_tuple(residual, jacobian); } std::tuple assemble_x_direct_jacobian( const torch::Tensor& gt_vertices, const torch::Tensor& batched_x, const torch::Tensor& batched_vertices, const torch::Tensor& batched_v_jacobian, const float vertex_weight, const float exp_reg_weight, const float pose_reg_weight, const torch::Tensor& vertex_indices, const int num_expressions ) { const int dim_x = dim_x_for(num_expressions); if (batched_vertices.dim() != 3 || batched_vertices.size(2) != 3) { throw std::runtime_error("assemble_x_direct_jacobian: batched_vertices must be [T,V,3]."); } const int num_vertices = infer_supported_num_vertices(batched_vertices.size(1), "assemble_x_direct_jacobian"); if ( gt_vertices.dim() != 3 || gt_vertices.size(1) != num_vertices || gt_vertices.size(2) != 3 || batched_x.dim() != 2 || batched_x.size(1) != dim_x || batched_vertices.dim() != 3 || batched_vertices.size(1) != num_vertices || batched_vertices.size(2) != 3 || batched_v_jacobian.dim() != 4 || batched_v_jacobian.size(1) != num_vertices || batched_v_jacobian.size(2) != dim_x || batched_v_jacobian.size(3) != 3 || vertex_indices.dim() != 1 ) { throw std::runtime_error("assemble_x_direct_jacobian: invalid tensor shape."); } const int batch_size = batched_x.size(0); if ( gt_vertices.size(0) != batch_size || batched_vertices.size(0) != batch_size || batched_v_jacobian.size(0) != batch_size ) { throw std::runtime_error("assemble_x_direct_jacobian: inconsistent tensor shape."); } const int num_items = vertex_indices.numel() > 0 ? vertex_indices.numel() : num_vertices; const int num_residuals = num_items * 3 + num_expressions + (NUM_FLAME_JOINTS - 1) * 3; torch::Tensor residual = torch::zeros({batch_size, num_residuals}, batched_vertices.options()); torch::Tensor jacobian = torch::zeros({batch_size, num_residuals, dim_x}, batched_vertices.options()); FlameTracking::assemble_x_direct_jacobian( num_vertices, num_expressions, batch_size, num_items, vertex_indices.numel() > 0 ? vertex_indices.contiguous().data_ptr() : nullptr, vertex_weight, batched_x.contiguous().data_ptr(), exp_reg_weight, pose_reg_weight, reinterpret_cast(batched_vertices.contiguous().data_ptr()), reinterpret_cast(gt_vertices.contiguous().data_ptr()), reinterpret_cast(batched_v_jacobian.contiguous().data_ptr()), residual.contiguous().data_ptr(), jacobian.contiguous().data_ptr() ); return std::make_tuple(residual, jacobian); } std::tuple assemble_identity_direct_jacobian( const torch::Tensor& gt_vertices, const torch::Tensor& identity, const torch::Tensor& batched_vertices, const torch::Tensor& batched_v_jacobian, const float vertex_weight, const float identity_reg_weight, const torch::Tensor& vertex_indices ) { if (batched_vertices.dim() != 3 || batched_vertices.size(2) != 3) { throw std::runtime_error("assemble_identity_direct_jacobian: batched_vertices must be [T,V,3]."); } const int num_vertices = infer_supported_num_vertices(batched_vertices.size(1), "assemble_identity_direct_jacobian"); if ( gt_vertices.dim() != 3 || gt_vertices.size(1) != num_vertices || gt_vertices.size(2) != 3 || identity.dim() != 1 || identity.size(0) != NUM_FLAME_IDENTITY_BASIS || batched_vertices.dim() != 3 || batched_vertices.size(1) != num_vertices || batched_vertices.size(2) != 3 || batched_v_jacobian.dim() != 4 || batched_v_jacobian.size(1) != num_vertices || batched_v_jacobian.size(2) != NUM_FLAME_IDENTITY_BASIS || batched_v_jacobian.size(3) != 3 || vertex_indices.dim() != 1 ) { throw std::runtime_error("assemble_identity_direct_jacobian: invalid tensor shape."); } const int batch_size = batched_vertices.size(0); if ( gt_vertices.size(0) != batch_size || batched_v_jacobian.size(0) != batch_size ) { throw std::runtime_error("assemble_identity_direct_jacobian: inconsistent tensor shape."); } const int num_items = vertex_indices.numel() > 0 ? vertex_indices.numel() : num_vertices; const int num_residuals = batch_size * num_items * 3 + NUM_FLAME_IDENTITY_BASIS; torch::Tensor residual = torch::zeros({num_residuals}, batched_vertices.options()); torch::Tensor jacobian = torch::zeros({num_residuals, NUM_FLAME_IDENTITY_BASIS}, batched_vertices.options()); FlameTracking::assemble_identity_direct_jacobian( num_vertices, batch_size, num_items, vertex_indices.numel() > 0 ? vertex_indices.contiguous().data_ptr() : nullptr, vertex_weight, identity.contiguous().data_ptr(), identity_reg_weight, reinterpret_cast(batched_vertices.contiguous().data_ptr()), reinterpret_cast(gt_vertices.contiguous().data_ptr()), reinterpret_cast(batched_v_jacobian.contiguous().data_ptr()), residual.contiguous().data_ptr(), jacobian.contiguous().data_ptr() ); return std::make_tuple(residual, jacobian); } torch::Tensor cholesky_solve( const int64_t& cublas_handle, const int64_t& cusolver_handle, const torch::Tensor& jacobian, // [R, X] or [B, R, X] const torch::Tensor& residual, // [R] or [B, R] const float lambda = 0.0f ) { if (jacobian.dim() == 2 && residual.dim() == 1) { const int m = jacobian.size(0); const int n = jacobian.size(1); torch::Tensor x = torch::zeros({n}, residual.options()); torch::Tensor mem = torch::empty({0}, residual.options().dtype(torch::kUInt8)); std::function mem_func = create_byte_allocator(mem); FlameTracking::cholesky_solve( reinterpret_cast(cublas_handle), reinterpret_cast(cusolver_handle), mem_func, jacobian.contiguous().data_ptr(), residual.contiguous().data_ptr(), x.contiguous().data_ptr(), m, n, lambda ); return x; } else if (jacobian.dim() == 3 && residual.dim() == 2) { const int batch_size = jacobian.size(0); const int m = jacobian.size(1); const int n = jacobian.size(2); torch::Tensor x = torch::zeros({batch_size, n}, residual.options()); torch::Tensor mem = torch::empty({0}, residual.options().dtype(torch::kUInt8)); std::function mem_func = create_byte_allocator(mem); FlameTracking::cholesky_solve_batched( reinterpret_cast(cublas_handle), reinterpret_cast(cusolver_handle), mem_func, jacobian.contiguous().data_ptr(), residual.contiguous().data_ptr(), x.contiguous().data_ptr(), m, n, batch_size, lambda ); return x; } else { throw std::runtime_error("cholesky_solve: invalid tensor shape."); } } int64_t create_cublas_handle() { cublasHandle_t handle; cublasCreate(&handle); cudaStream_t stream = at::cuda::getCurrentCUDAStream(); cublasSetStream(handle, stream); return reinterpret_cast(handle); } void destroy_cublas_handle(const int64_t& handle) { cublasDestroy(reinterpret_cast(handle)); } int64_t create_cusolver_handle() { cusolverDnHandle_t handle; cusolverDnCreate(&handle); cudaStream_t stream = at::cuda::getCurrentCUDAStream(); cusolverDnSetStream(handle, stream); return reinterpret_cast(handle); } void destroy_cusolver_handle(const int64_t& handle) { cusolverDnDestroy(reinterpret_cast(handle)); } PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) { m.def("flame_calc_canonical", &flame_calc_canonical, "FLAME calculate canonical vertices and joints"); m.def("flame_expression_jacobian", &flame_expression_jacobian, "FLAME expression jacobian"); m.def("flame_expression_forward", &flame_expression_forward, "FLAME expression forward"); m.def("flame_identity_jacobian", &flame_identity_jacobian, "FLAME identity jacobian"); m.def("assemble_x_jacobian", &assemble_x_jacobian, "Assemble jacobian matrix for optimizing x"); m.def("assemble_identity_jacobian", &assemble_identity_jacobian, "Assemble jacobian matrix for optimizing identity"); m.def("assemble_x_direct_jacobian", &assemble_x_direct_jacobian, "Assemble direct 3D jacobian matrix for optimizing x"); m.def("assemble_identity_direct_jacobian", &assemble_identity_direct_jacobian, "Assemble direct 3D jacobian matrix for optimizing identity"); m.def("cholesky_solve", &cholesky_solve, "Solve update vector with Cholesky"); m.def("create_cublas_handle", &create_cublas_handle, "Create cublas handle"); m.def("destroy_cublas_handle", &destroy_cublas_handle, "Destroy cublas handle"); m.def("create_cusolver_handle", &create_cusolver_handle, "Create cusolver handle"); m.def("destroy_cusolver_handle", &destroy_cusolver_handle, "Destroy cusolver handle"); }