1
0
mirror of https://github.com/opencv/opencv.git synced 2026-07-30 07:43:03 +04:00

Merge upstream branch

This commit is contained in:
Alexander Alekhin
2020-04-21 06:19:48 +00:00
6110 changed files with 1584756 additions and 447458 deletions
+22 -1
View File
@@ -4,4 +4,25 @@ if(NOT OPENCV_MODULES_PATH)
set(OPENCV_MODULES_PATH "${CMAKE_CURRENT_SOURCE_DIR}")
endif()
ocv_glob_modules(${OPENCV_MODULES_PATH} EXTRA ${OPENCV_EXTRA_MODULES_PATH})
ocv_glob_modules(${OPENCV_MODULES_PATH} ${OPENCV_EXTRA_MODULES_PATH})
# build lists of modules to be documented
set(OPENCV_MODULES_MAIN "")
set(OPENCV_MODULES_EXTRA "")
foreach(mod ${OPENCV_MODULES_BUILD} ${OPENCV_MODULES_DISABLED_USER} ${OPENCV_MODULES_DISABLED_AUTO} ${OPENCV_MODULES_DISABLED_FORCE})
string(REGEX REPLACE "^opencv_" "" mod "${mod}")
if("${OPENCV_MODULE_opencv_${mod}_LOCATION}" STREQUAL "${OpenCV_SOURCE_DIR}/modules/${mod}")
list(APPEND OPENCV_MODULES_MAIN ${mod})
else()
list(APPEND OPENCV_MODULES_EXTRA ${mod})
endif()
endforeach()
ocv_list_sort(OPENCV_MODULES_MAIN)
ocv_list_sort(OPENCV_MODULES_EXTRA)
set(FIXED_ORDER_MODULES core imgproc imgcodecs videoio highgui video calib3d features2d objdetect dnn ml flann photo stitching)
list(REMOVE_ITEM OPENCV_MODULES_MAIN ${FIXED_ORDER_MODULES})
set(OPENCV_MODULES_MAIN ${FIXED_ORDER_MODULES} ${OPENCV_MODULES_MAIN})
set(OPENCV_MODULES_MAIN ${OPENCV_MODULES_MAIN} CACHE INTERNAL "List of main modules" FORCE)
set(OPENCV_MODULES_EXTRA ${OPENCV_MODULES_EXTRA} CACHE INTERNAL "List of extra modules" FORCE)
-45
View File
@@ -1,45 +0,0 @@
IF(NOT ANDROID OR ANDROID_NATIVE_API_LEVEL LESS 8)
ocv_module_disable(androidcamera)
ENDIF()
set(the_description "Auxiliary module for Android native camera support")
set(OPENCV_MODULE_TYPE STATIC)
ocv_define_module(androidcamera INTERNAL opencv_core log dl)
ocv_include_directories("${CMAKE_CURRENT_SOURCE_DIR}/camera_wrapper" "${OpenCV_SOURCE_DIR}/platforms/android/service/engine/jni/include")
# Android source tree for native camera
SET (ANDROID_SOURCE_TREE "ANDROID_SOURCE_TREE-NOTFOUND" CACHE PATH
"Path to Android source tree. Set this variable to path to your Android sources to compile libnative_camera_rx.x.x.so for your Android")
SET(BUILD_ANDROID_CAMERA_WRAPPER OFF)
if(ANDROID_SOURCE_TREE)
FILE(STRINGS "${ANDROID_SOURCE_TREE}/development/sdk/platform_source.properties" ANDROID_VERSION REGEX "Platform\\.Version=[0-9]+\\.[0-9]+(\\.[0-9]+)?" )
string(REGEX REPLACE "Platform\\.Version=([0-9]+\\.[0-9]+(\\.[0-9]+)?)" "\\1" ANDROID_VERSION "${ANDROID_VERSION}")
if(ANDROID_VERSION MATCHES "^[0-9]+\\.[0-9]+$")
SET(ANDROID_VERSION "${ANDROID_VERSION}.0")
endif()
if(NOT "${ANDROID_VERSION}" STREQUAL "")
SET(BUILD_ANDROID_CAMERA_WRAPPER ON)
set(ANDROID_VERSION "${ANDROID_VERSION}" CACHE INTERNAL "Version of Android source tree")
endif()
endif()
set(BUILD_ANDROID_CAMERA_WRAPPER ${BUILD_ANDROID_CAMERA_WRAPPER} CACHE INTERNAL "Build new wrapper for Android")
MARK_AS_ADVANCED(ANDROID_SOURCE_TREE)
# process wrapper libs
if(BUILD_ANDROID_CAMERA_WRAPPER)
add_subdirectory(camera_wrapper)
else()
file(GLOB camera_wrappers "${CMAKE_CURRENT_SOURCE_DIR}/../../3rdparty/lib/${ANDROID_NDK_ABI_NAME}/libnative_camera_r*.so")
foreach(wrapper ${camera_wrappers})
ADD_CUSTOM_COMMAND(
TARGET ${the_module} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy "${wrapper}" "${LIBRARY_OUTPUT_PATH}"
)
get_filename_component(wrapper_name "${wrapper}" NAME)
install(FILES "${LIBRARY_OUTPUT_PATH}/${wrapper_name}"
DESTINATION ${OPENCV_LIB_INSTALL_PATH}
COMPONENT libs)
endforeach()
endif()
@@ -1,66 +0,0 @@
SET (the_target native_camera_r${ANDROID_VERSION})
project(${the_target})
link_directories("${ANDROID_SOURCE_TREE}/out/target/product/generic/system/lib")
if (ANDROID_VERSION VERSION_LESS "4.1")
INCLUDE_DIRECTORIES(BEFORE
${ANDROID_SOURCE_TREE}
${ANDROID_SOURCE_TREE}/frameworks/base/include/ui
${ANDROID_SOURCE_TREE}/frameworks/base/include/surfaceflinger
${ANDROID_SOURCE_TREE}/frameworks/base/include/camera
${ANDROID_SOURCE_TREE}/frameworks/base/include/media
${ANDROID_SOURCE_TREE}/frameworks/base/include
${ANDROID_SOURCE_TREE}/system/core/include
${ANDROID_SOURCE_TREE}/hardware/libhardware/include
${ANDROID_SOURCE_TREE}/frameworks/base/native/include
${ANDROID_SOURCE_TREE}/frameworks/base/opengl/include
)
else()
INCLUDE_DIRECTORIES(BEFORE
${ANDROID_SOURCE_TREE}
${ANDROID_SOURCE_TREE}/frameworks/native/include
${ANDROID_SOURCE_TREE}/frameworks/av/include
${ANDROID_SOURCE_TREE}/system/core/include
${ANDROID_SOURCE_TREE}/hardware/libhardware/include
)
endif()
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH FALSE)
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${CMAKE_C_FLAGS_RELEASE}")
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CMAKE_CXX_FLAGS_RELEASE}")
SET(CMAKE_C_FLAGS_RELEASE "")
SET(CMAKE_CXX_FLAGS_RELEASE "")
string(REPLACE "-O3" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
string(REPLACE "-frtti" "-fno-rtti" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") # because Android libraries are built without rtti
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Os -fno-strict-aliasing -finline-limit=64 -fuse-cxa-atexit" )
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Os -fno-strict-aliasing -finline-limit=64 -fuse-cxa-atexit")
SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-z,noexecstack")
ADD_LIBRARY(${the_target} SHARED camera_wrapper.h camera_wrapper.cpp)
string(REGEX REPLACE "[.]" "_" LIBRARY_DEF ${ANDROID_VERSION})
add_definitions(-DANDROID_r${LIBRARY_DEF})
ocv_target_link_libraries(${the_target} c m dl utils camera_client binder log)
if(NOT ANDROID_VERSION VERSION_LESS "3.0.0")
target_link_libraries(${the_target} gui )
endif()
SET_TARGET_PROPERTIES(${the_target} PROPERTIES
OUTPUT_NAME "${the_target}"
ARCHIVE_OUTPUT_DIRECTORY ${LIBRARY_OUTPUT_PATH}
RUNTIME_OUTPUT_DIRECTORY ${EXECUTABLE_OUTPUT_PATH}
)
if (NOT (CMAKE_BUILD_TYPE MATCHES "Debug"))
ADD_CUSTOM_COMMAND( TARGET ${the_target} POST_BUILD COMMAND ${CMAKE_STRIP} --strip-unneeded "${LIBRARY_OUTPUT_PATH}/lib${the_target}.so" )
endif()
install(TARGETS ${the_target} LIBRARY DESTINATION ${OPENCV_LIB_INSTALL_PATH} COMPONENT libs)
@@ -1,70 +0,0 @@
*** src2.3.3/frameworks/base/include/camera/Camera.h 2011-04-04 20:18:36.718480237 +0400
--- src_mock3.0.1/frameworks/base/include/camera/Camera.h 2012-01-15 20:51:36.000000000 +0400
***************
*** 20,25 ****
--- 20,27 ----
#include <utils/Timers.h>
#include <camera/ICameraClient.h>
+ #include <gui/ISurfaceTexture.h>
+
namespace android {
class ISurface;
***************
*** 76,81 ****
--- 78,90 ----
CAMERA_MSG_POSTVIEW_FRAME = 0x040,
CAMERA_MSG_RAW_IMAGE = 0x080,
CAMERA_MSG_COMPRESSED_IMAGE = 0x100,
+
+ #ifdef OMAP_ENHANCEMENT
+
+ CAMERA_MSG_BURST_IMAGE = 0x200,
+
+ #endif
+
CAMERA_MSG_ALL_MSGS = 0x1FF
};
***************
*** 144,150 ****
--- 153,164 ----
public:
virtual void notify(int32_t msgType, int32_t ext1, int32_t ext2) = 0;
virtual void postData(int32_t msgType, const sp<IMemory>& dataPtr) = 0;
+ #ifdef OMAP_ENHANCEMENT
+ virtual void postDataTimestamp(nsecs_t timestamp, int32_t msgType, const sp<IMemory>& dataPtr,
+ uint32_t offset=0, uint32_t stride=0) = 0;
+ #else
virtual void postDataTimestamp(nsecs_t timestamp, int32_t msgType, const sp<IMemory>& dataPtr) = 0;
+ #endif
};
class Camera : public BnCameraClient, public IBinder::DeathRecipient
***************
*** 170,175 ****
--- 184,191 ----
status_t setPreviewDisplay(const sp<Surface>& surface);
status_t setPreviewDisplay(const sp<ISurface>& surface);
+ // pass the SurfaceTexture object to the Camera
+ status_t setPreviewTexture(const sp<ISurfaceTexture>& surfaceTexture);
// start preview mode, must call setPreviewDisplay first
status_t startPreview();
***************
*** 215,221 ****
--- 231,242 ----
// ICameraClient interface
virtual void notifyCallback(int32_t msgType, int32_t ext, int32_t ext2);
virtual void dataCallback(int32_t msgType, const sp<IMemory>& dataPtr);
+ #ifdef OMAP_ENHANCEMENT
+ virtual void dataCallbackTimestamp(nsecs_t timestamp, int32_t msgType, const sp<IMemory>& dataPtr,
+ uint32_t offset=0, uint32_t stride=0);
+ #else
virtual void dataCallbackTimestamp(nsecs_t timestamp, int32_t msgType, const sp<IMemory>& dataPtr);
+ #endif
sp<ICamera> remote();
@@ -1,14 +0,0 @@
*** src2.3.3/frameworks/base/include/camera/ICamera.h 2011-04-04 20:18:36.718480237 +0400
--- src_mock3.0.1/frameworks/base/include/camera/ICamera.h 2012-01-15 20:50:30.000000000 +0400
***************
*** 48,53 ****
--- 48,56 ----
// pass the buffered ISurface to the camera service
virtual status_t setPreviewDisplay(const sp<ISurface>& surface) = 0;
+ // pass the preview texture. This is for 3.0 and higher versions of Android
+ setPreviewTexture(const sp<ISurfaceTexture>& surfaceTexture) = 0;
+
// set the preview callback flag to affect how the received frames from
// preview are handled.
virtual void setPreviewCallbackFlag(int flag) = 0;
@@ -1,9 +0,0 @@
Building camera wrapper for Android 3.0.1:
1) Get sources of Android 2.3.x (2.3.3 were used)
2) Apply patches provided with this instruction to frameworks/base/include/camera/ICamera.h and frameworks/base/include/camera/Camera.h
3) Get frameworks/base/include/gui/ISurfaceTexture.h and frameworks/base/include/gui/SurfaceTexture.h from Android 4.0.x (4.0.3 were used) sources and add them to your source tree.
4) Apply provided patch to the frameworks/base/include/gui/SurfaceTexture.h.
5) Pull /system/lib from your device running Andoid 3.x.x
6) Edit <Android Root>/development/sdk/platform_source.properties file. Set Android version to 3.0.1.
7) Build wrapper as normal using this modified source tree.
@@ -1,37 +0,0 @@
*** src4.0.3/src/frameworks/base/include/gui/SurfaceTexture.h 2012-01-18 16:32:41.424750385 +0400
--- src_mock3.0.1/frameworks/base/include/gui/SurfaceTexture.h 2012-01-12 21:28:14.000000000 +0400
***************
*** 68,75 ****
// texture will be bound in updateTexImage. useFenceSync specifies whether
// fences should be used to synchronize access to buffers if that behavior
// is enabled at compile-time.
! SurfaceTexture(GLuint tex, bool allowSynchronousMode = true,
! GLenum texTarget = GL_TEXTURE_EXTERNAL_OES, bool useFenceSync = true);
virtual ~SurfaceTexture();
--- 68,74 ----
// texture will be bound in updateTexImage. useFenceSync specifies whether
// fences should be used to synchronize access to buffers if that behavior
// is enabled at compile-time.
! SurfaceTexture(GLuint tex);
virtual ~SurfaceTexture();
***************
*** 280,286 ****
mBufferState(BufferSlot::FREE),
mRequestBufferCalled(false),
mTransform(0),
! mScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE),
mTimestamp(0),
mFrameNumber(0),
mFence(EGL_NO_SYNC_KHR) {
--- 279,285 ----
mBufferState(BufferSlot::FREE),
mRequestBufferCalled(false),
mTransform(0),
! mScalingMode(0),
mTimestamp(0),
mFrameNumber(0),
mFence(EGL_NO_SYNC_KHR) {
File diff suppressed because it is too large Load Diff
@@ -1,16 +0,0 @@
typedef bool (*CameraCallback)(void* buffer, size_t bufferSize, void* userData);
typedef void* (*InitCameraConnectC)(void* cameraCallback, int cameraId, void* userData);
typedef void (*CloseCameraConnectC)(void**);
typedef double (*GetCameraPropertyC)(void* camera, int propIdx);
typedef void (*SetCameraPropertyC)(void* camera, int propIdx, double value);
typedef void (*ApplyCameraPropertiesC)(void** camera);
extern "C"
{
void* initCameraConnectC(void* cameraCallback, int cameraId, void* userData);
void closeCameraConnectC(void**);
double getCameraPropertyC(void* camera, int propIdx);
void setCameraPropertyC(void* camera, int propIdx, double value);
void applyCameraPropertiesC(void** camera);
}
@@ -1,47 +0,0 @@
#ifndef _CAMERAACTIVITY_H_
#define _CAMERAACTIVITY_H_
#include <camera_properties.h>
class CameraActivity
{
public:
enum ErrorCode {
NO_ERROR=0,
ERROR_WRONG_FRAME_SIZE,
ERROR_WRONG_POINTER_CAMERA_WRAPPER,
ERROR_CAMERA_CONNECTED,
ERROR_CANNOT_OPEN_CAMERA_WRAPPER_LIB,
ERROR_CANNOT_GET_FUNCTION_FROM_CAMERA_WRAPPER_LIB,
ERROR_CANNOT_INITIALIZE_CONNECTION,
ERROR_ISNT_CONNECTED,
ERROR_JAVA_VM_CANNOT_GET_CLASS,
ERROR_JAVA_VM_CANNOT_GET_FIELD,
ERROR_CANNOT_SET_PREVIEW_DISPLAY,
ERROR_UNKNOWN=255
};
CameraActivity();
virtual ~CameraActivity();
virtual bool onFrameBuffer(void* buffer, int bufferSize);
ErrorCode connect(int cameraId = 0);
void disconnect();
bool isConnected() const;
double getProperty(int propIdx);
void setProperty(int propIdx, double value);
void applyProperties();
int getFrameWidth();
int getFrameHeight();
static void setPathLibFolder(const char* path);
private:
void* camera;
int frameWidth;
int frameHeight;
};
#endif
@@ -1,70 +0,0 @@
#ifndef CAMERA_PROPERTIES_H
#define CAMERA_PROPERTIES_H
enum {
ANDROID_CAMERA_PROPERTY_FRAMEWIDTH = 0,
ANDROID_CAMERA_PROPERTY_FRAMEHEIGHT = 1,
ANDROID_CAMERA_PROPERTY_SUPPORTED_PREVIEW_SIZES_STRING = 2,
ANDROID_CAMERA_PROPERTY_PREVIEW_FORMAT_STRING = 3,
ANDROID_CAMERA_PROPERTY_FPS = 4,
ANDROID_CAMERA_PROPERTY_EXPOSURE = 5,
ANDROID_CAMERA_PROPERTY_FLASH_MODE = 101,
ANDROID_CAMERA_PROPERTY_FOCUS_MODE = 102,
ANDROID_CAMERA_PROPERTY_WHITE_BALANCE = 103,
ANDROID_CAMERA_PROPERTY_ANTIBANDING = 104,
ANDROID_CAMERA_PROPERTY_FOCAL_LENGTH = 105,
ANDROID_CAMERA_PROPERTY_FOCUS_DISTANCE_NEAR = 106,
ANDROID_CAMERA_PROPERTY_FOCUS_DISTANCE_OPTIMAL = 107,
ANDROID_CAMERA_PROPERTY_FOCUS_DISTANCE_FAR = 108,
ANDROID_CAMERA_PROPERTY_EXPOSE_LOCK = 109,
ANDROID_CAMERA_PROPERTY_WHITEBALANCE_LOCK = 110
};
enum {
ANDROID_CAMERA_FLASH_MODE_AUTO = 0,
ANDROID_CAMERA_FLASH_MODE_OFF,
ANDROID_CAMERA_FLASH_MODE_ON,
ANDROID_CAMERA_FLASH_MODE_RED_EYE,
ANDROID_CAMERA_FLASH_MODE_TORCH,
ANDROID_CAMERA_FLASH_MODES_NUM
};
enum {
ANDROID_CAMERA_FOCUS_MODE_AUTO = 0,
ANDROID_CAMERA_FOCUS_MODE_CONTINUOUS_VIDEO,
ANDROID_CAMERA_FOCUS_MODE_EDOF,
ANDROID_CAMERA_FOCUS_MODE_FIXED,
ANDROID_CAMERA_FOCUS_MODE_INFINITY,
ANDROID_CAMERA_FOCUS_MODE_MACRO,
ANDROID_CAMERA_FOCUS_MODE_CONTINUOUS_PICTURE,
ANDROID_CAMERA_FOCUS_MODES_NUM
};
enum {
ANDROID_CAMERA_WHITE_BALANCE_AUTO = 0,
ANDROID_CAMERA_WHITE_BALANCE_CLOUDY_DAYLIGHT,
ANDROID_CAMERA_WHITE_BALANCE_DAYLIGHT,
ANDROID_CAMERA_WHITE_BALANCE_FLUORESCENT,
ANDROID_CAMERA_WHITE_BALANCE_INCANDESCENT,
ANDROID_CAMERA_WHITE_BALANCE_SHADE,
ANDROID_CAMERA_WHITE_BALANCE_TWILIGHT,
ANDROID_CAMERA_WHITE_BALANCE_WARM_FLUORESCENT,
ANDROID_CAMERA_WHITE_BALANCE_MODES_NUM
};
enum {
ANDROID_CAMERA_ANTIBANDING_50HZ = 0,
ANDROID_CAMERA_ANTIBANDING_60HZ,
ANDROID_CAMERA_ANTIBANDING_AUTO,
ANDROID_CAMERA_ANTIBANDING_OFF,
ANDROID_CAMERA_ANTIBANDING_MODES_NUM
};
enum {
ANDROID_CAMERA_FOCUS_DISTANCE_NEAR_INDEX = 0,
ANDROID_CAMERA_FOCUS_DISTANCE_OPTIMAL_INDEX,
ANDROID_CAMERA_FOCUS_DISTANCE_FAR_INDEX
};
#endif // CAMERA_PROPERTIES_H
@@ -1,451 +0,0 @@
#include <dlfcn.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <android/log.h>
#include <cctype>
#include <vector>
#include <algorithm>
#include <opencv2/core/version.hpp>
#include "camera_activity.hpp"
#include "camera_wrapper.h"
#include "EngineCommon.h"
#include "opencv2/core.hpp"
#undef LOG_TAG
#undef LOGE
#undef LOGD
#undef LOGI
#define LOG_TAG "OpenCV::camera"
#define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__))
#define LOGD(...) ((void)__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__))
#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__))
///////
// Debug
#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>
struct str_greater
{
bool operator() (const cv::String& a, const cv::String& b) { return a > b; }
};
class CameraWrapperConnector
{
public:
static CameraActivity::ErrorCode connect(int cameraId, CameraActivity* pCameraActivity, void** camera);
static CameraActivity::ErrorCode disconnect(void** camera);
static CameraActivity::ErrorCode setProperty(void* camera, int propIdx, double value);
static CameraActivity::ErrorCode getProperty(void* camera, int propIdx, double* value);
static CameraActivity::ErrorCode applyProperties(void** ppcamera);
static void setPathLibFolder(const cv::String& path);
private:
static cv::String pathLibFolder;
static bool isConnectedToLib;
static cv::String getPathLibFolder();
static cv::String getDefaultPathLibFolder();
static CameraActivity::ErrorCode connectToLib();
static CameraActivity::ErrorCode getSymbolFromLib(void * libHandle, const char* symbolName, void** ppSymbol);
static void fillListWrapperLibs(const cv::String& folderPath, std::vector<cv::String>& listLibs);
static InitCameraConnectC pInitCameraC;
static CloseCameraConnectC pCloseCameraC;
static GetCameraPropertyC pGetPropertyC;
static SetCameraPropertyC pSetPropertyC;
static ApplyCameraPropertiesC pApplyPropertiesC;
friend bool nextFrame(void* buffer, size_t bufferSize, void* userData);
};
cv::String CameraWrapperConnector::pathLibFolder;
bool CameraWrapperConnector::isConnectedToLib = false;
InitCameraConnectC CameraWrapperConnector::pInitCameraC = 0;
CloseCameraConnectC CameraWrapperConnector::pCloseCameraC = 0;
GetCameraPropertyC CameraWrapperConnector::pGetPropertyC = 0;
SetCameraPropertyC CameraWrapperConnector::pSetPropertyC = 0;
ApplyCameraPropertiesC CameraWrapperConnector::pApplyPropertiesC = 0;
#define INIT_CAMERA_SYMBOL_NAME "initCameraConnectC"
#define CLOSE_CAMERA_SYMBOL_NAME "closeCameraConnectC"
#define SET_CAMERA_PROPERTY_SYMBOL_NAME "setCameraPropertyC"
#define GET_CAMERA_PROPERTY_SYMBOL_NAME "getCameraPropertyC"
#define APPLY_CAMERA_PROPERTIES_SYMBOL_NAME "applyCameraPropertiesC"
#define PREFIX_CAMERA_WRAPPER_LIB "libnative_camera"
bool nextFrame(void* buffer, size_t bufferSize, void* userData)
{
if (userData == NULL)
return true;
return ((CameraActivity*)userData)->onFrameBuffer(buffer, bufferSize);
}
CameraActivity::ErrorCode CameraWrapperConnector::connect(int cameraId, CameraActivity* pCameraActivity, void** camera)
{
if (pCameraActivity == NULL)
{
LOGE("CameraWrapperConnector::connect error: wrong pointer to CameraActivity object");
return CameraActivity::ERROR_WRONG_POINTER_CAMERA_WRAPPER;
}
CameraActivity::ErrorCode errcode=connectToLib();
if (errcode) return errcode;
void* cmr = (*pInitCameraC)((void*)nextFrame, cameraId, (void*)pCameraActivity);
if (!cmr)
{
LOGE("CameraWrapperConnector::connectWrapper ERROR: the initializing function returned false");
return CameraActivity::ERROR_CANNOT_INITIALIZE_CONNECTION;
}
*camera = cmr;
return CameraActivity::NO_ERROR;
}
CameraActivity::ErrorCode CameraWrapperConnector::disconnect(void** camera)
{
if (camera == NULL || *camera == NULL)
{
LOGE("CameraWrapperConnector::disconnect error: wrong pointer to camera object");
return CameraActivity::ERROR_WRONG_POINTER_CAMERA_WRAPPER;
}
CameraActivity::ErrorCode errcode=connectToLib();
if (errcode) return errcode;
(*pCloseCameraC)(camera);
return CameraActivity::NO_ERROR;
}
CameraActivity::ErrorCode CameraWrapperConnector::setProperty(void* camera, int propIdx, double value)
{
if (camera == NULL)
{
LOGE("CameraWrapperConnector::setProperty error: wrong pointer to camera object");
return CameraActivity::ERROR_WRONG_POINTER_CAMERA_WRAPPER;
}
(*pSetPropertyC)(camera, propIdx, value);
return CameraActivity::NO_ERROR;
}
CameraActivity::ErrorCode CameraWrapperConnector::getProperty(void* camera, int propIdx, double* value)
{
if (camera == NULL)
{
LOGE("CameraWrapperConnector::getProperty error: wrong pointer to camera object");
return CameraActivity::ERROR_WRONG_POINTER_CAMERA_WRAPPER;
}
LOGE("calling (*pGetPropertyC)(%p, %d)", camera, propIdx);
*value = (*pGetPropertyC)(camera, propIdx);
return CameraActivity::NO_ERROR;
}
CameraActivity::ErrorCode CameraWrapperConnector::applyProperties(void** ppcamera)
{
if ((ppcamera == NULL) || (*ppcamera == NULL))
{
LOGE("CameraWrapperConnector::applyProperties error: wrong pointer to camera object");
return CameraActivity::ERROR_WRONG_POINTER_CAMERA_WRAPPER;
}
(*pApplyPropertiesC)(ppcamera);
return CameraActivity::NO_ERROR;
}
CameraActivity::ErrorCode CameraWrapperConnector::connectToLib()
{
if (isConnectedToLib) {
return CameraActivity::NO_ERROR;
}
dlerror();
cv::String folderPath = getPathLibFolder();
if (folderPath.empty())
{
LOGD("Trying to find native camera in default OpenCV packages");
folderPath = getDefaultPathLibFolder();
}
LOGD("CameraWrapperConnector::connectToLib: folderPath=%s", folderPath.c_str());
std::vector<cv::String> listLibs;
fillListWrapperLibs(folderPath, listLibs);
std::sort(listLibs.begin(), listLibs.end(), str_greater());
void * libHandle=0;
cv::String cur_path;
for(size_t i = 0; i < listLibs.size(); i++) {
cur_path=folderPath + listLibs[i];
LOGD("try to load library '%s'", listLibs[i].c_str());
libHandle=dlopen(cur_path.c_str(), RTLD_LAZY);
if (libHandle) {
LOGD("Loaded library '%s'", cur_path.c_str());
break;
} else {
LOGD("CameraWrapperConnector::connectToLib ERROR: cannot dlopen camera wrapper library %s, dlerror=\"%s\"",
cur_path.c_str(), dlerror());
}
}
if (!libHandle) {
LOGE("CameraWrapperConnector::connectToLib ERROR: cannot dlopen camera wrapper library");
return CameraActivity::ERROR_CANNOT_OPEN_CAMERA_WRAPPER_LIB;
}
InitCameraConnectC pInit_C;
CloseCameraConnectC pClose_C;
GetCameraPropertyC pGetProp_C;
SetCameraPropertyC pSetProp_C;
ApplyCameraPropertiesC pApplyProp_C;
CameraActivity::ErrorCode res;
res = getSymbolFromLib(libHandle, (const char*)INIT_CAMERA_SYMBOL_NAME, (void**)(&pInit_C));
if (res) return res;
res = getSymbolFromLib(libHandle, CLOSE_CAMERA_SYMBOL_NAME, (void**)(&pClose_C));
if (res) return res;
res = getSymbolFromLib(libHandle, GET_CAMERA_PROPERTY_SYMBOL_NAME, (void**)(&pGetProp_C));
if (res) return res;
res = getSymbolFromLib(libHandle, SET_CAMERA_PROPERTY_SYMBOL_NAME, (void**)(&pSetProp_C));
if (res) return res;
res = getSymbolFromLib(libHandle, APPLY_CAMERA_PROPERTIES_SYMBOL_NAME, (void**)(&pApplyProp_C));
if (res) return res;
pInitCameraC = pInit_C;
pCloseCameraC = pClose_C;
pGetPropertyC = pGetProp_C;
pSetPropertyC = pSetProp_C;
pApplyPropertiesC = pApplyProp_C;
isConnectedToLib=true;
return CameraActivity::NO_ERROR;
}
CameraActivity::ErrorCode CameraWrapperConnector::getSymbolFromLib(void* libHandle, const char* symbolName, void** ppSymbol)
{
dlerror();
*(void **) (ppSymbol)=dlsym(libHandle, symbolName);
const char* error_dlsym_init=dlerror();
if (error_dlsym_init) {
LOGE("CameraWrapperConnector::getSymbolFromLib ERROR: cannot get symbol of the function '%s' from the camera wrapper library, dlerror=\"%s\"",
symbolName, error_dlsym_init);
return CameraActivity::ERROR_CANNOT_GET_FUNCTION_FROM_CAMERA_WRAPPER_LIB;
}
return CameraActivity::NO_ERROR;
}
void CameraWrapperConnector::fillListWrapperLibs(const cv::String& folderPath, std::vector<cv::String>& listLibs)
{
DIR *dp;
struct dirent *ep;
dp = opendir (folderPath.c_str());
if (dp != NULL)
{
while ((ep = readdir (dp))) {
const char* cur_name=ep->d_name;
if (strstr(cur_name, PREFIX_CAMERA_WRAPPER_LIB)) {
listLibs.push_back(cur_name);
LOGE("||%s", cur_name);
}
}
(void) closedir (dp);
}
}
cv::String CameraWrapperConnector::getDefaultPathLibFolder()
{
#define BIN_PACKAGE_NAME(x) "org.opencv.lib_v" CVAUX_STR(CV_VERSION_MAJOR) CVAUX_STR(CV_VERSION_MINOR) "_" x
const char* const packageList[] = {BIN_PACKAGE_NAME("armv7a"), OPENCV_ENGINE_PACKAGE};
for (size_t i = 0; i < sizeof(packageList)/sizeof(packageList[0]); i++)
{
char path[128];
sprintf(path, "/data/data/%s/lib/", packageList[i]);
LOGD("Trying package \"%s\" (\"%s\")", packageList[i], path);
DIR* dir = opendir(path);
if (!dir)
{
LOGD("Package not found");
continue;
}
else
{
closedir(dir);
return path;
}
}
return cv::String();
}
cv::String CameraWrapperConnector::getPathLibFolder()
{
if (!pathLibFolder.empty())
return pathLibFolder;
Dl_info dl_info;
if(0 != dladdr((void *)nextFrame, &dl_info))
{
LOGD("Library name: %s", dl_info.dli_fname);
LOGD("Library base address: %p", dl_info.dli_fbase);
const char* libName=dl_info.dli_fname;
while( ((*libName)=='/') || ((*libName)=='.') )
libName++;
char lineBuf[2048];
FILE* file = fopen("/proc/self/smaps", "rt");
if(file)
{
while (fgets(lineBuf, sizeof lineBuf, file) != NULL)
{
//verify that line ends with library name
int lineLength = strlen(lineBuf);
int libNameLength = strlen(libName);
//trim end
for(int i = lineLength - 1; i >= 0 && isspace(lineBuf[i]); --i)
{
lineBuf[i] = 0;
--lineLength;
}
if (0 != strncmp(lineBuf + lineLength - libNameLength, libName, libNameLength))
{
//the line does not contain the library name
continue;
}
//extract path from smaps line
char* pathBegin = strchr(lineBuf, '/');
if (0 == pathBegin)
{
LOGE("Strange error: could not find path beginning in lin \"%s\"", lineBuf);
continue;
}
char* pathEnd = strrchr(pathBegin, '/');
pathEnd[1] = 0;
LOGD("Libraries folder found: %s", pathBegin);
fclose(file);
return pathBegin;
}
fclose(file);
LOGE("Could not find library path");
}
else
{
LOGE("Could not read /proc/self/smaps");
}
}
else
{
LOGE("Could not get library name and base address");
}
return cv::String();
}
void CameraWrapperConnector::setPathLibFolder(const cv::String& path)
{
pathLibFolder=path;
}
/////////////////////////////////////////////////////////////////////////////////////////////////
CameraActivity::CameraActivity() : camera(0), frameWidth(-1), frameHeight(-1)
{
}
CameraActivity::~CameraActivity()
{
if (camera != 0)
disconnect();
}
bool CameraActivity::onFrameBuffer(void* /*buffer*/, int /*bufferSize*/)
{
LOGD("CameraActivity::onFrameBuffer - empty callback");
return true;
}
void CameraActivity::disconnect()
{
CameraWrapperConnector::disconnect(&camera);
}
bool CameraActivity::isConnected() const
{
return camera != 0;
}
CameraActivity::ErrorCode CameraActivity::connect(int cameraId)
{
ErrorCode rescode = CameraWrapperConnector::connect(cameraId, this, &camera);
if (rescode) return rescode;
return NO_ERROR;
}
double CameraActivity::getProperty(int propIdx)
{
double propVal;
ErrorCode rescode = CameraWrapperConnector::getProperty(camera, propIdx, &propVal);
if (rescode) return -1;
return propVal;
}
void CameraActivity::setProperty(int propIdx, double value)
{
CameraWrapperConnector::setProperty(camera, propIdx, value);
}
void CameraActivity::applyProperties()
{
frameWidth = -1;
frameHeight = -1;
CameraWrapperConnector::applyProperties(&camera);
frameWidth = getProperty(ANDROID_CAMERA_PROPERTY_FRAMEWIDTH);
frameHeight = getProperty(ANDROID_CAMERA_PROPERTY_FRAMEHEIGHT);
}
int CameraActivity::getFrameWidth()
{
if (frameWidth <= 0)
frameWidth = getProperty(ANDROID_CAMERA_PROPERTY_FRAMEWIDTH);
return frameWidth;
}
int CameraActivity::getFrameHeight()
{
if (frameHeight <= 0)
frameHeight = getProperty(ANDROID_CAMERA_PROPERTY_FRAMEHEIGHT);
return frameHeight;
}
void CameraActivity::setPathLibFolder(const char* path)
{
CameraWrapperConnector::setPathLibFolder(path);
}
+7 -1
View File
@@ -1,2 +1,8 @@
set(the_description "Camera Calibration and 3D Reconstruction")
ocv_define_module(calib3d opencv_imgproc opencv_features2d)
set(debug_modules "")
if(DEBUG_opencv_calib3d)
list(APPEND debug_modules opencv_highgui)
endif()
ocv_define_module(calib3d opencv_imgproc opencv_features2d ${debug_modules}
WRAP java python js
)
+41
View File
@@ -0,0 +1,41 @@
@article{lepetit2009epnp,
title={Epnp: An accurate o (n) solution to the pnp problem},
author={Lepetit, Vincent and Moreno-Noguer, Francesc and Fua, Pascal},
journal={International journal of computer vision},
volume={81},
number={2},
pages={155--166},
year={2009},
publisher={Springer}
}
@article{gao2003complete,
title={Complete solution classification for the perspective-three-point problem},
author={Gao, Xiao-Shan and Hou, Xiao-Rong and Tang, Jianliang and Cheng, Hang-Fei},
journal={Pattern Analysis and Machine Intelligence, IEEE Transactions on},
volume={25},
number={8},
pages={930--943},
year={2003},
publisher={IEEE}
}
@inproceedings{hesch2011direct,
title={A direct least-squares (DLS) method for PnP},
author={Hesch, Joel and Roumeliotis, Stergios and others},
booktitle={Computer Vision (ICCV), 2011 IEEE International Conference on},
pages={383--390},
year={2011},
organization={IEEE}
}
@article{penate2013exhaustive,
title={Exhaustive linearization for robust camera pose and focal length estimation},
author={Penate-Sanchez, Adrian and Andrade-Cetto, Juan and Moreno-Noguer, Francesc},
journal={Pattern Analysis and Machine Intelligence, IEEE Transactions on},
volume={35},
number={10},
pages={2387--2400},
year={2013},
publisher={IEEE}
}
-8
View File
@@ -1,8 +0,0 @@
*************************************************
calib3d. Camera Calibration and 3D Reconstruction
*************************************************
.. toctree::
:maxdepth: 2
camera_calibration_and_3d_reconstruction
File diff suppressed because it is too large Load Diff
Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

File diff suppressed because it is too large Load Diff
@@ -41,8 +41,8 @@
//
//M*/
#ifndef __OPENCV_CALIB3D_C_H__
#define __OPENCV_CALIB3D_C_H__
#ifndef OPENCV_CALIB3D_C_H
#define OPENCV_CALIB3D_C_H
#include "opencv2/core/core_c.h"
@@ -50,6 +50,10 @@
extern "C" {
#endif
/** @addtogroup calib3d_c
@{
*/
/****************************************************************************************\
* Camera Calibration, Pose Estimation and Stereo *
\****************************************************************************************/
@@ -91,7 +95,8 @@ enum
{
CV_ITERATIVE = 0,
CV_EPNP = 1, // F.Moreno-Noguer, V.Lepetit and P.Fua "EPnP: Efficient Perspective-n-Point Camera Pose Estimation"
CV_P3P = 2 // X.S. Gao, X.-R. Hou, J. Tang, H.-F. Chang; "Complete Solution Classification for the Perspective-Three-Point Problem"
CV_P3P = 2, // X.S. Gao, X.-R. Hou, J. Tang, H.-F. Chang; "Complete Solution Classification for the Perspective-Three-Point Problem"
CV_DLS = 3 // Joel A. Hesch and Stergios I. Roumeliotis. "A Direct Least-Squares (DLS) Method for PnP"
};
CVAPI(int) cvFindFundamentalMat( const CvMat* points1, const CvMat* points2,
@@ -140,7 +145,9 @@ CVAPI(int) cvFindHomography( const CvMat* src_points,
CvMat* homography,
int method CV_DEFAULT(0),
double ransacReprojThreshold CV_DEFAULT(3),
CvMat* mask CV_DEFAULT(0));
CvMat* mask CV_DEFAULT(0),
int maxIters CV_DEFAULT(2000),
double confidence CV_DEFAULT(0.995));
/* Computes RQ decomposition for 3x3 matrices */
CVAPI(void) cvRQDecomp3x3( const CvMat *matrixM, CvMat *matrixR, CvMat *matrixQ,
@@ -236,7 +243,11 @@ CVAPI(void) cvDrawChessboardCorners( CvArr* image, CvSize pattern_size,
#define CV_CALIB_RATIONAL_MODEL 16384
#define CV_CALIB_THIN_PRISM_MODEL 32768
#define CV_CALIB_FIX_S1_S2_S3_S4 65536
#define CV_CALIB_TILTED_MODEL 262144
#define CV_CALIB_FIX_TAUX_TAUY 524288
#define CV_CALIB_FIX_TANGENT_DIST 2097152
#define CV_CALIB_NINTRINSIC 18
/* Finds intrinsic and extrinsic camera parameters
from a few views of known calibration pattern */
@@ -368,6 +379,8 @@ CVAPI(void) cvReprojectImageTo3D( const CvArr* disparityImage,
CvArr* _3dImage, const CvMat* Q,
int handleMissingValues CV_DEFAULT(0) );
/** @} calib3d_c */
#ifdef __cplusplus
} // extern "C"
@@ -406,8 +419,9 @@ public:
int state;
int iters;
bool completeSymmFlag;
int solveMethod;
};
#endif
#endif /* __OPENCV_CALIB3D_C_H__ */
#endif /* OPENCV_CALIB3D_C_H */
+31
View File
@@ -0,0 +1,31 @@
{
"class_ignore_list": [
"CirclesGridFinderParameters",
"CirclesGridFinderParameters2"
],
"namespaces_dict": {
"cv.fisheye": "fisheye"
},
"func_arg_fix" : {
"findFundamentalMat" : { "points1" : {"ctype" : "vector_Point2f"},
"points2" : {"ctype" : "vector_Point2f"} },
"cornerSubPix" : { "corners" : {"ctype" : "vector_Point2f"} },
"findHomography" : { "srcPoints" : {"ctype" : "vector_Point2f"},
"dstPoints" : {"ctype" : "vector_Point2f"} },
"solvePnP" : { "objectPoints" : {"ctype" : "vector_Point3f"},
"imagePoints" : {"ctype" : "vector_Point2f"},
"distCoeffs" : {"ctype" : "vector_double" } },
"solvePnPRansac" : { "objectPoints" : {"ctype" : "vector_Point3f"},
"imagePoints" : {"ctype" : "vector_Point2f"},
"distCoeffs" : {"ctype" : "vector_double" } },
"undistortPoints" : { "src" : {"ctype" : "vector_Point2f"},
"dst" : {"ctype" : "vector_Point2f"} },
"projectPoints" : { "objectPoints" : {"ctype" : "vector_Point3f"},
"imagePoints" : {"ctype" : "vector_Point2f"},
"distCoeffs" : {"ctype" : "vector_double" } },
"initCameraMatrix2D" : { "objectPoints" : {"ctype" : "vector_vector_Point3f"},
"imagePoints" : {"ctype" : "vector_vector_Point2f"} },
"findChessboardCorners" : { "corners" : {"ctype" : "vector_Point2f"} },
"drawChessboardCorners" : { "corners" : {"ctype" : "vector_Point2f"} }
}
}
@@ -1,7 +1,8 @@
package org.opencv.test.calib3d;
import java.util.ArrayList;
import org.opencv.calib3d.Calib3d;
import org.opencv.core.Core;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.MatOfDouble;
@@ -11,6 +12,7 @@ import org.opencv.core.Point;
import org.opencv.core.Scalar;
import org.opencv.core.Size;
import org.opencv.test.OpenCVTestCase;
import org.opencv.imgproc.Imgproc;
public class Calib3dTest extends OpenCVTestCase {
@@ -162,7 +164,7 @@ public class Calib3dTest extends OpenCVTestCase {
public void testFilterSpecklesMatDoubleIntDouble() {
gray_16s_1024.copyTo(dst);
Point center = new Point(gray_16s_1024.rows() / 2., gray_16s_1024.cols() / 2.);
Core.circle(dst, center, 1, Scalar.all(4096));
Imgproc.circle(dst, center, 1, Scalar.all(4096));
assertMatNotEqual(gray_16s_1024, dst);
Calib3d.filterSpeckles(dst, 1024.0, 100, 0.);
@@ -177,7 +179,7 @@ public class Calib3dTest extends OpenCVTestCase {
Size patternSize = new Size(9, 6);
MatOfPoint2f corners = new MatOfPoint2f();
Calib3d.findChessboardCorners(grayChess, patternSize, corners);
assertTrue(!corners.empty());
assertFalse(corners.empty());
}
public void testFindChessboardCornersMatSizeMatInt() {
@@ -185,7 +187,16 @@ public class Calib3dTest extends OpenCVTestCase {
MatOfPoint2f corners = new MatOfPoint2f();
Calib3d.findChessboardCorners(grayChess, patternSize, corners, Calib3d.CALIB_CB_ADAPTIVE_THRESH + Calib3d.CALIB_CB_NORMALIZE_IMAGE
+ Calib3d.CALIB_CB_FAST_CHECK);
assertTrue(!corners.empty());
assertFalse(corners.empty());
}
public void testFind4QuadCornerSubpix() {
Size patternSize = new Size(9, 6);
MatOfPoint2f corners = new MatOfPoint2f();
Size region_size = new Size(5, 5);
Calib3d.findChessboardCorners(grayChess, patternSize, corners);
Calib3d.find4QuadCornerSubpix(grayChess, corners, region_size);
assertFalse(corners.empty());
}
public void testFindCirclesGridMatSizeMat() {
@@ -199,7 +210,7 @@ public class Calib3dTest extends OpenCVTestCase {
for (int i = 0; i < 5; i++)
for (int j = 0; j < 5; j++) {
Point pt = new Point(size * (2 * i + 1) / 10, size * (2 * j + 1) / 10);
Core.circle(img, pt, 10, new Scalar(0), -1);
Imgproc.circle(img, pt, 10, new Scalar(0), -1);
}
assertTrue(Calib3d.findCirclesGrid(img, new Size(5, 5), centers));
@@ -224,7 +235,7 @@ public class Calib3dTest extends OpenCVTestCase {
for (int i = 0; i < 3; i++)
for (int j = 0; j < 5; j++) {
Point pt = new Point(offsetx + (2 * i + j % 2) * step, offsety + step * j);
Core.circle(img, pt, 10, new Scalar(0), -1);
Imgproc.circle(img, pt, 10, new Scalar(0), -1);
}
assertTrue(Calib3d.findCirclesGrid(img, new Size(3, 5), centers, Calib3d.CALIB_CB_CLUSTERING
@@ -236,6 +247,8 @@ public class Calib3dTest extends OpenCVTestCase {
}
public void testFindFundamentalMatListOfPointListOfPoint() {
fail("Not yet implemented");
/*
int minFundamentalMatPoints = 8;
MatOfPoint2f pts = new MatOfPoint2f();
@@ -252,6 +265,7 @@ public class Calib3dTest extends OpenCVTestCase {
truth = new Mat(3, 3, CvType.CV_64F);
truth.put(0, 0, 0, -0.577, 0.288, 0.577, 0, 0.288, -0.288, -0.288, 0);
assertMatEqual(truth, fm, EPS);
*/
}
public void testFindFundamentalMatListOfPointListOfPointInt() {
@@ -496,7 +510,7 @@ public class Calib3dTest extends OpenCVTestCase {
}
public void testSolvePnPListOfPoint3ListOfPointMatMatMatMat() {
Mat intrinsics = Mat.eye(3, 3, CvType.CV_32F);
Mat intrinsics = Mat.eye(3, 3, CvType.CV_64F);
intrinsics.put(0, 0, 400);
intrinsics.put(1, 1, 400);
intrinsics.put(0, 2, 640 / 2);
@@ -599,4 +613,73 @@ public class Calib3dTest extends OpenCVTestCase {
Calib3d.computeCorrespondEpilines(left, 1, fundamental, lines);
assertMatEqual(truth, lines, EPS);
}
public void testConstants()
{
// calib3d.hpp: some constants have conflict with constants from 'fisheye' namespace
assertEquals(1, Calib3d.CALIB_USE_INTRINSIC_GUESS);
assertEquals(2, Calib3d.CALIB_FIX_ASPECT_RATIO);
assertEquals(4, Calib3d.CALIB_FIX_PRINCIPAL_POINT);
assertEquals(8, Calib3d.CALIB_ZERO_TANGENT_DIST);
assertEquals(16, Calib3d.CALIB_FIX_FOCAL_LENGTH);
assertEquals(32, Calib3d.CALIB_FIX_K1);
assertEquals(64, Calib3d.CALIB_FIX_K2);
assertEquals(128, Calib3d.CALIB_FIX_K3);
assertEquals(0x0800, Calib3d.CALIB_FIX_K4);
assertEquals(0x1000, Calib3d.CALIB_FIX_K5);
assertEquals(0x2000, Calib3d.CALIB_FIX_K6);
assertEquals(0x4000, Calib3d.CALIB_RATIONAL_MODEL);
assertEquals(0x8000, Calib3d.CALIB_THIN_PRISM_MODEL);
assertEquals(0x10000, Calib3d.CALIB_FIX_S1_S2_S3_S4);
assertEquals(0x40000, Calib3d.CALIB_TILTED_MODEL);
assertEquals(0x80000, Calib3d.CALIB_FIX_TAUX_TAUY);
assertEquals(0x100000, Calib3d.CALIB_USE_QR);
assertEquals(0x200000, Calib3d.CALIB_FIX_TANGENT_DIST);
assertEquals(0x100, Calib3d.CALIB_FIX_INTRINSIC);
assertEquals(0x200, Calib3d.CALIB_SAME_FOCAL_LENGTH);
assertEquals(0x400, Calib3d.CALIB_ZERO_DISPARITY);
assertEquals((1 << 17), Calib3d.CALIB_USE_LU);
assertEquals((1 << 22), Calib3d.CALIB_USE_EXTRINSIC_GUESS);
}
public void testSolvePnPGeneric_regression_16040() {
Mat intrinsics = Mat.eye(3, 3, CvType.CV_64F);
intrinsics.put(0, 0, 400);
intrinsics.put(1, 1, 400);
intrinsics.put(0, 2, 640 / 2);
intrinsics.put(1, 2, 480 / 2);
final int minPnpPointsNum = 4;
MatOfPoint3f points3d = new MatOfPoint3f();
points3d.alloc(minPnpPointsNum);
MatOfPoint2f points2d = new MatOfPoint2f();
points2d.alloc(minPnpPointsNum);
for (int i = 0; i < minPnpPointsNum; i++) {
double x = Math.random() * 100 - 50;
double y = Math.random() * 100 - 50;
points2d.put(i, 0, x, y); //add(new Point(x, y));
points3d.put(i, 0, 0, y, x); // add(new Point3(0, y, x));
}
ArrayList<Mat> rvecs = new ArrayList<Mat>();
ArrayList<Mat> tvecs = new ArrayList<Mat>();
Mat rvec = new Mat();
Mat tvec = new Mat();
Mat reprojectionError = new Mat(2, 1, CvType.CV_64FC1);
Calib3d.solvePnPGeneric(points3d, points2d, intrinsics, new MatOfDouble(), rvecs, tvecs, false, Calib3d.SOLVEPNP_IPPE, rvec, tvec, reprojectionError);
Mat truth_rvec = new Mat(3, 1, CvType.CV_64F);
truth_rvec.put(0, 0, 0, Math.PI / 2, 0);
Mat truth_tvec = new Mat(3, 1, CvType.CV_64F);
truth_tvec.put(0, 0, -320, -240, 400);
assertMatEqual(truth_rvec, rvecs.get(0), 10 * EPS);
assertMatEqual(truth_tvec, tvecs.get(0), 1000 * EPS);
}
}
@@ -0,0 +1,73 @@
#!/usr/bin/env python
'''
camera calibration for distorted images with chess board samples
reads distorted images, calculates the calibration and write undistorted images
'''
# Python 2/3 compatibility
from __future__ import print_function
import numpy as np
import cv2 as cv
from tests_common import NewOpenCVTests
class calibration_test(NewOpenCVTests):
def test_calibration(self):
img_names = []
for i in range(1, 15):
if i < 10:
img_names.append('samples/data/left0{}.jpg'.format(str(i)))
elif i != 10:
img_names.append('samples/data/left{}.jpg'.format(str(i)))
square_size = 1.0
pattern_size = (9, 6)
pattern_points = np.zeros((np.prod(pattern_size), 3), np.float32)
pattern_points[:, :2] = np.indices(pattern_size).T.reshape(-1, 2)
pattern_points *= square_size
obj_points = []
img_points = []
h, w = 0, 0
for fn in img_names:
img = self.get_sample(fn, 0)
if img is None:
continue
h, w = img.shape[:2]
found, corners = cv.findChessboardCorners(img, pattern_size)
if found:
term = (cv.TERM_CRITERIA_EPS + cv.TERM_CRITERIA_COUNT, 30, 0.1)
cv.cornerSubPix(img, corners, (5, 5), (-1, -1), term)
if not found:
continue
img_points.append(corners.reshape(-1, 2))
obj_points.append(pattern_points)
# calculate camera distortion
rms, camera_matrix, dist_coefs, _rvecs, _tvecs = cv.calibrateCamera(obj_points, img_points, (w, h), None, None, flags = 0)
eps = 0.01
normCamEps = 10.0
normDistEps = 0.05
cameraMatrixTest = [[ 532.80992189, 0., 342.4952186 ],
[ 0., 532.93346422, 233.8879292 ],
[ 0., 0., 1. ]]
distCoeffsTest = [ -2.81325576e-01, 2.91130406e-02,
1.21234330e-03, -1.40825372e-04, 1.54865844e-01]
self.assertLess(abs(rms - 0.196334638034), eps)
self.assertLess(cv.norm(camera_matrix - cameraMatrixTest, cv.NORM_L1), normCamEps)
self.assertLess(cv.norm(dist_coefs - distCoeffsTest, cv.NORM_L1), normDistEps)
if __name__ == '__main__':
NewOpenCVTests.bootstrap()
@@ -0,0 +1,64 @@
#!/usr/bin/env python
# Python 2/3 compatibility
from __future__ import print_function
import numpy as np
import cv2 as cv
from tests_common import NewOpenCVTests
class solvepnp_test(NewOpenCVTests):
def test_regression_16040(self):
obj_points = np.array([[0, 0, 0], [0, 1, 0], [1, 1, 0], [1, 0, 0]], dtype=np.float32)
img_points = np.array(
[[700, 400], [700, 600], [900, 600], [900, 400]], dtype=np.float32
)
cameraMatrix = np.array(
[[712.0634, 0, 800], [0, 712.540, 500], [0, 0, 1]], dtype=np.float32
)
distCoeffs = np.array([[0, 0, 0, 0]], dtype=np.float32)
r = np.array([], dtype=np.float32)
x, r, t, e = cv.solvePnPGeneric(
obj_points, img_points, cameraMatrix, distCoeffs, reprojectionError=r
)
def test_regression_16040_2(self):
obj_points = np.array([[0, 0, 0], [0, 1, 0], [1, 1, 0], [1, 0, 0]], dtype=np.float32)
img_points = np.array(
[[[700, 400], [700, 600], [900, 600], [900, 400]]], dtype=np.float32
)
cameraMatrix = np.array(
[[712.0634, 0, 800], [0, 712.540, 500], [0, 0, 1]], dtype=np.float32
)
distCoeffs = np.array([[0, 0, 0, 0]], dtype=np.float32)
r = np.array([], dtype=np.float32)
x, r, t, e = cv.solvePnPGeneric(
obj_points, img_points, cameraMatrix, distCoeffs, reprojectionError=r
)
def test_regression_16049(self):
obj_points = np.array([[0, 0, 0], [0, 1, 0], [1, 1, 0], [1, 0, 0]], dtype=np.float32)
img_points = np.array(
[[[700, 400], [700, 600], [900, 600], [900, 400]]], dtype=np.float32
)
cameraMatrix = np.array(
[[712.0634, 0, 800], [0, 712.540, 500], [0, 0, 1]], dtype=np.float32
)
distCoeffs = np.array([[0, 0, 0, 0]], dtype=np.float32)
x, r, t, e = cv.solvePnPGeneric(
obj_points, img_points, cameraMatrix, distCoeffs
)
if e is None:
# noArray() is supported, see https://github.com/opencv/opencv/issues/16049
pass
else:
eDump = cv.utils.dumpInputArray(e)
self.assertEqual(eDump, "InputArray: empty()=false kind=0x00010000 flags=0x01010000 total(-1)=1 dims(-1)=2 size(-1)=1x1 type(-1)=CV_32FC1")
if __name__ == '__main__':
NewOpenCVTests.bootstrap()
@@ -45,10 +45,10 @@
#ifdef HAVE_OPENCL
namespace cvtest {
namespace opencv_test {
namespace ocl {
typedef std::tr1::tuple<int, int> StereoBMFixture_t;
typedef tuple<int, int> StereoBMFixture_t;
typedef TestBaseWithParam<StereoBMFixture_t> StereoBMFixture;
OCL_PERF_TEST_P(StereoBMFixture, StereoBM, ::testing::Combine(OCL_PERF_ENUM(32, 64, 128), OCL_PERF_ENUM(11,21) ) )
@@ -63,7 +63,7 @@ OCL_PERF_TEST_P(StereoBMFixture, StereoBM, ::testing::Combine(OCL_PERF_ENUM(32,
declare.in(left, right);
Ptr<StereoBM> bm = createStereoBM( n_disp, winSize );
Ptr<StereoBM> bm = StereoBM::create( n_disp, winSize );
bm->setPreFilterType(bm->PREFILTER_XSOBEL);
bm->setTextureThreshold(0);
+165
View File
@@ -0,0 +1,165 @@
/*M///////////////////////////////////////////////////////////////////////////////////////
//
// By downloading, copying, installing or using the software you agree to this license.
// If you do not agree to this license, do not download, install,
// copy or use the software.
//
//
// License Agreement
// For Open Source Computer Vision Library
// (3-clause BSD License)
//
// Copyright (C) 2015-2016, OpenCV Foundation, all rights reserved.
// Third party copyrights are property of their respective owners.
//
// 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 names of the copyright holders nor the names of the 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 copyright holders 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.
//
//M*/
#include "perf_precomp.hpp"
#include <algorithm>
#include <functional>
namespace opencv_test
{
using namespace perf;
CV_ENUM(Method, RANSAC, LMEDS)
typedef tuple<int, double, Method, size_t> AffineParams;
typedef TestBaseWithParam<AffineParams> EstimateAffine;
#define ESTIMATE_PARAMS Combine(Values(100000, 5000, 100), Values(0.99, 0.95, 0.9), Method::all(), Values(10, 0))
static float rngIn(float from, float to) { return from + (to-from) * (float)theRNG(); }
static Mat rngPartialAffMat() {
double theta = rngIn(0, (float)CV_PI*2.f);
double scale = rngIn(0, 3);
double tx = rngIn(-2, 2);
double ty = rngIn(-2, 2);
double aff[2*3] = { std::cos(theta) * scale, -std::sin(theta) * scale, tx,
std::sin(theta) * scale, std::cos(theta) * scale, ty };
return Mat(2, 3, CV_64F, aff).clone();
}
PERF_TEST_P( EstimateAffine, EstimateAffine2D, ESTIMATE_PARAMS )
{
AffineParams params = GetParam();
const int n = get<0>(params);
const double confidence = get<1>(params);
const int method = get<2>(params);
const size_t refining = get<3>(params);
Mat aff(2, 3, CV_64F);
cv::randu(aff, -2., 2.);
// LMEDS can't handle more than 50% outliers (by design)
int m;
if (method == LMEDS)
m = 3*n/5;
else
m = 2*n/5;
const float shift_outl = 15.f;
const float noise_level = 20.f;
Mat fpts(1, n, CV_32FC2);
Mat tpts(1, n, CV_32FC2);
randu(fpts, 0., 100.);
transform(fpts, tpts, aff);
/* adding noise to some points */
Mat outliers = tpts.colRange(m, n);
outliers.reshape(1) += shift_outl;
Mat noise (outliers.size(), outliers.type());
randu(noise, 0., noise_level);
outliers += noise;
Mat aff_est;
vector<uchar> inliers (n);
warmup(inliers, WARMUP_WRITE);
warmup(fpts, WARMUP_READ);
warmup(tpts, WARMUP_READ);
TEST_CYCLE()
{
aff_est = estimateAffine2D(fpts, tpts, inliers, method, 3, 2000, confidence, refining);
}
// we already have accuracy tests
SANITY_CHECK_NOTHING();
}
PERF_TEST_P( EstimateAffine, EstimateAffinePartial2D, ESTIMATE_PARAMS )
{
AffineParams params = GetParam();
const int n = get<0>(params);
const double confidence = get<1>(params);
const int method = get<2>(params);
const size_t refining = get<3>(params);
Mat aff = rngPartialAffMat();
int m;
// LMEDS can't handle more than 50% outliers (by design)
if (method == LMEDS)
m = 3*n/5;
else
m = 2*n/5;
const float shift_outl = 15.f; const float noise_level = 20.f;
Mat fpts(1, n, CV_32FC2);
Mat tpts(1, n, CV_32FC2);
randu(fpts, 0., 100.);
transform(fpts, tpts, aff);
/* adding noise*/
Mat outliers = tpts.colRange(m, n);
outliers.reshape(1) += shift_outl;
Mat noise (outliers.size(), outliers.type());
randu(noise, 0., noise_level);
outliers += noise;
Mat aff_est;
vector<uchar> inliers (n);
warmup(inliers, WARMUP_WRITE);
warmup(fpts, WARMUP_READ);
warmup(tpts, WARMUP_READ);
TEST_CYCLE()
{
aff_est = estimateAffinePartial2D(fpts, tpts, inliers, method, 3, 2000, confidence, refining);
}
// we already have accuracy tests
SANITY_CHECK_NOTHING();
}
} // namespace opencv_test
+5 -5
View File
@@ -1,12 +1,10 @@
#include "perf_precomp.hpp"
using namespace std;
using namespace cv;
namespace opencv_test
{
using namespace perf;
using std::tr1::make_tuple;
using std::tr1::get;
typedef std::tr1::tuple<std::string, cv::Size> String_Size_t;
typedef tuple<std::string, cv::Size> String_Size_t;
typedef perf::TestBaseWithParam<String_Size_t> String_Size;
PERF_TEST_P(String_Size, asymm_circles_grid, testing::Values(
@@ -40,3 +38,5 @@ PERF_TEST_P(String_Size, asymm_circles_grid, testing::Values(
SANITY_CHECK(ptvec, 2);
}
} // namespace
+41 -34
View File
@@ -1,26 +1,20 @@
#include "perf_precomp.hpp"
#ifdef HAVE_TBB
#include "tbb/task_scheduler_init.h"
#endif
using namespace std;
using namespace cv;
namespace opencv_test
{
using namespace perf;
using std::tr1::make_tuple;
using std::tr1::get;
CV_ENUM(pnpAlgo, ITERATIVE, EPNP /*, P3P*/)
CV_ENUM(pnpAlgo, SOLVEPNP_ITERATIVE, SOLVEPNP_EPNP, SOLVEPNP_P3P, SOLVEPNP_DLS, SOLVEPNP_UPNP)
typedef std::tr1::tuple<int, pnpAlgo> PointsNum_Algo_t;
typedef tuple<int, pnpAlgo> PointsNum_Algo_t;
typedef perf::TestBaseWithParam<PointsNum_Algo_t> PointsNum_Algo;
typedef perf::TestBaseWithParam<int> PointsNum;
PERF_TEST_P(PointsNum_Algo, solvePnP,
testing::Combine(
testing::Values(/*4,*/ 3*9, 7*13), //TODO: find why results on 4 points are too unstable
testing::Values((int)ITERATIVE, (int)EPNP)
testing::Combine( //When non planar, DLT needs at least 6 points for SOLVEPNP_ITERATIVE flag
testing::Values(6, 3*9, 7*13), //TODO: find why results on 4 points are too unstable
testing::Values((int)SOLVEPNP_ITERATIVE, (int)SOLVEPNP_EPNP, (int)SOLVEPNP_UPNP, (int)SOLVEPNP_DLS)
)
)
{
@@ -48,22 +42,31 @@ PERF_TEST_P(PointsNum_Algo, solvePnP,
//add noise
Mat noise(1, (int)points2d.size(), CV_32FC2);
randu(noise, 0, 0.01);
add(points2d, noise, points2d);
cv::add(points2d, noise, points2d);
declare.in(points3d, points2d);
declare.time(100);
TEST_CYCLE_N(1000)
{
solvePnP(points3d, points2d, intrinsics, distortion, rvec, tvec, false, algo);
cv::solvePnP(points3d, points2d, intrinsics, distortion, rvec, tvec, false, algo);
}
SANITY_CHECK(rvec, 1e-6);
SANITY_CHECK(tvec, 1e-3);
SANITY_CHECK(rvec, 1e-4);
SANITY_CHECK(tvec, 1e-4);
}
PERF_TEST(PointsNum_Algo, solveP3P)
PERF_TEST_P(PointsNum_Algo, solvePnPSmallPoints,
testing::Combine(
testing::Values(5),
testing::Values((int)SOLVEPNP_P3P, (int)SOLVEPNP_EPNP, (int)SOLVEPNP_DLS, (int)SOLVEPNP_UPNP)
)
)
{
int pointsNum = 4;
int pointsNum = get<0>(GetParam());
pnpAlgo algo = get<1>(GetParam());
if( algo == SOLVEPNP_P3P )
pointsNum = 4;
vector<Point2f> points2d(pointsNum);
vector<Point3f> points3d(pointsNum);
@@ -72,8 +75,8 @@ PERF_TEST(PointsNum_Algo, solveP3P)
Mat distortion = Mat::zeros(5, 1, CV_32FC1);
Mat intrinsics = Mat::eye(3, 3, CV_32FC1);
intrinsics.at<float> (0, 0) = 400.0;
intrinsics.at<float> (1, 1) = 400.0;
intrinsics.at<float> (0, 0) = 400.0f;
intrinsics.at<float> (1, 1) = 400.0f;
intrinsics.at<float> (0, 2) = 640 / 2;
intrinsics.at<float> (1, 2) = 480 / 2;
@@ -81,26 +84,31 @@ PERF_TEST(PointsNum_Algo, solveP3P)
warmup(rvec, WARMUP_RNG);
warmup(tvec, WARMUP_RNG);
projectPoints(points3d, rvec, tvec, intrinsics, distortion, points2d);
// normalize Rodrigues vector
Mat rvec_tmp = Mat::eye(3, 3, CV_32F);
cv::Rodrigues(rvec, rvec_tmp);
cv::Rodrigues(rvec_tmp, rvec);
cv::projectPoints(points3d, rvec, tvec, intrinsics, distortion, points2d);
//add noise
Mat noise(1, (int)points2d.size(), CV_32FC2);
randu(noise, 0, 0.01);
add(points2d, noise, points2d);
randu(noise, -0.001, 0.001);
cv::add(points2d, noise, points2d);
declare.in(points3d, points2d);
declare.time(100);
TEST_CYCLE_N(1000)
{
solvePnP(points3d, points2d, intrinsics, distortion, rvec, tvec, false, P3P);
cv::solvePnP(points3d, points2d, intrinsics, distortion, rvec, tvec, false, algo);
}
SANITY_CHECK(rvec, 1e-6);
SANITY_CHECK(tvec, 1e-6);
SANITY_CHECK(rvec, 1e-1);
SANITY_CHECK(tvec, 1e-2);
}
PERF_TEST_P(PointsNum, DISABLED_SolvePnPRansac, testing::Values(4, 3*9, 7*13))
PERF_TEST_P(PointsNum, DISABLED_SolvePnPRansac, testing::Values(5, 3*9, 7*13))
{
int count = GetParam();
@@ -117,8 +125,10 @@ PERF_TEST_P(PointsNum, DISABLED_SolvePnPRansac, testing::Values(4, 3*9, 7*13))
Mat dist_coef(1, 8, CV_32F, cv::Scalar::all(0));
vector<cv::Point2f> image_vec;
Mat rvec_gold(1, 3, CV_32FC1);
randu(rvec_gold, 0, 1);
Mat tvec_gold(1, 3, CV_32FC1);
randu(tvec_gold, 0, 1);
projectPoints(object, rvec_gold, tvec_gold, camera_mat, dist_coef, image_vec);
@@ -128,16 +138,13 @@ PERF_TEST_P(PointsNum, DISABLED_SolvePnPRansac, testing::Values(4, 3*9, 7*13))
Mat rvec;
Mat tvec;
#ifdef HAVE_TBB
// limit concurrency to get deterministic result
tbb::task_scheduler_init one_thread(1);
#endif
TEST_CYCLE()
{
solvePnPRansac(object, image, camera_mat, dist_coef, rvec, tvec);
cv::solvePnPRansac(object, image, camera_mat, dist_coef, rvec, tvec);
}
SANITY_CHECK(rvec, 1e-6);
SANITY_CHECK(tvec, 1e-6);
}
} // namespace
+3 -14
View File
@@ -1,21 +1,10 @@
#ifdef __GNUC__
# pragma GCC diagnostic ignored "-Wmissing-declarations"
# if defined __clang__ || defined __APPLE__
# pragma GCC diagnostic ignored "-Wmissing-prototypes"
# pragma GCC diagnostic ignored "-Wextra"
# endif
#endif
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html
#ifndef __OPENCV_PERF_PRECOMP_HPP__
#define __OPENCV_PERF_PRECOMP_HPP__
#include "opencv2/ts.hpp"
#include "opencv2/calib3d.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/imgproc.hpp"
#ifdef GTEST_CREATE_SHARED_LIBRARY
#error no modules except ts should have GTEST_CREATE_SHARED_LIBRARY defined
#endif
#endif
+182
View File
@@ -0,0 +1,182 @@
/*
* By downloading, copying, installing or using the software you agree to this license.
* If you do not agree to this license, do not download, install,
* copy or use the software.
*
*
* License Agreement
* For Open Source Computer Vision Library
* (3 - clause BSD License)
*
* 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 names of the copyright holders nor the names of the 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 copyright holders 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.
*/
#include "perf_precomp.hpp"
namespace opencv_test
{
using namespace perf;
using namespace testing;
static void MakeArtificialExample(Mat& dst_left_view, Mat& dst_view);
CV_ENUM(SGBMModes, StereoSGBM::MODE_SGBM, StereoSGBM::MODE_SGBM_3WAY, StereoSGBM::MODE_HH4);
typedef tuple<Size, int, SGBMModes> SGBMParams;
typedef TestBaseWithParam<SGBMParams> TestStereoCorrespSGBM;
#ifndef _DEBUG
PERF_TEST_P( TestStereoCorrespSGBM, SGBM, Combine(Values(Size(1280,720),Size(640,480)), Values(256,128), SGBMModes::all()) )
#else
PERF_TEST_P( TestStereoCorrespSGBM, DISABLED_TooLongInDebug_SGBM, Combine(Values(Size(1280,720),Size(640,480)), Values(256,128), SGBMModes::all()) )
#endif
{
SGBMParams params = GetParam();
Size sz = get<0>(params);
int num_disparities = get<1>(params);
int mode = get<2>(params);
Mat src_left(sz, CV_8UC3);
Mat src_right(sz, CV_8UC3);
Mat dst(sz, CV_16S);
MakeArtificialExample(src_left,src_right);
int wsize = 3;
int P1 = 8*src_left.channels()*wsize*wsize;
TEST_CYCLE()
{
Ptr<StereoSGBM> sgbm = StereoSGBM::create(0,num_disparities,wsize,P1,4*P1,1,63,25,0,0,mode);
sgbm->compute(src_left,src_right,dst);
}
SANITY_CHECK(dst, .01, ERROR_RELATIVE);
}
typedef tuple<Size, int> BMParams;
typedef TestBaseWithParam<BMParams> TestStereoCorrespBM;
PERF_TEST_P(TestStereoCorrespBM, BM, Combine(Values(Size(1280, 720), Size(640, 480)), Values(256, 128)))
{
BMParams params = GetParam();
Size sz = get<0>(params);
int num_disparities = get<1>(params);
Mat src_left(sz, CV_8UC1);
Mat src_right(sz, CV_8UC1);
Mat dst(sz, CV_16S);
MakeArtificialExample(src_left, src_right);
int wsize = 21;
TEST_CYCLE()
{
Ptr<StereoBM> bm = StereoBM::create(num_disparities, wsize);
bm->compute(src_left, src_right, dst);
}
SANITY_CHECK(dst, .01, ERROR_RELATIVE);
}
void MakeArtificialExample(Mat& dst_left_view, Mat& dst_right_view)
{
RNG rng(0);
int w = dst_left_view.cols;
int h = dst_left_view.rows;
//params:
unsigned char bg_level = (unsigned char)rng.uniform(0.0,255.0);
unsigned char fg_level = (unsigned char)rng.uniform(0.0,255.0);
int rect_width = (int)rng.uniform(w/16,w/2);
int rect_height = (int)rng.uniform(h/16,h/2);
int rect_disparity = (int)(0.15*w);
double sigma = 3.0;
int rect_x_offset = (w-rect_width) /2;
int rect_y_offset = (h-rect_height)/2;
if(dst_left_view.channels()==3)
{
dst_left_view = Scalar(Vec3b(bg_level,bg_level,bg_level));
dst_right_view = Scalar(Vec3b(bg_level,bg_level,bg_level));
}
else
{
dst_left_view = Scalar(bg_level);
dst_right_view = Scalar(bg_level);
}
Mat dst_left_view_rect = Mat(dst_left_view, Rect(rect_x_offset,rect_y_offset,rect_width,rect_height));
if(dst_left_view.channels()==3)
dst_left_view_rect = Scalar(Vec3b(fg_level,fg_level,fg_level));
else
dst_left_view_rect = Scalar(fg_level);
rect_x_offset-=rect_disparity;
Mat dst_right_view_rect = Mat(dst_right_view, Rect(rect_x_offset,rect_y_offset,rect_width,rect_height));
if(dst_right_view.channels()==3)
dst_right_view_rect = Scalar(Vec3b(fg_level,fg_level,fg_level));
else
dst_right_view_rect = Scalar(fg_level);
//add some gaussian noise:
unsigned char *l, *r;
for(int i=0;i<h;i++)
{
l = dst_left_view.ptr(i);
r = dst_right_view.ptr(i);
if(dst_left_view.channels()==3)
{
for(int j=0;j<w;j++)
{
l[0] = saturate_cast<unsigned char>(l[0] + rng.gaussian(sigma));
l[1] = saturate_cast<unsigned char>(l[1] + rng.gaussian(sigma));
l[2] = saturate_cast<unsigned char>(l[2] + rng.gaussian(sigma));
l+=3;
r[0] = saturate_cast<unsigned char>(r[0] + rng.gaussian(sigma));
r[1] = saturate_cast<unsigned char>(r[1] + rng.gaussian(sigma));
r[2] = saturate_cast<unsigned char>(r[2] + rng.gaussian(sigma));
r+=3;
}
}
else
{
for(int j=0;j<w;j++)
{
l[0] = saturate_cast<unsigned char>(l[0] + rng.gaussian(sigma));
l++;
r[0] = saturate_cast<unsigned char>(r[0] + rng.gaussian(sigma));
r++;
}
}
}
}
}
+21
View File
@@ -0,0 +1,21 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html
#include "perf_precomp.hpp"
namespace opencv_test
{
using namespace perf;
PERF_TEST(Undistort, InitUndistortMap)
{
Size size_w_h(512 + 3, 512);
Mat k(3, 3, CV_32FC1);
Mat d(1, 14, CV_64FC1);
Mat dst(size_w_h, CV_32FC2);
declare.in(k, d, WARMUP_RNG).out(dst);
TEST_CYCLE() initUndistortRectifyMap(k, d, noArray(), k, size_w_h, CV_32FC2, dst, noArray());
SANITY_CHECK_NOTHING();
}
} // namespace
+453
View File
@@ -0,0 +1,453 @@
#include "precomp.hpp"
#include "ap3p.h"
#include <cmath>
#include <complex>
#if defined (_MSC_VER) && (_MSC_VER <= 1700)
static inline double cbrt(double x) { return (double)cv::cubeRoot((float)x); };
#endif
using namespace std;
namespace {
void solveQuartic(const double *factors, double *realRoots) {
const double &a4 = factors[0];
const double &a3 = factors[1];
const double &a2 = factors[2];
const double &a1 = factors[3];
const double &a0 = factors[4];
double a4_2 = a4 * a4;
double a3_2 = a3 * a3;
double a4_3 = a4_2 * a4;
double a2a4 = a2 * a4;
double p4 = (8 * a2a4 - 3 * a3_2) / (8 * a4_2);
double q4 = (a3_2 * a3 - 4 * a2a4 * a3 + 8 * a1 * a4_2) / (8 * a4_3);
double r4 = (256 * a0 * a4_3 - 3 * (a3_2 * a3_2) - 64 * a1 * a3 * a4_2 + 16 * a2a4 * a3_2) / (256 * (a4_3 * a4));
double p3 = ((p4 * p4) / 12 + r4) / 3; // /=-3
double q3 = (72 * r4 * p4 - 2 * p4 * p4 * p4 - 27 * q4 * q4) / 432; // /=2
double t; // *=2
complex<double> w;
if (q3 >= 0)
w = -sqrt(static_cast<complex<double> >(q3 * q3 - p3 * p3 * p3)) - q3;
else
w = sqrt(static_cast<complex<double> >(q3 * q3 - p3 * p3 * p3)) - q3;
if (w.imag() == 0.0) {
w.real(cbrt(w.real()));
t = 2.0 * (w.real() + p3 / w.real());
} else {
w = pow(w, 1.0 / 3);
t = 4.0 * w.real();
}
complex<double> sqrt_2m = sqrt(static_cast<complex<double> >(-2 * p4 / 3 + t));
double B_4A = -a3 / (4 * a4);
double complex1 = 4 * p4 / 3 + t;
#if defined(__clang__) && defined(__arm__) && (__clang_major__ == 3 || __clang_major__ == 4) && !defined(__ANDROID__)
// details: https://github.com/opencv/opencv/issues/11135
// details: https://github.com/opencv/opencv/issues/11056
complex<double> complex2 = 2 * q4;
complex2 = complex<double>(complex2.real() / sqrt_2m.real(), 0);
#else
complex<double> complex2 = 2 * q4 / sqrt_2m;
#endif
double sqrt_2m_rh = sqrt_2m.real() / 2;
double sqrt1 = sqrt(-(complex1 + complex2)).real() / 2;
realRoots[0] = B_4A + sqrt_2m_rh + sqrt1;
realRoots[1] = B_4A + sqrt_2m_rh - sqrt1;
double sqrt2 = sqrt(-(complex1 - complex2)).real() / 2;
realRoots[2] = B_4A - sqrt_2m_rh + sqrt2;
realRoots[3] = B_4A - sqrt_2m_rh - sqrt2;
}
void polishQuarticRoots(const double *coeffs, double *roots) {
const int iterations = 2;
for (int i = 0; i < iterations; ++i) {
for (int j = 0; j < 4; ++j) {
double error =
(((coeffs[0] * roots[j] + coeffs[1]) * roots[j] + coeffs[2]) * roots[j] + coeffs[3]) * roots[j] +
coeffs[4];
double
derivative =
((4 * coeffs[0] * roots[j] + 3 * coeffs[1]) * roots[j] + 2 * coeffs[2]) * roots[j] + coeffs[3];
roots[j] -= error / derivative;
}
}
}
inline void vect_cross(const double *a, const double *b, double *result) {
result[0] = a[1] * b[2] - a[2] * b[1];
result[1] = -(a[0] * b[2] - a[2] * b[0]);
result[2] = a[0] * b[1] - a[1] * b[0];
}
inline double vect_dot(const double *a, const double *b) {
return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
}
inline double vect_norm(const double *a) {
return sqrt(a[0] * a[0] + a[1] * a[1] + a[2] * a[2]);
}
inline void vect_scale(const double s, const double *a, double *result) {
result[0] = a[0] * s;
result[1] = a[1] * s;
result[2] = a[2] * s;
}
inline void vect_sub(const double *a, const double *b, double *result) {
result[0] = a[0] - b[0];
result[1] = a[1] - b[1];
result[2] = a[2] - b[2];
}
inline void vect_divide(const double *a, const double d, double *result) {
result[0] = a[0] / d;
result[1] = a[1] / d;
result[2] = a[2] / d;
}
inline void mat_mult(const double a[3][3], const double b[3][3], double result[3][3]) {
result[0][0] = a[0][0] * b[0][0] + a[0][1] * b[1][0] + a[0][2] * b[2][0];
result[0][1] = a[0][0] * b[0][1] + a[0][1] * b[1][1] + a[0][2] * b[2][1];
result[0][2] = a[0][0] * b[0][2] + a[0][1] * b[1][2] + a[0][2] * b[2][2];
result[1][0] = a[1][0] * b[0][0] + a[1][1] * b[1][0] + a[1][2] * b[2][0];
result[1][1] = a[1][0] * b[0][1] + a[1][1] * b[1][1] + a[1][2] * b[2][1];
result[1][2] = a[1][0] * b[0][2] + a[1][1] * b[1][2] + a[1][2] * b[2][2];
result[2][0] = a[2][0] * b[0][0] + a[2][1] * b[1][0] + a[2][2] * b[2][0];
result[2][1] = a[2][0] * b[0][1] + a[2][1] * b[1][1] + a[2][2] * b[2][1];
result[2][2] = a[2][0] * b[0][2] + a[2][1] * b[1][2] + a[2][2] * b[2][2];
}
}
namespace cv {
void ap3p::init_inverse_parameters() {
inv_fx = 1. / fx;
inv_fy = 1. / fy;
cx_fx = cx / fx;
cy_fy = cy / fy;
}
ap3p::ap3p(cv::Mat cameraMatrix) {
if (cameraMatrix.depth() == CV_32F)
init_camera_parameters<float>(cameraMatrix);
else
init_camera_parameters<double>(cameraMatrix);
init_inverse_parameters();
}
ap3p::ap3p(double _fx, double _fy, double _cx, double _cy) {
fx = _fx;
fy = _fy;
cx = _cx;
cy = _cy;
init_inverse_parameters();
}
// This algorithm is from "Tong Ke, Stergios Roumeliotis, An Efficient Algebraic Solution to the Perspective-Three-Point Problem" (Accepted by CVPR 2017)
// See https://arxiv.org/pdf/1701.08237.pdf
// featureVectors: The 3 bearing measurements (normalized) stored as column vectors
// worldPoints: The positions of the 3 feature points stored as column vectors
// solutionsR: 4 possible solutions of rotation matrix of the world w.r.t the camera frame
// solutionsT: 4 possible solutions of translation of the world origin w.r.t the camera frame
int ap3p::computePoses(const double featureVectors[3][4],
const double worldPoints[3][4],
double solutionsR[4][3][3],
double solutionsT[4][3],
bool p4p) {
//world point vectors
double w1[3] = {worldPoints[0][0], worldPoints[1][0], worldPoints[2][0]};
double w2[3] = {worldPoints[0][1], worldPoints[1][1], worldPoints[2][1]};
double w3[3] = {worldPoints[0][2], worldPoints[1][2], worldPoints[2][2]};
// k1
double u0[3];
vect_sub(w1, w2, u0);
double nu0 = vect_norm(u0);
double k1[3];
vect_divide(u0, nu0, k1);
// bi
double b1[3] = {featureVectors[0][0], featureVectors[1][0], featureVectors[2][0]};
double b2[3] = {featureVectors[0][1], featureVectors[1][1], featureVectors[2][1]};
double b3[3] = {featureVectors[0][2], featureVectors[1][2], featureVectors[2][2]};
// k3,tz
double k3[3];
vect_cross(b1, b2, k3);
double nk3 = vect_norm(k3);
vect_divide(k3, nk3, k3);
double tz[3];
vect_cross(b1, k3, tz);
// ui,vi
double v1[3];
vect_cross(b1, b3, v1);
double v2[3];
vect_cross(b2, b3, v2);
double u1[3];
vect_sub(w1, w3, u1);
// coefficients related terms
double u1k1 = vect_dot(u1, k1);
double k3b3 = vect_dot(k3, b3);
// f1i
double f11 = k3b3;
double f13 = vect_dot(k3, v1);
double f15 = -u1k1 * f11;
//delta
double nl[3];
vect_cross(u1, k1, nl);
double delta = vect_norm(nl);
vect_divide(nl, delta, nl);
f11 *= delta;
f13 *= delta;
// f2i
double u2k1 = u1k1 - nu0;
double f21 = vect_dot(tz, v2);
double f22 = nk3 * k3b3;
double f23 = vect_dot(k3, v2);
double f24 = u2k1 * f22;
double f25 = -u2k1 * f21;
f21 *= delta;
f22 *= delta;
f23 *= delta;
double g1 = f13 * f22;
double g2 = f13 * f25 - f15 * f23;
double g3 = f11 * f23 - f13 * f21;
double g4 = -f13 * f24;
double g5 = f11 * f22;
double g6 = f11 * f25 - f15 * f21;
double g7 = -f15 * f24;
double coeffs[5] = {g5 * g5 + g1 * g1 + g3 * g3,
2 * (g5 * g6 + g1 * g2 + g3 * g4),
g6 * g6 + 2 * g5 * g7 + g2 * g2 + g4 * g4 - g1 * g1 - g3 * g3,
2 * (g6 * g7 - g1 * g2 - g3 * g4),
g7 * g7 - g2 * g2 - g4 * g4};
double s[4];
solveQuartic(coeffs, s);
polishQuarticRoots(coeffs, s);
double temp[3];
vect_cross(k1, nl, temp);
double Ck1nl[3][3] =
{{k1[0], nl[0], temp[0]},
{k1[1], nl[1], temp[1]},
{k1[2], nl[2], temp[2]}};
double Cb1k3tzT[3][3] =
{{b1[0], b1[1], b1[2]},
{k3[0], k3[1], k3[2]},
{tz[0], tz[1], tz[2]}};
double b3p[3];
vect_scale((delta / k3b3), b3, b3p);
double X3 = worldPoints[0][3];
double Y3 = worldPoints[1][3];
double Z3 = worldPoints[2][3];
double mu3 = featureVectors[0][3];
double mv3 = featureVectors[1][3];
double reproj_errors[4];
int nb_solutions = 0;
for (int i = 0; i < 4; ++i) {
double ctheta1p = s[i];
if (abs(ctheta1p) > 1)
continue;
double stheta1p = sqrt(1 - ctheta1p * ctheta1p);
stheta1p = (k3b3 > 0) ? stheta1p : -stheta1p;
double ctheta3 = g1 * ctheta1p + g2;
double stheta3 = g3 * ctheta1p + g4;
double ntheta3 = stheta1p / ((g5 * ctheta1p + g6) * ctheta1p + g7);
ctheta3 *= ntheta3;
stheta3 *= ntheta3;
double C13[3][3] =
{{ctheta3, 0, -stheta3},
{stheta1p * stheta3, ctheta1p, stheta1p * ctheta3},
{ctheta1p * stheta3, -stheta1p, ctheta1p * ctheta3}};
double temp_matrix[3][3];
double R[3][3];
mat_mult(Ck1nl, C13, temp_matrix);
mat_mult(temp_matrix, Cb1k3tzT, R);
// R' * p3
double rp3[3] =
{w3[0] * R[0][0] + w3[1] * R[1][0] + w3[2] * R[2][0],
w3[0] * R[0][1] + w3[1] * R[1][1] + w3[2] * R[2][1],
w3[0] * R[0][2] + w3[1] * R[1][2] + w3[2] * R[2][2]};
double pxstheta1p[3];
vect_scale(stheta1p, b3p, pxstheta1p);
vect_sub(pxstheta1p, rp3, solutionsT[nb_solutions]);
solutionsR[nb_solutions][0][0] = R[0][0];
solutionsR[nb_solutions][1][0] = R[0][1];
solutionsR[nb_solutions][2][0] = R[0][2];
solutionsR[nb_solutions][0][1] = R[1][0];
solutionsR[nb_solutions][1][1] = R[1][1];
solutionsR[nb_solutions][2][1] = R[1][2];
solutionsR[nb_solutions][0][2] = R[2][0];
solutionsR[nb_solutions][1][2] = R[2][1];
solutionsR[nb_solutions][2][2] = R[2][2];
if (p4p) {
double X3p = solutionsR[nb_solutions][0][0] * X3 + solutionsR[nb_solutions][0][1] * Y3 + solutionsR[nb_solutions][0][2] * Z3 + solutionsT[nb_solutions][0];
double Y3p = solutionsR[nb_solutions][1][0] * X3 + solutionsR[nb_solutions][1][1] * Y3 + solutionsR[nb_solutions][1][2] * Z3 + solutionsT[nb_solutions][1];
double Z3p = solutionsR[nb_solutions][2][0] * X3 + solutionsR[nb_solutions][2][1] * Y3 + solutionsR[nb_solutions][2][2] * Z3 + solutionsT[nb_solutions][2];
double mu3p = X3p / Z3p;
double mv3p = Y3p / Z3p;
reproj_errors[nb_solutions] = (mu3p - mu3) * (mu3p - mu3) + (mv3p - mv3) * (mv3p - mv3);
}
nb_solutions++;
}
//sort the solutions
if (p4p) {
for (int i = 1; i < nb_solutions; i++) {
for (int j = i; j > 0 && reproj_errors[j-1] > reproj_errors[j]; j--) {
std::swap(reproj_errors[j], reproj_errors[j-1]);
std::swap(solutionsR[j], solutionsR[j-1]);
std::swap(solutionsT[j], solutionsT[j-1]);
}
}
}
return nb_solutions;
}
bool ap3p::solve(cv::Mat &R, cv::Mat &tvec, const cv::Mat &opoints, const cv::Mat &ipoints) {
CV_INSTRUMENT_REGION();
double rotation_matrix[3][3] = {}, translation[3] = {};
std::vector<double> points;
if (opoints.depth() == ipoints.depth()) {
if (opoints.depth() == CV_32F)
extract_points<cv::Point3f, cv::Point2f>(opoints, ipoints, points);
else
extract_points<cv::Point3d, cv::Point2d>(opoints, ipoints, points);
} else if (opoints.depth() == CV_32F)
extract_points<cv::Point3f, cv::Point2d>(opoints, ipoints, points);
else
extract_points<cv::Point3d, cv::Point2f>(opoints, ipoints, points);
bool result = solve(rotation_matrix, translation,
points[0], points[1], points[2], points[3], points[4],
points[5], points[6], points[7], points[8], points[9],
points[10], points[11], points[12], points[13],points[14],
points[15], points[16], points[17], points[18], points[19]);
cv::Mat(3, 1, CV_64F, translation).copyTo(tvec);
cv::Mat(3, 3, CV_64F, rotation_matrix).copyTo(R);
return result;
}
int ap3p::solve(std::vector<cv::Mat> &Rs, std::vector<cv::Mat> &tvecs, const cv::Mat &opoints, const cv::Mat &ipoints) {
CV_INSTRUMENT_REGION();
double rotation_matrix[4][3][3] = {}, translation[4][3] = {};
std::vector<double> points;
if (opoints.depth() == ipoints.depth()) {
if (opoints.depth() == CV_32F)
extract_points<cv::Point3f, cv::Point2f>(opoints, ipoints, points);
else
extract_points<cv::Point3d, cv::Point2d>(opoints, ipoints, points);
} else if (opoints.depth() == CV_32F)
extract_points<cv::Point3f, cv::Point2d>(opoints, ipoints, points);
else
extract_points<cv::Point3d, cv::Point2f>(opoints, ipoints, points);
const bool p4p = std::max(opoints.checkVector(3, CV_32F), opoints.checkVector(3, CV_64F)) == 4;
int solutions = solve(rotation_matrix, translation,
points[0], points[1], points[2], points[3], points[4],
points[5], points[6], points[7], points[8], points[9],
points[10], points[11], points[12], points[13], points[14],
points[15], points[16], points[17], points[18], points[19],
p4p);
for (int i = 0; i < solutions; i++) {
cv::Mat R, tvec;
cv::Mat(3, 1, CV_64F, translation[i]).copyTo(tvec);
cv::Mat(3, 3, CV_64F, rotation_matrix[i]).copyTo(R);
Rs.push_back(R);
tvecs.push_back(tvec);
}
return solutions;
}
bool
ap3p::solve(double R[3][3], double t[3],
double mu0, double mv0, double X0, double Y0, double Z0,
double mu1, double mv1, double X1, double Y1, double Z1,
double mu2, double mv2, double X2, double Y2, double Z2,
double mu3, double mv3, double X3, double Y3, double Z3) {
double Rs[4][3][3] = {}, ts[4][3] = {};
const bool p4p = true;
int n = solve(Rs, ts, mu0, mv0, X0, Y0, Z0, mu1, mv1, X1, Y1, Z1, mu2, mv2, X2, Y2, Z2, mu3, mv3, X3, Y3, Z3, p4p);
if (n == 0)
return false;
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++)
R[i][j] = Rs[0][i][j];
t[i] = ts[0][i];
}
return true;
}
int ap3p::solve(double R[4][3][3], double t[4][3],
double mu0, double mv0, double X0, double Y0, double Z0,
double mu1, double mv1, double X1, double Y1, double Z1,
double mu2, double mv2, double X2, double Y2, double Z2,
double mu3, double mv3, double X3, double Y3, double Z3,
bool p4p) {
double mk0, mk1, mk2;
double norm;
mu0 = inv_fx * mu0 - cx_fx;
mv0 = inv_fy * mv0 - cy_fy;
norm = sqrt(mu0 * mu0 + mv0 * mv0 + 1);
mk0 = 1. / norm;
mu0 *= mk0;
mv0 *= mk0;
mu1 = inv_fx * mu1 - cx_fx;
mv1 = inv_fy * mv1 - cy_fy;
norm = sqrt(mu1 * mu1 + mv1 * mv1 + 1);
mk1 = 1. / norm;
mu1 *= mk1;
mv1 *= mk1;
mu2 = inv_fx * mu2 - cx_fx;
mv2 = inv_fy * mv2 - cy_fy;
norm = sqrt(mu2 * mu2 + mv2 * mv2 + 1);
mk2 = 1. / norm;
mu2 *= mk2;
mv2 *= mk2;
mu3 = inv_fx * mu3 - cx_fx;
mv3 = inv_fy * mv3 - cy_fy;
double mk3 = 1; //not used
double featureVectors[3][4] = {{mu0, mu1, mu2, mu3},
{mv0, mv1, mv2, mv3},
{mk0, mk1, mk2, mk3}};
double worldPoints[3][4] = {{X0, X1, X2, X3},
{Y0, Y1, Y2, Y3},
{Z0, Z1, Z2, Z3}};
return computePoses(featureVectors, worldPoints, R, t, p4p);
}
}
+75
View File
@@ -0,0 +1,75 @@
#ifndef P3P_P3P_H
#define P3P_P3P_H
#include <opencv2/core.hpp>
namespace cv {
class ap3p {
private:
template<typename T>
void init_camera_parameters(const cv::Mat &cameraMatrix) {
cx = cameraMatrix.at<T>(0, 2);
cy = cameraMatrix.at<T>(1, 2);
fx = cameraMatrix.at<T>(0, 0);
fy = cameraMatrix.at<T>(1, 1);
}
template<typename OpointType, typename IpointType>
void extract_points(const cv::Mat &opoints, const cv::Mat &ipoints, std::vector<double> &points) {
points.clear();
int npoints = std::max(opoints.checkVector(3, CV_32F), opoints.checkVector(3, CV_64F));
points.resize(5*4); //resize vector to fit for p4p case
for (int i = 0; i < npoints; i++) {
points[i * 5] = ipoints.at<IpointType>(i).x * fx + cx;
points[i * 5 + 1] = ipoints.at<IpointType>(i).y * fy + cy;
points[i * 5 + 2] = opoints.at<OpointType>(i).x;
points[i * 5 + 3] = opoints.at<OpointType>(i).y;
points[i * 5 + 4] = opoints.at<OpointType>(i).z;
}
//Fill vectors with unused values for p3p case
for (int i = npoints; i < 4; i++) {
for (int j = 0; j < 5; j++) {
points[i * 5 + j] = 0;
}
}
}
void init_inverse_parameters();
double fx, fy, cx, cy;
double inv_fx, inv_fy, cx_fx, cy_fy;
public:
ap3p() : fx(0), fy(0), cx(0), cy(0), inv_fx(0), inv_fy(0), cx_fx(0), cy_fy(0) {}
ap3p(double fx, double fy, double cx, double cy);
ap3p(cv::Mat cameraMatrix);
bool solve(cv::Mat &R, cv::Mat &tvec, const cv::Mat &opoints, const cv::Mat &ipoints);
int solve(std::vector<cv::Mat> &Rs, std::vector<cv::Mat> &tvecs, const cv::Mat &opoints, const cv::Mat &ipoints);
int solve(double R[4][3][3], double t[4][3],
double mu0, double mv0, double X0, double Y0, double Z0,
double mu1, double mv1, double X1, double Y1, double Z1,
double mu2, double mv2, double X2, double Y2, double Z2,
double mu3, double mv3, double X3, double Y3, double Z3,
bool p4p);
bool solve(double R[3][3], double t[3],
double mu0, double mv0, double X0, double Y0, double Z0,
double mu1, double mv1, double X1, double Y1, double Z1,
double mu2, double mv2, double X2, double Y2, double Z2,
double mu3, double mv3, double X3, double Y3, double Z3);
// This algorithm is from "Tong Ke, Stergios Roumeliotis, An Efficient Algebraic Solution to the Perspective-Three-Point Problem" (Accepted by CVPR 2017)
// See https://arxiv.org/pdf/1701.08237.pdf
// featureVectors: 3 bearing measurements (normalized) stored as column vectors
// worldPoints: Positions of the 3 feature points stored as column vectors
// solutionsR: 4 possible solutions of rotation matrix of the world w.r.t the camera frame
// solutionsT: 4 possible solutions of translation of the world origin w.r.t the camera frame
int computePoses(const double featureVectors[3][4], const double worldPoints[3][4], double solutionsR[4][3][3],
double solutionsT[4][3], bool p4p);
};
}
#endif //P3P_P3P_H
+47
View File
@@ -0,0 +1,47 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
// This file contains wrappers for legacy OpenCV C API
#include "precomp.hpp"
#include "opencv2/calib3d/calib3d_c.h"
using namespace cv;
CV_IMPL void
cvDrawChessboardCorners(CvArr* _image, CvSize pattern_size,
CvPoint2D32f* corners, int count, int found)
{
CV_Assert(corners != NULL); //CV_CheckNULL(corners, "NULL is not allowed for 'corners' parameter");
Mat image = cvarrToMat(_image);
CV_StaticAssert(sizeof(CvPoint2D32f) == sizeof(Point2f), "");
drawChessboardCorners(image, pattern_size, Mat(1, count, traits::Type<Point2f>::value, corners), found != 0);
}
CV_IMPL int
cvFindChessboardCorners(const void* arr, CvSize pattern_size,
CvPoint2D32f* out_corners_, int* out_corner_count,
int flags)
{
if (!out_corners_)
CV_Error( CV_StsNullPtr, "Null pointer to corners" );
Mat image = cvarrToMat(arr);
std::vector<Point2f> out_corners;
if (out_corner_count)
*out_corner_count = 0;
bool res = cv::findChessboardCorners(image, pattern_size, out_corners, flags);
int corner_count = (int)out_corners.size();
if (out_corner_count)
*out_corner_count = corner_count;
CV_CheckLE(corner_count, Size(pattern_size).area(), "Unexpected number of corners");
for (int i = 0; i < corner_count; ++i)
{
out_corners_[i] = cvPoint2D32f(out_corners[i]);
}
return res ? 1 : 0;
}
-66
View File
@@ -1,66 +0,0 @@
/*M///////////////////////////////////////////////////////////////////////////////////////
//
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
// By downloading, copying, installing or using the software you agree to this license.
// If you do not agree to this license, do not download, install,
// copy or use the software.
//
//
// License Agreement
// For Open Source Computer Vision Library
//
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistribution's of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistribution's 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.
//
// * The name of the copyright holders may not 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 Intel Corporation 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.
//
//M*/
#include "precomp.hpp"
using namespace cv;
//////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////
#if 0
bool cv::initModule_calib3d(void)
{
bool all = true;
all &= !RANSACPointSetRegistrator_info_auto.name().empty();
all &= !LMeDSPointSetRegistrator_info_auto.name().empty();
all &= !LMSolverImpl_info_auto.name().empty();
return all;
}
#endif
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+770
View File
@@ -0,0 +1,770 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
#include "precomp.hpp"
#include "opencv2/calib3d.hpp"
namespace cv {
static Mat homogeneousInverse(const Mat& T)
{
CV_Assert(T.rows == 4 && T.cols == 4);
Mat R = T(Rect(0, 0, 3, 3));
Mat t = T(Rect(3, 0, 1, 3));
Mat Rt = R.t();
Mat tinv = -Rt * t;
Mat Tinv = Mat::eye(4, 4, T.type());
Rt.copyTo(Tinv(Rect(0, 0, 3, 3)));
tinv.copyTo(Tinv(Rect(3, 0, 1, 3)));
return Tinv;
}
// q = rot2quatMinimal(R)
//
// R - 3x3 rotation matrix, or 4x4 homogeneous matrix
// q - 3x1 unit quaternion <qx, qy, qz>
// q = sin(theta/2) * v
// theta - rotation angle
// v - unit rotation axis, |v| = 1
static Mat rot2quatMinimal(const Mat& R)
{
CV_Assert(R.type() == CV_64FC1 && R.rows >= 3 && R.cols >= 3);
double m00 = R.at<double>(0,0), m01 = R.at<double>(0,1), m02 = R.at<double>(0,2);
double m10 = R.at<double>(1,0), m11 = R.at<double>(1,1), m12 = R.at<double>(1,2);
double m20 = R.at<double>(2,0), m21 = R.at<double>(2,1), m22 = R.at<double>(2,2);
double trace = m00 + m11 + m22;
double qx, qy, qz;
if (trace > 0) {
double S = sqrt(trace + 1.0) * 2; // S=4*qw
qx = (m21 - m12) / S;
qy = (m02 - m20) / S;
qz = (m10 - m01) / S;
} else if ((m00 > m11)&(m00 > m22)) {
double S = sqrt(1.0 + m00 - m11 - m22) * 2; // S=4*qx
qx = 0.25 * S;
qy = (m01 + m10) / S;
qz = (m02 + m20) / S;
} else if (m11 > m22) {
double S = sqrt(1.0 + m11 - m00 - m22) * 2; // S=4*qy
qx = (m01 + m10) / S;
qy = 0.25 * S;
qz = (m12 + m21) / S;
} else {
double S = sqrt(1.0 + m22 - m00 - m11) * 2; // S=4*qz
qx = (m02 + m20) / S;
qy = (m12 + m21) / S;
qz = 0.25 * S;
}
return (Mat_<double>(3,1) << qx, qy, qz);
}
static Mat skew(const Mat& v)
{
CV_Assert(v.type() == CV_64FC1 && v.rows == 3 && v.cols == 1);
double vx = v.at<double>(0,0);
double vy = v.at<double>(1,0);
double vz = v.at<double>(2,0);
return (Mat_<double>(3,3) << 0, -vz, vy,
vz, 0, -vx,
-vy, vx, 0);
}
// R = quatMinimal2rot(q)
//
// q - 3x1 unit quaternion <qx, qy, qz>
// R - 3x3 rotation matrix
// q = sin(theta/2) * v
// theta - rotation angle
// v - unit rotation axis, |v| = 1
static Mat quatMinimal2rot(const Mat& q)
{
CV_Assert(q.type() == CV_64FC1 && q.rows == 3 && q.cols == 1);
Mat p = q.t()*q;
double w = sqrt(1 - p.at<double>(0,0));
Mat diag_p = Mat::eye(3,3,CV_64FC1)*p.at<double>(0,0);
return 2*q*q.t() + 2*w*skew(q) + Mat::eye(3,3,CV_64FC1) - 2*diag_p;
}
// q = rot2quat(R)
//
// q - 4x1 unit quaternion <qw, qx, qy, qz>
// R - 3x3 rotation matrix
static Mat rot2quat(const Mat& R)
{
CV_Assert(R.type() == CV_64FC1 && R.rows >= 3 && R.cols >= 3);
double m00 = R.at<double>(0,0), m01 = R.at<double>(0,1), m02 = R.at<double>(0,2);
double m10 = R.at<double>(1,0), m11 = R.at<double>(1,1), m12 = R.at<double>(1,2);
double m20 = R.at<double>(2,0), m21 = R.at<double>(2,1), m22 = R.at<double>(2,2);
double trace = m00 + m11 + m22;
double qw, qx, qy, qz;
if (trace > 0) {
double S = sqrt(trace + 1.0) * 2; // S=4*qw
qw = 0.25 * S;
qx = (m21 - m12) / S;
qy = (m02 - m20) / S;
qz = (m10 - m01) / S;
} else if ((m00 > m11)&(m00 > m22)) {
double S = sqrt(1.0 + m00 - m11 - m22) * 2; // S=4*qx
qw = (m21 - m12) / S;
qx = 0.25 * S;
qy = (m01 + m10) / S;
qz = (m02 + m20) / S;
} else if (m11 > m22) {
double S = sqrt(1.0 + m11 - m00 - m22) * 2; // S=4*qy
qw = (m02 - m20) / S;
qx = (m01 + m10) / S;
qy = 0.25 * S;
qz = (m12 + m21) / S;
} else {
double S = sqrt(1.0 + m22 - m00 - m11) * 2; // S=4*qz
qw = (m10 - m01) / S;
qx = (m02 + m20) / S;
qy = (m12 + m21) / S;
qz = 0.25 * S;
}
return (Mat_<double>(4,1) << qw, qx, qy, qz);
}
// R = quat2rot(q)
//
// q - 4x1 unit quaternion <qw, qx, qy, qz>
// R - 3x3 rotation matrix
static Mat quat2rot(const Mat& q)
{
CV_Assert(q.type() == CV_64FC1 && q.rows == 4 && q.cols == 1);
double qw = q.at<double>(0,0);
double qx = q.at<double>(1,0);
double qy = q.at<double>(2,0);
double qz = q.at<double>(3,0);
Mat R(3, 3, CV_64FC1);
R.at<double>(0, 0) = 1 - 2*qy*qy - 2*qz*qz;
R.at<double>(0, 1) = 2*qx*qy - 2*qz*qw;
R.at<double>(0, 2) = 2*qx*qz + 2*qy*qw;
R.at<double>(1, 0) = 2*qx*qy + 2*qz*qw;
R.at<double>(1, 1) = 1 - 2*qx*qx - 2*qz*qz;
R.at<double>(1, 2) = 2*qy*qz - 2*qx*qw;
R.at<double>(2, 0) = 2*qx*qz - 2*qy*qw;
R.at<double>(2, 1) = 2*qy*qz + 2*qx*qw;
R.at<double>(2, 2) = 1 - 2*qx*qx - 2*qy*qy;
return R;
}
// Kronecker product or tensor product
// https://stackoverflow.com/a/36552682
static Mat kron(const Mat& A, const Mat& B)
{
CV_Assert(A.channels() == 1 && B.channels() == 1);
Mat1d Ad, Bd;
A.convertTo(Ad, CV_64F);
B.convertTo(Bd, CV_64F);
Mat1d Kd(Ad.rows * Bd.rows, Ad.cols * Bd.cols, 0.0);
for (int ra = 0; ra < Ad.rows; ra++)
{
for (int ca = 0; ca < Ad.cols; ca++)
{
Kd(Range(ra*Bd.rows, (ra + 1)*Bd.rows), Range(ca*Bd.cols, (ca + 1)*Bd.cols)) = Bd.mul(Ad(ra, ca));
}
}
Mat K;
Kd.convertTo(K, A.type());
return K;
}
// quaternion multiplication
static Mat qmult(const Mat& s, const Mat& t)
{
CV_Assert(s.type() == CV_64FC1 && t.type() == CV_64FC1);
CV_Assert(s.rows == 4 && s.cols == 1);
CV_Assert(t.rows == 4 && t.cols == 1);
double s0 = s.at<double>(0,0);
double s1 = s.at<double>(1,0);
double s2 = s.at<double>(2,0);
double s3 = s.at<double>(3,0);
double t0 = t.at<double>(0,0);
double t1 = t.at<double>(1,0);
double t2 = t.at<double>(2,0);
double t3 = t.at<double>(3,0);
Mat q(4, 1, CV_64FC1);
q.at<double>(0,0) = s0*t0 - s1*t1 - s2*t2 - s3*t3;
q.at<double>(1,0) = s0*t1 + s1*t0 + s2*t3 - s3*t2;
q.at<double>(2,0) = s0*t2 - s1*t3 + s2*t0 + s3*t1;
q.at<double>(3,0) = s0*t3 + s1*t2 - s2*t1 + s3*t0;
return q;
}
// dq = homogeneous2dualQuaternion(H)
//
// H - 4x4 homogeneous transformation: [R | t; 0 0 0 | 1]
// dq - 8x1 dual quaternion: <q (rotation part), qprime (translation part)>
static Mat homogeneous2dualQuaternion(const Mat& H)
{
CV_Assert(H.type() == CV_64FC1 && H.rows == 4 && H.cols == 4);
Mat dualq(8, 1, CV_64FC1);
Mat R = H(Rect(0, 0, 3, 3));
Mat t = H(Rect(3, 0, 1, 3));
Mat q = rot2quat(R);
Mat qt = Mat::zeros(4, 1, CV_64FC1);
t.copyTo(qt(Rect(0, 1, 1, 3)));
Mat qprime = 0.5 * qmult(qt, q);
q.copyTo(dualq(Rect(0, 0, 1, 4)));
qprime.copyTo(dualq(Rect(0, 4, 1, 4)));
return dualq;
}
// H = dualQuaternion2homogeneous(dq)
//
// H - 4x4 homogeneous transformation: [R | t; 0 0 0 | 1]
// dq - 8x1 dual quaternion: <q (rotation part), qprime (translation part)>
static Mat dualQuaternion2homogeneous(const Mat& dualq)
{
CV_Assert(dualq.type() == CV_64FC1 && dualq.rows == 8 && dualq.cols == 1);
Mat q = dualq(Rect(0, 0, 1, 4));
Mat qprime = dualq(Rect(0, 4, 1, 4));
Mat R = quat2rot(q);
q.at<double>(1,0) = -q.at<double>(1,0);
q.at<double>(2,0) = -q.at<double>(2,0);
q.at<double>(3,0) = -q.at<double>(3,0);
Mat qt = 2*qmult(qprime, q);
Mat t = qt(Rect(0, 1, 1, 3));
Mat H = Mat::eye(4, 4, CV_64FC1);
R.copyTo(H(Rect(0, 0, 3, 3)));
t.copyTo(H(Rect(3, 0, 1, 3)));
return H;
}
//Reference:
//R. Y. Tsai and R. K. Lenz, "A new technique for fully autonomous and efficient 3D robotics hand/eye calibration."
//In IEEE Transactions on Robotics and Automation, vol. 5, no. 3, pp. 345-358, June 1989.
//C++ code converted from Zoran Lazarevic's Matlab code:
//http://lazax.com/www.cs.columbia.edu/~laza/html/Stewart/matlab/handEye.m
static void calibrateHandEyeTsai(const std::vector<Mat>& Hg, const std::vector<Mat>& Hc,
Mat& R_cam2gripper, Mat& t_cam2gripper)
{
//Number of unique camera position pairs
int K = static_cast<int>((Hg.size()*Hg.size() - Hg.size()) / 2.0);
//Will store: skew(Pgij+Pcij)
Mat A(3*K, 3, CV_64FC1);
//Will store: Pcij - Pgij
Mat B(3*K, 1, CV_64FC1);
std::vector<Mat> vec_Hgij, vec_Hcij;
vec_Hgij.reserve(static_cast<size_t>(K));
vec_Hcij.reserve(static_cast<size_t>(K));
int idx = 0;
for (size_t i = 0; i < Hg.size(); i++)
{
for (size_t j = i+1; j < Hg.size(); j++, idx++)
{
//Defines coordinate transformation from Gi to Gj
//Hgi is from Gi (gripper) to RW (robot base)
//Hgj is from Gj (gripper) to RW (robot base)
Mat Hgij = homogeneousInverse(Hg[j]) * Hg[i]; //eq 6
vec_Hgij.push_back(Hgij);
//Rotation axis for Rgij which is the 3D rotation from gripper coordinate frame Gi to Gj
Mat Pgij = 2*rot2quatMinimal(Hgij);
//Defines coordinate transformation from Ci to Cj
//Hci is from CW (calibration target) to Ci (camera)
//Hcj is from CW (calibration target) to Cj (camera)
Mat Hcij = Hc[j] * homogeneousInverse(Hc[i]); //eq 7
vec_Hcij.push_back(Hcij);
//Rotation axis for Rcij
Mat Pcij = 2*rot2quatMinimal(Hcij);
//Left-hand side: skew(Pgij+Pcij)
skew(Pgij+Pcij).copyTo(A(Rect(0, idx*3, 3, 3)));
//Right-hand side: Pcij - Pgij
Mat diff = Pcij - Pgij;
diff.copyTo(B(Rect(0, idx*3, 1, 3)));
}
}
Mat Pcg_;
//Rotation from camera to gripper is obtained from the set of equations:
// skew(Pgij+Pcij) * Pcg_ = Pcij - Pgij (eq 12)
solve(A, B, Pcg_, DECOMP_SVD);
Mat Pcg_norm = Pcg_.t() * Pcg_;
//Obtained non-unit quaternion is scaled back to unit value that
//designates camera-gripper rotation
Mat Pcg = 2 * Pcg_ / sqrt(1 + Pcg_norm.at<double>(0,0)); //eq 14
Mat Rcg = quatMinimal2rot(Pcg/2.0);
idx = 0;
for (size_t i = 0; i < Hg.size(); i++)
{
for (size_t j = i+1; j < Hg.size(); j++, idx++)
{
//Defines coordinate transformation from Gi to Gj
//Hgi is from Gi (gripper) to RW (robot base)
//Hgj is from Gj (gripper) to RW (robot base)
Mat Hgij = vec_Hgij[static_cast<size_t>(idx)];
//Defines coordinate transformation from Ci to Cj
//Hci is from CW (calibration target) to Ci (camera)
//Hcj is from CW (calibration target) to Cj (camera)
Mat Hcij = vec_Hcij[static_cast<size_t>(idx)];
//Left-hand side: (Rgij - I)
Mat diff = Hgij(Rect(0,0,3,3)) - Mat::eye(3,3,CV_64FC1);
diff.copyTo(A(Rect(0, idx*3, 3, 3)));
//Right-hand side: Rcg*Tcij - Tgij
diff = Rcg*Hcij(Rect(3, 0, 1, 3)) - Hgij(Rect(3, 0, 1, 3));
diff.copyTo(B(Rect(0, idx*3, 1, 3)));
}
}
Mat Tcg;
//Translation from camera to gripper is obtained from the set of equations:
// (Rgij - I) * Tcg = Rcg*Tcij - Tgij (eq 15)
solve(A, B, Tcg, DECOMP_SVD);
R_cam2gripper = Rcg;
t_cam2gripper = Tcg;
}
//Reference:
//F. Park, B. Martin, "Robot Sensor Calibration: Solving AX = XB on the Euclidean Group."
//In IEEE Transactions on Robotics and Automation, 10(5): 717-721, 1994.
//Matlab code: http://math.loyola.edu/~mili/Calibration/
static void calibrateHandEyePark(const std::vector<Mat>& Hg, const std::vector<Mat>& Hc,
Mat& R_cam2gripper, Mat& t_cam2gripper)
{
Mat M = Mat::zeros(3, 3, CV_64FC1);
for (size_t i = 0; i < Hg.size(); i++)
{
for (size_t j = i+1; j < Hg.size(); j++)
{
Mat Hgij = homogeneousInverse(Hg[j]) * Hg[i];
Mat Hcij = Hc[j] * homogeneousInverse(Hc[i]);
Mat Rgij = Hgij(Rect(0, 0, 3, 3));
Mat Rcij = Hcij(Rect(0, 0, 3, 3));
Mat a, b;
Rodrigues(Rgij, a);
Rodrigues(Rcij, b);
M += b * a.t();
}
}
Mat eigenvalues, eigenvectors;
eigen(M.t()*M, eigenvalues, eigenvectors);
Mat v = Mat::zeros(3, 3, CV_64FC1);
for (int i = 0; i < 3; i++) {
v.at<double>(i,i) = 1.0 / sqrt(eigenvalues.at<double>(i,0));
}
Mat R = eigenvectors.t() * v * eigenvectors * M.t();
R_cam2gripper = R;
int K = static_cast<int>((Hg.size()*Hg.size() - Hg.size()) / 2.0);
Mat C(3*K, 3, CV_64FC1);
Mat d(3*K, 1, CV_64FC1);
Mat I3 = Mat::eye(3, 3, CV_64FC1);
int idx = 0;
for (size_t i = 0; i < Hg.size(); i++)
{
for (size_t j = i+1; j < Hg.size(); j++, idx++)
{
Mat Hgij = homogeneousInverse(Hg[j]) * Hg[i];
Mat Hcij = Hc[j] * homogeneousInverse(Hc[i]);
Mat Rgij = Hgij(Rect(0, 0, 3, 3));
Mat tgij = Hgij(Rect(3, 0, 1, 3));
Mat tcij = Hcij(Rect(3, 0, 1, 3));
Mat I_tgij = I3 - Rgij;
I_tgij.copyTo(C(Rect(0, 3*idx, 3, 3)));
Mat A_RB = tgij - R*tcij;
A_RB.copyTo(d(Rect(0, 3*idx, 1, 3)));
}
}
Mat t;
solve(C, d, t, DECOMP_SVD);
t_cam2gripper = t;
}
//Reference:
//R. Horaud, F. Dornaika, "Hand-Eye Calibration"
//In International Journal of Robotics Research, 14(3): 195-210, 1995.
//Matlab code: http://math.loyola.edu/~mili/Calibration/
static void calibrateHandEyeHoraud(const std::vector<Mat>& Hg, const std::vector<Mat>& Hc,
Mat& R_cam2gripper, Mat& t_cam2gripper)
{
Mat A = Mat::zeros(4, 4, CV_64FC1);
for (size_t i = 0; i < Hg.size(); i++)
{
for (size_t j = i+1; j < Hg.size(); j++)
{
Mat Hgij = homogeneousInverse(Hg[j]) * Hg[i];
Mat Hcij = Hc[j] * homogeneousInverse(Hc[i]);
Mat Rgij = Hgij(Rect(0, 0, 3, 3));
Mat Rcij = Hcij(Rect(0, 0, 3, 3));
Mat qgij = rot2quat(Rgij);
double r0 = qgij.at<double>(0,0);
double rx = qgij.at<double>(1,0);
double ry = qgij.at<double>(2,0);
double rz = qgij.at<double>(3,0);
// Q(r) Appendix A
Matx44d Qvi(r0, -rx, -ry, -rz,
rx, r0, -rz, ry,
ry, rz, r0, -rx,
rz, -ry, rx, r0);
Mat qcij = rot2quat(Rcij);
r0 = qcij.at<double>(0,0);
rx = qcij.at<double>(1,0);
ry = qcij.at<double>(2,0);
rz = qcij.at<double>(3,0);
// W(r) Appendix A
Matx44d Wvi(r0, -rx, -ry, -rz,
rx, r0, rz, -ry,
ry, -rz, r0, rx,
rz, ry, -rx, r0);
// Ai = (Q(vi') - W(vi))^T (Q(vi') - W(vi))
A += (Qvi - Wvi).t() * (Qvi - Wvi);
}
}
Mat eigenvalues, eigenvectors;
eigen(A, eigenvalues, eigenvectors);
Mat R = quat2rot(eigenvectors.row(3).t());
R_cam2gripper = R;
int K = static_cast<int>((Hg.size()*Hg.size() - Hg.size()) / 2.0);
Mat C(3*K, 3, CV_64FC1);
Mat d(3*K, 1, CV_64FC1);
Mat I3 = Mat::eye(3, 3, CV_64FC1);
int idx = 0;
for (size_t i = 0; i < Hg.size(); i++)
{
for (size_t j = i+1; j < Hg.size(); j++, idx++)
{
Mat Hgij = homogeneousInverse(Hg[j]) * Hg[i];
Mat Hcij = Hc[j] * homogeneousInverse(Hc[i]);
Mat Rgij = Hgij(Rect(0, 0, 3, 3));
Mat tgij = Hgij(Rect(3, 0, 1, 3));
Mat tcij = Hcij(Rect(3, 0, 1, 3));
Mat I_tgij = I3 - Rgij;
I_tgij.copyTo(C(Rect(0, 3*idx, 3, 3)));
Mat A_RB = tgij - R*tcij;
A_RB.copyTo(d(Rect(0, 3*idx, 1, 3)));
}
}
Mat t;
solve(C, d, t, DECOMP_SVD);
t_cam2gripper = t;
}
// sign function, return -1 if negative values, +1 otherwise
static int sign_double(double val)
{
return (0 < val) - (val < 0);
}
//Reference:
//N. Andreff, R. Horaud, B. Espiau, "On-line Hand-Eye Calibration."
//In Second International Conference on 3-D Digital Imaging and Modeling (3DIM'99), pages 430-436, 1999.
//Matlab code: http://math.loyola.edu/~mili/Calibration/
static void calibrateHandEyeAndreff(const std::vector<Mat>& Hg, const std::vector<Mat>& Hc,
Mat& R_cam2gripper, Mat& t_cam2gripper)
{
int K = static_cast<int>((Hg.size()*Hg.size() - Hg.size()) / 2.0);
Mat A(12*K, 12, CV_64FC1);
Mat B(12*K, 1, CV_64FC1);
Mat I9 = Mat::eye(9, 9, CV_64FC1);
Mat I3 = Mat::eye(3, 3, CV_64FC1);
Mat O9x3 = Mat::zeros(9, 3, CV_64FC1);
Mat O9x1 = Mat::zeros(9, 1, CV_64FC1);
int idx = 0;
for (size_t i = 0; i < Hg.size(); i++)
{
for (size_t j = i+1; j < Hg.size(); j++, idx++)
{
Mat Hgij = homogeneousInverse(Hg[j]) * Hg[i];
Mat Hcij = Hc[j] * homogeneousInverse(Hc[i]);
Mat Rgij = Hgij(Rect(0, 0, 3, 3));
Mat Rcij = Hcij(Rect(0, 0, 3, 3));
Mat tgij = Hgij(Rect(3, 0, 1, 3));
Mat tcij = Hcij(Rect(3, 0, 1, 3));
//Eq 10
Mat a00 = I9 - kron(Rgij, Rcij);
Mat a01 = O9x3;
Mat a10 = kron(I3, tcij.t());
Mat a11 = I3 - Rgij;
a00.copyTo(A(Rect(0, idx*12, 9, 9)));
a01.copyTo(A(Rect(9, idx*12, 3, 9)));
a10.copyTo(A(Rect(0, idx*12 + 9, 9, 3)));
a11.copyTo(A(Rect(9, idx*12 + 9, 3, 3)));
O9x1.copyTo(B(Rect(0, idx*12, 1, 9)));
tgij.copyTo(B(Rect(0, idx*12 + 9, 1, 3)));
}
}
Mat X;
solve(A, B, X, DECOMP_SVD);
Mat R = X(Rect(0, 0, 1, 9));
int newSize[] = {3, 3};
R = R.reshape(1, 2, newSize);
//Eq 15
double det = determinant(R);
R = pow(sign_double(det) / abs(det), 1.0/3.0) * R;
Mat w, u, vt;
SVDecomp(R, w, u, vt);
R = u*vt;
if (determinant(R) < 0)
{
Mat diag = (Mat_<double>(3,3) << 1.0, 0.0, 0.0,
0.0, 1.0, 0.0,
0.0, 0.0, -1.0);
R = u*diag*vt;
}
R_cam2gripper = R;
Mat t = X(Rect(0, 9, 1, 3));
t_cam2gripper = t;
}
//Reference:
//K. Daniilidis, "Hand-Eye Calibration Using Dual Quaternions."
//In The International Journal of Robotics Research,18(3): 286-298, 1998.
//Matlab code: http://math.loyola.edu/~mili/Calibration/
static void calibrateHandEyeDaniilidis(const std::vector<Mat>& Hg, const std::vector<Mat>& Hc,
Mat& R_cam2gripper, Mat& t_cam2gripper)
{
int K = static_cast<int>((Hg.size()*Hg.size() - Hg.size()) / 2.0);
Mat T = Mat::zeros(6*K, 8, CV_64FC1);
int idx = 0;
for (size_t i = 0; i < Hg.size(); i++)
{
for (size_t j = i+1; j < Hg.size(); j++, idx++)
{
Mat Hgij = homogeneousInverse(Hg[j]) * Hg[i];
Mat Hcij = Hc[j] * homogeneousInverse(Hc[i]);
Mat dualqa = homogeneous2dualQuaternion(Hgij);
Mat dualqb = homogeneous2dualQuaternion(Hcij);
Mat a = dualqa(Rect(0, 1, 1, 3));
Mat b = dualqb(Rect(0, 1, 1, 3));
Mat aprime = dualqa(Rect(0, 5, 1, 3));
Mat bprime = dualqb(Rect(0, 5, 1, 3));
//Eq 31
Mat s00 = a - b;
Mat s01 = skew(a + b);
Mat s10 = aprime - bprime;
Mat s11 = skew(aprime + bprime);
Mat s12 = a - b;
Mat s13 = skew(a + b);
s00.copyTo(T(Rect(0, idx*6, 1, 3)));
s01.copyTo(T(Rect(1, idx*6, 3, 3)));
s10.copyTo(T(Rect(0, idx*6 + 3, 1, 3)));
s11.copyTo(T(Rect(1, idx*6 + 3, 3, 3)));
s12.copyTo(T(Rect(4, idx*6 + 3, 1, 3)));
s13.copyTo(T(Rect(5, idx*6 + 3, 3, 3)));
}
}
Mat w, u, vt;
SVDecomp(T, w, u, vt);
Mat v = vt.t();
Mat u1 = v(Rect(6, 0, 1, 4));
Mat v1 = v(Rect(6, 4, 1, 4));
Mat u2 = v(Rect(7, 0, 1, 4));
Mat v2 = v(Rect(7, 4, 1, 4));
//Solves Eq 34, Eq 35
Mat ma = u1.t()*v1;
Mat mb = u1.t()*v2 + u2.t()*v1;
Mat mc = u2.t()*v2;
double a = ma.at<double>(0,0);
double b = mb.at<double>(0,0);
double c = mc.at<double>(0,0);
double s1 = (-b + sqrt(b*b - 4*a*c)) / (2*a);
double s2 = (-b - sqrt(b*b - 4*a*c)) / (2*a);
Mat sol1 = s1*s1*u1.t()*u1 + 2*s1*u1.t()*u2 + u2.t()*u2;
Mat sol2 = s2*s2*u1.t()*u1 + 2*s2*u1.t()*u2 + u2.t()*u2;
double s, val;
if (sol1.at<double>(0,0) > sol2.at<double>(0,0))
{
s = s1;
val = sol1.at<double>(0,0);
}
else
{
s = s2;
val = sol2.at<double>(0,0);
}
double lambda2 = sqrt(1.0 / val);
double lambda1 = s * lambda2;
Mat dualq = lambda1 * v(Rect(6, 0, 1, 8)) + lambda2*v(Rect(7, 0, 1, 8));
Mat X = dualQuaternion2homogeneous(dualq);
Mat R = X(Rect(0, 0, 3, 3));
Mat t = X(Rect(3, 0, 1, 3));
R_cam2gripper = R;
t_cam2gripper = t;
}
void calibrateHandEye(InputArrayOfArrays R_gripper2base, InputArrayOfArrays t_gripper2base,
InputArrayOfArrays R_target2cam, InputArrayOfArrays t_target2cam,
OutputArray R_cam2gripper, OutputArray t_cam2gripper,
HandEyeCalibrationMethod method)
{
CV_Assert(R_gripper2base.isMatVector() && t_gripper2base.isMatVector() &&
R_target2cam.isMatVector() && t_target2cam.isMatVector());
std::vector<Mat> R_gripper2base_, t_gripper2base_;
R_gripper2base.getMatVector(R_gripper2base_);
t_gripper2base.getMatVector(t_gripper2base_);
std::vector<Mat> R_target2cam_, t_target2cam_;
R_target2cam.getMatVector(R_target2cam_);
t_target2cam.getMatVector(t_target2cam_);
CV_Assert(R_gripper2base_.size() == t_gripper2base_.size() &&
R_target2cam_.size() == t_target2cam_.size() &&
R_gripper2base_.size() == R_target2cam_.size());
CV_Assert(R_gripper2base_.size() >= 3);
//Notation used in Tsai paper
//Defines coordinate transformation from G (gripper) to RW (robot base)
std::vector<Mat> Hg;
Hg.reserve(R_gripper2base_.size());
for (size_t i = 0; i < R_gripper2base_.size(); i++)
{
Mat m = Mat::eye(4, 4, CV_64FC1);
Mat R = m(Rect(0, 0, 3, 3));
R_gripper2base_[i].convertTo(R, CV_64F);
Mat t = m(Rect(3, 0, 1, 3));
t_gripper2base_[i].convertTo(t, CV_64F);
Hg.push_back(m);
}
//Defines coordinate transformation from CW (calibration target) to C (camera)
std::vector<Mat> Hc;
Hc.reserve(R_target2cam_.size());
for (size_t i = 0; i < R_target2cam_.size(); i++)
{
Mat m = Mat::eye(4, 4, CV_64FC1);
Mat R = m(Rect(0, 0, 3, 3));
R_target2cam_[i].convertTo(R, CV_64F);
Mat t = m(Rect(3, 0, 1, 3));
t_target2cam_[i].convertTo(t, CV_64F);
Hc.push_back(m);
}
Mat Rcg = Mat::eye(3, 3, CV_64FC1);
Mat Tcg = Mat::zeros(3, 1, CV_64FC1);
switch (method)
{
case CALIB_HAND_EYE_TSAI:
calibrateHandEyeTsai(Hg, Hc, Rcg, Tcg);
break;
case CALIB_HAND_EYE_PARK:
calibrateHandEyePark(Hg, Hc, Rcg, Tcg);
break;
case CALIB_HAND_EYE_HORAUD:
calibrateHandEyeHoraud(Hg, Hc, Rcg, Tcg);
break;
case CALIB_HAND_EYE_ANDREFF:
calibrateHandEyeAndreff(Hg, Hc, Rcg, Tcg);
break;
case CALIB_HAND_EYE_DANIILIDIS:
calibrateHandEyeDaniilidis(Hg, Hc, Rcg, Tcg);
break;
default:
break;
}
Rcg.copyTo(R_cam2gripper);
Tcg.copyTo(t_cam2gripper);
}
}
+118 -100
View File
@@ -46,26 +46,26 @@
#include <vector>
#include <algorithm>
//#define DEBUG_WINDOWS
using namespace cv;
using namespace std;
#if defined(DEBUG_WINDOWS)
# include "opencv2/opencv_modules.hpp"
# ifdef HAVE_OPENCV_HIGHGUI
# include "opencv2/highgui.hpp"
# else
# undef DEBUG_WINDOWS
# endif
#endif
static void icvGetQuadrangleHypotheses(CvSeq* contours, std::vector<std::pair<float, int> >& quads, int class_id)
static void icvGetQuadrangleHypotheses(const std::vector<std::vector< cv::Point > > & contours, const std::vector< cv::Vec4i > & hierarchy, std::vector<std::pair<float, int> >& quads, int class_id)
{
const float min_aspect_ratio = 0.3f;
const float max_aspect_ratio = 3.0f;
const float min_box_size = 10.0f;
for(CvSeq* seq = contours; seq != NULL; seq = seq->h_next)
typedef std::vector< std::vector< cv::Point > >::const_iterator iter_t;
iter_t i;
for (i = contours.begin(); i != contours.end(); ++i)
{
CvBox2D box = cvMinAreaRect2(seq);
const iter_t::difference_type idx = i - contours.begin();
if (hierarchy.at(idx)[3] != -1)
continue; // skip holes
const std::vector< cv::Point > & c = *i;
cv::RotatedRect box = cv::minAreaRect(c);
float box_size = MAX(box.size.width, box.size.height);
if(box_size < min_box_size)
{
@@ -96,6 +96,64 @@ inline bool less_pred(const std::pair<float, int>& p1, const std::pair<float, in
return p1.first < p2.first;
}
static void fillQuads(Mat & white, Mat & black, double white_thresh, double black_thresh, vector<pair<float, int> > & quads)
{
Mat thresh;
{
vector< vector<Point> > contours;
vector< Vec4i > hierarchy;
threshold(white, thresh, white_thresh, 255, THRESH_BINARY);
findContours(thresh, contours, hierarchy, RETR_CCOMP, CHAIN_APPROX_SIMPLE);
icvGetQuadrangleHypotheses(contours, hierarchy, quads, 1);
}
{
vector< vector<Point> > contours;
vector< Vec4i > hierarchy;
threshold(black, thresh, black_thresh, 255, THRESH_BINARY_INV);
findContours(thresh, contours, hierarchy, RETR_CCOMP, CHAIN_APPROX_SIMPLE);
icvGetQuadrangleHypotheses(contours, hierarchy, quads, 0);
}
}
static bool checkQuads(vector<pair<float, int> > & quads, const cv::Size & size)
{
const size_t min_quads_count = size.width*size.height/2;
std::sort(quads.begin(), quads.end(), less_pred);
// now check if there are many hypotheses with similar sizes
// do this by floodfill-style algorithm
const float size_rel_dev = 0.4f;
for(size_t i = 0; i < quads.size(); i++)
{
size_t j = i + 1;
for(; j < quads.size(); j++)
{
if(quads[j].first/quads[i].first > 1.0f + size_rel_dev)
{
break;
}
}
if(j + 1 > min_quads_count + i)
{
// check the number of black and white squares
std::vector<int> counts;
countClasses(quads, i, j, counts);
const int black_count = cvRound(ceil(size.width/2.0)*ceil(size.height/2.0));
const int white_count = cvRound(floor(size.width/2.0)*floor(size.height/2.0));
if(counts[0] < black_count*0.75 ||
counts[1] < white_count*0.75)
{
continue;
}
return true;
}
}
return false;
}
// does a fast check if a chessboard is in the input image. This is a workaround to
// a problem of cvFindChessboardCorners being slow on images with no chessboard
// - src: input image
@@ -104,104 +162,64 @@ inline bool less_pred(const std::pair<float, int>& p1, const std::pair<float, in
// 0 if there is no chessboard, -1 in case of error
int cvCheckChessboard(IplImage* src, CvSize size)
{
if(src->nChannels > 1)
{
cvError(CV_BadNumChannels, "cvCheckChessboard", "supports single-channel images only",
__FILE__, __LINE__);
}
cv::Mat img = cv::cvarrToMat(src);
return checkChessboard(img, size);
}
if(src->depth != 8)
{
cvError(CV_BadDepth, "cvCheckChessboard", "supports depth=8 images only",
__FILE__, __LINE__);
}
int checkChessboard(const cv::Mat & img, const cv::Size & size)
{
CV_Assert(img.channels() == 1 && img.depth() == CV_8U);
const int erosion_count = 1;
const float black_level = 20.f;
const float white_level = 130.f;
const float black_white_gap = 70.f;
#if defined(DEBUG_WINDOWS)
cvNamedWindow("1", 1);
cvShowImage("1", src);
cvWaitKey(0);
#endif //DEBUG_WINDOWS
CvMemStorage* storage = cvCreateMemStorage();
IplImage* white = cvCloneImage(src);
IplImage* black = cvCloneImage(src);
cvErode(white, white, NULL, erosion_count);
cvDilate(black, black, NULL, erosion_count);
IplImage* thresh = cvCreateImage(cvGetSize(src), IPL_DEPTH_8U, 1);
Mat white;
Mat black;
erode(img, white, Mat(), Point(-1, -1), erosion_count);
dilate(img, black, Mat(), Point(-1, -1), erosion_count);
int result = 0;
for(float thresh_level = black_level; thresh_level < white_level && !result; thresh_level += 20.0f)
{
cvThreshold(white, thresh, thresh_level + black_white_gap, 255, CV_THRESH_BINARY);
#if defined(DEBUG_WINDOWS)
cvShowImage("1", thresh);
cvWaitKey(0);
#endif //DEBUG_WINDOWS
CvSeq* first = 0;
std::vector<std::pair<float, int> > quads;
cvFindContours(thresh, storage, &first, sizeof(CvContour), CV_RETR_CCOMP);
icvGetQuadrangleHypotheses(first, quads, 1);
cvThreshold(black, thresh, thresh_level, 255, CV_THRESH_BINARY_INV);
#if defined(DEBUG_WINDOWS)
cvShowImage("1", thresh);
cvWaitKey(0);
#endif //DEBUG_WINDOWS
cvFindContours(thresh, storage, &first, sizeof(CvContour), CV_RETR_CCOMP);
icvGetQuadrangleHypotheses(first, quads, 0);
const size_t min_quads_count = size.width*size.height/2;
std::sort(quads.begin(), quads.end(), less_pred);
// now check if there are many hypotheses with similar sizes
// do this by floodfill-style algorithm
const float size_rel_dev = 0.4f;
for(size_t i = 0; i < quads.size(); i++)
{
size_t j = i + 1;
for(; j < quads.size(); j++)
{
if(quads[j].first/quads[i].first > 1.0f + size_rel_dev)
{
break;
}
}
if(j + 1 > min_quads_count + i)
{
// check the number of black and white squares
std::vector<int> counts;
countClasses(quads, i, j, counts);
const int black_count = cvRound(ceil(size.width/2.0)*ceil(size.height/2.0));
const int white_count = cvRound(floor(size.width/2.0)*floor(size.height/2.0));
if(counts[0] < black_count*0.75 ||
counts[1] < white_count*0.75)
{
continue;
}
result = 1;
break;
}
}
vector<pair<float, int> > quads;
fillQuads(white, black, thresh_level + black_white_gap, thresh_level, quads);
if (checkQuads(quads, size))
result = 1;
}
return result;
}
// does a fast check if a chessboard is in the input image. This is a workaround to
// a problem of cvFindChessboardCorners being slow on images with no chessboard
// - src: input binary image
// - size: chessboard size
// Returns 1 if a chessboard can be in this image and findChessboardCorners should be called,
// 0 if there is no chessboard, -1 in case of error
int checkChessboardBinary(const cv::Mat & img, const cv::Size & size)
{
CV_Assert(img.channels() == 1 && img.depth() == CV_8U);
Mat white = img.clone();
Mat black = img.clone();
int result = 0;
for ( int erosion_count = 0; erosion_count <= 3; erosion_count++ )
{
if ( 1 == result )
break;
if ( 0 != erosion_count ) // first iteration keeps original images
{
erode(white, white, Mat(), Point(-1, -1), 1);
dilate(black, black, Mat(), Point(-1, -1), 1);
}
vector<pair<float, int> > quads;
fillQuads(white, black, 128, 128, quads);
if (checkQuads(quads, size))
result = 1;
}
cvReleaseImage(&thresh);
cvReleaseImage(&white);
cvReleaseImage(&black);
cvReleaseMemStorage(&storage);
return result;
}
+99 -58
View File
@@ -43,9 +43,12 @@
#include "precomp.hpp"
#include "circlesgrid.hpp"
#include <limits>
// Requires CMake flag: DEBUG_opencv_calib3d=ON
//#define DEBUG_CIRCLES
#ifdef DEBUG_CIRCLES
# include <iostream>
# include "opencv2/opencv_modules.hpp"
# ifdef HAVE_OPENCV_HIGHGUI
# include "opencv2/highgui.hpp"
@@ -66,10 +69,10 @@ void drawPoints(const std::vector<Point2f> &points, Mat &outImage, int radius =
}
#endif
void CirclesGridClusterFinder::hierarchicalClustering(const std::vector<Point2f> points, const Size &patternSz, std::vector<Point2f> &patternPoints)
void CirclesGridClusterFinder::hierarchicalClustering(const std::vector<Point2f> &points, const Size &patternSz, std::vector<Point2f> &patternPoints)
{
#ifdef HAVE_TEGRA_OPTIMIZATION
if(tegra::hierarchicalClustering(points, patternSz, patternPoints))
if(tegra::useTegra() && tegra::hierarchicalClustering(points, patternSz, patternPoints))
return;
#endif
int j, n = (int)points.size();
@@ -116,6 +119,7 @@ void CirclesGridClusterFinder::hierarchicalClustering(const std::vector<Point2f>
Mat tmpRow = dists.row(minIdx);
Mat tmpCol = dists.col(minIdx);
cv::min(dists.row(minLoc.x), dists.row(minLoc.y), tmpRow);
tmpRow = tmpRow.t();
tmpRow.copyTo(tmpCol);
clusters[minIdx].splice(clusters[minIdx].end(), clusters[maxIdx]);
@@ -129,13 +133,13 @@ void CirclesGridClusterFinder::hierarchicalClustering(const std::vector<Point2f>
}
patternPoints.reserve(clusters[patternClusterIdx].size());
for(std::list<size_t>::iterator it = clusters[patternClusterIdx].begin(); it != clusters[patternClusterIdx].end(); it++)
for(std::list<size_t>::iterator it = clusters[patternClusterIdx].begin(); it != clusters[patternClusterIdx].end();++it)
{
patternPoints.push_back(points[*it]);
}
}
void CirclesGridClusterFinder::findGrid(const std::vector<cv::Point2f> points, cv::Size _patternSize, std::vector<Point2f>& centers)
void CirclesGridClusterFinder::findGrid(const std::vector<cv::Point2f> &points, cv::Size _patternSize, std::vector<Point2f>& centers)
{
patternSize = _patternSize;
centers.clear();
@@ -158,7 +162,7 @@ void CirclesGridClusterFinder::findGrid(const std::vector<cv::Point2f> points, c
#endif
std::vector<Point2f> hull2f;
convexHull(Mat(patternPoints), hull2f, false);
convexHull(patternPoints, hull2f, false);
const size_t cornersCount = isAsymmetricGrid ? 6 : 4;
if(hull2f.size() < cornersCount)
return;
@@ -176,7 +180,7 @@ void CirclesGridClusterFinder::findGrid(const std::vector<cv::Point2f> points, c
if(outsideCorners.size() != outsideCornersCount)
return;
}
getSortedCorners(hull2f, corners, outsideCorners, sortedCorners);
getSortedCorners(hull2f, patternPoints, corners, outsideCorners, sortedCorners);
if(sortedCorners.size() != cornersCount)
return;
@@ -222,7 +226,7 @@ void CirclesGridClusterFinder::findOutsideCorners(const std::vector<cv::Point2f>
CV_Assert(!corners.empty());
outsideCorners.clear();
//find two pairs of the most nearest corners
int i, j, n = (int)corners.size();
const size_t n = corners.size();
#ifdef DEBUG_CIRCLES
Mat cornersImage(1024, 1248, CV_8UC1, Scalar(0));
@@ -230,22 +234,22 @@ void CirclesGridClusterFinder::findOutsideCorners(const std::vector<cv::Point2f>
imshow("corners", cornersImage);
#endif
std::vector<Point2f> tangentVectors(corners.size());
for(size_t k=0; k<corners.size(); k++)
std::vector<Point2f> tangentVectors(n);
for(size_t k=0; k < n; k++)
{
Point2f diff = corners[(k + 1) % corners.size()] - corners[k];
Point2f diff = corners[(k + 1) % n] - corners[k];
tangentVectors[k] = diff * (1.0f / norm(diff));
}
//compute angles between all sides
Mat cosAngles(n, n, CV_32FC1, 0.0f);
for(i = 0; i < n; i++)
Mat cosAngles((int)n, (int)n, CV_32FC1, 0.0f);
for(size_t i = 0; i < n; i++)
{
for(j = i + 1; j < n; j++)
for(size_t j = i + 1; j < n; j++)
{
float val = fabs(tangentVectors[i].dot(tangentVectors[j]));
cosAngles.at<float>(i, j) = val;
cosAngles.at<float>(j, i) = val;
cosAngles.at<float>((int)i, (int)j) = val;
cosAngles.at<float>((int)j, (int)i) = val;
}
}
@@ -274,10 +278,10 @@ void CirclesGridClusterFinder::findOutsideCorners(const std::vector<cv::Point2f>
const int bigDiff = 4;
if(maxIdx - minIdx == bigDiff)
{
minIdx += n;
minIdx += (int)n;
std::swap(maxIdx, minIdx);
}
if(maxIdx - minIdx != n - bigDiff)
if(maxIdx - minIdx != (int)n - bigDiff)
{
return;
}
@@ -289,11 +293,22 @@ void CirclesGridClusterFinder::findOutsideCorners(const std::vector<cv::Point2f>
#ifdef DEBUG_CIRCLES
drawPoints(outsideCorners, cornersImage, 2, Scalar(128));
imshow("corners", outsideCornersImage);
imshow("corners", cornersImage);
#endif
}
void CirclesGridClusterFinder::getSortedCorners(const std::vector<cv::Point2f> &hull2f, const std::vector<cv::Point2f> &corners, const std::vector<cv::Point2f> &outsideCorners, std::vector<cv::Point2f> &sortedCorners)
namespace {
double pointLineDistance(const cv::Point2f &p, const cv::Vec4f &line)
{
Vec3f pa( line[0], line[1], 1 );
Vec3f pb( line[2], line[3], 1 );
Vec3f l = pa.cross(pb);
return std::abs((p.x * l[0] + p.y * l[1] + l[2])) * 1.0 /
std::sqrt(double(l[0] * l[0] + l[1] * l[1]));
}
}
void CirclesGridClusterFinder::getSortedCorners(const std::vector<cv::Point2f> &hull2f, const std::vector<cv::Point2f> &patternPoints, const std::vector<cv::Point2f> &corners, const std::vector<cv::Point2f> &outsideCorners, std::vector<cv::Point2f> &sortedCorners)
{
Point2f firstCorner;
if(isAsymmetricGrid)
@@ -320,7 +335,7 @@ void CirclesGridClusterFinder::getSortedCorners(const std::vector<cv::Point2f> &
std::vector<Point2f>::const_iterator firstCornerIterator = std::find(hull2f.begin(), hull2f.end(), firstCorner);
sortedCorners.clear();
for(std::vector<Point2f>::const_iterator it = firstCornerIterator; it != hull2f.end(); it++)
for(std::vector<Point2f>::const_iterator it = firstCornerIterator; it != hull2f.end();++it)
{
std::vector<Point2f>::const_iterator itCorners = std::find(corners.begin(), corners.end(), *it);
if(itCorners != corners.end())
@@ -328,7 +343,7 @@ void CirclesGridClusterFinder::getSortedCorners(const std::vector<cv::Point2f> &
sortedCorners.push_back(*it);
}
}
for(std::vector<Point2f>::const_iterator it = hull2f.begin(); it != firstCornerIterator; it++)
for(std::vector<Point2f>::const_iterator it = hull2f.begin(); it != firstCornerIterator;++it)
{
std::vector<Point2f>::const_iterator itCorners = std::find(corners.begin(), corners.end(), *it);
if(itCorners != corners.end())
@@ -339,10 +354,26 @@ void CirclesGridClusterFinder::getSortedCorners(const std::vector<cv::Point2f> &
if(!isAsymmetricGrid)
{
double dist1 = norm(sortedCorners[0] - sortedCorners[1]);
double dist2 = norm(sortedCorners[1] - sortedCorners[2]);
double dist01 = norm(sortedCorners[0] - sortedCorners[1]);
double dist12 = norm(sortedCorners[1] - sortedCorners[2]);
// Use half the average distance between circles on the shorter side as threshold for determining whether a point lies on an edge.
double thresh = min(dist01, dist12) / min(patternSize.width, patternSize.height) / 2;
if((dist1 > dist2 && patternSize.height > patternSize.width) || (dist1 < dist2 && patternSize.height < patternSize.width))
size_t circleCount01 = 0;
size_t circleCount12 = 0;
Vec4f line01( sortedCorners[0].x, sortedCorners[0].y, sortedCorners[1].x, sortedCorners[1].y );
Vec4f line12( sortedCorners[1].x, sortedCorners[1].y, sortedCorners[2].x, sortedCorners[2].y );
// Count the circles along both edges.
for (size_t i = 0; i < patternPoints.size(); i++)
{
if (pointLineDistance(patternPoints[i], line01) < thresh)
circleCount01++;
if (pointLineDistance(patternPoints[i], line12) < thresh)
circleCount12++;
}
// Ensure that the edge from sortedCorners[0] to sortedCorners[1] is the one with more circles (i.e. it is interpreted as the pattern's width).
if ((circleCount01 > circleCount12 && patternSize.height > patternSize.width) || (circleCount01 < circleCount12 && patternSize.height < patternSize.width))
{
for(size_t i=0; i<sortedCorners.size()-1; i++)
{
@@ -382,7 +413,7 @@ void CirclesGridClusterFinder::rectifyPatternPoints(const std::vector<cv::Point2
}
}
Mat homography = findHomography(Mat(sortedCorners), Mat(idealPoints), 0);
Mat homography = findHomography(sortedCorners, idealPoints, 0);
Mat rectifiedPointsMat;
transform(patternPoints, rectifiedPointsMat, homography);
rectifiedPatternPoints.clear();
@@ -391,6 +422,12 @@ void CirclesGridClusterFinder::rectifyPatternPoints(const std::vector<cv::Point2
void CirclesGridClusterFinder::parsePatternPoints(const std::vector<cv::Point2f> &patternPoints, const std::vector<cv::Point2f> &rectifiedPatternPoints, std::vector<cv::Point2f> &centers)
{
#ifndef HAVE_OPENCV_FLANN
CV_UNUSED(patternPoints);
CV_UNUSED(rectifiedPatternPoints);
CV_UNUSED(centers);
CV_Error(Error::StsNotImplemented, "The desired functionality requires flann module, which was disabled.");
#else
flann::LinearIndexParams flannIndexParams;
flann::Index flannIndex(Mat(rectifiedPatternPoints).reshape(1), flannIndexParams);
@@ -417,13 +454,14 @@ void CirclesGridClusterFinder::parsePatternPoints(const std::vector<cv::Point2f>
if(distsbuf[0] > maxRectifiedDistance)
{
#ifdef DEBUG_CIRCLES
cout << "Pattern not detected: too large rectified distance" << endl;
std::cout << "Pattern not detected: too large rectified distance" << std::endl;
#endif
centers.clear();
return;
}
}
}
#endif
}
Graph::Graph(size_t n)
@@ -466,11 +504,10 @@ void Graph::removeEdge(size_t id1, size_t id2)
bool Graph::areVerticesAdjacent(size_t id1, size_t id2) const
{
CV_Assert( doesVertexExist( id1 ) );
CV_Assert( doesVertexExist( id2 ) );
Vertices::const_iterator it = vertices.find(id1);
return it->second.neighbors.find(id2) != it->second.neighbors.end();
CV_Assert(it != vertices.end());
const Neighbors & neighbors = it->second.neighbors;
return neighbors.find(id2) != neighbors.end();
}
size_t Graph::getVerticesCount() const
@@ -480,9 +517,8 @@ size_t Graph::getVerticesCount() const
size_t Graph::getDegree(size_t id) const
{
CV_Assert( doesVertexExist(id) );
Vertices::const_iterator it = vertices.find(id);
CV_Assert( it != vertices.end() );
return it->second.neighbors.size();
}
@@ -493,21 +529,21 @@ void Graph::floydWarshall(cv::Mat &distanceMatrix, int infinity) const
const int n = (int)getVerticesCount();
distanceMatrix.create(n, n, CV_32SC1);
distanceMatrix.setTo(infinity);
for (Vertices::const_iterator it1 = vertices.begin(); it1 != vertices.end(); it1++)
for (Vertices::const_iterator it1 = vertices.begin(); it1 != vertices.end();++it1)
{
distanceMatrix.at<int> ((int)it1->first, (int)it1->first) = 0;
for (Neighbors::const_iterator it2 = it1->second.neighbors.begin(); it2 != it1->second.neighbors.end(); it2++)
for (Neighbors::const_iterator it2 = it1->second.neighbors.begin(); it2 != it1->second.neighbors.end();++it2)
{
CV_Assert( it1->first != *it2 );
distanceMatrix.at<int> ((int)it1->first, (int)*it2) = edgeWeight;
}
}
for (Vertices::const_iterator it1 = vertices.begin(); it1 != vertices.end(); it1++)
for (Vertices::const_iterator it1 = vertices.begin(); it1 != vertices.end();++it1)
{
for (Vertices::const_iterator it2 = vertices.begin(); it2 != vertices.end(); it2++)
for (Vertices::const_iterator it2 = vertices.begin(); it2 != vertices.end();++it2)
{
for (Vertices::const_iterator it3 = vertices.begin(); it3 != vertices.end(); it3++)
for (Vertices::const_iterator it3 = vertices.begin(); it3 != vertices.end();++it3)
{
int i1 = (int)it1->first, i2 = (int)it2->first, i3 = (int)it3->first;
int val1 = distanceMatrix.at<int> (i2, i3);
@@ -527,9 +563,8 @@ void Graph::floydWarshall(cv::Mat &distanceMatrix, int infinity) const
const Graph::Neighbors& Graph::getNeighbors(size_t id) const
{
CV_Assert( doesVertexExist(id) );
Vertices::const_iterator it = vertices.find(id);
CV_Assert( it != vertices.end() );
return it->second.neighbors;
}
@@ -551,16 +586,23 @@ CirclesGridFinderParameters::CirclesGridFinderParameters()
keypointScale = 1;
minGraphConfidence = 9;
vertexGain = 2;
vertexPenalty = -5;
vertexGain = 1;
vertexPenalty = -0.6f;
edgeGain = 1;
edgePenalty = -5;
existingVertexGain = 0;
edgePenalty = -0.6f;
existingVertexGain = 10000;
minRNGEdgeSwitchDist = 5.f;
gridType = SYMMETRIC_GRID;
}
CirclesGridFinderParameters2::CirclesGridFinderParameters2()
: CirclesGridFinderParameters()
{
squareSize = 1.0f;
maxRectifiedDistance = squareSize/2.0f;
}
CirclesGridFinder::CirclesGridFinder(Size _patternSize, const std::vector<Point2f> &testKeypoints,
const CirclesGridFinderParameters &_parameters) :
patternSize(static_cast<size_t> (_patternSize.width), static_cast<size_t> (_patternSize.height))
@@ -607,7 +649,7 @@ bool CirclesGridFinder::findHoles()
}
default:
CV_Error(Error::StsBadArg, "Unkown pattern type");
CV_Error(Error::StsBadArg, "Unknown pattern type");
}
return (isDetectionCorrect());
//CV_Error( 0, "Detection is not correct" );
@@ -618,10 +660,10 @@ void CirclesGridFinder::rng2gridGraph(Graph &rng, std::vector<cv::Point2f> &vect
for (size_t i = 0; i < rng.getVerticesCount(); i++)
{
Graph::Neighbors neighbors1 = rng.getNeighbors(i);
for (Graph::Neighbors::iterator it1 = neighbors1.begin(); it1 != neighbors1.end(); it1++)
for (Graph::Neighbors::iterator it1 = neighbors1.begin(); it1 != neighbors1.end(); ++it1)
{
Graph::Neighbors neighbors2 = rng.getNeighbors(*it1);
for (Graph::Neighbors::iterator it2 = neighbors2.begin(); it2 != neighbors2.end(); it2++)
for (Graph::Neighbors::iterator it2 = neighbors2.begin(); it2 != neighbors2.end(); ++it2)
{
if (i < *it2)
{
@@ -746,12 +788,8 @@ bool CirclesGridFinder::isDetectionCorrect()
}
return (vertices.size() == largeHeight * largeWidth + smallHeight * smallWidth);
}
default:
CV_Error(0, "Unknown pattern type");
}
return false;
CV_Error(Error::StsBadArg, "Unknown pattern type");
}
void CirclesGridFinder::findMCS(const std::vector<Point2f> &basis, std::vector<Graph> &basisGraphs)
@@ -835,11 +873,15 @@ Mat CirclesGridFinder::rectifyGrid(Size detectedGridSize, const std::vector<Poin
}
}
Mat H = findHomography(Mat(centers), Mat(dstPoints), RANSAC);
//Mat H = findHomography( Mat( corners ), Mat( dstPoints ) );
Mat H = findHomography(centers, dstPoints, RANSAC);
//Mat H = findHomography(corners, dstPoints);
if (H.empty())
{
H = Mat::zeros(3, 3, CV_64FC1);
warpedKeypoints.clear();
return H;
}
std::vector<Point2f> srcKeypoints;
for (size_t i = 0; i < keypoints.size(); i++)
@@ -848,7 +890,7 @@ Mat CirclesGridFinder::rectifyGrid(Size detectedGridSize, const std::vector<Poin
}
Mat dstKeypointsMat;
transform(Mat(srcKeypoints), dstKeypointsMat, H);
transform(srcKeypoints, dstKeypointsMat, H);
std::vector<Point2f> dstKeypoints;
convertPointsFromHomogeneous(dstKeypointsMat, dstKeypoints);
@@ -1136,7 +1178,7 @@ void CirclesGridFinder::findBasis(const std::vector<Point2f> &samples, std::vect
}
for (size_t i = 0; i < basis.size(); i++)
{
convexHull(Mat(clusters[i]), hulls[i]);
convexHull(clusters[i], hulls[i]);
}
basisGraphs.resize(basis.size(), Graph(keypoints.size()));
@@ -1151,7 +1193,7 @@ void CirclesGridFinder::findBasis(const std::vector<Point2f> &samples, std::vect
for (size_t k = 0; k < hulls.size(); k++)
{
if (pointPolygonTest(Mat(hulls[k]), vec, false) >= 0)
if (pointPolygonTest(hulls[k], vec, false) >= 0)
{
basisGraphs[k].addEdge(i, j);
}
@@ -1382,7 +1424,6 @@ void CirclesGridFinder::drawHoles(const Mat &srcImage, Mat &drawImage) const
if (i != holes.size() - 1)
line(drawImage, keypoints[holes[i][j]], keypoints[holes[i + 1][j]], Scalar(255, 0, 0), 2);
//circle(drawImage, keypoints[holes[i][j]], holeRadius, holeColor, holeThickness);
circle(drawImage, keypoints[holes[i][j]], holeRadius, holeColor, holeThickness);
}
}
@@ -1531,7 +1572,7 @@ void CirclesGridFinder::getCornerSegments(const std::vector<std::vector<size_t>
if (!isClockwise)
{
#ifdef DEBUG_CIRCLES
cout << "Corners are counterclockwise" << endl;
std::cout << "Corners are counterclockwise" << std::endl;
#endif
std::reverse(segments.begin(), segments.end());
std::reverse(cornerIndices.begin(), cornerIndices.end());
+9 -33
View File
@@ -56,20 +56,20 @@ class CirclesGridClusterFinder
CirclesGridClusterFinder& operator=(const CirclesGridClusterFinder&);
CirclesGridClusterFinder(const CirclesGridClusterFinder&);
public:
CirclesGridClusterFinder(bool _isAsymmetricGrid)
CirclesGridClusterFinder(const cv::CirclesGridFinderParameters2 &parameters)
{
isAsymmetricGrid = _isAsymmetricGrid;
squareSize = 1.0f;
maxRectifiedDistance = (float)(squareSize / 2.0);
isAsymmetricGrid = parameters.gridType == cv::CirclesGridFinderParameters::ASYMMETRIC_GRID;
squareSize = parameters.squareSize;
maxRectifiedDistance = parameters.maxRectifiedDistance;
}
void findGrid(const std::vector<cv::Point2f> points, cv::Size patternSize, std::vector<cv::Point2f>& centers);
void findGrid(const std::vector<cv::Point2f> &points, cv::Size patternSize, std::vector<cv::Point2f>& centers);
//cluster 2d points by geometric coordinates
void hierarchicalClustering(const std::vector<cv::Point2f> points, const cv::Size &patternSize, std::vector<cv::Point2f> &patternPoints);
void hierarchicalClustering(const std::vector<cv::Point2f> &points, const cv::Size &patternSize, std::vector<cv::Point2f> &patternPoints);
private:
void findCorners(const std::vector<cv::Point2f> &hull2f, std::vector<cv::Point2f> &corners);
void findOutsideCorners(const std::vector<cv::Point2f> &corners, std::vector<cv::Point2f> &outsideCorners);
void getSortedCorners(const std::vector<cv::Point2f> &hull2f, const std::vector<cv::Point2f> &corners, const std::vector<cv::Point2f> &outsideCorners, std::vector<cv::Point2f> &sortedCorners);
void getSortedCorners(const std::vector<cv::Point2f> &hull2f, const std::vector<cv::Point2f> &patternPoints, const std::vector<cv::Point2f> &corners, const std::vector<cv::Point2f> &outsideCorners, std::vector<cv::Point2f> &sortedCorners);
void rectifyPatternPoints(const std::vector<cv::Point2f> &patternPoints, const std::vector<cv::Point2f> &sortedCorners, std::vector<cv::Point2f> &rectifiedPatternPoints);
void parsePatternPoints(const std::vector<cv::Point2f> &patternPoints, const std::vector<cv::Point2f> &rectifiedPatternPoints, std::vector<cv::Point2f> &centers);
@@ -119,35 +119,11 @@ struct Path
}
};
struct CirclesGridFinderParameters
{
CirclesGridFinderParameters();
cv::Size2f densityNeighborhoodSize;
float minDensity;
int kmeansAttempts;
int minDistanceToAddKeypoint;
int keypointScale;
float minGraphConfidence;
float vertexGain;
float vertexPenalty;
float existingVertexGain;
float edgeGain;
float edgePenalty;
float convexHullFactor;
float minRNGEdgeSwitchDist;
enum GridType
{
SYMMETRIC_GRID, ASYMMETRIC_GRID
};
GridType gridType;
};
class CirclesGridFinder
{
public:
CirclesGridFinder(cv::Size patternSize, const std::vector<cv::Point2f> &testKeypoints,
const CirclesGridFinderParameters &parameters = CirclesGridFinderParameters());
const cv::CirclesGridFinderParameters &parameters = cv::CirclesGridFinderParameters());
bool findHoles();
static cv::Mat rectifyGrid(cv::Size detectedGridSize, const std::vector<cv::Point2f>& centers, const std::vector<
cv::Point2f> &keypoint, std::vector<cv::Point2f> &warpedKeypoints);
@@ -211,7 +187,7 @@ private:
std::vector<std::vector<size_t> > *smallHoles;
const cv::Size_<size_t> patternSize;
CirclesGridFinderParameters parameters;
cv::CirclesGridFinderParameters parameters;
CirclesGridFinder& operator=(const CirclesGridFinder&);
CirclesGridFinder(const CirclesGridFinder&);
+75 -33
View File
@@ -58,6 +58,7 @@ CvLevMarq::CvLevMarq()
iters = 0;
completeSymmFlag = false;
errNorm = prevErrNorm = DBL_MAX;
solveMethod = cv::DECOMP_SVD;
}
CvLevMarq::CvLevMarq( int nparams, int nerrs, CvTermCriteria criteria0, bool _completeSymmFlag )
@@ -93,9 +94,6 @@ void CvLevMarq::init( int nparams, int nerrs, CvTermCriteria criteria0, bool _co
prevParam.reset(cvCreateMat( nparams, 1, CV_64F ));
param.reset(cvCreateMat( nparams, 1, CV_64F ));
JtJ.reset(cvCreateMat( nparams, nparams, CV_64F ));
JtJN.reset(cvCreateMat( nparams, nparams, CV_64F ));
JtJV.reset(cvCreateMat( nparams, nparams, CV_64F ));
JtJW.reset(cvCreateMat( nparams, 1, CV_64F ));
JtErr.reset(cvCreateMat( nparams, 1, CV_64F ));
if( nerrs > 0 )
{
@@ -116,12 +114,11 @@ void CvLevMarq::init( int nparams, int nerrs, CvTermCriteria criteria0, bool _co
state = STARTED;
iters = 0;
completeSymmFlag = _completeSymmFlag;
solveMethod = cv::DECOMP_SVD;
}
bool CvLevMarq::update( const CvMat*& _param, CvMat*& matJ, CvMat*& _err )
{
double change;
matJ = _err = 0;
assert( !err.empty() );
@@ -174,7 +171,7 @@ bool CvLevMarq::update( const CvMat*& _param, CvMat*& matJ, CvMat*& _err )
lambdaLg10 = MAX(lambdaLg10-1, -16);
if( ++iters >= criteria.max_iter ||
(change = cvNorm(param, prevParam, CV_RELATIVE_L2)) < criteria.epsilon )
cvNorm(param, prevParam, CV_RELATIVE_L2) < criteria.epsilon )
{
_param = param;
state = DONE;
@@ -193,8 +190,6 @@ bool CvLevMarq::update( const CvMat*& _param, CvMat*& matJ, CvMat*& _err )
bool CvLevMarq::updateAlt( const CvMat*& _param, CvMat*& _JtJ, CvMat*& _JtErr, double*& _errNorm )
{
double change;
CV_Assert( !err );
if( state == DONE )
{
@@ -243,9 +238,11 @@ bool CvLevMarq::updateAlt( const CvMat*& _param, CvMat*& _JtJ, CvMat*& _JtErr, d
lambdaLg10 = MAX(lambdaLg10-1, -16);
if( ++iters >= criteria.max_iter ||
(change = cvNorm(param, prevParam, CV_RELATIVE_L2)) < criteria.epsilon )
cvNorm(param, prevParam, CV_RELATIVE_L2) < criteria.epsilon )
{
_param = param;
_JtJ = JtJ;
_JtErr = JtErr;
state = DONE;
return false;
}
@@ -260,35 +257,68 @@ bool CvLevMarq::updateAlt( const CvMat*& _param, CvMat*& _JtJ, CvMat*& _JtErr, d
return true;
}
namespace {
static void subMatrix(const cv::Mat& src, cv::Mat& dst, const std::vector<uchar>& cols,
const std::vector<uchar>& rows) {
int nonzeros_cols = cv::countNonZero(cols);
cv::Mat tmp(src.rows, nonzeros_cols, CV_64FC1);
for (int i = 0, j = 0; i < (int)cols.size(); i++)
{
if (cols[i])
{
src.col(i).copyTo(tmp.col(j++));
}
}
int nonzeros_rows = cv::countNonZero(rows);
dst.create(nonzeros_rows, nonzeros_cols, CV_64FC1);
for (int i = 0, j = 0; i < (int)rows.size(); i++)
{
if (rows[i])
{
tmp.row(i).copyTo(dst.row(j++));
}
}
}
}
void CvLevMarq::step()
{
using namespace cv;
const double LOG10 = log(10.);
double lambda = exp(lambdaLg10*LOG10);
int i, j, nparams = param->rows;
int nparams = param->rows;
for( i = 0; i < nparams; i++ )
if( mask->data.ptr[i] == 0 )
{
double *row = JtJ->data.db + i*nparams, *col = JtJ->data.db + i;
for( j = 0; j < nparams; j++ )
row[j] = col[j*nparams] = 0;
JtErr->data.db[i] = 0;
}
Mat _JtJ = cvarrToMat(JtJ);
Mat _mask = cvarrToMat(mask);
int nparams_nz = countNonZero(_mask);
if(!JtJN || JtJN->rows != nparams_nz) {
// prevent re-allocation in every step
JtJN.reset(cvCreateMat( nparams_nz, nparams_nz, CV_64F ));
JtJV.reset(cvCreateMat( nparams_nz, 1, CV_64F ));
JtJW.reset(cvCreateMat( nparams_nz, 1, CV_64F ));
}
Mat _JtJN = cvarrToMat(JtJN);
Mat _JtErr = cvarrToMat(JtJV);
Mat_<double> nonzero_param = cvarrToMat(JtJW);
subMatrix(cvarrToMat(JtErr), _JtErr, std::vector<uchar>(1, 1), _mask);
subMatrix(_JtJ, _JtJN, _mask, _mask);
if( !err )
cvCompleteSymm( JtJ, completeSymmFlag );
#if 1
cvCopy( JtJ, JtJN );
for( i = 0; i < nparams; i++ )
JtJN->data.db[(nparams+1)*i] *= 1. + lambda;
#else
cvSetIdentity(JtJN, cvRealScalar(lambda));
cvAdd( JtJ, JtJN, JtJN );
#endif
cvSVD( JtJN, JtJW, 0, JtJV, CV_SVD_MODIFY_A + CV_SVD_U_T + CV_SVD_V_T );
cvSVBkSb( JtJW, JtJV, JtJV, JtErr, param, CV_SVD_U_T + CV_SVD_V_T );
for( i = 0; i < nparams; i++ )
param->data.db[i] = prevParam->data.db[i] - (mask->data.ptr[i] ? param->data.db[i] : 0);
completeSymm( _JtJN, completeSymmFlag );
_JtJN.diag() *= 1. + lambda;
solve(_JtJN, _JtErr, nonzero_param, solveMethod);
int j = 0;
for( int i = 0; i < nparams; i++ )
param->data.db[i] = prevParam->data.db[i] - (mask->data.ptr[i] ? nonzero_param(j++) : 0);
}
@@ -299,7 +329,8 @@ CV_IMPL int cvRANSACUpdateNumIters( double p, double ep, int modelPoints, int ma
CV_IMPL int cvFindHomography( const CvMat* _src, const CvMat* _dst, CvMat* __H, int method,
double ransacReprojThreshold, CvMat* _mask )
double ransacReprojThreshold, CvMat* _mask, int maxIters,
double confidence)
{
cv::Mat src = cv::cvarrToMat(_src), dst = cv::cvarrToMat(_dst);
@@ -308,9 +339,20 @@ CV_IMPL int cvFindHomography( const CvMat* _src, const CvMat* _dst, CvMat* __H,
if( dst.channels() == 1 && (dst.rows == 2 || dst.rows == 3) && dst.cols > 3 )
cv::transpose(dst, dst);
if ( maxIters < 0 )
maxIters = 0;
if ( maxIters > 2000 )
maxIters = 2000;
if ( confidence < 0 )
confidence = 0;
if ( confidence > 1 )
confidence = 1;
const cv::Mat H = cv::cvarrToMat(__H), mask = cv::cvarrToMat(_mask);
cv::Mat H0 = cv::findHomography(src, dst, method, ransacReprojThreshold,
_mask ? cv::_OutputArray(mask) : cv::_OutputArray());
_mask ? cv::_OutputArray(mask) : cv::_OutputArray(), maxIters,
confidence);
if( H0.empty() )
{
+13 -13
View File
@@ -92,18 +92,18 @@ void cvFindStereoCorrespondenceBM( const CvArr* leftarr, const CvArr* rightarr,
CV_Assert( state != 0 );
cv::Ptr<cv::StereoMatcher> sm = cv::createStereoBM(state->numberOfDisparities,
cv::Ptr<cv::StereoBM> sm = cv::StereoBM::create(state->numberOfDisparities,
state->SADWindowSize);
sm->set("preFilterType", state->preFilterType);
sm->set("preFilterSize", state->preFilterSize);
sm->set("preFilterCap", state->preFilterCap);
sm->set("SADWindowSize", state->SADWindowSize);
sm->set("numDisparities", state->numberOfDisparities > 0 ? state->numberOfDisparities : 64);
sm->set("textureThreshold", state->textureThreshold);
sm->set("uniquenessRatio", state->uniquenessRatio);
sm->set("speckleRange", state->speckleRange);
sm->set("speckleWindowSize", state->speckleWindowSize);
sm->set("disp12MaxDiff", state->disp12MaxDiff);
sm->setPreFilterType(state->preFilterType);
sm->setPreFilterSize(state->preFilterSize);
sm->setPreFilterCap(state->preFilterCap);
sm->setBlockSize(state->SADWindowSize);
sm->setNumDisparities(state->numberOfDisparities > 0 ? state->numberOfDisparities : 64);
sm->setTextureThreshold(state->textureThreshold);
sm->setUniquenessRatio(state->uniquenessRatio);
sm->setSpeckleRange(state->speckleRange);
sm->setSpeckleWindowSize(state->speckleWindowSize);
sm->setDisp12MaxDiff(state->disp12MaxDiff);
sm->compute(left, right, disp);
}
@@ -111,8 +111,8 @@ void cvFindStereoCorrespondenceBM( const CvArr* leftarr, const CvArr* rightarr,
CvRect cvGetValidDisparityROI( CvRect roi1, CvRect roi2, int minDisparity,
int numberOfDisparities, int SADWindowSize )
{
return (CvRect)cv::getValidDisparityROI( roi1, roi2, minDisparity,
numberOfDisparities, SADWindowSize );
return cvRect(cv::getValidDisparityROI( roi1, roi2, minDisparity,
numberOfDisparities, SADWindowSize));
}
void cvValidateDisparity( CvArr* _disp, const CvArr* _cost, int minDisparity,
+659
View File
@@ -0,0 +1,659 @@
#include "precomp.hpp"
#include "dls.h"
#include <iostream>
#ifdef HAVE_EIGEN
# if defined __GNUC__ && defined __APPLE__
# pragma GCC diagnostic ignored "-Wshadow"
# endif
# if defined(_MSC_VER)
# pragma warning(push)
# pragma warning(disable:4701) // potentially uninitialized local variable
# pragma warning(disable:4702) // unreachable code
# pragma warning(disable:4714) // const marked as __forceinline not inlined
# endif
# include <Eigen/Core>
# include <Eigen/Eigenvalues>
# if defined(_MSC_VER)
# pragma warning(pop)
# endif
# include "opencv2/core/eigen.hpp"
#endif
using namespace std;
dls::dls(const cv::Mat& opoints, const cv::Mat& ipoints)
{
N = std::max(opoints.checkVector(3, CV_32F), opoints.checkVector(3, CV_64F));
p = cv::Mat(3, N, CV_64F);
z = cv::Mat(3, N, CV_64F);
mn = cv::Mat::zeros(3, 1, CV_64F);
cost__ = 9999;
f1coeff.resize(21);
f2coeff.resize(21);
f3coeff.resize(21);
if (opoints.depth() == ipoints.depth())
{
if (opoints.depth() == CV_32F)
init_points<cv::Point3f, cv::Point2f>(opoints, ipoints);
else
init_points<cv::Point3d, cv::Point2d>(opoints, ipoints);
}
else if (opoints.depth() == CV_32F)
init_points<cv::Point3f, cv::Point2d>(opoints, ipoints);
else
init_points<cv::Point3d, cv::Point2f>(opoints, ipoints);
}
dls::~dls()
{
// TODO Auto-generated destructor stub
}
bool dls::compute_pose(cv::Mat& R, cv::Mat& t)
{
std::vector<cv::Mat> R_;
R_.push_back(rotx(CV_PI/2));
R_.push_back(roty(CV_PI/2));
R_.push_back(rotz(CV_PI/2));
// version that calls dls 3 times, to avoid Cayley singularity
for (int i = 0; i < 3; ++i)
{
// Make a random rotation
cv::Mat pp = R_[i] * ( p - cv::repeat(mn, 1, p.cols) );
// clear for new data
C_est_.clear();
t_est_.clear();
cost_.clear();
this->run_kernel(pp); // run dls_pnp()
// find global minimum
for (unsigned int j = 0; j < cost_.size(); ++j)
{
if( cost_[j] < cost__ )
{
t_est__ = t_est_[j] - C_est_[j] * R_[i] * mn;
C_est__ = C_est_[j] * R_[i];
cost__ = cost_[j];
}
}
}
if(C_est__.cols > 0 && C_est__.rows > 0)
{
C_est__.copyTo(R);
t_est__.copyTo(t);
return true;
}
return false;
}
void dls::run_kernel(const cv::Mat& pp)
{
cv::Mat Mtilde(27, 27, CV_64F);
cv::Mat D = cv::Mat::zeros(9, 9, CV_64F);
build_coeff_matrix(pp, Mtilde, D);
cv::Mat eigenval_r, eigenval_i, eigenvec_r, eigenvec_i;
compute_eigenvec(Mtilde, eigenval_r, eigenval_i, eigenvec_r, eigenvec_i);
/*
* Now check the solutions
*/
// extract the optimal solutions from the eigen decomposition of the
// Multiplication matrix
cv::Mat sols = cv::Mat::zeros(3, 27, CV_64F);
std::vector<double> cost;
int count = 0;
for (int k = 0; k < 27; ++k)
{
// V(:,k) = V(:,k)/V(1,k);
cv::Mat V_kA = eigenvec_r.col(k); // 27x1
cv::Mat V_kB = cv::Mat(1, 1, z.depth(), V_kA.at<double>(0)); // 1x1
cv::Mat V_k; cv::solve(V_kB.t(), V_kA.t(), V_k); // A/B = B'\A'
cv::Mat( V_k.t()).copyTo( eigenvec_r.col(k) );
//if (imag(V(2,k)) == 0)
#ifdef HAVE_EIGEN
const double epsilon = 1e-4;
if( eigenval_i.at<double>(k,0) >= -epsilon && eigenval_i.at<double>(k,0) <= epsilon )
#endif
{
double stmp[3];
stmp[0] = eigenvec_r.at<double>(9, k);
stmp[1] = eigenvec_r.at<double>(3, k);
stmp[2] = eigenvec_r.at<double>(1, k);
cv::Mat H = Hessian(stmp);
cv::Mat eigenvalues, eigenvectors;
cv::eigen(H, eigenvalues, eigenvectors);
if(positive_eigenvalues(&eigenvalues))
{
// sols(:,i) = stmp;
cv::Mat stmp_mat(3, 1, CV_64F, &stmp);
stmp_mat.copyTo( sols.col(count) );
cv::Mat Cbar = cayley2rotbar(stmp_mat);
cv::Mat Cbarvec = Cbar.reshape(1,1).t();
// cost(i) = CbarVec' * D * CbarVec;
cv::Mat cost_mat = Cbarvec.t() * D * Cbarvec;
cost.push_back( cost_mat.at<double>(0) );
count++;
}
}
}
// extract solutions
sols = sols.clone().colRange(0, count);
std::vector<cv::Mat> C_est, t_est;
for (int j = 0; j < sols.cols; ++j)
{
// recover the optimal orientation
// C_est(:,:,j) = 1/(1 + sols(:,j)' * sols(:,j)) * cayley2rotbar(sols(:,j));
cv::Mat sols_j = sols.col(j);
double sols_mult = 1./(1.+cv::Mat( sols_j.t() * sols_j ).at<double>(0));
cv::Mat C_est_j = cayley2rotbar(sols_j).mul(sols_mult);
C_est.push_back( C_est_j );
cv::Mat A2 = cv::Mat::zeros(3, 3, CV_64F);
cv::Mat b2 = cv::Mat::zeros(3, 1, CV_64F);
for (int i = 0; i < N; ++i)
{
cv::Mat eye = cv::Mat::eye(3, 3, CV_64F);
cv::Mat z_mul = z.col(i)*z.col(i).t();
A2 += eye - z_mul;
b2 += (z_mul - eye) * C_est_j * pp.col(i);
}
// recover the optimal translation
cv::Mat X2; cv::solve(A2, b2, X2); // A\B
t_est.push_back(X2);
}
// check that the points are infront of the center of perspectivity
for (int k = 0; k < sols.cols; ++k)
{
cv::Mat cam_points = C_est[k] * pp + cv::repeat(t_est[k], 1, pp.cols);
cv::Mat cam_points_k = cam_points.row(2);
if(is_empty(&cam_points_k))
{
cv::Mat C_valid = C_est[k], t_valid = t_est[k];
double cost_valid = cost[k];
C_est_.push_back(C_valid);
t_est_.push_back(t_valid);
cost_.push_back(cost_valid);
}
}
}
void dls::build_coeff_matrix(const cv::Mat& pp, cv::Mat& Mtilde, cv::Mat& D)
{
CV_Assert(!pp.empty() && N > 0);
cv::Mat eye = cv::Mat::eye(3, 3, CV_64F);
// build coeff matrix
// An intermediate matrix, the inverse of what is called "H" in the paper
// (see eq. 25)
cv::Mat H = cv::Mat::zeros(3, 3, CV_64F);
cv::Mat A = cv::Mat::zeros(3, 9, CV_64F);
cv::Mat pp_i(3, 1, CV_64F);
cv::Mat z_i(3, 1, CV_64F);
for (int i = 0; i < N; ++i)
{
z.col(i).copyTo(z_i);
A += ( z_i*z_i.t() - eye ) * LeftMultVec(pp.col(i));
}
H = eye.mul(N) - z * z.t();
// A\B
cv::solve(H, A, A, cv::DECOMP_NORMAL);
H.release();
cv::Mat ppi_A(3, 1, CV_64F);
for (int i = 0; i < N; ++i)
{
z.col(i).copyTo(z_i);
ppi_A = LeftMultVec(pp.col(i)) + A;
D += ppi_A.t() * ( eye - z_i*z_i.t() ) * ppi_A;
}
A.release();
// fill the coefficients
fill_coeff(&D);
// generate random samples
std::vector<double> u(5);
cv::randn(u, 0, 200);
cv::Mat M2 = cayley_LS_M(f1coeff, f2coeff, f3coeff, u);
cv::Mat M2_1 = M2(cv::Range(0,27), cv::Range(0,27));
cv::Mat M2_2 = M2(cv::Range(0,27), cv::Range(27,120));
cv::Mat M2_3 = M2(cv::Range(27,120), cv::Range(27,120));
cv::Mat M2_4 = M2(cv::Range(27,120), cv::Range(0,27));
M2.release();
// A/B = B'\A'
cv::Mat M2_5; cv::solve(M2_3.t(), M2_2.t(), M2_5);
M2_2.release(); M2_3.release();
// construct the multiplication matrix via schur compliment of the Macaulay
// matrix
Mtilde = M2_1 - M2_5.t()*M2_4;
}
void dls::compute_eigenvec(const cv::Mat& Mtilde, cv::Mat& eigenval_real, cv::Mat& eigenval_imag,
cv::Mat& eigenvec_real, cv::Mat& eigenvec_imag)
{
#ifdef HAVE_EIGEN
Eigen::MatrixXd Mtilde_eig, zeros_eig;
cv::cv2eigen(Mtilde, Mtilde_eig);
cv::cv2eigen(cv::Mat::zeros(27, 27, CV_64F), zeros_eig);
Eigen::MatrixXcd Mtilde_eig_cmplx(27, 27);
Mtilde_eig_cmplx.real() = Mtilde_eig;
Mtilde_eig_cmplx.imag() = zeros_eig;
Eigen::ComplexEigenSolver<Eigen::MatrixXcd> ces;
ces.compute(Mtilde_eig_cmplx);
Eigen::MatrixXd eigval_real = ces.eigenvalues().real();
Eigen::MatrixXd eigval_imag = ces.eigenvalues().imag();
Eigen::MatrixXd eigvec_real = ces.eigenvectors().real();
Eigen::MatrixXd eigvec_imag = ces.eigenvectors().imag();
cv::eigen2cv(eigval_real, eigenval_real);
cv::eigen2cv(eigval_imag, eigenval_imag);
cv::eigen2cv(eigvec_real, eigenvec_real);
cv::eigen2cv(eigvec_imag, eigenvec_imag);
#else
EigenvalueDecomposition es(Mtilde);
eigenval_real = es.eigenvalues();
eigenvec_real = es.eigenvectors();
eigenval_imag = eigenvec_imag = cv::Mat();
#endif
}
void dls::fill_coeff(const cv::Mat * D_mat)
{
// TODO: shift D and coefficients one position to left
double D[10][10]; // put D_mat into array
for (int i = 0; i < D_mat->rows; ++i)
{
const double* Di = D_mat->ptr<double>(i);
for (int j = 0; j < D_mat->cols; ++j)
{
D[i+1][j+1] = Di[j];
}
}
// F1 COEFFICIENT
f1coeff[1] = 2*D[1][6] - 2*D[1][8] + 2*D[5][6] - 2*D[5][8] + 2*D[6][1] + 2*D[6][5] + 2*D[6][9] - 2*D[8][1] - 2*D[8][5] - 2*D[8][9] + 2*D[9][6] - 2*D[9][8]; // constant term
f1coeff[2] = 6*D[1][2] + 6*D[1][4] + 6*D[2][1] - 6*D[2][5] - 6*D[2][9] + 6*D[4][1] - 6*D[4][5] - 6*D[4][9] - 6*D[5][2] - 6*D[5][4] - 6*D[9][2] - 6*D[9][4]; // s1^2 * s2
f1coeff[3] = 4*D[1][7] - 4*D[1][3] + 8*D[2][6] - 8*D[2][8] - 4*D[3][1] + 4*D[3][5] + 4*D[3][9] + 8*D[4][6] - 8*D[4][8] + 4*D[5][3] - 4*D[5][7] + 8*D[6][2] + 8*D[6][4] + 4*D[7][1] - 4*D[7][5] - 4*D[7][9] - 8*D[8][2] - 8*D[8][4] + 4*D[9][3] - 4*D[9][7]; // s1 * s2
f1coeff[4] = 4*D[1][2] - 4*D[1][4] + 4*D[2][1] - 4*D[2][5] - 4*D[2][9] + 8*D[3][6] - 8*D[3][8] - 4*D[4][1] + 4*D[4][5] + 4*D[4][9] - 4*D[5][2] + 4*D[5][4] + 8*D[6][3] + 8*D[6][7] + 8*D[7][6] - 8*D[7][8] - 8*D[8][3] - 8*D[8][7] - 4*D[9][2] + 4*D[9][4]; //s1 * s3
f1coeff[5] = 8*D[2][2] - 8*D[3][3] - 8*D[4][4] + 8*D[6][6] + 8*D[7][7] - 8*D[8][8]; // s2 * s3
f1coeff[6] = 4*D[2][6] - 2*D[1][7] - 2*D[1][3] + 4*D[2][8] - 2*D[3][1] + 2*D[3][5] - 2*D[3][9] + 4*D[4][6] + 4*D[4][8] + 2*D[5][3] + 2*D[5][7] + 4*D[6][2] + 4*D[6][4] - 2*D[7][1] + 2*D[7][5] - 2*D[7][9] + 4*D[8][2] + 4*D[8][4] - 2*D[9][3] - 2*D[9][7]; // s2^2 * s3
f1coeff[7] = 2*D[2][5] - 2*D[1][4] - 2*D[2][1] - 2*D[1][2] - 2*D[2][9] - 2*D[4][1] + 2*D[4][5] - 2*D[4][9] + 2*D[5][2] + 2*D[5][4] - 2*D[9][2] - 2*D[9][4]; //s2^3
f1coeff[8] = 4*D[1][9] - 4*D[1][1] + 8*D[3][3] + 8*D[3][7] + 4*D[5][5] + 8*D[7][3] + 8*D[7][7] + 4*D[9][1] - 4*D[9][9]; // s1 * s3^2
f1coeff[9] = 4*D[1][1] - 4*D[5][5] - 4*D[5][9] + 8*D[6][6] - 8*D[6][8] - 8*D[8][6] + 8*D[8][8] - 4*D[9][5] - 4*D[9][9]; // s1
f1coeff[10] = 2*D[1][3] + 2*D[1][7] + 4*D[2][6] - 4*D[2][8] + 2*D[3][1] + 2*D[3][5] + 2*D[3][9] - 4*D[4][6] + 4*D[4][8] + 2*D[5][3] + 2*D[5][7] + 4*D[6][2] - 4*D[6][4] + 2*D[7][1] + 2*D[7][5] + 2*D[7][9] - 4*D[8][2] + 4*D[8][4] + 2*D[9][3] + 2*D[9][7]; // s3
f1coeff[11] = 2*D[1][2] + 2*D[1][4] + 2*D[2][1] + 2*D[2][5] + 2*D[2][9] - 4*D[3][6] + 4*D[3][8] + 2*D[4][1] + 2*D[4][5] + 2*D[4][9] + 2*D[5][2] + 2*D[5][4] - 4*D[6][3] + 4*D[6][7] + 4*D[7][6] - 4*D[7][8] + 4*D[8][3] - 4*D[8][7] + 2*D[9][2] + 2*D[9][4]; // s2
f1coeff[12] = 2*D[2][9] - 2*D[1][4] - 2*D[2][1] - 2*D[2][5] - 2*D[1][2] + 4*D[3][6] + 4*D[3][8] - 2*D[4][1] - 2*D[4][5] + 2*D[4][9] - 2*D[5][2] - 2*D[5][4] + 4*D[6][3] + 4*D[6][7] + 4*D[7][6] + 4*D[7][8] + 4*D[8][3] + 4*D[8][7] + 2*D[9][2] + 2*D[9][4]; // s2 * s3^2
f1coeff[13] = 6*D[1][6] - 6*D[1][8] - 6*D[5][6] + 6*D[5][8] + 6*D[6][1] - 6*D[6][5] - 6*D[6][9] - 6*D[8][1] + 6*D[8][5] + 6*D[8][9] - 6*D[9][6] + 6*D[9][8]; // s1^2
f1coeff[14] = 2*D[1][8] - 2*D[1][6] + 4*D[2][3] + 4*D[2][7] + 4*D[3][2] - 4*D[3][4] - 4*D[4][3] - 4*D[4][7] - 2*D[5][6] + 2*D[5][8] - 2*D[6][1] - 2*D[6][5] + 2*D[6][9] + 4*D[7][2] - 4*D[7][4] + 2*D[8][1] + 2*D[8][5] - 2*D[8][9] + 2*D[9][6] - 2*D[9][8]; // s3^2
f1coeff[15] = 2*D[1][8] - 2*D[1][6] - 4*D[2][3] + 4*D[2][7] - 4*D[3][2] - 4*D[3][4] - 4*D[4][3] + 4*D[4][7] + 2*D[5][6] - 2*D[5][8] - 2*D[6][1] + 2*D[6][5] - 2*D[6][9] + 4*D[7][2] + 4*D[7][4] + 2*D[8][1] - 2*D[8][5] + 2*D[8][9] - 2*D[9][6] + 2*D[9][8]; // s2^2
f1coeff[16] = 2*D[3][9] - 2*D[1][7] - 2*D[3][1] - 2*D[3][5] - 2*D[1][3] - 2*D[5][3] - 2*D[5][7] - 2*D[7][1] - 2*D[7][5] + 2*D[7][9] + 2*D[9][3] + 2*D[9][7]; // s3^3
f1coeff[17] = 4*D[1][6] + 4*D[1][8] + 8*D[2][3] + 8*D[2][7] + 8*D[3][2] + 8*D[3][4] + 8*D[4][3] + 8*D[4][7] - 4*D[5][6] - 4*D[5][8] + 4*D[6][1] - 4*D[6][5] - 4*D[6][9] + 8*D[7][2] + 8*D[7][4] + 4*D[8][1] - 4*D[8][5] - 4*D[8][9] - 4*D[9][6] - 4*D[9][8]; // s1 * s2 * s3
f1coeff[18] = 4*D[1][5] - 4*D[1][1] + 8*D[2][2] + 8*D[2][4] + 8*D[4][2] + 8*D[4][4] + 4*D[5][1] - 4*D[5][5] + 4*D[9][9]; // s1 * s2^2
f1coeff[19] = 6*D[1][3] + 6*D[1][7] + 6*D[3][1] - 6*D[3][5] - 6*D[3][9] - 6*D[5][3] - 6*D[5][7] + 6*D[7][1] - 6*D[7][5] - 6*D[7][9] - 6*D[9][3] - 6*D[9][7]; // s1^2 * s3
f1coeff[20] = 4*D[1][1] - 4*D[1][5] - 4*D[1][9] - 4*D[5][1] + 4*D[5][5] + 4*D[5][9] - 4*D[9][1] + 4*D[9][5] + 4*D[9][9]; // s1^3
// F2 COEFFICIENT
f2coeff[1] = - 2*D[1][3] + 2*D[1][7] - 2*D[3][1] - 2*D[3][5] - 2*D[3][9] - 2*D[5][3] + 2*D[5][7] + 2*D[7][1] + 2*D[7][5] + 2*D[7][9] - 2*D[9][3] + 2*D[9][7]; // constant term
f2coeff[2] = 4*D[1][5] - 4*D[1][1] + 8*D[2][2] + 8*D[2][4] + 8*D[4][2] + 8*D[4][4] + 4*D[5][1] - 4*D[5][5] + 4*D[9][9]; // s1^2 * s2
f2coeff[3] = 4*D[1][8] - 4*D[1][6] - 8*D[2][3] + 8*D[2][7] - 8*D[3][2] - 8*D[3][4] - 8*D[4][3] + 8*D[4][7] + 4*D[5][6] - 4*D[5][8] - 4*D[6][1] + 4*D[6][5] - 4*D[6][9] + 8*D[7][2] + 8*D[7][4] + 4*D[8][1] - 4*D[8][5] + 4*D[8][9] - 4*D[9][6] + 4*D[9][8]; // s1 * s2
f2coeff[4] = 8*D[2][2] - 8*D[3][3] - 8*D[4][4] + 8*D[6][6] + 8*D[7][7] - 8*D[8][8]; // s1 * s3
f2coeff[5] = 4*D[1][4] - 4*D[1][2] - 4*D[2][1] + 4*D[2][5] - 4*D[2][9] - 8*D[3][6] - 8*D[3][8] + 4*D[4][1] - 4*D[4][5] + 4*D[4][9] + 4*D[5][2] - 4*D[5][4] - 8*D[6][3] + 8*D[6][7] + 8*D[7][6] + 8*D[7][8] - 8*D[8][3] + 8*D[8][7] - 4*D[9][2] + 4*D[9][4]; // s2 * s3
f2coeff[6] = 6*D[5][6] - 6*D[1][8] - 6*D[1][6] + 6*D[5][8] - 6*D[6][1] + 6*D[6][5] - 6*D[6][9] - 6*D[8][1] + 6*D[8][5] - 6*D[8][9] - 6*D[9][6] - 6*D[9][8]; // s2^2 * s3
f2coeff[7] = 4*D[1][1] - 4*D[1][5] + 4*D[1][9] - 4*D[5][1] + 4*D[5][5] - 4*D[5][9] + 4*D[9][1] - 4*D[9][5] + 4*D[9][9]; // s2^3
f2coeff[8] = 2*D[2][9] - 2*D[1][4] - 2*D[2][1] - 2*D[2][5] - 2*D[1][2] + 4*D[3][6] + 4*D[3][8] - 2*D[4][1] - 2*D[4][5] + 2*D[4][9] - 2*D[5][2] - 2*D[5][4] + 4*D[6][3] + 4*D[6][7] + 4*D[7][6] + 4*D[7][8] + 4*D[8][3] + 4*D[8][7] + 2*D[9][2] + 2*D[9][4]; // s1 * s3^2
f2coeff[9] = 2*D[1][2] + 2*D[1][4] + 2*D[2][1] + 2*D[2][5] + 2*D[2][9] - 4*D[3][6] + 4*D[3][8] + 2*D[4][1] + 2*D[4][5] + 2*D[4][9] + 2*D[5][2] + 2*D[5][4] - 4*D[6][3] + 4*D[6][7] + 4*D[7][6] - 4*D[7][8] + 4*D[8][3] - 4*D[8][7] + 2*D[9][2] + 2*D[9][4]; // s1
f2coeff[10] = 2*D[1][6] + 2*D[1][8] - 4*D[2][3] + 4*D[2][7] - 4*D[3][2] + 4*D[3][4] + 4*D[4][3] - 4*D[4][7] + 2*D[5][6] + 2*D[5][8] + 2*D[6][1] + 2*D[6][5] + 2*D[6][9] + 4*D[7][2] - 4*D[7][4] + 2*D[8][1] + 2*D[8][5] + 2*D[8][9] + 2*D[9][6] + 2*D[9][8]; // s3
f2coeff[11] = 8*D[3][3] - 4*D[1][9] - 4*D[1][1] - 8*D[3][7] + 4*D[5][5] - 8*D[7][3] + 8*D[7][7] - 4*D[9][1] - 4*D[9][9]; // s2
f2coeff[12] = 4*D[1][1] - 4*D[5][5] + 4*D[5][9] + 8*D[6][6] + 8*D[6][8] + 8*D[8][6] + 8*D[8][8] + 4*D[9][5] - 4*D[9][9]; // s2 * s3^2
f2coeff[13] = 2*D[1][7] - 2*D[1][3] + 4*D[2][6] - 4*D[2][8] - 2*D[3][1] + 2*D[3][5] + 2*D[3][9] + 4*D[4][6] - 4*D[4][8] + 2*D[5][3] - 2*D[5][7] + 4*D[6][2] + 4*D[6][4] + 2*D[7][1] - 2*D[7][5] - 2*D[7][9] - 4*D[8][2] - 4*D[8][4] + 2*D[9][3] - 2*D[9][7]; // s1^2
f2coeff[14] = 2*D[1][3] - 2*D[1][7] + 4*D[2][6] + 4*D[2][8] + 2*D[3][1] + 2*D[3][5] - 2*D[3][9] - 4*D[4][6] - 4*D[4][8] + 2*D[5][3] - 2*D[5][7] + 4*D[6][2] - 4*D[6][4] - 2*D[7][1] - 2*D[7][5] + 2*D[7][9] + 4*D[8][2] - 4*D[8][4] - 2*D[9][3] + 2*D[9][7]; // s3^2
f2coeff[15] = 6*D[1][3] - 6*D[1][7] + 6*D[3][1] - 6*D[3][5] + 6*D[3][9] - 6*D[5][3] + 6*D[5][7] - 6*D[7][1] + 6*D[7][5] - 6*D[7][9] + 6*D[9][3] - 6*D[9][7]; // s2^2
f2coeff[16] = 2*D[6][9] - 2*D[1][8] - 2*D[5][6] - 2*D[5][8] - 2*D[6][1] - 2*D[6][5] - 2*D[1][6] - 2*D[8][1] - 2*D[8][5] + 2*D[8][9] + 2*D[9][6] + 2*D[9][8]; // s3^3
f2coeff[17] = 8*D[2][6] - 4*D[1][7] - 4*D[1][3] + 8*D[2][8] - 4*D[3][1] + 4*D[3][5] - 4*D[3][9] + 8*D[4][6] + 8*D[4][8] + 4*D[5][3] + 4*D[5][7] + 8*D[6][2] + 8*D[6][4] - 4*D[7][1] + 4*D[7][5] - 4*D[7][9] + 8*D[8][2] + 8*D[8][4] - 4*D[9][3] - 4*D[9][7]; // s1 * s2 * s3
f2coeff[18] = 6*D[2][5] - 6*D[1][4] - 6*D[2][1] - 6*D[1][2] - 6*D[2][9] - 6*D[4][1] + 6*D[4][5] - 6*D[4][9] + 6*D[5][2] + 6*D[5][4] - 6*D[9][2] - 6*D[9][4]; // s1 * s2^2
f2coeff[19] = 2*D[1][6] + 2*D[1][8] + 4*D[2][3] + 4*D[2][7] + 4*D[3][2] + 4*D[3][4] + 4*D[4][3] + 4*D[4][7] - 2*D[5][6] - 2*D[5][8] + 2*D[6][1] - 2*D[6][5] - 2*D[6][9] + 4*D[7][2] + 4*D[7][4] + 2*D[8][1] - 2*D[8][5] - 2*D[8][9] - 2*D[9][6] - 2*D[9][8]; // s1^2 * s3
f2coeff[20] = 2*D[1][2] + 2*D[1][4] + 2*D[2][1] - 2*D[2][5] - 2*D[2][9] + 2*D[4][1] - 2*D[4][5] - 2*D[4][9] - 2*D[5][2] - 2*D[5][4] - 2*D[9][2] - 2*D[9][4]; // s1^3
// F3 COEFFICIENT
f3coeff[1] = 2*D[1][2] - 2*D[1][4] + 2*D[2][1] + 2*D[2][5] + 2*D[2][9] - 2*D[4][1] - 2*D[4][5] - 2*D[4][9] + 2*D[5][2] - 2*D[5][4] + 2*D[9][2] - 2*D[9][4]; // constant term
f3coeff[2] = 2*D[1][6] + 2*D[1][8] + 4*D[2][3] + 4*D[2][7] + 4*D[3][2] + 4*D[3][4] + 4*D[4][3] + 4*D[4][7] - 2*D[5][6] - 2*D[5][8] + 2*D[6][1] - 2*D[6][5] - 2*D[6][9] + 4*D[7][2] + 4*D[7][4] + 2*D[8][1] - 2*D[8][5] - 2*D[8][9] - 2*D[9][6] - 2*D[9][8]; // s1^2 * s2
f3coeff[3] = 8*D[2][2] - 8*D[3][3] - 8*D[4][4] + 8*D[6][6] + 8*D[7][7] - 8*D[8][8]; // s1 * s2
f3coeff[4] = 4*D[1][8] - 4*D[1][6] + 8*D[2][3] + 8*D[2][7] + 8*D[3][2] - 8*D[3][4] - 8*D[4][3] - 8*D[4][7] - 4*D[5][6] + 4*D[5][8] - 4*D[6][1] - 4*D[6][5] + 4*D[6][9] + 8*D[7][2] - 8*D[7][4] + 4*D[8][1] + 4*D[8][5] - 4*D[8][9] + 4*D[9][6] - 4*D[9][8]; // s1 * s3
f3coeff[5] = 4*D[1][3] - 4*D[1][7] + 8*D[2][6] + 8*D[2][8] + 4*D[3][1] + 4*D[3][5] - 4*D[3][9] - 8*D[4][6] - 8*D[4][8] + 4*D[5][3] - 4*D[5][7] + 8*D[6][2] - 8*D[6][4] - 4*D[7][1] - 4*D[7][5] + 4*D[7][9] + 8*D[8][2] - 8*D[8][4] - 4*D[9][3] + 4*D[9][7]; // s2 * s3
f3coeff[6] = 4*D[1][1] - 4*D[5][5] + 4*D[5][9] + 8*D[6][6] + 8*D[6][8] + 8*D[8][6] + 8*D[8][8] + 4*D[9][5] - 4*D[9][9]; // s2^2 * s3
f3coeff[7] = 2*D[5][6] - 2*D[1][8] - 2*D[1][6] + 2*D[5][8] - 2*D[6][1] + 2*D[6][5] - 2*D[6][9] - 2*D[8][1] + 2*D[8][5] - 2*D[8][9] - 2*D[9][6] - 2*D[9][8]; // s2^3
f3coeff[8] = 6*D[3][9] - 6*D[1][7] - 6*D[3][1] - 6*D[3][5] - 6*D[1][3] - 6*D[5][3] - 6*D[5][7] - 6*D[7][1] - 6*D[7][5] + 6*D[7][9] + 6*D[9][3] + 6*D[9][7]; // s1 * s3^2
f3coeff[9] = 2*D[1][3] + 2*D[1][7] + 4*D[2][6] - 4*D[2][8] + 2*D[3][1] + 2*D[3][5] + 2*D[3][9] - 4*D[4][6] + 4*D[4][8] + 2*D[5][3] + 2*D[5][7] + 4*D[6][2] - 4*D[6][4] + 2*D[7][1] + 2*D[7][5] + 2*D[7][9] - 4*D[8][2] + 4*D[8][4] + 2*D[9][3] + 2*D[9][7]; // s1
f3coeff[10] = 8*D[2][2] - 4*D[1][5] - 4*D[1][1] - 8*D[2][4] - 8*D[4][2] + 8*D[4][4] - 4*D[5][1] - 4*D[5][5] + 4*D[9][9]; // s3
f3coeff[11] = 2*D[1][6] + 2*D[1][8] - 4*D[2][3] + 4*D[2][7] - 4*D[3][2] + 4*D[3][4] + 4*D[4][3] - 4*D[4][7] + 2*D[5][6] + 2*D[5][8] + 2*D[6][1] + 2*D[6][5] + 2*D[6][9] + 4*D[7][2] - 4*D[7][4] + 2*D[8][1] + 2*D[8][5] + 2*D[8][9] + 2*D[9][6] + 2*D[9][8]; // s2
f3coeff[12] = 6*D[6][9] - 6*D[1][8] - 6*D[5][6] - 6*D[5][8] - 6*D[6][1] - 6*D[6][5] - 6*D[1][6] - 6*D[8][1] - 6*D[8][5] + 6*D[8][9] + 6*D[9][6] + 6*D[9][8]; // s2 * s3^2
f3coeff[13] = 2*D[1][2] - 2*D[1][4] + 2*D[2][1] - 2*D[2][5] - 2*D[2][9] + 4*D[3][6] - 4*D[3][8] - 2*D[4][1] + 2*D[4][5] + 2*D[4][9] - 2*D[5][2] + 2*D[5][4] + 4*D[6][3] + 4*D[6][7] + 4*D[7][6] - 4*D[7][8] - 4*D[8][3] - 4*D[8][7] - 2*D[9][2] + 2*D[9][4]; // s1^2
f3coeff[14] = 6*D[1][4] - 6*D[1][2] - 6*D[2][1] - 6*D[2][5] + 6*D[2][9] + 6*D[4][1] + 6*D[4][5] - 6*D[4][9] - 6*D[5][2] + 6*D[5][4] + 6*D[9][2] - 6*D[9][4]; // s3^2
f3coeff[15] = 2*D[1][4] - 2*D[1][2] - 2*D[2][1] + 2*D[2][5] - 2*D[2][9] - 4*D[3][6] - 4*D[3][8] + 2*D[4][1] - 2*D[4][5] + 2*D[4][9] + 2*D[5][2] - 2*D[5][4] - 4*D[6][3] + 4*D[6][7] + 4*D[7][6] + 4*D[7][8] - 4*D[8][3] + 4*D[8][7] - 2*D[9][2] + 2*D[9][4]; // s2^2
f3coeff[16] = 4*D[1][1] + 4*D[1][5] - 4*D[1][9] + 4*D[5][1] + 4*D[5][5] - 4*D[5][9] - 4*D[9][1] - 4*D[9][5] + 4*D[9][9]; // s3^3
f3coeff[17] = 4*D[2][9] - 4*D[1][4] - 4*D[2][1] - 4*D[2][5] - 4*D[1][2] + 8*D[3][6] + 8*D[3][8] - 4*D[4][1] - 4*D[4][5] + 4*D[4][9] - 4*D[5][2] - 4*D[5][4] + 8*D[6][3] + 8*D[6][7] + 8*D[7][6] + 8*D[7][8] + 8*D[8][3] + 8*D[8][7] + 4*D[9][2] + 4*D[9][4]; // s1 * s2 * s3
f3coeff[18] = 4*D[2][6] - 2*D[1][7] - 2*D[1][3] + 4*D[2][8] - 2*D[3][1] + 2*D[3][5] - 2*D[3][9] + 4*D[4][6] + 4*D[4][8] + 2*D[5][3] + 2*D[5][7] + 4*D[6][2] + 4*D[6][4] - 2*D[7][1] + 2*D[7][5] - 2*D[7][9] + 4*D[8][2] + 4*D[8][4] - 2*D[9][3] - 2*D[9][7]; // s1 * s2^2
f3coeff[19] = 4*D[1][9] - 4*D[1][1] + 8*D[3][3] + 8*D[3][7] + 4*D[5][5] + 8*D[7][3] + 8*D[7][7] + 4*D[9][1] - 4*D[9][9]; // s1^2 * s3
f3coeff[20] = 2*D[1][3] + 2*D[1][7] + 2*D[3][1] - 2*D[3][5] - 2*D[3][9] - 2*D[5][3] - 2*D[5][7] + 2*D[7][1] - 2*D[7][5] - 2*D[7][9] - 2*D[9][3] - 2*D[9][7]; // s1^3
}
cv::Mat dls::LeftMultVec(const cv::Mat& v)
{
cv::Mat mat_ = cv::Mat::zeros(3, 9, CV_64F);
for (int i = 0; i < 3; ++i)
{
mat_.at<double>(i, 3*i + 0) = v.at<double>(0);
mat_.at<double>(i, 3*i + 1) = v.at<double>(1);
mat_.at<double>(i, 3*i + 2) = v.at<double>(2);
}
return mat_;
}
cv::Mat dls::cayley_LS_M(const std::vector<double>& a, const std::vector<double>& b, const std::vector<double>& c, const std::vector<double>& u)
{
// TODO: input matrix pointer
// TODO: shift coefficients one position to left
cv::Mat M = cv::Mat::zeros(120, 120, CV_64F);
M.at<double>(0,0)=u[1]; M.at<double>(0,35)=a[1]; M.at<double>(0,83)=b[1]; M.at<double>(0,118)=c[1];
M.at<double>(1,0)=u[4]; M.at<double>(1,1)=u[1]; M.at<double>(1,34)=a[1]; M.at<double>(1,35)=a[10]; M.at<double>(1,54)=b[1]; M.at<double>(1,83)=b[10]; M.at<double>(1,99)=c[1]; M.at<double>(1,118)=c[10];
M.at<double>(2,1)=u[4]; M.at<double>(2,2)=u[1]; M.at<double>(2,34)=a[10]; M.at<double>(2,35)=a[14]; M.at<double>(2,51)=a[1]; M.at<double>(2,54)=b[10]; M.at<double>(2,65)=b[1]; M.at<double>(2,83)=b[14]; M.at<double>(2,89)=c[1]; M.at<double>(2,99)=c[10]; M.at<double>(2,118)=c[14];
M.at<double>(3,0)=u[3]; M.at<double>(3,3)=u[1]; M.at<double>(3,35)=a[11]; M.at<double>(3,49)=a[1]; M.at<double>(3,76)=b[1]; M.at<double>(3,83)=b[11]; M.at<double>(3,118)=c[11]; M.at<double>(3,119)=c[1];
M.at<double>(4,1)=u[3]; M.at<double>(4,3)=u[4]; M.at<double>(4,4)=u[1]; M.at<double>(4,34)=a[11]; M.at<double>(4,35)=a[5]; M.at<double>(4,43)=a[1]; M.at<double>(4,49)=a[10]; M.at<double>(4,54)=b[11]; M.at<double>(4,71)=b[1]; M.at<double>(4,76)=b[10]; M.at<double>(4,83)=b[5]; M.at<double>(4,99)=c[11]; M.at<double>(4,100)=c[1]; M.at<double>(4,118)=c[5]; M.at<double>(4,119)=c[10];
M.at<double>(5,2)=u[3]; M.at<double>(5,4)=u[4]; M.at<double>(5,5)=u[1]; M.at<double>(5,34)=a[5]; M.at<double>(5,35)=a[12]; M.at<double>(5,41)=a[1]; M.at<double>(5,43)=a[10]; M.at<double>(5,49)=a[14]; M.at<double>(5,51)=a[11]; M.at<double>(5,54)=b[5]; M.at<double>(5,62)=b[1]; M.at<double>(5,65)=b[11]; M.at<double>(5,71)=b[10]; M.at<double>(5,76)=b[14]; M.at<double>(5,83)=b[12]; M.at<double>(5,89)=c[11]; M.at<double>(5,99)=c[5]; M.at<double>(5,100)=c[10]; M.at<double>(5,111)=c[1]; M.at<double>(5,118)=c[12]; M.at<double>(5,119)=c[14];
M.at<double>(6,3)=u[3]; M.at<double>(6,6)=u[1]; M.at<double>(6,30)=a[1]; M.at<double>(6,35)=a[15]; M.at<double>(6,49)=a[11]; M.at<double>(6,75)=b[1]; M.at<double>(6,76)=b[11]; M.at<double>(6,83)=b[15]; M.at<double>(6,107)=c[1]; M.at<double>(6,118)=c[15]; M.at<double>(6,119)=c[11];
M.at<double>(7,4)=u[3]; M.at<double>(7,6)=u[4]; M.at<double>(7,7)=u[1]; M.at<double>(7,30)=a[10]; M.at<double>(7,34)=a[15]; M.at<double>(7,35)=a[6]; M.at<double>(7,43)=a[11]; M.at<double>(7,45)=a[1]; M.at<double>(7,49)=a[5]; M.at<double>(7,54)=b[15]; M.at<double>(7,63)=b[1]; M.at<double>(7,71)=b[11]; M.at<double>(7,75)=b[10]; M.at<double>(7,76)=b[5]; M.at<double>(7,83)=b[6]; M.at<double>(7,99)=c[15]; M.at<double>(7,100)=c[11]; M.at<double>(7,107)=c[10]; M.at<double>(7,112)=c[1]; M.at<double>(7,118)=c[6]; M.at<double>(7,119)=c[5];
M.at<double>(8,5)=u[3]; M.at<double>(8,7)=u[4]; M.at<double>(8,8)=u[1]; M.at<double>(8,30)=a[14]; M.at<double>(8,34)=a[6]; M.at<double>(8,41)=a[11]; M.at<double>(8,43)=a[5]; M.at<double>(8,45)=a[10]; M.at<double>(8,46)=a[1]; M.at<double>(8,49)=a[12]; M.at<double>(8,51)=a[15]; M.at<double>(8,54)=b[6]; M.at<double>(8,62)=b[11]; M.at<double>(8,63)=b[10]; M.at<double>(8,65)=b[15]; M.at<double>(8,66)=b[1]; M.at<double>(8,71)=b[5]; M.at<double>(8,75)=b[14]; M.at<double>(8,76)=b[12]; M.at<double>(8,89)=c[15]; M.at<double>(8,99)=c[6]; M.at<double>(8,100)=c[5]; M.at<double>(8,102)=c[1]; M.at<double>(8,107)=c[14]; M.at<double>(8,111)=c[11]; M.at<double>(8,112)=c[10]; M.at<double>(8,119)=c[12];
M.at<double>(9,0)=u[2]; M.at<double>(9,9)=u[1]; M.at<double>(9,35)=a[9]; M.at<double>(9,36)=a[1]; M.at<double>(9,83)=b[9]; M.at<double>(9,84)=b[1]; M.at<double>(9,88)=c[1]; M.at<double>(9,118)=c[9];
M.at<double>(10,1)=u[2]; M.at<double>(10,9)=u[4]; M.at<double>(10,10)=u[1]; M.at<double>(10,33)=a[1]; M.at<double>(10,34)=a[9]; M.at<double>(10,35)=a[4]; M.at<double>(10,36)=a[10]; M.at<double>(10,54)=b[9]; M.at<double>(10,59)=b[1]; M.at<double>(10,83)=b[4]; M.at<double>(10,84)=b[10]; M.at<double>(10,88)=c[10]; M.at<double>(10,99)=c[9]; M.at<double>(10,117)=c[1]; M.at<double>(10,118)=c[4];
M.at<double>(11,2)=u[2]; M.at<double>(11,10)=u[4]; M.at<double>(11,11)=u[1]; M.at<double>(11,28)=a[1]; M.at<double>(11,33)=a[10]; M.at<double>(11,34)=a[4]; M.at<double>(11,35)=a[8]; M.at<double>(11,36)=a[14]; M.at<double>(11,51)=a[9]; M.at<double>(11,54)=b[4]; M.at<double>(11,57)=b[1]; M.at<double>(11,59)=b[10]; M.at<double>(11,65)=b[9]; M.at<double>(11,83)=b[8]; M.at<double>(11,84)=b[14]; M.at<double>(11,88)=c[14]; M.at<double>(11,89)=c[9]; M.at<double>(11,99)=c[4]; M.at<double>(11,114)=c[1]; M.at<double>(11,117)=c[10]; M.at<double>(11,118)=c[8];
M.at<double>(12,3)=u[2]; M.at<double>(12,9)=u[3]; M.at<double>(12,12)=u[1]; M.at<double>(12,35)=a[3]; M.at<double>(12,36)=a[11]; M.at<double>(12,39)=a[1]; M.at<double>(12,49)=a[9]; M.at<double>(12,76)=b[9]; M.at<double>(12,79)=b[1]; M.at<double>(12,83)=b[3]; M.at<double>(12,84)=b[11]; M.at<double>(12,88)=c[11]; M.at<double>(12,96)=c[1]; M.at<double>(12,118)=c[3]; M.at<double>(12,119)=c[9];
M.at<double>(13,4)=u[2]; M.at<double>(13,10)=u[3]; M.at<double>(13,12)=u[4]; M.at<double>(13,13)=u[1]; M.at<double>(13,33)=a[11]; M.at<double>(13,34)=a[3]; M.at<double>(13,35)=a[17]; M.at<double>(13,36)=a[5]; M.at<double>(13,39)=a[10]; M.at<double>(13,43)=a[9]; M.at<double>(13,47)=a[1]; M.at<double>(13,49)=a[4]; M.at<double>(13,54)=b[3]; M.at<double>(13,59)=b[11]; M.at<double>(13,60)=b[1]; M.at<double>(13,71)=b[9]; M.at<double>(13,76)=b[4]; M.at<double>(13,79)=b[10]; M.at<double>(13,83)=b[17]; M.at<double>(13,84)=b[5]; M.at<double>(13,88)=c[5]; M.at<double>(13,90)=c[1]; M.at<double>(13,96)=c[10]; M.at<double>(13,99)=c[3]; M.at<double>(13,100)=c[9]; M.at<double>(13,117)=c[11]; M.at<double>(13,118)=c[17]; M.at<double>(13,119)=c[4];
M.at<double>(14,5)=u[2]; M.at<double>(14,11)=u[3]; M.at<double>(14,13)=u[4]; M.at<double>(14,14)=u[1]; M.at<double>(14,28)=a[11]; M.at<double>(14,33)=a[5]; M.at<double>(14,34)=a[17]; M.at<double>(14,36)=a[12]; M.at<double>(14,39)=a[14]; M.at<double>(14,41)=a[9]; M.at<double>(14,42)=a[1]; M.at<double>(14,43)=a[4]; M.at<double>(14,47)=a[10]; M.at<double>(14,49)=a[8]; M.at<double>(14,51)=a[3]; M.at<double>(14,54)=b[17]; M.at<double>(14,56)=b[1]; M.at<double>(14,57)=b[11]; M.at<double>(14,59)=b[5]; M.at<double>(14,60)=b[10]; M.at<double>(14,62)=b[9]; M.at<double>(14,65)=b[3]; M.at<double>(14,71)=b[4]; M.at<double>(14,76)=b[8]; M.at<double>(14,79)=b[14]; M.at<double>(14,84)=b[12]; M.at<double>(14,88)=c[12]; M.at<double>(14,89)=c[3]; M.at<double>(14,90)=c[10]; M.at<double>(14,96)=c[14]; M.at<double>(14,99)=c[17]; M.at<double>(14,100)=c[4]; M.at<double>(14,106)=c[1]; M.at<double>(14,111)=c[9]; M.at<double>(14,114)=c[11]; M.at<double>(14,117)=c[5]; M.at<double>(14,119)=c[8];
M.at<double>(15,6)=u[2]; M.at<double>(15,12)=u[3]; M.at<double>(15,15)=u[1]; M.at<double>(15,29)=a[1]; M.at<double>(15,30)=a[9]; M.at<double>(15,35)=a[18]; M.at<double>(15,36)=a[15]; M.at<double>(15,39)=a[11]; M.at<double>(15,49)=a[3]; M.at<double>(15,74)=b[1]; M.at<double>(15,75)=b[9]; M.at<double>(15,76)=b[3]; M.at<double>(15,79)=b[11]; M.at<double>(15,83)=b[18]; M.at<double>(15,84)=b[15]; M.at<double>(15,88)=c[15]; M.at<double>(15,94)=c[1]; M.at<double>(15,96)=c[11]; M.at<double>(15,107)=c[9]; M.at<double>(15,118)=c[18]; M.at<double>(15,119)=c[3];
M.at<double>(16,7)=u[2]; M.at<double>(16,13)=u[3]; M.at<double>(16,15)=u[4]; M.at<double>(16,16)=u[1]; M.at<double>(16,29)=a[10]; M.at<double>(16,30)=a[4]; M.at<double>(16,33)=a[15]; M.at<double>(16,34)=a[18]; M.at<double>(16,36)=a[6]; M.at<double>(16,39)=a[5]; M.at<double>(16,43)=a[3]; M.at<double>(16,44)=a[1]; M.at<double>(16,45)=a[9]; M.at<double>(16,47)=a[11]; M.at<double>(16,49)=a[17]; M.at<double>(16,54)=b[18]; M.at<double>(16,59)=b[15]; M.at<double>(16,60)=b[11]; M.at<double>(16,63)=b[9]; M.at<double>(16,68)=b[1]; M.at<double>(16,71)=b[3]; M.at<double>(16,74)=b[10]; M.at<double>(16,75)=b[4]; M.at<double>(16,76)=b[17]; M.at<double>(16,79)=b[5]; M.at<double>(16,84)=b[6]; M.at<double>(16,88)=c[6]; M.at<double>(16,90)=c[11]; M.at<double>(16,94)=c[10]; M.at<double>(16,96)=c[5]; M.at<double>(16,97)=c[1]; M.at<double>(16,99)=c[18]; M.at<double>(16,100)=c[3]; M.at<double>(16,107)=c[4]; M.at<double>(16,112)=c[9]; M.at<double>(16,117)=c[15]; M.at<double>(16,119)=c[17];
M.at<double>(17,8)=u[2]; M.at<double>(17,14)=u[3]; M.at<double>(17,16)=u[4]; M.at<double>(17,17)=u[1]; M.at<double>(17,28)=a[15]; M.at<double>(17,29)=a[14]; M.at<double>(17,30)=a[8]; M.at<double>(17,33)=a[6]; M.at<double>(17,39)=a[12]; M.at<double>(17,41)=a[3]; M.at<double>(17,42)=a[11]; M.at<double>(17,43)=a[17]; M.at<double>(17,44)=a[10]; M.at<double>(17,45)=a[4]; M.at<double>(17,46)=a[9]; M.at<double>(17,47)=a[5]; M.at<double>(17,51)=a[18]; M.at<double>(17,56)=b[11]; M.at<double>(17,57)=b[15]; M.at<double>(17,59)=b[6]; M.at<double>(17,60)=b[5]; M.at<double>(17,62)=b[3]; M.at<double>(17,63)=b[4]; M.at<double>(17,65)=b[18]; M.at<double>(17,66)=b[9]; M.at<double>(17,68)=b[10]; M.at<double>(17,71)=b[17]; M.at<double>(17,74)=b[14]; M.at<double>(17,75)=b[8]; M.at<double>(17,79)=b[12]; M.at<double>(17,89)=c[18]; M.at<double>(17,90)=c[5]; M.at<double>(17,94)=c[14]; M.at<double>(17,96)=c[12]; M.at<double>(17,97)=c[10]; M.at<double>(17,100)=c[17]; M.at<double>(17,102)=c[9]; M.at<double>(17,106)=c[11]; M.at<double>(17,107)=c[8]; M.at<double>(17,111)=c[3]; M.at<double>(17,112)=c[4]; M.at<double>(17,114)=c[15]; M.at<double>(17,117)=c[6];
M.at<double>(18,9)=u[2]; M.at<double>(18,18)=u[1]; M.at<double>(18,35)=a[13]; M.at<double>(18,36)=a[9]; M.at<double>(18,53)=a[1]; M.at<double>(18,82)=b[1]; M.at<double>(18,83)=b[13]; M.at<double>(18,84)=b[9]; M.at<double>(18,87)=c[1]; M.at<double>(18,88)=c[9]; M.at<double>(18,118)=c[13];
M.at<double>(19,10)=u[2]; M.at<double>(19,18)=u[4]; M.at<double>(19,19)=u[1]; M.at<double>(19,32)=a[1]; M.at<double>(19,33)=a[9]; M.at<double>(19,34)=a[13]; M.at<double>(19,35)=a[19]; M.at<double>(19,36)=a[4]; M.at<double>(19,53)=a[10]; M.at<double>(19,54)=b[13]; M.at<double>(19,59)=b[9]; M.at<double>(19,61)=b[1]; M.at<double>(19,82)=b[10]; M.at<double>(19,83)=b[19]; M.at<double>(19,84)=b[4]; M.at<double>(19,87)=c[10]; M.at<double>(19,88)=c[4]; M.at<double>(19,99)=c[13]; M.at<double>(19,116)=c[1]; M.at<double>(19,117)=c[9]; M.at<double>(19,118)=c[19];
M.at<double>(20,11)=u[2]; M.at<double>(20,19)=u[4]; M.at<double>(20,20)=u[1]; M.at<double>(20,27)=a[1]; M.at<double>(20,28)=a[9]; M.at<double>(20,32)=a[10]; M.at<double>(20,33)=a[4]; M.at<double>(20,34)=a[19]; M.at<double>(20,36)=a[8]; M.at<double>(20,51)=a[13]; M.at<double>(20,53)=a[14]; M.at<double>(20,54)=b[19]; M.at<double>(20,55)=b[1]; M.at<double>(20,57)=b[9]; M.at<double>(20,59)=b[4]; M.at<double>(20,61)=b[10]; M.at<double>(20,65)=b[13]; M.at<double>(20,82)=b[14]; M.at<double>(20,84)=b[8]; M.at<double>(20,87)=c[14]; M.at<double>(20,88)=c[8]; M.at<double>(20,89)=c[13]; M.at<double>(20,99)=c[19]; M.at<double>(20,113)=c[1]; M.at<double>(20,114)=c[9]; M.at<double>(20,116)=c[10]; M.at<double>(20,117)=c[4];
M.at<double>(21,12)=u[2]; M.at<double>(21,18)=u[3]; M.at<double>(21,21)=u[1]; M.at<double>(21,35)=a[2]; M.at<double>(21,36)=a[3]; M.at<double>(21,38)=a[1]; M.at<double>(21,39)=a[9]; M.at<double>(21,49)=a[13]; M.at<double>(21,53)=a[11]; M.at<double>(21,76)=b[13]; M.at<double>(21,78)=b[1]; M.at<double>(21,79)=b[9]; M.at<double>(21,82)=b[11]; M.at<double>(21,83)=b[2]; M.at<double>(21,84)=b[3]; M.at<double>(21,87)=c[11]; M.at<double>(21,88)=c[3]; M.at<double>(21,92)=c[1]; M.at<double>(21,96)=c[9]; M.at<double>(21,118)=c[2]; M.at<double>(21,119)=c[13];
M.at<double>(22,13)=u[2]; M.at<double>(22,19)=u[3]; M.at<double>(22,21)=u[4]; M.at<double>(22,22)=u[1]; M.at<double>(22,32)=a[11]; M.at<double>(22,33)=a[3]; M.at<double>(22,34)=a[2]; M.at<double>(22,36)=a[17]; M.at<double>(22,38)=a[10]; M.at<double>(22,39)=a[4]; M.at<double>(22,40)=a[1]; M.at<double>(22,43)=a[13]; M.at<double>(22,47)=a[9]; M.at<double>(22,49)=a[19]; M.at<double>(22,53)=a[5]; M.at<double>(22,54)=b[2]; M.at<double>(22,59)=b[3]; M.at<double>(22,60)=b[9]; M.at<double>(22,61)=b[11]; M.at<double>(22,71)=b[13]; M.at<double>(22,72)=b[1]; M.at<double>(22,76)=b[19]; M.at<double>(22,78)=b[10]; M.at<double>(22,79)=b[4]; M.at<double>(22,82)=b[5]; M.at<double>(22,84)=b[17]; M.at<double>(22,87)=c[5]; M.at<double>(22,88)=c[17]; M.at<double>(22,90)=c[9]; M.at<double>(22,92)=c[10]; M.at<double>(22,95)=c[1]; M.at<double>(22,96)=c[4]; M.at<double>(22,99)=c[2]; M.at<double>(22,100)=c[13]; M.at<double>(22,116)=c[11]; M.at<double>(22,117)=c[3]; M.at<double>(22,119)=c[19];
M.at<double>(23,14)=u[2]; M.at<double>(23,20)=u[3]; M.at<double>(23,22)=u[4]; M.at<double>(23,23)=u[1]; M.at<double>(23,27)=a[11]; M.at<double>(23,28)=a[3]; M.at<double>(23,32)=a[5]; M.at<double>(23,33)=a[17]; M.at<double>(23,38)=a[14]; M.at<double>(23,39)=a[8]; M.at<double>(23,40)=a[10]; M.at<double>(23,41)=a[13]; M.at<double>(23,42)=a[9]; M.at<double>(23,43)=a[19]; M.at<double>(23,47)=a[4]; M.at<double>(23,51)=a[2]; M.at<double>(23,53)=a[12]; M.at<double>(23,55)=b[11]; M.at<double>(23,56)=b[9]; M.at<double>(23,57)=b[3]; M.at<double>(23,59)=b[17]; M.at<double>(23,60)=b[4]; M.at<double>(23,61)=b[5]; M.at<double>(23,62)=b[13]; M.at<double>(23,65)=b[2]; M.at<double>(23,71)=b[19]; M.at<double>(23,72)=b[10]; M.at<double>(23,78)=b[14]; M.at<double>(23,79)=b[8]; M.at<double>(23,82)=b[12]; M.at<double>(23,87)=c[12]; M.at<double>(23,89)=c[2]; M.at<double>(23,90)=c[4]; M.at<double>(23,92)=c[14]; M.at<double>(23,95)=c[10]; M.at<double>(23,96)=c[8]; M.at<double>(23,100)=c[19]; M.at<double>(23,106)=c[9]; M.at<double>(23,111)=c[13]; M.at<double>(23,113)=c[11]; M.at<double>(23,114)=c[3]; M.at<double>(23,116)=c[5]; M.at<double>(23,117)=c[17];
M.at<double>(24,15)=u[2]; M.at<double>(24,21)=u[3]; M.at<double>(24,24)=u[1]; M.at<double>(24,29)=a[9]; M.at<double>(24,30)=a[13]; M.at<double>(24,36)=a[18]; M.at<double>(24,38)=a[11]; M.at<double>(24,39)=a[3]; M.at<double>(24,49)=a[2]; M.at<double>(24,52)=a[1]; M.at<double>(24,53)=a[15]; M.at<double>(24,73)=b[1]; M.at<double>(24,74)=b[9]; M.at<double>(24,75)=b[13]; M.at<double>(24,76)=b[2]; M.at<double>(24,78)=b[11]; M.at<double>(24,79)=b[3]; M.at<double>(24,82)=b[15]; M.at<double>(24,84)=b[18]; M.at<double>(24,87)=c[15]; M.at<double>(24,88)=c[18]; M.at<double>(24,92)=c[11]; M.at<double>(24,93)=c[1]; M.at<double>(24,94)=c[9]; M.at<double>(24,96)=c[3]; M.at<double>(24,107)=c[13]; M.at<double>(24,119)=c[2];
M.at<double>(25,16)=u[2]; M.at<double>(25,22)=u[3]; M.at<double>(25,24)=u[4]; M.at<double>(25,25)=u[1]; M.at<double>(25,29)=a[4]; M.at<double>(25,30)=a[19]; M.at<double>(25,32)=a[15]; M.at<double>(25,33)=a[18]; M.at<double>(25,38)=a[5]; M.at<double>(25,39)=a[17]; M.at<double>(25,40)=a[11]; M.at<double>(25,43)=a[2]; M.at<double>(25,44)=a[9]; M.at<double>(25,45)=a[13]; M.at<double>(25,47)=a[3]; M.at<double>(25,52)=a[10]; M.at<double>(25,53)=a[6]; M.at<double>(25,59)=b[18]; M.at<double>(25,60)=b[3]; M.at<double>(25,61)=b[15]; M.at<double>(25,63)=b[13]; M.at<double>(25,68)=b[9]; M.at<double>(25,71)=b[2]; M.at<double>(25,72)=b[11]; M.at<double>(25,73)=b[10]; M.at<double>(25,74)=b[4]; M.at<double>(25,75)=b[19]; M.at<double>(25,78)=b[5]; M.at<double>(25,79)=b[17]; M.at<double>(25,82)=b[6]; M.at<double>(25,87)=c[6]; M.at<double>(25,90)=c[3]; M.at<double>(25,92)=c[5]; M.at<double>(25,93)=c[10]; M.at<double>(25,94)=c[4]; M.at<double>(25,95)=c[11]; M.at<double>(25,96)=c[17]; M.at<double>(25,97)=c[9]; M.at<double>(25,100)=c[2]; M.at<double>(25,107)=c[19]; M.at<double>(25,112)=c[13]; M.at<double>(25,116)=c[15]; M.at<double>(25,117)=c[18];
M.at<double>(26,17)=u[2]; M.at<double>(26,23)=u[3]; M.at<double>(26,25)=u[4]; M.at<double>(26,26)=u[1]; M.at<double>(26,27)=a[15]; M.at<double>(26,28)=a[18]; M.at<double>(26,29)=a[8]; M.at<double>(26,32)=a[6]; M.at<double>(26,38)=a[12]; M.at<double>(26,40)=a[5]; M.at<double>(26,41)=a[2]; M.at<double>(26,42)=a[3]; M.at<double>(26,44)=a[4]; M.at<double>(26,45)=a[19]; M.at<double>(26,46)=a[13]; M.at<double>(26,47)=a[17]; M.at<double>(26,52)=a[14]; M.at<double>(26,55)=b[15]; M.at<double>(26,56)=b[3]; M.at<double>(26,57)=b[18]; M.at<double>(26,60)=b[17]; M.at<double>(26,61)=b[6]; M.at<double>(26,62)=b[2]; M.at<double>(26,63)=b[19]; M.at<double>(26,66)=b[13]; M.at<double>(26,68)=b[4]; M.at<double>(26,72)=b[5]; M.at<double>(26,73)=b[14]; M.at<double>(26,74)=b[8]; M.at<double>(26,78)=b[12]; M.at<double>(26,90)=c[17]; M.at<double>(26,92)=c[12]; M.at<double>(26,93)=c[14]; M.at<double>(26,94)=c[8]; M.at<double>(26,95)=c[5]; M.at<double>(26,97)=c[4]; M.at<double>(26,102)=c[13]; M.at<double>(26,106)=c[3]; M.at<double>(26,111)=c[2]; M.at<double>(26,112)=c[19]; M.at<double>(26,113)=c[15]; M.at<double>(26,114)=c[18]; M.at<double>(26,116)=c[6];
M.at<double>(27,15)=u[3]; M.at<double>(27,29)=a[11]; M.at<double>(27,30)=a[3]; M.at<double>(27,36)=a[7]; M.at<double>(27,39)=a[15]; M.at<double>(27,49)=a[18]; M.at<double>(27,69)=b[9]; M.at<double>(27,70)=b[1]; M.at<double>(27,74)=b[11]; M.at<double>(27,75)=b[3]; M.at<double>(27,76)=b[18]; M.at<double>(27,79)=b[15]; M.at<double>(27,84)=b[7]; M.at<double>(27,88)=c[7]; M.at<double>(27,91)=c[1]; M.at<double>(27,94)=c[11]; M.at<double>(27,96)=c[15]; M.at<double>(27,107)=c[3]; M.at<double>(27,110)=c[9]; M.at<double>(27,119)=c[18];
M.at<double>(28,6)=u[3]; M.at<double>(28,30)=a[11]; M.at<double>(28,35)=a[7]; M.at<double>(28,49)=a[15]; M.at<double>(28,69)=b[1]; M.at<double>(28,75)=b[11]; M.at<double>(28,76)=b[15]; M.at<double>(28,83)=b[7]; M.at<double>(28,107)=c[11]; M.at<double>(28,110)=c[1]; M.at<double>(28,118)=c[7]; M.at<double>(28,119)=c[15];
M.at<double>(29,24)=u[3]; M.at<double>(29,29)=a[3]; M.at<double>(29,30)=a[2]; M.at<double>(29,38)=a[15]; M.at<double>(29,39)=a[18]; M.at<double>(29,52)=a[11]; M.at<double>(29,53)=a[7]; M.at<double>(29,69)=b[13]; M.at<double>(29,70)=b[9]; M.at<double>(29,73)=b[11]; M.at<double>(29,74)=b[3]; M.at<double>(29,75)=b[2]; M.at<double>(29,78)=b[15]; M.at<double>(29,79)=b[18]; M.at<double>(29,82)=b[7]; M.at<double>(29,87)=c[7]; M.at<double>(29,91)=c[9]; M.at<double>(29,92)=c[15]; M.at<double>(29,93)=c[11]; M.at<double>(29,94)=c[3]; M.at<double>(29,96)=c[18]; M.at<double>(29,107)=c[2]; M.at<double>(29,110)=c[13];
M.at<double>(30,37)=a[18]; M.at<double>(30,48)=a[7]; M.at<double>(30,52)=a[2]; M.at<double>(30,70)=b[20]; M.at<double>(30,73)=b[2]; M.at<double>(30,77)=b[18]; M.at<double>(30,81)=b[7]; M.at<double>(30,85)=c[7]; M.at<double>(30,91)=c[20]; M.at<double>(30,93)=c[2]; M.at<double>(30,98)=c[18];
M.at<double>(31,29)=a[2]; M.at<double>(31,37)=a[15]; M.at<double>(31,38)=a[18]; M.at<double>(31,50)=a[7]; M.at<double>(31,52)=a[3]; M.at<double>(31,69)=b[20]; M.at<double>(31,70)=b[13]; M.at<double>(31,73)=b[3]; M.at<double>(31,74)=b[2]; M.at<double>(31,77)=b[15]; M.at<double>(31,78)=b[18]; M.at<double>(31,80)=b[7]; M.at<double>(31,86)=c[7]; M.at<double>(31,91)=c[13]; M.at<double>(31,92)=c[18]; M.at<double>(31,93)=c[3]; M.at<double>(31,94)=c[2]; M.at<double>(31,98)=c[15]; M.at<double>(31,110)=c[20];
M.at<double>(32,48)=a[9]; M.at<double>(32,50)=a[13]; M.at<double>(32,53)=a[20]; M.at<double>(32,80)=b[13]; M.at<double>(32,81)=b[9]; M.at<double>(32,82)=b[20]; M.at<double>(32,85)=c[9]; M.at<double>(32,86)=c[13]; M.at<double>(32,87)=c[20];
M.at<double>(33,29)=a[15]; M.at<double>(33,30)=a[18]; M.at<double>(33,39)=a[7]; M.at<double>(33,64)=b[9]; M.at<double>(33,69)=b[3]; M.at<double>(33,70)=b[11]; M.at<double>(33,74)=b[15]; M.at<double>(33,75)=b[18]; M.at<double>(33,79)=b[7]; M.at<double>(33,91)=c[11]; M.at<double>(33,94)=c[15]; M.at<double>(33,96)=c[7]; M.at<double>(33,103)=c[9]; M.at<double>(33,107)=c[18]; M.at<double>(33,110)=c[3];
M.at<double>(34,29)=a[18]; M.at<double>(34,38)=a[7]; M.at<double>(34,52)=a[15]; M.at<double>(34,64)=b[13]; M.at<double>(34,69)=b[2]; M.at<double>(34,70)=b[3]; M.at<double>(34,73)=b[15]; M.at<double>(34,74)=b[18]; M.at<double>(34,78)=b[7]; M.at<double>(34,91)=c[3]; M.at<double>(34,92)=c[7]; M.at<double>(34,93)=c[15]; M.at<double>(34,94)=c[18]; M.at<double>(34,103)=c[13]; M.at<double>(34,110)=c[2];
M.at<double>(35,37)=a[7]; M.at<double>(35,52)=a[18]; M.at<double>(35,64)=b[20]; M.at<double>(35,70)=b[2]; M.at<double>(35,73)=b[18]; M.at<double>(35,77)=b[7]; M.at<double>(35,91)=c[2]; M.at<double>(35,93)=c[18]; M.at<double>(35,98)=c[7]; M.at<double>(35,103)=c[20];
M.at<double>(36,5)=u[4]; M.at<double>(36,34)=a[12]; M.at<double>(36,41)=a[10]; M.at<double>(36,43)=a[14]; M.at<double>(36,49)=a[16]; M.at<double>(36,51)=a[5]; M.at<double>(36,54)=b[12]; M.at<double>(36,62)=b[10]; M.at<double>(36,65)=b[5]; M.at<double>(36,71)=b[14]; M.at<double>(36,76)=b[16]; M.at<double>(36,89)=c[5]; M.at<double>(36,99)=c[12]; M.at<double>(36,100)=c[14]; M.at<double>(36,101)=c[1]; M.at<double>(36,109)=c[11]; M.at<double>(36,111)=c[10]; M.at<double>(36,119)=c[16];
M.at<double>(37,2)=u[4]; M.at<double>(37,34)=a[14]; M.at<double>(37,35)=a[16]; M.at<double>(37,51)=a[10]; M.at<double>(37,54)=b[14]; M.at<double>(37,65)=b[10]; M.at<double>(37,83)=b[16]; M.at<double>(37,89)=c[10]; M.at<double>(37,99)=c[14]; M.at<double>(37,109)=c[1]; M.at<double>(37,118)=c[16];
M.at<double>(38,30)=a[15]; M.at<double>(38,49)=a[7]; M.at<double>(38,64)=b[1]; M.at<double>(38,69)=b[11]; M.at<double>(38,75)=b[15]; M.at<double>(38,76)=b[7]; M.at<double>(38,103)=c[1]; M.at<double>(38,107)=c[15]; M.at<double>(38,110)=c[11]; M.at<double>(38,119)=c[7];
M.at<double>(39,28)=a[14]; M.at<double>(39,33)=a[16]; M.at<double>(39,51)=a[8]; M.at<double>(39,57)=b[14]; M.at<double>(39,59)=b[16]; M.at<double>(39,65)=b[8]; M.at<double>(39,89)=c[8]; M.at<double>(39,105)=c[9]; M.at<double>(39,108)=c[10]; M.at<double>(39,109)=c[4]; M.at<double>(39,114)=c[14]; M.at<double>(39,117)=c[16];
M.at<double>(40,27)=a[14]; M.at<double>(40,28)=a[8]; M.at<double>(40,32)=a[16]; M.at<double>(40,55)=b[14]; M.at<double>(40,57)=b[8]; M.at<double>(40,61)=b[16]; M.at<double>(40,105)=c[13]; M.at<double>(40,108)=c[4]; M.at<double>(40,109)=c[19]; M.at<double>(40,113)=c[14]; M.at<double>(40,114)=c[8]; M.at<double>(40,116)=c[16];
M.at<double>(41,30)=a[7]; M.at<double>(41,64)=b[11]; M.at<double>(41,69)=b[15]; M.at<double>(41,75)=b[7]; M.at<double>(41,103)=c[11]; M.at<double>(41,107)=c[7]; M.at<double>(41,110)=c[15];
M.at<double>(42,27)=a[8]; M.at<double>(42,31)=a[16]; M.at<double>(42,55)=b[8]; M.at<double>(42,58)=b[16]; M.at<double>(42,105)=c[20]; M.at<double>(42,108)=c[19]; M.at<double>(42,113)=c[8]; M.at<double>(42,115)=c[16];
M.at<double>(43,29)=a[7]; M.at<double>(43,64)=b[3]; M.at<double>(43,69)=b[18]; M.at<double>(43,70)=b[15]; M.at<double>(43,74)=b[7]; M.at<double>(43,91)=c[15]; M.at<double>(43,94)=c[7]; M.at<double>(43,103)=c[3]; M.at<double>(43,110)=c[18];
M.at<double>(44,28)=a[16]; M.at<double>(44,57)=b[16]; M.at<double>(44,105)=c[4]; M.at<double>(44,108)=c[14]; M.at<double>(44,109)=c[8]; M.at<double>(44,114)=c[16];
M.at<double>(45,27)=a[16]; M.at<double>(45,55)=b[16]; M.at<double>(45,105)=c[19]; M.at<double>(45,108)=c[8]; M.at<double>(45,113)=c[16];
M.at<double>(46,52)=a[7]; M.at<double>(46,64)=b[2]; M.at<double>(46,70)=b[18]; M.at<double>(46,73)=b[7]; M.at<double>(46,91)=c[18]; M.at<double>(46,93)=c[7]; M.at<double>(46,103)=c[2];
M.at<double>(47,40)=a[7]; M.at<double>(47,44)=a[18]; M.at<double>(47,52)=a[6]; M.at<double>(47,64)=b[19]; M.at<double>(47,67)=b[2]; M.at<double>(47,68)=b[18]; M.at<double>(47,70)=b[17]; M.at<double>(47,72)=b[7]; M.at<double>(47,73)=b[6]; M.at<double>(47,91)=c[17]; M.at<double>(47,93)=c[6]; M.at<double>(47,95)=c[7]; M.at<double>(47,97)=c[18]; M.at<double>(47,103)=c[19]; M.at<double>(47,104)=c[2];
M.at<double>(48,30)=a[6]; M.at<double>(48,43)=a[7]; M.at<double>(48,45)=a[15]; M.at<double>(48,63)=b[15]; M.at<double>(48,64)=b[10]; M.at<double>(48,67)=b[11]; M.at<double>(48,69)=b[5]; M.at<double>(48,71)=b[7]; M.at<double>(48,75)=b[6]; M.at<double>(48,100)=c[7]; M.at<double>(48,103)=c[10]; M.at<double>(48,104)=c[11]; M.at<double>(48,107)=c[6]; M.at<double>(48,110)=c[5]; M.at<double>(48,112)=c[15];
M.at<double>(49,41)=a[12]; M.at<double>(49,45)=a[16]; M.at<double>(49,46)=a[14]; M.at<double>(49,62)=b[12]; M.at<double>(49,63)=b[16]; M.at<double>(49,66)=b[14]; M.at<double>(49,101)=c[5]; M.at<double>(49,102)=c[14]; M.at<double>(49,105)=c[15]; M.at<double>(49,109)=c[6]; M.at<double>(49,111)=c[12]; M.at<double>(49,112)=c[16];
M.at<double>(50,41)=a[16]; M.at<double>(50,62)=b[16]; M.at<double>(50,101)=c[14]; M.at<double>(50,105)=c[5]; M.at<double>(50,109)=c[12]; M.at<double>(50,111)=c[16];
M.at<double>(51,64)=b[18]; M.at<double>(51,70)=b[7]; M.at<double>(51,91)=c[7]; M.at<double>(51,103)=c[18];
M.at<double>(52,41)=a[6]; M.at<double>(52,45)=a[12]; M.at<double>(52,46)=a[5]; M.at<double>(52,62)=b[6]; M.at<double>(52,63)=b[12]; M.at<double>(52,66)=b[5]; M.at<double>(52,67)=b[14]; M.at<double>(52,69)=b[16]; M.at<double>(52,101)=c[15]; M.at<double>(52,102)=c[5]; M.at<double>(52,104)=c[14]; M.at<double>(52,109)=c[7]; M.at<double>(52,110)=c[16]; M.at<double>(52,111)=c[6]; M.at<double>(52,112)=c[12];
M.at<double>(53,64)=b[15]; M.at<double>(53,69)=b[7]; M.at<double>(53,103)=c[15]; M.at<double>(53,110)=c[7];
M.at<double>(54,105)=c[14]; M.at<double>(54,109)=c[16];
M.at<double>(55,44)=a[7]; M.at<double>(55,64)=b[17]; M.at<double>(55,67)=b[18]; M.at<double>(55,68)=b[7]; M.at<double>(55,70)=b[6]; M.at<double>(55,91)=c[6]; M.at<double>(55,97)=c[7]; M.at<double>(55,103)=c[17]; M.at<double>(55,104)=c[18];
M.at<double>(56,105)=c[8]; M.at<double>(56,108)=c[16];
M.at<double>(57,64)=b[6]; M.at<double>(57,67)=b[7]; M.at<double>(57,103)=c[6]; M.at<double>(57,104)=c[7];
M.at<double>(58,46)=a[7]; M.at<double>(58,64)=b[12]; M.at<double>(58,66)=b[7]; M.at<double>(58,67)=b[6]; M.at<double>(58,102)=c[7]; M.at<double>(58,103)=c[12]; M.at<double>(58,104)=c[6];
M.at<double>(59,8)=u[4]; M.at<double>(59,30)=a[16]; M.at<double>(59,41)=a[5]; M.at<double>(59,43)=a[12]; M.at<double>(59,45)=a[14]; M.at<double>(59,46)=a[10]; M.at<double>(59,51)=a[6]; M.at<double>(59,62)=b[5]; M.at<double>(59,63)=b[14]; M.at<double>(59,65)=b[6]; M.at<double>(59,66)=b[10]; M.at<double>(59,71)=b[12]; M.at<double>(59,75)=b[16]; M.at<double>(59,89)=c[6]; M.at<double>(59,100)=c[12]; M.at<double>(59,101)=c[11]; M.at<double>(59,102)=c[10]; M.at<double>(59,107)=c[16]; M.at<double>(59,109)=c[15]; M.at<double>(59,111)=c[5]; M.at<double>(59,112)=c[14];
M.at<double>(60,8)=u[3]; M.at<double>(60,30)=a[12]; M.at<double>(60,41)=a[15]; M.at<double>(60,43)=a[6]; M.at<double>(60,45)=a[5]; M.at<double>(60,46)=a[11]; M.at<double>(60,51)=a[7]; M.at<double>(60,62)=b[15]; M.at<double>(60,63)=b[5]; M.at<double>(60,65)=b[7]; M.at<double>(60,66)=b[11]; M.at<double>(60,67)=b[10]; M.at<double>(60,69)=b[14]; M.at<double>(60,71)=b[6]; M.at<double>(60,75)=b[12]; M.at<double>(60,89)=c[7]; M.at<double>(60,100)=c[6]; M.at<double>(60,102)=c[11]; M.at<double>(60,104)=c[10]; M.at<double>(60,107)=c[12]; M.at<double>(60,110)=c[14]; M.at<double>(60,111)=c[15]; M.at<double>(60,112)=c[5];
M.at<double>(61,42)=a[16]; M.at<double>(61,56)=b[16]; M.at<double>(61,101)=c[8]; M.at<double>(61,105)=c[17]; M.at<double>(61,106)=c[16]; M.at<double>(61,108)=c[12];
M.at<double>(62,64)=b[7]; M.at<double>(62,103)=c[7];
M.at<double>(63,105)=c[16];
M.at<double>(64,46)=a[12]; M.at<double>(64,66)=b[12]; M.at<double>(64,67)=b[16]; M.at<double>(64,101)=c[6]; M.at<double>(64,102)=c[12]; M.at<double>(64,104)=c[16]; M.at<double>(64,105)=c[7];
M.at<double>(65,46)=a[6]; M.at<double>(65,64)=b[16]; M.at<double>(65,66)=b[6]; M.at<double>(65,67)=b[12]; M.at<double>(65,101)=c[7]; M.at<double>(65,102)=c[6]; M.at<double>(65,103)=c[16]; M.at<double>(65,104)=c[12];
M.at<double>(66,46)=a[16]; M.at<double>(66,66)=b[16]; M.at<double>(66,101)=c[12]; M.at<double>(66,102)=c[16]; M.at<double>(66,105)=c[6];
M.at<double>(67,101)=c[16]; M.at<double>(67,105)=c[12];
M.at<double>(68,41)=a[14]; M.at<double>(68,43)=a[16]; M.at<double>(68,51)=a[12]; M.at<double>(68,62)=b[14]; M.at<double>(68,65)=b[12]; M.at<double>(68,71)=b[16]; M.at<double>(68,89)=c[12]; M.at<double>(68,100)=c[16]; M.at<double>(68,101)=c[10]; M.at<double>(68,105)=c[11]; M.at<double>(68,109)=c[5]; M.at<double>(68,111)=c[14];
M.at<double>(69,37)=a[2]; M.at<double>(69,48)=a[18]; M.at<double>(69,52)=a[20]; M.at<double>(69,73)=b[20]; M.at<double>(69,77)=b[2]; M.at<double>(69,81)=b[18]; M.at<double>(69,85)=c[18]; M.at<double>(69,93)=c[20]; M.at<double>(69,98)=c[2];
M.at<double>(70,20)=u[2]; M.at<double>(70,27)=a[9]; M.at<double>(70,28)=a[13]; M.at<double>(70,31)=a[10]; M.at<double>(70,32)=a[4]; M.at<double>(70,33)=a[19]; M.at<double>(70,50)=a[14]; M.at<double>(70,51)=a[20]; M.at<double>(70,53)=a[8]; M.at<double>(70,55)=b[9]; M.at<double>(70,57)=b[13]; M.at<double>(70,58)=b[10]; M.at<double>(70,59)=b[19]; M.at<double>(70,61)=b[4]; M.at<double>(70,65)=b[20]; M.at<double>(70,80)=b[14]; M.at<double>(70,82)=b[8]; M.at<double>(70,86)=c[14]; M.at<double>(70,87)=c[8]; M.at<double>(70,89)=c[20]; M.at<double>(70,113)=c[9]; M.at<double>(70,114)=c[13]; M.at<double>(70,115)=c[10]; M.at<double>(70,116)=c[4]; M.at<double>(70,117)=c[19];
M.at<double>(71,45)=a[7]; M.at<double>(71,63)=b[7]; M.at<double>(71,64)=b[5]; M.at<double>(71,67)=b[15]; M.at<double>(71,69)=b[6]; M.at<double>(71,103)=c[5]; M.at<double>(71,104)=c[15]; M.at<double>(71,110)=c[6]; M.at<double>(71,112)=c[7];
M.at<double>(72,41)=a[7]; M.at<double>(72,45)=a[6]; M.at<double>(72,46)=a[15]; M.at<double>(72,62)=b[7]; M.at<double>(72,63)=b[6]; M.at<double>(72,64)=b[14]; M.at<double>(72,66)=b[15]; M.at<double>(72,67)=b[5]; M.at<double>(72,69)=b[12]; M.at<double>(72,102)=c[15]; M.at<double>(72,103)=c[14]; M.at<double>(72,104)=c[5]; M.at<double>(72,110)=c[12]; M.at<double>(72,111)=c[7]; M.at<double>(72,112)=c[6];
M.at<double>(73,48)=a[13]; M.at<double>(73,50)=a[20]; M.at<double>(73,80)=b[20]; M.at<double>(73,81)=b[13]; M.at<double>(73,85)=c[13]; M.at<double>(73,86)=c[20];
M.at<double>(74,25)=u[3]; M.at<double>(74,29)=a[17]; M.at<double>(74,32)=a[7]; M.at<double>(74,38)=a[6]; M.at<double>(74,40)=a[15]; M.at<double>(74,44)=a[3]; M.at<double>(74,45)=a[2]; M.at<double>(74,47)=a[18]; M.at<double>(74,52)=a[5]; M.at<double>(74,60)=b[18]; M.at<double>(74,61)=b[7]; M.at<double>(74,63)=b[2]; M.at<double>(74,67)=b[13]; M.at<double>(74,68)=b[3]; M.at<double>(74,69)=b[19]; M.at<double>(74,70)=b[4]; M.at<double>(74,72)=b[15]; M.at<double>(74,73)=b[5]; M.at<double>(74,74)=b[17]; M.at<double>(74,78)=b[6]; M.at<double>(74,90)=c[18]; M.at<double>(74,91)=c[4]; M.at<double>(74,92)=c[6]; M.at<double>(74,93)=c[5]; M.at<double>(74,94)=c[17]; M.at<double>(74,95)=c[15]; M.at<double>(74,97)=c[3]; M.at<double>(74,104)=c[13]; M.at<double>(74,110)=c[19]; M.at<double>(74,112)=c[2]; M.at<double>(74,116)=c[7];
M.at<double>(75,21)=u[2]; M.at<double>(75,36)=a[2]; M.at<double>(75,37)=a[1]; M.at<double>(75,38)=a[9]; M.at<double>(75,39)=a[13]; M.at<double>(75,49)=a[20]; M.at<double>(75,50)=a[11]; M.at<double>(75,53)=a[3]; M.at<double>(75,76)=b[20]; M.at<double>(75,77)=b[1]; M.at<double>(75,78)=b[9]; M.at<double>(75,79)=b[13]; M.at<double>(75,80)=b[11]; M.at<double>(75,82)=b[3]; M.at<double>(75,84)=b[2]; M.at<double>(75,86)=c[11]; M.at<double>(75,87)=c[3]; M.at<double>(75,88)=c[2]; M.at<double>(75,92)=c[9]; M.at<double>(75,96)=c[13]; M.at<double>(75,98)=c[1]; M.at<double>(75,119)=c[20];
M.at<double>(76,48)=a[20]; M.at<double>(76,81)=b[20]; M.at<double>(76,85)=c[20];
M.at<double>(77,34)=a[16]; M.at<double>(77,51)=a[14]; M.at<double>(77,54)=b[16]; M.at<double>(77,65)=b[14]; M.at<double>(77,89)=c[14]; M.at<double>(77,99)=c[16]; M.at<double>(77,105)=c[1]; M.at<double>(77,109)=c[10];
M.at<double>(78,27)=a[17]; M.at<double>(78,31)=a[12]; M.at<double>(78,37)=a[16]; M.at<double>(78,40)=a[8]; M.at<double>(78,42)=a[19]; M.at<double>(78,55)=b[17]; M.at<double>(78,56)=b[19]; M.at<double>(78,58)=b[12]; M.at<double>(78,72)=b[8]; M.at<double>(78,77)=b[16]; M.at<double>(78,95)=c[8]; M.at<double>(78,98)=c[16]; M.at<double>(78,101)=c[20]; M.at<double>(78,106)=c[19]; M.at<double>(78,108)=c[2]; M.at<double>(78,113)=c[17]; M.at<double>(78,115)=c[12];
M.at<double>(79,42)=a[12]; M.at<double>(79,44)=a[16]; M.at<double>(79,46)=a[8]; M.at<double>(79,56)=b[12]; M.at<double>(79,66)=b[8]; M.at<double>(79,68)=b[16]; M.at<double>(79,97)=c[16]; M.at<double>(79,101)=c[17]; M.at<double>(79,102)=c[8]; M.at<double>(79,105)=c[18]; M.at<double>(79,106)=c[12]; M.at<double>(79,108)=c[6];
M.at<double>(80,14)=u[4]; M.at<double>(80,28)=a[5]; M.at<double>(80,33)=a[12]; M.at<double>(80,39)=a[16]; M.at<double>(80,41)=a[4]; M.at<double>(80,42)=a[10]; M.at<double>(80,43)=a[8]; M.at<double>(80,47)=a[14]; M.at<double>(80,51)=a[17]; M.at<double>(80,56)=b[10]; M.at<double>(80,57)=b[5]; M.at<double>(80,59)=b[12]; M.at<double>(80,60)=b[14]; M.at<double>(80,62)=b[4]; M.at<double>(80,65)=b[17]; M.at<double>(80,71)=b[8]; M.at<double>(80,79)=b[16]; M.at<double>(80,89)=c[17]; M.at<double>(80,90)=c[14]; M.at<double>(80,96)=c[16]; M.at<double>(80,100)=c[8]; M.at<double>(80,101)=c[9]; M.at<double>(80,106)=c[10]; M.at<double>(80,108)=c[11]; M.at<double>(80,109)=c[3]; M.at<double>(80,111)=c[4]; M.at<double>(80,114)=c[5]; M.at<double>(80,117)=c[12];
M.at<double>(81,31)=a[3]; M.at<double>(81,32)=a[2]; M.at<double>(81,37)=a[4]; M.at<double>(81,38)=a[19]; M.at<double>(81,40)=a[13]; M.at<double>(81,47)=a[20]; M.at<double>(81,48)=a[5]; M.at<double>(81,50)=a[17]; M.at<double>(81,58)=b[3]; M.at<double>(81,60)=b[20]; M.at<double>(81,61)=b[2]; M.at<double>(81,72)=b[13]; M.at<double>(81,77)=b[4]; M.at<double>(81,78)=b[19]; M.at<double>(81,80)=b[17]; M.at<double>(81,81)=b[5]; M.at<double>(81,85)=c[5]; M.at<double>(81,86)=c[17]; M.at<double>(81,90)=c[20]; M.at<double>(81,92)=c[19]; M.at<double>(81,95)=c[13]; M.at<double>(81,98)=c[4]; M.at<double>(81,115)=c[3]; M.at<double>(81,116)=c[2];
M.at<double>(82,29)=a[6]; M.at<double>(82,44)=a[15]; M.at<double>(82,45)=a[18]; M.at<double>(82,47)=a[7]; M.at<double>(82,60)=b[7]; M.at<double>(82,63)=b[18]; M.at<double>(82,64)=b[4]; M.at<double>(82,67)=b[3]; M.at<double>(82,68)=b[15]; M.at<double>(82,69)=b[17]; M.at<double>(82,70)=b[5]; M.at<double>(82,74)=b[6]; M.at<double>(82,90)=c[7]; M.at<double>(82,91)=c[5]; M.at<double>(82,94)=c[6]; M.at<double>(82,97)=c[15]; M.at<double>(82,103)=c[4]; M.at<double>(82,104)=c[3]; M.at<double>(82,110)=c[17]; M.at<double>(82,112)=c[18];
M.at<double>(83,26)=u[2]; M.at<double>(83,27)=a[18]; M.at<double>(83,31)=a[6]; M.at<double>(83,37)=a[12]; M.at<double>(83,40)=a[17]; M.at<double>(83,42)=a[2]; M.at<double>(83,44)=a[19]; M.at<double>(83,46)=a[20]; M.at<double>(83,52)=a[8]; M.at<double>(83,55)=b[18]; M.at<double>(83,56)=b[2]; M.at<double>(83,58)=b[6]; M.at<double>(83,66)=b[20]; M.at<double>(83,68)=b[19]; M.at<double>(83,72)=b[17]; M.at<double>(83,73)=b[8]; M.at<double>(83,77)=b[12]; M.at<double>(83,93)=c[8]; M.at<double>(83,95)=c[17]; M.at<double>(83,97)=c[19]; M.at<double>(83,98)=c[12]; M.at<double>(83,102)=c[20]; M.at<double>(83,106)=c[2]; M.at<double>(83,113)=c[18]; M.at<double>(83,115)=c[6];
M.at<double>(84,16)=u[3]; M.at<double>(84,29)=a[5]; M.at<double>(84,30)=a[17]; M.at<double>(84,33)=a[7]; M.at<double>(84,39)=a[6]; M.at<double>(84,43)=a[18]; M.at<double>(84,44)=a[11]; M.at<double>(84,45)=a[3]; M.at<double>(84,47)=a[15]; M.at<double>(84,59)=b[7]; M.at<double>(84,60)=b[15]; M.at<double>(84,63)=b[3]; M.at<double>(84,67)=b[9]; M.at<double>(84,68)=b[11]; M.at<double>(84,69)=b[4]; M.at<double>(84,70)=b[10]; M.at<double>(84,71)=b[18]; M.at<double>(84,74)=b[5]; M.at<double>(84,75)=b[17]; M.at<double>(84,79)=b[6]; M.at<double>(84,90)=c[15]; M.at<double>(84,91)=c[10]; M.at<double>(84,94)=c[5]; M.at<double>(84,96)=c[6]; M.at<double>(84,97)=c[11]; M.at<double>(84,100)=c[18]; M.at<double>(84,104)=c[9]; M.at<double>(84,107)=c[17]; M.at<double>(84,110)=c[4]; M.at<double>(84,112)=c[3]; M.at<double>(84,117)=c[7];
M.at<double>(85,25)=u[2]; M.at<double>(85,29)=a[19]; M.at<double>(85,31)=a[15]; M.at<double>(85,32)=a[18]; M.at<double>(85,37)=a[5]; M.at<double>(85,38)=a[17]; M.at<double>(85,40)=a[3]; M.at<double>(85,44)=a[13]; M.at<double>(85,45)=a[20]; M.at<double>(85,47)=a[2]; M.at<double>(85,50)=a[6]; M.at<double>(85,52)=a[4]; M.at<double>(85,58)=b[15]; M.at<double>(85,60)=b[2]; M.at<double>(85,61)=b[18]; M.at<double>(85,63)=b[20]; M.at<double>(85,68)=b[13]; M.at<double>(85,72)=b[3]; M.at<double>(85,73)=b[4]; M.at<double>(85,74)=b[19]; M.at<double>(85,77)=b[5]; M.at<double>(85,78)=b[17]; M.at<double>(85,80)=b[6]; M.at<double>(85,86)=c[6]; M.at<double>(85,90)=c[2]; M.at<double>(85,92)=c[17]; M.at<double>(85,93)=c[4]; M.at<double>(85,94)=c[19]; M.at<double>(85,95)=c[3]; M.at<double>(85,97)=c[13]; M.at<double>(85,98)=c[5]; M.at<double>(85,112)=c[20]; M.at<double>(85,115)=c[15]; M.at<double>(85,116)=c[18];
M.at<double>(86,31)=a[18]; M.at<double>(86,37)=a[17]; M.at<double>(86,40)=a[2]; M.at<double>(86,44)=a[20]; M.at<double>(86,48)=a[6]; M.at<double>(86,52)=a[19]; M.at<double>(86,58)=b[18]; M.at<double>(86,68)=b[20]; M.at<double>(86,72)=b[2]; M.at<double>(86,73)=b[19]; M.at<double>(86,77)=b[17]; M.at<double>(86,81)=b[6]; M.at<double>(86,85)=c[6]; M.at<double>(86,93)=c[19]; M.at<double>(86,95)=c[2]; M.at<double>(86,97)=c[20]; M.at<double>(86,98)=c[17]; M.at<double>(86,115)=c[18];
M.at<double>(87,22)=u[2]; M.at<double>(87,31)=a[11]; M.at<double>(87,32)=a[3]; M.at<double>(87,33)=a[2]; M.at<double>(87,37)=a[10]; M.at<double>(87,38)=a[4]; M.at<double>(87,39)=a[19]; M.at<double>(87,40)=a[9]; M.at<double>(87,43)=a[20]; M.at<double>(87,47)=a[13]; M.at<double>(87,50)=a[5]; M.at<double>(87,53)=a[17]; M.at<double>(87,58)=b[11]; M.at<double>(87,59)=b[2]; M.at<double>(87,60)=b[13]; M.at<double>(87,61)=b[3]; M.at<double>(87,71)=b[20]; M.at<double>(87,72)=b[9]; M.at<double>(87,77)=b[10]; M.at<double>(87,78)=b[4]; M.at<double>(87,79)=b[19]; M.at<double>(87,80)=b[5]; M.at<double>(87,82)=b[17]; M.at<double>(87,86)=c[5]; M.at<double>(87,87)=c[17]; M.at<double>(87,90)=c[13]; M.at<double>(87,92)=c[4]; M.at<double>(87,95)=c[9]; M.at<double>(87,96)=c[19]; M.at<double>(87,98)=c[10]; M.at<double>(87,100)=c[20]; M.at<double>(87,115)=c[11]; M.at<double>(87,116)=c[3]; M.at<double>(87,117)=c[2];
M.at<double>(88,27)=a[2]; M.at<double>(88,31)=a[17]; M.at<double>(88,37)=a[8]; M.at<double>(88,40)=a[19]; M.at<double>(88,42)=a[20]; M.at<double>(88,48)=a[12]; M.at<double>(88,55)=b[2]; M.at<double>(88,56)=b[20]; M.at<double>(88,58)=b[17]; M.at<double>(88,72)=b[19]; M.at<double>(88,77)=b[8]; M.at<double>(88,81)=b[12]; M.at<double>(88,85)=c[12]; M.at<double>(88,95)=c[19]; M.at<double>(88,98)=c[8]; M.at<double>(88,106)=c[20]; M.at<double>(88,113)=c[2]; M.at<double>(88,115)=c[17];
M.at<double>(89,31)=a[7]; M.at<double>(89,37)=a[6]; M.at<double>(89,40)=a[18]; M.at<double>(89,44)=a[2]; M.at<double>(89,52)=a[17]; M.at<double>(89,58)=b[7]; M.at<double>(89,67)=b[20]; M.at<double>(89,68)=b[2]; M.at<double>(89,70)=b[19]; M.at<double>(89,72)=b[18]; M.at<double>(89,73)=b[17]; M.at<double>(89,77)=b[6]; M.at<double>(89,91)=c[19]; M.at<double>(89,93)=c[17]; M.at<double>(89,95)=c[18]; M.at<double>(89,97)=c[2]; M.at<double>(89,98)=c[6]; M.at<double>(89,104)=c[20]; M.at<double>(89,115)=c[7];
M.at<double>(90,27)=a[12]; M.at<double>(90,40)=a[16]; M.at<double>(90,42)=a[8]; M.at<double>(90,55)=b[12]; M.at<double>(90,56)=b[8]; M.at<double>(90,72)=b[16]; M.at<double>(90,95)=c[16]; M.at<double>(90,101)=c[19]; M.at<double>(90,105)=c[2]; M.at<double>(90,106)=c[8]; M.at<double>(90,108)=c[17]; M.at<double>(90,113)=c[12];
M.at<double>(91,23)=u[2]; M.at<double>(91,27)=a[3]; M.at<double>(91,28)=a[2]; M.at<double>(91,31)=a[5]; M.at<double>(91,32)=a[17]; M.at<double>(91,37)=a[14]; M.at<double>(91,38)=a[8]; M.at<double>(91,40)=a[4]; M.at<double>(91,41)=a[20]; M.at<double>(91,42)=a[13]; M.at<double>(91,47)=a[19]; M.at<double>(91,50)=a[12]; M.at<double>(91,55)=b[3]; M.at<double>(91,56)=b[13]; M.at<double>(91,57)=b[2]; M.at<double>(91,58)=b[5]; M.at<double>(91,60)=b[19]; M.at<double>(91,61)=b[17]; M.at<double>(91,62)=b[20]; M.at<double>(91,72)=b[4]; M.at<double>(91,77)=b[14]; M.at<double>(91,78)=b[8]; M.at<double>(91,80)=b[12]; M.at<double>(91,86)=c[12]; M.at<double>(91,90)=c[19]; M.at<double>(91,92)=c[8]; M.at<double>(91,95)=c[4]; M.at<double>(91,98)=c[14]; M.at<double>(91,106)=c[13]; M.at<double>(91,111)=c[20]; M.at<double>(91,113)=c[3]; M.at<double>(91,114)=c[2]; M.at<double>(91,115)=c[5]; M.at<double>(91,116)=c[17];
M.at<double>(92,17)=u[4]; M.at<double>(92,28)=a[6]; M.at<double>(92,29)=a[16]; M.at<double>(92,41)=a[17]; M.at<double>(92,42)=a[5]; M.at<double>(92,44)=a[14]; M.at<double>(92,45)=a[8]; M.at<double>(92,46)=a[4]; M.at<double>(92,47)=a[12]; M.at<double>(92,56)=b[5]; M.at<double>(92,57)=b[6]; M.at<double>(92,60)=b[12]; M.at<double>(92,62)=b[17]; M.at<double>(92,63)=b[8]; M.at<double>(92,66)=b[4]; M.at<double>(92,68)=b[14]; M.at<double>(92,74)=b[16]; M.at<double>(92,90)=c[12]; M.at<double>(92,94)=c[16]; M.at<double>(92,97)=c[14]; M.at<double>(92,101)=c[3]; M.at<double>(92,102)=c[4]; M.at<double>(92,106)=c[5]; M.at<double>(92,108)=c[15]; M.at<double>(92,109)=c[18]; M.at<double>(92,111)=c[17]; M.at<double>(92,112)=c[8]; M.at<double>(92,114)=c[6];
M.at<double>(93,17)=u[3]; M.at<double>(93,28)=a[7]; M.at<double>(93,29)=a[12]; M.at<double>(93,41)=a[18]; M.at<double>(93,42)=a[15]; M.at<double>(93,44)=a[5]; M.at<double>(93,45)=a[17]; M.at<double>(93,46)=a[3]; M.at<double>(93,47)=a[6]; M.at<double>(93,56)=b[15]; M.at<double>(93,57)=b[7]; M.at<double>(93,60)=b[6]; M.at<double>(93,62)=b[18]; M.at<double>(93,63)=b[17]; M.at<double>(93,66)=b[3]; M.at<double>(93,67)=b[4]; M.at<double>(93,68)=b[5]; M.at<double>(93,69)=b[8]; M.at<double>(93,70)=b[14]; M.at<double>(93,74)=b[12]; M.at<double>(93,90)=c[6]; M.at<double>(93,91)=c[14]; M.at<double>(93,94)=c[12]; M.at<double>(93,97)=c[5]; M.at<double>(93,102)=c[3]; M.at<double>(93,104)=c[4]; M.at<double>(93,106)=c[15]; M.at<double>(93,110)=c[8]; M.at<double>(93,111)=c[18]; M.at<double>(93,112)=c[17]; M.at<double>(93,114)=c[7];
M.at<double>(94,31)=a[2]; M.at<double>(94,37)=a[19]; M.at<double>(94,40)=a[20]; M.at<double>(94,48)=a[17]; M.at<double>(94,58)=b[2]; M.at<double>(94,72)=b[20]; M.at<double>(94,77)=b[19]; M.at<double>(94,81)=b[17]; M.at<double>(94,85)=c[17]; M.at<double>(94,95)=c[20]; M.at<double>(94,98)=c[19]; M.at<double>(94,115)=c[2];
M.at<double>(95,26)=u[4]; M.at<double>(95,27)=a[6]; M.at<double>(95,40)=a[12]; M.at<double>(95,42)=a[17]; M.at<double>(95,44)=a[8]; M.at<double>(95,46)=a[19]; M.at<double>(95,52)=a[16]; M.at<double>(95,55)=b[6]; M.at<double>(95,56)=b[17]; M.at<double>(95,66)=b[19]; M.at<double>(95,68)=b[8]; M.at<double>(95,72)=b[12]; M.at<double>(95,73)=b[16]; M.at<double>(95,93)=c[16]; M.at<double>(95,95)=c[12]; M.at<double>(95,97)=c[8]; M.at<double>(95,101)=c[2]; M.at<double>(95,102)=c[19]; M.at<double>(95,106)=c[17]; M.at<double>(95,108)=c[18]; M.at<double>(95,113)=c[6];
M.at<double>(96,23)=u[4]; M.at<double>(96,27)=a[5]; M.at<double>(96,28)=a[17]; M.at<double>(96,32)=a[12]; M.at<double>(96,38)=a[16]; M.at<double>(96,40)=a[14]; M.at<double>(96,41)=a[19]; M.at<double>(96,42)=a[4]; M.at<double>(96,47)=a[8]; M.at<double>(96,55)=b[5]; M.at<double>(96,56)=b[4]; M.at<double>(96,57)=b[17]; M.at<double>(96,60)=b[8]; M.at<double>(96,61)=b[12]; M.at<double>(96,62)=b[19]; M.at<double>(96,72)=b[14]; M.at<double>(96,78)=b[16]; M.at<double>(96,90)=c[8]; M.at<double>(96,92)=c[16]; M.at<double>(96,95)=c[14]; M.at<double>(96,101)=c[13]; M.at<double>(96,106)=c[4]; M.at<double>(96,108)=c[3]; M.at<double>(96,109)=c[2]; M.at<double>(96,111)=c[19]; M.at<double>(96,113)=c[5]; M.at<double>(96,114)=c[17]; M.at<double>(96,116)=c[12];
M.at<double>(97,42)=a[6]; M.at<double>(97,44)=a[12]; M.at<double>(97,46)=a[17]; M.at<double>(97,56)=b[6]; M.at<double>(97,66)=b[17]; M.at<double>(97,67)=b[8]; M.at<double>(97,68)=b[12]; M.at<double>(97,70)=b[16]; M.at<double>(97,91)=c[16]; M.at<double>(97,97)=c[12]; M.at<double>(97,101)=c[18]; M.at<double>(97,102)=c[17]; M.at<double>(97,104)=c[8]; M.at<double>(97,106)=c[6]; M.at<double>(97,108)=c[7];
M.at<double>(98,28)=a[12]; M.at<double>(98,41)=a[8]; M.at<double>(98,42)=a[14]; M.at<double>(98,47)=a[16]; M.at<double>(98,56)=b[14]; M.at<double>(98,57)=b[12]; M.at<double>(98,60)=b[16]; M.at<double>(98,62)=b[8]; M.at<double>(98,90)=c[16]; M.at<double>(98,101)=c[4]; M.at<double>(98,105)=c[3]; M.at<double>(98,106)=c[14]; M.at<double>(98,108)=c[5]; M.at<double>(98,109)=c[17]; M.at<double>(98,111)=c[8]; M.at<double>(98,114)=c[12];
M.at<double>(99,42)=a[7]; M.at<double>(99,44)=a[6]; M.at<double>(99,46)=a[18]; M.at<double>(99,56)=b[7]; M.at<double>(99,64)=b[8]; M.at<double>(99,66)=b[18]; M.at<double>(99,67)=b[17]; M.at<double>(99,68)=b[6]; M.at<double>(99,70)=b[12]; M.at<double>(99,91)=c[12]; M.at<double>(99,97)=c[6]; M.at<double>(99,102)=c[18]; M.at<double>(99,103)=c[8]; M.at<double>(99,104)=c[17]; M.at<double>(99,106)=c[7];
M.at<double>(100,51)=a[16]; M.at<double>(100,65)=b[16]; M.at<double>(100,89)=c[16]; M.at<double>(100,105)=c[10]; M.at<double>(100,109)=c[14];
M.at<double>(101,37)=a[9]; M.at<double>(101,38)=a[13]; M.at<double>(101,39)=a[20]; M.at<double>(101,48)=a[11]; M.at<double>(101,50)=a[3]; M.at<double>(101,53)=a[2]; M.at<double>(101,77)=b[9]; M.at<double>(101,78)=b[13]; M.at<double>(101,79)=b[20]; M.at<double>(101,80)=b[3]; M.at<double>(101,81)=b[11]; M.at<double>(101,82)=b[2]; M.at<double>(101,85)=c[11]; M.at<double>(101,86)=c[3]; M.at<double>(101,87)=c[2]; M.at<double>(101,92)=c[13]; M.at<double>(101,96)=c[20]; M.at<double>(101,98)=c[9];
M.at<double>(102,37)=a[13]; M.at<double>(102,38)=a[20]; M.at<double>(102,48)=a[3]; M.at<double>(102,50)=a[2]; M.at<double>(102,77)=b[13]; M.at<double>(102,78)=b[20]; M.at<double>(102,80)=b[2]; M.at<double>(102,81)=b[3]; M.at<double>(102,85)=c[3]; M.at<double>(102,86)=c[2]; M.at<double>(102,92)=c[20]; M.at<double>(102,98)=c[13];
M.at<double>(103,37)=a[20]; M.at<double>(103,48)=a[2]; M.at<double>(103,77)=b[20]; M.at<double>(103,81)=b[2]; M.at<double>(103,85)=c[2]; M.at<double>(103,98)=c[20];
M.at<double>(104,11)=u[4]; M.at<double>(104,28)=a[10]; M.at<double>(104,33)=a[14]; M.at<double>(104,34)=a[8]; M.at<double>(104,36)=a[16]; M.at<double>(104,51)=a[4]; M.at<double>(104,54)=b[8]; M.at<double>(104,57)=b[10]; M.at<double>(104,59)=b[14]; M.at<double>(104,65)=b[4]; M.at<double>(104,84)=b[16]; M.at<double>(104,88)=c[16]; M.at<double>(104,89)=c[4]; M.at<double>(104,99)=c[8]; M.at<double>(104,108)=c[1]; M.at<double>(104,109)=c[9]; M.at<double>(104,114)=c[10]; M.at<double>(104,117)=c[14];
M.at<double>(105,20)=u[4]; M.at<double>(105,27)=a[10]; M.at<double>(105,28)=a[4]; M.at<double>(105,32)=a[14]; M.at<double>(105,33)=a[8]; M.at<double>(105,51)=a[19]; M.at<double>(105,53)=a[16]; M.at<double>(105,55)=b[10]; M.at<double>(105,57)=b[4]; M.at<double>(105,59)=b[8]; M.at<double>(105,61)=b[14]; M.at<double>(105,65)=b[19]; M.at<double>(105,82)=b[16]; M.at<double>(105,87)=c[16]; M.at<double>(105,89)=c[19]; M.at<double>(105,108)=c[9]; M.at<double>(105,109)=c[13]; M.at<double>(105,113)=c[10]; M.at<double>(105,114)=c[4]; M.at<double>(105,116)=c[14]; M.at<double>(105,117)=c[8];
M.at<double>(106,27)=a[4]; M.at<double>(106,28)=a[19]; M.at<double>(106,31)=a[14]; M.at<double>(106,32)=a[8]; M.at<double>(106,50)=a[16]; M.at<double>(106,55)=b[4]; M.at<double>(106,57)=b[19]; M.at<double>(106,58)=b[14]; M.at<double>(106,61)=b[8]; M.at<double>(106,80)=b[16]; M.at<double>(106,86)=c[16]; M.at<double>(106,108)=c[13]; M.at<double>(106,109)=c[20]; M.at<double>(106,113)=c[4]; M.at<double>(106,114)=c[19]; M.at<double>(106,115)=c[14]; M.at<double>(106,116)=c[8];
M.at<double>(107,27)=a[19]; M.at<double>(107,31)=a[8]; M.at<double>(107,48)=a[16]; M.at<double>(107,55)=b[19]; M.at<double>(107,58)=b[8]; M.at<double>(107,81)=b[16]; M.at<double>(107,85)=c[16]; M.at<double>(107,108)=c[20]; M.at<double>(107,113)=c[19]; M.at<double>(107,115)=c[8];
M.at<double>(108,36)=a[20]; M.at<double>(108,48)=a[1]; M.at<double>(108,50)=a[9]; M.at<double>(108,53)=a[13]; M.at<double>(108,80)=b[9]; M.at<double>(108,81)=b[1]; M.at<double>(108,82)=b[13]; M.at<double>(108,84)=b[20]; M.at<double>(108,85)=c[1]; M.at<double>(108,86)=c[9]; M.at<double>(108,87)=c[13]; M.at<double>(108,88)=c[20];
M.at<double>(109,26)=u[3]; M.at<double>(109,27)=a[7]; M.at<double>(109,40)=a[6]; M.at<double>(109,42)=a[18]; M.at<double>(109,44)=a[17]; M.at<double>(109,46)=a[2]; M.at<double>(109,52)=a[12]; M.at<double>(109,55)=b[7]; M.at<double>(109,56)=b[18]; M.at<double>(109,66)=b[2]; M.at<double>(109,67)=b[19]; M.at<double>(109,68)=b[17]; M.at<double>(109,70)=b[8]; M.at<double>(109,72)=b[6]; M.at<double>(109,73)=b[12]; M.at<double>(109,91)=c[8]; M.at<double>(109,93)=c[12]; M.at<double>(109,95)=c[6]; M.at<double>(109,97)=c[17]; M.at<double>(109,102)=c[2]; M.at<double>(109,104)=c[19]; M.at<double>(109,106)=c[18]; M.at<double>(109,113)=c[7];
M.at<double>(110,7)=u[3]; M.at<double>(110,30)=a[5]; M.at<double>(110,34)=a[7]; M.at<double>(110,43)=a[15]; M.at<double>(110,45)=a[11]; M.at<double>(110,49)=a[6]; M.at<double>(110,54)=b[7]; M.at<double>(110,63)=b[11]; M.at<double>(110,67)=b[1]; M.at<double>(110,69)=b[10]; M.at<double>(110,71)=b[15]; M.at<double>(110,75)=b[5]; M.at<double>(110,76)=b[6]; M.at<double>(110,99)=c[7]; M.at<double>(110,100)=c[15]; M.at<double>(110,104)=c[1]; M.at<double>(110,107)=c[5]; M.at<double>(110,110)=c[10]; M.at<double>(110,112)=c[11]; M.at<double>(110,119)=c[6];
M.at<double>(111,18)=u[2]; M.at<double>(111,35)=a[20]; M.at<double>(111,36)=a[13]; M.at<double>(111,50)=a[1]; M.at<double>(111,53)=a[9]; M.at<double>(111,80)=b[1]; M.at<double>(111,82)=b[9]; M.at<double>(111,83)=b[20]; M.at<double>(111,84)=b[13]; M.at<double>(111,86)=c[1]; M.at<double>(111,87)=c[9]; M.at<double>(111,88)=c[13]; M.at<double>(111,118)=c[20];
M.at<double>(112,19)=u[2]; M.at<double>(112,31)=a[1]; M.at<double>(112,32)=a[9]; M.at<double>(112,33)=a[13]; M.at<double>(112,34)=a[20]; M.at<double>(112,36)=a[19]; M.at<double>(112,50)=a[10]; M.at<double>(112,53)=a[4]; M.at<double>(112,54)=b[20]; M.at<double>(112,58)=b[1]; M.at<double>(112,59)=b[13]; M.at<double>(112,61)=b[9]; M.at<double>(112,80)=b[10]; M.at<double>(112,82)=b[4]; M.at<double>(112,84)=b[19]; M.at<double>(112,86)=c[10]; M.at<double>(112,87)=c[4]; M.at<double>(112,88)=c[19]; M.at<double>(112,99)=c[20]; M.at<double>(112,115)=c[1]; M.at<double>(112,116)=c[9]; M.at<double>(112,117)=c[13];
M.at<double>(113,31)=a[9]; M.at<double>(113,32)=a[13]; M.at<double>(113,33)=a[20]; M.at<double>(113,48)=a[10]; M.at<double>(113,50)=a[4]; M.at<double>(113,53)=a[19]; M.at<double>(113,58)=b[9]; M.at<double>(113,59)=b[20]; M.at<double>(113,61)=b[13]; M.at<double>(113,80)=b[4]; M.at<double>(113,81)=b[10]; M.at<double>(113,82)=b[19]; M.at<double>(113,85)=c[10]; M.at<double>(113,86)=c[4]; M.at<double>(113,87)=c[19]; M.at<double>(113,115)=c[9]; M.at<double>(113,116)=c[13]; M.at<double>(113,117)=c[20];
M.at<double>(114,31)=a[13]; M.at<double>(114,32)=a[20]; M.at<double>(114,48)=a[4]; M.at<double>(114,50)=a[19]; M.at<double>(114,58)=b[13]; M.at<double>(114,61)=b[20]; M.at<double>(114,80)=b[19]; M.at<double>(114,81)=b[4]; M.at<double>(114,85)=c[4]; M.at<double>(114,86)=c[19]; M.at<double>(114,115)=c[13]; M.at<double>(114,116)=c[20];
M.at<double>(115,31)=a[20]; M.at<double>(115,48)=a[19]; M.at<double>(115,58)=b[20]; M.at<double>(115,81)=b[19]; M.at<double>(115,85)=c[19]; M.at<double>(115,115)=c[20];
M.at<double>(116,24)=u[2]; M.at<double>(116,29)=a[13]; M.at<double>(116,30)=a[20]; M.at<double>(116,37)=a[11]; M.at<double>(116,38)=a[3]; M.at<double>(116,39)=a[2]; M.at<double>(116,50)=a[15]; M.at<double>(116,52)=a[9]; M.at<double>(116,53)=a[18]; M.at<double>(116,73)=b[9]; M.at<double>(116,74)=b[13]; M.at<double>(116,75)=b[20]; M.at<double>(116,77)=b[11]; M.at<double>(116,78)=b[3]; M.at<double>(116,79)=b[2]; M.at<double>(116,80)=b[15]; M.at<double>(116,82)=b[18]; M.at<double>(116,86)=c[15]; M.at<double>(116,87)=c[18]; M.at<double>(116,92)=c[3]; M.at<double>(116,93)=c[9]; M.at<double>(116,94)=c[13]; M.at<double>(116,96)=c[2]; M.at<double>(116,98)=c[11]; M.at<double>(116,107)=c[20];
M.at<double>(117,29)=a[20]; M.at<double>(117,37)=a[3]; M.at<double>(117,38)=a[2]; M.at<double>(117,48)=a[15]; M.at<double>(117,50)=a[18]; M.at<double>(117,52)=a[13]; M.at<double>(117,73)=b[13]; M.at<double>(117,74)=b[20]; M.at<double>(117,77)=b[3]; M.at<double>(117,78)=b[2]; M.at<double>(117,80)=b[18]; M.at<double>(117,81)=b[15]; M.at<double>(117,85)=c[15]; M.at<double>(117,86)=c[18]; M.at<double>(117,92)=c[2]; M.at<double>(117,93)=c[13]; M.at<double>(117,94)=c[20]; M.at<double>(117,98)=c[3];
M.at<double>(118,27)=a[13]; M.at<double>(118,28)=a[20]; M.at<double>(118,31)=a[4]; M.at<double>(118,32)=a[19]; M.at<double>(118,48)=a[14]; M.at<double>(118,50)=a[8]; M.at<double>(118,55)=b[13]; M.at<double>(118,57)=b[20]; M.at<double>(118,58)=b[4]; M.at<double>(118,61)=b[19]; M.at<double>(118,80)=b[8]; M.at<double>(118,81)=b[14]; M.at<double>(118,85)=c[14]; M.at<double>(118,86)=c[8]; M.at<double>(118,113)=c[13]; M.at<double>(118,114)=c[20]; M.at<double>(118,115)=c[4]; M.at<double>(118,116)=c[19];
M.at<double>(119,27)=a[20]; M.at<double>(119,31)=a[19]; M.at<double>(119,48)=a[8]; M.at<double>(119,55)=b[20]; M.at<double>(119,58)=b[19]; M.at<double>(119,81)=b[8]; M.at<double>(119,85)=c[8]; M.at<double>(119,113)=c[20]; M.at<double>(119,115)=c[19];
return M.t();
}
cv::Mat dls::Hessian(const double s[])
{
// the vector of monomials is
// m = [ const ; s1^2 * s2 ; s1 * s2 ; s1 * s3 ; s2 * s3 ; s2^2 * s3 ; s2^3 ; ...
// s1 * s3^2 ; s1 ; s3 ; s2 ; s2 * s3^2 ; s1^2 ; s3^2 ; s2^2 ; s3^3 ; ...
// s1 * s2 * s3 ; s1 * s2^2 ; s1^2 * s3 ; s1^3]
//
// deriv of m w.r.t. s1
//Hs3 = [0 ; 2 * s(1) * s(2) ; s(2) ; s(3) ; 0 ; 0 ; 0 ; ...
// s(3)^2 ; 1 ; 0 ; 0 ; 0 ; 2 * s(1) ; 0 ; 0 ; 0 ; ...
// s(2) * s(3) ; s(2)^2 ; 2*s(1)*s(3); 3 * s(1)^2];
double Hs1[20];
Hs1[0]=0; Hs1[1]=2*s[0]*s[1]; Hs1[2]=s[1]; Hs1[3]=s[2]; Hs1[4]=0; Hs1[5]=0; Hs1[6]=0;
Hs1[7]=s[2]*s[2]; Hs1[8]=1; Hs1[9]=0; Hs1[10]=0; Hs1[11]=0; Hs1[12]=2*s[0]; Hs1[13]=0;
Hs1[14]=0; Hs1[15]=0; Hs1[16]=s[1]*s[2]; Hs1[17]=s[1]*s[1]; Hs1[18]=2*s[0]*s[2]; Hs1[19]=3*s[0]*s[0];
// deriv of m w.r.t. s2
//Hs2 = [0 ; s(1)^2 ; s(1) ; 0 ; s(3) ; 2 * s(2) * s(3) ; 3 * s(2)^2 ; ...
// 0 ; 0 ; 0 ; 1 ; s(3)^2 ; 0 ; 0 ; 2 * s(2) ; 0 ; ...
// s(1) * s(3) ; s(1) * 2 * s(2) ; 0 ; 0];
double Hs2[20];
Hs2[0]=0; Hs2[1]=s[0]*s[0]; Hs2[2]=s[0]; Hs2[3]=0; Hs2[4]=s[2]; Hs2[5]=2*s[1]*s[2]; Hs2[6]=3*s[1]*s[1];
Hs2[7]=0; Hs2[8]=0; Hs2[9]=0; Hs2[10]=1; Hs2[11]=s[2]*s[2]; Hs2[12]=0; Hs2[13]=0;
Hs2[14]=2*s[1]; Hs2[15]=0; Hs2[16]=s[0]*s[2]; Hs2[17]=2*s[0]*s[1]; Hs2[18]=0; Hs2[19]=0;
// deriv of m w.r.t. s3
//Hs3 = [0 ; 0 ; 0 ; s(1) ; s(2) ; s(2)^2 ; 0 ; ...
// s(1) * 2 * s(3) ; 0 ; 1 ; 0 ; s(2) * 2 * s(3) ; 0 ; 2 * s(3) ; 0 ; 3 * s(3)^2 ; ...
// s(1) * s(2) ; 0 ; s(1)^2 ; 0];
double Hs3[20];
Hs3[0]=0; Hs3[1]=0; Hs3[2]=0; Hs3[3]=s[0]; Hs3[4]=s[1]; Hs3[5]=s[1]*s[1]; Hs3[6]=0;
Hs3[7]=2*s[0]*s[2]; Hs3[8]=0; Hs3[9]=1; Hs3[10]=0; Hs3[11]=2*s[1]*s[2]; Hs3[12]=0; Hs3[13]=2*s[2];
Hs3[14]=0; Hs3[15]=3*s[2]*s[2]; Hs3[16]=s[0]*s[1]; Hs3[17]=0; Hs3[18]=s[0]*s[0]; Hs3[19]=0;
// fill Hessian matrix
cv::Mat H(3, 3, CV_64F);
H.at<double>(0,0) = cv::Mat(cv::Mat(f1coeff).rowRange(1,21).t()*cv::Mat(20, 1, CV_64F, &Hs1)).at<double>(0,0);
H.at<double>(0,1) = cv::Mat(cv::Mat(f1coeff).rowRange(1,21).t()*cv::Mat(20, 1, CV_64F, &Hs2)).at<double>(0,0);
H.at<double>(0,2) = cv::Mat(cv::Mat(f1coeff).rowRange(1,21).t()*cv::Mat(20, 1, CV_64F, &Hs3)).at<double>(0,0);
H.at<double>(1,0) = cv::Mat(cv::Mat(f2coeff).rowRange(1,21).t()*cv::Mat(20, 1, CV_64F, &Hs1)).at<double>(0,0);
H.at<double>(1,1) = cv::Mat(cv::Mat(f2coeff).rowRange(1,21).t()*cv::Mat(20, 1, CV_64F, &Hs2)).at<double>(0,0);
H.at<double>(1,2) = cv::Mat(cv::Mat(f2coeff).rowRange(1,21).t()*cv::Mat(20, 1, CV_64F, &Hs3)).at<double>(0,0);
H.at<double>(2,0) = cv::Mat(cv::Mat(f3coeff).rowRange(1,21).t()*cv::Mat(20, 1, CV_64F, &Hs1)).at<double>(0,0);
H.at<double>(2,1) = cv::Mat(cv::Mat(f3coeff).rowRange(1,21).t()*cv::Mat(20, 1, CV_64F, &Hs2)).at<double>(0,0);
H.at<double>(2,2) = cv::Mat(cv::Mat(f3coeff).rowRange(1,21).t()*cv::Mat(20, 1, CV_64F, &Hs3)).at<double>(0,0);
return H;
}
cv::Mat dls::cayley2rotbar(const cv::Mat& s)
{
double s_mul1 = cv::Mat(s.t()*s).at<double>(0,0);
cv::Mat s_mul2 = s*s.t();
cv::Mat eye = cv::Mat::eye(3, 3, CV_64F);
return cv::Mat( eye.mul(1.-s_mul1) + skewsymm(&s).mul(2.) + s_mul2.mul(2.) ).t();
}
cv::Mat dls::skewsymm(const cv::Mat * X1)
{
cv::MatConstIterator_<double> it = X1->begin<double>();
return (cv::Mat_<double>(3,3) << 0, -*(it+2), *(it+1),
*(it+2), 0, -*(it+0),
-*(it+1), *(it+0), 0);
}
cv::Mat dls::rotx(const double t)
{
// rotx: rotation about y-axis
double ct = cos(t);
double st = sin(t);
return (cv::Mat_<double>(3,3) << 1, 0, 0, 0, ct, -st, 0, st, ct);
}
cv::Mat dls::roty(const double t)
{
// roty: rotation about y-axis
double ct = cos(t);
double st = sin(t);
return (cv::Mat_<double>(3,3) << ct, 0, st, 0, 1, 0, -st, 0, ct);
}
cv::Mat dls::rotz(const double t)
{
// rotz: rotation about y-axis
double ct = cos(t);
double st = sin(t);
return (cv::Mat_<double>(3,3) << ct, -st, 0, st, ct, 0, 0, 0, 1);
}
cv::Mat dls::mean(const cv::Mat& M)
{
cv::Mat m = cv::Mat::zeros(3, 1, CV_64F);
for (int i = 0; i < M.cols; ++i) m += M.col(i);
return m.mul(1./(double)M.cols);
}
bool dls::is_empty(const cv::Mat * M)
{
cv::MatConstIterator_<double> it = M->begin<double>(), it_end = M->end<double>();
for(; it != it_end; ++it)
{
if(*it < 0) return false;
}
return true;
}
bool dls::positive_eigenvalues(const cv::Mat * eigenvalues)
{
CV_Assert(eigenvalues && !eigenvalues->empty());
cv::MatConstIterator_<double> it = eigenvalues->begin<double>();
return *(it) > 0 && *(it+1) > 0 && *(it+2) > 0;
}
+773
View File
@@ -0,0 +1,773 @@
#ifndef DLS_H_
#define DLS_H_
#include "precomp.hpp"
#include <iostream>
using namespace std;
using namespace cv;
class dls
{
public:
dls(const cv::Mat& opoints, const cv::Mat& ipoints);
~dls();
bool compute_pose(cv::Mat& R, cv::Mat& t);
private:
// initialisation
template <typename OpointType, typename IpointType>
void init_points(const cv::Mat& opoints, const cv::Mat& ipoints)
{
for(int i = 0; i < N; i++)
{
p.at<double>(0,i) = opoints.at<OpointType>(i).x;
p.at<double>(1,i) = opoints.at<OpointType>(i).y;
p.at<double>(2,i) = opoints.at<OpointType>(i).z;
// compute mean of object points
mn.at<double>(0) += p.at<double>(0,i);
mn.at<double>(1) += p.at<double>(1,i);
mn.at<double>(2) += p.at<double>(2,i);
// make z into unit vectors from normalized pixel coords
double sr = std::pow(ipoints.at<IpointType>(i).x, 2) +
std::pow(ipoints.at<IpointType>(i).y, 2) + (double)1;
sr = std::sqrt(sr);
z.at<double>(0,i) = ipoints.at<IpointType>(i).x / sr;
z.at<double>(1,i) = ipoints.at<IpointType>(i).y / sr;
z.at<double>(2,i) = (double)1 / sr;
}
mn.at<double>(0) /= (double)N;
mn.at<double>(1) /= (double)N;
mn.at<double>(2) /= (double)N;
}
// main algorithm
cv::Mat LeftMultVec(const cv::Mat& v);
void run_kernel(const cv::Mat& pp);
void build_coeff_matrix(const cv::Mat& pp, cv::Mat& Mtilde, cv::Mat& D);
void compute_eigenvec(const cv::Mat& Mtilde, cv::Mat& eigenval_real, cv::Mat& eigenval_imag,
cv::Mat& eigenvec_real, cv::Mat& eigenvec_imag);
void fill_coeff(const cv::Mat * D);
// useful functions
cv::Mat cayley_LS_M(const std::vector<double>& a, const std::vector<double>& b,
const std::vector<double>& c, const std::vector<double>& u);
cv::Mat Hessian(const double s[]);
cv::Mat cayley2rotbar(const cv::Mat& s);
cv::Mat skewsymm(const cv::Mat * X1);
// extra functions
cv::Mat rotx(const double t);
cv::Mat roty(const double t);
cv::Mat rotz(const double t);
cv::Mat mean(const cv::Mat& M);
bool is_empty(const cv::Mat * v);
bool positive_eigenvalues(const cv::Mat * eigenvalues);
cv::Mat p, z, mn; // object-image points
int N; // number of input points
std::vector<double> f1coeff, f2coeff, f3coeff, cost_; // coefficient for coefficients matrix
std::vector<cv::Mat> C_est_, t_est_; // optimal candidates
cv::Mat C_est__, t_est__; // optimal found solution
double cost__; // optimal found solution
};
class EigenvalueDecomposition {
private:
// Holds the data dimension.
int n;
// Stores real/imag part of a complex division.
double cdivr, cdivi;
// Pointer to internal memory.
double *d, *e, *ort;
double **V, **H;
// Holds the computed eigenvalues.
Mat _eigenvalues;
// Holds the computed eigenvectors.
Mat _eigenvectors;
// Allocates memory.
template<typename _Tp>
_Tp *alloc_1d(int m) {
return new _Tp[m];
}
// Allocates memory.
template<typename _Tp>
_Tp *alloc_1d(int m, _Tp val) {
_Tp *arr = alloc_1d<_Tp> (m);
for (int i = 0; i < m; i++)
arr[i] = val;
return arr;
}
// Allocates memory.
template<typename _Tp>
_Tp **alloc_2d(int m, int _n) {
_Tp **arr = new _Tp*[m];
for (int i = 0; i < m; i++)
arr[i] = new _Tp[_n];
return arr;
}
// Allocates memory.
template<typename _Tp>
_Tp **alloc_2d(int m, int _n, _Tp val) {
_Tp **arr = alloc_2d<_Tp> (m, _n);
for (int i = 0; i < m; i++) {
for (int j = 0; j < _n; j++) {
arr[i][j] = val;
}
}
return arr;
}
void cdiv(double xr, double xi, double yr, double yi) {
double r, dv;
if (std::abs(yr) > std::abs(yi)) {
r = yi / yr;
dv = yr + r * yi;
cdivr = (xr + r * xi) / dv;
cdivi = (xi - r * xr) / dv;
} else {
r = yr / yi;
dv = yi + r * yr;
cdivr = (r * xr + xi) / dv;
cdivi = (r * xi - xr) / dv;
}
}
// Nonsymmetric reduction from Hessenberg to real Schur form.
void hqr2() {
// This is derived from the Algol procedure hqr2,
// by Martin and Wilkinson, Handbook for Auto. Comp.,
// Vol.ii-Linear Algebra, and the corresponding
// Fortran subroutine in EISPACK.
// Initialize
int nn = this->n;
int n1 = nn - 1;
int low = 0;
int high = nn - 1;
double eps = std::pow(2.0, -52.0);
double exshift = 0.0;
double p = 0, q = 0, r = 0, s = 0, z = 0, t, w, x, y;
// Store roots isolated by balanc and compute matrix norm
double norm = 0.0;
for (int i = 0; i < nn; i++) {
if (i < low || i > high) {
d[i] = H[i][i];
e[i] = 0.0;
}
for (int j = std::max(i - 1, 0); j < nn; j++) {
norm = norm + std::abs(H[i][j]);
}
}
// Outer loop over eigenvalue index
int iter = 0;
while (n1 >= low) {
// Look for single small sub-diagonal element
int l = n1;
while (l > low) {
s = std::abs(H[l - 1][l - 1]) + std::abs(H[l][l]);
if (s == 0.0) {
s = norm;
}
if (std::abs(H[l][l - 1]) < eps * s) {
break;
}
l--;
}
// Check for convergence
// One root found
if (l == n1) {
H[n1][n1] = H[n1][n1] + exshift;
d[n1] = H[n1][n1];
e[n1] = 0.0;
n1--;
iter = 0;
// Two roots found
} else if (l == n1 - 1) {
w = H[n1][n1 - 1] * H[n1 - 1][n1];
p = (H[n1 - 1][n1 - 1] - H[n1][n1]) / 2.0;
q = p * p + w;
z = std::sqrt(std::abs(q));
H[n1][n1] = H[n1][n1] + exshift;
H[n1 - 1][n1 - 1] = H[n1 - 1][n1 - 1] + exshift;
x = H[n1][n1];
// Real pair
if (q >= 0) {
if (p >= 0) {
z = p + z;
} else {
z = p - z;
}
d[n1 - 1] = x + z;
d[n1] = d[n1 - 1];
if (z != 0.0) {
d[n1] = x - w / z;
}
e[n1 - 1] = 0.0;
e[n1] = 0.0;
x = H[n1][n1 - 1];
s = std::abs(x) + std::abs(z);
p = x / s;
q = z / s;
r = std::sqrt(p * p + q * q);
p = p / r;
q = q / r;
// Row modification
for (int j = n1 - 1; j < nn; j++) {
z = H[n1 - 1][j];
H[n1 - 1][j] = q * z + p * H[n1][j];
H[n1][j] = q * H[n1][j] - p * z;
}
// Column modification
for (int i = 0; i <= n1; i++) {
z = H[i][n1 - 1];
H[i][n1 - 1] = q * z + p * H[i][n1];
H[i][n1] = q * H[i][n1] - p * z;
}
// Accumulate transformations
for (int i = low; i <= high; i++) {
z = V[i][n1 - 1];
V[i][n1 - 1] = q * z + p * V[i][n1];
V[i][n1] = q * V[i][n1] - p * z;
}
// Complex pair
} else {
d[n1 - 1] = x + p;
d[n1] = x + p;
e[n1 - 1] = z;
e[n1] = -z;
}
n1 = n1 - 2;
iter = 0;
// No convergence yet
} else {
// Form shift
x = H[n1][n1];
y = 0.0;
w = 0.0;
if (l < n1) {
y = H[n1 - 1][n1 - 1];
w = H[n1][n1 - 1] * H[n1 - 1][n1];
}
// Wilkinson's original ad hoc shift
if (iter == 10) {
exshift += x;
for (int i = low; i <= n1; i++) {
H[i][i] -= x;
}
s = std::abs(H[n1][n1 - 1]) + std::abs(H[n1 - 1][n1 - 2]);
x = y = 0.75 * s;
w = -0.4375 * s * s;
}
// MATLAB's new ad hoc shift
if (iter == 30) {
s = (y - x) / 2.0;
s = s * s + w;
if (s > 0) {
s = std::sqrt(s);
if (y < x) {
s = -s;
}
s = x - w / ((y - x) / 2.0 + s);
for (int i = low; i <= n1; i++) {
H[i][i] -= s;
}
exshift += s;
x = y = w = 0.964;
}
}
iter = iter + 1; // (Could check iteration count here.)
// Look for two consecutive small sub-diagonal elements
int m = n1 - 2;
while (m >= l) {
z = H[m][m];
r = x - z;
s = y - z;
p = (r * s - w) / H[m + 1][m] + H[m][m + 1];
q = H[m + 1][m + 1] - z - r - s;
r = H[m + 2][m + 1];
s = std::abs(p) + std::abs(q) + std::abs(r);
p = p / s;
q = q / s;
r = r / s;
if (m == l) {
break;
}
if (std::abs(H[m][m - 1]) * (std::abs(q) + std::abs(r)) < eps * (std::abs(p)
* (std::abs(H[m - 1][m - 1]) + std::abs(z) + std::abs(
H[m + 1][m + 1])))) {
break;
}
m--;
}
for (int i = m + 2; i <= n1; i++) {
H[i][i - 2] = 0.0;
if (i > m + 2) {
H[i][i - 3] = 0.0;
}
}
// Double QR step involving rows l:n and columns m:n
for (int k = m; k <= n1 - 1; k++) {
bool notlast = (k != n1 - 1);
if (k != m) {
p = H[k][k - 1];
q = H[k + 1][k - 1];
r = (notlast ? H[k + 2][k - 1] : 0.0);
x = std::abs(p) + std::abs(q) + std::abs(r);
if (x != 0.0) {
p = p / x;
q = q / x;
r = r / x;
}
}
if (x == 0.0) {
break;
}
s = std::sqrt(p * p + q * q + r * r);
if (p < 0) {
s = -s;
}
if (s != 0) {
if (k != m) {
H[k][k - 1] = -s * x;
} else if (l != m) {
H[k][k - 1] = -H[k][k - 1];
}
p = p + s;
x = p / s;
y = q / s;
z = r / s;
q = q / p;
r = r / p;
// Row modification
for (int j = k; j < nn; j++) {
p = H[k][j] + q * H[k + 1][j];
if (notlast) {
p = p + r * H[k + 2][j];
H[k + 2][j] = H[k + 2][j] - p * z;
}
H[k][j] = H[k][j] - p * x;
H[k + 1][j] = H[k + 1][j] - p * y;
}
// Column modification
for (int i = 0; i <= std::min(n1, k + 3); i++) {
p = x * H[i][k] + y * H[i][k + 1];
if (notlast) {
p = p + z * H[i][k + 2];
H[i][k + 2] = H[i][k + 2] - p * r;
}
H[i][k] = H[i][k] - p;
H[i][k + 1] = H[i][k + 1] - p * q;
}
// Accumulate transformations
for (int i = low; i <= high; i++) {
p = x * V[i][k] + y * V[i][k + 1];
if (notlast) {
p = p + z * V[i][k + 2];
V[i][k + 2] = V[i][k + 2] - p * r;
}
V[i][k] = V[i][k] - p;
V[i][k + 1] = V[i][k + 1] - p * q;
}
} // (s != 0)
} // k loop
} // check convergence
} // while (n1 >= low)
// Backsubstitute to find vectors of upper triangular form
if (norm == 0.0) {
return;
}
for (n1 = nn - 1; n1 >= 0; n1--) {
p = d[n1];
q = e[n1];
// Real vector
if (q == 0) {
int l = n1;
H[n1][n1] = 1.0;
for (int i = n1 - 1; i >= 0; i--) {
w = H[i][i] - p;
r = 0.0;
for (int j = l; j <= n1; j++) {
r = r + H[i][j] * H[j][n1];
}
if (e[i] < 0.0) {
z = w;
s = r;
} else {
l = i;
if (e[i] == 0.0) {
if (w != 0.0) {
H[i][n1] = -r / w;
} else {
H[i][n1] = -r / (eps * norm);
}
// Solve real equations
} else {
x = H[i][i + 1];
y = H[i + 1][i];
q = (d[i] - p) * (d[i] - p) + e[i] * e[i];
t = (x * s - z * r) / q;
H[i][n1] = t;
if (std::abs(x) > std::abs(z)) {
H[i + 1][n1] = (-r - w * t) / x;
} else {
H[i + 1][n1] = (-s - y * t) / z;
}
}
// Overflow control
t = std::abs(H[i][n1]);
if ((eps * t) * t > 1) {
for (int j = i; j <= n1; j++) {
H[j][n1] = H[j][n1] / t;
}
}
}
}
// Complex vector
} else if (q < 0) {
int l = n1 - 1;
// Last vector component imaginary so matrix is triangular
if (std::abs(H[n1][n1 - 1]) > std::abs(H[n1 - 1][n1])) {
H[n1 - 1][n1 - 1] = q / H[n1][n1 - 1];
H[n1 - 1][n1] = -(H[n1][n1] - p) / H[n1][n1 - 1];
} else {
cdiv(0.0, -H[n1 - 1][n1], H[n1 - 1][n1 - 1] - p, q);
H[n1 - 1][n1 - 1] = cdivr;
H[n1 - 1][n1] = cdivi;
}
H[n1][n1 - 1] = 0.0;
H[n1][n1] = 1.0;
for (int i = n1 - 2; i >= 0; i--) {
double ra, sa;
ra = 0.0;
sa = 0.0;
for (int j = l; j <= n1; j++) {
ra = ra + H[i][j] * H[j][n1 - 1];
sa = sa + H[i][j] * H[j][n1];
}
w = H[i][i] - p;
if (e[i] < 0.0) {
z = w;
r = ra;
s = sa;
} else {
l = i;
if (e[i] == 0) {
cdiv(-ra, -sa, w, q);
H[i][n1 - 1] = cdivr;
H[i][n1] = cdivi;
} else {
// Solve complex equations
x = H[i][i + 1];
y = H[i + 1][i];
double vr = (d[i] - p) * (d[i] - p) + e[i] * e[i] - q * q;
double vi = (d[i] - p) * 2.0 * q;
if (vr == 0.0 && vi == 0.0) {
vr = eps * norm * (std::abs(w) + std::abs(q) + std::abs(x)
+ std::abs(y) + std::abs(z));
}
cdiv(x * r - z * ra + q * sa,
x * s - z * sa - q * ra, vr, vi);
H[i][n1 - 1] = cdivr;
H[i][n1] = cdivi;
if (std::abs(x) > (std::abs(z) + std::abs(q))) {
H[i + 1][n1 - 1] = (-ra - w * H[i][n1 - 1] + q
* H[i][n1]) / x;
H[i + 1][n1] = (-sa - w * H[i][n1] - q * H[i][n1
- 1]) / x;
} else {
cdiv(-r - y * H[i][n1 - 1], -s - y * H[i][n1], z,
q);
H[i + 1][n1 - 1] = cdivr;
H[i + 1][n1] = cdivi;
}
}
// Overflow control
t = std::max(std::abs(H[i][n1 - 1]), std::abs(H[i][n1]));
if ((eps * t) * t > 1) {
for (int j = i; j <= n1; j++) {
H[j][n1 - 1] = H[j][n1 - 1] / t;
H[j][n1] = H[j][n1] / t;
}
}
}
}
}
}
// Vectors of isolated roots
for (int i = 0; i < nn; i++) {
if (i < low || i > high) {
for (int j = i; j < nn; j++) {
V[i][j] = H[i][j];
}
}
}
// Back transformation to get eigenvectors of original matrix
for (int j = nn - 1; j >= low; j--) {
for (int i = low; i <= high; i++) {
z = 0.0;
for (int k = low; k <= std::min(j, high); k++) {
z = z + V[i][k] * H[k][j];
}
V[i][j] = z;
}
}
}
// Nonsymmetric reduction to Hessenberg form.
void orthes() {
// This is derived from the Algol procedures orthes and ortran,
// by Martin and Wilkinson, Handbook for Auto. Comp.,
// Vol.ii-Linear Algebra, and the corresponding
// Fortran subroutines in EISPACK.
int low = 0;
int high = n - 1;
for (int m = low + 1; m <= high - 1; m++) {
// Scale column.
double scale = 0.0;
for (int i = m; i <= high; i++) {
scale = scale + std::abs(H[i][m - 1]);
}
if (scale != 0.0) {
// Compute Householder transformation.
double h = 0.0;
for (int i = high; i >= m; i--) {
ort[i] = H[i][m - 1] / scale;
h += ort[i] * ort[i];
}
double g = std::sqrt(h);
if (ort[m] > 0) {
g = -g;
}
h = h - ort[m] * g;
ort[m] = ort[m] - g;
// Apply Householder similarity transformation
// H = (I-u*u'/h)*H*(I-u*u')/h)
for (int j = m; j < n; j++) {
double f = 0.0;
for (int i = high; i >= m; i--) {
f += ort[i] * H[i][j];
}
f = f / h;
for (int i = m; i <= high; i++) {
H[i][j] -= f * ort[i];
}
}
for (int i = 0; i <= high; i++) {
double f = 0.0;
for (int j = high; j >= m; j--) {
f += ort[j] * H[i][j];
}
f = f / h;
for (int j = m; j <= high; j++) {
H[i][j] -= f * ort[j];
}
}
ort[m] = scale * ort[m];
H[m][m - 1] = scale * g;
}
}
// Accumulate transformations (Algol's ortran).
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
V[i][j] = (i == j ? 1.0 : 0.0);
}
}
for (int m = high - 1; m >= low + 1; m--) {
if (H[m][m - 1] != 0.0) {
for (int i = m + 1; i <= high; i++) {
ort[i] = H[i][m - 1];
}
for (int j = m; j <= high; j++) {
double g = 0.0;
for (int i = m; i <= high; i++) {
g += ort[i] * V[i][j];
}
// Double division avoids possible underflow
g = (g / ort[m]) / H[m][m - 1];
for (int i = m; i <= high; i++) {
V[i][j] += g * ort[i];
}
}
}
}
}
// Releases all internal working memory.
void release() {
// releases the working data
delete[] d;
delete[] e;
delete[] ort;
for (int i = 0; i < n; i++) {
delete[] H[i];
delete[] V[i];
}
delete[] H;
delete[] V;
}
// Computes the Eigenvalue Decomposition for a matrix given in H.
void compute() {
// Allocate memory for the working data.
V = alloc_2d<double> (n, n, 0.0);
d = alloc_1d<double> (n);
e = alloc_1d<double> (n);
ort = alloc_1d<double> (n);
// Reduce to Hessenberg form.
orthes();
// Reduce Hessenberg to real Schur form.
hqr2();
// Copy eigenvalues to OpenCV Matrix.
_eigenvalues.create(1, n, CV_64FC1);
for (int i = 0; i < n; i++) {
_eigenvalues.at<double> (0, i) = d[i];
}
// Copy eigenvectors to OpenCV Matrix.
_eigenvectors.create(n, n, CV_64FC1);
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
_eigenvectors.at<double> (i, j) = V[i][j];
// Deallocate the memory by releasing all internal working data.
release();
}
public:
EigenvalueDecomposition()
: n(0), cdivr(0), cdivi(0), d(0), e(0), ort(0), V(0), H(0) {}
// Initializes & computes the Eigenvalue Decomposition for a general matrix
// given in src. This function is a port of the EigenvalueSolver in JAMA,
// which has been released to public domain by The MathWorks and the
// National Institute of Standards and Technology (NIST).
EigenvalueDecomposition(InputArray src) {
compute(src);
}
// This function computes the Eigenvalue Decomposition for a general matrix
// given in src. This function is a port of the EigenvalueSolver in JAMA,
// which has been released to public domain by The MathWorks and the
// National Institute of Standards and Technology (NIST).
void compute(InputArray src)
{
/*if(isSymmetric(src)) {
// Fall back to OpenCV for a symmetric matrix!
cv::eigen(src, _eigenvalues, _eigenvectors);
} else {*/
Mat tmp;
// Convert the given input matrix to double. Is there any way to
// prevent allocating the temporary memory? Only used for copying
// into working memory and deallocated after.
src.getMat().convertTo(tmp, CV_64FC1);
// Get dimension of the matrix.
this->n = tmp.cols;
// Allocate the matrix data to work on.
this->H = alloc_2d<double> (n, n);
// Now safely copy the data.
for (int i = 0; i < tmp.rows; i++) {
for (int j = 0; j < tmp.cols; j++) {
this->H[i][j] = tmp.at<double>(i, j);
}
}
// Deallocates the temporary matrix before computing.
tmp.release();
// Performs the eigenvalue decomposition of H.
compute();
// }
}
~EigenvalueDecomposition() {}
// Returns the eigenvalues of the Eigenvalue Decomposition.
Mat eigenvalues() { return _eigenvalues; }
// Returns the eigenvectors of the Eigenvalue Decomposition.
Mat eigenvectors() { return _eigenvectors; }
};
#endif // DLS_H
+37 -28
View File
@@ -2,7 +2,10 @@
#include "precomp.hpp"
#include "epnp.h"
epnp::epnp(const cv::Mat& cameraMatrix, const cv::Mat& opoints, const cv::Mat& ipoints)
namespace cv
{
epnp::epnp(const Mat& cameraMatrix, const Mat& opoints, const Mat& ipoints)
{
if (cameraMatrix.depth() == CV_32F)
init_camera_parameters<float>(cameraMatrix);
@@ -17,14 +20,14 @@ epnp::epnp(const cv::Mat& cameraMatrix, const cv::Mat& opoints, const cv::Mat& i
if (opoints.depth() == ipoints.depth())
{
if (opoints.depth() == CV_32F)
init_points<cv::Point3f,cv::Point2f>(opoints, ipoints);
init_points<Point3f,Point2f>(opoints, ipoints);
else
init_points<cv::Point3d,cv::Point2d>(opoints, ipoints);
init_points<Point3d,Point2d>(opoints, ipoints);
}
else if (opoints.depth() == CV_32F)
init_points<cv::Point3f,cv::Point2d>(opoints, ipoints);
init_points<Point3f,Point2d>(opoints, ipoints);
else
init_points<cv::Point3d,cv::Point2f>(opoints, ipoints);
init_points<Point3d,Point2f>(opoints, ipoints);
alphas.resize(4 * number_of_correspondences);
pcs.resize(3 * number_of_correspondences);
@@ -57,7 +60,7 @@ void epnp::choose_control_points(void)
// Take C1, C2, and C3 from PCA on the reference points:
CvMat * PW0 = cvCreateMat(number_of_correspondences, 3, CV_64F);
double pw0tpw0[3 * 3], dc[3], uct[3 * 3];
double pw0tpw0[3 * 3] = {}, dc[3] = {}, uct[3 * 3] = {};
CvMat PW0tPW0 = cvMat(3, 3, CV_64F, pw0tpw0);
CvMat DC = cvMat(3, 1, CV_64F, dc);
CvMat UCt = cvMat(3, 3, CV_64F, uct);
@@ -80,7 +83,7 @@ void epnp::choose_control_points(void)
void epnp::compute_barycentric_coordinates(void)
{
double cc[3 * 3], cc_inv[3 * 3];
double cc[3 * 3] = {}, cc_inv[3 * 3] = {};
CvMat CC = cvMat(3, 3, CV_64F, cc);
CvMat CC_inv = cvMat(3, 3, CV_64F, cc_inv);
@@ -95,10 +98,12 @@ void epnp::compute_barycentric_coordinates(void)
double * a = &alphas[0] + 4 * i;
for(int j = 0; j < 3; j++)
{
a[1 + j] =
ci[3 * j ] * (pi[0] - cws[0][0]) +
ci[3 * j + 1] * (pi[1] - cws[0][1]) +
ci[3 * j + 2] * (pi[2] - cws[0][2]);
ci[3 * j ] * (pi[0] - cws[0][0]) +
ci[3 * j + 1] * (pi[1] - cws[0][1]) +
ci[3 * j + 2] * (pi[2] - cws[0][2]);
}
a[0] = 1.0f - a[1] - a[2] - a[3];
}
}
@@ -129,7 +134,7 @@ void epnp::compute_ccs(const double * betas, const double * ut)
const double * v = ut + 12 * (11 - i);
for(int j = 0; j < 4; j++)
for(int k = 0; k < 3; k++)
ccs[j][k] += betas[i] * v[3 * j + k];
ccs[j][k] += betas[i] * v[3 * j + k];
}
}
@@ -144,7 +149,7 @@ void epnp::compute_pcs(void)
}
}
void epnp::compute_pose(cv::Mat& R, cv::Mat& t)
void epnp::compute_pose(Mat& R, Mat& t)
{
choose_control_points();
compute_barycentric_coordinates();
@@ -154,7 +159,7 @@ void epnp::compute_pose(cv::Mat& R, cv::Mat& t)
for(int i = 0; i < number_of_correspondences; i++)
fill_M(M, 2 * i, &alphas[0] + 4 * i, us[2 * i], us[2 * i + 1]);
double mtm[12 * 12], d[12], ut[12 * 12];
double mtm[12 * 12] = {}, d[12] = {}, ut[12 * 12] = {};
CvMat MtM = cvMat(12, 12, CV_64F, mtm);
CvMat D = cvMat(12, 1, CV_64F, d);
CvMat Ut = cvMat(12, 12, CV_64F, ut);
@@ -163,15 +168,15 @@ void epnp::compute_pose(cv::Mat& R, cv::Mat& t)
cvSVD(&MtM, &D, &Ut, 0, CV_SVD_MODIFY_A | CV_SVD_U_T);
cvReleaseMat(&M);
double l_6x10[6 * 10], rho[6];
double l_6x10[6 * 10] = {}, rho[6] = {};
CvMat L_6x10 = cvMat(6, 10, CV_64F, l_6x10);
CvMat Rho = cvMat(6, 1, CV_64F, rho);
compute_L_6x10(ut, l_6x10);
compute_rho(rho);
double Betas[4][4], rep_errors[4];
double Rs[4][3][3], ts[4][3];
double Betas[4][4] = {}, rep_errors[4] = {};
double Rs[4][3][3] = {}, ts[4][3] = {};
find_betas_approx_1(&L_6x10, &Rho, Betas[1]);
gauss_newton(&L_6x10, &Rho, Betas[1]);
@@ -189,8 +194,8 @@ void epnp::compute_pose(cv::Mat& R, cv::Mat& t)
if (rep_errors[2] < rep_errors[1]) N = 2;
if (rep_errors[3] < rep_errors[N]) N = 3;
cv::Mat(3, 1, CV_64F, ts[N]).copyTo(t);
cv::Mat(3, 3, CV_64F, Rs[N]).copyTo(R);
Mat(3, 1, CV_64F, ts[N]).copyTo(t);
Mat(3, 3, CV_64F, Rs[N]).copyTo(R);
}
void epnp::copy_R_and_t(const double R_src[3][3], const double t_src[3],
@@ -218,7 +223,7 @@ double epnp::dot(const double * v1, const double * v2)
void epnp::estimate_R_and_t(double R[3][3], double t[3])
{
double pc0[3], pw0[3];
double pc0[3] = {}, pw0[3] = {};
pc0[0] = pc0[1] = pc0[2] = 0.0;
pw0[0] = pw0[1] = pw0[2] = 0.0;
@@ -237,7 +242,7 @@ void epnp::estimate_R_and_t(double R[3][3], double t[3])
pw0[j] /= number_of_correspondences;
}
double abt[3 * 3], abt_d[3], abt_u[3 * 3], abt_v[3 * 3];
double abt[3 * 3] = {}, abt_d[3] = {}, abt_u[3 * 3] = {}, abt_v[3 * 3] = {};
CvMat ABt = cvMat(3, 3, CV_64F, abt);
CvMat ABt_D = cvMat(3, 1, CV_64F, abt_d);
CvMat ABt_U = cvMat(3, 3, CV_64F, abt_u);
@@ -281,7 +286,7 @@ void epnp::solve_for_sign(void)
if (pcs[2] < 0.0) {
for(int i = 0; i < 4; i++)
for(int j = 0; j < 3; j++)
ccs[i][j] = -ccs[i][j];
ccs[i][j] = -ccs[i][j];
for(int i = 0; i < number_of_correspondences; i++) {
pcs[3 * i ] = -pcs[3 * i];
@@ -329,7 +334,7 @@ double epnp::reprojection_error(const double R[3][3], const double t[3])
void epnp::find_betas_approx_1(const CvMat * L_6x10, const CvMat * Rho,
double * betas)
{
double l_6x4[6 * 4], b4[4];
double l_6x4[6 * 4] = {}, b4[4] = {};
CvMat L_6x4 = cvMat(6, 4, CV_64F, l_6x4);
CvMat B4 = cvMat(4, 1, CV_64F, b4);
@@ -361,7 +366,7 @@ void epnp::find_betas_approx_1(const CvMat * L_6x10, const CvMat * Rho,
void epnp::find_betas_approx_2(const CvMat * L_6x10, const CvMat * Rho,
double * betas)
{
double l_6x3[6 * 3], b3[3];
double l_6x3[6 * 3] = {}, b3[3] = {};
CvMat L_6x3 = cvMat(6, 3, CV_64F, l_6x3);
CvMat B3 = cvMat(3, 1, CV_64F, b3);
@@ -393,7 +398,7 @@ void epnp::find_betas_approx_2(const CvMat * L_6x10, const CvMat * Rho,
void epnp::find_betas_approx_3(const CvMat * L_6x10, const CvMat * Rho,
double * betas)
{
double l_6x5[6 * 5], b5[5];
double l_6x5[6 * 5] = {}, b5[5] = {};
CvMat L_6x5 = cvMat(6, 5, CV_64F, l_6x5);
CvMat B5 = cvMat(5, 1, CV_64F, b5);
@@ -428,7 +433,7 @@ void epnp::compute_L_6x10(const double * ut, double * l_6x10)
v[2] = ut + 12 * 9;
v[3] = ut + 12 * 8;
double dv[4][6][3];
double dv[4][6][3] = {};
for(int i = 0; i < 4; i++) {
int a = 0, b = 1;
@@ -439,8 +444,8 @@ void epnp::compute_L_6x10(const double * ut, double * l_6x10)
b++;
if (b > 3) {
a++;
b = a + 1;
a++;
b = a + 1;
}
}
}
@@ -503,7 +508,7 @@ void epnp::gauss_newton(const CvMat * L_6x10, const CvMat * Rho, double betas[4]
{
const int iterations_number = 5;
double a[6*4], b[6], x[4];
double a[6*4] = {}, b[6] = {}, x[4] = {};
CvMat A = cvMat(6, 4, CV_64F, a);
CvMat B = cvMat(6, 1, CV_64F, b);
CvMat X = cvMat(4, 1, CV_64F, x);
@@ -522,6 +527,8 @@ void epnp::qr_solve(CvMat * A, CvMat * b, CvMat * X)
{
const int nr = A->rows;
const int nc = A->cols;
if (nc <= 0 || nr <= 0)
return;
if (max_nr != 0 && max_nr < nr)
{
@@ -621,3 +628,5 @@ void epnp::qr_solve(CvMat * A, CvMat * b, CvMat * X)
pX[i] = (pb[i] - sum) / A2[i];
}
}
}
+12 -5
View File
@@ -4,6 +4,9 @@
#include "precomp.hpp"
#include "opencv2/core/core_c.h"
namespace cv
{
class epnp {
public:
epnp(const cv::Mat& cameraMatrix, const cv::Mat& opoints, const cv::Mat& ipoints);
@@ -14,6 +17,8 @@ class epnp {
void compute_pose(cv::Mat& R, cv::Mat& t);
private:
epnp(const epnp &); // copy disabled
epnp& operator=(const epnp &); // assign disabled
template <typename T>
void init_camera_parameters(const cv::Mat& cameraMatrix)
{
@@ -27,12 +32,12 @@ class epnp {
{
for(int i = 0; i < number_of_correspondences; i++)
{
pws[3 * i ] = opoints.at<OpointType>(0,i).x;
pws[3 * i + 1] = opoints.at<OpointType>(0,i).y;
pws[3 * i + 2] = opoints.at<OpointType>(0,i).z;
pws[3 * i ] = opoints.at<OpointType>(i).x;
pws[3 * i + 1] = opoints.at<OpointType>(i).y;
pws[3 * i + 2] = opoints.at<OpointType>(i).z;
us[2 * i ] = ipoints.at<IpointType>(0,i).x*fu + uc;
us[2 * i + 1] = ipoints.at<IpointType>(0,i).y*fv + vc;
us[2 * i ] = ipoints.at<IpointType>(i).x*fu + uc;
us[2 * i + 1] = ipoints.at<IpointType>(i).y*fv + vc;
}
}
double reprojection_error(const double R[3][3], const double t[3]);
@@ -78,4 +83,6 @@ class epnp {
double * A1, * A2;
};
}
#endif
+182 -136
View File
@@ -42,6 +42,7 @@
#include "precomp.hpp"
#include "fisheye.hpp"
#include <limits>
namespace cv { namespace
{
@@ -53,7 +54,7 @@ namespace cv { namespace
double dalpha;
};
void subMatrix(const Mat& src, Mat& dst, const std::vector<int>& cols, const std::vector<int>& rows);
void subMatrix(const Mat& src, Mat& dst, const std::vector<uchar>& cols, const std::vector<uchar>& rows);
}}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -62,12 +63,16 @@ namespace cv { namespace
void cv::fisheye::projectPoints(InputArray objectPoints, OutputArray imagePoints, const Affine3d& affine,
InputArray K, InputArray D, double alpha, OutputArray jacobian)
{
CV_INSTRUMENT_REGION();
projectPoints(objectPoints, imagePoints, affine.rvec(), affine.translation(), K, D, alpha, jacobian);
}
void cv::fisheye::projectPoints(InputArray objectPoints, OutputArray imagePoints, InputArray _rvec,
InputArray _tvec, InputArray _K, InputArray _D, double alpha, OutputArray jacobian)
{
CV_INSTRUMENT_REGION();
// will support only 3-channel data now for points
CV_Assert(objectPoints.type() == CV_32FC3 || objectPoints.type() == CV_64FC3);
imagePoints.create(objectPoints.size(), CV_MAKETYPE(objectPoints.depth(), 2));
@@ -99,8 +104,9 @@ void cv::fisheye::projectPoints(InputArray objectPoints, OutputArray imagePoints
Vec4d k = _D.depth() == CV_32F ? (Vec4d)*_D.getMat().ptr<Vec4f>(): *_D.getMat().ptr<Vec4d>();
const bool isJacobianNeeded = jacobian.needed();
JacobianRow *Jn = 0;
if (jacobian.needed())
if (isJacobianNeeded)
{
int nvars = 2 + 2 + 1 + 4 + 3 + 3; // f, c, alpha, k, om, T,
jacobian.create(2*(int)n, nvars, CV_64F);
@@ -121,7 +127,8 @@ void cv::fisheye::projectPoints(InputArray objectPoints, OutputArray imagePoints
{
Vec3d Xi = objectPoints.depth() == CV_32F ? (Vec3d)Xf[i] : Xd[i];
Vec3d Y = aff*Xi;
if (fabs(Y[2]) < DBL_MIN)
Y[2] = 1;
Vec2d x(Y[0]/Y[2], Y[1]/Y[2]);
double r2 = x.dot(x);
@@ -147,7 +154,7 @@ void cv::fisheye::projectPoints(InputArray objectPoints, OutputArray imagePoints
else
xpd[i] = final_point;
if (jacobian.needed())
if (isJacobianNeeded)
{
//Vec3d Xi = pdepth == CV_32F ? (Vec3d)Xf[i] : Xd[i];
//Vec3d Y = aff*Xi;
@@ -249,6 +256,8 @@ void cv::fisheye::projectPoints(InputArray objectPoints, OutputArray imagePoints
void cv::fisheye::distortPoints(InputArray undistorted, OutputArray distorted, InputArray K, InputArray D, double alpha)
{
CV_INSTRUMENT_REGION();
// will support only 2-channel data now for points
CV_Assert(undistorted.type() == CV_32FC2 || undistorted.type() == CV_64FC2);
distorted.create(undistorted.size(), undistorted.type());
@@ -311,6 +320,8 @@ void cv::fisheye::distortPoints(InputArray undistorted, OutputArray distorted, I
void cv::fisheye::undistortPoints( InputArray distorted, OutputArray undistorted, InputArray K, InputArray D, InputArray R, InputArray P)
{
CV_INSTRUMENT_REGION();
// will support only 2-channel data now for points
CV_Assert(distorted.type() == CV_32FC2 || distorted.type() == CV_64FC2);
undistorted.create(distorted.size(), distorted.type());
@@ -369,14 +380,28 @@ void cv::fisheye::undistortPoints( InputArray distorted, OutputArray undistorted
double scale = 1.0;
double theta_d = sqrt(pw[0]*pw[0] + pw[1]*pw[1]);
// the current camera model is only valid up to 180 FOV
// for larger FOV the loop below does not converge
// clip values so we still get plausible results for super fisheye images > 180 grad
theta_d = min(max(-CV_PI/2., theta_d), CV_PI/2.);
if (theta_d > 1e-8)
{
// compensate distortion iteratively
double theta = theta_d;
for(int j = 0; j < 10; j++ )
const double EPS = 1e-8; // or std::numeric_limits<double>::epsilon();
for (int j = 0; j < 10; j++)
{
double theta2 = theta*theta, theta4 = theta2*theta2, theta6 = theta4*theta2, theta8 = theta6*theta2;
theta = theta_d / (1 + k[0] * theta2 + k[1] * theta4 + k[2] * theta6 + k[3] * theta8);
double k0_theta2 = k[0] * theta2, k1_theta4 = k[1] * theta4, k2_theta6 = k[2] * theta6, k3_theta8 = k[3] * theta8;
/* new_theta = theta - theta_fix, theta_fix = f0(theta) / f0'(theta) */
double theta_fix = (theta * (1 + k0_theta2 + k1_theta4 + k2_theta6 + k3_theta8) - theta_d) /
(1 + 3*k0_theta2 + 5*k1_theta4 + 7*k2_theta6 + 9*k3_theta8);
theta = theta - theta_fix;
if (fabs(theta_fix) < EPS)
break;
}
scale = std::tan(theta) / theta_d;
@@ -401,12 +426,14 @@ void cv::fisheye::undistortPoints( InputArray distorted, OutputArray undistorted
void cv::fisheye::initUndistortRectifyMap( InputArray K, InputArray D, InputArray R, InputArray P,
const cv::Size& size, int m1type, OutputArray map1, OutputArray map2 )
{
CV_INSTRUMENT_REGION();
CV_Assert( m1type == CV_16SC2 || m1type == CV_32F || m1type <=0 );
map1.create( size, m1type <= 0 ? CV_16SC2 : m1type );
map2.create( size, map1.type() == CV_16SC2 ? CV_16UC1 : CV_32F );
CV_Assert((K.depth() == CV_32F || K.depth() == CV_64F) && (D.depth() == CV_32F || D.depth() == CV_64F));
CV_Assert((P.depth() == CV_32F || P.depth() == CV_64F) && (R.depth() == CV_32F || R.depth() == CV_64F));
CV_Assert((P.empty() || P.depth() == CV_32F || P.depth() == CV_64F) && (R.empty() || R.depth() == CV_32F || R.depth() == CV_64F));
CV_Assert(K.size() == Size(3, 3) && (D.empty() || D.total() == 4));
CV_Assert(R.empty() || R.size() == Size(3, 3) || R.total() * R.channels() == 3);
CV_Assert(P.empty() || P.size() == Size(3, 3) || P.size() == Size(4, 3));
@@ -458,17 +485,26 @@ void cv::fisheye::initUndistortRectifyMap( InputArray K, InputArray D, InputArra
for( int j = 0; j < size.width; ++j)
{
double x = _x/_w, y = _y/_w;
double u, v;
if( _w <= 0)
{
u = (_x > 0) ? -std::numeric_limits<double>::infinity() : std::numeric_limits<double>::infinity();
v = (_y > 0) ? -std::numeric_limits<double>::infinity() : std::numeric_limits<double>::infinity();
}
else
{
double x = _x/_w, y = _y/_w;
double r = sqrt(x*x + y*y);
double theta = atan(r);
double r = sqrt(x*x + y*y);
double theta = atan(r);
double theta2 = theta*theta, theta4 = theta2*theta2, theta6 = theta4*theta2, theta8 = theta4*theta4;
double theta_d = theta * (1 + k[0]*theta2 + k[1]*theta4 + k[2]*theta6 + k[3]*theta8);
double theta2 = theta*theta, theta4 = theta2*theta2, theta6 = theta4*theta2, theta8 = theta4*theta4;
double theta_d = theta * (1 + k[0]*theta2 + k[1]*theta4 + k[2]*theta6 + k[3]*theta8);
double scale = (r == 0) ? 1.0 : theta_d / r;
double u = f[0]*x*scale + c[0];
double v = f[1]*y*scale + c[1];
double scale = (r == 0) ? 1.0 : theta_d / r;
u = f[0]*x*scale + c[0];
v = f[1]*y*scale + c[1];
}
if( m1type == CV_16SC2 )
{
@@ -497,7 +533,9 @@ void cv::fisheye::initUndistortRectifyMap( InputArray K, InputArray D, InputArra
void cv::fisheye::undistortImage(InputArray distorted, OutputArray undistorted,
InputArray K, InputArray D, InputArray Knew, const Size& new_size)
{
Size size = new_size.area() != 0 ? new_size : distorted.size();
CV_INSTRUMENT_REGION();
Size size = !new_size.empty() ? new_size : distorted.size();
cv::Mat map1, map2;
fisheye::initUndistortRectifyMap(K, D, cv::Matx33d::eye(), Knew, size, CV_16SC2, map1, map2 );
@@ -511,8 +549,10 @@ void cv::fisheye::undistortImage(InputArray distorted, OutputArray undistorted,
void cv::fisheye::estimateNewCameraMatrixForUndistortRectify(InputArray K, InputArray D, const Size &image_size, InputArray R,
OutputArray P, double balance, const Size& new_size, double fov_scale)
{
CV_INSTRUMENT_REGION();
CV_Assert( K.size() == Size(3, 3) && (K.depth() == CV_32F || K.depth() == CV_64F));
CV_Assert((D.empty() || D.total() == 4) && (D.depth() == CV_32F || D.depth() == CV_64F || D.empty()));
CV_Assert(D.empty() || ((D.total() == 4) && (D.depth() == CV_32F || D.depth() == CV_64F)));
int w = image_size.width, h = image_size.height;
balance = std::min(std::max(balance, 0.0), 1.0);
@@ -524,20 +564,6 @@ void cv::fisheye::estimateNewCameraMatrixForUndistortRectify(InputArray K, Input
pptr[2] = Vec2d(w/2, h);
pptr[3] = Vec2d(0, h/2);
#if 0
const int N = 10;
cv::Mat points(1, N * 4, CV_64FC2);
Vec2d* pptr = points.ptr<Vec2d>();
for(int i = 0, k = 0; i < 10; ++i)
{
pptr[k++] = Vec2d(w/2, 0) - Vec2d(w/8, 0) + Vec2d(w/4/N*i, 0);
pptr[k++] = Vec2d(w/2, h-1) - Vec2d(w/8, h-1) + Vec2d(w/4/N*i, h-1);
pptr[k++] = Vec2d(0, h/2) - Vec2d(0, h/8) + Vec2d(0, h/4/N*i);
pptr[k++] = Vec2d(w-1, h/2) - Vec2d(w-1, h/8) + Vec2d(w-1, h/4/N*i);
}
#endif
fisheye::undistortPoints(points, points, K, D, R);
cv::Scalar center_mass = mean(points);
cv::Vec2d cn(center_mass.val);
@@ -559,17 +585,6 @@ void cv::fisheye::estimateNewCameraMatrixForUndistortRectify(InputArray K, Input
maxx = std::max(maxx, pptr[i][0]);
}
#if 0
double minx = -DBL_MAX, miny = -DBL_MAX, maxx = DBL_MAX, maxy = DBL_MAX;
for(size_t i = 0; i < points.total(); ++i)
{
if (i % 4 == 0) miny = std::max(miny, pptr[i][1]);
if (i % 4 == 1) maxy = std::min(maxy, pptr[i][1]);
if (i % 4 == 2) minx = std::max(minx, pptr[i][0]);
if (i % 4 == 3) maxx = std::min(maxx, pptr[i][0]);
}
#endif
double f1 = w * 0.5/(cn[0] - minx);
double f2 = w * 0.5/(maxx - cn[0]);
double f3 = h * 0.5 * aspect_ratio/(cn[1] - miny);
@@ -587,7 +602,7 @@ void cv::fisheye::estimateNewCameraMatrixForUndistortRectify(InputArray K, Input
new_f[1] /= aspect_ratio;
new_c[1] /= aspect_ratio;
if (new_size.area() > 0)
if (!new_size.empty())
{
double rx = new_size.width /(double)image_size.width;
double ry = new_size.height/(double)image_size.height;
@@ -609,6 +624,8 @@ void cv::fisheye::stereoRectify( InputArray K1, InputArray D1, InputArray K2, In
InputArray _R, InputArray _tvec, OutputArray R1, OutputArray R2, OutputArray P1, OutputArray P2,
OutputArray Q, int flags, const Size& newImageSize, double balance, double fov_scale)
{
CV_INSTRUMENT_REGION();
CV_Assert((_R.size() == Size(3, 3) || _R.total() * _R.channels() == 3) && (_R.depth() == CV_32F || _R.depth() == CV_64F));
CV_Assert(_tvec.total() * _tvec.channels() == 3 && (_tvec.depth() == CV_32F || _tvec.depth() == CV_64F));
@@ -691,15 +708,17 @@ double cv::fisheye::calibrate(InputArrayOfArrays objectPoints, InputArrayOfArray
InputOutputArray K, InputOutputArray D, OutputArrayOfArrays rvecs, OutputArrayOfArrays tvecs,
int flags , cv::TermCriteria criteria)
{
CV_INSTRUMENT_REGION();
CV_Assert(!objectPoints.empty() && !imagePoints.empty() && objectPoints.total() == imagePoints.total());
CV_Assert(objectPoints.type() == CV_32FC3 || objectPoints.type() == CV_64FC3);
CV_Assert(imagePoints.type() == CV_32FC2 || imagePoints.type() == CV_64FC2);
CV_Assert((!K.empty() && K.size() == Size(3,3)) || K.empty());
CV_Assert((!D.empty() && D.total() == 4) || D.empty());
CV_Assert((!rvecs.empty() && rvecs.channels() == 3) || rvecs.empty());
CV_Assert((!tvecs.empty() && tvecs.channels() == 3) || tvecs.empty());
CV_Assert(K.empty() || (K.size() == Size(3,3)));
CV_Assert(D.empty() || (D.total() == 4));
CV_Assert(rvecs.empty() || (rvecs.channels() == 3));
CV_Assert(tvecs.empty() || (tvecs.channels() == 3));
CV_Assert(((flags & CALIB_USE_INTRINSIC_GUESS) && !K.empty() && !D.empty()) || !(flags & CALIB_USE_INTRINSIC_GUESS));
CV_Assert((!K.empty() && !D.empty()) || !(flags & CALIB_USE_INTRINSIC_GUESS));
using namespace cv::internal;
//-------------------------------Initialization
@@ -709,8 +728,8 @@ double cv::fisheye::calibrate(InputArrayOfArrays objectPoints, InputArrayOfArray
finalParam.isEstimate[0] = 1;
finalParam.isEstimate[1] = 1;
finalParam.isEstimate[2] = 1;
finalParam.isEstimate[3] = 1;
finalParam.isEstimate[2] = flags & CALIB_FIX_PRINCIPAL_POINT ? 0 : 1;
finalParam.isEstimate[3] = flags & CALIB_FIX_PRINCIPAL_POINT ? 0 : 1;
finalParam.isEstimate[4] = flags & CALIB_FIX_SKEW ? 0 : 1;
finalParam.isEstimate[5] = flags & CALIB_FIX_K1 ? 0 : 1;
finalParam.isEstimate[6] = flags & CALIB_FIX_K2 ? 0 : 1;
@@ -753,7 +772,7 @@ double cv::fisheye::calibrate(InputArrayOfArrays objectPoints, InputArrayOfArray
//-------------------------------Optimization
for(int iter = 0; ; ++iter)
for(int iter = 0; iter < std::numeric_limits<int>::max(); ++iter)
{
if ((criteria.type == 1 && iter >= criteria.maxCount) ||
(criteria.type == 2 && change <= criteria.epsilon) ||
@@ -762,12 +781,12 @@ double cv::fisheye::calibrate(InputArrayOfArrays objectPoints, InputArrayOfArray
double alpha_smooth2 = 1 - std::pow(1 - alpha_smooth, iter + 1.0);
Mat JJ2_inv, ex3;
ComputeJacobians(objectPoints, imagePoints, finalParam, omc, Tc, check_cond,thresh_cond, JJ2_inv, ex3);
Mat JJ2, ex3;
ComputeJacobians(objectPoints, imagePoints, finalParam, omc, Tc, check_cond,thresh_cond, JJ2, ex3);
Mat G = alpha_smooth2 * JJ2_inv * ex3;
currentParam = finalParam + G;
Mat G;
solve(JJ2, ex3, G);
currentParam = finalParam + alpha_smooth2*G;
change = norm(Vec4d(currentParam.f[0], currentParam.f[1], currentParam.c[0], currentParam.c[1]) -
Vec4d(finalParam.f[0], finalParam.f[1], finalParam.c[0], finalParam.c[1]))
@@ -794,8 +813,29 @@ double cv::fisheye::calibrate(InputArrayOfArrays objectPoints, InputArrayOfArray
if (K.needed()) cv::Mat(_K).convertTo(K, K.empty() ? CV_64FC1 : K.type());
if (D.needed()) cv::Mat(finalParam.k).convertTo(D, D.empty() ? CV_64FC1 : D.type());
if (rvecs.needed()) cv::Mat(omc).convertTo(rvecs, rvecs.empty() ? CV_64FC3 : rvecs.type());
if (tvecs.needed()) cv::Mat(Tc).convertTo(tvecs, tvecs.empty() ? CV_64FC3 : tvecs.type());
if (rvecs.isMatVector())
{
int N = (int)objectPoints.total();
if(rvecs.empty())
rvecs.create(N, 1, CV_64FC3);
if(tvecs.empty())
tvecs.create(N, 1, CV_64FC3);
for(int i = 0; i < N; i++ )
{
rvecs.create(3, 1, CV_64F, i, true);
tvecs.create(3, 1, CV_64F, i, true);
memcpy(rvecs.getMat(i).ptr(), omc[i].val, sizeof(Vec3d));
memcpy(tvecs.getMat(i).ptr(), Tc[i].val, sizeof(Vec3d));
}
}
else
{
if (rvecs.needed()) cv::Mat(omc).convertTo(rvecs, rvecs.empty() ? CV_64FC3 : rvecs.type());
if (tvecs.needed()) cv::Mat(Tc).convertTo(tvecs, tvecs.empty() ? CV_64FC3 : tvecs.type());
}
return rms;
}
@@ -807,18 +847,20 @@ double cv::fisheye::stereoCalibrate(InputArrayOfArrays objectPoints, InputArrayO
InputOutputArray K1, InputOutputArray D1, InputOutputArray K2, InputOutputArray D2, Size imageSize,
OutputArray R, OutputArray T, int flags, TermCriteria criteria)
{
CV_INSTRUMENT_REGION();
CV_Assert(!objectPoints.empty() && !imagePoints1.empty() && !imagePoints2.empty());
CV_Assert(objectPoints.total() == imagePoints1.total() || imagePoints1.total() == imagePoints2.total());
CV_Assert(objectPoints.type() == CV_32FC3 || objectPoints.type() == CV_64FC3);
CV_Assert(imagePoints1.type() == CV_32FC2 || imagePoints1.type() == CV_64FC2);
CV_Assert(imagePoints2.type() == CV_32FC2 || imagePoints2.type() == CV_64FC2);
CV_Assert((!K1.empty() && K1.size() == Size(3,3)) || K1.empty());
CV_Assert((!D1.empty() && D1.total() == 4) || D1.empty());
CV_Assert((!K2.empty() && K1.size() == Size(3,3)) || K2.empty());
CV_Assert((!D2.empty() && D1.total() == 4) || D2.empty());
CV_Assert(K1.empty() || (K1.size() == Size(3,3)));
CV_Assert(D1.empty() || (D1.total() == 4));
CV_Assert(K2.empty() || (K2.size() == Size(3,3)));
CV_Assert(D2.empty() || (D2.total() == 4));
CV_Assert(((flags & CALIB_FIX_INTRINSIC) && !K1.empty() && !K2.empty() && !D1.empty() && !D2.empty()) || !(flags & CALIB_FIX_INTRINSIC));
CV_Assert((!K1.empty() && !K2.empty() && !D1.empty() && !D2.empty()) || !(flags & CALIB_FIX_INTRINSIC));
//-------------------------------Initialization
@@ -860,8 +902,8 @@ double cv::fisheye::stereoCalibrate(InputArrayOfArrays objectPoints, InputArrayO
if ((flags & CALIB_FIX_INTRINSIC))
{
internal::CalibrateExtrinsics(objectPoints, imagePoints1, intrinsicLeft, check_cond, thresh_cond, rvecs1, tvecs1);
internal::CalibrateExtrinsics(objectPoints, imagePoints2, intrinsicRight, check_cond, thresh_cond, rvecs2, tvecs2);
cv::internal::CalibrateExtrinsics(objectPoints, imagePoints1, intrinsicLeft, check_cond, thresh_cond, rvecs1, tvecs1);
cv::internal::CalibrateExtrinsics(objectPoints, imagePoints2, intrinsicRight, check_cond, thresh_cond, rvecs2, tvecs2);
}
intrinsicLeft.isEstimate[0] = flags & CALIB_FIX_INTRINSIC ? 0 : 1;
@@ -887,8 +929,8 @@ double cv::fisheye::stereoCalibrate(InputArrayOfArrays objectPoints, InputArrayO
intrinsicLeft_errors.isEstimate = intrinsicLeft.isEstimate;
intrinsicRight_errors.isEstimate = intrinsicRight.isEstimate;
std::vector<int> selectedParams;
std::vector<int> tmp(6 * (n_images + 1), 1);
std::vector<uchar> selectedParams;
std::vector<uchar> tmp(6 * (n_images + 1), 1);
selectedParams.insert(selectedParams.end(), intrinsicLeft.isEstimate.begin(), intrinsicLeft.isEstimate.end());
selectedParams.insert(selectedParams.end(), intrinsicRight.isEstimate.begin(), intrinsicRight.isEstimate.end());
selectedParams.insert(selectedParams.end(), tmp.begin(), tmp.end());
@@ -906,12 +948,11 @@ double cv::fisheye::stereoCalibrate(InputArrayOfArrays objectPoints, InputArrayO
om_ref.reshape(3, 1).copyTo(om_list.col(image_idx));
T_ref.reshape(3, 1).copyTo(T_list.col(image_idx));
}
cv::Vec3d omcur = internal::median3d(om_list);
cv::Vec3d Tcur = internal::median3d(T_list);
cv::Vec3d omcur = cv::internal::median3d(om_list);
cv::Vec3d Tcur = cv::internal::median3d(T_list);
cv::Mat J = cv::Mat::zeros(4 * n_points * n_images, 18 + 6 * (n_images + 1), CV_64FC1),
e = cv::Mat::zeros(4 * n_points * n_images, 1, CV_64FC1), Jkk, ekk;
cv::Mat J2_inv;
for(int iter = 0; ; ++iter)
{
@@ -949,7 +990,7 @@ double cv::fisheye::stereoCalibrate(InputArrayOfArrays objectPoints, InputArrayO
jacobians.col(14).copyTo(Jkk.col(4).rowRange(0, 2 * n_points));
//right camera jacobian
internal::compose_motion(rvec, tvec, omcur, Tcur, omr, Tr, domrdomckk, domrdTckk, domrdom, domrdT, dTrdomckk, dTrdTckk, dTrdom, dTrdT);
cv::internal::compose_motion(rvec, tvec, omcur, Tcur, omr, Tr, domrdomckk, domrdTckk, domrdom, domrdT, dTrdomckk, dTrdTckk, dTrdom, dTrdT);
rvec = cv::Mat(rvecs2[image_idx]);
tvec = cv::Mat(tvecs2[image_idx]);
@@ -988,14 +1029,15 @@ double cv::fisheye::stereoCalibrate(InputArrayOfArrays objectPoints, InputArrayO
cv::Vec6d oldTom(Tcur[0], Tcur[1], Tcur[2], omcur[0], omcur[1], omcur[2]);
//update all parameters
cv::subMatrix(J, J, selectedParams, std::vector<int>(J.rows, 1));
cv::Mat J2 = J.t() * J;
J2_inv = J2.inv();
cv::subMatrix(J, J, selectedParams, std::vector<uchar>(J.rows, 1));
int a = cv::countNonZero(intrinsicLeft.isEstimate);
int b = cv::countNonZero(intrinsicRight.isEstimate);
cv::Mat deltas = J2_inv * J.t() * e;
intrinsicLeft = intrinsicLeft + deltas.rowRange(0, a);
intrinsicRight = intrinsicRight + deltas.rowRange(a, a + b);
cv::Mat deltas;
solve(J.t() * J, J.t()*e, deltas);
if (a > 0)
intrinsicLeft = intrinsicLeft + deltas.rowRange(0, a);
if (b > 0)
intrinsicRight = intrinsicRight + deltas.rowRange(a, a + b);
omcur = omcur + cv::Vec3d(deltas.rowRange(a + b, a + b + 3));
Tcur = Tcur + cv::Vec3d(deltas.rowRange(a + b + 3, a + b + 6));
for (int image_idx = 0; image_idx < n_images; ++image_idx)
@@ -1040,12 +1082,12 @@ double cv::fisheye::stereoCalibrate(InputArrayOfArrays objectPoints, InputArrayO
}
namespace cv{ namespace {
void subMatrix(const Mat& src, Mat& dst, const std::vector<int>& cols, const std::vector<int>& rows)
void subMatrix(const Mat& src, Mat& dst, const std::vector<uchar>& cols, const std::vector<uchar>& rows)
{
CV_Assert(src.type() == CV_64FC1);
CV_Assert(src.channels() == 1);
int nonzeros_cols = cv::countNonZero(cols);
Mat tmp(src.rows, nonzeros_cols, CV_64FC1);
Mat tmp(src.rows, nonzeros_cols, CV_64F);
for (int i = 0, j = 0; i < (int)cols.size(); i++)
{
@@ -1056,16 +1098,14 @@ void subMatrix(const Mat& src, Mat& dst, const std::vector<int>& cols, const std
}
int nonzeros_rows = cv::countNonZero(rows);
Mat tmp1(nonzeros_rows, nonzeros_cols, CV_64FC1);
dst.create(nonzeros_rows, nonzeros_cols, CV_64F);
for (int i = 0, j = 0; i < (int)rows.size(); i++)
{
if (rows[i])
{
tmp.row(i).copyTo(tmp1.row(j++));
tmp.row(i).copyTo(dst.row(j++));
}
}
dst = tmp1.clone();
}
}}
@@ -1090,8 +1130,8 @@ cv::internal::IntrinsicParams cv::internal::IntrinsicParams::operator+(const Mat
tmp.f[0] = this->f[0] + (isEstimate[0] ? ptr[j++] : 0);
tmp.f[1] = this->f[1] + (isEstimate[1] ? ptr[j++] : 0);
tmp.c[0] = this->c[0] + (isEstimate[2] ? ptr[j++] : 0);
tmp.alpha = this->alpha + (isEstimate[4] ? ptr[j++] : 0);
tmp.c[1] = this->c[1] + (isEstimate[3] ? ptr[j++] : 0);
tmp.alpha = this->alpha + (isEstimate[4] ? ptr[j++] : 0);
tmp.k[0] = this->k[0] + (isEstimate[5] ? ptr[j++] : 0);
tmp.k[1] = this->k[1] + (isEstimate[6] ? ptr[j++] : 0);
tmp.k[2] = this->k[2] + (isEstimate[7] ? ptr[j++] : 0);
@@ -1133,7 +1173,9 @@ void cv::internal::projectPoints(cv::InputArray objectPoints, cv::OutputArray im
cv::InputArray _rvec,cv::InputArray _tvec,
const IntrinsicParams& param, cv::OutputArray jacobian)
{
CV_Assert(!objectPoints.empty() && objectPoints.type() == CV_64FC3);
CV_INSTRUMENT_REGION();
CV_Assert(!objectPoints.empty() && (objectPoints.type() == CV_32FC3 || objectPoints.type() == CV_64FC3));
Matx33d K(param.f[0], param.f[0] * param.alpha, param.c[0],
0, param.f[1], param.c[1],
0, 0, 1);
@@ -1146,6 +1188,7 @@ void cv::internal::ComputeExtrinsicRefine(const Mat& imagePoints, const Mat& obj
{
CV_Assert(!objectPoints.empty() && objectPoints.type() == CV_64FC3);
CV_Assert(!imagePoints.empty() && imagePoints.type() == CV_64FC2);
CV_Assert(rvec.total() > 2 && tvec.total() > 2);
Vec6d extrinsics(rvec.at<double>(0), rvec.at<double>(1), rvec.at<double>(2),
tvec.at<double>(0), tvec.at<double>(1), tvec.at<double>(2));
double change = 1;
@@ -1185,6 +1228,8 @@ void cv::internal::ComputeExtrinsicRefine(const Mat& imagePoints, const Mat& obj
cv::Mat cv::internal::ComputeHomography(Mat m, Mat M)
{
CV_INSTRUMENT_REGION();
int Np = m.cols;
if (m.rows < 3)
@@ -1284,15 +1329,17 @@ cv::Mat cv::internal::ComputeHomography(Mat m, Mat M)
cv::Mat cv::internal::NormalizePixels(const Mat& imagePoints, const IntrinsicParams& param)
{
CV_INSTRUMENT_REGION();
CV_Assert(!imagePoints.empty() && imagePoints.type() == CV_64FC2);
Mat distorted((int)imagePoints.total(), 1, CV_64FC2), undistorted;
const Vec2d* ptr = imagePoints.ptr<Vec2d>(0);
Vec2d* ptr_d = distorted.ptr<Vec2d>(0);
const Vec2d* ptr = imagePoints.ptr<Vec2d>();
Vec2d* ptr_d = distorted.ptr<Vec2d>();
for (size_t i = 0; i < imagePoints.total(); ++i)
{
ptr_d[i] = (ptr[i] - param.c).mul(Vec2d(1.0 / param.f[0], 1.0 / param.f[1]));
ptr_d[i][0] = ptr_d[i][0] - param.alpha * ptr_d[i][1];
ptr_d[i][0] -= param.alpha * ptr_d[i][1];
}
cv::fisheye::undistortPoints(distorted, undistorted, Matx33d::eye(), param.k);
return undistorted;
@@ -1300,12 +1347,11 @@ cv::Mat cv::internal::NormalizePixels(const Mat& imagePoints, const IntrinsicPar
void cv::internal::InitExtrinsics(const Mat& _imagePoints, const Mat& _objectPoints, const IntrinsicParams& param, Mat& omckk, Mat& Tckk)
{
CV_Assert(!_objectPoints.empty() && _objectPoints.type() == CV_64FC3);
CV_Assert(!_imagePoints.empty() && _imagePoints.type() == CV_64FC2);
Mat imagePointsNormalized = NormalizePixels(_imagePoints.t(), param).reshape(1).t();
Mat objectPoints = Mat(_objectPoints.t()).reshape(1).t();
Mat imagePointsNormalized = NormalizePixels(_imagePoints, param).reshape(1).t();
Mat objectPoints = _objectPoints.reshape(1).t();
Mat objectPointsMean, covObjectPoints;
Mat Rckk;
int Np = imagePointsNormalized.cols;
@@ -1322,9 +1368,13 @@ void cv::internal::InitExtrinsics(const Mat& _imagePoints, const Mat& _objectPoi
double sc = .5 * (norm(H.col(0)) + norm(H.col(1)));
H = H / sc;
Mat u1 = H.col(0).clone();
u1 = u1 / norm(u1);
double norm_u1 = norm(u1);
CV_Assert(fabs(norm_u1) > 0);
u1 = u1 / norm_u1;
Mat u2 = H.col(1).clone() - u1.dot(H.col(1).clone()) * u1;
u2 = u2 / norm(u2);
double norm_u2 = norm(u2);
CV_Assert(fabs(norm_u2) > 0);
u2 = u2 / norm_u2;
Mat u3 = u1.cross(u2);
Mat RRR;
hconcat(u1, u2, RRR);
@@ -1358,23 +1408,26 @@ void cv::internal::CalibrateExtrinsics(InputArrayOfArrays objectPoints, InputArr
objectPoints.getMat(image_idx).convertTo(object, CV_64FC3);
imagePoints.getMat (image_idx).convertTo(image, CV_64FC2);
InitExtrinsics(image, object, param, omckk, Tckk);
bool imT = image.rows < image.cols;
bool obT = object.rows < object.cols;
ComputeExtrinsicRefine(image, object, omckk, Tckk, JJ_kk, maxIter, param, thresh_cond);
InitExtrinsics(imT ? image.t() : image, obT ? object.t() : object, param, omckk, Tckk);
ComputeExtrinsicRefine(!imT ? image.t() : image, !obT ? object.t() : object, omckk, Tckk, JJ_kk, maxIter, param, thresh_cond);
if (check_cond)
{
SVD svd(JJ_kk, SVD::NO_UV);
CV_Assert(svd.w.at<double>(0) / svd.w.at<double>((int)svd.w.total() - 1) < thresh_cond);
if(svd.w.at<double>(0) / svd.w.at<double>((int)svd.w.total() - 1) > thresh_cond )
CV_Error( cv::Error::StsInternal, format("CALIB_CHECK_COND - Ill-conditioned matrix for input array %d",image_idx));
}
omckk.reshape(3,1).copyTo(omc.getMat().col(image_idx));
Tckk.reshape(3,1).copyTo(Tc.getMat().col(image_idx));
}
}
void cv::internal::ComputeJacobians(InputArrayOfArrays objectPoints, InputArrayOfArrays imagePoints,
const IntrinsicParams& param, InputArray omc, InputArray Tc,
const int& check_cond, const double& thresh_cond, Mat& JJ2_inv, Mat& ex3)
const int& check_cond, const double& thresh_cond, Mat& JJ2, Mat& ex3)
{
CV_Assert(!objectPoints.empty() && (objectPoints.type() == CV_32FC3 || objectPoints.type() == CV_64FC3));
CV_Assert(!imagePoints.empty() && (imagePoints.type() == CV_32FC2 || imagePoints.type() == CV_64FC2));
@@ -1384,7 +1437,7 @@ void cv::internal::ComputeJacobians(InputArrayOfArrays objectPoints, InputArrayO
int n = (int)objectPoints.total();
Mat JJ3 = Mat::zeros(9 + 6 * n, 9 + 6 * n, CV_64FC1);
JJ2 = Mat::zeros(9 + 6 * n, 9 + 6 * n, CV_64FC1);
ex3 = Mat::zeros(9 + 6 * n, 1, CV_64FC1 );
for (int image_idx = 0; image_idx < n; ++image_idx)
@@ -1393,12 +1446,13 @@ void cv::internal::ComputeJacobians(InputArrayOfArrays objectPoints, InputArrayO
objectPoints.getMat(image_idx).convertTo(object, CV_64FC3);
imagePoints.getMat (image_idx).convertTo(image, CV_64FC2);
bool imT = image.rows < image.cols;
Mat om(omc.getMat().col(image_idx)), T(Tc.getMat().col(image_idx));
std::vector<Point2d> x;
Mat jacobians;
projectPoints(object, x, om, T, param, jacobians);
Mat exkk = image.t() - Mat(x);
Mat exkk = (imT ? image.t() : image) - Mat(x);
Mat A(jacobians.rows, 9, CV_64FC1);
jacobians.colRange(0, 4).copyTo(A.colRange(0, 4));
@@ -1410,16 +1464,14 @@ void cv::internal::ComputeJacobians(InputArrayOfArrays objectPoints, InputArrayO
Mat B = jacobians.colRange(8, 14).clone();
B = B.t();
JJ3(Rect(0, 0, 9, 9)) = JJ3(Rect(0, 0, 9, 9)) + A * A.t();
JJ3(Rect(9 + 6 * image_idx, 9 + 6 * image_idx, 6, 6)) = B * B.t();
JJ2(Rect(0, 0, 9, 9)) += A * A.t();
JJ2(Rect(9 + 6 * image_idx, 9 + 6 * image_idx, 6, 6)) = B * B.t();
Mat AB = A * B.t();
AB.copyTo(JJ3(Rect(9 + 6 * image_idx, 0, 6, 9)));
JJ2(Rect(9 + 6 * image_idx, 0, 6, 9)) = A * B.t();
JJ2(Rect(0, 9 + 6 * image_idx, 9, 6)) = JJ2(Rect(9 + 6 * image_idx, 0, 6, 9)).t();
JJ3(Rect(0, 9 + 6 * image_idx, 9, 6)) = AB.t();
ex3(Rect(0,0,1,9)) = ex3(Rect(0,0,1,9)) + A * exkk.reshape(1, 2 * exkk.rows);
ex3(Rect(0, 9 + 6 * image_idx, 1, 6)) = B * exkk.reshape(1, 2 * exkk.rows);
ex3.rowRange(0, 9) += A * exkk.reshape(1, 2 * exkk.rows);
ex3.rowRange(9 + 6 * image_idx, 9 + 6 * (image_idx + 1)) = B * exkk.reshape(1, 2 * exkk.rows);
if (check_cond)
{
@@ -1429,12 +1481,11 @@ void cv::internal::ComputeJacobians(InputArrayOfArrays objectPoints, InputArrayO
}
}
std::vector<int> idxs(param.isEstimate);
std::vector<uchar> idxs(param.isEstimate);
idxs.insert(idxs.end(), 6 * n, 1);
subMatrix(JJ3, JJ3, idxs, idxs);
subMatrix(ex3, ex3, std::vector<int>(1, 1), idxs);
JJ2_inv = JJ3.inv();
subMatrix(JJ2, JJ2, idxs, idxs);
subMatrix(ex3, ex3, std::vector<uchar>(1, 1), idxs);
}
void cv::internal::EstimateUncertainties(InputArrayOfArrays objectPoints, InputArrayOfArrays imagePoints,
@@ -1447,49 +1498,44 @@ void cv::internal::EstimateUncertainties(InputArrayOfArrays objectPoints, InputA
CV_Assert(!omc.empty() && omc.type() == CV_64FC3);
CV_Assert(!Tc.empty() && Tc.type() == CV_64FC3);
Mat ex((int)(objectPoints.getMat(0).total() * objectPoints.total()), 1, CV_64FC2);
int total_ex = 0;
for (int image_idx = 0; image_idx < (int)objectPoints.total(); ++image_idx)
{
total_ex += (int)objectPoints.getMat(image_idx).total();
}
Mat ex(total_ex, 1, CV_64FC2);
int insert_idx = 0;
for (int image_idx = 0; image_idx < (int)objectPoints.total(); ++image_idx)
{
Mat image, object;
objectPoints.getMat(image_idx).convertTo(object, CV_64FC3);
imagePoints.getMat (image_idx).convertTo(image, CV_64FC2);
bool imT = image.rows < image.cols;
Mat om(omc.getMat().col(image_idx)), T(Tc.getMat().col(image_idx));
std::vector<Point2d> x;
projectPoints(object, x, om, T, params, noArray());
Mat ex_ = image.t() - Mat(x);
ex_.copyTo(ex.rowRange(ex_.rows * image_idx, ex_.rows * (image_idx + 1)));
Mat ex_ = (imT ? image.t() : image) - Mat(x);
ex_.copyTo(ex.rowRange(insert_idx, insert_idx + ex_.rows));
insert_idx += ex_.rows;
}
meanStdDev(ex, noArray(), std_err);
std_err *= sqrt((double)ex.total()/((double)ex.total() - 1.0));
Mat sigma_x;
Vec<double, 1> sigma_x;
meanStdDev(ex.reshape(1, 1), noArray(), sigma_x);
sigma_x *= sqrt(2.0 * (double)ex.total()/(2.0 * (double)ex.total() - 1.0));
Mat _JJ2_inv, ex3;
ComputeJacobians(objectPoints, imagePoints, params, omc, Tc, check_cond, thresh_cond, _JJ2_inv, ex3);
Mat JJ2, ex3;
ComputeJacobians(objectPoints, imagePoints, params, omc, Tc, check_cond, thresh_cond, JJ2, ex3);
Mat_<double>& JJ2_inv = (Mat_<double>&)_JJ2_inv;
sqrt(JJ2.inv(), JJ2);
sqrt(JJ2_inv, JJ2_inv);
double s = sigma_x.at<double>(0);
Mat r = 3 * s * JJ2_inv.diag();
errors = r;
rms = 0;
const Vec2d* ptr_ex = ex.ptr<Vec2d>();
for (size_t i = 0; i < ex.total(); i++)
{
rms += ptr_ex[i][0] * ptr_ex[i][0] + ptr_ex[i][1] * ptr_ex[i][1];
}
rms /= (double)ex.total();
rms = sqrt(rms);
errors = 3 * sigma_x(0) * JJ2.diag();
rms = sqrt(norm(ex, NORM_L2SQR)/ex.total());
}
void cv::internal::dAB(InputArray A, InputArray B, OutputArray dABdA, OutputArray dABdB)
+1 -1
View File
@@ -10,7 +10,7 @@ struct CV_EXPORTS IntrinsicParams
Vec2d c;
Vec4d k;
double alpha;
std::vector<int> isEstimate;
std::vector<uchar> isEstimate;
IntrinsicParams();
IntrinsicParams(Vec2d f, Vec2d c, Vec4d k, double alpha = 0);
+92 -40
View File
@@ -34,10 +34,10 @@
namespace cv
{
class EMEstimatorCallback : public PointSetRegistrator::Callback
class EMEstimatorCallback CV_FINAL : public PointSetRegistrator::Callback
{
public:
int runKernel( InputArray _m1, InputArray _m2, OutputArray _model ) const
int runKernel( InputArray _m1, InputArray _m2, OutputArray _model ) const CV_OVERRIDE
{
Mat q1 = _m1.getMat(), q2 = _m2.getMat();
Mat Q1 = q1.reshape(1, (int)q1.total());
@@ -61,7 +61,7 @@ public:
Mat EE = Mat(Vt.t()).colRange(5, 9) * 1.0;
Mat A(10, 20, CV_64F);
EE = EE.t();
getCoeffMat((double*)EE.data, (double*)A.data);
getCoeffMat(EE.ptr<double>(), A.ptr<double>());
EE = EE.t();
A = A.colRange(0, 10).inv() * A.colRange(10, 20);
@@ -137,7 +137,7 @@ public:
cv::Mat Evec = EE.col(0) * xs.back() + EE.col(1) * ys.back() + EE.col(2) * zs.back() + EE.col(3);
Evec /= norm(Evec);
memcpy(e + count * 9, Evec.data, 9 * sizeof(double));
memcpy(e + count * 9, Evec.ptr(), 9 * sizeof(double));
count++;
}
@@ -370,7 +370,7 @@ protected:
}
void computeError( InputArray _m1, InputArray _m2, InputArray _model, OutputArray _err ) const
void computeError( InputArray _m1, InputArray _m2, InputArray _model, OutputArray _err ) const CV_OVERRIDE
{
Mat X1 = _m1.getMat(), X2 = _m2.getMat(), model = _model.getMat();
const Point2d* x1ptr = X1.ptr<Point2d>();
@@ -402,37 +402,43 @@ protected:
}
// Input should be a vector of n 2D points or a Nx2 matrix
cv::Mat cv::findEssentialMat( InputArray _points1, InputArray _points2, double focal, Point2d pp,
cv::Mat cv::findEssentialMat( InputArray _points1, InputArray _points2, InputArray _cameraMatrix,
int method, double prob, double threshold, OutputArray _mask)
{
Mat points1, points2;
CV_INSTRUMENT_REGION();
Mat points1, points2, cameraMatrix;
_points1.getMat().convertTo(points1, CV_64F);
_points2.getMat().convertTo(points2, CV_64F);
_cameraMatrix.getMat().convertTo(cameraMatrix, CV_64F);
int npoints = points1.checkVector(2);
CV_Assert( npoints >= 5 && points2.checkVector(2) == npoints &&
CV_Assert( npoints >= 0 && points2.checkVector(2) == npoints &&
points1.type() == points2.type());
if( points1.channels() > 1 )
CV_Assert(cameraMatrix.rows == 3 && cameraMatrix.cols == 3 && cameraMatrix.channels() == 1);
if (points1.channels() > 1)
{
points1 = points1.reshape(1, npoints);
points2 = points2.reshape(1, npoints);
}
double ifocal = focal != 0 ? 1./focal : 1.;
for( int i = 0; i < npoints; i++ )
{
points1.at<double>(i, 0) = (points1.at<double>(i, 0) - pp.x)*ifocal;
points1.at<double>(i, 1) = (points1.at<double>(i, 1) - pp.y)*ifocal;
points2.at<double>(i, 0) = (points2.at<double>(i, 0) - pp.x)*ifocal;
points2.at<double>(i, 1) = (points2.at<double>(i, 1) - pp.y)*ifocal;
}
double fx = cameraMatrix.at<double>(0,0);
double fy = cameraMatrix.at<double>(1,1);
double cx = cameraMatrix.at<double>(0,2);
double cy = cameraMatrix.at<double>(1,2);
points1.col(0) = (points1.col(0) - cx) / fx;
points2.col(0) = (points2.col(0) - cx) / fx;
points1.col(1) = (points1.col(1) - cy) / fy;
points2.col(1) = (points2.col(1) - cy) / fy;
// Reshape data to fit opencv ransac function
points1 = points1.reshape(2, npoints);
points2 = points2.reshape(2, npoints);
threshold /= focal;
threshold /= (fx+fy)/2;
Mat E;
if( method == RANSAC )
@@ -443,29 +449,47 @@ cv::Mat cv::findEssentialMat( InputArray _points1, InputArray _points2, double f
return E;
}
int cv::recoverPose( InputArray E, InputArray _points1, InputArray _points2, OutputArray _R,
OutputArray _t, double focal, Point2d pp, InputOutputArray _mask)
cv::Mat cv::findEssentialMat( InputArray _points1, InputArray _points2, double focal, Point2d pp,
int method, double prob, double threshold, OutputArray _mask)
{
Mat points1, points2;
_points1.getMat().copyTo(points1);
_points2.getMat().copyTo(points2);
CV_INSTRUMENT_REGION();
Mat cameraMatrix = (Mat_<double>(3,3) << focal, 0, pp.x, 0, focal, pp.y, 0, 0, 1);
return cv::findEssentialMat(_points1, _points2, cameraMatrix, method, prob, threshold, _mask);
}
int cv::recoverPose( InputArray E, InputArray _points1, InputArray _points2,
InputArray _cameraMatrix, OutputArray _R, OutputArray _t, double distanceThresh,
InputOutputArray _mask, OutputArray triangulatedPoints)
{
CV_INSTRUMENT_REGION();
Mat points1, points2, cameraMatrix;
_points1.getMat().convertTo(points1, CV_64F);
_points2.getMat().convertTo(points2, CV_64F);
_cameraMatrix.getMat().convertTo(cameraMatrix, CV_64F);
int npoints = points1.checkVector(2);
CV_Assert( npoints >= 0 && points2.checkVector(2) == npoints &&
points1.type() == points2.type());
CV_Assert(cameraMatrix.rows == 3 && cameraMatrix.cols == 3 && cameraMatrix.channels() == 1);
if (points1.channels() > 1)
{
points1 = points1.reshape(1, npoints);
points2 = points2.reshape(1, npoints);
}
points1.convertTo(points1, CV_64F);
points2.convertTo(points2, CV_64F);
points1.col(0) = (points1.col(0) - pp.x) / focal;
points2.col(0) = (points2.col(0) - pp.x) / focal;
points1.col(1) = (points1.col(1) - pp.y) / focal;
points2.col(1) = (points2.col(1) - pp.y) / focal;
double fx = cameraMatrix.at<double>(0,0);
double fy = cameraMatrix.at<double>(1,1);
double cx = cameraMatrix.at<double>(0,2);
double cy = cameraMatrix.at<double>(1,2);
points1.col(0) = (points1.col(0) - cx) / fx;
points2.col(0) = (points2.col(0) - cx) / fx;
points1.col(1) = (points1.col(1) - cy) / fy;
points2.col(1) = (points2.col(1) - cy) / fy;
points1 = points1.t();
points2 = points2.t();
@@ -482,52 +506,61 @@ int cv::recoverPose( InputArray E, InputArray _points1, InputArray _points2, Out
// Do the cheirality check.
// Notice here a threshold dist is used to filter
// out far away points (i.e. infinite points) since
// there depth may vary between postive and negtive.
double dist = 50.0;
// their depth may vary between positive and negative.
std::vector<Mat> allTriangulations(4);
Mat Q;
triangulatePoints(P0, P1, points1, points2, Q);
if(triangulatedPoints.needed())
Q.copyTo(allTriangulations[0]);
Mat mask1 = Q.row(2).mul(Q.row(3)) > 0;
Q.row(0) /= Q.row(3);
Q.row(1) /= Q.row(3);
Q.row(2) /= Q.row(3);
Q.row(3) /= Q.row(3);
mask1 = (Q.row(2) < dist) & mask1;
mask1 = (Q.row(2) < distanceThresh) & mask1;
Q = P1 * Q;
mask1 = (Q.row(2) > 0) & mask1;
mask1 = (Q.row(2) < dist) & mask1;
mask1 = (Q.row(2) < distanceThresh) & mask1;
triangulatePoints(P0, P2, points1, points2, Q);
if(triangulatedPoints.needed())
Q.copyTo(allTriangulations[1]);
Mat mask2 = Q.row(2).mul(Q.row(3)) > 0;
Q.row(0) /= Q.row(3);
Q.row(1) /= Q.row(3);
Q.row(2) /= Q.row(3);
Q.row(3) /= Q.row(3);
mask2 = (Q.row(2) < dist) & mask2;
mask2 = (Q.row(2) < distanceThresh) & mask2;
Q = P2 * Q;
mask2 = (Q.row(2) > 0) & mask2;
mask2 = (Q.row(2) < dist) & mask2;
mask2 = (Q.row(2) < distanceThresh) & mask2;
triangulatePoints(P0, P3, points1, points2, Q);
if(triangulatedPoints.needed())
Q.copyTo(allTriangulations[2]);
Mat mask3 = Q.row(2).mul(Q.row(3)) > 0;
Q.row(0) /= Q.row(3);
Q.row(1) /= Q.row(3);
Q.row(2) /= Q.row(3);
Q.row(3) /= Q.row(3);
mask3 = (Q.row(2) < dist) & mask3;
mask3 = (Q.row(2) < distanceThresh) & mask3;
Q = P3 * Q;
mask3 = (Q.row(2) > 0) & mask3;
mask3 = (Q.row(2) < dist) & mask3;
mask3 = (Q.row(2) < distanceThresh) & mask3;
triangulatePoints(P0, P4, points1, points2, Q);
if(triangulatedPoints.needed())
Q.copyTo(allTriangulations[3]);
Mat mask4 = Q.row(2).mul(Q.row(3)) > 0;
Q.row(0) /= Q.row(3);
Q.row(1) /= Q.row(3);
Q.row(2) /= Q.row(3);
Q.row(3) /= Q.row(3);
mask4 = (Q.row(2) < dist) & mask4;
mask4 = (Q.row(2) < distanceThresh) & mask4;
Q = P4 * Q;
mask4 = (Q.row(2) > 0) & mask4;
mask4 = (Q.row(2) < dist) & mask4;
mask4 = (Q.row(2) < distanceThresh) & mask4;
mask1 = mask1.t();
mask2 = mask2.t();
@@ -538,7 +571,8 @@ int cv::recoverPose( InputArray E, InputArray _points1, InputArray _points2, Out
if (!_mask.empty())
{
Mat mask = _mask.getMat();
CV_Assert(mask.size() == mask1.size());
CV_Assert(npoints == mask.checkVector(1));
mask = mask.reshape(1, npoints);
bitwise_and(mask, mask1, mask1);
bitwise_and(mask, mask2, mask2);
bitwise_and(mask, mask3, mask3);
@@ -560,6 +594,7 @@ int cv::recoverPose( InputArray E, InputArray _points1, InputArray _points2, Out
if (good1 >= good2 && good1 >= good3 && good1 >= good4)
{
if(triangulatedPoints.needed()) allTriangulations[0].copyTo(triangulatedPoints);
R1.copyTo(_R);
t.copyTo(_t);
if (_mask.needed()) mask1.copyTo(_mask);
@@ -567,6 +602,7 @@ int cv::recoverPose( InputArray E, InputArray _points1, InputArray _points2, Out
}
else if (good2 >= good1 && good2 >= good3 && good2 >= good4)
{
if(triangulatedPoints.needed()) allTriangulations[1].copyTo(triangulatedPoints);
R2.copyTo(_R);
t.copyTo(_t);
if (_mask.needed()) mask2.copyTo(_mask);
@@ -574,6 +610,7 @@ int cv::recoverPose( InputArray E, InputArray _points1, InputArray _points2, Out
}
else if (good3 >= good1 && good3 >= good2 && good3 >= good4)
{
if(triangulatedPoints.needed()) allTriangulations[2].copyTo(triangulatedPoints);
t = -t;
R1.copyTo(_R);
t.copyTo(_t);
@@ -582,6 +619,7 @@ int cv::recoverPose( InputArray E, InputArray _points1, InputArray _points2, Out
}
else
{
if(triangulatedPoints.needed()) allTriangulations[3].copyTo(triangulatedPoints);
t = -t;
R2.copyTo(_R);
t.copyTo(_t);
@@ -590,9 +628,23 @@ int cv::recoverPose( InputArray E, InputArray _points1, InputArray _points2, Out
}
}
int cv::recoverPose( InputArray E, InputArray _points1, InputArray _points2, InputArray _cameraMatrix,
OutputArray _R, OutputArray _t, InputOutputArray _mask)
{
return cv::recoverPose(E, _points1, _points2, _cameraMatrix, _R, _t, 50, _mask);
}
int cv::recoverPose( InputArray E, InputArray _points1, InputArray _points2, OutputArray _R,
OutputArray _t, double focal, Point2d pp, InputOutputArray _mask)
{
Mat cameraMatrix = (Mat_<double>(3,3) << focal, 0, pp.x, 0, focal, pp.y, 0, 0, 1);
return cv::recoverPose(E, _points1, _points2, cameraMatrix, _R, _t, _mask);
}
void cv::decomposeEssentialMat( InputArray _E, OutputArray _R1, OutputArray _R2, OutputArray _t )
{
CV_INSTRUMENT_REGION();
Mat E = _E.getMat().reshape(1, 3);
CV_Assert(E.cols == 3 && E.rows == 3);
+350 -151
View File
@@ -41,52 +41,28 @@
//M*/
#include "precomp.hpp"
#include "rho.h"
#include <iostream>
namespace cv
{
static bool haveCollinearPoints( const Mat& m, int count )
{
int j, k, i = count-1;
const Point2f* ptr = m.ptr<Point2f>();
// check that the i-th selected point does not belong
// to a line connecting some previously selected points
for( j = 0; j < i; j++ )
{
double dx1 = ptr[j].x - ptr[i].x;
double dy1 = ptr[j].y - ptr[i].y;
for( k = 0; k < j; k++ )
{
double dx2 = ptr[k].x - ptr[i].x;
double dy2 = ptr[k].y - ptr[i].y;
if( fabs(dx2*dy1 - dy2*dx1) <= FLT_EPSILON*(fabs(dx1) + fabs(dy1) + fabs(dx2) + fabs(dy2)))
return true;
}
}
return false;
}
template<typename T> int compressPoints( T* ptr, const uchar* mask, int mstep, int count )
{
int i, j;
for( i = j = 0; i < count; i++ )
if( mask[i*mstep] )
{
if( i > j )
ptr[j] = ptr[i];
j++;
}
return j;
}
class HomographyEstimatorCallback : public PointSetRegistrator::Callback
/**
* This class estimates a homography \f$H\in \mathbb{R}^{3\times 3}\f$
* between \f$\mathbf{x} \in \mathbb{R}^3\f$ and
* \f$\mathbf{X} \in \mathbb{R}^3\f$ using DLT (direct linear transform)
* with algebraic distance.
*
* \f[
* \lambda \mathbf{x} = H \mathbf{X}
* \f]
* where \f$\lambda \in \mathbb{R} \f$.
*
*/
class HomographyEstimatorCallback CV_FINAL : public PointSetRegistrator::Callback
{
public:
bool checkSubset( InputArray _ms1, InputArray _ms2, int count ) const
bool checkSubset( InputArray _ms1, InputArray _ms2, int count ) const CV_OVERRIDE
{
Mat ms1 = _ms1.getMat(), ms2 = _ms2.getMat();
if( haveCollinearPoints(ms1, count) || haveCollinearPoints(ms2, count) )
@@ -96,7 +72,7 @@ public:
// are geometrically consistent. We check if every 3 correspondences sets
// fulfills the constraint.
//
// The usefullness of this constraint is explained in the paper:
// The usefulness of this constraint is explained in the paper:
//
// "Speeding-up homography estimation in mobile devices"
// Journal of Real-Time Image Processing. 2013. DOI: 10.1007/s11554-012-0314-1
@@ -123,7 +99,21 @@ public:
return true;
}
int runKernel( InputArray _m1, InputArray _m2, OutputArray _model ) const
/**
* Normalization method:
* - $x$ and $y$ coordinates are normalized independently
* - first the coordinates are shifted so that the average coordinate is \f$(0,0)\f$
* - then the coordinates are scaled so that the average L1 norm is 1, i.e,
* the average L1 norm of the \f$x\f$ coordinates is 1 and the average
* L1 norm of the \f$y\f$ coordinates is also 1.
*
* @param _m1 source points containing (X,Y), depth is CV_32F with 1 column 2 channels or
* 2 columns 1 channel
* @param _m2 destination points containing (x,y), depth is CV_32F with 1 column 2 channels or
* 2 columns 1 channel
* @param _model, CV_64FC1, 3x3, normalized, i.e., the last element is 1
*/
int runKernel( InputArray _m1, InputArray _m2, OutputArray _model ) const CV_OVERRIDE
{
Mat m1 = _m1.getMat(), m2 = _m2.getMat();
int i, count = m1.checkVector(2);
@@ -190,7 +180,15 @@ public:
return 1;
}
void computeError( InputArray _m1, InputArray _m2, InputArray _model, OutputArray _err ) const
/**
* Compute the reprojection error.
* m2 = H*m1
* @param _m1 depth CV_32F, 1-channel with 2 columns or 2-channel with 1 column
* @param _m2 depth CV_32F, 1-channel with 2 columns or 2-channel with 1 column
* @param _model CV_64FC1, 3x3
* @param _err, output, CV_32FC1, square of the L2 norm
*/
void computeError( InputArray _m1, InputArray _m2, InputArray _model, OutputArray _err ) const CV_OVERRIDE
{
Mat m1 = _m1.getMat(), m2 = _m2.getMat(), model = _model.getMat();
int i, count = m1.checkVector(2);
@@ -207,13 +205,13 @@ public:
float ww = 1.f/(Hf[6]*M[i].x + Hf[7]*M[i].y + 1.f);
float dx = (Hf[0]*M[i].x + Hf[1]*M[i].y + Hf[2])*ww - m[i].x;
float dy = (Hf[3]*M[i].x + Hf[4]*M[i].y + Hf[5])*ww - m[i].y;
err[i] = (float)(dx*dx + dy*dy);
err[i] = dx*dx + dy*dy;
}
}
};
class HomographyRefineCallback : public LMSolver::Callback
class HomographyRefineCallback CV_FINAL : public LMSolver::Callback
{
public:
HomographyRefineCallback(InputArray _src, InputArray _dst)
@@ -222,7 +220,7 @@ public:
dst = _dst.getMat();
}
bool compute(InputArray _param, OutputArray _err, OutputArray _Jac) const
bool compute(InputArray _param, OutputArray _err, OutputArray _Jac) const CV_OVERRIDE
{
int i, count = src.checkVector(2);
Mat param = _param.getMat();
@@ -269,15 +267,92 @@ public:
Mat src, dst;
};
} // end namesapce cv
namespace cv{
static bool createAndRunRHORegistrator(double confidence,
int maxIters,
double ransacReprojThreshold,
int npoints,
InputArray _src,
InputArray _dst,
OutputArray _H,
OutputArray _tempMask){
Mat src = _src.getMat();
Mat dst = _dst.getMat();
Mat tempMask;
bool result;
double beta = 0.35;/* 0.35 is a value that often works. */
/* Create temporary output matrix (RHO outputs a single-precision H only). */
Mat tmpH = Mat(3, 3, CV_32FC1);
/* Create output mask. */
tempMask = Mat(npoints, 1, CV_8U);
/**
* Make use of the RHO estimator API.
*
* This is where the math happens. A homography estimation context is
* initialized, used, then finalized.
*/
Ptr<RHO_HEST> p = rhoInit();
/**
* Optional. Ideally, the context would survive across calls to
* findHomography(), but no clean way appears to exit to do so. The price
* to pay is marginally more computational work than strictly needed.
*/
rhoEnsureCapacity(p, npoints, beta);
/**
* The critical call. All parameters are heavily documented in rho.h.
*
* Currently, NR (Non-Randomness criterion) and Final Refinement (with
* internal, optimized Levenberg-Marquardt method) are enabled. However,
* while refinement seems to correctly smooth jitter most of the time, when
* refinement fails it tends to make the estimate visually very much worse.
* It may be necessary to remove the refinement flags in a future commit if
* this behaviour is too problematic.
*/
result = !!rhoHest(p,
(const float*)src.data,
(const float*)dst.data,
(char*) tempMask.data,
(unsigned) npoints,
(float) ransacReprojThreshold,
(unsigned) maxIters,
(unsigned) maxIters,
confidence,
4U,
beta,
RHO_FLAG_ENABLE_NR | RHO_FLAG_ENABLE_FINAL_REFINEMENT,
NULL,
(float*)tmpH.data);
/* Convert float homography to double precision. */
tmpH.convertTo(_H, CV_64FC1);
/* Maps non-zero mask elements to 1, for the sake of the test case. */
for(int k=0;k<npoints;k++){
tempMask.data[k] = !!tempMask.data[k];
}
tempMask.copyTo(_tempMask);
return result;
}
}
cv::Mat cv::findHomography( InputArray _points1, InputArray _points2,
int method, double ransacReprojThreshold, OutputArray _mask )
int method, double ransacReprojThreshold, OutputArray _mask,
const int maxIters, const double confidence)
{
const double confidence = 0.995;
const int maxIters = 2000;
CV_INSTRUMENT_REGION();
const double defaultRANSACReprojThreshold = 3;
bool result = false;
@@ -318,13 +393,15 @@ cv::Mat cv::findHomography( InputArray _points1, InputArray _points2,
result = createRANSACPointSetRegistrator(cb, 4, ransacReprojThreshold, confidence, maxIters)->run(src, dst, H, tempMask);
else if( method == LMEDS )
result = createLMeDSPointSetRegistrator(cb, 4, confidence, maxIters)->run(src, dst, H, tempMask);
else if( method == RHO )
result = createAndRunRHORegistrator(confidence, maxIters, ransacReprojThreshold, npoints, src, dst, H, tempMask);
else
CV_Error(Error::StsBadArg, "Unknown estimation method");
if( result && npoints > 4 )
if( result && npoints > 4 && method != RHO)
{
compressPoints( src.ptr<Point2f>(), tempMask.ptr<uchar>(), 1, npoints );
npoints = compressPoints( dst.ptr<Point2f>(), tempMask.ptr<uchar>(), 1, npoints );
compressElems( src.ptr<Point2f>(), tempMask.ptr<uchar>(), 1, npoints );
npoints = compressElems( dst.ptr<Point2f>(), tempMask.ptr<uchar>(), 1, npoints );
if( npoints > 0 )
{
Mat src1 = src.rowRange(0, npoints);
@@ -344,7 +421,13 @@ cv::Mat cv::findHomography( InputArray _points1, InputArray _points2,
tempMask.copyTo(_mask);
}
else
{
H.release();
if(_mask.needed() ) {
tempMask = Mat::zeros(npoints >= 0 ? npoints : 0, 1, CV_8U);
tempMask.copyTo(_mask);
}
}
return H;
}
@@ -369,9 +452,31 @@ cv::Mat cv::findHomography( InputArray _points1, InputArray _points2,
namespace cv
{
/**
* Compute the fundamental matrix using the 7-point algorithm.
*
* \f[
* (\mathrm{m2}_i,1)^T \mathrm{fmatrix} (\mathrm{m1}_i,1) = 0
* \f]
*
* @param _m1 Contain points in the reference view. Depth CV_32F with 2-channel
* 1 column or 1-channel 2 columns. It has 7 rows.
* @param _m2 Contain points in the other view. Depth CV_32F with 2-channel
* 1 column or 1-channel 2 columns. It has 7 rows.
* @param _fmatrix Output fundamental matrix (or matrices) of type CV_64FC1.
* The user is responsible for allocating the memory before calling
* this function.
* @return Number of fundamental matrices. Valid values are 1, 2 or 3.
* - 1, row 0 to row 2 in _fmatrix is a valid fundamental matrix
* - 2, row 3 to row 5 in _fmatrix is a valid fundamental matrix
* - 3, row 6 to row 8 in _fmatrix is a valid fundamental matrix
*
* Note that the computed fundamental matrix is normalized, i.e.,
* the last element \f$F_{33}\f$ is 1.
*/
static int run7Point( const Mat& _m1, const Mat& _m2, Mat& _fmatrix )
{
double a[7*9], w[7], u[9*9], v[9*9], c[4], r[3];
double a[7*9], w[7], u[9*9], v[9*9], c[4], r[3] = {0};
double* f1, *f2;
double t0, t1, t2;
Mat A( 7, 9, CV_64F, a );
@@ -385,12 +490,47 @@ static int run7Point( const Mat& _m1, const Mat& _m2, Mat& _fmatrix )
double* fmatrix = _fmatrix.ptr<double>();
int i, k, n;
Point2d m1c(0, 0), m2c(0, 0);
double t, scale1 = 0, scale2 = 0;
const int count = 7;
// compute centers and average distances for each of the two point sets
for( i = 0; i < count; i++ )
{
m1c += Point2d(m1[i]);
m2c += Point2d(m2[i]);
}
// calculate the normalizing transformations for each of the point sets:
// after the transformation each set will have the mass center at the coordinate origin
// and the average distance from the origin will be ~sqrt(2).
t = 1./count;
m1c *= t;
m2c *= t;
for( i = 0; i < count; i++ )
{
scale1 += norm(Point2d(m1[i].x - m1c.x, m1[i].y - m1c.y));
scale2 += norm(Point2d(m2[i].x - m2c.x, m2[i].y - m2c.y));
}
scale1 *= t;
scale2 *= t;
if( scale1 < FLT_EPSILON || scale2 < FLT_EPSILON )
return 0;
scale1 = std::sqrt(2.)/scale1;
scale2 = std::sqrt(2.)/scale2;
// form a linear system: i-th row of A(=a) represents
// the equation: (m2[i], 1)'*F*(m1[i], 1) = 0
for( i = 0; i < 7; i++ )
{
double x0 = m1[i].x, y0 = m1[i].y;
double x1 = m2[i].x, y1 = m2[i].y;
double x0 = (m1[i].x - m1c.x)*scale1;
double y0 = (m1[i].y - m1c.y)*scale1;
double x1 = (m2[i].x - m2c.x)*scale2;
double y1 = (m2[i].y - m2c.y)*scale2;
a[i*9+0] = x1*x0;
a[i*9+1] = x1*y0;
@@ -411,7 +551,7 @@ static int run7Point( const Mat& _m1, const Mat& _m2, Mat& _fmatrix )
f1 = v + 7*9;
f2 = v + 8*9;
// f1, f2 is a basis => lambda*f1 + mu*f2 is an arbitrary f. matrix.
// f1, f2 is a basis => lambda*f1 + mu*f2 is an arbitrary fundamental matrix,
// as it is determined up to a scale, normalize lambda & mu (lambda + mu = 1),
// so f ~ lambda*f1 + (1 - lambda)*f2.
// use the additional constraint det(f) = det(lambda*f1 + (1-lambda)*f2) to find lambda.
@@ -454,6 +594,10 @@ static int run7Point( const Mat& _m1, const Mat& _m2, Mat& _fmatrix )
if( n < 1 || n > 3 )
return n;
// transformation matrices
Matx33d T1( scale1, 0, -scale1*m1c.x, 0, scale1, -scale1*m1c.y, 0, 0, 1 );
Matx33d T2( scale2, 0, -scale2*m2c.x, 0, scale2, -scale2*m2c.y, 0, 0, 1 );
for( k = 0; k < n; k++, fmatrix += 9 )
{
// for each root form the fundamental matrix
@@ -472,53 +616,66 @@ static int run7Point( const Mat& _m1, const Mat& _m2, Mat& _fmatrix )
for( i = 0; i < 8; i++ )
fmatrix[i] = f1[i]*lambda + f2[i]*mu;
// de-normalize
Mat F(3, 3, CV_64F, fmatrix);
F = T2.t() * F * T1;
// make F(3,3) = 1
if(fabs(F.at<double>(8)) > FLT_EPSILON )
F *= 1. / F.at<double>(8);
}
return n;
}
/**
* Compute the fundamental matrix using the 8-point algorithm.
*
* \f[
* (\mathrm{m2}_i,1)^T \mathrm{fmatrix} (\mathrm{m1}_i,1) = 0
* \f]
*
* @param _m1 Contain points in the reference view. Depth CV_32F with 2-channel
* 1 column or 1-channel 2 columns. It has 8 rows.
* @param _m2 Contain points in the other view. Depth CV_32F with 2-channel
* 1 column or 1-channel 2 columns. It has 8 rows.
* @param _fmatrix Output fundamental matrix (or matrices) of type CV_64FC1.
* The user is responsible for allocating the memory before calling
* this function.
* @return 1 on success, 0 on failure.
*
* Note that the computed fundamental matrix is normalized, i.e.,
* the last element \f$F_{33}\f$ is 1.
*/
static int run8Point( const Mat& _m1, const Mat& _m2, Mat& _fmatrix )
{
double a[9*9], w[9], v[9*9];
Mat W( 9, 1, CV_64F, w );
Mat V( 9, 9, CV_64F, v );
Mat A( 9, 9, CV_64F, a );
Mat U, F0, TF;
Point2d m1c(0,0), m2c(0,0);
double t, scale1 = 0, scale2 = 0;
const Point2f* m1 = _m1.ptr<Point2f>();
const Point2f* m2 = _m2.ptr<Point2f>();
double* fmatrix = _fmatrix.ptr<double>();
CV_Assert( (_m1.cols == 1 || _m1.rows == 1) && _m1.size() == _m2.size());
int i, j, k, count = _m1.checkVector(2);
int i, count = _m1.checkVector(2);
// compute centers and average distances for each of the two point sets
for( i = 0; i < count; i++ )
{
double x = m1[i].x, y = m1[i].y;
m1c.x += x; m1c.y += y;
x = m2[i].x, y = m2[i].y;
m2c.x += x; m2c.y += y;
m1c += Point2d(m1[i]);
m2c += Point2d(m2[i]);
}
// calculate the normalizing transformations for each of the point sets:
// after the transformation each set will have the mass center at the coordinate origin
// and the average distance from the origin will be ~sqrt(2).
t = 1./count;
m1c.x *= t; m1c.y *= t;
m2c.x *= t; m2c.y *= t;
m1c *= t;
m2c *= t;
for( i = 0; i < count; i++ )
{
double x = m1[i].x - m1c.x, y = m1[i].y - m1c.y;
scale1 += std::sqrt(x*x + y*y);
x = m2[i].x - m2c.x, y = m2[i].y - m2c.y;
scale2 += std::sqrt(x*x + y*y);
scale1 += norm(Point2d(m1[i].x - m1c.x, m1[i].y - m1c.y));
scale2 += norm(Point2d(m2[i].x - m2c.x, m2[i].y - m2c.y));
}
scale1 *= t;
@@ -530,7 +687,7 @@ static int run8Point( const Mat& _m1, const Mat& _m2, Mat& _fmatrix )
scale1 = std::sqrt(2.)/scale1;
scale2 = std::sqrt(2.)/scale2;
A.setTo(Scalar::all(0));
Matx<double, 9, 9> A;
// form a linear system Ax=0: for each selected pair of points m1 & m2,
// the row of A(=a) represents the coefficients of equation: (m2, 1)'*F*(m1, 1) = 0
@@ -541,71 +698,65 @@ static int run8Point( const Mat& _m1, const Mat& _m2, Mat& _fmatrix )
double y1 = (m1[i].y - m1c.y)*scale1;
double x2 = (m2[i].x - m2c.x)*scale2;
double y2 = (m2[i].y - m2c.y)*scale2;
double r[9] = { x2*x1, x2*y1, x2, y2*x1, y2*y1, y2, x1, y1, 1 };
for( j = 0; j < 9; j++ )
for( k = 0; k < 9; k++ )
a[j*9+k] += r[j]*r[k];
Vec<double, 9> r( x2*x1, x2*y1, x2, y2*x1, y2*y1, y2, x1, y1, 1 );
A += r*r.t();
}
Vec<double, 9> W;
Matx<double, 9, 9> V;
eigen(A, W, V);
for( i = 0; i < 9; i++ )
{
if( fabs(w[i]) < DBL_EPSILON )
if( fabs(W[i]) < DBL_EPSILON )
break;
}
if( i < 8 )
return 0;
F0 = Mat( 3, 3, CV_64F, v + 9*8 ); // take the last column of v as a solution of Af = 0
Matx33d F0( V.val + 9*8 ); // take the last column of v as a solution of Af = 0
// make F0 singular (of rank 2) by decomposing it with SVD,
// zeroing the last diagonal element of W and then composing the matrices back.
// use v as a temporary storage for different 3x3 matrices
W = U = V = TF = F0;
W = Mat(3, 1, CV_64F, v);
U = Mat(3, 3, CV_64F, v + 9);
V = Mat(3, 3, CV_64F, v + 18);
TF = Mat(3, 3, CV_64F, v + 27);
Vec3d w;
Matx33d U;
Matx33d Vt;
SVDecomp( F0, W, U, V, SVD::MODIFY_A );
W.at<double>(2) = 0.;
SVD::compute( F0, w, U, Vt);
w[2] = 0.;
// F0 <- U*diag([W(1), W(2), 0])*V'
gemm( U, Mat::diag(W), 1., 0, 0., TF, GEMM_1_T );
gemm( TF, V, 1., 0, 0., F0, 0/*CV_GEMM_B_T*/ );
F0 = U*Matx33d::diag(w)*Vt;
// apply the transformation that is inverse
// to what we used to normalize the point coordinates
double tt1[] = { scale1, 0, -scale1*m1c.x, 0, scale1, -scale1*m1c.y, 0, 0, 1 };
double tt2[] = { scale2, 0, -scale2*m2c.x, 0, scale2, -scale2*m2c.y, 0, 0, 1 };
Mat T1(3, 3, CV_64F, tt1), T2(3, 3, CV_64F, tt2);
Matx33d T1( scale1, 0, -scale1*m1c.x, 0, scale1, -scale1*m1c.y, 0, 0, 1 );
Matx33d T2( scale2, 0, -scale2*m2c.x, 0, scale2, -scale2*m2c.y, 0, 0, 1 );
// F0 <- T2'*F0*T1
gemm( T2, F0, 1., 0, 0., TF, GEMM_1_T );
F0 = Mat(3, 3, CV_64F, fmatrix);
gemm( TF, T1, 1., 0, 0., F0, 0 );
F0 = T2.t()*F0*T1;
// make F(3,3) = 1
if( fabs(F0.at<double>(2,2)) > FLT_EPSILON )
F0 *= 1./F0.at<double>(2,2);
if( fabs(F0(2,2)) > FLT_EPSILON )
F0 *= 1./F0(2,2);
Mat(F0).copyTo(_fmatrix);
return 1;
}
class FMEstimatorCallback : public PointSetRegistrator::Callback
class FMEstimatorCallback CV_FINAL : public PointSetRegistrator::Callback
{
public:
bool checkSubset( InputArray _ms1, InputArray _ms2, int count ) const
bool checkSubset( InputArray _ms1, InputArray _ms2, int count ) const CV_OVERRIDE
{
Mat ms1 = _ms1.getMat(), ms2 = _ms2.getMat();
return !haveCollinearPoints(ms1, count) && !haveCollinearPoints(ms2, count);
}
int runKernel( InputArray _m1, InputArray _m2, OutputArray _model ) const
int runKernel( InputArray _m1, InputArray _m2, OutputArray _model ) const CV_OVERRIDE
{
double f[9*3];
Mat m1 = _m1.getMat(), m2 = _m2.getMat();
@@ -621,7 +772,7 @@ public:
return n;
}
void computeError( InputArray _m1, InputArray _m2, InputArray _model, OutputArray _err ) const
void computeError( InputArray _m1, InputArray _m2, InputArray _model, OutputArray _err ) const CV_OVERRIDE
{
Mat __m1 = _m1.getMat(), __m2 = _m2.getMat(), __model = _model.getMat();
int i, count = __m1.checkVector(2);
@@ -657,9 +808,11 @@ public:
}
cv::Mat cv::findFundamentalMat( InputArray _points1, InputArray _points2,
int method, double param1, double param2,
OutputArray _mask )
int method, double ransacReprojThreshold, double confidence,
int maxIters, OutputArray _mask )
{
CV_INSTRUMENT_REGION();
Mat points1 = _points1.getMat(), points2 = _points2.getMat();
Mat m1, m2, F;
int npoints = -1;
@@ -702,15 +855,15 @@ cv::Mat cv::findFundamentalMat( InputArray _points1, InputArray _points2,
}
else
{
if( param1 <= 0 )
param1 = 3;
if( param2 < DBL_EPSILON || param2 > 1 - DBL_EPSILON )
param2 = 0.99;
if( ransacReprojThreshold <= 0 )
ransacReprojThreshold = 3;
if( confidence < DBL_EPSILON || confidence > 1 - DBL_EPSILON )
confidence = 0.99;
if( (method & ~3) == FM_RANSAC && npoints >= 15 )
result = createRANSACPointSetRegistrator(cb, 7, param1, param2)->run(m1, m2, F, _mask);
result = createRANSACPointSetRegistrator(cb, 7, ransacReprojThreshold, confidence, maxIters)->run(m1, m2, F, _mask);
else
result = createLMeDSPointSetRegistrator(cb, 7, param2)->run(m1, m2, F, _mask);
result = createLMeDSPointSetRegistrator(cb, 7, confidence)->run(m1, m2, F, _mask);
}
if( result <= 0 )
@@ -719,17 +872,26 @@ cv::Mat cv::findFundamentalMat( InputArray _points1, InputArray _points2,
return F;
}
cv::Mat cv::findFundamentalMat( InputArray _points1, InputArray _points2,
OutputArray _mask, int method, double param1, double param2 )
cv::Mat cv::findFundamentalMat( cv::InputArray points1, cv::InputArray points2,
int method, double ransacReprojThreshold, double confidence,
cv::OutputArray mask )
{
return cv::findFundamentalMat(_points1, _points2, method, param1, param2, _mask);
return cv::findFundamentalMat(points1, points2, method, ransacReprojThreshold, confidence, 1000, mask);
}
cv::Mat cv::findFundamentalMat( cv::InputArray points1, cv::InputArray points2, cv::OutputArray mask,
int method, double ransacReprojThreshold, double confidence )
{
return cv::findFundamentalMat(points1, points2, method, ransacReprojThreshold, confidence, 1000, mask);
}
void cv::computeCorrespondEpilines( InputArray _points, int whichImage,
InputArray _Fmat, OutputArray _lines )
{
double f[9];
CV_INSTRUMENT_REGION();
double f[9] = {0};
Mat tempF(3, 3, CV_64F, f);
Mat points = _points.getMat(), F = _Fmat.getMat();
@@ -767,8 +929,8 @@ void cv::computeCorrespondEpilines( InputArray _points, int whichImage,
if( depth == CV_32S || depth == CV_32F )
{
const Point* ptsi = (const Point*)points.data;
const Point2f* ptsf = (const Point2f*)points.data;
const Point* ptsi = points.ptr<Point>();
const Point2f* ptsf = points.ptr<Point2f>();
Point3f* dstf = lines.ptr<Point3f>();
for( int i = 0; i < npoints; i++ )
{
@@ -784,7 +946,7 @@ void cv::computeCorrespondEpilines( InputArray _points, int whichImage,
}
else
{
const Point2d* ptsd = (const Point2d*)points.data;
const Point2d* ptsd = points.ptr<Point2d>();
Point3d* dstd = lines.ptr<Point3d>();
for( int i = 0; i < npoints; i++ )
{
@@ -800,8 +962,18 @@ void cv::computeCorrespondEpilines( InputArray _points, int whichImage,
}
}
static inline double scaleFor(double x){
return (std::fabs(x) > std::numeric_limits<float>::epsilon()) ? 1./x : 1.;
}
static inline float scaleFor(float x){
return (std::fabs(x) > std::numeric_limits<float>::epsilon()) ? 1.f/x : 1.f;
}
void cv::convertPointsFromHomogeneous( InputArray _src, OutputArray _dst )
{
CV_INSTRUMENT_REGION();
Mat src = _src.getMat();
if( !src.isContinuous() )
src = src.clone();
@@ -829,8 +1001,8 @@ void cv::convertPointsFromHomogeneous( InputArray _src, OutputArray _dst )
{
if( cn == 3 )
{
const Point3i* sptr = (const Point3i*)src.data;
Point2f* dptr = (Point2f*)dst.data;
const Point3i* sptr = src.ptr<Point3i>();
Point2f* dptr = dst.ptr<Point2f>();
for( i = 0; i < npoints; i++ )
{
float scale = sptr[i].z != 0 ? 1.f/sptr[i].z : 1.f;
@@ -839,8 +1011,8 @@ void cv::convertPointsFromHomogeneous( InputArray _src, OutputArray _dst )
}
else
{
const Vec4i* sptr = (const Vec4i*)src.data;
Point3f* dptr = (Point3f*)dst.data;
const Vec4i* sptr = src.ptr<Vec4i>();
Point3f* dptr = dst.ptr<Point3f>();
for( i = 0; i < npoints; i++ )
{
float scale = sptr[i][3] != 0 ? 1.f/sptr[i][3] : 1.f;
@@ -852,21 +1024,21 @@ void cv::convertPointsFromHomogeneous( InputArray _src, OutputArray _dst )
{
if( cn == 3 )
{
const Point3f* sptr = (const Point3f*)src.data;
Point2f* dptr = (Point2f*)dst.data;
const Point3f* sptr = src.ptr<Point3f>();
Point2f* dptr = dst.ptr<Point2f>();
for( i = 0; i < npoints; i++ )
{
float scale = sptr[i].z != 0.f ? 1.f/sptr[i].z : 1.f;
float scale = scaleFor(sptr[i].z);
dptr[i] = Point2f(sptr[i].x*scale, sptr[i].y*scale);
}
}
else
{
const Vec4f* sptr = (const Vec4f*)src.data;
Point3f* dptr = (Point3f*)dst.data;
const Vec4f* sptr = src.ptr<Vec4f>();
Point3f* dptr = dst.ptr<Point3f>();
for( i = 0; i < npoints; i++ )
{
float scale = sptr[i][3] != 0.f ? 1.f/sptr[i][3] : 1.f;
float scale = scaleFor(sptr[i][3]);
dptr[i] = Point3f(sptr[i][0]*scale, sptr[i][1]*scale, sptr[i][2]*scale);
}
}
@@ -875,21 +1047,21 @@ void cv::convertPointsFromHomogeneous( InputArray _src, OutputArray _dst )
{
if( cn == 3 )
{
const Point3d* sptr = (const Point3d*)src.data;
Point2d* dptr = (Point2d*)dst.data;
const Point3d* sptr = src.ptr<Point3d>();
Point2d* dptr = dst.ptr<Point2d>();
for( i = 0; i < npoints; i++ )
{
double scale = sptr[i].z != 0. ? 1./sptr[i].z : 1.;
double scale = scaleFor(sptr[i].z);
dptr[i] = Point2d(sptr[i].x*scale, sptr[i].y*scale);
}
}
else
{
const Vec4d* sptr = (const Vec4d*)src.data;
Point3d* dptr = (Point3d*)dst.data;
const Vec4d* sptr = src.ptr<Vec4d>();
Point3d* dptr = dst.ptr<Point3d>();
for( i = 0; i < npoints; i++ )
{
double scale = sptr[i][3] != 0.f ? 1./sptr[i][3] : 1.;
double scale = scaleFor(sptr[i][3]);
dptr[i] = Point3d(sptr[i][0]*scale, sptr[i][1]*scale, sptr[i][2]*scale);
}
}
@@ -901,6 +1073,8 @@ void cv::convertPointsFromHomogeneous( InputArray _src, OutputArray _dst )
void cv::convertPointsToHomogeneous( InputArray _src, OutputArray _dst )
{
CV_INSTRUMENT_REGION();
Mat src = _src.getMat();
if( !src.isContinuous() )
src = src.clone();
@@ -913,7 +1087,7 @@ void cv::convertPointsToHomogeneous( InputArray _src, OutputArray _dst )
}
CV_Assert( npoints >= 0 && (depth == CV_32S || depth == CV_32F || depth == CV_64F));
int dtype = CV_MAKETYPE(depth <= CV_32F ? CV_32F : CV_64F, cn+1);
int dtype = CV_MAKETYPE(depth, cn+1);
_dst.create(npoints, 1, dtype);
Mat dst = _dst.getMat();
if( !dst.isContinuous() )
@@ -928,15 +1102,15 @@ void cv::convertPointsToHomogeneous( InputArray _src, OutputArray _dst )
{
if( cn == 2 )
{
const Point2i* sptr = (const Point2i*)src.data;
Point3i* dptr = (Point3i*)dst.data;
const Point2i* sptr = src.ptr<Point2i>();
Point3i* dptr = dst.ptr<Point3i>();
for( i = 0; i < npoints; i++ )
dptr[i] = Point3i(sptr[i].x, sptr[i].y, 1);
}
else
{
const Point3i* sptr = (const Point3i*)src.data;
Vec4i* dptr = (Vec4i*)dst.data;
const Point3i* sptr = src.ptr<Point3i>();
Vec4i* dptr = dst.ptr<Vec4i>();
for( i = 0; i < npoints; i++ )
dptr[i] = Vec4i(sptr[i].x, sptr[i].y, sptr[i].z, 1);
}
@@ -945,15 +1119,15 @@ void cv::convertPointsToHomogeneous( InputArray _src, OutputArray _dst )
{
if( cn == 2 )
{
const Point2f* sptr = (const Point2f*)src.data;
Point3f* dptr = (Point3f*)dst.data;
const Point2f* sptr = src.ptr<Point2f>();
Point3f* dptr = dst.ptr<Point3f>();
for( i = 0; i < npoints; i++ )
dptr[i] = Point3f(sptr[i].x, sptr[i].y, 1.f);
}
else
{
const Point3f* sptr = (const Point3f*)src.data;
Vec4f* dptr = (Vec4f*)dst.data;
const Point3f* sptr = src.ptr<Point3f>();
Vec4f* dptr = dst.ptr<Vec4f>();
for( i = 0; i < npoints; i++ )
dptr[i] = Vec4f(sptr[i].x, sptr[i].y, sptr[i].z, 1.f);
}
@@ -962,15 +1136,15 @@ void cv::convertPointsToHomogeneous( InputArray _src, OutputArray _dst )
{
if( cn == 2 )
{
const Point2d* sptr = (const Point2d*)src.data;
Point3d* dptr = (Point3d*)dst.data;
const Point2d* sptr = src.ptr<Point2d>();
Point3d* dptr = dst.ptr<Point3d>();
for( i = 0; i < npoints; i++ )
dptr[i] = Point3d(sptr[i].x, sptr[i].y, 1.);
}
else
{
const Point3d* sptr = (const Point3d*)src.data;
Vec4d* dptr = (Vec4d*)dst.data;
const Point3d* sptr = src.ptr<Point3d>();
Vec4d* dptr = dst.ptr<Vec4d>();
for( i = 0; i < npoints; i++ )
dptr[i] = Vec4d(sptr[i].x, sptr[i].y, sptr[i].z, 1.);
}
@@ -982,6 +1156,8 @@ void cv::convertPointsToHomogeneous( InputArray _src, OutputArray _dst )
void cv::convertPointsHomogeneous( InputArray _src, OutputArray _dst )
{
CV_INSTRUMENT_REGION();
int stype = _src.type(), dtype = _dst.type();
CV_Assert( _dst.fixedType() );
@@ -991,4 +1167,27 @@ void cv::convertPointsHomogeneous( InputArray _src, OutputArray _dst )
convertPointsToHomogeneous(_src, _dst);
}
double cv::sampsonDistance(InputArray _pt1, InputArray _pt2, InputArray _F)
{
CV_INSTRUMENT_REGION();
CV_Assert(_pt1.type() == CV_64F && _pt2.type() == CV_64F && _F.type() == CV_64F);
CV_DbgAssert(_pt1.rows() == 3 && _F.size() == Size(3, 3) && _pt1.rows() == _pt2.rows());
Mat pt1(_pt1.getMat());
Mat pt2(_pt2.getMat());
Mat F(_F.getMat());
Vec3d F_pt1 = *F.ptr<Matx33d>() * *pt1.ptr<Vec3d>();
Vec3d Ft_pt2 = F.ptr<Matx33d>()->t() * *pt2.ptr<Vec3d>();
double v = pt2.ptr<Vec3d>()->dot(F_pt1);
// square
Ft_pt2 = Ft_pt2.mul(Ft_pt2);
F_pt1 = F_pt1.mul(F_pt1);
return v*v / (F_pt1[0] + F_pt1[1] + Ft_pt2[0] + Ft_pt2[1]);
}
/* End of file. */
+134 -51
View File
@@ -1,50 +1,51 @@
/*M///////////////////////////////////////////////////////////////////////////////////////
//
// This is a homography decomposition implementation contributed to OpenCV
// by Samson Yilma. It implements the homography decomposition algorithm
// descriped in the research report:
// Malis, E and Vargas, M, "Deeper understanding of the homography decomposition
// for vision-based control", Research Report 6303, INRIA (2007)
//
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
// By downloading, copying, installing or using the software you agree to this license.
// If you do not agree to this license, do not download, install,
// copy or use the software.
//
//
// License Agreement
// For Open Source Computer Vision Library
//
// Copyright (C) 2014, Samson Yilma¸ (samson_yilma@yahoo.com), all rights reserved.
//
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistribution's of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistribution's 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.
//
// * The name of the copyright holders may not 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 Intel Corporation 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.
//
//M*/
//
// This is a homography decomposition implementation contributed to OpenCV
// by Samson Yilma. It implements the homography decomposition algorithm
// described in the research report:
// Malis, E and Vargas, M, "Deeper understanding of the homography decomposition
// for vision-based control", Research Report 6303, INRIA (2007)
//
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
// By downloading, copying, installing or using the software you agree to this license.
// If you do not agree to this license, do not download, install,
// copy or use the software.
//
//
// License Agreement
// For Open Source Computer Vision Library
//
// Copyright (C) 2014, Samson Yilma (samson_yilma@yahoo.com), all rights reserved.
// Copyright (C) 2018, Intel Corporation, all rights reserved.
//
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistribution's of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistribution's 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.
//
// * The name of the copyright holders may not 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 Intel Corporation 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.
//
//M*/
#include "precomp.hpp"
#include <memory>
@@ -84,30 +85,40 @@ protected:
}
private:
/**
* Normalize the homograhpy \f$H\f$.
*
* @param H Homography matrix.
* @param K Intrinsic parameter matrix.
* @return It returns
* \f[
* K^{-1} * H * K
* \f]
*/
cv::Matx33d normalize(const cv::Matx33d& H, const cv::Matx33d& K);
void removeScale();
cv::Matx33d _Hnorm;
};
class HomographyDecompZhang : public HomographyDecomp {
class HomographyDecompZhang CV_FINAL : public HomographyDecomp {
public:
HomographyDecompZhang():HomographyDecomp() {}
virtual ~HomographyDecompZhang() {}
private:
virtual void decompose(std::vector<CameraMotion>& camMotions);
virtual void decompose(std::vector<CameraMotion>& camMotions) CV_OVERRIDE;
bool findMotionFrom_tstar_n(const cv::Vec3d& tstar, const cv::Vec3d& n, CameraMotion& motion);
};
class HomographyDecompInria : public HomographyDecomp {
class HomographyDecompInria CV_FINAL : public HomographyDecomp {
public:
HomographyDecompInria():HomographyDecomp() {}
virtual ~HomographyDecompInria() {}
private:
virtual void decompose(std::vector<CameraMotion>& camMotions);
virtual void decompose(std::vector<CameraMotion>& camMotions) CV_OVERRIDE;
double oppositeOfMinor(const cv::Matx33d& M, const int row, const int col);
void findRmatFrom_tstar_n(const cv::Vec3d& tstar, const cv::Vec3d& n, const double v, cv::Matx33d& R);
};
@@ -174,6 +185,10 @@ bool HomographyDecompZhang::findMotionFrom_tstar_n(const cv::Vec3d& tstar, const
temp(1, 1) += 1.0;
temp(2, 2) += 1.0;
motion.R = getHnorm() * temp.inv();
if (cv::determinant(motion.R) < 0)
{
motion.R *= -1;
}
motion.t = motion.R * tstar;
motion.n = n;
return passesSameSideOfPlaneConstraint(motion);
@@ -183,6 +198,7 @@ void HomographyDecompZhang::decompose(std::vector<CameraMotion>& camMotions)
{
Mat W, U, Vt;
SVD::compute(getHnorm(), W, U, Vt);
CV_Assert(W.total() > 2 && Vt.total() > 7);
double lambda1=W.at<double>(0);
double lambda3=W.at<double>(2);
double lambda1m3 = (lambda1-lambda3);
@@ -300,6 +316,10 @@ void HomographyDecompInria::findRmatFrom_tstar_n(const cv::Vec3d& tstar, const c
0.0, 0.0, 1.0);
R = getHnorm() * (I - (2/v) * tstar_m * n_m.t() );
if (cv::determinant(R) < 0)
{
R *= -1;
}
}
void HomographyDecompInria::decompose(std::vector<CameraMotion>& camMotions)
@@ -447,7 +467,7 @@ int decomposeHomographyMat(InputArray _H,
Mat K = _K.getMat().reshape(1, 3);
CV_Assert(K.cols == 3 && K.rows == 3);
auto_ptr<HomographyDecomp> hdecomp(new HomographyDecompInria);
cv::Ptr<HomographyDecomp> hdecomp(new HomographyDecompInria);
vector<CameraMotion> motions;
hdecomp->decomposeHomography(H, K, motions);
@@ -479,4 +499,67 @@ int decomposeHomographyMat(InputArray _H,
return nsols;
}
void filterHomographyDecompByVisibleRefpoints(InputArrayOfArrays _rotations,
InputArrayOfArrays _normals,
InputArray _beforeRectifiedPoints,
InputArray _afterRectifiedPoints,
OutputArray _possibleSolutions,
InputArray _pointsMask)
{
CV_Assert(_beforeRectifiedPoints.type() == CV_32FC2 && _afterRectifiedPoints.type() == CV_32FC2);
CV_Assert(_pointsMask.empty() || _pointsMask.type() == CV_8U);
Mat beforeRectifiedPoints = _beforeRectifiedPoints.getMat();
Mat afterRectifiedPoints = _afterRectifiedPoints.getMat();
Mat pointsMask = _pointsMask.getMat();
int nsolutions = (int)_rotations.total();
int npoints = (int)beforeRectifiedPoints.total();
CV_Assert(pointsMask.empty() || pointsMask.checkVector(1, CV_8U) == npoints);
const uchar* pointsMaskPtr = pointsMask.data;
std::vector<uchar> solutionMask(nsolutions, (uchar)1);
std::vector<Mat> normals(nsolutions);
std::vector<Mat> rotnorm(nsolutions);
Mat R;
for( int i = 0; i < nsolutions; i++ )
{
_normals.getMat(i).convertTo(normals[i], CV_64F);
CV_Assert(normals[i].total() == 3);
_rotations.getMat(i).convertTo(R, CV_64F);
rotnorm[i] = R*normals[i];
CV_Assert(rotnorm[i].total() == 3);
}
for( int j = 0; j < npoints; j++ )
{
if( !pointsMaskPtr || pointsMaskPtr[j] )
{
Point2f prevPoint = beforeRectifiedPoints.at<Point2f>(j);
Point2f currPoint = afterRectifiedPoints.at<Point2f>(j);
for( int i = 0; i < nsolutions; i++ )
{
if( !solutionMask[i] )
continue;
const double* normal_i = normals[i].ptr<double>();
const double* rotnorm_i = rotnorm[i].ptr<double>();
double prevNormDot = normal_i[0]*prevPoint.x + normal_i[1]*prevPoint.y + normal_i[2];
double currNormDot = rotnorm_i[0]*currPoint.x + rotnorm_i[1]*currPoint.y + rotnorm_i[2];
if (prevNormDot <= 0 || currNormDot <= 0)
solutionMask[i] = (uchar)0;
}
}
}
std::vector<int> possibleSolutions;
for( int i = 0; i < nsolutions; i++ )
if( solutionMask[i] )
possibleSolutions.push_back(i);
Mat(possibleSolutions).copyTo(_possibleSolutions);
}
} //namespace cv
File diff suppressed because it is too large Load Diff
+259
View File
@@ -0,0 +1,259 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html
// This file is based on file issued with the following license:
/*============================================================================
Copyright 2017 Toby Collins
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. 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.
3. Neither the name of the copyright holder 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 HOLDER 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.
*/
#ifndef OPENCV_CALIB3D_IPPE_HPP
#define OPENCV_CALIB3D_IPPE_HPP
#include <opencv2/core.hpp>
namespace cv {
namespace IPPE {
class PoseSolver {
public:
/**
* @brief PoseSolver constructor
*/
PoseSolver();
/**
* @brief Finds the two possible poses of a planar object given a set of correspondences and their respective reprojection errors.
* The poses are sorted with the first having the lowest reprojection error.
* @param objectPoints Array of 4 or more coplanar object points defined in object coordinates.
* 1xN/Nx1 3-channel (float or double) where N is the number of points
* @param imagePoints Array of corresponding image points, 1xN/Nx1 2-channel. Points are in normalized pixel coordinates.
* @param rvec1 First rotation solution (3x1 rotation vector)
* @param tvec1 First translation solution (3x1 vector)
* @param reprojErr1 Reprojection error of first solution
* @param rvec2 Second rotation solution (3x1 rotation vector)
* @param tvec2 Second translation solution (3x1 vector)
* @param reprojErr2 Reprojection error of second solution
*/
void solveGeneric(InputArray objectPoints, InputArray imagePoints, OutputArray rvec1, OutputArray tvec1,
float& reprojErr1, OutputArray rvec2, OutputArray tvec2, float& reprojErr2);
/**
* @brief Finds the two possible poses of a square planar object and their respective reprojection errors using IPPE.
* The poses are sorted so that the first one is the one with the lowest reprojection error.
*
* @param objectPoints Array of 4 coplanar object points defined in the following object coordinates:
* - point 0: [-squareLength / 2.0, squareLength / 2.0, 0]
* - point 1: [squareLength / 2.0, squareLength / 2.0, 0]
* - point 2: [squareLength / 2.0, -squareLength / 2.0, 0]
* - point 3: [-squareLength / 2.0, -squareLength / 2.0, 0]
* 1xN/Nx1 3-channel (float or double) where N is the number of points
* @param imagePoints Array of corresponding image points, 1xN/Nx1 2-channel. Points are in normalized pixel coordinates.
* @param rvec1 First rotation solution (3x1 rotation vector)
* @param tvec1 First translation solution (3x1 vector)
* @param reprojErr1 Reprojection error of first solution
* @param rvec2 Second rotation solution (3x1 rotation vector)
* @param tvec2 Second translation solution (3x1 vector)
* @param reprojErr2 Reprojection error of second solution
*/
void solveSquare(InputArray objectPoints, InputArray imagePoints, OutputArray rvec1, OutputArray tvec1,
float& reprojErr1, OutputArray rvec2, OutputArray tvec2, float& reprojErr2);
private:
/**
* @brief Finds the two possible poses of a planar object given a set of correspondences in normalized pixel coordinates.
* These poses are **NOT** sorted on reprojection error. Note that the returned poses are object-to-camera transforms, and not camera-to-object transforms.
* @param objectPoints Array of 4 or more coplanar object points defined in object coordinates. 1xN/Nx1 3-channel (float or double).
* @param normalizedImagePoints Array of corresponding image points in normalized pixel coordinates, 1xN/Nx1 2-channel (float or double).
* @param Ma First pose solution (unsorted)
* @param Mb Second pose solution (unsorted)
*/
void solveGeneric(InputArray objectPoints, InputArray normalizedImagePoints, OutputArray Ma, OutputArray Mb);
/**
* @brief Finds the two possible poses of a planar object in its canonical position, given a set of correspondences in normalized pixel coordinates.
* These poses are **NOT** sorted on reprojection error. Note that the returned poses are object-to-camera transforms, and not camera-to-object transforms.
* @param canonicalObjPoints Array of 4 or more coplanar object points defined in object coordinates. 1xN/Nx1 3-channel (double) where N is the number of points
* @param normalizedInputPoints Array of corresponding image points in normalized pixel coordinates, 1xN/Nx1 2-channel (double) where N is the number of points
* @param H Homography mapping canonicalObjPoints to normalizedInputPoints.
* @param Ma
* @param Mb
*/
void solveCanonicalForm(InputArray canonicalObjPoints, InputArray normalizedInputPoints, const Matx33d& H,
OutputArray Ma, OutputArray Mb);
/**
* @brief Computes the translation solution for a given rotation solution
* @param objectPoints Array of corresponding object points, 1xN/Nx1 3-channel where N is the number of points
* @param normalizedImagePoints Array of corresponding image points (undistorted), 1xN/Nx1 2-channel where N is the number of points
* @param R Rotation solution (3x1 rotation vector)
* @param t Translation solution (3x1 rotation vector)
*/
void computeTranslation(InputArray objectPoints, InputArray normalizedImgPoints, InputArray R, OutputArray t);
/**
* @brief Computes the two rotation solutions from the Jacobian of a homography matrix H at a point (ux,uy) on the object plane.
* For highest accuracy the Jacobian should be computed at the centroid of the point correspondences (see the IPPE paper for the explanation of this).
* For a point (ux,uy) on the object plane, suppose the homography H maps (ux,uy) to a point (p,q) in the image (in normalized pixel coordinates).
* The Jacobian matrix [J00, J01; J10,J11] is the Jacobian of the mapping evaluated at (ux,uy).
* @param j00 Homography jacobian coefficient at (ux,uy)
* @param j01 Homography jacobian coefficient at (ux,uy)
* @param j10 Homography jacobian coefficient at (ux,uy)
* @param j11 Homography jacobian coefficient at (ux,uy)
* @param p The x coordinate of point (ux,uy) mapped into the image (undistorted and normalized position)
* @param q The y coordinate of point (ux,uy) mapped into the image (undistorted and normalized position)
*/
void computeRotations(double j00, double j01, double j10, double j11, double p, double q, OutputArray _R1, OutputArray _R2);
/**
* @brief Closed-form solution for the homography mapping with four corner correspondences of a square (it maps source points to target points).
* The source points are the four corners of a zero-centred squared defined by:
* - point 0: [-squareLength / 2.0, squareLength / 2.0]
* - point 1: [squareLength / 2.0, squareLength / 2.0]
* - point 2: [squareLength / 2.0, -squareLength / 2.0]
* - point 3: [-squareLength / 2.0, -squareLength / 2.0]
*
* @param targetPoints Array of four corresponding target points, 1x4/4x1 2-channel. Note that the points should be ordered to correspond with points 0, 1, 2 and 3.
* @param halfLength The square's half length (i.e. squareLength/2.0)
* @param H Homograhy mapping the source points to the target points, 3x3 single channel
*/
void homographyFromSquarePoints(InputArray targetPoints, double halfLength, OutputArray H);
/**
* @brief Fast conversion from a rotation matrix to a rotation vector using Rodrigues' formula
* @param R Input rotation matrix, 3x3 1-channel (double)
* @param r Output rotation vector, 3x1/1x3 1-channel (double)
*/
void rot2vec(InputArray R, OutputArray r);
/**
* @brief Takes a set of planar object points and transforms them to 'canonical' object coordinates This is when they have zero mean and are on the plane z=0
* @param objectPoints Array of 4 or more coplanar object points defined in object coordinates. 1xN/Nx1 3-channel (float or double) where N is the number of points
* @param canonicalObjectPoints Object points in canonical coordinates 1xN/Nx1 2-channel (double)
* @param MobjectPoints2Canonical Transform matrix mapping _objectPoints to _canonicalObjectPoints: 4x4 1-channel (double)
*/
void makeCanonicalObjectPoints(InputArray objectPoints, OutputArray canonicalObjectPoints, OutputArray MobjectPoints2Canonical);
/**
* @brief Evaluates the Root Mean Squared (RMS) reprojection error of a pose solution.
* @param objectPoints Array of 4 or more coplanar object points defined in object coordinates. 1xN/Nx1 3-channel (float or double) where N is the number of points
* @param imagePoints Array of corresponding image points, 1xN/Nx1 2-channel. This can either be in pixel coordinates or normalized pixel coordinates.
* @param M Pose matrix from 3D object to camera coordinates: 4x4 1-channel (double)
* @param err RMS reprojection error
*/
void evalReprojError(InputArray objectPoints, InputArray imagePoints, InputArray M, float& err);
/**
* @brief Sorts two pose solutions according to their RMS reprojection error (lowest first).
* @param objectPoints Array of 4 or more coplanar object points defined in object coordinates. 1xN/Nx1 3-channel (float or double) where N is the number of points
* @param imagePoints Array of corresponding image points, 1xN/Nx1 2-channel. This can either be in pixel coordinates or normalized pixel coordinates.
* @param Ma Pose matrix 1: 4x4 1-channel
* @param Mb Pose matrix 2: 4x4 1-channel
* @param M1 Member of (Ma,Mb} with lowest RMS reprojection error. Performs deep copy.
* @param M2 Member of (Ma,Mb} with highest RMS reprojection error. Performs deep copy.
* @param err1 RMS reprojection error of _M1
* @param err2 RMS reprojection error of _M2
*/
void sortPosesByReprojError(InputArray objectPoints, InputArray imagePoints, InputArray Ma, InputArray Mb, OutputArray M1, OutputArray M2, float& err1, float& err2);
/**
* @brief Finds the rotation _Ra that rotates a vector _a to the z axis (0,0,1)
* @param a vector: 3x1 mat (double)
* @param Ra Rotation: 3x3 mat (double)
*/
void rotateVec2ZAxis(const Matx31d& a, Matx33d& Ra);
/**
* @brief Computes the rotation _R that rotates the object points to the plane z=0. This uses the cross-product method with the first three object points.
* @param objectPoints Array of N>=3 coplanar object points defined in object coordinates. 1xN/Nx1 3-channel (float or double) where N is the number of points
* @param R Rotation Mat: 3x3 (double)
* @return Success (true) or failure (false)
*/
bool computeObjextSpaceR3Pts(InputArray objectPoints, Matx33d& R);
/**
* @brief computeObjextSpaceRSvD Computes the rotation _R that rotates the object points to the plane z=0. This uses the cross-product method with the first three object points.
* @param objectPointsZeroMean Zero-meaned coplanar object points: 3xN matrix (double) where N>=3
* @param R Rotation Mat: 3x3 (double)
*/
void computeObjextSpaceRSvD(InputArray objectPointsZeroMean, OutputArray R);
/**
* @brief Generates the 4 object points of a square planar object
* @param squareLength The square's length (which is also it's width) in object coordinate units (e.g. millimeters, meters, etc.)
* @param objectPoints Set of 4 object points (1x4 3-channel double)
*/
void generateSquareObjectCorners3D(double squareLength, OutputArray objectPoints);
/**
* @brief Generates the 4 object points of a square planar object, without including the z-component (which is z=0 for all points).
* @param squareLength The square's length (which is also it's width) in object coordinate units (e.g. millimeters, meters, etc.)
* @param objectPoints Set of 4 object points (1x4 2-channel double)
*/
void generateSquareObjectCorners2D(double squareLength, OutputArray objectPoints);
/**
* @brief Computes the average depth of an object given its pose in camera coordinates
* @param objectPoints: Object points defined in 3D object space
* @param rvec: Rotation component of pose
* @param tvec: Translation component of pose
* @return: average depth of the object
*/
double meanSceneDepth(InputArray objectPoints, InputArray rvec, InputArray tvec);
//! a small constant used to test 'small' values close to zero.
double IPPE_SMALL;
};
} //namespace IPPE
namespace HomographyHO {
/**
* @brief Computes the best-fitting homography matrix from source to target points using Harker and O'Leary's method:
* Harker, M., O'Leary, P., Computation of Homographies, Proceedings of the British Machine Vision Conference 2005, Oxford, England.
* This is not the author's implementation.
* @param srcPoints Array of source points: 1xN/Nx1 2-channel (float or double) where N is the number of points
* @param targPoints Array of target points: 1xN/Nx1 2-channel (float or double)
* @param H Homography from source to target: 3x3 1-channel (double)
*/
void homographyHO(InputArray srcPoints, InputArray targPoints, Matx33d& H);
/**
* @brief Performs data normalization before homography estimation. For details see Hartley, R., Zisserman, A., Multiple View Geometry in Computer Vision,
* Cambridge University Press, Cambridge, 2001
* @param Data Array of source data points: 1xN/Nx1 2-channel (float or double) where N is the number of points
* @param DataN Normalized data points: 1xN/Nx1 2-channel (float or double) where N is the number of points
* @param T Homogeneous transform from source to normalized: 3x3 1-channel (double)
* @param Ti Homogeneous transform from normalized to source: 3x3 1-channel (double)
*/
void normalizeDataIsotropic(InputArray Data, OutputArray DataN, OutputArray T, OutputArray Ti);
}
} //namespace cv
#endif
+11 -18
View File
@@ -44,7 +44,7 @@
#include <stdio.h>
/*
This is translation to C++ of the Matlab's LMSolve package by Miroslav Balda.
This is a translation to C++ from the Matlab's LMSolve package by Miroslav Balda.
Here is the original copyright:
============================================================================
@@ -77,19 +77,16 @@
namespace cv
{
class LMSolverImpl : public LMSolver
class LMSolverImpl CV_FINAL : public LMSolver
{
public:
LMSolverImpl() : maxIters(100) { init(); }
LMSolverImpl(const Ptr<LMSolver::Callback>& _cb, int _maxIters) : cb(_cb), maxIters(_maxIters) { init(); }
void init()
LMSolverImpl(const Ptr<LMSolver::Callback>& _cb, int _maxIters, double _eps = FLT_EPSILON)
: cb(_cb), epsx(_eps), epsf(_eps), maxIters(_maxIters)
{
epsx = epsf = FLT_EPSILON;
printInterval = 0;
}
int run(InputOutputArray _param0) const
int run(InputOutputArray _param0) const CV_OVERRIDE
{
Mat param0 = _param0.getMat(), x, xd, r, rd, J, A, Ap, v, temp_d, d;
int ptype = param0.type();
@@ -198,9 +195,7 @@ public:
return iter;
}
void setCallback(const Ptr<LMSolver::Callback>& _cb) { cb = _cb; }
AlgorithmInfo* info() const;
void setCallback(const Ptr<LMSolver::Callback>& _cb) CV_OVERRIDE { cb = _cb; }
Ptr<LMSolver::Callback> cb;
@@ -211,16 +206,14 @@ public:
};
CV_INIT_ALGORITHM(LMSolverImpl, "LMSolver",
obj.info()->addParam(obj, "epsx", obj.epsx);
obj.info()->addParam(obj, "epsf", obj.epsf);
obj.info()->addParam(obj, "maxIters", obj.maxIters);
obj.info()->addParam(obj, "printInterval", obj.printInterval))
Ptr<LMSolver> createLMSolver(const Ptr<LMSolver::Callback>& cb, int maxIters)
{
CV_Assert( !LMSolverImpl_info_auto.name().empty() );
return makePtr<LMSolverImpl>(cb, maxIters);
}
Ptr<LMSolver> createLMSolver(const Ptr<LMSolver::Callback>& cb, int maxIters, double eps)
{
return makePtr<LMSolverImpl>(cb, maxIters, eps);
}
}
@@ -7,11 +7,12 @@
// copy or use the software.
//
//
// License Agreement
// License Agreement
// For Open Source Computer Vision Library
//
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
// Copyright (C) 2015, Itseez Inc., all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
@@ -40,15 +41,12 @@
//
//M*/
//
// Library initialization file
//
#include "precomp.hpp"
#include "opencv2/video.hpp"
namespace cv
{
IPP_INITIALIZER_AUTO
bool initModule_video(void)
{
return true;
}
}
/* End of file. */
+151 -116
View File
@@ -44,217 +44,251 @@
////////////////////////////////////////// stereoBM //////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////
#ifdef csize
#define MAX_VAL 32767
void calcDisp(__local short * cost, __global short * disp, int uniquenessRatio, int mindisp, int ndisp, int w,
__local int * bestDisp, __local int * bestCost, int d, int x, int y, int cols, int rows, int wsz2)
#ifndef WSZ
#define WSZ 2
#endif
#define WSZ2 (WSZ / 2)
#ifdef DEFINE_KERNEL_STEREOBM
#define DISPARITY_SHIFT 4
#define FILTERED ((MIN_DISP - 1) << DISPARITY_SHIFT)
void calcDisp(__local short * cost, __global short * disp, int uniquenessRatio,
__local int * bestDisp, __local int * bestCost, int d, int x, int y, int cols, int rows)
{
short FILTERED = (mindisp - 1)<<4;
int best_disp = *bestDisp, best_cost = *bestCost, best_disp_back = ndisp - best_disp - 1;
int best_disp = *bestDisp, best_cost = *bestCost;
barrier(CLK_LOCAL_MEM_FENCE);
short c = cost[0];
int thresh = best_cost + (best_cost * uniquenessRatio / 100);
bool notUniq = ( (c <= thresh) && (d < (best_disp - 1) || d > (best_disp + 1) ) );
int thresh = best_cost + (best_cost * uniquenessRatio/100);
bool notUniq = ( (c <= thresh) && (d < (best_disp_back - 1) || d > (best_disp_back + 1) ) );
if(notUniq)
if (notUniq)
*bestCost = FILTERED;
barrier(CLK_LOCAL_MEM_FENCE);
if( *bestCost != FILTERED && x < cols-wsz2-mindisp && y < rows-wsz2 && d == best_disp_back)
if( *bestCost != FILTERED && x < cols - WSZ2 - MIN_DISP && y < rows - WSZ2 && d == best_disp)
{
int y3 = (best_disp_back > 0) ? cost[-w] : cost[w],
y2 = c,
y1 = (best_disp_back < ndisp-1) ? cost[w] : cost[-w];
int d_aprox = y3+y1-2*y2 + abs(y3-y1);
disp[0] = (short)(((best_disp_back + mindisp)*256 + (d_aprox != 0 ? (y3-y1)*256/d_aprox : 0) + 15) >> 4);
int d_aprox = 0;
int yp =0, yn = 0;
if ((0 < best_disp) && (best_disp < NUM_DISP - 1))
{
yp = cost[-2 * BLOCK_SIZE_Y];
yn = cost[2 * BLOCK_SIZE_Y];
d_aprox = yp + yn - 2 * c + abs(yp - yn);
}
disp[0] = (short)(((best_disp + MIN_DISP)*256 + (d_aprox != 0 ? (yp - yn) * 256 / d_aprox : 0) + 15) >> 4);
}
}
int calcLocalIdx(int x, int y, int d, int w)
{
return d*2*w + (w - 1 - y + x);
}
void calcNewCoordinates(int * x, int * y, int nthread)
{
int oldX = *x - (1-nthread), oldY = *y;
*x = (oldX == oldY) ? (0*nthread + (oldX + 2)*(1-nthread) ) : (oldX+1)*(1-nthread) + (oldX+1)*nthread;
*y = (oldX == oldY) ? (0*(1-nthread) + (oldY + 1)*nthread) : oldY + 1*(1-nthread);
}
short calcCostBorder(__global const uchar * leftptr, __global const uchar * rightptr, int x, int y, int nthread,
int wsz2, short * costbuf, int * h, int cols, int d, short cost, int winsize)
short * costbuf, int *h, int cols, int d, short cost)
{
int head = (*h)%wsz;
int head = (*h) % WSZ;
__global const uchar * left, * right;
int idx = mad24(y+wsz2*(2*nthread-1), cols, x+wsz2*(1-2*nthread));
int idx = mad24(y + WSZ2 * (2 * nthread - 1), cols, x + WSZ2 * (1 - 2 * nthread));
left = leftptr + idx;
right = rightptr + (idx - d);
int shift = 1*nthread + cols*(1-nthread);
short costdiff = 0;
for(int i = 0; i < winsize; i++)
if (0 == nthread)
{
costdiff += abs( left[0] - right[0] );
left += shift;
right += shift;
#pragma unroll
for (int i = 0; i < WSZ; i++)
{
costdiff += abs( left[0] - right[0] );
left += cols;
right += cols;
}
}
else // (1 == nthread)
{
#pragma unroll
for (int i = 0; i < WSZ; i++)
{
costdiff += abs(left[i] - right[i]);
}
}
cost += costdiff - costbuf[head];
costbuf[head] = costdiff;
(*h) = (*h)%wsz + 1;
*h = head + 1;
return cost;
}
short calcCostInside(__global const uchar * leftptr, __global const uchar * rightptr, int x, int y,
int wsz2, int cols, int d, short cost_up_left, short cost_up, short cost_left,
int winsize)
int cols, int d, short cost_up_left, short cost_up, short cost_left)
{
__global const uchar * left, * right;
int idx = mad24(y-wsz2-1, cols, x-wsz2-1);
int idx = mad24(y - WSZ2 - 1, cols, x - WSZ2 - 1);
left = leftptr + idx;
right = rightptr + (idx - d);
int idx2 = winsize*cols;
int idx2 = WSZ*cols;
uchar corrner1 = abs(left[0] - right[0]),
corrner2 = abs(left[winsize] - right[winsize]),
corrner2 = abs(left[WSZ] - right[WSZ]),
corrner3 = abs(left[idx2] - right[idx2]),
corrner4 = abs(left[idx2 + winsize] - right[idx2 + winsize]);
corrner4 = abs(left[idx2 + WSZ] - right[idx2 + WSZ]);
return cost_up + cost_left - cost_up_left + corrner1 -
corrner2 - corrner3 + corrner4;
}
__kernel void stereoBM(__global const uchar * leftptr, __global const uchar * rightptr, __global uchar * dispptr,
int disp_step, int disp_offset, int rows, int cols, int mindisp, int ndisp,
int preFilterCap, int textureTreshold, int uniquenessRatio, int sizeX, int sizeY, int winsize)
__kernel void stereoBM(__global const uchar * leftptr,
__global const uchar * rightptr,
__global uchar * dispptr, int disp_step, int disp_offset,
int rows, int cols, // rows, cols of left and right images, not disp
int textureThreshold, int uniquenessRatio)
{
int gx = get_global_id(0)*sizeX;
int gy = get_global_id(1)*sizeY;
int lz = get_local_id(2);
int lz = get_local_id(0);
int gx = get_global_id(1) * BLOCK_SIZE_X;
int gy = get_global_id(2) * BLOCK_SIZE_Y;
int nthread = lz/ndisp;
int d = lz%ndisp;
int wsz2 = wsz/2;
int nthread = lz / NUM_DISP;
int disp_idx = lz % NUM_DISP;
__global short * disp;
__global const uchar * left, * right;
__local short costFunc[csize];
__local short costFunc[2 * BLOCK_SIZE_Y * NUM_DISP];
__local short * cost;
__local int best_disp[2];
__local int best_cost[2];
best_cost[nthread] = MAX_VAL;
best_disp[nthread] = MAX_VAL;
best_disp[nthread] = -1;
barrier(CLK_LOCAL_MEM_FENCE);
short costbuf[wsz];
short costbuf[WSZ];
int head = 0;
int shiftX = wsz2 + ndisp + mindisp - 1;
int shiftY = wsz2;
int shiftX = WSZ2 + NUM_DISP + MIN_DISP - 1;
int shiftY = WSZ2;
int x = gx + shiftX, y = gy + shiftY, lx = 0, ly = 0;
int costIdx = calcLocalIdx(lx, ly, d, sizeY);
int costIdx = disp_idx * 2 * BLOCK_SIZE_Y + (BLOCK_SIZE_Y - 1);
cost = costFunc + costIdx;
int tempcost = 0;
if(x < cols-wsz2-mindisp && y < rows-wsz2)
if (x < cols - WSZ2 - MIN_DISP && y < rows - WSZ2)
{
int shift = 1*nthread + cols*(1-nthread);
for(int i = 0; i < winsize; i++)
if (0 == nthread)
{
int idx = mad24(y-wsz2+i*nthread, cols, x-wsz2+i*(1-nthread));
left = leftptr + idx;
right = rightptr + (idx - d);
short costdiff = 0;
for(int j = 0; j < winsize; j++)
#pragma unroll
for (int i = 0; i < WSZ; i++)
{
costdiff += abs( left[0] - right[0] );
left += shift;
right += shift;
int idx = mad24(y - WSZ2, cols, x - WSZ2 + i);
left = leftptr + idx;
right = rightptr + (idx - disp_idx);
short costdiff = 0;
for(int j = 0; j < WSZ; j++)
{
costdiff += abs( left[0] - right[0] );
left += cols;
right += cols;
}
costbuf[i] = costdiff;
}
if(nthread==1)
}
else // (1 == nthread)
{
#pragma unroll
for (int i = 0; i < WSZ; i++)
{
int idx = mad24(y - WSZ2 + i, cols, x - WSZ2);
left = leftptr + idx;
right = rightptr + (idx - disp_idx);
short costdiff = 0;
for (int j = 0; j < WSZ; j++)
{
costdiff += abs( left[j] - right[j]);
}
tempcost += costdiff;
costbuf[i] = costdiff;
}
costbuf[head] = costdiff;
head++;
}
}
if(nthread==1)
if (nthread == 1)
{
cost[0] = tempcost;
atomic_min(best_cost+nthread, tempcost);
atomic_min(best_cost + 1, tempcost);
}
barrier(CLK_LOCAL_MEM_FENCE);
if(best_cost[1] == tempcost)
atomic_min(best_disp + 1, ndisp - d - 1);
if (best_cost[1] == tempcost)
atomic_max(best_disp + 1, disp_idx);
barrier(CLK_LOCAL_MEM_FENCE);
int dispIdx = mad24(gy, disp_step, disp_offset + gx*(int)sizeof(short));
int dispIdx = mad24(gy, disp_step, mad24((int)sizeof(short), gx, disp_offset));
disp = (__global short *)(dispptr + dispIdx);
calcDisp(cost, disp, uniquenessRatio, mindisp, ndisp, 2*sizeY,
best_disp + 1, best_cost+1, d, x, y, cols, rows, wsz2);
calcDisp(cost, disp, uniquenessRatio, best_disp + 1, best_cost + 1, disp_idx, x, y, cols, rows);
barrier(CLK_LOCAL_MEM_FENCE);
lx = 1 - nthread;
ly = nthread;
for(int i = 0; i < sizeY*sizeX/2; i++)
for (int i = 0; i < BLOCK_SIZE_Y * BLOCK_SIZE_X / 2; i++)
{
x = (lx < sizeX) ? gx + shiftX + lx : cols;
y = (ly < sizeY) ? gy + shiftY + ly : rows;
x = (lx < BLOCK_SIZE_X) ? gx + shiftX + lx : cols;
y = (ly < BLOCK_SIZE_Y) ? gy + shiftY + ly : rows;
best_cost[nthread] = MAX_VAL;
best_disp[nthread] = MAX_VAL;
best_disp[nthread] = -1;
barrier(CLK_LOCAL_MEM_FENCE);
costIdx = calcLocalIdx(lx, ly, d, sizeY);
costIdx = mad24(2 * BLOCK_SIZE_Y, disp_idx, (BLOCK_SIZE_Y - 1 - ly + lx));
if (0 > costIdx)
costIdx = BLOCK_SIZE_Y - 1;
cost = costFunc + costIdx;
if(x < cols-wsz2-mindisp && y < rows-wsz2 )
if (x < cols - WSZ2 - MIN_DISP && y < rows - WSZ2)
{
tempcost = ( ly*(1-nthread) + lx*nthread == 0 ) ?
calcCostBorder(leftptr, rightptr, x, y, nthread, wsz2, costbuf, &head, cols, d,
cost[2*nthread-1], winsize) :
calcCostInside(leftptr, rightptr, x, y, wsz2, cols, d,
cost[0], cost[1], cost[-1], winsize);
tempcost = (ly * (1 - nthread) + lx * nthread == 0) ?
calcCostBorder(leftptr, rightptr, x, y, nthread, costbuf, &head, cols, disp_idx, cost[2*nthread-1]) :
calcCostInside(leftptr, rightptr, x, y, cols, disp_idx, cost[0], cost[1], cost[-1]);
}
cost[0] = tempcost;
atomic_min(best_cost + nthread, tempcost);
barrier(CLK_LOCAL_MEM_FENCE);
if(best_cost[nthread] == tempcost)
atomic_min(best_disp + nthread, ndisp - d - 1);
if (best_cost[nthread] == tempcost)
atomic_max(best_disp + nthread, disp_idx);
barrier(CLK_LOCAL_MEM_FENCE);
int dispIdx = mad24(gy+ly, disp_step, disp_offset + (gx+lx)*(int)sizeof(short));
dispIdx = mad24(gy + ly, disp_step, mad24((int)sizeof(short), (gx + lx), disp_offset));
disp = (__global short *)(dispptr + dispIdx);
calcDisp(cost, disp, uniquenessRatio, mindisp, ndisp, 2*sizeY,
best_disp + nthread, best_cost + nthread, d, x, y, cols, rows, wsz2);
calcDisp(cost, disp, uniquenessRatio, best_disp + nthread, best_cost + nthread, disp_idx, x, y, cols, rows);
barrier(CLK_LOCAL_MEM_FENCE);
calcNewCoordinates(&lx, &ly, nthread);
if (lx + nthread - 1 == ly)
{
lx = (lx + nthread + 1) * (1 - nthread);
ly = (ly + 1) * nthread;
}
else
{
lx += nthread;
ly = ly - nthread + 1;
}
}
}
#endif
#endif //DEFINE_KERNEL_STEREOBM
//////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////// Norm Prefiler ////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////
__kernel void prefilter_norm(__global unsigned char *input, __global unsigned char *output,
int rows, int cols, int prefilterCap, int winsize, int scale_g, int scale_s)
int rows, int cols, int prefilterCap, int scale_g, int scale_s)
{
// prefilterCap in range 1..63, checked in StereoBMImpl::compute
int x = get_global_id(0);
int y = get_global_id(1);
int wsz2 = winsize/2;
if(x < cols && y < rows)
{
@@ -262,13 +296,13 @@ __kernel void prefilter_norm(__global unsigned char *input, __global unsigned ch
input[y * cols + max(x-1,0)] * 1 + input[ y * cols + x] * 4 + input[y * cols + min(x+1, cols-1)] * 1 +
input[min(y+1, rows-1) * cols + x] * 1;
int cov2 = 0;
for(int i = -wsz2; i < wsz2+1; i++)
for(int j = -wsz2; j < wsz2+1; j++)
for(int i = -WSZ2; i < WSZ2+1; i++)
for(int j = -WSZ2; j < WSZ2+1; j++)
cov2 += input[clamp(y+i, 0, rows-1) * cols + clamp(x+j, 0, cols-1)];
int res = (cov1*scale_g - cov2*scale_s)>>10;
res = min(clamp(res, -prefilterCap, prefilterCap) + prefilterCap, 255);
output[y * cols + x] = res & 0xFF;
res = clamp(res, -prefilterCap, prefilterCap) + prefilterCap;
output[y * cols + x] = res;
}
}
@@ -280,20 +314,21 @@ __kernel void prefilter_norm(__global unsigned char *input, __global unsigned ch
__kernel void prefilter_xsobel(__global unsigned char *input, __global unsigned char *output,
int rows, int cols, int prefilterCap)
{
// prefilterCap in range 1..63, checked in StereoBMImpl::compute
int x = get_global_id(0);
int y = get_global_id(1);
if(x < cols && y < rows)
{
output[y * cols + x] = min(prefilterCap, 255) & 0xFF;
}
if (0 < x && !((y == rows-1) & (rows%2==1) ) )
{
int cov = input[ ((y > 0) ? y-1 : y+1) * cols + (x-1)] * (-1) + input[ ((y > 0) ? y-1 : y+1) * cols + ((x<cols-1) ? x+1 : x-1)] * (1) +
input[ (y) * cols + (x-1)] * (-2) + input[ (y) * cols + ((x<cols-1) ? x+1 : x-1)] * (2) +
input[((y<rows-1)?(y+1):(y-1))* cols + (x-1)] * (-1) + input[((y<rows-1)?(y+1):(y-1))* cols + ((x<cols-1) ? x+1 : x-1)] * (1);
if(x < cols && y < rows && x > 0 && !((y == rows-1)&(rows%2==1) ) )
{
int cov = input[ ((y > 0) ? y-1 : y+1) * cols + (x-1)] * (-1) + input[ ((y > 0) ? y-1 : y+1) * cols + ((x<cols-1) ? x+1 : x-1)] * (1) +
input[ (y) * cols + (x-1)] * (-2) + input[ (y) * cols + ((x<cols-1) ? x+1 : x-1)] * (2) +
input[((y<rows-1)?(y+1):(y-1))* cols + (x-1)] * (-1) + input[((y<rows-1)?(y+1):(y-1))* cols + ((x<cols-1) ? x+1 : x-1)] * (1);
cov = min(clamp(cov, -prefilterCap, prefilterCap) + prefilterCap, 255);
output[y * cols + x] = cov & 0xFF;
cov = clamp(cov, -prefilterCap, prefilterCap) + prefilterCap;
output[y * cols + x] = cov;
}
else
output[y * cols + x] = prefilterCap;
}
}
}
+86 -33
View File
@@ -33,7 +33,9 @@ p3p::p3p(double _fx, double _fy, double _cx, double _cy)
bool p3p::solve(cv::Mat& R, cv::Mat& tvec, const cv::Mat& opoints, const cv::Mat& ipoints)
{
double rotation_matrix[3][3], translation[3];
CV_INSTRUMENT_REGION();
double rotation_matrix[3][3] = {}, translation[3] = {};
std::vector<double> points;
if (opoints.depth() == ipoints.depth())
{
@@ -47,55 +49,83 @@ bool p3p::solve(cv::Mat& R, cv::Mat& tvec, const cv::Mat& opoints, const cv::Mat
else
extract_points<cv::Point3d,cv::Point2f>(opoints, ipoints, points);
bool result = solve(rotation_matrix, translation, points[0], points[1], points[2], points[3], points[4], points[5],
points[6], points[7], points[8], points[9], points[10], points[11], points[12], points[13], points[14],
points[15], points[16], points[17], points[18], points[19]);
bool result = solve(rotation_matrix, translation,
points[0], points[1], points[2], points[3], points[4],
points[5], points[6], points[7], points[8], points[9],
points[10], points[11], points[12], points[13], points[14],
points[15], points[16], points[17], points[18], points[19]);
cv::Mat(3, 1, CV_64F, translation).copyTo(tvec);
cv::Mat(3, 3, CV_64F, rotation_matrix).copyTo(R);
return result;
}
int p3p::solve(std::vector<cv::Mat>& Rs, std::vector<cv::Mat>& tvecs, const cv::Mat& opoints, const cv::Mat& ipoints)
{
CV_INSTRUMENT_REGION();
double rotation_matrix[4][3][3] = {}, translation[4][3] = {};
std::vector<double> points;
if (opoints.depth() == ipoints.depth())
{
if (opoints.depth() == CV_32F)
extract_points<cv::Point3f,cv::Point2f>(opoints, ipoints, points);
else
extract_points<cv::Point3d,cv::Point2d>(opoints, ipoints, points);
}
else if (opoints.depth() == CV_32F)
extract_points<cv::Point3f,cv::Point2d>(opoints, ipoints, points);
else
extract_points<cv::Point3d,cv::Point2f>(opoints, ipoints, points);
const bool p4p = std::max(opoints.checkVector(3, CV_32F), opoints.checkVector(3, CV_64F)) == 4;
int solutions = solve(rotation_matrix, translation,
points[0], points[1], points[2], points[3], points[4],
points[5], points[6], points[7], points[8], points[9],
points[10], points[11], points[12], points[13], points[14],
points[15], points[16], points[17], points[18], points[19],
p4p);
for (int i = 0; i < solutions; i++) {
cv::Mat R, tvec;
cv::Mat(3, 1, CV_64F, translation[i]).copyTo(tvec);
cv::Mat(3, 3, CV_64F, rotation_matrix[i]).copyTo(R);
Rs.push_back(R);
tvecs.push_back(tvec);
}
return solutions;
}
bool p3p::solve(double R[3][3], double t[3],
double mu0, double mv0, double X0, double Y0, double Z0,
double mu1, double mv1, double X1, double Y1, double Z1,
double mu2, double mv2, double X2, double Y2, double Z2,
double mu3, double mv3, double X3, double Y3, double Z3)
{
double Rs[4][3][3], ts[4][3];
double Rs[4][3][3] = {}, ts[4][3] = {};
int n = solve(Rs, ts, mu0, mv0, X0, Y0, Z0, mu1, mv1, X1, Y1, Z1, mu2, mv2, X2, Y2, Z2);
const bool p4p = true;
int n = solve(Rs, ts, mu0, mv0, X0, Y0, Z0, mu1, mv1, X1, Y1, Z1, mu2, mv2, X2, Y2, Z2, mu3, mv3, X3, Y3, Z3, p4p);
if (n == 0)
return false;
int ns = 0;
double min_reproj = 0;
for(int i = 0; i < n; i++) {
double X3p = Rs[i][0][0] * X3 + Rs[i][0][1] * Y3 + Rs[i][0][2] * Z3 + ts[i][0];
double Y3p = Rs[i][1][0] * X3 + Rs[i][1][1] * Y3 + Rs[i][1][2] * Z3 + ts[i][1];
double Z3p = Rs[i][2][0] * X3 + Rs[i][2][1] * Y3 + Rs[i][2][2] * Z3 + ts[i][2];
double mu3p = cx + fx * X3p / Z3p;
double mv3p = cy + fy * Y3p / Z3p;
double reproj = (mu3p - mu3) * (mu3p - mu3) + (mv3p - mv3) * (mv3p - mv3);
if (i == 0 || min_reproj > reproj) {
ns = i;
min_reproj = reproj;
}
}
for(int i = 0; i < 3; i++) {
for(int j = 0; j < 3; j++)
R[i][j] = Rs[ns][i][j];
t[i] = ts[ns][i];
R[i][j] = Rs[0][i][j];
t[i] = ts[0][i];
}
return true;
}
int p3p::solve(double R[4][3][3], double t[4][3],
double mu0, double mv0, double X0, double Y0, double Z0,
double mu1, double mv1, double X1, double Y1, double Z1,
double mu2, double mv2, double X2, double Y2, double Z2)
double mu0, double mv0, double X0, double Y0, double Z0,
double mu1, double mv1, double X1, double Y1, double Z1,
double mu2, double mv2, double X2, double Y2, double Z2,
double mu3, double mv3, double X3, double Y3, double Z3,
bool p4p)
{
double mk0, mk1, mk2;
double norm;
@@ -115,6 +145,9 @@ int p3p::solve(double R[4][3][3], double t[4][3],
norm = sqrt(mu2 * mu2 + mv2 * mv2 + 1);
mk2 = 1. / norm; mu2 *= mk2; mv2 *= mk2;
mu3 = inv_fx * mu3 - cx_fx;
mv3 = inv_fy * mv3 - cy_fy;
double distances[3];
distances[0] = sqrt( (X1 - X2) * (X1 - X2) + (Y1 - Y2) * (Y1 - Y2) + (Z1 - Z2) * (Z1 - Z2) );
distances[1] = sqrt( (X0 - X2) * (X0 - X2) + (Y0 - Y2) * (Y0 - Y2) + (Z0 - Z2) * (Z0 - Z2) );
@@ -126,10 +159,11 @@ int p3p::solve(double R[4][3][3], double t[4][3],
cosines[1] = mu0 * mu2 + mv0 * mv2 + mk0 * mk2;
cosines[2] = mu0 * mu1 + mv0 * mv1 + mk0 * mk1;
double lengths[4][3];
double lengths[4][3] = {};
int n = solve_for_lengths(lengths, distances, cosines);
int nb_solutions = 0;
double reproj_errors[4];
for(int i = 0; i < n; i++) {
double M_orig[3][3];
@@ -148,14 +182,34 @@ int p3p::solve(double R[4][3][3], double t[4][3],
if (!align(M_orig, X0, Y0, Z0, X1, Y1, Z1, X2, Y2, Z2, R[nb_solutions], t[nb_solutions]))
continue;
if (p4p) {
double X3p = R[nb_solutions][0][0] * X3 + R[nb_solutions][0][1] * Y3 + R[nb_solutions][0][2] * Z3 + t[nb_solutions][0];
double Y3p = R[nb_solutions][1][0] * X3 + R[nb_solutions][1][1] * Y3 + R[nb_solutions][1][2] * Z3 + t[nb_solutions][1];
double Z3p = R[nb_solutions][2][0] * X3 + R[nb_solutions][2][1] * Y3 + R[nb_solutions][2][2] * Z3 + t[nb_solutions][2];
double mu3p = X3p / Z3p;
double mv3p = Y3p / Z3p;
reproj_errors[nb_solutions] = (mu3p - mu3) * (mu3p - mu3) + (mv3p - mv3) * (mv3p - mv3);
}
nb_solutions++;
}
if (p4p) {
//sort the solutions
for (int i = 1; i < nb_solutions; i++) {
for (int j = i; j > 0 && reproj_errors[j-1] > reproj_errors[j]; j--) {
std::swap(reproj_errors[j], reproj_errors[j-1]);
std::swap(R[j], R[j-1]);
std::swap(t[j], t[j-1]);
}
}
}
return nb_solutions;
}
/// Given 3D distances between three points and cosines of 3 angles at the apex, calculates
/// the lentghs of the line segments connecting projection center (P) and the three 3D points (A, B, C).
/// the lengths of the line segments connecting projection center (P) and the three 3D points (A, B, C).
/// Returned distances are for |PA|, |PB|, |PC| respectively.
/// Only the solution to the main branch.
/// Reference : X.S. Gao, X.-R. Hou, J. Tang, H.-F. Chang; "Complete Solution Classification for the Perspective-Three-Point Problem"
@@ -265,21 +319,21 @@ bool p3p::align(double M_end[3][3],
double R[3][3], double T[3])
{
// Centroids:
double C_start[3], C_end[3];
double C_start[3] = {}, C_end[3] = {};
for(int i = 0; i < 3; i++) C_end[i] = (M_end[0][i] + M_end[1][i] + M_end[2][i]) / 3;
C_start[0] = (X0 + X1 + X2) / 3;
C_start[1] = (Y0 + Y1 + Y2) / 3;
C_start[2] = (Z0 + Z1 + Z2) / 3;
// Covariance matrix s:
double s[3 * 3];
double s[3 * 3] = {};
for(int j = 0; j < 3; j++) {
s[0 * 3 + j] = (X0 * M_end[0][j] + X1 * M_end[1][j] + X2 * M_end[2][j]) / 3 - C_end[j] * C_start[0];
s[1 * 3 + j] = (Y0 * M_end[0][j] + Y1 * M_end[1][j] + Y2 * M_end[2][j]) / 3 - C_end[j] * C_start[1];
s[2 * 3 + j] = (Z0 * M_end[0][j] + Z1 * M_end[1][j] + Z2 * M_end[2][j]) / 3 - C_end[j] * C_start[2];
}
double Qs[16], evs[4], U[16];
double Qs[16] = {}, evs[4] = {}, U[16] = {};
Qs[0 * 4 + 0] = s[0 * 3 + 0] + s[1 * 3 + 1] + s[2 * 3 + 2];
Qs[1 * 4 + 1] = s[0 * 3 + 0] - s[1 * 3 + 1] - s[2 * 3 + 2];
@@ -332,7 +386,7 @@ bool p3p::align(double M_end[3][3],
bool p3p::jacobi_4x4(double * A, double * D, double * U)
{
double B[4], Z[4];
double B[4] = {}, Z[4] = {};
double Id[16] = {1., 0., 0., 0.,
0., 1., 0., 0.,
0., 0., 1., 0.,
@@ -342,7 +396,6 @@ bool p3p::jacobi_4x4(double * A, double * D, double * U)
B[0] = A[0]; B[1] = A[5]; B[2] = A[10]; B[3] = A[15];
memcpy(D, B, 4 * sizeof(double));
memset(Z, 0, 4 * sizeof(double));
for(int iter = 0; iter < 50; iter++) {
double sum = fabs(A[1]) + fabs(A[2]) + fabs(A[3]) + fabs(A[6]) + fabs(A[7]) + fabs(A[11]);
+18 -8
View File
@@ -11,10 +11,13 @@ class p3p
p3p(cv::Mat cameraMatrix);
bool solve(cv::Mat& R, cv::Mat& tvec, const cv::Mat& opoints, const cv::Mat& ipoints);
int solve(std::vector<cv::Mat>& Rs, std::vector<cv::Mat>& tvecs, const cv::Mat& opoints, const cv::Mat& ipoints);
int solve(double R[4][3][3], double t[4][3],
double mu0, double mv0, double X0, double Y0, double Z0,
double mu1, double mv1, double X1, double Y1, double Z1,
double mu2, double mv2, double X2, double Y2, double Z2);
double mu2, double mv2, double X2, double Y2, double Z2,
double mu3, double mv3, double X3, double Y3, double Z3,
bool p4p);
bool solve(double R[3][3], double t[3],
double mu0, double mv0, double X0, double Y0, double Z0,
double mu1, double mv1, double X1, double Y1, double Z1,
@@ -34,14 +37,21 @@ class p3p
void extract_points(const cv::Mat& opoints, const cv::Mat& ipoints, std::vector<double>& points)
{
points.clear();
points.resize(20);
for(int i = 0; i < 4; i++)
int npoints = std::max(opoints.checkVector(3, CV_32F), opoints.checkVector(3, CV_64F));
points.resize(5*4); //resize vector to fit for p4p case
for(int i = 0; i < npoints; i++)
{
points[i*5] = ipoints.at<IpointType>(0,i).x*fx + cx;
points[i*5+1] = ipoints.at<IpointType>(0,i).y*fy + cy;
points[i*5+2] = opoints.at<OpointType>(0,i).x;
points[i*5+3] = opoints.at<OpointType>(0,i).y;
points[i*5+4] = opoints.at<OpointType>(0,i).z;
points[i*5] = ipoints.at<IpointType>(i).x*fx + cx;
points[i*5+1] = ipoints.at<IpointType>(i).y*fy + cy;
points[i*5+2] = opoints.at<OpointType>(i).x;
points[i*5+3] = opoints.at<OpointType>(i).y;
points[i*5+4] = opoints.at<OpointType>(i).z;
}
//Fill vectors with unused values for p3p case
for (int i = npoints; i < 4; i++) {
for (int j = 0; j < 5; j++) {
points[i * 5 + j] = 0;
}
}
}
void init_inverse_parameters();
+1 -1
View File
@@ -32,7 +32,7 @@ int solve_deg3(double a, double b, double c, double d,
double & x0, double & x1, double & x2)
{
if (a == 0) {
// Solve second order sytem
// Solve second order system
if (b == 0) {
// Solve first order system
if (c == 0)
+16 -15
View File
@@ -115,10 +115,10 @@ static CvStatus icvPOSIT( CvPOSITObject *pObject, CvPoint2D32f *imagePoints,
float* rotation, float* translation )
{
int i, j, k;
int count = 0, converged = 0;
float inorm, jnorm, invInorm, invJnorm, invScale, scale = 0, inv_Z = 0;
int count = 0;
bool converged = false;
float scale = 0, inv_Z = 0;
float diff = (float)criteria.epsilon;
float inv_focalLength = 1 / focalLength;
/* Check bad arguments */
if( imagePoints == NULL )
@@ -139,6 +139,7 @@ static CvStatus icvPOSIT( CvPOSITObject *pObject, CvPoint2D32f *imagePoints,
return CV_BADFACTOR_ERR;
/* init variables */
float inv_focalLength = 1 / focalLength;
int N = pObject->N;
float *objectVectors = pObject->obj_vecs;
float *invMatrix = pObject->inv_matr;
@@ -195,16 +196,18 @@ static CvStatus icvPOSIT( CvPOSITObject *pObject, CvPoint2D32f *imagePoints,
}
}
inorm = rotation[0] /*[0][0]*/ * rotation[0] /*[0][0]*/ +
float inorm =
rotation[0] /*[0][0]*/ * rotation[0] /*[0][0]*/ +
rotation[1] /*[0][1]*/ * rotation[1] /*[0][1]*/ +
rotation[2] /*[0][2]*/ * rotation[2] /*[0][2]*/;
jnorm = rotation[3] /*[1][0]*/ * rotation[3] /*[1][0]*/ +
float jnorm =
rotation[3] /*[1][0]*/ * rotation[3] /*[1][0]*/ +
rotation[4] /*[1][1]*/ * rotation[4] /*[1][1]*/ +
rotation[5] /*[1][2]*/ * rotation[5] /*[1][2]*/;
invInorm = cvInvSqrt( inorm );
invJnorm = cvInvSqrt( jnorm );
const float invInorm = cvInvSqrt( inorm );
const float invJnorm = cvInvSqrt( jnorm );
inorm *= invInorm;
jnorm *= invJnorm;
@@ -231,10 +234,10 @@ static CvStatus icvPOSIT( CvPOSITObject *pObject, CvPoint2D32f *imagePoints,
inv_Z = scale * inv_focalLength;
count++;
converged = ((criteria.type & CV_TERMCRIT_EPS) && (diff < criteria.epsilon));
converged |= ((criteria.type & CV_TERMCRIT_ITER) && (count == criteria.max_iter));
converged = ((criteria.type & CV_TERMCRIT_EPS) && (diff < criteria.epsilon))
|| ((criteria.type & CV_TERMCRIT_ITER) && (count == criteria.max_iter));
}
invScale = 1 / scale;
const float invScale = 1 / scale;
translation[0] = imagePoints[0].x * invScale;
translation[1] = imagePoints[0].y * invScale;
translation[2] = 1 / inv_Z;
@@ -266,8 +269,6 @@ static CvStatus icvReleasePOSITObject( CvPOSITObject ** ppObject )
void
icvPseudoInverse3D( float *a, float *b, int n, int method )
{
int k;
if( method == 0 )
{
float ata00 = 0;
@@ -276,8 +277,8 @@ icvPseudoInverse3D( float *a, float *b, int n, int method )
float ata01 = 0;
float ata02 = 0;
float ata12 = 0;
float det = 0;
int k;
/* compute matrix ata = transpose(a) * a */
for( k = 0; k < n; k++ )
{
@@ -295,7 +296,6 @@ icvPseudoInverse3D( float *a, float *b, int n, int method )
}
/* inverse matrix ata */
{
float inv_det;
float p00 = ata11 * ata22 - ata12 * ata12;
float p01 = -(ata01 * ata22 - ata12 * ata02);
float p02 = ata12 * ata01 - ata11 * ata02;
@@ -304,11 +304,12 @@ icvPseudoInverse3D( float *a, float *b, int n, int method )
float p12 = -(ata00 * ata12 - ata01 * ata02);
float p22 = ata00 * ata11 - ata01 * ata01;
float det = 0;
det += ata00 * p00;
det += ata01 * p01;
det += ata02 * p02;
inv_det = 1 / det;
const float inv_det = 1 / det;
/* compute resultant matrix */
for( k = 0; k < n; k++ )
+60 -3
View File
@@ -42,13 +42,15 @@
#ifndef __OPENCV_PRECOMP_H__
#define __OPENCV_PRECOMP_H__
#include "opencv2/calib3d.hpp"
#include "opencv2/imgproc.hpp"
#include "opencv2/features2d.hpp"
#include "opencv2/core/utility.hpp"
#include "opencv2/core/private.hpp"
#include "opencv2/calib3d.hpp"
#include "opencv2/imgproc.hpp"
#include "opencv2/features2d.hpp"
#include "opencv2/core/ocl.hpp"
#ifdef HAVE_TEGRA_OPTIMIZATION
@@ -61,6 +63,21 @@
namespace cv
{
/**
* Compute the number of iterations given the confidence, outlier ratio, number
* of model points and the maximum iteration number.
*
* @param p confidence value
* @param ep outlier ratio
* @param modelPoints number of model points required for estimation
* @param maxIters maximum number of iterations
* @return
* \f[
* \frac{\ln(1-p)}{\ln\left(1-(1-ep)^\mathrm{modelPoints}\right)}
* \f]
*
* If the computed number of iterations is larger than maxIters, then maxIters is returned.
*/
int RANSACUpdateNumIters( double p, double ep, int modelPoints, int maxIters );
class CV_EXPORTS LMSolver : public Algorithm
@@ -78,6 +95,7 @@ public:
};
CV_EXPORTS Ptr<LMSolver> createLMSolver(const Ptr<LMSolver::Callback>& cb, int maxIters);
CV_EXPORTS Ptr<LMSolver> createLMSolver(const Ptr<LMSolver::Callback>& cb, int maxIters, double eps);
class CV_EXPORTS PointSetRegistrator : public Algorithm
{
@@ -102,6 +120,45 @@ CV_EXPORTS Ptr<PointSetRegistrator> createRANSACPointSetRegistrator(const Ptr<Po
CV_EXPORTS Ptr<PointSetRegistrator> createLMeDSPointSetRegistrator(const Ptr<PointSetRegistrator::Callback>& cb,
int modelPoints, double confidence=0.99, int maxIters=1000 );
template<typename T> inline int compressElems( T* ptr, const uchar* mask, int mstep, int count )
{
int i, j;
for( i = j = 0; i < count; i++ )
if( mask[i*mstep] )
{
if( i > j )
ptr[j] = ptr[i];
j++;
}
return j;
}
static inline bool haveCollinearPoints( const Mat& m, int count )
{
int j, k, i = count-1;
const Point2f* ptr = m.ptr<Point2f>();
// check that the i-th selected point does not belong
// to a line connecting some previously selected points
// also checks that points are not too close to each other
for( j = 0; j < i; j++ )
{
double dx1 = ptr[j].x - ptr[i].x;
double dy1 = ptr[j].y - ptr[i].y;
for( k = 0; k < j; k++ )
{
double dx2 = ptr[k].x - ptr[i].x;
double dy2 = ptr[k].y - ptr[i].y;
if( fabs(dx2*dy1 - dy2*dx1) <= FLT_EPSILON*(fabs(dx1) + fabs(dy1) + fabs(dx2) + fabs(dy2)))
return true;
}
}
return false;
}
} // namespace cv
int checkChessboard(const cv::Mat & img, const cv::Size & size);
int checkChessboardBinary(const cv::Mat & img, const cv::Size & size);
#endif
+532 -87
View File
@@ -78,10 +78,7 @@ class RANSACPointSetRegistrator : public PointSetRegistrator
public:
RANSACPointSetRegistrator(const Ptr<PointSetRegistrator::Callback>& _cb=Ptr<PointSetRegistrator::Callback>(),
int _modelPoints=0, double _threshold=0, double _confidence=0.99, int _maxIters=1000)
: cb(_cb), modelPoints(_modelPoints), threshold(_threshold), confidence(_confidence), maxIters(_maxIters)
{
checkPartialSubsets = true;
}
: cb(_cb), modelPoints(_modelPoints), threshold(_threshold), confidence(_confidence), maxIters(_maxIters) {}
int findInliers( const Mat& m1, const Mat& m2, const Mat& model, Mat& err, Mat& mask, double thresh ) const
{
@@ -102,63 +99,63 @@ public:
return nz;
}
bool getSubset( const Mat& m1, const Mat& m2,
Mat& ms1, Mat& ms2, RNG& rng,
int maxAttempts=1000 ) const
bool getSubset( const Mat& m1, const Mat& m2, Mat& ms1, Mat& ms2, RNG& rng, int maxAttempts=1000 ) const
{
cv::AutoBuffer<int> _idx(modelPoints);
int* idx = _idx;
int i = 0, j, k, iters = 0;
int esz1 = (int)m1.elemSize(), esz2 = (int)m2.elemSize();
int d1 = m1.channels() > 1 ? m1.channels() : m1.cols;
int d2 = m2.channels() > 1 ? m2.channels() : m2.cols;
int count = m1.checkVector(d1), count2 = m2.checkVector(d2);
const int *m1ptr = (const int*)m1.data, *m2ptr = (const int*)m2.data;
int* idx = _idx.data();
const int d1 = m1.channels() > 1 ? m1.channels() : m1.cols;
const int d2 = m2.channels() > 1 ? m2.channels() : m2.cols;
int esz1 = (int)m1.elemSize1() * d1;
int esz2 = (int)m2.elemSize1() * d2;
CV_Assert((esz1 % sizeof(int)) == 0 && (esz2 % sizeof(int)) == 0);
esz1 /= sizeof(int);
esz2 /= sizeof(int);
const int count = m1.checkVector(d1);
const int count2 = m2.checkVector(d2);
CV_Assert(count >= modelPoints && count == count2);
const int *m1ptr = m1.ptr<int>();
const int *m2ptr = m2.ptr<int>();
ms1.create(modelPoints, 1, CV_MAKETYPE(m1.depth(), d1));
ms2.create(modelPoints, 1, CV_MAKETYPE(m2.depth(), d2));
int *ms1ptr = (int*)ms1.data, *ms2ptr = (int*)ms2.data;
int *ms1ptr = ms1.ptr<int>();
int *ms2ptr = ms2.ptr<int>();
CV_Assert( count >= modelPoints && count == count2 );
CV_Assert( (esz1 % sizeof(int)) == 0 && (esz2 % sizeof(int)) == 0 );
esz1 /= sizeof(int);
esz2 /= sizeof(int);
for(; iters < maxAttempts; iters++)
for( int iters = 0; iters < maxAttempts; ++iters )
{
for( i = 0; i < modelPoints && iters < maxAttempts; )
int i;
for( i = 0; i < modelPoints; ++i )
{
int idx_i = 0;
for(;;)
{
idx_i = idx[i] = rng.uniform(0, count);
for( j = 0; j < i; j++ )
if( idx_i == idx[j] )
break;
if( j == i )
break;
}
for( k = 0; k < esz1; k++ )
int idx_i;
for ( idx_i = rng.uniform(0, count);
std::find(idx, idx + i, idx_i) != idx + i;
idx_i = rng.uniform(0, count) )
{}
idx[i] = idx_i;
for( int k = 0; k < esz1; ++k )
ms1ptr[i*esz1 + k] = m1ptr[idx_i*esz1 + k];
for( k = 0; k < esz2; k++ )
for( int k = 0; k < esz2; ++k )
ms2ptr[i*esz2 + k] = m2ptr[idx_i*esz2 + k];
if( checkPartialSubsets && !cb->checkSubset( ms1, ms2, i+1 ))
{
iters++;
continue;
}
i++;
}
if( !checkPartialSubsets && i == modelPoints && !cb->checkSubset(ms1, ms2, i))
continue;
break;
if( cb->checkSubset(ms1, ms2, i) )
return true;
}
return i == modelPoints && iters < maxAttempts;
return false;
}
bool run(InputArray _m1, InputArray _m2, OutputArray _model, OutputArray _mask) const
bool run(InputArray _m1, InputArray _m2, OutputArray _model, OutputArray _mask) const CV_OVERRIDE
{
bool result = false;
Mat m1 = _m1.getMat(), m2 = _m2.getMat();
@@ -203,10 +200,10 @@ public:
for( iter = 0; iter < niters; iter++ )
{
int i, goodCount, nmodels;
int i, nmodels;
if( count > modelPoints )
{
bool found = getSubset( m1, m2, ms1, ms2, rng );
bool found = getSubset( m1, m2, ms1, ms2, rng, 10000 );
if( !found )
{
if( iter == 0 )
@@ -224,7 +221,7 @@ public:
for( i = 0; i < nmodels; i++ )
{
Mat model_i = model.rowRange( i*modelSize.height, (i+1)*modelSize.height );
goodCount = findInliers( m1, m2, model_i, err, mask, threshold );
int goodCount = findInliers( m1, m2, model_i, err, mask, threshold );
if( goodCount > MAX(maxGoodCount, modelPoints-1) )
{
@@ -254,13 +251,10 @@ public:
return result;
}
void setCallback(const Ptr<PointSetRegistrator::Callback>& _cb) { cb = _cb; }
AlgorithmInfo* info() const;
void setCallback(const Ptr<PointSetRegistrator::Callback>& _cb) CV_OVERRIDE { cb = _cb; }
Ptr<PointSetRegistrator::Callback> cb;
int modelPoints;
bool checkPartialSubsets;
double threshold;
double confidence;
int maxIters;
@@ -273,7 +267,7 @@ public:
int _modelPoints=0, double _confidence=0.99, int _maxIters=1000)
: RANSACPointSetRegistrator(_cb, _modelPoints, 0, _confidence, _maxIters) {}
bool run(InputArray _m1, InputArray _m2, OutputArray _model, OutputArray _mask) const
bool run(InputArray _m1, InputArray _m2, OutputArray _model, OutputArray _mask) const CV_OVERRIDE
{
const double outlierRatio = 0.45;
bool result = false;
@@ -283,7 +277,7 @@ public:
int d1 = m1.channels() > 1 ? m1.channels() : m1.cols;
int d2 = m2.channels() > 1 ? m2.channels() : m2.cols;
int count = m1.checkVector(d1), count2 = m2.checkVector(d2);
double minMedian = DBL_MAX, sigma;
double minMedian = DBL_MAX;
RNG rng((uint64)-1);
@@ -343,10 +337,8 @@ public:
else
errf = err;
CV_Assert( errf.isContinuous() && errf.type() == CV_32F && (int)errf.total() == count );
std::sort((int*)errf.data, (int*)errf.data + count);
double median = count % 2 != 0 ?
errf.at<float>(count/2) : (errf.at<float>(count/2-1) + errf.at<float>(count/2))*0.5;
std::nth_element(errf.ptr<int>(), errf.ptr<int>() + count/2, errf.ptr<int>() + count);
double median = errf.at<float>(count/2);
if( median < minMedian )
{
@@ -358,7 +350,7 @@ public:
if( minMedian < DBL_MAX )
{
sigma = 2.5*1.4826*(1 + 5./(count - modelPoints))*std::sqrt(minMedian);
double sigma = 2.5*1.4826*(1 + 5./(count - modelPoints))*std::sqrt(minMedian);
sigma = MAX( sigma, 0.001 );
count = findInliers( m1, m2, bestModel, err, mask, sigma );
@@ -378,24 +370,12 @@ public:
return result;
}
AlgorithmInfo* info() const;
};
CV_INIT_ALGORITHM(RANSACPointSetRegistrator, "PointSetRegistrator.RANSAC",
obj.info()->addParam(obj, "threshold", obj.threshold);
obj.info()->addParam(obj, "confidence", obj.confidence);
obj.info()->addParam(obj, "maxIters", obj.maxIters))
CV_INIT_ALGORITHM(LMeDSPointSetRegistrator, "PointSetRegistrator.LMeDS",
obj.info()->addParam(obj, "confidence", obj.confidence);
obj.info()->addParam(obj, "maxIters", obj.maxIters))
Ptr<PointSetRegistrator> createRANSACPointSetRegistrator(const Ptr<PointSetRegistrator::Callback>& _cb,
int _modelPoints, double _threshold,
double _confidence, int _maxIters)
{
CV_Assert( !RANSACPointSetRegistrator_info_auto.name().empty() );
return Ptr<PointSetRegistrator>(
new RANSACPointSetRegistrator(_cb, _modelPoints, _threshold, _confidence, _maxIters));
}
@@ -404,15 +384,28 @@ Ptr<PointSetRegistrator> createRANSACPointSetRegistrator(const Ptr<PointSetRegis
Ptr<PointSetRegistrator> createLMeDSPointSetRegistrator(const Ptr<PointSetRegistrator::Callback>& _cb,
int _modelPoints, double _confidence, int _maxIters)
{
CV_Assert( !LMeDSPointSetRegistrator_info_auto.name().empty() );
return Ptr<PointSetRegistrator>(
new LMeDSPointSetRegistrator(_cb, _modelPoints, _confidence, _maxIters));
}
/*
* Compute
* x a b c X t1
* y = d e f * Y + t2
* z g h i Z t3
*
* - every element in _m1 contains (X,Y,Z), which are called source points
* - every element in _m2 contains (x,y,z), which are called destination points
* - _model is of size 3x4, which contains
* a b c t1
* d e f t2
* g h i t3
*/
class Affine3DEstimatorCallback : public PointSetRegistrator::Callback
{
public:
int runKernel( InputArray _m1, InputArray _m2, OutputArray _model ) const
int runKernel( InputArray _m1, InputArray _m2, OutputArray _model ) const CV_OVERRIDE
{
Mat m1 = _m1.getMat(), m2 = _m2.getMat();
const Point3f* from = m1.ptr<Point3f>();
@@ -450,7 +443,7 @@ public:
return 1;
}
void computeError( InputArray _m1, InputArray _m2, InputArray _model, OutputArray _err ) const
void computeError( InputArray _m1, InputArray _m2, InputArray _model, OutputArray _err ) const CV_OVERRIDE
{
Mat m1 = _m1.getMat(), m2 = _m2.getMat(), model = _model.getMat();
const Point3f* from = m1.ptr<Point3f>();
@@ -473,11 +466,11 @@ public:
double b = F[4]*f.x + F[5]*f.y + F[ 6]*f.z + F[ 7] - t.y;
double c = F[8]*f.x + F[9]*f.y + F[10]*f.z + F[11] - t.z;
errptr[i] = (float)std::sqrt(a*a + b*b + c*c);
errptr[i] = (float)(a*a + b*b + c*c);
}
}
bool checkSubset( InputArray _ms1, InputArray _ms2, int count ) const
bool checkSubset( InputArray _ms1, InputArray _ms2, int count ) const CV_OVERRIDE
{
const float threshold = 0.996f;
Mat ms1 = _ms1.getMat(), ms2 = _ms2.getMat();
@@ -495,13 +488,13 @@ public:
for(j = 0; j < i; ++j)
{
Point3f d1 = ptr[j] - ptr[i];
float n1 = d1.x*d1.x + d1.y*d1.y;
float n1 = d1.x*d1.x + d1.y*d1.y + d1.z*d1.z;
for(k = 0; k < j; ++k)
{
Point3f d2 = ptr[k] - ptr[i];
float denom = (d2.x*d2.x + d2.y*d2.y)*n1;
float num = d1.x*d2.x + d1.y*d2.y;
float denom = (d2.x*d2.x + d2.y*d2.y + d2.z*d2.z)*n1;
float num = d1.x*d2.x + d1.y*d2.y + d1.z*d2.z;
if( num*num > threshold*threshold*denom )
return false;
@@ -512,12 +505,301 @@ public:
}
};
}
int cv::estimateAffine3D(InputArray _from, InputArray _to,
OutputArray _out, OutputArray _inliers,
double param1, double param2)
/*
* Compute
* x a b X c
* = * +
* y d e Y f
*
* - every element in _m1 contains (X,Y), which are called source points
* - every element in _m2 contains (x,y), which are called destination points
* - _model is of size 2x3, which contains
* a b c
* d e f
*/
class Affine2DEstimatorCallback : public PointSetRegistrator::Callback
{
public:
int runKernel( InputArray _m1, InputArray _m2, OutputArray _model ) const CV_OVERRIDE
{
Mat m1 = _m1.getMat(), m2 = _m2.getMat();
const Point2f* from = m1.ptr<Point2f>();
const Point2f* to = m2.ptr<Point2f>();
_model.create(2, 3, CV_64F);
Mat M_mat = _model.getMat();
double *M = M_mat.ptr<double>();
// we need 3 points to estimate affine transform
double x1 = from[0].x;
double y1 = from[0].y;
double x2 = from[1].x;
double y2 = from[1].y;
double x3 = from[2].x;
double y3 = from[2].y;
double X1 = to[0].x;
double Y1 = to[0].y;
double X2 = to[1].x;
double Y2 = to[1].y;
double X3 = to[2].x;
double Y3 = to[2].y;
/*
We want to solve AX = B
| x1 y1 1 0 0 0 |
| 0 0 0 x1 y1 1 |
| x2 y2 1 0 0 0 |
A = | 0 0 0 x2 y2 1 |
| x3 y3 1 0 0 0 |
| 0 0 0 x3 y3 1 |
B = (X1, Y1, X2, Y2, X3, Y3).t()
X = (a, b, c, d, e, f).t()
As the estimate of (a, b, c) only depends on the Xi, and (d, e, f) only
depends on the Yi, we do the *trick* to solve each one analytically.
| X1 | | x1 y1 1 | | a |
| X2 | = | x2 y2 1 | * | b |
| X3 | | x3 y3 1 | | c |
| Y1 | | x1 y1 1 | | d |
| Y2 | = | x2 y2 1 | * | e |
| Y3 | | x3 y3 1 | | f |
*/
double d = 1. / ( x1*(y2-y3) + x2*(y3-y1) + x3*(y1-y2) );
M[0] = d * ( X1*(y2-y3) + X2*(y3-y1) + X3*(y1-y2) );
M[1] = d * ( X1*(x3-x2) + X2*(x1-x3) + X3*(x2-x1) );
M[2] = d * ( X1*(x2*y3 - x3*y2) + X2*(x3*y1 - x1*y3) + X3*(x1*y2 - x2*y1) );
M[3] = d * ( Y1*(y2-y3) + Y2*(y3-y1) + Y3*(y1-y2) );
M[4] = d * ( Y1*(x3-x2) + Y2*(x1-x3) + Y3*(x2-x1) );
M[5] = d * ( Y1*(x2*y3 - x3*y2) + Y2*(x3*y1 - x1*y3) + Y3*(x1*y2 - x2*y1) );
return 1;
}
void computeError( InputArray _m1, InputArray _m2, InputArray _model, OutputArray _err ) const CV_OVERRIDE
{
Mat m1 = _m1.getMat(), m2 = _m2.getMat(), model = _model.getMat();
const Point2f* from = m1.ptr<Point2f>();
const Point2f* to = m2.ptr<Point2f>();
const double* F = model.ptr<double>();
int count = m1.checkVector(2);
CV_Assert( count > 0 );
_err.create(count, 1, CV_32F);
Mat err = _err.getMat();
float* errptr = err.ptr<float>();
// transform matrix to floats
float F0 = (float)F[0], F1 = (float)F[1], F2 = (float)F[2];
float F3 = (float)F[3], F4 = (float)F[4], F5 = (float)F[5];
for(int i = 0; i < count; i++ )
{
const Point2f& f = from[i];
const Point2f& t = to[i];
float a = F0*f.x + F1*f.y + F2 - t.x;
float b = F3*f.x + F4*f.y + F5 - t.y;
errptr[i] = a*a + b*b;
}
}
bool checkSubset( InputArray _ms1, InputArray _ms2, int count ) const CV_OVERRIDE
{
Mat ms1 = _ms1.getMat();
Mat ms2 = _ms2.getMat();
// check collinearity and also check that points are too close
return !haveCollinearPoints(ms1, count) && !haveCollinearPoints(ms2, count);
}
};
/*
* Compute
* x c -s X t1
* = * +
* y s c Y t2
*
* - every element in _m1 contains (X,Y), which are called source points
* - every element in _m2 contains (x,y), which are called destination points
* - _model is of size 2x3, which contains
* c -s t1
* s c t2
*/
class AffinePartial2DEstimatorCallback : public Affine2DEstimatorCallback
{
public:
int runKernel( InputArray _m1, InputArray _m2, OutputArray _model ) const CV_OVERRIDE
{
Mat m1 = _m1.getMat(), m2 = _m2.getMat();
const Point2f* from = m1.ptr<Point2f>();
const Point2f* to = m2.ptr<Point2f>();
_model.create(2, 3, CV_64F);
Mat M_mat = _model.getMat();
double *M = M_mat.ptr<double>();
// we need only 2 points to estimate transform
double x1 = from[0].x;
double y1 = from[0].y;
double x2 = from[1].x;
double y2 = from[1].y;
double X1 = to[0].x;
double Y1 = to[0].y;
double X2 = to[1].x;
double Y2 = to[1].y;
/*
we are solving AS = B
| x1 -y1 1 0 |
| y1 x1 0 1 |
A = | x2 -y2 1 0 |
| y2 x2 0 1 |
B = (X1, Y1, X2, Y2).t()
we solve that analytically
*/
double d = 1./((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2));
// solution vector
double S0 = d * ( (X1-X2)*(x1-x2) + (Y1-Y2)*(y1-y2) );
double S1 = d * ( (Y1-Y2)*(x1-x2) - (X1-X2)*(y1-y2) );
double S2 = d * ( (Y1-Y2)*(x1*y2 - x2*y1) - (X1*y2 - X2*y1)*(y1-y2) - (X1*x2 - X2*x1)*(x1-x2) );
double S3 = d * (-(X1-X2)*(x1*y2 - x2*y1) - (Y1*x2 - Y2*x1)*(x1-x2) - (Y1*y2 - Y2*y1)*(y1-y2) );
// set model, rotation part is antisymmetric
M[0] = M[4] = S0;
M[1] = -S1;
M[2] = S2;
M[3] = S1;
M[5] = S3;
return 1;
}
};
class Affine2DRefineCallback : public LMSolver::Callback
{
public:
Affine2DRefineCallback(InputArray _src, InputArray _dst)
{
src = _src.getMat();
dst = _dst.getMat();
}
bool compute(InputArray _param, OutputArray _err, OutputArray _Jac) const CV_OVERRIDE
{
int i, count = src.checkVector(2);
Mat param = _param.getMat();
_err.create(count*2, 1, CV_64F);
Mat err = _err.getMat(), J;
if( _Jac.needed())
{
_Jac.create(count*2, param.rows, CV_64F);
J = _Jac.getMat();
CV_Assert( J.isContinuous() && J.cols == 6 );
}
const Point2f* M = src.ptr<Point2f>();
const Point2f* m = dst.ptr<Point2f>();
const double* h = param.ptr<double>();
double* errptr = err.ptr<double>();
double* Jptr = J.data ? J.ptr<double>() : 0;
for( i = 0; i < count; i++ )
{
double Mx = M[i].x, My = M[i].y;
double xi = h[0]*Mx + h[1]*My + h[2];
double yi = h[3]*Mx + h[4]*My + h[5];
errptr[i*2] = xi - m[i].x;
errptr[i*2+1] = yi - m[i].y;
/*
Jacobian should be:
{x, y, 1, 0, 0, 0}
{0, 0, 0, x, y, 1}
*/
if( Jptr )
{
Jptr[0] = Mx; Jptr[1] = My; Jptr[2] = 1.;
Jptr[3] = Jptr[4] = Jptr[5] = 0.;
Jptr[6] = Jptr[7] = Jptr[8] = 0.;
Jptr[9] = Mx; Jptr[10] = My; Jptr[11] = 1.;
Jptr += 6*2;
}
}
return true;
}
Mat src, dst;
};
class AffinePartial2DRefineCallback : public LMSolver::Callback
{
public:
AffinePartial2DRefineCallback(InputArray _src, InputArray _dst)
{
src = _src.getMat();
dst = _dst.getMat();
}
bool compute(InputArray _param, OutputArray _err, OutputArray _Jac) const CV_OVERRIDE
{
int i, count = src.checkVector(2);
Mat param = _param.getMat();
_err.create(count*2, 1, CV_64F);
Mat err = _err.getMat(), J;
if( _Jac.needed())
{
_Jac.create(count*2, param.rows, CV_64F);
J = _Jac.getMat();
CV_Assert( J.isContinuous() && J.cols == 4 );
}
const Point2f* M = src.ptr<Point2f>();
const Point2f* m = dst.ptr<Point2f>();
const double* h = param.ptr<double>();
double* errptr = err.ptr<double>();
double* Jptr = J.data ? J.ptr<double>() : 0;
for( i = 0; i < count; i++ )
{
double Mx = M[i].x, My = M[i].y;
double xi = h[0]*Mx - h[1]*My + h[2];
double yi = h[1]*Mx + h[0]*My + h[3];
errptr[i*2] = xi - m[i].x;
errptr[i*2+1] = yi - m[i].y;
/*
Jacobian should be:
{x, -y, 1, 0}
{y, x, 0, 1}
*/
if( Jptr )
{
Jptr[0] = Mx; Jptr[1] = -My; Jptr[2] = 1.; Jptr[3] = 0.;
Jptr[4] = My; Jptr[5] = Mx; Jptr[6] = 0.; Jptr[7] = 1.;
Jptr += 4*2;
}
}
return true;
}
Mat src, dst;
};
int estimateAffine3D(InputArray _from, InputArray _to,
OutputArray _out, OutputArray _inliers,
double ransacThreshold, double confidence)
{
CV_INSTRUMENT_REGION();
Mat from = _from.getMat(), to = _to.getMat();
int count = from.checkVector(3);
@@ -530,8 +812,171 @@ int cv::estimateAffine3D(InputArray _from, InputArray _to,
dTo = dTo.reshape(3, count);
const double epsilon = DBL_EPSILON;
param1 = param1 <= 0 ? 3 : param1;
param2 = (param2 < epsilon) ? 0.99 : (param2 > 1 - epsilon) ? 0.99 : param2;
ransacThreshold = ransacThreshold <= 0 ? 3 : ransacThreshold;
confidence = (confidence < epsilon) ? 0.99 : (confidence > 1 - epsilon) ? 0.99 : confidence;
return createRANSACPointSetRegistrator(makePtr<Affine3DEstimatorCallback>(), 4, param1, param2)->run(dFrom, dTo, _out, _inliers);
return createRANSACPointSetRegistrator(makePtr<Affine3DEstimatorCallback>(), 4, ransacThreshold, confidence)->run(dFrom, dTo, _out, _inliers);
}
Mat estimateAffine2D(InputArray _from, InputArray _to, OutputArray _inliers,
const int method, const double ransacReprojThreshold,
const size_t maxIters, const double confidence,
const size_t refineIters)
{
Mat from = _from.getMat(), to = _to.getMat();
int count = from.checkVector(2);
bool result = false;
Mat H;
CV_Assert( count >= 0 && to.checkVector(2) == count );
if (from.type() != CV_32FC2 || to.type() != CV_32FC2)
{
Mat tmp1, tmp2;
from.convertTo(tmp1, CV_32FC2);
from = tmp1;
to.convertTo(tmp2, CV_32FC2);
to = tmp2;
}
else
{
// avoid changing of inputs in compressElems() call
from = from.clone();
to = to.clone();
}
// convert to N x 1 vectors
from = from.reshape(2, count);
to = to.reshape(2, count);
Mat inliers;
if(_inliers.needed())
{
_inliers.create(count, 1, CV_8U, -1, true);
inliers = _inliers.getMat();
}
// run robust method
Ptr<PointSetRegistrator::Callback> cb = makePtr<Affine2DEstimatorCallback>();
if( method == RANSAC )
result = createRANSACPointSetRegistrator(cb, 3, ransacReprojThreshold, confidence, static_cast<int>(maxIters))->run(from, to, H, inliers);
else if( method == LMEDS )
result = createLMeDSPointSetRegistrator(cb, 3, confidence, static_cast<int>(maxIters))->run(from, to, H, inliers);
else
CV_Error(Error::StsBadArg, "Unknown or unsupported robust estimation method");
if(result && count > 3 && refineIters)
{
// reorder to start with inliers
compressElems(from.ptr<Point2f>(), inliers.ptr<uchar>(), 1, count);
int inliers_count = compressElems(to.ptr<Point2f>(), inliers.ptr<uchar>(), 1, count);
if(inliers_count > 0)
{
Mat src = from.rowRange(0, inliers_count);
Mat dst = to.rowRange(0, inliers_count);
Mat Hvec = H.reshape(1, 6);
createLMSolver(makePtr<Affine2DRefineCallback>(src, dst), static_cast<int>(refineIters))->run(Hvec);
}
}
if (!result)
{
H.release();
if(_inliers.needed())
{
inliers = Mat::zeros(count, 1, CV_8U);
inliers.copyTo(_inliers);
}
}
return H;
}
Mat estimateAffinePartial2D(InputArray _from, InputArray _to, OutputArray _inliers,
const int method, const double ransacReprojThreshold,
const size_t maxIters, const double confidence,
const size_t refineIters)
{
Mat from = _from.getMat(), to = _to.getMat();
const int count = from.checkVector(2);
bool result = false;
Mat H;
CV_Assert( count >= 0 && to.checkVector(2) == count );
if (from.type() != CV_32FC2 || to.type() != CV_32FC2)
{
Mat tmp1, tmp2;
from.convertTo(tmp1, CV_32FC2);
from = tmp1;
to.convertTo(tmp2, CV_32FC2);
to = tmp2;
}
else
{
// avoid changing of inputs in compressElems() call
from = from.clone();
to = to.clone();
}
// convert to N x 1 vectors
from = from.reshape(2, count);
to = to.reshape(2, count);
Mat inliers;
if(_inliers.needed())
{
_inliers.create(count, 1, CV_8U, -1, true);
inliers = _inliers.getMat();
}
// run robust estimation
Ptr<PointSetRegistrator::Callback> cb = makePtr<AffinePartial2DEstimatorCallback>();
if( method == RANSAC )
result = createRANSACPointSetRegistrator(cb, 2, ransacReprojThreshold, confidence, static_cast<int>(maxIters))->run(from, to, H, inliers);
else if( method == LMEDS )
result = createLMeDSPointSetRegistrator(cb, 2, confidence, static_cast<int>(maxIters))->run(from, to, H, inliers);
else
CV_Error(Error::StsBadArg, "Unknown or unsupported robust estimation method");
if(result && count > 2 && refineIters)
{
// reorder to start with inliers
compressElems(from.ptr<Point2f>(), inliers.ptr<uchar>(), 1, count);
int inliers_count = compressElems(to.ptr<Point2f>(), inliers.ptr<uchar>(), 1, count);
if(inliers_count > 0)
{
Mat src = from.rowRange(0, inliers_count);
Mat dst = to.rowRange(0, inliers_count);
// H is
// a -b tx
// b a ty
// Hvec model for LevMarq is
// (a, b, tx, ty)
double *Hptr = H.ptr<double>();
double Hvec_buf[4] = {Hptr[0], Hptr[3], Hptr[2], Hptr[5]};
Mat Hvec (4, 1, CV_64F, Hvec_buf);
createLMSolver(makePtr<AffinePartial2DRefineCallback>(src, dst), static_cast<int>(refineIters))->run(Hvec);
// update H with refined parameters
Hptr[0] = Hptr[4] = Hvec_buf[0];
Hptr[1] = -Hvec_buf[1];
Hptr[2] = Hvec_buf[2];
Hptr[3] = Hvec_buf[1];
Hptr[5] = Hvec_buf[3];
}
}
if (!result)
{
H.release();
if(_inliers.needed())
{
inliers = Mat::zeros(count, 1, CV_8U);
inliers.copyTo(_inliers);
}
}
return H;
}
} // namespace cv
+7 -6
View File
@@ -61,13 +61,13 @@ static void orderContours(const std::vector<std::vector<Point> >& contours, Poin
for(i = 0; i < n; i++)
{
size_t ni = contours[i].size();
double min_dist = std::numeric_limits<double>::max();
float min_dist = std::numeric_limits<float>::max();
for(j = 0; j < ni; j++)
{
double dist = norm(Point2f((float)contours[i][j].x, (float)contours[i][j].y) - point);
min_dist = MIN(min_dist, dist);
min_dist = (float)MIN((double)min_dist, dist);
}
order.push_back(std::pair<int, float>((int)i, (float)min_dist));
order.push_back(std::pair<int, float>((int)i, min_dist));
}
std::sort(order.begin(), order.end(), is_smaller);
@@ -163,6 +163,8 @@ static int segment_hist_max(const Mat& hist, int& low_thresh, int& high_thresh)
bool cv::find4QuadCornerSubpix(InputArray _img, InputOutputArray _corners, Size region_size)
{
CV_INSTRUMENT_REGION();
Mat img = _img.getMat(), cornersM = _corners.getMat();
int ncorners = cornersM.checkVector(2, CV_32F);
CV_Assert( ncorners >= 0 );
@@ -192,9 +194,8 @@ bool cv::find4QuadCornerSubpix(InputArray _img, InputOutputArray _corners, Size
erode(white_comp, white_comp, Mat(), Point(-1, -1), erode_count);
std::vector<std::vector<Point> > white_contours, black_contours;
std::vector<Vec4i> white_hierarchy, black_hierarchy;
findContours(black_comp, black_contours, black_hierarchy, RETR_LIST, CHAIN_APPROX_SIMPLE);
findContours(white_comp, white_contours, white_hierarchy, RETR_LIST, CHAIN_APPROX_SIMPLE);
findContours(black_comp, black_contours, RETR_LIST, CHAIN_APPROX_SIMPLE);
findContours(white_comp, white_contours, RETR_LIST, CHAIN_APPROX_SIMPLE);
if(black_contours.size() < 5 || white_contours.size() < 5) continue;
File diff suppressed because it is too large Load Diff
+267
View File
@@ -0,0 +1,267 @@
/*
IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
By downloading, copying, installing or using the software you agree to this license.
If you do not agree to this license, do not download, install,
copy or use the software.
BSD 3-Clause License
Copyright (C) 2014, Olexa Bilaniuk, Hamid Bazargani & Robert Laganiere, all rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistribution's of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistribution's 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.
* The name of the copyright holders may not 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 Intel Corporation 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.
*/
/**
* Bilaniuk, Olexa, Hamid Bazargani, and Robert Laganiere. "Fast Target
* Recognition on Mobile Devices: Revisiting Gaussian Elimination for the
* Estimation of Planar Homographies." In Computer Vision and Pattern
* Recognition Workshops (CVPRW), 2014 IEEE Conference on, pp. 119-125.
* IEEE, 2014.
*/
/* Include Guards */
#ifndef __OPENCV_RHO_H__
#define __OPENCV_RHO_H__
/* Includes */
#include <opencv2/core.hpp>
/* Defines */
/* Flags */
#ifndef RHO_FLAG_NONE
#define RHO_FLAG_NONE (0U<<0)
#endif
#ifndef RHO_FLAG_ENABLE_NR
#define RHO_FLAG_ENABLE_NR (1U<<0)
#endif
#ifndef RHO_FLAG_ENABLE_REFINEMENT
#define RHO_FLAG_ENABLE_REFINEMENT (1U<<1)
#endif
#ifndef RHO_FLAG_ENABLE_FINAL_REFINEMENT
#define RHO_FLAG_ENABLE_FINAL_REFINEMENT (1U<<2)
#endif
/* Namespace cv */
namespace cv{
/* Data structures */
/**
* Homography Estimation context.
*/
struct RHO_HEST;
typedef struct RHO_HEST RHO_HEST;
/* Functions */
/**
* Initialize the estimator context, by allocating the aligned buffers
* internally needed.
*
* @return A pointer to the context if successful; NULL if an error occurred.
*/
Ptr<RHO_HEST> rhoInit(void);
/**
* Ensure that the estimator context's internal table for non-randomness
* criterion is at least of the given size, and uses the given beta. The table
* should be larger than the maximum number of matches fed into the estimator.
*
* A value of N of 0 requests deallocation of the table.
*
* @param [in] p The initialized estimator context
* @param [in] N If 0, deallocate internal table. If > 0, ensure that the
* internal table is of at least this size, reallocating if
* necessary.
* @param [in] beta The beta-factor to use within the table.
* @return 0 if unsuccessful; non-zero otherwise.
*/
int rhoEnsureCapacity(Ptr<RHO_HEST> p, unsigned N, double beta);
/**
* Seeds the internal PRNG with the given seed.
*
* Although it is not required to call this function, since context
* initialization seeds itself with entropy from rand(), this function allows
* reproducible results by using a specified seed.
*
* @param [in] p The estimator context whose PRNG is to be seeded.
* @param [in] seed The 64-bit integer seed.
*/
void rhoSeed(Ptr<RHO_HEST> p, uint64_t seed);
/**
* Estimates the homography using the given context, matches and parameters to
* PROSAC.
*
* The given context must have been initialized.
*
* The matches are provided as two arrays of N single-precision, floating-point
* (x,y) points. Points with corresponding offsets in the two arrays constitute
* a match. The homography estimation attempts to find the 3x3 matrix H which
* best maps the homogeneous-coordinate points in the source array to their
* corresponding homogeneous-coordinate points in the destination array.
*
* Note: At least 4 matches must be provided (N >= 4).
* Note: A point in either array takes up 2 floats. The first of two stores
* the x-coordinate and the second of the two stores the y-coordinate.
* Thus, the arrays resemble this in memory:
*
* src = [x0, y0, x1, y1, x2, y2, x3, y3, x4, y4, ...]
* Matches: | | | | |
* dst = [x0, y0, x1, y1, x2, y2, x3, y3, x4, y4, ...]
* Note: The matches are expected to be provided sorted by quality, or at
* least not be worse-than-random in ordering.
*
* A pointer to the base of an array of N bytes can be provided. It serves as
* an output mask to indicate whether the corresponding match is an inlier to
* the returned homography, if any. A zero indicates an outlier; A non-zero
* value indicates an inlier.
*
* The PROSAC estimator requires a few parameters of its own. These are:
*
* - The maximum distance that a source point projected onto the destination
* plane can be from its putative match and still be considered an
* inlier. Must be non-negative.
* A sane default is 3.0.
* - The maximum number of PROSAC iterations. This corresponds to the
* largest number of samples that will be drawn and tested.
* A sane default is 2000.
* - The RANSAC convergence parameter. This corresponds to the number of
* iterations after which PROSAC will start sampling like RANSAC.
* A sane default is 2000.
* - The confidence threshold. This corresponds to the probability of
* finding a correct solution. Must be bounded by [0, 1].
* A sane default is 0.995.
* - The minimum number of inliers acceptable. Only a solution with at
* least this many inliers will be returned. The minimum is 4.
* A sane default is 10% of N.
* - The beta-parameter for the non-randomness termination criterion.
* Ignored if non-randomness criterion disabled, otherwise must be
* bounded by (0, 1).
* A sane default is 0.35.
* - Optional flags to control the estimation. Available flags are:
* HEST_FLAG_NONE:
* No special processing.
* HEST_FLAG_ENABLE_NR:
* Enable non-randomness criterion. If set, the beta parameter
* must also be set.
* HEST_FLAG_ENABLE_REFINEMENT:
* Enable refinement of each new best model, as they are found.
* HEST_FLAG_ENABLE_FINAL_REFINEMENT:
* Enable one final refinement of the best model found before
* returning it.
*
* The PROSAC estimator optionally accepts an extrinsic initial guess of H.
*
* The PROSAC estimator outputs a final estimate of H provided it was able to
* find one with a minimum of supporting inliers. If it was not, it outputs
* the all-zero matrix.
*
* The extrinsic guess at and final estimate of H are both in the same form:
* A 3x3 single-precision floating-point matrix with step 3. Thus, it is a
* 9-element array of floats, with the elements as follows:
*
* [ H00, H01, H02,
* H10, H11, H12,
* H20, H21, 1.0 ]
*
* Notice that the homography is normalized to H22 = 1.0.
*
* The function returns the number of inliers if it was able to find a
* homography with at least the minimum required support, and 0 if it was not.
*
*
* @param [in/out] p The context to use for homography estimation. Must
* be already initialized. Cannot be NULL.
* @param [in] src The pointer to the source points of the matches.
* Must be aligned to 4 bytes. Cannot be NULL.
* @param [in] dst The pointer to the destination points of the matches.
* Must be aligned to 4 bytes. Cannot be NULL.
* @param [out] inl The pointer to the output mask of inlier matches.
* Must be aligned to 4 bytes. May be NULL.
* @param [in] N The number of matches. Minimum 4.
* @param [in] maxD The maximum distance. Minimum 0.
* @param [in] maxI The maximum number of PROSAC iterations.
* @param [in] rConvg The RANSAC convergence parameter.
* @param [in] cfd The required confidence in the solution.
* @param [in] minInl The minimum required number of inliers. Minimum 4.
* @param [in] beta The beta-parameter for the non-randomness criterion.
* @param [in] flags A union of flags to fine-tune the estimation.
* @param [in] guessH An extrinsic guess at the solution H, or NULL if
* none provided.
* @param [out] finalH The final estimation of H, or the zero matrix if
* the minimum number of inliers was not met.
* Cannot be NULL.
* @return The number of inliers if the minimum number of
* inliers for acceptance was reached; 0 otherwise.
*/
unsigned rhoHest(Ptr<RHO_HEST> p, /* Homography estimation context. */
const float* src, /* Source points */
const float* dst, /* Destination points */
char* inl, /* Inlier mask */
unsigned N, /* = src.length = dst.length = inl.length */
float maxD, /* 3.0 */
unsigned maxI, /* 2000 */
unsigned rConvg, /* 2000 */
double cfd, /* 0.995 */
unsigned minInl, /* 4 */
double beta, /* 0.35 */
unsigned flags, /* 0 */
const float* guessH, /* Extrinsic guess, NULL if none provided */
float* finalH); /* Final result. */
/* End Namespace cv */
}
#endif
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+31 -91
View File
@@ -63,8 +63,7 @@ cvTriangulatePoints(CvMat* projMatr1, CvMat* projMatr2, CvMat* projPoints1, CvMa
!CV_IS_MAT(points4D) )
CV_Error( CV_StsUnsupportedFormat, "Input parameters must be matrices" );
int numPoints;
numPoints = projPoints1->cols;
int numPoints = projPoints1->cols;
if( numPoints < 1 )
CV_Error( CV_StsOutOfRange, "Number of points must be more than zero" );
@@ -82,104 +81,39 @@ cvTriangulatePoints(CvMat* projMatr1, CvMat* projMatr2, CvMat* projPoints1, CvMa
projMatr2->cols != 4 || projMatr2->rows != 3)
CV_Error( CV_StsUnmatchedSizes, "Size of projection matrices must be 3x4" );
CvMat matrA;
double matrA_dat[24];
matrA = cvMat(6,4,CV_64F,matrA_dat);
// preallocate SVD matrices on stack
cv::Matx<double, 4, 4> matrA;
cv::Matx<double, 4, 4> matrU;
cv::Matx<double, 4, 1> matrW;
cv::Matx<double, 4, 4> matrV;
//CvMat matrU;
CvMat matrW;
CvMat matrV;
//double matrU_dat[9*9];
double matrW_dat[6*4];
double matrV_dat[4*4];
//matrU = cvMat(6,6,CV_64F,matrU_dat);
matrW = cvMat(6,4,CV_64F,matrW_dat);
matrV = cvMat(4,4,CV_64F,matrV_dat);
CvMat* projPoints[2];
CvMat* projMatrs[2];
projPoints[0] = projPoints1;
projPoints[1] = projPoints2;
projMatrs[0] = projMatr1;
projMatrs[1] = projMatr2;
CvMat* projPoints[2] = {projPoints1, projPoints2};
CvMat* projMatrs[2] = {projMatr1, projMatr2};
/* Solve system for each point */
int i,j;
for( i = 0; i < numPoints; i++ )/* For each point */
for( int i = 0; i < numPoints; i++ )/* For each point */
{
/* Fill matrix for current point */
for( j = 0; j < 2; j++ )/* For each view */
for( int j = 0; j < 2; j++ )/* For each view */
{
double x,y;
x = cvmGet(projPoints[j],0,i);
y = cvmGet(projPoints[j],1,i);
for( int k = 0; k < 4; k++ )
{
cvmSet(&matrA, j*3+0, k, x * cvmGet(projMatrs[j],2,k) - cvmGet(projMatrs[j],0,k) );
cvmSet(&matrA, j*3+1, k, y * cvmGet(projMatrs[j],2,k) - cvmGet(projMatrs[j],1,k) );
cvmSet(&matrA, j*3+2, k, x * cvmGet(projMatrs[j],1,k) - y * cvmGet(projMatrs[j],0,k) );
matrA(j*2+0, k) = x * cvmGet(projMatrs[j],2,k) - cvmGet(projMatrs[j],0,k);
matrA(j*2+1, k) = y * cvmGet(projMatrs[j],2,k) - cvmGet(projMatrs[j],1,k);
}
}
/* Solve system for current point */
{
cvSVD(&matrA,&matrW,0,&matrV,CV_SVD_V_T);
cv::SVD::compute(matrA, matrW, matrU, matrV);
/* Copy computed point */
cvmSet(points4D,0,i,cvmGet(&matrV,3,0));/* X */
cvmSet(points4D,1,i,cvmGet(&matrV,3,1));/* Y */
cvmSet(points4D,2,i,cvmGet(&matrV,3,2));/* Z */
cvmSet(points4D,3,i,cvmGet(&matrV,3,3));/* W */
}
/* Copy computed point */
cvmSet(points4D,0,i,matrV(3,0));/* X */
cvmSet(points4D,1,i,matrV(3,1));/* Y */
cvmSet(points4D,2,i,matrV(3,2));/* Z */
cvmSet(points4D,3,i,matrV(3,3));/* W */
}
#if 0
double err = 0;
/* Points was reconstructed. Try to reproject points */
/* We can compute reprojection error if need */
{
int i;
CvMat point3D;
double point3D_dat[4];
point3D = cvMat(4,1,CV_64F,point3D_dat);
CvMat point2D;
double point2D_dat[3];
point2D = cvMat(3,1,CV_64F,point2D_dat);
for( i = 0; i < numPoints; i++ )
{
double W = cvmGet(points4D,3,i);
point3D_dat[0] = cvmGet(points4D,0,i)/W;
point3D_dat[1] = cvmGet(points4D,1,i)/W;
point3D_dat[2] = cvmGet(points4D,2,i)/W;
point3D_dat[3] = 1;
/* !!! Project this point for each camera */
for( int currCamera = 0; currCamera < 2; currCamera++ )
{
cvMatMul(projMatrs[currCamera], &point3D, &point2D);
float x,y;
float xr,yr,wr;
x = (float)cvmGet(projPoints[currCamera],0,i);
y = (float)cvmGet(projPoints[currCamera],1,i);
wr = (float)point2D_dat[2];
xr = (float)(point2D_dat[0]/wr);
yr = (float)(point2D_dat[1]/wr);
float deltaX,deltaY;
deltaX = (float)fabs(x-xr);
deltaY = (float)fabs(y-yr);
err += deltaX*deltaX + deltaY*deltaY;
}
}
}
#endif
}
@@ -347,7 +281,7 @@ cvCorrectMatches(CvMat *F_, CvMat *points1_, CvMat *points2_, CvMat *new_points1
c = cvGetReal2D(RTFTR,2,1);
d = cvGetReal2D(RTFTR,2,2);
// Form the polynomial g(t) = k6*t + k5*t + k4*t + k3*t³ + k2*t² + k1*t + k0
// Form the polynomial g(t) = k6*t^6 + k5*t^5 + k4*t^4 + k3*t^3 + k2*t^2 + k1*t + k0
// from f1, f2, a, b, c and d
cvSetReal2D(polynomial,0,6,( +b*c*c*f1*f1*f1*f1*a-a*a*d*f1*f1*f1*f1*c ));
cvSetReal2D(polynomial,0,5,( +f2*f2*f2*f2*c*c*c*c+2*a*a*f2*f2*c*c-a*a*d*d*f1*f1*f1*f1+b*b*c*c*f1*f1*f1*f1+a*a*a*a ));
@@ -413,6 +347,8 @@ void cv::triangulatePoints( InputArray _projMatr1, InputArray _projMatr2,
InputArray _projPoints1, InputArray _projPoints2,
OutputArray _points4D )
{
CV_INSTRUMENT_REGION();
Mat matr1 = _projMatr1.getMat(), matr2 = _projMatr2.getMat();
Mat points1 = _projPoints1.getMat(), points2 = _projPoints2.getMat();
@@ -422,11 +358,12 @@ void cv::triangulatePoints( InputArray _projMatr1, InputArray _projMatr2,
if((points2.rows == 1 || points2.cols == 1) && points2.channels() == 2)
points2 = points2.reshape(1, static_cast<int>(points2.total())).t();
CvMat cvMatr1 = matr1, cvMatr2 = matr2;
CvMat cvPoints1 = points1, cvPoints2 = points2;
CvMat cvMatr1 = cvMat(matr1), cvMatr2 = cvMat(matr2);
CvMat cvPoints1 = cvMat(points1), cvPoints2 = cvMat(points2);
_points4D.create(4, points1.cols, points1.type());
CvMat cvPoints4D = _points4D.getMat();
Mat cvPoints4D_ = _points4D.getMat();
CvMat cvPoints4D = cvMat(cvPoints4D_);
cvTriangulatePoints(&cvMatr1, &cvMatr2, &cvPoints1, &cvPoints2, &cvPoints4D);
}
@@ -434,15 +371,18 @@ void cv::triangulatePoints( InputArray _projMatr1, InputArray _projMatr2,
void cv::correctMatches( InputArray _F, InputArray _points1, InputArray _points2,
OutputArray _newPoints1, OutputArray _newPoints2 )
{
CV_INSTRUMENT_REGION();
Mat F = _F.getMat();
Mat points1 = _points1.getMat(), points2 = _points2.getMat();
CvMat cvPoints1 = points1, cvPoints2 = points2;
CvMat cvF = F;
CvMat cvPoints1 = cvMat(points1), cvPoints2 = cvMat(points2);
CvMat cvF = cvMat(F);
_newPoints1.create(points1.size(), points1.type());
_newPoints2.create(points2.size(), points2.type());
CvMat cvNewPoints1 = _newPoints1.getMat(), cvNewPoints2 = _newPoints2.getMat();
Mat cvNewPoints1_ = _newPoints1.getMat(), cvNewPoints2_ = _newPoints2.getMat();
CvMat cvNewPoints1 = cvMat(cvNewPoints1_), cvNewPoints2 = cvMat(cvNewPoints2_);
cvCorrectMatches(&cvF, &cvPoints1, &cvPoints2, &cvNewPoints1, &cvNewPoints2);
}
+829
View File
@@ -0,0 +1,829 @@
//M*//////////////////////////////////////////////////////////////////////////////////////
//
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
// By downloading, copying, installing or using the software you agree to this license.
// If you do not agree to this license, do not download, install,
// copy or use the software.
//
//
// License Agreement
// For Open Source Computer Vision Library
//
// Copyright (C) 2000, Intel Corporation, all rights reserved.
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistribution's of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistribution's 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.
//
// * The name of the copyright holders may not 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 Intel Corporation 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.
//
//M*/
/****************************************************************************************\
* Exhaustive Linearization for Robust Camera Pose and Focal Length Estimation.
* Contributed by Edgar Riba
\****************************************************************************************/
#include "precomp.hpp"
#include "upnp.h"
#include <limits>
#if 0 // fix buffer overflow first (FIXIT mark in .cpp file)
using namespace std;
using namespace cv;
upnp::upnp(const Mat& cameraMatrix, const Mat& opoints, const Mat& ipoints)
{
if (cameraMatrix.depth() == CV_32F)
init_camera_parameters<float>(cameraMatrix);
else
init_camera_parameters<double>(cameraMatrix);
number_of_correspondences = std::max(opoints.checkVector(3, CV_32F), opoints.checkVector(3, CV_64F));
pws.resize(3 * number_of_correspondences);
us.resize(2 * number_of_correspondences);
if (opoints.depth() == ipoints.depth())
{
if (opoints.depth() == CV_32F)
init_points<Point3f,Point2f>(opoints, ipoints);
else
init_points<Point3d,Point2d>(opoints, ipoints);
}
else if (opoints.depth() == CV_32F)
init_points<Point3f,Point2d>(opoints, ipoints);
else
init_points<Point3d,Point2f>(opoints, ipoints);
alphas.resize(4 * number_of_correspondences);
pcs.resize(3 * number_of_correspondences);
max_nr = 0;
A1 = NULL;
A2 = NULL;
}
upnp::~upnp()
{
if (A1)
delete[] A1;
if (A2)
delete[] A2;
}
double upnp::compute_pose(Mat& R, Mat& t)
{
choose_control_points();
compute_alphas();
Mat * M = new Mat(2 * number_of_correspondences, 12, CV_64F);
for(int i = 0; i < number_of_correspondences; i++)
{
fill_M(M, 2 * i, &alphas[0] + 4 * i, us[2 * i], us[2 * i + 1]);
}
double mtm[12 * 12], d[12], ut[12 * 12], vt[12 * 12];
Mat MtM = Mat(12, 12, CV_64F, mtm);
Mat D = Mat(12, 1, CV_64F, d);
Mat Ut = Mat(12, 12, CV_64F, ut);
Mat Vt = Mat(12, 12, CV_64F, vt);
MtM = M->t() * (*M);
SVD::compute(MtM, D, Ut, Vt, SVD::MODIFY_A | SVD::FULL_UV);
Mat(Ut.t()).copyTo(Ut);
M->release();
delete M;
double l_6x12[6 * 12], rho[6];
Mat L_6x12 = Mat(6, 12, CV_64F, l_6x12);
Mat Rho = Mat(6, 1, CV_64F, rho);
compute_L_6x12(ut, l_6x12);
compute_rho(rho);
double Betas[3][4], Efs[3][1], rep_errors[3];
double Rs[3][3][3], ts[3][3];
find_betas_and_focal_approx_1(&Ut, &Rho, Betas[1], Efs[1]);
gauss_newton(&L_6x12, &Rho, Betas[1], Efs[1]);
rep_errors[1] = compute_R_and_t(ut, Betas[1], Rs[1], ts[1]);
find_betas_and_focal_approx_2(&Ut, &Rho, Betas[2], Efs[2]);
gauss_newton(&L_6x12, &Rho, Betas[2], Efs[2]);
rep_errors[2] = compute_R_and_t(ut, Betas[2], Rs[2], ts[2]);
int N = 1;
if (rep_errors[2] < rep_errors[1]) N = 2;
Mat(3, 1, CV_64F, ts[N]).copyTo(t);
Mat(3, 3, CV_64F, Rs[N]).copyTo(R);
fu = fv = Efs[N][0];
return fu;
}
void upnp::copy_R_and_t(const double R_src[3][3], const double t_src[3],
double R_dst[3][3], double t_dst[3])
{
for(int i = 0; i < 3; i++) {
for(int j = 0; j < 3; j++)
R_dst[i][j] = R_src[i][j];
t_dst[i] = t_src[i];
}
}
void upnp::estimate_R_and_t(double R[3][3], double t[3])
{
double pc0[3], pw0[3];
pc0[0] = pc0[1] = pc0[2] = 0.0;
pw0[0] = pw0[1] = pw0[2] = 0.0;
for(int i = 0; i < number_of_correspondences; i++) {
const double * pc = &pcs[3 * i];
const double * pw = &pws[3 * i];
for(int j = 0; j < 3; j++) {
pc0[j] += pc[j];
pw0[j] += pw[j];
}
}
for(int j = 0; j < 3; j++) {
pc0[j] /= number_of_correspondences;
pw0[j] /= number_of_correspondences;
}
double abt[3 * 3] = {0}, abt_d[3], abt_u[3 * 3], abt_v[3 * 3];
Mat ABt = Mat(3, 3, CV_64F, abt);
Mat ABt_D = Mat(3, 1, CV_64F, abt_d);
Mat ABt_U = Mat(3, 3, CV_64F, abt_u);
Mat ABt_V = Mat(3, 3, CV_64F, abt_v);
ABt.setTo(0.0);
for(int i = 0; i < number_of_correspondences; i++) {
double * pc = &pcs[3 * i];
double * pw = &pws[3 * i];
for(int j = 0; j < 3; j++) {
abt[3 * j ] += (pc[j] - pc0[j]) * (pw[0] - pw0[0]);
abt[3 * j + 1] += (pc[j] - pc0[j]) * (pw[1] - pw0[1]);
abt[3 * j + 2] += (pc[j] - pc0[j]) * (pw[2] - pw0[2]);
}
}
SVD::compute(ABt, ABt_D, ABt_U, ABt_V, SVD::MODIFY_A);
Mat(ABt_V.t()).copyTo(ABt_V);
for(int i = 0; i < 3; i++)
for(int j = 0; j < 3; j++)
R[i][j] = dot(abt_u + 3 * i, abt_v + 3 * j);
const double det =
R[0][0] * R[1][1] * R[2][2] + R[0][1] * R[1][2] * R[2][0] + R[0][2] * R[1][0] * R[2][1] -
R[0][2] * R[1][1] * R[2][0] - R[0][1] * R[1][0] * R[2][2] - R[0][0] * R[1][2] * R[2][1];
if (det < 0) {
R[2][0] = -R[2][0];
R[2][1] = -R[2][1];
R[2][2] = -R[2][2];
}
t[0] = pc0[0] - dot(R[0], pw0);
t[1] = pc0[1] - dot(R[1], pw0);
t[2] = pc0[2] - dot(R[2], pw0);
}
void upnp::solve_for_sign(void)
{
if (pcs[2] < 0.0) {
for(int i = 0; i < 4; i++)
for(int j = 0; j < 3; j++)
ccs[i][j] = -ccs[i][j];
for(int i = 0; i < number_of_correspondences; i++) {
pcs[3 * i ] = -pcs[3 * i];
pcs[3 * i + 1] = -pcs[3 * i + 1];
pcs[3 * i + 2] = -pcs[3 * i + 2];
}
}
}
double upnp::compute_R_and_t(const double * ut, const double * betas,
double R[3][3], double t[3])
{
compute_ccs(betas, ut);
compute_pcs();
solve_for_sign();
estimate_R_and_t(R, t);
return reprojection_error(R, t);
}
double upnp::reprojection_error(const double R[3][3], const double t[3])
{
double sum2 = 0.0;
for(int i = 0; i < number_of_correspondences; i++) {
double * pw = &pws[3 * i];
double Xc = dot(R[0], pw) + t[0];
double Yc = dot(R[1], pw) + t[1];
double inv_Zc = 1.0 / (dot(R[2], pw) + t[2]);
double ue = uc + fu * Xc * inv_Zc;
double ve = vc + fv * Yc * inv_Zc;
double u = us[2 * i], v = us[2 * i + 1];
sum2 += sqrt( (u - ue) * (u - ue) + (v - ve) * (v - ve) );
}
return sum2 / number_of_correspondences;
}
void upnp::choose_control_points()
{
for (int i = 0; i < 4; ++i)
cws[i][0] = cws[i][1] = cws[i][2] = 0.0;
cws[0][0] = cws[1][1] = cws[2][2] = 1.0;
}
void upnp::compute_alphas()
{
Mat CC = Mat(4, 3, CV_64F, &cws);
Mat PC = Mat(number_of_correspondences, 3, CV_64F, &pws[0]);
Mat ALPHAS = Mat(number_of_correspondences, 4, CV_64F, &alphas[0]);
Mat CC_ = CC.clone().t();
Mat PC_ = PC.clone().t();
Mat row14 = Mat::ones(1, 4, CV_64F);
Mat row1n = Mat::ones(1, number_of_correspondences, CV_64F);
CC_.push_back(row14);
PC_.push_back(row1n);
ALPHAS = Mat( CC_.inv() * PC_ ).t();
}
void upnp::fill_M(Mat * M, const int row, const double * as, const double u, const double v)
{
double * M1 = M->ptr<double>(row);
double * M2 = M1 + 12;
for(int i = 0; i < 4; i++) {
M1[3 * i ] = as[i] * fu;
M1[3 * i + 1] = 0.0;
M1[3 * i + 2] = as[i] * (uc - u);
M2[3 * i ] = 0.0;
M2[3 * i + 1] = as[i] * fv;
M2[3 * i + 2] = as[i] * (vc - v);
}
}
void upnp::compute_ccs(const double * betas, const double * ut)
{
for(int i = 0; i < 4; ++i)
ccs[i][0] = ccs[i][1] = ccs[i][2] = 0.0;
int N = 4;
for(int i = 0; i < N; ++i) {
const double * v = ut + 12 * (9 + i);
for(int j = 0; j < 4; ++j)
for(int k = 0; k < 3; ++k)
ccs[j][k] += betas[i] * v[3 * j + k]; // FIXIT: array subscript 144 is outside array bounds of 'double [144]' [-Warray-bounds]
// line 109: double ut[12 * 12]
// line 359: double u[12*12]
}
for (int i = 0; i < 4; ++i) ccs[i][2] *= fu;
}
void upnp::compute_pcs(void)
{
for(int i = 0; i < number_of_correspondences; i++) {
double * a = &alphas[0] + 4 * i;
double * pc = &pcs[0] + 3 * i;
for(int j = 0; j < 3; j++)
pc[j] = a[0] * ccs[0][j] + a[1] * ccs[1][j] + a[2] * ccs[2][j] + a[3] * ccs[3][j];
}
}
void upnp::find_betas_and_focal_approx_1(Mat * Ut, Mat * Rho, double * betas, double * efs)
{
Mat Kmf1 = Mat(12, 1, CV_64F, Ut->ptr<double>(11));
Mat dsq = Mat(6, 1, CV_64F, Rho->ptr<double>(0));
Mat D = compute_constraint_distance_2param_6eq_2unk_f_unk( Kmf1 );
Mat Dt = D.t();
Mat A = Dt * D;
Mat b = Dt * dsq;
Mat x = Mat(2, 1, CV_64F);
solve(A, b, x);
betas[0] = sqrt( abs( x.at<double>(0) ) );
betas[1] = betas[2] = betas[3] = 0.0;
efs[0] = sqrt( abs( x.at<double>(1) ) ) / betas[0];
}
void upnp::find_betas_and_focal_approx_2(Mat * Ut, Mat * Rho, double * betas, double * efs)
{
double u[12*12];
Mat U = Mat(12, 12, CV_64F, u);
Ut->copyTo(U);
Mat Kmf1 = Mat(12, 1, CV_64F, Ut->ptr<double>(10));
Mat Kmf2 = Mat(12, 1, CV_64F, Ut->ptr<double>(11));
Mat dsq = Mat(6, 1, CV_64F, Rho->ptr<double>(0));
Mat D = compute_constraint_distance_3param_6eq_6unk_f_unk( Kmf1, Kmf2 );
Mat A = D;
Mat b = dsq;
double x[6];
Mat X = Mat(6, 1, CV_64F, x);
solve(A, b, X, DECOMP_QR);
double solutions[18][3];
generate_all_possible_solutions_for_f_unk(x, solutions);
// find solution with minimum reprojection error
double min_error = std::numeric_limits<double>::max();
int min_sol = 0;
for (int i = 0; i < 18; ++i) {
betas[3] = solutions[i][0];
betas[2] = solutions[i][1];
betas[1] = betas[0] = 0.0;
fu = fv = solutions[i][2];
double Rs[3][3], ts[3];
double error_i = compute_R_and_t( u, betas, Rs, ts);
if( error_i < min_error)
{
min_error = error_i;
min_sol = i;
}
}
betas[0] = solutions[min_sol][0];
betas[1] = solutions[min_sol][1];
betas[2] = betas[3] = 0.0;
efs[0] = solutions[min_sol][2];
}
Mat upnp::compute_constraint_distance_2param_6eq_2unk_f_unk(const Mat& M1)
{
Mat P = Mat(6, 2, CV_64F);
double m[13];
for (int i = 1; i < 13; ++i) m[i] = *M1.ptr<double>(i-1);
double t1 = pow( m[4], 2 );
double t4 = pow( m[1], 2 );
double t5 = pow( m[5], 2 );
double t8 = pow( m[2], 2 );
double t10 = pow( m[6], 2 );
double t13 = pow( m[3], 2 );
double t15 = pow( m[7], 2 );
double t18 = pow( m[8], 2 );
double t22 = pow( m[9], 2 );
double t26 = pow( m[10], 2 );
double t29 = pow( m[11], 2 );
double t33 = pow( m[12], 2 );
*P.ptr<double>(0,0) = t1 - 2 * m[4] * m[1] + t4 + t5 - 2 * m[5] * m[2] + t8;
*P.ptr<double>(0,1) = t10 - 2 * m[6] * m[3] + t13;
*P.ptr<double>(1,0) = t15 - 2 * m[7] * m[1] + t4 + t18 - 2 * m[8] * m[2] + t8;
*P.ptr<double>(1,1) = t22 - 2 * m[9] * m[3] + t13;
*P.ptr<double>(2,0) = t26 - 2 * m[10] * m[1] + t4 + t29 - 2 * m[11] * m[2] + t8;
*P.ptr<double>(2,1) = t33 - 2 * m[12] * m[3] + t13;
*P.ptr<double>(3,0) = t15 - 2 * m[7] * m[4] + t1 + t18 - 2 * m[8] * m[5] + t5;
*P.ptr<double>(3,1) = t22 - 2 * m[9] * m[6] + t10;
*P.ptr<double>(4,0) = t26 - 2 * m[10] * m[4] + t1 + t29 - 2 * m[11] * m[5] + t5;
*P.ptr<double>(4,1) = t33 - 2 * m[12] * m[6] + t10;
*P.ptr<double>(5,0) = t26 - 2 * m[10] * m[7] + t15 + t29 - 2 * m[11] * m[8] + t18;
*P.ptr<double>(5,1) = t33 - 2 * m[12] * m[9] + t22;
return P;
}
Mat upnp::compute_constraint_distance_3param_6eq_6unk_f_unk(const Mat& M1, const Mat& M2)
{
Mat P = Mat(6, 6, CV_64F);
double m[3][13];
for (int i = 1; i < 13; ++i)
{
m[1][i] = *M1.ptr<double>(i-1);
m[2][i] = *M2.ptr<double>(i-1);
}
double t1 = pow( m[1][4], 2 );
double t2 = pow( m[1][1], 2 );
double t7 = pow( m[1][5], 2 );
double t8 = pow( m[1][2], 2 );
double t11 = m[1][1] * m[2][1];
double t12 = m[1][5] * m[2][5];
double t15 = m[1][2] * m[2][2];
double t16 = m[1][4] * m[2][4];
double t19 = pow( m[2][4], 2 );
double t22 = pow( m[2][2], 2 );
double t23 = pow( m[2][1], 2 );
double t24 = pow( m[2][5], 2 );
double t28 = pow( m[1][6], 2 );
double t29 = pow( m[1][3], 2 );
double t34 = pow( m[1][3], 2 );
double t36 = m[1][6] * m[2][6];
double t40 = pow( m[2][6], 2 );
double t41 = pow( m[2][3], 2 );
double t47 = pow( m[1][7], 2 );
double t48 = pow( m[1][8], 2 );
double t52 = m[1][7] * m[2][7];
double t55 = m[1][8] * m[2][8];
double t59 = pow( m[2][8], 2 );
double t62 = pow( m[2][7], 2 );
double t64 = pow( m[1][9], 2 );
double t68 = m[1][9] * m[2][9];
double t74 = pow( m[2][9], 2 );
double t78 = pow( m[1][10], 2 );
double t79 = pow( m[1][11], 2 );
double t84 = m[1][10] * m[2][10];
double t87 = m[1][11] * m[2][11];
double t90 = pow( m[2][10], 2 );
double t95 = pow( m[2][11], 2 );
double t99 = pow( m[1][12], 2 );
double t101 = m[1][12] * m[2][12];
double t105 = pow( m[2][12], 2 );
*P.ptr<double>(0,0) = t1 + t2 - 2 * m[1][4] * m[1][1] - 2 * m[1][5] * m[1][2] + t7 + t8;
*P.ptr<double>(0,1) = -2 * m[2][4] * m[1][1] + 2 * t11 + 2 * t12 - 2 * m[1][4] * m[2][1] - 2 * m[2][5] * m[1][2] + 2 * t15 + 2 * t16 - 2 * m[1][5] * m[2][2];
*P.ptr<double>(0,2) = t19 - 2 * m[2][4] * m[2][1] + t22 + t23 + t24 - 2 * m[2][5] * m[2][2];
*P.ptr<double>(0,3) = t28 + t29 - 2 * m[1][6] * m[1][3];
*P.ptr<double>(0,4) = -2 * m[2][6] * m[1][3] + 2 * t34 - 2 * m[1][6] * m[2][3] + 2 * t36;
*P.ptr<double>(0,5) = -2 * m[2][6] * m[2][3] + t40 + t41;
*P.ptr<double>(1,0) = t8 - 2 * m[1][8] * m[1][2] - 2 * m[1][7] * m[1][1] + t47 + t48 + t2;
*P.ptr<double>(1,1) = 2 * t15 - 2 * m[1][8] * m[2][2] - 2 * m[2][8] * m[1][2] + 2 * t52 - 2 * m[1][7] * m[2][1] - 2 * m[2][7] * m[1][1] + 2 * t55 + 2 * t11;
*P.ptr<double>(1,2) = -2 * m[2][8] * m[2][2] + t22 + t23 + t59 - 2 * m[2][7] * m[2][1] + t62;
*P.ptr<double>(1,3) = t29 + t64 - 2 * m[1][9] * m[1][3];
*P.ptr<double>(1,4) = 2 * t34 + 2 * t68 - 2 * m[2][9] * m[1][3] - 2 * m[1][9] * m[2][3];
*P.ptr<double>(1,5) = -2 * m[2][9] * m[2][3] + t74 + t41;
*P.ptr<double>(2,0) = -2 * m[1][11] * m[1][2] + t2 + t8 + t78 + t79 - 2 * m[1][10] * m[1][1];
*P.ptr<double>(2,1) = 2 * t15 - 2 * m[1][11] * m[2][2] + 2 * t84 - 2 * m[1][10] * m[2][1] - 2 * m[2][10] * m[1][1] + 2 * t87 - 2 * m[2][11] * m[1][2]+ 2 * t11;
*P.ptr<double>(2,2) = t90 + t22 - 2 * m[2][10] * m[2][1] + t23 - 2 * m[2][11] * m[2][2] + t95;
*P.ptr<double>(2,3) = -2 * m[1][12] * m[1][3] + t99 + t29;
*P.ptr<double>(2,4) = 2 * t34 + 2 * t101 - 2 * m[2][12] * m[1][3] - 2 * m[1][12] * m[2][3];
*P.ptr<double>(2,5) = t41 + t105 - 2 * m[2][12] * m[2][3];
*P.ptr<double>(3,0) = t48 + t1 - 2 * m[1][8] * m[1][5] + t7 - 2 * m[1][7] * m[1][4] + t47;
*P.ptr<double>(3,1) = 2 * t16 - 2 * m[1][7] * m[2][4] + 2 * t55 + 2 * t52 - 2 * m[1][8] * m[2][5] - 2 * m[2][8] * m[1][5] - 2 * m[2][7] * m[1][4] + 2 * t12;
*P.ptr<double>(3,2) = t24 - 2 * m[2][8] * m[2][5] + t19 - 2 * m[2][7] * m[2][4] + t62 + t59;
*P.ptr<double>(3,3) = -2 * m[1][9] * m[1][6] + t64 + t28;
*P.ptr<double>(3,4) = 2 * t68 + 2 * t36 - 2 * m[2][9] * m[1][6] - 2 * m[1][9] * m[2][6];
*P.ptr<double>(3,5) = t40 + t74 - 2 * m[2][9] * m[2][6];
*P.ptr<double>(4,0) = t1 - 2 * m[1][10] * m[1][4] + t7 + t78 + t79 - 2 * m[1][11] * m[1][5];
*P.ptr<double>(4,1) = 2 * t84 - 2 * m[1][11] * m[2][5] - 2 * m[1][10] * m[2][4] + 2 * t16 - 2 * m[2][11] * m[1][5] + 2 * t87 - 2 * m[2][10] * m[1][4] + 2 * t12;
*P.ptr<double>(4,2) = t19 + t24 - 2 * m[2][10] * m[2][4] - 2 * m[2][11] * m[2][5] + t95 + t90;
*P.ptr<double>(4,3) = t28 - 2 * m[1][12] * m[1][6] + t99;
*P.ptr<double>(4,4) = 2 * t101 + 2 * t36 - 2 * m[2][12] * m[1][6] - 2 * m[1][12] * m[2][6];
*P.ptr<double>(4,5) = t105 - 2 * m[2][12] * m[2][6] + t40;
*P.ptr<double>(5,0) = -2 * m[1][10] * m[1][7] + t47 + t48 + t78 + t79 - 2 * m[1][11] * m[1][8];
*P.ptr<double>(5,1) = 2 * t84 + 2 * t87 - 2 * m[2][11] * m[1][8] - 2 * m[1][10] * m[2][7] - 2 * m[2][10] * m[1][7] + 2 * t55 + 2 * t52 - 2 * m[1][11] * m[2][8];
*P.ptr<double>(5,2) = -2 * m[2][10] * m[2][7] - 2 * m[2][11] * m[2][8] + t62 + t59 + t90 + t95;
*P.ptr<double>(5,3) = t64 - 2 * m[1][12] * m[1][9] + t99;
*P.ptr<double>(5,4) = 2 * t68 - 2 * m[2][12] * m[1][9] - 2 * m[1][12] * m[2][9] + 2 * t101;
*P.ptr<double>(5,5) = t105 - 2 * m[2][12] * m[2][9] + t74;
return P;
}
void upnp::generate_all_possible_solutions_for_f_unk(const double betas[5], double solutions[18][3])
{
int matrix_to_resolve[18][9] = {
{ 2, 0, 0, 1, 1, 0, 2, 0, 2 }, { 2, 0, 0, 1, 1, 0, 1, 1, 2 },
{ 2, 0, 0, 1, 1, 0, 0, 2, 2 }, { 2, 0, 0, 0, 2, 0, 2, 0, 2 },
{ 2, 0, 0, 0, 2, 0, 1, 1, 2 }, { 2, 0, 0, 0, 2, 0, 0, 2, 2 },
{ 2, 0, 0, 2, 0, 2, 1, 1, 2 }, { 2, 0, 0, 2, 0, 2, 0, 2, 2 },
{ 2, 0, 0, 1, 1, 2, 0, 2, 2 }, { 1, 1, 0, 0, 2, 0, 2, 0, 2 },
{ 1, 1, 0, 0, 2, 0, 1, 1, 2 }, { 1, 1, 0, 2, 0, 2, 0, 2, 2 },
{ 1, 1, 0, 2, 0, 2, 1, 1, 2 }, { 1, 1, 0, 2, 0, 2, 0, 2, 2 },
{ 1, 1, 0, 1, 1, 2, 0, 2, 2 }, { 0, 2, 0, 2, 0, 2, 1, 1, 2 },
{ 0, 2, 0, 2, 0, 2, 0, 2, 2 }, { 0, 2, 0, 1, 1, 2, 0, 2, 2 }
};
int combination[18][3] = {
{ 1, 2, 4 }, { 1, 2, 5 }, { 1, 2, 6 }, { 1, 3, 4 },
{ 1, 3, 5 }, { 1, 3, 6 }, { 1, 4, 5 }, { 1, 4, 6 },
{ 1, 5, 6 }, { 2, 3, 4 }, { 2, 3, 5 }, { 2, 3, 6 },
{ 2, 4, 5 }, { 2, 4, 6 }, { 2, 5, 6 }, { 3, 4, 5 },
{ 3, 4, 6 }, { 3, 5, 6 }
};
for (int i = 0; i < 18; ++i) {
double matrix[9], independent_term[3];
Mat M = Mat(3, 3, CV_64F, matrix);
Mat I = Mat(3, 1, CV_64F, independent_term);
Mat S = Mat(1, 3, CV_64F);
for (int j = 0; j < 9; ++j) matrix[j] = (double)matrix_to_resolve[i][j];
independent_term[0] = log( abs( betas[ combination[i][0]-1 ] ) );
independent_term[1] = log( abs( betas[ combination[i][1]-1 ] ) );
independent_term[2] = log( abs( betas[ combination[i][2]-1 ] ) );
exp( Mat(M.inv() * I), S);
solutions[i][0] = S.at<double>(0);
solutions[i][1] = S.at<double>(1) * sign( betas[1] );
solutions[i][2] = abs( S.at<double>(2) );
}
}
void upnp::gauss_newton(const Mat * L_6x12, const Mat * Rho, double betas[4], double * f)
{
const int iterations_number = 50;
double a[6*4], b[6], x[4] = {0};
Mat * A = new Mat(6, 4, CV_64F, a);
Mat * B = new Mat(6, 1, CV_64F, b);
Mat * X = new Mat(4, 1, CV_64F, x);
for(int k = 0; k < iterations_number; k++)
{
compute_A_and_b_gauss_newton(L_6x12->ptr<double>(0), Rho->ptr<double>(0), betas, A, B, f[0]);
qr_solve(A, B, X);
for(int i = 0; i < 3; i++)
betas[i] += x[i];
f[0] += x[3];
}
if (f[0] < 0) f[0] = -f[0];
fu = fv = f[0];
A->release();
delete A;
B->release();
delete B;
X->release();
delete X;
}
void upnp::compute_A_and_b_gauss_newton(const double * l_6x12, const double * rho,
const double betas[4], Mat * A, Mat * b, double const f)
{
for(int i = 0; i < 6; i++) {
const double * rowL = l_6x12 + i * 12;
double * rowA = A->ptr<double>(i);
rowA[0] = 2 * rowL[0] * betas[0] + rowL[1] * betas[1] + rowL[2] * betas[2] + f*f * ( 2 * rowL[6]*betas[0] + rowL[7]*betas[1] + rowL[8]*betas[2] );
rowA[1] = rowL[1] * betas[0] + 2 * rowL[3] * betas[1] + rowL[4] * betas[2] + f*f * ( rowL[7]*betas[0] + 2 * rowL[9]*betas[1] + rowL[10]*betas[2] );
rowA[2] = rowL[2] * betas[0] + rowL[4] * betas[1] + 2 * rowL[5] * betas[2] + f*f * ( rowL[8]*betas[0] + rowL[10]*betas[1] + 2 * rowL[11]*betas[2] );
rowA[3] = 2*f * ( rowL[6]*betas[0]*betas[0] + rowL[7]*betas[0]*betas[1] + rowL[8]*betas[0]*betas[2] + rowL[9]*betas[1]*betas[1] + rowL[10]*betas[1]*betas[2] + rowL[11]*betas[2]*betas[2] ) ;
*b->ptr<double>(i) = rho[i] -
(
rowL[0] * betas[0] * betas[0] +
rowL[1] * betas[0] * betas[1] +
rowL[2] * betas[0] * betas[2] +
rowL[3] * betas[1] * betas[1] +
rowL[4] * betas[1] * betas[2] +
rowL[5] * betas[2] * betas[2] +
f*f * rowL[6] * betas[0] * betas[0] +
f*f * rowL[7] * betas[0] * betas[1] +
f*f * rowL[8] * betas[0] * betas[2] +
f*f * rowL[9] * betas[1] * betas[1] +
f*f * rowL[10] * betas[1] * betas[2] +
f*f * rowL[11] * betas[2] * betas[2]
);
}
}
void upnp::compute_L_6x12(const double * ut, double * l_6x12)
{
const double * v[3];
v[0] = ut + 12 * 9;
v[1] = ut + 12 * 10;
v[2] = ut + 12 * 11;
double dv[3][6][3];
for(int i = 0; i < 3; i++) {
int a = 0, b = 1;
for(int j = 0; j < 6; j++) {
dv[i][j][0] = v[i][3 * a ] - v[i][3 * b];
dv[i][j][1] = v[i][3 * a + 1] - v[i][3 * b + 1];
dv[i][j][2] = v[i][3 * a + 2] - v[i][3 * b + 2];
b++;
if (b > 3) {
a++;
b = a + 1;
}
}
}
for(int i = 0; i < 6; i++) {
double * row = l_6x12 + 12 * i;
row[0] = dotXY(dv[0][i], dv[0][i]);
row[1] = 2.0f * dotXY(dv[0][i], dv[1][i]);
row[2] = dotXY(dv[1][i], dv[1][i]);
row[3] = 2.0f * dotXY(dv[0][i], dv[2][i]);
row[4] = 2.0f * dotXY(dv[1][i], dv[2][i]);
row[5] = dotXY(dv[2][i], dv[2][i]);
row[6] = dotZ(dv[0][i], dv[0][i]);
row[7] = 2.0f * dotZ(dv[0][i], dv[1][i]);
row[8] = 2.0f * dotZ(dv[0][i], dv[2][i]);
row[9] = dotZ(dv[1][i], dv[1][i]);
row[10] = 2.0f * dotZ(dv[1][i], dv[2][i]);
row[11] = dotZ(dv[2][i], dv[2][i]);
}
}
void upnp::compute_rho(double * rho)
{
rho[0] = dist2(cws[0], cws[1]);
rho[1] = dist2(cws[0], cws[2]);
rho[2] = dist2(cws[0], cws[3]);
rho[3] = dist2(cws[1], cws[2]);
rho[4] = dist2(cws[1], cws[3]);
rho[5] = dist2(cws[2], cws[3]);
}
double upnp::dist2(const double * p1, const double * p2)
{
return
(p1[0] - p2[0]) * (p1[0] - p2[0]) +
(p1[1] - p2[1]) * (p1[1] - p2[1]) +
(p1[2] - p2[2]) * (p1[2] - p2[2]);
}
double upnp::dot(const double * v1, const double * v2)
{
return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2];
}
double upnp::dotXY(const double * v1, const double * v2)
{
return v1[0] * v2[0] + v1[1] * v2[1];
}
double upnp::dotZ(const double * v1, const double * v2)
{
return v1[2] * v2[2];
}
double upnp::sign(const double v)
{
return ( v < 0.0 ) ? -1.0 : ( v > 0.0 ) ? 1.0 : 0.0;
}
void upnp::qr_solve(Mat * A, Mat * b, Mat * X)
{
const int nr = A->rows;
const int nc = A->cols;
if (nr <= 0 || nc <= 0)
return;
if (max_nr != 0 && max_nr < nr)
{
delete [] A1;
delete [] A2;
}
if (max_nr < nr)
{
max_nr = nr;
A1 = new double[nr];
A2 = new double[nr];
}
double * pA = A->ptr<double>(0), * ppAkk = pA;
for(int k = 0; k < nc; k++)
{
double * ppAik1 = ppAkk, eta = fabs(*ppAik1);
for(int i = k + 1; i < nr; i++)
{
double elt = fabs(*ppAik1);
if (eta < elt) eta = elt;
ppAik1 += nc;
}
if (eta == 0)
{
A1[k] = A2[k] = 0.0;
//cerr << "God damnit, A is singular, this shouldn't happen." << endl;
return;
}
else
{
double * ppAik2 = ppAkk, sum2 = 0.0, inv_eta = 1. / eta;
for(int i = k; i < nr; i++)
{
*ppAik2 *= inv_eta;
sum2 += *ppAik2 * *ppAik2;
ppAik2 += nc;
}
double sigma = sqrt(sum2);
if (*ppAkk < 0)
sigma = -sigma;
*ppAkk += sigma;
A1[k] = sigma * *ppAkk;
A2[k] = -eta * sigma;
for(int j = k + 1; j < nc; j++)
{
double * ppAik = ppAkk, sum = 0;
for(int i = k; i < nr; i++)
{
sum += *ppAik * ppAik[j - k];
ppAik += nc;
}
double tau = sum / A1[k];
ppAik = ppAkk;
for(int i = k; i < nr; i++)
{
ppAik[j - k] -= tau * *ppAik;
ppAik += nc;
}
}
}
ppAkk += nc + 1;
}
// b <- Qt b
double * ppAjj = pA, * pb = b->ptr<double>(0);
for(int j = 0; j < nc; j++)
{
double * ppAij = ppAjj, tau = 0;
for(int i = j; i < nr; i++)
{
tau += *ppAij * pb[i];
ppAij += nc;
}
tau /= A1[j];
ppAij = ppAjj;
for(int i = j; i < nr; i++)
{
pb[i] -= tau * *ppAij;
ppAij += nc;
}
ppAjj += nc + 1;
}
// X = R-1 b
double * pX = X->ptr<double>(0);
pX[nc - 1] = pb[nc - 1] / A2[nc - 1];
for(int i = nc - 2; i >= 0; i--)
{
double * ppAij = pA + i * nc + (i + 1), sum = 0;
for(int j = i + 1; j < nc; j++)
{
sum += *ppAij * pX[j];
ppAij++;
}
pX[i] = (pb[i] - sum) / A2[i];
}
}
#endif
+140
View File
@@ -0,0 +1,140 @@
//M*//////////////////////////////////////////////////////////////////////////////////////
//
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
// By downloading, copying, installing or using the software you agree to this license.
// If you do not agree to this license, do not download, install,
// copy or use the software.
//
//
// License Agreement
// For Open Source Computer Vision Library
//
// Copyright (C) 2000, Intel Corporation, all rights reserved.
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistribution's of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistribution's 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.
//
// * The name of the copyright holders may not 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 Intel Corporation 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.
//
//M*/
/****************************************************************************************\
* Exhaustive Linearization for Robust Camera Pose and Focal Length Estimation.
* Contributed by Edgar Riba
\****************************************************************************************/
#ifndef OPENCV_CALIB3D_UPNP_H_
#define OPENCV_CALIB3D_UPNP_H_
#include "precomp.hpp"
#include "opencv2/core/core_c.h"
#include <iostream>
#if 0 // fix buffer overflow first (FIXIT mark in .cpp file)
class upnp
{
public:
upnp(const cv::Mat& cameraMatrix, const cv::Mat& opoints, const cv::Mat& ipoints);
~upnp();
double compute_pose(cv::Mat& R, cv::Mat& t);
private:
upnp(const upnp &); // copy disabled
upnp& operator=(const upnp &); // assign disabled
template <typename T>
void init_camera_parameters(const cv::Mat& cameraMatrix)
{
uc = cameraMatrix.at<T> (0, 2);
vc = cameraMatrix.at<T> (1, 2);
fu = 1;
fv = 1;
}
template <typename OpointType, typename IpointType>
void init_points(const cv::Mat& opoints, const cv::Mat& ipoints)
{
for(int i = 0; i < number_of_correspondences; i++)
{
pws[3 * i ] = opoints.at<OpointType>(i).x;
pws[3 * i + 1] = opoints.at<OpointType>(i).y;
pws[3 * i + 2] = opoints.at<OpointType>(i).z;
us[2 * i ] = ipoints.at<IpointType>(i).x;
us[2 * i + 1] = ipoints.at<IpointType>(i).y;
}
}
double reprojection_error(const double R[3][3], const double t[3]);
void choose_control_points();
void compute_alphas();
void fill_M(cv::Mat * M, const int row, const double * alphas, const double u, const double v);
void compute_ccs(const double * betas, const double * ut);
void compute_pcs(void);
void solve_for_sign(void);
void find_betas_and_focal_approx_1(cv::Mat * Ut, cv::Mat * Rho, double * betas, double * efs);
void find_betas_and_focal_approx_2(cv::Mat * Ut, cv::Mat * Rho, double * betas, double * efs);
void qr_solve(cv::Mat * A, cv::Mat * b, cv::Mat * X);
cv::Mat compute_constraint_distance_2param_6eq_2unk_f_unk(const cv::Mat& M1);
cv::Mat compute_constraint_distance_3param_6eq_6unk_f_unk(const cv::Mat& M1, const cv::Mat& M2);
void generate_all_possible_solutions_for_f_unk(const double betas[5], double solutions[18][3]);
double sign(const double v);
double dot(const double * v1, const double * v2);
double dotXY(const double * v1, const double * v2);
double dotZ(const double * v1, const double * v2);
double dist2(const double * p1, const double * p2);
void compute_rho(double * rho);
void compute_L_6x12(const double * ut, double * l_6x12);
void gauss_newton(const cv::Mat * L_6x12, const cv::Mat * Rho, double current_betas[4], double * efs);
void compute_A_and_b_gauss_newton(const double * l_6x12, const double * rho,
const double cb[4], cv::Mat * A, cv::Mat * b, double const f);
double compute_R_and_t(const double * ut, const double * betas,
double R[3][3], double t[3]);
void estimate_R_and_t(double R[3][3], double t[3]);
void copy_R_and_t(const double R_dst[3][3], const double t_dst[3],
double R_src[3][3], double t_src[3]);
double uc, vc, fu, fv;
std::vector<double> pws, us, alphas, pcs;
int number_of_correspondences;
double cws[4][3], ccs[4][3];
int max_nr;
double * A1, * A2;
};
#endif
#endif // OPENCV_CALIB3D_UPNP_H_
@@ -46,7 +46,7 @@
#ifdef HAVE_OPENCL
namespace cvtest {
namespace opencv_test {
namespace ocl {
PARAM_TEST_CASE(StereoBMFixture, int, int)
@@ -79,7 +79,7 @@ PARAM_TEST_CASE(StereoBMFixture, int, int)
OCL_TEST_P(StereoBMFixture, StereoBM)
{
Ptr<StereoBM> bm = createStereoBM( n_disp, winSize);
Ptr<StereoBM> bm = StereoBM::create( n_disp, winSize);
bm->setPreFilterType(bm->PREFILTER_XSOBEL);
bm->setTextureThreshold(0);
@@ -0,0 +1,197 @@
/*M///////////////////////////////////////////////////////////////////////////////////////
//
// By downloading, copying, installing or using the software you agree to this license.
// If you do not agree to this license, do not download, install,
// copy or use the software.
//
//
// License Agreement
// For Open Source Computer Vision Library
// (3-clause BSD License)
//
// Copyright (C) 2015-2016, OpenCV Foundation, all rights reserved.
// Third party copyrights are property of their respective owners.
//
// 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 names of the copyright holders nor the names of the 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 copyright holders 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.
//
//M*/
#include "test_precomp.hpp"
namespace opencv_test { namespace {
CV_ENUM(Method, RANSAC, LMEDS)
typedef TestWithParam<Method> EstimateAffine2D;
static float rngIn(float from, float to) { return from + (to-from) * (float)theRNG(); }
TEST_P(EstimateAffine2D, test3Points)
{
// try more transformations
for (size_t i = 0; i < 500; ++i)
{
Mat aff(2, 3, CV_64F);
cv::randu(aff, 1., 3.);
Mat fpts(1, 3, CV_32FC2);
Mat tpts(1, 3, CV_32FC2);
// setting points that are not in the same line
fpts.at<Point2f>(0) = Point2f( rngIn(1,2), rngIn(5,6) );
fpts.at<Point2f>(1) = Point2f( rngIn(3,4), rngIn(3,4) );
fpts.at<Point2f>(2) = Point2f( rngIn(1,2), rngIn(3,4) );
transform(fpts, tpts, aff);
vector<uchar> inliers;
Mat aff_est = estimateAffine2D(fpts, tpts, inliers, GetParam() /* method */);
EXPECT_NEAR(0., cvtest::norm(aff_est, aff, NORM_INF), 1e-3);
// all must be inliers
EXPECT_EQ(countNonZero(inliers), 3);
}
}
TEST_P(EstimateAffine2D, testNPoints)
{
// try more transformations
for (size_t i = 0; i < 500; ++i)
{
Mat aff(2, 3, CV_64F);
cv::randu(aff, -2., 2.);
const int method = GetParam();
const int n = 100;
int m;
// LMEDS can't handle more than 50% outliers (by design)
if (method == LMEDS)
m = 3*n/5;
else
m = 2*n/5;
const float shift_outl = 15.f;
const float noise_level = 20.f;
Mat fpts(1, n, CV_32FC2);
Mat tpts(1, n, CV_32FC2);
randu(fpts, 0., 100.);
transform(fpts, tpts, aff);
/* adding noise to some points */
Mat outliers = tpts.colRange(m, n);
outliers.reshape(1) += shift_outl;
Mat noise (outliers.size(), outliers.type());
randu(noise, 0., noise_level);
outliers += noise;
vector<uchar> inliers;
Mat aff_est = estimateAffine2D(fpts, tpts, inliers, method);
EXPECT_FALSE(aff_est.empty()) << "estimation failed, unable to estimate transform";
EXPECT_NEAR(0., cvtest::norm(aff_est, aff, NORM_INF), 1e-4);
bool inliers_good = count(inliers.begin(), inliers.end(), 1) == m &&
m == accumulate(inliers.begin(), inliers.begin() + m, 0);
EXPECT_TRUE(inliers_good);
}
}
// test conversion from other datatypes than float
TEST_P(EstimateAffine2D, testConversion)
{
Mat aff(2, 3, CV_32S);
cv::randu(aff, 1., 3.);
std::vector<Point> fpts(3);
std::vector<Point> tpts(3);
// setting points that are not in the same line
fpts[0] = Point2f( rngIn(1,2), rngIn(5,6) );
fpts[1] = Point2f( rngIn(3,4), rngIn(3,4) );
fpts[2] = Point2f( rngIn(1,2), rngIn(3,4) );
transform(fpts, tpts, aff);
vector<uchar> inliers;
Mat aff_est = estimateAffine2D(fpts, tpts, inliers, GetParam() /* method */);
ASSERT_FALSE(aff_est.empty());
aff.convertTo(aff, CV_64F); // need to convert before compare
EXPECT_NEAR(0., cvtest::norm(aff_est, aff, NORM_INF), 1e-3);
// all must be inliers
EXPECT_EQ(countNonZero(inliers), 3);
}
INSTANTIATE_TEST_CASE_P(Calib3d, EstimateAffine2D, Method::all());
// https://github.com/opencv/opencv/issues/14259
TEST(EstimateAffine2D, issue_14259_dont_change_inputs)
{
/*const static*/ float pts0_[10] = {
0.0f, 0.0f,
0.0f, 8.0f,
4.0f, 0.0f, // outlier
8.0f, 8.0f,
8.0f, 0.0f
};
/*const static*/ float pts1_[10] = {
0.1f, 0.1f,
0.1f, 8.1f,
0.0f, 4.0f, // outlier
8.1f, 8.1f,
8.1f, 0.1f
};
Mat pts0(Size(1, 5), CV_32FC2, (void*)pts0_);
Mat pts1(Size(1, 5), CV_32FC2, (void*)pts1_);
Mat pts0_copy = pts0.clone();
Mat pts1_copy = pts1.clone();
Mat inliers;
cv::Mat A = cv::estimateAffine2D(pts0, pts1, inliers);
for(int i = 0; i < pts0.rows; ++i)
{
EXPECT_EQ(pts0_copy.at<Vec2f>(i), pts0.at<Vec2f>(i)) << "pts0: i=" << i;
}
for(int i = 0; i < pts1.rows; ++i)
{
EXPECT_EQ(pts1_copy.at<Vec2f>(i), pts1.at<Vec2f>(i)) << "pts1: i=" << i;
}
EXPECT_EQ(0, (int)inliers.at<uchar>(2));
}
}} // namespace
+7 -6
View File
@@ -42,21 +42,20 @@
#include "test_precomp.hpp"
#include "opencv2/core/affine.hpp"
#include "opencv2/calib3d.hpp"
#include <iostream>
namespace opencv_test { namespace {
TEST(Calib3d_Affine3f, accuracy)
{
const double eps = 1e-5;
cv::Vec3d rvec(0.2, 0.5, 0.3);
cv::Affine3d affine(rvec);
cv::Mat expected;
cv::Rodrigues(rvec, expected);
ASSERT_EQ(0, cvtest::norm(cv::Mat(affine.matrix, false).colRange(0, 3).rowRange(0, 3) != expected, cv::NORM_L2));
ASSERT_EQ(0, cvtest::norm(cv::Mat(affine.linear()) != expected, cv::NORM_L2));
ASSERT_LE(cvtest::norm(cv::Mat(affine.matrix, false).colRange(0, 3).rowRange(0, 3), expected, cv::NORM_L2), eps);
ASSERT_LE(cvtest::norm(cv::Mat(affine.linear()), expected, cv::NORM_L2), eps);
cv::Matx33d R = cv::Matx33d::eye();
@@ -106,3 +105,5 @@ TEST(Calib3d_Affine3f, accuracy_rvec)
ASSERT_LT(cvtest::norm(va, vo, cv::NORM_L2), 1e-9);
}
}
}} // namespace
@@ -42,16 +42,7 @@
#include "test_precomp.hpp"
using namespace cv;
using namespace std;
#include <string>
#include <iostream>
#include <fstream>
#include <functional>
#include <iterator>
#include <limits>
#include <numeric>
namespace opencv_test { namespace {
class CV_Affine3D_EstTest : public cvtest::BaseTest
{
@@ -80,9 +71,9 @@ struct WrapAff
WrapAff(const Mat& aff) : F(aff.ptr<double>()) {}
Point3f operator()(const Point3f& p)
{
return Point3d( p.x * F[0] + p.y * F[1] + p.z * F[2] + F[3],
p.x * F[4] + p.y * F[5] + p.z * F[6] + F[7],
p.x * F[8] + p.y * F[9] + p.z * F[10] + F[11] );
return Point3f( (float)(p.x * F[0] + p.y * F[1] + p.z * F[2] + F[3]),
(float)(p.x * F[4] + p.y * F[5] + p.z * F[6] + F[7]),
(float)(p.x * F[8] + p.y * F[9] + p.z * F[10] + F[11]) );
}
};
@@ -101,7 +92,7 @@ bool CV_Affine3D_EstTest::test4Points()
fpts.ptr<Point3f>()[2] = Point3f( rngIn(1,2), rngIn(3,4), rngIn(5, 6) );
fpts.ptr<Point3f>()[3] = Point3f( rngIn(3,4), rngIn(1,2), rngIn(5, 6) );
transform(fpts.ptr<Point3f>(), fpts.ptr<Point3f>() + 4, tpts.ptr<Point3f>(), WrapAff(aff));
std::transform(fpts.ptr<Point3f>(), fpts.ptr<Point3f>() + 4, tpts.ptr<Point3f>(), WrapAff(aff));
Mat aff_est;
vector<uchar> outliers;
@@ -144,11 +135,16 @@ bool CV_Affine3D_EstTest::testNPoints()
Mat tpts(1, n, CV_32FC3);
randu(fpts, Scalar::all(0), Scalar::all(100));
transform(fpts.ptr<Point3f>(), fpts.ptr<Point3f>() + n, tpts.ptr<Point3f>(), WrapAff(aff));
std::transform(fpts.ptr<Point3f>(), fpts.ptr<Point3f>() + n, tpts.ptr<Point3f>(), WrapAff(aff));
/* adding noise*/
transform(tpts.ptr<Point3f>() + m, tpts.ptr<Point3f>() + n, tpts.ptr<Point3f>() + m, bind2nd(plus<Point3f>(), shift_outl));
transform(tpts.ptr<Point3f>() + m, tpts.ptr<Point3f>() + n, tpts.ptr<Point3f>() + m, Noise(noise_level));
#ifdef CV_CXX11
std::transform(tpts.ptr<Point3f>() + m, tpts.ptr<Point3f>() + n, tpts.ptr<Point3f>() + m,
[=] (const Point3f& pt) -> Point3f { return Noise(noise_level)(pt + shift_outl); });
#else
std::transform(tpts.ptr<Point3f>() + m, tpts.ptr<Point3f>() + n, tpts.ptr<Point3f>() + m, std::bind2nd(std::plus<Point3f>(), shift_outl));
std::transform(tpts.ptr<Point3f>() + m, tpts.ptr<Point3f>() + n, tpts.ptr<Point3f>() + m, Noise(noise_level));
#endif
Mat aff_est;
vector<uchar> outl;
@@ -194,4 +190,20 @@ void CV_Affine3D_EstTest::run( int /* start_from */)
ts->set_failed_test_info(cvtest::TS::OK);
}
TEST(Calib3d_EstimateAffineTransform, accuracy) { CV_Affine3D_EstTest test; test.safe_run(); }
TEST(Calib3d_EstimateAffine3D, accuracy) { CV_Affine3D_EstTest test; test.safe_run(); }
TEST(Calib3d_EstimateAffine3D, regression_16007)
{
std::vector<cv::Point3f> m1, m2;
m1.push_back(Point3f(1.0f, 0.0f, 0.0f)); m2.push_back(Point3f(1.0f, 1.0f, 0.0f));
m1.push_back(Point3f(1.0f, 0.0f, 1.0f)); m2.push_back(Point3f(1.0f, 1.0f, 1.0f));
m1.push_back(Point3f(0.5f, 0.0f, 0.5f)); m2.push_back(Point3f(0.5f, 1.0f, 0.5f));
m1.push_back(Point3f(2.5f, 0.0f, 2.5f)); m2.push_back(Point3f(2.5f, 1.0f, 2.5f));
m1.push_back(Point3f(2.0f, 0.0f, 1.0f)); m2.push_back(Point3f(2.0f, 1.0f, 1.0f));
cv::Mat m3D, inl;
int res = cv::estimateAffine3D(m1, m2, m3D, inl);
EXPECT_EQ(1, res);
}
}} // namespace
@@ -0,0 +1,206 @@
/*M///////////////////////////////////////////////////////////////////////////////////////
//
// By downloading, copying, installing or using the software you agree to this license.
// If you do not agree to this license, do not download, install,
// copy or use the software.
//
//
// License Agreement
// For Open Source Computer Vision Library
// (3-clause BSD License)
//
// Copyright (C) 2015-2016, OpenCV Foundation, all rights reserved.
// Third party copyrights are property of their respective owners.
//
// 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 names of the copyright holders nor the names of the 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 copyright holders 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.
//
//M*/
#include "test_precomp.hpp"
namespace opencv_test { namespace {
CV_ENUM(Method, RANSAC, LMEDS)
typedef TestWithParam<Method> EstimateAffinePartial2D;
static float rngIn(float from, float to) { return from + (to-from) * (float)theRNG(); }
// get random matrix of affine transformation limited to combinations of translation,
// rotation, and uniform scaling
static Mat rngPartialAffMat() {
double theta = rngIn(0, (float)CV_PI*2.f);
double scale = rngIn(0, 3);
double tx = rngIn(-2, 2);
double ty = rngIn(-2, 2);
double aff[2*3] = { std::cos(theta) * scale, -std::sin(theta) * scale, tx,
std::sin(theta) * scale, std::cos(theta) * scale, ty };
return Mat(2, 3, CV_64F, aff).clone();
}
TEST_P(EstimateAffinePartial2D, test2Points)
{
// try more transformations
for (size_t i = 0; i < 500; ++i)
{
Mat aff = rngPartialAffMat();
// setting points that are no in the same line
Mat fpts(1, 2, CV_32FC2);
Mat tpts(1, 2, CV_32FC2);
fpts.at<Point2f>(0) = Point2f( rngIn(1,2), rngIn(5,6) );
fpts.at<Point2f>(1) = Point2f( rngIn(3,4), rngIn(3,4) );
transform(fpts, tpts, aff);
vector<uchar> inliers;
Mat aff_est = estimateAffinePartial2D(fpts, tpts, inliers, GetParam() /* method */);
EXPECT_NEAR(0., cvtest::norm(aff_est, aff, NORM_INF), 1e-3);
// all must be inliers
EXPECT_EQ(countNonZero(inliers), 2);
}
}
TEST_P(EstimateAffinePartial2D, testNPoints)
{
// try more transformations
for (size_t i = 0; i < 500; ++i)
{
Mat aff = rngPartialAffMat();
const int method = GetParam();
const int n = 100;
int m;
// LMEDS can't handle more than 50% outliers (by design)
if (method == LMEDS)
m = 3*n/5;
else
m = 2*n/5;
const float shift_outl = 15.f;
const float noise_level = 20.f;
Mat fpts(1, n, CV_32FC2);
Mat tpts(1, n, CV_32FC2);
randu(fpts, 0., 100.);
transform(fpts, tpts, aff);
/* adding noise to some points */
Mat outliers = tpts.colRange(m, n);
outliers.reshape(1) += shift_outl;
Mat noise (outliers.size(), outliers.type());
randu(noise, 0., noise_level);
outliers += noise;
vector<uchar> inliers;
Mat aff_est = estimateAffinePartial2D(fpts, tpts, inliers, method);
EXPECT_FALSE(aff_est.empty());
EXPECT_NEAR(0., cvtest::norm(aff_est, aff, NORM_INF), 1e-4);
bool inliers_good = count(inliers.begin(), inliers.end(), 1) == m &&
m == accumulate(inliers.begin(), inliers.begin() + m, 0);
EXPECT_TRUE(inliers_good);
}
}
// test conversion from other datatypes than float
TEST_P(EstimateAffinePartial2D, testConversion)
{
Mat aff = rngPartialAffMat();
aff.convertTo(aff, CV_32S); // convert to int to transform ints properly
std::vector<Point> fpts(3);
std::vector<Point> tpts(3);
fpts[0] = Point2f( rngIn(1,2), rngIn(5,6) );
fpts[1] = Point2f( rngIn(3,4), rngIn(3,4) );
fpts[2] = Point2f( rngIn(1,2), rngIn(3,4) );
transform(fpts, tpts, aff);
vector<uchar> inliers;
Mat aff_est = estimateAffinePartial2D(fpts, tpts, inliers, GetParam() /* method */);
ASSERT_FALSE(aff_est.empty());
aff.convertTo(aff, CV_64F); // need to convert back before compare
EXPECT_NEAR(0., cvtest::norm(aff_est, aff, NORM_INF), 1e-3);
// all must be inliers
EXPECT_EQ(countNonZero(inliers), 3);
}
INSTANTIATE_TEST_CASE_P(Calib3d, EstimateAffinePartial2D, Method::all());
// https://github.com/opencv/opencv/issues/14259
TEST(EstimateAffinePartial2D, issue_14259_dont_change_inputs)
{
/*const static*/ float pts0_[10] = {
0.0f, 0.0f,
0.0f, 8.0f,
4.0f, 0.0f, // outlier
8.0f, 8.0f,
8.0f, 0.0f
};
/*const static*/ float pts1_[10] = {
0.1f, 0.1f,
0.1f, 8.1f,
0.0f, 4.0f, // outlier
8.1f, 8.1f,
8.1f, 0.1f
};
Mat pts0(Size(1, 5), CV_32FC2, (void*)pts0_);
Mat pts1(Size(1, 5), CV_32FC2, (void*)pts1_);
Mat pts0_copy = pts0.clone();
Mat pts1_copy = pts1.clone();
Mat inliers;
cv::Mat A = cv::estimateAffinePartial2D(pts0, pts1, inliers);
for(int i = 0; i < pts0.rows; ++i)
{
EXPECT_EQ(pts0_copy.at<Vec2f>(i), pts0.at<Vec2f>(i)) << "pts0: i=" << i;
}
for(int i = 0; i < pts1.rows; ++i)
{
EXPECT_EQ(pts1_copy.at<Vec2f>(i), pts1.at<Vec2f>(i)) << "pts1: i=" << i;
}
EXPECT_EQ(0, (int)inliers.at<uchar>(2));
}
}} // namespace
@@ -0,0 +1,381 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
#include "test_precomp.hpp"
#include "opencv2/calib3d.hpp"
namespace opencv_test { namespace {
class CV_CalibrateHandEyeTest : public cvtest::BaseTest
{
public:
CV_CalibrateHandEyeTest() {
eps_rvec[CALIB_HAND_EYE_TSAI] = 1.0e-8;
eps_rvec[CALIB_HAND_EYE_PARK] = 1.0e-8;
eps_rvec[CALIB_HAND_EYE_HORAUD] = 1.0e-8;
eps_rvec[CALIB_HAND_EYE_ANDREFF] = 1.0e-8;
eps_rvec[CALIB_HAND_EYE_DANIILIDIS] = 1.0e-8;
eps_tvec[CALIB_HAND_EYE_TSAI] = 1.0e-8;
eps_tvec[CALIB_HAND_EYE_PARK] = 1.0e-8;
eps_tvec[CALIB_HAND_EYE_HORAUD] = 1.0e-8;
eps_tvec[CALIB_HAND_EYE_ANDREFF] = 1.0e-8;
eps_tvec[CALIB_HAND_EYE_DANIILIDIS] = 1.0e-8;
eps_rvec_noise[CALIB_HAND_EYE_TSAI] = 2.0e-2;
eps_rvec_noise[CALIB_HAND_EYE_PARK] = 2.0e-2;
eps_rvec_noise[CALIB_HAND_EYE_HORAUD] = 2.0e-2;
eps_rvec_noise[CALIB_HAND_EYE_ANDREFF] = 1.0e-2;
eps_rvec_noise[CALIB_HAND_EYE_DANIILIDIS] = 1.0e-2;
eps_tvec_noise[CALIB_HAND_EYE_TSAI] = 5.0e-2;
eps_tvec_noise[CALIB_HAND_EYE_PARK] = 5.0e-2;
eps_tvec_noise[CALIB_HAND_EYE_HORAUD] = 5.0e-2;
eps_tvec_noise[CALIB_HAND_EYE_ANDREFF] = 5.0e-2;
eps_tvec_noise[CALIB_HAND_EYE_DANIILIDIS] = 5.0e-2;
}
protected:
virtual void run(int);
void generatePose(RNG& rng, double min_theta, double max_theta,
double min_tx, double max_tx,
double min_ty, double max_ty,
double min_tz, double max_tz,
Mat& R, Mat& tvec,
bool randSign=false);
void simulateData(RNG& rng, int nPoses,
std::vector<Mat> &R_gripper2base, std::vector<Mat> &t_gripper2base,
std::vector<Mat> &R_target2cam, std::vector<Mat> &t_target2cam,
bool noise, Mat& R_cam2gripper, Mat& t_cam2gripper);
Mat homogeneousInverse(const Mat& T);
std::string getMethodName(HandEyeCalibrationMethod method);
double sign_double(double val);
double eps_rvec[5];
double eps_tvec[5];
double eps_rvec_noise[5];
double eps_tvec_noise[5];
};
void CV_CalibrateHandEyeTest::run(int)
{
ts->set_failed_test_info(cvtest::TS::OK);
RNG& rng = ts->get_rng();
std::vector<std::vector<double> > vec_rvec_diff(5);
std::vector<std::vector<double> > vec_tvec_diff(5);
std::vector<std::vector<double> > vec_rvec_diff_noise(5);
std::vector<std::vector<double> > vec_tvec_diff_noise(5);
std::vector<HandEyeCalibrationMethod> methods;
methods.push_back(CALIB_HAND_EYE_TSAI);
methods.push_back(CALIB_HAND_EYE_PARK);
methods.push_back(CALIB_HAND_EYE_HORAUD);
methods.push_back(CALIB_HAND_EYE_ANDREFF);
methods.push_back(CALIB_HAND_EYE_DANIILIDIS);
const int nTests = 100;
for (int i = 0; i < nTests; i++)
{
const int nPoses = 10;
{
//No noise
std::vector<Mat> R_gripper2base, t_gripper2base;
std::vector<Mat> R_target2cam, t_target2cam;
Mat R_cam2gripper_true, t_cam2gripper_true;
const bool noise = false;
simulateData(rng, nPoses, R_gripper2base, t_gripper2base, R_target2cam, t_target2cam, noise, R_cam2gripper_true, t_cam2gripper_true);
for (size_t idx = 0; idx < methods.size(); idx++)
{
Mat rvec_cam2gripper_true;
cv::Rodrigues(R_cam2gripper_true, rvec_cam2gripper_true);
Mat R_cam2gripper_est, t_cam2gripper_est;
calibrateHandEye(R_gripper2base, t_gripper2base, R_target2cam, t_target2cam, R_cam2gripper_est, t_cam2gripper_est, methods[idx]);
Mat rvec_cam2gripper_est;
cv::Rodrigues(R_cam2gripper_est, rvec_cam2gripper_est);
double rvecDiff = cvtest::norm(rvec_cam2gripper_true, rvec_cam2gripper_est, NORM_L2);
double tvecDiff = cvtest::norm(t_cam2gripper_true, t_cam2gripper_est, NORM_L2);
vec_rvec_diff[idx].push_back(rvecDiff);
vec_tvec_diff[idx].push_back(tvecDiff);
const double epsilon_rvec = eps_rvec[idx];
const double epsilon_tvec = eps_tvec[idx];
//Maybe a better accuracy test would be to compare the mean and std errors with some thresholds?
if (rvecDiff > epsilon_rvec || tvecDiff > epsilon_tvec)
{
ts->printf(cvtest::TS::LOG, "Invalid accuracy (no noise) for method: %s, rvecDiff: %f, epsilon_rvec: %f, tvecDiff: %f, epsilon_tvec: %f\n",
getMethodName(methods[idx]).c_str(), rvecDiff, epsilon_rvec, tvecDiff, epsilon_tvec);
ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY);
}
}
}
{
//Gaussian noise on transformations between calibration target frame and camera frame and between gripper and robot base frames
std::vector<Mat> R_gripper2base, t_gripper2base;
std::vector<Mat> R_target2cam, t_target2cam;
Mat R_cam2gripper_true, t_cam2gripper_true;
const bool noise = true;
simulateData(rng, nPoses, R_gripper2base, t_gripper2base, R_target2cam, t_target2cam, noise, R_cam2gripper_true, t_cam2gripper_true);
for (size_t idx = 0; idx < methods.size(); idx++)
{
Mat rvec_cam2gripper_true;
cv::Rodrigues(R_cam2gripper_true, rvec_cam2gripper_true);
Mat R_cam2gripper_est, t_cam2gripper_est;
calibrateHandEye(R_gripper2base, t_gripper2base, R_target2cam, t_target2cam, R_cam2gripper_est, t_cam2gripper_est, methods[idx]);
Mat rvec_cam2gripper_est;
cv::Rodrigues(R_cam2gripper_est, rvec_cam2gripper_est);
double rvecDiff = cvtest::norm(rvec_cam2gripper_true, rvec_cam2gripper_est, NORM_L2);
double tvecDiff = cvtest::norm(t_cam2gripper_true, t_cam2gripper_est, NORM_L2);
vec_rvec_diff_noise[idx].push_back(rvecDiff);
vec_tvec_diff_noise[idx].push_back(tvecDiff);
const double epsilon_rvec = eps_rvec_noise[idx];
const double epsilon_tvec = eps_tvec_noise[idx];
//Maybe a better accuracy test would be to compare the mean and std errors with some thresholds?
if (rvecDiff > epsilon_rvec || tvecDiff > epsilon_tvec)
{
ts->printf(cvtest::TS::LOG, "Invalid accuracy (noise) for method: %s, rvecDiff: %f, epsilon_rvec: %f, tvecDiff: %f, epsilon_tvec: %f\n",
getMethodName(methods[idx]).c_str(), rvecDiff, epsilon_rvec, tvecDiff, epsilon_tvec);
ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY);
}
}
}
}
for (size_t idx = 0; idx < methods.size(); idx++)
{
{
double max_rvec_diff = *std::max_element(vec_rvec_diff[idx].begin(), vec_rvec_diff[idx].end());
double mean_rvec_diff = std::accumulate(vec_rvec_diff[idx].begin(),
vec_rvec_diff[idx].end(), 0.0) / vec_rvec_diff[idx].size();
double sq_sum_rvec_diff = std::inner_product(vec_rvec_diff[idx].begin(), vec_rvec_diff[idx].end(),
vec_rvec_diff[idx].begin(), 0.0);
double std_rvec_diff = std::sqrt(sq_sum_rvec_diff / vec_rvec_diff[idx].size() - mean_rvec_diff * mean_rvec_diff);
double max_tvec_diff = *std::max_element(vec_tvec_diff[idx].begin(), vec_tvec_diff[idx].end());
double mean_tvec_diff = std::accumulate(vec_tvec_diff[idx].begin(),
vec_tvec_diff[idx].end(), 0.0) / vec_tvec_diff[idx].size();
double sq_sum_tvec_diff = std::inner_product(vec_tvec_diff[idx].begin(), vec_tvec_diff[idx].end(),
vec_tvec_diff[idx].begin(), 0.0);
double std_tvec_diff = std::sqrt(sq_sum_tvec_diff / vec_tvec_diff[idx].size() - mean_tvec_diff * mean_tvec_diff);
std::cout << "\nMethod " << getMethodName(methods[idx]) << ":\n"
<< "Max rvec error: " << max_rvec_diff << ", Mean rvec error: " << mean_rvec_diff
<< ", Std rvec error: " << std_rvec_diff << "\n"
<< "Max tvec error: " << max_tvec_diff << ", Mean tvec error: " << mean_tvec_diff
<< ", Std tvec error: " << std_tvec_diff << std::endl;
}
{
double max_rvec_diff = *std::max_element(vec_rvec_diff_noise[idx].begin(), vec_rvec_diff_noise[idx].end());
double mean_rvec_diff = std::accumulate(vec_rvec_diff_noise[idx].begin(),
vec_rvec_diff_noise[idx].end(), 0.0) / vec_rvec_diff_noise[idx].size();
double sq_sum_rvec_diff = std::inner_product(vec_rvec_diff_noise[idx].begin(), vec_rvec_diff_noise[idx].end(),
vec_rvec_diff_noise[idx].begin(), 0.0);
double std_rvec_diff = std::sqrt(sq_sum_rvec_diff / vec_rvec_diff_noise[idx].size() - mean_rvec_diff * mean_rvec_diff);
double max_tvec_diff = *std::max_element(vec_tvec_diff_noise[idx].begin(), vec_tvec_diff_noise[idx].end());
double mean_tvec_diff = std::accumulate(vec_tvec_diff_noise[idx].begin(),
vec_tvec_diff_noise[idx].end(), 0.0) / vec_tvec_diff_noise[idx].size();
double sq_sum_tvec_diff = std::inner_product(vec_tvec_diff_noise[idx].begin(), vec_tvec_diff_noise[idx].end(),
vec_tvec_diff_noise[idx].begin(), 0.0);
double std_tvec_diff = std::sqrt(sq_sum_tvec_diff / vec_tvec_diff_noise[idx].size() - mean_tvec_diff * mean_tvec_diff);
std::cout << "Method (noise) " << getMethodName(methods[idx]) << ":\n"
<< "Max rvec error: " << max_rvec_diff << ", Mean rvec error: " << mean_rvec_diff
<< ", Std rvec error: " << std_rvec_diff << "\n"
<< "Max tvec error: " << max_tvec_diff << ", Mean tvec error: " << mean_tvec_diff
<< ", Std tvec error: " << std_tvec_diff << std::endl;
}
}
}
void CV_CalibrateHandEyeTest::generatePose(RNG& rng, double min_theta, double max_theta,
double min_tx, double max_tx,
double min_ty, double max_ty,
double min_tz, double max_tz,
Mat& R, Mat& tvec,
bool random_sign)
{
Mat axis(3, 1, CV_64FC1);
for (int i = 0; i < 3; i++)
{
axis.at<double>(i,0) = rng.uniform(-1.0, 1.0);
}
double theta = rng.uniform(min_theta, max_theta);
if (random_sign)
{
theta *= sign_double(rng.uniform(-1.0, 1.0));
}
Mat rvec(3, 1, CV_64FC1);
rvec.at<double>(0,0) = theta*axis.at<double>(0,0);
rvec.at<double>(1,0) = theta*axis.at<double>(1,0);
rvec.at<double>(2,0) = theta*axis.at<double>(2,0);
tvec.create(3, 1, CV_64FC1);
tvec.at<double>(0,0) = rng.uniform(min_tx, max_tx);
tvec.at<double>(1,0) = rng.uniform(min_ty, max_ty);
tvec.at<double>(2,0) = rng.uniform(min_tz, max_tz);
if (random_sign)
{
tvec.at<double>(0,0) *= sign_double(rng.uniform(-1.0, 1.0));
tvec.at<double>(1,0) *= sign_double(rng.uniform(-1.0, 1.0));
tvec.at<double>(2,0) *= sign_double(rng.uniform(-1.0, 1.0));
}
cv::Rodrigues(rvec, R);
}
void CV_CalibrateHandEyeTest::simulateData(RNG& rng, int nPoses,
std::vector<Mat> &R_gripper2base, std::vector<Mat> &t_gripper2base,
std::vector<Mat> &R_target2cam, std::vector<Mat> &t_target2cam,
bool noise, Mat& R_cam2gripper, Mat& t_cam2gripper)
{
//to avoid generating values close to zero,
//we use positive range values and randomize the sign
const bool random_sign = true;
generatePose(rng, 10.0*CV_PI/180.0, 50.0*CV_PI/180.0,
0.05, 0.5, 0.05, 0.5, 0.05, 0.5,
R_cam2gripper, t_cam2gripper, random_sign);
Mat R_target2base, t_target2base;
generatePose(rng, 5.0*CV_PI/180.0, 85.0*CV_PI/180.0,
0.5, 3.5, 0.5, 3.5, 0.5, 3.5,
R_target2base, t_target2base, random_sign);
for (int i = 0; i < nPoses; i++)
{
Mat R_gripper2base_, t_gripper2base_;
generatePose(rng, 5.0*CV_PI/180.0, 45.0*CV_PI/180.0,
0.5, 1.5, 0.5, 1.5, 0.5, 1.5,
R_gripper2base_, t_gripper2base_, random_sign);
R_gripper2base.push_back(R_gripper2base_);
t_gripper2base.push_back(t_gripper2base_);
Mat T_cam2gripper = Mat::eye(4, 4, CV_64FC1);
R_cam2gripper.copyTo(T_cam2gripper(Rect(0, 0, 3, 3)));
t_cam2gripper.copyTo(T_cam2gripper(Rect(3, 0, 1, 3)));
Mat T_gripper2base = Mat::eye(4, 4, CV_64FC1);
R_gripper2base_.copyTo(T_gripper2base(Rect(0, 0, 3, 3)));
t_gripper2base_.copyTo(T_gripper2base(Rect(3, 0, 1, 3)));
Mat T_base2cam = homogeneousInverse(T_cam2gripper) * homogeneousInverse(T_gripper2base);
Mat T_target2base = Mat::eye(4, 4, CV_64FC1);
R_target2base.copyTo(T_target2base(Rect(0, 0, 3, 3)));
t_target2base.copyTo(T_target2base(Rect(3, 0, 1, 3)));
Mat T_target2cam = T_base2cam * T_target2base;
if (noise)
{
//Add some noise for the transformation between the target and the camera
Mat R_target2cam_noise = T_target2cam(Rect(0, 0, 3, 3));
Mat rvec_target2cam_noise;
cv::Rodrigues(R_target2cam_noise, rvec_target2cam_noise);
rvec_target2cam_noise.at<double>(0,0) += rng.gaussian(0.002);
rvec_target2cam_noise.at<double>(1,0) += rng.gaussian(0.002);
rvec_target2cam_noise.at<double>(2,0) += rng.gaussian(0.002);
cv::Rodrigues(rvec_target2cam_noise, R_target2cam_noise);
Mat t_target2cam_noise = T_target2cam(Rect(3, 0, 1, 3));
t_target2cam_noise.at<double>(0,0) += rng.gaussian(0.005);
t_target2cam_noise.at<double>(1,0) += rng.gaussian(0.005);
t_target2cam_noise.at<double>(2,0) += rng.gaussian(0.005);
//Add some noise for the transformation between the gripper and the robot base
Mat R_gripper2base_noise = T_gripper2base(Rect(0, 0, 3, 3));
Mat rvec_gripper2base_noise;
cv::Rodrigues(R_gripper2base_noise, rvec_gripper2base_noise);
rvec_gripper2base_noise.at<double>(0,0) += rng.gaussian(0.001);
rvec_gripper2base_noise.at<double>(1,0) += rng.gaussian(0.001);
rvec_gripper2base_noise.at<double>(2,0) += rng.gaussian(0.001);
cv::Rodrigues(rvec_gripper2base_noise, R_gripper2base_noise);
Mat t_gripper2base_noise = T_gripper2base(Rect(3, 0, 1, 3));
t_gripper2base_noise.at<double>(0,0) += rng.gaussian(0.001);
t_gripper2base_noise.at<double>(1,0) += rng.gaussian(0.001);
t_gripper2base_noise.at<double>(2,0) += rng.gaussian(0.001);
}
R_target2cam.push_back(T_target2cam(Rect(0, 0, 3, 3)));
t_target2cam.push_back(T_target2cam(Rect(3, 0, 1, 3)));
}
}
Mat CV_CalibrateHandEyeTest::homogeneousInverse(const Mat& T)
{
CV_Assert( T.rows == 4 && T.cols == 4 );
Mat R = T(Rect(0, 0, 3, 3));
Mat t = T(Rect(3, 0, 1, 3));
Mat Rt = R.t();
Mat tinv = -Rt * t;
Mat Tinv = Mat::eye(4, 4, T.type());
Rt.copyTo(Tinv(Rect(0, 0, 3, 3)));
tinv.copyTo(Tinv(Rect(3, 0, 1, 3)));
return Tinv;
}
std::string CV_CalibrateHandEyeTest::getMethodName(HandEyeCalibrationMethod method)
{
std::string method_name = "";
switch (method)
{
case CALIB_HAND_EYE_TSAI:
method_name = "Tsai";
break;
case CALIB_HAND_EYE_PARK:
method_name = "Park";
break;
case CALIB_HAND_EYE_HORAUD:
method_name = "Horaud";
break;
case CALIB_HAND_EYE_ANDREFF:
method_name = "Andreff";
break;
case CALIB_HAND_EYE_DANIILIDIS:
method_name = "Daniilidis";
break;
default:
break;
}
return method_name;
}
double CV_CalibrateHandEyeTest::sign_double(double val)
{
return (0 < val) - (val < 0);
}
///////////////////////////////////////////////////////////////////////////////////////////////////
TEST(Calib3d_CalibrateHandEye, regression) { CV_CalibrateHandEyeTest test; test.safe_run(); }
}} // namespace
File diff suppressed because it is too large Load Diff
@@ -41,18 +41,9 @@
//M*/
#include "test_precomp.hpp"
#include <string>
#include <limits>
#include <vector>
#include <iostream>
#include <sstream>
#include <iomanip>
#include "test_chessboardgenerator.hpp"
using namespace cv;
using namespace std;
namespace opencv_test { namespace {
//template<class T> ostream& operator<<(ostream& out, const Mat_<T>& mat)
//{
@@ -75,10 +66,10 @@ Mat calcRvec(const vector<Point3f>& points, const Size& cornerSize)
Mat rot(3, 3, CV_64F);
*rot.ptr<Vec3d>(0) = ex;
*rot.ptr<Vec3d>(1) = ey;
*rot.ptr<Vec3d>(2) = ez * (1.0/norm(ez));
*rot.ptr<Vec3d>(2) = ez * (1.0/cv::norm(ez)); // TODO cvtest
Mat res;
Rodrigues(rot.t(), res);
cvtest::Rodrigues(rot.t(), res);
return res.reshape(1, 1);
}
@@ -154,7 +145,7 @@ protected:
if (fail)
{
// commented according to vp123's recomendation. TODO - improve accuaracy
// commented according to vp123's recommendation. TODO - improve accuracy
//ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY); ss
}
ts->printf( cvtest::TS::LOG, "%d) DistCoeff exp=(%.2f, %.2f, %.4f, %.4f %.2f)\n", r, k1, k2, p1, p2, k3);
@@ -174,7 +165,9 @@ protected:
const Point3d& tvec = *tvecs[i].ptr<Point3d>();
const Point3d& tvec_est = *tvecs_est[i].ptr<Point3d>();
if (norm(tvec_est - tvec) > eps* (norm(tvec) + dlt))
double n1 = cv::norm(tvec_est - tvec); // TODO cvtest
double n2 = cv::norm(tvec); // TODO cvtest
if (n1 > eps* (n2 + dlt))
{
if (err_count++ < errMsgNum)
{
@@ -183,7 +176,7 @@ protected:
else
{
ts->printf( cvtest::TS::LOG, "%d) Bad accuracy in returned tvecs. Index = %d\n", r, i);
ts->printf( cvtest::TS::LOG, "%d) norm(tvec_est - tvec) = %f, norm(tvec_exp) = %f \n", r, norm(tvec_est - tvec), norm(tvec));
ts->printf( cvtest::TS::LOG, "%d) norm(tvec_est - tvec) = %f, norm(tvec_exp) = %f \n", r, n1, n2);
}
}
ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY);
@@ -201,8 +194,8 @@ protected:
const int errMsgNum = 4;
for(size_t i = 0; i < rvecs.size(); ++i)
{
Rodrigues(rvecs[i], rmat);
Rodrigues(rvecs_est[i], rmat_est);
cvtest::Rodrigues(rvecs[i], rmat);
cvtest::Rodrigues(rvecs_est[i], rmat_est);
if (cvtest::norm(rmat_est, rmat, NORM_L2) > eps* (cvtest::norm(rmat, NORM_L2) + dlt))
{
@@ -237,7 +230,7 @@ protected:
projectPoints(_chessboard3D, _rvecs_exp[i], _tvecs_exp[i], eye33, zero15, uv_exp);
projectPoints(_chessboard3D, rvecs_est[i], tvecs_est[i], eye33, zero15, uv_est);
for(size_t j = 0; j < cb3d.size(); ++j)
res += norm(uv_exp[i] - uv_est[i]);
res += cv::norm(uv_exp[i] - uv_est[i]); // TODO cvtest
}
return res;
}
@@ -360,7 +353,7 @@ protected:
rvecs_spnp.resize(brdsNum);
tvecs_spnp.resize(brdsNum);
for(size_t i = 0; i < brdsNum; ++i)
solvePnP(Mat(objectPoints[i]), Mat(imagePoints[i]), camMat, distCoeffs, rvecs_spnp[i], tvecs_spnp[i]);
solvePnP(objectPoints[i], imagePoints[i], camMat, distCoeffs, rvecs_spnp[i], tvecs_spnp[i]);
compareShiftVecs(tvecs_exp, tvecs_spnp);
compareRotationVecs(rvecs_exp, rvecs_spnp);
@@ -429,3 +422,5 @@ protected:
};
TEST(Calib3d_CalibrateCamera_CPP, DISABLED_accuracy_on_artificial_data) { CV_CalibrateCameraArtificialTest test; test.safe_run(); }
}} // namespace
@@ -43,10 +43,7 @@
#include "test_chessboardgenerator.hpp"
#include "opencv2/calib3d/calib3d_c.h"
#include <iostream>
using namespace cv;
using namespace std;
namespace opencv_test { namespace {
class CV_CameraCalibrationBadArgTest : public cvtest::BadArgTest
{
@@ -78,7 +75,7 @@ protected:
void operator()() const
{
cvCalibrateCamera2(objPts, imgPts, npoints, imageSize,
cvCalibrateCamera2(objPts, imgPts, npoints, cvSize(imageSize),
cameraMatrix, distCoeffs, rvecs, tvecs, flags );
}
};
@@ -140,13 +137,13 @@ void CV_CameraCalibrationBadArgTest::run( int /* start_from */ )
//CV_CALIB_FIX_PRINCIPAL_POINT //CV_CALIB_ZERO_TANGENT_DIST
//CV_CALIB_FIX_FOCAL_LENGTH //CV_CALIB_FIX_K1 //CV_CALIB_FIX_K2 //CV_CALIB_FIX_K3
objPts = objPts_cpp;
imgPts = imgPts_cpp;
npoints = npoints_cpp;
cameraMatrix = cameraMatrix_cpp;
distCoeffs = distCoeffs_cpp;
rvecs = rvecs_cpp;
tvecs = tvecs_cpp;
objPts = cvMat(objPts_cpp);
imgPts = cvMat(imgPts_cpp);
npoints = cvMat(npoints_cpp);
cameraMatrix = cvMat(cameraMatrix_cpp);
distCoeffs = cvMat(distCoeffs_cpp);
rvecs = cvMat(rvecs_cpp);
tvecs = cvMat(tvecs_cpp);
/* /*//*/ */
int errors = 0;
@@ -181,8 +178,8 @@ void CV_CameraCalibrationBadArgTest::run( int /* start_from */ )
Mat bad_nts_cpp1 = Mat_<float>(M, 1, 1.f);
Mat bad_nts_cpp2 = Mat_<int>(3, 3, corSize.width * corSize.height);
CvMat bad_npts_c1 = bad_nts_cpp1;
CvMat bad_npts_c2 = bad_nts_cpp2;
CvMat bad_npts_c1 = cvMat(bad_nts_cpp1);
CvMat bad_npts_c2 = cvMat(bad_nts_cpp2);
bad_caller = caller;
bad_caller.npoints = &bad_npts_c1;
@@ -200,13 +197,13 @@ void CV_CameraCalibrationBadArgTest::run( int /* start_from */ )
bad_caller.tvecs = (CvMat*)zeros.ptr();
errors += run_test_case( CV_StsBadArg, "Bad tvecs header", bad_caller );
Mat bad_rvecs_cpp1(M+1, 1, CV_32FC3); CvMat bad_rvecs_c1 = bad_rvecs_cpp1;
Mat bad_tvecs_cpp1(M+1, 1, CV_32FC3); CvMat bad_tvecs_c1 = bad_tvecs_cpp1;
Mat bad_rvecs_cpp1(M+1, 1, CV_32FC3); CvMat bad_rvecs_c1 = cvMat(bad_rvecs_cpp1);
Mat bad_tvecs_cpp1(M+1, 1, CV_32FC3); CvMat bad_tvecs_c1 = cvMat(bad_tvecs_cpp1);
Mat bad_rvecs_cpp2(M, 2, CV_32FC3); CvMat bad_rvecs_c2 = bad_rvecs_cpp2;
Mat bad_tvecs_cpp2(M, 2, CV_32FC3); CvMat bad_tvecs_c2 = bad_tvecs_cpp2;
Mat bad_rvecs_cpp2(M, 2, CV_32FC3); CvMat bad_rvecs_c2 = cvMat(bad_rvecs_cpp2);
Mat bad_tvecs_cpp2(M, 2, CV_32FC3); CvMat bad_tvecs_c2 = cvMat(bad_tvecs_cpp2);
bad_caller = caller;
bad_caller.rvecs = &bad_rvecs_c1;
@@ -224,9 +221,9 @@ void CV_CameraCalibrationBadArgTest::run( int /* start_from */ )
bad_caller.tvecs = &bad_tvecs_c2;
errors += run_test_case( CV_StsBadArg, "Bad tvecs header", bad_caller );
Mat bad_cameraMatrix_cpp1(3, 3, CV_32S); CvMat bad_cameraMatrix_c1 = bad_cameraMatrix_cpp1;
Mat bad_cameraMatrix_cpp2(2, 3, CV_32F); CvMat bad_cameraMatrix_c2 = bad_cameraMatrix_cpp2;
Mat bad_cameraMatrix_cpp3(3, 2, CV_64F); CvMat bad_cameraMatrix_c3 = bad_cameraMatrix_cpp3;
Mat bad_cameraMatrix_cpp1(3, 3, CV_32S); CvMat bad_cameraMatrix_c1 = cvMat(bad_cameraMatrix_cpp1);
Mat bad_cameraMatrix_cpp2(2, 3, CV_32F); CvMat bad_cameraMatrix_c2 = cvMat(bad_cameraMatrix_cpp2);
Mat bad_cameraMatrix_cpp3(3, 2, CV_64F); CvMat bad_cameraMatrix_c3 = cvMat(bad_cameraMatrix_cpp3);
@@ -242,9 +239,9 @@ void CV_CameraCalibrationBadArgTest::run( int /* start_from */ )
bad_caller.cameraMatrix = &bad_cameraMatrix_c3;
errors += run_test_case( CV_StsBadArg, "Bad camearaMatrix header", bad_caller );
Mat bad_distCoeffs_cpp1(1, 5, CV_32S); CvMat bad_distCoeffs_c1 = bad_distCoeffs_cpp1;
Mat bad_distCoeffs_cpp2(2, 2, CV_64F); CvMat bad_distCoeffs_c2 = bad_distCoeffs_cpp2;
Mat bad_distCoeffs_cpp3(1, 6, CV_64F); CvMat bad_distCoeffs_c3 = bad_distCoeffs_cpp3;
Mat bad_distCoeffs_cpp1(1, 5, CV_32S); CvMat bad_distCoeffs_c1 = cvMat(bad_distCoeffs_cpp1);
Mat bad_distCoeffs_cpp2(2, 2, CV_64F); CvMat bad_distCoeffs_c2 = cvMat(bad_distCoeffs_cpp2);
Mat bad_distCoeffs_cpp3(1, 6, CV_64F); CvMat bad_distCoeffs_c3 = cvMat(bad_distCoeffs_cpp3);
@@ -262,7 +259,7 @@ void CV_CameraCalibrationBadArgTest::run( int /* start_from */ )
errors += run_test_case( CV_StsBadArg, "Bad distCoeffs header", bad_caller );
double CM[] = {0, 0, 0, /**/0, 0, 0, /**/0, 0, 0};
Mat bad_cameraMatrix_cpp4(3, 3, CV_64F, CM); CvMat bad_cameraMatrix_c4 = bad_cameraMatrix_cpp4;
Mat bad_cameraMatrix_cpp4(3, 3, CV_64F, CM); CvMat bad_cameraMatrix_c4 = cvMat(bad_cameraMatrix_cpp4);
bad_caller = caller;
bad_caller.flags |= CV_CALIB_USE_INTRINSIC_GUESS;
@@ -305,7 +302,7 @@ void CV_CameraCalibrationBadArgTest::run( int /* start_from */ )
/////////////////////////////////////////////////////////////////////////////////////
bad_caller = caller;
Mat bad_objPts_cpp5 = objPts_cpp.clone(); CvMat bad_objPts_c5 = bad_objPts_cpp5;
Mat bad_objPts_cpp5 = objPts_cpp.clone(); CvMat bad_objPts_c5 = cvMat(bad_objPts_cpp5);
bad_caller.objPts = &bad_objPts_c5;
cv::RNG& rng = theRNG();
@@ -350,9 +347,9 @@ protected:
Mat zeros(1, sizeof(CvMat), CV_8U, Scalar(0));
CvMat src_c, dst_c, jacobian_c;
Mat src_cpp(3, 1, CV_32F); src_c = src_cpp;
Mat dst_cpp(3, 3, CV_32F); dst_c = dst_cpp;
Mat jacobian_cpp(3, 9, CV_32F); jacobian_c = jacobian_cpp;
Mat src_cpp(3, 1, CV_32F); src_c = cvMat(src_cpp);
Mat dst_cpp(3, 3, CV_32F); dst_c = cvMat(dst_cpp);
Mat jacobian_cpp(3, 9, CV_32F); jacobian_c = cvMat(jacobian_cpp);
C_Caller caller, bad_caller;
caller.src = &src_c;
@@ -376,11 +373,11 @@ protected:
bad_caller.dst = 0;
errors += run_test_case( CV_StsNullPtr, "Dst is zero pointer", bad_caller );
Mat bad_src_cpp1(3, 1, CV_8U); CvMat bad_src_c1 = bad_src_cpp1;
Mat bad_dst_cpp1(3, 1, CV_8U); CvMat bad_dst_c1 = bad_dst_cpp1;
Mat bad_jac_cpp1(3, 1, CV_8U); CvMat bad_jac_c1 = bad_jac_cpp1;
Mat bad_jac_cpp2(3, 1, CV_32FC2); CvMat bad_jac_c2 = bad_jac_cpp2;
Mat bad_jac_cpp3(3, 1, CV_32F); CvMat bad_jac_c3 = bad_jac_cpp3;
Mat bad_src_cpp1(3, 1, CV_8U); CvMat bad_src_c1 = cvMat(bad_src_cpp1);
Mat bad_dst_cpp1(3, 1, CV_8U); CvMat bad_dst_c1 = cvMat(bad_dst_cpp1);
Mat bad_jac_cpp1(3, 1, CV_8U); CvMat bad_jac_c1 = cvMat(bad_jac_cpp1);
Mat bad_jac_cpp2(3, 1, CV_32FC2); CvMat bad_jac_c2 = cvMat(bad_jac_cpp2);
Mat bad_jac_cpp3(3, 1, CV_32F); CvMat bad_jac_c3 = cvMat(bad_jac_cpp3);
bad_caller = caller;
bad_caller.src = &bad_src_c1;
@@ -406,15 +403,15 @@ protected:
bad_caller.jacobian = &bad_jac_c3;
errors += run_test_case( CV_StsBadSize, "Bad jacobian format", bad_caller );
Mat bad_src_cpp2(1, 1, CV_32F); CvMat bad_src_c2 = bad_src_cpp2;
Mat bad_src_cpp2(1, 1, CV_32F); CvMat bad_src_c2 = cvMat(bad_src_cpp2);
bad_caller = caller;
bad_caller.src = &bad_src_c2;
errors += run_test_case( CV_StsBadSize, "Bad src format", bad_caller );
Mat bad_dst_cpp2(2, 1, CV_32F); CvMat bad_dst_c2 = bad_dst_cpp2;
Mat bad_dst_cpp3(3, 2, CV_32F); CvMat bad_dst_c3 = bad_dst_cpp3;
Mat bad_dst_cpp4(3, 3, CV_32FC2); CvMat bad_dst_c4 = bad_dst_cpp4;
Mat bad_dst_cpp2(2, 1, CV_32F); CvMat bad_dst_c2 = cvMat(bad_dst_cpp2);
Mat bad_dst_cpp3(3, 2, CV_32F); CvMat bad_dst_c3 = cvMat(bad_dst_cpp3);
Mat bad_dst_cpp4(3, 3, CV_32FC2); CvMat bad_dst_c4 = cvMat(bad_dst_cpp4);
bad_caller = caller;
bad_caller.dst = &bad_dst_c2;
@@ -430,11 +427,11 @@ protected:
/********/
src_cpp.create(3, 3, CV_32F); src_c = src_cpp;
dst_cpp.create(3, 1, CV_32F); dst_c = dst_cpp;
src_cpp.create(3, 3, CV_32F); src_c = cvMat(src_cpp);
dst_cpp.create(3, 1, CV_32F); dst_c = cvMat(dst_cpp);
Mat bad_dst_cpp5(5, 5, CV_32F); CvMat bad_dst_c5 = bad_dst_cpp5;
Mat bad_dst_cpp5(5, 5, CV_32F); CvMat bad_dst_c5 = cvMat(bad_dst_cpp5);
bad_caller = caller;
bad_caller.dst = &bad_dst_c5;
@@ -491,8 +488,7 @@ protected:
void run(int /* start_from */ )
{
CvMat zeros;
memset(&zeros, 0, sizeof(zeros));
CvMat zeros = CvMat();
C_Caller caller, bad_caller;
CvMat objectPoints_c, r_vec_c, t_vec_c, A_c, distCoeffs_c, imagePoints_c,
@@ -500,24 +496,24 @@ protected:
const int n = 10;
Mat imagePoints_cpp(1, n, CV_32FC2); imagePoints_c = imagePoints_cpp;
Mat imagePoints_cpp(1, n, CV_32FC2); imagePoints_c = cvMat(imagePoints_cpp);
Mat objectPoints_cpp(1, n, CV_32FC3);
randu(objectPoints_cpp, Scalar::all(1), Scalar::all(10));
objectPoints_c = objectPoints_cpp;
objectPoints_c = cvMat(objectPoints_cpp);
Mat t_vec_cpp(Mat::zeros(1, 3, CV_32F)); t_vec_c = t_vec_cpp;
Mat r_vec_cpp;
Rodrigues(Mat::eye(3, 3, CV_32F), r_vec_cpp); r_vec_c = r_vec_cpp;
Mat t_vec_cpp(Mat::zeros(1, 3, CV_32F)); t_vec_c = cvMat(t_vec_cpp);
Mat r_vec_cpp(3, 1, CV_32F);
cvtest::Rodrigues(Mat::eye(3, 3, CV_32F), r_vec_cpp); r_vec_c = cvMat(r_vec_cpp);
Mat A_cpp = camMat.clone(); A_c = A_cpp;
Mat distCoeffs_cpp = distCoeffs.clone(); distCoeffs_c = distCoeffs_cpp;
Mat A_cpp = camMat.clone(); A_c = cvMat(A_cpp);
Mat distCoeffs_cpp = distCoeffs.clone(); distCoeffs_c = cvMat(distCoeffs_cpp);
Mat dpdr_cpp(2*n, 3, CV_32F); dpdr_c = dpdr_cpp;
Mat dpdt_cpp(2*n, 3, CV_32F); dpdt_c = dpdt_cpp;
Mat dpdf_cpp(2*n, 2, CV_32F); dpdf_c = dpdf_cpp;
Mat dpdc_cpp(2*n, 2, CV_32F); dpdc_c = dpdc_cpp;
Mat dpdk_cpp(2*n, 4, CV_32F); dpdk_c = dpdk_cpp;
Mat dpdr_cpp(2*n, 3, CV_32F); dpdr_c = cvMat(dpdr_cpp);
Mat dpdt_cpp(2*n, 3, CV_32F); dpdt_c = cvMat(dpdt_cpp);
Mat dpdf_cpp(2*n, 2, CV_32F); dpdf_c = cvMat(dpdf_cpp);
Mat dpdc_cpp(2*n, 2, CV_32F); dpdc_c = cvMat(dpdc_cpp);
Mat dpdk_cpp(2*n, 4, CV_32F); dpdk_c = cvMat(dpdk_cpp);
caller.aspectRatio = 1.0;
caller.objectPoints = &objectPoints_c;
@@ -557,9 +553,9 @@ protected:
errors += run_test_case( CV_StsBadArg, "Zero imagePoints", bad_caller );
/****************************/
Mat bad_r_vec_cpp1(r_vec_cpp.size(), CV_32S); CvMat bad_r_vec_c1 = bad_r_vec_cpp1;
Mat bad_r_vec_cpp2(2, 2, CV_32F); CvMat bad_r_vec_c2 = bad_r_vec_cpp2;
Mat bad_r_vec_cpp3(r_vec_cpp.size(), CV_32FC2); CvMat bad_r_vec_c3 = bad_r_vec_cpp3;
Mat bad_r_vec_cpp1(r_vec_cpp.size(), CV_32S); CvMat bad_r_vec_c1 = cvMat(bad_r_vec_cpp1);
Mat bad_r_vec_cpp2(2, 2, CV_32F); CvMat bad_r_vec_c2 = cvMat(bad_r_vec_cpp2);
Mat bad_r_vec_cpp3(r_vec_cpp.size(), CV_32FC2); CvMat bad_r_vec_c3 = cvMat(bad_r_vec_cpp3);
bad_caller = caller;
bad_caller.r_vec = &bad_r_vec_c1;
@@ -574,9 +570,9 @@ protected:
errors += run_test_case( CV_StsBadArg, "Bad rvec format", bad_caller );
/****************************/
Mat bad_t_vec_cpp1(t_vec_cpp.size(), CV_32S); CvMat bad_t_vec_c1 = bad_t_vec_cpp1;
Mat bad_t_vec_cpp2(2, 2, CV_32F); CvMat bad_t_vec_c2 = bad_t_vec_cpp2;
Mat bad_t_vec_cpp3(1, 1, CV_32FC2); CvMat bad_t_vec_c3 = bad_t_vec_cpp3;
Mat bad_t_vec_cpp1(t_vec_cpp.size(), CV_32S); CvMat bad_t_vec_c1 = cvMat(bad_t_vec_cpp1);
Mat bad_t_vec_cpp2(2, 2, CV_32F); CvMat bad_t_vec_c2 = cvMat(bad_t_vec_cpp2);
Mat bad_t_vec_cpp3(1, 1, CV_32FC2); CvMat bad_t_vec_c3 = cvMat(bad_t_vec_cpp3);
bad_caller = caller;
bad_caller.t_vec = &bad_t_vec_c1;
@@ -591,8 +587,8 @@ protected:
errors += run_test_case( CV_StsBadArg, "Bad tvec format", bad_caller );
/****************************/
Mat bad_A_cpp1(A_cpp.size(), CV_32S); CvMat bad_A_c1 = bad_A_cpp1;
Mat bad_A_cpp2(2, 2, CV_32F); CvMat bad_A_c2 = bad_A_cpp2;
Mat bad_A_cpp1(A_cpp.size(), CV_32S); CvMat bad_A_c1 = cvMat(bad_A_cpp1);
Mat bad_A_cpp2(2, 2, CV_32F); CvMat bad_A_c2 = cvMat(bad_A_cpp2);
bad_caller = caller;
bad_caller.A = &bad_A_c1;
@@ -603,9 +599,9 @@ protected:
errors += run_test_case( CV_StsBadArg, "Bad A format", bad_caller );
/****************************/
Mat bad_distCoeffs_cpp1(distCoeffs_cpp.size(), CV_32S); CvMat bad_distCoeffs_c1 = bad_distCoeffs_cpp1;
Mat bad_distCoeffs_cpp2(2, 2, CV_32F); CvMat bad_distCoeffs_c2 = bad_distCoeffs_cpp2;
Mat bad_distCoeffs_cpp3(1, 7, CV_32F); CvMat bad_distCoeffs_c3 = bad_distCoeffs_cpp3;
Mat bad_distCoeffs_cpp1(distCoeffs_cpp.size(), CV_32S); CvMat bad_distCoeffs_c1 = cvMat(bad_distCoeffs_cpp1);
Mat bad_distCoeffs_cpp2(2, 2, CV_32F); CvMat bad_distCoeffs_c2 = cvMat(bad_distCoeffs_cpp2);
Mat bad_distCoeffs_cpp3(1, 7, CV_32F); CvMat bad_distCoeffs_c3 = cvMat(bad_distCoeffs_cpp3);
bad_caller = caller;
bad_caller.distCoeffs = &zeros;
@@ -625,9 +621,9 @@ protected:
/****************************/
Mat bad_dpdr_cpp1(dpdr_cpp.size(), CV_32S); CvMat bad_dpdr_c1 = bad_dpdr_cpp1;
Mat bad_dpdr_cpp2(dpdr_cpp.cols+1, 3, CV_32F); CvMat bad_dpdr_c2 = bad_dpdr_cpp2;
Mat bad_dpdr_cpp3(dpdr_cpp.cols, 7, CV_32F); CvMat bad_dpdr_c3 = bad_dpdr_cpp3;
Mat bad_dpdr_cpp1(dpdr_cpp.size(), CV_32S); CvMat bad_dpdr_c1 = cvMat(bad_dpdr_cpp1);
Mat bad_dpdr_cpp2(dpdr_cpp.cols+1, 3, CV_32F); CvMat bad_dpdr_c2 = cvMat(bad_dpdr_cpp2);
Mat bad_dpdr_cpp3(dpdr_cpp.cols, 7, CV_32F); CvMat bad_dpdr_c3 = cvMat(bad_dpdr_cpp3);
bad_caller = caller;
bad_caller.dpdr = &zeros;
@@ -665,7 +661,7 @@ protected:
/****************************/
Mat bad_dpdf_cpp2(dpdr_cpp.cols+1, 2, CV_32F); CvMat bad_dpdf_c2 = bad_dpdf_cpp2;
Mat bad_dpdf_cpp2(dpdr_cpp.cols+1, 2, CV_32F); CvMat bad_dpdf_c2 = cvMat(bad_dpdf_cpp2);
bad_caller = caller;
bad_caller.dpdf = &zeros;
@@ -735,3 +731,5 @@ protected:
TEST(Calib3d_CalibrateCamera_C, badarg) { CV_CameraCalibrationBadArgTest test; test.safe_run(); }
TEST(Calib3d_Rodrigues_C, badarg) { CV_Rodrigues2BadArgTest test; test.safe_run(); }
TEST(Calib3d_ProjectPoints_C, badarg) { CV_ProjectPoints2BadArgTest test; test.safe_run(); }
}} // namespace
@@ -0,0 +1,693 @@
/*M///////////////////////////////////////////////////////////////////////////////////////
//
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
// By downloading, copying, installing or using the software you agree to this license.
// If you do not agree to this license, do not download, install,
// copy or use the software.
//
//
// License Agreement
// For Open Source Computer Vision Library
//
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
// Copyright (C) 2009-2011, Willow Garage Inc., all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistribution's of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistribution's 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.
//
// * The name of the copyright holders may not 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 Intel Corporation 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.
//
//M*/
#include "test_precomp.hpp"
#include "opencv2/ts/cuda_test.hpp" // EXPECT_MAT_NEAR
namespace opencv_test { namespace {
#define NUM_DIST_COEFF_TILT 14
/**
Some conventions:
- the first camera determines the world coordinate system
- y points down, hence top means minimal y value (negative) and
bottom means maximal y value (positive)
- the field of view plane is tilted around x such that it
intersects the xy-plane in a line with a large (positive)
y-value
- image sensor and object are both modelled in the halfspace
z > 0
**/
class cameraCalibrationTiltTest : public ::testing::Test {
protected:
cameraCalibrationTiltTest()
: m_toRadian(acos(-1.0)/180.0)
, m_toDegree(180.0/acos(-1.0))
{}
virtual void SetUp();
protected:
static const cv::Size m_imageSize;
static const double m_pixelSize;
static const double m_circleConfusionPixel;
static const double m_lensFocalLength;
static const double m_lensFNumber;
static const double m_objectDistance;
static const double m_planeTiltDegree;
static const double m_pointTargetDist;
static const int m_pointTargetNum;
/** image distance corresponding to working distance */
double m_imageDistance;
/** image tilt angle corresponding to the tilt of the object plane */
double m_imageTiltDegree;
/** center of the field of view, near and far plane */
std::vector<cv::Vec3d> m_fovCenter;
/** normal of the field of view, near and far plane */
std::vector<cv::Vec3d> m_fovNormal;
/** points on a plane calibration target */
std::vector<cv::Point3d> m_pointTarget;
/** rotations for the calibration target */
std::vector<cv::Vec3d> m_pointTargetRvec;
/** translations for the calibration target */
std::vector<cv::Vec3d> m_pointTargetTvec;
/** camera matrix */
cv::Matx33d m_cameraMatrix;
/** distortion coefficients */
cv::Vec<double, NUM_DIST_COEFF_TILT> m_distortionCoeff;
/** random generator */
cv::RNG m_rng;
/** degree to radian conversion factor */
const double m_toRadian;
/** radian to degree conversion factor */
const double m_toDegree;
/**
computes for a given distance of an image or object point
the distance of the corresponding object or image point
*/
double opticalMap(double dist) {
return m_lensFocalLength*dist/(dist - m_lensFocalLength);
}
/** magnification of the optical map */
double magnification(double dist) {
return m_lensFocalLength/(dist - m_lensFocalLength);
}
/**
Changes given distortion coefficients randomly by adding
a uniformly distributed random variable in [-max max]
\param coeff input
\param max limits for the random variables
*/
void randomDistortionCoeff(
cv::Vec<double, NUM_DIST_COEFF_TILT>& coeff,
const cv::Vec<double, NUM_DIST_COEFF_TILT>& max)
{
for (int i = 0; i < coeff.rows; ++i)
coeff(i) += m_rng.uniform(-max(i), max(i));
}
/** numerical jacobian */
void numericalDerivative(
cv::Mat& jac,
double eps,
const std::vector<cv::Point3d>& obj,
const cv::Vec3d& rvec,
const cv::Vec3d& tvec,
const cv::Matx33d& camera,
const cv::Vec<double, NUM_DIST_COEFF_TILT>& distor);
/** remove points with projection outside the sensor array */
void removeInvalidPoints(
std::vector<cv::Point2d>& imagePoints,
std::vector<cv::Point3d>& objectPoints);
/** add uniform distribute noise in [-halfWidthNoise, halfWidthNoise]
to the image points and remove out of range points */
void addNoiseRemoveInvalidPoints(
std::vector<cv::Point2f>& imagePoints,
std::vector<cv::Point3f>& objectPoints,
std::vector<cv::Point2f>& noisyImagePoints,
double halfWidthNoise);
};
/** Number of Pixel of the sensor */
const cv::Size cameraCalibrationTiltTest::m_imageSize(1600, 1200);
/** Size of a pixel in mm */
const double cameraCalibrationTiltTest::m_pixelSize(.005);
/** Diameter of the circle of confusion */
const double cameraCalibrationTiltTest::m_circleConfusionPixel(3);
/** Focal length of the lens */
const double cameraCalibrationTiltTest::m_lensFocalLength(16.4);
/** F-Number */
const double cameraCalibrationTiltTest::m_lensFNumber(8);
/** Working distance */
const double cameraCalibrationTiltTest::m_objectDistance(200);
/** Angle between optical axis and object plane normal */
const double cameraCalibrationTiltTest::m_planeTiltDegree(55);
/** the calibration target are points on a square grid with this side length */
const double cameraCalibrationTiltTest::m_pointTargetDist(5);
/** the calibration target has (2*n + 1) x (2*n + 1) points */
const int cameraCalibrationTiltTest::m_pointTargetNum(15);
void cameraCalibrationTiltTest::SetUp()
{
m_imageDistance = opticalMap(m_objectDistance);
m_imageTiltDegree = m_toDegree * atan2(
m_imageDistance * tan(m_toRadian * m_planeTiltDegree),
m_objectDistance);
// half sensor height
double tmp = .5 * (m_imageSize.height - 1) * m_pixelSize
* cos(m_toRadian * m_imageTiltDegree);
// y-Value of tilted sensor
double yImage[2] = {tmp, -tmp};
// change in z because of the tilt
tmp *= sin(m_toRadian * m_imageTiltDegree);
// z-values of the sensor lower and upper corner
double zImage[2] = {
m_imageDistance + tmp,
m_imageDistance - tmp};
// circle of confusion
double circleConfusion = m_circleConfusionPixel*m_pixelSize;
// aperture of the lense
double aperture = m_lensFocalLength/m_lensFNumber;
// near and far factor on the image side
double nearFarFactorImage[2] = {
aperture/(aperture - circleConfusion),
aperture/(aperture + circleConfusion)};
// on the object side - points that determine the field of
// view
std::vector<cv::Vec3d> fovBottomTop(6);
std::vector<cv::Vec3d>::iterator itFov = fovBottomTop.begin();
for (size_t iBottomTop = 0; iBottomTop < 2; ++iBottomTop)
{
// mapping sensor to field of view
*itFov = cv::Vec3d(0,yImage[iBottomTop],zImage[iBottomTop]);
*itFov *= magnification((*itFov)(2));
++itFov;
for (size_t iNearFar = 0; iNearFar < 2; ++iNearFar, ++itFov)
{
// scaling to the near and far distance on the
// image side
*itFov = cv::Vec3d(0,yImage[iBottomTop],zImage[iBottomTop]) *
nearFarFactorImage[iNearFar];
// scaling to the object side
*itFov *= magnification((*itFov)(2));
}
}
m_fovCenter.resize(3);
m_fovNormal.resize(3);
for (size_t i = 0; i < 3; ++i)
{
m_fovCenter[i] = .5*(fovBottomTop[i] + fovBottomTop[i+3]);
m_fovNormal[i] = fovBottomTop[i+3] - fovBottomTop[i];
m_fovNormal[i] = cv::normalize(m_fovNormal[i]);
m_fovNormal[i] = cv::Vec3d(
m_fovNormal[i](0),
-m_fovNormal[i](2),
m_fovNormal[i](1));
// one target position in each plane
m_pointTargetTvec.push_back(m_fovCenter[i]);
cv::Vec3d rvec = cv::Vec3d(0,0,1).cross(m_fovNormal[i]);
rvec = cv::normalize(rvec);
rvec *= acos(m_fovNormal[i](2));
m_pointTargetRvec.push_back(rvec);
}
// calibration target
size_t num = 2*m_pointTargetNum + 1;
m_pointTarget.resize(num*num);
std::vector<cv::Point3d>::iterator itTarget = m_pointTarget.begin();
for (int iY = -m_pointTargetNum; iY <= m_pointTargetNum; ++iY)
{
for (int iX = -m_pointTargetNum; iX <= m_pointTargetNum; ++iX, ++itTarget)
{
*itTarget = cv::Point3d(iX, iY, 0) * m_pointTargetDist;
}
}
// oblique target positions
// approximate distance to the near and far plane
double dist = std::max(
std::abs(m_fovNormal[0].dot(m_fovCenter[0] - m_fovCenter[1])),
std::abs(m_fovNormal[0].dot(m_fovCenter[0] - m_fovCenter[2])));
// maximal angle such that target border "reaches" near and far plane
double maxAngle = atan2(dist, m_pointTargetNum*m_pointTargetDist);
std::vector<double> angle;
angle.push_back(-maxAngle);
angle.push_back(maxAngle);
cv::Matx33d baseMatrix;
cv::Rodrigues(m_pointTargetRvec.front(), baseMatrix);
for (std::vector<double>::const_iterator itAngle = angle.begin(); itAngle != angle.end(); ++itAngle)
{
cv::Matx33d rmat;
for (int i = 0; i < 2; ++i)
{
cv::Vec3d rvec(0,0,0);
rvec(i) = *itAngle;
cv::Rodrigues(rvec, rmat);
rmat = baseMatrix*rmat;
cv::Rodrigues(rmat, rvec);
m_pointTargetTvec.push_back(m_fovCenter.front());
m_pointTargetRvec.push_back(rvec);
}
}
// camera matrix
double cx = .5 * (m_imageSize.width - 1);
double cy = .5 * (m_imageSize.height - 1);
double f = m_imageDistance/m_pixelSize;
m_cameraMatrix = cv::Matx33d(
f,0,cx,
0,f,cy,
0,0,1);
// distortion coefficients
m_distortionCoeff = cv::Vec<double, NUM_DIST_COEFF_TILT>::all(0);
// tauX
m_distortionCoeff(12) = -m_toRadian*m_imageTiltDegree;
}
void cameraCalibrationTiltTest::numericalDerivative(
cv::Mat& jac,
double eps,
const std::vector<cv::Point3d>& obj,
const cv::Vec3d& rvec,
const cv::Vec3d& tvec,
const cv::Matx33d& camera,
const cv::Vec<double, NUM_DIST_COEFF_TILT>& distor)
{
cv::Vec3d r(rvec);
cv::Vec3d t(tvec);
cv::Matx33d cm(camera);
cv::Vec<double, NUM_DIST_COEFF_TILT> dc(distor);
double* param[10+NUM_DIST_COEFF_TILT] = {
&r(0), &r(1), &r(2),
&t(0), &t(1), &t(2),
&cm(0,0), &cm(1,1), &cm(0,2), &cm(1,2),
&dc(0), &dc(1), &dc(2), &dc(3), &dc(4), &dc(5), &dc(6),
&dc(7), &dc(8), &dc(9), &dc(10), &dc(11), &dc(12), &dc(13)};
std::vector<cv::Point2d> pix0, pix1;
double invEps = .5/eps;
for (int col = 0; col < 10+NUM_DIST_COEFF_TILT; ++col)
{
double save = *(param[col]);
*(param[col]) = save + eps;
cv::projectPoints(obj, r, t, cm, dc, pix0);
*(param[col]) = save - eps;
cv::projectPoints(obj, r, t, cm, dc, pix1);
*(param[col]) = save;
std::vector<cv::Point2d>::const_iterator it0 = pix0.begin();
std::vector<cv::Point2d>::const_iterator it1 = pix1.begin();
int row = 0;
for (;it0 != pix0.end(); ++it0, ++it1)
{
cv::Point2d d = invEps*(*it0 - *it1);
jac.at<double>(row, col) = d.x;
++row;
jac.at<double>(row, col) = d.y;
++row;
}
}
}
void cameraCalibrationTiltTest::removeInvalidPoints(
std::vector<cv::Point2d>& imagePoints,
std::vector<cv::Point3d>& objectPoints)
{
// remove object and imgage points out of range
std::vector<cv::Point2d>::iterator itImg = imagePoints.begin();
std::vector<cv::Point3d>::iterator itObj = objectPoints.begin();
while (itImg != imagePoints.end())
{
bool ok =
itImg->x >= 0 &&
itImg->x <= m_imageSize.width - 1.0 &&
itImg->y >= 0 &&
itImg->y <= m_imageSize.height - 1.0;
if (ok)
{
++itImg;
++itObj;
}
else
{
itImg = imagePoints.erase(itImg);
itObj = objectPoints.erase(itObj);
}
}
}
void cameraCalibrationTiltTest::addNoiseRemoveInvalidPoints(
std::vector<cv::Point2f>& imagePoints,
std::vector<cv::Point3f>& objectPoints,
std::vector<cv::Point2f>& noisyImagePoints,
double halfWidthNoise)
{
std::vector<cv::Point2f>::iterator itImg = imagePoints.begin();
std::vector<cv::Point3f>::iterator itObj = objectPoints.begin();
noisyImagePoints.clear();
noisyImagePoints.reserve(imagePoints.size());
while (itImg != imagePoints.end())
{
cv::Point2f pix = *itImg + cv::Point2f(
(float)m_rng.uniform(-halfWidthNoise, halfWidthNoise),
(float)m_rng.uniform(-halfWidthNoise, halfWidthNoise));
bool ok =
pix.x >= 0 &&
pix.x <= m_imageSize.width - 1.0 &&
pix.y >= 0 &&
pix.y <= m_imageSize.height - 1.0;
if (ok)
{
noisyImagePoints.push_back(pix);
++itImg;
++itObj;
}
else
{
itImg = imagePoints.erase(itImg);
itObj = objectPoints.erase(itObj);
}
}
}
TEST_F(cameraCalibrationTiltTest, projectPoints)
{
std::vector<cv::Point2d> imagePoints;
std::vector<cv::Point3d> objectPoints = m_pointTarget;
cv::Vec3d rvec = m_pointTargetRvec.front();
cv::Vec3d tvec = m_pointTargetTvec.front();
cv::Vec<double, NUM_DIST_COEFF_TILT> coeffNoiseHalfWidth(
.1, .1, // k1 k2
.01, .01, // p1 p2
.001, .001, .001, .001, // k3 k4 k5 k6
.001, .001, .001, .001, // s1 s2 s3 s4
.01, .01); // tauX tauY
for (size_t numTest = 0; numTest < 10; ++numTest)
{
// create random distortion coefficients
cv::Vec<double, NUM_DIST_COEFF_TILT> distortionCoeff = m_distortionCoeff;
randomDistortionCoeff(distortionCoeff, coeffNoiseHalfWidth);
// projection
cv::projectPoints(
objectPoints,
rvec,
tvec,
m_cameraMatrix,
distortionCoeff,
imagePoints);
// remove object and imgage points out of range
removeInvalidPoints(imagePoints, objectPoints);
int numPoints = (int)imagePoints.size();
int numParams = 10 + distortionCoeff.rows;
cv::Mat jacobian(2*numPoints, numParams, CV_64FC1);
// projection and jacobian
cv::projectPoints(
objectPoints,
rvec,
tvec,
m_cameraMatrix,
distortionCoeff,
imagePoints,
jacobian);
// numerical derivatives
cv::Mat numericJacobian(2*numPoints, numParams, CV_64FC1);
double eps = 1e-7;
numericalDerivative(
numericJacobian,
eps,
objectPoints,
rvec,
tvec,
m_cameraMatrix,
distortionCoeff);
#if 0
for (size_t row = 0; row < 2; ++row)
{
std::cout << "------ Row = " << row << " ------\n";
for (size_t i = 0; i < 10+NUM_DIST_COEFF_TILT; ++i)
{
std::cout << i
<< " jac = " << jacobian.at<double>(row,i)
<< " num = " << numericJacobian.at<double>(row,i)
<< " rel. diff = " << abs(numericJacobian.at<double>(row,i) - jacobian.at<double>(row,i))/abs(numericJacobian.at<double>(row,i))
<< "\n";
}
}
#endif
// relative difference for large values (rvec and tvec)
cv::Mat check = abs(jacobian(cv::Range::all(), cv::Range(0,6)) - numericJacobian(cv::Range::all(), cv::Range(0,6)))/
(1 + abs(jacobian(cv::Range::all(), cv::Range(0,6))));
double minVal, maxVal;
cv::minMaxIdx(check, &minVal, &maxVal);
EXPECT_LE(maxVal, .01);
// absolute difference for distortion and camera matrix
EXPECT_MAT_NEAR(jacobian(cv::Range::all(), cv::Range(6,numParams)), numericJacobian(cv::Range::all(), cv::Range(6,numParams)), 1e-5);
}
}
TEST_F(cameraCalibrationTiltTest, undistortPoints)
{
cv::Vec<double, NUM_DIST_COEFF_TILT> coeffNoiseHalfWidth(
.2, .1, // k1 k2
.01, .01, // p1 p2
.01, .01, .01, .01, // k3 k4 k5 k6
.001, .001, .001, .001, // s1 s2 s3 s4
.001, .001); // tauX tauY
double step = 99;
double toleranceBackProjection = 1e-5;
for (size_t numTest = 0; numTest < 10; ++numTest)
{
cv::Vec<double, NUM_DIST_COEFF_TILT> distortionCoeff = m_distortionCoeff;
randomDistortionCoeff(distortionCoeff, coeffNoiseHalfWidth);
// distorted points
std::vector<cv::Point2d> distorted;
for (double x = 0; x <= m_imageSize.width-1; x += step)
for (double y = 0; y <= m_imageSize.height-1; y += step)
distorted.push_back(cv::Point2d(x,y));
std::vector<cv::Point2d> normalizedUndistorted;
// undistort
cv::undistortPoints(distorted,
normalizedUndistorted,
m_cameraMatrix,
distortionCoeff);
// copy normalized points to 3D
std::vector<cv::Point3d> objectPoints;
for (std::vector<cv::Point2d>::const_iterator itPnt = normalizedUndistorted.begin();
itPnt != normalizedUndistorted.end(); ++itPnt)
objectPoints.push_back(cv::Point3d(itPnt->x, itPnt->y, 1));
// project
std::vector<cv::Point2d> imagePoints(objectPoints.size());
cv::projectPoints(objectPoints,
cv::Vec3d(0,0,0),
cv::Vec3d(0,0,0),
m_cameraMatrix,
distortionCoeff,
imagePoints);
EXPECT_MAT_NEAR(distorted, imagePoints, toleranceBackProjection);
}
}
template <typename INPUT, typename ESTIMATE>
void show(const std::string& name, const INPUT in, const ESTIMATE est)
{
std::cout << name << " = " << est << " (init = " << in
<< ", diff = " << est-in << ")\n";
}
template <typename INPUT>
void showVec(const std::string& name, const INPUT& in, const cv::Mat& est)
{
for (size_t i = 0; i < in.channels; ++i)
{
std::stringstream ss;
ss << name << "[" << i << "]";
show(ss.str(), in(i), est.at<double>(i));
}
}
/**
For given camera matrix and distortion coefficients
- project point target in different positions onto the sensor
- add pixel noise
- estimate camera model with noisy measurements
- compare result with initial model parameter
Parameter are differently affected by the noise
*/
TEST_F(cameraCalibrationTiltTest, calibrateCamera)
{
cv::Vec<double, NUM_DIST_COEFF_TILT> coeffNoiseHalfWidth(
.2, .1, // k1 k2
.01, .01, // p1 p2
0, 0, 0, 0, // k3 k4 k5 k6
.001, .001, .001, .001, // s1 s2 s3 s4
.001, .001); // tauX tauY
double pixelNoiseHalfWidth = .5;
std::vector<cv::Point3f> pointTarget;
pointTarget.reserve(m_pointTarget.size());
for (std::vector<cv::Point3d>::const_iterator it = m_pointTarget.begin(); it != m_pointTarget.end(); ++it)
pointTarget.push_back(cv::Point3f(
(float)(it->x),
(float)(it->y),
(float)(it->z)));
for (size_t numTest = 0; numTest < 5; ++numTest)
{
// create random distortion coefficients
cv::Vec<double, NUM_DIST_COEFF_TILT> distortionCoeff = m_distortionCoeff;
randomDistortionCoeff(distortionCoeff, coeffNoiseHalfWidth);
// container for calibration data
std::vector<std::vector<cv::Point3f> > viewsObjectPoints;
std::vector<std::vector<cv::Point2f> > viewsImagePoints;
std::vector<std::vector<cv::Point2f> > viewsNoisyImagePoints;
// simulate calibration data with projectPoints
std::vector<cv::Vec3d>::const_iterator itRvec = m_pointTargetRvec.begin();
std::vector<cv::Vec3d>::const_iterator itTvec = m_pointTargetTvec.begin();
// loop over different views
for (;itRvec != m_pointTargetRvec.end(); ++ itRvec, ++itTvec)
{
std::vector<cv::Point3f> objectPoints(pointTarget);
std::vector<cv::Point2f> imagePoints;
std::vector<cv::Point2f> noisyImagePoints;
// project calibration target to sensor
cv::projectPoints(
objectPoints,
*itRvec,
*itTvec,
m_cameraMatrix,
distortionCoeff,
imagePoints);
// remove invisible points
addNoiseRemoveInvalidPoints(
imagePoints,
objectPoints,
noisyImagePoints,
pixelNoiseHalfWidth);
// add data for view
viewsNoisyImagePoints.push_back(noisyImagePoints);
viewsImagePoints.push_back(imagePoints);
viewsObjectPoints.push_back(objectPoints);
}
// Output
std::vector<cv::Mat> outRvecs, outTvecs;
cv::Mat outCameraMatrix(3, 3, CV_64F, cv::Scalar::all(1)), outDistCoeff;
// Stopping criteria
cv::TermCriteria stop(
cv::TermCriteria::COUNT+cv::TermCriteria::EPS,
50000,
1e-14);
// model choice
int flag =
cv::CALIB_FIX_ASPECT_RATIO |
// cv::CALIB_RATIONAL_MODEL |
cv::CALIB_FIX_K3 |
// cv::CALIB_FIX_K6 |
cv::CALIB_THIN_PRISM_MODEL |
cv::CALIB_TILTED_MODEL;
// estimate
double backProjErr = cv::calibrateCamera(
viewsObjectPoints,
viewsNoisyImagePoints,
m_imageSize,
outCameraMatrix,
outDistCoeff,
outRvecs,
outTvecs,
flag,
stop);
EXPECT_LE(backProjErr, pixelNoiseHalfWidth);
#if 0
std::cout << "------ estimate ------\n";
std::cout << "back projection error = " << backProjErr << "\n";
std::cout << "points per view = {" << viewsObjectPoints.front().size();
for (size_t i = 1; i < viewsObjectPoints.size(); ++i)
std::cout << ", " << viewsObjectPoints[i].size();
std::cout << "}\n";
show("fx", m_cameraMatrix(0,0), outCameraMatrix.at<double>(0,0));
show("fy", m_cameraMatrix(1,1), outCameraMatrix.at<double>(1,1));
show("cx", m_cameraMatrix(0,2), outCameraMatrix.at<double>(0,2));
show("cy", m_cameraMatrix(1,2), outCameraMatrix.at<double>(1,2));
showVec("distor", distortionCoeff, outDistCoeff);
#endif
if (pixelNoiseHalfWidth > 0)
{
double tolRvec = pixelNoiseHalfWidth;
double tolTvec = m_objectDistance * tolRvec;
// back projection error
for (size_t i = 0; i < viewsNoisyImagePoints.size(); ++i)
{
double dRvec = cv::norm(m_pointTargetRvec[i],
cv::Vec3d(outRvecs[i].at<double>(0), outRvecs[i].at<double>(1), outRvecs[i].at<double>(2))
);
EXPECT_LE(dRvec, tolRvec);
double dTvec = cv::norm(m_pointTargetTvec[i],
cv::Vec3d(outTvecs[i].at<double>(0), outTvecs[i].at<double>(1), outTvecs[i].at<double>(2))
);
EXPECT_LE(dTvec, tolTvec);
std::vector<cv::Point2f> backProjection;
cv::projectPoints(
viewsObjectPoints[i],
outRvecs[i],
outTvecs[i],
outCameraMatrix,
outDistCoeff,
backProjection);
EXPECT_MAT_NEAR(backProjection, viewsNoisyImagePoints[i], 1.5*pixelNoiseHalfWidth);
EXPECT_MAT_NEAR(backProjection, viewsImagePoints[i], 1.5*pixelNoiseHalfWidth);
}
}
pixelNoiseHalfWidth *= .25;
}
}
}} // namespace
@@ -43,28 +43,24 @@
#include "test_precomp.hpp"
#include "test_chessboardgenerator.hpp"
#include <vector>
#include <iterator>
#include <algorithm>
using namespace cv;
using namespace std;
namespace cv {
ChessBoardGenerator::ChessBoardGenerator(const Size& _patternSize) : sensorWidth(32), sensorHeight(24),
squareEdgePointsNum(200), min_cos(std::sqrt(2.f)*0.5f), cov(0.5),
squareEdgePointsNum(200), min_cos(std::sqrt(3.f)*0.5f), cov(0.5),
patternSize(_patternSize), rendererResolutionMultiplier(4), tvec(Mat::zeros(1, 3, CV_32F))
{
Rodrigues(Mat::eye(3, 3, CV_32F), rvec);
rvec.create(3, 1, CV_32F);
cvtest::Rodrigues(Mat::eye(3, 3, CV_32F), rvec);
}
void cv::ChessBoardGenerator::generateEdge(const Point3f& p1, const Point3f& p2, vector<Point3f>& out) const
void ChessBoardGenerator::generateEdge(const Point3f& p1, const Point3f& p2, vector<Point3f>& out) const
{
Point3f step = (p2 - p1) * (1.f/squareEdgePointsNum);
for(size_t n = 0; n < squareEdgePointsNum; ++n)
out.push_back( p1 + step * (float)n);
}
Size cv::ChessBoardGenerator::cornersSize() const
Size ChessBoardGenerator::cornersSize() const
{
return Size(patternSize.width-1, patternSize.height-1);
}
@@ -76,7 +72,7 @@ struct Mult
Point2f operator()(const Point2f& p)const { return p * m; }
};
void cv::ChessBoardGenerator::generateBasis(Point3f& pb1, Point3f& pb2) const
void ChessBoardGenerator::generateBasis(Point3f& pb1, Point3f& pb2) const
{
RNG& rng = theRNG();
@@ -85,8 +81,10 @@ void cv::ChessBoardGenerator::generateBasis(Point3f& pb1, Point3f& pb2) const
{
n[0] = rng.uniform(-1.f, 1.f);
n[1] = rng.uniform(-1.f, 1.f);
n[2] = rng.uniform(-1.f, 1.f);
n[2] = rng.uniform(0.0f, 1.f);
float len = (float)norm(n);
if (len < 1e-3)
continue;
n[0]/=len;
n[1]/=len;
n[2]/=len;
@@ -106,7 +104,7 @@ void cv::ChessBoardGenerator::generateBasis(Point3f& pb1, Point3f& pb2) const
}
Mat cv::ChessBoardGenerator::generateChessBoard(const Mat& bg, const Mat& camMat, const Mat& distCoeffs,
Mat ChessBoardGenerator::generateChessBoard(const Mat& bg, const Mat& camMat, const Mat& distCoeffs,
const Point3f& zero, const Point3f& pb1, const Point3f& pb2,
float sqWidth, float sqHeight, const vector<Point3f>& whole,
vector<Point2f>& corners) const
@@ -128,10 +126,10 @@ Mat cv::ChessBoardGenerator::generateChessBoard(const Mat& bg, const Mat& camMat
generateEdge(p3, p4, pts_square3d);
generateEdge(p4, p1, pts_square3d);
projectPoints(Mat(pts_square3d), rvec, tvec, camMat, distCoeffs, pts_square2d);
projectPoints(pts_square3d, rvec, tvec, camMat, distCoeffs, pts_square2d);
squares_black.resize(squares_black.size() + 1);
vector<Point2f> temp;
approxPolyDP(Mat(pts_square2d), temp, 1.0, true);
approxPolyDP(pts_square2d, temp, 1.0, true);
transform(temp.begin(), temp.end(), back_inserter(squares_black.back()), Mult(rendererResolutionMultiplier));
}
@@ -141,7 +139,7 @@ Mat cv::ChessBoardGenerator::generateChessBoard(const Mat& bg, const Mat& camMat
for(int i = 0; i < patternSize.width - 1; ++i)
corners3d.push_back(zero + (i + 1) * sqWidth * pb1 + (j + 1) * sqHeight * pb2);
corners.clear();
projectPoints(Mat(corners3d), rvec, tvec, camMat, distCoeffs, corners);
projectPoints(corners3d, rvec, tvec, camMat, distCoeffs, corners);
vector<Point3f> whole3d;
vector<Point2f> whole2d;
@@ -149,9 +147,9 @@ Mat cv::ChessBoardGenerator::generateChessBoard(const Mat& bg, const Mat& camMat
generateEdge(whole[1], whole[2], whole3d);
generateEdge(whole[2], whole[3], whole3d);
generateEdge(whole[3], whole[0], whole3d);
projectPoints(Mat(whole3d), rvec, tvec, camMat, distCoeffs, whole2d);
projectPoints(whole3d, rvec, tvec, camMat, distCoeffs, whole2d);
vector<Point2f> temp_whole2d;
approxPolyDP(Mat(whole2d), temp_whole2d, 1.0, true);
approxPolyDP(whole2d, temp_whole2d, 1.0, true);
vector< vector<Point > > whole_contour(1);
transform(temp_whole2d.begin(), temp_whole2d.end(),
@@ -167,7 +165,7 @@ Mat cv::ChessBoardGenerator::generateChessBoard(const Mat& bg, const Mat& camMat
else
{
Mat tmp;
resize(bg, tmp, bg.size() * rendererResolutionMultiplier);
resize(bg, tmp, bg.size() * rendererResolutionMultiplier, 0, 0, INTER_LINEAR_EXACT);
drawContours(tmp, whole_contour, -1, Scalar::all(255), FILLED, LINE_AA);
drawContours(tmp, squares_black, -1, Scalar::all(0), FILLED, LINE_AA);
resize(tmp, result, bg.size(), 0, 0, INTER_AREA);
@@ -176,7 +174,7 @@ Mat cv::ChessBoardGenerator::generateChessBoard(const Mat& bg, const Mat& camMat
return result;
}
Mat cv::ChessBoardGenerator::operator ()(const Mat& bg, const Mat& camMat, const Mat& distCoeffs, vector<Point2f>& corners) const
Mat ChessBoardGenerator::operator ()(const Mat& bg, const Mat& camMat, const Mat& distCoeffs, vector<Point2f>& corners) const
{
cov = std::min(cov, 0.8);
double fovx, fovy, focalLen;
@@ -215,7 +213,7 @@ Mat cv::ChessBoardGenerator::operator ()(const Mat& bg, const Mat& camMat, const
pts3d[3] = p - pb1 * cbHalfWidthEx + cbHalfHeightEx * pb2;
/* can remake with better perf */
projectPoints(Mat(pts3d), rvec, tvec, camMat, distCoeffs, pts2d);
projectPoints(pts3d, rvec, tvec, camMat, distCoeffs, pts2d);
bool inrect1 = pts2d[0].x < bg.cols && pts2d[0].y < bg.rows && pts2d[0].x > 0 && pts2d[0].y > 0;
bool inrect2 = pts2d[1].x < bg.cols && pts2d[1].y < bg.rows && pts2d[1].x > 0 && pts2d[1].y > 0;
@@ -240,7 +238,7 @@ Mat cv::ChessBoardGenerator::operator ()(const Mat& bg, const Mat& camMat, const
}
Mat cv::ChessBoardGenerator::operator ()(const Mat& bg, const Mat& camMat, const Mat& distCoeffs,
Mat ChessBoardGenerator::operator ()(const Mat& bg, const Mat& camMat, const Mat& distCoeffs,
const Size2f& squareSize, vector<Point2f>& corners) const
{
cov = std::min(cov, 0.8);
@@ -280,7 +278,7 @@ Mat cv::ChessBoardGenerator::operator ()(const Mat& bg, const Mat& camMat, const
pts3d[3] = p - pb1 * cbHalfWidthEx + cbHalfHeightEx * pb2;
/* can remake with better perf */
projectPoints(Mat(pts3d), rvec, tvec, camMat, distCoeffs, pts2d);
projectPoints(pts3d, rvec, tvec, camMat, distCoeffs, pts2d);
bool inrect1 = pts2d[0].x < bg.cols && pts2d[0].y < bg.rows && pts2d[0].x > 0 && pts2d[0].y > 0;
bool inrect2 = pts2d[1].x < bg.cols && pts2d[1].y < bg.rows && pts2d[1].x > 0 && pts2d[1].y > 0;
@@ -299,7 +297,7 @@ Mat cv::ChessBoardGenerator::operator ()(const Mat& bg, const Mat& camMat, const
squareSize.width, squareSize.height, pts3d, corners);
}
Mat cv::ChessBoardGenerator::operator ()(const Mat& bg, const Mat& camMat, const Mat& distCoeffs,
Mat ChessBoardGenerator::operator ()(const Mat& bg, const Mat& camMat, const Mat& distCoeffs,
const Size2f& squareSize, const Point3f& pos, vector<Point2f>& corners) const
{
cov = std::min(cov, 0.8);
@@ -322,10 +320,12 @@ Mat cv::ChessBoardGenerator::operator ()(const Mat& bg, const Mat& camMat, const
pts3d[3] = p - pb1 * cbHalfWidthEx + cbHalfHeightEx * pb2;
/* can remake with better perf */
projectPoints(Mat(pts3d), rvec, tvec, camMat, distCoeffs, pts2d);
projectPoints(pts3d, rvec, tvec, camMat, distCoeffs, pts2d);
Point3f zero = p - pb1 * cbHalfWidth - cbHalfHeight * pb2;
return generateChessBoard(bg, camMat, distCoeffs, zero, pb1, pb2,
squareSize.width, squareSize.height, pts3d, corners);
}
} // namespace
@@ -1,11 +1,14 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
#ifndef CV_CHESSBOARDGENERATOR_H143KJTVYM389YTNHKFDHJ89NYVMO3VLMEJNTBGUEIYVCM203P
#define CV_CHESSBOARDGENERATOR_H143KJTVYM389YTNHKFDHJ89NYVMO3VLMEJNTBGUEIYVCM203P
#include "opencv2/calib3d.hpp"
namespace cv
{
using std::vector;
class ChessBoardGenerator
{
public:
+54 -44
View File
@@ -43,37 +43,35 @@
#include "test_chessboardgenerator.hpp"
#include <functional>
#include <limits>
#include <numeric>
using namespace std;
using namespace cv;
namespace opencv_test { namespace {
#define _L2_ERR
void show_points( const Mat& gray, const Mat& u, const vector<Point2f>& v, Size pattern_size, bool was_found )
//#define DEBUG_CHESSBOARD
#ifdef DEBUG_CHESSBOARD
void show_points( const Mat& gray, const Mat& expected, const vector<Point2f>& actual, bool was_found )
{
Mat rgb( gray.size(), CV_8U);
merge(vector<Mat>(3, gray), rgb);
for(size_t i = 0; i < v.size(); i++ )
circle( rgb, v[i], 3, Scalar(255, 0, 0), FILLED);
for(size_t i = 0; i < actual.size(); i++ )
circle( rgb, actual[i], 5, Scalar(0, 0, 200), 1, LINE_AA);
if( !u.empty() )
if( !expected.empty() )
{
const Point2f* u_data = u.ptr<Point2f>();
size_t count = u.cols * u.rows;
const Point2f* u_data = expected.ptr<Point2f>();
size_t count = expected.cols * expected.rows;
for(size_t i = 0; i < count; i++ )
circle( rgb, u_data[i], 3, Scalar(0, 255, 0), FILLED);
circle(rgb, u_data[i], 4, Scalar(0, 240, 0), 1, LINE_AA);
}
if (!v.empty())
{
Mat corners((int)v.size(), 1, CV_32FC2, (void*)&v[0]);
drawChessboardCorners( rgb, pattern_size, corners, was_found );
}
//namedWindow( "test", 0 ); imshow( "test", rgb ); waitKey(0);
putText(rgb, was_found ? "FOUND !!!" : "NOT FOUND", Point(5, 20), FONT_HERSHEY_PLAIN, 1, Scalar(0, 240, 0));
imshow( "test", rgb ); while ((uchar)waitKey(0) != 'q') {};
}
#else
#define show_points(...)
#endif
enum Pattern { CHESSBOARD, CIRCLES_GRID, ASYMMETRIC_CIRCLES_GRID };
@@ -101,7 +99,7 @@ double calcError(const vector<Point2f>& v, const Mat& u)
int count_exp = u.cols * u.rows;
const Point2f* u_data = u.ptr<Point2f>();
double err = numeric_limits<double>::max();
double err = std::numeric_limits<double>::max();
for( int k = 0; k < 2; ++k )
{
double err1 = 0;
@@ -200,7 +198,7 @@ void CV_ChessboardDetectorTest::run_batch( const string& filename )
if( !fs.isOpened() || board_list.empty() || !board_list.isSeq() || board_list.size() % 2 != 0 )
{
ts->printf( cvtest::TS::LOG, "%s can not be readed or is not valid\n", (folder + filename).c_str() );
ts->printf( cvtest::TS::LOG, "%s can not be read or is not valid\n", (folder + filename).c_str() );
ts->printf( cvtest::TS::LOG, "fs.isOpened=%d, board_list.empty=%d, board_list.isSeq=%d,board_list.size()%2=%d\n",
fs.isOpened(), (int)board_list.empty(), board_list.isSeq(), board_list.size()%2);
ts->set_failed_test_info( cvtest::TS::FAIL_MISSING_TEST_DATA );
@@ -253,7 +251,6 @@ void CV_ChessboardDetectorTest::run_batch( const string& filename )
result = findCirclesGrid(gray, pattern_size, v, CALIB_CB_ASYMMETRIC_GRID | algorithmFlags);
break;
}
show_points( gray, Mat(), v, pattern_size, result );
if( result ^ doesContatinChessboard || v.size() != count_exp )
{
@@ -267,37 +264,31 @@ void CV_ChessboardDetectorTest::run_batch( const string& filename )
#ifndef WRITE_POINTS
double err = calcError(v, expected);
#if 0
if( err > rough_success_error_level )
{
ts.printf( cvtest::TS::LOG, "bad accuracy of corner guesses\n" );
ts.set_failed_test_info( cvtest::TS::FAIL_BAD_ACCURACY );
continue;
}
#endif
max_rough_error = MAX( max_rough_error, err );
#endif
if( pattern == CHESSBOARD )
cornerSubPix( gray, v, Size(5, 5), Size(-1,-1), TermCriteria(TermCriteria::EPS|TermCriteria::MAX_ITER, 30, 0.1));
//find4QuadCornerSubpix(gray, v, Size(5, 5));
show_points( gray, expected, v, pattern_size, result );
show_points( gray, expected, v, result );
#ifndef WRITE_POINTS
// printf("called find4QuadCornerSubpix\n");
err = calcError(v, expected);
sum_error += err;
count++;
#if 1
if( err > precise_success_error_level )
{
ts->printf( cvtest::TS::LOG, "Image %s: bad accuracy of adjusted corners %f\n", img_file.c_str(), err );
ts->set_failed_test_info( cvtest::TS::FAIL_BAD_ACCURACY );
return;
}
#endif
ts->printf(cvtest::TS::LOG, "Error on %s is %f\n", img_file.c_str(), err);
max_precise_error = MAX( max_precise_error, err );
#endif
}
else
{
show_points( gray, Mat(), v, result );
}
#ifdef WRITE_POINTS
Mat mat_v(pattern_size, CV_32FC2, (void*)&v[0]);
@@ -341,21 +332,21 @@ bool validateData(const ChessBoardGenerator& cbg, const Size& imgSz,
{
const Point2f& cur = mat(i, j);
tmp = norm( cur - mat(i + 1, j + 1) );
tmp = cv::norm(cur - mat(i + 1, j + 1)); // TODO cvtest
if (tmp < minNeibDist)
tmp = minNeibDist;
minNeibDist = tmp;
tmp = norm( cur - mat(i - 1, j + 1 ) );
tmp = cv::norm(cur - mat(i - 1, j + 1)); // TODO cvtest
if (tmp < minNeibDist)
tmp = minNeibDist;
minNeibDist = tmp;
tmp = norm( cur - mat(i + 1, j - 1) );
tmp = cv::norm(cur - mat(i + 1, j - 1)); // TODO cvtest
if (tmp < minNeibDist)
tmp = minNeibDist;
minNeibDist = tmp;
tmp = norm( cur - mat(i - 1, j - 1) );
tmp = cv::norm(cur - mat(i - 1, j - 1)); // TODO cvtest
if (tmp < minNeibDist)
tmp = minNeibDist;
minNeibDist = tmp;
}
const double threshold = 0.25;
@@ -368,8 +359,6 @@ bool CV_ChessboardDetectorTest::checkByGenerator()
{
bool res = true;
// for some reason, this test sometimes fails on Ubuntu
#if (defined __APPLE__ && defined __x86_64__) || defined _MSC_VER
//theRNG() = 0x58e6e895b9913160;
//cv::DefaultRngAuto dra;
//theRNG() = *ts->get_rng();
@@ -445,7 +434,7 @@ bool CV_ChessboardDetectorTest::checkByGenerator()
if (found)
res = false;
Point2f c = std::accumulate(cg.begin(), cg.end(), Point2f(), plus<Point2f>()) * (1.f/cg.size());
Point2f c = std::accumulate(cg.begin(), cg.end(), Point2f(), std::plus<Point2f>()) * (1.f/cg.size());
Mat_<double> aff(2, 3);
aff << 1.0, 0.0, -(double)c.x, 0.0, 1.0, 0.0;
@@ -468,7 +457,6 @@ bool CV_ChessboardDetectorTest::checkByGenerator()
cv::drawChessboardCorners(cb, cbg.cornersSize(), Mat(corners_found), found);
}
#endif
return res;
}
@@ -476,6 +464,28 @@ bool CV_ChessboardDetectorTest::checkByGenerator()
TEST(Calib3d_ChessboardDetector, accuracy) { CV_ChessboardDetectorTest test( CHESSBOARD ); test.safe_run(); }
TEST(Calib3d_CirclesPatternDetector, accuracy) { CV_ChessboardDetectorTest test( CIRCLES_GRID ); test.safe_run(); }
TEST(Calib3d_AsymmetricCirclesPatternDetector, accuracy) { CV_ChessboardDetectorTest test( ASYMMETRIC_CIRCLES_GRID ); test.safe_run(); }
#ifdef HAVE_OPENCV_FLANN
TEST(Calib3d_AsymmetricCirclesPatternDetectorWithClustering, accuracy) { CV_ChessboardDetectorTest test( ASYMMETRIC_CIRCLES_GRID, CALIB_CB_CLUSTERING ); test.safe_run(); }
#endif
TEST(Calib3d_CirclesPatternDetectorWithClustering, accuracy)
{
cv::String dataDir = string(TS::ptr()->get_data_path()) + "cv/cameracalibration/circles/";
cv::Mat expected;
FileStorage fs(dataDir + "circles_corners15.dat", FileStorage::READ);
fs["corners"] >> expected;
fs.release();
cv::Mat image = cv::imread(dataDir + "circles15.png");
std::vector<Point2f> centers;
cv::findCirclesGrid(image, Size(10, 8), centers, CALIB_CB_SYMMETRIC_GRID | CALIB_CB_CLUSTERING);
ASSERT_EQ(expected.total(), centers.size());
double error = calcError(centers, expected);
ASSERT_LE(error, precise_success_error_level);
}
}} // namespace
/* End of file. */
@@ -43,10 +43,7 @@
#include "test_chessboardgenerator.hpp"
#include "opencv2/calib3d/calib3d_c.h"
#include <limits>
using namespace std;
using namespace cv;
namespace opencv_test { namespace {
class CV_ChessboardDetectorBadArgTest : public cvtest::BadArgTest
{
@@ -81,9 +78,9 @@ protected:
findChessboardCorners(img, pattern_size, corners, flags);
else
if (!drawCorners)
cvFindChessboardCorners( &arr, pattern_size, out_corners, out_corner_count, flags );
cvFindChessboardCorners( &arr, cvSize(pattern_size), out_corners, out_corner_count, flags );
else
cvDrawChessboardCorners( &drawCorImg, pattern_size,
cvDrawChessboardCorners( &drawCorImg, cvSize(pattern_size),
(CvPoint2D32f*)(corners.empty() ? 0 : &corners[0]),
(int)corners.size(), was_found);
}
@@ -118,7 +115,7 @@ void CV_ChessboardDetectorBadArgTest::run( int /*start_from */)
img = cb.clone();
pattern_size = Size(2,2);
errors += run_test_case( CV_StsOutOfRange, "Invlid pattern size" );
errors += run_test_case( CV_StsOutOfRange, "Invalid pattern size" );
pattern_size = cbg.cornersSize();
cb.convertTo(img, CV_32F);
@@ -131,14 +128,14 @@ void CV_ChessboardDetectorBadArgTest::run( int /*start_from */)
drawCorners = false;
img = cb.clone();
arr = img;
arr = cvMat(img);
out_corner_count = 0;
out_corners = 0;
errors += run_test_case( CV_StsNullPtr, "Null pointer to corners" );
drawCorners = true;
Mat cvdrawCornImg(img.size(), CV_8UC2);
drawCorImg = cvdrawCornImg;
drawCorImg = cvMat(cvdrawCornImg);
was_found = true;
errors += run_test_case( CV_StsUnsupportedFormat, "2 channel image" );
@@ -151,4 +148,5 @@ void CV_ChessboardDetectorBadArgTest::run( int /*start_from */)
TEST(Calib3d_ChessboardDetector, badarg) { CV_ChessboardDetectorBadArgTest test; test.safe_run(); }
}} // namespace
/* End of file. */
@@ -43,6 +43,8 @@
#include "opencv2/imgproc/imgproc_c.h"
#include "opencv2/calib3d/calib3d_c.h"
namespace opencv_test { namespace {
class CV_ChessboardDetectorTimingTest : public cvtest::BaseTest
{
public:
@@ -83,7 +85,7 @@ void CV_ChessboardDetectorTimingTest::run( int start_from )
if( !fs || !board_list || !CV_NODE_IS_SEQ(board_list->tag) ||
board_list->data.seq->total % 4 != 0 )
{
ts->printf( cvtest::TS::LOG, "chessboard_timing_list.dat can not be readed or is not valid" );
ts->printf( cvtest::TS::LOG, "chessboard_timing_list.dat can not be read or is not valid" );
code = cvtest::TS::FAIL_MISSING_TEST_DATA;
goto _exit_;
}
@@ -94,7 +96,7 @@ void CV_ChessboardDetectorTimingTest::run( int start_from )
{
int count0 = -1;
int count = 0;
CvSize pattern_size;
Size pattern_size;
int result, result1 = 0;
const char* imgname = cvReadString((CvFileNode*)cvGetSeqElem(board_list->data.seq,idx*4), "dummy.txt");
@@ -108,16 +110,12 @@ void CV_ChessboardDetectorTimingTest::run( int start_from )
filename = cv::format("%s%s", filepath.c_str(), imgname );
cv::Mat img2 = cv::imread( filename );
img = img2;
img = cvIplImage(img2);
if( img2.empty() )
{
ts->printf( cvtest::TS::LOG, "one of chessboard images can't be read: %s\n", filename.c_str() );
if( max_idx == 1 )
{
code = cvtest::TS::FAIL_MISSING_TEST_DATA;
goto _exit_;
}
code = cvtest::TS::FAIL_MISSING_TEST_DATA;
continue;
}
@@ -137,11 +135,11 @@ void CV_ChessboardDetectorTimingTest::run( int start_from )
v = (CvPoint2D32f*)_v->data.fl;
int64 _time0 = cvGetTickCount();
result = cvCheckChessboard(gray, pattern_size);
result = cvCheckChessboard(gray, cvSize(pattern_size));
int64 _time01 = cvGetTickCount();
OPENCV_CALL( result1 = cvFindChessboardCorners(
gray, pattern_size, v, &count, 15 ));
gray, cvSize(pattern_size), v, &count, 15 ));
int64 _time1 = cvGetTickCount();
if( result != is_chessboard )
@@ -186,4 +184,5 @@ _exit_:
TEST(Calib3d_ChessboardDetector, timing) { CV_ChessboardDetectorTimingTest test; test.safe_run(); }
}} // namespace
/* End of file. */
+11 -10
View File
@@ -42,8 +42,7 @@
#include "test_precomp.hpp"
using namespace cv;
using namespace std;
namespace opencv_test { namespace {
class Differential
{
@@ -155,14 +154,14 @@ protected:
Mat rvec3_exp, tvec3_exp;
Mat rmat1, rmat2;
Rodrigues(rvec1, rmat1);
Rodrigues(rvec2, rmat2);
Rodrigues(rmat2 * rmat1, rvec3_exp);
cv::Rodrigues(rvec1, rmat1); // TODO cvtest
cv::Rodrigues(rvec2, rmat2); // TODO cvtest
cv::Rodrigues(rmat2 * rmat1, rvec3_exp); // TODO cvtest
tvec3_exp = rmat2 * tvec1 + tvec2;
const double thres = 1e-5;
if (norm(rvec3_exp, rvec3) > thres || norm(tvec3_exp, tvec3) > thres)
if (cv::norm(rvec3_exp, rvec3) > thres || cv::norm(tvec3_exp, tvec3) > thres)
ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY);
const double eps = 1e-3;
@@ -176,7 +175,7 @@ protected:
Mat_<double> dr3_dr1, dt3_dr1;
diff.dRv1(dr3_dr1, dt3_dr1);
if (norm(dr3_dr1, dr3dr1) > thres || norm(dt3_dr1, dt3dr1) > thres)
if (cv::norm(dr3_dr1, dr3dr1) > thres || cv::norm(dt3_dr1, dt3dr1) > thres)
{
ts->printf( cvtest::TS::LOG, "Invalid derivates by r1\n" );
ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY);
@@ -185,7 +184,7 @@ protected:
Mat_<double> dr3_dr2, dt3_dr2;
diff.dRv2(dr3_dr2, dt3_dr2);
if (norm(dr3_dr2, dr3dr2) > thres || norm(dt3_dr2, dt3dr2) > thres)
if (cv::norm(dr3_dr2, dr3dr2) > thres || cv::norm(dt3_dr2, dt3dr2) > thres)
{
ts->printf( cvtest::TS::LOG, "Invalid derivates by r2\n" );
ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY);
@@ -194,7 +193,7 @@ protected:
Mat_<double> dr3_dt1, dt3_dt1;
diff.dTv1(dr3_dt1, dt3_dt1);
if (norm(dr3_dt1, dr3dt1) > thres || norm(dt3_dt1, dt3dt1) > thres)
if (cv::norm(dr3_dt1, dr3dt1) > thres || cv::norm(dt3_dt1, dt3dt1) > thres)
{
ts->printf( cvtest::TS::LOG, "Invalid derivates by t1\n" );
ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY);
@@ -203,7 +202,7 @@ protected:
Mat_<double> dr3_dt2, dt3_dt2;
diff.dTv2(dr3_dt2, dt3_dt2);
if (norm(dr3_dt2, dr3dt2) > thres || norm(dt3_dt2, dt3dt2) > thres)
if (cv::norm(dr3_dt2, dr3dt2) > thres || cv::norm(dt3_dt2, dt3dt2) > thres)
{
ts->printf( cvtest::TS::LOG, "Invalid derivates by t2\n" );
ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY);
@@ -212,3 +211,5 @@ protected:
};
TEST(Calib3d_ComposeRT, accuracy) { CV_composeRT_Test test; test.safe_run(); }
}} // namespace
+18 -5
View File
@@ -41,11 +41,9 @@
#include "test_precomp.hpp"
#include "opencv2/imgproc/imgproc_c.h"
#include <limits>
#include "test_chessboardgenerator.hpp"
using namespace std;
using namespace cv;
namespace opencv_test { namespace {
class CV_ChessboardSubpixelTest : public cvtest::BaseTest
{
@@ -78,7 +76,7 @@ int calcDistance(const vector<Point2f>& set1, const vector<Point2f>& set2, doubl
for(int j = 0; j < (int)set2.size(); j++)
{
double dist = norm(set1[i] - set2[j]);
double dist = cv::norm(set1[i] - set2[j]); // TODO cvtest
if(dist < min_dist)
{
min_idx = j;
@@ -182,7 +180,7 @@ void CV_ChessboardSubpixelTest::run( int )
break;
}
IplImage chessboard_image_header = chessboard_image;
IplImage chessboard_image_header = cvIplImage(chessboard_image);
cvFindCornerSubPix(&chessboard_image_header, (CvPoint2D32f*)&test_corners[0],
(int)test_corners.size(), cvSize(3, 3), cvSize(1, 1), cvTermCriteria(CV_TERMCRIT_EPS|CV_TERMCRIT_ITER,300,0.1));
find4QuadCornerSubpix(chessboard_image, test_corners, Size(5, 5));
@@ -242,4 +240,19 @@ void CV_ChessboardSubpixelTest::generateIntrinsicParams()
TEST(Calib3d_ChessboardSubPixDetector, accuracy) { CV_ChessboardSubpixelTest test; test.safe_run(); }
TEST(Calib3d_CornerSubPix, regression_7204)
{
cv::Mat image(cv::Size(70, 38), CV_8UC1, cv::Scalar::all(0));
image(cv::Rect(65, 26, 5, 5)).setTo(cv::Scalar::all(255));
image(cv::Rect(55, 31, 8, 1)).setTo(cv::Scalar::all(255));
image(cv::Rect(56, 35, 14, 2)).setTo(cv::Scalar::all(255));
image(cv::Rect(66, 24, 4, 2)).setTo(cv::Scalar::all(255));
image.at<uchar>(24, 69) = 0;
std::vector<cv::Point2f> corners;
corners.push_back(cv::Point2f(65, 30));
cv::cornerSubPix(image, corners, cv::Size(3, 3), cv::Size(-1, -1),
cv::TermCriteria(CV_TERMCRIT_EPS + CV_TERMCRIT_ITER, 30, 0.1));
}
}} // namespace
/* End of file. */
@@ -0,0 +1,144 @@
/*M///////////////////////////////////////////////////////////////////////////////////////
//
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
// By downloading, copying, installing or using the software you agree to this license.
// If you do not agree to this license, do not download, install,
// copy or use the software.
//
//
// License Agreement
// For Open Source Computer Vision Library
//
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistribution's of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistribution's 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.
//
// * The name of the copyright holders may not 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 Intel Corporation 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.
//
//M*/
#include "test_precomp.hpp"
namespace opencv_test { namespace {
class CV_DecomposeProjectionMatrixTest : public cvtest::BaseTest
{
public:
CV_DecomposeProjectionMatrixTest();
protected:
void run(int);
};
CV_DecomposeProjectionMatrixTest::CV_DecomposeProjectionMatrixTest()
{
test_case_count = 30;
}
void CV_DecomposeProjectionMatrixTest::run(int start_from)
{
ts->set_failed_test_info(cvtest::TS::OK);
cv::RNG& rng = ts->get_rng();
int progress = 0;
for (int iter = start_from; iter < test_case_count; ++iter)
{
ts->update_context(this, iter, true);
progress = update_progress(progress, iter, test_case_count, 0);
// Create the original (and random) camera matrix, rotation, and translation
cv::Vec2d f, c;
rng.fill(f, cv::RNG::UNIFORM, 300, 1000);
rng.fill(c, cv::RNG::UNIFORM, 150, 600);
double alpha = 0.01*rng.gaussian(1);
cv::Matx33d origK(f(0), alpha*f(0), c(0),
0, f(1), c(1),
0, 0, 1);
cv::Vec3d rVec;
rng.fill(rVec, cv::RNG::UNIFORM, -CV_PI, CV_PI);
cv::Matx33d origR;
cv::Rodrigues(rVec, origR); // TODO cvtest
cv::Vec3d origT;
rng.fill(origT, cv::RNG::NORMAL, 0, 1);
// Compose the projection matrix
cv::Matx34d P(3,4);
hconcat(origK*origR, origK*origT, P);
// Decompose
cv::Matx33d K, R;
cv::Vec4d homogCameraCenter;
decomposeProjectionMatrix(P, K, R, homogCameraCenter);
// Recover translation from the camera center
cv::Vec3d cameraCenter(homogCameraCenter(0), homogCameraCenter(1), homogCameraCenter(2));
cameraCenter /= homogCameraCenter(3);
cv::Vec3d t = -R*cameraCenter;
const double thresh = 1e-6;
if (cv::norm(origK, K, cv::NORM_INF) > thresh)
{
ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY);
break;
}
if (cv::norm(origR, R, cv::NORM_INF) > thresh)
{
ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY);
break;
}
if (cv::norm(origT, t, cv::NORM_INF) > thresh)
{
ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY);
break;
}
}
}
TEST(Calib3d_DecomposeProjectionMatrix, accuracy)
{
CV_DecomposeProjectionMatrixTest test;
test.safe_run();
}
}} // namespace
@@ -0,0 +1,575 @@
/*M///////////////////////////////////////////////////////////////////////////////////////
//
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
// By downloading, copying, installing or using the software you agree to this license.
// If you do not agree to this license, do not download, install,
// copy or use the software.
//
//
// License Agreement
// For Open Source Computer Vision Library
//
// Copyright (C) 2016, OpenCV Foundation, all rights reserved.
//
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistribution's of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistribution's 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.
//
// * The name of the copyright holders may not 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 Intel Corporation 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.
//
//M*/
#include "test_precomp.hpp"
#include "opencv2/calib3d.hpp"
namespace opencv_test { namespace {
class CV_FilterHomographyDecompTest : public cvtest::BaseTest {
public:
CV_FilterHomographyDecompTest()
{
buildTestDataSet();
}
protected:
void run(int)
{
vector<int> finalSolutions;
filterHomographyDecompByVisibleRefpoints(_rotations, _normals, _prevRectifiedPoints, _currRectifiedPoints, finalSolutions, _mask);
//there should be at least 2 solution
ASSERT_EQ(finalSolutions, _validSolutions);
}
private:
void buildTestDataSet()
{
double rotationsArray[4][9] = {
{
0.98811084196540500,
-0.15276633082836735,
0.017303530150126534,
0.14161851662094097,
0.94821044891315664,
0.28432576443578628,
-0.059842791884259422,
-0.27849487021693553,
0.95857156619751127
},
{
0.98811084196540500,
-0.15276633082836735,
0.017303530150126534,
0.14161851662094097,
0.94821044891315664,
0.28432576443578628,
-0.059842791884259422,
-0.27849487021693553,
0.95857156619751127
},
{
0.95471096402077438,
-0.21080808634428211,
-0.20996886890771557,
0.20702063153797226,
0.97751379914116743,
-0.040115216641822840,
0.21370407880090386,
-0.0051694506925720751,
0.97688476468997820
},
{
0.95471096402077438,
-0.21080808634428211,
-0.20996886890771557,
0.20702063153797226,
0.97751379914116743,
-0.040115216641822840,
0.21370407880090386,
-0.0051694506925720751,
0.97688476468997820
}
};
double normalsArray[4][3] = {
{
-0.023560516110791116,
0.085818414407956692,
0.99603217911325403
},
{
0.023560516110791116,
-0.085818414407956692,
-0.99603217911325403
},
{
-0.62483547397726014,
-0.56011861446691769,
0.54391889853844289
},
{
0.62483547397726014,
0.56011861446691769,
-0.54391889853844289
}
};
uchar maskArray[514] =
{
0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0,
0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0,
1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1,
0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0,
0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1,
1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0,
0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1,
1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0,
0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0,
0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0,
1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1,
1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0,
0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0,
0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
static const float currRectifiedPointArr[] =
{
-0.565732896f, -0.321162999f, -0.416198403f, -0.299646467f, -0.408354312f, -0.290387660f,
-0.386555284f, -0.287677139f, -0.348475337f, -0.276208878f, -0.415957332f, -0.266133875f,
-0.354961902f, -0.257545590f, -0.420189440f, -0.255190015f, -0.379785866f, -0.252570540f,
-0.144345313f, -0.249134675f, -0.162417486f, -0.223227784f, -0.129876539f, -0.219722182f,
-0.470801264f, -0.211166814f, 0.0992607549f, -0.209064797f, 0.123508267f, -0.196303099f,
-0.521849990f, -0.190706849f, -0.513497114f, -0.189186409f, -0.534959674f, -0.185138911f,
0.121614374f, -0.182721153f, 0.154205695f, -0.183763996f, -0.516449869f, -0.181606859f,
-0.523427486f, -0.180088669f, 0.149494573f, -0.179563865f, -0.552187204f, -0.172630817f,
-0.322249800f, -0.172333881f, 0.127574071f, -0.165683150f, 0.159817487f, -0.162389070f,
-0.578930736f, -0.160272732f, -0.600617707f, -0.155920163f, -0.249115735f, -0.154711768f,
-0.543279886f, -0.144873798f, -0.529992998f, -0.142433196f, 0.0554505363f, -0.142878756f,
-0.613355398f, -0.132748783f, 0.190059289f, -0.128930226f, -0.255682647f, -0.127393380f,
0.0299431719f, -0.125339776f, -0.282943249f, -0.118550651f, -0.0348402821f, -0.115398556f,
-0.0362741761f, -0.110100254f, -0.319089264f, -0.104354575f, -0.0401916653f, -0.0852083191f,
-0.372183621f, -0.0812346712f, -0.00707253255f, -0.0810251758f, 0.267345309f, -0.0787685066f,
0.258760840f, -0.0768160895f, -0.377273679f, -0.0763452053f, -0.0314898677f, -0.0743160769f,
0.223423928f, -0.0724818707f, 0.00284322398f, -0.0720727518f, 0.232531011f, -0.0682833865f,
0.282355100f, -0.0655683428f, -0.233353317f, -0.0613981225f, 0.290982842f, -0.0607336313f,
-0.0994169787f, -0.0376472026f, 0.257561266f, -0.0331368558f, 0.265076399f, -0.0320781991f,
0.0454338901f, -0.0238198638f, 0.0409987904f, -0.0186991505f, -0.502306283f, -0.0172236171f,
-0.464807063f, -0.0149533665f, -0.185798749f, -0.00540314987f, 0.182073534f, -0.000651287497f,
-0.435764432f, 0.00162558386f, -0.181552932f, 0.00792864431f, -0.700565279f, 0.0110246018f,
-0.144087434f, 0.0120453080f, -0.524990261f, 0.0138590708f, -0.182723984f, 0.0165519360f,
-0.217308879f, 0.0208590515f, 0.462978750f, 0.0247372910f, 0.0956632495f, 0.0323494300f,
0.0843820646f, 0.0424364135f, 0.122466311f, 0.0441578403f, -0.162433729f, 0.0528083183f,
0.0964344442f, 0.0624147579f, -0.271349967f, 0.0727724135f, -0.266336441f, 0.0719895661f,
0.0675768778f, 0.0848240927f, -0.689944625f, 0.0889045894f, -0.680990934f, 0.0903657600f,
-0.119472280f, 0.0930491239f, -0.124393739f, 0.0933082998f, -0.323403478f, 0.0937438533f,
-0.323273063f, 0.0969979763f, -0.352427900f, 0.101048596f, -0.327554941f, 0.104539163f,
-0.330044419f, 0.114519835f, 0.0235135648f, 0.118004657f, -0.671623945f, 0.130437061f,
-0.385111898f, 0.142786101f, -0.376281500f, 0.145800456f, -0.0169987213f, 0.148056105f,
-0.326495141f, 0.152596891f, -0.337120056f, 0.154522225f, -0.336885720f, 0.154304653f,
0.322089493f, 0.155130088f, -0.0713477954f, 0.163638428f, -0.0208650175f, 0.171433330f,
-0.380652726f, 0.172022790f, -0.0599780641f, 0.182294667f, 0.244408697f, 0.194245726f,
-0.101454332f, 0.198159069f, 0.257901788f, 0.200226694f, -0.0775909275f, 0.205242962f,
0.231870517f, 0.222396746f, -0.546760798f, 0.242291704f, -0.538914979f, 0.243761152f,
0.206653103f, 0.244874880f, -0.595693469f, 0.264329463f, -0.581023335f, 0.265664101f,
0.00444878871f, 0.267031074f, -0.573156178f, 0.271591753f, -0.543381274f, 0.271759123f,
0.00450209389f, 0.271335930f, -0.223618075f, 0.278416723f, 0.161934286f, 0.289435983f,
-0.199636295f, 0.296817899f, -0.250217140f, 0.299677849f, -0.258231103f, 0.314012855f,
-0.628315628f, 0.316889286f, 0.320948511f, 0.316358119f, -0.246845752f, 0.320511192f,
0.0687271580f, 0.321383297f, 0.0784438103f, 0.322898388f, 0.0946765989f, 0.325111747f,
-0.249674007f, 0.328731328f, -0.244633347f, 0.329467386f, -0.245841011f, 0.334985316f,
0.118609101f, 0.343532443f, 0.0497615598f, 0.348162144f, -0.221477821f, 0.349263757f,
0.0759577379f, 0.351840734f, 0.0504637137f, 0.373238713f, 0.0730970055f, 0.376537383f,
-0.204333842f, 0.381100655f, -0.557245076f, -0.339432925f, -0.402010202f, -0.288829565f,
-0.350465477f, -0.281259984f, -0.352995187f, -0.264569730f, -0.466762394f, -0.217114508f,
0.152002022f, -0.217566550f, 0.146226048f, -0.183914393f, 0.0949312001f, -0.177005857f,
-0.211882949f, -0.175594494f, -0.531562269f, -0.173924312f, -0.0727246776f, -0.167270422f,
0.0546481088f, -0.140193000f, -0.296819001f, -0.137850702f, -0.261863053f, -0.139540121f,
0.187967837f, -0.131033540f, 0.322852045f, -0.112108752f, -0.0432251953f, -0.102951847f,
-0.0453428440f, -0.0914504975f, -0.0182842426f, -0.0918859020f, 0.0140433423f, -0.0904538929f,
-0.377287626f, -0.0817026496f, 0.266108125f, -0.0797783583f, 0.257961422f, -0.0767710134f,
-0.495943695f, -0.0683977529f, 0.231466040f, -0.0675206482f, -0.240675926f, -0.0551427566f,
-0.482824773f, -0.0510699376f, -0.491354793f, -0.0414650664f, -0.0960614979f, -0.0377000235f,
-0.102409534f, -0.0369749814f, -0.471273214f, -0.0325376652f, -0.483320534f, -0.0174943600f,
-0.457503378f, -0.0152483145f, -0.178161725f, -0.0153892851f, -0.483233035f, -0.0106405178f,
-0.472914547f, -0.0105228210f, -0.166542307f, -0.00667150877f, 0.181261331f, -0.00449455017f,
-0.474292487f, -0.00428914558f, -0.185297221f, -0.00575157674f, -0.494381040f, -0.00278507406f,
-0.141748473f, -0.00289725070f, -0.487515569f, 0.000758233888f, 0.322646528f, 0.0197495818f,
0.142943904f, 0.0276249554f, -0.563232243f, 0.0306834858f, -0.555995941f, 0.0367121249f,
0.114935011f, 0.0496927276f, -0.152954608f, 0.0538645200f, -0.594885707f, 0.0562511310f,
0.0678326488f, 0.0756176412f, -0.667605639f, 0.0828208700f, -0.354470938f, 0.101424232f,
0.0228204262f, 0.120382607f, -0.639557123f, 0.124422595f, -0.690505445f, 0.126883239f,
-0.395509213f, 0.130242139f, -0.00618012529f, 0.139929801f, 0.175945997f, 0.140235618f,
0.198833048f, 0.167587668f, -0.334679037f, 0.177859858f, 0.236127406f, 0.192743436f,
0.283146858f, 0.204260647f, -0.0354267135f, 0.206209183f, 0.247388184f, 0.207016930f,
-0.0422560424f, 0.212493256f, 0.261681855f, 0.215763748f, 0.207528576f, 0.219807997f,
-0.300219178f, 0.221922547f, 0.206393883f, 0.245171010f, 0.239619836f, 0.244768366f,
-0.523026288f, 0.250639766f, -0.591975033f, 0.254252791f, 0.246785000f, 0.252878994f,
0.272995651f, 0.255815417f, 0.00825022161f, 0.265591830f, 0.192723796f, 0.266924977f,
-0.222951472f, 0.290150762f, -0.545146644f, 0.304910392f, 0.131736591f, 0.319247276f,
0.319435924f, 0.317917794f, 0.0687546134f, 0.321296155f, -0.255853772f, 0.327258259f,
0.0948092714f, 0.325284332f, 0.104488030f, 0.327628911f, -0.245483562f, 0.327617317f,
0.0647632629f, 0.363111496f, -0.382861346f, -0.287226975f, -0.354297429f, -0.278708905f,
-0.356116027f, -0.262691110f, -0.369049937f, -0.237850189f, -0.146217853f, -0.233530551f,
0.102752604f, -0.223108903f, 0.137545392f, -0.218163848f, 0.125815898f, -0.216970086f,
-0.557826996f, -0.194665924f, -0.533946335f, -0.184958249f, 0.0976954028f, -0.173691019f,
-0.240166873f, -0.160652772f, 0.166464865f, -0.154563308f, -0.0330923162f, -0.125799045f,
-0.290044904f, -0.118914597f, 0.00350888353f, -0.108661920f, -0.0109116854f, -0.106212743f,
-0.0298740193f, -0.102953635f, -0.287203342f, -0.0997403413f, -0.269498408f, -0.0981520712f,
-0.000815737061f, -0.0938294530f, 0.274663270f, -0.0844340026f, -0.371082008f, -0.0805466920f,
-0.368196100f, -0.0743779093f, 0.00675902702f, -0.0735078678f, 0.226267770f, -0.0744194537f,
-0.241736412f, -0.0630025938f, -0.408663541f, -0.0564615242f, 0.251640886f, -0.0519632548f,
0.249993712f, -0.0519672707f, -0.426033378f, -0.0365641154f, -0.467352122f, -0.0305716563f,
0.251341015f, -0.0268137120f, -0.443456501f, -0.0243669953f, -0.502199471f, -0.0151771074f,
-0.178487480f, -0.0155749097f, 0.178145915f, -0.00528379623f, -0.492981344f, -0.00174682145f,
-0.150337398f, 0.000692513015f, -0.457302928f, 0.00352234906f, 0.190587431f, 0.00151424226f,
-0.482671946f, 0.00682042213f, -0.158589542f, 0.0150188655f, -0.182223722f, 0.0145649035f,
0.107089065f, 0.0223725326f, 0.135399371f, 0.0275243558f, -0.552838683f, 0.0275048595f,
-0.432176501f, 0.0248741303f, -0.192510992f, 0.0281074084f, -0.553043425f, 0.0298770685f,
-0.684887648f, 0.0436144769f, 0.0850105733f, 0.0448755622f, -0.165784389f, 0.0439001285f,
0.102653719f, 0.0457992665f, 0.114853017f, 0.0504316092f, -0.647432685f, 0.0608204119f,
0.0828530043f, 0.0608987175f, 0.0894377902f, 0.0742467493f, 0.0702404827f, 0.0767309442f,
-0.613642335f, 0.0779517740f, -0.670592189f, 0.0849624202f, -0.395209312f, 0.0854151621f,
0.125186160f, 0.0919951499f, -0.359707922f, 0.102121405f, -0.354259193f, 0.101300709f,
0.0304000825f, 0.110619470f, -0.677573025f, 0.114422500f, 0.0305799693f, 0.121603437f,
-0.358950615f, 0.121660560f, -0.718753040f, 0.134569481f, 0.256451160f, 0.141883001f,
-0.0904129520f, 0.146879435f, -0.0184279438f, 0.148968369f, -0.356992692f, 0.160104826f,
-0.337676436f, 0.161766291f, 0.201174691f, 0.169025913f, -0.378423393f, 0.170933828f,
-0.601599216f, 0.174998865f, -0.0902864039f, 0.184311926f, -0.0584819093f, 0.184186250f,
0.294467270f, 0.182560727f, 0.250262231f, 0.186239958f, -0.326370239f, 0.191697389f,
-0.0980727375f, 0.196913749f, 0.253085673f, 0.201914877f, -0.0344332159f, 0.205900863f,
0.255287141f, 0.203029931f, -0.452713937f, 0.205191836f, 0.264822274f, 0.217408702f,
-0.0290334225f, 0.221684650f, -0.583990574f, 0.237398431f, -0.145020664f, 0.240374506f,
0.249667659f, 0.254706532f, 0.274279058f, 0.256447285f, -0.282936275f, 0.259140193f,
0.241211995f, 0.260401577f, -0.590560019f, 0.272659779f, -0.574947417f, 0.272671998f,
-0.224780366f, 0.279990941f, -0.525540829f, 0.287235677f, -0.247069210f, 0.298608154f,
-0.201292604f, 0.298156679f, 0.319822490f, 0.317605704f, -0.248013541f, 0.320789784f,
0.0957527757f, 0.326543272f, 0.105006196f, 0.328469753f, -0.264089525f, 0.332354158f,
-0.670460403f, 0.339870930f, 0.118318990f, 0.345167071f, 0.0737744719f, 0.353734553f,
0.0655663237f, 0.361025929f, -0.306805104f, 0.363820761f, 0.0524423867f, 0.371921480f,
0.0713953897f, 0.375074357f, -0.411387652f, -0.268335998f, -0.357590824f, -0.263346583f,
-0.407676578f, -0.253785878f, 0.0660323426f, -0.253718942f, -0.157670841f, -0.225629836f,
0.170453921f, -0.220800355f, -0.475751191f, -0.209005311f, -0.331408232f, -0.203059763f,
-0.173841938f, -0.199112654f, -0.503261328f, -0.193795130f, -0.532277644f, -0.190292686f,
-0.0972326621f, -0.191563144f, -0.0692789108f, -0.172031537f, -0.318824291f, -0.169072524f,
-0.576232314f, -0.162124678f, -0.0839322209f, -0.156304389f, -0.583625376f, -0.142171323f,
-0.0546422042f, -0.135338858f, 0.0501612425f, -0.132490858f, -0.645011544f, -0.111341864f,
-0.0925374180f, -0.0483307689f, -0.444242209f, -0.0263337940f, 0.0335495919f, -0.0281750113f,
0.274629444f, -0.0259516705f, 0.213774025f, -0.0240113474f, -0.194874078f, -0.0151330847f,
0.175111562f, -0.00868577976f, -0.185011521f, -0.000680683181f, 0.152071685f, 0.0204544198f,
0.321354061f, 0.0199794695f, -0.192160159f, 0.0275637116f, -0.189656645f, 0.0275667012f,
0.137452200f, 0.0298070628f, -0.194602579f, 0.0449027494f, -0.647751570f, 0.0625102371f,
0.124078721f, 0.0639316663f, 0.125849217f, 0.0762147456f, -0.614036798f, 0.0778791085f,
-0.684063017f, 0.0867959261f, -0.670344174f, 0.0846142769f, -0.127689242f, 0.0883567855f,
0.123796627f, 0.0907361880f, -0.356352538f, 0.101948388f, -0.388843179f, 0.110183217f,
0.0316384435f, 0.123791300f, -0.627986908f, 0.146491125f, -0.0747071728f, 0.158135459f,
-0.0235102437f, 0.168867558f, -0.0903210714f, 0.184088305f, 0.292073458f, 0.183571488f,
-0.0585953295f, 0.184784085f, -0.0317775607f, 0.218368888f, 0.209752038f, 0.223883361f,
-0.295424402f, 0.229150623f, -0.144439027f, 0.237902716f, -0.284140587f, 0.262761474f,
0.289083928f, 0.276900887f, 0.159017235f, 0.300793648f, -0.204925507f, 0.298536539f,
-0.544958472f, 0.305164427f, -0.261615157f, 0.306550682f, 0.0977220088f, 0.327949613f,
0.109876208f, 0.337665111f, -0.283918083f, 0.347385526f, 0.0436712503f, 0.350702018f,
0.114512287f, 0.367949426f, 0.106543839f, 0.375095814f, 0.505324781f, -0.272183985f,
0.0645913780f, -0.251512915f, -0.457196057f, -0.225893468f, -0.480293810f, -0.222602293f,
-0.138176888f, -0.209798917f, -0.110901751f, -0.198036820f, -0.196451947f, -0.191723794f,
-0.537742376f, -0.174413025f, -0.0650562346f, -0.174762890f, -0.567489207f, -0.165461496f,
0.0879585966f, -0.163023785f, -0.303777844f, -0.142031133f, 0.199195996f, -0.141861767f,
0.0491657220f, -0.132264882f, -0.497363061f, -0.107934952f, -0.000536393432f, -0.102828167f,
0.0155952247f, -0.0998895392f, -0.363601953f, -0.0897399634f, -0.224325985f, -0.0719678402f,
-0.0638299435f, -0.0646244809f, -0.108656809f, -0.0468749776f, -0.0865045264f, -0.0512534790f,
-0.469339728f, -0.0279338267f, 0.0578282699f, -0.0133374622f, -0.195265710f, -0.0115369316f,
0.296735317f, -0.0132813146f, 0.0664219409f, 0.0134935537f, 0.126060545f, 0.0333039127f,
0.139887005f, 0.0334976614f, -0.547339618f, 0.0433730707f, 0.0866046399f, 0.0527233221f,
0.131943896f, 0.0657638907f, -0.280056775f, 0.0685855150f, 0.0746403933f, 0.0795079395f,
0.125382811f, 0.0822770745f, -0.648187757f, 0.103887804f, -0.107411072f, 0.107508548f,
0.0155869983f, 0.108978622f, 0.0189307462f, 0.129617691f, 0.162685350f, 0.127225950f,
-0.0875291452f, 0.142281070f, 0.319728941f, 0.148827255f, -0.0259547811f, 0.169724479f,
0.259297132f, 0.190075457f, -0.467013776f, 0.212794706f, -0.315732479f, 0.219243437f,
-0.111042649f, 0.217940107f, 0.239550352f, 0.222786069f, 0.263966352f, 0.260309041f,
0.320023954f, -0.222228840f, -0.322707742f, -0.213004455f, -0.224977970f, -0.169595599f,
-0.605799317f, -0.142425537f, 0.0454332717f, -0.129945949f, 0.205748767f, -0.113405459f,
0.317985803f, -0.118630089f, 0.497755647f, -0.0962266177f, -0.393495560f, -0.0904672816f,
0.240035087f, -0.0737613589f, -0.212947786f, -0.0280145984f, 0.0674179196f, 0.0124880793f,
-0.545862198f, 0.0207057912f, -0.284409463f, 0.0626631007f, -0.107082598f, 0.0854173824f,
0.0578137375f, 0.0917839557f, 0.145844117f, 0.102937251f, 0.183878779f, 0.119614877f,
-0.626380265f, 0.140862882f, -0.0325521491f, 0.161834121f, -0.590211987f, 0.167720392f,
0.289599866f, 0.186565816f, -0.328821093f, 0.187714070f, -0.289086968f, 0.205165654f,
-0.445392698f, 0.215343162f, 0.173715711f, 0.273563296f, 0.284015119f, 0.270610362f,
0.0174398609f, 0.283809274f, -0.496335506f, -0.202981815f, 0.0389454551f, -0.166210428f,
-0.317301393f, -0.156280205f, -0.396320462f, -0.0949599668f, -0.213638976f, -0.0776446015f,
0.497601509f, -0.0928353444f, -0.260220319f, -0.0718628615f, -0.116495222f, -0.0543703064f,
-0.118132629f, -0.0156126227f, 0.0242815297f, 0.00629332382f, -0.537928998f, 0.00815516617f,
0.317720622f, 0.0271231923f, -0.582170665f, 0.0478387438f, -0.536856830f, 0.0466793887f,
-0.220819592f, 0.0433096550f, -0.246473342f, 0.0572598167f, 0.481240988f, 0.0503845438f,
-0.102453016f, 0.0649363101f, -0.149955124f, 0.0744054317f, -0.248215869f, 0.0916868672f,
-0.101221249f, 0.110788561f, -0.437672526f, 0.179065496f, -0.0383506976f, 0.183546484f,
-0.279600590f, 0.208760634f, 0.182261929f, 0.275244594f, 0.0253023170f, -0.170456246f,
-0.476852804f, -0.123630777f, -0.0803126246f, -0.0782076195f, -0.133338496f, -0.0659459904f,
-0.0822777376f, -0.00390591589f, 0.149250969f, 0.104314201f, 0.0418044887f, 0.149009049f,
-0.438308835f, 0.164682120f
};
const Point2f* currRectifiedPointArr_2f = (const Point2f*)currRectifiedPointArr;
vector<Point2f> currRectifiedPoints(currRectifiedPointArr_2f,
currRectifiedPointArr_2f + sizeof(currRectifiedPointArr) / sizeof(currRectifiedPointArr[0]) / 2);
_currRectifiedPoints.swap(currRectifiedPoints);
static const float prevRectifiedPointArr[] = {
-0.599324584f, -0.381164283f, -0.387985110f, -0.385367423f, -0.371437579f, -0.371891201f,
-0.340867460f, -0.370632380f, -0.289822906f, -0.364118159f, -0.372411519f, -0.335272551f,
-0.289586753f, -0.335766882f, -0.372335523f, -0.316857219f, -0.321099430f, -0.323233813f,
0.208661616f, -0.153931335f, -0.559897065f, 0.193362445f, 0.0181128159f, -0.325224668f,
-0.427504510f, 0.105302416f, 0.487470537f, -0.187071189f, 0.343267351f, -0.339755565f,
-0.477639943f, -0.204375938f, -0.466626763f, -0.204072326f, 0.340813518f, -0.347292691f,
0.342682719f, -0.320172101f, 0.383663863f, -0.327343374f, -0.467062414f, -0.193995550f,
-0.475603998f, -0.189820126f, 0.552475691f, 0.198386014f, -0.508027375f, -0.174297482f,
-0.211989403f, -0.217261642f, 0.180832058f, -0.127527758f, -0.112721168f, -0.125876635f,
-0.112387165f, -0.167135969f, -0.562491000f, -0.140186235f, 0.395156831f, -0.298828602f,
-0.485202312f, -0.135626689f, 0.148358017f, -0.195937276f, -0.248159677f, -0.254669130f,
-0.568366945f, -0.105187029f, -0.0714842379f, -0.0832463056f, -0.497599572f, -0.205334768f,
-0.0948727652f, 0.245045587f, 0.160857186f, 0.138075173f, 0.164952606f, -0.195109487f,
0.165254518f, -0.186554477f, -0.183777973f, -0.124357253f, 0.166813776f, -0.153241888f,
-0.241765827f, -0.0820638761f, 0.208661616f, -0.153931335f, 0.540147483f, -0.203156039f,
0.529201686f, -0.199348077f, -0.248159677f, -0.254669130f, 0.180369601f, -0.139303327f,
0.570952237f, -0.185722873f, 0.221771300f, -0.143187970f, 0.498627752f, -0.183768719f,
0.561214447f, -0.188666284f, -0.241409421f, -0.253560483f, 0.569648385f, -0.184499770f,
0.276665628f, -0.0881819800f, 0.533934176f, -0.142226711f, -0.299728751f, -0.330407321f,
0.270322412f, -0.256552309f, -0.255016476f, -0.0823200271f, -0.378096581f, 0.0264666155f,
-0.331565350f, 0.0210608803f, 0.0100810500f, -0.0213523544f, -0.248159677f, -0.254669130f,
0.249623299f, 0.164078355f, 0.0190342199f, -0.00415771967f, 0.604407132f, -0.259350061f,
0.0660026148f, -0.00787150953f, 0.605921566f, 0.114344336f, 0.0208173525f, 0.00527517078f,
-0.0200567022f, 0.0183092188f, -0.184784368f, -0.193566754f, -0.0125719802f, -0.344967902f,
0.343063682f, -0.0121044181f, 0.389022052f, -0.0171062462f, 0.163190305f, 0.200014487f,
0.362440646f, 0.0120019922f, -0.427743971f, 0.100272447f, -0.0714842379f, -0.0832463056f,
0.0664352402f, 0.0467514023f, -0.559897065f, 0.193362445f, -0.549086213f, 0.193808615f,
-0.241472989f, -0.253163874f, -0.241765827f, -0.0820638761f, -0.122216024f, 0.132651567f,
-0.122216024f, 0.132651567f, 0.515065968f, 0.205271944f, 0.180832058f, -0.127527758f,
-0.123633556f, 0.154476687f, -0.248159677f, -0.254669130f, 0.0208173525f, 0.00527517078f,
-0.483276874f, 0.191274792f, -0.167928949f, 0.200682297f, 0.232745290f, -0.211950779f,
-0.288701504f, -0.334238827f, -0.119621970f, 0.204155236f, -0.119621970f, 0.204155236f,
0.632996142f, 0.0804972649f, 0.189231426f, 0.164325386f, 0.249623299f, 0.164078355f,
0.0676716864f, 0.0479496233f, 0.207636267f, 0.184271768f, -0.300510556f, 0.358790994f,
-0.107678331f, 0.188473806f, 0.565983415f, 0.144723341f, 0.191329703f, 0.213909492f,
-0.0283227600f, -0.373237878f, -0.184958130f, 0.200373843f, 0.0346363746f, -0.0259889495f,
-0.112387165f, -0.167135969f, 0.251426309f, 0.210430339f, -0.477397382f, -0.131372169f,
-0.0667442903f, 0.0997460634f, 0.251426309f, 0.210430339f, -0.317926824f, 0.375238001f,
-0.0621999837f, 0.280056626f, 0.0443522707f, 0.321513236f, 0.471269101f, 0.260774940f,
-0.107678331f, 0.188473806f, 0.0208210852f, 0.350526422f, 0.0157474391f, 0.367335707f,
0.632996142f, 0.0804972649f, 0.646697879f, 0.265504390f, 0.0295150280f, 0.371205181f,
0.376071006f, 0.313471258f, -0.379525930f, 0.364357829f, -0.00628023129f, -0.0373278372f,
0.0291138459f, 0.381194293f, 0.0358079821f, 0.381886899f, 0.0344478637f, 0.386993408f,
0.433862329f, 0.328515977f, 0.359724253f, 0.345606029f, 0.0651357397f, 0.397334814f,
0.388413996f, 0.344747871f, -0.140228778f, 0.216103494f, 0.389989913f, 0.372472703f,
0.444995403f, 0.300240308f, -0.606455386f, 0.100793049f, -0.362332910f, -0.371920794f,
-0.478956074f, 0.234040022f, -0.289441198f, -0.344822973f, -0.0714842379f, -0.0832463056f,
0.375879139f, -0.374975592f, 0.376526117f, -0.326493502f, 0.313251913f, -0.306372881f,
-0.0577337518f, 0.0893306211f, -0.483683407f, -0.179540694f, -0.0763650239f, -0.258294433f,
0.276665628f, -0.0881819800f, -0.167122558f, -0.175508693f, -0.164081737f, 0.176902041f,
0.276665628f, -0.0881819800f, 0.602967978f, -0.260941893f, 0.158573851f, -0.178748295f,
0.159815103f, -0.160761341f, 0.194283918f, -0.165657878f, 0.231515527f, -0.172808051f,
-0.247000366f, 0.277822912f, 0.538969517f, -0.204621449f, 0.531404376f, -0.198565826f,
-0.388338953f, -0.0433262810f, 0.499413073f, -0.181929186f, -0.237337112f, 0.0934364349f,
-0.368045300f, -0.0204487685f, -0.374767631f, -0.00678646797f, -0.0667242110f, -0.248651102f,
-0.248159677f, -0.254669130f, -0.345217139f, -0.00101677026f, -0.353382975f, 0.0210586078f,
-0.322639942f, 0.0211628731f, 0.0184581745f, -0.0366852731f, 0.0259528626f, -0.0136881955f,
-0.339446336f, 0.0286702402f, 0.0335014127f, -0.0271516014f, 0.465966076f, 0.0830826238f,
-0.337860256f, 0.0362124667f, 0.188271523f, -0.146541893f, -0.298272073f, -0.323130161f,
0.0643569306f, -0.0264105909f, -0.353804410f, 0.0433940105f, 0.618646920f, -0.0855877250f,
0.411329508f, -0.0414552018f, -0.427743971f, 0.100272447f, -0.247000366f, 0.277822912f,
0.381912649f, -0.00914942939f, 0.0664352402f, 0.0467514023f, 0.138687640f, -0.114854909f,
-0.0170480162f, -0.372787565f, -0.535477102f, 0.183755845f, -0.155668780f, 0.144164801f,
-0.427504510f, 0.105302416f, -0.484430760f, 0.227277100f, -0.361284673f, -0.373513311f,
-0.316764563f, 0.331503242f, -0.0230990555f, 0.314180285f, 0.101539977f, -0.256640851f,
-0.210743994f, -0.111771651f, -0.560086846f, 0.151153624f, 0.542884171f, 0.141691014f,
0.596041858f, 0.144990161f, 0.239398748f, 0.207432285f, 0.557545543f, 0.155783832f,
0.233033463f, 0.214694947f, 0.572789013f, 0.162068501f, 0.512761712f, 0.176260322f,
0.287076950f, 0.0868823677f, 0.515065968f, 0.205271944f, 0.552475691f, 0.198386014f,
-0.301232725f, 0.347804308f, -0.379525930f, 0.364357829f, 0.561403453f, 0.206571117f,
0.590792358f, 0.206283644f, -0.428855836f, 0.100270294f, 0.300039053f, -0.283949375f,
0.0481642894f, 0.334260821f, -0.173260480f, -0.167126089f, 0.444995403f, 0.300240308f,
0.646697879f, 0.265504390f, 0.375487208f, 0.314186513f, 0.0217850581f, 0.381838262f,
0.404422343f, 0.313856274f, 0.417644382f, 0.314869910f, 0.0358079821f, 0.381886899f,
0.378262609f, 0.358303785f, -0.336999178f, -0.367679387f, -0.295442462f, -0.365161836f,
-0.293496192f, -0.342732310f, -0.298767596f, -0.303165644f, -0.0111337993f, -0.342149645f,
0.310648471f, -0.374146342f, 0.359467417f, -0.373746723f, 0.340779394f, -0.369219989f,
-0.527450860f, -0.203896046f, -0.490746915f, -0.194764644f, 0.314866364f, -0.300261766f,
-0.0298556220f, 0.0591949411f, 0.319549739f, 0.0552458987f, 0.163977623f, -0.209844783f,
-0.149107113f, -0.149005055f, 0.212483421f, -0.191198543f, 0.197611198f, -0.187811792f,
0.174361721f, -0.179897651f, 0.0387913659f, -0.0366905928f, -0.122265801f, -0.126270071f,
0.211038783f, -0.172842503f, 0.246728286f, 0.134398326f, -0.0577337518f, 0.0893306211f,
-0.415295422f, 0.105914228f, -0.292730510f, 0.0379575789f, 0.489636958f, -0.194117576f,
-0.254337519f, 0.0937413648f, 0.336177140f, 0.305443168f, 0.526942134f, -0.164069965f,
0.524966419f, -0.165161178f, -0.379173398f, 0.332068861f, -0.340792000f, 0.00105464540f,
0.525632977f, -0.134992197f, -0.308774501f, 0.00290521770f, -0.375407755f, 0.0294080544f,
0.0178439785f, -0.0365749858f, -0.255016476f, -0.0823200271f, -0.359951973f, 0.0446678996f,
0.0564084686f, -0.0197724514f, -0.315141559f, 0.0424463004f, 0.292196661f, 0.279810339f,
-0.345294952f, 0.0533128195f, 0.0458479226f, -0.00109126628f, 0.0179449394f, 0.00371767790f,
0.365872562f, -0.0412087664f, 0.403013051f, -0.0416624695f, -0.0714842379f, -0.0832463056f,
-0.209011748f, 0.133690849f, 0.0122421598f, 0.0230175443f, -0.0577337518f, 0.0893306211f,
-0.572846889f, 0.141102776f, 0.345340014f, -0.0111671211f, 0.0479373708f, 0.0379454680f,
0.363291621f, -0.00829032529f, 0.381912649f, -0.00914942939f, -0.521542430f, 0.151489466f,
0.345966965f, 0.0110620018f, 0.354562849f, 0.0254590791f, 0.334322065f, 0.0310698878f,
-0.00463629747f, -0.0357710384f, -0.538667142f, 0.185365483f, -0.209011748f, 0.133690849f,
0.398122877f, 0.0403857268f, -0.160881191f, 0.145009249f, -0.155668780f, 0.144164801f,
-0.0714842379f, -0.0832463056f, -0.536377013f, 0.221241340f, -0.0632879063f, -0.247039422f,
-0.155869946f, 0.169341147f, 0.578685045f, -0.223878756f, 0.557447612f, 0.0768704116f,
-0.188812047f, 0.228197843f, 0.246747240f, 0.136472240f, -0.142677084f, 0.213736445f,
-0.118143238f, 0.208306640f, -0.388338953f, -0.0433262810f, -0.163515776f, 0.231573820f,
-0.0738375857f, -0.256104171f, 0.173092276f, 0.191535592f, 0.208548918f, 0.185476139f,
-0.392410189f, 0.0686017647f, 0.555366814f, 0.130478472f, -0.101943128f, -0.113997340f,
0.0716935173f, 0.340265751f, 0.561738014f, 0.148283109f, 0.242452115f, 0.205116034f,
0.561738014f, 0.148283109f, -0.427743971f, 0.100272447f, 0.578137994f, 0.163653031f,
0.251277626f, 0.223055005f, -0.376505047f, 0.343530416f, -0.0714842379f, -0.0832463056f,
0.567448437f, 0.207419440f, 0.590792358f, 0.206283644f, 0.578685045f, -0.223878756f,
0.0635343120f, -0.00499309227f, -0.370767444f, 0.384881169f, -0.485191971f, -0.120962359f,
0.512761712f, 0.176260322f, -0.375972956f, 0.0288736783f, -0.147176415f, -0.185790271f,
0.0752977654f, 0.339190871f, 0.646697879f, 0.265504390f, 0.0282997675f, 0.373214334f,
0.410353780f, 0.316089481f, 0.417644382f, 0.314869910f, 0.0147482762f, 0.389459789f,
-0.182916895f, -0.140514761f, 0.433515042f, 0.330774426f, 0.388069838f, 0.347381502f,
0.378925055f, 0.357438952f, 0.247128293f, -0.116897359f, -0.0230906308f, 0.314556211f,
0.388534039f, 0.370789021f, -0.368050814f, -0.339653373f, -0.292694926f, -0.341653705f,
-0.353774697f, -0.320387989f, 0.599263310f, -0.264537901f, -0.0213720929f, -0.326088905f,
-0.571947694f, 0.141147330f, -0.0577337518f, 0.0893306211f, 0.108424753f, -0.267108470f,
-0.0317604132f, -0.0458168685f, -0.0967136100f, 0.242639020f, -0.486509413f, -0.204596937f,
0.239178345f, -0.219647482f, 0.108424753f, -0.267108470f, -0.280393064f, -0.283867925f,
-0.533659995f, -0.151733354f, 0.0880429000f, -0.240412414f, -0.534965396f, -0.124174178f,
0.142445788f, -0.118948005f, 0.0947291106f, 0.0767719224f, -0.597055852f, -0.0692315027f,
-0.254337519f, 0.0937413648f, -0.308869720f, 0.00354974205f, -0.409894019f, -0.0694356859f,
0.556049764f, -0.137727231f, -0.0317604132f, -0.0458168685f, -0.524152219f, 0.239541322f,
0.108424753f, -0.267108470f, 0.0143662402f, -0.0164190196f, 0.150936082f, 0.128616557f,
0.618646920f, -0.0855877250f, 0.0122421598f, 0.0230175443f, 0.0122421598f, 0.0230175443f,
-0.188812047f, 0.228197843f, 0.00441747159f, -0.297387213f, -0.520719767f, 0.152393058f,
0.392849416f, 0.00738697406f, 0.400074363f, 0.0185570847f, -0.161484867f, -0.192373112f,
-0.554901838f, 0.190730989f, -0.538667142f, 0.185365483f, -0.0667442903f, 0.0997460634f,
0.399885803f, 0.0410231315f, -0.159816831f, 0.145826310f, -0.193316415f, 0.161277503f,
-0.0678345188f, 0.287081748f, -0.383089483f, -0.283330113f, -0.538667142f, 0.185365483f,
0.245664895f, 0.162005231f, 0.173092276f, 0.191535592f, 0.601281762f, 0.120500855f,
0.208548918f, 0.185476139f, 0.246893004f, 0.220670119f, 0.516039073f, 0.178782418f,
-0.254337519f, 0.0937413648f, -0.254337519f, 0.0937413648f, -0.0230990555f, 0.314180285f,
0.610029638f, 0.227215171f, -0.254337519f, 0.0937413648f, 0.0697976872f, 0.343245506f,
0.538969517f, -0.204621449f, 0.00916308723f, 0.359826297f, 0.410353780f, 0.316089481f,
0.423950195f, 0.324112266f, 0.166566655f, 0.145402640f, 0.354594171f, 0.350193948f,
0.433712035f, 0.356235564f, 0.425307065f, 0.364637494f, 0.166924104f, -0.152513608f,
0.594130874f, -0.268246830f, -0.0843627378f, -0.0962528363f, 0.108424753f, -0.267108470f,
0.00760878995f, -0.304247797f, -0.471018314f, -0.178305879f, -0.0817007348f, -0.0933016762f,
0.232274890f, 0.154553935f, 0.108424753f, -0.267108470f, -0.525787771f, -0.161353886f,
-0.206048280f, 0.241006181f, -0.178062543f, -0.184703678f, 0.105906568f, 0.268231422f,
-0.0817007348f, -0.0933016762f, 0.490914792f, 0.276718110f, -0.176861435f, -0.153617889f,
0.0387795344f, 0.0457828715f, 0.456206828f, -0.250739783f, 0.0982551053f, 0.104225174f,
0.142445788f, -0.118948005f, 0.108424753f, -0.267108470f, -0.0817007348f, -0.0933016762f,
-0.340707630f, 0.00498990202f, 0.0947291106f, 0.0767719224f, 0.169802040f, 0.203134149f,
0.577375948f, -0.125099033f, 0.318376005f, -0.0486588739f, 0.388697982f, -0.0351444185f,
0.406605273f, -0.0364143848f, 0.274859309f, 0.0776181892f, 0.349759877f, -7.70174083e-05f,
0.402967423f, 0.00697830878f, 0.105906568f, 0.268231422f, 0.338973522f, 0.0359939188f,
0.394951165f, 0.0322254188f, -0.503028810f, 0.203627899f, -0.0840740278f, -0.234684706f,
0.108424753f, -0.267108470f, 0.286642373f, 0.103878126f, -0.0817007348f, -0.0933016762f,
0.332983583f, -0.0356097035f, 0.628004134f, 0.0766527727f, -0.112659439f, -0.196833044f,
0.568797410f, 0.136423931f, 0.456206828f, -0.250739783f, -0.254337519f, 0.0937413648f,
-0.206692874f, -0.210832119f, 0.550912619f, 0.171586066f, 0.581267595f, 0.213235661f,
0.334484309f, 0.303876013f, -0.469516128f, 0.0883551016f, 0.133899942f, 0.106862970f,
-0.560961962f, -0.114681393f, -0.0840740278f, -0.234684706f, 0.459386230f, -0.236088052f,
0.594130874f, -0.268246830f, -0.124856450f, 0.193096936f, -0.469516128f, 0.0883551016f,
0.514290810f, -0.193822652f, 0.158255994f, 0.233290926f, 0.317973822f, -0.0477817170f,
-0.0817007348f, -0.0933016762f, -0.0702776462f, -0.0671426803f, 0.440836668f, -0.100193374f,
0.326240778f, 0.0523138903f, -0.279556662f, -0.283929169f, -0.485202312f, -0.135626689f,
-0.467358112f, 0.246376559f, 0.232274890f, 0.154553935f, 0.258349210f, -0.269529581f,
0.600620329f, 0.126268178f, -0.0985416993f, 0.245674044f, -0.279264033f, -0.0990248993f,
0.108424753f, -0.267108470f, 0.259638488f, -0.100053802f, 0.605106652f, 0.223564968f,
0.129683495f, -0.100376993f, -0.0953388065f, 0.112722203f, -0.440420747f, -0.0396305211f,
-0.0181254297f, 0.0439292751f, -0.0878356919f, 0.0847257674f, -0.271582603f, 0.126064256f,
-0.183777973f, -0.124357253f, 0.431088895f, 0.0680654719f, -0.469516128f, 0.0883551016f,
-0.445174575f, 0.133306518f, -0.0878356919f, 0.0847257674f, -0.279039949f, 0.0810008645f,
0.612402737f, -0.0826834291f, -0.454494953f, 0.122878648f, 0.244000912f, -0.264438629f,
0.142445788f, -0.118948005f, 0.129683495f, -0.100376993f, -0.210078895f, 0.131698489f,
-0.277847171f, 0.0665081516f, 0.431088895f, 0.0680654719f, 0.252345473f, 0.0688349009f,
0.133899942f, 0.106862970f, 0.133899942f, 0.106862970f, -0.486509413f, -0.204596937f,
-0.0940247625f, 0.0698821172f, 0.133899942f, 0.106862970f, -0.440420747f, -0.0396305211f,
-0.0878356919f, 0.0847257674f, -0.0954068601f, -0.0968973264f, -0.277847171f, 0.0665081516f,
-0.277847171f, 0.0665081516f, 0.266677618f, 0.111257851f, 0.292424291f, -0.230888903f,
-0.0954068601f, -0.0968973264f
};
const Point2f* prevRectifiedPointArr_2f = (const Point2f*)prevRectifiedPointArr;
vector<Point2f> prevRectifiedPoints(prevRectifiedPointArr_2f, prevRectifiedPointArr_2f +
sizeof(prevRectifiedPointArr) / sizeof(prevRectifiedPointArr[0]) / 2);
_prevRectifiedPoints.swap(prevRectifiedPoints);
int validSolutionArr[2] = { 0, 2 };
vector<int> validSolutions(validSolutionArr, validSolutionArr +
sizeof(validSolutionArr) / sizeof(validSolutionArr[0]));
_validSolutions.swap(validSolutions);
vector<Mat> rotations;
vector<Mat> normals;
for (size_t i = 0; i < (sizeof(rotationsArray) / sizeof(*rotationsArray)); i++) {
Mat tempRotMat = Mat(Matx33d(
rotationsArray[i][0],
rotationsArray[i][1],
rotationsArray[i][2],
rotationsArray[i][3],
rotationsArray[i][4],
rotationsArray[i][5],
rotationsArray[i][6],
rotationsArray[i][7],
rotationsArray[i][8]
));
Mat tempNormMat = Mat(Matx31d(
normalsArray[i][0],
normalsArray[i][1],
normalsArray[i][2]
));
rotations.push_back(tempRotMat);
normals.push_back(tempNormMat);
}
_rotations.swap(rotations);
_normals.swap(normals);
_mask = Mat(514, 1, CV_8U, maskArray).clone();
}
bool isValidResult(const vector<int>& solutions)
{
return (solutions == _validSolutions);
}
vector<int> _validSolutions;
vector<Point2f> _prevRectifiedPoints, _currRectifiedPoints;
Mat _mask;
vector<Mat> _rotations, _normals;
};
TEST(Calib3d_FilterDecomposeHomography, regression) { CV_FilterHomographyDecompTest test; test.safe_run(); }
}}
+191 -97
View File
@@ -41,8 +41,11 @@
//M*/
#include "test_precomp.hpp"
#include <opencv2/ts/cuda_test.hpp>
#include <opencv2/ts/cuda_test.hpp> // EXPECT_MAT_NEAR
#include "../src/fisheye.hpp"
#include "opencv2/videoio.hpp"
namespace opencv_test { namespace {
class fisheyeTest : public ::testing::Test {
@@ -60,7 +63,7 @@ protected:
protected:
std::string combine(const std::string& _item1, const std::string& _item2);
cv::Mat mergeRectification(const cv::Mat& l, const cv::Mat& r);
static void merge4(const cv::Mat& tl, const cv::Mat& tr, const cv::Mat& bl, const cv::Mat& br, cv::Mat& merged);
};
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -100,15 +103,15 @@ TEST_F(fisheyeTest, projectPoints)
TEST_F(fisheyeTest, DISABLED_undistortImage)
{
cv::Matx33d K = this->K;
cv::Mat D = cv::Mat(this->D);
cv::Matx33d theK = this->K;
cv::Mat theD = cv::Mat(this->D);
std::string file = combine(datasets_repository_path, "/calib-3_stereo_from_JY/left/stereo_pair_014.jpg");
cv::Matx33d newK = K;
cv::Matx33d newK = theK;
cv::Mat distorted = cv::imread(file), undistorted;
{
newK(0, 0) = 100;
newK(1, 1) = 100;
cv::fisheye::undistortImage(distorted, undistorted, K, D, newK);
cv::fisheye::undistortImage(distorted, undistorted, theK, theD, newK);
cv::Mat correct = cv::imread(combine(datasets_repository_path, "new_f_100.png"));
if (correct.empty())
CV_Assert(cv::imwrite(combine(datasets_repository_path, "new_f_100.png"), undistorted));
@@ -117,8 +120,8 @@ TEST_F(fisheyeTest, DISABLED_undistortImage)
}
{
double balance = 1.0;
cv::fisheye::estimateNewCameraMatrixForUndistortRectify(K, D, distorted.size(), cv::noArray(), newK, balance);
cv::fisheye::undistortImage(distorted, undistorted, K, D, newK);
cv::fisheye::estimateNewCameraMatrixForUndistortRectify(theK, theD, distorted.size(), cv::noArray(), newK, balance);
cv::fisheye::undistortImage(distorted, undistorted, theK, theD, newK);
cv::Mat correct = cv::imread(combine(datasets_repository_path, "balance_1.0.png"));
if (correct.empty())
CV_Assert(cv::imwrite(combine(datasets_repository_path, "balance_1.0.png"), undistorted));
@@ -128,8 +131,8 @@ TEST_F(fisheyeTest, DISABLED_undistortImage)
{
double balance = 0.0;
cv::fisheye::estimateNewCameraMatrixForUndistortRectify(K, D, distorted.size(), cv::noArray(), newK, balance);
cv::fisheye::undistortImage(distorted, undistorted, K, D, newK);
cv::fisheye::estimateNewCameraMatrixForUndistortRectify(theK, theD, distorted.size(), cv::noArray(), newK, balance);
cv::fisheye::undistortImage(distorted, undistorted, theK, theD, newK);
cv::Mat correct = cv::imread(combine(datasets_repository_path, "balance_0.0.png"));
if (correct.empty())
CV_Assert(cv::imwrite(combine(datasets_repository_path, "balance_0.0.png"), undistorted));
@@ -142,7 +145,7 @@ TEST_F(fisheyeTest, jacobians)
{
int n = 10;
cv::Mat X(1, n, CV_64FC3);
cv::Mat om(3, 1, CV_64F), T(3, 1, CV_64F);
cv::Mat om(3, 1, CV_64F), theT(3, 1, CV_64F);
cv::Mat f(2, 1, CV_64F), c(2, 1, CV_64F);
cv::Mat k(4, 1, CV_64F);
double alpha;
@@ -155,8 +158,8 @@ TEST_F(fisheyeTest, jacobians)
r.fill(om, cv::RNG::NORMAL, 0, 1);
om = cv::abs(om);
r.fill(T, cv::RNG::NORMAL, 0, 1);
T = cv::abs(T); T.at<double>(2) = 4; T *= 10;
r.fill(theT, cv::RNG::NORMAL, 0, 1);
theT = cv::abs(theT); theT.at<double>(2) = 4; theT *= 10;
r.fill(f, cv::RNG::NORMAL, 0, 1);
f = cv::abs(f) * 1000;
@@ -170,19 +173,19 @@ TEST_F(fisheyeTest, jacobians)
alpha = 0.01*r.gaussian(1);
cv::Mat x1, x2, xpred;
cv::Matx33d K(f.at<double>(0), alpha * f.at<double>(0), c.at<double>(0),
cv::Matx33d theK(f.at<double>(0), alpha * f.at<double>(0), c.at<double>(0),
0, f.at<double>(1), c.at<double>(1),
0, 0, 1);
cv::Mat jacobians;
cv::fisheye::projectPoints(X, x1, om, T, K, k, alpha, jacobians);
cv::fisheye::projectPoints(X, x1, om, theT, theK, k, alpha, jacobians);
//test on T:
cv::Mat dT(3, 1, CV_64FC1);
r.fill(dT, cv::RNG::NORMAL, 0, 1);
dT *= 1e-9*cv::norm(T);
cv::Mat T2 = T + dT;
cv::fisheye::projectPoints(X, x2, om, T2, K, k, alpha, cv::noArray());
dT *= 1e-9*cv::norm(theT);
cv::Mat T2 = theT + dT;
cv::fisheye::projectPoints(X, x2, om, T2, theK, k, alpha, cv::noArray());
xpred = x1 + cv::Mat(jacobians.colRange(11,14) * dT).reshape(2, 1);
CV_Assert (cv::norm(x2 - xpred) < 1e-10);
@@ -191,7 +194,7 @@ TEST_F(fisheyeTest, jacobians)
r.fill(dom, cv::RNG::NORMAL, 0, 1);
dom *= 1e-9*cv::norm(om);
cv::Mat om2 = om + dom;
cv::fisheye::projectPoints(X, x2, om2, T, K, k, alpha, cv::noArray());
cv::fisheye::projectPoints(X, x2, om2, theT, theK, k, alpha, cv::noArray());
xpred = x1 + cv::Mat(jacobians.colRange(8,11) * dom).reshape(2, 1);
CV_Assert (cv::norm(x2 - xpred) < 1e-10);
@@ -199,8 +202,8 @@ TEST_F(fisheyeTest, jacobians)
cv::Mat df(2, 1, CV_64FC1);
r.fill(df, cv::RNG::NORMAL, 0, 1);
df *= 1e-9*cv::norm(f);
cv::Matx33d K2 = K + cv::Matx33d(df.at<double>(0), df.at<double>(0) * alpha, 0, 0, df.at<double>(1), 0, 0, 0, 0);
cv::fisheye::projectPoints(X, x2, om, T, K2, k, alpha, cv::noArray());
cv::Matx33d K2 = theK + cv::Matx33d(df.at<double>(0), df.at<double>(0) * alpha, 0, 0, df.at<double>(1), 0, 0, 0, 0);
cv::fisheye::projectPoints(X, x2, om, theT, K2, k, alpha, cv::noArray());
xpred = x1 + cv::Mat(jacobians.colRange(0,2) * df).reshape(2, 1);
CV_Assert (cv::norm(x2 - xpred) < 1e-10);
@@ -208,8 +211,8 @@ TEST_F(fisheyeTest, jacobians)
cv::Mat dc(2, 1, CV_64FC1);
r.fill(dc, cv::RNG::NORMAL, 0, 1);
dc *= 1e-9*cv::norm(c);
K2 = K + cv::Matx33d(0, 0, dc.at<double>(0), 0, 0, dc.at<double>(1), 0, 0, 0);
cv::fisheye::projectPoints(X, x2, om, T, K2, k, alpha, cv::noArray());
K2 = theK + cv::Matx33d(0, 0, dc.at<double>(0), 0, 0, dc.at<double>(1), 0, 0, 0);
cv::fisheye::projectPoints(X, x2, om, theT, K2, k, alpha, cv::noArray());
xpred = x1 + cv::Mat(jacobians.colRange(2,4) * dc).reshape(2, 1);
CV_Assert (cv::norm(x2 - xpred) < 1e-10);
@@ -218,7 +221,7 @@ TEST_F(fisheyeTest, jacobians)
r.fill(dk, cv::RNG::NORMAL, 0, 1);
dk *= 1e-9*cv::norm(k);
cv::Mat k2 = k + dk;
cv::fisheye::projectPoints(X, x2, om, T, K, k2, alpha, cv::noArray());
cv::fisheye::projectPoints(X, x2, om, theT, theK, k2, alpha, cv::noArray());
xpred = x1 + cv::Mat(jacobians.colRange(4,8) * dk).reshape(2, 1);
CV_Assert (cv::norm(x2 - xpred) < 1e-10);
@@ -227,8 +230,8 @@ TEST_F(fisheyeTest, jacobians)
r.fill(dalpha, cv::RNG::NORMAL, 0, 1);
dalpha *= 1e-9*cv::norm(f);
double alpha2 = alpha + dalpha.at<double>(0);
K2 = K + cv::Matx33d(0, f.at<double>(0) * dalpha.at<double>(0), 0, 0, 0, 0, 0, 0, 0);
cv::fisheye::projectPoints(X, x2, om, T, K, k, alpha2, cv::noArray());
K2 = theK + cv::Matx33d(0, f.at<double>(0) * dalpha.at<double>(0), 0, 0, 0, 0, 0, 0, 0);
cv::fisheye::projectPoints(X, x2, om, theT, theK, k, alpha2, cv::noArray());
xpred = x1 + cv::Mat(jacobians.col(14) * dalpha).reshape(2, 1);
CV_Assert (cv::norm(x2 - xpred) < 1e-10);
}
@@ -244,13 +247,13 @@ TEST_F(fisheyeTest, Calibration)
cv::FileStorage fs_left(combine(folder, "left.xml"), cv::FileStorage::READ);
CV_Assert(fs_left.isOpened());
for(int i = 0; i < n_images; ++i)
fs_left[cv::format("image_%d", i )] >> imagePoints[i];
fs_left[cv::format("image_%d", i )] >> imagePoints[i];
fs_left.release();
cv::FileStorage fs_object(combine(folder, "object.xml"), cv::FileStorage::READ);
CV_Assert(fs_object.isOpened());
for(int i = 0; i < n_images; ++i)
fs_object[cv::format("image_%d", i )] >> objectPoints[i];
fs_object[cv::format("image_%d", i )] >> objectPoints[i];
fs_object.release();
int flag = 0;
@@ -258,14 +261,14 @@ TEST_F(fisheyeTest, Calibration)
flag |= cv::fisheye::CALIB_CHECK_COND;
flag |= cv::fisheye::CALIB_FIX_SKEW;
cv::Matx33d K;
cv::Vec4d D;
cv::Matx33d theK;
cv::Vec4d theD;
cv::fisheye::calibrate(objectPoints, imagePoints, imageSize, K, D,
cv::fisheye::calibrate(objectPoints, imagePoints, imageSize, theK, theD,
cv::noArray(), cv::noArray(), flag, cv::TermCriteria(3, 20, 1e-6));
EXPECT_MAT_NEAR(K, this->K, 1e-10);
EXPECT_MAT_NEAR(D, this->D, 1e-10);
EXPECT_MAT_NEAR(theK, this->K, 1e-10);
EXPECT_MAT_NEAR(theD, this->D, 1e-10);
}
TEST_F(fisheyeTest, Homography)
@@ -279,13 +282,13 @@ TEST_F(fisheyeTest, Homography)
cv::FileStorage fs_left(combine(folder, "left.xml"), cv::FileStorage::READ);
CV_Assert(fs_left.isOpened());
for(int i = 0; i < n_images; ++i)
fs_left[cv::format("image_%d", i )] >> imagePoints[i];
fs_left[cv::format("image_%d", i )] >> imagePoints[i];
fs_left.release();
cv::FileStorage fs_object(combine(folder, "object.xml"), cv::FileStorage::READ);
CV_Assert(fs_object.isOpened());
for(int i = 0; i < n_images; ++i)
fs_object[cv::format("image_%d", i )] >> objectPoints[i];
fs_object[cv::format("image_%d", i )] >> objectPoints[i];
fs_object.release();
cv::internal::IntrinsicParams param;
@@ -302,15 +305,15 @@ TEST_F(fisheyeTest, Homography)
int Np = imagePointsNormalized.cols;
cv::calcCovarMatrix(_objectPoints, covObjectPoints, objectPointsMean, cv::COVAR_NORMAL | cv::COVAR_COLS);
cv::SVD svd(covObjectPoints);
cv::Mat R(svd.vt);
cv::Mat theR(svd.vt);
if (cv::norm(R(cv::Rect(2, 0, 1, 2))) < 1e-6)
R = cv::Mat::eye(3,3, CV_64FC1);
if (cv::determinant(R) < 0)
R = -R;
if (cv::norm(theR(cv::Rect(2, 0, 1, 2))) < 1e-6)
theR = cv::Mat::eye(3,3, CV_64FC1);
if (cv::determinant(theR) < 0)
theR = -theR;
cv::Mat T = -R * objectPointsMean;
cv::Mat X_new = R * _objectPoints + T * cv::Mat::ones(1, Np, CV_64FC1);
cv::Mat theT = -theR * objectPointsMean;
cv::Mat X_new = theR * _objectPoints + theT * cv::Mat::ones(1, Np, CV_64FC1);
cv::Mat H = cv::internal::ComputeHomography(imagePointsNormalized, X_new.rowRange(0, 2));
cv::Mat M = cv::Mat::ones(3, X_new.cols, CV_64FC1);
@@ -329,7 +332,7 @@ TEST_F(fisheyeTest, Homography)
EXPECT_MAT_NEAR(std_err, correct_std_err, 1e-12);
}
TEST_F(fisheyeTest, EtimateUncertainties)
TEST_F(fisheyeTest, EstimateUncertainties)
{
const int n_images = 34;
@@ -340,13 +343,13 @@ TEST_F(fisheyeTest, EtimateUncertainties)
cv::FileStorage fs_left(combine(folder, "left.xml"), cv::FileStorage::READ);
CV_Assert(fs_left.isOpened());
for(int i = 0; i < n_images; ++i)
fs_left[cv::format("image_%d", i )] >> imagePoints[i];
fs_left[cv::format("image_%d", i )] >> imagePoints[i];
fs_left.release();
cv::FileStorage fs_object(combine(folder, "object.xml"), cv::FileStorage::READ);
CV_Assert(fs_object.isOpened());
for(int i = 0; i < n_images; ++i)
fs_object[cv::format("image_%d", i )] >> objectPoints[i];
fs_object[cv::format("image_%d", i )] >> objectPoints[i];
fs_object.release();
int flag = 0;
@@ -354,20 +357,20 @@ TEST_F(fisheyeTest, EtimateUncertainties)
flag |= cv::fisheye::CALIB_CHECK_COND;
flag |= cv::fisheye::CALIB_FIX_SKEW;
cv::Matx33d K;
cv::Vec4d D;
cv::Matx33d theK;
cv::Vec4d theD;
std::vector<cv::Vec3d> rvec;
std::vector<cv::Vec3d> tvec;
cv::fisheye::calibrate(objectPoints, imagePoints, imageSize, K, D,
cv::fisheye::calibrate(objectPoints, imagePoints, imageSize, theK, theD,
rvec, tvec, flag, cv::TermCriteria(3, 20, 1e-6));
cv::internal::IntrinsicParams param, errors;
cv::Vec2d err_std;
double thresh_cond = 1e6;
int check_cond = 1;
param.Init(cv::Vec2d(K(0,0), K(1,1)), cv::Vec2d(K(0,2), K(1, 2)), D);
param.isEstimate = std::vector<int>(9, 1);
param.Init(cv::Vec2d(theK(0,0), theK(1,1)), cv::Vec2d(theK(0,2), theK(1, 2)), theD);
param.isEstimate = std::vector<uchar>(9, 1);
param.isEstimate[4] = 0;
errors.isEstimate = param.isEstimate;
@@ -381,11 +384,11 @@ TEST_F(fisheyeTest, EtimateUncertainties)
EXPECT_MAT_NEAR(errors.c, cv::Vec2d(0.890439368129246, 0.816096854937896), 1e-10);
EXPECT_MAT_NEAR(errors.k, cv::Vec4d(0.00516248605191506, 0.0168181467500934, 0.0213118690274604, 0.00916010877545648), 1e-10);
EXPECT_MAT_NEAR(err_std, cv::Vec2d(0.187475975266883, 0.185678953263995), 1e-10);
CV_Assert(abs(rms - 0.263782587133546) < 1e-10);
CV_Assert(fabs(rms - 0.263782587133546) < 1e-10);
CV_Assert(errors.alpha == 0);
}
TEST_F(fisheyeTest, rectify)
TEST_F(fisheyeTest, stereoRectify)
{
const std::string folder =combine(datasets_repository_path, "calib-3_stereo_from_JY");
@@ -393,28 +396,73 @@ TEST_F(fisheyeTest, rectify)
cv::Matx33d K1 = this->K, K2 = K1;
cv::Mat D1 = cv::Mat(this->D), D2 = D1;
cv::Vec3d T = this->T;
cv::Matx33d R = this->R;
cv::Vec3d theT = this->T;
cv::Matx33d theR = this->R;
double balance = 0.0, fov_scale = 1.1;
cv::Mat R1, R2, P1, P2, Q;
cv::fisheye::stereoRectify(K1, D1, K2, D2, calibration_size, R, T, R1, R2, P1, P2, Q,
cv::fisheye::stereoRectify(K1, D1, K2, D2, calibration_size, theR, theT, R1, R2, P1, P2, Q,
cv::CALIB_ZERO_DISPARITY, requested_size, balance, fov_scale);
// Collected with these CMake flags: -DWITH_IPP=OFF -DCV_ENABLE_INTRINSICS=OFF -DCV_DISABLE_OPTIMIZATION=ON -DCMAKE_BUILD_TYPE=Debug
cv::Matx33d R1_ref(
0.9992853269091279, 0.03779164101000276, -0.0007920188690205426,
-0.03778569762983931, 0.9992646472015868, 0.006511981857667881,
0.001037534936357442, -0.006477400933964018, 0.9999784831677112
);
cv::Matx33d R2_ref(
0.9994868963898833, -0.03197579751378937, -0.001868774538573449,
0.03196298186616116, 0.9994677442608699, -0.0065265589947392,
0.002076471801477729, 0.006463478587068991, 0.9999769555891836
);
cv::Matx34d P1_ref(
420.8551870450913, 0, 586.501617798451, 0,
0, 420.8551870450913, 374.7667511986098, 0,
0, 0, 1, 0
);
cv::Matx34d P2_ref(
420.8551870450913, 0, 586.501617798451, -41.77758076597302,
0, 420.8551870450913, 374.7667511986098, 0,
0, 0, 1, 0
);
cv::Matx44d Q_ref(
1, 0, 0, -586.501617798451,
0, 1, 0, -374.7667511986098,
0, 0, 0, 420.8551870450913,
0, 0, 10.07370889670733, -0
);
const double eps = 1e-10;
EXPECT_MAT_NEAR(R1_ref, R1, eps);
EXPECT_MAT_NEAR(R2_ref, R2, eps);
EXPECT_MAT_NEAR(P1_ref, P1, eps);
EXPECT_MAT_NEAR(P2_ref, P2, eps);
EXPECT_MAT_NEAR(Q_ref, Q, eps);
if (::testing::Test::HasFailure())
{
std::cout << "Actual values are:" << std::endl
<< "R1 =" << std::endl << R1 << std::endl
<< "R2 =" << std::endl << R2 << std::endl
<< "P1 =" << std::endl << P1 << std::endl
<< "P2 =" << std::endl << P2 << std::endl
<< "Q =" << std::endl << Q << std::endl;
}
#if 1 // Debug code
cv::Mat lmapx, lmapy, rmapx, rmapy;
//rewrite for fisheye
cv::fisheye::initUndistortRectifyMap(K1, D1, R1, P1, requested_size, CV_32F, lmapx, lmapy);
cv::fisheye::initUndistortRectifyMap(K2, D2, R2, P2, requested_size, CV_32F, rmapx, rmapy);
cv::Mat l, r, lundist, rundist;
cv::VideoCapture lcap(combine(folder, "left/stereo_pair_%03d.jpg")),
rcap(combine(folder, "right/stereo_pair_%03d.jpg"));
for(int i = 0;; ++i)
for (int i = 0; i < 34; ++i)
{
lcap >> l; rcap >> r;
if (l.empty() || r.empty())
break;
SCOPED_TRACE(cv::format("image %d", i));
l = imread(combine(folder, cv::format("left/stereo_pair_%03d.jpg", i)), cv::IMREAD_COLOR);
r = imread(combine(folder, cv::format("right/stereo_pair_%03d.jpg", i)), cv::IMREAD_COLOR);
ASSERT_FALSE(l.empty());
ASSERT_FALSE(r.empty());
int ndisp = 128;
cv::rectangle(l, cv::Rect(255, 0, 829, l.rows-1), cv::Scalar(0, 0, 255));
@@ -423,15 +471,18 @@ TEST_F(fisheyeTest, rectify)
cv::remap(l, lundist, lmapx, lmapy, cv::INTER_LINEAR);
cv::remap(r, rundist, rmapx, rmapy, cv::INTER_LINEAR);
cv::Mat rectification = mergeRectification(lundist, rundist);
for (int ii = 0; ii < lundist.rows; ii += 20)
{
cv::line(lundist, cv::Point(0, ii), cv::Point(lundist.cols, ii), cv::Scalar(0, 255, 0));
cv::line(rundist, cv::Point(0, ii), cv::Point(lundist.cols, ii), cv::Scalar(0, 255, 0));
}
cv::Mat correct = cv::imread(combine(datasets_repository_path, cv::format("rectification_AB_%03d.png", i)));
cv::Mat rectification;
merge4(l, r, lundist, rundist, rectification);
if (correct.empty())
cv::imwrite(combine(datasets_repository_path, cv::format("rectification_AB_%03d.png", i)), rectification);
else
EXPECT_MAT_NEAR(correct, rectification, 1e-10);
}
cv::imwrite(cv::format("fisheye_rectification_AB_%03d.png", i), rectification);
}
#endif
}
TEST_F(fisheyeTest, stereoCalibrate)
@@ -447,33 +498,32 @@ TEST_F(fisheyeTest, stereoCalibrate)
cv::FileStorage fs_left(combine(folder, "left.xml"), cv::FileStorage::READ);
CV_Assert(fs_left.isOpened());
for(int i = 0; i < n_images; ++i)
fs_left[cv::format("image_%d", i )] >> leftPoints[i];
fs_left[cv::format("image_%d", i )] >> leftPoints[i];
fs_left.release();
cv::FileStorage fs_right(combine(folder, "right.xml"), cv::FileStorage::READ);
CV_Assert(fs_right.isOpened());
for(int i = 0; i < n_images; ++i)
fs_right[cv::format("image_%d", i )] >> rightPoints[i];
fs_right[cv::format("image_%d", i )] >> rightPoints[i];
fs_right.release();
cv::FileStorage fs_object(combine(folder, "object.xml"), cv::FileStorage::READ);
CV_Assert(fs_object.isOpened());
for(int i = 0; i < n_images; ++i)
fs_object[cv::format("image_%d", i )] >> objectPoints[i];
fs_object[cv::format("image_%d", i )] >> objectPoints[i];
fs_object.release();
cv::Matx33d K1, K2, R;
cv::Vec3d T;
cv::Matx33d K1, K2, theR;
cv::Vec3d theT;
cv::Vec4d D1, D2;
int flag = 0;
flag |= cv::fisheye::CALIB_RECOMPUTE_EXTRINSIC;
flag |= cv::fisheye::CALIB_CHECK_COND;
flag |= cv::fisheye::CALIB_FIX_SKEW;
// flag |= cv::fisheye::CALIB_FIX_INTRINSIC;
cv::fisheye::stereoCalibrate(objectPoints, leftPoints, rightPoints,
K1, D1, K2, D2, imageSize, R, T, flag,
K1, D1, K2, D2, imageSize, theR, theT, flag,
cv::TermCriteria(3, 12, 0));
cv::Matx33d R_correct( 0.9975587205950972, 0.06953016383322372, 0.006492709911733523,
@@ -491,8 +541,8 @@ TEST_F(fisheyeTest, stereoCalibrate)
cv::Vec4d D1_correct (-7.44253716539556e-05, -0.00702662033932424, 0.00737569823650885, -0.00342230256441771);
cv::Vec4d D2_correct (-0.0130785435677431, 0.0284434505383497, -0.0360333869900506, 0.0144724062347222);
EXPECT_MAT_NEAR(R, R_correct, 1e-10);
EXPECT_MAT_NEAR(T, T_correct, 1e-10);
EXPECT_MAT_NEAR(theR, R_correct, 1e-10);
EXPECT_MAT_NEAR(theT, T_correct, 1e-10);
EXPECT_MAT_NEAR(K1, K1_correct, 1e-10);
EXPECT_MAT_NEAR(K2, K2_correct, 1e-10);
@@ -515,23 +565,23 @@ TEST_F(fisheyeTest, stereoCalibrateFixIntrinsic)
cv::FileStorage fs_left(combine(folder, "left.xml"), cv::FileStorage::READ);
CV_Assert(fs_left.isOpened());
for(int i = 0; i < n_images; ++i)
fs_left[cv::format("image_%d", i )] >> leftPoints[i];
fs_left[cv::format("image_%d", i )] >> leftPoints[i];
fs_left.release();
cv::FileStorage fs_right(combine(folder, "right.xml"), cv::FileStorage::READ);
CV_Assert(fs_right.isOpened());
for(int i = 0; i < n_images; ++i)
fs_right[cv::format("image_%d", i )] >> rightPoints[i];
fs_right[cv::format("image_%d", i )] >> rightPoints[i];
fs_right.release();
cv::FileStorage fs_object(combine(folder, "object.xml"), cv::FileStorage::READ);
CV_Assert(fs_object.isOpened());
for(int i = 0; i < n_images; ++i)
fs_object[cv::format("image_%d", i )] >> objectPoints[i];
fs_object[cv::format("image_%d", i )] >> objectPoints[i];
fs_object.release();
cv::Matx33d R;
cv::Vec3d T;
cv::Matx33d theR;
cv::Vec3d theT;
int flag = 0;
flag |= cv::fisheye::CALIB_RECOMPUTE_EXTRINSIC;
@@ -551,7 +601,7 @@ TEST_F(fisheyeTest, stereoCalibrateFixIntrinsic)
cv::Vec4d D2 (-0.0130785435677431, 0.0284434505383497, -0.0360333869900506, 0.0144724062347222);
cv::fisheye::stereoCalibrate(objectPoints, leftPoints, rightPoints,
K1, D1, K2, D2, imageSize, R, T, flag,
K1, D1, K2, D2, imageSize, theR, theT, flag,
cv::TermCriteria(3, 12, 0));
cv::Matx33d R_correct( 0.9975587205950972, 0.06953016383322372, 0.006492709911733523,
@@ -560,8 +610,50 @@ TEST_F(fisheyeTest, stereoCalibrateFixIntrinsic)
cv::Vec3d T_correct(-0.099402724724121, 0.00270812139265413, 0.00129330292472699);
EXPECT_MAT_NEAR(R, R_correct, 1e-10);
EXPECT_MAT_NEAR(T, T_correct, 1e-10);
EXPECT_MAT_NEAR(theR, R_correct, 1e-10);
EXPECT_MAT_NEAR(theT, T_correct, 1e-10);
}
TEST_F(fisheyeTest, CalibrationWithDifferentPointsNumber)
{
const int n_images = 2;
std::vector<std::vector<cv::Point2d> > imagePoints(n_images);
std::vector<std::vector<cv::Point3d> > objectPoints(n_images);
std::vector<cv::Point2d> imgPoints1(10);
std::vector<cv::Point2d> imgPoints2(15);
std::vector<cv::Point3d> objectPoints1(imgPoints1.size());
std::vector<cv::Point3d> objectPoints2(imgPoints2.size());
for (size_t i = 0; i < imgPoints1.size(); i++)
{
imgPoints1[i] = cv::Point2d((double)i, (double)i);
objectPoints1[i] = cv::Point3d((double)i, (double)i, 10.0);
}
for (size_t i = 0; i < imgPoints2.size(); i++)
{
imgPoints2[i] = cv::Point2d(i + 0.5, i + 0.5);
objectPoints2[i] = cv::Point3d(i + 0.5, i + 0.5, 10.0);
}
imagePoints[0] = imgPoints1;
imagePoints[1] = imgPoints2;
objectPoints[0] = objectPoints1;
objectPoints[1] = objectPoints2;
cv::Matx33d theK = cv::Matx33d::eye();
cv::Vec4d theD;
int flag = 0;
flag |= cv::fisheye::CALIB_RECOMPUTE_EXTRINSIC;
flag |= cv::fisheye::CALIB_USE_INTRINSIC_GUESS;
flag |= cv::fisheye::CALIB_FIX_SKEW;
cv::fisheye::calibrate(objectPoints, imagePoints, cv::Size(100, 100), theK, theD,
cv::noArray(), cv::noArray(), flag, cv::TermCriteria(3, 20, 1e-6));
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -597,17 +689,19 @@ std::string fisheyeTest::combine(const std::string& _item1, const std::string& _
return item1 + (last != '/' ? "/" : "") + item2;
}
cv::Mat fisheyeTest::mergeRectification(const cv::Mat& l, const cv::Mat& r)
void fisheyeTest::merge4(const cv::Mat& tl, const cv::Mat& tr, const cv::Mat& bl, const cv::Mat& br, cv::Mat& merged)
{
CV_Assert(l.type() == r.type() && l.size() == r.size());
cv::Mat merged(l.rows, l.cols * 2, l.type());
cv::Mat lpart = merged.colRange(0, l.cols);
cv::Mat rpart = merged.colRange(l.cols, merged.cols);
l.copyTo(lpart);
r.copyTo(rpart);
int type = tl.type();
cv::Size sz = tl.size();
ASSERT_EQ(type, tr.type()); ASSERT_EQ(type, bl.type()); ASSERT_EQ(type, br.type());
ASSERT_EQ(sz.width, tr.cols); ASSERT_EQ(sz.width, bl.cols); ASSERT_EQ(sz.width, br.cols);
ASSERT_EQ(sz.height, tr.rows); ASSERT_EQ(sz.height, bl.rows); ASSERT_EQ(sz.height, br.rows);
for(int i = 0; i < l.rows; i+=20)
cv::line(merged, cv::Point(0, i), cv::Point(merged.cols, i), cv::Scalar(0, 255, 0));
return merged;
merged.create(cv::Size(sz.width * 2, sz.height * 2), type);
tl.copyTo(merged(cv::Rect(0, 0, sz.width, sz.height)));
tr.copyTo(merged(cv::Rect(sz.width, 0, sz.width, sz.height)));
bl.copyTo(merged(cv::Rect(0, sz.height, sz.width, sz.height)));
br.copyTo(merged(cv::Rect(sz.width, sz.height, sz.width, sz.height)));
}
}} // namespace
+59 -48
View File
@@ -42,10 +42,9 @@
#include "test_precomp.hpp"
#include "opencv2/calib3d/calib3d_c.h"
using namespace cv;
using namespace std;
namespace cvtest {
int cvTsRodrigues( const CvMat* src, CvMat* dst, CvMat* jacobian )
static int cvTsRodrigues( const CvMat* src, CvMat* dst, CvMat* jacobian )
{
int depth;
int i;
@@ -348,14 +347,18 @@ int cvTsRodrigues( const CvMat* src, CvMat* dst, CvMat* jacobian )
}
void cvtest::Rodrigues(const Mat& src, Mat& dst, Mat* jac)
/*extern*/ void Rodrigues(const Mat& src, Mat& dst, Mat* jac)
{
CvMat _src = src, _dst = dst, _jac;
CV_Assert(src.data != dst.data && "Inplace is not supported");
CV_Assert(!dst.empty() && "'dst' must be allocated");
CvMat _src = cvMat(src), _dst = cvMat(dst), _jac;
if( jac )
_jac = *jac;
_jac = cvMat(*jac);
cvTsRodrigues(&_src, &_dst, jac ? &_jac : 0);
}
} // namespace
namespace opencv_test {
static void test_convertHomogeneous( const Mat& _src, Mat& _dst )
{
@@ -463,6 +466,7 @@ static void test_convertHomogeneous( const Mat& _src, Mat& _dst )
dst.convertTo(_dst, _dst.depth());
}
namespace {
void
test_projectPoints( const Mat& _3d, const Mat& Rt, const Mat& A, Mat& _2d, RNG* rng, double sigma )
@@ -663,13 +667,13 @@ void CV_RodriguesTest::run_func()
if( calc_jacobians )
{
v2m_jac = test_mat[OUTPUT][1];
m2v_jac = test_mat[OUTPUT][3];
v2m_jac = cvMat(test_mat[OUTPUT][1]);
m2v_jac = cvMat(test_mat[OUTPUT][3]);
}
if( !test_cpp )
{
CvMat _input = test_mat[INPUT][0], _output = test_mat[OUTPUT][0], _output2 = test_mat[OUTPUT][2];
CvMat _input = cvMat(test_mat[INPUT][0]), _output = cvMat(test_mat[OUTPUT][0]), _output2 = cvMat(test_mat[OUTPUT][2]);
cvRodrigues2( &_input, &_output, calc_jacobians ? &v2m_jac : 0 );
cvRodrigues2( &_output, &_output2, calc_jacobians ? &m2v_jac : 0 );
}
@@ -728,7 +732,7 @@ void CV_RodriguesTest::prepare_to_validation( int /*test_case_idx*/ )
cvtest::Rodrigues( m, vec2, m2v_jac );
cvtest::copy( vec, vec2 );
theta0 = norm( vec2, CV_L2 );
theta0 = cvtest::norm( vec2, CV_L2 );
theta1 = fmod( theta0, CV_PI*2 );
if( theta1 > CV_PI )
@@ -973,26 +977,12 @@ int CV_FundamentalMatTest::prepare_test_case( int test_case_idx )
return code;
}
void CV_FundamentalMatTest::run_func()
{
//if(!test_cpp)
{
CvMat _input0 = test_mat[INPUT][0], _input1 = test_mat[INPUT][1];
CvMat F = test_mat[TEMP][0], mask = test_mat[TEMP][1];
f_result = cvFindFundamentalMat( &_input0, &_input1, &F, method, MAX(sigma*3, 0.01), 0, &mask );
}
/*else
{
cv::findFundamentalMat(const Mat& points1, const Mat& points2,
vector<uchar>& mask, int method=FM_RANSAC,
double param1=3., double param2=0.99 );
CV_EXPORTS Mat findFundamentalMat( const Mat& points1, const Mat& points2,
int method=FM_RANSAC,
double param1=3., double param2=0.99 );
}*/
// cvFindFundamentalMat calls cv::findFundamentalMat
CvMat _input0 = cvMat(test_mat[INPUT][0]), _input1 = cvMat(test_mat[INPUT][1]);
CvMat F = cvMat(test_mat[TEMP][0]), mask = cvMat(test_mat[TEMP][1]);
f_result = cvFindFundamentalMat( &_input0, &_input1, &F, method, MAX(sigma*3, 0.01), 0, &mask );
}
@@ -1021,12 +1011,12 @@ void CV_FundamentalMatTest::prepare_to_validation( int test_case_idx )
cv::gemm( T, invA2, 1, Mat(), 0, F0 );
F0 *= 1./f0[8];
uchar* status = test_mat[TEMP][1].data;
double err_level = method <= CV_FM_8POINT ? 1 : get_success_error_level( test_case_idx, OUTPUT, 1 );
uchar* mtfm1 = test_mat[REF_OUTPUT][1].data;
uchar* mtfm2 = test_mat[OUTPUT][1].data;
double* f_prop1 = (double*)test_mat[REF_OUTPUT][0].data;
double* f_prop2 = (double*)test_mat[OUTPUT][0].data;
uchar* status = test_mat[TEMP][1].ptr();
double err_level = get_success_error_level( test_case_idx, OUTPUT, 1 );
uchar* mtfm1 = test_mat[REF_OUTPUT][1].ptr();
uchar* mtfm2 = test_mat[OUTPUT][1].ptr();
double* f_prop1 = test_mat[REF_OUTPUT][0].ptr<double>();
double* f_prop2 = test_mat[OUTPUT][0].ptr<double>();
int i, pt_count = test_mat[INPUT][2].cols;
Mat p1( 1, pt_count, CV_64FC2 );
@@ -1081,7 +1071,9 @@ protected:
void run_func();
void prepare_to_validation( int );
#if 0
double sampson_error(const double* f, double x1, double y1, double x2, double y2);
#endif
int method;
int img_size;
@@ -1313,6 +1305,7 @@ void CV_EssentialMatTest::run_func()
mask2.copyTo(test_mat[TEMP][4]);
}
#if 0
double CV_EssentialMatTest::sampson_error(const double * f, double x1, double y1, double x2, double y2)
{
double Fx1[3] = {
@@ -1330,8 +1323,8 @@ double CV_EssentialMatTest::sampson_error(const double * f, double x1, double y1
double error = x2tFx1 * x2tFx1 / (Fx1[0] * Fx1[0] + Fx1[1] * Fx1[1] + Ftx2[0] * Ftx2[0] + Ftx2[1] * Ftx2[1]);
error = sqrt(error);
return error;
}
#endif
void CV_EssentialMatTest::prepare_to_validation( int test_case_idx )
{
@@ -1357,12 +1350,12 @@ void CV_EssentialMatTest::prepare_to_validation( int test_case_idx )
cv::gemm( T1, T2, 1, Mat(), 0, F0 );
F0 *= 1./f0[8];
uchar* status = test_mat[TEMP][1].data;
uchar* status = test_mat[TEMP][1].ptr();
double err_level = get_success_error_level( test_case_idx, OUTPUT, 1 );
uchar* mtfm1 = test_mat[REF_OUTPUT][1].data;
uchar* mtfm2 = test_mat[OUTPUT][1].data;
double* e_prop1 = (double*)test_mat[REF_OUTPUT][0].data;
double* e_prop2 = (double*)test_mat[OUTPUT][0].data;
uchar* mtfm1 = test_mat[REF_OUTPUT][1].ptr();
uchar* mtfm2 = test_mat[OUTPUT][1].ptr();
double* e_prop1 = test_mat[REF_OUTPUT][0].ptr<double>();
double* e_prop2 = test_mat[OUTPUT][0].ptr<double>();
Mat E_prop2 = Mat(3, 1, CV_64F, e_prop2);
int i, pt_count = test_mat[INPUT][2].cols;
@@ -1407,12 +1400,12 @@ void CV_EssentialMatTest::prepare_to_validation( int test_case_idx )
double* pose_prop1 = (double*)test_mat[REF_OUTPUT][2].data;
double* pose_prop2 = (double*)test_mat[OUTPUT][2].data;
double terr1 = cvtest::norm(Rt0.col(3) / norm(Rt0.col(3)) + test_mat[TEMP][3], NORM_L2);
double terr2 = cvtest::norm(Rt0.col(3) / norm(Rt0.col(3)) - test_mat[TEMP][3], NORM_L2);
Mat rvec;
Rodrigues(Rt0.colRange(0, 3), rvec);
double* pose_prop1 = test_mat[REF_OUTPUT][2].ptr<double>();
double* pose_prop2 = test_mat[OUTPUT][2].ptr<double>();
double terr1 = cvtest::norm(Rt0.col(3) / cvtest::norm(Rt0.col(3), NORM_L2) + test_mat[TEMP][3], NORM_L2);
double terr2 = cvtest::norm(Rt0.col(3) / cvtest::norm(Rt0.col(3), NORM_L2) - test_mat[TEMP][3], NORM_L2);
Mat rvec(3, 1, CV_32F);
cvtest::Rodrigues(Rt0.colRange(0, 3), rvec);
pose_prop1[0] = 0;
// No check for CV_LMeDS on translation. Since it
// involves with some degraded problem, when data is exact inliers.
@@ -1550,7 +1543,7 @@ void CV_ConvertHomogeneousTest::fill_array( int /*test_case_idx*/, int /*i*/, in
void CV_ConvertHomogeneousTest::run_func()
{
CvMat _input = test_mat[INPUT][0], _output = test_mat[OUTPUT][0];
CvMat _input = cvMat(test_mat[INPUT][0]), _output = cvMat(test_mat[OUTPUT][0]);
cvConvertPointsHomogeneous( &_input, &_output );
}
@@ -1685,7 +1678,7 @@ void CV_ComputeEpilinesTest::fill_array( int test_case_idx, int i, int j, Mat& a
void CV_ComputeEpilinesTest::run_func()
{
CvMat _points = test_mat[INPUT][0], _F = test_mat[INPUT][1], _lines = test_mat[OUTPUT][0];
CvMat _points = cvMat(test_mat[INPUT][0]), _F = cvMat(test_mat[INPUT][1]), _lines = cvMat(test_mat[OUTPUT][0]);
cvComputeCorrespondEpilines( &_points, which_image, &_F, &_lines );
}
@@ -1723,4 +1716,22 @@ TEST(Calib3d_ConvertHomogeneoous, accuracy) { CV_ConvertHomogeneousTest test; te
TEST(Calib3d_ComputeEpilines, accuracy) { CV_ComputeEpilinesTest test; test.safe_run(); }
TEST(Calib3d_FindEssentialMat, accuracy) { CV_EssentialMatTest test; test.safe_run(); }
TEST(Calib3d_FindFundamentalMat, correctMatches)
{
double fdata[] = {0, 0, 0, 0, 0, -1, 0, 1, 0};
double p1data[] = {200, 0, 1};
double p2data[] = {170, 0, 1};
Mat F(3, 3, CV_64F, fdata);
Mat p1(1, 1, CV_64FC2, p1data);
Mat p2(1, 1, CV_64FC2, p2data);
Mat np1, np2;
correctMatches(F, p1, p2, np1, np2);
cout << np1 << endl;
cout << np2 << endl;
}
}} // namespace
/* End of file. */
+179 -33
View File
@@ -12,6 +12,7 @@
//
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
// Copyright (C) 2015, Itseez Inc., all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
@@ -41,7 +42,8 @@
//M*/
#include "test_precomp.hpp"
#include <time.h>
namespace opencv_test { namespace {
#define CALIB3D_HOMOGRAPHY_ERROR_MATRIX_SIZE 1
#define CALIB3D_HOMOGRAPHY_ERROR_MATRIX_DIFF 2
@@ -62,10 +64,10 @@
#define MAX_COUNT_OF_POINTS 303
#define COUNT_NORM_TYPES 3
#define METHODS_COUNT 3
#define METHODS_COUNT 4
int NORM_TYPE[COUNT_NORM_TYPES] = {cv::NORM_L1, cv::NORM_L2, cv::NORM_INF};
int METHOD[METHODS_COUNT] = {0, cv::RANSAC, cv::LMEDS};
int METHOD[METHODS_COUNT] = {0, cv::RANSAC, cv::LMEDS, cv::RHO};
using namespace cv;
using namespace std;
@@ -94,12 +96,12 @@ private:
void print_information_1(int j, int N, int method, const Mat& H);
void print_information_2(int j, int N, int method, const Mat& H, const Mat& H_res, int k, double diff);
void print_information_3(int j, int N, const Mat& mask);
void print_information_3(int method, int j, int N, const Mat& mask);
void print_information_4(int method, int j, int N, int k, int l, double diff);
void print_information_5(int method, int j, int N, int l, double diff);
void print_information_6(int j, int N, int k, double diff, bool value);
void print_information_7(int j, int N, int k, double diff, bool original_value, bool found_value);
void print_information_8(int j, int N, int k, int l, double diff);
void print_information_6(int method, int j, int N, int k, double diff, bool value);
void print_information_7(int method, int j, int N, int k, double diff, bool original_value, bool found_value);
void print_information_8(int method, int j, int N, int k, int l, double diff);
};
CV_HomographyTest::CV_HomographyTest() : max_diff(1e-2f), max_2diff(2e-2f)
@@ -144,7 +146,7 @@ void CV_HomographyTest::print_information_1(int j, int N, int _method, const Mat
cout << "Type of srcPoints: "; if ((j>-1) && (j<2)) cout << "Mat of CV_32FC2"; else cout << "vector <Point2f>";
cout << " Type of dstPoints: "; if (j % 2 == 0) cout << "Mat of CV_32FC2"; else cout << "vector <Point2f>"; cout << endl;
cout << "Count of points: " << N << endl; cout << endl;
cout << "Method: "; if (_method == 0) cout << 0; else if (_method == 8) cout << "RANSAC"; else cout << "LMEDS"; cout << endl;
cout << "Method: "; if (_method == 0) cout << 0; else if (_method == 8) cout << "RANSAC"; else if (_method == cv::RHO) cout << "RHO"; else cout << "LMEDS"; cout << endl;
cout << "Homography matrix:" << endl; cout << endl;
cout << H << endl; cout << endl;
cout << "Number of rows: " << H.rows << " Number of cols: " << H.cols << endl; cout << endl;
@@ -156,7 +158,7 @@ void CV_HomographyTest::print_information_2(int j, int N, int _method, const Mat
cout << "Type of srcPoints: "; if ((j>-1) && (j<2)) cout << "Mat of CV_32FC2"; else cout << "vector <Point2f>";
cout << " Type of dstPoints: "; if (j % 2 == 0) cout << "Mat of CV_32FC2"; else cout << "vector <Point2f>"; cout << endl;
cout << "Count of points: " << N << endl; cout << endl;
cout << "Method: "; if (_method == 0) cout << 0; else if (_method == 8) cout << "RANSAC"; else cout << "LMEDS"; cout << endl;
cout << "Method: "; if (_method == 0) cout << 0; else if (_method == 8) cout << "RANSAC"; else if (_method == cv::RHO) cout << "RHO"; else cout << "LMEDS"; cout << endl;
cout << "Original matrix:" << endl; cout << endl;
cout << H << endl; cout << endl;
cout << "Found matrix:" << endl; cout << endl;
@@ -166,13 +168,13 @@ void CV_HomographyTest::print_information_2(int j, int N, int _method, const Mat
cout << "Maximum allowed difference: " << max_diff << endl; cout << endl;
}
void CV_HomographyTest::print_information_3(int j, int N, const Mat& mask)
void CV_HomographyTest::print_information_3(int _method, int j, int N, const Mat& mask)
{
cout << endl; cout << "Checking for inliers/outliers mask..." << endl; cout << endl;
cout << "Type of srcPoints: "; if ((j>-1) && (j<2)) cout << "Mat of CV_32FC2"; else cout << "vector <Point2f>";
cout << " Type of dstPoints: "; if (j % 2 == 0) cout << "Mat of CV_32FC2"; else cout << "vector <Point2f>"; cout << endl;
cout << "Count of points: " << N << endl; cout << endl;
cout << "Method: RANSAC" << endl;
cout << "Method: "; if (_method == RANSAC) cout << "RANSAC" << endl; else if (_method == cv::RHO) cout << "RHO" << endl; else cout << _method << endl;
cout << "Found mask:" << endl; cout << endl;
cout << mask << endl; cout << endl;
cout << "Number of rows: " << mask.rows << " Number of cols: " << mask.cols << endl; cout << endl;
@@ -189,7 +191,7 @@ void CV_HomographyTest::print_information_4(int _method, int j, int N, int k, in
cout << "Number of point: " << k << endl;
cout << "Norm type using in criteria: "; if (NORM_TYPE[l] == 1) cout << "INF"; else if (NORM_TYPE[l] == 2) cout << "L1"; else cout << "L2"; cout << endl;
cout << "Difference with noise of point: " << diff << endl;
cout << "Maxumum allowed difference: " << max_2diff << endl; cout << endl;
cout << "Maximum allowed difference: " << max_2diff << endl; cout << endl;
}
void CV_HomographyTest::print_information_5(int _method, int j, int N, int l, double diff)
@@ -202,13 +204,13 @@ void CV_HomographyTest::print_information_5(int _method, int j, int N, int l, do
cout << "Count of points: " << N << endl;
cout << "Norm type using in criteria: "; if (NORM_TYPE[l] == 1) cout << "INF"; else if (NORM_TYPE[l] == 2) cout << "L1"; else cout << "L2"; cout << endl;
cout << "Difference with noise of points: " << diff << endl;
cout << "Maxumum allowed difference: " << max_diff << endl; cout << endl;
cout << "Maximum allowed difference: " << max_diff << endl; cout << endl;
}
void CV_HomographyTest::print_information_6(int j, int N, int k, double diff, bool value)
void CV_HomographyTest::print_information_6(int _method, int j, int N, int k, double diff, bool value)
{
cout << endl; cout << "Checking for inliers/outliers mask..." << endl; cout << endl;
cout << "Method: RANSAC" << endl;
cout << "Method: "; if (_method == RANSAC) cout << "RANSAC" << endl; else if (_method == cv::RHO) cout << "RHO" << endl; else cout << _method << endl;
cout << "Type of srcPoints: "; if ((j>-1) && (j<2)) cout << "Mat of CV_32FC2"; else cout << "vector <Point2f>";
cout << " Type of dstPoints: "; if (j % 2 == 0) cout << "Mat of CV_32FC2"; else cout << "vector <Point2f>"; cout << endl;
cout << "Count of points: " << N << " " << endl;
@@ -218,10 +220,10 @@ void CV_HomographyTest::print_information_6(int j, int N, int k, double diff, bo
cout << "Value of found mask: "<< value << endl; cout << endl;
}
void CV_HomographyTest::print_information_7(int j, int N, int k, double diff, bool original_value, bool found_value)
void CV_HomographyTest::print_information_7(int _method, int j, int N, int k, double diff, bool original_value, bool found_value)
{
cout << endl; cout << "Checking for inliers/outliers mask..." << endl; cout << endl;
cout << "Method: RANSAC" << endl;
cout << "Method: "; if (_method == RANSAC) cout << "RANSAC" << endl; else if (_method == cv::RHO) cout << "RHO" << endl; else cout << _method << endl;
cout << "Type of srcPoints: "; if ((j>-1) && (j<2)) cout << "Mat of CV_32FC2"; else cout << "vector <Point2f>";
cout << " Type of dstPoints: "; if (j % 2 == 0) cout << "Mat of CV_32FC2"; else cout << "vector <Point2f>"; cout << endl;
cout << "Count of points: " << N << " " << endl;
@@ -231,10 +233,10 @@ void CV_HomographyTest::print_information_7(int j, int N, int k, double diff, bo
cout << "Value of original mask: "<< original_value << " Value of found mask: " << found_value << endl; cout << endl;
}
void CV_HomographyTest::print_information_8(int j, int N, int k, int l, double diff)
void CV_HomographyTest::print_information_8(int _method, int j, int N, int k, int l, double diff)
{
cout << endl; cout << "Checking for reprojection error of inlier..." << endl; cout << endl;
cout << "Method: RANSAC" << endl;
cout << "Method: "; if (_method == RANSAC) cout << "RANSAC" << endl; else if (_method == cv::RHO) cout << "RHO" << endl; else cout << _method << endl;
cout << "Sigma of normal noise: " << sigma << endl;
cout << "Type of srcPoints: "; if ((j>-1) && (j<2)) cout << "Mat of CV_32FC2"; else cout << "vector <Point2f>";
cout << " Type of dstPoints: "; if (j % 2 == 0) cout << "Mat of CV_32FC2"; else cout << "vector <Point2f>"; cout << endl;
@@ -242,7 +244,7 @@ void CV_HomographyTest::print_information_8(int j, int N, int k, int l, double d
cout << "Number of point: " << k << " " << endl;
cout << "Norm type using in criteria: "; if (NORM_TYPE[l] == 1) cout << "INF"; else if (NORM_TYPE[l] == 2) cout << "L1"; else cout << "L2"; cout << endl;
cout << "Difference with noise of point: " << diff << endl;
cout << "Maxumum allowed difference: " << max_2diff << endl; cout << endl;
cout << "Maximum allowed difference: " << max_2diff << endl; cout << endl;
}
void CV_HomographyTest::run(int)
@@ -339,14 +341,15 @@ void CV_HomographyTest::run(int)
continue;
}
case cv::RHO:
case RANSAC:
{
cv::Mat mask [4]; double diff;
Mat H_res_64 [4] = { cv::findHomography(src_mat_2f, dst_mat_2f, RANSAC, reproj_threshold, mask[0]),
cv::findHomography(src_mat_2f, dst_vec, RANSAC, reproj_threshold, mask[1]),
cv::findHomography(src_vec, dst_mat_2f, RANSAC, reproj_threshold, mask[2]),
cv::findHomography(src_vec, dst_vec, RANSAC, reproj_threshold, mask[3]) };
Mat H_res_64 [4] = { cv::findHomography(src_mat_2f, dst_mat_2f, method, reproj_threshold, mask[0]),
cv::findHomography(src_mat_2f, dst_vec, method, reproj_threshold, mask[1]),
cv::findHomography(src_vec, dst_mat_2f, method, reproj_threshold, mask[2]),
cv::findHomography(src_vec, dst_vec, method, reproj_threshold, mask[3]) };
for (int j = 0; j < 4; ++j)
{
@@ -370,7 +373,7 @@ void CV_HomographyTest::run(int)
if (code)
{
print_information_3(j, N, mask[j]);
print_information_3(method, j, N, mask[j]);
switch (code)
{
@@ -466,14 +469,15 @@ void CV_HomographyTest::run(int)
continue;
}
case cv::RHO:
case RANSAC:
{
cv::Mat mask_res [4];
Mat H_res_64 [4] = { cv::findHomography(src_mat_2f, dst_mat_2f, RANSAC, reproj_threshold, mask_res[0]),
cv::findHomography(src_mat_2f, dst_vec, RANSAC, reproj_threshold, mask_res[1]),
cv::findHomography(src_vec, dst_mat_2f, RANSAC, reproj_threshold, mask_res[2]),
cv::findHomography(src_vec, dst_vec, RANSAC, reproj_threshold, mask_res[3]) };
Mat H_res_64 [4] = { cv::findHomography(src_mat_2f, dst_mat_2f, method, reproj_threshold, mask_res[0]),
cv::findHomography(src_mat_2f, dst_vec, method, reproj_threshold, mask_res[1]),
cv::findHomography(src_vec, dst_mat_2f, method, reproj_threshold, mask_res[2]),
cv::findHomography(src_vec, dst_vec, method, reproj_threshold, mask_res[3]) };
for (int j = 0; j < 4; ++j)
{
@@ -488,7 +492,7 @@ void CV_HomographyTest::run(int)
if (code)
{
print_information_3(j, N, mask_res[j]);
print_information_3(method, j, N, mask_res[j]);
switch (code)
{
@@ -520,14 +524,14 @@ void CV_HomographyTest::run(int)
if (mask_res[j].at<bool>(k, 0) != (diff <= reproj_threshold))
{
print_information_6(j, N, k, diff, mask_res[j].at<bool>(k, 0));
print_information_6(method, j, N, k, diff, mask_res[j].at<bool>(k, 0));
CV_Error(CALIB3D_HOMOGRAPHY_ERROR_RANSAC_MASK, MESSAGE_RANSAC_MASK_4);
return;
}
if (mask.at<bool>(k, 0) && !mask_res[j].at<bool>(k, 0))
{
print_information_7(j, N, k, diff, mask.at<bool>(k, 0), mask_res[j].at<bool>(k, 0));
print_information_7(method, j, N, k, diff, mask.at<bool>(k, 0), mask_res[j].at<bool>(k, 0));
CV_Error(CALIB3D_HOMOGRAPHY_ERROR_RANSAC_MASK, MESSAGE_RANSAC_MASK_5);
return;
}
@@ -547,7 +551,7 @@ void CV_HomographyTest::run(int)
if (diff - cv::norm(noise_2d, NORM_TYPE[l]) > max_2diff)
{
print_information_8(j, N, k, l, diff - cv::norm(noise_2d, NORM_TYPE[l]));
print_information_8(method, j, N, k, l, diff - cv::norm(noise_2d, NORM_TYPE[l]));
CV_Error(CALIB3D_HOMOGRAPHY_ERROR_RANSAC_DIFF, MESSAGE_RANSAC_DIFF);
return;
}
@@ -562,7 +566,149 @@ void CV_HomographyTest::run(int)
default: continue;
}
}
delete[]src_data;
src_data = NULL;
}
}
TEST(Calib3d_Homography, accuracy) { CV_HomographyTest test; test.safe_run(); }
TEST(Calib3d_Homography, EKcase)
{
float pt1data[] =
{
2.80073029e+002f, 2.39591217e+002f, 2.21912201e+002f, 2.59783997e+002f,
2.16053192e+002f, 2.78826569e+002f, 2.22782532e+002f, 2.82330383e+002f,
2.09924820e+002f, 2.89122559e+002f, 2.11077698e+002f, 2.89384674e+002f,
2.25287689e+002f, 2.88795532e+002f, 2.11180801e+002f, 2.89653503e+002f,
2.24126404e+002f, 2.90466064e+002f, 2.10914429e+002f, 2.90886963e+002f,
2.23439362e+002f, 2.91657715e+002f, 2.24809387e+002f, 2.91891602e+002f,
2.09809082e+002f, 2.92891113e+002f, 2.08771164e+002f, 2.93093231e+002f,
2.23160095e+002f, 2.93259460e+002f, 2.07874023e+002f, 2.93989990e+002f,
2.08963638e+002f, 2.94209839e+002f, 2.23963165e+002f, 2.94479645e+002f,
2.23241791e+002f, 2.94887817e+002f, 2.09438782e+002f, 2.95233337e+002f,
2.08901886e+002f, 2.95762878e+002f, 2.21867981e+002f, 2.95747711e+002f,
2.24195511e+002f, 2.98270905e+002f, 2.09331345e+002f, 3.05958191e+002f,
2.24727875e+002f, 3.07186035e+002f, 2.26718842e+002f, 3.08095795e+002f,
2.25363953e+002f, 3.08200226e+002f, 2.19897797e+002f, 3.13845093e+002f,
2.25013474e+002f, 3.15558777e+002f
};
float pt2data[] =
{
1.84072723e+002f, 1.43591202e+002f, 1.25912483e+002f, 1.63783859e+002f,
2.06439407e+002f, 2.20573929e+002f, 1.43801437e+002f, 1.80703903e+002f,
9.77904129e+000f, 2.49660202e+002f, 1.38458405e+001f, 2.14502701e+002f,
1.50636337e+002f, 2.15597183e+002f, 6.43103180e+001f, 2.51667648e+002f,
1.54952499e+002f, 2.20780014e+002f, 1.26638412e+002f, 2.43040924e+002f,
3.67568909e+002f, 1.83624954e+001f, 1.60657944e+002f, 2.21794052e+002f,
-1.29507828e+000f, 3.32472443e+002f, 8.51442242e+000f, 4.15561554e+002f,
1.27161377e+002f, 1.97260361e+002f, 5.40714645e+000f, 4.90978302e+002f,
2.25571690e+001f, 3.96912415e+002f, 2.95664978e+002f, 7.36064959e+000f,
1.27241104e+002f, 1.98887573e+002f, -1.25569367e+000f, 3.87713226e+002f,
1.04194012e+001f, 4.31495758e+002f, 1.25868874e+002f, 1.99751617e+002f,
1.28195480e+002f, 2.02270355e+002f, 2.23436356e+002f, 1.80489182e+002f,
1.28727692e+002f, 2.11185410e+002f, 2.03336639e+002f, 2.52182083e+002f,
1.29366486e+002f, 2.12201904e+002f, 1.23897598e+002f, 2.17847351e+002f,
1.29015259e+002f, 2.19560623e+002f
};
int npoints = (int)(sizeof(pt1data)/sizeof(pt1data[0])/2);
Mat p1(1, npoints, CV_32FC2, pt1data);
Mat p2(1, npoints, CV_32FC2, pt2data);
Mat mask;
Mat h = findHomography(p1, p2, RANSAC, 0.01, mask);
ASSERT_TRUE(!h.empty());
cv::transpose(mask, mask);
Mat p3, mask2;
int ninliers = countNonZero(mask);
Mat nmask[] = { mask, mask };
merge(nmask, 2, mask2);
perspectiveTransform(p1, p3, h);
mask2 = mask2.reshape(1);
p2 = p2.reshape(1);
p3 = p3.reshape(1);
double err = cvtest::norm(p2, p3, NORM_INF, mask2);
printf("ninliers: %d, inliers err: %.2g\n", ninliers, err);
ASSERT_GE(ninliers, 10);
ASSERT_LE(err, 0.01);
}
TEST(Calib3d_Homography, fromImages)
{
Mat img_1 = imread(cvtest::TS::ptr()->get_data_path() + "cv/optflow/image1.png", 0);
Mat img_2 = imread(cvtest::TS::ptr()->get_data_path() + "cv/optflow/image2.png", 0);
Ptr<ORB> orb = ORB::create();
vector<KeyPoint> keypoints_1, keypoints_2;
Mat descriptors_1, descriptors_2;
orb->detectAndCompute( img_1, Mat(), keypoints_1, descriptors_1, false );
orb->detectAndCompute( img_2, Mat(), keypoints_2, descriptors_2, false );
//-- Step 3: Matching descriptor vectors using Brute Force matcher
BFMatcher matcher(NORM_HAMMING,false);
std::vector< DMatch > matches;
matcher.match( descriptors_1, descriptors_2, matches );
double max_dist = 0; double min_dist = 100;
//-- Quick calculation of max and min distances between keypoints
for( int i = 0; i < descriptors_1.rows; i++ )
{
double dist = matches[i].distance;
if( dist < min_dist ) min_dist = dist;
if( dist > max_dist ) max_dist = dist;
}
//-- Draw only "good" matches (i.e. whose distance is less than 3*min_dist )
std::vector< DMatch > good_matches;
for( int i = 0; i < descriptors_1.rows; i++ )
{
if( matches[i].distance <= 100 )
good_matches.push_back( matches[i]);
}
//-- Localize the model
std::vector<Point2f> pointframe1;
std::vector<Point2f> pointframe2;
for( int i = 0; i < (int)good_matches.size(); i++ )
{
//-- Get the keypoints from the good matches
pointframe1.push_back( keypoints_1[ good_matches[i].queryIdx ].pt );
pointframe2.push_back( keypoints_2[ good_matches[i].trainIdx ].pt );
}
Mat H0, H1, inliers0, inliers1;
double min_t0 = DBL_MAX, min_t1 = DBL_MAX;
for( int i = 0; i < 10; i++ )
{
double t = (double)getTickCount();
H0 = findHomography( pointframe1, pointframe2, RANSAC, 3.0, inliers0 );
t = (double)getTickCount() - t;
min_t0 = std::min(min_t0, t);
}
int ninliers0 = countNonZero(inliers0);
for( int i = 0; i < 10; i++ )
{
double t = (double)getTickCount();
H1 = findHomography( pointframe1, pointframe2, RHO, 3.0, inliers1 );
t = (double)getTickCount() - t;
min_t1 = std::min(min_t1, t);
}
int ninliers1 = countNonZero(inliers1);
double freq = getTickFrequency();
printf("nfeatures1 = %d, nfeatures2=%d, matches=%d, ninliers(RANSAC)=%d, "
"time(RANSAC)=%.2fmsec, ninliers(RHO)=%d, time(RHO)=%.2fmsec\n",
(int)keypoints_1.size(), (int)keypoints_2.size(),
(int)good_matches.size(), ninliers0, min_t0*1000./freq, ninliers1, min_t1*1000./freq);
ASSERT_TRUE(!H0.empty());
ASSERT_GE(ninliers0, 80);
ASSERT_TRUE(!H1.empty());
ASSERT_GE(ninliers1, 80);
}
}} // namespace
@@ -13,7 +13,7 @@
// License Agreement
// For Open Source Computer Vision Library
//
// Copyright (C) 2014, Samson Yilma¸ (samson_yilma@yahoo.com), all rights reserved.
// Copyright (C) 2014, Samson Yilma (samson_yilma@yahoo.com), all rights reserved.
//
// Third party copyrights are property of their respective owners.
//
@@ -44,11 +44,8 @@
//M*/
#include "test_precomp.hpp"
#include "opencv2/calib3d.hpp"
#include <vector>
using namespace cv;
using namespace std;
namespace opencv_test { namespace {
class CV_HomographyDecompTest: public cvtest::BaseTest {
@@ -118,9 +115,9 @@ private:
riter != rotations.end() && titer != translations.end() && niter != normals.end();
++riter, ++titer, ++niter) {
double rdist = norm(*riter, _R, NORM_INF);
double tdist = norm(*titer, _t, NORM_INF);
double ndist = norm(*niter, _n, NORM_INF);
double rdist = cvtest::norm(*riter, _R, NORM_INF);
double tdist = cvtest::norm(*titer, _t, NORM_INF);
double ndist = cvtest::norm(*niter, _n, NORM_INF);
if ( rdist < max_error
&& tdist < max_error
@@ -136,3 +133,37 @@ private:
};
TEST(Calib3d_DecomposeHomography, regression) { CV_HomographyDecompTest test; test.safe_run(); }
TEST(Calib3d_DecomposeHomography, issue_4978)
{
Matx33d K(
1.0, 0.0, 0.0,
0.0, 1.0, 0.0,
0.0, 0.0, 1.0
);
Matx33d H(
-0.102896, 0.270191, -0.0031153,
0.0406387, 1.19569, -0.0120456,
0.445351, 0.0410889, 1
);
vector<Mat> rotations;
vector<Mat> translations;
vector<Mat> normals;
decomposeHomographyMat(H, K, rotations, translations, normals);
ASSERT_GT(rotations.size(), (size_t)0u);
for (size_t i = 0; i < rotations.size(); i++)
{
// check: det(R) = 1
EXPECT_TRUE(std::fabs(cv::determinant(rotations[i]) - 1.0) < 0.01)
<< "R: det=" << cv::determinant(rotations[0]) << std::endl << rotations[i] << std::endl
<< "T:" << std::endl << translations[i] << std::endl;
}
}
}} // namespace
+3
View File
@@ -1,3 +1,6 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
#include "test_precomp.hpp"
CV_TEST_MAIN("")

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