Add Chromium-only Blender WebEngine parity work
This commit is contained in:
34
blender-5.2.0/intern/libmv/.clang-format
Normal file
34
blender-5.2.0/intern/libmv/.clang-format
Normal file
@@ -0,0 +1,34 @@
|
||||
BasedOnStyle: Google
|
||||
|
||||
ColumnLimit: 80
|
||||
|
||||
Standard: Cpp11
|
||||
|
||||
# Indent nested preprocessor.
|
||||
# #ifdef Foo
|
||||
# # include <nested>
|
||||
# #endif
|
||||
IndentPPDirectives: AfterHash
|
||||
|
||||
# For the cases when namespace is closing with a wrong comment
|
||||
FixNamespaceComments: true
|
||||
|
||||
AllowShortFunctionsOnASingleLine: InlineOnly
|
||||
AllowShortBlocksOnASingleLine: false
|
||||
AllowShortIfStatementsOnASingleLine: false
|
||||
AllowShortLoopsOnASingleLine: false
|
||||
AllowShortCaseLabelsOnASingleLine: true
|
||||
|
||||
# No bin packing, every argument is on its own line.
|
||||
BinPackArguments: false
|
||||
BinPackParameters: false
|
||||
|
||||
# Ensure pointer alignment.
|
||||
# ObjectType* object;
|
||||
PointerAlignment: Left
|
||||
DerivePointerAlignment: false
|
||||
|
||||
AlignEscapedNewlines: Right
|
||||
|
||||
IncludeBlocks: Preserve
|
||||
SortIncludes: true
|
||||
247
blender-5.2.0/intern/libmv/CMakeLists.txt
Normal file
247
blender-5.2.0/intern/libmv/CMakeLists.txt
Normal file
@@ -0,0 +1,247 @@
|
||||
# SPDX-FileCopyrightText: 2011 Blender Authors
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
# NOTE: This file is automatically generated by bundle.sh script
|
||||
# If you're doing changes in this file, please update template
|
||||
# in that script too
|
||||
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
# Suppress these warnings until the issue is addressed as this adds noise.
|
||||
# Use `-Wno-deprecated-copy` as this may have been enabled via `-Wextra`.
|
||||
add_cxx_flag_per_config("-Wno-deprecated-copy")
|
||||
endif()
|
||||
|
||||
set(INC
|
||||
.
|
||||
)
|
||||
|
||||
set(INC_SYS
|
||||
)
|
||||
|
||||
set(SRC
|
||||
libmv-capi.h
|
||||
)
|
||||
|
||||
set(LIB
|
||||
|
||||
)
|
||||
|
||||
if(WITH_LIBMV)
|
||||
if(WIN32)
|
||||
add_definitions(-D_USE_MATH_DEFINES)
|
||||
endif()
|
||||
add_definitions(-DLIBMV_GFLAGS_NAMESPACE=${GFLAGS_NAMESPACE})
|
||||
|
||||
if(WITH_TBB)
|
||||
add_definitions(-DLIBMV_USE_TBB_THREADS)
|
||||
endif()
|
||||
|
||||
list(APPEND INC
|
||||
../guardedalloc
|
||||
)
|
||||
|
||||
list(APPEND INC_SYS
|
||||
)
|
||||
|
||||
list(APPEND LIB
|
||||
PRIVATE bf::dependencies::optional::ceres
|
||||
PUBLIC bf::dependencies::optional::tbb
|
||||
PRIVATE bf::dependencies::zlib
|
||||
|
||||
PRIVATE bf::dependencies::eigen
|
||||
PRIVATE bf::dependencies::gflags
|
||||
PRIVATE bf::dependencies::glog
|
||||
PRIVATE bf::dependencies::png
|
||||
)
|
||||
|
||||
add_definitions(
|
||||
-DWITH_LIBMV_GUARDED_ALLOC
|
||||
-DLIBMV_NO_FAST_DETECTOR=
|
||||
)
|
||||
|
||||
list(APPEND SRC
|
||||
intern/autotrack.cc
|
||||
intern/camera_intrinsics.cc
|
||||
intern/detector.cc
|
||||
intern/frame_accessor.cc
|
||||
intern/homography.cc
|
||||
intern/image.cc
|
||||
intern/logging.cc
|
||||
intern/reconstruction.cc
|
||||
intern/track_region.cc
|
||||
intern/tracks.cc
|
||||
intern/tracksN.cc
|
||||
libmv/autotrack/autotrack.cc
|
||||
libmv/autotrack/predict_tracks.cc
|
||||
libmv/autotrack/tracks.cc
|
||||
libmv/base/aligned_malloc.cc
|
||||
libmv/image/array_nd.cc
|
||||
libmv/image/convolve.cc
|
||||
libmv/multiview/conditioning.cc
|
||||
libmv/multiview/euclidean_resection.cc
|
||||
libmv/multiview/fundamental.cc
|
||||
libmv/multiview/homography.cc
|
||||
libmv/multiview/panography.cc
|
||||
libmv/multiview/panography_kernel.cc
|
||||
libmv/multiview/projection.cc
|
||||
libmv/multiview/triangulation.cc
|
||||
libmv/numeric/numeric.cc
|
||||
libmv/numeric/poly.cc
|
||||
libmv/simple_pipeline/bundle.cc
|
||||
libmv/simple_pipeline/camera_intrinsics.cc
|
||||
libmv/simple_pipeline/detect.cc
|
||||
libmv/simple_pipeline/distortion_models.cc
|
||||
libmv/simple_pipeline/initialize_reconstruction.cc
|
||||
libmv/simple_pipeline/intersect.cc
|
||||
libmv/simple_pipeline/keyframe_selection.cc
|
||||
libmv/simple_pipeline/modal_solver.cc
|
||||
libmv/simple_pipeline/packed_intrinsics.cc
|
||||
libmv/simple_pipeline/pipeline.cc
|
||||
libmv/simple_pipeline/reconstruction.cc
|
||||
libmv/simple_pipeline/reconstruction_scale.cc
|
||||
libmv/simple_pipeline/resect.cc
|
||||
libmv/simple_pipeline/tracks.cc
|
||||
libmv/tracking/brute_region_tracker.cc
|
||||
libmv/tracking/hybrid_region_tracker.cc
|
||||
libmv/tracking/klt_region_tracker.cc
|
||||
libmv/tracking/pyramid_region_tracker.cc
|
||||
libmv/tracking/retrack_region_tracker.cc
|
||||
libmv/tracking/track_region.cc
|
||||
libmv/tracking/trklt_region_tracker.cc
|
||||
|
||||
|
||||
intern/autotrack.h
|
||||
intern/camera_intrinsics.h
|
||||
intern/detector.h
|
||||
intern/frame_accessor.h
|
||||
intern/homography.h
|
||||
intern/image.h
|
||||
intern/logging.h
|
||||
intern/reconstruction.h
|
||||
intern/region.h
|
||||
intern/track_region.h
|
||||
intern/tracks.h
|
||||
intern/tracksN.h
|
||||
intern/utildefines.h
|
||||
libmv/autotrack/autotrack.h
|
||||
libmv/autotrack/callbacks.h
|
||||
libmv/autotrack/frame_accessor.h
|
||||
libmv/autotrack/marker.h
|
||||
libmv/autotrack/model.h
|
||||
libmv/autotrack/predict_tracks.h
|
||||
libmv/autotrack/quad.h
|
||||
libmv/autotrack/reconstruction.h
|
||||
libmv/autotrack/region.h
|
||||
libmv/autotrack/tracks.h
|
||||
libmv/base/aligned_malloc.h
|
||||
libmv/base/array.h
|
||||
libmv/base/id_generator.h
|
||||
libmv/base/map.h
|
||||
libmv/base/scoped_ptr.h
|
||||
libmv/base/vector.h
|
||||
libmv/base/vector_utils.h
|
||||
libmv/build/build_config.h
|
||||
libmv/image/array_nd.h
|
||||
libmv/image/convolve.h
|
||||
libmv/image/correlation.h
|
||||
libmv/image/image_converter.h
|
||||
libmv/image/image_drawing.h
|
||||
libmv/image/image.h
|
||||
libmv/image/sample.h
|
||||
libmv/image/tuple.h
|
||||
libmv/logging/logging.h
|
||||
libmv/multiview/conditioning.h
|
||||
libmv/multiview/euclidean_resection.h
|
||||
libmv/multiview/fundamental.h
|
||||
libmv/multiview/homography_error.h
|
||||
libmv/multiview/homography.h
|
||||
libmv/multiview/homography_parameterization.h
|
||||
libmv/multiview/nviewtriangulation.h
|
||||
libmv/multiview/panography.h
|
||||
libmv/multiview/panography_kernel.h
|
||||
libmv/multiview/projection.h
|
||||
libmv/multiview/resection.h
|
||||
libmv/multiview/triangulation.h
|
||||
libmv/multiview/two_view_kernel.h
|
||||
libmv/numeric/dogleg.h
|
||||
libmv/numeric/function_derivative.h
|
||||
libmv/numeric/levenberg_marquardt.h
|
||||
libmv/numeric/numeric.h
|
||||
libmv/numeric/poly.h
|
||||
libmv/simple_pipeline/bundle.h
|
||||
libmv/simple_pipeline/callbacks.h
|
||||
libmv/simple_pipeline/camera_intrinsics.h
|
||||
libmv/simple_pipeline/camera_intrinsics_impl.h
|
||||
libmv/simple_pipeline/detect.h
|
||||
libmv/simple_pipeline/distortion_models.h
|
||||
libmv/simple_pipeline/initialize_reconstruction.h
|
||||
libmv/simple_pipeline/intersect.h
|
||||
libmv/simple_pipeline/keyframe_selection.h
|
||||
libmv/simple_pipeline/modal_solver.h
|
||||
libmv/simple_pipeline/packed_intrinsics.h
|
||||
libmv/simple_pipeline/pipeline.h
|
||||
libmv/simple_pipeline/reconstruction.h
|
||||
libmv/simple_pipeline/reconstruction_scale.h
|
||||
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
|
||||
libmv/tracking/klt_region_tracker.h
|
||||
libmv/tracking/pyramid_region_tracker.h
|
||||
libmv/tracking/region_tracker.h
|
||||
libmv/tracking/retrack_region_tracker.h
|
||||
libmv/tracking/track_region.h
|
||||
libmv/tracking/trklt_region_tracker.h
|
||||
|
||||
third_party/msinttypes/inttypes.h
|
||||
third_party/msinttypes/stdint.h
|
||||
)
|
||||
|
||||
|
||||
if(WITH_GTESTS)
|
||||
blender_add_lib(libmv_test_dataset "./libmv/multiview/test_data_sets.cc" "${INC}" "${INC_SYS}" "${LIB}")
|
||||
|
||||
blender_add_test_executable("libmv_predict_tracks" "./libmv/autotrack/predict_tracks_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;${LIB}")
|
||||
blender_add_test_executable("libmv_tracks" "./libmv/autotrack/tracks_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;${LIB}")
|
||||
blender_add_test_executable("libmv_scoped_ptr" "./libmv/base/scoped_ptr_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;${LIB}")
|
||||
blender_add_test_executable("libmv_vector" "./libmv/base/vector_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;${LIB}")
|
||||
blender_add_test_executable("libmv_array_nd" "./libmv/image/array_nd_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;${LIB}")
|
||||
blender_add_test_executable("libmv_convolve" "./libmv/image/convolve_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;${LIB}")
|
||||
blender_add_test_executable("libmv_image" "./libmv/image/image_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;${LIB}")
|
||||
blender_add_test_executable("libmv_sample" "./libmv/image/sample_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;${LIB}")
|
||||
blender_add_test_executable("libmv_tuple" "./libmv/image/tuple_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;${LIB}")
|
||||
blender_add_test_executable("libmv_euclidean_resection" "./libmv/multiview/euclidean_resection_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;${LIB}")
|
||||
blender_add_test_executable("libmv_fundamental" "./libmv/multiview/fundamental_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;${LIB}")
|
||||
blender_add_test_executable("libmv_homography" "./libmv/multiview/homography_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;${LIB}")
|
||||
blender_add_test_executable("libmv_nviewtriangulation" "./libmv/multiview/nviewtriangulation_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;${LIB}")
|
||||
blender_add_test_executable("libmv_panography" "./libmv/multiview/panography_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;${LIB}")
|
||||
blender_add_test_executable("libmv_projection" "./libmv/multiview/projection_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;${LIB}")
|
||||
blender_add_test_executable("libmv_resection" "./libmv/multiview/resection_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;${LIB}")
|
||||
blender_add_test_executable("libmv_triangulation" "./libmv/multiview/triangulation_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;${LIB}")
|
||||
blender_add_test_executable("libmv_dogleg" "./libmv/numeric/dogleg_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;${LIB}")
|
||||
blender_add_test_executable("libmv_function_derivative" "./libmv/numeric/function_derivative_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;${LIB}")
|
||||
blender_add_test_executable("libmv_levenberg_marquardt" "./libmv/numeric/levenberg_marquardt_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;${LIB}")
|
||||
blender_add_test_executable("libmv_numeric" "./libmv/numeric/numeric_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;${LIB}")
|
||||
blender_add_test_executable("libmv_poly" "./libmv/numeric/poly_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;${LIB}")
|
||||
blender_add_test_executable("libmv_camera_intrinsics" "./libmv/simple_pipeline/camera_intrinsics_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;${LIB}")
|
||||
blender_add_test_executable("libmv_detect" "./libmv/simple_pipeline/detect_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;${LIB}")
|
||||
blender_add_test_executable("libmv_intersect" "./libmv/simple_pipeline/intersect_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;${LIB}")
|
||||
blender_add_test_executable("libmv_keyframe_selection" "./libmv/simple_pipeline/keyframe_selection_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;${LIB}")
|
||||
blender_add_test_executable("libmv_modal_solver" "./libmv/simple_pipeline/modal_solver_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;${LIB}")
|
||||
blender_add_test_executable("libmv_resect" "./libmv/simple_pipeline/resect_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;${LIB}")
|
||||
blender_add_test_executable("libmv_brute_region_tracker" "./libmv/tracking/brute_region_tracker_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;${LIB}")
|
||||
blender_add_test_executable("libmv_klt_region_tracker" "./libmv/tracking/klt_region_tracker_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;${LIB}")
|
||||
blender_add_test_executable("libmv_pyramid_region_tracker" "./libmv/tracking/pyramid_region_tracker_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;${LIB}")
|
||||
blender_add_test_executable("libmv_parallel_for" "./libmv/threading/parallel_for_test.cc" "${INC}" "${INC_SYS}" "libmv_test_dataset;bf_intern_libmv;${LIB}")
|
||||
endif()
|
||||
else()
|
||||
list(APPEND SRC
|
||||
intern/stub.cc
|
||||
)
|
||||
endif()
|
||||
|
||||
blender_add_lib(bf_intern_libmv "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
|
||||
556
blender-5.2.0/intern/libmv/ChangeLog
Normal file
556
blender-5.2.0/intern/libmv/ChangeLog
Normal file
@@ -0,0 +1,556 @@
|
||||
commit f701b2b9fbc641b3252b3513239eeb14477ed5e1
|
||||
Author: Lazydodo <github@lazydodo.com>
|
||||
Date: Wed Aug 21 10:38:33 2019 -0600
|
||||
|
||||
Cleanup: Fix build error with MSVC
|
||||
|
||||
Previously eigens internal include order somehow implicitly provided
|
||||
M_PI and friends. The recent eigen version bump broke this implicit
|
||||
behaviour, better to be explicit that we need the math defines for MSVC.
|
||||
|
||||
commit 5b2892f5a3cb6a7a2300f1c32e93d90e22302286
|
||||
Author: Sergey Sharybin <sergey@blender.org>
|
||||
Date: Mon Mar 15 14:58:13 2021 +0100
|
||||
|
||||
Cleanup: Spelling in Libmv comments
|
||||
|
||||
commit 54cdd2fe5cad29df83cb4d3c3ef1e02e06b022a7
|
||||
Author: Sergey Sharybin <sergey@blender.org>
|
||||
Date: Thu Oct 21 12:09:30 2021 +0200
|
||||
|
||||
Ensure clang-format is disabled for 3rd party libraries
|
||||
|
||||
commit 517d51654d6a4cf790a340b657cc0f7dc00f3158
|
||||
Author: Campbell Barton <ideasman42@gmail.com>
|
||||
Date: Tue Jul 13 21:58:45 2021 +1000
|
||||
|
||||
Fix x/y mismatch in retract region tracker
|
||||
|
||||
Correct X/Y mismatch in RetrackRegionTracker.
|
||||
|
||||
NOTE: This isn't used at the moment.
|
||||
|
||||
Reviewed By: sergey
|
||||
|
||||
Ref D11895
|
||||
|
||||
commit cfcfc803cf599c3bd7bbfa3bac7d5e9c8bca284b
|
||||
Author: Jesse Yurkovich <jesse.y@gmail.com>
|
||||
Date: Mon Jul 12 21:01:18 2021 -0700
|
||||
|
||||
Cleanup: Use correct _WIN32/64 defines for MSVC
|
||||
|
||||
Docs: https://docs.microsoft.com/en-us/cpp/preprocessor/predefined-macros
|
||||
|
||||
Differential Revision: https://developer.blender.org/D11460
|
||||
|
||||
commit ad8dfd41958b0e4cb90c7c4e5cc20f93c0832e73
|
||||
Author: Campbell Barton <ideasman42@gmail.com>
|
||||
Date: Thu Jun 24 15:56:58 2021 +1000
|
||||
|
||||
Cleanup: comment blocks, trailing space in comments
|
||||
|
||||
commit 9a6cc5daa6c38f33ce2385fe489c07d97586d452
|
||||
Author: Campbell Barton <ideasman42@gmail.com>
|
||||
Date: Sun Jun 13 15:11:40 2021 +1000
|
||||
|
||||
Fix new[]/delete[] mismatch
|
||||
|
||||
commit 5591d12928df74ed3517b5e61eeda0b64e6ade4f
|
||||
Author: Sergey Sharybin <sergey@blender.org>
|
||||
Date: Mon Mar 22 15:16:07 2021 +0100
|
||||
|
||||
Fix T86591: Tracking backwards is slower
|
||||
|
||||
The root of the issue was caused by the PredictMarkerPosition()
|
||||
always returning false when tracking backwards. This was making
|
||||
it so tracker always had to run brute initialization, which is
|
||||
an expensive operation.
|
||||
|
||||
From own timing here:
|
||||
|
||||
- Tracking forward takes 0.667637 seconds
|
||||
- Tracking backward used to take 2.591856 seconds
|
||||
- Tracking backward now takes 0.827724 seconds
|
||||
|
||||
This is a very nice speedup, although the tracking backwards is
|
||||
still somewhat slower. Will be investigated further as part of
|
||||
a regular development.
|
||||
|
||||
commit ad9546d2319b0db3e1ccc41c4f84899729d1ad1e
|
||||
Author: Sergey Sharybin <sergey@blender.org>
|
||||
Date: Mon Mar 15 15:48:15 2021 +0100
|
||||
|
||||
Fix T86262: Tracking backwards fails after gap in track
|
||||
|
||||
The issue was caused by a prediction algorithm detecting tracking the
|
||||
wrong way. Solved by passing tracking direction explicitly, so that
|
||||
prediction will always happen correctly regardless of the state of the
|
||||
Tracks context.
|
||||
|
||||
commit 0773a8d6dfe1bf997b8e42d61d136a65b67fde88
|
||||
Author: Sergey Sharybin <sergey@blender.org>
|
||||
Date: Thu Oct 21 11:18:10 2021 +0200
|
||||
|
||||
Cleanup: clang-format
|
||||
|
||||
Is based on Google style which was used in the Libmv project before,
|
||||
but is now consistently applied for the sources of the library itself
|
||||
and to C-API. With some time C-API will likely be removed, and it
|
||||
makes it easier to make it follow Libmv style, hence the diversion
|
||||
from Blender's style.
|
||||
|
||||
There are quite some exceptions (clang-format off) in the code around
|
||||
Eigen matrix initialization. It is rather annoying, and there could be
|
||||
some neat way to make initialization readable without such exception.
|
||||
|
||||
Could be some places where loss of readability in matrix initialization
|
||||
got lost as the change is quite big. If this has happened it is easier
|
||||
to address readability once actually working on the code.
|
||||
|
||||
This change allowed to spot some missing header guards, so that's nice.
|
||||
|
||||
Doing it in bundled version, as the upstream library needs to have some
|
||||
of the recent development ported over from bundle to upstream.
|
||||
|
||||
There should be no functional changes.
|
||||
|
||||
commit c4de0ccd5aede3bb90ac6ad1039a83c2260fbefd
|
||||
Author: Sergey Sharybin <sergey@blender.org>
|
||||
Date: Tue Feb 23 16:43:01 2021 +0100
|
||||
|
||||
Avoid use of LOG(INFO) in solver
|
||||
|
||||
Usage of LOG(INFO) actually went against own guidelines in the
|
||||
logging.h: the INFO is for messages which are to be printed
|
||||
regardless of debug/verbosity settings.
|
||||
|
||||
commit 94d925131b08e23cea7fdf735923e24b78a8c7fd
|
||||
Author: Campbell Barton <ideasman42@gmail.com>
|
||||
Date: Fri Feb 5 16:23:34 2021 +1100
|
||||
|
||||
Cleanup: correct spelling in comments
|
||||
|
||||
commit db0c8dbea19d684899dffcb46a2e79c12dc91e6b
|
||||
Author: Sergey Sharybin <sergey@blender.org>
|
||||
Date: Tue Dec 1 14:52:08 2020 +0100
|
||||
|
||||
Tweak default logging verbosity level
|
||||
|
||||
Log to verbosity level 1 rather than INFO severity.
|
||||
|
||||
Avoids a lot of overhead coming from construction of the INFO stream
|
||||
and improves performance and threadability of code which uses logging.
|
||||
|
||||
This makes tracking of 250 frames of a track of default settings to
|
||||
drop down from 0.6sec to 0.4sec.
|
||||
|
||||
commit 8f2c13edf0d3b0c8a2ad90d6ae13f11aed457709
|
||||
Author: Sergey Sharybin <sergey@blender.org>
|
||||
Date: Tue Dec 1 14:46:35 2020 +0100
|
||||
|
||||
Cleanup, remove unused logging macros
|
||||
|
||||
Unused and was not entirely happy with such short abbreviations.
|
||||
|
||||
commit 1dc2ab9847ce6cc84ab6c8e3e8613687480d735d
|
||||
Author: Sergey Sharybin <sergey@blender.org>
|
||||
Date: Fri Nov 27 15:58:55 2020 +0100
|
||||
|
||||
Add threading primitives
|
||||
|
||||
Allows to use mutex, scoped_lock, and conditional_variable from within
|
||||
the libmv namespace.
|
||||
|
||||
Implementation is coming from C++11. Other configurations are easy to
|
||||
implement, but currently C++11 is the way to go.
|
||||
|
||||
commit 6531b66fd3bf4b5ee112b904b390d164a03628c5
|
||||
Author: Sergey Sharybin <sergey@blender.org>
|
||||
Date: Fri Nov 27 15:43:44 2020 +0100
|
||||
|
||||
Add build configuration header
|
||||
|
||||
Allows to easily access build platform information, such as bitness,
|
||||
compiler, supported C++ version and so on.
|
||||
|
||||
commit 632c356375e4ac07da43b346384fdf67e7916426
|
||||
Author: Campbell Barton <ideasman42@gmail.com>
|
||||
Date: Fri Nov 6 14:35:38 2020 +1100
|
||||
|
||||
Cleanup: doxygen comments
|
||||
|
||||
commit c9479472ce8d63b1d6e4c9c1b072b975479cd4c0
|
||||
Author: Campbell Barton <ideasman42@gmail.com>
|
||||
Date: Fri Nov 6 11:25:27 2020 +1100
|
||||
|
||||
Cleanup: follow our code style for float literals
|
||||
|
||||
commit 6455055b265cddbf604523653b01c9d7adbc23d5
|
||||
Author: Ivan Perevala <ivpe>
|
||||
Date: Thu Oct 29 10:19:06 2020 +0100
|
||||
|
||||
Fix clang inconsistent-missing-override warnings.
|
||||
|
||||
Reviewed By: sergey, ankitm
|
||||
|
||||
Differential Revision: https://developer.blender.org/D9377
|
||||
|
||||
commit 020fc13a5336c872c3251e45d28cc02584094f33
|
||||
Author: Sergey Sharybin <sergey@blender.org>
|
||||
Date: Wed Oct 21 10:53:13 2020 +0200
|
||||
|
||||
Simplify configuration of intrinsics to refine
|
||||
|
||||
Previously, only predefined and limited set of intrinsics combinations
|
||||
could have been refined. This was caused by a bundle adjustment library
|
||||
used in the early days of the solver.
|
||||
|
||||
Now it is possible to fully customize which intrinsics are to be refined
|
||||
during camera solving. Internally solver supports per-parameter settings
|
||||
but in the interface they are grouped as following:
|
||||
|
||||
* Focal length
|
||||
* Optical center
|
||||
* Radial distortion coefficients (which includes k1, k2, k3, k4)
|
||||
* Tangential distortion coefficients (which includes p1, p2)
|
||||
|
||||
Differential Revision: https://developer.blender.org/D9294
|
||||
|
||||
commit 5ef3c2c41854a2e634ba692e9b63194aa74b928d
|
||||
Author: Sergey Sharybin <sergey@blender.org>
|
||||
Date: Wed Oct 21 10:48:13 2020 +0200
|
||||
|
||||
Fix typo in packed intrinsics
|
||||
|
||||
Was using doing an implicit cast of floating point value to boolean.
|
||||
Was not noticed before because the boolean value was never never used.
|
||||
|
||||
commit 648388878525aa585bdbbed2e457186b6483bd5e
|
||||
Author: Sergey Sharybin <sergey@blender.org>
|
||||
Date: Tue Oct 13 11:34:05 2020 +0200
|
||||
|
||||
Refactor camera intrinsics parameter block
|
||||
|
||||
Use the newly introduced packed intrinsics, which allows to remove
|
||||
code which was initializing parameters block based on distortion
|
||||
model type.
|
||||
|
||||
Now such initialization is done by a particular implementation of
|
||||
a distortion model.
|
||||
|
||||
Differential Revision: https://developer.blender.org/D9192
|
||||
|
||||
commit 60780e30a8aec37f53d4e952375ffc8d720341c6
|
||||
Author: Sergey Sharybin <sergey@blender.org>
|
||||
Date: Tue Oct 13 11:32:35 2020 +0200
|
||||
|
||||
Add generic class for packed intrinsics
|
||||
|
||||
This is a common class which can be used in all sort of minimization
|
||||
problems which needs camera intrinsics as a parameter block.
|
||||
|
||||
Currently unused, but will replace a lot of hard-coded logic in the
|
||||
bundle adjustment code.
|
||||
|
||||
commit 409924c76be27fec3b619d79379df76cb8de6a9a
|
||||
Author: Sergey Sharybin <sergey@blender.org>
|
||||
Date: Tue Oct 13 11:25:34 2020 +0200
|
||||
|
||||
Add array<type, size> to libmv namespace
|
||||
|
||||
commit 6411c7fed3c30dce05b9b2df239b7f4cf91cdecd
|
||||
Author: Sebastian Parborg <darkdefende@gmail.com>
|
||||
Date: Tue Oct 20 14:45:54 2020 +0200
|
||||
|
||||
Fix test on windows
|
||||
|
||||
There is no point in testing std::vector capacity as it can differ
|
||||
between std implementations.
|
||||
|
||||
commit 242d31ff4ed78a6f36ac29f139b03abc4f32f4f5
|
||||
Author: Harley Acheson <harley.acheson@gmail.com>
|
||||
Date: Mon Oct 19 08:51:50 2020 -0700
|
||||
|
||||
Spelling: Miscellaneous
|
||||
|
||||
Corrects 34 miscellaneous misspelled words.
|
||||
|
||||
Differential Revision: https://developer.blender.org/D9248
|
||||
|
||||
Reviewed by Campbell Barton
|
||||
|
||||
commit 91cd0310d6762fdcc7253303bec7f0453cf1f18f
|
||||
Author: Harley Acheson <harley.acheson@gmail.com>
|
||||
Date: Mon Oct 19 08:12:33 2020 -0700
|
||||
|
||||
Spelling: It's Versus Its
|
||||
|
||||
Corrects incorrect usage of contraction for 'it is', when possessive 'its' was required.
|
||||
|
||||
Differential Revision: https://developer.blender.org/D9250
|
||||
|
||||
Reviewed by Campbell Barton
|
||||
|
||||
commit 788ed5fa8ace0df951fe8f03e6510187ed694044
|
||||
Author: Sebastian Parborg <darkdefende@gmail.com>
|
||||
Date: Mon Oct 19 13:03:06 2020 +0200
|
||||
|
||||
Fix alignment issues when compiling with AVX support
|
||||
|
||||
There would be eigen alignment issues with the custom libmv vector
|
||||
class when compiling with AVX optimizations. This would lead to
|
||||
segfaults.
|
||||
|
||||
Simply use the std::vector base class as suggested by the old TODO in
|
||||
the class header.
|
||||
|
||||
Reviewed By: Sergey
|
||||
|
||||
Differential Revision: http://developer.blender.org/D8968
|
||||
|
||||
commit 21a114476eaafec4b25c0ea19eaccdd9e870160d
|
||||
Author: Sergey Sharybin <sergey@blender.org>
|
||||
Date: Mon Oct 12 14:59:18 2020 +0200
|
||||
|
||||
Remove array access from camera intrinsics
|
||||
|
||||
That was a suboptimal decision from back in the days, which ended up
|
||||
being problematic. It is no longer used, so remove it from API making
|
||||
it so new code does not depend on this weak concept.
|
||||
|
||||
commit b6a579fd88de5e13c812310a0416e698648578c4
|
||||
Author: Sergey Sharybin <sergey@blender.org>
|
||||
Date: Mon Oct 12 12:17:55 2020 +0200
|
||||
|
||||
Fix wrong packing order of intrinsics for BA step
|
||||
|
||||
The order got broken when Brown distortion model has been added.
|
||||
Made it so the indexing of parameters is strictly defined in the
|
||||
parameter block, matching how parameters are used in the cost
|
||||
function.
|
||||
|
||||
There is some duplication going on accessing parameters. This can
|
||||
be refactored in the future, by either moving common parts packing
|
||||
and cost function to an utility function in bundle.cc.
|
||||
Alternatively, can introduce a public PackedIntrinsics class which
|
||||
will contain a continuous block of parameters, and each of the
|
||||
camera models will have API to be initialized from packed form and
|
||||
to create this packed form.
|
||||
|
||||
The benefit of this approach over alternative solutions previously
|
||||
made in the master branch or suggested in D9116 is that the specific
|
||||
implementation of BA does not dictate the way how public classes need
|
||||
to be organized. It is API which needs to define how implementation
|
||||
goes, not the other way around.
|
||||
|
||||
Thanks Bastien and Ivan for the investigation!
|
||||
|
||||
commit 595d4a45db2f1d68bc6091b6fad3580411a10e40
|
||||
Author: Sergey Sharybin <sergey@blender.org>
|
||||
Date: Mon Oct 12 10:46:31 2020 +0200
|
||||
|
||||
Fix memory leak in modal solver
|
||||
|
||||
The leak was happening when problem did not have any parameters blocks
|
||||
defined. This happens, for example, if there are no 3D points at all,
|
||||
or when all markers are set to 0 weight.
|
||||
|
||||
Was noticeable in libmv_modal_solver_test when building with LSAN
|
||||
enabled.
|
||||
|
||||
commit b6ecdc497c00e5f9a0a6dffe1c27051c8df8482a
|
||||
Author: Sergey Sharybin <sergey@blender.org>
|
||||
Date: Mon Oct 12 10:44:57 2020 +0200
|
||||
|
||||
Cleanup, spelling in function name
|
||||
|
||||
Is a local function, not affecting API.
|
||||
|
||||
commit 2a712777076c076fcf8aae9b9657089d81fd129f
|
||||
Author: Ivan Perevala <ivpe>
|
||||
Date: Wed Sep 30 15:12:14 2020 +0200
|
||||
|
||||
Implement Brown-Conrady distortion model
|
||||
|
||||
Implemented Brown-Conrady lens distortion model with 4 radial and
|
||||
2 tangential coefficients to improve compatibility with other software,
|
||||
such as Agisoft Photoscan/Metashapes, 3DF Zephir, RealityCapture,
|
||||
Bentley ContextCapture, Alisevision Meshroom(opensource).
|
||||
|
||||
Also older programs: Bundler, CPMVS.
|
||||
In general terms, most photogrammetric software.
|
||||
|
||||
The new model is available under the distortion model menu in Lens
|
||||
settings.
|
||||
|
||||
For tests and demos check the original patch.
|
||||
|
||||
Reviewed By: sergey
|
||||
|
||||
Differential Revision: https://developer.blender.org/D9037
|
||||
|
||||
commit e4e2b8c382f9bb2490318cd621a438ccb4dd7327
|
||||
Author: Ivan <vanyOk>
|
||||
Date: Mon Sep 28 09:57:03 2020 +0200
|
||||
|
||||
Fix NukeCameraIntrinsics copy constructor
|
||||
|
||||
Copy the appropriate parameter
|
||||
|
||||
Reviewed By: sergey
|
||||
|
||||
Differential Revision: https://developer.blender.org/D9014
|
||||
|
||||
commit 1e5deb138ad1ef79204b118f4dc741f96bd0e650
|
||||
Author: Johan Walles <walles>
|
||||
Date: Tue Jul 7 11:09:31 2020 +0200
|
||||
|
||||
Add units to motion tracking solve errors
|
||||
|
||||
The unit being "pixels".
|
||||
|
||||
Before this change the solve errors were unitless in the UI.
|
||||
|
||||
With this change in place, the UI is now clear on that the unit of the
|
||||
reprojection errors is pixels (px).
|
||||
|
||||
Differential Revision: https://developer.blender.org/D8000
|
||||
|
||||
commit a8a5a701f2839a97af51825d98726e7eb7e6eb1d
|
||||
Author: Campbell Barton <ideasman42@gmail.com>
|
||||
Date: Wed Jul 1 13:12:24 2020 +1000
|
||||
|
||||
Cleanup: spelling
|
||||
|
||||
commit fd83866975d5d8163d9bb71d645239562cb329b5
|
||||
Author: Sergey Sharybin <sergey.vfx@gmail.com>
|
||||
Date: Thu Jun 18 10:12:01 2020 +0200
|
||||
|
||||
Update Ceres to the latest upstream version
|
||||
|
||||
Using latest master because of various compilation error fixes.
|
||||
|
||||
Brings a lot of recent development. From most interesting parts:
|
||||
|
||||
- New threading model.
|
||||
- Tiny solver.
|
||||
- Compatibility with C++17.
|
||||
|
||||
commit 307e9a945a320a1b7d9af210c2da1cfbb6439ec5
|
||||
Author: Sergey Sharybin <sergey.vfx@gmail.com>
|
||||
Date: Fri May 15 14:54:30 2020 +0200
|
||||
|
||||
Fix crash solving when having negative frames
|
||||
|
||||
Don't use linear array with frame as an index since it has the
|
||||
following disadvantages:
|
||||
|
||||
- Requires every application to take care of frame remapping, which
|
||||
could be way more annoying than it sounds.
|
||||
|
||||
- Inefficient from memory point of view when solving part of a footage
|
||||
which is closer to the end of frame range.
|
||||
|
||||
Using map technically is slower from performance point of view, but
|
||||
could not feel any difference as the actual computation is way more
|
||||
complex than access of camera on individual frames.
|
||||
|
||||
Solves crash aspect of T72009
|
||||
|
||||
commit 730eec3e0a382b23648d9000e07218519e1223d1
|
||||
Author: Sergey Sharybin <sergey.vfx@gmail.com>
|
||||
Date: Fri May 15 11:08:18 2020 +0200
|
||||
|
||||
Add map utility
|
||||
|
||||
commit 0d3f5d94474553c51a1e5d830521fca4ee82aa54
|
||||
Author: Sergey Sharybin <sergey.vfx@gmail.com>
|
||||
Date: Fri May 15 11:05:07 2020 +0200
|
||||
|
||||
Cleanup, spelling
|
||||
|
||||
commit d26503ab5a9d2faef0f388695d6bb1c46ffff4eb
|
||||
Author: Aaron Carlisle <carlisle.b3d@gmail.com>
|
||||
Date: Thu May 7 23:42:22 2020 -0400
|
||||
|
||||
Cleanup: Doxygen: fix markup warnings for links
|
||||
|
||||
commit 9e8e94e8281abb6edb5e81afe4365743994800d2
|
||||
Author: Sergey Sharybin <sergey.vfx@gmail.com>
|
||||
Date: Mon Apr 20 17:33:03 2020 +0200
|
||||
|
||||
Implement Nuke/Natron distortion model
|
||||
|
||||
Neither Nuke nor Natron support OpenCV's radial distortion model
|
||||
which makes it impossible to have any kind of interoperability.
|
||||
|
||||
The new model is available under the distortion model menu in Lens
|
||||
settings.
|
||||
|
||||
Differential Revision: https://developer.blender.org/D7484
|
||||
|
||||
commit 1dc83e7545fc832180322f492245933fb343f158
|
||||
Author: Sergey Sharybin <sergey.vfx@gmail.com>
|
||||
Date: Tue Apr 21 16:41:23 2020 +0200
|
||||
|
||||
Cleanup, naming
|
||||
|
||||
Initial bundle adjustment only supported OpenCV's radial distortion
|
||||
model, so the cost functor was called after it.
|
||||
|
||||
Nowadays it supports more than this single model, so naming was a bit
|
||||
wrong and misleading.
|
||||
|
||||
commit b6df71dd5e5a49b3459c522c10ae98e8795e69b5
|
||||
Author: Sergey Sharybin <sergey.vfx@gmail.com>
|
||||
Date: Tue Apr 21 12:25:45 2020 +0200
|
||||
|
||||
Cleanup, spelling and naming in bundle adjustment
|
||||
|
||||
Just more things which were discovered to be annoying on unclear when
|
||||
adding more features to this code.
|
||||
|
||||
commit a4bbe3a10a5b6b23acd261b1e6c772a6f6672c00
|
||||
Author: Sergey Sharybin <sergey.vfx@gmail.com>
|
||||
Date: Mon Apr 20 17:26:45 2020 +0200
|
||||
|
||||
Cleanup, spelling in comment
|
||||
|
||||
commit 4219e9d22bad012e9e64b83a5e26d1d4eff3fcc6
|
||||
Author: Sergey Sharybin <sergey.vfx@gmail.com>
|
||||
Date: Mon Apr 20 12:44:07 2020 +0200
|
||||
|
||||
De-duplicate creation of residual block
|
||||
|
||||
Allows to centralize logic which is needed to check which cost functor
|
||||
to use for the specific intrinsics.
|
||||
|
||||
commit 9637ebc18b2bac794c8f11b1c2c092a3a9c3e6d2
|
||||
Author: Sergey Sharybin <sergey.vfx@gmail.com>
|
||||
Date: Mon Apr 20 11:41:01 2020 +0200
|
||||
|
||||
Cleanup reprojection cost function
|
||||
|
||||
Make it smaller and more clear how and what it operates on.
|
||||
|
||||
commit 1efc975a5457cfee6baf41df67afb3e43834d57f
|
||||
Author: Sergey Sharybin <sergey.vfx@gmail.com>
|
||||
Date: Mon Apr 20 11:19:47 2020 +0200
|
||||
|
||||
Pass entire camera intrinsics to reprojection error functor
|
||||
|
||||
Currently no functional changes, but allows to have access to some
|
||||
invariant settings of camera intrinsics such as image dimensions.
|
||||
|
||||
commit 0f54f2b305f59fc99764b9c85fe28f35f10faa86
|
||||
Author: Sergey Sharybin <sergey.vfx@gmail.com>
|
||||
Date: Fri Apr 17 17:34:19 2020 +0200
|
||||
|
||||
Cleanup, rephrase comment
|
||||
|
||||
commit 229912b0e1746145c4ab710f8609ce90f690a8e2
|
||||
Author: Sergey Sharybin <sergey.vfx@gmail.com>
|
||||
Date: Fri Apr 17 17:29:04 2020 +0200
|
||||
|
||||
Cleanup, fix indentation
|
||||
194
blender-5.2.0/intern/libmv/bundle.sh
Executable file
194
blender-5.2.0/intern/libmv/bundle.sh
Executable file
@@ -0,0 +1,194 @@
|
||||
#!/bin/sh
|
||||
# SPDX-FileCopyrightText: 2011-2023 Blender Authors
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
if [ "x$1" = "x--i-really-know-what-im-doing" ] ; then
|
||||
echo Proceeding as requested by command line ...
|
||||
else
|
||||
echo "*** Please run again with --i-really-know-what-im-doing ..."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
BRANCH="main"
|
||||
|
||||
# repo="https://projects.blender.org/blender/libmv.git"
|
||||
repo="/home/sergey/Developer/libmv"
|
||||
tmp=`mktemp -d`
|
||||
|
||||
git clone -b $BRANCH $repo $tmp/libmv
|
||||
|
||||
git --git-dir $tmp/libmv/.git --work-tree $tmp/libmv log -n 50 > ChangeLog
|
||||
|
||||
find libmv -type f -exec rm -rf {} \;
|
||||
find third_party -type f -exec rm -rf {} \;
|
||||
|
||||
cat "files.txt" | while read f; do
|
||||
mkdir -p `dirname $f`
|
||||
cp $tmp/libmv/src/$f $f
|
||||
done
|
||||
|
||||
rm -rf $tmp
|
||||
|
||||
sources=`find ./libmv -type f -iname '*.cc' -or -iname '*.cpp' -or -iname '*.c' | grep -v _test.cc | grep -v test_data_sets | sed -r 's/^\.\// /' | sort -d`
|
||||
headers=`find ./libmv -type f -iname '*.h' | grep -v test_data_sets | sed -r 's/^\.\// /' | sort -d`
|
||||
|
||||
third_sources=`find ./third_party -type f -iname '*.cc' -or -iname '*.cpp' -or -iname '*.c' | sed -r 's/^\.\// /' | sort -d`
|
||||
third_headers=`find ./third_party -type f -iname '*.h' | sed -r 's/^\.\// /' | sort -d`
|
||||
|
||||
tests=`find ./libmv -type f -iname '*_test.cc' | sort -d | awk ' { name=gensub(".*/([A-Za-z_]+)_test.cc", "\\\\1", "g", $1); printf(" blender_add_test_executable(\"libmv_%s\" \"%s\" \"\${INC}\" \"\${INC_SYS}\" \"libmv_test_dataset;bf_intern_libmv;extern_ceres\")\n", name, $1) } '`
|
||||
|
||||
src_dir=`find ./libmv -type f -iname '*.cc' -exec dirname {} \; -or -iname '*.cpp' -exec dirname {} \; -or -iname '*.c' -exec dirname {} \; | sed -r 's/^\.\// /' | sort -d | uniq`
|
||||
src_third_dir=`find ./third_party -type f -iname '*.cc' -exec dirname {} \; -or -iname '*.cpp' -exec dirname {} \; -or -iname '*.c' -exec dirname {} \; | sed -r 's/^\.\// /' | sort -d | uniq`
|
||||
src=""
|
||||
win_src=""
|
||||
for x in $src_dir $src_third_dir; do
|
||||
t=""
|
||||
|
||||
if stat $x/*.cpp > /dev/null 2>&1; then
|
||||
t=" src += env.Glob('`echo $x'/*.cpp'`')"
|
||||
fi
|
||||
|
||||
if stat $x/*.c > /dev/null 2>&1; then
|
||||
if [ -z "$t" ]; then
|
||||
t=" src += env.Glob('`echo $x'/*.c'`')"
|
||||
else
|
||||
t="$t + env.Glob('`echo $x'/*.c'`')"
|
||||
fi
|
||||
fi
|
||||
|
||||
if stat $x/*.cc > /dev/null 2>&1; then
|
||||
if [ -z "$t" ]; then
|
||||
t=" src += env.Glob('`echo $x'/*.cc'`')"
|
||||
else
|
||||
t="$t + env.Glob('`echo $x'/*.cc'`')"
|
||||
fi
|
||||
fi
|
||||
|
||||
if test `echo $x | grep -c "windows\|gflags" ` -eq 0; then
|
||||
if [ -z "$src" ]; then
|
||||
src=$t
|
||||
else
|
||||
src=`echo "$src\n$t"`
|
||||
fi
|
||||
else
|
||||
if [ -z "$win_src" ]; then
|
||||
win_src=`echo " $t"`
|
||||
else
|
||||
win_src=`echo "$win_src\n $t"`
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
cat > CMakeLists.txt << EOF
|
||||
# SPDX-FileCopyrightText: 2011 Blender Authors
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
# NOTE: This file is automatically generated by bundle.sh script
|
||||
# If you're doing changes in this file, please update template
|
||||
# in that script too
|
||||
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
# Suppress these warnings until the issue is addressed as this adds noise.
|
||||
# Use `-Wno-deprecated-copy` as this may have been enabled via `-Wextra`.
|
||||
add_cxx_flag_per_config("-Wno-deprecated-copy")
|
||||
endif()
|
||||
|
||||
set(INC
|
||||
.
|
||||
)
|
||||
|
||||
set(INC_SYS
|
||||
)
|
||||
|
||||
set(SRC
|
||||
libmv-capi.h
|
||||
)
|
||||
|
||||
set(LIB
|
||||
|
||||
)
|
||||
|
||||
if(WITH_LIBMV)
|
||||
if(WIN32)
|
||||
add_definitions(-D_USE_MATH_DEFINES)
|
||||
endif()
|
||||
add_definitions(\${GFLAGS_DEFINES})
|
||||
add_definitions(\${GLOG_DEFINES})
|
||||
add_definitions(-DLIBMV_GFLAGS_NAMESPACE=\${GFLAGS_NAMESPACE})
|
||||
|
||||
list(APPEND INC
|
||||
\${GFLAGS_INCLUDE_DIRS}
|
||||
\${GLOG_INCLUDE_DIRS}
|
||||
../guardedalloc
|
||||
)
|
||||
|
||||
list(APPEND INC_SYS
|
||||
\${EIGEN3_INCLUDE_DIRS}
|
||||
\${PNG_INCLUDE_DIRS}
|
||||
\${ZLIB_INCLUDE_DIRS}
|
||||
../../extern/ceres/include
|
||||
../../extern/ceres/config
|
||||
)
|
||||
|
||||
list(APPEND LIB
|
||||
extern_ceres
|
||||
|
||||
\${GLOG_LIBRARIES}
|
||||
\${GFLAGS_LIBRARIES}
|
||||
\${PNG_LIBRARIES}
|
||||
)
|
||||
|
||||
add_definitions(
|
||||
-DWITH_LIBMV_GUARDED_ALLOC
|
||||
-DLIBMV_NO_FAST_DETECTOR=
|
||||
)
|
||||
|
||||
list(APPEND SRC
|
||||
intern/autotrack.cc
|
||||
intern/camera_intrinsics.cc
|
||||
intern/detector.cc
|
||||
intern/frame_accessor.cc
|
||||
intern/homography.cc
|
||||
intern/image.cc
|
||||
intern/logging.cc
|
||||
intern/reconstruction.cc
|
||||
intern/track_region.cc
|
||||
intern/tracks.cc
|
||||
intern/tracksN.cc
|
||||
${sources}
|
||||
${third_sources}
|
||||
|
||||
intern/autotrack.h
|
||||
intern/camera_intrinsics.h
|
||||
intern/detector.h
|
||||
intern/frame_accessor.h
|
||||
intern/homography.h
|
||||
intern/image.h
|
||||
intern/logging.h
|
||||
intern/reconstruction.h
|
||||
intern/region.h
|
||||
intern/track_region.h
|
||||
intern/tracks.h
|
||||
intern/tracksN.h
|
||||
intern/utildefines.h
|
||||
${headers}
|
||||
|
||||
${third_headers}
|
||||
)
|
||||
|
||||
|
||||
if(WITH_GTESTS)
|
||||
blender_add_lib(libmv_test_dataset "./libmv/multiview/test_data_sets.cc" "\${INC}" "\${INC_SYS}" "")
|
||||
|
||||
${tests}
|
||||
endif()
|
||||
else()
|
||||
list(APPEND SRC
|
||||
intern/stub.cc
|
||||
)
|
||||
endif()
|
||||
|
||||
blender_add_lib(bf_intern_libmv "\${SRC}" "\${INC}" "\${INC_SYS}" "\${LIB}")
|
||||
EOF
|
||||
145
blender-5.2.0/intern/libmv/files.txt
Normal file
145
blender-5.2.0/intern/libmv/files.txt
Normal file
@@ -0,0 +1,145 @@
|
||||
libmv/autotrack/autotrack.cc
|
||||
libmv/autotrack/autotrack.h
|
||||
libmv/autotrack/callbacks.h
|
||||
libmv/autotrack/frame_accessor.h
|
||||
libmv/autotrack/marker.h
|
||||
libmv/autotrack/model.h
|
||||
libmv/autotrack/predict_tracks.cc
|
||||
libmv/autotrack/predict_tracks.h
|
||||
libmv/autotrack/predict_tracks_test.cc
|
||||
libmv/autotrack/quad.h
|
||||
libmv/autotrack/reconstruction.h
|
||||
libmv/autotrack/region.h
|
||||
libmv/autotrack/tracks.cc
|
||||
libmv/autotrack/tracks.h
|
||||
libmv/autotrack/tracks_test.cc
|
||||
libmv/base/aligned_malloc.cc
|
||||
libmv/base/aligned_malloc.h
|
||||
libmv/base/array.h
|
||||
libmv/base/id_generator.h
|
||||
libmv/base/map.h
|
||||
libmv/base/scoped_ptr.h
|
||||
libmv/base/scoped_ptr_test.cc
|
||||
libmv/base/vector.h
|
||||
libmv/base/vector_test.cc
|
||||
libmv/base/vector_utils.h
|
||||
libmv/build/build_config.h
|
||||
libmv/image/array_nd.cc
|
||||
libmv/image/array_nd.h
|
||||
libmv/image/array_nd_test.cc
|
||||
libmv/image/convolve.cc
|
||||
libmv/image/convolve.h
|
||||
libmv/image/convolve_test.cc
|
||||
libmv/image/correlation.h
|
||||
libmv/image/image_converter.h
|
||||
libmv/image/image_drawing.h
|
||||
libmv/image/image.h
|
||||
libmv/image/image_test.cc
|
||||
libmv/image/sample.h
|
||||
libmv/image/sample_test.cc
|
||||
libmv/image/tuple.h
|
||||
libmv/image/tuple_test.cc
|
||||
libmv/logging/logging.h
|
||||
libmv/multiview/conditioning.cc
|
||||
libmv/multiview/conditioning.h
|
||||
libmv/multiview/euclidean_resection.cc
|
||||
libmv/multiview/euclidean_resection.h
|
||||
libmv/multiview/euclidean_resection_test.cc
|
||||
libmv/multiview/fundamental.cc
|
||||
libmv/multiview/fundamental.h
|
||||
libmv/multiview/fundamental_test.cc
|
||||
libmv/multiview/homography.cc
|
||||
libmv/multiview/homography_error.h
|
||||
libmv/multiview/homography.h
|
||||
libmv/multiview/homography_parameterization.h
|
||||
libmv/multiview/homography_test.cc
|
||||
libmv/multiview/nviewtriangulation.h
|
||||
libmv/multiview/nviewtriangulation_test.cc
|
||||
libmv/multiview/panography.cc
|
||||
libmv/multiview/panography.h
|
||||
libmv/multiview/panography_kernel.cc
|
||||
libmv/multiview/panography_kernel.h
|
||||
libmv/multiview/panography_test.cc
|
||||
libmv/multiview/projection.cc
|
||||
libmv/multiview/projection.h
|
||||
libmv/multiview/projection_test.cc
|
||||
libmv/multiview/resection.h
|
||||
libmv/multiview/resection_test.cc
|
||||
libmv/multiview/test_data_sets.cc
|
||||
libmv/multiview/test_data_sets.h
|
||||
libmv/multiview/triangulation.cc
|
||||
libmv/multiview/triangulation.h
|
||||
libmv/multiview/triangulation_test.cc
|
||||
libmv/multiview/two_view_kernel.h
|
||||
libmv/numeric/dogleg.h
|
||||
libmv/numeric/dogleg_test.cc
|
||||
libmv/numeric/function_derivative.h
|
||||
libmv/numeric/function_derivative_test.cc
|
||||
libmv/numeric/levenberg_marquardt.h
|
||||
libmv/numeric/levenberg_marquardt_test.cc
|
||||
libmv/numeric/numeric.cc
|
||||
libmv/numeric/numeric.h
|
||||
libmv/numeric/numeric_test.cc
|
||||
libmv/numeric/poly.cc
|
||||
libmv/numeric/poly.h
|
||||
libmv/numeric/poly_test.cc
|
||||
libmv/simple_pipeline/bundle.cc
|
||||
libmv/simple_pipeline/bundle.h
|
||||
libmv/simple_pipeline/callbacks.h
|
||||
libmv/simple_pipeline/camera_intrinsics.cc
|
||||
libmv/simple_pipeline/camera_intrinsics.h
|
||||
libmv/simple_pipeline/camera_intrinsics_impl.h
|
||||
libmv/simple_pipeline/camera_intrinsics_test.cc
|
||||
libmv/simple_pipeline/detect.cc
|
||||
libmv/simple_pipeline/detect.h
|
||||
libmv/simple_pipeline/detect_test.cc
|
||||
libmv/simple_pipeline/distortion_models.cc
|
||||
libmv/simple_pipeline/distortion_models.h
|
||||
libmv/simple_pipeline/initialize_reconstruction.cc
|
||||
libmv/simple_pipeline/initialize_reconstruction.h
|
||||
libmv/simple_pipeline/intersect.cc
|
||||
libmv/simple_pipeline/intersect.h
|
||||
libmv/simple_pipeline/intersect_test.cc
|
||||
libmv/simple_pipeline/keyframe_selection.cc
|
||||
libmv/simple_pipeline/keyframe_selection.h
|
||||
libmv/simple_pipeline/keyframe_selection_test.cc
|
||||
libmv/simple_pipeline/modal_solver.cc
|
||||
libmv/simple_pipeline/modal_solver.h
|
||||
libmv/simple_pipeline/modal_solver_test.cc
|
||||
libmv/simple_pipeline/packed_intrinsics.cc
|
||||
libmv/simple_pipeline/packed_intrinsics.h
|
||||
libmv/simple_pipeline/pipeline.cc
|
||||
libmv/simple_pipeline/pipeline.h
|
||||
libmv/simple_pipeline/reconstruction.cc
|
||||
libmv/simple_pipeline/reconstruction.h
|
||||
libmv/simple_pipeline/reconstruction_scale.cc
|
||||
libmv/simple_pipeline/reconstruction_scale.h
|
||||
libmv/simple_pipeline/resect.cc
|
||||
libmv/simple_pipeline/resect.h
|
||||
libmv/simple_pipeline/resect_test.cc
|
||||
libmv/simple_pipeline/tracks.cc
|
||||
libmv/simple_pipeline/tracks.h
|
||||
libmv/threading/threading.h
|
||||
libmv/tracking/brute_region_tracker.cc
|
||||
libmv/tracking/brute_region_tracker.h
|
||||
libmv/tracking/brute_region_tracker_test.cc
|
||||
libmv/tracking/hybrid_region_tracker.cc
|
||||
libmv/tracking/hybrid_region_tracker.h
|
||||
libmv/tracking/kalman_filter.h
|
||||
libmv/tracking/klt_region_tracker.cc
|
||||
libmv/tracking/klt_region_tracker.h
|
||||
libmv/tracking/klt_region_tracker_test.cc
|
||||
libmv/tracking/pyramid_region_tracker.cc
|
||||
libmv/tracking/pyramid_region_tracker.h
|
||||
libmv/tracking/pyramid_region_tracker_test.cc
|
||||
libmv/tracking/region_tracker.h
|
||||
libmv/tracking/retrack_region_tracker.cc
|
||||
libmv/tracking/retrack_region_tracker.h
|
||||
libmv/tracking/track_region.cc
|
||||
libmv/tracking/track_region.h
|
||||
libmv/tracking/trklt_region_tracker.cc
|
||||
libmv/tracking/trklt_region_tracker.h
|
||||
third_party/.clang-format
|
||||
third_party/msinttypes/inttypes.h
|
||||
third_party/msinttypes/README.libmv
|
||||
third_party/msinttypes/stdint.h
|
||||
87
blender-5.2.0/intern/libmv/intern/autotrack.cc
Normal file
87
blender-5.2.0/intern/libmv/intern/autotrack.cc
Normal file
@@ -0,0 +1,87 @@
|
||||
/* SPDX-FileCopyrightText: 2014 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "intern/autotrack.h"
|
||||
#include "intern/tracksN.h"
|
||||
#include "intern/utildefines.h"
|
||||
#include "libmv/autotrack/autotrack.h"
|
||||
|
||||
using libmv::TrackRegionOptions;
|
||||
using libmv::TrackRegionResult;
|
||||
using mv::AutoTrack;
|
||||
using mv::FrameAccessor;
|
||||
using mv::Marker;
|
||||
|
||||
libmv_AutoTrack* libmv_autoTrackNew(libmv_FrameAccessor* frame_accessor) {
|
||||
return (libmv_AutoTrack*)LIBMV_OBJECT_NEW(AutoTrack,
|
||||
(FrameAccessor*)frame_accessor);
|
||||
}
|
||||
|
||||
void libmv_autoTrackDestroy(libmv_AutoTrack* libmv_autotrack) {
|
||||
LIBMV_OBJECT_DELETE(libmv_autotrack, AutoTrack);
|
||||
}
|
||||
|
||||
void libmv_autoTrackSetOptions(libmv_AutoTrack* libmv_autotrack,
|
||||
const libmv_AutoTrackOptions* options) {
|
||||
AutoTrack* autotrack = ((AutoTrack*)libmv_autotrack);
|
||||
libmv_configureTrackRegionOptions(options->track_region,
|
||||
&autotrack->options.track_region);
|
||||
|
||||
autotrack->options.search_region.min(0) = options->search_region.min[0];
|
||||
autotrack->options.search_region.min(1) = options->search_region.min[1];
|
||||
autotrack->options.search_region.max(0) = options->search_region.max[0];
|
||||
autotrack->options.search_region.max(1) = options->search_region.max[1];
|
||||
}
|
||||
|
||||
int libmv_autoTrackMarker(libmv_AutoTrack* libmv_autotrack,
|
||||
const libmv_TrackRegionOptions* libmv_options,
|
||||
libmv_Marker* libmv_tracked_marker,
|
||||
libmv_TrackRegionResult* libmv_result) {
|
||||
Marker tracked_marker;
|
||||
TrackRegionOptions options;
|
||||
TrackRegionResult result;
|
||||
libmv_apiMarkerToMarker(*libmv_tracked_marker, &tracked_marker);
|
||||
libmv_configureTrackRegionOptions(*libmv_options, &options);
|
||||
bool ok = (((AutoTrack*)libmv_autotrack)
|
||||
->TrackMarker(&tracked_marker, &result, &options));
|
||||
libmv_markerToApiMarker(tracked_marker, libmv_tracked_marker);
|
||||
libmv_regionTrackergetResult(result, libmv_result);
|
||||
return ok && result.is_usable();
|
||||
}
|
||||
|
||||
void libmv_autoTrackAddMarker(libmv_AutoTrack* libmv_autotrack,
|
||||
const libmv_Marker* libmv_marker) {
|
||||
Marker marker;
|
||||
libmv_apiMarkerToMarker(*libmv_marker, &marker);
|
||||
((AutoTrack*)libmv_autotrack)->AddMarker(marker);
|
||||
}
|
||||
|
||||
void libmv_autoTrackSetMarkers(libmv_AutoTrack* libmv_autotrack,
|
||||
const libmv_Marker* libmv_marker,
|
||||
size_t num_markers) {
|
||||
if (num_markers == 0) {
|
||||
// Early output.
|
||||
return;
|
||||
}
|
||||
libmv::vector<Marker> markers;
|
||||
markers.resize(num_markers);
|
||||
for (size_t i = 0; i < num_markers; ++i) {
|
||||
libmv_apiMarkerToMarker(libmv_marker[i], &markers[i]);
|
||||
}
|
||||
((AutoTrack*)libmv_autotrack)->SetMarkers(&markers);
|
||||
}
|
||||
|
||||
int libmv_autoTrackGetMarker(libmv_AutoTrack* libmv_autotrack,
|
||||
int clip,
|
||||
int frame,
|
||||
int track,
|
||||
libmv_Marker* libmv_marker) {
|
||||
Marker marker;
|
||||
int ok =
|
||||
((AutoTrack*)libmv_autotrack)->GetMarker(clip, frame, track, &marker);
|
||||
if (ok) {
|
||||
libmv_markerToApiMarker(marker, libmv_marker);
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
53
blender-5.2.0/intern/libmv/intern/autotrack.h
Normal file
53
blender-5.2.0/intern/libmv/intern/autotrack.h
Normal file
@@ -0,0 +1,53 @@
|
||||
/* SPDX-FileCopyrightText: 2014 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#ifndef LIBMV_C_API_AUTOTRACK_H_
|
||||
#define LIBMV_C_API_AUTOTRACK_H_
|
||||
|
||||
#include "intern/frame_accessor.h"
|
||||
#include "intern/region.h"
|
||||
#include "intern/track_region.h"
|
||||
#include "intern/tracksN.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct libmv_AutoTrack libmv_AutoTrack;
|
||||
|
||||
typedef struct libmv_AutoTrackOptions {
|
||||
libmv_TrackRegionOptions track_region;
|
||||
libmv_Region search_region;
|
||||
} libmv_AutoTrackOptions;
|
||||
|
||||
libmv_AutoTrack* libmv_autoTrackNew(libmv_FrameAccessor* frame_accessor);
|
||||
|
||||
void libmv_autoTrackDestroy(libmv_AutoTrack* libmv_autotrack);
|
||||
|
||||
void libmv_autoTrackSetOptions(libmv_AutoTrack* libmv_autotrack,
|
||||
const libmv_AutoTrackOptions* options);
|
||||
|
||||
int libmv_autoTrackMarker(libmv_AutoTrack* libmv_autotrack,
|
||||
const libmv_TrackRegionOptions* libmv_options,
|
||||
libmv_Marker* libmv_tracker_marker,
|
||||
libmv_TrackRegionResult* libmv_result);
|
||||
|
||||
void libmv_autoTrackAddMarker(libmv_AutoTrack* libmv_autotrack,
|
||||
const libmv_Marker* libmv_marker);
|
||||
|
||||
void libmv_autoTrackSetMarkers(libmv_AutoTrack* libmv_autotrack,
|
||||
const libmv_Marker* libmv_marker,
|
||||
size_t num_markers);
|
||||
|
||||
int libmv_autoTrackGetMarker(libmv_AutoTrack* libmv_autotrack,
|
||||
int clip,
|
||||
int frame,
|
||||
int track,
|
||||
libmv_Marker* libmv_marker);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // LIBMV_C_API_TRACKS_H_
|
||||
413
blender-5.2.0/intern/libmv/intern/camera_intrinsics.cc
Normal file
413
blender-5.2.0/intern/libmv/intern/camera_intrinsics.cc
Normal file
@@ -0,0 +1,413 @@
|
||||
/* SPDX-FileCopyrightText: 2011 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "intern/camera_intrinsics.h"
|
||||
#include "intern/utildefines.h"
|
||||
#include "libmv/simple_pipeline/camera_intrinsics.h"
|
||||
|
||||
using libmv::BrownCameraIntrinsics;
|
||||
using libmv::CameraIntrinsics;
|
||||
using libmv::DivisionCameraIntrinsics;
|
||||
using libmv::NukeCameraIntrinsics;
|
||||
using libmv::PolynomialCameraIntrinsics;
|
||||
|
||||
libmv_CameraIntrinsics* libmv_cameraIntrinsicsNew(
|
||||
const libmv_CameraIntrinsicsOptions* libmv_camera_intrinsics_options) {
|
||||
CameraIntrinsics* camera_intrinsics =
|
||||
libmv_cameraIntrinsicsCreateFromOptions(libmv_camera_intrinsics_options);
|
||||
return (libmv_CameraIntrinsics*)camera_intrinsics;
|
||||
}
|
||||
|
||||
libmv_CameraIntrinsics* libmv_cameraIntrinsicsCopy(
|
||||
const libmv_CameraIntrinsics* libmv_intrinsics) {
|
||||
const CameraIntrinsics* orig_intrinsics =
|
||||
(const CameraIntrinsics*)libmv_intrinsics;
|
||||
|
||||
CameraIntrinsics* new_intrinsics = NULL;
|
||||
switch (orig_intrinsics->GetDistortionModelType()) {
|
||||
case libmv::DISTORTION_MODEL_POLYNOMIAL: {
|
||||
const PolynomialCameraIntrinsics* polynomial_intrinsics =
|
||||
static_cast<const PolynomialCameraIntrinsics*>(orig_intrinsics);
|
||||
new_intrinsics =
|
||||
LIBMV_OBJECT_NEW(PolynomialCameraIntrinsics, *polynomial_intrinsics);
|
||||
break;
|
||||
}
|
||||
case libmv::DISTORTION_MODEL_DIVISION: {
|
||||
const DivisionCameraIntrinsics* division_intrinsics =
|
||||
static_cast<const DivisionCameraIntrinsics*>(orig_intrinsics);
|
||||
new_intrinsics =
|
||||
LIBMV_OBJECT_NEW(DivisionCameraIntrinsics, *division_intrinsics);
|
||||
break;
|
||||
}
|
||||
case libmv::DISTORTION_MODEL_NUKE: {
|
||||
const NukeCameraIntrinsics* nuke_intrinsics =
|
||||
static_cast<const NukeCameraIntrinsics*>(orig_intrinsics);
|
||||
new_intrinsics = LIBMV_OBJECT_NEW(NukeCameraIntrinsics, *nuke_intrinsics);
|
||||
break;
|
||||
}
|
||||
case libmv::DISTORTION_MODEL_BROWN: {
|
||||
const BrownCameraIntrinsics* brown_intrinsics =
|
||||
static_cast<const BrownCameraIntrinsics*>(orig_intrinsics);
|
||||
new_intrinsics =
|
||||
LIBMV_OBJECT_NEW(BrownCameraIntrinsics, *brown_intrinsics);
|
||||
break;
|
||||
}
|
||||
default: assert(!"Unknown distortion model");
|
||||
}
|
||||
return (libmv_CameraIntrinsics*)new_intrinsics;
|
||||
}
|
||||
|
||||
void libmv_cameraIntrinsicsDestroy(libmv_CameraIntrinsics* libmv_intrinsics) {
|
||||
LIBMV_OBJECT_DELETE(libmv_intrinsics, CameraIntrinsics);
|
||||
}
|
||||
|
||||
void libmv_cameraIntrinsicsUpdate(
|
||||
const libmv_CameraIntrinsicsOptions* libmv_camera_intrinsics_options,
|
||||
libmv_CameraIntrinsics* libmv_intrinsics) {
|
||||
CameraIntrinsics* camera_intrinsics = (CameraIntrinsics*)libmv_intrinsics;
|
||||
|
||||
double focal_length = libmv_camera_intrinsics_options->focal_length;
|
||||
double principal_x = libmv_camera_intrinsics_options->principal_point_x;
|
||||
double principal_y = libmv_camera_intrinsics_options->principal_point_y;
|
||||
int image_width = libmv_camera_intrinsics_options->image_width;
|
||||
int image_height = libmv_camera_intrinsics_options->image_height;
|
||||
|
||||
/* Try avoid unnecessary updates, so pre-computed distortion grids
|
||||
* are not freed. */
|
||||
|
||||
if (camera_intrinsics->focal_length() != focal_length) {
|
||||
camera_intrinsics->SetFocalLength(focal_length, focal_length);
|
||||
}
|
||||
|
||||
if (camera_intrinsics->principal_point_x() != principal_x ||
|
||||
camera_intrinsics->principal_point_y() != principal_y) {
|
||||
camera_intrinsics->SetPrincipalPoint(principal_x, principal_y);
|
||||
}
|
||||
|
||||
if (camera_intrinsics->image_width() != image_width ||
|
||||
camera_intrinsics->image_height() != image_height) {
|
||||
camera_intrinsics->SetImageSize(image_width, image_height);
|
||||
}
|
||||
|
||||
switch (libmv_camera_intrinsics_options->distortion_model) {
|
||||
case LIBMV_DISTORTION_MODEL_POLYNOMIAL: {
|
||||
assert(camera_intrinsics->GetDistortionModelType() ==
|
||||
libmv::DISTORTION_MODEL_POLYNOMIAL);
|
||||
|
||||
PolynomialCameraIntrinsics* polynomial_intrinsics =
|
||||
(PolynomialCameraIntrinsics*)camera_intrinsics;
|
||||
|
||||
double k1 = libmv_camera_intrinsics_options->polynomial_k1;
|
||||
double k2 = libmv_camera_intrinsics_options->polynomial_k2;
|
||||
double k3 = libmv_camera_intrinsics_options->polynomial_k3;
|
||||
|
||||
if (polynomial_intrinsics->k1() != k1 ||
|
||||
polynomial_intrinsics->k2() != k2 ||
|
||||
polynomial_intrinsics->k3() != k3) {
|
||||
polynomial_intrinsics->SetRadialDistortion(k1, k2, k3);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case LIBMV_DISTORTION_MODEL_DIVISION: {
|
||||
assert(camera_intrinsics->GetDistortionModelType() ==
|
||||
libmv::DISTORTION_MODEL_DIVISION);
|
||||
|
||||
DivisionCameraIntrinsics* division_intrinsics =
|
||||
(DivisionCameraIntrinsics*)camera_intrinsics;
|
||||
|
||||
double k1 = libmv_camera_intrinsics_options->division_k1;
|
||||
double k2 = libmv_camera_intrinsics_options->division_k2;
|
||||
|
||||
if (division_intrinsics->k1() != k1 || division_intrinsics->k2() != k2) {
|
||||
division_intrinsics->SetDistortion(k1, k2);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case LIBMV_DISTORTION_MODEL_NUKE: {
|
||||
assert(camera_intrinsics->GetDistortionModelType() ==
|
||||
libmv::DISTORTION_MODEL_NUKE);
|
||||
|
||||
NukeCameraIntrinsics* nuke_intrinsics =
|
||||
(NukeCameraIntrinsics*)camera_intrinsics;
|
||||
|
||||
double k1 = libmv_camera_intrinsics_options->nuke_k1;
|
||||
double k2 = libmv_camera_intrinsics_options->nuke_k2;
|
||||
|
||||
if (nuke_intrinsics->k1() != k1 || nuke_intrinsics->k2() != k2) {
|
||||
nuke_intrinsics->SetRadialDistortion(k1, k2);
|
||||
}
|
||||
|
||||
double p1 = libmv_camera_intrinsics_options->nuke_p1;
|
||||
double p2 = libmv_camera_intrinsics_options->nuke_p2;
|
||||
|
||||
if (nuke_intrinsics->p1() != p1 || nuke_intrinsics->p2() != p2) {
|
||||
nuke_intrinsics->SetTangentialDistortion(p1, p2);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case LIBMV_DISTORTION_MODEL_BROWN: {
|
||||
assert(camera_intrinsics->GetDistortionModelType() ==
|
||||
libmv::DISTORTION_MODEL_BROWN);
|
||||
|
||||
BrownCameraIntrinsics* brown_intrinsics =
|
||||
(BrownCameraIntrinsics*)camera_intrinsics;
|
||||
|
||||
double k1 = libmv_camera_intrinsics_options->brown_k1;
|
||||
double k2 = libmv_camera_intrinsics_options->brown_k2;
|
||||
double k3 = libmv_camera_intrinsics_options->brown_k3;
|
||||
double k4 = libmv_camera_intrinsics_options->brown_k4;
|
||||
|
||||
if (brown_intrinsics->k1() != k1 || brown_intrinsics->k2() != k2 ||
|
||||
brown_intrinsics->k3() != k3 || brown_intrinsics->k4() != k4) {
|
||||
brown_intrinsics->SetRadialDistortion(k1, k2, k3, k4);
|
||||
}
|
||||
|
||||
double p1 = libmv_camera_intrinsics_options->brown_p1;
|
||||
double p2 = libmv_camera_intrinsics_options->brown_p2;
|
||||
|
||||
if (brown_intrinsics->p1() != p1 || brown_intrinsics->p2() != p2) {
|
||||
brown_intrinsics->SetTangentialDistortion(p1, p2);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default: assert(!"Unknown distortion model");
|
||||
}
|
||||
}
|
||||
|
||||
void libmv_cameraIntrinsicsExtractOptions(
|
||||
const libmv_CameraIntrinsics* libmv_intrinsics,
|
||||
libmv_CameraIntrinsicsOptions* camera_intrinsics_options) {
|
||||
const CameraIntrinsics* camera_intrinsics =
|
||||
(const CameraIntrinsics*)libmv_intrinsics;
|
||||
|
||||
// Fill in options which are common for all distortion models.
|
||||
camera_intrinsics_options->focal_length = camera_intrinsics->focal_length();
|
||||
camera_intrinsics_options->principal_point_x =
|
||||
camera_intrinsics->principal_point_x();
|
||||
camera_intrinsics_options->principal_point_y =
|
||||
camera_intrinsics->principal_point_y();
|
||||
|
||||
camera_intrinsics_options->image_width = camera_intrinsics->image_width();
|
||||
camera_intrinsics_options->image_height = camera_intrinsics->image_height();
|
||||
|
||||
switch (camera_intrinsics->GetDistortionModelType()) {
|
||||
case libmv::DISTORTION_MODEL_POLYNOMIAL: {
|
||||
const PolynomialCameraIntrinsics* polynomial_intrinsics =
|
||||
static_cast<const PolynomialCameraIntrinsics*>(camera_intrinsics);
|
||||
camera_intrinsics_options->distortion_model =
|
||||
LIBMV_DISTORTION_MODEL_POLYNOMIAL;
|
||||
camera_intrinsics_options->polynomial_k1 = polynomial_intrinsics->k1();
|
||||
camera_intrinsics_options->polynomial_k2 = polynomial_intrinsics->k2();
|
||||
camera_intrinsics_options->polynomial_k3 = polynomial_intrinsics->k3();
|
||||
camera_intrinsics_options->polynomial_p1 = polynomial_intrinsics->p1();
|
||||
camera_intrinsics_options->polynomial_p2 = polynomial_intrinsics->p2();
|
||||
break;
|
||||
}
|
||||
|
||||
case libmv::DISTORTION_MODEL_DIVISION: {
|
||||
const DivisionCameraIntrinsics* division_intrinsics =
|
||||
static_cast<const DivisionCameraIntrinsics*>(camera_intrinsics);
|
||||
camera_intrinsics_options->distortion_model =
|
||||
LIBMV_DISTORTION_MODEL_DIVISION;
|
||||
camera_intrinsics_options->division_k1 = division_intrinsics->k1();
|
||||
camera_intrinsics_options->division_k2 = division_intrinsics->k2();
|
||||
break;
|
||||
}
|
||||
|
||||
case libmv::DISTORTION_MODEL_NUKE: {
|
||||
const NukeCameraIntrinsics* nuke_intrinsics =
|
||||
static_cast<const NukeCameraIntrinsics*>(camera_intrinsics);
|
||||
camera_intrinsics_options->distortion_model = LIBMV_DISTORTION_MODEL_NUKE;
|
||||
camera_intrinsics_options->nuke_k1 = nuke_intrinsics->k1();
|
||||
camera_intrinsics_options->nuke_k2 = nuke_intrinsics->k2();
|
||||
camera_intrinsics_options->nuke_p1 = nuke_intrinsics->p1();
|
||||
camera_intrinsics_options->nuke_p2 = nuke_intrinsics->p2();
|
||||
break;
|
||||
}
|
||||
|
||||
case libmv::DISTORTION_MODEL_BROWN: {
|
||||
const BrownCameraIntrinsics* brown_intrinsics =
|
||||
static_cast<const BrownCameraIntrinsics*>(camera_intrinsics);
|
||||
camera_intrinsics_options->distortion_model =
|
||||
LIBMV_DISTORTION_MODEL_BROWN;
|
||||
camera_intrinsics_options->brown_k1 = brown_intrinsics->k1();
|
||||
camera_intrinsics_options->brown_k2 = brown_intrinsics->k2();
|
||||
camera_intrinsics_options->brown_k3 = brown_intrinsics->k3();
|
||||
camera_intrinsics_options->brown_k4 = brown_intrinsics->k4();
|
||||
camera_intrinsics_options->brown_p1 = brown_intrinsics->p1();
|
||||
camera_intrinsics_options->brown_p2 = brown_intrinsics->p2();
|
||||
break;
|
||||
}
|
||||
|
||||
default: assert(!"Unknown distortion model");
|
||||
}
|
||||
}
|
||||
|
||||
void libmv_cameraIntrinsicsUndistortByte(
|
||||
const libmv_CameraIntrinsics* libmv_intrinsics,
|
||||
const unsigned char* source_image,
|
||||
int width,
|
||||
int height,
|
||||
float overscan,
|
||||
int channels,
|
||||
unsigned char* destination_image) {
|
||||
CameraIntrinsics* camera_intrinsics = (CameraIntrinsics*)libmv_intrinsics;
|
||||
camera_intrinsics->UndistortBuffer(
|
||||
source_image, width, height, overscan, channels, destination_image);
|
||||
}
|
||||
|
||||
void libmv_cameraIntrinsicsUndistortFloat(
|
||||
const libmv_CameraIntrinsics* libmv_intrinsics,
|
||||
const float* source_image,
|
||||
int width,
|
||||
int height,
|
||||
float overscan,
|
||||
int channels,
|
||||
float* destination_image) {
|
||||
CameraIntrinsics* intrinsics = (CameraIntrinsics*)libmv_intrinsics;
|
||||
intrinsics->UndistortBuffer(
|
||||
source_image, width, height, overscan, channels, destination_image);
|
||||
}
|
||||
|
||||
void libmv_cameraIntrinsicsDistortByte(
|
||||
const struct libmv_CameraIntrinsics* libmv_intrinsics,
|
||||
const unsigned char* source_image,
|
||||
int width,
|
||||
int height,
|
||||
float overscan,
|
||||
int channels,
|
||||
unsigned char* destination_image) {
|
||||
CameraIntrinsics* intrinsics = (CameraIntrinsics*)libmv_intrinsics;
|
||||
intrinsics->DistortBuffer(
|
||||
source_image, width, height, overscan, channels, destination_image);
|
||||
}
|
||||
|
||||
void libmv_cameraIntrinsicsDistortFloat(
|
||||
const libmv_CameraIntrinsics* libmv_intrinsics,
|
||||
float* source_image,
|
||||
int width,
|
||||
int height,
|
||||
float overscan,
|
||||
int channels,
|
||||
float* destination_image) {
|
||||
CameraIntrinsics* intrinsics = (CameraIntrinsics*)libmv_intrinsics;
|
||||
intrinsics->DistortBuffer(
|
||||
source_image, width, height, overscan, channels, destination_image);
|
||||
}
|
||||
|
||||
void libmv_cameraIntrinsicsApply(
|
||||
const struct libmv_CameraIntrinsics* libmv_intrinsics,
|
||||
double x,
|
||||
double y,
|
||||
double* x1,
|
||||
double* y1) {
|
||||
CameraIntrinsics* intrinsics = (CameraIntrinsics*)libmv_intrinsics;
|
||||
intrinsics->ApplyIntrinsics(x, y, x1, y1);
|
||||
}
|
||||
|
||||
void libmv_cameraIntrinsicsInvert(
|
||||
const struct libmv_CameraIntrinsics* libmv_intrinsics,
|
||||
double x,
|
||||
double y,
|
||||
double* x1,
|
||||
double* y1) {
|
||||
CameraIntrinsics* intrinsics = (CameraIntrinsics*)libmv_intrinsics;
|
||||
intrinsics->InvertIntrinsics(x, y, x1, y1);
|
||||
}
|
||||
|
||||
static void libmv_cameraIntrinsicsFillFromOptions(
|
||||
const libmv_CameraIntrinsicsOptions* camera_intrinsics_options,
|
||||
CameraIntrinsics* camera_intrinsics) {
|
||||
camera_intrinsics->SetFocalLength(camera_intrinsics_options->focal_length,
|
||||
camera_intrinsics_options->focal_length);
|
||||
|
||||
camera_intrinsics->SetPrincipalPoint(
|
||||
camera_intrinsics_options->principal_point_x,
|
||||
camera_intrinsics_options->principal_point_y);
|
||||
|
||||
camera_intrinsics->SetImageSize(camera_intrinsics_options->image_width,
|
||||
camera_intrinsics_options->image_height);
|
||||
|
||||
switch (camera_intrinsics_options->distortion_model) {
|
||||
case LIBMV_DISTORTION_MODEL_POLYNOMIAL: {
|
||||
PolynomialCameraIntrinsics* polynomial_intrinsics =
|
||||
static_cast<PolynomialCameraIntrinsics*>(camera_intrinsics);
|
||||
|
||||
polynomial_intrinsics->SetRadialDistortion(
|
||||
camera_intrinsics_options->polynomial_k1,
|
||||
camera_intrinsics_options->polynomial_k2,
|
||||
camera_intrinsics_options->polynomial_k3);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case LIBMV_DISTORTION_MODEL_DIVISION: {
|
||||
DivisionCameraIntrinsics* division_intrinsics =
|
||||
static_cast<DivisionCameraIntrinsics*>(camera_intrinsics);
|
||||
|
||||
division_intrinsics->SetDistortion(
|
||||
camera_intrinsics_options->division_k1,
|
||||
camera_intrinsics_options->division_k2);
|
||||
break;
|
||||
}
|
||||
|
||||
case LIBMV_DISTORTION_MODEL_NUKE: {
|
||||
NukeCameraIntrinsics* nuke_intrinsics =
|
||||
static_cast<NukeCameraIntrinsics*>(camera_intrinsics);
|
||||
|
||||
nuke_intrinsics->SetRadialDistortion(camera_intrinsics_options->nuke_k1,
|
||||
camera_intrinsics_options->nuke_k2);
|
||||
nuke_intrinsics->SetTangentialDistortion(
|
||||
camera_intrinsics_options->nuke_p1,
|
||||
camera_intrinsics_options->nuke_p2);
|
||||
break;
|
||||
}
|
||||
|
||||
case LIBMV_DISTORTION_MODEL_BROWN: {
|
||||
BrownCameraIntrinsics* brown_intrinsics =
|
||||
static_cast<BrownCameraIntrinsics*>(camera_intrinsics);
|
||||
|
||||
brown_intrinsics->SetRadialDistortion(
|
||||
camera_intrinsics_options->brown_k1,
|
||||
camera_intrinsics_options->brown_k2,
|
||||
camera_intrinsics_options->brown_k3,
|
||||
camera_intrinsics_options->brown_k4);
|
||||
brown_intrinsics->SetTangentialDistortion(
|
||||
camera_intrinsics_options->brown_p1,
|
||||
camera_intrinsics_options->brown_p2);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default: assert(!"Unknown distortion model");
|
||||
}
|
||||
}
|
||||
|
||||
CameraIntrinsics* libmv_cameraIntrinsicsCreateFromOptions(
|
||||
const libmv_CameraIntrinsicsOptions* camera_intrinsics_options) {
|
||||
CameraIntrinsics* camera_intrinsics = NULL;
|
||||
switch (camera_intrinsics_options->distortion_model) {
|
||||
case LIBMV_DISTORTION_MODEL_POLYNOMIAL:
|
||||
camera_intrinsics = LIBMV_OBJECT_NEW(PolynomialCameraIntrinsics);
|
||||
break;
|
||||
case LIBMV_DISTORTION_MODEL_DIVISION:
|
||||
camera_intrinsics = LIBMV_OBJECT_NEW(DivisionCameraIntrinsics);
|
||||
break;
|
||||
case LIBMV_DISTORTION_MODEL_NUKE:
|
||||
camera_intrinsics = LIBMV_OBJECT_NEW(NukeCameraIntrinsics);
|
||||
break;
|
||||
case LIBMV_DISTORTION_MODEL_BROWN:
|
||||
camera_intrinsics = LIBMV_OBJECT_NEW(BrownCameraIntrinsics);
|
||||
break;
|
||||
default: assert(!"Unknown distortion model");
|
||||
}
|
||||
libmv_cameraIntrinsicsFillFromOptions(camera_intrinsics_options,
|
||||
camera_intrinsics);
|
||||
return camera_intrinsics;
|
||||
}
|
||||
123
blender-5.2.0/intern/libmv/intern/camera_intrinsics.h
Normal file
123
blender-5.2.0/intern/libmv/intern/camera_intrinsics.h
Normal file
@@ -0,0 +1,123 @@
|
||||
/* SPDX-FileCopyrightText: 2011 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#ifndef LIBMV_C_API_CAMERA_INTRINSICS_H_
|
||||
#define LIBMV_C_API_CAMERA_INTRINSICS_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct libmv_CameraIntrinsics libmv_CameraIntrinsics;
|
||||
|
||||
enum {
|
||||
LIBMV_DISTORTION_MODEL_POLYNOMIAL = 0,
|
||||
LIBMV_DISTORTION_MODEL_DIVISION = 1,
|
||||
LIBMV_DISTORTION_MODEL_NUKE = 2,
|
||||
LIBMV_DISTORTION_MODEL_BROWN = 3,
|
||||
};
|
||||
|
||||
typedef struct libmv_CameraIntrinsicsOptions {
|
||||
// Common settings of all distortion models.
|
||||
int distortion_model;
|
||||
int image_width, image_height;
|
||||
double focal_length;
|
||||
double principal_point_x, principal_point_y;
|
||||
|
||||
// Radial distortion model.
|
||||
double polynomial_k1, polynomial_k2, polynomial_k3;
|
||||
double polynomial_p1, polynomial_p2;
|
||||
|
||||
// Division distortion model.
|
||||
double division_k1, division_k2;
|
||||
|
||||
// Nuke distortion model.
|
||||
double nuke_k1, nuke_k2;
|
||||
double nuke_p1, nuke_p2;
|
||||
|
||||
// Brown-Conrady distortion model.
|
||||
double brown_k1, brown_k2, brown_k3, brown_k4;
|
||||
double brown_p1, brown_p2;
|
||||
} libmv_CameraIntrinsicsOptions;
|
||||
|
||||
libmv_CameraIntrinsics* libmv_cameraIntrinsicsNew(
|
||||
const libmv_CameraIntrinsicsOptions* libmv_camera_intrinsics_options);
|
||||
|
||||
libmv_CameraIntrinsics* libmv_cameraIntrinsicsCopy(
|
||||
const libmv_CameraIntrinsics* libmv_intrinsics);
|
||||
|
||||
void libmv_cameraIntrinsicsDestroy(libmv_CameraIntrinsics* libmv_intrinsics);
|
||||
void libmv_cameraIntrinsicsUpdate(
|
||||
const libmv_CameraIntrinsicsOptions* libmv_camera_intrinsics_options,
|
||||
libmv_CameraIntrinsics* libmv_intrinsics);
|
||||
|
||||
void libmv_cameraIntrinsicsExtractOptions(
|
||||
const libmv_CameraIntrinsics* libmv_intrinsics,
|
||||
libmv_CameraIntrinsicsOptions* camera_intrinsics_options);
|
||||
|
||||
void libmv_cameraIntrinsicsUndistortByte(
|
||||
const libmv_CameraIntrinsics* libmv_intrinsics,
|
||||
const unsigned char* source_image,
|
||||
int width,
|
||||
int height,
|
||||
float overscan,
|
||||
int channels,
|
||||
unsigned char* destination_image);
|
||||
|
||||
void libmv_cameraIntrinsicsUndistortFloat(
|
||||
const libmv_CameraIntrinsics* libmv_intrinsics,
|
||||
const float* source_image,
|
||||
int width,
|
||||
int height,
|
||||
float overscan,
|
||||
int channels,
|
||||
float* destination_image);
|
||||
|
||||
void libmv_cameraIntrinsicsDistortByte(
|
||||
const struct libmv_CameraIntrinsics* libmv_intrinsics,
|
||||
const unsigned char* source_image,
|
||||
int width,
|
||||
int height,
|
||||
float overscan,
|
||||
int channels,
|
||||
unsigned char* destination_image);
|
||||
|
||||
void libmv_cameraIntrinsicsDistortFloat(
|
||||
const libmv_CameraIntrinsics* libmv_intrinsics,
|
||||
float* source_image,
|
||||
int width,
|
||||
int height,
|
||||
float overscan,
|
||||
int channels,
|
||||
float* destination_image);
|
||||
|
||||
void libmv_cameraIntrinsicsApply(
|
||||
const struct libmv_CameraIntrinsics* libmv_intrinsics,
|
||||
double x,
|
||||
double y,
|
||||
double* x1,
|
||||
double* y1);
|
||||
|
||||
void libmv_cameraIntrinsicsInvert(
|
||||
const struct libmv_CameraIntrinsics* libmv_intrinsics,
|
||||
double x,
|
||||
double y,
|
||||
double* x1,
|
||||
double* y1);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
namespace libmv {
|
||||
class CameraIntrinsics;
|
||||
}
|
||||
|
||||
libmv::CameraIntrinsics* libmv_cameraIntrinsicsCreateFromOptions(
|
||||
const libmv_CameraIntrinsicsOptions* camera_intrinsics_options);
|
||||
#endif
|
||||
|
||||
#endif // LIBMV_C_API_CAMERA_INTRINSICS_H_
|
||||
126
blender-5.2.0/intern/libmv/intern/detector.cc
Normal file
126
blender-5.2.0/intern/libmv/intern/detector.cc
Normal file
@@ -0,0 +1,126 @@
|
||||
/* SPDX-FileCopyrightText: 2011 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "intern/detector.h"
|
||||
#include "intern/image.h"
|
||||
#include "intern/utildefines.h"
|
||||
#include "libmv/simple_pipeline/detect.h"
|
||||
|
||||
using libmv::Detect;
|
||||
using libmv::DetectOptions;
|
||||
using libmv::Feature;
|
||||
using libmv::FloatImage;
|
||||
|
||||
struct libmv_Features {
|
||||
int count;
|
||||
Feature* features;
|
||||
};
|
||||
|
||||
namespace {
|
||||
|
||||
libmv_Features* libmv_featuresFromVector(
|
||||
const libmv::vector<Feature>& features) {
|
||||
libmv_Features* libmv_features = LIBMV_STRUCT_NEW(libmv_Features, 1);
|
||||
int count = features.size();
|
||||
if (count) {
|
||||
libmv_features->features = LIBMV_STRUCT_NEW(Feature, count);
|
||||
for (int i = 0; i < count; i++) {
|
||||
libmv_features->features[i] = features.at(i);
|
||||
}
|
||||
} else {
|
||||
libmv_features->features = NULL;
|
||||
}
|
||||
libmv_features->count = count;
|
||||
return libmv_features;
|
||||
}
|
||||
|
||||
void libmv_convertDetectorOptions(libmv_DetectOptions* options,
|
||||
DetectOptions* detector_options) {
|
||||
switch (options->detector) {
|
||||
#define LIBMV_CONVERT(the_detector) \
|
||||
case LIBMV_DETECTOR_##the_detector: \
|
||||
detector_options->type = DetectOptions::the_detector; \
|
||||
break;
|
||||
LIBMV_CONVERT(FAST)
|
||||
LIBMV_CONVERT(MORAVEC)
|
||||
LIBMV_CONVERT(HARRIS)
|
||||
#undef LIBMV_CONVERT
|
||||
}
|
||||
detector_options->margin = options->margin;
|
||||
detector_options->min_distance = options->min_distance;
|
||||
detector_options->fast_min_trackness = options->fast_min_trackness;
|
||||
detector_options->moravec_max_count = options->moravec_max_count;
|
||||
detector_options->moravec_pattern = options->moravec_pattern;
|
||||
detector_options->harris_threshold = options->harris_threshold;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
libmv_Features* libmv_detectFeaturesByte(const unsigned char* image_buffer,
|
||||
int width,
|
||||
int height,
|
||||
int channels,
|
||||
libmv_DetectOptions* options) {
|
||||
// Prepare the image.
|
||||
FloatImage image;
|
||||
libmv_byteBufferToFloatImage(image_buffer, width, height, channels, &image);
|
||||
|
||||
// Configure detector.
|
||||
DetectOptions detector_options;
|
||||
libmv_convertDetectorOptions(options, &detector_options);
|
||||
|
||||
// Run the detector.
|
||||
libmv::vector<Feature> detected_features;
|
||||
Detect(image, detector_options, &detected_features);
|
||||
|
||||
// Convert result to C-API.
|
||||
libmv_Features* result = libmv_featuresFromVector(detected_features);
|
||||
return result;
|
||||
}
|
||||
|
||||
libmv_Features* libmv_detectFeaturesFloat(const float* image_buffer,
|
||||
int width,
|
||||
int height,
|
||||
int channels,
|
||||
libmv_DetectOptions* options) {
|
||||
// Prepare the image.
|
||||
FloatImage image;
|
||||
libmv_floatBufferToFloatImage(image_buffer, width, height, channels, &image);
|
||||
|
||||
// Configure detector.
|
||||
DetectOptions detector_options;
|
||||
libmv_convertDetectorOptions(options, &detector_options);
|
||||
|
||||
// Run the detector.
|
||||
libmv::vector<Feature> detected_features;
|
||||
Detect(image, detector_options, &detected_features);
|
||||
|
||||
// Convert result to C-API.
|
||||
libmv_Features* result = libmv_featuresFromVector(detected_features);
|
||||
return result;
|
||||
}
|
||||
|
||||
void libmv_featuresDestroy(libmv_Features* libmv_features) {
|
||||
if (libmv_features->features) {
|
||||
LIBMV_STRUCT_DELETE(libmv_features->features);
|
||||
}
|
||||
LIBMV_STRUCT_DELETE(libmv_features);
|
||||
}
|
||||
|
||||
int libmv_countFeatures(const libmv_Features* libmv_features) {
|
||||
return libmv_features->count;
|
||||
}
|
||||
|
||||
void libmv_getFeature(const libmv_Features* libmv_features,
|
||||
int number,
|
||||
double* x,
|
||||
double* y,
|
||||
double* score,
|
||||
double* size) {
|
||||
Feature& feature = libmv_features->features[number];
|
||||
*x = feature.x;
|
||||
*y = feature.y;
|
||||
*score = feature.score;
|
||||
*size = feature.size;
|
||||
}
|
||||
55
blender-5.2.0/intern/libmv/intern/detector.h
Normal file
55
blender-5.2.0/intern/libmv/intern/detector.h
Normal file
@@ -0,0 +1,55 @@
|
||||
/* SPDX-FileCopyrightText: 2011 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#ifndef LIBMV_C_API_DETECTOR_H_
|
||||
#define LIBMV_C_API_DETECTOR_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct libmv_Features libmv_Features;
|
||||
|
||||
enum {
|
||||
LIBMV_DETECTOR_FAST,
|
||||
LIBMV_DETECTOR_MORAVEC,
|
||||
LIBMV_DETECTOR_HARRIS,
|
||||
};
|
||||
|
||||
typedef struct libmv_DetectOptions {
|
||||
int detector;
|
||||
int margin;
|
||||
int min_distance;
|
||||
int fast_min_trackness;
|
||||
int moravec_max_count;
|
||||
unsigned char* moravec_pattern;
|
||||
double harris_threshold;
|
||||
} libmv_DetectOptions;
|
||||
|
||||
libmv_Features* libmv_detectFeaturesByte(const unsigned char* image_buffer,
|
||||
int width,
|
||||
int height,
|
||||
int channels,
|
||||
libmv_DetectOptions* options);
|
||||
|
||||
libmv_Features* libmv_detectFeaturesFloat(const float* image_buffer,
|
||||
int width,
|
||||
int height,
|
||||
int channels,
|
||||
libmv_DetectOptions* options);
|
||||
|
||||
void libmv_featuresDestroy(libmv_Features* libmv_features);
|
||||
int libmv_countFeatures(const libmv_Features* libmv_features);
|
||||
void libmv_getFeature(const libmv_Features* libmv_features,
|
||||
int number,
|
||||
double* x,
|
||||
double* y,
|
||||
double* score,
|
||||
double* size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // LIBMV_C_API_DETECTOR_H_
|
||||
178
blender-5.2.0/intern/libmv/intern/frame_accessor.cc
Normal file
178
blender-5.2.0/intern/libmv/intern/frame_accessor.cc
Normal file
@@ -0,0 +1,178 @@
|
||||
/* SPDX-FileCopyrightText: 2014 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "intern/frame_accessor.h"
|
||||
#include "intern/image.h"
|
||||
#include "intern/utildefines.h"
|
||||
#include "libmv/autotrack/frame_accessor.h"
|
||||
#include "libmv/autotrack/region.h"
|
||||
#include "libmv/image/image.h"
|
||||
|
||||
namespace {
|
||||
|
||||
using libmv::FloatImage;
|
||||
using mv::FrameAccessor;
|
||||
using mv::Region;
|
||||
|
||||
struct LibmvFrameAccessor : public FrameAccessor {
|
||||
LibmvFrameAccessor(libmv_FrameAccessorUserData* user_data,
|
||||
libmv_GetImageCallback get_image_callback,
|
||||
libmv_ReleaseImageCallback release_image_callback,
|
||||
libmv_GetMaskForTrackCallback get_mask_for_track_callback,
|
||||
libmv_ReleaseMaskCallback release_mask_callback)
|
||||
: user_data_(user_data),
|
||||
get_image_callback_(get_image_callback),
|
||||
release_image_callback_(release_image_callback),
|
||||
get_mask_for_track_callback_(get_mask_for_track_callback),
|
||||
release_mask_callback_(release_mask_callback) {}
|
||||
|
||||
virtual ~LibmvFrameAccessor() {}
|
||||
|
||||
libmv_InputMode get_libmv_input_mode(InputMode input_mode) {
|
||||
switch (input_mode) {
|
||||
#define CHECK_INPUT_MODE(mode) \
|
||||
case mode: return LIBMV_IMAGE_MODE_##mode;
|
||||
CHECK_INPUT_MODE(MONO)
|
||||
CHECK_INPUT_MODE(RGBA)
|
||||
#undef CHECK_INPUT_MODE
|
||||
}
|
||||
assert(!"unknown input mode passed from Libmv.");
|
||||
// TODO(sergey): Proper error handling here in the future.
|
||||
return LIBMV_IMAGE_MODE_MONO;
|
||||
}
|
||||
|
||||
void get_libmv_region(const Region& region, libmv_Region* libmv_region) {
|
||||
libmv_region->min[0] = region.min(0);
|
||||
libmv_region->min[1] = region.min(1);
|
||||
libmv_region->max[0] = region.max(0);
|
||||
libmv_region->max[1] = region.max(1);
|
||||
}
|
||||
|
||||
Key GetImage(int clip,
|
||||
int frame,
|
||||
InputMode input_mode,
|
||||
int downscale,
|
||||
const Region* region,
|
||||
const Transform* transform,
|
||||
FloatImage* destination) {
|
||||
float* float_buffer;
|
||||
int width, height, channels;
|
||||
libmv_Region libmv_region;
|
||||
if (region) {
|
||||
get_libmv_region(*region, &libmv_region);
|
||||
}
|
||||
Key cache_key = get_image_callback_(user_data_,
|
||||
clip,
|
||||
frame,
|
||||
get_libmv_input_mode(input_mode),
|
||||
downscale,
|
||||
region != NULL ? &libmv_region : NULL,
|
||||
(libmv_FrameTransform*)transform,
|
||||
&float_buffer,
|
||||
&width,
|
||||
&height,
|
||||
&channels);
|
||||
|
||||
// TODO(sergey): Dumb code for until we can set data directly.
|
||||
FloatImage temp_image(float_buffer, height, width, channels);
|
||||
destination->CopyFrom(temp_image);
|
||||
|
||||
return cache_key;
|
||||
}
|
||||
|
||||
void ReleaseImage(Key cache_key) { release_image_callback_(cache_key); }
|
||||
|
||||
Key GetMaskForTrack(int clip,
|
||||
int frame,
|
||||
int track,
|
||||
const Region* region,
|
||||
FloatImage* destination) {
|
||||
float* float_buffer;
|
||||
int width, height;
|
||||
libmv_Region libmv_region;
|
||||
if (region) {
|
||||
get_libmv_region(*region, &libmv_region);
|
||||
}
|
||||
Key cache_key =
|
||||
get_mask_for_track_callback_(user_data_,
|
||||
clip,
|
||||
frame,
|
||||
track,
|
||||
region != NULL ? &libmv_region : NULL,
|
||||
&float_buffer,
|
||||
&width,
|
||||
&height);
|
||||
|
||||
if (cache_key == NULL) {
|
||||
// No mask for the given track.
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// TODO(sergey): Dumb code for until we can set data directly.
|
||||
FloatImage temp_image(float_buffer, height, width, 1);
|
||||
destination->CopyFrom(temp_image);
|
||||
|
||||
return cache_key;
|
||||
}
|
||||
|
||||
void ReleaseMask(Key key) { release_mask_callback_(key); }
|
||||
|
||||
bool GetClipDimensions(int /*clip*/, int* /*width*/, int* /*height*/) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int NumClips() { return 1; }
|
||||
|
||||
int NumFrames(int /*clip*/) { return 0; }
|
||||
|
||||
libmv_FrameAccessorUserData* user_data_;
|
||||
libmv_GetImageCallback get_image_callback_;
|
||||
libmv_ReleaseImageCallback release_image_callback_;
|
||||
libmv_GetMaskForTrackCallback get_mask_for_track_callback_;
|
||||
libmv_ReleaseMaskCallback release_mask_callback_;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
libmv_FrameAccessor* libmv_FrameAccessorNew(
|
||||
libmv_FrameAccessorUserData* user_data,
|
||||
libmv_GetImageCallback get_image_callback,
|
||||
libmv_ReleaseImageCallback release_image_callback,
|
||||
libmv_GetMaskForTrackCallback get_mask_for_track_callback,
|
||||
libmv_ReleaseMaskCallback release_mask_callback) {
|
||||
return (libmv_FrameAccessor*)LIBMV_OBJECT_NEW(LibmvFrameAccessor,
|
||||
user_data,
|
||||
get_image_callback,
|
||||
release_image_callback,
|
||||
get_mask_for_track_callback,
|
||||
release_mask_callback);
|
||||
}
|
||||
|
||||
void libmv_FrameAccessorDestroy(libmv_FrameAccessor* frame_accessor) {
|
||||
LIBMV_OBJECT_DELETE(frame_accessor, LibmvFrameAccessor);
|
||||
}
|
||||
|
||||
int64_t libmv_frameAccessorgetTransformKey(
|
||||
const libmv_FrameTransform* transform) {
|
||||
return ((FrameAccessor::Transform*)transform)->key();
|
||||
}
|
||||
|
||||
void libmv_frameAccessorgetTransformRun(const libmv_FrameTransform* transform,
|
||||
const libmv_FloatImage* input_image,
|
||||
libmv_FloatImage* output_image) {
|
||||
const FloatImage input(input_image->buffer,
|
||||
input_image->height,
|
||||
input_image->width,
|
||||
input_image->channels);
|
||||
|
||||
FloatImage output;
|
||||
((FrameAccessor::Transform*)transform)->run(input, &output);
|
||||
|
||||
int num_pixels = output.Width() * output.Height() * output.Depth();
|
||||
output_image->buffer = new float[num_pixels];
|
||||
memcpy(output_image->buffer, output.Data(), num_pixels * sizeof(float));
|
||||
output_image->width = output.Width();
|
||||
output_image->height = output.Height();
|
||||
output_image->channels = output.Depth();
|
||||
}
|
||||
71
blender-5.2.0/intern/libmv/intern/frame_accessor.h
Normal file
71
blender-5.2.0/intern/libmv/intern/frame_accessor.h
Normal file
@@ -0,0 +1,71 @@
|
||||
/* SPDX-FileCopyrightText: 2014 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#ifndef LIBMV_C_API_FRAME_ACCESSOR_H_
|
||||
#define LIBMV_C_API_FRAME_ACCESSOR_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "intern/image.h"
|
||||
#include "intern/region.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct libmv_FrameAccessor libmv_FrameAccessor;
|
||||
typedef struct libmv_FrameTransform libmv_FrameTransform;
|
||||
typedef struct libmv_FrameAccessorUserData libmv_FrameAccessorUserData;
|
||||
typedef void* libmv_CacheKey;
|
||||
|
||||
typedef enum {
|
||||
LIBMV_IMAGE_MODE_MONO,
|
||||
LIBMV_IMAGE_MODE_RGBA,
|
||||
} libmv_InputMode;
|
||||
|
||||
typedef libmv_CacheKey (*libmv_GetImageCallback)(
|
||||
libmv_FrameAccessorUserData* user_data,
|
||||
int clip,
|
||||
int frame,
|
||||
libmv_InputMode input_mode,
|
||||
int downscale,
|
||||
const libmv_Region* region,
|
||||
const libmv_FrameTransform* transform,
|
||||
float** destination,
|
||||
int* width,
|
||||
int* height,
|
||||
int* channels);
|
||||
|
||||
typedef void (*libmv_ReleaseImageCallback)(libmv_CacheKey cache_key);
|
||||
|
||||
typedef libmv_CacheKey (*libmv_GetMaskForTrackCallback)(
|
||||
libmv_FrameAccessorUserData* user_data,
|
||||
int clip,
|
||||
int frame,
|
||||
int track,
|
||||
const libmv_Region* region,
|
||||
float** destination,
|
||||
int* width,
|
||||
int* height);
|
||||
typedef void (*libmv_ReleaseMaskCallback)(libmv_CacheKey cache_key);
|
||||
|
||||
libmv_FrameAccessor* libmv_FrameAccessorNew(
|
||||
libmv_FrameAccessorUserData* user_data,
|
||||
libmv_GetImageCallback get_image_callback,
|
||||
libmv_ReleaseImageCallback release_image_callback,
|
||||
libmv_GetMaskForTrackCallback get_mask_for_track_callback,
|
||||
libmv_ReleaseMaskCallback release_mask_callback);
|
||||
void libmv_FrameAccessorDestroy(libmv_FrameAccessor* frame_accessor);
|
||||
|
||||
int64_t libmv_frameAccessorgetTransformKey(
|
||||
const libmv_FrameTransform* transform);
|
||||
|
||||
void libmv_frameAccessorgetTransformRun(const libmv_FrameTransform* transform,
|
||||
const libmv_FloatImage* input_image,
|
||||
libmv_FloatImage* output_image);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // LIBMV_C_API_FRAME_ACCESSOR_H_
|
||||
35
blender-5.2.0/intern/libmv/intern/homography.cc
Normal file
35
blender-5.2.0/intern/libmv/intern/homography.cc
Normal file
@@ -0,0 +1,35 @@
|
||||
/* SPDX-FileCopyrightText: 2011 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "intern/homography.h"
|
||||
#include "intern/utildefines.h"
|
||||
#include "libmv/logging/logging.h"
|
||||
#include "libmv/multiview/homography.h"
|
||||
|
||||
void libmv_homography2DFromCorrespondencesEuc(/* const */ double (*x1)[2],
|
||||
/* const */ double (*x2)[2],
|
||||
int num_points,
|
||||
double H[3][3]) {
|
||||
libmv::Mat x1_mat, x2_mat;
|
||||
libmv::Mat3 H_mat;
|
||||
|
||||
x1_mat.resize(2, num_points);
|
||||
x2_mat.resize(2, num_points);
|
||||
|
||||
for (int i = 0; i < num_points; i++) {
|
||||
x1_mat.col(i) = libmv::Vec2(x1[i][0], x1[i][1]);
|
||||
x2_mat.col(i) = libmv::Vec2(x2[i][0], x2[i][1]);
|
||||
}
|
||||
|
||||
LG << "x1: " << x1_mat;
|
||||
LG << "x2: " << x2_mat;
|
||||
|
||||
libmv::EstimateHomographyOptions options;
|
||||
libmv::EstimateHomography2DFromCorrespondences(
|
||||
x1_mat, x2_mat, options, &H_mat);
|
||||
|
||||
LG << "H: " << H_mat;
|
||||
|
||||
memcpy(H, H_mat.data(), 9 * sizeof(double));
|
||||
}
|
||||
21
blender-5.2.0/intern/libmv/intern/homography.h
Normal file
21
blender-5.2.0/intern/libmv/intern/homography.h
Normal file
@@ -0,0 +1,21 @@
|
||||
/* SPDX-FileCopyrightText: 2011 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#ifndef LIBMV_C_API_HOMOGRAPHY_H_
|
||||
#define LIBMV_C_API_HOMOGRAPHY_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void libmv_homography2DFromCorrespondencesEuc(/* const */ double (*x1)[2],
|
||||
/* const */ double (*x2)[2],
|
||||
int num_points,
|
||||
double H[3][3]);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // LIBMV_C_API_HOMOGRAPHY_H_
|
||||
252
blender-5.2.0/intern/libmv/intern/image.cc
Normal file
252
blender-5.2.0/intern/libmv/intern/image.cc
Normal file
@@ -0,0 +1,252 @@
|
||||
/* SPDX-FileCopyrightText: 2011 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "intern/image.h"
|
||||
#include "intern/utildefines.h"
|
||||
#include "libmv/tracking/track_region.h"
|
||||
|
||||
#include <png.h>
|
||||
#include <cassert>
|
||||
|
||||
using libmv::FloatImage;
|
||||
using libmv::SamplePlanarPatch;
|
||||
|
||||
void libmv_floatImageDestroy(libmv_FloatImage* image) {
|
||||
delete[] image->buffer;
|
||||
}
|
||||
|
||||
/* Image <-> buffers conversion */
|
||||
|
||||
void libmv_byteBufferToFloatImage(const unsigned char* buffer,
|
||||
int width,
|
||||
int height,
|
||||
int channels,
|
||||
FloatImage* image) {
|
||||
image->Resize(height, width, channels);
|
||||
for (int y = 0, a = 0; y < height; y++) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
for (int k = 0; k < channels; k++) {
|
||||
(*image)(y, x, k) = (float)buffer[a++] / 255.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void libmv_floatBufferToFloatImage(const float* buffer,
|
||||
int width,
|
||||
int height,
|
||||
int channels,
|
||||
FloatImage* image) {
|
||||
image->Resize(height, width, channels);
|
||||
for (int y = 0, a = 0; y < height; y++) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
for (int k = 0; k < channels; k++) {
|
||||
(*image)(y, x, k) = buffer[a++];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void libmv_floatImageToFloatBuffer(const FloatImage& image, float* buffer) {
|
||||
for (int y = 0, a = 0; y < image.Height(); y++) {
|
||||
for (int x = 0; x < image.Width(); x++) {
|
||||
for (int k = 0; k < image.Depth(); k++) {
|
||||
buffer[a++] = image(y, x, k);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void libmv_floatImageToByteBuffer(const libmv::FloatImage& image,
|
||||
unsigned char* buffer) {
|
||||
for (int y = 0, a = 0; y < image.Height(); y++) {
|
||||
for (int x = 0; x < image.Width(); x++) {
|
||||
for (int k = 0; k < image.Depth(); k++) {
|
||||
buffer[a++] = image(y, x, k) * 255.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool savePNGImage(png_bytep* row_pointers,
|
||||
int width,
|
||||
int height,
|
||||
int depth,
|
||||
int color_type,
|
||||
const char* file_name) {
|
||||
png_infop info_ptr;
|
||||
png_structp png_ptr;
|
||||
FILE* fp = fopen(file_name, "wb");
|
||||
|
||||
if (fp == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Initialize stuff */
|
||||
png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
|
||||
info_ptr = png_create_info_struct(png_ptr);
|
||||
|
||||
if (setjmp(png_jmpbuf(png_ptr))) {
|
||||
fclose(fp);
|
||||
return false;
|
||||
}
|
||||
|
||||
png_init_io(png_ptr, fp);
|
||||
|
||||
/* Write PNG header. */
|
||||
if (setjmp(png_jmpbuf(png_ptr))) {
|
||||
fclose(fp);
|
||||
return false;
|
||||
}
|
||||
|
||||
png_set_IHDR(png_ptr,
|
||||
info_ptr,
|
||||
width,
|
||||
height,
|
||||
depth,
|
||||
color_type,
|
||||
PNG_INTERLACE_NONE,
|
||||
PNG_COMPRESSION_TYPE_BASE,
|
||||
PNG_FILTER_TYPE_BASE);
|
||||
|
||||
png_write_info(png_ptr, info_ptr);
|
||||
|
||||
/* Write bytes/ */
|
||||
if (setjmp(png_jmpbuf(png_ptr))) {
|
||||
fclose(fp);
|
||||
return false;
|
||||
}
|
||||
|
||||
png_write_image(png_ptr, row_pointers);
|
||||
|
||||
/* End write/ */
|
||||
if (setjmp(png_jmpbuf(png_ptr))) {
|
||||
fclose(fp);
|
||||
return false;
|
||||
}
|
||||
|
||||
png_write_end(png_ptr, NULL);
|
||||
fclose(fp);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool libmv_saveImage(const FloatImage& image,
|
||||
const char* prefix,
|
||||
int x0,
|
||||
int y0) {
|
||||
int x, y;
|
||||
png_bytep* row_pointers;
|
||||
|
||||
assert(image.Depth() == 1);
|
||||
|
||||
row_pointers = new png_bytep[image.Height()];
|
||||
|
||||
for (y = 0; y < image.Height(); y++) {
|
||||
row_pointers[y] = new png_byte[4 * image.Width()];
|
||||
|
||||
for (x = 0; x < image.Width(); x++) {
|
||||
if (x0 == x && image.Height() - y0 - 1 == y) {
|
||||
row_pointers[y][x * 4 + 0] = 255;
|
||||
row_pointers[y][x * 4 + 1] = 0;
|
||||
row_pointers[y][x * 4 + 2] = 0;
|
||||
row_pointers[y][x * 4 + 3] = 255;
|
||||
} else {
|
||||
float pixel = image(image.Height() - y - 1, x, 0);
|
||||
row_pointers[y][x * 4 + 0] = pixel * 255;
|
||||
row_pointers[y][x * 4 + 1] = pixel * 255;
|
||||
row_pointers[y][x * 4 + 2] = pixel * 255;
|
||||
row_pointers[y][x * 4 + 3] = 255;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int image_counter = 0;
|
||||
char file_name[128];
|
||||
snprintf(
|
||||
file_name, sizeof(file_name), "%s_%02d.png", prefix, ++image_counter);
|
||||
bool result = savePNGImage(row_pointers,
|
||||
image.Width(),
|
||||
image.Height(),
|
||||
8,
|
||||
PNG_COLOR_TYPE_RGBA,
|
||||
file_name);
|
||||
|
||||
for (y = 0; y < image.Height(); y++) {
|
||||
delete[] row_pointers[y];
|
||||
}
|
||||
delete[] row_pointers;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void libmv_samplePlanarPatchFloat(const float* image,
|
||||
int width,
|
||||
int height,
|
||||
int channels,
|
||||
const double* xs,
|
||||
const double* ys,
|
||||
int num_samples_x,
|
||||
int num_samples_y,
|
||||
const float* mask,
|
||||
float* patch,
|
||||
double* warped_position_x,
|
||||
double* warped_position_y) {
|
||||
FloatImage libmv_image, libmv_patch, libmv_mask;
|
||||
FloatImage* libmv_mask_for_sample = NULL;
|
||||
|
||||
libmv_floatBufferToFloatImage(image, width, height, channels, &libmv_image);
|
||||
|
||||
if (mask) {
|
||||
libmv_floatBufferToFloatImage(mask, width, height, 1, &libmv_mask);
|
||||
libmv_mask_for_sample = &libmv_mask;
|
||||
}
|
||||
|
||||
SamplePlanarPatch(libmv_image,
|
||||
xs,
|
||||
ys,
|
||||
num_samples_x,
|
||||
num_samples_y,
|
||||
libmv_mask_for_sample,
|
||||
&libmv_patch,
|
||||
warped_position_x,
|
||||
warped_position_y);
|
||||
|
||||
libmv_floatImageToFloatBuffer(libmv_patch, patch);
|
||||
}
|
||||
|
||||
void libmv_samplePlanarPatchByte(const unsigned char* image,
|
||||
int width,
|
||||
int height,
|
||||
int channels,
|
||||
const double* xs,
|
||||
const double* ys,
|
||||
int num_samples_x,
|
||||
int num_samples_y,
|
||||
const float* mask,
|
||||
unsigned char* patch,
|
||||
double* warped_position_x,
|
||||
double* warped_position_y) {
|
||||
libmv::FloatImage libmv_image, libmv_patch, libmv_mask;
|
||||
libmv::FloatImage* libmv_mask_for_sample = NULL;
|
||||
|
||||
libmv_byteBufferToFloatImage(image, width, height, channels, &libmv_image);
|
||||
|
||||
if (mask) {
|
||||
libmv_floatBufferToFloatImage(mask, width, height, 1, &libmv_mask);
|
||||
libmv_mask_for_sample = &libmv_mask;
|
||||
}
|
||||
|
||||
libmv::SamplePlanarPatch(libmv_image,
|
||||
xs,
|
||||
ys,
|
||||
num_samples_x,
|
||||
num_samples_y,
|
||||
libmv_mask_for_sample,
|
||||
&libmv_patch,
|
||||
warped_position_x,
|
||||
warped_position_y);
|
||||
|
||||
libmv_floatImageToByteBuffer(libmv_patch, patch);
|
||||
}
|
||||
77
blender-5.2.0/intern/libmv/intern/image.h
Normal file
77
blender-5.2.0/intern/libmv/intern/image.h
Normal file
@@ -0,0 +1,77 @@
|
||||
/* SPDX-FileCopyrightText: 2011 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#ifndef LIBMV_IMAGE_H_
|
||||
#define LIBMV_IMAGE_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
# include "libmv/image/image.h"
|
||||
void libmv_byteBufferToFloatImage(const unsigned char* buffer,
|
||||
int width,
|
||||
int height,
|
||||
int channels,
|
||||
libmv::FloatImage* image);
|
||||
|
||||
void libmv_floatBufferToFloatImage(const float* buffer,
|
||||
int width,
|
||||
int height,
|
||||
int channels,
|
||||
libmv::FloatImage* image);
|
||||
|
||||
void libmv_floatImageToFloatBuffer(const libmv::FloatImage& image,
|
||||
float* buffer);
|
||||
|
||||
void libmv_floatImageToByteBuffer(const libmv::FloatImage& image,
|
||||
unsigned char* buffer);
|
||||
|
||||
bool libmv_saveImage(const libmv::FloatImage& image,
|
||||
const char* prefix,
|
||||
int x0,
|
||||
int y0);
|
||||
#endif // __cplusplus
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct libmv_FloatImage {
|
||||
float* buffer;
|
||||
int width;
|
||||
int height;
|
||||
int channels;
|
||||
} libmv_FloatImage;
|
||||
|
||||
void libmv_floatImageDestroy(libmv_FloatImage* image);
|
||||
|
||||
void libmv_samplePlanarPatchFloat(const float* image,
|
||||
int width,
|
||||
int height,
|
||||
int channels,
|
||||
const double* xs,
|
||||
const double* ys,
|
||||
int num_samples_x,
|
||||
int num_samples_y,
|
||||
const float* mask,
|
||||
float* patch,
|
||||
double* warped_position_x,
|
||||
double* warped_position_y);
|
||||
|
||||
void libmv_samplePlanarPatchByte(const unsigned char* image,
|
||||
int width,
|
||||
int height,
|
||||
int channels,
|
||||
const double* xs,
|
||||
const double* ys,
|
||||
int num_samples_x,
|
||||
int num_samples_y,
|
||||
const float* mask,
|
||||
unsigned char* patch,
|
||||
double* warped_position_x,
|
||||
double* warped_position_y);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // LIBMV_IMAGE_H_
|
||||
47
blender-5.2.0/intern/libmv/intern/logging.cc
Normal file
47
blender-5.2.0/intern/libmv/intern/logging.cc
Normal file
@@ -0,0 +1,47 @@
|
||||
/* SPDX-FileCopyrightText: 2011 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include <gflags/gflags.h>
|
||||
|
||||
#include "intern/logging.h"
|
||||
#include "intern/utildefines.h"
|
||||
#include "libmv/logging/logging.h"
|
||||
|
||||
static bool is_verbosity_set() {
|
||||
using LIBMV_GFLAGS_NAMESPACE::GetCommandLineOption;
|
||||
|
||||
std::string verbosity;
|
||||
if (!GetCommandLineOption("v", &verbosity)) {
|
||||
return false;
|
||||
}
|
||||
return verbosity != "0";
|
||||
}
|
||||
|
||||
void libmv_initLogging(const char* argv0) {
|
||||
using LIBMV_GFLAGS_NAMESPACE::SetCommandLineOption;
|
||||
google::InitGoogleLogging(argv0);
|
||||
SetCommandLineOption("logtostderr", "1");
|
||||
if (!is_verbosity_set()) {
|
||||
SetCommandLineOption("v", "0");
|
||||
}
|
||||
SetCommandLineOption("stderrthreshold", "0");
|
||||
SetCommandLineOption("minloglevel", "0");
|
||||
}
|
||||
|
||||
void libmv_startDebugLogging(void) {
|
||||
using LIBMV_GFLAGS_NAMESPACE::SetCommandLineOption;
|
||||
SetCommandLineOption("logtostderr", "1");
|
||||
if (!is_verbosity_set()) {
|
||||
SetCommandLineOption("v", "2");
|
||||
}
|
||||
SetCommandLineOption("stderrthreshold", "0");
|
||||
SetCommandLineOption("minloglevel", "0");
|
||||
}
|
||||
|
||||
void libmv_setLoggingVerbosity(int verbosity) {
|
||||
using LIBMV_GFLAGS_NAMESPACE::SetCommandLineOption;
|
||||
char val[10];
|
||||
snprintf(val, sizeof(val), "%d", verbosity);
|
||||
SetCommandLineOption("v", val);
|
||||
}
|
||||
25
blender-5.2.0/intern/libmv/intern/logging.h
Normal file
25
blender-5.2.0/intern/libmv/intern/logging.h
Normal file
@@ -0,0 +1,25 @@
|
||||
/* SPDX-FileCopyrightText: 2011 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#ifndef LIBMV_C_API_LOGGING_H_
|
||||
#define LIBMV_C_API_LOGGING_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Initialize GLog logging.
|
||||
void libmv_initLogging(const char* argv0);
|
||||
|
||||
// Switch Glog to debug logging level.
|
||||
void libmv_startDebugLogging(void);
|
||||
|
||||
// Set GLog logging verbosity level.
|
||||
void libmv_setLoggingVerbosity(int verbosity);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // LIBMV_C_API_LOGGING_H_
|
||||
519
blender-5.2.0/intern/libmv/intern/reconstruction.cc
Normal file
519
blender-5.2.0/intern/libmv/intern/reconstruction.cc
Normal file
@@ -0,0 +1,519 @@
|
||||
/* SPDX-FileCopyrightText: 2011 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "intern/reconstruction.h"
|
||||
#include "intern/camera_intrinsics.h"
|
||||
#include "intern/tracks.h"
|
||||
#include "intern/utildefines.h"
|
||||
|
||||
#include "libmv/logging/logging.h"
|
||||
#include "libmv/simple_pipeline/bundle.h"
|
||||
#include "libmv/simple_pipeline/initialize_reconstruction.h"
|
||||
#include "libmv/simple_pipeline/keyframe_selection.h"
|
||||
#include "libmv/simple_pipeline/modal_solver.h"
|
||||
#include "libmv/simple_pipeline/pipeline.h"
|
||||
#include "libmv/simple_pipeline/reconstruction_scale.h"
|
||||
#include "libmv/simple_pipeline/tracks.h"
|
||||
|
||||
using libmv::CameraIntrinsics;
|
||||
using libmv::EuclideanCamera;
|
||||
using libmv::EuclideanPoint;
|
||||
using libmv::EuclideanReconstruction;
|
||||
using libmv::EuclideanScaleToUnity;
|
||||
using libmv::Marker;
|
||||
using libmv::ProgressUpdateCallback;
|
||||
|
||||
using libmv::EuclideanBundle;
|
||||
using libmv::EuclideanCompleteReconstruction;
|
||||
using libmv::EuclideanReconstructTwoFrames;
|
||||
using libmv::EuclideanReprojectionError;
|
||||
using libmv::PolynomialCameraIntrinsics;
|
||||
using libmv::Tracks;
|
||||
|
||||
struct libmv_Reconstruction {
|
||||
EuclideanReconstruction reconstruction;
|
||||
|
||||
/* Used for per-track average error calculation after reconstruction */
|
||||
Tracks tracks;
|
||||
CameraIntrinsics* intrinsics;
|
||||
|
||||
double error;
|
||||
bool is_valid;
|
||||
};
|
||||
|
||||
namespace {
|
||||
|
||||
class ReconstructUpdateCallback : public ProgressUpdateCallback {
|
||||
public:
|
||||
ReconstructUpdateCallback(
|
||||
reconstruct_progress_update_cb progress_update_callback,
|
||||
void* callback_customdata) {
|
||||
progress_update_callback_ = progress_update_callback;
|
||||
callback_customdata_ = callback_customdata;
|
||||
}
|
||||
|
||||
void invoke(double progress, const char* message) {
|
||||
if (progress_update_callback_) {
|
||||
progress_update_callback_(callback_customdata_, progress, message);
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
reconstruct_progress_update_cb progress_update_callback_;
|
||||
void* callback_customdata_;
|
||||
};
|
||||
|
||||
void libmv_solveRefineIntrinsics(
|
||||
const Tracks& tracks,
|
||||
const int refine_intrinsics,
|
||||
const int bundle_constraints,
|
||||
reconstruct_progress_update_cb progress_update_callback,
|
||||
void* callback_customdata,
|
||||
EuclideanReconstruction* reconstruction,
|
||||
CameraIntrinsics* intrinsics) {
|
||||
/* only a few combinations are supported but trust the caller/ */
|
||||
int bundle_intrinsics = 0;
|
||||
|
||||
if (refine_intrinsics & LIBMV_REFINE_FOCAL_LENGTH) {
|
||||
bundle_intrinsics |= libmv::BUNDLE_FOCAL_LENGTH;
|
||||
}
|
||||
if (refine_intrinsics & LIBMV_REFINE_PRINCIPAL_POINT) {
|
||||
bundle_intrinsics |= libmv::BUNDLE_PRINCIPAL_POINT;
|
||||
}
|
||||
|
||||
#define SET_DISTORTION_FLAG_CHECKED(type, coefficient) \
|
||||
do { \
|
||||
if (refine_intrinsics & LIBMV_REFINE_##type##_DISTORTION_##coefficient) { \
|
||||
bundle_intrinsics |= libmv::BUNDLE_##type##_##coefficient; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
SET_DISTORTION_FLAG_CHECKED(RADIAL, K1);
|
||||
SET_DISTORTION_FLAG_CHECKED(RADIAL, K2);
|
||||
SET_DISTORTION_FLAG_CHECKED(RADIAL, K3);
|
||||
SET_DISTORTION_FLAG_CHECKED(RADIAL, K4);
|
||||
|
||||
SET_DISTORTION_FLAG_CHECKED(TANGENTIAL, P1);
|
||||
SET_DISTORTION_FLAG_CHECKED(TANGENTIAL, P2);
|
||||
|
||||
#undef SET_DISTORTION_FLAG_CHECKED
|
||||
|
||||
progress_update_callback(callback_customdata, 1.0, "Refining solution");
|
||||
|
||||
EuclideanBundleCommonIntrinsics(tracks,
|
||||
bundle_intrinsics,
|
||||
bundle_constraints,
|
||||
reconstruction,
|
||||
intrinsics);
|
||||
}
|
||||
|
||||
void finishReconstruction(
|
||||
const Tracks& tracks,
|
||||
const CameraIntrinsics& camera_intrinsics,
|
||||
libmv_Reconstruction* libmv_reconstruction,
|
||||
reconstruct_progress_update_cb progress_update_callback,
|
||||
void* callback_customdata) {
|
||||
EuclideanReconstruction& reconstruction =
|
||||
libmv_reconstruction->reconstruction;
|
||||
|
||||
/* Reprojection error calculation. */
|
||||
progress_update_callback(callback_customdata, 1.0, "Finishing solution");
|
||||
libmv_reconstruction->tracks = tracks;
|
||||
libmv_reconstruction->error =
|
||||
EuclideanReprojectionError(tracks, reconstruction, camera_intrinsics);
|
||||
}
|
||||
|
||||
bool selectTwoKeyframesBasedOnGRICAndVariance(
|
||||
Tracks& tracks,
|
||||
Tracks& normalized_tracks,
|
||||
CameraIntrinsics& camera_intrinsics,
|
||||
int& keyframe1,
|
||||
int& keyframe2) {
|
||||
libmv::vector<int> keyframes;
|
||||
|
||||
/* Get list of all keyframe candidates first. */
|
||||
SelectKeyframesBasedOnGRICAndVariance(
|
||||
normalized_tracks, camera_intrinsics, keyframes);
|
||||
|
||||
if (keyframes.size() < 2) {
|
||||
LG << "Not enough keyframes detected by GRIC";
|
||||
return false;
|
||||
} else if (keyframes.size() == 2) {
|
||||
keyframe1 = keyframes[0];
|
||||
keyframe2 = keyframes[1];
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Now choose two keyframes with minimal reprojection error after initial
|
||||
* reconstruction choose keyframes with the least reprojection error after
|
||||
* solving from two candidate keyframes.
|
||||
*
|
||||
* In fact, currently libmv returns single pair only, so this code will
|
||||
* not actually run. But in the future this could change, so let's stay
|
||||
* prepared.
|
||||
*/
|
||||
int previous_keyframe = keyframes[0];
|
||||
double best_error = std::numeric_limits<double>::max();
|
||||
for (int i = 1; i < keyframes.size(); i++) {
|
||||
EuclideanReconstruction reconstruction;
|
||||
int current_keyframe = keyframes[i];
|
||||
libmv::vector<Marker> keyframe_markers =
|
||||
normalized_tracks.MarkersForTracksInBothImages(previous_keyframe,
|
||||
current_keyframe);
|
||||
|
||||
Tracks keyframe_tracks(keyframe_markers);
|
||||
|
||||
/* get a solution from two keyframes only */
|
||||
EuclideanReconstructTwoFrames(keyframe_markers, &reconstruction);
|
||||
EuclideanBundle(keyframe_tracks, &reconstruction);
|
||||
EuclideanCompleteReconstruction(keyframe_tracks, &reconstruction, NULL);
|
||||
|
||||
double current_error =
|
||||
EuclideanReprojectionError(tracks, reconstruction, camera_intrinsics);
|
||||
|
||||
LG << "Error between " << previous_keyframe << " and " << current_keyframe
|
||||
<< ": " << current_error;
|
||||
|
||||
if (current_error < best_error) {
|
||||
best_error = current_error;
|
||||
keyframe1 = previous_keyframe;
|
||||
keyframe2 = current_keyframe;
|
||||
}
|
||||
|
||||
previous_keyframe = current_keyframe;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Marker libmv_projectMarker(const EuclideanPoint& point,
|
||||
const EuclideanCamera& camera,
|
||||
const CameraIntrinsics& intrinsics) {
|
||||
libmv::Vec3 projected = camera.R * point.X + camera.t;
|
||||
projected /= projected(2);
|
||||
|
||||
libmv::Marker reprojected_marker;
|
||||
intrinsics.ApplyIntrinsics(
|
||||
projected(0), projected(1), &reprojected_marker.x, &reprojected_marker.y);
|
||||
|
||||
reprojected_marker.image = camera.image;
|
||||
reprojected_marker.track = point.track;
|
||||
return reprojected_marker;
|
||||
}
|
||||
|
||||
void libmv_getNormalizedTracks(const Tracks& tracks,
|
||||
const CameraIntrinsics& camera_intrinsics,
|
||||
Tracks* normalized_tracks) {
|
||||
libmv::vector<Marker> markers = tracks.AllMarkers();
|
||||
for (int i = 0; i < markers.size(); ++i) {
|
||||
Marker& marker = markers[i];
|
||||
camera_intrinsics.InvertIntrinsics(
|
||||
marker.x, marker.y, &marker.x, &marker.y);
|
||||
normalized_tracks->Insert(
|
||||
marker.image, marker.track, marker.x, marker.y, marker.weight);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
libmv_Reconstruction* libmv_solveReconstruction(
|
||||
const libmv_Tracks* libmv_tracks,
|
||||
const libmv_CameraIntrinsicsOptions* libmv_camera_intrinsics_options,
|
||||
libmv_ReconstructionOptions* libmv_reconstruction_options,
|
||||
reconstruct_progress_update_cb progress_update_callback,
|
||||
void* callback_customdata) {
|
||||
libmv_Reconstruction* libmv_reconstruction =
|
||||
LIBMV_OBJECT_NEW(libmv_Reconstruction);
|
||||
|
||||
Tracks& tracks = *((Tracks*)libmv_tracks);
|
||||
EuclideanReconstruction& reconstruction =
|
||||
libmv_reconstruction->reconstruction;
|
||||
|
||||
ReconstructUpdateCallback update_callback =
|
||||
ReconstructUpdateCallback(progress_update_callback, callback_customdata);
|
||||
|
||||
/* Retrieve reconstruction options from C-API to libmv API. */
|
||||
CameraIntrinsics* camera_intrinsics;
|
||||
camera_intrinsics = libmv_reconstruction->intrinsics =
|
||||
libmv_cameraIntrinsicsCreateFromOptions(libmv_camera_intrinsics_options);
|
||||
|
||||
/* Invert the camera intrinsics/ */
|
||||
Tracks normalized_tracks;
|
||||
libmv_getNormalizedTracks(tracks, *camera_intrinsics, &normalized_tracks);
|
||||
|
||||
/* keyframe selection. */
|
||||
int keyframe1 = libmv_reconstruction_options->keyframe1,
|
||||
keyframe2 = libmv_reconstruction_options->keyframe2;
|
||||
|
||||
if (libmv_reconstruction_options->select_keyframes) {
|
||||
LG << "Using automatic keyframe selection";
|
||||
|
||||
update_callback.invoke(0, "Selecting keyframes");
|
||||
|
||||
if (selectTwoKeyframesBasedOnGRICAndVariance(tracks,
|
||||
normalized_tracks,
|
||||
*camera_intrinsics,
|
||||
keyframe1,
|
||||
keyframe2)) {
|
||||
/* so keyframes in the interface would be updated */
|
||||
libmv_reconstruction_options->keyframe1 = keyframe1;
|
||||
libmv_reconstruction_options->keyframe2 = keyframe2;
|
||||
}
|
||||
}
|
||||
|
||||
/* Actual reconstruction. */
|
||||
LG << "frames to init from: " << keyframe1 << " " << keyframe2;
|
||||
|
||||
libmv::vector<Marker> keyframe_markers =
|
||||
normalized_tracks.MarkersForTracksInBothImages(keyframe1, keyframe2);
|
||||
|
||||
LG << "number of markers for init: " << keyframe_markers.size();
|
||||
|
||||
if (keyframe_markers.size() < 16) {
|
||||
LG << "No enough markers to initialize from";
|
||||
libmv_reconstruction->is_valid = false;
|
||||
return libmv_reconstruction;
|
||||
}
|
||||
|
||||
update_callback.invoke(0, "Initial reconstruction");
|
||||
|
||||
if (!EuclideanReconstructTwoFrames(keyframe_markers, &reconstruction)) {
|
||||
LG << "Failed to initialize reconstruction";
|
||||
libmv_reconstruction->is_valid = false;
|
||||
return libmv_reconstruction;
|
||||
}
|
||||
|
||||
EuclideanBundle(normalized_tracks, &reconstruction);
|
||||
EuclideanCompleteReconstruction(
|
||||
normalized_tracks, &reconstruction, &update_callback);
|
||||
|
||||
/* Refinement. */
|
||||
if (libmv_reconstruction_options->refine_intrinsics) {
|
||||
libmv_solveRefineIntrinsics(tracks,
|
||||
libmv_reconstruction_options->refine_intrinsics,
|
||||
libmv::BUNDLE_NO_CONSTRAINTS,
|
||||
progress_update_callback,
|
||||
callback_customdata,
|
||||
&reconstruction,
|
||||
camera_intrinsics);
|
||||
}
|
||||
|
||||
/* Set reconstruction scale to unity. */
|
||||
EuclideanScaleToUnity(&reconstruction);
|
||||
|
||||
/* Finish reconstruction. */
|
||||
finishReconstruction(tracks,
|
||||
*camera_intrinsics,
|
||||
libmv_reconstruction,
|
||||
progress_update_callback,
|
||||
callback_customdata);
|
||||
|
||||
libmv_reconstruction->is_valid = true;
|
||||
return (libmv_Reconstruction*)libmv_reconstruction;
|
||||
}
|
||||
|
||||
libmv_Reconstruction* libmv_solveModal(
|
||||
const libmv_Tracks* libmv_tracks,
|
||||
const libmv_CameraIntrinsicsOptions* libmv_camera_intrinsics_options,
|
||||
const libmv_ReconstructionOptions* libmv_reconstruction_options,
|
||||
reconstruct_progress_update_cb progress_update_callback,
|
||||
void* callback_customdata) {
|
||||
libmv_Reconstruction* libmv_reconstruction =
|
||||
LIBMV_OBJECT_NEW(libmv_Reconstruction);
|
||||
|
||||
Tracks& tracks = *((Tracks*)libmv_tracks);
|
||||
EuclideanReconstruction& reconstruction =
|
||||
libmv_reconstruction->reconstruction;
|
||||
|
||||
ReconstructUpdateCallback update_callback =
|
||||
ReconstructUpdateCallback(progress_update_callback, callback_customdata);
|
||||
|
||||
/* Retrieve reconstruction options from C-API to libmv API. */
|
||||
CameraIntrinsics* camera_intrinsics;
|
||||
camera_intrinsics = libmv_reconstruction->intrinsics =
|
||||
libmv_cameraIntrinsicsCreateFromOptions(libmv_camera_intrinsics_options);
|
||||
|
||||
/* Invert the camera intrinsics. */
|
||||
Tracks normalized_tracks;
|
||||
libmv_getNormalizedTracks(tracks, *camera_intrinsics, &normalized_tracks);
|
||||
|
||||
/* Actual reconstruction. */
|
||||
ModalSolver(normalized_tracks, &reconstruction, &update_callback);
|
||||
|
||||
PolynomialCameraIntrinsics empty_intrinsics;
|
||||
EuclideanBundleCommonIntrinsics(normalized_tracks,
|
||||
libmv::BUNDLE_NO_INTRINSICS,
|
||||
libmv::BUNDLE_NO_TRANSLATION,
|
||||
&reconstruction,
|
||||
&empty_intrinsics);
|
||||
|
||||
/* Refinement. */
|
||||
if (libmv_reconstruction_options->refine_intrinsics) {
|
||||
libmv_solveRefineIntrinsics(tracks,
|
||||
libmv_reconstruction_options->refine_intrinsics,
|
||||
libmv::BUNDLE_NO_TRANSLATION,
|
||||
progress_update_callback,
|
||||
callback_customdata,
|
||||
&reconstruction,
|
||||
camera_intrinsics);
|
||||
}
|
||||
|
||||
/* Finish reconstruction. */
|
||||
finishReconstruction(tracks,
|
||||
*camera_intrinsics,
|
||||
libmv_reconstruction,
|
||||
progress_update_callback,
|
||||
callback_customdata);
|
||||
|
||||
libmv_reconstruction->is_valid = true;
|
||||
return (libmv_Reconstruction*)libmv_reconstruction;
|
||||
}
|
||||
|
||||
int libmv_reconstructionIsValid(libmv_Reconstruction* libmv_reconstruction) {
|
||||
return libmv_reconstruction->is_valid;
|
||||
}
|
||||
|
||||
void libmv_reconstructionDestroy(libmv_Reconstruction* libmv_reconstruction) {
|
||||
LIBMV_OBJECT_DELETE(libmv_reconstruction->intrinsics, CameraIntrinsics);
|
||||
LIBMV_OBJECT_DELETE(libmv_reconstruction, libmv_Reconstruction);
|
||||
}
|
||||
|
||||
int libmv_reprojectionPointForTrack(
|
||||
const libmv_Reconstruction* libmv_reconstruction,
|
||||
int track,
|
||||
double pos[3]) {
|
||||
const EuclideanReconstruction* reconstruction =
|
||||
&libmv_reconstruction->reconstruction;
|
||||
const EuclideanPoint* point = reconstruction->PointForTrack(track);
|
||||
if (point) {
|
||||
pos[0] = point->X[0];
|
||||
pos[1] = point->X[2];
|
||||
pos[2] = point->X[1];
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
double libmv_reprojectionErrorForTrack(
|
||||
const libmv_Reconstruction* libmv_reconstruction, int track) {
|
||||
const EuclideanReconstruction* reconstruction =
|
||||
&libmv_reconstruction->reconstruction;
|
||||
const CameraIntrinsics* intrinsics = libmv_reconstruction->intrinsics;
|
||||
libmv::vector<Marker> markers =
|
||||
libmv_reconstruction->tracks.MarkersForTrack(track);
|
||||
|
||||
int num_reprojected = 0;
|
||||
double total_error = 0.0;
|
||||
|
||||
for (int i = 0; i < markers.size(); ++i) {
|
||||
double weight = markers[i].weight;
|
||||
const EuclideanCamera* camera =
|
||||
reconstruction->CameraForImage(markers[i].image);
|
||||
const EuclideanPoint* point =
|
||||
reconstruction->PointForTrack(markers[i].track);
|
||||
|
||||
if (!camera || !point || weight == 0.0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
num_reprojected++;
|
||||
|
||||
Marker reprojected_marker =
|
||||
libmv_projectMarker(*point, *camera, *intrinsics);
|
||||
double ex = (reprojected_marker.x - markers[i].x) * weight;
|
||||
double ey = (reprojected_marker.y - markers[i].y) * weight;
|
||||
|
||||
total_error += sqrt(ex * ex + ey * ey);
|
||||
}
|
||||
|
||||
return total_error / num_reprojected;
|
||||
}
|
||||
|
||||
double libmv_reprojectionErrorForImage(
|
||||
const libmv_Reconstruction* libmv_reconstruction, int image) {
|
||||
const EuclideanReconstruction* reconstruction =
|
||||
&libmv_reconstruction->reconstruction;
|
||||
const CameraIntrinsics* intrinsics = libmv_reconstruction->intrinsics;
|
||||
libmv::vector<Marker> markers =
|
||||
libmv_reconstruction->tracks.MarkersInImage(image);
|
||||
const EuclideanCamera* camera = reconstruction->CameraForImage(image);
|
||||
int num_reprojected = 0;
|
||||
double total_error = 0.0;
|
||||
|
||||
if (!camera) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
for (int i = 0; i < markers.size(); ++i) {
|
||||
const EuclideanPoint* point =
|
||||
reconstruction->PointForTrack(markers[i].track);
|
||||
|
||||
if (!point) {
|
||||
continue;
|
||||
}
|
||||
|
||||
num_reprojected++;
|
||||
|
||||
Marker reprojected_marker =
|
||||
libmv_projectMarker(*point, *camera, *intrinsics);
|
||||
double ex = (reprojected_marker.x - markers[i].x) * markers[i].weight;
|
||||
double ey = (reprojected_marker.y - markers[i].y) * markers[i].weight;
|
||||
|
||||
total_error += sqrt(ex * ex + ey * ey);
|
||||
}
|
||||
|
||||
return total_error / num_reprojected;
|
||||
}
|
||||
|
||||
int libmv_reprojectionCameraForImage(
|
||||
const libmv_Reconstruction* libmv_reconstruction,
|
||||
int image,
|
||||
double mat[4][4]) {
|
||||
const EuclideanReconstruction* reconstruction =
|
||||
&libmv_reconstruction->reconstruction;
|
||||
const EuclideanCamera* camera = reconstruction->CameraForImage(image);
|
||||
|
||||
if (camera) {
|
||||
for (int j = 0; j < 3; ++j) {
|
||||
for (int k = 0; k < 3; ++k) {
|
||||
int l = k;
|
||||
|
||||
if (k == 1) {
|
||||
l = 2;
|
||||
} else if (k == 2) {
|
||||
l = 1;
|
||||
}
|
||||
|
||||
if (j == 2) {
|
||||
mat[j][l] = -camera->R(j, k);
|
||||
} else {
|
||||
mat[j][l] = camera->R(j, k);
|
||||
}
|
||||
}
|
||||
mat[j][3] = 0.0;
|
||||
}
|
||||
|
||||
libmv::Vec3 optical_center = -camera->R.transpose() * camera->t;
|
||||
|
||||
mat[3][0] = optical_center(0);
|
||||
mat[3][1] = optical_center(2);
|
||||
mat[3][2] = optical_center(1);
|
||||
|
||||
mat[3][3] = 1.0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
double libmv_reprojectionError(
|
||||
const libmv_Reconstruction* libmv_reconstruction) {
|
||||
return libmv_reconstruction->error;
|
||||
}
|
||||
|
||||
libmv_CameraIntrinsics* libmv_reconstructionExtractIntrinsics(
|
||||
libmv_Reconstruction* libmv_reconstruction) {
|
||||
return (libmv_CameraIntrinsics*)libmv_reconstruction->intrinsics;
|
||||
}
|
||||
88
blender-5.2.0/intern/libmv/intern/reconstruction.h
Normal file
88
blender-5.2.0/intern/libmv/intern/reconstruction.h
Normal file
@@ -0,0 +1,88 @@
|
||||
/* SPDX-FileCopyrightText: 2011 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#ifndef LIBMV_C_API_RECONSTRUCTION_H_
|
||||
#define LIBMV_C_API_RECONSTRUCTION_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct libmv_Tracks;
|
||||
struct libmv_CameraIntrinsics;
|
||||
struct libmv_CameraIntrinsicsOptions;
|
||||
|
||||
typedef struct libmv_Reconstruction libmv_Reconstruction;
|
||||
|
||||
enum {
|
||||
LIBMV_REFINE_FOCAL_LENGTH = (1 << 0),
|
||||
LIBMV_REFINE_PRINCIPAL_POINT = (1 << 1),
|
||||
|
||||
LIBMV_REFINE_RADIAL_DISTORTION_K1 = (1 << 2),
|
||||
LIBMV_REFINE_RADIAL_DISTORTION_K2 = (1 << 3),
|
||||
LIBMV_REFINE_RADIAL_DISTORTION_K3 = (1 << 4),
|
||||
LIBMV_REFINE_RADIAL_DISTORTION_K4 = (1 << 5),
|
||||
LIBMV_REFINE_RADIAL_DISTORTION =
|
||||
(LIBMV_REFINE_RADIAL_DISTORTION_K1 | LIBMV_REFINE_RADIAL_DISTORTION_K2 |
|
||||
LIBMV_REFINE_RADIAL_DISTORTION_K3 | LIBMV_REFINE_RADIAL_DISTORTION_K4),
|
||||
|
||||
LIBMV_REFINE_TANGENTIAL_DISTORTION_P1 = (1 << 6),
|
||||
LIBMV_REFINE_TANGENTIAL_DISTORTION_P2 = (1 << 7),
|
||||
LIBMV_REFINE_TANGENTIAL_DISTORTION = (LIBMV_REFINE_TANGENTIAL_DISTORTION_P1 |
|
||||
LIBMV_REFINE_TANGENTIAL_DISTORTION_P2),
|
||||
};
|
||||
|
||||
typedef struct libmv_ReconstructionOptions {
|
||||
int select_keyframes;
|
||||
int keyframe1, keyframe2;
|
||||
int refine_intrinsics;
|
||||
} libmv_ReconstructionOptions;
|
||||
|
||||
typedef void (*reconstruct_progress_update_cb)(void* customdata,
|
||||
double progress,
|
||||
const char* message);
|
||||
|
||||
libmv_Reconstruction* libmv_solveReconstruction(
|
||||
const struct libmv_Tracks* libmv_tracks,
|
||||
const struct libmv_CameraIntrinsicsOptions* libmv_camera_intrinsics_options,
|
||||
libmv_ReconstructionOptions* libmv_reconstruction_options,
|
||||
reconstruct_progress_update_cb progress_update_callback,
|
||||
void* callback_customdata);
|
||||
|
||||
libmv_Reconstruction* libmv_solveModal(
|
||||
const struct libmv_Tracks* libmv_tracks,
|
||||
const struct libmv_CameraIntrinsicsOptions* libmv_camera_intrinsics_options,
|
||||
const libmv_ReconstructionOptions* libmv_reconstruction_options,
|
||||
reconstruct_progress_update_cb progress_update_callback,
|
||||
void* callback_customdata);
|
||||
|
||||
int libmv_reconstructionIsValid(libmv_Reconstruction* libmv_reconstruction);
|
||||
|
||||
void libmv_reconstructionDestroy(libmv_Reconstruction* libmv_reconstruction);
|
||||
|
||||
int libmv_reprojectionPointForTrack(
|
||||
const libmv_Reconstruction* libmv_reconstruction, int track, double pos[3]);
|
||||
|
||||
double libmv_reprojectionErrorForTrack(
|
||||
const libmv_Reconstruction* libmv_reconstruction, int track);
|
||||
|
||||
double libmv_reprojectionErrorForImage(
|
||||
const libmv_Reconstruction* libmv_reconstruction, int image);
|
||||
|
||||
int libmv_reprojectionCameraForImage(
|
||||
const libmv_Reconstruction* libmv_reconstruction,
|
||||
int image,
|
||||
double mat[4][4]);
|
||||
|
||||
double libmv_reprojectionError(
|
||||
const libmv_Reconstruction* libmv_reconstruction);
|
||||
|
||||
struct libmv_CameraIntrinsics* libmv_reconstructionExtractIntrinsics(
|
||||
libmv_Reconstruction* libmv_Reconstruction);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // LIBMV_C_API_RECONSTRUCTION_H_
|
||||
21
blender-5.2.0/intern/libmv/intern/region.h
Normal file
21
blender-5.2.0/intern/libmv/intern/region.h
Normal file
@@ -0,0 +1,21 @@
|
||||
/* SPDX-FileCopyrightText: 2014 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#ifndef LIBMV_C_API_REGION_H_
|
||||
#define LIBMV_C_API_REGION_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct libmv_Region {
|
||||
float min[2];
|
||||
float max[2];
|
||||
} libmv_Region;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // LIBMV_C_API_REGION_H_
|
||||
372
blender-5.2.0/intern/libmv/intern/stub.cc
Normal file
372
blender-5.2.0/intern/libmv/intern/stub.cc
Normal file
@@ -0,0 +1,372 @@
|
||||
/* SPDX-FileCopyrightText: 2013 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "libmv-capi.h"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
/* ************ Logging ************ */
|
||||
|
||||
void libmv_initLogging(const char* /*argv0*/) {
|
||||
}
|
||||
|
||||
void libmv_startDebugLogging(void) {
|
||||
}
|
||||
|
||||
void libmv_setLoggingVerbosity(int /*verbosity*/) {
|
||||
}
|
||||
|
||||
/* ************ Planar tracker ************ */
|
||||
|
||||
/* TrackRegion (new planar tracker) */
|
||||
int libmv_trackRegion(const libmv_TrackRegionOptions* /*options*/,
|
||||
const float* /*image1*/,
|
||||
int /*image1_width*/,
|
||||
int /*image1_height*/,
|
||||
const float* /*image2*/,
|
||||
int /*image2_width*/,
|
||||
int /*image2_height*/,
|
||||
const double* x1,
|
||||
const double* y1,
|
||||
libmv_TrackRegionResult* result,
|
||||
double* x2,
|
||||
double* y2) {
|
||||
/* Convert to doubles for the libmv API. The four corners and the center. */
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
x2[i] = x1[i];
|
||||
y2[i] = y1[i];
|
||||
}
|
||||
|
||||
result->termination = -1;
|
||||
result->termination_reason = "Built without libmv support";
|
||||
result->correlation = 0.0;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void libmv_samplePlanarPatchFloat(const float* /*image*/,
|
||||
int /*width*/,
|
||||
int /*height*/,
|
||||
int /*channels*/,
|
||||
const double* /*xs*/,
|
||||
const double* /*ys*/,
|
||||
int /*num_samples_x*/,
|
||||
int /*num_samples_y*/,
|
||||
const float* /*mask*/,
|
||||
float* /*patch*/,
|
||||
double* /*warped_position_x*/,
|
||||
double* /*warped_position_y*/) {
|
||||
/* TODO(sergey): implement */
|
||||
}
|
||||
|
||||
void libmv_samplePlanarPatchByte(const unsigned char* /*image*/,
|
||||
int /*width*/,
|
||||
int /*height*/,
|
||||
int /*channels*/,
|
||||
const double* /*xs*/,
|
||||
const double* /*ys*/,
|
||||
int /*num_samples_x*/,
|
||||
int /*num_samples_y*/,
|
||||
const float* /*mask*/,
|
||||
unsigned char* /*patch*/,
|
||||
double* /*warped_position_x*/,
|
||||
double* /*warped_position_y*/) {
|
||||
/* TODO(sergey): implement */
|
||||
}
|
||||
|
||||
void libmv_floatImageDestroy(libmv_FloatImage* /*image*/) {
|
||||
}
|
||||
|
||||
/* ************ Tracks ************ */
|
||||
|
||||
libmv_Tracks* libmv_tracksNew(void) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void libmv_tracksInsert(libmv_Tracks* /*libmv_tracks*/,
|
||||
int /*image*/,
|
||||
int /*track*/,
|
||||
double /*x*/,
|
||||
double /*y*/,
|
||||
double /*weight*/) {
|
||||
}
|
||||
|
||||
void libmv_tracksDestroy(libmv_Tracks* /*libmv_tracks*/) {
|
||||
}
|
||||
|
||||
/* ************ Reconstruction solver ************ */
|
||||
|
||||
libmv_Reconstruction* libmv_solveReconstruction(
|
||||
const libmv_Tracks* /*libmv_tracks*/,
|
||||
const libmv_CameraIntrinsicsOptions* /*libmv_camera_intrinsics_options*/,
|
||||
libmv_ReconstructionOptions* /*libmv_reconstruction_options*/,
|
||||
reconstruct_progress_update_cb /*progress_update_callback*/,
|
||||
void* /*callback_customdata*/) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
libmv_Reconstruction* libmv_solveModal(
|
||||
const libmv_Tracks* /*libmv_tracks*/,
|
||||
const libmv_CameraIntrinsicsOptions* /*libmv_camera_intrinsics_options*/,
|
||||
const libmv_ReconstructionOptions* /*libmv_reconstruction_options*/,
|
||||
reconstruct_progress_update_cb /*progress_update_callback*/,
|
||||
void* /*callback_customdata*/) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int libmv_reconstructionIsValid(
|
||||
libmv_Reconstruction* /*libmv_reconstruction*/) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int libmv_reprojectionPointForTrack(
|
||||
const libmv_Reconstruction* /*libmv_reconstruction*/,
|
||||
int /*track*/,
|
||||
double /*pos*/[3]) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
double libmv_reprojectionErrorForTrack(
|
||||
const libmv_Reconstruction* /*libmv_reconstruction*/, int /*track*/) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
double libmv_reprojectionErrorForImage(
|
||||
const libmv_Reconstruction* /*libmv_reconstruction*/, int /*image*/) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
int libmv_reprojectionCameraForImage(
|
||||
const libmv_Reconstruction* /*libmv_reconstruction*/,
|
||||
int /*image*/,
|
||||
double /*mat*/[4][4]) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
double libmv_reprojectionError(
|
||||
const libmv_Reconstruction* /*libmv_reconstruction*/) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
void libmv_reconstructionDestroy(
|
||||
struct libmv_Reconstruction* /*libmv_reconstruction*/) {
|
||||
}
|
||||
|
||||
/* ************ Feature detector ************ */
|
||||
|
||||
libmv_Features* libmv_detectFeaturesByte(const unsigned char* /*image_buffer*/,
|
||||
int /*width*/,
|
||||
int /*height*/,
|
||||
int /*channels*/,
|
||||
libmv_DetectOptions* /*options*/) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct libmv_Features* libmv_detectFeaturesFloat(
|
||||
const float* /*image_buffer*/,
|
||||
int /*width*/,
|
||||
int /*height*/,
|
||||
int /*channels*/,
|
||||
libmv_DetectOptions* /*options*/) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int libmv_countFeatures(const libmv_Features* /*libmv_features*/) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void libmv_getFeature(const libmv_Features* /*libmv_features*/,
|
||||
int /*number*/,
|
||||
double* x,
|
||||
double* y,
|
||||
double* score,
|
||||
double* size) {
|
||||
*x = 0.0;
|
||||
*y = 0.0;
|
||||
*score = 0.0;
|
||||
*size = 0.0;
|
||||
}
|
||||
|
||||
void libmv_featuresDestroy(struct libmv_Features* /*libmv_features*/) {
|
||||
}
|
||||
|
||||
/* ************ Camera intrinsics ************ */
|
||||
|
||||
libmv_CameraIntrinsics* libmv_reconstructionExtractIntrinsics(
|
||||
libmv_Reconstruction* /*libmv_reconstruction*/) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
libmv_CameraIntrinsics* libmv_cameraIntrinsicsNew(
|
||||
const libmv_CameraIntrinsicsOptions* /*libmv_camera_intrinsics_options*/) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
libmv_CameraIntrinsics* libmv_cameraIntrinsicsCopy(
|
||||
const libmv_CameraIntrinsics* /*libmvIntrinsics*/) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void libmv_cameraIntrinsicsDestroy(
|
||||
libmv_CameraIntrinsics* /*libmvIntrinsics*/) {
|
||||
}
|
||||
|
||||
void libmv_cameraIntrinsicsUpdate(
|
||||
const libmv_CameraIntrinsicsOptions* /*libmv_camera_intrinsics_options*/,
|
||||
libmv_CameraIntrinsics* /*libmv_intrinsics*/) {
|
||||
}
|
||||
|
||||
void libmv_cameraIntrinsicsExtractOptions(
|
||||
const libmv_CameraIntrinsics* /*libmv_intrinsics*/,
|
||||
libmv_CameraIntrinsicsOptions* camera_intrinsics_options) {
|
||||
memset(camera_intrinsics_options, 0, sizeof(libmv_CameraIntrinsicsOptions));
|
||||
camera_intrinsics_options->focal_length = 1.0;
|
||||
}
|
||||
|
||||
void libmv_cameraIntrinsicsUndistortByte(
|
||||
const libmv_CameraIntrinsics* /*libmv_intrinsics*/,
|
||||
const unsigned char* source_image,
|
||||
int width,
|
||||
int height,
|
||||
float /*overscan*/,
|
||||
int channels,
|
||||
unsigned char* destination_image) {
|
||||
memcpy(destination_image,
|
||||
source_image,
|
||||
channels * width * height * sizeof(unsigned char));
|
||||
}
|
||||
|
||||
void libmv_cameraIntrinsicsUndistortFloat(
|
||||
const libmv_CameraIntrinsics* /*libmv_intrinsics*/,
|
||||
const float* source_image,
|
||||
int width,
|
||||
int height,
|
||||
float /*overscan*/,
|
||||
int channels,
|
||||
float* destination_image) {
|
||||
memcpy(destination_image,
|
||||
source_image,
|
||||
channels * width * height * sizeof(float));
|
||||
}
|
||||
|
||||
void libmv_cameraIntrinsicsDistortByte(
|
||||
const struct libmv_CameraIntrinsics* /*libmv_intrinsics*/,
|
||||
const unsigned char* source_image,
|
||||
int width,
|
||||
int height,
|
||||
float /*overscan*/,
|
||||
int channels,
|
||||
unsigned char* destination_image) {
|
||||
memcpy(destination_image,
|
||||
source_image,
|
||||
channels * width * height * sizeof(unsigned char));
|
||||
}
|
||||
|
||||
void libmv_cameraIntrinsicsDistortFloat(
|
||||
const libmv_CameraIntrinsics* /*libmv_intrinsics*/,
|
||||
float* source_image,
|
||||
int width,
|
||||
int height,
|
||||
float /*overscan*/,
|
||||
int channels,
|
||||
float* destination_image) {
|
||||
memcpy(destination_image,
|
||||
source_image,
|
||||
channels * width * height * sizeof(float));
|
||||
}
|
||||
|
||||
/* ************ utils ************ */
|
||||
|
||||
void libmv_cameraIntrinsicsApply(
|
||||
const struct libmv_CameraIntrinsics* /*libmv_intrinsics*/,
|
||||
double /*x*/,
|
||||
double /*y*/,
|
||||
double* x1,
|
||||
double* y1) {
|
||||
*x1 = 0.0;
|
||||
*y1 = 0.0;
|
||||
}
|
||||
|
||||
void libmv_cameraIntrinsicsInvert(
|
||||
const struct libmv_CameraIntrinsics* /*libmv_intrinsics*/,
|
||||
double /*x*/,
|
||||
double /*y*/,
|
||||
double* x1,
|
||||
double* y1) {
|
||||
*x1 = 0.0;
|
||||
*y1 = 0.0;
|
||||
}
|
||||
|
||||
void libmv_homography2DFromCorrespondencesEuc(/* const */ double (* /*x1*/)[2],
|
||||
/* const */ double (* /*x2*/)[2],
|
||||
int /*num_points*/,
|
||||
double H[3][3]) {
|
||||
memset(H, 0, sizeof(double[3][3]));
|
||||
H[0][0] = 1.0f;
|
||||
H[1][1] = 1.0f;
|
||||
H[2][2] = 1.0f;
|
||||
}
|
||||
|
||||
/* ************ autotrack ************ */
|
||||
|
||||
libmv_AutoTrack* libmv_autoTrackNew(libmv_FrameAccessor* /*frame_accessor*/) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void libmv_autoTrackDestroy(libmv_AutoTrack* /*libmv_autotrack*/) {
|
||||
}
|
||||
|
||||
void libmv_autoTrackSetOptions(libmv_AutoTrack* /*libmv_autotrack*/,
|
||||
const libmv_AutoTrackOptions* /*options*/) {
|
||||
}
|
||||
|
||||
int libmv_autoTrackMarker(libmv_AutoTrack* /*libmv_autotrack*/,
|
||||
const libmv_TrackRegionOptions* /*libmv_options*/,
|
||||
libmv_Marker* /*libmv_tracker_marker*/,
|
||||
libmv_TrackRegionResult* /*libmv_result*/) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void libmv_autoTrackAddMarker(libmv_AutoTrack* /*libmv_autotrack*/,
|
||||
const libmv_Marker* /*libmv_marker*/) {
|
||||
}
|
||||
|
||||
void libmv_autoTrackSetMarkers(libmv_AutoTrack* /*libmv_autotrack*/,
|
||||
const libmv_Marker* /*libmv_marker-*/,
|
||||
size_t /*num_markers*/) {
|
||||
}
|
||||
|
||||
int libmv_autoTrackGetMarker(libmv_AutoTrack* /*libmv_autotrack*/,
|
||||
int /*clip*/,
|
||||
int /*frame*/,
|
||||
int /*track*/,
|
||||
libmv_Marker* /*libmv_marker*/) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ************ frame accessor ************ */
|
||||
|
||||
libmv_FrameAccessor* libmv_FrameAccessorNew(
|
||||
libmv_FrameAccessorUserData* /*user_data**/,
|
||||
libmv_GetImageCallback /*get_image_callback*/,
|
||||
libmv_ReleaseImageCallback /*release_image_callback*/,
|
||||
libmv_GetMaskForTrackCallback /*get_mask_for_track_callback*/,
|
||||
libmv_ReleaseMaskCallback /*release_mask_callback*/) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void libmv_FrameAccessorDestroy(libmv_FrameAccessor* /*frame_accessor*/) {
|
||||
}
|
||||
|
||||
int64_t libmv_frameAccessorgetTransformKey(
|
||||
const libmv_FrameTransform* /*transform*/) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void libmv_frameAccessorgetTransformRun(
|
||||
const libmv_FrameTransform* /*transform*/,
|
||||
const libmv_FloatImage* /*input_image*/,
|
||||
libmv_FloatImage* /*output_image*/) {
|
||||
}
|
||||
177
blender-5.2.0/intern/libmv/intern/track_region.cc
Normal file
177
blender-5.2.0/intern/libmv/intern/track_region.cc
Normal file
@@ -0,0 +1,177 @@
|
||||
/* SPDX-FileCopyrightText: 2011 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "intern/track_region.h"
|
||||
#include "intern/image.h"
|
||||
#include "intern/utildefines.h"
|
||||
#include "libmv/image/image.h"
|
||||
#include "libmv/logging/logging.h"
|
||||
#include "libmv/tracking/track_region.h"
|
||||
|
||||
/* define this to generate PNG images with content of search areas
|
||||
tracking between which failed */
|
||||
#undef DUMP_FAILURE
|
||||
|
||||
/* define this to generate PNG images with content of search areas
|
||||
on every iteration of tracking */
|
||||
#undef DUMP_ALWAYS
|
||||
|
||||
using libmv::FloatImage;
|
||||
using libmv::TrackRegion;
|
||||
using libmv::TrackRegionOptions;
|
||||
using libmv::TrackRegionResult;
|
||||
|
||||
namespace {
|
||||
|
||||
TrackRegionOptions::Direction convertDirection(
|
||||
libmv_TrackRegionDirection direction) {
|
||||
switch (direction) {
|
||||
case LIBMV_TRACK_REGION_FORWARD: return TrackRegionOptions::FORWARD;
|
||||
case LIBMV_TRACK_REGION_BACKWARD: return TrackRegionOptions::BACKWARD;
|
||||
}
|
||||
|
||||
LOG(FATAL) << "Unhandled tracking direction " << direction
|
||||
<< ", should never happen.";
|
||||
|
||||
return TrackRegionOptions::FORWARD;
|
||||
}
|
||||
|
||||
TrackRegionOptions::Mode convertMotionModelToMode(int motion_model) {
|
||||
switch (motion_model) {
|
||||
#define LIBMV_CONVERT(the_model) \
|
||||
case TrackRegionOptions::the_model: return TrackRegionOptions::the_model;
|
||||
|
||||
LIBMV_CONVERT(TRANSLATION)
|
||||
LIBMV_CONVERT(TRANSLATION_ROTATION)
|
||||
LIBMV_CONVERT(TRANSLATION_SCALE)
|
||||
LIBMV_CONVERT(TRANSLATION_ROTATION_SCALE)
|
||||
LIBMV_CONVERT(AFFINE)
|
||||
LIBMV_CONVERT(HOMOGRAPHY)
|
||||
|
||||
#undef LIBMV_CONVERT
|
||||
}
|
||||
|
||||
LOG(FATAL) << "Unhandled motion model " << motion_model
|
||||
<< ", should never happen.";
|
||||
|
||||
return TrackRegionOptions::TRANSLATION;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void libmv_configureTrackRegionOptions(
|
||||
const libmv_TrackRegionOptions& options,
|
||||
TrackRegionOptions* track_region_options) {
|
||||
track_region_options->direction = convertDirection(options.direction);
|
||||
track_region_options->mode = convertMotionModelToMode(options.motion_model);
|
||||
track_region_options->minimum_correlation = options.minimum_correlation;
|
||||
track_region_options->max_iterations = options.num_iterations;
|
||||
track_region_options->sigma = options.sigma;
|
||||
track_region_options->num_extra_points = 1;
|
||||
track_region_options->image1_mask = NULL;
|
||||
track_region_options->use_brute_initialization = options.use_brute;
|
||||
/* TODO(keir): This will make some cases better, but may be a regression until
|
||||
* the motion model is in. Since this is on trunk, enable it for now.
|
||||
*
|
||||
* TODO(sergey): This gives much worse results on mango footage (see 04_2e)
|
||||
* so disabling for now for until proper prediction model is landed.
|
||||
*
|
||||
* The thing is, currently blender sends input coordinates as the guess to
|
||||
* region tracker and in case of fast motion such an early out ruins the
|
||||
* track.
|
||||
*/
|
||||
track_region_options->attempt_refine_before_brute = false;
|
||||
track_region_options->use_normalized_intensities = options.use_normalization;
|
||||
}
|
||||
|
||||
void libmv_regionTrackergetResult(const TrackRegionResult& track_region_result,
|
||||
libmv_TrackRegionResult* result) {
|
||||
result->termination = (int)track_region_result.termination;
|
||||
result->termination_reason = "";
|
||||
result->correlation = track_region_result.correlation;
|
||||
}
|
||||
|
||||
int libmv_trackRegion(const libmv_TrackRegionOptions* options,
|
||||
const float* image1,
|
||||
int image1_width,
|
||||
int image1_height,
|
||||
const float* image2,
|
||||
int image2_width,
|
||||
int image2_height,
|
||||
const double* x1,
|
||||
const double* y1,
|
||||
libmv_TrackRegionResult* /*result*/,
|
||||
double* x2,
|
||||
double* y2) {
|
||||
double xx1[5], yy1[5];
|
||||
double xx2[5], yy2[5];
|
||||
bool tracking_result = false;
|
||||
|
||||
// Convert to doubles for the libmv api. The four corners and the center.
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
xx1[i] = x1[i];
|
||||
yy1[i] = y1[i];
|
||||
xx2[i] = x2[i];
|
||||
yy2[i] = y2[i];
|
||||
}
|
||||
|
||||
TrackRegionOptions track_region_options;
|
||||
FloatImage image1_mask;
|
||||
|
||||
libmv_configureTrackRegionOptions(*options, &track_region_options);
|
||||
if (options->image1_mask) {
|
||||
libmv_floatBufferToFloatImage(
|
||||
options->image1_mask, image1_width, image1_height, 1, &image1_mask);
|
||||
|
||||
track_region_options.image1_mask = &image1_mask;
|
||||
}
|
||||
|
||||
// Convert from raw float buffers to libmv's FloatImage.
|
||||
FloatImage old_patch, new_patch;
|
||||
libmv_floatBufferToFloatImage(
|
||||
image1, image1_width, image1_height, 1, &old_patch);
|
||||
libmv_floatBufferToFloatImage(
|
||||
image2, image2_width, image2_height, 1, &new_patch);
|
||||
|
||||
TrackRegionResult track_region_result;
|
||||
TrackRegion(old_patch,
|
||||
new_patch,
|
||||
xx1,
|
||||
yy1,
|
||||
track_region_options,
|
||||
xx2,
|
||||
yy2,
|
||||
&track_region_result);
|
||||
|
||||
// Convert to floats for the blender api.
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
x2[i] = xx2[i];
|
||||
y2[i] = yy2[i];
|
||||
}
|
||||
|
||||
// TODO(keir): Update the termination string with failure details.
|
||||
if (track_region_result.termination == TrackRegionResult::CONVERGENCE ||
|
||||
track_region_result.termination == TrackRegionResult::NO_CONVERGENCE) {
|
||||
tracking_result = true;
|
||||
}
|
||||
|
||||
// Debug dump of patches.
|
||||
#if defined(DUMP_FAILURE) || defined(DUMP_ALWAYS)
|
||||
bool need_dump = !tracking_result;
|
||||
|
||||
# ifdef DUMP_ALWAYS
|
||||
need_dump = true;
|
||||
# endif
|
||||
|
||||
if (need_dump) {
|
||||
libmv_saveImage(old_patch, "old_patch", x1[4], y1[4]);
|
||||
libmv_saveImage(new_patch, "new_patch", x2[4], y2[4]);
|
||||
if (options->image1_mask) {
|
||||
libmv_saveImage(image1_mask, "mask", x2[4], y2[4]);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return tracking_result;
|
||||
}
|
||||
65
blender-5.2.0/intern/libmv/intern/track_region.h
Normal file
65
blender-5.2.0/intern/libmv/intern/track_region.h
Normal file
@@ -0,0 +1,65 @@
|
||||
/* SPDX-FileCopyrightText: 2011 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#ifndef LIBMV_C_API_TRACK_REGION_H_
|
||||
#define LIBMV_C_API_TRACK_REGION_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum libmv_TrackRegionDirection {
|
||||
LIBMV_TRACK_REGION_FORWARD,
|
||||
LIBMV_TRACK_REGION_BACKWARD,
|
||||
} libmv_TrackRegionDirection;
|
||||
|
||||
typedef struct libmv_TrackRegionOptions {
|
||||
libmv_TrackRegionDirection direction;
|
||||
int motion_model;
|
||||
int num_iterations;
|
||||
int use_brute;
|
||||
int use_normalization;
|
||||
double minimum_correlation;
|
||||
double sigma;
|
||||
float* image1_mask;
|
||||
} libmv_TrackRegionOptions;
|
||||
|
||||
typedef struct libmv_TrackRegionResult {
|
||||
int termination;
|
||||
const char* termination_reason;
|
||||
double correlation;
|
||||
} libmv_TrackRegionResult;
|
||||
|
||||
#ifdef __cplusplus
|
||||
namespace libmv {
|
||||
struct TrackRegionOptions;
|
||||
struct TrackRegionResult;
|
||||
} // namespace libmv
|
||||
void libmv_configureTrackRegionOptions(
|
||||
const libmv_TrackRegionOptions& options,
|
||||
libmv::TrackRegionOptions* track_region_options);
|
||||
|
||||
void libmv_regionTrackergetResult(
|
||||
const libmv::TrackRegionResult& track_region_result,
|
||||
libmv_TrackRegionResult* result);
|
||||
#endif
|
||||
|
||||
int libmv_trackRegion(const libmv_TrackRegionOptions* options,
|
||||
const float* image1,
|
||||
int image1_width,
|
||||
int image1_height,
|
||||
const float* image2,
|
||||
int image2_width,
|
||||
int image2_height,
|
||||
const double* x1,
|
||||
const double* y1,
|
||||
libmv_TrackRegionResult* result,
|
||||
double* x2,
|
||||
double* y2);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // LIBMV_C_API_PLANAR_TRACKER_H_
|
||||
30
blender-5.2.0/intern/libmv/intern/tracks.cc
Normal file
30
blender-5.2.0/intern/libmv/intern/tracks.cc
Normal file
@@ -0,0 +1,30 @@
|
||||
/* SPDX-FileCopyrightText: 2011 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "intern/tracks.h"
|
||||
#include "intern/utildefines.h"
|
||||
|
||||
#include "libmv/simple_pipeline/tracks.h"
|
||||
|
||||
using libmv::Marker;
|
||||
using libmv::Tracks;
|
||||
|
||||
libmv_Tracks* libmv_tracksNew(void) {
|
||||
Tracks* tracks = LIBMV_OBJECT_NEW(Tracks);
|
||||
|
||||
return (libmv_Tracks*)tracks;
|
||||
}
|
||||
|
||||
void libmv_tracksDestroy(libmv_Tracks* libmv_tracks) {
|
||||
LIBMV_OBJECT_DELETE(libmv_tracks, Tracks);
|
||||
}
|
||||
|
||||
void libmv_tracksInsert(libmv_Tracks* libmv_tracks,
|
||||
int image,
|
||||
int track,
|
||||
double x,
|
||||
double y,
|
||||
double weight) {
|
||||
((Tracks*)libmv_tracks)->Insert(image, track, x, y, weight);
|
||||
}
|
||||
29
blender-5.2.0/intern/libmv/intern/tracks.h
Normal file
29
blender-5.2.0/intern/libmv/intern/tracks.h
Normal file
@@ -0,0 +1,29 @@
|
||||
/* SPDX-FileCopyrightText: 2011 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#ifndef LIBMV_C_API_TRACKS_H_
|
||||
#define LIBMV_C_API_TRACKS_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct libmv_Tracks libmv_Tracks;
|
||||
|
||||
libmv_Tracks* libmv_tracksNew(void);
|
||||
|
||||
void libmv_tracksDestroy(libmv_Tracks* libmv_tracks);
|
||||
|
||||
void libmv_tracksInsert(libmv_Tracks* libmv_tracks,
|
||||
int image,
|
||||
int track,
|
||||
double x,
|
||||
double y,
|
||||
double weight);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // LIBMV_C_API_TRACKS_H_
|
||||
113
blender-5.2.0/intern/libmv/intern/tracksN.cc
Normal file
113
blender-5.2.0/intern/libmv/intern/tracksN.cc
Normal file
@@ -0,0 +1,113 @@
|
||||
/* SPDX-FileCopyrightText: 2011 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "intern/tracksN.h"
|
||||
#include "intern/utildefines.h"
|
||||
#include "libmv/autotrack/marker.h"
|
||||
#include "libmv/autotrack/tracks.h"
|
||||
|
||||
using mv::Marker;
|
||||
using mv::Tracks;
|
||||
|
||||
void libmv_apiMarkerToMarker(const libmv_Marker& libmv_marker, Marker* marker) {
|
||||
marker->clip = libmv_marker.clip;
|
||||
marker->frame = libmv_marker.frame;
|
||||
marker->track = libmv_marker.track;
|
||||
marker->center(0) = libmv_marker.center[0];
|
||||
marker->center(1) = libmv_marker.center[1];
|
||||
for (int i = 0; i < 4; i++) {
|
||||
marker->patch.coordinates(i, 0) = libmv_marker.patch[i][0];
|
||||
marker->patch.coordinates(i, 1) = libmv_marker.patch[i][1];
|
||||
}
|
||||
marker->search_region.min(0) = libmv_marker.search_region_min[0];
|
||||
marker->search_region.min(1) = libmv_marker.search_region_min[1];
|
||||
marker->search_region.max(0) = libmv_marker.search_region_max[0];
|
||||
marker->search_region.max(1) = libmv_marker.search_region_max[1];
|
||||
marker->weight = libmv_marker.weight;
|
||||
marker->source = (Marker::Source)libmv_marker.source;
|
||||
marker->status = (Marker::Status)libmv_marker.status;
|
||||
marker->reference_clip = libmv_marker.reference_clip;
|
||||
marker->reference_frame = libmv_marker.reference_frame;
|
||||
marker->model_type = (Marker::ModelType)libmv_marker.model_type;
|
||||
marker->model_id = libmv_marker.model_id;
|
||||
marker->disabled_channels = libmv_marker.disabled_channels;
|
||||
}
|
||||
|
||||
void libmv_markerToApiMarker(const Marker& marker, libmv_Marker* libmv_marker) {
|
||||
libmv_marker->clip = marker.clip;
|
||||
libmv_marker->frame = marker.frame;
|
||||
libmv_marker->track = marker.track;
|
||||
libmv_marker->center[0] = marker.center(0);
|
||||
libmv_marker->center[1] = marker.center(1);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
libmv_marker->patch[i][0] = marker.patch.coordinates(i, 0);
|
||||
libmv_marker->patch[i][1] = marker.patch.coordinates(i, 1);
|
||||
}
|
||||
libmv_marker->search_region_min[0] = marker.search_region.min(0);
|
||||
libmv_marker->search_region_min[1] = marker.search_region.min(1);
|
||||
libmv_marker->search_region_max[0] = marker.search_region.max(0);
|
||||
libmv_marker->search_region_max[1] = marker.search_region.max(1);
|
||||
libmv_marker->weight = marker.weight;
|
||||
libmv_marker->source = (libmv_MarkerSource)marker.source;
|
||||
libmv_marker->status = (libmv_MarkerStatus)marker.status;
|
||||
libmv_marker->reference_clip = marker.reference_clip;
|
||||
libmv_marker->reference_frame = marker.reference_frame;
|
||||
libmv_marker->model_type = (libmv_MarkerModelType)marker.model_type;
|
||||
libmv_marker->model_id = marker.model_id;
|
||||
libmv_marker->disabled_channels = marker.disabled_channels;
|
||||
}
|
||||
|
||||
libmv_TracksN* libmv_tracksNewN(void) {
|
||||
Tracks* tracks = LIBMV_OBJECT_NEW(Tracks);
|
||||
|
||||
return (libmv_TracksN*)tracks;
|
||||
}
|
||||
|
||||
void libmv_tracksDestroyN(libmv_TracksN* libmv_tracks) {
|
||||
LIBMV_OBJECT_DELETE(libmv_tracks, Tracks);
|
||||
}
|
||||
|
||||
void libmv_tracksAddMarkerN(libmv_TracksN* libmv_tracks,
|
||||
const libmv_Marker* libmv_marker) {
|
||||
Marker marker;
|
||||
libmv_apiMarkerToMarker(*libmv_marker, &marker);
|
||||
((Tracks*)libmv_tracks)->AddMarker(marker);
|
||||
}
|
||||
|
||||
void libmv_tracksGetMarkerN(libmv_TracksN* libmv_tracks,
|
||||
int clip,
|
||||
int frame,
|
||||
int track,
|
||||
libmv_Marker* libmv_marker) {
|
||||
Marker marker;
|
||||
((Tracks*)libmv_tracks)->GetMarker(clip, frame, track, &marker);
|
||||
libmv_markerToApiMarker(marker, libmv_marker);
|
||||
}
|
||||
|
||||
void libmv_tracksRemoveMarkerN(libmv_TracksN* libmv_tracks,
|
||||
int clip,
|
||||
int frame,
|
||||
int track) {
|
||||
((Tracks*)libmv_tracks)->RemoveMarker(clip, frame, track);
|
||||
}
|
||||
|
||||
void libmv_tracksRemoveMarkersForTrack(libmv_TracksN* libmv_tracks, int track) {
|
||||
((Tracks*)libmv_tracks)->RemoveMarkersForTrack(track);
|
||||
}
|
||||
|
||||
int libmv_tracksMaxClipN(libmv_TracksN* libmv_tracks) {
|
||||
return ((Tracks*)libmv_tracks)->MaxClip();
|
||||
}
|
||||
|
||||
int libmv_tracksMaxFrameN(libmv_TracksN* libmv_tracks, int clip) {
|
||||
return ((Tracks*)libmv_tracks)->MaxFrame(clip);
|
||||
}
|
||||
|
||||
int libmv_tracksMaxTrackN(libmv_TracksN* libmv_tracks) {
|
||||
return ((Tracks*)libmv_tracks)->MaxTrack();
|
||||
}
|
||||
|
||||
int libmv_tracksNumMarkersN(libmv_TracksN* libmv_tracks) {
|
||||
return ((Tracks*)libmv_tracks)->NumMarkers();
|
||||
}
|
||||
105
blender-5.2.0/intern/libmv/intern/tracksN.h
Normal file
105
blender-5.2.0/intern/libmv/intern/tracksN.h
Normal file
@@ -0,0 +1,105 @@
|
||||
/* SPDX-FileCopyrightText: 2011 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
// TODO(serrgey): For the time being we're converting simple pipeline
|
||||
// to an autotrack pipeline we call it tracks.
|
||||
// Once we've done with porting we remove N.
|
||||
|
||||
#ifndef LIBMV_C_API_TRACKSN_H_
|
||||
#define LIBMV_C_API_TRACKSN_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct libmv_TracksN libmv_TracksN;
|
||||
|
||||
// Keep order in this enums exactly the same as in mv::Marker.
|
||||
// Otherwise API wouldn't convert the values properly.
|
||||
typedef enum libmv_MarkerSource {
|
||||
LIBMV_MARKER_SOURCE_MANUAL,
|
||||
LIBMV_MARKER_SOURCE_DETECTED,
|
||||
LIBMV_MARKER_SOURCE_TRACKED,
|
||||
LIBMV_MARKER_SOURCE_MATCHED,
|
||||
LIBMV_MARKER_SOURCE_PREDICTED,
|
||||
} libmv_MarkerSource;
|
||||
|
||||
typedef enum libmv_MarkerStatus {
|
||||
LIBMV_MARKER_STATUS_UNKNOWN,
|
||||
LIBMV_MARKER_STATUS_INLIER,
|
||||
LIBMV_MARKER_STATUS_OUTLIER,
|
||||
} libmv_MarkerStatus;
|
||||
|
||||
typedef enum libmv_MarkerModelType {
|
||||
LIBMV_MARKER_MODEL_TYPE_POINT,
|
||||
LIBMV_MARKER_MODEL_TYPE_PLANE,
|
||||
LIBMV_MARKER_MODEL_TYPE_LINE,
|
||||
LIBMV_MARKER_MODEL_TYPE_CUBE,
|
||||
} libmv_MarkerModelType;
|
||||
|
||||
enum libmv_MarkerChannel {
|
||||
LIBMV_MARKER_CHANNEL_R = (1 << 0),
|
||||
LIBMV_MARKER_CHANNEL_G = (1 << 1),
|
||||
LIBMV_MARKER_CHANNEL_B = (1 << 2),
|
||||
};
|
||||
|
||||
typedef struct libmv_Marker {
|
||||
int clip;
|
||||
int frame;
|
||||
int track;
|
||||
float center[2];
|
||||
float patch[4][2];
|
||||
float search_region_min[2];
|
||||
float search_region_max[2];
|
||||
float weight;
|
||||
libmv_MarkerSource source;
|
||||
libmv_MarkerStatus status;
|
||||
int reference_clip;
|
||||
int reference_frame;
|
||||
libmv_MarkerModelType model_type;
|
||||
int model_id;
|
||||
int disabled_channels;
|
||||
} libmv_Marker;
|
||||
|
||||
#ifdef __cplusplus
|
||||
namespace mv {
|
||||
struct Marker;
|
||||
}
|
||||
void libmv_apiMarkerToMarker(const libmv_Marker& libmv_marker,
|
||||
mv::Marker* marker);
|
||||
|
||||
void libmv_markerToApiMarker(const mv::Marker& marker,
|
||||
libmv_Marker* libmv_marker);
|
||||
#endif
|
||||
|
||||
libmv_TracksN* libmv_tracksNewN(void);
|
||||
|
||||
void libmv_tracksDestroyN(libmv_TracksN* libmv_tracks);
|
||||
|
||||
void libmv_tracksAddMarkerN(libmv_TracksN* libmv_tracks,
|
||||
const libmv_Marker* libmv_marker);
|
||||
|
||||
void libmv_tracksGetMarkerN(libmv_TracksN* libmv_tracks,
|
||||
int clip,
|
||||
int frame,
|
||||
int track,
|
||||
libmv_Marker* libmv_marker);
|
||||
|
||||
void libmv_tracksRemoveMarkerN(libmv_TracksN* libmv_tracks,
|
||||
int clip,
|
||||
int frame,
|
||||
int track);
|
||||
|
||||
void libmv_tracksRemoveMarkersForTrack(libmv_TracksN* libmv_tracks, int track);
|
||||
|
||||
int libmv_tracksMaxClipN(libmv_TracksN* libmv_tracks);
|
||||
int libmv_tracksMaxFrameN(libmv_TracksN* libmv_tracks, int clip);
|
||||
int libmv_tracksMaxTrackN(libmv_TracksN* libmv_tracks);
|
||||
int libmv_tracksNumMarkersN(libmv_TracksN* libmv_tracks);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // LIBMV_C_API_TRACKS_H_
|
||||
60
blender-5.2.0/intern/libmv/intern/utildefines.h
Normal file
60
blender-5.2.0/intern/libmv/intern/utildefines.h
Normal file
@@ -0,0 +1,60 @@
|
||||
/* SPDX-FileCopyrightText: 2013 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#ifndef LIBMV_C_API_UTILDEFINES_H_
|
||||
#define LIBMV_C_API_UTILDEFINES_H_
|
||||
|
||||
#if defined(_MSC_VER) && _MSC_VER < 1900
|
||||
# define __func__ __FUNCTION__
|
||||
# define snprintf _snprintf
|
||||
#endif
|
||||
|
||||
#ifdef WITH_LIBMV_GUARDED_ALLOC
|
||||
# include "MEM_guardedalloc.h"
|
||||
# if defined __GNUC__
|
||||
# define LIBMV_OBJECT_NEW(type, args...) \
|
||||
new (MEM_new_uninitialized(sizeof(type), __func__)) type(args)
|
||||
# else
|
||||
# define LIBMV_OBJECT_NEW(type, ...) \
|
||||
new (MEM_new_uninitialized(sizeof(type), __FUNCTION__)) type(__VA_ARGS__)
|
||||
# endif
|
||||
# define LIBMV_OBJECT_DELETE(what, type) \
|
||||
{ \
|
||||
if (what) { \
|
||||
((type*)what)->~type(); \
|
||||
MEM_delete_void(const_cast<void*>(static_cast<const void*>(what))); \
|
||||
} \
|
||||
} \
|
||||
(void)0
|
||||
# define LIBMV_STRUCT_NEW(type, count) \
|
||||
(type*)MEM_new_uninitialized(sizeof(type) * count, __func__)
|
||||
# define LIBMV_STRUCT_DELETE(what) \
|
||||
MEM_delete_void(const_cast<void*>(static_cast<const void*>(what)))
|
||||
#else
|
||||
// Need this to keep libmv-capi potentially standalone.
|
||||
# if defined __GNUC__ || defined __sun
|
||||
# define LIBMV_OBJECT_NEW(type, args...) \
|
||||
new (malloc(sizeof(type))) type(args)
|
||||
# else
|
||||
# define LIBMV_OBJECT_NEW(type, ...) \
|
||||
new (malloc(sizeof(type))) type(__VA_ARGS__)
|
||||
# endif
|
||||
# define LIBMV_OBJECT_DELETE(what, type) \
|
||||
{ \
|
||||
if (what) { \
|
||||
((type*)(what))->~type(); \
|
||||
free(what); \
|
||||
} \
|
||||
} \
|
||||
(void)0
|
||||
# define LIBMV_STRUCT_NEW(type, count) (type*)malloc(sizeof(type) * count)
|
||||
# define LIBMV_STRUCT_DELETE(what) \
|
||||
{ \
|
||||
if (what) \
|
||||
free(what); \
|
||||
} \
|
||||
(void)0
|
||||
#endif
|
||||
|
||||
#endif // LIBMV_C_API_UTILDEFINES_H_
|
||||
20
blender-5.2.0/intern/libmv/libmv-capi.h
Normal file
20
blender-5.2.0/intern/libmv/libmv-capi.h
Normal file
@@ -0,0 +1,20 @@
|
||||
/* SPDX-FileCopyrightText: 2011 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#ifndef LIBMV_C_API_H
|
||||
#define LIBMV_C_API_H
|
||||
|
||||
#include "intern/autotrack.h" // IWYU pragma: export
|
||||
#include "intern/camera_intrinsics.h" // IWYU pragma: export
|
||||
#include "intern/detector.h" // IWYU pragma: export
|
||||
#include "intern/frame_accessor.h" // IWYU pragma: export
|
||||
#include "intern/homography.h" // IWYU pragma: export
|
||||
#include "intern/image.h" // IWYU pragma: export
|
||||
#include "intern/logging.h" // IWYU pragma: export
|
||||
#include "intern/reconstruction.h" // IWYU pragma: export
|
||||
#include "intern/track_region.h" // IWYU pragma: export
|
||||
#include "intern/tracks.h" // IWYU pragma: export
|
||||
#include "intern/tracksN.h" // IWYU pragma: export
|
||||
|
||||
#endif // LIBMV_C_API_H
|
||||
317
blender-5.2.0/intern/libmv/libmv/autotrack/autotrack.cc
Normal file
317
blender-5.2.0/intern/libmv/libmv/autotrack/autotrack.cc
Normal file
@@ -0,0 +1,317 @@
|
||||
// Copyright (c) 2014 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.
|
||||
//
|
||||
// Author: mierle@gmail.com (Keir Mierle)
|
||||
|
||||
#include "libmv/autotrack/autotrack.h"
|
||||
#include "libmv/autotrack/frame_accessor.h"
|
||||
#include "libmv/autotrack/predict_tracks.h"
|
||||
#include "libmv/autotrack/quad.h"
|
||||
#include "libmv/base/scoped_ptr.h"
|
||||
#include "libmv/logging/logging.h"
|
||||
#include "libmv/numeric/numeric.h"
|
||||
|
||||
namespace mv {
|
||||
|
||||
namespace {
|
||||
|
||||
class DisableChannelsTransform : public FrameAccessor::Transform {
|
||||
public:
|
||||
DisableChannelsTransform(int disabled_channels)
|
||||
: disabled_channels_(disabled_channels) {}
|
||||
|
||||
int64_t key() const { return disabled_channels_; }
|
||||
|
||||
void run(const FloatImage& input, FloatImage* output) const {
|
||||
bool disable_red = (disabled_channels_ & Marker::CHANNEL_R) != 0,
|
||||
disable_green = (disabled_channels_ & Marker::CHANNEL_G) != 0,
|
||||
disable_blue = (disabled_channels_ & Marker::CHANNEL_B) != 0;
|
||||
|
||||
LG << "Disabling channels: " << (disable_red ? "R " : "")
|
||||
<< (disable_green ? "G " : "") << (disable_blue ? "B" : "");
|
||||
|
||||
// It's important to rescale the resultappropriately so that e.g. if only
|
||||
// blue is selected, it's not zeroed out.
|
||||
float scale = (disable_red ? 0.0f : 0.2126f) +
|
||||
(disable_green ? 0.0f : 0.7152f) +
|
||||
(disable_blue ? 0.0f : 0.0722f);
|
||||
|
||||
output->Resize(input.Height(), input.Width(), 1);
|
||||
for (int y = 0; y < input.Height(); y++) {
|
||||
for (int x = 0; x < input.Width(); x++) {
|
||||
float r = disable_red ? 0.0f : input(y, x, 0);
|
||||
float g = disable_green ? 0.0f : input(y, x, 1);
|
||||
float b = disable_blue ? 0.0f : input(y, x, 2);
|
||||
(*output)(y, x, 0) = (0.2126f * r + 0.7152f * g + 0.0722f * b) / scale;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
// Bitfield representing visible channels, bits are from Marker::Channel.
|
||||
int disabled_channels_;
|
||||
};
|
||||
|
||||
template <typename QuadT, typename ArrayT>
|
||||
void QuadToArrays(const QuadT& quad, ArrayT* x, ArrayT* y) {
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
x[i] = quad.coordinates(i, 0);
|
||||
y[i] = quad.coordinates(i, 1);
|
||||
}
|
||||
}
|
||||
|
||||
void MarkerToArrays(const Marker& marker, double* x, double* y) {
|
||||
Quad2Df offset_quad = marker.patch;
|
||||
Vec2f origin = marker.search_region.Rounded().min;
|
||||
offset_quad.coordinates.rowwise() -= origin.transpose();
|
||||
QuadToArrays(offset_quad, x, y);
|
||||
x[4] = marker.center.x() - origin(0);
|
||||
y[4] = marker.center.y() - origin(1);
|
||||
}
|
||||
|
||||
FrameAccessor::Key GetImageForMarker(const Marker& marker,
|
||||
FrameAccessor* frame_accessor,
|
||||
FloatImage* image) {
|
||||
// TODO(sergey): Currently we pass float region to the accessor,
|
||||
// but we don't want the accessor to decide the rounding, so we
|
||||
// do rounding here.
|
||||
// Ideally we would need to pass IntRegion to the frame accessor.
|
||||
Region region = marker.search_region.Rounded();
|
||||
libmv::scoped_ptr<FrameAccessor::Transform> transform = NULL;
|
||||
if (marker.disabled_channels != 0) {
|
||||
transform.reset(new DisableChannelsTransform(marker.disabled_channels));
|
||||
}
|
||||
return frame_accessor->GetImage(marker.clip,
|
||||
marker.frame,
|
||||
FrameAccessor::MONO,
|
||||
0, // No downscale for now.
|
||||
®ion,
|
||||
transform.get(),
|
||||
image);
|
||||
}
|
||||
|
||||
FrameAccessor::Key GetMaskForMarker(const Marker& marker,
|
||||
FrameAccessor* frame_accessor,
|
||||
FloatImage* mask) {
|
||||
Region region = marker.search_region.Rounded();
|
||||
return frame_accessor->GetMaskForTrack(
|
||||
marker.clip, marker.frame, marker.track, ®ion, mask);
|
||||
}
|
||||
|
||||
PredictDirection getPredictDirection(const TrackRegionOptions* track_options) {
|
||||
switch (track_options->direction) {
|
||||
case TrackRegionOptions::FORWARD: return PredictDirection::FORWARD;
|
||||
case TrackRegionOptions::BACKWARD: return PredictDirection::BACKWARD;
|
||||
}
|
||||
|
||||
LOG(FATAL) << "Unhandled tracking direction " << track_options->direction
|
||||
<< ", should never happen.";
|
||||
|
||||
return PredictDirection::AUTO;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool AutoTrack::TrackMarker(Marker* tracked_marker,
|
||||
TrackRegionResult* result,
|
||||
const TrackRegionOptions* track_options) {
|
||||
// Try to predict the location of the second marker.
|
||||
const PredictDirection predict_direction = getPredictDirection(track_options);
|
||||
bool predicted_position = false;
|
||||
if (PredictMarkerPosition(tracks_, predict_direction, tracked_marker)) {
|
||||
LG << "Successfully predicted!";
|
||||
predicted_position = true;
|
||||
} else {
|
||||
LG << "Prediction failed; trying to track anyway.";
|
||||
}
|
||||
|
||||
Marker reference_marker;
|
||||
tracks_.GetMarker(tracked_marker->reference_clip,
|
||||
tracked_marker->reference_frame,
|
||||
tracked_marker->track,
|
||||
&reference_marker);
|
||||
|
||||
// Convert markers into the format expected by TrackRegion.
|
||||
double x1[5], y1[5];
|
||||
MarkerToArrays(reference_marker, x1, y1);
|
||||
|
||||
double x2[5], y2[5];
|
||||
MarkerToArrays(*tracked_marker, x2, y2);
|
||||
|
||||
// TODO(keir): Technically this could take a smaller slice from the source
|
||||
// image instead of taking one the size of the search window.
|
||||
FloatImage reference_image;
|
||||
FrameAccessor::Key reference_key =
|
||||
GetImageForMarker(reference_marker, frame_accessor_, &reference_image);
|
||||
if (!reference_key) {
|
||||
LG << "Couldn't get frame for reference marker: " << reference_marker;
|
||||
return false;
|
||||
}
|
||||
|
||||
FloatImage reference_mask;
|
||||
FrameAccessor::Key reference_mask_key =
|
||||
GetMaskForMarker(reference_marker, frame_accessor_, &reference_mask);
|
||||
|
||||
FloatImage tracked_image;
|
||||
FrameAccessor::Key tracked_key =
|
||||
GetImageForMarker(*tracked_marker, frame_accessor_, &tracked_image);
|
||||
if (!tracked_key) {
|
||||
frame_accessor_->ReleaseImage(reference_key);
|
||||
LG << "Couldn't get frame for tracked marker: " << tracked_marker;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Store original position before tracking, so we can claculate offset later.
|
||||
Vec2f original_center = tracked_marker->center;
|
||||
|
||||
// Do the tracking!
|
||||
TrackRegionOptions local_track_region_options;
|
||||
if (track_options) {
|
||||
local_track_region_options = *track_options;
|
||||
}
|
||||
if (reference_mask_key != NULL) {
|
||||
LG << "Using mask for reference marker: " << reference_marker;
|
||||
local_track_region_options.image1_mask = &reference_mask;
|
||||
}
|
||||
local_track_region_options.num_extra_points = 1; // For center point.
|
||||
local_track_region_options.attempt_refine_before_brute = predicted_position;
|
||||
TrackRegion(reference_image,
|
||||
tracked_image,
|
||||
x1,
|
||||
y1,
|
||||
local_track_region_options,
|
||||
x2,
|
||||
y2,
|
||||
result);
|
||||
|
||||
// Copy results over the tracked marker.
|
||||
Vec2f tracked_origin = tracked_marker->search_region.Rounded().min;
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
tracked_marker->patch.coordinates(i, 0) = x2[i] + tracked_origin[0];
|
||||
tracked_marker->patch.coordinates(i, 1) = y2[i] + tracked_origin[1];
|
||||
}
|
||||
tracked_marker->center(0) = x2[4] + tracked_origin[0];
|
||||
tracked_marker->center(1) = y2[4] + tracked_origin[1];
|
||||
Vec2f delta = tracked_marker->center - original_center;
|
||||
tracked_marker->search_region.Offset(delta);
|
||||
tracked_marker->source = Marker::TRACKED;
|
||||
tracked_marker->status = Marker::UNKNOWN;
|
||||
tracked_marker->reference_clip = reference_marker.clip;
|
||||
tracked_marker->reference_frame = reference_marker.frame;
|
||||
|
||||
// Release the images and masks from the accessor cache.
|
||||
frame_accessor_->ReleaseImage(reference_key);
|
||||
frame_accessor_->ReleaseImage(tracked_key);
|
||||
frame_accessor_->ReleaseMask(reference_mask_key);
|
||||
|
||||
// TODO(keir): Possibly the return here should get removed since the results
|
||||
// are part of TrackResult. However, eventually the autotrack stuff will have
|
||||
// extra status (e.g. prediction fail, etc) that should get included.
|
||||
return true;
|
||||
}
|
||||
|
||||
void AutoTrack::AddMarker(const Marker& marker) {
|
||||
tracks_.AddMarker(marker);
|
||||
}
|
||||
|
||||
void AutoTrack::SetMarkers(vector<Marker>* markers) {
|
||||
tracks_.SetMarkers(markers);
|
||||
}
|
||||
|
||||
bool AutoTrack::GetMarker(int clip,
|
||||
int frame,
|
||||
int track,
|
||||
Marker* markers) const {
|
||||
return tracks_.GetMarker(clip, frame, track, markers);
|
||||
}
|
||||
|
||||
void AutoTrack::DetectAndTrack(const DetectAndTrackOptions& options) {
|
||||
int num_clips = frame_accessor_->NumClips();
|
||||
for (int clip = 0; clip < num_clips; ++clip) {
|
||||
int num_frames = frame_accessor_->NumFrames(clip);
|
||||
vector<Marker> previous_frame_markers;
|
||||
// Q: How to decide track #s when detecting?
|
||||
// Q: How to match markers from previous frame? set of prev frame tracks?
|
||||
// Q: How to decide what markers should get tracked and which ones should
|
||||
// not?
|
||||
for (int frame = 0; frame < num_frames; ++frame) {
|
||||
if (Cancelled()) {
|
||||
LG << "Got cancel message while detecting and tracking...";
|
||||
return;
|
||||
}
|
||||
// First, get or detect markers for this frame.
|
||||
vector<Marker> this_frame_markers;
|
||||
tracks_.GetMarkersInFrame(clip, frame, &this_frame_markers);
|
||||
LG << "Clip " << clip << ", frame " << frame << " have "
|
||||
<< this_frame_markers.size();
|
||||
if (this_frame_markers.size() < options.min_num_features) {
|
||||
DetectFeaturesInFrame(clip, frame);
|
||||
this_frame_markers.clear();
|
||||
tracks_.GetMarkersInFrame(clip, frame, &this_frame_markers);
|
||||
LG << "... detected " << this_frame_markers.size() << " features.";
|
||||
}
|
||||
if (previous_frame_markers.empty()) {
|
||||
LG << "First frame; skipping tracking stage.";
|
||||
previous_frame_markers.swap(this_frame_markers);
|
||||
continue;
|
||||
}
|
||||
// Second, find tracks that should get tracked forward into this frame.
|
||||
// To avoid tracking markers that are already tracked to this frame, make
|
||||
// a sorted set of the tracks that exist in the last frame.
|
||||
vector<int> tracks_in_this_frame;
|
||||
for (int i = 0; i < this_frame_markers.size(); ++i) {
|
||||
tracks_in_this_frame.push_back(this_frame_markers[i].track);
|
||||
}
|
||||
std::sort(tracks_in_this_frame.begin(), tracks_in_this_frame.end());
|
||||
|
||||
// Find tracks in the previous frame that are not in this one.
|
||||
vector<Marker*> previous_frame_markers_to_track;
|
||||
for (int i = 0; i < previous_frame_markers.size(); ++i) {
|
||||
if (std::binary_search(tracks_in_this_frame.begin(),
|
||||
tracks_in_this_frame.end(),
|
||||
previous_frame_markers[i].track)) {
|
||||
continue;
|
||||
}
|
||||
previous_frame_markers_to_track.push_back(&previous_frame_markers[i]);
|
||||
}
|
||||
|
||||
// Finally track the markers from the last frame into this one.
|
||||
// TODO(keir): Use OMP.
|
||||
for (int i = 0; i < previous_frame_markers_to_track.size(); ++i) {
|
||||
Marker this_frame_marker = *previous_frame_markers_to_track[i];
|
||||
this_frame_marker.frame = frame;
|
||||
LG << "Tracking: " << this_frame_marker;
|
||||
TrackRegionResult result;
|
||||
TrackMarker(&this_frame_marker, &result);
|
||||
if (result.is_usable()) {
|
||||
LG << "Success: " << this_frame_marker;
|
||||
AddMarker(this_frame_marker);
|
||||
this_frame_markers.push_back(this_frame_marker);
|
||||
} else {
|
||||
LG << "Failed to track: " << this_frame_marker;
|
||||
}
|
||||
}
|
||||
// Put the markers from this frame
|
||||
previous_frame_markers.swap(this_frame_markers);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mv
|
||||
224
blender-5.2.0/intern/libmv/libmv/autotrack/autotrack.h
Normal file
224
blender-5.2.0/intern/libmv/libmv/autotrack/autotrack.h
Normal file
@@ -0,0 +1,224 @@
|
||||
// Copyright (c) 2014 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.
|
||||
//
|
||||
// Author: mierle@gmail.com (Keir Mierle)
|
||||
|
||||
#ifndef LIBMV_AUTOTRACK_AUTOTRACK_H_
|
||||
#define LIBMV_AUTOTRACK_AUTOTRACK_H_
|
||||
|
||||
#include "libmv/autotrack/region.h"
|
||||
#include "libmv/autotrack/tracks.h"
|
||||
#include "libmv/tracking/track_region.h"
|
||||
|
||||
namespace libmv {
|
||||
class CameraIntrinsics;
|
||||
};
|
||||
|
||||
namespace mv {
|
||||
|
||||
using libmv::CameraIntrinsics;
|
||||
using libmv::TrackRegionOptions;
|
||||
using libmv::TrackRegionResult;
|
||||
|
||||
struct FrameAccessor;
|
||||
class OperationListener;
|
||||
|
||||
// The coordinator of all tracking operations; keeps track of all state
|
||||
// relating to tracking and reconstruction; for example, 2D tracks and motion
|
||||
// models, reconstructed cameras, points, and planes; tracking settings; etc.
|
||||
//
|
||||
// Typical usage for full autotrack:
|
||||
//
|
||||
// AutoTrack auto_track(image_accessor);
|
||||
// auto_track.SetNumFramesInClip(0, 10);
|
||||
// auto_track.SetNumFramesInClip(1, 54);
|
||||
// auto_track.AutoTrack()
|
||||
//
|
||||
// It is also possible to specify options to control the reconstruction.
|
||||
// Furthermore, the individual methods of reconstruction are exposed to make it
|
||||
// possible to interact with the pipeline as it runs. For example, to track one
|
||||
// marker across frames,
|
||||
//
|
||||
// AutoTrack auto_track(image_accessor);
|
||||
// auto_track.SetNumFramesInClip(0, 10);
|
||||
// auto_track.SetNumFramesInClip(1, 54);
|
||||
// auto_track.AddMarker(...);
|
||||
// auto_track.TrackMarkerToFrame(int clip1, int frame1,
|
||||
// int clip2, int frame2,
|
||||
// options?)
|
||||
//
|
||||
class AutoTrack {
|
||||
public:
|
||||
struct Options {
|
||||
// Default configuration for 2D tracking when calling TrackMarkerToFrame().
|
||||
TrackRegionOptions track_region;
|
||||
|
||||
// Default search window for region tracking, in absolute frame pixels.
|
||||
Region search_region;
|
||||
};
|
||||
|
||||
AutoTrack(FrameAccessor* frame_accessor) : frame_accessor_(frame_accessor) {}
|
||||
|
||||
// Marker manipulation.
|
||||
// Clip manipulation.
|
||||
|
||||
// Set the number of clips. These clips will get accessed from the frame
|
||||
// accessor, matches between frames found, and a reconstruction created.
|
||||
// void SetNumFrames(int clip, int num_frames);
|
||||
|
||||
// Tracking & Matching
|
||||
|
||||
// Find the marker for the track in the frame indicated by the marker.
|
||||
// Caller maintains ownership of *result and *tracked_marker.
|
||||
bool TrackMarker(Marker* tracked_marker,
|
||||
TrackRegionResult* result,
|
||||
const TrackRegionOptions* track_options = NULL);
|
||||
|
||||
// Wrapper around Tracks API; however these may add additional processing.
|
||||
void AddMarker(const Marker& tracked_marker);
|
||||
void SetMarkers(vector<Marker>* markers);
|
||||
bool GetMarker(int clip, int frame, int track, Marker* marker) const;
|
||||
|
||||
// TODO(keir): Implement frame matching! This could be very cool for loop
|
||||
// closing and connecting across clips.
|
||||
// void MatchFrames(int clip1, int frame1, int clip2, int frame2) {}
|
||||
|
||||
// Wrapper around the Reconstruction API.
|
||||
// Returns the new ID.
|
||||
int AddCameraIntrinsics(CameraIntrinsics* intrinsics) {
|
||||
(void)intrinsics;
|
||||
return 0;
|
||||
} // XXX
|
||||
int SetClipIntrinsics(int clip, int intrinsics) {
|
||||
(void)clip;
|
||||
(void)intrinsics;
|
||||
return 0;
|
||||
} // XXX
|
||||
|
||||
enum Motion {
|
||||
GENERAL_CAMERA_MOTION,
|
||||
TRIPOD_CAMERA_MOTION,
|
||||
};
|
||||
int SetClipMotion(int clip, Motion motion) {
|
||||
(void)clip;
|
||||
(void)motion;
|
||||
return 0;
|
||||
} // XXX
|
||||
|
||||
// Decide what to refine for the given intrinsics. bundle_options is from
|
||||
// bundle.h (e.g. BUNDLE_FOCAL_LENGTH | BUNDLE_RADIAL_K1).
|
||||
void SetIntrinsicsRefine(int intrinsics, int bundle_options) {
|
||||
(void)intrinsics;
|
||||
(void)bundle_options;
|
||||
} // XXX
|
||||
|
||||
// Keyframe read/write.
|
||||
struct ClipFrame {
|
||||
int clip;
|
||||
int frame;
|
||||
};
|
||||
const vector<ClipFrame>& keyframes() { return keyframes_; }
|
||||
void ClearKeyframes() { keyframes_.clear(); }
|
||||
void SetKeyframes(const vector<ClipFrame>& keyframes) {
|
||||
keyframes_ = keyframes;
|
||||
}
|
||||
|
||||
// What about reporting what happened? -- callbacks; maybe result struct.
|
||||
void Reconstruct();
|
||||
|
||||
// Detect and track in 2D.
|
||||
struct DetectAndTrackOptions {
|
||||
int min_num_features;
|
||||
};
|
||||
void DetectAndTrack(const DetectAndTrackOptions& options);
|
||||
|
||||
struct DetectFeaturesInFrameOptions {};
|
||||
void DetectFeaturesInFrame(
|
||||
int clip, int frame, const DetectFeaturesInFrameOptions* options = NULL) {
|
||||
(void)clip;
|
||||
(void)frame;
|
||||
(void)options;
|
||||
} // XXX
|
||||
|
||||
// Does not take ownership of the given listener, but keeps a reference to it.
|
||||
void AddListener(OperationListener* listener) { (void)listener; } // XXX
|
||||
|
||||
// Create the initial reconstruction,
|
||||
// void FindInitialReconstruction();
|
||||
|
||||
// State machine
|
||||
//
|
||||
// Question: Have explicit state? Or determine state from existing data?
|
||||
// Conclusion: Determine state from existing data.
|
||||
//
|
||||
// Preliminary state thoughts
|
||||
//
|
||||
// No tracks or markers
|
||||
// - Tracks empty.
|
||||
//
|
||||
// Initial tracks found
|
||||
// - All images have at least 5 tracks
|
||||
//
|
||||
// Ran RANSAC on tracks to mark inliers / outliers.
|
||||
// - All images have at least 8 "inlier" tracks
|
||||
//
|
||||
// Detector matching run to close loops and match across clips
|
||||
// - At least 5 matching tracks between clips
|
||||
//
|
||||
// Initial reconstruction found (2 frames)?
|
||||
// - There exists two cameras with intrinsics / extrinsics
|
||||
//
|
||||
// Preliminary reconstruction finished
|
||||
// - Poses for all frames in all clips estimated.
|
||||
//
|
||||
// Final reconstruction finished
|
||||
// - Final reconstruction bundle adjusted.
|
||||
|
||||
// For now, expose options directly. In the future this may change.
|
||||
Options options;
|
||||
|
||||
private:
|
||||
bool Log();
|
||||
bool Progress();
|
||||
bool Cancelled() { return false; }
|
||||
|
||||
Tracks tracks_; // May be normalized camera coordinates or raw pixels.
|
||||
// Reconstruction reconstruction_;
|
||||
|
||||
// TODO(keir): Add the motion models here.
|
||||
// vector<MotionModel> motion_models_;
|
||||
|
||||
// TODO(keir): Should num_clips and num_frames get moved to FrameAccessor?
|
||||
// TODO(keir): What about masking for clips and frames to prevent various
|
||||
// things like reconstruction or tracking from happening on certain frames?
|
||||
FrameAccessor* frame_accessor_;
|
||||
// int num_clips_;
|
||||
// vector<int> num_frames_; // Indexed by clip.
|
||||
|
||||
// The intrinsics for each clip, assuming each clip has fixed intrinsics.
|
||||
// TODO(keir): Decide what the semantics should be for varying focal length.
|
||||
vector<int> clip_intrinsics_;
|
||||
|
||||
vector<ClipFrame> keyframes_;
|
||||
};
|
||||
|
||||
} // namespace mv
|
||||
|
||||
#endif // LIBMV_AUTOTRACK_AUTOTRACK_H_
|
||||
38
blender-5.2.0/intern/libmv/libmv/autotrack/callbacks.h
Normal file
38
blender-5.2.0/intern/libmv/libmv/autotrack/callbacks.h
Normal file
@@ -0,0 +1,38 @@
|
||||
// Copyright (c) 2014 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.
|
||||
//
|
||||
// Author: mierle@gmail.com (Keir Mierle)
|
||||
|
||||
#ifndef LIBMV_AUTOTRACK_LISTENER_H_
|
||||
#define LIBMV_AUTOTRACK_LISTENER_H_
|
||||
|
||||
namespace mv {
|
||||
|
||||
struct OperationListener {
|
||||
// All hooks return true to continue or false to indicate the operation
|
||||
// should abort. Hooks should be thread safe (re-entrant).
|
||||
virtual bool Log(const string& message) = 0;
|
||||
virtual bool Progress(double fraction) = 0;
|
||||
virtual bool Cancelled() = 0;
|
||||
};
|
||||
|
||||
} // namespace mv
|
||||
|
||||
#endif // LIBMV_AUTOTRACK_LISTENER_H_
|
||||
102
blender-5.2.0/intern/libmv/libmv/autotrack/frame_accessor.h
Normal file
102
blender-5.2.0/intern/libmv/libmv/autotrack/frame_accessor.h
Normal file
@@ -0,0 +1,102 @@
|
||||
// Copyright (c) 2014 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.
|
||||
//
|
||||
// Author: mierle@gmail.com (Keir Mierle)
|
||||
|
||||
#ifndef LIBMV_AUTOTRACK_FRAME_ACCESSOR_H_
|
||||
#define LIBMV_AUTOTRACK_FRAME_ACCESSOR_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "libmv/image/image.h"
|
||||
|
||||
namespace mv {
|
||||
|
||||
struct Region;
|
||||
|
||||
using libmv::FloatImage;
|
||||
|
||||
// This is the abstraction to different sources of images that will be part of
|
||||
// a reconstruction. These may come from disk or they may come from Blender. In
|
||||
// most cases it's expected that the implementation provides some caching
|
||||
// otherwise performance will be terrible. Sometimes the images need to get
|
||||
// filtered, and this interface provides for that as well (and permits
|
||||
// implementations to cache filtered image pieces).
|
||||
struct FrameAccessor {
|
||||
struct Transform {
|
||||
virtual ~Transform() {}
|
||||
// The key should depend on the transform arguments. Must be non-zero.
|
||||
virtual int64_t key() const = 0;
|
||||
|
||||
// Apply the expected transform. Output is sized correctly already.
|
||||
// TODO(keir): What about blurs that need to access pixels outside the ROI?
|
||||
virtual void run(const FloatImage& input, FloatImage* output) const = 0;
|
||||
};
|
||||
|
||||
enum InputMode { MONO, RGBA };
|
||||
|
||||
typedef void* Key;
|
||||
|
||||
// Get a possibly-filtered version of a frame of a video. Downscale will
|
||||
// cause the input image to get downscaled by 2^downscale for pyramid access.
|
||||
// Region is always in original-image coordinates, and describes the
|
||||
// requested area. The transform describes an (optional) transform to apply
|
||||
// to the image before it is returned.
|
||||
//
|
||||
// When done with an image, you must call ReleaseImage with the returned key.
|
||||
virtual Key GetImage(int clip,
|
||||
int frame,
|
||||
InputMode input_mode,
|
||||
int downscale, // Downscale by 2^downscale.
|
||||
const Region* region, // Get full image if NULL.
|
||||
const Transform* transform, // May be NULL.
|
||||
FloatImage* destination) = 0;
|
||||
|
||||
// Releases an image from the frame accessor. Non-caching implementations may
|
||||
// free the image immediately; others may hold onto the image.
|
||||
virtual void ReleaseImage(Key) = 0;
|
||||
|
||||
// Get mask image for the given track.
|
||||
//
|
||||
// Implementation of this method should sample mask associated with the track
|
||||
// within given region to the given destination.
|
||||
//
|
||||
// Result is supposed to be a single channel image.
|
||||
//
|
||||
// If region is NULL, it assumed to be full-frame.
|
||||
virtual Key GetMaskForTrack(int clip,
|
||||
int frame,
|
||||
int track,
|
||||
const Region* region,
|
||||
FloatImage* destination) = 0;
|
||||
|
||||
// Release a specified mask.
|
||||
//
|
||||
// Non-caching implementation may free used memory immediately.
|
||||
virtual void ReleaseMask(Key key) = 0;
|
||||
|
||||
virtual bool GetClipDimensions(int clip, int* width, int* height) = 0;
|
||||
virtual int NumClips() = 0;
|
||||
virtual int NumFrames(int clip) = 0;
|
||||
};
|
||||
|
||||
} // namespace mv
|
||||
|
||||
#endif // LIBMV_AUTOTRACK_FRAME_ACCESSOR_H_
|
||||
131
blender-5.2.0/intern/libmv/libmv/autotrack/marker.h
Normal file
131
blender-5.2.0/intern/libmv/libmv/autotrack/marker.h
Normal file
@@ -0,0 +1,131 @@
|
||||
// Copyright (c) 2014 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.
|
||||
//
|
||||
// Author: mierle@gmail.com (Keir Mierle)
|
||||
|
||||
#ifndef LIBMV_AUTOTRACK_MARKER_H_
|
||||
#define LIBMV_AUTOTRACK_MARKER_H_
|
||||
|
||||
#include <ostream>
|
||||
|
||||
#include "libmv/autotrack/quad.h"
|
||||
#include "libmv/autotrack/region.h"
|
||||
#include "libmv/numeric/numeric.h"
|
||||
|
||||
namespace mv {
|
||||
|
||||
using libmv::Vec2f;
|
||||
|
||||
// A marker is the 2D location of a tracked region (quad) in an image.
|
||||
// Note that some of this information could be normalized by having a
|
||||
// collection of inter-connected structs. Instead the "fat Marker" design below
|
||||
// trades memory for data structure simplicity.
|
||||
struct Marker {
|
||||
int clip; // The clip this marker is from.
|
||||
int frame; // The frame within the clip this marker is from.
|
||||
int track; // The track this marker is from.
|
||||
|
||||
// The center of the marker in frame coordinates. This is typically, but not
|
||||
// always, the same as the center of the patch.
|
||||
Vec2f center;
|
||||
|
||||
// A frame-realtive quad defining the part of the image the marker covers.
|
||||
// For reference markers, the pixels in the patch are the tracking pattern.
|
||||
Quad2Df patch;
|
||||
|
||||
// Some markers are less certain than others; the weight determines the
|
||||
// amount this marker contributes to the error. 1.0 indicates normal
|
||||
// contribution; 0.0 indicates a zero-weight track (and will be omitted from
|
||||
// bundle adjustment).
|
||||
float weight;
|
||||
|
||||
enum Source {
|
||||
MANUAL, // The user placed this marker manually.
|
||||
DETECTED, // A keypoint detector found this point.
|
||||
TRACKED, // The tracking algorithm placed this marker.
|
||||
MATCHED, // A matching algorithm (e.g. SIFT or SURF or ORB) found this.
|
||||
PREDICTED, // A motion model predicted this marker. This is needed for
|
||||
// handling occlusions in some cases where an imaginary marker
|
||||
// is placed to keep camera motion smooth.
|
||||
};
|
||||
Source source;
|
||||
|
||||
// Markers may be inliers or outliers if the tracking fails; this allows
|
||||
// visualizing the markers in the image.
|
||||
enum Status { UNKNOWN, INLIER, OUTLIER };
|
||||
Status status;
|
||||
|
||||
// When doing correlation tracking, where to search in the current frame for
|
||||
// the pattern from the reference frame, in absolute frame coordinates.
|
||||
Region search_region;
|
||||
|
||||
// For tracked and matched markers, indicates what the reference was.
|
||||
int reference_clip;
|
||||
int reference_frame;
|
||||
|
||||
// Model related information for non-point tracks.
|
||||
//
|
||||
// Some tracks are on a larger object, such as a plane or a line or perhaps
|
||||
// another primitive (a rectangular prisim). This captures the information
|
||||
// needed to say that for example a collection of markers belongs to model #2
|
||||
// (and model #2 is a plane).
|
||||
enum ModelType { POINT, PLANE, LINE, CUBE };
|
||||
ModelType model_type;
|
||||
|
||||
// The model ID this track (e.g. the second model, which is a plane).
|
||||
int model_id;
|
||||
|
||||
// TODO(keir): Add a "int model_argument" to capture that e.g. a marker is on
|
||||
// the 3rd face of a cube.
|
||||
|
||||
enum Channel {
|
||||
CHANNEL_R = (1 << 0),
|
||||
CHANNEL_G = (1 << 1),
|
||||
CHANNEL_B = (1 << 2),
|
||||
};
|
||||
|
||||
// Channels from the original frame which this marker is unable to see.
|
||||
int disabled_channels;
|
||||
|
||||
// Offset everything (center, patch, search) by the given delta.
|
||||
template <typename T>
|
||||
void Offset(const T& offset) {
|
||||
center += offset.template cast<float>();
|
||||
patch.coordinates.rowwise() += offset.template cast<int>();
|
||||
search_region.Offset(offset);
|
||||
}
|
||||
|
||||
// Shift the center to the given new position (and patch, search).
|
||||
template <typename T>
|
||||
void SetPosition(const T& new_center) {
|
||||
Offset(new_center - center);
|
||||
}
|
||||
};
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& out, const Marker& marker) {
|
||||
out << "{" << marker.clip << ", " << marker.frame << ", " << marker.track
|
||||
<< ", (" << marker.center.x() << ", " << marker.center.y() << ")"
|
||||
<< "}";
|
||||
return out;
|
||||
}
|
||||
|
||||
} // namespace mv
|
||||
|
||||
#endif // LIBMV_AUTOTRACK_MARKER_H_
|
||||
39
blender-5.2.0/intern/libmv/libmv/autotrack/model.h
Normal file
39
blender-5.2.0/intern/libmv/libmv/autotrack/model.h
Normal file
@@ -0,0 +1,39 @@
|
||||
// Copyright (c) 2014 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.
|
||||
//
|
||||
// Author: mierle@gmail.com (Keir Mierle)
|
||||
|
||||
#ifndef LIBMV_AUTOTRACK_MODEL_H_
|
||||
#define LIBMV_AUTOTRACK_MODEL_H_
|
||||
|
||||
#include "libmv/autotrack/quad.h"
|
||||
#include "libmv/numeric/numeric.h"
|
||||
|
||||
namespace mv {
|
||||
|
||||
struct Model {
|
||||
enum ModelType { POINT, PLANE, LINE, CUBE };
|
||||
|
||||
// ???
|
||||
};
|
||||
|
||||
} // namespace mv
|
||||
|
||||
#endif // LIBMV_AUTOTRACK_MODEL_H_
|
||||
344
blender-5.2.0/intern/libmv/libmv/autotrack/predict_tracks.cc
Normal file
344
blender-5.2.0/intern/libmv/libmv/autotrack/predict_tracks.cc
Normal file
@@ -0,0 +1,344 @@
|
||||
// Copyright (c) 2014 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.
|
||||
//
|
||||
// Author: mierle@gmail.com (Keir Mierle)
|
||||
|
||||
#include "libmv/autotrack/predict_tracks.h"
|
||||
#include "libmv/autotrack/marker.h"
|
||||
#include "libmv/autotrack/tracks.h"
|
||||
#include "libmv/base/vector.h"
|
||||
#include "libmv/logging/logging.h"
|
||||
#include "libmv/tracking/kalman_filter.h"
|
||||
|
||||
namespace mv {
|
||||
|
||||
namespace {
|
||||
|
||||
using libmv::Vec2;
|
||||
using libmv::vector;
|
||||
|
||||
// Implied time delta between steps. Set empirically by tweaking and seeing
|
||||
// what numbers did best at prediction.
|
||||
const double dt = 3.8;
|
||||
|
||||
// State transition matrix.
|
||||
|
||||
// The states for predicting a track are as follows:
|
||||
//
|
||||
// 0 - X position
|
||||
// 1 - X velocity
|
||||
// 2 - X acceleration
|
||||
// 3 - Y position
|
||||
// 4 - Y velocity
|
||||
// 5 - Y acceleration
|
||||
//
|
||||
// Note that in the velocity-only state transition matrix, the acceleration
|
||||
// component is ignored; so technically the system could be modelled with only
|
||||
// 4 states instead of 6. For ease of implementation, this keeps order 6.
|
||||
|
||||
// Choose one or the other model from below (velocity or acceleration).
|
||||
|
||||
// For a typical system having constant velocity. This gives smooth-appearing
|
||||
// predictions, but they are not always as accurate.
|
||||
//
|
||||
// clang-format off
|
||||
const double velocity_state_transition_data[] = {
|
||||
1, dt, 0, 0, 0, 0,
|
||||
0, 1, 0, 0, 0, 0,
|
||||
0, 0, 1, 0, 0, 0,
|
||||
0, 0, 0, 1, dt, 0,
|
||||
0, 0, 0, 0, 1, 0,
|
||||
0, 0, 0, 0, 0, 1
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
#if 0
|
||||
// This 3rd-order system also models acceleration. This makes for "jerky"
|
||||
// predictions, but that tend to be more accurate.
|
||||
//
|
||||
// clang-format off
|
||||
const double acceleration_state_transition_data[] = {
|
||||
1, dt, dt*dt/2, 0, 0, 0,
|
||||
0, 1, dt, 0, 0, 0,
|
||||
0, 0, 1, 0, 0, 0,
|
||||
0, 0, 0, 1, dt, dt*dt/2,
|
||||
0, 0, 0, 0, 1, dt,
|
||||
0, 0, 0, 0, 0, 1
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
// This system (attempts) to add an angular velocity component. However, it's
|
||||
// total junk.
|
||||
//
|
||||
// clang-format off
|
||||
const double angular_state_transition_data[] = {
|
||||
1, dt, -dt, 0, 0, 0, // Position x
|
||||
0, 1, 0, 0, 0, 0, // Velocity x
|
||||
0, 0, 1, 0, 0, 0, // Angular momentum
|
||||
0, 0, dt, 1, dt, 0, // Position y
|
||||
0, 0, 0, 0, 1, 0, // Velocity y
|
||||
0, 0, 0, 0, 0, 1 // Ignored
|
||||
};
|
||||
// clang-format on
|
||||
#endif
|
||||
|
||||
const double* state_transition_data = velocity_state_transition_data;
|
||||
|
||||
// Observation matrix.
|
||||
// clang-format off
|
||||
const double observation_data[] = {
|
||||
1., 0., 0., 0., 0., 0.,
|
||||
0., 0., 0., 1., 0., 0.
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
// Process covariance.
|
||||
//
|
||||
// clang-format off
|
||||
const double process_covariance_data[] = {
|
||||
35, 0, 0, 0, 0, 0,
|
||||
0, 5, 0, 0, 0, 0,
|
||||
0, 0, 5, 0, 0, 0,
|
||||
0, 0, 0, 35, 0, 0,
|
||||
0, 0, 0, 0, 5, 0,
|
||||
0, 0, 0, 0, 0, 5
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
// Process covariance.
|
||||
const double measurement_covariance_data[] = {
|
||||
0.01,
|
||||
0.00,
|
||||
0.00,
|
||||
0.01,
|
||||
};
|
||||
|
||||
// Initial covariance.
|
||||
//
|
||||
// clang-format off
|
||||
const double initial_covariance_data[] = {
|
||||
10, 0, 0, 0, 0, 0,
|
||||
0, 1, 0, 0, 0, 0,
|
||||
0, 0, 1, 0, 0, 0,
|
||||
0, 0, 0, 10, 0, 0,
|
||||
0, 0, 0, 0, 1, 0,
|
||||
0, 0, 0, 0, 0, 1
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
typedef mv::KalmanFilter<double, 6, 2> TrackerKalman;
|
||||
|
||||
TrackerKalman filter(state_transition_data,
|
||||
observation_data,
|
||||
process_covariance_data,
|
||||
measurement_covariance_data);
|
||||
|
||||
bool OrderByFrameLessThan(const Marker* a, const Marker* b) {
|
||||
if (a->frame == b->frame) {
|
||||
if (a->clip == b->clip) {
|
||||
return a->track < b->track;
|
||||
}
|
||||
return a->clip < b->clip;
|
||||
}
|
||||
return a->frame < b->frame;
|
||||
}
|
||||
|
||||
// Predicted must be after the previous markers (in the frame numbering sense).
|
||||
void RunPrediction(const vector<Marker*> previous_markers,
|
||||
Marker* predicted_marker) {
|
||||
TrackerKalman::State state;
|
||||
state.mean << previous_markers[0]->center.x(), 0, 0,
|
||||
previous_markers[0]->center.y(), 0, 0;
|
||||
state.covariance =
|
||||
Eigen::Matrix<double, 6, 6, Eigen::RowMajor>(initial_covariance_data);
|
||||
|
||||
int current_frame = previous_markers[0]->frame;
|
||||
int target_frame = predicted_marker->frame;
|
||||
|
||||
bool predict_forward = current_frame < target_frame;
|
||||
int frame_delta = predict_forward ? 1 : -1;
|
||||
|
||||
for (int i = 1; i < previous_markers.size(); ++i) {
|
||||
// Step forward predicting the state until it is on the current marker.
|
||||
int predictions = 0;
|
||||
for (; current_frame != previous_markers[i]->frame;
|
||||
current_frame += frame_delta) {
|
||||
filter.Step(&state);
|
||||
predictions++;
|
||||
LG << "Predicted point (frame " << current_frame << "): " << state.mean(0)
|
||||
<< ", " << state.mean(3);
|
||||
}
|
||||
// Log the error -- not actually used, but interesting.
|
||||
Vec2 error = previous_markers[i]->center.cast<double>() -
|
||||
Vec2(state.mean(0), state.mean(3));
|
||||
LG << "Prediction error for " << predictions << " steps: (" << error.x()
|
||||
<< ", " << error.y() << "); norm: " << error.norm();
|
||||
// Now that the state is predicted in the current frame, update the state
|
||||
// based on the measurement from the current frame.
|
||||
filter.Update(previous_markers[i]->center.cast<double>(),
|
||||
Eigen::Matrix<double, 2, 2, Eigen::RowMajor>(
|
||||
measurement_covariance_data),
|
||||
&state);
|
||||
LG << "Updated point: " << state.mean(0) << ", " << state.mean(3);
|
||||
}
|
||||
// At this point as all the prediction that's possible is done. Finally
|
||||
// predict until the target frame.
|
||||
for (; current_frame != target_frame; current_frame += frame_delta) {
|
||||
filter.Step(&state);
|
||||
LG << "Final predicted point (frame " << current_frame
|
||||
<< "): " << state.mean(0) << ", " << state.mean(3);
|
||||
}
|
||||
|
||||
// The x and y positions are at 0 and 3; ignore acceleration and velocity.
|
||||
predicted_marker->center.x() = state.mean(0);
|
||||
predicted_marker->center.y() = state.mean(3);
|
||||
|
||||
// Take the patch from the last marker then shift it to match the prediction.
|
||||
const Marker& last_marker = *previous_markers[previous_markers.size() - 1];
|
||||
predicted_marker->patch = last_marker.patch;
|
||||
Vec2f delta = predicted_marker->center - last_marker.center;
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
predicted_marker->patch.coordinates.row(i) += delta;
|
||||
}
|
||||
|
||||
// Alter the search area as well so it always corresponds to the center.
|
||||
predicted_marker->search_region = last_marker.search_region;
|
||||
predicted_marker->search_region.Offset(delta);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool PredictMarkerPosition(const Tracks& tracks,
|
||||
const PredictDirection direction,
|
||||
Marker* marker) {
|
||||
// Get all markers for this clip and track.
|
||||
vector<Marker> markers;
|
||||
tracks.GetMarkersForTrackInClip(marker->clip, marker->track, &markers);
|
||||
|
||||
if (markers.empty()) {
|
||||
LG << "No markers to predict from for " << *marker;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Order the markers by frame within the clip.
|
||||
vector<Marker*> boxed_markers(markers.size());
|
||||
for (int i = 0; i < markers.size(); ++i) {
|
||||
boxed_markers[i] = &markers[i];
|
||||
}
|
||||
std::sort(boxed_markers.begin(), boxed_markers.end(), OrderByFrameLessThan);
|
||||
|
||||
// Find the insertion point for this marker among the returned ones.
|
||||
int insert_at = -1; // If we find the exact frame
|
||||
int insert_before = -1; // Otherwise...
|
||||
for (int i = 0; i < boxed_markers.size(); ++i) {
|
||||
if (boxed_markers[i]->frame == marker->frame) {
|
||||
insert_at = i;
|
||||
break;
|
||||
}
|
||||
if (boxed_markers[i]->frame > marker->frame) {
|
||||
insert_before = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Forward starts at the marker or insertion point, and goes forward.
|
||||
int forward_scan_begin, forward_scan_end;
|
||||
|
||||
// Backward scan starts at the marker or insertion point, and goes backward.
|
||||
int backward_scan_begin, backward_scan_end;
|
||||
|
||||
// Determine the scanning ranges.
|
||||
if (insert_at == -1 && insert_before == -1) {
|
||||
// Didn't find an insertion point except the end.
|
||||
forward_scan_begin = forward_scan_end = 0;
|
||||
backward_scan_begin = markers.size() - 1;
|
||||
backward_scan_end = 0;
|
||||
} else if (insert_at != -1) {
|
||||
// Found existing marker; scan before and after it.
|
||||
forward_scan_begin = insert_at + 1;
|
||||
forward_scan_end = markers.size() - 1;
|
||||
backward_scan_begin = insert_at - 1;
|
||||
backward_scan_end = 0;
|
||||
} else {
|
||||
// Didn't find existing marker but found an insertion point.
|
||||
forward_scan_begin = insert_before;
|
||||
forward_scan_end = markers.size() - 1;
|
||||
backward_scan_begin = insert_before - 1;
|
||||
backward_scan_end = 0;
|
||||
}
|
||||
|
||||
const int num_consecutive_needed = 2;
|
||||
|
||||
if (forward_scan_begin <= forward_scan_end &&
|
||||
forward_scan_end - forward_scan_begin > num_consecutive_needed) {
|
||||
// TODO(keir): Finish this.
|
||||
}
|
||||
|
||||
bool predict_forward = false;
|
||||
switch (direction) {
|
||||
case PredictDirection::AUTO:
|
||||
if (backward_scan_end <= backward_scan_begin) {
|
||||
// TODO(keir): Add smarter handling and detecting of consecutive frames!
|
||||
predict_forward = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case PredictDirection::FORWARD: predict_forward = true; break;
|
||||
|
||||
case PredictDirection::BACKWARD: predict_forward = false; break;
|
||||
}
|
||||
|
||||
const int max_frames_to_predict_from = 20;
|
||||
if (predict_forward) {
|
||||
if (backward_scan_begin - backward_scan_end < num_consecutive_needed) {
|
||||
// Not enough information to do a prediction.
|
||||
LG << "Predicting forward impossible, not enough information";
|
||||
return false;
|
||||
}
|
||||
LG << "Predicting forward";
|
||||
int predict_begin =
|
||||
std::max(backward_scan_begin - max_frames_to_predict_from, 0);
|
||||
int predict_end = backward_scan_begin;
|
||||
vector<Marker*> previous_markers;
|
||||
for (int i = predict_begin; i <= predict_end; ++i) {
|
||||
previous_markers.push_back(boxed_markers[i]);
|
||||
}
|
||||
RunPrediction(previous_markers, marker);
|
||||
return true;
|
||||
} else {
|
||||
if (forward_scan_end - forward_scan_begin < num_consecutive_needed) {
|
||||
// Not enough information to do a prediction.
|
||||
LG << "Predicting backward impossible, not enough information";
|
||||
return false;
|
||||
}
|
||||
LG << "Predicting backward";
|
||||
int predict_begin = std::min(
|
||||
forward_scan_begin + max_frames_to_predict_from, forward_scan_end);
|
||||
int predict_end = forward_scan_begin;
|
||||
vector<Marker*> previous_markers;
|
||||
for (int i = predict_begin; i >= predict_end; --i) {
|
||||
previous_markers.push_back(boxed_markers[i]);
|
||||
}
|
||||
RunPrediction(previous_markers, marker);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mv
|
||||
55
blender-5.2.0/intern/libmv/libmv/autotrack/predict_tracks.h
Normal file
55
blender-5.2.0/intern/libmv/libmv/autotrack/predict_tracks.h
Normal file
@@ -0,0 +1,55 @@
|
||||
// Copyright (c) 2014 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.
|
||||
//
|
||||
// Author: mierle@gmail.com (Keir Mierle)
|
||||
|
||||
#ifndef LIBMV_AUTOTRACK_PREDICT_TRACKS_H_
|
||||
#define LIBMV_AUTOTRACK_PREDICT_TRACKS_H_
|
||||
|
||||
namespace mv {
|
||||
|
||||
class Tracks;
|
||||
struct Marker;
|
||||
|
||||
enum class PredictDirection {
|
||||
// Detect direction in which to predict marker position based on an existing
|
||||
// Tracks context. Prediction will happen in the preferred direction of a
|
||||
// missing information.
|
||||
// If markers exists to the both sides of the given one the prediction
|
||||
// direction is preferred to be forward.
|
||||
AUTO,
|
||||
|
||||
// Predict position of the marker from the past to the future (used for
|
||||
// forward tracking).
|
||||
FORWARD,
|
||||
|
||||
// Predict position from the future to the past (used for backward tracking).
|
||||
BACKWARD,
|
||||
};
|
||||
|
||||
// Predict the position of the given marker, and update it accordingly. The
|
||||
// existing position will be overwritten.
|
||||
bool PredictMarkerPosition(const Tracks& tracks,
|
||||
const PredictDirection direction,
|
||||
Marker* marker);
|
||||
|
||||
} // namespace mv
|
||||
|
||||
#endif // LIBMV_AUTOTRACK_PREDICT_TRACKS_H_
|
||||
@@ -0,0 +1,195 @@
|
||||
// Copyright (c) 2014 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.
|
||||
//
|
||||
// Author: mierle@gmail.com (Keir Mierle)
|
||||
|
||||
#include "libmv/autotrack/predict_tracks.h"
|
||||
|
||||
#include "libmv/autotrack/marker.h"
|
||||
#include "libmv/autotrack/tracks.h"
|
||||
#include "libmv/logging/logging.h"
|
||||
#include "testing/testing.h"
|
||||
|
||||
namespace mv {
|
||||
|
||||
static void AddMarker(int frame, float x, float y, Tracks* tracks) {
|
||||
Marker marker;
|
||||
marker.clip = marker.track = 0;
|
||||
marker.frame = frame;
|
||||
marker.center.x() = x;
|
||||
marker.center.y() = y;
|
||||
marker.patch.coordinates << x - 1, y - 1, x + 1, y - 1, x + 1, y + 1, x - 1,
|
||||
y + 1;
|
||||
tracks->AddMarker(marker);
|
||||
}
|
||||
|
||||
TEST(PredictMarkerPosition, EasyLinearMotion) {
|
||||
Tracks tracks;
|
||||
AddMarker(0, 1.0, 0.0, &tracks);
|
||||
AddMarker(1, 2.0, 5.0, &tracks);
|
||||
AddMarker(2, 3.0, 10.0, &tracks);
|
||||
AddMarker(3, 4.0, 15.0, &tracks);
|
||||
AddMarker(4, 5.0, 20.0, &tracks);
|
||||
AddMarker(5, 6.0, 25.0, &tracks);
|
||||
AddMarker(6, 7.0, 30.0, &tracks);
|
||||
AddMarker(7, 8.0, 35.0, &tracks);
|
||||
|
||||
Marker predicted;
|
||||
predicted.clip = 0;
|
||||
predicted.track = 0;
|
||||
predicted.frame = 8;
|
||||
|
||||
PredictMarkerPosition(tracks, PredictDirection::AUTO, &predicted);
|
||||
double error = (libmv::Vec2f(9.0, 40.0) - predicted.center).norm();
|
||||
LG << "Got error: " << error;
|
||||
EXPECT_LT(error, 0.1);
|
||||
|
||||
// Check the patch coordinates as well.
|
||||
double x = 9, y = 40.0;
|
||||
Quad2Df expected_patch;
|
||||
expected_patch.coordinates << x - 1, y - 1, x + 1, y - 1, x + 1, y + 1, x - 1,
|
||||
y + 1;
|
||||
|
||||
error = (expected_patch.coordinates - predicted.patch.coordinates).norm();
|
||||
LG << "Patch error: " << error;
|
||||
EXPECT_LT(error, 0.1);
|
||||
}
|
||||
|
||||
TEST(PredictMarkerPosition, EasyBackwardLinearMotion) {
|
||||
Tracks tracks;
|
||||
AddMarker(8, 1.0, 0.0, &tracks);
|
||||
AddMarker(7, 2.0, 5.0, &tracks);
|
||||
AddMarker(6, 3.0, 10.0, &tracks);
|
||||
AddMarker(5, 4.0, 15.0, &tracks);
|
||||
AddMarker(4, 5.0, 20.0, &tracks);
|
||||
AddMarker(3, 6.0, 25.0, &tracks);
|
||||
AddMarker(2, 7.0, 30.0, &tracks);
|
||||
AddMarker(1, 8.0, 35.0, &tracks);
|
||||
|
||||
Marker predicted;
|
||||
predicted.clip = 0;
|
||||
predicted.track = 0;
|
||||
predicted.frame = 0;
|
||||
|
||||
PredictMarkerPosition(tracks, PredictDirection::AUTO, &predicted);
|
||||
LG << predicted;
|
||||
double error = (libmv::Vec2f(9.0, 40.0) - predicted.center).norm();
|
||||
LG << "Got error: " << error;
|
||||
EXPECT_LT(error, 0.1);
|
||||
|
||||
// Check the patch coordinates as well.
|
||||
double x = 9.0, y = 40.0;
|
||||
Quad2Df expected_patch;
|
||||
expected_patch.coordinates << x - 1, y - 1, x + 1, y - 1, x + 1, y + 1, x - 1,
|
||||
y + 1;
|
||||
|
||||
error = (expected_patch.coordinates - predicted.patch.coordinates).norm();
|
||||
LG << "Patch error: " << error;
|
||||
EXPECT_LT(error, 0.1);
|
||||
}
|
||||
|
||||
TEST(PredictMarkerPosition, TwoFrameGap) {
|
||||
Tracks tracks;
|
||||
AddMarker(0, 1.0, 0.0, &tracks);
|
||||
AddMarker(1, 2.0, 5.0, &tracks);
|
||||
AddMarker(2, 3.0, 10.0, &tracks);
|
||||
AddMarker(3, 4.0, 15.0, &tracks);
|
||||
AddMarker(4, 5.0, 20.0, &tracks);
|
||||
AddMarker(5, 6.0, 25.0, &tracks);
|
||||
AddMarker(6, 7.0, 30.0, &tracks);
|
||||
// Missing frame 7!
|
||||
|
||||
Marker predicted;
|
||||
predicted.clip = 0;
|
||||
predicted.track = 0;
|
||||
predicted.frame = 8;
|
||||
|
||||
PredictMarkerPosition(tracks, PredictDirection::AUTO, &predicted);
|
||||
double error = (libmv::Vec2f(9.0, 40.0) - predicted.center).norm();
|
||||
LG << "Got error: " << error;
|
||||
EXPECT_LT(error, 0.1);
|
||||
}
|
||||
|
||||
TEST(PredictMarkerPosition, FourFrameGap) {
|
||||
Tracks tracks;
|
||||
AddMarker(0, 1.0, 0.0, &tracks);
|
||||
AddMarker(1, 2.0, 5.0, &tracks);
|
||||
AddMarker(2, 3.0, 10.0, &tracks);
|
||||
AddMarker(3, 4.0, 15.0, &tracks);
|
||||
// Missing frames 4, 5, 6, 7.
|
||||
|
||||
Marker predicted;
|
||||
predicted.clip = 0;
|
||||
predicted.track = 0;
|
||||
predicted.frame = 8;
|
||||
|
||||
PredictMarkerPosition(tracks, PredictDirection::AUTO, &predicted);
|
||||
double error = (libmv::Vec2f(9.0, 40.0) - predicted.center).norm();
|
||||
LG << "Got error: " << error;
|
||||
EXPECT_LT(error, 2.0); // Generous error due to larger prediction window.
|
||||
}
|
||||
|
||||
TEST(PredictMarkerPosition, MultipleGaps) {
|
||||
Tracks tracks;
|
||||
AddMarker(0, 1.0, 0.0, &tracks);
|
||||
AddMarker(1, 2.0, 5.0, &tracks);
|
||||
AddMarker(2, 3.0, 10.0, &tracks);
|
||||
// AddMarker(3, 4.0, 15.0, &tracks); // Note the 3-frame gap.
|
||||
// AddMarker(4, 5.0, 20.0, &tracks);
|
||||
// AddMarker(5, 6.0, 25.0, &tracks);
|
||||
AddMarker(6, 7.0, 30.0, &tracks); // Intermediate measurement.
|
||||
// AddMarker(7, 8.0, 35.0, &tracks);
|
||||
|
||||
Marker predicted;
|
||||
predicted.clip = 0;
|
||||
predicted.track = 0;
|
||||
predicted.frame = 8;
|
||||
|
||||
PredictMarkerPosition(tracks, PredictDirection::AUTO, &predicted);
|
||||
double error = (libmv::Vec2f(9.0, 40.0) - predicted.center).norm();
|
||||
LG << "Got error: " << error;
|
||||
EXPECT_LT(error, 1.0); // Generous error due to larger prediction window.
|
||||
}
|
||||
|
||||
TEST(PredictMarkerPosition, MarkersInRandomOrder) {
|
||||
Tracks tracks;
|
||||
|
||||
// This is the same as the easy, except that the tracks are randomly ordered.
|
||||
AddMarker(0, 1.0, 0.0, &tracks);
|
||||
AddMarker(2, 3.0, 10.0, &tracks);
|
||||
AddMarker(7, 8.0, 35.0, &tracks);
|
||||
AddMarker(5, 6.0, 25.0, &tracks);
|
||||
AddMarker(4, 5.0, 20.0, &tracks);
|
||||
AddMarker(3, 4.0, 15.0, &tracks);
|
||||
AddMarker(6, 7.0, 30.0, &tracks);
|
||||
AddMarker(1, 2.0, 5.0, &tracks);
|
||||
|
||||
Marker predicted;
|
||||
predicted.clip = 0;
|
||||
predicted.track = 0;
|
||||
predicted.frame = 8;
|
||||
|
||||
PredictMarkerPosition(tracks, PredictDirection::AUTO, &predicted);
|
||||
double error = (libmv::Vec2f(9.0, 40.0) - predicted.center).norm();
|
||||
LG << "Got error: " << error;
|
||||
EXPECT_LT(error, 0.1);
|
||||
}
|
||||
|
||||
} // namespace mv
|
||||
57
blender-5.2.0/intern/libmv/libmv/autotrack/quad.h
Normal file
57
blender-5.2.0/intern/libmv/libmv/autotrack/quad.h
Normal file
@@ -0,0 +1,57 @@
|
||||
// Copyright (c) 2014 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.
|
||||
//
|
||||
// Author: mierle@gmail.com (Keir Mierle)
|
||||
|
||||
#ifndef LIBMV_AUTOTRACK_QUAD_H_
|
||||
#define LIBMV_AUTOTRACK_QUAD_H_
|
||||
|
||||
#include <Eigen/Core>
|
||||
|
||||
namespace mv {
|
||||
|
||||
template <typename T, int D>
|
||||
struct Quad {
|
||||
// A quad is 4 points; generally in 2D or 3D.
|
||||
//
|
||||
// +----------> x
|
||||
// |\.
|
||||
// | \.
|
||||
// | z (z goes into screen)
|
||||
// |
|
||||
// | r0----->r1
|
||||
// | ^ |
|
||||
// | | . |
|
||||
// | | V
|
||||
// | r3<-----r2
|
||||
// | \.
|
||||
// | \.
|
||||
// v normal goes away (right handed).
|
||||
// y
|
||||
//
|
||||
// Each row is one of the corners coordinates; either (x, y) or (x, y, z).
|
||||
Eigen::Matrix<T, 4, D> coordinates;
|
||||
};
|
||||
|
||||
typedef Quad<float, 2> Quad2Df;
|
||||
|
||||
} // namespace mv
|
||||
|
||||
#endif // LIBMV_AUTOTRACK_QUAD_H_
|
||||
90
blender-5.2.0/intern/libmv/libmv/autotrack/reconstruction.h
Normal file
90
blender-5.2.0/intern/libmv/libmv/autotrack/reconstruction.h
Normal file
@@ -0,0 +1,90 @@
|
||||
// Copyright (c) 2014 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.
|
||||
//
|
||||
// Author: mierle@gmail.com (Keir Mierle)
|
||||
|
||||
#ifndef LIBMV_AUTOTRACK_RECONSTRUCTION_H_
|
||||
#define LIBMV_AUTOTRACK_RECONSTRUCTION_H_
|
||||
|
||||
#include "libmv/base/map.h"
|
||||
#include "libmv/base/vector.h"
|
||||
#include "libmv/numeric/numeric.h"
|
||||
#include "libmv/simple_pipeline/camera_intrinsics.h"
|
||||
|
||||
namespace mv {
|
||||
|
||||
using libmv::CameraIntrinsics;
|
||||
using libmv::vector;
|
||||
|
||||
class Model;
|
||||
|
||||
class CameraPose {
|
||||
int clip;
|
||||
int frame;
|
||||
int intrinsics;
|
||||
Mat3 R;
|
||||
Vec3 t;
|
||||
};
|
||||
|
||||
class Point {
|
||||
int track;
|
||||
|
||||
// The coordinates of the point. Note that not all coordinates are always
|
||||
// used; for example points on a plane only use the first two coordinates.
|
||||
Vec3 X;
|
||||
};
|
||||
|
||||
// A reconstruction for a set of tracks. The indexing for clip, frame, and
|
||||
// track should match that of a Tracks object, stored elsewhere.
|
||||
class Reconstruction {
|
||||
public:
|
||||
// All methods copy their input reference or take ownership of the pointer.
|
||||
void AddCameraPose(const CameraPose& pose);
|
||||
int AddCameraIntrinsics(CameraIntrinsics* intrinsics);
|
||||
int AddPoint(const Point& point);
|
||||
int AddModel(Model* model);
|
||||
|
||||
// Returns the corresponding pose or point or NULL if missing.
|
||||
CameraPose* CameraPoseForFrame(int clip, int frame);
|
||||
const CameraPose* CameraPoseForFrame(int clip, int frame) const;
|
||||
Point* PointForTrack(int track);
|
||||
const Point* PointForTrack(int track) const;
|
||||
|
||||
const vector<vector<CameraPose>>& camera_poses() const {
|
||||
return camera_poses_;
|
||||
}
|
||||
|
||||
private:
|
||||
// Indexed by CameraPose::intrinsics. Owns the intrinsics objects.
|
||||
vector<CameraIntrinsics*> camera_intrinsics_;
|
||||
|
||||
// Indexed by Marker::clip then by Marker::frame.
|
||||
vector<map<int, CameraPose>> camera_poses_;
|
||||
|
||||
// Indexed by Marker::track.
|
||||
vector<Point> points_;
|
||||
|
||||
// Indexed by Marker::model_id. Owns model objects.
|
||||
vector<Model*> models_;
|
||||
};
|
||||
|
||||
} // namespace mv
|
||||
|
||||
#endif // LIBMV_AUTOTRACK_RECONSTRUCTION_H_
|
||||
67
blender-5.2.0/intern/libmv/libmv/autotrack/region.h
Normal file
67
blender-5.2.0/intern/libmv/libmv/autotrack/region.h
Normal file
@@ -0,0 +1,67 @@
|
||||
// Copyright (c) 2014 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.
|
||||
//
|
||||
// Author: mierle@gmail.com (Keir Mierle)
|
||||
|
||||
#ifndef LIBMV_AUTOTRACK_REGION_H_
|
||||
#define LIBMV_AUTOTRACK_REGION_H_
|
||||
|
||||
#include "libmv/numeric/numeric.h"
|
||||
|
||||
namespace mv {
|
||||
|
||||
using libmv::Vec2f;
|
||||
|
||||
// A region is a bounding box within an image.
|
||||
//
|
||||
// +----------> x
|
||||
// |
|
||||
// | (min.x, min.y) (max.x, min.y)
|
||||
// | +-------------------------+
|
||||
// | | |
|
||||
// | | |
|
||||
// | | |
|
||||
// | +-------------------------+
|
||||
// v (min.x, max.y) (max.x, max.y)
|
||||
// y
|
||||
//
|
||||
struct Region {
|
||||
Vec2f min;
|
||||
Vec2f max;
|
||||
|
||||
template <typename T>
|
||||
void Offset(const T& offset) {
|
||||
min += offset.template cast<float>();
|
||||
max += offset.template cast<float>();
|
||||
}
|
||||
|
||||
Region Rounded() const {
|
||||
Region result;
|
||||
result.min(0) = ceil(this->min(0));
|
||||
result.min(1) = ceil(this->min(1));
|
||||
result.max(0) = ceil(this->max(0));
|
||||
result.max(1) = ceil(this->max(1));
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace mv
|
||||
|
||||
#endif // LIBMV_AUTOTRACK_REGION_H_
|
||||
190
blender-5.2.0/intern/libmv/libmv/autotrack/tracks.cc
Normal file
190
blender-5.2.0/intern/libmv/libmv/autotrack/tracks.cc
Normal file
@@ -0,0 +1,190 @@
|
||||
// Copyright (c) 2014 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.
|
||||
//
|
||||
// Author: mierle@gmail.com (Keir Mierle)
|
||||
|
||||
#include "libmv/autotrack/tracks.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <vector>
|
||||
|
||||
#include "libmv/numeric/numeric.h"
|
||||
|
||||
namespace mv {
|
||||
|
||||
Tracks::Tracks(const Tracks& other) {
|
||||
markers_ = other.markers_;
|
||||
}
|
||||
|
||||
Tracks::Tracks(const vector<Marker>& markers) : markers_(markers) {
|
||||
}
|
||||
|
||||
bool Tracks::GetMarker(int clip, int frame, int track, Marker* marker) const {
|
||||
for (int i = 0; i < markers_.size(); ++i) {
|
||||
if (markers_[i].clip == clip && markers_[i].frame == frame &&
|
||||
markers_[i].track == track) {
|
||||
*marker = markers_[i];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Tracks::GetMarkersForTrack(int track, vector<Marker>* markers) const {
|
||||
for (int i = 0; i < markers_.size(); ++i) {
|
||||
if (track == markers_[i].track) {
|
||||
markers->push_back(markers_[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Tracks::GetMarkersForTrackInClip(int clip,
|
||||
int track,
|
||||
vector<Marker>* markers) const {
|
||||
for (int i = 0; i < markers_.size(); ++i) {
|
||||
if (clip == markers_[i].clip && track == markers_[i].track) {
|
||||
markers->push_back(markers_[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Tracks::GetMarkersInFrame(int clip,
|
||||
int frame,
|
||||
vector<Marker>* markers) const {
|
||||
for (int i = 0; i < markers_.size(); ++i) {
|
||||
if (markers_[i].clip == clip && markers_[i].frame == frame) {
|
||||
markers->push_back(markers_[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Tracks::GetMarkersForTracksInBothImages(int clip1,
|
||||
int frame1,
|
||||
int clip2,
|
||||
int frame2,
|
||||
vector<Marker>* markers) const {
|
||||
std::vector<int> image1_tracks;
|
||||
std::vector<int> image2_tracks;
|
||||
|
||||
// Collect the tracks in each of the two images.
|
||||
for (int i = 0; i < markers_.size(); ++i) {
|
||||
int clip = markers_[i].clip;
|
||||
int frame = markers_[i].frame;
|
||||
if (clip == clip1 && frame == frame1) {
|
||||
image1_tracks.push_back(markers_[i].track);
|
||||
} else if (clip == clip2 && frame == frame2) {
|
||||
image2_tracks.push_back(markers_[i].track);
|
||||
}
|
||||
}
|
||||
|
||||
// Intersect the two sets to find the tracks of interest.
|
||||
std::sort(image1_tracks.begin(), image1_tracks.end());
|
||||
std::sort(image2_tracks.begin(), image2_tracks.end());
|
||||
std::vector<int> intersection;
|
||||
std::set_intersection(image1_tracks.begin(),
|
||||
image1_tracks.end(),
|
||||
image2_tracks.begin(),
|
||||
image2_tracks.end(),
|
||||
std::back_inserter(intersection));
|
||||
|
||||
// Scan through and get the relevant tracks from the two images.
|
||||
for (int i = 0; i < markers_.size(); ++i) {
|
||||
// Save markers that are in either frame and are in our candidate set.
|
||||
if (((markers_[i].clip == clip1 && markers_[i].frame == frame1) ||
|
||||
(markers_[i].clip == clip2 && markers_[i].frame == frame2)) &&
|
||||
std::binary_search(
|
||||
intersection.begin(), intersection.end(), markers_[i].track)) {
|
||||
markers->push_back(markers_[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Tracks::AddMarker(const Marker& marker) {
|
||||
// TODO(keir): This is quadratic for repeated insertions. Fix this by adding
|
||||
// a smarter data structure like a set<>.
|
||||
for (int i = 0; i < markers_.size(); ++i) {
|
||||
if (markers_[i].clip == marker.clip && markers_[i].frame == marker.frame &&
|
||||
markers_[i].track == marker.track) {
|
||||
markers_[i] = marker;
|
||||
return;
|
||||
}
|
||||
}
|
||||
markers_.push_back(marker);
|
||||
}
|
||||
|
||||
void Tracks::SetMarkers(vector<Marker>* markers) {
|
||||
std::swap(markers_, *markers);
|
||||
}
|
||||
|
||||
bool Tracks::RemoveMarker(int clip, int frame, int track) {
|
||||
int size = markers_.size();
|
||||
for (int i = 0; i < markers_.size(); ++i) {
|
||||
if (markers_[i].clip == clip && markers_[i].frame == frame &&
|
||||
markers_[i].track == track) {
|
||||
markers_[i] = markers_[size - 1];
|
||||
markers_.resize(size - 1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Tracks::RemoveMarkersForTrack(int track) {
|
||||
int size = 0;
|
||||
for (int i = 0; i < markers_.size(); ++i) {
|
||||
if (markers_[i].track != track) {
|
||||
markers_[size++] = markers_[i];
|
||||
}
|
||||
}
|
||||
markers_.resize(size);
|
||||
}
|
||||
|
||||
int Tracks::MaxClip() const {
|
||||
int max_clip = 0;
|
||||
for (int i = 0; i < markers_.size(); ++i) {
|
||||
max_clip = std::max(markers_[i].clip, max_clip);
|
||||
}
|
||||
return max_clip;
|
||||
}
|
||||
|
||||
int Tracks::MaxFrame(int clip) const {
|
||||
int max_frame = 0;
|
||||
for (int i = 0; i < markers_.size(); ++i) {
|
||||
if (markers_[i].clip == clip) {
|
||||
max_frame = std::max(markers_[i].frame, max_frame);
|
||||
}
|
||||
}
|
||||
return max_frame;
|
||||
}
|
||||
|
||||
int Tracks::MaxTrack() const {
|
||||
int max_track = 0;
|
||||
for (int i = 0; i < markers_.size(); ++i) {
|
||||
max_track = std::max(markers_[i].track, max_track);
|
||||
}
|
||||
return max_track;
|
||||
}
|
||||
|
||||
int Tracks::NumMarkers() const {
|
||||
return markers_.size();
|
||||
}
|
||||
|
||||
} // namespace mv
|
||||
84
blender-5.2.0/intern/libmv/libmv/autotrack/tracks.h
Normal file
84
blender-5.2.0/intern/libmv/libmv/autotrack/tracks.h
Normal file
@@ -0,0 +1,84 @@
|
||||
// Copyright (c) 2014 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.
|
||||
//
|
||||
// Author: mierle@gmail.com (Keir Mierle)
|
||||
|
||||
#ifndef LIBMV_AUTOTRACK_TRACKS_H_
|
||||
#define LIBMV_AUTOTRACK_TRACKS_H_
|
||||
|
||||
#include "libmv/autotrack/marker.h"
|
||||
#include "libmv/base/vector.h"
|
||||
|
||||
namespace mv {
|
||||
|
||||
using libmv::vector;
|
||||
|
||||
// The Tracks container stores correspondences between frames.
|
||||
class Tracks {
|
||||
public:
|
||||
Tracks() {}
|
||||
Tracks(const Tracks& other);
|
||||
|
||||
// Create a tracks object with markers already initialized. Copies markers.
|
||||
explicit Tracks(const vector<Marker>& markers);
|
||||
|
||||
// All getters append to the output argument vector.
|
||||
bool GetMarker(int clip, int frame, int track, Marker* marker) const;
|
||||
void GetMarkersForTrack(int track, vector<Marker>* markers) const;
|
||||
void GetMarkersForTrackInClip(int clip,
|
||||
int track,
|
||||
vector<Marker>* markers) const;
|
||||
void GetMarkersInFrame(int clip, int frame, vector<Marker>* markers) const;
|
||||
|
||||
// Get the markers in frame1 and frame2 which have a common track.
|
||||
//
|
||||
// This is not the same as the union of the markers in frame1 and
|
||||
// frame2; each marker is for a track that appears in both images.
|
||||
void GetMarkersForTracksInBothImages(int clip1,
|
||||
int frame1,
|
||||
int clip2,
|
||||
int frame2,
|
||||
vector<Marker>* markers) const;
|
||||
|
||||
void AddMarker(const Marker& marker);
|
||||
|
||||
// Moves the contents of *markers over top of the existing markers. This
|
||||
// destroys *markers in the process (but avoids copies).
|
||||
void SetMarkers(vector<Marker>* markers);
|
||||
bool RemoveMarker(int clip, int frame, int track);
|
||||
void RemoveMarkersForTrack(int track);
|
||||
|
||||
int MaxClip() const;
|
||||
int MaxFrame(int clip) const;
|
||||
int MaxTrack() const;
|
||||
int NumMarkers() const;
|
||||
|
||||
const vector<Marker>& markers() const { return markers_; }
|
||||
|
||||
private:
|
||||
vector<Marker> markers_;
|
||||
|
||||
// TODO(keir): Consider adding access-map data structures to avoid all the
|
||||
// linear lookup penalties for the accessors.
|
||||
};
|
||||
|
||||
} // namespace mv
|
||||
|
||||
#endif // LIBMV_AUTOTRACK_TRACKS_H_
|
||||
52
blender-5.2.0/intern/libmv/libmv/autotrack/tracks_test.cc
Normal file
52
blender-5.2.0/intern/libmv/libmv/autotrack/tracks_test.cc
Normal file
@@ -0,0 +1,52 @@
|
||||
// Copyright (c) 2014 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.
|
||||
//
|
||||
// Author: mierle@gmail.com (Keir Mierle)
|
||||
|
||||
#include "libmv/autotrack/tracks.h"
|
||||
|
||||
#include "libmv/logging/logging.h"
|
||||
#include "testing/testing.h"
|
||||
|
||||
namespace mv {
|
||||
|
||||
TEST(Tracks, MaxFrame) {
|
||||
Marker marker;
|
||||
Tracks tracks;
|
||||
|
||||
// Add some markers to clip 0.
|
||||
marker.clip = 0;
|
||||
marker.frame = 1;
|
||||
tracks.AddMarker(marker);
|
||||
|
||||
// Add some markers to clip 1.
|
||||
marker.clip = 1;
|
||||
marker.frame = 1;
|
||||
tracks.AddMarker(marker);
|
||||
|
||||
marker.clip = 1;
|
||||
marker.frame = 12;
|
||||
tracks.AddMarker(marker);
|
||||
|
||||
EXPECT_EQ(1, tracks.MaxFrame(0));
|
||||
EXPECT_EQ(12, tracks.MaxFrame(1));
|
||||
}
|
||||
|
||||
} // namespace mv
|
||||
71
blender-5.2.0/intern/libmv/libmv/base/aligned_malloc.cc
Normal file
71
blender-5.2.0/intern/libmv/libmv/base/aligned_malloc.cc
Normal file
@@ -0,0 +1,71 @@
|
||||
// Copyright (c) 2014 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/base/aligned_malloc.h"
|
||||
#include "libmv/logging/logging.h"
|
||||
|
||||
#if !defined(__APPLE__) && !defined(__FreeBSD__) && !defined(__NetBSD__) && \
|
||||
!defined(__OpenBSD__)
|
||||
// Needed for memalign on Linux and _aligned_alloc on Windows.
|
||||
# ifdef FREE_WINDOWS
|
||||
/* make sure _aligned_malloc is included */
|
||||
# ifdef __MSVCRT_VERSION__
|
||||
# undef __MSVCRT_VERSION__
|
||||
# endif
|
||||
|
||||
# define __MSVCRT_VERSION__ 0x0700
|
||||
# endif // FREE_WINDOWS
|
||||
|
||||
# include <malloc.h>
|
||||
#else
|
||||
// Apple's malloc is 16-byte aligned, and does not have malloc.h, so include
|
||||
// stdilb instead.
|
||||
# include <cstdlib>
|
||||
#endif
|
||||
|
||||
namespace libmv {
|
||||
|
||||
void* aligned_malloc(int size, int alignment) {
|
||||
#ifdef _WIN32
|
||||
return _aligned_malloc(size, alignment);
|
||||
#elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || \
|
||||
defined(__OpenBSD__)
|
||||
void* result;
|
||||
|
||||
if (posix_memalign(&result, alignment, size)) {
|
||||
// non-zero means allocation error
|
||||
// either no allocation or bad alignment value
|
||||
return NULL;
|
||||
}
|
||||
return result;
|
||||
#else // This is for Linux.
|
||||
return memalign(alignment, size);
|
||||
#endif
|
||||
}
|
||||
|
||||
void aligned_free(void* ptr) {
|
||||
#ifdef _WIN32
|
||||
_aligned_free(ptr);
|
||||
#else
|
||||
free(ptr);
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace libmv
|
||||
34
blender-5.2.0/intern/libmv/libmv/base/aligned_malloc.h
Normal file
34
blender-5.2.0/intern/libmv/libmv/base/aligned_malloc.h
Normal file
@@ -0,0 +1,34 @@
|
||||
// Copyright (c) 2014 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.
|
||||
|
||||
#ifndef LIBMV_BASE_ALIGNED_MALLOC_H_
|
||||
#define LIBMV_BASE_ALIGNED_MALLOC_H_
|
||||
|
||||
namespace libmv {
|
||||
|
||||
// Allocate block of size bytes at least aligned to a given value.
|
||||
void* aligned_malloc(int size, int alignment);
|
||||
|
||||
// Free memory allocated by aligned_malloc.
|
||||
void aligned_free(void* ptr);
|
||||
|
||||
} // namespace libmv
|
||||
|
||||
#endif // LIBMV_BASE_ALIGNED_MALLOC_H_
|
||||
32
blender-5.2.0/intern/libmv/libmv/base/array.h
Normal file
32
blender-5.2.0/intern/libmv/libmv/base/array.h
Normal file
@@ -0,0 +1,32 @@
|
||||
// Copyright (c) 2020 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.
|
||||
|
||||
#ifndef LIBMV_BASE_ARRAY_H_
|
||||
#define LIBMV_BASE_ARRAY_H_
|
||||
|
||||
#include <array>
|
||||
|
||||
namespace libmv {
|
||||
|
||||
using std::array;
|
||||
|
||||
} // namespace libmv
|
||||
|
||||
#endif // LIBMV_BASE_ARRAY_H_
|
||||
38
blender-5.2.0/intern/libmv/libmv/base/id_generator.h
Normal file
38
blender-5.2.0/intern/libmv/libmv/base/id_generator.h
Normal file
@@ -0,0 +1,38 @@
|
||||
// Copyright (c) 2007, 2008 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.
|
||||
|
||||
#ifndef LIBMV_ID_GENERATOR_H
|
||||
#define LIBMV_ID_GENERATOR_H
|
||||
|
||||
namespace libmv {
|
||||
|
||||
template <typename ID>
|
||||
class IdGenerator {
|
||||
public:
|
||||
IdGenerator() : next_(0) {}
|
||||
ID Generate() { return next_++; }
|
||||
|
||||
private:
|
||||
ID next_;
|
||||
};
|
||||
|
||||
} // namespace libmv
|
||||
|
||||
#endif // LIBMV_ID_GENERATOR_H
|
||||
34
blender-5.2.0/intern/libmv/libmv/base/map.h
Normal file
34
blender-5.2.0/intern/libmv/libmv/base/map.h
Normal file
@@ -0,0 +1,34 @@
|
||||
// Copyright (c) 2020 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.
|
||||
|
||||
#ifndef LIBMV_BASE_MAP_H
|
||||
#define LIBMV_BASE_MAP_H
|
||||
|
||||
#include <map>
|
||||
#include <utility>
|
||||
|
||||
namespace libmv {
|
||||
|
||||
using std::make_pair;
|
||||
using std::map;
|
||||
|
||||
} // namespace libmv
|
||||
|
||||
#endif // LIBMV_BASE_MAP_H
|
||||
107
blender-5.2.0/intern/libmv/libmv/base/scoped_ptr.h
Normal file
107
blender-5.2.0/intern/libmv/libmv/base/scoped_ptr.h
Normal file
@@ -0,0 +1,107 @@
|
||||
// Copyright (c) 2009 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.
|
||||
|
||||
#ifndef LIBMV_BASE_SCOPED_PTR_H
|
||||
#define LIBMV_BASE_SCOPED_PTR_H
|
||||
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
|
||||
namespace libmv {
|
||||
|
||||
/**
|
||||
* A handle for a heap-allocated resource that should be freed when it goes out
|
||||
* of scope. This looks similar to the one found in TR1.
|
||||
*/
|
||||
template <typename T>
|
||||
class scoped_ptr {
|
||||
public:
|
||||
scoped_ptr(T* resource) : resource_(resource) {}
|
||||
~scoped_ptr() { reset(0); }
|
||||
|
||||
T* get() const { return resource_; }
|
||||
T* operator->() const { return resource_; }
|
||||
T& operator*() const { return *resource_; }
|
||||
|
||||
void reset(T* new_resource) {
|
||||
if (sizeof(T)) {
|
||||
delete resource_;
|
||||
}
|
||||
resource_ = new_resource;
|
||||
}
|
||||
|
||||
T* release() {
|
||||
T* released_resource = resource_;
|
||||
resource_ = 0;
|
||||
return released_resource;
|
||||
}
|
||||
|
||||
private:
|
||||
// No copying allowed.
|
||||
T* resource_;
|
||||
};
|
||||
|
||||
// Same as scoped_ptr but caller must allocate the data
|
||||
// with new[] and the destructor will free the memory
|
||||
// using delete[].
|
||||
template <typename T>
|
||||
class scoped_array {
|
||||
public:
|
||||
scoped_array(T* array) : array_(array) {}
|
||||
~scoped_array() { reset(NULL); }
|
||||
|
||||
T* get() const { return array_; }
|
||||
|
||||
T& operator[](std::ptrdiff_t i) const {
|
||||
assert(i >= 0);
|
||||
assert(array_ != NULL);
|
||||
return array_[i];
|
||||
}
|
||||
|
||||
void reset(T* new_array) {
|
||||
if (sizeof(T)) {
|
||||
delete[] array_;
|
||||
}
|
||||
array_ = new_array;
|
||||
}
|
||||
|
||||
T* release() {
|
||||
T* released_array = array_;
|
||||
array_ = NULL;
|
||||
return released_array;
|
||||
}
|
||||
|
||||
private:
|
||||
T* array_;
|
||||
|
||||
// Forbid comparison of different scoped_array types.
|
||||
template <typename T2>
|
||||
bool operator==(scoped_array<T2> const& p2) const;
|
||||
template <typename T2>
|
||||
bool operator!=(scoped_array<T2> const& p2) const;
|
||||
|
||||
// Disallow evil constructors
|
||||
scoped_array(const scoped_array&);
|
||||
void operator=(const scoped_array&);
|
||||
};
|
||||
|
||||
} // namespace libmv
|
||||
|
||||
#endif // LIBMV_BASE_SCOPED_PTR_H
|
||||
79
blender-5.2.0/intern/libmv/libmv/base/scoped_ptr_test.cc
Normal file
79
blender-5.2.0/intern/libmv/libmv/base/scoped_ptr_test.cc
Normal file
@@ -0,0 +1,79 @@
|
||||
// Copyright (c) 2009 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/base/scoped_ptr.h"
|
||||
#include "testing/testing.h"
|
||||
|
||||
namespace libmv {
|
||||
namespace {
|
||||
|
||||
struct FreeMe {
|
||||
FreeMe(int* freed) : freed(freed) {}
|
||||
~FreeMe() { (*freed)++; }
|
||||
int* freed;
|
||||
};
|
||||
|
||||
TEST(ScopedPtr, NullDoesNothing) {
|
||||
scoped_ptr<FreeMe> x(NULL);
|
||||
// Does nothing.
|
||||
}
|
||||
|
||||
TEST(ScopedPtr, FreesWhenOutOfScope) {
|
||||
int frees = 0;
|
||||
{
|
||||
scoped_ptr<FreeMe> scoped(new FreeMe(&frees));
|
||||
EXPECT_EQ(0, frees);
|
||||
}
|
||||
EXPECT_EQ(1, frees);
|
||||
}
|
||||
|
||||
TEST(ScopedPtr, Operators) {
|
||||
int tag = 123;
|
||||
scoped_ptr<FreeMe> scoped(new FreeMe(&tag));
|
||||
EXPECT_EQ(123, *(scoped->freed));
|
||||
EXPECT_EQ(123, *((*scoped).freed));
|
||||
}
|
||||
|
||||
TEST(ScopedPtr, Reset) {
|
||||
int frees = 0;
|
||||
scoped_ptr<FreeMe> scoped(new FreeMe(&frees));
|
||||
EXPECT_EQ(0, frees);
|
||||
scoped.reset(new FreeMe(&frees));
|
||||
EXPECT_EQ(1, frees);
|
||||
}
|
||||
|
||||
TEST(ScopedPtr, ReleaseAndGet) {
|
||||
int frees = 0;
|
||||
FreeMe* allocated = new FreeMe(&frees);
|
||||
FreeMe* released = NULL;
|
||||
{
|
||||
scoped_ptr<FreeMe> scoped(allocated);
|
||||
EXPECT_EQ(0, frees);
|
||||
EXPECT_EQ(allocated, scoped.get());
|
||||
released = scoped.release();
|
||||
EXPECT_EQ(0, frees);
|
||||
EXPECT_EQ(released, allocated);
|
||||
}
|
||||
EXPECT_EQ(0, frees);
|
||||
delete released;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace libmv
|
||||
43
blender-5.2.0/intern/libmv/libmv/base/vector.h
Normal file
43
blender-5.2.0/intern/libmv/libmv/base/vector.h
Normal file
@@ -0,0 +1,43 @@
|
||||
// Copyright (c) 2007, 2008 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.
|
||||
//
|
||||
// Get an aligned vector implementation. Must be included before <vector>. The
|
||||
// Eigen guys went through some trouble to make a portable override for the
|
||||
// fixed size vector types.
|
||||
|
||||
#ifndef LIBMV_BASE_VECTOR_H
|
||||
#define LIBMV_BASE_VECTOR_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <Eigen/Core>
|
||||
|
||||
namespace libmv {
|
||||
|
||||
// A simple container class, which guarantees the correct memory alignment
|
||||
// needed for most eigen vectorization. Don't use this container for classes
|
||||
// that cannot be copied via memcpy.
|
||||
|
||||
template <class ElementType>
|
||||
using vector = std::vector<ElementType, Eigen::aligned_allocator<ElementType>>;
|
||||
|
||||
} // namespace libmv
|
||||
|
||||
#endif // LIBMV_BASE_VECTOR_H
|
||||
207
blender-5.2.0/intern/libmv/libmv/base/vector_test.cc
Normal file
207
blender-5.2.0/intern/libmv/libmv/base/vector_test.cc
Normal file
@@ -0,0 +1,207 @@
|
||||
// Copyright (c) 2009 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/base/vector.h"
|
||||
#include <algorithm>
|
||||
#include "libmv/numeric/numeric.h"
|
||||
#include "testing/testing.h"
|
||||
|
||||
namespace {
|
||||
using namespace libmv;
|
||||
|
||||
// This uses a Vec2d which is a fixed-size vectorizable Eigen type. It is
|
||||
// necessary to test vectorizable types to ensure that the alignment asserts
|
||||
// trigger if the alignment is not correct.
|
||||
TEST(VectorAlignmentTest, PushBack) {
|
||||
Vec2 x1, x2;
|
||||
x1 << 1, 2;
|
||||
x2 << 3, 4;
|
||||
|
||||
vector<Vec2> vs;
|
||||
vs.push_back(x1);
|
||||
EXPECT_EQ(1, vs.size());
|
||||
|
||||
vs.push_back(x2);
|
||||
EXPECT_EQ(2, vs.size());
|
||||
|
||||
// The following is necessary because of some bug in gtest; the expected
|
||||
// parameter can't be a fixed size vectorizable type with alignment
|
||||
// requirements.
|
||||
Vec x1r = x1;
|
||||
Vec x2r = x2;
|
||||
EXPECT_EQ(x1r, vs[0]);
|
||||
EXPECT_EQ(x2r, vs[1]);
|
||||
|
||||
vs.push_back(x2);
|
||||
vs.push_back(x2);
|
||||
vs.push_back(x2);
|
||||
EXPECT_EQ(5, vs.size());
|
||||
}
|
||||
|
||||
// Count the number of destruct calls to test that the destructor gets called.
|
||||
int foo_construct_calls = 0;
|
||||
int foo_destruct_calls = 0;
|
||||
|
||||
struct Foo {
|
||||
public:
|
||||
Foo() : value(5) { foo_construct_calls++; }
|
||||
~Foo() { foo_destruct_calls++; }
|
||||
int value;
|
||||
};
|
||||
|
||||
struct VectorTest : public testing::Test {
|
||||
VectorTest() {
|
||||
foo_construct_calls = 0;
|
||||
foo_destruct_calls = 0;
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(VectorTest, EmptyVectorDoesNotConstruct) {
|
||||
{
|
||||
vector<Foo> v;
|
||||
EXPECT_EQ(0, v.size());
|
||||
}
|
||||
EXPECT_EQ(0, foo_construct_calls);
|
||||
EXPECT_EQ(0, foo_destruct_calls);
|
||||
}
|
||||
|
||||
TEST_F(VectorTest, DestructorGetsCalled) {
|
||||
{
|
||||
vector<Foo> v;
|
||||
v.resize(5);
|
||||
}
|
||||
EXPECT_EQ(5, foo_construct_calls);
|
||||
EXPECT_EQ(5, foo_destruct_calls);
|
||||
}
|
||||
|
||||
TEST_F(VectorTest, ReserveDoesNotCallConstructorsOrDestructors) {
|
||||
vector<Foo> v;
|
||||
EXPECT_EQ(0, v.size());
|
||||
EXPECT_EQ(0, foo_construct_calls);
|
||||
EXPECT_EQ(0, foo_destruct_calls);
|
||||
|
||||
v.reserve(5);
|
||||
EXPECT_EQ(0, v.size());
|
||||
EXPECT_EQ(0, foo_construct_calls);
|
||||
EXPECT_EQ(0, foo_destruct_calls);
|
||||
}
|
||||
|
||||
TEST_F(VectorTest, ResizeConstructsAndDestructsAsExpected) {
|
||||
vector<Foo> v;
|
||||
|
||||
// Create one object.
|
||||
v.resize(1);
|
||||
EXPECT_EQ(1, v.size());
|
||||
EXPECT_EQ(1, foo_construct_calls);
|
||||
EXPECT_EQ(5, v[0].value);
|
||||
|
||||
// Create two more.
|
||||
v.resize(3);
|
||||
EXPECT_EQ(3, v.size());
|
||||
EXPECT_EQ(3, foo_construct_calls);
|
||||
|
||||
// Delete the last one.
|
||||
v.resize(2);
|
||||
EXPECT_EQ(2, v.size());
|
||||
EXPECT_EQ(3, foo_construct_calls);
|
||||
|
||||
// Delete the remaining two.
|
||||
v.resize(0);
|
||||
EXPECT_EQ(0, v.size());
|
||||
EXPECT_EQ(3, foo_construct_calls);
|
||||
}
|
||||
|
||||
TEST_F(VectorTest, PushPopBack) {
|
||||
vector<Foo> v;
|
||||
|
||||
Foo foo;
|
||||
foo.value = 10;
|
||||
v.push_back(foo);
|
||||
EXPECT_EQ(1, v.size());
|
||||
EXPECT_EQ(10, v.back().value);
|
||||
|
||||
v.pop_back();
|
||||
EXPECT_EQ(0, v.size());
|
||||
EXPECT_EQ(1, foo_construct_calls);
|
||||
EXPECT_EQ(1, foo_destruct_calls);
|
||||
}
|
||||
|
||||
TEST_F(VectorTest, CopyConstructor) {
|
||||
vector<int> a;
|
||||
a.push_back(1);
|
||||
a.push_back(5);
|
||||
a.push_back(3);
|
||||
|
||||
vector<int> b(a);
|
||||
EXPECT_EQ(a.size(), b.size());
|
||||
for (int i = 0; i < a.size(); ++i) {
|
||||
EXPECT_EQ(a[i], b[i]);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(VectorTest, OperatorEquals) {
|
||||
vector<int> a, b;
|
||||
a.push_back(1);
|
||||
a.push_back(5);
|
||||
a.push_back(3);
|
||||
|
||||
b = a;
|
||||
|
||||
EXPECT_EQ(a.size(), b.size());
|
||||
for (int i = 0; i < a.size(); ++i) {
|
||||
EXPECT_EQ(a[i], b[i]);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(VectorTest, STLFind) {
|
||||
vector<int> a;
|
||||
a.push_back(1);
|
||||
a.push_back(5);
|
||||
a.push_back(3);
|
||||
|
||||
// Find returns an int *
|
||||
EXPECT_EQ(std::find(&a[0], &a[2], 1) == &a[0], true);
|
||||
EXPECT_EQ(std::find(&a[0], &a[2], 5) == &a[1], true);
|
||||
EXPECT_EQ(std::find(&a[0], &a[2], 3) == &a[2], true);
|
||||
|
||||
// Find returns an interator
|
||||
EXPECT_EQ(std::find(a.begin(), a.end(), 1) == std::next(a.begin(), 0), true);
|
||||
EXPECT_EQ(std::find(a.begin(), a.end(), 5) == std::next(a.begin(), 1), true);
|
||||
EXPECT_EQ(std::find(a.begin(), a.end(), 3) == std::next(a.begin(), 2), true);
|
||||
|
||||
// Search value that are not in the vector
|
||||
EXPECT_EQ(std::find(a.begin(), a.end(), 0) == a.end(), true);
|
||||
EXPECT_EQ(std::find(a.begin(), a.end(), 52) == a.end(), true);
|
||||
}
|
||||
|
||||
TEST(Vector, swap) {
|
||||
vector<int> a, b;
|
||||
a.push_back(1);
|
||||
a.push_back(2);
|
||||
b.push_back(3);
|
||||
a.swap(b);
|
||||
EXPECT_EQ(1, a.size());
|
||||
EXPECT_EQ(3, a[0]);
|
||||
EXPECT_EQ(2, b.size());
|
||||
EXPECT_EQ(1, b[0]);
|
||||
EXPECT_EQ(2, b[1]);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
33
blender-5.2.0/intern/libmv/libmv/base/vector_utils.h
Normal file
33
blender-5.2.0/intern/libmv/libmv/base/vector_utils.h
Normal file
@@ -0,0 +1,33 @@
|
||||
// Copyright (c) 2009 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.
|
||||
|
||||
#ifndef LIBMV_BASE_VECTOR_UTILS_H_
|
||||
#define LIBMV_BASE_VECTOR_UTILS_H_
|
||||
|
||||
/// Delete the contents of a container.
|
||||
template <class Array>
|
||||
void DeleteElements(Array* array) {
|
||||
for (int i = 0; i < array->size(); ++i) {
|
||||
delete (*array)[i];
|
||||
}
|
||||
array->clear();
|
||||
}
|
||||
|
||||
#endif // LIBMV_BASE_VECTOR_UTILS_H_
|
||||
413
blender-5.2.0/intern/libmv/libmv/build/build_config.h
Normal file
413
blender-5.2.0/intern/libmv/libmv/build/build_config.h
Normal file
@@ -0,0 +1,413 @@
|
||||
// Copyright (c) 2020 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.
|
||||
|
||||
#ifndef LIBMV_BUILD_BUILD_CONFIG_H_
|
||||
#define LIBMV_BUILD_BUILD_CONFIG_H_
|
||||
|
||||
#include <climits>
|
||||
#include <cstdint>
|
||||
|
||||
// Initially is based on Chromium's build_config.h, with tweaks and extensions
|
||||
// needed for this project.
|
||||
//
|
||||
// NOTE: All commonly used symbols (which are checked on a "top" level, from
|
||||
// outside of any platform-specific ifdef block) are to be explicitly defined
|
||||
// to 0 when they are not "active". This is extra lines of code in this file,
|
||||
// but is not being edited that often. Such approach helps catching cases when
|
||||
// one attempted to access build configuration variable without including the
|
||||
// header by simply using -Wundef compiler attribute.
|
||||
//
|
||||
// NOTE: Not having things explicitly defined to 0 is harmless (in terms it
|
||||
// follows same rules as Google projects) and will simply cause compiler to
|
||||
// become more noisy, which is simple to correct.
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// A set of macros to use for platform detection.
|
||||
|
||||
#if defined(__native_client__)
|
||||
// __native_client__ must be first, so that other OS_ defines are not set.
|
||||
# define OS_NACL 1
|
||||
// OS_NACL comes in two sandboxing technology flavors, SFI or Non-SFI.
|
||||
// PNaCl toolchain defines __native_client_nonsfi__ macro in Non-SFI build
|
||||
// mode, while it does not in SFI build mode.
|
||||
# if defined(__native_client_nonsfi__)
|
||||
# define OS_NACL_NONSFI
|
||||
# else
|
||||
# define OS_NACL_SFI
|
||||
# endif
|
||||
#elif defined(_AIX)
|
||||
# define OS_AIX 1
|
||||
#elif defined(ANDROID)
|
||||
# define OS_ANDROID 1
|
||||
#elif defined(__APPLE__)
|
||||
// Only include TargetConditions after testing ANDROID as some android builds
|
||||
// on mac don't have this header available and it's not needed unless the target
|
||||
// is really mac/ios.
|
||||
# include <TargetConditionals.h>
|
||||
# define OS_MACOSX 1
|
||||
# if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
|
||||
# define OS_IOS 1
|
||||
# endif // defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
|
||||
#elif defined(__HAIKU__)
|
||||
# define OS_HAIKU 1
|
||||
#elif defined(__hpux)
|
||||
# define OS_HPUX 1
|
||||
#elif defined(__linux__)
|
||||
# define OS_LINUX 1
|
||||
// Include a system header to pull in features.h for glibc/uclibc macros.
|
||||
# include <unistd.h>
|
||||
# if defined(__GLIBC__) && !defined(__UCLIBC__)
|
||||
// We really are using glibc, not uClibc pretending to be glibc.
|
||||
# define LIBC_GLIBC 1
|
||||
# endif
|
||||
#elif defined(__sgi)
|
||||
# define OS_IRIX 1
|
||||
#elif defined(_WIN32)
|
||||
# define OS_WIN 1
|
||||
#elif defined(__Fuchsia__)
|
||||
# define OS_FUCHSIA 1
|
||||
#elif defined(__FreeBSD__)
|
||||
# define OS_FREEBSD 1
|
||||
#elif defined(__NetBSD__)
|
||||
# define OS_NETBSD 1
|
||||
#elif defined(__OpenBSD__)
|
||||
# define OS_OPENBSD 1
|
||||
#elif defined(__sun)
|
||||
# define OS_SOLARIS 1
|
||||
#elif defined(__QNXNTO__)
|
||||
# define OS_QNX 1
|
||||
#elif defined(__asmjs__) || defined(__wasm__)
|
||||
# define OS_ASMJS
|
||||
#else
|
||||
# error Please add support for your platform in build/build_config.h
|
||||
#endif
|
||||
|
||||
#if !defined(OS_AIX)
|
||||
# define OS_AIX 0
|
||||
#endif
|
||||
#if !defined(OS_ASMJS)
|
||||
# define OS_ASMJS 0
|
||||
#endif
|
||||
#if !defined(OS_NACL)
|
||||
# define OS_NACL 0
|
||||
#endif
|
||||
#if !defined(OS_NACL_NONSFI)
|
||||
# define OS_NACL_NONSFI 0
|
||||
#endif
|
||||
#if !defined(OS_NACL_SFI)
|
||||
# define OS_NACL_SFI 0
|
||||
#endif
|
||||
#if !defined(OS_ANDROID)
|
||||
# define OS_ANDROID 0
|
||||
#endif
|
||||
#if !defined(OS_MACOSX)
|
||||
# define OS_MACOSX 0
|
||||
#endif
|
||||
#if !defined(OS_IOS)
|
||||
# define OS_IOS 0
|
||||
#endif
|
||||
#if !defined(OS_HAIKU)
|
||||
# define OS_HAIKU 0
|
||||
#endif
|
||||
#if !defined(OS_HPUX)
|
||||
# define OS_HPUX 0
|
||||
#endif
|
||||
#if !defined(OS_IRIX)
|
||||
# define OS_IRIX 0
|
||||
#endif
|
||||
#if !defined(OS_LINUX)
|
||||
# define OS_LINUX 0
|
||||
#endif
|
||||
#if !defined(LIBC_GLIBC)
|
||||
# define LIBC_GLIBC 0
|
||||
#endif
|
||||
#if !defined(OS_WIN)
|
||||
# define OS_WIN 0
|
||||
#endif
|
||||
#if !defined(OS_FUCHSIA)
|
||||
# define OS_FUCHSIA 0
|
||||
#endif
|
||||
#if !defined(OS_FREEBSD)
|
||||
# define OS_FREEBSD 0
|
||||
#endif
|
||||
#if !defined(OS_NETBSD)
|
||||
# define OS_NETBSD 0
|
||||
#endif
|
||||
#if !defined(OS_OPENBSD)
|
||||
# define OS_OPENBSD 0
|
||||
#endif
|
||||
#if !defined(OS_SOLARIS)
|
||||
# define OS_SOLARIS 0
|
||||
#endif
|
||||
#if !defined(OS_QNX)
|
||||
# define OS_QNX 0
|
||||
#endif
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Apple OS family detection.
|
||||
|
||||
#if defined(OS_MAC) || defined(OS_IOS)
|
||||
# define OS_APPLE 1
|
||||
#else
|
||||
# define OS_APPLE 0
|
||||
#endif
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// *BSD OS family detection.
|
||||
//
|
||||
// For access to standard BSD features, use OS_BSD instead of a
|
||||
// more specific macro.
|
||||
|
||||
#if OS_FREEBSD || OS_OPENBSD || OS_NETBSD
|
||||
# define OS_BSD 1
|
||||
#else
|
||||
# define OS_BSD 0
|
||||
#endif
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// POSIX system detection.
|
||||
//
|
||||
// For access to standard POSIXish features use OS_POSIX instead of a
|
||||
// more specific macro.
|
||||
|
||||
#if OS_AIX || OS_ANDROID || OS_ASMJS || OS_FREEBSD || OS_LINUX || OS_MACOSX || \
|
||||
OS_NACL || OS_NETBSD || OS_OPENBSD || OS_QNX || OS_SOLARIS
|
||||
# define OS_POSIX 1
|
||||
#else
|
||||
# define OS_POSIX 0
|
||||
#endif
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Compiler detection, including its capabilities.
|
||||
|
||||
#if defined(__clang__)
|
||||
# define COMPILER_CLANG 1
|
||||
#elif defined(__GNUC__)
|
||||
# define COMPILER_GCC 1
|
||||
# define COMPILER_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
|
||||
#elif defined(_MSC_VER)
|
||||
# define COMPILER_MSVC 1
|
||||
# define COMPILER_MSVC_VERSION (_MSC_VER)
|
||||
#elif defined(__MINGW32__)
|
||||
# define COMPILER_MINGW32 1
|
||||
#elif defined(__MINGW64__)
|
||||
# define COMPILER_MINGW64 1
|
||||
#else
|
||||
# error Please add support for your compiler in build/build_config.h
|
||||
#endif
|
||||
|
||||
#if !defined(COMPILER_CLANG)
|
||||
# define COMPILER_CLANG 0
|
||||
#endif
|
||||
#if !defined(COMPILER_GCC)
|
||||
# define COMPILER_GCC 0
|
||||
#endif
|
||||
#if !defined(COMPILER_MSVC)
|
||||
# define COMPILER_MSVC 0
|
||||
#endif
|
||||
#if !defined(COMPILER_MINGW32)
|
||||
# define COMPILER_MINGW32 0
|
||||
#endif
|
||||
#if !defined(COMPILER_MINGW64)
|
||||
# define COMPILER_MINGW64 0
|
||||
#endif
|
||||
|
||||
// Compiler is any of MinGW family.
|
||||
#if COMPILER_MINGW32 || COMPILER_MINGW64
|
||||
# define COMPILER_MINGW 1
|
||||
#else
|
||||
# define COMPILER_MINGW 0
|
||||
#endif
|
||||
|
||||
// Check what is the latest C++ specification the compiler supports.
|
||||
//
|
||||
// NOTE: Use explicit definition here to avoid expansion-to-defined warning from
|
||||
// being generated. While this will most likely a false-positive warning in this
|
||||
// particular case, that warning might be helpful to catch errors elsewhere.
|
||||
|
||||
// C++11 check.
|
||||
#if ((defined(__cplusplus) && (__cplusplus > 199711L)) || \
|
||||
(defined(_MSC_VER) && (_MSC_VER >= 1800)))
|
||||
# define COMPILER_SUPPORTS_CXX11 1
|
||||
#else
|
||||
# define COMPILER_SUPPORTS_CXX11 0
|
||||
#endif
|
||||
// C++14 check.
|
||||
#if (defined(__cplusplus) && (__cplusplus > 201311L))
|
||||
# define COMPILER_SUPPORTS_CXX14 1
|
||||
#else
|
||||
# define COMPILER_SUPPORTS_CXX14 0
|
||||
#endif
|
||||
// C++17 check.
|
||||
#if (defined(__cplusplus) && (__cplusplus > 201611L))
|
||||
# define COMPILER_SUPPORTS_CXX17 1
|
||||
#else
|
||||
# define COMPILER_SUPPORTS_CXX17 0
|
||||
#endif
|
||||
// C++20 check.
|
||||
#if (defined(__cplusplus) && (__cplusplus > 201911L))
|
||||
# define COMPILER_SUPPORTS_CXX20 1
|
||||
#else
|
||||
# define COMPILER_SUPPORTS_CXX20 0
|
||||
#endif
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Processor architecture detection.
|
||||
//
|
||||
// For more info on what's defined, see:
|
||||
//
|
||||
// http://msdn.microsoft.com/en-us/library/b0084kay.aspx
|
||||
// http://www.agner.org/optimize/calling_conventions.pdf
|
||||
//
|
||||
// or with gcc, run: "echo | gcc -E -dM -"
|
||||
#if defined(_M_X64) || defined(__x86_64__)
|
||||
# define ARCH_CPU_X86_FAMILY 1
|
||||
# define ARCH_CPU_X86_64 1
|
||||
# define ARCH_CPU_64_BITS 1
|
||||
# define ARCH_CPU_LITTLE_ENDIAN 1
|
||||
#elif defined(_M_IX86) || defined(__i386__)
|
||||
# define ARCH_CPU_X86_FAMILY 1
|
||||
# define ARCH_CPU_X86 1
|
||||
# define ARCH_CPU_32_BITS 1
|
||||
# define ARCH_CPU_LITTLE_ENDIAN 1
|
||||
#elif defined(__s390x__)
|
||||
# define ARCH_CPU_S390_FAMILY 1
|
||||
# define ARCH_CPU_S390X 1
|
||||
# define ARCH_CPU_64_BITS 1
|
||||
# define ARCH_CPU_BIG_ENDIAN 1
|
||||
#elif defined(__s390__)
|
||||
# define ARCH_CPU_S390_FAMILY 1
|
||||
# define ARCH_CPU_S390 1
|
||||
# define ARCH_CPU_31_BITS 1
|
||||
# define ARCH_CPU_BIG_ENDIAN 1
|
||||
#elif (defined(__PPC64__) || defined(__PPC__)) && /* NOLINT */ \
|
||||
defined(__BIG_ENDIAN__) // NOLINT
|
||||
# define ARCH_CPU_PPC64_FAMILY 1
|
||||
# define ARCH_CPU_PPC64 1
|
||||
# define ARCH_CPU_64_BITS 1
|
||||
# define ARCH_CPU_BIG_ENDIAN 1
|
||||
#elif defined(__PPC64__)
|
||||
# define ARCH_CPU_PPC64_FAMILY 1
|
||||
# define ARCH_CPU_PPC64 1
|
||||
# define ARCH_CPU_64_BITS 1
|
||||
# define ARCH_CPU_LITTLE_ENDIAN 1
|
||||
#elif defined(__ARMEL__)
|
||||
# define ARCH_CPU_ARM_FAMILY 1
|
||||
# define ARCH_CPU_ARMEL 1
|
||||
# define ARCH_CPU_32_BITS 1
|
||||
# define ARCH_CPU_LITTLE_ENDIAN 1
|
||||
#elif defined(__aarch64__) || defined(_M_ARM64)
|
||||
# define ARCH_CPU_ARM_FAMILY 1
|
||||
# define ARCH_CPU_ARM64 1
|
||||
# define ARCH_CPU_64_BITS 1
|
||||
# define ARCH_CPU_LITTLE_ENDIAN 1
|
||||
#elif defined(__pnacl__) || defined(__asmjs__) || defined(__wasm__)
|
||||
# define ARCH_CPU_32_BITS 1
|
||||
# define ARCH_CPU_LITTLE_ENDIAN 1
|
||||
#elif defined(__loongarch_lp64)
|
||||
# define ARCH_CPU_LOONG_FAMILY 1
|
||||
# define ARCH_CPU_LOONG64 1
|
||||
# define ARCH_CPU_64_BITS 1
|
||||
# define ARCH_CPU_LITTLE_ENDIAN 1
|
||||
#elif defined(__MIPSEL__)
|
||||
# if defined(__LP64__)
|
||||
# define ARCH_CPU_MIPS_FAMILY 1
|
||||
# define ARCH_CPU_MIPS64EL 1
|
||||
# define ARCH_CPU_64_BITS 1
|
||||
# define ARCH_CPU_LITTLE_ENDIAN 1
|
||||
# else
|
||||
# define ARCH_CPU_MIPS_FAMILY 1
|
||||
# define ARCH_CPU_MIPSEL 1
|
||||
# define ARCH_CPU_32_BITS 1
|
||||
# define ARCH_CPU_LITTLE_ENDIAN 1
|
||||
# endif
|
||||
#elif defined(__MIPSEB__)
|
||||
# if defined(__LP64__)
|
||||
# define ARCH_CPU_MIPS_FAMILY 1
|
||||
# define ARCH_CPU_MIPS64 1
|
||||
# define ARCH_CPU_64_BITS 1
|
||||
# define ARCH_CPU_BIG_ENDIAN 1
|
||||
# else
|
||||
# define ARCH_CPU_MIPS_FAMILY 1
|
||||
# define ARCH_CPU_MIPS 1
|
||||
# define ARCH_CPU_32_BITS 1
|
||||
# define ARCH_CPU_BIG_ENDIAN 1
|
||||
# endif
|
||||
#elif defined(__riscv)
|
||||
# define ARCH_CPU_RISCV_FAMILY 1
|
||||
# if defined(__LP128__)
|
||||
# define ARCH_CPU_RISCV128 1
|
||||
# define ARCH_CPU_128_BITS 1
|
||||
# elif defined(__LP64__)
|
||||
# define ARCH_CPU_RISCV64 1
|
||||
# define ARCH_CPU_64_BITS 1
|
||||
# else
|
||||
# define ARCH_CPU_RISCV32 1
|
||||
# define ARCH_CPU_32_BITS 1
|
||||
# endif
|
||||
# if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
|
||||
# define ARCH_CPU_LITTLE_ENDIAN 1
|
||||
# else
|
||||
# define ARCH_CPU_BIG_ENDIAN 1
|
||||
# endif
|
||||
#else
|
||||
# error Please add support for your architecture in build/build_config.h
|
||||
#endif
|
||||
|
||||
#if !defined(ARCH_CPU_LITTLE_ENDIAN)
|
||||
# define ARCH_CPU_LITTLE_ENDIAN 0
|
||||
#endif
|
||||
#if !defined(ARCH_CPU_BIG_ENDIAN)
|
||||
# define ARCH_CPU_BIG_ENDIAN 0
|
||||
#endif
|
||||
|
||||
#if !defined(ARCH_CPU_32_BITS)
|
||||
# define ARCH_CPU_32_BITS 0
|
||||
#endif
|
||||
#if !defined(ARCH_CPU_64_BITS)
|
||||
# define ARCH_CPU_64_BITS 0
|
||||
#endif
|
||||
#if !defined(ARCH_CPU_128_BITS)
|
||||
# define ARCH_CPU_128_BITS 0
|
||||
#endif
|
||||
|
||||
#if !defined(ARCH_CPU_X86_FAMILY)
|
||||
# define ARCH_CPU_X86_FAMILY 0
|
||||
#endif
|
||||
#if !defined(ARCH_CPU_ARM_FAMILY)
|
||||
# define ARCH_CPU_ARM_FAMILY 0
|
||||
#endif
|
||||
#if !defined(ARCH_CPU_LOONG_FAMILY)
|
||||
# define ARCH_CPU_LOONG_FAMILY 0
|
||||
#endif
|
||||
#if !defined(ARCH_CPU_MIPS_FAMILY)
|
||||
# define ARCH_CPU_MIPS_FAMILY 0
|
||||
#endif
|
||||
#if !defined(ARCH_CPU_PPC64_FAMILY)
|
||||
# define ARCH_CPU_PPC64_FAMILY 0
|
||||
#endif
|
||||
#if !defined(ARCH_CPU_S390_FAMILY)
|
||||
# define ARCH_CPU_S390_FAMILY 0
|
||||
#endif
|
||||
#if !defined(ARCH_CPU_RISCV_FAMILY)
|
||||
# define ARCH_CPU_RISCV_FAMILY 0
|
||||
#endif
|
||||
|
||||
#endif // LIBMV_BUILD_BUILD_CONFIG_H_
|
||||
107
blender-5.2.0/intern/libmv/libmv/image/array_nd.cc
Normal file
107
blender-5.2.0/intern/libmv/libmv/image/array_nd.cc
Normal file
@@ -0,0 +1,107 @@
|
||||
// Copyright (c) 2007, 2008 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 <cmath>
|
||||
#include <iostream>
|
||||
#include "libmv/image/image.h"
|
||||
|
||||
namespace libmv {
|
||||
|
||||
void FloatArrayToScaledByteArray(const Array3Df& float_array,
|
||||
Array3Du* byte_array,
|
||||
bool automatic_range_detection) {
|
||||
byte_array->ResizeLike(float_array);
|
||||
float minval = HUGE_VAL;
|
||||
float maxval = -HUGE_VAL;
|
||||
if (automatic_range_detection) {
|
||||
for (int i = 0; i < float_array.Height(); ++i) {
|
||||
for (int j = 0; j < float_array.Width(); ++j) {
|
||||
for (int k = 0; k < float_array.Depth(); ++k) {
|
||||
minval = std::min(minval, float_array(i, j, k));
|
||||
maxval = std::max(maxval, float_array(i, j, k));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
minval = 0;
|
||||
maxval = 1;
|
||||
}
|
||||
for (int i = 0; i < float_array.Height(); ++i) {
|
||||
for (int j = 0; j < float_array.Width(); ++j) {
|
||||
for (int k = 0; k < float_array.Depth(); ++k) {
|
||||
float unscaled = (float_array(i, j, k) - minval) / (maxval - minval);
|
||||
(*byte_array)(i, j, k) = (unsigned char)(255 * unscaled);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ByteArrayToScaledFloatArray(const Array3Du& byte_array,
|
||||
Array3Df* float_array) {
|
||||
float_array->ResizeLike(byte_array);
|
||||
for (int i = 0; i < byte_array.Height(); ++i) {
|
||||
for (int j = 0; j < byte_array.Width(); ++j) {
|
||||
for (int k = 0; k < byte_array.Depth(); ++k) {
|
||||
(*float_array)(i, j, k) = float(byte_array(i, j, k)) / 255.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SplitChannels(const Array3Df& input,
|
||||
Array3Df* channel0,
|
||||
Array3Df* channel1,
|
||||
Array3Df* channel2) {
|
||||
assert(input.Depth() >= 3);
|
||||
channel0->Resize(input.Height(), input.Width());
|
||||
channel1->Resize(input.Height(), input.Width());
|
||||
channel2->Resize(input.Height(), input.Width());
|
||||
for (int row = 0; row < input.Height(); ++row) {
|
||||
for (int column = 0; column < input.Width(); ++column) {
|
||||
(*channel0)(row, column) = input(row, column, 0);
|
||||
(*channel1)(row, column) = input(row, column, 1);
|
||||
(*channel2)(row, column) = input(row, column, 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PrintArray(const Array3Df& array) {
|
||||
using namespace std;
|
||||
|
||||
printf("[\n");
|
||||
for (int r = 0; r < array.Height(); ++r) {
|
||||
printf("[");
|
||||
for (int c = 0; c < array.Width(); ++c) {
|
||||
if (array.Depth() == 1) {
|
||||
printf("%11f, ", array(r, c));
|
||||
} else {
|
||||
printf("[");
|
||||
for (int k = 0; k < array.Depth(); ++k) {
|
||||
printf("%11f, ", array(r, c, k));
|
||||
}
|
||||
printf("],");
|
||||
}
|
||||
}
|
||||
printf("],\n");
|
||||
}
|
||||
printf("]\n");
|
||||
}
|
||||
|
||||
} // namespace libmv
|
||||
471
blender-5.2.0/intern/libmv/libmv/image/array_nd.h
Normal file
471
blender-5.2.0/intern/libmv/libmv/image/array_nd.h
Normal file
@@ -0,0 +1,471 @@
|
||||
// Copyright (c) 2007, 2008 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.
|
||||
|
||||
#ifndef LIBMV_IMAGE_ARRAY_ND_H
|
||||
#define LIBMV_IMAGE_ARRAY_ND_H
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
#include "libmv/image/tuple.h"
|
||||
|
||||
namespace libmv {
|
||||
|
||||
class BaseArray {};
|
||||
|
||||
/// A multidimensional array class.
|
||||
template <typename T, int N>
|
||||
class ArrayND : public BaseArray {
|
||||
public:
|
||||
typedef T Scalar;
|
||||
|
||||
/// Type for the multidimensional indices.
|
||||
typedef Tuple<int, N> Index;
|
||||
|
||||
/// Create an empty array.
|
||||
ArrayND() : data_(NULL), own_data_(true) { Resize(Index(0)); }
|
||||
|
||||
/// Create an array with the specified shape.
|
||||
ArrayND(const Index& shape) : data_(NULL), own_data_(true) { Resize(shape); }
|
||||
|
||||
/// Create an array with the specified shape.
|
||||
ArrayND(int* shape) : data_(NULL), own_data_(true) { Resize(shape); }
|
||||
|
||||
/// Copy constructor.
|
||||
ArrayND(const ArrayND<T, N>& b) : data_(NULL), own_data_(true) {
|
||||
ResizeLike(b);
|
||||
std::memcpy(Data(), b.Data(), sizeof(T) * Size());
|
||||
}
|
||||
|
||||
ArrayND(int s0) : data_(NULL), own_data_(true) { Resize(s0); }
|
||||
ArrayND(int s0, int s1) : data_(NULL), own_data_(true) { Resize(s0, s1); }
|
||||
ArrayND(int s0, int s1, int s2) : data_(NULL), own_data_(true) {
|
||||
Resize(s0, s1, s2);
|
||||
}
|
||||
|
||||
ArrayND(T* data, int s0, int s1, int s2)
|
||||
: shape_(0), strides_(0), data_(data), own_data_(false) {
|
||||
Resize(s0, s1, s2);
|
||||
}
|
||||
|
||||
/// Destructor deletes pixel data.
|
||||
~ArrayND() {
|
||||
if (own_data_) {
|
||||
delete[] data_;
|
||||
}
|
||||
}
|
||||
|
||||
/// Assignation copies pixel data.
|
||||
ArrayND& operator=(const ArrayND<T, N>& b) {
|
||||
assert(this != &b);
|
||||
ResizeLike(b);
|
||||
std::memcpy(Data(), b.Data(), sizeof(T) * Size());
|
||||
return *this;
|
||||
}
|
||||
|
||||
const Index& Shapes() const { return shape_; }
|
||||
|
||||
const Index& Strides() const { return strides_; }
|
||||
|
||||
/// Create an array of shape s.
|
||||
void Resize(const Index& new_shape) {
|
||||
if (data_ != NULL && shape_ == new_shape) {
|
||||
// Don't bother realloacting if the shapes match.
|
||||
return;
|
||||
}
|
||||
shape_.Reset(new_shape);
|
||||
strides_(N - 1) = 1;
|
||||
for (int i = N - 1; i > 0; --i) {
|
||||
strides_(i - 1) = strides_(i) * shape_(i);
|
||||
}
|
||||
if (own_data_) {
|
||||
delete[] data_;
|
||||
data_ = NULL;
|
||||
if (Size() > 0) {
|
||||
data_ = new T[Size()];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename D>
|
||||
void ResizeLike(const ArrayND<D, N>& other) {
|
||||
Resize(other.Shape());
|
||||
}
|
||||
|
||||
/// Resizes the array to shape s. All data is lost.
|
||||
void Resize(const int* new_shape_array) { Resize(Index(new_shape_array)); }
|
||||
|
||||
/// Resize a 1D array to length s0.
|
||||
void Resize(int s0) {
|
||||
assert(N == 1);
|
||||
int shape[] = {s0};
|
||||
Resize(shape);
|
||||
}
|
||||
|
||||
/// Resize a 2D array to shape (s0,s1).
|
||||
void Resize(int s0, int s1) {
|
||||
int shape[N] = {s0, s1};
|
||||
for (int i = 2; i < N; ++i) {
|
||||
shape[i] = 1;
|
||||
}
|
||||
Resize(shape);
|
||||
}
|
||||
|
||||
// Match Eigen2's API.
|
||||
void resize(int rows, int cols) { Resize(rows, cols); }
|
||||
|
||||
/// Resize a 3D array to shape (s0,s1,s2).
|
||||
void Resize(int s0, int s1, int s2) {
|
||||
assert(N == 3);
|
||||
int shape[] = {s0, s1, s2};
|
||||
Resize(shape);
|
||||
}
|
||||
|
||||
template <typename D>
|
||||
void CopyFrom(const ArrayND<D, N>& other) {
|
||||
ResizeLike(other);
|
||||
T* data = Data();
|
||||
const D* other_data = other.Data();
|
||||
for (int i = 0; i < Size(); ++i) {
|
||||
data[i] = T(other_data[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void Fill(T value) {
|
||||
for (int i = 0; i < Size(); ++i) {
|
||||
Data()[i] = value;
|
||||
}
|
||||
}
|
||||
|
||||
// Match Eigen's API.
|
||||
void fill(T value) {
|
||||
for (int i = 0; i < Size(); ++i) {
|
||||
Data()[i] = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// Return a tuple containing the length of each axis.
|
||||
const Index& Shape() const { return shape_; }
|
||||
|
||||
/// Return the length of an axis.
|
||||
int Shape(int axis) const { return shape_(axis); }
|
||||
|
||||
/// Return the distance between neighboring elements along axis.
|
||||
int Stride(int axis) const { return strides_(axis); }
|
||||
|
||||
/// Return the number of elements of the array.
|
||||
int Size() const {
|
||||
int size = 1;
|
||||
for (int i = 0; i < N; ++i) {
|
||||
size *= Shape(i);
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
/// Return the total amount of memory used by the array.
|
||||
int MemorySizeInBytes() const { return sizeof(*this) + Size() * sizeof(T); }
|
||||
|
||||
/// Pointer to the first element of the array.
|
||||
T* Data() { return data_; }
|
||||
|
||||
/// Constant pointer to the first element of the array.
|
||||
const T* Data() const { return data_; }
|
||||
|
||||
/// Distance between the first element and the element at position index.
|
||||
int Offset(const Index& index) const {
|
||||
int offset = 0;
|
||||
for (int i = 0; i < N; ++i) {
|
||||
offset += index(i) * Stride(i);
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
/// 1D specialization.
|
||||
int Offset(int i0) const {
|
||||
assert(N == 1);
|
||||
return i0 * Stride(0);
|
||||
}
|
||||
|
||||
/// 2D specialization.
|
||||
int Offset(int i0, int i1) const {
|
||||
assert(N == 2);
|
||||
return i0 * Stride(0) + i1 * Stride(1);
|
||||
}
|
||||
|
||||
/// 3D specialization.
|
||||
int Offset(int i0, int i1, int i2) const {
|
||||
assert(N == 3);
|
||||
return i0 * Stride(0) + i1 * Stride(1) + i2 * Stride(2);
|
||||
}
|
||||
|
||||
/// Return a reference to the element at position index.
|
||||
T& operator()(const Index& index) {
|
||||
// TODO(pau) Boundary checking in debug mode.
|
||||
return *(Data() + Offset(index));
|
||||
}
|
||||
|
||||
/// 1D specialization.
|
||||
T& operator()(int i0) { return *(Data() + Offset(i0)); }
|
||||
|
||||
/// 2D specialization.
|
||||
T& operator()(int i0, int i1) {
|
||||
assert(0 <= i0 && i0 < Shape(0));
|
||||
assert(0 <= i1 && i1 < Shape(1));
|
||||
return *(Data() + Offset(i0, i1));
|
||||
}
|
||||
|
||||
/// 3D specialization.
|
||||
T& operator()(int i0, int i1, int i2) {
|
||||
assert(0 <= i0 && i0 < Shape(0));
|
||||
assert(0 <= i1 && i1 < Shape(1));
|
||||
assert(0 <= i2 && i2 < Shape(2));
|
||||
return *(Data() + Offset(i0, i1, i2));
|
||||
}
|
||||
|
||||
/// Return a constant reference to the element at position index.
|
||||
const T& operator()(const Index& index) const {
|
||||
return *(Data() + Offset(index));
|
||||
}
|
||||
|
||||
/// 1D specialization.
|
||||
const T& operator()(int i0) const { return *(Data() + Offset(i0)); }
|
||||
|
||||
/// 2D specialization.
|
||||
const T& operator()(int i0, int i1) const {
|
||||
assert(0 <= i0 && i0 < Shape(0));
|
||||
assert(0 <= i1 && i1 < Shape(1));
|
||||
return *(Data() + Offset(i0, i1));
|
||||
}
|
||||
|
||||
/// 3D specialization.
|
||||
const T& operator()(int i0, int i1, int i2) const {
|
||||
return *(Data() + Offset(i0, i1, i2));
|
||||
}
|
||||
|
||||
/// True if index is inside array.
|
||||
bool Contains(const Index& index) const {
|
||||
for (int i = 0; i < N; ++i) {
|
||||
if (index(i) < 0 || index(i) >= Shape(i)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// 1D specialization.
|
||||
bool Contains(int i0) const { return 0 <= i0 && i0 < Shape(0); }
|
||||
|
||||
/// 2D specialization.
|
||||
bool Contains(int i0, int i1) const {
|
||||
return 0 <= i0 && i0 < Shape(0) && 0 <= i1 && i1 < Shape(1);
|
||||
}
|
||||
|
||||
/// 3D specialization.
|
||||
bool Contains(int i0, int i1, int i2) const {
|
||||
return 0 <= i0 && i0 < Shape(0) && 0 <= i1 && i1 < Shape(1) && 0 <= i2 &&
|
||||
i2 < Shape(2);
|
||||
}
|
||||
|
||||
bool operator==(const ArrayND<T, N>& other) const {
|
||||
if (shape_ != other.shape_) {
|
||||
return false;
|
||||
}
|
||||
if (strides_ != other.strides_) {
|
||||
return false;
|
||||
}
|
||||
for (int i = 0; i < Size(); ++i) {
|
||||
if (this->Data()[i] != other.Data()[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool operator!=(const ArrayND<T, N>& other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
ArrayND<T, N> operator*(const ArrayND<T, N>& other) const {
|
||||
assert(Shape() = other.Shape());
|
||||
ArrayND<T, N> res;
|
||||
res.ResizeLike(*this);
|
||||
for (int i = 0; i < res.Size(); ++i) {
|
||||
res.Data()[i] = Data()[i] * other.Data()[i];
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
protected:
|
||||
/// The number of element in each dimension.
|
||||
Index shape_;
|
||||
|
||||
/// How to jump to neighbors in each dimension.
|
||||
Index strides_;
|
||||
|
||||
/// Pointer to the first element of the array.
|
||||
T* data_;
|
||||
|
||||
/// Flag if this Array either own or reference the data
|
||||
bool own_data_;
|
||||
};
|
||||
|
||||
/// 3D array (row, column, channel).
|
||||
template <typename T>
|
||||
class Array3D : public ArrayND<T, 3> {
|
||||
typedef ArrayND<T, 3> Base;
|
||||
|
||||
public:
|
||||
Array3D() : Base() {}
|
||||
Array3D(int height, int width, int depth = 1) : Base(height, width, depth) {}
|
||||
Array3D(T* data, int height, int width, int depth = 1)
|
||||
: Base(data, height, width, depth) {}
|
||||
|
||||
void Resize(int height, int width, int depth = 1) {
|
||||
Base::Resize(height, width, depth);
|
||||
}
|
||||
|
||||
int Height() const { return Base::Shape(0); }
|
||||
int Width() const { return Base::Shape(1); }
|
||||
int Depth() const { return Base::Shape(2); }
|
||||
|
||||
// Match Eigen2's API so that Array3D's and Mat*'s can work together via
|
||||
// template magic.
|
||||
int rows() const { return Height(); }
|
||||
int cols() const { return Width(); }
|
||||
int depth() const { return Depth(); }
|
||||
|
||||
int Get_Step() const { return Width() * Depth(); }
|
||||
|
||||
/// Enable accessing with 2 indices for grayscale images.
|
||||
T& operator()(int i0, int i1, int i2 = 0) {
|
||||
assert(0 <= i0 && i0 < Height());
|
||||
assert(0 <= i1 && i1 < Width());
|
||||
return Base::operator()(i0, i1, i2);
|
||||
}
|
||||
const T& operator()(int i0, int i1, int i2 = 0) const {
|
||||
assert(0 <= i0 && i0 < Height());
|
||||
assert(0 <= i1 && i1 < Width());
|
||||
return Base::operator()(i0, i1, i2);
|
||||
}
|
||||
};
|
||||
|
||||
typedef Array3D<unsigned char> Array3Du;
|
||||
typedef Array3D<unsigned int> Array3Dui;
|
||||
typedef Array3D<int> Array3Di;
|
||||
typedef Array3D<float> Array3Df;
|
||||
typedef Array3D<short> Array3Ds;
|
||||
|
||||
void SplitChannels(const Array3Df& input,
|
||||
Array3Df* channel0,
|
||||
Array3Df* channel1,
|
||||
Array3Df* channel2);
|
||||
|
||||
void PrintArray(const Array3Df& array);
|
||||
|
||||
/** Convert a float array into a byte array by scaling values by 255* (max-min).
|
||||
* where max and min are automatically detected
|
||||
* (if automatic_range_detection = true)
|
||||
* \note and TODO this automatic detection only works when the image contains
|
||||
* at least one pixel of both bounds.
|
||||
*/
|
||||
void FloatArrayToScaledByteArray(const Array3Df& float_array,
|
||||
Array3Du* byte_array,
|
||||
bool automatic_range_detection = false);
|
||||
|
||||
//! Convert a byte array into a float array by dividing values by 255.
|
||||
void ByteArrayToScaledFloatArray(const Array3Du& byte_array,
|
||||
Array3Df* float_array);
|
||||
|
||||
template <typename AArrayType, typename BArrayType, typename CArrayType>
|
||||
void MultiplyElements(const AArrayType& a, const BArrayType& b, CArrayType* c) {
|
||||
// This function does an element-wise multiply between
|
||||
// the two Arrays A and B, and stores the result in C.
|
||||
// A and B must have the same dimensions.
|
||||
assert(a.Shape() == b.Shape());
|
||||
c->ResizeLike(a);
|
||||
|
||||
// To perform the multiplcation, a "current" index into the N-dimensions of
|
||||
// the A and B matrix specifies which elements are being multiplied.
|
||||
typename CArrayType::Index index;
|
||||
|
||||
// The index starts at the maximum value for each dimension
|
||||
const typename CArrayType::Index& cShape = c->Shape();
|
||||
for (int i = 0; i < CArrayType::Index::SIZE; ++i) {
|
||||
index(i) = cShape(i) - 1;
|
||||
}
|
||||
|
||||
// After each multiplication, the highest-dimensional index is reduced.
|
||||
// if this reduces it less than zero, it resets to its maximum value
|
||||
// and decrements the index of the next lower dimension.
|
||||
// This ripple-action continues until the entire new array has been
|
||||
// calculated, indicated by dimension zero having a negative index.
|
||||
while (index(0) >= 0) {
|
||||
(*c)(index) = a(index) * b(index);
|
||||
|
||||
int dimension = CArrayType::Index::SIZE - 1;
|
||||
index(dimension) = index(dimension) - 1;
|
||||
while (dimension > 0 && index(dimension) < 0) {
|
||||
index(dimension) = cShape(dimension) - 1;
|
||||
index(dimension - 1) = index(dimension - 1) - 1;
|
||||
--dimension;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename TA, typename TB, typename TC>
|
||||
void MultiplyElements(const ArrayND<TA, 3>& a,
|
||||
const ArrayND<TB, 3>& b,
|
||||
ArrayND<TC, 3>* c) {
|
||||
// Specialization for N==3
|
||||
c->ResizeLike(a);
|
||||
assert(a.Shape(0) == b.Shape(0));
|
||||
assert(a.Shape(1) == b.Shape(1));
|
||||
assert(a.Shape(2) == b.Shape(2));
|
||||
for (int i = 0; i < a.Shape(0); ++i) {
|
||||
for (int j = 0; j < a.Shape(1); ++j) {
|
||||
for (int k = 0; k < a.Shape(2); ++k) {
|
||||
(*c)(i, j, k) = TC(a(i, j, k) * b(i, j, k));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename TA, typename TB, typename TC>
|
||||
void MultiplyElements(const Array3D<TA>& a,
|
||||
const Array3D<TB>& b,
|
||||
Array3D<TC>* c) {
|
||||
// Specialization for N==3
|
||||
c->ResizeLike(a);
|
||||
assert(a.Shape(0) == b.Shape(0));
|
||||
assert(a.Shape(1) == b.Shape(1));
|
||||
assert(a.Shape(2) == b.Shape(2));
|
||||
for (int i = 0; i < a.Shape(0); ++i) {
|
||||
for (int j = 0; j < a.Shape(1); ++j) {
|
||||
for (int k = 0; k < a.Shape(2); ++k) {
|
||||
(*c)(i, j, k) = TC(a(i, j, k) * b(i, j, k));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace libmv
|
||||
|
||||
#endif // LIBMV_IMAGE_ARRAY_ND_H
|
||||
328
blender-5.2.0/intern/libmv/libmv/image/array_nd_test.cc
Normal file
328
blender-5.2.0/intern/libmv/libmv/image/array_nd_test.cc
Normal file
@@ -0,0 +1,328 @@
|
||||
// Copyright (c) 2007, 2008 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/image/array_nd.h"
|
||||
#include "testing/testing.h"
|
||||
|
||||
using libmv::Array3D;
|
||||
using libmv::Array3Df;
|
||||
using libmv::ArrayND;
|
||||
|
||||
namespace {
|
||||
|
||||
TEST(ArrayND, EmptyConstructor) {
|
||||
ArrayND<int, 2> a;
|
||||
|
||||
EXPECT_EQ(0, a.Shape(0));
|
||||
EXPECT_EQ(0, a.Shape(1));
|
||||
}
|
||||
|
||||
TEST(ArrayND, IndexConstructor) {
|
||||
int s[] = {1, 2, 3};
|
||||
ArrayND<int, 3>::Index shape(s);
|
||||
ArrayND<int, 3> a(shape);
|
||||
|
||||
EXPECT_EQ(1, a.Shape(0));
|
||||
EXPECT_EQ(2, a.Shape(1));
|
||||
EXPECT_EQ(3, a.Shape(2));
|
||||
}
|
||||
|
||||
TEST(ArrayND, PointerConstructor) {
|
||||
int s[] = {1, 2, 3};
|
||||
ArrayND<int, 3> a(s);
|
||||
|
||||
EXPECT_EQ(1, a.Shape(0));
|
||||
EXPECT_EQ(2, a.Shape(1));
|
||||
EXPECT_EQ(3, a.Shape(2));
|
||||
}
|
||||
|
||||
TEST(ArrayND, CopyConstructor) {
|
||||
int s[] = {1, 2, 3};
|
||||
ArrayND<int, 3> a(s);
|
||||
a(0, 1, 1) = 3;
|
||||
a(0, 1, 2) = 3;
|
||||
ArrayND<int, 3> b(a);
|
||||
EXPECT_EQ(1, b.Shape(0));
|
||||
EXPECT_EQ(2, b.Shape(1));
|
||||
EXPECT_EQ(3, b.Shape(2));
|
||||
EXPECT_EQ(3, b(0, 1, 1));
|
||||
b(0, 1, 2) = 2;
|
||||
EXPECT_EQ(3, a(0, 1, 2));
|
||||
}
|
||||
|
||||
TEST(ArrayND, Assignation) {
|
||||
int s[] = {1, 2, 3};
|
||||
ArrayND<int, 3> a(s);
|
||||
a(0, 1, 1) = 3;
|
||||
a(0, 1, 2) = 3;
|
||||
ArrayND<int, 3> b;
|
||||
b = a;
|
||||
EXPECT_EQ(1, b.Shape(0));
|
||||
EXPECT_EQ(2, b.Shape(1));
|
||||
EXPECT_EQ(3, b.Shape(2));
|
||||
EXPECT_EQ(3, b(0, 1, 1));
|
||||
b(0, 1, 2) = 2;
|
||||
EXPECT_EQ(3, a(0, 1, 2));
|
||||
}
|
||||
|
||||
TEST(ArrayND, Fill) {
|
||||
int s[] = {2, 2};
|
||||
ArrayND<int, 2> a(s);
|
||||
a.Fill(42);
|
||||
EXPECT_EQ(42, a(0, 0));
|
||||
EXPECT_EQ(42, a(0, 1));
|
||||
EXPECT_EQ(42, a(1, 0));
|
||||
EXPECT_EQ(42, a(1, 1));
|
||||
}
|
||||
|
||||
TEST(ArrayND, Size) {
|
||||
int s[] = {1, 2, 3};
|
||||
ArrayND<int, 3>::Index shape(s);
|
||||
ArrayND<int, 3> a(shape);
|
||||
|
||||
int l[] = {0, 1, 2};
|
||||
ArrayND<int, 3>::Index last(l);
|
||||
|
||||
EXPECT_EQ(a.Size(), a.Offset(last) + 1);
|
||||
EXPECT_TRUE(a.Contains(last));
|
||||
EXPECT_FALSE(a.Contains(shape));
|
||||
}
|
||||
|
||||
TEST(ArrayND, MemorySizeInBytes) {
|
||||
int s[] = {2, 3};
|
||||
ArrayND<int, 2>::Index shape(s);
|
||||
ArrayND<int, 2> a(shape);
|
||||
|
||||
int size = 24 + sizeof(a);
|
||||
EXPECT_EQ(size, a.MemorySizeInBytes());
|
||||
}
|
||||
|
||||
TEST(ArrayND, Parenthesis) {
|
||||
typedef ArrayND<int, 2>::Index Index;
|
||||
|
||||
int s[] = {3, 3};
|
||||
ArrayND<int, 2> a(s);
|
||||
|
||||
*(a.Data() + 0) = 0;
|
||||
*(a.Data() + 5) = 5;
|
||||
|
||||
int i1[] = {0, 0};
|
||||
EXPECT_EQ(0, a(Index(i1)));
|
||||
int i2[] = {1, 2};
|
||||
EXPECT_EQ(5, a(Index(i2)));
|
||||
}
|
||||
|
||||
TEST(ArrayND, 1DConstructor) {
|
||||
ArrayND<int, 1> a(3);
|
||||
|
||||
EXPECT_EQ(3, a.Shape(0));
|
||||
}
|
||||
|
||||
TEST(ArrayND, 2DConstructor) {
|
||||
ArrayND<int, 2> a(1, 2);
|
||||
|
||||
EXPECT_EQ(1, a.Shape(0));
|
||||
EXPECT_EQ(2, a.Shape(1));
|
||||
}
|
||||
|
||||
TEST(ArrayND, 3DConstructor) {
|
||||
ArrayND<int, 3> a(1, 2, 3);
|
||||
|
||||
EXPECT_EQ(1, a.Shape(0));
|
||||
EXPECT_EQ(2, a.Shape(1));
|
||||
EXPECT_EQ(3, a.Shape(2));
|
||||
}
|
||||
|
||||
TEST(ArrayND, 1DAccessor) {
|
||||
ArrayND<int, 1> a(3);
|
||||
a(0) = 1;
|
||||
a(1) = 2;
|
||||
|
||||
EXPECT_EQ(1, a(0));
|
||||
EXPECT_EQ(2, a(1));
|
||||
EXPECT_EQ(1, *(a.Data()));
|
||||
EXPECT_EQ(2, *(a.Data() + a.Stride(0)));
|
||||
}
|
||||
|
||||
TEST(ArrayND, 2DAccessor) {
|
||||
ArrayND<int, 2> a(3, 3);
|
||||
a(0, 0) = 1;
|
||||
a(1, 1) = 2;
|
||||
|
||||
EXPECT_EQ(1, a(0, 0));
|
||||
EXPECT_EQ(2, a(1, 1));
|
||||
EXPECT_EQ(1, *(a.Data()));
|
||||
EXPECT_EQ(2, *(a.Data() + a.Stride(0) + a.Stride(1)));
|
||||
}
|
||||
|
||||
TEST(ArrayND, 3DAccessor) {
|
||||
ArrayND<int, 3> a(3, 3, 3);
|
||||
a(0, 0, 0) = 1;
|
||||
a(1, 1, 1) = 2;
|
||||
|
||||
EXPECT_EQ(1, a(0, 0, 0));
|
||||
EXPECT_EQ(2, a(1, 1, 1));
|
||||
EXPECT_EQ(1, *(a.Data()));
|
||||
EXPECT_EQ(2, *(a.Data() + a.Stride(0) + a.Stride(1) + a.Stride(2)));
|
||||
}
|
||||
|
||||
TEST(ArrayND, CopyFrom) {
|
||||
ArrayND<int, 3> a(2, 2, 1);
|
||||
a(0, 0, 0) = 1;
|
||||
a(0, 1, 0) = 2;
|
||||
a(1, 0, 0) = 3;
|
||||
a(1, 1, 0) = 4;
|
||||
ArrayND<float, 3> b;
|
||||
b.CopyFrom(a);
|
||||
EXPECT_FLOAT_EQ(1.0f, b(0, 0, 0));
|
||||
EXPECT_FLOAT_EQ(2.0f, b(0, 1, 0));
|
||||
EXPECT_FLOAT_EQ(3.0f, b(1, 0, 0));
|
||||
EXPECT_FLOAT_EQ(4.0f, b(1, 1, 0));
|
||||
}
|
||||
|
||||
TEST(ArrayND, MultiplyElements) {
|
||||
ArrayND<int, 3> a(2, 2, 1);
|
||||
a(0, 0, 0) = 1;
|
||||
a(0, 1, 0) = 2;
|
||||
a(1, 0, 0) = 3;
|
||||
a(1, 1, 0) = 4;
|
||||
ArrayND<int, 3> b(2, 2, 1);
|
||||
b(0, 0, 0) = 6;
|
||||
b(0, 1, 0) = 5;
|
||||
b(1, 0, 0) = 4;
|
||||
b(1, 1, 0) = 3;
|
||||
ArrayND<int, 3> c;
|
||||
MultiplyElements(a, b, &c);
|
||||
EXPECT_FLOAT_EQ(6, c(0, 0, 0));
|
||||
EXPECT_FLOAT_EQ(10, c(0, 1, 0));
|
||||
EXPECT_FLOAT_EQ(12, c(1, 0, 0));
|
||||
EXPECT_FLOAT_EQ(12, c(1, 1, 0));
|
||||
}
|
||||
|
||||
TEST(ArrayND, IsEqualOperator) {
|
||||
ArrayND<int, 3> a(2, 2, 1);
|
||||
a(0, 0, 0) = 1;
|
||||
a(0, 1, 0) = 2;
|
||||
a(1, 0, 0) = 3;
|
||||
a(1, 1, 0) = 4;
|
||||
ArrayND<int, 3> b(2, 2, 1);
|
||||
b(0, 0, 0) = 1;
|
||||
b(0, 1, 0) = 2;
|
||||
b(1, 0, 0) = 3;
|
||||
b(1, 1, 0) = 4;
|
||||
EXPECT_TRUE(a == b);
|
||||
EXPECT_FALSE(a != b);
|
||||
b(1, 1, 0) = 5;
|
||||
EXPECT_TRUE(a != b);
|
||||
EXPECT_FALSE(a == b);
|
||||
}
|
||||
|
||||
TEST(Array3D, Sizes) {
|
||||
Array3D<int> array;
|
||||
EXPECT_EQ(array.Height(), 0);
|
||||
EXPECT_EQ(array.Width(), 0);
|
||||
EXPECT_EQ(array.Depth(), 0);
|
||||
EXPECT_EQ(array.Shape(0), 0);
|
||||
}
|
||||
|
||||
TEST(Array3D, CopyConstructor) {
|
||||
Array3D<int> array(10, 10);
|
||||
array(0, 0) = 1;
|
||||
array(0, 1) = 1;
|
||||
Array3D<int> copy(array);
|
||||
EXPECT_EQ(copy.Height(), 10);
|
||||
EXPECT_EQ(copy.Width(), 10);
|
||||
EXPECT_EQ(copy.Depth(), 1);
|
||||
EXPECT_EQ(copy(0, 0), 1);
|
||||
copy(0, 1) = 2;
|
||||
EXPECT_EQ(array(0, 1), 1);
|
||||
}
|
||||
|
||||
TEST(Array3D, Assignation) {
|
||||
Array3D<int> array(10, 10);
|
||||
array(0, 0) = 1;
|
||||
array(0, 1) = 1;
|
||||
Array3D<int> copy;
|
||||
copy = array;
|
||||
EXPECT_EQ(copy.Height(), 10);
|
||||
EXPECT_EQ(copy.Width(), 10);
|
||||
EXPECT_EQ(copy.Depth(), 1);
|
||||
EXPECT_EQ(copy(0, 0), 1);
|
||||
copy(0, 1) = 2;
|
||||
EXPECT_EQ(array(0, 1), 1);
|
||||
}
|
||||
|
||||
TEST(Array3D, Parenthesis) {
|
||||
Array3D<int> array(1, 2, 3);
|
||||
array(0, 1, 0) = 3;
|
||||
EXPECT_EQ(array(0, 1), 3);
|
||||
}
|
||||
|
||||
TEST(Array3Df, SplitChannels) {
|
||||
Array3Df array(1, 2, 3);
|
||||
array(0, 0, 0) = 1;
|
||||
array(0, 1, 0) = 1;
|
||||
array(0, 0, 1) = 2;
|
||||
array(0, 1, 1) = 2;
|
||||
array(0, 0, 2) = 3;
|
||||
array(0, 1, 2) = 3;
|
||||
Array3Df c0, c1, c2;
|
||||
SplitChannels(array, &c0, &c1, &c2);
|
||||
for (int row = 0; row < 1; ++row) {
|
||||
for (int column = 0; column < 2; ++column) {
|
||||
EXPECT_EQ(array(row, column, 0), c0(row, column));
|
||||
EXPECT_EQ(array(row, column, 1), c1(row, column));
|
||||
EXPECT_EQ(array(row, column, 2), c2(row, column));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(ArrayND, MultiplyElementsGeneric) {
|
||||
ArrayND<double, 5> A;
|
||||
ArrayND<int, 5> B;
|
||||
ArrayND<double, 5> C;
|
||||
int shape[] = {1, 3, 5, 7, 1};
|
||||
A.Resize(shape);
|
||||
B.Resize(shape);
|
||||
|
||||
A.Fill(1.1);
|
||||
B.Fill(2);
|
||||
MultiplyElements(A, B, &C);
|
||||
|
||||
ArrayND<double, 5>::Index cIndex;
|
||||
for (int d0 = 0; d0 < shape[0]; ++d0) {
|
||||
for (int d1 = 0; d1 < shape[1]; ++d1) {
|
||||
for (int d2 = 0; d2 < shape[2]; ++d2) {
|
||||
for (int d3 = 0; d3 < shape[3]; ++d3) {
|
||||
for (int d4 = 0; d4 < shape[4]; ++d4) {
|
||||
cIndex(0) = d0;
|
||||
cIndex(1) = d1;
|
||||
cIndex(2) = d2;
|
||||
cIndex(3) = d3;
|
||||
cIndex(4) = d4;
|
||||
EXPECT_EQ(2.2, C(cIndex));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
351
blender-5.2.0/intern/libmv/libmv/image/convolve.cc
Normal file
351
blender-5.2.0/intern/libmv/libmv/image/convolve.cc
Normal file
@@ -0,0 +1,351 @@
|
||||
// Copyright (c) 2007, 2008 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/image/convolve.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include "libmv/image/image.h"
|
||||
|
||||
namespace libmv {
|
||||
|
||||
// Compute a Gaussian kernel and derivative, such that you can take the
|
||||
// derivative of an image by convolving with the kernel horizontally then the
|
||||
// derivative vertically to get (eg) the y derivative.
|
||||
void ComputeGaussianKernel(double sigma, Vec* kernel, Vec* derivative) {
|
||||
assert(sigma >= 0.0);
|
||||
|
||||
// 0.004 implies a 3 pixel kernel with 1 pixel sigma.
|
||||
const float truncation_factor = 0.004f;
|
||||
|
||||
// Calculate the kernel size based on sigma such that it is odd.
|
||||
float precisehalfwidth = GaussianInversePositive(truncation_factor, sigma);
|
||||
int width = lround(2 * precisehalfwidth);
|
||||
if (width % 2 == 0) {
|
||||
width++;
|
||||
}
|
||||
// Calculate the gaussian kernel and its derivative.
|
||||
kernel->resize(width);
|
||||
derivative->resize(width);
|
||||
kernel->setZero();
|
||||
derivative->setZero();
|
||||
int halfwidth = width / 2;
|
||||
for (int i = -halfwidth; i <= halfwidth; ++i) {
|
||||
(*kernel)(i + halfwidth) = Gaussian(i, sigma);
|
||||
(*derivative)(i + halfwidth) = GaussianDerivative(i, sigma);
|
||||
}
|
||||
// Since images should not get brighter or darker, normalize.
|
||||
NormalizeL1(kernel);
|
||||
|
||||
// Normalize the derivative differently. See
|
||||
// www.cs.duke.edu/courses/spring03/cps296.1/handouts/Image%20Processing.pdf
|
||||
double factor = 0.;
|
||||
for (int i = -halfwidth; i <= halfwidth; ++i) {
|
||||
factor -= i * (*derivative)(i + halfwidth);
|
||||
}
|
||||
*derivative /= factor;
|
||||
}
|
||||
|
||||
template <int size, bool vertical>
|
||||
void FastConvolve(const Vec& kernel,
|
||||
int width,
|
||||
int height,
|
||||
const float* src,
|
||||
int src_stride,
|
||||
int src_line_stride,
|
||||
float* dst,
|
||||
int dst_stride) {
|
||||
double coefficients[2 * size + 1];
|
||||
for (int k = 0; k < 2 * size + 1; ++k) {
|
||||
coefficients[k] = kernel(2 * size - k);
|
||||
}
|
||||
// Fast path: if the kernel has a certain size, use the constant sized loops.
|
||||
for (int y = 0; y < height; ++y) {
|
||||
for (int x = 0; x < width; ++x) {
|
||||
double sum = 0;
|
||||
for (int k = -size; k <= size; ++k) {
|
||||
if (vertical) {
|
||||
if (y + k >= 0 && y + k < height) {
|
||||
sum += src[k * src_line_stride] * coefficients[k + size];
|
||||
}
|
||||
} else {
|
||||
if (x + k >= 0 && x + k < width) {
|
||||
sum += src[k * src_stride] * coefficients[k + size];
|
||||
}
|
||||
}
|
||||
}
|
||||
dst[0] = static_cast<float>(sum);
|
||||
src += src_stride;
|
||||
dst += dst_stride;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <bool vertical>
|
||||
void Convolve(const Array3Df& in,
|
||||
const Vec& kernel,
|
||||
Array3Df* out_pointer,
|
||||
int plane) {
|
||||
int width = in.Width();
|
||||
int height = in.Height();
|
||||
Array3Df& out = *out_pointer;
|
||||
if (plane == -1) {
|
||||
out.ResizeLike(in);
|
||||
plane = 0;
|
||||
}
|
||||
|
||||
assert(kernel.size() % 2 == 1);
|
||||
assert(&in != out_pointer);
|
||||
|
||||
int src_line_stride = in.Stride(0);
|
||||
int src_stride = in.Stride(1);
|
||||
int dst_stride = out.Stride(1);
|
||||
const float* src = in.Data();
|
||||
float* dst = out.Data() + plane;
|
||||
|
||||
// Use a dispatch table to make most convolutions used in practice use the
|
||||
// fast path.
|
||||
int half_width = kernel.size() / 2;
|
||||
switch (half_width) {
|
||||
#define static_convolution(size) \
|
||||
case size: \
|
||||
FastConvolve<size, vertical>(kernel, \
|
||||
width, \
|
||||
height, \
|
||||
src, \
|
||||
src_stride, \
|
||||
src_line_stride, \
|
||||
dst, \
|
||||
dst_stride); \
|
||||
break;
|
||||
static_convolution(1) static_convolution(2) static_convolution(3)
|
||||
static_convolution(4) static_convolution(5) static_convolution(6)
|
||||
static_convolution(7)
|
||||
#undef static_convolution
|
||||
default : int dynamic_size = kernel.size() / 2;
|
||||
for (int y = 0; y < height; ++y) {
|
||||
for (int x = 0; x < width; ++x) {
|
||||
double sum = 0;
|
||||
// Slow path: this loop cannot be unrolled.
|
||||
for (int k = -dynamic_size; k <= dynamic_size; ++k) {
|
||||
if (vertical) {
|
||||
if (y + k >= 0 && y + k < height) {
|
||||
sum += src[k * src_line_stride] *
|
||||
kernel(2 * dynamic_size - (k + dynamic_size));
|
||||
}
|
||||
} else {
|
||||
if (x + k >= 0 && x + k < width) {
|
||||
sum += src[k * src_stride] *
|
||||
kernel(2 * dynamic_size - (k + dynamic_size));
|
||||
}
|
||||
}
|
||||
}
|
||||
dst[0] = static_cast<float>(sum);
|
||||
src += src_stride;
|
||||
dst += dst_stride;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ConvolveHorizontal(const Array3Df& in,
|
||||
const Vec& kernel,
|
||||
Array3Df* out_pointer,
|
||||
int plane) {
|
||||
Convolve<false>(in, kernel, out_pointer, plane);
|
||||
}
|
||||
|
||||
void ConvolveVertical(const Array3Df& in,
|
||||
const Vec& kernel,
|
||||
Array3Df* out_pointer,
|
||||
int plane) {
|
||||
Convolve<true>(in, kernel, out_pointer, plane);
|
||||
}
|
||||
|
||||
void ConvolveGaussian(const Array3Df& in, double sigma, Array3Df* out_pointer) {
|
||||
Vec kernel, derivative;
|
||||
ComputeGaussianKernel(sigma, &kernel, &derivative);
|
||||
|
||||
Array3Df tmp;
|
||||
ConvolveVertical(in, kernel, &tmp);
|
||||
ConvolveHorizontal(tmp, kernel, out_pointer);
|
||||
}
|
||||
|
||||
void ImageDerivatives(const Array3Df& in,
|
||||
double sigma,
|
||||
Array3Df* gradient_x,
|
||||
Array3Df* gradient_y) {
|
||||
Vec kernel, derivative;
|
||||
ComputeGaussianKernel(sigma, &kernel, &derivative);
|
||||
Array3Df tmp;
|
||||
|
||||
// Compute first derivative in x.
|
||||
ConvolveVertical(in, kernel, &tmp);
|
||||
ConvolveHorizontal(tmp, derivative, gradient_x);
|
||||
|
||||
// Compute first derivative in y.
|
||||
ConvolveHorizontal(in, kernel, &tmp);
|
||||
ConvolveVertical(tmp, derivative, gradient_y);
|
||||
}
|
||||
|
||||
void BlurredImageAndDerivatives(const Array3Df& in,
|
||||
double sigma,
|
||||
Array3Df* blurred_image,
|
||||
Array3Df* gradient_x,
|
||||
Array3Df* gradient_y) {
|
||||
Vec kernel, derivative;
|
||||
ComputeGaussianKernel(sigma, &kernel, &derivative);
|
||||
Array3Df tmp;
|
||||
|
||||
// Compute convolved image.
|
||||
ConvolveVertical(in, kernel, &tmp);
|
||||
ConvolveHorizontal(tmp, kernel, blurred_image);
|
||||
|
||||
// Compute first derivative in x (reusing vertical convolution above).
|
||||
ConvolveHorizontal(tmp, derivative, gradient_x);
|
||||
|
||||
// Compute first derivative in y.
|
||||
ConvolveHorizontal(in, kernel, &tmp);
|
||||
ConvolveVertical(tmp, derivative, gradient_y);
|
||||
}
|
||||
|
||||
// Compute the gaussian blur of an image and the derivatives of the blurred
|
||||
// image, and store the results in three channels. Since the blurred value and
|
||||
// gradients are closer in memory, this leads to better performance if all
|
||||
// three values are needed at the same time.
|
||||
void BlurredImageAndDerivativesChannels(const Array3Df& in,
|
||||
double sigma,
|
||||
Array3Df* blurred_and_gradxy) {
|
||||
assert(in.Depth() == 1);
|
||||
|
||||
Vec kernel, derivative;
|
||||
ComputeGaussianKernel(sigma, &kernel, &derivative);
|
||||
|
||||
// Compute convolved image.
|
||||
Array3Df tmp;
|
||||
ConvolveVertical(in, kernel, &tmp);
|
||||
blurred_and_gradxy->Resize(in.Height(), in.Width(), 3);
|
||||
ConvolveHorizontal(tmp, kernel, blurred_and_gradxy, 0);
|
||||
|
||||
// Compute first derivative in x.
|
||||
ConvolveHorizontal(tmp, derivative, blurred_and_gradxy, 1);
|
||||
|
||||
// Compute first derivative in y.
|
||||
ConvolveHorizontal(in, kernel, &tmp);
|
||||
ConvolveVertical(tmp, derivative, blurred_and_gradxy, 2);
|
||||
}
|
||||
|
||||
void BoxFilterHorizontal(const Array3Df& in,
|
||||
int window_size,
|
||||
Array3Df* out_pointer) {
|
||||
Array3Df& out = *out_pointer;
|
||||
out.ResizeLike(in);
|
||||
int half_width = (window_size - 1) / 2;
|
||||
|
||||
for (int k = 0; k < in.Depth(); ++k) {
|
||||
for (int i = 0; i < in.Height(); ++i) {
|
||||
float sum = 0;
|
||||
// Init sum.
|
||||
for (int j = 0; j < half_width; ++j) {
|
||||
sum += in(i, j, k);
|
||||
}
|
||||
// Fill left border.
|
||||
for (int j = 0; j < half_width + 1; ++j) {
|
||||
sum += in(i, j + half_width, k);
|
||||
out(i, j, k) = sum;
|
||||
}
|
||||
// Fill interior.
|
||||
for (int j = half_width + 1; j < in.Width() - half_width; ++j) {
|
||||
sum -= in(i, j - half_width - 1, k);
|
||||
sum += in(i, j + half_width, k);
|
||||
out(i, j, k) = sum;
|
||||
}
|
||||
// Fill right border.
|
||||
for (int j = in.Width() - half_width; j < in.Width(); ++j) {
|
||||
sum -= in(i, j - half_width - 1, k);
|
||||
out(i, j, k) = sum;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BoxFilterVertical(const Array3Df& in,
|
||||
int window_size,
|
||||
Array3Df* out_pointer) {
|
||||
Array3Df& out = *out_pointer;
|
||||
out.ResizeLike(in);
|
||||
int half_width = (window_size - 1) / 2;
|
||||
|
||||
for (int k = 0; k < in.Depth(); ++k) {
|
||||
for (int j = 0; j < in.Width(); ++j) {
|
||||
float sum = 0;
|
||||
// Init sum.
|
||||
for (int i = 0; i < half_width; ++i) {
|
||||
sum += in(i, j, k);
|
||||
}
|
||||
// Fill left border.
|
||||
for (int i = 0; i < half_width + 1; ++i) {
|
||||
sum += in(i + half_width, j, k);
|
||||
out(i, j, k) = sum;
|
||||
}
|
||||
// Fill interior.
|
||||
for (int i = half_width + 1; i < in.Height() - half_width; ++i) {
|
||||
sum -= in(i - half_width - 1, j, k);
|
||||
sum += in(i + half_width, j, k);
|
||||
out(i, j, k) = sum;
|
||||
}
|
||||
// Fill right border.
|
||||
for (int i = in.Height() - half_width; i < in.Height(); ++i) {
|
||||
sum -= in(i - half_width - 1, j, k);
|
||||
out(i, j, k) = sum;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BoxFilter(const Array3Df& in, int box_width, Array3Df* out) {
|
||||
Array3Df tmp;
|
||||
BoxFilterHorizontal(in, box_width, &tmp);
|
||||
BoxFilterVertical(tmp, box_width, out);
|
||||
}
|
||||
|
||||
void LaplaceFilter(unsigned char* src,
|
||||
unsigned char* dst,
|
||||
int width,
|
||||
int height,
|
||||
int strength) {
|
||||
for (int y = 1; y < height - 1; y++) {
|
||||
for (int x = 1; x < width - 1; x++) {
|
||||
const unsigned char* s = &src[y * width + x];
|
||||
int l = 128 + s[-width - 1] + s[-width] + s[-width + 1] + s[1] -
|
||||
8 * s[0] + s[1] + s[width - 1] + s[width] + s[width + 1];
|
||||
int d = ((256 - strength) * s[0] + strength * l) / 256;
|
||||
if (d < 0) {
|
||||
d = 0;
|
||||
}
|
||||
if (d > 255) {
|
||||
d = 255;
|
||||
}
|
||||
dst[y * width + x] = d;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace libmv
|
||||
107
blender-5.2.0/intern/libmv/libmv/image/convolve.h
Normal file
107
blender-5.2.0/intern/libmv/libmv/image/convolve.h
Normal file
@@ -0,0 +1,107 @@
|
||||
// Copyright (c) 2007, 2008, 2011 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.
|
||||
|
||||
#ifndef LIBMV_IMAGE_CONVOLVE_H_
|
||||
#define LIBMV_IMAGE_CONVOLVE_H_
|
||||
|
||||
#include "libmv/image/image.h"
|
||||
#include "libmv/numeric/numeric.h"
|
||||
|
||||
namespace libmv {
|
||||
|
||||
// TODO(keir): Find a better place for these functions. gaussian.h in numeric?
|
||||
|
||||
// Zero mean Gaussian.
|
||||
inline double Gaussian(double x, double sigma) {
|
||||
return 1 / sqrt(2 * M_PI * sigma * sigma) * exp(-(x * x / 2 / sigma / sigma));
|
||||
}
|
||||
// 2D gaussian (zero mean)
|
||||
// (9) in http://mathworld.wolfram.com/GaussianFunction.html
|
||||
inline double Gaussian2D(double x, double y, double sigma) {
|
||||
return 1.0 / (2.0 * M_PI * sigma * sigma) *
|
||||
exp(-(x * x + y * y) / (2.0 * sigma * sigma));
|
||||
}
|
||||
inline double GaussianDerivative(double x, double sigma) {
|
||||
return -x / sigma / sigma * Gaussian(x, sigma);
|
||||
}
|
||||
// Solve the inverse of the Gaussian for positive x.
|
||||
inline double GaussianInversePositive(double y, double sigma) {
|
||||
return sqrt(-2 * sigma * sigma * log(y * sigma * sqrt(2 * M_PI)));
|
||||
}
|
||||
|
||||
void ComputeGaussianKernel(double sigma, Vec* kernel, Vec* derivative);
|
||||
void ConvolveHorizontal(const FloatImage& in,
|
||||
const Vec& kernel,
|
||||
FloatImage* out_pointer,
|
||||
int plane = -1);
|
||||
void ConvolveVertical(const FloatImage& in,
|
||||
const Vec& kernel,
|
||||
FloatImage* out_pointer,
|
||||
int plane = -1);
|
||||
void ConvolveGaussian(const FloatImage& in,
|
||||
double sigma,
|
||||
FloatImage* out_pointer);
|
||||
|
||||
void ImageDerivatives(const FloatImage& in,
|
||||
double sigma,
|
||||
FloatImage* gradient_x,
|
||||
FloatImage* gradient_y);
|
||||
|
||||
void BlurredImageAndDerivatives(const FloatImage& in,
|
||||
double sigma,
|
||||
FloatImage* blurred_image,
|
||||
FloatImage* gradient_x,
|
||||
FloatImage* gradient_y);
|
||||
|
||||
// Blur and take the gradients of an image, storing the results inside the
|
||||
// three channels of blurred_and_gradxy.
|
||||
void BlurredImageAndDerivativesChannels(const FloatImage& in,
|
||||
double sigma,
|
||||
FloatImage* blurred_and_gradxy);
|
||||
|
||||
void BoxFilterHorizontal(const FloatImage& in,
|
||||
int window_size,
|
||||
FloatImage* out_pointer);
|
||||
|
||||
void BoxFilterVertical(const FloatImage& in,
|
||||
int window_size,
|
||||
FloatImage* out_pointer);
|
||||
|
||||
void BoxFilter(const FloatImage& in, int box_width, FloatImage* out);
|
||||
|
||||
/*!
|
||||
Convolve \a src into \a dst with the discrete laplacian operator.
|
||||
|
||||
\a src and \a dst should be \a width x \a height images.
|
||||
\a strength is an interpolation coefficient (0-256) between original image
|
||||
and the laplacian.
|
||||
|
||||
\note Make sure the search region is filtered with the same strength as the
|
||||
pattern.
|
||||
*/
|
||||
void LaplaceFilter(unsigned char* src,
|
||||
unsigned char* dst,
|
||||
int width,
|
||||
int height,
|
||||
int strength);
|
||||
|
||||
} // namespace libmv
|
||||
|
||||
#endif // LIBMV_IMAGE_CONVOLVE_H_
|
||||
110
blender-5.2.0/intern/libmv/libmv/image/convolve_test.cc
Normal file
110
blender-5.2.0/intern/libmv/libmv/image/convolve_test.cc
Normal file
@@ -0,0 +1,110 @@
|
||||
// Copyright (c) 2007, 2008 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 <iostream>
|
||||
|
||||
#include "libmv/image/convolve.h"
|
||||
#include "libmv/image/image.h"
|
||||
#include "libmv/numeric/numeric.h"
|
||||
#include "testing/testing.h"
|
||||
|
||||
using namespace libmv;
|
||||
|
||||
namespace {
|
||||
|
||||
TEST(Convolve, ComputeGaussianKernel) {
|
||||
Vec kernel, derivative;
|
||||
ComputeGaussianKernel(1, &kernel, &derivative);
|
||||
EXPECT_EQ(7, kernel.size());
|
||||
// TODO(keir): Put in a more thorough test here!
|
||||
}
|
||||
|
||||
TEST(Convolve, ConvolveGaussian) {
|
||||
FloatImage im(10, 10);
|
||||
im.Fill(1);
|
||||
FloatImage blured;
|
||||
ConvolveGaussian(im, 3, &blured);
|
||||
EXPECT_NEAR(im(5, 5), 1, 1e-7);
|
||||
}
|
||||
|
||||
TEST(Convolve, BoxFilterHorizontal) {
|
||||
FloatImage im(10, 10), convolved, filtered;
|
||||
im.Fill(1);
|
||||
BoxFilterHorizontal(im, 3, &filtered);
|
||||
Vec kernel(3);
|
||||
kernel.setConstant(1.);
|
||||
ConvolveHorizontal(im, kernel, &convolved);
|
||||
EXPECT_EQ(filtered(5, 5), 3);
|
||||
EXPECT_TRUE(filtered == convolved);
|
||||
}
|
||||
|
||||
TEST(Convolve, BoxFilter) {
|
||||
FloatImage image(5, 5), filtered;
|
||||
// A single 1.0 inside a 5x5 image should expand to a 3x3 square.
|
||||
image.Fill(0);
|
||||
image(2, 2) = 1.0;
|
||||
BoxFilter(image, 3, &filtered);
|
||||
for (int j = 0; j < 5; j++) {
|
||||
for (int i = 0; i < 5; i++) {
|
||||
if (i == 0 || i == 4 || j == 0 || j == 4) {
|
||||
EXPECT_EQ(0.0, filtered(j, i));
|
||||
} else {
|
||||
EXPECT_EQ(1.0, filtered(j, i));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Convolve, BlurredImageAndDerivativesChannelsFlat) {
|
||||
FloatImage im(10, 10), blurred_and_derivatives;
|
||||
im.Fill(1);
|
||||
BlurredImageAndDerivativesChannels(im, 1.0, &blurred_and_derivatives);
|
||||
EXPECT_NEAR(blurred_and_derivatives(5, 5, 0), 1.0, 1e-7);
|
||||
EXPECT_NEAR(blurred_and_derivatives(5, 5, 1), 0.0, 1e-7);
|
||||
EXPECT_NEAR(blurred_and_derivatives(5, 5, 2), 0.0, 1e-7);
|
||||
}
|
||||
|
||||
TEST(Convolve, BlurredImageAndDerivativesChannelsHorizontalSlope) {
|
||||
FloatImage image(10, 10), blurred_and_derivatives;
|
||||
for (int j = 0; j < 10; ++j) {
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
image(j, i) = 2 * i;
|
||||
}
|
||||
}
|
||||
BlurredImageAndDerivativesChannels(image, 0.9, &blurred_and_derivatives);
|
||||
EXPECT_NEAR(blurred_and_derivatives(5, 5, 0), 10.0, 1e-7);
|
||||
EXPECT_NEAR(blurred_and_derivatives(5, 5, 1), 2.0, 1e-7);
|
||||
EXPECT_NEAR(blurred_and_derivatives(5, 5, 2), 0.0, 1e-7);
|
||||
}
|
||||
|
||||
TEST(Convolve, BlurredImageAndDerivativesChannelsVerticalSlope) {
|
||||
FloatImage image(10, 10), blurred_and_derivatives;
|
||||
for (int j = 0; j < 10; ++j) {
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
image(j, i) = 2 * j;
|
||||
}
|
||||
}
|
||||
BlurredImageAndDerivativesChannels(image, 0.9, &blurred_and_derivatives);
|
||||
EXPECT_NEAR(blurred_and_derivatives(5, 5, 0), 10.0, 1e-7);
|
||||
EXPECT_NEAR(blurred_and_derivatives(5, 5, 1), 0.0, 1e-7);
|
||||
EXPECT_NEAR(blurred_and_derivatives(5, 5, 2), 2.0, 1e-7);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
73
blender-5.2.0/intern/libmv/libmv/image/correlation.h
Normal file
73
blender-5.2.0/intern/libmv/libmv/image/correlation.h
Normal file
@@ -0,0 +1,73 @@
|
||||
// Copyright (c) 2012 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.
|
||||
|
||||
#ifndef LIBMV_IMAGE_CORRELATION_H
|
||||
#define LIBMV_IMAGE_CORRELATION_H
|
||||
|
||||
#include "libmv/image/image.h"
|
||||
#include "libmv/logging/logging.h"
|
||||
|
||||
namespace libmv {
|
||||
|
||||
inline double PearsonProductMomentCorrelation(
|
||||
const FloatImage& image_and_gradient1_sampled,
|
||||
const FloatImage& image_and_gradient2_sampled) {
|
||||
assert(image_and_gradient1_sampled.Width() ==
|
||||
image_and_gradient2_sampled.Width());
|
||||
assert(image_and_gradient1_sampled.Height() ==
|
||||
image_and_gradient2_sampled.Height());
|
||||
|
||||
const int width = image_and_gradient1_sampled.Width(),
|
||||
height = image_and_gradient1_sampled.Height();
|
||||
double sX = 0, sY = 0, sXX = 0, sYY = 0, sXY = 0;
|
||||
|
||||
for (int r = 0; r < height; ++r) {
|
||||
for (int c = 0; c < width; ++c) {
|
||||
double x = image_and_gradient1_sampled(r, c, 0);
|
||||
double y = image_and_gradient2_sampled(r, c, 0);
|
||||
sX += x;
|
||||
sY += y;
|
||||
sXX += x * x;
|
||||
sYY += y * y;
|
||||
sXY += x * y;
|
||||
}
|
||||
}
|
||||
|
||||
// Normalize.
|
||||
double N = width * height;
|
||||
sX /= N;
|
||||
sY /= N;
|
||||
sXX /= N;
|
||||
sYY /= N;
|
||||
sXY /= N;
|
||||
|
||||
double var_x = sXX - sX * sX;
|
||||
double var_y = sYY - sY * sY;
|
||||
double covariance_xy = sXY - sX * sY;
|
||||
|
||||
double correlation = covariance_xy / sqrt(var_x * var_y);
|
||||
LG << "Covariance xy: " << covariance_xy << ", var 1: " << var_x
|
||||
<< ", var 2: " << var_y << ", correlation: " << correlation;
|
||||
return correlation;
|
||||
}
|
||||
|
||||
} // namespace libmv
|
||||
|
||||
#endif // LIBMV_IMAGE_IMAGE_CORRELATION_H
|
||||
136
blender-5.2.0/intern/libmv/libmv/image/image.h
Normal file
136
blender-5.2.0/intern/libmv/libmv/image/image.h
Normal file
@@ -0,0 +1,136 @@
|
||||
// Copyright (c) 2007, 2008 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.
|
||||
|
||||
#ifndef LIBMV_IMAGE_IMAGE_H
|
||||
#define LIBMV_IMAGE_IMAGE_H
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include "libmv/image/array_nd.h"
|
||||
|
||||
namespace libmv {
|
||||
|
||||
typedef Array3Du ByteImage; // For backwards compatibility.
|
||||
typedef Array3Df FloatImage;
|
||||
|
||||
// Type added only to manage special 2D array for feature detection
|
||||
typedef Array3Di IntImage;
|
||||
typedef Array3Ds ShortImage;
|
||||
|
||||
// An image class that is a thin wrapper around Array3D's of various types.
|
||||
// TODO(keir): Decide if we should add reference counting semantics... Maybe it
|
||||
// is the best solution after all.
|
||||
class Image {
|
||||
public:
|
||||
// Create an image from an array. The image takes ownership of the array.
|
||||
Image(Array3Du* array) : array_type_(BYTE), array_(array) {}
|
||||
Image(Array3Df* array) : array_type_(FLOAT), array_(array) {}
|
||||
|
||||
Image(const Image& img) : array_type_(NONE), array_(NULL) { *this = img; }
|
||||
|
||||
// Underlying data type.
|
||||
enum DataType {
|
||||
NONE,
|
||||
BYTE,
|
||||
FLOAT,
|
||||
INT,
|
||||
SHORT,
|
||||
};
|
||||
|
||||
// Size in bytes that the image takes in memory.
|
||||
int MemorySizeInBytes() {
|
||||
int size;
|
||||
switch (array_type_) {
|
||||
case BYTE:
|
||||
size = reinterpret_cast<Array3Du*>(array_)->MemorySizeInBytes();
|
||||
break;
|
||||
case FLOAT:
|
||||
size = reinterpret_cast<Array3Df*>(array_)->MemorySizeInBytes();
|
||||
break;
|
||||
case INT:
|
||||
size = reinterpret_cast<Array3Di*>(array_)->MemorySizeInBytes();
|
||||
break;
|
||||
case SHORT:
|
||||
size = reinterpret_cast<Array3Ds*>(array_)->MemorySizeInBytes();
|
||||
break;
|
||||
default: size = 0; assert(0);
|
||||
}
|
||||
size += sizeof(*this);
|
||||
return size;
|
||||
}
|
||||
|
||||
~Image() {
|
||||
switch (array_type_) {
|
||||
case BYTE: delete reinterpret_cast<Array3Du*>(array_); break;
|
||||
case FLOAT: delete reinterpret_cast<Array3Df*>(array_); break;
|
||||
case INT: delete reinterpret_cast<Array3Di*>(array_); break;
|
||||
case SHORT: delete reinterpret_cast<Array3Ds*>(array_); break;
|
||||
default: assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
Image& operator=(const Image& f) {
|
||||
if (this != &f) {
|
||||
array_type_ = f.array_type_;
|
||||
switch (array_type_) {
|
||||
case BYTE:
|
||||
delete reinterpret_cast<Array3Du*>(array_);
|
||||
array_ = new Array3Du(*(Array3Du*)f.array_);
|
||||
break;
|
||||
case FLOAT:
|
||||
delete reinterpret_cast<Array3Df*>(array_);
|
||||
array_ = new Array3Df(*(Array3Df*)f.array_);
|
||||
break;
|
||||
case INT:
|
||||
delete reinterpret_cast<Array3Di*>(array_);
|
||||
array_ = new Array3Di(*(Array3Di*)f.array_);
|
||||
break;
|
||||
case SHORT:
|
||||
delete reinterpret_cast<Array3Ds*>(array_);
|
||||
array_ = new Array3Ds(*(Array3Ds*)f.array_);
|
||||
break;
|
||||
default: assert(0);
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
Array3Du* AsArray3Du() const {
|
||||
if (array_type_ == BYTE) {
|
||||
return reinterpret_cast<Array3Du*>(array_);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Array3Df* AsArray3Df() const {
|
||||
if (array_type_ == FLOAT) {
|
||||
return reinterpret_cast<Array3Df*>(array_);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
private:
|
||||
DataType array_type_;
|
||||
BaseArray* array_;
|
||||
};
|
||||
|
||||
} // namespace libmv
|
||||
|
||||
#endif // LIBMV_IMAGE_IMAGE_IMAGE_H
|
||||
82
blender-5.2.0/intern/libmv/libmv/image/image_converter.h
Normal file
82
blender-5.2.0/intern/libmv/libmv/image/image_converter.h
Normal file
@@ -0,0 +1,82 @@
|
||||
// Copyright (c) 2009 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.
|
||||
|
||||
#ifndef LIBMV_IMAGE_IMAGE_CONVERTER_H
|
||||
#define LIBMV_IMAGE_IMAGE_CONVERTER_H
|
||||
|
||||
#include "libmv/image/array_nd.h"
|
||||
|
||||
namespace libmv {
|
||||
|
||||
// The factor comes from http://www.easyrgb.com/
|
||||
// RGB to XYZ : Y is the luminance channel
|
||||
// var_R * 0.2126 + var_G * 0.7152 + var_B * 0.0722
|
||||
template <typename T>
|
||||
inline T RGB2GRAY(const T r, const T g, const T b) {
|
||||
return static_cast<T>(r * 0.2126 + g * 0.7152 + b * 0.0722);
|
||||
}
|
||||
|
||||
/*
|
||||
// Specialization for the uchar type. (that do not want to work...)
|
||||
template<>
|
||||
inline unsigned char RGB2GRAY<unsigned char>(const unsigned char r,
|
||||
const unsigned char g,
|
||||
const unsigned char b) {
|
||||
return (unsigned char)(r * 0.2126 + g * 0.7152 + b * 0.0722 +0.5);
|
||||
}*/
|
||||
|
||||
template <class ImageIn, class ImageOut>
|
||||
void Rgb2Gray(const ImageIn& imaIn, ImageOut* imaOut) {
|
||||
// It is all fine to cnvert RGBA image here as well,
|
||||
// all the additional channels will be nicely ignored.
|
||||
assert(imaIn.Depth() >= 3);
|
||||
|
||||
imaOut->Resize(imaIn.Height(), imaIn.Width(), 1);
|
||||
// Convert each RGB pixel into Gray value (luminance)
|
||||
|
||||
for (int j = 0; j < imaIn.Height(); ++j) {
|
||||
for (int i = 0; i < imaIn.Width(); ++i) {
|
||||
(*imaOut)(j, i) =
|
||||
RGB2GRAY(imaIn(j, i, 0), imaIn(j, i, 1), imaIn(j, i, 2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Convert given float image to an unsigned char array of pixels.
|
||||
template <class Image>
|
||||
unsigned char* FloatImageToUCharArray(const Image& image) {
|
||||
unsigned char* buffer =
|
||||
new unsigned char[image.Width() * image.Height() * image.Depth()];
|
||||
|
||||
for (int y = 0; y < image.Height(); y++) {
|
||||
for (int x = 0; x < image.Width(); x++) {
|
||||
for (int d = 0; d < image.Depth(); d++) {
|
||||
int index = (y * image.Width() + x) * image.Depth() + d;
|
||||
buffer[index] = 255.0 * image(y, x, d);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
} // namespace libmv
|
||||
|
||||
#endif // LIBMV_IMAGE_IMAGE_CONVERTER_H
|
||||
296
blender-5.2.0/intern/libmv/libmv/image/image_drawing.h
Normal file
296
blender-5.2.0/intern/libmv/libmv/image/image_drawing.h
Normal file
@@ -0,0 +1,296 @@
|
||||
// Copyright (c) 2009 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.
|
||||
|
||||
// Generic Image Processing Algorithm (GIPA)
|
||||
// Use an ImageModel class that must implement the following :
|
||||
//
|
||||
// ::Contains(int y, int x) <= Tell if a point is inside or not the image
|
||||
// ::operator(int y,int x) <= Modification accessor over the pixel (y,x)
|
||||
// ::Width()
|
||||
// ::Height()
|
||||
|
||||
#ifndef LIBMV_IMAGE_IMAGE_DRAWING_H
|
||||
#define LIBMV_IMAGE_IMAGE_DRAWING_H
|
||||
|
||||
namespace libmv {
|
||||
|
||||
/// Put the pixel in the image to the given color only if the point (xc,yc)
|
||||
/// is inside the image.
|
||||
template <class Image, class Color>
|
||||
inline void safePutPixel(int yc, int xc, const Color& col, Image* pim) {
|
||||
if (!pim) {
|
||||
return;
|
||||
}
|
||||
if (pim->Contains(yc, xc)) {
|
||||
(*pim)(yc, xc) = col;
|
||||
}
|
||||
}
|
||||
/// Put the pixel in the image to the given color only if the point (xc,yc)
|
||||
/// is inside the image. This function support multi-channel color
|
||||
/// \note The color pointer col must have size as the image depth
|
||||
template <class Image, class Color>
|
||||
inline void safePutPixel(int yc, int xc, const Color* col, Image* pim) {
|
||||
if (!pim) {
|
||||
return;
|
||||
}
|
||||
if (pim->Contains(yc, xc)) {
|
||||
for (int i = 0; i < pim->Depth(); ++i) {
|
||||
(*pim)(yc, xc, i) = *(col + i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Bresenham approach to draw ellipse.
|
||||
// http://raphaello.univ-fcomte.fr/ig/algorithme/ExemplesGLUt/BresenhamEllipse.htm
|
||||
// Add the rotation of the ellipse.
|
||||
// As the algo. use symmetry we must use 4 rotations.
|
||||
template <class Image, class Color>
|
||||
void DrawEllipse(int xc,
|
||||
int yc,
|
||||
int radiusA,
|
||||
int radiusB,
|
||||
const Color& col,
|
||||
Image* pim,
|
||||
double angle = 0.0) {
|
||||
int a = radiusA;
|
||||
int b = radiusB;
|
||||
|
||||
// Counter Clockwise rotation matrix.
|
||||
double matXY[4] = {cos(angle), sin(angle), -sin(angle), cos(angle)};
|
||||
int x, y;
|
||||
double d1, d2;
|
||||
x = 0;
|
||||
y = b;
|
||||
d1 = b * b - a * a * b + a * a / 4;
|
||||
|
||||
float rotX = (matXY[0] * x + matXY[1] * y);
|
||||
float rotY = (matXY[2] * x + matXY[3] * y);
|
||||
safePutPixel(yc + rotY, xc + rotX, col, pim);
|
||||
rotX = (matXY[0] * x - matXY[1] * y);
|
||||
rotY = (matXY[2] * x - matXY[3] * y);
|
||||
safePutPixel(yc + rotY, xc + rotX, col, pim);
|
||||
rotX = (-matXY[0] * x - matXY[1] * y);
|
||||
rotY = (-matXY[2] * x - matXY[3] * y);
|
||||
safePutPixel(yc + rotY, xc + rotX, col, pim);
|
||||
rotX = (-matXY[0] * x + matXY[1] * y);
|
||||
rotY = (-matXY[2] * x + matXY[3] * y);
|
||||
safePutPixel(yc + rotY, xc + rotX, col, pim);
|
||||
|
||||
while (a * a * (y - .5) > b * b * (x + 1)) {
|
||||
if (d1 < 0) {
|
||||
d1 += b * b * (2 * x + 3);
|
||||
++x;
|
||||
} else {
|
||||
d1 += b * b * (2 * x + 3) + a * a * (-2 * y + 2);
|
||||
++x;
|
||||
--y;
|
||||
}
|
||||
rotX = (matXY[0] * x + matXY[1] * y);
|
||||
rotY = (matXY[2] * x + matXY[3] * y);
|
||||
safePutPixel(yc + rotY, xc + rotX, col, pim);
|
||||
rotX = (matXY[0] * x - matXY[1] * y);
|
||||
rotY = (matXY[2] * x - matXY[3] * y);
|
||||
safePutPixel(yc + rotY, xc + rotX, col, pim);
|
||||
rotX = (-matXY[0] * x - matXY[1] * y);
|
||||
rotY = (-matXY[2] * x - matXY[3] * y);
|
||||
safePutPixel(yc + rotY, xc + rotX, col, pim);
|
||||
rotX = (-matXY[0] * x + matXY[1] * y);
|
||||
rotY = (-matXY[2] * x + matXY[3] * y);
|
||||
safePutPixel(yc + rotY, xc + rotX, col, pim);
|
||||
}
|
||||
d2 = b * b * (x + .5) * (x + .5) + a * a * (y - 1) * (y - 1) - a * a * b * b;
|
||||
while (y > 0) {
|
||||
if (d2 < 0) {
|
||||
d2 += b * b * (2 * x + 2) + a * a * (-2 * y + 3);
|
||||
--y;
|
||||
++x;
|
||||
} else {
|
||||
d2 += a * a * (-2 * y + 3);
|
||||
--y;
|
||||
}
|
||||
rotX = (matXY[0] * x + matXY[1] * y);
|
||||
rotY = (matXY[2] * x + matXY[3] * y);
|
||||
safePutPixel(yc + rotY, xc + rotX, col, pim);
|
||||
rotX = (matXY[0] * x - matXY[1] * y);
|
||||
rotY = (matXY[2] * x - matXY[3] * y);
|
||||
safePutPixel(yc + rotY, xc + rotX, col, pim);
|
||||
rotX = (-matXY[0] * x - matXY[1] * y);
|
||||
rotY = (-matXY[2] * x - matXY[3] * y);
|
||||
safePutPixel(yc + rotY, xc + rotX, col, pim);
|
||||
rotX = (-matXY[0] * x + matXY[1] * y);
|
||||
rotY = (-matXY[2] * x + matXY[3] * y);
|
||||
safePutPixel(yc + rotY, xc + rotX, col, pim);
|
||||
}
|
||||
}
|
||||
|
||||
// Bresenham approach do not allow to draw concentric circle without holes.
|
||||
// So it's better the use the Andres method.
|
||||
// http://fr.wikipedia.org/wiki/Algorithme_de_tracé_de_cercle_d'Andres.
|
||||
template <class Image, class Color>
|
||||
void DrawCircle(int x, int y, int radius, const Color& col, Image* pim) {
|
||||
Image& im = *pim;
|
||||
if (im.Contains(y + radius, x + radius) ||
|
||||
im.Contains(y + radius, x - radius) ||
|
||||
im.Contains(y - radius, x + radius) ||
|
||||
im.Contains(y - radius, x - radius)) {
|
||||
int x1 = 0;
|
||||
int y1 = radius;
|
||||
int d = radius - 1;
|
||||
while (y1 >= x1) {
|
||||
// Draw the point for each octant.
|
||||
safePutPixel(y1 + y, x1 + x, col, pim);
|
||||
safePutPixel(x1 + y, y1 + x, col, pim);
|
||||
safePutPixel(y1 + y, -x1 + x, col, pim);
|
||||
safePutPixel(x1 + y, -y1 + x, col, pim);
|
||||
safePutPixel(-y1 + y, x1 + x, col, pim);
|
||||
safePutPixel(-x1 + y, y1 + x, col, pim);
|
||||
safePutPixel(-y1 + y, -x1 + x, col, pim);
|
||||
safePutPixel(-x1 + y, -y1 + x, col, pim);
|
||||
if (d >= 2 * x1) {
|
||||
d = d - 2 * x1 - 1;
|
||||
x1 += 1;
|
||||
} else {
|
||||
if (d <= 2 * (radius - y1)) {
|
||||
d = d + 2 * y1 - 1;
|
||||
y1 -= 1;
|
||||
} else {
|
||||
d = d + 2 * (y1 - x1 - 1);
|
||||
y1 -= 1;
|
||||
x1 += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Bresenham algorithm
|
||||
template <class Image, class Color>
|
||||
void DrawLine(int xa, int ya, int xb, int yb, const Color& col, Image* pim) {
|
||||
Image& im = *pim;
|
||||
|
||||
// If one point is outside the image
|
||||
// Replace the outside point by the intersection of the line and
|
||||
// the limit (either x=width or y=height).
|
||||
if (!im.Contains(ya, xa) || !im.Contains(yb, xb)) {
|
||||
int width = pim->Width();
|
||||
int height = pim->Height();
|
||||
const bool xdir = xa < xb, ydir = ya < yb;
|
||||
float nx0 = xa, nx1 = xb, ny0 = ya, ny1 = yb, &xleft = xdir ? nx0 : nx1,
|
||||
&yleft = xdir ? ny0 : ny1, &xright = xdir ? nx1 : nx0,
|
||||
&yright = xdir ? ny1 : ny0, &xup = ydir ? nx0 : nx1,
|
||||
&yup = ydir ? ny0 : ny1, &xdown = ydir ? nx1 : nx0,
|
||||
&ydown = ydir ? ny1 : ny0;
|
||||
|
||||
if (xright < 0 || xleft >= width) {
|
||||
return;
|
||||
}
|
||||
if (xleft < 0) {
|
||||
yleft -= xleft * (yright - yleft) / (xright - xleft);
|
||||
xleft = 0;
|
||||
}
|
||||
if (xright >= width) {
|
||||
yright -= (xright - width) * (yright - yleft) / (xright - xleft);
|
||||
xright = width - 1;
|
||||
}
|
||||
if (ydown < 0 || yup >= height) {
|
||||
return;
|
||||
}
|
||||
if (yup < 0) {
|
||||
xup -= yup * (xdown - xup) / (ydown - yup);
|
||||
yup = 0;
|
||||
}
|
||||
if (ydown >= height) {
|
||||
xdown -= (ydown - height) * (xdown - xup) / (ydown - yup);
|
||||
ydown = height - 1;
|
||||
}
|
||||
|
||||
xa = (int)xleft;
|
||||
xb = (int)xright;
|
||||
ya = (int)yleft;
|
||||
yb = (int)yright;
|
||||
}
|
||||
|
||||
int xbas, xhaut, ybas, yhaut;
|
||||
// Check the condition ybas < yhaut.
|
||||
if (ya <= yb) {
|
||||
xbas = xa;
|
||||
ybas = ya;
|
||||
xhaut = xb;
|
||||
yhaut = yb;
|
||||
} else {
|
||||
xbas = xb;
|
||||
ybas = yb;
|
||||
xhaut = xa;
|
||||
yhaut = ya;
|
||||
}
|
||||
// Initialize slope.
|
||||
int x, y, dx, dy, incrmX, incrmY, dp, N, S;
|
||||
dx = xhaut - xbas;
|
||||
dy = yhaut - ybas;
|
||||
if (dx > 0) { // If xhaut > xbas we will increment X.
|
||||
incrmX = 1;
|
||||
} else {
|
||||
incrmX = -1; // else we will decrement X.
|
||||
dx *= -1;
|
||||
}
|
||||
if (dy > 0) { // Positive slope will increment X.
|
||||
incrmY = 1;
|
||||
} else { // Negative slope.
|
||||
incrmY = -1;
|
||||
}
|
||||
if (dx >= dy) {
|
||||
dp = 2 * dy - dx;
|
||||
S = 2 * dy;
|
||||
N = 2 * (dy - dx);
|
||||
y = ybas;
|
||||
x = xbas;
|
||||
while (x != xhaut) {
|
||||
safePutPixel(y, x, col, pim);
|
||||
x += incrmX;
|
||||
if (dp <= 0) { // Go in direction of the South Pixel.
|
||||
dp += S;
|
||||
} else { // Go to the North.
|
||||
dp += N;
|
||||
y += incrmY;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
dp = 2 * dx - dy;
|
||||
S = 2 * dx;
|
||||
N = 2 * (dx - dy);
|
||||
x = xbas;
|
||||
y = ybas;
|
||||
while (y < yhaut) {
|
||||
safePutPixel(y, x, col, pim);
|
||||
y += incrmY;
|
||||
if (dp <= 0) { // Go in direction of the South Pixel.
|
||||
dp += S;
|
||||
} else { // Go to the North.
|
||||
dp += N;
|
||||
x += incrmX;
|
||||
}
|
||||
}
|
||||
}
|
||||
safePutPixel(y, x, col, pim);
|
||||
}
|
||||
|
||||
} // namespace libmv
|
||||
|
||||
#endif // LIBMV_IMAGE_IMAGE_DRAWING_H
|
||||
45
blender-5.2.0/intern/libmv/libmv/image/image_test.cc
Normal file
45
blender-5.2.0/intern/libmv/libmv/image/image_test.cc
Normal file
@@ -0,0 +1,45 @@
|
||||
// Copyright (c) 2007, 2008 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 <iostream>
|
||||
|
||||
#include "libmv/image/image.h"
|
||||
#include "testing/testing.h"
|
||||
|
||||
using libmv::Array3Df;
|
||||
using libmv::Image;
|
||||
|
||||
namespace {
|
||||
|
||||
TEST(Image, SimpleImageAccessors) {
|
||||
Array3Df* array = new Array3Df(2, 3);
|
||||
Image image(array);
|
||||
EXPECT_EQ(array, image.AsArray3Df());
|
||||
EXPECT_TRUE(NULL == image.AsArray3Du());
|
||||
}
|
||||
|
||||
TEST(Image, MemorySizeInBytes) {
|
||||
Array3Df* array = new Array3Df(2, 3);
|
||||
Image image(array);
|
||||
int size = sizeof(image) + array->MemorySizeInBytes();
|
||||
EXPECT_EQ(size, image.MemorySizeInBytes());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
138
blender-5.2.0/intern/libmv/libmv/image/sample.h
Normal file
138
blender-5.2.0/intern/libmv/libmv/image/sample.h
Normal file
@@ -0,0 +1,138 @@
|
||||
// Copyright (c) 2007, 2008 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.
|
||||
|
||||
#ifndef LIBMV_IMAGE_SAMPLE_H_
|
||||
#define LIBMV_IMAGE_SAMPLE_H_
|
||||
|
||||
#include "libmv/image/image.h"
|
||||
|
||||
namespace libmv {
|
||||
|
||||
/// Nearest neighbor interpolation.
|
||||
template <typename T>
|
||||
inline T SampleNearest(const Array3D<T>& image, float y, float x, int v = 0) {
|
||||
const int i = int(round(y));
|
||||
const int j = int(round(x));
|
||||
return image(i, j, v);
|
||||
}
|
||||
|
||||
inline void LinearInitAxis(float x, int size, int* x1, int* x2, float* dx) {
|
||||
const int ix = static_cast<int>(x);
|
||||
if (ix < 0) {
|
||||
*x1 = 0;
|
||||
*x2 = 0;
|
||||
*dx = 1.0;
|
||||
} else if (ix > size - 2) {
|
||||
*x1 = size - 1;
|
||||
*x2 = size - 1;
|
||||
*dx = 1.0;
|
||||
} else {
|
||||
*x1 = ix;
|
||||
*x2 = ix + 1;
|
||||
*dx = *x2 - x;
|
||||
}
|
||||
}
|
||||
|
||||
/// Linear interpolation.
|
||||
template <typename T>
|
||||
inline T SampleLinear(const Array3D<T>& image, float y, float x, int v = 0) {
|
||||
int x1, y1, x2, y2;
|
||||
float dx, dy;
|
||||
|
||||
LinearInitAxis(y, image.Height(), &y1, &y2, &dy);
|
||||
LinearInitAxis(x, image.Width(), &x1, &x2, &dx);
|
||||
|
||||
const T im11 = image(y1, x1, v);
|
||||
const T im12 = image(y1, x2, v);
|
||||
const T im21 = image(y2, x1, v);
|
||||
const T im22 = image(y2, x2, v);
|
||||
|
||||
return T(dy * (dx * im11 + (1.0 - dx) * im12) +
|
||||
(1 - dy) * (dx * im21 + (1.0 - dx) * im22));
|
||||
}
|
||||
|
||||
/// Linear interpolation, of all channels. The sample is assumed to have the
|
||||
/// same size as the number of channels in image.
|
||||
template <typename T>
|
||||
inline void SampleLinear(const Array3D<T>& image, float y, float x, T* sample) {
|
||||
int x1, y1, x2, y2;
|
||||
float dx, dy;
|
||||
|
||||
LinearInitAxis(y, image.Height(), &y1, &y2, &dy);
|
||||
LinearInitAxis(x, image.Width(), &x1, &x2, &dx);
|
||||
|
||||
for (int i = 0; i < image.Depth(); ++i) {
|
||||
const T im11 = image(y1, x1, i);
|
||||
const T im12 = image(y1, x2, i);
|
||||
const T im21 = image(y2, x1, i);
|
||||
const T im22 = image(y2, x2, i);
|
||||
|
||||
sample[i] = T(dy * (dx * im11 + (1.0 - dx) * im12) +
|
||||
(1 - dy) * (dx * im21 + (1.0 - dx) * im22));
|
||||
}
|
||||
}
|
||||
|
||||
// Downsample all channels by 2. If the image has odd width or height, the last
|
||||
// row or column is ignored.
|
||||
// FIXME(MatthiasF): this implementation shouldn't be in an interface file
|
||||
inline void DownsampleChannelsBy2(const Array3Df& in, Array3Df* out) {
|
||||
int height = in.Height() / 2;
|
||||
int width = in.Width() / 2;
|
||||
int depth = in.Depth();
|
||||
|
||||
out->Resize(height, width, depth);
|
||||
|
||||
// 2x2 box filter downsampling.
|
||||
for (int r = 0; r < height; ++r) {
|
||||
for (int c = 0; c < width; ++c) {
|
||||
for (int k = 0; k < depth; ++k) {
|
||||
// clang-format off
|
||||
(*out)(r, c, k) = (in(2 * r, 2 * c, k) +
|
||||
in(2 * r + 1, 2 * c, k) +
|
||||
in(2 * r, 2 * c + 1, k) +
|
||||
in(2 * r + 1, 2 * c + 1, k)) / 4.0f;
|
||||
// clang-format on
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sample a region centered at x,y in image with size extending by half_width
|
||||
// from x,y. Channels specifies the number of channels to sample from.
|
||||
inline void SamplePattern(const FloatImage& image,
|
||||
double x,
|
||||
double y,
|
||||
int half_width,
|
||||
int channels,
|
||||
FloatImage* sampled) {
|
||||
sampled->Resize(2 * half_width + 1, 2 * half_width + 1, channels);
|
||||
for (int r = -half_width; r <= half_width; ++r) {
|
||||
for (int c = -half_width; c <= half_width; ++c) {
|
||||
for (int i = 0; i < channels; ++i) {
|
||||
(*sampled)(r + half_width, c + half_width, i) =
|
||||
SampleLinear(image, y + r, x + c, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace libmv
|
||||
|
||||
#endif // LIBMV_IMAGE_SAMPLE_H_
|
||||
89
blender-5.2.0/intern/libmv/libmv/image/sample_test.cc
Normal file
89
blender-5.2.0/intern/libmv/libmv/image/sample_test.cc
Normal file
@@ -0,0 +1,89 @@
|
||||
// Copyright (c) 2007, 2008 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/image/sample.h"
|
||||
#include "testing/testing.h"
|
||||
|
||||
using namespace libmv;
|
||||
|
||||
namespace {
|
||||
|
||||
TEST(Image, Nearest) {
|
||||
Array3Du image(2, 2);
|
||||
image(0, 0) = 0;
|
||||
image(0, 1) = 1;
|
||||
image(1, 0) = 2;
|
||||
image(1, 1) = 3;
|
||||
EXPECT_EQ(0, SampleNearest(image, -0.4f, -0.4f));
|
||||
EXPECT_EQ(0, SampleNearest(image, 0.4f, 0.4f));
|
||||
EXPECT_EQ(3, SampleNearest(image, 0.6f, 0.6f));
|
||||
EXPECT_EQ(3, SampleNearest(image, 1.4f, 1.4f));
|
||||
}
|
||||
|
||||
TEST(Image, Linear) {
|
||||
Array3Df image(2, 2);
|
||||
image(0, 0) = 0;
|
||||
image(0, 1) = 1;
|
||||
image(1, 0) = 2;
|
||||
image(1, 1) = 3;
|
||||
EXPECT_EQ(1.5, SampleLinear(image, 0.5, 0.5));
|
||||
}
|
||||
|
||||
TEST(Image, DownsampleBy2) {
|
||||
Array3Df image(2, 2);
|
||||
image(0, 0) = 0;
|
||||
image(0, 1) = 1;
|
||||
image(1, 0) = 2;
|
||||
image(1, 1) = 3;
|
||||
Array3Df resampled_image;
|
||||
DownsampleChannelsBy2(image, &resampled_image);
|
||||
ASSERT_EQ(1, resampled_image.Height());
|
||||
ASSERT_EQ(1, resampled_image.Width());
|
||||
ASSERT_EQ(1, resampled_image.Depth());
|
||||
EXPECT_FLOAT_EQ(6. / 4., resampled_image(0, 0));
|
||||
}
|
||||
|
||||
TEST(Image, DownsampleBy2MultiChannel) {
|
||||
Array3Df image(2, 2, 3);
|
||||
image(0, 0, 0) = 0;
|
||||
image(0, 1, 0) = 1;
|
||||
image(1, 0, 0) = 2;
|
||||
image(1, 1, 0) = 3;
|
||||
|
||||
image(0, 0, 1) = 5;
|
||||
image(0, 1, 1) = 6;
|
||||
image(1, 0, 1) = 7;
|
||||
image(1, 1, 1) = 8;
|
||||
|
||||
image(0, 0, 2) = 9;
|
||||
image(0, 1, 2) = 10;
|
||||
image(1, 0, 2) = 11;
|
||||
image(1, 1, 2) = 12;
|
||||
|
||||
Array3Df resampled_image;
|
||||
DownsampleChannelsBy2(image, &resampled_image);
|
||||
ASSERT_EQ(1, resampled_image.Height());
|
||||
ASSERT_EQ(1, resampled_image.Width());
|
||||
ASSERT_EQ(3, resampled_image.Depth());
|
||||
EXPECT_FLOAT_EQ((0 + 1 + 2 + 3) / 4., resampled_image(0, 0, 0));
|
||||
EXPECT_FLOAT_EQ((5 + 6 + 7 + 8) / 4., resampled_image(0, 0, 1));
|
||||
EXPECT_FLOAT_EQ((9 + 10 + 11 + 12) / 4., resampled_image(0, 0, 2));
|
||||
}
|
||||
} // namespace
|
||||
94
blender-5.2.0/intern/libmv/libmv/image/tuple.h
Normal file
94
blender-5.2.0/intern/libmv/libmv/image/tuple.h
Normal file
@@ -0,0 +1,94 @@
|
||||
// Copyright (c) 2007, 2008 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.
|
||||
|
||||
#ifndef LIBMV_IMAGE_TUPLE_H
|
||||
#define LIBMV_IMAGE_TUPLE_H
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace libmv {
|
||||
|
||||
// A vector of elements with fixed length and deep copy semantics.
|
||||
template <typename T, int N>
|
||||
class Tuple {
|
||||
public:
|
||||
enum { SIZE = N };
|
||||
Tuple() {}
|
||||
Tuple(T initial_value) { Reset(initial_value); }
|
||||
|
||||
template <typename D>
|
||||
Tuple(D* values) {
|
||||
Reset(values);
|
||||
}
|
||||
|
||||
template <typename D>
|
||||
Tuple(const Tuple<D, N>& b) {
|
||||
Reset(b);
|
||||
}
|
||||
|
||||
template <typename D>
|
||||
Tuple& operator=(const Tuple<D, N>& b) {
|
||||
Reset(b);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename D>
|
||||
void Reset(const Tuple<D, N>& b) {
|
||||
Reset(b.Data());
|
||||
}
|
||||
|
||||
template <typename D>
|
||||
void Reset(D* values) {
|
||||
for (int i = 0; i < N; i++) {
|
||||
data_[i] = T(values[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// Set all tuple values to the same thing.
|
||||
void Reset(T value) {
|
||||
for (int i = 0; i < N; i++) {
|
||||
data_[i] = value;
|
||||
}
|
||||
}
|
||||
|
||||
// Pointer to the first element.
|
||||
T* Data() { return &data_[0]; }
|
||||
const T* Data() const { return &data_[0]; }
|
||||
|
||||
T& operator()(int i) { return data_[i]; }
|
||||
const T& operator()(int i) const { return data_[i]; }
|
||||
|
||||
bool operator==(const Tuple<T, N>& other) const {
|
||||
for (int i = 0; i < N; ++i) {
|
||||
if ((*this)(i) != other(i)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool operator!=(const Tuple<T, N>& other) const { return !(*this == other); }
|
||||
|
||||
private:
|
||||
T data_[N];
|
||||
};
|
||||
|
||||
} // namespace libmv
|
||||
|
||||
#endif // LIBMV_IMAGE_TUPLE_H
|
||||
84
blender-5.2.0/intern/libmv/libmv/image/tuple_test.cc
Normal file
84
blender-5.2.0/intern/libmv/libmv/image/tuple_test.cc
Normal file
@@ -0,0 +1,84 @@
|
||||
// Copyright (c) 2007, 2008 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/image/tuple.h"
|
||||
#include "testing/testing.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
using libmv::Tuple;
|
||||
|
||||
namespace {
|
||||
|
||||
TEST(Tuple, InitConstantValue) {
|
||||
Tuple<int, 3> t(5);
|
||||
EXPECT_EQ(t(0), 5);
|
||||
EXPECT_EQ(t(0), 5);
|
||||
EXPECT_EQ(t(0), 5);
|
||||
}
|
||||
|
||||
TEST(Tuple, InitFromPointer) {
|
||||
float vals[3] = {1.0f, 2.0f, 3.0f};
|
||||
|
||||
Tuple<float, 3> t(vals);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
EXPECT_EQ(t(i), vals[i]);
|
||||
}
|
||||
|
||||
Tuple<int, 3> b(t);
|
||||
EXPECT_EQ(b(0), int(vals[0]));
|
||||
EXPECT_EQ(b(1), int(vals[1]));
|
||||
EXPECT_EQ(b(2), int(vals[2]));
|
||||
}
|
||||
|
||||
TEST(Tuple, Swap) {
|
||||
unsigned char vala[3] = {1, 2, 3};
|
||||
unsigned char valb[3] = {4, 5, 6};
|
||||
|
||||
Tuple<unsigned char, 3> a(vala);
|
||||
Tuple<unsigned char, 3> b(valb);
|
||||
|
||||
std::swap(a, b);
|
||||
|
||||
EXPECT_EQ(a(0), int(valb[0]));
|
||||
EXPECT_EQ(a(1), int(valb[1]));
|
||||
EXPECT_EQ(a(2), int(valb[2]));
|
||||
EXPECT_EQ(b(0), int(vala[0]));
|
||||
EXPECT_EQ(b(1), int(vala[1]));
|
||||
EXPECT_EQ(b(2), int(vala[2]));
|
||||
}
|
||||
|
||||
TEST(Tuple, IsEqualOperator) {
|
||||
Tuple<int, 3> a;
|
||||
a(0) = 1;
|
||||
a(1) = 2;
|
||||
a(2) = 3;
|
||||
Tuple<int, 3> b;
|
||||
b(0) = 1;
|
||||
b(1) = 2;
|
||||
b(2) = 3;
|
||||
EXPECT_TRUE(a == b);
|
||||
EXPECT_FALSE(a != b);
|
||||
b(1) = 5;
|
||||
EXPECT_TRUE(a != b);
|
||||
EXPECT_FALSE(a == b);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
42
blender-5.2.0/intern/libmv/libmv/logging/logging.h
Normal file
42
blender-5.2.0/intern/libmv/libmv/logging/logging.h
Normal file
@@ -0,0 +1,42 @@
|
||||
// Copyright (c) 2007, 2008, 2009 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.
|
||||
|
||||
#ifndef LIBMV_LOGGING_LOGGING_H
|
||||
#define LIBMV_LOGGING_LOGGING_H
|
||||
|
||||
#include <glog/logging.h>
|
||||
|
||||
// Note on logging severity and verbosity level.
|
||||
//
|
||||
// Reserve LOG(INFO) for messages which are always to be put to log and don't
|
||||
// use the INFO severity for the debugging/troubleshooting type of messages.
|
||||
// Some reasoning behind:
|
||||
//
|
||||
// - Library integration would want to disable "noisy" messages coming from
|
||||
// algorithms.
|
||||
//
|
||||
// - It is not possible to disable INFO severity entirely: there is enough
|
||||
// of preparation being done for the message stream. What is even worse
|
||||
// is that such stream preparation causes measurable time spent in spin
|
||||
// lock, ruining multi-threading.
|
||||
|
||||
#define LG VLOG(1)
|
||||
|
||||
#endif // LIBMV_LOGGING_LOGGING_H
|
||||
103
blender-5.2.0/intern/libmv/libmv/multiview/conditioning.cc
Normal file
103
blender-5.2.0/intern/libmv/libmv/multiview/conditioning.cc
Normal file
@@ -0,0 +1,103 @@
|
||||
// Copyright (c) 2010 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/multiview/conditioning.h"
|
||||
#include "libmv/multiview/projection.h"
|
||||
|
||||
namespace libmv {
|
||||
|
||||
// HZ 4.4.4 pag.109: Point conditioning (non isotropic)
|
||||
void PreconditionerFromPoints(const Mat& points, Mat3* T) {
|
||||
Vec mean, variance;
|
||||
MeanAndVarianceAlongRows(points, &mean, &variance);
|
||||
|
||||
double xfactor = sqrt(2.0 / variance(0));
|
||||
double yfactor = sqrt(2.0 / variance(1));
|
||||
|
||||
// If variance is equal to 0.0 set scaling factor to identity.
|
||||
// -> Else it will provide nan value (because division by 0).
|
||||
if (variance(0) < 1e-8) {
|
||||
xfactor = mean(0) = 1.0;
|
||||
}
|
||||
if (variance(1) < 1e-8) {
|
||||
yfactor = mean(1) = 1.0;
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
*T << xfactor, 0, -xfactor * mean(0),
|
||||
0, yfactor, -yfactor * mean(1),
|
||||
0, 0, 1;
|
||||
// clang-format on
|
||||
}
|
||||
// HZ 4.4.4 pag.107: Point conditioning (isotropic)
|
||||
void IsotropicPreconditionerFromPoints(const Mat& points, Mat3* T) {
|
||||
Vec mean, variance;
|
||||
MeanAndVarianceAlongRows(points, &mean, &variance);
|
||||
|
||||
double var_norm = variance.norm();
|
||||
double factor = sqrt(2.0 / var_norm);
|
||||
|
||||
// If variance is equal to 0.0 set scaling factor to identity.
|
||||
// -> Else it will provide nan value (because division by 0).
|
||||
if (var_norm < 1e-8) {
|
||||
factor = 1.0;
|
||||
mean.setOnes();
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
*T << factor, 0, -factor * mean(0),
|
||||
0, factor, -factor * mean(1),
|
||||
0, 0, 1;
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
void ApplyTransformationToPoints(const Mat& points,
|
||||
const Mat3& T,
|
||||
Mat* transformed_points) {
|
||||
int n = points.cols();
|
||||
transformed_points->resize(2, n);
|
||||
Mat3X p(3, n);
|
||||
EuclideanToHomogeneous(points, &p);
|
||||
p = T * p;
|
||||
HomogeneousToEuclidean(p, transformed_points);
|
||||
}
|
||||
|
||||
void NormalizePoints(const Mat& points, Mat* normalized_points, Mat3* T) {
|
||||
PreconditionerFromPoints(points, T);
|
||||
ApplyTransformationToPoints(points, *T, normalized_points);
|
||||
}
|
||||
|
||||
void NormalizeIsotropicPoints(const Mat& points,
|
||||
Mat* normalized_points,
|
||||
Mat3* T) {
|
||||
IsotropicPreconditionerFromPoints(points, T);
|
||||
ApplyTransformationToPoints(points, *T, normalized_points);
|
||||
}
|
||||
|
||||
// Denormalize the results. See HZ page 109.
|
||||
void UnnormalizerT::Unnormalize(const Mat3& T1, const Mat3& T2, Mat3* H) {
|
||||
*H = T2.transpose() * (*H) * T1;
|
||||
}
|
||||
|
||||
void UnnormalizerI::Unnormalize(const Mat3& T1, const Mat3& T2, Mat3* H) {
|
||||
*H = T2.inverse() * (*H) * T1;
|
||||
}
|
||||
|
||||
} // namespace libmv
|
||||
57
blender-5.2.0/intern/libmv/libmv/multiview/conditioning.h
Normal file
57
blender-5.2.0/intern/libmv/libmv/multiview/conditioning.h
Normal file
@@ -0,0 +1,57 @@
|
||||
// Copyright (c) 2010 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.
|
||||
|
||||
#ifndef LIBMV_MULTIVIEW_CONDITIONNING_H_
|
||||
#define LIBMV_MULTIVIEW_CONDITIONNING_H_
|
||||
|
||||
#include "libmv/numeric/numeric.h"
|
||||
|
||||
namespace libmv {
|
||||
|
||||
// Point conditioning (non isotropic)
|
||||
void PreconditionerFromPoints(const Mat& points, Mat3* T);
|
||||
// Point conditioning (isotropic)
|
||||
void IsotropicPreconditionerFromPoints(const Mat& points, Mat3* T);
|
||||
|
||||
void ApplyTransformationToPoints(const Mat& points,
|
||||
const Mat3& T,
|
||||
Mat* transformed_points);
|
||||
|
||||
void NormalizePoints(const Mat& points, Mat* normalized_points, Mat3* T);
|
||||
|
||||
void NormalizeIsotropicPoints(const Mat& points,
|
||||
Mat* normalized_points,
|
||||
Mat3* T);
|
||||
|
||||
/// Use inverse for unnormalize
|
||||
struct UnnormalizerI {
|
||||
// Denormalize the results. See HZ page 109.
|
||||
static void Unnormalize(const Mat3& T1, const Mat3& T2, Mat3* H);
|
||||
};
|
||||
|
||||
/// Use transpose for unnormalize
|
||||
struct UnnormalizerT {
|
||||
// Denormalize the results. See HZ page 109.
|
||||
static void Unnormalize(const Mat3& T1, const Mat3& T2, Mat3* H);
|
||||
};
|
||||
|
||||
} // namespace libmv
|
||||
|
||||
#endif // LIBMV_MULTIVIEW_CONDITIONNING_H_
|
||||
@@ -0,0 +1,798 @@
|
||||
// Copyright (c) 2009 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/multiview/euclidean_resection.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
|
||||
#include <Eigen/Geometry>
|
||||
#include <Eigen/SVD>
|
||||
|
||||
#include "libmv/base/vector.h"
|
||||
#include "libmv/logging/logging.h"
|
||||
#include "libmv/multiview/projection.h"
|
||||
|
||||
namespace libmv {
|
||||
namespace euclidean_resection {
|
||||
|
||||
typedef unsigned int uint;
|
||||
|
||||
bool EuclideanResection(const Mat2X& x_camera,
|
||||
const Mat3X& X_world,
|
||||
Mat3* R,
|
||||
Vec3* t,
|
||||
ResectionMethod method) {
|
||||
switch (method) {
|
||||
case RESECTION_ANSAR_DANIILIDIS:
|
||||
EuclideanResectionAnsarDaniilidis(x_camera, X_world, R, t);
|
||||
break;
|
||||
case RESECTION_EPNP:
|
||||
return EuclideanResectionEPnP(x_camera, X_world, R, t);
|
||||
break;
|
||||
case RESECTION_PPNP:
|
||||
return EuclideanResectionPPnP(x_camera, X_world, R, t);
|
||||
break;
|
||||
default: LOG(FATAL) << "Unknown resection method.";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool EuclideanResection(const Mat& x_image,
|
||||
const Mat3X& X_world,
|
||||
const Mat3& K,
|
||||
Mat3* R,
|
||||
Vec3* t,
|
||||
ResectionMethod method) {
|
||||
CHECK(x_image.rows() == 2 || x_image.rows() == 3)
|
||||
<< "Invalid size for x_image: " << x_image.rows() << "x"
|
||||
<< x_image.cols();
|
||||
|
||||
Mat2X x_camera;
|
||||
if (x_image.rows() == 2) {
|
||||
EuclideanToNormalizedCamera(x_image, K, &x_camera);
|
||||
} else if (x_image.rows() == 3) {
|
||||
HomogeneousToNormalizedCamera(x_image, K, &x_camera);
|
||||
}
|
||||
return EuclideanResection(x_camera, X_world, R, t, method);
|
||||
}
|
||||
|
||||
void AbsoluteOrientation(const Mat3X& X, const Mat3X& Xp, Mat3* R, Vec3* t) {
|
||||
int num_points = X.cols();
|
||||
Vec3 C = X.rowwise().sum() / num_points; // Centroid of X.
|
||||
Vec3 Cp = Xp.rowwise().sum() / num_points; // Centroid of Xp.
|
||||
|
||||
// Normalize the two point sets.
|
||||
Mat3X Xn(3, num_points), Xpn(3, num_points);
|
||||
for (int i = 0; i < num_points; ++i) {
|
||||
Xn.col(i) = X.col(i) - C;
|
||||
Xpn.col(i) = Xp.col(i) - Cp;
|
||||
}
|
||||
|
||||
// Construct the N matrix (pg. 635).
|
||||
double Sxx = Xn.row(0).dot(Xpn.row(0));
|
||||
double Syy = Xn.row(1).dot(Xpn.row(1));
|
||||
double Szz = Xn.row(2).dot(Xpn.row(2));
|
||||
double Sxy = Xn.row(0).dot(Xpn.row(1));
|
||||
double Syx = Xn.row(1).dot(Xpn.row(0));
|
||||
double Sxz = Xn.row(0).dot(Xpn.row(2));
|
||||
double Szx = Xn.row(2).dot(Xpn.row(0));
|
||||
double Syz = Xn.row(1).dot(Xpn.row(2));
|
||||
double Szy = Xn.row(2).dot(Xpn.row(1));
|
||||
|
||||
Mat4 N;
|
||||
// clang-format off
|
||||
N << Sxx + Syy + Szz, Syz - Szy, Szx - Sxz, Sxy - Syx,
|
||||
Syz - Szy, Sxx - Syy - Szz, Sxy + Syx, Szx + Sxz,
|
||||
Szx - Sxz, Sxy + Syx, -Sxx + Syy - Szz, Syz + Szy,
|
||||
Sxy - Syx, Szx + Sxz, Syz + Szy, -Sxx - Syy + Szz;
|
||||
// clang-format on
|
||||
|
||||
// Find the unit quaternion q that maximizes qNq. It is the eigenvector
|
||||
// corresponding to the lagest eigenvalue.
|
||||
Vec4 q = N.jacobiSvd(Eigen::ComputeFullU).matrixU().col(0);
|
||||
|
||||
// Retrieve the 3x3 rotation matrix.
|
||||
Vec4 qq = q.array() * q.array();
|
||||
double q0q1 = q(0) * q(1);
|
||||
double q0q2 = q(0) * q(2);
|
||||
double q0q3 = q(0) * q(3);
|
||||
double q1q2 = q(1) * q(2);
|
||||
double q1q3 = q(1) * q(3);
|
||||
double q2q3 = q(2) * q(3);
|
||||
|
||||
// clang-format off
|
||||
(*R) << qq(0) + qq(1) - qq(2) - qq(3),
|
||||
2 * (q1q2 - q0q3),
|
||||
2 * (q1q3 + q0q2),
|
||||
2 * (q1q2+ q0q3),
|
||||
qq(0) - qq(1) + qq(2) - qq(3),
|
||||
2 * (q2q3 - q0q1),
|
||||
2 * (q1q3 - q0q2),
|
||||
2 * (q2q3 + q0q1),
|
||||
qq(0) - qq(1) - qq(2) + qq(3);
|
||||
// clang-format on
|
||||
|
||||
// Fix the handedness of the R matrix.
|
||||
if (R->determinant() < 0) {
|
||||
R->row(2) = -R->row(2);
|
||||
}
|
||||
// Compute the final translation.
|
||||
*t = Cp - *R * C;
|
||||
}
|
||||
|
||||
// Convert i and j indices of the original variables into their quadratic
|
||||
// permutation single index. It follows that t_ij = t_ji.
|
||||
static int IJToPointIndex(int i, int j, int num_points) {
|
||||
// Always make sure that j is bigger than i. This handles t_ij = t_ji.
|
||||
if (j < i) {
|
||||
std::swap(i, j);
|
||||
}
|
||||
int idx;
|
||||
int num_permutation_rows = num_points * (num_points - 1) / 2;
|
||||
|
||||
// All t_ii's are located at the end of the t vector after all t_ij's.
|
||||
if (j == i) {
|
||||
idx = num_permutation_rows + i;
|
||||
} else {
|
||||
int offset = (num_points - i - 1) * (num_points - i) / 2;
|
||||
idx = (num_permutation_rows - offset + j - i - 1);
|
||||
}
|
||||
return idx;
|
||||
};
|
||||
|
||||
// Convert i and j indexes of the solution for lambda to their linear indexes.
|
||||
static int IJToIndex(int i, int j, int num_lambda) {
|
||||
if (j < i) {
|
||||
std::swap(i, j);
|
||||
}
|
||||
int A = num_lambda * (num_lambda + 1) / 2;
|
||||
int B = num_lambda - i;
|
||||
int C = B * (B + 1) / 2;
|
||||
int idx = A - C + j - i;
|
||||
return idx;
|
||||
};
|
||||
|
||||
static int Sign(double value) {
|
||||
return (value < 0) ? -1 : 1;
|
||||
};
|
||||
|
||||
// Organizes a square matrix into a single row constraint on the elements of
|
||||
// Lambda to create the constraints in equation (5) in "Linear Pose Estimation
|
||||
// from Points or Lines", by Ansar, A. and Daniilidis, PAMI 2003. vol. 25, no.
|
||||
// 5.
|
||||
static Vec MatrixToConstraint(const Mat& A, int num_k_columns, int num_lambda) {
|
||||
Vec C(num_k_columns);
|
||||
C.setZero();
|
||||
int idx = 0;
|
||||
for (int i = 0; i < num_lambda; ++i) {
|
||||
for (int j = i; j < num_lambda; ++j) {
|
||||
C(idx) = A(i, j);
|
||||
if (i != j) {
|
||||
C(idx) += A(j, i);
|
||||
}
|
||||
++idx;
|
||||
}
|
||||
}
|
||||
return C;
|
||||
}
|
||||
|
||||
// Normalizes the columns of vectors.
|
||||
static void NormalizeColumnVectors(Mat3X* vectors) {
|
||||
int num_columns = vectors->cols();
|
||||
for (int i = 0; i < num_columns; ++i) {
|
||||
vectors->col(i).normalize();
|
||||
}
|
||||
}
|
||||
|
||||
void EuclideanResectionAnsarDaniilidis(const Mat2X& x_camera,
|
||||
const Mat3X& X_world,
|
||||
Mat3* R,
|
||||
Vec3* t) {
|
||||
CHECK(x_camera.cols() == X_world.cols());
|
||||
CHECK(x_camera.cols() > 3);
|
||||
|
||||
int num_points = x_camera.cols();
|
||||
|
||||
// Copy the normalized camera coords into 3 vectors and normalize them so
|
||||
// that they are unit vectors from the camera center.
|
||||
Mat3X x_camera_unit(3, num_points);
|
||||
x_camera_unit.block(0, 0, 2, num_points) = x_camera;
|
||||
x_camera_unit.row(2).setOnes();
|
||||
NormalizeColumnVectors(&x_camera_unit);
|
||||
|
||||
int num_m_rows = num_points * (num_points - 1) / 2;
|
||||
int num_tt_variables = num_points * (num_points + 1) / 2;
|
||||
int num_m_columns = num_tt_variables + 1;
|
||||
Mat M(num_m_columns, num_m_columns);
|
||||
M.setZero();
|
||||
Matu ij_index(num_tt_variables, 2);
|
||||
|
||||
// Create the constraint equations for the t_ij variables (7) and arrange
|
||||
// them into the M matrix (8). Also store the initial (i, j) indices.
|
||||
int row = 0;
|
||||
for (int i = 0; i < num_points; ++i) {
|
||||
for (int j = i + 1; j < num_points; ++j) {
|
||||
M(row, row) = -2 * x_camera_unit.col(i).dot(x_camera_unit.col(j));
|
||||
M(row, num_m_rows + i) = x_camera_unit.col(i).dot(x_camera_unit.col(i));
|
||||
M(row, num_m_rows + j) = x_camera_unit.col(j).dot(x_camera_unit.col(j));
|
||||
Vec3 Xdiff = X_world.col(i) - X_world.col(j);
|
||||
double center_to_point_distance = Xdiff.norm();
|
||||
M(row, num_m_columns - 1) =
|
||||
-center_to_point_distance * center_to_point_distance;
|
||||
ij_index(row, 0) = i;
|
||||
ij_index(row, 1) = j;
|
||||
++row;
|
||||
}
|
||||
ij_index(i + num_m_rows, 0) = i;
|
||||
ij_index(i + num_m_rows, 1) = i;
|
||||
}
|
||||
|
||||
int num_lambda = num_points + 1; // Dimension of the null space of M.
|
||||
Mat V = M.jacobiSvd(Eigen::ComputeFullV).matrixV().block(
|
||||
0, num_m_rows, num_m_columns, num_lambda);
|
||||
|
||||
// TODO(vess): The number of constraint equations in K (num_k_rows) must be
|
||||
// (num_points + 1) * (num_points + 2)/2. This creates a performance issue
|
||||
// for more than 4 points. It is fine for 4 points at the moment with 18
|
||||
// instead of 15 equations.
|
||||
int num_k_rows =
|
||||
num_m_rows +
|
||||
num_points * (num_points * (num_points - 1) / 2 - num_points + 1);
|
||||
int num_k_columns = num_lambda * (num_lambda + 1) / 2;
|
||||
Mat K(num_k_rows, num_k_columns);
|
||||
K.setZero();
|
||||
|
||||
// Construct the first part of the K matrix corresponding to (t_ii, t_jk) for
|
||||
// i != j.
|
||||
int counter_k_row = 0;
|
||||
for (int idx1 = num_m_rows; idx1 < num_tt_variables; ++idx1) {
|
||||
for (int idx2 = 0; idx2 < num_m_rows; ++idx2) {
|
||||
unsigned int i = ij_index(idx1, 0);
|
||||
unsigned int j = ij_index(idx2, 0);
|
||||
unsigned int k = ij_index(idx2, 1);
|
||||
|
||||
if (i != j && i != k) {
|
||||
int idx3 = IJToPointIndex(i, j, num_points);
|
||||
int idx4 = IJToPointIndex(i, k, num_points);
|
||||
|
||||
K.row(counter_k_row) =
|
||||
MatrixToConstraint(V.row(idx1).transpose() * V.row(idx2) -
|
||||
V.row(idx3).transpose() * V.row(idx4),
|
||||
num_k_columns,
|
||||
num_lambda);
|
||||
++counter_k_row;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Construct the second part of the K matrix corresponding to (t_ii,t_jk) for
|
||||
// j==k.
|
||||
for (int idx1 = num_m_rows; idx1 < num_tt_variables; ++idx1) {
|
||||
for (int idx2 = idx1 + 1; idx2 < num_tt_variables; ++idx2) {
|
||||
unsigned int i = ij_index(idx1, 0);
|
||||
unsigned int j = ij_index(idx2, 0);
|
||||
unsigned int k = ij_index(idx2, 1);
|
||||
|
||||
int idx3 = IJToPointIndex(i, j, num_points);
|
||||
int idx4 = IJToPointIndex(i, k, num_points);
|
||||
|
||||
K.row(counter_k_row) =
|
||||
MatrixToConstraint(V.row(idx1).transpose() * V.row(idx2) -
|
||||
V.row(idx3).transpose() * V.row(idx4),
|
||||
num_k_columns,
|
||||
num_lambda);
|
||||
++counter_k_row;
|
||||
}
|
||||
}
|
||||
Vec L_sq = K.jacobiSvd(Eigen::ComputeFullV).matrixV().col(num_k_columns - 1);
|
||||
|
||||
// Pivot on the largest element for numerical stability. Afterwards recover
|
||||
// the sign of the lambda solution.
|
||||
double max_L_sq_value = fabs(L_sq(IJToIndex(0, 0, num_lambda)));
|
||||
int max_L_sq_index = 1;
|
||||
for (int i = 1; i < num_lambda; ++i) {
|
||||
double abs_sq_value = fabs(L_sq(IJToIndex(i, i, num_lambda)));
|
||||
if (max_L_sq_value < abs_sq_value) {
|
||||
max_L_sq_value = abs_sq_value;
|
||||
max_L_sq_index = i;
|
||||
}
|
||||
}
|
||||
// Ensure positiveness of the largest value corresponding to lambda_ii.
|
||||
L_sq =
|
||||
L_sq * Sign(L_sq(IJToIndex(max_L_sq_index, max_L_sq_index, num_lambda)));
|
||||
|
||||
Vec L(num_lambda);
|
||||
L(max_L_sq_index) =
|
||||
sqrt(L_sq(IJToIndex(max_L_sq_index, max_L_sq_index, num_lambda)));
|
||||
|
||||
for (int i = 0; i < num_lambda; ++i) {
|
||||
if (i != max_L_sq_index) {
|
||||
L(i) = L_sq(IJToIndex(max_L_sq_index, i, num_lambda)) / L(max_L_sq_index);
|
||||
}
|
||||
}
|
||||
|
||||
// Correct the scale using the fact that the last constraint is equal to 1.
|
||||
L = L / (V.row(num_m_columns - 1).dot(L));
|
||||
Vec X = V * L;
|
||||
|
||||
// Recover the distances from the camera center to the 3D points Q.
|
||||
Vec d(num_points);
|
||||
d.setZero();
|
||||
for (int c_point = num_m_rows; c_point < num_tt_variables; ++c_point) {
|
||||
d(c_point - num_m_rows) = sqrt(X(c_point));
|
||||
}
|
||||
|
||||
// Create the 3D points in the camera system.
|
||||
Mat X_cam(3, num_points);
|
||||
for (int c_point = 0; c_point < num_points; ++c_point) {
|
||||
X_cam.col(c_point) = d(c_point) * x_camera_unit.col(c_point);
|
||||
}
|
||||
// Recover the camera translation and rotation.
|
||||
AbsoluteOrientation(X_world, X_cam, R, t);
|
||||
}
|
||||
|
||||
// Selects 4 virtual control points using mean and PCA.
|
||||
static void SelectControlPoints(const Mat3X& X_world,
|
||||
Mat* X_centered,
|
||||
Mat34* X_control_points) {
|
||||
size_t num_points = X_world.cols();
|
||||
|
||||
// The first virtual control point, C0, is the centroid.
|
||||
Vec mean, variance;
|
||||
MeanAndVarianceAlongRows(X_world, &mean, &variance);
|
||||
X_control_points->col(0) = mean;
|
||||
|
||||
// Computes PCA
|
||||
X_centered->resize(3, num_points);
|
||||
for (size_t c = 0; c < num_points; c++) {
|
||||
X_centered->col(c) = X_world.col(c) - mean;
|
||||
}
|
||||
Mat3 X_centered_sq = (*X_centered) * X_centered->transpose();
|
||||
Eigen::JacobiSVD<Mat3> X_centered_sq_svd(X_centered_sq, Eigen::ComputeFullU);
|
||||
Vec3 w = X_centered_sq_svd.singularValues();
|
||||
Mat3 u = X_centered_sq_svd.matrixU();
|
||||
for (size_t c = 0; c < 3; c++) {
|
||||
double k = sqrt(w(c) / num_points);
|
||||
X_control_points->col(c + 1) = mean + k * u.col(c);
|
||||
}
|
||||
}
|
||||
|
||||
// Computes the barycentric coordinates for all real points
|
||||
static bool ComputeBarycentricCoordinates(const Mat3X& X_world_centered,
|
||||
const Mat34& X_control_points,
|
||||
Mat4X* alphas) {
|
||||
size_t num_points = X_world_centered.cols();
|
||||
Mat3 C2;
|
||||
for (size_t c = 1; c < 4; c++) {
|
||||
C2.col(c - 1) = X_control_points.col(c) - X_control_points.col(0);
|
||||
}
|
||||
|
||||
// The selected basis might have correlated vectors causing inverse to produce
|
||||
// undefined result which could lead to issues later on in SVD decomposition.
|
||||
//
|
||||
// Evidently, when built with MSVC it could lead to crash in SVD when the
|
||||
// input contains NaN values:
|
||||
// https://projects.blender.org/blender/blender/issues/145185
|
||||
//
|
||||
// TODO(sergey): Look into choosing a different basis in these cases.
|
||||
bool is_invertible;
|
||||
Mat3 C2inv;
|
||||
C2.computeInverseWithCheck(C2inv, is_invertible);
|
||||
if (!is_invertible) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Mat3X a = C2inv * X_world_centered;
|
||||
|
||||
alphas->resize(4, num_points);
|
||||
alphas->setZero();
|
||||
alphas->block(1, 0, 3, num_points) = a;
|
||||
for (size_t c = 0; c < num_points; c++) {
|
||||
(*alphas)(0, c) = 1.0 - alphas->col(c).sum();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Estimates the coordinates of all real points in the camera coordinate frame
|
||||
static void ComputePointsCoordinatesInCameraFrame(
|
||||
const Mat4X& alphas,
|
||||
const Vec4& betas,
|
||||
const Eigen::Matrix<double, 12, 12>& U,
|
||||
Mat3X* X_camera) {
|
||||
size_t num_points = alphas.cols();
|
||||
|
||||
// Estimates the control points in the camera reference frame.
|
||||
Mat34 C2b;
|
||||
C2b.setZero();
|
||||
for (size_t cu = 0; cu < 4; cu++) {
|
||||
for (size_t c = 0; c < 4; c++) {
|
||||
C2b.col(c) += betas(cu) * U.block(11 - cu, c * 3, 1, 3).transpose();
|
||||
}
|
||||
}
|
||||
|
||||
// Estimates the 3D points in the camera reference frame
|
||||
X_camera->resize(3, num_points);
|
||||
for (size_t c = 0; c < num_points; c++) {
|
||||
X_camera->col(c) = C2b * alphas.col(c);
|
||||
}
|
||||
|
||||
// Check the sign of the z coordinate of the points (should be positive)
|
||||
uint num_z_neg = 0;
|
||||
for (size_t i = 0; i < X_camera->cols(); ++i) {
|
||||
if ((*X_camera)(2, i) < 0) {
|
||||
num_z_neg++;
|
||||
}
|
||||
}
|
||||
|
||||
// If more than 50% of z are negative, we change the signs
|
||||
if (num_z_neg > 0.5 * X_camera->cols()) {
|
||||
C2b = -C2b;
|
||||
*X_camera = -(*X_camera);
|
||||
}
|
||||
}
|
||||
|
||||
bool EuclideanResectionEPnP(const Mat2X& x_camera,
|
||||
const Mat3X& X_world,
|
||||
Mat3* R,
|
||||
Vec3* t) {
|
||||
CHECK(x_camera.cols() == X_world.cols());
|
||||
CHECK(x_camera.cols() > 3);
|
||||
size_t num_points = X_world.cols();
|
||||
|
||||
// Select the control points.
|
||||
Mat34 X_control_points;
|
||||
Mat X_centered;
|
||||
SelectControlPoints(X_world, &X_centered, &X_control_points);
|
||||
|
||||
// Compute the barycentric coordinates.
|
||||
Mat4X alphas(4, num_points);
|
||||
if (!ComputeBarycentricCoordinates(X_centered, X_control_points, &alphas)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Estimates the M matrix with the barycentric coordinates
|
||||
Mat M(2 * num_points, 12);
|
||||
for (size_t c = 0; c < num_points; c++) {
|
||||
double a0 = alphas(0, c);
|
||||
double a1 = alphas(1, c);
|
||||
double a2 = alphas(2, c);
|
||||
double a3 = alphas(3, c);
|
||||
double ui = x_camera(0, c);
|
||||
double vi = x_camera(1, c);
|
||||
// clang-format off
|
||||
M.block(2*c, 0, 2, 12) << a0, 0,
|
||||
a0*(-ui), a1, 0,
|
||||
a1*(-ui), a2, 0,
|
||||
a2*(-ui), a3, 0,
|
||||
a3*(-ui), 0,
|
||||
a0, a0*(-vi), 0,
|
||||
a1, a1*(-vi), 0,
|
||||
a2, a2*(-vi), 0,
|
||||
a3, a3*(-vi);
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
// TODO(julien): Avoid the transpose by rewriting the u2.block() calls.
|
||||
Eigen::JacobiSVD<Mat> MtMsvd(M.transpose() * M, Eigen::ComputeFullU);
|
||||
Eigen::Matrix<double, 12, 12> u2 = MtMsvd.matrixU().transpose();
|
||||
|
||||
// Estimate the L matrix.
|
||||
Eigen::Matrix<double, 6, 3> dv1;
|
||||
Eigen::Matrix<double, 6, 3> dv2;
|
||||
Eigen::Matrix<double, 6, 3> dv3;
|
||||
Eigen::Matrix<double, 6, 3> dv4;
|
||||
|
||||
dv1.row(0) = u2.block(11, 0, 1, 3) - u2.block(11, 3, 1, 3);
|
||||
dv1.row(1) = u2.block(11, 0, 1, 3) - u2.block(11, 6, 1, 3);
|
||||
dv1.row(2) = u2.block(11, 0, 1, 3) - u2.block(11, 9, 1, 3);
|
||||
dv1.row(3) = u2.block(11, 3, 1, 3) - u2.block(11, 6, 1, 3);
|
||||
dv1.row(4) = u2.block(11, 3, 1, 3) - u2.block(11, 9, 1, 3);
|
||||
dv1.row(5) = u2.block(11, 6, 1, 3) - u2.block(11, 9, 1, 3);
|
||||
dv2.row(0) = u2.block(10, 0, 1, 3) - u2.block(10, 3, 1, 3);
|
||||
dv2.row(1) = u2.block(10, 0, 1, 3) - u2.block(10, 6, 1, 3);
|
||||
dv2.row(2) = u2.block(10, 0, 1, 3) - u2.block(10, 9, 1, 3);
|
||||
dv2.row(3) = u2.block(10, 3, 1, 3) - u2.block(10, 6, 1, 3);
|
||||
dv2.row(4) = u2.block(10, 3, 1, 3) - u2.block(10, 9, 1, 3);
|
||||
dv2.row(5) = u2.block(10, 6, 1, 3) - u2.block(10, 9, 1, 3);
|
||||
dv3.row(0) = u2.block(9, 0, 1, 3) - u2.block(9, 3, 1, 3);
|
||||
dv3.row(1) = u2.block(9, 0, 1, 3) - u2.block(9, 6, 1, 3);
|
||||
dv3.row(2) = u2.block(9, 0, 1, 3) - u2.block(9, 9, 1, 3);
|
||||
dv3.row(3) = u2.block(9, 3, 1, 3) - u2.block(9, 6, 1, 3);
|
||||
dv3.row(4) = u2.block(9, 3, 1, 3) - u2.block(9, 9, 1, 3);
|
||||
dv3.row(5) = u2.block(9, 6, 1, 3) - u2.block(9, 9, 1, 3);
|
||||
dv4.row(0) = u2.block(8, 0, 1, 3) - u2.block(8, 3, 1, 3);
|
||||
dv4.row(1) = u2.block(8, 0, 1, 3) - u2.block(8, 6, 1, 3);
|
||||
dv4.row(2) = u2.block(8, 0, 1, 3) - u2.block(8, 9, 1, 3);
|
||||
dv4.row(3) = u2.block(8, 3, 1, 3) - u2.block(8, 6, 1, 3);
|
||||
dv4.row(4) = u2.block(8, 3, 1, 3) - u2.block(8, 9, 1, 3);
|
||||
dv4.row(5) = u2.block(8, 6, 1, 3) - u2.block(8, 9, 1, 3);
|
||||
|
||||
Eigen::Matrix<double, 6, 10> L;
|
||||
for (size_t r = 0; r < 6; r++) {
|
||||
// clang-format off
|
||||
L.row(r) << dv1.row(r).dot(dv1.row(r)),
|
||||
2.0 * dv1.row(r).dot(dv2.row(r)),
|
||||
dv2.row(r).dot(dv2.row(r)),
|
||||
2.0 * dv1.row(r).dot(dv3.row(r)),
|
||||
2.0 * dv2.row(r).dot(dv3.row(r)),
|
||||
dv3.row(r).dot(dv3.row(r)),
|
||||
2.0 * dv1.row(r).dot(dv4.row(r)),
|
||||
2.0 * dv2.row(r).dot(dv4.row(r)),
|
||||
2.0 * dv3.row(r).dot(dv4.row(r)),
|
||||
dv4.row(r).dot(dv4.row(r));
|
||||
// clang-format on
|
||||
}
|
||||
Vec6 rho;
|
||||
// clang-format off
|
||||
rho << (X_control_points.col(0) - X_control_points.col(1)).squaredNorm(),
|
||||
(X_control_points.col(0) - X_control_points.col(2)).squaredNorm(),
|
||||
(X_control_points.col(0) - X_control_points.col(3)).squaredNorm(),
|
||||
(X_control_points.col(1) - X_control_points.col(2)).squaredNorm(),
|
||||
(X_control_points.col(1) - X_control_points.col(3)).squaredNorm(),
|
||||
(X_control_points.col(2) - X_control_points.col(3)).squaredNorm();
|
||||
// clang-format on
|
||||
|
||||
// There are three possible solutions based on the three approximations of L
|
||||
// (betas). Below, each one is solved for then the best one is chosen.
|
||||
Mat3X X_camera;
|
||||
Mat3 K;
|
||||
K.setIdentity();
|
||||
vector<Mat3> Rs(3);
|
||||
vector<Vec3> ts(3);
|
||||
Vec rmse(3);
|
||||
|
||||
// At one point this threshold was 1e-3, and caused no end of problems in
|
||||
// Blender by causing frames to not resect when they would have worked fine.
|
||||
// When the resect failed, the projective followup is run leading to worse
|
||||
// results, and often the dreaded "flipping" where objects get flipped
|
||||
// between frames. Instead, disable the check for now, always succeeding. The
|
||||
// ultimate check is always reprojection error anyway.
|
||||
//
|
||||
// TODO(keir): Decide if setting this to infinity, effectively disabling the
|
||||
// check, is the right approach. So far this seems the case.
|
||||
double kSuccessThreshold = std::numeric_limits<double>::max();
|
||||
|
||||
// Find the first possible solution for R, t corresponding to:
|
||||
// Betas = [b00 b01 b11 b02 b12 b22 b03 b13 b23 b33]
|
||||
// Betas_approx_1 = [b00 b01 b02 b03]
|
||||
Vec4 betas = Vec4::Zero();
|
||||
Eigen::Matrix<double, 6, 4> l_6x4;
|
||||
for (size_t r = 0; r < 6; r++) {
|
||||
l_6x4.row(r) << L(r, 0), L(r, 1), L(r, 3), L(r, 6);
|
||||
}
|
||||
Eigen::JacobiSVD<Mat> svd_of_l4(l_6x4,
|
||||
Eigen::ComputeFullU | Eigen::ComputeFullV);
|
||||
Vec4 b4 = svd_of_l4.solve(rho);
|
||||
if ((l_6x4 * b4).isApprox(rho, kSuccessThreshold)) {
|
||||
if (b4(0) < 0) {
|
||||
b4 = -b4;
|
||||
}
|
||||
b4(0) = std::sqrt(b4(0));
|
||||
betas << b4(0), b4(1) / b4(0), b4(2) / b4(0), b4(3) / b4(0);
|
||||
ComputePointsCoordinatesInCameraFrame(alphas, betas, u2, &X_camera);
|
||||
AbsoluteOrientation(X_world, X_camera, &Rs[0], &ts[0]);
|
||||
rmse(0) = RootMeanSquareError(x_camera, X_world, K, Rs[0], ts[0]);
|
||||
} else {
|
||||
LOG(ERROR) << "First approximation of beta not good enough.";
|
||||
ts[0].setZero();
|
||||
rmse(0) = std::numeric_limits<double>::max();
|
||||
}
|
||||
|
||||
// Find the second possible solution for R, t corresponding to:
|
||||
// Betas = [b00 b01 b11 b02 b12 b22 b03 b13 b23 b33]
|
||||
// Betas_approx_2 = [b00 b01 b11]
|
||||
betas.setZero();
|
||||
Eigen::Matrix<double, 6, 3> l_6x3;
|
||||
l_6x3 = L.block(0, 0, 6, 3);
|
||||
Eigen::JacobiSVD<Mat> svdOfL3(l_6x3,
|
||||
Eigen::ComputeFullU | Eigen::ComputeFullV);
|
||||
Vec3 b3 = svdOfL3.solve(rho);
|
||||
VLOG(2) << " rho = " << rho;
|
||||
VLOG(2) << " l_6x3 * b3 = " << l_6x3 * b3;
|
||||
if ((l_6x3 * b3).isApprox(rho, kSuccessThreshold)) {
|
||||
if (b3(0) < 0) {
|
||||
betas(0) = std::sqrt(-b3(0));
|
||||
betas(1) = (b3(2) < 0) ? std::sqrt(-b3(2)) : 0;
|
||||
} else {
|
||||
betas(0) = std::sqrt(b3(0));
|
||||
betas(1) = (b3(2) > 0) ? std::sqrt(b3(2)) : 0;
|
||||
}
|
||||
if (b3(1) < 0) {
|
||||
betas(0) = -betas(0);
|
||||
}
|
||||
betas(2) = 0;
|
||||
betas(3) = 0;
|
||||
ComputePointsCoordinatesInCameraFrame(alphas, betas, u2, &X_camera);
|
||||
AbsoluteOrientation(X_world, X_camera, &Rs[1], &ts[1]);
|
||||
rmse(1) = RootMeanSquareError(x_camera, X_world, K, Rs[1], ts[1]);
|
||||
} else {
|
||||
LOG(ERROR) << "Second approximation of beta not good enough.";
|
||||
ts[1].setZero();
|
||||
rmse(1) = std::numeric_limits<double>::max();
|
||||
}
|
||||
|
||||
// Find the third possible solution for R, t corresponding to:
|
||||
// Betas = [b00 b01 b11 b02 b12 b22 b03 b13 b23 b33]
|
||||
// Betas_approx_3 = [b00 b01 b11 b02 b12]
|
||||
betas.setZero();
|
||||
Eigen::Matrix<double, 6, 5> l_6x5;
|
||||
l_6x5 = L.block(0, 0, 6, 5);
|
||||
Eigen::JacobiSVD<Mat> svdOfL5(l_6x5,
|
||||
Eigen::ComputeFullU | Eigen::ComputeFullV);
|
||||
Vec5 b5 = svdOfL5.solve(rho);
|
||||
if ((l_6x5 * b5).isApprox(rho, kSuccessThreshold)) {
|
||||
if (b5(0) < 0) {
|
||||
betas(0) = std::sqrt(-b5(0));
|
||||
if (b5(2) < 0) {
|
||||
betas(1) = std::sqrt(-b5(2));
|
||||
} else {
|
||||
b5(2) = 0;
|
||||
}
|
||||
} else {
|
||||
betas(0) = std::sqrt(b5(0));
|
||||
if (b5(2) > 0) {
|
||||
betas(1) = std::sqrt(b5(2));
|
||||
} else {
|
||||
b5(2) = 0;
|
||||
}
|
||||
}
|
||||
if (b5(1) < 0) {
|
||||
betas(0) = -betas(0);
|
||||
}
|
||||
betas(2) = b5(3) / betas(0);
|
||||
betas(3) = 0;
|
||||
ComputePointsCoordinatesInCameraFrame(alphas, betas, u2, &X_camera);
|
||||
AbsoluteOrientation(X_world, X_camera, &Rs[2], &ts[2]);
|
||||
rmse(2) = RootMeanSquareError(x_camera, X_world, K, Rs[2], ts[2]);
|
||||
} else {
|
||||
LOG(ERROR) << "Third approximation of beta not good enough.";
|
||||
ts[2].setZero();
|
||||
rmse(2) = std::numeric_limits<double>::max();
|
||||
}
|
||||
|
||||
// Finally, with all three solutions, select the (R, t) with the best RMSE.
|
||||
VLOG(2) << "RMSE for solution 0: " << rmse(0);
|
||||
VLOG(2) << "RMSE for solution 1: " << rmse(1);
|
||||
VLOG(2) << "RMSE for solution 2: " << rmse(2);
|
||||
size_t n = 0;
|
||||
if (rmse(1) < rmse(0)) {
|
||||
n = 1;
|
||||
}
|
||||
if (rmse(2) < rmse(n)) {
|
||||
n = 2;
|
||||
}
|
||||
if (rmse(n) == std::numeric_limits<double>::max()) {
|
||||
LOG(ERROR) << "All three possibilities failed. Reporting failure.";
|
||||
return false;
|
||||
}
|
||||
|
||||
VLOG(1) << "RMSE for best solution #" << n << ": " << rmse(n);
|
||||
*R = Rs[n];
|
||||
*t = ts[n];
|
||||
|
||||
// TODO(julien): Improve the solutions with non-linear refinement.
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
Straight from the paper:
|
||||
http://www.diegm.uniud.it/fusiello/papers/3dimpvt12-b.pdf
|
||||
|
||||
function [R T] = ppnp(P,S,tol)
|
||||
% input
|
||||
% P : matrix (nx3) image coordinates in camera reference [u v 1]
|
||||
% S : matrix (nx3) coordinates in world reference [X Y Z]
|
||||
% tol: exit threshold
|
||||
%
|
||||
% output
|
||||
% R : matrix (3x3) rotation (world-to-camera)
|
||||
% T : vector (3x1) translation (world-to-camera)
|
||||
%
|
||||
n = size(P,1);
|
||||
Z = zeros(n);
|
||||
e = ones(n,1);
|
||||
A = eye(n)-((e*e’)./n);
|
||||
II = e./n;
|
||||
err = +Inf;
|
||||
E_old = 1000*ones(n,3);
|
||||
while err>tol
|
||||
[U,˜,V] = svd(P’*Z*A*S);
|
||||
VT = V’;
|
||||
R=U*[1 0 0; 0 1 0; 0 0 det(U*VT)]*VT;
|
||||
PR = P*R;
|
||||
c = (S-Z*PR)’*II;
|
||||
Y = S-e*c’;
|
||||
Zmindiag = diag(PR*Y’)./(sum(P.*P,2));
|
||||
Zmindiag(Zmindiag<0)=0;
|
||||
Z = diag(Zmindiag);
|
||||
E = Y-Z*PR;
|
||||
err = norm(E-E_old,’fro’);
|
||||
E_old = E;
|
||||
end
|
||||
T = -R*c;
|
||||
end
|
||||
|
||||
*/
|
||||
// TODO(keir): Re-do all the variable names and add comments matching the paper.
|
||||
// This implementation has too much of the terseness of the original. On the
|
||||
// other hand, it did work on the first try.
|
||||
bool EuclideanResectionPPnP(const Mat2X& x_camera,
|
||||
const Mat3X& X_world,
|
||||
Mat3* R,
|
||||
Vec3* t) {
|
||||
int n = x_camera.cols();
|
||||
Mat Z = Mat::Zero(n, n);
|
||||
Vec e = Vec::Ones(n);
|
||||
Mat A = Mat::Identity(n, n) - (e * e.transpose() / n);
|
||||
Vec II = e / n;
|
||||
|
||||
Mat P(n, 3);
|
||||
P.col(0) = x_camera.row(0);
|
||||
P.col(1) = x_camera.row(1);
|
||||
P.col(2).setConstant(1.0);
|
||||
|
||||
Mat S = X_world.transpose();
|
||||
|
||||
double error = std::numeric_limits<double>::infinity();
|
||||
Mat E_old = 1000 * Mat::Ones(n, 3);
|
||||
|
||||
Vec3 c;
|
||||
Mat E(n, 3);
|
||||
|
||||
int iteration = 0;
|
||||
double tolerance = 1e-5;
|
||||
// TODO(keir): The limit of 100 can probably be reduced, but this will require
|
||||
// some investigation.
|
||||
while (error > tolerance && iteration < 100) {
|
||||
Mat3 tmp = P.transpose() * Z * A * S;
|
||||
Eigen::JacobiSVD<Mat3> svd(tmp, Eigen::ComputeFullU | Eigen::ComputeFullV);
|
||||
Mat3 U = svd.matrixU();
|
||||
Mat3 VT = svd.matrixV().transpose();
|
||||
Vec3 s;
|
||||
s << 1, 1, (U * VT).determinant();
|
||||
*R = U * s.asDiagonal() * VT;
|
||||
Mat PR = P * *R; // n x 3
|
||||
c = (S - Z * PR).transpose() * II;
|
||||
Mat Y = S - e * c.transpose(); // n x 3
|
||||
Vec Zmindiag = (PR * Y.transpose())
|
||||
.diagonal()
|
||||
.cwiseQuotient(P.rowwise().squaredNorm());
|
||||
for (int i = 0; i < n; ++i) {
|
||||
Zmindiag[i] = std::max(Zmindiag[i], 0.0);
|
||||
}
|
||||
Z = Zmindiag.asDiagonal();
|
||||
E = Y - Z * PR;
|
||||
error = (E - E_old).norm();
|
||||
LG << "PPnP error(" << (iteration++) << "): " << error;
|
||||
E_old = E;
|
||||
}
|
||||
*t = -*R * c;
|
||||
|
||||
// TODO(keir): Figure out what the failure cases are. Is it too many
|
||||
// iterations? Spend some time going through the math figuring out if there
|
||||
// is some way to detect that the algorithm is going crazy, and return false.
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace euclidean_resection
|
||||
} // namespace libmv
|
||||
149
blender-5.2.0/intern/libmv/libmv/multiview/euclidean_resection.h
Normal file
149
blender-5.2.0/intern/libmv/libmv/multiview/euclidean_resection.h
Normal file
@@ -0,0 +1,149 @@
|
||||
// Copyright (c) 2010 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.
|
||||
|
||||
#ifndef LIBMV_MULTIVIEW_EUCLIDEAN_RESECTION_H_
|
||||
#define LIBMV_MULTIVIEW_EUCLIDEAN_RESECTION_H_
|
||||
|
||||
#include "libmv/multiview/projection.h"
|
||||
#include "libmv/numeric/numeric.h"
|
||||
|
||||
namespace libmv {
|
||||
namespace euclidean_resection {
|
||||
|
||||
enum ResectionMethod {
|
||||
RESECTION_ANSAR_DANIILIDIS,
|
||||
|
||||
// The "EPnP" algorithm by Lepetit et al.
|
||||
// http://cvlab.epfl.ch/~lepetit/papers/lepetit_ijcv08.pdf
|
||||
RESECTION_EPNP,
|
||||
|
||||
// The Procrustes PNP algorithm ("PPnP")
|
||||
// http://www.diegm.uniud.it/fusiello/papers/3dimpvt12-b.pdf
|
||||
RESECTION_PPNP
|
||||
};
|
||||
|
||||
/**
|
||||
* Computes the extrinsic parameters, R and t for a calibrated camera
|
||||
* from 4 or more 3D points and their normalized images.
|
||||
*
|
||||
* \param x_camera Image points in normalized camera coordinates e.g. x_camera
|
||||
* = inv(K) * x_image.
|
||||
* \param X_world 3D points in the world coordinate system
|
||||
* \param R Solution for the camera rotation matrix
|
||||
* \param t Solution for the camera translation vector
|
||||
* \param method The resection method to use.
|
||||
*/
|
||||
bool EuclideanResection(const Mat2X& x_camera,
|
||||
const Mat3X& X_world,
|
||||
Mat3* R,
|
||||
Vec3* t,
|
||||
ResectionMethod method = RESECTION_EPNP);
|
||||
|
||||
/**
|
||||
* Computes the extrinsic parameters, R and t for a calibrated camera
|
||||
* from 4 or more 3D points and their images.
|
||||
*
|
||||
* \param x_image Image points in non-normalized image coordinates. The
|
||||
* coordates are laid out one per row. The matrix can be Nx2
|
||||
* or Nx3 for euclidean or homogenous 2D coordinates.
|
||||
* \param X_world 3D points in the world coordinate system
|
||||
* \param K Intrinsic parameters camera matrix
|
||||
* \param R Solution for the camera rotation matrix
|
||||
* \param t Solution for the camera translation vector
|
||||
* \param method Resection method
|
||||
*/
|
||||
bool EuclideanResection(const Mat& x_image,
|
||||
const Mat3X& X_world,
|
||||
const Mat3& K,
|
||||
Mat3* R,
|
||||
Vec3* t,
|
||||
ResectionMethod method = RESECTION_EPNP);
|
||||
|
||||
/**
|
||||
* The absolute orientation algorithm recovers the transformation between a set
|
||||
* of 3D points, X and Xp such that:
|
||||
*
|
||||
* Xp = R*X + t
|
||||
*
|
||||
* The recovery of the absolute orientation is implemented after this article:
|
||||
* Horn, Hilden, "Closed-form solution of absolute orientation using
|
||||
* orthonormal matrices"
|
||||
*/
|
||||
void AbsoluteOrientation(const Mat3X& X, const Mat3X& Xp, Mat3* R, Vec3* t);
|
||||
|
||||
/**
|
||||
* Computes the extrinsic parameters, R and t for a calibrated camera from 4 or
|
||||
* more 3D points and their images.
|
||||
*
|
||||
* \param x_camera Image points in normalized camera coordinates, e.g.
|
||||
* x_camera=inv(K)*x_image
|
||||
* \param X_world 3D points in the world coordinate system
|
||||
* \param R Solution for the camera rotation matrix
|
||||
* \param t Solution for the camera translation vector
|
||||
*
|
||||
* This is the algorithm described in: "Linear Pose Estimation from Points or
|
||||
* Lines", by Ansar, A. and Daniilidis, PAMI 2003. vol. 25, no. 5.
|
||||
*/
|
||||
void EuclideanResectionAnsarDaniilidis(const Mat2X& x_camera,
|
||||
const Mat3X& X_world,
|
||||
Mat3* R,
|
||||
Vec3* t);
|
||||
/**
|
||||
* Computes the extrinsic parameters, R and t for a calibrated camera from 4 or
|
||||
* more 3D points and their images.
|
||||
*
|
||||
* \param x_camera Image points in normalized camera coordinates,
|
||||
* e.g. x_camera = inv(K) * x_image
|
||||
* \param X_world 3D points in the world coordinate system
|
||||
* \param R Solution for the camera rotation matrix
|
||||
* \param t Solution for the camera translation vector
|
||||
*
|
||||
* This is the algorithm described in:
|
||||
* "{EP$n$P: An Accurate $O(n)$ Solution to the P$n$P Problem", by V. Lepetit
|
||||
* and F. Moreno-Noguer and P. Fua, IJCV 2009. vol. 81, no. 2
|
||||
* \note the non-linear optimization is not implemented here.
|
||||
*/
|
||||
bool EuclideanResectionEPnP(const Mat2X& x_camera,
|
||||
const Mat3X& X_world,
|
||||
Mat3* R,
|
||||
Vec3* t);
|
||||
|
||||
/**
|
||||
* Computes the extrinsic parameters, R and t for a calibrated camera from 4 or
|
||||
* more 3D points and their images.
|
||||
*
|
||||
* \param x_camera Image points in normalized camera coordinates,
|
||||
* e.g. x_camera = inv(K) * x_image
|
||||
* \param X_world 3D points in the world coordinate system
|
||||
* \param R Solution for the camera rotation matrix
|
||||
* \param t Solution for the camera translation vector
|
||||
*
|
||||
* Straight from the paper:
|
||||
* http://www.diegm.uniud.it/fusiello/papers/3dimpvt12-b.pdf
|
||||
*/
|
||||
bool EuclideanResectionPPnP(const Mat2X& x_camera,
|
||||
const Mat3X& X_world,
|
||||
Mat3* R,
|
||||
Vec3* t);
|
||||
|
||||
} // namespace euclidean_resection
|
||||
} // namespace libmv
|
||||
|
||||
#endif /* LIBMV_MULTIVIEW_EUCLIDEAN_RESECTION_H_ */
|
||||
@@ -0,0 +1,251 @@
|
||||
// Copyright (c) 2009 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/multiview/euclidean_resection.h"
|
||||
#include "libmv/logging/logging.h"
|
||||
#include "libmv/multiview/projection.h"
|
||||
#include "libmv/numeric/numeric.h"
|
||||
#include "testing/testing.h"
|
||||
|
||||
using namespace libmv::euclidean_resection;
|
||||
using namespace libmv;
|
||||
|
||||
// Generates all necessary inputs and expected outputs for EuclideanResection.
|
||||
static void CreateCameraSystem(const Mat3& KK,
|
||||
const Mat3X& x_image,
|
||||
const Vec& X_distances,
|
||||
const Mat3& R_input,
|
||||
const Vec3& T_input,
|
||||
Mat2X* x_camera,
|
||||
Mat3X* X_world,
|
||||
Mat3* R_expected,
|
||||
Vec3* T_expected) {
|
||||
int num_points = x_image.cols();
|
||||
|
||||
Mat3X x_unit_cam(3, num_points);
|
||||
x_unit_cam = KK.inverse() * x_image;
|
||||
|
||||
// Create normalized camera coordinates to be used as an input to the PnP
|
||||
// function, instead of using NormalizeColumnVectors(&x_unit_cam).
|
||||
*x_camera = x_unit_cam.block(0, 0, 2, num_points);
|
||||
for (int i = 0; i < num_points; ++i) {
|
||||
x_unit_cam.col(i).normalize();
|
||||
}
|
||||
|
||||
// Create the 3D points in the camera system.
|
||||
Mat X_camera(3, num_points);
|
||||
for (int i = 0; i < num_points; ++i) {
|
||||
X_camera.col(i) = X_distances(i) * x_unit_cam.col(i);
|
||||
}
|
||||
|
||||
// Apply the transformation to the camera 3D points
|
||||
Mat translation_matrix(3, num_points);
|
||||
translation_matrix.row(0).setConstant(T_input(0));
|
||||
translation_matrix.row(1).setConstant(T_input(1));
|
||||
translation_matrix.row(2).setConstant(T_input(2));
|
||||
|
||||
*X_world = R_input * X_camera + translation_matrix;
|
||||
|
||||
// Create the expected result for comparison.
|
||||
*R_expected = R_input.transpose();
|
||||
*T_expected = *R_expected * (-T_input);
|
||||
};
|
||||
|
||||
TEST(AbsoluteOrientation, QuaternionSolution) {
|
||||
int num_points = 4;
|
||||
Mat X;
|
||||
Mat Xp;
|
||||
X = 100 * Mat::Random(3, num_points);
|
||||
|
||||
// Create a random translation and rotation.
|
||||
Mat3 R_input;
|
||||
R_input = Eigen::AngleAxisd(rand(), Eigen::Vector3d::UnitZ()) *
|
||||
Eigen::AngleAxisd(rand(), Eigen::Vector3d::UnitY()) *
|
||||
Eigen::AngleAxisd(rand(), Eigen::Vector3d::UnitZ());
|
||||
|
||||
Vec3 t_input;
|
||||
t_input.setRandom();
|
||||
t_input = 100 * t_input;
|
||||
|
||||
Mat translation_matrix(3, num_points);
|
||||
translation_matrix.row(0).setConstant(t_input(0));
|
||||
translation_matrix.row(1).setConstant(t_input(1));
|
||||
translation_matrix.row(2).setConstant(t_input(2));
|
||||
|
||||
// Create the transformed 3D points Xp as Xp = R * X + t.
|
||||
Xp = R_input * X + translation_matrix;
|
||||
|
||||
// Output variables.
|
||||
Mat3 R;
|
||||
Vec3 t;
|
||||
|
||||
AbsoluteOrientation(X, Xp, &R, &t);
|
||||
|
||||
EXPECT_MATRIX_NEAR(t, t_input, 1e-6);
|
||||
EXPECT_MATRIX_NEAR(R, R_input, 1e-8);
|
||||
}
|
||||
|
||||
TEST(EuclideanResection, Points4KnownImagePointsRandomTranslationRotation) {
|
||||
// In this test only the translation and rotation are random. The image
|
||||
// points are selected from a real case and are well conditioned.
|
||||
Vec2i image_dimensions;
|
||||
image_dimensions << 1600, 1200;
|
||||
|
||||
Mat3 KK;
|
||||
// clang-format off
|
||||
KK << 2796, 0, 804,
|
||||
0 , 2796, 641,
|
||||
0, 0, 1;
|
||||
// clang-format on
|
||||
|
||||
// The real image points.
|
||||
int num_points = 4;
|
||||
Mat3X x_image(3, num_points);
|
||||
// clang-format off
|
||||
x_image << 1164.06, 734.948, 749.599, 430.727,
|
||||
681.386, 844.59, 496.315, 580.775,
|
||||
1, 1, 1, 1;
|
||||
// clang-format on
|
||||
|
||||
// A vector of the 4 distances to the 3D points.
|
||||
Vec X_distances = 100 * Vec::Random(num_points).array().abs();
|
||||
|
||||
// Create the random camera motion R and t that resection should recover.
|
||||
Mat3 R_input;
|
||||
R_input = Eigen::AngleAxisd(rand(), Eigen::Vector3d::UnitZ()) *
|
||||
Eigen::AngleAxisd(rand(), Eigen::Vector3d::UnitY()) *
|
||||
Eigen::AngleAxisd(rand(), Eigen::Vector3d::UnitZ());
|
||||
|
||||
Vec3 T_input;
|
||||
T_input.setRandom();
|
||||
T_input = 100 * T_input;
|
||||
|
||||
// Create the camera system, also getting the expected result of the
|
||||
// transformation.
|
||||
Mat3 R_expected;
|
||||
Vec3 T_expected;
|
||||
Mat3X X_world;
|
||||
Mat2X x_camera;
|
||||
CreateCameraSystem(KK,
|
||||
x_image,
|
||||
X_distances,
|
||||
R_input,
|
||||
T_input,
|
||||
&x_camera,
|
||||
&X_world,
|
||||
&R_expected,
|
||||
&T_expected);
|
||||
|
||||
// Finally, run the code under test.
|
||||
Mat3 R_output;
|
||||
Vec3 T_output;
|
||||
EuclideanResection(
|
||||
x_camera, X_world, &R_output, &T_output, RESECTION_ANSAR_DANIILIDIS);
|
||||
|
||||
EXPECT_MATRIX_NEAR(T_output, T_expected, 1e-5);
|
||||
EXPECT_MATRIX_NEAR(R_output, R_expected, 1e-7);
|
||||
|
||||
// For now, the EPnP doesn't have a non-linear optimization step and so is
|
||||
// not precise enough with only 4 points.
|
||||
//
|
||||
// TODO(jmichot): Reenable this test when there is nonlinear refinement.
|
||||
#if 0
|
||||
R_output.setIdentity();
|
||||
T_output.setZero();
|
||||
|
||||
EuclideanResection(x_camera, X_world,
|
||||
&R_output, &T_output,
|
||||
RESECTION_EPNP);
|
||||
|
||||
EXPECT_MATRIX_NEAR(T_output, T_expected, 1e-5);
|
||||
EXPECT_MATRIX_NEAR(R_output, R_expected, 1e-7);*/
|
||||
#endif
|
||||
}
|
||||
|
||||
// TODO(jmichot): Reduce the code duplication here with the code above.
|
||||
TEST(EuclideanResection, Points6AllRandomInput) {
|
||||
Mat3 KK;
|
||||
// clang-format off
|
||||
KK << 2796, 0, 804,
|
||||
0 , 2796, 641,
|
||||
0, 0, 1;
|
||||
// clang-format on
|
||||
|
||||
// Create random image points for a 1600x1200 image.
|
||||
int w = 1600;
|
||||
int h = 1200;
|
||||
int num_points = 6;
|
||||
Mat3X x_image(3, num_points);
|
||||
x_image.row(0) = w * Vec::Random(num_points).array().abs();
|
||||
x_image.row(1) = h * Vec::Random(num_points).array().abs();
|
||||
x_image.row(2).setOnes();
|
||||
|
||||
// Normalized camera coordinates to be used as an input to the PnP function.
|
||||
Mat2X x_camera;
|
||||
Vec X_distances = 100 * Vec::Random(num_points).array().abs();
|
||||
|
||||
// Create the random camera motion R and t that resection should recover.
|
||||
Mat3 R_input;
|
||||
R_input = Eigen::AngleAxisd(rand(), Eigen::Vector3d::UnitZ()) *
|
||||
Eigen::AngleAxisd(rand(), Eigen::Vector3d::UnitY()) *
|
||||
Eigen::AngleAxisd(rand(), Eigen::Vector3d::UnitZ());
|
||||
|
||||
Vec3 T_input;
|
||||
T_input.setRandom();
|
||||
T_input = 100 * T_input;
|
||||
|
||||
// Create the camera system.
|
||||
Mat3 R_expected;
|
||||
Vec3 T_expected;
|
||||
Mat3X X_world;
|
||||
CreateCameraSystem(KK,
|
||||
x_image,
|
||||
X_distances,
|
||||
R_input,
|
||||
T_input,
|
||||
&x_camera,
|
||||
&X_world,
|
||||
&R_expected,
|
||||
&T_expected);
|
||||
|
||||
// Test each of the resection methods.
|
||||
{
|
||||
Mat3 R_output;
|
||||
Vec3 T_output;
|
||||
EuclideanResection(
|
||||
x_camera, X_world, &R_output, &T_output, RESECTION_ANSAR_DANIILIDIS);
|
||||
EXPECT_MATRIX_NEAR(T_output, T_expected, 1e-5);
|
||||
EXPECT_MATRIX_NEAR(R_output, R_expected, 1e-7);
|
||||
}
|
||||
{
|
||||
Mat3 R_output;
|
||||
Vec3 T_output;
|
||||
EuclideanResection(x_camera, X_world, &R_output, &T_output, RESECTION_EPNP);
|
||||
EXPECT_MATRIX_NEAR(T_output, T_expected, 1e-5);
|
||||
EXPECT_MATRIX_NEAR(R_output, R_expected, 1e-7);
|
||||
}
|
||||
{
|
||||
Mat3 R_output;
|
||||
Vec3 T_output;
|
||||
EuclideanResection(x_image, X_world, KK, &R_output, &T_output);
|
||||
EXPECT_MATRIX_NEAR(T_output, T_expected, 1e-5);
|
||||
EXPECT_MATRIX_NEAR(R_output, R_expected, 1e-7);
|
||||
}
|
||||
}
|
||||
541
blender-5.2.0/intern/libmv/libmv/multiview/fundamental.cc
Normal file
541
blender-5.2.0/intern/libmv/libmv/multiview/fundamental.cc
Normal file
@@ -0,0 +1,541 @@
|
||||
// Copyright (c) 2007, 2008 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/multiview/fundamental.h"
|
||||
|
||||
#include "ceres/ceres.h"
|
||||
#include "libmv/logging/logging.h"
|
||||
#include "libmv/multiview/conditioning.h"
|
||||
#include "libmv/multiview/projection.h"
|
||||
#include "libmv/multiview/triangulation.h"
|
||||
#include "libmv/numeric/numeric.h"
|
||||
#include "libmv/numeric/poly.h"
|
||||
|
||||
namespace libmv {
|
||||
|
||||
static void EliminateRow(const Mat34& P, int row, Mat* X) {
|
||||
X->resize(2, 4);
|
||||
|
||||
int first_row = (row + 1) % 3;
|
||||
int second_row = (row + 2) % 3;
|
||||
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
(*X)(0, i) = P(first_row, i);
|
||||
(*X)(1, i) = P(second_row, i);
|
||||
}
|
||||
}
|
||||
|
||||
void ProjectionsFromFundamental(const Mat3& F, Mat34* P1, Mat34* P2) {
|
||||
*P1 << Mat3::Identity(), Vec3::Zero();
|
||||
Vec3 e2;
|
||||
Mat3 Ft = F.transpose();
|
||||
Nullspace(&Ft, &e2);
|
||||
*P2 << CrossProductMatrix(e2) * F, e2;
|
||||
}
|
||||
|
||||
// Addapted from vgg_F_from_P.
|
||||
void FundamentalFromProjections(const Mat34& P1, const Mat34& P2, Mat3* F) {
|
||||
Mat X[3];
|
||||
Mat Y[3];
|
||||
Mat XY;
|
||||
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
EliminateRow(P1, i, X + i);
|
||||
EliminateRow(P2, i, Y + i);
|
||||
}
|
||||
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
for (int j = 0; j < 3; ++j) {
|
||||
VerticalStack(X[j], Y[i], &XY);
|
||||
(*F)(i, j) = XY.determinant();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// HZ 11.1 pag.279 (x1 = x, x2 = x')
|
||||
// http://www.cs.unc.edu/~marc/tutorial/node54.html
|
||||
static double EightPointSolver(const Mat& x1, const Mat& x2, Mat3* F) {
|
||||
DCHECK_EQ(x1.rows(), 2);
|
||||
DCHECK_GE(x1.cols(), 8);
|
||||
DCHECK_EQ(x1.rows(), x2.rows());
|
||||
DCHECK_EQ(x1.cols(), x2.cols());
|
||||
|
||||
int n = x1.cols();
|
||||
Mat A(n, 9);
|
||||
for (int i = 0; i < n; ++i) {
|
||||
A(i, 0) = x2(0, i) * x1(0, i);
|
||||
A(i, 1) = x2(0, i) * x1(1, i);
|
||||
A(i, 2) = x2(0, i);
|
||||
A(i, 3) = x2(1, i) * x1(0, i);
|
||||
A(i, 4) = x2(1, i) * x1(1, i);
|
||||
A(i, 5) = x2(1, i);
|
||||
A(i, 6) = x1(0, i);
|
||||
A(i, 7) = x1(1, i);
|
||||
A(i, 8) = 1;
|
||||
}
|
||||
|
||||
Vec9 f;
|
||||
double smaller_singular_value = Nullspace(&A, &f);
|
||||
*F = Map<RMat3>(f.data());
|
||||
return smaller_singular_value;
|
||||
}
|
||||
|
||||
// HZ 11.1.1 pag.280
|
||||
void EnforceFundamentalRank2Constraint(Mat3* F) {
|
||||
Eigen::JacobiSVD<Mat3> USV(*F, Eigen::ComputeFullU | Eigen::ComputeFullV);
|
||||
Vec3 d = USV.singularValues();
|
||||
d(2) = 0.0;
|
||||
*F = USV.matrixU() * d.asDiagonal() * USV.matrixV().transpose();
|
||||
}
|
||||
|
||||
// HZ 11.2 pag.281 (x1 = x, x2 = x')
|
||||
double NormalizedEightPointSolver(const Mat& x1, const Mat& x2, Mat3* F) {
|
||||
DCHECK_EQ(x1.rows(), 2);
|
||||
DCHECK_GE(x1.cols(), 8);
|
||||
DCHECK_EQ(x1.rows(), x2.rows());
|
||||
DCHECK_EQ(x1.cols(), x2.cols());
|
||||
|
||||
// Normalize the data.
|
||||
Mat3 T1, T2;
|
||||
PreconditionerFromPoints(x1, &T1);
|
||||
PreconditionerFromPoints(x2, &T2);
|
||||
Mat x1_normalized, x2_normalized;
|
||||
ApplyTransformationToPoints(x1, T1, &x1_normalized);
|
||||
ApplyTransformationToPoints(x2, T2, &x2_normalized);
|
||||
|
||||
// Estimate the fundamental matrix.
|
||||
double smaller_singular_value =
|
||||
EightPointSolver(x1_normalized, x2_normalized, F);
|
||||
EnforceFundamentalRank2Constraint(F);
|
||||
|
||||
// Denormalize the fundamental matrix.
|
||||
*F = T2.transpose() * (*F) * T1;
|
||||
|
||||
return smaller_singular_value;
|
||||
}
|
||||
|
||||
// Seven-point algorithm.
|
||||
// http://www.cs.unc.edu/~marc/tutorial/node55.html
|
||||
double FundamentalFrom7CorrespondencesLinear(const Mat& x1,
|
||||
const Mat& x2,
|
||||
std::vector<Mat3>* F) {
|
||||
DCHECK_EQ(x1.rows(), 2);
|
||||
DCHECK_EQ(x1.cols(), 7);
|
||||
DCHECK_EQ(x1.rows(), x2.rows());
|
||||
DCHECK_EQ(x2.cols(), x2.cols());
|
||||
|
||||
// Build a 9 x n matrix from point matches, where each row is equivalent to
|
||||
// the equation x'T*F*x = 0 for a single correspondence pair (x', x). The
|
||||
// domain of the matrix is a 9 element vector corresponding to F. The
|
||||
// nullspace should be rank two; the two dimensions correspond to the set of
|
||||
// F matrices satisfying the epipolar geometry.
|
||||
Matrix<double, 7, 9> A;
|
||||
for (int ii = 0; ii < 7; ++ii) {
|
||||
A(ii, 0) = x1(0, ii) * x2(0, ii); // 0 represents x coords,
|
||||
A(ii, 1) = x1(1, ii) * x2(0, ii); // 1 represents y coords.
|
||||
A(ii, 2) = x2(0, ii);
|
||||
A(ii, 3) = x1(0, ii) * x2(1, ii);
|
||||
A(ii, 4) = x1(1, ii) * x2(1, ii);
|
||||
A(ii, 5) = x2(1, ii);
|
||||
A(ii, 6) = x1(0, ii);
|
||||
A(ii, 7) = x1(1, ii);
|
||||
A(ii, 8) = 1.0;
|
||||
}
|
||||
|
||||
// Find the two F matrices in the nullspace of A.
|
||||
Vec9 f1, f2;
|
||||
double s = Nullspace2(&A, &f1, &f2);
|
||||
Mat3 F1 = Map<RMat3>(f1.data());
|
||||
Mat3 F2 = Map<RMat3>(f2.data());
|
||||
|
||||
// Then, use the condition det(F) = 0 to determine F. In other words, solve
|
||||
// det(F1 + a*F2) = 0 for a.
|
||||
double a = F1(0, 0), j = F2(0, 0);
|
||||
double b = F1(0, 1), k = F2(0, 1);
|
||||
double c = F1(0, 2), l = F2(0, 2);
|
||||
double d = F1(1, 0), m = F2(1, 0);
|
||||
double e = F1(1, 1), n = F2(1, 1);
|
||||
double f = F1(1, 2), o = F2(1, 2);
|
||||
double g = F1(2, 0), p = F2(2, 0);
|
||||
double h = F1(2, 1), q = F2(2, 1);
|
||||
double i = F1(2, 2), r = F2(2, 2);
|
||||
|
||||
// Run fundamental_7point_coeffs.py to get the below coefficients.
|
||||
// The coefficients are in ascending powers of alpha, i.e. P[N]*x^N.
|
||||
double P[4] = {
|
||||
a * e * i + b * f * g + c * d * h - a * f * h - b * d * i - c * e * g,
|
||||
a * e * r + a * i * n + b * f * p + b * g * o + c * d * q + c * h * m +
|
||||
d * h * l + e * i * j + f * g * k - a * f * q - a * h * o -
|
||||
b * d * r - b * i * m - c * e * p - c * g * n - d * i * k -
|
||||
e * g * l - f * h * j,
|
||||
a * n * r + b * o * p + c * m * q + d * l * q + e * j * r + f * k * p +
|
||||
g * k * o + h * l * m + i * j * n - a * o * q - b * m * r -
|
||||
c * n * p - d * k * r - e * l * p - f * j * q - g * l * n -
|
||||
h * j * o - i * k * m,
|
||||
j * n * r + k * o * p + l * m * q - j * o * q - k * m * r - l * n * p,
|
||||
};
|
||||
|
||||
// Solve for the roots of P[3]*x^3 + P[2]*x^2 + P[1]*x + P[0] = 0.
|
||||
double roots[3];
|
||||
int num_roots = SolveCubicPolynomial(P, roots);
|
||||
|
||||
// Build the fundamental matrix for each solution.
|
||||
for (int kk = 0; kk < num_roots; ++kk) {
|
||||
F->push_back(F1 + roots[kk] * F2);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
double FundamentalFromCorrespondences7Point(const Mat& x1,
|
||||
const Mat& x2,
|
||||
std::vector<Mat3>* F) {
|
||||
DCHECK_EQ(x1.rows(), 2);
|
||||
DCHECK_GE(x1.cols(), 7);
|
||||
DCHECK_EQ(x1.rows(), x2.rows());
|
||||
DCHECK_EQ(x1.cols(), x2.cols());
|
||||
|
||||
// Normalize the data.
|
||||
Mat3 T1, T2;
|
||||
PreconditionerFromPoints(x1, &T1);
|
||||
PreconditionerFromPoints(x2, &T2);
|
||||
Mat x1_normalized, x2_normalized;
|
||||
ApplyTransformationToPoints(x1, T1, &x1_normalized);
|
||||
ApplyTransformationToPoints(x2, T2, &x2_normalized);
|
||||
|
||||
// Estimate the fundamental matrix.
|
||||
double smaller_singular_value = FundamentalFrom7CorrespondencesLinear(
|
||||
x1_normalized, x2_normalized, &(*F));
|
||||
|
||||
for (int k = 0; k < F->size(); ++k) {
|
||||
Mat3& Fmat = (*F)[k];
|
||||
// Denormalize the fundamental matrix.
|
||||
Fmat = T2.transpose() * Fmat * T1;
|
||||
}
|
||||
return smaller_singular_value;
|
||||
}
|
||||
|
||||
void NormalizeFundamental(const Mat3& F, Mat3* F_normalized) {
|
||||
*F_normalized = F / FrobeniusNorm(F);
|
||||
if ((*F_normalized)(2, 2) < 0) {
|
||||
*F_normalized *= -1;
|
||||
}
|
||||
}
|
||||
|
||||
double SampsonDistance(const Mat& F, const Vec2& x1, const Vec2& x2) {
|
||||
Vec3 x(x1(0), x1(1), 1.0);
|
||||
Vec3 y(x2(0), x2(1), 1.0);
|
||||
|
||||
Vec3 F_x = F * x;
|
||||
Vec3 Ft_y = F.transpose() * y;
|
||||
double y_F_x = y.dot(F_x);
|
||||
|
||||
return Square(y_F_x) /
|
||||
(F_x.head<2>().squaredNorm() + Ft_y.head<2>().squaredNorm());
|
||||
}
|
||||
|
||||
double SymmetricEpipolarDistance(const Mat& F, const Vec2& x1, const Vec2& x2) {
|
||||
Vec3 x(x1(0), x1(1), 1.0);
|
||||
Vec3 y(x2(0), x2(1), 1.0);
|
||||
|
||||
Vec3 F_x = F * x;
|
||||
Vec3 Ft_y = F.transpose() * y;
|
||||
double y_F_x = y.dot(F_x);
|
||||
|
||||
return Square(y_F_x) *
|
||||
(1 / F_x.head<2>().squaredNorm() + 1 / Ft_y.head<2>().squaredNorm());
|
||||
}
|
||||
|
||||
// HZ 9.6 pag 257 (formula 9.12)
|
||||
void EssentialFromFundamental(const Mat3& F,
|
||||
const Mat3& K1,
|
||||
const Mat3& K2,
|
||||
Mat3* E) {
|
||||
*E = K2.transpose() * F * K1;
|
||||
}
|
||||
|
||||
// HZ 9.6 pag 257 (formula 9.12)
|
||||
// Or http://ai.stanford.edu/~birch/projective/node20.html
|
||||
void FundamentalFromEssential(const Mat3& E,
|
||||
const Mat3& K1,
|
||||
const Mat3& K2,
|
||||
Mat3* F) {
|
||||
*F = K2.inverse().transpose() * E * K1.inverse();
|
||||
}
|
||||
|
||||
void RelativeCameraMotion(const Mat3& R1,
|
||||
const Vec3& t1,
|
||||
const Mat3& R2,
|
||||
const Vec3& t2,
|
||||
Mat3* R,
|
||||
Vec3* t) {
|
||||
*R = R2 * R1.transpose();
|
||||
*t = t2 - (*R) * t1;
|
||||
}
|
||||
|
||||
// HZ 9.6 pag 257
|
||||
void EssentialFromRt(
|
||||
const Mat3& R1, const Vec3& t1, const Mat3& R2, const Vec3& t2, Mat3* E) {
|
||||
Mat3 R;
|
||||
Vec3 t;
|
||||
RelativeCameraMotion(R1, t1, R2, t2, &R, &t);
|
||||
Mat3 Tx = CrossProductMatrix(t);
|
||||
*E = Tx * R;
|
||||
}
|
||||
|
||||
// HZ 9.6 pag 259 (Result 9.19)
|
||||
void MotionFromEssential(const Mat3& E,
|
||||
std::vector<Mat3>* Rs,
|
||||
std::vector<Vec3>* ts) {
|
||||
Eigen::JacobiSVD<Mat3> USV(E, Eigen::ComputeFullU | Eigen::ComputeFullV);
|
||||
Mat3 U = USV.matrixU();
|
||||
Mat3 Vt = USV.matrixV().transpose();
|
||||
|
||||
// Last column of U is undetermined since d = (a a 0).
|
||||
if (U.determinant() < 0) {
|
||||
U.col(2) *= -1;
|
||||
}
|
||||
// Last row of Vt is undetermined since d = (a a 0).
|
||||
if (Vt.determinant() < 0) {
|
||||
Vt.row(2) *= -1;
|
||||
}
|
||||
|
||||
Mat3 W;
|
||||
// clang-format off
|
||||
W << 0, -1, 0,
|
||||
1, 0, 0,
|
||||
0, 0, 1;
|
||||
// clang-format on
|
||||
|
||||
Mat3 U_W_Vt = U * W * Vt;
|
||||
Mat3 U_Wt_Vt = U * W.transpose() * Vt;
|
||||
|
||||
Rs->resize(4);
|
||||
(*Rs)[0] = U_W_Vt;
|
||||
(*Rs)[1] = U_W_Vt;
|
||||
(*Rs)[2] = U_Wt_Vt;
|
||||
(*Rs)[3] = U_Wt_Vt;
|
||||
|
||||
ts->resize(4);
|
||||
(*ts)[0] = U.col(2);
|
||||
(*ts)[1] = -U.col(2);
|
||||
(*ts)[2] = U.col(2);
|
||||
(*ts)[3] = -U.col(2);
|
||||
}
|
||||
|
||||
int MotionFromEssentialChooseSolution(const std::vector<Mat3>& Rs,
|
||||
const std::vector<Vec3>& ts,
|
||||
const Mat3& K1,
|
||||
const Vec2& x1,
|
||||
const Mat3& K2,
|
||||
const Vec2& x2) {
|
||||
DCHECK_EQ(4, Rs.size());
|
||||
DCHECK_EQ(4, ts.size());
|
||||
|
||||
Mat34 P1, P2;
|
||||
Mat3 R1;
|
||||
Vec3 t1;
|
||||
R1.setIdentity();
|
||||
t1.setZero();
|
||||
P_From_KRt(K1, R1, t1, &P1);
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
const Mat3& R2 = Rs[i];
|
||||
const Vec3& t2 = ts[i];
|
||||
P_From_KRt(K2, R2, t2, &P2);
|
||||
Vec3 X;
|
||||
TriangulateDLT(P1, x1, P2, x2, &X);
|
||||
double d1 = Depth(R1, t1, X);
|
||||
double d2 = Depth(R2, t2, X);
|
||||
// Test if point is front to the two cameras.
|
||||
if (d1 > 0 && d2 > 0) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool MotionFromEssentialAndCorrespondence(const Mat3& E,
|
||||
const Mat3& K1,
|
||||
const Vec2& x1,
|
||||
const Mat3& K2,
|
||||
const Vec2& x2,
|
||||
Mat3* R,
|
||||
Vec3* t) {
|
||||
std::vector<Mat3> Rs;
|
||||
std::vector<Vec3> ts;
|
||||
MotionFromEssential(E, &Rs, &ts);
|
||||
int solution = MotionFromEssentialChooseSolution(Rs, ts, K1, x1, K2, x2);
|
||||
if (solution >= 0) {
|
||||
*R = Rs[solution];
|
||||
*t = ts[solution];
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void FundamentalToEssential(const Mat3& F, Mat3* E) {
|
||||
Eigen::JacobiSVD<Mat3> svd(F, Eigen::ComputeFullU | Eigen::ComputeFullV);
|
||||
|
||||
// See Hartley & Zisserman page 294, result 11.1, which shows how to get the
|
||||
// closest essential matrix to a matrix that is "almost" an essential matrix.
|
||||
double a = svd.singularValues()(0);
|
||||
double b = svd.singularValues()(1);
|
||||
double s = (a + b) / 2.0;
|
||||
|
||||
LG << "Initial reconstruction's rotation is non-euclidean by "
|
||||
<< (((a - b) / std::max(a, b)) * 100)
|
||||
<< "%; singular values:" << svd.singularValues().transpose();
|
||||
|
||||
Vec3 diag;
|
||||
diag << s, s, 0;
|
||||
|
||||
*E = svd.matrixU() * diag.asDiagonal() * svd.matrixV().transpose();
|
||||
}
|
||||
|
||||
// Default settings for fundamental estimation which should be suitable
|
||||
// for a wide range of use cases.
|
||||
EstimateFundamentalOptions::EstimateFundamentalOptions()
|
||||
: max_num_iterations(50), expected_average_symmetric_distance(1e-16) {
|
||||
}
|
||||
|
||||
namespace {
|
||||
// Cost functor which computes symmetric epipolar distance
|
||||
// used for fundamental matrix refinement.
|
||||
class FundamentalSymmetricEpipolarCostFunctor {
|
||||
public:
|
||||
FundamentalSymmetricEpipolarCostFunctor(const Vec2& x, const Vec2& y)
|
||||
: x_(x), y_(y) {}
|
||||
|
||||
template <typename T>
|
||||
bool operator()(const T* fundamental_parameters, T* residuals) const {
|
||||
typedef Eigen::Matrix<T, 3, 3> Mat3;
|
||||
typedef Eigen::Matrix<T, 3, 1> Vec3;
|
||||
|
||||
Mat3 F(fundamental_parameters);
|
||||
|
||||
Vec3 x(T(x_(0)), T(x_(1)), T(1.0));
|
||||
Vec3 y(T(y_(0)), T(y_(1)), T(1.0));
|
||||
|
||||
Vec3 F_x = F * x;
|
||||
Vec3 Ft_y = F.transpose() * y;
|
||||
T y_F_x = y.dot(F_x);
|
||||
|
||||
residuals[0] = y_F_x * T(1) / F_x.head(2).norm();
|
||||
residuals[1] = y_F_x * T(1) / Ft_y.head(2).norm();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const Mat x_;
|
||||
const Mat y_;
|
||||
};
|
||||
|
||||
// Termination checking callback used for fundamental estimation.
|
||||
// It finished the minimization as soon as actual average of
|
||||
// symmetric epipolar distance is less or equal to the expected
|
||||
// average value.
|
||||
class TerminationCheckingCallback : public ceres::IterationCallback {
|
||||
public:
|
||||
TerminationCheckingCallback(const Mat& x1,
|
||||
const Mat& x2,
|
||||
const EstimateFundamentalOptions& options,
|
||||
Mat3* F)
|
||||
: options_(options), x1_(x1), x2_(x2), F_(F) {}
|
||||
|
||||
virtual ceres::CallbackReturnType operator()(
|
||||
const ceres::IterationSummary& summary) {
|
||||
// If the step wasn't successful, there's nothing to do.
|
||||
if (!summary.step_is_successful) {
|
||||
return ceres::SOLVER_CONTINUE;
|
||||
}
|
||||
|
||||
// Calculate average of symmetric epipolar distance.
|
||||
double average_distance = 0.0;
|
||||
for (int i = 0; i < x1_.cols(); i++) {
|
||||
average_distance = SymmetricEpipolarDistance(*F_, x1_.col(i), x2_.col(i));
|
||||
}
|
||||
average_distance /= x1_.cols();
|
||||
|
||||
if (average_distance <= options_.expected_average_symmetric_distance) {
|
||||
return ceres::SOLVER_TERMINATE_SUCCESSFULLY;
|
||||
}
|
||||
|
||||
return ceres::SOLVER_CONTINUE;
|
||||
}
|
||||
|
||||
private:
|
||||
const EstimateFundamentalOptions& options_;
|
||||
const Mat& x1_;
|
||||
const Mat& x2_;
|
||||
Mat3* F_;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
/* Fundamental transformation estimation. */
|
||||
bool EstimateFundamentalFromCorrespondences(
|
||||
const Mat& x1,
|
||||
const Mat& x2,
|
||||
const EstimateFundamentalOptions& options,
|
||||
Mat3* F) {
|
||||
// Step 1: Algebraic fundamental estimation.
|
||||
|
||||
// Assume algebraic estiation always succeeds,
|
||||
NormalizedEightPointSolver(x1, x2, F);
|
||||
|
||||
LG << "Estimated matrix after algebraic estimation:\n" << *F;
|
||||
|
||||
// Step 2: Refine matrix using Ceres minimizer.
|
||||
ceres::Problem problem;
|
||||
for (int i = 0; i < x1.cols(); i++) {
|
||||
FundamentalSymmetricEpipolarCostFunctor*
|
||||
fundamental_symmetric_epipolar_cost_function =
|
||||
new FundamentalSymmetricEpipolarCostFunctor(x1.col(i), x2.col(i));
|
||||
|
||||
problem.AddResidualBlock(
|
||||
new ceres::AutoDiffCostFunction<FundamentalSymmetricEpipolarCostFunctor,
|
||||
2, // num_residuals
|
||||
9>(
|
||||
fundamental_symmetric_epipolar_cost_function),
|
||||
NULL,
|
||||
F->data());
|
||||
}
|
||||
|
||||
// Configure the solve.
|
||||
ceres::Solver::Options solver_options;
|
||||
solver_options.linear_solver_type = ceres::DENSE_QR;
|
||||
solver_options.max_num_iterations = options.max_num_iterations;
|
||||
solver_options.update_state_every_iteration = true;
|
||||
|
||||
// Terminate if the average symmetric distance is good enough.
|
||||
TerminationCheckingCallback callback(x1, x2, options, F);
|
||||
solver_options.callbacks.push_back(&callback);
|
||||
|
||||
// Run the solve.
|
||||
ceres::Solver::Summary summary;
|
||||
ceres::Solve(solver_options, &problem, &summary);
|
||||
|
||||
VLOG(1) << "Summary:\n" << summary.FullReport();
|
||||
|
||||
LG << "Final refined matrix:\n" << *F;
|
||||
|
||||
return summary.IsSolutionUsable();
|
||||
}
|
||||
|
||||
} // namespace libmv
|
||||
175
blender-5.2.0/intern/libmv/libmv/multiview/fundamental.h
Normal file
175
blender-5.2.0/intern/libmv/libmv/multiview/fundamental.h
Normal file
@@ -0,0 +1,175 @@
|
||||
// Copyright (c) 2007, 2008, 2011 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.
|
||||
|
||||
#ifndef LIBMV_MULTIVIEW_FUNDAMENTAL_H_
|
||||
#define LIBMV_MULTIVIEW_FUNDAMENTAL_H_
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "libmv/numeric/numeric.h"
|
||||
|
||||
namespace libmv {
|
||||
|
||||
void ProjectionsFromFundamental(const Mat3& F, Mat34* P1, Mat34* P2);
|
||||
void FundamentalFromProjections(const Mat34& P1, const Mat34& P2, Mat3* F);
|
||||
|
||||
/**
|
||||
* 7 points (minimal case, points coordinates must be normalized before):
|
||||
*/
|
||||
double FundamentalFrom7CorrespondencesLinear(const Mat& x1,
|
||||
const Mat& x2,
|
||||
std::vector<Mat3>* F);
|
||||
|
||||
/**
|
||||
* 7 points (points coordinates must be in image space):
|
||||
*/
|
||||
double FundamentalFromCorrespondences7Point(const Mat& x1,
|
||||
const Mat& x2,
|
||||
std::vector<Mat3>* F);
|
||||
|
||||
/**
|
||||
* 8 points (points coordinates must be in image space):
|
||||
*/
|
||||
double NormalizedEightPointSolver(const Mat& x1, const Mat& x2, Mat3* F);
|
||||
|
||||
/**
|
||||
* Fundamental matrix utility function:
|
||||
*/
|
||||
void EnforceFundamentalRank2Constraint(Mat3* F);
|
||||
|
||||
void NormalizeFundamental(const Mat3& F, Mat3* F_normalized);
|
||||
|
||||
/**
|
||||
* Approximate squared reprojection errror.
|
||||
*
|
||||
* See page 287 of HZ equation 11.9. This avoids triangulating the point,
|
||||
* relying only on the entries in F.
|
||||
*/
|
||||
double SampsonDistance(const Mat& F, const Vec2& x1, const Vec2& x2);
|
||||
|
||||
/**
|
||||
* Calculates the sum of the distances from the points to the epipolar lines.
|
||||
*
|
||||
* See page 288 of HZ equation 11.10.
|
||||
*/
|
||||
double SymmetricEpipolarDistance(const Mat& F, const Vec2& x1, const Vec2& x2);
|
||||
|
||||
/**
|
||||
* Compute the relative camera motion between two cameras.
|
||||
*
|
||||
* Given the motion parameters of two cameras, computes the motion parameters
|
||||
* of the second one assuming the first one to be at the origin.
|
||||
* If T1 and T2 are the camera motions, the computed relative motion is
|
||||
* T = T2 T1^{-1}
|
||||
*/
|
||||
void RelativeCameraMotion(const Mat3& R1,
|
||||
const Vec3& t1,
|
||||
const Mat3& R2,
|
||||
const Vec3& t2,
|
||||
Mat3* R,
|
||||
Vec3* t);
|
||||
|
||||
void EssentialFromFundamental(const Mat3& F,
|
||||
const Mat3& K1,
|
||||
const Mat3& K2,
|
||||
Mat3* E);
|
||||
|
||||
void FundamentalFromEssential(const Mat3& E,
|
||||
const Mat3& K1,
|
||||
const Mat3& K2,
|
||||
Mat3* F);
|
||||
|
||||
void EssentialFromRt(
|
||||
const Mat3& R1, const Vec3& t1, const Mat3& R2, const Vec3& t2, Mat3* E);
|
||||
|
||||
void MotionFromEssential(const Mat3& E,
|
||||
std::vector<Mat3>* Rs,
|
||||
std::vector<Vec3>* ts);
|
||||
|
||||
/**
|
||||
* Choose one of the four possible motion solutions from an essential matrix.
|
||||
*
|
||||
* Decides the right solution by checking that the triangulation of a match
|
||||
* x1--x2 lies in front of the cameras. See HZ 9.6 pag 259 (9.6.3 Geometrical
|
||||
* interpretation of the 4 solutions)
|
||||
*
|
||||
* \return index of the right solution or -1 if no solution.
|
||||
*/
|
||||
int MotionFromEssentialChooseSolution(const std::vector<Mat3>& Rs,
|
||||
const std::vector<Vec3>& ts,
|
||||
const Mat3& K1,
|
||||
const Vec2& x1,
|
||||
const Mat3& K2,
|
||||
const Vec2& x2);
|
||||
|
||||
bool MotionFromEssentialAndCorrespondence(const Mat3& E,
|
||||
const Mat3& K1,
|
||||
const Vec2& x1,
|
||||
const Mat3& K2,
|
||||
const Vec2& x2,
|
||||
Mat3* R,
|
||||
Vec3* t);
|
||||
|
||||
/**
|
||||
* Find closest essential matrix E to fundamental F
|
||||
*/
|
||||
void FundamentalToEssential(const Mat3& F, Mat3* E);
|
||||
|
||||
/**
|
||||
* This structure contains options that controls how the fundamental
|
||||
* estimation operates.
|
||||
*
|
||||
* Defaults should be suitable for a wide range of use cases, but
|
||||
* better performance and accuracy might require tweaking/
|
||||
*/
|
||||
struct EstimateFundamentalOptions {
|
||||
// Default constructor which sets up a options for generic usage.
|
||||
EstimateFundamentalOptions(void);
|
||||
|
||||
// Maximal number of iterations for refinement step.
|
||||
int max_num_iterations;
|
||||
|
||||
// Expected average of symmetric epipolar distance between
|
||||
// actual destination points and original ones transformed by
|
||||
// estimated fundamental matrix.
|
||||
//
|
||||
// Refinement will finish as soon as average of symmetric
|
||||
// epipolar distance is less or equal to this value.
|
||||
//
|
||||
// This distance is measured in the same units as input points are.
|
||||
double expected_average_symmetric_distance;
|
||||
};
|
||||
|
||||
/**
|
||||
* Fundamental transformation estimation.
|
||||
*
|
||||
* This function estimates the fundamental transformation from a list of 2D
|
||||
* correspondences by doing algebraic estimation first followed with result
|
||||
* refinement.
|
||||
*/
|
||||
bool EstimateFundamentalFromCorrespondences(
|
||||
const Mat& x1,
|
||||
const Mat& x2,
|
||||
const EstimateFundamentalOptions& options,
|
||||
Mat3* F);
|
||||
|
||||
} // namespace libmv
|
||||
|
||||
#endif // LIBMV_MULTIVIEW_FUNDAMENTAL_H_
|
||||
166
blender-5.2.0/intern/libmv/libmv/multiview/fundamental_test.cc
Normal file
166
blender-5.2.0/intern/libmv/libmv/multiview/fundamental_test.cc
Normal file
@@ -0,0 +1,166 @@
|
||||
// Copyright (c) 2007, 2008 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 <iostream>
|
||||
|
||||
#include "libmv/logging/logging.h"
|
||||
#include "libmv/multiview/conditioning.h"
|
||||
#include "libmv/multiview/fundamental.h"
|
||||
#include "libmv/multiview/projection.h"
|
||||
#include "libmv/multiview/test_data_sets.h"
|
||||
#include "libmv/numeric/numeric.h"
|
||||
#include "testing/testing.h"
|
||||
|
||||
namespace {
|
||||
|
||||
using namespace libmv;
|
||||
|
||||
TEST(Fundamental, FundamentalFromProjections) {
|
||||
Mat34 P1_gt, P2_gt;
|
||||
// clang-format off
|
||||
P1_gt << 1, 0, 0, 0,
|
||||
0, 1, 0, 0,
|
||||
0, 0, 1, 0;
|
||||
P2_gt << 1, 1, 1, 3,
|
||||
0, 2, 0, 3,
|
||||
0, 1, 1, 0;
|
||||
// clang-format on
|
||||
Mat3 F_gt;
|
||||
FundamentalFromProjections(P1_gt, P2_gt, &F_gt);
|
||||
|
||||
Mat34 P1, P2;
|
||||
ProjectionsFromFundamental(F_gt, &P1, &P2);
|
||||
|
||||
Mat3 F;
|
||||
FundamentalFromProjections(P1, P2, &F);
|
||||
|
||||
EXPECT_MATRIX_PROP(F_gt, F, 1e-6);
|
||||
}
|
||||
|
||||
TEST(Fundamental, PreconditionerFromPoints) {
|
||||
int n = 4;
|
||||
Mat points(2, n);
|
||||
// clang-format off
|
||||
points << 0, 0, 1, 1,
|
||||
0, 2, 1, 3;
|
||||
// clang-format on
|
||||
|
||||
Mat3 T;
|
||||
PreconditionerFromPoints(points, &T);
|
||||
|
||||
Mat normalized_points;
|
||||
ApplyTransformationToPoints(points, T, &normalized_points);
|
||||
|
||||
Vec mean, variance;
|
||||
MeanAndVarianceAlongRows(normalized_points, &mean, &variance);
|
||||
|
||||
EXPECT_NEAR(0, mean(0), 1e-8);
|
||||
EXPECT_NEAR(0, mean(1), 1e-8);
|
||||
EXPECT_NEAR(2, variance(0), 1e-8);
|
||||
EXPECT_NEAR(2, variance(1), 1e-8);
|
||||
}
|
||||
|
||||
TEST(Fundamental, EssentialFromFundamental) {
|
||||
TwoViewDataSet d = TwoRealisticCameras();
|
||||
|
||||
Mat3 E_from_Rt;
|
||||
EssentialFromRt(d.R1, d.t1, d.R2, d.t2, &E_from_Rt);
|
||||
|
||||
Mat3 E_from_F;
|
||||
EssentialFromFundamental(d.F, d.K1, d.K2, &E_from_F);
|
||||
|
||||
EXPECT_MATRIX_PROP(E_from_Rt, E_from_F, 1e-6);
|
||||
}
|
||||
|
||||
TEST(Fundamental, MotionFromEssential) {
|
||||
TwoViewDataSet d = TwoRealisticCameras();
|
||||
|
||||
Mat3 E;
|
||||
EssentialFromRt(d.R1, d.t1, d.R2, d.t2, &E);
|
||||
|
||||
Mat3 R;
|
||||
Vec3 t;
|
||||
RelativeCameraMotion(d.R1, d.t1, d.R2, d.t2, &R, &t);
|
||||
NormalizeL2(&t);
|
||||
|
||||
std::vector<Mat3> Rs;
|
||||
std::vector<Vec3> ts;
|
||||
MotionFromEssential(E, &Rs, &ts);
|
||||
bool one_solution_is_correct = false;
|
||||
for (size_t i = 0; i < Rs.size(); ++i) {
|
||||
if (FrobeniusDistance(Rs[i], R) < 1e-8 && DistanceL2(ts[i], t) < 1e-8) {
|
||||
one_solution_is_correct = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
EXPECT_TRUE(one_solution_is_correct);
|
||||
}
|
||||
|
||||
TEST(Fundamental, MotionFromEssentialChooseSolution) {
|
||||
TwoViewDataSet d = TwoRealisticCameras();
|
||||
|
||||
Mat3 E;
|
||||
EssentialFromRt(d.R1, d.t1, d.R2, d.t2, &E);
|
||||
|
||||
Mat3 R;
|
||||
Vec3 t;
|
||||
RelativeCameraMotion(d.R1, d.t1, d.R2, d.t2, &R, &t);
|
||||
NormalizeL2(&t);
|
||||
|
||||
std::vector<Mat3> Rs;
|
||||
std::vector<Vec3> ts;
|
||||
MotionFromEssential(E, &Rs, &ts);
|
||||
|
||||
Vec2 x1, x2;
|
||||
MatrixColumn(d.x1, 0, &x1);
|
||||
MatrixColumn(d.x2, 0, &x2);
|
||||
int solution = MotionFromEssentialChooseSolution(Rs, ts, d.K1, x1, d.K2, x2);
|
||||
|
||||
EXPECT_LE(0, solution);
|
||||
EXPECT_LE(solution, 3);
|
||||
EXPECT_LE(FrobeniusDistance(Rs[solution], R), 1e-8);
|
||||
EXPECT_LE(DistanceL2(ts[solution], t), 1e-8);
|
||||
}
|
||||
|
||||
TEST(Fundamental, MotionFromEssentialAndCorrespondence) {
|
||||
TwoViewDataSet d = TwoRealisticCameras();
|
||||
|
||||
Mat3 E;
|
||||
EssentialFromRt(d.R1, d.t1, d.R2, d.t2, &E);
|
||||
|
||||
Mat3 R;
|
||||
Vec3 t;
|
||||
RelativeCameraMotion(d.R1, d.t1, d.R2, d.t2, &R, &t);
|
||||
NormalizeL2(&t);
|
||||
|
||||
Vec2 x1, x2;
|
||||
MatrixColumn(d.x1, 0, &x1);
|
||||
MatrixColumn(d.x2, 0, &x2);
|
||||
|
||||
Mat3 R_estimated;
|
||||
Vec3 t_estimated;
|
||||
MotionFromEssentialAndCorrespondence(
|
||||
E, d.K1, x1, d.K2, x2, &R_estimated, &t_estimated);
|
||||
|
||||
EXPECT_LE(FrobeniusDistance(R_estimated, R), 1e-8);
|
||||
EXPECT_LE(DistanceL2(t_estimated, t), 1e-8);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
464
blender-5.2.0/intern/libmv/libmv/multiview/homography.cc
Normal file
464
blender-5.2.0/intern/libmv/libmv/multiview/homography.cc
Normal file
@@ -0,0 +1,464 @@
|
||||
// Copyright (c) 2008, 2009 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/multiview/homography.h"
|
||||
|
||||
#include "ceres/ceres.h"
|
||||
#include "libmv/logging/logging.h"
|
||||
#include "libmv/multiview/conditioning.h"
|
||||
#include "libmv/multiview/homography_parameterization.h"
|
||||
|
||||
namespace libmv {
|
||||
/** 2D Homography transformation estimation in the case that points are in
|
||||
* euclidean coordinates.
|
||||
*
|
||||
* x = H y
|
||||
* x and y vector must have the same direction, we could write
|
||||
* crossproduct(|x|, * H * |y| ) = |0|
|
||||
*
|
||||
* | 0 -1 x2| |a b c| |y1| |0|
|
||||
* | 1 0 -x1| * |d e f| * |y2| = |0|
|
||||
* |-x2 x1 0| |g h 1| |1 | |0|
|
||||
*
|
||||
* That gives :
|
||||
*
|
||||
* (-d+x2*g)*y1 + (-e+x2*h)*y2 + -f+x2 |0|
|
||||
* (a-x1*g)*y1 + (b-x1*h)*y2 + c-x1 = |0|
|
||||
* (-x2*a+x1*d)*y1 + (-x2*b+x1*e)*y2 + -x2*c+x1*f |0|
|
||||
*/
|
||||
static bool Homography2DFromCorrespondencesLinearEuc(
|
||||
const Mat& x1, const Mat& x2, Mat3* H, double expected_precision) {
|
||||
assert(2 == x1.rows());
|
||||
assert(4 <= x1.cols());
|
||||
assert(x1.rows() == x2.rows());
|
||||
assert(x1.cols() == x2.cols());
|
||||
|
||||
int n = x1.cols();
|
||||
MatX8 L = Mat::Zero(n * 3, 8);
|
||||
Mat b = Mat::Zero(n * 3, 1);
|
||||
for (int i = 0; i < n; ++i) {
|
||||
int j = 3 * i;
|
||||
L(j, 0) = x1(0, i); // a
|
||||
L(j, 1) = x1(1, i); // b
|
||||
L(j, 2) = 1.0; // c
|
||||
L(j, 6) = -x2(0, i) * x1(0, i); // g
|
||||
L(j, 7) = -x2(0, i) * x1(1, i); // h
|
||||
b(j, 0) = x2(0, i); // i
|
||||
|
||||
++j;
|
||||
L(j, 3) = x1(0, i); // d
|
||||
L(j, 4) = x1(1, i); // e
|
||||
L(j, 5) = 1.0; // f
|
||||
L(j, 6) = -x2(1, i) * x1(0, i); // g
|
||||
L(j, 7) = -x2(1, i) * x1(1, i); // h
|
||||
b(j, 0) = x2(1, i); // i
|
||||
|
||||
// This ensures better stability
|
||||
// TODO(julien) make a lite version without this 3rd set
|
||||
++j;
|
||||
L(j, 0) = x2(1, i) * x1(0, i); // a
|
||||
L(j, 1) = x2(1, i) * x1(1, i); // b
|
||||
L(j, 2) = x2(1, i); // c
|
||||
L(j, 3) = -x2(0, i) * x1(0, i); // d
|
||||
L(j, 4) = -x2(0, i) * x1(1, i); // e
|
||||
L(j, 5) = -x2(0, i); // f
|
||||
}
|
||||
// Solve Lx=B
|
||||
Vec h = L.fullPivLu().solve(b);
|
||||
Homography2DNormalizedParameterization<double>::To(h, H);
|
||||
if ((L * h).isApprox(b, expected_precision)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
/** 2D Homography transformation estimation in the case that points are in
|
||||
* homogeneous coordinates.
|
||||
*
|
||||
* | 0 -x3 x2| |a b c| |y1| -x3*d+x2*g -x3*e+x2*h -x3*f+x2*1 |y1| (-x3*d+x2*g)*y1 (-x3*e+x2*h)*y2 (-x3*f+x2*1)*y3 |0|
|
||||
* | x3 0 -x1| * |d e f| * |y2| = x3*a-x1*g x3*b-x1*h x3*c-x1*1 * |y2| = (x3*a-x1*g)*y1 (x3*b-x1*h)*y2 (x3*c-x1*1)*y3 = |0|
|
||||
* |-x2 x1 0| |g h 1| |y3| -x2*a+x1*d -x2*b+x1*e -x2*c+x1*f |y3| (-x2*a+x1*d)*y1 (-x2*b+x1*e)*y2 (-x2*c+x1*f)*y3 |0|
|
||||
* X = |a b c d e f g h|^t
|
||||
*/
|
||||
// clang-format on
|
||||
bool Homography2DFromCorrespondencesLinear(const Mat& x1,
|
||||
const Mat& x2,
|
||||
Mat3* H,
|
||||
double expected_precision) {
|
||||
if (x1.rows() == 2) {
|
||||
return Homography2DFromCorrespondencesLinearEuc(
|
||||
x1, x2, H, expected_precision);
|
||||
}
|
||||
assert(3 == x1.rows());
|
||||
assert(4 <= x1.cols());
|
||||
assert(x1.rows() == x2.rows());
|
||||
assert(x1.cols() == x2.cols());
|
||||
|
||||
const int x = 0;
|
||||
const int y = 1;
|
||||
const int w = 2;
|
||||
int n = x1.cols();
|
||||
MatX8 L = Mat::Zero(n * 3, 8);
|
||||
Mat b = Mat::Zero(n * 3, 1);
|
||||
for (int i = 0; i < n; ++i) {
|
||||
int j = 3 * i;
|
||||
L(j, 0) = x2(w, i) * x1(x, i); // a
|
||||
L(j, 1) = x2(w, i) * x1(y, i); // b
|
||||
L(j, 2) = x2(w, i) * x1(w, i); // c
|
||||
L(j, 6) = -x2(x, i) * x1(x, i); // g
|
||||
L(j, 7) = -x2(x, i) * x1(y, i); // h
|
||||
b(j, 0) = x2(x, i) * x1(w, i);
|
||||
|
||||
++j;
|
||||
L(j, 3) = x2(w, i) * x1(x, i); // d
|
||||
L(j, 4) = x2(w, i) * x1(y, i); // e
|
||||
L(j, 5) = x2(w, i) * x1(w, i); // f
|
||||
L(j, 6) = -x2(y, i) * x1(x, i); // g
|
||||
L(j, 7) = -x2(y, i) * x1(y, i); // h
|
||||
b(j, 0) = x2(y, i) * x1(w, i);
|
||||
|
||||
// This ensures better stability
|
||||
++j;
|
||||
L(j, 0) = x2(y, i) * x1(x, i); // a
|
||||
L(j, 1) = x2(y, i) * x1(y, i); // b
|
||||
L(j, 2) = x2(y, i) * x1(w, i); // c
|
||||
L(j, 3) = -x2(x, i) * x1(x, i); // d
|
||||
L(j, 4) = -x2(x, i) * x1(y, i); // e
|
||||
L(j, 5) = -x2(x, i) * x1(w, i); // f
|
||||
}
|
||||
// Solve Lx=B
|
||||
Vec h = L.fullPivLu().solve(b);
|
||||
if ((L * h).isApprox(b, expected_precision)) {
|
||||
Homography2DNormalizedParameterization<double>::To(h, H);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Default settings for homography estimation which should be suitable
|
||||
// for a wide range of use cases.
|
||||
EstimateHomographyOptions::EstimateHomographyOptions()
|
||||
: use_normalization(true),
|
||||
max_num_iterations(50),
|
||||
expected_average_symmetric_distance(1e-16) {
|
||||
}
|
||||
|
||||
namespace {
|
||||
void GetNormalizedPoints(const Mat& original_points,
|
||||
Mat* normalized_points,
|
||||
Mat3* normalization_matrix) {
|
||||
IsotropicPreconditionerFromPoints(original_points, normalization_matrix);
|
||||
ApplyTransformationToPoints(
|
||||
original_points, *normalization_matrix, normalized_points);
|
||||
}
|
||||
|
||||
// Cost functor which computes symmetric geometric distance
|
||||
// used for homography matrix refinement.
|
||||
class HomographySymmetricGeometricCostFunctor {
|
||||
public:
|
||||
HomographySymmetricGeometricCostFunctor(const Vec2& x, const Vec2& y)
|
||||
: x_(x), y_(y) {}
|
||||
|
||||
template <typename T>
|
||||
bool operator()(const T* homography_parameters, T* residuals) const {
|
||||
typedef Eigen::Matrix<T, 3, 3> Mat3;
|
||||
typedef Eigen::Matrix<T, 3, 1> Vec3;
|
||||
|
||||
Mat3 H(homography_parameters);
|
||||
|
||||
Vec3 x(T(x_(0)), T(x_(1)), T(1.0));
|
||||
Vec3 y(T(y_(0)), T(y_(1)), T(1.0));
|
||||
|
||||
Vec3 H_x = H * x;
|
||||
Vec3 Hinv_y = H.inverse() * y;
|
||||
|
||||
H_x /= H_x(2);
|
||||
Hinv_y /= Hinv_y(2);
|
||||
|
||||
// This is a forward error.
|
||||
residuals[0] = H_x(0) - T(y_(0));
|
||||
residuals[1] = H_x(1) - T(y_(1));
|
||||
|
||||
// This is a backward error.
|
||||
residuals[2] = Hinv_y(0) - T(x_(0));
|
||||
residuals[3] = Hinv_y(1) - T(x_(1));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
EIGEN_MAKE_ALIGNED_OPERATOR_NEW
|
||||
|
||||
const Vec2 x_;
|
||||
const Vec2 y_;
|
||||
};
|
||||
|
||||
// Termination checking callback used for homography estimation.
|
||||
// It finished the minimization as soon as actual average of
|
||||
// symmetric geometric distance is less or equal to the expected
|
||||
// average value.
|
||||
class TerminationCheckingCallback : public ceres::IterationCallback {
|
||||
public:
|
||||
TerminationCheckingCallback(const Mat& x1,
|
||||
const Mat& x2,
|
||||
const EstimateHomographyOptions& options,
|
||||
Mat3* H)
|
||||
: options_(options), x1_(x1), x2_(x2), H_(H) {}
|
||||
|
||||
virtual ceres::CallbackReturnType operator()(
|
||||
const ceres::IterationSummary& summary) {
|
||||
// If the step wasn't successful, there's nothing to do.
|
||||
if (!summary.step_is_successful) {
|
||||
return ceres::SOLVER_CONTINUE;
|
||||
}
|
||||
|
||||
// Calculate average of symmetric geometric distance.
|
||||
double average_distance = 0.0;
|
||||
for (int i = 0; i < x1_.cols(); i++) {
|
||||
average_distance =
|
||||
SymmetricGeometricDistance(*H_, x1_.col(i), x2_.col(i));
|
||||
}
|
||||
average_distance /= x1_.cols();
|
||||
|
||||
if (average_distance <= options_.expected_average_symmetric_distance) {
|
||||
return ceres::SOLVER_TERMINATE_SUCCESSFULLY;
|
||||
}
|
||||
|
||||
return ceres::SOLVER_CONTINUE;
|
||||
}
|
||||
|
||||
private:
|
||||
const EstimateHomographyOptions& options_;
|
||||
const Mat& x1_;
|
||||
const Mat& x2_;
|
||||
Mat3* H_;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
/** 2D Homography transformation estimation in the case that points are in
|
||||
* euclidean coordinates.
|
||||
*/
|
||||
bool EstimateHomography2DFromCorrespondences(
|
||||
const Mat& x1,
|
||||
const Mat& x2,
|
||||
const EstimateHomographyOptions& options,
|
||||
Mat3* H) {
|
||||
// TODO(sergey): Support homogenous coordinates, not just euclidean.
|
||||
|
||||
assert(2 == x1.rows());
|
||||
assert(4 <= x1.cols());
|
||||
assert(x1.rows() == x2.rows());
|
||||
assert(x1.cols() == x2.cols());
|
||||
|
||||
Mat3 T1 = Mat3::Identity(), T2 = Mat3::Identity();
|
||||
|
||||
// Step 1: Algebraic homography estimation.
|
||||
Mat x1_normalized, x2_normalized;
|
||||
|
||||
if (options.use_normalization) {
|
||||
LG << "Estimating homography using normalization.";
|
||||
GetNormalizedPoints(x1, &x1_normalized, &T1);
|
||||
GetNormalizedPoints(x2, &x2_normalized, &T2);
|
||||
} else {
|
||||
x1_normalized = x1;
|
||||
x2_normalized = x2;
|
||||
}
|
||||
|
||||
// Assume algebraic estiation always suceeds,
|
||||
Homography2DFromCorrespondencesLinear(x1_normalized, x2_normalized, H);
|
||||
|
||||
// Denormalize the homography matrix.
|
||||
if (options.use_normalization) {
|
||||
*H = T2.inverse() * (*H) * T1;
|
||||
}
|
||||
|
||||
LG << "Estimated matrix after algebraic estimation:\n" << *H;
|
||||
|
||||
// Step 2: Refine matrix using Ceres minimizer.
|
||||
ceres::Problem problem;
|
||||
for (int i = 0; i < x1.cols(); i++) {
|
||||
HomographySymmetricGeometricCostFunctor*
|
||||
homography_symmetric_geometric_cost_function =
|
||||
new HomographySymmetricGeometricCostFunctor(x1.col(i), x2.col(i));
|
||||
|
||||
problem.AddResidualBlock(
|
||||
new ceres::AutoDiffCostFunction<HomographySymmetricGeometricCostFunctor,
|
||||
4, // num_residuals
|
||||
9>(
|
||||
homography_symmetric_geometric_cost_function),
|
||||
NULL,
|
||||
H->data());
|
||||
}
|
||||
|
||||
// Configure the solve.
|
||||
ceres::Solver::Options solver_options;
|
||||
solver_options.linear_solver_type = ceres::DENSE_QR;
|
||||
solver_options.max_num_iterations = options.max_num_iterations;
|
||||
solver_options.update_state_every_iteration = true;
|
||||
|
||||
// Terminate if the average symmetric distance is good enough.
|
||||
TerminationCheckingCallback callback(x1, x2, options, H);
|
||||
solver_options.callbacks.push_back(&callback);
|
||||
|
||||
// Run the solve.
|
||||
ceres::Solver::Summary summary;
|
||||
ceres::Solve(solver_options, &problem, &summary);
|
||||
|
||||
VLOG(1) << "Summary:\n" << summary.FullReport();
|
||||
|
||||
LG << "Final refined matrix:\n" << *H;
|
||||
|
||||
return summary.IsSolutionUsable();
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
/**
|
||||
* x2 ~ A * x1
|
||||
* x2^t * Hi * A *x1 = 0
|
||||
* H1 = H2 = H3 =
|
||||
* | 0 0 0 1| |-x2w| |0 0 0 0| | 0 | | 0 0 1 0| |-x2z|
|
||||
* | 0 0 0 0| -> | 0 | |0 0 1 0| -> |-x2z| | 0 0 0 0| -> | 0 |
|
||||
* | 0 0 0 0| | 0 | |0-1 0 0| | x2y| |-1 0 0 0| | x2x|
|
||||
* |-1 0 0 0| | x2x| |0 0 0 0| | 0 | | 0 0 0 0| | 0 |
|
||||
* H4 = H5 = H6 =
|
||||
* |0 0 0 0| | 0 | | 0 1 0 0| |-x2y| |0 0 0 0| | 0 |
|
||||
* |0 0 0 1| -> |-x2w| |-1 0 0 0| -> | x2x| |0 0 0 0| -> | 0 |
|
||||
* |0 0 0 0| | 0 | | 0 0 0 0| | 0 | |0 0 0 1| |-x2w|
|
||||
* |0-1 0 0| | x2y| | 0 0 0 0| | 0 | |0 0-1 0| | x2z|
|
||||
* |a b c d|
|
||||
* A = |e f g h|
|
||||
* |i j k l|
|
||||
* |m n o 1|
|
||||
*
|
||||
* x2^t * H1 * A *x1 = (-x2w*a +x2x*m )*x1x + (-x2w*b +x2x*n )*x1y + (-x2w*c +x2x*o )*x1z + (-x2w*d +x2x*1 )*x1w = 0
|
||||
* x2^t * H2 * A *x1 = (-x2z*e +x2y*i )*x1x + (-x2z*f +x2y*j )*x1y + (-x2z*g +x2y*k )*x1z + (-x2z*h +x2y*l )*x1w = 0
|
||||
* x2^t * H3 * A *x1 = (-x2z*a +x2x*i )*x1x + (-x2z*b +x2x*j )*x1y + (-x2z*c +x2x*k )*x1z + (-x2z*d +x2x*l )*x1w = 0
|
||||
* x2^t * H4 * A *x1 = (-x2w*e +x2y*m )*x1x + (-x2w*f +x2y*n )*x1y + (-x2w*g +x2y*o )*x1z + (-x2w*h +x2y*1 )*x1w = 0
|
||||
* x2^t * H5 * A *x1 = (-x2y*a +x2x*e )*x1x + (-x2y*b +x2x*f )*x1y + (-x2y*c +x2x*g )*x1z + (-x2y*d +x2x*h )*x1w = 0
|
||||
* x2^t * H6 * A *x1 = (-x2w*i +x2z*m )*x1x + (-x2w*j +x2z*n )*x1y + (-x2w*k +x2z*o )*x1z + (-x2w*l +x2z*1 )*x1w = 0
|
||||
*
|
||||
* X = |a b c d e f g h i j k l m n o|^t
|
||||
*/
|
||||
// clang-format on
|
||||
bool Homography3DFromCorrespondencesLinear(const Mat& x1,
|
||||
const Mat& x2,
|
||||
Mat4* H,
|
||||
double expected_precision) {
|
||||
assert(4 == x1.rows());
|
||||
assert(5 <= x1.cols());
|
||||
assert(x1.rows() == x2.rows());
|
||||
assert(x1.cols() == x2.cols());
|
||||
const int x = 0;
|
||||
const int y = 1;
|
||||
const int z = 2;
|
||||
const int w = 3;
|
||||
int n = x1.cols();
|
||||
MatX15 L = Mat::Zero(n * 6, 15);
|
||||
Mat b = Mat::Zero(n * 6, 1);
|
||||
for (int i = 0; i < n; ++i) {
|
||||
int j = 6 * i;
|
||||
L(j, 0) = -x2(w, i) * x1(x, i); // a
|
||||
L(j, 1) = -x2(w, i) * x1(y, i); // b
|
||||
L(j, 2) = -x2(w, i) * x1(z, i); // c
|
||||
L(j, 3) = -x2(w, i) * x1(w, i); // d
|
||||
L(j, 12) = x2(x, i) * x1(x, i); // m
|
||||
L(j, 13) = x2(x, i) * x1(y, i); // n
|
||||
L(j, 14) = x2(x, i) * x1(z, i); // o
|
||||
b(j, 0) = -x2(x, i) * x1(w, i);
|
||||
|
||||
++j;
|
||||
L(j, 4) = -x2(z, i) * x1(x, i); // e
|
||||
L(j, 5) = -x2(z, i) * x1(y, i); // f
|
||||
L(j, 6) = -x2(z, i) * x1(z, i); // g
|
||||
L(j, 7) = -x2(z, i) * x1(w, i); // h
|
||||
L(j, 8) = x2(y, i) * x1(x, i); // i
|
||||
L(j, 9) = x2(y, i) * x1(y, i); // j
|
||||
L(j, 10) = x2(y, i) * x1(z, i); // k
|
||||
L(j, 11) = x2(y, i) * x1(w, i); // l
|
||||
|
||||
++j;
|
||||
L(j, 0) = -x2(z, i) * x1(x, i); // a
|
||||
L(j, 1) = -x2(z, i) * x1(y, i); // b
|
||||
L(j, 2) = -x2(z, i) * x1(z, i); // c
|
||||
L(j, 3) = -x2(z, i) * x1(w, i); // d
|
||||
L(j, 8) = x2(x, i) * x1(x, i); // i
|
||||
L(j, 9) = x2(x, i) * x1(y, i); // j
|
||||
L(j, 10) = x2(x, i) * x1(z, i); // k
|
||||
L(j, 11) = x2(x, i) * x1(w, i); // l
|
||||
|
||||
++j;
|
||||
L(j, 4) = -x2(w, i) * x1(x, i); // e
|
||||
L(j, 5) = -x2(w, i) * x1(y, i); // f
|
||||
L(j, 6) = -x2(w, i) * x1(z, i); // g
|
||||
L(j, 7) = -x2(w, i) * x1(w, i); // h
|
||||
L(j, 12) = x2(y, i) * x1(x, i); // m
|
||||
L(j, 13) = x2(y, i) * x1(y, i); // n
|
||||
L(j, 14) = x2(y, i) * x1(z, i); // o
|
||||
b(j, 0) = -x2(y, i) * x1(w, i);
|
||||
|
||||
++j;
|
||||
L(j, 0) = -x2(y, i) * x1(x, i); // a
|
||||
L(j, 1) = -x2(y, i) * x1(y, i); // b
|
||||
L(j, 2) = -x2(y, i) * x1(z, i); // c
|
||||
L(j, 3) = -x2(y, i) * x1(w, i); // d
|
||||
L(j, 4) = x2(x, i) * x1(x, i); // e
|
||||
L(j, 5) = x2(x, i) * x1(y, i); // f
|
||||
L(j, 6) = x2(x, i) * x1(z, i); // g
|
||||
L(j, 7) = x2(x, i) * x1(w, i); // h
|
||||
|
||||
++j;
|
||||
L(j, 8) = -x2(w, i) * x1(x, i); // i
|
||||
L(j, 9) = -x2(w, i) * x1(y, i); // j
|
||||
L(j, 10) = -x2(w, i) * x1(z, i); // k
|
||||
L(j, 11) = -x2(w, i) * x1(w, i); // l
|
||||
L(j, 12) = x2(z, i) * x1(x, i); // m
|
||||
L(j, 13) = x2(z, i) * x1(y, i); // n
|
||||
L(j, 14) = x2(z, i) * x1(z, i); // o
|
||||
b(j, 0) = -x2(z, i) * x1(w, i);
|
||||
}
|
||||
// Solve Lx=B
|
||||
Vec h = L.fullPivLu().solve(b);
|
||||
if ((L * h).isApprox(b, expected_precision)) {
|
||||
Homography3DNormalizedParameterization<double>::To(h, H);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
double SymmetricGeometricDistance(const Mat3& H,
|
||||
const Vec2& x1,
|
||||
const Vec2& x2) {
|
||||
Vec3 x(x1(0), x1(1), 1.0);
|
||||
Vec3 y(x2(0), x2(1), 1.0);
|
||||
|
||||
Vec3 H_x = H * x;
|
||||
Vec3 Hinv_y = H.inverse() * y;
|
||||
|
||||
H_x /= H_x(2);
|
||||
Hinv_y /= Hinv_y(2);
|
||||
|
||||
return (H_x.head<2>() - y.head<2>()).squaredNorm() +
|
||||
(Hinv_y.head<2>() - x.head<2>()).squaredNorm();
|
||||
}
|
||||
|
||||
} // namespace libmv
|
||||
149
blender-5.2.0/intern/libmv/libmv/multiview/homography.h
Normal file
149
blender-5.2.0/intern/libmv/libmv/multiview/homography.h
Normal file
@@ -0,0 +1,149 @@
|
||||
// Copyright (c) 2011 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.
|
||||
|
||||
#ifndef LIBMV_MULTIVIEW_HOMOGRAPHY_H_
|
||||
#define LIBMV_MULTIVIEW_HOMOGRAPHY_H_
|
||||
|
||||
#include "libmv/numeric/numeric.h"
|
||||
|
||||
namespace libmv {
|
||||
|
||||
/**
|
||||
* 2D homography transformation estimation.
|
||||
*
|
||||
* This function estimates the homography transformation from a list of 2D
|
||||
* correspondences which represents either:
|
||||
*
|
||||
* - 3D points on a plane, with a general moving camera.
|
||||
* - 3D points with a rotating camera (pure rotation).
|
||||
* - 3D points + different planar projections
|
||||
*
|
||||
* \param x1: The first 2xN or 3xN matrix of euclidean or homogeneous points.
|
||||
* \param x2: The second 2xN or 3xN matrix of euclidean or homogeneous points.
|
||||
* \param H: The 3x3 homography transformation matrix (8 dof) such that
|
||||
* <pre>
|
||||
* x2 = H * x1 with |a b c|
|
||||
* H = |d e f|
|
||||
* |g h 1|
|
||||
* </pre>
|
||||
* \param expected_precision: The expected precision in order for instance
|
||||
* to accept almost homography matrices.
|
||||
*
|
||||
* \return True if the transformation estimation has succeeded.
|
||||
* \note There must be at least 4 non-colinear points.
|
||||
*/
|
||||
bool Homography2DFromCorrespondencesLinear(
|
||||
const Mat& x1,
|
||||
const Mat& x2,
|
||||
Mat3* H,
|
||||
double expected_precision = EigenDouble::dummy_precision());
|
||||
|
||||
/**
|
||||
* This structure contains options that controls how the homography
|
||||
* estimation operates.
|
||||
*
|
||||
* Defaults should be suitable for a wide range of use cases, but
|
||||
* better performance and accuracy might require tweaking/
|
||||
*/
|
||||
struct EstimateHomographyOptions {
|
||||
// Default constructor which sets up a options for generic usage.
|
||||
EstimateHomographyOptions(void);
|
||||
|
||||
// Normalize correspondencies before estimating the homography
|
||||
// in order to increase estimation stability.
|
||||
//
|
||||
// Normaliztion will make it so centroid od correspondences
|
||||
// is the coordinate origin and their average distance from
|
||||
// the origin is sqrt(2).
|
||||
//
|
||||
// See:
|
||||
// - R. Hartley and A. Zisserman. Multiple View Geometry in Computer
|
||||
// Vision. Cambridge University Press, second edition, 2003.
|
||||
// - https://www.cs.ubc.ca/grads/resources/thesis/May09/Dubrofsky_Elan.pdf
|
||||
bool use_normalization;
|
||||
|
||||
// Maximal number of iterations for the refinement step.
|
||||
int max_num_iterations;
|
||||
|
||||
// Expected average of symmetric geometric distance between
|
||||
// actual destination points and original ones transformed by
|
||||
// estimated homography matrix.
|
||||
//
|
||||
// Refinement will finish as soon as average of symmetric
|
||||
// geometric distance is less or equal to this value.
|
||||
//
|
||||
// This distance is measured in the same units as input points are.
|
||||
double expected_average_symmetric_distance;
|
||||
};
|
||||
|
||||
/**
|
||||
* 2D homography transformation estimation.
|
||||
*
|
||||
* This function estimates the homography transformation from a list of 2D
|
||||
* correspondences by doing algebraic estimation first followed with result
|
||||
* refinement.
|
||||
*/
|
||||
bool EstimateHomography2DFromCorrespondences(
|
||||
const Mat& x1,
|
||||
const Mat& x2,
|
||||
const EstimateHomographyOptions& options,
|
||||
Mat3* H);
|
||||
|
||||
/**
|
||||
* 3D Homography transformation estimation.
|
||||
*
|
||||
* This function can be used in order to estimate the homography transformation
|
||||
* from a list of 3D correspondences.
|
||||
*
|
||||
* \param[in] x1 The first 4xN matrix of homogeneous points
|
||||
* \param[in] x2 The second 4xN matrix of homogeneous points
|
||||
* \param[out] H The 4x4 homography transformation matrix (15 dof) such that
|
||||
* <pre>
|
||||
* x2 = H * x1 with |a b c d|
|
||||
* H = |e f g h|
|
||||
* |i j k l|
|
||||
* |m n o 1|
|
||||
* </pre>
|
||||
* \param[in] expected_precision The expected precision in order for instance
|
||||
* to accept almost homography matrices.
|
||||
*
|
||||
* \return true if the transformation estimation has succeeded
|
||||
*
|
||||
* \note Need at least 5 non coplanar points
|
||||
* \note Points coordinates must be in homogeneous coordinates
|
||||
*/
|
||||
bool Homography3DFromCorrespondencesLinear(
|
||||
const Mat& x1,
|
||||
const Mat& x2,
|
||||
Mat4* H,
|
||||
double expected_precision = EigenDouble::dummy_precision());
|
||||
|
||||
/**
|
||||
* Calculate symmetric geometric cost:
|
||||
*
|
||||
* D(H * x1, x2)^2 + D(H^-1 * x2, x1)
|
||||
*/
|
||||
double SymmetricGeometricDistance(const Mat3& H,
|
||||
const Vec2& x1,
|
||||
const Vec2& x2);
|
||||
|
||||
} // namespace libmv
|
||||
|
||||
#endif // LIBMV_MULTIVIEW_HOMOGRAPHY_H_
|
||||
250
blender-5.2.0/intern/libmv/libmv/multiview/homography_error.h
Normal file
250
blender-5.2.0/intern/libmv/libmv/multiview/homography_error.h
Normal file
@@ -0,0 +1,250 @@
|
||||
// Copyright (c) 2011 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.
|
||||
|
||||
#ifndef LIBMV_MULTIVIEW_HOMOGRAPHY_ERRORS_H_
|
||||
#define LIBMV_MULTIVIEW_HOMOGRAPHY_ERRORS_H_
|
||||
|
||||
#include "libmv/multiview/projection.h"
|
||||
|
||||
namespace libmv {
|
||||
namespace homography {
|
||||
namespace homography2D {
|
||||
|
||||
/**
|
||||
* Structure for estimating the asymmetric error between a vector x2 and the
|
||||
* transformed x1 such that
|
||||
* Error = ||x2 - Psi(H * x1)||^2
|
||||
* where Psi is the function that transforms homogeneous to euclidean coords.
|
||||
* \note It should be distributed as Chi-squared with k = 2.
|
||||
*/
|
||||
struct AsymmetricError {
|
||||
/**
|
||||
* Computes the asymmetric residuals between a set of 2D points x2 and the
|
||||
* transformed 2D point set x1 such that
|
||||
* Residuals_i = x2_i - Psi(H * x1_i)
|
||||
* where Psi is the function that transforms homogeneous to euclidean coords.
|
||||
*
|
||||
* \param[in] H The 3x3 homography matrix.
|
||||
* The estimated homography should approximatelly hold the condition y = H x.
|
||||
* \param[in] x1 A set of 2D points (2xN or 3xN matrix of column vectors).
|
||||
* \param[in] x2 A set of 2D points (2xN or 3xN matrix of column vectors).
|
||||
* \param[out] dx A 2xN matrix of column vectors of residuals errors
|
||||
*/
|
||||
static void Residuals(const Mat& H, const Mat& x1, const Mat& x2, Mat2X* dx) {
|
||||
dx->resize(2, x1.cols());
|
||||
Mat3X x2h_est;
|
||||
if (x1.rows() == 2) {
|
||||
x2h_est = H * EuclideanToHomogeneous(static_cast<Mat2X>(x1));
|
||||
} else {
|
||||
x2h_est = H * x1;
|
||||
}
|
||||
dx->row(0) = x2h_est.row(0).array() / x2h_est.row(2).array();
|
||||
dx->row(1) = x2h_est.row(1).array() / x2h_est.row(2).array();
|
||||
if (x2.rows() == 2) {
|
||||
*dx = x2 - *dx;
|
||||
} else {
|
||||
*dx = HomogeneousToEuclidean(static_cast<Mat3X>(x2)) - *dx;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Computes the asymmetric residuals between a 2D point x2 and the transformed
|
||||
* 2D point x1 such that
|
||||
* Residuals = x2 - Psi(H * x1)
|
||||
* where Psi is the function that transforms homogeneous to euclidean coords.
|
||||
*
|
||||
* \param[in] H The 3x3 homography matrix.
|
||||
* The estimated homography should approximatelly hold the condition y = H x.
|
||||
* \param[in] x1 A 2D point (vector of size 2 or 3 (euclidean/homogeneous))
|
||||
* \param[in] x2 A 2D point (vector of size 2 or 3 (euclidean/homogeneous))
|
||||
* \param[out] dx A vector of size 2 of the residual error
|
||||
*/
|
||||
static void Residuals(const Mat& H, const Vec& x1, const Vec& x2, Vec2* dx) {
|
||||
Vec3 x2h_est;
|
||||
if (x1.rows() == 2) {
|
||||
x2h_est = H * EuclideanToHomogeneous(static_cast<Vec2>(x1));
|
||||
} else {
|
||||
x2h_est = H * x1;
|
||||
}
|
||||
if (x2.rows() == 2) {
|
||||
*dx = x2 - x2h_est.head<2>() / x2h_est[2];
|
||||
} else {
|
||||
*dx = HomogeneousToEuclidean(static_cast<Vec3>(x2)) -
|
||||
x2h_est.head<2>() / x2h_est[2];
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Computes the squared norm of the residuals between a set of 2D points x2
|
||||
* and the transformed 2D point set x1 such that
|
||||
* Error = || x2 - Psi(H * x1) ||^2
|
||||
* where Psi is the function that transforms homogeneous to euclidean coords.
|
||||
*
|
||||
* \param[in] H The 3x3 homography matrix.
|
||||
* The estimated homography should approximatelly hold the condition y = H x.
|
||||
* \param[in] x1 A set of 2D points (2xN or 3xN matrix of column vectors).
|
||||
* \param[in] x2 A set of 2D points (2xN or 3xN matrix of column vectors).
|
||||
* \return The squared norm of the asymmetric residuals errors
|
||||
*/
|
||||
static double Error(const Mat& H, const Mat& x1, const Mat& x2) {
|
||||
Mat2X dx;
|
||||
Residuals(H, x1, x2, &dx);
|
||||
return dx.squaredNorm();
|
||||
}
|
||||
/**
|
||||
* Computes the squared norm of the residuals between a 2D point x2 and the
|
||||
* transformed 2D point x1 such that rms = || x2 - Psi(H * x1) ||^2
|
||||
* where Psi is the function that transforms homogeneous to euclidean coords.
|
||||
*
|
||||
* \param[in] H The 3x3 homography matrix.
|
||||
* The estimated homography should approximatelly hold the condition y = H x.
|
||||
* \param[in] x1 A 2D point (vector of size 2 or 3 (euclidean/homogeneous))
|
||||
* \param[in] x2 A 2D point (vector of size 2 or 3 (euclidean/homogeneous))
|
||||
* \return The squared norm of the asymmetric residual error
|
||||
*/
|
||||
static double Error(const Mat& H, const Vec& x1, const Vec& x2) {
|
||||
Vec2 dx;
|
||||
Residuals(H, x1, x2, &dx);
|
||||
return dx.squaredNorm();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Structure for estimating the symmetric error
|
||||
* between a vector x2 and the transformed x1 such that
|
||||
* Error = ||x2 - Psi(H * x1)||^2 + ||x1 - Psi(H^-1 * x2)||^2
|
||||
* where Psi is the function that transforms homogeneous to euclidean coords.
|
||||
* \note It should be distributed as Chi-squared with k = 4.
|
||||
*/
|
||||
struct SymmetricError {
|
||||
/**
|
||||
* Computes the squared norm of the residuals between x2 and the
|
||||
* transformed x1 such that
|
||||
* Error = ||x2 - Psi(H * x1)||^2 + ||x1 - Psi(H^-1 * x2)||^2
|
||||
* where Psi is the function that transforms homogeneous to euclidean coords.
|
||||
*
|
||||
* \param[in] H The 3x3 homography matrix.
|
||||
* The estimated homography should approximatelly hold the condition y = H x.
|
||||
* \param[in] x1 A 2D point (vector of size 2 or 3 (euclidean/homogeneous))
|
||||
* \param[in] x2 A 2D point (vector of size 2 or 3 (euclidean/homogeneous))
|
||||
* \return The squared norm of the symmetric residuals errors
|
||||
*/
|
||||
static double Error(const Mat& H, const Vec& x1, const Vec& x2) {
|
||||
// TODO(keir): This is awesomely inefficient because it does a 3x3
|
||||
// inversion for each evaluation.
|
||||
Mat3 Hinv = H.inverse();
|
||||
return AsymmetricError::Error(H, x1, x2) +
|
||||
AsymmetricError::Error(Hinv, x2, x1);
|
||||
}
|
||||
// TODO(julien) Add residuals function \see AsymmetricError
|
||||
};
|
||||
/**
|
||||
* Structure for estimating the algebraic error (cross product)
|
||||
* between a vector x2 and the transformed x1 such that
|
||||
* Error = ||[x2] * H * x1||^^2
|
||||
* where [x2] is the skew matrix of x2.
|
||||
*/
|
||||
struct AlgebraicError {
|
||||
// TODO(julien) Make an AlgebraicError2Rows and AlgebraicError3Rows
|
||||
|
||||
/**
|
||||
* Computes the algebraic residuals (cross product) between a set of 2D
|
||||
* points x2 and the transformed 2D point set x1 such that
|
||||
* [x2] * H * x1 where [x2] is the skew matrix of x2.
|
||||
*
|
||||
* \param[in] H The 3x3 homography matrix.
|
||||
* The estimated homography should approximatelly hold the condition y = H x.
|
||||
* \param[in] x1 A set of 2D points (2xN or 3xN matrix of column vectors).
|
||||
* \param[in] x2 A set of 2D points (2xN or 3xN matrix of column vectors).
|
||||
* \param[out] dx A 3xN matrix of column vectors of residuals errors
|
||||
*/
|
||||
static void Residuals(const Mat& H, const Mat& x1, const Mat& x2, Mat3X* dx) {
|
||||
dx->resize(3, x1.cols());
|
||||
Vec3 col;
|
||||
for (int i = 0; i < x1.cols(); ++i) {
|
||||
Residuals(H, x1.col(i), x2.col(i), &col);
|
||||
dx->col(i) = col;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Computes the algebraic residuals (cross product) between a 2D point x2
|
||||
* and the transformed 2D point x1 such that
|
||||
* [x2] * H * x1 where [x2] is the skew matrix of x2.
|
||||
*
|
||||
* \param[in] H The 3x3 homography matrix.
|
||||
* The estimated homography should approximatelly hold the condition y = H x.
|
||||
* \param[in] x1 A 2D point (vector of size 2 or 3 (euclidean/homogeneous))
|
||||
* \param[in] x2 A 2D point (vector of size 2 or 3 (euclidean/homogeneous))
|
||||
* \param[out] dx A vector of size 3 of the residual error
|
||||
*/
|
||||
static void Residuals(const Mat& H, const Vec& x1, const Vec& x2, Vec3* dx) {
|
||||
Vec3 x2h_est;
|
||||
if (x1.rows() == 2) {
|
||||
x2h_est = H * EuclideanToHomogeneous(static_cast<Vec2>(x1));
|
||||
} else {
|
||||
x2h_est = H * x1;
|
||||
}
|
||||
if (x2.rows() == 2) {
|
||||
*dx = SkewMat(EuclideanToHomogeneous(static_cast<Vec2>(x2))) * x2h_est;
|
||||
} else {
|
||||
*dx = SkewMat(x2) * x2h_est;
|
||||
}
|
||||
// TODO(julien) This is inefficient since it creates an
|
||||
// identical 3x3 skew matrix for each evaluation.
|
||||
}
|
||||
/**
|
||||
* Computes the squared norm of the algebraic residuals between a set of 2D
|
||||
* points x2 and the transformed 2D point set x1 such that
|
||||
* [x2] * H * x1 where [x2] is the skew matrix of x2.
|
||||
*
|
||||
* \param[in] H The 3x3 homography matrix.
|
||||
* The estimated homography should approximatelly hold the condition y = H x.
|
||||
* \param[in] x1 A set of 2D points (2xN or 3xN matrix of column vectors).
|
||||
* \param[in] x2 A set of 2D points (2xN or 3xN matrix of column vectors).
|
||||
* \return The squared norm of the asymmetric residuals errors
|
||||
*/
|
||||
static double Error(const Mat& H, const Mat& x1, const Mat& x2) {
|
||||
Mat3X dx;
|
||||
Residuals(H, x1, x2, &dx);
|
||||
return dx.squaredNorm();
|
||||
}
|
||||
/**
|
||||
* Computes the squared norm of the algebraic residuals between a 2D point x2
|
||||
* and the transformed 2D point x1 such that
|
||||
* [x2] * H * x1 where [x2] is the skew matrix of x2.
|
||||
*
|
||||
* \param[in] H The 3x3 homography matrix.
|
||||
* The estimated homography should approximatelly hold the condition y = H x.
|
||||
* \param[in] x1 A 2D point (vector of size 2 or 3 (euclidean/homogeneous))
|
||||
* \param[in] x2 A 2D point (vector of size 2 or 3 (euclidean/homogeneous))
|
||||
* \return The squared norm of the asymmetric residual error
|
||||
*/
|
||||
static double Error(const Mat& H, const Vec& x1, const Vec& x2) {
|
||||
Vec3 dx;
|
||||
Residuals(H, x1, x2, &dx);
|
||||
return dx.squaredNorm();
|
||||
}
|
||||
};
|
||||
// TODO(keir): Add error based on ideal points.
|
||||
|
||||
} // namespace homography2D
|
||||
// TODO(julien) add homography3D errors
|
||||
} // namespace homography
|
||||
} // namespace libmv
|
||||
|
||||
#endif // LIBMV_MULTIVIEW_HOMOGRAPHY_ERRORS_H_
|
||||
@@ -0,0 +1,99 @@
|
||||
// Copyright (c) 2011 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.
|
||||
|
||||
#ifndef LIBMV_MULTIVIEW_HOMOGRAPHY_PARAMETERIZATION_H_
|
||||
#define LIBMV_MULTIVIEW_HOMOGRAPHY_PARAMETERIZATION_H_
|
||||
|
||||
#include "libmv/numeric/numeric.h"
|
||||
|
||||
namespace libmv {
|
||||
|
||||
/** A parameterization of the 2D homography matrix that uses 8 parameters so
|
||||
* that the matrix is normalized (H(2,2) == 1).
|
||||
* The homography matrix H is built from a list of 8 parameters (a, b,...g, h)
|
||||
* as follows
|
||||
* |a b c|
|
||||
* H = |d e f|
|
||||
* |g h 1|
|
||||
*/
|
||||
template <typename T = double>
|
||||
class Homography2DNormalizedParameterization {
|
||||
public:
|
||||
typedef Eigen::Matrix<T, 8, 1> Parameters; // a, b, ... g, h
|
||||
typedef Eigen::Matrix<T, 3, 3> Parameterized; // H
|
||||
|
||||
/// Convert from the 8 parameters to a H matrix.
|
||||
static void To(const Parameters& p, Parameterized* h) {
|
||||
// clang-format off
|
||||
*h << p(0), p(1), p(2),
|
||||
p(3), p(4), p(5),
|
||||
p(6), p(7), 1.0;
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
/// Convert from a H matrix to the 8 parameters.
|
||||
static void From(const Parameterized& h, Parameters* p) {
|
||||
// clang-format off
|
||||
*p << h(0, 0), h(0, 1), h(0, 2),
|
||||
h(1, 0), h(1, 1), h(1, 2),
|
||||
h(2, 0), h(2, 1);
|
||||
// clang-format on
|
||||
}
|
||||
};
|
||||
|
||||
/** A parameterization of the 2D homography matrix that uses 15 parameters so
|
||||
* that the matrix is normalized (H(3,3) == 1).
|
||||
* The homography matrix H is built from a list of 15 parameters (a, b,...n, o)
|
||||
* as follows
|
||||
* |a b c d|
|
||||
* H = |e f g h|
|
||||
* |i j k l|
|
||||
* |m n o 1|
|
||||
*/
|
||||
template <typename T = double>
|
||||
class Homography3DNormalizedParameterization {
|
||||
public:
|
||||
typedef Eigen::Matrix<T, 15, 1> Parameters; // a, b, ... n, o
|
||||
typedef Eigen::Matrix<T, 4, 4> Parameterized; // H
|
||||
|
||||
/// Convert from the 15 parameters to a H matrix.
|
||||
static void To(const Parameters& p, Parameterized* h) {
|
||||
// clang-format off
|
||||
*h << p(0), p(1), p(2), p(3),
|
||||
p(4), p(5), p(6), p(7),
|
||||
p(8), p(9), p(10), p(11),
|
||||
p(12), p(13), p(14), 1.0;
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
/// Convert from a H matrix to the 15 parameters.
|
||||
static void From(const Parameterized& h, Parameters* p) {
|
||||
// clang-format off
|
||||
*p << h(0, 0), h(0, 1), h(0, 2), h(0, 3),
|
||||
h(1, 0), h(1, 1), h(1, 2), h(1, 3),
|
||||
h(2, 0), h(2, 1), h(2, 2), h(2, 3),
|
||||
h(3, 0), h(3, 1), h(3, 2);
|
||||
// clang-format on
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace libmv
|
||||
|
||||
#endif // LIBMV_MULTIVIEW_HOMOGRAPHY_PARAMETERIZATION_H_
|
||||
294
blender-5.2.0/intern/libmv/libmv/multiview/homography_test.cc
Normal file
294
blender-5.2.0/intern/libmv/libmv/multiview/homography_test.cc
Normal file
@@ -0,0 +1,294 @@
|
||||
// Copyright (c) 2011 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/multiview/homography.h"
|
||||
#include "libmv/logging/logging.h"
|
||||
#include "libmv/multiview/projection.h"
|
||||
#include "testing/testing.h"
|
||||
|
||||
namespace {
|
||||
using namespace libmv;
|
||||
|
||||
namespace {
|
||||
|
||||
// Check whether homography transform M actually transforms
|
||||
// given vectors x1 to x2. Used to check validness of a reconstructed
|
||||
// homography matrix.
|
||||
// TODO(sergey): Consider using this in all tests since possible homography
|
||||
// matrix is not fixed to a single value and different-looking matrices
|
||||
// might actually crrespond to the same exact transform.
|
||||
void CheckHomography2DTransform(const Mat3& H, const Mat& x1, const Mat& x2) {
|
||||
for (int i = 0; i < x2.cols(); ++i) {
|
||||
Vec3 x2_expected = x2.col(i);
|
||||
Vec3 x2_observed = H * x1.col(i);
|
||||
x2_observed /= x2_observed(2);
|
||||
EXPECT_MATRIX_NEAR(x2_expected, x2_observed, 1e-8);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST(Homography2DTest, Rotation45AndTranslationXY) {
|
||||
Mat x1(3, 4);
|
||||
// clang-format off
|
||||
x1 << 0, 1, 0, 5,
|
||||
0, 0, 2, 3,
|
||||
1, 1, 1, 1;
|
||||
// clang-format on
|
||||
|
||||
double angle = 45.0;
|
||||
Mat3 m;
|
||||
// clang-format off
|
||||
m << cos(angle), -sin(angle), -2,
|
||||
sin(angle), cos(angle), 5,
|
||||
0, 0, 1;
|
||||
// clang-format on
|
||||
|
||||
Mat x2 = x1;
|
||||
// Transform point from ground truth matrix
|
||||
for (int i = 0; i < x2.cols(); ++i) {
|
||||
x2.col(i) = m * x1.col(i);
|
||||
}
|
||||
|
||||
Mat3 homography_mat;
|
||||
EXPECT_TRUE(Homography2DFromCorrespondencesLinear(x1, x2, &homography_mat));
|
||||
VLOG(1) << "Mat Homography2D ";
|
||||
VLOG(1) << homography_mat;
|
||||
VLOG(1) << "Mat GT ";
|
||||
VLOG(1) << m;
|
||||
EXPECT_MATRIX_NEAR(homography_mat, m, 1e-8);
|
||||
}
|
||||
|
||||
TEST(Homography2DTest, AffineGeneral4) {
|
||||
// TODO(julien) find why it doesn't work with 4 points!!!
|
||||
Mat x1(3, 4);
|
||||
// clang-format off
|
||||
x1 << 0, 1, 0, 2,
|
||||
0, 0, 1, 2,
|
||||
1, 1, 1, 1;
|
||||
// clang-format on
|
||||
Mat3 m;
|
||||
// clang-format off
|
||||
m << 3, -1, 4,
|
||||
6, -2, -3,
|
||||
0, 0, 1;
|
||||
// clang-format on
|
||||
|
||||
Mat x2 = x1;
|
||||
for (int i = 0; i < x2.cols(); ++i) {
|
||||
x2.col(i) = m * x1.col(i);
|
||||
}
|
||||
|
||||
Mat3 homography_mat;
|
||||
EXPECT_TRUE(Homography2DFromCorrespondencesLinear(x1, x2, &homography_mat));
|
||||
VLOG(1) << "Mat Homography2D";
|
||||
VLOG(1) << homography_mat;
|
||||
CheckHomography2DTransform(homography_mat, x1, x2);
|
||||
|
||||
// Test with euclidean coordinates
|
||||
Mat eX1, eX2;
|
||||
HomogeneousToEuclidean(x1, &eX1);
|
||||
HomogeneousToEuclidean(x2, &eX2);
|
||||
homography_mat.setIdentity();
|
||||
EXPECT_TRUE(Homography2DFromCorrespondencesLinear(eX1, eX2, &homography_mat));
|
||||
|
||||
VLOG(1) << "Mat Homography2D ";
|
||||
VLOG(1) << homography_mat;
|
||||
CheckHomography2DTransform(homography_mat, x1, x2);
|
||||
}
|
||||
|
||||
TEST(Homography2DTest, AffineGeneral5) {
|
||||
Mat x1(3, 5);
|
||||
// clang-format off
|
||||
x1 << 0, 1, 0, 2, 5,
|
||||
0, 0, 1, 2, 2,
|
||||
1, 1, 1, 1, 1;
|
||||
// clang-format on
|
||||
Mat3 m;
|
||||
// clang-format off
|
||||
m << 3, -1, 4,
|
||||
6, -2, -3,
|
||||
0, 0, 1;
|
||||
// clang-format on
|
||||
|
||||
Mat x2 = x1;
|
||||
for (int i = 0; i < x2.cols(); ++i) {
|
||||
x2.col(i) = m * x1.col(i);
|
||||
}
|
||||
|
||||
Mat3 homography_mat;
|
||||
EXPECT_TRUE(Homography2DFromCorrespondencesLinear(x1, x2, &homography_mat));
|
||||
|
||||
VLOG(1) << "Mat Homography2D ";
|
||||
VLOG(1) << homography_mat;
|
||||
EXPECT_MATRIX_NEAR(homography_mat, m, 1e-8);
|
||||
|
||||
// Test with euclidean coordinates
|
||||
Mat eX1, eX2;
|
||||
HomogeneousToEuclidean(x1, &eX1);
|
||||
HomogeneousToEuclidean(x2, &eX2);
|
||||
homography_mat.setIdentity();
|
||||
EXPECT_TRUE(Homography2DFromCorrespondencesLinear(eX1, eX2, &homography_mat));
|
||||
|
||||
VLOG(1) << "Mat Homography2D ";
|
||||
VLOG(1) << homography_mat;
|
||||
EXPECT_MATRIX_NEAR(homography_mat, m, 1e-8);
|
||||
}
|
||||
|
||||
TEST(Homography2DTest, HomographyGeneral) {
|
||||
Mat x1(3, 4);
|
||||
// clang-format off
|
||||
x1 << 0, 1, 0, 5,
|
||||
0, 0, 2, 3,
|
||||
1, 1, 1, 1;
|
||||
// clang-format on
|
||||
Mat3 m;
|
||||
// clang-format off
|
||||
m << 3, -1, 4,
|
||||
6, -2, -3,
|
||||
1, -3, 1;
|
||||
// clang-format on
|
||||
|
||||
Mat x2 = x1;
|
||||
for (int i = 0; i < x2.cols(); ++i) {
|
||||
x2.col(i) = m * x1.col(i);
|
||||
}
|
||||
|
||||
Mat3 homography_mat;
|
||||
EXPECT_TRUE(Homography2DFromCorrespondencesLinear(x1, x2, &homography_mat));
|
||||
|
||||
VLOG(1) << "Mat Homography2D ";
|
||||
VLOG(1) << homography_mat;
|
||||
EXPECT_MATRIX_NEAR(homography_mat, m, 1e-8);
|
||||
}
|
||||
|
||||
TEST(Homography3DTest, RotationAndTranslationXYZ) {
|
||||
Mat x1(4, 5);
|
||||
// clang-format off
|
||||
x1 << 0, 0, 1, 5, 2,
|
||||
0, 1, 2, 3, 5,
|
||||
0, 2, 0, 1, 5,
|
||||
1, 1, 1, 1, 1;
|
||||
// clang-format on
|
||||
Mat4 M;
|
||||
M.setIdentity();
|
||||
/*
|
||||
M = AngleAxisd(45.0, Vector3f::UnitZ())
|
||||
* AngleAxisd(25.0, Vector3f::UnitX())
|
||||
* AngleAxisd(5.0, Vector3f::UnitZ());*/
|
||||
|
||||
// Rotation on x + translation
|
||||
double angle = 45.0;
|
||||
Mat4 rot;
|
||||
// clang-format off
|
||||
rot << 1, 0, 0, 1,
|
||||
0, cos(angle), -sin(angle), 3,
|
||||
0, sin(angle), cos(angle), -2,
|
||||
0, 0, 0, 1;
|
||||
// clang-format on
|
||||
M *= rot;
|
||||
// Rotation on y
|
||||
angle = 25.0;
|
||||
// clang-format off
|
||||
rot << cos(angle), 0, sin(angle), 0,
|
||||
0, 1, 0, 0,
|
||||
-sin(angle), 0, cos(angle), 0,
|
||||
0, 0, 0, 1;
|
||||
// clang-format on
|
||||
M *= rot;
|
||||
// Rotation on z
|
||||
angle = 5.0;
|
||||
// clang-format off
|
||||
rot << cos(angle), -sin(angle), 0, 0,
|
||||
sin(angle), cos(angle), 0, 0,
|
||||
0, 0, 1, 0,
|
||||
0, 0, 0, 1;
|
||||
// clang-format on
|
||||
M *= rot;
|
||||
Mat x2 = x1;
|
||||
for (int i = 0; i < x2.cols(); ++i) {
|
||||
x2.col(i) = M * x1.col(i);
|
||||
}
|
||||
|
||||
Mat4 homography_mat;
|
||||
EXPECT_TRUE(Homography3DFromCorrespondencesLinear(x1, x2, &homography_mat));
|
||||
|
||||
VLOG(1) << "Mat Homography3D " << homography_mat;
|
||||
VLOG(1) << "Mat GT " << M;
|
||||
EXPECT_MATRIX_NEAR(homography_mat, M, 1e-8);
|
||||
}
|
||||
|
||||
TEST(Homography3DTest, AffineGeneral) {
|
||||
Mat x1(4, 5);
|
||||
// clang-format off
|
||||
x1 << 0, 0, 1, 5, 2,
|
||||
0, 1, 2, 3, 5,
|
||||
0, 2, 0, 1, 5,
|
||||
1, 1, 1, 1, 1;
|
||||
// clang-format on
|
||||
Mat4 m;
|
||||
// clang-format off
|
||||
m << 3, -1, 4, 1,
|
||||
6, -2, -3, -6,
|
||||
1, 0, 1, 2,
|
||||
0, 0, 0, 1;
|
||||
// clang-format on
|
||||
|
||||
Mat x2 = x1;
|
||||
for (int i = 0; i < x2.cols(); ++i) {
|
||||
x2.col(i) = m * x1.col(i);
|
||||
}
|
||||
|
||||
Mat4 homography_mat;
|
||||
EXPECT_TRUE(Homography3DFromCorrespondencesLinear(x1, x2, &homography_mat));
|
||||
VLOG(1) << "Mat Homography3D ";
|
||||
VLOG(1) << homography_mat;
|
||||
EXPECT_MATRIX_NEAR(homography_mat, m, 1e-8);
|
||||
}
|
||||
|
||||
TEST(Homography3DTest, HomographyGeneral) {
|
||||
Mat x1(4, 5);
|
||||
// clang-format off
|
||||
x1 << 0, 0, 1, 5, 2,
|
||||
0, 1, 2, 3, 5,
|
||||
0, 2, 0, 1, 5,
|
||||
1, 1, 1, 1, 1;
|
||||
// clang-format on
|
||||
Mat4 m;
|
||||
// clang-format off
|
||||
m << 3, -1, 4, 1,
|
||||
6, -2, -3, -6,
|
||||
1, 0, 1, 2,
|
||||
-3, 1, 0, 1;
|
||||
// clang-format on
|
||||
|
||||
Mat x2 = x1;
|
||||
for (int i = 0; i < x2.cols(); ++i) {
|
||||
x2.col(i) = m * x1.col(i);
|
||||
}
|
||||
|
||||
Mat4 homography_mat;
|
||||
EXPECT_TRUE(Homography3DFromCorrespondencesLinear(x1, x2, &homography_mat));
|
||||
VLOG(1) << "Mat Homography3D";
|
||||
VLOG(1) << homography_mat;
|
||||
EXPECT_MATRIX_NEAR(homography_mat, m, 1e-8);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -0,0 +1,80 @@
|
||||
// Copyright (c) 2009 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.
|
||||
//
|
||||
// Compute a 3D position of a point from several images of it. In particular,
|
||||
// compute the projective point X in R^4 such that x = PX.
|
||||
//
|
||||
// Algorithm is the standard DLT; for derivation see appendix of Keir's thesis.
|
||||
|
||||
#ifndef LIBMV_MULTIVIEW_NVIEWTRIANGULATION_H
|
||||
#define LIBMV_MULTIVIEW_NVIEWTRIANGULATION_H
|
||||
|
||||
#include "libmv/base/vector.h"
|
||||
#include "libmv/logging/logging.h"
|
||||
#include "libmv/numeric/numeric.h"
|
||||
|
||||
namespace libmv {
|
||||
|
||||
// x's are 2D coordinates (x,y,1) in each image; Ps are projective cameras. The
|
||||
// output, X, is a homogeneous four vectors.
|
||||
template <typename T>
|
||||
void NViewTriangulate(const Matrix<T, 2, Dynamic>& x,
|
||||
const vector<Matrix<T, 3, 4>>& Ps,
|
||||
Matrix<T, 4, 1>* X) {
|
||||
int nviews = x.cols();
|
||||
assert(nviews == Ps.size());
|
||||
|
||||
Matrix<T, Dynamic, Dynamic> design(3 * nviews, 4 + nviews);
|
||||
design.setConstant(0.0);
|
||||
for (int i = 0; i < nviews; i++) {
|
||||
design.template block<3, 4>(3 * i, 0) = -Ps[i];
|
||||
design(3 * i + 0, 4 + i) = x(0, i);
|
||||
design(3 * i + 1, 4 + i) = x(1, i);
|
||||
design(3 * i + 2, 4 + i) = 1.0;
|
||||
}
|
||||
Matrix<T, Dynamic, 1> X_and_alphas;
|
||||
Nullspace(&design, &X_and_alphas);
|
||||
X->resize(4);
|
||||
*X = X_and_alphas.head(4);
|
||||
}
|
||||
|
||||
// x's are 2D coordinates (x,y,1) in each image; Ps are projective cameras. The
|
||||
// output, X, is a homogeneous four vectors.
|
||||
// This method uses the algebraic distance approximation.
|
||||
// Note that this method works better when the 2D points are normalized
|
||||
// with an isotopic normalization.
|
||||
template <typename T>
|
||||
void NViewTriangulateAlgebraic(const Matrix<T, 2, Dynamic>& x,
|
||||
const vector<Matrix<T, 3, 4>>& Ps,
|
||||
Matrix<T, 4, 1>* X) {
|
||||
int nviews = x.cols();
|
||||
assert(nviews == Ps.size());
|
||||
|
||||
Matrix<T, Dynamic, 4> design(2 * nviews, 4);
|
||||
for (int i = 0; i < nviews; i++) {
|
||||
design.template block<2, 4>(2 * i, 0) = SkewMatMinimal(x.col(i)) * Ps[i];
|
||||
}
|
||||
X->resize(4);
|
||||
Nullspace(&design, X);
|
||||
}
|
||||
|
||||
} // namespace libmv
|
||||
|
||||
#endif // LIBMV_MULTIVIEW_RESECTION_H
|
||||
@@ -0,0 +1,94 @@
|
||||
// Copyright (c) 2009 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 <iostream>
|
||||
|
||||
#include "libmv/base/vector.h"
|
||||
#include "libmv/logging/logging.h"
|
||||
#include "libmv/multiview/nviewtriangulation.h"
|
||||
#include "libmv/multiview/projection.h"
|
||||
#include "libmv/multiview/test_data_sets.h"
|
||||
#include "libmv/numeric/numeric.h"
|
||||
#include "testing/testing.h"
|
||||
|
||||
namespace {
|
||||
|
||||
using namespace libmv;
|
||||
|
||||
TEST(NViewTriangulate, FiveViews) {
|
||||
int nviews = 5;
|
||||
int npoints = 6;
|
||||
NViewDataSet d = NRealisticCamerasFull(nviews, npoints);
|
||||
|
||||
// Collect P matrices together.
|
||||
vector<Mat34> Ps(nviews);
|
||||
for (int j = 0; j < nviews; ++j) {
|
||||
Ps[j] = d.P(j);
|
||||
}
|
||||
|
||||
for (int i = 0; i < npoints; ++i) {
|
||||
// Collect the image of point i in each frame.
|
||||
Mat2X xs(2, nviews);
|
||||
for (int j = 0; j < nviews; ++j) {
|
||||
xs.col(j) = d.x[j].col(i);
|
||||
}
|
||||
Vec4 X;
|
||||
NViewTriangulate(xs, Ps, &X);
|
||||
|
||||
// Check reprojection error. Should be nearly zero.
|
||||
for (int j = 0; j < nviews; ++j) {
|
||||
Vec3 x_reprojected = Ps[j] * X;
|
||||
x_reprojected /= x_reprojected(2);
|
||||
double error = (x_reprojected.head(2) - xs.col(j)).norm();
|
||||
EXPECT_NEAR(error, 0.0, 1e-9);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(NViewTriangulateAlgebraic, FiveViews) {
|
||||
int nviews = 5;
|
||||
int npoints = 6;
|
||||
NViewDataSet d = NRealisticCamerasFull(nviews, npoints);
|
||||
|
||||
// Collect P matrices together.
|
||||
vector<Mat34> Ps(nviews);
|
||||
for (int j = 0; j < nviews; ++j) {
|
||||
Ps[j] = d.P(j);
|
||||
}
|
||||
|
||||
for (int i = 0; i < npoints; ++i) {
|
||||
// Collect the image of point i in each frame.
|
||||
Mat2X xs(2, nviews);
|
||||
for (int j = 0; j < nviews; ++j) {
|
||||
xs.col(j) = d.x[j].col(i);
|
||||
}
|
||||
Vec4 X;
|
||||
NViewTriangulate(xs, Ps, &X);
|
||||
|
||||
// Check reprojection error. Should be nearly zero.
|
||||
for (int j = 0; j < nviews; ++j) {
|
||||
Vec3 x_reprojected = Ps[j] * X;
|
||||
x_reprojected /= x_reprojected(2);
|
||||
double error = (x_reprojected.head<2>() - xs.col(j)).norm();
|
||||
EXPECT_NEAR(error, 0.0, 1e-9);
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
130
blender-5.2.0/intern/libmv/libmv/multiview/panography.cc
Normal file
130
blender-5.2.0/intern/libmv/libmv/multiview/panography.cc
Normal file
@@ -0,0 +1,130 @@
|
||||
// Copyright (c) 2009 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/multiview/panography.h"
|
||||
|
||||
namespace libmv {
|
||||
|
||||
static bool Build_Minimal2Point_PolynomialFactor(
|
||||
const Mat& x1,
|
||||
const Mat& x2,
|
||||
double* P) { // P must be a double[4]
|
||||
assert(2 == x1.rows());
|
||||
assert(2 == x1.cols());
|
||||
assert(x1.rows() == x2.rows());
|
||||
assert(x1.cols() == x2.cols());
|
||||
|
||||
// Setup the variable of the input problem:
|
||||
Vec xx1 = (x1.col(0)).transpose();
|
||||
Vec yx1 = (x1.col(1)).transpose();
|
||||
|
||||
double a12 = xx1.dot(yx1);
|
||||
Vec xx2 = (x2.col(0)).transpose();
|
||||
Vec yx2 = (x2.col(1)).transpose();
|
||||
double b12 = xx2.dot(yx2);
|
||||
|
||||
double a1 = xx1.squaredNorm();
|
||||
double a2 = yx1.squaredNorm();
|
||||
|
||||
double b1 = xx2.squaredNorm();
|
||||
double b2 = yx2.squaredNorm();
|
||||
|
||||
// Build the 3rd degre polynomial in F^2.
|
||||
//
|
||||
// f^6 * p + f^4 * q + f^2* r + s = 0;
|
||||
//
|
||||
// Coefficients in ascending powers of alpha, i.e. P[N]*x^N.
|
||||
// Run panography_coeffs.py to get the below coefficients.
|
||||
P[0] = b1 * b2 * a12 * a12 - a1 * a2 * b12 * b12;
|
||||
P[1] = -2 * a1 * a2 * b12 + 2 * a12 * b1 * b2 + b1 * a12 * a12 +
|
||||
b2 * a12 * a12 - a1 * b12 * b12 - a2 * b12 * b12;
|
||||
P[2] = b1 * b2 - a1 * a2 - 2 * a1 * b12 - 2 * a2 * b12 + 2 * a12 * b1 +
|
||||
2 * a12 * b2 + a12 * a12 - b12 * b12;
|
||||
P[3] = b1 + b2 - 2 * b12 - a1 - a2 + 2 * a12;
|
||||
|
||||
// If P[3] equal to 0 we get ill conditionned data
|
||||
return (P[3] != 0.0);
|
||||
}
|
||||
|
||||
// This implements a minimal solution (2 points) for panoramic stitching:
|
||||
//
|
||||
// http://www.cs.ubc.ca/~mbrown/minimal/minimal.html
|
||||
//
|
||||
// [1] M. Brown and R. Hartley and D. Nister. Minimal Solutions for Panoramic
|
||||
// Stitching. CVPR07.
|
||||
void F_FromCorrespondance_2points(const Mat& x1,
|
||||
const Mat& x2,
|
||||
vector<double>* fs) {
|
||||
// Build Polynomial factor to get squared focal value.
|
||||
double P[4];
|
||||
Build_Minimal2Point_PolynomialFactor(x1, x2, &P[0]);
|
||||
|
||||
// Solve it by using F = f^2 and a Cubic polynomial solver
|
||||
//
|
||||
// F^3 * p + F^2 * q + F^1 * r + s = 0
|
||||
//
|
||||
double roots[3];
|
||||
int num_roots = SolveCubicPolynomial(P, roots);
|
||||
for (int i = 0; i < num_roots; ++i) {
|
||||
if (roots[i] > 0.0) {
|
||||
fs->push_back(sqrt(roots[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Compute the 3x3 rotation matrix that fits two 3D point clouds in the least
|
||||
// square sense. The method is from:
|
||||
//
|
||||
// K. Arun,T. Huand and D. Blostein. Least-squares fitting of 2 3-D point
|
||||
// sets. IEEE Transactions on Pattern Analysis and Machine Intelligence,
|
||||
// 9:698-700, 1987.
|
||||
void GetR_FixedCameraCenter(const Mat& x1,
|
||||
const Mat& x2,
|
||||
const double focal,
|
||||
Mat3* R) {
|
||||
assert(3 == x1.rows());
|
||||
assert(2 <= x1.cols());
|
||||
assert(x1.rows() == x2.rows());
|
||||
assert(x1.cols() == x2.cols());
|
||||
|
||||
// Build simplified K matrix
|
||||
Mat3 K(Mat3::Identity() * 1.0 / focal);
|
||||
K(2, 2) = 1.0;
|
||||
|
||||
// Build the correlation matrix; equation (22) in [1].
|
||||
Mat3 C = Mat3::Zero();
|
||||
for (int i = 0; i < x1.cols(); ++i) {
|
||||
Mat r1i = (K * x1.col(i)).normalized();
|
||||
Mat r2i = (K * x2.col(i)).normalized();
|
||||
C += r2i * r1i.transpose();
|
||||
}
|
||||
|
||||
// Solve for rotation. Equations (24) and (25) in [1].
|
||||
Eigen::JacobiSVD<Mat> svd(C, Eigen::ComputeThinU | Eigen::ComputeThinV);
|
||||
Mat3 scale = Mat3::Identity();
|
||||
scale(2, 2) =
|
||||
((svd.matrixU() * svd.matrixV().transpose()).determinant() > 0.0) ? 1.0
|
||||
: -1.0;
|
||||
|
||||
(*R) = svd.matrixU() * scale * svd.matrixV().transpose();
|
||||
}
|
||||
|
||||
} // namespace libmv
|
||||
101
blender-5.2.0/intern/libmv/libmv/multiview/panography.h
Normal file
101
blender-5.2.0/intern/libmv/libmv/multiview/panography.h
Normal file
@@ -0,0 +1,101 @@
|
||||
// Copyright (c) 2009 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.
|
||||
//
|
||||
|
||||
#ifndef LIBMV_MULTIVIEW_PANOGRAPHY_H
|
||||
#define LIBMV_MULTIVIEW_PANOGRAPHY_H
|
||||
|
||||
#include "libmv/base/vector.h"
|
||||
#include "libmv/numeric/numeric.h"
|
||||
#include "libmv/numeric/poly.h"
|
||||
|
||||
namespace libmv {
|
||||
|
||||
// This implements a minimal solution (2 points) for panoramic stitching:
|
||||
//
|
||||
// http://www.cs.ubc.ca/~mbrown/minimal/minimal.html
|
||||
//
|
||||
// [1] M. Brown and R. Hartley and D. Nister. Minimal Solutions for Panoramic
|
||||
// Stitching. CVPR07.
|
||||
//
|
||||
// The 2-point algorithm solves for the rotation of the camera with a single
|
||||
// focal length (4 degrees of freedom).
|
||||
//
|
||||
// Compute from 1 to 3 possible focal length for 2 point correspondences.
|
||||
// Suppose that the cameras share the same optical center and focal lengths:
|
||||
//
|
||||
// Image 1 => H*x = x' => Image 2
|
||||
// x (u1j) x' (u2j)
|
||||
// a (u11) a' (u21)
|
||||
// b (u12) b' (u22)
|
||||
//
|
||||
// The return values are 1 to 3 possible values for the focal lengths such
|
||||
// that:
|
||||
//
|
||||
// [f 0 0]
|
||||
// K = [0 f 0]
|
||||
// [0 0 1]
|
||||
//
|
||||
void F_FromCorrespondance_2points(const Mat& x1,
|
||||
const Mat& x2,
|
||||
vector<double>* fs);
|
||||
|
||||
// Compute the 3x3 rotation matrix that fits two 3D point clouds in the least
|
||||
// square sense. The method is from:
|
||||
//
|
||||
// K. Arun,T. Huand and D. Blostein. Least-squares fitting of 2 3-D point
|
||||
// sets. IEEE Transactions on Pattern Analysis and Machine Intelligence,
|
||||
// 9:698-700, 1987.
|
||||
//
|
||||
// Given the calibration matrices K1, K2 solve for the rotation from
|
||||
// corresponding image rays.
|
||||
//
|
||||
// R = min || X2 - R * x1 ||.
|
||||
//
|
||||
// In case of panography, which is for a camera that shares the same camera
|
||||
// center,
|
||||
//
|
||||
// H = K2 * R * K1.inverse();
|
||||
//
|
||||
// For the full explanation, see Section 8, Solving for Rotation from [1].
|
||||
//
|
||||
// Parameters:
|
||||
//
|
||||
// x1 : Point cloud A (3D coords)
|
||||
// x2 : Point cloud B (3D coords)
|
||||
//
|
||||
// [f 0 0]
|
||||
// K1 = [0 f 0]
|
||||
// [0 0 1]
|
||||
//
|
||||
// K2 (the same form as K1, but may have different f)
|
||||
//
|
||||
// Returns: A rotation matrix that minimizes
|
||||
//
|
||||
// R = arg min || X2 - R * x1 ||
|
||||
//
|
||||
void GetR_FixedCameraCenter(const Mat& x1,
|
||||
const Mat& x2,
|
||||
const double focal,
|
||||
Mat3* R);
|
||||
|
||||
} // namespace libmv
|
||||
|
||||
#endif // LIBMV_MULTIVIEW_PANOGRAPHY_H
|
||||
@@ -0,0 +1,51 @@
|
||||
// Copyright (c) 2008, 2009 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/multiview/panography_kernel.h"
|
||||
#include "libmv/multiview/panography.h"
|
||||
|
||||
namespace libmv {
|
||||
namespace panography {
|
||||
namespace kernel {
|
||||
|
||||
void TwoPointSolver::Solve(const Mat& x1, const Mat& x2, vector<Mat3>* Hs) {
|
||||
// Solve for the focal lengths.
|
||||
vector<double> fs;
|
||||
F_FromCorrespondance_2points(x1, x2, &fs);
|
||||
|
||||
// Then solve for the rotations and homographies.
|
||||
Mat x1h, x2h;
|
||||
EuclideanToHomogeneous(x1, &x1h);
|
||||
EuclideanToHomogeneous(x2, &x2h);
|
||||
for (int i = 0; i < fs.size(); ++i) {
|
||||
Mat3 K1 = Mat3::Identity() * fs[i];
|
||||
K1(2, 2) = 1.0;
|
||||
|
||||
Mat3 R;
|
||||
GetR_FixedCameraCenter(x1h, x2h, fs[i], &R);
|
||||
R /= R(2, 2);
|
||||
|
||||
(*Hs).push_back(K1 * R * K1.inverse());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace kernel
|
||||
} // namespace panography
|
||||
} // namespace libmv
|
||||
@@ -0,0 +1,54 @@
|
||||
// Copyright (c) 2009 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.
|
||||
|
||||
#ifndef LIBMV_MULTIVIEW_PANOGRAPHY_KERNEL_H
|
||||
#define LIBMV_MULTIVIEW_PANOGRAPHY_KERNEL_H
|
||||
|
||||
#include "libmv/base/vector.h"
|
||||
#include "libmv/multiview/conditioning.h"
|
||||
#include "libmv/multiview/homography_error.h"
|
||||
#include "libmv/multiview/projection.h"
|
||||
#include "libmv/multiview/two_view_kernel.h"
|
||||
#include "libmv/numeric/numeric.h"
|
||||
|
||||
namespace libmv {
|
||||
namespace panography {
|
||||
namespace kernel {
|
||||
|
||||
struct TwoPointSolver {
|
||||
enum { MINIMUM_SAMPLES = 2 };
|
||||
static void Solve(const Mat& x1, const Mat& x2, vector<Mat3>* Hs);
|
||||
};
|
||||
|
||||
typedef two_view::kernel::
|
||||
Kernel<TwoPointSolver, homography::homography2D::AsymmetricError, Mat3>
|
||||
UnnormalizedKernel;
|
||||
|
||||
typedef two_view::kernel::Kernel<
|
||||
two_view::kernel::NormalizedSolver<TwoPointSolver, UnnormalizerI>,
|
||||
homography::homography2D::AsymmetricError,
|
||||
Mat3>
|
||||
Kernel;
|
||||
|
||||
} // namespace kernel
|
||||
} // namespace panography
|
||||
} // namespace libmv
|
||||
|
||||
#endif // LIBMV_MULTIVIEW_PANOGRAPHY_KERNEL_H
|
||||
154
blender-5.2.0/intern/libmv/libmv/multiview/panography_test.cc
Normal file
154
blender-5.2.0/intern/libmv/libmv/multiview/panography_test.cc
Normal file
@@ -0,0 +1,154 @@
|
||||
// Copyright (c) 2009 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/multiview/panography.h"
|
||||
#include "libmv/logging/logging.h"
|
||||
#include "libmv/multiview/panography_kernel.h"
|
||||
#include "libmv/multiview/projection.h"
|
||||
#include "libmv/numeric/numeric.h"
|
||||
#include "testing/testing.h"
|
||||
|
||||
namespace libmv {
|
||||
namespace {
|
||||
|
||||
TEST(Panography, PrintSomeSharedFocalEstimationValues) {
|
||||
Mat x1(2, 2), x2(2, 2);
|
||||
x1 << 158, 78, 124, 113;
|
||||
x2 << 300, 214, 125, 114;
|
||||
|
||||
// Normalize data (set principal point 0,0 and image border to 1.0).
|
||||
x1.block<1, 2>(0, 0) /= 320;
|
||||
x1.block<1, 2>(1, 0) /= 240;
|
||||
x2.block<1, 2>(0, 0) /= 320;
|
||||
x2.block<1, 2>(1, 0) /= 240;
|
||||
x1 += Mat2::Constant(0.5);
|
||||
x2 += Mat2::Constant(0.5);
|
||||
|
||||
vector<double> fs;
|
||||
F_FromCorrespondance_2points(x1, x2, &fs);
|
||||
|
||||
// Assert we found a valid solution.
|
||||
EXPECT_EQ(1, fs.size());
|
||||
EXPECT_NEAR(3.47194, fs[0], 1e-3);
|
||||
}
|
||||
|
||||
TEST(Panography, GetR_FixedCameraCenterWithIdentity) {
|
||||
Mat x1(3, 3);
|
||||
// clang-format off
|
||||
x1 << 0.5, 0.6, 0.7,
|
||||
0.5, 0.5, 0.4,
|
||||
10.0, 10.0, 10.0;
|
||||
// clang-format on
|
||||
|
||||
Mat3 R;
|
||||
GetR_FixedCameraCenter(x1, x1, 1.0, &R);
|
||||
R /= R(2, 2);
|
||||
EXPECT_MATRIX_NEAR(Mat3::Identity(), R, 1e-8);
|
||||
LOG(INFO) << "R \n" << R;
|
||||
}
|
||||
|
||||
TEST(Panography, Homography_GetR_Test_PitchY30) {
|
||||
int n = 3;
|
||||
|
||||
Mat x1(3, n);
|
||||
// clang-format off
|
||||
x1 << 0.5, 0.6, 0.7,
|
||||
0.5, 0.5, 0.4,
|
||||
10, 10, 10;
|
||||
// clang-format on
|
||||
|
||||
Mat x2 = x1;
|
||||
const double alpha = 30.0 * M_PI / 180.0;
|
||||
Mat3 rotY;
|
||||
// clang-format off
|
||||
rotY << cos(alpha), 0, -sin(alpha),
|
||||
0, 1, 0,
|
||||
sin(alpha), 0, cos(alpha);
|
||||
// clang-format on
|
||||
|
||||
for (int i = 0; i < n; ++i) {
|
||||
x2.block<3, 1>(0, i) = rotY * x1.col(i);
|
||||
}
|
||||
|
||||
Mat3 R;
|
||||
GetR_FixedCameraCenter(x1, x2, 1.0, &R);
|
||||
|
||||
// Assert that residuals are small enough
|
||||
for (int i = 0; i < n; ++i) {
|
||||
Vec residuals = (R * x1.col(i)) - x2.col(i);
|
||||
EXPECT_NEAR(0, residuals.norm(), 1e-6);
|
||||
}
|
||||
|
||||
// Check that the rotation angle along Y is the expected one.
|
||||
// Use the euler approximation to recover the angle.
|
||||
double pitch_y = asin(R(2, 0)) * 180.0 / M_PI;
|
||||
EXPECT_NEAR(30, pitch_y, 1e-4);
|
||||
}
|
||||
|
||||
TEST(MinimalPanoramic, Real_Case_Kernel) {
|
||||
const int n = 2;
|
||||
Mat x1(2, n); // From image 0.jpg
|
||||
// clang-format off
|
||||
x1<< 158, 78,
|
||||
124, 113;
|
||||
// clang-format on
|
||||
|
||||
Mat x2(2, n); // From image 3.jpg
|
||||
// clang-format off
|
||||
x2<< 300, 214,
|
||||
125, 114;
|
||||
// clang-format on
|
||||
|
||||
Mat3 Ground_TruthHomography;
|
||||
// clang-format off
|
||||
Ground_TruthHomography<< 1, 0.02, 129.83,
|
||||
-0.02, 1.012, 0.07823,
|
||||
0, 0, 1;
|
||||
// clang-format on
|
||||
|
||||
vector<Mat3> Hs;
|
||||
|
||||
libmv::panography::kernel::TwoPointSolver::Solve(x1, x2, &Hs);
|
||||
|
||||
LOG(INFO) << "Got " << Hs.size() << " solutions.";
|
||||
for (int j = 0; j < Hs.size(); ++j) {
|
||||
Mat3 H = Hs[j];
|
||||
|
||||
EXPECT_MATRIX_NEAR(H, Ground_TruthHomography, 1e-1);
|
||||
|
||||
Mat x1h, x2h;
|
||||
EuclideanToHomogeneous(x1, &x1h);
|
||||
EuclideanToHomogeneous(x2, &x2h);
|
||||
|
||||
// Assert that residuals are small enough
|
||||
for (int i = 0; i < n; ++i) {
|
||||
Vec x1p = H * x1h.col(i);
|
||||
Vec residuals = x1p / x1p(2) - x2h.col(i);
|
||||
EXPECT_MATRIX_NEAR_ZERO(residuals, 1e-5);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace libmv
|
||||
|
||||
// TODO(pmoulon): Add a real test case based on images.
|
||||
// TODO(pmoulon): Add a check for the actual f value for the real images.
|
||||
// TODO(pmoulon): Add a test that has some inliers and outliers.
|
||||
238
blender-5.2.0/intern/libmv/libmv/multiview/projection.cc
Normal file
238
blender-5.2.0/intern/libmv/libmv/multiview/projection.cc
Normal file
@@ -0,0 +1,238 @@
|
||||
// Copyright (c) 2007, 2008 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/multiview/projection.h"
|
||||
#include "libmv/numeric/numeric.h"
|
||||
|
||||
namespace libmv {
|
||||
|
||||
void P_From_KRt(const Mat3& K, const Mat3& R, const Vec3& t, Mat34* P) {
|
||||
P->block<3, 3>(0, 0) = R;
|
||||
P->col(3) = t;
|
||||
(*P) = K * (*P);
|
||||
}
|
||||
|
||||
void KRt_From_P(const Mat34& P, Mat3* Kp, Mat3* Rp, Vec3* tp) {
|
||||
// Decompose using the RQ decomposition HZ A4.1.1 pag.579.
|
||||
Mat3 K = P.block(0, 0, 3, 3);
|
||||
|
||||
Mat3 Q;
|
||||
Q.setIdentity();
|
||||
|
||||
// Set K(2,1) to zero.
|
||||
if (K(2, 1) != 0) {
|
||||
double c = -K(2, 2);
|
||||
double s = K(2, 1);
|
||||
double l = sqrt(c * c + s * s);
|
||||
c /= l;
|
||||
s /= l;
|
||||
Mat3 Qx;
|
||||
// clang-format off
|
||||
Qx << 1, 0, 0,
|
||||
0, c, -s,
|
||||
0, s, c;
|
||||
// clang-format on
|
||||
K = K * Qx;
|
||||
Q = Qx.transpose() * Q;
|
||||
}
|
||||
// Set K(2,0) to zero.
|
||||
if (K(2, 0) != 0) {
|
||||
double c = K(2, 2);
|
||||
double s = K(2, 0);
|
||||
double l = sqrt(c * c + s * s);
|
||||
c /= l;
|
||||
s /= l;
|
||||
Mat3 Qy;
|
||||
// clang-format off
|
||||
Qy << c, 0, s,
|
||||
0, 1, 0,
|
||||
-s, 0, c;
|
||||
// clang-format on
|
||||
K = K * Qy;
|
||||
Q = Qy.transpose() * Q;
|
||||
}
|
||||
// Set K(1,0) to zero.
|
||||
if (K(1, 0) != 0) {
|
||||
double c = -K(1, 1);
|
||||
double s = K(1, 0);
|
||||
double l = sqrt(c * c + s * s);
|
||||
c /= l;
|
||||
s /= l;
|
||||
Mat3 Qz;
|
||||
// clang-format off
|
||||
Qz << c, -s, 0,
|
||||
s, c, 0,
|
||||
0, 0, 1;
|
||||
// clang-format on
|
||||
K = K * Qz;
|
||||
Q = Qz.transpose() * Q;
|
||||
}
|
||||
|
||||
Mat3 R = Q;
|
||||
|
||||
// Ensure that the diagonal is positive.
|
||||
// TODO(pau) Change this to ensure that:
|
||||
// - K(0,0) > 0
|
||||
// - K(2,2) = 1
|
||||
// - det(R) = 1
|
||||
if (K(2, 2) < 0) {
|
||||
K = -K;
|
||||
R = -R;
|
||||
}
|
||||
if (K(1, 1) < 0) {
|
||||
Mat3 S;
|
||||
// clang-format off
|
||||
S << 1, 0, 0,
|
||||
0, -1, 0,
|
||||
0, 0, 1;
|
||||
// clang-format on
|
||||
K = K * S;
|
||||
R = S * R;
|
||||
}
|
||||
if (K(0, 0) < 0) {
|
||||
Mat3 S;
|
||||
// clang-format off
|
||||
S << -1, 0, 0,
|
||||
0, 1, 0,
|
||||
0, 0, 1;
|
||||
// clang-format on
|
||||
K = K * S;
|
||||
R = S * R;
|
||||
}
|
||||
|
||||
// Compute translation.
|
||||
Vec p(3);
|
||||
p << P(0, 3), P(1, 3), P(2, 3);
|
||||
// TODO(pau) This sould be done by a SolveLinearSystem(A, b, &x) call.
|
||||
// TODO(keir) use the eigen LU solver syntax...
|
||||
Vec3 t = K.inverse() * p;
|
||||
|
||||
// scale K so that K(2,2) = 1
|
||||
K = K / K(2, 2);
|
||||
|
||||
*Kp = K;
|
||||
*Rp = R;
|
||||
*tp = t;
|
||||
}
|
||||
|
||||
void ProjectionShiftPrincipalPoint(const Mat34& P,
|
||||
const Vec2& principal_point,
|
||||
const Vec2& principal_point_new,
|
||||
Mat34* P_new) {
|
||||
Mat3 T;
|
||||
// clang-format off
|
||||
T << 1, 0, principal_point_new(0) - principal_point(0),
|
||||
0, 1, principal_point_new(1) - principal_point(1),
|
||||
0, 0, 1;
|
||||
// clang-format on
|
||||
*P_new = T * P;
|
||||
}
|
||||
|
||||
void ProjectionChangeAspectRatio(const Mat34& P,
|
||||
const Vec2& principal_point,
|
||||
double aspect_ratio,
|
||||
double aspect_ratio_new,
|
||||
Mat34* P_new) {
|
||||
Mat3 T;
|
||||
// clang-format off
|
||||
T << 1, 0, 0,
|
||||
0, aspect_ratio_new / aspect_ratio, 0,
|
||||
0, 0, 1;
|
||||
// clang-format on
|
||||
Mat34 P_temp;
|
||||
|
||||
ProjectionShiftPrincipalPoint(P, principal_point, Vec2(0, 0), &P_temp);
|
||||
P_temp = T * P_temp;
|
||||
ProjectionShiftPrincipalPoint(P_temp, Vec2(0, 0), principal_point, P_new);
|
||||
}
|
||||
|
||||
void HomogeneousToEuclidean(const Mat& H, Mat* X) {
|
||||
int d = H.rows() - 1;
|
||||
int n = H.cols();
|
||||
X->resize(d, n);
|
||||
for (size_t i = 0; i < n; ++i) {
|
||||
double h = H(d, i);
|
||||
for (int j = 0; j < d; ++j) {
|
||||
(*X)(j, i) = H(j, i) / h;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HomogeneousToEuclidean(const Mat3X& h, Mat2X* e) {
|
||||
e->resize(2, h.cols());
|
||||
e->row(0) = h.row(0).array() / h.row(2).array();
|
||||
e->row(1) = h.row(1).array() / h.row(2).array();
|
||||
}
|
||||
void HomogeneousToEuclidean(const Mat4X& h, Mat3X* e) {
|
||||
e->resize(3, h.cols());
|
||||
e->row(0) = h.row(0).array() / h.row(3).array();
|
||||
e->row(1) = h.row(1).array() / h.row(3).array();
|
||||
e->row(2) = h.row(2).array() / h.row(3).array();
|
||||
}
|
||||
|
||||
void HomogeneousToEuclidean(const Vec3& H, Vec2* X) {
|
||||
double w = H(2);
|
||||
*X << H(0) / w, H(1) / w;
|
||||
}
|
||||
|
||||
void HomogeneousToEuclidean(const Vec4& H, Vec3* X) {
|
||||
double w = H(3);
|
||||
*X << H(0) / w, H(1) / w, H(2) / w;
|
||||
}
|
||||
|
||||
void EuclideanToHomogeneous(const Mat& X, Mat* H) {
|
||||
int d = X.rows();
|
||||
int n = X.cols();
|
||||
H->resize(d + 1, n);
|
||||
H->block(0, 0, d, n) = X;
|
||||
H->row(d).setOnes();
|
||||
}
|
||||
|
||||
void EuclideanToHomogeneous(const Vec2& X, Vec3* H) {
|
||||
*H << X(0), X(1), 1;
|
||||
}
|
||||
|
||||
void EuclideanToHomogeneous(const Vec3& X, Vec4* H) {
|
||||
*H << X(0), X(1), X(2), 1;
|
||||
}
|
||||
|
||||
// TODO(julien) Call conditioning.h/ApplyTransformationToPoints ?
|
||||
void EuclideanToNormalizedCamera(const Mat2X& x, const Mat3& K, Mat2X* n) {
|
||||
Mat3X x_image_h;
|
||||
EuclideanToHomogeneous(x, &x_image_h);
|
||||
Mat3X x_camera_h = K.inverse() * x_image_h;
|
||||
HomogeneousToEuclidean(x_camera_h, n);
|
||||
}
|
||||
|
||||
void HomogeneousToNormalizedCamera(const Mat3X& x, const Mat3& K, Mat2X* n) {
|
||||
Mat3X x_camera_h = K.inverse() * x;
|
||||
HomogeneousToEuclidean(x_camera_h, n);
|
||||
}
|
||||
|
||||
double Depth(const Mat3& R, const Vec3& t, const Vec3& X) {
|
||||
return (R * X)(2) + t(2);
|
||||
}
|
||||
|
||||
double Depth(const Mat3& R, const Vec3& t, const Vec4& X) {
|
||||
Vec3 Xe = X.head<3>() / X(3);
|
||||
return Depth(R, t, Xe);
|
||||
}
|
||||
|
||||
} // namespace libmv
|
||||
232
blender-5.2.0/intern/libmv/libmv/multiview/projection.h
Normal file
232
blender-5.2.0/intern/libmv/libmv/multiview/projection.h
Normal file
@@ -0,0 +1,232 @@
|
||||
// Copyright (c) 2007, 2008 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.
|
||||
|
||||
#ifndef LIBMV_MULTIVIEW_PROJECTION_H_
|
||||
#define LIBMV_MULTIVIEW_PROJECTION_H_
|
||||
|
||||
#include "libmv/numeric/numeric.h"
|
||||
|
||||
namespace libmv {
|
||||
|
||||
void P_From_KRt(const Mat3& K, const Mat3& R, const Vec3& t, Mat34* P);
|
||||
void KRt_From_P(const Mat34& P, Mat3* K, Mat3* R, Vec3* t);
|
||||
|
||||
// Applies a change of basis to the image coordinates of the projection matrix
|
||||
// so that the principal point becomes principal_point_new.
|
||||
void ProjectionShiftPrincipalPoint(const Mat34& P,
|
||||
const Vec2& principal_point,
|
||||
const Vec2& principal_point_new,
|
||||
Mat34* P_new);
|
||||
|
||||
// Applies a change of basis to the image coordinates of the projection matrix
|
||||
// so that the aspect ratio becomes aspect_ratio_new. This is done by
|
||||
// stretching the y axis. The aspect ratio is defined as the quotient between
|
||||
// the focal length of the y and the x axis.
|
||||
void ProjectionChangeAspectRatio(const Mat34& P,
|
||||
const Vec2& principal_point,
|
||||
double aspect_ratio,
|
||||
double aspect_ratio_new,
|
||||
Mat34* P_new);
|
||||
|
||||
void HomogeneousToEuclidean(const Mat& H, Mat* X);
|
||||
void HomogeneousToEuclidean(const Mat3X& h, Mat2X* e);
|
||||
void HomogeneousToEuclidean(const Mat4X& h, Mat3X* e);
|
||||
void HomogeneousToEuclidean(const Vec3& H, Vec2* X);
|
||||
void HomogeneousToEuclidean(const Vec4& H, Vec3* X);
|
||||
inline Vec2 HomogeneousToEuclidean(const Vec3& h) {
|
||||
return h.head<2>() / h(2);
|
||||
}
|
||||
inline Vec3 HomogeneousToEuclidean(const Vec4& h) {
|
||||
return h.head<3>() / h(3);
|
||||
}
|
||||
inline Mat2X HomogeneousToEuclidean(const Mat3X& h) {
|
||||
Mat2X e(2, h.cols());
|
||||
e.row(0) = h.row(0).array() / h.row(2).array();
|
||||
e.row(1) = h.row(1).array() / h.row(2).array();
|
||||
return e;
|
||||
}
|
||||
|
||||
void EuclideanToHomogeneous(const Mat& X, Mat* H);
|
||||
inline Mat3X EuclideanToHomogeneous(const Mat2X& x) {
|
||||
Mat3X h(3, x.cols());
|
||||
h.block(0, 0, 2, x.cols()) = x;
|
||||
h.row(2).setOnes();
|
||||
return h;
|
||||
}
|
||||
inline void EuclideanToHomogeneous(const Mat2X& x, Mat3X* h) {
|
||||
h->resize(3, x.cols());
|
||||
h->block(0, 0, 2, x.cols()) = x;
|
||||
h->row(2).setOnes();
|
||||
}
|
||||
inline Mat4X EuclideanToHomogeneous(const Mat3X& x) {
|
||||
Mat4X h(4, x.cols());
|
||||
h.block(0, 0, 3, x.cols()) = x;
|
||||
h.row(3).setOnes();
|
||||
return h;
|
||||
}
|
||||
inline void EuclideanToHomogeneous(const Mat3X& x, Mat4X* h) {
|
||||
h->resize(4, x.cols());
|
||||
h->block(0, 0, 3, x.cols()) = x;
|
||||
h->row(3).setOnes();
|
||||
}
|
||||
void EuclideanToHomogeneous(const Vec2& X, Vec3* H);
|
||||
void EuclideanToHomogeneous(const Vec3& X, Vec4* H);
|
||||
inline Vec3 EuclideanToHomogeneous(const Vec2& x) {
|
||||
return Vec3(x(0), x(1), 1);
|
||||
}
|
||||
inline Vec4 EuclideanToHomogeneous(const Vec3& x) {
|
||||
return Vec4(x(0), x(1), x(2), 1);
|
||||
}
|
||||
// Conversion from image coordinates to normalized camera coordinates
|
||||
void EuclideanToNormalizedCamera(const Mat2X& x, const Mat3& K, Mat2X* n);
|
||||
void HomogeneousToNormalizedCamera(const Mat3X& x, const Mat3& K, Mat2X* n);
|
||||
|
||||
inline Vec2 Project(const Mat34& P, const Vec3& X) {
|
||||
Vec4 HX;
|
||||
HX << X, 1.0;
|
||||
Vec3 hx = P * HX;
|
||||
return hx.head<2>() / hx(2);
|
||||
}
|
||||
|
||||
inline void Project(const Mat34& P, const Vec4& X, Vec3* x) {
|
||||
*x = P * X;
|
||||
}
|
||||
|
||||
inline void Project(const Mat34& P, const Vec4& X, Vec2* x) {
|
||||
Vec3 hx = P * X;
|
||||
*x = hx.head<2>() / hx(2);
|
||||
}
|
||||
|
||||
inline void Project(const Mat34& P, const Vec3& X, Vec3* x) {
|
||||
Vec4 HX;
|
||||
HX << X, 1.0;
|
||||
Project(P, HX, x);
|
||||
}
|
||||
|
||||
inline void Project(const Mat34& P, const Vec3& X, Vec2* x) {
|
||||
Vec3 hx;
|
||||
Project(P, X, &hx);
|
||||
*x = hx.head<2>() / hx(2);
|
||||
}
|
||||
|
||||
inline void Project(const Mat34& P, const Mat4X& X, Mat2X* x) {
|
||||
x->resize(2, X.cols());
|
||||
for (int c = 0; c < X.cols(); ++c) {
|
||||
Vec3 hx = P * X.col(c);
|
||||
x->col(c) = hx.head<2>() / hx(2);
|
||||
}
|
||||
}
|
||||
|
||||
inline Mat2X Project(const Mat34& P, const Mat4X& X) {
|
||||
Mat2X x;
|
||||
Project(P, X, &x);
|
||||
return x;
|
||||
}
|
||||
|
||||
inline void Project(const Mat34& P, const Mat3X& X, Mat2X* x) {
|
||||
x->resize(2, X.cols());
|
||||
for (int c = 0; c < X.cols(); ++c) {
|
||||
Vec4 HX;
|
||||
HX << X.col(c), 1.0;
|
||||
Vec3 hx = P * HX;
|
||||
x->col(c) = hx.head<2>() / hx(2);
|
||||
}
|
||||
}
|
||||
|
||||
inline void Project(const Mat34& P, const Mat3X& X, const Vecu& ids, Mat2X* x) {
|
||||
x->resize(2, ids.size());
|
||||
Vec4 HX;
|
||||
Vec3 hx;
|
||||
for (int c = 0; c < ids.size(); ++c) {
|
||||
HX << X.col(ids[c]), 1.0;
|
||||
hx = P * HX;
|
||||
x->col(c) = hx.head<2>() / hx(2);
|
||||
}
|
||||
}
|
||||
|
||||
inline Mat2X Project(const Mat34& P, const Mat3X& X) {
|
||||
Mat2X x(2, X.cols());
|
||||
Project(P, X, &x);
|
||||
return x;
|
||||
}
|
||||
|
||||
inline Mat2X Project(const Mat34& P, const Mat3X& X, const Vecu& ids) {
|
||||
Mat2X x(2, ids.size());
|
||||
Project(P, X, ids, &x);
|
||||
return x;
|
||||
}
|
||||
|
||||
double Depth(const Mat3& R, const Vec3& t, const Vec3& X);
|
||||
double Depth(const Mat3& R, const Vec3& t, const Vec4& X);
|
||||
|
||||
/**
|
||||
* Returns true if the homogenious 3D point X is in front of
|
||||
* the camera P.
|
||||
*/
|
||||
inline bool isInFrontOfCamera(const Mat34& P, const Vec4& X) {
|
||||
double condition_1 = P.row(2).dot(X) * X[3];
|
||||
double condition_2 = X[2] * X[3];
|
||||
if (condition_1 > 0 && condition_2 > 0) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
inline bool isInFrontOfCamera(const Mat34& P, const Vec3& X) {
|
||||
Vec4 X_homo;
|
||||
X_homo.segment<3>(0) = X;
|
||||
X_homo(3) = 1;
|
||||
return isInFrontOfCamera(P, X_homo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms a 2D point from pixel image coordinates to a 2D point in
|
||||
* normalized image coordinates.
|
||||
*/
|
||||
inline Vec2 ImageToNormImageCoordinates(Mat3& Kinverse, Vec2& x) {
|
||||
Vec3 x_h = Kinverse * EuclideanToHomogeneous(x);
|
||||
return HomogeneousToEuclidean(x_h);
|
||||
}
|
||||
|
||||
/// Estimates the root mean square error (2D)
|
||||
inline double RootMeanSquareError(const Mat2X& x_image,
|
||||
const Mat4X& X_world,
|
||||
const Mat34& P) {
|
||||
size_t num_points = x_image.cols();
|
||||
Mat2X dx = Project(P, X_world) - x_image;
|
||||
return dx.norm() / num_points;
|
||||
}
|
||||
|
||||
/// Estimates the root mean square error (2D)
|
||||
inline double RootMeanSquareError(const Mat2X& x_image,
|
||||
const Mat3X& X_world,
|
||||
const Mat3& K,
|
||||
const Mat3& R,
|
||||
const Vec3& t) {
|
||||
Mat34 P;
|
||||
P_From_KRt(K, R, t, &P);
|
||||
size_t num_points = x_image.cols();
|
||||
Mat2X dx = Project(P, X_world) - x_image;
|
||||
return dx.norm() / num_points;
|
||||
}
|
||||
} // namespace libmv
|
||||
|
||||
#endif // LIBMV_MULTIVIEW_PROJECTION_H_
|
||||
125
blender-5.2.0/intern/libmv/libmv/multiview/projection_test.cc
Normal file
125
blender-5.2.0/intern/libmv/libmv/multiview/projection_test.cc
Normal file
@@ -0,0 +1,125 @@
|
||||
// Copyright (c) 2007, 2008 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 <iostream>
|
||||
|
||||
#include "libmv/multiview/projection.h"
|
||||
#include "libmv/numeric/numeric.h"
|
||||
#include "testing/testing.h"
|
||||
|
||||
namespace {
|
||||
using namespace libmv;
|
||||
|
||||
TEST(Projection, P_From_KRt) {
|
||||
Mat3 K, Kp;
|
||||
// clang-format off
|
||||
K << 10, 1, 30,
|
||||
0, 20, 40,
|
||||
0, 0, 1;
|
||||
// clang-format on
|
||||
|
||||
Mat3 R, Rp;
|
||||
// clang-format off
|
||||
R << 1, 0, 0,
|
||||
0, 1, 0,
|
||||
0, 0, 1;
|
||||
// clang-format on
|
||||
|
||||
Vec3 t, tp;
|
||||
t << 1, 2, 3;
|
||||
|
||||
Mat34 P;
|
||||
P_From_KRt(K, R, t, &P);
|
||||
KRt_From_P(P, &Kp, &Rp, &tp);
|
||||
|
||||
EXPECT_MATRIX_NEAR(K, Kp, 1e-8);
|
||||
EXPECT_MATRIX_NEAR(R, Rp, 1e-8);
|
||||
EXPECT_MATRIX_NEAR(t, tp, 1e-8);
|
||||
|
||||
// TODO(keir): Change the code to ensure det(R) == 1, which is not currently
|
||||
// the case. Also add a test for that here.
|
||||
}
|
||||
|
||||
Vec4 GetRandomPoint() {
|
||||
Vec4 X;
|
||||
X.setRandom();
|
||||
X(3) = 1;
|
||||
return X;
|
||||
}
|
||||
|
||||
TEST(Projection, isInFrontOfCamera) {
|
||||
Mat34 P;
|
||||
// clang-format off
|
||||
P << 1, 0, 0, 0,
|
||||
0, 1, 0, 0,
|
||||
0, 0, 1, 0;
|
||||
// clang-format on
|
||||
|
||||
Vec4 X_front = GetRandomPoint();
|
||||
Vec4 X_back = GetRandomPoint();
|
||||
X_front(2) = 10; /* Any point in the positive Z direction
|
||||
* where Z > 1 is in front of the camera. */
|
||||
X_back(2) = -10; /* Any point in the negative Z direction
|
||||
* is behind the camera. */
|
||||
|
||||
bool res_front = isInFrontOfCamera(P, X_front);
|
||||
bool res_back = isInFrontOfCamera(P, X_back);
|
||||
|
||||
EXPECT_TRUE(res_front);
|
||||
EXPECT_FALSE(res_back);
|
||||
}
|
||||
|
||||
TEST(AutoCalibration, ProjectionShiftPrincipalPoint) {
|
||||
Mat34 P1, P2;
|
||||
// clang-format off
|
||||
P1 << 1, 0, 0, 0,
|
||||
0, 1, 0, 0,
|
||||
0, 0, 1, 0;
|
||||
P2 << 1, 0, 3, 0,
|
||||
0, 1, 4, 0,
|
||||
0, 0, 1, 0;
|
||||
// clang-format on
|
||||
Mat34 P1_computed, P2_computed;
|
||||
ProjectionShiftPrincipalPoint(P1, Vec2(0, 0), Vec2(3, 4), &P2_computed);
|
||||
ProjectionShiftPrincipalPoint(P2, Vec2(3, 4), Vec2(0, 0), &P1_computed);
|
||||
|
||||
EXPECT_MATRIX_EQ(P1, P1_computed);
|
||||
EXPECT_MATRIX_EQ(P2, P2_computed);
|
||||
}
|
||||
|
||||
TEST(AutoCalibration, ProjectionChangeAspectRatio) {
|
||||
Mat34 P1, P2;
|
||||
// clang-format off
|
||||
P1 << 1, 0, 3, 0,
|
||||
0, 1, 4, 0,
|
||||
0, 0, 1, 0;
|
||||
P2 << 1, 0, 3, 0,
|
||||
0, 2, 4, 0,
|
||||
0, 0, 1, 0;
|
||||
// clang-format on
|
||||
Mat34 P1_computed, P2_computed;
|
||||
ProjectionChangeAspectRatio(P1, Vec2(3, 4), 1, 2, &P2_computed);
|
||||
ProjectionChangeAspectRatio(P2, Vec2(3, 4), 2, 1, &P1_computed);
|
||||
|
||||
EXPECT_MATRIX_EQ(P1, P1_computed);
|
||||
EXPECT_MATRIX_EQ(P2, P2_computed);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
62
blender-5.2.0/intern/libmv/libmv/multiview/resection.h
Normal file
62
blender-5.2.0/intern/libmv/libmv/multiview/resection.h
Normal file
@@ -0,0 +1,62 @@
|
||||
// Copyright (c) 2009 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.
|
||||
//
|
||||
// Compute the projection matrix from a set of 3D points X and their
|
||||
// projections x = PX in 2D. This is useful if a point cloud is reconstructed.
|
||||
//
|
||||
// Algorithm is the standard DLT as described in Hartley & Zisserman, page 179.
|
||||
|
||||
#ifndef LIBMV_MULTIVIEW_RESECTION_H
|
||||
#define LIBMV_MULTIVIEW_RESECTION_H
|
||||
|
||||
#include "libmv/logging/logging.h"
|
||||
#include "libmv/numeric/numeric.h"
|
||||
|
||||
namespace libmv {
|
||||
namespace resection {
|
||||
|
||||
// x's are 2D image coordinates, (x,y,1), and X's are homogeneous four vectors.
|
||||
template <typename T>
|
||||
void Resection(const Matrix<T, 2, Dynamic>& x,
|
||||
const Matrix<T, 4, Dynamic>& X,
|
||||
Matrix<T, 3, 4>* P) {
|
||||
int N = x.cols();
|
||||
assert(X.cols() == N);
|
||||
|
||||
Matrix<T, Dynamic, 12> design(2 * N, 12);
|
||||
design.setZero();
|
||||
for (int i = 0; i < N; i++) {
|
||||
T xi = x(0, i);
|
||||
T yi = x(1, i);
|
||||
// See equation (7.2) on page 179 of H&Z.
|
||||
design.template block<1, 4>(2 * i, 4) = -X.col(i).transpose();
|
||||
design.template block<1, 4>(2 * i, 8) = yi * X.col(i).transpose();
|
||||
design.template block<1, 4>(2 * i + 1, 0) = X.col(i).transpose();
|
||||
design.template block<1, 4>(2 * i + 1, 8) = -xi * X.col(i).transpose();
|
||||
}
|
||||
Matrix<T, 12, 1> p;
|
||||
Nullspace(&design, &p);
|
||||
reshape(p, 3, 4, P);
|
||||
}
|
||||
|
||||
} // namespace resection
|
||||
} // namespace libmv
|
||||
|
||||
#endif // LIBMV_MULTIVIEW_RESECTION_H
|
||||
61
blender-5.2.0/intern/libmv/libmv/multiview/resection_test.cc
Normal file
61
blender-5.2.0/intern/libmv/libmv/multiview/resection_test.cc
Normal file
@@ -0,0 +1,61 @@
|
||||
// Copyright (c) 2009 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 <iostream>
|
||||
|
||||
#include "libmv/logging/logging.h"
|
||||
#include "libmv/multiview/projection.h"
|
||||
#include "libmv/multiview/resection.h"
|
||||
#include "libmv/multiview/test_data_sets.h"
|
||||
#include "libmv/numeric/numeric.h"
|
||||
#include "testing/testing.h"
|
||||
|
||||
namespace {
|
||||
|
||||
using namespace libmv;
|
||||
using namespace libmv::resection;
|
||||
|
||||
TEST(Resection, ThreeViews) {
|
||||
int nviews = 5;
|
||||
int npoints = 6;
|
||||
NViewDataSet d = NRealisticCamerasFull(nviews, npoints);
|
||||
for (int i = 0; i < nviews; ++i) {
|
||||
Mat4X X(4, npoints);
|
||||
X.block(0, 0, 3, npoints) = d.X;
|
||||
X.row(3).setOnes();
|
||||
const Mat2X& x = d.x[i];
|
||||
Mat34 P;
|
||||
Resection(x, X, &P);
|
||||
Mat34 P_expected = d.P(i);
|
||||
|
||||
// Because the P matrices are homogeneous, it is necessary to be tricky
|
||||
// about the scale factor to make them match.
|
||||
P_expected *= 1 / P_expected.array().abs().sum();
|
||||
P *= 1 / P.array().abs().sum();
|
||||
if (!((P(0, 0) > 0 && P_expected(0, 0) > 0) ||
|
||||
(P(0, 0) < 0 && P_expected(0, 0) < 0))) {
|
||||
P *= -1;
|
||||
}
|
||||
|
||||
EXPECT_MATRIX_NEAR(P_expected, P, 1e-9);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
204
blender-5.2.0/intern/libmv/libmv/multiview/test_data_sets.cc
Normal file
204
blender-5.2.0/intern/libmv/libmv/multiview/test_data_sets.cc
Normal file
@@ -0,0 +1,204 @@
|
||||
// Copyright (c) 2007, 2008 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/multiview/test_data_sets.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include "libmv/multiview/fundamental.h"
|
||||
#include "libmv/multiview/projection.h"
|
||||
#include "libmv/numeric/numeric.h"
|
||||
|
||||
namespace libmv {
|
||||
|
||||
TwoViewDataSet TwoRealisticCameras(bool same_K) {
|
||||
TwoViewDataSet d;
|
||||
|
||||
// clang-format off
|
||||
d.K1 << 320, 0, 160,
|
||||
0, 320, 120,
|
||||
0, 0, 1;
|
||||
// clang-format on
|
||||
if (same_K) {
|
||||
d.K2 = d.K1;
|
||||
} else {
|
||||
// clang-format off
|
||||
d.K2 << 360, 0, 170,
|
||||
0, 360, 110,
|
||||
0, 0, 1;
|
||||
// clang-format on
|
||||
}
|
||||
d.R1 = RotationAroundZ(-0.1);
|
||||
d.R2 = RotationAroundX(-0.1);
|
||||
d.t1 << 1, 1, 10;
|
||||
d.t2 << -2, -1, 10;
|
||||
P_From_KRt(d.K1, d.R1, d.t1, &d.P1);
|
||||
P_From_KRt(d.K2, d.R2, d.t2, &d.P2);
|
||||
|
||||
FundamentalFromProjections(d.P1, d.P2, &d.F);
|
||||
|
||||
d.X.resize(3, 30);
|
||||
d.X.setRandom();
|
||||
|
||||
Project(d.P1, d.X, &d.x1);
|
||||
Project(d.P2, d.X, &d.x2);
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
nViewDatasetConfigator::nViewDatasetConfigator(
|
||||
int fx, int fy, int cx, int cy, double distance, double jitter_amount) {
|
||||
_fx = fx;
|
||||
_fy = fy;
|
||||
_cx = cx;
|
||||
_cy = cy;
|
||||
_dist = distance;
|
||||
_jitter_amount = jitter_amount;
|
||||
}
|
||||
|
||||
NViewDataSet NRealisticCamerasFull(int nviews,
|
||||
int npoints,
|
||||
const nViewDatasetConfigator config) {
|
||||
NViewDataSet d;
|
||||
d.n = nviews;
|
||||
d.K.resize(nviews);
|
||||
d.R.resize(nviews);
|
||||
d.t.resize(nviews);
|
||||
d.C.resize(nviews);
|
||||
d.x.resize(nviews);
|
||||
d.x_ids.resize(nviews);
|
||||
|
||||
d.X.resize(3, npoints);
|
||||
d.X.setRandom();
|
||||
d.X *= 0.6;
|
||||
|
||||
Vecu all_point_ids(npoints);
|
||||
for (size_t j = 0; j < npoints; ++j) {
|
||||
all_point_ids[j] = j;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < nviews; ++i) {
|
||||
Vec3 camera_center, jitter, lookdir;
|
||||
|
||||
double theta = i * 2 * M_PI / nviews;
|
||||
camera_center << sin(theta), 0.0, cos(theta);
|
||||
camera_center *= config._dist;
|
||||
d.C[i] = camera_center;
|
||||
|
||||
jitter.setRandom();
|
||||
jitter *= config._jitter_amount / camera_center.norm();
|
||||
lookdir = -camera_center + jitter;
|
||||
|
||||
// clang-format off
|
||||
d.K[i] << config._fx, 0, config._cx,
|
||||
0, config._fy, config._cy,
|
||||
0, 0, 1;
|
||||
// clang-format on
|
||||
d.R[i] = LookAt(lookdir);
|
||||
d.t[i] = -d.R[i] * camera_center;
|
||||
d.x[i] = Project(d.P(i), d.X);
|
||||
d.x_ids[i] = all_point_ids;
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
NViewDataSet NRealisticCamerasSparse(int nviews,
|
||||
int npoints,
|
||||
float view_ratio,
|
||||
unsigned min_projections,
|
||||
const nViewDatasetConfigator config) {
|
||||
assert(view_ratio <= 1.0);
|
||||
assert(view_ratio > 0.0);
|
||||
assert(min_projections <= npoints);
|
||||
NViewDataSet d;
|
||||
d.n = nviews;
|
||||
d.K.resize(nviews);
|
||||
d.R.resize(nviews);
|
||||
d.t.resize(nviews);
|
||||
d.C.resize(nviews);
|
||||
d.x.resize(nviews);
|
||||
d.x_ids.resize(nviews);
|
||||
|
||||
d.X.resize(3, npoints);
|
||||
d.X.setRandom();
|
||||
d.X *= 0.6;
|
||||
|
||||
Mat visibility(nviews, npoints);
|
||||
visibility.setZero();
|
||||
Mat randoms(nviews, npoints);
|
||||
randoms.setRandom();
|
||||
randoms = (randoms.array() + 1) / 2.0;
|
||||
unsigned num_visibles = 0;
|
||||
for (size_t i = 0; i < nviews; ++i) {
|
||||
num_visibles = 0;
|
||||
for (size_t j = 0; j < npoints; j++) {
|
||||
if (randoms(i, j) <= view_ratio) {
|
||||
visibility(i, j) = true;
|
||||
num_visibles++;
|
||||
}
|
||||
}
|
||||
if (num_visibles < min_projections) {
|
||||
unsigned num_projections_to_add = min_projections - num_visibles;
|
||||
for (size_t j = 0; j < npoints && num_projections_to_add > 0; ++j) {
|
||||
if (!visibility(i, j)) {
|
||||
num_projections_to_add--;
|
||||
}
|
||||
}
|
||||
num_visibles += num_projections_to_add;
|
||||
}
|
||||
d.x_ids[i].resize(num_visibles);
|
||||
d.x[i].resize(2, num_visibles);
|
||||
}
|
||||
|
||||
size_t j_visible = 0;
|
||||
Vec3 X;
|
||||
for (size_t i = 0; i < nviews; ++i) {
|
||||
Vec3 camera_center, jitter, lookdir;
|
||||
|
||||
double theta = i * 2 * M_PI / nviews;
|
||||
camera_center << sin(theta), 0.0, cos(theta);
|
||||
camera_center *= config._dist;
|
||||
d.C[i] = camera_center;
|
||||
|
||||
jitter.setRandom();
|
||||
jitter *= config._jitter_amount / camera_center.norm();
|
||||
lookdir = -camera_center + jitter;
|
||||
|
||||
// clang-format off
|
||||
d.K[i] << config._fx, 0, config._cx,
|
||||
0, config._fy, config._cy,
|
||||
0, 0, 1;
|
||||
// clang-format on
|
||||
d.R[i] = LookAt(lookdir);
|
||||
d.t[i] = -d.R[i] * camera_center;
|
||||
j_visible = 0;
|
||||
for (size_t j = 0; j < npoints; j++) {
|
||||
if (visibility(i, j)) {
|
||||
X = d.X.col(j);
|
||||
d.x[i].col(j_visible) = Project(d.P(i), X);
|
||||
d.x_ids[i][j_visible] = j;
|
||||
j_visible++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
} // namespace libmv
|
||||
109
blender-5.2.0/intern/libmv/libmv/multiview/test_data_sets.h
Normal file
109
blender-5.2.0/intern/libmv/libmv/multiview/test_data_sets.h
Normal file
@@ -0,0 +1,109 @@
|
||||
// Copyright (c) 2007, 2008 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.
|
||||
|
||||
#ifndef LIBMV_MULTIVIEW_TEST_DATA_SETS_H_
|
||||
#define LIBMV_MULTIVIEW_TEST_DATA_SETS_H_
|
||||
|
||||
#include "libmv/base/vector.h"
|
||||
#include "libmv/multiview/fundamental.h"
|
||||
#include "libmv/multiview/projection.h"
|
||||
#include "libmv/numeric/numeric.h"
|
||||
|
||||
namespace libmv {
|
||||
|
||||
struct TwoViewDataSet {
|
||||
Mat3 K1, K2; // Internal parameters.
|
||||
Mat3 R1, R2; // Rotation.
|
||||
Vec3 t1, t2; // Translation.
|
||||
Mat34 P1, P2; // Projection matrix, P = K(R|t)
|
||||
Mat3 F; // Fundamental matrix.
|
||||
Mat3X X; // 3D points.
|
||||
Mat2X x1, x2; // Projected points.
|
||||
};
|
||||
|
||||
// Two cameras at (-1,-1,-10) and (2,1,-10) looking approximately towards z+.
|
||||
TwoViewDataSet TwoRealisticCameras(bool same_K = false);
|
||||
|
||||
// An N-view metric dataset . An important difference between this
|
||||
// and the other reconstruction data types is that all points are seen by all
|
||||
// cameras.
|
||||
struct NViewDataSet {
|
||||
vector<Mat3> K; // Internal parameters (fx, fy, etc).
|
||||
vector<Mat3> R; // Rotation.
|
||||
vector<Vec3> t; // Translation.
|
||||
vector<Vec3> C; // Camera centers.
|
||||
Mat3X X; // 3D points.
|
||||
vector<Mat2X> x; // Projected points; may have noise added.
|
||||
vector<Vecu> x_ids; // Indexes of points corresponding to the projections
|
||||
|
||||
int n; // Actual number of cameras.
|
||||
|
||||
Mat34 P(int i) {
|
||||
assert(i < n);
|
||||
return K[i] * HStack(R[i], t[i]);
|
||||
}
|
||||
Mat3 F(int i, int j) {
|
||||
Mat3 F_;
|
||||
FundamentalFromProjections(P(i), P(j), &F_);
|
||||
return F_;
|
||||
}
|
||||
void Reproject() {
|
||||
for (int i = 0; i < n; ++i) {
|
||||
x[i] = Project(P(i), X);
|
||||
}
|
||||
}
|
||||
// TODO(keir): Add gaussian jitter functions.
|
||||
};
|
||||
|
||||
struct nViewDatasetConfigator {
|
||||
/// Internal camera parameters
|
||||
int _fx;
|
||||
int _fy;
|
||||
int _cx;
|
||||
int _cy;
|
||||
|
||||
/// Camera random position parameters
|
||||
double _dist;
|
||||
double _jitter_amount;
|
||||
|
||||
nViewDatasetConfigator(int fx = 1000,
|
||||
int fy = 1000,
|
||||
int cx = 500,
|
||||
int cy = 500,
|
||||
double distance = 1.5,
|
||||
double jitter_amount = 0.01);
|
||||
};
|
||||
|
||||
NViewDataSet NRealisticCamerasFull(
|
||||
int nviews,
|
||||
int npoints,
|
||||
const nViewDatasetConfigator config = nViewDatasetConfigator());
|
||||
|
||||
// Generates sparse projections (not all points are projected)
|
||||
NViewDataSet NRealisticCamerasSparse(
|
||||
int nviews,
|
||||
int npoints,
|
||||
float view_ratio = 0.6,
|
||||
unsigned min_projections = 3,
|
||||
const nViewDatasetConfigator config = nViewDatasetConfigator());
|
||||
|
||||
} // namespace libmv
|
||||
|
||||
#endif // LIBMV_MULTIVIEW_TEST_DATA_SETS_H_
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user