vendor: OpenCV 5.0.0 snapshot at 755e50675d97db9b7d449d8bd6b09888646f6c6e
This commit is contained in:
@@ -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()
|
||||
@@ -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
|
||||
@@ -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()
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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())
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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()
|
||||
@@ -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.: 8071–8103, 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;
|
||||
}
|
||||
@@ -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.: 8071–8103, 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
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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:366–372, 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
|
||||
@@ -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:366–372, 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_));
|
||||
@@ -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:366–372, 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_));
|
||||
@@ -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
|
||||
@@ -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
@@ -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() {
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
@@ -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
|
||||
@@ -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
Reference in New Issue
Block a user