Camera tracking integration

===========================

Commiting camera tracking integration gsoc project into trunk.

This commit includes:

- Bundled version of libmv library (with some changes against official repo,
  re-sync with libmv repo a bit later)
- New datatype ID called MovieClip which is optimized to work with movie
  clips (both of movie files and image sequences) and doing camera/motion
  tracking operations.
- New editor called Clip Editor which is currently used for motion/tracking
  stuff only, but which can be easily extended to work with masks too.

  This editor supports:
  * Loading movie files/image sequences
  * Build proxies with different size for loaded movie clip, also supports
    building undistorted proxies to increase speed of playback in
    undistorted mode.
  * Manual lens distortion mode calibration using grid and grease pencil
  * Supervised 2D tracking using two different algorithms KLT and SAD.
  * Basic algorithm for feature detection
  * Camera motion solving. scene orientation

- New constraints to "link" scene objects with solved motions from clip:

  * Follow Track (make object follow 2D motion of track with given name
    or parent object to reconstructed 3D position of track)
  * Camera Solver to make camera moving in the same way as reconstructed camera

This commit NOT includes changes from tomato branch:

- New nodes (they'll be commited as separated patch)
- Automatic image offset guessing for image input node and image editor
  (need to do more tests and gather more feedback)
- Code cleanup in libmv-capi. It's not so critical cleanup, just increasing
  readability and understanadability of code. Better to make this chaneg when
  Keir will finish his current patch.

More details about this project can be found on this page:
    http://wiki.blender.org/index.php/User:Nazg-gul/GSoC-2011

Further development of small features would be done in trunk, bigger/experimental
features would first be implemented in tomato branch.
This commit is contained in:
Sergey Sharybin 2011-11-07 12:55:18 +00:00
parent e122dc0748
commit 27d42c63d9
306 changed files with 75102 additions and 6811 deletions

@ -202,6 +202,9 @@ option(WITH_JACK "Enable Jack Support (http://www.jackaudio.org)" OFF)
option(WITH_LZO "Enable fast LZO compression (used for pointcache)" ON)
option(WITH_LZMA "Enable best LZMA compression, (used for pointcache)" ON)
# Camera/motion tracking
option(WITH_LIBMV "Enable libmv structure from motion library" ON)
# Misc
option(WITH_INPUT_NDOF "Enable NDOF input devices (SpaceNavigator and friends)" ON)
option(WITH_RAYOPTIMIZATION "Enable use of SIMD (SSE) optimizations for the raytracer" ON)

@ -329,6 +329,10 @@ def creator(env):
if env['WITH_BF_SDL']:
defs.append('WITH_SDL')
if env['WITH_BF_LIBMV']:
incs.append('#/extern/libmv')
defs.append('WITH_LIBMV')
if env['WITH_BF_PYTHON']:
incs.append('#/source/blender/python')
defs.append('WITH_PYTHON')

@ -515,6 +515,8 @@ def read_opts(env, cfg, args):
(BoolVariable('WITH_BF_LZO', 'Enable fast LZO pointcache compression', True)),
(BoolVariable('WITH_BF_LZMA', 'Enable best LZMA pointcache compression', True)),
(BoolVariable('WITH_BF_LIBMV', 'Enable libmv structure from motion library', True)),
('BF_X264_CONFIG', 'configuration flags for x264', ''),
('BF_XVIDCORE_CONFIG', 'configuration flags for xvidcore', ''),
# (BoolVariable('WITH_BF_DOCS', 'Generate API documentation', False)),

@ -63,3 +63,7 @@ endif()
if(WITH_LZMA)
add_subdirectory(lzma)
endif()
if(WITH_LIBMV)
add_subdirectory(libmv)
endif()

3
extern/SConscript vendored

@ -28,3 +28,6 @@ if env['WITH_BF_LZO']:
if env['WITH_BF_LZMA']:
SConscript(['lzma/SConscript'])
if env['WITH_BF_LIBMV']:
SConscript(['libmv/SConscript'])

210
extern/libmv/CMakeLists.txt vendored Normal file

@ -0,0 +1,210 @@
# ***** BEGIN GPL LICENSE BLOCK *****
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
# The Original Code is Copyright (C) 2011, Blender Foundation
# All rights reserved.
#
# Contributor(s): Blender Foundation,
# Sergey Sharybin
#
# ***** END GPL LICENSE BLOCK *****
set(INC
.
../Eigen3
./third_party/ssba
./third_party/ldl/Include
../colamd/Include
)
set(INC_SYS
${PNG_INCLUDE_DIR}
${ZLIB_INCLUDE_DIRS}
)
set(SRC
libmv-capi.cpp
libmv/numeric/numeric.cc
libmv/numeric/poly.cc
libmv/simple_pipeline/reconstruction.cc
libmv/simple_pipeline/resect.cc
libmv/simple_pipeline/intersect.cc
libmv/simple_pipeline/initialize_reconstruction.cc
libmv/simple_pipeline/camera_intrinsics.cc
libmv/simple_pipeline/pipeline.cc
libmv/simple_pipeline/detect.cc
libmv/simple_pipeline/tracks.cc
libmv/simple_pipeline/bundle.cc
libmv/image/convolve.cc
libmv/image/array_nd.cc
libmv/tracking/pyramid_region_tracker.cc
libmv/tracking/sad.cc
libmv/tracking/trklt_region_tracker.cc
libmv/tracking/klt_region_tracker.cc
libmv/tracking/retrack_region_tracker.cc
libmv/multiview/projection.cc
libmv/multiview/conditioning.cc
libmv/multiview/fundamental.cc
libmv/multiview/euclidean_resection.cc
libmv/multiview/triangulation.cc
third_party/ssba/Geometry/v3d_metricbundle.cpp
third_party/ssba/Math/v3d_optimization.cpp
third_party/gflags/gflags.cc
third_party/gflags/gflags_reporting.cc
third_party/gflags/gflags_completions.cc
third_party/fast/fast_9.c
third_party/fast/fast_10.c
third_party/fast/fast_11.c
third_party/fast/fast_12.c
third_party/fast/fast.c
third_party/fast/nonmax.c
third_party/ldl/Source/ldl.c
libmv-capi.h
libmv/logging/logging.h
libmv/numeric/dogleg.h
libmv/numeric/levenberg_marquardt.h
libmv/numeric/poly.h
libmv/numeric/function_derivative.h
libmv/numeric/numeric.h
libmv/simple_pipeline/resect.h
libmv/simple_pipeline/reconstruction.h
libmv/simple_pipeline/camera_intrinsics.h
libmv/simple_pipeline/tracks.h
libmv/simple_pipeline/detect.h
libmv/simple_pipeline/pipeline.h
libmv/simple_pipeline/intersect.h
libmv/simple_pipeline/bundle.h
libmv/simple_pipeline/initialize_reconstruction.h
libmv/image/convolve.h
libmv/image/tuple.h
libmv/image/array_nd.h
libmv/image/sample.h
libmv/image/image.h
libmv/tracking/region_tracker.h
libmv/tracking/retrack_region_tracker.h
libmv/tracking/sad.h
libmv/tracking/pyramid_region_tracker.h
libmv/tracking/trklt_region_tracker.h
libmv/tracking/klt_region_tracker.h
libmv/base/id_generator.h
libmv/base/vector.h
libmv/base/scoped_ptr.h
libmv/base/vector_utils.h
libmv/multiview/nviewtriangulation.h
libmv/multiview/resection.h
libmv/multiview/euclidean_resection.h
libmv/multiview/triangulation.h
libmv/multiview/projection.h
libmv/multiview/fundamental.h
libmv/multiview/conditioning.h
third_party/ssba/Geometry/v3d_metricbundle.h
third_party/ssba/Geometry/v3d_cameramatrix.h
third_party/ssba/Geometry/v3d_distortion.h
third_party/ssba/Math/v3d_linear_utils.h
third_party/ssba/Math/v3d_optimization.h
third_party/ssba/Math/v3d_mathutilities.h
third_party/ssba/Math/v3d_linear.h
third_party/gflags/gflags_completions.h
third_party/gflags/mutex.h
third_party/gflags/config.h
third_party/gflags/gflags.h
third_party/fast/fast.h
third_party/ldl/Include/ldl.h
third_party/msinttypes/stdint.h
third_party/msinttypes/inttypes.h
)
IF(WIN32)
list(APPEND SRC
third_party/glog/src/logging.cc
third_party/glog/src/raw_logging.cc
third_party/glog/src/utilities.cc
third_party/glog/src/vlog_is_on.cc
third_party/glog/src/windows/port.cc
third_party/glog/src/utilities.h
third_party/glog/src/stacktrace_generic-inl.h
third_party/glog/src/stacktrace.h
third_party/glog/src/stacktrace_x86_64-inl.h
third_party/glog/src/base/googleinit.h
third_party/glog/src/base/mutex.h
third_party/glog/src/base/commandlineflags.h
third_party/glog/src/stacktrace_powerpc-inl.h
third_party/glog/src/stacktrace_x86-inl.h
third_party/glog/src/config.h
third_party/glog/src/stacktrace_libunwind-inl.h
third_party/glog/src/windows/glog/raw_logging.h
third_party/glog/src/windows/glog/vlog_is_on.h
third_party/glog/src/windows/glog/logging.h
third_party/glog/src/windows/glog/log_severity.h
third_party/glog/src/windows/port.h
third_party/glog/src/windows/config.h
)
list(APPEND INC
./third_party/glog/src/windows
./third_party/msinttypes
)
IF(MSVC)
set(MSVC_OFLAGS O1 O2 Ox)
foreach(FLAG )
string(REPLACE "" "Od" CMAKE_CXX_FLAGS_RELEASE "")
string(REPLACE "" "Od" CMAKE_C_FLAGS_RELWITHDEBINFO "")
endforeach()
ENDIF(MSVC)
ELSE(WIN32)
list(APPEND SRC
third_party/glog/src/utilities.cc
third_party/glog/src/symbolize.cc
third_party/glog/src/vlog_is_on.cc
third_party/glog/src/signalhandler.cc
third_party/glog/src/logging.cc
third_party/glog/src/demangle.cc
third_party/glog/src/raw_logging.cc
third_party/glog/src/utilities.h
third_party/glog/src/stacktrace_generic-inl.h
third_party/glog/src/config_mac.h
third_party/glog/src/stacktrace.h
third_party/glog/src/stacktrace_x86_64-inl.h
third_party/glog/src/symbolize.h
third_party/glog/src/base/googleinit.h
third_party/glog/src/base/mutex.h
third_party/glog/src/base/commandlineflags.h
third_party/glog/src/stacktrace_powerpc-inl.h
third_party/glog/src/stacktrace_x86-inl.h
third_party/glog/src/config.h
third_party/glog/src/demangle.h
third_party/glog/src/stacktrace_libunwind-inl.h
third_party/glog/src/glog/raw_logging.h
third_party/glog/src/glog/vlog_is_on.h
third_party/glog/src/glog/logging.h
third_party/glog/src/glog/log_severity.h
third_party/glog/src/config_linux.h
)
list(APPEND INC
./third_party/glog/src
)
ENDIF(WIN32)
add_definitions(-DV3DLIB_ENABLE_SUITESPARSE -DGOOGLE_GLOG_DLL_DECL=)
blender_add_lib(extern_libmv "${SRC}" "${INC}" "${INC_SYS}")

312
extern/libmv/ChangeLog vendored Normal file

@ -0,0 +1,312 @@
commit 531c79bf95fddaaa70707d1abcd4fdafda16bbf0
Author: Matthias Fauconneau <matthias.fauconneau@gmail.com>
Date: Sat Aug 20 00:00:42 2011 +0200
Display warped pattern in marker preview.
commit bb5c27e671b6f8eb56ddf490f0795d59bede591b
Author: Matthias Fauconneau <matthias.fauconneau@gmail.com>
Date: Fri Aug 19 18:37:48 2011 +0200
Fix CMake build.
commit 2ac7281ff6b9545b425dd84fb03bf9c5c98b4de2
Author: Matthias Fauconneau <matthias.fauconneau@gmail.com>
Date: Fri Aug 19 17:34:45 2011 +0200
Avoid symbol shadowing.
commit 2a7c3de4acc60e0433b4952f69e30528dbafe0d2
Author: Matthias Fauconneau <matthias.fauconneau@gmail.com>
Date: Fri Aug 19 17:22:47 2011 +0200
Better dragging behavior when hitting borders.
commit a14eb3953c9521b2e08ff9ddd45b33ff1f8aeafb
Author: Matthias Fauconneau <matthias.fauconneau@gmail.com>
Date: Fri Aug 19 17:12:12 2011 +0200
Update marker preview to new affine tracking.
commit 5299ea67043459eda147950e589c2d327a8fbced
Author: Matthias Fauconneau <matthias.fauconneau@gmail.com>
Date: Fri Aug 19 16:05:54 2011 +0200
sqrt takes double precision.
commit 9f9221ce151d788c49b48f6f293ab2e2f8813978
Author: Matthias Fauconneau <matthias.fauconneau@gmail.com>
Date: Fri Aug 19 16:04:37 2011 +0200
MSVC compatibility: heap allocate pattern, explicit float cast.
commit 702658d2f8616964a6eeb3743fd85e97ac7ff09d
Author: Matthias Fauconneau <matthias.fauconneau@gmail.com>
Date: Fri Aug 19 14:59:24 2011 +0200
Expose regularization parameters (areaPenalty and conditionPenalty) in API.
commit 3e84ae5fbac10451d4935418f6281a90cedace11
Author: Matthias Fauconneau <matthias.fauconneau@gmail.com>
Date: Fri Aug 19 14:19:27 2011 +0200
Add LaplaceFilter.
Add regularization in affine SAD Tracker (keep constant area and good condition number).
UI: Better track display (+enable line antialiasing).
commit 6d26d9a8ccc4ce009fbf253898fea8864dd5001a
Author: Matthias Fauconneau <matthias.fauconneau@gmail.com>
Date: Fri Aug 19 10:25:26 2011 +0200
Add optimization for integer pixel search.
Allows more agressive settings for affine coordinate descent.
commit 70ceae81c0ab561b07e640ecb9933f0a902b57cd
Author: Matthias Fauconneau <matthias.fauconneau@gmail.com>
Date: Fri Aug 19 00:02:12 2011 +0200
Document coordinate descent method in affine SAD matcher.
Add heuristic to prevent high distortions.
commit 75520f4bc4ccbb272a1b4149d3b8d05a90f7f896
Author: Matthias Fauconneau <matthias.fauconneau@gmail.com>
Date: Thu Aug 18 23:14:17 2011 +0200
Fix affine iteration.
commit 4e8e0aa6018e3eb2fbebdad7f1cbd6c909d26e79
Author: Matthias Fauconneau <matthias.fauconneau@gmail.com>
Date: Thu Aug 18 23:03:26 2011 +0200
Handle rotations.
commit 3ce41cf3c1b5c136a61d8f4c63ccae3cafbdb8da
Author: Matthias Fauconneau <matthias.fauconneau@gmail.com>
Date: Thu Aug 18 22:24:47 2011 +0200
Slow brute-force affine diamond search implementation.
commit 1c4acd03e030c1c50dc6fc36c419c72ea69a0713
Author: Matthias Fauconneau <matthias.fauconneau@gmail.com>
Date: Thu Aug 18 20:51:43 2011 +0200
Fix detect.cc.
commit ec18cc5ea9ae2e641075a847e82d0aacb8415ad8
Author: Matthias Fauconneau <matthias.fauconneau@gmail.com>
Date: Thu Aug 18 17:45:37 2011 +0200
Compute and return Pearson product-moment correlation coefficient between reference and matched pattern.
commit 21d4245c63a01bfc736192d55baf10983e7c9ec7
Author: Matthias Fauconneau <matthias.fauconneau@gmail.com>
Date: Thu Aug 18 16:18:44 2011 +0200
UI and API support for affine tracking.
commit a4876d8c40dcde615b44009c38c49e9a1b1d4698
Author: Matthias Fauconneau <matthias.fauconneau@gmail.com>
Date: Wed Aug 17 20:26:01 2011 +0200
Hack to make sad.cc compile with MSVC on system without support for the SSE instruction set.
commit 0de723dfce5bbe44dbd19be8cd6dd6e9b03b7924
Author: Matthias Fauconneau <matthias.fauconneau@gmail.com>
Date: Wed Aug 17 20:10:46 2011 +0200
Fix slow path (for computers without SSE2).
Heap allocate scores in detect.cc
commit 65a9d496f81e8b37eae39a4063957b8be9a4e6f0
Author: Matthias Fauconneau <matthias.fauconneau@gmail.com>
Date: Wed Aug 17 19:25:17 2011 +0200
Fix compilation on OSX.
commit d22720e618456329388d2c107422c3b371657cba
Author: Matthias Fauconneau <matthias.fauconneau@gmail.com>
Date: Wed Aug 17 14:14:45 2011 +0200
Improve Detect and SAD Tracker API and documentation.
commit 5d6cd4ad365b061901bad40695b51d568487a0cf
Author: Matthias Fauconneau <matthias.fauconneau@gmail.com>
Date: Wed Aug 17 11:57:29 2011 +0200
MSVC support fixes.
commit 50f0323173c6deebd6aaf9c126f0b51b2a79c3c1
Author: Matthias Fauconneau <matthias.fauconneau@gmail.com>
Date: Tue Aug 16 23:21:37 2011 +0200
Detector can detect features similar to a given pattern.
commit 5734cc27bbf84c2b6edcfcc1ea736798e12d5820
Author: Matthias Fauconneau <matthias.fauconneau@gmail.com>
Date: Tue Aug 16 22:53:54 2011 +0200
Ensure SAD Tracker is C compatible.
Update Detect API documentation.
commit 701c42842574064fea992f8822e3899cb9066108
Author: Matthias Fauconneau <matthias.fauconneau@gmail.com>
Date: Tue Aug 16 21:56:42 2011 +0200
Remove FAST detector.
Add Moravec detector.
This detector is more suited to tracking since it try to choose patterns which are unlikely to drift by computing SAD with neighbouring patches.
It could be improved to better avoid edges.
commit 9bdf93e13fc880c78b6f34397da673388c16040e
Author: Matthias Fauconneau <matthias.fauconneau@gmail.com>
Date: Tue Aug 16 21:55:08 2011 +0200
Fix Qt Tracker GL to work with AMD drivers.
commit 81613ee0cc94b315f333c9632b18b95d426aad05
Author: Matthias Fauconneau <matthias.fauconneau@gmail.com>
Date: Tue Aug 16 21:54:12 2011 +0200
Make CameraIntrinsics (and thus Qt tracker) compilable without linking libmv.
commit a1d9a8fa8b01ef7cf2a79b3b891633fc333fc9cf
Author: Matthias Fauconneau <matthias.fauconneau@gmail.com>
Date: Tue Aug 16 21:24:51 2011 +0200
Fix SAD tracker. Pattern was transposed by affine pattern sampler.
commit c3b794da2e7fd23f2fbdf90dbd71de0e6b3bc811
Author: Matthias Fauconneau <matthias.fauconneau@gmail.com>
Date: Tue Aug 16 21:19:02 2011 +0200
Fix SAD tracker. Pattern was transposed by affine pattern sampler.
commit a9b61bf3356f27174cdd983f562f99c3a6a2cc35
Author: Matthias Fauconneau <matthias.fauconneau@gmail.com>
Date: Sun Aug 14 09:56:51 2011 +0200
Clarify CameraIntrinsics documentation.
Edit CameraInstrinsics test to fail.
commit 10bdad9ad2cea2603896263cde5a5339169a9af0
Author: Matthias Fauconneau <matthias.fauconneau@gmail.com>
Date: Fri Aug 12 21:05:32 2011 +0200
Fix out of bound access in warp bilinear sampling.
commit dd9a418db021a28af2c1198d5e5b9e68fe048a03
Author: Matthias Fauconneau <matthias.fauconneau@gmail.com>
Date: Fri Aug 12 19:14:36 2011 +0200
Fix compilation with -funsigned-char.
commit bd1a268ede39b67f2ba4b360f6fc693419e7cd7f
Author: Matthias Fauconneau <matthias.fauconneau@gmail.com>
Date: Fri Aug 12 18:39:27 2011 +0200
CameraIntrinsics fixes.
commit ae513b39fb779632f96ceff7c1e014fb8e68702a
Author: Matthias Fauconneau <matthias.fauconneau@gmail.com>
Date: Tue Aug 9 19:38:58 2011 +0200
Remove stray QDebug include.
commit 1e58f55078ce6009a885be30ae0316aec6ed8239
Author: Matthias Fauconneau <matthias.fauconneau@gmail.com>
Date: Tue Aug 9 14:16:31 2011 +0200
Make API future-proof (for an eventual affine or planar tracker).
commit c2af303e7bf0dddcb02937323ac5846b1801e6cc
Author: Matthias Fauconneau <matthias.fauconneau@gmail.com>
Date: Tue Aug 9 11:13:29 2011 +0200
Remove reconstruction breaking debug code.
commit 8792a633e5c5f1c1f12e164b9e8897ca0790ac59
Author: Matthias Fauconneau <matthias.fauconneau@gmail.com>
Date: Tue Aug 9 10:49:18 2011 +0200
Remove getchar()s.
commit 63a9bdee0cbd1197e0315d01c27bfc2361bd5656
Author: Matthias Fauconneau <matthias.fauconneau@gmail.com>
Date: Tue Aug 9 10:35:07 2011 +0200
Adapt patch to new PipelineRoutines code generation strategy.
commit 096ff1a4070f7212c50fb0a4b2feec7ca9d97158
Author: Matthias Fauconneau <matthias.fauconneau@gmail.com>
Date: Tue Aug 9 09:54:12 2011 +0200
Merge max_image and max_track fix from tomato.
commit d8450cd3c37278a397482cd36b1e2419f154cfb9
Author: Matthias Fauconneau <matthias.fauconneau@gmail.com>
Date: Tue Aug 9 09:38:49 2011 +0200
Synchronize tree with Tomato: Merge patch for better resection, keep deprecated KLT tracker.
commit e9b2dca920cf9575c15150a4988634b00e343a41
Author: Matthias Fauconneau <matthias.fauconneau@gmail.com>
Date: Mon Aug 8 17:07:08 2011 +0200
Fixes, Documentation.
commit 4fc1c57a2d92442808ac4a3676e6d9a25a51e310
Author: Matthias Fauconneau <matthias.fauconneau@gmail.com>
Date: Sun Aug 7 14:35:08 2011 +0200
Improve tracker resilience by penalizing large motion vectors.
commit cc8e7e8e08cd91f75c080a0091461ca9fe969664
Author: Matthias Fauconneau <matthias.fauconneau@gmail.com>
Date: Sun Aug 7 09:28:09 2011 +0200
Leverage SSE2 SAD instruction for 16x speed improvement in integer pixel search resulting in ~1ms per marker for 16x16 pattern on 128x128 region.
commit f362ab4999a768370fca57552464b459eb9fbddc
Author: Matthias Fauconneau <matthias.fauconneau@gmail.com>
Date: Sun Aug 7 09:06:04 2011 +0200
Improve SAD Tracker subpixel precision (avoid drift even when adapting at each frame).
commit fce7a214c561b5f5f0e17115c31fb48814bde2db
Author: Matthias Fauconneau <matthias.fauconneau@gmail.com>
Date: Sat Aug 6 21:57:06 2011 +0200
Track using simple Sum of Absolute Differences matching.
This method is simpler, more robust, faster and accurate.
commit 620a7a35d9a2818bf6e9dbf5d11debda4be6bc26
Author: Matthias Fauconneau <matthias.fauconneau@gmail.com>
Date: Fri Jul 29 12:35:57 2011 +0200
Add Intersect unit test.
commit a2bf58fa57be11215eb17ff7f7de58f97d480ec3
Author: Matthias Fauconneau <matthias.fauconneau@gmail.com>
Date: Thu Jul 28 11:08:06 2011 +0200
Remove tests depending on dead code.
Fix CameraIntrinsics test.
Add Intersect and Resect tests.
commit 19bddee10b4879c8cd2238ccdf5b8f7620cf8384
Author: Matthias Fauconneau <matthias.fauconneau@gmail.com>
Date: Wed Jul 27 12:07:21 2011 +0200
Image Distortion: Fixes and more testing.
commit 0454d97da328fb0eda8c6c50511ac31864a6d3d6
Author: Matthias Fauconneau <matthias.fauconneau@gmail.com>
Date: Wed Jul 27 10:32:37 2011 +0200
Test float image distortion.
commit 8db01595a8721f766d85931a8d92b780461d8741
Author: Matthias Fauconneau <matthias.fauconneau@gmail.com>
Date: Wed Jul 27 10:27:07 2011 +0200
Image Distortion: Bilinear sampling, Optimization, Instantiate all variants (Distort/Undistort, float/ubyte, 1-4 channels).

60
extern/libmv/SConscript vendored Normal file

@ -0,0 +1,60 @@
#!/usr/bin/python
import sys
import os
Import('env')
defs = []
cflags_libmv = Split(env['CFLAGS'])
ccflags_libmv = Split(env['CCFLAGS'])
cxxflags_libmv = Split(env['CXXFLAGS'])
defs.append('V3DLIB_ENABLE_SUITESPARSE')
defs.append('GOOGLE_GLOG_DLL_DECL=')
src = env.Glob("*.cpp")
src += env.Glob('libmv/image/*.cc')
src += env.Glob('libmv/multiview/*.cc')
src += env.Glob('libmv/numeric/*.cc')
src += env.Glob('libmv/simple_pipeline/*.cc')
src += env.Glob('libmv/tracking/*.cc')
src += env.Glob('third_party/fast/*.c')
src += env.Glob('third_party/gflags/*.cc')
src += env.Glob('third_party/ldl/Source/*.c')
src += env.Glob('third_party/ssba/Geometry/*.cpp')
src += env.Glob('third_party/ssba/Math/*.cpp')
incs = '. ../Eigen3'
incs += ' ' + env['BF_PNG_INC']
incs += ' ' + env['BF_ZLIB_INC']
if env['OURPLATFORM'] in ('win32-vc', 'win32-mingw', 'linuxcross', 'win64-vc'):
incs += ' ./third_party/glog/src/windows ./third_party/glog/src/windows/glog ./third_party/msinttypes'
src += ['./third_party/glog/src/logging.cc', './third_party/glog/src/raw_logging.cc', './third_party/glog/src/utilities.cc', './third_party/glog/src/vlog_is_on.cc']
src += ['./third_party/glog/src/windows/port.cc']
if env['OURPLATFORM'] in ('win32-vc', 'win64-vc'):
cflags_libmv.append('/Od')
ccflags_libmv.append('/Od')
cxxflags_libmv.append('/Od')
if not env['BF_DEBUG']:
defs.append('NDEBUG')
else:
if not env['BF_DEBUG']:
cflags_libmv = Split(env['REL_CFLAGS'])
ccflags_libmv = Split(env['REL_CCFLAGS'])
cxxflags_libmv = Split(env['REL_CXXFLAGS'])
else:
src += env.Glob("third_party/glog/src/*.cc")
incs += ' ./third_party/glog/src'
if not env['BF_DEBUG']:
cflags_libmv = Split(env['REL_CFLAGS'])
ccflags_libmv = Split(env['REL_CCFLAGS'])
cxxflags_libmv = Split(env['REL_CXXFLAGS'])
incs += ' ./third_party/ssba ./third_party/ldl/Include ../colamd/Include'
env.BlenderLib ( libname = 'extern_libmv', sources=src, includes=Split(incs), defines=defs, libtype=['extern', 'player'], priority=[20,137], compileflags=cflags_libmv, cc_compileflags=ccflags_libmv, cxx_compileflags=cxxflags_libmv )

250
extern/libmv/bundle.sh vendored Executable file

@ -0,0 +1,250 @@
#!/bin/sh
#BRANCH="keir"
BRANCH="Matthias-Fauconneau"
if [ -d ./.svn ]; then
echo "This script is supposed to work only when using git-svn"
exit 1
fi
repo="git://github.com/${BRANCH}/libmv.git"
tmp=`mktemp -d`
git clone $repo $tmp/libmv
#git --git-dir $tmp/libmv/.git --work-tree $tmp/libmv log --since="1 month ago" > ChangeLog
git --git-dir $tmp/libmv/.git --work-tree $tmp/libmv log -n 50 > ChangeLog
for p in `cat ./patches/series`; do
echo "Applying patch $p..."
cat ./patches/$p | patch -d $tmp/libmv -p1
done
rm -rf libmv
rm -rf third_party
cat "files.txt" | while f=`line`; do
mkdir -p `dirname $f`
cp $tmp/libmv/src/$f $f
done
rm -rf $tmp
chmod 664 ./third_party/glog/src/windows/*.cc ./third_party/glog/src/windows/*.h ./third_party/glog/src/windows/glog/*.h
sources=`find ./libmv -type f -iname '*.cc' -or -iname '*.cpp' -or -iname '*.c' | sed -r 's/^\.\//\t/'`
headers=`find ./libmv -type f -iname '*.h' | sed -r 's/^\.\//\t/'`
third_sources=`find ./third_party -type f -iname '*.cc' -or -iname '*.cpp' -or -iname '*.c' | grep -v glog | sed -r 's/^\.\//\t/'`
third_headers=`find ./third_party -type f -iname '*.h' | grep -v glog | sed -r 's/^\.\//\t/'`
third_glog_sources=`find ./third_party -type f -iname '*.cc' -or -iname '*.cpp' -or -iname '*.c' | grep glog | grep -v windows | sed -r 's/^\.\//\t\t/'`
third_glog_headers=`find ./third_party -type f -iname '*.h' | grep glog | grep -v windows | sed -r 's/^\.\//\t\t/'`
src_dir=`find ./libmv -type f -iname '*.cc' -exec dirname {} \; -or -iname '*.cpp' -exec dirname {} \; -or -iname '*.c' -exec dirname {} \; | sed -r 's/^\.\//\t/' | sort | 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/^\.\//\t/' | sort | uniq`
src=""
win_src=""
for x in $src_dir $src_third_dir; do
t=""
if test `echo "$x" | grep -c glog ` -eq 1; then
continue;
fi
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 ` -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
# \$Id\$
# ***** BEGIN GPL LICENSE BLOCK *****
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
# The Original Code is Copyright (C) 2011, Blender Foundation
# All rights reserved.
#
# Contributor(s): Blender Foundation,
# Sergey Sharybin
#
# ***** END GPL LICENSE BLOCK *****
set(INC
.
../Eigen3
./third_party/ssba
./third_party/ldl/Include
../colamd/Include
)
set(INC_SYS
${PNG_INCLUDE_DIR}
${ZLIB_INCLUDE_DIRS}
)
set(SRC
libmv-capi.cpp
${sources}
${third_sources}
libmv-capi.h
${headers}
${third_headers}
)
IF(WIN32)
list(APPEND SRC
third_party/glog/src/logging.cc
third_party/glog/src/raw_logging.cc
third_party/glog/src/utilities.cc
third_party/glog/src/vlog_is_on.cc
third_party/glog/src/windows/port.cc
third_party/glog/src/utilities.h
third_party/glog/src/stacktrace_generic-inl.h
third_party/glog/src/stacktrace.h
third_party/glog/src/stacktrace_x86_64-inl.h
third_party/glog/src/base/googleinit.h
third_party/glog/src/base/mutex.h
third_party/glog/src/base/commandlineflags.h
third_party/glog/src/stacktrace_powerpc-inl.h
third_party/glog/src/stacktrace_x86-inl.h
third_party/glog/src/config.h
third_party/glog/src/stacktrace_libunwind-inl.h
third_party/glog/src/windows/glog/raw_logging.h
third_party/glog/src/windows/glog/vlog_is_on.h
third_party/glog/src/windows/glog/logging.h
third_party/glog/src/windows/glog/log_severity.h
third_party/glog/src/windows/port.h
third_party/glog/src/windows/config.h
)
list(APPEND INC
./third_party/glog/src/windows
./third_party/msinttypes
)
IF(MSVC)
set(MSVC_OFLAGS O1 O2 Ox)
foreach(FLAG ${MSVC_OFLAGS})
string(REPLACE "${FLAG}" "Od" CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}")
string(REPLACE "${FLAG}" "Od" CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO}")
endforeach()
ENDIF(MSVC)
ELSE(WIN32)
list(APPEND SRC
${third_glog_sources}
${third_glog_headers}
)
list(APPEND INC
./third_party/glog/src
)
ENDIF(WIN32)
add_definitions(-DV3DLIB_ENABLE_SUITESPARSE -DGOOGLE_GLOG_DLL_DECL=)
blender_add_lib(extern_libmv "\${SRC}" "\${INC}" "\${INC_SYS}")
EOF
cat > SConscript << EOF
#!/usr/bin/python
import sys
import os
Import('env')
defs = []
cflags_libmv = Split(env['CFLAGS'])
ccflags_libmv = Split(env['CCFLAGS'])
cxxflags_libmv = Split(env['CXXFLAGS'])
defs.append('V3DLIB_ENABLE_SUITESPARSE')
defs.append('GOOGLE_GLOG_DLL_DECL=')
src = env.Glob("*.cpp")
$src
incs = '. ../Eigen3'
incs += ' ' + env['BF_PNG_INC']
incs += ' ' + env['BF_ZLIB_INC']
if env['OURPLATFORM'] in ('win32-vc', 'win32-mingw', 'linuxcross', 'win64-vc'):
incs += ' ./third_party/glog/src/windows ./third_party/glog/src/windows/glog ./third_party/msinttypes'
${win_src}
src += ['./third_party/glog/src/logging.cc', './third_party/glog/src/raw_logging.cc', './third_party/glog/src/utilities.cc', './third_party/glog/src/vlog_is_on.cc']
src += ['./third_party/glog/src/windows/port.cc']
if env['OURPLATFORM'] in ('win32-vc', 'win64-vc'):
cflags_libmv.append('/Od')
ccflags_libmv.append('/Od')
cxxflags_libmv.append('/Od')
if not env['BF_DEBUG']:
defs.append('NDEBUG')
else:
if not env['BF_DEBUG']:
cflags_libmv = Split(env['REL_CFLAGS'])
ccflags_libmv = Split(env['REL_CCFLAGS'])
cxxflags_libmv = Split(env['REL_CXXFLAGS'])
else:
src += env.Glob("third_party/glog/src/*.cc")
incs += ' ./third_party/glog/src'
if not env['BF_DEBUG']:
cflags_libmv = Split(env['REL_CFLAGS'])
ccflags_libmv = Split(env['REL_CCFLAGS'])
cxxflags_libmv = Split(env['REL_CXXFLAGS'])
incs += ' ./third_party/ssba ./third_party/ldl/Include ../colamd/Include'
env.BlenderLib ( libname = 'extern_libmv', sources=src, includes=Split(incs), defines=defs, libtype=['extern', 'player'], priority=[20,137], compileflags=cflags_libmv, cc_compileflags=ccflags_libmv, cxx_compileflags=cxxflags_libmv )
EOF

141
extern/libmv/files.txt vendored Normal file

@ -0,0 +1,141 @@
libmv/logging/logging.h
libmv/numeric/dogleg.h
libmv/numeric/levenberg_marquardt.h
libmv/numeric/poly.h
libmv/numeric/numeric.cc
libmv/numeric/function_derivative.h
libmv/numeric/poly.cc
libmv/numeric/tinyvector.cc
libmv/numeric/numeric.h
libmv/simple_pipeline/reconstruction.cc
libmv/simple_pipeline/resect.h
libmv/simple_pipeline/resect.cc
libmv/simple_pipeline/reconstruction.h
libmv/simple_pipeline/camera_intrinsics.h
libmv/simple_pipeline/intersect.cc
libmv/simple_pipeline/initialize_reconstruction.cc
libmv/simple_pipeline/camera_intrinsics.cc
libmv/simple_pipeline/pipeline.cc
libmv/simple_pipeline/tracks.h
libmv/simple_pipeline/detect.h
libmv/simple_pipeline/detect.cc
libmv/simple_pipeline/pipeline.h
libmv/simple_pipeline/tracks.cc
libmv/simple_pipeline/bundle.cc
libmv/simple_pipeline/intersect.h
libmv/simple_pipeline/bundle.h
libmv/simple_pipeline/initialize_reconstruction.h
libmv/image/convolve.h
libmv/image/tuple.h
libmv/image/array_nd.h
libmv/image/convolve.cc
libmv/image/array_nd.cc
libmv/image/sample.h
libmv/image/image.h
libmv/tracking/pyramid_region_tracker.cc
libmv/tracking/region_tracker.h
libmv/tracking/sad.cc
libmv/tracking/trklt_region_tracker.cc
libmv/tracking/klt_region_tracker.cc
libmv/tracking/retrack_region_tracker.h
libmv/tracking/sad.h
libmv/tracking/pyramid_region_tracker.h
libmv/tracking/trklt_region_tracker.h
libmv/tracking/retrack_region_tracker.cc
libmv/tracking/klt_region_tracker.h
libmv/base/id_generator.h
libmv/base/vector.h
libmv/base/scoped_ptr.h
libmv/base/vector_utils.h
libmv/multiview/projection.cc
libmv/multiview/conditioning.cc
libmv/multiview/nviewtriangulation.h
libmv/multiview/resection.h
libmv/multiview/fundamental.cc
libmv/multiview/euclidean_resection.cc
libmv/multiview/euclidean_resection.h
libmv/multiview/triangulation.h
libmv/multiview/projection.h
libmv/multiview/triangulation.cc
libmv/multiview/fundamental.h
libmv/multiview/conditioning.h
third_party/ssba/README.TXT
third_party/ssba/COPYING.TXT
third_party/ssba/Geometry/v3d_metricbundle.h
third_party/ssba/Geometry/v3d_metricbundle.cpp
third_party/ssba/Geometry/v3d_cameramatrix.h
third_party/ssba/Geometry/v3d_distortion.h
third_party/ssba/README.libmv
third_party/ssba/Math/v3d_linear_utils.h
third_party/ssba/Math/v3d_optimization.h
third_party/ssba/Math/v3d_mathutilities.h
third_party/ssba/Math/v3d_linear.h
third_party/ssba/Math/v3d_optimization.cpp
third_party/gflags/gflags_completions.h
third_party/gflags/mutex.h
third_party/gflags/gflags.cc
third_party/gflags/gflags_reporting.cc
third_party/gflags/README.libmv
third_party/gflags/config.h
third_party/gflags/gflags_completions.cc
third_party/gflags/gflags.h
third_party/fast/fast_9.c
third_party/fast/fast_10.c
third_party/fast/fast_11.c
third_party/fast/fast.h
third_party/fast/LICENSE
third_party/fast/fast_12.c
third_party/fast/fast.c
third_party/fast/README
third_party/fast/README.libmv
third_party/fast/nonmax.c
third_party/ldl/Include/ldl.h
third_party/ldl/CMakeLists.txt
third_party/ldl/README.libmv
third_party/ldl/Doc/ChangeLog
third_party/ldl/Doc/lesser.txt
third_party/ldl/README.txt
third_party/ldl/Source/ldl.c
third_party/glog/ChangeLog
third_party/glog/COPYING
third_party/glog/src/utilities.cc
third_party/glog/src/utilities.h
third_party/glog/src/symbolize.cc
third_party/glog/src/stacktrace_generic-inl.h
third_party/glog/src/config_mac.h
third_party/glog/src/vlog_is_on.cc
third_party/glog/src/signalhandler.cc
third_party/glog/src/stacktrace.h
third_party/glog/src/stacktrace_x86_64-inl.h
third_party/glog/src/symbolize.h
third_party/glog/src/base/googleinit.h
third_party/glog/src/base/mutex.h
third_party/glog/src/base/commandlineflags.h
third_party/glog/src/windows/preprocess.sh
third_party/glog/src/windows/port.h
third_party/glog/src/windows/config.h
third_party/glog/src/windows/glog/raw_logging.h
third_party/glog/src/windows/glog/vlog_is_on.h
third_party/glog/src/windows/glog/logging.h
third_party/glog/src/windows/glog/log_severity.h
third_party/glog/src/windows/port.cc
third_party/glog/src/logging.cc
third_party/glog/src/stacktrace_powerpc-inl.h
third_party/glog/src/stacktrace_x86-inl.h
third_party/glog/src/demangle.cc
third_party/glog/src/config.h
third_party/glog/src/demangle.h
third_party/glog/src/stacktrace_libunwind-inl.h
third_party/glog/src/glog/raw_logging.h
third_party/glog/src/glog/vlog_is_on.h
third_party/glog/src/glog/logging.h
third_party/glog/src/glog/log_severity.h
third_party/glog/src/raw_logging.cc
third_party/glog/src/config_linux.h
third_party/glog/NEWS
third_party/glog/README
third_party/glog/README.libmv
third_party/glog/AUTHORS
third_party/msinttypes/stdint.h
third_party/msinttypes/inttypes.h
third_party/msinttypes/README.libmv

770
extern/libmv/libmv-capi.cpp vendored Normal file

@ -0,0 +1,770 @@
/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2011 Blender Foundation.
* All rights reserved.
*
* Contributor(s): Blender Foundation,
* Sergey Sharybin
*
* ***** END GPL LICENSE BLOCK *****
*/
/* define this to generate PNG images with content of search areas
tracking between which failed */
#undef DUMP_FAILURE
#include "libmv-capi.h"
#include "glog/logging.h"
#include "Math/v3d_optimization.h"
#include "libmv/tracking/klt_region_tracker.h"
#include "libmv/tracking/trklt_region_tracker.h"
#include "libmv/tracking/pyramid_region_tracker.h"
#include "libmv/tracking/sad.h"
#include "libmv/simple_pipeline/tracks.h"
#include "libmv/simple_pipeline/initialize_reconstruction.h"
#include "libmv/simple_pipeline/bundle.h"
#include "libmv/simple_pipeline/detect.h"
#include "libmv/simple_pipeline/pipeline.h"
#include "libmv/simple_pipeline/camera_intrinsics.h"
#include <stdlib.h>
#ifdef DUMP_FAILURE
# include <png.h>
#endif
#ifdef _MSC_VER
# define snprintf _snprintf
#endif
#define DEFAULT_WINDOW_HALFSIZE 5
typedef struct libmv_RegionTracker {
libmv::TrkltRegionTracker *trklt_region_tracker;
libmv::RegionTracker *region_tracker;
} libmv_RegionTracker;
typedef struct libmv_Reconstruction {
libmv::EuclideanReconstruction reconstruction;
/* used for per-track average error calculation after reconstruction */
libmv::Tracks tracks;
libmv::CameraIntrinsics intrinsics;
double error;
} libmv_Reconstruction;
typedef struct libmv_Features {
int count, margin;
libmv::Feature *features;
} libmv_Features;
/* ************ Logging ************ */
void libmv_initLogging(const char *argv0)
{
google::InitGoogleLogging(argv0);
google::SetCommandLineOption("logtostderr", "1");
google::SetCommandLineOption("v", "0");
google::SetCommandLineOption("stderrthreshold", "7");
google::SetCommandLineOption("minloglevel", "7");
V3D::optimizerVerbosenessLevel = 0;
}
void libmv_startDebugLogging(void)
{
google::SetCommandLineOption("logtostderr", "1");
google::SetCommandLineOption("v", "0");
google::SetCommandLineOption("stderrthreshold", "1");
google::SetCommandLineOption("minloglevel", "0");
V3D::optimizerVerbosenessLevel = 1;
}
void libmv_setLoggingVerbosity(int verbosity)
{
char val[10];
snprintf(val, sizeof(val), "%d", verbosity);
google::SetCommandLineOption("v", val);
V3D::optimizerVerbosenessLevel = verbosity;
}
/* ************ RegionTracker ************ */
libmv_RegionTracker *libmv_regionTrackerNew(int max_iterations, int pyramid_level)
{
libmv::TrkltRegionTracker *trklt_region_tracker = new libmv::TrkltRegionTracker;
trklt_region_tracker->half_window_size = DEFAULT_WINDOW_HALFSIZE;
trklt_region_tracker->max_iterations = max_iterations;
trklt_region_tracker->min_determinant = 1e-4;
libmv::PyramidRegionTracker *region_tracker =
new libmv::PyramidRegionTracker(trklt_region_tracker, pyramid_level);
libmv_RegionTracker *configured_region_tracker = new libmv_RegionTracker;
configured_region_tracker->trklt_region_tracker = trklt_region_tracker;
configured_region_tracker->region_tracker = region_tracker;
return configured_region_tracker;
}
static void floatBufToImage(const float *buf, int width, int height, libmv::FloatImage *image)
{
int x, y, a = 0;
image->resize(height, width);
for (y = 0; y < height; y++) {
for (x = 0; x < width; x++) {
(*image)(y, x, 0) = buf[a++];
}
}
}
#ifdef DUMP_FAILURE
void savePNGImage(png_bytep *row_pointers, int width, int height, int depth, int color_type, char *file_name)
{
png_infop info_ptr;
png_structp png_ptr;
FILE *fp = fopen(file_name, "wb");
if (!fp)
return;
/* 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;
}
png_init_io(png_ptr, fp);
/* write header */
if (setjmp(png_jmpbuf(png_ptr))) {
fclose(fp);
return;
}
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;
}
png_write_image(png_ptr, row_pointers);
/* end write */
if (setjmp(png_jmpbuf(png_ptr))) {
fclose(fp);
return;
}
png_write_end(png_ptr, NULL);
fclose(fp);
}
static void saveImage(char *prefix, libmv::FloatImage image, int x0, int y0)
{
int x, y;
png_bytep *row_pointers;
row_pointers= (png_bytep*)malloc(sizeof(png_bytep)*image.Height());
for (y = 0; y < image.Height(); y++) {
row_pointers[y]= (png_bytep)malloc(sizeof(png_byte)*4*image.Width());
for (x = 0; x < image.Width(); x++) {
if (x0 == x && y0 == 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(y, 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 a= 0;
char buf[128];
snprintf(buf, sizeof(buf), "%s_%02d.png", prefix, ++a);
savePNGImage(row_pointers, image.Width(), image.Height(), 8, PNG_COLOR_TYPE_RGBA, buf);
}
for (y = 0; y < image.Height(); y++) {
free(row_pointers[y]);
}
free(row_pointers);
}
static void saveBytesImage(char *prefix, unsigned char *data, int width, int height)
{
int x, y;
png_bytep *row_pointers;
row_pointers= (png_bytep*)malloc(sizeof(png_bytep)*height);
for (y = 0; y < height; y++) {
row_pointers[y]= (png_bytep)malloc(sizeof(png_byte)*4*width);
for (x = 0; x < width; x++) {
char pixel = data[width*y+x];
row_pointers[y][x*4+0]= pixel;
row_pointers[y][x*4+1]= pixel;
row_pointers[y][x*4+2]= pixel;
row_pointers[y][x*4+3]= 255;
}
}
{
static int a= 0;
char buf[128];
snprintf(buf, sizeof(buf), "%s_%02d.png", prefix, ++a);
savePNGImage(row_pointers, width, height, 8, PNG_COLOR_TYPE_RGBA, buf);
}
for (y = 0; y < height; y++) {
free(row_pointers[y]);
}
free(row_pointers);
}
#endif
int libmv_regionTrackerTrack(libmv_RegionTracker *libmv_tracker, const float *ima1, const float *ima2,
int width, int height, int half_window_size,
double x1, double y1, double *x2, double *y2)
{
libmv::RegionTracker *region_tracker;
libmv::TrkltRegionTracker *trklt_region_tracker;
libmv::FloatImage old_patch, new_patch;
trklt_region_tracker = libmv_tracker->trklt_region_tracker;
region_tracker = libmv_tracker->region_tracker;
trklt_region_tracker->half_window_size = half_window_size;
floatBufToImage(ima1, width, height, &old_patch);
floatBufToImage(ima2, width, height, &new_patch);
#ifndef DUMP_FAILURE
return region_tracker->Track(old_patch, new_patch, x1, y1, x2, y2);
#else
{
double sx2 = *x2, sy2 = *y2;
int result = region_tracker->Track(old_patch, new_patch, x1, y1, x2, y2);
if (!result) {
saveImage("old_patch", old_patch, x1, y1);
saveImage("new_patch", new_patch, sx2, sy2);
}
return result;
}
#endif
}
void libmv_regionTrackerDestroy(libmv_RegionTracker *libmv_tracker)
{
delete libmv_tracker->region_tracker;
delete libmv_tracker;
}
/* ************ Tracks ************ */
void libmv_SADSamplePattern(unsigned char *image, int stride,
float warp[3][2], unsigned char *pattern)
{
libmv::mat32 mat32;
memcpy(mat32.data, warp, sizeof(float)*3*2);
libmv::SamplePattern(image, stride, mat32, pattern, 16);
}
float libmv_SADTrackerTrack(unsigned char *pattern, unsigned char *warped, unsigned char *image, int stride,
int width, int height, float warp[3][2])
{
float result;
libmv::mat32 mat32;
memcpy(mat32.data, warp, sizeof(float)*3*2);
result = libmv::Track(pattern, warped, 16, image, stride, width, height, &mat32, 16, 16);
memcpy(warp, mat32.data, sizeof(float)*3*2);
return result;
}
/* ************ Tracks ************ */
libmv_Tracks *libmv_tracksNew(void)
{
libmv::Tracks *libmv_tracks = new libmv::Tracks();
return (libmv_Tracks *)libmv_tracks;
}
void libmv_tracksInsert(struct libmv_Tracks *libmv_tracks, int image, int track, double x, double y)
{
((libmv::Tracks*)libmv_tracks)->Insert(image, track, x, y);
}
void libmv_tracksDestroy(libmv_Tracks *libmv_tracks)
{
delete (libmv::Tracks*)libmv_tracks;
}
/* ************ Reconstruction solver ************ */
libmv_Reconstruction *libmv_solveReconstruction(libmv_Tracks *tracks, int keyframe1, int keyframe2,
double focal_length, double principal_x, double principal_y, double k1, double k2, double k3)
{
/* Invert the camera intrinsics. */
libmv::vector<libmv::Marker> markers = ((libmv::Tracks*)tracks)->AllMarkers();
libmv_Reconstruction *libmv_reconstruction = new libmv_Reconstruction();
libmv::EuclideanReconstruction *reconstruction = &libmv_reconstruction->reconstruction;
libmv::CameraIntrinsics *intrinsics = &libmv_reconstruction->intrinsics;
intrinsics->SetFocalLength(focal_length, focal_length);
intrinsics->SetPrincipalPoint(principal_x, principal_y);
intrinsics->SetRadialDistortion(k1, k2, k3);
if(focal_length) {
/* do a lens undistortion if focal length is non-zero only */
for (int i = 0; i < markers.size(); ++i) {
intrinsics->InvertIntrinsics(markers[i].x,
markers[i].y,
&(markers[i].x),
&(markers[i].y));
}
}
libmv::Tracks normalized_tracks(markers);
libmv::vector<libmv::Marker> keyframe_markers =
normalized_tracks.MarkersForTracksInBothImages(keyframe1, keyframe2);
libmv::EuclideanReconstructTwoFrames(keyframe_markers, reconstruction);
libmv::EuclideanBundle(normalized_tracks, reconstruction);
libmv::EuclideanCompleteReconstruction(normalized_tracks, reconstruction);
libmv_reconstruction->tracks = *(libmv::Tracks *)tracks;
libmv_reconstruction->error = libmv::EuclideanReprojectionError(*(libmv::Tracks *)tracks, *reconstruction, *intrinsics);
return (libmv_Reconstruction *)libmv_reconstruction;
}
int libmv_reporojectionPointForTrack(libmv_Reconstruction *libmv_reconstruction, int track, double pos[3])
{
libmv::EuclideanReconstruction *reconstruction = &libmv_reconstruction->reconstruction;
libmv::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;
}
static libmv::Marker ProjectMarker(const libmv::EuclideanPoint &point, const libmv::EuclideanCamera &camera,
const libmv::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;
}
double libmv_reporojectionErrorForTrack(libmv_Reconstruction *libmv_reconstruction, int track)
{
libmv::EuclideanReconstruction *reconstruction = &libmv_reconstruction->reconstruction;
libmv::CameraIntrinsics *intrinsics = &libmv_reconstruction->intrinsics;
libmv::vector<libmv::Marker> markers = libmv_reconstruction->tracks.MarkersForTrack(track);
int num_reprojected = 0;
double total_error = 0.0;
for (int i = 0; i < markers.size(); ++i) {
const libmv::EuclideanCamera *camera = reconstruction->CameraForImage(markers[i].image);
const libmv::EuclideanPoint *point = reconstruction->PointForTrack(markers[i].track);
if (!camera || !point) {
continue;
}
num_reprojected++;
libmv::Marker reprojected_marker = ProjectMarker(*point, *camera, *intrinsics);
double ex = reprojected_marker.x - markers[i].x;
double ey = reprojected_marker.y - markers[i].y;
total_error += sqrt(ex*ex + ey*ey);
}
return total_error / num_reprojected;
}
double libmv_reporojectionErrorForImage(libmv_Reconstruction *libmv_reconstruction, int image)
{
libmv::EuclideanReconstruction *reconstruction = &libmv_reconstruction->reconstruction;
libmv::CameraIntrinsics *intrinsics = &libmv_reconstruction->intrinsics;
libmv::vector<libmv::Marker> markers = libmv_reconstruction->tracks.MarkersInImage(image);
const libmv::EuclideanCamera *camera = reconstruction->CameraForImage(image);
int num_reprojected = 0;
double total_error = 0.0;
if (!camera)
return 0;
for (int i = 0; i < markers.size(); ++i) {
const libmv::EuclideanPoint *point = reconstruction->PointForTrack(markers[i].track);
if (!point) {
continue;
}
num_reprojected++;
libmv::Marker reprojected_marker = ProjectMarker(*point, *camera, *intrinsics);
double ex = reprojected_marker.x - markers[i].x;
double ey = reprojected_marker.y - markers[i].y;
total_error += sqrt(ex*ex + ey*ey);
}
return total_error / num_reprojected;
}
int libmv_reporojectionCameraForImage(libmv_Reconstruction *libmv_reconstruction, int image, double mat[4][4])
{
libmv::EuclideanReconstruction *reconstruction = &libmv_reconstruction->reconstruction;
libmv::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(libmv_Reconstruction *libmv_reconstruction)
{
return libmv_reconstruction->error;
}
void libmv_destroyReconstruction(libmv_Reconstruction *libmv_reconstruction)
{
delete libmv_reconstruction;
}
/* ************ feature detector ************ */
struct libmv_Features *libmv_detectFeaturesFAST(unsigned char *data, int width, int height, int stride,
int margin, int min_trackness, int min_distance)
{
libmv::Feature *features = NULL;
std::vector<libmv::Feature> v;
libmv_Features *libmv_features = new libmv_Features();
int i= 0, count;
if(margin) {
data += margin*stride+margin;
width -= 2*margin;
height -= 2*margin;
}
v = libmv::DetectFAST(data, width, height, stride, min_trackness, min_distance);
count = v.size();
if(count) {
features= new libmv::Feature[count];
for(std::vector<libmv::Feature>::iterator it = v.begin(); it != v.end(); it++) {
features[i++]= *it;
}
}
libmv_features->features = features;
libmv_features->count = count;
libmv_features->margin = margin;
return (libmv_Features *)libmv_features;
}
struct libmv_Features *libmv_detectFeaturesMORAVEC(unsigned char *data, int width, int height, int stride,
int margin, int count, int min_distance)
{
libmv::Feature *features = NULL;
libmv_Features *libmv_features = new libmv_Features;
if(count) {
if(margin) {
data += margin*stride+margin;
width -= 2*margin;
height -= 2*margin;
}
features = new libmv::Feature[count];
libmv::DetectMORAVEC(data, stride, width, height, features, &count, min_distance, NULL);
}
libmv_features->count = count;
libmv_features->margin = margin;
libmv_features->features = features;
return libmv_features;
}
int libmv_countFeatures(struct libmv_Features *libmv_features)
{
return libmv_features->count;
}
void libmv_getFeature(struct libmv_Features *libmv_features, int number, double *x, double *y, double *score, double *size)
{
libmv::Feature feature= libmv_features->features[number];
*x = feature.x + libmv_features->margin;
*y = feature.y + libmv_features->margin;
*score = feature.score;
*size = feature.size;
}
void libmv_destroyFeatures(struct libmv_Features *libmv_features)
{
if(libmv_features->features)
delete [] libmv_features->features;
delete libmv_features;
}
/* ************ camera intrinsics ************ */
struct libmv_CameraIntrinsics *libmv_CameraIntrinsicsNew(double focal_length, double principal_x, double principal_y,
double k1, double k2, double k3, int width, int height)
{
libmv::CameraIntrinsics *intrinsics= new libmv::CameraIntrinsics();
intrinsics->SetFocalLength(focal_length, focal_length);
intrinsics->SetPrincipalPoint(principal_x, principal_y);
intrinsics->SetRadialDistortion(k1, k2, k3);
intrinsics->SetImageSize(width, height);
return (struct libmv_CameraIntrinsics *) intrinsics;
}
struct libmv_CameraIntrinsics *libmv_CameraIntrinsicsCopy(struct libmv_CameraIntrinsics *libmvIntrinsics)
{
libmv::CameraIntrinsics *orig_intrinsics = (libmv::CameraIntrinsics *) libmvIntrinsics;
libmv::CameraIntrinsics *new_intrinsics= new libmv::CameraIntrinsics(*orig_intrinsics);
return (struct libmv_CameraIntrinsics *) new_intrinsics;
}
void libmv_CameraIntrinsicsDestroy(struct libmv_CameraIntrinsics *libmvIntrinsics)
{
libmv::CameraIntrinsics *intrinsics = (libmv::CameraIntrinsics *) libmvIntrinsics;
delete intrinsics;
}
void libmv_CameraIntrinsicsUpdate(struct libmv_CameraIntrinsics *libmvIntrinsics, double focal_length,
double principal_x, double principal_y, double k1, double k2, double k3, int width, int height)
{
libmv::CameraIntrinsics *intrinsics = (libmv::CameraIntrinsics *) libmvIntrinsics;
if (intrinsics->focal_length() != focal_length)
intrinsics->SetFocalLength(focal_length, focal_length);
if (intrinsics->principal_point_x() != principal_x || intrinsics->principal_point_y() != principal_y)
intrinsics->SetFocalLength(focal_length, focal_length);
if (intrinsics->k1() != k1 || intrinsics->k2() != k2 || intrinsics->k3() != k3)
intrinsics->SetRadialDistortion(k1, k2, k3);
if (intrinsics->image_width() != width || intrinsics->image_height() != height)
intrinsics->SetImageSize(width, height);
}
void libmv_CameraIntrinsicsUndistortByte(struct libmv_CameraIntrinsics *libmvIntrinsics,
unsigned char *src, unsigned char *dst, int width, int height, float overscan, int channels)
{
libmv::CameraIntrinsics *intrinsics = (libmv::CameraIntrinsics *) libmvIntrinsics;
intrinsics->Undistort(src, dst, width, height, overscan, channels);
}
void libmv_CameraIntrinsicsUndistortFloat(struct libmv_CameraIntrinsics *libmvIntrinsics,
float *src, float *dst, int width, int height, float overscan, int channels)
{
libmv::CameraIntrinsics *intrinsics = (libmv::CameraIntrinsics *) libmvIntrinsics;
intrinsics->Undistort(src, dst, width, height, overscan, channels);
}
void libmv_CameraIntrinsicsDistortByte(struct libmv_CameraIntrinsics *libmvIntrinsics,
unsigned char *src, unsigned char *dst, int width, int height, float overscan, int channels)
{
libmv::CameraIntrinsics *intrinsics = (libmv::CameraIntrinsics *) libmvIntrinsics;
intrinsics->Distort(src, dst, width, height, overscan, channels);
}
void libmv_CameraIntrinsicsDistortFloat(struct libmv_CameraIntrinsics *libmvIntrinsics,
float *src, float *dst, int width, int height, float overscan, int channels)
{
libmv::CameraIntrinsics *intrinsics = (libmv::CameraIntrinsics *) libmvIntrinsics;
intrinsics->Distort(src, dst, width, height, overscan, channels);
}
/* ************ distortion ************ */
void libmv_undistortByte(double focal_length, double principal_x, double principal_y, double k1, double k2, double k3,
unsigned char *src, unsigned char *dst, int width, int height, float overscan, int channels)
{
libmv::CameraIntrinsics intrinsics;
intrinsics.SetFocalLength(focal_length, focal_length);
intrinsics.SetPrincipalPoint(principal_x, principal_y);
intrinsics.SetRadialDistortion(k1, k2, k3);
intrinsics.Undistort(src, dst, width, height, overscan, channels);
}
void libmv_undistortFloat(double focal_length, double principal_x, double principal_y, double k1, double k2, double k3,
float *src, float *dst, int width, int height, float overscan, int channels)
{
libmv::CameraIntrinsics intrinsics;
intrinsics.SetFocalLength(focal_length, focal_length);
intrinsics.SetPrincipalPoint(principal_x, principal_y);
intrinsics.SetRadialDistortion(k1, k2, k3);
intrinsics.Undistort(src, dst, width, height, overscan, channels);
}
void libmv_distortByte(double focal_length, double principal_x, double principal_y, double k1, double k2, double k3,
unsigned char *src, unsigned char *dst, int width, int height, float overscan, int channels)
{
libmv::CameraIntrinsics intrinsics;
intrinsics.SetFocalLength(focal_length, focal_length);
intrinsics.SetPrincipalPoint(principal_x, principal_y);
intrinsics.SetRadialDistortion(k1, k2, k3);
intrinsics.Distort(src, dst, width, height, overscan, channels);
}
void libmv_distortFloat(double focal_length, double principal_x, double principal_y, double k1, double k2, double k3,
float *src, float *dst, int width, int height, float overscan, int channels)
{
libmv::CameraIntrinsics intrinsics;
intrinsics.SetFocalLength(focal_length, focal_length);
intrinsics.SetPrincipalPoint(principal_x, principal_y);
intrinsics.SetRadialDistortion(k1, k2, k3);
intrinsics.Distort(src, dst, width, height, overscan, channels);
}
/* ************ utils ************ */
void libmv_applyCameraIntrinsics(double focal_length, double principal_x, double principal_y, double k1, double k2, double k3,
double x, double y, double *x1, double *y1)
{
libmv::CameraIntrinsics intrinsics;
intrinsics.SetFocalLength(focal_length, focal_length);
intrinsics.SetPrincipalPoint(principal_x, principal_y);
intrinsics.SetRadialDistortion(k1, k2, k3);
if(focal_length) {
/* do a lens undistortion if focal length is non-zero only */
intrinsics.ApplyIntrinsics(x, y, x1, y1);
}
}
void libmv_InvertIntrinsics(double focal_length, double principal_x, double principal_y, double k1, double k2, double k3,
double x, double y, double *x1, double *y1)
{
libmv::CameraIntrinsics intrinsics;
intrinsics.SetFocalLength(focal_length, focal_length);
intrinsics.SetPrincipalPoint(principal_x, principal_y);
intrinsics.SetRadialDistortion(k1, k2, k3);
if(focal_length) {
/* do a lens distortion if focal length is non-zero only */
intrinsics.InvertIntrinsics(x, y, x1, y1);
}
}

128
extern/libmv/libmv-capi.h vendored Normal file

@ -0,0 +1,128 @@
/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* The Original Code is Copyright (C) 2011 Blender Foundation.
* All rights reserved.
*
* Contributor(s): Blender Foundation,
* Sergey Sharybin
*
* ***** END GPL LICENSE BLOCK *****
*/
#ifndef LIBMV_C_API_H
#define LIBMV_C_API_H
#ifdef __cplusplus
extern "C" {
#endif
struct libmv_RegionTracker;
struct libmv_Tracks;
struct libmv_Reconstruction;
struct libmv_Features;
struct libmv_CameraIntrinsics;
/* Logging */
void libmv_initLogging(const char *argv0);
void libmv_startDebugLogging(void);
void libmv_setLoggingVerbosity(int verbosity);
/* RegionTracker */
struct libmv_RegionTracker *libmv_regionTrackerNew(int max_iterations, int pyramid_level);
int libmv_regionTrackerTrack(struct libmv_RegionTracker *libmv_tracker, const float *ima1, const float *ima2,
int width, int height, int half_window_size,
double x1, double y1, double *x2, double *y2);
void libmv_regionTrackerDestroy(struct libmv_RegionTracker *libmv_tracker);
/* SAD Tracker */
void libmv_SADSamplePattern(unsigned char *image, int stride,
float warp[3][2], unsigned char *pattern);
float libmv_SADTrackerTrack(unsigned char *pattern, unsigned char *warped, unsigned char *image,
int stride, int width, int height, float warp[3][2]);
/* Tracks */
struct libmv_Tracks *libmv_tracksNew(void);
void libmv_tracksInsert(struct libmv_Tracks *libmv_tracks, int image, int track, double x, double y);
void libmv_tracksDestroy(struct libmv_Tracks *libmv_tracks);
/* Reconstruction solver */
struct libmv_Reconstruction *libmv_solveReconstruction(struct libmv_Tracks *tracks, int keyframe1, int keyframe2,
double focal_length, double principal_x, double principal_y, double k1, double k2, double k3);
int libmv_reporojectionPointForTrack(struct libmv_Reconstruction *libmv_reconstruction, int track, double pos[3]);
double libmv_reporojectionErrorForTrack(struct libmv_Reconstruction *libmv_reconstruction, int track);
double libmv_reporojectionErrorForImage(struct libmv_Reconstruction *libmv_reconstruction, int image);
int libmv_reporojectionCameraForImage(struct libmv_Reconstruction *libmv_reconstruction, int image, double mat[4][4]);
double libmv_reprojectionError(struct libmv_Reconstruction *libmv_reconstruction);
void libmv_destroyReconstruction(struct libmv_Reconstruction *libmv_reconstruction);
/* feature detector */
struct libmv_Features *libmv_detectFeaturesFAST(unsigned char *data, int width, int height, int stride,
int margin, int min_trackness, int min_distance);
struct libmv_Features *libmv_detectFeaturesMORAVEC(unsigned char *data, int width, int height, int stride,
int margin, int count, int min_distance);
int libmv_countFeatures(struct libmv_Features *libmv_features);
void libmv_getFeature(struct libmv_Features *libmv_features, int number, double *x, double *y, double *score, double *size);
void libmv_destroyFeatures(struct libmv_Features *libmv_features);
/* camera intrinsics */
struct libmv_CameraIntrinsics *libmv_CameraIntrinsicsNew(double focal_length, double principal_x, double principal_y,
double k1, double k2, double k3, int width, int height);
struct libmv_CameraIntrinsics *libmv_CameraIntrinsicsCopy(struct libmv_CameraIntrinsics *libmvIntrinsics);
struct libmv_CameraIntrinsics *libmv_CameraIntrinsicsCopy(struct libmv_CameraIntrinsics *libmvIntrinsics);
void libmv_CameraIntrinsicsDestroy(struct libmv_CameraIntrinsics *libmvIntrinsics);
void libmv_CameraIntrinsicsUpdate(struct libmv_CameraIntrinsics *libmvIntrinsics, double focal_length,
double principal_x, double principal_y, double k1, double k2, double k3, int width, int height);
void libmv_CameraIntrinsicsUndistortByte(struct libmv_CameraIntrinsics *libmvIntrinsics,
unsigned char *src, unsigned char *dst, int width, int height, float overscan, int channels);
void libmv_CameraIntrinsicsUndistortFloat(struct libmv_CameraIntrinsics *libmvIntrinsics,
float *src, float *dst, int width, int height, float overscan, int channels);
void libmv_CameraIntrinsicsDistortByte(struct libmv_CameraIntrinsics *libmvIntrinsics,
unsigned char *src, unsigned char *dst, int width, int height, float overscan, int channels);
void libmv_CameraIntrinsicsDistortFloat(struct libmv_CameraIntrinsics *libmvIntrinsics,
float *src, float *dst, int width, int height, float overscan, int channels);
/* dsitortion */
void libmv_undistortByte(double focal_length, double principal_x, double principal_y, double k1, double k2, double k3,
unsigned char *src, unsigned char *dst, int width, int height, int channels);
void libmv_undistortFloat(double focal_length, double principal_x, double principal_y, double k1, double k2, double k3,
float *src, float *dst, int width, int height, int channels);
void libmv_distortByte(double focal_length, double principal_x, double principal_y, double k1, double k2, double k3,
unsigned char *src, unsigned char *dst, int width, int height, int channels);
void libmv_distortFloat(double focal_length, double principal_x, double principal_y, double k1, double k2, double k3,
float *src, float *dst, int width, int height, int channels);
/* utils */
void libmv_applyCameraIntrinsics(double focal_length, double principal_x, double principal_y, double k1, double k2, double k3,
double x, double y, double *x1, double *y1);
void libmv_InvertIntrinsics(double focal_length, double principal_x, double principal_y, double k1, double k2, double k3,
double x, double y, double *x1, double *y1);
#ifdef __cplusplus
}
#endif
#endif // LIBMV_C_API_H

37
extern/libmv/libmv/base/id_generator.h vendored Normal file

@ -0,0 +1,37 @@
// 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

60
extern/libmv/libmv/base/scoped_ptr.h vendored Normal file

@ -0,0 +1,60 @@
// 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
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_;
};
} // namespace libmv
#endif // LIBMV_BASE_SCOPED_PTR_H

172
extern/libmv/libmv/base/vector.h vendored Normal file

@ -0,0 +1,172 @@
// 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 <cstring>
#include <new>
#include <Eigen/Core>
namespace libmv {
// A simple container class, which guarantees 16 byte alignment needed for most
// vectorization. Don't use this container for classes that cannot be copied
// via memcpy.
// FIXME: this class has some issues:
// - doesn't support iterators.
// - impede compatibility with code using STL.
// - the STL already provide support for custom allocators
// it could be replaced with a simple
// template <T> class vector : std::vector<T, aligned_allocator> {} declaration
// provided it doesn't break code relying on libmv::vector specific behavior
template <typename T,
typename Allocator = Eigen::aligned_allocator<T> >
class vector {
public:
~vector() { clear(); }
vector() { init(); }
vector(int size) { init(); resize(size); }
vector(int size, const T & val) {
init();
resize(size);
std::fill(data_, data_+size_, val); }
// Copy constructor and assignment.
vector(const vector<T, Allocator> &rhs) {
init();
copy(rhs);
}
vector<T, Allocator> &operator=(const vector<T, Allocator> &rhs) {
if (&rhs != this) {
copy(rhs);
}
return *this;
}
/// Swaps the contents of two vectors in constant time.
void swap(vector<T, Allocator> &other) {
std::swap(allocator_, other.allocator_);
std::swap(size_, other.size_);
std::swap(capacity_, other.capacity_);
std::swap(data_, other.data_);
}
T *data() const { return data_; }
int size() const { return size_; }
int capacity() const { return capacity_; }
const T& back() const { return data_[size_ - 1]; }
T& back() { return data_[size_ - 1]; }
const T& front() const { return data_[0]; }
T& front() { return data_[0]; }
const T& operator[](int n) const { return data_[n]; }
T& operator[](int n) { return data_[n]; }
const T * begin() const { return data_; }
const T * end() const { return data_+size_; }
T * begin() { return data_; }
T * end() { return data_+size_; }
void resize(size_t size) {
reserve(size);
if (size > size_) {
construct(size_, size);
} else if (size < size_) {
destruct(size, size_);
}
size_ = size;
}
void push_back(const T &value) {
if (size_ == capacity_) {
reserve(size_ ? 2 * size_ : 1);
}
new (&data_[size_++]) T(value);
}
void pop_back() {
resize(size_ - 1);
}
void clear() {
destruct(0, size_);
deallocate();
init();
}
void reserve(unsigned int size) {
if (size > size_) {
T *data = static_cast<T *>(allocate(size));
memcpy(data, data_, sizeof(*data)*size_);
allocator_.deallocate(data_, capacity_);
data_ = data;
capacity_ = size;
}
}
private:
void construct(int start, int end) {
for (int i = start; i < end; ++i) {
new (&data_[i]) T;
}
}
void destruct(int start, int end) {
for (int i = start; i < end; ++i) {
data_[i].~T();
}
}
void init() {
size_ = 0;
data_ = 0;
capacity_ = 0;
}
void *allocate(int size) {
return size ? allocator_.allocate(size) : 0;
}
void deallocate() {
allocator_.deallocate(data_, size_);
data_ = 0;
}
void copy(const vector<T, Allocator> &rhs) {
resize(rhs.size());
for (int i = 0; i < rhs.size(); ++i) {
(*this)[i] = rhs[i];
}
}
Allocator allocator_;
size_t size_;
size_t capacity_;
T *data_;
};
} // namespace libmv
#endif // LIBMV_BASE_VECTOR_H

34
extern/libmv/libmv/base/vector_utils.h vendored Normal file

@ -0,0 +1,34 @@
// 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_

108
extern/libmv/libmv/image/array_nd.cc vendored Normal file

@ -0,0 +1,108 @@
// 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/image.h"
#include <iostream>
#include <cmath>
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

473
extern/libmv/libmv/image/array_nd.h vendored Normal file

@ -0,0 +1,473 @@
// 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) : data_(data), own_data(false) { Resize(s0, s1, s2); }
/// Destructor deletes pixel data.
~ArrayND() {
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));
}
}
}
}
} // namespace libmv
#endif // LIBMV_IMAGE_ARRAY_ND_H

305
extern/libmv/libmv/image/convolve.cc vendored Normal file

@ -0,0 +1,305 @@
// 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 "libmv/image/image.h"
#include "libmv/image/convolve.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 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);
}
} // namespace libmv

93
extern/libmv/libmv/image/convolve.h vendored Normal file

@ -0,0 +1,93 @@
// 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);
} // namespace libmv
#endif // LIBMV_IMAGE_CONVOLVE_H_

158
extern/libmv/libmv/image/image.h vendored Normal file

@ -0,0 +1,158 @@
// 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

103
extern/libmv/libmv/image/sample.h vendored Normal file

@ -0,0 +1,103 @@
// 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);
}
static inline void LinearInitAxis(float fx, int width,
int *x1, int *x2,
float *dx1, float *dx2) {
const int ix = int(fx);
if (ix < 0) {
*x1 = 0;
*x2 = 0;
*dx1 = 1;
*dx2 = 0;
} else if (ix > width-2) {
*x1 = width-1;
*x2 = width-1;
*dx1 = 1;
*dx2 = 0;
} else {
*x1 = ix;
*x2 = *x1 + 1;
*dx1 = *x2 - fx;
*dx2 = 1 - *dx1;
}
}
/// 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 dx1, dy1, dx2, dy2;
LinearInitAxis(y, image.Height(), &y1, &y2, &dy1, &dy2);
LinearInitAxis(x, image.Width(), &x1, &x2, &dx1, &dx2);
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(dy1 * ( dx1 * im11 + dx2 * im12 ) +
dy2 * ( dx1 * im21 + dx2 * 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) {
(*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;
}
}
}
}
} // namespace libmv
#endif // LIBMV_IMAGE_SAMPLE_H_

90
extern/libmv/libmv/image/tuple.h vendored Normal file

@ -0,0 +1,90 @@
// 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 lenght 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

31
extern/libmv/libmv/logging/logging.h vendored Normal file

@ -0,0 +1,31 @@
// 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"
#define LG LOG(INFO)
#define V0 LOG(INFO)
#define V1 LOG(INFO)
#define V2 LOG(INFO)
#endif // LIBMV_LOGGING_LOGGING_H

@ -0,0 +1,99 @@
// 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;
*T << xfactor, 0, -xfactor * mean(0),
0, yfactor, -yfactor * mean(1),
0, 0, 1;
}
// 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();
}
*T << factor, 0, -factor * mean(0),
0, factor, -factor * mean(1),
0, 0, 1;
}
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

@ -0,0 +1,60 @@
// 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,661 @@
// 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 <cmath>
#include <limits>
#include <Eigen/SVD>
#include <Eigen/Geometry>
#include "libmv/base/vector.h"
#include "libmv/logging/logging.h"
#include "libmv/multiview/euclidean_resection.h"
#include "libmv/multiview/projection.h"
namespace libmv {
namespace euclidean_resection {
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;
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;
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;
// 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);
(*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);
// 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.
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
void 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);
}
Mat3 C2inv = C2.inverse();
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();
}
}
// Estimates the coordinates of all real points in the camera coordinate frame
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);
ComputeBarycentricCoordinates(X_centered, X_control_points, &alphas);
// Estimates the M matrix with the barycentric coordinates
Mat M(2 * num_points, 12);
Eigen::Matrix<double, 2, 12> sub_M;
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);
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);
}
// 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++) {
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));
}
Vec6 rho;
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();
// 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);
// TODO(julien): Document where the "1e-3" magical constant comes from below.
// 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, 1e-3)) {
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, 1e-3)) {
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, 1e-3)) {
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(0);
VLOG(2) << "RMSE for solution 2: " << rmse(0);
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;
}
} // namespace resection
} // namespace libmv

@ -0,0 +1,124 @@
// 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/numeric/numeric.h"
#include "libmv/multiview/projection.h"
namespace libmv {
namespace euclidean_resection {
enum ResectionMethod {
RESECTION_ANSAR_DANIILIDIS,
RESECTION_EPNP,
};
/**
* 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);
} // namespace euclidean_resection
} // namespace libmv
#endif /* LIBMV_MULTIVIEW_EUCLIDEAN_RESECTION_H_ */

@ -0,0 +1,391 @@
// 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/logging/logging.h"
#include "libmv/numeric/numeric.h"
#include "libmv/numeric/poly.h"
#include "libmv/multiview/conditioning.h"
#include "libmv/multiview/projection.h"
#include "libmv/multiview/triangulation.h"
#include "libmv/multiview/fundamental.h"
namespace libmv {
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
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),
b = F1(0, 1), k = F2(0, 1),
c = F1(0, 2), l = F2(0, 2),
d = F1(1, 0), m = F2(1, 0),
e = F1(1, 1), n = F2(1, 1),
f = F1(1, 2), o = F2(1, 2),
g = F1(2, 0), p = F2(2, 0),
h = F1(2, 1), q = F2(2, 1),
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();
Vec3 d = USV.singularValues();
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;
W << 0, -1, 0,
1, 0, 0,
0, 0, 1;
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;
}
}
} // namespace libmv

@ -0,0 +1,144 @@
// 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);
/**
* The normalized 8-point fundamental matrix solver.
*/
double NormalizedEightPointSolver(const Mat &x1,
const Mat &x2,
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);
} // namespace libmv
#endif // LIBMV_MULTIVIEW_FUNDAMENTAL_H_

@ -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,221 @@
// 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;
Qx << 1, 0, 0,
0, c, -s,
0, s, c;
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;
Qy << c, 0, s,
0, 1, 0,
-s, 0, c;
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;
Qz << c,-s, 0,
s, c, 0,
0, 0, 1;
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;
S << 1, 0, 0,
0,-1, 0,
0, 0, 1;
K = K * S;
R = S * R;
}
if (K(0,0) < 0) {
Mat3 S;
S << -1, 0, 0,
0, 1, 0,
0, 0, 1;
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;
T << 1, 0, principal_point_new(0) - principal_point(0),
0, 1, principal_point_new(1) - principal_point(1),
0, 0, 1;
*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;
T << 1, 0, 0,
0, aspect_ratio_new / aspect_ratio, 0,
0, 0, 1;
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

@ -0,0 +1,231 @@
// 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, x);
*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_

@ -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

@ -0,0 +1,49 @@
// 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/numeric/numeric.h"
#include "libmv/multiview/projection.h"
#include "libmv/multiview/triangulation.h"
namespace libmv {
// HZ 12.2 pag.312
void TriangulateDLT(const Mat34 &P1, const Vec2 &x1,
const Mat34 &P2, const Vec2 &x2,
Vec4 *X_homogeneous) {
Mat4 design;
for (int i = 0; i < 4; ++i) {
design(0,i) = x1(0) * P1(2,i) - P1(0,i);
design(1,i) = x1(1) * P1(2,i) - P1(1,i);
design(2,i) = x2(0) * P2(2,i) - P2(0,i);
design(3,i) = x2(1) * P2(2,i) - P2(1,i);
}
Nullspace(&design, X_homogeneous);
}
void TriangulateDLT(const Mat34 &P1, const Vec2 &x1,
const Mat34 &P2, const Vec2 &x2,
Vec3 *X_euclidean) {
Vec4 X_homogeneous;
TriangulateDLT(P1, x1, P2, x2, &X_homogeneous);
HomogeneousToEuclidean(X_homogeneous, X_euclidean);
}
} // namespace libmv

@ -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_MULTIVIEW_TRIANGULATION_H_
#define LIBMV_MULTIVIEW_TRIANGULATION_H_
#include "libmv/numeric/numeric.h"
namespace libmv {
void TriangulateDLT(const Mat34 &P1, const Vec2 &x1,
const Mat34 &P2, const Vec2 &x2,
Vec4 *X_homogeneous);
void TriangulateDLT(const Mat34 &P1, const Vec2 &x1,
const Mat34 &P2, const Vec2 &x2,
Vec3 *X_euclidean);
} // namespace libmv
#endif // LIBMV_MULTIVIEW_TRIANGULATION_H_

261
extern/libmv/libmv/numeric/dogleg.h vendored Normal file

@ -0,0 +1,261 @@
// 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.
//
// A simple implementation of Powell's dogleg nonlinear minimization.
//
// [1] K. Madsen, H. Nielsen, O. Tingleoff. Methods for Non-linear Least
// Squares Problems.
// http://www2.imm.dtu.dk/pubdb/views/edoc_download.php/3215/pdf/imm3215.pdf
//
// TODO(keir): Cite the Lourakis' dogleg paper.
#ifndef LIBMV_NUMERIC_DOGLEG_H
#define LIBMV_NUMERIC_DOGLEG_H
#include <cmath>
#include <cstdio>
#include "libmv/numeric/numeric.h"
#include "libmv/numeric/function_derivative.h"
#include "libmv/logging/logging.h"
namespace libmv {
template<typename Function,
typename Jacobian = NumericJacobian<Function>,
typename Solver = Eigen::PartialPivLU<
Matrix<typename Function::FMatrixType::RealScalar,
Function::XMatrixType::RowsAtCompileTime,
Function::XMatrixType::RowsAtCompileTime> > >
class Dogleg {
public:
typedef typename Function::XMatrixType::RealScalar Scalar;
typedef typename Function::FMatrixType FVec;
typedef typename Function::XMatrixType Parameters;
typedef Matrix<typename Function::FMatrixType::RealScalar,
Function::FMatrixType::RowsAtCompileTime,
Function::XMatrixType::RowsAtCompileTime> JMatrixType;
typedef Matrix<typename JMatrixType::RealScalar,
JMatrixType::ColsAtCompileTime,
JMatrixType::ColsAtCompileTime> AMatrixType;
enum Status {
RUNNING,
GRADIENT_TOO_SMALL, // eps > max(J'*f(x))
RELATIVE_STEP_SIZE_TOO_SMALL, // eps > ||dx|| / ||x||
TRUST_REGION_TOO_SMALL, // eps > radius / ||x||
ERROR_TOO_SMALL, // eps > ||f(x)||
HIT_MAX_ITERATIONS,
};
enum Step {
DOGLEG,
GAUSS_NEWTON,
STEEPEST_DESCENT,
};
Dogleg(const Function &f)
: f_(f), df_(f) {}
struct SolverParameters {
SolverParameters()
: gradient_threshold(1e-16),
relative_step_threshold(1e-16),
error_threshold(1e-16),
initial_trust_radius(1e0),
max_iterations(500) {}
Scalar gradient_threshold; // eps > max(J'*f(x))
Scalar relative_step_threshold; // eps > ||dx|| / ||x||
Scalar error_threshold; // eps > ||f(x)||
Scalar initial_trust_radius; // Initial u for solving normal equations.
int max_iterations; // Maximum number of solver iterations.
};
struct Results {
Scalar error_magnitude; // ||f(x)||
Scalar gradient_magnitude; // ||J'f(x)||
int iterations;
Status status;
};
Status Update(const Parameters &x, const SolverParameters &params,
JMatrixType *J, AMatrixType *A, FVec *error, Parameters *g) {
*J = df_(x);
// TODO(keir): In the case of m = n, avoid computing A and just do J^-1 directly.
*A = (*J).transpose() * (*J);
*error = f_(x);
*g = (*J).transpose() * *error;
if (g->array().abs().maxCoeff() < params.gradient_threshold) {
return GRADIENT_TOO_SMALL;
} else if (error->array().abs().maxCoeff() < params.error_threshold) {
return ERROR_TOO_SMALL;
}
return RUNNING;
}
Step SolveDoglegDirection(const Parameters &dx_sd,
const Parameters &dx_gn,
Scalar radius,
Scalar alpha,
Parameters *dx_dl,
Scalar *beta) {
Parameters a, b_minus_a;
// Solve for Dogleg step dx_dl.
if (dx_gn.norm() < radius) {
*dx_dl = dx_gn;
return GAUSS_NEWTON;
} else if (alpha * dx_sd.norm() > radius) {
*dx_dl = (radius / dx_sd.norm()) * dx_sd;
return STEEPEST_DESCENT;
} else {
Parameters a = alpha * dx_sd;
const Parameters &b = dx_gn;
b_minus_a = a - b;
Scalar Mbma2 = b_minus_a.squaredNorm();
Scalar Ma2 = a.squaredNorm();
Scalar c = a.dot(b_minus_a);
Scalar radius2 = radius*radius;
if (c <= 0) {
*beta = (-c + sqrt(c*c + Mbma2*(radius2 - Ma2)))/(Mbma2);
} else {
*beta = (radius2 - Ma2) /
(c + sqrt(c*c + Mbma2*(radius2 - Ma2)));
}
*dx_dl = alpha * dx_sd + (*beta) * (dx_gn - alpha*dx_sd);
return DOGLEG;
}
}
Results minimize(Parameters *x_and_min) {
SolverParameters params;
return minimize(params, x_and_min);
}
Results minimize(const SolverParameters &params, Parameters *x_and_min) {
Parameters &x = *x_and_min;
JMatrixType J;
AMatrixType A;
FVec error;
Parameters g;
Results results;
results.status = Update(x, params, &J, &A, &error, &g);
Scalar radius = params.initial_trust_radius;
bool x_updated = true;
Parameters x_new;
Parameters dx_sd; // Steepest descent step.
Parameters dx_dl; // Dogleg step.
Parameters dx_gn; // Gauss-Newton step.
printf("iteration ||f(x)|| max(g) radius\n");
int i = 0;
for (; results.status == RUNNING && i < params.max_iterations; ++i) {
printf("%9d %12g %12g %12g",
i, f_(x).norm(), g.array().abs().maxCoeff(), radius);
//LG << "iteration: " << i;
//LG << "||f(x)||: " << f_(x).norm();
//LG << "max(g): " << g.cwise().abs().maxCoeff();
//LG << "radius: " << radius;
// Eqn 3.19 from [1]
Scalar alpha = g.squaredNorm() / (J*g).squaredNorm();
// Solve for steepest descent direction dx_sd.
dx_sd = -g;
// Solve for Gauss-Newton direction dx_gn.
if (x_updated) {
// TODO(keir): See Appendix B of [1] for discussion of when A is
// singular and there are many solutions. Solving that involves the SVD
// and is slower, but should still work.
Solver solver(A);
dx_gn = solver.solve(-g);
if (!(A * dx_gn).isApprox(-g)) {
LOG(ERROR) << "Failed to solve normal eqns. TODO: Solve via SVD.";
return results;
}
x_updated = false;
}
// Solve for dogleg direction dx_dl.
Scalar beta = 0;
Step step = SolveDoglegDirection(dx_sd, dx_gn, radius, alpha,
&dx_dl, &beta);
Scalar e3 = params.relative_step_threshold;
if (dx_dl.norm() < e3*(x.norm() + e3)) {
results.status = RELATIVE_STEP_SIZE_TOO_SMALL;
break;
}
x_new = x + dx_dl;
Scalar actual = f_(x).squaredNorm() - f_(x_new).squaredNorm();
Scalar predicted = 0;
if (step == GAUSS_NEWTON) {
predicted = f_(x).squaredNorm();
} else if (step == STEEPEST_DESCENT) {
predicted = radius * (2*alpha*g.norm() - radius) / 2 / alpha;
} else if (step == DOGLEG) {
predicted = 0.5 * alpha * (1-beta)*(1-beta)*g.squaredNorm() +
beta*(2-beta)*f_(x).squaredNorm();
}
Scalar rho = actual / predicted;
if (step == GAUSS_NEWTON) printf(" GAUSS");
if (step == STEEPEST_DESCENT) printf(" STEE");
if (step == DOGLEG) printf(" DOGL");
printf(" %12g %12g %12g\n", rho, actual, predicted);
if (rho > 0) {
// Accept update because the linear model is a good fit.
x = x_new;
results.status = Update(x, params, &J, &A, &error, &g);
x_updated = true;
}
if (rho > 0.75) {
radius = std::max(radius, 3*dx_dl.norm());
} else if (rho < 0.25) {
radius /= 2;
if (radius < e3 * (x.norm() + e3)) {
results.status = TRUST_REGION_TOO_SMALL;
}
}
}
if (results.status == RUNNING) {
results.status = HIT_MAX_ITERATIONS;
}
results.error_magnitude = error.norm();
results.gradient_magnitude = g.norm();
results.iterations = i;
return results;
}
private:
const Function &f_;
Jacobian df_;
};
} // namespace mv
#endif // LIBMV_NUMERIC_DOGLEG_H

@ -0,0 +1,107 @@
// 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_NUMERIC_DERIVATIVE_H
#define LIBMV_NUMERIC_DERIVATIVE_H
#include <cmath>
#include "libmv/numeric/numeric.h"
#include "libmv/logging/logging.h"
namespace libmv {
// Numeric derivative of a function.
// TODO(keir): Consider adding a quadratic approximation.
enum NumericJacobianMode {
CENTRAL,
FORWARD,
};
template<typename Function, NumericJacobianMode mode=CENTRAL>
class NumericJacobian {
public:
typedef typename Function::XMatrixType Parameters;
typedef typename Function::XMatrixType::RealScalar XScalar;
typedef typename Function::FMatrixType FMatrixType;
typedef Matrix<typename Function::FMatrixType::RealScalar,
Function::FMatrixType::RowsAtCompileTime,
Function::XMatrixType::RowsAtCompileTime>
JMatrixType;
NumericJacobian(const Function &f) : f_(f) {}
// TODO(keir): Perhaps passing the jacobian back by value is not a good idea.
JMatrixType operator()(const Parameters &x) {
// Empirically determined constant.
Parameters eps = x.array().abs() * XScalar(1e-5);
// To handle cases where a paremeter is exactly zero, instead use the mean
// eps for the other dimensions.
XScalar mean_eps = eps.sum() / eps.rows();
if (mean_eps == XScalar(0)) {
// TODO(keir): Do something better here.
mean_eps = 1e-8; // ~sqrt(machine precision).
}
// TODO(keir): Elimininate this needless function evaluation for the
// central difference case.
FMatrixType fx = f_(x);
const int rows = fx.rows();
const int cols = x.rows();
JMatrixType jacobian(rows, cols);
Parameters x_plus_delta = x;
for (int c = 0; c < cols; ++c) {
if (eps(c) == XScalar(0)) {
eps(c) = mean_eps;
}
x_plus_delta(c) = x(c) + eps(c);
jacobian.col(c) = f_(x_plus_delta);
XScalar one_over_h = 1 / eps(c);
if (mode == CENTRAL) {
x_plus_delta(c) = x(c) - eps(c);
jacobian.col(c) -= f_(x_plus_delta);
one_over_h /= 2;
} else {
jacobian.col(c) -= fx;
}
x_plus_delta(c) = x(c);
jacobian.col(c) = jacobian.col(c) * one_over_h;
}
return jacobian;
}
private:
const Function &f_;
};
template<typename Function, typename Jacobian>
bool CheckJacobian(const Function &f, const typename Function::XMatrixType &x) {
Jacobian j_analytic(f);
NumericJacobian<Function> j_numeric(f);
typename NumericJacobian<Function>::JMatrixType J_numeric = j_numeric(x);
typename NumericJacobian<Function>::JMatrixType J_analytic = j_analytic(x);
LG << J_numeric - J_analytic;
return true;
}
} // namespace libmv
#endif // LIBMV_NUMERIC_DERIVATIVE_H

@ -0,0 +1,183 @@
// 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.
//
// A simple implementation of levenberg marquardt.
//
// [1] K. Madsen, H. Nielsen, O. Tingleoff. Methods for Non-linear Least
// Squares Problems.
// http://www2.imm.dtu.dk/pubdb/views/edoc_download.php/3215/pdf/imm3215.pdf
//
// TODO(keir): Cite the Lourakis' dogleg paper.
#ifndef LIBMV_NUMERIC_LEVENBERG_MARQUARDT_H
#define LIBMV_NUMERIC_LEVENBERG_MARQUARDT_H
#include <cmath>
#include "libmv/numeric/numeric.h"
#include "libmv/numeric/function_derivative.h"
#include "libmv/logging/logging.h"
namespace libmv {
template<typename Function,
typename Jacobian = NumericJacobian<Function>,
typename Solver = Eigen::PartialPivLU<
Matrix<typename Function::FMatrixType::RealScalar,
Function::XMatrixType::RowsAtCompileTime,
Function::XMatrixType::RowsAtCompileTime> > >
class LevenbergMarquardt {
public:
typedef typename Function::XMatrixType::RealScalar Scalar;
typedef typename Function::FMatrixType FVec;
typedef typename Function::XMatrixType Parameters;
typedef Matrix<typename Function::FMatrixType::RealScalar,
Function::FMatrixType::RowsAtCompileTime,
Function::XMatrixType::RowsAtCompileTime> JMatrixType;
typedef Matrix<typename JMatrixType::RealScalar,
JMatrixType::ColsAtCompileTime,
JMatrixType::ColsAtCompileTime> AMatrixType;
// TODO(keir): Some of these knobs can be derived from each other and
// removed, instead of requiring the user to set them.
enum Status {
RUNNING,
GRADIENT_TOO_SMALL, // eps > max(J'*f(x))
RELATIVE_STEP_SIZE_TOO_SMALL, // eps > ||dx|| / ||x||
ERROR_TOO_SMALL, // eps > ||f(x)||
HIT_MAX_ITERATIONS,
};
LevenbergMarquardt(const Function &f)
: f_(f), df_(f) {}
struct SolverParameters {
SolverParameters()
: gradient_threshold(1e-16),
relative_step_threshold(1e-16),
error_threshold(1e-16),
initial_scale_factor(1e-3),
max_iterations(100) {}
Scalar gradient_threshold; // eps > max(J'*f(x))
Scalar relative_step_threshold; // eps > ||dx|| / ||x||
Scalar error_threshold; // eps > ||f(x)||
Scalar initial_scale_factor; // Initial u for solving normal equations.
int max_iterations; // Maximum number of solver iterations.
};
struct Results {
Scalar error_magnitude; // ||f(x)||
Scalar gradient_magnitude; // ||J'f(x)||
int iterations;
Status status;
};
Status Update(const Parameters &x, const SolverParameters &params,
JMatrixType *J, AMatrixType *A, FVec *error, Parameters *g) {
*J = df_(x);
*A = (*J).transpose() * (*J);
*error = -f_(x);
*g = (*J).transpose() * *error;
if (g->array().abs().maxCoeff() < params.gradient_threshold) {
return GRADIENT_TOO_SMALL;
} else if (error->norm() < params.error_threshold) {
return ERROR_TOO_SMALL;
}
return RUNNING;
}
Results minimize(Parameters *x_and_min) {
SolverParameters params;
minimize(params, x_and_min);
}
Results minimize(const SolverParameters &params, Parameters *x_and_min) {
Parameters &x = *x_and_min;
JMatrixType J;
AMatrixType A;
FVec error;
Parameters g;
Results results;
results.status = Update(x, params, &J, &A, &error, &g);
Scalar u = Scalar(params.initial_scale_factor*A.diagonal().maxCoeff());
Scalar v = 2;
Parameters dx, x_new;
int i;
for (i = 0; results.status == RUNNING && i < params.max_iterations; ++i) {
VLOG(1) << "iteration: " << i;
VLOG(1) << "||f(x)||: " << f_(x).norm();
VLOG(1) << "max(g): " << g.array().abs().maxCoeff();
VLOG(1) << "u: " << u;
VLOG(1) << "v: " << v;
AMatrixType A_augmented = A + u*AMatrixType::Identity(J.cols(), J.cols());
Solver solver(A_augmented);
dx = solver.solve(g);
bool solved = (A_augmented * dx).isApprox(g);
if (!solved) {
LOG(ERROR) << "Failed to solve";
}
if (solved && dx.norm() <= params.relative_step_threshold * x.norm()) {
results.status = RELATIVE_STEP_SIZE_TOO_SMALL;
break;
}
if (solved) {
x_new = x + dx;
// Rho is the ratio of the actual reduction in error to the reduction
// in error that would be obtained if the problem was linear.
// See [1] for details.
Scalar rho((error.squaredNorm() - f_(x_new).squaredNorm())
/ dx.dot(u*dx + g));
if (rho > 0) {
// Accept the Gauss-Newton step because the linear model fits well.
x = x_new;
results.status = Update(x, params, &J, &A, &error, &g);
Scalar tmp = Scalar(2*rho-1);
u = u*std::max(1/3., 1 - (tmp*tmp*tmp));
v = 2;
continue;
}
}
// Reject the update because either the normal equations failed to solve
// or the local linear model was not good (rho < 0). Instead, increase u
// to move closer to gradient descent.
u *= v;
v *= 2;
}
if (results.status == RUNNING) {
results.status = HIT_MAX_ITERATIONS;
}
results.error_magnitude = error.norm();
results.gradient_magnitude = g.norm();
results.iterations = i;
return results;
}
private:
const Function &f_;
Jacobian df_;
};
} // namespace mv
#endif // LIBMV_NUMERIC_LEVENBERG_MARQUARDT_H

136
extern/libmv/libmv/numeric/numeric.cc vendored 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.
#include "libmv/numeric/numeric.h"
namespace libmv {
Mat3 RotationAroundX(double angle) {
double c, s;
sincos(angle, &s, &c);
Mat3 R;
R << 1, 0, 0,
0, c, -s,
0, s, c;
return R;
}
Mat3 RotationAroundY(double angle) {
double c, s;
sincos(angle, &s, &c);
Mat3 R;
R << c, 0, s,
0, 1, 0,
-s, 0, c;
return R;
}
Mat3 RotationAroundZ(double angle) {
double c, s;
sincos(angle, &s, &c);
Mat3 R;
R << c, -s, 0,
s, c, 0,
0, 0, 1;
return R;
}
Mat3 RotationRodrigues(const Vec3 &axis) {
double theta = axis.norm();
Vec3 w = axis / theta;
Mat3 W = CrossProductMatrix(w);
return Mat3::Identity() + sin(theta) * W + (1 - cos(theta)) * W * W;
}
Mat3 LookAt(Vec3 center) {
Vec3 zc = center.normalized();
Vec3 xc = Vec3::UnitY().cross(zc).normalized();
Vec3 yc = zc.cross(xc);
Mat3 R;
R.row(0) = xc;
R.row(1) = yc;
R.row(2) = zc;
return R;
}
Mat3 CrossProductMatrix(const Vec3 &x) {
Mat3 X;
X << 0, -x(2), x(1),
x(2), 0, -x(0),
-x(1), x(0), 0;
return X;
}
void MeanAndVarianceAlongRows(const Mat &A,
Vec *mean_pointer,
Vec *variance_pointer) {
Vec &mean = *mean_pointer;
Vec &variance = *variance_pointer;
int n = A.rows();
int m = A.cols();
mean.resize(n);
variance.resize(n);
for (int i = 0; i < n; ++i) {
mean(i) = 0;
variance(i) = 0;
for (int j = 0; j < m; ++j) {
double x = A(i, j);
mean(i) += x;
variance(i) += x * x;
}
}
mean /= m;
for (int i = 0; i < n; ++i) {
variance(i) = variance(i) / m - Square(mean(i));
}
}
void HorizontalStack(const Mat &left, const Mat &right, Mat *stacked) {
assert(left.rows() == left.rows());
int n = left.rows();
int m1 = left.cols();
int m2 = right.cols();
stacked->resize(n, m1 + m2);
stacked->block(0, 0, n, m1) = left;
stacked->block(0, m1, n, m2) = right;
}
void MatrixColumn(const Mat &A, int i, Vec2 *v) {
assert(A.rows() == 2);
*v << A(0,i), A(1,i);
}
void MatrixColumn(const Mat &A, int i, Vec3 *v) {
assert(A.rows() == 3);
*v << A(0,i), A(1,i), A(2,i);
}
void MatrixColumn(const Mat &A, int i, Vec4 *v) {
assert(A.rows() == 4);
*v << A(0,i), A(1,i), A(2,i), A(3,i);
}
} // namespace libmv

479
extern/libmv/libmv/numeric/numeric.h vendored Normal file

@ -0,0 +1,479 @@
// 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.
//
// Matrix and vector classes, based on Eigen2.
//
// Avoid using Eigen2 classes directly; instead typedef them here.
#ifndef LIBMV_NUMERIC_NUMERIC_H
#define LIBMV_NUMERIC_NUMERIC_H
#include <Eigen/Cholesky>
#include <Eigen/Core>
#include <Eigen/Eigenvalues>
#include <Eigen/Geometry>
#include <Eigen/LU>
#include <Eigen/QR>
#include <Eigen/SVD>
#if _WIN32 || __APPLE__
void static sincos (double x, double *sinx, double *cosx) {
*sinx = sin(x);
*cosx = cos(x);
}
#endif //_WIN32 || __APPLE__
#if (defined(WIN32) || defined(WIN64)) && !defined(__MINGW32__)
inline long lround(double d) {
return (long)(d>0 ? d+0.5 : ceil(d-0.5));
}
inline int round(double d) {
return (d>0) ? int(d+0.5) : int(d-0.5);
}
typedef unsigned int uint;
#endif //_WIN32
namespace libmv {
typedef Eigen::MatrixXd Mat;
typedef Eigen::VectorXd Vec;
typedef Eigen::MatrixXf Matf;
typedef Eigen::VectorXf Vecf;
typedef Eigen::Matrix<unsigned int, Eigen::Dynamic, Eigen::Dynamic> Matu;
typedef Eigen::Matrix<unsigned int, Eigen::Dynamic, 1> Vecu;
typedef Eigen::Matrix<unsigned int, 2, 1> Vec2u;
typedef Eigen::Matrix<double, 2, 2> Mat2;
typedef Eigen::Matrix<double, 2, 3> Mat23;
typedef Eigen::Matrix<double, 3, 3> Mat3;
typedef Eigen::Matrix<double, 3, 4> Mat34;
typedef Eigen::Matrix<double, 3, 5> Mat35;
typedef Eigen::Matrix<double, 4, 1> Mat41;
typedef Eigen::Matrix<double, 4, 3> Mat43;
typedef Eigen::Matrix<double, 4, 4> Mat4;
typedef Eigen::Matrix<double, 4, 6> Mat46;
typedef Eigen::Matrix<float, 2, 2> Mat2f;
typedef Eigen::Matrix<float, 2, 3> Mat23f;
typedef Eigen::Matrix<float, 3, 3> Mat3f;
typedef Eigen::Matrix<float, 3, 4> Mat34f;
typedef Eigen::Matrix<float, 3, 5> Mat35f;
typedef Eigen::Matrix<float, 4, 3> Mat43f;
typedef Eigen::Matrix<float, 4, 4> Mat4f;
typedef Eigen::Matrix<float, 4, 6> Mat46f;
typedef Eigen::Matrix<double, 3, 3, Eigen::RowMajor> RMat3;
typedef Eigen::Matrix<double, 4, 4, Eigen::RowMajor> RMat4;
typedef Eigen::Matrix<double, 2, Eigen::Dynamic> Mat2X;
typedef Eigen::Matrix<double, 3, Eigen::Dynamic> Mat3X;
typedef Eigen::Matrix<double, 4, Eigen::Dynamic> Mat4X;
typedef Eigen::Matrix<double, Eigen::Dynamic, 2> MatX2;
typedef Eigen::Matrix<double, Eigen::Dynamic, 3> MatX3;
typedef Eigen::Matrix<double, Eigen::Dynamic, 4> MatX4;
typedef Eigen::Matrix<double, Eigen::Dynamic, 5> MatX5;
typedef Eigen::Matrix<double, Eigen::Dynamic, 6> MatX6;
typedef Eigen::Matrix<double, Eigen::Dynamic, 7> MatX7;
typedef Eigen::Matrix<double, Eigen::Dynamic, 8> MatX8;
typedef Eigen::Matrix<double, Eigen::Dynamic, 9> MatX9;
typedef Eigen::Matrix<double, Eigen::Dynamic,15> MatX15;
typedef Eigen::Matrix<double, Eigen::Dynamic,16> MatX16;
typedef Eigen::Vector2d Vec2;
typedef Eigen::Vector3d Vec3;
typedef Eigen::Vector4d Vec4;
typedef Eigen::Matrix<double, 5, 1> Vec5;
typedef Eigen::Matrix<double, 6, 1> Vec6;
typedef Eigen::Matrix<double, 7, 1> Vec7;
typedef Eigen::Matrix<double, 8, 1> Vec8;
typedef Eigen::Matrix<double, 9, 1> Vec9;
typedef Eigen::Matrix<double, 10, 1> Vec10;
typedef Eigen::Matrix<double, 11, 1> Vec11;
typedef Eigen::Matrix<double, 12, 1> Vec12;
typedef Eigen::Matrix<double, 13, 1> Vec13;
typedef Eigen::Matrix<double, 14, 1> Vec14;
typedef Eigen::Matrix<double, 15, 1> Vec15;
typedef Eigen::Matrix<double, 16, 1> Vec16;
typedef Eigen::Matrix<double, 17, 1> Vec17;
typedef Eigen::Matrix<double, 18, 1> Vec18;
typedef Eigen::Matrix<double, 19, 1> Vec19;
typedef Eigen::Matrix<double, 20, 1> Vec20;
typedef Eigen::Vector2f Vec2f;
typedef Eigen::Vector3f Vec3f;
typedef Eigen::Vector4f Vec4f;
typedef Eigen::VectorXi VecXi;
typedef Eigen::Vector2i Vec2i;
typedef Eigen::Vector3i Vec3i;
typedef Eigen::Vector4i Vec4i;
typedef Eigen::Matrix<float,
Eigen::Dynamic,
Eigen::Dynamic,
Eigen::RowMajor> RMatf;
typedef Eigen::NumTraits<double> EigenDouble;
using Eigen::Map;
using Eigen::Dynamic;
using Eigen::Matrix;
// Find U, s, and VT such that
//
// A = U * diag(s) * VT
//
template <typename TMat, typename TVec>
inline void SVD(TMat *A, Vec *s, Mat *U, Mat *VT) {
assert(0);
}
// Solve the linear system Ax = 0 via SVD. Store the solution in x, such that
// ||x|| = 1.0. Return the singluar value corresponding to the solution.
// Destroys A and resizes x if necessary.
// TODO(maclean): Take the SVD of the transpose instead of this zero padding.
template <typename TMat, typename TVec>
double Nullspace(TMat *A, TVec *nullspace) {
Eigen::JacobiSVD<TMat> svd(*A, Eigen::ComputeFullV);
(*nullspace) = svd.matrixV().col(A->cols()-1);
if (A->rows() >= A->cols())
return svd.singularValues()(A->cols()-1);
else
return 0.0;
}
// Solve the linear system Ax = 0 via SVD. Finds two solutions, x1 and x2, such
// that x1 is the best solution and x2 is the next best solution (in the L2
// norm sense). Store the solution in x1 and x2, such that ||x|| = 1.0. Return
// the singluar value corresponding to the solution x1. Destroys A and resizes
// x if necessary.
template <typename TMat, typename TVec1, typename TVec2>
double Nullspace2(TMat *A, TVec1 *x1, TVec2 *x2) {
Eigen::JacobiSVD<TMat> svd(*A, Eigen::ComputeFullV);
*x1 = svd.matrixV().col(A->cols() - 1);
*x2 = svd.matrixV().col(A->cols() - 2);
if (A->rows() >= A->cols())
return svd.singularValues()(A->cols()-1);
else
return 0.0;
}
// In place transpose for square matrices.
template<class TA>
inline void TransposeInPlace(TA *A) {
*A = A->transpose().eval();
}
template<typename TVec>
inline double NormL1(const TVec &x) {
return x.array().abs().sum();
}
template<typename TVec>
inline double NormL2(const TVec &x) {
return x.norm();
}
template<typename TVec>
inline double NormLInfinity(const TVec &x) {
return x.array().abs().maxCoeff();
}
template<typename TVec>
inline double DistanceL1(const TVec &x, const TVec &y) {
return (x - y).array().abs().sum();
}
template<typename TVec>
inline double DistanceL2(const TVec &x, const TVec &y) {
return (x - y).norm();
}
template<typename TVec>
inline double DistanceLInfinity(const TVec &x, const TVec &y) {
return (x - y).array().abs().maxCoeff();
}
// Normalize a vector with the L1 norm, and return the norm before it was
// normalized.
template<typename TVec>
inline double NormalizeL1(TVec *x) {
double norm = NormL1(*x);
*x /= norm;
return norm;
}
// Normalize a vector with the L2 norm, and return the norm before it was
// normalized.
template<typename TVec>
inline double NormalizeL2(TVec *x) {
double norm = NormL2(*x);
*x /= norm;
return norm;
}
// Normalize a vector with the L^Infinity norm, and return the norm before it
// was normalized.
template<typename TVec>
inline double NormalizeLInfinity(TVec *x) {
double norm = NormLInfinity(*x);
*x /= norm;
return norm;
}
// Return the square of a number.
template<typename T>
inline T Square(T x) {
return x * x;
}
Mat3 RotationAroundX(double angle);
Mat3 RotationAroundY(double angle);
Mat3 RotationAroundZ(double angle);
// Returns the rotation matrix of a rotation of angle |axis| around axis.
// This is computed using the Rodrigues formula, see:
// http://mathworld.wolfram.com/RodriguesRotationFormula.html
Mat3 RotationRodrigues(const Vec3 &axis);
// Make a rotation matrix such that center becomes the direction of the
// positive z-axis, and y is oriented close to up.
Mat3 LookAt(Vec3 center);
// Return a diagonal matrix from a vector containg the diagonal values.
template <typename TVec>
inline Mat Diag(const TVec &x) {
return x.asDiagonal();
}
template<typename TMat>
inline double FrobeniusNorm(const TMat &A) {
return sqrt(A.array().abs2().sum());
}
template<typename TMat>
inline double FrobeniusDistance(const TMat &A, const TMat &B) {
return FrobeniusNorm(A - B);
}
inline Vec3 CrossProduct(const Vec3 &x, const Vec3 &y) {
return x.cross(y);
}
Mat3 CrossProductMatrix(const Vec3 &x);
void MeanAndVarianceAlongRows(const Mat &A,
Vec *mean_pointer,
Vec *variance_pointer);
#if _WIN32
// TODO(bomboze): un-#if this for both platforms once tested under Windows
/* This solution was extensively discussed here http://forum.kde.org/viewtopic.php?f=74&t=61940 */
#define SUM_OR_DYNAMIC(x,y) (x==Eigen::Dynamic||y==Eigen::Dynamic)?Eigen::Dynamic:(x+y)
template<typename Derived1, typename Derived2>
struct hstack_return {
typedef typename Derived1::Scalar Scalar;
enum {
RowsAtCompileTime = Derived1::RowsAtCompileTime,
ColsAtCompileTime = SUM_OR_DYNAMIC(Derived1::ColsAtCompileTime, Derived2::ColsAtCompileTime),
Options = Derived1::Flags&Eigen::RowMajorBit ? Eigen::RowMajor : 0,
MaxRowsAtCompileTime = Derived1::MaxRowsAtCompileTime,
MaxColsAtCompileTime = SUM_OR_DYNAMIC(Derived1::MaxColsAtCompileTime, Derived2::MaxColsAtCompileTime)
};
typedef Eigen::Matrix<Scalar,
RowsAtCompileTime,
ColsAtCompileTime,
Options,
MaxRowsAtCompileTime,
MaxColsAtCompileTime> type;
};
template<typename Derived1, typename Derived2>
typename hstack_return<Derived1,Derived2>::type
HStack (const Eigen::MatrixBase<Derived1>& lhs, const Eigen::MatrixBase<Derived2>& rhs) {
typename hstack_return<Derived1,Derived2>::type res;
res.resize(lhs.rows(), lhs.cols()+rhs.cols());
res << lhs, rhs;
return res;
};
template<typename Derived1, typename Derived2>
struct vstack_return {
typedef typename Derived1::Scalar Scalar;
enum {
RowsAtCompileTime = SUM_OR_DYNAMIC(Derived1::RowsAtCompileTime, Derived2::RowsAtCompileTime),
ColsAtCompileTime = Derived1::ColsAtCompileTime,
Options = Derived1::Flags&Eigen::RowMajorBit ? Eigen::RowMajor : 0,
MaxRowsAtCompileTime = SUM_OR_DYNAMIC(Derived1::MaxRowsAtCompileTime, Derived2::MaxRowsAtCompileTime),
MaxColsAtCompileTime = Derived1::MaxColsAtCompileTime
};
typedef Eigen::Matrix<Scalar,
RowsAtCompileTime,
ColsAtCompileTime,
Options,
MaxRowsAtCompileTime,
MaxColsAtCompileTime> type;
};
template<typename Derived1, typename Derived2>
typename vstack_return<Derived1,Derived2>::type
VStack (const Eigen::MatrixBase<Derived1>& lhs, const Eigen::MatrixBase<Derived2>& rhs) {
typename vstack_return<Derived1,Derived2>::type res;
res.resize(lhs.rows()+rhs.rows(), lhs.cols());
res << lhs, rhs;
return res;
};
#else //_WIN32
// Since it is not possible to typedef privately here, use a macro.
// Always take dynamic columns if either side is dynamic.
#define COLS \
((ColsLeft == Eigen::Dynamic || ColsRight == Eigen::Dynamic) \
? Eigen::Dynamic : (ColsLeft + ColsRight))
// Same as above, except that prefer fixed size if either is fixed.
#define ROWS \
((RowsLeft == Eigen::Dynamic && RowsRight == Eigen::Dynamic) \
? Eigen::Dynamic \
: ((RowsLeft == Eigen::Dynamic) \
? RowsRight \
: RowsLeft \
) \
)
// TODO(keir): Add a static assert if both rows are at compiletime.
template<typename T, int RowsLeft, int RowsRight, int ColsLeft, int ColsRight>
Eigen::Matrix<T, ROWS, COLS>
HStack(const Eigen::Matrix<T, RowsLeft, ColsLeft> &left,
const Eigen::Matrix<T, RowsRight, ColsRight> &right) {
assert(left.rows() == right.rows());
int n = left.rows();
int m1 = left.cols();
int m2 = right.cols();
Eigen::Matrix<T, ROWS, COLS> stacked(n, m1 + m2);
stacked.block(0, 0, n, m1) = left;
stacked.block(0, m1, n, m2) = right;
return stacked;
}
// Reuse the above macros by swapping the order of Rows and Cols. Nasty, but
// the duplication is worse.
// TODO(keir): Add a static assert if both rows are at compiletime.
// TODO(keir): Mail eigen list about making this work for general expressions
// rather than only matrix types.
template<typename T, int RowsLeft, int RowsRight, int ColsLeft, int ColsRight>
Eigen::Matrix<T, COLS, ROWS>
VStack(const Eigen::Matrix<T, ColsLeft, RowsLeft> &top,
const Eigen::Matrix<T, ColsRight, RowsRight> &bottom) {
assert(top.cols() == bottom.cols());
int n1 = top.rows();
int n2 = bottom.rows();
int m = top.cols();
Eigen::Matrix<T, COLS, ROWS> stacked(n1 + n2, m);
stacked.block(0, 0, n1, m) = top;
stacked.block(n1, 0, n2, m) = bottom;
return stacked;
}
#undef COLS
#undef ROWS
#endif //_WIN32
void HorizontalStack(const Mat &left, const Mat &right, Mat *stacked);
template<typename TTop, typename TBot, typename TStacked>
void VerticalStack(const TTop &top, const TBot &bottom, TStacked *stacked) {
assert(top.cols() == bottom.cols());
int n1 = top.rows();
int n2 = bottom.rows();
int m = top.cols();
stacked->resize(n1 + n2, m);
stacked->block(0, 0, n1, m) = top;
stacked->block(n1, 0, n2, m) = bottom;
}
void MatrixColumn(const Mat &A, int i, Vec2 *v);
void MatrixColumn(const Mat &A, int i, Vec3 *v);
void MatrixColumn(const Mat &A, int i, Vec4 *v);
template <typename TMat, typename TCols>
TMat ExtractColumns(const TMat &A, const TCols &columns) {
TMat compressed(A.rows(), columns.size());
for (int i = 0; i < columns.size(); ++i) {
compressed.col(i) = A.col(columns[i]);
}
return compressed;
}
template <typename TMat, typename TDest>
void reshape(const TMat &a, int rows, int cols, TDest *b) {
assert(a.rows()*a.cols() == rows*cols);
b->resize(rows, cols);
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
(*b)(i, j) = a[cols*i + j];
}
}
}
inline bool isnan(double i) {
#ifdef WIN32
return _isnan(i) > 0;
#else
return std::isnan(i);
#endif
}
/// Ceil function that has the same behaviour for positive
/// and negative values
template <typename FloatType>
FloatType ceil0(const FloatType& value) {
FloatType result = std::ceil( std::fabs( value ) );
return (value < 0.0) ? -result : result;
}
/// Returns the skew anti-symmetric matrix of a vector
inline Mat3 SkewMat(const Vec3 &x) {
Mat3 skew;
skew << 0 , -x(2), x(1),
x(2), 0 , -x(0),
-x(1), x(0), 0;
return skew;
}
/// Returns the skew anti-symmetric matrix of a vector with only
/// the first two (independent) lines
inline Mat23 SkewMatMinimal(const Vec2 &x) {
Mat23 skew;
skew << 0,-1, x(1),
1, 0, -x(0);
return skew;
}
} // namespace libmv
#endif // LIBMV_NUMERIC_NUMERIC_H

23
extern/libmv/libmv/numeric/poly.cc vendored Normal file

@ -0,0 +1,23 @@
// 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.
//
// Routines for solving polynomials.
// TODO(keir): Add a solver for degree > 3 polynomials.

123
extern/libmv/libmv/numeric/poly.h vendored Normal file

@ -0,0 +1,123 @@
// 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_NUMERIC_POLY_H_
#define LIBMV_NUMERIC_POLY_H_
#include <cmath>
#include <stdio.h>
namespace libmv {
// Solve the cubic polynomial
//
// x^3 + a*x^2 + b*x + c = 0
//
// The number of roots (from zero to three) is returned. If the number of roots
// is less than three, then higher numbered x's are not changed. For example,
// if there are 2 roots, only x0 and x1 are set.
//
// The GSL cubic solver was used as a reference for this routine.
template<typename Real>
int SolveCubicPolynomial(Real a, Real b, Real c,
Real *x0, Real *x1, Real *x2) {
Real q = a * a - 3 * b;
Real r = 2 * a * a * a - 9 * a * b + 27 * c;
Real Q = q / 9;
Real R = r / 54;
Real Q3 = Q * Q * Q;
Real R2 = R * R;
Real CR2 = 729 * r * r;
Real CQ3 = 2916 * q * q * q;
if (R == 0 && Q == 0) {
// Tripple root in one place.
*x0 = *x1 = *x2 = -a / 3 ;
return 3;
} else if (CR2 == CQ3) {
// This test is actually R2 == Q3, written in a form suitable for exact
// computation with integers.
//
// Due to finite precision some double roots may be missed, and considered
// to be a pair of complex roots z = x +/- epsilon i close to the real
// axis.
Real sqrtQ = sqrt (Q);
if (R > 0) {
*x0 = -2 * sqrtQ - a / 3;
*x1 = sqrtQ - a / 3;
*x2 = sqrtQ - a / 3;
} else {
*x0 = -sqrtQ - a / 3;
*x1 = -sqrtQ - a / 3;
*x2 = 2 * sqrtQ - a / 3;
}
return 3;
} else if (CR2 < CQ3) {
// This case is equivalent to R2 < Q3.
Real sqrtQ = sqrt (Q);
Real sqrtQ3 = sqrtQ * sqrtQ * sqrtQ;
Real theta = acos (R / sqrtQ3);
Real norm = -2 * sqrtQ;
*x0 = norm * cos (theta / 3) - a / 3;
*x1 = norm * cos ((theta + 2.0 * M_PI) / 3) - a / 3;
*x2 = norm * cos ((theta - 2.0 * M_PI) / 3) - a / 3;
// Put the roots in ascending order.
if (*x0 > *x1) {
std::swap(*x0, *x1);
}
if (*x1 > *x2) {
std::swap(*x1, *x2);
if (*x0 > *x1) {
std::swap(*x0, *x1);
}
}
return 3;
}
Real sgnR = (R >= 0 ? 1 : -1);
Real A = -sgnR * pow (fabs (R) + sqrt (R2 - Q3), 1.0/3.0);
Real B = Q / A ;
*x0 = A + B - a / 3;
return 1;
}
// The coefficients are in ascending powers, i.e. coeffs[N]*x^N.
template<typename Real>
int SolveCubicPolynomial(const Real *coeffs, Real *solutions) {
if (coeffs[0] == 0.0) {
// TODO(keir): This is a quadratic not a cubic. Implement a quadratic
// solver!
return 0;
}
Real a = coeffs[2] / coeffs[3];
Real b = coeffs[1] / coeffs[3];
Real c = coeffs[0] / coeffs[3];
return SolveCubicPolynomial(a, b, c,
solutions + 0,
solutions + 1,
solutions + 2);
}
} // namespace libmv
#endif // LIBMV_NUMERIC_POLY_H_

@ -0,0 +1,184 @@
// 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.
#define V3DLIB_ENABLE_SUITESPARSE 1
#include <map>
#include "libmv/base/vector.h"
#include "libmv/logging/logging.h"
#include "libmv/multiview/fundamental.h"
#include "libmv/multiview/projection.h"
#include "libmv/numeric/numeric.h"
#include "libmv/simple_pipeline/reconstruction.h"
#include "libmv/simple_pipeline/tracks.h"
#include "third_party/ssba/Geometry/v3d_cameramatrix.h"
#include "third_party/ssba/Geometry/v3d_metricbundle.h"
#include "third_party/ssba/Math/v3d_linear.h"
#include "third_party/ssba/Math/v3d_linear_utils.h"
namespace libmv {
void EuclideanBundle(const Tracks &tracks,
EuclideanReconstruction *reconstruction) {
vector<Marker> markers = tracks.AllMarkers();
// "index" in this context is the index that V3D's optimizer will see. The
// V3D index must be dense in that the cameras are numbered 0...n-1, which is
// not the case for the "image" numbering that arises from the tracks
// structure. The complicated mapping is necessary to convert between the two
// representations.
std::map<EuclideanCamera *, int> camera_to_index;
std::map<EuclideanPoint *, int> point_to_index;
vector<EuclideanCamera *> index_to_camera;
vector<EuclideanPoint *> index_to_point;
int num_cameras = 0;
int num_points = 0;
for (int i = 0; i < markers.size(); ++i) {
const Marker &marker = markers[i];
EuclideanCamera *camera = reconstruction->CameraForImage(marker.image);
EuclideanPoint *point = reconstruction->PointForTrack(marker.track);
if (camera && point) {
if (camera_to_index.find(camera) == camera_to_index.end()) {
camera_to_index[camera] = num_cameras;
index_to_camera.push_back(camera);
num_cameras++;
}
if (point_to_index.find(point) == point_to_index.end()) {
point_to_index[point] = num_points;
index_to_point.push_back(point);
num_points++;
}
}
}
// Make a V3D identity matrix, needed in a few places for K, since this
// assumes a calibrated setup.
V3D::Matrix3x3d identity3x3;
identity3x3[0][0] = 1.0;
identity3x3[0][1] = 0.0;
identity3x3[0][2] = 0.0;
identity3x3[1][0] = 0.0;
identity3x3[1][1] = 1.0;
identity3x3[1][2] = 0.0;
identity3x3[2][0] = 0.0;
identity3x3[2][1] = 0.0;
identity3x3[2][2] = 1.0;
// Convert libmv's cameras to V3D's cameras.
std::vector<V3D::CameraMatrix> v3d_cameras(index_to_camera.size());
for (int k = 0; k < index_to_camera.size(); ++k) {
V3D::Matrix3x3d R;
V3D::Vector3d t;
// Libmv's rotation matrix type.
const Mat3 &R_libmv = index_to_camera[k]->R;
const Vec3 &t_libmv = index_to_camera[k]->t;
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 3; ++j) {
R[i][j] = R_libmv(i, j);
}
t[i] = t_libmv(i);
}
v3d_cameras[k].setIntrinsic(identity3x3);
v3d_cameras[k].setRotation(R);
v3d_cameras[k].setTranslation(t);
}
LG << "Number of cameras: " << index_to_camera.size();
// Convert libmv's points to V3D's points.
std::vector<V3D::Vector3d> v3d_points(index_to_point.size());
for (int i = 0; i < index_to_point.size(); i++) {
v3d_points[i][0] = index_to_point[i]->X(0);
v3d_points[i][1] = index_to_point[i]->X(1);
v3d_points[i][2] = index_to_point[i]->X(2);
}
LG << "Number of points: " << index_to_point.size();
// Convert libmv's measurements to v3d measurements.
int num_residuals = 0;
std::vector<V3D::Vector2d> v3d_measurements;
std::vector<int> v3d_camera_for_measurement;
std::vector<int> v3d_point_for_measurement;
for (int i = 0; i < markers.size(); ++i) {
EuclideanCamera *camera = reconstruction->CameraForImage(markers[i].image);
EuclideanPoint *point = reconstruction->PointForTrack(markers[i].track);
if (!camera || !point) {
continue;
}
V3D::Vector2d v3d_point;
v3d_point[0] = markers[i].x;
v3d_point[1] = markers[i].y;
v3d_measurements.push_back(v3d_point);
v3d_camera_for_measurement.push_back(camera_to_index[camera]);
v3d_point_for_measurement.push_back(point_to_index[point]);
num_residuals++;
}
LG << "Number of residuals: " << num_residuals;
// This is calibrated reconstruction, so use zero distortion.
V3D::StdDistortionFunction v3d_distortion;
v3d_distortion.k1 = 0;
v3d_distortion.k2 = 0;
v3d_distortion.p1 = 0;
v3d_distortion.p2 = 0;
// Finally, run the bundle adjustment.
double const inlierThreshold = 500000.0;
V3D::CommonInternalsMetricBundleOptimizer opt(V3D::FULL_BUNDLE_METRIC,
inlierThreshold,
identity3x3,
v3d_distortion,
v3d_cameras,
v3d_points,
v3d_measurements,
v3d_camera_for_measurement,
v3d_point_for_measurement);
opt.maxIterations = 50;
opt.minimize();
LG << "Bundle status: " << opt.status;
// Convert V3D's cameras back to libmv's cameras.
for (int k = 0; k < num_cameras; k++) {
V3D::Matrix3x4d const Rt = v3d_cameras[k].getOrientation();
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 3; ++j) {
index_to_camera[k]->R(i, j) = Rt[i][j];
}
index_to_camera[k]->t(i) = Rt[i][3];
}
}
// Convert V3D's points back to libmv's points.
for (int k = 0; k < num_points; k++) {
for (int i = 0; i < 3; ++i) {
index_to_point[k]->X(i) = v3d_points[k][i];
}
}
}
void ProjectiveBundle(const Tracks & /*tracks*/,
ProjectiveReconstruction * /*reconstruction*/) {
// TODO(keir): Implement this! This can't work until we have a better bundler
// than SSBA, since SSBA has no support for projective bundling.
}
} // namespace libmv

@ -0,0 +1,72 @@
// 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_SIMPLE_PIPELINE_BUNDLE_H
#define LIBMV_SIMPLE_PIPELINE_BUNDLE_H
namespace libmv {
class EuclideanReconstruction;
class ProjectiveReconstruction;
class Tracks;
/*!
Refine camera poses and 3D coordinates using bundle adjustment.
This routine adjusts all cameras and points in \a *reconstruction. This
assumes a full observation for reconstructed tracks; this implies that if
there is a reconstructed 3D point (a bundle) for a track, then all markers
for that track will be included in the minimization. \a tracks should
contain markers used in the initial reconstruction.
The cameras and bundles (3D points) are refined in-place.
\note This assumes an outlier-free set of markers.
\note This assumes a calibrated reconstruction, e.g. the markers are
already corrected for camera intrinsics and radial distortion.
\sa EuclideanResect, EuclideanIntersect, EuclideanReconstructTwoFrames
*/
void EuclideanBundle(const Tracks &tracks,
EuclideanReconstruction *reconstruction);
/*!
Refine camera poses and 3D coordinates using bundle adjustment.
This routine adjusts all cameras and points in \a *reconstruction. This
assumes a full observation for reconstructed tracks; this implies that if
there is a reconstructed 3D point (a bundle) for a track, then all markers
for that track will be included in the minimization. \a tracks should
contain markers used in the initial reconstruction.
The cameras and bundles (homogeneous 3D points) are refined in-place.
\note This assumes an outlier-free set of markers.
\note This assumes that radial distortion is already corrected for, but
does not assume that that other intrinsics are.
\sa ProjectiveResect, ProjectiveIntersect, ProjectiveReconstructTwoFrames
*/
void ProjectiveBundle(const Tracks &tracks,
ProjectiveReconstruction *reconstruction);
} // namespace libmv
#endif // LIBMV_SIMPLE_PIPELINE_BUNDLE_H

@ -0,0 +1,351 @@
// 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/simple_pipeline/camera_intrinsics.h"
#include "libmv/numeric/levenberg_marquardt.h"
namespace libmv {
struct Offset {
signed char ix, iy;
unsigned char fx,fy;
};
struct Grid {
struct Offset *offset;
int width, height;
double overscan;
};
static struct Grid *copyGrid(struct Grid *from)
{
struct Grid *to = NULL;
if (from) {
to = new Grid;
to->width = from->width;
to->height = from->height;
to->overscan = from->overscan;
to->offset = new Offset[to->width*to->height];
memcpy(to->offset, from->offset, sizeof(struct Offset)*to->width*to->height);
}
return to;
}
CameraIntrinsics::CameraIntrinsics()
: K_(Mat3::Identity()),
image_width_(0),
image_height_(0),
k1_(0),
k2_(0),
k3_(0),
p1_(0),
p2_(0),
distort_(0),
undistort_(0) {}
CameraIntrinsics::CameraIntrinsics(const CameraIntrinsics &from)
: K_(from.K_),
image_width_(from.image_width_),
image_height_(from.image_height_),
k1_(from.k1_),
k2_(from.k2_),
k3_(from.k3_),
p1_(from.p1_),
p2_(from.p2_)
{
distort_ = copyGrid(from.distort_);
undistort_ = copyGrid(from.undistort_);
}
CameraIntrinsics::~CameraIntrinsics() {
FreeLookupGrid();
}
/// Set the entire calibration matrix at once.
void CameraIntrinsics::SetK(const Mat3 new_k) {
K_ = new_k;
FreeLookupGrid();
}
/// Set both x and y focal length in pixels.
void CameraIntrinsics::SetFocalLength(double focal_x, double focal_y) {
K_(0, 0) = focal_x;
K_(1, 1) = focal_y;
FreeLookupGrid();
}
void CameraIntrinsics::SetPrincipalPoint(double cx, double cy) {
K_(0, 2) = cx;
K_(1, 2) = cy;
FreeLookupGrid();
}
void CameraIntrinsics::SetImageSize(int width, int height) {
image_width_ = width;
image_height_ = height;
FreeLookupGrid();
}
void CameraIntrinsics::SetRadialDistortion(double k1, double k2, double k3) {
k1_ = k1;
k2_ = k2;
k3_ = k3;
FreeLookupGrid();
}
void CameraIntrinsics::SetTangentialDistortion(double p1, double p2) {
p1_ = p1;
p2_ = p2;
FreeLookupGrid();
}
void CameraIntrinsics::ApplyIntrinsics(double normalized_x,
double normalized_y,
double *image_x,
double *image_y) const {
double x = normalized_x;
double y = normalized_y;
// Apply distortion to the normalized points to get (xd, yd).
double r2 = x*x + y*y;
double r4 = r2 * r2;
double r6 = r4 * r2;
double r_coeff = (1 + k1_*r2 + k2_*r4 + k3_*r6);
double xd = x * r_coeff + 2*p1_*x*y + p2_*(r2 + 2*x*x);
double yd = y * r_coeff + 2*p2_*x*y + p1_*(r2 + 2*y*y);
// Apply focal length and principal point to get the final image coordinates.
*image_x = focal_length_x() * xd + principal_point_x();
*image_y = focal_length_y() * yd + principal_point_y();
}
struct InvertIntrinsicsCostFunction {
public:
typedef Vec2 FMatrixType;
typedef Vec2 XMatrixType;
InvertIntrinsicsCostFunction(const CameraIntrinsics &intrinsics,
double image_x, double image_y)
: intrinsics(intrinsics), x(image_x), y(image_y) {}
Vec2 operator()(const Vec2 &u) const {
double xx, yy;
intrinsics.ApplyIntrinsics(u(0), u(1), &xx, &yy);
Vec2 fx;
fx << (xx - x), (yy - y);
return fx;
}
const CameraIntrinsics &intrinsics;
double x, y;
};
void CameraIntrinsics::InvertIntrinsics(double image_x,
double image_y,
double *normalized_x,
double *normalized_y) const {
// Compute the initial guess. For a camera with no distortion, this will also
// be the final answer; the LM iteration will terminate immediately.
Vec2 normalized;
normalized(0) = (image_x - principal_point_x()) / focal_length_x();
normalized(1) = (image_y - principal_point_y()) / focal_length_y();
typedef LevenbergMarquardt<InvertIntrinsicsCostFunction> Solver;
InvertIntrinsicsCostFunction intrinsics_cost(*this, image_x, image_y);
Solver::SolverParameters params;
Solver solver(intrinsics_cost);
/*Solver::Results results =*/ solver.minimize(params, &normalized);
// TODO(keir): Better error handling.
*normalized_x = normalized(0);
*normalized_y = normalized(1);
}
// TODO(MatthiasF): downsample lookup
template<typename WarpFunction>
void CameraIntrinsics::ComputeLookupGrid(Grid* grid, int width, int height, double overscan) {
double w = (double)width / (1 + overscan);
double h = (double)height / (1 + overscan);
double aspx = (double)w / image_width_;
double aspy = (double)h / image_height_;
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
double src_x = (x - 0.5 * overscan * w) / aspx, src_y = (y - 0.5 * overscan * h) / aspy;
double warp_x, warp_y;
WarpFunction(this,src_x,src_y,&warp_x,&warp_y);
warp_x = warp_x*aspx + 0.5 * overscan * w;
warp_y = warp_y*aspy + 0.5 * overscan * h;
int ix = int(warp_x), iy = int(warp_y);
int fx = round((warp_x-ix)*256), fy = round((warp_y-iy)*256);
if(fx == 256) { fx=0; ix++; }
if(fy == 256) { fy=0; iy++; }
// Use nearest border pixel
if( ix < 0 ) { ix = 0, fx = 0; }
if( iy < 0 ) { iy = 0, fy = 0; }
if( ix >= width-2 ) ix = width-2;
if( iy >= height-2 ) iy = height-2;
if ( ix-x > -128 && ix-x < 128 && iy-y > -128 && iy-y < 128 ) {
Offset offset = { ix-x, iy-y, fx, fy };
grid->offset[y*width+x] = offset;
} else {
Offset offset = { 0, 0, 0, 0 };
grid->offset[y*width+x] = offset;
}
}
}
}
// TODO(MatthiasF): cubic B-Spline image sampling, bilinear lookup
template<typename T,int N>
static void Warp(const Grid* grid, const T* src, T* dst,
int width, int height) {
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
Offset offset = grid->offset[y*width+x];
const T* s = &src[((y+offset.iy)*width+(x+offset.ix))*N];
for (int i = 0; i < N; i++) {
dst[(y*width+x)*N+i] = ((s[ i] * (256-offset.fx) + s[ N+i] * offset.fx) * (256-offset.fy)
+(s[width*N+i] * (256-offset.fx) + s[width*N+N+i] * offset.fx) * offset.fy) / (256*256);
}
}
}
}
void CameraIntrinsics::FreeLookupGrid() {
if(distort_) {
delete distort_->offset;
delete distort_;
distort_ = NULL;
}
if(undistort_) {
delete undistort_->offset;
delete undistort_;
undistort_ = NULL;
}
}
// FIXME: C++ templates limitations makes thing complicated, but maybe there is a simpler method.
struct ApplyIntrinsicsFunction {
ApplyIntrinsicsFunction(CameraIntrinsics* intrinsics, double x, double y,
double *warp_x, double *warp_y) {
intrinsics->ApplyIntrinsics(
(x-intrinsics->principal_point_x())/intrinsics->focal_length_x(),
(y-intrinsics->principal_point_y())/intrinsics->focal_length_y(),
warp_x, warp_y);
}
};
struct InvertIntrinsicsFunction {
InvertIntrinsicsFunction(CameraIntrinsics* intrinsics, double x, double y,
double *warp_x, double *warp_y) {
intrinsics->InvertIntrinsics(x,y,warp_x,warp_y);
*warp_x = *warp_x*intrinsics->focal_length_x()+intrinsics->principal_point_x();
*warp_y = *warp_y*intrinsics->focal_length_y()+intrinsics->principal_point_y();
}
};
void CameraIntrinsics::CheckDistortLookupGrid(int width, int height, double overscan)
{
if(distort_) {
if(distort_->width != width || distort_->height != height || distort_->overscan != overscan) {
delete [] distort_->offset;
distort_->offset = NULL;
}
} else {
distort_ = new Grid;
distort_->offset = NULL;
}
if(!distort_->offset) {
distort_->offset = new Offset[width*height];
ComputeLookupGrid<InvertIntrinsicsFunction>(distort_,width,height,overscan);
}
distort_->width = width;
distort_->height = height;
distort_->overscan = overscan;
}
void CameraIntrinsics::CheckUndistortLookupGrid(int width, int height, double overscan)
{
if(undistort_) {
if(undistort_->width != width || undistort_->height != height || undistort_->overscan != overscan) {
delete [] undistort_->offset;
undistort_->offset = NULL;
}
} else {
undistort_ = new Grid;
undistort_->offset = NULL;
}
if(!undistort_->offset) {
undistort_->offset = new Offset[width*height];
ComputeLookupGrid<ApplyIntrinsicsFunction>(undistort_,width,height,overscan);
}
undistort_->width = width;
undistort_->height = height;
undistort_->overscan = overscan;
}
void CameraIntrinsics::Distort(const float* src, float* dst, int width, int height, double overscan, int channels) {
CheckDistortLookupGrid(width, height, overscan);
if(channels==1) Warp<float,1>(distort_,src,dst,width,height);
else if(channels==2) Warp<float,2>(distort_,src,dst,width,height);
else if(channels==3) Warp<float,3>(distort_,src,dst,width,height);
else if(channels==4) Warp<float,4>(distort_,src,dst,width,height);
//else assert("channels must be between 1 and 4");
}
void CameraIntrinsics::Distort(const unsigned char* src, unsigned char* dst, int width, int height, double overscan, int channels) {
CheckDistortLookupGrid(width, height, overscan);
if(channels==1) Warp<unsigned char,1>(distort_,src,dst,width,height);
else if(channels==2) Warp<unsigned char,2>(distort_,src,dst,width,height);
else if(channels==3) Warp<unsigned char,3>(distort_,src,dst,width,height);
else if(channels==4) Warp<unsigned char,4>(distort_,src,dst,width,height);
//else assert("channels must be between 1 and 4");
}
void CameraIntrinsics::Undistort(const float* src, float* dst, int width, int height, double overscan, int channels) {
CheckUndistortLookupGrid(width, height, overscan);
if(channels==1) Warp<float,1>(undistort_,src,dst,width,height);
else if(channels==2) Warp<float,2>(undistort_,src,dst,width,height);
else if(channels==3) Warp<float,3>(undistort_,src,dst,width,height);
else if(channels==4) Warp<float,4>(undistort_,src,dst,width,height);
//else assert("channels must be between 1 and 4");
}
void CameraIntrinsics::Undistort(const unsigned char* src, unsigned char* dst, int width, int height, double overscan, int channels) {
CheckUndistortLookupGrid(width, height, overscan);
if(channels==1) Warp<unsigned char,1>(undistort_,src,dst,width,height);
else if(channels==2) Warp<unsigned char,2>(undistort_,src,dst,width,height);
else if(channels==3) Warp<unsigned char,3>(undistort_,src,dst,width,height);
else if(channels==4) Warp<unsigned char,4>(undistort_,src,dst,width,height);
//else assert("channels must be between 1 and 4");
}
} // namespace libmv

@ -0,0 +1,152 @@
// 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_SIMPLE_PIPELINE_CAMERA_INTRINSICS_H_
#define LIBMV_SIMPLE_PIPELINE_CAMERA_INTRINSICS_H_
#include <Eigen/Core>
typedef Eigen::Matrix<double, 3, 3> Mat3;
namespace libmv {
struct Grid;
class CameraIntrinsics {
public:
CameraIntrinsics();
CameraIntrinsics(const CameraIntrinsics &from);
~CameraIntrinsics();
const Mat3 &K() const { return K_; }
// FIXME(MatthiasF): these should be CamelCase methods
double focal_length() const { return K_(0, 0); }
double focal_length_x() const { return K_(0, 0); }
double focal_length_y() const { return K_(1, 1); }
double principal_point_x() const { return K_(0, 2); }
double principal_point_y() const { return K_(1, 2); }
int image_width() const { return image_width_; }
int image_height() const { return image_height_; }
double k1() const { return k1_; }
double k2() const { return k2_; }
double k3() const { return k3_; }
double p1() const { return p1_; }
double p2() const { return p2_; }
/// Set the entire calibration matrix at once.
void SetK(const Mat3 new_k);
/// Set both x and y focal length in pixels.
void SetFocalLength(double focal_x, double focal_y);
void SetPrincipalPoint(double cx, double cy);
void SetImageSize(int width, int height);
void SetRadialDistortion(double k1, double k2, double k3 = 0);
void SetTangentialDistortion(double p1, double p2);
/*!
Apply camera intrinsics to the normalized point to get image coordinates.
This applies the lens distortion to a point which is in normalized
camera coordinates (i.e. the principal point is at (0, 0)) to get image
coordinates in pixels.
*/
void ApplyIntrinsics(double normalized_x, double normalized_y,
double *image_x, double *image_y) const;
/*!
Invert camera intrinsics on the image point to get normalized coordinates.
This reverses the effect of lens distortion on a point which is in image
coordinates to get normalized camera coordinates.
*/
void InvertIntrinsics(double image_x, double image_y,
double *normalized_x, double *normalized_y) const;
/*!
Distort an image using the current camera instrinsics
The distorted image is computed in \a dst using samples from \a src.
both buffers should be \a width x \a height x \a channels sized.
\note This is the reference implementation using floating point images.
*/
void Distort(const float* src, float* dst,
int width, int height, double overscan, int channels);
/*!
Distort an image using the current camera instrinsics
The distorted image is computed in \a dst using samples from \a src.
both buffers should be \a width x \a height x \a channels sized.
\note This version is much faster.
*/
void Distort(const unsigned char* src, unsigned char* dst,
int width, int height, double overscan, int channels);
/*!
Undistort an image using the current camera instrinsics
The undistorted image is computed in \a dst using samples from \a src.
both buffers should be \a width x \a height x \a channels sized.
\note This is the reference implementation using floating point images.
*/
void Undistort(const float* src, float* dst,
int width, int height, double overscan, int channels);
/*!
Undistort an image using the current camera instrinsics
The undistorted image is computed in \a dst using samples from \a src.
both buffers should be \a width x \a height x \a channels sized.
\note This version is much faster.
*/
void Undistort(const unsigned char* src, unsigned char* dst,
int width, int height, double overscan, int channels);
private:
template<typename WarpFunction> void ComputeLookupGrid(struct Grid* grid, int width, int height, double overscan);
void CheckUndistortLookupGrid(int width, int height, double overscan);
void CheckDistortLookupGrid(int width, int height, double overscan);
void FreeLookupGrid();
// The traditional intrinsics matrix from x = K[R|t]X.
Mat3 K_;
// This is the size of the image. This is necessary to, for example, handle
// the case of processing a scaled image.
int image_width_;
int image_height_;
// OpenCV's distortion model with third order polynomial radial distortion
// terms and second order tangential distortion. The distortion is applied to
// the normalized coordinates before the focal length, which makes them
// independent of image size.
double k1_, k2_, k3_, p1_, p2_;
struct Grid *distort_;
struct Grid *undistort_;
};
} // namespace libmv
#endif // LIBMV_SIMPLE_PIPELINE_CAMERA_INTRINSICS_H_

@ -0,0 +1,184 @@
/****************************************************************************
**
** 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/simple_pipeline/detect.h"
#include <third_party/fast/fast.h>
#include <stdlib.h>
#include <memory.h>
#ifdef __SSE2__
#include <emmintrin.h>
#endif
namespace libmv {
typedef unsigned int uint;
int featurecmp(const void *a_v, const void *b_v)
{
Feature *a = (Feature*)a_v;
Feature *b = (Feature*)b_v;
return b->score - a->score;
}
std::vector<Feature> DetectFAST(const unsigned char* data, int width, int height, int stride,
int min_trackness, int min_distance) {
std::vector<Feature> features;
// TODO(MatthiasF): Support targetting a feature count (binary search trackness)
int num_features;
xy* all = fast9_detect(data, width, height,
stride, min_trackness, &num_features);
if(num_features == 0) {
free(all);
return features;
}
int* scores = fast9_score(data, stride, all, num_features, min_trackness);
// TODO: merge with close feature suppression
xy* nonmax = nonmax_suppression(all, scores, num_features, &num_features);
free(all);
// Remove too close features
// TODO(MatthiasF): A resolution independent parameter would be better than distance
// e.g. a coefficient going from 0 (no minimal distance) to 1 (optimal circle packing)
// FIXME(MatthiasF): this method will not necessarily give all maximum markers
if(num_features) {
Feature *all_features = new Feature[num_features];
for(int i = 0; i < num_features; ++i) {
Feature a = { nonmax[i].x, nonmax[i].y, scores[i], 0 };
all_features[i] = a;
}
qsort((void *)all_features, num_features, sizeof(Feature), featurecmp);
features.reserve(num_features);
int prev_score = all_features[0].score;
for(int i = 0; i < num_features; ++i) {
bool ok = true;
Feature a = all_features[i];
if(a.score>prev_score)
abort();
prev_score = a.score;
// compare each feature against filtered set
for(int j = 0; j < features.size(); j++) {
Feature& b = features[j];
if ( (a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y) < min_distance*min_distance ) {
// already a nearby feature
ok = false;
break;
}
}
if(ok) {
// add the new feature
features.push_back(a);
}
}
delete [] all_features;
}
free(scores);
free(nonmax);
return features;
}
#ifdef __SSE2__
static uint SAD(const ubyte* imageA, const ubyte* imageB, int strideA, int strideB) {
__m128i a = _mm_setzero_si128();
for(int i = 0; i < 16; i++) {
a = _mm_adds_epu16(a, _mm_sad_epu8( _mm_loadu_si128((__m128i*)(imageA+i*strideA)),
_mm_loadu_si128((__m128i*)(imageB+i*strideB))));
}
return _mm_extract_epi16(a,0) + _mm_extract_epi16(a,4);
}
#else
static uint SAD(const ubyte* imageA, const ubyte* imageB, int strideA, int strideB) {
uint sad=0;
for(int i = 0; i < 16; i++) {
for(int j = 0; j < 16; j++) {
sad += abs((int)imageA[i*strideA+j] - imageB[i*strideB+j]);
}
}
return sad;
}
#endif
void DetectMORAVEC(ubyte* image, int stride, int width, int height, Feature* detected, int* count, int distance, ubyte* pattern) {
unsigned short histogram[256];
memset(histogram,0,sizeof(histogram));
ubyte* scores = new ubyte[width*height];
memset(scores,0,width*height);
const int r = 1; //radius for self similarity comparison
for(int y=distance; y<height-distance; y++) {
for(int x=distance; x<width-distance; x++) {
ubyte* s = &image[y*stride+x];
int score = // low self-similarity with overlapping patterns //OPTI: load pattern once
SAD(s, s-r*stride-r, stride, stride)+SAD(s, s-r*stride, stride, stride)+SAD(s, s-r*stride+r, stride, stride)+
SAD(s, s -r, stride, stride)+ SAD(s, s +r, stride, stride)+
SAD(s, s+r*stride-r, stride, stride)+SAD(s, s+r*stride, stride, stride)+SAD(s, s+r*stride+r, stride, stride);
score /= 256; // normalize
if(pattern) score -= SAD(s, pattern, stride, 16); // find only features similar to pattern
if(score<=16) continue; // filter very self-similar features
score -= 16; // translate to score/histogram values
if(score>255) score=255; // clip
ubyte* c = &scores[y*width+x];
for(int i=-distance; i<0; i++) {
for(int j=-distance; j<distance; j++) {
int s = c[i*width+j];
if(s == 0) continue;
if(s >= score) goto nonmax;
c[i*width+j]=0, histogram[s]--;
}
}
for(int i=0, j=-distance; j<0; j++) {
int s = c[i*width+j];
if(s == 0) continue;
if(s >= score) goto nonmax;
c[i*width+j]=0, histogram[s]--;
}
c[0] = score, histogram[score]++;
nonmax:;
}
}
int min=255, total=0;
for(; min>0; min--) {
int h = histogram[min];
if(total+h > *count) break;
total += h;
}
int i=0;
for(int y=16; y<height-16; y++) {
for(int x=16; x<width-16; x++) {
int s = scores[y*width+x];
Feature f = { x+8, y+8, s, 16 };
if(s>min) detected[i++] = f;
}
}
*count = i;
delete[] scores;
}
}

@ -0,0 +1,95 @@
/****************************************************************************
**
** 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_SIMPLE_PIPELINE_DETECT_H_
#define LIBMV_SIMPLE_PIPELINE_DETECT_H_
#include <vector>
namespace libmv {
typedef unsigned char ubyte;
/*!
A Feature is the 2D location of a detected feature in an image.
\a x, \a y is the position of the feature in pixels from the top left corner.
\a score is an estimate of how well the feature will be tracked.
\a size can be used as an initial pattern size to track the feature.
\sa Detect
*/
struct Feature {
/// Position in pixels (from top-left corner)
/// \note libmv might eventually support subpixel precision.
float x, y;
/// Trackness of the feature
float score;
/// Size of the feature in pixels
float size;
};
/*!
Detect features in an image.
You need to input a single channel 8-bit image using pointer to image \a data,
\a width, \a height and \a stride (i.e bytes per line).
You can tweak the count of detected features using \a min_trackness, which is
the minimum score to add a feature, and \a min_distance which is the minimal
distance accepted between two featuress.
\note You can binary search over \a min_trackness to get a given feature count.
\note a way to get an uniform distribution of a given feature count is:
\a min_distance = \a width * \a height / desired_feature_count ^ 2
\return All detected feartures matching given parameters
*/
std::vector<Feature> DetectFAST(const unsigned char* data, int width, int height,
int stride, int min_trackness = 128,
int min_distance = 120);
/*!
Detect features in an image.
\a image is a single channel 8-bit image of size \a width x \a height
\a detected is an array with space to hold \a *count features.
\a *count is the maximum count to detect on input and the actual
detected count on output.
\a distance is the minimal distance between detected features.
if \a pattern is null all good features will be found.
if \a pattern is not null only features similar to \a pattern will be found.
\note \a You can crop the image (to avoid detecting markers near the borders) without copying:
image += marginY*stride+marginX, width -= 2*marginX, height -= 2*marginY;
*/
void DetectMORAVEC(ubyte* image, int stride, int width, int height, Feature* detected, int* count, int distance /*=32*/, ubyte* pattern /*=0*/);
}
#endif

@ -0,0 +1,218 @@
// 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/base/vector.h"
#include "libmv/logging/logging.h"
#include "libmv/multiview/fundamental.h"
#include "libmv/multiview/projection.h"
#include "libmv/numeric/levenberg_marquardt.h"
#include "libmv/numeric/numeric.h"
#include "libmv/simple_pipeline/reconstruction.h"
#include "libmv/simple_pipeline/tracks.h"
namespace libmv {
namespace {
void CoordinatesForMarkersInImage(const vector<Marker> &markers,
int image,
Mat *coordinates) {
vector<Vec2> coords;
for (int i = 0; i < markers.size(); ++i) {
const Marker &marker = markers[i];
if (markers[i].image == image) {
coords.push_back(Vec2(marker.x, marker.y));
}
}
coordinates->resize(2, coords.size());
for (int i = 0; i < coords.size(); i++) {
coordinates->col(i) = coords[i];
}
}
void GetImagesInMarkers(const vector<Marker> &markers,
int *image1, int *image2) {
if (markers.size() < 2) {
return;
}
*image1 = markers[0].image;
for (int i = 1; i < markers.size(); ++i) {
if (markers[i].image != *image1) {
*image2 = markers[i].image;
return;
}
}
*image2 = -1;
LOG(FATAL) << "Only one image in the markers.";
}
} // namespace
bool EuclideanReconstructTwoFrames(const vector<Marker> &markers,
EuclideanReconstruction *reconstruction) {
if (markers.size() < 16) {
return false;
}
int image1, image2;
GetImagesInMarkers(markers, &image1, &image2);
Mat x1, x2;
CoordinatesForMarkersInImage(markers, image1, &x1);
CoordinatesForMarkersInImage(markers, image2, &x2);
Mat3 F;
NormalizedEightPointSolver(x1, x2, &F);
// The F matrix should be an E matrix, but squash it just to be sure.
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;
Mat3 E = svd.matrixU() * diag.asDiagonal() * svd.matrixV().transpose();
// Recover motion between the two images. Since this function assumes a
// calibrated camera, use the identity for K.
Mat3 R;
Vec3 t;
Mat3 K = Mat3::Identity();
if (!MotionFromEssentialAndCorrespondence(E,
K, x1.col(0),
K, x2.col(0),
&R, &t)) {
return false;
}
// Image 1 gets the reference frame, image 2 gets the relative motion.
reconstruction->InsertCamera(image1, Mat3::Identity(), Vec3::Zero());
reconstruction->InsertCamera(image2, R, t);
LG << "From two frame reconstruction got:\nR:\n" << R
<< "\nt:" << t.transpose();
return true;
}
namespace {
Mat3 DecodeF(const Vec9 &encoded_F) {
// Decode F and force it to be rank 2.
Map<const Mat3> full_rank_F(encoded_F.data(), 3, 3);
Eigen::JacobiSVD<Mat3> svd(full_rank_F, Eigen::ComputeFullU | Eigen::ComputeFullV);
Vec3 diagonal = svd.singularValues();
diagonal(2) = 0;
Mat3 F = svd.matrixU() * diagonal.asDiagonal() * svd.matrixV().transpose();
return F;
}
// This is the stupidest way to refine F known to mankind, since it requires
// doing a full SVD of F at each iteration. This uses sampson error.
struct FundamentalSampsonCostFunction {
public:
typedef Vec FMatrixType;
typedef Vec9 XMatrixType;
// Assumes markers are ordered by track.
FundamentalSampsonCostFunction(const vector<Marker> &markers)
: markers(markers) {}
Vec operator()(const Vec9 &encoded_F) const {
// Decode F and force it to be rank 2.
Mat3 F = DecodeF(encoded_F);
Vec residuals(markers.size() / 2);
residuals.setZero();
for (int i = 0; i < markers.size() / 2; ++i) {
const Marker &marker1 = markers[2*i + 0];
const Marker &marker2 = markers[2*i + 1];
CHECK_EQ(marker1.track, marker2.track);
Vec2 x1(marker1.x, marker1.y);
Vec2 x2(marker2.x, marker2.y);
residuals[i] = SampsonDistance(F, x1, x2);
}
return residuals;
}
const vector<Marker> &markers;
};
} // namespace
bool ProjectiveReconstructTwoFrames(const vector<Marker> &markers,
ProjectiveReconstruction *reconstruction) {
if (markers.size() < 16) {
return false;
}
int image1, image2;
GetImagesInMarkers(markers, &image1, &image2);
Mat x1, x2;
CoordinatesForMarkersInImage(markers, image1, &x1);
CoordinatesForMarkersInImage(markers, image2, &x2);
Mat3 F;
NormalizedEightPointSolver(x1, x2, &F);
// XXX Verify sampson distance.
#if 0
// Refine the resulting projection fundamental matrix using Sampson's
// approximation of geometric error. This avoids having to do a full bundle
// at the cost of some accuracy.
//
// TODO(keir): After switching to a better bundling library, use a proper
// full bundle adjust here instead of this lame bundle adjustment.
typedef LevenbergMarquardt<FundamentalSampsonCostFunction> Solver;
FundamentalSampsonCostFunction fundamental_cost(markers);
// Pack the initial P matrix into a size-12 vector..
Vec9 encoded_F = Map<Vec9>(F.data(), 3, 3);
Solver solver(fundamental_cost);
Solver::SolverParameters params;
Solver::Results results = solver.minimize(params, &encoded_F);
// TODO(keir): Check results to ensure clean termination.
// Recover F from the minimization.
F = DecodeF(encoded_F);
#endif
// Image 1 gets P = [I|0], image 2 gets arbitrary P.
Mat34 P1 = Mat34::Zero();
P1.block<3, 3>(0, 0) = Mat3::Identity();
Mat34 P2;
ProjectionsFromFundamental(F, &P1, &P2);
reconstruction->InsertCamera(image1, P1);
reconstruction->InsertCamera(image2, P2);
LG << "From two frame reconstruction got P2:\n" << P2;
return true;
}
} // namespace libmv

@ -0,0 +1,74 @@
// 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_SIMPLE_PIPELINE_INITIALIZE_RECONSTRUCTION_H
#define LIBMV_SIMPLE_PIPELINE_INITIALIZE_RECONSTRUCTION_H
#include "libmv/base/vector.h"
namespace libmv {
struct Marker;
class EuclideanReconstruction;
class ProjectiveReconstruction;
/*!
Initialize the \link EuclideanReconstruction reconstruction \endlink using
two frames.
\a markers should contain all \l Marker markers \endlink belonging to
tracks visible in both frames. The pose estimation of the camera for
these frames will be inserted into \a *reconstruction.
\note The two frames need to have both enough parallax and enough common tracks
for accurate reconstruction. At least 8 tracks are suggested.
\note The origin of the coordinate system is defined to be the camera of
the first keyframe.
\note This assumes a calibrated reconstruction, e.g. the markers are
already corrected for camera intrinsics and radial distortion.
\note This assumes an outlier-free set of markers.
\sa EuclideanResect, EuclideanIntersect, EuclideanBundle
*/
bool EuclideanReconstructTwoFrames(const vector<Marker> &markers,
EuclideanReconstruction *reconstruction);
/*!
Initialize the \link ProjectiveReconstruction reconstruction \endlink using
two frames.
\a markers should contain all \l Marker markers \endlink belonging to
tracks visible in both frames. An estimate of the projection matrices for
the two frames will get added to the reconstruction.
\note The two frames need to have both enough parallax and enough common tracks
for accurate reconstruction. At least 8 tracks are suggested.
\note The origin of the coordinate system is defined to be the camera of
the first keyframe.
\note This assumes the markers are already corrected for radial distortion.
\note This assumes an outlier-free set of markers.
\sa ProjectiveResect, ProjectiveIntersect, ProjectiveBundle
*/
bool ProjectiveReconstructTwoFrames(const vector<Marker> &markers,
ProjectiveReconstruction *reconstruction);
} // namespace libmv
#endif // LIBMV_SIMPLE_PIPELINE_INITIALIZE_RECONSTRUCTION_H

@ -0,0 +1,205 @@
// 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/base/vector.h"
#include "libmv/logging/logging.h"
#include "libmv/multiview/projection.h"
#include "libmv/multiview/triangulation.h"
#include "libmv/multiview/nviewtriangulation.h"
#include "libmv/multiview/projection.h"
#include "libmv/numeric/numeric.h"
#include "libmv/numeric/levenberg_marquardt.h"
#include "libmv/simple_pipeline/reconstruction.h"
#include "libmv/simple_pipeline/tracks.h"
namespace libmv {
namespace {
struct EuclideanIntersectCostFunction {
public:
typedef Vec FMatrixType;
typedef Vec3 XMatrixType;
EuclideanIntersectCostFunction(const vector<Marker> &markers,
const EuclideanReconstruction &reconstruction)
: markers(markers),
reconstruction(reconstruction) {}
Vec operator()(const Vec3 &X) const {
Vec residuals(2 * markers.size());
residuals.setZero();
for (int i = 0; i < markers.size(); ++i) {
const EuclideanCamera &camera =
*reconstruction.CameraForImage(markers[i].image);
Vec3 projected = camera.R * X + camera.t;
projected /= projected(2);
residuals[2*i + 0] = projected(0) - markers[i].x;
residuals[2*i + 1] = projected(1) - markers[i].y;
}
return residuals;
}
const vector<Marker> &markers;
const EuclideanReconstruction &reconstruction;
};
} // namespace
bool EuclideanIntersect(const vector<Marker> &markers,
EuclideanReconstruction *reconstruction) {
if (markers.size() < 2) {
return false;
}
// Compute projective camera matrices for the cameras the intersection is
// going to use.
Mat3 K = Mat3::Identity();
vector<Mat34> cameras;
Mat34 P;
for (int i = 0; i < markers.size(); ++i) {
EuclideanCamera *camera = reconstruction->CameraForImage(markers[i].image);
P_From_KRt(K, camera->R, camera->t, &P);
cameras.push_back(P);
}
// Stack the 2D coordinates together as required by NViewTriangulate.
Mat2X points(2, markers.size());
for (int i = 0; i < markers.size(); ++i) {
points(0, i) = markers[i].x;
points(1, i) = markers[i].y;
}
Vec4 Xp;
LG << "Intersecting with " << markers.size() << " markers.";
NViewTriangulateAlgebraic(points, cameras, &Xp);
// Get euclidean version of the homogeneous point.
Xp /= Xp(3);
Vec3 X = Xp.head<3>();
typedef LevenbergMarquardt<EuclideanIntersectCostFunction> Solver;
EuclideanIntersectCostFunction triangulate_cost(markers, *reconstruction);
Solver::SolverParameters params;
Solver solver(triangulate_cost);
Solver::Results results = solver.minimize(params, &X);
// Try projecting the point; make sure it's in front of everyone.
for (int i = 0; i < cameras.size(); ++i) {
const EuclideanCamera &camera =
*reconstruction->CameraForImage(markers[i].image);
Vec3 x = camera.R * X + camera.t;
if (x(2) < 0) {
LOG(ERROR) << "POINT BEHIND CAMERA " << markers[i].image
<< ": " << x.transpose();
}
}
Vec3 point = X.head<3>();
reconstruction->InsertPoint(markers[0].track, point);
// TODO(keir): Add proper error checking.
return true;
}
namespace {
struct ProjectiveIntersectCostFunction {
public:
typedef Vec FMatrixType;
typedef Vec4 XMatrixType;
ProjectiveIntersectCostFunction(
const vector<Marker> &markers,
const ProjectiveReconstruction &reconstruction)
: markers(markers), reconstruction(reconstruction) {}
Vec operator()(const Vec4 &X) const {
Vec residuals(2 * markers.size());
residuals.setZero();
for (int i = 0; i < markers.size(); ++i) {
const ProjectiveCamera &camera =
*reconstruction.CameraForImage(markers[i].image);
Vec3 projected = camera.P * X;
projected /= projected(2);
residuals[2*i + 0] = projected(0) - markers[i].x;
residuals[2*i + 1] = projected(1) - markers[i].y;
}
return residuals;
}
const vector<Marker> &markers;
const ProjectiveReconstruction &reconstruction;
};
} // namespace
bool ProjectiveIntersect(const vector<Marker> &markers,
ProjectiveReconstruction *reconstruction) {
if (markers.size() < 2) {
return false;
}
// Get the cameras to use for the intersection.
vector<Mat34> cameras;
for (int i = 0; i < markers.size(); ++i) {
ProjectiveCamera *camera = reconstruction->CameraForImage(markers[i].image);
cameras.push_back(camera->P);
}
// Stack the 2D coordinates together as required by NViewTriangulate.
Mat2X points(2, markers.size());
for (int i = 0; i < markers.size(); ++i) {
points(0, i) = markers[i].x;
points(1, i) = markers[i].y;
}
Vec4 X;
LG << "Intersecting with " << markers.size() << " markers.";
NViewTriangulateAlgebraic(points, cameras, &X);
X /= X(3);
typedef LevenbergMarquardt<ProjectiveIntersectCostFunction> Solver;
ProjectiveIntersectCostFunction triangulate_cost(markers, *reconstruction);
Solver::SolverParameters params;
Solver solver(triangulate_cost);
Solver::Results results = solver.minimize(params, &X);
(void) results; // TODO(keir): Ensure results are good.
// Try projecting the point; make sure it's in front of everyone.
for (int i = 0; i < cameras.size(); ++i) {
const ProjectiveCamera &camera =
*reconstruction->CameraForImage(markers[i].image);
Vec3 x = camera.P * X;
if (x(2) < 0) {
LOG(ERROR) << "POINT BEHIND CAMERA " << markers[i].image
<< ": " << x.transpose();
}
}
reconstruction->InsertPoint(markers[0].track, X);
// TODO(keir): Add proper error checking.
return true;
}
} // namespace libmv

@ -0,0 +1,77 @@
// 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_SIMPLE_PIPELINE_INTERSECT_H
#define LIBMV_SIMPLE_PIPELINE_INTERSECT_H
#include "libmv/base/vector.h"
#include "libmv/simple_pipeline/tracks.h"
#include "libmv/simple_pipeline/reconstruction.h"
namespace libmv {
/*!
Estimate the 3D coordinates of a track by intersecting rays from images.
This takes a set of markers, where each marker is for the same track but
different images, and reconstructs the 3D position of that track. Each of
the frames for which there is a marker for that track must have a
corresponding reconstructed camera in \a *reconstruction.
\a markers should contain all \l Marker markers \endlink belonging to
tracks visible in all frames.
\a reconstruction should contain the cameras for all frames.
The new \l Point points \endlink will be inserted in \a reconstruction.
\note This assumes a calibrated reconstruction, e.g. the markers are
already corrected for camera intrinsics and radial distortion.
\note This assumes an outlier-free set of markers.
\sa EuclideanResect
*/
bool EuclideanIntersect(const vector<Marker> &markers,
EuclideanReconstruction *reconstruction);
/*!
Estimate the homogeneous coordinates of a track by intersecting rays.
This takes a set of markers, where each marker is for the same track but
different images, and reconstructs the homogeneous 3D position of that
track. Each of the frames for which there is a marker for that track must
have a corresponding reconstructed camera in \a *reconstruction.
\a markers should contain all \l Marker markers \endlink belonging to
tracks visible in all frames.
\a reconstruction should contain the cameras for all frames.
The new \l Point points \endlink will be inserted in \a reconstruction.
\note This assumes that radial distortion is already corrected for, but
does not assume that e.g. focal length and principal point are
accounted for.
\note This assumes an outlier-free set of markers.
\sa Resect
*/
bool ProjectiveIntersect(const vector<Marker> &markers,
ProjectiveReconstruction *reconstruction);
} // namespace libmv
#endif // LIBMV_SIMPLE_PIPELINE_INTERSECT_H

@ -0,0 +1,317 @@
// 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 <cstdio>
#include "libmv/logging/logging.h"
#include "libmv/simple_pipeline/bundle.h"
#include "libmv/simple_pipeline/intersect.h"
#include "libmv/simple_pipeline/resect.h"
#include "libmv/simple_pipeline/reconstruction.h"
#include "libmv/simple_pipeline/tracks.h"
#include "libmv/simple_pipeline/camera_intrinsics.h"
#ifdef _MSC_VER
# define snprintf _snprintf
#endif
namespace libmv {
namespace {
// These are "strategy" classes which make it possible to use the same code for
// both projective and euclidean reconstruction.
// FIXME(MatthiasF): OOP would achieve the same goal while avoiding
// template bloat and making interface changes much easier.
struct EuclideanPipelineRoutines {
typedef EuclideanReconstruction Reconstruction;
typedef EuclideanCamera Camera;
typedef EuclideanPoint Point;
static void Bundle(const Tracks &tracks,
EuclideanReconstruction *reconstruction) {
EuclideanBundle(tracks, reconstruction);
}
static bool Resect(const vector<Marker> &markers,
EuclideanReconstruction *reconstruction, bool final_pass) {
return EuclideanResect(markers, reconstruction, final_pass);
}
static bool Intersect(const vector<Marker> &markers,
EuclideanReconstruction *reconstruction) {
return EuclideanIntersect(markers, reconstruction);
}
static Marker ProjectMarker(const EuclideanPoint &point,
const EuclideanCamera &camera,
const CameraIntrinsics &intrinsics) {
Vec3 projected = camera.R * point.X + camera.t;
projected /= projected(2);
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;
}
};
struct ProjectivePipelineRoutines {
typedef ProjectiveReconstruction Reconstruction;
typedef ProjectiveCamera Camera;
typedef ProjectivePoint Point;
static void Bundle(const Tracks &tracks,
ProjectiveReconstruction *reconstruction) {
ProjectiveBundle(tracks, reconstruction);
}
static bool Resect(const vector<Marker> &markers,
ProjectiveReconstruction *reconstruction, bool final_pass) {
return ProjectiveResect(markers, reconstruction);
}
static bool Intersect(const vector<Marker> &markers,
ProjectiveReconstruction *reconstruction) {
return ProjectiveIntersect(markers, reconstruction);
}
static Marker ProjectMarker(const ProjectivePoint &point,
const ProjectiveCamera &camera,
const CameraIntrinsics &intrinsics) {
Vec3 projected = camera.P * point.X;
projected /= projected(2);
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;
}
};
} // namespace
template<typename PipelineRoutines>
void InternalCompleteReconstruction(
const Tracks &tracks,
typename PipelineRoutines::Reconstruction *reconstruction) {
int max_track = tracks.MaxTrack();
int max_image = tracks.MaxImage();
int num_resects = -1;
int num_intersects = -1;
LG << "Max track: " << max_track;
LG << "Max image: " << max_image;
LG << "Number of markers: " << tracks.NumMarkers();
while (num_resects != 0 || num_intersects != 0) {
// Do all possible intersections.
num_intersects = 0;
for (int track = 0; track <= max_track; ++track) {
if (reconstruction->PointForTrack(track)) {
LG << "Skipping point: " << track;
continue;
}
vector<Marker> all_markers = tracks.MarkersForTrack(track);
LG << "Got " << all_markers.size() << " markers for track " << track;
vector<Marker> reconstructed_markers;
for (int i = 0; i < all_markers.size(); ++i) {
if (reconstruction->CameraForImage(all_markers[i].image)) {
reconstructed_markers.push_back(all_markers[i]);
}
}
LG << "Got " << reconstructed_markers.size()
<< " reconstructed markers for track " << track;
if (reconstructed_markers.size() >= 2) {
PipelineRoutines::Intersect(reconstructed_markers, reconstruction);
num_intersects++;
LG << "Ran Intersect() for track " << track;
}
}
if (num_intersects) {
PipelineRoutines::Bundle(tracks, reconstruction);
LG << "Ran Bundle() after intersections.";
}
LG << "Did " << num_intersects << " intersects.";
// Do all possible resections.
num_resects = 0;
for (int image = 0; image <= max_image; ++image) {
if (reconstruction->CameraForImage(image)) {
LG << "Skipping frame: " << image;
continue;
}
vector<Marker> all_markers = tracks.MarkersInImage(image);
LG << "Got " << all_markers.size() << " markers for image " << image;
vector<Marker> reconstructed_markers;
for (int i = 0; i < all_markers.size(); ++i) {
if (reconstruction->PointForTrack(all_markers[i].track)) {
reconstructed_markers.push_back(all_markers[i]);
}
}
LG << "Got " << reconstructed_markers.size()
<< " reconstructed markers for image " << image;
if (reconstructed_markers.size() >= 5) {
if (PipelineRoutines::Resect(reconstructed_markers, reconstruction, false)) {
num_resects++;
LG << "Ran Resect() for image " << image;
} else {
LG << "Failed Resect() for image " << image;
}
}
}
if (num_resects) {
PipelineRoutines::Bundle(tracks, reconstruction);
}
LG << "Did " << num_resects << " resects.";
}
// One last pass...
num_resects = 0;
for (int image = 0; image <= max_image; ++image) {
if (reconstruction->CameraForImage(image)) {
LG << "Skipping frame: " << image;
continue;
}
vector<Marker> all_markers = tracks.MarkersInImage(image);
vector<Marker> reconstructed_markers;
for (int i = 0; i < all_markers.size(); ++i) {
if (reconstruction->PointForTrack(all_markers[i].track)) {
reconstructed_markers.push_back(all_markers[i]);
}
}
if (reconstructed_markers.size() >= 5) {
if (PipelineRoutines::Resect(reconstructed_markers, reconstruction, true)) {
num_resects++;
LG << "Ran Resect() for image " << image;
} else {
LG << "Failed Resect() for image " << image;
}
}
}
if (num_resects) {
PipelineRoutines::Bundle(tracks, reconstruction);
}
}
template<typename PipelineRoutines>
double InternalReprojectionError(const Tracks &image_tracks,
const typename PipelineRoutines::Reconstruction &reconstruction,
const CameraIntrinsics &intrinsics) {
int num_skipped = 0;
int num_reprojected = 0;
double total_error = 0.0;
vector<Marker> markers = image_tracks.AllMarkers();
for (int i = 0; i < markers.size(); ++i) {
const typename PipelineRoutines::Camera *camera =
reconstruction.CameraForImage(markers[i].image);
const typename PipelineRoutines::Point *point =
reconstruction.PointForTrack(markers[i].track);
if (!camera || !point) {
num_skipped++;
continue;
}
num_reprojected++;
Marker reprojected_marker =
PipelineRoutines::ProjectMarker(*point, *camera, intrinsics);
double ex = reprojected_marker.x - markers[i].x;
double ey = reprojected_marker.y - markers[i].y;
const int N = 100;
char line[N];
snprintf(line, N,
"image %-3d track %-3d "
"x %7.1f y %7.1f "
"rx %7.1f ry %7.1f "
"ex %7.1f ey %7.1f"
" e %7.1f",
markers[i].image,
markers[i].track,
markers[i].x,
markers[i].y,
reprojected_marker.x,
reprojected_marker.y,
ex,
ey,
sqrt(ex*ex + ey*ey));
//LG << line;
total_error += sqrt(ex*ex + ey*ey);
}
LG << "Skipped " << num_skipped << " markers.";
LG << "Reprojected " << num_reprojected << " markers.";
LG << "Total error: " << total_error;
LG << "Average error: " << (total_error / num_reprojected) << " [pixels].";
return total_error / num_reprojected;
}
double EuclideanReprojectionError(const Tracks &image_tracks,
const EuclideanReconstruction &reconstruction,
const CameraIntrinsics &intrinsics) {
return InternalReprojectionError<EuclideanPipelineRoutines>(image_tracks,
reconstruction,
intrinsics);
}
double ProjectiveReprojectionError(
const Tracks &image_tracks,
const ProjectiveReconstruction &reconstruction,
const CameraIntrinsics &intrinsics) {
return InternalReprojectionError<ProjectivePipelineRoutines>(image_tracks,
reconstruction,
intrinsics);
}
void EuclideanCompleteReconstruction(const Tracks &tracks,
EuclideanReconstruction *reconstruction) {
InternalCompleteReconstruction<EuclideanPipelineRoutines>(tracks,
reconstruction);
}
void ProjectiveCompleteReconstruction(const Tracks &tracks,
ProjectiveReconstruction *reconstruction) {
InternalCompleteReconstruction<ProjectivePipelineRoutines>(tracks,
reconstruction);
}
void InvertIntrinsicsForTracks(const Tracks &raw_tracks,
const CameraIntrinsics &camera_intrinsics,
Tracks *calibrated_tracks) {
vector<Marker> markers = raw_tracks.AllMarkers();
for (int i = 0; i < markers.size(); ++i) {
camera_intrinsics.InvertIntrinsics(markers[i].x,
markers[i].y,
&(markers[i].x),
&(markers[i].y));
}
*calibrated_tracks = Tracks(markers);
}
} // namespace libmv

@ -0,0 +1,95 @@
// 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_SIMPLE_PIPELINE_PIPELINE_H_
#define LIBMV_SIMPLE_PIPELINE_PIPELINE_H_
#include "libmv/simple_pipeline/tracks.h"
#include "libmv/simple_pipeline/reconstruction.h"
namespace libmv {
/*!
Estimate camera poses and scene 3D coordinates for all frames and tracks.
This method should be used once there is an initial reconstruction in
place, for example by reconstructing from two frames that have a sufficient
baseline and number of tracks in common. This function iteratively
triangulates points that are visible by cameras that have their pose
estimated, then resections (i.e. estimates the pose) of cameras that are
not estimated yet that can see triangulated points. This process is
repeated until all points and cameras are estimated. Periodically, bundle
adjustment is run to ensure a quality reconstruction.
\a tracks should contain markers used in the reconstruction.
\a reconstruction should contain at least some 3D points or some estimated
cameras. The minimum number of cameras is two (with no 3D points) and the
minimum number of 3D points (with no estimated cameras) is 5.
\sa EuclideanResect, EuclideanIntersect, EuclideanBundle
*/
void EuclideanCompleteReconstruction(const Tracks &tracks,
EuclideanReconstruction *reconstruction);
/*!
Estimate camera matrices and homogeneous 3D coordinates for all frames and
tracks.
This method should be used once there is an initial reconstruction in
place, for example by reconstructing from two frames that have a sufficient
baseline and number of tracks in common. This function iteratively
triangulates points that are visible by cameras that have their pose
estimated, then resections (i.e. estimates the pose) of cameras that are
not estimated yet that can see triangulated points. This process is
repeated until all points and cameras are estimated. Periodically, bundle
adjustment is run to ensure a quality reconstruction.
\a tracks should contain markers used in the reconstruction.
\a reconstruction should contain at least some 3D points or some estimated
cameras. The minimum number of cameras is two (with no 3D points) and the
minimum number of 3D points (with no estimated cameras) is 5.
\sa ProjectiveResect, ProjectiveIntersect, ProjectiveBundle
*/
void ProjectiveCompleteReconstruction(const Tracks &tracks,
ProjectiveReconstruction *reconstruction);
class CameraIntrinsics;
// TODO(keir): Decide if we want these in the public API, and if so, what the
// appropriate include file is.
double EuclideanReprojectionError(const Tracks &image_tracks,
const EuclideanReconstruction &reconstruction,
const CameraIntrinsics &intrinsics);
double ProjectiveReprojectionError(
const Tracks &image_tracks,
const ProjectiveReconstruction &reconstruction,
const CameraIntrinsics &intrinsics);
void InvertIntrinsicsForTracks(const Tracks &raw_tracks,
const CameraIntrinsics &camera_intrinsics,
Tracks *calibrated_tracks);
} // namespace libmv
#endif // LIBMV_SIMPLE_PIPELINE_PIPELINE_H_

@ -0,0 +1,191 @@
// 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/simple_pipeline/reconstruction.h"
#include "libmv/numeric/numeric.h"
#include "libmv/logging/logging.h"
namespace libmv {
EuclideanReconstruction::EuclideanReconstruction() {}
EuclideanReconstruction::EuclideanReconstruction(
const EuclideanReconstruction &other) {
cameras_ = other.cameras_;
points_ = other.points_;
}
EuclideanReconstruction &EuclideanReconstruction::operator=(
const EuclideanReconstruction &other) {
if (&other != this) {
cameras_ = other.cameras_;
points_ = other.points_;
}
return *this;
}
void EuclideanReconstruction::InsertCamera(int image,
const Mat3 &R,
const Vec3 &t) {
LG << "InsertCamera " << image << ":\nR:\n"<< R << "\nt:\n" << t;
if (image >= cameras_.size()) {
cameras_.resize(image + 1);
}
cameras_[image].image = image;
cameras_[image].R = R;
cameras_[image].t = t;
}
void EuclideanReconstruction::InsertPoint(int track, const Vec3 &X) {
LG << "InsertPoint " << track << ":\n" << X;
if (track >= points_.size()) {
points_.resize(track + 1);
}
points_[track].track = track;
points_[track].X = X;
}
EuclideanCamera *EuclideanReconstruction::CameraForImage(int image) {
return const_cast<EuclideanCamera *>(
static_cast<const EuclideanReconstruction *>(
this)->CameraForImage(image));
}
const EuclideanCamera *EuclideanReconstruction::CameraForImage(
int image) const {
if (image < 0 || image >= cameras_.size()) {
return NULL;
}
const EuclideanCamera *camera = &cameras_[image];
if (camera->image == -1) {
return NULL;
}
return camera;
}
vector<EuclideanCamera> EuclideanReconstruction::AllCameras() const {
vector<EuclideanCamera> cameras;
for (int i = 0; i < cameras_.size(); ++i) {
if (cameras_[i].image != -1) {
cameras.push_back(cameras_[i]);
}
}
return cameras;
}
EuclideanPoint *EuclideanReconstruction::PointForTrack(int track) {
return const_cast<EuclideanPoint *>(
static_cast<const EuclideanReconstruction *>(this)->PointForTrack(track));
}
const EuclideanPoint *EuclideanReconstruction::PointForTrack(int track) const {
if (track < 0 || track >= points_.size()) {
return NULL;
}
const EuclideanPoint *point = &points_[track];
if (point->track == -1) {
return NULL;
}
return point;
}
vector<EuclideanPoint> EuclideanReconstruction::AllPoints() const {
vector<EuclideanPoint> points;
for (int i = 0; i < points_.size(); ++i) {
if (points_[i].track != -1) {
points.push_back(points_[i]);
}
}
return points;
}
void ProjectiveReconstruction::InsertCamera(int image,
const Mat34 &P) {
LG << "InsertCamera " << image << ":\nP:\n"<< P;
if (image >= cameras_.size()) {
cameras_.resize(image + 1);
}
cameras_[image].image = image;
cameras_[image].P = P;
}
void ProjectiveReconstruction::InsertPoint(int track, const Vec4 &X) {
LG << "InsertPoint " << track << ":\n" << X;
if (track >= points_.size()) {
points_.resize(track + 1);
}
points_[track].track = track;
points_[track].X = X;
}
ProjectiveCamera *ProjectiveReconstruction::CameraForImage(int image) {
return const_cast<ProjectiveCamera *>(
static_cast<const ProjectiveReconstruction *>(
this)->CameraForImage(image));
}
const ProjectiveCamera *ProjectiveReconstruction::CameraForImage(
int image) const {
if (image < 0 || image >= cameras_.size()) {
return NULL;
}
const ProjectiveCamera *camera = &cameras_[image];
if (camera->image == -1) {
return NULL;
}
return camera;
}
vector<ProjectiveCamera> ProjectiveReconstruction::AllCameras() const {
vector<ProjectiveCamera> cameras;
for (int i = 0; i < cameras_.size(); ++i) {
if (cameras_[i].image != -1) {
cameras.push_back(cameras_[i]);
}
}
return cameras;
}
ProjectivePoint *ProjectiveReconstruction::PointForTrack(int track) {
return const_cast<ProjectivePoint *>(
static_cast<const ProjectiveReconstruction *>(this)->PointForTrack(track));
}
const ProjectivePoint *ProjectiveReconstruction::PointForTrack(int track) const {
if (track < 0 || track >= points_.size()) {
return NULL;
}
const ProjectivePoint *point = &points_[track];
if (point->track == -1) {
return NULL;
}
return point;
}
vector<ProjectivePoint> ProjectiveReconstruction::AllPoints() const {
vector<ProjectivePoint> points;
for (int i = 0; i < points_.size(); ++i) {
if (points_[i].track != -1) {
points.push_back(points_[i]);
}
}
return points;
}
} // namespace libmv

@ -0,0 +1,217 @@
// 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_SIMPLE_PIPELINE_RECONSTRUCTION_H_
#define LIBMV_SIMPLE_PIPELINE_RECONSTRUCTION_H_
#include "libmv/base/vector.h"
#include "libmv/numeric/numeric.h"
namespace libmv {
/*!
A EuclideanCamera is the location and rotation of the camera viewing \a image.
\a image identify which image from \l Tracks this camera represents.
\a R is a 3x3 matrix representing the rotation of the camera.
\a t is a translation vector representing its positions.
\sa Reconstruction
*/
struct EuclideanCamera {
EuclideanCamera() : image(-1) {}
EuclideanCamera(const EuclideanCamera &c) : image(c.image), R(c.R), t(c.t) {}
int image;
Mat3 R;
Vec3 t;
};
/*!
A Point is the 3D location of a track.
\a track identify which track from \l Tracks this point corresponds to.
\a X represents the 3D position of the track.
\sa Reconstruction
*/
struct EuclideanPoint {
EuclideanPoint() : track(-1) {}
EuclideanPoint(const EuclideanPoint &p) : track(p.track), X(p.X) {}
int track;
Vec3 X;
};
/*!
The EuclideanReconstruction class stores \link EuclideanCamera cameras
\endlink and \link EuclideanPoint points \endlink.
The EuclideanReconstruction container is intended as the store of 3D
reconstruction data to be used with the MultiView API.
The container has lookups to query a \a EuclideanCamera from an \a image or
a \a EuclideanPoint from a \a track.
\sa Camera, Point
*/
class EuclideanReconstruction {
public:
// Default constructor starts with no cameras.
EuclideanReconstruction();
/// Copy constructor.
EuclideanReconstruction(const EuclideanReconstruction &other);
EuclideanReconstruction &operator=(const EuclideanReconstruction &other);
/*!
Insert a camera into the set. If there is already a camera for the given
\a image, the existing camera is replaced. If there is no camera for the
given \a image, a new one is added.
\a image is the key used to retrieve the cameras with the other methods
in this class.
\note You should use the same \a image identifier as in \l Tracks.
*/
void InsertCamera(int image, const Mat3 &R, const Vec3 &t);
/*!
Insert a point into the reconstruction. If there is already a point for
the given \a track, the existing point is replaced. If there is no point
for the given \a track, a new one is added.
\a track is the key used to retrieve the points with the
other methods in this class.
\note You should use the same \a track identifier as in \l Tracks.
*/
void InsertPoint(int track, const Vec3 &X);
/// Returns a pointer to the camera corresponding to \a image.
EuclideanCamera *CameraForImage(int image);
const EuclideanCamera *CameraForImage(int image) const;
/// Returns all cameras.
vector<EuclideanCamera> AllCameras() const;
/// Returns a pointer to the point corresponding to \a track.
EuclideanPoint *PointForTrack(int track);
const EuclideanPoint *PointForTrack(int track) const;
/// Returns all points.
vector<EuclideanPoint> AllPoints() const;
private:
vector<EuclideanCamera> cameras_;
vector<EuclideanPoint> points_;
};
/*!
A ProjectiveCamera is the projection matrix for the camera of \a image.
\a image identify which image from \l Tracks this camera represents.
\a P is the 3x4 projection matrix.
\sa ProjectiveReconstruction
*/
struct ProjectiveCamera {
ProjectiveCamera() : image(-1) {}
ProjectiveCamera(const ProjectiveCamera &c) : image(c.image), P(c.P) {}
int image;
Mat34 P;
};
/*!
A Point is the 3D location of a track.
\a track identifies which track from \l Tracks this point corresponds to.
\a X is the homogeneous 3D position of the track.
\sa Reconstruction
*/
struct ProjectivePoint {
ProjectivePoint() : track(-1) {}
ProjectivePoint(const ProjectivePoint &p) : track(p.track), X(p.X) {}
int track;
Vec4 X;
};
/*!
The ProjectiveReconstruction class stores \link ProjectiveCamera cameras
\endlink and \link ProjectivePoint points \endlink.
The ProjectiveReconstruction container is intended as the store of 3D
reconstruction data to be used with the MultiView API.
The container has lookups to query a \a ProjectiveCamera from an \a image or
a \a ProjectivePoint from a \a track.
\sa Camera, Point
*/
class ProjectiveReconstruction {
public:
/*!
Insert a camera into the set. If there is already a camera for the given
\a image, the existing camera is replaced. If there is no camera for the
given \a image, a new one is added.
\a image is the key used to retrieve the cameras with the other methods
in this class.
\note You should use the same \a image identifier as in \l Tracks.
*/
void InsertCamera(int image, const Mat34 &P);
/*!
Insert a point into the reconstruction. If there is already a point for
the given \a track, the existing point is replaced. If there is no point
for the given \a track, a new one is added.
\a track is the key used to retrieve the points with the
other methods in this class.
\note You should use the same \a track identifier as in \l Tracks.
*/
void InsertPoint(int track, const Vec4 &X);
/// Returns a pointer to the camera corresponding to \a image.
ProjectiveCamera *CameraForImage(int image);
const ProjectiveCamera *CameraForImage(int image) const;
/// Returns all cameras.
vector<ProjectiveCamera> AllCameras() const;
/// Returns a pointer to the point corresponding to \a track.
ProjectivePoint *PointForTrack(int track);
const ProjectivePoint *PointForTrack(int track) const;
/// Returns all points.
vector<ProjectivePoint> AllPoints() const;
private:
vector<ProjectiveCamera> cameras_;
vector<ProjectivePoint> points_;
};
} // namespace libmv
#endif // LIBMV_SIMPLE_PIPELINE_RECONSTRUCTION_H_

@ -0,0 +1,271 @@
// 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 <cstdio>
#include "libmv/base/vector.h"
#include "libmv/logging/logging.h"
#include "libmv/multiview/euclidean_resection.h"
#include "libmv/multiview/resection.h"
#include "libmv/multiview/projection.h"
#include "libmv/numeric/numeric.h"
#include "libmv/numeric/levenberg_marquardt.h"
#include "libmv/simple_pipeline/reconstruction.h"
#include "libmv/simple_pipeline/tracks.h"
namespace libmv {
namespace {
Mat2X PointMatrixFromMarkers(const vector<Marker> &markers) {
Mat2X points(2, markers.size());
for (int i = 0; i < markers.size(); ++i) {
points(0, i) = markers[i].x;
points(1, i) = markers[i].y;
}
return points;
}
Mat3 RotationFromEulerVector(Vec3 euler_vector) {
double theta = euler_vector.norm();
if (theta == 0.0) {
return Mat3::Identity();
}
Vec3 w = euler_vector / theta;
Mat3 w_hat = CrossProductMatrix(w);
return Mat3::Identity() + w_hat*sin(theta) + w_hat*w_hat*(1 - cos(theta));
}
// Uses an incremental rotation:
//
// x = R' * R * X + t;
//
// to avoid issues with the rotation representation. R' is derived from a
// euler vector encoding the rotation in 3 parameters; the direction is the
// axis to rotate around and the magnitude is the amount of the rotation.
struct EuclideanResectCostFunction {
public:
typedef Vec FMatrixType;
typedef Vec6 XMatrixType;
EuclideanResectCostFunction(const vector<Marker> &markers,
const EuclideanReconstruction &reconstruction,
const Mat3 initial_R)
: markers(markers),
reconstruction(reconstruction),
initial_R(initial_R) {}
// dRt has dR (delta R) encoded as a euler vector in the first 3 parameters,
// followed by t in the next 3 parameters.
Vec operator()(const Vec6 &dRt) const {
// Unpack R, t from dRt.
Mat3 R = RotationFromEulerVector(dRt.head<3>()) * initial_R;
Vec3 t = dRt.tail<3>();
// Compute the reprojection error for each coordinate.
Vec residuals(2 * markers.size());
residuals.setZero();
for (int i = 0; i < markers.size(); ++i) {
const EuclideanPoint &point =
*reconstruction.PointForTrack(markers[i].track);
Vec3 projected = R * point.X + t;
projected /= projected(2);
residuals[2*i + 0] = projected(0) - markers[i].x;
residuals[2*i + 1] = projected(1) - markers[i].y;
}
return residuals;
}
const vector<Marker> &markers;
const EuclideanReconstruction &reconstruction;
const Mat3 &initial_R;
};
} // namespace
bool EuclideanResect(const vector<Marker> &markers,
EuclideanReconstruction *reconstruction, bool final_pass) {
if (markers.size() < 5) {
return false;
}
Mat2X points_2d = PointMatrixFromMarkers(markers);
Mat3X points_3d(3, markers.size());
for (int i = 0; i < markers.size(); i++) {
points_3d.col(i) = reconstruction->PointForTrack(markers[i].track)->X;
}
LG << "Points for resect:\n" << points_2d;
Mat3 R;
Vec3 t;
if (0 || !euclidean_resection::EuclideanResection(points_2d, points_3d, &R, &t)) {
// printf("Resection for image %d failed\n", markers[0].image);
LG << "Resection for image " << markers[0].image << " failed;"
<< " trying fallback projective resection.";
if (!final_pass) return false;
// Euclidean resection failed. Fall back to projective resection, which is
// less reliable but better conditioned when there are many points.
Mat34 P;
Mat4X points_3d_homogeneous(4, markers.size());
for (int i = 0; i < markers.size(); i++) {
points_3d_homogeneous.col(i).head<3>() = points_3d.col(i);
points_3d_homogeneous(3, i) = 1.0;
}
resection::Resection(points_2d, points_3d_homogeneous, &P);
if ((P * points_3d_homogeneous.col(0))(2) < 0) {
LG << "Point behind camera; switch sign.";
P = -P;
}
Mat3 ignored;
KRt_From_P(P, &ignored, &R, &t);
// The R matrix should be a rotation, but don't rely on it.
Eigen::JacobiSVD<Mat3> svd(R, Eigen::ComputeFullU | Eigen::ComputeFullV);
LG << "Resection rotation is: " << svd.singularValues().transpose();
LG << "Determinant is: " << R.determinant();
// Correct to make R a rotation.
R = svd.matrixU() * svd.matrixV().transpose();
Vec3 xx = R * points_3d.col(0) + t;
if (xx(2) < 0.0) {
LG << "Final point is still behind camera...";
}
// XXX Need to check if error is horrible and fail here too in that case.
}
// Refine the result.
typedef LevenbergMarquardt<EuclideanResectCostFunction> Solver;
// Give the cost our initial guess for R.
EuclideanResectCostFunction resect_cost(markers, *reconstruction, R);
// Encode the initial parameters: start with zero delta rotation, and the
// guess for t obtained from resection.
Vec6 dRt = Vec6::Zero();
dRt.tail<3>() = t;
Solver solver(resect_cost);
Solver::SolverParameters params;
Solver::Results results = solver.minimize(params, &dRt);
LG << "LM found incremental rotation: " << dRt.head<3>().transpose();
// TODO(keir): Check results to ensure clean termination.
// Unpack the rotation and translation.
R = RotationFromEulerVector(dRt.head<3>()) * R;
t = dRt.tail<3>();
LG << "Resection for image " << markers[0].image << " got:\n"
<< "R:\n" << R << "\nt:\n" << t;
reconstruction->InsertCamera(markers[0].image, R, t);
return true;
}
namespace {
// Directly parameterize the projection matrix, P, which is a 12 parameter
// homogeneous entry. In theory P should be parameterized with only 11
// parametetrs, but in practice it works fine to let the extra degree of
// freedom drift.
struct ProjectiveResectCostFunction {
public:
typedef Vec FMatrixType;
typedef Vec12 XMatrixType;
ProjectiveResectCostFunction(const vector<Marker> &markers,
const ProjectiveReconstruction &reconstruction)
: markers(markers),
reconstruction(reconstruction) {}
Vec operator()(const Vec12 &vector_P) const {
// Unpack P from vector_P.
Map<const Mat34> P(vector_P.data(), 3, 4);
// Compute the reprojection error for each coordinate.
Vec residuals(2 * markers.size());
residuals.setZero();
for (int i = 0; i < markers.size(); ++i) {
const ProjectivePoint &point =
*reconstruction.PointForTrack(markers[i].track);
Vec3 projected = P * point.X;
projected /= projected(2);
residuals[2*i + 0] = projected(0) - markers[i].x;
residuals[2*i + 1] = projected(1) - markers[i].y;
}
return residuals;
}
const vector<Marker> &markers;
const ProjectiveReconstruction &reconstruction;
};
} // namespace
bool ProjectiveResect(const vector<Marker> &markers,
ProjectiveReconstruction *reconstruction) {
if (markers.size() < 5) {
return false;
}
// Stack the homogeneous 3D points as the columns of a matrix.
Mat2X points_2d = PointMatrixFromMarkers(markers);
Mat4X points_3d_homogeneous(4, markers.size());
for (int i = 0; i < markers.size(); i++) {
points_3d_homogeneous.col(i) =
reconstruction->PointForTrack(markers[i].track)->X;
}
LG << "Points for resect:\n" << points_2d;
// Resection the point.
Mat34 P;
resection::Resection(points_2d, points_3d_homogeneous, &P);
// Flip the sign of P if necessary to keep the point in front of the camera.
if ((P * points_3d_homogeneous.col(0))(2) < 0) {
LG << "Point behind camera; switch sign.";
P = -P;
}
// TODO(keir): Check if error is horrible and fail in that case.
// Refine the resulting projection matrix using geometric error.
typedef LevenbergMarquardt<ProjectiveResectCostFunction> Solver;
ProjectiveResectCostFunction resect_cost(markers, *reconstruction);
// Pack the initial P matrix into a size-12 vector..
Vec12 vector_P = Map<Vec12>(P.data());
Solver solver(resect_cost);
Solver::SolverParameters params;
Solver::Results results = solver.minimize(params, &vector_P);
// TODO(keir): Check results to ensure clean termination.
// Unpack the projection matrix.
P = Map<Mat34>(vector_P.data(), 3, 4);
LG << "Resection for image " << markers[0].image << " got:\n"
<< "P:\n" << P;
reconstruction->InsertCamera(markers[0].image, P);
return true;
}
} // namespace libmv

@ -0,0 +1,86 @@
// 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_SIMPLE_PIPELINE_RESECT_H
#define LIBMV_SIMPLE_PIPELINE_RESECT_H
#include "libmv/base/vector.h"
#include "libmv/simple_pipeline/tracks.h"
#include "libmv/simple_pipeline/reconstruction.h"
namespace libmv {
/*!
Estimate the Euclidean pose of a camera from 2D to 3D correspondences.
This takes a set of markers visible in one frame (which is the one to
resection), such that the markers are also reconstructed in 3D in the
reconstruction object, and solves for the pose and orientation of the
camera for that frame.
\a markers should contain \l Marker markers \endlink belonging to tracks
visible in the one frame to be resectioned. Each of the tracks associated
with the markers must have a corresponding reconstructed 3D position in the
\a *reconstruction object.
\a *reconstruction should contain the 3D points associated with the tracks
for the markers present in \a markers.
\note This assumes a calibrated reconstruction, e.g. the markers are
already corrected for camera intrinsics and radial distortion.
\note This assumes an outlier-free set of markers.
\return True if the resection was successful, false otherwise.
\sa EuclideanIntersect, EuclideanReconstructTwoFrames
*/
bool EuclideanResect(const vector<Marker> &markers,
EuclideanReconstruction *reconstruction, bool final_pass);
/*!
Estimate the projective pose of a camera from 2D to 3D correspondences.
This takes a set of markers visible in one frame (which is the one to
resection), such that the markers are also reconstructed in a projective
frame in the reconstruction object, and solves for the projective matrix of
the camera for that frame.
\a markers should contain \l Marker markers \endlink belonging to tracks
visible in the one frame to be resectioned. Each of the tracks associated
with the markers must have a corresponding reconstructed homogeneous 3D
position in the \a *reconstruction object.
\a *reconstruction should contain the homogeneous 3D points associated with
the tracks for the markers present in \a markers.
\note This assumes radial distortion has already been corrected, but
otherwise works for uncalibrated sequences.
\note This assumes an outlier-free set of markers.
\return True if the resection was successful, false otherwise.
\sa ProjectiveIntersect, ProjectiveReconstructTwoFrames
*/
bool ProjectiveResect(const vector<Marker> &markers,
ProjectiveReconstruction *reconstruction);
} // namespace libmv
#endif // LIBMV_SIMPLE_PIPELINE_RESECT_H

@ -0,0 +1,159 @@
// 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 <algorithm>
#include <vector>
#include <iterator>
#include "libmv/numeric/numeric.h"
#include "libmv/simple_pipeline/tracks.h"
namespace libmv {
Tracks::Tracks(const Tracks &other) {
markers_ = other.markers_;
}
Tracks::Tracks(const vector<Marker> &markers) : markers_(markers) {}
void Tracks::Insert(int image, int track, double x, double y) {
// TODO(keir): Wow, 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].image == image &&
markers_[i].track == track) {
markers_[i].x = x;
markers_[i].y = y;
return;
}
}
Marker marker = { image, track, x, y };
markers_.push_back(marker);
}
vector<Marker> Tracks::AllMarkers() const {
return markers_;
}
vector<Marker> Tracks::MarkersInImage(int image) const {
vector<Marker> markers;
for (int i = 0; i < markers_.size(); ++i) {
if (image == markers_[i].image) {
markers.push_back(markers_[i]);
}
}
return markers;
}
vector<Marker> Tracks::MarkersForTrack(int track) const {
vector<Marker> markers;
for (int i = 0; i < markers_.size(); ++i) {
if (track == markers_[i].track) {
markers.push_back(markers_[i]);
}
}
return markers;
}
vector<Marker> Tracks::MarkersForTracksInBothImages(int image1, int image2) const {
std::vector<int> image1_tracks;
std::vector<int> image2_tracks;
for (int i = 0; i < markers_.size(); ++i) {
int image = markers_[i].image;
if (image == image1) {
image1_tracks.push_back(markers_[i].track);
} else if (image == image2) {
image2_tracks.push_back(markers_[i].track);
}
}
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));
vector<Marker> markers;
for (int i = 0; i < markers_.size(); ++i) {
if ((markers_[i].image == image1 || markers_[i].image == image2) &&
std::binary_search(intersection.begin(),intersection.end(),
markers_[i].track)) {
markers.push_back(markers_[i]);
}
}
return markers;
}
Marker Tracks::MarkerInImageForTrack(int image, int track) const {
for (int i = 0; i < markers_.size(); ++i) {
if (markers_[i].image == image && markers_[i].track == track) {
return markers_[i];
}
}
Marker null = { -1, -1, -1, -1 };
return null;
}
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);
}
void Tracks::RemoveMarker(int image, int track) {
int size = 0;
for (int i = 0; i < markers_.size(); ++i) {
if (markers_[i].image != image || markers_[i].track != track) {
markers_[size++] = markers_[i];
}
}
markers_.resize(size);
}
int Tracks::MaxImage() const {
// TODO(MatthiasF): maintain a max_image_ member (updated on Insert)
int max_image = 0;
for (int i = 0; i < markers_.size(); ++i) {
max_image = std::max(markers_[i].image, max_image);
}
return max_image;
}
int Tracks::MaxTrack() const {
// TODO(MatthiasF): maintain a max_track_ member (updated on Insert)
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 libmv

@ -0,0 +1,119 @@
// 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_SIMPLE_PIPELINE_TRACKS_H_
#define LIBMV_SIMPLE_PIPELINE_TRACKS_H_
#include "libmv/base/vector.h"
namespace libmv {
/*!
A Marker is the 2D location of a tracked point in an image.
\a x, \a y is the position of the marker in pixels from the top left corner
in the image identified by \a image. All markers for to the same target
form a track identified by a common \a track number.
\note Markers are typically aggregated with the help of the \l Tracks class.
\sa Tracks
*/
struct Marker {
int image;
int track;
double x, y;
};
/*!
The Tracks class stores \link Marker reconstruction markers \endlink.
The Tracks container is intended as the store of correspondences between
images, which must get created before any 3D reconstruction can take place.
The container has several fast lookups for queries typically needed for
structure from motion algorithms, such as \l MarkersForTracksInBothImages().
\sa Marker
*/
class Tracks {
public:
Tracks() {}
// Copy constructor for a tracks object.
Tracks(const Tracks &other);
/// Construct a new tracks object using the given markers to start.
Tracks(const vector<Marker> &markers);
/*!
Inserts a marker into the set. If there is already a marker for the given
\a image and \a track, the existing marker is replaced. If there is no
marker for the given \a image and \a track, a new one is added.
\a image and \a track are the keys used to retrieve the markers with the
other methods in this class.
\note To get an identifier for a new track, use \l MaxTrack() + 1.
*/
void Insert(int image, int track, double x, double y);
/// Returns all the markers.
vector<Marker> AllMarkers() const;
/// Returns all the markers belonging to a track.
vector<Marker> MarkersForTrack(int track) const;
/// Returns all the markers visible in \a image.
vector<Marker> MarkersInImage(int image) const;
/*!
Returns the markers in \a image1 and \a image2 which have a common track.
This is not the same as the union of the markers in \a image1 and \a
image2; each marker is for a track that appears in both images.
*/
vector<Marker> MarkersForTracksInBothImages(int image1, int image2) const;
/// Returns the marker in \a image belonging to \a track.
Marker MarkerInImageForTrack(int image, int track) const;
/// Removes all the markers belonging to \a track.
void RemoveMarkersForTrack(int track);
/// Removes the marker in \a image belonging to \a track.
void RemoveMarker(int image, int track);
/// Returns the maximum image identifier used.
int MaxImage() const;
/// Returns the maximum track identifier used.
int MaxTrack() const;
/// Returns the number of markers.
int NumMarkers() const;
private:
vector<Marker> markers_;
};
} // namespace libmv
#endif // LIBMV_SIMPLE_PIPELINE_MARKERS_H_

@ -0,0 +1,132 @@
// Copyright (c) 2007, 2008, 2009, 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/logging/logging.h"
#include "libmv/tracking/klt_region_tracker.h"
#include "libmv/image/image.h"
#include "libmv/image/convolve.h"
#include "libmv/image/sample.h"
namespace libmv {
// Compute the gradient matrix noted by Z and the error vector e. See Good
// Features to Track.
//
// TODO(keir): The calls to SampleLinear() do boundary checking that should
// instead happen outside the loop. Since this is the innermost loop, the extra
// bounds checking hurts performance.
static void ComputeTrackingEquation(const Array3Df &image_and_gradient1,
const Array3Df &image_and_gradient2,
double x1, double y1,
double x2, double y2,
int half_width,
float *gxx,
float *gxy,
float *gyy,
float *ex,
float *ey) {
*gxx = *gxy = *gyy = 0;
*ex = *ey = 0;
for (int r = -half_width; r <= half_width; ++r) {
for (int c = -half_width; c <= half_width; ++c) {
float xx1 = x1 + c;
float yy1 = y1 + r;
float xx2 = x2 + c;
float yy2 = y2 + r;
float I = SampleLinear(image_and_gradient1, yy1, xx1, 0);
float J = SampleLinear(image_and_gradient2, yy2, xx2, 0);
float gx = SampleLinear(image_and_gradient2, yy2, xx2, 1);
float gy = SampleLinear(image_and_gradient2, yy2, xx2, 2);
*gxx += gx * gx;
*gxy += gx * gy;
*gyy += gy * gy;
*ex += (I - J) * gx;
*ey += (I - J) * gy;
}
}
}
// Solve the tracking equation
//
// [gxx gxy] [dx] = [ex]
// [gxy gyy] [dy] = [ey]
//
// for dx and dy. Borrowed from Stan Birchfield's KLT implementation.
static bool SolveTrackingEquation(float gxx, float gxy, float gyy,
float ex, float ey,
float min_determinant,
float *dx, float *dy) {
float det = gxx * gyy - gxy * gxy;
if (det < min_determinant) {
*dx = 0;
*dy = 0;
return false;
}
*dx = (gyy * ex - gxy * ey) / det;
*dy = (gxx * ey - gxy * ex) / det;
return true;
}
bool KltRegionTracker::Track(const FloatImage &image1,
const FloatImage &image2,
double x1, double y1,
double *x2, double *y2) const {
Array3Df image_and_gradient1;
Array3Df image_and_gradient2;
BlurredImageAndDerivativesChannels(image1, sigma, &image_and_gradient1);
BlurredImageAndDerivativesChannels(image2, sigma, &image_and_gradient2);
int i;
float dx = 0, dy = 0;
for (i = 0; i < max_iterations; ++i) {
// Compute gradient matrix and error vector.
float gxx, gxy, gyy, ex, ey;
ComputeTrackingEquation(image_and_gradient1,
image_and_gradient2,
x1, y1,
*x2, *y2,
half_window_size,
&gxx, &gxy, &gyy, &ex, &ey);
// Solve the linear system for the best update to x2 and y2.
if (!SolveTrackingEquation(gxx, gxy, gyy, ex, ey, min_determinant,
&dx, &dy)) {
// The determinant, which indicates the trackiness of the point, is too
// small, so fail out.
LG << "Determinant too small; failing tracking.";
return false;
}
// Update the position with the solved displacement.
*x2 += dx;
*y2 += dy;
// If the update is small, then we probably found the target.
if (dx * dx + dy * dy < min_update_squared_distance) {
LG << "Successful track in " << i << " iterations.";
return true;
}
}
// Getting here means we hit max iterations, so tracking failed.
LG << "Too many iterations.";
return false;
}
} // namespace libmv

@ -0,0 +1,55 @@
// Copyright (c) 2007, 2008, 2009, 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_REGION_TRACKING_KLT_REGION_TRACKER_H_
#define LIBMV_REGION_TRACKING_KLT_REGION_TRACKER_H_
#include "libmv/image/image.h"
#include "libmv/tracking/region_tracker.h"
namespace libmv {
struct KltRegionTracker : public RegionTracker {
KltRegionTracker()
: half_window_size(4),
max_iterations(16),
min_determinant(1e-6),
min_update_squared_distance(1e-6),
sigma(0.9) {}
virtual ~KltRegionTracker() {}
// Tracker interface.
virtual bool Track(const FloatImage &image1,
const FloatImage &image2,
double x1, double y1,
double *x2, double *y2) const;
// No point in creating getters or setters.
int half_window_size;
int max_iterations;
double min_determinant;
double min_update_squared_distance;
double sigma;
};
} // namespace libmv
#endif // LIBMV_REGION_TRACKING_KLT_REGION_TRACKER_H_

@ -0,0 +1,80 @@
// Copyright (c) 2007, 2008, 2009, 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 <vector>
#include "libmv/image/convolve.h"
#include "libmv/image/image.h"
#include "libmv/image/sample.h"
#include "libmv/logging/logging.h"
#include "libmv/tracking/pyramid_region_tracker.h"
namespace libmv {
static void MakePyramid(const FloatImage &image, int num_levels,
std::vector<FloatImage> *pyramid) {
pyramid->resize(num_levels);
(*pyramid)[0] = image;
for (int i = 1; i < num_levels; ++i) {
DownsampleChannelsBy2((*pyramid)[i - 1], &(*pyramid)[i]);
}
}
bool PyramidRegionTracker::Track(const FloatImage &image1,
const FloatImage &image2,
double x1, double y1,
double *x2, double *y2) const {
// Shrink the guessed x and y location to match the coarsest level + 1 (which
// when gets corrected in the loop).
*x2 /= pow(2., num_levels_);
*y2 /= pow(2., num_levels_);
// Create all the levels of the pyramid, since tracking has to happen from
// the coarsest to finest levels, which means holding on to all levels of the
// pyraid at once.
std::vector<FloatImage> pyramid1(num_levels_);
std::vector<FloatImage> pyramid2(num_levels_);
MakePyramid(image1, num_levels_, &pyramid1);
MakePyramid(image2, num_levels_, &pyramid2);
for (int i = num_levels_ - 1; i >= 0; --i) {
// Position in the first image at pyramid level i.
double xx = x1 / pow(2., i);
double yy = y1 / pow(2., i);
// Guess the new tracked position is where the last level tracked to.
*x2 *= 2;
*y2 *= 2;
// Track the point on this level with the base tracker.
bool succeeded = tracker_->Track(pyramid1[i], pyramid2[i], xx, yy, x2, y2);
if (i == 0 && !succeeded) {
// Only fail on the highest-resolution level, because a failure on a
// coarse level does not mean failure at a lower level (consider
// out-of-bounds conditions).
LG << "Finest level of pyramid tracking failed; failing.";
return false;
}
}
return true;
}
} // namespace libmv

@ -0,0 +1,46 @@
// 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_CORRESPONDENCE_PYRAMID_TRACKER_H_
#define LIBMV_CORRESPONDENCE_PYRAMID_TRACKER_H_
#include "libmv/image/image.h"
#include "libmv/base/scoped_ptr.h"
#include "libmv/tracking/region_tracker.h"
namespace libmv {
class PyramidRegionTracker : public RegionTracker {
public:
PyramidRegionTracker(RegionTracker *tracker, int num_levels)
: tracker_(tracker), num_levels_(num_levels) {}
virtual bool Track(const FloatImage &image1,
const FloatImage &image2,
double x1, double y1,
double *x2, double *y2) const;
private:
scoped_ptr<RegionTracker> tracker_;
int num_levels_;
};
} // namespace libmv
#endif // LIBMV_CORRESPONDENCE_PYRAMID_TRACKER_H_

@ -0,0 +1,48 @@
// 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_TRACKING_TRACKER_H_
#define LIBMV_TRACKING_TRACKER_H_
#include "libmv/image/image.h"
namespace libmv {
class RegionTracker {
public:
RegionTracker() {}
virtual ~RegionTracker() {}
/*!
Track a point from \a image1 to \a image2.
\a x2, \a y2 should start out as a best guess for the position in \a
image2. If no guess is available, (\a x1, \a y1) is a good start. Returns
true on success, false otherwise
*/
virtual bool Track(const FloatImage &image1,
const FloatImage &image2,
double x1, double y1,
double *x2, double *y2) const = 0;
};
} // namespace libmv
#endif // LIBMV_CORRESPONDENCE_TRACKER_H_

@ -0,0 +1,47 @@
// 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 <cmath>
#include <vector>
#include "libmv/tracking/retrack_region_tracker.h"
namespace libmv {
bool RetrackRegionTracker::Track(const FloatImage &image1,
const FloatImage &image2,
double x1, double y1,
double *x2, double *y2) const {
// Track forward, getting x2 and y2.
if (!tracker_->Track(image1, image2, x1, y1, x2, y2)) {
return false;
}
// Now track x2 and y2 backward, to get xx1 and yy1 which, if the track is
// good, should match x1 and y1 (but may not if the track is bad).
double xx1 = *x2, yy1 = *x2;
if (!tracker_->Track(image2, image1, *x2, *y2, &xx1, &yy1)) {
return false;
}
double dx = xx1 - x1;
double dy = yy1 - y1;
return sqrt(dx * dx + dy * dy) < tolerance_;
}
} // namespace libmv

@ -0,0 +1,48 @@
// 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_TRACKING_RETRACK_REGION_TRACKER_H_
#define LIBMV_TRACKING_RETRACK_REGION_TRACKER_H_
#include "libmv/image/image.h"
#include "libmv/base/scoped_ptr.h"
#include "libmv/tracking/region_tracker.h"
namespace libmv {
// A region tracker that tries tracking backwards and forwards, rejecting a
// track that doesn't track backwards to the starting point.
class RetrackRegionTracker : public RegionTracker {
public:
RetrackRegionTracker(RegionTracker *tracker, double tolerance)
: tracker_(tracker), tolerance_(tolerance) {}
virtual bool Track(const FloatImage &image1,
const FloatImage &image2,
double x1, double y1,
double *x2, double *y2) const;
private:
scoped_ptr<RegionTracker> tracker_;
double tolerance_;
};
} // namespace libmv
#endif // LIBMV_TRACKING_RETRACK_REGION_TRACKER_H_

174
extern/libmv/libmv/tracking/sad.cc vendored Normal file

@ -0,0 +1,174 @@
/****************************************************************************
**
** 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/tracking/sad.h"
#include <stdlib.h>
#include <math.h>
namespace libmv {
void LaplaceFilter(ubyte* src, ubyte* dst, int width, int height, int strength) {
for(int y=1; y<height-1; y++) for(int x=1; x<width-1; x++) {
const ubyte* 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;
}
}
struct vec2 {
float x,y;
inline vec2(float x, float y):x(x),y(y){}
};
inline vec2 operator*(mat32 m, vec2 v) {
return vec2(v.x*m(0,0)+v.y*m(0,1)+m(0,2),v.x*m(1,0)+v.y*m(1,1)+m(1,2));
}
//! fixed point bilinear sample with precision k
template <int k> inline int sample(const ubyte* image,int stride, int x, int y, int u, int v) {
const ubyte* s = &image[y*stride+x];
return ((s[ 0] * (k-u) + s[ 1] * u) * (k-v)
+ (s[stride] * (k-u) + s[stride+1] * u) * ( v) ) / (k*k);
}
#ifdef __SSE__
#include <xmmintrin.h>
int lround(float x) { return _mm_cvtss_si32(_mm_set_ss(x)); }
#elif defined(_MSC_VER)
int lround(float x) { return x+0.5; }
#endif
//TODO(MatthiasF): SSE optimization
void SamplePattern(ubyte* image, int stride, mat32 warp, ubyte* pattern, int size) {
const int k = 256;
for (int i = 0; i < size; i++) for (int j = 0; j < size; j++) {
vec2 p = warp*vec2(j-size/2,i-size/2);
int fx = lround(p.x*k), fy = lround(p.y*k);
int ix = fx/k, iy = fy/k;
int u = fx%k, v = fy%k;
pattern[i*size+j] = sample<k>(image,stride,ix,iy,u,v);
}
}
#ifdef __SSE2__
#include <emmintrin.h>
static uint SAD(const ubyte* pattern, const ubyte* image, int stride, int size) {
__m128i a = _mm_setzero_si128();
for(int i = 0; i < size; i++) {
for(int j = 0; j < size/16; j++) {
a = _mm_adds_epu16(a, _mm_sad_epu8( _mm_loadu_si128((__m128i*)(pattern+i*size+j*16)),
_mm_loadu_si128((__m128i*)(image+i*stride+j*16))));
}
}
return _mm_extract_epi16(a,0) + _mm_extract_epi16(a,4);
}
#else
static uint SAD(const ubyte* pattern, const ubyte* image, int stride, int size) {
uint sad=0;
for(int i = 0; i < size; i++) {
for(int j = 0; j < size; j++) {
sad += abs((int)pattern[i*size+j] - image[i*stride+j]);
}
}
return sad;
}
#endif
float sq(float x) { return x*x; }
float Track(ubyte* reference, ubyte* warped, int size, ubyte* image, int stride, int w, int h, mat32* warp, float areaPenalty, float conditionPenalty) {
mat32 m=*warp;
uint min=-1;
// exhaustive search integer pixel translation
int ix = m(0,2), iy = m(1,2);
for(int y = size/2; y < h-size/2; y++) {
for(int x = size/2; x < w-size/2; x++) {
m(0,2) = x, m(1,2) = y;
uint sad = SAD(warped,&image[(y-size/2)*stride+(x-size/2)],stride,size);
// TODO: using chroma could help disambiguate some cases
if(sad < min) {
min = sad;
ix = x, iy = y;
}
}
}
m(0,2) = ix, m(1,2) = iy;
min=-1; //reset score since direct warped search match too well (but the wrong pattern).
// 6D coordinate descent to find affine transform
ubyte* match = new ubyte[size*size];
float step = 0.5;
for(int p = 0; p < 8; p++) { //foreach precision level
for(int i = 0; i < 2; i++) { // iterate twice per precision level
//TODO: other sweep pattern might converge better
for(int d=0; d < 6; d++) { // iterate dimension sequentially (cyclic descent)
for(float e = -step; e <= step; e+=step) { //solve subproblem (evaluate only along one coordinate)
mat32 t = m;
t.data[d] += e;
//TODO: better performance would also allow a more exhaustive search
SamplePattern(image,stride,t,match,size);
uint sad = SAD(reference,match,size,size);
// regularization: keep constant area and good condition
float area = t(0,0)*t(1,1)-t(0,1)*t(1,0);
float x = sq(t(0,0))+sq(t(0,1)), y = sq(t(1,0))+sq(t(1,1));
float condition = x>y ? x/y : y/x;
sad += size*size*( areaPenalty*sq(area-1) + conditionPenalty*sq(condition-1) );
if(sad < min) {
min = sad;
m = t;
}
}
}
}
step /= 2;
}
*warp = m;
// Compute Pearson product-moment correlation coefficient
uint sX=0,sY=0,sXX=0,sYY=0,sXY=0;
SamplePattern(image,stride,m,match,size);
SAD(reference,match,size,size);
for(int i = 0; i < size; i++) {
for(int j = 0; j < size; j++) {
int x = reference[i*size+j];
int y = match[i*size+j];
sX += x;
sY += y;
sXX += x*x;
sYY += y*y;
sXY += x*y;
}
}
delete[] match;
const int N = size*size;
sX /= N, sY /= N, sXX /= N, sYY /= N, sXY /= N;
return (sXY-sX*sY)/sqrt(double((sXX-sX*sX)*(sYY-sY*sY)));
}
} // namespace libmv

109
extern/libmv/libmv/tracking/sad.h vendored Normal file

@ -0,0 +1,109 @@
/****************************************************************************
**
** 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_TRACKING_SAD_H_
#define LIBMV_TRACKING_SAD_H_
#ifdef __cplusplus
namespace libmv {
#endif
typedef unsigned char ubyte;
typedef unsigned int uint;
/*!
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(ubyte* src, ubyte* dst, int width, int height, int strength);
/// Affine transformation matrix in column major order.
struct mat32 {
float data[3*2];
#ifdef __cplusplus
inline mat32(int d=1) { for(int i=0;i<3*2;i++) data[i]=0; if(d!=0) for(int i=0;i<2;i++) m(i,i)=d; }
inline float m(int i, int j) const { return data[j*2+i]; }
inline float& m(int i, int j) { return data[j*2+i]; }
inline float operator()(int i, int j) const { return m(i,j); }
inline float& operator()(int i, int j) { return m(i,j); }
inline operator bool() const { for (int i=0; i<3*2; i++) if(data[i]!=0) return true; return false; }
#endif
};
/*!
Sample \a pattern from \a image.
\a warp is the transformation to apply to \a image when sampling the \a pattern.
*/
void SamplePattern(ubyte* image, int stride, mat32 warp, ubyte* pattern, int size);
/*!
Track \a pattern in \a image.
This template matcher computes the
\link http://en.wikipedia.org/wiki/Sum_of_absolute_differences Sum of Absolute Differences (SAD) \endlink
for each integer pixel position in the search region and then iteratively
refine subpixel position using a square search.
A similar method is used for motion estimation in video encoders.
\a reference is the pattern to track.
\a warped is a warped version of reference for fast unsampled integer search.
Best is to directly extract an already warped pattern from previous frame.
The \a size of the patterns should be aligned to 16.
\a image is a reference to the region to search.
\a stride is size of \a image lines.
On input, \a warp is the predicted affine transformation (e.g from previous frame)
On return, \a warp is the affine transformation which best match the reference \a pattern
\a areaPenalty and conditionPenalty control the regularization and need to be tweaked depending on the motion.
Setting them to 0 will allow any transformation (including unrealistic distortions and scaling).
Good values are between 0-32. 16 can be used as a realistic default.
areaPenalty control scaling (decrease to allow pull/zoom, increase to allow only 2D rotation).
a large conditionPenalty avoid a large ratio between the largest and smallest axices.
It need to be decreased for non-2D rotation (when pattern appears to scale along an axis).
\return Pearson product-moment correlation coefficient between reference and matched pattern.
This measure of the linear dependence between the patterns
ranges from 1 (negative correlation) to 1 (positive correlation).
A value of 0 implies that there is no linear correlation between the variables.
\note To track affine features:
- Sample reference pattern using estimated (e.g previous frame) warp.
-
\note \a stride allow you to reference your search region instead of copying.
\note For a 16x speedup, compile this tracker with SSE2 support.
*/
float Track(ubyte* reference, ubyte* warped, int size, ubyte* image, int stride, int width, int height, mat32* warp,
float areaPenalty, float conditionPenalty);
#ifdef __cplusplus
} // namespace libmv
#endif
#endif // LIBMV_TRACKING_SAD_H_

@ -0,0 +1,135 @@
// 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/tracking/trklt_region_tracker.h"
#include "libmv/logging/logging.h"
#include "libmv/numeric/numeric.h"
#include "libmv/image/image.h"
#include "libmv/image/convolve.h"
#include "libmv/image/sample.h"
namespace libmv {
// Computes U and e from the Ud = e equation (number 14) from the paper.
static void ComputeTrackingEquation(const Array3Df &image_and_gradient1,
const Array3Df &image_and_gradient2,
double x1, double y1,
double x2, double y2,
int half_width,
double lambda,
Mat2f *U,
Vec2f *e) {
Mat2f A, B, C, D;
A = B = C = D = Mat2f::Zero();
Vec2f R, S, V, W;
R = S = V = W = Vec2f::Zero();
for (int r = -half_width; r <= half_width; ++r) {
for (int c = -half_width; c <= half_width; ++c) {
float xx1 = x1 + c;
float yy1 = y1 + r;
float xx2 = x2 + c;
float yy2 = y2 + r;
float I = SampleLinear(image_and_gradient1, yy1, xx1, 0);
float J = SampleLinear(image_and_gradient2, yy2, xx2, 0);
Vec2f gI, gJ;
gI << SampleLinear(image_and_gradient1, yy1, xx1, 1),
SampleLinear(image_and_gradient1, yy1, xx1, 2);
gJ << SampleLinear(image_and_gradient2, yy2, xx2, 1),
SampleLinear(image_and_gradient2, yy2, xx2, 2);
// Equation 15 from the paper.
A += gI * gI.transpose();
B += gI * gJ.transpose();
C += gJ * gJ.transpose();
R += I * gI;
S += J * gI;
V += I * gJ;
W += J * gJ;
}
}
// In the paper they show a D matrix, but it is just B transpose, so use that
// instead of explicitly computing D.
Mat2f Di = B.transpose().inverse();
// Equation 14 from the paper.
*U = A*Di*C + lambda*Di*C - 0.5*B;
*e = (A + lambda*Mat2f::Identity())*Di*(V - W) + 0.5*(S - R);
}
bool TrkltRegionTracker::Track(const FloatImage &image1,
const FloatImage &image2,
double x1, double y1,
double *x2, double *y2) const {
Array3Df image_and_gradient1;
Array3Df image_and_gradient2;
BlurredImageAndDerivativesChannels(image1, sigma, &image_and_gradient1);
BlurredImageAndDerivativesChannels(image2, sigma, &image_and_gradient2);
int i;
Vec2f d = Vec2f::Zero();
for (i = 0; i < max_iterations; ++i) {
// Compute gradient matrix and error vector.
Mat2f U;
Vec2f e;
ComputeTrackingEquation(image_and_gradient1,
image_and_gradient2,
x1, y1,
*x2, *y2,
half_window_size,
lambda,
&U, &e);
// Solve the linear system for the best update to x2 and y2.
d = U.lu().solve(e);
// Update the position with the solved displacement.
*x2 += d[0];
*y2 += d[1];
// Check for the quality of the solution, but not until having already
// updated the position with our best estimate. The reason to do the update
// anyway is that the user already knows the position is bad, so we may as
// well try our best.
float determinant = U.determinant();
if (fabs(determinant) < min_determinant) {
// The determinant, which indicates the trackiness of the point, is too
// small, so fail out.
LG << "Determinant " << determinant << " is too small; failing tracking.";
return false;
}
// If the update is small, then we probably found the target.
if (d.squaredNorm() < min_update_squared_distance) {
LG << "Successful track in " << i << " iterations.";
return true;
}
}
// Getting here means we hit max iterations, so tracking failed.
LG << "Too many iterations; max is set to " << max_iterations << ".";
return false;
}
} // namespace libmv

@ -0,0 +1,65 @@
// 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_REGION_TRACKING_TRKLT_REGION_TRACKER_H_
#define LIBMV_REGION_TRACKING_TRKLT_REGION_TRACKER_H_
#include "libmv/image/image.h"
#include "libmv/tracking/region_tracker.h"
namespace libmv {
// An improved KLT algorithm that enforces that the tracking is time-reversible
// [1]. This is not the same as the "symmetric" KLT that is sometimes used.
// Anecdotally, this tracks much more consistently than vanilla KLT.
//
// [1] H. Wu, R. Chellappa, and A. Sankaranarayanan and S. Kevin Zhou. Robust
// visual tracking using the time-reversibility constraint. International
// Conference on Computer Vision (ICCV), Rio de Janeiro, October 2007.
//
struct TrkltRegionTracker : public RegionTracker {
TrkltRegionTracker()
: half_window_size(4),
max_iterations(100),
min_determinant(1e-6),
min_update_squared_distance(1e-6),
sigma(0.9),
lambda(0.05) {}
virtual ~TrkltRegionTracker() {}
// Tracker interface.
virtual bool Track(const FloatImage &image1,
const FloatImage &image2,
double x1, double y1,
double *x2, double *y2) const;
// No point in creating getters or setters.
int half_window_size;
int max_iterations;
double min_determinant;
double min_update_squared_distance;
double sigma;
double lambda;
};
} // namespace libmv
#endif // LIBMV_REGION_TRACKING_TRKLT_REGION_TRACKER_H_

4
extern/libmv/mkfiles.sh vendored Executable file

@ -0,0 +1,4 @@
#!/bin/sh
find ./libmv/ -type f | sed -r 's/^\.\///' > files.txt
find ./third_party/ -type f | sed -r 's/^\.\///' >> files.txt

122
extern/libmv/patches/bundle_tweaks.patch vendored Normal file

@ -0,0 +1,122 @@
diff --git a/src/libmv/logging/logging.h b/src/libmv/logging/logging.h
index 067da52..af86c4b 100644
--- a/src/libmv/logging/logging.h
+++ b/src/libmv/logging/logging.h
@@ -21,7 +21,7 @@
#ifndef LIBMV_LOGGING_LOGGING_H
#define LIBMV_LOGGING_LOGGING_H
-#include "third_party/glog/src/glog/logging.h"
+#include "glog/logging.h"
#define LG LOG(INFO)
#define V0 LOG(INFO)
diff --git a/src/third_party/glog/src/glog/logging.h b/src/third_party/glog/src/glog/logging.h
index 57615ef..a58d478 100644
--- a/src/third_party/glog/src/glog/logging.h
+++ b/src/third_party/glog/src/glog/logging.h
@@ -33,6 +33,7 @@
// Pretty much everybody needs to #include this file so that they can
// log various happenings.
//
+
#ifndef _LOGGING_H_
#define _LOGGING_H_
diff --git a/src/third_party/glog/src/logging.cc b/src/third_party/glog/src/logging.cc
index 868898f..1bb3867 100644
--- a/src/third_party/glog/src/logging.cc
+++ b/src/third_party/glog/src/logging.cc
@@ -58,8 +58,8 @@
#include <errno.h> // for errno
#include <sstream>
#include "base/commandlineflags.h" // to get the program name
-#include "glog/logging.h"
-#include "glog/raw_logging.h"
+#include <glog/logging.h>
+#include <glog/raw_logging.h>
#include "base/googleinit.h"
#ifdef HAVE_STACKTRACE
@@ -1232,7 +1232,9 @@ void LogMessage::RecordCrashReason(
}
static void logging_fail() {
-#if defined(_DEBUG) && defined(_MSC_VER)
+// #if defined(_DEBUG) && defined(_MSC_VER)
+// doesn't work for my laptop (sergey)
+#if 0
// When debugging on windows, avoid the obnoxious dialog and make
// it possible to continue past a LOG(FATAL) in the debugger
_asm int 3
diff --git a/src/third_party/glog/src/raw_logging.cc b/src/third_party/glog/src/raw_logging.cc
index 50c6a71..b179a1e 100644
--- a/src/third_party/glog/src/raw_logging.cc
+++ b/src/third_party/glog/src/raw_logging.cc
@@ -42,8 +42,8 @@
#include <fcntl.h> // for open()
#include <time.h>
#include "config.h"
-#include "glog/logging.h" // To pick up flag settings etc.
-#include "glog/raw_logging.h"
+#include <glog/logging.h> // To pick up flag settings etc.
+#include <glog/raw_logging.h>
#include "base/commandlineflags.h"
#ifdef HAVE_STACKTRACE
diff --git a/src/third_party/glog/src/utilities.h b/src/third_party/glog/src/utilities.h
index ee54f94..2d4e99e 100644
--- a/src/third_party/glog/src/utilities.h
+++ b/src/third_party/glog/src/utilities.h
@@ -79,7 +79,7 @@
#endif
#include "config.h"
-#include "glog/logging.h"
+#include <glog/logging.h>
// There are three different ways we can try to get the stack trace:
//
diff --git a/src/third_party/glog/src/vlog_is_on.cc b/src/third_party/glog/src/vlog_is_on.cc
index ee0e412..ed88514 100644
--- a/src/third_party/glog/src/vlog_is_on.cc
+++ b/src/third_party/glog/src/vlog_is_on.cc
@@ -40,8 +40,8 @@
#include <cstdio>
#include <string>
#include "base/commandlineflags.h"
-#include "glog/logging.h"
-#include "glog/raw_logging.h"
+#include <glog/logging.h>
+#include <glog/raw_logging.h>
#include "base/googleinit.h"
// glog doesn't have annotation
diff --git a/src/third_party/glog/src/windows/config.h b/src/third_party/glog/src/windows/config.h
index 114762e..682a1b9 100755
--- a/src/third_party/glog/src/windows/config.h
+++ b/src/third_party/glog/src/windows/config.h
@@ -19,7 +19,7 @@
#undef HAVE_LIBUNWIND_H
/* define if you have google gflags library */
-#undef HAVE_LIB_GFLAGS
+#define HAVE_LIB_GFLAGS 1
/* define if you have libunwind */
#undef HAVE_LIB_UNWIND
diff --git a/src/third_party/glog/src/windows/glog/logging.h b/src/third_party/glog/src/windows/glog/logging.h
index 7a6df74..de51586 100755
--- a/src/third_party/glog/src/windows/glog/logging.h
+++ b/src/third_party/glog/src/windows/glog/logging.h
@@ -82,8 +82,8 @@
#include <inttypes.h> // a third place for uint16_t or u_int16_t
#endif
-#if 0
-#include <gflags/gflags.h>
+#if 1
+#include "third_party/gflags/gflags.h"
#endif
namespace google {

13
extern/libmv/patches/config_mac.patch vendored Normal file

@ -0,0 +1,13 @@
diff --git a/src/third_party/glog/src/config_mac.h b/src/third_party/glog/src/config_mac.h
index a45575b..5f953d1 100644
--- a/src/third_party/glog/src/config_mac.h
+++ b/src/third_party/glog/src/config_mac.h
@@ -131,7 +131,7 @@
#define PACKAGE_VERSION "0.3.1"
/* How to access the PC from a struct ucontext */
-#define PC_FROM_UCONTEXT uc_mcontext->__ss.__rip
+#undef PC_FROM_UCONTEXT
/* Define to necessary symbol if this constant uses a non-standard name on
your system. */

181
extern/libmv/patches/detect.patch vendored Normal file

@ -0,0 +1,181 @@
diff --git a/src/libmv/simple_pipeline/detect.cc b/src/libmv/simple_pipeline/detect.cc
index 6fc0cdd..8ac42ab 100644
--- a/src/libmv/simple_pipeline/detect.cc
+++ b/src/libmv/simple_pipeline/detect.cc
@@ -23,15 +23,89 @@
****************************************************************************/
#include "libmv/simple_pipeline/detect.h"
+#include <third_party/fast/fast.h>
#include <stdlib.h>
-#include <string.h>
+#include <memory.h>
+
+#ifdef __SSE2__
+#include <emmintrin.h>
+#endif
namespace libmv {
typedef unsigned int uint;
+int featurecmp(const void *a_v, const void *b_v)
+{
+ Feature *a = (Feature*)a_v;
+ Feature *b = (Feature*)b_v;
+
+ return b->score - a->score;
+}
+
+std::vector<Feature> DetectFAST(const unsigned char* data, int width, int height, int stride,
+ int min_trackness, int min_distance) {
+ std::vector<Feature> features;
+ // TODO(MatthiasF): Support targetting a feature count (binary search trackness)
+ int num_features;
+ xy* all = fast9_detect(data, width, height,
+ stride, min_trackness, &num_features);
+ if(num_features == 0) {
+ free(all);
+ return features;
+ }
+ int* scores = fast9_score(data, stride, all, num_features, min_trackness);
+ // TODO: merge with close feature suppression
+ xy* nonmax = nonmax_suppression(all, scores, num_features, &num_features);
+ free(all);
+ // Remove too close features
+ // TODO(MatthiasF): A resolution independent parameter would be better than distance
+ // e.g. a coefficient going from 0 (no minimal distance) to 1 (optimal circle packing)
+ // FIXME(MatthiasF): this method will not necessarily give all maximum markers
+ if(num_features) {
+ Feature *all_features = new Feature[num_features];
+
+ for(int i = 0; i < num_features; ++i) {
+ Feature a = { nonmax[i].x, nonmax[i].y, scores[i], 0 };
+ all_features[i] = a;
+ }
+
+ qsort((void *)all_features, num_features, sizeof(Feature), featurecmp);
+
+ features.reserve(num_features);
+
+ int prev_score = all_features[0].score;
+ for(int i = 0; i < num_features; ++i) {
+ bool ok = true;
+ Feature a = all_features[i];
+ if(a.score>prev_score)
+ abort();
+ prev_score = a.score;
+
+ // compare each feature against filtered set
+ for(int j = 0; j < features.size(); j++) {
+ Feature& b = features[j];
+ if ( (a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y) < min_distance*min_distance ) {
+ // already a nearby feature
+ ok = false;
+ break;
+ }
+ }
+
+ if(ok) {
+ // add the new feature
+ features.push_back(a);
+ }
+ }
+
+ delete [] all_features;
+ }
+ free(scores);
+ free(nonmax);
+ return features;
+}
+
#ifdef __SSE2__
-#include <emmintrin.h>
static uint SAD(const ubyte* imageA, const ubyte* imageB, int strideA, int strideB) {
__m128i a = _mm_setzero_si128();
for(int i = 0; i < 16; i++) {
@@ -52,7 +126,7 @@ static uint SAD(const ubyte* imageA, const ubyte* imageB, int strideA, int strid
}
#endif
-void Detect(ubyte* image, int stride, int width, int height, Feature* detected, int* count, int distance, ubyte* pattern) {
+void DetectMORAVEC(ubyte* image, int stride, int width, int height, Feature* detected, int* count, int distance, ubyte* pattern) {
unsigned short histogram[256];
memset(histogram,0,sizeof(histogram));
ubyte* scores = new ubyte[width*height];
diff --git a/src/libmv/simple_pipeline/detect.h b/src/libmv/simple_pipeline/detect.h
index 23b239b..bbe7aed 100644
--- a/src/libmv/simple_pipeline/detect.h
+++ b/src/libmv/simple_pipeline/detect.h
@@ -25,27 +25,52 @@
#ifndef LIBMV_SIMPLE_PIPELINE_DETECT_H_
#define LIBMV_SIMPLE_PIPELINE_DETECT_H_
-#ifdef __cplusplus
+#include <vector>
+
namespace libmv {
-#endif
typedef unsigned char ubyte;
/*!
- \a Feature is the 2D location of a detected feature in an image.
+ A Feature is the 2D location of a detected feature in an image.
- \a x, \a y is the position of the center in pixels (from image top-left).
- \a score is an estimate of how well the pattern will be tracked.
- \a size can be used as an initial size to track the pattern.
+ \a x, \a y is the position of the feature in pixels from the top left corner.
+ \a score is an estimate of how well the feature will be tracked.
+ \a size can be used as an initial pattern size to track the feature.
\sa Detect
*/
struct Feature {
+ /// Position in pixels (from top-left corner)
+ /// \note libmv might eventually support subpixel precision.
float x, y;
+ /// Trackness of the feature
float score;
+ /// Size of the feature in pixels
float size;
};
- //radius for non maximal suppression
+
+/*!
+ Detect features in an image.
+
+ You need to input a single channel 8-bit image using pointer to image \a data,
+ \a width, \a height and \a stride (i.e bytes per line).
+
+ You can tweak the count of detected features using \a min_trackness, which is
+ the minimum score to add a feature, and \a min_distance which is the minimal
+ distance accepted between two featuress.
+
+ \note You can binary search over \a min_trackness to get a given feature count.
+
+ \note a way to get an uniform distribution of a given feature count is:
+ \a min_distance = \a width * \a height / desired_feature_count ^ 2
+
+ \return All detected feartures matching given parameters
+*/
+std::vector<Feature> DetectFAST(const unsigned char* data, int width, int height,
+ int stride, int min_trackness = 128,
+ int min_distance = 120);
+
/*!
Detect features in an image.
@@ -63,10 +88,8 @@ struct Feature {
\note \a You can crop the image (to avoid detecting markers near the borders) without copying:
image += marginY*stride+marginX, width -= 2*marginX, height -= 2*marginY;
*/
-void Detect(ubyte* image, int stride, int width, int height, Feature* detected, int* count, int distance /*=32*/, ubyte* pattern /*=0*/);
+void DetectMORAVEC(ubyte* image, int stride, int width, int height, Feature* detected, int* count, int distance /*=32*/, ubyte* pattern /*=0*/);
-#ifdef __cplusplus
}
-#endif
#endif

24
extern/libmv/patches/fast.patch vendored Normal file

@ -0,0 +1,24 @@
diff --git a/src/third_party/fast/fast.h b/src/third_party/fast/fast.h
index 2b3825a..06fa90e 100644
--- a/src/third_party/fast/fast.h
+++ b/src/third_party/fast/fast.h
@@ -1,6 +1,10 @@
#ifndef FAST_H
#define FAST_H
+#ifdef __cplusplus
+extern "C" {
+#endif
+
typedef struct { int x, y; } xy;
typedef unsigned char byte;
@@ -28,4 +32,8 @@ xy* fast12_detect_nonmax(const byte* im, int xsize, int ysize, int stride, int b
xy* nonmax_suppression(const xy* corners, const int* scores, int num_corners, int* ret_num_nonmax);
+#ifdef __cplusplus
+}
+#endif
+
#endif

@ -0,0 +1,21 @@
diff --git a/src/libmv/numeric/function_derivative.h b/src/libmv/numeric/function_derivative.h
index 0075d23..d7bc437 100644
--- a/src/libmv/numeric/function_derivative.h
+++ b/src/libmv/numeric/function_derivative.h
@@ -24,6 +24,7 @@
#include <cmath>
#include "libmv/numeric/numeric.h"
+#include "libmv/logging/logging.h"
namespace libmv {
@@ -97,7 +98,7 @@ bool CheckJacobian(const Function &f, const typename Function::XMatrixType &x) {
typename NumericJacobian<Function>::JMatrixType J_numeric = j_numeric(x);
typename NumericJacobian<Function>::JMatrixType J_analytic = j_analytic(x);
- //LG << J_numeric - J_analytic;
+ LG << J_numeric - J_analytic;
return true;
}

@ -0,0 +1,21 @@
diff --git a/src/libmv/simple_pipeline/camera_intrinsics.cc b/src/libmv/simple_pipeline/camera_intrinsics.cc
index 4e88e1f..f9888ff 100644
--- a/src/libmv/simple_pipeline/camera_intrinsics.cc
+++ b/src/libmv/simple_pipeline/camera_intrinsics.cc
@@ -160,9 +160,13 @@ void CameraIntrinsics::ComputeLookupGrid(Offset* grid, int width, int height) {
if( iy < 0 ) { iy = 0, fy = 0; }
if( ix >= width-2 ) ix = width-2;
if( iy >= height-2 ) iy = height-2;
- //assert( ix-x > -128 && ix-x < 128 && iy-y > -128 && iy-y < 128 );
- Offset offset = { ix-x, iy-y, fx, fy };
- grid[y*width+x] = offset;
+ if ( ix-x > -128 && ix-x < 128 && iy-y > -128 && iy-y < 128 ) {
+ Offset offset = { ix-x, iy-y, fx, fy };
+ grid[y*width+x] = offset;
+ } else {
+ Offset offset = { 0, 0, 0, 0 };
+ grid[y*width+x] = offset;
+ }
}
}
}

@ -0,0 +1,71 @@
diff --git a/src/libmv/numeric/levenberg_marquardt.h b/src/libmv/numeric/levenberg_marquardt.h
index 6a54f66..4473b72 100644
--- a/src/libmv/numeric/levenberg_marquardt.h
+++ b/src/libmv/numeric/levenberg_marquardt.h
@@ -33,6 +33,7 @@
#include "libmv/numeric/numeric.h"
#include "libmv/numeric/function_derivative.h"
+#include "libmv/logging/logging.h"
namespace libmv {
@@ -123,26 +124,40 @@ class LevenbergMarquardt {
Parameters dx, x_new;
int i;
for (i = 0; results.status == RUNNING && i < params.max_iterations; ++i) {
- if (dx.norm() <= params.relative_step_threshold * x.norm()) {
+ VLOG(1) << "iteration: " << i;
+ VLOG(1) << "||f(x)||: " << f_(x).norm();
+ VLOG(1) << "max(g): " << g.array().abs().maxCoeff();
+ VLOG(1) << "u: " << u;
+ VLOG(1) << "v: " << v;
+
+ AMatrixType A_augmented = A + u*AMatrixType::Identity(J.cols(), J.cols());
+ Solver solver(A_augmented);
+ dx = solver.solve(g);
+ bool solved = (A_augmented * dx).isApprox(g);
+ if (!solved) {
+ LOG(ERROR) << "Failed to solve";
+ }
+ if (solved && dx.norm() <= params.relative_step_threshold * x.norm()) {
results.status = RELATIVE_STEP_SIZE_TOO_SMALL;
break;
- }
- x_new = x + dx;
- // Rho is the ratio of the actual reduction in error to the reduction
- // in error that would be obtained if the problem was linear.
- // See [1] for details.
- Scalar rho((error.squaredNorm() - f_(x_new).squaredNorm())
- / dx.dot(u*dx + g));
- if (rho > 0) {
- // Accept the Gauss-Newton step because the linear model fits well.
- x = x_new;
- results.status = Update(x, params, &J, &A, &error, &g);
- Scalar tmp = Scalar(2*rho-1);
- u = u*std::max(1/3., 1 - (tmp*tmp*tmp));
- v = 2;
- continue;
- }
-
+ }
+ if (solved) {
+ x_new = x + dx;
+ // Rho is the ratio of the actual reduction in error to the reduction
+ // in error that would be obtained if the problem was linear.
+ // See [1] for details.
+ Scalar rho((error.squaredNorm() - f_(x_new).squaredNorm())
+ / dx.dot(u*dx + g));
+ if (rho > 0) {
+ // Accept the Gauss-Newton step because the linear model fits well.
+ x = x_new;
+ results.status = Update(x, params, &J, &A, &error, &g);
+ Scalar tmp = Scalar(2*rho-1);
+ u = u*std::max(1/3., 1 - (tmp*tmp*tmp));
+ v = 2;
+ continue;
+ }
+ }
// Reject the update because either the normal equations failed to solve
// or the local linear model was not good (rho < 0). Instead, increase u
// to move closer to gradient descent.

13
extern/libmv/patches/mingw.patch vendored Normal file

@ -0,0 +1,13 @@
diff --git a/src/libmv/numeric/numeric.h b/src/libmv/numeric/numeric.h
index f39d126..21e0f06 100644
--- a/src/libmv/numeric/numeric.h
+++ b/src/libmv/numeric/numeric.h
@@ -40,7 +40,7 @@
}
#endif //_WIN32 || __APPLE__
-#if _WIN32
+#if (defined(WIN32) || defined(WIN64)) && !defined(__MINGW32__)
inline long lround(double d) {
return (long)(d>0 ? d+0.5 : ceil(d-0.5));
}

12
extern/libmv/patches/msvc2010.patch vendored Normal file

@ -0,0 +1,12 @@
diff --git a/src/libmv/simple_pipeline/tracks.cc b/src/libmv/simple_pipeline/tracks.cc
index 0e2a1b6..3fb8ddb 100644
--- a/src/libmv/simple_pipeline/tracks.cc
+++ b/src/libmv/simple_pipeline/tracks.cc
@@ -20,6 +20,7 @@
#include <algorithm>
#include <vector>
+#include <iterator>
#include "libmv/numeric/numeric.h"
#include "libmv/simple_pipeline/tracks.h"

182
extern/libmv/patches/overscan.patch vendored Normal file

@ -0,0 +1,182 @@
diff --git a/src/libmv/simple_pipeline/camera_intrinsics.cc b/src/libmv/simple_pipeline/camera_intrinsics.cc
index 110a16d..366129d 100644
--- a/src/libmv/simple_pipeline/camera_intrinsics.cc
+++ b/src/libmv/simple_pipeline/camera_intrinsics.cc
@@ -31,6 +31,7 @@ struct Offset {
struct Grid {
struct Offset *offset;
int width, height;
+ double overscan;
};
static struct Grid *copyGrid(struct Grid *from)
@@ -42,6 +43,7 @@ static struct Grid *copyGrid(struct Grid *from)
to->width = from->width;
to->height = from->height;
+ to->overscan = from->overscan;
to->offset = new Offset[to->width*to->height];
memcpy(to->offset, from->offset, sizeof(struct Offset)*to->width*to->height);
@@ -184,17 +186,19 @@ void CameraIntrinsics::InvertIntrinsics(double image_x,
// TODO(MatthiasF): downsample lookup
template<typename WarpFunction>
-void CameraIntrinsics::ComputeLookupGrid(Grid* grid, int width, int height) {
- double aspx = (double)width / image_width_;
- double aspy = (double)height / image_height_;
+void CameraIntrinsics::ComputeLookupGrid(Grid* grid, int width, int height, double overscan) {
+ double w = (double)width / (1 + overscan);
+ double h = (double)height / (1 + overscan);
+ double aspx = (double)w / image_width_;
+ double aspy = (double)h / image_height_;
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
- double src_x = x / aspx, src_y = y / aspy;
+ double src_x = (x - 0.5 * overscan * w) / aspx, src_y = (y - 0.5 * overscan * h) / aspy;
double warp_x, warp_y;
WarpFunction(this,src_x,src_y,&warp_x,&warp_y);
- warp_x = warp_x*aspx;
- warp_y = warp_y*aspy;
+ warp_x = warp_x*aspx + 0.5 * overscan * w;
+ warp_y = warp_y*aspy + 0.5 * overscan * h;
int ix = int(warp_x), iy = int(warp_y);
int fx = round((warp_x-ix)*256), fy = round((warp_y-iy)*256);
if(fx == 256) { fx=0; ix++; }
@@ -264,10 +268,10 @@ struct InvertIntrinsicsFunction {
}
};
-void CameraIntrinsics::CheckDistortLookupGrid(int width, int height)
+void CameraIntrinsics::CheckDistortLookupGrid(int width, int height, double overscan)
{
if(distort_) {
- if(distort_->width != width || distort_->height != height) {
+ if(distort_->width != width || distort_->height != height || distort_->overscan != overscan) {
delete [] distort_->offset;
distort_->offset = NULL;
}
@@ -278,17 +282,18 @@ void CameraIntrinsics::CheckDistortLookupGrid(int width, int height)
if(!distort_->offset) {
distort_->offset = new Offset[width*height];
- ComputeLookupGrid<InvertIntrinsicsFunction>(distort_,width,height);
+ ComputeLookupGrid<InvertIntrinsicsFunction>(distort_,width,height,overscan);
}
distort_->width = width;
distort_->height = height;
+ distort_->overscan = overscan;
}
-void CameraIntrinsics::CheckUndistortLookupGrid(int width, int height)
+void CameraIntrinsics::CheckUndistortLookupGrid(int width, int height, double overscan)
{
if(undistort_) {
- if(undistort_->width != width || undistort_->height != height) {
+ if(undistort_->width != width || undistort_->height != height || undistort_->overscan != overscan) {
delete [] undistort_->offset;
undistort_->offset = NULL;
}
@@ -299,15 +304,16 @@ void CameraIntrinsics::CheckUndistortLookupGrid(int width, int height)
if(!undistort_->offset) {
undistort_->offset = new Offset[width*height];
- ComputeLookupGrid<ApplyIntrinsicsFunction>(undistort_,width,height);
+ ComputeLookupGrid<ApplyIntrinsicsFunction>(undistort_,width,height,overscan);
}
undistort_->width = width;
undistort_->height = height;
+ undistort_->overscan = overscan;
}
-void CameraIntrinsics::Distort(const float* src, float* dst, int width, int height, int channels) {
- CheckDistortLookupGrid(width, height);
+void CameraIntrinsics::Distort(const float* src, float* dst, int width, int height, double overscan, int channels) {
+ CheckDistortLookupGrid(width, height, overscan);
if(channels==1) Warp<float,1>(distort_,src,dst,width,height);
else if(channels==2) Warp<float,2>(distort_,src,dst,width,height);
else if(channels==3) Warp<float,3>(distort_,src,dst,width,height);
@@ -315,8 +321,8 @@ void CameraIntrinsics::Distort(const float* src, float* dst, int width, int heig
//else assert("channels must be between 1 and 4");
}
-void CameraIntrinsics::Distort(const unsigned char* src, unsigned char* dst, int width, int height, int channels) {
- CheckDistortLookupGrid(width, height);
+void CameraIntrinsics::Distort(const unsigned char* src, unsigned char* dst, int width, int height, double overscan, int channels) {
+ CheckDistortLookupGrid(width, height, overscan);
if(channels==1) Warp<unsigned char,1>(distort_,src,dst,width,height);
else if(channels==2) Warp<unsigned char,2>(distort_,src,dst,width,height);
else if(channels==3) Warp<unsigned char,3>(distort_,src,dst,width,height);
@@ -324,8 +330,8 @@ void CameraIntrinsics::Distort(const unsigned char* src, unsigned char* dst, int
//else assert("channels must be between 1 and 4");
}
-void CameraIntrinsics::Undistort(const float* src, float* dst, int width, int height, int channels) {
- CheckUndistortLookupGrid(width, height);
+void CameraIntrinsics::Undistort(const float* src, float* dst, int width, int height, double overscan, int channels) {
+ CheckUndistortLookupGrid(width, height, overscan);
if(channels==1) Warp<float,1>(undistort_,src,dst,width,height);
else if(channels==2) Warp<float,2>(undistort_,src,dst,width,height);
else if(channels==3) Warp<float,3>(undistort_,src,dst,width,height);
@@ -333,8 +339,8 @@ void CameraIntrinsics::Undistort(const float* src, float* dst, int width, int he
//else assert("channels must be between 1 and 4");
}
-void CameraIntrinsics::Undistort(const unsigned char* src, unsigned char* dst, int width, int height, int channels) {
- CheckUndistortLookupGrid(width, height);
+void CameraIntrinsics::Undistort(const unsigned char* src, unsigned char* dst, int width, int height, double overscan, int channels) {
+ CheckUndistortLookupGrid(width, height, overscan);
if(channels==1) Warp<unsigned char,1>(undistort_,src,dst,width,height);
else if(channels==2) Warp<unsigned char,2>(undistort_,src,dst,width,height);
else if(channels==3) Warp<unsigned char,3>(undistort_,src,dst,width,height);
diff --git a/src/libmv/simple_pipeline/camera_intrinsics.h b/src/libmv/simple_pipeline/camera_intrinsics.h
index f525571..f4bf903 100644
--- a/src/libmv/simple_pipeline/camera_intrinsics.h
+++ b/src/libmv/simple_pipeline/camera_intrinsics.h
@@ -91,7 +91,7 @@ class CameraIntrinsics {
\note This is the reference implementation using floating point images.
*/
void Distort(const float* src, float* dst,
- int width, int height, int channels);
+ int width, int height, double overscan, int channels);
/*!
Distort an image using the current camera instrinsics
@@ -101,7 +101,7 @@ class CameraIntrinsics {
\note This version is much faster.
*/
void Distort(const unsigned char* src, unsigned char* dst,
- int width, int height, int channels);
+ int width, int height, double overscan, int channels);
/*!
Undistort an image using the current camera instrinsics
@@ -111,7 +111,7 @@ class CameraIntrinsics {
\note This is the reference implementation using floating point images.
*/
void Undistort(const float* src, float* dst,
- int width, int height, int channels);
+ int width, int height, double overscan, int channels);
/*!
Undistort an image using the current camera instrinsics
@@ -121,12 +121,12 @@ class CameraIntrinsics {
\note This version is much faster.
*/
void Undistort(const unsigned char* src, unsigned char* dst,
- int width, int height, int channels);
+ int width, int height, double overscan, int channels);
private:
- template<typename WarpFunction> void ComputeLookupGrid(struct Grid* grid, int width, int height);
- void CheckUndistortLookupGrid(int width, int height);
- void CheckDistortLookupGrid(int width, int height);
+ template<typename WarpFunction> void ComputeLookupGrid(struct Grid* grid, int width, int height, double overscan);
+ void CheckUndistortLookupGrid(int width, int height, double overscan);
+ void CheckDistortLookupGrid(int width, int height, double overscan);
void FreeLookupGrid();
// The traditional intrinsics matrix from x = K[R|t]X.

@ -0,0 +1,261 @@
diff --git a/src/libmv/simple_pipeline/camera_intrinsics.cc b/src/libmv/simple_pipeline/camera_intrinsics.cc
index f9888ff..110a16d 100644
--- a/src/libmv/simple_pipeline/camera_intrinsics.cc
+++ b/src/libmv/simple_pipeline/camera_intrinsics.cc
@@ -23,7 +23,32 @@
namespace libmv {
-struct Offset { signed char ix,iy; unsigned char fx,fy; };
+struct Offset {
+ signed char ix, iy;
+ unsigned char fx,fy;
+};
+
+struct Grid {
+ struct Offset *offset;
+ int width, height;
+};
+
+static struct Grid *copyGrid(struct Grid *from)
+{
+ struct Grid *to = NULL;
+
+ if (from) {
+ to = new Grid;
+
+ to->width = from->width;
+ to->height = from->height;
+
+ to->offset = new Offset[to->width*to->height];
+ memcpy(to->offset, from->offset, sizeof(struct Offset)*to->width*to->height);
+ }
+
+ return to;
+}
CameraIntrinsics::CameraIntrinsics()
: K_(Mat3::Identity()),
@@ -37,9 +62,22 @@ CameraIntrinsics::CameraIntrinsics()
distort_(0),
undistort_(0) {}
+CameraIntrinsics::CameraIntrinsics(const CameraIntrinsics &from)
+ : K_(from.K_),
+ image_width_(from.image_width_),
+ image_height_(from.image_height_),
+ k1_(from.k1_),
+ k2_(from.k2_),
+ k3_(from.k3_),
+ p1_(from.p1_),
+ p2_(from.p2_)
+{
+ distort_ = copyGrid(from.distort_);
+ undistort_ = copyGrid(from.undistort_);
+}
+
CameraIntrinsics::~CameraIntrinsics() {
- if(distort_) delete[] distort_;
- if(undistort_) delete[] undistort_;
+ FreeLookupGrid();
}
/// Set the entire calibration matrix at once.
@@ -146,11 +184,17 @@ void CameraIntrinsics::InvertIntrinsics(double image_x,
// TODO(MatthiasF): downsample lookup
template<typename WarpFunction>
-void CameraIntrinsics::ComputeLookupGrid(Offset* grid, int width, int height) {
+void CameraIntrinsics::ComputeLookupGrid(Grid* grid, int width, int height) {
+ double aspx = (double)width / image_width_;
+ double aspy = (double)height / image_height_;
+
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
+ double src_x = x / aspx, src_y = y / aspy;
double warp_x, warp_y;
- WarpFunction(this,x,y,&warp_x,&warp_y);
+ WarpFunction(this,src_x,src_y,&warp_x,&warp_y);
+ warp_x = warp_x*aspx;
+ warp_y = warp_y*aspy;
int ix = int(warp_x), iy = int(warp_y);
int fx = round((warp_x-ix)*256), fy = round((warp_y-iy)*256);
if(fx == 256) { fx=0; ix++; }
@@ -162,10 +206,10 @@ void CameraIntrinsics::ComputeLookupGrid(Offset* grid, int width, int height) {
if( iy >= height-2 ) iy = height-2;
if ( ix-x > -128 && ix-x < 128 && iy-y > -128 && iy-y < 128 ) {
Offset offset = { ix-x, iy-y, fx, fy };
- grid[y*width+x] = offset;
+ grid->offset[y*width+x] = offset;
} else {
Offset offset = { 0, 0, 0, 0 };
- grid[y*width+x] = offset;
+ grid->offset[y*width+x] = offset;
}
}
}
@@ -173,11 +217,11 @@ void CameraIntrinsics::ComputeLookupGrid(Offset* grid, int width, int height) {
// TODO(MatthiasF): cubic B-Spline image sampling, bilinear lookup
template<typename T,int N>
-static void Warp(const Offset* grid, const T* src, T* dst,
+static void Warp(const Grid* grid, const T* src, T* dst,
int width, int height) {
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
- Offset offset = grid[y*width+x];
+ Offset offset = grid->offset[y*width+x];
const T* s = &src[((y+offset.iy)*width+(x+offset.ix))*N];
for (int i = 0; i < N; i++) {
dst[(y*width+x)*N+i] = ((s[ i] * (256-offset.fx) + s[ N+i] * offset.fx) * (256-offset.fy)
@@ -188,8 +232,17 @@ static void Warp(const Offset* grid, const T* src, T* dst,
}
void CameraIntrinsics::FreeLookupGrid() {
- if(distort_) delete distort_, distort_=0;
- if(undistort_) delete undistort_, undistort_=0;
+ if(distort_) {
+ delete distort_->offset;
+ delete distort_;
+ distort_ = NULL;
+ }
+
+ if(undistort_) {
+ delete undistort_->offset;
+ delete undistort_;
+ undistort_ = NULL;
+ }
}
// FIXME: C++ templates limitations makes thing complicated, but maybe there is a simpler method.
@@ -211,11 +264,50 @@ struct InvertIntrinsicsFunction {
}
};
-void CameraIntrinsics::Distort(const float* src, float* dst, int width, int height, int channels) {
- if(!distort_) {
- distort_ = new Offset[width*height];
- ComputeLookupGrid<InvertIntrinsicsFunction>(distort_,width,height);
+void CameraIntrinsics::CheckDistortLookupGrid(int width, int height)
+{
+ if(distort_) {
+ if(distort_->width != width || distort_->height != height) {
+ delete [] distort_->offset;
+ distort_->offset = NULL;
+ }
+ } else {
+ distort_ = new Grid;
+ distort_->offset = NULL;
+ }
+
+ if(!distort_->offset) {
+ distort_->offset = new Offset[width*height];
+ ComputeLookupGrid<InvertIntrinsicsFunction>(distort_,width,height);
}
+
+ distort_->width = width;
+ distort_->height = height;
+}
+
+void CameraIntrinsics::CheckUndistortLookupGrid(int width, int height)
+{
+ if(undistort_) {
+ if(undistort_->width != width || undistort_->height != height) {
+ delete [] undistort_->offset;
+ undistort_->offset = NULL;
+ }
+ } else {
+ undistort_ = new Grid;
+ undistort_->offset = NULL;
+ }
+
+ if(!undistort_->offset) {
+ undistort_->offset = new Offset[width*height];
+ ComputeLookupGrid<ApplyIntrinsicsFunction>(undistort_,width,height);
+ }
+
+ undistort_->width = width;
+ undistort_->height = height;
+}
+
+void CameraIntrinsics::Distort(const float* src, float* dst, int width, int height, int channels) {
+ CheckDistortLookupGrid(width, height);
if(channels==1) Warp<float,1>(distort_,src,dst,width,height);
else if(channels==2) Warp<float,2>(distort_,src,dst,width,height);
else if(channels==3) Warp<float,3>(distort_,src,dst,width,height);
@@ -224,10 +316,7 @@ void CameraIntrinsics::Distort(const float* src, float* dst, int width, int heig
}
void CameraIntrinsics::Distort(const unsigned char* src, unsigned char* dst, int width, int height, int channels) {
- if(!distort_) {
- distort_ = new Offset[width*height];
- ComputeLookupGrid<InvertIntrinsicsFunction>(distort_,width,height);
- }
+ CheckDistortLookupGrid(width, height);
if(channels==1) Warp<unsigned char,1>(distort_,src,dst,width,height);
else if(channels==2) Warp<unsigned char,2>(distort_,src,dst,width,height);
else if(channels==3) Warp<unsigned char,3>(distort_,src,dst,width,height);
@@ -236,10 +325,7 @@ void CameraIntrinsics::Distort(const unsigned char* src, unsigned char* dst, int
}
void CameraIntrinsics::Undistort(const float* src, float* dst, int width, int height, int channels) {
- if(!undistort_) {
- undistort_ = new Offset[width*height];
- ComputeLookupGrid<ApplyIntrinsicsFunction>(undistort_,width,height);
- }
+ CheckUndistortLookupGrid(width, height);
if(channels==1) Warp<float,1>(undistort_,src,dst,width,height);
else if(channels==2) Warp<float,2>(undistort_,src,dst,width,height);
else if(channels==3) Warp<float,3>(undistort_,src,dst,width,height);
@@ -248,10 +334,7 @@ void CameraIntrinsics::Undistort(const float* src, float* dst, int width, int he
}
void CameraIntrinsics::Undistort(const unsigned char* src, unsigned char* dst, int width, int height, int channels) {
- if(!undistort_) {
- undistort_ = new Offset[width*height];
- ComputeLookupGrid<ApplyIntrinsicsFunction>(undistort_,width,height);
- }
+ CheckUndistortLookupGrid(width, height);
if(channels==1) Warp<unsigned char,1>(undistort_,src,dst,width,height);
else if(channels==2) Warp<unsigned char,2>(undistort_,src,dst,width,height);
else if(channels==3) Warp<unsigned char,3>(undistort_,src,dst,width,height);
diff --git a/src/libmv/simple_pipeline/camera_intrinsics.h b/src/libmv/simple_pipeline/camera_intrinsics.h
index 29bc8a1..f525571 100644
--- a/src/libmv/simple_pipeline/camera_intrinsics.h
+++ b/src/libmv/simple_pipeline/camera_intrinsics.h
@@ -26,11 +26,12 @@ typedef Eigen::Matrix<double, 3, 3> Mat3;
namespace libmv {
-struct Offset;
+struct Grid;
class CameraIntrinsics {
public:
CameraIntrinsics();
+ CameraIntrinsics(const CameraIntrinsics &from);
~CameraIntrinsics();
const Mat3 &K() const { return K_; }
@@ -123,7 +124,9 @@ class CameraIntrinsics {
int width, int height, int channels);
private:
- template<typename WarpFunction> void ComputeLookupGrid(Offset* grid, int width, int height);
+ template<typename WarpFunction> void ComputeLookupGrid(struct Grid* grid, int width, int height);
+ void CheckUndistortLookupGrid(int width, int height);
+ void CheckDistortLookupGrid(int width, int height);
void FreeLookupGrid();
// The traditional intrinsics matrix from x = K[R|t]X.
@@ -140,8 +143,8 @@ class CameraIntrinsics {
// independent of image size.
double k1_, k2_, k3_, p1_, p2_;
- Offset* distort_;
- Offset* undistort_;
+ struct Grid *distort_;
+ struct Grid *undistort_;
};
} // namespace libmv

13
extern/libmv/patches/series vendored Normal file

@ -0,0 +1,13 @@
v3d_verbosity.patch
snrptinf_fix.patch
bundle_tweaks.patch
fast.patch
config_mac.patch
levenberg_marquardt.patch
function_derivative.patch
high_distortion_crash_fix.patch
mingw.patch
msvc2010.patch
scaled_distortion.patch
overscan.patch
detect.patch

15
extern/libmv/patches/snrptinf_fix.patch vendored Normal file

@ -0,0 +1,15 @@
diff --git a/src/libmv/simple_pipeline/pipeline.cc b/src/libmv/simple_pipeline/pipeline.cc
index 652d70c..25cd2c2 100644
--- a/src/libmv/simple_pipeline/pipeline.cc
+++ b/src/libmv/simple_pipeline/pipeline.cc
@@ -28,6 +28,10 @@
#include "libmv/simple_pipeline/tracks.h"
#include "libmv/simple_pipeline/camera_intrinsics.h"
+#ifdef _MSC_VER
+# define snprintf _snprintf
+#endif
+
namespace libmv {
void CompleteReconstruction(const Tracks &tracks,

@ -0,0 +1,12 @@
diff --git a/src/libmv/simple_pipeline/bundle.cc b/src/libmv/simple_pipeline/bundle.cc
index 310660d..f819603 100644
--- a/src/libmv/simple_pipeline/bundle.cc
+++ b/src/libmv/simple_pipeline/bundle.cc
@@ -141,7 +141,6 @@ void Bundle(const Tracks &tracks, Reconstruction *reconstruction) {
v3d_distortion.p2 = 0;
// Finally, run the bundle adjustment.
- V3D::optimizerVerbosenessLevel = 1;
double const inlierThreshold = 500000.0;
V3D::CommonInternalsMetricBundleOptimizer opt(V3D::FULL_BUNDLE_METRIC,
inlierThreshold,

30
extern/libmv/third_party/fast/LICENSE vendored Normal file

@ -0,0 +1,30 @@
Copyright (c) 2006, 2008 Edward Rosten
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
*Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
*Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
*Neither the name of the University of Cambridge nor the names of
its contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

31
extern/libmv/third_party/fast/README vendored Normal file

@ -0,0 +1,31 @@
FAST feature detectors in C Version 2.0
---------------------------------------
The files are valid C and C++ code, and have no special requirements for
compiling, and they do not depend on any libraries. Just compile them along with
the rest of your project.
To use the functions, #include "fast.h"
The corner detectors have the following prototype (where X is 9, 10, 11 or 12):
xy* fastX_detect_nonmax(const unsigned char * data, int xsize, int ysize, int stride, int threshold, int* numcorners)
Where xy is the following simple struct typedef:
typedef struct
{
int x, y;
} xy;
The image is passed in as a block of data and dimensions, and the list of
corners is returned as an array of xy structs, and an integer (numcorners)
with the number of corners returned. The data can be deallocated with free().
Nonmaximal suppression is performed on the corners. Note that the stride
is the number of bytes between rows. If your image has no padding, then this
is the same as xsize.
The detection, scoring and nonmaximal suppression are available as individual
functions. To see how to use the individual functions, see fast.c

@ -0,0 +1,9 @@
Project: FAST (FAST Corner Detection)
URL: http://mi.eng.cam.ac.uk/~er258/work/fast-C-src/
License: BSD
Upstream version: 2.1, released 12-Jan-2009
Local modifications:
- Created CMakeLists.txt for CMake build.
- Update CMakeLists to be sure that the library is a compatible with C++ linkage.
- Update CMakeLists to not include fast.h to compile fast library with VS2005.

71
extern/libmv/third_party/fast/fast.c vendored Normal file

@ -0,0 +1,71 @@
#include <stdlib.h>
#include "fast.h"
xy* fast9_detect_nonmax(const byte* im, int xsize, int ysize, int stride, int b, int* ret_num_corners)
{
xy* corners;
int num_corners;
int* scores;
xy* nonmax;
corners = fast9_detect(im, xsize, ysize, stride, b, &num_corners);
scores = fast9_score(im, stride, corners, num_corners, b);
nonmax = nonmax_suppression(corners, scores, num_corners, ret_num_corners);
free(corners);
free(scores);
return nonmax;
}
xy* fast10_detect_nonmax(const byte* im, int xsize, int ysize, int stride, int b, int* ret_num_corners)
{
xy* corners;
int num_corners;
int* scores;
xy* nonmax;
corners = fast10_detect(im, xsize, ysize, stride, b, &num_corners);
scores = fast10_score(im, stride, corners, num_corners, b);
nonmax = nonmax_suppression(corners, scores, num_corners, ret_num_corners);
free(corners);
free(scores);
return nonmax;
}
xy* fast11_detect_nonmax(const byte* im, int xsize, int ysize, int stride, int b, int* ret_num_corners)
{
xy* corners;
int num_corners;
int* scores;
xy* nonmax;
corners = fast11_detect(im, xsize, ysize, stride, b, &num_corners);
scores = fast11_score(im, stride, corners, num_corners, b);
nonmax = nonmax_suppression(corners, scores, num_corners, ret_num_corners);
free(corners);
free(scores);
return nonmax;
}
xy* fast12_detect_nonmax(const byte* im, int xsize, int ysize, int stride, int b, int* ret_num_corners)
{
xy* corners;
int num_corners;
int* scores;
xy* nonmax;
corners = fast12_detect(im, xsize, ysize, stride, b, &num_corners);
scores = fast12_score(im, stride, corners, num_corners, b);
nonmax = nonmax_suppression(corners, scores, num_corners, ret_num_corners);
free(corners);
free(scores);
return nonmax;
}

39
extern/libmv/third_party/fast/fast.h vendored Normal file

@ -0,0 +1,39 @@
#ifndef FAST_H
#define FAST_H
#ifdef __cplusplus
extern "C" {
#endif
typedef struct { int x, y; } xy;
typedef unsigned char byte;
int fast9_corner_score(const byte* p, const int pixel[], int bstart);
int fast10_corner_score(const byte* p, const int pixel[], int bstart);
int fast11_corner_score(const byte* p, const int pixel[], int bstart);
int fast12_corner_score(const byte* p, const int pixel[], int bstart);
xy* fast9_detect(const byte* im, int xsize, int ysize, int stride, int b, int* ret_num_corners);
xy* fast10_detect(const byte* im, int xsize, int ysize, int stride, int b, int* ret_num_corners);
xy* fast11_detect(const byte* im, int xsize, int ysize, int stride, int b, int* ret_num_corners);
xy* fast12_detect(const byte* im, int xsize, int ysize, int stride, int b, int* ret_num_corners);
int* fast9_score(const byte* i, int stride, xy* corners, int num_corners, int b);
int* fast10_score(const byte* i, int stride, xy* corners, int num_corners, int b);
int* fast11_score(const byte* i, int stride, xy* corners, int num_corners, int b);
int* fast12_score(const byte* i, int stride, xy* corners, int num_corners, int b);
xy* fast9_detect_nonmax(const byte* im, int xsize, int ysize, int stride, int b, int* ret_num_corners);
xy* fast10_detect_nonmax(const byte* im, int xsize, int ysize, int stride, int b, int* ret_num_corners);
xy* fast11_detect_nonmax(const byte* im, int xsize, int ysize, int stride, int b, int* ret_num_corners);
xy* fast12_detect_nonmax(const byte* im, int xsize, int ysize, int stride, int b, int* ret_num_corners);
xy* nonmax_suppression(const xy* corners, const int* scores, int num_corners, int* ret_num_nonmax);
#ifdef __cplusplus
}
#endif
#endif

4666
extern/libmv/third_party/fast/fast_10.c vendored Normal file

File diff suppressed because it is too large Load Diff

3910
extern/libmv/third_party/fast/fast_11.c vendored Normal file

File diff suppressed because it is too large Load Diff

3134
extern/libmv/third_party/fast/fast_12.c vendored Normal file

File diff suppressed because it is too large Load Diff

5910
extern/libmv/third_party/fast/fast_9.c vendored Normal file

File diff suppressed because it is too large Load Diff

117
extern/libmv/third_party/fast/nonmax.c vendored Normal file

@ -0,0 +1,117 @@
#include <stdlib.h>
#include "fast.h"
#define Compare(X, Y) ((X)>=(Y))
xy* nonmax_suppression(const xy* corners, const int* scores, int num_corners, int* ret_num_nonmax)
{
int num_nonmax=0;
int last_row;
int* row_start;
int i, j;
xy* ret_nonmax;
const int sz = (int)num_corners;
/*Point above points (roughly) to the pixel above the one of interest, if there
is a feature there.*/
int point_above = 0;
int point_below = 0;
if(num_corners < 1)
{
*ret_num_nonmax = 0;
return 0;
}
ret_nonmax = (xy*)malloc(num_corners * sizeof(xy));
/* Find where each row begins
(the corners are output in raster scan order). A beginning of -1 signifies
that there are no corners on that row. */
last_row = corners[num_corners-1].y;
row_start = (int*)malloc((last_row+1)*sizeof(int));
for(i=0; i < last_row+1; i++)
row_start[i] = -1;
{
int prev_row = -1;
for(i=0; i< num_corners; i++)
if(corners[i].y != prev_row)
{
row_start[corners[i].y] = i;
prev_row = corners[i].y;
}
}
for(i=0; i < sz; i++)
{
int score = scores[i];
xy pos = corners[i];
/*Check left */
if(i > 0)
if(corners[i-1].x == pos.x-1 && corners[i-1].y == pos.y && Compare(scores[i-1], score))
continue;
/*Check right*/
if(i < (sz - 1))
if(corners[i+1].x == pos.x+1 && corners[i+1].y == pos.y && Compare(scores[i+1], score))
continue;
/*Check above (if there is a valid row above)*/
if(pos.y != 0 && row_start[pos.y - 1] != -1)
{
/*Make sure that current point_above is one
row above.*/
if(corners[point_above].y < pos.y - 1)
point_above = row_start[pos.y-1];
/*Make point_above point to the first of the pixels above the current point,
if it exists.*/
for(; corners[point_above].y < pos.y && corners[point_above].x < pos.x - 1; point_above++)
{}
for(j=point_above; corners[j].y < pos.y && corners[j].x <= pos.x + 1; j++)
{
int x = corners[j].x;
if( (x == pos.x - 1 || x ==pos.x || x == pos.x+1) && Compare(scores[j], score))
goto cont;
}
}
/*Check below (if there is anything below)*/
if(pos.y != last_row && row_start[pos.y + 1] != -1 && point_below < sz) /*Nothing below*/
{
if(corners[point_below].y < pos.y + 1)
point_below = row_start[pos.y+1];
/* Make point below point to one of the pixels belowthe current point, if it
exists.*/
for(; point_below < sz && corners[point_below].y == pos.y+1 && corners[point_below].x < pos.x - 1; point_below++)
{}
for(j=point_below; j < sz && corners[j].y == pos.y+1 && corners[j].x <= pos.x + 1; j++)
{
int x = corners[j].x;
if( (x == pos.x - 1 || x ==pos.x || x == pos.x+1) && Compare(scores[j],score))
goto cont;
}
}
ret_nonmax[num_nonmax++] = corners[i];
cont:
;
}
free(row_start);
*ret_num_nonmax = num_nonmax;
return ret_nonmax;
}

@ -0,0 +1,14 @@
Project: Google Flags
URL: http://code.google.com/p/google-gflags/
License: New BSD
Upstream version: 1.5
Local modifications:
- Flattened the tree and only included files needed for libmv. This involved
changing some of the includes to point to the current directory instead of a
nested gflags directory.
- Added a poor-man's version of upstream's port.cc/h to make gflags compile on
windows. This isn't sufficient but is a stopgap for now.
TODO(keir): Import and use gflags for Windows from upstream.

110
extern/libmv/third_party/gflags/config.h vendored Normal file

@ -0,0 +1,110 @@
/* src/config.h. Generated from config.h.in by configure. */
/* src/config.h.in. Generated from configure.ac by autoheader. */
/* Always the empty-string on non-windows systems. On windows, should be
"__declspec(dllexport)". This way, when we compile the dll, we export our
functions/classes. It's safe to define this here because config.h is only
used internally, to compile the DLL, and every DLL source file #includes
"config.h" before anything else. */
#define GFLAGS_DLL_DECL /**/
/* Namespace for Google classes */
#define GOOGLE_NAMESPACE ::google
/* Define to 1 if you have the <dlfcn.h> header file. */
#define HAVE_DLFCN_H 1
/* Define to 1 if you have the <fnmatch.h> header file. */
#undef HAVE_FNMATCH_H
/* Define to 1 if you have the <inttypes.h> header file. */
#define HAVE_INTTYPES_H 1
/* Define to 1 if you have the <memory.h> header file. */
#define HAVE_MEMORY_H 1
/* define if the compiler implements namespaces */
#define HAVE_NAMESPACES 1
/* Define if you have POSIX threads libraries and header files. */
#define HAVE_PTHREAD 1
/* Define to 1 if you have the `putenv' function. */
#define HAVE_PUTENV 1
/* Define to 1 if you have the `setenv' function. */
#define HAVE_SETENV 1
/* Define to 1 if you have the <stdint.h> header file. */
#define HAVE_STDINT_H 1
/* Define to 1 if you have the <stdlib.h> header file. */
#define HAVE_STDLIB_H 1
/* Define to 1 if you have the <strings.h> header file. */
#define HAVE_STRINGS_H 1
/* Define to 1 if you have the <string.h> header file. */
#define HAVE_STRING_H 1
/* Define to 1 if you have the `strtoll' function. */
#define HAVE_STRTOLL 1
/* Define to 1 if you have the `strtoq' function. */
#define HAVE_STRTOQ 1
/* Define to 1 if you have the <sys/stat.h> header file. */
#define HAVE_SYS_STAT_H 1
/* Define to 1 if you have the <sys/types.h> header file. */
#define HAVE_SYS_TYPES_H 1
/* Define to 1 if you have the <unistd.h> header file. */
#define HAVE_UNISTD_H 1
/* define if your compiler has __attribute__ */
#define HAVE___ATTRIBUTE__ 1
/* Define to the sub-directory in which libtool stores uninstalled libraries.
*/
#define LT_OBJDIR ".libs/"
/* Name of package */
#define PACKAGE "gflags"
/* Define to the address where bug reports for this package should be sent. */
#define PACKAGE_BUGREPORT "opensource@google.com"
/* Define to the full name of this package. */
#define PACKAGE_NAME "gflags"
/* Define to the full name and version of this package. */
#define PACKAGE_STRING "gflags 1.5"
/* Define to the one symbol short name of this package. */
#define PACKAGE_TARNAME "gflags"
/* Define to the home page for this package. */
#define PACKAGE_URL ""
/* Define to the version of this package. */
#define PACKAGE_VERSION "1.5"
/* Define to necessary symbol if this constant uses a non-standard name on
your system. */
/* #undef PTHREAD_CREATE_JOINABLE */
/* Define to 1 if you have the ANSI C header files. */
#define STDC_HEADERS 1
/* the namespace where STL code like vector<> is defined */
#define STL_NAMESPACE std
/* Version number of package */
#define VERSION "1.5"
/* Stops putting the code inside the Google namespace */
#define _END_GOOGLE_NAMESPACE_ }
/* Puts following code inside the Google namespace */
#define _START_GOOGLE_NAMESPACE_ namespace google {

1971
extern/libmv/third_party/gflags/gflags.cc vendored Normal file

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More