vendor: OpenCV 5.0.0 snapshot at 40738fb16ceddb5fb3fea747585f7ce6abb0605b

This commit is contained in:
Gitea Mirror Bot
2026-08-22 00:10:33 +08:00
commit f7f077da11
6933 changed files with 2335208 additions and 0 deletions
+13
View File
@@ -0,0 +1,13 @@
Copyright (c) 2013 Spotify AB
Licensed under the Apache License, Version 2.0 (the "License"); you may not
use this file except in compliance with the License. You may obtain a copy of
the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
License for the specific language governing permissions and limitations under
the License.
File diff suppressed because it is too large Load Diff
+264
View File
@@ -0,0 +1,264 @@
// This is from https://code.google.com/p/mman-win32/
//
// Licensed under MIT
#ifndef _MMAN_WIN32_H
#define _MMAN_WIN32_H
#ifndef _WIN32_WINNT // Allow use of features specific to Windows XP or later.
#define _WIN32_WINNT 0x0501 // Change this to the appropriate value to target other versions of Windows.
#endif
#include <sys/types.h>
#include <windows.h>
#include <memoryapi.h>
#include <errno.h>
#include <io.h>
#define PROT_NONE 0
#define PROT_READ 1
#define PROT_WRITE 2
#define PROT_EXEC 4
#define MAP_FILE 0
#define MAP_SHARED 1
#define MAP_PRIVATE 2
#define MAP_TYPE 0xf
#define MAP_FIXED 0x10
#define MAP_ANONYMOUS 0x20
#define MAP_ANON MAP_ANONYMOUS
#define MAP_FAILED ((void *)-1)
/* Flags for msync. */
#define MS_ASYNC 1
#define MS_SYNC 2
#define MS_INVALIDATE 4
#ifndef FILE_MAP_EXECUTE
#define FILE_MAP_EXECUTE 0x0020
#endif
static int __map_mman_error(const DWORD err, const int /*deferr*/)
{
if (err == 0)
return 0;
//TODO: implement
return err;
}
static DWORD __map_mmap_prot_page(const int prot)
{
DWORD protect = 0;
if (prot == PROT_NONE)
return protect;
if ((prot & PROT_EXEC) != 0)
{
protect = ((prot & PROT_WRITE) != 0) ?
PAGE_EXECUTE_READWRITE : PAGE_EXECUTE_READ;
}
else
{
protect = ((prot & PROT_WRITE) != 0) ?
PAGE_READWRITE : PAGE_READONLY;
}
return protect;
}
static DWORD __map_mmap_prot_file(const int prot)
{
DWORD desiredAccess = 0;
if (prot == PROT_NONE)
return desiredAccess;
if ((prot & PROT_READ) != 0)
desiredAccess |= FILE_MAP_READ;
if ((prot & PROT_WRITE) != 0)
desiredAccess |= FILE_MAP_WRITE;
if ((prot & PROT_EXEC) != 0)
desiredAccess |= FILE_MAP_EXECUTE;
return desiredAccess;
}
inline void* mmap(void * /*addr*/, size_t len, int prot, int flags, int fildes, off_t off)
{
HANDLE fm, h;
void * map = MAP_FAILED;
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable: 4293)
#endif
const DWORD dwFileOffsetLow = (sizeof(off_t) <= sizeof(DWORD)) ?
(DWORD)off : (DWORD)(off & 0xFFFFFFFFL);
const DWORD dwFileOffsetHigh = (sizeof(off_t) <= sizeof(DWORD)) ?
(DWORD)0 : (DWORD)((off >> 32) & 0xFFFFFFFFL);
const DWORD protect = __map_mmap_prot_page(prot);
const DWORD desiredAccess = __map_mmap_prot_file(prot);
const off_t maxSize = off + (off_t)len;
const DWORD dwMaxSizeLow = (sizeof(off_t) <= sizeof(DWORD)) ?
(DWORD)maxSize : (DWORD)(maxSize & 0xFFFFFFFFL);
const DWORD dwMaxSizeHigh = (sizeof(off_t) <= sizeof(DWORD)) ?
(DWORD)0 : (DWORD)((maxSize >> 32) & 0xFFFFFFFFL);
#ifdef _MSC_VER
#pragma warning(pop)
#endif
errno = 0;
if (len == 0
/* Unsupported flag combinations */
|| (flags & MAP_FIXED) != 0
/* Usupported protection combinations */
|| prot == PROT_EXEC)
{
errno = EINVAL;
return MAP_FAILED;
}
h = ((flags & MAP_ANONYMOUS) == 0) ?
(HANDLE)_get_osfhandle(fildes) : INVALID_HANDLE_VALUE;
if ((flags & MAP_ANONYMOUS) == 0 && h == INVALID_HANDLE_VALUE)
{
errno = EBADF;
return MAP_FAILED;
}
fm = CreateFileMapping(h, NULL, protect, dwMaxSizeHigh, dwMaxSizeLow, NULL);
if (fm == NULL)
{
errno = __map_mman_error(GetLastError(), EPERM);
return MAP_FAILED;
}
map = MapViewOfFile(fm, desiredAccess, dwFileOffsetHigh, dwFileOffsetLow, len);
CloseHandle(fm);
if (map == NULL)
{
errno = __map_mman_error(GetLastError(), EPERM);
return MAP_FAILED;
}
return map;
}
inline int munmap(void *addr, size_t /*len*/)
{
if (UnmapViewOfFile(addr))
return 0;
errno = __map_mman_error(GetLastError(), EPERM);
return -1;
}
inline int mprotect(void *addr, size_t len, int prot)
{
DWORD newProtect = __map_mmap_prot_page(prot);
DWORD oldProtect = 0;
if (VirtualProtect(addr, len, newProtect, &oldProtect))
return 0;
errno = __map_mman_error(GetLastError(), EPERM);
return -1;
}
inline int msync(void *addr, size_t len, int /*flags*/)
{
if (FlushViewOfFile(addr, len))
return 0;
errno = __map_mman_error(GetLastError(), EPERM);
return -1;
}
#pragma region Desktop Family or OneCore Family
#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM)
WINBASEAPI
BOOL
WINAPI
VirtualLock(
_In_ LPVOID lpAddress,
_In_ SIZE_T dwSize
);
WINBASEAPI
BOOL
WINAPI
VirtualUnlock(
_In_ LPVOID lpAddress,
_In_ SIZE_T dwSize
);
#endif // WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM)
inline int mlock(const void *addr, size_t len)
{
if (VirtualLock((LPVOID)addr, len))
return 0;
errno = __map_mman_error(GetLastError(), EPERM);
return -1;
}
inline int munlock(const void *addr, size_t len)
{
if (VirtualUnlock((LPVOID)addr, len))
return 0;
errno = __map_mman_error(GetLastError(), EPERM);
return -1;
}
#if !defined(__MINGW32__)
inline int ftruncate(const int fd, const int64_t size) {
if (fd < 0) {
errno = EBADF;
return -1;
}
HANDLE h = reinterpret_cast<HANDLE>(_get_osfhandle(fd));
LARGE_INTEGER li_start, li_size;
li_start.QuadPart = static_cast<int64_t>(0);
li_size.QuadPart = size;
if (SetFilePointerEx(h, li_start, NULL, FILE_CURRENT) == ~0 ||
SetFilePointerEx(h, li_size, NULL, FILE_BEGIN) == ~0 ||
!SetEndOfFile(h)) {
unsigned long error = GetLastError();
fprintf(stderr, "I/O error while truncating: %lu\n", error);
switch (error) {
case ERROR_INVALID_HANDLE:
errno = EBADF;
break;
default:
errno = EIO;
break;
}
return -1;
}
return 0;
}
#endif
#endif
+135
View File
@@ -0,0 +1,135 @@
/*
**
** License Agreement
** For chi_table.h
**
** Copyright (C) 2007 Per-Erik Forssen, 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.
**
** Content origin: http://users.isy.liu.se/cvl/perfo/software/chi_table.h
*/
#define TABLE_SIZE 400
static double chitab3[]={0, 0.0150057, 0.0239478, 0.0315227,
0.0383427, 0.0446605, 0.0506115, 0.0562786,
0.0617174, 0.0669672, 0.0720573, 0.0770099,
0.081843, 0.0865705, 0.0912043, 0.0957541,
0.100228, 0.104633, 0.108976, 0.113261,
0.117493, 0.121676, 0.125814, 0.12991,
0.133967, 0.137987, 0.141974, 0.145929,
0.149853, 0.15375, 0.15762, 0.161466,
0.165287, 0.169087, 0.172866, 0.176625,
0.180365, 0.184088, 0.187794, 0.191483,
0.195158, 0.198819, 0.202466, 0.2061,
0.209722, 0.213332, 0.216932, 0.220521,
0.2241, 0.22767, 0.231231, 0.234783,
0.238328, 0.241865, 0.245395, 0.248918,
0.252435, 0.255947, 0.259452, 0.262952,
0.266448, 0.269939, 0.273425, 0.276908,
0.280386, 0.283862, 0.287334, 0.290803,
0.29427, 0.297734, 0.301197, 0.304657,
0.308115, 0.311573, 0.315028, 0.318483,
0.321937, 0.32539, 0.328843, 0.332296,
0.335749, 0.339201, 0.342654, 0.346108,
0.349562, 0.353017, 0.356473, 0.35993,
0.363389, 0.366849, 0.37031, 0.373774,
0.377239, 0.380706, 0.384176, 0.387648,
0.391123, 0.3946, 0.39808, 0.401563,
0.405049, 0.408539, 0.412032, 0.415528,
0.419028, 0.422531, 0.426039, 0.429551,
0.433066, 0.436586, 0.440111, 0.44364,
0.447173, 0.450712, 0.454255, 0.457803,
0.461356, 0.464915, 0.468479, 0.472049,
0.475624, 0.479205, 0.482792, 0.486384,
0.489983, 0.493588, 0.4972, 0.500818,
0.504442, 0.508073, 0.511711, 0.515356,
0.519008, 0.522667, 0.526334, 0.530008,
0.533689, 0.537378, 0.541075, 0.54478,
0.548492, 0.552213, 0.555942, 0.55968,
0.563425, 0.56718, 0.570943, 0.574715,
0.578497, 0.582287, 0.586086, 0.589895,
0.593713, 0.597541, 0.601379, 0.605227,
0.609084, 0.612952, 0.61683, 0.620718,
0.624617, 0.628526, 0.632447, 0.636378,
0.64032, 0.644274, 0.648239, 0.652215,
0.656203, 0.660203, 0.664215, 0.668238,
0.672274, 0.676323, 0.680384, 0.684457,
0.688543, 0.692643, 0.696755, 0.700881,
0.70502, 0.709172, 0.713339, 0.717519,
0.721714, 0.725922, 0.730145, 0.734383,
0.738636, 0.742903, 0.747185, 0.751483,
0.755796, 0.760125, 0.76447, 0.768831,
0.773208, 0.777601, 0.782011, 0.786438,
0.790882, 0.795343, 0.799821, 0.804318,
0.808831, 0.813363, 0.817913, 0.822482,
0.827069, 0.831676, 0.836301, 0.840946,
0.84561, 0.850295, 0.854999, 0.859724,
0.864469, 0.869235, 0.874022, 0.878831,
0.883661, 0.888513, 0.893387, 0.898284,
0.903204, 0.908146, 0.913112, 0.918101,
0.923114, 0.928152, 0.933214, 0.938301,
0.943413, 0.94855, 0.953713, 0.958903,
0.964119, 0.969361, 0.974631, 0.979929,
0.985254, 0.990608, 0.99599, 1.0014,
1.00684, 1.01231, 1.01781, 1.02335,
1.02891, 1.0345, 1.04013, 1.04579,
1.05148, 1.05721, 1.06296, 1.06876,
1.07459, 1.08045, 1.08635, 1.09228,
1.09826, 1.10427, 1.11032, 1.1164,
1.12253, 1.1287, 1.1349, 1.14115,
1.14744, 1.15377, 1.16015, 1.16656,
1.17303, 1.17954, 1.18609, 1.19269,
1.19934, 1.20603, 1.21278, 1.21958,
1.22642, 1.23332, 1.24027, 1.24727,
1.25433, 1.26144, 1.26861, 1.27584,
1.28312, 1.29047, 1.29787, 1.30534,
1.31287, 1.32046, 1.32812, 1.33585,
1.34364, 1.3515, 1.35943, 1.36744,
1.37551, 1.38367, 1.39189, 1.4002,
1.40859, 1.41705, 1.42561, 1.43424,
1.44296, 1.45177, 1.46068, 1.46967,
1.47876, 1.48795, 1.49723, 1.50662,
1.51611, 1.52571, 1.53541, 1.54523,
1.55517, 1.56522, 1.57539, 1.58568,
1.59611, 1.60666, 1.61735, 1.62817,
1.63914, 1.65025, 1.66152, 1.67293,
1.68451, 1.69625, 1.70815, 1.72023,
1.73249, 1.74494, 1.75757, 1.77041,
1.78344, 1.79669, 1.81016, 1.82385,
1.83777, 1.85194, 1.86635, 1.88103,
1.89598, 1.91121, 1.92674, 1.94257,
1.95871, 1.97519, 1.99201, 2.0092,
2.02676, 2.04471, 2.06309, 2.08189,
2.10115, 2.12089, 2.14114, 2.16192,
2.18326, 2.2052, 2.22777, 2.25101,
2.27496, 2.29966, 2.32518, 2.35156,
2.37886, 2.40717, 2.43655, 2.46709,
2.49889, 2.53206, 2.56673, 2.60305,
2.64117, 2.6813, 2.72367, 2.76854,
2.81623, 2.86714, 2.92173, 2.98059,
3.04446, 3.1143, 3.19135, 3.27731,
3.37455, 3.48653, 3.61862, 3.77982,
3.98692, 4.2776, 4.77167, 133.333 };
+28
View File
@@ -0,0 +1,28 @@
License Agreement
For chi_table.h
Copyright (C) 2007 Per-Erik Forssen, 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.
+12
View File
@@ -0,0 +1,12 @@
set(the_description "Features Framework")
ocv_add_dispatched_file(sift SSE4_1 AVX2 AVX512_SKX)
set(debug_modules "")
if(DEBUG_opencv_features)
list(APPEND debug_modules opencv_highgui)
endif()
ocv_define_module(features opencv_imgproc opencv_geometry ${debug_modules} OPTIONAL opencv_flann opencv_dnn WRAP java objc python js)
ocv_install_3rdparty_licenses(mscr "${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/mscr/chi_table_LICENSE.txt")
ocv_install_3rdparty_licenses(annoylib "${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/annoy/LICENSE")
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,48 @@
/*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.
// 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*/
#ifdef __OPENCV_BUILD
#error this is a compatibility header which should not be used inside the OpenCV library
#endif
#include "opencv2/features.hpp"
@@ -0,0 +1,33 @@
#ifndef OPENCV_FEATURE2D_HAL_INTERFACE_H
#define OPENCV_FEATURE2D_HAL_INTERFACE_H
#include "opencv2/core/cvdef.h"
//! @addtogroup features_hal_interface
//! @{
//! @name Fast feature detector types
//! @sa cv::FastFeatureDetector
//! @{
#define CV_HAL_TYPE_5_8 0
#define CV_HAL_TYPE_7_12 1
#define CV_HAL_TYPE_9_16 2
//! @}
//! @name Key point
//! @sa cv::KeyPoint
//! @{
struct CV_EXPORTS cvhalKeyPoint
{
float x;
float y;
float size;
float angle;
float response;
int octave;
int class_id;
};
//! @}
//! @}
#endif
@@ -0,0 +1,6 @@
#ifdef __OPENCV_BUILD
#error this is a compatibility header which should not be used inside the OpenCV library
#endif
#include "opencv2/features.hpp"
+1
View File
@@ -0,0 +1 @@
include/opencv2/features.hpp
@@ -0,0 +1 @@
misc/java/src/cpp/features_converters.hpp
+21
View File
@@ -0,0 +1,21 @@
{
"type_dict" : {
"Feature2D": {
"j_type": "Feature2D",
"jn_type": "long",
"jni_type": "jlong",
"jni_var": "Feature2D %(n)s",
"suffix": "J",
"j_import": "org.opencv.features.Feature2D"
},
"uchar": {
"j_type": "byte",
"jn_type": "byte",
"jni_type": "jbyte",
"suffix": "B"
}
},
"func_arg_fix" : {
"goodFeaturesToTrack" : { "corners" : {"ctype" : "vector_Point"} }
}
}
@@ -0,0 +1,112 @@
#define LOG_TAG "org.opencv.utils.Converters"
#include "common.h"
#include "features_converters.hpp"
using namespace cv;
#define CHECK_MAT(cond) if(!(cond)){ LOGD("FAILED: " #cond); return; }
//vector_KeyPoint
void Mat_to_vector_KeyPoint(Mat& mat, std::vector<KeyPoint>& v_kp)
{
v_kp.clear();
CHECK_MAT(mat.type()==CV_32FC(7) && mat.cols==1);
for(int i=0; i<mat.rows; i++)
{
Vec<float, 7> v = mat.at< Vec<float, 7> >(i, 0);
KeyPoint kp(v[0], v[1], v[2], v[3], v[4], (int)v[5], (int)v[6]);
v_kp.push_back(kp);
}
return;
}
void vector_KeyPoint_to_Mat(std::vector<KeyPoint>& v_kp, Mat& mat)
{
int count = (int)v_kp.size();
mat.create(count, 1, CV_32FC(7));
for(int i=0; i<count; i++)
{
KeyPoint kp = v_kp[i];
mat.at< Vec<float, 7> >(i, 0) = Vec<float, 7>(kp.pt.x, kp.pt.y, kp.size, kp.angle, kp.response, (float)kp.octave, (float)kp.class_id);
}
}
//vector_DMatch
void Mat_to_vector_DMatch(Mat& mat, std::vector<DMatch>& v_dm)
{
v_dm.clear();
CHECK_MAT(mat.type()==CV_32FC4 && mat.cols==1);
for(int i=0; i<mat.rows; i++)
{
Vec<float, 4> v = mat.at< Vec<float, 4> >(i, 0);
DMatch dm((int)v[0], (int)v[1], (int)v[2], v[3]);
v_dm.push_back(dm);
}
return;
}
void vector_DMatch_to_Mat(std::vector<DMatch>& v_dm, Mat& mat)
{
int count = (int)v_dm.size();
mat.create(count, 1, CV_32FC4);
for(int i=0; i<count; i++)
{
DMatch dm = v_dm[i];
mat.at< Vec<float, 4> >(i, 0) = Vec<float, 4>((float)dm.queryIdx, (float)dm.trainIdx, (float)dm.imgIdx, dm.distance);
}
}
void Mat_to_vector_vector_KeyPoint(Mat& mat, std::vector< std::vector< KeyPoint > >& vv_kp)
{
std::vector<Mat> vm;
vm.reserve( mat.rows );
Mat_to_vector_Mat(mat, vm);
for(size_t i=0; i<vm.size(); i++)
{
std::vector<KeyPoint> vkp;
Mat_to_vector_KeyPoint(vm[i], vkp);
vv_kp.push_back(vkp);
}
}
void vector_vector_KeyPoint_to_Mat(std::vector< std::vector< KeyPoint > >& vv_kp, Mat& mat)
{
std::vector<Mat> vm;
vm.reserve( vv_kp.size() );
for(size_t i=0; i<vv_kp.size(); i++)
{
Mat m;
vector_KeyPoint_to_Mat(vv_kp[i], m);
vm.push_back(m);
}
vector_Mat_to_Mat(vm, mat);
}
void Mat_to_vector_vector_DMatch(Mat& mat, std::vector< std::vector< DMatch > >& vv_dm)
{
std::vector<Mat> vm;
vm.reserve( mat.rows );
Mat_to_vector_Mat(mat, vm);
for(size_t i=0; i<vm.size(); i++)
{
std::vector<DMatch> vdm;
Mat_to_vector_DMatch(vm[i], vdm);
vv_dm.push_back(vdm);
}
}
void vector_vector_DMatch_to_Mat(std::vector< std::vector< DMatch > >& vv_dm, Mat& mat)
{
std::vector<Mat> vm;
vm.reserve( vv_dm.size() );
for(size_t i=0; i<vv_dm.size(); i++)
{
Mat m;
vector_DMatch_to_Mat(vv_dm[i], m);
vm.push_back(m);
}
vector_Mat_to_Mat(vm, mat);
}
@@ -0,0 +1,21 @@
#ifndef __FEATURES_CONVERTERS_HPP__
#define __FEATURES_CONVERTERS_HPP__
#include "opencv2/opencv_modules.hpp"
#include "opencv2/core.hpp"
#include "opencv2/features.hpp"
void Mat_to_vector_KeyPoint(cv::Mat& mat, std::vector<cv::KeyPoint>& v_kp);
void vector_KeyPoint_to_Mat(std::vector<cv::KeyPoint>& v_kp, cv::Mat& mat);
void Mat_to_vector_DMatch(cv::Mat& mat, std::vector<cv::DMatch>& v_dm);
void vector_DMatch_to_Mat(std::vector<cv::DMatch>& v_dm, cv::Mat& mat);
void Mat_to_vector_vector_KeyPoint(cv::Mat& mat, std::vector< std::vector< cv::KeyPoint > >& vv_kp);
void vector_vector_KeyPoint_to_Mat(std::vector< std::vector< cv::KeyPoint > >& vv_kp, cv::Mat& mat);
void Mat_to_vector_vector_DMatch(cv::Mat& mat, std::vector< std::vector< cv::DMatch > >& vv_dm);
void vector_vector_DMatch_to_Mat(std::vector< std::vector< cv::DMatch > >& vv_dm, cv::Mat& mat);
#endif
@@ -0,0 +1,304 @@
package org.opencv.test.features;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.MatOfDMatch;
import org.opencv.core.MatOfKeyPoint;
import org.opencv.core.Point;
import org.opencv.core.Scalar;
import org.opencv.core.DMatch;
import org.opencv.features.DescriptorMatcher;
import org.opencv.features.BFMatcher;
import org.opencv.core.KeyPoint;
import org.opencv.test.OpenCVTestCase;
import org.opencv.test.OpenCVTestRunner;
import org.opencv.imgproc.Imgproc;
import org.opencv.features.Feature2D;
public class BruteForceDescriptorMatcherTest extends OpenCVTestCase {
DescriptorMatcher matcher;
int matSize;
DMatch[] truth;
private Mat getMaskImg() {
return new Mat(5, 2, CvType.CV_8U, new Scalar(0)) {
{
put(0, 0, 1, 1, 1, 1);
}
};
}
private Mat getQueryDescriptors() {
Mat img = getQueryImg();
MatOfKeyPoint keypoints = new MatOfKeyPoint();
Mat descriptors = new Mat();
Feature2D detector = createClassInstance(XFEATURES2D+"SURF", DEFAULT_FACTORY, null, null);
Feature2D extractor = createClassInstance(XFEATURES2D+"SURF", DEFAULT_FACTORY, null, null);
setProperty(detector, "hessianThreshold", "double", 8000);
setProperty(detector, "nOctaves", "int", 3);
setProperty(detector, "nOctaveLayers", "int", 4);
setProperty(detector, "upright", "boolean", false);
detector.detect(img, keypoints);
extractor.compute(img, keypoints, descriptors);
return descriptors;
}
private Mat getQueryImg() {
Mat cross = new Mat(matSize, matSize, CvType.CV_8U, new Scalar(255));
Imgproc.line(cross, new Point(30, matSize / 2), new Point(matSize - 31, matSize / 2), new Scalar(100), 3);
Imgproc.line(cross, new Point(matSize / 2, 30), new Point(matSize / 2, matSize - 31), new Scalar(100), 3);
return cross;
}
private Mat getTrainDescriptors() {
Mat img = getTrainImg();
MatOfKeyPoint keypoints = new MatOfKeyPoint(new KeyPoint(50, 50, 16, 0, 20000, 1, -1), new KeyPoint(42, 42, 16, 160, 10000, 1, -1));
Mat descriptors = new Mat();
Feature2D extractor = createClassInstance(XFEATURES2D+"SURF", DEFAULT_FACTORY, null, null);
extractor.compute(img, keypoints, descriptors);
return descriptors;
}
private Mat getTrainImg() {
Mat cross = new Mat(matSize, matSize, CvType.CV_8U, new Scalar(255));
Imgproc.line(cross, new Point(20, matSize / 2), new Point(matSize - 21, matSize / 2), new Scalar(100), 2);
Imgproc.line(cross, new Point(matSize / 2, 20), new Point(matSize / 2, matSize - 21), new Scalar(100), 2);
return cross;
}
protected void setUp() throws Exception {
super.setUp();
matcher = DescriptorMatcher.create(DescriptorMatcher.BRUTEFORCE);
matSize = 100;
truth = new DMatch[] {
new DMatch(0, 0, 0, 0.6159003f),
new DMatch(1, 1, 0, 0.9177120f),
new DMatch(2, 1, 0, 0.3112163f),
new DMatch(3, 1, 0, 0.2925074f),
new DMatch(4, 1, 0, 0.26520672f)
};
}
// https://github.com/opencv/opencv/issues/11268
public void testConstructor()
{
BFMatcher self_created_matcher = new BFMatcher();
Mat train = new Mat(1, 1, CvType.CV_8U, new Scalar(123));
self_created_matcher.add(Arrays.asList(train));
assertTrue(!self_created_matcher.empty());
}
public void testAdd() {
matcher.add(Arrays.asList(new Mat()));
assertFalse(matcher.empty());
}
public void testClear() {
matcher.add(Arrays.asList(new Mat()));
matcher.clear();
assertTrue(matcher.empty());
}
public void testClone() {
Mat train = new Mat(1, 1, CvType.CV_8U, new Scalar(123));
Mat truth = train.clone();
matcher.add(Arrays.asList(train));
DescriptorMatcher cloned = matcher.clone();
assertNotNull(cloned);
List<Mat> descriptors = cloned.getTrainDescriptors();
assertEquals(1, descriptors.size());
assertMatEqual(truth, descriptors.get(0));
}
public void testCloneBoolean() {
matcher.add(Arrays.asList(new Mat()));
DescriptorMatcher cloned = matcher.clone(true);
assertNotNull(cloned);
assertTrue(cloned.empty());
}
public void testCreate() {
assertNotNull(matcher);
}
public void testEmpty() {
assertTrue(matcher.empty());
}
public void testGetTrainDescriptors() {
Mat train = new Mat(1, 1, CvType.CV_8U, new Scalar(123));
Mat truth = train.clone();
matcher.add(Arrays.asList(train));
List<Mat> descriptors = matcher.getTrainDescriptors();
assertEquals(1, descriptors.size());
assertMatEqual(truth, descriptors.get(0));
}
public void testIsMaskSupported() {
assertTrue(matcher.isMaskSupported());
}
public void testKnnMatchMatListOfListOfDMatchInt() {
fail("Not yet implemented");
}
public void testKnnMatchMatListOfListOfDMatchIntListOfMat() {
fail("Not yet implemented");
}
public void testKnnMatchMatListOfListOfDMatchIntListOfMatBoolean() {
fail("Not yet implemented");
}
public void testKnnMatchMatMatListOfListOfDMatchInt() {
final int k = 3;
Mat train = getTrainDescriptors();
Mat query = getQueryDescriptors();
List<MatOfDMatch> matches = new ArrayList<MatOfDMatch>();
matcher.knnMatch(query, train, matches, k);
/*
Log.d("knnMatch", "train = " + train);
Log.d("knnMatch", "query = " + query);
matcher.add(train);
matcher.knnMatch(query, matches, k);
*/
assertEquals(query.rows(), matches.size());
for(int i = 0; i<matches.size(); i++)
{
MatOfDMatch vdm = matches.get(i);
//Log.d("knn", "vdm["+i+"]="+vdm.dump());
assertTrue(Math.min(k, train.rows()) >= vdm.total());
for(DMatch dm : vdm.toArray())
{
assertEquals(dm.queryIdx, i);
}
}
}
public void testKnnMatchMatMatListOfListOfDMatchIntMat() {
fail("Not yet implemented");
}
public void testKnnMatchMatMatListOfListOfDMatchIntMatBoolean() {
fail("Not yet implemented");
}
public void testMatchMatListOfDMatch() {
Mat train = getTrainDescriptors();
Mat query = getQueryDescriptors();
MatOfDMatch matches = new MatOfDMatch();
matcher.add(Arrays.asList(train));
matcher.match(query, matches);
assertArrayDMatchEquals(truth, matches.toArray(), EPS);
}
public void testMatchMatListOfDMatchListOfMat() {
Mat train = getTrainDescriptors();
Mat query = getQueryDescriptors();
Mat mask = getMaskImg();
MatOfDMatch matches = new MatOfDMatch();
matcher.add(Arrays.asList(train));
matcher.match(query, matches, Arrays.asList(mask));
assertListDMatchEquals(Arrays.asList(truth[0], truth[1]), matches.toList(), EPS);
}
public void testMatchMatMatListOfDMatch() {
Mat train = getTrainDescriptors();
Mat query = getQueryDescriptors();
MatOfDMatch matches = new MatOfDMatch();
matcher.match(query, train, matches);
assertArrayDMatchEquals(truth, matches.toArray(), EPS);
// OpenCVTestRunner.Log("matches found: " + matches.size());
// for (DMatch m : matches)
// OpenCVTestRunner.Log(m.toString());
}
public void testMatchMatMatListOfDMatchMat() {
Mat train = getTrainDescriptors();
Mat query = getQueryDescriptors();
Mat mask = getMaskImg();
MatOfDMatch matches = new MatOfDMatch();
matcher.match(query, train, matches, mask);
assertListDMatchEquals(Arrays.asList(truth[0], truth[1]), matches.toList(), EPS);
}
public void testRadiusMatchMatListOfListOfDMatchFloat() {
fail("Not yet implemented");
}
public void testRadiusMatchMatListOfListOfDMatchFloatListOfMat() {
fail("Not yet implemented");
}
public void testRadiusMatchMatListOfListOfDMatchFloatListOfMatBoolean() {
fail("Not yet implemented");
}
public void testRadiusMatchMatMatListOfListOfDMatchFloat() {
fail("Not yet implemented");
}
public void testRadiusMatchMatMatListOfListOfDMatchFloatMat() {
fail("Not yet implemented");
}
public void testRadiusMatchMatMatListOfListOfDMatchFloatMatBoolean() {
fail("Not yet implemented");
}
public void testRead() {
String filename = OpenCVTestRunner.getTempFileName("yml");
writeFile(filename, "%YAML:1.0\n---\n");
matcher.read(filename);
assertTrue(true);// BruteforceMatcher has no settings
}
public void testTrain() {
matcher.train();// BruteforceMatcher does not need to train
}
public void testWrite() {
String filename = OpenCVTestRunner.getTempFileName("yml");
matcher.write(filename);
String truth = "%YAML 1.2\n---\n";
assertEquals(truth, readFile(filename));
}
}
@@ -0,0 +1,262 @@
package org.opencv.test.features;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.MatOfDMatch;
import org.opencv.core.MatOfKeyPoint;
import org.opencv.core.Point;
import org.opencv.core.Scalar;
import org.opencv.core.DMatch;
import org.opencv.features.DescriptorMatcher;
import org.opencv.features.FastFeatureDetector;
import org.opencv.test.OpenCVTestCase;
import org.opencv.test.OpenCVTestRunner;
import org.opencv.imgproc.Imgproc;
import org.opencv.features.Feature2D;
public class BruteForceHammingDescriptorMatcherTest extends OpenCVTestCase {
DescriptorMatcher matcher;
int matSize;
DMatch[] truth;
private Mat getMaskImg() {
return new Mat(4, 4, CvType.CV_8U, new Scalar(0)) {
{
put(0, 0, 1, 1, 1, 1, 1, 1, 1, 1);
}
};
}
private Mat getQueryDescriptors() {
return getTestDescriptors(getQueryImg());
}
private Mat getQueryImg() {
Mat img = new Mat(matSize, matSize, CvType.CV_8U, new Scalar(255));
Imgproc.line(img, new Point(40, matSize - 40), new Point(matSize - 50, 50), new Scalar(0), 8);
return img;
}
private Mat getTestDescriptors(Mat img) {
MatOfKeyPoint keypoints = new MatOfKeyPoint();
Mat descriptors = new Mat();
Feature2D detector = FastFeatureDetector.create();
Feature2D extractor = createClassInstance(XFEATURES2D+"BriefDescriptorExtractor", DEFAULT_FACTORY, null, null);
detector.detect(img, keypoints);
extractor.compute(img, keypoints, descriptors);
return descriptors;
}
private Mat getTrainDescriptors() {
return getTestDescriptors(getTrainImg());
}
private Mat getTrainImg() {
Mat img = new Mat(matSize, matSize, CvType.CV_8U, new Scalar(255));
Imgproc.line(img, new Point(40, 40), new Point(matSize - 40, matSize - 40), new Scalar(0), 8);
return img;
}
protected void setUp() throws Exception {
super.setUp();
matcher = DescriptorMatcher.create(DescriptorMatcher.BRUTEFORCE_HAMMING);
matSize = 100;
truth = new DMatch[] {
new DMatch(0, 0, 0, 51),
new DMatch(1, 2, 0, 42),
new DMatch(2, 1, 0, 40),
new DMatch(3, 3, 0, 53) };
}
public void testAdd() {
matcher.add(Arrays.asList(new Mat()));
assertFalse(matcher.empty());
}
public void testClear() {
matcher.add(Arrays.asList(new Mat()));
matcher.clear();
assertTrue(matcher.empty());
}
public void testClone() {
Mat train = new Mat(1, 1, CvType.CV_8U, new Scalar(123));
Mat truth = train.clone();
matcher.add(Arrays.asList(train));
DescriptorMatcher cloned = matcher.clone();
assertNotNull(cloned);
List<Mat> descriptors = cloned.getTrainDescriptors();
assertEquals(1, descriptors.size());
assertMatEqual(truth, descriptors.get(0));
}
public void testCloneBoolean() {
matcher.add(Arrays.asList(new Mat()));
DescriptorMatcher cloned = matcher.clone(true);
assertNotNull(cloned);
assertTrue(cloned.empty());
}
public void testCreate() {
assertNotNull(matcher);
}
public void testEmpty() {
assertTrue(matcher.empty());
}
public void testGetTrainDescriptors() {
Mat train = new Mat(1, 1, CvType.CV_8U, new Scalar(123));
Mat truth = train.clone();
matcher.add(Arrays.asList(train));
List<Mat> descriptors = matcher.getTrainDescriptors();
assertEquals(1, descriptors.size());
assertMatEqual(truth, descriptors.get(0));
}
public void testIsMaskSupported() {
assertTrue(matcher.isMaskSupported());
}
public void testKnnMatchMatListOfListOfDMatchInt() {
fail("Not yet implemented");
}
public void testKnnMatchMatListOfListOfDMatchIntListOfMat() {
fail("Not yet implemented");
}
public void testKnnMatchMatListOfListOfDMatchIntListOfMatBoolean() {
fail("Not yet implemented");
}
public void testKnnMatchMatMatListOfListOfDMatchInt() {
fail("Not yet implemented");
}
public void testKnnMatchMatMatListOfListOfDMatchIntMat() {
fail("Not yet implemented");
}
public void testKnnMatchMatMatListOfListOfDMatchIntMatBoolean() {
fail("Not yet implemented");
}
public void testMatchMatListOfDMatch() {
Mat train = getTrainDescriptors();
Mat query = getQueryDescriptors();
MatOfDMatch matches = new MatOfDMatch();
matcher.add(Arrays.asList(train));
matcher.match(query, matches);
assertListDMatchEquals(Arrays.asList(truth), matches.toList(), EPS);
}
public void testMatchMatListOfDMatchListOfMat() {
Mat train = getTrainDescriptors();
Mat query = getQueryDescriptors();
Mat mask = getMaskImg();
MatOfDMatch matches = new MatOfDMatch();
matcher.add(Arrays.asList(train));
matcher.match(query, matches, Arrays.asList(mask));
assertListDMatchEquals(Arrays.asList(truth[0], truth[1]), matches.toList(), EPS);
}
public void testMatchMatMatListOfDMatch() {
Mat train = getTrainDescriptors();
Mat query = getQueryDescriptors();
MatOfDMatch matches = new MatOfDMatch();
matcher.match(query, train, matches);
assertListDMatchEquals(Arrays.asList(truth), matches.toList(), EPS);
}
public void testMatchMatMatListOfDMatchMat() {
Mat train = getTrainDescriptors();
Mat query = getQueryDescriptors();
Mat mask = getMaskImg();
MatOfDMatch matches = new MatOfDMatch();
matcher.match(query, train, matches, mask);
assertListDMatchEquals(Arrays.asList(truth[0], truth[1]), matches.toList(), EPS);
}
public void testRadiusMatchMatListOfListOfDMatchFloat() {
Mat train = getTrainDescriptors();
Mat query = getQueryDescriptors();
ArrayList<MatOfDMatch> matches = new ArrayList<MatOfDMatch>();
matcher.radiusMatch(query, train, matches, 50.f);
assertEquals(4, matches.size());
assertTrue(matches.get(0).empty());
assertMatEqual(matches.get(1), new MatOfDMatch(truth[1]), EPS);
assertMatEqual(matches.get(2), new MatOfDMatch(truth[2]), EPS);
assertTrue(matches.get(3).empty());
}
public void testRadiusMatchMatListOfListOfDMatchFloatListOfMat() {
fail("Not yet implemented");
}
public void testRadiusMatchMatListOfListOfDMatchFloatListOfMatBoolean() {
fail("Not yet implemented");
}
public void testRadiusMatchMatMatListOfListOfDMatchFloat() {
fail("Not yet implemented");
}
public void testRadiusMatchMatMatListOfListOfDMatchFloatMat() {
fail("Not yet implemented");
}
public void testRadiusMatchMatMatListOfListOfDMatchFloatMatBoolean() {
fail("Not yet implemented");
}
public void testRead() {
String filename = OpenCVTestRunner.getTempFileName("yml");
writeFile(filename, "%YAML:1.0\n---\n");
matcher.read(filename);
assertTrue(true);// BruteforceMatcher has no settings
}
public void testTrain() {
matcher.train();// BruteforceMatcher does not need to train
}
public void testWrite() {
String filename = OpenCVTestRunner.getTempFileName("yml");
matcher.write(filename);
String truth = "%YAML 1.2\n---\n";
assertEquals(truth, readFile(filename));
}
}
@@ -0,0 +1,257 @@
package org.opencv.test.features;
import java.util.Arrays;
import java.util.List;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.MatOfDMatch;
import org.opencv.core.MatOfKeyPoint;
import org.opencv.core.Point;
import org.opencv.core.Scalar;
import org.opencv.core.DMatch;
import org.opencv.features.DescriptorMatcher;
import org.opencv.features.FastFeatureDetector;
import org.opencv.test.OpenCVTestCase;
import org.opencv.test.OpenCVTestRunner;
import org.opencv.imgproc.Imgproc;
import org.opencv.features.Feature2D;
public class BruteForceHammingLUTDescriptorMatcherTest extends OpenCVTestCase {
DescriptorMatcher matcher;
int matSize;
DMatch[] truth;
private Mat getMaskImg() {
return new Mat(4, 4, CvType.CV_8U, new Scalar(0)) {
{
put(0, 0, 1, 1, 1, 1, 1, 1, 1, 1);
}
};
}
private Mat getQueryDescriptors() {
return getTestDescriptors(getQueryImg());
}
private Mat getQueryImg() {
Mat img = new Mat(matSize, matSize, CvType.CV_8U, new Scalar(255));
Imgproc.line(img, new Point(40, matSize - 40), new Point(matSize - 50, 50), new Scalar(0), 8);
return img;
}
private Mat getTestDescriptors(Mat img) {
MatOfKeyPoint keypoints = new MatOfKeyPoint();
Mat descriptors = new Mat();
Feature2D detector = FastFeatureDetector.create();
Feature2D extractor = createClassInstance(XFEATURES2D+"BriefDescriptorExtractor", DEFAULT_FACTORY, null, null);
detector.detect(img, keypoints);
extractor.compute(img, keypoints, descriptors);
return descriptors;
}
private Mat getTrainDescriptors() {
return getTestDescriptors(getTrainImg());
}
private Mat getTrainImg() {
Mat img = new Mat(matSize, matSize, CvType.CV_8U, new Scalar(255));
Imgproc.line(img, new Point(40, 40), new Point(matSize - 40, matSize - 40), new Scalar(0), 8);
return img;
}
protected void setUp() throws Exception {
super.setUp();
matcher = DescriptorMatcher.create(DescriptorMatcher.BRUTEFORCE_HAMMINGLUT);
matSize = 100;
truth = new DMatch[] {
new DMatch(0, 0, 0, 51),
new DMatch(1, 2, 0, 42),
new DMatch(2, 1, 0, 40),
new DMatch(3, 3, 0, 53) };
}
public void testAdd() {
matcher.add(Arrays.asList(new Mat()));
assertFalse(matcher.empty());
}
public void testClear() {
matcher.add(Arrays.asList(new Mat()));
matcher.clear();
assertTrue(matcher.empty());
}
public void testClone() {
Mat train = new Mat(1, 1, CvType.CV_8U, new Scalar(123));
Mat truth = train.clone();
matcher.add(Arrays.asList(train));
DescriptorMatcher cloned = matcher.clone();
assertNotNull(cloned);
List<Mat> descriptors = cloned.getTrainDescriptors();
assertEquals(1, descriptors.size());
assertMatEqual(truth, descriptors.get(0));
}
public void testCloneBoolean() {
matcher.add(Arrays.asList(new Mat()));
DescriptorMatcher cloned = matcher.clone(true);
assertNotNull(cloned);
assertTrue(cloned.empty());
}
public void testCreate() {
assertNotNull(matcher);
}
public void testEmpty() {
assertTrue(matcher.empty());
}
public void testGetTrainDescriptors() {
Mat train = new Mat(1, 1, CvType.CV_8U, new Scalar(123));
Mat truth = train.clone();
matcher.add(Arrays.asList(train));
List<Mat> descriptors = matcher.getTrainDescriptors();
assertEquals(1, descriptors.size());
assertMatEqual(truth, descriptors.get(0));
}
public void testIsMaskSupported() {
assertTrue(matcher.isMaskSupported());
}
public void testKnnMatchMatListOfListOfDMatchInt() {
fail("Not yet implemented");
}
public void testKnnMatchMatListOfListOfDMatchIntListOfMat() {
fail("Not yet implemented");
}
public void testKnnMatchMatListOfListOfDMatchIntListOfMatBoolean() {
fail("Not yet implemented");
}
public void testKnnMatchMatMatListOfListOfDMatchInt() {
fail("Not yet implemented");
}
public void testKnnMatchMatMatListOfListOfDMatchIntMat() {
fail("Not yet implemented");
}
public void testKnnMatchMatMatListOfListOfDMatchIntMatBoolean() {
fail("Not yet implemented");
}
public void testMatchMatListOfDMatch() {
Mat train = getTrainDescriptors();
Mat query = getQueryDescriptors();
MatOfDMatch matches = new MatOfDMatch();
matcher.add(Arrays.asList(train));
matcher.match(query, matches);
assertArrayDMatchEquals(truth, matches.toArray(), EPS);
}
public void testMatchMatListOfDMatchListOfMat() {
Mat train = getTrainDescriptors();
Mat query = getQueryDescriptors();
Mat mask = getMaskImg();
MatOfDMatch matches = new MatOfDMatch();
matcher.add(Arrays.asList(train));
matcher.match(query, matches, Arrays.asList(mask));
assertListDMatchEquals(Arrays.asList(truth[0], truth[1]), matches.toList(), EPS);
}
public void testMatchMatMatListOfDMatch() {
Mat train = getTrainDescriptors();
Mat query = getQueryDescriptors();
MatOfDMatch matches = new MatOfDMatch();
matcher.match(query, train, matches);
/*
OpenCVTestRunner.Log("matches found: " + matches.size());
for (DMatch m : matches.toArray())
OpenCVTestRunner.Log(m.toString());
*/
assertArrayDMatchEquals(truth, matches.toArray(), EPS);
}
public void testMatchMatMatListOfDMatchMat() {
Mat train = getTrainDescriptors();
Mat query = getQueryDescriptors();
Mat mask = getMaskImg();
MatOfDMatch matches = new MatOfDMatch();
matcher.match(query, train, matches, mask);
assertListDMatchEquals(Arrays.asList(truth[0], truth[1]), matches.toList(), EPS);
}
public void testRadiusMatchMatListOfListOfDMatchFloat() {
fail("Not yet implemented");
}
public void testRadiusMatchMatListOfListOfDMatchFloatListOfMat() {
fail("Not yet implemented");
}
public void testRadiusMatchMatListOfListOfDMatchFloatListOfMatBoolean() {
fail("Not yet implemented");
}
public void testRadiusMatchMatMatListOfListOfDMatchFloat() {
fail("Not yet implemented");
}
public void testRadiusMatchMatMatListOfListOfDMatchFloatMat() {
fail("Not yet implemented");
}
public void testRadiusMatchMatMatListOfListOfDMatchFloatMatBoolean() {
fail("Not yet implemented");
}
public void testRead() {
String filename = OpenCVTestRunner.getTempFileName("yml");
writeFile(filename, "%YAML:1.0\n---\n");
matcher.read(filename);
assertTrue(true);// BruteforceMatcher has no settings
}
public void testTrain() {
matcher.train();// BruteforceMatcher does not need to train
}
public void testWrite() {
String filename = OpenCVTestRunner.getTempFileName("yml");
matcher.write(filename);
String truth = "%YAML 1.2\n---\n";
assertEquals(truth, readFile(filename));
}
}
@@ -0,0 +1,268 @@
package org.opencv.test.features;
import java.util.Arrays;
import java.util.List;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.MatOfDMatch;
import org.opencv.core.MatOfKeyPoint;
import org.opencv.core.Point;
import org.opencv.core.Scalar;
import org.opencv.core.DMatch;
import org.opencv.features.DescriptorMatcher;
import org.opencv.core.KeyPoint;
import org.opencv.test.OpenCVTestCase;
import org.opencv.test.OpenCVTestRunner;
import org.opencv.imgproc.Imgproc;
import org.opencv.features.Feature2D;
public class BruteForceL1DescriptorMatcherTest extends OpenCVTestCase {
DescriptorMatcher matcher;
int matSize;
DMatch[] truth;
private Mat getMaskImg() {
return new Mat(5, 2, CvType.CV_8U, new Scalar(0)) {
{
put(0, 0, 1, 1, 1, 1);
}
};
}
private Mat getQueryDescriptors() {
Mat img = getQueryImg();
MatOfKeyPoint keypoints = new MatOfKeyPoint();
Mat descriptors = new Mat();
Feature2D detector = createClassInstance(XFEATURES2D+"SURF", DEFAULT_FACTORY, null, null);
Feature2D extractor = createClassInstance(XFEATURES2D+"SURF", DEFAULT_FACTORY, null, null);
setProperty(detector, "extended", "boolean", true);
setProperty(detector, "hessianThreshold", "double", 8000);
setProperty(detector, "nOctaveLayers", "int", 2);
setProperty(detector, "nOctaves", "int", 3);
setProperty(detector, "upright", "boolean", false);
detector.detect(img, keypoints);
extractor.compute(img, keypoints, descriptors);
return descriptors;
}
private Mat getQueryImg() {
Mat cross = new Mat(matSize, matSize, CvType.CV_8U, new Scalar(255));
Imgproc.line(cross, new Point(30, matSize / 2), new Point(matSize - 31, matSize / 2), new Scalar(100), 3);
Imgproc.line(cross, new Point(matSize / 2, 30), new Point(matSize / 2, matSize - 31), new Scalar(100), 3);
return cross;
}
private Mat getTrainDescriptors() {
Mat img = getTrainImg();
MatOfKeyPoint keypoints = new MatOfKeyPoint(new KeyPoint(50, 50, 16, 0, 20000, 1, -1), new KeyPoint(42, 42, 16, 160, 10000, 1, -1));
Mat descriptors = new Mat();
Feature2D extractor = createClassInstance(XFEATURES2D+"SURF", DEFAULT_FACTORY, null, null);
extractor.compute(img, keypoints, descriptors);
return descriptors;
}
private Mat getTrainImg() {
Mat cross = new Mat(matSize, matSize, CvType.CV_8U, new Scalar(255));
Imgproc.line(cross, new Point(20, matSize / 2), new Point(matSize - 21, matSize / 2), new Scalar(100), 2);
Imgproc.line(cross, new Point(matSize / 2, 20), new Point(matSize / 2, matSize - 21), new Scalar(100), 2);
return cross;
}
protected void setUp() throws Exception {
super.setUp();
matcher = DescriptorMatcher.create(DescriptorMatcher.BRUTEFORCE_L1);
matSize = 100;
truth = new DMatch[] {
new DMatch(0, 0, 0, 3.0710702f),
new DMatch(1, 1, 0, 3.562016f),
new DMatch(2, 1, 0, 1.3682679f),
new DMatch(3, 1, 0, 1.3012862f),
new DMatch(4, 1, 0, 1.1852086f)
};
}
public void testAdd() {
matcher.add(Arrays.asList(new Mat()));
assertFalse(matcher.empty());
}
public void testClear() {
matcher.add(Arrays.asList(new Mat()));
matcher.clear();
assertTrue(matcher.empty());
}
public void testClone() {
Mat train = new Mat(1, 1, CvType.CV_8U, new Scalar(123));
Mat truth = train.clone();
matcher.add(Arrays.asList(train));
DescriptorMatcher cloned = matcher.clone();
assertNotNull(cloned);
List<Mat> descriptors = cloned.getTrainDescriptors();
assertEquals(1, descriptors.size());
assertMatEqual(truth, descriptors.get(0));
}
public void testCloneBoolean() {
matcher.add(Arrays.asList(new Mat()));
DescriptorMatcher cloned = matcher.clone(true);
assertNotNull(cloned);
assertTrue(cloned.empty());
}
public void testCreate() {
assertNotNull(matcher);
}
public void testEmpty() {
assertTrue(matcher.empty());
}
public void testGetTrainDescriptors() {
Mat train = new Mat(1, 1, CvType.CV_8U, new Scalar(123));
Mat truth = train.clone();
matcher.add(Arrays.asList(train));
List<Mat> descriptors = matcher.getTrainDescriptors();
assertEquals(1, descriptors.size());
assertMatEqual(truth, descriptors.get(0));
}
public void testIsMaskSupported() {
assertTrue(matcher.isMaskSupported());
}
public void testKnnMatchMatListOfListOfDMatchInt() {
fail("Not yet implemented");
}
public void testKnnMatchMatListOfListOfDMatchIntListOfMat() {
fail("Not yet implemented");
}
public void testKnnMatchMatListOfListOfDMatchIntListOfMatBoolean() {
fail("Not yet implemented");
}
public void testKnnMatchMatMatListOfListOfDMatchInt() {
fail("Not yet implemented");
}
public void testKnnMatchMatMatListOfListOfDMatchIntMat() {
fail("Not yet implemented");
}
public void testKnnMatchMatMatListOfListOfDMatchIntMatBoolean() {
fail("Not yet implemented");
}
public void testMatchMatListOfDMatch() {
Mat train = getTrainDescriptors();
Mat query = getQueryDescriptors();
MatOfDMatch matches = new MatOfDMatch();
matcher.add(Arrays.asList(train));
matcher.match(query, matches);
assertArrayDMatchEquals(truth, matches.toArray(), EPS);
}
public void testMatchMatListOfDMatchListOfMat() {
Mat train = getTrainDescriptors();
Mat query = getQueryDescriptors();
Mat mask = getMaskImg();
MatOfDMatch matches = new MatOfDMatch();
matcher.add(Arrays.asList(train));
matcher.match(query, matches, Arrays.asList(mask));
assertListDMatchEquals(Arrays.asList(truth[0], truth[1]), matches.toList(), EPS);
}
public void testMatchMatMatListOfDMatch() {
Mat train = getTrainDescriptors();
Mat query = getQueryDescriptors();
MatOfDMatch matches = new MatOfDMatch();
matcher.match(query, train, matches);
assertArrayDMatchEquals(truth, matches.toArray(), EPS);
}
public void testMatchMatMatListOfDMatchMat() {
Mat train = getTrainDescriptors();
Mat query = getQueryDescriptors();
Mat mask = getMaskImg();
MatOfDMatch matches = new MatOfDMatch();
matcher.match(query, train, matches, mask);
assertListDMatchEquals(Arrays.asList(truth[0], truth[1]), matches.toList(), EPS);
}
public void testRadiusMatchMatListOfListOfDMatchFloat() {
fail("Not yet implemented");
}
public void testRadiusMatchMatListOfListOfDMatchFloatListOfMat() {
fail("Not yet implemented");
}
public void testRadiusMatchMatListOfListOfDMatchFloatListOfMatBoolean() {
fail("Not yet implemented");
}
public void testRadiusMatchMatMatListOfListOfDMatchFloat() {
fail("Not yet implemented");
}
public void testRadiusMatchMatMatListOfListOfDMatchFloatMat() {
fail("Not yet implemented");
}
public void testRadiusMatchMatMatListOfListOfDMatchFloatMatBoolean() {
fail("Not yet implemented");
}
public void testRead() {
String filename = OpenCVTestRunner.getTempFileName("yml");
writeFile(filename, "%YAML:1.0\n---\n");
matcher.read(filename);
assertTrue(true);// BruteforceMatcher has no settings
}
public void testTrain() {
matcher.train();// BruteforceMatcher does not need to train
}
public void testWrite() {
String filename = OpenCVTestRunner.getTempFileName("yml");
matcher.write(filename);
String truth = "%YAML 1.2\n---\n";
assertEquals(truth, readFile(filename));
}
}
@@ -0,0 +1,280 @@
package org.opencv.test.features;
import java.util.Arrays;
import java.util.List;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.MatOfDMatch;
import org.opencv.core.MatOfKeyPoint;
import org.opencv.core.Point;
import org.opencv.core.Scalar;
import org.opencv.core.DMatch;
import org.opencv.features.DescriptorMatcher;
import org.opencv.core.KeyPoint;
import org.opencv.test.OpenCVTestCase;
import org.opencv.test.OpenCVTestRunner;
import org.opencv.imgproc.Imgproc;
import org.opencv.features.Feature2D;
public class BruteForceSL2DescriptorMatcherTest extends OpenCVTestCase {
DescriptorMatcher matcher;
int matSize;
DMatch[] truth;
private Mat getMaskImg() {
return new Mat(5, 2, CvType.CV_8U, new Scalar(0)) {
{
put(0, 0, 1, 1, 1, 1);
}
};
}
/*
private float sqr(float val){
return val * val;
}
*/
private Mat getQueryDescriptors() {
Mat img = getQueryImg();
MatOfKeyPoint keypoints = new MatOfKeyPoint();
Mat descriptors = new Mat();
Feature2D detector = createClassInstance(XFEATURES2D+"SURF", DEFAULT_FACTORY, null, null);
Feature2D extractor = createClassInstance(XFEATURES2D+"SURF", DEFAULT_FACTORY, null, null);
setProperty(detector, "hessianThreshold", "double", 8000);
setProperty(detector, "nOctaves", "int", 3);
setProperty(detector, "nOctaveLayers", "int", 4);
setProperty(detector, "upright", "boolean", false);
detector.detect(img, keypoints);
extractor.compute(img, keypoints, descriptors);
return descriptors;
}
private Mat getQueryImg() {
Mat cross = new Mat(matSize, matSize, CvType.CV_8U, new Scalar(255));
Imgproc.line(cross, new Point(30, matSize / 2), new Point(matSize - 31, matSize / 2), new Scalar(100), 3);
Imgproc.line(cross, new Point(matSize / 2, 30), new Point(matSize / 2, matSize - 31), new Scalar(100), 3);
return cross;
}
private Mat getTrainDescriptors() {
Mat img = getTrainImg();
MatOfKeyPoint keypoints = new MatOfKeyPoint(new KeyPoint(50, 50, 16, 0, 20000, 1, -1), new KeyPoint(42, 42, 16, 160, 10000, 1, -1));
Mat descriptors = new Mat();
Feature2D extractor = createClassInstance(XFEATURES2D+"SURF", DEFAULT_FACTORY, null, null);
extractor.compute(img, keypoints, descriptors);
return descriptors;
}
private Mat getTrainImg() {
Mat cross = new Mat(matSize, matSize, CvType.CV_8U, new Scalar(255));
Imgproc.line(cross, new Point(20, matSize / 2), new Point(matSize - 21, matSize / 2), new Scalar(100), 2);
Imgproc.line(cross, new Point(matSize / 2, 20), new Point(matSize / 2, matSize - 21), new Scalar(100), 2);
return cross;
}
protected void setUp() throws Exception {
super.setUp();
matcher = DescriptorMatcher.create(DescriptorMatcher.BRUTEFORCE_SL2);
matSize = 100;
truth = new DMatch[] {
new DMatch(0, 0, 0, 0.37933317f),
new DMatch(1, 1, 0, 0.8421953f),
new DMatch(2, 1, 0, 0.0968556f),
new DMatch(3, 1, 0, 0.0855606f),
new DMatch(4, 1, 0, 0.07033461f)
};
}
public void testAdd() {
matcher.add(Arrays.asList(new Mat()));
assertFalse(matcher.empty());
}
public void testClear() {
matcher.add(Arrays.asList(new Mat()));
matcher.clear();
assertTrue(matcher.empty());
}
public void testClone() {
Mat train = new Mat(1, 1, CvType.CV_8U, new Scalar(123));
Mat truth = train.clone();
matcher.add(Arrays.asList(train));
DescriptorMatcher cloned = matcher.clone();
assertNotNull(cloned);
List<Mat> descriptors = cloned.getTrainDescriptors();
assertEquals(1, descriptors.size());
assertMatEqual(truth, descriptors.get(0));
}
public void testCloneBoolean() {
matcher.add(Arrays.asList(new Mat()));
DescriptorMatcher cloned = matcher.clone(true);
assertNotNull(cloned);
assertTrue(cloned.empty());
}
public void testCreate() {
assertNotNull(matcher);
}
public void testEmpty() {
assertTrue(matcher.empty());
}
public void testGetTrainDescriptors() {
Mat train = new Mat(1, 1, CvType.CV_8U, new Scalar(123));
Mat truth = train.clone();
matcher.add(Arrays.asList(train));
List<Mat> descriptors = matcher.getTrainDescriptors();
assertEquals(1, descriptors.size());
assertMatEqual(truth, descriptors.get(0));
}
public void testIsMaskSupported() {
assertTrue(matcher.isMaskSupported());
}
public void testKnnMatchMatListOfListOfDMatchInt() {
fail("Not yet implemented");
}
public void testKnnMatchMatListOfListOfDMatchIntListOfMat() {
fail("Not yet implemented");
}
public void testKnnMatchMatListOfListOfDMatchIntListOfMatBoolean() {
fail("Not yet implemented");
}
public void testKnnMatchMatMatListOfListOfDMatchInt() {
fail("Not yet implemented");
}
public void testKnnMatchMatMatListOfListOfDMatchIntMat() {
fail("Not yet implemented");
}
public void testKnnMatchMatMatListOfListOfDMatchIntMatBoolean() {
fail("Not yet implemented");
}
public void testMatchMatListOfDMatch() {
Mat train = getTrainDescriptors();
Mat query = getQueryDescriptors();
MatOfDMatch matches = new MatOfDMatch();
matcher.add(Arrays.asList(train));
matcher.match(query, matches);
OpenCVTestRunner.Log(matches);
OpenCVTestRunner.Log(matches);
OpenCVTestRunner.Log(matches);
assertArrayDMatchEquals(truth, matches.toArray(), EPS);
}
public void testMatchMatListOfDMatchListOfMat() {
Mat train = getTrainDescriptors();
Mat query = getQueryDescriptors();
Mat mask = getMaskImg();
MatOfDMatch matches = new MatOfDMatch();
matcher.add(Arrays.asList(train));
matcher.match(query, matches, Arrays.asList(mask));
assertListDMatchEquals(Arrays.asList(truth[0], truth[1]), matches.toList(), EPS);
}
public void testMatchMatMatListOfDMatch() {
Mat train = getTrainDescriptors();
Mat query = getQueryDescriptors();
MatOfDMatch matches = new MatOfDMatch();
matcher.match(query, train, matches);
assertArrayDMatchEquals(truth, matches.toArray(), EPS);
// OpenCVTestRunner.Log("matches found: " + matches.size());
// for (DMatch m : matches)
// OpenCVTestRunner.Log(m.toString());
}
public void testMatchMatMatListOfDMatchMat() {
Mat train = getTrainDescriptors();
Mat query = getQueryDescriptors();
Mat mask = getMaskImg();
MatOfDMatch matches = new MatOfDMatch();
matcher.match(query, train, matches, mask);
assertListDMatchEquals(Arrays.asList(truth[0], truth[1]), matches.toList(), EPS);
}
public void testRadiusMatchMatListOfListOfDMatchFloat() {
fail("Not yet implemented");
}
public void testRadiusMatchMatListOfListOfDMatchFloatListOfMat() {
fail("Not yet implemented");
}
public void testRadiusMatchMatListOfListOfDMatchFloatListOfMatBoolean() {
fail("Not yet implemented");
}
public void testRadiusMatchMatMatListOfListOfDMatchFloat() {
fail("Not yet implemented");
}
public void testRadiusMatchMatMatListOfListOfDMatchFloatMat() {
fail("Not yet implemented");
}
public void testRadiusMatchMatMatListOfListOfDMatchFloatMatBoolean() {
fail("Not yet implemented");
}
public void testRead() {
String filename = OpenCVTestRunner.getTempFileName("yml");
writeFile(filename, "%YAML:1.0\n---\n");
matcher.read(filename);
assertTrue(true);// BruteforceMatcher has no settings
}
public void testTrain() {
matcher.train();// BruteforceMatcher does not need to train
}
public void testWrite() {
String filename = OpenCVTestRunner.getTempFileName("yml");
matcher.write(filename);
String truth = "%YAML 1.2\n---\n";
assertEquals(truth, readFile(filename));
}
}
@@ -0,0 +1,39 @@
package org.opencv.test.features;
import org.opencv.test.OpenCVTestCase;
public class DENSEFeatureDetectorTest extends OpenCVTestCase {
public void testCreate() {
fail("Not yet implemented");
}
public void testDetectListOfMatListOfListOfKeyPoint() {
fail("Not yet implemented");
}
public void testDetectListOfMatListOfListOfKeyPointListOfMat() {
fail("Not yet implemented");
}
public void testDetectMatListOfKeyPoint() {
fail("Not yet implemented");
}
public void testDetectMatListOfKeyPointMat() {
fail("Not yet implemented");
}
public void testEmpty() {
fail("Not yet implemented");
}
public void testRead() {
fail("Not yet implemented");
}
public void testWrite() {
fail("Not yet implemented");
}
}
@@ -0,0 +1,141 @@
package org.opencv.test.features;
import java.util.Arrays;
import org.opencv.core.Core;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.MatOfKeyPoint;
import org.opencv.core.Point;
import org.opencv.core.Scalar;
import org.opencv.features.FastFeatureDetector;
import org.opencv.core.KeyPoint;
import org.opencv.test.OpenCVTestCase;
import org.opencv.test.OpenCVTestRunner;
import org.opencv.imgproc.Imgproc;
public class FASTFeatureDetectorTest extends OpenCVTestCase {
FastFeatureDetector detector;
KeyPoint[] truth;
private Mat getMaskImg() {
Mat mask = new Mat(100, 100, CvType.CV_8U, new Scalar(255));
Mat right = mask.submat(0, 100, 50, 100);
right.setTo(new Scalar(0));
return mask;
}
private Mat getTestImg() {
Mat img = new Mat(100, 100, CvType.CV_8U, new Scalar(255));
Imgproc.line(img, new Point(30, 30), new Point(70, 70), new Scalar(0), 8);
return img;
}
@Override
protected void setUp() throws Exception {
super.setUp();
detector = FastFeatureDetector.create();
truth = new KeyPoint[] { new KeyPoint(32, 27, 7, -1, 254, 0, -1), new KeyPoint(27, 32, 7, -1, 254, 0, -1), new KeyPoint(73, 68, 7, -1, 254, 0, -1),
new KeyPoint(68, 73, 7, -1, 254, 0, -1) };
}
public void testCreate() {
assertNotNull(detector);
}
public void testDetectListOfMatListOfListOfKeyPoint() {
fail("Not yet implemented");
}
public void testDetectListOfMatListOfListOfKeyPointListOfMat() {
fail("Not yet implemented");
}
public void testDetectMatListOfKeyPoint() {
Mat img = getTestImg();
MatOfKeyPoint keypoints = new MatOfKeyPoint();
detector.detect(img, keypoints);
assertListKeyPointEquals(Arrays.asList(truth), keypoints.toList(), EPS);
// OpenCVTestRunner.Log("points found: " + keypoints.size());
// for (KeyPoint kp : keypoints)
// OpenCVTestRunner.Log(kp.toString());
}
public void testDetectMatListOfKeyPointMat() {
Mat img = getTestImg();
Mat mask = getMaskImg();
MatOfKeyPoint keypoints = new MatOfKeyPoint();
detector.detect(img, keypoints, mask);
assertListKeyPointEquals(Arrays.asList(truth[0], truth[1]), keypoints.toList(), EPS);
}
public void testEmpty() {
// assertFalse(detector.empty());
fail("Not yet implemented"); // FAST does not override empty() method
}
public void testRead() {
String filename = OpenCVTestRunner.getTempFileName("xml");
writeFile(filename, "<?xml version=\"1.0\"?>\n<opencv_storage>\n<name>Feature2D.FastFeatureDetector</name>\n<threshold>10</threshold>\n<nonmaxSuppression>1</nonmaxSuppression>\n<type>2</type>\n</opencv_storage>\n");
detector.read(filename);
assertEquals(10, detector.getThreshold());
assertEquals(true, detector.getNonmaxSuppression());
assertEquals(2, detector.getType());
MatOfKeyPoint keypoints1 = new MatOfKeyPoint();
detector.detect(grayChess, keypoints1);
writeFile(filename, "<?xml version=\"1.0\"?>\n<opencv_storage>\n<name>Feature2D.FastFeatureDetector</name>\n<threshold>150</threshold>\n<nonmaxSuppression>1</nonmaxSuppression>\n<type>2</type>\n</opencv_storage>\n");
detector.read(filename);
MatOfKeyPoint keypoints2 = new MatOfKeyPoint();
detector.detect(grayChess, keypoints2);
assertTrue(keypoints2.total() <= keypoints1.total());
}
public void testReadYml() {
String filename = OpenCVTestRunner.getTempFileName("yml");
writeFile(filename, "%YAML:1.0\n---\nthreshold: 130\nnonmaxSuppression: 1\ntype: 2\n");
detector.read(filename);
assertEquals(130, detector.getThreshold());
assertEquals(true, detector.getNonmaxSuppression());
assertEquals(2, detector.getType());
MatOfKeyPoint keypoints1 = new MatOfKeyPoint();
detector.detect(grayChess, keypoints1);
writeFile(filename, "%YAML:1.0\n---\nthreshold: 150\nnonmaxSuppression: 1\ntype: 2\n");
detector.read(filename);
MatOfKeyPoint keypoints2 = new MatOfKeyPoint();
detector.detect(grayChess, keypoints2);
assertTrue(keypoints2.total() <= keypoints1.total());
}
public void testWriteYml() {
String filename = OpenCVTestRunner.getTempFileName("yml");
detector.write(filename);
String truth = "%YAML 1.2\n---\nname: \"Feature2D.FastFeatureDetector\"\nthreshold: 10\nnonmaxSuppression: true\ntype: 2\n";
String data = readFile(filename);
assertEquals(truth, data);
}
}
@@ -0,0 +1,195 @@
package org.opencv.test.features;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.opencv.geometry.Geometry;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.MatOfInt;
import org.opencv.core.MatOfDMatch;
import org.opencv.core.MatOfKeyPoint;
import org.opencv.core.MatOfPoint;
import org.opencv.core.MatOfPoint2f;
import org.opencv.core.Point;
import org.opencv.core.Range;
import org.opencv.core.Scalar;
import org.opencv.core.DMatch;
import org.opencv.features.DescriptorMatcher;
import org.opencv.features.Features;
import org.opencv.core.KeyPoint;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
import org.opencv.test.OpenCVTestCase;
import org.opencv.test.OpenCVTestRunner;
import org.opencv.features.Feature2D;
public class FeaturesTest extends OpenCVTestCase {
public void testDrawKeypointsMatListOfKeyPointMat() {
fail("Not yet implemented");
}
public void testDrawKeypointsMatListOfKeyPointMatScalar() {
fail("Not yet implemented");
}
public void testDrawKeypointsMatListOfKeyPointMatScalarInt() {
fail("Not yet implemented");
}
public void testDrawMatches2MatListOfKeyPointMatListOfKeyPointListOfListOfDMatchMat() {
fail("Not yet implemented");
}
public void testDrawMatches2MatListOfKeyPointMatListOfKeyPointListOfListOfDMatchMatScalar() {
fail("Not yet implemented");
}
public void testDrawMatches2MatListOfKeyPointMatListOfKeyPointListOfListOfDMatchMatScalarScalar() {
fail("Not yet implemented");
}
public void testDrawMatches2MatListOfKeyPointMatListOfKeyPointListOfListOfDMatchMatScalarScalarListOfListOfByte() {
fail("Not yet implemented");
}
public void testDrawMatches2MatListOfKeyPointMatListOfKeyPointListOfListOfDMatchMatScalarScalarListOfListOfByteInt() {
fail("Not yet implemented");
}
public void testDrawMatchesMatListOfKeyPointMatListOfKeyPointListOfDMatchMat() {
fail("Not yet implemented");
}
public void testDrawMatchesMatListOfKeyPointMatListOfKeyPointListOfDMatchMatScalar() {
fail("Not yet implemented");
}
public void testDrawMatchesMatListOfKeyPointMatListOfKeyPointListOfDMatchMatScalarScalar() {
fail("Not yet implemented");
}
public void testDrawMatchesMatListOfKeyPointMatListOfKeyPointListOfDMatchMatScalarScalarListOfByte() {
fail("Not yet implemented");
}
public void testDrawMatchesMatListOfKeyPointMatListOfKeyPointListOfDMatchMatScalarScalarListOfByteInt() {
fail("Not yet implemented");
}
public void testPTOD()
{
String detectorCfg = "%YAML:1.0\n---\nhessianThreshold: 4000.\nextended: 0\nupright: 0\nOctaves: 4\nOctaveLayers: 3\n";
String extractorCfg = "%YAML:1.0\n---\nhessianThreshold: 4000.\nextended: 0\nupright: 0\nOctaves: 4\nOctaveLayers: 3\n";
Feature2D detector = createClassInstance(XFEATURES2D+"SURF", DEFAULT_FACTORY, null, null);
Feature2D extractor = createClassInstance(XFEATURES2D+"SURF", DEFAULT_FACTORY, null, null);
DescriptorMatcher matcher = DescriptorMatcher.create(DescriptorMatcher.BRUTEFORCE);
String detectorCfgFile = OpenCVTestRunner.getTempFileName("yml");
writeFile(detectorCfgFile, detectorCfg);
detector.read(detectorCfgFile);
String extractorCfgFile = OpenCVTestRunner.getTempFileName("yml");
writeFile(extractorCfgFile, extractorCfg);
extractor.read(extractorCfgFile);
Mat imgTrain = Imgcodecs.imread(OpenCVTestRunner.LENA_PATH, Imgcodecs.IMREAD_GRAYSCALE);
Mat imgQuery = imgTrain.submat(new Range(0, imgTrain.rows() - 100), Range.all());
MatOfKeyPoint trainKeypoints = new MatOfKeyPoint();
MatOfKeyPoint queryKeypoints = new MatOfKeyPoint();
detector.detect(imgTrain, trainKeypoints);
detector.detect(imgQuery, queryKeypoints);
// OpenCVTestRunner.Log("Keypoints found: " + trainKeypoints.size() +
// ":" + queryKeypoints.size());
Mat trainDescriptors = new Mat();
Mat queryDescriptors = new Mat();
extractor.compute(imgTrain, trainKeypoints, trainDescriptors);
extractor.compute(imgQuery, queryKeypoints, queryDescriptors);
MatOfDMatch matches = new MatOfDMatch();
matcher.add(Arrays.asList(trainDescriptors));
matcher.match(queryDescriptors, matches);
// OpenCVTestRunner.Log("Matches found: " + matches.size());
DMatch adm[] = matches.toArray();
List<Point> lp1 = new ArrayList<Point>(adm.length);
List<Point> lp2 = new ArrayList<Point>(adm.length);
KeyPoint tkp[] = trainKeypoints.toArray();
KeyPoint qkp[] = queryKeypoints.toArray();
for (int i = 0; i < adm.length; i++) {
DMatch dm = adm[i];
lp1.add(tkp[dm.trainIdx].pt);
lp2.add(qkp[dm.queryIdx].pt);
}
MatOfPoint2f points1 = new MatOfPoint2f(lp1.toArray(new Point[0]));
MatOfPoint2f points2 = new MatOfPoint2f(lp2.toArray(new Point[0]));
Mat hmg = Geometry.findHomography(points1, points2, Geometry.RANSAC, 3);
assertMatEqual(Mat.eye(3, 3, CvType.CV_64F), hmg, EPS);
Mat outimg = new Mat();
Features.drawMatches(imgQuery, queryKeypoints, imgTrain, trainKeypoints, matches, outimg);
String outputPath = OpenCVTestRunner.getOutputFileName("PTODresult.png");
Imgcodecs.imwrite(outputPath, outimg);
// OpenCVTestRunner.Log("Output image is saved to: " + outputPath);
}
public void testDrawKeypoints()
{
Mat outImg = Mat.ones(11, 11, CvType.CV_8U);
MatOfKeyPoint kps = new MatOfKeyPoint(new KeyPoint(5, 5, 1)); // x, y, size
Features.drawKeypoints(new Mat(), kps, outImg, new Scalar(255),
Features.DrawMatchesFlags_DRAW_OVER_OUTIMG);
Mat ref = new MatOfInt(new int[] {
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 15, 54, 15, 1, 1, 1, 1,
1, 1, 1, 76, 217, 217, 221, 81, 1, 1, 1,
1, 1, 100, 224, 111, 57, 115, 225, 101, 1, 1,
1, 44, 215, 100, 1, 1, 1, 101, 214, 44, 1,
1, 54, 212, 57, 1, 1, 1, 55, 212, 55, 1,
1, 40, 215, 104, 1, 1, 1, 105, 215, 40, 1,
1, 1, 102, 221, 111, 55, 115, 222, 103, 1, 1,
1, 1, 1, 76, 218, 217, 220, 81, 1, 1, 1,
1, 1, 1, 1, 15, 55, 15, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
}).reshape(1, 11);
ref.convertTo(ref, CvType.CV_8U);
assertMatEqual(ref, outImg);
}
public void testGoodFeaturesToTrackMatListOfPointIntDoubleDouble() {
Mat src = gray0;
Imgproc.rectangle(src, new Point(2, 2), new Point(8, 8), new Scalar(100), -1);
MatOfPoint lp = new MatOfPoint();
Features.goodFeaturesToTrack(src, lp, 100, 0.01, 3);
assertEquals(4, lp.total());
}
public void testGoodFeaturesToTrackMatListOfPointIntDoubleDoubleMatIntBooleanDouble() {
Mat src = gray0;
Imgproc.rectangle(src, new Point(2, 2), new Point(8, 8), new Scalar(100), -1);
MatOfPoint lp = new MatOfPoint();
Features.goodFeaturesToTrack(src, lp, 100, 0.01, 3, gray1, 4, 3, true, 0);
assertEquals(4, lp.total());
}
}
@@ -0,0 +1,389 @@
package org.opencv.test.features;
import java.util.Arrays;
import java.util.List;
import org.opencv.core.CvException;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.MatOfDMatch;
import org.opencv.core.MatOfKeyPoint;
import org.opencv.core.Point;
import org.opencv.core.Scalar;
import org.opencv.core.DMatch;
import org.opencv.features.DescriptorMatcher;
import org.opencv.features.FlannBasedMatcher;
import org.opencv.core.KeyPoint;
import org.opencv.test.OpenCVTestCase;
import org.opencv.test.OpenCVTestRunner;
import org.opencv.imgproc.Imgproc;
import org.opencv.features.Feature2D;
public class FlannBasedDescriptorMatcherTest extends OpenCVTestCase {
static final String xmlParamsDefault = "<?xml version=\"1.0\"?>\n"
+ "<opencv_storage>\n"
+ "<format>3</format>\n"
+ "<indexParams>\n"
+ " <_>\n"
+ " <name>algorithm</name>\n"
+ " <type>9</type>\n" // FLANN_INDEX_TYPE_ALGORITHM
+ " <value>1</value></_>\n"
+ " <_>\n"
+ " <name>trees</name>\n"
+ " <type>4</type>\n"
+ " <value>4</value></_></indexParams>\n"
+ "<searchParams>\n"
+ " <_>\n"
+ " <name>checks</name>\n"
+ " <type>4</type>\n"
+ " <value>32</value></_>\n"
+ " <_>\n"
+ " <name>eps</name>\n"
+ " <type>5</type>\n"
+ " <value>0.</value></_>\n"
+ " <_>\n"
+ " <name>explore_all_trees</name>\n"
+ " <type>8</type>\n"
+ " <value>0</value></_>\n"
+ " <_>\n"
+ " <name>sorted</name>\n"
+ " <type>8</type>\n" // FLANN_INDEX_TYPE_BOOL
+ " <value>1</value></_></searchParams>\n"
+ "</opencv_storage>\n";
static final String ymlParamsDefault = "%YAML 1.2\n---\n"
+ "format: 3\n"
+ "indexParams:\n"
+ " -\n"
+ " name: algorithm\n"
+ " type: 9\n" // FLANN_INDEX_TYPE_ALGORITHM
+ " value: 1\n"
+ " -\n"
+ " name: trees\n"
+ " type: 4\n"
+ " value: 4\n"
+ "searchParams:\n"
+ " -\n"
+ " name: checks\n"
+ " type: 4\n"
+ " value: 32\n"
+ " -\n"
+ " name: eps\n"
+ " type: 5\n"
+ " value: 0.\n"
+ " -\n"
+ " name: explore_all_trees\n"
+ " type: 8\n"
+ " value: 0\n"
+ " -\n"
+ " name: sorted\n"
+ " type: 8\n" // FLANN_INDEX_TYPE_BOOL
+ " value: 1\n";
static final String ymlParamsModified = "%YAML 1.2\n---\n"
+ "format: 3\n"
+ "indexParams:\n"
+ " -\n"
+ " name: algorithm\n"
+ " type: 9\n" // FLANN_INDEX_TYPE_ALGORITHM
+ " value: 6\n"// this line is changed!
+ " -\n"
+ " name: trees\n"
+ " type: 4\n"
+ " value: 4\n"
+ "searchParams:\n"
+ " -\n"
+ " name: checks\n"
+ " type: 4\n"
+ " value: 32\n"
+ " -\n"
+ " name: eps\n"
+ " type: 5\n"
+ " value: 4.\n"// this line is changed!
+ " -\n"
+ " name: explore_all_trees\n"
+ " type: 8\n"
+ " value: 1\n"// this line is changed!
+ " -\n"
+ " name: sorted\n"
+ " type: 8\n" // FLANN_INDEX_TYPE_BOOL
+ " value: 1\n";
DescriptorMatcher matcher;
int matSize;
DMatch[] truth;
private Mat getMaskImg() {
return new Mat(5, 2, CvType.CV_8U, new Scalar(0)) {
{
put(0, 0, 1, 1, 1, 1);
}
};
}
private Mat getQueryDescriptors() {
Mat img = getQueryImg();
MatOfKeyPoint keypoints = new MatOfKeyPoint();
Mat descriptors = new Mat();
Feature2D detector = createClassInstance(XFEATURES2D+"SURF", DEFAULT_FACTORY, null, null);
Feature2D extractor = createClassInstance(XFEATURES2D+"SURF", DEFAULT_FACTORY, null, null);
setProperty(detector, "hessianThreshold", "double", 8000);
setProperty(detector, "nOctaves", "int", 3);
setProperty(detector, "upright", "boolean", false);
detector.detect(img, keypoints);
extractor.compute(img, keypoints, descriptors);
return descriptors;
}
private Mat getQueryImg() {
Mat cross = new Mat(matSize, matSize, CvType.CV_8U, new Scalar(255));
Imgproc.line(cross, new Point(30, matSize / 2), new Point(matSize - 31, matSize / 2), new Scalar(100), 3);
Imgproc.line(cross, new Point(matSize / 2, 30), new Point(matSize / 2, matSize - 31), new Scalar(100), 3);
return cross;
}
private Mat getTrainDescriptors() {
Mat img = getTrainImg();
MatOfKeyPoint keypoints = new MatOfKeyPoint(new KeyPoint(50, 50, 16, 0, 20000, 1, -1), new KeyPoint(42, 42, 16, 160, 10000, 1, -1));
Mat descriptors = new Mat();
Feature2D extractor = createClassInstance(XFEATURES2D+"SURF", DEFAULT_FACTORY, null, null);
extractor.compute(img, keypoints, descriptors);
return descriptors;
}
private Mat getTrainImg() {
Mat cross = new Mat(matSize, matSize, CvType.CV_8U, new Scalar(255));
Imgproc.line(cross, new Point(20, matSize / 2), new Point(matSize - 21, matSize / 2), new Scalar(100), 2);
Imgproc.line(cross, new Point(matSize / 2, 20), new Point(matSize / 2, matSize - 21), new Scalar(100), 2);
return cross;
}
protected void setUp() throws Exception {
super.setUp();
matcher = DescriptorMatcher.create(DescriptorMatcher.FLANNBASED);
matSize = 100;
truth = new DMatch[] {
new DMatch(0, 0, 0, 0.6159003f),
new DMatch(1, 1, 0, 0.9177120f),
new DMatch(2, 1, 0, 0.3112163f),
new DMatch(3, 1, 0, 0.2925075f),
new DMatch(4, 1, 0, 0.26520672f)
};
}
// https://github.com/opencv/opencv/issues/11268
public void testConstructor()
{
FlannBasedMatcher self_created_matcher = new FlannBasedMatcher();
Mat train = new Mat(1, 1, CvType.CV_8U, new Scalar(123));
self_created_matcher.add(Arrays.asList(train));
assertTrue(!self_created_matcher.empty());
}
public void testAdd() {
matcher.add(Arrays.asList(new Mat()));
assertFalse(matcher.empty());
}
public void testClear() {
matcher.add(Arrays.asList(new Mat()));
matcher.clear();
assertTrue(matcher.empty());
}
public void testClone() {
Mat train = new Mat(1, 1, CvType.CV_8U, new Scalar(123));
matcher.add(Arrays.asList(train));
try {
matcher.clone();
fail("Expected CvException (cv::Error::StsNotImplemented)");
} catch (CvException cverr) {
// expected
}
}
public void testCloneBoolean() {
matcher.add(Arrays.asList(new Mat()));
DescriptorMatcher cloned = matcher.clone(true);
assertNotNull(cloned);
assertTrue(cloned.empty());
}
public void testCreate() {
assertNotNull(matcher);
}
public void testEmpty() {
assertTrue(matcher.empty());
}
public void testGetTrainDescriptors() {
Mat train = new Mat(1, 1, CvType.CV_8U, new Scalar(123));
Mat truth = train.clone();
matcher.add(Arrays.asList(train));
List<Mat> descriptors = matcher.getTrainDescriptors();
assertEquals(1, descriptors.size());
assertMatEqual(truth, descriptors.get(0));
}
public void testIsMaskSupported() {
assertFalse(matcher.isMaskSupported());
}
public void testKnnMatchMatListOfListOfDMatchInt() {
fail("Not yet implemented");
}
public void testKnnMatchMatListOfListOfDMatchIntListOfMat() {
fail("Not yet implemented");
}
public void testKnnMatchMatListOfListOfDMatchIntListOfMatBoolean() {
fail("Not yet implemented");
}
public void testKnnMatchMatMatListOfListOfDMatchInt() {
fail("Not yet implemented");
}
public void testKnnMatchMatMatListOfListOfDMatchIntMat() {
fail("Not yet implemented");
}
public void testKnnMatchMatMatListOfListOfDMatchIntMatBoolean() {
fail("Not yet implemented");
}
public void testMatchMatListOfDMatch() {
Mat train = getTrainDescriptors();
Mat query = getQueryDescriptors();
MatOfDMatch matches = new MatOfDMatch();
matcher.add(Arrays.asList(train));
matcher.train();
matcher.match(query, matches);
assertArrayDMatchEquals(truth, matches.toArray(), EPS);
}
public void testMatchMatListOfDMatchListOfMat() {
Mat train = getTrainDescriptors();
Mat query = getQueryDescriptors();
Mat mask = getMaskImg();
MatOfDMatch matches = new MatOfDMatch();
matcher.add(Arrays.asList(train));
matcher.train();
matcher.match(query, matches, Arrays.asList(mask));
assertArrayDMatchEquals(truth, matches.toArray(), EPS);
}
public void testMatchMatMatListOfDMatch() {
Mat train = getTrainDescriptors();
Mat query = getQueryDescriptors();
MatOfDMatch matches = new MatOfDMatch();
matcher.match(query, train, matches);
assertArrayDMatchEquals(truth, matches.toArray(), EPS);
// OpenCVTestRunner.Log(matches.toString());
// OpenCVTestRunner.Log(matches);
}
public void testMatchMatMatListOfDMatchMat() {
Mat train = getTrainDescriptors();
Mat query = getQueryDescriptors();
Mat mask = getMaskImg();
MatOfDMatch matches = new MatOfDMatch();
matcher.match(query, train, matches, mask);
assertListDMatchEquals(Arrays.asList(truth), matches.toList(), EPS);
}
public void testRadiusMatchMatListOfListOfDMatchFloat() {
fail("Not yet implemented");
}
public void testRadiusMatchMatListOfListOfDMatchFloatListOfMat() {
fail("Not yet implemented");
}
public void testRadiusMatchMatListOfListOfDMatchFloatListOfMatBoolean() {
fail("Not yet implemented");
}
public void testRadiusMatchMatMatListOfListOfDMatchFloat() {
fail("Not yet implemented");
}
public void testRadiusMatchMatMatListOfListOfDMatchFloatMat() {
fail("Not yet implemented");
}
public void testRadiusMatchMatMatListOfListOfDMatchFloatMatBoolean() {
fail("Not yet implemented");
}
public void testRead() {
String filenameR = OpenCVTestRunner.getTempFileName("yml");
String filenameW = OpenCVTestRunner.getTempFileName("yml");
writeFile(filenameR, ymlParamsModified);
matcher.read(filenameR);
matcher.write(filenameW);
assertEquals(ymlParamsModified, readFile(filenameW));
}
public void testTrain() {
Mat train = getTrainDescriptors();
matcher.add(Arrays.asList(train));
matcher.train();
}
public void testTrainNoData() {
try {
matcher.train();
fail("Expected CvException - FlannBasedMatcher::train should fail on empty train set");
} catch (CvException cverr) {
// expected
}
}
public void testWrite() {
String filename = OpenCVTestRunner.getTempFileName("xml");
matcher.write(filename);
assertEquals(xmlParamsDefault, readFile(filename));
}
public void testWriteYml() {
String filename = OpenCVTestRunner.getTempFileName("yml");
matcher.write(filename);
assertEquals(ymlParamsDefault, readFile(filename));
}
}
@@ -0,0 +1,67 @@
package org.opencv.test.features;
import org.opencv.test.OpenCVTestCase;
import org.opencv.test.OpenCVTestRunner;
import org.opencv.features.GFTTDetector;
public class GFTTFeatureDetectorTest extends OpenCVTestCase {
GFTTDetector detector;
@Override
protected void setUp() throws Exception {
super.setUp();
detector = GFTTDetector.create(); // default constructor have (1000, 0.01, 1, 3, 3, false, 0.04)
}
public void testCreate() {
assertNotNull(detector);
}
public void testDetectListOfMatListOfListOfKeyPoint() {
fail("Not yet implemented");
}
public void testDetectListOfMatListOfListOfKeyPointListOfMat() {
fail("Not yet implemented");
}
public void testDetectMatListOfKeyPoint() {
fail("Not yet implemented");
}
public void testDetectMatListOfKeyPointMat() {
fail("Not yet implemented");
}
public void testEmpty() {
fail("Not yet implemented");
}
public void testReadYml() {
String filename = OpenCVTestRunner.getTempFileName("yml");
writeFile(filename, "%YAML:1.0\n---\nname: \"Feature2D.GFTTDetector\"\nnfeatures: 500\nqualityLevel: 2.0000000000000000e-02\nminDistance: 2.\nblockSize: 4\ngradSize: 5\nuseHarrisDetector: 1\nk: 5.0000000000000000e-02\n");
detector.read(filename);
assertEquals(500, detector.getMaxFeatures());
assertEquals(0.02, detector.getQualityLevel());
assertEquals(2.0, detector.getMinDistance());
assertEquals(4, detector.getBlockSize());
assertEquals(5, detector.getGradientSize());
assertEquals(true, detector.getHarrisDetector());
assertEquals(0.05, detector.getK());
}
public void testWriteYml() {
String filename = OpenCVTestRunner.getTempFileName("yml");
detector.write(filename);
String truth = "%YAML 1.2\n---\nname: \"Feature2D.GFTTDetector\"\nnfeatures: 1000\nqualityLevel: 0.01\nminDistance: 1.\nblockSize: 3\ngradSize: 3\nuseHarrisDetector: false\nk: 0.040000000000000001\n";
String actual = readFile(filename);
actual = actual.replaceAll("e([+-])0(\\d\\d)", "e$1$2"); // NOTE: workaround for different platforms double representation
assertEquals(truth, actual);
}
}
@@ -0,0 +1,39 @@
package org.opencv.test.features;
import org.opencv.test.OpenCVTestCase;
public class HARRISFeatureDetectorTest extends OpenCVTestCase {
public void testCreate() {
fail("Not yet implemented");
}
public void testDetectListOfMatListOfListOfKeyPoint() {
fail("Not yet implemented");
}
public void testDetectListOfMatListOfListOfKeyPointListOfMat() {
fail("Not yet implemented");
}
public void testDetectMatListOfKeyPoint() {
fail("Not yet implemented");
}
public void testDetectMatListOfKeyPointMat() {
fail("Not yet implemented");
}
public void testEmpty() {
fail("Not yet implemented");
}
public void testRead() {
fail("Not yet implemented");
}
public void testWrite() {
fail("Not yet implemented");
}
}
@@ -0,0 +1,70 @@
package org.opencv.test.features;
import org.opencv.test.OpenCVTestCase;
import org.opencv.test.OpenCVTestRunner;
import org.opencv.features.MSER;
public class MSERFeatureDetectorTest extends OpenCVTestCase {
MSER detector;
@Override
protected void setUp() throws Exception {
super.setUp();
detector = MSER.create(); // default constructor have (5, 60, 14400, .25, .2, 200, 1.01, .003, 5)
}
public void testCreate() {
assertNotNull(detector);
}
public void testDetectListOfMatListOfListOfKeyPoint() {
fail("Not yet implemented");
}
public void testDetectListOfMatListOfListOfKeyPointListOfMat() {
fail("Not yet implemented");
}
public void testDetectMatListOfKeyPoint() {
fail("Not yet implemented");
}
public void testDetectMatListOfKeyPointMat() {
fail("Not yet implemented");
}
public void testEmpty() {
fail("Not yet implemented");
}
public void testReadYml() {
String filename = OpenCVTestRunner.getTempFileName("yml");
writeFile(filename, "%YAML:1.0\n---\nname: \"Feature2D.MSER\"\ndelta: 6\nminArea: 62\nmaxArea: 14402\nmaxVariation: .26\nminDiversity: .3\nmaxEvolution: 201\nareaThreshold: 1.02\nminMargin: 3.0e-3\nedgeBlurSize: 3\npass2Only: 1\n");
detector.read(filename);
assertEquals(6, detector.getDelta());
assertEquals(62, detector.getMinArea());
assertEquals(14402, detector.getMaxArea());
assertEquals(.26, detector.getMaxVariation());
assertEquals(.3, detector.getMinDiversity());
assertEquals(201, detector.getMaxEvolution());
assertEquals(1.02, detector.getAreaThreshold());
assertEquals(0.003, detector.getMinMargin());
assertEquals(3, detector.getEdgeBlurSize());
assertEquals(true, detector.getPass2Only());
}
public void testWriteYml() {
String filename = OpenCVTestRunner.getTempFileName("yml");
detector.write(filename);
String truth = "%YAML 1.2\n---\nname: \"Feature2D.MSER\"\ndelta: 5\nminArea: 60\nmaxArea: 14400\nmaxVariation: 0.25\nminDiversity: 0.20000000000000001\nmaxEvolution: 200\nareaThreshold: 1.01\nminMargin: 0.0030000000000000001\nedgeBlurSize: 5\npass2Only: false\n";
String actual = readFile(filename);
actual = actual.replaceAll("e([+-])0(\\d\\d)", "e$1$2"); // NOTE: workaround for different platforms double representation
assertEquals(truth, actual);
}
}
@@ -0,0 +1,121 @@
package org.opencv.test.features;
import org.opencv.core.Core;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.MatOfKeyPoint;
import org.opencv.core.Point;
import org.opencv.core.Scalar;
import org.opencv.core.KeyPoint;
import org.opencv.features.ORB;
import org.opencv.test.OpenCVTestCase;
import org.opencv.test.OpenCVTestRunner;
import org.opencv.imgproc.Imgproc;
public class ORBDescriptorExtractorTest extends OpenCVTestCase {
ORB extractor;
int matSize;
public static void assertDescriptorsClose(Mat expected, Mat actual, int allowedDistance) {
double distance = Core.norm(expected, actual, Core.NORM_HAMMING);
assertTrue("expected:<" + allowedDistance + "> but was:<" + distance + ">", distance <= allowedDistance);
}
private Mat getTestImg() {
Mat cross = new Mat(matSize, matSize, CvType.CV_8U, new Scalar(255));
Imgproc.line(cross, new Point(20, matSize / 2), new Point(matSize - 21, matSize / 2), new Scalar(100), 2);
Imgproc.line(cross, new Point(matSize / 2, 20), new Point(matSize / 2, matSize - 21), new Scalar(100), 2);
return cross;
}
@Override
protected void setUp() throws Exception {
super.setUp();
extractor = ORB.create();
matSize = 100;
}
public void testComputeListOfMatListOfListOfKeyPointListOfMat() {
fail("Not yet implemented");
}
public void testComputeMatListOfKeyPointMat() {
KeyPoint point = new KeyPoint(55.775577545166016f, 44.224422454833984f, 16, 9.754629f, 8617.863f, 1, -1);
MatOfKeyPoint keypoints = new MatOfKeyPoint(point);
Mat img = getTestImg();
Mat descriptors = new Mat();
extractor.compute(img, keypoints, descriptors);
Mat truth = new Mat(1, 32, CvType.CV_8UC1) {
{
put(0, 0,
6, 74, 6, 129, 2, 130, 56, 0, 44, 132, 66, 165, 172, 6, 3, 72, 102, 61, 171, 214, 0, 144, 65, 232, 4, 32, 138, 131, 4, 21, 37, 217);
}
};
assertDescriptorsClose(truth, descriptors, 1);
}
public void testCreate() {
assertNotNull(extractor);
}
public void testDescriptorSize() {
assertEquals(32, extractor.descriptorSize());
}
public void testDescriptorType() {
assertEquals(CvType.CV_8U, extractor.descriptorType());
}
public void testEmpty() {
// assertFalse(extractor.empty());
fail("Not yet implemented"); // ORB does not override empty() method
}
public void testReadYml() {
KeyPoint point = new KeyPoint(55.775577545166016f, 44.224422454833984f, 16, 9.754629f, 8617.863f, 1, -1);
MatOfKeyPoint keypoints = new MatOfKeyPoint(point);
Mat img = getTestImg();
Mat descriptors = new Mat();
String filename = OpenCVTestRunner.getTempFileName("yml");
writeFile(filename, "%YAML:1.0\n---\nnfeatures: 500\nscaleFactor: 1.1\nnlevels: 3\nedgeThreshold: 31\nfirstLevel: 0\nwta_k: 2\nscoreType: 0\npatchSize: 31\nfastThreshold: 20\n");
extractor.read(filename);
assertEquals(500, extractor.getMaxFeatures());
assertEquals(1.1, extractor.getScaleFactor());
assertEquals(3, extractor.getNLevels());
assertEquals(31, extractor.getEdgeThreshold());
assertEquals(0, extractor.getFirstLevel());
assertEquals(2, extractor.getWTA_K());
assertEquals(0, extractor.getScoreType());
assertEquals(31, extractor.getPatchSize());
assertEquals(20, extractor.getFastThreshold());
extractor.compute(img, keypoints, descriptors);
Mat truth = new Mat(1, 32, CvType.CV_8UC1) {
{
put(0, 0,
6, 10, 22, 5, 2, 130, 56, 0, 44, 164, 66, 165, 140, 6, 1, 72, 38, 61, 163, 210, 0, 208, 1, 104, 4, 32, 74, 131, 0, 37, 37, 67);
}
};
assertDescriptorsClose(truth, descriptors, 1);
}
public void testWriteYml() {
String filename = OpenCVTestRunner.getTempFileName("yml");
extractor.write(filename);
String truth = "%YAML 1.2\n---\nname: \"Feature2D.ORB\"\nnfeatures: 500\nscaleFactor: 1.2000000476837158\nnlevels: 8\nedgeThreshold: 31\nfirstLevel: 0\nwta_k: 2\nscoreType: 0\npatchSize: 31\nfastThreshold: 20\n";
// String truth = "%YAML:1.0\n---\n";
String actual = readFile(filename);
actual = actual.replaceAll("e\\+000", "e+00"); // NOTE: workaround for different platforms double representation
assertEquals(truth, actual);
}
}
@@ -0,0 +1,78 @@
package org.opencv.test.features;
import org.junit.Assert;
import org.opencv.core.CvType;
import org.opencv.core.KeyPoint;
import org.opencv.core.Mat;
import org.opencv.core.MatOfKeyPoint;
import org.opencv.core.Scalar;
import org.opencv.features.Features;
import org.opencv.features.ORB;
import org.opencv.test.OpenCVTestCase;
public class ORBFeatureDetectorTest extends OpenCVTestCase {
public void testCreate() {
fail("Not yet implemented");
}
public void testDetectListOfMatListOfListOfKeyPoint() {
fail("Not yet implemented");
}
public void testDetectListOfMatListOfListOfKeyPointListOfMat() {
fail("Not yet implemented");
}
public void testDetectMatListOfKeyPoint() {
fail("Not yet implemented");
}
public void testDetectMatListOfKeyPointMat() {
fail("Not yet implemented");
}
public void testEmpty() {
fail("Not yet implemented");
}
public void testRead() {
fail("Not yet implemented");
}
public void testWrite() {
fail("Not yet implemented");
}
public void testDetectTwoPoints() {
Mat img = new Mat(256,256, CvType.CV_8UC3, new Scalar(0,0,0));
img.put(35, 40, 255,255, 255);
img.put(152, 98, 200,0, 0);
MatOfKeyPoint keypoints = new MatOfKeyPoint();
ORB orb = ORB.create();
Mat descriptors = new Mat();
orb.detectAndCompute(img, new Mat(), keypoints, descriptors);
KeyPoint[] keypointsArray = keypoints.toArray();
assertEquals(2, keypointsArray.length);
long x1 = Math.round(keypointsArray[0].pt.x);
long y1 = Math.round(keypointsArray[0].pt.y);
long x2 = Math.round(keypointsArray[1].pt.x);
long y2 = Math.round(keypointsArray[1].pt.y);
if (x2 > x1) {
assertEquals(40, x1);
assertEquals(35, y1);
assertEquals(98, x2);
assertEquals(152, y2);
} else {
assertEquals(40, x2);
assertEquals(35, y2);
assertEquals(98, x1);
assertEquals(152, y1);
}
}
}
@@ -0,0 +1,109 @@
package org.opencv.test.features;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.MatOfKeyPoint;
import org.opencv.core.Point;
import org.opencv.core.Scalar;
import org.opencv.core.KeyPoint;
import org.opencv.features.SIFT;
import org.opencv.test.OpenCVTestCase;
import org.opencv.test.OpenCVTestRunner;
import org.opencv.imgproc.Imgproc;
import org.opencv.features.SIFT;
public class SIFTDescriptorExtractorTest extends OpenCVTestCase {
SIFT extractor;
KeyPoint keypoint;
int matSize;
Mat truth;
private Mat getTestImg() {
Mat cross = new Mat(matSize, matSize, CvType.CV_8U, new Scalar(255));
Imgproc.line(cross, new Point(20, matSize / 2), new Point(matSize - 21, matSize / 2), new Scalar(100), 2);
Imgproc.line(cross, new Point(matSize / 2, 20), new Point(matSize / 2, matSize - 21), new Scalar(100), 2);
return cross;
}
@Override
protected void setUp() throws Exception {
super.setUp();
extractor = SIFT.create();
keypoint = new KeyPoint(55.775577545166016f, 44.224422454833984f, 16, 9.754629f, 8617.863f, 1, -1);
matSize = 100;
truth = new Mat(1, 128, CvType.CV_32FC1) {
{
put(0, 0,
0, 0, 0, 1, 3, 0, 0, 0, 15, 23, 22, 20, 24, 2, 0, 0, 7, 8, 2, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 27, 16, 13, 2, 0, 0, 117,
86, 79, 68, 117, 42, 5, 5, 79, 60, 117, 25, 9, 2, 28, 19, 11, 13,
20, 2, 0, 0, 5, 8, 0, 0, 76, 58, 34, 31, 97, 16, 95, 49, 117, 92,
117, 112, 117, 76, 117, 54, 117, 25, 29, 22, 117, 117, 16, 11, 14,
1, 0, 0, 22, 26, 0, 0, 0, 0, 1, 4, 15, 2, 47, 8, 0, 0, 82, 56, 31,
17, 81, 12, 0, 0, 26, 23, 18, 23, 0, 0, 0, 0, 0, 0, 0, 0
);
}
};
}
public void testComputeListOfMatListOfListOfKeyPointListOfMat() {
fail("Not yet implemented");
}
public void testComputeMatListOfKeyPointMat() {
MatOfKeyPoint keypoints = new MatOfKeyPoint(keypoint);
Mat img = getTestImg();
Mat descriptors = new Mat();
extractor.compute(img, keypoints, descriptors);
assertMatEqual(truth, descriptors, EPS);
}
public void testCreate() {
assertNotNull(extractor);
}
public void testDescriptorSize() {
assertEquals(128, extractor.descriptorSize());
}
public void testDescriptorType() {
assertEquals(CvType.CV_32F, extractor.descriptorType());
}
public void testEmpty() {
// assertFalse(extractor.empty());
fail("Not yet implemented"); // SIFT does not override empty() method
}
public void testReadYml() {
String filename = OpenCVTestRunner.getTempFileName("yml");
writeFile(filename, "%YAML:1.0\n---\nname: \"Feature2D.SIFT\"\nnfeatures: 100\nnOctaveLayers: 4\ncontrastThreshold: 5.0000000000000001e-02\nedgeThreshold: 11\nsigma: 1.7\ndescriptorType: 5\n");
extractor.read(filename);
assertEquals(128, extractor.descriptorSize());
assertEquals(100, extractor.getNFeatures());
assertEquals(4, extractor.getNOctaveLayers());
assertEquals(0.05, extractor.getContrastThreshold());
assertEquals(11., extractor.getEdgeThreshold());
assertEquals(1.7, extractor.getSigma());
assertEquals(5, extractor.descriptorType());
}
public void testWriteYml() {
String filename = OpenCVTestRunner.getTempFileName("yml");
extractor.write(filename);
String truth = "%YAML 1.2\n---\nname: \"Feature2D.SIFT\"\nnfeatures: 0\nnOctaveLayers: 3\ncontrastThreshold: 0.040000000000000001\nedgeThreshold: 10.\nsigma: 1.6000000000000001\ndescriptorType: 5\n";
String actual = readFile(filename);
actual = actual.replaceAll("e([+-])0(\\d\\d)", "e$1$2"); // NOTE: workaround for different platforms double representation
assertEquals(truth, actual);
}
}
@@ -0,0 +1,39 @@
package org.opencv.test.features;
import org.opencv.test.OpenCVTestCase;
public class SIFTFeatureDetectorTest extends OpenCVTestCase {
public void testCreate() {
fail("Not yet implemented");
}
public void testDetectListOfMatListOfListOfKeyPoint() {
fail("Not yet implemented");
}
public void testDetectListOfMatListOfListOfKeyPointListOfMat() {
fail("Not yet implemented");
}
public void testDetectMatListOfKeyPoint() {
fail("Not yet implemented");
}
public void testDetectMatListOfKeyPointMat() {
fail("Not yet implemented");
}
public void testEmpty() {
fail("Not yet implemented");
}
public void testRead() {
fail("Not yet implemented");
}
public void testWrite() {
fail("Not yet implemented");
}
}
@@ -0,0 +1,138 @@
package org.opencv.test.features;
import java.util.Arrays;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.MatOfKeyPoint;
import org.opencv.core.Point;
import org.opencv.core.Scalar;
import org.opencv.core.KeyPoint;
import org.opencv.test.OpenCVTestCase;
import org.opencv.test.OpenCVTestRunner;
import org.opencv.imgproc.Imgproc;
import org.opencv.features.SimpleBlobDetector;
import org.opencv.features.SimpleBlobDetector_Params;
public class SIMPLEBLOBFeatureDetectorTest extends OpenCVTestCase {
SimpleBlobDetector detector;
int matSize;
KeyPoint[] truth;
private Mat getMaskImg() {
Mat mask = new Mat(matSize, matSize, CvType.CV_8U, new Scalar(255));
Mat right = mask.submat(0, matSize, matSize / 2, matSize);
right.setTo(new Scalar(0));
return mask;
}
private Mat getTestImg() {
int center = matSize / 2;
int offset = 40;
Mat img = new Mat(matSize, matSize, CvType.CV_8U, new Scalar(255));
Imgproc.circle(img, new Point(center - offset, center), 24, new Scalar(0), -1);
Imgproc.circle(img, new Point(center + offset, center), 20, new Scalar(50), -1);
Imgproc.circle(img, new Point(center, center - offset), 18, new Scalar(100), -1);
Imgproc.circle(img, new Point(center, center + offset), 14, new Scalar(150), -1);
Imgproc.circle(img, new Point(center, center), 10, new Scalar(200), -1);
return img;
}
@Override
protected void setUp() throws Exception {
super.setUp();
detector = SimpleBlobDetector.create();
matSize = 200;
truth = new KeyPoint[] {
new KeyPoint(140, 100, 41.036568f, -1, 0, 0, -1),
new KeyPoint(60, 100, 48.538486f, -1, 0, 0, -1),
new KeyPoint(100, 60, 36.769554f, -1, 0, 0, -1),
new KeyPoint(100, 140, 28.635643f, -1, 0, 0, -1),
new KeyPoint(100, 100, 20.880613f, -1, 0, 0, -1)
};
}
public void testCreate() {
assertNotNull(detector);
}
public void testDetectListOfMatListOfListOfKeyPoint() {
fail("Not yet implemented");
}
public void testDetectListOfMatListOfListOfKeyPointListOfMat() {
fail("Not yet implemented");
}
public void testDetectMatListOfKeyPoint() {
Mat img = getTestImg();
MatOfKeyPoint keypoints = new MatOfKeyPoint();
detector.detect(img, keypoints);
assertListKeyPointEquals(Arrays.asList(truth), keypoints.toList(), EPS);
}
public void testDetectMatListOfKeyPointMat() {
Mat img = getTestImg();
Mat mask = getMaskImg();
MatOfKeyPoint keypoints = new MatOfKeyPoint();
detector.detect(img, keypoints, mask);
assertListKeyPointEquals(Arrays.asList(truth[1]), keypoints.toList(), EPS);
}
public void testEmpty() {
// assertFalse(detector.empty());
fail("Not yet implemented");
}
public void testReadYml() {
Mat img = getTestImg();
MatOfKeyPoint keypoints1 = new MatOfKeyPoint();
detector.detect(img, keypoints1);
String filename = OpenCVTestRunner.getTempFileName("yml");
writeFile(filename, "%YAML:1.0\nthresholdStep: 10.0\nminThreshold: 50\nmaxThreshold: 220\nminRepeatability: 2\nminDistBetweenBlobs: 10.\nfilterByColor: 1\nblobColor: 0\nfilterByArea: 1\nminArea: 800\nmaxArea: 6000\nfilterByCircularity: 0\nminCircularity: 0.7\nmaxCircularity: 10.\nfilterByInertia: 1\nminInertiaRatio: 0.2\nmaxInertiaRatio: 11.\nfilterByConvexity: true\nminConvexity: 0.9\nmaxConvexity: 12.\n");
detector.read(filename);
SimpleBlobDetector_Params params = detector.getParams();
assertEquals(10.0f, params.get_thresholdStep());
assertEquals(50f, params.get_minThreshold());
assertEquals(220f, params.get_maxThreshold());
assertEquals(2, params.get_minRepeatability());
assertEquals(10.0f, params.get_minDistBetweenBlobs());
assertEquals(true, params.get_filterByColor());
assertEquals(0, params.get_blobColor());
assertEquals(true, params.get_filterByArea());
assertEquals(800f, params.get_minArea());
assertEquals(6000f, params.get_maxArea());
assertEquals(false, params.get_filterByCircularity());
assertEquals(0.7f, params.get_minCircularity());
assertEquals(10.0f, params.get_maxCircularity());
assertEquals(true, params.get_filterByInertia());
assertEquals(0.2f, params.get_minInertiaRatio());
assertEquals(11.0f, params.get_maxInertiaRatio());
assertEquals(true, params.get_filterByConvexity());
assertEquals(0.9f, params.get_minConvexity());
assertEquals(12.0f, params.get_maxConvexity());
MatOfKeyPoint keypoints2 = new MatOfKeyPoint();
detector.detect(img, keypoints2);
assertTrue(keypoints2.total() <= keypoints1.total());
}
public void testWrite() {
String filename = OpenCVTestRunner.getTempFileName("xml");
detector.write(filename);
String truth = "<?xml version=\"1.0\"?>\n<opencv_storage>\n<format>3</format>\n<thresholdStep>10.</thresholdStep>\n<minThreshold>50.</minThreshold>\n<maxThreshold>220.</maxThreshold>\n<minRepeatability>2</minRepeatability>\n<minDistBetweenBlobs>10.</minDistBetweenBlobs>\n<filterByColor>1</filterByColor>\n<blobColor>0</blobColor>\n<filterByArea>1</filterByArea>\n<minArea>25.</minArea>\n<maxArea>5000.</maxArea>\n<filterByCircularity>0</filterByCircularity>\n<minCircularity>0.80000001192092896</minCircularity>\n<maxCircularity>3.4028234663852886e+38</maxCircularity>\n<filterByInertia>1</filterByInertia>\n<minInertiaRatio>0.10000000149011612</minInertiaRatio>\n<maxInertiaRatio>3.4028234663852886e+38</maxInertiaRatio>\n<filterByConvexity>1</filterByConvexity>\n<minConvexity>0.94999998807907104</minConvexity>\n<maxConvexity>3.4028234663852886e+38</maxConvexity>\n<collectContours>0</collectContours>\n</opencv_storage>\n";
assertEquals(truth, readFile(filename));
}
}
+18
View File
@@ -0,0 +1,18 @@
{
"whitelist":
{
"": [
"goodFeaturesToTrack"
],
"Feature2D": ["detect", "compute", "detectAndCompute", "descriptorSize", "descriptorType", "defaultNorm", "empty", "getDefaultName"],
"ORB": ["create", "setMaxFeatures", "setScaleFactor", "setNLevels", "setEdgeThreshold", "setFastThreshold", "setFirstLevel", "setWTA_K", "setScoreType", "setPatchSize", "getFastThreshold", "getDefaultName"],
"MSER": ["create", "detectRegions", "setDelta", "getDelta", "setMinArea", "getMinArea", "setMaxArea", "getMaxArea", "setPass2Only", "getPass2Only", "getDefaultName"],
"FastFeatureDetector": ["create", "setThreshold", "getThreshold", "setNonmaxSuppression", "getNonmaxSuppression", "setType", "getType", "getDefaultName"],
"GFTTDetector": ["create", "setMaxFeatures", "getMaxFeatures", "setQualityLevel", "getQualityLevel", "setMinDistance", "getMinDistance", "setBlockSize", "getBlockSize", "setHarrisDetector", "getHarrisDetector", "setK", "getK", "getDefaultName"],
"SimpleBlobDetector": ["create", "setParams", "getParams", "getDefaultName"],
"SimpleBlobDetector_Params": [],
"DescriptorMatcher": ["add", "clear", "empty", "isMaskSupported", "train", "match", "knnMatch", "radiusMatch", "clone", "create"],
"BFMatcher": ["isMaskSupported", "create"],
"": ["drawKeypoints", "drawMatches", "drawMatchesKnn"]
}
}
+29
View File
@@ -0,0 +1,29 @@
{
"ManualFuncs" : {
"SimpleBlobDetector": {
"setParams": { "declaration" : [""], "implementation" : [""] },
"getParams": { "declaration" : [""], "implementation" : [""] }
}
},
"enum_fix" : {
"FastFeatureDetector" : { "DetectorType": "FastDetectorType" }
},
"func_arg_fix" : {
"goodFeaturesToTrack" : { "corners" : {"ctype" : "vector_Point"} },
"Feature2D": {
"(void)compute:(NSArray<Mat*>*)images keypoints:(NSMutableArray<NSMutableArray<KeyPoint*>*>*)keypoints descriptors:(NSMutableArray<Mat*>*)descriptors" : { "compute" : {"name" : "compute2"} },
"(void)detect:(NSArray<Mat*>*)images keypoints:(NSMutableArray<NSMutableArray<KeyPoint*>*>*)keypoints masks:(NSArray<Mat*>*)masks" : { "detect" : {"name" : "detect2"} }
},
"DescriptorMatcher": {
"(DescriptorMatcher*)create:(NSString*)descriptorMatcherType" : { "create" : {"name" : "create2"} }
},
"FlannBasedMatcher": {
"FlannBasedMatcher": { "indexParams" : {"defval" : "cv::makePtr<cv::flann::KDTreeIndexParams>()"}, "searchParams" : {"defval" : "cv::makePtr<cv::flann::SearchParams>()"} }
},
"BFMatcher": {
"BFMatcher" : { "normType" : {"ctype" : "NormTypes"} },
"(BFMatcher*)create:(int)normType crossCheck:(BOOL)crossCheck" : { "create" : {"name" : "createBFMatcher"},
"normType" : {"ctype" : "NormTypes"} }
}
}
}
@@ -0,0 +1,30 @@
//
// FeaturesTest.swift
//
// Created by Alexander Smorkalov on 2026/13/04.
//
import XCTest
import OpenCV
class ImgprocTest: OpenCVTestCase {
func testGoodFeaturesToTrackMatListOfPointIntDoubleDouble() {
let src = gray0
Imgproc.rectangle(img: src, pt1: Point(x: 2, y: 2), pt2: Point(x: 8, y: 8), color: Scalar(100), thickness: -1)
var lp = [Point]()
Imgproc.goodFeaturesToTrack(image: src, corners: &lp, maxCorners: 100, qualityLevel: 0.01, minDistance: 3)
XCTAssertEqual(4, lp.count)
}
func testGoodFeaturesToTrackMatListOfPointIntDoubleDoubleMatIntBooleanDouble() {
let src = gray0
Imgproc.rectangle(img: src, pt1: Point(x: 2, y: 2), pt2: Point(x: 8, y: 8), color: Scalar(100), thickness: -1)
var lp = [Point]()
Imgproc.goodFeaturesToTrack(image: src, corners: &lp, maxCorners: 100, qualityLevel: 0.01, minDistance: 3, mask: gray1, blockSize: 4, gradientSize: 3, useHarrisDetector: true, k: 0)
XCTAssertEqual(4, lp.count)
}
}
@@ -0,0 +1,6 @@
#ifdef HAVE_OPENCV_FEATURES
typedef SimpleBlobDetector::Params SimpleBlobDetector_Params;
typedef FastFeatureDetector::DetectorType FastFeatureDetector_DetectorType;
typedef DescriptorMatcher::MatcherType DescriptorMatcher_MatcherType;
typedef ORB::ScoreType ORB_ScoreType;
#endif
@@ -0,0 +1,164 @@
#!/usr/bin/env python
'''
Feature homography
==================
Example of using features framework for interactive video homography matching.
ORB features and FLANN matcher are used. The actual tracking is implemented by
PlaneTracker class in plane_tracker.py
'''
# Python 2/3 compatibility
from __future__ import print_function
import numpy as np
import cv2 as cv
import sys
PY3 = sys.version_info[0] == 3
if PY3:
xrange = range
# local modules
from tst_scene_render import TestSceneRender
def intersectionRate(s1, s2):
x1, y1, x2, y2 = s1
s1 = np.array([[x1, y1], [x2,y1], [x2, y2], [x1, y2]])
area, _intersection = cv.intersectConvexConvex(s1, np.array(s2))
return 2 * area / (cv.contourArea(s1) + cv.contourArea(np.array(s2)))
from tests_common import NewOpenCVTests
class feature_homography_test(NewOpenCVTests):
render = None
tracker = None
framesCounter = 0
frame = None
def test_feature_homography(self):
self.render = TestSceneRender(self.get_sample('samples/data/graf1.png'),
self.get_sample('samples/data/box.png'), noise = 0.5, speed = 0.5)
self.frame = self.render.getNextFrame()
self.tracker = PlaneTracker()
self.tracker.clear()
self.tracker.add_target(self.frame, self.render.getCurrentRect())
while self.framesCounter < 100:
self.framesCounter += 1
tracked = self.tracker.track(self.frame)
if len(tracked) > 0:
tracked = tracked[0]
self.assertGreater(intersectionRate(self.render.getCurrentRect(), np.int32(tracked.quad)), 0.6)
else:
self.assertEqual(0, 1, 'Tracking error')
self.frame = self.render.getNextFrame()
# built-in modules
from collections import namedtuple
FLANN_INDEX_KDTREE = 1
FLANN_INDEX_LSH = 6
flann_params= dict(algorithm = FLANN_INDEX_LSH,
table_number = 6, # 12
key_size = 12, # 20
multi_probe_level = 1) #2
MIN_MATCH_COUNT = 10
'''
image - image to track
rect - tracked rectangle (x1, y1, x2, y2)
keypoints - keypoints detected inside rect
descrs - their descriptors
data - some user-provided data
'''
PlanarTarget = namedtuple('PlaneTarget', 'image, rect, keypoints, descrs, data')
'''
target - reference to PlanarTarget
p0 - matched points coords in target image
p1 - matched points coords in input frame
H - homography matrix from p0 to p1
quad - target boundary quad in input frame
'''
TrackedTarget = namedtuple('TrackedTarget', 'target, p0, p1, H, quad')
class PlaneTracker:
def __init__(self):
self.detector = cv.ORB_create( nfeatures = 1000 )
self.matcher = cv.FlannBasedMatcher(flann_params, {}) # bug : need to pass empty dict (#1329)
self.targets = []
self.frame_points = []
def add_target(self, image, rect, data=None):
'''Add a new tracking target.'''
x0, y0, x1, y1 = rect
raw_points, raw_descrs = self.detect_features(image)
points, descs = [], []
for kp, desc in zip(raw_points, raw_descrs):
x, y = kp.pt
if x0 <= x <= x1 and y0 <= y <= y1:
points.append(kp)
descs.append(desc)
descs = np.uint8(descs)
self.matcher.add([descs])
target = PlanarTarget(image = image, rect=rect, keypoints = points, descrs=descs, data=data)
self.targets.append(target)
def clear(self):
'''Remove all targets'''
self.targets = []
self.matcher.clear()
def track(self, frame):
'''Returns a list of detected TrackedTarget objects'''
self.frame_points, frame_descrs = self.detect_features(frame)
if len(self.frame_points) < MIN_MATCH_COUNT:
return []
matches = self.matcher.knnMatch(frame_descrs, k = 2)
matches = [m[0] for m in matches if len(m) == 2 and m[0].distance < m[1].distance * 0.75]
if len(matches) < MIN_MATCH_COUNT:
return []
matches_by_id = [[] for _ in xrange(len(self.targets))]
for m in matches:
matches_by_id[m.imgIdx].append(m)
tracked = []
for imgIdx, matches in enumerate(matches_by_id):
if len(matches) < MIN_MATCH_COUNT:
continue
target = self.targets[imgIdx]
p0 = [target.keypoints[m.trainIdx].pt for m in matches]
p1 = [self.frame_points[m.queryIdx].pt for m in matches]
p0, p1 = np.float32((p0, p1))
H, status = cv.findHomography(p0, p1, cv.RANSAC, 3.0)
status = status.ravel() != 0
if status.sum() < MIN_MATCH_COUNT:
continue
p0, p1 = p0[status], p1[status]
x0, y0, x1, y1 = target.rect
quad = np.float32([[x0, y0], [x1, y0], [x1, y1], [x0, y1]])
quad = cv.perspectiveTransform(quad.reshape(1, -1, 2), H).reshape(-1, 2)
track = TrackedTarget(target=target, p0=p0, p1=p1, H=H, quad=quad)
tracked.append(track)
tracked.sort(key = lambda t: len(t.p0), reverse=True)
return tracked
def detect_features(self, frame):
'''detect_features(self, frame) -> keypoints, descrs'''
keypoints, descrs = self.detector.detectAndCompute(frame, None)
if descrs is None: # detectAndCompute returns descs=None if no keypoints found
descrs = []
return keypoints, descrs
if __name__ == '__main__':
NewOpenCVTests.bootstrap()
@@ -0,0 +1,150 @@
/*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) 2010-2012, Multicoreware, Inc., all rights reserved.
// Copyright (C) 2010-2012, Advanced Micro Devices, Inc., all rights reserved.
// Third party copyrights are property of their respective owners.
//
// @Authors
// Fangfang Bai, fangfang@multicorewareinc.com
// Jin Ma, jin@multicorewareinc.com
//
// 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 "../perf_precomp.hpp"
#include "opencv2/ts/ocl_perf.hpp"
#ifdef HAVE_OPENCL
namespace opencv_test {
namespace ocl {
//////////////////// BruteForceMatch /////////////////
typedef Size_MatType BruteForceMatcherFixture;
OCL_PERF_TEST_P(BruteForceMatcherFixture, Match, ::testing::Combine(OCL_PERF_ENUM(OCL_SIZE_1, OCL_SIZE_2, OCL_SIZE_3), OCL_PERF_ENUM((MatType)CV_32FC1) ) )
{
const Size_MatType_t params = GetParam();
const Size srcSize = get<0>(params);
const int type = get<1>(params);
checkDeviceMaxMemoryAllocSize(srcSize, type);
vector<DMatch> matches;
UMat uquery(srcSize, type), utrain(srcSize, type);
declare.in(uquery, utrain, WARMUP_RNG);
BFMatcher matcher(NORM_L2);
OCL_TEST_CYCLE()
matcher.match(uquery, utrain, matches);
SANITY_CHECK_MATCHES(matches, 1e-3);
}
OCL_PERF_TEST_P(BruteForceMatcherFixture, KnnMatch, ::testing::Combine(OCL_PERF_ENUM(OCL_SIZE_1, OCL_SIZE_2, OCL_SIZE_3), OCL_PERF_ENUM((MatType)CV_32FC1) ) )
{
const Size_MatType_t params = GetParam();
const Size srcSize = get<0>(params);
const int type = get<1>(params);
checkDeviceMaxMemoryAllocSize(srcSize, type);
vector< vector<DMatch> > matches;
UMat uquery(srcSize, type), utrain(srcSize, type);
declare.in(uquery, utrain, WARMUP_RNG);
BFMatcher matcher(NORM_L2);
OCL_TEST_CYCLE()
matcher.knnMatch(uquery, utrain, matches, 2);
vector<DMatch> & matches0 = matches[0], & matches1 = matches[1];
SANITY_CHECK_MATCHES(matches0, 1e-3);
SANITY_CHECK_MATCHES(matches1, 1e-3);
}
OCL_PERF_TEST_P(BruteForceMatcherFixture, RadiusMatch, ::testing::Combine(OCL_PERF_ENUM(OCL_SIZE_1, OCL_SIZE_2, OCL_SIZE_3), OCL_PERF_ENUM((MatType)CV_32FC1) ) )
{
const Size_MatType_t params = GetParam();
const Size srcSize = get<0>(params);
const int type = get<1>(params);
checkDeviceMaxMemoryAllocSize(srcSize, type);
vector< vector<DMatch> > matches;
UMat uquery(srcSize, type), utrain(srcSize, type);
declare.in(uquery, utrain, WARMUP_RNG);
BFMatcher matcher(NORM_L2);
OCL_TEST_CYCLE()
matcher.radiusMatch(uquery, utrain, matches, 2.0f);
vector<DMatch> & matches0 = matches[0], & matches1 = matches[1];
SANITY_CHECK_MATCHES(matches0, 1e-3);
SANITY_CHECK_MATCHES(matches1, 1e-3);
}
OCL_PERF_TEST_P(BruteForceMatcherFixture, MatchCrossCheck, ::testing::Combine(OCL_PERF_ENUM(OCL_SIZE_1, OCL_SIZE_2, OCL_SIZE_3), OCL_PERF_ENUM((MatType)CV_32FC1) ) )
{
const Size_MatType_t params = GetParam();
const Size srcSize = get<0>(params);
const int type = get<1>(params);
checkDeviceMaxMemoryAllocSize(srcSize, type);
vector<DMatch> matches;
UMat uquery(srcSize, type), utrain(srcSize, type);
declare.in(uquery, utrain, WARMUP_RNG);
BFMatcher matcher(NORM_L2, true /*crossCheck*/);
OCL_TEST_CYCLE()
matcher.match(uquery, utrain, matches);
SANITY_CHECK_MATCHES(matches, 1e-3);
}
} // ocl
} // cvtest
#endif // HAVE_OPENCL
@@ -0,0 +1,81 @@
#include "../perf_precomp.hpp"
#include "opencv2/ts/ocl_perf.hpp"
#include "../perf_feature2d.hpp"
#ifdef HAVE_OPENCL
namespace opencv_test {
namespace ocl {
OCL_PERF_TEST_P(feature2d, detect, testing::Combine(Feature2DType::all(), TEST_IMAGES))
{
Ptr<Feature2D> detector = getFeature2D(get<0>(GetParam()));
std::string filename = getDataPath(get<1>(GetParam()));
Mat mimg = imread(filename, IMREAD_GRAYSCALE);
ASSERT_FALSE(mimg.empty());
ASSERT_TRUE(detector);
UMat img, mask;
mimg.copyTo(img);
declare.in(img);
vector<KeyPoint> points;
OCL_TEST_CYCLE() detector->detect(img, points, mask);
EXPECT_GT(points.size(), 20u);
SANITY_CHECK_NOTHING();
}
OCL_PERF_TEST_P(feature2d, extract, testing::Combine(testing::Values(DETECTORS_EXTRACTORS), TEST_IMAGES))
{
Ptr<Feature2D> detector = ORB::create();
Ptr<Feature2D> extractor = getFeature2D(get<0>(GetParam()));
std::string filename = getDataPath(get<1>(GetParam()));
Mat mimg = imread(filename, IMREAD_GRAYSCALE);
ASSERT_FALSE(mimg.empty());
ASSERT_TRUE(extractor);
UMat img, mask;
mimg.copyTo(img);
declare.in(img);
vector<KeyPoint> points;
detector->detect(img, points, mask);
EXPECT_GT(points.size(), 20u);
UMat descriptors;
OCL_TEST_CYCLE() extractor->compute(img, points, descriptors);
EXPECT_EQ((size_t)descriptors.rows, points.size());
SANITY_CHECK_NOTHING();
}
OCL_PERF_TEST_P(feature2d, detectAndExtract, testing::Combine(testing::Values(DETECTORS_EXTRACTORS), TEST_IMAGES))
{
Ptr<Feature2D> detector = getFeature2D(get<0>(GetParam()));
std::string filename = getDataPath(get<1>(GetParam()));
Mat mimg = imread(filename, IMREAD_GRAYSCALE);
ASSERT_FALSE(mimg.empty());
ASSERT_TRUE(detector);
UMat img, mask;
mimg.copyTo(img);
declare.in(img);
vector<KeyPoint> points;
UMat descriptors;
OCL_TEST_CYCLE() detector->detectAndCompute(img, mask, points, descriptors, false);
EXPECT_GT(points.size(), 20u);
EXPECT_EQ((size_t)descriptors.rows, points.size());
SANITY_CHECK_NOTHING();
}
} // ocl
} // cvtest
#endif // HAVE_OPENCL
+116
View File
@@ -0,0 +1,116 @@
///////////////////////////////////////////////////////////////////////////////////////
//
// 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) 2010-2012, Multicoreware, Inc., all rights reserved.
// Copyright (C) 2010-2012, Institute Of Software Chinese Academy Of Science, all rights reserved.
// Copyright (C) 2010-2012, Advanced Micro Devices, 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 "../perf_precomp.hpp"
#include "opencv2/ts/ocl_perf.hpp"
#include <sstream>
#ifdef HAVE_OPENCL
namespace opencv_test {
namespace ocl {
//////////////////////////// GoodFeaturesToTrack //////////////////////////
typedef tuple<String, double, bool> GoodFeaturesToTrackParams;
typedef TestBaseWithParam<GoodFeaturesToTrackParams> GoodFeaturesToTrackFixture;
OCL_PERF_TEST_P(GoodFeaturesToTrackFixture, GoodFeaturesToTrack,
::testing::Combine(OCL_PERF_ENUM(String("gpu/opticalflow/rubberwhale1.png")),
OCL_PERF_ENUM(0.0, 3.0), Bool()))
{
GoodFeaturesToTrackParams params = GetParam();
const String fileName = get<0>(params);
const double minDistance = get<1>(params), qualityLevel = 0.01;
const bool harrisDetector = get<2>(params);
const int maxCorners = 1000;
Mat img = imread(getDataPath(fileName), cv::IMREAD_GRAYSCALE);
ASSERT_FALSE(img.empty()) << "could not load " << fileName;
checkDeviceMaxMemoryAllocSize(img.size(), img.type());
UMat src(img.size(), img.type()), dst(1, maxCorners, CV_32FC2);
img.copyTo(src);
declare.in(src, WARMUP_READ).out(dst);
OCL_TEST_CYCLE() cv::goodFeaturesToTrack(src, dst, maxCorners, qualityLevel,
minDistance, noArray(), 3, 3, harrisDetector, 0.04);
SANITY_CHECK(dst);
}
OCL_PERF_TEST_P(GoodFeaturesToTrackFixture, GoodFeaturesToTrackWithQuality,
::testing::Combine(OCL_PERF_ENUM(String("gpu/opticalflow/rubberwhale1.png")),
OCL_PERF_ENUM(3.0), Bool()))
{
GoodFeaturesToTrackParams params = GetParam();
const String fileName = get<0>(params);
const double minDistance = get<1>(params), qualityLevel = 0.01;
const bool harrisDetector = get<2>(params);
const int maxCorners = 1000;
Mat img = imread(getDataPath(fileName), cv::IMREAD_GRAYSCALE);
ASSERT_FALSE(img.empty()) << "could not load " << fileName;
checkDeviceMaxMemoryAllocSize(img.size(), img.type());
UMat src(img.size(), img.type()), dst(1, maxCorners, CV_32FC2);
img.copyTo(src);
std::vector<float> cornersQuality;
declare.in(src, WARMUP_READ).out(dst);
OCL_TEST_CYCLE() cv::goodFeaturesToTrack(src, dst, maxCorners, qualityLevel, minDistance,
noArray(), cornersQuality, 3, 3, harrisDetector, 0.04);
SANITY_CHECK(dst);
SANITY_CHECK(cornersQuality, 1e-6);
}
} } // namespace opencv_test::ocl
#endif
@@ -0,0 +1,167 @@
#include "perf_precomp.hpp"
namespace opencv_test
{
using namespace perf;
CV_ENUM(NormType, NORM_L1, NORM_L2, NORM_L2SQR, NORM_HAMMING, NORM_HAMMING2)
typedef tuple<NormType, MatType, bool> Norm_Destination_CrossCheck_t;
typedef perf::TestBaseWithParam<Norm_Destination_CrossCheck_t> Norm_Destination_CrossCheck;
typedef tuple<NormType, bool> Norm_CrossCheck_t;
typedef perf::TestBaseWithParam<Norm_CrossCheck_t> Norm_CrossCheck;
typedef tuple<MatType, bool> Source_CrossCheck_t;
typedef perf::TestBaseWithParam<Source_CrossCheck_t> Source_CrossCheck;
void generateData( Mat& query, Mat& train, const int sourceType );
PERF_TEST_P(Norm_Destination_CrossCheck, batchDistance_8U,
testing::Combine(testing::Values((int)NORM_L1, (int)NORM_L2SQR),
testing::Values(CV_32S, CV_32F),
testing::Bool()
)
)
{
NormType normType = get<0>(GetParam());
int destinationType = get<1>(GetParam());
bool isCrossCheck = get<2>(GetParam());
int knn = isCrossCheck ? 1 : 0;
Mat queryDescriptors;
Mat trainDescriptors;
Mat dist;
Mat ndix;
generateData(queryDescriptors, trainDescriptors, CV_8U);
TEST_CYCLE()
{
batchDistance(queryDescriptors, trainDescriptors, dist, destinationType, (isCrossCheck) ? ndix : noArray(),
normType, knn, Mat(), 0, isCrossCheck);
}
SANITY_CHECK(dist);
if (isCrossCheck) SANITY_CHECK(ndix);
}
PERF_TEST_P(Norm_CrossCheck, batchDistance_Dest_32S,
testing::Combine(testing::Values((int)NORM_HAMMING, (int)NORM_HAMMING2),
testing::Bool()
)
)
{
NormType normType = get<0>(GetParam());
bool isCrossCheck = get<1>(GetParam());
int knn = isCrossCheck ? 1 : 0;
Mat queryDescriptors;
Mat trainDescriptors;
Mat dist;
Mat ndix;
generateData(queryDescriptors, trainDescriptors, CV_8U);
TEST_CYCLE()
{
batchDistance(queryDescriptors, trainDescriptors, dist, CV_32S, (isCrossCheck) ? ndix : noArray(),
normType, knn, Mat(), 0, isCrossCheck);
}
SANITY_CHECK(dist);
if (isCrossCheck) SANITY_CHECK(ndix);
}
PERF_TEST_P(Source_CrossCheck, batchDistance_L2,
testing::Combine(testing::Values(CV_8U, CV_32F),
testing::Bool()
)
)
{
int sourceType = get<0>(GetParam());
bool isCrossCheck = get<1>(GetParam());
int knn = isCrossCheck ? 1 : 0;
Mat queryDescriptors;
Mat trainDescriptors;
Mat dist;
Mat ndix;
generateData(queryDescriptors, trainDescriptors, sourceType);
declare.time(50);
TEST_CYCLE()
{
batchDistance(queryDescriptors, trainDescriptors, dist, CV_32F, (isCrossCheck) ? ndix : noArray(),
NORM_L2, knn, Mat(), 0, isCrossCheck);
}
SANITY_CHECK(dist);
if (isCrossCheck) SANITY_CHECK(ndix);
}
PERF_TEST_P(Norm_CrossCheck, batchDistance_32F,
testing::Combine(testing::Values((int)NORM_L1, (int)NORM_L2SQR),
testing::Bool()
)
)
{
NormType normType = get<0>(GetParam());
bool isCrossCheck = get<1>(GetParam());
int knn = isCrossCheck ? 1 : 0;
Mat queryDescriptors;
Mat trainDescriptors;
Mat dist;
Mat ndix;
generateData(queryDescriptors, trainDescriptors, CV_32F);
declare.time(100);
TEST_CYCLE()
{
batchDistance(queryDescriptors, trainDescriptors, dist, CV_32F, (isCrossCheck) ? ndix : noArray(),
normType, knn, Mat(), 0, isCrossCheck);
}
SANITY_CHECK(dist, 1e-4);
if (isCrossCheck) SANITY_CHECK(ndix);
}
void generateData( Mat& query, Mat& train, const int sourceType )
{
const int dim = 500;
const int queryDescCount = 300; // must be even number because we split train data in some cases in two
const int countFactor = 4; // do not change it
RNG& rng = theRNG();
// Generate query descriptors randomly.
// Descriptor vector elements are integer values.
Mat buf( queryDescCount, dim, CV_32SC1 );
rng.fill( buf, RNG::UNIFORM, Scalar::all(0), Scalar(3) );
buf.convertTo( query, sourceType );
// Generate train descriptors as follows:
// copy each query descriptor to train set countFactor times
// and perturb some one element of the copied descriptors in
// in ascending order. General boundaries of the perturbation
// are (0.f, 1.f).
train.create( query.rows*countFactor, query.cols, sourceType );
float step = (sourceType == CV_8U ? 256.f : 1.f) / countFactor;
for( int qIdx = 0; qIdx < query.rows; qIdx++ )
{
Mat queryDescriptor = query.row(qIdx);
for( int c = 0; c < countFactor; c++ )
{
int tIdx = qIdx * countFactor + c;
Mat trainDescriptor = train.row(tIdx);
queryDescriptor.copyTo( trainDescriptor );
int elem = rng(dim);
float diff = rng.uniform( step*c, step*(c+1) );
trainDescriptor.col(elem) += diff;
}
}
}
} // namespace
+43
View File
@@ -0,0 +1,43 @@
#include "perf_precomp.hpp"
#include "perf_feature2d.hpp"
namespace opencv_test
{
using namespace perf;
typedef tuple<int, int, bool, string> Fast_Params_t;
typedef perf::TestBaseWithParam<Fast_Params_t> Fast_Params;
PERF_TEST_P(Fast_Params, detect,
testing::Combine(
testing::Values(20,30,100), // threshold
testing::Values(
// (int)FastFeatureDetector::TYPE_5_8,
// (int)FastFeatureDetector::TYPE_7_12,
(int)FastFeatureDetector::TYPE_9_16 // detector_type
),
testing::Bool(), // nonmaxSuppression
testing::Values("cv/inpaint/orig.png",
"cv/cameracalibration/chess9.png")
))
{
int threshold_p = get<0>(GetParam());
int type_p = get<1>(GetParam());
bool nonmaxSuppression_p = get<2>(GetParam());
string filename = getDataPath(get<3>(GetParam()));
Mat img = imread(filename, IMREAD_GRAYSCALE);
ASSERT_FALSE(img.empty()) << "Failed to load image: " << filename;
vector<KeyPoint> keypoints;
declare.in(img);
TEST_CYCLE()
{
FAST(img, keypoints, threshold_p, nonmaxSuppression_p, (FastFeatureDetector::DetectorType)type_p);
}
SANITY_CHECK_NOTHING();
}
} // namespace opencv_test
+72
View File
@@ -0,0 +1,72 @@
#include "perf_feature2d.hpp"
namespace opencv_test
{
using namespace perf;
PERF_TEST_P(feature2d, detect, testing::Combine(Feature2DType::all(), TEST_IMAGES))
{
Ptr<Feature2D> detector = getFeature2D(get<0>(GetParam()));
std::string filename = getDataPath(get<1>(GetParam()));
Mat img = imread(filename, IMREAD_GRAYSCALE);
ASSERT_FALSE(img.empty());
ASSERT_TRUE(detector);
declare.in(img);
Mat mask;
vector<KeyPoint> points;
TEST_CYCLE() detector->detect(img, points, mask);
EXPECT_GT(points.size(), 20u);
SANITY_CHECK_NOTHING();
}
PERF_TEST_P(feature2d, extract, testing::Combine(testing::Values(DETECTORS_EXTRACTORS), TEST_IMAGES))
{
Ptr<Feature2D> detector = ORB::create();
Ptr<Feature2D> extractor = getFeature2D(get<0>(GetParam()));
std::string filename = getDataPath(get<1>(GetParam()));
Mat img = imread(filename, IMREAD_GRAYSCALE);
ASSERT_FALSE(img.empty());
ASSERT_TRUE(extractor);
declare.in(img);
Mat mask;
vector<KeyPoint> points;
detector->detect(img, points, mask);
EXPECT_GT(points.size(), 20u);
Mat descriptors;
TEST_CYCLE() extractor->compute(img, points, descriptors);
EXPECT_EQ((size_t)descriptors.rows, points.size());
SANITY_CHECK_NOTHING();
}
PERF_TEST_P(feature2d, detectAndExtract, testing::Combine(testing::Values(DETECTORS_EXTRACTORS), TEST_IMAGES))
{
Ptr<Feature2D> detector = getFeature2D(get<0>(GetParam()));
std::string filename = getDataPath(get<1>(GetParam()));
Mat img = imread(filename, IMREAD_GRAYSCALE);
ASSERT_FALSE(img.empty());
ASSERT_TRUE(detector);
declare.in(img);
Mat mask;
vector<KeyPoint> points;
Mat descriptors;
TEST_CYCLE() detector->detectAndCompute(img, mask, points, descriptors, false);
EXPECT_GT(points.size(), 20u);
EXPECT_EQ((size_t)descriptors.rows, points.size());
SANITY_CHECK_NOTHING();
}
} // namespace
+67
View File
@@ -0,0 +1,67 @@
#ifndef __OPENCV_PERF_FEATURES_HPP__
#define __OPENCV_PERF_FEATURES_HPP__
#include "perf_precomp.hpp"
namespace opencv_test
{
/* configuration for tests of detectors/descriptors. shared between ocl and cpu tests. */
// detectors/descriptors configurations to test
#define DETECTORS_ONLY \
FAST_DEFAULT, FAST_20_TRUE_TYPE5_8, FAST_20_TRUE_TYPE7_12, FAST_20_TRUE_TYPE9_16, \
FAST_20_FALSE_TYPE5_8, FAST_20_FALSE_TYPE7_12, FAST_20_FALSE_TYPE9_16, \
\
MSER_DEFAULT
#define DETECTORS_EXTRACTORS \
ORB_DEFAULT, ORB_1500_13_1, \
SIFT_DEFAULT
#define CV_ENUM_EXPAND(name, ...) CV_ENUM(name, __VA_ARGS__)
enum Feature2DVals { DETECTORS_ONLY, DETECTORS_EXTRACTORS };
CV_ENUM_EXPAND(Feature2DType, DETECTORS_ONLY, DETECTORS_EXTRACTORS)
typedef tuple<Feature2DType, string> Feature2DType_String_t;
typedef perf::TestBaseWithParam<Feature2DType_String_t> feature2d;
#define TEST_IMAGES testing::Values(\
"cv/detectors_descriptors_evaluation/images_datasets/leuven/img1.png",\
"stitching/a3.png", \
"stitching/s2.jpg")
static inline Ptr<Feature2D> getFeature2D(Feature2DType type)
{
switch(type) {
case ORB_DEFAULT:
return ORB::create();
case ORB_1500_13_1:
return ORB::create(1500, 1.3f, 1);
case FAST_DEFAULT:
return FastFeatureDetector::create();
case FAST_20_TRUE_TYPE5_8:
return FastFeatureDetector::create(20, true, FastFeatureDetector::TYPE_5_8);
case FAST_20_TRUE_TYPE7_12:
return FastFeatureDetector::create(20, true, FastFeatureDetector::TYPE_7_12);
case FAST_20_TRUE_TYPE9_16:
return FastFeatureDetector::create(20, true, FastFeatureDetector::TYPE_9_16);
case FAST_20_FALSE_TYPE5_8:
return FastFeatureDetector::create(20, false, FastFeatureDetector::TYPE_5_8);
case FAST_20_FALSE_TYPE7_12:
return FastFeatureDetector::create(20, false, FastFeatureDetector::TYPE_7_12);
case FAST_20_FALSE_TYPE9_16:
return FastFeatureDetector::create(20, false, FastFeatureDetector::TYPE_9_16);
case MSER_DEFAULT:
return MSER::create();
case SIFT_DEFAULT:
return SIFT::create();
default:
return Ptr<Feature2D>();
}
}
} // namespace
#endif // __OPENCV_PERF_FEATURES_HPP__
@@ -0,0 +1,77 @@
// 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 {
typedef tuple<string, int, double, int, int, bool> Image_MaxCorners_QualityLevel_MinDistance_BlockSize_gradientSize_UseHarris_t;
typedef perf::TestBaseWithParam<Image_MaxCorners_QualityLevel_MinDistance_BlockSize_gradientSize_UseHarris_t> Image_MaxCorners_QualityLevel_MinDistance_BlockSize_gradientSize_UseHarris;
PERF_TEST_P(Image_MaxCorners_QualityLevel_MinDistance_BlockSize_gradientSize_UseHarris, goodFeaturesToTrack,
testing::Combine(
testing::Values( "stitching/a1.png", "cv/shared/pic5.png"),
testing::Values( 100, 500 ),
testing::Values( 0.1, 0.01 ),
testing::Values( 3, 5 ),
testing::Values( 3, 5 ),
testing::Bool()
)
)
{
string filename = getDataPath(get<0>(GetParam()));
int maxCorners = get<1>(GetParam());
double qualityLevel = get<2>(GetParam());
int blockSize = get<3>(GetParam());
int gradientSize = get<4>(GetParam());
bool useHarrisDetector = get<5>(GetParam());
Mat image = imread(filename, IMREAD_GRAYSCALE);
if (image.empty())
FAIL() << "Unable to load source image" << filename;
std::vector<Point2f> corners;
double minDistance = 1;
TEST_CYCLE() goodFeaturesToTrack(image, corners, maxCorners, qualityLevel, minDistance, noArray(), blockSize, gradientSize, useHarrisDetector);
if (corners.size() > 50)
corners.erase(corners.begin() + 50, corners.end());
SANITY_CHECK(corners);
}
PERF_TEST_P(Image_MaxCorners_QualityLevel_MinDistance_BlockSize_gradientSize_UseHarris, goodFeaturesToTrackWithQuality,
testing::Combine(
testing::Values( "stitching/a1.png", "cv/shared/pic5.png"),
testing::Values( 50 ),
testing::Values( 0.01 ),
testing::Values( 3 ),
testing::Values( 3 ),
testing::Bool()
)
)
{
string filename = getDataPath(get<0>(GetParam()));
int maxCorners = get<1>(GetParam());
double qualityLevel = get<2>(GetParam());
int blockSize = get<3>(GetParam());
int gradientSize = get<4>(GetParam());
bool useHarrisDetector = get<5>(GetParam());
double minDistance = 1;
Mat image = imread(filename, IMREAD_GRAYSCALE);
if (image.empty())
FAIL() << "Unable to load source image" << filename;
std::vector<Point2f> corners;
std::vector<float> cornersQuality;
TEST_CYCLE() goodFeaturesToTrack(image, corners, maxCorners, qualityLevel, minDistance, noArray(),
cornersQuality, blockSize, gradientSize, useHarrisDetector);
SANITY_CHECK(corners);
SANITY_CHECK(cornersQuality, 1e-6);
}
} // namespace
+7
View File
@@ -0,0 +1,7 @@
#include "perf_precomp.hpp"
#if defined(HAVE_HPX)
#include <hpx/hpx_main.hpp>
#endif
CV_PERF_TEST_MAIN(features2d)
+7
View File
@@ -0,0 +1,7 @@
#ifndef __OPENCV_PERF_PRECOMP_HPP__
#define __OPENCV_PERF_PRECOMP_HPP__
#include "opencv2/ts.hpp"
#include "opencv2/features.hpp"
#endif
+361
View File
@@ -0,0 +1,361 @@
// 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 code issued with the following license.
/*********************************************************************
* Software License Agreement (BSD License)
*
* Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
* Copyright (C) 2008-2013, Willow Garage Inc., all rights reserved.
* Copyright (C) 2013, Evgeny Toropov, 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.
* * 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
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*********************************************************************/
/*
Guoshen Yu, Jean-Michel Morel, ASIFT: An Algorithm for Fully Affine
Invariant Comparison, Image Processing On Line, 1 (2011), pp. 11-38.
https://doi.org/10.5201/ipol.2011.my-asift
*/
#include "precomp.hpp"
#include <iostream>
namespace cv {
class AffineFeature_Impl CV_FINAL : public AffineFeature
{
public:
explicit AffineFeature_Impl(const Ptr<Feature2D>& backend,
int maxTilt, int minTilt, float tiltStep, float rotateStepBase);
int descriptorSize() const CV_OVERRIDE
{
return backend_->descriptorSize();
}
int descriptorType() const CV_OVERRIDE
{
return backend_->descriptorType();
}
int defaultNorm() const CV_OVERRIDE
{
return backend_->defaultNorm();
}
void detectAndCompute(InputArray image, InputArray mask, std::vector<KeyPoint>& keypoints,
OutputArray descriptors, bool useProvidedKeypoints=false) CV_OVERRIDE;
void setViewParams(const std::vector<float>& tilts, const std::vector<float>& rolls) CV_OVERRIDE;
void getViewParams(std::vector<float>& tilts, std::vector<float>& rolls) const CV_OVERRIDE;
protected:
void splitKeypointsByView(const std::vector<KeyPoint>& keypoints_,
std::vector< std::vector<KeyPoint> >& keypointsByView) const;
const Ptr<Feature2D> backend_;
int maxTilt_;
int minTilt_;
float tiltStep_;
float rotateStepBase_;
// Tilt factors.
std::vector<float> tilts_;
// Roll factors.
std::vector<float> rolls_;
private:
AffineFeature_Impl(const AffineFeature_Impl &); // copy disabled
AffineFeature_Impl& operator=(const AffineFeature_Impl &); // assign disabled
};
AffineFeature_Impl::AffineFeature_Impl(const Ptr<FeatureDetector>& backend,
int maxTilt, int minTilt, float tiltStep, float rotateStepBase)
: backend_(backend), maxTilt_(maxTilt), minTilt_(minTilt), tiltStep_(tiltStep), rotateStepBase_(rotateStepBase)
{
int i = minTilt_;
if( i == 0 )
{
tilts_.push_back(1);
rolls_.push_back(0);
i++;
}
float tilt = 1;
for( ; i <= maxTilt_; i++ )
{
tilt *= tiltStep_;
float rotateStep = rotateStepBase_ / tilt;
int rollN = cvFloor(180.0f / rotateStep);
if( rollN * rotateStep == 180.0f )
rollN--;
for( int j = 0; j <= rollN; j++ )
{
tilts_.push_back(tilt);
rolls_.push_back(rotateStep * j);
}
}
}
void AffineFeature_Impl::setViewParams(const std::vector<float>& tilts,
const std::vector<float>& rolls)
{
CV_Assert(tilts.size() == rolls.size());
tilts_ = tilts;
rolls_ = rolls;
}
void AffineFeature_Impl::getViewParams(std::vector<float>& tilts,
std::vector<float>& rolls) const
{
tilts = tilts_;
rolls = rolls_;
}
void AffineFeature_Impl::splitKeypointsByView(const std::vector<KeyPoint>& keypoints_,
std::vector< std::vector<KeyPoint> >& keypointsByView) const
{
for( size_t i = 0; i < keypoints_.size(); i++ )
{
const KeyPoint& kp = keypoints_[i];
CV_Assert( kp.class_id >= 0 && kp.class_id < (int)tilts_.size() );
keypointsByView[kp.class_id].push_back(kp);
}
}
class skewedDetectAndCompute : public ParallelLoopBody
{
public:
skewedDetectAndCompute(
const std::vector<float>& _tilts,
const std::vector<float>& _rolls,
std::vector< std::vector<KeyPoint> >& _keypointsCollection,
std::vector<Mat>& _descriptorCollection,
const Mat& _image,
const Mat& _mask,
const bool _do_keypoints,
const bool _do_descriptors,
const Ptr<Feature2D>& _backend)
: tilts(_tilts),
rolls(_rolls),
keypointsCollection(_keypointsCollection),
descriptorCollection(_descriptorCollection),
image(_image),
mask(_mask),
do_keypoints(_do_keypoints),
do_descriptors(_do_descriptors),
backend(_backend) {}
void operator()( const cv::Range& range ) const CV_OVERRIDE
{
CV_TRACE_FUNCTION();
const int begin = range.start;
const int end = range.end;
for( int a = begin; a < end; a++ )
{
Mat warpedImage, warpedMask;
Matx23f pose, invPose;
affineSkew(tilts[a], rolls[a], warpedImage, warpedMask, pose);
invertAffineTransform(pose, invPose);
std::vector<KeyPoint> wKeypoints;
Mat wDescriptors;
if( !do_keypoints )
{
const std::vector<KeyPoint>& keypointsInView = keypointsCollection[a];
if( keypointsInView.size() == 0 ) // when there are no keypoints in this affine view
continue;
std::vector<Point2f> pts_, pts;
KeyPoint::convert(keypointsInView, pts_);
transform(pts_, pts, pose);
wKeypoints.resize(keypointsInView.size());
for( size_t wi = 0; wi < wKeypoints.size(); wi++ )
{
wKeypoints[wi] = keypointsInView[wi];
wKeypoints[wi].pt = pts[wi];
}
}
backend->detectAndCompute(warpedImage, warpedMask, wKeypoints, wDescriptors, !do_keypoints);
if( do_keypoints )
{
// KeyPointsFilter::runByPixelsMask( wKeypoints, warpedMask );
if( wKeypoints.size() == 0 )
{
keypointsCollection[a].clear();
continue;
}
std::vector<Point2f> pts_, pts;
KeyPoint::convert(wKeypoints, pts_);
transform(pts_, pts, invPose);
keypointsCollection[a].resize(wKeypoints.size());
for( size_t wi = 0; wi < wKeypoints.size(); wi++ )
{
keypointsCollection[a][wi] = wKeypoints[wi];
keypointsCollection[a][wi].pt = pts[wi];
keypointsCollection[a][wi].class_id = a;
}
}
if( do_descriptors )
wDescriptors.copyTo(descriptorCollection[a]);
}
}
private:
void affineSkew(float tilt, float phi,
Mat& warpedImage, Mat& warpedMask, Matx23f& pose) const
{
int h = image.size().height;
int w = image.size().width;
Mat rotImage;
Mat mask0;
if( mask.empty() )
mask0 = Mat(h, w, CV_8UC1, 255);
else
mask0 = mask;
pose = Matx23f(1,0,0,
0,1,0);
if( phi == 0 )
image.copyTo(rotImage);
else
{
phi = phi * (float)CV_PI / 180;
float s = std::sin(phi);
float c = std::cos(phi);
Matx22f A(c, -s, s, c);
Matx<float, 4, 2> corners(0, 0, (float)w, 0, (float)w,(float)h, 0, (float)h);
Mat tf(corners * A.t());
Mat tcorners;
tf.convertTo(tcorners, CV_32S);
Rect rect = boundingRect(tcorners);
h = rect.height; w = rect.width;
pose = Matx23f(c, -s, -(float)rect.x,
s, c, -(float)rect.y);
warpAffine(image, rotImage, pose, Size(w, h), INTER_LINEAR, BORDER_REPLICATE, Scalar(), cv::ALGO_HINT_ACCURATE);
}
if( tilt == 1 )
warpedImage = rotImage;
else
{
float s = 0.8f * sqrt(tilt * tilt - 1);
GaussianBlur(rotImage, rotImage, Size(0, 0), s, 0.01);
resize(rotImage, warpedImage, Size(0, 0), 1.0/tilt, 1.0, INTER_NEAREST);
pose(0, 0) /= tilt;
pose(0, 1) /= tilt;
pose(0, 2) /= tilt;
}
if( phi != 0 || tilt != 1 )
warpAffine(mask0, warpedMask, pose, warpedImage.size(), INTER_NEAREST, BORDER_CONSTANT, Scalar(), cv::ALGO_HINT_ACCURATE);
else
warpedMask = mask0;
}
const std::vector<float>& tilts;
const std::vector<float>& rolls;
std::vector< std::vector<KeyPoint> >& keypointsCollection;
std::vector<Mat>& descriptorCollection;
const Mat& image;
const Mat& mask;
const bool do_keypoints;
const bool do_descriptors;
const Ptr<Feature2D>& backend;
};
void AffineFeature_Impl::detectAndCompute(InputArray _image, InputArray _mask,
std::vector<KeyPoint>& keypoints,
OutputArray _descriptors,
bool useProvidedKeypoints)
{
CV_TRACE_FUNCTION();
bool do_keypoints = !useProvidedKeypoints;
bool do_descriptors = _descriptors.needed();
Mat image = _image.getMat(), mask = _mask.getMat();
Mat descriptors;
if( (!do_keypoints && !do_descriptors) || _image.empty() )
return;
std::vector< std::vector<KeyPoint> > keypointsCollection(tilts_.size());
std::vector< Mat > descriptorCollection(tilts_.size());
if( do_keypoints )
keypoints.clear();
else
splitKeypointsByView(keypoints, keypointsCollection);
parallel_for_(Range(0, (int)tilts_.size()), skewedDetectAndCompute(tilts_, rolls_, keypointsCollection, descriptorCollection,
image, mask, do_keypoints, do_descriptors, backend_));
if( do_keypoints )
for( size_t i = 0; i < keypointsCollection.size(); i++ )
{
const std::vector<KeyPoint>& keys = keypointsCollection[i];
keypoints.insert(keypoints.end(), keys.begin(), keys.end());
}
if( do_descriptors )
{
_descriptors.create((int)keypoints.size(), backend_->descriptorSize(), backend_->descriptorType());
descriptors = _descriptors.getMat();
int iter = 0;
for( size_t i = 0; i < descriptorCollection.size(); i++ )
{
const Mat& descs = descriptorCollection[i];
if( descs.empty() )
continue;
Mat roi(descriptors, Rect(0, iter, descriptors.cols, descs.rows));
descs.copyTo(roi);
iter += descs.rows;
}
}
}
Ptr<AffineFeature> AffineFeature::create(const Ptr<Feature2D>& backend,
int maxTilt, int minTilt, float tiltStep, float rotateStepBase)
{
CV_Assert(minTilt < maxTilt);
CV_Assert(tiltStep > 0);
CV_Assert(rotateStepBase > 0);
return makePtr<AffineFeature_Impl>(backend, maxTilt, minTilt, tiltStep, rotateStepBase);
}
String AffineFeature::getDefaultName() const
{
return (Feature2D::getDefaultName() + ".AffineFeature");
}
} // namespace
+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.
#ifndef OPENCV_FEATURES_ALIKED_CONTEXT_HPP
#define OPENCV_FEATURES_ALIKED_CONTEXT_HPP
#include "opencv2/features.hpp"
namespace cv
{
struct ALIKEDContext
{
Mat normalizedKeypoints; // Nx2 float, coordinates in [-1, 1]
Size imageSize;
};
} // namespace cv
#endif
+266
View File
@@ -0,0 +1,266 @@
// 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
#define ANNOYLIB_MULTITHREADED_BUILD
#include "precomp.hpp"
#include "../3rdparty/annoy/annoylib.h"
#include <opencv2/core/utils/logger.hpp>
namespace cv
{
struct Random
{
static const uint64 default_seed = 0xffffffff;
#if __cplusplus < 201103L
typedef uint64 seed_type;
#endif
RNG rng;
Random(uint64 seed = default_seed)
{
rng.state = seed;
}
inline int flip()
{
// Draw random 0 or 1
return rng.next() & 1;
}
inline size_t index(size_t n)
{
// Draw random integer between 0 and n-1 where n is at most the number of data points you have
return rng(unsigned(n));
}
inline void set_seed(uint64 seed)
{
rng.state = seed;
}
};
template <typename DataType, typename DistanceType>
class ANNIndexImpl : public ANNIndex
{
public:
ANNIndexImpl(int dimension) : dim(dimension)
{
index = makePtr<::cvannoy::AnnoyIndex<int, DataType, DistanceType, Random, ::cvannoy::AnnoyIndexMultiThreadedBuildPolicy>>(dimension);
}
void addItems(InputArray _dataset) CV_OVERRIDE
{
CV_Assert(!_dataset.empty());
Mat features = _dataset.getMat();
CV_Assert(features.cols == dim);
CV_Assert(features.type() == cv::DataType<DataType>::type);
int num = features.rows;
char* msg = nullptr;
if (!index->add_item(0, features.ptr<DataType>(0), &msg))
{
if (msg)
{
String errorMsg = msg;
free(msg);
CV_Error(Error::StsError, errorMsg);
}
else
{
CV_Error(Error::StsError, "Fail to add an item.");
}
}
for (int i = 1; i < num; ++i)
index->add_item(i, features.ptr<DataType>(i));
}
void build(int trees) CV_OVERRIDE
{
if (index->get_n_items() <= 0)
CV_Error(Error::StsError, "No items added. Please add items before building the index.");
if (trees <= 0)
trees = -1;
char* msg = nullptr;
if (!index->build(trees, -1, &msg))
{
if (msg)
{
String errorMsg = msg;
free(msg);
CV_Error(Error::StsError, errorMsg);
}
else
{
CV_Error(Error::StsError, "Fail to build the index.");
}
}
}
void knnSearch(InputArray _query, OutputArray _indices, OutputArray _dists, int knn, int search_k) CV_OVERRIDE
{
CV_Assert(!_query.empty() && _query.isContinuous());
Mat query = _query.getMat(), indices, dists;
CV_Assert(query.type() == cv::DataType<DataType>::type);
CV_Assert(knn > 0 && knn <= index->get_n_items());
int numQuery = query.rows;
if (_indices.needed())
{
indices = _indices.getMat();
if (!indices.isContinuous() || indices.type() != CV_32S ||
indices.rows != numQuery || indices.cols != knn)
{
if (!indices.isContinuous())
_indices.release();
_indices.create(numQuery, knn, CV_32S);
indices = _indices.getMat();
}
}
else
indices.create(numQuery, knn, CV_32S);
if (_dists.needed())
{
dists = _dists.getMat();
if (!dists.isContinuous() || dists.type() != cv::DataType<DataType>::type ||
dists.rows != numQuery || dists.cols != knn)
{
if (!_dists.isContinuous())
_dists.release();
_dists.create(numQuery, knn, cv::DataType<DataType>::type);
dists = _dists.getMat();
}
}
else
dists.create(numQuery, knn, cv::DataType<DataType>::type);
auto processBatch = [&](const Range& range)
{
std::vector<int> nns;
std::vector<DataType> distances;
for (int i = range.start; i < range.end; ++i)
{
index->get_nns_by_vector(query.ptr<DataType>(i), knn, search_k, &nns, &distances);
std::copy(nns.begin(), nns.end(), indices.ptr<int>(i));
std::copy(distances.begin(), distances.end(), dists.ptr<DataType>(i));
nns.clear();
distances.clear();
}
};
parallel_for_(Range(0, numQuery), processBatch);
}
void save(const String &filename, bool prefault) CV_OVERRIDE
{
char* msg = nullptr;
if (!index->save(filename.c_str(), prefault, &msg))
{
if (msg)
{
String errorMsg = msg;
free(msg);
CV_Error(Error::StsError, errorMsg);
}
else
{
CV_Error(Error::StsError, "Fail to save the index.");
}
}
}
void load(const String &filename, bool prefault) CV_OVERRIDE
{
char* msg = nullptr;
if (!index->load(filename.c_str(), prefault, &msg))
{
if (msg)
{
String errorMsg = msg;
free(msg);
CV_Error(Error::StsError, errorMsg);
}
else
{
CV_Error(Error::StsError, "Fail to load the index.");
}
}
}
int getTreeNumber() CV_OVERRIDE
{
return index->get_n_trees();
}
int getItemNumber() CV_OVERRIDE
{
return index->get_n_items();
}
bool setOnDiskBuild(const String &filename) CV_OVERRIDE
{
char* msg = nullptr;
if (index->on_disk_build(filename.c_str(), &msg))
return true;
else
{
if (msg)
{
String errorMsg = msg;
CV_LOG_ERROR(NULL, errorMsg);
free(msg);
}
else
{
CV_LOG_ERROR(NULL, "Cannot set build on disk.");
}
return false;
}
}
void setSeed(int seed) CV_OVERRIDE
{
index->set_seed(static_cast<uint32_t>(seed));
}
private:
int dim;
Ptr<::cvannoy::AnnoyIndex<int, DataType, DistanceType, Random, ::cvannoy::AnnoyIndexMultiThreadedBuildPolicy>> index;
};
Ptr<ANNIndex> ANNIndex::create(int dim, ANNIndex::Distance distType)
{
switch (distType)
{
case ANNIndex::DIST_EUCLIDEAN:
return makePtr<ANNIndexImpl<float, ::cvannoy::Euclidean>>(dim);
break;
case ANNIndex::DIST_MANHATTAN:
return makePtr<ANNIndexImpl<float, ::cvannoy::Manhattan>>(dim);
break;
case ANNIndex::DIST_ANGULAR:
return makePtr<ANNIndexImpl<float, ::cvannoy::Angular>>(dim);
break;
case ANNIndex::DIST_HAMMING:
return makePtr<ANNIndexImpl<uchar, ::cvannoy::Hamming>>(dim);
break;
case ANNIndex::DIST_DOTPRODUCT:
return makePtr<ANNIndexImpl<float, ::cvannoy::DotProduct>>(dim);
break;
default:
CV_Error(Error::StsBadArg, "Unknown/unsupported distance type");
}
};
}
+490
View File
@@ -0,0 +1,490 @@
/*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"
#include <iterator>
#include <limits>
#include <opencv2/core/utils/logger.hpp>
// Requires CMake flag: DEBUG_opencv_features=ON
//#define DEBUG_BLOB_DETECTOR
#ifdef DEBUG_BLOB_DETECTOR
#include "opencv2/highgui.hpp"
#endif
namespace cv
{
class CV_EXPORTS_W SimpleBlobDetectorImpl : public SimpleBlobDetector
{
public:
explicit SimpleBlobDetectorImpl(const SimpleBlobDetector::Params &parameters = SimpleBlobDetector::Params());
virtual void read( const FileNode& fn ) CV_OVERRIDE;
virtual void write( FileStorage& fs ) const CV_OVERRIDE;
void setParams(const SimpleBlobDetector::Params& _params ) CV_OVERRIDE {
SimpleBlobDetectorImpl::validateParameters(_params);
params = _params;
}
SimpleBlobDetector::Params getParams() const CV_OVERRIDE { return params; }
static void validateParameters(const SimpleBlobDetector::Params& p)
{
if (p.thresholdStep <= 0)
CV_Error(Error::StsBadArg, "thresholdStep>0");
if (p.minThreshold > p.maxThreshold || p.minThreshold < 0)
CV_Error(Error::StsBadArg, "0<=minThreshold<=maxThreshold");
if (p.minDistBetweenBlobs <=0 )
CV_Error(Error::StsBadArg, "minDistBetweenBlobs>0");
if (p.minArea > p.maxArea || p.minArea <=0)
CV_Error(Error::StsBadArg, "0<minArea<=maxArea");
if (p.minCircularity > p.maxCircularity || p.minCircularity <= 0)
CV_Error(Error::StsBadArg, "0<minCircularity<=maxCircularity");
if (p.minInertiaRatio > p.maxInertiaRatio || p.minInertiaRatio <= 0)
CV_Error(Error::StsBadArg, "0<minInertiaRatio<=maxInertiaRatio");
if (p.minConvexity > p.maxConvexity || p.minConvexity <= 0)
CV_Error(Error::StsBadArg, "0<minConvexity<=maxConvexity");
}
protected:
struct CV_EXPORTS Center
{
Point2d location;
double radius;
double confidence;
};
virtual void detect( InputArray image, std::vector<KeyPoint>& keypoints, InputArray mask=noArray() ) CV_OVERRIDE;
virtual void findBlobs(InputArray image, InputArray binaryImage, std::vector<Center> &centers,
std::vector<std::vector<Point> > &contours, std::vector<Moments> &moments) const;
virtual const std::vector<std::vector<Point> >& getBlobContours() const CV_OVERRIDE;
Params params;
std::vector<std::vector<Point> > blobContours;
};
/*
* SimpleBlobDetector
*/
SimpleBlobDetector::Params::Params()
{
thresholdStep = 10;
minThreshold = 50;
maxThreshold = 220;
minRepeatability = 2;
minDistBetweenBlobs = 10;
filterByColor = true;
blobColor = 0;
filterByArea = true;
minArea = 25;
maxArea = 5000;
filterByCircularity = false;
minCircularity = 0.8f;
maxCircularity = std::numeric_limits<float>::max();
filterByInertia = true;
//minInertiaRatio = 0.6;
minInertiaRatio = 0.1f;
maxInertiaRatio = std::numeric_limits<float>::max();
filterByConvexity = true;
//minConvexity = 0.8;
minConvexity = 0.95f;
maxConvexity = std::numeric_limits<float>::max();
collectContours = false;
}
void SimpleBlobDetector::Params::read(const cv::FileNode& fn )
{
thresholdStep = fn["thresholdStep"];
minThreshold = fn["minThreshold"];
maxThreshold = fn["maxThreshold"];
minRepeatability = (size_t)(int)fn["minRepeatability"];
minDistBetweenBlobs = fn["minDistBetweenBlobs"];
filterByColor = (int)fn["filterByColor"] != 0 ? true : false;
blobColor = (uchar)(int)fn["blobColor"];
filterByArea = (int)fn["filterByArea"] != 0 ? true : false;
minArea = fn["minArea"];
maxArea = fn["maxArea"];
filterByCircularity = (int)fn["filterByCircularity"] != 0 ? true : false;
minCircularity = fn["minCircularity"];
maxCircularity = fn["maxCircularity"];
filterByInertia = (int)fn["filterByInertia"] != 0 ? true : false;
minInertiaRatio = fn["minInertiaRatio"];
maxInertiaRatio = fn["maxInertiaRatio"];
filterByConvexity = (int)fn["filterByConvexity"] != 0 ? true : false;
minConvexity = fn["minConvexity"];
maxConvexity = fn["maxConvexity"];
collectContours = (int)fn["collectContours"] != 0 ? true : false;
}
void SimpleBlobDetector::Params::write(cv::FileStorage& fs) const
{
fs << "thresholdStep" << thresholdStep;
fs << "minThreshold" << minThreshold;
fs << "maxThreshold" << maxThreshold;
fs << "minRepeatability" << (int)minRepeatability;
fs << "minDistBetweenBlobs" << minDistBetweenBlobs;
fs << "filterByColor" << (int)filterByColor;
fs << "blobColor" << (int)blobColor;
fs << "filterByArea" << (int)filterByArea;
fs << "minArea" << minArea;
fs << "maxArea" << maxArea;
fs << "filterByCircularity" << (int)filterByCircularity;
fs << "minCircularity" << minCircularity;
fs << "maxCircularity" << maxCircularity;
fs << "filterByInertia" << (int)filterByInertia;
fs << "minInertiaRatio" << minInertiaRatio;
fs << "maxInertiaRatio" << maxInertiaRatio;
fs << "filterByConvexity" << (int)filterByConvexity;
fs << "minConvexity" << minConvexity;
fs << "maxConvexity" << maxConvexity;
fs << "collectContours" << (int)collectContours;
}
SimpleBlobDetectorImpl::SimpleBlobDetectorImpl(const SimpleBlobDetector::Params &parameters) :
params(parameters)
{
}
void SimpleBlobDetectorImpl::read( const cv::FileNode& fn )
{
SimpleBlobDetector::Params rp;
rp.read(fn);
SimpleBlobDetectorImpl::validateParameters(rp);
params = rp;
}
void SimpleBlobDetectorImpl::write( cv::FileStorage& fs ) const
{
writeFormat(fs);
params.write(fs);
}
void SimpleBlobDetectorImpl::findBlobs(InputArray _image, InputArray _binaryImage, std::vector<Center> &centers,
std::vector<std::vector<Point> > &contoursOut, std::vector<Moments> &momentss) const
{
CV_INSTRUMENT_REGION();
Mat image = _image.getMat(), binaryImage = _binaryImage.getMat();
CV_UNUSED(image);
centers.clear();
contoursOut.clear();
momentss.clear();
std::vector < std::vector<Point> > contours;
findContours(binaryImage, contours, RETR_LIST, CHAIN_APPROX_NONE);
#ifdef DEBUG_BLOB_DETECTOR
Mat keypointsImage;
cvtColor(binaryImage, keypointsImage, COLOR_GRAY2RGB);
Mat contoursImage;
cvtColor(binaryImage, contoursImage, COLOR_GRAY2RGB);
drawContours( contoursImage, contours, -1, Scalar(0,255,0) );
imshow("contours", contoursImage );
#endif
for (size_t contourIdx = 0; contourIdx < contours.size(); contourIdx++)
{
Center center;
center.confidence = 1;
Moments moms = moments(contours[contourIdx]);
if (params.filterByArea)
{
double area = moms.m00;
if (area < params.minArea || area >= params.maxArea)
continue;
}
if (params.filterByCircularity)
{
double area = moms.m00;
double perimeter = arcLength(contours[contourIdx], true);
double ratio = 4 * CV_PI * area / (perimeter * perimeter);
if (ratio < params.minCircularity || ratio >= params.maxCircularity)
continue;
}
if (params.filterByInertia)
{
double denominator = std::sqrt(std::pow(2 * moms.mu11, 2) + std::pow(moms.mu20 - moms.mu02, 2));
const double eps = 1e-2;
double ratio;
if (denominator > eps)
{
double cosmin = (moms.mu20 - moms.mu02) / denominator;
double sinmin = 2 * moms.mu11 / denominator;
double cosmax = -cosmin;
double sinmax = -sinmin;
double imin = 0.5 * (moms.mu20 + moms.mu02) - 0.5 * (moms.mu20 - moms.mu02) * cosmin - moms.mu11 * sinmin;
double imax = 0.5 * (moms.mu20 + moms.mu02) - 0.5 * (moms.mu20 - moms.mu02) * cosmax - moms.mu11 * sinmax;
ratio = imin / imax;
}
else
{
ratio = 1;
}
if (ratio < params.minInertiaRatio || ratio >= params.maxInertiaRatio)
continue;
center.confidence = ratio * ratio;
}
if (params.filterByConvexity)
{
std::vector < Point > hull;
convexHull(contours[contourIdx], hull);
double area = moms.m00;
double hullArea = contourArea(hull);
if (fabs(hullArea) < DBL_EPSILON)
continue;
double ratio = area / hullArea;
if (ratio < params.minConvexity || ratio >= params.maxConvexity)
continue;
}
if(moms.m00 == 0.0)
continue;
center.location = Point2d(moms.m10 / moms.m00, moms.m01 / moms.m00);
if (params.filterByColor)
{
if (binaryImage.at<uchar> (cvRound(center.location.y), cvRound(center.location.x)) != params.blobColor)
continue;
}
//compute blob radius
{
std::vector<double> dists;
for (size_t pointIdx = 0; pointIdx < contours[contourIdx].size(); pointIdx++)
{
Point2d pt = contours[contourIdx][pointIdx];
dists.push_back(norm(center.location - pt));
}
std::sort(dists.begin(), dists.end());
center.radius = (dists[(dists.size() - 1) / 2] + dists[dists.size() / 2]) / 2.;
}
centers.push_back(center);
if (params.collectContours)
{
contoursOut.push_back(contours[contourIdx]);
momentss.push_back(moms);
}
#ifdef DEBUG_BLOB_DETECTOR
circle( keypointsImage, center.location, 1, Scalar(0,0,255), 1 );
#endif
}
#ifdef DEBUG_BLOB_DETECTOR
imshow("bk", keypointsImage );
waitKey();
#endif
}
void SimpleBlobDetectorImpl::detect(InputArray image, std::vector<cv::KeyPoint>& keypoints, InputArray mask)
{
CV_INSTRUMENT_REGION();
keypoints.clear();
blobContours.clear();
CV_Assert(params.minRepeatability != 0);
Mat grayscaleImage;
if (image.channels() == 3 || image.channels() == 4)
cvtColor(image, grayscaleImage, COLOR_BGR2GRAY);
else
grayscaleImage = image.getMat();
if (grayscaleImage.type() != CV_8UC1) {
CV_Error(Error::StsUnsupportedFormat, "Blob detector only supports 8-bit images!");
}
CV_CheckGT(params.thresholdStep, 0.0f, "");
if (params.minThreshold + params.thresholdStep >= params.maxThreshold)
{
// https://github.com/opencv/opencv/issues/6667
CV_LOG_ONCE_INFO(NULL, "SimpleBlobDetector: params.minDistBetweenBlobs is ignored for case with single threshold");
CV_CheckEQ(params.minRepeatability, 1u, "Incompatible parameters for case with single threshold");
}
std::vector < std::vector<Center> > centers;
std::vector<Moments> momentss;
for (double thresh = params.minThreshold; thresh < params.maxThreshold; thresh += params.thresholdStep)
{
Mat binarizedImage;
threshold(grayscaleImage, binarizedImage, thresh, 255, THRESH_BINARY);
std::vector < Center > curCenters;
std::vector<std::vector<Point> > curContours;
std::vector<Moments> curMomentss;
findBlobs(grayscaleImage, binarizedImage, curCenters, curContours, curMomentss);
std::vector < std::vector<Center> > newCenters;
std::vector<std::vector<Point> > newContours;
std::vector<Moments> newMomentss;
for (size_t i = 0; i < curCenters.size(); i++)
{
bool isNew = true;
for (size_t j = 0; j < centers.size(); j++)
{
double dist = norm(centers[j][ centers[j].size() / 2 ].location - curCenters[i].location);
isNew = dist >= params.minDistBetweenBlobs && dist >= centers[j][ centers[j].size() / 2 ].radius && dist >= curCenters[i].radius;
if (!isNew)
{
centers[j].push_back(curCenters[i]);
size_t k = centers[j].size() - 1;
while( k > 0 && curCenters[i].radius < centers[j][k-1].radius )
{
centers[j][k] = centers[j][k-1];
k--;
}
if (params.collectContours)
{
if (curCenters[i].confidence > centers[j][k].confidence
|| (curCenters[i].confidence == centers[j][k].confidence && curMomentss[i].m00 > momentss[j].m00))
{
blobContours[j] = curContours[i];
momentss[j] = curMomentss[i];
}
}
centers[j][k] = curCenters[i];
break;
}
}
if (isNew)
{
newCenters.push_back(std::vector<Center> (1, curCenters[i]));
if (params.collectContours)
{
newContours.push_back(curContours[i]);
newMomentss.push_back(curMomentss[i]);
}
}
}
std::copy(newCenters.begin(), newCenters.end(), std::back_inserter(centers));
if (params.collectContours)
{
std::copy(newContours.begin(), newContours.end(), std::back_inserter(blobContours));
std::copy(newMomentss.begin(), newMomentss.end(), std::back_inserter(momentss));
}
}
for (size_t i = 0; i < centers.size(); i++)
{
if (centers[i].size() < params.minRepeatability)
continue;
Point2d sumPoint(0, 0);
double normalizer = 0;
for (size_t j = 0; j < centers[i].size(); j++)
{
sumPoint += centers[i][j].confidence * centers[i][j].location;
normalizer += centers[i][j].confidence;
}
sumPoint *= (1. / normalizer);
KeyPoint kpt(sumPoint, (float)(centers[i][centers[i].size() / 2].radius) * 2.0f);
keypoints.push_back(kpt);
}
if (!mask.empty())
{
if (params.collectContours)
{
KeyPointsFilter::runByPixelsMask2VectorPoint(keypoints, blobContours, mask.getMat());
}
else
{
KeyPointsFilter::runByPixelsMask(keypoints, mask.getMat());
}
}
}
const std::vector<std::vector<Point> >& SimpleBlobDetectorImpl::getBlobContours() const {
return blobContours;
}
Ptr<SimpleBlobDetector> SimpleBlobDetector::create(const SimpleBlobDetector::Params& params)
{
SimpleBlobDetectorImpl::validateParameters(params);
return makePtr<SimpleBlobDetectorImpl>(params);
}
String SimpleBlobDetector::getDefaultName() const
{
return (Feature2D::getDefaultName() + ".SimpleBlobDetector");
}
}
+234
View File
@@ -0,0 +1,234 @@
// 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.
// Copyright (C) 2026, BigVision LLC, all rights reserved.
// Third party copyrights are property of their respective owners.
#include "precomp.hpp"
#include "opencv2/features.hpp"
#ifdef HAVE_OPENCV_DNN
#include "opencv2/dnn.hpp"
#include <algorithm>
#include <numeric>
namespace cv {
using namespace dnn;
// Default network input size used when the user does not specify one explicitly.
// Matches the fixed-shape standalone DISK ONNX export shipped in opencv_extra.
static const Size kDefaultDiskInputSize = Size(1024, 1024);
// DISK is a fully convolutional network with a 16x downsampling stride, so user-provided
// input sizes must be positive multiples of 16.
static const int kDiskStride = 16;
class DISK_Impl CV_FINAL : public DISK
{
public:
DISK_Impl(const String& modelPath, int maxKeypoints, float scoreThreshold,
const Size& imageSize, int backendId, int targetId)
: maxKeypoints_(maxKeypoints),
scoreThreshold_(scoreThreshold),
imageSize_(imageSize)
{
validateImageSize(imageSize_);
initNet(readNetFromONNX(modelPath), backendId, targetId);
}
DISK_Impl(const std::vector<uchar>& bufferModel, int maxKeypoints, float scoreThreshold,
const Size& imageSize, int backendId, int targetId)
: maxKeypoints_(maxKeypoints),
scoreThreshold_(scoreThreshold),
imageSize_(imageSize)
{
validateImageSize(imageSize_);
initNet(readNetFromONNX(bufferModel), backendId, targetId);
}
void detectAndCompute(InputArray _image, InputArray _mask,
std::vector<KeyPoint>& keypoints,
OutputArray _descriptors,
bool useProvidedKeypoints) CV_OVERRIDE
{
CV_Assert(!useProvidedKeypoints && "DISK does not support providing keypoints externally");
keypoints.clear();
Mat image = _image.getMat();
if (image.empty())
{
if (_descriptors.needed())
_descriptors.release();
return;
}
Mat mask = _mask.getMat();
if (!mask.empty())
{
CV_Assert(mask.type() == CV_8UC1);
CV_Assert(mask.size() == image.size());
}
const Size netSize = (imageSize_.width > 0 && imageSize_.height > 0) ? imageSize_ : kDefaultDiskInputSize;
const float scaleX = static_cast<float>(image.cols) / netSize.width;
const float scaleY = static_cast<float>(image.rows) / netSize.height;
Mat blob;
// 1/255 normalization, swap BGR->RGB, no mean subtraction.
blobFromImage(image, blob, 1.0 / 255.0, netSize, Scalar(), /*swapRB=*/true, /*crop=*/false);
net_.setInput(blob, "image");
const std::vector<String> outNames = {"keypoints", "scores", "descriptors"};
std::vector<Mat> outs;
net_.forward(outs, outNames);
CV_Assert(outs.size() == 3);
// DISK's ONNX export emits keypoints as int64 pixel coordinates, scores and
// descriptors as float32. Reshape each output to (N, *) for row-wise indexing.
Mat kptsBlob = outs[0].reshape(1, outs[0].size[1]); // N x 2, int64
Mat scoresBlob = outs[1].reshape(1, outs[1].size[1]); // N x 1, float32
Mat descBlob = outs[2].reshape(1, outs[2].size[1]); // N x D, float32
CV_Assert(kptsBlob.depth() == CV_64S);
CV_Assert(scoresBlob.depth() == CV_32F);
CV_Assert(descBlob.depth() == CV_32F);
const int numFeatures = kptsBlob.rows;
CV_Assert(scoresBlob.rows == numFeatures);
CV_Assert(descBlob.rows == numFeatures);
const int64_t* kptsData = kptsBlob.ptr<int64_t>();
const float* scoresData = scoresBlob.ptr<float>();
std::vector<int> validIndices;
validIndices.reserve(numFeatures);
keypoints.reserve(numFeatures);
for (int i = 0; i < numFeatures; ++i)
{
const float score = scoresData[i];
if (score <= scoreThreshold_)
continue;
const float x = static_cast<float>(kptsData[i * 2]) * scaleX;
const float y = static_cast<float>(kptsData[i * 2 + 1]) * scaleY;
if (!mask.empty())
{
const int ix = cvFloor(x);
const int iy = cvFloor(y);
if (ix < 0 || iy < 0 || ix >= mask.cols || iy >= mask.rows)
continue;
if (mask.at<uchar>(iy, ix) == 0)
continue;
}
keypoints.emplace_back(x, y, 1.0f, -1.0f, score);
validIndices.push_back(i);
}
if (maxKeypoints_ > 0 && static_cast<int>(keypoints.size()) > maxKeypoints_)
{
std::vector<int> order(keypoints.size());
std::iota(order.begin(), order.end(), 0);
std::partial_sort(order.begin(), order.begin() + maxKeypoints_, order.end(),
[&](int a, int b) { return keypoints[a].response > keypoints[b].response; });
order.resize(maxKeypoints_);
std::vector<KeyPoint> kept;
std::vector<int> keptIdx;
kept.reserve(maxKeypoints_);
keptIdx.reserve(maxKeypoints_);
for (int idx : order)
{
kept.push_back(keypoints[idx]);
keptIdx.push_back(validIndices[idx]);
}
keypoints.swap(kept);
validIndices.swap(keptIdx);
}
if (_descriptors.needed())
{
if (validIndices.empty())
{
_descriptors.release();
return;
}
const int dim = descBlob.cols;
_descriptors.create(static_cast<int>(validIndices.size()), dim, CV_32F);
Mat descriptors = _descriptors.getMat();
for (size_t i = 0; i < validIndices.size(); ++i)
descBlob.row(validIndices[i]).copyTo(descriptors.row(static_cast<int>(i)));
}
}
int descriptorSize() const CV_OVERRIDE { return 128; }
int descriptorType() const CV_OVERRIDE { return CV_32F; }
int defaultNorm() const CV_OVERRIDE { return NORM_L2; }
bool empty() const CV_OVERRIDE { return net_.empty(); }
void setMaxKeypoints(int maxKeypoints) CV_OVERRIDE { maxKeypoints_ = maxKeypoints; }
int getMaxKeypoints() const CV_OVERRIDE { return maxKeypoints_; }
void setScoreThreshold(float threshold) CV_OVERRIDE { scoreThreshold_ = threshold; }
float getScoreThreshold() const CV_OVERRIDE { return scoreThreshold_; }
void setImageSize(const Size& size) CV_OVERRIDE
{
validateImageSize(size);
imageSize_ = size;
}
Size getImageSize() const CV_OVERRIDE { return imageSize_; }
String getDefaultName() const CV_OVERRIDE { return Feature2D::getDefaultName() + ".DISK"; }
private:
void initNet(const Net& net, int backendId, int targetId)
{
net_ = net;
net_.setPreferableBackend(backendId);
net_.setPreferableTarget(targetId);
}
static void validateImageSize(const Size& size)
{
if (size.width == 0 && size.height == 0)
return; // use default
CV_Assert(size.width > 0 && size.height > 0);
CV_Assert(size.width % kDiskStride == 0);
CV_Assert(size.height % kDiskStride == 0);
}
int maxKeypoints_;
float scoreThreshold_;
Size imageSize_;
Net net_;
};
Ptr<DISK> DISK::create(const String& modelPath, int maxKeypoints, float scoreThreshold,
const Size& imageSize, int backendId, int targetId)
{
CV_TRACE_FUNCTION();
return makePtr<DISK_Impl>(modelPath, maxKeypoints, scoreThreshold, imageSize, backendId, targetId);
}
Ptr<DISK> DISK::create(const std::vector<uchar>& bufferModel, int maxKeypoints, float scoreThreshold,
const Size& imageSize, int backendId, int targetId)
{
CV_TRACE_FUNCTION();
return makePtr<DISK_Impl>(bufferModel, maxKeypoints, scoreThreshold, imageSize, backendId, targetId);
}
String DISK::getDefaultName() const
{
return Feature2D::getDefaultName() + ".DISK";
}
} // namespace cv
#endif // HAVE_OPENCV_DNN
+279
View File
@@ -0,0 +1,279 @@
/*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.
//
//
// Intel License Agreement
// For Open Source Computer Vision Library
//
// Copyright (C) 2000, 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 Intel Corporation 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"
const int draw_shift_bits = 4;
const int draw_multiplier = 1 << draw_shift_bits;
namespace cv
{
/*
* Functions to draw keypoints and matches.
*/
static inline void _drawKeypoint( InputOutputArray img, const KeyPoint& p, const Scalar& color, DrawMatchesFlags flags )
{
CV_Assert( !img.empty() );
Point center( cvRound(p.pt.x * draw_multiplier), cvRound(p.pt.y * draw_multiplier) );
if( !!(flags & DrawMatchesFlags::DRAW_RICH_KEYPOINTS) )
{
int radius = cvRound(p.size/2 * draw_multiplier); // KeyPoint::size is a diameter
// draw the circles around keypoints with the keypoints size
circle( img, center, radius, color, 1, LINE_AA, draw_shift_bits );
// draw orientation of the keypoint, if it is applicable
if( p.angle != -1 )
{
float srcAngleRad = p.angle*(float)CV_PI/180.f;
Point orient( cvRound(cos(srcAngleRad)*radius ),
cvRound(sin(srcAngleRad)*radius )
);
line( img, center, center+orient, color, 1, LINE_AA, draw_shift_bits );
}
#if 0
else
{
// draw center with R=1
int radius = 1 * draw_multiplier;
circle( img, center, radius, color, 1, LINE_AA, draw_shift_bits );
}
#endif
}
else
{
// draw center with R=3
int radius = 3 * draw_multiplier;
circle( img, center, radius, color, 1, LINE_AA, draw_shift_bits );
}
}
void drawKeypoints( InputArray image, const std::vector<KeyPoint>& keypoints, InputOutputArray outImage,
const Scalar& _color, DrawMatchesFlags flags )
{
CV_INSTRUMENT_REGION();
if( !(flags & DrawMatchesFlags::DRAW_OVER_OUTIMG) )
{
if (image.type() == CV_8UC3 || image.type() == CV_8UC4)
{
image.copyTo(outImage);
}
else if( image.type() == CV_8UC1 )
{
cvtColor( image, outImage, COLOR_GRAY2BGR );
}
else
{
CV_Error( Error::StsBadArg, "Incorrect type of input image: " + typeToString(image.type()) );
}
}
RNG& rng=theRNG();
bool isRandColor = _color == Scalar::all(-1);
CV_Assert( !outImage.empty() );
std::vector<KeyPoint>::const_iterator it = keypoints.begin(),
end = keypoints.end();
for( ; it != end; ++it )
{
Scalar color = isRandColor ? Scalar( rng(256), rng(256), rng(256), 255 ) : _color;
_drawKeypoint( outImage, *it, color, flags );
}
}
static void _prepareImage(InputArray src, const Mat& dst)
{
CV_CheckType(src.type(), src.type() == CV_8UC1 || src.type() == CV_8UC3 || src.type() == CV_8UC4, "Unsupported source image");
CV_CheckType(dst.type(), dst.type() == CV_8UC3 || dst.type() == CV_8UC4, "Unsupported destination image");
const int src_cn = src.channels();
const int dst_cn = dst.channels();
if (src_cn == dst_cn)
src.copyTo(dst);
else if (src_cn == 1)
cvtColor(src, dst, dst_cn == 3 ? COLOR_GRAY2BGR : COLOR_GRAY2BGRA);
else if (src_cn == 3 && dst_cn == 4)
cvtColor(src, dst, COLOR_BGR2BGRA);
else if (src_cn == 4 && dst_cn == 3)
cvtColor(src, dst, COLOR_BGRA2BGR);
else
CV_Error(Error::StsInternal, "");
}
static void _prepareImgAndDrawKeypoints( InputArray img1, const std::vector<KeyPoint>& keypoints1,
InputArray img2, const std::vector<KeyPoint>& keypoints2,
InputOutputArray _outImg, Mat& outImg1, Mat& outImg2,
const Scalar& singlePointColor, DrawMatchesFlags flags )
{
Mat outImg;
Size img1size = img1.size(), img2size = img2.size();
Size size( img1size.width + img2size.width, MAX(img1size.height, img2size.height) );
if( !!(flags & DrawMatchesFlags::DRAW_OVER_OUTIMG) )
{
outImg = _outImg.getMat();
if( size.width > outImg.cols || size.height > outImg.rows )
CV_Error( Error::StsBadSize, "outImg has size less than need to draw img1 and img2 together" );
outImg1 = outImg( Rect(0, 0, img1size.width, img1size.height) );
outImg2 = outImg( Rect(img1size.width, 0, img2size.width, img2size.height) );
}
else
{
const int cn1 = img1.channels(), cn2 = img2.channels();
const int out_cn = std::max(3, std::max(cn1, cn2));
_outImg.create(size, CV_MAKETYPE(img1.depth(), out_cn));
outImg = _outImg.getMat();
outImg = Scalar::all(0);
outImg1 = outImg( Rect(0, 0, img1size.width, img1size.height) );
outImg2 = outImg( Rect(img1size.width, 0, img2size.width, img2size.height) );
_prepareImage(img1, outImg1);
_prepareImage(img2, outImg2);
}
// draw keypoints
if( !(flags & DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS) )
{
Mat _outImg1 = outImg( Rect(0, 0, img1size.width, img1size.height) );
drawKeypoints( _outImg1, keypoints1, _outImg1, singlePointColor, flags | DrawMatchesFlags::DRAW_OVER_OUTIMG );
Mat _outImg2 = outImg( Rect(img1size.width, 0, img2size.width, img2size.height) );
drawKeypoints( _outImg2, keypoints2, _outImg2, singlePointColor, flags | DrawMatchesFlags::DRAW_OVER_OUTIMG );
}
}
static inline void _drawMatch( InputOutputArray outImg, InputOutputArray outImg1, InputOutputArray outImg2 ,
const KeyPoint& kp1, const KeyPoint& kp2, const Scalar& matchColor, DrawMatchesFlags flags,
const int matchesThickness )
{
RNG& rng = theRNG();
bool isRandMatchColor = matchColor == Scalar::all(-1);
Scalar color = isRandMatchColor ? Scalar( rng(256), rng(256), rng(256), 255 ) : matchColor;
_drawKeypoint( outImg1, kp1, color, flags );
_drawKeypoint( outImg2, kp2, color, flags );
Point2f pt1 = kp1.pt,
pt2 = kp2.pt,
dpt2 = Point2f( std::min(pt2.x+outImg1.size().width, float(outImg.size().width-1)), pt2.y );
line( outImg,
Point(cvRound(pt1.x*draw_multiplier), cvRound(pt1.y*draw_multiplier)),
Point(cvRound(dpt2.x*draw_multiplier), cvRound(dpt2.y*draw_multiplier)),
color, matchesThickness, LINE_AA, draw_shift_bits );
}
void drawMatches( InputArray img1, const std::vector<KeyPoint>& keypoints1,
InputArray img2, const std::vector<KeyPoint>& keypoints2,
const std::vector<DMatch>& matches1to2, InputOutputArray outImg,
const Scalar& matchColor, const Scalar& singlePointColor,
const std::vector<char>& matchesMask, DrawMatchesFlags flags )
{
drawMatches( img1, keypoints1,
img2, keypoints2,
matches1to2, outImg,
1, matchColor,
singlePointColor, matchesMask,
flags);
}
void drawMatches( InputArray img1, const std::vector<KeyPoint>& keypoints1,
InputArray img2, const std::vector<KeyPoint>& keypoints2,
const std::vector<DMatch>& matches1to2, InputOutputArray outImg,
const int matchesThickness, const Scalar& matchColor,
const Scalar& singlePointColor, const std::vector<char>& matchesMask,
DrawMatchesFlags flags )
{
if( !matchesMask.empty() && matchesMask.size() != matches1to2.size() )
CV_Error( Error::StsBadSize, "matchesMask must have the same size as matches1to2" );
Mat outImg1, outImg2;
_prepareImgAndDrawKeypoints( img1, keypoints1, img2, keypoints2,
outImg, outImg1, outImg2, singlePointColor, flags );
// draw matches
for( size_t m = 0; m < matches1to2.size(); m++ )
{
if( matchesMask.empty() || matchesMask[m] )
{
int i1 = matches1to2[m].queryIdx;
int i2 = matches1to2[m].trainIdx;
CV_Assert(i1 >= 0 && i1 < static_cast<int>(keypoints1.size()));
CV_Assert(i2 >= 0 && i2 < static_cast<int>(keypoints2.size()));
const KeyPoint &kp1 = keypoints1[i1], &kp2 = keypoints2[i2];
_drawMatch( outImg, outImg1, outImg2, kp1, kp2, matchColor, flags, matchesThickness );
}
}
}
void drawMatches( InputArray img1, const std::vector<KeyPoint>& keypoints1,
InputArray img2, const std::vector<KeyPoint>& keypoints2,
const std::vector<std::vector<DMatch> >& matches1to2, InputOutputArray outImg,
const Scalar& matchColor, const Scalar& singlePointColor,
const std::vector<std::vector<char> >& matchesMask, DrawMatchesFlags flags )
{
if( !matchesMask.empty() && matchesMask.size() != matches1to2.size() )
CV_Error( Error::StsBadSize, "matchesMask must have the same size as matches1to2" );
Mat outImg1, outImg2;
_prepareImgAndDrawKeypoints( img1, keypoints1, img2, keypoints2,
outImg, outImg1, outImg2, singlePointColor, flags );
// draw matches
for( size_t i = 0; i < matches1to2.size(); i++ )
{
for( size_t j = 0; j < matches1to2[i].size(); j++ )
{
int i1 = matches1to2[i][j].queryIdx;
int i2 = matches1to2[i][j].trainIdx;
if( matchesMask.empty() || matchesMask[i][j] )
{
const KeyPoint &kp1 = keypoints1[i1], &kp2 = keypoints2[i2];
_drawMatch( outImg, outImg1, outImg2, kp1, kp2, matchColor, flags, 1 );
}
}
}
}
}
+47
View File
@@ -0,0 +1,47 @@
/*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-2010, 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"
namespace cv
{
}
+572
View File
@@ -0,0 +1,572 @@
//*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"
#include <limits>
using namespace cv;
template<typename _Tp> static int solveQuadratic(_Tp a, _Tp b, _Tp c, _Tp& x1, _Tp& x2)
{
if( a == 0 )
{
if( b == 0 )
{
x1 = x2 = 0;
return c == 0;
}
x1 = x2 = -c/b;
return 1;
}
_Tp d = b*b - 4*a*c;
if( d < 0 )
{
x1 = x2 = 0;
return 0;
}
if( d > 0 )
{
d = std::sqrt(d);
double s = 1/(2*a);
x1 = (-b - d)*s;
x2 = (-b + d)*s;
if( x1 > x2 )
std::swap(x1, x2);
return 2;
}
x1 = x2 = -b/(2*a);
return 1;
}
//for android ndk
#undef _S
static inline Point2f applyHomography( const Mat_<double>& H, const Point2f& pt )
{
double z = H(2,0)*pt.x + H(2,1)*pt.y + H(2,2);
if( z )
{
double w = 1./z;
return Point2f( (float)((H(0,0)*pt.x + H(0,1)*pt.y + H(0,2))*w), (float)((H(1,0)*pt.x + H(1,1)*pt.y + H(1,2))*w) );
}
return Point2f( std::numeric_limits<float>::max(), std::numeric_limits<float>::max() );
}
static inline void linearizeHomographyAt( const Mat_<double>& H, const Point2f& pt, Mat_<double>& A )
{
A.create(2,2);
double p1 = H(0,0)*pt.x + H(0,1)*pt.y + H(0,2),
p2 = H(1,0)*pt.x + H(1,1)*pt.y + H(1,2),
p3 = H(2,0)*pt.x + H(2,1)*pt.y + H(2,2),
p3_2 = p3*p3;
if( p3 )
{
A(0,0) = H(0,0)/p3 - p1*H(2,0)/p3_2; // fxdx
A(0,1) = H(0,1)/p3 - p1*H(2,1)/p3_2; // fxdy
A(1,0) = H(1,0)/p3 - p2*H(2,0)/p3_2; // fydx
A(1,1) = H(1,1)/p3 - p2*H(2,1)/p3_2; // fydx
}
else
A.setTo(Scalar::all(std::numeric_limits<double>::max()));
}
class EllipticKeyPoint
{
public:
EllipticKeyPoint();
EllipticKeyPoint( const Point2f& _center, const Scalar& _ellipse );
static void convert( const std::vector<KeyPoint>& src, std::vector<EllipticKeyPoint>& dst );
static void convert( const std::vector<EllipticKeyPoint>& src, std::vector<KeyPoint>& dst );
static Mat_<double> getSecondMomentsMatrix( const Scalar& _ellipse );
Mat_<double> getSecondMomentsMatrix() const;
void calcProjection( const Mat_<double>& H, EllipticKeyPoint& projection ) const;
static void calcProjection( const std::vector<EllipticKeyPoint>& src, const Mat_<double>& H, std::vector<EllipticKeyPoint>& dst );
Point2f center;
Scalar ellipse; // 3 elements a, b, c: ax^2+2bxy+cy^2=1
Size_<float> axes; // half length of ellipse axes
Size_<float> boundingBox; // half sizes of bounding box which sides are parallel to the coordinate axes
};
EllipticKeyPoint::EllipticKeyPoint()
{
*this = EllipticKeyPoint(Point2f(0,0), Scalar(1, 0, 1) );
}
EllipticKeyPoint::EllipticKeyPoint( const Point2f& _center, const Scalar& _ellipse )
{
center = _center;
ellipse = _ellipse;
double a = ellipse[0], b = ellipse[1], c = ellipse[2];
double ac_b2 = a*c - b*b;
double x1, x2;
solveQuadratic(1., -(a+c), ac_b2, x1, x2);
axes.width = (float)(1/sqrt(x1));
axes.height = (float)(1/sqrt(x2));
boundingBox.width = (float)sqrt(ellipse[2]/ac_b2);
boundingBox.height = (float)sqrt(ellipse[0]/ac_b2);
}
Mat_<double> EllipticKeyPoint::getSecondMomentsMatrix( const Scalar& _ellipse )
{
Mat_<double> M(2, 2);
M(0,0) = _ellipse[0];
M(1,0) = M(0,1) = _ellipse[1];
M(1,1) = _ellipse[2];
return M;
}
Mat_<double> EllipticKeyPoint::getSecondMomentsMatrix() const
{
return getSecondMomentsMatrix(ellipse);
}
void EllipticKeyPoint::calcProjection( const Mat_<double>& H, EllipticKeyPoint& projection ) const
{
Point2f dstCenter = applyHomography(H, center);
Mat_<double> invM; invert(getSecondMomentsMatrix(), invM);
Mat_<double> Aff; linearizeHomographyAt(H, center, Aff);
Mat_<double> dstM; invert(Aff*invM*Aff.t(), dstM);
projection = EllipticKeyPoint( dstCenter, Scalar(dstM(0,0), dstM(0,1), dstM(1,1)) );
}
void EllipticKeyPoint::convert( const std::vector<KeyPoint>& src, std::vector<EllipticKeyPoint>& dst )
{
CV_INSTRUMENT_REGION();
if( !src.empty() )
{
dst.resize(src.size());
for( size_t i = 0; i < src.size(); i++ )
{
float rad = src[i].size/2;
CV_Assert( rad );
float fac = 1.f/(rad*rad);
dst[i] = EllipticKeyPoint( src[i].pt, Scalar(fac, 0, fac) );
}
}
}
void EllipticKeyPoint::convert( const std::vector<EllipticKeyPoint>& src, std::vector<KeyPoint>& dst )
{
CV_INSTRUMENT_REGION();
if( !src.empty() )
{
dst.resize(src.size());
for( size_t i = 0; i < src.size(); i++ )
{
Size_<float> axes = src[i].axes;
float rad = sqrt(axes.height*axes.width);
dst[i] = KeyPoint(src[i].center, 2*rad );
}
}
}
void EllipticKeyPoint::calcProjection( const std::vector<EllipticKeyPoint>& src, const Mat_<double>& H, std::vector<EllipticKeyPoint>& dst )
{
if( !src.empty() )
{
CV_Assert( !H.empty() && H.cols == 3 && H.rows == 3);
dst.resize(src.size());
std::vector<EllipticKeyPoint>::const_iterator srcIt = src.begin();
std::vector<EllipticKeyPoint>::iterator dstIt = dst.begin();
for( ; srcIt != src.end() && dstIt != dst.end(); ++srcIt, ++dstIt )
srcIt->calcProjection(H, *dstIt);
}
}
static void filterEllipticKeyPointsByImageSize( std::vector<EllipticKeyPoint>& keypoints, const Size& imgSize )
{
if( !keypoints.empty() )
{
std::vector<EllipticKeyPoint> filtered;
filtered.reserve(keypoints.size());
std::vector<EllipticKeyPoint>::const_iterator it = keypoints.begin();
for( ; it != keypoints.end(); ++it )
{
if( it->center.x + it->boundingBox.width < imgSize.width &&
it->center.x - it->boundingBox.width > 0 &&
it->center.y + it->boundingBox.height < imgSize.height &&
it->center.y - it->boundingBox.height > 0 )
filtered.push_back(*it);
}
keypoints.assign(filtered.begin(), filtered.end());
}
}
struct IntersectAreaCounter
{
IntersectAreaCounter( float _dr, int _minx,
int _miny, int _maxy,
const Point2f& _diff,
const Scalar& _ellipse1, const Scalar& _ellipse2 ) :
dr(_dr), bua(0), bna(0), minx(_minx), miny(_miny), maxy(_maxy),
diff(_diff), ellipse1(_ellipse1), ellipse2(_ellipse2) {}
IntersectAreaCounter( const IntersectAreaCounter& counter, Split )
{
*this = counter;
bua = 0;
bna = 0;
}
void operator()( const BlockedRange& range )
{
CV_Assert( miny < maxy );
CV_Assert( dr > FLT_EPSILON );
int temp_bua = bua, temp_bna = bna;
for( int i = range.begin(); i != range.end(); i++ )
{
float rx1 = minx + i*dr;
float rx2 = rx1 - diff.x;
for( float ry1 = (float)miny; ry1 <= (float)maxy; ry1 += dr )
{
float ry2 = ry1 - diff.y;
//compute the distance from the ellipse center
float e1 = (float)(ellipse1[0]*rx1*rx1 + 2*ellipse1[1]*rx1*ry1 + ellipse1[2]*ry1*ry1);
float e2 = (float)(ellipse2[0]*rx2*rx2 + 2*ellipse2[1]*rx2*ry2 + ellipse2[2]*ry2*ry2);
//compute the area
if( e1<1 && e2<1 ) temp_bna++;
if( e1<1 || e2<1 ) temp_bua++;
}
}
bua = temp_bua;
bna = temp_bna;
}
void join( IntersectAreaCounter& ac )
{
bua += ac.bua;
bna += ac.bna;
}
float dr;
int bua, bna;
int minx;
int miny, maxy;
Point2f diff;
Scalar ellipse1, ellipse2;
};
struct SIdx
{
SIdx() : S(-1), i1(-1), i2(-1) {}
SIdx(float _S, int _i1, int _i2) : S(_S), i1(_i1), i2(_i2) {}
float S;
int i1;
int i2;
bool operator<(const SIdx& v) const { return S > v.S; }
struct UsedFinder
{
UsedFinder(const SIdx& _used) : used(_used) {}
const SIdx& used;
bool operator()(const SIdx& v) const { return (v.i1 == used.i1 || v.i2 == used.i2); }
UsedFinder& operator=(const UsedFinder&) = delete;
// To avoid -Wdeprecated-copy warning, copy constructor is needed.
UsedFinder(const UsedFinder&) = default;
};
};
static void computeOneToOneMatchedOverlaps( const std::vector<EllipticKeyPoint>& keypoints1, const std::vector<EllipticKeyPoint>& keypoints2t,
bool commonPart, std::vector<SIdx>& overlaps, float minOverlap )
{
CV_Assert( minOverlap >= 0.f );
overlaps.clear();
if( keypoints1.empty() || keypoints2t.empty() )
return;
overlaps.clear();
overlaps.reserve(cvRound(keypoints1.size() * keypoints2t.size() * 0.01));
for( size_t i1 = 0; i1 < keypoints1.size(); i1++ )
{
EllipticKeyPoint kp1 = keypoints1[i1];
float maxDist = sqrt(kp1.axes.width*kp1.axes.height),
fac = 30.f/maxDist;
if( !commonPart )
fac=3;
maxDist = maxDist*4;
fac = 1.f/(fac*fac);
EllipticKeyPoint keypoint1a = EllipticKeyPoint( kp1.center, Scalar(fac*kp1.ellipse[0], fac*kp1.ellipse[1], fac*kp1.ellipse[2]) );
for( size_t i2 = 0; i2 < keypoints2t.size(); i2++ )
{
EllipticKeyPoint kp2 = keypoints2t[i2];
Point2f diff = kp2.center - kp1.center;
if( norm(diff) < maxDist )
{
EllipticKeyPoint keypoint2a = EllipticKeyPoint( kp2.center, Scalar(fac*kp2.ellipse[0], fac*kp2.ellipse[1], fac*kp2.ellipse[2]) );
//find the largest eigenvalue
int maxx = (int)ceil(( keypoint1a.boundingBox.width > (diff.x+keypoint2a.boundingBox.width)) ?
keypoint1a.boundingBox.width : (diff.x+keypoint2a.boundingBox.width));
int minx = (int)floor((-keypoint1a.boundingBox.width < (diff.x-keypoint2a.boundingBox.width)) ?
-keypoint1a.boundingBox.width : (diff.x-keypoint2a.boundingBox.width));
int maxy = (int)ceil(( keypoint1a.boundingBox.height > (diff.y+keypoint2a.boundingBox.height)) ?
keypoint1a.boundingBox.height : (diff.y+keypoint2a.boundingBox.height));
int miny = (int)floor((-keypoint1a.boundingBox.height < (diff.y-keypoint2a.boundingBox.height)) ?
-keypoint1a.boundingBox.height : (diff.y-keypoint2a.boundingBox.height));
int mina = (maxx-minx) < (maxy-miny) ? (maxx-minx) : (maxy-miny) ;
//compute the area
float dr = (float)mina/50.f;
int N = (int)floor((float)(maxx - minx) / dr);
IntersectAreaCounter ac( dr, minx, miny, maxy, diff, keypoint1a.ellipse, keypoint2a.ellipse );
parallel_reduce( BlockedRange(0, N+1), ac );
if( ac.bna > 0 )
{
float ov = (float)ac.bna / (float)ac.bua;
if( ov >= minOverlap )
overlaps.push_back(SIdx(ov, (int)i1, (int)i2));
}
}
}
}
std::sort( overlaps.begin(), overlaps.end() );
typedef std::vector<SIdx>::iterator It;
It pos = overlaps.begin();
It end = overlaps.end();
while(pos != end)
{
It prev = pos++;
end = std::remove_if(pos, end, SIdx::UsedFinder(*prev));
}
overlaps.erase(pos, overlaps.end());
}
static void calculateRepeatability( const Mat& img1, const Mat& img2, const Mat& H1to2,
const std::vector<KeyPoint>& _keypoints1, const std::vector<KeyPoint>& _keypoints2,
float& repeatability, int& correspondencesCount,
Mat* thresholdedOverlapMask=0 )
{
std::vector<EllipticKeyPoint> keypoints1, keypoints2, keypoints1t, keypoints2t;
EllipticKeyPoint::convert( _keypoints1, keypoints1 );
EllipticKeyPoint::convert( _keypoints2, keypoints2 );
// calculate projections of key points
EllipticKeyPoint::calcProjection( keypoints1, H1to2, keypoints1t );
Mat H2to1; invert(H1to2, H2to1);
EllipticKeyPoint::calcProjection( keypoints2, H2to1, keypoints2t );
float overlapThreshold;
bool ifEvaluateDetectors = thresholdedOverlapMask == 0;
if( ifEvaluateDetectors )
{
overlapThreshold = 1.f - 0.4f;
// remove key points from outside of the common image part
Size sz1 = img1.size(), sz2 = img2.size();
filterEllipticKeyPointsByImageSize( keypoints1, sz1 );
filterEllipticKeyPointsByImageSize( keypoints1t, sz2 );
filterEllipticKeyPointsByImageSize( keypoints2, sz2 );
filterEllipticKeyPointsByImageSize( keypoints2t, sz1 );
}
else
{
overlapThreshold = 1.f - 0.5f;
thresholdedOverlapMask->create( (int)keypoints1.size(), (int)keypoints2t.size(), CV_8UC1 );
thresholdedOverlapMask->setTo( Scalar::all(0) );
}
size_t size1 = keypoints1.size(), size2 = keypoints2t.size();
size_t minCount = MIN( size1, size2 );
// calculate overlap errors
std::vector<SIdx> overlaps;
computeOneToOneMatchedOverlaps( keypoints1, keypoints2t, ifEvaluateDetectors, overlaps, overlapThreshold/*min overlap*/ );
correspondencesCount = -1;
repeatability = -1.f;
if( overlaps.empty() )
return;
if( ifEvaluateDetectors )
{
// regions one-to-one matching
correspondencesCount = (int)overlaps.size();
repeatability = minCount ? (float)correspondencesCount / minCount : -1;
}
else
{
for( size_t i = 0; i < overlaps.size(); i++ )
{
int y = overlaps[i].i1;
int x = overlaps[i].i2;
thresholdedOverlapMask->at<uchar>(y,x) = 1;
}
}
}
void cv::evaluateFeatureDetector( const Mat& img1, const Mat& img2, const Mat& H1to2,
std::vector<KeyPoint>* _keypoints1, std::vector<KeyPoint>* _keypoints2,
float& repeatability, int& correspCount,
const Ptr<FeatureDetector>& _fdetector )
{
CV_INSTRUMENT_REGION();
Ptr<FeatureDetector> fdetector(_fdetector);
std::vector<KeyPoint> *keypoints1, *keypoints2, buf1, buf2;
keypoints1 = _keypoints1 != 0 ? _keypoints1 : &buf1;
keypoints2 = _keypoints2 != 0 ? _keypoints2 : &buf2;
if( (keypoints1->empty() || keypoints2->empty()) && !fdetector )
CV_Error( Error::StsBadArg, "fdetector must not be empty when keypoints1 or keypoints2 is empty" );
if( keypoints1->empty() )
fdetector->detect( img1, *keypoints1 );
if( keypoints2->empty() )
fdetector->detect( img2, *keypoints2 );
calculateRepeatability( img1, img2, H1to2, *keypoints1, *keypoints2, repeatability, correspCount );
}
struct DMatchForEvaluation : public DMatch
{
uchar isCorrect;
DMatchForEvaluation( const DMatch &dm ) : DMatch( dm ), isCorrect(0) {}
};
static inline float recall( int correctMatchCount, int correspondenceCount )
{
return correspondenceCount ? (float)correctMatchCount / (float)correspondenceCount : -1;
}
static inline float precision( int correctMatchCount, int falseMatchCount )
{
return correctMatchCount + falseMatchCount ? (float)correctMatchCount / (float)(correctMatchCount + falseMatchCount) : -1;
}
void cv::computeRecallPrecisionCurve( const std::vector<std::vector<DMatch> >& matches1to2,
const std::vector<std::vector<uchar> >& correctMatches1to2Mask,
std::vector<Point2f>& recallPrecisionCurve )
{
CV_INSTRUMENT_REGION();
CV_Assert( matches1to2.size() == correctMatches1to2Mask.size() );
std::vector<DMatchForEvaluation> allMatches;
int correspondenceCount = 0;
for( size_t i = 0; i < matches1to2.size(); i++ )
{
for( size_t j = 0; j < matches1to2[i].size(); j++ )
{
DMatchForEvaluation match = matches1to2[i][j];
match.isCorrect = correctMatches1to2Mask[i][j] ;
allMatches.push_back( match );
correspondenceCount += match.isCorrect != 0 ? 1 : 0;
}
}
std::sort( allMatches.begin(), allMatches.end() );
int correctMatchCount = 0, falseMatchCount = 0;
recallPrecisionCurve.resize( allMatches.size() );
for( size_t i = 0; i < allMatches.size(); i++ )
{
if( allMatches[i].isCorrect )
correctMatchCount++;
else
falseMatchCount++;
float r = recall( correctMatchCount, correspondenceCount );
float p = precision( correctMatchCount, falseMatchCount );
recallPrecisionCurve[i] = Point2f(1-p, r);
}
}
float cv::getRecall( const std::vector<Point2f>& recallPrecisionCurve, float l_precision )
{
CV_INSTRUMENT_REGION();
int nearestPointIndex = getNearestPoint( recallPrecisionCurve, l_precision );
float recall = -1.f;
if( nearestPointIndex >= 0 )
recall = recallPrecisionCurve[nearestPointIndex].y;
return recall;
}
int cv::getNearestPoint( const std::vector<Point2f>& recallPrecisionCurve, float l_precision )
{
CV_INSTRUMENT_REGION();
int nearestPointIndex = -1;
if( l_precision >= 0 && l_precision <= 1 )
{
float minDiff = FLT_MAX;
for( size_t i = 0; i < recallPrecisionCurve.size(); i++ )
{
float curDiff = std::fabs(l_precision - recallPrecisionCurve[i].x);
if( curDiff <= minDiff )
{
nearestPointIndex = (int)i;
minDiff = curDiff;
}
}
}
return nearestPointIndex;
}
+184
View File
@@ -0,0 +1,184 @@
/* This is FAST corner detector, contributed to OpenCV by the author, Edward Rosten.
Below is the original copyright and the references */
/*
Copyright (c) 2006, 2008 Edward Rosten
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
*Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
*Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
*Neither the name of the University of Cambridge nor the names of
its contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
The references are:
* Machine learning for high-speed corner detection,
E. Rosten and T. Drummond, ECCV 2006
* Faster and better: A machine learning approach to corner detection
E. Rosten, R. Porter and T. Drummond, PAMI, 2009
*/
#include "precomp.hpp"
#include "fast.hpp"
#include "opencv2/core/hal/intrin.hpp"
namespace cv
{
namespace opt_AVX2
{
class FAST_t_patternSize16_AVX2_Impl CV_FINAL: public FAST_t_patternSize16_AVX2
{
public:
FAST_t_patternSize16_AVX2_Impl(int _cols, int _threshold, bool _nonmax_suppression, const int* _pixel):
cols(_cols), nonmax_suppression(_nonmax_suppression), pixel(_pixel)
{
//patternSize = 16
t256c = (char)_threshold;
threshold = std::min(std::max(_threshold, 0), 255);
}
virtual void process(int &j, const uchar* &ptr, uchar* curr, int* cornerpos, int &ncorners) CV_OVERRIDE
{
static const __m256i delta256 = _mm256_broadcastsi128_si256(_mm_set1_epi8((char)(-128))), K16_256 = _mm256_broadcastsi128_si256(_mm_set1_epi8((char)8));
const __m256i t256 = _mm256_broadcastsi128_si256(_mm_set1_epi8(t256c));
for (; j < cols - 32 - 3; j += 32, ptr += 32)
{
__m256i m0, m1;
__m256i v0 = _mm256_loadu_si256((const __m256i*)ptr);
__m256i v1 = _mm256_xor_si256(_mm256_subs_epu8(v0, t256), delta256);
v0 = _mm256_xor_si256(_mm256_adds_epu8(v0, t256), delta256);
__m256i x0 = _mm256_sub_epi8(_mm256_loadu_si256((const __m256i*)(ptr + pixel[0])), delta256);
__m256i x1 = _mm256_sub_epi8(_mm256_loadu_si256((const __m256i*)(ptr + pixel[4])), delta256);
__m256i x2 = _mm256_sub_epi8(_mm256_loadu_si256((const __m256i*)(ptr + pixel[8])), delta256);
__m256i x3 = _mm256_sub_epi8(_mm256_loadu_si256((const __m256i*)(ptr + pixel[12])), delta256);
m0 = _mm256_and_si256(_mm256_cmpgt_epi8(x0, v0), _mm256_cmpgt_epi8(x1, v0));
m1 = _mm256_and_si256(_mm256_cmpgt_epi8(v1, x0), _mm256_cmpgt_epi8(v1, x1));
m0 = _mm256_or_si256(m0, _mm256_and_si256(_mm256_cmpgt_epi8(x1, v0), _mm256_cmpgt_epi8(x2, v0)));
m1 = _mm256_or_si256(m1, _mm256_and_si256(_mm256_cmpgt_epi8(v1, x1), _mm256_cmpgt_epi8(v1, x2)));
m0 = _mm256_or_si256(m0, _mm256_and_si256(_mm256_cmpgt_epi8(x2, v0), _mm256_cmpgt_epi8(x3, v0)));
m1 = _mm256_or_si256(m1, _mm256_and_si256(_mm256_cmpgt_epi8(v1, x2), _mm256_cmpgt_epi8(v1, x3)));
m0 = _mm256_or_si256(m0, _mm256_and_si256(_mm256_cmpgt_epi8(x3, v0), _mm256_cmpgt_epi8(x0, v0)));
m1 = _mm256_or_si256(m1, _mm256_and_si256(_mm256_cmpgt_epi8(v1, x3), _mm256_cmpgt_epi8(v1, x0)));
m0 = _mm256_or_si256(m0, m1);
unsigned int mask = _mm256_movemask_epi8(m0); //unsigned is important!
if (mask == 0){
continue;
}
if ((mask & 0xffff) == 0)
{
j -= 16;
ptr -= 16;
continue;
}
__m256i c0 = _mm256_setzero_si256(), c1 = c0, max0 = c0, max1 = c0;
for (int k = 0; k < 25; k++)
{
__m256i x = _mm256_xor_si256(_mm256_loadu_si256((const __m256i*)(ptr + pixel[k])), delta256);
m0 = _mm256_cmpgt_epi8(x, v0);
m1 = _mm256_cmpgt_epi8(v1, x);
c0 = _mm256_and_si256(_mm256_sub_epi8(c0, m0), m0);
c1 = _mm256_and_si256(_mm256_sub_epi8(c1, m1), m1);
max0 = _mm256_max_epu8(max0, c0);
max1 = _mm256_max_epu8(max1, c1);
}
max0 = _mm256_max_epu8(max0, max1);
unsigned int m = _mm256_movemask_epi8(_mm256_cmpgt_epi8(max0, K16_256));
for (int k = 0; m > 0 && k < 32; k++, m >>= 1)
if (m & 1)
{
cornerpos[ncorners++] = j + k;
if (nonmax_suppression)
{
short d[25];
for (int q = 0; q < 25; q++)
d[q] = (short)(ptr[k] - ptr[k + pixel[q]]);
v_int16x8 q0 = v_setall_s16(-1000), q1 = v_setall_s16(1000);
for (int q = 0; q < 16; q += 8)
{
v_int16x8 v0_ = v_load(d + q + 1);
v_int16x8 v1_ = v_load(d + q + 2);
v_int16x8 a = v_min(v0_, v1_);
v_int16x8 b = v_max(v0_, v1_);
v0_ = v_load(d + q + 3);
a = v_min(a, v0_);
b = v_max(b, v0_);
v0_ = v_load(d + q + 4);
a = v_min(a, v0_);
b = v_max(b, v0_);
v0_ = v_load(d + q + 5);
a = v_min(a, v0_);
b = v_max(b, v0_);
v0_ = v_load(d + q + 6);
a = v_min(a, v0_);
b = v_max(b, v0_);
v0_ = v_load(d + q + 7);
a = v_min(a, v0_);
b = v_max(b, v0_);
v0_ = v_load(d + q + 8);
a = v_min(a, v0_);
b = v_max(b, v0_);
v0_ = v_load(d + q);
q0 = v_max(q0, v_min(a, v0_));
q1 = v_min(q1, v_max(b, v0_));
v0_ = v_load(d + q + 9);
q0 = v_max(q0, v_min(a, v0_));
q1 = v_min(q1, v_max(b, v0_));
}
q0 = v_max(q0, v_sub(v_setzero_s16(), q1));
curr[j + k] = (uchar)(v_reduce_max(q0) - 1);
}
}
}
_mm256_zeroupper();
}
virtual ~FAST_t_patternSize16_AVX2_Impl() CV_OVERRIDE {}
private:
int cols;
char t256c;
int threshold;
bool nonmax_suppression;
const int* pixel;
};
Ptr<FAST_t_patternSize16_AVX2> FAST_t_patternSize16_AVX2::getImpl(int _cols, int _threshold, bool _nonmax_suppression, const int* _pixel)
{
return Ptr<FAST_t_patternSize16_AVX2>(new FAST_t_patternSize16_AVX2_Impl(_cols, _threshold, _nonmax_suppression, _pixel));
}
}
}
+583
View File
@@ -0,0 +1,583 @@
/* This is FAST corner detector, contributed to OpenCV by the author, Edward Rosten.
Below is the original copyright and the references */
/*
Copyright (c) 2006, 2008 Edward Rosten
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
*Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
*Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
*Neither the name of the University of Cambridge nor the names of
its contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
The references are:
* Machine learning for high-speed corner detection,
E. Rosten and T. Drummond, ECCV 2006
* Faster and better: A machine learning approach to corner detection
E. Rosten, R. Porter and T. Drummond, PAMI, 2009
*/
#include "precomp.hpp"
#include "fast.hpp"
#include "fast_score.hpp"
#include "opencl_kernels_features.hpp"
#include "hal_replacement.hpp"
#include "opencv2/core/hal/intrin.hpp"
#include "opencv2/core/utils/buffer_area.private.hpp"
namespace cv
{
template<int patternSize>
void FAST_t(InputArray _img, std::vector<KeyPoint>& keypoints, int threshold, bool nonmax_suppression)
{
Mat img = _img.getMat();
const int K = patternSize/2, N = patternSize + K + 1;
int i, j, k, pixel[25];
makeOffsets(pixel, (int)img.step, patternSize);
#if CV_SIMD128
const int quarterPatternSize = patternSize/4;
v_uint8x16 delta = v_setall_u8(0x80), t = v_setall_u8((char)threshold), K16 = v_setall_u8((char)K);
#if CV_TRY_AVX2
Ptr<opt_AVX2::FAST_t_patternSize16_AVX2> fast_t_impl_avx2;
if(CV_CPU_HAS_SUPPORT_AVX2)
fast_t_impl_avx2 = opt_AVX2::FAST_t_patternSize16_AVX2::getImpl(img.cols, threshold, nonmax_suppression, pixel);
#endif
#endif
keypoints.clear();
threshold = std::min(std::max(threshold, 0), 255);
uchar threshold_tab[512];
for( i = -255; i <= 255; i++ )
threshold_tab[i+255] = (uchar)(i < -threshold ? 1 : i > threshold ? 2 : 0);
uchar* buf[3] = { 0 };
int* cpbuf[3] = { 0 };
utils::BufferArea area;
for (unsigned idx = 0; idx < 3; ++idx)
{
area.allocate(buf[idx], img.cols);
area.allocate(cpbuf[idx], img.cols + 1);
}
area.commit();
for (unsigned idx = 0; idx < 3; ++idx)
{
memset(buf[idx], 0, img.cols);
}
for(i = 3; i < img.rows-2; i++)
{
const uchar* ptr = img.ptr<uchar>(i) + 3;
uchar* curr = buf[(i - 3)%3];
int* cornerpos = cpbuf[(i - 3)%3] + 1; // cornerpos[-1] is used to store a value
memset(curr, 0, img.cols);
int ncorners = 0;
if( i < img.rows - 3 )
{
j = 3;
#if CV_SIMD128
{
if( patternSize == 16 )
{
#if CV_TRY_AVX2
if (fast_t_impl_avx2)
fast_t_impl_avx2->process(j, ptr, curr, cornerpos, ncorners);
#endif
//vz if (j <= (img.cols - 27)) //it doesn't make sense using vectors for less than 8 elements
{
for (; j < img.cols - 16 - 3; j += 16, ptr += 16)
{
v_uint8x16 v = v_load(ptr);
v_int8x16 v0 = v_reinterpret_as_s8(v_xor(v_add(v, t), delta));
v_int8x16 v1 = v_reinterpret_as_s8(v_xor(v_sub(v, t), delta));
v_int8x16 x0 = v_reinterpret_as_s8(v_sub_wrap(v_load(ptr + pixel[0]), delta));
v_int8x16 x1 = v_reinterpret_as_s8(v_sub_wrap(v_load(ptr + pixel[quarterPatternSize]), delta));
v_int8x16 x2 = v_reinterpret_as_s8(v_sub_wrap(v_load(ptr + pixel[2*quarterPatternSize]), delta));
v_int8x16 x3 = v_reinterpret_as_s8(v_sub_wrap(v_load(ptr + pixel[3*quarterPatternSize]), delta));
v_int8x16 m0, m1;
m0 = v_and(v_lt(v0, x0), v_lt(v0, x1));
m1 = v_and(v_lt(x0, v1), v_lt(x1, v1));
m0 = v_or(m0, v_and(v_lt(v0, x1), v_lt(v0, x2)));
m1 = v_or(m1, v_and(v_lt(x1, v1), v_lt(x2, v1)));
m0 = v_or(m0, v_and(v_lt(v0, x2), v_lt(v0, x3)));
m1 = v_or(m1, v_and(v_lt(x2, v1), v_lt(x3, v1)));
m0 = v_or(m0, v_and(v_lt(v0, x3), v_lt(v0, x0)));
m1 = v_or(m1, v_and(v_lt(x3, v1), v_lt(x0, v1)));
m0 = v_or(m0, m1);
if( !v_check_any(m0) )
continue;
if( !v_check_any(v_combine_low(m0, m0)) )
{
j -= 8;
ptr -= 8;
continue;
}
v_int8x16 c0 = v_setzero_s8();
v_int8x16 c1 = v_setzero_s8();
v_uint8x16 max0 = v_setzero_u8();
v_uint8x16 max1 = v_setzero_u8();
for( k = 0; k < N; k++ )
{
v_int8x16 x = v_reinterpret_as_s8(v_xor(v_load((ptr + pixel[k])), delta));
m0 = v_lt(v0, x);
m1 = v_lt(x, v1);
c0 = v_and(v_sub_wrap(c0, m0), m0);
c1 = v_and(v_sub_wrap(c1, m1), m1);
max0 = v_max(max0, v_reinterpret_as_u8(c0));
max1 = v_max(max1, v_reinterpret_as_u8(c1));
}
max0 = v_lt(K16, v_max(max0, max1));
unsigned int m = v_signmask(v_reinterpret_as_s8(max0));
for( k = 0; m > 0 && k < 16; k++, m >>= 1 )
{
if( m & 1 )
{
cornerpos[ncorners++] = j+k;
if(nonmax_suppression)
{
short d[25];
int _k = 0;
#if CV_ENABLE_UNROLLED
for (; _k + 4 < 25; _k += 5)
{
d[_k] = (short)(ptr[k] - ptr[k + pixel[_k]]);
d[_k + 1] = (short)(ptr[k] - ptr[k + pixel[_k + 1]]);
d[_k + 2] = (short)(ptr[k] - ptr[k + pixel[_k + 2]]);
d[_k + 3] = (short)(ptr[k] - ptr[k + pixel[_k + 3]]);
d[_k + 4] = (short)(ptr[k] - ptr[k + pixel[_k + 4]]);
}
#else
for ( ; _k < 25; _k++)
d[_k] = (short)(ptr[k] - ptr[k + pixel[_k]]);
#endif
v_int16x8 a0, b0, a1, b1;
a0 = b0 = a1 = b1 = v_load(d + 8);
for(int shift = 0; shift < 8; ++shift)
{
v_int16x8 v_nms = v_load(d + shift);
a0 = v_min(a0, v_nms);
b0 = v_max(b0, v_nms);
v_nms = v_load(d + 9 + shift);
a1 = v_min(a1, v_nms);
b1 = v_max(b1, v_nms);
}
curr[j + k] = (uchar)(v_reduce_max(v_max(v_max(a0, a1), v_sub(v_setzero_s16(), v_min(b0, b1)))) - 1);
}
}
}
}
}
}
}
#endif
for( ; j < img.cols - 3; j++, ptr++ )
{
int v = ptr[0];
const uchar* tab = &threshold_tab[0] - v + 255;
int d = tab[ptr[pixel[0]]] | tab[ptr[pixel[8]]];
if( d == 0 )
continue;
d &= tab[ptr[pixel[2]]] | tab[ptr[pixel[10]]];
d &= tab[ptr[pixel[4]]] | tab[ptr[pixel[12]]];
d &= tab[ptr[pixel[6]]] | tab[ptr[pixel[14]]];
if( d == 0 )
continue;
d &= tab[ptr[pixel[1]]] | tab[ptr[pixel[9]]];
d &= tab[ptr[pixel[3]]] | tab[ptr[pixel[11]]];
d &= tab[ptr[pixel[5]]] | tab[ptr[pixel[13]]];
d &= tab[ptr[pixel[7]]] | tab[ptr[pixel[15]]];
if( d & 1 )
{
int vt = v - threshold, count = 0;
for( k = 0; k < N; k++ )
{
int x = ptr[pixel[k]];
if(x < vt)
{
if( ++count > K )
{
cornerpos[ncorners++] = j;
if(nonmax_suppression)
curr[j] = (uchar)cornerScore<patternSize>(ptr, pixel, threshold);
break;
}
}
else
count = 0;
}
}
if( d & 2 )
{
int vt = v + threshold, count = 0;
for( k = 0; k < N; k++ )
{
int x = ptr[pixel[k]];
if(x > vt)
{
if( ++count > K )
{
cornerpos[ncorners++] = j;
if(nonmax_suppression)
curr[j] = (uchar)cornerScore<patternSize>(ptr, pixel, threshold);
break;
}
}
else
count = 0;
}
}
}
}
cornerpos[-1] = ncorners;
if( i == 3 )
continue;
const uchar* prev = buf[(i - 4 + 3)%3];
const uchar* pprev = buf[(i - 5 + 3)%3];
cornerpos = cpbuf[(i - 4 + 3)%3] + 1; // cornerpos[-1] is used to store a value
ncorners = cornerpos[-1];
for( k = 0; k < ncorners; k++ )
{
j = cornerpos[k];
int score = prev[j];
if( !nonmax_suppression ||
(score > prev[j+1] && score > prev[j-1] &&
score > pprev[j-1] && score > pprev[j] && score > pprev[j+1] &&
score > curr[j-1] && score > curr[j] && score > curr[j+1]) )
{
keypoints.push_back(KeyPoint((float)j, (float)(i-1), 7.f, -1, (float)score));
}
}
}
}
#ifdef HAVE_OPENCL
template<typename pt>
struct cmp_pt
{
bool operator ()(const pt& a, const pt& b) const { return a.y < b.y || (a.y == b.y && a.x < b.x); }
};
static bool ocl_FAST( InputArray _img, std::vector<KeyPoint>& keypoints,
int threshold, bool nonmax_suppression, int maxKeypoints )
{
UMat img = _img.getUMat();
if( img.cols < 7 || img.rows < 7 )
return false;
size_t globalsize[] = { (size_t)img.cols-6, (size_t)img.rows-6 };
ocl::Kernel fastKptKernel("FAST_findKeypoints", ocl::features::fast_oclsrc);
if (fastKptKernel.empty())
return false;
UMat kp1(1, maxKeypoints*2+1, CV_32S);
UMat ucounter1(kp1, Rect(0,0,1,1));
ucounter1.setTo(Scalar::all(0));
if( !fastKptKernel.args(ocl::KernelArg::ReadOnly(img),
ocl::KernelArg::PtrReadWrite(kp1),
maxKeypoints, threshold).run(2, globalsize, 0, true))
return false;
Mat mcounter;
ucounter1.copyTo(mcounter);
int i, counter = mcounter.at<int>(0);
counter = std::min(counter, maxKeypoints);
keypoints.clear();
if( counter == 0 )
return true;
if( !nonmax_suppression )
{
Mat m;
kp1(Rect(0, 0, counter*2+1, 1)).copyTo(m);
const Point* pt = (const Point*)(m.ptr<int>() + 1);
for( i = 0; i < counter; i++ )
keypoints.push_back(KeyPoint((float)pt[i].x, (float)pt[i].y, 7.f, -1, 1.f));
}
else
{
UMat kp2(1, maxKeypoints*3+1, CV_32S);
UMat ucounter2 = kp2(Rect(0,0,1,1));
ucounter2.setTo(Scalar::all(0));
ocl::Kernel fastNMSKernel("FAST_nonmaxSupression", ocl::features::fast_oclsrc);
if (fastNMSKernel.empty())
return false;
size_t globalsize_nms[] = { (size_t)counter };
if( !fastNMSKernel.args(ocl::KernelArg::PtrReadOnly(kp1),
ocl::KernelArg::PtrReadWrite(kp2),
ocl::KernelArg::ReadOnly(img),
counter, counter).run(1, globalsize_nms, 0, true))
return false;
Mat m2;
kp2(Rect(0, 0, counter*3+1, 1)).copyTo(m2);
Point3i* pt2 = (Point3i*)(m2.ptr<int>() + 1);
int newcounter = std::min(m2.at<int>(0), counter);
std::sort(pt2, pt2 + newcounter, cmp_pt<Point3i>());
for( i = 0; i < newcounter; i++ )
keypoints.push_back(KeyPoint((float)pt2[i].x, (float)pt2[i].y, 7.f, -1, (float)pt2[i].z));
}
return true;
}
#endif
static inline int hal_FAST(cv::Mat& src, std::vector<KeyPoint>& keypoints, int threshold, bool nonmax_suppression, FastFeatureDetector::DetectorType type)
{
if (threshold > 20)
return CV_HAL_ERROR_NOT_IMPLEMENTED;
cv::Mat scores(src.size(), src.type());
int error = cv_hal_FAST_dense(src.data, src.step, scores.data, scores.step, src.cols, src.rows, type);
if (error != CV_HAL_ERROR_OK)
return error;
cv::Mat suppressedScores(src.size(), src.type());
if (nonmax_suppression)
{
error = cv_hal_FAST_NMS(scores.data, scores.step, suppressedScores.data, suppressedScores.step, scores.cols, scores.rows);
if (error != CV_HAL_ERROR_OK)
return error;
}
else
{
suppressedScores = scores;
}
if (!threshold && nonmax_suppression) threshold = 1;
cv::KeyPoint kpt(0, 0, 7.f, -1, 0);
unsigned uthreshold = (unsigned) threshold;
int ofs = 3;
int stride = (int)suppressedScores.step;
const unsigned char* pscore = suppressedScores.data;
keypoints.clear();
for (int y = ofs; y + ofs < suppressedScores.rows; ++y)
{
kpt.pt.y = (float)(y);
for (int x = ofs; x + ofs < suppressedScores.cols; ++x)
{
unsigned score = pscore[y * stride + x];
if (score > uthreshold)
{
kpt.pt.x = (float)(x);
kpt.response = (nonmax_suppression != 0) ? (float)((int)score - 1) : 0.f;
keypoints.push_back(kpt);
}
}
}
return CV_HAL_ERROR_OK;
}
void FAST(InputArray _img, std::vector<KeyPoint>& keypoints, int threshold, bool nonmax_suppression, FastFeatureDetector::DetectorType type)
{
CV_INSTRUMENT_REGION();
const size_t max_fast_features = std::max(_img.total()/100, size_t(1000)); // Simple heuristic that depends on resolution.
CV_OCL_RUN(_img.isUMat() && type == FastFeatureDetector::TYPE_9_16,
ocl_FAST(_img, keypoints, threshold, nonmax_suppression, (int)max_fast_features));
cv::Mat img = _img.getMat();
CALL_HAL(fast_dense, hal_FAST, img, keypoints, threshold, nonmax_suppression, type);
size_t keypoints_count = 1;
keypoints.clear();
KeyPoint* kps = (KeyPoint*)malloc(sizeof(KeyPoint) * keypoints_count);
int hal_ret = cv_hal_FASTv2(img.data, img.step, img.cols, img.rows, (void**)&kps,
&keypoints_count, threshold, nonmax_suppression, type, realloc);
if (hal_ret == CV_HAL_ERROR_OK) {
keypoints.assign(kps, kps + keypoints_count);
free(kps);
return;
} else {
free(kps);
keypoints_count = max_fast_features;
keypoints.clear();
keypoints.resize(keypoints_count);
CALL_HAL(fast, cv_hal_FAST, img.data, img.step, img.cols, img.rows,
(uchar*)(keypoints.data()), &keypoints_count, threshold, nonmax_suppression, type);
}
switch(type) {
case FastFeatureDetector::TYPE_5_8:
FAST_t<8>(_img, keypoints, threshold, nonmax_suppression);
break;
case FastFeatureDetector::TYPE_7_12:
FAST_t<12>(_img, keypoints, threshold, nonmax_suppression);
break;
case FastFeatureDetector::TYPE_9_16:
FAST_t<16>(_img, keypoints, threshold, nonmax_suppression);
break;
}
}
class FastFeatureDetector_Impl CV_FINAL : public FastFeatureDetector
{
public:
FastFeatureDetector_Impl( int _threshold, bool _nonmaxSuppression, FastFeatureDetector::DetectorType _type )
: threshold(_threshold), nonmaxSuppression(_nonmaxSuppression), type(_type)
{}
void read( const FileNode& fn) CV_OVERRIDE
{
// if node is empty, keep previous value
if (!fn["threshold"].empty())
fn["threshold"] >> threshold;
if (!fn["nonmaxSuppression"].empty())
fn["nonmaxSuppression"] >> nonmaxSuppression;
if (!fn["type"].empty())
fn["type"] >> type;
}
void write( FileStorage& fs) const CV_OVERRIDE
{
if(fs.isOpened())
{
fs << "name" << getDefaultName();
fs << "threshold" << threshold;
fs << "nonmaxSuppression" << nonmaxSuppression;
fs << "type" << type;
}
}
void detect( InputArray _image, std::vector<KeyPoint>& keypoints, InputArray _mask ) CV_OVERRIDE
{
CV_INSTRUMENT_REGION();
if(_image.empty())
{
keypoints.clear();
return;
}
Mat mask = _mask.getMat(), grayImage;
UMat ugrayImage;
_InputArray gray = _image;
if( _image.type() != CV_8U )
{
_OutputArray ogray = _image.isUMat() ? _OutputArray(ugrayImage) : _OutputArray(grayImage);
cvtColor( _image, ogray, COLOR_BGR2GRAY );
gray = ogray;
}
FAST( gray, keypoints, threshold, nonmaxSuppression, type );
KeyPointsFilter::runByPixelsMask( keypoints, mask );
}
void set(int prop, double value)
{
if(prop == THRESHOLD)
threshold = cvRound(value);
else if(prop == NONMAX_SUPPRESSION)
nonmaxSuppression = value != 0;
else if(prop == FAST_N)
type = static_cast<FastFeatureDetector::DetectorType>(cvRound(value));
else
CV_Error(Error::StsBadArg, "");
}
double get(int prop) const
{
if(prop == THRESHOLD)
return threshold;
if(prop == NONMAX_SUPPRESSION)
return nonmaxSuppression;
if(prop == FAST_N)
return static_cast<int>(type);
CV_Error(Error::StsBadArg, "");
return 0;
}
void setThreshold(int threshold_) CV_OVERRIDE { threshold = threshold_; }
int getThreshold() const CV_OVERRIDE { return threshold; }
void setNonmaxSuppression(bool f) CV_OVERRIDE { nonmaxSuppression = f; }
bool getNonmaxSuppression() const CV_OVERRIDE { return nonmaxSuppression; }
void setType(FastFeatureDetector::DetectorType type_) CV_OVERRIDE{ type = type_; }
FastFeatureDetector::DetectorType getType() const CV_OVERRIDE{ return type; }
int threshold;
bool nonmaxSuppression;
FastFeatureDetector::DetectorType type;
};
Ptr<FastFeatureDetector> FastFeatureDetector::create( int threshold, bool nonmaxSuppression, FastFeatureDetector::DetectorType type )
{
return makePtr<FastFeatureDetector_Impl>(threshold, nonmaxSuppression, type);
}
String FastFeatureDetector::getDefaultName() const
{
return (Feature2D::getDefaultName() + ".FastFeatureDetector");
}
}
+62
View File
@@ -0,0 +1,62 @@
/* This is FAST corner detector, contributed to OpenCV by the author, Edward Rosten.
Below is the original copyright and the references */
/*
Copyright (c) 2006, 2008 Edward Rosten
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
*Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
*Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
*Neither the name of the University of Cambridge nor the names of
its contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
The references are:
* Machine learning for high-speed corner detection,
E. Rosten and T. Drummond, ECCV 2006
* Faster and better: A machine learning approach to corner detection
E. Rosten, R. Porter and T. Drummond, PAMI, 2009
*/
#ifndef OPENCV_FEATURES_FAST_HPP
#define OPENCV_FEATURES_FAST_HPP
namespace cv
{
namespace opt_AVX2
{
#if CV_TRY_AVX2
class FAST_t_patternSize16_AVX2
{
public:
static Ptr<FAST_t_patternSize16_AVX2> getImpl(int _cols, int _threshold, bool _nonmax_suppression, const int* _pixel);
virtual void process(int &j, const uchar* &ptr, uchar* curr, int* cornerpos, int &ncorners) = 0;
virtual ~FAST_t_patternSize16_AVX2() {}
};
#endif
}
}
#endif
+366
View File
@@ -0,0 +1,366 @@
/* This is FAST corner detector, contributed to OpenCV by the author, Edward Rosten.
Below is the original copyright and the references */
/*
Copyright (c) 2006, 2008 Edward Rosten
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
*Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
*Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
*Neither the name of the University of Cambridge nor the names of
its contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
The references are:
* Machine learning for high-speed corner detection,
E. Rosten and T. Drummond, ECCV 2006
* Faster and better: A machine learning approach to corner detection
E. Rosten, R. Porter and T. Drummond, PAMI, 2009
*/
#include "fast_score.hpp"
#include "opencv2/core/hal/intrin.hpp"
#define VERIFY_CORNERS 0
namespace cv {
void makeOffsets(int pixel[25], int rowStride, int patternSize)
{
static const int offsets16[][2] =
{
{0, 3}, { 1, 3}, { 2, 2}, { 3, 1}, { 3, 0}, { 3, -1}, { 2, -2}, { 1, -3},
{0, -3}, {-1, -3}, {-2, -2}, {-3, -1}, {-3, 0}, {-3, 1}, {-2, 2}, {-1, 3}
};
static const int offsets12[][2] =
{
{0, 2}, { 1, 2}, { 2, 1}, { 2, 0}, { 2, -1}, { 1, -2},
{0, -2}, {-1, -2}, {-2, -1}, {-2, 0}, {-2, 1}, {-1, 2}
};
static const int offsets8[][2] =
{
{0, 1}, { 1, 1}, { 1, 0}, { 1, -1},
{0, -1}, {-1, -1}, {-1, 0}, {-1, 1}
};
const int (*offsets)[2] = patternSize == 16 ? offsets16 :
patternSize == 12 ? offsets12 :
patternSize == 8 ? offsets8 : 0;
CV_Assert(pixel && offsets);
int k = 0;
for( ; k < patternSize; k++ )
pixel[k] = offsets[k][0] + offsets[k][1] * rowStride;
for( ; k < 25; k++ )
pixel[k] = pixel[k - patternSize];
}
#if VERIFY_CORNERS
static void testCorner(const uchar* ptr, const int pixel[], int K, int N, int threshold) {
// check that with the computed "threshold" the pixel is still a corner
// and that with the increased-by-1 "threshold" the pixel is not a corner anymore
for( int delta = 0; delta <= 1; delta++ )
{
int v0 = std::min(ptr[0] + threshold + delta, 255);
int v1 = std::max(ptr[0] - threshold - delta, 0);
int c0 = 0, c1 = 0;
for( int k = 0; k < N; k++ )
{
int x = ptr[pixel[k]];
if(x > v0)
{
if( ++c0 > K )
break;
c1 = 0;
}
else if( x < v1 )
{
if( ++c1 > K )
break;
c0 = 0;
}
else
{
c0 = c1 = 0;
}
}
CV_Assert( (delta == 0 && std::max(c0, c1) > K) ||
(delta == 1 && std::max(c0, c1) <= K) );
}
}
#endif
template<>
int cornerScore<16>(const uchar* ptr, const int pixel[], int threshold)
{
const int K = 8, N = K*3 + 1;
int k, v = ptr[0];
short d[N];
for( k = 0; k < N; k++ )
d[k] = (short)(v - ptr[pixel[k]]);
#if CV_SIMD128
if (true)
{
v_int16x8 q0 = v_setall_s16(-1000), q1 = v_setall_s16(1000);
for (k = 0; k < 16; k += 8)
{
v_int16x8 v0 = v_load(d + k + 1);
v_int16x8 v1 = v_load(d + k + 2);
v_int16x8 a = v_min(v0, v1);
v_int16x8 b = v_max(v0, v1);
v0 = v_load(d + k + 3);
a = v_min(a, v0);
b = v_max(b, v0);
v0 = v_load(d + k + 4);
a = v_min(a, v0);
b = v_max(b, v0);
v0 = v_load(d + k + 5);
a = v_min(a, v0);
b = v_max(b, v0);
v0 = v_load(d + k + 6);
a = v_min(a, v0);
b = v_max(b, v0);
v0 = v_load(d + k + 7);
a = v_min(a, v0);
b = v_max(b, v0);
v0 = v_load(d + k + 8);
a = v_min(a, v0);
b = v_max(b, v0);
v0 = v_load(d + k);
q0 = v_max(q0, v_min(a, v0));
q1 = v_min(q1, v_max(b, v0));
v0 = v_load(d + k + 9);
q0 = v_max(q0, v_min(a, v0));
q1 = v_min(q1, v_max(b, v0));
}
q0 = v_max(q0, v_sub(v_setzero_s16(), q1));
threshold = v_reduce_max(q0) - 1;
}
else
#endif
{
int a0 = threshold;
for( k = 0; k < 16; k += 2 )
{
int a = std::min((int)d[k+1], (int)d[k+2]);
a = std::min(a, (int)d[k+3]);
if( a <= a0 )
continue;
a = std::min(a, (int)d[k+4]);
a = std::min(a, (int)d[k+5]);
a = std::min(a, (int)d[k+6]);
a = std::min(a, (int)d[k+7]);
a = std::min(a, (int)d[k+8]);
a0 = std::max(a0, std::min(a, (int)d[k]));
a0 = std::max(a0, std::min(a, (int)d[k+9]));
}
int b0 = -a0;
for( k = 0; k < 16; k += 2 )
{
int b = std::max((int)d[k+1], (int)d[k+2]);
b = std::max(b, (int)d[k+3]);
b = std::max(b, (int)d[k+4]);
b = std::max(b, (int)d[k+5]);
if( b >= b0 )
continue;
b = std::max(b, (int)d[k+6]);
b = std::max(b, (int)d[k+7]);
b = std::max(b, (int)d[k+8]);
b0 = std::min(b0, std::max(b, (int)d[k]));
b0 = std::min(b0, std::max(b, (int)d[k+9]));
}
threshold = -b0 - 1;
}
#if VERIFY_CORNERS
testCorner(ptr, pixel, K, N, threshold);
#endif
return threshold;
}
template<>
int cornerScore<12>(const uchar* ptr, const int pixel[], int threshold)
{
const int K = 6, N = K*3 + 1;
int k, v = ptr[0];
short d[N + 4];
for( k = 0; k < N; k++ )
d[k] = (short)(v - ptr[pixel[k]]);
#if CV_SIMD128
for( k = 0; k < 4; k++ )
d[N+k] = d[k];
#endif
#if CV_SIMD128
if (true)
{
v_int16x8 q0 = v_setall_s16(-1000), q1 = v_setall_s16(1000);
for (k = 0; k < 16; k += 8)
{
v_int16x8 v0 = v_load(d + k + 1);
v_int16x8 v1 = v_load(d + k + 2);
v_int16x8 a = v_min(v0, v1);
v_int16x8 b = v_max(v0, v1);
v0 = v_load(d + k + 3);
a = v_min(a, v0);
b = v_max(b, v0);
v0 = v_load(d + k + 4);
a = v_min(a, v0);
b = v_max(b, v0);
v0 = v_load(d + k + 5);
a = v_min(a, v0);
b = v_max(b, v0);
v0 = v_load(d + k + 6);
a = v_min(a, v0);
b = v_max(b, v0);
v0 = v_load(d + k);
q0 = v_max(q0, v_min(a, v0));
q1 = v_min(q1, v_max(b, v0));
v0 = v_load(d + k + 7);
q0 = v_max(q0, v_min(a, v0));
q1 = v_min(q1, v_max(b, v0));
}
q0 = v_max(q0, v_sub(v_setzero_s16(), q1));
threshold = v_reduce_max(q0) - 1;
}
else
#endif
{
int a0 = threshold;
for( k = 0; k < 12; k += 2 )
{
int a = std::min((int)d[k+1], (int)d[k+2]);
if( a <= a0 )
continue;
a = std::min(a, (int)d[k+3]);
a = std::min(a, (int)d[k+4]);
a = std::min(a, (int)d[k+5]);
a = std::min(a, (int)d[k+6]);
a0 = std::max(a0, std::min(a, (int)d[k]));
a0 = std::max(a0, std::min(a, (int)d[k+7]));
}
int b0 = -a0;
for( k = 0; k < 12; k += 2 )
{
int b = std::max((int)d[k+1], (int)d[k+2]);
b = std::max(b, (int)d[k+3]);
b = std::max(b, (int)d[k+4]);
if( b >= b0 )
continue;
b = std::max(b, (int)d[k+5]);
b = std::max(b, (int)d[k+6]);
b0 = std::min(b0, std::max(b, (int)d[k]));
b0 = std::min(b0, std::max(b, (int)d[k+7]));
}
threshold = -b0-1;
}
#if VERIFY_CORNERS
testCorner(ptr, pixel, K, N, threshold);
#endif
return threshold;
}
template<>
int cornerScore<8>(const uchar* ptr, const int pixel[], int threshold)
{
const int K = 4, N = K * 3 + 1;
int k, v = ptr[0];
short d[N];
for (k = 0; k < N; k++)
d[k] = (short)(v - ptr[pixel[k]]);
#if CV_SIMD128 \
&& (!defined(CV_SIMD128_CPP) || (!defined(__GNUC__) || __GNUC__ != 5)) // "movdqa" bug on "v_load(d + 1)" line (Ubuntu 16.04 + GCC 5.4)
if (true)
{
v_int16x8 v0 = v_load(d + 1);
v_int16x8 v1 = v_load(d + 2);
v_int16x8 a = v_min(v0, v1);
v_int16x8 b = v_max(v0, v1);
v0 = v_load(d + 3);
a = v_min(a, v0);
b = v_max(b, v0);
v0 = v_load(d + 4);
a = v_min(a, v0);
b = v_max(b, v0);
v0 = v_load(d);
v_int16x8 q0 = v_min(a, v0);
v_int16x8 q1 = v_max(b, v0);
v0 = v_load(d + 5);
q0 = v_max(q0, v_min(a, v0));
q1 = v_min(q1, v_max(b, v0));
q0 = v_max(q0, v_sub(v_setzero_s16(), q1));
threshold = v_reduce_max(q0) - 1;
}
else
#endif
{
int a0 = threshold;
for( k = 0; k < 8; k += 2 )
{
int a = std::min((int)d[k+1], (int)d[k+2]);
if( a <= a0 )
continue;
a = std::min(a, (int)d[k+3]);
a = std::min(a, (int)d[k+4]);
a0 = std::max(a0, std::min(a, (int)d[k]));
a0 = std::max(a0, std::min(a, (int)d[k+5]));
}
int b0 = -a0;
for( k = 0; k < 8; k += 2 )
{
int b = std::max((int)d[k+1], (int)d[k+2]);
b = std::max(b, (int)d[k+3]);
if( b >= b0 )
continue;
b = std::max(b, (int)d[k+4]);
b0 = std::min(b0, std::max(b, (int)d[k]));
b0 = std::min(b0, std::max(b, (int)d[k+5]));
}
threshold = -b0-1;
}
#if VERIFY_CORNERS
testCorner(ptr, pixel, K, N, threshold);
#endif
return threshold;
}
} // namespace cv
+62
View File
@@ -0,0 +1,62 @@
/* This is FAST corner detector, contributed to OpenCV by the author, Edward Rosten.
Below is the original copyright and the references */
/*
Copyright (c) 2006, 2008 Edward Rosten
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
*Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
*Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
*Neither the name of the University of Cambridge nor the names of
its contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
The references are:
* Machine learning for high-speed corner detection,
E. Rosten and T. Drummond, ECCV 2006
* Faster and better: A machine learning approach to corner detection
E. Rosten, R. Porter and T. Drummond, PAMI, 2009
*/
#ifndef __OPENCV_FEATURES_2D_FAST_HPP__
#define __OPENCV_FEATURES_2D_FAST_HPP__
#ifdef __cplusplus
#include "precomp.hpp"
namespace cv
{
void makeOffsets(int pixel[25], int row_stride, int patternSize);
template<int patternSize>
int cornerScore(const uchar* ptr, const int pixel[], int threshold);
}
#endif
#endif
+224
View File
@@ -0,0 +1,224 @@
/*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"
namespace cv
{
using std::vector;
Feature2D::~Feature2D() {}
/*
* Detect keypoints in an image.
* image The image.
* keypoints The detected keypoints.
* mask Mask specifying where to look for keypoints (optional). Must be a char
* matrix with non-zero values in the region of interest.
*/
void Feature2D::detect( InputArray image,
std::vector<KeyPoint>& keypoints,
InputArray mask )
{
CV_INSTRUMENT_REGION();
if( image.empty() )
{
keypoints.clear();
return;
}
detectAndCompute(image, mask, keypoints, noArray(), false);
}
void Feature2D::detect( InputArrayOfArrays images,
std::vector<std::vector<KeyPoint> >& keypoints,
InputArrayOfArrays masks )
{
CV_INSTRUMENT_REGION();
int nimages = (int)images.total();
if (!masks.empty())
{
CV_Assert(masks.total() == (size_t)nimages);
}
keypoints.resize(nimages);
if (images.isMatVector())
{
for (int i = 0; i < nimages; i++)
{
detect(images.getMat(i), keypoints[i], masks.empty() ? noArray() : masks.getMat(i));
}
}
else
{
// assume UMats
for (int i = 0; i < nimages; i++)
{
detect(images.getUMat(i), keypoints[i], masks.empty() ? noArray() : masks.getUMat(i));
}
}
}
/*
* Compute the descriptors for a set of keypoints in an image.
* image The image.
* keypoints The input keypoints. Keypoints for which a descriptor cannot be computed are removed.
* descriptors Copmputed descriptors. Row i is the descriptor for keypoint i.
*/
void Feature2D::compute( InputArray image,
std::vector<KeyPoint>& keypoints,
OutputArray descriptors )
{
CV_INSTRUMENT_REGION();
if( image.empty() )
{
descriptors.release();
return;
}
detectAndCompute(image, noArray(), keypoints, descriptors, true);
}
void Feature2D::compute( InputArrayOfArrays images,
std::vector<std::vector<KeyPoint> >& keypoints,
OutputArrayOfArrays descriptors )
{
CV_INSTRUMENT_REGION();
if( !descriptors.needed() )
return;
int nimages = (int)images.total();
CV_Assert( keypoints.size() == (size_t)nimages );
// resize descriptors to appropriate size and compute
if (descriptors.isMatVector())
{
vector<Mat>& vec = *(vector<Mat>*)descriptors.getObj();
vec.resize(nimages);
for (int i = 0; i < nimages; i++)
{
compute(images.getMat(i), keypoints[i], vec[i]);
}
}
else if (descriptors.isUMatVector())
{
vector<UMat>& vec = *(vector<UMat>*)descriptors.getObj();
vec.resize(nimages);
for (int i = 0; i < nimages; i++)
{
compute(images.getUMat(i), keypoints[i], vec[i]);
}
}
else
{
CV_Error(Error::StsBadArg, "descriptors must be vector<Mat> or vector<UMat>");
}
}
/* Detects keypoints and computes the descriptors */
void Feature2D::detectAndCompute( InputArray, InputArray,
std::vector<KeyPoint>&,
OutputArray,
bool )
{
CV_INSTRUMENT_REGION();
CV_Error(Error::StsNotImplemented, "");
}
void Feature2D::write( const String& fileName ) const
{
FileStorage fs(fileName, FileStorage::WRITE);
write(fs);
}
void Feature2D::read( const String& fileName )
{
FileStorage fs(fileName, FileStorage::READ);
read(fs.root());
}
void Feature2D::write( FileStorage&) const
{
}
void Feature2D::read( const FileNode&)
{
}
int Feature2D::descriptorSize() const
{
return 0;
}
int Feature2D::descriptorType() const
{
return CV_32F;
}
int Feature2D::defaultNorm() const
{
int tp = descriptorType();
return tp == CV_8U ? NORM_HAMMING : NORM_L2;
}
// Return true if detector object is empty
bool Feature2D::empty() const
{
return true;
}
String Feature2D::getDefaultName() const
{
return "Feature2D";
}
}
+186
View File
@@ -0,0 +1,186 @@
// 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"
#ifdef HAVE_OPENCV_DNN
#include "opencv2/dnn.hpp"
#include "aliked_context.hpp"
#endif
namespace cv
{
ALIKED::ALIKED() {}
ALIKED::~ALIKED() {}
ALIKED::Params::Params()
{
inputSize = Size(640, 640);
normalizeDescriptors = true;
#ifdef HAVE_OPENCV_DNN
engine = dnn::ENGINE_NEW;
backend = dnn::DNN_BACKEND_DEFAULT;
target = dnn::DNN_TARGET_CPU;
#else
engine = -1;
backend = -1;
target = -1;
#endif
}
#ifdef HAVE_OPENCV_DNN
class ALIKEDImpl : public ALIKED
{
public:
ALIKEDImpl(const ALIKED::Params& _params, const String& modelPath)
: params(_params)
{
net = dnn::readNet(modelPath, "", "", static_cast<dnn::EngineType>(params.engine));
CV_Assert(!net.empty());
net.setPreferableBackend(params.backend);
net.setPreferableTarget(params.target);
}
ALIKEDImpl(const std::vector<uchar>& modelData, const ALIKED::Params& _params)
: params(_params)
{
net = dnn::readNetFromONNX(modelData);
CV_Assert(!net.empty());
net.setPreferableBackend(params.backend);
net.setPreferableTarget(params.target);
}
void detectAndCompute(InputArray image, InputArray mask,
std::vector<KeyPoint>& keypoints,
OutputArray descriptors,
bool useProvidedKeypoints) CV_OVERRIDE;
int descriptorSize() const CV_OVERRIDE;
int descriptorType() const CV_OVERRIDE;
int defaultNorm() const CV_OVERRIDE;
bool empty() const CV_OVERRIDE;
const ALIKEDContext& getLastContext() const { return lastContext; }
protected:
dnn::Net net;
ALIKED::Params params;
ALIKEDContext lastContext;
void runNetwork(InputArray image, std::vector<KeyPoint>& keypoints,
Mat& descriptors, Mat& scores);
};
void ALIKEDImpl::runNetwork(InputArray _image, std::vector<KeyPoint>& keypoints,
Mat& descriptors, Mat& scores)
{
Mat image = _image.getMat();
Size inputSz = params.inputSize;
Size origSize = image.size();
// BGR->RGB conversion via swapRB=true
Mat blob = dnn::blobFromImage(image, 1.0/255.0, inputSz, Scalar(), /*swapRB=*/true, /*crop=*/false);
net.setInput(blob, "image");
std::vector<String> outNames = {"keypoints", "descriptors", "scores"};
std::vector<Mat> outputs;
net.forward(outputs, outNames);
CV_Assert(outputs.size() == 3);
// ORT engine drops the batch dimension, so outputs are:
// keypoints: [N, 2] (not [1, N, 2])
// descriptors: [N, 128] (not [1, N, 128])
// scores: [N] (not [1, N, 1])
int N = outputs[0].rows;
Mat normKpts = outputs[0].reshape(0, N); // Nx2
Mat desc = outputs[1].reshape(0, N); // Nx128
Mat scr = outputs[2].reshape(0, N); // Nx1
// Store normalized keypoints for LightGlue context
lastContext.normalizedKeypoints = normKpts.clone();
lastContext.imageSize = origSize;
// Convert normalized [-1,1] coordinates to pixel coordinates
keypoints.resize(N);
for (int i = 0; i < N; i++)
{
float nx = normKpts.at<float>(i, 0);
float ny = normKpts.at<float>(i, 1);
float px = (nx + 1.0f) * 0.5f * (float)origSize.width;
float py = (ny + 1.0f) * 0.5f * (float)origSize.height;
float score = scr.at<float>(i, 0);
keypoints[i] = KeyPoint(px, py, 1.0f, -1.0f, score, 0, -1);
}
// Optionally L2-normalize descriptors
if (params.normalizeDescriptors)
{
for (int i = 0; i < N; i++)
{
Mat row = desc.row(i);
normalize(row, row);
}
}
descriptors = desc;
scores = scr;
}
void ALIKEDImpl::detectAndCompute(InputArray image, InputArray mask,
std::vector<KeyPoint>& keypoints,
OutputArray descriptors,
bool useProvidedKeypoints)
{
CV_INSTRUMENT_REGION();
CV_UNUSED(mask);
CV_UNUSED(useProvidedKeypoints);
if (image.empty())
{
keypoints.clear();
descriptors.release();
return;
}
Mat desc;
Mat sc;
runNetwork(image, keypoints, desc, sc);
if (descriptors.needed())
desc.copyTo(descriptors);
}
int ALIKEDImpl::descriptorSize() const { return 128; }
int ALIKEDImpl::descriptorType() const { return CV_32F; }
int ALIKEDImpl::defaultNorm() const { return NORM_L2; }
bool ALIKEDImpl::empty() const { return net.empty(); }
Ptr<ALIKED> ALIKED::create(const String& modelPath, const ALIKED::Params& params)
{
return makePtr<ALIKEDImpl>(params, modelPath);
}
Ptr<ALIKED> ALIKED::create(const std::vector<uchar>& modelData, const ALIKED::Params& params)
{
return makePtr<ALIKEDImpl>(modelData, params);
}
#else // !HAVE_OPENCV_DNN
Ptr<ALIKED> ALIKED::create(const String& modelPath, const ALIKED::Params& params)
{
CV_UNUSED(modelPath);
CV_UNUSED(params);
CV_Error(cv::Error::StsNotImplemented,
"ALIKED requires OpenCV built with opencv_dnn module!");
}
#endif // HAVE_OPENCV_DNN
} // namespace cv
+458
View File
@@ -0,0 +1,458 @@
/*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.
//
//
// Intel License Agreement
// For Open Source Computer Vision Library
//
// Copyright (C) 2000, 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 Intel Corporation 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 "opencl_kernels_features.hpp"
#include <cstdio>
#include <vector>
#include <iostream>
#include <functional>
namespace cv
{
struct greaterThanPtr
{
bool operator () (const float * a, const float * b) const
// Ensure a fully deterministic result of the sort
{ return (*a > *b) ? true : (*a < *b) ? false : (a > b); }
};
#ifdef HAVE_OPENCL
struct Corner
{
float val;
short y;
short x;
bool operator < (const Corner & c) const
// Ensure a fully deterministic result of the sort
{ return (val > c.val) ? true : (val < c.val) ? false : (y > c.y) ? true : (y < c.y) ? false : (x > c.x); }
};
static bool ocl_goodFeaturesToTrack( InputArray _image, OutputArray _corners,
int maxCorners, double qualityLevel, double minDistance,
InputArray _mask, OutputArray _cornersQuality, int blockSize, int gradientSize,
bool useHarrisDetector, double harrisK)
{
UMat eig, maxEigenValue;
if( useHarrisDetector )
cornerHarris( _image, eig, blockSize, gradientSize, harrisK );
else
cornerMinEigenVal( _image, eig, blockSize, gradientSize );
Size imgsize = _image.size();
size_t total, i, j, ncorners = 0, possibleCornersCount =
std::max(1024, static_cast<int>(imgsize.area() * 0.1));
bool haveMask = !_mask.empty();
UMat corners_buffer(1, (int)possibleCornersCount + 1, CV_32FC2);
CV_Assert(sizeof(Corner) == corners_buffer.elemSize());
Mat tmpCorners;
// find threshold
{
CV_Assert(eig.type() == CV_32FC1);
int dbsize = ocl::Device::getDefault().maxComputeUnits();
size_t wgs = ocl::Device::getDefault().maxWorkGroupSize();
int wgs2_aligned = 1;
while (wgs2_aligned < (int)wgs)
wgs2_aligned <<= 1;
wgs2_aligned >>= 1;
ocl::Kernel k("maxEigenVal", ocl::features::gftt_oclsrc,
format("-D OP_MAX_EIGEN_VAL -D WGS=%d -D groupnum=%d -D WGS2_ALIGNED=%d%s",
(int)wgs, dbsize, wgs2_aligned, haveMask ? " -D HAVE_MASK" : ""));
if (k.empty())
return false;
UMat mask = _mask.getUMat();
maxEigenValue.create(1, dbsize, CV_32FC1);
ocl::KernelArg eigarg = ocl::KernelArg::ReadOnlyNoSize(eig),
dbarg = ocl::KernelArg::PtrWriteOnly(maxEigenValue),
maskarg = ocl::KernelArg::ReadOnlyNoSize(mask),
cornersarg = ocl::KernelArg::PtrWriteOnly(corners_buffer);
if (haveMask)
k.args(eigarg, eig.cols, (int)eig.total(), dbarg, maskarg);
else
k.args(eigarg, eig.cols, (int)eig.total(), dbarg);
size_t globalsize = dbsize * wgs;
if (!k.run(1, &globalsize, &wgs, false))
return false;
ocl::Kernel k2("maxEigenValTask", ocl::features::gftt_oclsrc,
format("-D OP_MAX_EIGEN_VAL -D WGS=%zu -D WGS2_ALIGNED=%d -D groupnum=%d",
wgs, wgs2_aligned, dbsize));
if (k2.empty())
return false;
k2.args(dbarg, (float)qualityLevel, cornersarg);
if (!k2.runTask(false))
return false;
}
// collect list of pointers to features - put them into temporary image
{
ocl::Kernel k("findCorners", ocl::features::gftt_oclsrc,
format("-D OP_FIND_CORNERS%s", haveMask ? " -D HAVE_MASK" : ""));
if (k.empty())
return false;
ocl::KernelArg eigarg = ocl::KernelArg::ReadOnlyNoSize(eig),
cornersarg = ocl::KernelArg::PtrWriteOnly(corners_buffer),
thresholdarg = ocl::KernelArg::PtrReadOnly(maxEigenValue);
if (!haveMask)
k.args(eigarg, cornersarg, eig.rows - 2, eig.cols - 2, thresholdarg,
(int)possibleCornersCount);
else
{
UMat mask = _mask.getUMat();
k.args(eigarg, ocl::KernelArg::ReadOnlyNoSize(mask),
cornersarg, eig.rows - 2, eig.cols - 2,
thresholdarg, (int)possibleCornersCount);
}
size_t globalsize[2] = { (size_t)eig.cols - 2, (size_t)eig.rows - 2 };
if (!k.run(2, globalsize, NULL, false))
return false;
tmpCorners = corners_buffer.getMat(ACCESS_RW);
total = std::min<size_t>(tmpCorners.at<Vec2i>(0, 0)[0], possibleCornersCount);
if (total == 0)
{
_corners.release();
return true;
}
}
Corner* corner_ptr = tmpCorners.ptr<Corner>() + 1;
std::sort(corner_ptr, corner_ptr + total);
std::vector<Point2f> corners;
std::vector<float> cornersQuality;
corners.reserve(total);
cornersQuality.reserve(total);
if (minDistance >= 1)
{
// Partition the image into larger grids
int w = imgsize.width, h = imgsize.height;
const int cell_size = cvRound(minDistance);
const int grid_width = (w + cell_size - 1) / cell_size;
const int grid_height = (h + cell_size - 1) / cell_size;
std::vector<std::vector<Point2f> > grid(grid_width*grid_height);
minDistance *= minDistance;
for( i = 0; i < total; i++ )
{
const Corner & c = corner_ptr[i];
bool good = true;
int x_cell = c.x / cell_size;
int y_cell = c.y / cell_size;
int x1 = x_cell - 1;
int y1 = y_cell - 1;
int x2 = x_cell + 1;
int y2 = y_cell + 1;
// boundary check
x1 = std::max(0, x1);
y1 = std::max(0, y1);
x2 = std::min(grid_width - 1, x2);
y2 = std::min(grid_height - 1, y2);
for( int yy = y1; yy <= y2; yy++ )
for( int xx = x1; xx <= x2; xx++ )
{
std::vector<Point2f> &m = grid[yy * grid_width + xx];
if( m.size() )
{
for(j = 0; j < m.size(); j++)
{
float dx = c.x - m[j].x;
float dy = c.y - m[j].y;
if( dx*dx + dy*dy < minDistance )
{
good = false;
goto break_out;
}
}
}
}
break_out:
if (good)
{
grid[y_cell*grid_width + x_cell].push_back(Point2f((float)c.x, (float)c.y));
corners.push_back(Point2f((float)c.x, (float)c.y));
cornersQuality.push_back(c.val);
++ncorners;
if( maxCorners > 0 && (int)ncorners == maxCorners )
break;
}
}
}
else
{
for( i = 0; i < total; i++ )
{
const Corner & c = corner_ptr[i];
corners.push_back(Point2f((float)c.x, (float)c.y));
cornersQuality.push_back(c.val);
++ncorners;
if( maxCorners > 0 && (int)ncorners == maxCorners )
break;
}
}
Mat(corners).reshape(2, (int)ncorners).
convertTo(_corners, _corners.fixedType() ? _corners.type() : CV_32F);
if (_cornersQuality.needed()) {
Mat(cornersQuality).reshape(1, (int)ncorners).
convertTo(_cornersQuality, _cornersQuality.fixedType() ? _cornersQuality.type() : CV_32F);
}
return true;
}
#endif
}
void cv::goodFeaturesToTrack( InputArray image, OutputArray corners,
int maxCorners, double qualityLevel, double minDistance,
InputArray mask, int blockSize, bool useHarrisDetector, double k )
{
return goodFeaturesToTrack(image, corners, maxCorners, qualityLevel, minDistance,
mask, noArray(), blockSize, 3, useHarrisDetector, k);
}
void cv::goodFeaturesToTrack( InputArray image, OutputArray corners,
int maxCorners, double qualityLevel, double minDistance,
InputArray mask, int blockSize, int gradientSize, bool useHarrisDetector, double k )
{
return goodFeaturesToTrack( image, corners, maxCorners, qualityLevel, minDistance,
mask, noArray(), blockSize, gradientSize, useHarrisDetector, k );
}
void cv::goodFeaturesToTrack( InputArray _image, OutputArray _corners,
int maxCorners, double qualityLevel, double minDistance,
InputArray _mask, OutputArray _cornersQuality, int blockSize, int gradientSize,
bool useHarrisDetector, double harrisK )
{
CV_INSTRUMENT_REGION();
CV_Assert( qualityLevel > 0 && minDistance >= 0 && maxCorners >= 0 );
CV_Assert( _mask.empty() || ((_mask.type() == CV_8UC1 || _mask.type() == CV_BoolC1) && _mask.sameSize(_image)) );
CV_OCL_RUN(_image.dims() <= 2 && _image.isUMat(),
ocl_goodFeaturesToTrack(_image, _corners, maxCorners, qualityLevel, minDistance,
_mask, _cornersQuality, blockSize, gradientSize, useHarrisDetector, harrisK))
Mat image = _image.getMat(), eig, tmp;
if (image.empty())
{
_corners.release();
_cornersQuality.release();
return;
}
if( useHarrisDetector )
cornerHarris( image, eig, blockSize, gradientSize, harrisK );
else
cornerMinEigenVal( image, eig, blockSize, gradientSize );
double maxVal = 0;
minMaxLoc( eig, 0, &maxVal, 0, 0, _mask );
threshold( eig, eig, maxVal*qualityLevel, 0, THRESH_TOZERO );
dilate( eig, tmp, Mat());
Size imgsize = image.size();
std::vector<const float*> tmpCorners;
// collect list of pointers to features - put them into temporary image
Mat mask = _mask.getMat();
for( int y = 1; y < imgsize.height - 1; y++ )
{
const float* eig_data = (const float*)eig.ptr(y);
const float* tmp_data = (const float*)tmp.ptr(y);
const uchar* mask_data = mask.data ? mask.ptr(y) : 0;
for( int x = 1; x < imgsize.width - 1; x++ )
{
float val = eig_data[x];
if( val != 0 && val == tmp_data[x] && (!mask_data || mask_data[x]) )
tmpCorners.push_back(eig_data + x);
}
}
std::vector<Point2f> corners;
std::vector<float> cornersQuality;
size_t i, j, total = tmpCorners.size(), ncorners = 0;
if (total == 0)
{
_corners.release();
_cornersQuality.release();
return;
}
std::sort( tmpCorners.begin(), tmpCorners.end(), greaterThanPtr() );
if (minDistance >= 1)
{
// Partition the image into larger grids
int w = image.cols;
int h = image.rows;
const int cell_size = cvRound(minDistance);
const int grid_width = (w + cell_size - 1) / cell_size;
const int grid_height = (h + cell_size - 1) / cell_size;
std::vector<std::vector<Point2f> > grid(grid_width*grid_height);
minDistance *= minDistance;
for( i = 0; i < total; i++ )
{
int ofs = (int)((const uchar*)tmpCorners[i] - eig.ptr());
int y = (int)(ofs / eig.step);
int x = (int)((ofs - y*eig.step)/sizeof(float));
bool good = true;
int x_cell = x / cell_size;
int y_cell = y / cell_size;
int x1 = x_cell - 1;
int y1 = y_cell - 1;
int x2 = x_cell + 1;
int y2 = y_cell + 1;
// boundary check
x1 = std::max(0, x1);
y1 = std::max(0, y1);
x2 = std::min(grid_width-1, x2);
y2 = std::min(grid_height-1, y2);
for( int yy = y1; yy <= y2; yy++ )
{
for( int xx = x1; xx <= x2; xx++ )
{
std::vector <Point2f> &m = grid[yy*grid_width + xx];
if( m.size() )
{
for(j = 0; j < m.size(); j++)
{
float dx = x - m[j].x;
float dy = y - m[j].y;
if( dx*dx + dy*dy < minDistance )
{
good = false;
goto break_out;
}
}
}
}
}
break_out:
if (good)
{
grid[y_cell*grid_width + x_cell].push_back(Point2f((float)x, (float)y));
cornersQuality.push_back(*tmpCorners[i]);
corners.push_back(Point2f((float)x, (float)y));
++ncorners;
if( maxCorners > 0 && (int)ncorners == maxCorners )
break;
}
}
}
else
{
for( i = 0; i < total; i++ )
{
cornersQuality.push_back(*tmpCorners[i]);
int ofs = (int)((const uchar*)tmpCorners[i] - eig.ptr());
int y = (int)(ofs / eig.step);
int x = (int)((ofs - y*eig.step)/sizeof(float));
corners.push_back(Point2f((float)x, (float)y));
++ncorners;
if( maxCorners > 0 && (int)ncorners == maxCorners )
break;
}
}
Mat(corners).reshape(2, (int)ncorners).
convertTo(_corners, _corners.fixedType() ? _corners.type() : CV_32F);
if (_cornersQuality.needed()) {
Mat(cornersQuality).reshape(1, (int)ncorners).
convertTo(_cornersQuality, _cornersQuality.fixedType() ? _cornersQuality.type() : CV_32F);
}
}
/* End of file. */
+185
View File
@@ -0,0 +1,185 @@
/*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.
//
//
// Intel License Agreement
// For Open Source Computer Vision Library
//
// Copyright (C) 2000, 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 Intel Corporation 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"
namespace cv
{
class GFTTDetector_Impl CV_FINAL : public GFTTDetector
{
public:
GFTTDetector_Impl( int _nfeatures, double _qualityLevel,
double _minDistance, int _blockSize, int _gradientSize,
bool _useHarrisDetector, double _k )
: nfeatures(_nfeatures), qualityLevel(_qualityLevel), minDistance(_minDistance),
blockSize(_blockSize), gradSize(_gradientSize), useHarrisDetector(_useHarrisDetector), k(_k)
{
}
void read( const FileNode& fn) CV_OVERRIDE
{
// if node is empty, keep previous value
if (!fn["nfeatures"].empty())
fn["nfeatures"] >> nfeatures;
if (!fn["qualityLevel"].empty())
fn["qualityLevel"] >> qualityLevel;
if (!fn["minDistance"].empty())
fn["minDistance"] >> minDistance;
if (!fn["blockSize"].empty())
fn["blockSize"] >> blockSize;
if (!fn["gradSize"].empty())
fn["gradSize"] >> gradSize;
if (!fn["useHarrisDetector"].empty())
fn["useHarrisDetector"] >> useHarrisDetector;
if (!fn["k"].empty())
fn["k"] >> k;
}
void write( FileStorage& fs) const CV_OVERRIDE
{
if(fs.isOpened())
{
fs << "name" << getDefaultName();
fs << "nfeatures" << nfeatures;
fs << "qualityLevel" << qualityLevel;
fs << "minDistance" << minDistance;
fs << "blockSize" << blockSize;
fs << "gradSize" << gradSize;
fs << "useHarrisDetector" << useHarrisDetector;
fs << "k" << k;
}
}
void setMaxFeatures(int maxFeatures) CV_OVERRIDE { nfeatures = maxFeatures; }
int getMaxFeatures() const CV_OVERRIDE { return nfeatures; }
void setQualityLevel(double qlevel) CV_OVERRIDE { qualityLevel = qlevel; }
double getQualityLevel() const CV_OVERRIDE { return qualityLevel; }
void setMinDistance(double minDistance_) CV_OVERRIDE { minDistance = minDistance_; }
double getMinDistance() const CV_OVERRIDE { return minDistance; }
void setBlockSize(int blockSize_) CV_OVERRIDE { blockSize = blockSize_; }
int getBlockSize() const CV_OVERRIDE { return blockSize; }
void setGradientSize(int gradientSize_) CV_OVERRIDE { gradSize = gradientSize_; }
int getGradientSize() CV_OVERRIDE { return gradSize; }
void setHarrisDetector(bool val) CV_OVERRIDE { useHarrisDetector = val; }
bool getHarrisDetector() const CV_OVERRIDE { return useHarrisDetector; }
void setK(double k_) CV_OVERRIDE { k = k_; }
double getK() const CV_OVERRIDE { return k; }
void detect( InputArray _image, std::vector<KeyPoint>& keypoints, InputArray _mask ) CV_OVERRIDE
{
CV_INSTRUMENT_REGION();
if(_image.empty())
{
keypoints.clear();
return;
}
std::vector<Point2f> corners;
std::vector<float> cornersQuality;
if (_image.isUMat())
{
UMat ugrayImage;
if( _image.type() != CV_8U )
cvtColor( _image, ugrayImage, COLOR_BGR2GRAY );
else
ugrayImage = _image.getUMat();
goodFeaturesToTrack( ugrayImage, corners, nfeatures, qualityLevel, minDistance, _mask,
cornersQuality, blockSize, gradSize, useHarrisDetector, k );
}
else
{
Mat image = _image.getMat(), grayImage = image;
if( image.type() != CV_8U )
cvtColor( image, grayImage, COLOR_BGR2GRAY );
goodFeaturesToTrack( grayImage, corners, nfeatures, qualityLevel, minDistance, _mask,
cornersQuality, blockSize, gradSize, useHarrisDetector, k );
}
CV_Assert(corners.size() == cornersQuality.size());
keypoints.resize(corners.size());
for (size_t i = 0; i < corners.size(); i++)
keypoints[i] = KeyPoint(corners[i], (float)blockSize, -1, cornersQuality[i]);
}
int nfeatures;
double qualityLevel;
double minDistance;
int blockSize;
int gradSize;
bool useHarrisDetector;
double k;
};
Ptr<GFTTDetector> GFTTDetector::create( int _nfeatures, double _qualityLevel,
double _minDistance, int _blockSize, int _gradientSize,
bool _useHarrisDetector, double _k )
{
return makePtr<GFTTDetector_Impl>(_nfeatures, _qualityLevel,
_minDistance, _blockSize, _gradientSize, _useHarrisDetector, _k);
}
Ptr<GFTTDetector> GFTTDetector::create( int _nfeatures, double _qualityLevel,
double _minDistance, int _blockSize,
bool _useHarrisDetector, double _k )
{
return makePtr<GFTTDetector_Impl>(_nfeatures, _qualityLevel,
_minDistance, _blockSize, 3, _useHarrisDetector, _k);
}
String GFTTDetector::getDefaultName() const
{
return (Feature2D::getDefaultName() + ".GFTTDetector");
}
}
+161
View File
@@ -0,0 +1,161 @@
/*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) 2017, 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*/
#ifndef OPENCV_FEATURES_HAL_REPLACEMENT_HPP
#define OPENCV_FEATURES_HAL_REPLACEMENT_HPP
#include "opencv2/core/hal/interface.h"
#if defined(__clang__) // clang or MSVC clang
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-parameter"
#elif defined(_MSC_VER)
#pragma warning(push)
#pragma warning(disable : 4100)
#elif defined(__GNUC__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
#endif
//! @addtogroup features_hal_interface
//! @note Define your functions to override default implementations:
//! @code
//! #undef hal_add8u
//! #define hal_add8u my_add8u
//! @endcode
//! @{
/**
@brief Detects corners using the FAST algorithm, returns mask.
@param src_data Source image data
@param src_step Source image step
@param dst_data Destination mask data
@param dst_step Destination mask step
@param width Source image width
@param height Source image height
@param type FAST type
*/
inline int hal_ni_FAST_dense(const uchar* src_data, size_t src_step, uchar* dst_data, size_t dst_step, int width, int height, cv::FastFeatureDetector::DetectorType type) { return CV_HAL_ERROR_NOT_IMPLEMENTED; }
//! @cond IGNORED
#define cv_hal_FAST_dense hal_ni_FAST_dense
//! @endcond
/**
@brief Non-maximum suppression for FAST_9_16.
@param src_data,src_step Source mask
@param dst_data,dst_step Destination mask after NMS
@param width,height Source mask dimensions
*/
inline int hal_ni_FAST_NMS(const uchar* src_data, size_t src_step, uchar* dst_data, size_t dst_step, int width, int height) { return CV_HAL_ERROR_NOT_IMPLEMENTED; }
//! @cond IGNORED
#define cv_hal_FAST_NMS hal_ni_FAST_NMS
//! @endcond
/**
@brief Detects corners using the FAST algorithm.
@param src_data Source image data
@param src_step Source image step
@param width Source image width
@param height Source image height
@param keypoints_data Pointer to keypoints
@param keypoints_count Count of keypoints
@param threshold Threshold for keypoint
@param nonmax_suppression Indicates if make nonmaxima suppression or not.
@param type FAST type
*/
inline int hal_ni_FAST(const uchar* src_data, size_t src_step, int width, int height, uchar* keypoints_data, size_t* keypoints_count, int threshold, bool nonmax_suppression, int /*cv::FastFeatureDetector::DetectorType*/ type) { return CV_HAL_ERROR_NOT_IMPLEMENTED; }
/**
@brief Detects corners using the FAST algorithm.
@param src_data Source image data
@param src_step Source image step
@param width Source image width
@param height Source image height
@param keypoints_data Pointer to keypoints
@param keypoints_count Count of keypoints
@param threshold Threshold for keypoint
@param nonmax_suppression Indicates if make nonmaxima suppression or not.
@param type FAST type
@param realloc_func function for reallocation
*/
inline int hal_ni_FASTv2(const uchar* src_data, size_t src_step, int width, int height, void** keypoints_data, size_t* keypoints_count, int threshold, bool nonmax_suppression, int /*cv::FastFeatureDetector::DetectorType*/ type, void* (*realloc_func)(void*, size_t)) { return CV_HAL_ERROR_NOT_IMPLEMENTED; }
//! @cond IGNORED
#define cv_hal_FAST hal_ni_FAST
#define cv_hal_FASTv2 hal_ni_FASTv2
//! @endcond
//! @}
#if defined(__clang__)
#pragma clang diagnostic pop
#elif defined(_MSC_VER)
#pragma warning(pop)
#elif defined(__GNUC__)
#pragma GCC diagnostic pop
#endif
#include "custom_hal.hpp"
//! @cond IGNORED
#define CALL_HAL_RET(name, fun, retval, ...) \
int res = __CV_EXPAND(fun(__VA_ARGS__, &retval)); \
if (res == CV_HAL_ERROR_OK) \
return retval; \
else if (res != CV_HAL_ERROR_NOT_IMPLEMENTED) \
CV_Error_(cv::Error::StsInternal, \
("HAL implementation " CVAUX_STR(name) " ==> " CVAUX_STR(fun) " returned %d (0x%08x)", res, res));
#define CALL_HAL(name, fun, ...) \
{ \
int res = __CV_EXPAND(fun(__VA_ARGS__)); \
if (res == CV_HAL_ERROR_OK) \
return; \
else if (res != CV_HAL_ERROR_NOT_IMPLEMENTED) \
CV_Error_(cv::Error::StsInternal, \
("HAL implementation " CVAUX_STR(name) " ==> " CVAUX_STR(fun) " returned %d (0x%08x)", res, res)); \
}
//! @endcond
#endif
+293
View File
@@ -0,0 +1,293 @@
/*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) 2008, 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 Intel Corporation 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"
namespace cv
{
struct KeypointResponseGreaterThanOrEqualToThreshold
{
KeypointResponseGreaterThanOrEqualToThreshold(float _value) :
value(_value)
{
}
inline bool operator()(const KeyPoint& kpt) const
{
return kpt.response >= value;
}
float value;
};
struct KeypointResponseGreater
{
inline bool operator()(const KeyPoint& kp1, const KeyPoint& kp2) const
{
return kp1.response > kp2.response;
}
};
// takes keypoints and culls them by the response
void KeyPointsFilter::retainBest(std::vector<KeyPoint>& keypoints, int n_points)
{
//this is only necessary if the keypoints size is greater than the number of desired points.
if( n_points >= 0 && keypoints.size() > (size_t)n_points )
{
if (n_points==0)
{
keypoints.clear();
return;
}
//first use nth element to partition the keypoints into the best and worst.
std::nth_element(keypoints.begin(), keypoints.begin() + n_points - 1, keypoints.end(), KeypointResponseGreater());
//this is the boundary response, and in the case of FAST may be ambiguous
float ambiguous_response = keypoints[n_points - 1].response;
//use std::partition to grab all of the keypoints with the boundary response.
std::vector<KeyPoint>::const_iterator new_end =
std::partition(keypoints.begin() + n_points, keypoints.end(),
KeypointResponseGreaterThanOrEqualToThreshold(ambiguous_response));
//resize the keypoints, given this new end point. nth_element and partition reordered the points inplace
keypoints.resize(new_end - keypoints.begin());
}
}
struct RoiPredicate
{
RoiPredicate( const Rect& _r ) : r(_r)
{}
bool operator()( const KeyPoint& keyPt ) const
{
// workaround for https://github.com/opencv/opencv/issues/26016
// To keep its behaviour, keyPt.pt casts to Point_<int>.
return !r.contains( Point_<int>(keyPt.pt) );
}
Rect r;
};
void KeyPointsFilter::runByImageBorder( std::vector<KeyPoint>& keypoints, Size imageSize, int borderSize )
{
if( borderSize > 0)
{
if (imageSize.height <= borderSize * 2 || imageSize.width <= borderSize * 2)
keypoints.clear();
else
keypoints.erase( std::remove_if(keypoints.begin(), keypoints.end(),
RoiPredicate(Rect(Point(borderSize, borderSize),
Point(imageSize.width - borderSize, imageSize.height - borderSize)))),
keypoints.end() );
}
}
struct SizePredicate
{
SizePredicate( float _minSize, float _maxSize ) : minSize(_minSize), maxSize(_maxSize)
{}
bool operator()( const KeyPoint& keyPt ) const
{
float size = keyPt.size;
return (size < minSize) || (size > maxSize);
}
float minSize, maxSize;
};
void KeyPointsFilter::runByKeypointSize( std::vector<KeyPoint>& keypoints, float minSize, float maxSize )
{
CV_Assert( minSize >= 0 );
CV_Assert( maxSize >= 0);
CV_Assert( minSize <= maxSize );
keypoints.erase( std::remove_if(keypoints.begin(), keypoints.end(), SizePredicate(minSize, maxSize)),
keypoints.end() );
}
class MaskPredicate
{
public:
MaskPredicate( const Mat& _mask ) : mask(_mask) {}
bool operator() (const KeyPoint& key_pt) const
{
return mask.at<uchar>( (int)(key_pt.pt.y + 0.5f), (int)(key_pt.pt.x + 0.5f) ) == 0;
}
MaskPredicate& operator=(const MaskPredicate&) = delete;
// To avoid -Wdeprecated-copy warning, copy constructor is needed.
MaskPredicate(const MaskPredicate&) = default;
private:
const Mat mask;
};
void KeyPointsFilter::runByPixelsMask( std::vector<KeyPoint>& keypoints, const Mat& mask )
{
CV_INSTRUMENT_REGION();
if( mask.empty() )
return;
keypoints.erase(std::remove_if(keypoints.begin(), keypoints.end(), MaskPredicate(mask)), keypoints.end());
}
/*
* Remove objects from some image and a vector by mask for pixels of this image
*/
template <typename T>
void runByPixelsMask2(std::vector<KeyPoint> &keypoints, std::vector<T> &removeFrom, const Mat &mask)
{
if (mask.empty())
return;
MaskPredicate maskPredicate(mask);
removeFrom.erase(std::remove_if(removeFrom.begin(), removeFrom.end(),
[&](const T &x)
{
auto index = &x - &removeFrom.front();
return maskPredicate(keypoints[index]);
}),
removeFrom.end());
keypoints.erase(std::remove_if(keypoints.begin(), keypoints.end(), maskPredicate), keypoints.end());
}
void KeyPointsFilter::runByPixelsMask2VectorPoint(std::vector<KeyPoint> &keypoints, std::vector<std::vector<Point> > &removeFrom, const Mat &mask)
{
runByPixelsMask2(keypoints, removeFrom, mask);
}
struct KeyPoint_LessThan
{
KeyPoint_LessThan(const std::vector<KeyPoint>& _kp) : kp(&_kp) {}
bool operator()(int i, int j) const
{
const KeyPoint& kp1 = (*kp)[i];
const KeyPoint& kp2 = (*kp)[j];
if( kp1.pt.x != kp2.pt.x )
return kp1.pt.x < kp2.pt.x;
if( kp1.pt.y != kp2.pt.y )
return kp1.pt.y < kp2.pt.y;
if( kp1.size != kp2.size )
return kp1.size > kp2.size;
if( kp1.angle != kp2.angle )
return kp1.angle < kp2.angle;
if( kp1.response != kp2.response )
return kp1.response > kp2.response;
if( kp1.octave != kp2.octave )
return kp1.octave > kp2.octave;
if( kp1.class_id != kp2.class_id )
return kp1.class_id > kp2.class_id;
return i < j;
}
const std::vector<KeyPoint>* kp;
};
void KeyPointsFilter::removeDuplicated( std::vector<KeyPoint>& keypoints )
{
int i, j, n = (int)keypoints.size();
std::vector<int> kpidx(n);
std::vector<uchar> mask(n, (uchar)1);
for( i = 0; i < n; i++ )
kpidx[i] = i;
std::sort(kpidx.begin(), kpidx.end(), KeyPoint_LessThan(keypoints));
for( i = 1, j = 0; i < n; i++ )
{
KeyPoint& kp1 = keypoints[kpidx[i]];
KeyPoint& kp2 = keypoints[kpidx[j]];
if( kp1.pt.x != kp2.pt.x || kp1.pt.y != kp2.pt.y ||
kp1.size != kp2.size || kp1.angle != kp2.angle )
j = i;
else
mask[kpidx[i]] = 0;
}
for( i = j = 0; i < n; i++ )
{
if( mask[i] )
{
if( i != j )
keypoints[j] = keypoints[i];
j++;
}
}
keypoints.resize(j);
}
struct KeyPoint12_LessThan
{
bool operator()(const KeyPoint &kp1, const KeyPoint &kp2) const
{
if( kp1.pt.x != kp2.pt.x )
return kp1.pt.x < kp2.pt.x;
if( kp1.pt.y != kp2.pt.y )
return kp1.pt.y < kp2.pt.y;
if( kp1.size != kp2.size )
return kp1.size > kp2.size;
if( kp1.angle != kp2.angle )
return kp1.angle < kp2.angle;
if( kp1.response != kp2.response )
return kp1.response > kp2.response;
if( kp1.octave != kp2.octave )
return kp1.octave > kp2.octave;
return kp1.class_id > kp2.class_id;
}
};
void KeyPointsFilter::removeDuplicatedSorted( std::vector<KeyPoint>& keypoints )
{
int i, j, n = (int)keypoints.size();
if (n < 2) return;
std::sort(keypoints.begin(), keypoints.end(), KeyPoint12_LessThan());
for( i = 0, j = 1; j < n; ++j )
{
const KeyPoint& kp1 = keypoints[i];
const KeyPoint& kp2 = keypoints[j];
if( kp1.pt.x != kp2.pt.x || kp1.pt.y != kp2.pt.y ||
kp1.size != kp2.size || kp1.angle != kp2.angle ) {
keypoints[++i] = keypoints[j];
}
}
keypoints.resize(i + 1);
}
}
+52
View File
@@ -0,0 +1,52 @@
/*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.
// 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,
// 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*/
//
// Library initialization file
//
#include "precomp.hpp"
IPP_INITIALIZER_AUTO
/* End of file. */
File diff suppressed because it is too large Load Diff
+282
View File
@@ -0,0 +1,282 @@
// 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"
#ifdef HAVE_OPENCV_DNN
#include "opencv2/dnn.hpp"
#include "aliked_context.hpp"
#endif
namespace cv
{
LightGlueMatcher::LightGlueMatcher() {}
LightGlueMatcher::~LightGlueMatcher() {}
#ifdef HAVE_OPENCV_DNN
struct LightGluePairContext
{
Mat queryKeypoints; // Nx2 float (normalized [-1,1] or pixel)
Mat trainKeypoints; // Mx2 float
Size queryImageSize;
Size trainImageSize;
bool valid;
LightGluePairContext() : valid(false) {}
void clear()
{
queryKeypoints.release();
trainKeypoints.release();
queryImageSize = Size();
trainImageSize = Size();
valid = false;
}
};
class LightGlueMatcherImpl : public LightGlueMatcher
{
public:
LightGlueMatcherImpl(const String& modelPath, float _scoreThreshold, int backend, int target)
{
scoreThreshold = _scoreThreshold;
net = dnn::readNet(modelPath, "", "");
CV_Assert(!net.empty());
net.setPreferableBackend(backend);
net.setPreferableTarget(target);
}
LightGlueMatcherImpl(const std::vector<uchar>& modelData, float _scoreThreshold, int backend, int target)
{
scoreThreshold = _scoreThreshold;
net = dnn::readNetFromONNX(modelData);
CV_Assert(!net.empty());
net.setPreferableBackend(backend);
net.setPreferableTarget(target);
}
// Private constructor for clone() — shares the already-loaded network
LightGlueMatcherImpl(const dnn::Net& _net, float _scoreThreshold)
: net(_net), scoreThreshold(_scoreThreshold) {};
// DescriptorMatcher interface
bool isMaskSupported() const CV_OVERRIDE { return false; }
Ptr<DescriptorMatcher> clone(bool emptyTrainData) const CV_OVERRIDE;
// LightGlueMatcher interface
void setPairInfo(InputArray queryKpts, InputArray trainKpts,
Size queryImageSize = Size(), Size trainImageSize = Size()) CV_OVERRIDE;
void clearPairInfo() CV_OVERRIDE;
protected:
void knnMatchImpl(InputArray queryDescriptors,
std::vector<std::vector<DMatch>>& matches, int k,
InputArrayOfArrays masks = noArray(),
bool compactResult = false) CV_OVERRIDE;
void radiusMatchImpl(InputArray queryDescriptors,
std::vector<std::vector<DMatch>>& matches, float maxDistance,
InputArrayOfArrays masks = noArray(),
bool compactResult = false) CV_OVERRIDE;
void lightglueMatch(const Mat& queryDesc, const Mat& trainDesc,
const Mat& queryKpts, const Mat& trainKpts,
Size queryImgSize, Size trainImgSize,
std::vector<DMatch>& matches);
bool resolveContext(Mat& queryKpts, Mat& trainKpts,
Size& queryImgSize, Size& trainImgSize);
dnn::Net net;
float scoreThreshold;
LightGluePairContext pairContext;
};
Ptr<DescriptorMatcher> LightGlueMatcherImpl::clone(bool emptyTrainData) const
{
Ptr<LightGlueMatcherImpl> m = makePtr<LightGlueMatcherImpl>(net, scoreThreshold);
// Always copy pairContext - it's matcher state, not train data
m->pairContext = pairContext;
if (!emptyTrainData)
{
m->trainDescCollection = trainDescCollection;
m->utrainDescCollection = utrainDescCollection;
}
return m;
}
void LightGlueMatcherImpl::setPairInfo(InputArray _queryKpts, InputArray _trainKpts,
Size _queryImageSize, Size _trainImageSize)
{
pairContext.queryKeypoints = _queryKpts.getMat().clone();
pairContext.trainKeypoints = _trainKpts.getMat().clone();
pairContext.queryImageSize = _queryImageSize;
pairContext.trainImageSize = _trainImageSize;
pairContext.valid = true;
}
void LightGlueMatcherImpl::clearPairInfo()
{
pairContext.clear();
}
bool LightGlueMatcherImpl::resolveContext(Mat& queryKpts, Mat& trainKpts,
Size& queryImgSize, Size& trainImgSize)
{
if (pairContext.valid)
{
queryKpts = pairContext.queryKeypoints;
trainKpts = pairContext.trainKeypoints;
queryImgSize = pairContext.queryImageSize;
trainImgSize = pairContext.trainImageSize;
return true;
}
return false;
}
void LightGlueMatcherImpl::lightglueMatch(const Mat& queryDesc, const Mat& trainDesc,
const Mat& queryKpts, const Mat& trainKpts,
Size queryImgSize, Size trainImgSize,
std::vector<DMatch>& matches)
{
int N = queryDesc.rows;
int M = trainDesc.rows;
// Normalize keypoints to [-1, 1] if in pixel coordinates
Mat kpts0 = queryKpts.clone();
Mat kpts1 = trainKpts.clone();
if (queryImgSize.width > 0 && queryImgSize.height > 0)
{
for (int i = 0; i < kpts0.rows; i++)
{
kpts0.at<float>(i, 0) = kpts0.at<float>(i, 0) / (float)queryImgSize.width * 2.0f - 1.0f;
kpts0.at<float>(i, 1) = kpts0.at<float>(i, 1) / (float)queryImgSize.height * 2.0f - 1.0f;
}
}
if (trainImgSize.width > 0 && trainImgSize.height > 0)
{
for (int i = 0; i < kpts1.rows; i++)
{
kpts1.at<float>(i, 0) = kpts1.at<float>(i, 0) / (float)trainImgSize.width * 2.0f - 1.0f;
kpts1.at<float>(i, 1) = kpts1.at<float>(i, 1) / (float)trainImgSize.height * 2.0f - 1.0f;
}
}
// Prepare blobs: [1, N, 2] and [1, N, D]
int descDim = queryDesc.cols;
int szK0[] = {1, N, 2};
int szK1[] = {1, M, 2};
int szD0[] = {1, N, descDim};
int szD1[] = {1, M, descDim};
Mat kpts0blob = kpts0.reshape(0, 3, szK0);
Mat kpts1blob = kpts1.reshape(0, 3, szK1);
Mat desc0blob = queryDesc.reshape(0, 3, szD0);
Mat desc1blob = trainDesc.reshape(0, 3, szD1);
net.setInput(kpts0blob, "kpts0");
net.setInput(kpts1blob, "kpts1");
net.setInput(desc0blob, "desc0");
net.setInput(desc1blob, "desc1");
std::vector<String> outNames = {"matches0", "mscores0"};
std::vector<Mat> outs;
net.forward(outs, outNames);
CV_Assert(outs.size() == 2);
// matches0: [M, 2] int64 - pair indices (kpt0_idx, kpt1_idx)
// mscores0: [M] float32 - confidence per pair
Mat matchesMat = outs[0];
Mat scoresMat = outs[1];
matches.clear();
int nMatches = matchesMat.rows;
matches.reserve(nMatches);
for (int i = 0; i < nMatches; i++)
{
int qIdx = (int)matchesMat.at<int64_t>(i, 0);
int tIdx = (int)matchesMat.at<int64_t>(i, 1);
if (qIdx >= 0 && tIdx >= 0 && qIdx < N && tIdx < M)
{
float score = scoresMat.at<float>(i);
if (score >= scoreThreshold)
{
matches.push_back(DMatch(qIdx, tIdx, 1.0f - score));
}
}
}
}
void LightGlueMatcherImpl::knnMatchImpl(InputArray _queryDescriptors,
std::vector<std::vector<DMatch>>& matches,
int k, InputArrayOfArrays, bool)
{
CV_INSTRUMENT_REGION();
if (k != 1)
CV_Error(cv::Error::StsBadArg, "LightGlueMatcher only supports k=1");
Mat queryKpts, trainKpts;
Size queryImgSize, trainImgSize;
if (!resolveContext(queryKpts, trainKpts, queryImgSize, trainImgSize))
{
CV_Error(cv::Error::StsBadArg,
"LightGlueMatcher: no valid context. Call setPairInfo() before matching.");
}
CV_Assert(!trainDescCollection.empty());
const Mat& trainDesc = trainDescCollection[0];
Mat queryDesc = _queryDescriptors.getMat();
std::vector<DMatch> flatMatches;
lightglueMatch(queryDesc, trainDesc, queryKpts, trainKpts,
queryImgSize, trainImgSize, flatMatches);
matches.clear();
matches.resize(queryDesc.rows);
for (const auto& m : flatMatches)
{
matches[m.queryIdx].push_back(m);
}
clearPairInfo();
}
void LightGlueMatcherImpl::radiusMatchImpl(InputArray, std::vector<std::vector<DMatch>>&,
float, InputArrayOfArrays, bool)
{
CV_Error(cv::Error::StsNotImplemented,
"radiusMatch is not supported by LightGlueMatcher. Use match() or knnMatch().");
}
Ptr<LightGlueMatcher> LightGlueMatcher::create(const String& modelPath, float scoreThreshold, int backend, int target)
{
return makePtr<LightGlueMatcherImpl>(modelPath, scoreThreshold, backend, target);
}
Ptr<LightGlueMatcher> LightGlueMatcher::create(const std::vector<uchar>& modelData,
float scoreThreshold, int backend, int target)
{
return makePtr<LightGlueMatcherImpl>(modelData, scoreThreshold, backend, target);
}
#else // !HAVE_OPENCV_DNN
Ptr<LightGlueMatcher> LightGlueMatcher::create(const String& modelPath,
float scoreThreshold, int backend, int target)
{
CV_UNUSED(modelPath);
CV_UNUSED(scoreThreshold);
CV_UNUSED(backend);
CV_UNUSED(target);
CV_Error(cv::Error::StsNotImplemented,
"LightGlueMatcher requires OpenCV built with opencv_dnn module!");
}
#endif // HAVE_OPENCV_DNN
} // namespace cv
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,694 @@
/*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) 2010-2012, Multicoreware, Inc., all rights reserved.
// Copyright (C) 2010-2012, Advanced Micro Devices, Inc., all rights reserved.
// Third party copyrights are property of their respective owners.
//
// @Authors
// Nathan, liujun@multicorewareinc.com
// Peng Xiao, pengxiao@outlook.com
// Baichuan Su, baichuan@multicorewareinc.com
//
// 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*/
#pragma OPENCL EXTENSION cl_khr_global_int32_base_atomics:enable
#define MAX_FLOAT 3.40282e+038f
#ifndef T
#define T float
#endif
#ifndef BLOCK_SIZE
#define BLOCK_SIZE 16
#endif
#ifndef MAX_DESC_LEN
#define MAX_DESC_LEN 64
#endif
#define BLOCK_SIZE_ODD (BLOCK_SIZE + 1)
#ifndef SHARED_MEM_SZ
# if (BLOCK_SIZE < MAX_DESC_LEN)
# define SHARED_MEM_SZ (kercn * (BLOCK_SIZE * MAX_DESC_LEN + BLOCK_SIZE * BLOCK_SIZE))
# else
# define SHARED_MEM_SZ (kercn * 2 * BLOCK_SIZE_ODD * BLOCK_SIZE)
# endif
#endif
#ifndef DIST_TYPE
#define DIST_TYPE 2
#endif
// dirty fix for non-template support
#if (DIST_TYPE == 2) // L1Dist
# ifdef T_FLOAT
typedef float result_type;
# if (8 == kercn)
typedef float8 value_type;
# define DIST(x, y) {value_type d = fabs((x) - (y)); result += d.s0 + d.s1 + d.s2 + d.s3 + d.s4 + d.s5 + d.s6 + d.s7;}
# elif (4 == kercn)
typedef float4 value_type;
# define DIST(x, y) {value_type d = fabs((x) - (y)); result += d.s0 + d.s1 + d.s2 + d.s3;}
# else
typedef float value_type;
# define DIST(x, y) result += fabs((x) - (y))
# endif
# else
typedef int result_type;
# if (8 == kercn)
typedef int8 value_type;
# define DIST(x, y) {value_type d = abs((x) - (y)); result += d.s0 + d.s1 + d.s2 + d.s3 + d.s4 + d.s5 + d.s6 + d.s7;}
# elif (4 == kercn)
typedef int4 value_type;
# define DIST(x, y) {value_type d = abs((x) - (y)); result += d.s0 + d.s1 + d.s2 + d.s3;}
# else
typedef int value_type;
# define DIST(x, y) result += abs((x) - (y))
# endif
# endif
# define DIST_RES(x) (x)
#elif (DIST_TYPE == 4) // L2Dist
typedef float result_type;
# if (8 == kercn)
typedef float8 value_type;
# define DIST(x, y) {value_type d = ((x) - (y)); result += dot(d.s0123, d.s0123) + dot(d.s4567, d.s4567);}
# elif (4 == kercn)
typedef float4 value_type;
# define DIST(x, y) {value_type d = ((x) - (y)); result += dot(d, d);}
# else
typedef float value_type;
# define DIST(x, y) {value_type d = ((x) - (y)); result = mad(d, d, result);}
# endif
# define DIST_RES(x) sqrt(x)
#elif (DIST_TYPE == 6) // Hamming
# if (8 == kercn)
typedef int8 value_type;
# elif (4 == kercn)
typedef int4 value_type;
# else
typedef int value_type;
# endif
typedef int result_type;
# define DIST(x, y) result += popcount( (x) ^ (y) )
# define DIST_RES(x) (x)
#endif
inline result_type reduce_block(
__local value_type *s_query,
__local value_type *s_train,
int lidx,
int lidy
)
{
result_type result = 0;
#pragma unroll
for (int j = 0 ; j < BLOCK_SIZE ; j++)
{
DIST(s_query[lidy * BLOCK_SIZE_ODD + j], s_train[j * BLOCK_SIZE_ODD + lidx]);
}
return DIST_RES(result);
}
inline result_type reduce_block_match(
__local value_type *s_query,
__local value_type *s_train,
int lidx,
int lidy
)
{
result_type result = 0;
#pragma unroll
for (int j = 0 ; j < BLOCK_SIZE ; j++)
{
DIST(s_query[lidy * BLOCK_SIZE_ODD + j], s_train[j * BLOCK_SIZE_ODD + lidx]);
}
return result;
}
inline result_type reduce_multi_block(
__local value_type *s_query,
__local value_type *s_train,
int block_index,
int lidx,
int lidy
)
{
result_type result = 0;
#pragma unroll
for (int j = 0 ; j < BLOCK_SIZE ; j++)
{
DIST(s_query[lidy * MAX_DESC_LEN + block_index * BLOCK_SIZE + j], s_train[j * BLOCK_SIZE + lidx]);
}
return result;
}
__kernel void BruteForceMatch_Match(
__global T *query,
__global T *train,
__global int *bestTrainIdx,
__global float *bestDistance,
int query_rows,
int query_cols,
int train_rows,
int train_cols,
int step
)
{
const int lidx = get_local_id(0);
const int lidy = get_local_id(1);
const int groupidx = get_group_id(0);
const int queryIdx = mad24(BLOCK_SIZE, groupidx, lidy);
const int queryOffset = min(queryIdx, query_rows - 1) * step;
__global TN *query_vec = (__global TN *)(query + queryOffset);
query_cols /= kercn;
__local float sharebuffer[SHARED_MEM_SZ];
__local value_type *s_query = (__local value_type *)sharebuffer;
#if 0 < MAX_DESC_LEN
__local value_type *s_train = (__local value_type *)sharebuffer + BLOCK_SIZE * MAX_DESC_LEN;
// load the query into local memory.
#pragma unroll
for (int i = 0; i < MAX_DESC_LEN / BLOCK_SIZE; i++)
{
const int loadx = mad24(BLOCK_SIZE, i, lidx);
s_query[mad24(MAX_DESC_LEN, lidy, loadx)] = loadx < query_cols ? query_vec[loadx] : 0;
}
#else
__local value_type *s_train = (__local value_type *)sharebuffer + BLOCK_SIZE_ODD * BLOCK_SIZE;
const int s_query_i = mad24(BLOCK_SIZE_ODD, lidy, lidx);
const int s_train_i = mad24(BLOCK_SIZE_ODD, lidx, lidy);
#endif
float myBestDistance = MAX_FLOAT;
int myBestTrainIdx = -1;
// loopUnrolledCached to find the best trainIdx and best distance.
for (int t = 0, endt = (train_rows + BLOCK_SIZE - 1) / BLOCK_SIZE; t < endt; t++)
{
result_type result = 0;
const int trainOffset = min(mad24(BLOCK_SIZE, t, lidy), train_rows - 1) * step;
__global TN *train_vec = (__global TN *)(train + trainOffset);
#if 0 < MAX_DESC_LEN
#pragma unroll
for (int i = 0; i < MAX_DESC_LEN / BLOCK_SIZE; i++)
{
//load a BLOCK_SIZE * BLOCK_SIZE block into local train.
const int loadx = mad24(BLOCK_SIZE, i, lidx);
s_train[mad24(BLOCK_SIZE, lidx, lidy)] = loadx < train_cols ? train_vec[loadx] : 0;
//synchronize to make sure each elem for reduceIteration in share memory is written already.
barrier(CLK_LOCAL_MEM_FENCE);
result += reduce_multi_block(s_query, s_train, i, lidx, lidy);
barrier(CLK_LOCAL_MEM_FENCE);
}
#else
for (int i = 0, endq = (query_cols + BLOCK_SIZE - 1) / BLOCK_SIZE; i < endq; i++)
{
const int loadx = mad24(i, BLOCK_SIZE, lidx);
//load query and train into local memory
if (loadx < query_cols)
{
s_query[s_query_i] = query_vec[loadx];
s_train[s_train_i] = train_vec[loadx];
}
else
{
s_query[s_query_i] = 0;
s_train[s_train_i] = 0;
}
barrier(CLK_LOCAL_MEM_FENCE);
result += reduce_block_match(s_query, s_train, lidx, lidy);
barrier(CLK_LOCAL_MEM_FENCE);
}
#endif
result = DIST_RES(result);
const int trainIdx = mad24(BLOCK_SIZE, t, lidx);
if (queryIdx < query_rows && trainIdx < train_rows && result < myBestDistance /*&& mask(queryIdx, trainIdx)*/)
{
myBestDistance = result;
myBestTrainIdx = trainIdx;
}
}
barrier(CLK_LOCAL_MEM_FENCE);
__local float *s_distance = (__local float *)sharebuffer;
__local int *s_trainIdx = (__local int *)(sharebuffer + BLOCK_SIZE_ODD * BLOCK_SIZE);
//findBestMatch
s_distance += lidy * BLOCK_SIZE_ODD;
s_trainIdx += lidy * BLOCK_SIZE_ODD;
s_distance[lidx] = myBestDistance;
s_trainIdx[lidx] = myBestTrainIdx;
barrier(CLK_LOCAL_MEM_FENCE);
//reduce -- now all reduce implement in each threads.
#pragma unroll
for (int k = 0 ; k < BLOCK_SIZE; k++)
{
if (myBestDistance > s_distance[k])
{
myBestDistance = s_distance[k];
myBestTrainIdx = s_trainIdx[k];
}
}
if (queryIdx < query_rows && lidx == 0)
{
bestTrainIdx[queryIdx] = myBestTrainIdx;
bestDistance[queryIdx] = myBestDistance;
}
}
//radius_match
__kernel void BruteForceMatch_RadiusMatch(
__global T *query,
__global T *train,
float maxDistance,
__global int *bestTrainIdx,
__global float *bestDistance,
__global int *nMatches,
int query_rows,
int query_cols,
int train_rows,
int train_cols,
int bestTrainIdx_cols,
int step,
int ostep
)
{
const int lidx = get_local_id(0);
const int lidy = get_local_id(1);
const int groupidx = get_group_id(0);
const int groupidy = get_group_id(1);
const int queryIdx = mad24(BLOCK_SIZE, groupidy, lidy);
const int queryOffset = min(queryIdx, query_rows - 1) * step;
__global TN *query_vec = (__global TN *)(query + queryOffset);
const int trainIdx = mad24(BLOCK_SIZE, groupidx, lidx);
const int trainOffset = min(mad24(BLOCK_SIZE, groupidx, lidy), train_rows - 1) * step;
__global TN *train_vec = (__global TN *)(train + trainOffset);
query_cols /= kercn;
__local float sharebuffer[SHARED_MEM_SZ];
__local value_type *s_query = (__local value_type *)sharebuffer;
__local value_type *s_train = (__local value_type *)sharebuffer + BLOCK_SIZE_ODD * BLOCK_SIZE;
result_type result = 0;
const int s_query_i = mad24(BLOCK_SIZE_ODD, lidy, lidx);
const int s_train_i = mad24(BLOCK_SIZE_ODD, lidx, lidy);
for (int i = 0 ; i < (query_cols + BLOCK_SIZE - 1) / BLOCK_SIZE ; ++i)
{
//load a BLOCK_SIZE * BLOCK_SIZE block into local train.
const int loadx = mad24(BLOCK_SIZE, i, lidx);
if (loadx < query_cols)
{
s_query[s_query_i] = query_vec[loadx];
s_train[s_train_i] = train_vec[loadx];
}
else
{
s_query[s_query_i] = 0;
s_train[s_train_i] = 0;
}
//synchronize to make sure each elem for reduceIteration in share memory is written already.
barrier(CLK_LOCAL_MEM_FENCE);
result += reduce_block(s_query, s_train, lidx, lidy);
barrier(CLK_LOCAL_MEM_FENCE);
}
if (queryIdx < query_rows && trainIdx < train_rows && convert_float(result) < maxDistance)
{
int ind = atom_inc(nMatches + queryIdx);
if(ind < bestTrainIdx_cols)
{
bestTrainIdx[mad24(queryIdx, ostep, ind)] = trainIdx;
bestDistance[mad24(queryIdx, ostep, ind)] = result;
}
}
}
__kernel void BruteForceMatch_knnMatch(
__global T *query,
__global T *train,
__global int2 *bestTrainIdx,
__global float2 *bestDistance,
int query_rows,
int query_cols,
int train_rows,
int train_cols,
int step
)
{
const int lidx = get_local_id(0);
const int lidy = get_local_id(1);
const int groupidx = get_group_id(0);
const int queryIdx = mad24(BLOCK_SIZE, groupidx, lidy);
const int queryOffset = min(queryIdx, query_rows - 1) * step;
__global TN *query_vec = (__global TN *)(query + queryOffset);
query_cols /= kercn;
__local float sharebuffer[SHARED_MEM_SZ];
__local value_type *s_query = (__local value_type *)sharebuffer;
#if 0 < MAX_DESC_LEN
__local value_type *s_train = (__local value_type *)sharebuffer + BLOCK_SIZE * MAX_DESC_LEN;
// load the query into local memory.
#pragma unroll
for (int i = 0 ; i < MAX_DESC_LEN / BLOCK_SIZE; i ++)
{
int loadx = mad24(BLOCK_SIZE, i, lidx);
s_query[mad24(MAX_DESC_LEN, lidy, loadx)] = loadx < query_cols ? query_vec[loadx] : 0;
}
#else
__local value_type *s_train = (__local value_type *)sharebuffer + BLOCK_SIZE_ODD * BLOCK_SIZE;
const int s_query_i = mad24(BLOCK_SIZE_ODD, lidy, lidx);
const int s_train_i = mad24(BLOCK_SIZE_ODD, lidx, lidy);
#endif
float myBestDistance1 = MAX_FLOAT;
float myBestDistance2 = MAX_FLOAT;
int myBestTrainIdx1 = -1;
int myBestTrainIdx2 = -1;
for (int t = 0, endt = (train_rows + BLOCK_SIZE - 1) / BLOCK_SIZE; t < endt ; t++)
{
result_type result = 0;
int trainOffset = min(mad24(BLOCK_SIZE, t, lidy), train_rows - 1) * step;
__global TN *train_vec = (__global TN *)(train + trainOffset);
#if 0 < MAX_DESC_LEN
#pragma unroll
for (int i = 0 ; i < MAX_DESC_LEN / BLOCK_SIZE ; i++)
{
//load a BLOCK_SIZE * BLOCK_SIZE block into local train.
const int loadx = mad24(BLOCK_SIZE, i, lidx);
s_train[mad24(BLOCK_SIZE, lidx, lidy)] = loadx < train_cols ? train_vec[loadx] : 0;
//synchronize to make sure each elem for reduceIteration in share memory is written already.
barrier(CLK_LOCAL_MEM_FENCE);
result += reduce_multi_block(s_query, s_train, i, lidx, lidy);
barrier(CLK_LOCAL_MEM_FENCE);
}
#else
for (int i = 0, endq = (query_cols + BLOCK_SIZE -1) / BLOCK_SIZE; i < endq ; i++)
{
const int loadx = mad24(BLOCK_SIZE, i, lidx);
//load query and train into local memory
if (loadx < query_cols)
{
s_query[s_query_i] = query_vec[loadx];
s_train[s_train_i] = train_vec[loadx];
}
else
{
s_query[s_query_i] = 0;
s_train[s_train_i] = 0;
}
barrier(CLK_LOCAL_MEM_FENCE);
result += reduce_block_match(s_query, s_train, lidx, lidy);
barrier(CLK_LOCAL_MEM_FENCE);
}
#endif
result = DIST_RES(result);
const int trainIdx = mad24(BLOCK_SIZE, t, lidx);
if (queryIdx < query_rows && trainIdx < train_rows)
{
if (result < myBestDistance1)
{
myBestDistance2 = myBestDistance1;
myBestTrainIdx2 = myBestTrainIdx1;
myBestDistance1 = result;
myBestTrainIdx1 = trainIdx;
}
else if (result < myBestDistance2)
{
myBestDistance2 = result;
myBestTrainIdx2 = trainIdx;
}
}
}
barrier(CLK_LOCAL_MEM_FENCE);
__local float *s_distance = (__local float *)sharebuffer;
__local int *s_trainIdx = (__local int *)(sharebuffer + BLOCK_SIZE_ODD * BLOCK_SIZE);
// find BestMatch
s_distance += lidy * BLOCK_SIZE_ODD;
s_trainIdx += lidy * BLOCK_SIZE_ODD;
s_distance[lidx] = myBestDistance1;
s_trainIdx[lidx] = myBestTrainIdx1;
float bestDistance1 = MAX_FLOAT;
float bestDistance2 = MAX_FLOAT;
int bestTrainIdx1 = -1;
int bestTrainIdx2 = -1;
barrier(CLK_LOCAL_MEM_FENCE);
if (lidx == 0)
{
for (int i = 0 ; i < BLOCK_SIZE ; i++)
{
float val = s_distance[i];
if (val < bestDistance1)
{
bestDistance2 = bestDistance1;
bestTrainIdx2 = bestTrainIdx1;
bestDistance1 = val;
bestTrainIdx1 = s_trainIdx[i];
}
else if (val < bestDistance2)
{
bestDistance2 = val;
bestTrainIdx2 = s_trainIdx[i];
}
}
}
barrier(CLK_LOCAL_MEM_FENCE);
s_distance[lidx] = myBestDistance2;
s_trainIdx[lidx] = myBestTrainIdx2;
barrier(CLK_LOCAL_MEM_FENCE);
if (lidx == 0)
{
for (int i = 0 ; i < BLOCK_SIZE ; i++)
{
float val = s_distance[i];
if (val < bestDistance2)
{
bestDistance2 = val;
bestTrainIdx2 = s_trainIdx[i];
}
}
}
myBestDistance1 = bestDistance1;
myBestDistance2 = bestDistance2;
myBestTrainIdx1 = bestTrainIdx1;
myBestTrainIdx2 = bestTrainIdx2;
if (queryIdx < query_rows && lidx == 0)
{
bestTrainIdx[queryIdx] = (int2)(myBestTrainIdx1, myBestTrainIdx2);
bestDistance[queryIdx] = (float2)(myBestDistance1, myBestDistance2);
}
}
#ifdef HAVE_INT64_ATOMICS
#pragma OPENCL EXTENSION cl_khr_int64_base_atomics : enable
__kernel void BruteForceMatch_CrossCheckMatch(
__global T *query,
__global T *train,
__global int *bestTrainIdx,
__global float *bestDistance,
__global ulong *revBest,
int query_rows,
int query_cols,
int train_rows,
int train_cols,
int step
)
{
const int lidx = get_local_id(0);
const int lidy = get_local_id(1);
const int groupidx = get_group_id(0);
const int queryIdx = mad24(BLOCK_SIZE, groupidx, lidy);
const int queryOffset = min(queryIdx, query_rows - 1) * step;
__global TN *query_vec = (__global TN *)(query + queryOffset);
query_cols /= kercn;
__local float sharebuffer[SHARED_MEM_SZ];
__local value_type *s_query = (__local value_type *)sharebuffer;
#if 0 < MAX_DESC_LEN
__local value_type *s_train = (__local value_type *)sharebuffer + BLOCK_SIZE * MAX_DESC_LEN;
#pragma unroll
for (int i = 0; i < MAX_DESC_LEN / BLOCK_SIZE; i++)
{
const int loadx = mad24(BLOCK_SIZE, i, lidx);
s_query[mad24(MAX_DESC_LEN, lidy, loadx)] = loadx < query_cols ? query_vec[loadx] : 0;
}
#else
__local value_type *s_train = (__local value_type *)sharebuffer + BLOCK_SIZE_ODD * BLOCK_SIZE;
const int s_query_i = mad24(BLOCK_SIZE_ODD, lidy, lidx);
const int s_train_i = mad24(BLOCK_SIZE_ODD, lidx, lidy);
#endif
float myBestDistance = MAX_FLOAT;
int myBestTrainIdx = -1;
for (int t = 0, endt = (train_rows + BLOCK_SIZE - 1) / BLOCK_SIZE; t < endt; t++)
{
result_type result = 0;
const int trainOffset = min(mad24(BLOCK_SIZE, t, lidy), train_rows - 1) * step;
__global TN *train_vec = (__global TN *)(train + trainOffset);
#if 0 < MAX_DESC_LEN
#pragma unroll
for (int i = 0; i < MAX_DESC_LEN / BLOCK_SIZE; i++)
{
const int loadx = mad24(BLOCK_SIZE, i, lidx);
s_train[mad24(BLOCK_SIZE, lidx, lidy)] = loadx < train_cols ? train_vec[loadx] : 0;
barrier(CLK_LOCAL_MEM_FENCE);
result += reduce_multi_block(s_query, s_train, i, lidx, lidy);
barrier(CLK_LOCAL_MEM_FENCE);
}
#else
for (int i = 0, endq = (query_cols + BLOCK_SIZE - 1) / BLOCK_SIZE; i < endq; i++)
{
const int loadx = mad24(BLOCK_SIZE, i, lidx);
if (loadx < query_cols)
{
s_query[s_query_i] = query_vec[loadx];
s_train[s_train_i] = train_vec[loadx];
}
else
{
s_query[s_query_i] = 0;
s_train[s_train_i] = 0;
}
barrier(CLK_LOCAL_MEM_FENCE);
result += reduce_block_match(s_query, s_train, lidx, lidy);
barrier(CLK_LOCAL_MEM_FENCE);
}
#endif
result = DIST_RES(result);
const int trainIdx = mad24(BLOCK_SIZE, t, lidx);
if (queryIdx < query_rows && trainIdx < train_rows)
{
if (result < myBestDistance)
{
myBestDistance = result;
myBestTrainIdx = trainIdx;
}
uint dist_bits = as_uint(result);
ulong packed = ((ulong)dist_bits << 32) | (ulong)queryIdx;
atom_min(&revBest[trainIdx], packed);
}
}
barrier(CLK_LOCAL_MEM_FENCE);
__local float *s_distance = (__local float *)sharebuffer;
__local int *s_trainIdx = (__local int *)(sharebuffer + BLOCK_SIZE_ODD * BLOCK_SIZE);
s_distance += lidy * BLOCK_SIZE_ODD;
s_trainIdx += lidy * BLOCK_SIZE_ODD;
s_distance[lidx] = myBestDistance;
s_trainIdx[lidx] = myBestTrainIdx;
barrier(CLK_LOCAL_MEM_FENCE);
#pragma unroll
for (int k = 0; k < BLOCK_SIZE; k++)
{
if (myBestDistance > s_distance[k])
{
myBestDistance = s_distance[k];
myBestTrainIdx = s_trainIdx[k];
}
}
if (queryIdx < query_rows && lidx == 0)
{
bestTrainIdx[queryIdx] = myBestTrainIdx;
bestDistance[queryIdx] = myBestDistance;
}
}
#endif
+162
View File
@@ -0,0 +1,162 @@
// OpenCL port of the FAST corner detector.
// Copyright (C) 2014, Itseez Inc. See the license at http://opencv.org
inline int cornerScore(__global const uchar* img, int step)
{
int k, tofs, v = img[0], a0 = 0, b0;
int d[16];
#define LOAD2(idx, ofs) \
tofs = ofs; d[idx] = (short)(v - img[tofs]); d[idx+8] = (short)(v - img[-tofs])
LOAD2(0, 3);
LOAD2(1, -step+3);
LOAD2(2, -step*2+2);
LOAD2(3, -step*3+1);
LOAD2(4, -step*3);
LOAD2(5, -step*3-1);
LOAD2(6, -step*2-2);
LOAD2(7, -step-3);
#pragma unroll
for( k = 0; k < 16; k += 2 )
{
int a = min((int)d[(k+1)&15], (int)d[(k+2)&15]);
a = min(a, (int)d[(k+3)&15]);
a = min(a, (int)d[(k+4)&15]);
a = min(a, (int)d[(k+5)&15]);
a = min(a, (int)d[(k+6)&15]);
a = min(a, (int)d[(k+7)&15]);
a = min(a, (int)d[(k+8)&15]);
a0 = max(a0, min(a, (int)d[k&15]));
a0 = max(a0, min(a, (int)d[(k+9)&15]));
}
b0 = -a0;
#pragma unroll
for( k = 0; k < 16; k += 2 )
{
int b = max((int)d[(k+1)&15], (int)d[(k+2)&15]);
b = max(b, (int)d[(k+3)&15]);
b = max(b, (int)d[(k+4)&15]);
b = max(b, (int)d[(k+5)&15]);
b = max(b, (int)d[(k+6)&15]);
b = max(b, (int)d[(k+7)&15]);
b = max(b, (int)d[(k+8)&15]);
b0 = min(b0, max(b, (int)d[k]));
b0 = min(b0, max(b, (int)d[(k+9)&15]));
}
return -b0-1;
}
__kernel
void FAST_findKeypoints(
__global const uchar * _img, int step, int img_offset,
int img_rows, int img_cols,
volatile __global int* kp_loc,
int max_keypoints, int threshold )
{
int j = get_global_id(0) + 3;
int i = get_global_id(1) + 3;
if (i < img_rows - 3 && j < img_cols - 3)
{
__global const uchar* img = _img + mad24(i, step, j + img_offset);
int v = img[0], t0 = v - threshold, t1 = v + threshold;
int k, tofs, v0, v1;
int m0 = 0, m1 = 0;
#define UPDATE_MASK(idx, ofs) \
tofs = ofs; v0 = img[tofs]; v1 = img[-tofs]; \
m0 |= ((v0 < t0) << idx) | ((v1 < t0) << (8 + idx)); \
m1 |= ((v0 > t1) << idx) | ((v1 > t1) << (8 + idx))
UPDATE_MASK(0, 3);
if( (m0 | m1) == 0 )
return;
UPDATE_MASK(2, -step*2+2);
UPDATE_MASK(4, -step*3);
UPDATE_MASK(6, -step*2-2);
#define EVEN_MASK (1+4+16+64)
if( ((m0 | (m0 >> 8)) & EVEN_MASK) != EVEN_MASK &&
((m1 | (m1 >> 8)) & EVEN_MASK) != EVEN_MASK )
return;
UPDATE_MASK(1, -step+3);
UPDATE_MASK(3, -step*3+1);
UPDATE_MASK(5, -step*3-1);
UPDATE_MASK(7, -step-3);
if( ((m0 | (m0 >> 8)) & 255) != 255 &&
((m1 | (m1 >> 8)) & 255) != 255 )
return;
m0 |= m0 << 16;
m1 |= m1 << 16;
#define CHECK0(i) ((m0 & (511 << i)) == (511 << i))
#define CHECK1(i) ((m1 & (511 << i)) == (511 << i))
if( CHECK0(0) + CHECK0(1) + CHECK0(2) + CHECK0(3) +
CHECK0(4) + CHECK0(5) + CHECK0(6) + CHECK0(7) +
CHECK0(8) + CHECK0(9) + CHECK0(10) + CHECK0(11) +
CHECK0(12) + CHECK0(13) + CHECK0(14) + CHECK0(15) +
CHECK1(0) + CHECK1(1) + CHECK1(2) + CHECK1(3) +
CHECK1(4) + CHECK1(5) + CHECK1(6) + CHECK1(7) +
CHECK1(8) + CHECK1(9) + CHECK1(10) + CHECK1(11) +
CHECK1(12) + CHECK1(13) + CHECK1(14) + CHECK1(15) == 0 )
return;
{
int idx = atomic_inc(kp_loc);
if( idx < max_keypoints )
{
kp_loc[1 + 2*idx] = j;
kp_loc[2 + 2*idx] = i;
}
}
}
}
///////////////////////////////////////////////////////////////////////////
// nonmaxSupression
__kernel
void FAST_nonmaxSupression(
__global const int* kp_in, volatile __global int* kp_out,
__global const uchar * _img, int step, int img_offset,
int rows, int cols, int counter, int max_keypoints)
{
const int idx = get_global_id(0);
if (idx < counter)
{
int x = kp_in[1 + 2*idx];
int y = kp_in[2 + 2*idx];
__global const uchar* img = _img + mad24(y, step, x + img_offset);
int s = cornerScore(img, step);
if( (x < 4 || s > cornerScore(img-1, step)) +
(y < 4 || s > cornerScore(img-step, step)) != 2 )
return;
if( (x >= cols - 4 || s > cornerScore(img+1, step)) +
(y >= rows - 4 || s > cornerScore(img+step, step)) +
(x < 4 || y < 4 || s > cornerScore(img-step-1, step)) +
(x >= cols - 4 || y < 4 || s > cornerScore(img-step+1, step)) +
(x < 4 || y >= rows - 4 || s > cornerScore(img+step-1, step)) +
(x >= cols - 4 || y >= rows - 4 || s > cornerScore(img+step+1, step)) == 6)
{
int new_idx = atomic_inc(kp_out);
if( new_idx < max_keypoints )
{
kp_out[1 + 3*new_idx] = x;
kp_out[2 + 3*new_idx] = y;
kp_out[3 + 3*new_idx] = s;
}
}
}
}
+161
View File
@@ -0,0 +1,161 @@
/*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) 2010-2012, Institute Of Software Chinese Academy Of Science, all rights reserved.
// Copyright (C) 2010-2012, Advanced Micro Devices, Inc., all rights reserved.
// Third party copyrights are property of their respective owners.
//
// @Authors
// Zhang Ying, zhangying913@gmail.com
//
// 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*/
#ifdef OP_MAX_EIGEN_VAL
__kernel void maxEigenVal(__global const uchar * srcptr, int src_step, int src_offset, int cols,
int total, __global uchar * dstptr
#ifdef HAVE_MASK
, __global const uchar * maskptr, int mask_step, int mask_offset
#endif
)
{
int lid = get_local_id(0);
int gid = get_group_id(0);
int id = get_global_id(0);
__local float localmem_max[WGS2_ALIGNED];
float maxval = -FLT_MAX;
for (int grain = groupnum * WGS; id < total; id += grain)
{
int src_index = mad24(id / cols, src_step, mad24((id % cols), (int)sizeof(float), src_offset));
#ifdef HAVE_MASK
int mask_index = mad24(id / cols, mask_step, id % cols + mask_offset);
if (maskptr[mask_index])
#endif
maxval = max(maxval, *(__global const float *)(srcptr + src_index));
}
if (lid < WGS2_ALIGNED)
localmem_max[lid] = maxval;
barrier(CLK_LOCAL_MEM_FENCE);
if (lid >= WGS2_ALIGNED && total >= WGS2_ALIGNED)
localmem_max[lid - WGS2_ALIGNED] = max(maxval, localmem_max[lid - WGS2_ALIGNED]);
barrier(CLK_LOCAL_MEM_FENCE);
for (int lsize = WGS2_ALIGNED >> 1; lsize > 0; lsize >>= 1)
{
if (lid < lsize)
{
int lid2 = lsize + lid;
localmem_max[lid] = max(localmem_max[lid], localmem_max[lid2]);
}
barrier(CLK_LOCAL_MEM_FENCE);
}
if (lid == 0)
*(__global float *)(dstptr + (int)sizeof(float) * gid) = localmem_max[0];
}
__kernel void maxEigenValTask(__global float * dst, float qualityLevel,
__global int * cornersptr)
{
float maxval = -FLT_MAX;
#pragma unroll
for (int x = 0; x < groupnum; ++x)
maxval = max(maxval, dst[x]);
dst[0] = maxval * qualityLevel;
cornersptr[0] = 0;
}
#elif OP_FIND_CORNERS
#define GET_SRC_32F(_y, _x) *(__global const float *)(eigptr + (_y) * eig_step + (_x) * (int)sizeof(float) )
__kernel void findCorners(__global const uchar * eigptr, int eig_step, int eig_offset,
#ifdef HAVE_MASK
__global const uchar * mask, int mask_step, int mask_offset,
#endif
__global uchar * cornersptr, int rows, int cols,
__constant float * threshold, int max_corners)
{
int x = get_global_id(0);
int y = get_global_id(1);
__global int* counter = (__global int*) cornersptr;
__global float2 * corners = (__global float2 *)(cornersptr + (int)sizeof(float2));
if (y < rows && x < cols
#ifdef HAVE_MASK
&& mask[mad24(y, mask_step, x + mask_offset)]
#endif
)
{
++x, ++y;
float val = GET_SRC_32F(y, x);
if (val > threshold[0])
{
float maxVal = val;
maxVal = max(GET_SRC_32F(y - 1, x - 1), maxVal);
maxVal = max(GET_SRC_32F(y - 1, x ), maxVal);
maxVal = max(GET_SRC_32F(y - 1, x + 1), maxVal);
maxVal = max(GET_SRC_32F(y , x - 1), maxVal);
maxVal = max(GET_SRC_32F(y , x + 1), maxVal);
maxVal = max(GET_SRC_32F(y + 1, x - 1), maxVal);
maxVal = max(GET_SRC_32F(y + 1, x ), maxVal);
maxVal = max(GET_SRC_32F(y + 1, x + 1), maxVal);
if (val == maxVal)
{
int ind = atomic_inc(counter);
if (ind < max_corners)
{
// pack and store eigenvalue and its coordinates
corners[ind].x = val;
corners[ind].y = as_float(y | (x << 16));
}
}
}
}
}
#endif
+254
View File
@@ -0,0 +1,254 @@
// OpenCL port of the ORB feature detector and descriptor extractor
// Copyright (C) 2014, Itseez Inc. See the license at http://opencv.org
//
// The original code has been contributed by Peter Andreas Entschev, peter@entschev.com
#define LAYERINFO_SIZE 1
#define LAYERINFO_OFS 0
#define KEYPOINT_SIZE 3
#define ORIENTED_KEYPOINT_SIZE 4
#define KEYPOINT_X 0
#define KEYPOINT_Y 1
#define KEYPOINT_Z 2
#define KEYPOINT_ANGLE 3
/////////////////////////////////////////////////////////////
#ifdef ORB_RESPONSES
__kernel void
ORB_HarrisResponses(__global const uchar* imgbuf, int imgstep, int imgoffset0,
__global const int* layerinfo, __global const int* keypoints,
__global float* responses, int nkeypoints )
{
int idx = get_global_id(0);
if( idx < nkeypoints )
{
__global const int* kpt = keypoints + idx*KEYPOINT_SIZE;
__global const int* layer = layerinfo + kpt[KEYPOINT_Z]*LAYERINFO_SIZE;
__global const uchar* img = imgbuf + imgoffset0 + layer[LAYERINFO_OFS] +
(kpt[KEYPOINT_Y] - blockSize/2)*imgstep + (kpt[KEYPOINT_X] - blockSize/2);
int i, j;
int a = 0, b = 0, c = 0;
for( i = 0; i < blockSize; i++, img += imgstep-blockSize )
{
for( j = 0; j < blockSize; j++, img++ )
{
int Ix = (img[1] - img[-1])*2 + img[-imgstep+1] - img[-imgstep-1] + img[imgstep+1] - img[imgstep-1];
int Iy = (img[imgstep] - img[-imgstep])*2 + img[imgstep-1] - img[-imgstep-1] + img[imgstep+1] - img[-imgstep+1];
a += Ix*Ix;
b += Iy*Iy;
c += Ix*Iy;
}
}
responses[idx] = ((float)a * b - (float)c * c - HARRIS_K * (float)(a + b) * (a + b))*scale_sq_sq;
}
}
#endif
/////////////////////////////////////////////////////////////
#ifdef ORB_ANGLES
#define _DBL_EPSILON 2.2204460492503131e-16f
#define atan2_p1 (0.9997878412794807f*57.29577951308232f)
#define atan2_p3 (-0.3258083974640975f*57.29577951308232f)
#define atan2_p5 (0.1555786518463281f*57.29577951308232f)
#define atan2_p7 (-0.04432655554792128f*57.29577951308232f)
inline float fastAtan2( float y, float x )
{
float ax = fabs(x), ay = fabs(y);
float a, c, c2;
if( ax >= ay )
{
c = ay/(ax + _DBL_EPSILON);
c2 = c*c;
a = (((atan2_p7*c2 + atan2_p5)*c2 + atan2_p3)*c2 + atan2_p1)*c;
}
else
{
c = ax/(ay + _DBL_EPSILON);
c2 = c*c;
a = 90.f - (((atan2_p7*c2 + atan2_p5)*c2 + atan2_p3)*c2 + atan2_p1)*c;
}
if( x < 0 )
a = 180.f - a;
if( y < 0 )
a = 360.f - a;
return a;
}
__kernel void
ORB_ICAngle(__global const uchar* imgbuf, int imgstep, int imgoffset0,
__global const int* layerinfo, __global const int* keypoints,
__global float* responses, const __global int* u_max,
int nkeypoints, int half_k )
{
int idx = get_global_id(0);
if( idx < nkeypoints )
{
__global const int* kpt = keypoints + idx*KEYPOINT_SIZE;
__global const int* layer = layerinfo + kpt[KEYPOINT_Z]*LAYERINFO_SIZE;
__global const uchar* center = imgbuf + imgoffset0 + layer[LAYERINFO_OFS] +
kpt[KEYPOINT_Y]*imgstep + kpt[KEYPOINT_X];
int u, v, m_01 = 0, m_10 = 0;
// Treat the center line differently, v=0
for( u = -half_k; u <= half_k; u++ )
m_10 += u * center[u];
// Go line by line in the circular patch
for( v = 1; v <= half_k; v++ )
{
// Proceed over the two lines
int v_sum = 0;
int d = u_max[v];
for( u = -d; u <= d; u++ )
{
int val_plus = center[u + v*imgstep], val_minus = center[u - v*imgstep];
v_sum += (val_plus - val_minus);
m_10 += u * (val_plus + val_minus);
}
m_01 += v * v_sum;
}
// we do not use OpenCL's atan2 intrinsic,
// because we want to get _exactly_ the same results as the CPU version
responses[idx] = fastAtan2((float)m_01, (float)m_10);
}
}
#endif
/////////////////////////////////////////////////////////////
#ifdef ORB_DESCRIPTORS
__kernel void
ORB_computeDescriptor(__global const uchar* imgbuf, int imgstep, int imgoffset0,
__global const int* layerinfo, __global const int* keypoints,
__global uchar* _desc, const __global int* pattern,
int nkeypoints, int dsize )
{
int idx = get_global_id(0);
if( idx < nkeypoints )
{
int i;
__global const int* kpt = keypoints + idx*ORIENTED_KEYPOINT_SIZE;
__global const int* layer = layerinfo + kpt[KEYPOINT_Z]*LAYERINFO_SIZE;
__global const uchar* center = imgbuf + imgoffset0 + layer[LAYERINFO_OFS] +
kpt[KEYPOINT_Y]*imgstep + kpt[KEYPOINT_X];
float angle = as_float(kpt[KEYPOINT_ANGLE]);
angle *= 0.01745329251994329547f;
float cosa;
float sina = sincos(angle, &cosa);
__global uchar* desc = _desc + idx*dsize;
#define GET_VALUE(idx) \
center[mad24(convert_int_rte(pattern[(idx)*2] * sina + pattern[(idx)*2+1] * cosa), imgstep, \
convert_int_rte(pattern[(idx)*2] * cosa - pattern[(idx)*2+1] * sina))]
for( i = 0; i < dsize; i++ )
{
int val;
#if WTA_K == 2
int t0, t1;
t0 = GET_VALUE(0); t1 = GET_VALUE(1);
val = t0 < t1;
t0 = GET_VALUE(2); t1 = GET_VALUE(3);
val |= (t0 < t1) << 1;
t0 = GET_VALUE(4); t1 = GET_VALUE(5);
val |= (t0 < t1) << 2;
t0 = GET_VALUE(6); t1 = GET_VALUE(7);
val |= (t0 < t1) << 3;
t0 = GET_VALUE(8); t1 = GET_VALUE(9);
val |= (t0 < t1) << 4;
t0 = GET_VALUE(10); t1 = GET_VALUE(11);
val |= (t0 < t1) << 5;
t0 = GET_VALUE(12); t1 = GET_VALUE(13);
val |= (t0 < t1) << 6;
t0 = GET_VALUE(14); t1 = GET_VALUE(15);
val |= (t0 < t1) << 7;
pattern += 16*2;
#elif WTA_K == 3
int t0, t1, t2;
t0 = GET_VALUE(0); t1 = GET_VALUE(1); t2 = GET_VALUE(2);
val = t2 > t1 ? (t2 > t0 ? 2 : 0) : (t1 > t0);
t0 = GET_VALUE(3); t1 = GET_VALUE(4); t2 = GET_VALUE(5);
val |= (t2 > t1 ? (t2 > t0 ? 2 : 0) : (t1 > t0)) << 2;
t0 = GET_VALUE(6); t1 = GET_VALUE(7); t2 = GET_VALUE(8);
val |= (t2 > t1 ? (t2 > t0 ? 2 : 0) : (t1 > t0)) << 4;
t0 = GET_VALUE(9); t1 = GET_VALUE(10); t2 = GET_VALUE(11);
val |= (t2 > t1 ? (t2 > t0 ? 2 : 0) : (t1 > t0)) << 6;
pattern += 12*2;
#elif WTA_K == 4
int t0, t1, t2, t3, k;
int a, b;
t0 = GET_VALUE(0); t1 = GET_VALUE(1);
t2 = GET_VALUE(2); t3 = GET_VALUE(3);
a = 0, b = 2;
if( t1 > t0 ) t0 = t1, a = 1;
if( t3 > t2 ) t2 = t3, b = 3;
k = t0 > t2 ? a : b;
val = k;
t0 = GET_VALUE(4); t1 = GET_VALUE(5);
t2 = GET_VALUE(6); t3 = GET_VALUE(7);
a = 0, b = 2;
if( t1 > t0 ) t0 = t1, a = 1;
if( t3 > t2 ) t2 = t3, b = 3;
k = t0 > t2 ? a : b;
val |= k << 2;
t0 = GET_VALUE(8); t1 = GET_VALUE(9);
t2 = GET_VALUE(10); t3 = GET_VALUE(11);
a = 0, b = 2;
if( t1 > t0 ) t0 = t1, a = 1;
if( t3 > t2 ) t2 = t3, b = 3;
k = t0 > t2 ? a : b;
val |= k << 4;
t0 = GET_VALUE(12); t1 = GET_VALUE(13);
t2 = GET_VALUE(14); t3 = GET_VALUE(15);
a = 0, b = 2;
if( t1 > t0 ) t0 = t1, a = 1;
if( t3 > t2 ) t2 = t3, b = 3;
k = t0 > t2 ? a : b;
val |= k << 6;
pattern += 16*2;
#else
#error "unknown/undefined WTA_K value; should be 2, 3 or 4"
#endif
desc[i] = (uchar)val;
}
}
}
#endif
File diff suppressed because it is too large Load Diff
+61
View File
@@ -0,0 +1,61 @@
/*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*/
#ifndef __OPENCV_PRECOMP_H__
#define __OPENCV_PRECOMP_H__
#include "opencv2/features.hpp"
#include "opencv2/imgproc.hpp"
#include "opencv2/geometry.hpp"
#ifdef HAVE_OPENCV_DNN
#include "opencv2/dnn.hpp"
#endif
#include "opencv2/core/utility.hpp"
#include "opencv2/core/private.hpp"
#include "opencv2/core/ocl.hpp"
#include "opencv2/core/hal/hal.hpp"
#include <algorithm>
#endif
+622
View File
@@ -0,0 +1,622 @@
// 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.
//
// Copyright (c) 2006-2010, Rob Hess <hess@eecs.oregonstate.edu>
// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
// Copyright (C) 2020, Intel Corporation, all rights reserved.
/**********************************************************************************************\
Implementation of SIFT is based on the code from http://blogs.oregonstate.edu/hess/code/sift/
Below is the original copyright.
Patent US6711293 expired in March 2020.
// Copyright (c) 2006-2010, Rob Hess <hess@eecs.oregonstate.edu>
// All rights reserved.
// The following patent has been issued for methods embodied in this
// software: "Method and apparatus for identifying scale invariant features
// in an image and use of same for locating an object in an image," David
// G. Lowe, US Patent 6,711,293 (March 23, 2004). Provisional application
// filed March 8, 1999. Assignee: The University of British Columbia. For
// further details, contact David Lowe (lowe@cs.ubc.ca) or the
// University-Industry Liaison Office of the University of British
// Columbia.
// Note that restrictions imposed by this patent (and possibly others)
// exist independently of and may be in conflict with the freedoms granted
// in this license, which refers to copyright of the program, not patents
// for any methods that it implements. Both copyright and patent law must
// be obeyed to legally use and redistribute this program and it is not the
// purpose of this license to induce you to infringe any patents or other
// property right claims or to contest validity of any such claims. If you
// redistribute or use the program, then this license merely protects you
// from committing copyright infringement. It does not protect you from
// committing patent infringement. So, before you do anything with this
// program, make sure that you have permission to do so not merely in terms
// of copyright, but also in terms of patent law.
// Please note that this license is not to be understood as a guarantee
// either. If you use the program according to this license, but in
// conflict with patent law, it does not mean that the licensor will refund
// you for any losses that you incur if you are sued for your patent
// infringement.
// 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 and
// patent notices, this list of conditions and the following
// disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in
// the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Oregon State University 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 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 "precomp.hpp"
#include <opencv2/core/hal/hal.hpp>
#include <opencv2/core/utils/tls.hpp>
#include <opencv2/core/utils/logger.hpp>
#include "sift.simd.hpp"
#include "sift.simd_declarations.hpp" // defines CV_CPU_DISPATCH_MODES_ALL=AVX2,...,BASELINE based on CMakeLists.txt content
namespace cv {
/*!
SIFT implementation.
The class implements SIFT algorithm by D. Lowe.
*/
class SIFT_Impl : public SIFT
{
public:
explicit SIFT_Impl( int nfeatures = 0, int nOctaveLayers = 3,
double contrastThreshold = 0.04, double edgeThreshold = 10,
double sigma = 1.6, int descriptorType = CV_32F,
bool enable_precise_upscale = true );
//! returns the descriptor size in floats (128)
int descriptorSize() const CV_OVERRIDE;
//! returns the descriptor type
int descriptorType() const CV_OVERRIDE;
//! returns the default norm type
int defaultNorm() const CV_OVERRIDE;
//! finds the keypoints and computes descriptors for them using SIFT algorithm.
//! Optionally it can compute descriptors for the user-provided keypoints
void detectAndCompute(InputArray img, InputArray mask,
std::vector<KeyPoint>& keypoints,
OutputArray descriptors,
bool useProvidedKeypoints = false) CV_OVERRIDE;
void buildGaussianPyramid( const Mat& base, std::vector<Mat>& pyr, int nOctaves ) const;
void buildDoGPyramid( const std::vector<Mat>& pyr, std::vector<Mat>& dogpyr ) const;
void findScaleSpaceExtrema( const std::vector<Mat>& gauss_pyr, const std::vector<Mat>& dog_pyr,
std::vector<KeyPoint>& keypoints ) const;
void read( const FileNode& fn) CV_OVERRIDE;
void write( FileStorage& fs) const CV_OVERRIDE;
void setNFeatures(int maxFeatures) CV_OVERRIDE { nfeatures = maxFeatures; }
int getNFeatures() const CV_OVERRIDE { return nfeatures; }
void setNOctaveLayers(int nOctaveLayers_) CV_OVERRIDE { nOctaveLayers = nOctaveLayers_; }
int getNOctaveLayers() const CV_OVERRIDE { return nOctaveLayers; }
void setContrastThreshold(double contrastThreshold_) CV_OVERRIDE { contrastThreshold = contrastThreshold_; }
double getContrastThreshold() const CV_OVERRIDE { return contrastThreshold; }
void setEdgeThreshold(double edgeThreshold_) CV_OVERRIDE { edgeThreshold = edgeThreshold_; }
double getEdgeThreshold() const CV_OVERRIDE { return edgeThreshold; }
void setSigma(double sigma_) CV_OVERRIDE { sigma = sigma_; }
double getSigma() const CV_OVERRIDE { return sigma; }
protected:
CV_PROP_RW int nfeatures;
CV_PROP_RW int nOctaveLayers;
CV_PROP_RW double contrastThreshold;
CV_PROP_RW double edgeThreshold;
CV_PROP_RW double sigma;
CV_PROP_RW int descriptor_type;
CV_PROP_RW bool enable_precise_upscale;
};
Ptr<SIFT> SIFT::create( int _nfeatures, int _nOctaveLayers,
double _contrastThreshold, double _edgeThreshold, double _sigma, bool enable_precise_upscale )
{
CV_TRACE_FUNCTION();
return makePtr<SIFT_Impl>(_nfeatures, _nOctaveLayers, _contrastThreshold, _edgeThreshold, _sigma, CV_32F, enable_precise_upscale);
}
Ptr<SIFT> SIFT::create( int _nfeatures, int _nOctaveLayers,
double _contrastThreshold, double _edgeThreshold, double _sigma, int _descriptorType, bool enable_precise_upscale )
{
CV_TRACE_FUNCTION();
// SIFT descriptor supports 32bit floating point and 8bit unsigned int.
CV_Assert(_descriptorType == CV_32F || _descriptorType == CV_8U);
return makePtr<SIFT_Impl>(_nfeatures, _nOctaveLayers, _contrastThreshold, _edgeThreshold, _sigma, _descriptorType, enable_precise_upscale);
}
String SIFT::getDefaultName() const
{
return (Feature2D::getDefaultName() + ".SIFT");
}
static inline void
unpackOctave(const KeyPoint& kpt, int& octave, int& layer, float& scale)
{
octave = kpt.octave & 255;
layer = (kpt.octave >> 8) & 255;
octave = octave < 128 ? octave : (-128 | octave);
scale = octave >= 0 ? 1.f/(1 << octave) : (float)(1 << -octave);
}
static Mat createInitialImage( const Mat& img, bool doubleImageSize, float sigma, bool enable_precise_upscale )
{
CV_TRACE_FUNCTION();
Mat gray, gray_fpt;
if( img.channels() == 3 || img.channels() == 4 )
{
cvtColor(img, gray, COLOR_BGR2GRAY);
gray.convertTo(gray_fpt, DataType<sift_wt>::type, SIFT_FIXPT_SCALE, 0);
}
else
img.convertTo(gray_fpt, DataType<sift_wt>::type, SIFT_FIXPT_SCALE, 0);
float sig_diff;
if( doubleImageSize )
{
sig_diff = sqrtf( std::max(sigma * sigma - SIFT_INIT_SIGMA * SIFT_INIT_SIGMA * 4, 0.01f) );
Mat dbl;
if (enable_precise_upscale) {
dbl.create(Size(gray_fpt.cols*2, gray_fpt.rows*2), gray_fpt.type());
Mat H = Mat::zeros(2, 3, CV_32F);
H.at<float>(0, 0) = 0.5f;
H.at<float>(1, 1) = 0.5f;
cv::warpAffine(gray_fpt, dbl, H, dbl.size(), INTER_LINEAR | WARP_INVERSE_MAP, BORDER_REFLECT);
} else {
#if DoG_TYPE_SHORT
resize(gray_fpt, dbl, Size(gray_fpt.cols*2, gray_fpt.rows*2), 0, 0, INTER_LINEAR_EXACT);
#else
resize(gray_fpt, dbl, Size(gray_fpt.cols*2, gray_fpt.rows*2), 0, 0, INTER_LINEAR);
#endif
}
Mat result;
GaussianBlur(dbl, result, Size(), sig_diff, sig_diff);
return result;
}
else
{
sig_diff = sqrtf( std::max(sigma * sigma - SIFT_INIT_SIGMA * SIFT_INIT_SIGMA, 0.01f) );
Mat result;
GaussianBlur(gray_fpt, result, Size(), sig_diff, sig_diff);
return result;
}
}
void SIFT_Impl::buildGaussianPyramid( const Mat& base, std::vector<Mat>& pyr, int nOctaves ) const
{
CV_TRACE_FUNCTION();
std::vector<double> sig(nOctaveLayers + 3);
pyr.resize(nOctaves*(nOctaveLayers + 3));
// precompute Gaussian sigmas using the following formula:
// \sigma_{total}^2 = \sigma_{i}^2 + \sigma_{i-1}^2
sig[0] = sigma;
double k = std::pow( 2, 1. / nOctaveLayers );
for( int i = 1; i < nOctaveLayers + 3; i++ )
{
double sig_prev = (double)std::pow(k, i-1)*sigma;
double sig_total = sig_prev*k;
sig[i] = std::sqrt(sig_total*sig_total - sig_prev*sig_prev);
}
for( int o = 0; o < nOctaves; o++ )
{
for( int i = 0; i < nOctaveLayers + 3; i++ )
{
Mat& dst = pyr[o*(nOctaveLayers + 3) + i];
if( o == 0 && i == 0 )
dst = base;
// base of new octave is halved image from end of previous octave
else if( i == 0 )
{
const Mat& src = pyr[(o-1)*(nOctaveLayers + 3) + nOctaveLayers];
resize(src, dst, Size(src.cols/2, src.rows/2),
0, 0, INTER_NEAREST);
}
else
{
const Mat& src = pyr[o*(nOctaveLayers + 3) + i-1];
GaussianBlur(src, dst, Size(), sig[i], sig[i]);
}
}
}
}
class buildDoGPyramidComputer : public ParallelLoopBody
{
public:
buildDoGPyramidComputer(
int _nOctaveLayers,
const std::vector<Mat>& _gpyr,
std::vector<Mat>& _dogpyr)
: nOctaveLayers(_nOctaveLayers),
gpyr(_gpyr),
dogpyr(_dogpyr) { }
void operator()( const cv::Range& range ) const CV_OVERRIDE
{
CV_TRACE_FUNCTION();
const int begin = range.start;
const int end = range.end;
for( int a = begin; a < end; a++ )
{
const int o = a / (nOctaveLayers + 2);
const int i = a % (nOctaveLayers + 2);
const Mat& src1 = gpyr[o*(nOctaveLayers + 3) + i];
const Mat& src2 = gpyr[o*(nOctaveLayers + 3) + i + 1];
Mat& dst = dogpyr[o*(nOctaveLayers + 2) + i];
subtract(src2, src1, dst, noArray(), DataType<sift_wt>::type);
}
}
private:
int nOctaveLayers;
const std::vector<Mat>& gpyr;
std::vector<Mat>& dogpyr;
};
void SIFT_Impl::buildDoGPyramid( const std::vector<Mat>& gpyr, std::vector<Mat>& dogpyr ) const
{
CV_TRACE_FUNCTION();
int nOctaves = (int)gpyr.size()/(nOctaveLayers + 3);
dogpyr.resize( nOctaves*(nOctaveLayers + 2) );
parallel_for_(Range(0, nOctaves * (nOctaveLayers + 2)), buildDoGPyramidComputer(nOctaveLayers, gpyr, dogpyr));
}
class findScaleSpaceExtremaComputer : public ParallelLoopBody
{
public:
findScaleSpaceExtremaComputer(
int _o,
int _i,
int _threshold,
int _idx,
int _step,
int _cols,
int _nOctaveLayers,
double _contrastThreshold,
double _edgeThreshold,
double _sigma,
const std::vector<Mat>& _gauss_pyr,
const std::vector<Mat>& _dog_pyr,
TLSData<std::vector<KeyPoint> > &_tls_kpts_struct)
: o(_o),
i(_i),
threshold(_threshold),
idx(_idx),
step(_step),
cols(_cols),
nOctaveLayers(_nOctaveLayers),
contrastThreshold(_contrastThreshold),
edgeThreshold(_edgeThreshold),
sigma(_sigma),
gauss_pyr(_gauss_pyr),
dog_pyr(_dog_pyr),
tls_kpts_struct(_tls_kpts_struct) { }
void operator()( const cv::Range& range ) const CV_OVERRIDE
{
CV_TRACE_FUNCTION();
std::vector<KeyPoint>& kpts = tls_kpts_struct.getRef();
CV_CPU_DISPATCH(findScaleSpaceExtrema, (o, i, threshold, idx, step, cols, nOctaveLayers, contrastThreshold, edgeThreshold, sigma, gauss_pyr, dog_pyr, kpts, range),
CV_CPU_DISPATCH_MODES_ALL);
}
private:
int o, i;
int threshold;
int idx, step, cols;
int nOctaveLayers;
double contrastThreshold;
double edgeThreshold;
double sigma;
const std::vector<Mat>& gauss_pyr;
const std::vector<Mat>& dog_pyr;
TLSData<std::vector<KeyPoint> > &tls_kpts_struct;
};
//
// Detects features at extrema in DoG scale space. Bad features are discarded
// based on contrast and ratio of principal curvatures.
void SIFT_Impl::findScaleSpaceExtrema( const std::vector<Mat>& gauss_pyr, const std::vector<Mat>& dog_pyr,
std::vector<KeyPoint>& keypoints ) const
{
CV_TRACE_FUNCTION();
const int nOctaves = (int)gauss_pyr.size()/(nOctaveLayers + 3);
const int threshold = cvFloor(0.5 * contrastThreshold / nOctaveLayers * 255 * SIFT_FIXPT_SCALE);
keypoints.clear();
TLSDataAccumulator<std::vector<KeyPoint> > tls_kpts_struct;
for( int o = 0; o < nOctaves; o++ )
for( int i = 1; i <= nOctaveLayers; i++ )
{
const int idx = o*(nOctaveLayers+2)+i;
const Mat& img = dog_pyr[idx];
const int step = (int)img.step1();
const int rows = img.rows, cols = img.cols;
parallel_for_(Range(SIFT_IMG_BORDER, rows-SIFT_IMG_BORDER),
findScaleSpaceExtremaComputer(
o, i, threshold, idx, step, cols,
nOctaveLayers,
contrastThreshold,
edgeThreshold,
sigma,
gauss_pyr, dog_pyr, tls_kpts_struct));
}
std::vector<std::vector<KeyPoint>*> kpt_vecs;
tls_kpts_struct.gather(kpt_vecs);
for (size_t i = 0; i < kpt_vecs.size(); ++i) {
keypoints.insert(keypoints.end(), kpt_vecs[i]->begin(), kpt_vecs[i]->end());
}
}
static
void calcSIFTDescriptor(
const Mat& img, Point2f ptf, float ori, float scl,
int d, int n, Mat& dst, int row
)
{
CV_TRACE_FUNCTION();
CV_CPU_DISPATCH(calcSIFTDescriptor, (img, ptf, ori, scl, d, n, dst, row),
CV_CPU_DISPATCH_MODES_ALL);
}
class calcDescriptorsComputer : public ParallelLoopBody
{
public:
calcDescriptorsComputer(const std::vector<Mat>& _gpyr,
const std::vector<KeyPoint>& _keypoints,
Mat& _descriptors,
int _nOctaveLayers,
int _firstOctave)
: gpyr(_gpyr),
keypoints(_keypoints),
descriptors(_descriptors),
nOctaveLayers(_nOctaveLayers),
firstOctave(_firstOctave) { }
void operator()( const cv::Range& range ) const CV_OVERRIDE
{
CV_TRACE_FUNCTION();
const int begin = range.start;
const int end = range.end;
static const int d = SIFT_DESCR_WIDTH, n = SIFT_DESCR_HIST_BINS;
for ( int i = begin; i<end; i++ )
{
KeyPoint kpt = keypoints[i];
int octave, layer;
float scale;
unpackOctave(kpt, octave, layer, scale);
CV_Assert(octave >= firstOctave && layer <= nOctaveLayers+2);
float size=kpt.size*scale;
Point2f ptf(kpt.pt.x*scale, kpt.pt.y*scale);
const Mat& img = gpyr[(octave - firstOctave)*(nOctaveLayers + 3) + layer];
float angle = 360.f - kpt.angle;
if(std::abs(angle - 360.f) < FLT_EPSILON)
angle = 0.f;
calcSIFTDescriptor(img, ptf, angle, size*0.5f, d, n, descriptors, i);
}
}
private:
const std::vector<Mat>& gpyr;
const std::vector<KeyPoint>& keypoints;
Mat& descriptors;
int nOctaveLayers;
int firstOctave;
};
static void calcDescriptors(const std::vector<Mat>& gpyr, const std::vector<KeyPoint>& keypoints,
Mat& descriptors, int nOctaveLayers, int firstOctave )
{
CV_TRACE_FUNCTION();
parallel_for_(Range(0, static_cast<int>(keypoints.size())), calcDescriptorsComputer(gpyr, keypoints, descriptors, nOctaveLayers, firstOctave));
}
//////////////////////////////////////////////////////////////////////////////////////////
SIFT_Impl::SIFT_Impl( int _nfeatures, int _nOctaveLayers,
double _contrastThreshold, double _edgeThreshold, double _sigma, int _descriptorType, bool _enable_precise_upscale)
: nfeatures(_nfeatures), nOctaveLayers(_nOctaveLayers),
contrastThreshold(_contrastThreshold), edgeThreshold(_edgeThreshold), sigma(_sigma), descriptor_type(_descriptorType),
enable_precise_upscale(_enable_precise_upscale)
{
if (!enable_precise_upscale) {
CV_LOG_ONCE_INFO(NULL, "precise upscale disabled, this is now deprecated as it was found to induce a location bias");
}
}
int SIFT_Impl::descriptorSize() const
{
return SIFT_DESCR_WIDTH*SIFT_DESCR_WIDTH*SIFT_DESCR_HIST_BINS;
}
int SIFT_Impl::descriptorType() const
{
return descriptor_type;
}
int SIFT_Impl::defaultNorm() const
{
return NORM_L2;
}
void SIFT_Impl::detectAndCompute(InputArray _image, InputArray _mask,
std::vector<KeyPoint>& keypoints,
OutputArray _descriptors,
bool useProvidedKeypoints)
{
CV_TRACE_FUNCTION();
int firstOctave = -1, actualNOctaves = 0, actualNLayers = 0;
Mat image = _image.getMat(), mask = _mask.getMat();
if( image.empty() || image.depth() != CV_8U )
CV_Error( Error::StsBadArg, "image is empty or has incorrect depth (!=CV_8U)" );
if( !mask.empty() && mask.type() != CV_8UC1 )
CV_Error( Error::StsBadArg, "mask has incorrect type (!=CV_8UC1)" );
if( useProvidedKeypoints )
{
firstOctave = 0;
int maxOctave = INT_MIN;
for( size_t i = 0; i < keypoints.size(); i++ )
{
int octave, layer;
float scale;
unpackOctave(keypoints[i], octave, layer, scale);
firstOctave = std::min(firstOctave, octave);
maxOctave = std::max(maxOctave, octave);
actualNLayers = std::max(actualNLayers, layer-2);
}
firstOctave = std::min(firstOctave, 0);
CV_Assert( firstOctave >= -1 && actualNLayers <= nOctaveLayers );
actualNOctaves = maxOctave - firstOctave + 1;
}
Mat base = createInitialImage(image, firstOctave < 0, (float)sigma, enable_precise_upscale);
std::vector<Mat> gpyr;
int nOctaves = actualNOctaves > 0 ? actualNOctaves : cvRound(std::log( (double)std::min( base.cols, base.rows ) ) / std::log(2.) - 2) - firstOctave;
//double t, tf = getTickFrequency();
//t = (double)getTickCount();
buildGaussianPyramid(base, gpyr, nOctaves);
//t = (double)getTickCount() - t;
//printf("pyramid construction time: %g\n", t*1000./tf);
if( !useProvidedKeypoints )
{
std::vector<Mat> dogpyr;
buildDoGPyramid(gpyr, dogpyr);
//t = (double)getTickCount();
findScaleSpaceExtrema(gpyr, dogpyr, keypoints);
KeyPointsFilter::removeDuplicatedSorted( keypoints );
if( nfeatures > 0 )
KeyPointsFilter::retainBest(keypoints, nfeatures);
//t = (double)getTickCount() - t;
//printf("keypoint detection time: %g\n", t*1000./tf);
if( firstOctave < 0 )
for( size_t i = 0; i < keypoints.size(); i++ )
{
KeyPoint& kpt = keypoints[i];
float scale = 1.f/(float)(1 << -firstOctave);
kpt.octave = (kpt.octave & ~255) | ((kpt.octave + firstOctave) & 255);
kpt.pt *= scale;
kpt.size *= scale;
}
if( !mask.empty() )
KeyPointsFilter::runByPixelsMask( keypoints, mask );
}
else
{
// filter keypoints by mask
//KeyPointsFilter::runByPixelsMask( keypoints, mask );
}
if( _descriptors.needed() )
{
//t = (double)getTickCount();
int dsize = descriptorSize();
_descriptors.create((int)keypoints.size(), dsize, descriptor_type);
Mat descriptors = _descriptors.getMat();
calcDescriptors(gpyr, keypoints, descriptors, nOctaveLayers, firstOctave);
//t = (double)getTickCount() - t;
//printf("descriptor extraction time: %g\n", t*1000./tf);
}
}
void SIFT_Impl::read( const FileNode& fn)
{
// if node is empty, keep previous value
if (!fn["nfeatures"].empty())
fn["nfeatures"] >> nfeatures;
if (!fn["nOctaveLayers"].empty())
fn["nOctaveLayers"] >> nOctaveLayers;
if (!fn["contrastThreshold"].empty())
fn["contrastThreshold"] >> contrastThreshold;
if (!fn["edgeThreshold"].empty())
fn["edgeThreshold"] >> edgeThreshold;
if (!fn["sigma"].empty())
fn["sigma"] >> sigma;
if (!fn["descriptorType"].empty())
fn["descriptorType"] >> descriptor_type;
}
void SIFT_Impl::write( FileStorage& fs) const
{
if(fs.isOpened())
{
fs << "name" << getDefaultName();
fs << "nfeatures" << nfeatures;
fs << "nOctaveLayers" << nOctaveLayers;
fs << "contrastThreshold" << contrastThreshold;
fs << "edgeThreshold" << edgeThreshold;
fs << "sigma" << sigma;
fs << "descriptorType" << descriptor_type;
}
}
}
File diff suppressed because it is too large Load Diff
+103
View File
@@ -0,0 +1,103 @@
// 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.
//
// Copyright (C) 2017, Intel Corporation, all rights reserved.
// Third party copyrights are property of their respective owners.
#include "test_precomp.hpp"
#include "npy_blob.hpp"
namespace cv
{
static std::string getType(const std::string& header)
{
std::string field = "'descr':";
size_t idx = header.find(field);
CV_Assert(idx != std::string::npos);
size_t from = header.find('\'', idx + field.size()) + 1;
size_t to = header.find('\'', from);
return header.substr(from, to - from);
}
static std::string getFortranOrder(const std::string& header)
{
std::string field = "'fortran_order':";
size_t idx = header.find(field);
CV_Assert(idx != std::string::npos);
size_t from = header.find_last_of(' ', idx + field.size()) + 1;
size_t to = header.find(',', from);
return header.substr(from, to - from);
}
static std::vector<int> getShape(const std::string& header)
{
std::string field = "'shape':";
size_t idx = header.find(field);
CV_Assert(idx != std::string::npos);
size_t from = header.find('(', idx + field.size()) + 1;
size_t to = header.find(')', from);
std::string shapeStr = header.substr(from, to - from);
if (shapeStr.empty())
return std::vector<int>(1, 1);
// Remove all commas.
shapeStr.erase(std::remove(shapeStr.begin(), shapeStr.end(), ','),
shapeStr.end());
std::istringstream ss(shapeStr);
int value;
std::vector<int> shape;
while (ss >> value)
{
shape.push_back(value);
}
return shape;
}
Mat blobFromNPY(const std::string& path)
{
std::ifstream ifs(path.c_str(), std::ios::binary);
CV_Assert(ifs.is_open());
std::string magic(6, '*');
ifs.read(&magic[0], magic.size());
CV_Assert(magic == "\x93NUMPY");
ifs.ignore(1); // Skip major version byte.
ifs.ignore(1); // Skip minor version byte.
unsigned short headerSize;
ifs.read((char*)&headerSize, sizeof(headerSize));
std::string header(headerSize, '*');
ifs.read(&header[0], header.size());
// Extract data type.
int matType;
if (getType(header) == "<f4")
matType = CV_32F;
else if (getType(header) == "<i4")
matType = CV_32S;
else if (getType(header) == "<i8")
matType = CV_64S;
else
CV_Error(Error::BadDepth, "Unsupported numpy type");
CV_Assert(getFortranOrder(header) == "False");
std::vector<int> shape = getShape(header);
Mat blob(shape, matType);
ifs.read((char*)blob.data, blob.total() * blob.elemSize());
CV_Assert((size_t)ifs.gcount() == blob.total() * blob.elemSize());
return blob;
}
} // namespace cv
+20
View File
@@ -0,0 +1,20 @@
// 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.
//
// Copyright (C) 2017, Intel Corporation, all rights reserved.
// Third party copyrights are property of their respective owners.
#ifndef __OPENCV_DNN_TEST_NPY_BLOB_HPP__
#define __OPENCV_DNN_TEST_NPY_BLOB_HPP__
namespace cv
{
// Parse serialized NumPy array by np.save(...)
// Based on specification of .npy data format.
Mat blobFromNPY(const std::string& path);
}
#endif
@@ -0,0 +1,213 @@
/*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) 2010-2012, Institute Of Software Chinese Academy Of Science, all rights reserved.
// Copyright (C) 2010-2012, Advanced Micro Devices, Inc., all rights reserved.
// Copyright (C) 2010-2012, Multicoreware, Inc., all rights reserved.
// Third party copyrights are property of their respective owners.
//
// @Authors
// Niko Li, newlife20080214@gmail.com
// Jia Haipeng, jiahaipeng95@gmail.com
// Zero Lin, Zero.Lin@amd.com
// Zhang Ying, zhangying913@gmail.com
// Yao Wang, bitwangyaoyao@gmail.com
//
// 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 "cvconfig.h"
#include "opencv2/ts/ocl_test.hpp"
#ifdef HAVE_OPENCL
namespace opencv_test {
namespace ocl {
PARAM_TEST_CASE(BruteForceMatcher, int, int)
{
int distType;
int dim;
int queryDescCount;
int countFactor;
Mat query, train;
UMat uquery, utrain;
virtual void SetUp()
{
distType = GET_PARAM(0);
dim = GET_PARAM(1);
queryDescCount = 300; // must be even number because we split train data in some cases in two
countFactor = 4; // do not change it
cv::Mat queryBuf, trainBuf;
// Generate query descriptors randomly.
// Descriptor vector elements are integer values.
queryBuf.create(queryDescCount, dim, CV_32SC1);
rng.fill(queryBuf, cv::RNG::UNIFORM, cv::Scalar::all(0), cv::Scalar::all(3));
queryBuf.convertTo(queryBuf, CV_32FC1);
// Generate train descriptors as follows:
// copy each query descriptor to train set countFactor times
// and perturb some one element of the copied descriptors in
// in ascending order. General boundaries of the perturbation
// are (0.f, 1.f).
trainBuf.create(queryDescCount * countFactor, dim, CV_32FC1);
float step = 1.f / countFactor;
for (int qIdx = 0; qIdx < queryDescCount; qIdx++)
{
cv::Mat queryDescriptor = queryBuf.row(qIdx);
for (int c = 0; c < countFactor; c++)
{
int tIdx = qIdx * countFactor + c;
cv::Mat trainDescriptor = trainBuf.row(tIdx);
queryDescriptor.copyTo(trainDescriptor);
int elem = rng(dim);
float diff = rng.uniform(step * c, step * (c + 1));
trainDescriptor.at<float>(0, elem) += diff;
}
}
queryBuf.convertTo(query, CV_32F);
trainBuf.convertTo(train, CV_32F);
query.copyTo(uquery);
train.copyTo(utrain);
}
};
#ifdef __ANDROID__
OCL_TEST_P(BruteForceMatcher, DISABLED_Match_Single)
#else
OCL_TEST_P(BruteForceMatcher, Match_Single)
#endif
{
BFMatcher matcher(distType);
std::vector<cv::DMatch> matches;
matcher.match(uquery, utrain, matches);
ASSERT_EQ(static_cast<size_t>(queryDescCount), matches.size());
int badCount = 0;
for (size_t i = 0; i < matches.size(); i++)
{
cv::DMatch match = matches[i];
if ((match.queryIdx != (int)i) || (match.trainIdx != (int)i * countFactor) || (match.imgIdx != 0))
badCount++;
}
ASSERT_EQ(0, badCount);
}
#ifdef __ANDROID__
OCL_TEST_P(BruteForceMatcher, DISABLED_KnnMatch_2_Single)
#else
OCL_TEST_P(BruteForceMatcher, KnnMatch_2_Single)
#endif
{
const int knn = 2;
BFMatcher matcher(distType);
std::vector< std::vector<cv::DMatch> > matches;
matcher.knnMatch(uquery, utrain, matches, knn);
ASSERT_EQ(static_cast<size_t>(queryDescCount), matches.size());
int badCount = 0;
for (size_t i = 0; i < matches.size(); i++)
{
if ((int)matches[i].size() != knn)
badCount++;
else
{
int localBadCount = 0;
for (int k = 0; k < knn; k++)
{
cv::DMatch match = matches[i][k];
if ((match.queryIdx != (int)i) || (match.trainIdx != (int)i * countFactor + k) || (match.imgIdx != 0))
localBadCount++;
}
badCount += localBadCount > 0 ? 1 : 0;
}
}
ASSERT_EQ(0, badCount);
}
#ifdef __ANDROID__
OCL_TEST_P(BruteForceMatcher, DISABLED_RadiusMatch_Single)
#else
OCL_TEST_P(BruteForceMatcher, RadiusMatch_Single)
#endif
{
float radius = 1.f / countFactor;
BFMatcher matcher(distType);
std::vector< std::vector<cv::DMatch> > matches;
matcher.radiusMatch(uquery, utrain, matches, radius);
ASSERT_EQ(static_cast<size_t>(queryDescCount), matches.size());
int badCount = 0;
for (size_t i = 0; i < matches.size(); i++)
{
if ((int)matches[i].size() != 1)
{
badCount++;
}
else
{
cv::DMatch match = matches[i][0];
if ((match.queryIdx != (int)i) || (match.trainIdx != (int)i * countFactor) || (match.imgIdx != 0))
badCount++;
}
}
ASSERT_EQ(0, badCount);
}
OCL_INSTANTIATE_TEST_CASE_P(Matcher, BruteForceMatcher, Combine( Values((int)NORM_L1, (int)NORM_L2),
Values(57, 64, 83, 128, 179, 256, 304) ) );
}//ocl
}//cvtest
#endif //HAVE_OPENCL
+149
View File
@@ -0,0 +1,149 @@
///////////////////////////////////////////////////////////////////////////////////////
//
// 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) 2010-2012, Multicoreware, Inc., all rights reserved.
// Copyright (C) 2010-2012, Institute Of Software Chinese Academy Of Science, all rights reserved.
// Copyright (C) 2010-2012, Advanced Micro Devices, 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/ocl_test.hpp"
#ifdef HAVE_OPENCL
namespace opencv_test {
namespace ocl {
//////////////////////////// GoodFeaturesToTrack //////////////////////////
PARAM_TEST_CASE(GoodFeaturesToTrack, double, bool)
{
double minDistance;
bool useRoi;
static const int maxCorners;
static const double qualityLevel;
TEST_DECLARE_INPUT_PARAMETER(src);
UMat points, upoints;
std::vector<float> quality, uquality;
virtual void SetUp()
{
minDistance = GET_PARAM(0);
useRoi = GET_PARAM(1);
}
void generateTestData()
{
Mat frame = readImage("../gpu/opticalflow/rubberwhale1.png", IMREAD_GRAYSCALE);
ASSERT_FALSE(frame.empty()) << "could not load gpu/opticalflow/rubberwhale1.png";
Size roiSize = frame.size();
Border srcBorder = randomBorder(0, useRoi ? 2 : 0);
randomSubMat(src, src_roi, roiSize, srcBorder, frame.type(), 5, 256);
src_roi.copyTo(frame);
UMAT_UPLOAD_INPUT_PARAMETER(src);
}
void UMatToVector(const UMat & um, std::vector<Point2f> & v) const
{
v.resize(um.size().area());
um.copyTo(Mat(um.size(), CV_32FC2, &v[0]));
}
};
const int GoodFeaturesToTrack::maxCorners = 1000;
const double GoodFeaturesToTrack::qualityLevel = 0.01;
OCL_TEST_P(GoodFeaturesToTrack, Accuracy)
{
for (int j = 0; j < test_loop_times; ++j)
{
generateTestData();
std::vector<Point2f> upts, pts;
OCL_OFF(cv::goodFeaturesToTrack(src_roi, points, maxCorners, qualityLevel, minDistance, noArray(), quality));
ASSERT_FALSE(points.empty());
UMatToVector(points, pts);
OCL_ON(cv::goodFeaturesToTrack(usrc_roi, upoints, maxCorners, qualityLevel, minDistance, noArray(), uquality));
ASSERT_FALSE(upoints.empty());
UMatToVector(upoints, upts);
ASSERT_EQ(pts.size(), quality.size());
ASSERT_EQ(upts.size(), uquality.size());
ASSERT_EQ(upts.size(), pts.size());
int mistmatch = 0;
for (size_t i = 0; i < pts.size(); ++i)
{
Point2i a = upts[i], b = pts[i];
bool eq = std::abs(a.x - b.x) < 1 && std::abs(a.y - b.y) < 1 &&
std::abs(quality[i] - uquality[i]) <= 3.f * FLT_EPSILON * std::max(quality[i], uquality[i]);
if (!eq)
++mistmatch;
}
double bad_ratio = static_cast<double>(mistmatch) / pts.size();
ASSERT_GE(1e-2, bad_ratio);
}
}
OCL_TEST_P(GoodFeaturesToTrack, EmptyCorners)
{
generateTestData();
usrc_roi.setTo(Scalar::all(0));
OCL_ON(cv::goodFeaturesToTrack(usrc_roi, upoints, maxCorners, qualityLevel, minDistance, noArray(), uquality));
ASSERT_TRUE(upoints.empty());
ASSERT_TRUE(uquality.empty());
}
OCL_INSTANTIATE_TEST_CASE_P(Imgproc, GoodFeaturesToTrack,
::testing::Combine(testing::Values(0.0, 3.0), Bool()));
} } // namespace opencv_test::ocl
#endif
@@ -0,0 +1,207 @@
// 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"
// #define GENERATE_DATA // generate data in debug mode
namespace opencv_test { namespace {
#ifndef GENERATE_DATA
static bool isSimilarKeypoints( const KeyPoint& p1, const KeyPoint& p2 )
{
const float maxPtDif = 1.f;
const float maxSizeDif = 1.f;
const float maxAngleDif = 2.f;
const float maxResponseDif = 0.1f;
float dist = (float)cv::norm( p1.pt - p2.pt );
return (dist < maxPtDif &&
fabs(p1.size - p2.size) < maxSizeDif &&
abs(p1.angle - p2.angle) < maxAngleDif &&
abs(p1.response - p2.response) < maxResponseDif &&
(p1.octave & 0xffff) == (p2.octave & 0xffff) // do not care about sublayers and class_id
);
}
#endif
TEST(Features2d_AFFINE_FEATURE, regression)
{
Mat image = imread(cvtest::findDataFile("features2d/tsukuba.png"));
string xml = cvtest::TS::ptr()->get_data_path() + "asift/regression_cpp.xml.gz";
ASSERT_FALSE(image.empty());
Mat gray;
cvtColor(image, gray, COLOR_BGR2GRAY);
// Default ASIFT generates too large descriptors. This test uses small maxTilt to suppress the size of testdata.
Ptr<AffineFeature> ext = AffineFeature::create(SIFT::create(), 2, 0, 1.4142135623730951f, 144.0f);
Mat mpt, msize, mangle, mresponse, moctave, mclass_id;
#ifdef GENERATE_DATA
// calculate
vector<KeyPoint> calcKeypoints;
Mat calcDescriptors;
ext->detectAndCompute(gray, Mat(), calcKeypoints, calcDescriptors, false);
// create keypoints XML
FileStorage fs(xml, FileStorage::WRITE);
ASSERT_TRUE(fs.isOpened()) << xml;
std::cout << "Creating keypoints XML..." << std::endl;
mpt = Mat(calcKeypoints.size(), 2, CV_32F);
msize = Mat(calcKeypoints.size(), 1, CV_32F);
mangle = Mat(calcKeypoints.size(), 1, CV_32F);
mresponse = Mat(calcKeypoints.size(), 1, CV_32F);
moctave = Mat(calcKeypoints.size(), 1, CV_32S);
mclass_id = Mat(calcKeypoints.size(), 1, CV_32S);
for( size_t i = 0; i < calcKeypoints.size(); i++ )
{
const KeyPoint& key = calcKeypoints[i];
mpt.at<float>(i, 0) = key.pt.x;
mpt.at<float>(i, 1) = key.pt.y;
msize.at<float>(i, 0) = key.size;
mangle.at<float>(i, 0) = key.angle;
mresponse.at<float>(i, 0) = key.response;
moctave.at<int>(i, 0) = key.octave;
mclass_id.at<int>(i, 0) = key.class_id;
}
fs << "keypoints_pt" << mpt;
fs << "keypoints_size" << msize;
fs << "keypoints_angle" << mangle;
fs << "keypoints_response" << mresponse;
fs << "keypoints_octave" << moctave;
fs << "keypoints_class_id" << mclass_id;
// create descriptor XML
fs << "descriptors" << calcDescriptors;
fs.release();
#else
const float badCountsRatio = 0.01f;
const float badDescriptorDist = 1.0f;
const float maxBadKeypointsRatio = 0.15f;
const float maxBadDescriptorRatio = 0.15f;
// read keypoints
vector<KeyPoint> validKeypoints;
Mat validDescriptors;
FileStorage fs(xml, FileStorage::READ);
ASSERT_TRUE(fs.isOpened()) << xml;
fs["keypoints_pt"] >> mpt;
ASSERT_EQ(mpt.type(), CV_32F);
fs["keypoints_size"] >> msize;
ASSERT_EQ(msize.type(), CV_32F);
fs["keypoints_angle"] >> mangle;
ASSERT_EQ(mangle.type(), CV_32F);
fs["keypoints_response"] >> mresponse;
ASSERT_EQ(mresponse.type(), CV_32F);
fs["keypoints_octave"] >> moctave;
ASSERT_EQ(moctave.type(), CV_32S);
fs["keypoints_class_id"] >> mclass_id;
ASSERT_EQ(mclass_id.type(), CV_32S);
validKeypoints.resize(mpt.rows);
for( int i = 0; i < (int)validKeypoints.size(); i++ )
{
validKeypoints[i].pt.x = mpt.at<float>(i, 0);
validKeypoints[i].pt.y = mpt.at<float>(i, 1);
validKeypoints[i].size = msize.at<float>(i, 0);
validKeypoints[i].angle = mangle.at<float>(i, 0);
validKeypoints[i].response = mresponse.at<float>(i, 0);
validKeypoints[i].octave = moctave.at<int>(i, 0);
validKeypoints[i].class_id = mclass_id.at<int>(i, 0);
}
// read descriptors
fs["descriptors"] >> validDescriptors;
fs.release();
// calc and compare keypoints
vector<KeyPoint> calcKeypoints;
ext->detectAndCompute(gray, Mat(), calcKeypoints, noArray(), false);
float countRatio = (float)validKeypoints.size() / (float)calcKeypoints.size();
ASSERT_LT(countRatio, 1 + badCountsRatio) << "Bad keypoints count ratio.";
ASSERT_GT(countRatio, 1 - badCountsRatio) << "Bad keypoints count ratio.";
int badPointCount = 0, commonPointCount = max((int)validKeypoints.size(), (int)calcKeypoints.size());
for( size_t v = 0; v < validKeypoints.size(); v++ )
{
int nearestIdx = -1;
float minDist = std::numeric_limits<float>::max();
float angleDistOfNearest = std::numeric_limits<float>::max();
for( size_t c = 0; c < calcKeypoints.size(); c++ )
{
if( validKeypoints[v].class_id != calcKeypoints[c].class_id )
continue;
float curDist = (float)cv::norm( calcKeypoints[c].pt - validKeypoints[v].pt );
if( curDist < minDist )
{
minDist = curDist;
nearestIdx = (int)c;
angleDistOfNearest = abs( calcKeypoints[c].angle - validKeypoints[v].angle );
}
else if( curDist == minDist ) // the keypoints whose positions are same but angles are different
{
float angleDist = abs( calcKeypoints[c].angle - validKeypoints[v].angle );
if( angleDist < angleDistOfNearest )
{
nearestIdx = (int)c;
angleDistOfNearest = angleDist;
}
}
}
if( nearestIdx == -1 || !isSimilarKeypoints( validKeypoints[v], calcKeypoints[nearestIdx] ) )
badPointCount++;
}
float badKeypointsRatio = (float)badPointCount / (float)commonPointCount;
std::cout << "badKeypointsRatio: " << badKeypointsRatio << std::endl;
ASSERT_LT( badKeypointsRatio , maxBadKeypointsRatio ) << "Bad accuracy!";
// Calc and compare descriptors. This uses validKeypoints for extraction.
Mat calcDescriptors;
ext->detectAndCompute(gray, Mat(), validKeypoints, calcDescriptors, true);
int dim = validDescriptors.cols;
int badDescriptorCount = 0;
L1<float> distance;
for( int i = 0; i < (int)validKeypoints.size(); i++ )
{
float dist = distance( validDescriptors.ptr<float>(i), calcDescriptors.ptr<float>(i), dim );
if( dist > badDescriptorDist )
badDescriptorCount++;
}
float badDescriptorRatio = (float)badDescriptorCount / (float)validKeypoints.size();
std::cout << "badDescriptorRatio: " << badDescriptorRatio << std::endl;
ASSERT_LT( badDescriptorRatio, maxBadDescriptorRatio ) << "Too many descriptors mismatched.";
#endif
}
TEST(Features2d_AFFINE_FEATURE, mask)
{
Mat gray = imread(cvtest::findDataFile("features2d/tsukuba.png"), IMREAD_GRAYSCALE);
ASSERT_FALSE(gray.empty()) << "features2d/tsukuba.png image was not found in test data!";
// small tilt range to limit internal mask warping
Ptr<AffineFeature> ext = AffineFeature::create(SIFT::create(), 1, 0);
Mat mask = Mat::zeros(gray.size(), CV_8UC1);
mask(Rect(50, 50, mask.cols-100, mask.rows-100)).setTo(255);
// calc and compare keypoints
vector<KeyPoint> calcKeypoints;
ext->detectAndCompute(gray, mask, calcKeypoints, noArray(), false);
// added expanded test range to cover sub-pixel coordinates for features on mask border
for( size_t i = 0; i < calcKeypoints.size(); i++ )
{
ASSERT_TRUE((calcKeypoints[i].pt.x >= 50-1) && (calcKeypoints[i].pt.x <= mask.cols-50+1));
ASSERT_TRUE((calcKeypoints[i].pt.y >= 50-1) && (calcKeypoints[i].pt.y <= mask.rows-50+1));
}
}
}} // namespace
@@ -0,0 +1,171 @@
// 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 "npy_blob.hpp"
#ifdef HAVE_OPENCV_DNN
#include "opencv2/dnn.hpp"
#include "opencv2/core/utils/configuration.private.hpp"
namespace opencv_test { namespace {
static void skipIfClassicDnnEngine()
{
const auto engine = static_cast<cv::dnn::EngineType>(
cv::utils::getConfigurationParameterSizeT("OPENCV_FORCE_DNN_ENGINE", cv::dnn::ENGINE_AUTO));
if (engine == cv::dnn::ENGINE_CLASSIC)
throw SkipTestException("ALIKED/LightGlue reference outputs are generated with the new DNN engine");
}
TEST(Features2d_ALIKED, Regression)
{
skipIfClassicDnnEngine();
const std::string modelPath = cvtest::findDataFile("dnn/onnx/models/aliked-n16rot-top1k-640.onnx", false);
Ptr<ALIKED> aliked = ALIKED::create(modelPath);
ASSERT_FALSE(aliked.empty());
ASSERT_FALSE(aliked->empty());
EXPECT_EQ(aliked->descriptorSize(), 128);
EXPECT_EQ(aliked->descriptorType(), CV_32F);
EXPECT_EQ(aliked->defaultNorm(), NORM_L2);
const std::string imgPath = cvtest::findDataFile("shared/box.png");
Mat img = imread(imgPath);
ASSERT_FALSE(img.empty()) << "Could not load test image: " << imgPath;
std::vector<KeyPoint> keypoints;
Mat descriptors;
aliked->detectAndCompute(img, noArray(), keypoints, descriptors);
EXPECT_GT(keypoints.size(), 100u);
ASSERT_EQ(descriptors.rows, static_cast<int>(keypoints.size()));
EXPECT_EQ(descriptors.cols, 128);
EXPECT_EQ(descriptors.type(), CV_32F);
for (const KeyPoint& kp : keypoints)
{
EXPECT_GE(kp.pt.x, 0.f);
EXPECT_GE(kp.pt.y, 0.f);
EXPECT_LT(kp.pt.x, static_cast<float>(img.cols));
EXPECT_LT(kp.pt.y, static_cast<float>(img.rows));
EXPECT_GT(kp.response, 0.f);
}
// Load ORT reference outputs (generated with same OpenCV preprocessing)
Mat refKpts = blobFromNPY(cvtest::findDataFile("dnn/aliked_keypoints_box.npy"));
Mat refDescs = blobFromNPY(cvtest::findDataFile("dnn/aliked_descriptors_box.npy"));
// Keypoint count must match exactly
ASSERT_EQ(static_cast<int>(keypoints.size()), refKpts.rows)
<< "Keypoint count mismatch: got " << keypoints.size()
<< ", expected " << refKpts.rows;
// Compare each keypoint (normalized coords -> pixel coords)
const float origW = static_cast<float>(img.cols);
const float origH = static_cast<float>(img.rows);
for (int i = 0; i < refKpts.rows; i++)
{
float refX = (refKpts.at<float>(i, 0) + 1.0f) * 0.5f * origW;
float refY = (refKpts.at<float>(i, 1) + 1.0f) * 0.5f * origH;
EXPECT_NEAR(keypoints[i].pt.x, refX, 1e-4) << "Keypoint " << i << " x mismatch";
EXPECT_NEAR(keypoints[i].pt.y, refY, 1e-4) << "Keypoint " << i << " y mismatch";
}
// Compare descriptors row by row
for (int i = 0; i < refDescs.rows; i++)
{
Mat diff = descriptors.row(i) - refDescs.row(i);
double maxDiff = cv::norm(diff, cv::NORM_INF);
EXPECT_LT(maxDiff, 1e-5) << "Descriptor " << i << " mismatch (max diff=" << maxDiff << ")";
}
}
TEST(Features2d_LightGlue, Regression)
{
skipIfClassicDnnEngine();
const std::string alikedPath = cvtest::findDataFile("dnn/onnx/models/aliked-n16rot-top1k-640.onnx", false);
const std::string lgPath = cvtest::findDataFile("dnn/onnx/models/aliked_lightglue.onnx", false);
Ptr<ALIKED> aliked = ALIKED::create(alikedPath);
Ptr<LightGlueMatcher> lg = LightGlueMatcher::create(lgPath);
ASSERT_FALSE(aliked.empty());
ASSERT_FALSE(lg.empty());
Mat img1 = imread(cvtest::findDataFile("shared/box.png"));
Mat img2 = imread(cvtest::findDataFile("shared/box_in_scene.png"));
ASSERT_FALSE(img1.empty());
ASSERT_FALSE(img2.empty());
// Detect features on both images
std::vector<KeyPoint> kpts1, kpts2;
Mat descs1, descs2;
aliked->detectAndCompute(img1, noArray(), kpts1, descs1);
aliked->detectAndCompute(img2, noArray(), kpts2, descs2);
ASSERT_GT(static_cast<int>(kpts1.size()), 0);
ASSERT_GT(static_cast<int>(kpts2.size()), 0);
// Build keypoint matrices (pixel coordinates)
Mat kpts1Mat(static_cast<int>(kpts1.size()), 2, CV_32F);
Mat kpts2Mat(static_cast<int>(kpts2.size()), 2, CV_32F);
for (size_t i = 0; i < kpts1.size(); i++)
{
kpts1Mat.at<float>(static_cast<int>(i), 0) = kpts1[i].pt.x;
kpts1Mat.at<float>(static_cast<int>(i), 1) = kpts1[i].pt.y;
}
for (size_t i = 0; i < kpts2.size(); i++)
{
kpts2Mat.at<float>(static_cast<int>(i), 0) = kpts2[i].pt.x;
kpts2Mat.at<float>(static_cast<int>(i), 1) = kpts2[i].pt.y;
}
lg->setPairInfo(kpts1Mat, kpts2Mat, img1.size(), img2.size());
std::vector<DMatch> matches;
lg->match(descs1, descs2, matches);
ASSERT_GT(static_cast<int>(matches.size()), 0);
for (const auto& m : matches)
{
EXPECT_GE(m.queryIdx, 0);
EXPECT_LT(m.queryIdx, static_cast<int>(kpts1.size()));
EXPECT_GE(m.trainIdx, 0);
EXPECT_LT(m.trainIdx, static_cast<int>(kpts2.size()));
}
// Load ORT reference outputs
Mat refMatches = blobFromNPY(cvtest::findDataFile("dnn/lightglue_matches.npy"));
// Match count must match exactly (same OpenCV preprocessing in both)
ASSERT_EQ(static_cast<int>(matches.size()), refMatches.rows)
<< "Match count mismatch: got " << matches.size()
<< ", expected " << refMatches.rows;
// Compare each match (index pairs should be identical)
for (int i = 0; i < refMatches.rows; i++)
{
int refQIdx = static_cast<int>(refMatches.at<int64_t>(i, 0));
int refTIdx = static_cast<int>(refMatches.at<int64_t>(i, 1));
EXPECT_EQ(matches[i].queryIdx, refQIdx) << "Match " << i << " queryIdx mismatch";
EXPECT_EQ(matches[i].trainIdx, refTIdx) << "Match " << i << " trainIdx mismatch";
}
}
#else // !HAVE_OPENCV_DNN
TEST(Features2d_ALIKED, not_available)
{
EXPECT_THROW(ALIKED::create("dummy.onnx"), cv::Exception);
}
TEST(Features2d_LightGlueMatcher, not_available)
{
EXPECT_THROW(LightGlueMatcher::create("dummy.onnx"), cv::Exception);
}
#endif // HAVE_OPENCV_DNN
}} // namespace opencv_test
@@ -0,0 +1,46 @@
// 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"
namespace opencv_test { namespace {
TEST(Features2d_BlobDetector, bug_6667)
{
cv::Mat image = cv::Mat(cv::Size(100, 100), CV_8UC1, cv::Scalar(255, 255, 255));
cv::circle(image, Point(50, 50), 20, cv::Scalar(0), -1);
SimpleBlobDetector::Params params;
params.minThreshold = 250;
params.maxThreshold = 260;
params.minRepeatability = 1; // https://github.com/opencv/opencv/issues/6667
std::vector<KeyPoint> keypoints;
Ptr<SimpleBlobDetector> detector = SimpleBlobDetector::create(params);
detector->detect(image, keypoints);
ASSERT_NE((int) keypoints.size(), 0);
}
TEST(Features2d_BlobDetector, withContours)
{
cv::Mat image = cv::Mat(cv::Size(100, 100), CV_8UC1, cv::Scalar(255, 255, 255));
cv::circle(image, Point(50, 50), 20, cv::Scalar(0), -1);
SimpleBlobDetector::Params params;
params.minThreshold = 250;
params.maxThreshold = 260;
params.minRepeatability = 1; // https://github.com/opencv/opencv/issues/6667
params.collectContours = true;
std::vector<KeyPoint> keypoints;
Ptr<SimpleBlobDetector> detector = SimpleBlobDetector::create(params);
detector->detect(image, keypoints);
ASSERT_NE((int)keypoints.size(), 0);
ASSERT_GT((int)detector->getBlobContours().size(), 0);
std::vector<Point> contour = detector->getBlobContours()[0];
ASSERT_TRUE(std::any_of(contour.begin(), contour.end(),
[](Point p)
{
return abs(p.x - 30) < 2 && abs(p.y - 50) < 2;
}));
}
}} // namespace
@@ -0,0 +1,34 @@
// 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 "test_invariance_utils.hpp"
#include "test_descriptors_invariance.impl.hpp"
namespace opencv_test { namespace {
const static std::string IMAGE_TSUKUBA = "features2d/tsukuba.png";
const static std::string IMAGE_BIKES = "detectors_descriptors_evaluation/images_datasets/bikes/img1.png";
#define Value(...) Values(make_tuple(__VA_ARGS__))
/*
* Descriptors's rotation invariance check
*/
INSTANTIATE_TEST_CASE_P(SIFT, DescriptorRotationInvariance,
Value(IMAGE_TSUKUBA, []() { return SIFT::create(); }, []() { return SIFT::create(); }, 0.98f));
INSTANTIATE_TEST_CASE_P(ORB, DescriptorRotationInvariance,
Value(IMAGE_TSUKUBA, []() { return ORB::create(); }, []() { return ORB::create(); }, 0.99f));
/*
* Descriptor's scale invariance check
*/
INSTANTIATE_TEST_CASE_P(SIFT, DescriptorScaleInvariance,
Value(IMAGE_BIKES, []() { return SIFT::create(0, 3, 0.09); }, []() { return SIFT::create(0, 3, 0.09); }, 0.78f));
}} // namespace
@@ -0,0 +1,203 @@
// 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_invariance_utils.hpp"
#include <functional>
namespace opencv_test { namespace {
#define SHOW_DEBUG_LOG 1
// NOTE: using factory function (function<Ptr<Type>()>) instead of object instance (Ptr<Type>) as a
// test parameter, because parameters exist during whole test program run and consume a lot of memory
typedef std::function<cv::Ptr<cv::FeatureDetector>()> DetectorFactory;
typedef std::function<cv::Ptr<cv::DescriptorExtractor>()> ExtractorFactory;
typedef tuple<std::string, DetectorFactory, ExtractorFactory, float>
String_FeatureDetector_DescriptorExtractor_Float_t;
static
void SetSuitableSIFTOctave(vector<KeyPoint>& keypoints,
int firstOctave = -1, int nOctaveLayers = 3, double sigma = 1.6)
{
for (size_t i = 0; i < keypoints.size(); i++ )
{
int octv, layer;
KeyPoint& kpt = keypoints[i];
double octv_layer = std::log(kpt.size / sigma) / std::log(2.) - 1;
octv = cvFloor(octv_layer);
layer = cvRound( (octv_layer - octv) * nOctaveLayers );
if (octv < firstOctave)
{
octv = firstOctave;
layer = 0;
}
kpt.octave = (layer << 8) | (octv & 255);
}
}
static
void rotateKeyPoints(const vector<KeyPoint>& src, const Mat& H, float angle, vector<KeyPoint>& dst)
{
// suppose that H is rotation given from rotateImage() and angle has value passed to rotateImage()
vector<Point2f> srcCenters, dstCenters;
KeyPoint::convert(src, srcCenters);
perspectiveTransform(srcCenters, dstCenters, H);
dst = src;
for(size_t i = 0; i < dst.size(); i++)
{
dst[i].pt = dstCenters[i];
float dstAngle = src[i].angle + angle;
if(dstAngle >= 360.f)
dstAngle -= 360.f;
dst[i].angle = dstAngle;
}
}
class DescriptorInvariance : public TestWithParam<String_FeatureDetector_DescriptorExtractor_Float_t>
{
protected:
virtual void SetUp() {
// Read test data
const std::string filename = cvtest::TS::ptr()->get_data_path() + get<0>(GetParam());
image0 = imread(filename);
ASSERT_FALSE(image0.empty()) << "couldn't read input image";
featureDetector = get<1>(GetParam())();
descriptorExtractor = get<2>(GetParam())();
minInliersRatio = get<3>(GetParam());
}
Ptr<FeatureDetector> featureDetector;
Ptr<DescriptorExtractor> descriptorExtractor;
float minInliersRatio;
Mat image0;
};
typedef DescriptorInvariance DescriptorScaleInvariance;
typedef DescriptorInvariance DescriptorRotationInvariance;
TEST_P(DescriptorRotationInvariance, rotation)
{
Mat image1, mask1;
const int borderSize = 16;
Mat mask0(image0.size(), CV_8UC1, Scalar(0));
mask0(Rect(borderSize, borderSize, mask0.cols - 2*borderSize, mask0.rows - 2*borderSize)).setTo(Scalar(255));
vector<KeyPoint> keypoints0;
Mat descriptors0;
featureDetector->detect(image0, keypoints0, mask0);
std::cout << "Keypoints: " << keypoints0.size() << std::endl;
EXPECT_GE(keypoints0.size(), 15u);
descriptorExtractor->compute(image0, keypoints0, descriptors0);
BFMatcher bfmatcher(descriptorExtractor->defaultNorm());
const float minIntersectRatio = 0.5f;
const int maxAngle = 360, angleStep = 15;
for(int angle = 0; angle < maxAngle; angle += angleStep)
{
Mat H = rotateImage(image0, mask0, static_cast<float>(angle), image1, mask1);
vector<KeyPoint> keypoints1;
rotateKeyPoints(keypoints0, H, static_cast<float>(angle), keypoints1);
Mat descriptors1;
descriptorExtractor->compute(image1, keypoints1, descriptors1);
vector<DMatch> descMatches;
bfmatcher.match(descriptors0, descriptors1, descMatches);
int descInliersCount = 0;
for(size_t m = 0; m < descMatches.size(); m++)
{
const KeyPoint& transformed_p0 = keypoints1[descMatches[m].queryIdx];
const KeyPoint& p1 = keypoints1[descMatches[m].trainIdx];
if(calcIntersectRatio(transformed_p0.pt, 0.5f * transformed_p0.size,
p1.pt, 0.5f * p1.size) >= minIntersectRatio)
{
descInliersCount++;
}
}
float descInliersRatio = static_cast<float>(descInliersCount) / keypoints0.size();
EXPECT_GE(descInliersRatio, minInliersRatio);
#if SHOW_DEBUG_LOG
std::cout
<< "angle = " << angle
<< ", inliers = " << descInliersCount
<< ", descInliersRatio = " << static_cast<float>(descInliersCount) / keypoints0.size()
<< std::endl;
#endif
}
}
TEST_P(DescriptorScaleInvariance, scale)
{
vector<KeyPoint> keypoints0;
featureDetector->detect(image0, keypoints0);
std::cout << "Keypoints: " << keypoints0.size() << std::endl;
EXPECT_GE(keypoints0.size(), 15u);
Mat descriptors0;
descriptorExtractor->compute(image0, keypoints0, descriptors0);
BFMatcher bfmatcher(descriptorExtractor->defaultNorm());
for(int scaleIdx = 1; scaleIdx <= 3; scaleIdx++)
{
float scale = 1.f + scaleIdx * 0.5f;
Mat image1;
resize(image0, image1, Size(), 1./scale, 1./scale, INTER_LINEAR_EXACT);
vector<KeyPoint> keypoints1;
scaleKeyPoints(keypoints0, keypoints1, 1.0f/scale);
if (featureDetector->getDefaultName() == "Feature2D.SIFT")
{
SetSuitableSIFTOctave(keypoints1);
}
Mat descriptors1;
descriptorExtractor->compute(image1, keypoints1, descriptors1);
vector<DMatch> descMatches;
bfmatcher.match(descriptors0, descriptors1, descMatches);
const float minIntersectRatio = 0.5f;
int descInliersCount = 0;
for(size_t m = 0; m < descMatches.size(); m++)
{
const KeyPoint& transformed_p0 = keypoints0[descMatches[m].queryIdx];
const KeyPoint& p1 = keypoints0[descMatches[m].trainIdx];
if(calcIntersectRatio(transformed_p0.pt, 0.5f * transformed_p0.size,
p1.pt, 0.5f * p1.size) >= minIntersectRatio)
{
descInliersCount++;
}
}
float descInliersRatio = static_cast<float>(descInliersCount) / keypoints0.size();
EXPECT_GE(descInliersRatio, minInliersRatio);
#if SHOW_DEBUG_LOG
std::cout
<< "scale = " << scale
<< ", inliers = " << descInliersCount
<< ", descInliersRatio = " << static_cast<float>(descInliersCount) / keypoints0.size()
<< std::endl;
#endif
}
}
#undef SHOW_DEBUG_LOG
}} // namespace
namespace std {
using namespace opencv_test;
static inline void PrintTo(const String_FeatureDetector_DescriptorExtractor_Float_t& v, std::ostream* os)
{
*os << "(\"" << get<0>(v)
<< "\", " << get<3>(v)
<< ")";
}
} // namespace
@@ -0,0 +1,162 @@
// 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"
namespace opencv_test { namespace {
const string FEATURES2D_DIR = "features2d";
const string IMAGE_FILENAME = "tsukuba.png";
const string DESCRIPTOR_DIR = FEATURES2D_DIR + "/descriptor_extractors";
}} // namespace
#include "test_descriptors_regression.impl.hpp"
namespace opencv_test { namespace {
/****************************************************************************************\
* Tests registrations *
\****************************************************************************************/
TEST( Features2d_DescriptorExtractor_SIFT, regression )
{
CV_DescriptorExtractorTest<L1<float> > test( "descriptor-sift", 1.0f,
SIFT::create() );
test.safe_run();
}
TEST( Features2d_DescriptorExtractor_ORB, regression )
{
// TODO adjust the parameters below
CV_DescriptorExtractorTest<Hamming> test( "descriptor-orb",
#if CV_NEON
(CV_DescriptorExtractorTest<Hamming>::DistanceType)25.f,
#else
(CV_DescriptorExtractorTest<Hamming>::DistanceType)12.f,
#endif
ORB::create() );
test.safe_run();
}
TEST( Features2d_DescriptorExtractor, batch_ORB )
{
string path = string(cvtest::TS::ptr()->get_data_path() + "detectors_descriptors_evaluation/images_datasets/graf");
vector<Mat> imgs, descriptors;
vector<vector<KeyPoint> > keypoints;
int i, n = 6;
Ptr<ORB> orb = ORB::create();
for( i = 0; i < n; i++ )
{
string imgname = format("%s/img%d.png", path.c_str(), i+1);
Mat img = imread(imgname, IMREAD_GRAYSCALE);
imgs.push_back(img);
}
orb->detect(imgs, keypoints);
orb->compute(imgs, keypoints, descriptors);
ASSERT_EQ((int)keypoints.size(), n);
ASSERT_EQ((int)descriptors.size(), n);
for( i = 0; i < n; i++ )
{
EXPECT_GT((int)keypoints[i].size(), 100);
EXPECT_GT(descriptors[i].rows, 100);
}
}
TEST( Features2d_DescriptorExtractor, batch_SIFT )
{
string path = string(cvtest::TS::ptr()->get_data_path() + "detectors_descriptors_evaluation/images_datasets/graf");
vector<Mat> imgs, descriptors;
vector<vector<KeyPoint> > keypoints;
int i, n = 6;
Ptr<SIFT> sift = SIFT::create();
for( i = 0; i < n; i++ )
{
string imgname = format("%s/img%d.png", path.c_str(), i+1);
Mat img = imread(imgname, IMREAD_GRAYSCALE);
imgs.push_back(img);
}
sift->detect(imgs, keypoints);
sift->compute(imgs, keypoints, descriptors);
ASSERT_EQ((int)keypoints.size(), n);
ASSERT_EQ((int)descriptors.size(), n);
for( i = 0; i < n; i++ )
{
EXPECT_GT((int)keypoints[i].size(), 100);
EXPECT_GT(descriptors[i].rows, 100);
}
}
class DescriptorImage : public TestWithParam<std::string>
{
protected:
virtual void SetUp() {
pattern = GetParam();
}
std::string pattern;
};
TEST_P(DescriptorImage, no_crash)
{
vector<String> fnames;
glob(cvtest::TS::ptr()->get_data_path() + pattern, fnames, false);
std::sort(fnames.begin(), fnames.end());
Ptr<ORB> orb = ORB::create();
size_t n = fnames.size();
vector<KeyPoint> keypoints;
Mat descriptors;
orb->setMaxFeatures(5000);
for(size_t i = 0; i < n; i++ )
{
printf("%d. image: %s:\n", (int)i, fnames[i].c_str());
if( strstr(fnames[i].c_str(), "MP.png") != 0 )
{
printf("\tskip\n");
continue;
}
bool checkCount = strstr(fnames[i].c_str(), "templ.png") == 0;
Mat img = imread(fnames[i], -1);
printf("\t%dx%d\n", img.cols, img.rows);
#define TEST_DETECTOR(name, descriptor) \
keypoints.clear(); descriptors.release(); \
printf("\t" name "\n"); fflush(stdout); \
descriptor->detectAndCompute(img, noArray(), keypoints, descriptors); \
printf("\t\t\t(%d keypoints, descriptor size = %d)\n", (int)keypoints.size(), descriptors.cols); fflush(stdout); \
if (checkCount) \
{ \
EXPECT_GT((int)keypoints.size(), 0); \
} \
ASSERT_EQ(descriptors.rows, (int)keypoints.size());
TEST_DETECTOR("ORB", orb);
}
}
INSTANTIATE_TEST_CASE_P(Features2d, DescriptorImage,
testing::Values(
"shared/lena.png",
"shared/box*.png",
"shared/fruits*.png",
"shared/airplane.png",
"shared/graffiti.png",
"shared/1_itseez-0001*.png",
"shared/pic*.png",
"shared/templ.png"
)
);
}} // namespace
@@ -0,0 +1,345 @@
// 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
namespace opencv_test { namespace {
/****************************************************************************************\
* Regression tests for descriptor extractors. *
\****************************************************************************************/
static void double_image(Mat& src, Mat& dst) {
dst.create(Size(src.cols*2, src.rows*2), src.type());
Mat H = Mat::zeros(2, 3, CV_32F);
H.at<float>(0, 0) = 0.5f;
H.at<float>(1, 1) = 0.5f;
cv::warpAffine(src, dst, H, dst.size(), INTER_LINEAR | WARP_INVERSE_MAP, BORDER_REFLECT);
}
static Mat prepare_img(bool rows_indexed) {
int rows = 5;
int columns = 5;
Mat img(rows, columns, CV_32F);
for (int i = 0; i < rows; i++) {
for (int j = 0; j < columns; j++) {
if (rows_indexed) {
img.at<float>(i, j) = (float)i;
} else {
img.at<float>(i, j) = (float)j;
}
}
}
return img;
}
static void writeMatInBin( const Mat& mat, const string& filename )
{
FILE* f = fopen( filename.c_str(), "wb");
if( f )
{
CV_Assert(4 == sizeof(int));
int type = mat.type();
fwrite( (void*)&mat.rows, sizeof(int), 1, f );
fwrite( (void*)&mat.cols, sizeof(int), 1, f );
fwrite( (void*)&type, sizeof(int), 1, f );
int dataSize = (int)(mat.step * mat.rows);
fwrite( (void*)&dataSize, sizeof(int), 1, f );
fwrite( (void*)mat.ptr(), 1, dataSize, f );
fclose(f);
}
}
static Mat readMatFromBin( const string& filename )
{
FILE* f = fopen( filename.c_str(), "rb" );
if( f )
{
CV_Assert(4 == sizeof(int));
int rows, cols, type, dataSize;
size_t elements_read1 = fread( (void*)&rows, sizeof(int), 1, f );
size_t elements_read2 = fread( (void*)&cols, sizeof(int), 1, f );
size_t elements_read3 = fread( (void*)&type, sizeof(int), 1, f );
size_t elements_read4 = fread( (void*)&dataSize, sizeof(int), 1, f );
CV_Assert(elements_read1 == 1 && elements_read2 == 1 && elements_read3 == 1 && elements_read4 == 1);
int step = dataSize / rows / CV_ELEM_SIZE(type);
CV_Assert(step >= cols);
Mat returnMat = Mat(rows, step, type).colRange(0, cols);
size_t elements_read = fread( returnMat.ptr(), 1, dataSize, f );
CV_Assert(elements_read == (size_t)(dataSize));
fclose(f);
return returnMat;
}
return Mat();
}
template<class Distance>
class CV_DescriptorExtractorTest : public cvtest::BaseTest
{
public:
typedef typename Distance::ValueType ValueType;
typedef typename Distance::ResultType DistanceType;
CV_DescriptorExtractorTest( const string _name, DistanceType _maxDist, const Ptr<DescriptorExtractor>& _dextractor,
Distance d = Distance(), Ptr<FeatureDetector> _detector = Ptr<FeatureDetector>()):
name(_name), maxDist(_maxDist), dextractor(_dextractor), distance(d) , detector(_detector) {}
~CV_DescriptorExtractorTest()
{
}
protected:
virtual void createDescriptorExtractor() {}
void compareDescriptors( const Mat& validDescriptors, const Mat& calcDescriptors )
{
if( validDescriptors.size != calcDescriptors.size || validDescriptors.type() != calcDescriptors.type() )
{
ts->printf(cvtest::TS::LOG, "Valid and computed descriptors matrices must have the same size and type.\n");
ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA );
return;
}
CV_Assert( DataType<ValueType>::type == validDescriptors.type() );
int dimension = validDescriptors.cols;
DistanceType curMaxDist = 0;
size_t exact_count = 0, failed_count = 0;
for( int y = 0; y < validDescriptors.rows; y++ )
{
DistanceType dist = distance( validDescriptors.ptr<ValueType>(y), calcDescriptors.ptr<ValueType>(y), dimension );
if (dist == 0)
exact_count++;
if( dist > curMaxDist )
{
if (dist > maxDist)
failed_count++;
curMaxDist = dist;
}
#if 0
if (dist > 0)
{
std::cout << "i=" << y << " fail_count=" << failed_count << " dist=" << dist << std::endl;
std::cout << "valid: " << validDescriptors.row(y) << std::endl;
std::cout << " calc: " << calcDescriptors.row(y) << std::endl;
}
#endif
}
float exact_percents = (100 * (float)exact_count / validDescriptors.rows);
float failed_percents = (100 * (float)failed_count / validDescriptors.rows);
std::stringstream ss;
ss << "Exact count (dist == 0): " << exact_count << " (" << (int)exact_percents << "%)" << std::endl
<< "Failed count (dist > " << maxDist << "): " << failed_count << " (" << (int)failed_percents << "%)" << std::endl
<< "Max distance between valid and computed descriptors (" << validDescriptors.size() << "): " << curMaxDist;
EXPECT_LE(failed_percents, 20.0f);
std::cout << ss.str() << std::endl;
}
void emptyDataTest()
{
assert( dextractor );
// One image.
Mat image;
vector<KeyPoint> keypoints;
Mat descriptors;
try
{
dextractor->compute( image, keypoints, descriptors );
}
catch(...)
{
ts->printf( cvtest::TS::LOG, "compute() on empty image and empty keypoints must not generate exception (1).\n");
ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA );
}
RNG rng;
image = cvtest::randomMat(rng, Size(50, 50), CV_8UC3, 0, 255, false);
try
{
dextractor->compute( image, keypoints, descriptors );
}
catch(...)
{
ts->printf( cvtest::TS::LOG, "compute() on nonempty image and empty keypoints must not generate exception (1).\n");
ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA );
}
image = prepare_img(false);
Mat dbl;
try
{
double_image(image, dbl);
Mat downsized_back(dbl.rows/2, dbl.cols/2, CV_32F);
resize(dbl, downsized_back, Size(dbl.cols/2, dbl.rows/2), 0, 0, INTER_NEAREST);
cv::Mat diff = (image != downsized_back);
ASSERT_EQ(0, cv::norm(image, downsized_back, NORM_INF));
}
catch(...)
{
ts->printf( cvtest::TS::LOG, "double_image() must not generate exception (1).\n");
ts->printf( cvtest::TS::LOG, "double_image() when downsized back by NEAREST must generate the same original image (1).\n");
ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA );
}
// Several images.
vector<Mat> images;
vector<vector<KeyPoint> > keypointsCollection;
vector<Mat> descriptorsCollection;
try
{
dextractor->compute( images, keypointsCollection, descriptorsCollection );
}
catch(...)
{
ts->printf( cvtest::TS::LOG, "compute() on empty images and empty keypoints collection must not generate exception (2).\n");
ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA );
}
}
void regressionTest()
{
assert( dextractor );
// Read the test image.
string imgFilename = string(ts->get_data_path()) + FEATURES2D_DIR + "/" + IMAGE_FILENAME;
Mat img = imread( imgFilename );
if( img.empty() )
{
ts->printf( cvtest::TS::LOG, "Image %s can not be read.\n", imgFilename.c_str() );
ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA );
return;
}
const std::string keypoints_filename = string(ts->get_data_path()) +
(detector.empty()
? (FEATURES2D_DIR + "/" + std::string("keypoints.xml.gz"))
: (DESCRIPTOR_DIR + "/" + name + "_keypoints.xml.gz"));
FileStorage fs(keypoints_filename, FileStorage::READ);
vector<KeyPoint> keypoints;
EXPECT_TRUE(fs.isOpened()) << "Keypoint testdata is missing. Re-computing and re-writing keypoints testdata...";
if (!fs.isOpened())
{
fs.open(keypoints_filename, FileStorage::WRITE);
ASSERT_TRUE(fs.isOpened()) << "File for writing keypoints can not be opened.";
if (detector.empty())
{
Ptr<ORB> fd = ORB::create();
fd->detect(img, keypoints);
}
else
{
detector->detect(img, keypoints);
}
write(fs, "keypoints", keypoints);
fs.release();
}
else
{
read(fs.getFirstTopLevelNode(), keypoints);
fs.release();
}
if(!detector.empty())
{
vector<KeyPoint> calcKeypoints;
detector->detect(img, calcKeypoints);
// TODO validate received keypoints
int diff = abs((int)calcKeypoints.size() - (int)keypoints.size());
if (diff > 0)
{
std::cout << "Keypoints difference: " << diff << std::endl;
EXPECT_LE(diff, (int)(keypoints.size() * 0.03f));
}
}
ASSERT_FALSE(keypoints.empty());
{
Mat calcDescriptors;
double t = (double)getTickCount();
dextractor->compute(img, keypoints, calcDescriptors);
t = getTickCount() - t;
ts->printf(cvtest::TS::LOG, "\nAverage time of computing one descriptor = %g ms.\n", t/((double)getTickFrequency()*1000.)/calcDescriptors.rows);
if (calcDescriptors.rows != (int)keypoints.size())
{
ts->printf( cvtest::TS::LOG, "Count of computed descriptors and keypoints count must be equal.\n" );
ts->printf( cvtest::TS::LOG, "Count of keypoints is %d.\n", (int)keypoints.size() );
ts->printf( cvtest::TS::LOG, "Count of computed descriptors is %d.\n", calcDescriptors.rows );
ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
return;
}
if (calcDescriptors.cols != dextractor->descriptorSize() || calcDescriptors.type() != dextractor->descriptorType())
{
ts->printf( cvtest::TS::LOG, "Incorrect descriptor size or descriptor type.\n" );
ts->printf( cvtest::TS::LOG, "Expected size is %d.\n", dextractor->descriptorSize() );
ts->printf( cvtest::TS::LOG, "Calculated size is %d.\n", calcDescriptors.cols );
ts->printf( cvtest::TS::LOG, "Expected type is %d.\n", dextractor->descriptorType() );
ts->printf( cvtest::TS::LOG, "Calculated type is %d.\n", calcDescriptors.type() );
ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
return;
}
// TODO read and write descriptor extractor parameters and check them
Mat validDescriptors = readDescriptors();
EXPECT_FALSE(validDescriptors.empty()) << "Descriptors testdata is missing. Re-writing descriptors testdata...";
if (!validDescriptors.empty())
{
compareDescriptors(validDescriptors, calcDescriptors);
}
else
{
ASSERT_TRUE(writeDescriptors(calcDescriptors)) << "Descriptors can not be written.";
}
}
}
void run(int)
{
createDescriptorExtractor();
if( !dextractor )
{
ts->printf(cvtest::TS::LOG, "Descriptor extractor is empty.\n");
ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA );
return;
}
emptyDataTest();
regressionTest();
ts->set_failed_test_info( cvtest::TS::OK );
}
virtual Mat readDescriptors()
{
Mat res = readMatFromBin( string(ts->get_data_path()) + DESCRIPTOR_DIR + "/" + string(name) );
return res;
}
virtual bool writeDescriptors( Mat& descs )
{
writeMatInBin( descs, string(ts->get_data_path()) + DESCRIPTOR_DIR + "/" + string(name) );
return true;
}
string name;
const DistanceType maxDist;
Ptr<DescriptorExtractor> dextractor;
Distance distance;
Ptr<FeatureDetector> detector;
private:
CV_DescriptorExtractorTest& operator=(const CV_DescriptorExtractorTest&) { return *this; }
};
}} // namespace
@@ -0,0 +1,37 @@
// 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 "test_invariance_utils.hpp"
#include "test_detectors_invariance.impl.hpp"
namespace opencv_test { namespace {
const static std::string IMAGE_TSUKUBA = "features2d/tsukuba.png";
const static std::string IMAGE_BIKES = "detectors_descriptors_evaluation/images_datasets/bikes/img1.png";
#define Value(...) Values(make_tuple(__VA_ARGS__))
/*
* Detector's rotation invariance check
*/
INSTANTIATE_TEST_CASE_P(SIFT, DetectorRotationInvariance,
Value(IMAGE_TSUKUBA, []() { return SIFT::create(); }, 0.45f, 0.70f));
INSTANTIATE_TEST_CASE_P(ORB, DetectorRotationInvariance,
Value(IMAGE_TSUKUBA, []() { return ORB::create(); }, 0.5f, 0.76f));
/*
* Detector's scale invariance check
*/
INSTANTIATE_TEST_CASE_P(SIFT, DetectorScaleInvariance,
Value(IMAGE_BIKES, []() { return SIFT::create(0, 3, 0.09); }, 0.60f, 0.98f));
INSTANTIATE_TEST_CASE_P(ORB, DetectorScaleInvariance,
Value(IMAGE_BIKES, []() { return ORB::create(); }, 0.08f, 0.49f));
}} // namespace
@@ -0,0 +1,226 @@
// 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_invariance_utils.hpp"
#include <functional>
namespace opencv_test { namespace {
#define SHOW_DEBUG_LOG 1
// NOTE: using factory function (function<Ptr<Type>()>) instead of object instance (Ptr<Type>) as a
// test parameter, because parameters exist during whole test program run and consume a lot of memory
typedef std::function<cv::Ptr<cv::FeatureDetector>()> DetectorFactory;
typedef tuple<std::string, DetectorFactory, float, float> String_FeatureDetector_Float_Float_t;
static
void matchKeyPoints(const vector<KeyPoint>& keypoints0, const Mat& H,
const vector<KeyPoint>& keypoints1,
vector<DMatch>& matches)
{
vector<Point2f> points0;
KeyPoint::convert(keypoints0, points0);
Mat points0t;
if(H.empty())
points0t = Mat(points0);
else
perspectiveTransform(Mat(points0), points0t, H);
matches.clear();
for(int i0 = 0; i0 < static_cast<int>(keypoints0.size()); i0++)
{
int nearestPointIndex = -1;
float maxIntersectRatio = 0.f;
const float r0 = 0.5f * keypoints0[i0].size;
for(size_t i1 = 0; i1 < keypoints1.size(); i1++)
{
float r1 = 0.5f * keypoints1[i1].size;
float intersectRatio = calcIntersectRatio(points0t.at<Point2f>(i0), r0,
keypoints1[i1].pt, r1);
if(intersectRatio > maxIntersectRatio)
{
maxIntersectRatio = intersectRatio;
nearestPointIndex = static_cast<int>(i1);
}
}
matches.push_back(DMatch(i0, nearestPointIndex, maxIntersectRatio));
}
}
class DetectorInvariance : public TestWithParam<String_FeatureDetector_Float_Float_t>
{
protected:
virtual void SetUp() {
// Read test data
const std::string filename = cvtest::TS::ptr()->get_data_path() + get<0>(GetParam());
image0 = imread(filename);
ASSERT_FALSE(image0.empty()) << "couldn't read input image";
featureDetector = get<1>(GetParam())();
minKeyPointMatchesRatio = get<2>(GetParam());
minInliersRatio = get<3>(GetParam());
}
Ptr<FeatureDetector> featureDetector;
float minKeyPointMatchesRatio;
float minInliersRatio;
Mat image0;
};
typedef DetectorInvariance DetectorScaleInvariance;
typedef DetectorInvariance DetectorRotationInvariance;
TEST_P(DetectorRotationInvariance, rotation)
{
Mat image1, mask1;
const int borderSize = 16;
Mat mask0(image0.size(), CV_8UC1, Scalar(0));
mask0(Rect(borderSize, borderSize, mask0.cols - 2*borderSize, mask0.rows - 2*borderSize)).setTo(Scalar(255));
vector<KeyPoint> keypoints0;
featureDetector->detect(image0, keypoints0, mask0);
EXPECT_GE(keypoints0.size(), 15u);
const int maxAngle = 360, angleStep = 15;
for(int angle = 0; angle < maxAngle; angle += angleStep)
{
Mat H = rotateImage(image0, mask0, static_cast<float>(angle), image1, mask1);
vector<KeyPoint> keypoints1;
featureDetector->detect(image1, keypoints1, mask1);
vector<DMatch> matches;
matchKeyPoints(keypoints0, H, keypoints1, matches);
int angleInliersCount = 0;
const float minIntersectRatio = 0.5f;
int keyPointMatchesCount = 0;
for(size_t m = 0; m < matches.size(); m++)
{
if(matches[m].distance < minIntersectRatio)
continue;
keyPointMatchesCount++;
// Check does this inlier have consistent angles
const float maxAngleDiff = 15.f; // grad
float angle0 = keypoints0[matches[m].queryIdx].angle;
float angle1 = keypoints1[matches[m].trainIdx].angle;
ASSERT_FALSE(angle0 == -1 || angle1 == -1) << "Given FeatureDetector is not rotation invariant, it can not be tested here.";
ASSERT_GE(angle0, 0.f);
ASSERT_LT(angle0, 360.f);
ASSERT_GE(angle1, 0.f);
ASSERT_LT(angle1, 360.f);
float rotAngle0 = angle0 + angle;
if(rotAngle0 >= 360.f)
rotAngle0 -= 360.f;
float angleDiff = std::max(rotAngle0, angle1) - std::min(rotAngle0, angle1);
angleDiff = std::min(angleDiff, static_cast<float>(360.f - angleDiff));
ASSERT_GE(angleDiff, 0.f);
bool isAngleCorrect = angleDiff < maxAngleDiff;
if(isAngleCorrect)
angleInliersCount++;
}
float keyPointMatchesRatio = static_cast<float>(keyPointMatchesCount) / keypoints0.size();
EXPECT_GE(keyPointMatchesRatio, minKeyPointMatchesRatio) << "angle: " << angle;
if(keyPointMatchesCount)
{
float angleInliersRatio = static_cast<float>(angleInliersCount) / keyPointMatchesCount;
EXPECT_GE(angleInliersRatio, minInliersRatio) << "angle: " << angle;
}
#if SHOW_DEBUG_LOG
std::cout
<< "angle = " << angle
<< ", keypoints = " << keypoints1.size()
<< ", keyPointMatchesRatio = " << keyPointMatchesRatio
<< ", angleInliersRatio = " << (keyPointMatchesCount ? (static_cast<float>(angleInliersCount) / keyPointMatchesCount) : 0)
<< std::endl;
#endif
}
}
TEST_P(DetectorScaleInvariance, scale)
{
vector<KeyPoint> keypoints0;
featureDetector->detect(image0, keypoints0);
EXPECT_GE(keypoints0.size(), 15u);
for(int scaleIdx = 1; scaleIdx <= 3; scaleIdx++)
{
float scale = 1.f + scaleIdx * 0.5f;
Mat image1;
resize(image0, image1, Size(), 1./scale, 1./scale, INTER_LINEAR_EXACT);
vector<KeyPoint> keypoints1, osiKeypoints1; // osi - original size image
featureDetector->detect(image1, keypoints1);
EXPECT_GE(keypoints1.size(), 15u);
EXPECT_LE(keypoints1.size(), keypoints0.size()) << "Strange behavior of the detector. "
"It gives more points count in an image of the smaller size.";
scaleKeyPoints(keypoints1, osiKeypoints1, scale);
vector<DMatch> matches;
// image1 is query image (it's reduced image0)
// image0 is train image
matchKeyPoints(osiKeypoints1, Mat(), keypoints0, matches);
const float minIntersectRatio = 0.5f;
int keyPointMatchesCount = 0;
int scaleInliersCount = 0;
for(size_t m = 0; m < matches.size(); m++)
{
if(matches[m].distance < minIntersectRatio)
continue;
keyPointMatchesCount++;
// Check does this inlier have consistent sizes
const float maxSizeDiff = 0.8f;//0.9f; // grad
float size0 = keypoints0[matches[m].trainIdx].size;
float size1 = osiKeypoints1[matches[m].queryIdx].size;
ASSERT_GT(size0, 0);
ASSERT_GT(size1, 0);
if(std::min(size0, size1) > maxSizeDiff * std::max(size0, size1))
scaleInliersCount++;
}
float keyPointMatchesRatio = static_cast<float>(keyPointMatchesCount) / keypoints1.size();
EXPECT_GE(keyPointMatchesRatio, minKeyPointMatchesRatio);
if(keyPointMatchesCount)
{
float scaleInliersRatio = static_cast<float>(scaleInliersCount) / keyPointMatchesCount;
EXPECT_GE(scaleInliersRatio, minInliersRatio);
}
#if SHOW_DEBUG_LOG
std::cout
<< "scale = " << scale
<< ", keyPointMatchesRatio = " << keyPointMatchesRatio
<< ", scaleInliersRatio = " << (keyPointMatchesCount ? static_cast<float>(scaleInliersCount) / keyPointMatchesCount : 0)
<< std::endl;
#endif
}
}
#undef SHOW_DEBUG_LOG
}} // namespace
namespace std {
using namespace opencv_test;
static inline void PrintTo(const String_FeatureDetector_Float_Float_t& v, std::ostream* os)
{
*os << "(\"" << get<0>(v)
<< "\", " << get<2>(v)
<< ", " << get<3>(v)
<< ")";
}
} // namespace
@@ -0,0 +1,59 @@
// 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"
namespace opencv_test { namespace {
const string FEATURES2D_DIR = "features2d";
const string IMAGE_FILENAME = "tsukuba.png";
const string DETECTOR_DIR = FEATURES2D_DIR + "/feature_detectors";
}} // namespace
#include "test_detectors_regression.impl.hpp"
namespace opencv_test { namespace {
/****************************************************************************************\
* Tests registrations *
\****************************************************************************************/
TEST( Features2d_Detector_SIFT, regression )
{
CV_FeatureDetectorTest test( "detector-sift", SIFT::create() );
test.safe_run();
}
TEST( Features2d_Detector_FAST, regression )
{
CV_FeatureDetectorTest test( "detector-fast", FastFeatureDetector::create() );
test.safe_run();
}
TEST( Features2d_Detector_GFTT, regression )
{
CV_FeatureDetectorTest test( "detector-gftt", GFTTDetector::create() );
test.safe_run();
}
TEST( Features2d_Detector_Harris, regression )
{
Ptr<GFTTDetector> gftt = GFTTDetector::create();
gftt->setHarrisDetector(true);
CV_FeatureDetectorTest test( "detector-harris", gftt);
test.safe_run();
}
TEST( Features2d_Detector_MSER, DISABLED_regression )
{
CV_FeatureDetectorTest test( "detector-mser", MSER::create() );
test.safe_run();
}
TEST( Features2d_Detector_ORB, regression )
{
CV_FeatureDetectorTest test( "detector-orb", ORB::create() );
test.safe_run();
}
}} // namespace
@@ -0,0 +1,201 @@
// 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
namespace opencv_test { namespace {
/****************************************************************************************\
* Regression tests for feature detectors comparing keypoints. *
\****************************************************************************************/
class CV_FeatureDetectorTest : public cvtest::BaseTest
{
public:
CV_FeatureDetectorTest( const string& _name, const Ptr<FeatureDetector>& _fdetector ) :
name(_name), fdetector(_fdetector) {}
protected:
bool isSimilarKeypoints( const KeyPoint& p1, const KeyPoint& p2 );
void compareKeypointSets( const vector<KeyPoint>& validKeypoints, const vector<KeyPoint>& calcKeypoints );
void emptyDataTest();
void regressionTest(); // TODO test of detect() with mask
virtual void run( int );
string name;
Ptr<FeatureDetector> fdetector;
};
void CV_FeatureDetectorTest::emptyDataTest()
{
// One image.
Mat image;
vector<KeyPoint> keypoints;
try
{
fdetector->detect( image, keypoints );
}
catch(...)
{
ts->printf( cvtest::TS::LOG, "detect() on empty image must not generate exception (1).\n" );
ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
}
if( !keypoints.empty() )
{
ts->printf( cvtest::TS::LOG, "detect() on empty image must return empty keypoints vector (1).\n" );
ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
return;
}
// Several images.
vector<Mat> images;
vector<vector<KeyPoint> > keypointCollection;
try
{
fdetector->detect( images, keypointCollection );
}
catch(...)
{
ts->printf( cvtest::TS::LOG, "detect() on empty image vector must not generate exception (2).\n" );
ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
}
}
bool CV_FeatureDetectorTest::isSimilarKeypoints( const KeyPoint& p1, const KeyPoint& p2 )
{
const float maxPtDif = 1.f;
const float maxSizeDif = 1.f;
const float maxAngleDif = 2.f;
const float maxResponseDif = 0.1f;
float dist = (float)cv::norm( p1.pt - p2.pt );
return (dist < maxPtDif &&
fabs(p1.size - p2.size) < maxSizeDif &&
abs(p1.angle - p2.angle) < maxAngleDif &&
abs(p1.response - p2.response) < maxResponseDif &&
p1.octave == p2.octave &&
p1.class_id == p2.class_id );
}
void CV_FeatureDetectorTest::compareKeypointSets( const vector<KeyPoint>& validKeypoints, const vector<KeyPoint>& calcKeypoints )
{
const float maxCountRatioDif = 0.01f;
// Compare counts of validation and calculated keypoints.
float countRatio = (float)validKeypoints.size() / (float)calcKeypoints.size();
if( countRatio < 1 - maxCountRatioDif || countRatio > 1.f + maxCountRatioDif )
{
ts->printf( cvtest::TS::LOG, "Bad keypoints count ratio (validCount = %d, calcCount = %d).\n",
validKeypoints.size(), calcKeypoints.size() );
ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
return;
}
int progress = 0, progressCount = (int)(validKeypoints.size() * calcKeypoints.size());
int badPointCount = 0, commonPointCount = max((int)validKeypoints.size(), (int)calcKeypoints.size());
for( size_t v = 0; v < validKeypoints.size(); v++ )
{
int nearestIdx = -1;
float minDist = std::numeric_limits<float>::max();
for( size_t c = 0; c < calcKeypoints.size(); c++ )
{
progress = update_progress( progress, (int)(v*calcKeypoints.size() + c), progressCount, 0 );
float curDist = (float)cv::norm( calcKeypoints[c].pt - validKeypoints[v].pt );
if( curDist < minDist )
{
minDist = curDist;
nearestIdx = (int)c;
}
}
assert( minDist >= 0 );
if( !isSimilarKeypoints( validKeypoints[v], calcKeypoints[nearestIdx] ) )
badPointCount++;
}
ts->printf( cvtest::TS::LOG, "badPointCount = %d; validPointCount = %d; calcPointCount = %d\n",
badPointCount, validKeypoints.size(), calcKeypoints.size() );
if( badPointCount > 0.9 * commonPointCount )
{
ts->printf( cvtest::TS::LOG, " - Bad accuracy!\n" );
ts->set_failed_test_info( cvtest::TS::FAIL_BAD_ACCURACY );
return;
}
ts->printf( cvtest::TS::LOG, " - OK\n" );
}
void CV_FeatureDetectorTest::regressionTest()
{
assert( !fdetector.empty() );
string imgFilename = string(ts->get_data_path()) + FEATURES2D_DIR + "/" + IMAGE_FILENAME;
string resFilename = string(ts->get_data_path()) + DETECTOR_DIR + "/" + string(name) + ".xml.gz";
// Read the test image.
Mat image = imread( imgFilename );
if( image.empty() )
{
ts->printf( cvtest::TS::LOG, "Image %s can not be read.\n", imgFilename.c_str() );
ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA );
return;
}
FileStorage fs( resFilename, FileStorage::READ );
// Compute keypoints.
vector<KeyPoint> calcKeypoints;
fdetector->detect( image, calcKeypoints );
if( fs.isOpened() ) // Compare computed and valid keypoints.
{
// TODO compare saved feature detector params with current ones
// Read validation keypoints set.
vector<KeyPoint> validKeypoints;
read( fs["keypoints"], validKeypoints );
if( validKeypoints.empty() )
{
ts->printf( cvtest::TS::LOG, "Keypoints can not be read.\n" );
ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA );
return;
}
compareKeypointSets( validKeypoints, calcKeypoints );
}
else // Write detector parameters and computed keypoints as validation data.
{
fs.open( resFilename, FileStorage::WRITE );
if( !fs.isOpened() )
{
ts->printf( cvtest::TS::LOG, "File %s can not be opened to write.\n", resFilename.c_str() );
ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA );
return;
}
else
{
fs << "detector_params" << "{";
fdetector->write( fs );
fs << "}";
write( fs, "keypoints", calcKeypoints );
}
}
}
void CV_FeatureDetectorTest::run( int /*start_from*/ )
{
if( !fdetector )
{
ts->printf( cvtest::TS::LOG, "Feature detector is empty.\n" );
ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA );
return;
}
emptyDataTest();
regressionTest();
ts->set_failed_test_info( cvtest::TS::OK );
}
}} // namespace
+174
View File
@@ -0,0 +1,174 @@
// 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.
// Copyright (C) 2026, BigVision LLC, all rights reserved.
// Third party copyrights are property of their respective owners.
#include "test_precomp.hpp"
#include "npy_blob.hpp"
#ifdef HAVE_OPENCV_DNN
#include "opencv2/dnn.hpp"
#include "opencv2/core/utils/configuration.private.hpp"
namespace opencv_test { namespace {
static void skipIfClassicDnnEngine()
{
const auto engine = static_cast<cv::dnn::EngineType>(
cv::utils::getConfigurationParameterSizeT("OPENCV_FORCE_DNN_ENGINE", cv::dnn::ENGINE_AUTO));
if (engine == cv::dnn::ENGINE_CLASSIC)
throw SkipTestException("DISK ONNX model is not supported by the classic DNN engine");
}
static void testDiskRegression(const Size& imageSize, const std::string& tag)
{
skipIfClassicDnnEngine();
applyTestTag(CV_TEST_TAG_MEMORY_2GB);
Mat refKpts = blobFromNPY(cvtest::findDataFile("features/disk/box_in_scene_" + tag + "_kpts.npy"));
Mat refDesc = blobFromNPY(cvtest::findDataFile("features/disk/box_in_scene_" + tag + "_desc.npy"));
if (refKpts.type() != CV_32F)
refKpts.convertTo(refKpts, CV_32F);
ASSERT_EQ(refKpts.cols, 3);
const int n = refKpts.rows;
const std::string modelPath = cvtest::findDataFile("dnn/disk.onnx", false);
Ptr<DISK> detector;
ASSERT_NO_THROW(detector = DISK::create(modelPath, n, 0.0f, imageSize));
ASSERT_TRUE(detector);
EXPECT_FALSE(detector->empty());
EXPECT_EQ(detector->descriptorSize(), 128);
EXPECT_EQ(detector->descriptorType(), CV_32F);
EXPECT_EQ(detector->defaultNorm(), NORM_L2);
Mat img = imread(cvtest::findDataFile("shared/box_in_scene.png"));
ASSERT_FALSE(img.empty());
std::vector<KeyPoint> keypoints;
Mat descriptors;
detector->detectAndCompute(img, noArray(), keypoints, descriptors);
ASSERT_EQ(static_cast<int>(keypoints.size()), n) << "keypoint count mismatch (" << tag << ")";
ASSERT_EQ(descriptors.rows, n);
ASSERT_EQ(descriptors.cols, refDesc.cols);
ASSERT_EQ(descriptors.type(), CV_32F);
Mat pos(n, 2, CV_32F), resp(n, 1, CV_32F);
for (int i = 0; i < n; ++i)
{
pos.at<float>(i, 0) = keypoints[i].pt.x;
pos.at<float>(i, 1) = keypoints[i].pt.y;
resp.at<float>(i, 0) = keypoints[i].response;
}
EXPECT_LE(cvtest::norm(pos, refKpts.colRange(0, 2), NORM_INF), 1e-3)
<< "keypoint positions differ (" << tag << ")";
EXPECT_LE(cvtest::norm(resp, refKpts.col(2), NORM_INF), 0.1)
<< "keypoint responses differ (" << tag << ")";
EXPECT_LE(cvtest::norm(descriptors, refDesc, NORM_INF), 1e-2)
<< "descriptors differ (" << tag << ")";
}
TEST(Features2d_DISK, regression_default)
{
testDiskRegression(Size(), "default");
}
TEST(Features2d_DISK, regression_512x384)
{
testDiskRegression(Size(512, 384), "512x384");
}
TEST(Features2d_DISK, MaxKeypointsAndThreshold)
{
skipIfClassicDnnEngine();
applyTestTag(CV_TEST_TAG_MEMORY_2GB);
const std::string modelPath = cvtest::findDataFile("dnn/disk.onnx", false);
Ptr<DISK> detector = DISK::create(modelPath);
ASSERT_TRUE(detector);
Mat img = imread(cvtest::findDataFile("shared/lena.png"));
ASSERT_FALSE(img.empty());
std::vector<KeyPoint> baseKpts;
Mat baseDesc;
detector->detectAndCompute(img, noArray(), baseKpts, baseDesc);
ASSERT_GT(baseKpts.size(), 50u);
const int kCap = 50;
detector->setMaxKeypoints(kCap);
EXPECT_EQ(detector->getMaxKeypoints(), kCap);
std::vector<KeyPoint> capKpts;
Mat capDesc;
detector->detectAndCompute(img, noArray(), capKpts, capDesc);
EXPECT_EQ(capKpts.size(), static_cast<size_t>(kCap));
EXPECT_EQ(capDesc.rows, kCap);
float minKept = std::numeric_limits<float>::max();
for (const KeyPoint& kp : capKpts)
minKept = std::min(minKept, kp.response);
detector->setMaxKeypoints(-1);
detector->setScoreThreshold(minKept);
std::vector<KeyPoint> thrKpts;
detector->detectAndCompute(img, noArray(), thrKpts, noArray());
for (const KeyPoint& kp : thrKpts)
EXPECT_GT(kp.response, minKept);
}
TEST(Features2d_DISK, MaskSupport)
{
skipIfClassicDnnEngine();
applyTestTag(CV_TEST_TAG_MEMORY_2GB);
const std::string modelPath = cvtest::findDataFile("dnn/disk.onnx", false);
Ptr<DISK> detector = DISK::create(modelPath);
Mat img = imread(cvtest::findDataFile("shared/lena.png"));
ASSERT_FALSE(img.empty());
Mat mask = Mat::zeros(img.size(), CV_8UC1);
const Rect roi(img.cols / 4, img.rows / 4, img.cols / 2, img.rows / 2);
mask(roi).setTo(255);
std::vector<KeyPoint> keypoints;
Mat descriptors;
detector->detectAndCompute(img, mask, keypoints, descriptors);
ASSERT_FALSE(keypoints.empty());
ASSERT_EQ(descriptors.rows, static_cast<int>(keypoints.size()));
for (const KeyPoint& kp : keypoints)
{
EXPECT_TRUE(roi.contains(Point(cvFloor(kp.pt.x), cvFloor(kp.pt.y))))
<< "Keypoint " << kp.pt << " escaped the mask ROI " << roi;
}
}
TEST(Features2d_DISK, InvalidImageSize)
{
skipIfClassicDnnEngine();
const std::string modelPath = cvtest::findDataFile("dnn/disk.onnx", false);
EXPECT_THROW(DISK::create(modelPath, -1, 0.0f, Size(1000, 1024)), cv::Exception);
EXPECT_THROW(DISK::create(modelPath, -1, 0.0f, Size(1024, 1000)), cv::Exception);
EXPECT_THROW(DISK::create(modelPath, -1, 0.0f, Size(-16, 1024)), cv::Exception);
Ptr<DISK> detector;
ASSERT_NO_THROW(detector = DISK::create(modelPath, -1, 0.0f, Size()));
ASSERT_TRUE(detector);
EXPECT_THROW(detector->setImageSize(Size(15, 1024)), cv::Exception);
EXPECT_NO_THROW(detector->setImageSize(Size(512, 512)));
EXPECT_EQ(detector->getImageSize(), Size(512, 512));
}
}} // namespace
#endif // HAVE_OPENCV_DNN
+78
View File
@@ -0,0 +1,78 @@
// 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.
//
// Copyright (C) 2018, Intel Corporation, all rights reserved.
// Third party copyrights are property of their respective owners.
#include "test_precomp.hpp"
namespace opencv_test { namespace {
static
Mat getReference_DrawKeypoint(int cn)
{
static Mat ref = (Mat_<uint8_t>(11, 11) <<
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 15, 54, 15, 1, 1, 1, 1,
1, 1, 1, 76, 217, 217, 221, 81, 1, 1, 1,
1, 1, 100, 224, 111, 57, 115, 225, 101, 1, 1,
1, 44, 215, 100, 1, 1, 1, 101, 214, 44, 1,
1, 54, 212, 57, 1, 1, 1, 55, 212, 55, 1,
1, 40, 215, 104, 1, 1, 1, 105, 215, 40, 1,
1, 1, 102, 221, 111, 55, 115, 222, 103, 1, 1,
1, 1, 1, 76, 218, 217, 220, 81, 1, 1, 1,
1, 1, 1, 1, 15, 55, 15, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1);
Mat res;
cvtColor(ref, res, (cn == 4) ? COLOR_GRAY2BGRA : COLOR_GRAY2BGR);
return res;
}
typedef testing::TestWithParam<MatType> Features2D_drawKeypoints;
TEST_P(Features2D_drawKeypoints, Accuracy)
{
const int cn = CV_MAT_CN(GetParam());
Mat inpImg(11, 11, GetParam(), Scalar(1, 1, 1, 255)), outImg;
std::vector<KeyPoint> keypoints(1, KeyPoint(5, 5, 1));
drawKeypoints(inpImg, keypoints, outImg, Scalar::all(255));
ASSERT_EQ(outImg.channels(), (cn == 4) ? 4 : 3);
Mat ref_ = getReference_DrawKeypoint(cn);
EXPECT_EQ(0, cv::norm(outImg, ref_, NORM_INF));
}
INSTANTIATE_TEST_CASE_P(/**/, Features2D_drawKeypoints, Values(CV_8UC1, CV_8UC3, CV_8UC4));
typedef testing::TestWithParam<tuple<MatType, MatType> > Features2D_drawMatches;
TEST_P(Features2D_drawMatches, Accuracy)
{
Mat inpImg1(11, 11, get<0>(GetParam()), Scalar(1, 1, 1, 255));
Mat inpImg2(11, 11, get<1>(GetParam()), Scalar(2, 2, 2, 255)), outImg2, outImg;
std::vector<KeyPoint> keypoints(1, KeyPoint(5, 5, 1));
// Get outImg2 using drawKeypoints assuming that it works correctly (see the test above).
drawKeypoints(inpImg2, keypoints, outImg2, Scalar::all(255));
ASSERT_EQ(outImg2.channels(), (inpImg2.channels() == 4) ? 4 : 3);
// Merge both references.
const int cn = max(3, max(inpImg1.channels(), inpImg2.channels()));
if (cn == 4 && outImg2.channels() == 3)
cvtColor(outImg2, outImg2, COLOR_BGR2BGRA);
Mat ref_ = getReference_DrawKeypoint(cn);
Mat concattedRef;
hconcat(ref_, outImg2, concattedRef);
std::vector<DMatch> matches;
drawMatches(inpImg1, keypoints, inpImg2, keypoints, matches, outImg,
Scalar::all(255), Scalar::all(255));
ASSERT_EQ(outImg.channels(), cn);
EXPECT_EQ(0, cv::norm(outImg, concattedRef, NORM_INF));
}
INSTANTIATE_TEST_CASE_P(/**/, Features2D_drawMatches, Combine(
Values(CV_8UC1, CV_8UC3, CV_8UC4),
Values(CV_8UC1, CV_8UC3, CV_8UC4)
));
}} // namespace
+168
View File
@@ -0,0 +1,168 @@
/*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_FastTest : public cvtest::BaseTest
{
public:
CV_FastTest();
~CV_FastTest();
protected:
void run(int);
};
CV_FastTest::CV_FastTest() {}
CV_FastTest::~CV_FastTest() {}
void CV_FastTest::run( int )
{
for(int type=0; type <= 2; ++type) {
Mat image1 = imread(string(ts->get_data_path()) + "inpaint/orig.png");
Mat image2 = imread(string(ts->get_data_path()) + "cameracalibration/chess9.png");
string xml = string(ts->get_data_path()) + format("fast/result%d.xml", type);
if (image1.empty() || image2.empty())
{
ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA );
return;
}
Mat gray1, gray2;
cvtColor(image1, gray1, COLOR_BGR2GRAY);
cvtColor(image2, gray2, COLOR_BGR2GRAY);
vector<KeyPoint> keypoints1;
vector<KeyPoint> keypoints2;
FAST(gray1, keypoints1, 30, true, static_cast<FastFeatureDetector::DetectorType>(type));
FAST(gray2, keypoints2, (type > 0 ? 30 : 20), true, static_cast<FastFeatureDetector::DetectorType>(type));
for(size_t i = 0; i < keypoints1.size(); ++i)
{
const KeyPoint& kp = keypoints1[i];
cv::circle(image1, kp.pt, cvRound(kp.size/2), Scalar(255, 0, 0));
}
for(size_t i = 0; i < keypoints2.size(); ++i)
{
const KeyPoint& kp = keypoints2[i];
cv::circle(image2, kp.pt, cvRound(kp.size/2), Scalar(255, 0, 0));
}
Mat kps1(1, (int)(keypoints1.size() * sizeof(KeyPoint)), CV_8U, &keypoints1[0]);
Mat kps2(1, (int)(keypoints2.size() * sizeof(KeyPoint)), CV_8U, &keypoints2[0]);
FileStorage fs(xml, FileStorage::READ);
if (!fs.isOpened())
{
fs.open(xml, FileStorage::WRITE);
if (!fs.isOpened())
{
ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_TEST_DATA);
return;
}
fs << "exp_kps1" << kps1;
fs << "exp_kps2" << kps2;
fs.release();
fs.open(xml, FileStorage::READ);
if (!fs.isOpened())
{
ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_TEST_DATA);
return;
}
}
Mat exp_kps1, exp_kps2;
read( fs["exp_kps1"], exp_kps1, Mat() );
read( fs["exp_kps2"], exp_kps2, Mat() );
fs.release();
if ( exp_kps1.size != kps1.size || 0 != cvtest::norm(exp_kps1, kps1, NORM_L2) ||
exp_kps2.size != kps2.size || 0 != cvtest::norm(exp_kps2, kps2, NORM_L2))
{
ts->set_failed_test_info(cvtest::TS::FAIL_MISMATCH);
return;
}
/*cv::namedWindow("Img1"); cv::imshow("Img1", image1);
cv::namedWindow("Img2"); cv::imshow("Img2", image2);
cv::waitKey(0);*/
}
ts->set_failed_test_info(cvtest::TS::OK);
}
TEST(Features2d_FAST, regression) { CV_FastTest test; test.safe_run(); }
// #define DUMP_TEST_DATA
TEST(Features2d_FAST, noNMS)
{
Mat img = imread(string(cvtest::TS::ptr()->get_data_path()) + "inpaint/orig.png", cv::IMREAD_GRAYSCALE);
string xml = string(cvtest::TS::ptr()->get_data_path()) + "fast/result_no_nonmax.xml";
vector<KeyPoint> keypoints;
FAST(img, keypoints, 100, false, FastFeatureDetector::DetectorType::TYPE_9_16);
Mat kps(1, (int)(keypoints.size() * sizeof(KeyPoint)), CV_8U, &keypoints[0]);
Mat gt_kps;
FileStorage fs(xml, FileStorage::READ);
#ifdef DUMP_TEST_DATA
if (!fs.isOpened())
{
fs.open(xml, FileStorage::WRITE);
fs << "exp_kps" << kps;
fs.release();
fs.open(xml, FileStorage::READ);
}
#endif
ASSERT_TRUE(fs.isOpened());
fs["exp_kps"] >> gt_kps;
fs.release();
ASSERT_GT(gt_kps.total(), size_t(0));
ASSERT_EQ( 0, cvtest::norm(gt_kps, kps, NORM_L2));
}
}} // namespace
@@ -0,0 +1,550 @@
/*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.
//
//
// Intel License Agreement
// For Open Source Computer Vision Library
//
// Copyright (C) 2000, 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 Intel Corporation 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 {
enum { MINEIGENVAL=0, HARRIS=1, EIGENVALSVECS=2 };
#if 0 //set 1 to switch ON debug message
#define TEST_MESSAGE( message ) std::cout << message;
#define TEST_MESSAGEL( message, val) std::cout << message << val << std::endl;
#else
#define TEST_MESSAGE( message )
#define TEST_MESSAGEL( message, val)
#endif
/////////////////////ref//////////////////////
struct greaterThanPtr
{
bool operator () (const float * a, const float * b) const
{ return *a > *b; }
};
static void
test_cornerEigenValsVecs( const Mat& src, Mat& eigenv, int block_size,
int _aperture_size, double k, int mode, int borderType, const Scalar& _borderValue )
{
int i, j;
Scalar borderValue = _borderValue;
int aperture_size = _aperture_size < 0 ? 3 : _aperture_size;
Point anchor( aperture_size/2, aperture_size/2 );
CV_Assert( src.type() == CV_8UC1 || src.type() == CV_32FC1 );
CV_Assert( eigenv.type() == CV_32FC1 );
CV_Assert( ( src.rows == eigenv.rows ) &&
(((mode == MINEIGENVAL)||(mode == HARRIS)) && (src.cols == eigenv.cols)) );
int type = src.type();
int ftype = CV_32FC1;
double kernel_scale = 1;
Mat dx2, dy2, dxdy(src.size(), CV_32F), kernel;
kernel = cvtest::calcSobelKernel2D( 1, 0, _aperture_size );
cvtest::filter2D( src, dx2, ftype, kernel*kernel_scale, anchor, 0, borderType, borderValue );
kernel = cvtest::calcSobelKernel2D( 0, 1, _aperture_size );
cvtest::filter2D( src, dy2, ftype, kernel*kernel_scale, anchor, 0, borderType,borderValue );
double denom = (1 << (aperture_size-1))*block_size;
if( _aperture_size < 0 )
denom *= 2.;
if(type != ftype )
denom *= 255.;
denom = 1. / (denom * denom);
for( i = 0; i < src.rows; i++ )
{
float* dxdyp = dxdy.ptr<float>(i);
float* dx2p = dx2.ptr<float>(i);
float* dy2p = dy2.ptr<float>(i);
for( j = 0; j < src.cols; j++ )
{
double xval = dx2p[j], yval = dy2p[j];
dxdyp[j] = (float)(xval*yval*denom);
dx2p[j] = (float)(xval*xval*denom);
dy2p[j] = (float)(yval*yval*denom);
}
}
kernel = Mat::ones(block_size, block_size, CV_32F);
anchor = Point(block_size/2, block_size/2);
cvtest::filter2D( dx2, dx2, ftype, kernel, anchor, 0, borderType, borderValue );
cvtest::filter2D( dy2, dy2, ftype, kernel, anchor, 0, borderType, borderValue );
cvtest::filter2D( dxdy, dxdy, ftype, kernel, anchor, 0, borderType, borderValue );
if( mode == MINEIGENVAL )
{
for( i = 0; i < src.rows; i++ )
{
float* eigenvp = eigenv.ptr<float>(i);
const float* dxdyp = dxdy.ptr<float>(i);
const float* dx2p = dx2.ptr<float>(i);
const float* dy2p = dy2.ptr<float>(i);
for( j = 0; j < src.cols; j++ )
{
double a = dx2p[j], b = dxdyp[j], c = dy2p[j];
double d = sqrt( ( a - c )*( a - c ) + 4*b*b );
eigenvp[j] = (float)( 0.5*(a + c - d));
}
}
}
else if( mode == HARRIS )
{
for( i = 0; i < src.rows; i++ )
{
float* eigenvp = eigenv.ptr<float>(i);
const float* dxdyp = dxdy.ptr<float>(i);
const float* dx2p = dx2.ptr<float>(i);
const float* dy2p = dy2.ptr<float>(i);
for( j = 0; j < src.cols; j++ )
{
double a = dx2p[j], b = dxdyp[j], c = dy2p[j];
eigenvp[j] = (float)(a*c - b*b - k*(a + c)*(a + c));
}
}
}
}
static void
test_goodFeaturesToTrack( InputArray _image, OutputArray _corners,
int maxCorners, double qualityLevel, double minDistance,
InputArray _mask, OutputArray _cornersQuality,
int blockSize, int gradientSize, bool useHarrisDetector, double harrisK)
{
CV_Assert( qualityLevel > 0 && minDistance >= 0 && maxCorners >= 0 );
CV_Assert( _mask.empty() || (_mask.type() == CV_8UC1 && _mask.sameSize(_image)) );
Mat image = _image.getMat(), mask = _mask.getMat();
int aperture_size = gradientSize;
int borderType = BORDER_DEFAULT;
Mat eig, tmp, tt;
eig.create( image.size(), CV_32F );
if( useHarrisDetector )
test_cornerEigenValsVecs( image, eig, blockSize, aperture_size, harrisK, HARRIS, borderType, 0 );
else
test_cornerEigenValsVecs( image, eig, blockSize, aperture_size, 0, MINEIGENVAL, borderType, 0 );
double maxVal = 0;
cvtest::minMaxIdx( eig, 0, &maxVal, 0, 0, mask );
cvtest::threshold( eig, eig, (float)(maxVal*qualityLevel), 0.f,THRESH_TOZERO );
cvtest::dilate( eig, tmp, Mat(),Point(-1,-1),borderType,0);
Size imgsize = image.size();
vector<const float*> tmpCorners;
// collect list of pointers to features - put them into temporary image
for( int y = 1; y < imgsize.height - 1; y++ )
{
const float* eig_data = (const float*)eig.ptr(y);
const float* tmp_data = (const float*)tmp.ptr(y);
const uchar* mask_data = mask.data ? mask.ptr(y) : 0;
for( int x = 1; x < imgsize.width - 1; x++ )
{
float val = eig_data[x];
if( val != 0 && val == tmp_data[x] && (!mask_data || mask_data[x]) )
{
tmpCorners.push_back(eig_data + x);
}
}
}
vector<Point2f> corners;
vector<float> cornersQuality;
size_t i, j, total = tmpCorners.size(), ncorners = 0;
std::sort( tmpCorners.begin(), tmpCorners.end(), greaterThanPtr() );
if(minDistance >= 1)
{
// Partition the image into larger grids
int w = image.cols;
int h = image.rows;
const int cell_size = cvRound(minDistance);
const int grid_width = (w + cell_size - 1) / cell_size;
const int grid_height = (h + cell_size - 1) / cell_size;
std::vector<std::vector<Point2f> > grid(grid_width*grid_height);
minDistance *= minDistance;
for( i = 0; i < total; i++ )
{
int ofs = (int)((const uchar*)tmpCorners[i] - eig.data);
int y = (int)(ofs / eig.step);
int x = (int)((ofs - y*eig.step)/sizeof(float));
bool good = true;
int x_cell = x / cell_size;
int y_cell = y / cell_size;
int x1 = x_cell - 1;
int y1 = y_cell - 1;
int x2 = x_cell + 1;
int y2 = y_cell + 1;
// boundary check
x1 = std::max(0, x1);
y1 = std::max(0, y1);
x2 = std::min(grid_width-1, x2);
y2 = std::min(grid_height-1, y2);
for( int yy = y1; yy <= y2; yy++ )
{
for( int xx = x1; xx <= x2; xx++ )
{
vector <Point2f> &m = grid[yy*grid_width + xx];
if( m.size() )
{
for(j = 0; j < m.size(); j++)
{
float dx = x - m[j].x;
float dy = y - m[j].y;
if( dx*dx + dy*dy < minDistance )
{
good = false;
goto break_out;
}
}
}
}
}
break_out:
if(good)
{
grid[y_cell*grid_width + x_cell].push_back(Point2f((float)x, (float)y));
cornersQuality.push_back(*tmpCorners[i]);
corners.push_back(Point2f((float)x, (float)y));
++ncorners;
if( maxCorners > 0 && (int)ncorners == maxCorners )
break;
}
}
}
else
{
for( i = 0; i < total; i++ )
{
cornersQuality.push_back(*tmpCorners[i]);
int ofs = (int)((const uchar*)tmpCorners[i] - eig.data);
int y = (int)(ofs / eig.step);
int x = (int)((ofs - y*eig.step)/sizeof(float));
corners.push_back(Point2f((float)x, (float)y));
++ncorners;
if( maxCorners > 0 && (int)ncorners == maxCorners )
break;
}
}
Mat(corners).convertTo(_corners, _corners.fixedType() ? _corners.type() : CV_32F);
if (_cornersQuality.needed()) {
Mat(cornersQuality).convertTo(_cornersQuality, _cornersQuality.fixedType() ? _cornersQuality.type() : CV_32F);
}
}
/////////////////end of ref code//////////////////////////
class CV_GoodFeatureToTTest : public cvtest::ArrayTest
{
public:
CV_GoodFeatureToTTest();
protected:
int prepare_test_case( int test_case_idx );
void run_func();
int validate_test_results( int test_case_idx );
Mat src, src_gray;
Mat src_gray32f, src_gray8U;
Mat mask;
int maxCorners;
vector<Point2f> corners;
vector<Point2f> Refcorners;
vector<float> cornersQuality;
vector<float> RefcornersQuality;
double qualityLevel;
double minDistance;
int blockSize;
int gradientSize;
bool useHarrisDetector;
double k;
int SrcType;
};
CV_GoodFeatureToTTest::CV_GoodFeatureToTTest()
{
RNG& rng = ts->get_rng();
maxCorners = rng.uniform( 50, 100 );
qualityLevel = 0.01;
minDistance = 10;
blockSize = 3;
gradientSize = 3;
useHarrisDetector = false;
k = 0.04;
mask = Mat();
test_case_count = 4;
SrcType = 0;
}
int CV_GoodFeatureToTTest::prepare_test_case( int test_case_idx )
{
const static int types[] = { CV_32FC1, CV_8UC1 };
cvtest::TS& tst = *cvtest::TS::ptr();
src = imread(string(tst.get_data_path()) + "shared/fruits.png", IMREAD_COLOR);
CV_Assert(src.data != NULL);
cvtColor( src, src_gray, COLOR_BGR2GRAY );
SrcType = types[test_case_idx & 0x1];
useHarrisDetector = test_case_idx & 2 ? true : false;
return 1;
}
void CV_GoodFeatureToTTest::run_func()
{
int cn = src_gray.channels();
CV_Assert( cn == 1 );
CV_Assert( ( CV_MAT_DEPTH(SrcType) == CV_32FC1 ) || ( CV_MAT_DEPTH(SrcType) == CV_8UC1 ));
TEST_MESSAGEL (" maxCorners = ", maxCorners)
if (useHarrisDetector)
{
TEST_MESSAGE (" useHarrisDetector = true\n");
}
else
{
TEST_MESSAGE (" useHarrisDetector = false\n");
}
if( CV_MAT_DEPTH(SrcType) == CV_32FC1)
{
if (src_gray.depth() != CV_32FC1 ) src_gray.convertTo(src_gray32f, CV_32FC1);
else src_gray32f = src_gray.clone();
TEST_MESSAGE ("goodFeaturesToTrack 32f\n")
goodFeaturesToTrack( src_gray32f,
corners,
maxCorners,
qualityLevel,
minDistance,
Mat(),
cornersQuality,
blockSize,
gradientSize,
useHarrisDetector,
k );
}
else
{
if (src_gray.depth() != CV_8UC1 ) src_gray.convertTo(src_gray8U, CV_8UC1);
else src_gray8U = src_gray.clone();
TEST_MESSAGE ("goodFeaturesToTrack 8U\n")
goodFeaturesToTrack( src_gray8U,
corners,
maxCorners,
qualityLevel,
minDistance,
Mat(),
cornersQuality,
blockSize,
gradientSize,
useHarrisDetector,
k );
}
}
int CV_GoodFeatureToTTest::validate_test_results( int test_case_idx )
{
static const double eps = 2e-6;
if( CV_MAT_DEPTH(SrcType) == CV_32FC1 )
{
if (src_gray.depth() != CV_32FC1 ) src_gray.convertTo(src_gray32f, CV_32FC1);
else src_gray32f = src_gray.clone();
TEST_MESSAGE ("test_goodFeaturesToTrack 32f\n")
test_goodFeaturesToTrack( src_gray32f,
Refcorners,
maxCorners,
qualityLevel,
minDistance,
Mat(),
RefcornersQuality,
blockSize,
gradientSize,
useHarrisDetector,
k );
}
else
{
if (src_gray.depth() != CV_8UC1 ) src_gray.convertTo(src_gray8U, CV_8UC1);
else src_gray8U = src_gray.clone();
TEST_MESSAGE ("test_goodFeaturesToTrack 8U\n")
test_goodFeaturesToTrack( src_gray8U,
Refcorners,
maxCorners,
qualityLevel,
minDistance,
Mat(),
RefcornersQuality,
blockSize,
gradientSize,
useHarrisDetector,
k );
}
double e = cv::norm(corners, Refcorners); // TODO cvtest
if (e > eps)
{
TEST_MESSAGEL ("Number of features: Refcorners = ", Refcorners.size())
TEST_MESSAGEL (" TestCorners = ", corners.size())
TEST_MESSAGE ("\n")
EXPECT_LE(e, eps); // never true
ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY);
for(int i = 0; i < (int)std::min((unsigned int)(corners.size()), (unsigned int)(Refcorners.size())); i++){
if ( (corners[i].x != Refcorners[i].x) || (corners[i].y != Refcorners[i].y))
printf("i = %i X %2.2f Xref %2.2f Y %2.2f Yref %2.2f\n",i,corners[i].x,Refcorners[i].x,corners[i].y,Refcorners[i].y);
}
}
else
{
TEST_MESSAGEL (" Refcorners = ", Refcorners.size())
TEST_MESSAGEL (" TestCorners = ", corners.size())
TEST_MESSAGE ("\n")
ts->set_failed_test_info(cvtest::TS::OK);
}
e = cv::norm(cornersQuality, RefcornersQuality, NORM_RELATIVE | NORM_INF);
if (e > eps)
{
EXPECT_LE(e, eps); // never true
ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY);
int min_size = (int)std::min(cornersQuality.size(), RefcornersQuality.size());
for(int i = 0; i < min_size; i++) {
if (std::abs(cornersQuality[i] - RefcornersQuality[i]) > eps * std::max(cornersQuality[i], RefcornersQuality[i]))
printf("i = %i Quality %2.6f Quality ref %2.6f\n", i, cornersQuality[i], RefcornersQuality[i]);
}
}
return BaseTest::validate_test_results(test_case_idx);
}
TEST(Imgproc_GoodFeatureToT, accuracy) { CV_GoodFeatureToTTest test; test.safe_run(); }
TEST(Imgproc_GoodFeatureToT, mask)
{
Mat gray = imread(cvtest::findDataFile("shared/baboon.png"), IMREAD_GRAYSCALE);
ASSERT_FALSE(gray.empty());
cv::Rect roi(gray.cols/4, gray.rows/4, gray.cols/2, gray.rows/2);
Mat mask = Mat::zeros(gray.size(), CV_8UC1);
mask(roi).setTo(255);
Mat mask_bool = Mat::zeros(gray.size(), CV_BoolC1);
mask_bool(roi).setTo(1);
Mat gray_roi = gray(roi);
Mat corners_mask, corners_bool, corners_ref;
vector<float> ref_quality;
test_goodFeaturesToTrack(gray, corners_ref, 100, 0.3, 3, mask, ref_quality, 3, 3, false, 0.04);
cv::goodFeaturesToTrack(gray, corners_mask, 100, 0.3, 3, mask);
cv::goodFeaturesToTrack(gray, corners_bool, 100, 0.3, 3, mask_bool);
EXPECT_MAT_NEAR(corners_mask, corners_ref.t(), 0.0);
EXPECT_MAT_NEAR(corners_mask, corners_bool, 0.0);
}
}} // namespace
/* End of file. */
@@ -0,0 +1,85 @@
// 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_TEST_INVARIANCE_UTILS_HPP__
#define __OPENCV_TEST_INVARIANCE_UTILS_HPP__
namespace opencv_test { namespace {
Mat generateHomography(float angle)
{
// angle - rotation around Oz in degrees
float angleRadian = static_cast<float>(angle * CV_PI / 180);
Mat H = Mat::eye(3, 3, CV_32FC1);
H.at<float>(0,0) = H.at<float>(1,1) = std::cos(angleRadian);
H.at<float>(0,1) = -std::sin(angleRadian);
H.at<float>(1,0) = std::sin(angleRadian);
return H;
}
Mat rotateImage(const Mat& srcImage, const Mat& srcMask, float angle, Mat& dstImage, Mat& dstMask)
{
// angle - rotation around Oz in degrees
float diag = std::sqrt(static_cast<float>(srcImage.cols * srcImage.cols + srcImage.rows * srcImage.rows));
Mat LUShift = Mat::eye(3, 3, CV_32FC1); // left up
LUShift.at<float>(0,2) = static_cast<float>(-srcImage.cols/2);
LUShift.at<float>(1,2) = static_cast<float>(-srcImage.rows/2);
Mat RDShift = Mat::eye(3, 3, CV_32FC1); // right down
RDShift.at<float>(0,2) = diag/2;
RDShift.at<float>(1,2) = diag/2;
Size sz(cvRound(diag), cvRound(diag));
Mat H = RDShift * generateHomography(angle) * LUShift;
warpPerspective(srcImage, dstImage, H, sz);
warpPerspective(srcMask, dstMask, H, sz);
return H;
}
float calcCirclesIntersectArea(const Point2f& p0, float r0, const Point2f& p1, float r1)
{
float c = static_cast<float>(cv::norm(p0 - p1)), sqr_c = c * c;
float sqr_r0 = r0 * r0;
float sqr_r1 = r1 * r1;
if(r0 + r1 <= c)
return 0;
float minR = std::min(r0, r1);
float maxR = std::max(r0, r1);
if(c + minR <= maxR)
return static_cast<float>(CV_PI * minR * minR);
float cos_halfA0 = (sqr_r0 + sqr_c - sqr_r1) / (2 * r0 * c);
float cos_halfA1 = (sqr_r1 + sqr_c - sqr_r0) / (2 * r1 * c);
float A0 = 2 * acos(cos_halfA0);
float A1 = 2 * acos(cos_halfA1);
return 0.5f * sqr_r0 * (A0 - sin(A0)) +
0.5f * sqr_r1 * (A1 - sin(A1));
}
float calcIntersectRatio(const Point2f& p0, float r0, const Point2f& p1, float r1)
{
float intersectArea = calcCirclesIntersectArea(p0, r0, p1, r1);
float unionArea = static_cast<float>(CV_PI) * (r0 * r0 + r1 * r1) - intersectArea;
return intersectArea / unionArea;
}
void scaleKeyPoints(const vector<KeyPoint>& src, vector<KeyPoint>& dst, float scale)
{
dst.resize(src.size());
for (size_t i = 0; i < src.size(); i++) {
dst[i] = src[i];
dst[i].pt.x = dst[i].pt.x * scale + (scale - 1.0f) / 2.0f;
dst[i].pt.y = dst[i].pt.y * scale + (scale - 1.0f) / 2.0f;
dst[i].size *= scale;
}
}
}} // namespace
#endif // __OPENCV_TEST_INVARIANCE_UTILS_HPP__
+158
View File
@@ -0,0 +1,158 @@
/*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.
//
//
// Intel License Agreement
// For Open Source Computer Vision Library
//
// Copyright (C) 2000, 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 Intel Corporation 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 {
const string FEATURES2D_DIR = "features2d";
const string IMAGE_FILENAME = "tsukuba.png";
/****************************************************************************************\
* Test for KeyPoint *
\****************************************************************************************/
class CV_FeatureDetectorKeypointsTest : public cvtest::BaseTest
{
public:
CV_FeatureDetectorKeypointsTest(const Ptr<FeatureDetector>& _detector) :
detector(_detector) {}
protected:
virtual void run(int)
{
CV_Assert(detector);
string imgFilename = string(ts->get_data_path()) + FEATURES2D_DIR + "/" + IMAGE_FILENAME;
// Read the test image.
Mat image = imread(imgFilename);
if(image.empty())
{
ts->printf(cvtest::TS::LOG, "Image %s can not be read.\n", imgFilename.c_str());
ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_TEST_DATA);
return;
}
vector<KeyPoint> keypoints;
detector->detect(image, keypoints);
if(keypoints.empty())
{
ts->printf(cvtest::TS::LOG, "Detector can't find keypoints in image.\n");
ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_OUTPUT);
return;
}
Rect r(0, 0, image.cols, image.rows);
for(size_t i = 0; i < keypoints.size(); i++)
{
const KeyPoint& kp = keypoints[i];
if(!r.contains(kp.pt))
{
ts->printf(cvtest::TS::LOG, "KeyPoint::pt is out of image (x=%f, y=%f).\n", kp.pt.x, kp.pt.y);
ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_OUTPUT);
return;
}
if(kp.size <= 0.f)
{
ts->printf(cvtest::TS::LOG, "KeyPoint::size is not positive (%f).\n", kp.size);
ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_OUTPUT);
return;
}
if((kp.angle < 0.f && kp.angle != -1.f) || kp.angle >= 360.f)
{
ts->printf(cvtest::TS::LOG, "KeyPoint::angle is out of range [0, 360). It's %f.\n", kp.angle);
ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_OUTPUT);
return;
}
}
ts->set_failed_test_info(cvtest::TS::OK);
}
Ptr<FeatureDetector> detector;
};
// Registration of tests
TEST(Features2d_Detector_Keypoints_FAST, validation)
{
CV_FeatureDetectorKeypointsTest test(FastFeatureDetector::create());
test.safe_run();
}
TEST(Features2d_Detector_Keypoints_HARRIS, validation)
{
CV_FeatureDetectorKeypointsTest test(GFTTDetector::create(1000, 0.01, 1, 3, 3, true, 0.04));
test.safe_run();
}
TEST(Features2d_Detector_Keypoints_GFTT, validation)
{
Ptr<GFTTDetector> gftt = GFTTDetector::create();
gftt->setHarrisDetector(true);
CV_FeatureDetectorKeypointsTest test(gftt);
test.safe_run();
}
TEST(Features2d_Detector_Keypoints_MSER, validation)
{
CV_FeatureDetectorKeypointsTest test(MSER::create());
test.safe_run();
}
TEST(Features2d_Detector_Keypoints_ORB, validation)
{
CV_FeatureDetectorKeypointsTest test(ORB::create());
test.safe_run();
}
TEST(Features2d_Detector_Keypoints_SIFT, validation)
{
CV_FeatureDetectorKeypointsTest test(SIFT::create());
test.safe_run();
}
}} // namespace
+18
View File
@@ -0,0 +1,18 @@
// 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"
#if defined(HAVE_HPX)
#include <hpx/hpx_main.hpp>
#endif
static
void initTests()
{
cvtest::addDataSearchEnv("OPENCV_DNN_TEST_DATA_PATH");
cvtest::addDataSearchSubDirectory(""); // override "cv" prefix below to access without "../dnn" hacks
cvtest::addDataSearchEnv("OPENCV_DNN_TEST_DATA_PATH");
}
CV_TEST_MAIN("cv", initTests())

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