vendor: OpenCV 5.0.0 snapshot at 755e50675d97db9b7d449d8bd6b09888646f6c6e

This commit is contained in:
Gitea Mirror Bot
2026-08-22 00:11:13 +08:00
commit 12022378a3
3872 changed files with 2513409 additions and 0 deletions
+44
View File
@@ -0,0 +1,44 @@
set(the_description "Contributed/Experimental Algorithms for Salient 2D Features Detection")
if(HAVE_CUDA)
ocv_warnings_disable(CMAKE_CXX_FLAGS -Wundef)
endif()
ocv_define_module(xfeatures2d opencv_core opencv_imgproc opencv_features opencv_geometry OPTIONAL opencv_shape opencv_ml opencv_cudaarithm WRAP python java objc)
ocv_install_3rdparty_licenses(xfeatures2d "${CMAKE_CURRENT_SOURCE_DIR}/src/kaze/LICENSE.KAZE" "${CMAKE_CURRENT_SOURCE_DIR}/src/kaze/LICENSE.AKAZE")
if(HAVE_CUDA AND ENABLE_CUDA_FIRST_CLASS_LANGUAGE AND HAVE_opencv_cudaarithm)
ocv_target_link_libraries(${the_module} PRIVATE CUDA::cudart${CUDA_LIB_EXT})
endif()
if(NOT OPENCV_SKIP_FEATURES2D_DOWNLOADING)
include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/download_vgg.cmake)
include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/download_boostdesc.cmake)
set(DOWNLOAD_DIR "${OpenCV_BINARY_DIR}/downloads/xfeatures2d")
download_boost_descriptors("${DOWNLOAD_DIR}" boost_status)
download_vgg_descriptors("${DOWNLOAD_DIR}" vgg_status)
if(boost_status)
ocv_append_source_file_compile_definitions(${CMAKE_CURRENT_SOURCE_DIR}/src/boostdesc.cpp "OPENCV_XFEATURES2D_HAS_BOOST_DATA=1")
else()
message(WARNING "xfeatures2d: Boost descriptor implementation is not available due to missing data (download failed: https://github.com/opencv/opencv_contrib/issues/1301)")
endif()
if(vgg_status)
ocv_append_source_file_compile_definitions(${CMAKE_CURRENT_SOURCE_DIR}/src/vgg.cpp "OPENCV_XFEATURES2D_HAS_VGG_DATA=1")
else()
message(WARNING "xfeatures2d: VGG descriptor implementation is not available due to missing data (download failed: https://github.com/opencv/opencv_contrib/issues/1301)")
endif()
if(boost_status OR vgg_status)
ocv_module_include_directories("${DOWNLOAD_DIR}")
endif()
endif()
if(TARGET opencv_test_${name})
ocv_target_include_directories(opencv_test_${name} "${OpenCV_SOURCE_DIR}/modules") # use common files from features2d tests
if(boost_status)
ocv_target_compile_definitions(opencv_test_${name} PRIVATE "OPENCV_XFEATURES2D_HAS_BOOST_DATA=1")
endif()
if(vgg_status)
ocv_target_compile_definitions(opencv_test_${name} PRIVATE "OPENCV_XFEATURES2D_HAS_VGG_DATA=1")
endif()
endif()
+8
View File
@@ -0,0 +1,8 @@
Extra 2D Features Framework
===========================
1. Experimental 2D feature algorithms
2. Non-free 2D feature algorithms
Extra 2D Features Framework containing experimental and non-free 2D feature detector/descriptor algorithms:
SURF, BRIEF, Censure, Freak, LUCID, Daisy, BEBLID, TEBLID, Self-similar.
@@ -0,0 +1,36 @@
function(download_boost_descriptors dst_dir status_var)
set(OPENCV_3RDPARTY_COMMIT "34e4206aef44d50e6bbcd0ab06354b52e7466d26")
set(ids BGM BGM_BI BGM_HD BINBOOST_064 BINBOOST_128 BINBOOST_256 LBGM)
set(name_BGM boostdesc_bgm.i)
set(name_BGM_BI boostdesc_bgm_bi.i)
set(name_BGM_HD boostdesc_bgm_hd.i)
set(name_BINBOOST_064 boostdesc_binboost_064.i)
set(name_BINBOOST_128 boostdesc_binboost_128.i)
set(name_BINBOOST_256 boostdesc_binboost_256.i)
set(name_LBGM boostdesc_lbgm.i)
set(hash_BGM "0ea90e7a8f3f7876d450e4149c97c74f")
set(hash_BGM_BI "232c966b13651bd0e46a1497b0852191")
set(hash_BGM_HD "324426a24fa56ad9c5b8e3e0b3e5303e")
set(hash_BINBOOST_064 "202e1b3e9fec871b04da31f7f016679f")
set(hash_BINBOOST_128 "98ea99d399965c03d555cef3ea502a0b")
set(hash_BINBOOST_256 "e6dcfa9f647779eb1ce446a8d759b6ea")
set(hash_LBGM "0ae0675534aa318d9668f2a179c2a052")
set(${status_var} TRUE PARENT_SCOPE)
foreach(id ${ids})
ocv_download(FILENAME ${name_${id}}
HASH ${hash_${id}}
URL
"${OPENCV_BOOSTDESC_URL}"
"$ENV{OPENCV_BOOSTDESC_URL}"
"https://raw.githubusercontent.com/opencv/opencv_3rdparty/${OPENCV_3RDPARTY_COMMIT}/"
DESTINATION_DIR ${dst_dir}
ID "xfeatures2d/boostdesc"
RELATIVE_URL
STATUS res)
if(NOT res)
set(${status_var} FALSE PARENT_SCOPE)
endif()
endforeach()
endfunction()
@@ -0,0 +1,30 @@
function(download_vgg_descriptors dst_dir status_var)
set(OPENCV_3RDPARTY_COMMIT "fccf7cd6a4b12079f73bbfb21745f9babcd4eb1d")
set(ids VGG_48 VGG_64 VGG_80 VGG_120)
set(name_VGG_48 "vgg_generated_48.i")
set(name_VGG_64 "vgg_generated_64.i")
set(name_VGG_80 "vgg_generated_80.i")
set(name_VGG_120 "vgg_generated_120.i")
set(hash_VGG_48 "e8d0dcd54d1bcfdc29203d011a797179")
set(hash_VGG_64 "7126a5d9a8884ebca5aea5d63d677225")
set(hash_VGG_80 "7cd47228edec52b6d82f46511af325c5")
set(hash_VGG_120 "151805e03568c9f490a5e3a872777b75")
set(${status_var} TRUE PARENT_SCOPE)
foreach(id ${ids})
ocv_download(FILENAME ${name_${id}}
HASH ${hash_${id}}
URL
"${OPENCV_VGGDESC_URL}"
"$ENV{OPENCV_VGGDESC_URL}"
"https://raw.githubusercontent.com/opencv/opencv_3rdparty/${OPENCV_3RDPARTY_COMMIT}/"
DESTINATION_DIR "${dst_dir}"
ID "xfeatures2d/vgg"
RELATIVE_URL
STATUS res)
if(NOT res)
set(${status_var} FALSE PARENT_SCOPE)
endif()
endforeach()
endfunction()
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,284 @@
#!/usr/bin/perl
use strict;
use warnings;
use autodie; # die if problem reading or writing a file
my $filein = "./agast.txt";
my $fileout = "./agast_new.txt";
my $i1=1;
my $i2=1;
my $i3=1;
my $tmp;
my $ifcount0=0;
my $ifcount1=0;
my $ifcount2=0;
my $ifcount3=0;
my $ifcount4=0;
my $elsecount;
my $myfirstline = $ARGV[0];
my $mylastline = $ARGV[1];
my $tablename = $ARGV[2];
my @array0 = ();
my @array1 = ();
my @array2 = ();
my @array3 = ();
my $homogeneous;
my $success_homogeneous;
my $structured;
my $success_structured;
open(my $in1, "<", $filein) or die "Can't open $filein: $!";
open(my $out, ">", $fileout) or die "Can't open $fileout: $!";
$array0[0] = 0;
$i1=1;
while (my $line1 = <$in1>)
{
chomp $line1;
$array0[$i1] = 0;
if (($i1>=$myfirstline)&&($i1<=$mylastline))
{
if($line1=~/if\(ptr\[offset(\d+)/)
{
if($line1=~/if\(ptr\[offset(\d+).*\>.*cb/)
{
$tmp=$1;
}
else
{
if($line1=~/if\(ptr\[offset(\d+).*\<.*c\_b/)
{
$tmp=$1+128;
}
else
{
die "invalid array index!"
}
}
$array1[$ifcount1] = $tmp;
$array0[$ifcount1] = $i1;
$ifcount1++;
}
else
{
}
}
$i1++;
}
$homogeneous=$ifcount1;
$success_homogeneous=$ifcount1+1;
$structured=$ifcount1+2;
$success_structured=$ifcount1+3;
close $in1 or die "Can't close $filein: $!";
open($in1, "<", $filein) or die "Can't open $filein: $!";
$i1=1;
while (my $line1 = <$in1>)
{
chomp $line1;
if (($i1>=$myfirstline)&&($i1<=$mylastline))
{
if ($array0[$ifcount2] == $i1)
{
$array2[$ifcount2]=0;
$array3[$ifcount2]=0;
if ($array0[$ifcount2+1] == ($i1+1))
{
$array2[$ifcount2]=($ifcount2+1);
}
else
{
open(my $in2, "<", $filein) or die "Can't open $filein: $!";
$i2=1;
while (my $line2 = <$in2>)
{
chomp $line2;
if ($i2 == $i1)
{
last;
}
$i2++;
}
my $line2 = <$in2>;
chomp $line2;
if ($line2=~/goto (\w+)/)
{
$tmp=$1;
if ($tmp eq "homogeneous")
{
$array2[$ifcount2]=$homogeneous;
}
if ($tmp eq "success_homogeneous")
{
$array2[$ifcount2]=$success_homogeneous;
}
if ($tmp eq "structured")
{
$array2[$ifcount2]=$structured;
}
if ($tmp eq "success_structured")
{
$array2[$ifcount2]=$success_structured;
}
}
else
{
die "goto expected: $!";
}
close $in2 or die "Can't close $filein: $!";
}
#find next else and interpret it
open(my $in3, "<", $filein) or die "Can't open $filein: $!";
$i3=1;
$ifcount3=0;
$elsecount=0;
while (my $line3 = <$in3>)
{
chomp $line3;
$i3++;
if ($i3 == $i1)
{
last;
}
}
while (my $line3 = <$in3>)
{
chomp $line3;
$ifcount3++;
if (($elsecount==0)&&($i3>$i1))
{
if ($line3=~/goto (\w+)/)
{
$tmp=$1;
if ($tmp eq "homogeneous")
{
$array3[$ifcount2]=$homogeneous;
}
if ($tmp eq "success_homogeneous")
{
$array3[$ifcount2]=$success_homogeneous;
}
if ($tmp eq "structured")
{
$array3[$ifcount2]=$structured;
}
if ($tmp eq "success_structured")
{
$array3[$ifcount2]=$success_structured;
}
}
else
{
if ($line3=~/if\(ptr\[offset/)
{
$ifcount4=0;
while ($array0[$ifcount4]!=$i3)
{
$ifcount4++;
if ($ifcount4==$ifcount1)
{
die "if else match expected: $!";
}
$array3[$ifcount2]=$ifcount4;
}
}
else
{
die "elseif or elsegoto match expected: $!";
}
}
last;
}
else
{
if ($line3=~/if\(ptr\[offset/)
{
$elsecount++;
}
else
{
if ($line3=~/else/)
{
$elsecount--;
}
}
}
$i3++;
}
printf("%3d [%3d][0x%08x]\n", $array0[$ifcount2], $ifcount2, (($array1[$ifcount2]&15)<<28)|($array2[$ifcount2]<<16)|(($array1[$ifcount2]&128)<<5)|($array3[$ifcount2]));
close $in3 or die "Can't close $filein: $!";
$ifcount2++;
}
else
{
}
}
$i1++;
}
printf(" [%3d][0x%08x]\n", $homogeneous, 252);
printf(" [%3d][0x%08x]\n", $success_homogeneous, 253);
printf(" [%3d][0x%08x]\n", $structured, 254);
printf(" [%3d][0x%08x]\n", $success_structured, 255);
close $in1 or die "Can't close $filein: $!";
$ifcount0=0;
$ifcount2=0;
printf $out " static const unsigned long %s[] = {\n ", $tablename;
while ($ifcount0 < $ifcount1)
{
printf $out "0x%08x, ", (($array1[$ifcount0]&15)<<28)|($array2[$ifcount0]<<16)|(($array1[$ifcount0]&128)<<5)|($array3[$ifcount0]);
$ifcount0++;
$ifcount2++;
if ($ifcount2==8)
{
$ifcount2=0;
printf $out "\n";
printf $out " ";
}
}
printf $out "0x%08x, ", 252;
$ifcount0++;
$ifcount2++;
if ($ifcount2==8)
{
$ifcount2=0;
printf $out "\n";
printf $out " ";
}
printf $out "0x%08x, ", 253;
$ifcount0++;
$ifcount2++;
if ($ifcount2==8)
{
$ifcount2=0;
printf $out "\n";
printf $out " ";
}
printf $out "0x%08x, ", 254;
$ifcount0++;
$ifcount2++;
if ($ifcount2==8)
{
$ifcount2=0;
printf $out "\n";
printf $out " ";
}
printf $out "0x%08x\n", 255;
$ifcount0++;
$ifcount2++;
printf $out " };\n\n";
$#array0 = -1;
$#array1 = -1;
$#array2 = -1;
$#array3 = -1;
close $out or die "Can't close $fileout: $!";
@@ -0,0 +1,244 @@
#!/usr/bin/perl
use strict;
use warnings;
use autodie; # die if problem reading or writing a file
my $filein = "./agast_score.txt";
my $fileout = "./agast_new.txt";
my $i1=1;
my $i2=1;
my $i3=1;
my $tmp;
my $ifcount0=0;
my $ifcount1=0;
my $ifcount2=0;
my $ifcount3=0;
my $ifcount4=0;
my $elsecount;
my $myfirstline = $ARGV[0];
my $mylastline = $ARGV[1];
my $tablename = $ARGV[2];
my @array0 = ();
my @array1 = ();
my @array2 = ();
my @array3 = ();
my $is_not_a_corner;
my $is_a_corner;
open(my $in1, "<", $filein) or die "Can't open $filein: $!";
open(my $out, ">", $fileout) or die "Can't open $fileout: $!";
$array0[0] = 0;
$i1=1;
while (my $line1 = <$in1>)
{
chomp $line1;
$array0[$i1] = 0;
if (($i1>=$myfirstline)&&($i1<=$mylastline))
{
if($line1=~/if\(ptr\[offset(\d+)/)
{
if($line1=~/if\(ptr\[offset(\d+).*\>.*cb/)
{
$tmp=$1;
}
else
{
if($line1=~/if\(ptr\[offset(\d+).*\<.*c\_b/)
{
$tmp=$1+128;
}
else
{
die "invalid array index!"
}
}
$array1[$ifcount1] = $tmp;
$array0[$ifcount1] = $i1;
$ifcount1++;
}
else
{
}
}
$i1++;
}
$is_not_a_corner=$ifcount1;
$is_a_corner=$ifcount1+1;
close $in1 or die "Can't close $filein: $!";
open($in1, "<", $filein) or die "Can't open $filein: $!";
$i1=1;
while (my $line1 = <$in1>)
{
chomp $line1;
if (($i1>=$myfirstline)&&($i1<=$mylastline))
{
if ($array0[$ifcount2] == $i1)
{
$array2[$ifcount2]=0;
$array3[$ifcount2]=0;
if ($array0[$ifcount2+1] == ($i1+1))
{
$array2[$ifcount2]=($ifcount2+1);
}
else
{
open(my $in2, "<", $filein) or die "Can't open $filein: $!";
$i2=1;
while (my $line2 = <$in2>)
{
chomp $line2;
if ($i2 == $i1)
{
last;
}
$i2++;
}
my $line2 = <$in2>;
chomp $line2;
if ($line2=~/goto (\w+)/)
{
$tmp=$1;
if ($tmp eq "is_not_a_corner")
{
$array2[$ifcount2]=$is_not_a_corner;
}
if ($tmp eq "is_a_corner")
{
$array2[$ifcount2]=$is_a_corner;
}
}
else
{
die "goto expected: $!";
}
close $in2 or die "Can't close $filein: $!";
}
#find next else and interpret it
open(my $in3, "<", $filein) or die "Can't open $filein: $!";
$i3=1;
$ifcount3=0;
$elsecount=0;
while (my $line3 = <$in3>)
{
chomp $line3;
$i3++;
if ($i3 == $i1)
{
last;
}
}
while (my $line3 = <$in3>)
{
chomp $line3;
$ifcount3++;
if (($elsecount==0)&&($i3>$i1))
{
if ($line3=~/goto (\w+)/)
{
$tmp=$1;
if ($tmp eq "is_not_a_corner")
{
$array3[$ifcount2]=$is_not_a_corner;
}
if ($tmp eq "is_a_corner")
{
$array3[$ifcount2]=$is_a_corner;
}
}
else
{
if ($line3=~/if\(ptr\[offset/)
{
$ifcount4=0;
while ($array0[$ifcount4]!=$i3)
{
$ifcount4++;
if ($ifcount4==$ifcount1)
{
die "if else match expected: $!";
}
$array3[$ifcount2]=$ifcount4;
}
}
else
{
die "elseif or elsegoto match expected: $!";
}
}
last;
}
else
{
if ($line3=~/if\(ptr\[offset/)
{
$elsecount++;
}
else
{
if ($line3=~/else/)
{
$elsecount--;
}
}
}
$i3++;
}
printf("%3d [%3d][0x%08x]\n", $array0[$ifcount2], $ifcount2, (($array1[$ifcount2]&15)<<28)|($array2[$ifcount2]<<16)|(($array1[$ifcount2]&128)<<5)|($array3[$ifcount2]));
close $in3 or die "Can't close $filein: $!";
$ifcount2++;
}
else
{
}
}
$i1++;
}
printf(" [%3d][0x%08x]\n", $is_not_a_corner, 254);
printf(" [%3d][0x%08x]\n", $is_a_corner, 255);
close $in1 or die "Can't close $filein: $!";
$ifcount0=0;
$ifcount2=0;
printf $out " static const unsigned long %s[] = {\n ", $tablename;
while ($ifcount0 < $ifcount1)
{
printf $out "0x%08x, ", (($array1[$ifcount0]&15)<<28)|($array2[$ifcount0]<<16)|(($array1[$ifcount0]&128)<<5)|($array3[$ifcount0]);
$ifcount0++;
$ifcount2++;
if ($ifcount2==8)
{
$ifcount2=0;
printf $out "\n";
printf $out " ";
}
}
printf $out "0x%08x, ", 254;
$ifcount0++;
$ifcount2++;
if ($ifcount2==8)
{
$ifcount2=0;
printf $out "\n";
printf $out " ";
}
printf $out "0x%08x\n", 255;
$ifcount0++;
$ifcount2++;
printf $out " };\n\n";
$#array0 = -1;
$#array1 = -1;
$#array2 = -1;
$#array3 = -1;
close $out or die "Can't close $fileout: $!";
@@ -0,0 +1,32 @@
perl read_file_score32.pl 9059 9385 table_5_8_corner_struct
move agast_new.txt agast_score_table.txt
perl read_file_score32.pl 2215 3387 table_7_12d_corner_struct
copy /A agast_score_table.txt + agast_new.txt agast_score_table.txt
del agast_new.txt
perl read_file_score32.pl 3428 9022 table_7_12s_corner_struct
copy /A agast_score_table.txt + agast_new.txt agast_score_table.txt
del agast_new.txt
perl read_file_score32.pl 118 2174 table_9_16_corner_struct
copy /A agast_score_table.txt + agast_new.txt agast_score_table.txt
del agast_new.txt
perl read_file_nondiff32.pl 103 430 table_5_8_struct1
move agast_new.txt agast_table.txt
perl read_file_nondiff32.pl 440 779 table_5_8_struct2
copy /A agast_table.txt + agast_new.txt agast_table.txt
del agast_new.txt
perl read_file_nondiff32.pl 869 2042 table_7_12d_struct1
copy /A agast_table.txt + agast_new.txt agast_table.txt
del agast_new.txt
perl read_file_nondiff32.pl 2052 3225 table_7_12d_struct2
copy /A agast_table.txt + agast_new.txt agast_table.txt
del agast_new.txt
perl read_file_nondiff32.pl 3315 4344 table_7_12s_struct1
copy /A agast_table.txt + agast_new.txt agast_table.txt
del agast_new.txt
perl read_file_nondiff32.pl 4354 5308 table_7_12s_struct2
copy /A agast_table.txt + agast_new.txt agast_table.txt
del agast_new.txt
perl read_file_nondiff32.pl 5400 7454 table_9_16_struct
copy /A agast_table.txt + agast_new.txt agast_table.txt
del agast_new.txt
+194
View File
@@ -0,0 +1,194 @@
@incollection{Agrawal08,
title = {Censure: Center surround extremas for realtime feature detection and matching},
author = {Agrawal, Motilal and Konolige, Kurt and Blas, Morten Rufus},
booktitle = {Computer Vision--ECCV 2008},
pages = {102--115},
year = {2008},
publisher = {Springer}
}
@inproceedings{AOV12,
title = {Freak: Fast retina keypoint},
author = {Alahi, Alexandre and Ortiz, Raphael and Vandergheynst, Pierre},
booktitle = {Computer Vision and Pattern Recognition (CVPR), 2012 IEEE Conference on},
pages = {510--517},
year = {2012},
organization = {IEEE}
}
@article{Bay06,
title = {Surf: Speeded up robust features},
author = {Bay, Herbert and Tuytelaars, Tinne and Van Gool, Luc},
journal = {Computer Vision--ECCV 2006},
pages = {404--417},
year = {2006},
publisher = {Springer Berlin Heidelberg}
}
@inproceedings{BeecksUS10,
author = {Christian Beecks and Merih Seran Uysal and Thomas Seidl},
title = {Signature Quadratic Form Distance},
booktitle = {CIVR},
pages = {438--445},
publisher = {ACM},
year = {2010}
}
@inproceedings{Bian2017gms,
title = {GMS: Grid-based Motion Statistics for Fast, Ultra-robust Feature Correspondence},
author = {JiaWang Bian and Wen-Yan Lin and Yasuyuki Matsushita and Sai-Kit Yeung and Tan Dat Nguyen and Ming-Ming Cheng},
booktitle = {IEEE Conference on Computer Vision and Pattern Recognition},
year = {2017}
}
@incollection{calon2010,
title = {Brief: Binary robust independent elementary features},
author = {Calonder, Michael and Lepetit, Vincent and Strecha, Christoph and Fua, Pascal},
booktitle = {Computer Vision--ECCV 2010},
pages = {778--792},
year = {2010},
publisher = {Springer}
}
@article{KrulisLS16,
author = {Martin Krulis and Jakub Lokoc and Tomas Skopal},
title = {Efficient extraction of clustering-based feature signatures using {GPU} architectures},
journal = {Multimedia Tools Appl.},
volume = {75},
number = {13},
pages = {8071--8103},
year = {2016}
}
@article{Lowry2018LOGOSLG,
title = {LOGOS: Local Geometric Support for High-Outlier Spatial Verification},
author = {Stephanie Lowry and Henrik Andreasson},
journal = {2018 IEEE International Conference on Robotics and Automation (ICRA)},
year = {2018},
pages = {7262-7269},
doi = {10.1109/ICRA.2018.8460988},
}
@article{Mikolajczyk2004,
title = {Scale \& affine invariant interest point detectors},
author = {Mikolajczyk, Krystian and Schmid, Cordelia},
journal = {International journal of computer vision},
volume = {60},
number = {1},
pages = {63--86},
year = {2004},
publisher = {Springer}
}
@ARTICLE{Najman2014,
author={Y. {Xu} and P. {Monasse} and T. {Géraud} and L. {Najman}},
journal={IEEE Transactions on Image Processing},
title={Tree-Based Morse Regions: A Topological Approach to Local Feature Detection},
year={2014},
volume={23},
number={12},
pages={5612-5625},
abstract={This paper introduces a topological approach to local invariant feature detection motivated by Morse theory. We use the critical points of the graph of the intensity image, revealing directly the topology information as initial interest points. Critical points are selected from what we call a tree-based shape-space. In particular, they are selected from both the connected components of the upper level sets of the image (the Max-tree) and those of the lower level sets (the Min-tree). They correspond to specific nodes on those two trees: 1) to the leaves (extrema) and 2) to the nodes having bifurcation (saddle points). We then associate to each critical point the largest region that contains it and is topologically equivalent in its tree. We call such largest regions the tree-based Morse regions (TBMRs). The TBMR can be seen as a variant of maximally stable extremal region (MSER), which are contrasted regions. Contrarily to MSER, TBMR relies only on topological information and thus fully inherit the invariance properties of the space of shapes (e.g., invariance to affine contrast changes and covariance to continuous transformations). In particular, TBMR extracts the regions independently of the contrast, which makes it truly contrast invariant. Furthermore, it is quasi-parameter free. TBMR extraction is fast, having the same complexity as MSER. Experimentally, TBMR achieves a repeatability on par with state-of-the-art methods, but obtains a significantly higher number of features. Both the accuracy and robustness of TBMR are demonstrated by applications to image registration and 3D reconstruction.},
keywords={feature extraction;image reconstruction;image registration;trees (mathematics);tree-based Morse regions;topological approach;local invariant feature detection;Morse theory;intensity image;initial interest points;critical points;tree-based shape-space;upper level image sets;Max-tree;lower level sets;Min-tree;saddle points;bifurcation;maximally stable extremal region variant;MSER;topological information;TBMR extraction;3D reconstruction;image registration;Feature extraction;Detectors;Shape;Time complexity;Level set;Three-dimensional displays;Image registration;Min/Max tree;local features;affine region detectors;image registration;3D reconstruction;Min/Max tree;local features;affine region detectors;image registration;3D reconstruction},
doi={10.1109/TIP.2014.2364127},
ISSN={1941-0042},
month={Dec},}
@article{Simonyan14,
author = {Simonyan, K. and Vedaldi, A. and Zisserman, A.},
title = {Learning Local Feature Descriptors Using Convex Optimisation},
journal = {IEEE Transactions on Pattern Analysis and Machine Intelligence},
year = {2014}
}
@article{Tola10,
author = {E. Tola and V. Lepetit and P. Fua},
title = {DAISY: An Efficient Dense Descriptor Applied to Wide Baseline Stereo},
journal = {IEEE Transactions on Pattern Analysis and Machine Intelligence},
year = {2010},
month = {May},
pages = {815--830},
volume = {32},
number = {5}
}
@inproceedings{Tombari14,
title = {Interest Points via Maximal Self-Dissimilarities},
author = {Tombari, Federico and Di Stefano, Luigi},
booktitle = {Asian Conference on Computer Vision -- ACCV 2014},
year = {2014}
}
@inproceedings{Trzcinski13a,
author = {T. Trzcinski, M. Christoudias, V. Lepetit and P. Fua},
title = {Boosting Binary Keypoint Descriptors},
booktitle = {Computer Vision and Pattern Recognition},
year = {2013}
}
@article{Trzcinski13b,
author = {T. Trzcinski, M. Christoudias and V. Lepetit},
title = {Learning Image Descriptors with Boosting},
journal = {IEEE Transactions on Pattern Analysis and Machine Intelligence (PAMI)},
year = {2013}
}
@incollection{LUCID,
title = {Locally uniform comparison image descriptor},
author = {Ziegler, Andrew, Eric Christiansen, David Kriegman, and Serge J. Belongie}
booktitle = {Advances in Neural Information Processing Systems}
pages = {1--9}
year = {2012}
publisher = {NIPS}
}
@article{Suarez2020BEBLID,
title = {{BEBLID: Boosted Efficient Binary Local Image Descriptor}},
journal = {Pattern Recognition Letters},
volume = {133},
pages = {366--372},
year = {2020},
issn = {0167-8655},
doi = {https://doi.org/10.1016/j.patrec.2020.04.005},
url = {https://raw.githubusercontent.com/iago-suarez/BEBLID/master/BEBLID_Boosted_Efficient_Binary_Local_Image_Descriptor.pdf},
author = {Iago Su\'arez and Ghesn Sfeir and Jos\'e M. Buenaposada and Luis Baumela},
}
@article{Suarez2021TEBLID,
title = {Revisiting Binary Local Image Description for Resource Limited Devices},
journal = {IEEE Robotics and Automation Letters},
volume = {6},
pages = {8317--8324},
year = {2021},
number = {4},
doi = {https://doi.org/10.1109/LRA.2021.3107024},
url = {https://arxiv.org/pdf/2108.08380.pdf},
author = {Iago Su\'arez and Jos\'e M. Buenaposada and Luis Baumela},
}
@inproceedings{winder2007learning,
title= {Learning Local Image Descriptors},
author= {Winder, Simon AJ and Brown, Matthew},
booktitle= {Computer Vision and Pattern Recognition},
pages={1--8},
year={2007},
}
@incollection{ABD12,
author = {Alcantarilla, Pablo Fern{\'a}ndez and Bartoli, Adrien and Davison, Andrew J},
title = {KAZE features},
booktitle = {Computer Vision--ECCV 2012},
year = {2012},
pages = {214--227},
publisher = {Springer},
url = {https://www.doc.ic.ac.uk/~ajd/Publications/alcantarilla_etal_eccv2012.pdf}
}
@inproceedings{LCS11,
author = {Leutenegger, Stefan and Chli, Margarita and Siegwart, Roland Yves},
title = {BRISK: Binary robust invariant scalable keypoints},
booktitle = {Computer Vision (ICCV), 2011 IEEE International Conference on},
year = {2011},
pages = {2548--2555},
publisher = {IEEE}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,202 @@
/*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_XFEATURES2D_CUDA_HPP__
#define __OPENCV_XFEATURES2D_CUDA_HPP__
#include "opencv2/core/cuda.hpp"
namespace cv { namespace cuda {
//! @addtogroup xfeatures2d_nonfree
//! @{
/** @brief Class used for extracting Speeded Up Robust Features (SURF) from an image. :
The class SURF_CUDA implements Speeded Up Robust Features descriptor. There is a fast multi-scale
Hessian keypoint detector that can be used to find the keypoints (which is the default option). But
the descriptors can also be computed for the user-specified keypoints. Only 8-bit grayscale images
are supported.
The class SURF_CUDA can store results in the GPU and CPU memory. It provides functions to convert
results between CPU and GPU version ( uploadKeypoints, downloadKeypoints, downloadDescriptors ). The
format of CPU results is the same as SURF results. GPU results are stored in GpuMat. The keypoints
matrix is \f$\texttt{nFeatures} \times 7\f$ matrix with the CV_32FC1 type.
- keypoints.ptr\<float\>(X_ROW)[i] contains x coordinate of the i-th feature.
- keypoints.ptr\<float\>(Y_ROW)[i] contains y coordinate of the i-th feature.
- keypoints.ptr\<float\>(LAPLACIAN_ROW)[i] contains the laplacian sign of the i-th feature.
- keypoints.ptr\<float\>(OCTAVE_ROW)[i] contains the octave of the i-th feature.
- keypoints.ptr\<float\>(SIZE_ROW)[i] contains the size of the i-th feature.
- keypoints.ptr\<float\>(ANGLE_ROW)[i] contain orientation of the i-th feature.
- keypoints.ptr\<float\>(HESSIAN_ROW)[i] contains the response of the i-th feature.
The descriptors matrix is \f$\texttt{nFeatures} \times \texttt{descriptorSize}\f$ matrix with the
CV_32FC1 type.
The class SURF_CUDA uses some buffers and provides access to it. All buffers can be safely released
between function calls.
@sa SURF
@note
- An example for using the SURF keypoint matcher on GPU can be found at
opencv_source_code/samples/gpu/surf_keypoint_matcher.cpp
*/
class CV_EXPORTS_W SURF_CUDA
{
public:
enum KeypointLayout
{
X_ROW = 0,
Y_ROW,
LAPLACIAN_ROW,
OCTAVE_ROW,
SIZE_ROW,
ANGLE_ROW,
HESSIAN_ROW,
ROWS_COUNT
};
//! the default constructor
SURF_CUDA();
//! the full constructor taking all the necessary parameters
explicit SURF_CUDA(double _hessianThreshold, int _nOctaves=4,
int _nOctaveLayers=2, bool _extended=false, float _keypointsRatio=0.01f, bool _upright = false);
/**
@param _hessianThreshold Threshold for hessian keypoint detector used in SURF.
@param _nOctaves Number of pyramid octaves the keypoint detector will use.
@param _nOctaveLayers Number of octave layers within each octave.
@param _extended Extended descriptor flag (true - use extended 128-element descriptors; false - use
64-element descriptors).
@param _keypointsRatio Limits a maximum number of features
@param _upright Up-right or rotated features flag (true - do not compute orientation of features;
false - compute orientation).
*/
CV_WRAP static Ptr<SURF_CUDA> create(double _hessianThreshold, int _nOctaves = 4,
int _nOctaveLayers = 2, bool _extended = false, float _keypointsRatio = 0.01f, bool _upright = false);
//! returns the descriptor size in float's (64 or 128)
CV_WRAP int descriptorSize() const;
//! returns the default norm type
CV_WRAP int defaultNorm() const;
//! upload host keypoints to device memory
void uploadKeypoints(const std::vector<KeyPoint>& keypoints, GpuMat& keypointsGPU);
//! download keypoints from device to host memory
CV_WRAP void downloadKeypoints(const GpuMat& keypointsGPU, CV_OUT std::vector<KeyPoint>& keypoints);
//! download descriptors from device to host memory
void downloadDescriptors(const GpuMat& descriptorsGPU, std::vector<float>& descriptors);
//! finds the keypoints using fast hessian detector used in SURF
//! supports CV_8UC1 images
//! keypoints will have nFeature cols and 6 rows
//! keypoints.ptr<float>(X_ROW)[i] will contain x coordinate of i'th feature
//! keypoints.ptr<float>(Y_ROW)[i] will contain y coordinate of i'th feature
//! keypoints.ptr<float>(LAPLACIAN_ROW)[i] will contain laplacian sign of i'th feature
//! keypoints.ptr<float>(OCTAVE_ROW)[i] will contain octave of i'th feature
//! keypoints.ptr<float>(SIZE_ROW)[i] will contain size of i'th feature
//! keypoints.ptr<float>(ANGLE_ROW)[i] will contain orientation of i'th feature
//! keypoints.ptr<float>(HESSIAN_ROW)[i] will contain response of i'th feature
void operator()(const GpuMat& img, const GpuMat& mask, GpuMat& keypoints);
//! finds the keypoints and computes their descriptors.
//! Optionally it can compute descriptors for the user-provided keypoints and recompute keypoints direction
void operator()(const GpuMat& img, const GpuMat& mask, GpuMat& keypoints, GpuMat& descriptors,
bool useProvidedKeypoints = false);
/** @brief Finds the keypoints using fast hessian detector used in SURF
@param img Source image, currently supports only CV_8UC1 images.
@param mask A mask image same size as src and of type CV_8UC1.
@param keypoints Detected keypoints.
*/
CV_WRAP inline void detect(const GpuMat& img, const GpuMat& mask, CV_OUT GpuMat& keypoints) {
(*this)(img, mask, keypoints);
}
void operator()(const GpuMat& img, const GpuMat& mask, std::vector<KeyPoint>& keypoints);
void operator()(const GpuMat& img, const GpuMat& mask, std::vector<KeyPoint>& keypoints, GpuMat& descriptors,
bool useProvidedKeypoints = false);
/** @brief Finds the keypoints and computes their descriptors using fast hessian detector used in SURF
@param img Source image, currently supports only CV_8UC1 images.
@param mask A mask image same size as src and of type CV_8UC1.
@param keypoints Detected keypoints.
@param descriptors Keypoint descriptors.
@param useProvidedKeypoints Compute descriptors for the user-provided keypoints and recompute keypoints direction.
*/
CV_WRAP inline void detectWithDescriptors(const GpuMat& img, const GpuMat& mask, CV_OUT GpuMat& keypoints, CV_OUT GpuMat& descriptors,
bool useProvidedKeypoints = false) {
(*this)(img, mask, keypoints, descriptors, useProvidedKeypoints);
}
void operator()(const GpuMat& img, const GpuMat& mask, std::vector<KeyPoint>& keypoints, std::vector<float>& descriptors,
bool useProvidedKeypoints = false);
void releaseMemory();
// SURF parameters
CV_PROP double hessianThreshold;
CV_PROP int nOctaves;
CV_PROP int nOctaveLayers;
CV_PROP bool extended;
CV_PROP bool upright;
//! max keypoints = min(keypointsRatio * img.size().area(), 65535)
CV_PROP float keypointsRatio;
GpuMat sum, mask1, maskSum;
GpuMat det, trace;
GpuMat maxPosBuffer;
};
//! @}
}} // namespace cv { namespace cuda {
#endif // __OPENCV_XFEATURES2D_CUDA_HPP__
@@ -0,0 +1,127 @@
/*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_XFEATURES2D_FEATURES_2D_HPP__
#define __OPENCV_XFEATURES2D_FEATURES_2D_HPP__
#include "opencv2/features.hpp"
namespace cv
{
namespace xfeatures2d
{
//! @addtogroup xfeatures2d_nonfree
//! @{
/** @brief Class for extracting Speeded Up Robust Features from an image @cite Bay06 .
The algorithm parameters:
- member int extended
- 0 means that the basic descriptors (64 elements each) shall be computed
- 1 means that the extended descriptors (128 elements each) shall be computed
- member int upright
- 0 means that detector computes orientation of each feature.
- 1 means that the orientation is not computed (which is much, much faster). For example,
if you match images from a stereo pair, or do image stitching, the matched features
likely have very similar angles, and you can speed up feature extraction by setting
upright=1.
- member double hessianThreshold
Threshold for the keypoint detector. Only features, whose hessian is larger than
hessianThreshold are retained by the detector. Therefore, the larger the value, the less
keypoints you will get. A good default value could be from 300 to 500, depending from the
image contrast.
- member int nOctaves
The number of a gaussian pyramid octaves that the detector uses. It is set to 4 by default.
If you want to get very large features, use the larger value. If you want just small
features, decrease it.
- member int nOctaveLayers
The number of images within each octave of a gaussian pyramid. It is set to 2 by default.
@note
- An example using the SURF feature detector can be found at
opencv_source_code/samples/cpp/generic_descriptor_match.cpp
- Another example using the SURF feature detector, extractor and matcher can be found at
opencv_source_code/samples/cpp/matcher_simple.cpp
*/
class CV_EXPORTS_W SURF : public Feature2D
{
public:
/**
@param hessianThreshold Threshold for hessian keypoint detector used in SURF.
@param nOctaves Number of pyramid octaves the keypoint detector will use.
@param nOctaveLayers Number of octave layers within each octave.
@param extended Extended descriptor flag (true - use extended 128-element descriptors; false - use
64-element descriptors).
@param upright Up-right or rotated features flag (true - do not compute orientation of features;
false - compute orientation).
*/
CV_WRAP static Ptr<SURF> create(double hessianThreshold=100,
int nOctaves = 4, int nOctaveLayers = 3,
bool extended = false, bool upright = false);
CV_WRAP virtual void setHessianThreshold(double hessianThreshold) = 0;
CV_WRAP virtual double getHessianThreshold() const = 0;
CV_WRAP virtual void setNOctaves(int nOctaves) = 0;
CV_WRAP virtual int getNOctaves() const = 0;
CV_WRAP virtual void setNOctaveLayers(int nOctaveLayers) = 0;
CV_WRAP virtual int getNOctaveLayers() const = 0;
CV_WRAP virtual void setExtended(bool extended) = 0;
CV_WRAP virtual bool getExtended() const = 0;
CV_WRAP virtual void setUpright(bool upright) = 0;
CV_WRAP virtual bool getUpright() const = 0;
CV_WRAP String getDefaultName() const CV_OVERRIDE;
};
typedef SURF SurfFeatureDetector;
typedef SURF SurfDescriptorExtractor;
//! @}
}
} /* namespace cv */
#endif
@@ -0,0 +1,85 @@
package org.opencv.test.features;
import org.opencv.test.OpenCVTestCase;
import org.opencv.test.OpenCVTestRunner;
import org.opencv.xfeatures2d.AgastFeatureDetector;
public class AGASTFeatureDetectorTest extends OpenCVTestCase {
AgastFeatureDetector detector;
@Override
protected void setUp() throws Exception {
super.setUp();
detector = AgastFeatureDetector.create(); // default (10,true,3)
}
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 testRead() {
String filename = OpenCVTestRunner.getTempFileName("xml");
writeFile(filename, "<?xml version=\"1.0\"?>\n<opencv_storage>\n<name>Feature2D.AgastFeatureDetector</name>\n<threshold>11</threshold>\n<nonmaxSuppression>0</nonmaxSuppression>\n<type>2</type>\n</opencv_storage>\n");
detector.read(filename);
assertEquals(11, detector.getThreshold());
assertEquals(false, detector.getNonmaxSuppression());
assertEquals(2, detector.getType());
}
public void testReadYml() {
String filename = OpenCVTestRunner.getTempFileName("yml");
writeFile(filename, "%YAML:1.0\n---\nname: \"Feature2D.AgastFeatureDetector\"\nthreshold: 11\nnonmaxSuppression: 0\ntype: 2\n");
detector.read(filename);
assertEquals(11, detector.getThreshold());
assertEquals(false, detector.getNonmaxSuppression());
assertEquals(2, detector.getType());
}
public void testWrite() {
String filename = OpenCVTestRunner.getTempFileName("xml");
detector.write(filename);
String truth = "<?xml version=\"1.0\"?>\n<opencv_storage>\n<name>Feature2D.AgastFeatureDetector</name>\n<threshold>10</threshold>\n<nonmaxSuppression>1</nonmaxSuppression>\n<type>3</type>\n</opencv_storage>\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);
}
public void testWriteYml() {
String filename = OpenCVTestRunner.getTempFileName("yml");
detector.write(filename);
String truth = "%YAML 1.2\n---\nname: \"Feature2D.AgastFeatureDetector\"\nthreshold: 10\nnonmaxSuppression: true\ntype: 3\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,67 @@
package org.opencv.test.features;
import org.opencv.test.OpenCVTestCase;
import org.opencv.test.OpenCVTestRunner;
import org.opencv.xfeatures2d.AKAZE;
public class AKAZEDescriptorExtractorTest extends OpenCVTestCase {
AKAZE extractor;
@Override
protected void setUp() throws Exception {
super.setUp();
extractor = AKAZE.create(); // default (5,0,3,0.001f,4,4,1)
}
public void testCreate() {
assertNotNull(extractor);
}
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---\nformat: 3\nname: \"Feature2D.AKAZE\"\ndescriptor: 4\ndescriptor_channels: 2\ndescriptor_size: 32\nthreshold: 0.125\noctaves: 3\nsublevels: 5\ndiffusivity: 2\n");
extractor.read(filename);
assertEquals(4, extractor.getDescriptorType());
assertEquals(2, extractor.getDescriptorChannels());
assertEquals(32, extractor.getDescriptorSize());
assertEquals(0.125, extractor.getThreshold());
assertEquals(3, extractor.getNOctaves());
assertEquals(5, extractor.getNOctaveLayers());
assertEquals(2, extractor.getDiffusivity());
}
public void testWriteYml() {
String filename = OpenCVTestRunner.getTempFileName("yml");
extractor.write(filename);
String truth = "%YAML 1.2\n---\nformat: 3\nname: \"Feature2D.AKAZE\"\ndescriptor: 5\ndescriptor_channels: 3\ndescriptor_size: 0\nthreshold: 0.0010000000474974513\noctaves: 4\nsublevels: 4\ndiffusivity: 1\nmax_points: -1\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,48 @@
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.features.DescriptorMatcher;
import org.opencv.xfeatures2d.BOWImgDescriptorExtractor;
import org.opencv.test.OpenCVTestCase;
import org.opencv.test.OpenCVTestRunner;
import org.opencv.imgproc.Imgproc;
public class BOWImgDescriptorExtractorTest extends OpenCVTestCase {
ORB extractor;
DescriptorMatcher matcher;
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();
matcher = DescriptorMatcher.create(DescriptorMatcher.BRUTEFORCE);
matSize = 100;
}
public void testCreate() {
BOWImgDescriptorExtractor bow = new BOWImgDescriptorExtractor(extractor, matcher);
}
}
@@ -0,0 +1,102 @@
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.test.OpenCVTestCase;
import org.opencv.test.OpenCVTestRunner;
import org.opencv.imgproc.Imgproc;
import org.opencv.features.Feature2D;
public class BRIEFDescriptorExtractorTest extends OpenCVTestCase {
Feature2D extractor;
int matSize;
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 = createClassInstance(XFEATURES2D+"BriefDescriptorExtractor", DEFAULT_FACTORY, null, null);
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, 96, 0, 76, 24, 47, 182, 68, 137,
149, 195, 67, 16, 187, 224, 74, 8,
82, 169, 87, 70, 44, 4, 192, 56,
13, 128, 44, 106, 146, 72, 194, 245);
}
};
assertMatEqual(truth, descriptors);
}
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"); // BRIEF does not override empty() method
}
public void testRead() {
String filename = OpenCVTestRunner.getTempFileName("yml");
writeFile(filename, "%YAML:1.0\n---\ndescriptorSize: 64\n");
extractor.read(filename);
assertEquals(64, extractor.descriptorSize());
}
public void testWrite() {
String filename = OpenCVTestRunner.getTempFileName("xml");
extractor.write(filename);
String truth = "<?xml version=\"1.0\"?>\n<opencv_storage>\n<name>Feature2D.BRIEF</name>\n<descriptorSize>32</descriptorSize>\n<use_orientation>0</use_orientation>\n</opencv_storage>\n";
assertEquals(truth, readFile(filename));
}
public void testWriteYml() {
String filename = OpenCVTestRunner.getTempFileName("yml");
extractor.write(filename);
String truth = "%YAML 1.2\n---\nname: \"Feature2D.BRIEF\"\ndescriptorSize: 32\nuse_orientation: false\n";
assertEquals(truth, readFile(filename));
}
}
@@ -0,0 +1,63 @@
package org.opencv.test.features;
import org.opencv.test.OpenCVTestCase;
import org.opencv.test.OpenCVTestRunner;
import org.opencv.xfeatures2d.BRISK;
public class BRISKDescriptorExtractorTest extends OpenCVTestCase {
BRISK extractor;
@Override
protected void setUp() throws Exception {
super.setUp();
extractor = BRISK.create(); // default (30,3,1)
}
public void testCreate() {
assertNotNull(extractor);
}
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.BRISK\"\nthreshold: 31\noctaves: 4\npatternScale: 1.1\n");
extractor.read(filename);
assertEquals(31, extractor.getThreshold());
assertEquals(4, extractor.getOctaves());
assertEquals(1.1f, extractor.getPatternScale());
}
public void testWriteYml() {
String filename = OpenCVTestRunner.getTempFileName("yml");
extractor.write(filename);
String truth = "%YAML 1.2\n---\nname: \"Feature2D.BRISK\"\nthreshold: 30\noctaves: 3\npatternScale: 1.\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,67 @@
package org.opencv.test.features;
import org.opencv.test.OpenCVTestCase;
import org.opencv.test.OpenCVTestRunner;
import org.opencv.xfeatures2d.DAISY;
public class DAISYDescriptorExtractorTest extends OpenCVTestCase {
DAISY extractor;
@Override
protected void setUp() throws Exception {
super.setUp();
extractor = DAISY.create(); // default (15, 3, 8, 8, 100, noArray, true, false)
}
public void testCreate() {
assertNotNull(extractor);
}
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.DAISY\"\nradius: 16.\nq_radius: 4\nq_theta: 9\nq_hist: 10\nnorm_type: 101\nenable_interpolation: 0\nuse_orientation: 1\n");
extractor.read(filename);
assertEquals(16.0f, extractor.getRadius());
assertEquals(4, extractor.getQRadius());
assertEquals(9, extractor.getQTheta());
assertEquals(10, extractor.getQHist());
assertEquals(101, extractor.getNorm());
assertEquals(false, extractor.getInterpolation());
assertEquals(true, extractor.getUseOrientation());
}
public void testWriteYml() {
String filename = OpenCVTestRunner.getTempFileName("yml");
extractor.write(filename);
String truth = "%YAML 1.2\n---\nname: \"Feature2D.DAISY\"\nradius: 15.\nq_radius: 3\nq_theta: 8\nq_hist: 8\nnorm_type: 100\nenable_interpolation: true\nuse_orientation: false\n";
String actual = readFile(filename);
actual = actual.replaceAll("e([+-])0(\\d\\d)", "e$1$2"); // NOTE: workaround for different platforms double representation
assertEquals(truth, actual);
}
}
@@ -0,0 +1,64 @@
package org.opencv.test.features;
import org.opencv.test.OpenCVTestCase;
import org.opencv.test.OpenCVTestRunner;
import org.opencv.xfeatures2d.FREAK;
public class FREAKDescriptorExtractorTest extends OpenCVTestCase {
FREAK extractor;
@Override
protected void setUp() throws Exception {
super.setUp();
extractor = FREAK.create(); // default (true,true,22,4)
}
public void testCreate() {
assertNotNull(extractor);
}
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.FREAK\"\norientationNormalized: 0\nscaleNormalized: 0\npatternScale: 23.\nnOctaves: 5\n");
extractor.read(filename);
assertEquals(false, extractor.getOrientationNormalized());
assertEquals(false, extractor.getScaleNormalized());
assertEquals(23.0, extractor.getPatternScale());
assertEquals(5, extractor.getNOctaves());
}
public void testWriteYml() {
String filename = OpenCVTestRunner.getTempFileName("yml");
extractor.write(filename);
String truth = "%YAML 1.2\n---\nname: \"Feature2D.FREAK\"\norientationNormalized: true\nscaleNormalized: true\npatternScale: 22.\nnOctaves: 4\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,65 @@
package org.opencv.test.features;
import org.opencv.test.OpenCVTestCase;
import org.opencv.test.OpenCVTestRunner;
import org.opencv.xfeatures2d.HarrisLaplaceFeatureDetector;
public class HARRISFeatureDetectorTest extends OpenCVTestCase {
HarrisLaplaceFeatureDetector detector;
@Override
protected void setUp() throws Exception {
super.setUp();
detector = HarrisLaplaceFeatureDetector.create(); // default constructor have (6, 0.01, 0.01, 5000, 4)
}
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.HARRIS-LAPLACE\"\nnumOctaves: 5\ncorn_thresh: 0.02\nDOG_thresh: 0.03\nmaxCorners: 4000\nnum_layers: 2\n");
detector.read(filename);
assertEquals(5, detector.getNumOctaves());
assertEquals(0.02f, detector.getCornThresh());
assertEquals(0.03f, detector.getDOGThresh());
assertEquals(4000, detector.getMaxCorners());
assertEquals(2, detector.getNumLayers());
}
public void testWriteYml() {
String filename = OpenCVTestRunner.getTempFileName("yml");
detector.write(filename);
String truth = "%YAML:1.0\n---\nname: \"Feature2D.HARRIS-LAPLACE\"\nnumOctaves: 6\ncorn_thresh: 9.9999997764825821e-03\nDOG_thresh: 9.9999997764825821e-03\nmaxCorners: 5000\nnum_layers: 4\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,66 @@
package org.opencv.test.features;
import org.opencv.test.OpenCVTestCase;
import org.opencv.test.OpenCVTestRunner;
import org.opencv.xfeatures2d.KAZE;
public class KAZEDescriptorExtractorTest extends OpenCVTestCase {
KAZE extractor;
@Override
protected void setUp() throws Exception {
super.setUp();
extractor = KAZE.create(); // default (false,false,0.001f,4,4,1)
}
public void testCreate() {
assertNotNull(extractor);
}
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---\nformat: 3\nname: \"Feature2D.KAZE\"\nextended: 1\nupright: 1\nthreshold: 0.125\noctaves: 3\nsublevels: 5\ndiffusivity: 2\n");
extractor.read(filename);
assertEquals(true, extractor.getExtended());
assertEquals(true, extractor.getUpright());
assertEquals(0.125, extractor.getThreshold());
assertEquals(3, extractor.getNOctaves());
assertEquals(5, extractor.getNOctaveLayers());
assertEquals(2, extractor.getDiffusivity());
}
public void testWriteYml() {
String filename = OpenCVTestRunner.getTempFileName("yml");
extractor.write(filename);
String truth = "%YAML 1.2\n---\nformat: 3\nname: \"Feature2D.KAZE\"\nextended: 0\nupright: 0\nthreshold: 0.0010000000474974513\noctaves: 4\nsublevels: 4\ndiffusivity: 1\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,64 @@
package org.opencv.test.features;
import org.opencv.test.OpenCVTestCase;
import org.opencv.test.OpenCVTestRunner;
import org.opencv.xfeatures2d.LATCH;
public class LATCHDescriptorExtractorTest extends OpenCVTestCase {
LATCH extractor;
@Override
protected void setUp() throws Exception {
super.setUp();
extractor = LATCH.create(); // default (32,true,3,2.0)
}
public void testCreate() {
assertNotNull(extractor);
}
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.LATCH\"\ndescriptorSize: 64\nrotationInvariance: 0\nhalf_ssd_size: 5\nsigma: 3.\n");
extractor.read(filename);
assertEquals(64, extractor.getBytes());
assertEquals(false, extractor.getRotationInvariance());
assertEquals(5, extractor.getHalfSSDsize());
assertEquals(3.0, extractor.getSigma());
}
public void testWriteYml() {
String filename = OpenCVTestRunner.getTempFileName("yml");
extractor.write(filename);
String truth = "%YAML 1.2\n---\nname: \"Feature2D.LATCH\"\ndescriptorSize: 32\nrotationInvariance: true\nhalf_ssd_size: 3\nsigma: 2.\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,62 @@
package org.opencv.test.features;
import org.opencv.test.OpenCVTestCase;
import org.opencv.test.OpenCVTestRunner;
import org.opencv.xfeatures2d.LUCID;
public class LUCIDDescriptorExtractorTest extends OpenCVTestCase {
LUCID extractor;
@Override
protected void setUp() throws Exception {
super.setUp();
extractor = LUCID.create(); // default (1,2)
}
public void testCreate() {
assertNotNull(extractor);
}
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.LUCID\"\nlucid_kernel: 2\nblur_kernel: 3\n");
extractor.read(filename);
assertEquals(2, extractor.getLucidKernel());
assertEquals(3, extractor.getBlurKernel());
}
public void testWriteYml() {
String filename = OpenCVTestRunner.getTempFileName("yml");
extractor.write(filename);
String truth = "%YAML 1.2\n---\nname: \"Feature2D.LUCID\"\nlucid_kernel: 1\nblur_kernel: 2\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,69 @@
package org.opencv.test.features;
import org.opencv.test.OpenCVTestCase;
import org.opencv.test.OpenCVTestRunner;
import org.opencv.xfeatures2d.MSDDetector;
public class MSDFeatureDetectorTest extends OpenCVTestCase {
MSDDetector detector;
@Override
protected void setUp() throws Exception {
super.setUp();
detector = MSDDetector.create(); // default (3,5,5,0,250.4,',1.25,-1,false)
}
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.MSD\"\npatch_radius: 4\nsearch_area_radius: 6\nnms_radius: 7\nnms_scale_radius: 1\nth_saliency: 251.\nkNN: 2\nscale_factor: 1.26\nn_scales: 3\ncompute_orientation: 1\n");
detector.read(filename);
assertEquals(4, detector.getPatchRadius());
assertEquals(6, detector.getSearchAreaRadius());
assertEquals(7, detector.getNmsRadius());
assertEquals(1, detector.getNmsScaleRadius());
assertEquals(251.0f, detector.getThSaliency());
assertEquals(2, detector.getKNN());
assertEquals(1.26f, detector.getScaleFactor());
assertEquals(3, detector.getNScales());
assertEquals(true, detector.getComputeOrientation());
}
public void testWriteYml() {
String filename = OpenCVTestRunner.getTempFileName("yml");
detector.write(filename);
String truth = "%YAML 1.2\n---\nname: \"Feature2D.MSD\"\npatch_radius: 3\nsearch_area_radius: 5\nnms_radius: 5\nnms_scale_radius: 0\nth_saliency: 250.\nkNN: 4\nscale_factor: 1.25\nn_scales: -1\ncompute_orientation: false\n";
String actual = readFile(filename);
actual = actual.replaceAll("e([+-])0(\\d\\d)", "e$1$2"); // NOTE: workaround for different platforms double representation
assertEquals(truth, actual);
}
}
@@ -0,0 +1,128 @@
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.xfeatures2d.StarDetector;
public class STARFeatureDetectorTest extends OpenCVTestCase {
StarDetector 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() {
Scalar color = new Scalar(0);
int center = matSize / 2;
int radius = 6;
int offset = 40;
Mat img = new Mat(matSize, matSize, CvType.CV_8U, new Scalar(255));
Imgproc.circle(img, new Point(center - offset, center), radius, color, -1);
Imgproc.circle(img, new Point(center + offset, center), radius, color, -1);
Imgproc.circle(img, new Point(center, center - offset), radius, color, -1);
Imgproc.circle(img, new Point(center, center + offset), radius, color, -1);
Imgproc.circle(img, new Point(center, center), radius, color, -1);
return img;
}
protected void setUp() throws Exception {
super.setUp();
detector = createClassInstance(XFEATURES2D+"StarDetector", DEFAULT_FACTORY, null, null);
matSize = 200;
truth = new KeyPoint[] {
new KeyPoint( 95, 80, 22, -1, 31.5957f, 0, -1),
new KeyPoint(105, 80, 22, -1, 31.5957f, 0, -1),
new KeyPoint( 80, 95, 22, -1, 31.5957f, 0, -1),
new KeyPoint(120, 95, 22, -1, 31.5957f, 0, -1),
new KeyPoint(100, 100, 8, -1, 30.f, 0, -1),
new KeyPoint( 80, 105, 22, -1, 31.5957f, 0, -1),
new KeyPoint(120, 105, 22, -1, 31.5957f, 0, -1),
new KeyPoint( 95, 120, 22, -1, 31.5957f, 0, -1),
new KeyPoint(105, 120, 22, -1, 31.5957f, 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[0], truth[2], truth[5], truth[7]), 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\n---\nname: \"Feature2D.STAR\"\nmaxSize: 45\nresponseThreshold: 150\nlineThresholdProjected: 10\nlineThresholdBinarized: 8\nsuppressNonmaxSize: 5\n");
detector.read(filename);
assertEquals(45, detector.getMaxSize());
assertEquals(150, detector.getResponseThreshold());
assertEquals(10, detector.getLineThresholdProjected());
assertEquals(8, detector.getLineThresholdBinarized());
assertEquals(5, detector.getSuppressNonmaxSize());
MatOfKeyPoint keypoints2 = new MatOfKeyPoint();
detector.detect(img, keypoints2);
assertTrue(keypoints2.total() <= keypoints1.total());
}
public void testWriteYml() {
String filename = OpenCVTestRunner.getTempFileName("yml");
detector.write(filename);
String truth = "%YAML 1.2\n---\nname: \"Feature2D.STAR\"\nmaxSize: 45\nresponseThreshold: 30\nlineThresholdProjected: 10\nlineThresholdBinarized: 8\nsuppressNonmaxSize: 5\n";
assertEquals(truth, readFile(filename));
}
}
@@ -0,0 +1,115 @@
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.test.OpenCVTestCase;
import org.opencv.test.OpenCVTestRunner;
import org.opencv.imgproc.Imgproc;
import org.opencv.xfeatures2d.SURF;
public class SURFDescriptorExtractorTest extends OpenCVTestCase {
SURF extractor;
int matSize;
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();
Class[] cParams = {double.class, int.class, int.class, boolean.class, boolean.class};
Object[] oValues = {100, 2, 4, true, false};
extractor = createClassInstance(XFEATURES2D+"SURF", DEFAULT_FACTORY, cParams, oValues);
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, 128, CvType.CV_32FC1) {
{
put(0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0.058821894, 0.058821894, -0.045962855, 0.046261817, 0.0085156476,
0.0085754395, -0.0064509804, 0.0064509804, 0.00044069235, 0.00044069235, 0, 0, 0.00025723741,
0.00025723741, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.00025723741, 0.00025723741, -0.00044069235,
0.00044069235, 0, 0, 0.36278215, 0.36278215, -0.24688604, 0.26173124, 0.052068226, 0.052662034,
-0.032815345, 0.032815345, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -0.0064523756,
0.0064523756, 0.0082002236, 0.0088908644, -0.059001274, 0.059001274, 0.045789491, 0.04648013,
0.11961588, 0.22789426, -0.01322381, 0.18291828, -0.14042182, 0.23973691, 0.073782086, 0.23769434,
-0.027880307, 0.027880307, 0.049587864, 0.049587864, -0.33991757, 0.33991757, 0.21437603, 0.21437603,
-0.0020763327, 0.0020763327, 0.006245892, 0.006245892, -0.04067041, 0.04067041, 0.019361559,
0.019361559, 0, 0, -0.0035977389, 0.0035977389, 0, 0, -0.00099993451, 0.00099993451, 0.040670406,
0.040670406, -0.019361559, 0.019361559, 0.006245892, 0.006245892, -0.0020763327, 0.0020763327,
-0.00034532088, 0.00034532088, 0, 0, 0, 0, 0.00034532088, 0.00034532088, -0.00099993451,
0.00099993451, 0, 0, 0, 0, 0.0035977389, 0.0035977389
);
}
};
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");
}
public void testReadYml() {
String filename = OpenCVTestRunner.getTempFileName("yml");
writeFile(filename, "%YAML:1.0\n---\nname: \"Feature2D.SURF\"\nhessianThreshold: 100.\nextended: 1\nupright: 0\nnOctaves: 2\nnOctaveLayers: 4\n");
extractor.read(filename);
assertEquals(128, extractor.descriptorSize());
assertEquals(true, extractor.getExtended());
assertEquals(false, extractor.getUpright());
assertEquals(2, extractor.getNOctaves());
assertEquals(4, extractor.getNOctaveLayers());
assertEquals(100., extractor.getHessianThreshold());
}
public void testWriteYml() {
String filename = OpenCVTestRunner.getTempFileName("yml");
extractor.write(filename);
String truth = "%YAML 1.2\n---\nname: \"Feature2D.SURF\"\nhessianThreshold: 100.\nextended: true\nupright: false\nnOctaves: 2\nnOctaveLayers: 4\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,176 @@
package org.opencv.test.features;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
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.xfeatures2d.SURF;
public class SURFFeatureDetectorTest extends OpenCVTestCase {
SURF 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() {
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;
}
private void order(List<KeyPoint> points) {
Collections.sort(points, new Comparator<KeyPoint>() {
public int compare(KeyPoint p1, KeyPoint p2) {
if (p1.angle < p2.angle)
return -1;
if (p1.angle > p2.angle)
return 1;
return 0;
}
});
}
@Override
protected void setUp() throws Exception {
super.setUp();
detector = createClassInstance(XFEATURES2D + "SURF", DEFAULT_FACTORY, null, null);
matSize = 100;
truth = new KeyPoint[] {
new KeyPoint(55.775578f, 55.775578f, 16, 80.245735f, 8617.8633f, 0, -1),
new KeyPoint(44.224422f, 55.775578f, 16, 170.24574f, 8617.8633f, 0, -1),
new KeyPoint(44.224422f, 44.224422f, 16, 260.24573f, 8617.8633f, 0, -1),
new KeyPoint(55.775578f, 44.224422f, 16, 350.24573f, 8617.8633f, 0, -1)
};
}
public void testCreate() {
assertNotNull(detector);
}
public void testDetectListOfMatListOfListOfKeyPoint() {
setProperty(detector, "hessianThreshold", "double", 8000);
setProperty(detector, "nOctaves", "int", 3);
setProperty(detector, "nOctaveLayers", "int", 4);
setProperty(detector, "upright", "boolean", false);
setProperty(detector, "extended", "boolean", true);
List<MatOfKeyPoint> keypoints = new ArrayList<MatOfKeyPoint>();
Mat cross = getTestImg();
List<Mat> crosses = new ArrayList<Mat>(3);
crosses.add(cross);
crosses.add(cross);
crosses.add(cross);
detector.detect(crosses, keypoints);
assertEquals(3, keypoints.size());
for (MatOfKeyPoint mkp : keypoints) {
List<KeyPoint> lkp = mkp.toList();
order(lkp);
assertListKeyPointEquals(Arrays.asList(truth), lkp, EPS);
}
}
public void testDetectListOfMatListOfListOfKeyPointListOfMat() {
fail("Not yet implemented");
}
public void testDetectMatListOfKeyPoint() {
setProperty(detector, "hessianThreshold", "double", 8000);
setProperty(detector, "nOctaves", "int", 3);
setProperty(detector, "nOctaveLayers", "int", 4);
setProperty(detector, "upright", "boolean", false);
MatOfKeyPoint keypoints = new MatOfKeyPoint();
Mat cross = getTestImg();
detector.detect(cross, keypoints);
List<KeyPoint> lkp = keypoints.toList();
order(lkp);
assertListKeyPointEquals(Arrays.asList(truth), lkp, EPS);
}
public void testDetectMatListOfKeyPointMat() {
setProperty(detector, "hessianThreshold", "double", 8000);
setProperty(detector, "nOctaves", "int", 3);
setProperty(detector, "nOctaveLayers", "int", 4);
setProperty(detector, "upright", "boolean", false);
setProperty(detector, "extended", "boolean", true);
Mat img = getTestImg();
Mat mask = getMaskImg();
MatOfKeyPoint keypoints = new MatOfKeyPoint();
detector.detect(img, keypoints, mask);
List<KeyPoint> lkp = keypoints.toList();
order(lkp);
assertListKeyPointEquals(Arrays.asList(truth[1], truth[2]), lkp, EPS);
}
public void testEmpty() {
// assertFalse(detector.empty());
fail("Not yet implemented");
}
public void testReadYml() {
Mat cross = getTestImg();
MatOfKeyPoint keypoints1 = new MatOfKeyPoint();
detector.detect(cross, keypoints1);
String filename = OpenCVTestRunner.getTempFileName("xml");
writeFile(filename, "<?xml version=\"1.0\"?>\n<opencv_storage>\n<name>Feature2D.SURF</name>\n<hessianThreshold>8000.</hessianThreshold>\n<extended>1</extended>\n<upright>0</upright>\n<nOctaves>3</nOctaves>\n<nOctaveLayers>4</nOctaveLayers>\n</opencv_storage>\n");
detector.read(filename);
assertEquals(128, detector.descriptorSize());
assertEquals(8000., detector.getHessianThreshold());
assertEquals(true, detector.getExtended());
assertEquals(false, detector.getUpright());
assertEquals(3, detector.getNOctaves());
assertEquals(4, detector.getNOctaveLayers());
MatOfKeyPoint keypoints2 = new MatOfKeyPoint();
detector.detect(cross, keypoints2);
assertTrue(keypoints2.total() <= keypoints1.total());
}
public void testWriteYml() {
String filename = OpenCVTestRunner.getTempFileName("yml");
detector.write(filename);
String truth = "%YAML 1.2\n---\nname: \"Feature2D.SURF\"\nhessianThreshold: 100.\nextended: false\nupright: false\nnOctaves: 4\nnOctaveLayers: 3\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,9 @@
{
"whitelist":
{
"BRISK": ["create", "getDefaultName"],
"AgastFeatureDetector": ["create", "setThreshold", "getThreshold", "setNonmaxSuppression", "getNonmaxSuppression", "setType", "getType", "getDefaultName"],
"KAZE": ["create", "setExtended", "getExtended", "setUpright", "getUpright", "setThreshold", "getThreshold", "setNOctaves", "getNOctaves", "setNOctaveLayers", "getNOctaveLayers", "setDiffusivity", "getDiffusivity", "getDefaultName"],
"AKAZE": ["create", "setDescriptorType", "getDescriptorType", "setDescriptorSize", "getDescriptorSize", "setDescriptorChannels", "getDescriptorChannels", "setThreshold", "getThreshold", "setNOctaves", "getNOctaves", "setNOctaveLayers", "getNOctaveLayers", "setDiffusivity", "getDiffusivity", "getDefaultName"]
}
}
@@ -0,0 +1,20 @@
{
"class_ignore_list" : [
"SURF_CUDA"
],
"AdditionalImports" : {
"*" : [ "\"xfeatures2d.hpp\"" ]
},
"func_arg_fix" : {
"DAISY" : {
"create" : { "norm" : { "ctype" : "NormalizationType",
"defval" : "cv::xfeatures2d::DAISY::NRM_NONE"} }
},
"PCTSignatures" : {
"(PCTSignatures*)create:(NSArray<Point2f*>*)initSamplingPoints initSeedCount:(int)initSeedCount" : { "create" : {"name" : "create2"} }
}
},
"enum_fix" : {
"AgastFeatureDetector" : { "DetectorType": "AgastDetectorType" }
}
}
@@ -0,0 +1,2 @@
// Compatibility
#include "shadow_sift.hpp"
@@ -0,0 +1,12 @@
#ifdef HAVE_OPENCV_XFEATURES2D
#include "opencv2/xfeatures2d.hpp"
using namespace cv::xfeatures2d;
typedef DAISY::NormalizationType DAISY_NormalizationType;
typedef AKAZE::DescriptorType AKAZE_DescriptorType;
typedef AgastFeatureDetector::DetectorType AgastFeatureDetector_DetectorType;
typedef KAZE::DiffusivityType KAZE_DiffusivityType;
#endif
@@ -0,0 +1,20 @@
// Compatibility
// SIFT is moved to the main repository
namespace cv {
namespace xfeatures2d {
/** Use cv.SIFT_create() instead */
CV_WRAP static inline
Ptr<cv::SIFT> SIFT_create(int nfeatures = 0, int nOctaveLayers = 3,
double contrastThreshold = 0.04, double edgeThreshold = 10,
double sigma = 1.6)
{
CV_LOG_ONCE_WARNING(NULL, "DEPRECATED: cv.xfeatures2d.SIFT_create() is deprecated due SIFT tranfer to the main repository. "
"https://github.com/opencv/opencv/issues/16736"
);
return SIFT::create(nfeatures, nOctaveLayers, contrastThreshold, edgeThreshold, sigma);
}
}} // namespace
@@ -0,0 +1,48 @@
#!/usr/bin/env python
import os
import cv2 as cv
import numpy as np
from tests_common import NewOpenCVTests, unittest
class xfeatures2d_test(NewOpenCVTests):
def setUp(self):
super(xfeatures2d_test, self).setUp()
if not cv.cuda.getCudaEnabledDeviceCount():
self.skipTest("No CUDA-capable device is detected")
@unittest.skipIf('OPENCV_TEST_DATA_PATH' not in os.environ,
"OPENCV_TEST_DATA_PATH is not defined")
def test_surf(self):
img_path = os.environ['OPENCV_TEST_DATA_PATH'] + "/gpu/features2d/aloe.png"
hessianThreshold = 100
nOctaves = 3
nOctaveLayers = 2
extended = False
keypointsRatio = 0.05
upright = False
npMat = cv.cvtColor(cv.imread(img_path),cv.COLOR_BGR2GRAY)
cuMat = cv.cuda_GpuMat(npMat)
try:
cuSurf = cv.cuda_SURF_CUDA.create(hessianThreshold,nOctaves,nOctaveLayers,extended,keypointsRatio,upright)
surf = cv.xfeatures2d_SURF.create(hessianThreshold,nOctaves,nOctaveLayers,extended,upright)
except cv.error as e:
self.assertEqual(e.code, cv.Error.StsNotImplemented)
self.skipTest("OPENCV_ENABLE_NONFREE is not enabled in this build.")
cuKeypoints = cuSurf.detect(cuMat,cv.cuda_GpuMat())
keypointsHost = cuSurf.downloadKeypoints(cuKeypoints)
keypoints = surf.detect(npMat)
self.assertTrue(len(keypointsHost) == len(keypoints))
cuKeypoints, cuDescriptors = cuSurf.detectWithDescriptors(cuMat,cv.cuda_GpuMat(),cuKeypoints,useProvidedKeypoints=True)
keypointsHost = cuSurf.downloadKeypoints(cuKeypoints)
descriptorsHost = cuDescriptors.download()
keypoints, descriptors = surf.compute(npMat,keypoints)
self.assertTrue(len(keypointsHost) == len(keypoints) and descriptorsHost.shape == descriptors.shape)
if __name__ == '__main__':
NewOpenCVTests.bootstrap()
@@ -0,0 +1,36 @@
#!/usr/bin/env python
# Python 2/3 compatibility
from __future__ import print_function
import os
import numpy as np
import cv2 as cv
from tests_common import NewOpenCVTests
class MSDDetector_test(NewOpenCVTests):
def test_create(self):
msd = cv.xfeatures2d.MSDDetector_create()
self.assertFalse(msd is None)
img1 = np.zeros((100, 100, 3), dtype=np.uint8)
kp1_ = msd.detect(img1, None)
class matchLOGOS_test(NewOpenCVTests):
def test_basic(self):
frame = self.get_sample('python/images/baboon.png', cv.IMREAD_COLOR)
detector = cv.xfeatures2d.AKAZE_create(threshold = 0.003)
keypoints1, descrs1 = detector.detectAndCompute(frame, None)
keypoints2, descrs2 = detector.detectAndCompute(frame, None)
matches1to2 = cv.xfeatures2d.matchLOGOS(keypoints1, keypoints2, range(len(keypoints1)), range(len(keypoints2)))
self.assertFalse(matches1to2 is None)
if __name__ == '__main__':
NewOpenCVTests.bootstrap()
@@ -0,0 +1,24 @@
#!/usr/bin/env python
# Python 2/3 compatibility
from __future__ import print_function
import os
import numpy as np
import cv2 as cv
from tests_common import NewOpenCVTests
class sift_compatibility_test(NewOpenCVTests):
def test_create(self):
sift = cv.xfeatures2d.SIFT_create()
self.assertFalse(sift is None)
img1 = np.zeros((100, 100, 3), dtype=np.uint8)
kp1_, des1_ = sift.detectAndCompute(img1, None)
if __name__ == '__main__':
NewOpenCVTests.bootstrap()
+36
View File
@@ -0,0 +1,36 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
#include "perf_precomp.hpp"
namespace opencv_test { namespace {
typedef perf::TestBaseWithParam<std::string> beblid;
#define BEBLID_IMAGES \
"cv/detectors_descriptors_evaluation/images_datasets/leuven/img1.png",\
"stitching/a3.png"
#ifdef OPENCV_ENABLE_NONFREE
PERF_TEST_P(beblid, extract, testing::Values(BEBLID_IMAGES))
{
string filename = getDataPath(GetParam());
Mat frame = imread(filename, IMREAD_GRAYSCALE);
ASSERT_FALSE(frame.empty()) << "Unable to load source image " << filename;
Mat mask;
declare.in(frame).time(90);
Ptr<SURF> detector = SURF::create();
vector<KeyPoint> points;
detector->detect(frame, points, mask);
Ptr<BEBLID> descriptor = BEBLID::create(6.25f);
cv::Mat descriptors;
TEST_CYCLE() descriptor->compute(frame, points, descriptors);
SANITY_CHECK_NOTHING();
}
#endif // NONFREE
}} // namespace
+33
View File
@@ -0,0 +1,33 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
#include "perf_precomp.hpp"
namespace opencv_test { namespace {
typedef perf::TestBaseWithParam<std::string> daisy;
#define DAISY_IMAGES \
"cv/detectors_descriptors_evaluation/images_datasets/leuven/img1.png",\
"stitching/a3.png"
PERF_TEST_P(daisy, extract, testing::Values(DAISY_IMAGES))
{
string filename = getDataPath(GetParam());
Mat frame = imread(filename, IMREAD_GRAYSCALE);
ASSERT_FALSE(frame.empty()) << "Unable to load source image " << filename;
Mat mask;
declare.in(frame).time(90);
Ptr<DAISY> descriptor = DAISY::create();
vector<KeyPoint> points;
Mat_<float> descriptors;
// compute all daisies in image
TEST_CYCLE() descriptor->compute(frame, descriptors);
SANITY_CHECK_NOTHING();
}
}} // namespace
@@ -0,0 +1,71 @@
#include "perf_feature2d.hpp"
namespace opencv_test
{
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 = AKAZE::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,60 @@
#ifndef __OPENCV_PERF_FEATURE2D_HPP__
#define __OPENCV_PERF_FEATURE2D_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 \
AGAST_DEFAULT, AGAST_5_8, AGAST_7_12d, AGAST_7_12s, AGAST_OAST_9_16
#define DETECTORS_EXTRACTORS \
AKAZE_DEFAULT, AKAZE_DESCRIPTOR_KAZE, \
BRISK_DEFAULT, \
KAZE_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 AGAST_DEFAULT:
return AgastFeatureDetector::create();
case AGAST_5_8:
return AgastFeatureDetector::create(70, true, AgastFeatureDetector::AGAST_5_8);
case AGAST_7_12d:
return AgastFeatureDetector::create(70, true, AgastFeatureDetector::AGAST_7_12d);
case AGAST_7_12s:
return AgastFeatureDetector::create(70, true, AgastFeatureDetector::AGAST_7_12s);
case AGAST_OAST_9_16:
return AgastFeatureDetector::create(70, true, AgastFeatureDetector::OAST_9_16);
case AKAZE_DEFAULT:
return AKAZE::create();
case AKAZE_DESCRIPTOR_KAZE:
return AKAZE::create(AKAZE::DESCRIPTOR_KAZE);
case BRISK_DEFAULT:
return BRISK::create();
case KAZE_DEFAULT:
return KAZE::create();
default:
return Ptr<Feature2D>();
}
}
} // namespace
#endif // __OPENCV_PERF_FEATURE2D_HPP__
@@ -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 = AKAZE::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
+36
View File
@@ -0,0 +1,36 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
#include "perf_precomp.hpp"
namespace opencv_test { namespace {
typedef perf::TestBaseWithParam<std::string> latch;
#define LATCH_IMAGES \
"cv/detectors_descriptors_evaluation/images_datasets/leuven/img1.png",\
"stitching/a3.png"
#ifdef OPENCV_ENABLE_NONFREE
PERF_TEST_P(latch, extract, testing::Values(LATCH_IMAGES))
{
string filename = getDataPath(GetParam());
Mat frame = imread(filename, IMREAD_GRAYSCALE);
ASSERT_FALSE(frame.empty()) << "Unable to load source image " << filename;
Mat mask;
declare.in(frame).time(90);
Ptr<SURF> detector = SURF::create();
vector<KeyPoint> points;
detector->detect(frame, points, mask);
Ptr<LATCH> descriptor = LATCH::create();
vector<uchar> descriptors;
TEST_CYCLE() descriptor->compute(frame, points, descriptors);
SANITY_CHECK_NOTHING();
}
#endif // NONFREE
}} // namespace
+14
View File
@@ -0,0 +1,14 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
#include "perf_precomp.hpp"
#include "opencv2/ts/cuda_perf.hpp"
static const char * impls[] = {
#ifdef HAVE_CUDA
"cuda",
#endif
"plain"
};
CV_PERF_TEST_MAIN_WITH_IMPLS(xfeatures2d, impls, perf::printCudaInfo())
+33
View File
@@ -0,0 +1,33 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
#include "perf_precomp.hpp"
namespace opencv_test { namespace {
typedef perf::TestBaseWithParam<std::string> msd;
#define MSD_IMAGES \
"cv/detectors_descriptors_evaluation/images_datasets/leuven/img1.png",\
"stitching/a3.png"
PERF_TEST_P(msd, detect, testing::Values(MSD_IMAGES))
{
string filename = getDataPath(GetParam());
Mat frame = imread(filename, IMREAD_GRAYSCALE);
if (frame.empty())
FAIL() << "Unable to load source image " << filename;
Mat mask;
declare.in(frame);
Ptr<MSDDetector> detector = MSDDetector::create();
vector<KeyPoint> points;
TEST_CYCLE() detector->detect(frame, points, mask);
sort(points.begin(), points.end(), comparators::KeypointGreater());
SANITY_CHECK_KEYPOINTS(points, 1e-3);
}
}} // namespace
+25
View File
@@ -0,0 +1,25 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
#ifndef __OPENCV_PERF_PRECOMP_HPP__
#define __OPENCV_PERF_PRECOMP_HPP__
#include "cvconfig.h"
#include "opencv2/ts.hpp"
#include "opencv2/xfeatures2d.hpp"
#ifdef HAVE_OPENCV_OCL
# include "opencv2/ocl.hpp"
#endif
#ifdef HAVE_CUDA
# include "opencv2/xfeatures2d/cuda.hpp"
#endif
namespace opencv_test {
using namespace cv::xfeatures2d;
using namespace perf;
}
#endif
+68
View File
@@ -0,0 +1,68 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
#include "perf_precomp.hpp"
#ifdef OPENCV_ENABLE_NONFREE
namespace opencv_test { namespace {
typedef perf::TestBaseWithParam<std::string> surf;
#define SURF_IMAGES \
"cv/detectors_descriptors_evaluation/images_datasets/leuven/img1.png",\
"stitching/a3.png"
PERF_TEST_P(surf, detect, testing::Values(SURF_IMAGES))
{
string filename = getDataPath(GetParam());
Mat frame = imread(filename, IMREAD_GRAYSCALE);
ASSERT_FALSE(frame.empty()) << "Unable to load source image " << filename;
Mat mask;
declare.in(frame).time(90);
Ptr<SURF> detector = SURF::create();
vector<KeyPoint> points;
TEST_CYCLE() detector->detect(frame, points, mask);
SANITY_CHECK_NOTHING();
}
PERF_TEST_P(surf, extract, testing::Values(SURF_IMAGES))
{
string filename = getDataPath(GetParam());
Mat frame = imread(filename, IMREAD_GRAYSCALE);
ASSERT_FALSE(frame.empty()) << "Unable to load source image " << filename;
Mat mask;
declare.in(frame).time(90);
Ptr<SURF> detector = SURF::create();
vector<KeyPoint> points;
Mat descriptors;
detector->detect(frame, points, mask);
TEST_CYCLE() detector->compute(frame, points, descriptors);
SANITY_CHECK_NOTHING();
}
PERF_TEST_P(surf, full, testing::Values(SURF_IMAGES))
{
string filename = getDataPath(GetParam());
Mat frame = imread(filename, IMREAD_GRAYSCALE);
ASSERT_FALSE(frame.empty()) << "Unable to load source image " << filename;
Mat mask;
declare.in(frame).time(90);
Ptr<SURF> detector = SURF::create();
vector<KeyPoint> points;
Mat descriptors;
TEST_CYCLE() detector->detectAndCompute(frame, mask, points, descriptors, false);
SANITY_CHECK_NOTHING();
}
}} // namespace
#endif // NONFREE
+102
View File
@@ -0,0 +1,102 @@
/*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 "perf_precomp.hpp"
#if defined(HAVE_CUDA) && defined(OPENCV_ENABLE_NONFREE)
#include "opencv2/ts/cuda_perf.hpp"
namespace opencv_test { namespace {
//////////////////////////////////////////////////////////////////////
// SURF
#ifdef HAVE_OPENCV_CUDAARITHM
DEF_PARAM_TEST_1(Image, string);
PERF_TEST_P(Image, CUDA_SURF,
Values<std::string>("gpu/perf/aloe.png"))
{
declare.time(50.0);
const cv::Mat img = readImage(GetParam(), cv::IMREAD_GRAYSCALE);
ASSERT_FALSE(img.empty());
if (PERF_RUN_CUDA())
{
cv::cuda::SURF_CUDA d_surf;
const cv::cuda::GpuMat d_img(img);
cv::cuda::GpuMat d_keypoints, d_descriptors;
TEST_CYCLE() d_surf(d_img, cv::cuda::GpuMat(), d_keypoints, d_descriptors);
std::vector<cv::KeyPoint> gpu_keypoints;
d_surf.downloadKeypoints(d_keypoints, gpu_keypoints);
cv::Mat gpu_descriptors(d_descriptors);
sortKeyPoints(gpu_keypoints, gpu_descriptors);
SANITY_CHECK_KEYPOINTS(gpu_keypoints);
SANITY_CHECK(gpu_descriptors, 1e-3);
}
else
{
cv::Ptr<cv::Feature2D> surf = cv::xfeatures2d::SURF::create();
std::vector<cv::KeyPoint> cpu_keypoints;
cv::Mat cpu_descriptors;
TEST_CYCLE() surf->detect(img, cpu_keypoints);
TEST_CYCLE() surf->compute(img, cpu_keypoints, cpu_descriptors);
SANITY_CHECK_KEYPOINTS(cpu_keypoints);
SANITY_CHECK(cpu_descriptors);
}
}
#endif // HAVE_OPENCV_CUDAARITHM
}} // namespace
#endif // HAVE_CUDA && OPENCV_ENABLE_NONFREE
+108
View File
@@ -0,0 +1,108 @@
/*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
// Peng Xiao, pengxiao@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"
#if defined(HAVE_OPENCV_OCL) && defined(OPENCV_ENABLE_NONFREE)
namespace opencv_test { namespace {
typedef perf::TestBaseWithParam<std::string> OCL_SURF;
#define SURF_IMAGES \
"cv/detectors_descriptors_evaluation/images_datasets/leuven/img1.png",\
"stitching/a3.png"
PERF_TEST_P(OCL_SURF, DISABLED_with_data_transfer, testing::Values(SURF_IMAGES))
{
string filename = getDataPath(GetParam());
Mat img = imread(filename, IMREAD_GRAYSCALE);
ASSERT_FALSE(img.empty());
SURF_OCL d_surf;
oclMat d_keypoints;
oclMat d_descriptors;
Mat cpu_kp;
Mat cpu_dp;
declare.time(60);
TEST_CYCLE()
{
oclMat d_src(img);
d_surf(d_src, oclMat(), d_keypoints, d_descriptors);
d_keypoints.download(cpu_kp);
d_descriptors.download(cpu_dp);
}
SANITY_CHECK_NOTHING();
}
PERF_TEST_P(OCL_SURF, DISABLED_without_data_transfer, testing::Values(SURF_IMAGES))
{
string filename = getDataPath(GetParam());
Mat img = imread(filename, IMREAD_GRAYSCALE);
ASSERT_FALSE(img.empty());
SURF_OCL d_surf;
oclMat d_keypoints;
oclMat d_descriptors;
oclMat d_src(img);
declare.time(60);
TEST_CYCLE() d_surf(d_src, oclMat(), d_keypoints, d_descriptors);
Mat cpu_kp;
Mat cpu_dp;
d_keypoints.download(cpu_kp);
d_descriptors.download(cpu_dp);
SANITY_CHECK_NOTHING();
}
}} // namespace
#endif // HAVE_OPENCV_OCL && OPENCV_ENABLE_NONFREE
+36
View File
@@ -0,0 +1,36 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
#include "perf_precomp.hpp"
namespace opencv_test { namespace {
typedef perf::TestBaseWithParam<std::string> teblid;
#define TEBLID_IMAGES \
"cv/detectors_descriptors_evaluation/images_datasets/leuven/img1.png",\
"stitching/a3.png"
#ifdef OPENCV_ENABLE_NONFREE
PERF_TEST_P(teblid, extract, testing::Values(TEBLID_IMAGES))
{
string filename = getDataPath(GetParam());
Mat frame = imread(filename, IMREAD_GRAYSCALE);
ASSERT_FALSE(frame.empty()) << "Unable to load source image " << filename;
Mat mask;
declare.in(frame).time(90);
Ptr<SURF> detector = SURF::create();
vector<KeyPoint> points;
detector->detect(frame, points, mask);
Ptr<TEBLID> descriptor = TEBLID::create(6.25f);
cv::Mat descriptors;
TEST_CYCLE() descriptor->compute(frame, points, descriptors);
SANITY_CHECK_NOTHING();
}
#endif // NONFREE
}} // namespace
+39
View File
@@ -0,0 +1,39 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
#include "perf_precomp.hpp"
namespace opencv_test { namespace {
#ifdef OPENCV_XFEATURES2D_HAS_VGG_DATA
typedef perf::TestBaseWithParam<std::string> vgg;
#define VGG_IMAGES \
"cv/detectors_descriptors_evaluation/images_datasets/leuven/img1.png",\
"stitching/a3.png"
PERF_TEST_P(vgg, extract, testing::Values(VGG_IMAGES))
{
string filename = getDataPath(GetParam());
Mat frame = imread(filename, IMREAD_GRAYSCALE);
ASSERT_FALSE(frame.empty()) << "Unable to load source image " << filename;
Mat mask;
declare.in(frame).time(90);
Ptr<KAZE> detector = KAZE::create();
vector<KeyPoint> points;
detector->detect(frame, points, mask);
Ptr<VGG> descriptor = VGG::create();
Mat_<float> descriptors;
// compute keypoints descriptor
TEST_CYCLE() descriptor->compute(frame, points, descriptors);
SANITY_CHECK_NOTHING();
}
#endif // OPENCV_XFEATURES2D_HAS_VGG_DATA
}} // namespace
+115
View File
@@ -0,0 +1,115 @@
#include <iostream>
#include "opencv2/opencv_modules.hpp"
#ifdef HAVE_OPENCV_XFEATURES2D
#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/features.hpp>
#include <opencv2/xfeatures2d.hpp>
#include <opencv2/imgcodecs.hpp>
#include <vector>
// If you find this code useful, please add a reference to the following paper in your work:
// Gil Levi and Tal Hassner, "LATCH: Learned Arrangements of Three Patch Codes", arXiv preprint arXiv:1501.03719, 15 Jan. 2015
using namespace std;
using namespace cv;
const float inlier_threshold = 2.5f; // Distance threshold to identify inliers
const float nn_match_ratio = 0.8f; // Nearest neighbor matching ratio
int main(int argc, char* argv[])
{
CommandLineParser parser(argc, argv,
"{@img1 | graf1.png | input image 1}"
"{@img2 | graf3.png | input image 2}"
"{@homography | H1to3p.xml | homography matrix}");
Mat img1 = imread( samples::findFile( parser.get<String>("@img1") ), IMREAD_GRAYSCALE);
Mat img2 = imread( samples::findFile( parser.get<String>("@img2") ), IMREAD_GRAYSCALE);
Mat homography;
FileStorage fs( samples::findFile( parser.get<String>("@homography") ), FileStorage::READ);
fs.getFirstTopLevelNode() >> homography;
vector<KeyPoint> kpts1, kpts2;
Mat desc1, desc2;
Ptr<cv::ORB> orb_detector = cv::ORB::create(10000);
Ptr<xfeatures2d::LATCH> latch = xfeatures2d::LATCH::create();
orb_detector->detect(img1, kpts1);
latch->compute(img1, kpts1, desc1);
orb_detector->detect(img2, kpts2);
latch->compute(img2, kpts2, desc2);
BFMatcher matcher(NORM_HAMMING);
vector< vector<DMatch> > nn_matches;
matcher.knnMatch(desc1, desc2, nn_matches, 2);
vector<KeyPoint> matched1, matched2, inliers1, inliers2;
vector<DMatch> good_matches;
for (size_t i = 0; i < nn_matches.size(); i++) {
DMatch first = nn_matches[i][0];
float dist1 = nn_matches[i][0].distance;
float dist2 = nn_matches[i][1].distance;
if (dist1 < nn_match_ratio * dist2) {
matched1.push_back(kpts1[first.queryIdx]);
matched2.push_back(kpts2[first.trainIdx]);
}
}
for (unsigned i = 0; i < matched1.size(); i++) {
Mat col = Mat::ones(3, 1, CV_64F);
col.at<double>(0) = matched1[i].pt.x;
col.at<double>(1) = matched1[i].pt.y;
col = homography * col;
col /= col.at<double>(2);
double dist = sqrt(pow(col.at<double>(0) - matched2[i].pt.x, 2) +
pow(col.at<double>(1) - matched2[i].pt.y, 2));
if (dist < inlier_threshold) {
int new_i = static_cast<int>(inliers1.size());
inliers1.push_back(matched1[i]);
inliers2.push_back(matched2[i]);
good_matches.push_back(DMatch(new_i, new_i, 0));
}
}
Mat res;
drawMatches(img1, inliers1, img2, inliers2, good_matches, res);
imwrite("latch_result.png", res);
double inlier_ratio = inliers1.size() * 1.0 / matched1.size();
cout << "LATCH Matching Results" << endl;
cout << "*******************************" << endl;
cout << "# Keypoints 1: \t" << kpts1.size() << endl;
cout << "# Keypoints 2: \t" << kpts2.size() << endl;
cout << "# Matches: \t" << matched1.size() << endl;
cout << "# Inliers: \t" << inliers1.size() << endl;
cout << "# Inliers Ratio: \t" << inlier_ratio << endl;
cout << endl;
imshow("result", res);
waitKey();
return 0;
}
#else
int main()
{
std::cerr << "OpenCV was built without xfeatures2d module" << std::endl;
return 0;
}
#endif
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,293 @@
#!/usr/bin/python
"""
/*********************************************************************
* Software License Agreement (BSD License)
*
* Copyright (c) 2016
*
* Balint Cristian <cristian dot balint at gmail dot com>
*
*
* 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 copyright holders 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.
*********************************************************************/
/* export-boostdesc.py */
/* Export C headers from binary data */
// [http://infoscience.epfl.ch/record/186246/files/boostDesc_1.0.tar.gz]
"""
import sys
import struct
def float_to_hex(f):
return struct.unpack( '<I', struct.pack('<f', f) )[0]
def main():
# usage
if ( len(sys.argv) < 3 ):
print( "Usage: %s <binary-type (BGM, LBGM, BINBOOST)> <boostdesc-binary-filename>" % sys.argv[0] )
sys.exit(0)
if ( ( sys.argv[1] != "BGM" ) and
( sys.argv[1] != "LBGM" ) and
( sys.argv[1] != "BINBOOST" ) ):
print( "Invalid type [%s]" % sys.argv[1] )
sys.exit(0)
# enum literals
Assign = [ "ASSIGN_HARD",
"ASSIGN_BILINEAR",
"ASSIGN_SOFT",
"ASSIGN_HARD_MAGN",
"ASSIGN_SOFT_MAGN" ]
# open binary data file
f = open( sys.argv[2], 'rb' )
# header
print "/*"
print " *"
print " * Header exported from binary."
print " * [%s %s %s]" % ( sys.argv[0], sys.argv[1], sys.argv[2] )
print " *"
print " */"
# ini
nDim = 1;
nWLs = 0;
# dimensionality (where is the case)
if ( ( sys.argv[1] == "LBGM" ) or
( sys.argv[1] == "BINBOOST" ) ):
nDim = struct.unpack( '<i', f.read(4) )[0]
print
print "// dimensionality of learner"
print "static const int nDim = %i;" % nDim
# week learners (where is the case)
if ( sys.argv[1] != "BINBOOST" ):
nWLs = struct.unpack( '<i', f.read(4) )[0]
# common header
orientQuant = struct.unpack( '<i', f.read(4) )[0]
patchSize = struct.unpack( '<i', f.read(4) )[0]
iGradAssignType = struct.unpack( '<i', f.read(4) )[0]
print
print "// orientations"
print "static const int orientQuant = %i;" % orientQuant
print
print "// patch size"
print "static const int patchSize = %i;" % patchSize
print
print "// gradient assignment type"
print "static const int iGradAssignType = %s;" % Assign[iGradAssignType]
arr_thresh = ""
arr_orient = ""
arr__y_min = ""
arr__y_max = ""
arr__x_min = ""
arr__x_max = ""
arr__alpha = ""
arr___beta = ""
dims = nDim
if ( sys.argv[1] == "LBGM" ):
dims = 1
# iterate each dimension
for d in range( 0, dims ):
if ( sys.argv[1] == "BINBOOST" ):
nWLs = struct.unpack( '<i', f.read(4) )[0]
if ( d == 0 ):
print
print "// number of weak learners"
print "static const int nWLs = %i;" % nWLs
# iterate each members
for i in range( 0, nWLs ):
# unpack structure array
thresh = struct.unpack( '<f', f.read(4) )[0]
orient = struct.unpack( '<i', f.read(4) )[0]
y_min = struct.unpack( '<i', f.read(4) )[0]
y_max = struct.unpack( '<i', f.read(4) )[0]
x_min = struct.unpack( '<i', f.read(4) )[0]
x_max = struct.unpack( '<i', f.read(4) )[0]
alpha = struct.unpack( '<f', f.read(4) )[0]
beta = 0
if ( sys.argv[1] == "BINBOOST" ):
beta = struct.unpack( '<f', f.read(4) )[0]
# first entry
if ( d*dims + i == 0 ):
arr_thresh += "\n"
arr_thresh += "// threshold array (%s x %s)\n" % (dims,nWLs)
arr_thresh += "static const unsigned int thresh[] =\n{\n"
arr_orient += "\n"
arr_orient += "// orientation array (%s x %s)\n" % (dims,nWLs)
arr_orient += "static const int orient[] =\n{\n"
arr__y_min += "\n"
arr__y_min += "// Y min array (%s x %s)\n" % (dims,nWLs)
arr__y_min += "static const int y_min[] =\n{\n"
arr__y_max += "\n"
arr__y_max += "// Y max array (%s x %s)\n" % (dims,nWLs)
arr__y_max += "static const int y_max[] =\n{\n"
arr__x_min += "\n"
arr__x_min += "// X min array (%s x %s)\n" % (dims,nWLs)
arr__x_min += "static const int x_min[] =\n{\n"
arr__x_max += "\n"
arr__x_max += "// X max array (%s x %s)\n" % (dims,nWLs)
arr__x_max += "static const int x_max[] =\n{\n"
arr__alpha += "\n"
arr__alpha += "// alpha array (%s x %s)\n" % (dims,nWLs)
arr__alpha += "static const unsigned int alpha[] =\n{\n"
if ( sys.argv[1] == "BINBOOST" ):
arr___beta += "\n"
arr___beta += "// beta array (%s x %s)\n" % (dims,nWLs)
arr___beta += "static const unsigned int beta[] =\n{\n"
# last entry
if ( i == nWLs - 1 ) and ( d == dims - 1):
arr_thresh += " 0x%08x\n};" % float_to_hex(thresh)
arr_orient += " 0x%02x\n};" % orient
arr__y_min += " 0x%02x\n};" % y_min
arr__y_max += " 0x%02x\n};" % y_max
arr__x_min += " 0x%02x\n};" % x_min
arr__x_max += " 0x%02x\n};" % x_max
arr__alpha += " 0x%08x\n};" % float_to_hex(alpha)
if ( sys.argv[1] == "BINBOOST" ):
arr___beta += " 0x%08x\n};" % float_to_hex(beta)
break
# align entries
if ( (d*dims + i + 1) % 8 ):
arr_thresh += " 0x%08x," % float_to_hex(thresh)
arr_orient += " 0x%02x," % orient
arr__y_min += " 0x%02x," % y_min
arr__y_max += " 0x%02x," % y_max
arr__x_min += " 0x%02x," % x_min
arr__x_max += " 0x%02x," % x_max
arr__alpha += " 0x%08x," % float_to_hex(alpha)
if ( sys.argv[1] == "BINBOOST" ):
arr___beta += " 0x%08x," % float_to_hex(beta)
else:
arr_thresh += " 0x%08x,\n" % float_to_hex(thresh)
arr_orient += " 0x%02x,\n" % orient
arr__y_min += " 0x%02x,\n" % y_min
arr__y_max += " 0x%02x,\n" % y_max
arr__x_min += " 0x%02x,\n" % x_min
arr__x_max += " 0x%02x,\n" % x_max
arr__alpha += " 0x%08x,\n" % float_to_hex(alpha)
if ( sys.argv[1] == "BINBOOST" ):
arr___beta += " 0x%08x,\n" % float_to_hex(beta)
# extra array (when LBGM)
if ( sys.argv[1] == "LBGM" ):
arr___beta += "\n"
arr___beta += "// beta array (%s x %s)\n" % (nWLs,nDim)
arr___beta += "static const unsigned int beta[] =\n{\n"
for i in range( 0, nWLs ):
for d in range( 0, nDim ):
beta = struct.unpack( '<f', f.read(4) )[0]
# last entry
if ( i == nWLs-1 ) and ( d == nDim-1 ):
arr___beta += " 0x%08x\n};" % float_to_hex(beta)
break
# align entries
if ( (i*nDim + d + 1) % 8 ):
arr___beta += " 0x%08x," % float_to_hex(beta)
else:
arr___beta += " 0x%08x,\n" % float_to_hex(beta)
# release
f.close()
# dump on screen
print arr_thresh
print arr_orient
print arr__y_min
print arr__y_max
print arr__x_min
print arr__x_max
print arr__alpha
if ( ( sys.argv[1] == "LBGM" ) or
( sys.argv[1] == "BINBOOST" ) ):
print arr___beta
if __name__ == "__main__":
main()
+138
View File
@@ -0,0 +1,138 @@
#include <iostream>
#include <opencv2/core.hpp>
#include <opencv2/videoio.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/features.hpp>
#include <opencv2/flann.hpp>
#include <opencv2/xfeatures2d.hpp>
using namespace cv;
using namespace cv::xfeatures2d;
////////////////////////////////////////////////////
// This program demonstrates the GMS matching strategy.
int main(int argc, char* argv[])
{
const char* keys =
"{ h help | | print help message }"
"{ l left | | specify left (reference) image }"
"{ r right | | specify right (query) image }"
"{ camera | 0 | specify the camera device number }"
"{ nfeatures | 10000 | specify the maximum number of ORB features }"
"{ fastThreshold | 20 | specify the FAST threshold }"
"{ drawSimple | true | do not draw not matched keypoints }"
"{ withRotation | false | take rotation into account }"
"{ withScale | false | take scale into account }";
CommandLineParser cmd(argc, argv, keys);
if (cmd.has("help"))
{
std::cout << "Usage: gms_matcher [options]" << std::endl;
std::cout << "Available options:" << std::endl;
cmd.printMessage();
return EXIT_SUCCESS;
}
Ptr<Feature2D> orb = ORB::create(cmd.get<int>("nfeatures"));
orb.dynamicCast<cv::ORB>()->setFastThreshold(cmd.get<int>("fastThreshold"));
Ptr<DescriptorMatcher> matcher = DescriptorMatcher::create("BruteForce-Hamming");
if (!cmd.get<String>("left").empty() && !cmd.get<String>("right").empty())
{
Mat imgL = imread(cmd.get<String>("left"));
Mat imgR = imread(cmd.get<String>("right"));
std::vector<KeyPoint> kpRef, kpCur;
Mat descRef, descCur;
orb->detectAndCompute(imgL, noArray(), kpRef, descRef);
orb->detectAndCompute(imgR, noArray(), kpCur, descCur);
std::vector<DMatch> matchesAll, matchesGMS;
matcher->match(descCur, descRef, matchesAll);
matchGMS(imgR.size(), imgL.size(), kpCur, kpRef, matchesAll, matchesGMS, cmd.get<bool>("withRotation"), cmd.get<bool>("withScale"));
std::cout << "matchesGMS: " << matchesGMS.size() << std::endl;
Mat frameMatches;
if (cmd.get<bool>("drawSimple"))
drawMatches(imgR, kpCur, imgL, kpRef, matchesGMS, frameMatches, Scalar::all(-1), Scalar::all(-1),
std::vector<char>(), DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS);
else
drawMatches(imgR, kpCur, imgL, kpRef, matchesGMS, frameMatches);
imshow("Matches GMS", frameMatches);
waitKey();
}
else
{
std::vector<KeyPoint> kpRef;
Mat descRef;
VideoCapture capture(cmd.get<int>("camera"));
//Camera warm-up
for (int i = 0; i < 10; i++)
{
Mat frame;
capture >> frame;
}
Mat frameRef;
for (;;)
{
Mat frame;
capture >> frame;
if (frameRef.empty())
{
frame.copyTo(frameRef);
orb->detectAndCompute(frameRef, noArray(), kpRef, descRef);
}
TickMeter tm;
tm.start();
std::vector<KeyPoint> kp;
Mat desc;
orb->detectAndCompute(frame, noArray(), kp, desc);
tm.stop();
double t_orb = tm.getTimeMilli();
tm.reset();
tm.start();
std::vector<DMatch> matchesAll, matchesGMS;
matcher->match(desc, descRef, matchesAll);
tm.stop();
double t_match = tm.getTimeMilli();
matchGMS(frame.size(), frameRef.size(), kp, kpRef, matchesAll, matchesGMS, cmd.get<bool>("withRotation"), cmd.get<bool>("withScale"));
tm.stop();
Mat frameMatches;
if (cmd.get<bool>("drawSimple"))
drawMatches(frame, kp, frameRef, kpRef, matchesGMS, frameMatches, Scalar::all(-1), Scalar::all(-1),
std::vector<char>(), DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS);
else
drawMatches(frame, kp, frameRef, kpRef, matchesGMS, frameMatches);
String label = format("ORB: %.2f ms", t_orb);
putText(frameMatches, label, Point(20, 20), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(0,0,255));
label = format("Matching: %.2f ms", t_match);
putText(frameMatches, label, Point(20, 40), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(0,0,255));
label = format("GMS matching: %.2f ms", tm.getTimeMilli());
putText(frameMatches, label, Point(20, 60), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(0,0,255));
putText(frameMatches, "Press r to reinitialize the reference image.", Point(frameMatches.cols-380, 20), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(0,0,255));
putText(frameMatches, "Press esc to quit.", Point(frameMatches.cols-180, 40), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(0,0,255));
imshow("Matches GMS", frameMatches);
int c = waitKey(30);
if (c == 27)
break;
else if (c == 'r')
{
frame.copyTo(frameRef);
orb->detectAndCompute(frameRef, noArray(), kpRef, descRef);
}
}
}
return EXIT_SUCCESS;
}
@@ -0,0 +1,172 @@
/*
By downloading, copying, installing or using the software you agree to this license.
If you do not agree to this license, do not download, install,
copy or use the software.
License Agreement
For Open Source Computer Vision Library
(3-clause BSD License)
Copyright (C) 2000-2016, Intel Corporation, all rights reserved.
Copyright (C) 2009-2011, Willow Garage Inc., all rights reserved.
Copyright (C) 2009-2016, NVIDIA Corporation, all rights reserved.
Copyright (C) 2010-2013, Advanced Micro Devices, Inc., all rights reserved.
Copyright (C) 2015-2016, OpenCV Foundation, all rights reserved.
Copyright (C) 2015-2016, 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:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the names of the copyright holders nor the names of the contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
This software is provided by the copyright holders and contributors "as is" and
any express or implied warranties, including, but not limited to, the implied
warranties of merchantability and fitness for a particular purpose are disclaimed.
In no event shall copyright holders or contributors be liable for any direct,
indirect, incidental, special, exemplary, or consequential damages
(including, but not limited to, procurement of substitute goods or services;
loss of use, data, or profits; or business interruption) however caused
and on any theory of liability, whether in contract, strict liability,
or tort (including negligence or otherwise) arising in any way out of
the use of this software, even if advised of the possibility of such damage.
*/
/*
Contributed by Gregor Kovalcik <gregor dot kovalcik at gmail dot com>
based on code provided by Martin Krulis, Jakub Lokoc and Tomas Skopal.
References:
Martin Krulis, Jakub Lokoc, Tomas Skopal.
Efficient Extraction of Clustering-Based Feature Signatures Using GPU Architectures.
Multimedia tools and applications, 75(13), pp.: 80718103, Springer, ISSN: 1380-7501, 2016
Christian Beecks, Merih Seran Uysal, Thomas Seidl.
Signature quadratic form distance.
In Proceedings of the ACM International Conference on Image and Video Retrieval, pages 438-445.
ACM, 2010.
*/
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/xfeatures2d.hpp>
#include <iostream>
#include <string>
using namespace std;
using namespace cv;
using namespace xfeatures2d;
void printHelpMessage(void);
void printHelpMessage(void)
{
cout << "Example of the PCTSignatures algorithm computing and visualizing\n"
"image signature for one image, or comparing multiple images with the first\n"
"image using the signature quadratic form distance.\n\n"
"Usage: pct_signatures ImageToProcessAndDisplay\n"
"or: pct_signatures ReferenceImage [ImagesToCompareWithTheReferenceImage]\n\n"
"The program has 2 modes:\n"
"- single argument: program computes and visualizes the image signature\n"
"- multiple arguments: program compares the first image to the others\n"
" using pct signatures and signature quadratic form distance (SQFD)";
}
/** @brief
Example of the PCTSignatures algorithm.
The program has 2 modes:
- single argument mode, where the program computes and visualizes the image signature
- multiple argument mode, where the program compares the first image to the others
using signatures and signature quadratic form distance (SQFD)
*/
int main(int argc, char** argv)
{
if (argc < 2) // Check arguments
{
printHelpMessage();
return 1;
}
Mat source;
source = imread(argv[1]); // Read the file
if (!source.data) // Check for invalid input
{
cerr << "Could not open or find the image: " << argv[1];
return -1;
}
Mat signature, result; // define variables
int initSampleCount = 2000;
int initSeedCount = 400;
int grayscaleBitsPerPixel = 4;
vector<Point2f> initPoints;
namedWindow("Source", WINDOW_AUTOSIZE); // Create windows for display.
namedWindow("Result", WINDOW_AUTOSIZE);
// create the algorithm
PCTSignatures::generateInitPoints(initPoints, initSampleCount, PCTSignatures::UNIFORM);
Ptr<PCTSignatures> pctSignatures = PCTSignatures::create(initPoints, initSeedCount);
pctSignatures->setGrayscaleBits(grayscaleBitsPerPixel);
// compute and visualize the first image
double start = (double)getTickCount();
pctSignatures->computeSignature(source, signature);
double end = (double)getTickCount();
cout << "Signature of the reference image computed in " << (end - start) / (getTickFrequency() * 1.0f) << " seconds." << endl;
PCTSignatures::drawSignature(source, signature, result);
imshow("Source", source); // show the result
imshow("Result", result);
if (argc == 2) // single image -> finish right after the visualization
{
waitKey(0); // Wait for user input
return 0;
}
// multiple images -> compare to the first one
else
{
vector<Mat> images;
vector<Mat> signatures;
vector<float> distances;
for (int i = 2; i < argc; i++)
{
Mat image = imread(argv[i]);
if (!source.data) // Check for invalid input
{
cerr << "Could not open or find the image: " << argv[i] << std::endl;
return 1;
}
images.push_back(image);
}
pctSignatures->computeSignatures(images, signatures);
Ptr<PCTSignaturesSQFD> pctSQFD = PCTSignaturesSQFD::create();
pctSQFD->computeQuadraticFormDistances(signature, signatures, distances);
for (int i = 0; i < (int)(distances.size()); i++)
{
cout << "Image: " << argv[i + 2] << ", similarity: " << distances[i] << endl;
}
waitKey(0); // Wait for user input
}
return 0;
}
+170
View File
@@ -0,0 +1,170 @@
/*
By downloading, copying, installing or using the software you agree to this license.
If you do not agree to this license, do not download, install,
copy or use the software.
License Agreement
For Open Source Computer Vision Library
(3-clause BSD License)
Copyright (C) 2000-2016, Intel Corporation, all rights reserved.
Copyright (C) 2009-2011, Willow Garage Inc., all rights reserved.
Copyright (C) 2009-2016, NVIDIA Corporation, all rights reserved.
Copyright (C) 2010-2013, Advanced Micro Devices, Inc., all rights reserved.
Copyright (C) 2015-2016, OpenCV Foundation, all rights reserved.
Copyright (C) 2015-2016, 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:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the names of the copyright holders nor the names of the contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
This software is provided by the copyright holders and contributors "as is" and
any express or implied warranties, including, but not limited to, the implied
warranties of merchantability and fitness for a particular purpose are disclaimed.
In no event shall copyright holders or contributors be liable for any direct,
indirect, incidental, special, exemplary, or consequential damages
(including, but not limited to, procurement of substitute goods or services;
loss of use, data, or profits; or business interruption) however caused
and on any theory of liability, whether in contract, strict liability,
or tort (including negligence or otherwise) arising in any way out of
the use of this software, even if advised of the possibility of such damage.
*/
/*
Contributed by Gregor Kovalcik <gregor dot kovalcik at gmail dot com>
based on code provided by Martin Krulis, Jakub Lokoc and Tomas Skopal.
References:
Martin Krulis, Jakub Lokoc, Tomas Skopal.
Efficient Extraction of Clustering-Based Feature Signatures Using GPU Architectures.
Multimedia tools and applications, 75(13), pp.: 80718103, Springer, ISSN: 1380-7501, 2016
Christian Beecks, Merih Seran Uysal, Thomas Seidl.
Signature quadratic form distance.
In Proceedings of the ACM International Conference on Image and Video Retrieval, pages 438-445.
ACM, 2010.
*/
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/xfeatures2d.hpp>
#include <iostream>
#include <string>
using namespace std;
using namespace cv;
using namespace xfeatures2d;
void printHelpMessage(void);
void printHelpMessage(void)
{
cout << "Example of the PCTSignatures algorithm.\n\n"
"This program computes and visualizes position-color-texture signatures\n"
"using images from webcam if available.\n\n"
"Usage:\n"
"pct_webcam [sample_count] [seed_count]\n"
"Note: sample_count must be greater or equal to seed_count.";
}
/** @brief
Example of the PCTSignatures algorithm.
This program computes and visualizes position-color-texture signatures
of images taken from webcam if available.
*/
int main(int argc, char** argv)
{
// define variables
Mat frame, signature, result;
int initSampleCount = 2000;
int initSeedCount = 400;
int grayscaleBitsPerPixel = 4;
// parse for help argument
{
for (int i = 1; i < argc; i++)
{
if ((string)argv[i] == "-h" || (string)argv[i] == "--help")
{
printHelpMessage();
return 0;
}
}
}
// parse optional arguments
if (argc > 1) // sample count
{
initSampleCount = atoi(argv[1]);
if (initSampleCount <= 0)
{
cerr << "Sample count have to be a positive integer: " << argv[1] << endl;
return 1;
}
initSeedCount = (int)floor(static_cast<float>(initSampleCount / 4));
initSeedCount = std::max(1, initSeedCount); // fallback if sample count == 1
}
if (argc > 2) // seed count
{
initSeedCount = atoi(argv[2]);
if (initSeedCount <= 0)
{
cerr << "Seed count have to be a positive integer: " << argv[2] << endl;
return 1;
}
if (initSeedCount > initSampleCount)
{
cerr << "Seed count have to be lower or equal to sample count!" << endl;
return 1;
}
}
// create algorithm
Ptr<PCTSignatures> pctSignatures = PCTSignatures::create(initSampleCount, initSeedCount, PCTSignatures::UNIFORM);
pctSignatures->setGrayscaleBits(grayscaleBitsPerPixel);
// open video capture device
VideoCapture videoCapture;
if (!videoCapture.open(0))
{
cerr << "Unable to open the first video capture device with ID = 0!" << endl;
return 1;
}
// Create windows for display.
namedWindow("Source", WINDOW_AUTOSIZE);
namedWindow("Result", WINDOW_AUTOSIZE);
// run drawing loop
for (;;)
{
videoCapture >> frame;
if (frame.empty()) break; // end of video stream
pctSignatures->computeSignature(frame, signature);
PCTSignatures::drawSignature(Mat::zeros(frame.size(), frame.type()), signature, result);
imshow("Source", frame); // Show our images inside the windows.
imshow("Result", result);
if (waitKey(1) == 27) break; // stop videocapturing by pressing ESC
}
return 0;
}
@@ -0,0 +1,89 @@
/*
* shape_context.cpp -- Shape context demo for shape matching
*/
#include <iostream>
#include "opencv2/opencv_modules.hpp"
#ifdef HAVE_OPENCV_SHAPE
#include "opencv2/shape.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/imgproc.hpp"
#include "opencv2/features.hpp"
#include "opencv2/xfeatures2d.hpp"
#include "opencv2/core/utility.hpp"
#include <string>
using namespace std;
using namespace cv;
using namespace cv::xfeatures2d;
static void help()
{
printf("\nThis program demonstrates how to use common interface for shape transformers\n"
"Call\n"
"shape_transformation [image1] [image2]\n");
}
int main(int argc, char** argv)
{
help();
if (argc < 3)
{
printf("Not enough parameters\n");
return -1;
}
Mat img1 = imread(argv[1], IMREAD_GRAYSCALE);
Mat img2 = imread(argv[2], IMREAD_GRAYSCALE);
if(img1.empty() || img2.empty())
{
printf("Can't read one of the images\n");
return -1;
}
// detecting keypoints & computing descriptors
Ptr<SURF> surf = SURF::create(5000);
vector<KeyPoint> keypoints1, keypoints2;
Mat descriptors1, descriptors2;
surf->detectAndCompute(img1, Mat(), keypoints1, descriptors1);
surf->detectAndCompute(img2, Mat(), keypoints2, descriptors2);
// matching descriptors
BFMatcher matcher(surf->defaultNorm());
vector<DMatch> matches;
matcher.match(descriptors1, descriptors2, matches);
// drawing the results
namedWindow("matches", 1);
Mat img_matches;
drawMatches(img1, keypoints1, img2, keypoints2, matches, img_matches);
imshow("matches", img_matches);
// extract points
vector<Point2f> pts1, pts2;
for (size_t ii=0; ii<keypoints1.size(); ii++)
pts1.push_back( keypoints1[ii].pt );
for (size_t ii=0; ii<keypoints2.size(); ii++)
pts2.push_back( keypoints2[ii].pt );
// Apply TPS
Ptr<ThinPlateSplineShapeTransformer> mytps = createThinPlateSplineShapeTransformer(25000); //TPS with a relaxed constraint
mytps->estimateTransformation(pts1, pts2, matches);
mytps->warpImage(img2, img2);
imshow("Tranformed", img2);
waitKey(0);
return 0;
}
#else
int main()
{
std::cerr << "OpenCV was built without shape module" << std::endl;
return 0;
}
#endif // HAVE_OPENCV_SHAPE
@@ -0,0 +1,225 @@
#include <iostream>
#include <stdio.h>
#include "opencv2/core.hpp"
#include "opencv2/core/utility.hpp"
#include "opencv2/core/ocl.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/features.hpp"
#include "opencv2/geometry.hpp"
#include "opencv2/imgproc.hpp"
#include "opencv2/xfeatures2d.hpp"
using namespace cv;
using namespace cv::xfeatures2d;
const int LOOP_NUM = 10;
const int GOOD_PTS_MAX = 50;
const float GOOD_PORTION = 0.15f;
int64 work_begin = 0;
int64 work_end = 0;
static void workBegin()
{
work_begin = getTickCount();
}
static void workEnd()
{
work_end = getTickCount() - work_begin;
}
static double getTime()
{
return work_end /((double)getTickFrequency() )* 1000.;
}
struct SURFDetector
{
Ptr<Feature2D> surf;
SURFDetector(double hessian = 800.0)
{
surf = SURF::create(hessian);
}
template<class T>
void operator()(const T& in, const T& mask, std::vector<cv::KeyPoint>& pts, T& descriptors, bool useProvided = false)
{
surf->detectAndCompute(in, mask, pts, descriptors, useProvided);
}
};
template<class KPMatcher>
struct SURFMatcher
{
KPMatcher matcher;
template<class T>
void match(const T& in1, const T& in2, std::vector<cv::DMatch>& matches)
{
matcher.match(in1, in2, matches);
}
};
static Mat drawGoodMatches(
const Mat& img1,
const Mat& img2,
const std::vector<KeyPoint>& keypoints1,
const std::vector<KeyPoint>& keypoints2,
std::vector<DMatch>& matches,
std::vector<Point2f>& scene_corners_
)
{
//-- Sort matches and preserve top 10% matches
std::sort(matches.begin(), matches.end());
std::vector< DMatch > good_matches;
double minDist = matches.front().distance;
double maxDist = matches.back().distance;
const int ptsPairs = std::min(GOOD_PTS_MAX, (int)(matches.size() * GOOD_PORTION));
for( int i = 0; i < ptsPairs; i++ )
{
good_matches.push_back( matches[i] );
}
std::cout << "\nMax distance: " << maxDist << std::endl;
std::cout << "Min distance: " << minDist << std::endl;
std::cout << "Calculating homography using " << ptsPairs << " point pairs." << std::endl;
// drawing the results
Mat img_matches;
drawMatches( img1, keypoints1, img2, keypoints2,
good_matches, img_matches, Scalar::all(-1), Scalar::all(-1),
std::vector<char>(), DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS );
//-- Localize the object
std::vector<Point2f> obj;
std::vector<Point2f> scene;
for( size_t i = 0; i < good_matches.size(); i++ )
{
//-- Get the keypoints from the good matches
obj.push_back( keypoints1[ good_matches[i].queryIdx ].pt );
scene.push_back( keypoints2[ good_matches[i].trainIdx ].pt );
}
//-- Get the corners from the image_1 ( the object to be "detected" )
std::vector<Point2f> obj_corners(4);
obj_corners[0] = Point(0,0);
obj_corners[1] = Point( img1.cols, 0 );
obj_corners[2] = Point( img1.cols, img1.rows );
obj_corners[3] = Point( 0, img1.rows );
std::vector<Point2f> scene_corners(4);
Mat H = findHomography( obj, scene, RANSAC );
perspectiveTransform( obj_corners, scene_corners, H);
scene_corners_ = scene_corners;
//-- Draw lines between the corners (the mapped object in the scene - image_2 )
line( img_matches,
scene_corners[0] + Point2f( (float)img1.cols, 0), scene_corners[1] + Point2f( (float)img1.cols, 0),
Scalar( 0, 255, 0), 2, LINE_AA );
line( img_matches,
scene_corners[1] + Point2f( (float)img1.cols, 0), scene_corners[2] + Point2f( (float)img1.cols, 0),
Scalar( 0, 255, 0), 2, LINE_AA );
line( img_matches,
scene_corners[2] + Point2f( (float)img1.cols, 0), scene_corners[3] + Point2f( (float)img1.cols, 0),
Scalar( 0, 255, 0), 2, LINE_AA );
line( img_matches,
scene_corners[3] + Point2f( (float)img1.cols, 0), scene_corners[0] + Point2f( (float)img1.cols, 0),
Scalar( 0, 255, 0), 2, LINE_AA );
return img_matches;
}
////////////////////////////////////////////////////
// This program demonstrates the usage of SURF_OCL.
// use cpu findHomography interface to calculate the transformation matrix
int main(int argc, char* argv[])
{
const char* keys =
"{ h help | | print help message }"
"{ l left | box.png | specify left image }"
"{ r right | box_in_scene.png | specify right image }"
"{ o output | SURF_output.jpg | specify output save path }"
"{ m cpu_mode | | run without OpenCL }";
CommandLineParser cmd(argc, argv, keys);
if (cmd.has("help"))
{
std::cout << "Usage: surf_matcher [options]" << std::endl;
std::cout << "Available options:" << std::endl;
cmd.printMessage();
return EXIT_SUCCESS;
}
if (cmd.has("cpu_mode"))
{
ocl::setUseOpenCL(false);
std::cout << "OpenCL was disabled" << std::endl;
}
UMat img1, img2;
std::string outpath = cmd.get<std::string>("o");
std::string leftName = cmd.get<std::string>("l");
imread(leftName, IMREAD_GRAYSCALE).copyTo(img1);
if(img1.empty())
{
std::cout << "Couldn't load " << leftName << std::endl;
cmd.printMessage();
return EXIT_FAILURE;
}
std::string rightName = cmd.get<std::string>("r");
imread(rightName, IMREAD_GRAYSCALE).copyTo(img2);
if(img2.empty())
{
std::cout << "Couldn't load " << rightName << std::endl;
cmd.printMessage();
return EXIT_FAILURE;
}
double surf_time = 0.;
//declare input/output
std::vector<KeyPoint> keypoints1, keypoints2;
std::vector<DMatch> matches;
UMat _descriptors1, _descriptors2;
Mat descriptors1 = _descriptors1.getMat(ACCESS_RW),
descriptors2 = _descriptors2.getMat(ACCESS_RW);
//instantiate detectors/matchers
SURFDetector surf;
SURFMatcher<BFMatcher> matcher;
//-- start of timing section
for (int i = 0; i <= LOOP_NUM; i++)
{
if(i == 1) workBegin();
surf(img1.getMat(ACCESS_READ), Mat(), keypoints1, descriptors1);
surf(img2.getMat(ACCESS_READ), Mat(), keypoints2, descriptors2);
matcher.match(descriptors1, descriptors2, matches);
}
workEnd();
std::cout << "FOUND " << keypoints1.size() << " keypoints on first image" << std::endl;
std::cout << "FOUND " << keypoints2.size() << " keypoints on second image" << std::endl;
surf_time = getTime();
std::cout << "SURF run time: " << surf_time / LOOP_NUM << " ms" << std::endl<<"\n";
std::vector<Point2f> corner;
Mat img_matches = drawGoodMatches(img1.getMat(ACCESS_READ), img2.getMat(ACCESS_READ), keypoints1, keypoints2, matches, corner);
//-- Show detected matches
namedWindow("surf matches", 0);
imshow("surf matches", img_matches);
imwrite(outpath, img_matches);
waitKey(0);
return EXIT_SUCCESS;
}
@@ -0,0 +1,248 @@
/*
* video_homography.cpp
*
* Created on: Oct 18, 2010
* Author: erublee
*/
#include <iostream>
#include "opencv2/opencv_modules.hpp"
#ifdef HAVE_OPENCV_3D
#include "opencv2/geometry.hpp"
#include "opencv2/videoio.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/imgproc.hpp"
#include "opencv2/features.hpp"
#include "opencv2/xfeatures2d.hpp"
#include <list>
#include <vector>
using namespace std;
using namespace cv;
using namespace cv::xfeatures2d;
static void help(char **av)
{
cout << "\nThis program demonstrated the use of features with the Fast corner detector and brief descriptors\n"
<< "to track planar objects by computing their homography from the key (training) image to the query (test) image\n\n" << endl;
cout << "usage: " << av[0] << " <video device number>\n" << endl;
cout << "The following keys do stuff:" << endl;
cout << " t : grabs a reference frame to match against" << endl;
cout << " l : makes the reference frame new every frame" << endl;
cout << " q or escape: quit" << endl;
}
namespace
{
void drawMatchesRelative(const vector<KeyPoint>& train, const vector<KeyPoint>& query,
std::vector<cv::DMatch>& matches, Mat& img, const vector<unsigned char>& mask = vector<
unsigned char> ())
{
for (int i = 0; i < (int)matches.size(); i++)
{
if (mask.empty() || mask[i])
{
Point2f pt_new = query[matches[i].queryIdx].pt;
Point2f pt_old = train[matches[i].trainIdx].pt;
cv::line(img, pt_new, pt_old, Scalar(125, 255, 125), 1);
cv::circle(img, pt_new, 2, Scalar(255, 0, 125), 1);
}
}
}
//Takes a descriptor and turns it into an xy point
void keypoints2points(const vector<KeyPoint>& in, vector<Point2f>& out)
{
out.clear();
out.reserve(in.size());
for (size_t i = 0; i < in.size(); ++i)
{
out.push_back(in[i].pt);
}
}
//Takes an xy point and appends that to a keypoint structure
void points2keypoints(const vector<Point2f>& in, vector<KeyPoint>& out)
{
out.clear();
out.reserve(in.size());
for (size_t i = 0; i < in.size(); ++i)
{
out.push_back(KeyPoint(in[i], 1));
}
}
//Uses computed homography H to warp original input points to new planar position
void warpKeypoints(const Mat& H, const vector<KeyPoint>& in, vector<KeyPoint>& out)
{
vector<Point2f> pts;
keypoints2points(in, pts);
vector<Point2f> pts_w(pts.size());
Mat m_pts_w(pts_w);
perspectiveTransform(Mat(pts), m_pts_w, H);
points2keypoints(pts_w, out);
}
//Converts matching indices to xy points
void matches2points(const vector<KeyPoint>& train, const vector<KeyPoint>& query,
const std::vector<cv::DMatch>& matches, std::vector<cv::Point2f>& pts_train,
std::vector<Point2f>& pts_query)
{
pts_train.clear();
pts_query.clear();
pts_train.reserve(matches.size());
pts_query.reserve(matches.size());
size_t i = 0;
for (; i < matches.size(); i++)
{
const DMatch & dmatch = matches[i];
pts_query.push_back(query[dmatch.queryIdx].pt);
pts_train.push_back(train[dmatch.trainIdx].pt);
}
}
void resetH(Mat&H)
{
H = Mat::eye(3, 3, CV_32FC1);
}
}
int main(int ac, char ** av)
{
if (ac != 2)
{
help(av);
return 1;
}
Ptr<BriefDescriptorExtractor> brief = BriefDescriptorExtractor::create(32);
VideoCapture capture;
capture.open(atoi(av[1]));
if (!capture.isOpened())
{
help(av);
cout << "capture device " << atoi(av[1]) << " failed to open!" << endl;
return 1;
}
cout << "following keys do stuff:" << endl;
cout << "t : grabs a reference frame to match against" << endl;
cout << "l : makes the reference frame new every frame" << endl;
cout << "q or escape: quit" << endl;
Mat frame;
vector<DMatch> matches;
BFMatcher desc_matcher(brief->defaultNorm());
vector<Point2f> train_pts, query_pts;
vector<KeyPoint> train_kpts, query_kpts;
vector<unsigned char> match_mask;
Mat gray;
bool ref_live = true;
Mat train_desc, query_desc;
Ptr<FastFeatureDetector> detector = FastFeatureDetector::create(10, true);
Mat H_prev = Mat::eye(3, 3, CV_32FC1);
for (;;)
{
capture >> frame;
if (frame.empty())
break;
cvtColor(frame, gray, COLOR_RGB2GRAY);
detector->detect(gray, query_kpts); //Find interest points
brief->compute(gray, query_kpts, query_desc); //Compute brief descriptors at each keypoint location
if (!train_kpts.empty())
{
vector<KeyPoint> test_kpts;
warpKeypoints(H_prev.inv(), query_kpts, test_kpts);
//Mat mask = windowedMatchingMask(test_kpts, train_kpts, 25, 25);
desc_matcher.match(query_desc, train_desc, matches, Mat());
drawKeypoints(frame, test_kpts, frame, Scalar(255, 0, 0), DrawMatchesFlags::DRAW_OVER_OUTIMG);
matches2points(train_kpts, query_kpts, matches, train_pts, query_pts);
if (matches.size() > 5)
{
Mat H = findHomography(train_pts, query_pts, RANSAC, 4, match_mask);
if (countNonZero(Mat(match_mask)) > 15)
{
H_prev = H;
}
else
resetH(H_prev);
drawMatchesRelative(train_kpts, query_kpts, matches, frame, match_mask);
}
else
resetH(H_prev);
}
else
{
H_prev = Mat::eye(3, 3, CV_32FC1);
Mat out;
drawKeypoints(gray, query_kpts, out);
frame = out;
}
imshow("frame", frame);
if (ref_live)
{
train_kpts = query_kpts;
query_desc.copyTo(train_desc);
}
char key = (char)waitKey(2);
switch (key)
{
case 'l':
ref_live = true;
resetH(H_prev);
break;
case 't':
ref_live = false;
train_kpts = query_kpts;
query_desc.copyTo(train_desc);
resetH(H_prev);
break;
case 27:
case 'q':
return 0;
break;
}
}
return 0;
}
#else
int main()
{
std::cerr << "OpenCV was built without 3d module" << std::endl;
return 0;
}
#endif
@@ -0,0 +1,685 @@
// 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
/*
* Functions to perform affine adaptation of keypoint and to calculate descriptors of elliptic regions
*/
#include "precomp.hpp"
#include "opencv2/imgproc.hpp"
namespace {
using namespace cv;
using namespace cv::xfeatures2d;
/*
* Functions to perform affine adaptation of circular keypoint
*/
void calcAffineCovariantRegions(const Mat& image, const std::vector<KeyPoint>& keypoints, std::vector<Elliptic_KeyPoint>& affRegions);
void calcAffineCovariantDescriptors( const Ptr<DescriptorExtractor>& dextractor, const Mat& img, std::vector<Elliptic_KeyPoint>& affRegions, Mat& descriptors );
void calcSecondMomentMatrix(const Mat & dx2, const Mat & dxy, const Mat & dy2, Point p, Matx22f& M);
bool calcAffineAdaptation(const Mat & image, Elliptic_KeyPoint& keypoint);
float selIntegrationScale(const Mat & image, float si, Point c);
float selDifferentiationScale(const Mat & image, Mat & Lxm2smooth, Mat & Lxmysmooth, Mat & Lym2smooth, float si, Point c);
float calcSecondMomentSqrt(const Mat & dx2, const Mat & dxy, const Mat & dy2, Point p, Matx22f& Mk);
float normMaxEval(Matx22f & U, Mat& uVal, Mat& uVect);
/*
* Calculates second moments matrix in point p
*/
void calcSecondMomentMatrix(const Mat & dx2, const Mat & dxy, const Mat & dy2, Point p, Matx22f & M)
{
int x = p.x;
int y = p.y;
M(0, 0) = dx2.at<float> (y, x);
M(0, 1) = M(1, 0) = dxy.at<float> (y, x);
M(1, 1) = dy2.at<float> (y, x);
}
/*
* Performs affine adaptation
*/
bool calcAffineAdaptation(const Mat & fimage, Elliptic_KeyPoint & keypoint)
{
Matx23f transf; /*Transformation matrix*/
Matx21f size; /*Image size after transformation*/
Matx21f c; /*Transformed point*/
Matx21f p; /*Image point*/
Matx22f U(1.f, 0.f, 0.f, 1.f); /*Normalization matrix*/
Mat warpedImg, Lxm2smooth, Lym2smooth, Lxmysmooth, img_roi;
Matx22f Mk;
float Qinv = 1, q, si = keypoint.si;
bool divergence = false, convergence = false;
int i = 0;
//Coordinates in image
int py = (int) keypoint.pt.y;
int px = (int) keypoint.pt.x;
//Roi coordinates
int roix, roiy;
//Coordinates in U-trasformation
int cx = px;
int cy = py;
int cxPr = cx;
int cyPr = cy;
float radius = keypoint.size / 2 * 1.4f;
float half_width, half_height;
Rect roi;
float ax1, ax2;
float phi = 0;
ax1 = ax2 = keypoint.size / 2;
Mat drawImg;
//Affine adaptation
while (i <= 10 && !divergence && !convergence)
{
//Transformation matrix
transf = Matx23f(
U(0,0), U(0,1), 0.f,
U(1,0), U(1,1), 0.f
);
keypoint.transf = transf;
Size_<float> boundingBox;
float ac_b2 = float(determinant(U));
boundingBox.width = ceil(U(1, 1)/ac_b2 * 3 * si*1.4f );
boundingBox.height = ceil(U(0, 0)/ac_b2 * 3 * si*1.4f );
//Create window around interest point
half_width = std::min((float) std::min(fimage.cols - px-1, px), boundingBox.width);
half_height = std::min((float) std::min(fimage.rows - py-1, py), boundingBox.height);
roix = max(px - (int) boundingBox.width, 0);
roiy = max(py - (int) boundingBox.height, 0);
roi = Rect(roix, roiy, px - roix + int(half_width)+1, py - roiy + int(half_height)+1);
//create ROI
img_roi = fimage(roi);
//Point within the ROI
p(0, 0) = float(px - roix);
p(1, 0) = float(py - roiy);
if (half_width <= 0 || half_height <= 0)
return divergence;
//Find coordinates of square's angles to find size of warped ellipse's bounding box
float u00 = U(0, 0);
float u01 = U(0, 1);
float u10 = U(1, 0);
float u11 = U(1, 1);
float minx = u01 * img_roi.rows < 0 ? u01 * img_roi.rows : 0;
float miny = u10 * img_roi.cols < 0 ? u10 * img_roi.cols : 0;
float maxx = (u00 * img_roi.cols > u00 * img_roi.cols + u01 * img_roi.rows ? u00
* img_roi.cols : u00 * img_roi.cols + u01 * img_roi.rows) - minx;
float maxy = (u11 * img_roi.rows > u10 * img_roi.cols + u11 * img_roi.rows ? u11
* img_roi.rows : u10 * img_roi.cols + u11 * img_roi.rows) - miny;
//Shift
transf(0, 2) = -minx;
transf(1, 2) = -miny;
/*float min_width = minx >= 0 ? u00 * img_roi.cols - u01 * img_roi.rows : u00 * img_roi.cols
+ u01 * img_roi.rows;
float min_height = miny >= 0 ? u11 * img_roi.rows - u10 * img_roi.cols : u10 * img_roi.cols
+ u11 * img_roi.rows;*/
if (maxx >= 2*radius+1 && maxy >= 2*radius+1)
{
//Size of normalized window must be 2*radius
//Transformation
Mat warpedImgRoi;
warpAffine(img_roi, warpedImgRoi, transf, Size(int(maxx), int(maxy)),INTER_AREA, BORDER_REPLICATE);
//Point in U-Normalized coordinates
c = U * p;
cx = int(c(0, 0) - minx);
cy = int(c(1, 0) - miny);
if (warpedImgRoi.rows > 2 * radius+1 && warpedImgRoi.cols > 2 * radius+1)
{
//Cut around normalized patch
roix = std::max(cx - int(ceil(radius)), 0);
roiy = std::max(cy - int(ceil(radius)), 0);
roi = Rect(roix, roiy,
cx - roix + std::min(int(ceil(radius)), warpedImgRoi.cols - cx-1)+1,
cy - roiy + std::min(int(ceil(radius)), warpedImgRoi.rows - cy-1)+1);
warpedImg = warpedImgRoi(roi);
//Coordinates in cutted ROI
cx = cx - roix;
cy = cy - roiy;
} else
warpedImgRoi.copyTo(warpedImg);
//Integration Scale selection
si = selIntegrationScale(warpedImg, si, Point(cx, cy));
//Differentation scale selection
selDifferentiationScale(warpedImg, Lxm2smooth, Lxmysmooth, Lym2smooth, si,
Point(cx, cy));
if (Lym2smooth.empty()) {
divergence = true;
continue;
}
//Spatial Localization
cxPr = cx; //Previous iteration point in normalized window
cyPr = cy;
float cornMax = 0;
for (int j = 0; j < 3; j++)
{
for (int t = 0; t < 3; t++)
{
float dx2 = Lxm2smooth.at<float> (cyPr - 1 + j, cxPr - 1 + t);
float dy2 = Lym2smooth.at<float> (cyPr - 1 + j, cxPr - 1 + t);
float dxy = Lxmysmooth.at<float> (cyPr - 1 + j, cxPr - 1 + t);
float det = dx2 * dy2 - dxy * dxy;
float tr = dx2 + dy2;
float cornerness = det - (0.04f * tr*tr);
if (cornerness > cornMax)
{
cornMax = cornerness;
cx = cxPr - 1 + t;
cy = cyPr - 1 + j;
}
}
}
//Transform point in image coordinates
p(0, 0) = float(px);
p(1, 0) = float(py);
//Displacement vector
c(0, 0) = float(cx - cxPr);
c(1, 0) = float(cy - cyPr);
//New interest point location in image
p = p + Matx22f(Matx22d(U).inv()) * c;
px = int(p(0, 0));
py = int(p(1, 0));
q = calcSecondMomentSqrt(Lxm2smooth, Lxmysmooth, Lym2smooth, Point(cx, cy), Mk);
float ratio = 1 - q;
//if ratio == 1 means q == 0 and one axes equals to 0
if (!cvIsNaN(ratio) && ratio != 1)
{
//Update U matrix
U = U * Mk;
Mat uVal, uV;
eigen(U, uVal, uV);
Qinv = normMaxEval(U, uVal, uV);
//Keypoint doesn't converge
if (Qinv >= 6)
divergence = true;
//Keypoint converges
else if (ratio <= 0.05f)
{
convergence = true;
//Set transformation matrix
transf = Matx23f(
U(0,0), U(0,1), 0.f,
U(1,0), U(1,1), 0.f
);
keypoint.transf = transf;
ax1 = 1.f / std::abs(uVal.at<float> (0, 0)) * 3 * si;
ax2 = 1.f / std::abs(uVal.at<float> (1, 0)) * 3 * si;
phi = float(atan(uV.at<float> (1, 0) / uV.at<float> (0, 0)) * (180) / CV_PI);
keypoint.axes = Size_<float> (ax1, ax2);
keypoint.angle = phi;
keypoint.pt = Point2f( (float) px, (float) py);
keypoint.si = si;
keypoint.size = 2 * 3 * si;
} else
radius = 3 * si * 1.4f;
} else divergence = true;
} else divergence = true;
++i;
}
return convergence;
}
/*
* Selects the integration scale that maximize LoG in point c
*/
float selIntegrationScale(const Mat & image, float si, Point c)
{
Mat Lap, L;
int cx = c.x;
int cy = c.y;
float maxLap = 0;
float maxsx = si;
int gsize;
float sigma, sigma_prev = 0;
image.copyTo(L);
/* Search best integration scale between previous and successive layer
*/
for (float u = 0.7f; u <= 1.41f; u += 0.1f)
{
float sik = u * si;
sigma = sqrt(powf(sik, 2) - powf(sigma_prev, 2));
gsize = int(ceil(sigma * 3)) * 2 + 1;
GaussianBlur(L, L, Size(gsize, gsize), sigma);
sigma_prev = sik;
Laplacian(L, Lap, CV_32F, 3);
float lapVal = sik * sik * std::abs(Lap.at<float> (cy, cx));
if (u == 0.7f)
maxLap = lapVal;
if (lapVal >= maxLap)
{
maxLap = lapVal;
maxsx = sik;
}
}
return maxsx;
}
/*
* Calculates second moments matrix square root
*/
float calcSecondMomentSqrt(const Mat & dx2, const Mat & dxy, const Mat & dy2, Point p, Matx22f & Mk)
{
Mat V, eigVal, Vinv, D;
Matx22f M;
calcSecondMomentMatrix(dx2, dxy, dy2, p, M);
/* *
* M = V * D * V.inv()
* V has eigenvectors as columns
* D is a diagonal Matrix with eigenvalues as elements
* V.inv() is the inverse of V
* */
eigen(M, eigVal, V);
V = V.t();
Vinv = V.inv();
float eval1 = eigVal.at<float> (0, 0) = sqrt(eigVal.at<float> (0, 0));
float eval2 = eigVal.at<float> (1, 0) = sqrt(eigVal.at<float> (1, 0));
D = Mat::diag(eigVal);
//square root of M
Mk = Mat(V * D * Vinv);
//return q isotropic measure
return min(eval1, eval2) / max(eval1, eval2);
}
float normMaxEval(Matx22f & U, Mat & uVal, Mat & uVec)
{
/* *
* Decomposition:
* U = V * D * V.inv()
* V has eigenvectors as columns
* D is a diagonal Matrix with eigenvalues as elements
* V.inv() is the inverse of V
* */
uVec = uVec.t();
Mat uVinv = uVec.inv();
//Normalize min eigenvalue to 1 to expand patch in the direction of min eigenvalue of U.inv()
float uval1 = uVal.at<float> (0, 0);
float uval2 = uVal.at<float> (1, 0);
if (std::abs(uval1) < std::abs(uval2))
{
uVal.at<float> (0, 0) = 1;
uVal.at<float> (1, 0) = uval2 / uval1;
} else
{
uVal.at<float> (1, 0) = 1;
uVal.at<float> (0, 0) = uval1 / uval2;
}
Mat D = Mat::diag(uVal);
//U normalized
U = Mat(uVec * D * uVinv);
return max(std::abs(uVal.at<float> (0, 0)), std::abs(uVal.at<float> (1, 0))) / min(
std::abs(uVal.at<float> (0, 0)), std::abs(uVal.at<float> (1, 0))); //define the direction of warping
}
/*
* Selects diffrentiation scale
*/
float selDifferentiationScale(const Mat & img, Mat & Lxm2smooth, Mat & Lxmysmooth,
Mat & Lym2smooth, float si, Point c)
{
float s = 0.5f;
float sdk = s * si;
float sigma_prev = 0, sigma;
Mat L, dx2, dxy, dy2;
double qMax = 0;
//Gaussian kernel size
int gsize;
Size ksize;
img.copyTo(L);
while (s <= 0.751f)
{
Matx22f M;
float sd = s * si;
//Smooth previous smoothed image L
sigma = sqrt(powf(sd, 2) - powf(sigma_prev, 2));
gsize = int(ceil(sigma * 3)) * 2 + 1;
GaussianBlur(L, L, Size(gsize, gsize), sigma);
sigma_prev = sd;
//X and Y derivatives
Mat Lx, Ly;
Sobel(L, Lx, L.depth(), 1, 0, 1);
Lx = Lx * sd;
Sobel(L, Ly, L.depth(), 0, 1, 1);
Ly = Ly * sd;
//Size of gaussian kernel
gsize = int(ceil(si * 3)) * 2 + 1;
ksize = Size(gsize, gsize);
Mat Lxm2 = Lx.mul(Lx);
GaussianBlur(Lxm2, dx2, ksize, si);
Mat Lym2 = Ly.mul(Ly);
GaussianBlur(Lym2, dy2, ksize, si);
Mat Lxmy = Lx.mul(Ly);
GaussianBlur(Lxmy, dxy, ksize, si);
calcSecondMomentMatrix(dx2, dxy, dy2, Point(c.x, c.y), M);
//calc eigenvalues
Mat eval;
eigen(M, eval);
double eval1 = std::abs(eval.at<float> (0, 0));
double eval2 = std::abs(eval.at<float> (1, 0));
double m = max(eval1, eval2);
double q = (m == 0) ? -1 : min(eval1, eval2) / m;
if (q >= qMax)
{
qMax = q;
sdk = sd;
dx2.copyTo(Lxm2smooth);
dxy.copyTo(Lxmysmooth);
dy2.copyTo(Lym2smooth);
}
s += 0.05f;
}
return sdk;
}
void calcAffineCovariantRegions(const Mat & image, const std::vector<KeyPoint> & keypoints,
std::vector<Elliptic_KeyPoint> & affRegions)
{
for (size_t i = 0; i < keypoints.size(); ++i)
{
KeyPoint kp = keypoints[i];
Elliptic_KeyPoint ex(kp.pt, 0, Size_<float> (kp.size / 2, kp.size / 2), kp.size,
kp.size / 6);
if (calcAffineAdaptation(image, ex))
affRegions.push_back(ex);
}
//Erase similar keypoint
float maxDiff = 4;
Mat colorimg;
for (size_t i = 0; i < affRegions.size(); i++)
{
Elliptic_KeyPoint kp1 = affRegions[i];
for (size_t j = i+1; j < affRegions.size(); j++){
Elliptic_KeyPoint kp2 = affRegions[j];
if(norm(kp1.pt-kp2.pt)<=maxDiff){
float phi1, phi2;
Size axes1, axes2;
float si1, si2;
phi1 = kp1.angle;
phi2 = kp2.angle;
axes1 = kp1.axes;
axes2 = kp2.axes;
si1 = kp1.si;
si2 = kp2.si;
if(std::abs(phi1-phi2)<15 && std::max(si1,si2)/std::min(si1,si2)<1.4f && axes1.width-axes2.width<5 && axes1.height-axes2.height<5){
affRegions.erase(affRegions.begin()+j);
j--;
}
}
}
}
}
void calcAffineCovariantDescriptors(const Ptr<DescriptorExtractor>& dextractor, const Mat& img,
std::vector<Elliptic_KeyPoint>& affRegions, Mat& descriptors)
{
assert(!affRegions.empty());
int descriptorSize = dextractor->descriptorSize();
int descriptorType = dextractor->descriptorType();
descriptors.create(Size(descriptorSize, int(affRegions.size())), descriptorType);
descriptors.setTo(0);
int i = 0;
for (std::vector<Elliptic_KeyPoint>::iterator it = affRegions.begin(); it < affRegions.end(); ++it)
{
Point p = it->pt;
Matx21f size;
size(0, 0) = size(1, 0) = it->size;
//U matrix
Matx23f transf = it->transf;
Matx22f U(
transf(0,0), transf(0,1),
transf(1,0), transf(1,1)
);
float radius = it->size / 2;
float si = it->si;
Size_<float> boundingBox;
float ac_b2 = float(determinant(U));
boundingBox.width = ceil(U(1, 1)/ac_b2 * 3 * si );
boundingBox.height = ceil(U(0, 0)/ac_b2 * 3 * si );
//Create window around interest point
float half_width = std::min((float) std::min(img.cols - p.x-1, p.x), boundingBox.width);
float half_height = std::min((float) std::min(img.rows - p.y-1, p.y), boundingBox.height);
int roix = max(p.x - (int) boundingBox.width, 0);
int roiy = max(p.y - (int) boundingBox.height, 0);
Rect roi = Rect(roix, roiy, p.x - roix + int(half_width)+1, p.y - roiy + int(half_height)+1);
Mat img_roi = img(roi);
size(0, 0) = float(img_roi.cols);
size(1, 0) = float(img_roi.rows);
size = U * size;
Mat transfImgRoi, transfImg;
warpAffine(img_roi, transfImgRoi, transf, Size(int(ceil(size(0, 0))), int(ceil(size(1, 0)))),
INTER_AREA, BORDER_DEFAULT);
Matx21f c; //Transformed point
Matx21f pt; //Image point
//Point within the Roi
pt(0, 0) = float(p.x - roix);
pt(1, 0) = float(p.y - roiy);
//Point in U-Normalized coordinates
c = U * pt;
float cx = c(0, 0);
float cy = c(1, 0);
//Cut around point to have patch of 2*keypoint->size
roix = std::max(int(ceil(cx - radius)), 0);
roiy = std::max(int(ceil(cy - radius)), 0);
roi = Rect(roix, roiy, int(ceil(std::min(cx - roix + radius, size(0, 0)))),
int(ceil(std::min(cy - roiy + radius, size(1, 0)))));
transfImg = transfImgRoi(roi);
cx = c(0, 0) - roix;
cy = c(1, 0) - roiy;
Mat tmpDesc;
KeyPoint kp(Point(int(cx), int(cy)), it->size);
std::vector<KeyPoint> k(1, kp);
transfImg.convertTo(transfImg, CV_8U);
dextractor->compute(transfImg, k, tmpDesc);
tmpDesc.row(0).copyTo(descriptors.row(i));
i++;
}
}
} // anonymous namespace
namespace cv
{
namespace xfeatures2d
{
class AffineFeature2D_Impl CV_FINAL : public AffineFeature2D
{
public:
AffineFeature2D_Impl(
Ptr<FeatureDetector> keypoint_detector,
Ptr<DescriptorExtractor> descriptor_extractor
) : m_keypoint_detector(keypoint_detector)
, m_descriptor_extractor(descriptor_extractor) {}
protected:
using Feature2D::detect; // overload, don't hide
void detect(InputArray image, std::vector<Elliptic_KeyPoint>& keypoints, InputArray mask) CV_OVERRIDE;
void detectAndCompute(InputArray image, InputArray mask, std::vector<Elliptic_KeyPoint>& keypoints, OutputArray descriptors, bool useProvidedKeypoints) CV_OVERRIDE;
void detectAndCompute(InputArray image, InputArray mask, std::vector<KeyPoint>& keypoints, OutputArray descriptors, bool useProvidedKeypoints) CV_OVERRIDE;
int descriptorSize() const CV_OVERRIDE;
int descriptorType() const CV_OVERRIDE;
int defaultNorm() const CV_OVERRIDE;
private:
Ptr<FeatureDetector> m_keypoint_detector;
Ptr<DescriptorExtractor> m_descriptor_extractor;
};
Ptr<AffineFeature2D> AffineFeature2D::create(
Ptr<FeatureDetector> keypoint_detector,
Ptr<DescriptorExtractor> descriptor_extractor)
{
return makePtr<AffineFeature2D_Impl>(keypoint_detector, descriptor_extractor);
}
void AffineFeature2D_Impl::detect(
InputArray image,
std::vector<Elliptic_KeyPoint>& keypoints,
InputArray mask)
{
std::vector<KeyPoint> non_elliptic_keypoints;
m_keypoint_detector->detect(image, non_elliptic_keypoints, mask);
Mat fimage;
image.getMat().convertTo(fimage, CV_32F, 1.f/255);
calcAffineCovariantRegions(fimage, non_elliptic_keypoints, keypoints);
}
void AffineFeature2D_Impl::detectAndCompute(
InputArray image,
InputArray mask,
std::vector<Elliptic_KeyPoint>& keypoints,
OutputArray descriptors,
bool useProvidedKeypoints)
{
if(!useProvidedKeypoints)
{
std::vector<KeyPoint> non_elliptic_keypoints;
m_keypoint_detector->detect(image, non_elliptic_keypoints, mask);
Mat fimage;
image.getMat().convertTo(fimage, CV_32F, 1.f/255);
calcAffineCovariantRegions(fimage, non_elliptic_keypoints, keypoints);
}
if(descriptors.needed())calcAffineCovariantDescriptors(m_descriptor_extractor, image.getMat(), keypoints, descriptors.getMatRef());
}
void AffineFeature2D_Impl::detectAndCompute(
InputArray image,
InputArray mask,
std::vector<KeyPoint>& keypoints,
OutputArray descriptors,
bool useProvidedKeypoints)
{
if(!useProvidedKeypoints)
{
m_keypoint_detector->detect(image, keypoints, mask);
}
if(descriptors.needed()) {
Mat fimage;
image.getMat().convertTo(fimage, CV_32F, 1.f/255);
std::vector<Elliptic_KeyPoint> elliptic_keypoints;
calcAffineCovariantRegions(fimage, keypoints, elliptic_keypoints);
calcAffineCovariantDescriptors(m_descriptor_extractor, image.getMat(), elliptic_keypoints, descriptors.getMatRef());
}
}
int AffineFeature2D_Impl::descriptorSize() const
{
return m_descriptor_extractor->descriptorSize();
}
int AffineFeature2D_Impl::descriptorType() const
{
return m_descriptor_extractor->descriptorType();
}
int AffineFeature2D_Impl::defaultNorm() const
{
return m_descriptor_extractor->defaultNorm();
}
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+71
View File
@@ -0,0 +1,71 @@
/* This is AGAST and OAST, an optimal and accelerated corner detector
based on the accelerated segment tests
Below is the original copyright and the references */
/*
Copyright (C) 2010 Elmar Mair
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:
* Adaptive and Generic Corner Detection Based on the Accelerated Segment Test,
Elmar Mair and Gregory D. Hager and Darius Burschka
and Michael Suppa and Gerhard Hirzinger ECCV 2010
URL: http://www6.in.tum.de/Main/ResearchAgast
*/
#ifndef __OPENCV_FEATURES_2D_AGAST_HPP__
#define __OPENCV_FEATURES_2D_AGAST_HPP__
#ifdef __cplusplus
#include "precomp.hpp"
namespace cv
{
namespace xfeatures2d
{
#if !(defined __i386__ || defined(_M_IX86) || defined __x86_64__ || defined(_M_X64))
int agast_tree_search(const uint32_t table_struct32[], int pixel_[], const unsigned char* const ptr, int threshold);
int AGAST_ALL_SCORE(const uchar* ptr, const int pixel[], int threshold, AgastFeatureDetector::DetectorType agasttype);
#endif //!(defined __i386__ || defined(_M_IX86) || defined __x86_64__ || defined(_M_X64))
void makeAgastOffsets(int pixel[16], int row_stride, AgastFeatureDetector::DetectorType type);
template<AgastFeatureDetector::DetectorType type>
int agast_cornerScore(const uchar* ptr, const int pixel[], int threshold);
}
}
#endif
#endif
+280
View File
@@ -0,0 +1,280 @@
/*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*/
/*
OpenCV wrapper of reference implementation of
[1] Fast Explicit Diffusion for Accelerated Features in Nonlinear Scale Spaces.
Pablo F. Alcantarilla, J. Nuevo and Adrien Bartoli.
In British Machine Vision Conference (BMVC), Bristol, UK, September 2013
http://www.robesafe.com/personal/pablo.alcantarilla/papers/Alcantarilla13bmvc.pdf
@author Eugene Khvedchenya <ekhvedchenya@gmail.com>
*/
#include "precomp.hpp"
#include "kaze/AKAZEFeatures.h"
#include <iostream>
namespace cv
{
namespace xfeatures2d
{
using namespace std;
class AKAZE_Impl : public AKAZE
{
public:
AKAZE_Impl(DescriptorType _descriptor_type, int _descriptor_size, int _descriptor_channels,
float _threshold, int _octaves, int _sublevels, KAZE::DiffusivityType _diffusivity, int _max_points)
: descriptor(_descriptor_type)
, descriptor_channels(_descriptor_channels)
, descriptor_size(_descriptor_size)
, threshold(_threshold)
, octaves(_octaves)
, sublevels(_sublevels)
, diffusivity(_diffusivity)
, max_points(_max_points)
{
}
virtual ~AKAZE_Impl() CV_OVERRIDE
{
}
void setDescriptorType(int dtype) CV_OVERRIDE{ descriptor = dtype; }
int getDescriptorType() const CV_OVERRIDE{ return descriptor; }
void setDescriptorSize(int dsize) CV_OVERRIDE { descriptor_size = dsize; }
int getDescriptorSize() const CV_OVERRIDE { return descriptor_size; }
void setDescriptorChannels(int dch) CV_OVERRIDE { descriptor_channels = dch; }
int getDescriptorChannels() const CV_OVERRIDE { return descriptor_channels; }
void setThreshold(double threshold_) CV_OVERRIDE { threshold = (float)threshold_; }
double getThreshold() const CV_OVERRIDE { return threshold; }
void setNOctaves(int octaves_) CV_OVERRIDE { octaves = octaves_; }
int getNOctaves() const CV_OVERRIDE { return octaves; }
void setNOctaveLayers(int octaveLayers_) CV_OVERRIDE { sublevels = octaveLayers_; }
int getNOctaveLayers() const CV_OVERRIDE { return sublevels; }
void setDiffusivity(int diff_) CV_OVERRIDE{ diffusivity = diff_; }
int getDiffusivity() const CV_OVERRIDE{ return diffusivity; }
void setMaxPoints(int max_points_) CV_OVERRIDE { max_points = max_points_; }
int getMaxPoints() const CV_OVERRIDE { return max_points; }
// returns the descriptor size in bytes
int descriptorSize() const CV_OVERRIDE
{
switch (descriptor)
{
case DESCRIPTOR_KAZE:
case DESCRIPTOR_KAZE_UPRIGHT:
return 64;
case DESCRIPTOR_MLDB:
case DESCRIPTOR_MLDB_UPRIGHT:
// We use the full length binary descriptor -> 486 bits
if (descriptor_size == 0)
{
int t = (6 + 36 + 120) * descriptor_channels;
return divUp(t, 8);
}
else
{
// We use the random bit selection length binary descriptor
return divUp(descriptor_size, 8);
}
default:
return -1;
}
}
// returns the descriptor type
int descriptorType() const CV_OVERRIDE
{
switch (descriptor)
{
case DESCRIPTOR_KAZE:
case DESCRIPTOR_KAZE_UPRIGHT:
return CV_32F;
case DESCRIPTOR_MLDB:
case DESCRIPTOR_MLDB_UPRIGHT:
return CV_8U;
default:
return -1;
}
}
// returns the default norm type
int defaultNorm() const CV_OVERRIDE
{
switch (descriptor)
{
case DESCRIPTOR_KAZE:
case DESCRIPTOR_KAZE_UPRIGHT:
return NORM_L2;
case DESCRIPTOR_MLDB:
case DESCRIPTOR_MLDB_UPRIGHT:
return NORM_HAMMING;
default:
return -1;
}
}
void detectAndCompute(InputArray image, InputArray mask,
std::vector<KeyPoint>& keypoints,
OutputArray descriptors,
bool useProvidedKeypoints) CV_OVERRIDE
{
CV_INSTRUMENT_REGION();
CV_Assert( ! image.empty() );
AKAZEOptions options;
options.descriptor = descriptor;
options.descriptor_channels = descriptor_channels;
options.descriptor_size = descriptor_size;
options.img_width = image.cols();
options.img_height = image.rows();
options.dthreshold = threshold;
options.omax = octaves;
options.nsublevels = sublevels;
options.diffusivity = diffusivity;
AKAZEFeatures impl(options);
impl.Create_Nonlinear_Scale_Space(image);
if (!useProvidedKeypoints)
{
impl.Feature_Detection(keypoints);
}
if (!mask.empty())
{
KeyPointsFilter::runByPixelsMask(keypoints, mask.getMat());
}
if (max_points > 0 && (int)keypoints.size() > max_points) {
std::partial_sort(keypoints.begin(), keypoints.begin() + max_points, keypoints.end(),
[](const cv::KeyPoint& k1, const cv::KeyPoint& k2) {return k1.response > k2.response;});
keypoints.erase(keypoints.begin() + max_points, keypoints.end());
}
if(descriptors.needed())
{
impl.Compute_Descriptors(keypoints, descriptors);
CV_Assert((descriptors.empty() || descriptors.cols() == descriptorSize()));
CV_Assert((descriptors.empty() || (descriptors.type() == descriptorType())));
}
}
void write(FileStorage& fs) const CV_OVERRIDE
{
writeFormat(fs);
fs << "name" << getDefaultName();
fs << "descriptor" << descriptor;
fs << "descriptor_channels" << descriptor_channels;
fs << "descriptor_size" << descriptor_size;
fs << "threshold" << threshold;
fs << "octaves" << octaves;
fs << "sublevels" << sublevels;
fs << "diffusivity" << diffusivity;
fs << "max_points" << max_points;
}
void read(const FileNode& fn) CV_OVERRIDE
{
// if node is empty, keep previous value
if (!fn["descriptor"].empty())
descriptor = static_cast<DescriptorType>((int)fn["descriptor"]);
if (!fn["descriptor_channels"].empty())
descriptor_channels = (int)fn["descriptor_channels"];
if (!fn["descriptor_size"].empty())
descriptor_size = (int)fn["descriptor_size"];
if (!fn["threshold"].empty())
threshold = (float)fn["threshold"];
if (!fn["octaves"].empty())
octaves = (int)fn["octaves"];
if (!fn["sublevels"].empty())
sublevels = (int)fn["sublevels"];
if (!fn["diffusivity"].empty())
diffusivity = static_cast<KAZE::DiffusivityType>((int)fn["diffusivity"]);
if (!fn["max_points"].empty())
max_points = (int)fn["max_points"];
}
int descriptor;
int descriptor_channels;
int descriptor_size;
float threshold;
int octaves;
int sublevels;
int diffusivity;
int max_points;
};
Ptr<AKAZE> AKAZE::create(DescriptorType descriptor_type,
int descriptor_size, int descriptor_channels,
float threshold, int octaves,
int sublevels, KAZE::DiffusivityType diffusivity, int max_points)
{
return makePtr<AKAZE_Impl>(descriptor_type, descriptor_size, descriptor_channels,
threshold, octaves, sublevels, diffusivity, max_points);
}
String AKAZE::getDefaultName() const
{
return (Feature2D::getDefaultName() + ".AKAZE");
}
}
} // namespace cv
+219
View File
@@ -0,0 +1,219 @@
/*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
{
namespace xfeatures2d
{
BOWTrainer::BOWTrainer() : size(0)
{}
BOWTrainer::~BOWTrainer()
{}
void BOWTrainer::add( const Mat& _descriptors )
{
CV_Assert( !_descriptors.empty() );
if( !descriptors.empty() )
{
CV_Assert( descriptors[0].cols == _descriptors.cols );
CV_Assert( descriptors[0].type() == _descriptors.type() );
size += _descriptors.rows;
}
else
{
size = _descriptors.rows;
}
descriptors.push_back(_descriptors);
}
const std::vector<Mat>& BOWTrainer::getDescriptors() const
{
return descriptors;
}
int BOWTrainer::descriptorsCount() const
{
return descriptors.empty() ? 0 : size;
}
void BOWTrainer::clear()
{
descriptors.clear();
}
BOWKMeansTrainer::BOWKMeansTrainer( int _clusterCount, const TermCriteria& _termcrit,
int _attempts, int _flags ) :
clusterCount(_clusterCount), termcrit(_termcrit), attempts(_attempts), flags(_flags)
{}
Mat BOWKMeansTrainer::cluster() const
{
CV_INSTRUMENT_REGION();
CV_Assert( !descriptors.empty() );
Mat mergedDescriptors( descriptorsCount(), descriptors[0].cols, descriptors[0].type() );
for( size_t i = 0, start = 0; i < descriptors.size(); i++ )
{
Mat submut = mergedDescriptors.rowRange((int)start, (int)(start + descriptors[i].rows));
descriptors[i].copyTo(submut);
start += descriptors[i].rows;
}
return cluster( mergedDescriptors );
}
BOWKMeansTrainer::~BOWKMeansTrainer()
{}
Mat BOWKMeansTrainer::cluster( const Mat& _descriptors ) const
{
CV_INSTRUMENT_REGION();
Mat labels, vocabulary;
kmeans( _descriptors, clusterCount, labels, termcrit, attempts, flags, vocabulary );
return vocabulary;
}
BOWImgDescriptorExtractor::BOWImgDescriptorExtractor( const Ptr<DescriptorExtractor>& _dextractor,
const Ptr<DescriptorMatcher>& _dmatcher ) :
dextractor(_dextractor), dmatcher(_dmatcher)
{}
BOWImgDescriptorExtractor::BOWImgDescriptorExtractor( const Ptr<DescriptorMatcher>& _dmatcher ) :
dmatcher(_dmatcher)
{}
BOWImgDescriptorExtractor::~BOWImgDescriptorExtractor()
{}
void BOWImgDescriptorExtractor::setVocabulary( const Mat& _vocabulary )
{
dmatcher->clear();
vocabulary = _vocabulary;
dmatcher->add( std::vector<Mat>(1, vocabulary) );
}
const Mat& BOWImgDescriptorExtractor::getVocabulary() const
{
return vocabulary;
}
void BOWImgDescriptorExtractor::compute( InputArray image, std::vector<KeyPoint>& keypoints, OutputArray imgDescriptor,
std::vector<std::vector<int> >* pointIdxsOfClusters, Mat* descriptors )
{
CV_INSTRUMENT_REGION();
imgDescriptor.release();
if( keypoints.empty() )
return;
// Compute descriptors for the image.
Mat _descriptors;
dextractor->compute( image, keypoints, _descriptors );
compute( _descriptors, imgDescriptor, pointIdxsOfClusters );
// Add the descriptors of image keypoints
if (descriptors) {
*descriptors = _descriptors.clone();
}
}
int BOWImgDescriptorExtractor::descriptorSize() const
{
return vocabulary.empty() ? 0 : vocabulary.rows;
}
int BOWImgDescriptorExtractor::descriptorType() const
{
return CV_32FC1;
}
void BOWImgDescriptorExtractor::compute( InputArray keypointDescriptors, OutputArray _imgDescriptor, std::vector<std::vector<int> >* pointIdxsOfClusters )
{
CV_INSTRUMENT_REGION();
CV_Assert( !vocabulary.empty() );
CV_Assert(!keypointDescriptors.empty());
int clusterCount = descriptorSize(); // = vocabulary.rows
// Match keypoint descriptors to cluster center (to vocabulary)
std::vector<DMatch> matches;
dmatcher->match( keypointDescriptors, matches );
// Compute image descriptor
if( pointIdxsOfClusters )
{
pointIdxsOfClusters->clear();
pointIdxsOfClusters->resize(clusterCount);
}
_imgDescriptor.create(1, clusterCount, descriptorType());
_imgDescriptor.setTo(Scalar::all(0));
Mat imgDescriptor = _imgDescriptor.getMat();
float *dptr = imgDescriptor.ptr<float>();
for( size_t i = 0; i < matches.size(); i++ )
{
int queryIdx = matches[i].queryIdx;
int trainIdx = matches[i].trainIdx; // cluster index
CV_Assert( queryIdx == (int)i );
dptr[trainIdx] = dptr[trainIdx] + 1.f;
if( pointIdxsOfClusters )
(*pointIdxsOfClusters)[trainIdx].push_back( queryIdx );
}
// Normalize image descriptor.
imgDescriptor /= keypointDescriptors.size().height;
}
}
} // namespace cv
+484
View File
@@ -0,0 +1,484 @@
// 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.
// Author: Iago Suarez <iago.suarez.canosa@alumnos.upm.es>
// Implementation of the article:
// Iago Suarez, Ghesn Sfeir, Jose M. Buenaposada, and Luis Baumela.
// BEBLID: Boosted Efficient Binary Local Image Descriptor.
// Pattern Recognition Letters, 133:366372, 2020.
#include "precomp.hpp"
#define CV_BEBLID_PARALLEL
#define CV_ROUNDNUM(x) ((int)(x + 0.5f))
#define CV_DEGREES_TO_RADS 0.017453292519943295 // (M_PI / 180.0)
#define CV_BEBLID_EXTRA_RATIO_MARGIN 1.75f
using namespace cv;
using namespace std;
namespace cv
{
namespace xfeatures2d
{
// Struct containing the 6 parameters that define an Average Box weak-learner
struct ABWLParams
{
int x1, y1, x2, y2, boxRadius, th;
};
// Same as previous with floating point threshold
struct ABWLParamsFloatTh
{
int x1, y1, x2, y2, boxRadius;
float th;
};
// BEBLID implementation
template <class WeakLearnerT>
class BEBLID_Impl CV_FINAL: public BEBLID
{
public:
// constructor
explicit BEBLID_Impl(float scale_factor, const std::vector<WeakLearnerT>& wl_params);
// destructor
~BEBLID_Impl() CV_OVERRIDE = default;
// returns the descriptor length in bytes
int descriptorSize() const CV_OVERRIDE { return int(wl_params_.size() / 8); }
// returns the descriptor type
int descriptorType() const CV_OVERRIDE { return CV_8UC1; }
// returns the default norm type
int defaultNorm() const CV_OVERRIDE { return cv::NORM_HAMMING; }
void setScaleFactor(float scale_factor) CV_OVERRIDE { scale_factor_ = scale_factor; }
float getScaleFactor() const { return scale_factor_;}
// compute descriptors given keypoints
void compute(InputArray image, vector<KeyPoint> &keypoints, OutputArray descriptors) CV_OVERRIDE;
private:
std::vector<WeakLearnerT> wl_params_;
float scale_factor_;
cv::Size patch_size_;
void computeBoxDiffsDescriptor(const cv::Mat &integralImg,
const std::vector<cv::KeyPoint> &keypoints,
cv::Mat &descriptors);
}; // END BEBLID_Impl CLASS
// TEBLID implementation
class TEBLID_Impl CV_FINAL: public TEBLID
{
public:
// constructor
explicit TEBLID_Impl(float scale_factor, const std::vector<ABWLParamsFloatTh>& wl_params) :
impl(scale_factor, wl_params){}
// destructor
~TEBLID_Impl() CV_OVERRIDE = default;
// returns the descriptor length in bytes
int descriptorSize() const CV_OVERRIDE { return impl.descriptorSize(); }
// returns the descriptor type
int descriptorType() const CV_OVERRIDE { return impl.descriptorType(); }
// returns the default norm type
int defaultNorm() const CV_OVERRIDE { return impl.defaultNorm(); }
// compute descriptors given keypoints
void compute(InputArray image, vector<KeyPoint> &keypoints, OutputArray descriptors) CV_OVERRIDE
{
impl.compute(image, keypoints, descriptors);
}
private:
BEBLID_Impl<ABWLParamsFloatTh> impl;
}; // END TEBLID_Impl CLASS
Ptr<TEBLID> TEBLID::create(float scale_factor, int n_bits)
{
if (n_bits == TEBLID::SIZE_512_BITS)
{
#include "teblid.p512.hpp"
return makePtr<TEBLID_Impl>(scale_factor, teblid_wl_params_512);
}
else if(n_bits == TEBLID::SIZE_256_BITS)
{
#include "teblid.p256.hpp"
return makePtr<TEBLID_Impl>(scale_factor, teblid_wl_params_256);
}
else
{
CV_Error(Error::StsBadArg, "n_bits should be either TEBLID::SIZE_512_BITS or TEBLID::SIZE_256_BITS");
}
}
/**
* @brief Function that determines if a keypoint is close to the image border.
* @param kp The detected keypoint
* @param imgSize The size of the image
* @param patchSize The size of the normalized patch where the measurement functions were learnt.
* @param scaleFactor A scale factor that magnifies the measurement functions w.r.t. the keypoint.
* @return true if the keypoint is in the border, false otherwise
*/
static inline bool isKeypointInTheBorder(const cv::KeyPoint &kp,
const cv::Size &imgSize,
const cv::Size &patchSize = {32, 32},
float scaleFactor = 1)
{
// This would be the correct measure but since we will compare with half of the size, use this as border size
float s = scaleFactor * kp.size / (patchSize.width + patchSize.height);
cv::Size2f border(patchSize.width * s * CV_BEBLID_EXTRA_RATIO_MARGIN,
patchSize.height * s * CV_BEBLID_EXTRA_RATIO_MARGIN);
if (kp.pt.x < border.width || kp.pt.x + border.width >= imgSize.width)
return true;
if (kp.pt.y < border.height || kp.pt.y + border.height >= imgSize.height)
return true;
return false;
}
/**
* @brief Rectifies the coordinates of the measurement functions that conform the descriptor
* with the keypoint location parameters.
* @param wlPatchParams The input weak learner parameters learnt for the normalized patch
* @param wlImageParams The output weak learner parameters adapted to the keypoint location
* @param kp The keypoint defining the offset, rotation and scale to be applied
* @param scaleFactor A scale factor that magnifies the measurement functions w.r.t. the keypoint.
* @param patchSize The size of the normalized patch where the measurement functions were learnt.
*/
template< typename WeakLearnerT>
static inline void rectifyABWL(const std::vector<WeakLearnerT> &wlPatchParams,
std::vector<WeakLearnerT> &wlImageParams,
const cv::KeyPoint &kp,
float scaleFactor = 1,
const cv::Size &patchSize = cv::Size(32, 32))
{
float m00, m01, m02, m10, m11, m12;
float s, cosine, sine;
s = scaleFactor * kp.size / (0.5f * (patchSize.width + patchSize.height));
wlImageParams.resize(wlPatchParams.size());
if (kp.angle == -1)
{
m00 = s;
m01 = 0.0f;
m02 = -0.5f * s * patchSize.width + kp.pt.x;
m10 = 0.0f;
m11 = s;
m12 = -s * 0.5f * patchSize.height + kp.pt.y;
}
else
{
cosine = (kp.angle >= 0) ? float(cos(kp.angle * CV_DEGREES_TO_RADS)) : 1.f;
sine = (kp.angle >= 0) ? float(sin(kp.angle * CV_DEGREES_TO_RADS)) : 0.f;
m00 = s * cosine;
m01 = -s * sine;
m02 = (-s * cosine + s * sine) * patchSize.width * 0.5f + kp.pt.x;
m10 = s * sine;
m11 = s * cosine;
m12 = (-s * sine - s * cosine) * patchSize.height * 0.5f + kp.pt.y;
}
for (size_t i = 0; i < wlPatchParams.size(); i++)
{
wlImageParams[i].x1 = CV_ROUNDNUM(m00 * wlPatchParams[i].x1 + m01 * wlPatchParams[i].y1 + m02);
wlImageParams[i].y1 = CV_ROUNDNUM(m10 * wlPatchParams[i].x1 + m11 * wlPatchParams[i].y1 + m12);
wlImageParams[i].x2 = CV_ROUNDNUM(m00 * wlPatchParams[i].x2 + m01 * wlPatchParams[i].y2 + m02);
wlImageParams[i].y2 = CV_ROUNDNUM(m10 * wlPatchParams[i].x2 + m11 * wlPatchParams[i].y2 + m12);
wlImageParams[i].boxRadius = CV_ROUNDNUM(s * wlPatchParams[i].boxRadius);
}
}
/**
* @brief Computes the Average Box Weak-Learner response, measuring the difference of
* gray level in the two square regions.
* @param wlImageParams The weak-learner parameter defining the size and locations of each box.
* @param integralImage The integral image used to compute the average gray value in the square regions.
* @return The difference of gray level in the two squares defined by wlImageParams
*/
template <typename WeakLearnerT>
static inline float computeABWLResponse(const WeakLearnerT &wlImageParams,
const cv::Mat &integralImage)
{
CV_DbgAssert(!integralImage.empty());
CV_DbgAssert(integralImage.type() == CV_32SC1);
int frameWidth, frameHeight, box1x1, box1y1, box1x2, box1y2, box2x1, box2y1, box2x2, box2y2;
int A, B, C, D;
int box_area1, box_area2;
float sum1, sum2, average1, average2;
// Since the integral image has one extra row and col, calculate the patch dimensions
frameWidth = integralImage.cols;
frameHeight = integralImage.rows;
// For the first box, we calculate its margin coordinates
box1x1 = wlImageParams.x1 - wlImageParams.boxRadius;
if (box1x1 < 0)
box1x1 = 0;
else if (box1x1 >= frameWidth - 1)
box1x1 = frameWidth - 2;
box1y1 = wlImageParams.y1 - wlImageParams.boxRadius;
if (box1y1 < 0)
box1y1 = 0;
else if (box1y1 >= frameHeight - 1)
box1y1 = frameHeight - 2;
box1x2 = wlImageParams.x1 + wlImageParams.boxRadius + 1;
if (box1x2 <= 0)
box1x2 = 1;
else if (box1x2 >= frameWidth)
box1x2 = frameWidth - 1;
box1y2 = wlImageParams.y1 + wlImageParams.boxRadius + 1;
if (box1y2 <= 0)
box1y2 = 1;
else if (box1y2 >= frameHeight)
box1y2 = frameHeight - 1;
CV_DbgAssert((box1x1 < box1x2 && box1y1 < box1y2) && "Box 1 has size 0");
// For the second box, we calculate its margin coordinates
box2x1 = wlImageParams.x2 - wlImageParams.boxRadius;
if (box2x1 < 0)
box2x1 = 0;
else if (box2x1 >= frameWidth - 1)
box2x1 = frameWidth - 2;
box2y1 = wlImageParams.y2 - wlImageParams.boxRadius;
if (box2y1 < 0)
box2y1 = 0;
else if (box2y1 >= frameHeight - 1)
box2y1 = frameHeight - 2;
box2x2 = wlImageParams.x2 + wlImageParams.boxRadius + 1;
if (box2x2 <= 0)
box2x2 = 1;
else if (box2x2 >= frameWidth)
box2x2 = frameWidth - 1;
box2y2 = wlImageParams.y2 + wlImageParams.boxRadius + 1;
if (box2y2 <= 0)
box2y2 = 1;
else if (box2y2 >= frameHeight)
box2y2 = frameHeight - 1;
CV_DbgAssert((box2x1 < box2x2 && box2y1 < box2y2) && "Box 2 has size 0");
// Read the integral image values for the first box
A = integralImage.at<int>(box1y1, box1x1);
B = integralImage.at<int>(box1y1, box1x2);
C = integralImage.at<int>(box1y2, box1x1);
D = integralImage.at<int>(box1y2, box1x2);
// Calculate the mean intensity value of the pixels in the box
sum1 = float(A + D - B - C);
box_area1 = (box1y2 - box1y1) * (box1x2 - box1x1);
CV_DbgAssert(box_area1 > 0);
average1 = sum1 / box_area1;
// Calculate the indices on the integral image where the box falls
A = integralImage.at<int>(box2y1, box2x1);
B = integralImage.at<int>(box2y1, box2x2);
C = integralImage.at<int>(box2y2, box2x1);
D = integralImage.at<int>(box2y2, box2x2);
// Calculate the mean intensity value of the pixels in the box
sum2 = float(A + D - B - C);
box_area2 = (box2y2 - box2y1) * (box2x2 - box2x1);
CV_DbgAssert(box_area2 > 0);
average2 = sum2 / box_area2;
return average1 - average2;
}
// descriptor computation using keypoints
template <class WeakLearnerT>
void BEBLID_Impl<WeakLearnerT>::compute(InputArray _image, vector<KeyPoint> &keypoints, OutputArray _descriptors)
{
Mat image = _image.getMat();
if (image.empty())
return;
if (keypoints.empty())
{
// clean output buffer (it may be reused with "allocated" data)
_descriptors.release();
return;
}
Mat grayImage;
switch (image.type()) {
case CV_8UC1:
grayImage = image;
break;
case CV_8UC3:
cvtColor(image, grayImage, COLOR_BGR2GRAY);
break;
case CV_8UC4:
cvtColor(image, grayImage, COLOR_BGRA2GRAY);
break;
default:
CV_Error(Error::StsBadArg, "Image should be 8UC1, 8UC3 or 8UC4");
}
cv::Mat integralImg;
// compute the integral image
cv::integral(grayImage, integralImg);
// Create the output array of descriptors
_descriptors.create((int)keypoints.size(), descriptorSize(), descriptorType());
// descriptor storage
cv::Mat descriptors = _descriptors.getMat();
CV_DbgAssert(descriptors.type() == CV_8UC1);
// Compute the BEBLID descriptors
computeBoxDiffsDescriptor(integralImg, keypoints, descriptors);
}
// constructor
template <class WeakLearnerT>
BEBLID_Impl<WeakLearnerT>::BEBLID_Impl(float scale_factor, const std::vector<WeakLearnerT>& wl_params)
: wl_params_(wl_params), scale_factor_(scale_factor), patch_size_(32, 32)
{
}
// Internal function that implements the core of BEBLID descriptor
template<class WeakLearnerT>
void BEBLID_Impl<WeakLearnerT>::computeBoxDiffsDescriptor(const cv::Mat &integralImg,
const std::vector<cv::KeyPoint> &keypoints,
cv::Mat &descriptors)
{
CV_DbgAssert(!integralImg.empty());
CV_DbgAssert(size_t(descriptors.rows) == keypoints.size());
const int *integralPtr = integralImg.ptr<int>();
cv::Size frameSize(integralImg.cols - 1, integralImg.rows - 1);
// Parallel Loop to process descriptors
#ifndef CV_BEBLID_PARALLEL
const cv::Range range(0, keypoints.size());
#else
cv::parallel_for_(cv::Range(0, int(keypoints.size())), [&](const Range &range)
#endif
{
// Get a pointer to the first element in the range
WeakLearnerT *wl;
float responseFun;
int areaResponseFun, kpIdx;
size_t wlIdx;
int box1x1, box1y1, box1x2, box1y2, box2x1, box2y1, box2x2, box2y2, bit_idx, side;
uchar byte = 0;
std::vector<WeakLearnerT> imgWLParams(wl_params_.size());
uchar *d = &descriptors.at<uchar>(range.start, 0);
for (kpIdx = range.start; kpIdx < range.end; kpIdx++)
{
// Rectify the weak learners coordinates using the keypoint information
rectifyABWL(wl_params_, imgWLParams, keypoints[kpIdx], scale_factor_, patch_size_);
if (isKeypointInTheBorder(keypoints[kpIdx], frameSize, patch_size_, scale_factor_))
{
// Code to process the keypoints in the image margins
for (wlIdx = 0; wlIdx < wl_params_.size(); wlIdx++) {
bit_idx = 7 - int(wlIdx % 8);
responseFun = computeABWLResponse(imgWLParams[wlIdx], integralImg);
// Set the bit to 1 if the response function is less or equal to the threshod
byte |= (responseFun <= wl_params_[wlIdx].th) << bit_idx;
// If we filled the byte, save it
if (bit_idx == 0)
{
*d = byte;
byte = 0;
d++;
}
}
}
else
{
// Code to process the keypoints in the image center
wl = imgWLParams.data();
for (wlIdx = 0; wlIdx < wl_params_.size(); wlIdx++)
{
bit_idx = 7 - int(wlIdx % 8);
// For the first box, we calculate its margin coordinates
box1x1 = wl->x1 - wl->boxRadius;
box1y1 = (wl->y1 - wl->boxRadius) * integralImg.cols;
box1x2 = wl->x1 + wl->boxRadius + 1;
box1y2 = (wl->y1 + wl->boxRadius + 1) * integralImg.cols;
// For the second box, we calculate its margin coordinates
box2x1 = wl->x2 - wl->boxRadius;
box2y1 = (wl->y2 - wl->boxRadius) * integralImg.cols;
box2x2 = wl->x2 + wl->boxRadius + 1;
box2y2 = (wl->y2 + wl->boxRadius + 1) * integralImg.cols;
side = 1 + (wl->boxRadius << 1);
// Get the difference between the average level of the two boxes
areaResponseFun = (integralPtr[box1y1 + box1x1] // A of Box1
+ integralPtr[box1y2 + box1x2] // D of Box1
- integralPtr[box1y1 + box1x2] // B of Box1
- integralPtr[box1y2 + box1x1] // C of Box1
- integralPtr[box2y1 + box2x1] // A of Box2
- integralPtr[box2y2 + box2x2] // D of Box2
+ integralPtr[box2y1 + box2x2] // B of Box2
+ integralPtr[box2y2 + box2x1]); // C of Box2
// Set the bit to 1 if the response function is less or equal to the threshod
byte |= (areaResponseFun <= (wl_params_[wlIdx].th * (side * side))) << bit_idx;
wl++;
// If we filled the byte, save it
if (bit_idx == 0)
{
*d = byte;
byte = 0;
d++;
}
} // End of for each dimension
} // End of else (of pixels in the image center)
} // End of for each keypoint
} // End of thread scope
#ifdef CV_BEBLID_PARALLEL
);
#endif
}
Ptr<BEBLID> BEBLID::create(float scale_factor, int n_bits)
{
if (n_bits == BEBLID::SIZE_512_BITS)
{
#include "beblid.p512.hpp"
return makePtr<BEBLID_Impl<ABWLParams>>(scale_factor, beblid_wl_params_512);
}
else if(n_bits == BEBLID::SIZE_256_BITS)
{
#include "beblid.p256.hpp"
return makePtr<BEBLID_Impl<ABWLParams>>(scale_factor, beblid_wl_params_256);
}
else
{
CV_Error(Error::StsBadArg, "n_bits should be either BEBLID::SIZE_512_BITS or BEBLID::SIZE_256_BITS");
}
}
String BEBLID::getDefaultName() const
{
return (Feature2D::getDefaultName() + ".BEBLID");
}
String TEBLID::getDefaultName() const
{
return (Feature2D::getDefaultName() + ".TEBLID");
}
} // END NAMESPACE XFEATURES2D
} // END NAMESPACE CV
+83
View File
@@ -0,0 +1,83 @@
// 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.
// Author: Iago Suarez <iago.suarez.canosa@alumnos.upm.es>
// Implementation of the article:
// Iago Suarez, Ghesn Sfeir, Jose M. Buenaposada, and Luis Baumela.
// BEBLID: Boosted Efficient Binary Local Image Descriptor.
// Pattern Recognition Letters, 133:366372, 2020.
// ABWLParams: x1, y1, x2, y2, boxRadius, th
// Pre-trained parameters of BEBLID-256 trained in Liberty data set with
// a million of patch pairs, 20% positives and 80% negatives
static const ABWLParams beblid_wl_params_256_[] = {
{26, 20, 14, 16, 5, 16}, {17, 17, 15, 15, 2, 7}, {18, 16, 8, 13, 3, 18},
{19, 15, 13, 14, 3, 17}, {16, 16, 5, 15, 4, 10}, {25, 10, 16, 16, 6, 11},
{16, 15, 12, 15, 1, 12}, {18, 17, 14, 17, 1, 13}, {15, 14, 5, 21, 5, 6}, {14, 14, 11, 7, 4, 2},
{23, 27, 16, 17, 4, 8}, {12, 17, 10, 24, 5, 0}, {15, 15, 13, 14, 1, 6}, {16, 16, 14, 16, 1, 7},
{19, 18, 16, 15, 1, 6}, {24, 7, 19, 15, 6, 4}, {15, 16, 6, 8, 5, 6}, {24, 16, 8, 15, 7, 22},
{15, 6, 13, 16, 4, 6}, {17, 19, 15, 15, 1, 6}, {17, 12, 16, 16, 1, 2}, {11, 15, 7, 25, 6, 0},
{15, 15, 14, 10, 2, 2}, {26, 15, 18, 17, 4, 6}, {18, 12, 17, 27, 4, 3}, {9, 15, 6, 8, 6, 1},
{15, 17, 14, 23, 3, 1}, {11, 17, 4, 14, 4, 1}, {22, 18, 19, 5, 5, 5}, {11, 18, 11, 5, 5, 3},
{22, 5, 19, 19, 5, 2}, {12, 26, 6, 15, 3, 5}, {16, 16, 14, 18, 1, 7}, {22, 26, 22, 13, 5, 2},
{18, 13, 16, 16, 1, 4}, {14, 26, 13, 10, 5, 3}, {17, 13, 14, 14, 1, 10}, {21, 16, 19, 7, 3, 4},
{14, 15, 14, 13, 1, 0}, {26, 26, 20, 18, 5, 1}, {12, 10, 8, 21, 4, 3}, {14, 17, 13, 7, 3, 0},
{13, 12, 10, 19, 2, 4}, {17, 20, 17, 13, 2, 0}, {8, 25, 6, 11, 6, 2}, {27, 11, 20, 24, 4, 3},
{14, 18, 12, 14, 2, 5}, {22, 19, 18, 20, 2, 5}, {18, 4, 17, 14, 3, 1}, {13, 28, 13, 18, 3, 3},
{15, 12, 14, 17, 1, 4}, {13, 20, 10, 11, 2, 3}, {10, 5, 4, 17, 4, 2}, {7, 18, 3, 18, 3, 2},
{21, 11, 15, 2, 2, 11}, {20, 15, 17, 17, 1, 6}, {10, 20, 4, 27, 4, 3}, {24, 25, 23, 7, 6, 0},
{18, 15, 18, 12, 2, 0}, {17, 16, 16, 13, 1, 3}, {14, 20, 14, 15, 1, 1}, {17, 17, 17, 14, 1, 0},
{7, 15, 6, 5, 5, 3}, {11, 21, 11, 13, 2, 1}, {18, 16, 15, 9, 1, 7}, {19, 19, 18, 15, 1, 2},
{28, 19, 20, 16, 3, 1}, {14, 16, 11, 10, 1, 3}, {22, 13, 19, 14, 1, 2}, {9, 10, 4, 4, 4, 3},
{20, 26, 10, 29, 2, 12}, {14, 17, 12, 19, 1, 3}, {21, 18, 18, 24, 2, 6}, {16, 15, 15, 19, 1, 4},
{27, 4, 24, 15, 4, 2}, {15, 22, 14, 6, 2, 2}, {13, 16, 9, 12, 1, 2}, {12, 12, 11, 18, 1, 2},
{22, 17, 20, 11, 2, 2}, {18, 28, 17, 23, 3, 1}, {6, 9, 5, 21, 4, 0}, {12, 3, 8, 11, 3, 5},
{21, 16, 19, 16, 1, 2}, {18, 16, 17, 19, 1, 2}, {27, 12, 22, 3, 3, 2}, {13, 27, 4, 26, 4, 3},
{5, 22, 3, 26, 3, 2}, {24, 28, 23, 20, 3, 2}, {11, 17, 8, 19, 2, 0}, {13, 16, 11, 16, 1, 3},
{18, 15, 18, 8, 2, 1}, {15, 17, 14, 14, 1, 3}, {19, 14, 17, 12, 1, 4}, {25, 10, 22, 20, 2, 0},
{14, 12, 13, 9, 1, 1}, {9, 10, 3, 9, 3, 2}, {20, 22, 19, 17, 1, 0}, {16, 24, 16, 10, 2, 0},
{15, 23, 13, 29, 2, 2}, {15, 20, 14, 17, 1, 4}, {27, 27, 22, 27, 4, 1}, {14, 7, 6, 3, 3, 3},
{21, 3, 20, 7, 3, 0}, {29, 5, 25, 11, 2, 1}, {15, 21, 15, 20, 1, 0}, {8, 17, 8, 11, 2, 1},
{17, 13, 17, 8, 1, 0}, {7, 25, 3, 21, 3, 0}, {7, 11, 7, 8, 3, 1}, {4, 11, 3, 26, 3, 2},
{15, 18, 15, 11, 1, 1}, {23, 15, 20, 19, 2, 2}, {5, 9, 3, 4, 3, 2}, {28, 18, 25, 8, 3, 0},
{20, 22, 17, 30, 1, 5}, {29, 29, 28, 16, 2, 1}, {28, 11, 24, 15, 2, 1}, {20, 7, 18, 9, 1, 2},
{19, 12, 18, 16, 1, 2}, {11, 20, 11, 17, 2, 1}, {13, 16, 13, 13, 1, 0}, {29, 3, 23, 5, 2, 0},
{19, 21, 17, 18, 1, 3}, {12, 8, 12, 3, 2, 2}, {14, 13, 13, 20, 1, 2}, {11, 21, 9, 29, 2, 3},
{7, 30, 6, 22, 1, 2}, {11, 9, 10, 15, 1, 3}, {8, 3, 2, 9, 2, 0}, {19, 7, 18, 3, 3, 2},
{21, 9, 19, 11, 1, 1}, {18, 10, 17, 13, 1, 2}, {6, 17, 1, 30, 1, 6}, {17, 29, 16, 28, 2, 1},
{17, 20, 17, 18, 1, 0}, {15, 9, 13, 23, 1, 4}, {12, 14, 11, 16, 1, 1}, {7, 17, 5, 14, 2, 1},
{30, 30, 23, 12, 1, 2}, {29, 18, 26, 20, 2, 0}, {10, 20, 9, 17, 2, 1}, {4, 15, 2, 8, 2, 2},
{7, 7, 7, 3, 3, 1}, {9, 19, 8, 24, 1, 2}, {28, 25, 27, 25, 3, 0}, {13, 15, 12, 18, 1, 1},
{25, 2, 19, 5, 2, 2}, {15, 4, 15, 3, 3, 0}, {25, 19, 24, 29, 2, 2}, {18, 24, 18, 20, 1, 1},
{4, 10, 1, 2, 1, 3}, {5, 18, 1, 18, 1, 2}, {13, 22, 13, 19, 1, 1}, {10, 26, 8, 28, 2, 0},
{24, 13, 24, 6, 1, 1}, {15, 19, 14, 15, 1, 4}, {5, 8, 2, 16, 2, 0}, {12, 4, 11, 2, 2, 0},
{14, 29, 14, 24, 1, 1}, {3, 20, 1, 22, 1, 1}, {17, 5, 12, 1, 1, 5}, {21, 16, 20, 23, 1, 2},
{25, 17, 22, 13, 1, 0}, {6, 21, 5, 16, 1, 0}, {7, 15, 6, 19, 1, 1}, {20, 17, 19, 15, 1, 1},
{3, 29, 3, 23, 2, 1}, {16, 25, 16, 22, 1, 0}, {28, 20, 28, 12, 3, 0}, {27, 13, 23, 10, 1, 0},
{24, 24, 17, 29, 1, 5}, {13, 2, 11, 4, 1, 2}, {22, 23, 21, 21, 1, 0}, {19, 30, 19, 24, 1, 1},
{30, 30, 26, 27, 1, 0}, {17, 5, 17, 1, 1, 0}, {26, 7, 24, 1, 1, 1}, {28, 6, 28, 3, 3, 0},
{3, 15, 1, 13, 1, 1}, {7, 8, 5, 6, 1, 1}, {19, 16, 19, 15, 1, 0}, {12, 9, 11, 7, 1, 0},
{17, 22, 16, 20, 1, 2}, {12, 14, 12, 11, 1, 1}, {25, 29, 23, 26, 1, 0}, {15, 19, 15, 18, 1, 0},
{13, 22, 12, 25, 1, 0}, {1, 22, 1, 11, 1, 0}, {14, 12, 14, 9, 1, 1}, {10, 27, 9, 23, 1, 2},
{9, 4, 6, 1, 1, 1}, {22, 12, 21, 16, 1, 0}, {5, 27, 1, 28, 1, 1}, {30, 14, 28, 7, 1, 0},
{17, 9, 16, 21, 1, 2}, {17, 9, 17, 6, 1, 0}, {4, 4, 1, 1, 1, 1}, {30, 2, 28, 5, 1, 0},
{18, 4, 17, 7, 1, 1}, {15, 13, 15, 10, 1, 1}, {12, 30, 11, 26, 1, 2}, {16, 28, 15, 29, 1, 1},
{30, 11, 28, 11, 1, 0}, {9, 12, 8, 10, 1, 1}, {22, 19, 21, 16, 1, 0}, {30, 20, 29, 26, 1, 0},
{22, 10, 20, 7, 1, 2}, {2, 2, 1, 5, 1, 0}, {9, 9, 7, 9, 1, 0}, {27, 1, 25, 3, 1, 0},
{21, 23, 20, 25, 1, 1}, {10, 3, 8, 5, 1, 1}, {24, 1, 23, 3, 1, 0}, {5, 29, 4, 28, 1, 0},
{27, 23, 26, 18, 1, 1}, {22, 2, 22, 1, 1, 0}, {7, 20, 6, 19, 1, 0}, {12, 26, 9, 25, 1, 2},
{7, 1, 5, 2, 1, 0}, {2, 21, 1, 18, 1, 0}, {2, 24, 1, 21, 1, 0}, {8, 17, 8, 14, 1, 0},
{30, 1, 28, 2, 1, 0}, {15, 30, 15, 28, 1, 0}, {2, 5, 1, 9, 1, 0}, {18, 28, 17, 26, 1, 1},
{7, 29, 1, 30, 1, 1}, {17, 2, 17, 1, 1, 0}, {21, 13, 21, 9, 1, 1}, {29, 15, 27, 15, 1, 0},
{28, 8, 27, 7, 2, 0}, {29, 14, 28, 18, 1, 0}, {2, 26, 1, 30, 1, 1}, {16, 8, 16, 6, 1, 0},
{30, 26, 26, 24, 1, 0}, {15, 17, 15, 16, 6, 0}, {30, 29, 27, 30, 1, 0}, {3, 30, 1, 28, 1, 0},
{17, 1, 16, 2, 1, 1}, {14, 30, 12, 30, 1, 1}, {12, 17, 12, 16, 1, 0}, {4, 18, 4, 16, 1, 0},
{11, 4, 11, 1, 1, 1}, {21, 2, 18, 1, 1, 2}, {16, 17, 16, 15, 5, 0}, {3, 1, 2, 2, 1, 0},
{23, 17, 23, 16, 1, 0}, {18, 12, 18, 11, 1, 0}, {10, 28, 8, 30, 1, 0}, {12, 10, 12, 8, 1, 1},
{2, 14, 1, 9, 1, 1}, {6, 25, 6, 21, 1, 1}, {6, 2, 2, 1, 1, 1}, {30, 19, 29, 20, 1, 0},
{25, 21, 23, 20, 1, 0}, {16, 10, 16, 9, 1, 0}
};
static const std::vector<ABWLParams> beblid_wl_params_256(std::begin(beblid_wl_params_256_),
std::end(beblid_wl_params_256_));
+148
View File
@@ -0,0 +1,148 @@
// 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.
// Author: Iago Suarez <iago.suarez.canosa@alumnos.upm.es>
// Implementation of the article:
// Iago Suarez, Ghesn Sfeir, Jose M. Buenaposada, and Luis Baumela.
// BEBLID: Boosted Efficient Binary Local Image Descriptor.
// Pattern Recognition Letters, 133:366372, 2020.
// ABWLParams: x1, y1, x2, y2, boxRadius, th
// Pre-trained parameters of BEBLID-512 trained in Liberty data set with
// a million of patch pairs, 20% positives and 80% negatives
static const ABWLParams beblid_wl_params_512_[] = {
{24, 18, 15, 17, 6, 13}, {19, 14, 13, 17, 2, 18}, {23, 19, 12, 15, 6, 19},
{24, 14, 16, 16, 6, 11}, {16, 15, 12, 16, 1, 12}, {16, 15, 7, 10, 4, 10},
{17, 12, 8, 17, 3, 16}, {24, 12, 11, 17, 7, 19}, {19, 17, 14, 11, 3, 13},
{16, 15, 13, 15, 1, 10}, {16, 14, 6, 18, 5, 10}, {25, 5, 14, 15, 5, 15},
{17, 18, 14, 16, 2, 10}, {17, 14, 14, 13, 2, 9}, {15, 14, 6, 22, 5, 7}, {14, 16, 5, 17, 5, 5},
{16, 13, 15, 16, 1, 4}, {18, 17, 15, 15, 1, 9}, {26, 26, 15, 14, 5, 12}, {18, 18, 16, 16, 1, 4},
{15, 14, 14, 27, 4, 0}, {17, 13, 15, 16, 1, 6}, {15, 15, 13, 14, 1, 6}, {18, 17, 16, 16, 1, 4},
{14, 13, 6, 7, 5, 4}, {27, 12, 17, 15, 4, 8}, {12, 13, 7, 24, 7, 2}, {17, 18, 15, 15, 1, 6},
{16, 16, 12, 17, 1, 12}, {27, 20, 16, 16, 4, 11}, {12, 14, 7, 5, 5, 0}, {12, 16, 7, 26, 5, 0},
{15, 15, 15, 7, 4, -1}, {16, 17, 14, 17, 2, 6}, {16, 13, 10, 6, 4, 7}, {15, 26, 15, 19, 4, 1},
{26, 5, 17, 13, 5, 7}, {15, 23, 5, 12, 5, 8}, {17, 14, 10, 11, 3, 14}, {21, 27, 17, 16, 4, 5},
{15, 16, 14, 16, 1, 3}, {14, 11, 12, 26, 5, 1}, {12, 14, 12, 5, 4, -3}, {16, 16, 14, 12, 1, 7},
{13, 20, 7, 13, 3, 4}, {19, 6, 17, 16, 6, 3}, {11, 9, 10, 19, 4, 2}, {14, 15, 13, 9, 3, 1},
{16, 16, 14, 25, 3, 3}, {8, 26, 8, 13, 4, 3}, {16, 14, 15, 19, 2, 3}, {18, 15, 15, 16, 1, 9},
{26, 23, 19, 16, 5, 4}, {11, 21, 4, 13, 4, 1}, {20, 16, 20, 5, 4, 2}, {15, 16, 15, 13, 1, 0},
{16, 20, 16, 15, 2, 0}, {22, 13, 17, 14, 2, 8}, {18, 17, 14, 15, 1, 13}, {21, 12, 20, 26, 4, 3},
{10, 7, 8, 18, 5, 3}, {11, 26, 11, 20, 5, 2}, {13, 21, 13, 17, 3, 1}, {10, 23, 6, 7, 6, 1},
{10, 14, 5, 14, 5, 0}, {23, 25, 16, 6, 6, 8}, {18, 16, 18, 5, 4, 1}, {16, 16, 16, 14, 1, 0},
{11, 15, 4, 23, 4, -2}, {17, 14, 16, 16, 1, 2}, {26, 4, 20, 24, 4, 2}, {20, 19, 18, 14, 2, 3},
{14, 17, 10, 15, 2, 6}, {17, 13, 17, 9, 3, 0}, {26, 21, 5, 24, 5, 20}, {20, 15, 19, 25, 5, 3},
{27, 15, 19, 5, 4, 5}, {10, 14, 10, 6, 6, -2}, {12, 22, 11, 10, 3, 2}, {17, 16, 16, 20, 2, 3},
{15, 15, 12, 19, 1, 7}, {15, 11, 14, 17, 2, 4}, {14, 20, 10, 15, 2, 7}, {10, 14, 3, 7, 3, -5},
{12, 16, 9, 11, 3, 1}, {19, 17, 17, 11, 2, 5}, {26, 7, 19, 26, 5, 4}, {20, 10, 19, 18, 3, 1},
{17, 13, 16, 16, 1, 2}, {17, 11, 16, 4, 4, 2}, {15, 19, 14, 12, 2, 3}, {17, 18, 16, 13, 1, 3},
{11, 9, 4, 27, 4, -1}, {21, 23, 18, 17, 3, 3}, {7, 21, 6, 7, 5, -1}, {25, 27, 21, 18, 4, -1},
{14, 17, 14, 14, 2, 0}, {12, 11, 8, 19, 3, 3}, {14, 15, 13, 22, 2, 0}, {8, 23, 5, 17, 5, 1},
{15, 16, 14, 8, 2, 1}, {16, 24, 15, 18, 3, 3}, {19, 25, 19, 18, 5, -1}, {11, 23, 10, 13, 2, 3},
{19, 14, 18, 22, 2, 3}, {26, 15, 22, 6, 4, 2}, {24, 17, 19, 8, 3, 5}, {21, 15, 16, 15, 1, 10},
{15, 14, 14, 20, 1, 2}, {16, 27, 13, 5, 4, 5}, {10, 4, 5, 13, 4, 3}, {12, 14, 10, 10, 2, 0},
{14, 18, 14, 11, 1, -1}, {23, 6, 22, 20, 5, 0}, {14, 12, 10, 19, 2, 6}, {17, 18, 17, 15, 2, 0},
{16, 15, 15, 18, 1, 4}, {11, 13, 3, 4, 3, -4}, {15, 14, 15, 8, 2, -1}, {11, 23, 5, 26, 5, 0},
{20, 20, 19, 17, 2, 1}, {22, 19, 19, 20, 2, 3}, {16, 5, 15, 24, 4, 2}, {18, 15, 16, 12, 1, 5},
{28, 27, 23, 15, 3, -2}, {7, 25, 6, 18, 6, 2}, {12, 19, 12, 13, 3, 0}, {9, 7, 4, 17, 4, 1},
{14, 18, 13, 12, 1, 2}, {13, 16, 10, 23, 2, 1}, {24, 25, 23, 13, 6, -1}, {8, 13, 7, 4, 4, -3},
{17, 15, 17, 11, 2, 0}, {20, 13, 18, 15, 1, 3}, {28, 3, 23, 15, 3, -2}, {13, 17, 12, 11, 1, 0},
{16, 18, 16, 11, 1, 0}, {26, 16, 24, 26, 5, 2}, {14, 14, 11, 15, 1, 6}, {15, 9, 15, 3, 3, -1},
{12, 28, 10, 19, 3, 6}, {18, 17, 18, 14, 2, 0}, {16, 14, 14, 15, 1, 7}, {20, 18, 19, 10, 2, 2},
{27, 28, 18, 24, 3, 4}, {15, 11, 14, 25, 2, 1}, {16, 18, 15, 16, 1, 3}, {5, 27, 4, 6, 4, 0},
{17, 20, 17, 14, 1, 0}, {13, 15, 9, 14, 1, 3}, {9, 23, 3, 23, 3, -1}, {9, 10, 3, 9, 3, -2},
{16, 27, 16, 9, 3, 0}, {13, 17, 11, 15, 1, 3}, {14, 18, 14, 15, 1, 0}, {28, 12, 20, 21, 3, 2},
{23, 7, 4, 27, 4, 16}, {16, 18, 16, 16, 1, -1}, {13, 16, 12, 19, 1, 1}, {20, 11, 19, 18, 2, 1},
{23, 14, 19, 13, 1, 2}, {23, 10, 19, 3, 3, 5}, {15, 18, 13, 15, 1, 6}, {8, 14, 3, 19, 3, -3},
{7, 18, 3, 17, 3, -2}, {22, 4, 21, 7, 4, 0}, {3, 28, 3, 18, 3, 2}, {19, 20, 17, 14, 1, 4},
{16, 22, 15, 6, 2, 2}, {22, 20, 19, 29, 2, 5}, {11, 21, 9, 14, 2, 2}, {7, 9, 6, 4, 4, -2},
{26, 19, 23, 9, 4, 1}, {16, 17, 16, 12, 2, 0}, {15, 5, 3, 4, 3, 4}, {18, 14, 17, 17, 1, 2},
{19, 11, 17, 13, 1, 4}, {11, 17, 10, 10, 2, -1}, {15, 23, 12, 29, 2, 3},
{28, 20, 24, 17, 3, -1}, {13, 10, 11, 2, 2, -1}, {28, 11, 23, 15, 3, -1},
{16, 21, 16, 20, 2, 0}, {8, 8, 7, 17, 2, 2}, {15, 19, 14, 16, 1, 4}, {17, 11, 17, 10, 2, 0},
{22, 21, 19, 16, 1, 1}, {13, 17, 13, 14, 1, 0}, {19, 13, 18, 16, 1, 2}, {6, 25, 5, 27, 4, -1},
{16, 29, 16, 22, 2, 0}, {23, 27, 23, 22, 4, -1}, {29, 2, 22, 10, 2, -1}, {22, 10, 22, 5, 5, 1},
{20, 16, 19, 15, 1, 1}, {20, 9, 19, 14, 1, 0}, {29, 29, 23, 22, 2, -1}, {12, 11, 10, 18, 1, 3},
{4, 16, 4, 2, 2, -2}, {14, 8, 13, 2, 2, 0}, {16, 3, 15, 6, 3, 2}, {23, 8, 15, 2, 2, 10},
{18, 19, 18, 16, 1, 0}, {12, 21, 6, 18, 1, 2}, {18, 15, 16, 19, 1, 5}, {16, 21, 16, 8, 2, 0},
{18, 26, 17, 23, 2, 1}, {7, 8, 3, 3, 3, -3}, {6, 24, 3, 28, 3, -2}, {10, 19, 9, 26, 2, -3},
{17, 9, 16, 13, 1, 2}, {13, 15, 13, 10, 1, -2}, {18, 16, 18, 12, 1, 0}, {17, 13, 17, 11, 1, 0},
{6, 16, 3, 12, 3, -2}, {15, 21, 15, 20, 1, 0}, {23, 17, 20, 15, 2, 1}, {28, 22, 25, 8, 3, 0},
{5, 16, 3, 25, 3, -3}, {14, 13, 13, 20, 1, 2}, {28, 28, 20, 27, 3, 2}, {15, 29, 8, 25, 2, 7},
{10, 28, 5, 24, 3, 2}, {19, 14, 18, 13, 1, 2}, {19, 26, 14, 28, 3, 7}, {18, 21, 17, 18, 1, 2},
{13, 17, 9, 20, 1, 2}, {15, 13, 13, 11, 1, 4}, {27, 7, 25, 15, 4, -1}, {12, 15, 11, 17, 1, 1},
{13, 20, 12, 15, 1, 3}, {15, 20, 14, 22, 1, 2}, {19, 29, 17, 27, 2, 2}, {19, 3, 18, 5, 3, 1},
{9, 21, 9, 17, 2, 1}, {19, 18, 17, 18, 1, 4}, {25, 13, 24, 18, 3, 0}, {11, 15, 10, 13, 1, 0},
{9, 9, 8, 3, 2, -2}, {6, 8, 3, 8, 3, -1}, {28, 19, 23, 28, 3, 2}, {10, 30, 9, 23, 1, 3},
{5, 5, 3, 18, 3, 1}, {14, 17, 12, 20, 1, 3}, {29, 16, 23, 15, 2, -1}, {23, 15, 21, 22, 2, 2},
{28, 3, 25, 5, 3, 0}, {12, 20, 11, 17, 1, 2}, {20, 22, 18, 20, 1, 2}, {5, 9, 2, 2, 2, -3},
{7, 27, 3, 19, 3, 1}, {13, 2, 7, 6, 2, 4}, {18, 29, 17, 25, 2, 1}, {15, 21, 14, 17, 1, 4},
{13, 29, 12, 26, 2, 2}, {5, 22, 4, 12, 2, 0}, {16, 21, 16, 11, 1, 0}, {16, 23, 16, 10, 1, 0},
{11, 5, 10, 11, 2, 3}, {15, 10, 14, 21, 1, 3}, {10, 18, 9, 18, 1, 0}, {17, 9, 16, 5, 2, 2},
{19, 19, 19, 12, 1, 0}, {25, 12, 22, 4, 2, 2}, {6, 18, 1, 20, 1, -3}, {10, 13, 10, 10, 2, -1},
{25, 16, 22, 16, 1, 0}, {18, 13, 18, 12, 1, 0}, {14, 13, 12, 11, 1, 3}, {10, 27, 1, 29, 1, -1},
{13, 8, 11, 6, 1, 1}, {24, 24, 21, 28, 3, 2}, {22, 17, 20, 17, 1, 1}, {12, 13, 11, 18, 1, 1},
{23, 3, 21, 7, 3, 0}, {18, 12, 17, 13, 1, 2}, {7, 28, 7, 25, 3, 1}, {28, 28, 28, 15, 3, -1},
{17, 7, 17, 2, 2, 0}, {19, 9, 17, 11, 1, 3}, {14, 23, 14, 9, 1, 0}, {7, 22, 7, 19, 2, 1},
{29, 24, 29, 2, 2, 0}, {28, 15, 25, 11, 3, 0}, {5, 11, 1, 10, 1, -2}, {2, 22, 2, 2, 2, -1},
{22, 30, 16, 27, 1, 5}, {20, 15, 19, 13, 1, 1}, {23, 19, 22, 14, 2, 0}, {5, 7, 5, 3, 3, -1},
{19, 20, 18, 18, 1, 1}, {29, 9, 25, 13, 2, -1}, {29, 23, 26, 23, 2, 0}, {9, 13, 8, 8, 1, -2},
{21, 22, 21, 18, 2, -1}, {29, 12, 28, 20, 2, 0}, {18, 5, 1, 4, 1, 9}, {17, 4, 17, 2, 2, 0},
{28, 29, 24, 25, 2, 0}, {14, 23, 13, 29, 1, 0}, {13, 5, 13, 1, 1, -1}, {20, 25, 20, 21, 1, -1},
{6, 5, 2, 11, 2, 0}, {10, 14, 9, 21, 1, -1}, {13, 16, 13, 14, 1, 0}, {19, 17, 18, 14, 1, 2},
{14, 21, 14, 17, 1, 1}, {20, 10, 18, 12, 1, 2}, {20, 4, 19, 3, 3, 1}, {3, 15, 1, 30, 1, -3},
{13, 4, 8, 1, 1, 2}, {10, 18, 9, 14, 1, 0}, {6, 15, 1, 12, 1, -3}, {10, 25, 10, 20, 1, 2},
{14, 11, 14, 7, 1, -1}, {22, 9, 20, 4, 1, 2}, {15, 27, 8, 30, 1, 4}, {10, 5, 10, 2, 2, -1},
{17, 16, 16, 12, 1, 3}, {15, 18, 15, 10, 1, -1}, {20, 30, 20, 23, 1, -1}, {14, 9, 13, 22, 1, 2},
{14, 22, 12, 25, 1, 2}, {5, 23, 2, 23, 2, -1}, {10, 16, 9, 16, 1, 0}, {26, 2, 19, 4, 1, 2},
{3, 23, 2, 13, 2, 0}, {3, 17, 3, 7, 2, -1}, {15, 26, 15, 23, 1, 0}, {22, 14, 22, 8, 1, 1},
{28, 9, 27, 6, 3, 0}, {26, 22, 25, 28, 3, 1}, {17, 10, 17, 5, 1, 1}, {11, 21, 10, 17, 1, 2},
{20, 18, 20, 16, 1, 0}, {7, 20, 5, 20, 1, -1}, {17, 24, 17, 8, 1, 0}, {24, 9, 20, 9, 1, 1},
{4, 13, 1, 16, 1, -1}, {30, 1, 28, 16, 1, -1}, {17, 21, 17, 17, 1, 0}, {19, 4, 11, 2, 1, 9},
{30, 5, 24, 6, 1, 0}, {22, 19, 22, 12, 1, 0}, {9, 16, 9, 12, 1, -1}, {12, 16, 12, 12, 1, -1},
{12, 24, 11, 29, 1, -1}, {3, 6, 1, 4, 1, -1}, {23, 29, 20, 27, 2, 1}, {23, 17, 22, 16, 1, 0},
{30, 20, 26, 22, 1, 0}, {9, 2, 6, 5, 2, 1}, {20, 17, 19, 16, 1, 1}, {18, 26, 17, 30, 1, 1},
{29, 14, 28, 14, 2, 0}, {20, 13, 19, 14, 1, 1}, {15, 23, 15, 21, 1, 0}, {8, 26, 2, 30, 1, -2},
{4, 5, 3, 2, 2, -1}, {7, 16, 6, 12, 1, -1}, {29, 9, 23, 2, 2, 1}, {13, 2, 12, 5, 2, 2},
{20, 18, 19, 21, 1, 2}, {7, 29, 2, 25, 2, 0}, {20, 3, 18, 8, 1, 1}, {14, 14, 14, 11, 1, -1},
{12, 12, 12, 10, 1, -1}, {17, 27, 15, 30, 1, 2}, {22, 27, 20, 29, 2, 1}, {7, 12, 5, 9, 1, -2},
{30, 30, 24, 24, 1, 0}, {19, 3, 19, 2, 2, 0}, {13, 19, 12, 18, 1, 2}, {3, 30, 2, 24, 1, 1},
{9, 14, 7, 19, 1, -1}, {17, 22, 17, 18, 1, 0}, {18, 24, 17, 22, 1, 1}, {2, 18, 1, 23, 1, -1},
{30, 23, 24, 19, 1, -1}, {11, 10, 11, 5, 1, -2}, {9, 30, 9, 27, 1, 1}, {21, 13, 20, 8, 1, 2},
{6, 3, 2, 2, 2, -1}, {23, 22, 22, 26, 1, 1}, {12, 26, 11, 25, 1, 1}, {22, 1, 19, 5, 1, 1},
{4, 24, 1, 25, 1, -1}, {5, 13, 5, 7, 1, -1}, {26, 22, 24, 16, 1, -1}, {27, 8, 27, 3, 2, 0},
{13, 18, 13, 16, 1, 0}, {19, 15, 18, 17, 1, 2}, {30, 29, 26, 28, 1, 0}, {20, 15, 20, 14, 1, 0},
{3, 18, 1, 15, 1, -1}, {18, 11, 17, 10, 1, 2}, {4, 18, 4, 16, 1, 0}, {8, 27, 5, 30, 1, -1},
{30, 15, 28, 22, 1, 0}, {9, 19, 8, 22, 1, -1}, {30, 4, 29, 4, 1, 0}, {17, 10, 17, 8, 1, 0},
{22, 6, 22, 1, 1, 1}, {2, 11, 1, 15, 1, 0}, {3, 16, 1, 17, 1, -1}, {9, 3, 8, 2, 2, 0},
{3, 11, 1, 10, 1, -1}, {16, 29, 15, 28, 1, 1}, {15, 20, 15, 19, 1, 0}, {20, 17, 19, 17, 1, 1},
{10, 3, 9, 8, 1, 2}, {10, 22, 7, 26, 1, -1}, {8, 16, 6, 16, 1, -1}, {16, 28, 16, 25, 1, 0},
{12, 25, 10, 21, 1, 3}, {8, 9, 7, 7, 1, -1}, {3, 1, 1, 6, 1, 0}, {16, 7, 15, 9, 1, 2},
{30, 23, 29, 23, 1, 0}, {22, 24, 21, 29, 1, 1}, {15, 1, 14, 3, 1, 1}, {18, 6, 17, 9, 1, 1},
{26, 25, 25, 19, 1, -1}, {25, 13, 22, 18, 1, 0}, {11, 1, 10, 3, 1, 1}, {29, 28, 28, 30, 1, 0},
{16, 17, 16, 13, 5, 0}, {28, 18, 28, 12, 2, 0}, {3, 22, 1, 23, 1, -1}, {10, 11, 10, 9, 1, -1},
{7, 13, 6, 20, 1, -1}, {1, 15, 1, 6, 1, -1}, {16, 12, 16, 11, 1, 0}, {3, 26, 2, 30, 1, -1},
{28, 30, 26, 23, 1, -1}, {17, 22, 16, 25, 1, 2}, {30, 13, 26, 7, 1, 0}, {10, 8, 7, 10, 1, 1},
{2, 27, 1, 22, 1, 0}, {30, 7, 27, 8, 1, 0}, {22, 19, 21, 22, 1, 1}, {5, 19, 4, 21, 1, -1},
{24, 6, 23, 11, 1, -1}, {24, 17, 23, 14, 1, 0}, {30, 7, 28, 1, 1, 0}, {11, 16, 11, 15, 1, 0},
{29, 2, 26, 4, 1, 0}, {20, 4, 18, 1, 1, 2}, {18, 2, 17, 3, 1, 1}, {20, 30, 18, 29, 1, 1},
{29, 15, 29, 9, 2, 0}, {14, 8, 14, 5, 1, -1}, {17, 15, 16, 18, 1, 3}, {12, 4, 11, 2, 2, 0},
{23, 8, 21, 11, 1, 0}, {8, 30, 7, 24, 1, 2}, {2, 20, 1, 16, 1, 0}, {15, 26, 14, 29, 1, 1},
{4, 30, 3, 29, 1, 0}, {19, 17, 19, 16, 1, 0}, {13, 17, 13, 15, 1, 0}, {2, 9, 1, 1, 1, -1},
{30, 28, 27, 27, 1, 0}, {27, 4, 26, 1, 1, 0}, {19, 23, 19, 20, 1, -1}, {15, 24, 15, 23, 1, 0},
{2, 29, 1, 28, 1, 0}, {2, 5, 1, 6, 1, 0}, {24, 29, 23, 26, 1, 0}, {13, 12, 12, 11, 1, 1},
{12, 17, 12, 15, 1, 0}, {24, 26, 24, 22, 1, -1}, {11, 3, 10, 5, 1, 1}, {30, 2, 30, 1, 1, 0},
{18, 30, 18, 29, 1, 0}, {30, 25, 29, 29, 1, 0}, {12, 30, 10, 28, 1, 1}, {24, 12, 22, 14, 1, 0},
{6, 13, 4, 15, 1, -1}, {2, 26, 2, 23, 1, 0}, {8, 9, 7, 13, 1, 1}, {30, 1, 27, 1, 1, 0},
{26, 29, 24, 30, 1, 0}, {18, 11, 18, 10, 1, 0}, {30, 19, 29, 17, 1, 0}, {20, 27, 19, 24, 1, 0},
{28, 20, 26, 24, 1, 0}, {25, 9, 24, 9, 1, 0}, {27, 4, 24, 6, 1, 0}, {23, 21, 22, 19, 1, 0},
{7, 13, 7, 10, 1, -1}, {12, 11, 11, 11, 1, 1}, {28, 26, 26, 26, 1, 0}, {8, 4, 6, 4, 1, 0},
{15, 30, 15, 28, 1, 0}, {30, 14, 28, 14, 1, 0}, {17, 7, 17, 5, 1, 0}, {29, 10, 28, 6, 1, 0},
{12, 17, 11, 17, 1, 1}, {16, 3, 16, 1, 1, 0}, {21, 3, 19, 3, 1, 1}, {12, 30, 11, 28, 1, 1},
{18, 16, 18, 15, 1, 0}, {8, 18, 7, 20, 1, -1}, {5, 4, 1, 1, 1, -1}, {3, 27, 1, 30, 1, -1},
{26, 4, 26, 1, 1, 0}, {5, 21, 2, 20, 1, -1}, {14, 1, 13, 3, 1, 1}, {30, 9, 28, 8, 1, 0},
{13, 15, 12, 12, 1, 1}, {7, 23, 6, 25, 1, -1}
};
static const std::vector<ABWLParams> beblid_wl_params_512(std::begin(beblid_wl_params_512_),
std::end(beblid_wl_params_512_));
+778
View File
@@ -0,0 +1,778 @@
/*********************************************************************
* Software License Agreement (BSD License)
*
* Copyright (c) 2013, 2016
*
* Tomasz Trzcinski <t dot trzcinski at ii dot pw dot edu dot pl>
* Mario Christoudias <mariochristoudias at gmail dot com>
* Vincent Lepetit <lepetit at icg dot tugraz dot at>
*
*
* 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 copyright holders 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.
*********************************************************************/
/*
"Learning Image Descriptors with Boosting"
T. Trzcinski, M. Christoudias and V. Lepetit
IEEE Transactions on Pattern Analysis and Machine Intelligence, 2013
"Boosting Binary Keypoint Descriptors"
T. Trzcinski, M. Christoudias, V. Lepetit and P. Fua
Computer Vision and Pattern Recognition (CVPR), 2013
Original code: Tomasz Trzcinski <t dot trzcinski at ii dot pw dot edu dot pl>
OpenCV port by: Cristian Balint <cristian dot balint at gmail dot com>
*/
#include <bitset>
#include "precomp.hpp"
using namespace cv;
using namespace std;
namespace cv
{
namespace xfeatures2d
{
#ifdef OPENCV_XFEATURES2D_HAS_BOOST_DATA
/*
!BoostDesc implementation
*/
class BoostDesc_Impl CV_FINAL : public BoostDesc
{
public:
// constructor
explicit BoostDesc_Impl( int desc = BINBOOST_256,
bool use_scale_orientation = true,
float scale_factor = 6.25f );
// destructor
~BoostDesc_Impl() CV_OVERRIDE;
void read( const FileNode& fn ) CV_OVERRIDE;
void write( FileStorage& fs ) const CV_OVERRIDE;
// returns the descriptor length in bytes
int descriptorSize() const CV_OVERRIDE { return m_descriptor_size; }
// returns the descriptor type
int descriptorType() const CV_OVERRIDE { return m_descriptor_type; }
// returns the default norm type
int defaultNorm() const CV_OVERRIDE { return m_descriptor_norm; }
// compute descriptors given keypoints
void compute( InputArray image, vector<KeyPoint>& keypoints, OutputArray descriptors ) CV_OVERRIDE;
// getter / setter
void setUseScaleOrientation(const bool use_scale_orientation) CV_OVERRIDE { m_use_scale_orientation = use_scale_orientation; }
bool getUseScaleOrientation() const CV_OVERRIDE { return m_use_scale_orientation; }
void setScaleFactor(const float scale_factor) CV_OVERRIDE { m_scale_factor = scale_factor; }
float getScaleFactor() const CV_OVERRIDE { return m_scale_factor; }
protected:
/*
* BoostDesc parameters
*/
// size, type, norm
int m_descriptor_size;
int m_descriptor_type;
int m_descriptor_norm;
// desc type
int m_desc_type;
// gradient
// assignment types
enum Assign
{
ASSIGN_HARD = 0,
ASSIGN_BILINEAR = 1,
ASSIGN_SOFT = 2,
ASSIGN_HARD_MAGN = 3,
ASSIGN_SOFT_MAGN = 4
};
// dims
int m_Dims;
// no. weak
// learners
int m_nWLs;
// gradient type
int m_grad_atype;
// patch size
int m_patch_size;
// orient quantity
int m_orient_q;
// patch scale factor
float m_scale_factor;
/*
* BoostDesc switches
*/
// switch to enable sample by keypoints orientation
bool m_use_scale_orientation;
/*
* BoostDesc arrays
*/
// image
Mat m_image;
// parameters
// weak learner
Mat m_wl_thresh;
Mat m_wl_orient;
Mat m_wl_x_min, m_wl_x_max;
Mat m_wl_y_min, m_wl_y_max;
Mat m_wl_alpha, m_wl_beta;
private:
/*
* BoostDesc functions
*/
// initialize parameters
inline void ini_params( const int orientQuant, const int patchSize,
const int iGradAssignType,
const int nDim, const int nWLs,
const unsigned int thresh[], const int orient[],
const int x_min[], const int x_max[],
const int y_min[], const int y_max[],
const unsigned int alpha[], const unsigned int beta[] );
}; // END BoostDesc_Impl CLASS
// -------------------------------------------------
/* BoostDesc internal routines */
static void computeGradientMaps( const Mat& im,
const int gradAssignType,
const int orientQuant,
vector<Mat>& gradMap )
{
enum Assign
{
ASSIGN_HARD = 0,
ASSIGN_BILINEAR = 1,
ASSIGN_SOFT = 2,
ASSIGN_HARD_MAGN = 3,
ASSIGN_SOFT_MAGN = 4
};
Mat derivx( im.size(), CV_32FC1 );
Mat derivy( im.size(), CV_32FC1 );
Sobel( im, derivx, derivx.depth(), 1, 0 );
Sobel( im, derivy, derivy.depth(), 0, 1 );
for ( int i = 0; i < orientQuant; i++ )
gradMap.push_back( Mat::zeros( im.size(), CV_8UC1 ) );
int index, index2;
double binCenter, weight;
double binSize = (2 * CV_PI) / orientQuant;
// fill in temp matrices with
// respones to edge detection
const float* pDerivx = derivx.ptr<float>();
const float* pDerivy = derivy.ptr<float>();
for ( int i = 0; i < im.rows; i++ )
{
for ( int j = 0; j < im.cols; j++ )
{
float gradMagnitude = sqrt( (*pDerivx) * (*pDerivx)
+ (*pDerivy) * (*pDerivy) );
if ( gradMagnitude > 20 )
{
double theta = atan2( *pDerivy, *pDerivx );
theta = ( theta < 0 ) ? theta + 2*CV_PI : theta;
index = int( theta / binSize );
index = ( index == orientQuant ) ? 0 : index;
switch ( gradAssignType )
{
case ASSIGN_HARD:
gradMap[index].at<uchar>(i,j) = 1;
break;
case ASSIGN_HARD_MAGN:
gradMap[index].at<uchar>(i,j) = (uchar) cvRound( gradMagnitude );
break;
case ASSIGN_BILINEAR:
index2 = (int) ceil( theta / binSize );
index2 = ( index2 == orientQuant ) ? 0 : index2;
binCenter = ( index + 0.5f ) * binSize;
weight = 1 - abs( theta - binCenter ) / binSize;
gradMap[index ].at<uchar>(i,j) = (uchar) cvRound( 255 * weight );
gradMap[index2].at<uchar>(i,j) = (uchar) cvRound( 255 * ( 1 - weight ) );
break;
case ASSIGN_SOFT:
for ( int binNum = 0; binNum < orientQuant/2 + 1; binNum++ )
{
index2 = ( binNum + index + orientQuant - orientQuant/4 ) % orientQuant;
binCenter = ( index2 + 0.5f ) * binSize;
weight = cos( theta - binCenter );
weight = ( weight < 0 ) ? 0 : weight;
gradMap[index2].at<uchar>(i,j) = (uchar) cvRound( 255 * weight );
}
break;
case ASSIGN_SOFT_MAGN:
for ( int binNum = 0; binNum < orientQuant/2 + 1; binNum++ )
{
index2 = ( binNum + index + orientQuant - orientQuant/4 ) % orientQuant;
binCenter = ( index2 + 0.5f ) * binSize;
weight = cos( theta - binCenter );
weight = ( weight < 0 ) ? 0 : weight;
gradMap[index2].at<uchar>(i,j) = (uchar) cvRound( gradMagnitude * weight );
}
break;
} // end switch
}
++pDerivy;
++pDerivx;
}
}
}
static void computeIntegrals( const vector<Mat>& gradMap,
const int orientQuant,
vector<Mat>& integralMap )
{
// init integral images
int rows = gradMap[0].rows;
int cols = gradMap[0].cols;
for ( int i = 0; i < orientQuant+1; i++ )
integralMap.push_back( Mat::zeros( rows+1, cols+1, CV_8UC1 ) );
// generate corresponding integral images
for( int i = 0; i < orientQuant; i++ )
integral( gradMap[i], integralMap[i] );
// copy the values from the first quantization bin
integralMap[0].copyTo( integralMap[orientQuant] );
int* ptrSum, *ptr;
for ( int k = 1; k < orientQuant; k++ )
{
ptr = (int*) integralMap[k].ptr<int>();
ptrSum = (int*) integralMap[orientQuant].ptr<int>();
for (int i=0; i<(rows+1)*(cols+1); ++i)
{
*ptrSum += *ptr;
++ptrSum;
++ptr;
}
}
}
static float computeWLResponse( const int x_min, const int x_max,
const int y_min, const int y_max,
const int orient, const float thresh,
const int orientQuant,
const vector<Mat>& integralMap )
{
const int width = integralMap[0].cols;
const int idx1 = (y_min ) * width + x_min;
const int idx2 = (y_min ) * width + x_max + 1;
const int idx3 = (y_max + 1) * width + x_min;
const int idx4 = (y_max + 1) * width + x_max + 1;
const int* ptr = integralMap[orient].ptr<int>();
int A, B ,C, D;
A = ptr[idx1]; B = ptr[idx2];
C = ptr[idx3]; D = ptr[idx4];
const float current = float(D + A - B - C);
ptr = integralMap[orientQuant].ptr<int>();
A = ptr[idx1]; B = ptr[idx2];
C = ptr[idx3]; D = ptr[idx4];
const float total = float(D + A - B - C);
return total ? ( (current / total) - thresh ) : 0.f;
}
static void rectifyPatch( const Mat& image, const KeyPoint& kp,
const int& patchSize, Mat& patch,
const bool use_scale_orientation,
const float scale_factor )
{
Mat M;
if ( use_scale_orientation )
{
const float s = scale_factor * (float) kp.size / (float) patchSize;
const float cosine = (kp.angle>=0) ? cos(kp.angle*(float)CV_PI/180.0f) : 1.f;
const float sine = (kp.angle>=0) ? sin(kp.angle*(float)CV_PI/180.0f) : 0.f;
float M_[] = {
s*cosine, -s*sine, (-s*cosine + s*sine ) * patchSize/2.0f + kp.pt.x,
s*sine, s*cosine, (-s*sine - s*cosine) * patchSize/2.0f + kp.pt.y
};
M = Mat( 2, 3, CV_32FC1, M_ ).clone();
}
else
{
const float s = scale_factor * (float)kp.size / (float)patchSize;
float M_[] = {
s, 0.f, -s * patchSize/2.0f + kp.pt.x,
0.f, s, -s * patchSize/2.0f + kp.pt.y
};
M = Mat( 2, 3, CV_32FC1, M_ ).clone();
}
warpAffine( image, patch, M, Size( patchSize, patchSize ),
WARP_INVERSE_MAP + INTER_CUBIC + WARP_FILL_OUTLIERS );
}
// -------------------------------------------------
/* BoostDesc interface implementation */
struct ComputeBoostDescInvoker : ParallelLoopBody
{
ComputeBoostDescInvoker( const Mat& _image, Mat* _descriptors,
const vector<KeyPoint>& _keypoints,
const int _desc_type, const int _grad_atype,
const int _orient_q, const int _patch_size,
const int _nWLs, const int _Dims,
const Mat& _wl_x_min, const Mat& _wl_x_max,
const Mat& _wl_y_min, const Mat& _wl_y_max,
const Mat& _wl_thresh, const Mat& _wl_orient,
const Mat& _wl_alpha, const Mat& _wl_beta,
const bool _use_scale_orientation,
const float _scale_factor )
{
nWLs = _nWLs;
Dims = _Dims;
image = _image;
orient_q = _orient_q;
desc_type = _desc_type;
keypoints = _keypoints;
grad_atype = _grad_atype;
patch_size = _patch_size;
descriptors = _descriptors;
wl_beta = _wl_beta;
wl_alpha = _wl_alpha;
wl_x_min = _wl_x_min;
wl_x_max = _wl_x_max;
wl_y_min = _wl_y_min;
wl_y_max = _wl_y_max;
wl_thresh = _wl_thresh;
wl_orient = _wl_orient;
scale_factor = _scale_factor;
use_scale_orientation = _use_scale_orientation;
}
void operator ()( const cv::Range& range ) const CV_OVERRIDE
{
// maps
vector<Mat> gradMap, integralMap;
// small binary map
uchar binLookUp[8];
for ( unsigned int i = 0; i < 8; i++ )
binLookUp[i] = (uchar) 1 << i;
for ( int i = range.start; i < range.end; i++ )
{
Mat patch;
// rectify the patch around a given keypoint
rectifyPatch( image, keypoints[i], patch_size,
patch, use_scale_orientation, scale_factor );
// compute gradient maps (and integral gradient maps)
computeGradientMaps( patch, grad_atype, orient_q, gradMap );
computeIntegrals( gradMap, orient_q, integralMap );
float WLR;
/*
* BGM
*/
if ( ( desc_type == BGM ) ||
( desc_type == BGM_HARD ) ||
( desc_type == BGM_BILINEAR )
)
{
uchar* desc = descriptors->ptr<uchar>(i);
for ( int j = 0; j < nWLs; j++ )
{
WLR = computeWLResponse( wl_x_min.at<int>(0,j), wl_x_max.at<int>(0,j),
wl_y_min.at<int>(0,j), wl_y_max.at<int>(0,j),
wl_orient.at<int>(0,j), wl_thresh.at<float>(0,j),
orient_q, integralMap );
desc[j/8] |= ( WLR >= 0 ) ? binLookUp[ j % 8 ] : 0;
}
} // end BGM
/*
* LBGM
*/
if ( desc_type == LBGM )
{
std::bitset<512> wlResponses;
for ( int j = 0; j < nWLs; j++ )
{
WLR = computeWLResponse( wl_x_min.at<int>(0,j), wl_x_max.at<int>(0,j),
wl_y_min.at<int>(0,j), wl_y_max.at<int>(0,j),
wl_orient.at<int>(0,j), wl_thresh.at<float>(0,j),
orient_q, integralMap );
wlResponses[j] = ( WLR >= 0 ) ? 1 : 0;
}
float* desc = descriptors->ptr<float>(i);
for ( int d = 0; d < Dims; d++ )
{
for ( int wl = 0; wl < nWLs; wl++ )
{
desc[d] += ( wlResponses[wl] ) ? wl_beta.at<float>(wl,d) : -wl_beta.at<float>(wl,d);
}
}
} // end LBGM
/*
* BINBOOST
*/
if ( ( desc_type == BINBOOST_64 ) ||
( desc_type == BINBOOST_128 ) ||
( desc_type == BINBOOST_256 )
)
{
float resp;
for ( int d = 0; d < Dims; d++ )
{
resp = 0;
uchar* desc = descriptors->ptr<uchar>(i);
for ( int wl = 0; wl < nWLs; wl++ )
{
WLR = computeWLResponse( wl_x_min.at<int>(d,wl), wl_x_max.at<int>(d,wl),
wl_y_min.at<int>(d,wl), wl_y_max.at<int>(d,wl),
wl_orient.at<int>(d,wl), wl_thresh.at<float>(d,wl),
orient_q, integralMap );
resp += ( WLR >= 0 ) ? wl_beta.at<float>(d,wl) : -wl_beta.at<float>(d,wl);
}
desc[d/8] |= ( resp >= 0 ) ? binLookUp[d%8] : 0;
}
} // end BINBOOST
// clean-up
patch.release();
gradMap.clear();
integralMap.clear();
} // end for loop
} // end operator
int nWLs;
int Dims;
int orient_q;
int desc_type;
int patch_size;
int grad_atype;
int patch_szie;
Mat image;
Mat *descriptors;
vector<KeyPoint> keypoints;
Mat wl_x_min, wl_x_max, wl_y_min, wl_y_max;
Mat wl_thresh, wl_orient, wl_alpha, wl_beta;
float scale_factor;
bool use_scale_orientation;
enum
{
BGM = 100, BGM_HARD = 101, BGM_BILINEAR = 102, LBGM = 200,
BINBOOST_64 = 300, BINBOOST_128 = 301, BINBOOST_256 = 302
};
};
// descriptor computation using keypoints
void BoostDesc_Impl::compute( InputArray _image, vector<KeyPoint>& keypoints, OutputArray _descriptors )
{
// do nothing if no image
if( _image.getMat().empty() )
return;
if( keypoints.empty() )
return;
m_image = _image.getMat().clone();
// Only 8bit images
CV_Assert( m_image.depth() == CV_8U );
// convert to gray inplace
if( m_image.channels() > 1 )
cvtColor( m_image, m_image, COLOR_BGR2GRAY );
// initialize the variables
_descriptors.create( (int)keypoints.size(), descriptorSize(), descriptorType() );
_descriptors.setTo( Scalar::all(0) );
// descriptor storage
Mat descriptors = _descriptors.getMat();
parallel_for_( Range( 0, (int) keypoints.size() ),
ComputeBoostDescInvoker( m_image, &descriptors, keypoints,
m_desc_type, m_grad_atype, m_orient_q,
m_patch_size, m_nWLs, m_Dims,
m_wl_x_min, m_wl_x_max, m_wl_y_min, m_wl_y_max,
m_wl_thresh, m_wl_orient, m_wl_alpha, m_wl_beta,
m_use_scale_orientation, m_scale_factor )
);
}
void BoostDesc_Impl::ini_params( const int orientQuant, const int patchSize,
const int iGradAssignType,
const int nDim, const int nWLs,
const unsigned int thresh[], const int orient[],
const int x_min[], const int x_max[],
const int y_min[], const int y_max[],
const unsigned int alpha[], const unsigned int beta[] )
{
// desc type, norm, size
if ( m_desc_type == LBGM )
{
m_descriptor_size = nDim;
m_descriptor_norm = NORM_L2;
m_descriptor_type = CV_32FC1;
}
else
{
if ( ( m_desc_type == BGM ) ||
( m_desc_type == BGM_HARD ) ||
( m_desc_type == BGM_BILINEAR )
)
m_descriptor_size = nWLs / 8;
else
m_descriptor_size = nDim / 8;
m_descriptor_type = CV_8UC1;
m_descriptor_norm = NORM_HAMMING;
}
// 2d array dim
int dim0 = nDim;
int dim1 = nWLs;
// override beta dim0 on LBGM
if ( m_desc_type == LBGM ) dim0 = 1;
m_Dims = nDim;
m_nWLs = nWLs;
m_orient_q = orientQuant;
m_patch_size = patchSize;
m_grad_atype = iGradAssignType;
// cast into opencv Mat type as float
m_wl_thresh = Mat( dim0, dim1, CV_32F, reinterpret_cast<float *>(const_cast<unsigned int *>(thresh)) );
m_wl_alpha = Mat( dim0, dim1, CV_32F, reinterpret_cast<float *>(const_cast<unsigned int *>(alpha )) );
// cast into opencv Mat type as integer
m_wl_orient = Mat( dim0, dim1, CV_32S, const_cast<int *>(orient) );
m_wl_x_min = Mat( dim0, dim1, CV_32S, const_cast<int *>(x_min ) );
m_wl_x_max = Mat( dim0, dim1, CV_32S, const_cast<int *>(x_max ) );
m_wl_y_min = Mat( dim0, dim1, CV_32S, const_cast<int *>(y_min ) );
m_wl_y_max = Mat( dim0, dim1, CV_32S, const_cast<int *>(y_max ) );
// no beta
if ( beta == NULL ) return;
if ( m_desc_type == LBGM )
m_wl_beta = Mat( dim1, nDim, CV_32F, reinterpret_cast<float *>(const_cast<unsigned int *>(beta)) );
else
m_wl_beta = Mat( dim0, dim1, CV_32F, reinterpret_cast<float *>(const_cast<unsigned int *>(beta)) );
}
// constructor
BoostDesc_Impl::BoostDesc_Impl( int _desc, bool _use_scale_orientation, float _scale_factor )
: m_desc_type( _desc ), m_scale_factor( _scale_factor ),
m_use_scale_orientation( _use_scale_orientation )
{
// desc type
switch ( m_desc_type )
{
case BGM:
{
#include "boostdesc_bgm.i"
ini_params( orientQuant, patchSize,
iGradAssignType,
nDim, nWLs, thresh, orient,
x_min, x_max, y_min, y_max,
alpha, NULL );
}
break;
case BGM_HARD:
{
#include "boostdesc_bgm_hd.i"
ini_params( orientQuant, patchSize,
iGradAssignType,
nDim, nWLs, thresh, orient,
x_min, x_max, y_min, y_max,
alpha, NULL );
}
break;
case BGM_BILINEAR:
{
#include "boostdesc_bgm_bi.i"
ini_params( orientQuant, patchSize,
iGradAssignType,
nDim, nWLs, thresh, orient,
x_min, x_max, y_min, y_max,
alpha, NULL );
}
break;
case LBGM:
{
#include "boostdesc_lbgm.i"
ini_params( orientQuant, patchSize,
iGradAssignType,
nDim, nWLs, thresh, orient,
x_min, x_max, y_min, y_max,
alpha, beta );
}
break;
case BINBOOST_64:
{
#include "boostdesc_binboost_064.i"
ini_params( orientQuant, patchSize,
iGradAssignType,
nDim, nWLs, thresh, orient,
x_min, x_max, y_min, y_max,
alpha, beta );
}
break;
case BINBOOST_128:
{
#include "boostdesc_binboost_128.i"
ini_params( orientQuant, patchSize,
iGradAssignType,
nDim, nWLs, thresh, orient,
x_min, x_max, y_min, y_max,
alpha, beta );
}
break;
case BINBOOST_256:
{
#include "boostdesc_binboost_256.i"
ini_params( orientQuant, patchSize,
iGradAssignType,
nDim, nWLs, thresh, orient,
x_min, x_max, y_min, y_max,
alpha, beta );
}
break;
default:
CV_Error( Error::StsInternal, "Unknown Descriptor Type." );
}
}
// destructor
BoostDesc_Impl::~BoostDesc_Impl()
{
}
void BoostDesc_Impl::read (const FileNode& fn)
{
// if node is empty, keep previous value
if (!fn["desc_type"].empty())
fn["desc_type"] >> m_desc_type;
if (!fn["scale_factor"].empty())
fn["scale_factor"] >> m_scale_factor;
if (!fn["use_scale_orientation"].empty())
fn["use_scale_orientation"] >> m_use_scale_orientation;
}
void BoostDesc_Impl::write (FileStorage& fs) const
{
if(fs.isOpened())
{
fs << "name" << getDefaultName();
fs << "desc_type" << m_desc_type;
fs << "scale_factor" << m_scale_factor;
fs << "use_scale_orientation" << m_use_scale_orientation;
}
}
#endif // OPENCV_XFEATURES2D_HAS_BOOST_DATA
Ptr<BoostDesc> BoostDesc::create( int desc, bool use_scale_orientation, float scale_factor )
{
#ifdef OPENCV_XFEATURES2D_HAS_BOOST_DATA
return makePtr<BoostDesc_Impl>( desc, use_scale_orientation, scale_factor );
#else
CV_UNUSED(desc); CV_UNUSED(use_scale_orientation); CV_UNUSED(scale_factor);
CV_Error(Error::StsNotImplemented, "The OpenCV xfeatures2d binaries is built without downloaded Boost decriptor features: https://github.com/opencv/opencv_contrib/issues/1301");
#endif
}
String BoostDesc::getDefaultName() const
{
return (Feature2D::getDefaultName() + ".BOOST");
}
} // END NAMESPACE XFEATURES2D
} // END NAMESPACE CV
+296
View File
@@ -0,0 +1,296 @@
/*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"
#include <algorithm>
#include <vector>
#include <iostream>
#include <iomanip>
namespace cv
{
namespace xfeatures2d
{
/*
* BRIEF Descriptor
*/
class BriefDescriptorExtractorImpl : public BriefDescriptorExtractor
{
public:
enum { PATCH_SIZE = 48, KERNEL_SIZE = 9 };
// bytes is a length of descriptor in bytes. It can be equal 16, 32 or 64 bytes.
BriefDescriptorExtractorImpl( int bytes = 32, bool use_orientation = false );
void read( const FileNode& ) CV_OVERRIDE;
void write( FileStorage& ) const CV_OVERRIDE;
int descriptorSize() const CV_OVERRIDE;
int descriptorType() const CV_OVERRIDE;
int defaultNorm() const CV_OVERRIDE;
void setDescriptorSize(int bytes) CV_OVERRIDE;
int getDescriptorSize() const CV_OVERRIDE { return bytes_;}
void setUseOrientation(bool use_orientation) CV_OVERRIDE { use_orientation_ = use_orientation; }
bool getUseOrientation() const CV_OVERRIDE { return use_orientation_; };
void compute(InputArray image, std::vector<KeyPoint>& keypoints, OutputArray descriptors) CV_OVERRIDE;
protected:
typedef void(*PixelTestFn)(InputArray, const std::vector<KeyPoint>&, OutputArray, bool use_orientation );
int bytes_;
bool use_orientation_;
PixelTestFn test_fn_;
};
Ptr<BriefDescriptorExtractor> BriefDescriptorExtractor::create( int bytes, bool use_orientation )
{
return makePtr<BriefDescriptorExtractorImpl>(bytes, use_orientation );
}
String BriefDescriptorExtractor::getDefaultName() const
{
return (Feature2D::getDefaultName() + ".BRIEF");
}
inline int smoothedSum(const Mat& sum, const KeyPoint& pt, int y, int x, bool use_orientation, Matx21f R)
{
static const int HALF_KERNEL = BriefDescriptorExtractorImpl::KERNEL_SIZE / 2;
if ( use_orientation )
{
int rx = (int)(((float)x)*R(1,0) - ((float)y)*R(0,0));
int ry = (int)(((float)x)*R(0,0) + ((float)y)*R(1,0));
if (rx > 24) rx = 24;
if (rx < -24) rx = -24;
if (ry > 24) ry = 24;
if (ry < -24) ry = -24;
x = rx; y = ry;
}
const int img_y = (int)(pt.pt.y + 0.5) + y;
const int img_x = (int)(pt.pt.x + 0.5) + x;
return sum.at<int>(img_y + HALF_KERNEL + 1, img_x + HALF_KERNEL + 1)
- sum.at<int>(img_y + HALF_KERNEL + 1, img_x - HALF_KERNEL)
- sum.at<int>(img_y - HALF_KERNEL, img_x + HALF_KERNEL + 1)
+ sum.at<int>(img_y - HALF_KERNEL, img_x - HALF_KERNEL);
}
static void pixelTests16(InputArray _sum, const std::vector<KeyPoint>& keypoints, OutputArray _descriptors, bool use_orientation )
{
Matx21f R;
Mat sum = _sum.getMat(), descriptors = _descriptors.getMat();
for (size_t i = 0; i < keypoints.size(); ++i)
{
uchar* desc = descriptors.ptr(static_cast<int>(i));
const KeyPoint& pt = keypoints[i];
if ( use_orientation )
{
float angle = pt.angle;
angle *= (float)(CV_PI/180.f);
R(0,0) = sin(angle);
R(1,0) = cos(angle);
}
#include "generated_16.i"
}
}
static void pixelTests32(InputArray _sum, const std::vector<KeyPoint>& keypoints, OutputArray _descriptors, bool use_orientation)
{
Matx21f R;
Mat sum = _sum.getMat(), descriptors = _descriptors.getMat();
for (size_t i = 0; i < keypoints.size(); ++i)
{
uchar* desc = descriptors.ptr(static_cast<int>(i));
const KeyPoint& pt = keypoints[i];
if ( use_orientation )
{
float angle = pt.angle;
angle *= (float)(CV_PI / 180.f);
R(0,0) = sin(angle);
R(1,0) = cos(angle);
}
#include "generated_32.i"
}
}
static void pixelTests64(InputArray _sum, const std::vector<KeyPoint>& keypoints, OutputArray _descriptors, bool use_orientation)
{
Matx21f R;
Mat sum = _sum.getMat(), descriptors = _descriptors.getMat();
for (size_t i = 0; i < keypoints.size(); ++i)
{
uchar* desc = descriptors.ptr(static_cast<int>(i));
const KeyPoint& pt = keypoints[i];
if ( use_orientation )
{
float angle = pt.angle;
angle *= (float)(CV_PI/180.f);
R(0,0) = sin(angle);
R(1,0) = cos(angle);
}
#include "generated_64.i"
}
}
BriefDescriptorExtractorImpl::BriefDescriptorExtractorImpl(int bytes, bool use_orientation) :
bytes_(bytes), use_orientation_(use_orientation), test_fn_(NULL)
{
switch (bytes)
{
case 16:
test_fn_ = pixelTests16;
break;
case 32:
test_fn_ = pixelTests32;
break;
case 64:
test_fn_ = pixelTests64;
break;
default:
CV_Error(Error::StsBadArg, "bytes must be 16, 32, or 64");
}
}
void BriefDescriptorExtractorImpl::setDescriptorSize(int bytes)
{
bytes_ = bytes;
switch (bytes)
{
case 16:
test_fn_ = pixelTests16;
break;
case 32:
test_fn_ = pixelTests32;
break;
case 64:
test_fn_ = pixelTests64;
break;
default:
CV_Error(Error::StsBadArg, "bytes must be 16, 32, or 64");
}
}
int BriefDescriptorExtractorImpl::descriptorSize() const
{
return bytes_;
}
int BriefDescriptorExtractorImpl::descriptorType() const
{
return CV_8UC1;
}
int BriefDescriptorExtractorImpl::defaultNorm() const
{
return NORM_HAMMING;
}
void BriefDescriptorExtractorImpl::read( const FileNode& fn)
{
// if node is empty, keep previous value
if (!fn["descriptorSize"].empty())
{
int dSize = fn["descriptorSize"];
switch (dSize)
{
case 16:
test_fn_ = pixelTests16;
break;
case 32:
test_fn_ = pixelTests32;
break;
case 64:
test_fn_ = pixelTests64;
break;
default:
CV_Error(Error::StsBadArg, "descriptorSize must be 16, 32, or 64");
}
bytes_ = dSize;
}
if (!fn["use_orientation"].empty())
fn["use_orientation"] >> use_orientation_;
}
void BriefDescriptorExtractorImpl::write( FileStorage& fs) const
{
if ( fs.isOpened() )
{
fs << "name" << getDefaultName();
fs << "descriptorSize" << bytes_;
fs << "use_orientation" << use_orientation_;
}
}
void BriefDescriptorExtractorImpl::compute(InputArray image,
std::vector<KeyPoint>& keypoints,
OutputArray descriptors)
{
// Construct integral image for fast smoothing (box filter)
Mat sum;
Mat grayImage = image.getMat();
if( image.type() != CV_8U ) cvtColor( image, grayImage, COLOR_BGR2GRAY );
///TODO allow the user to pass in a precomputed integral image
//if(image.type() == CV_32S)
// sum = image;
//else
integral( grayImage, sum, CV_32S);
//Remove keypoints very close to the border
KeyPointsFilter::runByImageBorder(keypoints, image.size(), PATCH_SIZE/2 + KERNEL_SIZE/2);
descriptors.create((int)keypoints.size(), bytes_, CV_8U);
descriptors.setTo(Scalar::all(0));
test_fn_(sum, keypoints, descriptors, use_orientation_);
}
}
} // namespace cv
File diff suppressed because it is too large Load Diff
+946
View File
@@ -0,0 +1,946 @@
/*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 "opencv2/opencv_modules.hpp"
#if defined(HAVE_OPENCV_CUDAARITHM) && defined(OPENCV_ENABLE_NONFREE)
#include "opencv2/core/cuda/common.hpp"
#include "opencv2/core/cuda/limits.hpp"
#include "opencv2/core/cuda/saturate_cast.hpp"
#include "opencv2/core/cuda/reduce.hpp"
#include "opencv2/core/cuda/utility.hpp"
#include "opencv2/core/cuda/functional.hpp"
#include "opencv2/core/cuda/filters.hpp"
#include <opencv2/cudev/ptr2d/texture.hpp>
namespace cv { namespace cuda { namespace device
{
namespace surf
{
void loadGlobalConstants(int maxCandidates, int maxFeatures, int img_rows, int img_cols, int nOctaveLayers, float hessianThreshold);
void loadOctaveConstants(int octave, int layer_rows, int layer_cols);
void icvCalcLayerDetAndTrace_gpu(const PtrStepSz<unsigned int>& sum, const PtrStepf& det, const PtrStepf& trace, int img_rows, int img_cols,
int octave, int nOctaveLayer);
void icvFindMaximaInLayer_gpu(const PtrStepSz<unsigned int>& maskSum, const PtrStepf& det, const PtrStepf& trace, int4* maxPosBuffer, unsigned int* maxCounter,
int img_rows, int img_cols, int octave, bool use_mask, int nLayers);
void icvInterpolateKeypoint_gpu(const PtrStepf& det, const int4* maxPosBuffer, unsigned int maxCounter,
float* featureX, float* featureY, int* featureLaplacian, int* featureOctave, float* featureSize, float* featureHessian,
unsigned int* featureCounter);
void icvCalcOrientation_gpu(const PtrStepSz<unsigned int>& sum, const float* featureX, const float* featureY, const float* featureSize, float* featureDir, int nFeatures);
void compute_descriptors_gpu(const PtrStepSzb& img, PtrStepSz<float4> descriptors, const float* featureX, const float* featureY, const float* featureSize, const float* featureDir, int nFeatures);
}
}}}
namespace cv { namespace cuda { namespace device
{
namespace surf
{
////////////////////////////////////////////////////////////////////////
// Global parameters
// The maximum number of features (before subpixel interpolation) that memory is reserved for.
__constant__ int c_max_candidates;
// The maximum number of features that memory is reserved for.
__constant__ int c_max_features;
// The image size.
__constant__ int c_img_rows;
__constant__ int c_img_cols;
// The number of layers.
__constant__ int c_nOctaveLayers;
// The hessian threshold.
__constant__ float c_hessianThreshold;
// The current octave.
__constant__ int c_octave;
// The current layer size.
__constant__ int c_layer_rows;
__constant__ int c_layer_cols;
void loadGlobalConstants(int maxCandidates, int maxFeatures, int img_rows, int img_cols, int nOctaveLayers, float hessianThreshold)
{
cudaSafeCall( cudaMemcpyToSymbol(c_max_candidates, &maxCandidates, sizeof(maxCandidates)) );
cudaSafeCall( cudaMemcpyToSymbol(c_max_features, &maxFeatures, sizeof(maxFeatures)) );
cudaSafeCall( cudaMemcpyToSymbol(c_img_rows, &img_rows, sizeof(img_rows)) );
cudaSafeCall( cudaMemcpyToSymbol(c_img_cols, &img_cols, sizeof(img_cols)) );
cudaSafeCall( cudaMemcpyToSymbol(c_nOctaveLayers, &nOctaveLayers, sizeof(nOctaveLayers)) );
cudaSafeCall( cudaMemcpyToSymbol(c_hessianThreshold, &hessianThreshold, sizeof(hessianThreshold)) );
}
void loadOctaveConstants(int octave, int layer_rows, int layer_cols)
{
cudaSafeCall( cudaMemcpyToSymbol(c_octave, &octave, sizeof(octave)) );
cudaSafeCall( cudaMemcpyToSymbol(c_layer_rows, &layer_rows, sizeof(layer_rows)) );
cudaSafeCall( cudaMemcpyToSymbol(c_layer_cols, &layer_cols, sizeof(layer_cols)) );
}
template <int N> __device__ float icvCalcHaarPatternSum(cudev::TexturePtr<unsigned int> texSum, const float src[][5], int oldSize, int newSize, int y, int x)
{
#if defined __CUDA_ARCH__ && __CUDA_ARCH__ >= 200
typedef double real_t;
#else
typedef float real_t;
#endif
float ratio = (float)newSize / oldSize;
real_t d = 0;
#pragma unroll
for (int k = 0; k < N; ++k)
{
int dx1 = __float2int_rn(ratio * src[k][0]);
int dy1 = __float2int_rn(ratio * src[k][1]);
int dx2 = __float2int_rn(ratio * src[k][2]);
int dy2 = __float2int_rn(ratio * src[k][3]);
real_t t = 0;
t += texSum(y + dy1, x + dx1);
t -= texSum(y + dy2, x + dx1);
t -= texSum(y + dy1, x + dx2);
t += texSum(y + dy2, x + dx2);
d += t * src[k][4] / ((dx2 - dx1) * (dy2 - dy1));
}
return (float)d;
}
////////////////////////////////////////////////////////////////////////
// Hessian
__constant__ float c_DX [3][5] = { {0, 2, 3, 7, 1}, {3, 2, 6, 7, -2}, {6, 2, 9, 7, 1} };
__constant__ float c_DY [3][5] = { {2, 0, 7, 3, 1}, {2, 3, 7, 6, -2}, {2, 6, 7, 9, 1} };
__constant__ float c_DXY[4][5] = { {1, 1, 4, 4, 1}, {5, 1, 8, 4, -1}, {1, 5, 4, 8, -1}, {5, 5, 8, 8, 1} };
__host__ __device__ __forceinline__ int calcSize(int octave, int layer)
{
/* Wavelet size at first layer of first octave. */
const int HAAR_SIZE0 = 9;
/* Wavelet size increment between layers. This should be an even number,
such that the wavelet sizes in an octave are either all even or all odd.
This ensures that when looking for the neighbours of a sample, the layers
above and below are aligned correctly. */
const int HAAR_SIZE_INC = 6;
return (HAAR_SIZE0 + HAAR_SIZE_INC * layer) << octave;
}
__global__ void icvCalcLayerDetAndTrace(cudev::TexturePtr<unsigned int> texSum, PtrStepf det, PtrStepf trace)
{
// Determine the indices
const int gridDim_y = gridDim.y / (c_nOctaveLayers + 2);
const int blockIdx_y = blockIdx.y % gridDim_y;
const int blockIdx_z = blockIdx.y / gridDim_y;
const int j = threadIdx.x + blockIdx.x * blockDim.x;
const int i = threadIdx.y + blockIdx_y * blockDim.y;
const int layer = blockIdx_z;
const int size = calcSize(c_octave, layer);
const int samples_i = 1 + ((c_img_rows - size) >> c_octave);
const int samples_j = 1 + ((c_img_cols - size) >> c_octave);
// Ignore pixels where some of the kernel is outside the image
const int margin = (size >> 1) >> c_octave;
if (size <= c_img_rows && size <= c_img_cols && i < samples_i && j < samples_j)
{
const float dx = icvCalcHaarPatternSum<3>(texSum, c_DX , 9, size, (i << c_octave), (j << c_octave));
const float dy = icvCalcHaarPatternSum<3>(texSum, c_DY , 9, size, (i << c_octave), (j << c_octave));
const float dxy = icvCalcHaarPatternSum<4>(texSum, c_DXY, 9, size, (i << c_octave), (j << c_octave));
det.ptr(layer * c_layer_rows + i + margin)[j + margin] = dx * dy - 0.81f * dxy * dxy;
trace.ptr(layer * c_layer_rows + i + margin)[j + margin] = dx + dy;
}
}
void icvCalcLayerDetAndTrace_gpu(const PtrStepSz<unsigned int>& sum, const PtrStepf& det, const PtrStepf& trace, int img_rows, int img_cols,
int octave, int nOctaveLayers)
{
const int min_size = calcSize(octave, 0);
const int max_samples_i = 1 + ((img_rows - min_size) >> octave);
const int max_samples_j = 1 + ((img_cols - min_size) >> octave);
cudev::Texture<unsigned int> texSum(sum);
dim3 threads(16, 16);
dim3 grid;
grid.x = divUp(max_samples_j, threads.x);
grid.y = divUp(max_samples_i, threads.y) * (nOctaveLayers + 2);
icvCalcLayerDetAndTrace<<<grid, threads>>>(texSum, det, trace);
cudaSafeCall( cudaGetLastError() );
cudaSafeCall( cudaDeviceSynchronize() );
}
////////////////////////////////////////////////////////////////////////
// NONMAX
__constant__ float c_DM[5] = {0, 0, 9, 9, 1};
template<bool useMask = true>
struct Mask
{
__host__ Mask(){};
__host__ Mask(cudev::TexturePtr<unsigned int> tex_): tex(tex_) {};
__device__ bool check(int sum_i, int sum_j, int size)
{
int dx1 = 0;
int dy1 = 0;
int dx2 = 0;
int dy2 = 0;
float ratio = 0;
float d = 0;
float t = 0;
if (!useMask) return true;
ratio = (float)size / 9.0f;
dx1 = __float2int_rn(ratio * c_DM[0]);
dy1 = __float2int_rn(ratio * c_DM[1]);
dx2 = __float2int_rn(ratio * c_DM[2]);
dy2 = __float2int_rn(ratio * c_DM[3]);
t += tex(sum_i + dy1, sum_j + dx1);
t -= tex(sum_i + dy2, sum_j + dx1);
t -= tex(sum_i + dy1, sum_j + dx2);
t += tex(sum_i + dy2, sum_j + dx2);
d += t * c_DM[4] / ((dx2 - dx1) * (dy2 - dy1));
return (d >= 0.5f);
}
cudev::TexturePtr<unsigned int> tex;
};
template<class T>
__global__ void icvFindMaximaInLayer(T mask, const PtrStepf det, const PtrStepf trace, int4* maxPosBuffer,
unsigned int* maxCounter)
{
#if defined __CUDA_ARCH__ && __CUDA_ARCH__ >= 110
extern __shared__ float N9[];
// The hidx variables are the indices to the hessian buffer.
const int gridDim_y = gridDim.y / c_nOctaveLayers;
const int blockIdx_y = blockIdx.y % gridDim_y;
const int blockIdx_z = blockIdx.y / gridDim_y;
const int layer = blockIdx_z + 1;
const int size = calcSize(c_octave, layer);
// Ignore pixels without a 3x3x3 neighbourhood in the layer above
const int margin = ((calcSize(c_octave, layer + 1) >> 1) >> c_octave) + 1;
const int j = threadIdx.x + blockIdx.x * (blockDim.x - 2) + margin - 1;
const int i = threadIdx.y + blockIdx_y * (blockDim.y - 2) + margin - 1;
// Is this thread within the hessian buffer?
const int zoff = blockDim.x * blockDim.y;
const int localLin = threadIdx.x + threadIdx.y * blockDim.x + zoff;
N9[localLin - zoff] = det.ptr(c_layer_rows * (layer - 1) + ::min(::max(i, 0), c_img_rows - 1))[::min(::max(j, 0), c_img_cols - 1)];
N9[localLin ] = det.ptr(c_layer_rows * (layer ) + ::min(::max(i, 0), c_img_rows - 1))[::min(::max(j, 0), c_img_cols - 1)];
N9[localLin + zoff] = det.ptr(c_layer_rows * (layer + 1) + ::min(::max(i, 0), c_img_rows - 1))[::min(::max(j, 0), c_img_cols - 1)];
__syncthreads();
if (i < c_layer_rows - margin && j < c_layer_cols - margin && threadIdx.x > 0 && threadIdx.x < blockDim.x - 1 && threadIdx.y > 0 && threadIdx.y < blockDim.y - 1)
{
float val0 = N9[localLin];
if (val0 > c_hessianThreshold)
{
// Coordinates for the start of the wavelet in the sum image. There
// is some integer division involved, so don't try to simplify this
// (cancel out sampleStep) without checking the result is the same
const int sum_i = (i - ((size >> 1) >> c_octave)) << c_octave;
const int sum_j = (j - ((size >> 1) >> c_octave)) << c_octave;
if (mask.check(sum_i, sum_j, size))
{
// Check to see if we have a max (in its 26 neighbours)
const bool condmax = val0 > N9[localLin - 1 - blockDim.x - zoff]
&& val0 > N9[localLin - blockDim.x - zoff]
&& val0 > N9[localLin + 1 - blockDim.x - zoff]
&& val0 > N9[localLin - 1 - zoff]
&& val0 > N9[localLin - zoff]
&& val0 > N9[localLin + 1 - zoff]
&& val0 > N9[localLin - 1 + blockDim.x - zoff]
&& val0 > N9[localLin + blockDim.x - zoff]
&& val0 > N9[localLin + 1 + blockDim.x - zoff]
&& val0 > N9[localLin - 1 - blockDim.x]
&& val0 > N9[localLin - blockDim.x]
&& val0 > N9[localLin + 1 - blockDim.x]
&& val0 > N9[localLin - 1 ]
&& val0 > N9[localLin + 1 ]
&& val0 > N9[localLin - 1 + blockDim.x]
&& val0 > N9[localLin + blockDim.x]
&& val0 > N9[localLin + 1 + blockDim.x]
&& val0 > N9[localLin - 1 - blockDim.x + zoff]
&& val0 > N9[localLin - blockDim.x + zoff]
&& val0 > N9[localLin + 1 - blockDim.x + zoff]
&& val0 > N9[localLin - 1 + zoff]
&& val0 > N9[localLin + zoff]
&& val0 > N9[localLin + 1 + zoff]
&& val0 > N9[localLin - 1 + blockDim.x + zoff]
&& val0 > N9[localLin + blockDim.x + zoff]
&& val0 > N9[localLin + 1 + blockDim.x + zoff]
;
if(condmax)
{
unsigned int ind = atomicInc(maxCounter,(unsigned int) -1);
if (ind < c_max_candidates)
{
const int laplacian = (int) copysignf(1.0f, trace.ptr(layer * c_layer_rows + i)[j]);
maxPosBuffer[ind] = make_int4(j, i, layer, laplacian);
}
}
}
}
}
#endif
}
void icvFindMaximaInLayer_gpu(const PtrStepSz<unsigned int>& maskSum, const PtrStepf& det, const PtrStepf& trace, int4* maxPosBuffer, unsigned int* maxCounter,
int img_rows, int img_cols, int octave, bool use_mask, int nOctaveLayers)
{
const int layer_rows = img_rows >> octave;
const int layer_cols = img_cols >> octave;
const int min_margin = ((calcSize(octave, 2) >> 1) >> octave) + 1;
dim3 threads(16, 16);
dim3 grid;
grid.x = divUp(layer_cols - 2 * min_margin, threads.x - 2);
grid.y = divUp(layer_rows - 2 * min_margin, threads.y - 2) * nOctaveLayers;
const size_t smem_size = threads.x * threads.y * 3 * sizeof(float);
if (use_mask) {
cudev::Texture<unsigned int> texMaskSum(maskSum);
Mask<true> mask(texMaskSum);
icvFindMaximaInLayer<<<grid, threads, smem_size>>>(mask, det, trace, maxPosBuffer, maxCounter);
}
else {
Mask<false> mask;
icvFindMaximaInLayer<<<grid, threads, smem_size>>>(mask, det, trace, maxPosBuffer, maxCounter);
}
cudaSafeCall( cudaGetLastError() );
cudaSafeCall( cudaDeviceSynchronize() );
}
////////////////////////////////////////////////////////////////////////
// INTERPOLATION
__global__ void icvInterpolateKeypoint(const PtrStepf det, const int4* maxPosBuffer,
float* featureX, float* featureY, int* featureLaplacian, int* featureOctave, float* featureSize, float* featureHessian,
unsigned int* featureCounter)
{
#if defined __CUDA_ARCH__ && __CUDA_ARCH__ >= 110
const int4 maxPos = maxPosBuffer[blockIdx.x];
const int j = maxPos.x - 1 + threadIdx.x;
const int i = maxPos.y - 1 + threadIdx.y;
const int layer = maxPos.z - 1 + threadIdx.z;
__shared__ float N9[3][3][3];
N9[threadIdx.z][threadIdx.y][threadIdx.x] = det.ptr(c_layer_rows * layer + i)[j];
__syncthreads();
if (threadIdx.x == 0 && threadIdx.y == 0 && threadIdx.z == 0)
{
__shared__ float dD[3];
//dx
dD[0] = -0.5f * (N9[1][1][2] - N9[1][1][0]);
//dy
dD[1] = -0.5f * (N9[1][2][1] - N9[1][0][1]);
//ds
dD[2] = -0.5f * (N9[2][1][1] - N9[0][1][1]);
__shared__ float H[3][3];
//dxx
H[0][0] = N9[1][1][0] - 2.0f * N9[1][1][1] + N9[1][1][2];
//dxy
H[0][1]= 0.25f * (N9[1][2][2] - N9[1][2][0] - N9[1][0][2] + N9[1][0][0]);
//dxs
H[0][2]= 0.25f * (N9[2][1][2] - N9[2][1][0] - N9[0][1][2] + N9[0][1][0]);
//dyx = dxy
H[1][0] = H[0][1];
//dyy
H[1][1] = N9[1][0][1] - 2.0f * N9[1][1][1] + N9[1][2][1];
//dys
H[1][2]= 0.25f * (N9[2][2][1] - N9[2][0][1] - N9[0][2][1] + N9[0][0][1]);
//dsx = dxs
H[2][0] = H[0][2];
//dsy = dys
H[2][1] = H[1][2];
//dss
H[2][2] = N9[0][1][1] - 2.0f * N9[1][1][1] + N9[2][1][1];
__shared__ float x[3];
if (solve3x3(H, dD, x))
{
if (::fabs(x[0]) <= 1.f && ::fabs(x[1]) <= 1.f && ::fabs(x[2]) <= 1.f)
{
// if the step is within the interpolation region, perform it
const int size = calcSize(c_octave, maxPos.z);
const int sum_i = (maxPos.y - ((size >> 1) >> c_octave)) << c_octave;
const int sum_j = (maxPos.x - ((size >> 1) >> c_octave)) << c_octave;
const float center_i = sum_i + (float)(size - 1) / 2;
const float center_j = sum_j + (float)(size - 1) / 2;
const float px = center_j + x[0] * (1 << c_octave);
const float py = center_i + x[1] * (1 << c_octave);
const int ds = size - calcSize(c_octave, maxPos.z - 1);
const float psize = roundf(size + x[2] * ds);
/* The sampling intervals and wavelet sized for selecting an orientation
and building the keypoint descriptor are defined relative to 's' */
const float s = psize * 1.2f / 9.0f;
/* To find the dominant orientation, the gradients in x and y are
sampled in a circle of radius 6s using wavelets of size 4s.
We ensure the gradient wavelet size is even to ensure the
wavelet pattern is balanced and symmetric around its center */
const int grad_wav_size = 2 * __float2int_rn(2.0f * s);
// check when grad_wav_size is too big
if ((c_img_rows + 1) >= grad_wav_size && (c_img_cols + 1) >= grad_wav_size)
{
// Get a new feature index.
unsigned int ind = atomicInc(featureCounter, (unsigned int)-1);
if (ind < c_max_features)
{
featureX[ind] = px;
featureY[ind] = py;
featureLaplacian[ind] = maxPos.w;
featureOctave[ind] = c_octave;
featureSize[ind] = psize;
featureHessian[ind] = N9[1][1][1];
}
} // grad_wav_size check
} // If the subpixel interpolation worked
}
} // If this is thread 0.
#endif
}
void icvInterpolateKeypoint_gpu(const PtrStepf& det, const int4* maxPosBuffer, unsigned int maxCounter,
float* featureX, float* featureY, int* featureLaplacian, int* featureOctave, float* featureSize, float* featureHessian,
unsigned int* featureCounter)
{
dim3 threads;
threads.x = 3;
threads.y = 3;
threads.z = 3;
dim3 grid;
grid.x = maxCounter;
icvInterpolateKeypoint<<<grid, threads>>>(det, maxPosBuffer, featureX, featureY, featureLaplacian, featureOctave, featureSize, featureHessian, featureCounter);
cudaSafeCall( cudaGetLastError() );
cudaSafeCall( cudaDeviceSynchronize() );
}
////////////////////////////////////////////////////////////////////////
// Orientation
#define ORI_SEARCH_INC 5
#define ORI_WIN 60
#define ORI_SAMPLES 113
__constant__ float c_aptX[ORI_SAMPLES] = {-6, -5, -5, -5, -5, -5, -5, -5, -4, -4, -4, -4, -4, -4, -4, -4, -4, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -3, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 6};
__constant__ float c_aptY[ORI_SAMPLES] = {0, -3, -2, -1, 0, 1, 2, 3, -4, -3, -2, -1, 0, 1, 2, 3, 4, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, -4, -3, -2, -1, 0, 1, 2, 3, 4, -3, -2, -1, 0, 1, 2, 3, 0};
__constant__ float c_aptW[ORI_SAMPLES] = {0.001455130288377404f, 0.001707611023448408f, 0.002547456417232752f, 0.003238451667129993f, 0.0035081731621176f, 0.003238451667129993f, 0.002547456417232752f, 0.001707611023448408f, 0.002003900473937392f, 0.0035081731621176f, 0.005233579315245152f, 0.00665318313986063f, 0.00720730796456337f, 0.00665318313986063f, 0.005233579315245152f, 0.0035081731621176f, 0.002003900473937392f, 0.001707611023448408f, 0.0035081731621176f, 0.006141661666333675f, 0.009162282571196556f, 0.01164754293859005f, 0.01261763460934162f, 0.01164754293859005f, 0.009162282571196556f, 0.006141661666333675f, 0.0035081731621176f, 0.001707611023448408f, 0.002547456417232752f, 0.005233579315245152f, 0.009162282571196556f, 0.01366852037608624f, 0.01737609319388866f, 0.0188232995569706f, 0.01737609319388866f, 0.01366852037608624f, 0.009162282571196556f, 0.005233579315245152f, 0.002547456417232752f, 0.003238451667129993f, 0.00665318313986063f, 0.01164754293859005f, 0.01737609319388866f, 0.02208934165537357f, 0.02392910048365593f, 0.02208934165537357f, 0.01737609319388866f, 0.01164754293859005f, 0.00665318313986063f, 0.003238451667129993f, 0.001455130288377404f, 0.0035081731621176f, 0.00720730796456337f, 0.01261763460934162f, 0.0188232995569706f, 0.02392910048365593f, 0.02592208795249462f, 0.02392910048365593f, 0.0188232995569706f, 0.01261763460934162f, 0.00720730796456337f, 0.0035081731621176f, 0.001455130288377404f, 0.003238451667129993f, 0.00665318313986063f, 0.01164754293859005f, 0.01737609319388866f, 0.02208934165537357f, 0.02392910048365593f, 0.02208934165537357f, 0.01737609319388866f, 0.01164754293859005f, 0.00665318313986063f, 0.003238451667129993f, 0.002547456417232752f, 0.005233579315245152f, 0.009162282571196556f, 0.01366852037608624f, 0.01737609319388866f, 0.0188232995569706f, 0.01737609319388866f, 0.01366852037608624f, 0.009162282571196556f, 0.005233579315245152f, 0.002547456417232752f, 0.001707611023448408f, 0.0035081731621176f, 0.006141661666333675f, 0.009162282571196556f, 0.01164754293859005f, 0.01261763460934162f, 0.01164754293859005f, 0.009162282571196556f, 0.006141661666333675f, 0.0035081731621176f, 0.001707611023448408f, 0.002003900473937392f, 0.0035081731621176f, 0.005233579315245152f, 0.00665318313986063f, 0.00720730796456337f, 0.00665318313986063f, 0.005233579315245152f, 0.0035081731621176f, 0.002003900473937392f, 0.001707611023448408f, 0.002547456417232752f, 0.003238451667129993f, 0.0035081731621176f, 0.003238451667129993f, 0.002547456417232752f, 0.001707611023448408f, 0.001455130288377404f};
__constant__ float c_NX[2][5] = {{0, 0, 2, 4, -1}, {2, 0, 4, 4, 1}};
__constant__ float c_NY[2][5] = {{0, 0, 4, 2, 1}, {0, 2, 4, 4, -1}};
__global__ void icvCalcOrientation(cudev::TexturePtr<unsigned int> texSum, const float* featureX, const float* featureY, const float* featureSize, float* featureDir)
{
__shared__ float s_X[128];
__shared__ float s_Y[128];
__shared__ float s_angle[128];
__shared__ float s_sumx[32 * 4];
__shared__ float s_sumy[32 * 4];
/* The sampling intervals and wavelet sized for selecting an orientation
and building the keypoint descriptor are defined relative to 's' */
const float s = featureSize[blockIdx.x] * 1.2f / 9.0f;
/* To find the dominant orientation, the gradients in x and y are
sampled in a circle of radius 6s using wavelets of size 4s.
We ensure the gradient wavelet size is even to ensure the
wavelet pattern is balanced and symmetric around its center */
const int grad_wav_size = 2 * __float2int_rn(2.0f * s);
// check when grad_wav_size is too big
if ((c_img_rows + 1) < grad_wav_size || (c_img_cols + 1) < grad_wav_size)
return;
// Calc X, Y, angle and store it to shared memory
const int tid = threadIdx.y * blockDim.x + threadIdx.x;
float X = 0.0f, Y = 0.0f, angle = 0.0f;
if (tid < ORI_SAMPLES)
{
const float margin = (float)(grad_wav_size - 1) / 2.0f;
const int x = __float2int_rn(featureX[blockIdx.x] + c_aptX[tid] * s - margin);
const int y = __float2int_rn(featureY[blockIdx.x] + c_aptY[tid] * s - margin);
if (y >= 0 && y < (c_img_rows + 1) - grad_wav_size &&
x >= 0 && x < (c_img_cols + 1) - grad_wav_size)
{
X = c_aptW[tid] * icvCalcHaarPatternSum<2>(texSum, c_NX, 4, grad_wav_size, y, x);
Y = c_aptW[tid] * icvCalcHaarPatternSum<2>(texSum, c_NY, 4, grad_wav_size, y, x);
angle = atan2f(Y, X);
if (angle < 0)
angle += 2.0f * CV_PI_F;
angle *= 180.0f / CV_PI_F;
}
}
s_X[tid] = X;
s_Y[tid] = Y;
s_angle[tid] = angle;
__syncthreads();
float bestx = 0, besty = 0, best_mod = 0;
#if defined __CUDA_ARCH__ && __CUDA_ARCH__ >= 200
#pragma unroll
#endif
for (int i = 0; i < 18; ++i)
{
const int dir = (i * 4 + threadIdx.y) * ORI_SEARCH_INC;
float sumx = 0.0f, sumy = 0.0f;
int d = ::abs(__float2int_rn(s_angle[threadIdx.x]) - dir);
if (d < ORI_WIN / 2 || d > 360 - ORI_WIN / 2)
{
sumx = s_X[threadIdx.x];
sumy = s_Y[threadIdx.x];
}
d = ::abs(__float2int_rn(s_angle[threadIdx.x + 32]) - dir);
if (d < ORI_WIN / 2 || d > 360 - ORI_WIN / 2)
{
sumx += s_X[threadIdx.x + 32];
sumy += s_Y[threadIdx.x + 32];
}
d = ::abs(__float2int_rn(s_angle[threadIdx.x + 64]) - dir);
if (d < ORI_WIN / 2 || d > 360 - ORI_WIN / 2)
{
sumx += s_X[threadIdx.x + 64];
sumy += s_Y[threadIdx.x + 64];
}
d = ::abs(__float2int_rn(s_angle[threadIdx.x + 96]) - dir);
if (d < ORI_WIN / 2 || d > 360 - ORI_WIN / 2)
{
sumx += s_X[threadIdx.x + 96];
sumy += s_Y[threadIdx.x + 96];
}
plus<float> op;
device::reduce<32>(smem_tuple(s_sumx + threadIdx.y * 32, s_sumy + threadIdx.y * 32),
thrust::tie(sumx, sumy), threadIdx.x, thrust::make_tuple(op, op));
const float temp_mod = sumx * sumx + sumy * sumy;
if (temp_mod > best_mod)
{
best_mod = temp_mod;
bestx = sumx;
besty = sumy;
}
__syncthreads();
}
if (threadIdx.x == 0)
{
s_X[threadIdx.y] = bestx;
s_Y[threadIdx.y] = besty;
s_angle[threadIdx.y] = best_mod;
}
__syncthreads();
if (threadIdx.x == 0 && threadIdx.y == 0)
{
int bestIdx = 0;
if (s_angle[1] > s_angle[bestIdx])
bestIdx = 1;
if (s_angle[2] > s_angle[bestIdx])
bestIdx = 2;
if (s_angle[3] > s_angle[bestIdx])
bestIdx = 3;
float kp_dir = atan2f(s_Y[bestIdx], s_X[bestIdx]);
if (kp_dir < 0)
kp_dir += 2.0f * CV_PI_F;
kp_dir *= 180.0f / CV_PI_F;
kp_dir = 360.0f - kp_dir;
if (::fabsf(kp_dir - 360.f) < numeric_limits<float>::epsilon())
kp_dir = 0.f;
featureDir[blockIdx.x] = kp_dir;
}
}
#undef ORI_SEARCH_INC
#undef ORI_WIN
#undef ORI_SAMPLES
void icvCalcOrientation_gpu(const PtrStepSz<unsigned int>& sum, const float* featureX, const float* featureY, const float* featureSize, float* featureDir, int nFeatures)
{
cudev::Texture<unsigned int> texSum(sum);
dim3 threads;
threads.x = 32;
threads.y = 4;
dim3 grid;
grid.x = nFeatures;
icvCalcOrientation<<<grid, threads>>>(texSum, featureX, featureY, featureSize, featureDir);
cudaSafeCall( cudaGetLastError() );
cudaSafeCall( cudaDeviceSynchronize() );
}
////////////////////////////////////////////////////////////////////////
// Descriptors
#define PATCH_SZ 20
__constant__ float c_DW[PATCH_SZ * PATCH_SZ] =
{
3.695352233989979e-006f, 8.444558261544444e-006f, 1.760426494001877e-005f, 3.34794785885606e-005f, 5.808438800158911e-005f, 9.193058212986216e-005f, 0.0001327334757661447f, 0.0001748319627949968f, 0.0002100782439811155f, 0.0002302826324012131f, 0.0002302826324012131f, 0.0002100782439811155f, 0.0001748319627949968f, 0.0001327334757661447f, 9.193058212986216e-005f, 5.808438800158911e-005f, 3.34794785885606e-005f, 1.760426494001877e-005f, 8.444558261544444e-006f, 3.695352233989979e-006f,
8.444558261544444e-006f, 1.929736572492402e-005f, 4.022897701361217e-005f, 7.650675252079964e-005f, 0.0001327334903180599f, 0.0002100782585330308f, 0.0003033203829545528f, 0.0003995231236331165f, 0.0004800673632416874f, 0.0005262381164357066f, 0.0005262381164357066f, 0.0004800673632416874f, 0.0003995231236331165f, 0.0003033203829545528f, 0.0002100782585330308f, 0.0001327334903180599f, 7.650675252079964e-005f, 4.022897701361217e-005f, 1.929736572492402e-005f, 8.444558261544444e-006f,
1.760426494001877e-005f, 4.022897701361217e-005f, 8.386484114453197e-005f, 0.0001594926579855382f, 0.0002767078403849155f, 0.0004379475140012801f, 0.0006323281559161842f, 0.0008328808471560478f, 0.001000790391117334f, 0.001097041997127235f, 0.001097041997127235f, 0.001000790391117334f, 0.0008328808471560478f, 0.0006323281559161842f, 0.0004379475140012801f, 0.0002767078403849155f, 0.0001594926579855382f, 8.386484114453197e-005f, 4.022897701361217e-005f, 1.760426494001877e-005f,
3.34794785885606e-005f, 7.650675252079964e-005f, 0.0001594926579855382f, 0.0003033203247468919f, 0.0005262380582280457f, 0.0008328807889483869f, 0.001202550483867526f, 0.001583957928232849f, 0.001903285388834775f, 0.002086334861814976f, 0.002086334861814976f, 0.001903285388834775f, 0.001583957928232849f, 0.001202550483867526f, 0.0008328807889483869f, 0.0005262380582280457f, 0.0003033203247468919f, 0.0001594926579855382f, 7.650675252079964e-005f, 3.34794785885606e-005f,
5.808438800158911e-005f, 0.0001327334903180599f, 0.0002767078403849155f, 0.0005262380582280457f, 0.0009129836107604206f, 0.001444985857233405f, 0.002086335094645619f, 0.002748048631474376f, 0.00330205773934722f, 0.003619635012000799f, 0.003619635012000799f, 0.00330205773934722f, 0.002748048631474376f, 0.002086335094645619f, 0.001444985857233405f, 0.0009129836107604206f, 0.0005262380582280457f, 0.0002767078403849155f, 0.0001327334903180599f, 5.808438800158911e-005f,
9.193058212986216e-005f, 0.0002100782585330308f, 0.0004379475140012801f, 0.0008328807889483869f, 0.001444985857233405f, 0.002286989474669099f, 0.00330205773934722f, 0.004349356517195702f, 0.00522619066759944f, 0.005728822201490402f, 0.005728822201490402f, 0.00522619066759944f, 0.004349356517195702f, 0.00330205773934722f, 0.002286989474669099f, 0.001444985857233405f, 0.0008328807889483869f, 0.0004379475140012801f, 0.0002100782585330308f, 9.193058212986216e-005f,
0.0001327334757661447f, 0.0003033203829545528f, 0.0006323281559161842f, 0.001202550483867526f, 0.002086335094645619f, 0.00330205773934722f, 0.004767658654600382f, 0.006279794964939356f, 0.007545807864516974f, 0.008271530270576477f, 0.008271530270576477f, 0.007545807864516974f, 0.006279794964939356f, 0.004767658654600382f, 0.00330205773934722f, 0.002086335094645619f, 0.001202550483867526f, 0.0006323281559161842f, 0.0003033203829545528f, 0.0001327334757661447f,
0.0001748319627949968f, 0.0003995231236331165f, 0.0008328808471560478f, 0.001583957928232849f, 0.002748048631474376f, 0.004349356517195702f, 0.006279794964939356f, 0.008271529339253902f, 0.009939077310264111f, 0.01089497376233339f, 0.01089497376233339f, 0.009939077310264111f, 0.008271529339253902f, 0.006279794964939356f, 0.004349356517195702f, 0.002748048631474376f, 0.001583957928232849f, 0.0008328808471560478f, 0.0003995231236331165f, 0.0001748319627949968f,
0.0002100782439811155f, 0.0004800673632416874f, 0.001000790391117334f, 0.001903285388834775f, 0.00330205773934722f, 0.00522619066759944f, 0.007545807864516974f, 0.009939077310264111f, 0.01194280479103327f, 0.01309141051024199f, 0.01309141051024199f, 0.01194280479103327f, 0.009939077310264111f, 0.007545807864516974f, 0.00522619066759944f, 0.00330205773934722f, 0.001903285388834775f, 0.001000790391117334f, 0.0004800673632416874f, 0.0002100782439811155f,
0.0002302826324012131f, 0.0005262381164357066f, 0.001097041997127235f, 0.002086334861814976f, 0.003619635012000799f, 0.005728822201490402f, 0.008271530270576477f, 0.01089497376233339f, 0.01309141051024199f, 0.01435048412531614f, 0.01435048412531614f, 0.01309141051024199f, 0.01089497376233339f, 0.008271530270576477f, 0.005728822201490402f, 0.003619635012000799f, 0.002086334861814976f, 0.001097041997127235f, 0.0005262381164357066f, 0.0002302826324012131f,
0.0002302826324012131f, 0.0005262381164357066f, 0.001097041997127235f, 0.002086334861814976f, 0.003619635012000799f, 0.005728822201490402f, 0.008271530270576477f, 0.01089497376233339f, 0.01309141051024199f, 0.01435048412531614f, 0.01435048412531614f, 0.01309141051024199f, 0.01089497376233339f, 0.008271530270576477f, 0.005728822201490402f, 0.003619635012000799f, 0.002086334861814976f, 0.001097041997127235f, 0.0005262381164357066f, 0.0002302826324012131f,
0.0002100782439811155f, 0.0004800673632416874f, 0.001000790391117334f, 0.001903285388834775f, 0.00330205773934722f, 0.00522619066759944f, 0.007545807864516974f, 0.009939077310264111f, 0.01194280479103327f, 0.01309141051024199f, 0.01309141051024199f, 0.01194280479103327f, 0.009939077310264111f, 0.007545807864516974f, 0.00522619066759944f, 0.00330205773934722f, 0.001903285388834775f, 0.001000790391117334f, 0.0004800673632416874f, 0.0002100782439811155f,
0.0001748319627949968f, 0.0003995231236331165f, 0.0008328808471560478f, 0.001583957928232849f, 0.002748048631474376f, 0.004349356517195702f, 0.006279794964939356f, 0.008271529339253902f, 0.009939077310264111f, 0.01089497376233339f, 0.01089497376233339f, 0.009939077310264111f, 0.008271529339253902f, 0.006279794964939356f, 0.004349356517195702f, 0.002748048631474376f, 0.001583957928232849f, 0.0008328808471560478f, 0.0003995231236331165f, 0.0001748319627949968f,
0.0001327334757661447f, 0.0003033203829545528f, 0.0006323281559161842f, 0.001202550483867526f, 0.002086335094645619f, 0.00330205773934722f, 0.004767658654600382f, 0.006279794964939356f, 0.007545807864516974f, 0.008271530270576477f, 0.008271530270576477f, 0.007545807864516974f, 0.006279794964939356f, 0.004767658654600382f, 0.00330205773934722f, 0.002086335094645619f, 0.001202550483867526f, 0.0006323281559161842f, 0.0003033203829545528f, 0.0001327334757661447f,
9.193058212986216e-005f, 0.0002100782585330308f, 0.0004379475140012801f, 0.0008328807889483869f, 0.001444985857233405f, 0.002286989474669099f, 0.00330205773934722f, 0.004349356517195702f, 0.00522619066759944f, 0.005728822201490402f, 0.005728822201490402f, 0.00522619066759944f, 0.004349356517195702f, 0.00330205773934722f, 0.002286989474669099f, 0.001444985857233405f, 0.0008328807889483869f, 0.0004379475140012801f, 0.0002100782585330308f, 9.193058212986216e-005f,
5.808438800158911e-005f, 0.0001327334903180599f, 0.0002767078403849155f, 0.0005262380582280457f, 0.0009129836107604206f, 0.001444985857233405f, 0.002086335094645619f, 0.002748048631474376f, 0.00330205773934722f, 0.003619635012000799f, 0.003619635012000799f, 0.00330205773934722f, 0.002748048631474376f, 0.002086335094645619f, 0.001444985857233405f, 0.0009129836107604206f, 0.0005262380582280457f, 0.0002767078403849155f, 0.0001327334903180599f, 5.808438800158911e-005f,
3.34794785885606e-005f, 7.650675252079964e-005f, 0.0001594926579855382f, 0.0003033203247468919f, 0.0005262380582280457f, 0.0008328807889483869f, 0.001202550483867526f, 0.001583957928232849f, 0.001903285388834775f, 0.002086334861814976f, 0.002086334861814976f, 0.001903285388834775f, 0.001583957928232849f, 0.001202550483867526f, 0.0008328807889483869f, 0.0005262380582280457f, 0.0003033203247468919f, 0.0001594926579855382f, 7.650675252079964e-005f, 3.34794785885606e-005f,
1.760426494001877e-005f, 4.022897701361217e-005f, 8.386484114453197e-005f, 0.0001594926579855382f, 0.0002767078403849155f, 0.0004379475140012801f, 0.0006323281559161842f, 0.0008328808471560478f, 0.001000790391117334f, 0.001097041997127235f, 0.001097041997127235f, 0.001000790391117334f, 0.0008328808471560478f, 0.0006323281559161842f, 0.0004379475140012801f, 0.0002767078403849155f, 0.0001594926579855382f, 8.386484114453197e-005f, 4.022897701361217e-005f, 1.760426494001877e-005f,
8.444558261544444e-006f, 1.929736572492402e-005f, 4.022897701361217e-005f, 7.650675252079964e-005f, 0.0001327334903180599f, 0.0002100782585330308f, 0.0003033203829545528f, 0.0003995231236331165f, 0.0004800673632416874f, 0.0005262381164357066f, 0.0005262381164357066f, 0.0004800673632416874f, 0.0003995231236331165f, 0.0003033203829545528f, 0.0002100782585330308f, 0.0001327334903180599f, 7.650675252079964e-005f, 4.022897701361217e-005f, 1.929736572492402e-005f, 8.444558261544444e-006f,
3.695352233989979e-006f, 8.444558261544444e-006f, 1.760426494001877e-005f, 3.34794785885606e-005f, 5.808438800158911e-005f, 9.193058212986216e-005f, 0.0001327334757661447f, 0.0001748319627949968f, 0.0002100782439811155f, 0.0002302826324012131f, 0.0002302826324012131f, 0.0002100782439811155f, 0.0001748319627949968f, 0.0001327334757661447f, 9.193058212986216e-005f, 5.808438800158911e-005f, 3.34794785885606e-005f, 1.760426494001877e-005f, 8.444558261544444e-006f, 3.695352233989979e-006f
};
struct WinReader
{
typedef uchar elem_type;
__device__ WinReader(cudev::TexturePtr<uchar> tex_) : tex(tex_) {};
__device__ __forceinline__ uchar operator ()(int i, int j) const
{
float pixel_x = centerX + (win_offset + j) * cos_dir + (win_offset + i) * sin_dir;
float pixel_y = centerY - (win_offset + j) * sin_dir + (win_offset + i) * cos_dir;
return tex(pixel_y, pixel_x);
}
float centerX;
float centerY;
float win_offset;
float cos_dir;
float sin_dir;
int width;
int height;
cudev::TexturePtr<uchar> tex;
};
__device__ void calc_dx_dy(cudev::TexturePtr<uchar> tex, const float* featureX, const float* featureY, const float* featureSize, const float* featureDir,
float& dx, float& dy)
{
__shared__ float s_PATCH[PATCH_SZ + 1][PATCH_SZ + 1];
dx = dy = 0.0f;
WinReader win(tex);
win.centerX = featureX[blockIdx.x];
win.centerY = featureY[blockIdx.x];
// The sampling intervals and wavelet sized for selecting an orientation
// and building the keypoint descriptor are defined relative to 's'
const float s = featureSize[blockIdx.x] * 1.2f / 9.0f;
// Extract a window of pixels around the keypoint of size 20s
const int win_size = (int)((PATCH_SZ + 1) * s);
win.width = win.height = win_size;
// Nearest neighbour version (faster)
win.win_offset = -(win_size - 1.0f) / 2.0f;
float descriptor_dir = 360.0f - featureDir[blockIdx.x];
if (::fabsf(descriptor_dir - 360.f) < numeric_limits<float>::epsilon())
descriptor_dir = 0.f;
descriptor_dir *= CV_PI_F / 180.0f;
sincosf(descriptor_dir, &win.sin_dir, &win.cos_dir);
const int tid = threadIdx.y * blockDim.x + threadIdx.x;
const int xLoadInd = tid % (PATCH_SZ + 1);
const int yLoadInd = tid / (PATCH_SZ + 1);
if (yLoadInd < (PATCH_SZ + 1))
{
if (s > 1)
{
AreaFilter<WinReader> filter(win, s, s);
s_PATCH[yLoadInd][xLoadInd] = filter(yLoadInd, xLoadInd);
}
else
{
LinearFilter<WinReader> filter(win);
s_PATCH[yLoadInd][xLoadInd] = filter(yLoadInd * s, xLoadInd * s);
}
}
__syncthreads();
const int xPatchInd = threadIdx.x % 5;
const int yPatchInd = threadIdx.x / 5;
if (yPatchInd < 5)
{
const int xBlockInd = threadIdx.y % 4;
const int yBlockInd = threadIdx.y / 4;
const int xInd = xBlockInd * 5 + xPatchInd;
const int yInd = yBlockInd * 5 + yPatchInd;
const float dw = c_DW[yInd * PATCH_SZ + xInd];
dx = (s_PATCH[yInd ][xInd + 1] - s_PATCH[yInd][xInd] + s_PATCH[yInd + 1][xInd + 1] - s_PATCH[yInd + 1][xInd ]) * dw;
dy = (s_PATCH[yInd + 1][xInd ] - s_PATCH[yInd][xInd] + s_PATCH[yInd + 1][xInd + 1] - s_PATCH[yInd ][xInd + 1]) * dw;
}
}
__global__ void compute_descriptors_64(cudev::TexturePtr<uchar> texImg, PtrStep<float4> descriptors, const float* featureX, const float* featureY, const float* featureSize, const float* featureDir)
{
__shared__ float smem[32 * 16];
float* sRow = smem + threadIdx.y * 32;
float dx, dy;
calc_dx_dy(texImg, featureX, featureY, featureSize, featureDir, dx, dy);
float dxabs = ::fabsf(dx);
float dyabs = ::fabsf(dy);
plus<float> op;
reduce<32>(sRow, dx, threadIdx.x, op);
reduce<32>(sRow, dy, threadIdx.x, op);
reduce<32>(sRow, dxabs, threadIdx.x, op);
reduce<32>(sRow, dyabs, threadIdx.x, op);
float4* descriptors_block = descriptors.ptr(blockIdx.x) + threadIdx.y;
// write dx, dy, |dx|, |dy|
if (threadIdx.x == 0)
*descriptors_block = make_float4(dx, dy, dxabs, dyabs);
}
__global__ void compute_descriptors_128(cudev::TexturePtr<uchar> texImg, PtrStep<float4> descriptors, const float* featureX, const float* featureY, const float* featureSize, const float* featureDir)
{
__shared__ float smem[32 * 16];
float* sRow = smem + threadIdx.y * 32;
float dx, dy;
calc_dx_dy(texImg, featureX, featureY, featureSize, featureDir, dx, dy);
float4* descriptors_block = descriptors.ptr(blockIdx.x) + threadIdx.y * 2;
plus<float> op;
float d1 = 0.0f;
float d2 = 0.0f;
float abs1 = 0.0f;
float abs2 = 0.0f;
if (dy >= 0)
{
d1 = dx;
abs1 = ::fabsf(dx);
}
else
{
d2 = dx;
abs2 = ::fabsf(dx);
}
reduce<32>(sRow, d1, threadIdx.x, op);
reduce<32>(sRow, d2, threadIdx.x, op);
reduce<32>(sRow, abs1, threadIdx.x, op);
reduce<32>(sRow, abs2, threadIdx.x, op);
// write dx (dy >= 0), |dx| (dy >= 0), dx (dy < 0), |dx| (dy < 0)
if (threadIdx.x == 0)
descriptors_block[0] = make_float4(d1, abs1, d2, abs2);
if (dx >= 0)
{
d1 = dy;
abs1 = ::fabsf(dy);
d2 = 0.0f;
abs2 = 0.0f;
}
else
{
d1 = 0.0f;
abs1 = 0.0f;
d2 = dy;
abs2 = ::fabsf(dy);
}
reduce<32>(sRow, d1, threadIdx.x, op);
reduce<32>(sRow, d2, threadIdx.x, op);
reduce<32>(sRow, abs1, threadIdx.x, op);
reduce<32>(sRow, abs2, threadIdx.x, op);
// write dy (dx >= 0), |dy| (dx >= 0), dy (dx < 0), |dy| (dx < 0)
if (threadIdx.x == 0)
descriptors_block[1] = make_float4(d1, abs1, d2, abs2);
}
template <int BLOCK_DIM_X> __global__ void normalize_descriptors(PtrStepf descriptors)
{
__shared__ float smem[BLOCK_DIM_X];
__shared__ float s_len;
// no need for thread ID
float* descriptor_base = descriptors.ptr(blockIdx.x);
// read in the unnormalized descriptor values (squared)
const float val = descriptor_base[threadIdx.x];
float len = val * val;
reduce<BLOCK_DIM_X>(smem, len, threadIdx.x, plus<float>());
if (threadIdx.x == 0)
s_len = ::sqrtf(len);
__syncthreads();
// normalize and store in output
descriptor_base[threadIdx.x] = val / s_len;
}
void compute_descriptors_gpu(const PtrStepSzb& img, PtrStepSz<float4> descriptors, const float* featureX, const float* featureY, const float* featureSize, const float* featureDir, int nFeatures)
{
// compute unnormalized descriptors, then normalize them - odd indexing since grid must be 2D
cudev::Texture<unsigned char> texImg(img);
if (descriptors.cols == 64)
{
compute_descriptors_64<<<nFeatures, dim3(32, 16)>>>(texImg, descriptors, featureX, featureY, featureSize, featureDir);
cudaSafeCall( cudaGetLastError() );
cudaSafeCall( cudaDeviceSynchronize() );
normalize_descriptors<64><<<nFeatures, 64>>>((PtrStepSzf) descriptors);
cudaSafeCall( cudaGetLastError() );
cudaSafeCall( cudaDeviceSynchronize() );
}
else
{
compute_descriptors_128<<<nFeatures, dim3(32, 16)>>>(texImg, descriptors, featureX, featureY, featureSize, featureDir);
cudaSafeCall( cudaGetLastError() );
cudaSafeCall( cudaDeviceSynchronize() );
normalize_descriptors<128><<<nFeatures, 128>>>((PtrStepSzf) descriptors);
cudaSafeCall( cudaGetLastError() );
cudaSafeCall( cudaDeviceSynchronize() );
}
}
} // namespace surf
}}} // namespace cv { namespace cuda { namespace cudev
#endif // HAVE_OPENCV_CUDAARITHM
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,22 @@
// 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"
namespace cv {
namespace xfeatures2d {
Elliptic_KeyPoint::Elliptic_KeyPoint(Point2f _pt, float _angle, Size _axes, float _size, float _si) :
KeyPoint(_pt,_size,_angle), axes(_axes), si(_si) {
}
Elliptic_KeyPoint::Elliptic_KeyPoint(){
}
Elliptic_KeyPoint::~Elliptic_KeyPoint() {
}
}
}
+480
View File
@@ -0,0 +1,480 @@
// 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 <opencv2/xfeatures2d.hpp>
#ifndef VERIFY_CORNERS
#define VERIFY_CORNERS 0
#endif
namespace {
using namespace cv;
#if VERIFY_CORNERS
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 patternSize>
int cornerScore(const uchar* ptr, const int pixel[], int threshold);
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_SSE2
__m128i q0 = _mm_set1_epi16(-1000), q1 = _mm_set1_epi16(1000);
for( k = 0; k < 16; k += 8 )
{
__m128i v0 = _mm_loadu_si128((__m128i*)(d+k+1));
__m128i v1 = _mm_loadu_si128((__m128i*)(d+k+2));
__m128i a = _mm_min_epi16(v0, v1);
__m128i b = _mm_max_epi16(v0, v1);
v0 = _mm_loadu_si128((__m128i*)(d+k+3));
a = _mm_min_epi16(a, v0);
b = _mm_max_epi16(b, v0);
v0 = _mm_loadu_si128((__m128i*)(d+k+4));
a = _mm_min_epi16(a, v0);
b = _mm_max_epi16(b, v0);
v0 = _mm_loadu_si128((__m128i*)(d+k+5));
a = _mm_min_epi16(a, v0);
b = _mm_max_epi16(b, v0);
v0 = _mm_loadu_si128((__m128i*)(d+k+6));
a = _mm_min_epi16(a, v0);
b = _mm_max_epi16(b, v0);
v0 = _mm_loadu_si128((__m128i*)(d+k+7));
a = _mm_min_epi16(a, v0);
b = _mm_max_epi16(b, v0);
v0 = _mm_loadu_si128((__m128i*)(d+k+8));
a = _mm_min_epi16(a, v0);
b = _mm_max_epi16(b, v0);
v0 = _mm_loadu_si128((__m128i*)(d+k));
q0 = _mm_max_epi16(q0, _mm_min_epi16(a, v0));
q1 = _mm_min_epi16(q1, _mm_max_epi16(b, v0));
v0 = _mm_loadu_si128((__m128i*)(d+k+9));
q0 = _mm_max_epi16(q0, _mm_min_epi16(a, v0));
q1 = _mm_min_epi16(q1, _mm_max_epi16(b, v0));
}
q0 = _mm_max_epi16(q0, _mm_sub_epi16(_mm_setzero_si128(), q1));
q0 = _mm_max_epi16(q0, _mm_unpackhi_epi64(q0, q0));
q0 = _mm_max_epi16(q0, _mm_srli_si128(q0, 4));
q0 = _mm_max_epi16(q0, _mm_srli_si128(q0, 2));
threshold = (short)_mm_cvtsi128_si32(q0) - 1;
#else
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;
#endif
#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_SSE2
for( k = 0; k < 4; k++ )
d[N+k] = d[k];
#endif
#if CV_SSE2
__m128i q0 = _mm_set1_epi16(-1000), q1 = _mm_set1_epi16(1000);
for( k = 0; k < 16; k += 8 )
{
__m128i v0 = _mm_loadu_si128((__m128i*)(d+k+1));
__m128i v1 = _mm_loadu_si128((__m128i*)(d+k+2));
__m128i a = _mm_min_epi16(v0, v1);
__m128i b = _mm_max_epi16(v0, v1);
v0 = _mm_loadu_si128((__m128i*)(d+k+3));
a = _mm_min_epi16(a, v0);
b = _mm_max_epi16(b, v0);
v0 = _mm_loadu_si128((__m128i*)(d+k+4));
a = _mm_min_epi16(a, v0);
b = _mm_max_epi16(b, v0);
v0 = _mm_loadu_si128((__m128i*)(d+k+5));
a = _mm_min_epi16(a, v0);
b = _mm_max_epi16(b, v0);
v0 = _mm_loadu_si128((__m128i*)(d+k+6));
a = _mm_min_epi16(a, v0);
b = _mm_max_epi16(b, v0);
v0 = _mm_loadu_si128((__m128i*)(d+k));
q0 = _mm_max_epi16(q0, _mm_min_epi16(a, v0));
q1 = _mm_min_epi16(q1, _mm_max_epi16(b, v0));
v0 = _mm_loadu_si128((__m128i*)(d+k+7));
q0 = _mm_max_epi16(q0, _mm_min_epi16(a, v0));
q1 = _mm_min_epi16(q1, _mm_max_epi16(b, v0));
}
q0 = _mm_max_epi16(q0, _mm_sub_epi16(_mm_setzero_si128(), q1));
q0 = _mm_max_epi16(q0, _mm_unpackhi_epi64(q0, q0));
q0 = _mm_max_epi16(q0, _mm_srli_si128(q0, 4));
q0 = _mm_max_epi16(q0, _mm_srli_si128(q0, 2));
threshold = (short)_mm_cvtsi128_si32(q0) - 1;
#else
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;
#endif
#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_SSE2
__m128i v0 = _mm_loadu_si128((__m128i*)(d+1));
__m128i v1 = _mm_loadu_si128((__m128i*)(d+2));
__m128i a = _mm_min_epi16(v0, v1);
__m128i b = _mm_max_epi16(v0, v1);
v0 = _mm_loadu_si128((__m128i*)(d+3));
a = _mm_min_epi16(a, v0);
b = _mm_max_epi16(b, v0);
v0 = _mm_loadu_si128((__m128i*)(d+4));
a = _mm_min_epi16(a, v0);
b = _mm_max_epi16(b, v0);
v0 = _mm_loadu_si128((__m128i*)(d));
__m128i q0 = _mm_min_epi16(a, v0);
__m128i q1 = _mm_max_epi16(b, v0);
v0 = _mm_loadu_si128((__m128i*)(d+5));
q0 = _mm_max_epi16(q0, _mm_min_epi16(a, v0));
q1 = _mm_min_epi16(q1, _mm_max_epi16(b, v0));
q0 = _mm_max_epi16(q0, _mm_sub_epi16(_mm_setzero_si128(), q1));
q0 = _mm_max_epi16(q0, _mm_unpackhi_epi64(q0, q0));
q0 = _mm_max_epi16(q0, _mm_srli_si128(q0, 4));
q0 = _mm_max_epi16(q0, _mm_srli_si128(q0, 2));
threshold = (short)_mm_cvtsi128_si32(q0) - 1;
#else
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;
#endif
#if VERIFY_CORNERS
testCorner(ptr, pixel, K, N, threshold);
#endif
return threshold;
}
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];
}
template<int patternSize>
void FASTForPointSet_t( InputArray image, std::vector<KeyPoint>& keypoints, int threshold, bool nonmaxSuppression ) {
Mat img = image.getMat();
const int K = patternSize/2, N = patternSize + K + 1;
int i, k, pixel[25];
makeOffsets(pixel, (int)img.step, patternSize);
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);
AutoBuffer<uchar> _buf((img.cols+16)*3*(sizeof(int) + sizeof(uchar)) + 128);
uchar* buf[3];
buf[0] = _buf.data(); buf[1] = buf[0] + img.cols; buf[2] = buf[1] + img.cols;
int* cpbuf[3];
cpbuf[0] = (int*)alignPtr(buf[2] + img.cols, sizeof(int)) + 1;
cpbuf[1] = cpbuf[0] + img.cols + 1;
cpbuf[2] = cpbuf[1] + img.cols + 1;
memset(buf[0], 0, img.cols*3);
// Calculate threshold for the keypoints
for (size_t keyPointIdx=0; keyPointIdx < keypoints.size(); keyPointIdx++) {
// Set response to -1:
// All keypoints with response <= 0 will be removed afterwards
keypoints[keyPointIdx].response = -1;
// Poiter to keyPoint in image
Point keyPoint = keypoints[keyPointIdx].pt;
const uchar* ptr = img.ptr<uchar>(keyPoint.y, keyPoint.x);
// value of the pixel at certain position
int v = ptr[0];
// Initialize Lookup table
// If k=v --> tab[k] is at the center of the thrshold table
// The threshold table is made as follows:
// -255 -threshold 0 +threshold 255
// 111111111111111111|0000000000000|0000000000000|222222222222222
const uchar* tab = &threshold_tab[0] - v + 255;
// Calculate the fast value
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]]];
// For at least half pixels darker than v count the number
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 )
{
// Calculate score
keypoints[keyPointIdx].response = (uchar)cornerScore<patternSize>(ptr, pixel, threshold);
// Non Maxima Supression I
if (nonmaxSuppression && keyPointIdx>0 && keypoints[keyPointIdx-1].response < keypoints[keyPointIdx].response) {
keypoints[keyPointIdx-1].response = -1;
}
break;
}
}
else
count = 0;
}
}
// For at least half pixels brighter than v count the number
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 )
{
// Calculate score
keypoints[keyPointIdx].response = (uchar)cornerScore<patternSize>(ptr, pixel, threshold);
// Non Maxima Suppression I
if (nonmaxSuppression && keyPointIdx>0 &&keypoints[keyPointIdx-1].response < keypoints[keyPointIdx].response) {
keypoints[keyPointIdx-1].response = -1;
}
break;
}
}
else
count = 0;
}
}
}
// Remove unused Keypoints
size_t maxKeypointSize = keypoints.size();
for (size_t keyPointIdx=maxKeypointSize; keyPointIdx > 0;) {
keyPointIdx--;
if (keypoints[keyPointIdx].response <= 0) {
keypoints.erase(keypoints.begin() + keyPointIdx);
} else if (nonmaxSuppression && keyPointIdx>0 && keypoints[keyPointIdx-1].response > keypoints[keyPointIdx].response) {
// Non Maxima Suppression II
keypoints.erase(keypoints.begin() + keyPointIdx);
}
}
}
}
namespace cv {
namespace xfeatures2d {
void FASTForPointSet(InputArray _img, std::vector<KeyPoint>& keypoints, int threshold, bool nonmax_suppression, FastFeatureDetector::DetectorType type)
{
if (keypoints.empty()) {
FAST(_img, keypoints, threshold, nonmax_suppression, type);
return;
}
switch(type) {
case FastFeatureDetector::TYPE_5_8:
FASTForPointSet_t<8>(_img, keypoints, threshold, nonmax_suppression);
break;
case FastFeatureDetector::TYPE_7_12:
FASTForPointSet_t<12>(_img, keypoints, threshold, nonmax_suppression);
break;
case FastFeatureDetector::TYPE_9_16:
FASTForPointSet_t<16>(_img, keypoints, threshold, nonmax_suppression);
break;
}
}
}
}
+888
View File
@@ -0,0 +1,888 @@
// freak.cpp
//
// Copyright (C) 2011-2012 Signal processing laboratory 2, EPFL,
// Kirell Benzi (kirell.benzi@epfl.ch),
// Raphael Ortiz (raphael.ortiz@a3.epfl.ch)
// Alexandre Alahi (alexandre.alahi@epfl.ch)
// and Pierre Vandergheynst (pierre.vandergheynst@epfl.ch)
//
// 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.
#include "precomp.hpp"
#include <fstream>
#include <stdlib.h>
#include <algorithm>
#include <iostream>
#include <bitset>
#include <sstream>
#include <algorithm>
#include <iomanip>
#include <string.h>
namespace cv
{
namespace xfeatures2d
{
/*!
FREAK implementation
*/
class FREAK_Impl CV_FINAL : public FREAK
{
public:
/** Constructor
* @param orientationNormalized enable orientation normalization
* @param scaleNormalized enable scale normalization
* @param patternScale scaling of the description pattern
* @param nbOctave number of octaves covered by the detected keypoints
* @param selectedPairs (optional) user defined selected pairs
*/
explicit FREAK_Impl( bool orientationNormalized = true,
bool scaleNormalized = true,
float patternScale = 22.0f,
int nOctaves = 4,
const std::vector<int>& selectedPairs = std::vector<int>());
virtual ~FREAK_Impl();
void read( const FileNode& fn) CV_OVERRIDE;
void write( FileStorage& fs) const CV_OVERRIDE;
void setOrientationNormalized(bool _orientationNormalized) CV_OVERRIDE {orientationNormalized = _orientationNormalized;}
bool getOrientationNormalized() const CV_OVERRIDE { return orientationNormalized; }
void setScaleNormalized(bool _scaleNormalized) CV_OVERRIDE {scaleNormalized = _scaleNormalized;}
bool getScaleNormalized() const CV_OVERRIDE { return scaleNormalized; }
void setPatternScale(double _patternScale) CV_OVERRIDE {patternScale = _patternScale;}
double getPatternScale() const CV_OVERRIDE { return patternScale; }
void setNOctaves(int _nOctaves) CV_OVERRIDE {nOctaves = _nOctaves;}
int getNOctaves() const CV_OVERRIDE { return nOctaves; }
/** returns the descriptor length in bytes */
virtual int descriptorSize() const CV_OVERRIDE;
/** returns the descriptor type */
virtual int descriptorType() const CV_OVERRIDE;
/** returns the default norm type */
virtual int defaultNorm() const CV_OVERRIDE;
/** select the 512 "best description pairs"
* @param images grayscale images set
* @param keypoints set of detected keypoints
* @param corrThresh correlation threshold
* @param verbose print construction information
* @return list of best pair indexes
*/
std::vector<int> selectPairs( const std::vector<Mat>& images, std::vector<std::vector<KeyPoint> >& keypoints,
const double corrThresh = 0.7, bool verbose = true );
virtual void compute( InputArray image, std::vector<KeyPoint>& keypoints, OutputArray descriptors ) CV_OVERRIDE;
protected:
void buildPattern();
template <typename imgType, typename iiType>
imgType meanIntensity( InputArray image, InputArray integral, const float kp_x, const float kp_y,
const unsigned int scale, const unsigned int rot, const unsigned int point );
template <typename srcMatType, typename iiMatType>
void computeDescriptors( InputArray image, std::vector<KeyPoint>& keypoints, OutputArray descriptors );
template <typename srcMatType>
void extractDescriptor(srcMatType *pointsValue, void ** ptr);
bool orientationNormalized; //true if the orientation is normalized, false otherwise
bool scaleNormalized; //true if the scale is normalized, false otherwise
double patternScale; //scaling of the pattern
int nOctaves; //number of octaves
bool extAll; // true if all pairs need to be extracted for pairs selection
double patternScale0;
int nOctaves0;
std::vector<int> selectedPairs0;
struct PatternPoint
{
float x; // x coordinate relative to center
float y; // x coordinate relative to center
float sigma; // Gaussian smoothing sigma
};
struct DescriptionPair
{
uchar i; // index of the first point
uchar j; // index of the second point
};
struct OrientationPair
{
uchar i; // index of the first point
uchar j; // index of the second point
int weight_dx; // dx/(norm_sq))*4096
int weight_dy; // dy/(norm_sq))*4096
};
std::vector<PatternPoint> patternLookup; // look-up table for the pattern points (position+sigma of all points at all scales and orientation)
int patternSizes[NB_SCALES]; // size of the pattern at a specific scale (used to check if a point is within image boundaries)
DescriptionPair descriptionPairs[NB_PAIRS];
OrientationPair orientationPairs[NB_ORIENPAIRS];
};
static const double FREAK_LOG2 = 0.693147180559945;
static const int FREAK_NB_ORIENTATION = 256;
static const int FREAK_NB_POINTS = 43;
static const int FREAK_SMALLEST_KP_SIZE = 7; // smallest size of keypoints
// default pairs
static const int FREAK_DEF_PAIRS[FREAK_Impl::NB_PAIRS] =
{
404,431,818,511,181,52,311,874,774,543,719,230,417,205,11,
560,149,265,39,306,165,857,250,8,61,15,55,717,44,412,
592,134,761,695,660,782,625,487,549,516,271,665,762,392,178,
796,773,31,672,845,548,794,677,654,241,831,225,238,849,83,
691,484,826,707,122,517,583,731,328,339,571,475,394,472,580,
381,137,93,380,327,619,729,808,218,213,459,141,806,341,95,
382,568,124,750,193,749,706,843,79,199,317,329,768,198,100,
466,613,78,562,783,689,136,838,94,142,164,679,219,419,366,
418,423,77,89,523,259,683,312,555,20,470,684,123,458,453,833,
72,113,253,108,313,25,153,648,411,607,618,128,305,232,301,84,
56,264,371,46,407,360,38,99,176,710,114,578,66,372,653,
129,359,424,159,821,10,323,393,5,340,891,9,790,47,0,175,346,
236,26,172,147,574,561,32,294,429,724,755,398,787,288,299,
769,565,767,722,757,224,465,723,498,467,235,127,802,446,233,
544,482,800,318,16,532,801,441,554,173,60,530,713,469,30,
212,630,899,170,266,799,88,49,512,399,23,500,107,524,90,
194,143,135,192,206,345,148,71,119,101,563,870,158,254,214,
276,464,332,725,188,385,24,476,40,231,620,171,258,67,109,
844,244,187,388,701,690,50,7,850,479,48,522,22,154,12,659,
736,655,577,737,830,811,174,21,237,335,353,234,53,270,62,
182,45,177,245,812,673,355,556,612,166,204,54,248,365,226,
242,452,700,685,573,14,842,481,468,781,564,416,179,405,35,
819,608,624,367,98,643,448,2,460,676,440,240,130,146,184,
185,430,65,807,377,82,121,708,239,310,138,596,730,575,477,
851,797,247,27,85,586,307,779,326,494,856,324,827,96,748,
13,397,125,688,702,92,293,716,277,140,112,4,80,855,839,1,
413,347,584,493,289,696,19,751,379,76,73,115,6,590,183,734,
197,483,217,344,330,400,186,243,587,220,780,200,793,246,824,
41,735,579,81,703,322,760,720,139,480,490,91,814,813,163,
152,488,763,263,425,410,576,120,319,668,150,160,302,491,515,
260,145,428,97,251,395,272,252,18,106,358,854,485,144,550,
131,133,378,68,102,104,58,361,275,209,697,582,338,742,589,
325,408,229,28,304,191,189,110,126,486,211,547,533,70,215,
670,249,36,581,389,605,331,518,442,822
};
// used to sort pairs during pairs selection
struct PairStat
{
double mean;
int idx;
};
struct sortMean
{
bool operator()( const PairStat& a, const PairStat& b ) const
{
return a.mean < b.mean;
}
};
void FREAK_Impl::buildPattern()
{
if( patternScale == patternScale0 && nOctaves == nOctaves0 && !patternLookup.empty() )
return;
nOctaves0 = nOctaves;
patternScale0 = patternScale;
patternLookup.resize(FREAK::NB_SCALES*FREAK_NB_ORIENTATION*FREAK_NB_POINTS);
double scaleStep = std::pow(2.0, (double)(nOctaves)/FREAK::NB_SCALES ); // 2 ^ ( (nOctaves-1) /nbScales)
double scalingFactor, alpha, beta, theta = 0;
// pattern definition, radius normalized to 1.0 (outer point position+sigma=1.0)
const int n[8] = {6,6,6,6,6,6,6,1}; // number of points on each concentric circle (from outer to inner)
const double bigR(2.0/3.0); // bigger radius
const double smallR(2.0/24.0); // smaller radius
const double unitSpace( (bigR-smallR)/21.0 ); // define spaces between concentric circles (from center to outer: 1,2,3,4,5,6)
// radii of the concentric cirles (from outer to inner)
const double radius[8] = {bigR, bigR-6*unitSpace, bigR-11*unitSpace, bigR-15*unitSpace, bigR-18*unitSpace, bigR-20*unitSpace, smallR, 0.0};
// sigma of pattern points (each group of 6 points on a concentric cirle has the same sigma)
const double sigma[8] = {radius[0]/2.0, radius[1]/2.0, radius[2]/2.0,
radius[3]/2.0, radius[4]/2.0, radius[5]/2.0,
radius[6]/2.0, radius[6]/2.0
};
// fill the lookup table
for( int scaleIdx=0; scaleIdx < FREAK::NB_SCALES; ++scaleIdx )
{
patternSizes[scaleIdx] = 0; // proper initialization
scalingFactor = std::pow(scaleStep,scaleIdx); //scale of the pattern, scaleStep ^ scaleIdx
for( int orientationIdx = 0; orientationIdx < FREAK_NB_ORIENTATION; ++orientationIdx )
{
theta = double(orientationIdx)* 2*CV_PI/double(FREAK_NB_ORIENTATION); // orientation of the pattern
int pointIdx = 0;
PatternPoint* patternLookupPtr = &patternLookup[0];
for( size_t i = 0; i < 8; ++i )
{
for( int k = 0 ; k < n[i]; ++k )
{
beta = CV_PI/n[i] * (i%2); // orientation offset so that groups of points on each circles are staggered
alpha = double(k)* 2*CV_PI/double(n[i])+beta+theta;
// add the point to the look-up table
PatternPoint& point = patternLookupPtr[ scaleIdx*FREAK_NB_ORIENTATION*FREAK_NB_POINTS+orientationIdx*FREAK_NB_POINTS+pointIdx ];
point.x = static_cast<float>(radius[i] * cos(alpha) * scalingFactor * patternScale);
point.y = static_cast<float>(radius[i] * sin(alpha) * scalingFactor * patternScale);
point.sigma = static_cast<float>(sigma[i] * scalingFactor * patternScale);
// adapt the sizeList if necessary
const int sizeMax = static_cast<int>(ceil((radius[i]+sigma[i])*scalingFactor*patternScale)) + 1;
if( patternSizes[scaleIdx] < sizeMax )
patternSizes[scaleIdx] = sizeMax;
++pointIdx;
}
}
}
}
// build the list of orientation pairs
orientationPairs[0].i=0; orientationPairs[0].j=3; orientationPairs[1].i=1; orientationPairs[1].j=4; orientationPairs[2].i=2; orientationPairs[2].j=5;
orientationPairs[3].i=0; orientationPairs[3].j=2; orientationPairs[4].i=1; orientationPairs[4].j=3; orientationPairs[5].i=2; orientationPairs[5].j=4;
orientationPairs[6].i=3; orientationPairs[6].j=5; orientationPairs[7].i=4; orientationPairs[7].j=0; orientationPairs[8].i=5; orientationPairs[8].j=1;
orientationPairs[9].i=6; orientationPairs[9].j=9; orientationPairs[10].i=7; orientationPairs[10].j=10; orientationPairs[11].i=8; orientationPairs[11].j=11;
orientationPairs[12].i=6; orientationPairs[12].j=8; orientationPairs[13].i=7; orientationPairs[13].j=9; orientationPairs[14].i=8; orientationPairs[14].j=10;
orientationPairs[15].i=9; orientationPairs[15].j=11; orientationPairs[16].i=10; orientationPairs[16].j=6; orientationPairs[17].i=11; orientationPairs[17].j=7;
orientationPairs[18].i=12; orientationPairs[18].j=15; orientationPairs[19].i=13; orientationPairs[19].j=16; orientationPairs[20].i=14; orientationPairs[20].j=17;
orientationPairs[21].i=12; orientationPairs[21].j=14; orientationPairs[22].i=13; orientationPairs[22].j=15; orientationPairs[23].i=14; orientationPairs[23].j=16;
orientationPairs[24].i=15; orientationPairs[24].j=17; orientationPairs[25].i=16; orientationPairs[25].j=12; orientationPairs[26].i=17; orientationPairs[26].j=13;
orientationPairs[27].i=18; orientationPairs[27].j=21; orientationPairs[28].i=19; orientationPairs[28].j=22; orientationPairs[29].i=20; orientationPairs[29].j=23;
orientationPairs[30].i=18; orientationPairs[30].j=20; orientationPairs[31].i=19; orientationPairs[31].j=21; orientationPairs[32].i=20; orientationPairs[32].j=22;
orientationPairs[33].i=21; orientationPairs[33].j=23; orientationPairs[34].i=22; orientationPairs[34].j=18; orientationPairs[35].i=23; orientationPairs[35].j=19;
orientationPairs[36].i=24; orientationPairs[36].j=27; orientationPairs[37].i=25; orientationPairs[37].j=28; orientationPairs[38].i=26; orientationPairs[38].j=29;
orientationPairs[39].i=30; orientationPairs[39].j=33; orientationPairs[40].i=31; orientationPairs[40].j=34; orientationPairs[41].i=32; orientationPairs[41].j=35;
orientationPairs[42].i=36; orientationPairs[42].j=39; orientationPairs[43].i=37; orientationPairs[43].j=40; orientationPairs[44].i=38; orientationPairs[44].j=41;
for( unsigned m = FREAK::NB_ORIENPAIRS; m--; )
{
const float dx = patternLookup[orientationPairs[m].i].x-patternLookup[orientationPairs[m].j].x;
const float dy = patternLookup[orientationPairs[m].i].y-patternLookup[orientationPairs[m].j].y;
const float norm_sq = (dx*dx+dy*dy);
orientationPairs[m].weight_dx = cvRound((dx/(norm_sq))*4096.0);
orientationPairs[m].weight_dy = cvRound((dy/(norm_sq))*4096.0);
}
// build the list of description pairs
std::vector<DescriptionPair> allPairs;
for( unsigned int i = 1; i < (unsigned int)FREAK_NB_POINTS; ++i )
{
// (generate all the pairs)
for( unsigned int j = 0; (unsigned int)j < i; ++j )
{
DescriptionPair pair = {(uchar)i,(uchar)j};
allPairs.push_back(pair);
}
}
// Input vector provided
if( !selectedPairs0.empty() )
{
if( (int)selectedPairs0.size() == FREAK::NB_PAIRS )
{
for( int i = 0; i < FREAK::NB_PAIRS; ++i )
descriptionPairs[i] = allPairs[selectedPairs0.at(i)];
}
else
{
CV_Error(Error::StsVecLengthErr, "Input vector does not match the required size");
}
}
else // default selected pairs
{
for( int i = 0; i < FREAK::NB_PAIRS; ++i )
descriptionPairs[i] = allPairs[FREAK_DEF_PAIRS[i]];
}
}
void FREAK_Impl::compute( InputArray _image, std::vector<KeyPoint>& keypoints, OutputArray _descriptors )
{
Mat image = _image.getMat();
if( image.empty() )
return;
if( keypoints.empty() )
return;
((FREAK_Impl*)this)->buildPattern();
// Convert to gray if not already
Mat grayImage;
if( image.channels() == 3 || image.channels() == 4 )
cvtColor(image, grayImage, COLOR_BGR2GRAY);
else {
CV_Assert(image.channels() == 1);
grayImage = image;
}
// Use 32-bit integers if we won't overflow in the integral image
if ((image.depth() == CV_8U || image.depth() == CV_8S) &&
(image.rows * image.cols) < 8388608 ) // 8388608 = 2 ^ (32 - 8(bit depth) - 1(sign bit))
{
// Create the integral image appropriate for our type & usage
if (image.depth() == CV_8U)
computeDescriptors<uchar, int>(grayImage, keypoints, _descriptors);
else if (image.depth() == CV_8S)
computeDescriptors<char, int>(grayImage, keypoints, _descriptors);
else
CV_Error( Error::StsUnsupportedFormat, "" );
} else {
// Create the integral image appropriate for our type & usage
if ( image.depth() == CV_8U )
computeDescriptors<uchar, double>(grayImage, keypoints, _descriptors);
else if ( image.depth() == CV_8S )
computeDescriptors<char, double>(grayImage, keypoints, _descriptors);
else if ( image.depth() == CV_16U )
computeDescriptors<ushort, double>(grayImage, keypoints, _descriptors);
else if ( image.depth() == CV_16S )
computeDescriptors<short, double>(grayImage, keypoints, _descriptors);
else
CV_Error( Error::StsUnsupportedFormat, "" );
}
}
template <typename srcMatType>
void FREAK_Impl::extractDescriptor(srcMatType *pointsValue, void ** ptr)
{
std::bitset<FREAK::NB_PAIRS>** ptrScalar = (std::bitset<FREAK::NB_PAIRS>**) ptr;
// extracting descriptor preserving the order of SSE version
int cnt = 0;
for( int n = 7; n < FREAK::NB_PAIRS; n += 128)
{
for( int m = 8; m--; )
{
int nm = n-m;
for(int kk = nm+15*8; kk >= nm; kk-=8, ++cnt)
{
(*ptrScalar)->set(kk, pointsValue[descriptionPairs[cnt].i] >= pointsValue[descriptionPairs[cnt].j]);
}
}
}
--(*ptrScalar);
}
#if CV_SSE2
template <>
void FREAK_Impl::extractDescriptor(uchar *pointsValue, void ** ptr)
{
__m128i** ptrSSE = (__m128i**) ptr;
// note that comparisons order is modified in each block (but first 128 comparisons remain globally the same-->does not affect the 128,384 bits segmanted matching strategy)
int cnt = 0;
for( int n = FREAK::NB_PAIRS/128; n-- ; )
{
__m128i result128 = _mm_setzero_si128();
for( int m = 128/16; m--; cnt += 16 )
{
__m128i operand1 = _mm_set_epi8(pointsValue[descriptionPairs[cnt+0].i],
pointsValue[descriptionPairs[cnt+1].i],
pointsValue[descriptionPairs[cnt+2].i],
pointsValue[descriptionPairs[cnt+3].i],
pointsValue[descriptionPairs[cnt+4].i],
pointsValue[descriptionPairs[cnt+5].i],
pointsValue[descriptionPairs[cnt+6].i],
pointsValue[descriptionPairs[cnt+7].i],
pointsValue[descriptionPairs[cnt+8].i],
pointsValue[descriptionPairs[cnt+9].i],
pointsValue[descriptionPairs[cnt+10].i],
pointsValue[descriptionPairs[cnt+11].i],
pointsValue[descriptionPairs[cnt+12].i],
pointsValue[descriptionPairs[cnt+13].i],
pointsValue[descriptionPairs[cnt+14].i],
pointsValue[descriptionPairs[cnt+15].i]);
__m128i operand2 = _mm_set_epi8(pointsValue[descriptionPairs[cnt+0].j],
pointsValue[descriptionPairs[cnt+1].j],
pointsValue[descriptionPairs[cnt+2].j],
pointsValue[descriptionPairs[cnt+3].j],
pointsValue[descriptionPairs[cnt+4].j],
pointsValue[descriptionPairs[cnt+5].j],
pointsValue[descriptionPairs[cnt+6].j],
pointsValue[descriptionPairs[cnt+7].j],
pointsValue[descriptionPairs[cnt+8].j],
pointsValue[descriptionPairs[cnt+9].j],
pointsValue[descriptionPairs[cnt+10].j],
pointsValue[descriptionPairs[cnt+11].j],
pointsValue[descriptionPairs[cnt+12].j],
pointsValue[descriptionPairs[cnt+13].j],
pointsValue[descriptionPairs[cnt+14].j],
pointsValue[descriptionPairs[cnt+15].j]);
__m128i workReg = _mm_min_epu8(operand1, operand2); // emulated "not less than" for 8-bit UNSIGNED integers
workReg = _mm_cmpeq_epi8(workReg, operand2); // emulated "not less than" for 8-bit UNSIGNED integers
workReg = _mm_and_si128(_mm_set1_epi16(short(0x8080 >> m)), workReg); // merge the last 16 bits with the 128bits std::vector until full
result128 = _mm_or_si128(result128, workReg);
}
(**ptrSSE) = result128;
++(*ptrSSE);
}
(*ptrSSE) -= 8;
}
#endif
template <typename srcMatType, typename iiMatType>
void FREAK_Impl::computeDescriptors( InputArray _image, std::vector<KeyPoint>& keypoints, OutputArray _descriptors ){
Mat image = _image.getMat();
Mat imgIntegral;
integral(image, imgIntegral, DataType<iiMatType>::type);
std::vector<int> kpScaleIdx(keypoints.size()); // used to save pattern scale index corresponding to each keypoints
const std::vector<int>::iterator ScaleIdxBegin = kpScaleIdx.begin(); // used in std::vector erase function
const std::vector<cv::KeyPoint>::iterator kpBegin = keypoints.begin(); // used in std::vector erase function
const float sizeCst = static_cast<float>(FREAK::NB_SCALES/(FREAK_LOG2* nOctaves));
srcMatType pointsValue[FREAK_NB_POINTS];
int thetaIdx = 0;
int direction0;
int direction1;
// compute the scale index corresponding to the keypoint size and remove keypoints close to the border
if( scaleNormalized )
{
for( size_t k = keypoints.size(); k--; )
{
//Is k non-zero? If so, decrement it and continue"
kpScaleIdx[k] = std::max( (int)(std::log(keypoints[k].size/FREAK_SMALLEST_KP_SIZE)*sizeCst+0.5) ,0);
if( kpScaleIdx[k] >= FREAK::NB_SCALES )
kpScaleIdx[k] = FREAK::NB_SCALES-1;
if( keypoints[k].pt.x <= patternSizes[kpScaleIdx[k]] || //check if the description at this specific position and scale fits inside the image
keypoints[k].pt.y <= patternSizes[kpScaleIdx[k]] ||
keypoints[k].pt.x >= image.cols-patternSizes[kpScaleIdx[k]] ||
keypoints[k].pt.y >= image.rows-patternSizes[kpScaleIdx[k]]
)
{
keypoints.erase(kpBegin+k);
kpScaleIdx.erase(ScaleIdxBegin+k);
}
}
}
else
{
const int scIdx = std::max( cvRound(1.0986122886681*sizeCst) ,0);
for( size_t k = keypoints.size(); k--; )
{
kpScaleIdx[k] = scIdx; // equivalent to the formule when the scale is normalized with a constant size of keypoints[k].size=3*SMALLEST_KP_SIZE
if( kpScaleIdx[k] >= FREAK::NB_SCALES )
{
kpScaleIdx[k] = FREAK::NB_SCALES-1;
}
if( keypoints[k].pt.x <= patternSizes[kpScaleIdx[k]] ||
keypoints[k].pt.y <= patternSizes[kpScaleIdx[k]] ||
keypoints[k].pt.x >= image.cols-patternSizes[kpScaleIdx[k]] ||
keypoints[k].pt.y >= image.rows-patternSizes[kpScaleIdx[k]]
)
{
keypoints.erase(kpBegin+k);
kpScaleIdx.erase(ScaleIdxBegin+k);
}
}
}
// allocate descriptor memory, estimate orientations, extract descriptors
if( !extAll )
{
// extract the best comparisons only
_descriptors.create((int)keypoints.size(), FREAK::NB_PAIRS/8, CV_8U);
_descriptors.setTo(Scalar::all(0));
Mat descriptors = _descriptors.getMat();
void *ptr = descriptors.data+(keypoints.size()-1)*descriptors.step[0];
for( size_t k = keypoints.size(); k--; ) {
// estimate orientation (gradient)
if( !orientationNormalized )
{
thetaIdx = 0; // assign 0° to all keypoints
keypoints[k].angle = 0.0;
}
else
{
// get the points intensity value in the un-rotated pattern
for( int i = FREAK_NB_POINTS; i--; ) {
pointsValue[i] = meanIntensity<srcMatType, iiMatType>(image, imgIntegral,
keypoints[k].pt.x, keypoints[k].pt.y,
kpScaleIdx[k], 0, i);
}
direction0 = 0;
direction1 = 0;
for( int m = 45; m--; )
{
//iterate through the orientation pairs
const int delta = (pointsValue[ orientationPairs[m].i ]-pointsValue[ orientationPairs[m].j ]);
direction0 += delta*(orientationPairs[m].weight_dx)/2048;
direction1 += delta*(orientationPairs[m].weight_dy)/2048;
}
keypoints[k].angle = static_cast<float>(atan2((float)direction1,(float)direction0)*(180.0/CV_PI));//estimate orientation
thetaIdx = cvRound(FREAK_NB_ORIENTATION*keypoints[k].angle*(1/360.0));
if( thetaIdx < 0 )
thetaIdx += FREAK_NB_ORIENTATION;
if( thetaIdx >= FREAK_NB_ORIENTATION )
thetaIdx -= FREAK_NB_ORIENTATION;
}
// extract descriptor at the computed orientation
for( int i = FREAK_NB_POINTS; i--; ) {
pointsValue[i] = meanIntensity<srcMatType, iiMatType>(image, imgIntegral,
keypoints[k].pt.x, keypoints[k].pt.y,
kpScaleIdx[k], thetaIdx, i);
}
// Extract descriptor
extractDescriptor<srcMatType>(pointsValue, &ptr);
}
}
else // extract all possible comparisons for selection
{
_descriptors.create((int)keypoints.size(), 128, CV_8U);
_descriptors.setTo(Scalar::all(0));
Mat descriptors = _descriptors.getMat();
std::bitset<1024>* ptr = (std::bitset<1024>*) (descriptors.data+(keypoints.size()-1)*descriptors.step[0]);
for( size_t k = keypoints.size(); k--; )
{
//estimate orientation (gradient)
if( !orientationNormalized )
{
thetaIdx = 0;//assign 0° to all keypoints
keypoints[k].angle = 0.0;
}
else
{
//get the points intensity value in the un-rotated pattern
for( int i = FREAK_NB_POINTS;i--; )
pointsValue[i] = meanIntensity<srcMatType, iiMatType>(image, imgIntegral,
keypoints[k].pt.x,keypoints[k].pt.y,
kpScaleIdx[k], 0, i);
direction0 = 0;
direction1 = 0;
for( int m = 45; m--; )
{
//iterate through the orientation pairs
const int delta = (pointsValue[ orientationPairs[m].i ]-pointsValue[ orientationPairs[m].j ]);
direction0 += delta*(orientationPairs[m].weight_dx)/2048;
direction1 += delta*(orientationPairs[m].weight_dy)/2048;
}
keypoints[k].angle = static_cast<float>(atan2((float)direction1,(float)direction0)*(180.0/CV_PI)); //estimate orientation
thetaIdx = cvRound(FREAK_NB_ORIENTATION*keypoints[k].angle*(1/360.0));
if( thetaIdx < 0 )
thetaIdx += FREAK_NB_ORIENTATION;
if( thetaIdx >= FREAK_NB_ORIENTATION )
thetaIdx -= FREAK_NB_ORIENTATION;
}
// get the points intensity value in the rotated pattern
for( int i = FREAK_NB_POINTS; i--; ) {
pointsValue[i] = meanIntensity<srcMatType, iiMatType>(image, imgIntegral,
keypoints[k].pt.x, keypoints[k].pt.y,
kpScaleIdx[k], thetaIdx, i);
}
int cnt(0);
for( int i = 1; i < FREAK_NB_POINTS; ++i )
{
//(generate all the pairs)
for( int j = 0; j < i; ++j )
{
ptr->set(cnt, pointsValue[i] >= pointsValue[j] );
++cnt;
}
}
--ptr;
}
}
}
// simply take average on a square patch, not even gaussian approx
template <typename imgType, typename iiType>
imgType FREAK_Impl::meanIntensity( InputArray _image, InputArray _integral,
const float kp_x,
const float kp_y,
const unsigned int scale,
const unsigned int rot,
const unsigned int point)
{
Mat image = _image.getMat(), integral = _integral.getMat();
// get point position in image
const PatternPoint& FreakPoint = patternLookup[scale*FREAK_NB_ORIENTATION*FREAK_NB_POINTS + rot*FREAK_NB_POINTS + point];
const float xf = FreakPoint.x+kp_x;
const float yf = FreakPoint.y+kp_y;
const int x = int(xf);
const int y = int(yf);
// get the sigma:
const float radius = FreakPoint.sigma;
// calculate output:
if( radius < 0.5 )
{
// interpolation multipliers:
const int r_x = static_cast<int>((xf-x)*1024);
const int r_y = static_cast<int>((yf-y)*1024);
const int r_x_1 = (1024-r_x);
const int r_y_1 = (1024-r_y);
unsigned int ret_val;
// linear interpolation:
ret_val = r_x_1*r_y_1*int(image.at<imgType>(y , x ))
+ r_x *r_y_1*int(image.at<imgType>(y , x+1))
+ r_x_1*r_y *int(image.at<imgType>(y+1, x ))
+ r_x *r_y *int(image.at<imgType>(y+1, x+1));
//return the rounded mean
ret_val += 2 * 1024 * 1024;
return static_cast<imgType>(ret_val / (4 * 1024 * 1024));
}
// expected case:
// calculate borders
const int x_left = cvRound(xf-radius);
const int y_top = cvRound(yf-radius);
const int x_right = cvRound(xf+radius+1);//integral image is 1px wider
const int y_bottom = cvRound(yf+radius+1);//integral image is 1px higher
iiType ret_val;
ret_val = integral.at<iiType>(y_bottom,x_right);//bottom right corner
ret_val -= integral.at<iiType>(y_bottom,x_left);
ret_val += integral.at<iiType>(y_top,x_left);
ret_val -= integral.at<iiType>(y_top,x_right);
const int area = (x_right - x_left) * (y_bottom - y_top);
ret_val = (ret_val + area/2) / area;
//~ std::cout<<integral.step[1]<<std::endl;
return static_cast<imgType>(ret_val);
}
// pair selection algorithm from a set of training images and corresponding keypoints
std::vector<int> FREAK_Impl::selectPairs(const std::vector<Mat>& images
, std::vector<std::vector<KeyPoint> >& keypoints
, const double corrTresh
, bool verbose )
{
extAll = true;
// compute descriptors with all pairs
Mat descriptors;
if( verbose )
std::cout << "Number of images: " << images.size() << std::endl;
for( size_t i = 0;i < images.size(); ++i )
{
Mat descriptorsTmp;
compute(images[i],keypoints[i],descriptorsTmp);
descriptors.push_back(descriptorsTmp);
}
if( verbose )
std::cout << "number of keypoints: " << descriptors.rows << std::endl;
//descriptor in floating point format (each bit is a float)
Mat descriptorsFloat = Mat::zeros(descriptors.rows, 903, CV_32F);
std::bitset<1024>* ptr = (std::bitset<1024>*) (descriptors.data+(descriptors.rows-1)*descriptors.step[0]);
for( int m = descriptors.rows; m--; )
{
for( int n = 903; n--; )
{
if( ptr->test(n) == true )
descriptorsFloat.at<float>(m,n)=1.0f;
}
--ptr;
}
std::vector<PairStat> pairStat;
for( int n = 903; n--; )
{
// the higher the variance, the better --> mean = 0.5
PairStat tmp = { fabs( mean(descriptorsFloat.col(n))[0]-0.5 ) ,n};
pairStat.push_back(tmp);
}
std::sort( pairStat.begin(),pairStat.end(), sortMean() );
std::vector<PairStat> bestPairs;
for( int m = 0; m < 903; ++m )
{
if( verbose )
std::cout << m << ":" << bestPairs.size() << " " << std::flush;
double corrMax(0);
for( size_t n = 0; n < bestPairs.size(); ++n )
{
int idxA = bestPairs[n].idx;
int idxB = pairStat[m].idx;
double corr(0);
// compute correlation between 2 pairs
corr = fabs(compareHist(descriptorsFloat.col(idxA), descriptorsFloat.col(idxB), HISTCMP_CORREL));
if( corr > corrMax )
{
corrMax = corr;
if( corrMax >= corrTresh )
break;
}
}
if( corrMax < corrTresh/*0.7*/ )
bestPairs.push_back(pairStat[m]);
if( bestPairs.size() >= 512 )
{
if( verbose )
std::cout << m << std::endl;
break;
}
}
std::vector<int> idxBestPairs;
if( (int)bestPairs.size() >= FREAK::NB_PAIRS )
{
for( int i = 0; i < FREAK::NB_PAIRS; ++i )
idxBestPairs.push_back(bestPairs[i].idx);
}
else
{
if( verbose )
std::cout << "correlation threshold too small (restrictive)" << std::endl;
CV_Error(Error::StsError, "correlation threshold too small (restrictive)");
}
extAll = false;
return idxBestPairs;
}
/*
// create an image showing the brisk pattern
void FREAKImpl::drawPattern()
{
Mat pattern = Mat::zeros(1000, 1000, CV_8UC3) + Scalar(255,255,255);
int sFac = 500 / patternScale;
for( int n = 0; n < kNB_POINTS; ++n )
{
PatternPoint& pt = patternLookup[n];
circle(pattern, Point( pt.x*sFac,pt.y*sFac)+Point(500,500), pt.sigma*sFac, Scalar(0,0,255),2);
// rectangle(pattern, Point( (pt.x-pt.sigma)*sFac,(pt.y-pt.sigma)*sFac)+Point(500,500), Point( (pt.x+pt.sigma)*sFac,(pt.y+pt.sigma)*sFac)+Point(500,500), Scalar(0,0,255),2);
circle(pattern, Point( pt.x*sFac,pt.y*sFac)+Point(500,500), 1, Scalar(0,0,0),3);
std::ostringstream oss;
oss << n;
putText( pattern, oss.str(), Point( pt.x*sFac,pt.y*sFac)+Point(500,500), FONT_HERSHEY_SIMPLEX,0.5, Scalar(0,0,0), 1);
}
imshow( "FreakDescriptorExtractor pattern", pattern );
waitKey(0);
}
*/
// -------------------------------------------------
/* FREAK interface implementation */
FREAK_Impl::FREAK_Impl( bool _orientationNormalized, bool _scaleNormalized
, float _patternScale, int _nOctaves, const std::vector<int>& _selectedPairs )
: orientationNormalized(_orientationNormalized), scaleNormalized(_scaleNormalized),
patternScale(_patternScale), nOctaves(_nOctaves), extAll(false),
patternScale0(0.0), nOctaves0(0), selectedPairs0(_selectedPairs)
{
}
FREAK_Impl::~FREAK_Impl()
{
}
void FREAK_Impl::read( const FileNode& fn)
{
// if node is empty, keep previous value
if (!fn["orientationNormalized"].empty())
fn["orientationNormalized"] >> orientationNormalized;
if (!fn["scaleNormalized"].empty())
fn["scaleNormalized"] >> scaleNormalized;
if (!fn["patternScale"].empty())
fn["patternScale"] >> patternScale;
if (!fn["nOctaves"].empty())
fn["nOctaves"] >> nOctaves;
}
void FREAK_Impl::write( FileStorage& fs) const
{
if(fs.isOpened())
{
fs << "name" << getDefaultName();
fs << "orientationNormalized" << orientationNormalized;
fs << "scaleNormalized" << scaleNormalized;
fs << "patternScale" << patternScale;
fs << "nOctaves" << nOctaves;
}
}
int FREAK_Impl::descriptorSize() const
{
return FREAK::NB_PAIRS / 8; // descriptor length in bytes
}
int FREAK_Impl::descriptorType() const
{
return CV_8U;
}
int FREAK_Impl::defaultNorm() const
{
return NORM_HAMMING;
}
Ptr<FREAK> FREAK::create(bool orientationNormalized,
bool scaleNormalized,
float patternScale,
int nOctaves,
const std::vector<int>& selectedPairs)
{
return makePtr<FREAK_Impl>(orientationNormalized, scaleNormalized,
patternScale, nOctaves, selectedPairs);
}
String FREAK::getDefaultName() const
{
return (Feature2D::getDefaultName() + ".FREAK");
}
}
} // END NAMESPACE CV
+19
View File
@@ -0,0 +1,19 @@
// Code generated with '$ scripts/generate_code.py src/test_pairs.txt 16'
#define SMOOTHED(y,x) smoothedSum(sum, pt, y, x, use_orientation, R)
desc[0] = (uchar)(((SMOOTHED(-2, -1) < SMOOTHED(7, -1)) << 7) + ((SMOOTHED(-14, -1) < SMOOTHED(-3, 3)) << 6) + ((SMOOTHED(1, -2) < SMOOTHED(11, 2)) << 5) + ((SMOOTHED(1, 6) < SMOOTHED(-10, -7)) << 4) + ((SMOOTHED(13, 2) < SMOOTHED(-1, 0)) << 3) + ((SMOOTHED(-14, 5) < SMOOTHED(5, -3)) << 2) + ((SMOOTHED(-2, 8) < SMOOTHED(2, 4)) << 1) + ((SMOOTHED(-11, 8) < SMOOTHED(-15, 5)) << 0));
desc[1] = (uchar)(((SMOOTHED(-6, -23) < SMOOTHED(8, -9)) << 7) + ((SMOOTHED(-12, 6) < SMOOTHED(-10, 8)) << 6) + ((SMOOTHED(-3, -1) < SMOOTHED(8, 1)) << 5) + ((SMOOTHED(3, 6) < SMOOTHED(5, 6)) << 4) + ((SMOOTHED(-7, -6) < SMOOTHED(5, -5)) << 3) + ((SMOOTHED(22, -2) < SMOOTHED(-11, -8)) << 2) + ((SMOOTHED(14, 7) < SMOOTHED(8, 5)) << 1) + ((SMOOTHED(-1, 14) < SMOOTHED(-5, -14)) << 0));
desc[2] = (uchar)(((SMOOTHED(-14, 9) < SMOOTHED(2, 0)) << 7) + ((SMOOTHED(7, -3) < SMOOTHED(22, 6)) << 6) + ((SMOOTHED(-6, 6) < SMOOTHED(-8, -5)) << 5) + ((SMOOTHED(-5, 9) < SMOOTHED(7, -1)) << 4) + ((SMOOTHED(-3, -7) < SMOOTHED(-10, -18)) << 3) + ((SMOOTHED(4, -5) < SMOOTHED(0, 11)) << 2) + ((SMOOTHED(2, 3) < SMOOTHED(9, 10)) << 1) + ((SMOOTHED(-10, 3) < SMOOTHED(4, 9)) << 0));
desc[3] = (uchar)(((SMOOTHED(0, 12) < SMOOTHED(-3, 19)) << 7) + ((SMOOTHED(1, 15) < SMOOTHED(-11, -5)) << 6) + ((SMOOTHED(14, -1) < SMOOTHED(7, 8)) << 5) + ((SMOOTHED(7, -23) < SMOOTHED(-5, 5)) << 4) + ((SMOOTHED(0, -6) < SMOOTHED(-10, 17)) << 3) + ((SMOOTHED(13, -4) < SMOOTHED(-3, -4)) << 2) + ((SMOOTHED(-12, 1) < SMOOTHED(-12, 2)) << 1) + ((SMOOTHED(0, 8) < SMOOTHED(3, 22)) << 0));
desc[4] = (uchar)(((SMOOTHED(-13, 13) < SMOOTHED(3, -1)) << 7) + ((SMOOTHED(-16, 17) < SMOOTHED(6, 10)) << 6) + ((SMOOTHED(7, 15) < SMOOTHED(-5, 0)) << 5) + ((SMOOTHED(2, -12) < SMOOTHED(19, -2)) << 4) + ((SMOOTHED(3, -6) < SMOOTHED(-4, -15)) << 3) + ((SMOOTHED(8, 3) < SMOOTHED(0, 14)) << 2) + ((SMOOTHED(4, -11) < SMOOTHED(5, 5)) << 1) + ((SMOOTHED(11, -7) < SMOOTHED(7, 1)) << 0));
desc[5] = (uchar)(((SMOOTHED(6, 12) < SMOOTHED(21, 3)) << 7) + ((SMOOTHED(-3, 2) < SMOOTHED(14, 1)) << 6) + ((SMOOTHED(5, 1) < SMOOTHED(-5, 11)) << 5) + ((SMOOTHED(3, -17) < SMOOTHED(-6, 2)) << 4) + ((SMOOTHED(6, 8) < SMOOTHED(5, -10)) << 3) + ((SMOOTHED(-14, -2) < SMOOTHED(0, 4)) << 2) + ((SMOOTHED(5, -7) < SMOOTHED(-6, 5)) << 1) + ((SMOOTHED(10, 4) < SMOOTHED(4, -7)) << 0));
desc[6] = (uchar)(((SMOOTHED(22, 0) < SMOOTHED(7, -18)) << 7) + ((SMOOTHED(-1, -3) < SMOOTHED(0, 18)) << 6) + ((SMOOTHED(-4, 22) < SMOOTHED(-5, 3)) << 5) + ((SMOOTHED(1, -7) < SMOOTHED(2, -3)) << 4) + ((SMOOTHED(19, -20) < SMOOTHED(17, -2)) << 3) + ((SMOOTHED(3, -10) < SMOOTHED(-8, 24)) << 2) + ((SMOOTHED(-5, -14) < SMOOTHED(7, 5)) << 1) + ((SMOOTHED(-2, 12) < SMOOTHED(-4, -15)) << 0));
desc[7] = (uchar)(((SMOOTHED(4, 12) < SMOOTHED(0, -19)) << 7) + ((SMOOTHED(20, 13) < SMOOTHED(3, 5)) << 6) + ((SMOOTHED(-8, -12) < SMOOTHED(5, 0)) << 5) + ((SMOOTHED(-5, 6) < SMOOTHED(-7, -11)) << 4) + ((SMOOTHED(6, -11) < SMOOTHED(-3, -22)) << 3) + ((SMOOTHED(15, 4) < SMOOTHED(10, 1)) << 2) + ((SMOOTHED(-7, -4) < SMOOTHED(15, -6)) << 1) + ((SMOOTHED(5, 10) < SMOOTHED(0, 24)) << 0));
desc[8] = (uchar)(((SMOOTHED(3, 6) < SMOOTHED(22, -2)) << 7) + ((SMOOTHED(-13, 14) < SMOOTHED(4, -4)) << 6) + ((SMOOTHED(-13, 8) < SMOOTHED(-18, -22)) << 5) + ((SMOOTHED(-1, -1) < SMOOTHED(-7, 3)) << 4) + ((SMOOTHED(-19, -12) < SMOOTHED(4, 3)) << 3) + ((SMOOTHED(8, 10) < SMOOTHED(13, -2)) << 2) + ((SMOOTHED(-6, -1) < SMOOTHED(-6, -5)) << 1) + ((SMOOTHED(2, -21) < SMOOTHED(-3, 2)) << 0));
desc[9] = (uchar)(((SMOOTHED(4, -7) < SMOOTHED(0, 16)) << 7) + ((SMOOTHED(-6, -5) < SMOOTHED(-12, -1)) << 6) + ((SMOOTHED(1, -1) < SMOOTHED(9, 18)) << 5) + ((SMOOTHED(-7, 10) < SMOOTHED(-11, 6)) << 4) + ((SMOOTHED(4, 3) < SMOOTHED(19, -7)) << 3) + ((SMOOTHED(-18, 5) < SMOOTHED(-4, 5)) << 2) + ((SMOOTHED(4, 0) < SMOOTHED(-20, 4)) << 1) + ((SMOOTHED(7, -11) < SMOOTHED(18, 12)) << 0));
desc[10] = (uchar)(((SMOOTHED(-20, 17) < SMOOTHED(-18, 7)) << 7) + ((SMOOTHED(2, 15) < SMOOTHED(19, -11)) << 6) + ((SMOOTHED(-18, 6) < SMOOTHED(-7, 3)) << 5) + ((SMOOTHED(-4, 1) < SMOOTHED(-14, 13)) << 4) + ((SMOOTHED(17, 3) < SMOOTHED(2, -8)) << 3) + ((SMOOTHED(-7, 2) < SMOOTHED(1, 6)) << 2) + ((SMOOTHED(17, -9) < SMOOTHED(-2, 8)) << 1) + ((SMOOTHED(-8, -6) < SMOOTHED(-1, 12)) << 0));
desc[11] = (uchar)(((SMOOTHED(-2, 4) < SMOOTHED(-1, 6)) << 7) + ((SMOOTHED(-2, 7) < SMOOTHED(6, 8)) << 6) + ((SMOOTHED(-8, -1) < SMOOTHED(-7, -9)) << 5) + ((SMOOTHED(8, -9) < SMOOTHED(15, 0)) << 4) + ((SMOOTHED(0, 22) < SMOOTHED(-4, -15)) << 3) + ((SMOOTHED(-14, -1) < SMOOTHED(3, -2)) << 2) + ((SMOOTHED(-7, -4) < SMOOTHED(17, -7)) << 1) + ((SMOOTHED(-8, -2) < SMOOTHED(9, -4)) << 0));
desc[12] = (uchar)(((SMOOTHED(5, -7) < SMOOTHED(7, 7)) << 7) + ((SMOOTHED(-5, 13) < SMOOTHED(-8, 11)) << 6) + ((SMOOTHED(11, -4) < SMOOTHED(0, 8)) << 5) + ((SMOOTHED(5, -11) < SMOOTHED(-9, -6)) << 4) + ((SMOOTHED(2, -6) < SMOOTHED(3, -20)) << 3) + ((SMOOTHED(-6, 2) < SMOOTHED(6, 10)) << 2) + ((SMOOTHED(-6, -6) < SMOOTHED(-15, 7)) << 1) + ((SMOOTHED(-6, -3) < SMOOTHED(2, 1)) << 0));
desc[13] = (uchar)(((SMOOTHED(11, 0) < SMOOTHED(-3, 2)) << 7) + ((SMOOTHED(7, -12) < SMOOTHED(14, 5)) << 6) + ((SMOOTHED(0, -7) < SMOOTHED(-1, -1)) << 5) + ((SMOOTHED(-16, 0) < SMOOTHED(6, 8)) << 4) + ((SMOOTHED(22, 11) < SMOOTHED(0, -3)) << 3) + ((SMOOTHED(19, 0) < SMOOTHED(5, -17)) << 2) + ((SMOOTHED(-23, -14) < SMOOTHED(-13, -19)) << 1) + ((SMOOTHED(-8, 10) < SMOOTHED(-11, -2)) << 0));
desc[14] = (uchar)(((SMOOTHED(-11, 6) < SMOOTHED(-10, 13)) << 7) + ((SMOOTHED(1, -7) < SMOOTHED(14, 0)) << 6) + ((SMOOTHED(-12, 1) < SMOOTHED(-5, -5)) << 5) + ((SMOOTHED(4, 7) < SMOOTHED(8, -1)) << 4) + ((SMOOTHED(-1, -5) < SMOOTHED(15, 2)) << 3) + ((SMOOTHED(-3, -1) < SMOOTHED(7, -10)) << 2) + ((SMOOTHED(3, -6) < SMOOTHED(10, -18)) << 1) + ((SMOOTHED(-7, -13) < SMOOTHED(-13, 10)) << 0));
desc[15] = (uchar)(((SMOOTHED(1, -1) < SMOOTHED(13, -10)) << 7) + ((SMOOTHED(-19, 14) < SMOOTHED(8, -14)) << 6) + ((SMOOTHED(-4, -13) < SMOOTHED(7, 1)) << 5) + ((SMOOTHED(1, -2) < SMOOTHED(12, -7)) << 4) + ((SMOOTHED(3, -5) < SMOOTHED(1, -5)) << 3) + ((SMOOTHED(-2, -2) < SMOOTHED(8, -10)) << 2) + ((SMOOTHED(2, 14) < SMOOTHED(8, 7)) << 1) + ((SMOOTHED(3, 9) < SMOOTHED(8, 2)) << 0));
#undef SMOOTHED
+35
View File
@@ -0,0 +1,35 @@
// Code generated with '$ scripts/generate_code.py src/test_pairs.txt 32'
#define SMOOTHED(y,x) smoothedSum(sum, pt, y, x, use_orientation, R)
desc[0] = (uchar)(((SMOOTHED(-2, -1) < SMOOTHED(7, -1)) << 7) + ((SMOOTHED(-14, -1) < SMOOTHED(-3, 3)) << 6) + ((SMOOTHED(1, -2) < SMOOTHED(11, 2)) << 5) + ((SMOOTHED(1, 6) < SMOOTHED(-10, -7)) << 4) + ((SMOOTHED(13, 2) < SMOOTHED(-1, 0)) << 3) + ((SMOOTHED(-14, 5) < SMOOTHED(5, -3)) << 2) + ((SMOOTHED(-2, 8) < SMOOTHED(2, 4)) << 1) + ((SMOOTHED(-11, 8) < SMOOTHED(-15, 5)) << 0));
desc[1] = (uchar)(((SMOOTHED(-6, -23) < SMOOTHED(8, -9)) << 7) + ((SMOOTHED(-12, 6) < SMOOTHED(-10, 8)) << 6) + ((SMOOTHED(-3, -1) < SMOOTHED(8, 1)) << 5) + ((SMOOTHED(3, 6) < SMOOTHED(5, 6)) << 4) + ((SMOOTHED(-7, -6) < SMOOTHED(5, -5)) << 3) + ((SMOOTHED(22, -2) < SMOOTHED(-11, -8)) << 2) + ((SMOOTHED(14, 7) < SMOOTHED(8, 5)) << 1) + ((SMOOTHED(-1, 14) < SMOOTHED(-5, -14)) << 0));
desc[2] = (uchar)(((SMOOTHED(-14, 9) < SMOOTHED(2, 0)) << 7) + ((SMOOTHED(7, -3) < SMOOTHED(22, 6)) << 6) + ((SMOOTHED(-6, 6) < SMOOTHED(-8, -5)) << 5) + ((SMOOTHED(-5, 9) < SMOOTHED(7, -1)) << 4) + ((SMOOTHED(-3, -7) < SMOOTHED(-10, -18)) << 3) + ((SMOOTHED(4, -5) < SMOOTHED(0, 11)) << 2) + ((SMOOTHED(2, 3) < SMOOTHED(9, 10)) << 1) + ((SMOOTHED(-10, 3) < SMOOTHED(4, 9)) << 0));
desc[3] = (uchar)(((SMOOTHED(0, 12) < SMOOTHED(-3, 19)) << 7) + ((SMOOTHED(1, 15) < SMOOTHED(-11, -5)) << 6) + ((SMOOTHED(14, -1) < SMOOTHED(7, 8)) << 5) + ((SMOOTHED(7, -23) < SMOOTHED(-5, 5)) << 4) + ((SMOOTHED(0, -6) < SMOOTHED(-10, 17)) << 3) + ((SMOOTHED(13, -4) < SMOOTHED(-3, -4)) << 2) + ((SMOOTHED(-12, 1) < SMOOTHED(-12, 2)) << 1) + ((SMOOTHED(0, 8) < SMOOTHED(3, 22)) << 0));
desc[4] = (uchar)(((SMOOTHED(-13, 13) < SMOOTHED(3, -1)) << 7) + ((SMOOTHED(-16, 17) < SMOOTHED(6, 10)) << 6) + ((SMOOTHED(7, 15) < SMOOTHED(-5, 0)) << 5) + ((SMOOTHED(2, -12) < SMOOTHED(19, -2)) << 4) + ((SMOOTHED(3, -6) < SMOOTHED(-4, -15)) << 3) + ((SMOOTHED(8, 3) < SMOOTHED(0, 14)) << 2) + ((SMOOTHED(4, -11) < SMOOTHED(5, 5)) << 1) + ((SMOOTHED(11, -7) < SMOOTHED(7, 1)) << 0));
desc[5] = (uchar)(((SMOOTHED(6, 12) < SMOOTHED(21, 3)) << 7) + ((SMOOTHED(-3, 2) < SMOOTHED(14, 1)) << 6) + ((SMOOTHED(5, 1) < SMOOTHED(-5, 11)) << 5) + ((SMOOTHED(3, -17) < SMOOTHED(-6, 2)) << 4) + ((SMOOTHED(6, 8) < SMOOTHED(5, -10)) << 3) + ((SMOOTHED(-14, -2) < SMOOTHED(0, 4)) << 2) + ((SMOOTHED(5, -7) < SMOOTHED(-6, 5)) << 1) + ((SMOOTHED(10, 4) < SMOOTHED(4, -7)) << 0));
desc[6] = (uchar)(((SMOOTHED(22, 0) < SMOOTHED(7, -18)) << 7) + ((SMOOTHED(-1, -3) < SMOOTHED(0, 18)) << 6) + ((SMOOTHED(-4, 22) < SMOOTHED(-5, 3)) << 5) + ((SMOOTHED(1, -7) < SMOOTHED(2, -3)) << 4) + ((SMOOTHED(19, -20) < SMOOTHED(17, -2)) << 3) + ((SMOOTHED(3, -10) < SMOOTHED(-8, 24)) << 2) + ((SMOOTHED(-5, -14) < SMOOTHED(7, 5)) << 1) + ((SMOOTHED(-2, 12) < SMOOTHED(-4, -15)) << 0));
desc[7] = (uchar)(((SMOOTHED(4, 12) < SMOOTHED(0, -19)) << 7) + ((SMOOTHED(20, 13) < SMOOTHED(3, 5)) << 6) + ((SMOOTHED(-8, -12) < SMOOTHED(5, 0)) << 5) + ((SMOOTHED(-5, 6) < SMOOTHED(-7, -11)) << 4) + ((SMOOTHED(6, -11) < SMOOTHED(-3, -22)) << 3) + ((SMOOTHED(15, 4) < SMOOTHED(10, 1)) << 2) + ((SMOOTHED(-7, -4) < SMOOTHED(15, -6)) << 1) + ((SMOOTHED(5, 10) < SMOOTHED(0, 24)) << 0));
desc[8] = (uchar)(((SMOOTHED(3, 6) < SMOOTHED(22, -2)) << 7) + ((SMOOTHED(-13, 14) < SMOOTHED(4, -4)) << 6) + ((SMOOTHED(-13, 8) < SMOOTHED(-18, -22)) << 5) + ((SMOOTHED(-1, -1) < SMOOTHED(-7, 3)) << 4) + ((SMOOTHED(-19, -12) < SMOOTHED(4, 3)) << 3) + ((SMOOTHED(8, 10) < SMOOTHED(13, -2)) << 2) + ((SMOOTHED(-6, -1) < SMOOTHED(-6, -5)) << 1) + ((SMOOTHED(2, -21) < SMOOTHED(-3, 2)) << 0));
desc[9] = (uchar)(((SMOOTHED(4, -7) < SMOOTHED(0, 16)) << 7) + ((SMOOTHED(-6, -5) < SMOOTHED(-12, -1)) << 6) + ((SMOOTHED(1, -1) < SMOOTHED(9, 18)) << 5) + ((SMOOTHED(-7, 10) < SMOOTHED(-11, 6)) << 4) + ((SMOOTHED(4, 3) < SMOOTHED(19, -7)) << 3) + ((SMOOTHED(-18, 5) < SMOOTHED(-4, 5)) << 2) + ((SMOOTHED(4, 0) < SMOOTHED(-20, 4)) << 1) + ((SMOOTHED(7, -11) < SMOOTHED(18, 12)) << 0));
desc[10] = (uchar)(((SMOOTHED(-20, 17) < SMOOTHED(-18, 7)) << 7) + ((SMOOTHED(2, 15) < SMOOTHED(19, -11)) << 6) + ((SMOOTHED(-18, 6) < SMOOTHED(-7, 3)) << 5) + ((SMOOTHED(-4, 1) < SMOOTHED(-14, 13)) << 4) + ((SMOOTHED(17, 3) < SMOOTHED(2, -8)) << 3) + ((SMOOTHED(-7, 2) < SMOOTHED(1, 6)) << 2) + ((SMOOTHED(17, -9) < SMOOTHED(-2, 8)) << 1) + ((SMOOTHED(-8, -6) < SMOOTHED(-1, 12)) << 0));
desc[11] = (uchar)(((SMOOTHED(-2, 4) < SMOOTHED(-1, 6)) << 7) + ((SMOOTHED(-2, 7) < SMOOTHED(6, 8)) << 6) + ((SMOOTHED(-8, -1) < SMOOTHED(-7, -9)) << 5) + ((SMOOTHED(8, -9) < SMOOTHED(15, 0)) << 4) + ((SMOOTHED(0, 22) < SMOOTHED(-4, -15)) << 3) + ((SMOOTHED(-14, -1) < SMOOTHED(3, -2)) << 2) + ((SMOOTHED(-7, -4) < SMOOTHED(17, -7)) << 1) + ((SMOOTHED(-8, -2) < SMOOTHED(9, -4)) << 0));
desc[12] = (uchar)(((SMOOTHED(5, -7) < SMOOTHED(7, 7)) << 7) + ((SMOOTHED(-5, 13) < SMOOTHED(-8, 11)) << 6) + ((SMOOTHED(11, -4) < SMOOTHED(0, 8)) << 5) + ((SMOOTHED(5, -11) < SMOOTHED(-9, -6)) << 4) + ((SMOOTHED(2, -6) < SMOOTHED(3, -20)) << 3) + ((SMOOTHED(-6, 2) < SMOOTHED(6, 10)) << 2) + ((SMOOTHED(-6, -6) < SMOOTHED(-15, 7)) << 1) + ((SMOOTHED(-6, -3) < SMOOTHED(2, 1)) << 0));
desc[13] = (uchar)(((SMOOTHED(11, 0) < SMOOTHED(-3, 2)) << 7) + ((SMOOTHED(7, -12) < SMOOTHED(14, 5)) << 6) + ((SMOOTHED(0, -7) < SMOOTHED(-1, -1)) << 5) + ((SMOOTHED(-16, 0) < SMOOTHED(6, 8)) << 4) + ((SMOOTHED(22, 11) < SMOOTHED(0, -3)) << 3) + ((SMOOTHED(19, 0) < SMOOTHED(5, -17)) << 2) + ((SMOOTHED(-23, -14) < SMOOTHED(-13, -19)) << 1) + ((SMOOTHED(-8, 10) < SMOOTHED(-11, -2)) << 0));
desc[14] = (uchar)(((SMOOTHED(-11, 6) < SMOOTHED(-10, 13)) << 7) + ((SMOOTHED(1, -7) < SMOOTHED(14, 0)) << 6) + ((SMOOTHED(-12, 1) < SMOOTHED(-5, -5)) << 5) + ((SMOOTHED(4, 7) < SMOOTHED(8, -1)) << 4) + ((SMOOTHED(-1, -5) < SMOOTHED(15, 2)) << 3) + ((SMOOTHED(-3, -1) < SMOOTHED(7, -10)) << 2) + ((SMOOTHED(3, -6) < SMOOTHED(10, -18)) << 1) + ((SMOOTHED(-7, -13) < SMOOTHED(-13, 10)) << 0));
desc[15] = (uchar)(((SMOOTHED(1, -1) < SMOOTHED(13, -10)) << 7) + ((SMOOTHED(-19, 14) < SMOOTHED(8, -14)) << 6) + ((SMOOTHED(-4, -13) < SMOOTHED(7, 1)) << 5) + ((SMOOTHED(1, -2) < SMOOTHED(12, -7)) << 4) + ((SMOOTHED(3, -5) < SMOOTHED(1, -5)) << 3) + ((SMOOTHED(-2, -2) < SMOOTHED(8, -10)) << 2) + ((SMOOTHED(2, 14) < SMOOTHED(8, 7)) << 1) + ((SMOOTHED(3, 9) < SMOOTHED(8, 2)) << 0));
desc[16] = (uchar)(((SMOOTHED(-9, 1) < SMOOTHED(-18, 0)) << 7) + ((SMOOTHED(4, 0) < SMOOTHED(1, 12)) << 6) + ((SMOOTHED(0, 9) < SMOOTHED(-14, -10)) << 5) + ((SMOOTHED(-13, -9) < SMOOTHED(-2, 6)) << 4) + ((SMOOTHED(1, 5) < SMOOTHED(10, 10)) << 3) + ((SMOOTHED(-3, -6) < SMOOTHED(-16, -5)) << 2) + ((SMOOTHED(11, 6) < SMOOTHED(-5, 0)) << 1) + ((SMOOTHED(-23, 10) < SMOOTHED(1, 2)) << 0));
desc[17] = (uchar)(((SMOOTHED(13, -5) < SMOOTHED(-3, 9)) << 7) + ((SMOOTHED(-4, -1) < SMOOTHED(-13, -5)) << 6) + ((SMOOTHED(10, 13) < SMOOTHED(-11, 8)) << 5) + ((SMOOTHED(19, 20) < SMOOTHED(-9, 2)) << 4) + ((SMOOTHED(4, -8) < SMOOTHED(0, -9)) << 3) + ((SMOOTHED(-14, 10) < SMOOTHED(15, 19)) << 2) + ((SMOOTHED(-14, -12) < SMOOTHED(-10, -3)) << 1) + ((SMOOTHED(-23, -3) < SMOOTHED(17, -2)) << 0));
desc[18] = (uchar)(((SMOOTHED(-3, -11) < SMOOTHED(6, -14)) << 7) + ((SMOOTHED(19, -2) < SMOOTHED(-4, 2)) << 6) + ((SMOOTHED(-5, 5) < SMOOTHED(3, -13)) << 5) + ((SMOOTHED(2, -2) < SMOOTHED(-5, 4)) << 4) + ((SMOOTHED(17, 4) < SMOOTHED(17, -11)) << 3) + ((SMOOTHED(-7, -2) < SMOOTHED(1, 23)) << 2) + ((SMOOTHED(8, 13) < SMOOTHED(1, -16)) << 1) + ((SMOOTHED(-13, -5) < SMOOTHED(1, -17)) << 0));
desc[19] = (uchar)(((SMOOTHED(4, 6) < SMOOTHED(-8, -3)) << 7) + ((SMOOTHED(-5, -9) < SMOOTHED(-2, -10)) << 6) + ((SMOOTHED(-9, 0) < SMOOTHED(-7, -2)) << 5) + ((SMOOTHED(5, 0) < SMOOTHED(5, 2)) << 4) + ((SMOOTHED(-4, -16) < SMOOTHED(6, 3)) << 3) + ((SMOOTHED(2, -15) < SMOOTHED(-2, 12)) << 2) + ((SMOOTHED(4, -1) < SMOOTHED(6, 2)) << 1) + ((SMOOTHED(1, 1) < SMOOTHED(-2, -8)) << 0));
desc[20] = (uchar)(((SMOOTHED(-2, 12) < SMOOTHED(-5, -2)) << 7) + ((SMOOTHED(-8, 8) < SMOOTHED(-9, 9)) << 6) + ((SMOOTHED(2, -10) < SMOOTHED(3, 1)) << 5) + ((SMOOTHED(-4, 10) < SMOOTHED(-9, 4)) << 4) + ((SMOOTHED(6, 12) < SMOOTHED(2, 5)) << 3) + ((SMOOTHED(-3, -8) < SMOOTHED(0, 5)) << 2) + ((SMOOTHED(-13, 1) < SMOOTHED(-7, 2)) << 1) + ((SMOOTHED(-1, -10) < SMOOTHED(7, -18)) << 0));
desc[21] = (uchar)(((SMOOTHED(-1, 8) < SMOOTHED(-9, -10)) << 7) + ((SMOOTHED(-23, -1) < SMOOTHED(6, 2)) << 6) + ((SMOOTHED(-5, -3) < SMOOTHED(3, 2)) << 5) + ((SMOOTHED(0, 11) < SMOOTHED(-4, -7)) << 4) + ((SMOOTHED(15, 2) < SMOOTHED(-10, -3)) << 3) + ((SMOOTHED(-20, -8) < SMOOTHED(-13, 3)) << 2) + ((SMOOTHED(-19, -12) < SMOOTHED(5, -11)) << 1) + ((SMOOTHED(-17, -13) < SMOOTHED(-3, 2)) << 0));
desc[22] = (uchar)(((SMOOTHED(7, 4) < SMOOTHED(-12, 0)) << 7) + ((SMOOTHED(5, -1) < SMOOTHED(-14, -6)) << 6) + ((SMOOTHED(-4, 11) < SMOOTHED(0, -4)) << 5) + ((SMOOTHED(3, 10) < SMOOTHED(7, -3)) << 4) + ((SMOOTHED(13, 21) < SMOOTHED(-11, 6)) << 3) + ((SMOOTHED(-12, 24) < SMOOTHED(-7, -4)) << 2) + ((SMOOTHED(4, 16) < SMOOTHED(3, -14)) << 1) + ((SMOOTHED(-3, 5) < SMOOTHED(-7, -12)) << 0));
desc[23] = (uchar)(((SMOOTHED(0, -4) < SMOOTHED(7, -5)) << 7) + ((SMOOTHED(-17, -9) < SMOOTHED(13, -7)) << 6) + ((SMOOTHED(22, -6) < SMOOTHED(-11, 5)) << 5) + ((SMOOTHED(2, -8) < SMOOTHED(23, -11)) << 4) + ((SMOOTHED(7, -10) < SMOOTHED(-1, 14)) << 3) + ((SMOOTHED(-3, -10) < SMOOTHED(8, 3)) << 2) + ((SMOOTHED(-13, 1) < SMOOTHED(-6, 0)) << 1) + ((SMOOTHED(-7, -21) < SMOOTHED(6, -14)) << 0));
desc[24] = (uchar)(((SMOOTHED(18, 19) < SMOOTHED(-4, -6)) << 7) + ((SMOOTHED(10, 7) < SMOOTHED(-1, -4)) << 6) + ((SMOOTHED(-1, 21) < SMOOTHED(1, -5)) << 5) + ((SMOOTHED(-10, 6) < SMOOTHED(-11, -2)) << 4) + ((SMOOTHED(18, -3) < SMOOTHED(-1, 7)) << 3) + ((SMOOTHED(-3, -9) < SMOOTHED(-5, 10)) << 2) + ((SMOOTHED(-13, 14) < SMOOTHED(17, -3)) << 1) + ((SMOOTHED(11, -19) < SMOOTHED(-1, -18)) << 0));
desc[25] = (uchar)(((SMOOTHED(8, -2) < SMOOTHED(-18, -23)) << 7) + ((SMOOTHED(0, -5) < SMOOTHED(-2, -9)) << 6) + ((SMOOTHED(-4, -11) < SMOOTHED(2, -8)) << 5) + ((SMOOTHED(14, 6) < SMOOTHED(-3, -6)) << 4) + ((SMOOTHED(-3, 0) < SMOOTHED(-15, 0)) << 3) + ((SMOOTHED(-9, 4) < SMOOTHED(-15, -9)) << 2) + ((SMOOTHED(-1, 11) < SMOOTHED(3, 11)) << 1) + ((SMOOTHED(-10, -16) < SMOOTHED(-7, 7)) << 0));
desc[26] = (uchar)(((SMOOTHED(-2, -10) < SMOOTHED(-10, -2)) << 7) + ((SMOOTHED(-5, -3) < SMOOTHED(5, -23)) << 6) + ((SMOOTHED(13, -8) < SMOOTHED(-15, -11)) << 5) + ((SMOOTHED(-15, 11) < SMOOTHED(6, -6)) << 4) + ((SMOOTHED(-16, -3) < SMOOTHED(-2, 2)) << 3) + ((SMOOTHED(6, 12) < SMOOTHED(-16, 24)) << 2) + ((SMOOTHED(-10, 0) < SMOOTHED(8, 11)) << 1) + ((SMOOTHED(-7, 7) < SMOOTHED(-19, -7)) << 0));
desc[27] = (uchar)(((SMOOTHED(5, 16) < SMOOTHED(9, -3)) << 7) + ((SMOOTHED(9, 7) < SMOOTHED(-7, -16)) << 6) + ((SMOOTHED(3, 2) < SMOOTHED(-10, 9)) << 5) + ((SMOOTHED(21, 1) < SMOOTHED(8, 7)) << 4) + ((SMOOTHED(7, 0) < SMOOTHED(1, 17)) << 3) + ((SMOOTHED(-8, 12) < SMOOTHED(9, 6)) << 2) + ((SMOOTHED(11, -7) < SMOOTHED(-8, -6)) << 1) + ((SMOOTHED(19, 0) < SMOOTHED(9, 3)) << 0));
desc[28] = (uchar)(((SMOOTHED(1, -7) < SMOOTHED(-5, -11)) << 7) + ((SMOOTHED(0, 8) < SMOOTHED(-2, 14)) << 6) + ((SMOOTHED(12, -2) < SMOOTHED(-15, -6)) << 5) + ((SMOOTHED(4, 12) < SMOOTHED(0, -21)) << 4) + ((SMOOTHED(17, -4) < SMOOTHED(-6, -7)) << 3) + ((SMOOTHED(-10, -9) < SMOOTHED(-14, -7)) << 2) + ((SMOOTHED(-15, -10) < SMOOTHED(-15, -14)) << 1) + ((SMOOTHED(-7, -5) < SMOOTHED(5, -12)) << 0));
desc[29] = (uchar)(((SMOOTHED(-4, 0) < SMOOTHED(15, -4)) << 7) + ((SMOOTHED(5, 2) < SMOOTHED(-6, -23)) << 6) + ((SMOOTHED(-4, -21) < SMOOTHED(-6, 4)) << 5) + ((SMOOTHED(-10, 5) < SMOOTHED(-15, 6)) << 4) + ((SMOOTHED(4, -3) < SMOOTHED(-1, 5)) << 3) + ((SMOOTHED(-4, 19) < SMOOTHED(-23, -4)) << 2) + ((SMOOTHED(-4, 17) < SMOOTHED(13, -11)) << 1) + ((SMOOTHED(1, 12) < SMOOTHED(4, -14)) << 0));
desc[30] = (uchar)(((SMOOTHED(-11, -6) < SMOOTHED(-20, 10)) << 7) + ((SMOOTHED(4, 5) < SMOOTHED(3, 20)) << 6) + ((SMOOTHED(-8, -20) < SMOOTHED(3, 1)) << 5) + ((SMOOTHED(-19, 9) < SMOOTHED(9, -3)) << 4) + ((SMOOTHED(18, 15) < SMOOTHED(11, -4)) << 3) + ((SMOOTHED(12, 16) < SMOOTHED(8, 7)) << 2) + ((SMOOTHED(-14, -8) < SMOOTHED(-3, 9)) << 1) + ((SMOOTHED(-6, 0) < SMOOTHED(2, -4)) << 0));
desc[31] = (uchar)(((SMOOTHED(1, -10) < SMOOTHED(-1, 2)) << 7) + ((SMOOTHED(8, -7) < SMOOTHED(-6, 18)) << 6) + ((SMOOTHED(9, 12) < SMOOTHED(-7, -23)) << 5) + ((SMOOTHED(8, -6) < SMOOTHED(5, 2)) << 4) + ((SMOOTHED(-9, 6) < SMOOTHED(-12, -7)) << 3) + ((SMOOTHED(-1, -2) < SMOOTHED(-7, 2)) << 2) + ((SMOOTHED(9, 9) < SMOOTHED(7, 15)) << 1) + ((SMOOTHED(6, 2) < SMOOTHED(-6, 6)) << 0));
#undef SMOOTHED
+67
View File
@@ -0,0 +1,67 @@
// Code generated with '$ scripts/generate_code.py src/test_pairs.txt 64'
#define SMOOTHED(y,x) smoothedSum(sum, pt, y, x, use_orientation, R)
desc[0] = (uchar)(((SMOOTHED(-2, -1) < SMOOTHED(7, -1)) << 7) + ((SMOOTHED(-14, -1) < SMOOTHED(-3, 3)) << 6) + ((SMOOTHED(1, -2) < SMOOTHED(11, 2)) << 5) + ((SMOOTHED(1, 6) < SMOOTHED(-10, -7)) << 4) + ((SMOOTHED(13, 2) < SMOOTHED(-1, 0)) << 3) + ((SMOOTHED(-14, 5) < SMOOTHED(5, -3)) << 2) + ((SMOOTHED(-2, 8) < SMOOTHED(2, 4)) << 1) + ((SMOOTHED(-11, 8) < SMOOTHED(-15, 5)) << 0));
desc[1] = (uchar)(((SMOOTHED(-6, -23) < SMOOTHED(8, -9)) << 7) + ((SMOOTHED(-12, 6) < SMOOTHED(-10, 8)) << 6) + ((SMOOTHED(-3, -1) < SMOOTHED(8, 1)) << 5) + ((SMOOTHED(3, 6) < SMOOTHED(5, 6)) << 4) + ((SMOOTHED(-7, -6) < SMOOTHED(5, -5)) << 3) + ((SMOOTHED(22, -2) < SMOOTHED(-11, -8)) << 2) + ((SMOOTHED(14, 7) < SMOOTHED(8, 5)) << 1) + ((SMOOTHED(-1, 14) < SMOOTHED(-5, -14)) << 0));
desc[2] = (uchar)(((SMOOTHED(-14, 9) < SMOOTHED(2, 0)) << 7) + ((SMOOTHED(7, -3) < SMOOTHED(22, 6)) << 6) + ((SMOOTHED(-6, 6) < SMOOTHED(-8, -5)) << 5) + ((SMOOTHED(-5, 9) < SMOOTHED(7, -1)) << 4) + ((SMOOTHED(-3, -7) < SMOOTHED(-10, -18)) << 3) + ((SMOOTHED(4, -5) < SMOOTHED(0, 11)) << 2) + ((SMOOTHED(2, 3) < SMOOTHED(9, 10)) << 1) + ((SMOOTHED(-10, 3) < SMOOTHED(4, 9)) << 0));
desc[3] = (uchar)(((SMOOTHED(0, 12) < SMOOTHED(-3, 19)) << 7) + ((SMOOTHED(1, 15) < SMOOTHED(-11, -5)) << 6) + ((SMOOTHED(14, -1) < SMOOTHED(7, 8)) << 5) + ((SMOOTHED(7, -23) < SMOOTHED(-5, 5)) << 4) + ((SMOOTHED(0, -6) < SMOOTHED(-10, 17)) << 3) + ((SMOOTHED(13, -4) < SMOOTHED(-3, -4)) << 2) + ((SMOOTHED(-12, 1) < SMOOTHED(-12, 2)) << 1) + ((SMOOTHED(0, 8) < SMOOTHED(3, 22)) << 0));
desc[4] = (uchar)(((SMOOTHED(-13, 13) < SMOOTHED(3, -1)) << 7) + ((SMOOTHED(-16, 17) < SMOOTHED(6, 10)) << 6) + ((SMOOTHED(7, 15) < SMOOTHED(-5, 0)) << 5) + ((SMOOTHED(2, -12) < SMOOTHED(19, -2)) << 4) + ((SMOOTHED(3, -6) < SMOOTHED(-4, -15)) << 3) + ((SMOOTHED(8, 3) < SMOOTHED(0, 14)) << 2) + ((SMOOTHED(4, -11) < SMOOTHED(5, 5)) << 1) + ((SMOOTHED(11, -7) < SMOOTHED(7, 1)) << 0));
desc[5] = (uchar)(((SMOOTHED(6, 12) < SMOOTHED(21, 3)) << 7) + ((SMOOTHED(-3, 2) < SMOOTHED(14, 1)) << 6) + ((SMOOTHED(5, 1) < SMOOTHED(-5, 11)) << 5) + ((SMOOTHED(3, -17) < SMOOTHED(-6, 2)) << 4) + ((SMOOTHED(6, 8) < SMOOTHED(5, -10)) << 3) + ((SMOOTHED(-14, -2) < SMOOTHED(0, 4)) << 2) + ((SMOOTHED(5, -7) < SMOOTHED(-6, 5)) << 1) + ((SMOOTHED(10, 4) < SMOOTHED(4, -7)) << 0));
desc[6] = (uchar)(((SMOOTHED(22, 0) < SMOOTHED(7, -18)) << 7) + ((SMOOTHED(-1, -3) < SMOOTHED(0, 18)) << 6) + ((SMOOTHED(-4, 22) < SMOOTHED(-5, 3)) << 5) + ((SMOOTHED(1, -7) < SMOOTHED(2, -3)) << 4) + ((SMOOTHED(19, -20) < SMOOTHED(17, -2)) << 3) + ((SMOOTHED(3, -10) < SMOOTHED(-8, 24)) << 2) + ((SMOOTHED(-5, -14) < SMOOTHED(7, 5)) << 1) + ((SMOOTHED(-2, 12) < SMOOTHED(-4, -15)) << 0));
desc[7] = (uchar)(((SMOOTHED(4, 12) < SMOOTHED(0, -19)) << 7) + ((SMOOTHED(20, 13) < SMOOTHED(3, 5)) << 6) + ((SMOOTHED(-8, -12) < SMOOTHED(5, 0)) << 5) + ((SMOOTHED(-5, 6) < SMOOTHED(-7, -11)) << 4) + ((SMOOTHED(6, -11) < SMOOTHED(-3, -22)) << 3) + ((SMOOTHED(15, 4) < SMOOTHED(10, 1)) << 2) + ((SMOOTHED(-7, -4) < SMOOTHED(15, -6)) << 1) + ((SMOOTHED(5, 10) < SMOOTHED(0, 24)) << 0));
desc[8] = (uchar)(((SMOOTHED(3, 6) < SMOOTHED(22, -2)) << 7) + ((SMOOTHED(-13, 14) < SMOOTHED(4, -4)) << 6) + ((SMOOTHED(-13, 8) < SMOOTHED(-18, -22)) << 5) + ((SMOOTHED(-1, -1) < SMOOTHED(-7, 3)) << 4) + ((SMOOTHED(-19, -12) < SMOOTHED(4, 3)) << 3) + ((SMOOTHED(8, 10) < SMOOTHED(13, -2)) << 2) + ((SMOOTHED(-6, -1) < SMOOTHED(-6, -5)) << 1) + ((SMOOTHED(2, -21) < SMOOTHED(-3, 2)) << 0));
desc[9] = (uchar)(((SMOOTHED(4, -7) < SMOOTHED(0, 16)) << 7) + ((SMOOTHED(-6, -5) < SMOOTHED(-12, -1)) << 6) + ((SMOOTHED(1, -1) < SMOOTHED(9, 18)) << 5) + ((SMOOTHED(-7, 10) < SMOOTHED(-11, 6)) << 4) + ((SMOOTHED(4, 3) < SMOOTHED(19, -7)) << 3) + ((SMOOTHED(-18, 5) < SMOOTHED(-4, 5)) << 2) + ((SMOOTHED(4, 0) < SMOOTHED(-20, 4)) << 1) + ((SMOOTHED(7, -11) < SMOOTHED(18, 12)) << 0));
desc[10] = (uchar)(((SMOOTHED(-20, 17) < SMOOTHED(-18, 7)) << 7) + ((SMOOTHED(2, 15) < SMOOTHED(19, -11)) << 6) + ((SMOOTHED(-18, 6) < SMOOTHED(-7, 3)) << 5) + ((SMOOTHED(-4, 1) < SMOOTHED(-14, 13)) << 4) + ((SMOOTHED(17, 3) < SMOOTHED(2, -8)) << 3) + ((SMOOTHED(-7, 2) < SMOOTHED(1, 6)) << 2) + ((SMOOTHED(17, -9) < SMOOTHED(-2, 8)) << 1) + ((SMOOTHED(-8, -6) < SMOOTHED(-1, 12)) << 0));
desc[11] = (uchar)(((SMOOTHED(-2, 4) < SMOOTHED(-1, 6)) << 7) + ((SMOOTHED(-2, 7) < SMOOTHED(6, 8)) << 6) + ((SMOOTHED(-8, -1) < SMOOTHED(-7, -9)) << 5) + ((SMOOTHED(8, -9) < SMOOTHED(15, 0)) << 4) + ((SMOOTHED(0, 22) < SMOOTHED(-4, -15)) << 3) + ((SMOOTHED(-14, -1) < SMOOTHED(3, -2)) << 2) + ((SMOOTHED(-7, -4) < SMOOTHED(17, -7)) << 1) + ((SMOOTHED(-8, -2) < SMOOTHED(9, -4)) << 0));
desc[12] = (uchar)(((SMOOTHED(5, -7) < SMOOTHED(7, 7)) << 7) + ((SMOOTHED(-5, 13) < SMOOTHED(-8, 11)) << 6) + ((SMOOTHED(11, -4) < SMOOTHED(0, 8)) << 5) + ((SMOOTHED(5, -11) < SMOOTHED(-9, -6)) << 4) + ((SMOOTHED(2, -6) < SMOOTHED(3, -20)) << 3) + ((SMOOTHED(-6, 2) < SMOOTHED(6, 10)) << 2) + ((SMOOTHED(-6, -6) < SMOOTHED(-15, 7)) << 1) + ((SMOOTHED(-6, -3) < SMOOTHED(2, 1)) << 0));
desc[13] = (uchar)(((SMOOTHED(11, 0) < SMOOTHED(-3, 2)) << 7) + ((SMOOTHED(7, -12) < SMOOTHED(14, 5)) << 6) + ((SMOOTHED(0, -7) < SMOOTHED(-1, -1)) << 5) + ((SMOOTHED(-16, 0) < SMOOTHED(6, 8)) << 4) + ((SMOOTHED(22, 11) < SMOOTHED(0, -3)) << 3) + ((SMOOTHED(19, 0) < SMOOTHED(5, -17)) << 2) + ((SMOOTHED(-23, -14) < SMOOTHED(-13, -19)) << 1) + ((SMOOTHED(-8, 10) < SMOOTHED(-11, -2)) << 0));
desc[14] = (uchar)(((SMOOTHED(-11, 6) < SMOOTHED(-10, 13)) << 7) + ((SMOOTHED(1, -7) < SMOOTHED(14, 0)) << 6) + ((SMOOTHED(-12, 1) < SMOOTHED(-5, -5)) << 5) + ((SMOOTHED(4, 7) < SMOOTHED(8, -1)) << 4) + ((SMOOTHED(-1, -5) < SMOOTHED(15, 2)) << 3) + ((SMOOTHED(-3, -1) < SMOOTHED(7, -10)) << 2) + ((SMOOTHED(3, -6) < SMOOTHED(10, -18)) << 1) + ((SMOOTHED(-7, -13) < SMOOTHED(-13, 10)) << 0));
desc[15] = (uchar)(((SMOOTHED(1, -1) < SMOOTHED(13, -10)) << 7) + ((SMOOTHED(-19, 14) < SMOOTHED(8, -14)) << 6) + ((SMOOTHED(-4, -13) < SMOOTHED(7, 1)) << 5) + ((SMOOTHED(1, -2) < SMOOTHED(12, -7)) << 4) + ((SMOOTHED(3, -5) < SMOOTHED(1, -5)) << 3) + ((SMOOTHED(-2, -2) < SMOOTHED(8, -10)) << 2) + ((SMOOTHED(2, 14) < SMOOTHED(8, 7)) << 1) + ((SMOOTHED(3, 9) < SMOOTHED(8, 2)) << 0));
desc[16] = (uchar)(((SMOOTHED(-9, 1) < SMOOTHED(-18, 0)) << 7) + ((SMOOTHED(4, 0) < SMOOTHED(1, 12)) << 6) + ((SMOOTHED(0, 9) < SMOOTHED(-14, -10)) << 5) + ((SMOOTHED(-13, -9) < SMOOTHED(-2, 6)) << 4) + ((SMOOTHED(1, 5) < SMOOTHED(10, 10)) << 3) + ((SMOOTHED(-3, -6) < SMOOTHED(-16, -5)) << 2) + ((SMOOTHED(11, 6) < SMOOTHED(-5, 0)) << 1) + ((SMOOTHED(-23, 10) < SMOOTHED(1, 2)) << 0));
desc[17] = (uchar)(((SMOOTHED(13, -5) < SMOOTHED(-3, 9)) << 7) + ((SMOOTHED(-4, -1) < SMOOTHED(-13, -5)) << 6) + ((SMOOTHED(10, 13) < SMOOTHED(-11, 8)) << 5) + ((SMOOTHED(19, 20) < SMOOTHED(-9, 2)) << 4) + ((SMOOTHED(4, -8) < SMOOTHED(0, -9)) << 3) + ((SMOOTHED(-14, 10) < SMOOTHED(15, 19)) << 2) + ((SMOOTHED(-14, -12) < SMOOTHED(-10, -3)) << 1) + ((SMOOTHED(-23, -3) < SMOOTHED(17, -2)) << 0));
desc[18] = (uchar)(((SMOOTHED(-3, -11) < SMOOTHED(6, -14)) << 7) + ((SMOOTHED(19, -2) < SMOOTHED(-4, 2)) << 6) + ((SMOOTHED(-5, 5) < SMOOTHED(3, -13)) << 5) + ((SMOOTHED(2, -2) < SMOOTHED(-5, 4)) << 4) + ((SMOOTHED(17, 4) < SMOOTHED(17, -11)) << 3) + ((SMOOTHED(-7, -2) < SMOOTHED(1, 23)) << 2) + ((SMOOTHED(8, 13) < SMOOTHED(1, -16)) << 1) + ((SMOOTHED(-13, -5) < SMOOTHED(1, -17)) << 0));
desc[19] = (uchar)(((SMOOTHED(4, 6) < SMOOTHED(-8, -3)) << 7) + ((SMOOTHED(-5, -9) < SMOOTHED(-2, -10)) << 6) + ((SMOOTHED(-9, 0) < SMOOTHED(-7, -2)) << 5) + ((SMOOTHED(5, 0) < SMOOTHED(5, 2)) << 4) + ((SMOOTHED(-4, -16) < SMOOTHED(6, 3)) << 3) + ((SMOOTHED(2, -15) < SMOOTHED(-2, 12)) << 2) + ((SMOOTHED(4, -1) < SMOOTHED(6, 2)) << 1) + ((SMOOTHED(1, 1) < SMOOTHED(-2, -8)) << 0));
desc[20] = (uchar)(((SMOOTHED(-2, 12) < SMOOTHED(-5, -2)) << 7) + ((SMOOTHED(-8, 8) < SMOOTHED(-9, 9)) << 6) + ((SMOOTHED(2, -10) < SMOOTHED(3, 1)) << 5) + ((SMOOTHED(-4, 10) < SMOOTHED(-9, 4)) << 4) + ((SMOOTHED(6, 12) < SMOOTHED(2, 5)) << 3) + ((SMOOTHED(-3, -8) < SMOOTHED(0, 5)) << 2) + ((SMOOTHED(-13, 1) < SMOOTHED(-7, 2)) << 1) + ((SMOOTHED(-1, -10) < SMOOTHED(7, -18)) << 0));
desc[21] = (uchar)(((SMOOTHED(-1, 8) < SMOOTHED(-9, -10)) << 7) + ((SMOOTHED(-23, -1) < SMOOTHED(6, 2)) << 6) + ((SMOOTHED(-5, -3) < SMOOTHED(3, 2)) << 5) + ((SMOOTHED(0, 11) < SMOOTHED(-4, -7)) << 4) + ((SMOOTHED(15, 2) < SMOOTHED(-10, -3)) << 3) + ((SMOOTHED(-20, -8) < SMOOTHED(-13, 3)) << 2) + ((SMOOTHED(-19, -12) < SMOOTHED(5, -11)) << 1) + ((SMOOTHED(-17, -13) < SMOOTHED(-3, 2)) << 0));
desc[22] = (uchar)(((SMOOTHED(7, 4) < SMOOTHED(-12, 0)) << 7) + ((SMOOTHED(5, -1) < SMOOTHED(-14, -6)) << 6) + ((SMOOTHED(-4, 11) < SMOOTHED(0, -4)) << 5) + ((SMOOTHED(3, 10) < SMOOTHED(7, -3)) << 4) + ((SMOOTHED(13, 21) < SMOOTHED(-11, 6)) << 3) + ((SMOOTHED(-12, 24) < SMOOTHED(-7, -4)) << 2) + ((SMOOTHED(4, 16) < SMOOTHED(3, -14)) << 1) + ((SMOOTHED(-3, 5) < SMOOTHED(-7, -12)) << 0));
desc[23] = (uchar)(((SMOOTHED(0, -4) < SMOOTHED(7, -5)) << 7) + ((SMOOTHED(-17, -9) < SMOOTHED(13, -7)) << 6) + ((SMOOTHED(22, -6) < SMOOTHED(-11, 5)) << 5) + ((SMOOTHED(2, -8) < SMOOTHED(23, -11)) << 4) + ((SMOOTHED(7, -10) < SMOOTHED(-1, 14)) << 3) + ((SMOOTHED(-3, -10) < SMOOTHED(8, 3)) << 2) + ((SMOOTHED(-13, 1) < SMOOTHED(-6, 0)) << 1) + ((SMOOTHED(-7, -21) < SMOOTHED(6, -14)) << 0));
desc[24] = (uchar)(((SMOOTHED(18, 19) < SMOOTHED(-4, -6)) << 7) + ((SMOOTHED(10, 7) < SMOOTHED(-1, -4)) << 6) + ((SMOOTHED(-1, 21) < SMOOTHED(1, -5)) << 5) + ((SMOOTHED(-10, 6) < SMOOTHED(-11, -2)) << 4) + ((SMOOTHED(18, -3) < SMOOTHED(-1, 7)) << 3) + ((SMOOTHED(-3, -9) < SMOOTHED(-5, 10)) << 2) + ((SMOOTHED(-13, 14) < SMOOTHED(17, -3)) << 1) + ((SMOOTHED(11, -19) < SMOOTHED(-1, -18)) << 0));
desc[25] = (uchar)(((SMOOTHED(8, -2) < SMOOTHED(-18, -23)) << 7) + ((SMOOTHED(0, -5) < SMOOTHED(-2, -9)) << 6) + ((SMOOTHED(-4, -11) < SMOOTHED(2, -8)) << 5) + ((SMOOTHED(14, 6) < SMOOTHED(-3, -6)) << 4) + ((SMOOTHED(-3, 0) < SMOOTHED(-15, 0)) << 3) + ((SMOOTHED(-9, 4) < SMOOTHED(-15, -9)) << 2) + ((SMOOTHED(-1, 11) < SMOOTHED(3, 11)) << 1) + ((SMOOTHED(-10, -16) < SMOOTHED(-7, 7)) << 0));
desc[26] = (uchar)(((SMOOTHED(-2, -10) < SMOOTHED(-10, -2)) << 7) + ((SMOOTHED(-5, -3) < SMOOTHED(5, -23)) << 6) + ((SMOOTHED(13, -8) < SMOOTHED(-15, -11)) << 5) + ((SMOOTHED(-15, 11) < SMOOTHED(6, -6)) << 4) + ((SMOOTHED(-16, -3) < SMOOTHED(-2, 2)) << 3) + ((SMOOTHED(6, 12) < SMOOTHED(-16, 24)) << 2) + ((SMOOTHED(-10, 0) < SMOOTHED(8, 11)) << 1) + ((SMOOTHED(-7, 7) < SMOOTHED(-19, -7)) << 0));
desc[27] = (uchar)(((SMOOTHED(5, 16) < SMOOTHED(9, -3)) << 7) + ((SMOOTHED(9, 7) < SMOOTHED(-7, -16)) << 6) + ((SMOOTHED(3, 2) < SMOOTHED(-10, 9)) << 5) + ((SMOOTHED(21, 1) < SMOOTHED(8, 7)) << 4) + ((SMOOTHED(7, 0) < SMOOTHED(1, 17)) << 3) + ((SMOOTHED(-8, 12) < SMOOTHED(9, 6)) << 2) + ((SMOOTHED(11, -7) < SMOOTHED(-8, -6)) << 1) + ((SMOOTHED(19, 0) < SMOOTHED(9, 3)) << 0));
desc[28] = (uchar)(((SMOOTHED(1, -7) < SMOOTHED(-5, -11)) << 7) + ((SMOOTHED(0, 8) < SMOOTHED(-2, 14)) << 6) + ((SMOOTHED(12, -2) < SMOOTHED(-15, -6)) << 5) + ((SMOOTHED(4, 12) < SMOOTHED(0, -21)) << 4) + ((SMOOTHED(17, -4) < SMOOTHED(-6, -7)) << 3) + ((SMOOTHED(-10, -9) < SMOOTHED(-14, -7)) << 2) + ((SMOOTHED(-15, -10) < SMOOTHED(-15, -14)) << 1) + ((SMOOTHED(-7, -5) < SMOOTHED(5, -12)) << 0));
desc[29] = (uchar)(((SMOOTHED(-4, 0) < SMOOTHED(15, -4)) << 7) + ((SMOOTHED(5, 2) < SMOOTHED(-6, -23)) << 6) + ((SMOOTHED(-4, -21) < SMOOTHED(-6, 4)) << 5) + ((SMOOTHED(-10, 5) < SMOOTHED(-15, 6)) << 4) + ((SMOOTHED(4, -3) < SMOOTHED(-1, 5)) << 3) + ((SMOOTHED(-4, 19) < SMOOTHED(-23, -4)) << 2) + ((SMOOTHED(-4, 17) < SMOOTHED(13, -11)) << 1) + ((SMOOTHED(1, 12) < SMOOTHED(4, -14)) << 0));
desc[30] = (uchar)(((SMOOTHED(-11, -6) < SMOOTHED(-20, 10)) << 7) + ((SMOOTHED(4, 5) < SMOOTHED(3, 20)) << 6) + ((SMOOTHED(-8, -20) < SMOOTHED(3, 1)) << 5) + ((SMOOTHED(-19, 9) < SMOOTHED(9, -3)) << 4) + ((SMOOTHED(18, 15) < SMOOTHED(11, -4)) << 3) + ((SMOOTHED(12, 16) < SMOOTHED(8, 7)) << 2) + ((SMOOTHED(-14, -8) < SMOOTHED(-3, 9)) << 1) + ((SMOOTHED(-6, 0) < SMOOTHED(2, -4)) << 0));
desc[31] = (uchar)(((SMOOTHED(1, -10) < SMOOTHED(-1, 2)) << 7) + ((SMOOTHED(8, -7) < SMOOTHED(-6, 18)) << 6) + ((SMOOTHED(9, 12) < SMOOTHED(-7, -23)) << 5) + ((SMOOTHED(8, -6) < SMOOTHED(5, 2)) << 4) + ((SMOOTHED(-9, 6) < SMOOTHED(-12, -7)) << 3) + ((SMOOTHED(-1, -2) < SMOOTHED(-7, 2)) << 2) + ((SMOOTHED(9, 9) < SMOOTHED(7, 15)) << 1) + ((SMOOTHED(6, 2) < SMOOTHED(-6, 6)) << 0));
desc[32] = (uchar)(((SMOOTHED(16, 12) < SMOOTHED(0, 19)) << 7) + ((SMOOTHED(4, 3) < SMOOTHED(6, 0)) << 6) + ((SMOOTHED(-2, -1) < SMOOTHED(2, 17)) << 5) + ((SMOOTHED(8, 1) < SMOOTHED(3, 1)) << 4) + ((SMOOTHED(-12, -1) < SMOOTHED(-11, 0)) << 3) + ((SMOOTHED(-11, 2) < SMOOTHED(7, 9)) << 2) + ((SMOOTHED(-1, 3) < SMOOTHED(-19, 4)) << 1) + ((SMOOTHED(-1, -11) < SMOOTHED(-1, 3)) << 0));
desc[33] = (uchar)(((SMOOTHED(1, -10) < SMOOTHED(-10, -4)) << 7) + ((SMOOTHED(-2, 3) < SMOOTHED(6, 11)) << 6) + ((SMOOTHED(3, 7) < SMOOTHED(-9, -8)) << 5) + ((SMOOTHED(24, -14) < SMOOTHED(-2, -10)) << 4) + ((SMOOTHED(-3, -3) < SMOOTHED(-18, -6)) << 3) + ((SMOOTHED(-13, -10) < SMOOTHED(-7, -1)) << 2) + ((SMOOTHED(2, -7) < SMOOTHED(9, -6)) << 1) + ((SMOOTHED(2, -4) < SMOOTHED(6, -13)) << 0));
desc[34] = (uchar)(((SMOOTHED(4, -4) < SMOOTHED(-2, 3)) << 7) + ((SMOOTHED(-4, 2) < SMOOTHED(9, 13)) << 6) + ((SMOOTHED(-11, 5) < SMOOTHED(-6, -11)) << 5) + ((SMOOTHED(4, -2) < SMOOTHED(11, -9)) << 4) + ((SMOOTHED(-19, 0) < SMOOTHED(-23, -5)) << 3) + ((SMOOTHED(-5, -7) < SMOOTHED(-3, -6)) << 2) + ((SMOOTHED(-6, -4) < SMOOTHED(12, 14)) << 1) + ((SMOOTHED(12, -11) < SMOOTHED(-8, -16)) << 0));
desc[35] = (uchar)(((SMOOTHED(-21, 15) < SMOOTHED(-12, 6)) << 7) + ((SMOOTHED(-2, -1) < SMOOTHED(-8, 16)) << 6) + ((SMOOTHED(6, -1) < SMOOTHED(-8, -2)) << 5) + ((SMOOTHED(1, -1) < SMOOTHED(-9, 8)) << 4) + ((SMOOTHED(3, -4) < SMOOTHED(-2, -2)) << 3) + ((SMOOTHED(-7, 0) < SMOOTHED(4, -8)) << 2) + ((SMOOTHED(11, -11) < SMOOTHED(-12, 2)) << 1) + ((SMOOTHED(2, 3) < SMOOTHED(11, 7)) << 0));
desc[36] = (uchar)(((SMOOTHED(-7, -4) < SMOOTHED(-9, -6)) << 7) + ((SMOOTHED(3, -7) < SMOOTHED(-5, 0)) << 6) + ((SMOOTHED(3, -7) < SMOOTHED(-10, -5)) << 5) + ((SMOOTHED(-3, -1) < SMOOTHED(8, -10)) << 4) + ((SMOOTHED(0, 8) < SMOOTHED(5, 1)) << 3) + ((SMOOTHED(9, 0) < SMOOTHED(1, 16)) << 2) + ((SMOOTHED(8, 4) < SMOOTHED(-11, -3)) << 1) + ((SMOOTHED(-15, 9) < SMOOTHED(8, 17)) << 0));
desc[37] = (uchar)(((SMOOTHED(0, 2) < SMOOTHED(-9, 17)) << 7) + ((SMOOTHED(-6, -11) < SMOOTHED(-10, -3)) << 6) + ((SMOOTHED(1, 1) < SMOOTHED(15, -8)) << 5) + ((SMOOTHED(-12, -13) < SMOOTHED(-2, 4)) << 4) + ((SMOOTHED(-6, 4) < SMOOTHED(-6, -10)) << 3) + ((SMOOTHED(5, -7) < SMOOTHED(7, -5)) << 2) + ((SMOOTHED(10, 6) < SMOOTHED(8, 9)) << 1) + ((SMOOTHED(-5, 7) < SMOOTHED(-18, -3)) << 0));
desc[38] = (uchar)(((SMOOTHED(-6, 3) < SMOOTHED(5, 4)) << 7) + ((SMOOTHED(-10, -13) < SMOOTHED(-5, -3)) << 6) + ((SMOOTHED(-11, 2) < SMOOTHED(-16, 0)) << 5) + ((SMOOTHED(7, -21) < SMOOTHED(-5, -13)) << 4) + ((SMOOTHED(-14, -14) < SMOOTHED(-4, -4)) << 3) + ((SMOOTHED(4, 9) < SMOOTHED(7, -3)) << 2) + ((SMOOTHED(4, 11) < SMOOTHED(10, -4)) << 1) + ((SMOOTHED(6, 17) < SMOOTHED(9, 17)) << 0));
desc[39] = (uchar)(((SMOOTHED(-10, 8) < SMOOTHED(0, -11)) << 7) + ((SMOOTHED(-6, -16) < SMOOTHED(-6, 8)) << 6) + ((SMOOTHED(-13, 5) < SMOOTHED(10, -5)) << 5) + ((SMOOTHED(3, 2) < SMOOTHED(12, 16)) << 4) + ((SMOOTHED(13, -8) < SMOOTHED(0, -6)) << 3) + ((SMOOTHED(10, 0) < SMOOTHED(4, -11)) << 2) + ((SMOOTHED(8, 5) < SMOOTHED(10, -2)) << 1) + ((SMOOTHED(11, -7) < SMOOTHED(-13, 3)) << 0));
desc[40] = (uchar)(((SMOOTHED(2, 4) < SMOOTHED(-7, -3)) << 7) + ((SMOOTHED(-14, -2) < SMOOTHED(-11, 16)) << 6) + ((SMOOTHED(11, -6) < SMOOTHED(7, 6)) << 5) + ((SMOOTHED(-3, 15) < SMOOTHED(8, -10)) << 4) + ((SMOOTHED(-3, 8) < SMOOTHED(12, -12)) << 3) + ((SMOOTHED(-13, 6) < SMOOTHED(-14, 7)) << 2) + ((SMOOTHED(-11, -5) < SMOOTHED(-8, -6)) << 1) + ((SMOOTHED(7, -6) < SMOOTHED(6, 3)) << 0));
desc[41] = (uchar)(((SMOOTHED(-4, 10) < SMOOTHED(5, 1)) << 7) + ((SMOOTHED(9, 16) < SMOOTHED(10, 13)) << 6) + ((SMOOTHED(-17, 10) < SMOOTHED(2, 8)) << 5) + ((SMOOTHED(-5, 1) < SMOOTHED(4, -4)) << 4) + ((SMOOTHED(-14, 8) < SMOOTHED(-5, 2)) << 3) + ((SMOOTHED(4, -9) < SMOOTHED(-6, -3)) << 2) + ((SMOOTHED(3, -7) < SMOOTHED(-10, 0)) << 1) + ((SMOOTHED(-2, -8) < SMOOTHED(-10, 4)) << 0));
desc[42] = (uchar)(((SMOOTHED(-8, 5) < SMOOTHED(-9, 24)) << 7) + ((SMOOTHED(2, -8) < SMOOTHED(8, -9)) << 6) + ((SMOOTHED(-4, 17) < SMOOTHED(-5, 2)) << 5) + ((SMOOTHED(14, 0) < SMOOTHED(-9, 9)) << 4) + ((SMOOTHED(11, 15) < SMOOTHED(-6, 5)) << 3) + ((SMOOTHED(-8, 1) < SMOOTHED(-3, 4)) << 2) + ((SMOOTHED(9, -21) < SMOOTHED(10, 2)) << 1) + ((SMOOTHED(2, -1) < SMOOTHED(4, 11)) << 0));
desc[43] = (uchar)(((SMOOTHED(24, 3) < SMOOTHED(2, -2)) << 7) + ((SMOOTHED(-8, 17) < SMOOTHED(-14, -10)) << 6) + ((SMOOTHED(6, 5) < SMOOTHED(-13, 7)) << 5) + ((SMOOTHED(11, 10) < SMOOTHED(0, -1)) << 4) + ((SMOOTHED(4, 6) < SMOOTHED(-10, 6)) << 3) + ((SMOOTHED(-12, -2) < SMOOTHED(5, 6)) << 2) + ((SMOOTHED(3, -1) < SMOOTHED(8, -15)) << 1) + ((SMOOTHED(1, -4) < SMOOTHED(-7, 11)) << 0));
desc[44] = (uchar)(((SMOOTHED(1, 11) < SMOOTHED(5, 0)) << 7) + ((SMOOTHED(6, -12) < SMOOTHED(10, 1)) << 6) + ((SMOOTHED(-3, -2) < SMOOTHED(-1, 4)) << 5) + ((SMOOTHED(-2, -11) < SMOOTHED(-1, 12)) << 4) + ((SMOOTHED(7, -8) < SMOOTHED(-20, -18)) << 3) + ((SMOOTHED(2, 0) < SMOOTHED(-9, 2)) << 2) + ((SMOOTHED(-13, -1) < SMOOTHED(-16, 2)) << 1) + ((SMOOTHED(3, -1) < SMOOTHED(-5, -17)) << 0));
desc[45] = (uchar)(((SMOOTHED(15, 8) < SMOOTHED(3, -14)) << 7) + ((SMOOTHED(-13, -12) < SMOOTHED(6, 15)) << 6) + ((SMOOTHED(2, -8) < SMOOTHED(2, 6)) << 5) + ((SMOOTHED(6, 22) < SMOOTHED(-3, -23)) << 4) + ((SMOOTHED(-2, -7) < SMOOTHED(-6, 0)) << 3) + ((SMOOTHED(13, -10) < SMOOTHED(-6, 6)) << 2) + ((SMOOTHED(6, 7) < SMOOTHED(-10, 12)) << 1) + ((SMOOTHED(-6, 7) < SMOOTHED(-2, 11)) << 0));
desc[46] = (uchar)(((SMOOTHED(0, -22) < SMOOTHED(-2, -17)) << 7) + ((SMOOTHED(-4, -1) < SMOOTHED(-11, -14)) << 6) + ((SMOOTHED(-2, -8) < SMOOTHED(7, 12)) << 5) + ((SMOOTHED(12, -5) < SMOOTHED(7, -13)) << 4) + ((SMOOTHED(2, -2) < SMOOTHED(-7, 6)) << 3) + ((SMOOTHED(0, 8) < SMOOTHED(-3, 23)) << 2) + ((SMOOTHED(6, 12) < SMOOTHED(13, -11)) << 1) + ((SMOOTHED(-21, -10) < SMOOTHED(10, 8)) << 0));
desc[47] = (uchar)(((SMOOTHED(-3, 0) < SMOOTHED(7, 15)) << 7) + ((SMOOTHED(7, -6) < SMOOTHED(-5, -12)) << 6) + ((SMOOTHED(-21, -10) < SMOOTHED(12, -11)) << 5) + ((SMOOTHED(-5, -11) < SMOOTHED(8, -11)) << 4) + ((SMOOTHED(5, 0) < SMOOTHED(-11, -1)) << 3) + ((SMOOTHED(8, -9) < SMOOTHED(7, -1)) << 2) + ((SMOOTHED(11, -23) < SMOOTHED(21, -5)) << 1) + ((SMOOTHED(0, -5) < SMOOTHED(-8, 6)) << 0));
desc[48] = (uchar)(((SMOOTHED(-6, 8) < SMOOTHED(8, 12)) << 7) + ((SMOOTHED(-7, 5) < SMOOTHED(3, -2)) << 6) + ((SMOOTHED(-5, -20) < SMOOTHED(-12, 9)) << 5) + ((SMOOTHED(-6, 12) < SMOOTHED(-11, 3)) << 4) + ((SMOOTHED(4, 5) < SMOOTHED(13, 11)) << 3) + ((SMOOTHED(2, 12) < SMOOTHED(13, -12)) << 2) + ((SMOOTHED(-4, -13) < SMOOTHED(4, 7)) << 1) + ((SMOOTHED(0, 15) < SMOOTHED(-3, -16)) << 0));
desc[49] = (uchar)(((SMOOTHED(-3, 2) < SMOOTHED(-2, 14)) << 7) + ((SMOOTHED(4, -14) < SMOOTHED(16, -11)) << 6) + ((SMOOTHED(-13, 3) < SMOOTHED(23, 10)) << 5) + ((SMOOTHED(9, -19) < SMOOTHED(2, 5)) << 4) + ((SMOOTHED(5, 3) < SMOOTHED(14, -7)) << 3) + ((SMOOTHED(19, -13) < SMOOTHED(-11, 15)) << 2) + ((SMOOTHED(14, 0) < SMOOTHED(-2, -5)) << 1) + ((SMOOTHED(11, -4) < SMOOTHED(0, -6)) << 0));
desc[50] = (uchar)(((SMOOTHED(-2, 5) < SMOOTHED(-13, -8)) << 7) + ((SMOOTHED(-11, -15) < SMOOTHED(-7, -17)) << 6) + ((SMOOTHED(1, 3) < SMOOTHED(-10, -8)) << 5) + ((SMOOTHED(-13, -10) < SMOOTHED(7, -12)) << 4) + ((SMOOTHED(0, -13) < SMOOTHED(23, -6)) << 3) + ((SMOOTHED(2, -17) < SMOOTHED(-7, -3)) << 2) + ((SMOOTHED(1, 3) < SMOOTHED(4, -10)) << 1) + ((SMOOTHED(13, 4) < SMOOTHED(14, -6)) << 0));
desc[51] = (uchar)(((SMOOTHED(-19, -2) < SMOOTHED(-1, 5)) << 7) + ((SMOOTHED(9, -8) < SMOOTHED(10, -5)) << 6) + ((SMOOTHED(7, -1) < SMOOTHED(5, 7)) << 5) + ((SMOOTHED(9, -10) < SMOOTHED(19, 0)) << 4) + ((SMOOTHED(7, 5) < SMOOTHED(-4, -7)) << 3) + ((SMOOTHED(-11, 1) < SMOOTHED(-1, -11)) << 2) + ((SMOOTHED(2, -1) < SMOOTHED(-4, 11)) << 1) + ((SMOOTHED(-1, 7) < SMOOTHED(2, -2)) << 0));
desc[52] = (uchar)(((SMOOTHED(1, -20) < SMOOTHED(-9, -6)) << 7) + ((SMOOTHED(-4, -18) < SMOOTHED(8, -18)) << 6) + ((SMOOTHED(-16, -2) < SMOOTHED(7, -6)) << 5) + ((SMOOTHED(-3, -6) < SMOOTHED(-1, -4)) << 4) + ((SMOOTHED(0, -16) < SMOOTHED(24, -5)) << 3) + ((SMOOTHED(-4, -2) < SMOOTHED(-1, 9)) << 2) + ((SMOOTHED(-8, 2) < SMOOTHED(-6, 15)) << 1) + ((SMOOTHED(11, 4) < SMOOTHED(0, -3)) << 0));
desc[53] = (uchar)(((SMOOTHED(7, 6) < SMOOTHED(2, -10)) << 7) + ((SMOOTHED(-7, -9) < SMOOTHED(12, -6)) << 6) + ((SMOOTHED(24, 15) < SMOOTHED(-8, -1)) << 5) + ((SMOOTHED(15, -9) < SMOOTHED(-3, -15)) << 4) + ((SMOOTHED(17, -5) < SMOOTHED(11, -10)) << 3) + ((SMOOTHED(-2, 13) < SMOOTHED(-15, 4)) << 2) + ((SMOOTHED(-2, -1) < SMOOTHED(4, -23)) << 1) + ((SMOOTHED(-16, 3) < SMOOTHED(-7, -14)) << 0));
desc[54] = (uchar)(((SMOOTHED(-3, -5) < SMOOTHED(-10, -9)) << 7) + ((SMOOTHED(-5, 3) < SMOOTHED(-2, -1)) << 6) + ((SMOOTHED(-1, 4) < SMOOTHED(1, 8)) << 5) + ((SMOOTHED(12, 9) < SMOOTHED(9, -14)) << 4) + ((SMOOTHED(-9, 17) < SMOOTHED(-3, 0)) << 3) + ((SMOOTHED(5, 4) < SMOOTHED(13, -6)) << 2) + ((SMOOTHED(-1, -8) < SMOOTHED(19, 10)) << 1) + ((SMOOTHED(8, -5) < SMOOTHED(-15, 2)) << 0));
desc[55] = (uchar)(((SMOOTHED(-12, -9) < SMOOTHED(-4, -5)) << 7) + ((SMOOTHED(12, 0) < SMOOTHED(24, 4)) << 6) + ((SMOOTHED(8, -2) < SMOOTHED(14, 4)) << 5) + ((SMOOTHED(8, -4) < SMOOTHED(-7, 16)) << 4) + ((SMOOTHED(5, -1) < SMOOTHED(-8, -4)) << 3) + ((SMOOTHED(-2, 18) < SMOOTHED(-5, 17)) << 2) + ((SMOOTHED(8, -2) < SMOOTHED(-9, -2)) << 1) + ((SMOOTHED(3, -7) < SMOOTHED(1, -6)) << 0));
desc[56] = (uchar)(((SMOOTHED(-5, -22) < SMOOTHED(-5, -2)) << 7) + ((SMOOTHED(-8, -10) < SMOOTHED(14, 1)) << 6) + ((SMOOTHED(-3, -13) < SMOOTHED(3, 9)) << 5) + ((SMOOTHED(-4, -1) < SMOOTHED(-1, 0)) << 4) + ((SMOOTHED(-7, -21) < SMOOTHED(12, -19)) << 3) + ((SMOOTHED(-8, 8) < SMOOTHED(24, 8)) << 2) + ((SMOOTHED(12, -6) < SMOOTHED(-2, 3)) << 1) + ((SMOOTHED(-5, -11) < SMOOTHED(-22, -4)) << 0));
desc[57] = (uchar)(((SMOOTHED(-3, 5) < SMOOTHED(-4, 4)) << 7) + ((SMOOTHED(-16, 24) < SMOOTHED(7, -9)) << 6) + ((SMOOTHED(-10, 23) < SMOOTHED(-9, 18)) << 5) + ((SMOOTHED(1, 12) < SMOOTHED(17, 21)) << 4) + ((SMOOTHED(24, -6) < SMOOTHED(-3, -11)) << 3) + ((SMOOTHED(-7, 17) < SMOOTHED(1, -6)) << 2) + ((SMOOTHED(4, 4) < SMOOTHED(2, -7)) << 1) + ((SMOOTHED(14, 6) < SMOOTHED(-12, 3)) << 0));
desc[58] = (uchar)(((SMOOTHED(-6, 0) < SMOOTHED(-16, 13)) << 7) + ((SMOOTHED(-10, 5) < SMOOTHED(7, 12)) << 6) + ((SMOOTHED(5, 2) < SMOOTHED(6, -3)) << 5) + ((SMOOTHED(7, 0) < SMOOTHED(-23, 1)) << 4) + ((SMOOTHED(15, -5) < SMOOTHED(1, 14)) << 3) + ((SMOOTHED(-3, -1) < SMOOTHED(6, 6)) << 2) + ((SMOOTHED(6, -9) < SMOOTHED(-9, 12)) << 1) + ((SMOOTHED(4, -2) < SMOOTHED(-4, 7)) << 0));
desc[59] = (uchar)(((SMOOTHED(-4, -5) < SMOOTHED(4, 4)) << 7) + ((SMOOTHED(-13, 0) < SMOOTHED(6, -10)) << 6) + ((SMOOTHED(2, -12) < SMOOTHED(-6, -3)) << 5) + ((SMOOTHED(16, 0) < SMOOTHED(-3, 3)) << 4) + ((SMOOTHED(5, -14) < SMOOTHED(6, 11)) << 3) + ((SMOOTHED(5, 11) < SMOOTHED(0, -13)) << 2) + ((SMOOTHED(7, 5) < SMOOTHED(-1, -5)) << 1) + ((SMOOTHED(12, 4) < SMOOTHED(6, 10)) << 0));
desc[60] = (uchar)(((SMOOTHED(-10, 4) < SMOOTHED(-1, -11)) << 7) + ((SMOOTHED(4, 10) < SMOOTHED(-14, 5)) << 6) + ((SMOOTHED(11, -14) < SMOOTHED(-13, 0)) << 5) + ((SMOOTHED(2, 8) < SMOOTHED(12, 24)) << 4) + ((SMOOTHED(-1, 3) < SMOOTHED(-1, 2)) << 3) + ((SMOOTHED(9, -14) < SMOOTHED(-23, 3)) << 2) + ((SMOOTHED(-8, -6) < SMOOTHED(0, 9)) << 1) + ((SMOOTHED(-15, 14) < SMOOTHED(10, -10)) << 0));
desc[61] = (uchar)(((SMOOTHED(-10, -6) < SMOOTHED(-7, -5)) << 7) + ((SMOOTHED(11, 5) < SMOOTHED(-3, -15)) << 6) + ((SMOOTHED(1, 0) < SMOOTHED(1, 8)) << 5) + ((SMOOTHED(-11, -6) < SMOOTHED(-4, -18)) << 4) + ((SMOOTHED(9, 0) < SMOOTHED(22, -4)) << 3) + ((SMOOTHED(-5, -1) < SMOOTHED(-9, 4)) << 2) + ((SMOOTHED(-20, 2) < SMOOTHED(1, 6)) << 1) + ((SMOOTHED(1, 2) < SMOOTHED(-9, -12)) << 0));
desc[62] = (uchar)(((SMOOTHED(5, 15) < SMOOTHED(4, -6)) << 7) + ((SMOOTHED(19, 4) < SMOOTHED(4, 11)) << 6) + ((SMOOTHED(17, -4) < SMOOTHED(-8, -1)) << 5) + ((SMOOTHED(-8, -12) < SMOOTHED(7, -3)) << 4) + ((SMOOTHED(11, 9) < SMOOTHED(8, 1)) << 3) + ((SMOOTHED(9, 22) < SMOOTHED(-15, 15)) << 2) + ((SMOOTHED(-7, -7) < SMOOTHED(1, -23)) << 1) + ((SMOOTHED(-5, 13) < SMOOTHED(-8, 2)) << 0));
desc[63] = (uchar)(((SMOOTHED(3, -5) < SMOOTHED(11, -11)) << 7) + ((SMOOTHED(3, -18) < SMOOTHED(14, -5)) << 6) + ((SMOOTHED(-20, 7) < SMOOTHED(-10, -23)) << 5) + ((SMOOTHED(-2, -5) < SMOOTHED(6, 0)) << 4) + ((SMOOTHED(-17, -13) < SMOOTHED(-3, 2)) << 3) + ((SMOOTHED(-6, -1) < SMOOTHED(14, -2)) << 2) + ((SMOOTHED(-12, -16) < SMOOTHED(15, 6)) << 1) + ((SMOOTHED(-12, -2) < SMOOTHED(3, -19)) << 0));
#undef SMOOTHED
+460
View File
@@ -0,0 +1,460 @@
// 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 is the implementation of the paper
* GMS: Grid-based Motion Statistics for Fast, Ultra-robust Feature Correspondence.
* JiaWang Bian, Wen-Yan Lin, Yasuyuki Matsushita, Sai-Kit Yeung, Tan Dat Nguyen, Ming-Ming Cheng
* IEEE CVPR, 2017
* ProjectPage: http://jwbian.net/gms
*********************************************************************/
#include "precomp.hpp"
#include <algorithm>
using namespace std;
namespace cv
{
namespace xfeatures2d
{
// 8 possible rotation and each one is 3 X 3
const int mRotationPatterns[8][9] = {
{
1,2,3,
4,5,6,
7,8,9
},
{
4,1,2,
7,5,3,
8,9,6
},
{
7,4,1,
8,5,2,
9,6,3
},
{
8,7,4,
9,5,1,
6,3,2
},
{
9,8,7,
6,5,4,
3,2,1
},
{
6,9,8,
3,5,7,
2,1,4
},
{
3,6,9,
2,5,8,
1,4,7
},
{
2,3,6,
1,5,9,
4,7,8
}
};
// 5 level scales
const double mScaleRatios[5] = { 1.0, 1.0 / 2, 1.0 / std::sqrt(2.0), std::sqrt(2.0), 2.0 };
class GMSMatcher
{
public:
// OpenCV Keypoints & Correspond Image Size & Nearest Neighbor Matches
GMSMatcher(const vector<KeyPoint>& vkp1, const Size& size1, const vector<KeyPoint>& vkp2, const Size& size2,
const vector<DMatch>& vDMatches, const double thresholdFactor) : mThresholdFactor(thresholdFactor)
{
// Input initialize
normalizePoints(vkp1, size1, mvP1);
normalizePoints(vkp2, size2, mvP2);
mNumberMatches = vDMatches.size();
convertMatches(vDMatches, mvMatches);
// Grid initialize
mGridSizeLeft = Size(20, 20);
mGridNumberLeft = mGridSizeLeft.width * mGridSizeLeft.height;
// Initialize the neighbor of left grid
mGridNeighborLeft = Mat::zeros(mGridNumberLeft, 9, CV_32SC1);
initalizeNeighbors(mGridNeighborLeft, mGridSizeLeft);
}
~GMSMatcher() {}
// Get Inlier Mask
// Return number of inliers
int getInlierMask(vector<bool> &vbInliers, const bool withRotation = false, const bool withScale = false);
private:
// Normalized Points
vector<Point2f> mvP1, mvP2;
// Matches
vector<pair<int, int> > mvMatches;
// Number of Matches
size_t mNumberMatches;
// Grid Size
Size mGridSizeLeft, mGridSizeRight;
int mGridNumberLeft;
int mGridNumberRight;
// x : left grid idx
// y : right grid idx
// value : how many matches from idx_left to idx_right
Mat mMotionStatistics;
//
vector<int> mNumberPointsInPerCellLeft;
// Inldex : grid_idx_left
// Value : grid_idx_right
vector<int> mCellPairs;
// Every Matches has a cell-pair
// first : grid_idx_left
// second : grid_idx_right
vector<pair<int, int> > mvMatchPairs;
// Inlier Mask for output
vector<bool> mvbInlierMask;
//
Mat mGridNeighborLeft;
Mat mGridNeighborRight;
double mThresholdFactor;
// Assign Matches to Cell Pairs
void assignMatchPairs(const int GridType);
void convertMatches(const vector<DMatch> &vDMatches, vector<pair<int, int> > &vMatches);
int getGridIndexLeft(const Point2f &pt, const int type);
int getGridIndexRight(const Point2f &pt);
vector<int> getNB9(const int idx, const Size& GridSize);
void initalizeNeighbors(Mat &neighbor, const Size& GridSize);
void normalizePoints(const vector<KeyPoint> &kp, const Size &size, vector<Point2f> &npts);
// Run
int run(const int rotationType);
void setScale(const int scale);
// Verify Cell Pairs
void verifyCellPairs(const int rotationType);
};
void GMSMatcher::assignMatchPairs(const int gridType)
{
for (size_t i = 0; i < mNumberMatches; i++)
{
Point2f &lp = mvP1[mvMatches[i].first];
Point2f &rp = mvP2[mvMatches[i].second];
int lgidx = mvMatchPairs[i].first = getGridIndexLeft(lp, gridType);
int rgidx = -1;
if (gridType == 1)
{
rgidx = mvMatchPairs[i].second = getGridIndexRight(rp);
}
else
{
rgidx = mvMatchPairs[i].second;
}
if (lgidx < 0 || rgidx < 0) continue;
mMotionStatistics.at<int>(lgidx, rgidx)++;
mNumberPointsInPerCellLeft[lgidx]++;
}
}
// Convert OpenCV DMatch to Match (pair<int, int>)
void GMSMatcher::convertMatches(const vector<DMatch> &vDMatches, vector<pair<int, int> > &vMatches)
{
vMatches.resize(mNumberMatches);
for (size_t i = 0; i < mNumberMatches; i++)
vMatches[i] = pair<int, int>(vDMatches[i].queryIdx, vDMatches[i].trainIdx);
}
int GMSMatcher::getGridIndexLeft(const Point2f &pt, const int type)
{
int x = 0, y = 0;
if (type == 1) {
x = cvFloor(pt.x * mGridSizeLeft.width);
y = cvFloor(pt.y * mGridSizeLeft.height);
}
if (type == 2) {
x = cvFloor(pt.x * mGridSizeLeft.width + 0.5);
y = cvFloor(pt.y * mGridSizeLeft.height);
}
if (type == 3) {
x = cvFloor(pt.x * mGridSizeLeft.width);
y = cvFloor(pt.y * mGridSizeLeft.height + 0.5);
}
if (type == 4) {
x = cvFloor(pt.x * mGridSizeLeft.width + 0.5);
y = cvFloor(pt.y * mGridSizeLeft.height + 0.5);
}
if (x >= mGridSizeLeft.width || y >= mGridSizeLeft.height)
return -1;
return x + y * mGridSizeLeft.width;
}
int GMSMatcher::getGridIndexRight(const Point2f &pt)
{
int x = cvFloor(pt.x * mGridSizeRight.width);
int y = cvFloor(pt.y * mGridSizeRight.height);
return x + y * mGridSizeRight.width;
}
int GMSMatcher::getInlierMask(vector<bool> &vbInliers, const bool withRotation, const bool withScale)
{
int max_inlier = 0;
if (!withScale && !withRotation)
{
setScale(0);
max_inlier = run(1);
vbInliers = mvbInlierMask;
return max_inlier;
}
if (withRotation && withScale)
{
for (int scale = 0; scale < 5; scale++)
{
setScale(scale);
for (int rotationType = 1; rotationType <= 8; rotationType++)
{
int num_inlier = run(rotationType);
if (num_inlier > max_inlier)
{
vbInliers = mvbInlierMask;
max_inlier = num_inlier;
}
}
}
return max_inlier;
}
if (withRotation && !withScale)
{
setScale(0);
for (int rotationType = 1; rotationType <= 8; rotationType++)
{
int num_inlier = run(rotationType);
if (num_inlier > max_inlier)
{
vbInliers = mvbInlierMask;
max_inlier = num_inlier;
}
}
return max_inlier;
}
if (!withRotation && withScale)
{
for (int scale = 0; scale < 5; scale++)
{
setScale(scale);
int num_inlier = run(1);
if (num_inlier > max_inlier)
{
vbInliers = mvbInlierMask;
max_inlier = num_inlier;
}
}
return max_inlier;
}
return max_inlier;
}
// Get Neighbor 9
vector<int> GMSMatcher::getNB9(const int idx, const Size& gridSize)
{
vector<int> NB9(9, -1);
int idx_x = idx % gridSize.width;
int idx_y = idx / gridSize.width;
for (int yi = -1; yi <= 1; yi++)
{
for (int xi = -1; xi <= 1; xi++)
{
int idx_xx = idx_x + xi;
int idx_yy = idx_y + yi;
if (idx_xx < 0 || idx_xx >= gridSize.width || idx_yy < 0 || idx_yy >= gridSize.height)
continue;
NB9[xi + 4 + yi * 3] = idx_xx + idx_yy * gridSize.width;
}
}
return NB9;
}
void GMSMatcher::initalizeNeighbors(Mat &neighbor, const Size& gridSize)
{
for (int i = 0; i < neighbor.rows; i++)
{
vector<int> NB9 = getNB9(i, gridSize);
int *data = neighbor.ptr<int>(i);
memcpy(data, &NB9[0], sizeof(int) * 9);
}
}
// Normalize Key Points to Range(0 - 1)
void GMSMatcher::normalizePoints(const vector<KeyPoint> &kp, const Size &size, vector<Point2f> &npts)
{
const size_t numP = kp.size();
const int width = size.width;
const int height = size.height;
npts.resize(numP);
for (size_t i = 0; i < numP; i++)
{
npts[i].x = kp[i].pt.x / width;
npts[i].y = kp[i].pt.y / height;
}
}
int GMSMatcher::run(const int rotationType)
{
mvbInlierMask.assign(mNumberMatches, false);
// Initialize Motion Statisctics
mMotionStatistics = Mat::zeros(mGridNumberLeft, mGridNumberRight, CV_32SC1);
mvMatchPairs.assign(mNumberMatches, pair<int, int>(0, 0));
for (int gridType = 1; gridType <= 4; gridType++)
{
// initialize
mMotionStatistics.setTo(0);
mCellPairs.assign(mGridNumberLeft, -1);
mNumberPointsInPerCellLeft.assign(mGridNumberLeft, 0);
assignMatchPairs(gridType);
verifyCellPairs(rotationType);
// Mark inliers
for (size_t i = 0; i < mNumberMatches; i++)
{
if (mvMatchPairs[i].first >= 0 && mCellPairs[mvMatchPairs[i].first] == mvMatchPairs[i].second)
mvbInlierMask[i] = true;
}
}
return (int) count(mvbInlierMask.begin(), mvbInlierMask.end(), true); //number of inliers
}
void GMSMatcher::setScale(const int scale)
{
// Set Scale
mGridSizeRight.width = cvRound(mGridSizeLeft.width * mScaleRatios[scale]);
mGridSizeRight.height = cvRound(mGridSizeLeft.height * mScaleRatios[scale]);
mGridNumberRight = mGridSizeRight.width * mGridSizeRight.height;
// Initialize the neighbor of right grid
mGridNeighborRight = Mat::zeros(mGridNumberRight, 9, CV_32SC1);
initalizeNeighbors(mGridNeighborRight, mGridSizeRight);
}
void GMSMatcher::verifyCellPairs(const int rotationType)
{
const int *CurrentRP = mRotationPatterns[rotationType - 1];
for (int i = 0; i < mGridNumberLeft; i++)
{
if (sum(mMotionStatistics.row(i))[0] == 0)
{
mCellPairs[i] = -1;
continue;
}
int max_number = 0;
for (int j = 0; j < mGridNumberRight; j++)
{
int *value = mMotionStatistics.ptr<int>(i);
if (value[j] > max_number)
{
mCellPairs[i] = j;
max_number = value[j];
}
}
int idx_grid_rt = mCellPairs[i];
const int *NB9_lt = mGridNeighborLeft.ptr<int>(i);
const int *NB9_rt = mGridNeighborRight.ptr<int>(idx_grid_rt);
int score = 0;
double thresh = 0;
int numpair = 0;
for (size_t j = 0; j < 9; j++)
{
int ll = NB9_lt[j];
int rr = NB9_rt[CurrentRP[j] - 1];
if (ll == -1 || rr == -1)
continue;
score += mMotionStatistics.at<int>(ll, rr);
thresh += mNumberPointsInPerCellLeft[ll];
numpair++;
}
thresh = mThresholdFactor * std::sqrt(thresh / numpair);
if (score < thresh)
mCellPairs[i] = -2;
}
}
void matchGMS( const Size& size1, const Size& size2, const vector<KeyPoint>& keypoints1, const vector<KeyPoint>& keypoints2,
const vector<DMatch>& matches1to2, vector<DMatch>& matchesGMS, const bool withRotation, const bool withScale,
const double thresholdFactor )
{
GMSMatcher gms(keypoints1, size1, keypoints2, size2, matches1to2, thresholdFactor);
vector<bool> inlierMask;
gms.getInlierMask(inlierMask, withRotation, withScale);
matchesGMS.clear();
for (size_t i = 0; i < inlierMask.size(); i++) {
if (inlierMask[i])
matchesGMS.push_back(matches1to2[i]);
}
}
} //namespace xfeatures2d
} //namespace cv
@@ -0,0 +1,599 @@
// 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"
namespace {
using namespace cv;
bool sort_func(KeyPoint kp1, KeyPoint kp2)
{
return (kp1.response > kp2.response);
}
class Pyramid
{
protected:
class Octave
{
public:
std::vector<Mat> layers;
Octave(std::vector<Mat> layers);
virtual ~Octave();
Mat getLayerAt(int i);
};
class DOGOctave
{
public:
std::vector<Mat> layers;
DOGOctave(std::vector<Mat> layers);
virtual ~DOGOctave();
Mat getLayerAt(int i);
};
private:
std::vector<Octave> octaves;
std::vector<DOGOctave> DOG_octaves;
void build(const Mat& img, bool DOG);
public:
class Params
{
public:
int octavesN;
int layersN;
float sigma0;
int omin;
float step;
Params(int octavesN, int layersN, float sigma0, int omin);
void clear();
};
Params params;
Pyramid(const Mat& img, int octavesN, int layersN = 2, float sigma0 = 1, int omin = 0,
bool DOG = false);
Mat getLayer(int octave, int layer);
Mat getDOGLayer(int octave, int layer);
float getSigma(int layer);
virtual ~Pyramid();
void clear();
};
/**
* Pyramid class constructor
* octavesN_: number of octaves
* layersN_: number of layers before subsampling layer
* sigma0_: starting sigma (depends on detector's type, i.e. SIFT sigma0 = 1.6, Harris sigma0 = 1)
* omin_: if omin<0 an octave is added before first octave. In this octave the image size is doubled
* _DOG: if true, a DOG pyramid is build
*/
Pyramid::Pyramid(const Mat & img, int octavesN_, int layersN_, float sigma0_, int omin_, bool _DOG) :
params(
//Need to set the octavesN parameter globally. See issue #1513
MIN(octavesN_, int(floor(log((double)MIN(img.size().width, img.size().height)) / log(2.0f)))),
layersN_,
sigma0_,
omin_
)
{
build(img, _DOG);
}
/**
* Build gaussian pyramid with layersN_ + 3 layers and 2^(1/layersN_) step between layers
* each octave is downsampled of a factor of 2
*/
void Pyramid::build(const Mat& img, bool DOG)
{
Size ksize(0, 0);
int gsize;
float sigma0 = params.sigma0;
float sigma = sigma0;
int layersN = params.layersN + 3;
int omin = params.omin;
float k = params.step;
/*layer to downsample*/
int down_lay = int(1 / log(k));
int octave, layer;
double sigmaN = 0.5;
std::vector<Mat> layers, DOG_layers;
/* standard deviation of current layer*/
float sigma_curr = sigma;
/* standard deviation of previous layer*/
float sigma_prev = sigma;
if (omin < 0)
{
omin = -1;
Mat tmp_img;
Mat blurred_img;
gsize = int(ceil(sigmaN * 3)) * 2 + 1;
GaussianBlur(img, blurred_img, Size(gsize,gsize), sigmaN);
resize(blurred_img, tmp_img, ksize, 2, 2, INTER_AREA);
layers.push_back(tmp_img);
for (layer = 1; layer < layersN; layer++)
{
sigma_curr = getSigma(layer);
sigma = sqrt(powf(sigma_curr, 2) - powf(sigma_prev, 2));
Mat prev_lay = layers[layer - 1], curr_lay, DOG_lay;
/* smoothing is applied on previous layer so sigma_curr^2 = sigma^2 + sigma_prev^2 */
gsize = int(ceil(sigma * 3)) * 2 + 1;
GaussianBlur(prev_lay, curr_lay, Size(gsize,gsize), sigma);
layers.push_back(curr_lay);
if (DOG)
{
absdiff(curr_lay, prev_lay, DOG_lay);
DOG_layers.push_back(DOG_lay);
}
sigma_prev = sigma_curr;
}
Octave tmp_oct(layers);
octaves.push_back(tmp_oct);
layers.clear();
if (DOG)
{
DOGOctave tmp_DOG_Oct(DOG_layers);
DOG_octaves.push_back(tmp_DOG_Oct);
DOG_layers.clear();
}
}
/* Presmoothing on first layer */
float sb = float(sigmaN) / powf(2.0f, (float) omin);
sigma = sigma0;
if (sigma0 > sb)
sigma = sqrt(sigma0 * sigma0 - sb * sb);
/*1° step on image*/
Mat tmpImg;
gsize = int(ceil(sigma * 3)) * 2 + 1;
GaussianBlur(img, tmpImg, Size(gsize,gsize), sigma);
layers.push_back(tmpImg);
/*for every octave build layers*/
sigma_prev = sigma;
for (octave = 0; octave < params.octavesN; octave++)
{
for (layer = 1; layer < layersN; layer++)
{
sigma_curr = getSigma(layer);
sigma = sqrt(powf(sigma_curr, 2) - powf(sigma_prev, 2));
Mat prev_lay = layers[layer - 1], curr_lay, DOG_lay;
gsize = int(ceil(sigma * 3)) * 2 + 1;
GaussianBlur(prev_lay, curr_lay, Size(gsize,gsize), sigma);
layers.push_back(curr_lay);
if (DOG)
{
absdiff(curr_lay, prev_lay, DOG_lay);
DOG_layers.push_back(DOG_lay);
}
sigma_prev = sigma_curr;
}
Mat resized_lay;
resize(layers[down_lay], resized_lay, ksize, 1.0f / 2, 1.0f / 2, INTER_AREA);
Octave tmp_oct(layers);
octaves.push_back(tmp_oct);
if (DOG)
{
DOGOctave tmp_DOG_Oct(DOG_layers);
DOG_octaves.push_back(tmp_DOG_Oct);
DOG_layers.clear();
}
sigma_curr = sigma_prev = sigma0;
layers.clear();
layers.push_back(resized_lay);
}
}
/**
* Return layer at indicated octave and layer numbers
*/
Mat Pyramid::getLayer(int octave, int layer)
{
return octaves[octave].getLayerAt(layer);
}
/**
* Return DOG layer at indicated octave and layer numbers
*/
Mat Pyramid::getDOGLayer(int octave, int layer)
{
CV_Assert(!DOG_octaves.empty());
return DOG_octaves[octave].getLayerAt(layer);
}
/**
* Return sigma value of indicated layer
* sigma value of layer is the same at each octave
* i.e. sigma of first layer at each octave is sigma0
*/
float Pyramid::getSigma(int layer)
{
return powf(params.step, float(layer)) * params.sigma0;
}
/**
* Destructor of Pyramid class
*/
Pyramid::~Pyramid()
{
clear();
}
/**
* Clear octaves and params
*/
void Pyramid::clear()
{
octaves.clear();
params.clear();
}
/**
* Params for Pyramid class
*
*/
Pyramid::Params::Params(int octavesN_, int layersN_, float sigma0_, int omin_) :
octavesN(octavesN_), layersN(layersN_), sigma0(sigma0_), omin(omin_)
{
CV_Assert(layersN > 0 && octavesN_>0);
step = powf(2, 1.0f / layersN);
}
/**
* Set to zero all params
*/
void Pyramid::Params::clear()
{
octavesN = 0;
layersN = 0;
sigma0 = 0;
omin = 0;
step = 0;
}
/**
* Create an Octave with layers
*/
Pyramid::Octave::Octave(std::vector<Mat> _layers) : layers(_layers) {}
/**
* Return the Octave's layer at index i
*/
Mat Pyramid::Octave::getLayerAt(int i)
{
CV_Assert(i < (int) layers.size());
return layers[i];
}
Pyramid::Octave::~Octave()
{
}
Pyramid::DOGOctave::DOGOctave(std::vector<Mat> _layers) : layers(_layers) {}
Pyramid::DOGOctave::~DOGOctave()
{
}
Mat Pyramid::DOGOctave::getLayerAt(int i)
{
CV_Assert(i < (int) layers.size());
return layers[i];
}
} // anonymous namespace
namespace cv
{
namespace xfeatures2d
{
/*
* HarrisLaplaceFeatureDetector_Impl
*/
class HarrisLaplaceFeatureDetector_Impl CV_FINAL : public HarrisLaplaceFeatureDetector
{
public:
HarrisLaplaceFeatureDetector_Impl(
int numOctaves=6,
float corn_thresh=0.01,
float DOG_thresh=0.01,
int maxCorners=5000,
int num_layers=4
);
void read( const FileNode& fn ) CV_OVERRIDE;
void write( FileStorage& fs ) const CV_OVERRIDE;
void setNumOctaves(int numOctaves_) CV_OVERRIDE {numOctaves = numOctaves_;}
int getNumOctaves() const CV_OVERRIDE {return numOctaves;}
void setCornThresh(float corn_thresh_) CV_OVERRIDE {corn_thresh = corn_thresh_;}
float getCornThresh() const CV_OVERRIDE {return corn_thresh;}
void setDOGThresh(float DOG_thresh_) CV_OVERRIDE {DOG_thresh = DOG_thresh_;}
float getDOGThresh() const CV_OVERRIDE {return DOG_thresh;}
void setMaxCorners(int maxCorners_) CV_OVERRIDE {maxCorners = maxCorners_;}
int getMaxCorners() const CV_OVERRIDE {return maxCorners;}
void setNumLayers(int num_layers_) CV_OVERRIDE {num_layers = num_layers_; CV_Assert(num_layers == 2 || num_layers==4);}
int getNumLayers() const CV_OVERRIDE {return num_layers;}
protected:
void detect( InputArray image, std::vector<KeyPoint>& keypoints, InputArray mask=noArray() ) CV_OVERRIDE;
int numOctaves;
float corn_thresh;
float DOG_thresh;
int maxCorners;
int num_layers;
};
Ptr<HarrisLaplaceFeatureDetector> HarrisLaplaceFeatureDetector::create(
int numOctaves,
float corn_thresh,
float DOG_thresh,
int maxCorners,
int num_layers)
{
return makePtr<HarrisLaplaceFeatureDetector_Impl>(numOctaves, corn_thresh, DOG_thresh, maxCorners, num_layers);
}
CV_WRAP String HarrisLaplaceFeatureDetector::getDefaultName() const
{
return (Feature2D::getDefaultName() + ".HARRIS-LAPLACE");
}
HarrisLaplaceFeatureDetector_Impl::HarrisLaplaceFeatureDetector_Impl(
int _numOctaves,
float _corn_thresh,
float _DOG_thresh,
int _maxCorners,
int _num_layers
) :
numOctaves(_numOctaves),
corn_thresh(_corn_thresh),
DOG_thresh(_DOG_thresh),
maxCorners(_maxCorners),
num_layers(_num_layers)
{
CV_Assert(num_layers == 2 || num_layers==4);
}
void HarrisLaplaceFeatureDetector_Impl::read (const FileNode& fn)
{
// if node is empty, keep previous value
if (!fn["numOctaves"].empty())
fn["numOctaves"] >> numOctaves;
if (!fn["corn_thresh"].empty())
fn["corn_thresh"] >> corn_thresh;
if (!fn["corn_thresh"].empty())
fn["DOG_thresh"] >> DOG_thresh;
if (!fn["maxCorners"].empty())
fn["maxCorners"] >> maxCorners;
if (!fn["num_layers"].empty())
fn["num_layers"] >> num_layers;
}
void HarrisLaplaceFeatureDetector_Impl::write (FileStorage& fs) const
{
if ( fs.isOpened() )
{
fs << "name" << getDefaultName();
fs << "numOctaves" << numOctaves;
fs << "corn_thresh" << corn_thresh;
fs << "DOG_thresh" << DOG_thresh;
fs << "maxCorners" << maxCorners;
fs << "num_layers" << num_layers;
}
}
/*
* Detect method
* The method detect Harris corners on scale space as described in
* "K. Mikolajczyk and C. Schmid.
* Scale & affine invariant interest point detectors.
* International Journal of Computer Vision, 2004"
*/
void HarrisLaplaceFeatureDetector_Impl::detect(InputArray img, std::vector<KeyPoint>& keypoints, InputArray msk )
{
Mat image = img.getMat();
if( image.empty() )
{
keypoints.clear();
return;
}
Mat mask = msk.getMat();
if( !mask.empty() )
{
CV_Assert(mask.type() == CV_8UC1);
CV_Assert(mask.size == image.size);
}
Mat_<float> dx2, dy2, dxy;
Mat Lx, Ly;
float si, sd;
int gsize;
Mat fimage;
image.convertTo(fimage, CV_32F, 1.f/255);
/*Build gaussian pyramid*/
Pyramid pyr(fimage, numOctaves, num_layers, 1, -1, true);
keypoints = std::vector<KeyPoint> (0);
/*Find Harris corners on each layer*/
//Use pyr.params.octavesN instead of numOctaves. See issue #1513
for (int octave = 0; octave <= pyr.params.octavesN; octave++)
{
for (int layer = 1; layer <= num_layers; layer++)
{
if (octave == 0)
layer = num_layers;
Mat Lxm2smooth, Lxmysmooth, Lym2smooth;
si = powf(2.f, layer / (float) num_layers);
sd = si * 0.7f;
Mat curr_layer;
if (num_layers == 4)
{
if (layer == 1)
{
Mat tmp = pyr.getLayer(octave - 1, num_layers - 1);
resize(tmp, curr_layer, Size(0, 0), 0.5, 0.5, INTER_AREA);
} else
curr_layer = pyr.getLayer(octave, layer - 2);
} else /*if num_layer==2*/
{
curr_layer = pyr.getLayer(octave, layer - 1);
}
/*Calculates second moment matrix*/
/*Derivatives*/
Sobel(curr_layer, Lx, CV_32F, 1, 0, 1);
Sobel(curr_layer, Ly, CV_32F, 0, 1, 1);
/*Normalization*/
Lx = Lx * sd;
Ly = Ly * sd;
Mat Lxm2 = Lx.mul(Lx);
Mat Lym2 = Ly.mul(Ly);
Mat Lxmy = Lx.mul(Ly);
gsize = int(ceil(si * 3)) * 2 + 1;
/*Convolution*/
GaussianBlur(Lxm2, Lxm2smooth, Size(gsize, gsize), si, si, BORDER_REPLICATE);
GaussianBlur(Lym2, Lym2smooth, Size(gsize, gsize), si, si, BORDER_REPLICATE);
GaussianBlur(Lxmy, Lxmysmooth, Size(gsize, gsize), si, si, BORDER_REPLICATE);
Mat cornern_mat(curr_layer.size(), CV_32F);
/*Calculates cornerness in each pixel of the image*/
for (int row = 0; row < curr_layer.rows; row++)
{
for (int col = 0; col < curr_layer.cols; col++)
{
float dx2f = Lxm2smooth.at<float> (row, col);
float dy2f = Lym2smooth.at<float> (row, col);
float dxyf = Lxmysmooth.at<float> (row, col);
float det = dx2f * dy2f - dxyf * dxyf;
float tr = dx2f + dy2f;
float cornerness = det - (0.04f * tr * tr);
cornern_mat.at<float> (row, col) = cornerness;
}
}
double maxVal = 0;
Mat corn_dilate;
/*Find max cornerness value and rejects all corners that are lower than a threshold*/
minMaxLoc(cornern_mat, 0, &maxVal, 0, 0);
threshold(cornern_mat, cornern_mat, maxVal * corn_thresh, 0, THRESH_TOZERO);
dilate(cornern_mat, corn_dilate, Mat());
Size imgsize = curr_layer.size();
/*Verify for each of the initial points whether the DoG attains a maximum at the scale of the point*/
Mat prevDOG, curDOG, succDOG;
prevDOG = pyr.getDOGLayer(octave, layer - 1);
curDOG = pyr.getDOGLayer(octave, layer);
succDOG = pyr.getDOGLayer(octave, layer + 1);
for (int y = 1; y < imgsize.height - 1; y++)
{
for (int x = 1; x < imgsize.width - 1; x++)
{
float val = cornern_mat.at<float> (y, x);
if (val != 0 && val == corn_dilate.at<float> (y, x))
{
float curVal = curDOG.at<float> (y, x);
float prevVal = prevDOG.at<float> (y, x);
float succVal = succDOG.at<float> (y, x);
KeyPoint kp(
Point2f(x * powf(2.0f, (float) octave - 1) + powf(2.0f, (float) octave - 1) / 2,
y * powf(2.0f, (float) octave - 1) + powf(2.0f, (float) octave - 1) / 2),
3 * powf(2.0f, (float) octave - 1) * si * 2, 0, val, octave);
if(!mask.empty() && mask.at<unsigned char>(int(kp.pt.y), int(kp.pt.x)) == 0)
{
// ignore keypoints where mask is zero
continue;
}
/*Check whether keypoint size is inside the image*/
float start_kp_x = kp.pt.x - kp.size / 2;
float start_kp_y = kp.pt.y - kp.size / 2;
float end_kp_x = start_kp_x + kp.size;
float end_kp_y = start_kp_y + kp.size;
if (curVal > prevVal && curVal > succVal && curVal >= DOG_thresh
&& start_kp_x > 0 && start_kp_y > 0 && end_kp_x < image.cols
&& end_kp_y < image.rows)
keypoints.push_back(kp);
}
}
}
}
}
/*Sort keypoints in decreasing cornerness order*/
sort(keypoints.begin(), keypoints.end(), sort_func);
for (size_t i = 1; i < keypoints.size(); i++)
{
float max_diff = powf(2, keypoints[i].octave + 1.f / 2);
if (keypoints[i].response == keypoints[i - 1].response && norm(
keypoints[i].pt - keypoints[i - 1].pt) <= max_diff)
{
float x = (keypoints[i].pt.x + keypoints[i - 1].pt.x) / 2;
float y = (keypoints[i].pt.y + keypoints[i - 1].pt.y) / 2;
keypoints[i].pt = Point2f(x, y);
--i;
keypoints.erase(keypoints.begin() + i);
}
}
/*Select strongest keypoints*/
if (maxCorners > 0 && maxCorners < (int) keypoints.size())
keypoints.resize(maxCorners);
}
}
}
+216
View File
@@ -0,0 +1,216 @@
/*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*/
/*
OpenCV wrapper of reference implementation of
[1] KAZE Features. Pablo F. Alcantarilla, Adrien Bartoli and Andrew J. Davison.
In European Conference on Computer Vision (ECCV), Fiorenze, Italy, October 2012
http://www.robesafe.com/personal/pablo.alcantarilla/papers/Alcantarilla12eccv.pdf
@author Eugene Khvedchenya <ekhvedchenya@gmail.com>
*/
#include "precomp.hpp"
#include "kaze/KAZEFeatures.h"
namespace cv
{
namespace xfeatures2d
{
class KAZE_Impl CV_FINAL : public KAZE
{
public:
KAZE_Impl(bool _extended, bool _upright, float _threshold, int _octaves,
int _sublevels, KAZE::DiffusivityType _diffusivity)
: extended(_extended)
, upright(_upright)
, threshold(_threshold)
, octaves(_octaves)
, sublevels(_sublevels)
, diffusivity(_diffusivity)
{
}
virtual ~KAZE_Impl() CV_OVERRIDE {}
void setExtended(bool extended_) CV_OVERRIDE { extended = extended_; }
bool getExtended() const CV_OVERRIDE { return extended; }
void setUpright(bool upright_) CV_OVERRIDE { upright = upright_; }
bool getUpright() const CV_OVERRIDE { return upright; }
void setThreshold(double threshold_) CV_OVERRIDE { threshold = (float)threshold_; }
double getThreshold() const CV_OVERRIDE { return threshold; }
void setNOctaves(int octaves_) CV_OVERRIDE { octaves = octaves_; }
int getNOctaves() const CV_OVERRIDE { return octaves; }
void setNOctaveLayers(int octaveLayers_) CV_OVERRIDE { sublevels = octaveLayers_; }
int getNOctaveLayers() const CV_OVERRIDE { return sublevels; }
void setDiffusivity(int diff_) CV_OVERRIDE{ diffusivity = diff_; }
int getDiffusivity() const CV_OVERRIDE{ return diffusivity; }
// returns the descriptor size in bytes
int descriptorSize() const CV_OVERRIDE
{
return extended ? 128 : 64;
}
// returns the descriptor type
int descriptorType() const CV_OVERRIDE
{
return CV_32F;
}
// returns the default norm type
int defaultNorm() const CV_OVERRIDE
{
return NORM_L2;
}
void detectAndCompute(InputArray image, InputArray mask,
std::vector<KeyPoint>& keypoints,
OutputArray descriptors,
bool useProvidedKeypoints) CV_OVERRIDE
{
CV_INSTRUMENT_REGION();
cv::Mat img = image.getMat();
if (img.channels() > 1)
cvtColor(image, img, COLOR_BGR2GRAY);
Mat img1_32;
if ( img.depth() == CV_32F )
img1_32 = img;
else if ( img.depth() == CV_8U )
img.convertTo(img1_32, CV_32F, 1.0 / 255.0, 0);
else if ( img.depth() == CV_16U )
img.convertTo(img1_32, CV_32F, 1.0 / 65535.0, 0);
CV_Assert( ! img1_32.empty() );
KAZEOptions options;
options.img_width = img.cols;
options.img_height = img.rows;
options.extended = extended;
options.upright = upright;
options.dthreshold = threshold;
options.omax = octaves;
options.nsublevels = sublevels;
options.diffusivity = diffusivity;
KAZEFeatures impl(options);
impl.Create_Nonlinear_Scale_Space(img1_32);
if (!useProvidedKeypoints)
{
impl.Feature_Detection(keypoints);
}
if (!mask.empty())
{
cv::KeyPointsFilter::runByPixelsMask(keypoints, mask.getMat());
}
if( descriptors.needed() )
{
Mat desc;
impl.Feature_Description(keypoints, desc);
desc.copyTo(descriptors);
CV_Assert((!desc.rows || desc.cols == descriptorSize()));
CV_Assert((!desc.rows || (desc.type() == descriptorType())));
}
}
void write(FileStorage& fs) const CV_OVERRIDE
{
writeFormat(fs);
fs << "name" << getDefaultName();
fs << "extended" << (int)extended;
fs << "upright" << (int)upright;
fs << "threshold" << threshold;
fs << "octaves" << octaves;
fs << "sublevels" << sublevels;
fs << "diffusivity" << diffusivity;
}
void read(const FileNode& fn) CV_OVERRIDE
{
// if node is empty, keep previous value
if (!fn["extended"].empty())
extended = (int)fn["extended"] != 0;
if (!fn["upright"].empty())
upright = (int)fn["upright"] != 0;
if (!fn["threshold"].empty())
threshold = (float)fn["threshold"];
if (!fn["octaves"].empty())
octaves = (int)fn["octaves"];
if (!fn["sublevels"].empty())
sublevels = (int)fn["sublevels"];
if (!fn["diffusivity"].empty())
diffusivity = static_cast<KAZE::DiffusivityType>((int)fn["diffusivity"]);
}
bool extended;
bool upright;
float threshold;
int octaves;
int sublevels;
int diffusivity;
};
Ptr<KAZE> KAZE::create(bool extended, bool upright,
float threshold,
int octaves, int sublevels,
KAZE::DiffusivityType diffusivity)
{
return makePtr<KAZE_Impl>(extended, upright, threshold, octaves, sublevels, diffusivity);
}
String KAZE::getDefaultName() const
{
return (Feature2D::getDefaultName() + ".KAZE");
}
}
} // namespace cv
@@ -0,0 +1,69 @@
/**
* @file AKAZEConfig.h
* @brief AKAZE configuration file
* @date Feb 23, 2014
* @author Pablo F. Alcantarilla, Jesus Nuevo
*/
#ifndef __OPENCV_FEATURES_2D_AKAZE_CONFIG_H__
#define __OPENCV_FEATURES_2D_AKAZE_CONFIG_H__
namespace cv
{
namespace xfeatures2d
{
/* ************************************************************************* */
/// AKAZE configuration options structure
struct AKAZEOptions {
AKAZEOptions()
: omax(4)
, nsublevels(4)
, img_width(0)
, img_height(0)
, soffset(1.6f)
, derivative_factor(1.5f)
, sderivatives(1.0)
, diffusivity(KAZE::DIFF_PM_G2)
, dthreshold(0.001f)
, min_dthreshold(0.00001f)
, descriptor(AKAZE::DESCRIPTOR_MLDB)
, descriptor_size(0)
, descriptor_channels(3)
, descriptor_pattern_size(10)
, kcontrast(0.001f)
, kcontrast_percentile(0.7f)
, kcontrast_nbins(300)
{
}
int omax; ///< Maximum octave evolution of the image 2^sigma (coarsest scale sigma units)
int nsublevels; ///< Default number of sublevels per scale level
int img_width; ///< Width of the input image
int img_height; ///< Height of the input image
float soffset; ///< Base scale offset (sigma units)
float derivative_factor; ///< Factor for the multiscale derivatives
float sderivatives; ///< Smoothing factor for the derivatives
KAZE::DiffusivityType diffusivity; ///< Diffusivity type
float dthreshold; ///< Detector response threshold to accept point
float min_dthreshold; ///< Minimum detector threshold to accept a point
AKAZE::DescriptorType descriptor; ///< Type of descriptor
int descriptor_size; ///< Size of the descriptor in bits. 0->Full size
int descriptor_channels; ///< Number of channels in the descriptor (1, 2, 3)
int descriptor_pattern_size; ///< Actual patch size is 2*pattern_size*point.scale
float kcontrast; ///< The contrast factor parameter
float kcontrast_percentile; ///< Percentile level for the contrast factor
int kcontrast_nbins; ///< Number of bins for the contrast factor histogram
};
}
}
#endif
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,118 @@
/**
* @file AKAZE.h
* @brief Main class for detecting and computing binary descriptors in an
* accelerated nonlinear scale space
* @date Mar 27, 2013
* @author Pablo F. Alcantarilla, Jesus Nuevo
*/
#ifndef __OPENCV_FEATURES_2D_AKAZE_FEATURES_H__
#define __OPENCV_FEATURES_2D_AKAZE_FEATURES_H__
/* ************************************************************************* */
// Includes
#include "AKAZEConfig.h"
namespace cv
{
namespace xfeatures2d
{
/// A-KAZE nonlinear diffusion filtering evolution
template <typename MatType>
struct Evolution
{
Evolution() {
etime = 0.0f;
esigma = 0.0f;
octave = 0;
sublevel = 0;
sigma_size = 0;
octave_ratio = 0.0f;
border = 0;
}
template <typename T>
explicit Evolution(const Evolution<T> &other) {
size = other.size;
etime = other.etime;
esigma = other.esigma;
octave = other.octave;
sublevel = other.sublevel;
sigma_size = other.sigma_size;
octave_ratio = other.octave_ratio;
border = other.border;
other.Lx.copyTo(Lx);
other.Ly.copyTo(Ly);
other.Lt.copyTo(Lt);
other.Lsmooth.copyTo(Lsmooth);
other.Ldet.copyTo(Ldet);
}
MatType Lx, Ly; ///< First order spatial derivatives
MatType Lt; ///< Evolution image
MatType Lsmooth; ///< Smoothed image, used only for computing determinant, released afterwards
MatType Ldet; ///< Detector response
Size size; ///< Size of the layer
float etime; ///< Evolution time
float esigma; ///< Evolution sigma. For linear diffusion t = sigma^2 / 2
int octave; ///< Image octave
int sublevel; ///< Image sublevel in each octave
int sigma_size; ///< Integer esigma. For computing the feature detector responses
float octave_ratio; ///< Scaling ratio of this octave. ratio = 2^octave
int border; ///< Width of border where descriptors cannot be computed
};
typedef Evolution<Mat> MEvolution;
typedef Evolution<UMat> UEvolution;
typedef std::vector<MEvolution> Pyramid;
typedef std::vector<UEvolution> UMatPyramid;
/* ************************************************************************* */
// AKAZE Class Declaration
class AKAZEFeatures {
private:
AKAZEOptions options_; ///< Configuration options for AKAZE
Pyramid evolution_; ///< Vector of nonlinear diffusion evolution
/// FED parameters
int ncycles_; ///< Number of cycles
bool reordering_; ///< Flag for reordering time steps
std::vector<std::vector<float > > tsteps_; ///< Vector of FED dynamic time steps
std::vector<int> nsteps_; ///< Vector of number of steps per cycle
/// Matrices for the M-LDB descriptor computation
cv::Mat descriptorSamples_; // List of positions in the grids to sample LDB bits from.
cv::Mat descriptorBits_;
cv::Mat bitMask_;
/// Scale Space methods
void Allocate_Memory_Evolution();
void Find_Scale_Space_Extrema(std::vector<Mat>& keypoints_by_layers);
void Do_Subpixel_Refinement(std::vector<Mat>& keypoints_by_layers,
std::vector<KeyPoint>& kpts);
/// Feature description methods
void Compute_Keypoints_Orientation(std::vector<cv::KeyPoint>& kpts) const;
public:
/// Constructor with input arguments
AKAZEFeatures(const AKAZEOptions& options);
void Create_Nonlinear_Scale_Space(InputArray img);
void Feature_Detection(std::vector<cv::KeyPoint>& kpts);
void Compute_Descriptors(std::vector<cv::KeyPoint>& kpts, OutputArray desc);
};
/* ************************************************************************* */
/// Inline functions
void generateDescriptorSubsample(cv::Mat& sampleList, cv::Mat& comparisons,
int nbits, int pattern_size, int nchannels);
}
}
#endif
+60
View File
@@ -0,0 +1,60 @@
/**
* @file KAZEConfig.h
* @brief Configuration file
* @date Dec 27, 2011
* @author Pablo F. Alcantarilla
*/
#ifndef __OPENCV_FEATURES_2D_KAZE_CONFIG_H__
#define __OPENCV_FEATURES_2D_KAZE_CONFIG_H__
// OpenCV Includes
#include "../precomp.hpp"
#include <opencv2/features.hpp>
namespace cv
{
namespace xfeatures2d
{
//*************************************************************************************
struct KAZEOptions {
KAZEOptions()
: diffusivity(KAZE::DIFF_PM_G2)
, soffset(1.60f)
, omax(4)
, nsublevels(4)
, img_width(0)
, img_height(0)
, sderivatives(1.0f)
, dthreshold(0.001f)
, kcontrast(0.01f)
, kcontrast_percentille(0.7f)
, kcontrast_bins(300)
, upright(false)
, extended(false)
{
}
KAZE::DiffusivityType diffusivity;
float soffset;
int omax;
int nsublevels;
int img_width;
int img_height;
float sderivatives;
float dthreshold;
float kcontrast;
float kcontrast_percentille;
int kcontrast_bins;
bool upright;
bool extended;
};
}
}
#endif
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,67 @@
/**
* @file KAZE.h
* @brief Main program for detecting and computing descriptors in a nonlinear
* scale space
* @date Jan 21, 2012
* @author Pablo F. Alcantarilla
*/
#ifndef __OPENCV_FEATURES_2D_KAZE_FEATURES_H__
#define __OPENCV_FEATURES_2D_KAZE_FEATURES_H__
/* ************************************************************************* */
// Includes
#include "KAZEConfig.h"
#include "nldiffusion_functions.h"
#include "fed.h"
#include "TEvolution.h"
namespace cv
{
namespace xfeatures2d
{
/* ************************************************************************* */
// KAZE Class Declaration
class KAZEFeatures
{
private:
/// Parameters of the Nonlinear diffusion class
KAZEOptions options_; ///< Configuration options for KAZE
std::vector<TEvolution> evolution_; ///< Vector of nonlinear diffusion evolution
/// Vector of keypoint vectors for finding extrema in multiple threads
std::vector<std::vector<cv::KeyPoint> > kpts_par_;
/// FED parameters
int ncycles_; ///< Number of cycles
bool reordering_; ///< Flag for reordering time steps
std::vector<std::vector<float > > tsteps_; ///< Vector of FED dynamic time steps
std::vector<int> nsteps_; ///< Vector of number of steps per cycle
public:
/// Constructor
KAZEFeatures(KAZEOptions& options);
/// Public methods for KAZE interface
void Allocate_Memory_Evolution(void);
int Create_Nonlinear_Scale_Space(const cv::Mat& img);
void Feature_Detection(std::vector<cv::KeyPoint>& kpts);
void Feature_Description(std::vector<cv::KeyPoint>& kpts, cv::Mat& desc);
static void Compute_Main_Orientation(cv::KeyPoint& kpt, const std::vector<TEvolution>& evolution_, const KAZEOptions& options);
/// Feature Detection Methods
void Compute_KContrast(const cv::Mat& img, const float& kper);
void Compute_Multiscale_Derivatives(void);
void Compute_Detector_Response(void);
void Determinant_Hessian(std::vector<cv::KeyPoint>& kpts);
void Do_Subpixel_Refinement(std::vector<cv::KeyPoint>& kpts);
};
}
}
#endif
+5
View File
@@ -0,0 +1,5 @@
This folder contains both KAZE and AKAZE sources.
For license details, please refer to the following files:
- KAZE: LICENSE.KAZE
- AKAZE: LICENSE.AKAZE
@@ -0,0 +1,26 @@
Copyright (c) 2014, Pablo Fernandez Alcantarilla, Jesus Nuevo
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 copyright holders nor the names of its contributors
may be used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+26
View File
@@ -0,0 +1,26 @@
Copyright (c) 2012, Pablo Fernández Alcantarilla
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 copyright holders nor the names of its contributors
may be used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+41
View File
@@ -0,0 +1,41 @@
/**
* @file TEvolution.h
* @brief Header file with the declaration of the TEvolution struct
* @date Jun 02, 2014
* @author Pablo F. Alcantarilla
*/
#ifndef __OPENCV_FEATURES_2D_TEVOLUTION_H__
#define __OPENCV_FEATURES_2D_TEVOLUTION_H__
namespace cv
{
/* ************************************************************************* */
/// KAZE/A-KAZE nonlinear diffusion filtering evolution
struct TEvolution
{
TEvolution() {
etime = 0.0f;
esigma = 0.0f;
octave = 0;
sublevel = 0;
sigma_size = 0;
}
Mat Lx, Ly; ///< First order spatial derivatives
Mat Lxx, Lxy, Lyy; ///< Second order spatial derivatives
Mat Lt; ///< Evolution image
Mat Lsmooth; ///< Smoothed image
Mat Ldet; ///< Detector response
float etime; ///< Evolution time
float esigma; ///< Evolution sigma. For linear diffusion t = sigma^2 / 2
int octave; ///< Image octave
int sublevel; ///< Image sublevel in each octave
int sigma_size; ///< Integer esigma. For computing the feature detector responses
};
}
#endif
+192
View File
@@ -0,0 +1,192 @@
//=============================================================================
//
// fed.cpp
// Authors: Pablo F. Alcantarilla (1), Jesus Nuevo (2)
// Institutions: Georgia Institute of Technology (1)
// TrueVision Solutions (2)
// Date: 15/09/2013
// Email: pablofdezalc@gmail.com
//
// AKAZE Features Copyright 2013, Pablo F. Alcantarilla, Jesus Nuevo
// All Rights Reserved
// See LICENSE for the license information
//=============================================================================
/**
* @file fed.cpp
* @brief Functions for performing Fast Explicit Diffusion and building the
* nonlinear scale space
* @date Sep 15, 2013
* @author Pablo F. Alcantarilla, Jesus Nuevo
* @note This code is derived from FED/FJ library from Grewenig et al.,
* The FED/FJ library allows solving more advanced problems
* Please look at the following papers for more information about FED:
* [1] S. Grewenig, J. Weickert, C. Schroers, A. Bruhn. Cyclic Schemes for
* PDE-Based Image Analysis. Technical Report No. 327, Department of Mathematics,
* Saarland University, Saarbrücken, Germany, March 2013
* [2] S. Grewenig, J. Weickert, A. Bruhn. From box filtering to fast explicit diffusion.
* DAGM, 2010
*
*/
#include "../precomp.hpp"
#include "fed.h"
using namespace std;
//*************************************************************************************
//*************************************************************************************
/**
* @brief This function allocates an array of the least number of time steps such
* that a certain stopping time for the whole process can be obtained and fills
* it with the respective FED time step sizes for one cycle
* The function returns the number of time steps per cycle or 0 on failure
* @param T Desired process stopping time
* @param M Desired number of cycles
* @param tau_max Stability limit for the explicit scheme
* @param reordering Reordering flag
* @param tau The vector with the dynamic step sizes
*/
int fed_tau_by_process_time(const float& T, const int& M, const float& tau_max,
const bool& reordering, std::vector<float>& tau) {
// All cycles have the same fraction of the stopping time
return fed_tau_by_cycle_time(T/(float)M,tau_max,reordering,tau);
}
//*************************************************************************************
//*************************************************************************************
/**
* @brief This function allocates an array of the least number of time steps such
* that a certain stopping time for the whole process can be obtained and fills it
* it with the respective FED time step sizes for one cycle
* The function returns the number of time steps per cycle or 0 on failure
* @param t Desired cycle stopping time
* @param tau_max Stability limit for the explicit scheme
* @param reordering Reordering flag
* @param tau The vector with the dynamic step sizes
*/
int fed_tau_by_cycle_time(const float& t, const float& tau_max,
const bool& reordering, std::vector<float> &tau) {
int n = 0; // Number of time steps
float scale = 0.0; // Ratio of t we search to maximal t
// Compute necessary number of time steps
n = cvCeil(sqrtf(3.0f*t/tau_max+0.25f)-0.5f-1.0e-8f);
scale = 3.0f*t/(tau_max*(float)(n*(n+1)));
// Call internal FED time step creation routine
return fed_tau_internal(n,scale,tau_max,reordering,tau);
}
//*************************************************************************************
//*************************************************************************************
/**
* @brief This function allocates an array of time steps and fills it with FED
* time step sizes
* The function returns the number of time steps per cycle or 0 on failure
* @param n Number of internal steps
* @param scale Ratio of t we search to maximal t
* @param tau_max Stability limit for the explicit scheme
* @param reordering Reordering flag
* @param tau The vector with the dynamic step sizes
*/
int fed_tau_internal(const int& n, const float& scale, const float& tau_max,
const bool& reordering, std::vector<float> &tau) {
float c = 0.0, d = 0.0; // Time savers
vector<float> tauh; // Helper vector for unsorted taus
if (n <= 0) {
return 0;
}
// Allocate memory for the time step size
tau = vector<float>(n);
if (reordering) {
tauh = vector<float>(n);
}
// Compute time saver
c = 1.0f / (4.0f * (float)n + 2.0f);
d = scale * tau_max / 2.0f;
// Set up originally ordered tau vector
for (int k = 0; k < n; ++k) {
float h = cosf((float)CV_PI * (2.0f * (float)k + 1.0f) * c);
if (reordering) {
tauh[k] = d / (h * h);
}
else {
tau[k] = d / (h * h);
}
}
// Permute list of time steps according to chosen reordering function
int kappa = 0, prime = 0;
if (reordering == true) {
// Choose kappa cycle with k = n/2
// This is a heuristic. We can use Leja ordering instead!!
kappa = n / 2;
// Get modulus for permutation
prime = n + 1;
while (!fed_is_prime_internal(prime)) {
prime++;
}
// Perform permutation
for (int k = 0, l = 0; l < n; ++k, ++l) {
int index = 0;
while ((index = ((k+1)*kappa) % prime - 1) >= n) {
k++;
}
tau[l] = tauh[index];
}
}
return n;
}
//*************************************************************************************
//*************************************************************************************
/**
* @brief This function checks if a number is prime or not
* @param number Number to check if it is prime or not
* @return true if the number is prime
*/
bool fed_is_prime_internal(const int& number) {
bool is_prime = false;
if (number <= 1) {
return false;
}
else if (number == 1 || number == 2 || number == 3 || number == 5 || number == 7) {
return true;
}
else if ((number % 2) == 0 || (number % 3) == 0 || (number % 5) == 0 || (number % 7) == 0) {
return false;
}
else {
is_prime = true;
int upperLimit = (int)sqrt(1.0f + number);
int divisor = 11;
while (divisor <= upperLimit ) {
if (number % divisor == 0)
{
is_prime = false;
}
divisor +=2;
}
return is_prime;
}
}
+25
View File
@@ -0,0 +1,25 @@
#ifndef __OPENCV_FEATURES_2D_FED_H__
#define __OPENCV_FEATURES_2D_FED_H__
//******************************************************************************
//******************************************************************************
// Includes
#include <vector>
//*************************************************************************************
//*************************************************************************************
// Declaration of functions
int fed_tau_by_process_time(const float& T, const int& M, const float& tau_max,
const bool& reordering, std::vector<float>& tau);
int fed_tau_by_cycle_time(const float& t, const float& tau_max,
const bool& reordering, std::vector<float> &tau) ;
int fed_tau_internal(const int& n, const float& scale, const float& tau_max,
const bool& reordering, std::vector<float> &tau);
bool fed_is_prime_internal(const int& number);
//*************************************************************************************
//*************************************************************************************
#endif // __OPENCV_FEATURES_2D_FED_H__
@@ -0,0 +1,542 @@
//=============================================================================
//
// nldiffusion_functions.cpp
// Author: Pablo F. Alcantarilla
// Institution: University d'Auvergne
// Address: Clermont Ferrand, France
// Date: 27/12/2011
// Email: pablofdezalc@gmail.com
//
// KAZE Features Copyright 2012, Pablo F. Alcantarilla
// All Rights Reserved
// See LICENSE for the license information
//=============================================================================
/**
* @file nldiffusion_functions.cpp
* @brief Functions for non-linear diffusion applications:
* 2D Gaussian Derivatives
* Perona and Malik conductivity equations
* Perona and Malik evolution
* @date Dec 27, 2011
* @author Pablo F. Alcantarilla
*/
#include "../precomp.hpp"
#include "nldiffusion_functions.h"
#include <iostream>
// Namespaces
/* ************************************************************************* */
namespace cv
{
using namespace std;
/* ************************************************************************* */
/**
* @brief This function smoothes an image with a Gaussian kernel
* @param src Input image
* @param dst Output image
* @param ksize_x Kernel size in X-direction (horizontal)
* @param ksize_y Kernel size in Y-direction (vertical)
* @param sigma Kernel standard deviation
*/
void gaussian_2D_convolution(const cv::Mat& src, cv::Mat& dst, int ksize_x, int ksize_y, float sigma) {
int ksize_x_ = 0, ksize_y_ = 0;
// Compute an appropriate kernel size according to the specified sigma
if (sigma > ksize_x || sigma > ksize_y || ksize_x == 0 || ksize_y == 0) {
ksize_x_ = cvCeil(2.0f*(1.0f + (sigma - 0.8f) / (0.3f)));
ksize_y_ = ksize_x_;
}
// The kernel size must be and odd number
if ((ksize_x_ % 2) == 0) {
ksize_x_ += 1;
}
if ((ksize_y_ % 2) == 0) {
ksize_y_ += 1;
}
// Perform the Gaussian Smoothing with border replication
GaussianBlur(src, dst, Size(ksize_x_, ksize_y_), sigma, sigma, BORDER_REPLICATE);
}
/* ************************************************************************* */
/**
* @brief This function computes image derivatives with Scharr kernel
* @param src Input image
* @param dst Output image
* @param xorder Derivative order in X-direction (horizontal)
* @param yorder Derivative order in Y-direction (vertical)
* @note Scharr operator approximates better rotation invariance than
* other stencils such as Sobel. See Weickert and Scharr,
* A Scheme for Coherence-Enhancing Diffusion Filtering with Optimized Rotation Invariance,
* Journal of Visual Communication and Image Representation 2002
*/
void image_derivatives_scharr(const cv::Mat& src, cv::Mat& dst, int xorder, int yorder) {
Scharr(src, dst, CV_32F, xorder, yorder, 1.0, 0, BORDER_DEFAULT);
}
/* ************************************************************************* */
/**
* @brief This function computes the Perona and Malik conductivity coefficient g1
* g1 = exp(-|dL|^2/k^2)
* @param _Lx First order image derivative in X-direction (horizontal)
* @param _Ly First order image derivative in Y-direction (vertical)
* @param _dst Output image
* @param k Contrast factor parameter
*/
void pm_g1(InputArray _Lx, InputArray _Ly, OutputArray _dst, float k) {
_dst.create(_Lx.size(), _Lx.type());
Mat Lx = _Lx.getMat();
Mat Ly = _Ly.getMat();
Mat dst = _dst.getMat();
Size sz = Lx.size();
float inv_k = 1.0f / (k*k);
for (int y = 0; y < sz.height; y++) {
const float* Lx_row = Lx.ptr<float>(y);
const float* Ly_row = Ly.ptr<float>(y);
float* dst_row = dst.ptr<float>(y);
for (int x = 0; x < sz.width; x++) {
dst_row[x] = (-inv_k*(Lx_row[x]*Lx_row[x] + Ly_row[x]*Ly_row[x]));
}
}
exp(dst, dst);
}
/* ************************************************************************* */
/**
* @brief This function computes the Perona and Malik conductivity coefficient g2
* g2 = 1 / (1 + dL^2 / k^2)
* @param _Lx First order image derivative in X-direction (horizontal)
* @param _Ly First order image derivative in Y-direction (vertical)
* @param _dst Output image
* @param k Contrast factor parameter
*/
void pm_g2(InputArray _Lx, InputArray _Ly, OutputArray _dst, float k) {
CV_INSTRUMENT_REGION();
_dst.create(_Lx.size(), _Lx.type());
Mat Lx = _Lx.getMat();
Mat Ly = _Ly.getMat();
Mat dst = _dst.getMat();
Size sz = Lx.size();
dst.create(sz, Lx.type());
float k2inv = 1.0f / (k * k);
for(int y = 0; y < sz.height; y++) {
const float *Lx_row = Lx.ptr<float>(y);
const float *Ly_row = Ly.ptr<float>(y);
float* dst_row = dst.ptr<float>(y);
for(int x = 0; x < sz.width; x++) {
dst_row[x] = 1.0f / (1.0f + ((Lx_row[x] * Lx_row[x] + Ly_row[x] * Ly_row[x]) * k2inv));
}
}
}
/* ************************************************************************* */
/**
* @brief This function computes Weickert conductivity coefficient gw
* @param _Lx First order image derivative in X-direction (horizontal)
* @param _Ly First order image derivative in Y-direction (vertical)
* @param _dst Output image
* @param k Contrast factor parameter
* @note For more information check the following paper: J. Weickert
* Applications of nonlinear diffusion in image processing and computer vision,
* Proceedings of Algorithmy 2000
*/
void weickert_diffusivity(InputArray _Lx, InputArray _Ly, OutputArray _dst, float k) {
_dst.create(_Lx.size(), _Lx.type());
Mat Lx = _Lx.getMat();
Mat Ly = _Ly.getMat();
Mat dst = _dst.getMat();
Size sz = Lx.size();
float inv_k = 1.0f / (k*k);
for (int y = 0; y < sz.height; y++) {
const float* Lx_row = Lx.ptr<float>(y);
const float* Ly_row = Ly.ptr<float>(y);
float* dst_row = dst.ptr<float>(y);
for (int x = 0; x < sz.width; x++) {
float dL = inv_k*(Lx_row[x]*Lx_row[x] + Ly_row[x]*Ly_row[x]);
dst_row[x] = -3.315f/(dL*dL*dL*dL);
}
}
exp(dst, dst);
dst = 1.0 - dst;
}
/* ************************************************************************* */
/**
* @brief This function computes Charbonnier conductivity coefficient gc
* gc = 1 / sqrt(1 + dL^2 / k^2)
* @param _Lx First order image derivative in X-direction (horizontal)
* @param _Ly First order image derivative in Y-direction (vertical)
* @param _dst Output image
* @param k Contrast factor parameter
* @note For more information check the following paper: J. Weickert
* Applications of nonlinear diffusion in image processing and computer vision,
* Proceedings of Algorithmy 2000
*/
void charbonnier_diffusivity(InputArray _Lx, InputArray _Ly, OutputArray _dst, float k) {
_dst.create(_Lx.size(), _Lx.type());
Mat Lx = _Lx.getMat();
Mat Ly = _Ly.getMat();
Mat dst = _dst.getMat();
Size sz = Lx.size();
float inv_k = 1.0f / (k*k);
for (int y = 0; y < sz.height; y++) {
const float* Lx_row = Lx.ptr<float>(y);
const float* Ly_row = Ly.ptr<float>(y);
float* dst_row = dst.ptr<float>(y);
for (int x = 0; x < sz.width; x++) {
float den = sqrt(1.0f+inv_k*(Lx_row[x]*Lx_row[x] + Ly_row[x]*Ly_row[x]));
dst_row[x] = 1.0f / den;
}
}
}
/* ************************************************************************* */
/**
* @brief This function computes a good empirical value for the k contrast factor
* given an input image, the percentile (0-1), the gradient scale and the number of
* bins in the histogram
* @param img Input image
* @param perc Percentile of the image gradient histogram (0-1)
* @param gscale Scale for computing the image gradient histogram
* @param nbins Number of histogram bins
* @param ksize_x Kernel size in X-direction (horizontal) for the Gaussian smoothing kernel
* @param ksize_y Kernel size in Y-direction (vertical) for the Gaussian smoothing kernel
* @return k contrast factor
*/
float compute_k_percentile(const cv::Mat& img, float perc, float gscale, int nbins, int ksize_x, int ksize_y) {
CV_INSTRUMENT_REGION();
int nbin = 0, nelements = 0, nthreshold = 0, k = 0;
float kperc = 0.0, modg = 0.0;
float npoints = 0.0;
float hmax = 0.0;
// Create the array for the histogram
std::vector<int> hist(nbins, 0);
// Create the matrices
Mat gaussian = Mat::zeros(img.rows, img.cols, CV_32F);
Mat Lx = Mat::zeros(img.rows, img.cols, CV_32F);
Mat Ly = Mat::zeros(img.rows, img.cols, CV_32F);
// Perform the Gaussian convolution
gaussian_2D_convolution(img, gaussian, ksize_x, ksize_y, gscale);
// Compute the Gaussian derivatives Lx and Ly
Scharr(gaussian, Lx, CV_32F, 1, 0, 1, 0, cv::BORDER_DEFAULT);
Scharr(gaussian, Ly, CV_32F, 0, 1, 1, 0, cv::BORDER_DEFAULT);
// Skip the borders for computing the histogram
for (int i = 1; i < gaussian.rows - 1; i++) {
const float *lx = Lx.ptr<float>(i);
const float *ly = Ly.ptr<float>(i);
for (int j = 1; j < gaussian.cols - 1; j++) {
modg = lx[j]*lx[j] + ly[j]*ly[j];
// Get the maximum
if (modg > hmax) {
hmax = modg;
}
}
}
hmax = sqrt(hmax);
// Skip the borders for computing the histogram
for (int i = 1; i < gaussian.rows - 1; i++) {
const float *lx = Lx.ptr<float>(i);
const float *ly = Ly.ptr<float>(i);
for (int j = 1; j < gaussian.cols - 1; j++) {
modg = lx[j]*lx[j] + ly[j]*ly[j];
// Find the correspondent bin
if (modg != 0.0) {
nbin = (int)floor(nbins*(sqrt(modg) / hmax));
if (nbin == nbins) {
nbin--;
}
hist[nbin]++;
npoints++;
}
}
}
// Now find the perc of the histogram percentile
nthreshold = (int)(npoints*perc);
for (k = 0; nelements < nthreshold && k < nbins; k++) {
nelements = nelements + hist[k];
}
if (nelements < nthreshold) {
kperc = 0.03f;
}
else {
kperc = hmax*((float)(k) / (float)nbins);
}
return kperc;
}
/* ************************************************************************* */
/**
* @brief This function computes Scharr image derivatives
* @param src Input image
* @param dst Output image
* @param xorder Derivative order in X-direction (horizontal)
* @param yorder Derivative order in Y-direction (vertical)
* @param scale Scale factor for the derivative size
*/
void compute_scharr_derivatives(const cv::Mat& src, cv::Mat& dst, int xorder, int yorder, int scale) {
Mat kx, ky;
compute_derivative_kernels(kx, ky, xorder, yorder, scale);
sepFilter2D(src, dst, CV_32F, kx, ky);
}
/* ************************************************************************* */
/**
* @brief Compute derivative kernels for sizes different than 3
* @param _kx Horizontal kernel ues
* @param _ky Vertical kernel values
* @param dx Derivative order in X-direction (horizontal)
* @param dy Derivative order in Y-direction (vertical)
* @param scale Scale factor or derivative size
*/
void compute_derivative_kernels(cv::OutputArray _kx, cv::OutputArray _ky, int dx, int dy, int scale) {
CV_INSTRUMENT_REGION();
int ksize = 3 + 2 * (scale - 1);
// The standard Scharr kernel
if (scale == 1) {
getDerivKernels(_kx, _ky, dx, dy, 0, true, CV_32F);
return;
}
_kx.create(ksize, 1, CV_32F, -1, true);
_ky.create(ksize, 1, CV_32F, -1, true);
Mat kx = _kx.getMat();
Mat ky = _ky.getMat();
std::vector<float> kerI;
float w = 10.0f / 3.0f;
float norm = 1.0f / (2.0f*scale*(w + 2.0f));
for (int k = 0; k < 2; k++) {
Mat* kernel = k == 0 ? &kx : &ky;
int order = k == 0 ? dx : dy;
kerI.assign(ksize, 0.0f);
if (order == 0) {
kerI[0] = norm, kerI[ksize / 2] = w*norm, kerI[ksize - 1] = norm;
}
else if (order == 1) {
kerI[0] = -1, kerI[ksize / 2] = 0, kerI[ksize - 1] = 1;
}
Mat temp(kernel->rows, kernel->cols, CV_32F, &kerI[0]);
temp.copyTo(*kernel);
}
}
class Nld_Step_Scalar_Invoker : public cv::ParallelLoopBody
{
public:
Nld_Step_Scalar_Invoker(cv::Mat& Ld, const cv::Mat& c, cv::Mat& Lstep, float _stepsize)
: _Ld(&Ld)
, _c(&c)
, _Lstep(&Lstep)
, stepsize(_stepsize)
{
}
virtual ~Nld_Step_Scalar_Invoker()
{
}
void operator()(const cv::Range& range) const CV_OVERRIDE
{
cv::Mat& Ld = *_Ld;
const cv::Mat& c = *_c;
cv::Mat& Lstep = *_Lstep;
for (int i = range.start; i < range.end; i++)
{
const float *c_prev = c.ptr<float>(i - 1);
const float *c_curr = c.ptr<float>(i);
const float *c_next = c.ptr<float>(i + 1);
const float *ld_prev = Ld.ptr<float>(i - 1);
const float *ld_curr = Ld.ptr<float>(i);
const float *ld_next = Ld.ptr<float>(i + 1);
float *dst = Lstep.ptr<float>(i);
for (int j = 1; j < Lstep.cols - 1; j++)
{
float xpos = (c_curr[j] + c_curr[j+1])*(ld_curr[j+1] - ld_curr[j]);
float xneg = (c_curr[j-1] + c_curr[j]) *(ld_curr[j] - ld_curr[j-1]);
float ypos = (c_curr[j] + c_next[j]) *(ld_next[j] - ld_curr[j]);
float yneg = (c_prev[j] + c_curr[j]) *(ld_curr[j] - ld_prev[j]);
dst[j] = 0.5f*stepsize*(xpos - xneg + ypos - yneg);
}
}
}
private:
cv::Mat * _Ld;
const cv::Mat * _c;
cv::Mat * _Lstep;
float stepsize;
};
/* ************************************************************************* */
/**
* @brief This function performs a scalar non-linear diffusion step
* @param Ld Output image in the evolution
* @param c Conductivity image
* @param Lstep Previous image in the evolution
* @param stepsize The step size in time units
* @note Forward Euler Scheme 3x3 stencil
* The function c is a scalar value that depends on the gradient norm
* dL_by_ds = d(c dL_by_dx)_by_dx + d(c dL_by_dy)_by_dy
*/
void nld_step_scalar(cv::Mat& Ld, const cv::Mat& c, cv::Mat& Lstep, float stepsize) {
CV_INSTRUMENT_REGION();
cv::parallel_for_(cv::Range(1, Lstep.rows - 1), Nld_Step_Scalar_Invoker(Ld, c, Lstep, stepsize), (double)Ld.total()/(1 << 16));
float xneg, xpos, yneg, ypos;
float* dst = Lstep.ptr<float>(0);
const float* cprv = NULL;
const float* ccur = c.ptr<float>(0);
const float* cnxt = c.ptr<float>(1);
const float* ldprv = NULL;
const float* ldcur = Ld.ptr<float>(0);
const float* ldnxt = Ld.ptr<float>(1);
for (int j = 1; j < Lstep.cols - 1; j++) {
xpos = (ccur[j] + ccur[j+1]) * (ldcur[j+1] - ldcur[j]);
xneg = (ccur[j-1] + ccur[j]) * (ldcur[j] - ldcur[j-1]);
ypos = (ccur[j] + cnxt[j]) * (ldnxt[j] - ldcur[j]);
dst[j] = 0.5f*stepsize*(xpos - xneg + ypos);
}
dst = Lstep.ptr<float>(Lstep.rows - 1);
ccur = c.ptr<float>(Lstep.rows - 1);
cprv = c.ptr<float>(Lstep.rows - 2);
ldcur = Ld.ptr<float>(Lstep.rows - 1);
ldprv = Ld.ptr<float>(Lstep.rows - 2);
for (int j = 1; j < Lstep.cols - 1; j++) {
xpos = (ccur[j] + ccur[j+1]) * (ldcur[j+1] - ldcur[j]);
xneg = (ccur[j-1] + ccur[j]) * (ldcur[j] - ldcur[j-1]);
yneg = (cprv[j] + ccur[j]) * (ldcur[j] - ldprv[j]);
dst[j] = 0.5f*stepsize*(xpos - xneg - yneg);
}
ccur = c.ptr<float>(1);
ldcur = Ld.ptr<float>(1);
cprv = c.ptr<float>(0);
ldprv = Ld.ptr<float>(0);
int r0 = Lstep.cols - 1;
int r1 = Lstep.cols - 2;
for (int i = 1; i < Lstep.rows - 1; i++) {
cnxt = c.ptr<float>(i + 1);
ldnxt = Ld.ptr<float>(i + 1);
dst = Lstep.ptr<float>(i);
xpos = (ccur[0] + ccur[1]) * (ldcur[1] - ldcur[0]);
ypos = (ccur[0] + cnxt[0]) * (ldnxt[0] - ldcur[0]);
yneg = (cprv[0] + ccur[0]) * (ldcur[0] - ldprv[0]);
dst[0] = 0.5f*stepsize*(xpos + ypos - yneg);
xneg = (ccur[r1] + ccur[r0]) * (ldcur[r0] - ldcur[r1]);
ypos = (ccur[r0] + cnxt[r0]) * (ldnxt[r0] - ldcur[r0]);
yneg = (cprv[r0] + ccur[r0]) * (ldcur[r0] - ldprv[r0]);
dst[r0] = 0.5f*stepsize*(-xneg + ypos - yneg);
cprv = ccur;
ccur = cnxt;
ldprv = ldcur;
ldcur = ldnxt;
}
Ld += Lstep;
}
/* ************************************************************************* */
/**
* @brief This function downsamples the input image using OpenCV resize
* @param src Input image to be downsampled
* @param dst Output image with half of the resolution of the input image
*/
void halfsample_image(const cv::Mat& src, cv::Mat& dst) {
// Make sure the destination image is of the right size
CV_Assert(src.cols / 2 == dst.cols);
CV_Assert(src.rows / 2 == dst.rows);
resize(src, dst, dst.size(), 0, 0, cv::INTER_AREA);
}
/* ************************************************************************* */
/**
* @brief This function checks if a given pixel is a maximum in a local neighbourhood
* @param img Input image where we will perform the maximum search
* @param dsize Half size of the neighbourhood
* @param value Response value at (x,y) position
* @param row Image row coordinate
* @param col Image column coordinate
* @param same_img Flag to indicate if the image value at (x,y) is in the input image
* @return 1->is maximum, 0->otherwise
*/
bool check_maximum_neighbourhood(const cv::Mat& img, int dsize, float value, int row, int col, bool same_img) {
bool response = true;
for (int i = row - dsize; i <= row + dsize; i++) {
for (int j = col - dsize; j <= col + dsize; j++) {
if (i >= 0 && i < img.rows && j >= 0 && j < img.cols) {
if (same_img == true) {
if (i != row || j != col) {
if ((*(img.ptr<float>(i)+j)) > value) {
response = false;
return response;
}
}
}
else {
if ((*(img.ptr<float>(i)+j)) > value) {
response = false;
return response;
}
}
}
}
}
return response;
}
}
@@ -0,0 +1,47 @@
/**
* @file nldiffusion_functions.h
* @brief Functions for non-linear diffusion applications:
* 2D Gaussian Derivatives
* Perona and Malik conductivity equations
* Perona and Malik evolution
* @date Dec 27, 2011
* @author Pablo F. Alcantarilla
*/
#ifndef __OPENCV_FEATURES_2D_NLDIFFUSION_FUNCTIONS_H__
#define __OPENCV_FEATURES_2D_NLDIFFUSION_FUNCTIONS_H__
/* ************************************************************************* */
// Declaration of functions
namespace cv
{
// Gaussian 2D convolution
void gaussian_2D_convolution(const cv::Mat& src, cv::Mat& dst, int ksize_x, int ksize_y, float sigma);
// Diffusivity functions
void pm_g1(InputArray Lx, InputArray Ly, OutputArray dst, float k);
void pm_g2(InputArray Lx, InputArray Ly, OutputArray dst, float k);
void weickert_diffusivity(InputArray Lx, InputArray Ly, OutputArray dst, float k);
void charbonnier_diffusivity(InputArray Lx, InputArray Ly, OutputArray dst, float k);
float compute_k_percentile(const cv::Mat& img, float perc, float gscale, int nbins, int ksize_x, int ksize_y);
// Image derivatives
void compute_scharr_derivatives(const cv::Mat& src, cv::Mat& dst, int xorder, int yorder, int scale);
void compute_derivative_kernels(cv::OutputArray _kx, cv::OutputArray _ky, int dx, int dy, int scale);
void image_derivatives_scharr(const cv::Mat& src, cv::Mat& dst, int xorder, int yorder);
// Nonlinear diffusion filtering scalar step
void nld_step_scalar(cv::Mat& Ld, const cv::Mat& c, cv::Mat& Lstep, float stepsize);
// For non-maxima suppression
bool check_maximum_neighbourhood(const cv::Mat& img, int dsize, float value, int row, int col, bool same_img);
// Image downsampling
void halfsample_image(const cv::Mat& src, cv::Mat& dst);
}
#endif
+42
View File
@@ -0,0 +1,42 @@
#ifndef __OPENCV_FEATURES_2D_KAZE_UTILS_H__
#define __OPENCV_FEATURES_2D_KAZE_UTILS_H__
/* ************************************************************************* */
/**
* @brief This function computes the value of a 2D Gaussian function
* @param x X Position
* @param y Y Position
* @param sigma Standard Deviation
*/
inline float gaussian(float x, float y, float sigma) {
return expf(-(x*x + y*y) / (2.0f*sigma*sigma));
}
/* ************************************************************************* */
/**
* @brief This function checks descriptor limits
* @param x X Position
* @param y Y Position
* @param width Image width
* @param height Image height
*/
inline void checkDescriptorLimits(int &x, int &y, int width, int height) {
if (x < 0) {
x = 0;
}
if (y < 0) {
y = 0;
}
if (x > width - 1) {
x = width - 1;
}
if (y > height - 1) {
y = height - 1;
}
}
#endif
File diff suppressed because it is too large Load Diff
+69
View File
@@ -0,0 +1,69 @@
// 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 "logos/Logos.hpp"
namespace cv
{
namespace xfeatures2d
{
void matchLOGOS(const std::vector<KeyPoint>& keypoints1, const std::vector<KeyPoint>& keypoints2,
const std::vector<int>& nn1, const std::vector<int>& nn2, std::vector<DMatch>& matches1to2)
{
CV_CheckEQ(keypoints1.size(), nn1.size(), "Number of keypoints1 must be equal to the number of nn1.");
CV_CheckEQ(keypoints2.size(), nn2.size(), "Number of keypoints2 must be equal to the number of nn2.");
if (keypoints1.empty() || keypoints2.empty())
{
return;
}
std::vector<logos::Point*> vP1, vP2;
vP1.reserve(keypoints1.size());
vP2.reserve(keypoints2.size());
for (size_t i = 0; i < keypoints1.size(); i++)
{
logos::Point* pt1 = new logos::Point(keypoints1[i].pt.x, keypoints1[i].pt.y,
static_cast<float>(keypoints1[i].angle*CV_PI/180),
keypoints1[i].size, nn1[i]);
vP1.push_back(pt1);
}
for (size_t i = 0; i < keypoints2.size(); i++)
{
logos::Point* pt2 = new logos::Point(keypoints2[i].pt.x, keypoints2[i].pt.y,
static_cast<float>(keypoints2[i].angle*CV_PI/180),
keypoints2[i].size, nn2[i]);
vP2.push_back(pt2);
}
logos::Logos logos;
std::vector<logos::PointPair*> globalMatches;
logos.estimateMatches(vP1, vP2, globalMatches);
matches1to2.clear();
matches1to2.reserve(globalMatches.size());
for (size_t i = 0; i < globalMatches.size(); i++)
{
logos::PointPair* pp = globalMatches[i];
matches1to2.push_back(DMatch(pp->getPos1(), pp->getPos2(), 0));
}
for (size_t i = 0; i < globalMatches.size(); i++)
{
delete globalMatches[i];
}
for (size_t i = 0; i < vP1.size(); i++)
{
delete vP1[i];
}
for (size_t i = 0; i < vP2.size(); i++)
{
delete vP2[i];
}
}
} //namespace xfeatures2d
} //namespace cv
+189
View File
@@ -0,0 +1,189 @@
// 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
/*
* MIT License
*
* Copyright (c) 2018 Stephanie Lowry
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <cmath>
#include "Logos.hpp"
#include <opencv2/core.hpp>
namespace logos
{
Logos::Logos()
{
LogosParameters defaultParams;
init(defaultParams);
}
Logos::Logos(const LogosParameters& p)
{
init(p);
}
void Logos::init(const LogosParameters& p)
{
setParams(p);
LB = static_cast<float>(-CV_PI);
BINSIZE = logosParams.GLOBALORILIMIT/3;
BINNUMBER = static_cast<unsigned int>(ceil(2*CV_PI/BINSIZE));
bins.resize(BINNUMBER);
std::fill(bins.begin(), bins.end(), 0);
}
int Logos::estimateMatches(std::vector<Point*> vP1, std::vector<Point*> vP2, std::vector<PointPair*>& globalmatches)
{
matches.clear();
// for each point
int count1 = 0;
for (std::vector<Point*>::iterator pit1 = vP1.begin(); pit1 != vP1.end(); ++pit1, count1++)
{
(*pit1)->nearestNeighbours(vP1, count1, getNum1());
int count2 = 0;
// find possible matches
for (std::vector<Point*>::iterator pit2 = vP2.begin(); pit2 != vP2.end(); ++pit2, count2++)
{
if ((*pit1)->getLabel() != (*pit2)->getLabel())
{
continue;
}
// this is a possible match in Image 2
// get nearest neighbours
(*pit2)->nearestNeighbours(vP2, count2, getNum2());
PointPair* ptpr = new PointPair(*pit1, *pit2);
ptpr->addPositions(count1, count2);
ptpr->computeLocalSupport(pp, getNum2());
// calc matches
int support = 0;
for (std::vector<PointPair*>::const_iterator it = pp.begin(); it < pp.end(); ++it)
{
Match m(ptpr, *it);
if (evaluateMatch(m))
{
support++;
}
}
for (size_t i = 0; i < pp.size(); i++)
{
delete pp[i];
}
pp.clear();
if (support > 0)
{
ptpr->setSupport(support);
matches.push_back(ptpr);
updateBin(ptpr->getRelOri());
}
else
{
delete ptpr;
ptpr = NULL;
}
}
}
// do global orientation
double maxang = calcGlobalOrientation();
// find which matches are within global orientation limit
int numinliers = 0;
globalmatches.clear();
for (std::vector<PointPair*>::iterator it = matches.begin(); it != matches.end(); ++it)
{
if (std::fabs((*it)->getRelOri() - maxang) < logosParams.GLOBALORILIMIT)
{
numinliers++;
globalmatches.push_back(*it);
}
else
{
delete *it;
*it = NULL;
}
}
return numinliers;
}
bool Logos::evaluateMatch(const Match& m) const
{
return ((m.getRelOrientation() < getIntraOriLimit()) &&
(m.getRelScale() < getIntraScaleLimit()) &&
(m.getInterOrientation() < getInterOriLimit()) &&
(m.getInterScale() < getInterScaleLimit()));
}
void Logos::updateBin(float input)
{
unsigned int binnumber = static_cast<unsigned int>(cvFloor((input-LB) / BINSIZE));
// compare binnumber to BINNUMBER
if (binnumber < BINNUMBER)
{
bins[binnumber]++;
}
else
{
bins[BINNUMBER-1]++;
}
}
float Logos::calcGlobalOrientation()
{
// find max bin
// check BINNUMBER is big enough
if (BINNUMBER < 3)
{
return 0;
}
std::vector<int> bins2(BINNUMBER);
int maxval = 0;
unsigned int maxix = 0;
bins2[0] = bins[0] + bins[1] + bins[BINNUMBER-1];
maxval = bins2[0];
for (unsigned int i = 1; i < BINNUMBER; i++)
{
if (i == BINNUMBER-1)
{
bins2[i] = bins[i]+bins[i-1]+bins[0];
}
else
{
bins2[i] = bins[i]+bins[i-1]+bins[i+1];
}
if (bins2[i] > maxval)
{
maxval = bins2[i];
maxix = i;
}
}
// convert to an angle
return LB + maxix*BINSIZE + BINSIZE/2;
}
}

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