Libmv: Abstract parallel range in camera intrinsics
The goal is to be able to switch away from OpenMP by default. In order to achieve this a new parallel_for() function is added, which follows the same declaration as the one from TBB. For now the simplest formulation is used where range is provided by start and last indices (the last one is excluded from the range). The side effect is that Libmv now expects the threading limits to be imposed by the default thread area (or have an explicit thread area with the limit in the caller). In a way this is similar to other external libraries. Pull Request: https://projects.blender.org/blender/blender/pulls/136834
This commit is contained in:
committed by
Sergey Sharybin
parent
37ac685854
commit
ccb493b805
@@ -29,6 +29,10 @@ if(WITH_LIBMV)
|
||||
add_definitions(${GLOG_DEFINES})
|
||||
add_definitions(-DLIBMV_GFLAGS_NAMESPACE=${GFLAGS_NAMESPACE})
|
||||
|
||||
if(WITH_TBB)
|
||||
add_definitions(-DLIBMV_USE_TBB_THREADS)
|
||||
endif()
|
||||
|
||||
list(APPEND INC
|
||||
${GFLAGS_INCLUDE_DIRS}
|
||||
${GLOG_INCLUDE_DIRS}
|
||||
@@ -45,6 +49,7 @@ if(WITH_LIBMV)
|
||||
|
||||
list(APPEND LIB
|
||||
extern_ceres
|
||||
PUBLIC bf::dependencies::optional::tbb
|
||||
|
||||
${GLOG_LIBRARIES}
|
||||
${GFLAGS_LIBRARIES}
|
||||
@@ -182,6 +187,7 @@ if(WITH_LIBMV)
|
||||
libmv/simple_pipeline/resect.h
|
||||
libmv/simple_pipeline/tracks.h
|
||||
libmv/threading/threading.h
|
||||
libmv/threading/parallel_for.h
|
||||
libmv/tracking/brute_region_tracker.h
|
||||
libmv/tracking/hybrid_region_tracker.h
|
||||
libmv/tracking/kalman_filter.h
|
||||
@@ -231,6 +237,7 @@ if(WITH_LIBMV)
|
||||
blender_add_test_executable("libmv_brute_region_tracker" "./libmv/tracking/brute_region_tracker_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;extern_ceres")
|
||||
blender_add_test_executable("libmv_klt_region_tracker" "./libmv/tracking/klt_region_tracker_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;extern_ceres")
|
||||
blender_add_test_executable("libmv_pyramid_region_tracker" "./libmv/tracking/pyramid_region_tracker_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;extern_ceres")
|
||||
blender_add_test_executable("libmv_parallel_for" "./libmv/threading/parallel_for_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;extern_ceres")
|
||||
endif()
|
||||
else()
|
||||
list(APPEND SRC
|
||||
|
||||
@@ -74,10 +74,7 @@ void libmv_cameraIntrinsicsUpdate(
|
||||
int image_height = libmv_camera_intrinsics_options->image_height;
|
||||
|
||||
/* Try avoid unnecessary updates, so pre-computed distortion grids
|
||||
* are not freed.
|
||||
*/
|
||||
|
||||
camera_intrinsics->SetThreads(libmv_camera_intrinsics_options->num_threads);
|
||||
* are not freed. */
|
||||
|
||||
if (camera_intrinsics->focal_length() != focal_length) {
|
||||
camera_intrinsics->SetFocalLength(focal_length, focal_length);
|
||||
@@ -177,12 +174,6 @@ void libmv_cameraIntrinsicsUpdate(
|
||||
}
|
||||
}
|
||||
|
||||
void libmv_cameraIntrinsicsSetThreads(libmv_CameraIntrinsics* libmv_intrinsics,
|
||||
int threads) {
|
||||
CameraIntrinsics* camera_intrinsics = (CameraIntrinsics*)libmv_intrinsics;
|
||||
camera_intrinsics->SetThreads(threads);
|
||||
}
|
||||
|
||||
void libmv_cameraIntrinsicsExtractOptions(
|
||||
const libmv_CameraIntrinsics* libmv_intrinsics,
|
||||
libmv_CameraIntrinsicsOptions* camera_intrinsics_options) {
|
||||
|
||||
@@ -20,7 +20,6 @@ enum {
|
||||
|
||||
typedef struct libmv_CameraIntrinsicsOptions {
|
||||
// Common settings of all distortion models.
|
||||
int num_threads;
|
||||
int distortion_model;
|
||||
int image_width, image_height;
|
||||
double focal_length;
|
||||
@@ -52,9 +51,6 @@ void libmv_cameraIntrinsicsUpdate(
|
||||
const libmv_CameraIntrinsicsOptions* libmv_camera_intrinsics_options,
|
||||
libmv_CameraIntrinsics* libmv_intrinsics);
|
||||
|
||||
void libmv_cameraIntrinsicsSetThreads(libmv_CameraIntrinsics* libmv_intrinsics,
|
||||
int threads);
|
||||
|
||||
void libmv_cameraIntrinsicsExtractOptions(
|
||||
const libmv_CameraIntrinsics* libmv_intrinsics,
|
||||
libmv_CameraIntrinsicsOptions* camera_intrinsics_options);
|
||||
|
||||
@@ -218,10 +218,6 @@ void libmv_cameraIntrinsicsUpdate(
|
||||
libmv_CameraIntrinsics* /*libmv_intrinsics*/) {
|
||||
}
|
||||
|
||||
void libmv_cameraIntrinsicsSetThreads(
|
||||
libmv_CameraIntrinsics* /*libmv_intrinsics*/, int /*threads*/) {
|
||||
}
|
||||
|
||||
void libmv_cameraIntrinsicsExtractOptions(
|
||||
const libmv_CameraIntrinsics* /*libmv_intrinsics*/,
|
||||
libmv_CameraIntrinsicsOptions* camera_intrinsics_options) {
|
||||
|
||||
@@ -29,15 +29,14 @@ namespace libmv {
|
||||
namespace internal {
|
||||
|
||||
LookupWarpGrid::LookupWarpGrid()
|
||||
: offset_(NULL), width_(0), height_(0), overscan_(0.0), threads_(1) {
|
||||
: offset_(NULL), width_(0), height_(0), overscan_(0.0) {
|
||||
}
|
||||
|
||||
LookupWarpGrid::LookupWarpGrid(const LookupWarpGrid& from)
|
||||
: offset_(NULL),
|
||||
width_(from.width_),
|
||||
height_(from.height_),
|
||||
overscan_(from.overscan_),
|
||||
threads_(from.threads_) {
|
||||
overscan_(from.overscan_) {
|
||||
if (from.offset_) {
|
||||
offset_ = new Offset[width_ * height_];
|
||||
memcpy(offset_, from.offset_, sizeof(Offset) * width_ * height_);
|
||||
@@ -53,11 +52,6 @@ void LookupWarpGrid::Reset() {
|
||||
offset_ = NULL;
|
||||
}
|
||||
|
||||
// Set number of threads used for threaded buffer distortion/undistortion.
|
||||
void LookupWarpGrid::SetThreads(int threads) {
|
||||
threads_ = threads;
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
||||
CameraIntrinsics::CameraIntrinsics()
|
||||
@@ -99,12 +93,6 @@ void CameraIntrinsics::SetPrincipalPoint(double cx, double cy) {
|
||||
ResetLookupGrids();
|
||||
}
|
||||
|
||||
// Set number of threads used for threaded buffer distortion/undistortion.
|
||||
void CameraIntrinsics::SetThreads(int threads) {
|
||||
distort_.SetThreads(threads);
|
||||
undistort_.SetThreads(threads);
|
||||
}
|
||||
|
||||
void CameraIntrinsics::ImageSpaceToNormalized(double image_x,
|
||||
double image_y,
|
||||
double* normalized_x,
|
||||
|
||||
@@ -82,9 +82,6 @@ class LookupWarpGrid {
|
||||
// This will tag the grid for update without re-computing it.
|
||||
void Reset();
|
||||
|
||||
// Set number of threads used for threaded buffer distortion/undistortion.
|
||||
void SetThreads(int threads);
|
||||
|
||||
private:
|
||||
// This structure contains an offset in both x,y directions
|
||||
// in an optimized way sawing some bytes per pixel in the memory.
|
||||
@@ -120,9 +117,6 @@ class LookupWarpGrid {
|
||||
|
||||
// Overscan of the image being processed by this grid.
|
||||
double overscan_;
|
||||
|
||||
// Number of threads which will be used for buffer istortion/undistortion.
|
||||
int threads_;
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
@@ -160,9 +154,6 @@ class CameraIntrinsics {
|
||||
// Set principal point in pixels.
|
||||
void SetPrincipalPoint(double cx, double cy);
|
||||
|
||||
// Set number of threads used for threaded buffer distortion/undistortion.
|
||||
void SetThreads(int threads);
|
||||
|
||||
// Convert image space coordinates to normalized.
|
||||
void ImageSpaceToNormalized(double image_x,
|
||||
double image_y,
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
|
||||
#include "libmv/threading/parallel_for.h"
|
||||
|
||||
namespace libmv {
|
||||
|
||||
namespace {
|
||||
@@ -59,15 +61,12 @@ void LookupWarpGrid::Compute(const CameraIntrinsics& intrinsics,
|
||||
int width,
|
||||
int height,
|
||||
double overscan) {
|
||||
double w = (double)width / (1.0 + overscan);
|
||||
double h = (double)height / (1.0 + overscan);
|
||||
double aspx = (double)w / intrinsics.image_width();
|
||||
double aspy = (double)h / intrinsics.image_height();
|
||||
#if defined(_OPENMP)
|
||||
# pragma omp parallel for schedule(static) \
|
||||
num_threads(threads_) if (threads_ > 1 && height > 100)
|
||||
#endif
|
||||
for (int y = 0; y < height; y++) {
|
||||
const double w = (double)width / (1.0 + overscan);
|
||||
const double h = (double)height / (1.0 + overscan);
|
||||
const double aspx = (double)w / intrinsics.image_width();
|
||||
const double aspy = (double)h / intrinsics.image_height();
|
||||
|
||||
parallel_for(0, height, [&](const int y) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
double src_x = (x - 0.5 * overscan * w) / aspx,
|
||||
src_y = (y - 0.5 * overscan * h) / aspy;
|
||||
@@ -80,18 +79,18 @@ void LookupWarpGrid::Compute(const CameraIntrinsics& intrinsics,
|
||||
if (fx == 256) {
|
||||
fx = 0;
|
||||
ix++;
|
||||
} // NOLINT
|
||||
}
|
||||
if (fy == 256) {
|
||||
fy = 0;
|
||||
iy++;
|
||||
} // NOLINT
|
||||
}
|
||||
// Use nearest border pixel
|
||||
if (ix < 0) {
|
||||
ix = 0, fx = 0;
|
||||
} // NOLINT
|
||||
}
|
||||
if (iy < 0) {
|
||||
iy = 0, fy = 0;
|
||||
} // NOLINT
|
||||
}
|
||||
if (ix >= width - 2)
|
||||
ix = width - 2;
|
||||
if (iy >= height - 2)
|
||||
@@ -103,7 +102,7 @@ void LookupWarpGrid::Compute(const CameraIntrinsics& intrinsics,
|
||||
(unsigned char)fy};
|
||||
offset_[y * width + x] = offset;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
template <typename WarpFunction>
|
||||
@@ -132,11 +131,7 @@ void LookupWarpGrid::Apply(const PixelType* input_buffer,
|
||||
int height,
|
||||
int channels,
|
||||
PixelType* output_buffer) {
|
||||
#if defined(_OPENMP)
|
||||
# pragma omp parallel for schedule(static) \
|
||||
num_threads(threads_) if (threads_ > 1 && height > 100)
|
||||
#endif
|
||||
for (int y = 0; y < height; y++) {
|
||||
parallel_for(0, height, [&](const int y) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
Offset offset = offset_[y * width + x];
|
||||
const int pixel_index =
|
||||
@@ -152,7 +147,7 @@ void LookupWarpGrid::Apply(const PixelType* input_buffer,
|
||||
(256 * 256);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
||||
60
intern/libmv/libmv/threading/parallel_for.h
Normal file
60
intern/libmv/libmv/threading/parallel_for.h
Normal file
@@ -0,0 +1,60 @@
|
||||
// Copyright (c) 2025 libmv authors.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
|
||||
// TBB-compatible implementation of parallel_for() functions.
|
||||
//
|
||||
// The parallel_for() function applies given functor for every index within the
|
||||
// given range. The range might be provided in multiple ways.
|
||||
//
|
||||
// Supports multiple underlying threading implementations (in the order of
|
||||
// preference):
|
||||
// - TBB, requires LIBMV_USE_TBB_THREADS to be defined.
|
||||
// - OpenMP, requires compiler to use -fopenmp, used when _OPENMP is defined.
|
||||
// - Single-threaded fall-back.
|
||||
//
|
||||
// The function occupies all threads of the default theading pool.
|
||||
|
||||
#ifndef LIBMV_THREADING_PARALLEL_FOR_H_
|
||||
#define LIBMV_THREADING_PARALLEL_FOR_H_
|
||||
|
||||
#if defined(LIBMV_USE_TBB_THREADS)
|
||||
# include <tbb/parallel_for.h>
|
||||
#endif
|
||||
|
||||
namespace libmv {
|
||||
|
||||
// Run the function f() for all indices within [first, last).
|
||||
template <typename Index, typename Function>
|
||||
void parallel_for(const Index first, const Index last, const Function& f) {
|
||||
#if defined(LIBMV_USE_TBB_THREADS)
|
||||
tbb::parallel_for(first, last, f);
|
||||
#else
|
||||
# if defined(_OPENMP)
|
||||
# pragma omp parallel for schedule(static)
|
||||
# endif
|
||||
for (Index i = first; i < last; ++i) {
|
||||
f(i);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace libmv
|
||||
|
||||
#endif // LIBMV_THREADING_PARALLEL_FOR_H_
|
||||
49
intern/libmv/libmv/threading/parallel_for_test.cc
Normal file
49
intern/libmv/libmv/threading/parallel_for_test.cc
Normal file
@@ -0,0 +1,49 @@
|
||||
// Copyright (c) 2025 libmv authors.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
|
||||
#include "libmv/threading/parallel_for.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
#include "libmv/threading/threading.h"
|
||||
#include "testing/testing.h"
|
||||
|
||||
namespace libmv {
|
||||
|
||||
TEST(ParallelFor, SimpleRange) {
|
||||
const int num_elements = 100;
|
||||
|
||||
std::vector<int> data;
|
||||
mutex data_mutex;
|
||||
|
||||
parallel_for(0, num_elements, [&](const int i) {
|
||||
scoped_lock lock(data_mutex);
|
||||
data.push_back(i);
|
||||
});
|
||||
|
||||
std::sort(data.begin(), data.end());
|
||||
|
||||
for (int i = 0; i < num_elements; ++i) {
|
||||
EXPECT_EQ(data[i], i);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace libmv
|
||||
@@ -482,7 +482,6 @@ void BKE_tracking_distortion_update(struct MovieDistortion *distortion,
|
||||
struct MovieTracking *tracking,
|
||||
int calibration_width,
|
||||
int calibration_height);
|
||||
void BKE_tracking_distortion_set_threads(struct MovieDistortion *distortion, int threads);
|
||||
struct MovieDistortion *BKE_tracking_distortion_copy(struct MovieDistortion *distortion);
|
||||
struct ImBuf *BKE_tracking_distortion_exec(struct MovieDistortion *distortion,
|
||||
struct MovieTracking *tracking,
|
||||
|
||||
@@ -2304,11 +2304,6 @@ void BKE_tracking_distortion_update(MovieDistortion *distortion,
|
||||
libmv_cameraIntrinsicsUpdate(&camera_intrinsics_options, distortion->intrinsics);
|
||||
}
|
||||
|
||||
void BKE_tracking_distortion_set_threads(MovieDistortion *distortion, int threads)
|
||||
{
|
||||
libmv_cameraIntrinsicsSetThreads(distortion->intrinsics, threads);
|
||||
}
|
||||
|
||||
MovieDistortion *BKE_tracking_distortion_copy(MovieDistortion *distortion)
|
||||
{
|
||||
MovieDistortion *new_distortion;
|
||||
|
||||
@@ -493,8 +493,6 @@ void tracking_cameraIntrinscisOptionsFromTracking(
|
||||
tracking_principal_point_normalized_to_pixel(
|
||||
camera->principal_point, calibration_width, calibration_height, principal_px);
|
||||
|
||||
camera_intrinsics_options->num_threads = BLI_system_thread_count();
|
||||
|
||||
camera_intrinsics_options->focal_length = camera->focal;
|
||||
|
||||
camera_intrinsics_options->principal_point_x = principal_px[0];
|
||||
|
||||
@@ -1253,13 +1253,11 @@ static void do_movie_proxy(void *pjv,
|
||||
const int efra = clip->len;
|
||||
|
||||
if (build_undistort_count) {
|
||||
int threads = BLI_system_thread_count();
|
||||
int width, height;
|
||||
|
||||
BKE_movieclip_get_size(clip, nullptr, &width, &height);
|
||||
|
||||
distortion = BKE_tracking_distortion_new(&clip->tracking, width, height);
|
||||
BKE_tracking_distortion_set_threads(distortion, threads);
|
||||
}
|
||||
|
||||
for (int cfra = sfra; cfra <= efra; cfra++) {
|
||||
|
||||
Reference in New Issue
Block a user