mirror of
https://github.com/opencv/opencv.git
synced 2026-07-29 23:33:05 +04:00
Merge pull request #26405 from kaingwade:rename_features2d
Rename features2d #26405 This PR renames the module _features2d_ to _features_ as one of the Big OpenCV Cleanup #25007. Related PR: opencv/opencv_contrib: [#3820](https://github.com/opencv/opencv_contrib/pull/3820) opencv/ci-gha-workflow: [#192](https://github.com/opencv/ci-gha-workflow/pull/192)
This commit is contained in:
+13
@@ -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.
|
||||
+1597
File diff suppressed because it is too large
Load Diff
+242
@@ -0,0 +1,242 @@
|
||||
|
||||
// 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 <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;
|
||||
}
|
||||
|
||||
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
@@ -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 };
|
||||
@@ -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.
|
||||
@@ -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 ${debug_modules} OPTIONAL opencv_flann 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"
|
||||
@@ -0,0 +1 @@
|
||||
include/opencv2/features.hpp
|
||||
@@ -0,0 +1 @@
|
||||
misc/java/src/cpp/features_converters.hpp
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.0\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.0\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.0\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.0\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.0\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.0\n---\nname: \"Feature2D.FastFeatureDetector\"\nthreshold: 10\nnonmaxSuppression: 1\ntype: 2\n";
|
||||
String data = readFile(filename);
|
||||
|
||||
assertEquals(truth, data);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
package org.opencv.test.features;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.opencv.cv3d.Cv3d;
|
||||
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.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.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 = Cv3d.findHomography(points1, points2, Cv3d.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);
|
||||
}
|
||||
}
|
||||
@@ -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.0\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.0\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.0\n---\nname: \"Feature2D.GFTTDetector\"\nnfeatures: 1000\nqualityLevel: 0.01\nminDistance: 1.\nblockSize: 3\ngradSize: 3\nuseHarrisDetector: 0\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.0\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: 0\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.0\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.0\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,139 @@
|
||||
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());
|
||||
// FIXME: blobColor field has uchar type in C++ and cannot be automatically wrapped to Java as it does not support unsigned types
|
||||
//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));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"whitelist":
|
||||
{
|
||||
"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"]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"ManualFuncs" : {
|
||||
"SimpleBlobDetector": {
|
||||
"setParams": { "declaration" : [""], "implementation" : [""] },
|
||||
"getParams": { "declaration" : [""], "implementation" : [""] }
|
||||
}
|
||||
},
|
||||
"enum_fix" : {
|
||||
"FastFeatureDetector" : { "DetectorType": "FastDetectorType" }
|
||||
},
|
||||
"func_arg_fix" : {
|
||||
"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,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,129 @@
|
||||
/*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
|
||||
} // 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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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,7 @@
|
||||
#include "perf_precomp.hpp"
|
||||
|
||||
#if defined(HAVE_HPX)
|
||||
#include <hpx/hpx_main.hpp>
|
||||
#endif
|
||||
|
||||
CV_PERF_TEST_MAIN(features2d)
|
||||
@@ -0,0 +1,7 @@
|
||||
#ifndef __OPENCV_PERF_PRECOMP_HPP__
|
||||
#define __OPENCV_PERF_PRECOMP_HPP__
|
||||
|
||||
#include "opencv2/ts.hpp"
|
||||
#include "opencv2/features.hpp"
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,360 @@
|
||||
// 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
|
||||
@@ -0,0 +1,264 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html
|
||||
|
||||
#include "precomp.hpp"
|
||||
#include "../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::AnnoyIndexSingleThreadedBuildPolicy>>(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::AnnoyIndexSingleThreadedBuildPolicy>> 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");
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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 ¶meters = 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> ¢ers,
|
||||
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 ¶meters) :
|
||||
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> ¢ers,
|
||||
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");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
|
||||
}
|
||||
@@ -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( int i = 0; it != keypoints.end(); ++it, i++ )
|
||||
{
|
||||
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;
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,557 @@
|
||||
/* 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];
|
||||
for (int _k = 0; _k < 25; _k++)
|
||||
d[_k] = (short)(ptr[k] - ptr[k + pixel[_k]]);
|
||||
|
||||
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();
|
||||
|
||||
CV_OCL_RUN(_img.isUMat() && type == FastFeatureDetector::TYPE_9_16,
|
||||
ocl_FAST(_img, keypoints, threshold, nonmax_suppression, 10000));
|
||||
|
||||
cv::Mat img = _img.getMat();
|
||||
CALL_HAL(fast_dense, hal_FAST, img, keypoints, threshold, nonmax_suppression, type);
|
||||
|
||||
size_t 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");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
/*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; }
|
||||
|
||||
//! @cond IGNORED
|
||||
#define cv_hal_FAST hal_ni_FAST
|
||||
//! @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
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,560 @@
|
||||
/*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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -0,0 +1,56 @@
|
||||
/*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/core/utility.hpp"
|
||||
#include "opencv2/core/private.hpp"
|
||||
#include "opencv2/core/ocl.hpp"
|
||||
#include "opencv2/core/hal/hal.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#endif
|
||||
@@ -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. Asignee: 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 = std::pow(k, (double)(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
@@ -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
|
||||
@@ -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,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
|
||||
@@ -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
|
||||
@@ -0,0 +1,138 @@
|
||||
/*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(); }
|
||||
|
||||
}} // namespace
|
||||
@@ -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__
|
||||
@@ -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
|
||||
@@ -0,0 +1,10 @@
|
||||
// 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
|
||||
|
||||
CV_TEST_MAIN("cv")
|
||||
@@ -0,0 +1,635 @@
|
||||
/*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";
|
||||
|
||||
/****************************************************************************************\
|
||||
* Algorithmic tests for descriptor matchers *
|
||||
\****************************************************************************************/
|
||||
class CV_DescriptorMatcherTest : public cvtest::BaseTest
|
||||
{
|
||||
public:
|
||||
CV_DescriptorMatcherTest( const string& _name, const Ptr<DescriptorMatcher>& _dmatcher, float _badPart ) :
|
||||
badPart(_badPart), name(_name), dmatcher(_dmatcher)
|
||||
{}
|
||||
protected:
|
||||
static const int dim = 500;
|
||||
static const int queryDescCount = 300; // must be even number because we split train data in some cases in two
|
||||
static const int countFactor = 4; // do not change it
|
||||
const float badPart;
|
||||
|
||||
virtual void run( int );
|
||||
void generateData( Mat& query, Mat& train );
|
||||
|
||||
#if 0
|
||||
void emptyDataTest(); // FIXIT not used
|
||||
#endif
|
||||
void matchTest( const Mat& query, const Mat& train );
|
||||
void knnMatchTest( const Mat& query, const Mat& train );
|
||||
void radiusMatchTest( const Mat& query, const Mat& train );
|
||||
|
||||
string name;
|
||||
Ptr<DescriptorMatcher> dmatcher;
|
||||
|
||||
private:
|
||||
CV_DescriptorMatcherTest& operator=(const CV_DescriptorMatcherTest&) { return *this; }
|
||||
};
|
||||
|
||||
#if 0
|
||||
void CV_DescriptorMatcherTest::emptyDataTest()
|
||||
{
|
||||
assert( !dmatcher.empty() );
|
||||
Mat queryDescriptors, trainDescriptors, mask;
|
||||
vector<Mat> trainDescriptorCollection, masks;
|
||||
vector<DMatch> matches;
|
||||
vector<vector<DMatch> > vmatches;
|
||||
|
||||
try
|
||||
{
|
||||
dmatcher->match( queryDescriptors, trainDescriptors, matches, mask );
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
ts->printf( cvtest::TS::LOG, "match() on empty descriptors must not generate exception (1).\n" );
|
||||
ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
dmatcher->knnMatch( queryDescriptors, trainDescriptors, vmatches, 2, mask );
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
ts->printf( cvtest::TS::LOG, "knnMatch() on empty descriptors must not generate exception (1).\n" );
|
||||
ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
dmatcher->radiusMatch( queryDescriptors, trainDescriptors, vmatches, 10.f, mask );
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
ts->printf( cvtest::TS::LOG, "radiusMatch() on empty descriptors must not generate exception (1).\n" );
|
||||
ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
dmatcher->add( trainDescriptorCollection );
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
ts->printf( cvtest::TS::LOG, "add() on empty descriptors must not generate exception.\n" );
|
||||
ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
dmatcher->match( queryDescriptors, matches, masks );
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
ts->printf( cvtest::TS::LOG, "match() on empty descriptors must not generate exception (2).\n" );
|
||||
ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
dmatcher->knnMatch( queryDescriptors, vmatches, 2, masks );
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
ts->printf( cvtest::TS::LOG, "knnMatch() on empty descriptors must not generate exception (2).\n" );
|
||||
ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
dmatcher->radiusMatch( queryDescriptors, vmatches, 10.f, masks );
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
ts->printf( cvtest::TS::LOG, "radiusMatch() on empty descriptors must not generate exception (2).\n" );
|
||||
ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
void CV_DescriptorMatcherTest::generateData( Mat& query, Mat& train )
|
||||
{
|
||||
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, 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).
|
||||
train.create( query.rows*countFactor, query.cols, CV_32FC1 );
|
||||
float step = 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.at<float>(0, elem) += diff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CV_DescriptorMatcherTest::matchTest( const Mat& query, const Mat& train )
|
||||
{
|
||||
dmatcher->clear();
|
||||
|
||||
// test const version of match()
|
||||
{
|
||||
vector<DMatch> matches;
|
||||
dmatcher->match( query, train, matches );
|
||||
|
||||
if( (int)matches.size() != queryDescCount )
|
||||
{
|
||||
ts->printf(cvtest::TS::LOG, "Incorrect matches count while test match() function (1).\n");
|
||||
ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
|
||||
}
|
||||
else
|
||||
{
|
||||
int badCount = 0;
|
||||
for( size_t i = 0; i < matches.size(); i++ )
|
||||
{
|
||||
DMatch& match = matches[i];
|
||||
if( (match.queryIdx != (int)i) || (match.trainIdx != (int)i*countFactor) || (match.imgIdx != 0) )
|
||||
badCount++;
|
||||
}
|
||||
if( (float)badCount > (float)queryDescCount*badPart )
|
||||
{
|
||||
ts->printf( cvtest::TS::LOG, "%f - too large bad matches part while test match() function (1).\n",
|
||||
(float)badCount/(float)queryDescCount );
|
||||
ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// test const version of match() for the same query and test descriptors
|
||||
{
|
||||
vector<DMatch> matches;
|
||||
dmatcher->match( query, query, matches );
|
||||
|
||||
if( (int)matches.size() != query.rows )
|
||||
{
|
||||
ts->printf(cvtest::TS::LOG, "Incorrect matches count while test match() function for the same query and test descriptors (1).\n");
|
||||
ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
|
||||
}
|
||||
else
|
||||
{
|
||||
for( size_t i = 0; i < matches.size(); i++ )
|
||||
{
|
||||
DMatch& match = matches[i];
|
||||
//std::cout << match.distance << std::endl;
|
||||
|
||||
if( match.queryIdx != (int)i || match.trainIdx != (int)i || std::abs(match.distance) > FLT_EPSILON )
|
||||
{
|
||||
ts->printf( cvtest::TS::LOG, "Bad match (i=%d, queryIdx=%d, trainIdx=%d, distance=%f) while test match() function for the same query and test descriptors (1).\n",
|
||||
i, match.queryIdx, match.trainIdx, match.distance );
|
||||
ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// test version of match() with add()
|
||||
{
|
||||
vector<DMatch> matches;
|
||||
// make add() twice to test such case
|
||||
dmatcher->add( vector<Mat>(1,train.rowRange(0, train.rows/2)) );
|
||||
dmatcher->add( vector<Mat>(1,train.rowRange(train.rows/2, train.rows)) );
|
||||
// prepare masks (make first nearest match illegal)
|
||||
vector<Mat> masks(2);
|
||||
for(int mi = 0; mi < 2; mi++ )
|
||||
{
|
||||
masks[mi] = Mat(query.rows, train.rows/2, CV_8UC1, Scalar::all(1));
|
||||
for( int di = 0; di < queryDescCount/2; di++ )
|
||||
masks[mi].col(di*countFactor).setTo(Scalar::all(0));
|
||||
}
|
||||
|
||||
dmatcher->match( query, matches, masks );
|
||||
|
||||
if( (int)matches.size() != queryDescCount )
|
||||
{
|
||||
ts->printf(cvtest::TS::LOG, "Incorrect matches count while test match() function (2).\n");
|
||||
ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
|
||||
}
|
||||
else
|
||||
{
|
||||
int badCount = 0;
|
||||
for( size_t i = 0; i < matches.size(); i++ )
|
||||
{
|
||||
DMatch& match = matches[i];
|
||||
int shift = dmatcher->isMaskSupported() ? 1 : 0;
|
||||
{
|
||||
if( i < queryDescCount/2 )
|
||||
{
|
||||
if( (match.queryIdx != (int)i) || (match.trainIdx != (int)i*countFactor + shift) || (match.imgIdx != 0) )
|
||||
badCount++;
|
||||
}
|
||||
else
|
||||
{
|
||||
if( (match.queryIdx != (int)i) || (match.trainIdx != ((int)i-queryDescCount/2)*countFactor + shift) || (match.imgIdx != 1) )
|
||||
badCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if( (float)badCount > (float)queryDescCount*badPart )
|
||||
{
|
||||
ts->printf( cvtest::TS::LOG, "%f - too large bad matches part while test match() function (2).\n",
|
||||
(float)badCount/(float)queryDescCount );
|
||||
ts->set_failed_test_info( cvtest::TS::FAIL_BAD_ACCURACY );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CV_DescriptorMatcherTest::knnMatchTest( const Mat& query, const Mat& train )
|
||||
{
|
||||
dmatcher->clear();
|
||||
|
||||
// test const version of knnMatch()
|
||||
{
|
||||
const int knn = 3;
|
||||
|
||||
vector<vector<DMatch> > matches;
|
||||
dmatcher->knnMatch( query, train, matches, knn );
|
||||
|
||||
if( (int)matches.size() != queryDescCount )
|
||||
{
|
||||
ts->printf(cvtest::TS::LOG, "Incorrect matches count while test knnMatch() function (1).\n");
|
||||
ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
|
||||
}
|
||||
else
|
||||
{
|
||||
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++ )
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
if( (float)badCount > (float)queryDescCount*badPart )
|
||||
{
|
||||
ts->printf( cvtest::TS::LOG, "%f - too large bad matches part while test knnMatch() function (1).\n",
|
||||
(float)badCount/(float)queryDescCount );
|
||||
ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// test version of knnMatch() with add()
|
||||
{
|
||||
const int knn = 2;
|
||||
vector<vector<DMatch> > matches;
|
||||
// make add() twice to test such case
|
||||
dmatcher->add( vector<Mat>(1,train.rowRange(0, train.rows/2)) );
|
||||
dmatcher->add( vector<Mat>(1,train.rowRange(train.rows/2, train.rows)) );
|
||||
// prepare masks (make first nearest match illegal)
|
||||
vector<Mat> masks(2);
|
||||
for(int mi = 0; mi < 2; mi++ )
|
||||
{
|
||||
masks[mi] = Mat(query.rows, train.rows/2, CV_8UC1, Scalar::all(1));
|
||||
for( int di = 0; di < queryDescCount/2; di++ )
|
||||
masks[mi].col(di*countFactor).setTo(Scalar::all(0));
|
||||
}
|
||||
|
||||
dmatcher->knnMatch( query, matches, knn, masks );
|
||||
|
||||
if( (int)matches.size() != queryDescCount )
|
||||
{
|
||||
ts->printf(cvtest::TS::LOG, "Incorrect matches count while test knnMatch() function (2).\n");
|
||||
ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
|
||||
}
|
||||
else
|
||||
{
|
||||
int badCount = 0;
|
||||
int shift = dmatcher->isMaskSupported() ? 1 : 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++ )
|
||||
{
|
||||
DMatch& match = matches[i][k];
|
||||
{
|
||||
if( i < queryDescCount/2 )
|
||||
{
|
||||
if( (match.queryIdx != (int)i) || (match.trainIdx != (int)i*countFactor + k + shift) ||
|
||||
(match.imgIdx != 0) )
|
||||
localBadCount++;
|
||||
}
|
||||
else
|
||||
{
|
||||
if( (match.queryIdx != (int)i) || (match.trainIdx != ((int)i-queryDescCount/2)*countFactor + k + shift) ||
|
||||
(match.imgIdx != 1) )
|
||||
localBadCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
badCount += localBadCount > 0 ? 1 : 0;
|
||||
}
|
||||
}
|
||||
if( (float)badCount > (float)queryDescCount*badPart )
|
||||
{
|
||||
ts->printf( cvtest::TS::LOG, "%f - too large bad matches part while test knnMatch() function (2).\n",
|
||||
(float)badCount/(float)queryDescCount );
|
||||
ts->set_failed_test_info( cvtest::TS::FAIL_BAD_ACCURACY );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CV_DescriptorMatcherTest::radiusMatchTest( const Mat& query, const Mat& train )
|
||||
{
|
||||
dmatcher->clear();
|
||||
// test const version of match()
|
||||
{
|
||||
const float radius = 1.f/countFactor;
|
||||
vector<vector<DMatch> > matches;
|
||||
dmatcher->radiusMatch( query, train, matches, radius );
|
||||
|
||||
if( (int)matches.size() != queryDescCount )
|
||||
{
|
||||
ts->printf(cvtest::TS::LOG, "Incorrect matches count while test radiusMatch() function (1).\n");
|
||||
ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
|
||||
}
|
||||
else
|
||||
{
|
||||
int badCount = 0;
|
||||
for( size_t i = 0; i < matches.size(); i++ )
|
||||
{
|
||||
if( (int)matches[i].size() != 1 )
|
||||
badCount++;
|
||||
else
|
||||
{
|
||||
DMatch& match = matches[i][0];
|
||||
if( (match.queryIdx != (int)i) || (match.trainIdx != (int)i*countFactor) || (match.imgIdx != 0) )
|
||||
badCount++;
|
||||
}
|
||||
}
|
||||
if( (float)badCount > (float)queryDescCount*badPart )
|
||||
{
|
||||
ts->printf( cvtest::TS::LOG, "%f - too large bad matches part while test radiusMatch() function (1).\n",
|
||||
(float)badCount/(float)queryDescCount );
|
||||
ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// test version of match() with add()
|
||||
{
|
||||
int n = 3;
|
||||
const float radius = 1.f/countFactor * n;
|
||||
vector<vector<DMatch> > matches;
|
||||
// make add() twice to test such case
|
||||
dmatcher->add( vector<Mat>(1,train.rowRange(0, train.rows/2)) );
|
||||
dmatcher->add( vector<Mat>(1,train.rowRange(train.rows/2, train.rows)) );
|
||||
// prepare masks (make first nearest match illegal)
|
||||
vector<Mat> masks(2);
|
||||
for(int mi = 0; mi < 2; mi++ )
|
||||
{
|
||||
masks[mi] = Mat(query.rows, train.rows/2, CV_8UC1, Scalar::all(1));
|
||||
for( int di = 0; di < queryDescCount/2; di++ )
|
||||
masks[mi].col(di*countFactor).setTo(Scalar::all(0));
|
||||
}
|
||||
|
||||
dmatcher->radiusMatch( query, matches, radius, masks );
|
||||
|
||||
//int curRes = cvtest::TS::OK;
|
||||
if( (int)matches.size() != queryDescCount )
|
||||
{
|
||||
ts->printf(cvtest::TS::LOG, "Incorrect matches count while test radiusMatch() function (1).\n");
|
||||
ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT );
|
||||
}
|
||||
|
||||
int badCount = 0;
|
||||
int shift = dmatcher->isMaskSupported() ? 1 : 0;
|
||||
int needMatchCount = dmatcher->isMaskSupported() ? n-1 : n;
|
||||
for( size_t i = 0; i < matches.size(); i++ )
|
||||
{
|
||||
if( (int)matches[i].size() != needMatchCount )
|
||||
badCount++;
|
||||
else
|
||||
{
|
||||
int localBadCount = 0;
|
||||
for( int k = 0; k < needMatchCount; k++ )
|
||||
{
|
||||
DMatch& match = matches[i][k];
|
||||
{
|
||||
if( i < queryDescCount/2 )
|
||||
{
|
||||
if( (match.queryIdx != (int)i) || (match.trainIdx != (int)i*countFactor + k + shift) ||
|
||||
(match.imgIdx != 0) )
|
||||
localBadCount++;
|
||||
}
|
||||
else
|
||||
{
|
||||
if( (match.queryIdx != (int)i) || (match.trainIdx != ((int)i-queryDescCount/2)*countFactor + k + shift) ||
|
||||
(match.imgIdx != 1) )
|
||||
localBadCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
badCount += localBadCount > 0 ? 1 : 0;
|
||||
}
|
||||
}
|
||||
if( (float)badCount > (float)queryDescCount*badPart )
|
||||
{
|
||||
//curRes = cvtest::TS::FAIL_INVALID_OUTPUT;
|
||||
ts->printf( cvtest::TS::LOG, "%f - too large bad matches part while test radiusMatch() function (2).\n",
|
||||
(float)badCount/(float)queryDescCount );
|
||||
ts->set_failed_test_info( cvtest::TS::FAIL_BAD_ACCURACY );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CV_DescriptorMatcherTest::run( int )
|
||||
{
|
||||
Mat query, train;
|
||||
generateData( query, train );
|
||||
|
||||
matchTest( query, train );
|
||||
|
||||
knnMatchTest( query, train );
|
||||
|
||||
radiusMatchTest( query, train );
|
||||
}
|
||||
|
||||
/****************************************************************************************\
|
||||
* Tests registrations *
|
||||
\****************************************************************************************/
|
||||
|
||||
TEST( Features2d_DescriptorMatcher_BruteForce, regression )
|
||||
{
|
||||
CV_DescriptorMatcherTest test( "descriptor-matcher-brute-force",
|
||||
DescriptorMatcher::create("BruteForce"), 0.01f );
|
||||
test.safe_run();
|
||||
}
|
||||
|
||||
#ifdef HAVE_OPENCV_FLANN
|
||||
TEST( Features2d_DescriptorMatcher_FlannBased, regression )
|
||||
{
|
||||
CV_DescriptorMatcherTest test( "descriptor-matcher-flann-based",
|
||||
DescriptorMatcher::create("FlannBased"), 0.04f );
|
||||
test.safe_run();
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST( Features2d_DMatch, read_write )
|
||||
{
|
||||
FileStorage fs(".xml", FileStorage::WRITE + FileStorage::MEMORY);
|
||||
vector<DMatch> matches;
|
||||
matches.push_back(DMatch(1,2,3,4.5f));
|
||||
fs << "Match" << matches;
|
||||
String str = fs.releaseAndGetString();
|
||||
ASSERT_NE( strstr(str.c_str(), "4.5"), (char*)0 );
|
||||
}
|
||||
|
||||
#ifdef HAVE_OPENCV_FLANN
|
||||
TEST( Features2d_FlannBasedMatcher, read_write )
|
||||
{
|
||||
static const char* ymlfile = "%YAML:1.0\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: 0\n"
|
||||
" -\n"
|
||||
" name: sorted\n"
|
||||
" type: 8\n" // FLANN_INDEX_TYPE_BOOL
|
||||
" value: 1\n";
|
||||
|
||||
Ptr<DescriptorMatcher> matcher = FlannBasedMatcher::create();
|
||||
FileStorage fs_in(ymlfile, FileStorage::READ + FileStorage::MEMORY);
|
||||
matcher->read(fs_in.root());
|
||||
FileStorage fs_out(".yml", FileStorage::WRITE + FileStorage::MEMORY);
|
||||
matcher->write(fs_out);
|
||||
std::string out = fs_out.releaseAndGetString();
|
||||
|
||||
EXPECT_EQ(ymlfile, out);
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST(Features2d_DMatch, issue_11855)
|
||||
{
|
||||
Mat sources = (Mat_<uchar>(2, 3) << 1, 1, 0,
|
||||
1, 1, 1);
|
||||
Mat targets = (Mat_<uchar>(2, 3) << 1, 1, 1,
|
||||
0, 0, 0);
|
||||
Ptr<BFMatcher> bf = BFMatcher::create(NORM_HAMMING, true);
|
||||
vector<vector<DMatch> > match;
|
||||
bf->knnMatch(sources, targets, match, 1, noArray(), true);
|
||||
|
||||
ASSERT_EQ((size_t)1, match.size());
|
||||
ASSERT_EQ((size_t)1, match[0].size());
|
||||
EXPECT_EQ(1, match[0][0].queryIdx);
|
||||
EXPECT_EQ(0, match[0][0].trainIdx);
|
||||
EXPECT_EQ(0.0f, match[0][0].distance);
|
||||
}
|
||||
|
||||
TEST(Features2d_DMatch, issue_17771)
|
||||
{
|
||||
Mat sources = (Mat_<uchar>(2, 3) << 1, 1, 0,
|
||||
1, 1, 1);
|
||||
Mat targets = (Mat_<uchar>(2, 3) << 1, 1, 1,
|
||||
0, 0, 0);
|
||||
UMat usources = sources.getUMat(ACCESS_READ);
|
||||
UMat utargets = targets.getUMat(ACCESS_READ);
|
||||
vector<vector<DMatch> > match;
|
||||
Ptr<BFMatcher> ubf = BFMatcher::create(NORM_HAMMING);
|
||||
Mat mask = (Mat_<uchar>(2, 2) << 1, 0, 0, 1);
|
||||
EXPECT_NO_THROW(ubf->knnMatch(usources, utargets, match, 1, mask, true));
|
||||
}
|
||||
|
||||
}} // namespace
|
||||
@@ -0,0 +1,208 @@
|
||||
/*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 {
|
||||
|
||||
#undef RENDER_MSERS
|
||||
#define RENDER_MSERS 0
|
||||
|
||||
#if defined RENDER_MSERS && RENDER_MSERS
|
||||
static void renderMSERs(const Mat& gray, Mat& img, const vector<vector<Point> >& msers)
|
||||
{
|
||||
cvtColor(gray, img, COLOR_GRAY2BGR);
|
||||
RNG rng((uint64)1749583);
|
||||
for( int i = 0; i < (int)msers.size(); i++ )
|
||||
{
|
||||
uchar b = rng.uniform(0, 256);
|
||||
uchar g = rng.uniform(0, 256);
|
||||
uchar r = rng.uniform(0, 256);
|
||||
Vec3b color(b, g, r);
|
||||
|
||||
const Point* pt = &msers[i][0];
|
||||
size_t j, n = msers[i].size();
|
||||
for( j = 0; j < n; j++ )
|
||||
img.at<Vec3b>(pt[j]) = color;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST(Features2d_MSER, cases)
|
||||
{
|
||||
uchar buf[] =
|
||||
{
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255
|
||||
};
|
||||
Mat big_image = imread(cvtest::TS::ptr()->get_data_path() + "mser/puzzle.png", 0);
|
||||
Mat small_image(14, 26, CV_8U, buf);
|
||||
static const int thresharr[] = { 0, 70, 120, 180, 255 };
|
||||
|
||||
const int kDelta = 5;
|
||||
Ptr<MSER> mserExtractor = MSER::create( kDelta );
|
||||
vector<vector<Point> > msers;
|
||||
vector<Rect> boxes;
|
||||
|
||||
RNG rng((uint64)123456);
|
||||
|
||||
for( int i = 0; i < 100; i++ )
|
||||
{
|
||||
bool use_big_image = rng.uniform(0, 7) != 0;
|
||||
bool invert = rng.uniform(0, 2) != 0;
|
||||
bool binarize = use_big_image ? rng.uniform(0, 5) != 0 : false;
|
||||
bool blur = rng.uniform(0, 2) != 0;
|
||||
int thresh = thresharr[rng.uniform(0, 5)];
|
||||
|
||||
/*if( i == 0 )
|
||||
{
|
||||
use_big_image = true;
|
||||
invert = binarize = blur = false;
|
||||
}*/
|
||||
|
||||
const Mat& src0 = use_big_image ? big_image : small_image;
|
||||
Mat src = src0.clone();
|
||||
|
||||
int kMinArea = use_big_image ? 256 : 10;
|
||||
int kMaxArea = (int)src.total()/4;
|
||||
|
||||
mserExtractor->setMinArea(kMinArea);
|
||||
mserExtractor->setMaxArea(kMaxArea);
|
||||
mserExtractor->setMinDiversity(0);
|
||||
|
||||
if( invert )
|
||||
bitwise_not(src, src);
|
||||
if( binarize )
|
||||
cv::threshold(src, src, thresh, 255, THRESH_BINARY);
|
||||
if( blur )
|
||||
GaussianBlur(src, src, Size(5, 5), 1.5, 1.5);
|
||||
|
||||
int minRegs = use_big_image ? 7 : 2;
|
||||
int maxRegs = use_big_image ? 1000 : 20;
|
||||
if( binarize && (thresh == 0 || thresh == 255) )
|
||||
minRegs = maxRegs = 0;
|
||||
|
||||
mserExtractor->detectRegions( src, msers, boxes );
|
||||
int nmsers = (int)msers.size();
|
||||
ASSERT_EQ(nmsers, (int)boxes.size());
|
||||
|
||||
if( maxRegs < nmsers || minRegs > nmsers )
|
||||
{
|
||||
printf("%d. minArea=%d, maxArea=%d, nmsers=%d, minRegs=%d, maxRegs=%d, "
|
||||
"image=%s, invert=%d, binarize=%d, thresh=%d, blur=%d\n",
|
||||
i, kMinArea, kMaxArea, nmsers, minRegs, maxRegs, use_big_image ? "big" : "small",
|
||||
(int)invert, (int)binarize, thresh, (int)blur);
|
||||
#if defined RENDER_MSERS && RENDER_MSERS
|
||||
Mat image;
|
||||
imshow("source", src);
|
||||
renderMSERs(src, image, msers);
|
||||
imshow("result", image);
|
||||
waitKey();
|
||||
#endif
|
||||
}
|
||||
|
||||
ASSERT_LE(minRegs, nmsers);
|
||||
ASSERT_GE(maxRegs, nmsers);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Features2d_MSER, history_update_regression)
|
||||
{
|
||||
String dataPath = cvtest::TS::ptr()->get_data_path() + "mser/";
|
||||
vector<Mat> tstImages;
|
||||
tstImages.push_back(imread(dataPath + "mser_test.png", IMREAD_GRAYSCALE));
|
||||
tstImages.push_back(imread(dataPath + "mser_test2.png", IMREAD_GRAYSCALE));
|
||||
|
||||
for(size_t j = 0; j < tstImages.size(); j++)
|
||||
{
|
||||
size_t previous_size = 0;
|
||||
for(int minArea = 100; minArea > 10; minArea--)
|
||||
{
|
||||
Ptr<MSER> mser = MSER::create(1, minArea, (int)(tstImages[j].cols * tstImages[j].rows * 0.2));
|
||||
mser->setPass2Only(true);
|
||||
mser->setMinDiversity(0);
|
||||
vector<vector<Point> > mserContours;
|
||||
vector<Rect> boxRects;
|
||||
mser->detectRegions(tstImages[j], mserContours, boxRects);
|
||||
ASSERT_LE(previous_size, mserContours.size());
|
||||
previous_size = mserContours.size();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST(Features2d_MSER, bug_5630)
|
||||
{
|
||||
String dataPath = cvtest::TS::ptr()->get_data_path() + "mser/";
|
||||
Mat img = imread(dataPath + "mser_test.png", IMREAD_GRAYSCALE);
|
||||
Ptr<MSER> mser = MSER::create(1, 1);
|
||||
vector<vector<Point> > mserContours;
|
||||
vector<Rect> boxRects;
|
||||
|
||||
// set min diversity and run detection
|
||||
mser->setMinDiversity(0.1);
|
||||
mser->detectRegions(img, mserContours, boxRects);
|
||||
size_t originalNumberOfContours = mserContours.size();
|
||||
|
||||
// increase min diversity and run detection again
|
||||
mser->setMinDiversity(0.2);
|
||||
mser->detectRegions(img, mserContours, boxRects);
|
||||
size_t newNumberOfContours = mserContours.size();
|
||||
|
||||
// there should be fewer regions detected with a higher min diversity
|
||||
ASSERT_LT(newNumberOfContours, originalNumberOfContours);
|
||||
|
||||
}
|
||||
|
||||
}} // namespace
|
||||
@@ -0,0 +1,384 @@
|
||||
/*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) 2014, 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*/
|
||||
|
||||
#include "test_precomp.hpp"
|
||||
|
||||
namespace opencv_test { namespace {
|
||||
|
||||
#ifdef HAVE_OPENCV_FLANN
|
||||
using namespace cv::flann;
|
||||
#endif
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
class NearestNeighborTest : public cvtest::BaseTest
|
||||
{
|
||||
public:
|
||||
NearestNeighborTest() {}
|
||||
protected:
|
||||
static const int minValue = 0;
|
||||
static const int maxValue = 1;
|
||||
static const int dims = 30;
|
||||
static const int featuresCount = 2000;
|
||||
static const int K = 1; // * should also test 2nd nn etc.?
|
||||
|
||||
|
||||
virtual void run( int start_from );
|
||||
virtual void createModel( const Mat& data ) = 0;
|
||||
virtual int findNeighbors( Mat& points, Mat& neighbors ) = 0;
|
||||
virtual int checkGetPoints( const Mat& data );
|
||||
virtual int checkFindBoxed();
|
||||
virtual int checkFind( const Mat& data );
|
||||
virtual void releaseModel() = 0;
|
||||
};
|
||||
|
||||
int NearestNeighborTest::checkGetPoints( const Mat& )
|
||||
{
|
||||
return cvtest::TS::OK;
|
||||
}
|
||||
|
||||
int NearestNeighborTest::checkFindBoxed()
|
||||
{
|
||||
return cvtest::TS::OK;
|
||||
}
|
||||
|
||||
int NearestNeighborTest::checkFind( const Mat& data )
|
||||
{
|
||||
int code = cvtest::TS::OK;
|
||||
int pointsCount = 1000;
|
||||
float noise = 0.2f;
|
||||
|
||||
RNG rng;
|
||||
Mat points( pointsCount, dims, CV_32FC1 );
|
||||
Mat results( pointsCount, K, CV_32SC1 );
|
||||
|
||||
std::vector<int> fmap( pointsCount );
|
||||
for( int pi = 0; pi < pointsCount; pi++ )
|
||||
{
|
||||
int fi = rng.next() % featuresCount;
|
||||
fmap[pi] = fi;
|
||||
for( int d = 0; d < dims; d++ )
|
||||
points.at<float>(pi, d) = data.at<float>(fi, d) + rng.uniform(0.0f, 1.0f) * noise;
|
||||
}
|
||||
|
||||
code = findNeighbors( points, results );
|
||||
|
||||
if( code == cvtest::TS::OK )
|
||||
{
|
||||
int correctMatches = 0;
|
||||
for( int pi = 0; pi < pointsCount; pi++ )
|
||||
{
|
||||
if( fmap[pi] == results.at<int>(pi, 0) )
|
||||
correctMatches++;
|
||||
}
|
||||
|
||||
double correctPerc = correctMatches / (double)pointsCount;
|
||||
EXPECT_GE(correctPerc, .73) << "correctMatches=" << correctMatches << " pointsCount=" << pointsCount;
|
||||
}
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
void NearestNeighborTest::run( int /*start_from*/ ) {
|
||||
int code = cvtest::TS::OK, tempCode;
|
||||
Mat desc( featuresCount, dims, CV_32FC1 );
|
||||
ts->get_rng().fill( desc, RNG::UNIFORM, minValue, maxValue );
|
||||
|
||||
createModel( desc.clone() ); // .clone() is used to simulate dangling pointers problem: https://github.com/opencv/opencv/issues/17553
|
||||
|
||||
tempCode = checkGetPoints( desc );
|
||||
if( tempCode != cvtest::TS::OK )
|
||||
{
|
||||
ts->printf( cvtest::TS::LOG, "bad accuracy of GetPoints \n" );
|
||||
code = tempCode;
|
||||
}
|
||||
|
||||
tempCode = checkFindBoxed();
|
||||
if( tempCode != cvtest::TS::OK )
|
||||
{
|
||||
ts->printf( cvtest::TS::LOG, "bad accuracy of FindBoxed \n" );
|
||||
code = tempCode;
|
||||
}
|
||||
|
||||
tempCode = checkFind( desc );
|
||||
if( tempCode != cvtest::TS::OK )
|
||||
{
|
||||
ts->printf( cvtest::TS::LOG, "bad accuracy of Find \n" );
|
||||
code = tempCode;
|
||||
}
|
||||
|
||||
releaseModel();
|
||||
|
||||
if (::testing::Test::HasFailure()) code = cvtest::TS::FAIL_BAD_ACCURACY;
|
||||
ts->set_failed_test_info( code );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
#ifdef HAVE_OPENCV_FLANN
|
||||
|
||||
class CV_FlannTest : public NearestNeighborTest
|
||||
{
|
||||
public:
|
||||
CV_FlannTest() : NearestNeighborTest(), index(NULL) { }
|
||||
protected:
|
||||
void createIndex( const Mat& data, const IndexParams& params );
|
||||
int knnSearch( Mat& points, Mat& neighbors );
|
||||
int radiusSearch( Mat& points, Mat& neighbors );
|
||||
virtual void releaseModel();
|
||||
Index* index;
|
||||
};
|
||||
|
||||
void CV_FlannTest::createIndex( const Mat& data, const IndexParams& params )
|
||||
{
|
||||
// release previously allocated index
|
||||
releaseModel();
|
||||
|
||||
index = new Index( data, params );
|
||||
}
|
||||
|
||||
int CV_FlannTest::knnSearch( Mat& points, Mat& neighbors )
|
||||
{
|
||||
Mat dist( points.rows, neighbors.cols, CV_32FC1);
|
||||
int knn = 1, j;
|
||||
|
||||
// 1st way
|
||||
index->knnSearch( points, neighbors, dist, knn, SearchParams() );
|
||||
|
||||
// 2nd way
|
||||
Mat neighbors1( neighbors.size(), CV_32SC1 );
|
||||
for( int i = 0; i < points.rows; i++ )
|
||||
{
|
||||
float* fltPtr = points.ptr<float>(i);
|
||||
vector<float> query( fltPtr, fltPtr + points.cols );
|
||||
vector<int> indices( neighbors1.cols, 0 );
|
||||
vector<float> dists( dist.cols, 0 );
|
||||
index->knnSearch( query, indices, dists, knn, SearchParams() );
|
||||
vector<int>::const_iterator it = indices.begin();
|
||||
for( j = 0; it != indices.end(); ++it, j++ )
|
||||
neighbors1.at<int>(i,j) = *it;
|
||||
}
|
||||
|
||||
// compare results
|
||||
EXPECT_LE(cvtest::norm(neighbors, neighbors1, NORM_L1), 0);
|
||||
|
||||
return ::testing::Test::HasFailure() ? cvtest::TS::FAIL_BAD_ACCURACY : cvtest::TS::OK;
|
||||
}
|
||||
|
||||
int CV_FlannTest::radiusSearch( Mat& points, Mat& neighbors )
|
||||
{
|
||||
Mat dist( 1, neighbors.cols, CV_32FC1);
|
||||
Mat neighbors1( neighbors.size(), CV_32SC1 );
|
||||
float radius = 10.0f;
|
||||
int j;
|
||||
|
||||
// radiusSearch can only search one feature at a time for range search
|
||||
for( int i = 0; i < points.rows; i++ )
|
||||
{
|
||||
// 1st way
|
||||
Mat p( 1, points.cols, CV_32FC1, points.ptr<float>(i) ),
|
||||
n( 1, neighbors.cols, CV_32SC1, neighbors.ptr<int>(i) );
|
||||
index->radiusSearch( p, n, dist, radius, neighbors.cols, SearchParams() );
|
||||
|
||||
// 2nd way
|
||||
float* fltPtr = points.ptr<float>(i);
|
||||
vector<float> query( fltPtr, fltPtr + points.cols );
|
||||
vector<int> indices( neighbors1.cols, 0 );
|
||||
vector<float> dists( dist.cols, 0 );
|
||||
index->radiusSearch( query, indices, dists, radius, neighbors.cols, SearchParams() );
|
||||
vector<int>::const_iterator it = indices.begin();
|
||||
for( j = 0; it != indices.end(); ++it, j++ )
|
||||
neighbors1.at<int>(i,j) = *it;
|
||||
}
|
||||
|
||||
// compare results
|
||||
EXPECT_LE(cvtest::norm(neighbors, neighbors1, NORM_L1), 0);
|
||||
|
||||
return ::testing::Test::HasFailure() ? cvtest::TS::FAIL_BAD_ACCURACY : cvtest::TS::OK;
|
||||
}
|
||||
|
||||
void CV_FlannTest::releaseModel()
|
||||
{
|
||||
if (index)
|
||||
{
|
||||
delete index;
|
||||
index = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------
|
||||
class CV_FlannLinearIndexTest : public CV_FlannTest
|
||||
{
|
||||
public:
|
||||
CV_FlannLinearIndexTest() {}
|
||||
protected:
|
||||
virtual void createModel( const Mat& data ) { createIndex( data, LinearIndexParams() ); }
|
||||
virtual int findNeighbors( Mat& points, Mat& neighbors ) { return knnSearch( points, neighbors ); }
|
||||
};
|
||||
|
||||
//---------------------------------------
|
||||
class CV_FlannKMeansIndexTest : public CV_FlannTest
|
||||
{
|
||||
public:
|
||||
CV_FlannKMeansIndexTest() {}
|
||||
protected:
|
||||
virtual void createModel( const Mat& data ) { createIndex( data, KMeansIndexParams() ); }
|
||||
virtual int findNeighbors( Mat& points, Mat& neighbors ) { return radiusSearch( points, neighbors ); }
|
||||
};
|
||||
|
||||
//---------------------------------------
|
||||
class CV_FlannKDTreeIndexTest : public CV_FlannTest
|
||||
{
|
||||
public:
|
||||
CV_FlannKDTreeIndexTest() {}
|
||||
protected:
|
||||
virtual void createModel( const Mat& data ) { createIndex( data, KDTreeIndexParams() ); }
|
||||
virtual int findNeighbors( Mat& points, Mat& neighbors ) { return radiusSearch( points, neighbors ); }
|
||||
};
|
||||
|
||||
//----------------------------------------
|
||||
class CV_FlannCompositeIndexTest : public CV_FlannTest
|
||||
{
|
||||
public:
|
||||
CV_FlannCompositeIndexTest() {}
|
||||
protected:
|
||||
virtual void createModel( const Mat& data ) { createIndex( data, CompositeIndexParams() ); }
|
||||
virtual int findNeighbors( Mat& points, Mat& neighbors ) { return knnSearch( points, neighbors ); }
|
||||
};
|
||||
|
||||
//----------------------------------------
|
||||
class CV_FlannAutotunedIndexTest : public CV_FlannTest
|
||||
{
|
||||
public:
|
||||
CV_FlannAutotunedIndexTest() {}
|
||||
protected:
|
||||
virtual void createModel( const Mat& data ) { createIndex( data, AutotunedIndexParams() ); }
|
||||
virtual int findNeighbors( Mat& points, Mat& neighbors ) { return knnSearch( points, neighbors ); }
|
||||
};
|
||||
//----------------------------------------
|
||||
class CV_FlannSavedIndexTest : public CV_FlannTest
|
||||
{
|
||||
public:
|
||||
CV_FlannSavedIndexTest() {}
|
||||
protected:
|
||||
virtual void createModel( const Mat& data );
|
||||
virtual int findNeighbors( Mat& points, Mat& neighbors ) { return knnSearch( points, neighbors ); }
|
||||
};
|
||||
|
||||
void CV_FlannSavedIndexTest::createModel(const cv::Mat &data)
|
||||
{
|
||||
switch ( cvtest::randInt(ts->get_rng()) % 2 )
|
||||
{
|
||||
//case 0: createIndex( data, LinearIndexParams() ); break; // nothing to save for linear search
|
||||
case 0: createIndex( data, KMeansIndexParams() ); break;
|
||||
case 1: createIndex( data, KDTreeIndexParams() ); break;
|
||||
//case 2: createIndex( data, CompositeIndexParams() ); break; // nothing to save for linear search
|
||||
//case 2: createIndex( data, AutotunedIndexParams() ); break; // possible linear index !
|
||||
default: CV_Assert(0);
|
||||
}
|
||||
string filename = tempfile();
|
||||
index->save( filename );
|
||||
|
||||
createIndex( data, SavedIndexParams(filename.c_str()));
|
||||
remove( filename.c_str() );
|
||||
}
|
||||
|
||||
TEST(Features2d_FLANN_Linear, regression) { CV_FlannLinearIndexTest test; test.safe_run(); }
|
||||
TEST(Features2d_FLANN_KMeans, regression) { CV_FlannKMeansIndexTest test; test.safe_run(); }
|
||||
TEST(Features2d_FLANN_KDTree, regression) { CV_FlannKDTreeIndexTest test; test.safe_run(); }
|
||||
TEST(Features2d_FLANN_Composite, regression) { CV_FlannCompositeIndexTest test; test.safe_run(); }
|
||||
TEST(Features2d_FLANN_Auto, regression) { CV_FlannAutotunedIndexTest test; test.safe_run(); }
|
||||
TEST(Features2d_FLANN_Saved, regression) { CV_FlannSavedIndexTest test; test.safe_run(); }
|
||||
|
||||
#endif
|
||||
|
||||
class CV_AnnoyTest : public NearestNeighborTest
|
||||
{
|
||||
public:
|
||||
CV_AnnoyTest() : NearestNeighborTest(), index(NULL) {}
|
||||
|
||||
protected:
|
||||
void createModel(const Mat& data) CV_OVERRIDE
|
||||
{
|
||||
index = ANNIndex::create(data.cols);
|
||||
index->addItems(data);
|
||||
index->build();
|
||||
}
|
||||
|
||||
int findNeighbors(Mat& points, Mat& neighbors) CV_OVERRIDE
|
||||
{
|
||||
Mat distances(points.rows, neighbors.cols, CV_32FC1);
|
||||
int knn = 1;
|
||||
|
||||
index->knnSearch(points, neighbors, distances, knn);
|
||||
|
||||
Mat neighbors1(neighbors.size(), CV_32SC1);
|
||||
for(int i = 0; i < points.rows; i++)
|
||||
{
|
||||
float* fltPtr = points.ptr<float>(i);
|
||||
vector<float> query(fltPtr, fltPtr + points.cols);
|
||||
vector<int> indices(neighbors1.cols, 0);
|
||||
vector<float> dists(distances.cols, 0);
|
||||
|
||||
index->knnSearch(query, indices, dists, knn);
|
||||
|
||||
vector<int>::const_iterator it = indices.begin();
|
||||
for(int j = 0; it != indices.end(); ++it, j++)
|
||||
neighbors1.at<int>(i, j) = *it;
|
||||
}
|
||||
|
||||
if (cvtest::norm(neighbors, neighbors1, NORM_L1) > 0)
|
||||
{
|
||||
ts->printf(cvtest::TS::LOG, "bad accuray of find nearest neighbors\n");
|
||||
ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY);
|
||||
}
|
||||
|
||||
return ts->get_err_code();
|
||||
}
|
||||
|
||||
void releaseModel() CV_OVERRIDE {}
|
||||
|
||||
Ptr<ANNIndex> index;
|
||||
};
|
||||
|
||||
TEST(Features2d_ANNIndex, regression) {CV_AnnoyTest test; test.safe_run();}
|
||||
|
||||
}} // namespace
|
||||
@@ -0,0 +1,171 @@
|
||||
/*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 {
|
||||
|
||||
TEST(Features2D_ORB, _1996)
|
||||
{
|
||||
Ptr<FeatureDetector> fd = ORB::create(10000, 1.2f, 8, 31, 0, 2, ORB::HARRIS_SCORE, 31, 20);
|
||||
Ptr<DescriptorExtractor> de = fd;
|
||||
|
||||
Mat image = imread(string(cvtest::TS::ptr()->get_data_path()) + "shared/lena.png");
|
||||
ASSERT_FALSE(image.empty());
|
||||
|
||||
Mat roi(image.size(), CV_8UC1, Scalar(0));
|
||||
|
||||
Point poly[] = {Point(100, 20), Point(300, 50), Point(400, 200), Point(10, 500)};
|
||||
fillConvexPoly(roi, poly, int(sizeof(poly) / sizeof(poly[0])), Scalar(255));
|
||||
|
||||
std::vector<KeyPoint> keypoints;
|
||||
fd->detect(image, keypoints, roi);
|
||||
Mat descriptors;
|
||||
de->compute(image, keypoints, descriptors);
|
||||
|
||||
//image.setTo(Scalar(255,255,255), roi);
|
||||
|
||||
int roiViolations = 0;
|
||||
for(std::vector<KeyPoint>::const_iterator kp = keypoints.begin(); kp != keypoints.end(); ++kp)
|
||||
{
|
||||
int x = cvRound(kp->pt.x);
|
||||
int y = cvRound(kp->pt.y);
|
||||
|
||||
ASSERT_LE(0, x);
|
||||
ASSERT_LE(0, y);
|
||||
ASSERT_GT(image.cols, x);
|
||||
ASSERT_GT(image.rows, y);
|
||||
|
||||
// if (!roi.at<uchar>(y,x))
|
||||
// {
|
||||
// roiViolations++;
|
||||
// circle(image, kp->pt, 3, Scalar(0,0,255));
|
||||
// }
|
||||
}
|
||||
|
||||
// if(roiViolations)
|
||||
// {
|
||||
// imshow("img", image);
|
||||
// waitKey();
|
||||
// }
|
||||
|
||||
ASSERT_EQ(0, roiViolations);
|
||||
}
|
||||
|
||||
TEST(Features2D_ORB, crash_5031)
|
||||
{
|
||||
cv::Mat image = cv::Mat::zeros(cv::Size(1920, 1080), CV_8UC3);
|
||||
|
||||
int nfeatures = 8000;
|
||||
float orbScaleFactor = 1.2f;
|
||||
int nlevels = 18;
|
||||
int edgeThreshold = 4;
|
||||
int firstLevel = 0;
|
||||
int WTA_K = 2;
|
||||
ORB::ScoreType scoreType = cv::ORB::HARRIS_SCORE;
|
||||
int patchSize = 47;
|
||||
int fastThreshold = 20;
|
||||
|
||||
Ptr<ORB> orb = cv::ORB::create(nfeatures, orbScaleFactor, nlevels, edgeThreshold, firstLevel, WTA_K, scoreType, patchSize, fastThreshold);
|
||||
|
||||
std::vector<cv::KeyPoint> keypoints;
|
||||
cv::Mat descriptors;
|
||||
|
||||
cv::KeyPoint kp;
|
||||
kp.pt.x = 443;
|
||||
kp.pt.y = 5;
|
||||
kp.size = 47;
|
||||
kp.angle = 53.4580612f;
|
||||
kp.response = 0.0000470733867f;
|
||||
kp.octave = 0;
|
||||
kp.class_id = -1;
|
||||
|
||||
keypoints.push_back(kp);
|
||||
|
||||
ASSERT_NO_THROW(orb->compute(image, keypoints, descriptors));
|
||||
}
|
||||
|
||||
|
||||
TEST(Features2D_ORB, regression_16197)
|
||||
{
|
||||
Mat img(Size(72, 72), CV_8UC1, Scalar::all(0));
|
||||
Ptr<ORB> orbPtr = ORB::create();
|
||||
orbPtr->setNLevels(5);
|
||||
orbPtr->setFirstLevel(3);
|
||||
orbPtr->setScaleFactor(1.8);
|
||||
orbPtr->setPatchSize(8);
|
||||
orbPtr->setEdgeThreshold(8);
|
||||
|
||||
std::vector<KeyPoint> kps;
|
||||
Mat fv;
|
||||
|
||||
// exception in debug mode, crash in release
|
||||
ASSERT_NO_THROW(orbPtr->detectAndCompute(img, noArray(), kps, fv));
|
||||
}
|
||||
|
||||
// https://github.com/opencv/opencv-python/issues/537
|
||||
BIGDATA_TEST(Features2D_ORB, regression_opencv_python_537) // memory usage: ~3 Gb
|
||||
{
|
||||
applyTestTag(
|
||||
CV_TEST_TAG_LONG,
|
||||
CV_TEST_TAG_DEBUG_VERYLONG,
|
||||
CV_TEST_TAG_MEMORY_6GB
|
||||
);
|
||||
|
||||
const int width = 25000;
|
||||
const int height = 25000;
|
||||
Mat img(Size(width, height), CV_8UC1, Scalar::all(0));
|
||||
|
||||
const int border = 23, num_lines = 23;
|
||||
for (int i = 0; i < num_lines; i++)
|
||||
{
|
||||
cv::Point2i point1(border + i * 100, border + i * 100);
|
||||
cv::Point2i point2(width - border - i * 100, height - border * i * 100);
|
||||
cv::line(img, point1, point2, 255, 1, LINE_AA);
|
||||
}
|
||||
|
||||
Ptr<ORB> orbPtr = ORB::create(31);
|
||||
std::vector<KeyPoint> kps;
|
||||
Mat fv;
|
||||
ASSERT_NO_THROW(orbPtr->detectAndCompute(img, noArray(), kps, fv));
|
||||
}
|
||||
|
||||
}} // namespace
|
||||
@@ -0,0 +1,10 @@
|
||||
// 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_PRECOMP_HPP__
|
||||
#define __OPENCV_TEST_PRECOMP_HPP__
|
||||
|
||||
#include "opencv2/ts.hpp"
|
||||
#include "opencv2/features.hpp"
|
||||
|
||||
#endif
|
||||
@@ -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_SIFT, descriptor_type)
|
||||
{
|
||||
Mat image = imread(cvtest::findDataFile("features2d/tsukuba.png"));
|
||||
ASSERT_FALSE(image.empty());
|
||||
|
||||
Mat gray;
|
||||
cvtColor(image, gray, COLOR_BGR2GRAY);
|
||||
|
||||
vector<KeyPoint> keypoints;
|
||||
Mat descriptorsFloat, descriptorsUchar;
|
||||
Ptr<SIFT> siftFloat = cv::SIFT::create(0, 3, 0.04, 10, 1.6, CV_32F);
|
||||
siftFloat->detectAndCompute(gray, Mat(), keypoints, descriptorsFloat, false);
|
||||
ASSERT_EQ(descriptorsFloat.type(), CV_32F) << "type mismatch";
|
||||
|
||||
Ptr<SIFT> siftUchar = cv::SIFT::create(0, 3, 0.04, 10, 1.6, CV_8U);
|
||||
siftUchar->detectAndCompute(gray, Mat(), keypoints, descriptorsUchar, false);
|
||||
ASSERT_EQ(descriptorsUchar.type(), CV_8U) << "type mismatch";
|
||||
|
||||
Mat descriptorsFloat2;
|
||||
descriptorsUchar.assignTo(descriptorsFloat2, CV_32F);
|
||||
Mat diff = descriptorsFloat != descriptorsFloat2;
|
||||
ASSERT_EQ(countNonZero(diff), 0) << "descriptors are not identical";
|
||||
}
|
||||
|
||||
TEST(Features2d_SIFT, regression_26139)
|
||||
{
|
||||
auto extractor = cv::SIFT::create();
|
||||
cv::Mat1b image{cv::Size{300, 300}, 0};
|
||||
std::vector<cv::KeyPoint> kps {
|
||||
cv::KeyPoint(154.076813f, 136.160904f, 111.078636f, 216.195618f, 0.00000899323549f, 7)
|
||||
};
|
||||
cv::Mat descriptors;
|
||||
extractor->compute(image, kps, descriptors); // we expect no memory corruption
|
||||
ASSERT_EQ(descriptors.size(), Size(128, 1));
|
||||
}
|
||||
|
||||
|
||||
}} // namespace
|
||||
@@ -0,0 +1,38 @@
|
||||
// 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_KeypointUtils, retainBest_issue_12594)
|
||||
{
|
||||
const size_t N = 9;
|
||||
|
||||
// Construct 4-way tie for 3rd highest - correct answer for "3 best" is 6
|
||||
const float no_problem[] = { 5.0f, 4.0f, 1.0f, 2.0f, 0.0f, 3.0f, 3.0f, 3.0f, 3.0f };
|
||||
|
||||
// Same set, different order that exposes partial sort property of std::nth_element
|
||||
// Note: the problem case may depend on your particular implementation of STL
|
||||
const float problem[] = { 3.0f, 3.0f, 3.0f, 3.0f, 4.0f, 5.0f, 0.0f, 1.0f, 2.0f };
|
||||
|
||||
const size_t NBEST = 3u;
|
||||
const size_t ANSWER = 6u;
|
||||
|
||||
std::vector<cv::KeyPoint> sorted_cv(N);
|
||||
std::vector<cv::KeyPoint> unsorted_cv(N);
|
||||
|
||||
for (size_t i = 0; i < N; ++i)
|
||||
{
|
||||
sorted_cv[i].response = no_problem[i];
|
||||
unsorted_cv[i].response = problem[i];
|
||||
}
|
||||
|
||||
cv::KeyPointsFilter::retainBest(sorted_cv, NBEST);
|
||||
cv::KeyPointsFilter::retainBest(unsorted_cv, NBEST);
|
||||
|
||||
EXPECT_EQ(ANSWER, sorted_cv.size());
|
||||
EXPECT_EQ(ANSWER, unsorted_cv.size());
|
||||
}
|
||||
|
||||
}} // namespace
|
||||
Reference in New Issue
Block a user