chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 12:06:04 +08:00
commit 86c9b1c39f
7743 changed files with 3316339 additions and 0 deletions
+135
View File
@@ -0,0 +1,135 @@
/*
**
** License Agreement
** For chi_table.h
**
** Copyright (C) 2007 Per-Erik Forssen, all rights reserved.
**
** Redistribution and use in source and binary forms, with or without modification,
** are permitted provided that the following conditions are met:
**
** * Redistribution's of source code must retain the above copyright notice,
** this list of conditions and the following disclaimer.
**
** * Redistribution's in binary form must reproduce the above copyright notice,
** this list of conditions and the following disclaimer in the documentation
** and/or other materials provided with the distribution.
**
** * The name of the copyright holders may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** This software is provided by the copyright holders and contributors "as is" and
** any express or implied warranties, including, but not limited to, the implied
** warranties of merchantability and fitness for a particular purpose are disclaimed.
** In no event shall the Intel Corporation or contributors be liable for any direct,
** indirect, incidental, special, exemplary, or consequential damages
** (including, but not limited to, procurement of substitute goods or services;
** loss of use, data, or profits; or business interruption) however caused
** and on any theory of liability, whether in contract, strict liability,
** or tort (including negligence or otherwise) arising in any way out of
** the use of this software, even if advised of the possibility of such damage.
**
** Content origin: http://users.isy.liu.se/cvl/perfo/software/chi_table.h
*/
#define TABLE_SIZE 400
static double chitab3[]={0, 0.0150057, 0.0239478, 0.0315227,
0.0383427, 0.0446605, 0.0506115, 0.0562786,
0.0617174, 0.0669672, 0.0720573, 0.0770099,
0.081843, 0.0865705, 0.0912043, 0.0957541,
0.100228, 0.104633, 0.108976, 0.113261,
0.117493, 0.121676, 0.125814, 0.12991,
0.133967, 0.137987, 0.141974, 0.145929,
0.149853, 0.15375, 0.15762, 0.161466,
0.165287, 0.169087, 0.172866, 0.176625,
0.180365, 0.184088, 0.187794, 0.191483,
0.195158, 0.198819, 0.202466, 0.2061,
0.209722, 0.213332, 0.216932, 0.220521,
0.2241, 0.22767, 0.231231, 0.234783,
0.238328, 0.241865, 0.245395, 0.248918,
0.252435, 0.255947, 0.259452, 0.262952,
0.266448, 0.269939, 0.273425, 0.276908,
0.280386, 0.283862, 0.287334, 0.290803,
0.29427, 0.297734, 0.301197, 0.304657,
0.308115, 0.311573, 0.315028, 0.318483,
0.321937, 0.32539, 0.328843, 0.332296,
0.335749, 0.339201, 0.342654, 0.346108,
0.349562, 0.353017, 0.356473, 0.35993,
0.363389, 0.366849, 0.37031, 0.373774,
0.377239, 0.380706, 0.384176, 0.387648,
0.391123, 0.3946, 0.39808, 0.401563,
0.405049, 0.408539, 0.412032, 0.415528,
0.419028, 0.422531, 0.426039, 0.429551,
0.433066, 0.436586, 0.440111, 0.44364,
0.447173, 0.450712, 0.454255, 0.457803,
0.461356, 0.464915, 0.468479, 0.472049,
0.475624, 0.479205, 0.482792, 0.486384,
0.489983, 0.493588, 0.4972, 0.500818,
0.504442, 0.508073, 0.511711, 0.515356,
0.519008, 0.522667, 0.526334, 0.530008,
0.533689, 0.537378, 0.541075, 0.54478,
0.548492, 0.552213, 0.555942, 0.55968,
0.563425, 0.56718, 0.570943, 0.574715,
0.578497, 0.582287, 0.586086, 0.589895,
0.593713, 0.597541, 0.601379, 0.605227,
0.609084, 0.612952, 0.61683, 0.620718,
0.624617, 0.628526, 0.632447, 0.636378,
0.64032, 0.644274, 0.648239, 0.652215,
0.656203, 0.660203, 0.664215, 0.668238,
0.672274, 0.676323, 0.680384, 0.684457,
0.688543, 0.692643, 0.696755, 0.700881,
0.70502, 0.709172, 0.713339, 0.717519,
0.721714, 0.725922, 0.730145, 0.734383,
0.738636, 0.742903, 0.747185, 0.751483,
0.755796, 0.760125, 0.76447, 0.768831,
0.773208, 0.777601, 0.782011, 0.786438,
0.790882, 0.795343, 0.799821, 0.804318,
0.808831, 0.813363, 0.817913, 0.822482,
0.827069, 0.831676, 0.836301, 0.840946,
0.84561, 0.850295, 0.854999, 0.859724,
0.864469, 0.869235, 0.874022, 0.878831,
0.883661, 0.888513, 0.893387, 0.898284,
0.903204, 0.908146, 0.913112, 0.918101,
0.923114, 0.928152, 0.933214, 0.938301,
0.943413, 0.94855, 0.953713, 0.958903,
0.964119, 0.969361, 0.974631, 0.979929,
0.985254, 0.990608, 0.99599, 1.0014,
1.00684, 1.01231, 1.01781, 1.02335,
1.02891, 1.0345, 1.04013, 1.04579,
1.05148, 1.05721, 1.06296, 1.06876,
1.07459, 1.08045, 1.08635, 1.09228,
1.09826, 1.10427, 1.11032, 1.1164,
1.12253, 1.1287, 1.1349, 1.14115,
1.14744, 1.15377, 1.16015, 1.16656,
1.17303, 1.17954, 1.18609, 1.19269,
1.19934, 1.20603, 1.21278, 1.21958,
1.22642, 1.23332, 1.24027, 1.24727,
1.25433, 1.26144, 1.26861, 1.27584,
1.28312, 1.29047, 1.29787, 1.30534,
1.31287, 1.32046, 1.32812, 1.33585,
1.34364, 1.3515, 1.35943, 1.36744,
1.37551, 1.38367, 1.39189, 1.4002,
1.40859, 1.41705, 1.42561, 1.43424,
1.44296, 1.45177, 1.46068, 1.46967,
1.47876, 1.48795, 1.49723, 1.50662,
1.51611, 1.52571, 1.53541, 1.54523,
1.55517, 1.56522, 1.57539, 1.58568,
1.59611, 1.60666, 1.61735, 1.62817,
1.63914, 1.65025, 1.66152, 1.67293,
1.68451, 1.69625, 1.70815, 1.72023,
1.73249, 1.74494, 1.75757, 1.77041,
1.78344, 1.79669, 1.81016, 1.82385,
1.83777, 1.85194, 1.86635, 1.88103,
1.89598, 1.91121, 1.92674, 1.94257,
1.95871, 1.97519, 1.99201, 2.0092,
2.02676, 2.04471, 2.06309, 2.08189,
2.10115, 2.12089, 2.14114, 2.16192,
2.18326, 2.2052, 2.22777, 2.25101,
2.27496, 2.29966, 2.32518, 2.35156,
2.37886, 2.40717, 2.43655, 2.46709,
2.49889, 2.53206, 2.56673, 2.60305,
2.64117, 2.6813, 2.72367, 2.76854,
2.81623, 2.86714, 2.92173, 2.98059,
3.04446, 3.1143, 3.19135, 3.27731,
3.37455, 3.48653, 3.61862, 3.77982,
3.98692, 4.2776, 4.77167, 133.333 };
+28
View File
@@ -0,0 +1,28 @@
License Agreement
For chi_table.h
Copyright (C) 2007 Per-Erik Forssen, all rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistribution's of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistribution's in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* The name of the copyright holders may not be used to endorse or promote products
derived from this software without specific prior written permission.
This software is provided by the copyright holders and contributors "as is" and
any express or implied warranties, including, but not limited to, the implied
warranties of merchantability and fitness for a particular purpose are disclaimed.
In no event shall the Intel Corporation or contributors be liable for any direct,
indirect, incidental, special, exemplary, or consequential damages
(including, but not limited to, procurement of substitute goods or services;
loss of use, data, or profits; or business interruption) however caused
and on any theory of liability, whether in contract, strict liability,
or tort (including negligence or otherwise) arising in any way out of
the use of this software, even if advised of the possibility of such damage.
+12
View File
@@ -0,0 +1,12 @@
set(the_description "2D Features Framework")
ocv_add_dispatched_file(sift SSE4_1 AVX2 AVX512_SKX)
set(debug_modules "")
if(DEBUG_opencv_features2d)
list(APPEND debug_modules opencv_highgui)
endif()
ocv_define_module(features2d opencv_imgproc ${debug_modules} OPTIONAL opencv_flann WRAP java objc python js)
ocv_install_3rdparty_licenses(mscr "${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/mscr/chi_table_LICENSE.txt")
ocv_install_3rdparty_licenses(features2d "${CMAKE_CURRENT_SOURCE_DIR}/src/kaze/LICENSE.KAZE" "${CMAKE_CURRENT_SOURCE_DIR}/src/kaze/LICENSE.AKAZE")
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: $!";
+244
View File
@@ -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
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,48 @@
/*M///////////////////////////////////////////////////////////////////////////////////////
//
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
// By downloading, copying, installing or using the software you agree to this license.
// If you do not agree to this license, do not download, install,
// copy or use the software.
//
//
// License Agreement
// For Open Source Computer Vision Library
//
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistribution's of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistribution's in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// * The name of the copyright holders may not be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// This software is provided by the copyright holders and contributors "as is" and
// any express or implied warranties, including, but not limited to, the implied
// warranties of merchantability and fitness for a particular purpose are disclaimed.
// In no event shall the Intel Corporation or contributors be liable for any direct,
// indirect, incidental, special, exemplary, or consequential damages
// (including, but not limited to, procurement of substitute goods or services;
// loss of use, data, or profits; or business interruption) however caused
// and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of
// the use of this software, even if advised of the possibility of such damage.
//
//M*/
#ifdef __OPENCV_BUILD
#error this is a compatibility header which should not be used inside the OpenCV library
#endif
#include "opencv2/features2d.hpp"
@@ -0,0 +1,33 @@
#ifndef OPENCV_FEATURE2D_HAL_INTERFACE_H
#define OPENCV_FEATURE2D_HAL_INTERFACE_H
#include "opencv2/core/cvdef.h"
//! @addtogroup features2d_hal_interface
//! @{
//! @name Fast feature detector types
//! @sa cv::FastFeatureDetector
//! @{
#define CV_HAL_TYPE_5_8 0
#define CV_HAL_TYPE_7_12 1
#define CV_HAL_TYPE_9_16 2
//! @}
//! @name Key point
//! @sa cv::KeyPoint
//! @{
struct CV_EXPORTS cvhalKeyPoint
{
float x;
float y;
float size;
float angle;
float response;
int octave;
int class_id;
};
//! @}
//! @}
#endif
+1
View File
@@ -0,0 +1 @@
include/opencv2/features2d.hpp
@@ -0,0 +1 @@
misc/java/src/cpp/features2d_converters.hpp
@@ -0,0 +1,18 @@
{
"type_dict" : {
"Feature2D": {
"j_type": "Feature2D",
"jn_type": "long",
"jni_type": "jlong",
"jni_var": "Feature2D %(n)s",
"suffix": "J",
"j_import": "org.opencv.features2d.Feature2D"
},
"uchar": {
"j_type": "byte",
"jn_type": "byte",
"jni_type": "jbyte",
"suffix": "B"
}
}
}
@@ -0,0 +1,112 @@
#define LOG_TAG "org.opencv.utils.Converters"
#include "common.h"
#include "features2d_converters.hpp"
using namespace cv;
#define CHECK_MAT(cond) if(!(cond)){ LOGD("FAILED: " #cond); return; }
//vector_KeyPoint
void Mat_to_vector_KeyPoint(Mat& mat, std::vector<KeyPoint>& v_kp)
{
v_kp.clear();
CHECK_MAT(mat.type()==CV_32FC(7) && mat.cols==1);
for(int i=0; i<mat.rows; i++)
{
Vec<float, 7> v = mat.at< Vec<float, 7> >(i, 0);
KeyPoint kp(v[0], v[1], v[2], v[3], v[4], (int)v[5], (int)v[6]);
v_kp.push_back(kp);
}
return;
}
void vector_KeyPoint_to_Mat(std::vector<KeyPoint>& v_kp, Mat& mat)
{
int count = (int)v_kp.size();
mat.create(count, 1, CV_32FC(7));
for(int i=0; i<count; i++)
{
KeyPoint kp = v_kp[i];
mat.at< Vec<float, 7> >(i, 0) = Vec<float, 7>(kp.pt.x, kp.pt.y, kp.size, kp.angle, kp.response, (float)kp.octave, (float)kp.class_id);
}
}
//vector_DMatch
void Mat_to_vector_DMatch(Mat& mat, std::vector<DMatch>& v_dm)
{
v_dm.clear();
CHECK_MAT(mat.type()==CV_32FC4 && mat.cols==1);
for(int i=0; i<mat.rows; i++)
{
Vec<float, 4> v = mat.at< Vec<float, 4> >(i, 0);
DMatch dm((int)v[0], (int)v[1], (int)v[2], v[3]);
v_dm.push_back(dm);
}
return;
}
void vector_DMatch_to_Mat(std::vector<DMatch>& v_dm, Mat& mat)
{
int count = (int)v_dm.size();
mat.create(count, 1, CV_32FC4);
for(int i=0; i<count; i++)
{
DMatch dm = v_dm[i];
mat.at< Vec<float, 4> >(i, 0) = Vec<float, 4>((float)dm.queryIdx, (float)dm.trainIdx, (float)dm.imgIdx, dm.distance);
}
}
void Mat_to_vector_vector_KeyPoint(Mat& mat, std::vector< std::vector< KeyPoint > >& vv_kp)
{
std::vector<Mat> vm;
vm.reserve( mat.rows );
Mat_to_vector_Mat(mat, vm);
for(size_t i=0; i<vm.size(); i++)
{
std::vector<KeyPoint> vkp;
Mat_to_vector_KeyPoint(vm[i], vkp);
vv_kp.push_back(vkp);
}
}
void vector_vector_KeyPoint_to_Mat(std::vector< std::vector< KeyPoint > >& vv_kp, Mat& mat)
{
std::vector<Mat> vm;
vm.reserve( vv_kp.size() );
for(size_t i=0; i<vv_kp.size(); i++)
{
Mat m;
vector_KeyPoint_to_Mat(vv_kp[i], m);
vm.push_back(m);
}
vector_Mat_to_Mat(vm, mat);
}
void Mat_to_vector_vector_DMatch(Mat& mat, std::vector< std::vector< DMatch > >& vv_dm)
{
std::vector<Mat> vm;
vm.reserve( mat.rows );
Mat_to_vector_Mat(mat, vm);
for(size_t i=0; i<vm.size(); i++)
{
std::vector<DMatch> vdm;
Mat_to_vector_DMatch(vm[i], vdm);
vv_dm.push_back(vdm);
}
}
void vector_vector_DMatch_to_Mat(std::vector< std::vector< DMatch > >& vv_dm, Mat& mat)
{
std::vector<Mat> vm;
vm.reserve( vv_dm.size() );
for(size_t i=0; i<vv_dm.size(); i++)
{
Mat m;
vector_DMatch_to_Mat(vv_dm[i], m);
vm.push_back(m);
}
vector_Mat_to_Mat(vm, mat);
}
@@ -0,0 +1,21 @@
#ifndef __FEATURES2D_CONVERTERS_HPP__
#define __FEATURES2D_CONVERTERS_HPP__
#include "opencv2/opencv_modules.hpp"
#include "opencv2/core.hpp"
#include "opencv2/features2d.hpp"
void Mat_to_vector_KeyPoint(cv::Mat& mat, std::vector<cv::KeyPoint>& v_kp);
void vector_KeyPoint_to_Mat(std::vector<cv::KeyPoint>& v_kp, cv::Mat& mat);
void Mat_to_vector_DMatch(cv::Mat& mat, std::vector<cv::DMatch>& v_dm);
void vector_DMatch_to_Mat(std::vector<cv::DMatch>& v_dm, cv::Mat& mat);
void Mat_to_vector_vector_KeyPoint(cv::Mat& mat, std::vector< std::vector< cv::KeyPoint > >& vv_kp);
void vector_vector_KeyPoint_to_Mat(std::vector< std::vector< cv::KeyPoint > >& vv_kp, cv::Mat& mat);
void Mat_to_vector_vector_DMatch(cv::Mat& mat, std::vector< std::vector< cv::DMatch > >& vv_dm);
void vector_vector_DMatch_to_Mat(std::vector< std::vector< cv::DMatch > >& vv_dm, cv::Mat& mat);
#endif
@@ -0,0 +1,85 @@
package org.opencv.test.features2d;
import org.opencv.test.OpenCVTestCase;
import org.opencv.test.OpenCVTestRunner;
import org.opencv.features2d.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.0\n---\nname: \"Feature2D.AgastFeatureDetector\"\nthreshold: 10\nnonmaxSuppression: 1\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.features2d;
import org.opencv.test.OpenCVTestCase;
import org.opencv.test.OpenCVTestRunner;
import org.opencv.features2d.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.0\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.features2d;
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.features2d.ORB;
import org.opencv.features2d.DescriptorMatcher;
import org.opencv.features2d.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.features2d;
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.features2d.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.0\n---\nname: \"Feature2D.BRIEF\"\ndescriptorSize: 32\nuse_orientation: 0\n";
assertEquals(truth, readFile(filename));
}
}
@@ -0,0 +1,63 @@
package org.opencv.test.features2d;
import org.opencv.test.OpenCVTestCase;
import org.opencv.test.OpenCVTestRunner;
import org.opencv.features2d.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.0\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,304 @@
package org.opencv.test.features2d;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.MatOfDMatch;
import org.opencv.core.MatOfKeyPoint;
import org.opencv.core.Point;
import org.opencv.core.Scalar;
import org.opencv.core.DMatch;
import org.opencv.features2d.DescriptorMatcher;
import org.opencv.features2d.BFMatcher;
import org.opencv.core.KeyPoint;
import org.opencv.test.OpenCVTestCase;
import org.opencv.test.OpenCVTestRunner;
import org.opencv.imgproc.Imgproc;
import org.opencv.features2d.Feature2D;
public class BruteForceDescriptorMatcherTest extends OpenCVTestCase {
DescriptorMatcher matcher;
int matSize;
DMatch[] truth;
private Mat getMaskImg() {
return new Mat(5, 2, CvType.CV_8U, new Scalar(0)) {
{
put(0, 0, 1, 1, 1, 1);
}
};
}
private Mat getQueryDescriptors() {
Mat img = getQueryImg();
MatOfKeyPoint keypoints = new MatOfKeyPoint();
Mat descriptors = new Mat();
Feature2D detector = createClassInstance(XFEATURES2D+"SURF", DEFAULT_FACTORY, null, null);
Feature2D extractor = createClassInstance(XFEATURES2D+"SURF", DEFAULT_FACTORY, null, null);
setProperty(detector, "hessianThreshold", "double", 8000);
setProperty(detector, "nOctaves", "int", 3);
setProperty(detector, "nOctaveLayers", "int", 4);
setProperty(detector, "upright", "boolean", false);
detector.detect(img, keypoints);
extractor.compute(img, keypoints, descriptors);
return descriptors;
}
private Mat getQueryImg() {
Mat cross = new Mat(matSize, matSize, CvType.CV_8U, new Scalar(255));
Imgproc.line(cross, new Point(30, matSize / 2), new Point(matSize - 31, matSize / 2), new Scalar(100), 3);
Imgproc.line(cross, new Point(matSize / 2, 30), new Point(matSize / 2, matSize - 31), new Scalar(100), 3);
return cross;
}
private Mat getTrainDescriptors() {
Mat img = getTrainImg();
MatOfKeyPoint keypoints = new MatOfKeyPoint(new KeyPoint(50, 50, 16, 0, 20000, 1, -1), new KeyPoint(42, 42, 16, 160, 10000, 1, -1));
Mat descriptors = new Mat();
Feature2D extractor = createClassInstance(XFEATURES2D+"SURF", DEFAULT_FACTORY, null, null);
extractor.compute(img, keypoints, descriptors);
return descriptors;
}
private Mat getTrainImg() {
Mat cross = new Mat(matSize, matSize, CvType.CV_8U, new Scalar(255));
Imgproc.line(cross, new Point(20, matSize / 2), new Point(matSize - 21, matSize / 2), new Scalar(100), 2);
Imgproc.line(cross, new Point(matSize / 2, 20), new Point(matSize / 2, matSize - 21), new Scalar(100), 2);
return cross;
}
protected void setUp() throws Exception {
super.setUp();
matcher = DescriptorMatcher.create(DescriptorMatcher.BRUTEFORCE);
matSize = 100;
truth = new DMatch[] {
new DMatch(0, 0, 0, 0.6159003f),
new DMatch(1, 1, 0, 0.9177120f),
new DMatch(2, 1, 0, 0.3112163f),
new DMatch(3, 1, 0, 0.2925074f),
new DMatch(4, 1, 0, 0.26520672f)
};
}
// https://github.com/opencv/opencv/issues/11268
public void testConstructor()
{
BFMatcher self_created_matcher = new BFMatcher();
Mat train = new Mat(1, 1, CvType.CV_8U, new Scalar(123));
self_created_matcher.add(Arrays.asList(train));
assertTrue(!self_created_matcher.empty());
}
public void testAdd() {
matcher.add(Arrays.asList(new Mat()));
assertFalse(matcher.empty());
}
public void testClear() {
matcher.add(Arrays.asList(new Mat()));
matcher.clear();
assertTrue(matcher.empty());
}
public void testClone() {
Mat train = new Mat(1, 1, CvType.CV_8U, new Scalar(123));
Mat truth = train.clone();
matcher.add(Arrays.asList(train));
DescriptorMatcher cloned = matcher.clone();
assertNotNull(cloned);
List<Mat> descriptors = cloned.getTrainDescriptors();
assertEquals(1, descriptors.size());
assertMatEqual(truth, descriptors.get(0));
}
public void testCloneBoolean() {
matcher.add(Arrays.asList(new Mat()));
DescriptorMatcher cloned = matcher.clone(true);
assertNotNull(cloned);
assertTrue(cloned.empty());
}
public void testCreate() {
assertNotNull(matcher);
}
public void testEmpty() {
assertTrue(matcher.empty());
}
public void testGetTrainDescriptors() {
Mat train = new Mat(1, 1, CvType.CV_8U, new Scalar(123));
Mat truth = train.clone();
matcher.add(Arrays.asList(train));
List<Mat> descriptors = matcher.getTrainDescriptors();
assertEquals(1, descriptors.size());
assertMatEqual(truth, descriptors.get(0));
}
public void testIsMaskSupported() {
assertTrue(matcher.isMaskSupported());
}
public void testKnnMatchMatListOfListOfDMatchInt() {
fail("Not yet implemented");
}
public void testKnnMatchMatListOfListOfDMatchIntListOfMat() {
fail("Not yet implemented");
}
public void testKnnMatchMatListOfListOfDMatchIntListOfMatBoolean() {
fail("Not yet implemented");
}
public void testKnnMatchMatMatListOfListOfDMatchInt() {
final int k = 3;
Mat train = getTrainDescriptors();
Mat query = getQueryDescriptors();
List<MatOfDMatch> matches = new ArrayList<MatOfDMatch>();
matcher.knnMatch(query, train, matches, k);
/*
Log.d("knnMatch", "train = " + train);
Log.d("knnMatch", "query = " + query);
matcher.add(train);
matcher.knnMatch(query, matches, k);
*/
assertEquals(query.rows(), matches.size());
for(int i = 0; i<matches.size(); i++)
{
MatOfDMatch vdm = matches.get(i);
//Log.d("knn", "vdm["+i+"]="+vdm.dump());
assertTrue(Math.min(k, train.rows()) >= vdm.total());
for(DMatch dm : vdm.toArray())
{
assertEquals(dm.queryIdx, i);
}
}
}
public void testKnnMatchMatMatListOfListOfDMatchIntMat() {
fail("Not yet implemented");
}
public void testKnnMatchMatMatListOfListOfDMatchIntMatBoolean() {
fail("Not yet implemented");
}
public void testMatchMatListOfDMatch() {
Mat train = getTrainDescriptors();
Mat query = getQueryDescriptors();
MatOfDMatch matches = new MatOfDMatch();
matcher.add(Arrays.asList(train));
matcher.match(query, matches);
assertArrayDMatchEquals(truth, matches.toArray(), EPS);
}
public void testMatchMatListOfDMatchListOfMat() {
Mat train = getTrainDescriptors();
Mat query = getQueryDescriptors();
Mat mask = getMaskImg();
MatOfDMatch matches = new MatOfDMatch();
matcher.add(Arrays.asList(train));
matcher.match(query, matches, Arrays.asList(mask));
assertListDMatchEquals(Arrays.asList(truth[0], truth[1]), matches.toList(), EPS);
}
public void testMatchMatMatListOfDMatch() {
Mat train = getTrainDescriptors();
Mat query = getQueryDescriptors();
MatOfDMatch matches = new MatOfDMatch();
matcher.match(query, train, matches);
assertArrayDMatchEquals(truth, matches.toArray(), EPS);
// OpenCVTestRunner.Log("matches found: " + matches.size());
// for (DMatch m : matches)
// OpenCVTestRunner.Log(m.toString());
}
public void testMatchMatMatListOfDMatchMat() {
Mat train = getTrainDescriptors();
Mat query = getQueryDescriptors();
Mat mask = getMaskImg();
MatOfDMatch matches = new MatOfDMatch();
matcher.match(query, train, matches, mask);
assertListDMatchEquals(Arrays.asList(truth[0], truth[1]), matches.toList(), EPS);
}
public void testRadiusMatchMatListOfListOfDMatchFloat() {
fail("Not yet implemented");
}
public void testRadiusMatchMatListOfListOfDMatchFloatListOfMat() {
fail("Not yet implemented");
}
public void testRadiusMatchMatListOfListOfDMatchFloatListOfMatBoolean() {
fail("Not yet implemented");
}
public void testRadiusMatchMatMatListOfListOfDMatchFloat() {
fail("Not yet implemented");
}
public void testRadiusMatchMatMatListOfListOfDMatchFloatMat() {
fail("Not yet implemented");
}
public void testRadiusMatchMatMatListOfListOfDMatchFloatMatBoolean() {
fail("Not yet implemented");
}
public void testRead() {
String filename = OpenCVTestRunner.getTempFileName("yml");
writeFile(filename, "%YAML:1.0\n---\n");
matcher.read(filename);
assertTrue(true);// BruteforceMatcher has no settings
}
public void testTrain() {
matcher.train();// BruteforceMatcher does not need to train
}
public void testWrite() {
String filename = OpenCVTestRunner.getTempFileName("yml");
matcher.write(filename);
String truth = "%YAML:1.0\n---\n";
assertEquals(truth, readFile(filename));
}
}
@@ -0,0 +1,262 @@
package org.opencv.test.features2d;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.MatOfDMatch;
import org.opencv.core.MatOfKeyPoint;
import org.opencv.core.Point;
import org.opencv.core.Scalar;
import org.opencv.core.DMatch;
import org.opencv.features2d.DescriptorMatcher;
import org.opencv.features2d.FastFeatureDetector;
import org.opencv.test.OpenCVTestCase;
import org.opencv.test.OpenCVTestRunner;
import org.opencv.imgproc.Imgproc;
import org.opencv.features2d.Feature2D;
public class BruteForceHammingDescriptorMatcherTest extends OpenCVTestCase {
DescriptorMatcher matcher;
int matSize;
DMatch[] truth;
private Mat getMaskImg() {
return new Mat(4, 4, CvType.CV_8U, new Scalar(0)) {
{
put(0, 0, 1, 1, 1, 1, 1, 1, 1, 1);
}
};
}
private Mat getQueryDescriptors() {
return getTestDescriptors(getQueryImg());
}
private Mat getQueryImg() {
Mat img = new Mat(matSize, matSize, CvType.CV_8U, new Scalar(255));
Imgproc.line(img, new Point(40, matSize - 40), new Point(matSize - 50, 50), new Scalar(0), 8);
return img;
}
private Mat getTestDescriptors(Mat img) {
MatOfKeyPoint keypoints = new MatOfKeyPoint();
Mat descriptors = new Mat();
Feature2D detector = FastFeatureDetector.create();
Feature2D extractor = createClassInstance(XFEATURES2D+"BriefDescriptorExtractor", DEFAULT_FACTORY, null, null);
detector.detect(img, keypoints);
extractor.compute(img, keypoints, descriptors);
return descriptors;
}
private Mat getTrainDescriptors() {
return getTestDescriptors(getTrainImg());
}
private Mat getTrainImg() {
Mat img = new Mat(matSize, matSize, CvType.CV_8U, new Scalar(255));
Imgproc.line(img, new Point(40, 40), new Point(matSize - 40, matSize - 40), new Scalar(0), 8);
return img;
}
protected void setUp() throws Exception {
super.setUp();
matcher = DescriptorMatcher.create(DescriptorMatcher.BRUTEFORCE_HAMMING);
matSize = 100;
truth = new DMatch[] {
new DMatch(0, 0, 0, 51),
new DMatch(1, 2, 0, 42),
new DMatch(2, 1, 0, 40),
new DMatch(3, 3, 0, 53) };
}
public void testAdd() {
matcher.add(Arrays.asList(new Mat()));
assertFalse(matcher.empty());
}
public void testClear() {
matcher.add(Arrays.asList(new Mat()));
matcher.clear();
assertTrue(matcher.empty());
}
public void testClone() {
Mat train = new Mat(1, 1, CvType.CV_8U, new Scalar(123));
Mat truth = train.clone();
matcher.add(Arrays.asList(train));
DescriptorMatcher cloned = matcher.clone();
assertNotNull(cloned);
List<Mat> descriptors = cloned.getTrainDescriptors();
assertEquals(1, descriptors.size());
assertMatEqual(truth, descriptors.get(0));
}
public void testCloneBoolean() {
matcher.add(Arrays.asList(new Mat()));
DescriptorMatcher cloned = matcher.clone(true);
assertNotNull(cloned);
assertTrue(cloned.empty());
}
public void testCreate() {
assertNotNull(matcher);
}
public void testEmpty() {
assertTrue(matcher.empty());
}
public void testGetTrainDescriptors() {
Mat train = new Mat(1, 1, CvType.CV_8U, new Scalar(123));
Mat truth = train.clone();
matcher.add(Arrays.asList(train));
List<Mat> descriptors = matcher.getTrainDescriptors();
assertEquals(1, descriptors.size());
assertMatEqual(truth, descriptors.get(0));
}
public void testIsMaskSupported() {
assertTrue(matcher.isMaskSupported());
}
public void testKnnMatchMatListOfListOfDMatchInt() {
fail("Not yet implemented");
}
public void testKnnMatchMatListOfListOfDMatchIntListOfMat() {
fail("Not yet implemented");
}
public void testKnnMatchMatListOfListOfDMatchIntListOfMatBoolean() {
fail("Not yet implemented");
}
public void testKnnMatchMatMatListOfListOfDMatchInt() {
fail("Not yet implemented");
}
public void testKnnMatchMatMatListOfListOfDMatchIntMat() {
fail("Not yet implemented");
}
public void testKnnMatchMatMatListOfListOfDMatchIntMatBoolean() {
fail("Not yet implemented");
}
public void testMatchMatListOfDMatch() {
Mat train = getTrainDescriptors();
Mat query = getQueryDescriptors();
MatOfDMatch matches = new MatOfDMatch();
matcher.add(Arrays.asList(train));
matcher.match(query, matches);
assertListDMatchEquals(Arrays.asList(truth), matches.toList(), EPS);
}
public void testMatchMatListOfDMatchListOfMat() {
Mat train = getTrainDescriptors();
Mat query = getQueryDescriptors();
Mat mask = getMaskImg();
MatOfDMatch matches = new MatOfDMatch();
matcher.add(Arrays.asList(train));
matcher.match(query, matches, Arrays.asList(mask));
assertListDMatchEquals(Arrays.asList(truth[0], truth[1]), matches.toList(), EPS);
}
public void testMatchMatMatListOfDMatch() {
Mat train = getTrainDescriptors();
Mat query = getQueryDescriptors();
MatOfDMatch matches = new MatOfDMatch();
matcher.match(query, train, matches);
assertListDMatchEquals(Arrays.asList(truth), matches.toList(), EPS);
}
public void testMatchMatMatListOfDMatchMat() {
Mat train = getTrainDescriptors();
Mat query = getQueryDescriptors();
Mat mask = getMaskImg();
MatOfDMatch matches = new MatOfDMatch();
matcher.match(query, train, matches, mask);
assertListDMatchEquals(Arrays.asList(truth[0], truth[1]), matches.toList(), EPS);
}
public void testRadiusMatchMatListOfListOfDMatchFloat() {
Mat train = getTrainDescriptors();
Mat query = getQueryDescriptors();
ArrayList<MatOfDMatch> matches = new ArrayList<MatOfDMatch>();
matcher.radiusMatch(query, train, matches, 50.f);
assertEquals(4, matches.size());
assertTrue(matches.get(0).empty());
assertMatEqual(matches.get(1), new MatOfDMatch(truth[1]), EPS);
assertMatEqual(matches.get(2), new MatOfDMatch(truth[2]), EPS);
assertTrue(matches.get(3).empty());
}
public void testRadiusMatchMatListOfListOfDMatchFloatListOfMat() {
fail("Not yet implemented");
}
public void testRadiusMatchMatListOfListOfDMatchFloatListOfMatBoolean() {
fail("Not yet implemented");
}
public void testRadiusMatchMatMatListOfListOfDMatchFloat() {
fail("Not yet implemented");
}
public void testRadiusMatchMatMatListOfListOfDMatchFloatMat() {
fail("Not yet implemented");
}
public void testRadiusMatchMatMatListOfListOfDMatchFloatMatBoolean() {
fail("Not yet implemented");
}
public void testRead() {
String filename = OpenCVTestRunner.getTempFileName("yml");
writeFile(filename, "%YAML:1.0\n---\n");
matcher.read(filename);
assertTrue(true);// BruteforceMatcher has no settings
}
public void testTrain() {
matcher.train();// BruteforceMatcher does not need to train
}
public void testWrite() {
String filename = OpenCVTestRunner.getTempFileName("yml");
matcher.write(filename);
String truth = "%YAML:1.0\n---\n";
assertEquals(truth, readFile(filename));
}
}
@@ -0,0 +1,257 @@
package org.opencv.test.features2d;
import java.util.Arrays;
import java.util.List;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.MatOfDMatch;
import org.opencv.core.MatOfKeyPoint;
import org.opencv.core.Point;
import org.opencv.core.Scalar;
import org.opencv.core.DMatch;
import org.opencv.features2d.DescriptorMatcher;
import org.opencv.features2d.FastFeatureDetector;
import org.opencv.test.OpenCVTestCase;
import org.opencv.test.OpenCVTestRunner;
import org.opencv.imgproc.Imgproc;
import org.opencv.features2d.Feature2D;
public class BruteForceHammingLUTDescriptorMatcherTest extends OpenCVTestCase {
DescriptorMatcher matcher;
int matSize;
DMatch[] truth;
private Mat getMaskImg() {
return new Mat(4, 4, CvType.CV_8U, new Scalar(0)) {
{
put(0, 0, 1, 1, 1, 1, 1, 1, 1, 1);
}
};
}
private Mat getQueryDescriptors() {
return getTestDescriptors(getQueryImg());
}
private Mat getQueryImg() {
Mat img = new Mat(matSize, matSize, CvType.CV_8U, new Scalar(255));
Imgproc.line(img, new Point(40, matSize - 40), new Point(matSize - 50, 50), new Scalar(0), 8);
return img;
}
private Mat getTestDescriptors(Mat img) {
MatOfKeyPoint keypoints = new MatOfKeyPoint();
Mat descriptors = new Mat();
Feature2D detector = FastFeatureDetector.create();
Feature2D extractor = createClassInstance(XFEATURES2D+"BriefDescriptorExtractor", DEFAULT_FACTORY, null, null);
detector.detect(img, keypoints);
extractor.compute(img, keypoints, descriptors);
return descriptors;
}
private Mat getTrainDescriptors() {
return getTestDescriptors(getTrainImg());
}
private Mat getTrainImg() {
Mat img = new Mat(matSize, matSize, CvType.CV_8U, new Scalar(255));
Imgproc.line(img, new Point(40, 40), new Point(matSize - 40, matSize - 40), new Scalar(0), 8);
return img;
}
protected void setUp() throws Exception {
super.setUp();
matcher = DescriptorMatcher.create(DescriptorMatcher.BRUTEFORCE_HAMMINGLUT);
matSize = 100;
truth = new DMatch[] {
new DMatch(0, 0, 0, 51),
new DMatch(1, 2, 0, 42),
new DMatch(2, 1, 0, 40),
new DMatch(3, 3, 0, 53) };
}
public void testAdd() {
matcher.add(Arrays.asList(new Mat()));
assertFalse(matcher.empty());
}
public void testClear() {
matcher.add(Arrays.asList(new Mat()));
matcher.clear();
assertTrue(matcher.empty());
}
public void testClone() {
Mat train = new Mat(1, 1, CvType.CV_8U, new Scalar(123));
Mat truth = train.clone();
matcher.add(Arrays.asList(train));
DescriptorMatcher cloned = matcher.clone();
assertNotNull(cloned);
List<Mat> descriptors = cloned.getTrainDescriptors();
assertEquals(1, descriptors.size());
assertMatEqual(truth, descriptors.get(0));
}
public void testCloneBoolean() {
matcher.add(Arrays.asList(new Mat()));
DescriptorMatcher cloned = matcher.clone(true);
assertNotNull(cloned);
assertTrue(cloned.empty());
}
public void testCreate() {
assertNotNull(matcher);
}
public void testEmpty() {
assertTrue(matcher.empty());
}
public void testGetTrainDescriptors() {
Mat train = new Mat(1, 1, CvType.CV_8U, new Scalar(123));
Mat truth = train.clone();
matcher.add(Arrays.asList(train));
List<Mat> descriptors = matcher.getTrainDescriptors();
assertEquals(1, descriptors.size());
assertMatEqual(truth, descriptors.get(0));
}
public void testIsMaskSupported() {
assertTrue(matcher.isMaskSupported());
}
public void testKnnMatchMatListOfListOfDMatchInt() {
fail("Not yet implemented");
}
public void testKnnMatchMatListOfListOfDMatchIntListOfMat() {
fail("Not yet implemented");
}
public void testKnnMatchMatListOfListOfDMatchIntListOfMatBoolean() {
fail("Not yet implemented");
}
public void testKnnMatchMatMatListOfListOfDMatchInt() {
fail("Not yet implemented");
}
public void testKnnMatchMatMatListOfListOfDMatchIntMat() {
fail("Not yet implemented");
}
public void testKnnMatchMatMatListOfListOfDMatchIntMatBoolean() {
fail("Not yet implemented");
}
public void testMatchMatListOfDMatch() {
Mat train = getTrainDescriptors();
Mat query = getQueryDescriptors();
MatOfDMatch matches = new MatOfDMatch();
matcher.add(Arrays.asList(train));
matcher.match(query, matches);
assertArrayDMatchEquals(truth, matches.toArray(), EPS);
}
public void testMatchMatListOfDMatchListOfMat() {
Mat train = getTrainDescriptors();
Mat query = getQueryDescriptors();
Mat mask = getMaskImg();
MatOfDMatch matches = new MatOfDMatch();
matcher.add(Arrays.asList(train));
matcher.match(query, matches, Arrays.asList(mask));
assertListDMatchEquals(Arrays.asList(truth[0], truth[1]), matches.toList(), EPS);
}
public void testMatchMatMatListOfDMatch() {
Mat train = getTrainDescriptors();
Mat query = getQueryDescriptors();
MatOfDMatch matches = new MatOfDMatch();
matcher.match(query, train, matches);
/*
OpenCVTestRunner.Log("matches found: " + matches.size());
for (DMatch m : matches.toArray())
OpenCVTestRunner.Log(m.toString());
*/
assertArrayDMatchEquals(truth, matches.toArray(), EPS);
}
public void testMatchMatMatListOfDMatchMat() {
Mat train = getTrainDescriptors();
Mat query = getQueryDescriptors();
Mat mask = getMaskImg();
MatOfDMatch matches = new MatOfDMatch();
matcher.match(query, train, matches, mask);
assertListDMatchEquals(Arrays.asList(truth[0], truth[1]), matches.toList(), EPS);
}
public void testRadiusMatchMatListOfListOfDMatchFloat() {
fail("Not yet implemented");
}
public void testRadiusMatchMatListOfListOfDMatchFloatListOfMat() {
fail("Not yet implemented");
}
public void testRadiusMatchMatListOfListOfDMatchFloatListOfMatBoolean() {
fail("Not yet implemented");
}
public void testRadiusMatchMatMatListOfListOfDMatchFloat() {
fail("Not yet implemented");
}
public void testRadiusMatchMatMatListOfListOfDMatchFloatMat() {
fail("Not yet implemented");
}
public void testRadiusMatchMatMatListOfListOfDMatchFloatMatBoolean() {
fail("Not yet implemented");
}
public void testRead() {
String filename = OpenCVTestRunner.getTempFileName("yml");
writeFile(filename, "%YAML:1.0\n---\n");
matcher.read(filename);
assertTrue(true);// BruteforceMatcher has no settings
}
public void testTrain() {
matcher.train();// BruteforceMatcher does not need to train
}
public void testWrite() {
String filename = OpenCVTestRunner.getTempFileName("yml");
matcher.write(filename);
String truth = "%YAML:1.0\n---\n";
assertEquals(truth, readFile(filename));
}
}
@@ -0,0 +1,268 @@
package org.opencv.test.features2d;
import java.util.Arrays;
import java.util.List;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.MatOfDMatch;
import org.opencv.core.MatOfKeyPoint;
import org.opencv.core.Point;
import org.opencv.core.Scalar;
import org.opencv.core.DMatch;
import org.opencv.features2d.DescriptorMatcher;
import org.opencv.core.KeyPoint;
import org.opencv.test.OpenCVTestCase;
import org.opencv.test.OpenCVTestRunner;
import org.opencv.imgproc.Imgproc;
import org.opencv.features2d.Feature2D;
public class BruteForceL1DescriptorMatcherTest extends OpenCVTestCase {
DescriptorMatcher matcher;
int matSize;
DMatch[] truth;
private Mat getMaskImg() {
return new Mat(5, 2, CvType.CV_8U, new Scalar(0)) {
{
put(0, 0, 1, 1, 1, 1);
}
};
}
private Mat getQueryDescriptors() {
Mat img = getQueryImg();
MatOfKeyPoint keypoints = new MatOfKeyPoint();
Mat descriptors = new Mat();
Feature2D detector = createClassInstance(XFEATURES2D+"SURF", DEFAULT_FACTORY, null, null);
Feature2D extractor = createClassInstance(XFEATURES2D+"SURF", DEFAULT_FACTORY, null, null);
setProperty(detector, "extended", "boolean", true);
setProperty(detector, "hessianThreshold", "double", 8000);
setProperty(detector, "nOctaveLayers", "int", 2);
setProperty(detector, "nOctaves", "int", 3);
setProperty(detector, "upright", "boolean", false);
detector.detect(img, keypoints);
extractor.compute(img, keypoints, descriptors);
return descriptors;
}
private Mat getQueryImg() {
Mat cross = new Mat(matSize, matSize, CvType.CV_8U, new Scalar(255));
Imgproc.line(cross, new Point(30, matSize / 2), new Point(matSize - 31, matSize / 2), new Scalar(100), 3);
Imgproc.line(cross, new Point(matSize / 2, 30), new Point(matSize / 2, matSize - 31), new Scalar(100), 3);
return cross;
}
private Mat getTrainDescriptors() {
Mat img = getTrainImg();
MatOfKeyPoint keypoints = new MatOfKeyPoint(new KeyPoint(50, 50, 16, 0, 20000, 1, -1), new KeyPoint(42, 42, 16, 160, 10000, 1, -1));
Mat descriptors = new Mat();
Feature2D extractor = createClassInstance(XFEATURES2D+"SURF", DEFAULT_FACTORY, null, null);
extractor.compute(img, keypoints, descriptors);
return descriptors;
}
private Mat getTrainImg() {
Mat cross = new Mat(matSize, matSize, CvType.CV_8U, new Scalar(255));
Imgproc.line(cross, new Point(20, matSize / 2), new Point(matSize - 21, matSize / 2), new Scalar(100), 2);
Imgproc.line(cross, new Point(matSize / 2, 20), new Point(matSize / 2, matSize - 21), new Scalar(100), 2);
return cross;
}
protected void setUp() throws Exception {
super.setUp();
matcher = DescriptorMatcher.create(DescriptorMatcher.BRUTEFORCE_L1);
matSize = 100;
truth = new DMatch[] {
new DMatch(0, 0, 0, 3.0710702f),
new DMatch(1, 1, 0, 3.562016f),
new DMatch(2, 1, 0, 1.3682679f),
new DMatch(3, 1, 0, 1.3012862f),
new DMatch(4, 1, 0, 1.1852086f)
};
}
public void testAdd() {
matcher.add(Arrays.asList(new Mat()));
assertFalse(matcher.empty());
}
public void testClear() {
matcher.add(Arrays.asList(new Mat()));
matcher.clear();
assertTrue(matcher.empty());
}
public void testClone() {
Mat train = new Mat(1, 1, CvType.CV_8U, new Scalar(123));
Mat truth = train.clone();
matcher.add(Arrays.asList(train));
DescriptorMatcher cloned = matcher.clone();
assertNotNull(cloned);
List<Mat> descriptors = cloned.getTrainDescriptors();
assertEquals(1, descriptors.size());
assertMatEqual(truth, descriptors.get(0));
}
public void testCloneBoolean() {
matcher.add(Arrays.asList(new Mat()));
DescriptorMatcher cloned = matcher.clone(true);
assertNotNull(cloned);
assertTrue(cloned.empty());
}
public void testCreate() {
assertNotNull(matcher);
}
public void testEmpty() {
assertTrue(matcher.empty());
}
public void testGetTrainDescriptors() {
Mat train = new Mat(1, 1, CvType.CV_8U, new Scalar(123));
Mat truth = train.clone();
matcher.add(Arrays.asList(train));
List<Mat> descriptors = matcher.getTrainDescriptors();
assertEquals(1, descriptors.size());
assertMatEqual(truth, descriptors.get(0));
}
public void testIsMaskSupported() {
assertTrue(matcher.isMaskSupported());
}
public void testKnnMatchMatListOfListOfDMatchInt() {
fail("Not yet implemented");
}
public void testKnnMatchMatListOfListOfDMatchIntListOfMat() {
fail("Not yet implemented");
}
public void testKnnMatchMatListOfListOfDMatchIntListOfMatBoolean() {
fail("Not yet implemented");
}
public void testKnnMatchMatMatListOfListOfDMatchInt() {
fail("Not yet implemented");
}
public void testKnnMatchMatMatListOfListOfDMatchIntMat() {
fail("Not yet implemented");
}
public void testKnnMatchMatMatListOfListOfDMatchIntMatBoolean() {
fail("Not yet implemented");
}
public void testMatchMatListOfDMatch() {
Mat train = getTrainDescriptors();
Mat query = getQueryDescriptors();
MatOfDMatch matches = new MatOfDMatch();
matcher.add(Arrays.asList(train));
matcher.match(query, matches);
assertArrayDMatchEquals(truth, matches.toArray(), EPS);
}
public void testMatchMatListOfDMatchListOfMat() {
Mat train = getTrainDescriptors();
Mat query = getQueryDescriptors();
Mat mask = getMaskImg();
MatOfDMatch matches = new MatOfDMatch();
matcher.add(Arrays.asList(train));
matcher.match(query, matches, Arrays.asList(mask));
assertListDMatchEquals(Arrays.asList(truth[0], truth[1]), matches.toList(), EPS);
}
public void testMatchMatMatListOfDMatch() {
Mat train = getTrainDescriptors();
Mat query = getQueryDescriptors();
MatOfDMatch matches = new MatOfDMatch();
matcher.match(query, train, matches);
assertArrayDMatchEquals(truth, matches.toArray(), EPS);
}
public void testMatchMatMatListOfDMatchMat() {
Mat train = getTrainDescriptors();
Mat query = getQueryDescriptors();
Mat mask = getMaskImg();
MatOfDMatch matches = new MatOfDMatch();
matcher.match(query, train, matches, mask);
assertListDMatchEquals(Arrays.asList(truth[0], truth[1]), matches.toList(), EPS);
}
public void testRadiusMatchMatListOfListOfDMatchFloat() {
fail("Not yet implemented");
}
public void testRadiusMatchMatListOfListOfDMatchFloatListOfMat() {
fail("Not yet implemented");
}
public void testRadiusMatchMatListOfListOfDMatchFloatListOfMatBoolean() {
fail("Not yet implemented");
}
public void testRadiusMatchMatMatListOfListOfDMatchFloat() {
fail("Not yet implemented");
}
public void testRadiusMatchMatMatListOfListOfDMatchFloatMat() {
fail("Not yet implemented");
}
public void testRadiusMatchMatMatListOfListOfDMatchFloatMatBoolean() {
fail("Not yet implemented");
}
public void testRead() {
String filename = OpenCVTestRunner.getTempFileName("yml");
writeFile(filename, "%YAML:1.0\n---\n");
matcher.read(filename);
assertTrue(true);// BruteforceMatcher has no settings
}
public void testTrain() {
matcher.train();// BruteforceMatcher does not need to train
}
public void testWrite() {
String filename = OpenCVTestRunner.getTempFileName("yml");
matcher.write(filename);
String truth = "%YAML:1.0\n---\n";
assertEquals(truth, readFile(filename));
}
}
@@ -0,0 +1,280 @@
package org.opencv.test.features2d;
import java.util.Arrays;
import java.util.List;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.MatOfDMatch;
import org.opencv.core.MatOfKeyPoint;
import org.opencv.core.Point;
import org.opencv.core.Scalar;
import org.opencv.core.DMatch;
import org.opencv.features2d.DescriptorMatcher;
import org.opencv.core.KeyPoint;
import org.opencv.test.OpenCVTestCase;
import org.opencv.test.OpenCVTestRunner;
import org.opencv.imgproc.Imgproc;
import org.opencv.features2d.Feature2D;
public class BruteForceSL2DescriptorMatcherTest extends OpenCVTestCase {
DescriptorMatcher matcher;
int matSize;
DMatch[] truth;
private Mat getMaskImg() {
return new Mat(5, 2, CvType.CV_8U, new Scalar(0)) {
{
put(0, 0, 1, 1, 1, 1);
}
};
}
/*
private float sqr(float val){
return val * val;
}
*/
private Mat getQueryDescriptors() {
Mat img = getQueryImg();
MatOfKeyPoint keypoints = new MatOfKeyPoint();
Mat descriptors = new Mat();
Feature2D detector = createClassInstance(XFEATURES2D+"SURF", DEFAULT_FACTORY, null, null);
Feature2D extractor = createClassInstance(XFEATURES2D+"SURF", DEFAULT_FACTORY, null, null);
setProperty(detector, "hessianThreshold", "double", 8000);
setProperty(detector, "nOctaves", "int", 3);
setProperty(detector, "nOctaveLayers", "int", 4);
setProperty(detector, "upright", "boolean", false);
detector.detect(img, keypoints);
extractor.compute(img, keypoints, descriptors);
return descriptors;
}
private Mat getQueryImg() {
Mat cross = new Mat(matSize, matSize, CvType.CV_8U, new Scalar(255));
Imgproc.line(cross, new Point(30, matSize / 2), new Point(matSize - 31, matSize / 2), new Scalar(100), 3);
Imgproc.line(cross, new Point(matSize / 2, 30), new Point(matSize / 2, matSize - 31), new Scalar(100), 3);
return cross;
}
private Mat getTrainDescriptors() {
Mat img = getTrainImg();
MatOfKeyPoint keypoints = new MatOfKeyPoint(new KeyPoint(50, 50, 16, 0, 20000, 1, -1), new KeyPoint(42, 42, 16, 160, 10000, 1, -1));
Mat descriptors = new Mat();
Feature2D extractor = createClassInstance(XFEATURES2D+"SURF", DEFAULT_FACTORY, null, null);
extractor.compute(img, keypoints, descriptors);
return descriptors;
}
private Mat getTrainImg() {
Mat cross = new Mat(matSize, matSize, CvType.CV_8U, new Scalar(255));
Imgproc.line(cross, new Point(20, matSize / 2), new Point(matSize - 21, matSize / 2), new Scalar(100), 2);
Imgproc.line(cross, new Point(matSize / 2, 20), new Point(matSize / 2, matSize - 21), new Scalar(100), 2);
return cross;
}
protected void setUp() throws Exception {
super.setUp();
matcher = DescriptorMatcher.create(DescriptorMatcher.BRUTEFORCE_SL2);
matSize = 100;
truth = new DMatch[] {
new DMatch(0, 0, 0, 0.37933317f),
new DMatch(1, 1, 0, 0.8421953f),
new DMatch(2, 1, 0, 0.0968556f),
new DMatch(3, 1, 0, 0.0855606f),
new DMatch(4, 1, 0, 0.07033461f)
};
}
public void testAdd() {
matcher.add(Arrays.asList(new Mat()));
assertFalse(matcher.empty());
}
public void testClear() {
matcher.add(Arrays.asList(new Mat()));
matcher.clear();
assertTrue(matcher.empty());
}
public void testClone() {
Mat train = new Mat(1, 1, CvType.CV_8U, new Scalar(123));
Mat truth = train.clone();
matcher.add(Arrays.asList(train));
DescriptorMatcher cloned = matcher.clone();
assertNotNull(cloned);
List<Mat> descriptors = cloned.getTrainDescriptors();
assertEquals(1, descriptors.size());
assertMatEqual(truth, descriptors.get(0));
}
public void testCloneBoolean() {
matcher.add(Arrays.asList(new Mat()));
DescriptorMatcher cloned = matcher.clone(true);
assertNotNull(cloned);
assertTrue(cloned.empty());
}
public void testCreate() {
assertNotNull(matcher);
}
public void testEmpty() {
assertTrue(matcher.empty());
}
public void testGetTrainDescriptors() {
Mat train = new Mat(1, 1, CvType.CV_8U, new Scalar(123));
Mat truth = train.clone();
matcher.add(Arrays.asList(train));
List<Mat> descriptors = matcher.getTrainDescriptors();
assertEquals(1, descriptors.size());
assertMatEqual(truth, descriptors.get(0));
}
public void testIsMaskSupported() {
assertTrue(matcher.isMaskSupported());
}
public void testKnnMatchMatListOfListOfDMatchInt() {
fail("Not yet implemented");
}
public void testKnnMatchMatListOfListOfDMatchIntListOfMat() {
fail("Not yet implemented");
}
public void testKnnMatchMatListOfListOfDMatchIntListOfMatBoolean() {
fail("Not yet implemented");
}
public void testKnnMatchMatMatListOfListOfDMatchInt() {
fail("Not yet implemented");
}
public void testKnnMatchMatMatListOfListOfDMatchIntMat() {
fail("Not yet implemented");
}
public void testKnnMatchMatMatListOfListOfDMatchIntMatBoolean() {
fail("Not yet implemented");
}
public void testMatchMatListOfDMatch() {
Mat train = getTrainDescriptors();
Mat query = getQueryDescriptors();
MatOfDMatch matches = new MatOfDMatch();
matcher.add(Arrays.asList(train));
matcher.match(query, matches);
OpenCVTestRunner.Log(matches);
OpenCVTestRunner.Log(matches);
OpenCVTestRunner.Log(matches);
assertArrayDMatchEquals(truth, matches.toArray(), EPS);
}
public void testMatchMatListOfDMatchListOfMat() {
Mat train = getTrainDescriptors();
Mat query = getQueryDescriptors();
Mat mask = getMaskImg();
MatOfDMatch matches = new MatOfDMatch();
matcher.add(Arrays.asList(train));
matcher.match(query, matches, Arrays.asList(mask));
assertListDMatchEquals(Arrays.asList(truth[0], truth[1]), matches.toList(), EPS);
}
public void testMatchMatMatListOfDMatch() {
Mat train = getTrainDescriptors();
Mat query = getQueryDescriptors();
MatOfDMatch matches = new MatOfDMatch();
matcher.match(query, train, matches);
assertArrayDMatchEquals(truth, matches.toArray(), EPS);
// OpenCVTestRunner.Log("matches found: " + matches.size());
// for (DMatch m : matches)
// OpenCVTestRunner.Log(m.toString());
}
public void testMatchMatMatListOfDMatchMat() {
Mat train = getTrainDescriptors();
Mat query = getQueryDescriptors();
Mat mask = getMaskImg();
MatOfDMatch matches = new MatOfDMatch();
matcher.match(query, train, matches, mask);
assertListDMatchEquals(Arrays.asList(truth[0], truth[1]), matches.toList(), EPS);
}
public void testRadiusMatchMatListOfListOfDMatchFloat() {
fail("Not yet implemented");
}
public void testRadiusMatchMatListOfListOfDMatchFloatListOfMat() {
fail("Not yet implemented");
}
public void testRadiusMatchMatListOfListOfDMatchFloatListOfMatBoolean() {
fail("Not yet implemented");
}
public void testRadiusMatchMatMatListOfListOfDMatchFloat() {
fail("Not yet implemented");
}
public void testRadiusMatchMatMatListOfListOfDMatchFloatMat() {
fail("Not yet implemented");
}
public void testRadiusMatchMatMatListOfListOfDMatchFloatMatBoolean() {
fail("Not yet implemented");
}
public void testRead() {
String filename = OpenCVTestRunner.getTempFileName("yml");
writeFile(filename, "%YAML:1.0\n---\n");
matcher.read(filename);
assertTrue(true);// BruteforceMatcher has no settings
}
public void testTrain() {
matcher.train();// BruteforceMatcher does not need to train
}
public void testWrite() {
String filename = OpenCVTestRunner.getTempFileName("yml");
matcher.write(filename);
String truth = "%YAML:1.0\n---\n";
assertEquals(truth, readFile(filename));
}
}
@@ -0,0 +1,39 @@
package org.opencv.test.features2d;
import org.opencv.test.OpenCVTestCase;
public class DENSEFeatureDetectorTest extends OpenCVTestCase {
public void testCreate() {
fail("Not yet implemented");
}
public void testDetectListOfMatListOfListOfKeyPoint() {
fail("Not yet implemented");
}
public void testDetectListOfMatListOfListOfKeyPointListOfMat() {
fail("Not yet implemented");
}
public void testDetectMatListOfKeyPoint() {
fail("Not yet implemented");
}
public void testDetectMatListOfKeyPointMat() {
fail("Not yet implemented");
}
public void testEmpty() {
fail("Not yet implemented");
}
public void testRead() {
fail("Not yet implemented");
}
public void testWrite() {
fail("Not yet implemented");
}
}
@@ -0,0 +1,141 @@
package org.opencv.test.features2d;
import java.util.Arrays;
import org.opencv.core.Core;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.MatOfKeyPoint;
import org.opencv.core.Point;
import org.opencv.core.Scalar;
import org.opencv.features2d.FastFeatureDetector;
import org.opencv.core.KeyPoint;
import org.opencv.test.OpenCVTestCase;
import org.opencv.test.OpenCVTestRunner;
import org.opencv.imgproc.Imgproc;
public class FASTFeatureDetectorTest extends OpenCVTestCase {
FastFeatureDetector detector;
KeyPoint[] truth;
private Mat getMaskImg() {
Mat mask = new Mat(100, 100, CvType.CV_8U, new Scalar(255));
Mat right = mask.submat(0, 100, 50, 100);
right.setTo(new Scalar(0));
return mask;
}
private Mat getTestImg() {
Mat img = new Mat(100, 100, CvType.CV_8U, new Scalar(255));
Imgproc.line(img, new Point(30, 30), new Point(70, 70), new Scalar(0), 8);
return img;
}
@Override
protected void setUp() throws Exception {
super.setUp();
detector = FastFeatureDetector.create();
truth = new KeyPoint[] { new KeyPoint(32, 27, 7, -1, 254, 0, -1), new KeyPoint(27, 32, 7, -1, 254, 0, -1), new KeyPoint(73, 68, 7, -1, 254, 0, -1),
new KeyPoint(68, 73, 7, -1, 254, 0, -1) };
}
public void testCreate() {
assertNotNull(detector);
}
public void testDetectListOfMatListOfListOfKeyPoint() {
fail("Not yet implemented");
}
public void testDetectListOfMatListOfListOfKeyPointListOfMat() {
fail("Not yet implemented");
}
public void testDetectMatListOfKeyPoint() {
Mat img = getTestImg();
MatOfKeyPoint keypoints = new MatOfKeyPoint();
detector.detect(img, keypoints);
assertListKeyPointEquals(Arrays.asList(truth), keypoints.toList(), EPS);
// OpenCVTestRunner.Log("points found: " + keypoints.size());
// for (KeyPoint kp : keypoints)
// OpenCVTestRunner.Log(kp.toString());
}
public void testDetectMatListOfKeyPointMat() {
Mat img = getTestImg();
Mat mask = getMaskImg();
MatOfKeyPoint keypoints = new MatOfKeyPoint();
detector.detect(img, keypoints, mask);
assertListKeyPointEquals(Arrays.asList(truth[0], truth[1]), keypoints.toList(), EPS);
}
public void testEmpty() {
// assertFalse(detector.empty());
fail("Not yet implemented"); // FAST does not override empty() method
}
public void testRead() {
String filename = OpenCVTestRunner.getTempFileName("xml");
writeFile(filename, "<?xml version=\"1.0\"?>\n<opencv_storage>\n<name>Feature2D.FastFeatureDetector</name>\n<threshold>10</threshold>\n<nonmaxSuppression>1</nonmaxSuppression>\n<type>2</type>\n</opencv_storage>\n");
detector.read(filename);
assertEquals(10, detector.getThreshold());
assertEquals(true, detector.getNonmaxSuppression());
assertEquals(2, detector.getType());
MatOfKeyPoint keypoints1 = new MatOfKeyPoint();
detector.detect(grayChess, keypoints1);
writeFile(filename, "<?xml version=\"1.0\"?>\n<opencv_storage>\n<name>Feature2D.FastFeatureDetector</name>\n<threshold>150</threshold>\n<nonmaxSuppression>1</nonmaxSuppression>\n<type>2</type>\n</opencv_storage>\n");
detector.read(filename);
MatOfKeyPoint keypoints2 = new MatOfKeyPoint();
detector.detect(grayChess, keypoints2);
assertTrue(keypoints2.total() <= keypoints1.total());
}
public void testReadYml() {
String filename = OpenCVTestRunner.getTempFileName("yml");
writeFile(filename, "%YAML:1.0\n---\nthreshold: 130\nnonmaxSuppression: 1\ntype: 2\n");
detector.read(filename);
assertEquals(130, detector.getThreshold());
assertEquals(true, detector.getNonmaxSuppression());
assertEquals(2, detector.getType());
MatOfKeyPoint keypoints1 = new MatOfKeyPoint();
detector.detect(grayChess, keypoints1);
writeFile(filename, "%YAML:1.0\n---\nthreshold: 150\nnonmaxSuppression: 1\ntype: 2\n");
detector.read(filename);
MatOfKeyPoint keypoints2 = new MatOfKeyPoint();
detector.detect(grayChess, keypoints2);
assertTrue(keypoints2.total() <= keypoints1.total());
}
public void testWriteYml() {
String filename = OpenCVTestRunner.getTempFileName("yml");
detector.write(filename);
String truth = "%YAML:1.0\n---\nname: \"Feature2D.FastFeatureDetector\"\nthreshold: 10\nnonmaxSuppression: 1\ntype: 2\n";
String data = readFile(filename);
assertEquals(truth, data);
}
}
@@ -0,0 +1,172 @@
package org.opencv.test.features2d;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.opencv.calib3d.Calib3d;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.MatOfInt;
import org.opencv.core.MatOfDMatch;
import org.opencv.core.MatOfKeyPoint;
import org.opencv.core.MatOfPoint2f;
import org.opencv.core.Point;
import org.opencv.core.Range;
import org.opencv.core.Scalar;
import org.opencv.core.DMatch;
import org.opencv.features2d.DescriptorMatcher;
import org.opencv.features2d.Features2d;
import org.opencv.core.KeyPoint;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.test.OpenCVTestCase;
import org.opencv.test.OpenCVTestRunner;
import org.opencv.features2d.Feature2D;
public class Features2dTest extends OpenCVTestCase {
public void testDrawKeypointsMatListOfKeyPointMat() {
fail("Not yet implemented");
}
public void testDrawKeypointsMatListOfKeyPointMatScalar() {
fail("Not yet implemented");
}
public void testDrawKeypointsMatListOfKeyPointMatScalarInt() {
fail("Not yet implemented");
}
public void testDrawMatches2MatListOfKeyPointMatListOfKeyPointListOfListOfDMatchMat() {
fail("Not yet implemented");
}
public void testDrawMatches2MatListOfKeyPointMatListOfKeyPointListOfListOfDMatchMatScalar() {
fail("Not yet implemented");
}
public void testDrawMatches2MatListOfKeyPointMatListOfKeyPointListOfListOfDMatchMatScalarScalar() {
fail("Not yet implemented");
}
public void testDrawMatches2MatListOfKeyPointMatListOfKeyPointListOfListOfDMatchMatScalarScalarListOfListOfByte() {
fail("Not yet implemented");
}
public void testDrawMatches2MatListOfKeyPointMatListOfKeyPointListOfListOfDMatchMatScalarScalarListOfListOfByteInt() {
fail("Not yet implemented");
}
public void testDrawMatchesMatListOfKeyPointMatListOfKeyPointListOfDMatchMat() {
fail("Not yet implemented");
}
public void testDrawMatchesMatListOfKeyPointMatListOfKeyPointListOfDMatchMatScalar() {
fail("Not yet implemented");
}
public void testDrawMatchesMatListOfKeyPointMatListOfKeyPointListOfDMatchMatScalarScalar() {
fail("Not yet implemented");
}
public void testDrawMatchesMatListOfKeyPointMatListOfKeyPointListOfDMatchMatScalarScalarListOfByte() {
fail("Not yet implemented");
}
public void testDrawMatchesMatListOfKeyPointMatListOfKeyPointListOfDMatchMatScalarScalarListOfByteInt() {
fail("Not yet implemented");
}
public void testPTOD()
{
String detectorCfg = "%YAML:1.0\n---\nhessianThreshold: 4000.\nextended: 0\nupright: 0\nOctaves: 4\nOctaveLayers: 3\n";
String extractorCfg = "%YAML:1.0\n---\nhessianThreshold: 4000.\nextended: 0\nupright: 0\nOctaves: 4\nOctaveLayers: 3\n";
Feature2D detector = createClassInstance(XFEATURES2D+"SURF", DEFAULT_FACTORY, null, null);
Feature2D extractor = createClassInstance(XFEATURES2D+"SURF", DEFAULT_FACTORY, null, null);
DescriptorMatcher matcher = DescriptorMatcher.create(DescriptorMatcher.BRUTEFORCE);
String detectorCfgFile = OpenCVTestRunner.getTempFileName("yml");
writeFile(detectorCfgFile, detectorCfg);
detector.read(detectorCfgFile);
String extractorCfgFile = OpenCVTestRunner.getTempFileName("yml");
writeFile(extractorCfgFile, extractorCfg);
extractor.read(extractorCfgFile);
Mat imgTrain = Imgcodecs.imread(OpenCVTestRunner.LENA_PATH, Imgcodecs.IMREAD_GRAYSCALE);
Mat imgQuery = imgTrain.submat(new Range(0, imgTrain.rows() - 100), Range.all());
MatOfKeyPoint trainKeypoints = new MatOfKeyPoint();
MatOfKeyPoint queryKeypoints = new MatOfKeyPoint();
detector.detect(imgTrain, trainKeypoints);
detector.detect(imgQuery, queryKeypoints);
// OpenCVTestRunner.Log("Keypoints found: " + trainKeypoints.size() +
// ":" + queryKeypoints.size());
Mat trainDescriptors = new Mat();
Mat queryDescriptors = new Mat();
extractor.compute(imgTrain, trainKeypoints, trainDescriptors);
extractor.compute(imgQuery, queryKeypoints, queryDescriptors);
MatOfDMatch matches = new MatOfDMatch();
matcher.add(Arrays.asList(trainDescriptors));
matcher.match(queryDescriptors, matches);
// OpenCVTestRunner.Log("Matches found: " + matches.size());
DMatch adm[] = matches.toArray();
List<Point> lp1 = new ArrayList<Point>(adm.length);
List<Point> lp2 = new ArrayList<Point>(adm.length);
KeyPoint tkp[] = trainKeypoints.toArray();
KeyPoint qkp[] = queryKeypoints.toArray();
for (int i = 0; i < adm.length; i++) {
DMatch dm = adm[i];
lp1.add(tkp[dm.trainIdx].pt);
lp2.add(qkp[dm.queryIdx].pt);
}
MatOfPoint2f points1 = new MatOfPoint2f(lp1.toArray(new Point[0]));
MatOfPoint2f points2 = new MatOfPoint2f(lp2.toArray(new Point[0]));
Mat hmg = Calib3d.findHomography(points1, points2, Calib3d.RANSAC, 3);
assertMatEqual(Mat.eye(3, 3, CvType.CV_64F), hmg, EPS);
Mat outimg = new Mat();
Features2d.drawMatches(imgQuery, queryKeypoints, imgTrain, trainKeypoints, matches, outimg);
String outputPath = OpenCVTestRunner.getOutputFileName("PTODresult.png");
Imgcodecs.imwrite(outputPath, outimg);
// OpenCVTestRunner.Log("Output image is saved to: " + outputPath);
}
public void testDrawKeypoints()
{
Mat outImg = Mat.ones(11, 11, CvType.CV_8U);
MatOfKeyPoint kps = new MatOfKeyPoint(new KeyPoint(5, 5, 1)); // x, y, size
Features2d.drawKeypoints(new Mat(), kps, outImg, new Scalar(255),
Features2d.DrawMatchesFlags_DRAW_OVER_OUTIMG);
Mat ref = new MatOfInt(new int[] {
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 15, 54, 15, 1, 1, 1, 1,
1, 1, 1, 76, 217, 217, 221, 81, 1, 1, 1,
1, 1, 100, 224, 111, 57, 115, 225, 101, 1, 1,
1, 44, 215, 100, 1, 1, 1, 101, 214, 44, 1,
1, 54, 212, 57, 1, 1, 1, 55, 212, 55, 1,
1, 40, 215, 104, 1, 1, 1, 105, 215, 40, 1,
1, 1, 102, 221, 111, 55, 115, 222, 103, 1, 1,
1, 1, 1, 76, 218, 217, 220, 81, 1, 1, 1,
1, 1, 1, 1, 15, 55, 15, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
}).reshape(1, 11);
ref.convertTo(ref, CvType.CV_8U);
assertMatEqual(ref, outImg);
}
}
@@ -0,0 +1,389 @@
package org.opencv.test.features2d;
import java.util.Arrays;
import java.util.List;
import org.opencv.core.CvException;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.MatOfDMatch;
import org.opencv.core.MatOfKeyPoint;
import org.opencv.core.Point;
import org.opencv.core.Scalar;
import org.opencv.core.DMatch;
import org.opencv.features2d.DescriptorMatcher;
import org.opencv.features2d.FlannBasedMatcher;
import org.opencv.core.KeyPoint;
import org.opencv.test.OpenCVTestCase;
import org.opencv.test.OpenCVTestRunner;
import org.opencv.imgproc.Imgproc;
import org.opencv.features2d.Feature2D;
public class FlannBasedDescriptorMatcherTest extends OpenCVTestCase {
static final String xmlParamsDefault = "<?xml version=\"1.0\"?>\n"
+ "<opencv_storage>\n"
+ "<format>3</format>\n"
+ "<indexParams>\n"
+ " <_>\n"
+ " <name>algorithm</name>\n"
+ " <type>9</type>\n" // FLANN_INDEX_TYPE_ALGORITHM
+ " <value>1</value></_>\n"
+ " <_>\n"
+ " <name>trees</name>\n"
+ " <type>4</type>\n"
+ " <value>4</value></_></indexParams>\n"
+ "<searchParams>\n"
+ " <_>\n"
+ " <name>checks</name>\n"
+ " <type>4</type>\n"
+ " <value>32</value></_>\n"
+ " <_>\n"
+ " <name>eps</name>\n"
+ " <type>5</type>\n"
+ " <value>0.</value></_>\n"
+ " <_>\n"
+ " <name>explore_all_trees</name>\n"
+ " <type>8</type>\n"
+ " <value>0</value></_>\n"
+ " <_>\n"
+ " <name>sorted</name>\n"
+ " <type>8</type>\n" // FLANN_INDEX_TYPE_BOOL
+ " <value>1</value></_></searchParams>\n"
+ "</opencv_storage>\n";
static final String ymlParamsDefault = "%YAML:1.0\n---\n"
+ "format: 3\n"
+ "indexParams:\n"
+ " -\n"
+ " name: algorithm\n"
+ " type: 9\n" // FLANN_INDEX_TYPE_ALGORITHM
+ " value: 1\n"
+ " -\n"
+ " name: trees\n"
+ " type: 4\n"
+ " value: 4\n"
+ "searchParams:\n"
+ " -\n"
+ " name: checks\n"
+ " type: 4\n"
+ " value: 32\n"
+ " -\n"
+ " name: eps\n"
+ " type: 5\n"
+ " value: 0.\n"
+ " -\n"
+ " name: explore_all_trees\n"
+ " type: 8\n"
+ " value: 0\n"
+ " -\n"
+ " name: sorted\n"
+ " type: 8\n" // FLANN_INDEX_TYPE_BOOL
+ " value: 1\n";
static final String ymlParamsModified = "%YAML:1.0\n---\n"
+ "format: 3\n"
+ "indexParams:\n"
+ " -\n"
+ " name: algorithm\n"
+ " type: 9\n" // FLANN_INDEX_TYPE_ALGORITHM
+ " value: 6\n"// this line is changed!
+ " -\n"
+ " name: trees\n"
+ " type: 4\n"
+ " value: 4\n"
+ "searchParams:\n"
+ " -\n"
+ " name: checks\n"
+ " type: 4\n"
+ " value: 32\n"
+ " -\n"
+ " name: eps\n"
+ " type: 5\n"
+ " value: 4.\n"// this line is changed!
+ " -\n"
+ " name: explore_all_trees\n"
+ " type: 8\n"
+ " value: 1\n"// this line is changed!
+ " -\n"
+ " name: sorted\n"
+ " type: 8\n" // FLANN_INDEX_TYPE_BOOL
+ " value: 1\n";
DescriptorMatcher matcher;
int matSize;
DMatch[] truth;
private Mat getMaskImg() {
return new Mat(5, 2, CvType.CV_8U, new Scalar(0)) {
{
put(0, 0, 1, 1, 1, 1);
}
};
}
private Mat getQueryDescriptors() {
Mat img = getQueryImg();
MatOfKeyPoint keypoints = new MatOfKeyPoint();
Mat descriptors = new Mat();
Feature2D detector = createClassInstance(XFEATURES2D+"SURF", DEFAULT_FACTORY, null, null);
Feature2D extractor = createClassInstance(XFEATURES2D+"SURF", DEFAULT_FACTORY, null, null);
setProperty(detector, "hessianThreshold", "double", 8000);
setProperty(detector, "nOctaves", "int", 3);
setProperty(detector, "upright", "boolean", false);
detector.detect(img, keypoints);
extractor.compute(img, keypoints, descriptors);
return descriptors;
}
private Mat getQueryImg() {
Mat cross = new Mat(matSize, matSize, CvType.CV_8U, new Scalar(255));
Imgproc.line(cross, new Point(30, matSize / 2), new Point(matSize - 31, matSize / 2), new Scalar(100), 3);
Imgproc.line(cross, new Point(matSize / 2, 30), new Point(matSize / 2, matSize - 31), new Scalar(100), 3);
return cross;
}
private Mat getTrainDescriptors() {
Mat img = getTrainImg();
MatOfKeyPoint keypoints = new MatOfKeyPoint(new KeyPoint(50, 50, 16, 0, 20000, 1, -1), new KeyPoint(42, 42, 16, 160, 10000, 1, -1));
Mat descriptors = new Mat();
Feature2D extractor = createClassInstance(XFEATURES2D+"SURF", DEFAULT_FACTORY, null, null);
extractor.compute(img, keypoints, descriptors);
return descriptors;
}
private Mat getTrainImg() {
Mat cross = new Mat(matSize, matSize, CvType.CV_8U, new Scalar(255));
Imgproc.line(cross, new Point(20, matSize / 2), new Point(matSize - 21, matSize / 2), new Scalar(100), 2);
Imgproc.line(cross, new Point(matSize / 2, 20), new Point(matSize / 2, matSize - 21), new Scalar(100), 2);
return cross;
}
protected void setUp() throws Exception {
super.setUp();
matcher = DescriptorMatcher.create(DescriptorMatcher.FLANNBASED);
matSize = 100;
truth = new DMatch[] {
new DMatch(0, 0, 0, 0.6159003f),
new DMatch(1, 1, 0, 0.9177120f),
new DMatch(2, 1, 0, 0.3112163f),
new DMatch(3, 1, 0, 0.2925075f),
new DMatch(4, 1, 0, 0.26520672f)
};
}
// https://github.com/opencv/opencv/issues/11268
public void testConstructor()
{
FlannBasedMatcher self_created_matcher = new FlannBasedMatcher();
Mat train = new Mat(1, 1, CvType.CV_8U, new Scalar(123));
self_created_matcher.add(Arrays.asList(train));
assertTrue(!self_created_matcher.empty());
}
public void testAdd() {
matcher.add(Arrays.asList(new Mat()));
assertFalse(matcher.empty());
}
public void testClear() {
matcher.add(Arrays.asList(new Mat()));
matcher.clear();
assertTrue(matcher.empty());
}
public void testClone() {
Mat train = new Mat(1, 1, CvType.CV_8U, new Scalar(123));
matcher.add(Arrays.asList(train));
try {
matcher.clone();
fail("Expected CvException (CV_StsNotImplemented)");
} catch (CvException cverr) {
// expected
}
}
public void testCloneBoolean() {
matcher.add(Arrays.asList(new Mat()));
DescriptorMatcher cloned = matcher.clone(true);
assertNotNull(cloned);
assertTrue(cloned.empty());
}
public void testCreate() {
assertNotNull(matcher);
}
public void testEmpty() {
assertTrue(matcher.empty());
}
public void testGetTrainDescriptors() {
Mat train = new Mat(1, 1, CvType.CV_8U, new Scalar(123));
Mat truth = train.clone();
matcher.add(Arrays.asList(train));
List<Mat> descriptors = matcher.getTrainDescriptors();
assertEquals(1, descriptors.size());
assertMatEqual(truth, descriptors.get(0));
}
public void testIsMaskSupported() {
assertFalse(matcher.isMaskSupported());
}
public void testKnnMatchMatListOfListOfDMatchInt() {
fail("Not yet implemented");
}
public void testKnnMatchMatListOfListOfDMatchIntListOfMat() {
fail("Not yet implemented");
}
public void testKnnMatchMatListOfListOfDMatchIntListOfMatBoolean() {
fail("Not yet implemented");
}
public void testKnnMatchMatMatListOfListOfDMatchInt() {
fail("Not yet implemented");
}
public void testKnnMatchMatMatListOfListOfDMatchIntMat() {
fail("Not yet implemented");
}
public void testKnnMatchMatMatListOfListOfDMatchIntMatBoolean() {
fail("Not yet implemented");
}
public void testMatchMatListOfDMatch() {
Mat train = getTrainDescriptors();
Mat query = getQueryDescriptors();
MatOfDMatch matches = new MatOfDMatch();
matcher.add(Arrays.asList(train));
matcher.train();
matcher.match(query, matches);
assertArrayDMatchEquals(truth, matches.toArray(), EPS);
}
public void testMatchMatListOfDMatchListOfMat() {
Mat train = getTrainDescriptors();
Mat query = getQueryDescriptors();
Mat mask = getMaskImg();
MatOfDMatch matches = new MatOfDMatch();
matcher.add(Arrays.asList(train));
matcher.train();
matcher.match(query, matches, Arrays.asList(mask));
assertArrayDMatchEquals(truth, matches.toArray(), EPS);
}
public void testMatchMatMatListOfDMatch() {
Mat train = getTrainDescriptors();
Mat query = getQueryDescriptors();
MatOfDMatch matches = new MatOfDMatch();
matcher.match(query, train, matches);
assertArrayDMatchEquals(truth, matches.toArray(), EPS);
// OpenCVTestRunner.Log(matches.toString());
// OpenCVTestRunner.Log(matches);
}
public void testMatchMatMatListOfDMatchMat() {
Mat train = getTrainDescriptors();
Mat query = getQueryDescriptors();
Mat mask = getMaskImg();
MatOfDMatch matches = new MatOfDMatch();
matcher.match(query, train, matches, mask);
assertListDMatchEquals(Arrays.asList(truth), matches.toList(), EPS);
}
public void testRadiusMatchMatListOfListOfDMatchFloat() {
fail("Not yet implemented");
}
public void testRadiusMatchMatListOfListOfDMatchFloatListOfMat() {
fail("Not yet implemented");
}
public void testRadiusMatchMatListOfListOfDMatchFloatListOfMatBoolean() {
fail("Not yet implemented");
}
public void testRadiusMatchMatMatListOfListOfDMatchFloat() {
fail("Not yet implemented");
}
public void testRadiusMatchMatMatListOfListOfDMatchFloatMat() {
fail("Not yet implemented");
}
public void testRadiusMatchMatMatListOfListOfDMatchFloatMatBoolean() {
fail("Not yet implemented");
}
public void testRead() {
String filenameR = OpenCVTestRunner.getTempFileName("yml");
String filenameW = OpenCVTestRunner.getTempFileName("yml");
writeFile(filenameR, ymlParamsModified);
matcher.read(filenameR);
matcher.write(filenameW);
assertEquals(ymlParamsModified, readFile(filenameW));
}
public void testTrain() {
Mat train = getTrainDescriptors();
matcher.add(Arrays.asList(train));
matcher.train();
}
public void testTrainNoData() {
try {
matcher.train();
fail("Expected CvException - FlannBasedMatcher::train should fail on empty train set");
} catch (CvException cverr) {
// expected
}
}
public void testWrite() {
String filename = OpenCVTestRunner.getTempFileName("xml");
matcher.write(filename);
assertEquals(xmlParamsDefault, readFile(filename));
}
public void testWriteYml() {
String filename = OpenCVTestRunner.getTempFileName("yml");
matcher.write(filename);
assertEquals(ymlParamsDefault, readFile(filename));
}
}
@@ -0,0 +1,67 @@
package org.opencv.test.features2d;
import org.opencv.test.OpenCVTestCase;
import org.opencv.test.OpenCVTestRunner;
import org.opencv.features2d.GFTTDetector;
public class GFTTFeatureDetectorTest extends OpenCVTestCase {
GFTTDetector detector;
@Override
protected void setUp() throws Exception {
super.setUp();
detector = GFTTDetector.create(); // default constructor have (1000, 0.01, 1, 3, 3, false, 0.04)
}
public void testCreate() {
assertNotNull(detector);
}
public void testDetectListOfMatListOfListOfKeyPoint() {
fail("Not yet implemented");
}
public void testDetectListOfMatListOfListOfKeyPointListOfMat() {
fail("Not yet implemented");
}
public void testDetectMatListOfKeyPoint() {
fail("Not yet implemented");
}
public void testDetectMatListOfKeyPointMat() {
fail("Not yet implemented");
}
public void testEmpty() {
fail("Not yet implemented");
}
public void testReadYml() {
String filename = OpenCVTestRunner.getTempFileName("yml");
writeFile(filename, "%YAML:1.0\n---\nname: \"Feature2D.GFTTDetector\"\nnfeatures: 500\nqualityLevel: 2.0000000000000000e-02\nminDistance: 2.\nblockSize: 4\ngradSize: 5\nuseHarrisDetector: 1\nk: 5.0000000000000000e-02\n");
detector.read(filename);
assertEquals(500, detector.getMaxFeatures());
assertEquals(0.02, detector.getQualityLevel());
assertEquals(2.0, detector.getMinDistance());
assertEquals(4, detector.getBlockSize());
assertEquals(5, detector.getGradientSize());
assertEquals(true, detector.getHarrisDetector());
assertEquals(0.05, detector.getK());
}
public void testWriteYml() {
String filename = OpenCVTestRunner.getTempFileName("yml");
detector.write(filename);
String truth = "%YAML:1.0\n---\nname: \"Feature2D.GFTTDetector\"\nnfeatures: 1000\nqualityLevel: 0.01\nminDistance: 1.\nblockSize: 3\ngradSize: 3\nuseHarrisDetector: 0\nk: 0.040000000000000001\n";
String actual = readFile(filename);
actual = actual.replaceAll("e([+-])0(\\d\\d)", "e$1$2"); // NOTE: workaround for different platforms double representation
assertEquals(truth, actual);
}
}
@@ -0,0 +1,39 @@
package org.opencv.test.features2d;
import org.opencv.test.OpenCVTestCase;
public class HARRISFeatureDetectorTest extends OpenCVTestCase {
public void testCreate() {
fail("Not yet implemented");
}
public void testDetectListOfMatListOfListOfKeyPoint() {
fail("Not yet implemented");
}
public void testDetectListOfMatListOfListOfKeyPointListOfMat() {
fail("Not yet implemented");
}
public void testDetectMatListOfKeyPoint() {
fail("Not yet implemented");
}
public void testDetectMatListOfKeyPointMat() {
fail("Not yet implemented");
}
public void testEmpty() {
fail("Not yet implemented");
}
public void testRead() {
fail("Not yet implemented");
}
public void testWrite() {
fail("Not yet implemented");
}
}
@@ -0,0 +1,66 @@
package org.opencv.test.features2d;
import org.opencv.test.OpenCVTestCase;
import org.opencv.test.OpenCVTestRunner;
import org.opencv.features2d.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.0\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,70 @@
package org.opencv.test.features2d;
import org.opencv.test.OpenCVTestCase;
import org.opencv.test.OpenCVTestRunner;
import org.opencv.features2d.MSER;
public class MSERFeatureDetectorTest extends OpenCVTestCase {
MSER detector;
@Override
protected void setUp() throws Exception {
super.setUp();
detector = MSER.create(); // default constructor have (5, 60, 14400, .25, .2, 200, 1.01, .003, 5)
}
public void testCreate() {
assertNotNull(detector);
}
public void testDetectListOfMatListOfListOfKeyPoint() {
fail("Not yet implemented");
}
public void testDetectListOfMatListOfListOfKeyPointListOfMat() {
fail("Not yet implemented");
}
public void testDetectMatListOfKeyPoint() {
fail("Not yet implemented");
}
public void testDetectMatListOfKeyPointMat() {
fail("Not yet implemented");
}
public void testEmpty() {
fail("Not yet implemented");
}
public void testReadYml() {
String filename = OpenCVTestRunner.getTempFileName("yml");
writeFile(filename, "%YAML:1.0\n---\nname: \"Feature2D.MSER\"\ndelta: 6\nminArea: 62\nmaxArea: 14402\nmaxVariation: .26\nminDiversity: .3\nmaxEvolution: 201\nareaThreshold: 1.02\nminMargin: 3.0e-3\nedgeBlurSize: 3\npass2Only: 1\n");
detector.read(filename);
assertEquals(6, detector.getDelta());
assertEquals(62, detector.getMinArea());
assertEquals(14402, detector.getMaxArea());
assertEquals(.26, detector.getMaxVariation());
assertEquals(.3, detector.getMinDiversity());
assertEquals(201, detector.getMaxEvolution());
assertEquals(1.02, detector.getAreaThreshold());
assertEquals(0.003, detector.getMinMargin());
assertEquals(3, detector.getEdgeBlurSize());
assertEquals(true, detector.getPass2Only());
}
public void testWriteYml() {
String filename = OpenCVTestRunner.getTempFileName("yml");
detector.write(filename);
String truth = "%YAML:1.0\n---\nname: \"Feature2D.MSER\"\ndelta: 5\nminArea: 60\nmaxArea: 14400\nmaxVariation: 0.25\nminDiversity: 0.20000000000000001\nmaxEvolution: 200\nareaThreshold: 1.01\nminMargin: 0.0030000000000000001\nedgeBlurSize: 5\npass2Only: 0\n";
String actual = readFile(filename);
actual = actual.replaceAll("e([+-])0(\\d\\d)", "e$1$2"); // NOTE: workaround for different platforms double representation
assertEquals(truth, actual);
}
}
@@ -0,0 +1,121 @@
package org.opencv.test.features2d;
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.features2d.ORB;
import org.opencv.test.OpenCVTestCase;
import org.opencv.test.OpenCVTestRunner;
import org.opencv.imgproc.Imgproc;
public class ORBDescriptorExtractorTest extends OpenCVTestCase {
ORB extractor;
int matSize;
public static void assertDescriptorsClose(Mat expected, Mat actual, int allowedDistance) {
double distance = Core.norm(expected, actual, Core.NORM_HAMMING);
assertTrue("expected:<" + allowedDistance + "> but was:<" + distance + ">", distance <= allowedDistance);
}
private Mat getTestImg() {
Mat cross = new Mat(matSize, matSize, CvType.CV_8U, new Scalar(255));
Imgproc.line(cross, new Point(20, matSize / 2), new Point(matSize - 21, matSize / 2), new Scalar(100), 2);
Imgproc.line(cross, new Point(matSize / 2, 20), new Point(matSize / 2, matSize - 21), new Scalar(100), 2);
return cross;
}
@Override
protected void setUp() throws Exception {
super.setUp();
extractor = ORB.create();
matSize = 100;
}
public void testComputeListOfMatListOfListOfKeyPointListOfMat() {
fail("Not yet implemented");
}
public void testComputeMatListOfKeyPointMat() {
KeyPoint point = new KeyPoint(55.775577545166016f, 44.224422454833984f, 16, 9.754629f, 8617.863f, 1, -1);
MatOfKeyPoint keypoints = new MatOfKeyPoint(point);
Mat img = getTestImg();
Mat descriptors = new Mat();
extractor.compute(img, keypoints, descriptors);
Mat truth = new Mat(1, 32, CvType.CV_8UC1) {
{
put(0, 0,
6, 74, 6, 129, 2, 130, 56, 0, 44, 132, 66, 165, 172, 6, 3, 72, 102, 61, 171, 214, 0, 144, 65, 232, 4, 32, 138, 131, 4, 21, 37, 217);
}
};
assertDescriptorsClose(truth, descriptors, 1);
}
public void testCreate() {
assertNotNull(extractor);
}
public void testDescriptorSize() {
assertEquals(32, extractor.descriptorSize());
}
public void testDescriptorType() {
assertEquals(CvType.CV_8U, extractor.descriptorType());
}
public void testEmpty() {
// assertFalse(extractor.empty());
fail("Not yet implemented"); // ORB does not override empty() method
}
public void testReadYml() {
KeyPoint point = new KeyPoint(55.775577545166016f, 44.224422454833984f, 16, 9.754629f, 8617.863f, 1, -1);
MatOfKeyPoint keypoints = new MatOfKeyPoint(point);
Mat img = getTestImg();
Mat descriptors = new Mat();
String filename = OpenCVTestRunner.getTempFileName("yml");
writeFile(filename, "%YAML:1.0\n---\nnfeatures: 500\nscaleFactor: 1.1\nnlevels: 3\nedgeThreshold: 31\nfirstLevel: 0\nwta_k: 2\nscoreType: 0\npatchSize: 31\nfastThreshold: 20\n");
extractor.read(filename);
assertEquals(500, extractor.getMaxFeatures());
assertEquals(1.1, extractor.getScaleFactor());
assertEquals(3, extractor.getNLevels());
assertEquals(31, extractor.getEdgeThreshold());
assertEquals(0, extractor.getFirstLevel());
assertEquals(2, extractor.getWTA_K());
assertEquals(0, extractor.getScoreType());
assertEquals(31, extractor.getPatchSize());
assertEquals(20, extractor.getFastThreshold());
extractor.compute(img, keypoints, descriptors);
Mat truth = new Mat(1, 32, CvType.CV_8UC1) {
{
put(0, 0,
6, 10, 22, 5, 2, 130, 56, 0, 44, 164, 66, 165, 140, 6, 1, 72, 38, 61, 163, 210, 0, 208, 1, 104, 4, 32, 74, 131, 0, 37, 37, 67);
}
};
assertDescriptorsClose(truth, descriptors, 1);
}
public void testWriteYml() {
String filename = OpenCVTestRunner.getTempFileName("yml");
extractor.write(filename);
String truth = "%YAML:1.0\n---\nname: \"Feature2D.ORB\"\nnfeatures: 500\nscaleFactor: 1.2000000476837158\nnlevels: 8\nedgeThreshold: 31\nfirstLevel: 0\nwta_k: 2\nscoreType: 0\npatchSize: 31\nfastThreshold: 20\n";
// String truth = "%YAML:1.0\n---\n";
String actual = readFile(filename);
actual = actual.replaceAll("e\\+000", "e+00"); // NOTE: workaround for different platforms double representation
assertEquals(truth, actual);
}
}
@@ -0,0 +1,78 @@
package org.opencv.test.features2d;
import org.junit.Assert;
import org.opencv.core.CvType;
import org.opencv.core.KeyPoint;
import org.opencv.core.Mat;
import org.opencv.core.MatOfKeyPoint;
import org.opencv.core.Scalar;
import org.opencv.features2d.Features2d;
import org.opencv.features2d.ORB;
import org.opencv.test.OpenCVTestCase;
public class ORBFeatureDetectorTest extends OpenCVTestCase {
public void testCreate() {
fail("Not yet implemented");
}
public void testDetectListOfMatListOfListOfKeyPoint() {
fail("Not yet implemented");
}
public void testDetectListOfMatListOfListOfKeyPointListOfMat() {
fail("Not yet implemented");
}
public void testDetectMatListOfKeyPoint() {
fail("Not yet implemented");
}
public void testDetectMatListOfKeyPointMat() {
fail("Not yet implemented");
}
public void testEmpty() {
fail("Not yet implemented");
}
public void testRead() {
fail("Not yet implemented");
}
public void testWrite() {
fail("Not yet implemented");
}
public void testDetectTwoPoints() {
Mat img = new Mat(256,256, CvType.CV_8UC3, new Scalar(0,0,0));
img.put(35, 40, 255,255, 255);
img.put(152, 98, 200,0, 0);
MatOfKeyPoint keypoints = new MatOfKeyPoint();
ORB orb = ORB.create();
Mat descriptors = new Mat();
orb.detectAndCompute(img, new Mat(), keypoints, descriptors);
KeyPoint[] keypointsArray = keypoints.toArray();
assertEquals(2, keypointsArray.length);
long x1 = Math.round(keypointsArray[0].pt.x);
long y1 = Math.round(keypointsArray[0].pt.y);
long x2 = Math.round(keypointsArray[1].pt.x);
long y2 = Math.round(keypointsArray[1].pt.y);
if (x2 > x1) {
assertEquals(40, x1);
assertEquals(35, y1);
assertEquals(98, x2);
assertEquals(152, y2);
} else {
assertEquals(40, x2);
assertEquals(35, y2);
assertEquals(98, x1);
assertEquals(152, y1);
}
}
}
@@ -0,0 +1,109 @@
package org.opencv.test.features2d;
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.features2d.SIFT;
import org.opencv.test.OpenCVTestCase;
import org.opencv.test.OpenCVTestRunner;
import org.opencv.imgproc.Imgproc;
import org.opencv.features2d.SIFT;
public class SIFTDescriptorExtractorTest extends OpenCVTestCase {
SIFT extractor;
KeyPoint keypoint;
int matSize;
Mat truth;
private Mat getTestImg() {
Mat cross = new Mat(matSize, matSize, CvType.CV_8U, new Scalar(255));
Imgproc.line(cross, new Point(20, matSize / 2), new Point(matSize - 21, matSize / 2), new Scalar(100), 2);
Imgproc.line(cross, new Point(matSize / 2, 20), new Point(matSize / 2, matSize - 21), new Scalar(100), 2);
return cross;
}
@Override
protected void setUp() throws Exception {
super.setUp();
extractor = SIFT.create();
keypoint = new KeyPoint(55.775577545166016f, 44.224422454833984f, 16, 9.754629f, 8617.863f, 1, -1);
matSize = 100;
truth = new Mat(1, 128, CvType.CV_32FC1) {
{
put(0, 0,
0, 0, 0, 1, 3, 0, 0, 0, 15, 23, 22, 20, 24, 2, 0, 0, 7, 8, 2, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 27, 16, 13, 2, 0, 0, 117,
86, 79, 68, 117, 42, 5, 5, 79, 60, 117, 25, 9, 2, 28, 19, 11, 13,
20, 2, 0, 0, 5, 8, 0, 0, 76, 58, 34, 31, 97, 16, 95, 49, 117, 92,
117, 112, 117, 76, 117, 54, 117, 25, 29, 22, 117, 117, 16, 11, 14,
1, 0, 0, 22, 26, 0, 0, 0, 0, 1, 4, 15, 2, 47, 8, 0, 0, 82, 56, 31,
17, 81, 12, 0, 0, 26, 23, 18, 23, 0, 0, 0, 0, 0, 0, 0, 0
);
}
};
}
public void testComputeListOfMatListOfListOfKeyPointListOfMat() {
fail("Not yet implemented");
}
public void testComputeMatListOfKeyPointMat() {
MatOfKeyPoint keypoints = new MatOfKeyPoint(keypoint);
Mat img = getTestImg();
Mat descriptors = new Mat();
extractor.compute(img, keypoints, descriptors);
assertMatEqual(truth, descriptors, EPS);
}
public void testCreate() {
assertNotNull(extractor);
}
public void testDescriptorSize() {
assertEquals(128, extractor.descriptorSize());
}
public void testDescriptorType() {
assertEquals(CvType.CV_32F, extractor.descriptorType());
}
public void testEmpty() {
// assertFalse(extractor.empty());
fail("Not yet implemented"); // SIFT does not override empty() method
}
public void testReadYml() {
String filename = OpenCVTestRunner.getTempFileName("yml");
writeFile(filename, "%YAML:1.0\n---\nname: \"Feature2D.SIFT\"\nnfeatures: 100\nnOctaveLayers: 4\ncontrastThreshold: 5.0000000000000001e-02\nedgeThreshold: 11\nsigma: 1.7\ndescriptorType: 5\n");
extractor.read(filename);
assertEquals(128, extractor.descriptorSize());
assertEquals(100, extractor.getNFeatures());
assertEquals(4, extractor.getNOctaveLayers());
assertEquals(0.05, extractor.getContrastThreshold());
assertEquals(11., extractor.getEdgeThreshold());
assertEquals(1.7, extractor.getSigma());
assertEquals(5, extractor.descriptorType());
}
public void testWriteYml() {
String filename = OpenCVTestRunner.getTempFileName("yml");
extractor.write(filename);
String truth = "%YAML:1.0\n---\nname: \"Feature2D.SIFT\"\nnfeatures: 0\nnOctaveLayers: 3\ncontrastThreshold: 0.040000000000000001\nedgeThreshold: 10.\nsigma: 1.6000000000000001\ndescriptorType: 5\n";
String actual = readFile(filename);
actual = actual.replaceAll("e([+-])0(\\d\\d)", "e$1$2"); // NOTE: workaround for different platforms double representation
assertEquals(truth, actual);
}
}
@@ -0,0 +1,39 @@
package org.opencv.test.features2d;
import org.opencv.test.OpenCVTestCase;
public class SIFTFeatureDetectorTest extends OpenCVTestCase {
public void testCreate() {
fail("Not yet implemented");
}
public void testDetectListOfMatListOfListOfKeyPoint() {
fail("Not yet implemented");
}
public void testDetectListOfMatListOfListOfKeyPointListOfMat() {
fail("Not yet implemented");
}
public void testDetectMatListOfKeyPoint() {
fail("Not yet implemented");
}
public void testDetectMatListOfKeyPointMat() {
fail("Not yet implemented");
}
public void testEmpty() {
fail("Not yet implemented");
}
public void testRead() {
fail("Not yet implemented");
}
public void testWrite() {
fail("Not yet implemented");
}
}
@@ -0,0 +1,138 @@
package org.opencv.test.features2d;
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.features2d.SimpleBlobDetector;
import org.opencv.features2d.SimpleBlobDetector_Params;
public class SIMPLEBLOBFeatureDetectorTest extends OpenCVTestCase {
SimpleBlobDetector detector;
int matSize;
KeyPoint[] truth;
private Mat getMaskImg() {
Mat mask = new Mat(matSize, matSize, CvType.CV_8U, new Scalar(255));
Mat right = mask.submat(0, matSize, matSize / 2, matSize);
right.setTo(new Scalar(0));
return mask;
}
private Mat getTestImg() {
int center = matSize / 2;
int offset = 40;
Mat img = new Mat(matSize, matSize, CvType.CV_8U, new Scalar(255));
Imgproc.circle(img, new Point(center - offset, center), 24, new Scalar(0), -1);
Imgproc.circle(img, new Point(center + offset, center), 20, new Scalar(50), -1);
Imgproc.circle(img, new Point(center, center - offset), 18, new Scalar(100), -1);
Imgproc.circle(img, new Point(center, center + offset), 14, new Scalar(150), -1);
Imgproc.circle(img, new Point(center, center), 10, new Scalar(200), -1);
return img;
}
@Override
protected void setUp() throws Exception {
super.setUp();
detector = SimpleBlobDetector.create();
matSize = 200;
truth = new KeyPoint[] {
new KeyPoint(140, 100, 41.036568f, -1, 0, 0, -1),
new KeyPoint(60, 100, 48.538486f, -1, 0, 0, -1),
new KeyPoint(100, 60, 36.769554f, -1, 0, 0, -1),
new KeyPoint(100, 140, 28.635643f, -1, 0, 0, -1),
new KeyPoint(100, 100, 20.880613f, -1, 0, 0, -1)
};
}
public void testCreate() {
assertNotNull(detector);
}
public void testDetectListOfMatListOfListOfKeyPoint() {
fail("Not yet implemented");
}
public void testDetectListOfMatListOfListOfKeyPointListOfMat() {
fail("Not yet implemented");
}
public void testDetectMatListOfKeyPoint() {
Mat img = getTestImg();
MatOfKeyPoint keypoints = new MatOfKeyPoint();
detector.detect(img, keypoints);
assertListKeyPointEquals(Arrays.asList(truth), keypoints.toList(), EPS);
}
public void testDetectMatListOfKeyPointMat() {
Mat img = getTestImg();
Mat mask = getMaskImg();
MatOfKeyPoint keypoints = new MatOfKeyPoint();
detector.detect(img, keypoints, mask);
assertListKeyPointEquals(Arrays.asList(truth[1]), keypoints.toList(), EPS);
}
public void testEmpty() {
// assertFalse(detector.empty());
fail("Not yet implemented");
}
public void testReadYml() {
Mat img = getTestImg();
MatOfKeyPoint keypoints1 = new MatOfKeyPoint();
detector.detect(img, keypoints1);
String filename = OpenCVTestRunner.getTempFileName("yml");
writeFile(filename, "%YAML:1.0\nthresholdStep: 10.0\nminThreshold: 50\nmaxThreshold: 220\nminRepeatability: 2\nminDistBetweenBlobs: 10.\nfilterByColor: 1\nblobColor: 0\nfilterByArea: 1\nminArea: 800\nmaxArea: 6000\nfilterByCircularity: 0\nminCircularity: 0.7\nmaxCircularity: 10.\nfilterByInertia: 1\nminInertiaRatio: 0.2\nmaxInertiaRatio: 11.\nfilterByConvexity: true\nminConvexity: 0.9\nmaxConvexity: 12.\n");
detector.read(filename);
SimpleBlobDetector_Params params = detector.getParams();
assertEquals(10.0f, params.get_thresholdStep());
assertEquals(50f, params.get_minThreshold());
assertEquals(220f, params.get_maxThreshold());
assertEquals(2, params.get_minRepeatability());
assertEquals(10.0f, params.get_minDistBetweenBlobs());
assertEquals(true, params.get_filterByColor());
assertEquals(0, params.get_blobColor());
assertEquals(true, params.get_filterByArea());
assertEquals(800f, params.get_minArea());
assertEquals(6000f, params.get_maxArea());
assertEquals(false, params.get_filterByCircularity());
assertEquals(0.7f, params.get_minCircularity());
assertEquals(10.0f, params.get_maxCircularity());
assertEquals(true, params.get_filterByInertia());
assertEquals(0.2f, params.get_minInertiaRatio());
assertEquals(11.0f, params.get_maxInertiaRatio());
assertEquals(true, params.get_filterByConvexity());
assertEquals(0.9f, params.get_minConvexity());
assertEquals(12.0f, params.get_maxConvexity());
MatOfKeyPoint keypoints2 = new MatOfKeyPoint();
detector.detect(img, keypoints2);
assertTrue(keypoints2.total() <= keypoints1.total());
}
public void testWrite() {
String filename = OpenCVTestRunner.getTempFileName("xml");
detector.write(filename);
String truth = "<?xml version=\"1.0\"?>\n<opencv_storage>\n<format>3</format>\n<thresholdStep>10.</thresholdStep>\n<minThreshold>50.</minThreshold>\n<maxThreshold>220.</maxThreshold>\n<minRepeatability>2</minRepeatability>\n<minDistBetweenBlobs>10.</minDistBetweenBlobs>\n<filterByColor>1</filterByColor>\n<blobColor>0</blobColor>\n<filterByArea>1</filterByArea>\n<minArea>25.</minArea>\n<maxArea>5000.</maxArea>\n<filterByCircularity>0</filterByCircularity>\n<minCircularity>0.80000001192092896</minCircularity>\n<maxCircularity>3.4028234663852886e+38</maxCircularity>\n<filterByInertia>1</filterByInertia>\n<minInertiaRatio>0.10000000149011612</minInertiaRatio>\n<maxInertiaRatio>3.4028234663852886e+38</maxInertiaRatio>\n<filterByConvexity>1</filterByConvexity>\n<minConvexity>0.94999998807907104</minConvexity>\n<maxConvexity>3.4028234663852886e+38</maxConvexity>\n<collectContours>0</collectContours>\n</opencv_storage>\n";
assertEquals(truth, readFile(filename));
}
}
+19
View File
@@ -0,0 +1,19 @@
{
"whitelist":
{
"Feature2D": ["detect", "compute", "detectAndCompute", "descriptorSize", "descriptorType", "defaultNorm", "empty", "getDefaultName"],
"BRISK": ["create", "getDefaultName"],
"ORB": ["create", "setMaxFeatures", "setScaleFactor", "setNLevels", "setEdgeThreshold", "setFastThreshold", "setFirstLevel", "setWTA_K", "setScoreType", "setPatchSize", "getFastThreshold", "getDefaultName"],
"MSER": ["create", "detectRegions", "setDelta", "getDelta", "setMinArea", "getMinArea", "setMaxArea", "getMaxArea", "setPass2Only", "getPass2Only", "getDefaultName"],
"FastFeatureDetector": ["create", "setThreshold", "getThreshold", "setNonmaxSuppression", "getNonmaxSuppression", "setType", "getType", "getDefaultName"],
"AgastFeatureDetector": ["create", "setThreshold", "getThreshold", "setNonmaxSuppression", "getNonmaxSuppression", "setType", "getType", "getDefaultName"],
"GFTTDetector": ["create", "setMaxFeatures", "getMaxFeatures", "setQualityLevel", "getQualityLevel", "setMinDistance", "getMinDistance", "setBlockSize", "getBlockSize", "setHarrisDetector", "getHarrisDetector", "setK", "getK", "getDefaultName"],
"SimpleBlobDetector": ["create", "setParams", "getParams", "getDefaultName"],
"SimpleBlobDetector_Params": [],
"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"],
"DescriptorMatcher": ["add", "clear", "empty", "isMaskSupported", "train", "match", "knnMatch", "radiusMatch", "clone", "create"],
"BFMatcher": ["isMaskSupported", "create"],
"": ["drawKeypoints", "drawMatches", "drawMatchesKnn"]
}
}
@@ -0,0 +1,29 @@
{
"ManualFuncs" : {
"SimpleBlobDetector": {
"setParams": { "declaration" : [""], "implementation" : [""] },
"getParams": { "declaration" : [""], "implementation" : [""] }
}
},
"enum_fix" : {
"FastFeatureDetector" : { "DetectorType": "FastDetectorType" },
"AgastFeatureDetector" : { "DetectorType": "AgastDetectorType" }
},
"func_arg_fix" : {
"Feature2D": {
"(void)compute:(NSArray<Mat*>*)images keypoints:(NSMutableArray<NSMutableArray<KeyPoint*>*>*)keypoints descriptors:(NSMutableArray<Mat*>*)descriptors" : { "compute" : {"name" : "compute2"} },
"(void)detect:(NSArray<Mat*>*)images keypoints:(NSMutableArray<NSMutableArray<KeyPoint*>*>*)keypoints masks:(NSArray<Mat*>*)masks" : { "detect" : {"name" : "detect2"} }
},
"DescriptorMatcher": {
"(DescriptorMatcher*)create:(NSString*)descriptorMatcherType" : { "create" : {"name" : "create2"} }
},
"FlannBasedMatcher": {
"FlannBasedMatcher": { "indexParams" : {"defval" : "cv::makePtr<cv::flann::KDTreeIndexParams>()"}, "searchParams" : {"defval" : "cv::makePtr<cv::flann::SearchParams>()"} }
},
"BFMatcher": {
"BFMatcher" : { "normType" : {"ctype" : "NormTypes"} },
"(BFMatcher*)create:(int)normType crossCheck:(BOOL)crossCheck" : { "create" : {"name" : "createBFMatcher"},
"normType" : {"ctype" : "NormTypes"} }
}
}
}
@@ -0,0 +1,9 @@
#ifdef HAVE_OPENCV_FEATURES2D
typedef SimpleBlobDetector::Params SimpleBlobDetector_Params;
typedef AKAZE::DescriptorType AKAZE_DescriptorType;
typedef AgastFeatureDetector::DetectorType AgastFeatureDetector_DetectorType;
typedef FastFeatureDetector::DetectorType FastFeatureDetector_DetectorType;
typedef DescriptorMatcher::MatcherType DescriptorMatcher_MatcherType;
typedef KAZE::DiffusivityType KAZE_DiffusivityType;
typedef ORB::ScoreType ORB_ScoreType;
#endif
@@ -0,0 +1,164 @@
#!/usr/bin/env python
'''
Feature homography
==================
Example of using features2d framework for interactive video homography matching.
ORB features and FLANN matcher are used. The actual tracking is implemented by
PlaneTracker class in plane_tracker.py
'''
# Python 2/3 compatibility
from __future__ import print_function
import numpy as np
import cv2 as cv
import sys
PY3 = sys.version_info[0] == 3
if PY3:
xrange = range
# local modules
from tst_scene_render import TestSceneRender
def intersectionRate(s1, s2):
x1, y1, x2, y2 = s1
s1 = np.array([[x1, y1], [x2,y1], [x2, y2], [x1, y2]])
area, _intersection = cv.intersectConvexConvex(s1, np.array(s2))
return 2 * area / (cv.contourArea(s1) + cv.contourArea(np.array(s2)))
from tests_common import NewOpenCVTests
class feature_homography_test(NewOpenCVTests):
render = None
tracker = None
framesCounter = 0
frame = None
def test_feature_homography(self):
self.render = TestSceneRender(self.get_sample('samples/data/graf1.png'),
self.get_sample('samples/data/box.png'), noise = 0.5, speed = 0.5)
self.frame = self.render.getNextFrame()
self.tracker = PlaneTracker()
self.tracker.clear()
self.tracker.add_target(self.frame, self.render.getCurrentRect())
while self.framesCounter < 100:
self.framesCounter += 1
tracked = self.tracker.track(self.frame)
if len(tracked) > 0:
tracked = tracked[0]
self.assertGreater(intersectionRate(self.render.getCurrentRect(), np.int32(tracked.quad)), 0.6)
else:
self.assertEqual(0, 1, 'Tracking error')
self.frame = self.render.getNextFrame()
# built-in modules
from collections import namedtuple
FLANN_INDEX_KDTREE = 1
FLANN_INDEX_LSH = 6
flann_params= dict(algorithm = FLANN_INDEX_LSH,
table_number = 6, # 12
key_size = 12, # 20
multi_probe_level = 1) #2
MIN_MATCH_COUNT = 10
'''
image - image to track
rect - tracked rectangle (x1, y1, x2, y2)
keypoints - keypoints detected inside rect
descrs - their descriptors
data - some user-provided data
'''
PlanarTarget = namedtuple('PlaneTarget', 'image, rect, keypoints, descrs, data')
'''
target - reference to PlanarTarget
p0 - matched points coords in target image
p1 - matched points coords in input frame
H - homography matrix from p0 to p1
quad - target boundary quad in input frame
'''
TrackedTarget = namedtuple('TrackedTarget', 'target, p0, p1, H, quad')
class PlaneTracker:
def __init__(self):
self.detector = cv.AKAZE_create(threshold = 0.003)
self.matcher = cv.FlannBasedMatcher(flann_params, {}) # bug : need to pass empty dict (#1329)
self.targets = []
self.frame_points = []
def add_target(self, image, rect, data=None):
'''Add a new tracking target.'''
x0, y0, x1, y1 = rect
raw_points, raw_descrs = self.detect_features(image)
points, descs = [], []
for kp, desc in zip(raw_points, raw_descrs):
x, y = kp.pt
if x0 <= x <= x1 and y0 <= y <= y1:
points.append(kp)
descs.append(desc)
descs = np.uint8(descs)
self.matcher.add([descs])
target = PlanarTarget(image = image, rect=rect, keypoints = points, descrs=descs, data=data)
self.targets.append(target)
def clear(self):
'''Remove all targets'''
self.targets = []
self.matcher.clear()
def track(self, frame):
'''Returns a list of detected TrackedTarget objects'''
self.frame_points, frame_descrs = self.detect_features(frame)
if len(self.frame_points) < MIN_MATCH_COUNT:
return []
matches = self.matcher.knnMatch(frame_descrs, k = 2)
matches = [m[0] for m in matches if len(m) == 2 and m[0].distance < m[1].distance * 0.75]
if len(matches) < MIN_MATCH_COUNT:
return []
matches_by_id = [[] for _ in xrange(len(self.targets))]
for m in matches:
matches_by_id[m.imgIdx].append(m)
tracked = []
for imgIdx, matches in enumerate(matches_by_id):
if len(matches) < MIN_MATCH_COUNT:
continue
target = self.targets[imgIdx]
p0 = [target.keypoints[m.trainIdx].pt for m in matches]
p1 = [self.frame_points[m.queryIdx].pt for m in matches]
p0, p1 = np.float32((p0, p1))
H, status = cv.findHomography(p0, p1, cv.RANSAC, 3.0)
status = status.ravel() != 0
if status.sum() < MIN_MATCH_COUNT:
continue
p0, p1 = p0[status], p1[status]
x0, y0, x1, y1 = target.rect
quad = np.float32([[x0, y0], [x1, y0], [x1, y1], [x0, y1]])
quad = cv.perspectiveTransform(quad.reshape(1, -1, 2), H).reshape(-1, 2)
track = TrackedTarget(target=target, p0=p0, p1=p1, H=H, quad=quad)
tracked.append(track)
tracked.sort(key = lambda t: len(t.p0), reverse=True)
return tracked
def detect_features(self, frame):
'''detect_features(self, frame) -> keypoints, descrs'''
keypoints, descrs = self.detector.detectAndCompute(frame, None)
if descrs is None: # detectAndCompute returns descs=None if no keypoints found
descrs = []
return keypoints, descrs
if __name__ == '__main__':
NewOpenCVTests.bootstrap()
@@ -0,0 +1,127 @@
// 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/ocl_perf.hpp"
#ifdef HAVE_OPENCL
namespace opencv_test {
namespace ocl {
typedef Size_MatType AKAZEFixture;
OCL_PERF_TEST_P(AKAZEFixture, detectAndCompute, ::testing::Combine(OCL_PERF_ENUM(OCL_SIZE_1, OCL_SIZE_2, OCL_SIZE_3), OCL_PERF_ENUM((MatType)CV_8UC1)))
{
const Size_MatType_t params = GetParam();
const Size srcSize = get<0>(params);
const int type = get<1>(params);
checkDeviceMaxMemoryAllocSize(srcSize, type);
UMat img(srcSize, type), mask;
declare.in(img, WARMUP_RNG);
Ptr<AKAZE> akaze = AKAZE::create(AKAZE::DESCRIPTOR_MLDB_UPRIGHT, 0, 3, 0.001f, 1, 1, KAZE::DIFF_PM_G2);
vector<KeyPoint> points;
UMat descriptors;
OCL_TEST_CYCLE() akaze->detectAndCompute(img, mask, points, descriptors, false);
EXPECT_GT(points.size(), 20u);
EXPECT_EQ((size_t)descriptors.rows, points.size());
SANITY_CHECK_NOTHING();
}
typedef Size_MatType AKAZEOclMldbUprightFixture;
OCL_PERF_TEST_P(AKAZEOclMldbUprightFixture, detectAndComputeMLDB,
::testing::Combine(
::testing::Values(
cv::Size(320, 240), cv::Size(640, 480), cv::Size(960, 540),
cv::Size(1280, 720), cv::Size(1920, 1080), cv::Size(2560, 1440), cv::Size(3840, 2160)
),
OCL_PERF_ENUM((MatType)CV_8UC1)
))
{
const Size_MatType_t params = GetParam();
const cv::Size srcSize = get<0>(params);
UMat img(srcSize, CV_8U);
declare.in(img, WARMUP_RNG);
Ptr<Feature2D> det = AKAZE::create(AKAZE::DESCRIPTOR_MLDB_UPRIGHT, 0, 3, 0.001f, 4, 4, KAZE::DIFF_PM_G2);
vector<KeyPoint> kps;
UMat descriptors;
OCL_TEST_CYCLE() det->detectAndCompute(img, noArray(), kps, descriptors, false);
EXPECT_GT(kps.size(), 0u);
SANITY_CHECK_NOTHING();
}
typedef tuple<std::string, int> AKAZEOclRealImagesParams;
typedef TestBaseWithParam<AKAZEOclRealImagesParams> AKAZEOclRealImagesFixture;
OCL_PERF_TEST_P(AKAZEOclRealImagesFixture, detectAndComputeMLDBRealImages,
::testing::Combine(
::testing::Values(
std::string("stitching/boat1.jpg"),
std::string("stitching/boat2.jpg"),
std::string("stitching/boat3.jpg"),
std::string("stitching/boat4.jpg"),
std::string("stitching/boat5.jpg"),
std::string("stitching/boat6.jpg")
),
::testing::Values(0)
))
{
const std::string imgName = get<0>(GetParam());
Mat img_mat = imread(getDataPath(imgName), IMREAD_GRAYSCALE);
if (img_mat.empty())
throw cvtest::SkipTestException("Image not found: " + imgName);
UMat img;
img_mat.copyTo(img);
declare.in(img);
Ptr<Feature2D> det = AKAZE::create(AKAZE::DESCRIPTOR_MLDB_UPRIGHT, 0, 3, 0.001f, 4, 4, KAZE::DIFF_PM_G2);
vector<KeyPoint> kps;
UMat descriptors;
OCL_TEST_CYCLE() det->detectAndCompute(img, noArray(), kps, descriptors, false);
EXPECT_GT(kps.size(), 0u);
SANITY_CHECK_NOTHING();
}
typedef Size_MatType AKAZEOclKazeUprightFixture;
OCL_PERF_TEST_P(AKAZEOclKazeUprightFixture, detectAndComputeKAZEUpright,
::testing::Combine(
::testing::Values(cv::Size(320, 240), cv::Size(640, 480), cv::Size(1280, 720)),
OCL_PERF_ENUM((MatType)CV_8UC1)
))
{
const Size_MatType_t params = GetParam();
const cv::Size srcSize = get<0>(params);
UMat img(srcSize, CV_8U);
declare.in(img, WARMUP_RNG);
Ptr<Feature2D> det = AKAZE::create(AKAZE::DESCRIPTOR_KAZE_UPRIGHT, 0, 3, 0.001f, 4, 4, KAZE::DIFF_PM_G2);
vector<KeyPoint> kps;
UMat descriptors;
OCL_TEST_CYCLE() det->detectAndCompute(img, noArray(), kps, descriptors, false);
EXPECT_GT(kps.size(), 0u);
SANITY_CHECK_NOTHING();
}
} // ocl
} // opencv_test
#endif // HAVE_OPENCL
@@ -0,0 +1,150 @@
/*M///////////////////////////////////////////////////////////////////////////////////////
//
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
// By downloading, copying, installing or using the software you agree to this license.
// If you do not agree to this license, do not download, install,
// copy or use the software.
//
//
// License Agreement
// For Open Source Computer Vision Library
//
// Copyright (C) 2010-2012, Multicoreware, Inc., all rights reserved.
// Copyright (C) 2010-2012, Advanced Micro Devices, Inc., all rights reserved.
// Third party copyrights are property of their respective owners.
//
// @Authors
// Fangfang Bai, fangfang@multicorewareinc.com
// Jin Ma, jin@multicorewareinc.com
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistribution's of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistribution's in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// * The name of the copyright holders may not be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// This software is provided by the copyright holders and contributors as is and
// any express or implied warranties, including, but not limited to, the implied
// warranties of merchantability and fitness for a particular purpose are disclaimed.
// In no event shall the Intel Corporation or contributors be liable for any direct,
// indirect, incidental, special, exemplary, or consequential damages
// (including, but not limited to, procurement of substitute goods or services;
// loss of use, data, or profits; or business interruption) however caused
// and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of
// the use of this software, even if advised of the possibility of such damage.
//
//M*/
#include "../perf_precomp.hpp"
#include "opencv2/ts/ocl_perf.hpp"
#ifdef HAVE_OPENCL
namespace opencv_test {
namespace ocl {
//////////////////// BruteForceMatch /////////////////
typedef Size_MatType BruteForceMatcherFixture;
OCL_PERF_TEST_P(BruteForceMatcherFixture, Match, ::testing::Combine(OCL_PERF_ENUM(OCL_SIZE_1, OCL_SIZE_2, OCL_SIZE_3), OCL_PERF_ENUM((MatType)CV_32FC1) ) )
{
const Size_MatType_t params = GetParam();
const Size srcSize = get<0>(params);
const int type = get<1>(params);
checkDeviceMaxMemoryAllocSize(srcSize, type);
vector<DMatch> matches;
UMat uquery(srcSize, type), utrain(srcSize, type);
declare.in(uquery, utrain, WARMUP_RNG);
BFMatcher matcher(NORM_L2);
OCL_TEST_CYCLE()
matcher.match(uquery, utrain, matches);
SANITY_CHECK_MATCHES(matches, 1e-3);
}
OCL_PERF_TEST_P(BruteForceMatcherFixture, KnnMatch, ::testing::Combine(OCL_PERF_ENUM(OCL_SIZE_1, OCL_SIZE_2, OCL_SIZE_3), OCL_PERF_ENUM((MatType)CV_32FC1) ) )
{
const Size_MatType_t params = GetParam();
const Size srcSize = get<0>(params);
const int type = get<1>(params);
checkDeviceMaxMemoryAllocSize(srcSize, type);
vector< vector<DMatch> > matches;
UMat uquery(srcSize, type), utrain(srcSize, type);
declare.in(uquery, utrain, WARMUP_RNG);
BFMatcher matcher(NORM_L2);
OCL_TEST_CYCLE()
matcher.knnMatch(uquery, utrain, matches, 2);
vector<DMatch> & matches0 = matches[0], & matches1 = matches[1];
SANITY_CHECK_MATCHES(matches0, 1e-3);
SANITY_CHECK_MATCHES(matches1, 1e-3);
}
OCL_PERF_TEST_P(BruteForceMatcherFixture, RadiusMatch, ::testing::Combine(OCL_PERF_ENUM(OCL_SIZE_1, OCL_SIZE_2, OCL_SIZE_3), OCL_PERF_ENUM((MatType)CV_32FC1) ) )
{
const Size_MatType_t params = GetParam();
const Size srcSize = get<0>(params);
const int type = get<1>(params);
checkDeviceMaxMemoryAllocSize(srcSize, type);
vector< vector<DMatch> > matches;
UMat uquery(srcSize, type), utrain(srcSize, type);
declare.in(uquery, utrain, WARMUP_RNG);
BFMatcher matcher(NORM_L2);
OCL_TEST_CYCLE()
matcher.radiusMatch(uquery, utrain, matches, 2.0f);
vector<DMatch> & matches0 = matches[0], & matches1 = matches[1];
SANITY_CHECK_MATCHES(matches0, 1e-3);
SANITY_CHECK_MATCHES(matches1, 1e-3);
}
OCL_PERF_TEST_P(BruteForceMatcherFixture, MatchCrossCheck, ::testing::Combine(OCL_PERF_ENUM(OCL_SIZE_1, OCL_SIZE_2, OCL_SIZE_3), OCL_PERF_ENUM((MatType)CV_32FC1) ) )
{
const Size_MatType_t params = GetParam();
const Size srcSize = get<0>(params);
const int type = get<1>(params);
checkDeviceMaxMemoryAllocSize(srcSize, type);
vector<DMatch> matches;
UMat uquery(srcSize, type), utrain(srcSize, type);
declare.in(uquery, utrain, WARMUP_RNG);
BFMatcher matcher(NORM_L2, true /*crossCheck*/);
OCL_TEST_CYCLE()
matcher.match(uquery, utrain, matches);
SANITY_CHECK_MATCHES(matches, 1e-3);
}
} // ocl
} // cvtest
#endif // HAVE_OPENCL
@@ -0,0 +1,81 @@
#include "../perf_precomp.hpp"
#include "opencv2/ts/ocl_perf.hpp"
#include "../perf_feature2d.hpp"
#ifdef HAVE_OPENCL
namespace opencv_test {
namespace ocl {
OCL_PERF_TEST_P(feature2d, detect, testing::Combine(Feature2DType::all(), TEST_IMAGES))
{
Ptr<Feature2D> detector = getFeature2D(get<0>(GetParam()));
std::string filename = getDataPath(get<1>(GetParam()));
Mat mimg = imread(filename, IMREAD_GRAYSCALE);
ASSERT_FALSE(mimg.empty());
ASSERT_TRUE(detector);
UMat img, mask;
mimg.copyTo(img);
declare.in(img);
vector<KeyPoint> points;
OCL_TEST_CYCLE() detector->detect(img, points, mask);
EXPECT_GT(points.size(), 20u);
SANITY_CHECK_NOTHING();
}
OCL_PERF_TEST_P(feature2d, extract, testing::Combine(testing::Values(DETECTORS_EXTRACTORS), TEST_IMAGES))
{
Ptr<Feature2D> detector = 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,167 @@
#include "perf_precomp.hpp"
namespace opencv_test
{
using namespace perf;
CV_ENUM(NormType, NORM_L1, NORM_L2, NORM_L2SQR, NORM_HAMMING, NORM_HAMMING2)
typedef tuple<NormType, MatType, bool> Norm_Destination_CrossCheck_t;
typedef perf::TestBaseWithParam<Norm_Destination_CrossCheck_t> Norm_Destination_CrossCheck;
typedef tuple<NormType, bool> Norm_CrossCheck_t;
typedef perf::TestBaseWithParam<Norm_CrossCheck_t> Norm_CrossCheck;
typedef tuple<MatType, bool> Source_CrossCheck_t;
typedef perf::TestBaseWithParam<Source_CrossCheck_t> Source_CrossCheck;
void generateData( Mat& query, Mat& train, const int sourceType );
PERF_TEST_P(Norm_Destination_CrossCheck, batchDistance_8U,
testing::Combine(testing::Values((int)NORM_L1, (int)NORM_L2SQR),
testing::Values(CV_32S, CV_32F),
testing::Bool()
)
)
{
NormType normType = get<0>(GetParam());
int destinationType = get<1>(GetParam());
bool isCrossCheck = get<2>(GetParam());
int knn = isCrossCheck ? 1 : 0;
Mat queryDescriptors;
Mat trainDescriptors;
Mat dist;
Mat ndix;
generateData(queryDescriptors, trainDescriptors, CV_8U);
TEST_CYCLE()
{
batchDistance(queryDescriptors, trainDescriptors, dist, destinationType, (isCrossCheck) ? ndix : noArray(),
normType, knn, Mat(), 0, isCrossCheck);
}
SANITY_CHECK(dist);
if (isCrossCheck) SANITY_CHECK(ndix);
}
PERF_TEST_P(Norm_CrossCheck, batchDistance_Dest_32S,
testing::Combine(testing::Values((int)NORM_HAMMING, (int)NORM_HAMMING2),
testing::Bool()
)
)
{
NormType normType = get<0>(GetParam());
bool isCrossCheck = get<1>(GetParam());
int knn = isCrossCheck ? 1 : 0;
Mat queryDescriptors;
Mat trainDescriptors;
Mat dist;
Mat ndix;
generateData(queryDescriptors, trainDescriptors, CV_8U);
TEST_CYCLE()
{
batchDistance(queryDescriptors, trainDescriptors, dist, CV_32S, (isCrossCheck) ? ndix : noArray(),
normType, knn, Mat(), 0, isCrossCheck);
}
SANITY_CHECK(dist);
if (isCrossCheck) SANITY_CHECK(ndix);
}
PERF_TEST_P(Source_CrossCheck, batchDistance_L2,
testing::Combine(testing::Values(CV_8U, CV_32F),
testing::Bool()
)
)
{
int sourceType = get<0>(GetParam());
bool isCrossCheck = get<1>(GetParam());
int knn = isCrossCheck ? 1 : 0;
Mat queryDescriptors;
Mat trainDescriptors;
Mat dist;
Mat ndix;
generateData(queryDescriptors, trainDescriptors, sourceType);
declare.time(50);
TEST_CYCLE()
{
batchDistance(queryDescriptors, trainDescriptors, dist, CV_32F, (isCrossCheck) ? ndix : noArray(),
NORM_L2, knn, Mat(), 0, isCrossCheck);
}
SANITY_CHECK(dist);
if (isCrossCheck) SANITY_CHECK(ndix);
}
PERF_TEST_P(Norm_CrossCheck, batchDistance_32F,
testing::Combine(testing::Values((int)NORM_L1, (int)NORM_L2SQR),
testing::Bool()
)
)
{
NormType normType = get<0>(GetParam());
bool isCrossCheck = get<1>(GetParam());
int knn = isCrossCheck ? 1 : 0;
Mat queryDescriptors;
Mat trainDescriptors;
Mat dist;
Mat ndix;
generateData(queryDescriptors, trainDescriptors, CV_32F);
declare.time(100);
TEST_CYCLE()
{
batchDistance(queryDescriptors, trainDescriptors, dist, CV_32F, (isCrossCheck) ? ndix : noArray(),
normType, knn, Mat(), 0, isCrossCheck);
}
SANITY_CHECK(dist, 1e-4);
if (isCrossCheck) SANITY_CHECK(ndix);
}
void generateData( Mat& query, Mat& train, const int sourceType )
{
const int dim = 500;
const int queryDescCount = 300; // must be even number because we split train data in some cases in two
const int countFactor = 4; // do not change it
RNG& rng = theRNG();
// Generate query descriptors randomly.
// Descriptor vector elements are integer values.
Mat buf( queryDescCount, dim, CV_32SC1 );
rng.fill( buf, RNG::UNIFORM, Scalar::all(0), Scalar(3) );
buf.convertTo( query, sourceType );
// Generate train descriptors as follows:
// copy each query descriptor to train set countFactor times
// and perturb some one element of the copied descriptors in
// in ascending order. General boundaries of the perturbation
// are (0.f, 1.f).
train.create( query.rows*countFactor, query.cols, sourceType );
float step = (sourceType == CV_8U ? 256.f : 1.f) / countFactor;
for( int qIdx = 0; qIdx < query.rows; qIdx++ )
{
Mat queryDescriptor = query.row(qIdx);
for( int c = 0; c < countFactor; c++ )
{
int tIdx = qIdx * countFactor + c;
Mat trainDescriptor = train.row(tIdx);
queryDescriptor.copyTo( trainDescriptor );
int elem = rng(dim);
float diff = rng.uniform( step*c, step*(c+1) );
trainDescriptor.col(elem) += diff;
}
}
}
} // namespace
+43
View File
@@ -0,0 +1,43 @@
#include "perf_precomp.hpp"
#include "perf_feature2d.hpp"
namespace opencv_test
{
using namespace perf;
typedef tuple<int, int, bool, string> Fast_Params_t;
typedef perf::TestBaseWithParam<Fast_Params_t> Fast_Params;
PERF_TEST_P(Fast_Params, detect,
testing::Combine(
testing::Values(20,30,100), // threshold
testing::Values(
// (int)FastFeatureDetector::TYPE_5_8,
// (int)FastFeatureDetector::TYPE_7_12,
(int)FastFeatureDetector::TYPE_9_16 // detector_type
),
testing::Bool(), // nonmaxSuppression
testing::Values("cv/inpaint/orig.png",
"cv/cameracalibration/chess9.png")
))
{
int threshold_p = get<0>(GetParam());
int type_p = get<1>(GetParam());
bool nonmaxSuppression_p = get<2>(GetParam());
string filename = getDataPath(get<3>(GetParam()));
Mat img = imread(filename, IMREAD_GRAYSCALE);
ASSERT_FALSE(img.empty()) << "Failed to load image: " << filename;
vector<KeyPoint> keypoints;
declare.in(img);
TEST_CYCLE()
{
FAST(img, keypoints, threshold_p, nonmaxSuppression_p, (FastFeatureDetector::DetectorType)type_p);
}
SANITY_CHECK_NOTHING();
}
} // namespace opencv_test
@@ -0,0 +1,72 @@
#include "perf_feature2d.hpp"
namespace opencv_test
{
using namespace perf;
PERF_TEST_P(feature2d, detect, testing::Combine(Feature2DType::all(), TEST_IMAGES))
{
Ptr<Feature2D> detector = getFeature2D(get<0>(GetParam()));
std::string filename = getDataPath(get<1>(GetParam()));
Mat img = imread(filename, IMREAD_GRAYSCALE);
ASSERT_FALSE(img.empty());
ASSERT_TRUE(detector);
declare.in(img);
Mat mask;
vector<KeyPoint> points;
TEST_CYCLE() detector->detect(img, points, mask);
EXPECT_GT(points.size(), 20u);
SANITY_CHECK_NOTHING();
}
PERF_TEST_P(feature2d, extract, testing::Combine(testing::Values(DETECTORS_EXTRACTORS), TEST_IMAGES))
{
Ptr<Feature2D> detector = 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,90 @@
#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 \
FAST_DEFAULT, FAST_20_TRUE_TYPE5_8, FAST_20_TRUE_TYPE7_12, FAST_20_TRUE_TYPE9_16, \
FAST_20_FALSE_TYPE5_8, FAST_20_FALSE_TYPE7_12, FAST_20_FALSE_TYPE9_16, \
\
AGAST_DEFAULT, AGAST_5_8, AGAST_7_12d, AGAST_7_12s, AGAST_OAST_9_16, \
\
MSER_DEFAULT
#define DETECTORS_EXTRACTORS \
ORB_DEFAULT, ORB_1500_13_1, \
AKAZE_DEFAULT, AKAZE_DESCRIPTOR_KAZE, \
BRISK_DEFAULT, \
KAZE_DEFAULT, \
SIFT_DEFAULT
#define CV_ENUM_EXPAND(name, ...) CV_ENUM(name, __VA_ARGS__)
enum Feature2DVals { DETECTORS_ONLY, DETECTORS_EXTRACTORS };
CV_ENUM_EXPAND(Feature2DType, DETECTORS_ONLY, DETECTORS_EXTRACTORS)
typedef tuple<Feature2DType, string> Feature2DType_String_t;
typedef perf::TestBaseWithParam<Feature2DType_String_t> feature2d;
#define TEST_IMAGES testing::Values(\
"cv/detectors_descriptors_evaluation/images_datasets/leuven/img1.png",\
"stitching/a3.png", \
"stitching/s2.jpg")
static inline Ptr<Feature2D> getFeature2D(Feature2DType type)
{
switch(type) {
case ORB_DEFAULT:
return ORB::create();
case ORB_1500_13_1:
return ORB::create(1500, 1.3f, 1);
case FAST_DEFAULT:
return FastFeatureDetector::create();
case FAST_20_TRUE_TYPE5_8:
return FastFeatureDetector::create(20, true, FastFeatureDetector::TYPE_5_8);
case FAST_20_TRUE_TYPE7_12:
return FastFeatureDetector::create(20, true, FastFeatureDetector::TYPE_7_12);
case FAST_20_TRUE_TYPE9_16:
return FastFeatureDetector::create(20, true, FastFeatureDetector::TYPE_9_16);
case FAST_20_FALSE_TYPE5_8:
return FastFeatureDetector::create(20, false, FastFeatureDetector::TYPE_5_8);
case FAST_20_FALSE_TYPE7_12:
return FastFeatureDetector::create(20, false, FastFeatureDetector::TYPE_7_12);
case FAST_20_FALSE_TYPE9_16:
return FastFeatureDetector::create(20, false, FastFeatureDetector::TYPE_9_16);
case 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();
case MSER_DEFAULT:
return MSER::create();
case SIFT_DEFAULT:
return SIFT::create();
default:
return Ptr<Feature2D>();
}
}
} // namespace
#endif // __OPENCV_PERF_FEATURE2D_HPP__
+7
View File
@@ -0,0 +1,7 @@
#include "perf_precomp.hpp"
#if defined(HAVE_HPX)
#include <hpx/hpx_main.hpp>
#endif
CV_PERF_TEST_MAIN(features2d)
+91
View File
@@ -0,0 +1,91 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// Copyright (C) 2026, Advanced Micro Devices, Inc., all rights reserved.
// Brute-force descriptor matching perf tests. These exercise the core distance
// kernels (hal::normL2Sqr_ / normL1_ / normHamming via cv::batchDistance) that
// back BFMatcher, which is how float descriptors (SIFT 128-d, SURF 64-d) and
// binary descriptors (ORB 32-byte) are matched.
#include "perf_precomp.hpp"
namespace opencv_test
{
using namespace perf;
// (descriptor dimension, query/train descriptor count)
typedef tuple<int, int> Dim_Count_t;
typedef TestBaseWithParam<Dim_Count_t> DescriptorMatcherFixture;
// Float descriptors matched with L2 (SIFT=128, SURF=64) — uses hal::normL2Sqr_.
PERF_TEST_P(DescriptorMatcherFixture, bfmatch_knn_L2_float,
testing::Combine(
testing::Values(64, 128), // SURF, SIFT descriptor sizes
testing::Values(512, 1000)
))
{
const int dim = get<0>(GetParam());
const int count = get<1>(GetParam());
Mat query(count, dim, CV_32F);
Mat train(count, dim, CV_32F);
declare.in(query, train, WARMUP_RNG);
declare.time(60);
BFMatcher matcher(NORM_L2, false);
std::vector<std::vector<DMatch> > matches;
TEST_CYCLE() matcher.knnMatch(query, train, matches, 2);
SANITY_CHECK_NOTHING();
}
// Float descriptors matched with L1 — uses hal::normL1_.
PERF_TEST_P(DescriptorMatcherFixture, bfmatch_knn_L1_float,
testing::Combine(
testing::Values(64, 128),
testing::Values(512, 1000)
))
{
const int dim = get<0>(GetParam());
const int count = get<1>(GetParam());
Mat query(count, dim, CV_32F);
Mat train(count, dim, CV_32F);
declare.in(query, train, WARMUP_RNG);
declare.time(60);
BFMatcher matcher(NORM_L1, false);
std::vector<std::vector<DMatch> > matches;
TEST_CYCLE() matcher.knnMatch(query, train, matches, 2);
SANITY_CHECK_NOTHING();
}
// Binary descriptors matched with Hamming (ORB/BRISK=32 bytes) — uses hal::normHamming.
PERF_TEST_P(DescriptorMatcherFixture, bfmatch_knn_Hamming_binary,
testing::Combine(
testing::Values(32, 64), // ORB (32), BRISK/FREAK (64) byte sizes
testing::Values(512, 1000)
))
{
const int bytes = get<0>(GetParam());
const int count = get<1>(GetParam());
Mat query(count, bytes, CV_8U);
Mat train(count, bytes, CV_8U);
declare.in(query, train, WARMUP_RNG);
declare.time(60);
BFMatcher matcher(NORM_HAMMING, false);
std::vector<std::vector<DMatch> > matches;
TEST_CYCLE() matcher.knnMatch(query, train, matches, 2);
SANITY_CHECK_NOTHING();
}
} // namespace
+7
View File
@@ -0,0 +1,7 @@
#ifndef __OPENCV_PERF_PRECOMP_HPP__
#define __OPENCV_PERF_PRECOMP_HPP__
#include "opencv2/ts.hpp"
#include "opencv2/features2d.hpp"
#endif
+360
View File
@@ -0,0 +1,360 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html.
//
// This file is based on code issued with the following license.
/*********************************************************************
* Software License Agreement (BSD License)
*
* Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
* Copyright (C) 2008-2013, Willow Garage Inc., all rights reserved.
* Copyright (C) 2013, Evgeny Toropov, all rights reserved.
* Third party copyrights are property of their respective owners.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* * The name of the copyright holders may not be used to endorse
* or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*********************************************************************/
/*
Guoshen Yu, Jean-Michel Morel, ASIFT: An Algorithm for Fully Affine
Invariant Comparison, Image Processing On Line, 1 (2011), pp. 11-38.
https://doi.org/10.5201/ipol.2011.my-asift
*/
#include "precomp.hpp"
#include <iostream>
namespace cv {
class AffineFeature_Impl CV_FINAL : public AffineFeature
{
public:
explicit AffineFeature_Impl(const Ptr<Feature2D>& backend,
int maxTilt, int minTilt, float tiltStep, float rotateStepBase);
int descriptorSize() const CV_OVERRIDE
{
return backend_->descriptorSize();
}
int descriptorType() const CV_OVERRIDE
{
return backend_->descriptorType();
}
int defaultNorm() const CV_OVERRIDE
{
return backend_->defaultNorm();
}
void detectAndCompute(InputArray image, InputArray mask, std::vector<KeyPoint>& keypoints,
OutputArray descriptors, bool useProvidedKeypoints=false) CV_OVERRIDE;
void setViewParams(const std::vector<float>& tilts, const std::vector<float>& rolls) CV_OVERRIDE;
void getViewParams(std::vector<float>& tilts, std::vector<float>& rolls) const CV_OVERRIDE;
protected:
void splitKeypointsByView(const std::vector<KeyPoint>& keypoints_,
std::vector< std::vector<KeyPoint> >& keypointsByView) const;
const Ptr<Feature2D> backend_;
int maxTilt_;
int minTilt_;
float tiltStep_;
float rotateStepBase_;
// Tilt factors.
std::vector<float> tilts_;
// Roll factors.
std::vector<float> rolls_;
private:
AffineFeature_Impl(const AffineFeature_Impl &); // copy disabled
AffineFeature_Impl& operator=(const AffineFeature_Impl &); // assign disabled
};
AffineFeature_Impl::AffineFeature_Impl(const Ptr<FeatureDetector>& backend,
int maxTilt, int minTilt, float tiltStep, float rotateStepBase)
: backend_(backend), maxTilt_(maxTilt), minTilt_(minTilt), tiltStep_(tiltStep), rotateStepBase_(rotateStepBase)
{
int i = minTilt_;
if( i == 0 )
{
tilts_.push_back(1);
rolls_.push_back(0);
i++;
}
float tilt = 1;
for( ; i <= maxTilt_; i++ )
{
tilt *= tiltStep_;
float rotateStep = rotateStepBase_ / tilt;
int rollN = cvFloor(180.0f / rotateStep);
if( rollN * rotateStep == 180.0f )
rollN--;
for( int j = 0; j <= rollN; j++ )
{
tilts_.push_back(tilt);
rolls_.push_back(rotateStep * j);
}
}
}
void AffineFeature_Impl::setViewParams(const std::vector<float>& tilts,
const std::vector<float>& rolls)
{
CV_Assert(tilts.size() == rolls.size());
tilts_ = tilts;
rolls_ = rolls;
}
void AffineFeature_Impl::getViewParams(std::vector<float>& tilts,
std::vector<float>& rolls) const
{
tilts = tilts_;
rolls = rolls_;
}
void AffineFeature_Impl::splitKeypointsByView(const std::vector<KeyPoint>& keypoints_,
std::vector< std::vector<KeyPoint> >& keypointsByView) const
{
for( size_t i = 0; i < keypoints_.size(); i++ )
{
const KeyPoint& kp = keypoints_[i];
CV_Assert( kp.class_id >= 0 && kp.class_id < (int)tilts_.size() );
keypointsByView[kp.class_id].push_back(kp);
}
}
class skewedDetectAndCompute : public ParallelLoopBody
{
public:
skewedDetectAndCompute(
const std::vector<float>& _tilts,
const std::vector<float>& _rolls,
std::vector< std::vector<KeyPoint> >& _keypointsCollection,
std::vector<Mat>& _descriptorCollection,
const Mat& _image,
const Mat& _mask,
const bool _do_keypoints,
const bool _do_descriptors,
const Ptr<Feature2D>& _backend)
: tilts(_tilts),
rolls(_rolls),
keypointsCollection(_keypointsCollection),
descriptorCollection(_descriptorCollection),
image(_image),
mask(_mask),
do_keypoints(_do_keypoints),
do_descriptors(_do_descriptors),
backend(_backend) {}
void operator()( const cv::Range& range ) const CV_OVERRIDE
{
CV_TRACE_FUNCTION();
const int begin = range.start;
const int end = range.end;
for( int a = begin; a < end; a++ )
{
Mat warpedImage, warpedMask;
Matx23f pose, invPose;
affineSkew(tilts[a], rolls[a], warpedImage, warpedMask, pose);
invertAffineTransform(pose, invPose);
std::vector<KeyPoint> wKeypoints;
Mat wDescriptors;
if( !do_keypoints )
{
const std::vector<KeyPoint>& keypointsInView = keypointsCollection[a];
if( keypointsInView.size() == 0 ) // when there are no keypoints in this affine view
continue;
std::vector<Point2f> pts_, pts;
KeyPoint::convert(keypointsInView, pts_);
transform(pts_, pts, pose);
wKeypoints.resize(keypointsInView.size());
for( size_t wi = 0; wi < wKeypoints.size(); wi++ )
{
wKeypoints[wi] = keypointsInView[wi];
wKeypoints[wi].pt = pts[wi];
}
}
backend->detectAndCompute(warpedImage, warpedMask, wKeypoints, wDescriptors, !do_keypoints);
if( do_keypoints )
{
// KeyPointsFilter::runByPixelsMask( wKeypoints, warpedMask );
if( wKeypoints.size() == 0 )
{
keypointsCollection[a].clear();
continue;
}
std::vector<Point2f> pts_, pts;
KeyPoint::convert(wKeypoints, pts_);
transform(pts_, pts, invPose);
keypointsCollection[a].resize(wKeypoints.size());
for( size_t wi = 0; wi < wKeypoints.size(); wi++ )
{
keypointsCollection[a][wi] = wKeypoints[wi];
keypointsCollection[a][wi].pt = pts[wi];
keypointsCollection[a][wi].class_id = a;
}
}
if( do_descriptors )
wDescriptors.copyTo(descriptorCollection[a]);
}
}
private:
void affineSkew(float tilt, float phi,
Mat& warpedImage, Mat& warpedMask, Matx23f& pose) const
{
int h = image.size().height;
int w = image.size().width;
Mat rotImage;
Mat mask0;
if( mask.empty() )
mask0 = Mat(h, w, CV_8UC1, 255);
else
mask0 = mask;
pose = Matx23f(1,0,0,
0,1,0);
if( phi == 0 )
image.copyTo(rotImage);
else
{
phi = phi * (float)CV_PI / 180;
float s = std::sin(phi);
float c = std::cos(phi);
Matx22f A(c, -s, s, c);
Matx<float, 4, 2> corners(0, 0, (float)w, 0, (float)w,(float)h, 0, (float)h);
Mat tf(corners * A.t());
Mat tcorners;
tf.convertTo(tcorners, CV_32S);
Rect rect = boundingRect(tcorners);
h = rect.height; w = rect.width;
pose = Matx23f(c, -s, -(float)rect.x,
s, c, -(float)rect.y);
warpAffine(image, rotImage, pose, Size(w, h), INTER_LINEAR, BORDER_REPLICATE);
}
if( tilt == 1 )
warpedImage = rotImage;
else
{
float s = 0.8f * sqrt(tilt * tilt - 1);
GaussianBlur(rotImage, rotImage, Size(0, 0), s, 0.01);
resize(rotImage, warpedImage, Size(0, 0), 1.0/tilt, 1.0, INTER_NEAREST);
pose(0, 0) /= tilt;
pose(0, 1) /= tilt;
pose(0, 2) /= tilt;
}
if( phi != 0 || tilt != 1 )
warpAffine(mask0, warpedMask, pose, warpedImage.size(), INTER_NEAREST);
else
warpedMask = mask0;
}
const std::vector<float>& tilts;
const std::vector<float>& rolls;
std::vector< std::vector<KeyPoint> >& keypointsCollection;
std::vector<Mat>& descriptorCollection;
const Mat& image;
const Mat& mask;
const bool do_keypoints;
const bool do_descriptors;
const Ptr<Feature2D>& backend;
};
void AffineFeature_Impl::detectAndCompute(InputArray _image, InputArray _mask,
std::vector<KeyPoint>& keypoints,
OutputArray _descriptors,
bool useProvidedKeypoints)
{
CV_TRACE_FUNCTION();
bool do_keypoints = !useProvidedKeypoints;
bool do_descriptors = _descriptors.needed();
Mat image = _image.getMat(), mask = _mask.getMat();
Mat descriptors;
if( (!do_keypoints && !do_descriptors) || _image.empty() )
return;
std::vector< std::vector<KeyPoint> > keypointsCollection(tilts_.size());
std::vector< Mat > descriptorCollection(tilts_.size());
if( do_keypoints )
keypoints.clear();
else
splitKeypointsByView(keypoints, keypointsCollection);
parallel_for_(Range(0, (int)tilts_.size()), skewedDetectAndCompute(tilts_, rolls_, keypointsCollection, descriptorCollection,
image, mask, do_keypoints, do_descriptors, backend_));
if( do_keypoints )
for( size_t i = 0; i < keypointsCollection.size(); i++ )
{
const std::vector<KeyPoint>& keys = keypointsCollection[i];
keypoints.insert(keypoints.end(), keys.begin(), keys.end());
}
if( do_descriptors )
{
_descriptors.create((int)keypoints.size(), backend_->descriptorSize(), backend_->descriptorType());
descriptors = _descriptors.getMat();
int iter = 0;
for( size_t i = 0; i < descriptorCollection.size(); i++ )
{
const Mat& descs = descriptorCollection[i];
if( descs.empty() )
continue;
Mat roi(descriptors, Rect(0, iter, descriptors.cols, descs.rows));
descs.copyTo(roi);
iter += descs.rows;
}
}
}
Ptr<AffineFeature> AffineFeature::create(const Ptr<Feature2D>& backend,
int maxTilt, int minTilt, float tiltStep, float rotateStepBase)
{
CV_Assert(minTilt < maxTilt);
CV_Assert(tiltStep > 0);
CV_Assert(rotateStepBase > 0);
return makePtr<AffineFeature_Impl>(backend, maxTilt, minTilt, tiltStep, rotateStepBase);
}
String AffineFeature::getDefaultName() const
{
return (Feature2D::getDefaultName() + ".AffineFeature");
}
} // namespace
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+69
View File
@@ -0,0 +1,69 @@
/* 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
{
#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
+310
View File
@@ -0,0 +1,310 @@
/*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
{
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(DescriptorType dtype) CV_OVERRIDE{ descriptor = dtype; }
DescriptorType 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(KAZE::DiffusivityType diff_) CV_OVERRIDE{ diffusivity = diff_; }
KAZE::DiffusivityType 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);
UMatPyramid uPyr;
#ifdef HAVE_OPENCL
// NOTE: The AKAZE kernels use excessive private memory 336 bytes per work-item:
// float histogram[36] and float values[48] arrays. On 32-bit Windows, this
// exceeds the driver's private memory limits, causing SEH exception (0xc0000005).
#if defined(_M_IX86) || defined(__i386__)
bool use_opencl = false;
#else
bool use_opencl = cv::ocl::useOpenCL() && image.isUMat() && descriptor == DESCRIPTOR_MLDB_UPRIGHT
&& !ocl::Device::getDefault().hostUnifiedMemory();
#endif
if (use_opencl)
{
impl.GetEvolutionPyramid(uPyr); // Get initialized pyramid
impl.Create_Nonlinear_Scale_Space_UMat(image, uPyr);
if (!useProvidedKeypoints)
{
impl.Feature_Detection_UMat(uPyr, keypoints);
}
}
else
#endif
{
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())
{
#ifdef HAVE_OPENCL
if (use_opencl)
{
impl.Compute_Descriptors_UMat(keypoints, descriptors, uPyr);
}
else
#endif
{
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"];
}
DescriptorType descriptor;
int descriptor_channels;
int descriptor_size;
float threshold;
int octaves;
int sublevels;
KAZE::DiffusivityType 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");
}
}
+216
View File
@@ -0,0 +1,216 @@
/*M///////////////////////////////////////////////////////////////////////////////////////
//
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
// By downloading, copying, installing or using the software you agree to this license.
// If you do not agree to this license, do not download, install,
// copy or use the software.
//
//
// 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
{
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;
}
}
+503
View File
@@ -0,0 +1,503 @@
/*M///////////////////////////////////////////////////////////////////////////////////////
//
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
// By downloading, copying, installing or using the software you agree to this license.
// If you do not agree to this license, do not download, install,
// copy or use the software.
//
//
// License Agreement
// For Open Source Computer Vision Library
//
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistribution's of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistribution's in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// * The name of the copyright holders may not be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// This software is provided by the copyright holders and contributors "as is" and
// any express or implied warranties, including, but not limited to, the implied
// warranties of merchantability and fitness for a particular purpose are disclaimed.
// In no event shall the Intel Corporation or contributors be liable for any direct,
// indirect, incidental, special, exemplary, or consequential damages
// (including, but not limited to, procurement of substitute goods or services;
// loss of use, data, or profits; or business interruption) however caused
// and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of
// the use of this software, even if advised of the possibility of such damage.
//
//M*/
#include "precomp.hpp"
#include <iterator>
#include <limits>
#include <opencv2/core/utils/logger.hpp>
// Requires CMake flag: DEBUG_opencv_features2d=ON
//#define DEBUG_BLOB_DETECTOR
#ifdef DEBUG_BLOB_DETECTOR
#include "opencv2/highgui.hpp"
#endif
namespace cv
{
// TODO: To be removed in 5.x branch
const std::vector<std::vector<cv::Point> >& SimpleBlobDetector::getBlobContours() const
{
CV_Error(Error::StsNotImplemented, "Method SimpleBlobDetector::getBlobContours() is not implemented");
}
class CV_EXPORTS_W SimpleBlobDetectorImpl : public SimpleBlobDetector
{
public:
explicit SimpleBlobDetectorImpl(const SimpleBlobDetector::Params &parameters = SimpleBlobDetector::Params());
virtual void read( const FileNode& fn ) CV_OVERRIDE;
virtual void write( FileStorage& fs ) const CV_OVERRIDE;
void setParams(const SimpleBlobDetector::Params& _params ) CV_OVERRIDE {
SimpleBlobDetectorImpl::validateParameters(_params);
params = _params;
}
SimpleBlobDetector::Params getParams() const CV_OVERRIDE { return params; }
static void validateParameters(const SimpleBlobDetector::Params& p)
{
if (p.thresholdStep <= 0)
CV_Error(Error::StsBadArg, "thresholdStep>0");
if (p.minThreshold > p.maxThreshold || p.minThreshold < 0)
CV_Error(Error::StsBadArg, "0<=minThreshold<=maxThreshold");
if (p.minDistBetweenBlobs <=0 )
CV_Error(Error::StsBadArg, "minDistBetweenBlobs>0");
if (p.minArea > p.maxArea || p.minArea <=0)
CV_Error(Error::StsBadArg, "0<minArea<=maxArea");
if (p.minCircularity > p.maxCircularity || p.minCircularity <= 0)
CV_Error(Error::StsBadArg, "0<minCircularity<=maxCircularity");
if (p.minInertiaRatio > p.maxInertiaRatio || p.minInertiaRatio <= 0)
CV_Error(Error::StsBadArg, "0<minInertiaRatio<=maxInertiaRatio");
if (p.minConvexity > p.maxConvexity || p.minConvexity <= 0)
CV_Error(Error::StsBadArg, "0<minConvexity<=maxConvexity");
}
protected:
struct CV_EXPORTS Center
{
Point2d location;
double radius;
double confidence;
};
virtual void detect( InputArray image, std::vector<KeyPoint>& keypoints, InputArray mask=noArray() ) CV_OVERRIDE;
virtual void findBlobs(InputArray image, InputArray binaryImage, std::vector<Center> &centers,
std::vector<std::vector<Point> > &contours, std::vector<Moments> &moments) const;
virtual const std::vector<std::vector<Point> >& getBlobContours() const CV_OVERRIDE;
Params params;
std::vector<std::vector<Point> > blobContours;
};
/*
* SimpleBlobDetector
*/
SimpleBlobDetector::Params::Params()
{
thresholdStep = 10;
minThreshold = 50;
maxThreshold = 220;
minRepeatability = 2;
minDistBetweenBlobs = 10;
filterByColor = true;
blobColor = 0;
filterByArea = true;
minArea = 25;
maxArea = 5000;
filterByCircularity = false;
minCircularity = 0.8f;
maxCircularity = std::numeric_limits<float>::max();
filterByInertia = true;
//minInertiaRatio = 0.6;
minInertiaRatio = 0.1f;
maxInertiaRatio = std::numeric_limits<float>::max();
filterByConvexity = true;
//minConvexity = 0.8;
minConvexity = 0.95f;
maxConvexity = std::numeric_limits<float>::max();
collectContours = false;
}
void SimpleBlobDetector::Params::read(const cv::FileNode& fn )
{
thresholdStep = fn["thresholdStep"];
minThreshold = fn["minThreshold"];
maxThreshold = fn["maxThreshold"];
minRepeatability = (size_t)(int)fn["minRepeatability"];
minDistBetweenBlobs = fn["minDistBetweenBlobs"];
filterByColor = (int)fn["filterByColor"] != 0 ? true : false;
blobColor = (uchar)(int)fn["blobColor"];
filterByArea = (int)fn["filterByArea"] != 0 ? true : false;
minArea = fn["minArea"];
maxArea = fn["maxArea"];
filterByCircularity = (int)fn["filterByCircularity"] != 0 ? true : false;
minCircularity = fn["minCircularity"];
maxCircularity = fn["maxCircularity"];
filterByInertia = (int)fn["filterByInertia"] != 0 ? true : false;
minInertiaRatio = fn["minInertiaRatio"];
maxInertiaRatio = fn["maxInertiaRatio"];
filterByConvexity = (int)fn["filterByConvexity"] != 0 ? true : false;
minConvexity = fn["minConvexity"];
maxConvexity = fn["maxConvexity"];
collectContours = (int)fn["collectContours"] != 0 ? true : false;
}
void SimpleBlobDetector::Params::write(cv::FileStorage& fs) const
{
fs << "thresholdStep" << thresholdStep;
fs << "minThreshold" << minThreshold;
fs << "maxThreshold" << maxThreshold;
fs << "minRepeatability" << (int)minRepeatability;
fs << "minDistBetweenBlobs" << minDistBetweenBlobs;
fs << "filterByColor" << (int)filterByColor;
fs << "blobColor" << (int)blobColor;
fs << "filterByArea" << (int)filterByArea;
fs << "minArea" << minArea;
fs << "maxArea" << maxArea;
fs << "filterByCircularity" << (int)filterByCircularity;
fs << "minCircularity" << minCircularity;
fs << "maxCircularity" << maxCircularity;
fs << "filterByInertia" << (int)filterByInertia;
fs << "minInertiaRatio" << minInertiaRatio;
fs << "maxInertiaRatio" << maxInertiaRatio;
fs << "filterByConvexity" << (int)filterByConvexity;
fs << "minConvexity" << minConvexity;
fs << "maxConvexity" << maxConvexity;
fs << "collectContours" << (int)collectContours;
}
SimpleBlobDetectorImpl::SimpleBlobDetectorImpl(const SimpleBlobDetector::Params &parameters) :
params(parameters)
{
}
void SimpleBlobDetectorImpl::read( const cv::FileNode& fn )
{
SimpleBlobDetector::Params rp;
rp.read(fn);
SimpleBlobDetectorImpl::validateParameters(rp);
params = rp;
}
void SimpleBlobDetectorImpl::write( cv::FileStorage& fs ) const
{
writeFormat(fs);
params.write(fs);
}
void SimpleBlobDetectorImpl::findBlobs(InputArray _image, InputArray _binaryImage, std::vector<Center> &centers,
std::vector<std::vector<Point> > &contoursOut, std::vector<Moments> &momentss) const
{
CV_INSTRUMENT_REGION();
Mat image = _image.getMat(), binaryImage = _binaryImage.getMat();
CV_UNUSED(image);
centers.clear();
contoursOut.clear();
momentss.clear();
std::vector < std::vector<Point> > contours;
findContours(binaryImage, contours, RETR_LIST, CHAIN_APPROX_NONE);
#ifdef DEBUG_BLOB_DETECTOR
Mat keypointsImage;
cvtColor(binaryImage, keypointsImage, COLOR_GRAY2RGB);
Mat contoursImage;
cvtColor(binaryImage, contoursImage, COLOR_GRAY2RGB);
drawContours( contoursImage, contours, -1, Scalar(0,255,0) );
imshow("contours", contoursImage );
#endif
for (size_t contourIdx = 0; contourIdx < contours.size(); contourIdx++)
{
Center center;
center.confidence = 1;
Moments moms = moments(contours[contourIdx]);
if (params.filterByArea)
{
double area = moms.m00;
if (area < params.minArea || area >= params.maxArea)
continue;
}
if (params.filterByCircularity)
{
double area = moms.m00;
double perimeter = arcLength(contours[contourIdx], true);
double ratio = 4 * CV_PI * area / (perimeter * perimeter);
if (ratio < params.minCircularity || ratio >= params.maxCircularity)
continue;
}
if (params.filterByInertia)
{
double denominator = std::sqrt(std::pow(2 * moms.mu11, 2) + std::pow(moms.mu20 - moms.mu02, 2));
const double eps = 1e-2;
double ratio;
if (denominator > eps)
{
double cosmin = (moms.mu20 - moms.mu02) / denominator;
double sinmin = 2 * moms.mu11 / denominator;
double cosmax = -cosmin;
double sinmax = -sinmin;
double imin = 0.5 * (moms.mu20 + moms.mu02) - 0.5 * (moms.mu20 - moms.mu02) * cosmin - moms.mu11 * sinmin;
double imax = 0.5 * (moms.mu20 + moms.mu02) - 0.5 * (moms.mu20 - moms.mu02) * cosmax - moms.mu11 * sinmax;
ratio = imin / imax;
}
else
{
ratio = 1;
}
if (ratio < params.minInertiaRatio || ratio >= params.maxInertiaRatio)
continue;
center.confidence = ratio * ratio;
}
if (params.filterByConvexity)
{
std::vector < Point > hull;
convexHull(contours[contourIdx], hull);
double area = moms.m00;
double hullArea = contourArea(hull);
if (fabs(hullArea) < DBL_EPSILON)
continue;
double ratio = area / hullArea;
if (ratio < params.minConvexity || ratio >= params.maxConvexity)
continue;
}
if(moms.m00 == 0.0)
continue;
center.location = Point2d(moms.m10 / moms.m00, moms.m01 / moms.m00);
if (params.filterByColor)
{
if (binaryImage.at<uchar> (cvRound(center.location.y), cvRound(center.location.x)) != params.blobColor)
continue;
}
//compute blob radius
{
const std::vector<cv::Point>& contour = contours[contourIdx];
const size_t contourSize = contour.size();
AutoBuffer<double> dists(contourSize);
for (size_t pointIdx = 0; pointIdx < contourSize; pointIdx++)
{
const Point2d& pt = contour[pointIdx];
dists[pointIdx] = norm(center.location - pt);
}
std::sort(dists.begin(), dists.end());
center.radius = (dists[(dists.size() - 1) / 2] + dists[dists.size() / 2]) / 2.;
}
centers.push_back(center);
if (params.collectContours)
{
contoursOut.push_back(contours[contourIdx]);
momentss.push_back(moms);
}
#ifdef DEBUG_BLOB_DETECTOR
circle( keypointsImage, center.location, 1, Scalar(0,0,255), 1 );
#endif
}
#ifdef DEBUG_BLOB_DETECTOR
imshow("bk", keypointsImage );
waitKey();
#endif
}
void SimpleBlobDetectorImpl::detect(InputArray image, std::vector<cv::KeyPoint>& keypoints, InputArray mask)
{
CV_INSTRUMENT_REGION();
keypoints.clear();
blobContours.clear();
CV_Assert(params.minRepeatability != 0);
Mat grayscaleImage;
if (image.channels() == 3 || image.channels() == 4)
cvtColor(image, grayscaleImage, COLOR_BGR2GRAY);
else
grayscaleImage = image.getMat();
if (grayscaleImage.type() != CV_8UC1) {
CV_Error(Error::StsUnsupportedFormat, "Blob detector only supports 8-bit images!");
}
CV_CheckGT(params.thresholdStep, 0.0f, "");
if (params.minThreshold + params.thresholdStep >= params.maxThreshold)
{
// https://github.com/opencv/opencv/issues/6667
CV_LOG_ONCE_INFO(NULL, "SimpleBlobDetector: params.minDistBetweenBlobs is ignored for case with single threshold");
#if 0 // OpenCV 5.0
CV_CheckEQ(params.minRepeatability, 1u, "Incompatible parameters for case with single threshold");
#else
if (params.minRepeatability != 1)
CV_LOG_WARNING(NULL, "SimpleBlobDetector: params.minRepeatability=" << params.minRepeatability << " is incompatible for case with single threshold. Empty result is expected.");
#endif
}
std::vector < std::vector<Center> > centers;
std::vector<Moments> momentss;
for (double thresh = params.minThreshold; thresh < params.maxThreshold; thresh += params.thresholdStep)
{
Mat binarizedImage;
threshold(grayscaleImage, binarizedImage, thresh, 255, THRESH_BINARY);
std::vector < Center > curCenters;
std::vector<std::vector<Point> > curContours;
std::vector<Moments> curMomentss;
findBlobs(grayscaleImage, binarizedImage, curCenters, curContours, curMomentss);
std::vector < std::vector<Center> > newCenters;
std::vector<std::vector<Point> > newContours;
std::vector<Moments> newMomentss;
for (size_t i = 0; i < curCenters.size(); i++)
{
bool isNew = true;
for (size_t j = 0; j < centers.size(); j++)
{
double dist = norm(centers[j][ centers[j].size() / 2 ].location - curCenters[i].location);
isNew = dist >= params.minDistBetweenBlobs && dist >= centers[j][ centers[j].size() / 2 ].radius && dist >= curCenters[i].radius;
if (!isNew)
{
centers[j].push_back(curCenters[i]);
size_t k = centers[j].size() - 1;
while( k > 0 && curCenters[i].radius < centers[j][k-1].radius )
{
centers[j][k] = centers[j][k-1];
k--;
}
if (params.collectContours)
{
if (curCenters[i].confidence > centers[j][k].confidence
|| (curCenters[i].confidence == centers[j][k].confidence && curMomentss[i].m00 > momentss[j].m00))
{
blobContours[j] = curContours[i];
momentss[j] = curMomentss[i];
}
}
centers[j][k] = curCenters[i];
break;
}
}
if (isNew)
{
newCenters.push_back(std::vector<Center> (1, curCenters[i]));
if (params.collectContours)
{
newContours.push_back(curContours[i]);
newMomentss.push_back(curMomentss[i]);
}
}
}
std::copy(newCenters.begin(), newCenters.end(), std::back_inserter(centers));
if (params.collectContours)
{
std::copy(newContours.begin(), newContours.end(), std::back_inserter(blobContours));
std::copy(newMomentss.begin(), newMomentss.end(), std::back_inserter(momentss));
}
}
for (size_t i = 0; i < centers.size(); i++)
{
if (centers[i].size() < params.minRepeatability)
continue;
Point2d sumPoint(0, 0);
double normalizer = 0;
for (size_t j = 0; j < centers[i].size(); j++)
{
sumPoint += centers[i][j].confidence * centers[i][j].location;
normalizer += centers[i][j].confidence;
}
sumPoint *= (1. / normalizer);
KeyPoint kpt(sumPoint, (float)(centers[i][centers[i].size() / 2].radius) * 2.0f);
keypoints.push_back(kpt);
}
if (!mask.empty())
{
if (params.collectContours)
{
KeyPointsFilter::runByPixelsMask2VectorPoint(keypoints, blobContours, mask.getMat());
}
else
{
KeyPointsFilter::runByPixelsMask(keypoints, mask.getMat());
}
}
}
const std::vector<std::vector<Point> >& SimpleBlobDetectorImpl::getBlobContours() const {
return blobContours;
}
Ptr<SimpleBlobDetector> SimpleBlobDetector::create(const SimpleBlobDetector::Params& params)
{
SimpleBlobDetectorImpl::validateParameters(params);
return makePtr<SimpleBlobDetectorImpl>(params);
}
String SimpleBlobDetector::getDefaultName() const
{
return (Feature2D::getDefaultName() + ".SimpleBlobDetector");
}
}
File diff suppressed because it is too large Load Diff
+279
View File
@@ -0,0 +1,279 @@
/*M///////////////////////////////////////////////////////////////////////////////////////
//
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
// By downloading, copying, installing or using the software you agree to this license.
// If you do not agree to this license, do not download, install,
// copy or use the software.
//
//
// Intel License Agreement
// For Open Source Computer Vision Library
//
// Copyright (C) 2000, Intel Corporation, all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistribution's of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistribution's in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// * The name of Intel Corporation may not be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// This software is provided by the copyright holders and contributors "as is" and
// any express or implied warranties, including, but not limited to, the implied
// warranties of merchantability and fitness for a particular purpose are disclaimed.
// In no event shall the Intel Corporation or contributors be liable for any direct,
// indirect, incidental, special, exemplary, or consequential damages
// (including, but not limited to, procurement of substitute goods or services;
// loss of use, data, or profits; or business interruption) however caused
// and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of
// the use of this software, even if advised of the possibility of such damage.
//
//M*/
#include "precomp.hpp"
const int draw_shift_bits = 4;
const int draw_multiplier = 1 << draw_shift_bits;
namespace cv
{
/*
* Functions to draw keypoints and matches.
*/
static inline void _drawKeypoint( InputOutputArray img, const KeyPoint& p, const Scalar& color, DrawMatchesFlags flags )
{
CV_Assert( !img.empty() );
Point center( cvRound(p.pt.x * draw_multiplier), cvRound(p.pt.y * draw_multiplier) );
if( !!(flags & DrawMatchesFlags::DRAW_RICH_KEYPOINTS) )
{
int radius = cvRound(p.size/2 * draw_multiplier); // KeyPoint::size is a diameter
// draw the circles around keypoints with the keypoints size
circle( img, center, radius, color, 1, LINE_AA, draw_shift_bits );
// draw orientation of the keypoint, if it is applicable
if( p.angle != -1 )
{
float srcAngleRad = p.angle*(float)CV_PI/180.f;
Point orient( cvRound(cos(srcAngleRad)*radius ),
cvRound(sin(srcAngleRad)*radius )
);
line( img, center, center+orient, color, 1, LINE_AA, draw_shift_bits );
}
#if 0
else
{
// draw center with R=1
int radius = 1 * draw_multiplier;
circle( img, center, radius, color, 1, LINE_AA, draw_shift_bits );
}
#endif
}
else
{
// draw center with R=3
int radius = 3 * draw_multiplier;
circle( img, center, radius, color, 1, LINE_AA, draw_shift_bits );
}
}
void drawKeypoints( InputArray image, const std::vector<KeyPoint>& keypoints, InputOutputArray outImage,
const Scalar& _color, DrawMatchesFlags flags )
{
CV_INSTRUMENT_REGION();
if( !(flags & DrawMatchesFlags::DRAW_OVER_OUTIMG) )
{
if (image.type() == CV_8UC3 || image.type() == CV_8UC4)
{
image.copyTo(outImage);
}
else if( image.type() == CV_8UC1 )
{
cvtColor( image, outImage, COLOR_GRAY2BGR );
}
else
{
CV_Error( Error::StsBadArg, "Incorrect type of input image: " + typeToString(image.type()) );
}
}
RNG& rng=theRNG();
bool isRandColor = _color == Scalar::all(-1);
CV_Assert( !outImage.empty() );
std::vector<KeyPoint>::const_iterator it = keypoints.begin(),
end = keypoints.end();
for( ; it != end; ++it )
{
Scalar color = isRandColor ? Scalar( rng(256), rng(256), rng(256), 255 ) : _color;
_drawKeypoint( outImage, *it, color, flags );
}
}
static void _prepareImage(InputArray src, const Mat& dst)
{
CV_CheckType(src.type(), src.type() == CV_8UC1 || src.type() == CV_8UC3 || src.type() == CV_8UC4, "Unsupported source image");
CV_CheckType(dst.type(), dst.type() == CV_8UC3 || dst.type() == CV_8UC4, "Unsupported destination image");
const int src_cn = src.channels();
const int dst_cn = dst.channels();
if (src_cn == dst_cn)
src.copyTo(dst);
else if (src_cn == 1)
cvtColor(src, dst, dst_cn == 3 ? COLOR_GRAY2BGR : COLOR_GRAY2BGRA);
else if (src_cn == 3 && dst_cn == 4)
cvtColor(src, dst, COLOR_BGR2BGRA);
else if (src_cn == 4 && dst_cn == 3)
cvtColor(src, dst, COLOR_BGRA2BGR);
else
CV_Error(Error::StsInternal, "");
}
static void _prepareImgAndDrawKeypoints( InputArray img1, const std::vector<KeyPoint>& keypoints1,
InputArray img2, const std::vector<KeyPoint>& keypoints2,
InputOutputArray _outImg, Mat& outImg1, Mat& outImg2,
const Scalar& singlePointColor, DrawMatchesFlags flags )
{
Mat outImg;
Size img1size = img1.size(), img2size = img2.size();
Size size( img1size.width + img2size.width, MAX(img1size.height, img2size.height) );
if( !!(flags & DrawMatchesFlags::DRAW_OVER_OUTIMG) )
{
outImg = _outImg.getMat();
if( size.width > outImg.cols || size.height > outImg.rows )
CV_Error( Error::StsBadSize, "outImg has size less than need to draw img1 and img2 together" );
outImg1 = outImg( Rect(0, 0, img1size.width, img1size.height) );
outImg2 = outImg( Rect(img1size.width, 0, img2size.width, img2size.height) );
}
else
{
const int cn1 = img1.channels(), cn2 = img2.channels();
const int out_cn = std::max(3, std::max(cn1, cn2));
_outImg.create(size, CV_MAKETYPE(img1.depth(), out_cn));
outImg = _outImg.getMat();
outImg = Scalar::all(0);
outImg1 = outImg( Rect(0, 0, img1size.width, img1size.height) );
outImg2 = outImg( Rect(img1size.width, 0, img2size.width, img2size.height) );
_prepareImage(img1, outImg1);
_prepareImage(img2, outImg2);
}
// draw keypoints
if( !(flags & DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS) )
{
Mat _outImg1 = outImg( Rect(0, 0, img1size.width, img1size.height) );
drawKeypoints( _outImg1, keypoints1, _outImg1, singlePointColor, flags | DrawMatchesFlags::DRAW_OVER_OUTIMG );
Mat _outImg2 = outImg( Rect(img1size.width, 0, img2size.width, img2size.height) );
drawKeypoints( _outImg2, keypoints2, _outImg2, singlePointColor, flags | DrawMatchesFlags::DRAW_OVER_OUTIMG );
}
}
static inline void _drawMatch( InputOutputArray outImg, InputOutputArray outImg1, InputOutputArray outImg2 ,
const KeyPoint& kp1, const KeyPoint& kp2, const Scalar& matchColor, DrawMatchesFlags flags,
const int matchesThickness )
{
RNG& rng = theRNG();
bool isRandMatchColor = matchColor == Scalar::all(-1);
Scalar color = isRandMatchColor ? Scalar( rng(256), rng(256), rng(256), 255 ) : matchColor;
_drawKeypoint( outImg1, kp1, color, flags );
_drawKeypoint( outImg2, kp2, color, flags );
Point2f pt1 = kp1.pt,
pt2 = kp2.pt,
dpt2 = Point2f( std::min(pt2.x+outImg1.size().width, float(outImg.size().width-1)), pt2.y );
line( outImg,
Point(cvRound(pt1.x*draw_multiplier), cvRound(pt1.y*draw_multiplier)),
Point(cvRound(dpt2.x*draw_multiplier), cvRound(dpt2.y*draw_multiplier)),
color, matchesThickness, LINE_AA, draw_shift_bits );
}
void drawMatches( InputArray img1, const std::vector<KeyPoint>& keypoints1,
InputArray img2, const std::vector<KeyPoint>& keypoints2,
const std::vector<DMatch>& matches1to2, InputOutputArray outImg,
const Scalar& matchColor, const Scalar& singlePointColor,
const std::vector<char>& matchesMask, DrawMatchesFlags flags )
{
drawMatches( img1, keypoints1,
img2, keypoints2,
matches1to2, outImg,
1, matchColor,
singlePointColor, matchesMask,
flags);
}
void drawMatches( InputArray img1, const std::vector<KeyPoint>& keypoints1,
InputArray img2, const std::vector<KeyPoint>& keypoints2,
const std::vector<DMatch>& matches1to2, InputOutputArray outImg,
const int matchesThickness, const Scalar& matchColor,
const Scalar& singlePointColor, const std::vector<char>& matchesMask,
DrawMatchesFlags flags )
{
if( !matchesMask.empty() && matchesMask.size() != matches1to2.size() )
CV_Error( Error::StsBadSize, "matchesMask must have the same size as matches1to2" );
Mat outImg1, outImg2;
_prepareImgAndDrawKeypoints( img1, keypoints1, img2, keypoints2,
outImg, outImg1, outImg2, singlePointColor, flags );
// draw matches
for( size_t m = 0; m < matches1to2.size(); m++ )
{
if( matchesMask.empty() || matchesMask[m] )
{
int i1 = matches1to2[m].queryIdx;
int i2 = matches1to2[m].trainIdx;
CV_Assert(i1 >= 0 && i1 < static_cast<int>(keypoints1.size()));
CV_Assert(i2 >= 0 && i2 < static_cast<int>(keypoints2.size()));
const KeyPoint &kp1 = keypoints1[i1], &kp2 = keypoints2[i2];
_drawMatch( outImg, outImg1, outImg2, kp1, kp2, matchColor, flags, matchesThickness );
}
}
}
void drawMatches( InputArray img1, const std::vector<KeyPoint>& keypoints1,
InputArray img2, const std::vector<KeyPoint>& keypoints2,
const std::vector<std::vector<DMatch> >& matches1to2, InputOutputArray outImg,
const Scalar& matchColor, const Scalar& singlePointColor,
const std::vector<std::vector<char> >& matchesMask, DrawMatchesFlags flags )
{
if( !matchesMask.empty() && matchesMask.size() != matches1to2.size() )
CV_Error( Error::StsBadSize, "matchesMask must have the same size as matches1to2" );
Mat outImg1, outImg2;
_prepareImgAndDrawKeypoints( img1, keypoints1, img2, keypoints2,
outImg, outImg1, outImg2, singlePointColor, flags );
// draw matches
for( size_t i = 0; i < matches1to2.size(); i++ )
{
for( size_t j = 0; j < matches1to2[i].size(); j++ )
{
int i1 = matches1to2[i][j].queryIdx;
int i2 = matches1to2[i][j].trainIdx;
if( matchesMask.empty() || matchesMask[i][j] )
{
const KeyPoint &kp1 = keypoints1[i1], &kp2 = keypoints2[i2];
_drawMatch( outImg, outImg1, outImg2, kp1, kp2, matchColor, flags, 1 );
}
}
}
}
}
+47
View File
@@ -0,0 +1,47 @@
/*M///////////////////////////////////////////////////////////////////////////////////////
//
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
// By downloading, copying, installing or using the software you agree to this license.
// If you do not agree to this license, do not download, install,
// copy or use the software.
//
//
// License Agreement
// For Open Source Computer Vision Library
//
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
// Copyright (C) 2009-2010, Willow Garage Inc., all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistribution's of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistribution's in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// * The name of the copyright holders may not be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// This software is provided by the copyright holders and contributors "as is" and
// any express or implied warranties, including, but not limited to, the implied
// warranties of merchantability and fitness for a particular purpose are disclaimed.
// In no event shall the Intel Corporation or contributors be liable for any direct,
// indirect, incidental, special, exemplary, or consequential damages
// (including, but not limited to, procurement of substitute goods or services;
// loss of use, data, or profits; or business interruption) however caused
// and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of
// the use of this software, even if advised of the possibility of such damage.
//
//M*/
#include "precomp.hpp"
namespace cv
{
}
+572
View File
@@ -0,0 +1,572 @@
//*M///////////////////////////////////////////////////////////////////////////////////////
//
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
// By downloading, copying, installing or using the software you agree to this license.
// If you do not agree to this license, do not download, install,
// copy or use the software.
//
//
// License Agreement
// For Open Source Computer Vision Library
//
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistribution's of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistribution's in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// * The name of the copyright holders may not be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// This software is provided by the copyright holders and contributors "as is" and
// any express or implied warranties, including, but not limited to, the implied
// warranties of merchantability and fitness for a particular purpose are disclaimed.
// In no event shall the Intel Corporation or contributors be liable for any direct,
// indirect, incidental, special, exemplary, or consequential damages
// (including, but not limited to, procurement of substitute goods or services;
// loss of use, data, or profits; or business interruption) however caused
// and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of
// the use of this software, even if advised of the possibility of such damage.
//
//M*/
#include "precomp.hpp"
#include <limits>
using namespace cv;
template<typename _Tp> static int solveQuadratic(_Tp a, _Tp b, _Tp c, _Tp& x1, _Tp& x2)
{
if( a == 0 )
{
if( b == 0 )
{
x1 = x2 = 0;
return c == 0;
}
x1 = x2 = -c/b;
return 1;
}
_Tp d = b*b - 4*a*c;
if( d < 0 )
{
x1 = x2 = 0;
return 0;
}
if( d > 0 )
{
d = std::sqrt(d);
double s = 1/(2*a);
x1 = (-b - d)*s;
x2 = (-b + d)*s;
if( x1 > x2 )
std::swap(x1, x2);
return 2;
}
x1 = x2 = -b/(2*a);
return 1;
}
//for android ndk
#undef _S
static inline Point2f applyHomography( const Mat_<double>& H, const Point2f& pt )
{
double z = H(2,0)*pt.x + H(2,1)*pt.y + H(2,2);
if( z )
{
double w = 1./z;
return Point2f( (float)((H(0,0)*pt.x + H(0,1)*pt.y + H(0,2))*w), (float)((H(1,0)*pt.x + H(1,1)*pt.y + H(1,2))*w) );
}
return Point2f( std::numeric_limits<float>::max(), std::numeric_limits<float>::max() );
}
static inline void linearizeHomographyAt( const Mat_<double>& H, const Point2f& pt, Mat_<double>& A )
{
A.create(2,2);
double p1 = H(0,0)*pt.x + H(0,1)*pt.y + H(0,2),
p2 = H(1,0)*pt.x + H(1,1)*pt.y + H(1,2),
p3 = H(2,0)*pt.x + H(2,1)*pt.y + H(2,2),
p3_2 = p3*p3;
if( p3 )
{
A(0,0) = H(0,0)/p3 - p1*H(2,0)/p3_2; // fxdx
A(0,1) = H(0,1)/p3 - p1*H(2,1)/p3_2; // fxdy
A(1,0) = H(1,0)/p3 - p2*H(2,0)/p3_2; // fydx
A(1,1) = H(1,1)/p3 - p2*H(2,1)/p3_2; // fydx
}
else
A.setTo(Scalar::all(std::numeric_limits<double>::max()));
}
class EllipticKeyPoint
{
public:
EllipticKeyPoint();
EllipticKeyPoint( const Point2f& _center, const Scalar& _ellipse );
static void convert( const std::vector<KeyPoint>& src, std::vector<EllipticKeyPoint>& dst );
static void convert( const std::vector<EllipticKeyPoint>& src, std::vector<KeyPoint>& dst );
static Mat_<double> getSecondMomentsMatrix( const Scalar& _ellipse );
Mat_<double> getSecondMomentsMatrix() const;
void calcProjection( const Mat_<double>& H, EllipticKeyPoint& projection ) const;
static void calcProjection( const std::vector<EllipticKeyPoint>& src, const Mat_<double>& H, std::vector<EllipticKeyPoint>& dst );
Point2f center;
Scalar ellipse; // 3 elements a, b, c: ax^2+2bxy+cy^2=1
Size_<float> axes; // half length of ellipse axes
Size_<float> boundingBox; // half sizes of bounding box which sides are parallel to the coordinate axes
};
EllipticKeyPoint::EllipticKeyPoint()
{
*this = EllipticKeyPoint(Point2f(0,0), Scalar(1, 0, 1) );
}
EllipticKeyPoint::EllipticKeyPoint( const Point2f& _center, const Scalar& _ellipse )
{
center = _center;
ellipse = _ellipse;
double a = ellipse[0], b = ellipse[1], c = ellipse[2];
double ac_b2 = a*c - b*b;
double x1, x2;
solveQuadratic(1., -(a+c), ac_b2, x1, x2);
axes.width = (float)(1/sqrt(x1));
axes.height = (float)(1/sqrt(x2));
boundingBox.width = (float)sqrt(ellipse[2]/ac_b2);
boundingBox.height = (float)sqrt(ellipse[0]/ac_b2);
}
Mat_<double> EllipticKeyPoint::getSecondMomentsMatrix( const Scalar& _ellipse )
{
Mat_<double> M(2, 2);
M(0,0) = _ellipse[0];
M(1,0) = M(0,1) = _ellipse[1];
M(1,1) = _ellipse[2];
return M;
}
Mat_<double> EllipticKeyPoint::getSecondMomentsMatrix() const
{
return getSecondMomentsMatrix(ellipse);
}
void EllipticKeyPoint::calcProjection( const Mat_<double>& H, EllipticKeyPoint& projection ) const
{
Point2f dstCenter = applyHomography(H, center);
Mat_<double> invM; invert(getSecondMomentsMatrix(), invM);
Mat_<double> Aff; linearizeHomographyAt(H, center, Aff);
Mat_<double> dstM; invert(Aff*invM*Aff.t(), dstM);
projection = EllipticKeyPoint( dstCenter, Scalar(dstM(0,0), dstM(0,1), dstM(1,1)) );
}
void EllipticKeyPoint::convert( const std::vector<KeyPoint>& src, std::vector<EllipticKeyPoint>& dst )
{
CV_INSTRUMENT_REGION();
if( !src.empty() )
{
dst.resize(src.size());
for( size_t i = 0; i < src.size(); i++ )
{
float rad = src[i].size/2;
CV_Assert( rad );
float fac = 1.f/(rad*rad);
dst[i] = EllipticKeyPoint( src[i].pt, Scalar(fac, 0, fac) );
}
}
}
void EllipticKeyPoint::convert( const std::vector<EllipticKeyPoint>& src, std::vector<KeyPoint>& dst )
{
CV_INSTRUMENT_REGION();
if( !src.empty() )
{
dst.resize(src.size());
for( size_t i = 0; i < src.size(); i++ )
{
Size_<float> axes = src[i].axes;
float rad = sqrt(axes.height*axes.width);
dst[i] = KeyPoint(src[i].center, 2*rad );
}
}
}
void EllipticKeyPoint::calcProjection( const std::vector<EllipticKeyPoint>& src, const Mat_<double>& H, std::vector<EllipticKeyPoint>& dst )
{
if( !src.empty() )
{
CV_Assert( !H.empty() && H.cols == 3 && H.rows == 3);
dst.resize(src.size());
std::vector<EllipticKeyPoint>::const_iterator srcIt = src.begin();
std::vector<EllipticKeyPoint>::iterator dstIt = dst.begin();
for( ; srcIt != src.end() && dstIt != dst.end(); ++srcIt, ++dstIt )
srcIt->calcProjection(H, *dstIt);
}
}
static void filterEllipticKeyPointsByImageSize( std::vector<EllipticKeyPoint>& keypoints, const Size& imgSize )
{
if( !keypoints.empty() )
{
std::vector<EllipticKeyPoint> filtered;
filtered.reserve(keypoints.size());
std::vector<EllipticKeyPoint>::const_iterator it = keypoints.begin();
for( ; it != keypoints.end(); ++it )
{
if( it->center.x + it->boundingBox.width < imgSize.width &&
it->center.x - it->boundingBox.width > 0 &&
it->center.y + it->boundingBox.height < imgSize.height &&
it->center.y - it->boundingBox.height > 0 )
filtered.push_back(*it);
}
keypoints.assign(filtered.begin(), filtered.end());
}
}
struct IntersectAreaCounter
{
IntersectAreaCounter( float _dr, int _minx,
int _miny, int _maxy,
const Point2f& _diff,
const Scalar& _ellipse1, const Scalar& _ellipse2 ) :
dr(_dr), bua(0), bna(0), minx(_minx), miny(_miny), maxy(_maxy),
diff(_diff), ellipse1(_ellipse1), ellipse2(_ellipse2) {}
IntersectAreaCounter( const IntersectAreaCounter& counter, Split )
{
*this = counter;
bua = 0;
bna = 0;
}
void operator()( const BlockedRange& range )
{
CV_Assert( miny < maxy );
CV_Assert( dr > FLT_EPSILON );
int temp_bua = bua, temp_bna = bna;
for( int i = range.begin(); i != range.end(); i++ )
{
float rx1 = minx + i*dr;
float rx2 = rx1 - diff.x;
for( float ry1 = (float)miny; ry1 <= (float)maxy; ry1 += dr )
{
float ry2 = ry1 - diff.y;
//compute the distance from the ellipse center
float e1 = (float)(ellipse1[0]*rx1*rx1 + 2*ellipse1[1]*rx1*ry1 + ellipse1[2]*ry1*ry1);
float e2 = (float)(ellipse2[0]*rx2*rx2 + 2*ellipse2[1]*rx2*ry2 + ellipse2[2]*ry2*ry2);
//compute the area
if( e1<1 && e2<1 ) temp_bna++;
if( e1<1 || e2<1 ) temp_bua++;
}
}
bua = temp_bua;
bna = temp_bna;
}
void join( IntersectAreaCounter& ac )
{
bua += ac.bua;
bna += ac.bna;
}
float dr;
int bua, bna;
int minx;
int miny, maxy;
Point2f diff;
Scalar ellipse1, ellipse2;
};
struct SIdx
{
SIdx() : S(-1), i1(-1), i2(-1) {}
SIdx(float _S, int _i1, int _i2) : S(_S), i1(_i1), i2(_i2) {}
float S;
int i1;
int i2;
bool operator<(const SIdx& v) const { return S > v.S; }
struct UsedFinder
{
UsedFinder(const SIdx& _used) : used(_used) {}
const SIdx& used;
bool operator()(const SIdx& v) const { return (v.i1 == used.i1 || v.i2 == used.i2); }
UsedFinder& operator=(const UsedFinder&) = delete;
// To avoid -Wdeprecated-copy warning, copy constructor is needed.
UsedFinder(const UsedFinder&) = default;
};
};
static void computeOneToOneMatchedOverlaps( const std::vector<EllipticKeyPoint>& keypoints1, const std::vector<EllipticKeyPoint>& keypoints2t,
bool commonPart, std::vector<SIdx>& overlaps, float minOverlap )
{
CV_Assert( minOverlap >= 0.f );
overlaps.clear();
if( keypoints1.empty() || keypoints2t.empty() )
return;
overlaps.clear();
overlaps.reserve(cvRound(keypoints1.size() * keypoints2t.size() * 0.01));
for( size_t i1 = 0; i1 < keypoints1.size(); i1++ )
{
EllipticKeyPoint kp1 = keypoints1[i1];
float maxDist = sqrt(kp1.axes.width*kp1.axes.height),
fac = 30.f/maxDist;
if( !commonPart )
fac=3;
maxDist = maxDist*4;
fac = 1.f/(fac*fac);
EllipticKeyPoint keypoint1a = EllipticKeyPoint( kp1.center, Scalar(fac*kp1.ellipse[0], fac*kp1.ellipse[1], fac*kp1.ellipse[2]) );
for( size_t i2 = 0; i2 < keypoints2t.size(); i2++ )
{
EllipticKeyPoint kp2 = keypoints2t[i2];
Point2f diff = kp2.center - kp1.center;
if( norm(diff) < maxDist )
{
EllipticKeyPoint keypoint2a = EllipticKeyPoint( kp2.center, Scalar(fac*kp2.ellipse[0], fac*kp2.ellipse[1], fac*kp2.ellipse[2]) );
//find the largest eigenvalue
int maxx = (int)ceil(( keypoint1a.boundingBox.width > (diff.x+keypoint2a.boundingBox.width)) ?
keypoint1a.boundingBox.width : (diff.x+keypoint2a.boundingBox.width));
int minx = (int)floor((-keypoint1a.boundingBox.width < (diff.x-keypoint2a.boundingBox.width)) ?
-keypoint1a.boundingBox.width : (diff.x-keypoint2a.boundingBox.width));
int maxy = (int)ceil(( keypoint1a.boundingBox.height > (diff.y+keypoint2a.boundingBox.height)) ?
keypoint1a.boundingBox.height : (diff.y+keypoint2a.boundingBox.height));
int miny = (int)floor((-keypoint1a.boundingBox.height < (diff.y-keypoint2a.boundingBox.height)) ?
-keypoint1a.boundingBox.height : (diff.y-keypoint2a.boundingBox.height));
int mina = (maxx-minx) < (maxy-miny) ? (maxx-minx) : (maxy-miny) ;
//compute the area
float dr = (float)mina/50.f;
int N = (int)floor((float)(maxx - minx) / dr);
IntersectAreaCounter ac( dr, minx, miny, maxy, diff, keypoint1a.ellipse, keypoint2a.ellipse );
parallel_reduce( BlockedRange(0, N+1), ac );
if( ac.bna > 0 )
{
float ov = (float)ac.bna / (float)ac.bua;
if( ov >= minOverlap )
overlaps.push_back(SIdx(ov, (int)i1, (int)i2));
}
}
}
}
std::sort( overlaps.begin(), overlaps.end() );
typedef std::vector<SIdx>::iterator It;
It pos = overlaps.begin();
It end = overlaps.end();
while(pos != end)
{
It prev = pos++;
end = std::remove_if(pos, end, SIdx::UsedFinder(*prev));
}
overlaps.erase(pos, overlaps.end());
}
static void calculateRepeatability( const Mat& img1, const Mat& img2, const Mat& H1to2,
const std::vector<KeyPoint>& _keypoints1, const std::vector<KeyPoint>& _keypoints2,
float& repeatability, int& correspondencesCount,
Mat* thresholdedOverlapMask=0 )
{
std::vector<EllipticKeyPoint> keypoints1, keypoints2, keypoints1t, keypoints2t;
EllipticKeyPoint::convert( _keypoints1, keypoints1 );
EllipticKeyPoint::convert( _keypoints2, keypoints2 );
// calculate projections of key points
EllipticKeyPoint::calcProjection( keypoints1, H1to2, keypoints1t );
Mat H2to1; invert(H1to2, H2to1);
EllipticKeyPoint::calcProjection( keypoints2, H2to1, keypoints2t );
float overlapThreshold;
bool ifEvaluateDetectors = thresholdedOverlapMask == 0;
if( ifEvaluateDetectors )
{
overlapThreshold = 1.f - 0.4f;
// remove key points from outside of the common image part
Size sz1 = img1.size(), sz2 = img2.size();
filterEllipticKeyPointsByImageSize( keypoints1, sz1 );
filterEllipticKeyPointsByImageSize( keypoints1t, sz2 );
filterEllipticKeyPointsByImageSize( keypoints2, sz2 );
filterEllipticKeyPointsByImageSize( keypoints2t, sz1 );
}
else
{
overlapThreshold = 1.f - 0.5f;
thresholdedOverlapMask->create( (int)keypoints1.size(), (int)keypoints2t.size(), CV_8UC1 );
thresholdedOverlapMask->setTo( Scalar::all(0) );
}
size_t size1 = keypoints1.size(), size2 = keypoints2t.size();
size_t minCount = MIN( size1, size2 );
// calculate overlap errors
std::vector<SIdx> overlaps;
computeOneToOneMatchedOverlaps( keypoints1, keypoints2t, ifEvaluateDetectors, overlaps, overlapThreshold/*min overlap*/ );
correspondencesCount = -1;
repeatability = -1.f;
if( overlaps.empty() )
return;
if( ifEvaluateDetectors )
{
// regions one-to-one matching
correspondencesCount = (int)overlaps.size();
repeatability = minCount ? (float)correspondencesCount / minCount : -1;
}
else
{
for( size_t i = 0; i < overlaps.size(); i++ )
{
int y = overlaps[i].i1;
int x = overlaps[i].i2;
thresholdedOverlapMask->at<uchar>(y,x) = 1;
}
}
}
void cv::evaluateFeatureDetector( const Mat& img1, const Mat& img2, const Mat& H1to2,
std::vector<KeyPoint>* _keypoints1, std::vector<KeyPoint>* _keypoints2,
float& repeatability, int& correspCount,
const Ptr<FeatureDetector>& _fdetector )
{
CV_INSTRUMENT_REGION();
Ptr<FeatureDetector> fdetector(_fdetector);
std::vector<KeyPoint> *keypoints1, *keypoints2, buf1, buf2;
keypoints1 = _keypoints1 != 0 ? _keypoints1 : &buf1;
keypoints2 = _keypoints2 != 0 ? _keypoints2 : &buf2;
if( (keypoints1->empty() || keypoints2->empty()) && !fdetector )
CV_Error( Error::StsBadArg, "fdetector must not be empty when keypoints1 or keypoints2 is empty" );
if( keypoints1->empty() )
fdetector->detect( img1, *keypoints1 );
if( keypoints2->empty() )
fdetector->detect( img2, *keypoints2 );
calculateRepeatability( img1, img2, H1to2, *keypoints1, *keypoints2, repeatability, correspCount );
}
struct DMatchForEvaluation : public DMatch
{
uchar isCorrect;
DMatchForEvaluation( const DMatch &dm ) : DMatch( dm ), isCorrect(0) {}
};
static inline float recall( int correctMatchCount, int correspondenceCount )
{
return correspondenceCount ? (float)correctMatchCount / (float)correspondenceCount : -1;
}
static inline float precision( int correctMatchCount, int falseMatchCount )
{
return correctMatchCount + falseMatchCount ? (float)correctMatchCount / (float)(correctMatchCount + falseMatchCount) : -1;
}
void cv::computeRecallPrecisionCurve( const std::vector<std::vector<DMatch> >& matches1to2,
const std::vector<std::vector<uchar> >& correctMatches1to2Mask,
std::vector<Point2f>& recallPrecisionCurve )
{
CV_INSTRUMENT_REGION();
CV_Assert( matches1to2.size() == correctMatches1to2Mask.size() );
std::vector<DMatchForEvaluation> allMatches;
int correspondenceCount = 0;
for( size_t i = 0; i < matches1to2.size(); i++ )
{
for( size_t j = 0; j < matches1to2[i].size(); j++ )
{
DMatchForEvaluation match = matches1to2[i][j];
match.isCorrect = correctMatches1to2Mask[i][j] ;
allMatches.push_back( match );
correspondenceCount += match.isCorrect != 0 ? 1 : 0;
}
}
std::sort( allMatches.begin(), allMatches.end() );
int correctMatchCount = 0, falseMatchCount = 0;
recallPrecisionCurve.resize( allMatches.size() );
for( size_t i = 0; i < allMatches.size(); i++ )
{
if( allMatches[i].isCorrect )
correctMatchCount++;
else
falseMatchCount++;
float r = recall( correctMatchCount, correspondenceCount );
float p = precision( correctMatchCount, falseMatchCount );
recallPrecisionCurve[i] = Point2f(1-p, r);
}
}
float cv::getRecall( const std::vector<Point2f>& recallPrecisionCurve, float l_precision )
{
CV_INSTRUMENT_REGION();
int nearestPointIndex = getNearestPoint( recallPrecisionCurve, l_precision );
float recall = -1.f;
if( nearestPointIndex >= 0 )
recall = recallPrecisionCurve[nearestPointIndex].y;
return recall;
}
int cv::getNearestPoint( const std::vector<Point2f>& recallPrecisionCurve, float l_precision )
{
CV_INSTRUMENT_REGION();
int nearestPointIndex = -1;
if( l_precision >= 0 && l_precision <= 1 )
{
float minDiff = FLT_MAX;
for( size_t i = 0; i < recallPrecisionCurve.size(); i++ )
{
float curDiff = std::fabs(l_precision - recallPrecisionCurve[i].x);
if( curDiff <= minDiff )
{
nearestPointIndex = (int)i;
minDiff = curDiff;
}
}
}
return nearestPointIndex;
}
+184
View File
@@ -0,0 +1,184 @@
/* This is FAST corner detector, contributed to OpenCV by the author, Edward Rosten.
Below is the original copyright and the references */
/*
Copyright (c) 2006, 2008 Edward Rosten
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
*Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
*Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
*Neither the name of the University of Cambridge nor the names of
its contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
The references are:
* Machine learning for high-speed corner detection,
E. Rosten and T. Drummond, ECCV 2006
* Faster and better: A machine learning approach to corner detection
E. Rosten, R. Porter and T. Drummond, PAMI, 2009
*/
#include "precomp.hpp"
#include "fast.hpp"
#include "opencv2/core/hal/intrin.hpp"
namespace cv
{
namespace opt_AVX2
{
class FAST_t_patternSize16_AVX2_Impl CV_FINAL: public FAST_t_patternSize16_AVX2
{
public:
FAST_t_patternSize16_AVX2_Impl(int _cols, int _threshold, bool _nonmax_suppression, const int* _pixel):
cols(_cols), nonmax_suppression(_nonmax_suppression), pixel(_pixel)
{
//patternSize = 16
t256c = (char)_threshold;
threshold = std::min(std::max(_threshold, 0), 255);
}
virtual void process(int &j, const uchar* &ptr, uchar* curr, int* cornerpos, int &ncorners) CV_OVERRIDE
{
static const __m256i delta256 = _mm256_broadcastsi128_si256(_mm_set1_epi8((char)(-128))), K16_256 = _mm256_broadcastsi128_si256(_mm_set1_epi8((char)8));
const __m256i t256 = _mm256_broadcastsi128_si256(_mm_set1_epi8(t256c));
for (; j < cols - 32 - 3; j += 32, ptr += 32)
{
__m256i m0, m1;
__m256i v0 = _mm256_loadu_si256((const __m256i*)ptr);
__m256i v1 = _mm256_xor_si256(_mm256_subs_epu8(v0, t256), delta256);
v0 = _mm256_xor_si256(_mm256_adds_epu8(v0, t256), delta256);
__m256i x0 = _mm256_sub_epi8(_mm256_loadu_si256((const __m256i*)(ptr + pixel[0])), delta256);
__m256i x1 = _mm256_sub_epi8(_mm256_loadu_si256((const __m256i*)(ptr + pixel[4])), delta256);
__m256i x2 = _mm256_sub_epi8(_mm256_loadu_si256((const __m256i*)(ptr + pixel[8])), delta256);
__m256i x3 = _mm256_sub_epi8(_mm256_loadu_si256((const __m256i*)(ptr + pixel[12])), delta256);
m0 = _mm256_and_si256(_mm256_cmpgt_epi8(x0, v0), _mm256_cmpgt_epi8(x1, v0));
m1 = _mm256_and_si256(_mm256_cmpgt_epi8(v1, x0), _mm256_cmpgt_epi8(v1, x1));
m0 = _mm256_or_si256(m0, _mm256_and_si256(_mm256_cmpgt_epi8(x1, v0), _mm256_cmpgt_epi8(x2, v0)));
m1 = _mm256_or_si256(m1, _mm256_and_si256(_mm256_cmpgt_epi8(v1, x1), _mm256_cmpgt_epi8(v1, x2)));
m0 = _mm256_or_si256(m0, _mm256_and_si256(_mm256_cmpgt_epi8(x2, v0), _mm256_cmpgt_epi8(x3, v0)));
m1 = _mm256_or_si256(m1, _mm256_and_si256(_mm256_cmpgt_epi8(v1, x2), _mm256_cmpgt_epi8(v1, x3)));
m0 = _mm256_or_si256(m0, _mm256_and_si256(_mm256_cmpgt_epi8(x3, v0), _mm256_cmpgt_epi8(x0, v0)));
m1 = _mm256_or_si256(m1, _mm256_and_si256(_mm256_cmpgt_epi8(v1, x3), _mm256_cmpgt_epi8(v1, x0)));
m0 = _mm256_or_si256(m0, m1);
unsigned int mask = _mm256_movemask_epi8(m0); //unsigned is important!
if (mask == 0){
continue;
}
if ((mask & 0xffff) == 0)
{
j -= 16;
ptr -= 16;
continue;
}
__m256i c0 = _mm256_setzero_si256(), c1 = c0, max0 = c0, max1 = c0;
for (int k = 0; k < 25; k++)
{
__m256i x = _mm256_xor_si256(_mm256_loadu_si256((const __m256i*)(ptr + pixel[k])), delta256);
m0 = _mm256_cmpgt_epi8(x, v0);
m1 = _mm256_cmpgt_epi8(v1, x);
c0 = _mm256_and_si256(_mm256_sub_epi8(c0, m0), m0);
c1 = _mm256_and_si256(_mm256_sub_epi8(c1, m1), m1);
max0 = _mm256_max_epu8(max0, c0);
max1 = _mm256_max_epu8(max1, c1);
}
max0 = _mm256_max_epu8(max0, max1);
unsigned int m = _mm256_movemask_epi8(_mm256_cmpgt_epi8(max0, K16_256));
for (int k = 0; m > 0 && k < 32; k++, m >>= 1)
if (m & 1)
{
cornerpos[ncorners++] = j + k;
if (nonmax_suppression)
{
short d[25];
for (int q = 0; q < 25; q++)
d[q] = (short)(ptr[k] - ptr[k + pixel[q]]);
v_int16x8 q0 = v_setall_s16(-1000), q1 = v_setall_s16(1000);
for (int q = 0; q < 16; q += 8)
{
v_int16x8 v0_ = v_load(d + q + 1);
v_int16x8 v1_ = v_load(d + q + 2);
v_int16x8 a = v_min(v0_, v1_);
v_int16x8 b = v_max(v0_, v1_);
v0_ = v_load(d + q + 3);
a = v_min(a, v0_);
b = v_max(b, v0_);
v0_ = v_load(d + q + 4);
a = v_min(a, v0_);
b = v_max(b, v0_);
v0_ = v_load(d + q + 5);
a = v_min(a, v0_);
b = v_max(b, v0_);
v0_ = v_load(d + q + 6);
a = v_min(a, v0_);
b = v_max(b, v0_);
v0_ = v_load(d + q + 7);
a = v_min(a, v0_);
b = v_max(b, v0_);
v0_ = v_load(d + q + 8);
a = v_min(a, v0_);
b = v_max(b, v0_);
v0_ = v_load(d + q);
q0 = v_max(q0, v_min(a, v0_));
q1 = v_min(q1, v_max(b, v0_));
v0_ = v_load(d + q + 9);
q0 = v_max(q0, v_min(a, v0_));
q1 = v_min(q1, v_max(b, v0_));
}
q0 = v_max(q0, v_sub(v_setzero_s16(), q1));
curr[j + k] = (uchar)(v_reduce_max(q0) - 1);
}
}
}
_mm256_zeroupper();
}
virtual ~FAST_t_patternSize16_AVX2_Impl() CV_OVERRIDE {}
private:
int cols;
char t256c;
int threshold;
bool nonmax_suppression;
const int* pixel;
};
Ptr<FAST_t_patternSize16_AVX2> FAST_t_patternSize16_AVX2::getImpl(int _cols, int _threshold, bool _nonmax_suppression, const int* _pixel)
{
return Ptr<FAST_t_patternSize16_AVX2>(new FAST_t_patternSize16_AVX2_Impl(_cols, _threshold, _nonmax_suppression, _pixel));
}
}
}
+599
View File
@@ -0,0 +1,599 @@
/* This is FAST corner detector, contributed to OpenCV by the author, Edward Rosten.
Below is the original copyright and the references */
/*
Copyright (c) 2006, 2008 Edward Rosten
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
*Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
*Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
*Neither the name of the University of Cambridge nor the names of
its contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
The references are:
* Machine learning for high-speed corner detection,
E. Rosten and T. Drummond, ECCV 2006
* Faster and better: A machine learning approach to corner detection
E. Rosten, R. Porter and T. Drummond, PAMI, 2009
*/
#include "precomp.hpp"
#include "fast.hpp"
#include "fast_score.hpp"
#include "opencl_kernels_features2d.hpp"
#include "hal_replacement.hpp"
#include "opencv2/core/hal/intrin.hpp"
#include "opencv2/core/utils/buffer_area.private.hpp"
namespace cv
{
template<int patternSize>
void FAST_t(InputArray _img, std::vector<KeyPoint>& keypoints, int threshold, bool nonmax_suppression)
{
Mat img = _img.getMat();
const int K = patternSize/2, N = patternSize + K + 1;
int i, j, k, pixel[25];
makeOffsets(pixel, (int)img.step, patternSize);
#if CV_SIMD128
const int quarterPatternSize = patternSize/4;
v_uint8x16 delta = v_setall_u8(0x80), t = v_setall_u8((char)threshold), K16 = v_setall_u8((char)K);
#if CV_TRY_AVX2
Ptr<opt_AVX2::FAST_t_patternSize16_AVX2> fast_t_impl_avx2;
if(CV_CPU_HAS_SUPPORT_AVX2)
fast_t_impl_avx2 = opt_AVX2::FAST_t_patternSize16_AVX2::getImpl(img.cols, threshold, nonmax_suppression, pixel);
#endif
#endif
keypoints.clear();
threshold = std::min(std::max(threshold, 0), 255);
uchar threshold_tab[512];
for( i = -255; i <= 255; i++ )
threshold_tab[i+255] = (uchar)(i < -threshold ? 1 : i > threshold ? 2 : 0);
uchar* buf[3] = { 0 };
int* cpbuf[3] = { 0 };
utils::BufferArea area;
for (unsigned idx = 0; idx < 3; ++idx)
{
area.allocate(buf[idx], img.cols);
area.allocate(cpbuf[idx], img.cols + 1);
}
area.commit();
for (unsigned idx = 0; idx < 3; ++idx)
{
memset(buf[idx], 0, img.cols);
}
for(i = 3; i < img.rows-2; i++)
{
const uchar* ptr = img.ptr<uchar>(i) + 3;
uchar* curr = buf[(i - 3)%3];
int* cornerpos = cpbuf[(i - 3)%3] + 1; // cornerpos[-1] is used to store a value
memset(curr, 0, img.cols);
int ncorners = 0;
if( i < img.rows - 3 )
{
j = 3;
#if CV_SIMD128
{
if( patternSize == 16 )
{
#if CV_TRY_AVX2
if (fast_t_impl_avx2)
fast_t_impl_avx2->process(j, ptr, curr, cornerpos, ncorners);
#endif
//vz if (j <= (img.cols - 27)) //it doesn't make sense using vectors for less than 8 elements
{
for (; j < img.cols - 16 - 3; j += 16, ptr += 16)
{
v_uint8x16 v = v_load(ptr);
v_int8x16 v0 = v_reinterpret_as_s8(v_xor(v_add(v, t), delta));
v_int8x16 v1 = v_reinterpret_as_s8(v_xor(v_sub(v, t), delta));
v_int8x16 x0 = v_reinterpret_as_s8(v_sub_wrap(v_load(ptr + pixel[0]), delta));
v_int8x16 x1 = v_reinterpret_as_s8(v_sub_wrap(v_load(ptr + pixel[quarterPatternSize]), delta));
v_int8x16 x2 = v_reinterpret_as_s8(v_sub_wrap(v_load(ptr + pixel[2*quarterPatternSize]), delta));
v_int8x16 x3 = v_reinterpret_as_s8(v_sub_wrap(v_load(ptr + pixel[3*quarterPatternSize]), delta));
v_int8x16 m0, m1;
m0 = v_and(v_lt(v0, x0), v_lt(v0, x1));
m1 = v_and(v_lt(x0, v1), v_lt(x1, v1));
m0 = v_or(m0, v_and(v_lt(v0, x1), v_lt(v0, x2)));
m1 = v_or(m1, v_and(v_lt(x1, v1), v_lt(x2, v1)));
m0 = v_or(m0, v_and(v_lt(v0, x2), v_lt(v0, x3)));
m1 = v_or(m1, v_and(v_lt(x2, v1), v_lt(x3, v1)));
m0 = v_or(m0, v_and(v_lt(v0, x3), v_lt(v0, x0)));
m1 = v_or(m1, v_and(v_lt(x3, v1), v_lt(x0, v1)));
m0 = v_or(m0, m1);
if( !v_check_any(m0) )
continue;
if( !v_check_any(v_combine_low(m0, m0)) )
{
j -= 8;
ptr -= 8;
continue;
}
v_int8x16 c0 = v_setzero_s8();
v_int8x16 c1 = v_setzero_s8();
v_uint8x16 max0 = v_setzero_u8();
v_uint8x16 max1 = v_setzero_u8();
for( k = 0; k < N; k++ )
{
v_int8x16 x = v_reinterpret_as_s8(v_xor(v_load((ptr + pixel[k])), delta));
m0 = v_lt(v0, x);
m1 = v_lt(x, v1);
c0 = v_and(v_sub_wrap(c0, m0), m0);
c1 = v_and(v_sub_wrap(c1, m1), m1);
max0 = v_max(max0, v_reinterpret_as_u8(c0));
max1 = v_max(max1, v_reinterpret_as_u8(c1));
}
max0 = v_lt(K16, v_max(max0, max1));
unsigned int m = v_signmask(v_reinterpret_as_s8(max0));
for( k = 0; m > 0 && k < 16; k++, m >>= 1 )
{
if( m & 1 )
{
cornerpos[ncorners++] = j+k;
if(nonmax_suppression)
{
short d[25];
int _k = 0;
#if CV_ENABLE_UNROLLED
for (; _k + 4 < 25; _k += 5)
{
d[_k] = (short)(ptr[k] - ptr[k + pixel[_k]]);
d[_k + 1] = (short)(ptr[k] - ptr[k + pixel[_k + 1]]);
d[_k + 2] = (short)(ptr[k] - ptr[k + pixel[_k + 2]]);
d[_k + 3] = (short)(ptr[k] - ptr[k + pixel[_k + 3]]);
d[_k + 4] = (short)(ptr[k] - ptr[k + pixel[_k + 4]]);
}
#else
for ( ; _k < 25; _k++)
d[_k] = (short)(ptr[k] - ptr[k + pixel[_k]]);
#endif
v_int16x8 a0, b0, a1, b1;
a0 = b0 = a1 = b1 = v_load(d + 8);
for(int shift = 0; shift < 8; ++shift)
{
v_int16x8 v_nms = v_load(d + shift);
a0 = v_min(a0, v_nms);
b0 = v_max(b0, v_nms);
v_nms = v_load(d + 9 + shift);
a1 = v_min(a1, v_nms);
b1 = v_max(b1, v_nms);
}
curr[j + k] = (uchar)(v_reduce_max(v_max(v_max(a0, a1), v_sub(v_setzero_s16(), v_min(b0, b1)))) - 1);
}
}
}
}
}
}
}
#endif
for( ; j < img.cols - 3; j++, ptr++ )
{
int v = ptr[0];
const uchar* tab = &threshold_tab[0] - v + 255;
int d = tab[ptr[pixel[0]]] | tab[ptr[pixel[8]]];
if( d == 0 )
continue;
d &= tab[ptr[pixel[2]]] | tab[ptr[pixel[10]]];
d &= tab[ptr[pixel[4]]] | tab[ptr[pixel[12]]];
d &= tab[ptr[pixel[6]]] | tab[ptr[pixel[14]]];
if( d == 0 )
continue;
d &= tab[ptr[pixel[1]]] | tab[ptr[pixel[9]]];
d &= tab[ptr[pixel[3]]] | tab[ptr[pixel[11]]];
d &= tab[ptr[pixel[5]]] | tab[ptr[pixel[13]]];
d &= tab[ptr[pixel[7]]] | tab[ptr[pixel[15]]];
if( d & 1 )
{
int vt = v - threshold, count = 0;
for( k = 0; k < N; k++ )
{
int x = ptr[pixel[k]];
if(x < vt)
{
if( ++count > K )
{
cornerpos[ncorners++] = j;
if(nonmax_suppression)
curr[j] = (uchar)cornerScore<patternSize>(ptr, pixel, threshold);
break;
}
}
else
count = 0;
}
}
if( d & 2 )
{
int vt = v + threshold, count = 0;
for( k = 0; k < N; k++ )
{
int x = ptr[pixel[k]];
if(x > vt)
{
if( ++count > K )
{
cornerpos[ncorners++] = j;
if(nonmax_suppression)
curr[j] = (uchar)cornerScore<patternSize>(ptr, pixel, threshold);
break;
}
}
else
count = 0;
}
}
}
}
cornerpos[-1] = ncorners;
if( i == 3 )
continue;
const uchar* prev = buf[(i - 4 + 3)%3];
const uchar* pprev = buf[(i - 5 + 3)%3];
cornerpos = cpbuf[(i - 4 + 3)%3] + 1; // cornerpos[-1] is used to store a value
ncorners = cornerpos[-1];
for( k = 0; k < ncorners; k++ )
{
j = cornerpos[k];
int score = prev[j];
if( !nonmax_suppression ||
(score > prev[j+1] && score > prev[j-1] &&
score > pprev[j-1] && score > pprev[j] && score > pprev[j+1] &&
score > curr[j-1] && score > curr[j] && score > curr[j+1]) )
{
keypoints.push_back(KeyPoint((float)j, (float)(i-1), 7.f, -1, (float)score));
}
}
}
}
#ifdef HAVE_OPENCL
template<typename pt>
struct cmp_pt
{
bool operator ()(const pt& a, const pt& b) const { return a.y < b.y || (a.y == b.y && a.x < b.x); }
};
static bool ocl_FAST( InputArray _img, std::vector<KeyPoint>& keypoints,
int threshold, bool nonmax_suppression, int maxKeypoints )
{
UMat img = _img.getUMat();
if( img.cols < 7 || img.rows < 7 )
return false;
size_t globalsize[] = { (size_t)img.cols-6, (size_t)img.rows-6 };
ocl::Kernel fastKptKernel("FAST_findKeypoints", ocl::features2d::fast_oclsrc);
if (fastKptKernel.empty())
return false;
UMat kp1(1, maxKeypoints*2+1, CV_32S);
UMat ucounter1(kp1, Rect(0,0,1,1));
ucounter1.setTo(Scalar::all(0));
if( !fastKptKernel.args(ocl::KernelArg::ReadOnly(img),
ocl::KernelArg::PtrReadWrite(kp1),
maxKeypoints, threshold).run(2, globalsize, 0, true))
return false;
Mat mcounter;
ucounter1.copyTo(mcounter);
int i, counter = mcounter.at<int>(0);
counter = std::min(counter, maxKeypoints);
keypoints.clear();
if( counter == 0 )
return true;
if( !nonmax_suppression )
{
Mat m;
kp1(Rect(0, 0, counter*2+1, 1)).copyTo(m);
const Point* pt = (const Point*)(m.ptr<int>() + 1);
for( i = 0; i < counter; i++ )
keypoints.push_back(KeyPoint((float)pt[i].x, (float)pt[i].y, 7.f, -1, 1.f));
}
else
{
UMat kp2(1, maxKeypoints*3+1, CV_32S);
UMat ucounter2 = kp2(Rect(0,0,1,1));
ucounter2.setTo(Scalar::all(0));
ocl::Kernel fastNMSKernel("FAST_nonmaxSupression", ocl::features2d::fast_oclsrc);
if (fastNMSKernel.empty())
return false;
size_t globalsize_nms[] = { (size_t)counter };
if( !fastNMSKernel.args(ocl::KernelArg::PtrReadOnly(kp1),
ocl::KernelArg::PtrReadWrite(kp2),
ocl::KernelArg::ReadOnly(img),
counter, counter).run(1, globalsize_nms, 0, true))
return false;
Mat m2;
kp2(Rect(0, 0, counter*3+1, 1)).copyTo(m2);
Point3i* pt2 = (Point3i*)(m2.ptr<int>() + 1);
int newcounter = std::min(m2.at<int>(0), counter);
std::sort(pt2, pt2 + newcounter, cmp_pt<Point3i>());
for( i = 0; i < newcounter; i++ )
keypoints.push_back(KeyPoint((float)pt2[i].x, (float)pt2[i].y, 7.f, -1, (float)pt2[i].z));
}
return true;
}
#endif
static inline int hal_FAST(cv::Mat& src, std::vector<KeyPoint>& keypoints, int threshold, bool nonmax_suppression, FastFeatureDetector::DetectorType type)
{
if (threshold > 20)
return CV_HAL_ERROR_NOT_IMPLEMENTED;
cv::Mat scores(src.size(), src.type());
int error = cv_hal_FAST_dense(src.data, src.step, scores.data, scores.step, src.cols, src.rows, type);
if (error != CV_HAL_ERROR_OK)
return error;
cv::Mat suppressedScores(src.size(), src.type());
if (nonmax_suppression)
{
error = cv_hal_FAST_NMS(scores.data, scores.step, suppressedScores.data, suppressedScores.step, scores.cols, scores.rows);
if (error != CV_HAL_ERROR_OK)
return error;
}
else
{
suppressedScores = scores;
}
if (!threshold && nonmax_suppression) threshold = 1;
cv::KeyPoint kpt(0, 0, 7.f, -1, 0);
unsigned uthreshold = (unsigned) threshold;
int ofs = 3;
int stride = (int)suppressedScores.step;
const unsigned char* pscore = suppressedScores.data;
keypoints.clear();
for (int y = ofs; y + ofs < suppressedScores.rows; ++y)
{
kpt.pt.y = (float)(y);
for (int x = ofs; x + ofs < suppressedScores.cols; ++x)
{
unsigned score = pscore[y * stride + x];
if (score > uthreshold)
{
kpt.pt.x = (float)(x);
kpt.response = (nonmax_suppression != 0) ? (float)((int)score - 1) : 0.f;
keypoints.push_back(kpt);
}
}
}
return CV_HAL_ERROR_OK;
}
void FAST(InputArray _img, std::vector<KeyPoint>& keypoints, int threshold, bool nonmax_suppression, FastFeatureDetector::DetectorType type)
{
CV_INSTRUMENT_REGION();
if (type != FastFeatureDetector::TYPE_5_8 &&
type != FastFeatureDetector::TYPE_7_12 &&
type != FastFeatureDetector::TYPE_9_16)
{
CV_Error_(Error::StsBadArg,
("Unknown FastFeatureDetector detector type: %d", static_cast<int>(type)));
}
const size_t max_fast_features = std::max(_img.total()/100, size_t(1000)); // Simple heuristic that depends on resolution.
CV_OCL_RUN(_img.isUMat() && type == FastFeatureDetector::TYPE_9_16,
ocl_FAST(_img, keypoints, threshold, nonmax_suppression, (int)max_fast_features));
cv::Mat img = _img.getMat();
CALL_HAL(fast_dense, hal_FAST, img, keypoints, threshold, nonmax_suppression, type);
size_t keypoints_count = 1;
keypoints.clear();
KeyPoint* kps = (KeyPoint*)malloc(sizeof(KeyPoint) * keypoints_count);
int hal_ret = cv_hal_FASTv2(img.data, img.step, img.cols, img.rows, (void**)&kps,
&keypoints_count, threshold, nonmax_suppression, type, realloc);
if (hal_ret == CV_HAL_ERROR_OK) {
keypoints.assign(kps, kps + keypoints_count);
free(kps);
return;
} else {
free(kps);
keypoints_count = max_fast_features;
keypoints.clear();
keypoints.resize(keypoints_count);
CALL_HAL(fast, cv_hal_FAST, img.data, img.step, img.cols, img.rows,
(uchar*)(keypoints.data()), &keypoints_count, threshold, nonmax_suppression, type);
}
switch(type) {
case FastFeatureDetector::TYPE_5_8:
FAST_t<8>(_img, keypoints, threshold, nonmax_suppression);
break;
case FastFeatureDetector::TYPE_7_12:
FAST_t<12>(_img, keypoints, threshold, nonmax_suppression);
break;
case FastFeatureDetector::TYPE_9_16:
FAST_t<16>(_img, keypoints, threshold, nonmax_suppression);
break;
}
}
void FAST(InputArray _img, std::vector<KeyPoint>& keypoints, int threshold, bool nonmax_suppression)
{
CV_INSTRUMENT_REGION();
FAST(_img, keypoints, threshold, nonmax_suppression, FastFeatureDetector::TYPE_9_16);
}
class FastFeatureDetector_Impl CV_FINAL : public FastFeatureDetector
{
public:
FastFeatureDetector_Impl( int _threshold, bool _nonmaxSuppression, FastFeatureDetector::DetectorType _type )
: threshold(_threshold), nonmaxSuppression(_nonmaxSuppression), type(_type)
{}
void read( const FileNode& fn) CV_OVERRIDE
{
// if node is empty, keep previous value
if (!fn["threshold"].empty())
fn["threshold"] >> threshold;
if (!fn["nonmaxSuppression"].empty())
fn["nonmaxSuppression"] >> nonmaxSuppression;
if (!fn["type"].empty())
fn["type"] >> type;
}
void write( FileStorage& fs) const CV_OVERRIDE
{
if(fs.isOpened())
{
fs << "name" << getDefaultName();
fs << "threshold" << threshold;
fs << "nonmaxSuppression" << nonmaxSuppression;
fs << "type" << type;
}
}
void detect( InputArray _image, std::vector<KeyPoint>& keypoints, InputArray _mask ) CV_OVERRIDE
{
CV_INSTRUMENT_REGION();
if(_image.empty())
{
keypoints.clear();
return;
}
Mat mask = _mask.getMat(), grayImage;
UMat ugrayImage;
_InputArray gray = _image;
if( _image.type() != CV_8U )
{
_OutputArray ogray = _image.isUMat() ? _OutputArray(ugrayImage) : _OutputArray(grayImage);
cvtColor( _image, ogray, COLOR_BGR2GRAY );
gray = ogray;
}
FAST( gray, keypoints, threshold, nonmaxSuppression, type );
KeyPointsFilter::runByPixelsMask( keypoints, mask );
}
void set(int prop, double value)
{
if(prop == THRESHOLD)
threshold = cvRound(value);
else if(prop == NONMAX_SUPPRESSION)
nonmaxSuppression = value != 0;
else if(prop == FAST_N)
type = static_cast<FastFeatureDetector::DetectorType>(cvRound(value));
else
CV_Error_(Error::StsBadArg, ("Unknown FastFeatureDetector property: %d", prop));
}
double get(int prop) const
{
if(prop == THRESHOLD)
return threshold;
if(prop == NONMAX_SUPPRESSION)
return nonmaxSuppression;
if(prop == FAST_N)
return static_cast<int>(type);
CV_Error_(Error::StsBadArg, ("Unknown FastFeatureDetector property: %d", prop));
return 0;
}
void setThreshold(int threshold_) CV_OVERRIDE { threshold = threshold_; }
int getThreshold() const CV_OVERRIDE { return threshold; }
void setNonmaxSuppression(bool f) CV_OVERRIDE { nonmaxSuppression = f; }
bool getNonmaxSuppression() const CV_OVERRIDE { return nonmaxSuppression; }
void setType(FastFeatureDetector::DetectorType type_) CV_OVERRIDE{ type = type_; }
FastFeatureDetector::DetectorType getType() const CV_OVERRIDE{ return type; }
int threshold;
bool nonmaxSuppression;
FastFeatureDetector::DetectorType type;
};
Ptr<FastFeatureDetector> FastFeatureDetector::create( int threshold, bool nonmaxSuppression, FastFeatureDetector::DetectorType type )
{
return makePtr<FastFeatureDetector_Impl>(threshold, nonmaxSuppression, type);
}
String FastFeatureDetector::getDefaultName() const
{
return (Feature2D::getDefaultName() + ".FastFeatureDetector");
}
}
+62
View File
@@ -0,0 +1,62 @@
/* This is FAST corner detector, contributed to OpenCV by the author, Edward Rosten.
Below is the original copyright and the references */
/*
Copyright (c) 2006, 2008 Edward Rosten
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
*Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
*Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
*Neither the name of the University of Cambridge nor the names of
its contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
The references are:
* Machine learning for high-speed corner detection,
E. Rosten and T. Drummond, ECCV 2006
* Faster and better: A machine learning approach to corner detection
E. Rosten, R. Porter and T. Drummond, PAMI, 2009
*/
#ifndef OPENCV_FEATURES2D_FAST_HPP
#define OPENCV_FEATURES2D_FAST_HPP
namespace cv
{
namespace opt_AVX2
{
#if CV_TRY_AVX2
class FAST_t_patternSize16_AVX2
{
public:
static Ptr<FAST_t_patternSize16_AVX2> getImpl(int _cols, int _threshold, bool _nonmax_suppression, const int* _pixel);
virtual void process(int &j, const uchar* &ptr, uchar* curr, int* cornerpos, int &ncorners) = 0;
virtual ~FAST_t_patternSize16_AVX2() {}
};
#endif
}
}
#endif
+366
View File
@@ -0,0 +1,366 @@
/* This is FAST corner detector, contributed to OpenCV by the author, Edward Rosten.
Below is the original copyright and the references */
/*
Copyright (c) 2006, 2008 Edward Rosten
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
*Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
*Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
*Neither the name of the University of Cambridge nor the names of
its contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
The references are:
* Machine learning for high-speed corner detection,
E. Rosten and T. Drummond, ECCV 2006
* Faster and better: A machine learning approach to corner detection
E. Rosten, R. Porter and T. Drummond, PAMI, 2009
*/
#include "fast_score.hpp"
#include "opencv2/core/hal/intrin.hpp"
#define VERIFY_CORNERS 0
namespace cv {
void makeOffsets(int pixel[25], int rowStride, int patternSize)
{
static const int offsets16[][2] =
{
{0, 3}, { 1, 3}, { 2, 2}, { 3, 1}, { 3, 0}, { 3, -1}, { 2, -2}, { 1, -3},
{0, -3}, {-1, -3}, {-2, -2}, {-3, -1}, {-3, 0}, {-3, 1}, {-2, 2}, {-1, 3}
};
static const int offsets12[][2] =
{
{0, 2}, { 1, 2}, { 2, 1}, { 2, 0}, { 2, -1}, { 1, -2},
{0, -2}, {-1, -2}, {-2, -1}, {-2, 0}, {-2, 1}, {-1, 2}
};
static const int offsets8[][2] =
{
{0, 1}, { 1, 1}, { 1, 0}, { 1, -1},
{0, -1}, {-1, -1}, {-1, 0}, {-1, 1}
};
const int (*offsets)[2] = patternSize == 16 ? offsets16 :
patternSize == 12 ? offsets12 :
patternSize == 8 ? offsets8 : 0;
CV_Assert(pixel && offsets);
int k = 0;
for( ; k < patternSize; k++ )
pixel[k] = offsets[k][0] + offsets[k][1] * rowStride;
for( ; k < 25; k++ )
pixel[k] = pixel[k - patternSize];
}
#if VERIFY_CORNERS
static void testCorner(const uchar* ptr, const int pixel[], int K, int N, int threshold) {
// check that with the computed "threshold" the pixel is still a corner
// and that with the increased-by-1 "threshold" the pixel is not a corner anymore
for( int delta = 0; delta <= 1; delta++ )
{
int v0 = std::min(ptr[0] + threshold + delta, 255);
int v1 = std::max(ptr[0] - threshold - delta, 0);
int c0 = 0, c1 = 0;
for( int k = 0; k < N; k++ )
{
int x = ptr[pixel[k]];
if(x > v0)
{
if( ++c0 > K )
break;
c1 = 0;
}
else if( x < v1 )
{
if( ++c1 > K )
break;
c0 = 0;
}
else
{
c0 = c1 = 0;
}
}
CV_Assert( (delta == 0 && std::max(c0, c1) > K) ||
(delta == 1 && std::max(c0, c1) <= K) );
}
}
#endif
template<>
int cornerScore<16>(const uchar* ptr, const int pixel[], int threshold)
{
const int K = 8, N = K*3 + 1;
int k, v = ptr[0];
short d[N];
for( k = 0; k < N; k++ )
d[k] = (short)(v - ptr[pixel[k]]);
#if CV_SIMD128
if (true)
{
v_int16x8 q0 = v_setall_s16(-1000), q1 = v_setall_s16(1000);
for (k = 0; k < 16; k += 8)
{
v_int16x8 v0 = v_load(d + k + 1);
v_int16x8 v1 = v_load(d + k + 2);
v_int16x8 a = v_min(v0, v1);
v_int16x8 b = v_max(v0, v1);
v0 = v_load(d + k + 3);
a = v_min(a, v0);
b = v_max(b, v0);
v0 = v_load(d + k + 4);
a = v_min(a, v0);
b = v_max(b, v0);
v0 = v_load(d + k + 5);
a = v_min(a, v0);
b = v_max(b, v0);
v0 = v_load(d + k + 6);
a = v_min(a, v0);
b = v_max(b, v0);
v0 = v_load(d + k + 7);
a = v_min(a, v0);
b = v_max(b, v0);
v0 = v_load(d + k + 8);
a = v_min(a, v0);
b = v_max(b, v0);
v0 = v_load(d + k);
q0 = v_max(q0, v_min(a, v0));
q1 = v_min(q1, v_max(b, v0));
v0 = v_load(d + k + 9);
q0 = v_max(q0, v_min(a, v0));
q1 = v_min(q1, v_max(b, v0));
}
q0 = v_max(q0, v_sub(v_setzero_s16(), q1));
threshold = v_reduce_max(q0) - 1;
}
else
#endif
{
int a0 = threshold;
for( k = 0; k < 16; k += 2 )
{
int a = std::min((int)d[k+1], (int)d[k+2]);
a = std::min(a, (int)d[k+3]);
if( a <= a0 )
continue;
a = std::min(a, (int)d[k+4]);
a = std::min(a, (int)d[k+5]);
a = std::min(a, (int)d[k+6]);
a = std::min(a, (int)d[k+7]);
a = std::min(a, (int)d[k+8]);
a0 = std::max(a0, std::min(a, (int)d[k]));
a0 = std::max(a0, std::min(a, (int)d[k+9]));
}
int b0 = -a0;
for( k = 0; k < 16; k += 2 )
{
int b = std::max((int)d[k+1], (int)d[k+2]);
b = std::max(b, (int)d[k+3]);
b = std::max(b, (int)d[k+4]);
b = std::max(b, (int)d[k+5]);
if( b >= b0 )
continue;
b = std::max(b, (int)d[k+6]);
b = std::max(b, (int)d[k+7]);
b = std::max(b, (int)d[k+8]);
b0 = std::min(b0, std::max(b, (int)d[k]));
b0 = std::min(b0, std::max(b, (int)d[k+9]));
}
threshold = -b0 - 1;
}
#if VERIFY_CORNERS
testCorner(ptr, pixel, K, N, threshold);
#endif
return threshold;
}
template<>
int cornerScore<12>(const uchar* ptr, const int pixel[], int threshold)
{
const int K = 6, N = K*3 + 1;
int k, v = ptr[0];
short d[N + 4];
for( k = 0; k < N; k++ )
d[k] = (short)(v - ptr[pixel[k]]);
#if CV_SIMD128
for( k = 0; k < 4; k++ )
d[N+k] = d[k];
#endif
#if CV_SIMD128
if (true)
{
v_int16x8 q0 = v_setall_s16(-1000), q1 = v_setall_s16(1000);
for (k = 0; k < 16; k += 8)
{
v_int16x8 v0 = v_load(d + k + 1);
v_int16x8 v1 = v_load(d + k + 2);
v_int16x8 a = v_min(v0, v1);
v_int16x8 b = v_max(v0, v1);
v0 = v_load(d + k + 3);
a = v_min(a, v0);
b = v_max(b, v0);
v0 = v_load(d + k + 4);
a = v_min(a, v0);
b = v_max(b, v0);
v0 = v_load(d + k + 5);
a = v_min(a, v0);
b = v_max(b, v0);
v0 = v_load(d + k + 6);
a = v_min(a, v0);
b = v_max(b, v0);
v0 = v_load(d + k);
q0 = v_max(q0, v_min(a, v0));
q1 = v_min(q1, v_max(b, v0));
v0 = v_load(d + k + 7);
q0 = v_max(q0, v_min(a, v0));
q1 = v_min(q1, v_max(b, v0));
}
q0 = v_max(q0, v_sub(v_setzero_s16(), q1));
threshold = v_reduce_max(q0) - 1;
}
else
#endif
{
int a0 = threshold;
for( k = 0; k < 12; k += 2 )
{
int a = std::min((int)d[k+1], (int)d[k+2]);
if( a <= a0 )
continue;
a = std::min(a, (int)d[k+3]);
a = std::min(a, (int)d[k+4]);
a = std::min(a, (int)d[k+5]);
a = std::min(a, (int)d[k+6]);
a0 = std::max(a0, std::min(a, (int)d[k]));
a0 = std::max(a0, std::min(a, (int)d[k+7]));
}
int b0 = -a0;
for( k = 0; k < 12; k += 2 )
{
int b = std::max((int)d[k+1], (int)d[k+2]);
b = std::max(b, (int)d[k+3]);
b = std::max(b, (int)d[k+4]);
if( b >= b0 )
continue;
b = std::max(b, (int)d[k+5]);
b = std::max(b, (int)d[k+6]);
b0 = std::min(b0, std::max(b, (int)d[k]));
b0 = std::min(b0, std::max(b, (int)d[k+7]));
}
threshold = -b0-1;
}
#if VERIFY_CORNERS
testCorner(ptr, pixel, K, N, threshold);
#endif
return threshold;
}
template<>
int cornerScore<8>(const uchar* ptr, const int pixel[], int threshold)
{
const int K = 4, N = K * 3 + 1;
int k, v = ptr[0];
short d[N];
for (k = 0; k < N; k++)
d[k] = (short)(v - ptr[pixel[k]]);
#if CV_SIMD128 \
&& (!defined(CV_SIMD128_CPP) || (!defined(__GNUC__) || __GNUC__ != 5)) // "movdqa" bug on "v_load(d + 1)" line (Ubuntu 16.04 + GCC 5.4)
if (true)
{
v_int16x8 v0 = v_load(d + 1);
v_int16x8 v1 = v_load(d + 2);
v_int16x8 a = v_min(v0, v1);
v_int16x8 b = v_max(v0, v1);
v0 = v_load(d + 3);
a = v_min(a, v0);
b = v_max(b, v0);
v0 = v_load(d + 4);
a = v_min(a, v0);
b = v_max(b, v0);
v0 = v_load(d);
v_int16x8 q0 = v_min(a, v0);
v_int16x8 q1 = v_max(b, v0);
v0 = v_load(d + 5);
q0 = v_max(q0, v_min(a, v0));
q1 = v_min(q1, v_max(b, v0));
q0 = v_max(q0, v_sub(v_setzero_s16(), q1));
threshold = v_reduce_max(q0) - 1;
}
else
#endif
{
int a0 = threshold;
for( k = 0; k < 8; k += 2 )
{
int a = std::min((int)d[k+1], (int)d[k+2]);
if( a <= a0 )
continue;
a = std::min(a, (int)d[k+3]);
a = std::min(a, (int)d[k+4]);
a0 = std::max(a0, std::min(a, (int)d[k]));
a0 = std::max(a0, std::min(a, (int)d[k+5]));
}
int b0 = -a0;
for( k = 0; k < 8; k += 2 )
{
int b = std::max((int)d[k+1], (int)d[k+2]);
b = std::max(b, (int)d[k+3]);
if( b >= b0 )
continue;
b = std::max(b, (int)d[k+4]);
b0 = std::min(b0, std::max(b, (int)d[k]));
b0 = std::min(b0, std::max(b, (int)d[k+5]));
}
threshold = -b0-1;
}
#if VERIFY_CORNERS
testCorner(ptr, pixel, K, N, threshold);
#endif
return threshold;
}
} // namespace cv
+62
View File
@@ -0,0 +1,62 @@
/* This is FAST corner detector, contributed to OpenCV by the author, Edward Rosten.
Below is the original copyright and the references */
/*
Copyright (c) 2006, 2008 Edward Rosten
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
*Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
*Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
*Neither the name of the University of Cambridge nor the names of
its contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
The references are:
* Machine learning for high-speed corner detection,
E. Rosten and T. Drummond, ECCV 2006
* Faster and better: A machine learning approach to corner detection
E. Rosten, R. Porter and T. Drummond, PAMI, 2009
*/
#ifndef __OPENCV_FEATURES_2D_FAST_HPP__
#define __OPENCV_FEATURES_2D_FAST_HPP__
#ifdef __cplusplus
#include "precomp.hpp"
namespace cv
{
void makeOffsets(int pixel[25], int row_stride, int patternSize);
template<int patternSize>
int cornerScore(const uchar* ptr, const int pixel[], int threshold);
}
#endif
#endif
+224
View File
@@ -0,0 +1,224 @@
/*M///////////////////////////////////////////////////////////////////////////////////////
//
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
// By downloading, copying, installing or using the software you agree to this license.
// If you do not agree to this license, do not download, install,
// copy or use the software.
//
//
// License Agreement
// For Open Source Computer Vision Library
//
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistribution's of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistribution's in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// * The name of the copyright holders may not be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// This software is provided by the copyright holders and contributors "as is" and
// any express or implied warranties, including, but not limited to, the implied
// warranties of merchantability and fitness for a particular purpose are disclaimed.
// In no event shall the Intel Corporation or contributors be liable for any direct,
// indirect, incidental, special, exemplary, or consequential damages
// (including, but not limited to, procurement of substitute goods or services;
// loss of use, data, or profits; or business interruption) however caused
// and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of
// the use of this software, even if advised of the possibility of such damage.
//
//M*/
#include "precomp.hpp"
namespace cv
{
using std::vector;
Feature2D::~Feature2D() {}
/*
* Detect keypoints in an image.
* image The image.
* keypoints The detected keypoints.
* mask Mask specifying where to look for keypoints (optional). Must be a char
* matrix with non-zero values in the region of interest.
*/
void Feature2D::detect( InputArray image,
std::vector<KeyPoint>& keypoints,
InputArray mask )
{
CV_INSTRUMENT_REGION();
if( image.empty() )
{
keypoints.clear();
return;
}
detectAndCompute(image, mask, keypoints, noArray(), false);
}
void Feature2D::detect( InputArrayOfArrays images,
std::vector<std::vector<KeyPoint> >& keypoints,
InputArrayOfArrays masks )
{
CV_INSTRUMENT_REGION();
int nimages = (int)images.total();
if (!masks.empty())
{
CV_Assert(masks.total() == (size_t)nimages);
}
keypoints.resize(nimages);
if (images.isMatVector())
{
for (int i = 0; i < nimages; i++)
{
detect(images.getMat(i), keypoints[i], masks.empty() ? noArray() : masks.getMat(i));
}
}
else
{
// assume UMats
for (int i = 0; i < nimages; i++)
{
detect(images.getUMat(i), keypoints[i], masks.empty() ? noArray() : masks.getUMat(i));
}
}
}
/*
* Compute the descriptors for a set of keypoints in an image.
* image The image.
* keypoints The input keypoints. Keypoints for which a descriptor cannot be computed are removed.
* descriptors Copmputed descriptors. Row i is the descriptor for keypoint i.
*/
void Feature2D::compute( InputArray image,
std::vector<KeyPoint>& keypoints,
OutputArray descriptors )
{
CV_INSTRUMENT_REGION();
if( image.empty() )
{
descriptors.release();
return;
}
detectAndCompute(image, noArray(), keypoints, descriptors, true);
}
void Feature2D::compute( InputArrayOfArrays images,
std::vector<std::vector<KeyPoint> >& keypoints,
OutputArrayOfArrays descriptors )
{
CV_INSTRUMENT_REGION();
if( !descriptors.needed() )
return;
int nimages = (int)images.total();
CV_Assert( keypoints.size() == (size_t)nimages );
// resize descriptors to appropriate size and compute
if (descriptors.isMatVector())
{
vector<Mat>& vec = *(vector<Mat>*)descriptors.getObj();
vec.resize(nimages);
for (int i = 0; i < nimages; i++)
{
compute(images.getMat(i), keypoints[i], vec[i]);
}
}
else if (descriptors.isUMatVector())
{
vector<UMat>& vec = *(vector<UMat>*)descriptors.getObj();
vec.resize(nimages);
for (int i = 0; i < nimages; i++)
{
compute(images.getUMat(i), keypoints[i], vec[i]);
}
}
else
{
CV_Error(Error::StsBadArg, "descriptors must be vector<Mat> or vector<UMat>");
}
}
/* Detects keypoints and computes the descriptors */
void Feature2D::detectAndCompute( InputArray, InputArray,
std::vector<KeyPoint>&,
OutputArray,
bool )
{
CV_INSTRUMENT_REGION();
CV_Error(Error::StsNotImplemented, "");
}
void Feature2D::write( const String& fileName ) const
{
FileStorage fs(fileName, FileStorage::WRITE);
write(fs);
}
void Feature2D::read( const String& fileName )
{
FileStorage fs(fileName, FileStorage::READ);
read(fs.root());
}
void Feature2D::write( FileStorage&) const
{
}
void Feature2D::read( const FileNode&)
{
}
int Feature2D::descriptorSize() const
{
return 0;
}
int Feature2D::descriptorType() const
{
return CV_32F;
}
int Feature2D::defaultNorm() const
{
int tp = descriptorType();
return tp == CV_8U ? NORM_HAMMING : NORM_L2;
}
// Return true if detector object is empty
bool Feature2D::empty() const
{
return true;
}
String Feature2D::getDefaultName() const
{
return "Feature2D";
}
}
+185
View File
@@ -0,0 +1,185 @@
/*M///////////////////////////////////////////////////////////////////////////////////////
//
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
// By downloading, copying, installing or using the software you agree to this license.
// If you do not agree to this license, do not download, install,
// copy or use the software.
//
//
// Intel License Agreement
// For Open Source Computer Vision Library
//
// Copyright (C) 2000, Intel Corporation, all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistribution's of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistribution's in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// * The name of Intel Corporation may not be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// This software is provided by the copyright holders and contributors "as is" and
// any express or implied warranties, including, but not limited to, the implied
// warranties of merchantability and fitness for a particular purpose are disclaimed.
// In no event shall the Intel Corporation or contributors be liable for any direct,
// indirect, incidental, special, exemplary, or consequential damages
// (including, but not limited to, procurement of substitute goods or services;
// loss of use, data, or profits; or business interruption) however caused
// and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of
// the use of this software, even if advised of the possibility of such damage.
//
//M*/
#include "precomp.hpp"
namespace cv
{
class GFTTDetector_Impl CV_FINAL : public GFTTDetector
{
public:
GFTTDetector_Impl( int _nfeatures, double _qualityLevel,
double _minDistance, int _blockSize, int _gradientSize,
bool _useHarrisDetector, double _k )
: nfeatures(_nfeatures), qualityLevel(_qualityLevel), minDistance(_minDistance),
blockSize(_blockSize), gradSize(_gradientSize), useHarrisDetector(_useHarrisDetector), k(_k)
{
}
void read( const FileNode& fn) CV_OVERRIDE
{
// if node is empty, keep previous value
if (!fn["nfeatures"].empty())
fn["nfeatures"] >> nfeatures;
if (!fn["qualityLevel"].empty())
fn["qualityLevel"] >> qualityLevel;
if (!fn["minDistance"].empty())
fn["minDistance"] >> minDistance;
if (!fn["blockSize"].empty())
fn["blockSize"] >> blockSize;
if (!fn["gradSize"].empty())
fn["gradSize"] >> gradSize;
if (!fn["useHarrisDetector"].empty())
fn["useHarrisDetector"] >> useHarrisDetector;
if (!fn["k"].empty())
fn["k"] >> k;
}
void write( FileStorage& fs) const CV_OVERRIDE
{
if(fs.isOpened())
{
fs << "name" << getDefaultName();
fs << "nfeatures" << nfeatures;
fs << "qualityLevel" << qualityLevel;
fs << "minDistance" << minDistance;
fs << "blockSize" << blockSize;
fs << "gradSize" << gradSize;
fs << "useHarrisDetector" << useHarrisDetector;
fs << "k" << k;
}
}
void setMaxFeatures(int maxFeatures) CV_OVERRIDE { nfeatures = maxFeatures; }
int getMaxFeatures() const CV_OVERRIDE { return nfeatures; }
void setQualityLevel(double qlevel) CV_OVERRIDE { qualityLevel = qlevel; }
double getQualityLevel() const CV_OVERRIDE { return qualityLevel; }
void setMinDistance(double minDistance_) CV_OVERRIDE { minDistance = minDistance_; }
double getMinDistance() const CV_OVERRIDE { return minDistance; }
void setBlockSize(int blockSize_) CV_OVERRIDE { blockSize = blockSize_; }
int getBlockSize() const CV_OVERRIDE { return blockSize; }
void setGradientSize(int gradientSize_) CV_OVERRIDE { gradSize = gradientSize_; }
int getGradientSize() CV_OVERRIDE { return gradSize; }
void setHarrisDetector(bool val) CV_OVERRIDE { useHarrisDetector = val; }
bool getHarrisDetector() const CV_OVERRIDE { return useHarrisDetector; }
void setK(double k_) CV_OVERRIDE { k = k_; }
double getK() const CV_OVERRIDE { return k; }
void detect( InputArray _image, std::vector<KeyPoint>& keypoints, InputArray _mask ) CV_OVERRIDE
{
CV_INSTRUMENT_REGION();
if(_image.empty())
{
keypoints.clear();
return;
}
std::vector<Point2f> corners;
std::vector<float> cornersQuality;
if (_image.isUMat())
{
UMat ugrayImage;
if( _image.type() != CV_8U )
cvtColor( _image, ugrayImage, COLOR_BGR2GRAY );
else
ugrayImage = _image.getUMat();
goodFeaturesToTrack( ugrayImage, corners, nfeatures, qualityLevel, minDistance, _mask,
cornersQuality, blockSize, gradSize, useHarrisDetector, k );
}
else
{
Mat image = _image.getMat(), grayImage = image;
if( image.type() != CV_8U )
cvtColor( image, grayImage, COLOR_BGR2GRAY );
goodFeaturesToTrack( grayImage, corners, nfeatures, qualityLevel, minDistance, _mask,
cornersQuality, blockSize, gradSize, useHarrisDetector, k );
}
CV_Assert(corners.size() == cornersQuality.size());
keypoints.resize(corners.size());
for (size_t i = 0; i < corners.size(); i++)
keypoints[i] = KeyPoint(corners[i], (float)blockSize, -1, cornersQuality[i]);
}
int nfeatures;
double qualityLevel;
double minDistance;
int blockSize;
int gradSize;
bool useHarrisDetector;
double k;
};
Ptr<GFTTDetector> GFTTDetector::create( int _nfeatures, double _qualityLevel,
double _minDistance, int _blockSize, int _gradientSize,
bool _useHarrisDetector, double _k )
{
return makePtr<GFTTDetector_Impl>(_nfeatures, _qualityLevel,
_minDistance, _blockSize, _gradientSize, _useHarrisDetector, _k);
}
Ptr<GFTTDetector> GFTTDetector::create( int _nfeatures, double _qualityLevel,
double _minDistance, int _blockSize,
bool _useHarrisDetector, double _k )
{
return makePtr<GFTTDetector_Impl>(_nfeatures, _qualityLevel,
_minDistance, _blockSize, 3, _useHarrisDetector, _k);
}
String GFTTDetector::getDefaultName() const
{
return (Feature2D::getDefaultName() + ".GFTTDetector");
}
}
+161
View File
@@ -0,0 +1,161 @@
/*M///////////////////////////////////////////////////////////////////////////////////////
//
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
// By downloading, copying, installing or using the software you agree to this license.
// If you do not agree to this license, do not download, install,
// copy or use the software.
//
//
// License Agreement
// For Open Source Computer Vision Library
//
// Copyright (C) 2017, Intel Corporation, all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistribution's of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistribution's in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// * The name of the copyright holders may not be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// This software is provided by the copyright holders and contributors "as is" and
// any express or implied warranties, including, but not limited to, the implied
// warranties of merchantability and fitness for a particular purpose are disclaimed.
// In no event shall the Intel Corporation or contributors be liable for any direct,
// indirect, incidental, special, exemplary, or consequential damages
// (including, but not limited to, procurement of substitute goods or services;
// loss of use, data, or profits; or business interruption) however caused
// and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of
// the use of this software, even if advised of the possibility of such damage.
//
//M*/
#ifndef OPENCV_FEATURES2D_HAL_REPLACEMENT_HPP
#define OPENCV_FEATURES2D_HAL_REPLACEMENT_HPP
#include "opencv2/core/hal/interface.h"
#if defined(__clang__) // clang or MSVC clang
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-parameter"
#elif defined(_MSC_VER)
#pragma warning(push)
#pragma warning(disable : 4100)
#elif defined(__GNUC__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
#endif
//! @addtogroup features2d_hal_interface
//! @note Define your functions to override default implementations:
//! @code
//! #undef hal_add8u
//! #define hal_add8u my_add8u
//! @endcode
//! @{
/**
@brief Detects corners using the FAST algorithm, returns mask.
@param src_data Source image data
@param src_step Source image step
@param dst_data Destination mask data
@param dst_step Destination mask step
@param width Source image width
@param height Source image height
@param type FAST type
*/
inline int hal_ni_FAST_dense(const uchar* src_data, size_t src_step, uchar* dst_data, size_t dst_step, int width, int height, cv::FastFeatureDetector::DetectorType type) { return CV_HAL_ERROR_NOT_IMPLEMENTED; }
//! @cond IGNORED
#define cv_hal_FAST_dense hal_ni_FAST_dense
//! @endcond
/**
@brief Non-maximum suppression for FAST_9_16.
@param src_data,src_step Source mask
@param dst_data,dst_step Destination mask after NMS
@param width,height Source mask dimensions
*/
inline int hal_ni_FAST_NMS(const uchar* src_data, size_t src_step, uchar* dst_data, size_t dst_step, int width, int height) { return CV_HAL_ERROR_NOT_IMPLEMENTED; }
//! @cond IGNORED
#define cv_hal_FAST_NMS hal_ni_FAST_NMS
//! @endcond
/**
@brief Detects corners using the FAST algorithm.
@param src_data Source image data
@param src_step Source image step
@param width Source image width
@param height Source image height
@param keypoints_data Pointer to keypoints
@param keypoints_count Count of keypoints
@param threshold Threshold for keypoint
@param nonmax_suppression Indicates if make nonmaxima suppression or not.
@param type FAST type
*/
inline int hal_ni_FAST(const uchar* src_data, size_t src_step, int width, int height, uchar* keypoints_data, size_t* keypoints_count, int threshold, bool nonmax_suppression, int /*cv::FastFeatureDetector::DetectorType*/ type) { return CV_HAL_ERROR_NOT_IMPLEMENTED; }
/**
@brief Detects corners using the FAST algorithm.
@param src_data Source image data
@param src_step Source image step
@param width Source image width
@param height Source image height
@param keypoints_data Pointer to keypoints
@param keypoints_count Count of keypoints
@param threshold Threshold for keypoint
@param nonmax_suppression Indicates if make nonmaxima suppression or not.
@param type FAST type
@param realloc_func function for reallocation
*/
inline int hal_ni_FASTv2(const uchar* src_data, size_t src_step, int width, int height, void** keypoints_data, size_t* keypoints_count, int threshold, bool nonmax_suppression, int /*cv::FastFeatureDetector::DetectorType*/ type, void* (*realloc_func)(void*, size_t)) { return CV_HAL_ERROR_NOT_IMPLEMENTED; }
//! @cond IGNORED
#define cv_hal_FAST hal_ni_FAST
#define cv_hal_FASTv2 hal_ni_FASTv2
//! @endcond
//! @}
#if defined(__clang__)
#pragma clang diagnostic pop
#elif defined(_MSC_VER)
#pragma warning(pop)
#elif defined(__GNUC__)
#pragma GCC diagnostic pop
#endif
#include "custom_hal.hpp"
//! @cond IGNORED
#define CALL_HAL_RET(name, fun, retval, ...) \
int res = __CV_EXPAND(fun(__VA_ARGS__, &retval)); \
if (res == CV_HAL_ERROR_OK) \
return retval; \
else if (res != CV_HAL_ERROR_NOT_IMPLEMENTED) \
CV_Error_(cv::Error::StsInternal, \
("HAL implementation " CVAUX_STR(name) " ==> " CVAUX_STR(fun) " returned %d (0x%08x)", res, res));
#define CALL_HAL(name, fun, ...) \
{ \
int res = __CV_EXPAND(fun(__VA_ARGS__)); \
if (res == CV_HAL_ERROR_OK) \
return; \
else if (res != CV_HAL_ERROR_NOT_IMPLEMENTED) \
CV_Error_(cv::Error::StsInternal, \
("HAL implementation " CVAUX_STR(name) " ==> " CVAUX_STR(fun) " returned %d (0x%08x)", res, res)); \
}
//! @endcond
#endif
+273
View File
@@ -0,0 +1,273 @@
/*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
{
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(KAZE::DiffusivityType diff_) CV_OVERRIDE{ diffusivity = diff_; }
KAZE::DiffusivityType 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();
#ifdef HAVE_OPENCL
bool use_opencl = ocl::useOpenCL() && upright
&& !ocl::Device::getDefault().hostUnifiedMemory();
if (use_opencl)
{
UMat img_gray = image.getUMat();
if (img_gray.channels() > 1)
{
UMat tmp;
cvtColor(img_gray, tmp, COLOR_BGR2GRAY);
img_gray = tmp;
}
UMat img1_32;
int depth = img_gray.depth();
if ( depth == CV_32F )
img1_32 = img_gray;
else if ( depth == CV_8U )
img_gray.convertTo(img1_32, CV_32F, 1.0 / 255.0, 0);
else if ( depth == CV_16U )
img_gray.convertTo(img1_32, CV_32F, 1.0 / 65535.0, 0);
CV_Assert( ! img1_32.empty() );
KAZEOptions options;
options.img_width = img1_32.cols;
options.img_height = img1_32.rows;
options.extended = extended;
options.upright = upright;
options.dthreshold = threshold;
options.omax = octaves;
options.nsublevels = sublevels;
options.diffusivity = diffusivity;
KAZEFeatures impl(options);
UTPyramid uPyr;
impl.Create_Nonlinear_Scale_Space_UMat(img1_32, uPyr);
if (!useProvidedKeypoints)
{
impl.Feature_Detection_UMat(uPyr, keypoints);
}
if (!mask.empty())
{
cv::KeyPointsFilter::runByPixelsMask(keypoints, mask.getMat());
}
if( descriptors.needed() )
{
impl.Compute_Descriptors_UMat(keypoints, descriptors, uPyr);
CV_Assert((!descriptors.empty() || descriptors.cols() == descriptorSize()));
CV_Assert((!descriptors.empty() || (descriptors.type() == descriptorType())));
}
return;
}
#endif
// CPU path
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;
KAZE::DiffusivityType 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");
}
}
+65
View File
@@ -0,0 +1,65 @@
/**
* @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
{
/* ************************************************************************* */
/// 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
+121
View File
@@ -0,0 +1,121 @@
/**
* @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
{
/// 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);
public:
void GetEvolutionPyramid(UMatPyramid& uPyr);
void Create_Nonlinear_Scale_Space_UMat(InputArray img, UMatPyramid& uPyr);
void Feature_Detection_UMat(UMatPyramid& uPyr, std::vector<cv::KeyPoint>& kpts);
void Compute_Descriptors_UMat(std::vector<cv::KeyPoint>& kpts, OutputArray desc, UMatPyramid& uPyr);
/// 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
+56
View File
@@ -0,0 +1,56 @@
/**
* @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/features2d.hpp>
namespace cv
{
//*************************************************************************************
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,70 @@
/**
* @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
{
/* ************************************************************************* */
// KAZE Class Declaration
class KAZEFeatures
{
private:
/// Parameters of the Nonlinear diffusion class
KAZEOptions options_; ///< Configuration options for KAZE
TPyramid 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 TPyramid& evolution_, const KAZEOptions& options);
#ifdef HAVE_OPENCL
void Create_Nonlinear_Scale_Space_UMat(InputArray img, UTPyramid& uPyr);
void Feature_Detection_UMat(UTPyramid& uPyr, std::vector<cv::KeyPoint>& kpts);
void Compute_Descriptors_UMat(std::vector<cv::KeyPoint>& kpts, OutputArray desc, UTPyramid& uPyr);
#endif
/// Feature Detection Methods
void Compute_KContrast(const cv::Mat& img, const float& kper);
void Compute_Multiscale_Derivatives(void);
void Compute_Detector_Response(void);
void Determinant_Hessian(std::vector<cv::KeyPoint>& kpts);
void Do_Subpixel_Refinement(std::vector<cv::KeyPoint>& kpts);
};
}
#endif
+5
View File
@@ -0,0 +1,5 @@
This folder contains both KAZE and AKAZE sources.
For license details, please refer to the following files:
- KAZE: LICENSE.KAZE
- AKAZE: LICENSE.AKAZE
+26
View File
@@ -0,0 +1,26 @@
Copyright (c) 2014, Pablo Fernandez Alcantarilla, Jesus Nuevo
All Rights Reserved
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the copyright holders nor the names of its contributors
may be used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+26
View File
@@ -0,0 +1,26 @@
Copyright (c) 2012, Pablo Fernández Alcantarilla
All Rights Reserved
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the copyright holders nor the names of its contributors
may be used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+47
View File
@@ -0,0 +1,47 @@
/**
* @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
template <typename MatType>
struct TEvolutionT
{
TEvolutionT() {
etime = 0.0f;
esigma = 0.0f;
octave = 0;
sublevel = 0;
sigma_size = 0;
}
MatType Lx, Ly; ///< First order spatial derivatives
MatType Lxx, Lxy, Lyy; ///< Second order spatial derivatives
MatType Lt; ///< Evolution image
MatType Lsmooth; ///< Smoothed image
MatType 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
};
typedef TEvolutionT<Mat> TEvolution;
typedef TEvolutionT<UMat> UTEvolution;
typedef std::vector<TEvolution> TPyramid;
typedef std::vector<UTEvolution> UTPyramid;
}
#endif
@@ -0,0 +1,218 @@
// Internal header: shared nonlinear diffusion utilities used by both
// AKAZEFeatures.cpp and KAZEFeatures.cpp.
// Not part of the public API.
#ifndef __OPENCV_FEATURES_2D_AKAZE_DIFFUSION_HPP__
#define __OPENCV_FEATURES_2D_AKAZE_DIFFUSION_HPP__
#include "../precomp.hpp"
#include "nldiffusion_functions.h"
#ifdef HAVE_OPENCL
#include "opencl_kernels_features2d.hpp"
#endif
namespace cv
{
static inline void
nld_step_scalar_one_lane(const Mat& Lt, const Mat& Lf, Mat& Lstep, float step_size, int row_begin, int row_end)
{
CV_INSTRUMENT_REGION();
Lstep.create(Lt.size(), Lt.type());
const int cols = Lt.cols - 2;
int row = row_begin;
const float *lt_a, *lt_c, *lt_b;
const float *lf_a, *lf_c, *lf_b;
float *dst;
float step_r = 0.f;
if (row == 0) {
lt_c = Lt.ptr<float>(0) + 1;
lf_c = Lf.ptr<float>(0) + 1;
lt_b = Lt.ptr<float>(1) + 1;
lf_b = Lf.ptr<float>(1) + 1;
dst = Lstep.ptr<float>(0);
dst[0] = 0.0f;
++dst;
for (int j = 0; j < cols; j++) {
step_r = (lf_c[j] + lf_c[j + 1])*(lt_c[j + 1] - lt_c[j]) +
(lf_c[j] + lf_c[j - 1])*(lt_c[j - 1] - lt_c[j]) +
(lf_c[j] + lf_b[j ])*(lt_b[j ] - lt_c[j]);
dst[j] = step_r * step_size;
}
dst[cols] = 0.0f;
++row;
}
int middle_end = std::min(Lt.rows - 1, row_end);
for (; row < middle_end; ++row)
{
lt_a = Lt.ptr<float>(row - 1);
lf_a = Lf.ptr<float>(row - 1);
lt_c = Lt.ptr<float>(row );
lf_c = Lf.ptr<float>(row );
lt_b = Lt.ptr<float>(row + 1);
lf_b = Lf.ptr<float>(row + 1);
dst = Lstep.ptr<float>(row);
step_r = (lf_c[0] + lf_c[1])*(lt_c[1] - lt_c[0]) +
(lf_c[0] + lf_b[0])*(lt_b[0] - lt_c[0]) +
(lf_c[0] + lf_a[0])*(lt_a[0] - lt_c[0]);
dst[0] = step_r * step_size;
lt_a++; lt_c++; lt_b++;
lf_a++; lf_c++; lf_b++;
dst++;
for (int j = 0; j < cols; j++)
{
step_r = (lf_c[j] + lf_c[j + 1])*(lt_c[j + 1] - lt_c[j]) +
(lf_c[j] + lf_c[j - 1])*(lt_c[j - 1] - lt_c[j]) +
(lf_c[j] + lf_b[j ])*(lt_b[j ] - lt_c[j]) +
(lf_c[j] + lf_a[j ])*(lt_a[j ] - lt_c[j]);
dst[j] = step_r * step_size;
}
step_r = (lf_c[cols] + lf_c[cols - 1])*(lt_c[cols - 1] - lt_c[cols]) +
(lf_c[cols] + lf_b[cols ])*(lt_b[cols ] - lt_c[cols]) +
(lf_c[cols] + lf_a[cols ])*(lt_a[cols ] - lt_c[cols]);
dst[cols] = step_r * step_size;
}
if (row_end == Lt.rows) {
lt_a = Lt.ptr<float>(row - 1) + 1;
lf_a = Lf.ptr<float>(row - 1) + 1;
lt_c = Lt.ptr<float>(row ) + 1;
lf_c = Lf.ptr<float>(row ) + 1;
dst = Lstep.ptr<float>(row);
dst[0] = 0.0f;
++dst;
for (int j = 0; j < cols; j++) {
step_r = (lf_c[j] + lf_c[j + 1])*(lt_c[j + 1] - lt_c[j]) +
(lf_c[j] + lf_c[j - 1])*(lt_c[j - 1] - lt_c[j]) +
(lf_c[j] + lf_a[j ])*(lt_a[j ] - lt_c[j]);
dst[j] = step_r * step_size;
}
dst[cols] = 0.0f;
}
}
class NonLinearScalarDiffusionStep : public ParallelLoopBody
{
public:
NonLinearScalarDiffusionStep(const Mat& Lt, const Mat& Lf, Mat& Lstep, float step_size)
: Lt_(&Lt), Lf_(&Lf), Lstep_(&Lstep), step_size_(step_size)
{}
void operator()(const Range& range) const CV_OVERRIDE
{
nld_step_scalar_one_lane(*Lt_, *Lf_, *Lstep_, step_size_, range.start, range.end);
}
private:
const Mat* Lt_;
const Mat* Lf_;
Mat* Lstep_;
float step_size_;
};
#ifdef HAVE_OPENCL
static inline bool
ocl_non_linear_diffusion_step(InputArray Lt_, InputArray Lf_, OutputArray Lstep_, float step_size)
{
if(!Lt_.isContinuous())
return false;
UMat Lt = Lt_.getUMat();
UMat Lf = Lf_.getUMat();
UMat Lstep = Lstep_.getUMat();
size_t globalSize[] = {(size_t)Lt.cols, (size_t)Lt.rows};
ocl::Kernel ker("AKAZE_nld_step_scalar", ocl::features2d::akaze_oclsrc);
if( ker.empty() )
return false;
return ker.args(
ocl::KernelArg::ReadOnly(Lt),
ocl::KernelArg::PtrReadOnly(Lf),
ocl::KernelArg::PtrWriteOnly(Lstep),
step_size).run(2, globalSize, 0, false);
}
static inline bool
ocl_pm_g2(InputArray Lx_, InputArray Ly_, OutputArray Lflow_, float kcontrast)
{
UMat Lx = Lx_.getUMat();
UMat Ly = Ly_.getUMat();
UMat Lflow = Lflow_.getUMat();
int total = Lx.rows * Lx.cols;
size_t globalSize[] = {(size_t)total};
ocl::Kernel ker("AKAZE_pm_g2", ocl::features2d::akaze_oclsrc);
if( ker.empty() )
return false;
return ker.args(
ocl::KernelArg::PtrReadOnly(Lx),
ocl::KernelArg::PtrReadOnly(Ly),
ocl::KernelArg::PtrWriteOnly(Lflow),
kcontrast, total).run(1, globalSize, 0, false);
}
#endif // HAVE_OPENCL
static inline void
non_linear_diffusion_step(InputArray Lt_, InputArray Lf_, OutputArray Lstep_, float step_size)
{
CV_INSTRUMENT_REGION();
Lstep_.create(Lt_.size(), Lt_.type());
CV_OCL_RUN(Lt_.isUMat() && Lf_.isUMat() && Lstep_.isUMat(),
ocl_non_linear_diffusion_step(Lt_, Lf_, Lstep_, step_size));
Mat Lt = Lt_.getMat();
Mat Lf = Lf_.getMat();
Mat Lstep = Lstep_.getMat();
parallel_for_(Range(0, Lt.rows), NonLinearScalarDiffusionStep(Lt, Lf, Lstep, step_size));
}
static inline void
compute_diffusivity(InputArray Lx, InputArray Ly, OutputArray Lflow, float kcontrast, KAZE::DiffusivityType diffusivity)
{
CV_INSTRUMENT_REGION();
Lflow.create(Lx.size(), Lx.type());
switch (diffusivity) {
case KAZE::DIFF_PM_G1:
pm_g1(Lx, Ly, Lflow, kcontrast);
break;
case KAZE::DIFF_PM_G2:
CV_OCL_RUN(Lx.isUMat() && Ly.isUMat() && Lflow.isUMat(), ocl_pm_g2(Lx, Ly, Lflow, kcontrast));
pm_g2(Lx, Ly, Lflow, kcontrast);
break;
case KAZE::DIFF_WEICKERT:
weickert_diffusivity(Lx, Ly, Lflow, kcontrast);
break;
case KAZE::DIFF_CHARBONNIER:
charbonnier_diffusivity(Lx, Ly, Lflow, kcontrast);
break;
default:
CV_Error_(Error::StsError, ("Diffusivity is not supported: %d", static_cast<int>(diffusivity)));
break;
}
}
} // namespace cv
#endif // __OPENCV_FEATURES_2D_AKAZE_DIFFUSION_HPP__
+192
View File
@@ -0,0 +1,192 @@
//=============================================================================
//
// fed.cpp
// Authors: Pablo F. Alcantarilla (1), Jesus Nuevo (2)
// Institutions: Georgia Institute of Technology (1)
// TrueVision Solutions (2)
// Date: 15/09/2013
// Email: pablofdezalc@gmail.com
//
// AKAZE Features Copyright 2013, Pablo F. Alcantarilla, Jesus Nuevo
// All Rights Reserved
// See LICENSE for the license information
//=============================================================================
/**
* @file fed.cpp
* @brief Functions for performing Fast Explicit Diffusion and building the
* nonlinear scale space
* @date Sep 15, 2013
* @author Pablo F. Alcantarilla, Jesus Nuevo
* @note This code is derived from FED/FJ library from Grewenig et al.,
* The FED/FJ library allows solving more advanced problems
* Please look at the following papers for more information about FED:
* [1] S. Grewenig, J. Weickert, C. Schroers, A. Bruhn. Cyclic Schemes for
* PDE-Based Image Analysis. Technical Report No. 327, Department of Mathematics,
* Saarland University, Saarbrücken, Germany, March 2013
* [2] S. Grewenig, J. Weickert, A. Bruhn. From box filtering to fast explicit diffusion.
* DAGM, 2010
*
*/
#include "../precomp.hpp"
#include "fed.h"
using namespace std;
//*************************************************************************************
//*************************************************************************************
/**
* @brief This function allocates an array of the least number of time steps such
* that a certain stopping time for the whole process can be obtained and fills
* it with the respective FED time step sizes for one cycle
* The function returns the number of time steps per cycle or 0 on failure
* @param T Desired process stopping time
* @param M Desired number of cycles
* @param tau_max Stability limit for the explicit scheme
* @param reordering Reordering flag
* @param tau The vector with the dynamic step sizes
*/
int fed_tau_by_process_time(const float& T, const int& M, const float& tau_max,
const bool& reordering, std::vector<float>& tau) {
// All cycles have the same fraction of the stopping time
return fed_tau_by_cycle_time(T/(float)M,tau_max,reordering,tau);
}
//*************************************************************************************
//*************************************************************************************
/**
* @brief This function allocates an array of the least number of time steps such
* that a certain stopping time for the whole process can be obtained and fills it
* it with the respective FED time step sizes for one cycle
* The function returns the number of time steps per cycle or 0 on failure
* @param t Desired cycle stopping time
* @param tau_max Stability limit for the explicit scheme
* @param reordering Reordering flag
* @param tau The vector with the dynamic step sizes
*/
int fed_tau_by_cycle_time(const float& t, const float& tau_max,
const bool& reordering, std::vector<float> &tau) {
int n = 0; // Number of time steps
float scale = 0.0; // Ratio of t we search to maximal t
// Compute necessary number of time steps
n = cvCeil(sqrtf(3.0f*t/tau_max+0.25f)-0.5f-1.0e-8f);
scale = 3.0f*t/(tau_max*(float)(n*(n+1)));
// Call internal FED time step creation routine
return fed_tau_internal(n,scale,tau_max,reordering,tau);
}
//*************************************************************************************
//*************************************************************************************
/**
* @brief This function allocates an array of time steps and fills it with FED
* time step sizes
* The function returns the number of time steps per cycle or 0 on failure
* @param n Number of internal steps
* @param scale Ratio of t we search to maximal t
* @param tau_max Stability limit for the explicit scheme
* @param reordering Reordering flag
* @param tau The vector with the dynamic step sizes
*/
int fed_tau_internal(const int& n, const float& scale, const float& tau_max,
const bool& reordering, std::vector<float> &tau) {
float c = 0.0, d = 0.0; // Time savers
vector<float> tauh; // Helper vector for unsorted taus
if (n <= 0) {
return 0;
}
// Allocate memory for the time step size
tau = vector<float>(n);
if (reordering) {
tauh = vector<float>(n);
}
// Compute time saver
c = 1.0f / (4.0f * (float)n + 2.0f);
d = scale * tau_max / 2.0f;
// Set up originally ordered tau vector
for (int k = 0; k < n; ++k) {
float h = cosf((float)CV_PI * (2.0f * (float)k + 1.0f) * c);
if (reordering) {
tauh[k] = d / (h * h);
}
else {
tau[k] = d / (h * h);
}
}
// Permute list of time steps according to chosen reordering function
int kappa = 0, prime = 0;
if (reordering == true) {
// Choose kappa cycle with k = n/2
// This is a heuristic. We can use Leja ordering instead!!
kappa = n / 2;
// Get modulus for permutation
prime = n + 1;
while (!fed_is_prime_internal(prime)) {
prime++;
}
// Perform permutation
for (int k = 0, l = 0; l < n; ++k, ++l) {
int index = 0;
while ((index = ((k+1)*kappa) % prime - 1) >= n) {
k++;
}
tau[l] = tauh[index];
}
}
return n;
}
//*************************************************************************************
//*************************************************************************************
/**
* @brief This function checks if a number is prime or not
* @param number Number to check if it is prime or not
* @return true if the number is prime
*/
bool fed_is_prime_internal(const int& number) {
bool is_prime = false;
if (number <= 1) {
return false;
}
else if (number == 1 || number == 2 || number == 3 || number == 5 || number == 7) {
return true;
}
else if ((number % 2) == 0 || (number % 3) == 0 || (number % 5) == 0 || (number % 7) == 0) {
return false;
}
else {
is_prime = true;
int upperLimit = (int)sqrt(1.0f + number);
int divisor = 11;
while (divisor <= upperLimit ) {
if (number % divisor == 0)
{
is_prime = false;
}
divisor +=2;
}
return is_prime;
}
}
+25
View File
@@ -0,0 +1,25 @@
#ifndef __OPENCV_FEATURES_2D_FED_H__
#define __OPENCV_FEATURES_2D_FED_H__
//******************************************************************************
//******************************************************************************
// Includes
#include <vector>
//*************************************************************************************
//*************************************************************************************
// Declaration of functions
int fed_tau_by_process_time(const float& T, const int& M, const float& tau_max,
const bool& reordering, std::vector<float>& tau);
int fed_tau_by_cycle_time(const float& t, const float& tau_max,
const bool& reordering, std::vector<float> &tau) ;
int fed_tau_internal(const int& n, const float& scale, const float& tau_max,
const bool& reordering, std::vector<float> &tau);
bool fed_is_prime_internal(const int& number);
//*************************************************************************************
//*************************************************************************************
#endif // __OPENCV_FEATURES_2D_FED_H__
@@ -0,0 +1,542 @@
//=============================================================================
//
// nldiffusion_functions.cpp
// Author: Pablo F. Alcantarilla
// Institution: University d'Auvergne
// Address: Clermont Ferrand, France
// Date: 27/12/2011
// Email: pablofdezalc@gmail.com
//
// KAZE Features Copyright 2012, Pablo F. Alcantarilla
// All Rights Reserved
// See LICENSE for the license information
//=============================================================================
/**
* @file nldiffusion_functions.cpp
* @brief Functions for non-linear diffusion applications:
* 2D Gaussian Derivatives
* Perona and Malik conductivity equations
* Perona and Malik evolution
* @date Dec 27, 2011
* @author Pablo F. Alcantarilla
*/
#include "../precomp.hpp"
#include "nldiffusion_functions.h"
#include <iostream>
// Namespaces
/* ************************************************************************* */
namespace cv
{
using namespace std;
/* ************************************************************************* */
/**
* @brief This function smoothes an image with a Gaussian kernel
* @param src Input image
* @param dst Output image
* @param ksize_x Kernel size in X-direction (horizontal)
* @param ksize_y Kernel size in Y-direction (vertical)
* @param sigma Kernel standard deviation
*/
void gaussian_2D_convolution(const cv::Mat& src, cv::Mat& dst, int ksize_x, int ksize_y, float sigma) {
int ksize_x_ = 0, ksize_y_ = 0;
// Compute an appropriate kernel size according to the specified sigma
if (sigma > ksize_x || sigma > ksize_y || ksize_x == 0 || ksize_y == 0) {
ksize_x_ = cvCeil(2.0f*(1.0f + (sigma - 0.8f) / (0.3f)));
ksize_y_ = ksize_x_;
}
// The kernel size must be and odd number
if ((ksize_x_ % 2) == 0) {
ksize_x_ += 1;
}
if ((ksize_y_ % 2) == 0) {
ksize_y_ += 1;
}
// Perform the Gaussian Smoothing with border replication
GaussianBlur(src, dst, Size(ksize_x_, ksize_y_), sigma, sigma, BORDER_REPLICATE);
}
/* ************************************************************************* */
/**
* @brief This function computes image derivatives with Scharr kernel
* @param src Input image
* @param dst Output image
* @param xorder Derivative order in X-direction (horizontal)
* @param yorder Derivative order in Y-direction (vertical)
* @note Scharr operator approximates better rotation invariance than
* other stencils such as Sobel. See Weickert and Scharr,
* A Scheme for Coherence-Enhancing Diffusion Filtering with Optimized Rotation Invariance,
* Journal of Visual Communication and Image Representation 2002
*/
void image_derivatives_scharr(const cv::Mat& src, cv::Mat& dst, int xorder, int yorder) {
Scharr(src, dst, CV_32F, xorder, yorder, 1.0, 0, BORDER_DEFAULT);
}
/* ************************************************************************* */
/**
* @brief This function computes the Perona and Malik conductivity coefficient g1
* g1 = exp(-|dL|^2/k^2)
* @param _Lx First order image derivative in X-direction (horizontal)
* @param _Ly First order image derivative in Y-direction (vertical)
* @param _dst Output image
* @param k Contrast factor parameter
*/
void pm_g1(InputArray _Lx, InputArray _Ly, OutputArray _dst, float k) {
_dst.create(_Lx.size(), _Lx.type());
Mat Lx = _Lx.getMat();
Mat Ly = _Ly.getMat();
Mat dst = _dst.getMat();
Size sz = Lx.size();
float inv_k = 1.0f / (k*k);
for (int y = 0; y < sz.height; y++) {
const float* Lx_row = Lx.ptr<float>(y);
const float* Ly_row = Ly.ptr<float>(y);
float* dst_row = dst.ptr<float>(y);
for (int x = 0; x < sz.width; x++) {
dst_row[x] = (-inv_k*(Lx_row[x]*Lx_row[x] + Ly_row[x]*Ly_row[x]));
}
}
exp(dst, dst);
}
/* ************************************************************************* */
/**
* @brief This function computes the Perona and Malik conductivity coefficient g2
* g2 = 1 / (1 + dL^2 / k^2)
* @param _Lx First order image derivative in X-direction (horizontal)
* @param _Ly First order image derivative in Y-direction (vertical)
* @param _dst Output image
* @param k Contrast factor parameter
*/
void pm_g2(InputArray _Lx, InputArray _Ly, OutputArray _dst, float k) {
CV_INSTRUMENT_REGION();
_dst.create(_Lx.size(), _Lx.type());
Mat Lx = _Lx.getMat();
Mat Ly = _Ly.getMat();
Mat dst = _dst.getMat();
Size sz = Lx.size();
dst.create(sz, Lx.type());
float k2inv = 1.0f / (k * k);
for(int y = 0; y < sz.height; y++) {
const float *Lx_row = Lx.ptr<float>(y);
const float *Ly_row = Ly.ptr<float>(y);
float* dst_row = dst.ptr<float>(y);
for(int x = 0; x < sz.width; x++) {
dst_row[x] = 1.0f / (1.0f + ((Lx_row[x] * Lx_row[x] + Ly_row[x] * Ly_row[x]) * k2inv));
}
}
}
/* ************************************************************************* */
/**
* @brief This function computes Weickert conductivity coefficient gw
* @param _Lx First order image derivative in X-direction (horizontal)
* @param _Ly First order image derivative in Y-direction (vertical)
* @param _dst Output image
* @param k Contrast factor parameter
* @note For more information check the following paper: J. Weickert
* Applications of nonlinear diffusion in image processing and computer vision,
* Proceedings of Algorithmy 2000
*/
void weickert_diffusivity(InputArray _Lx, InputArray _Ly, OutputArray _dst, float k) {
_dst.create(_Lx.size(), _Lx.type());
Mat Lx = _Lx.getMat();
Mat Ly = _Ly.getMat();
Mat dst = _dst.getMat();
Size sz = Lx.size();
float inv_k = 1.0f / (k*k);
for (int y = 0; y < sz.height; y++) {
const float* Lx_row = Lx.ptr<float>(y);
const float* Ly_row = Ly.ptr<float>(y);
float* dst_row = dst.ptr<float>(y);
for (int x = 0; x < sz.width; x++) {
float dL = inv_k*(Lx_row[x]*Lx_row[x] + Ly_row[x]*Ly_row[x]);
dst_row[x] = -3.315f/(dL*dL*dL*dL);
}
}
exp(dst, dst);
dst = 1.0 - dst;
}
/* ************************************************************************* */
/**
* @brief This function computes Charbonnier conductivity coefficient gc
* gc = 1 / sqrt(1 + dL^2 / k^2)
* @param _Lx First order image derivative in X-direction (horizontal)
* @param _Ly First order image derivative in Y-direction (vertical)
* @param _dst Output image
* @param k Contrast factor parameter
* @note For more information check the following paper: J. Weickert
* Applications of nonlinear diffusion in image processing and computer vision,
* Proceedings of Algorithmy 2000
*/
void charbonnier_diffusivity(InputArray _Lx, InputArray _Ly, OutputArray _dst, float k) {
_dst.create(_Lx.size(), _Lx.type());
Mat Lx = _Lx.getMat();
Mat Ly = _Ly.getMat();
Mat dst = _dst.getMat();
Size sz = Lx.size();
float inv_k = 1.0f / (k*k);
for (int y = 0; y < sz.height; y++) {
const float* Lx_row = Lx.ptr<float>(y);
const float* Ly_row = Ly.ptr<float>(y);
float* dst_row = dst.ptr<float>(y);
for (int x = 0; x < sz.width; x++) {
float den = sqrt(1.0f+inv_k*(Lx_row[x]*Lx_row[x] + Ly_row[x]*Ly_row[x]));
dst_row[x] = 1.0f / den;
}
}
}
/* ************************************************************************* */
/**
* @brief This function computes a good empirical value for the k contrast factor
* given an input image, the percentile (0-1), the gradient scale and the number of
* bins in the histogram
* @param img Input image
* @param perc Percentile of the image gradient histogram (0-1)
* @param gscale Scale for computing the image gradient histogram
* @param nbins Number of histogram bins
* @param ksize_x Kernel size in X-direction (horizontal) for the Gaussian smoothing kernel
* @param ksize_y Kernel size in Y-direction (vertical) for the Gaussian smoothing kernel
* @return k contrast factor
*/
float compute_k_percentile(const cv::Mat& img, float perc, float gscale, int nbins, int ksize_x, int ksize_y) {
CV_INSTRUMENT_REGION();
int nbin = 0, nelements = 0, nthreshold = 0, k = 0;
float kperc = 0.0, modg = 0.0;
float npoints = 0.0;
float hmax = 0.0;
// Create the array for the histogram
std::vector<int> hist(nbins, 0);
// Create the matrices
Mat gaussian = Mat::zeros(img.rows, img.cols, CV_32F);
Mat Lx = Mat::zeros(img.rows, img.cols, CV_32F);
Mat Ly = Mat::zeros(img.rows, img.cols, CV_32F);
// Perform the Gaussian convolution
gaussian_2D_convolution(img, gaussian, ksize_x, ksize_y, gscale);
// Compute the Gaussian derivatives Lx and Ly
Scharr(gaussian, Lx, CV_32F, 1, 0, 1, 0, cv::BORDER_DEFAULT);
Scharr(gaussian, Ly, CV_32F, 0, 1, 1, 0, cv::BORDER_DEFAULT);
// Skip the borders for computing the histogram
for (int i = 1; i < gaussian.rows - 1; i++) {
const float *lx = Lx.ptr<float>(i);
const float *ly = Ly.ptr<float>(i);
for (int j = 1; j < gaussian.cols - 1; j++) {
modg = lx[j]*lx[j] + ly[j]*ly[j];
// Get the maximum
if (modg > hmax) {
hmax = modg;
}
}
}
hmax = sqrt(hmax);
// Skip the borders for computing the histogram
for (int i = 1; i < gaussian.rows - 1; i++) {
const float *lx = Lx.ptr<float>(i);
const float *ly = Ly.ptr<float>(i);
for (int j = 1; j < gaussian.cols - 1; j++) {
modg = lx[j]*lx[j] + ly[j]*ly[j];
// Find the correspondent bin
if (modg != 0.0) {
nbin = (int)floor(nbins*(sqrt(modg) / hmax));
if (nbin == nbins) {
nbin--;
}
hist[nbin]++;
npoints++;
}
}
}
// Now find the perc of the histogram percentile
nthreshold = (int)(npoints*perc);
for (k = 0; nelements < nthreshold && k < nbins; k++) {
nelements = nelements + hist[k];
}
if (nelements < nthreshold) {
kperc = 0.03f;
}
else {
kperc = hmax*((float)(k) / (float)nbins);
}
return kperc;
}
/* ************************************************************************* */
/**
* @brief This function computes Scharr image derivatives
* @param src Input image
* @param dst Output image
* @param xorder Derivative order in X-direction (horizontal)
* @param yorder Derivative order in Y-direction (vertical)
* @param scale Scale factor for the derivative size
*/
void compute_scharr_derivatives(const cv::Mat& src, cv::Mat& dst, int xorder, int yorder, int scale) {
Mat kx, ky;
compute_derivative_kernels(kx, ky, xorder, yorder, scale);
sepFilter2D(src, dst, CV_32F, kx, ky);
}
/* ************************************************************************* */
/**
* @brief Compute derivative kernels for sizes different than 3
* @param _kx Horizontal kernel ues
* @param _ky Vertical kernel values
* @param dx Derivative order in X-direction (horizontal)
* @param dy Derivative order in Y-direction (vertical)
* @param scale Scale factor or derivative size
*/
void compute_derivative_kernels(cv::OutputArray _kx, cv::OutputArray _ky, int dx, int dy, int scale) {
CV_INSTRUMENT_REGION();
int ksize = 3 + 2 * (scale - 1);
// The standard Scharr kernel
if (scale == 1) {
getDerivKernels(_kx, _ky, dx, dy, 0, true, CV_32F);
return;
}
_kx.create(ksize, 1, CV_32F, -1, true);
_ky.create(ksize, 1, CV_32F, -1, true);
Mat kx = _kx.getMat();
Mat ky = _ky.getMat();
std::vector<float> kerI;
float w = 10.0f / 3.0f;
float norm = 1.0f / (2.0f*scale*(w + 2.0f));
for (int k = 0; k < 2; k++) {
Mat* kernel = k == 0 ? &kx : &ky;
int order = k == 0 ? dx : dy;
kerI.assign(ksize, 0.0f);
if (order == 0) {
kerI[0] = norm, kerI[ksize / 2] = w*norm, kerI[ksize - 1] = norm;
}
else if (order == 1) {
kerI[0] = -1, kerI[ksize / 2] = 0, kerI[ksize - 1] = 1;
}
Mat temp(kernel->rows, kernel->cols, CV_32F, &kerI[0]);
temp.copyTo(*kernel);
}
}
class Nld_Step_Scalar_Invoker : public cv::ParallelLoopBody
{
public:
Nld_Step_Scalar_Invoker(cv::Mat& Ld, const cv::Mat& c, cv::Mat& Lstep, float _stepsize)
: _Ld(&Ld)
, _c(&c)
, _Lstep(&Lstep)
, stepsize(_stepsize)
{
}
virtual ~Nld_Step_Scalar_Invoker()
{
}
void operator()(const cv::Range& range) const CV_OVERRIDE
{
cv::Mat& Ld = *_Ld;
const cv::Mat& c = *_c;
cv::Mat& Lstep = *_Lstep;
for (int i = range.start; i < range.end; i++)
{
const float *c_prev = c.ptr<float>(i - 1);
const float *c_curr = c.ptr<float>(i);
const float *c_next = c.ptr<float>(i + 1);
const float *ld_prev = Ld.ptr<float>(i - 1);
const float *ld_curr = Ld.ptr<float>(i);
const float *ld_next = Ld.ptr<float>(i + 1);
float *dst = Lstep.ptr<float>(i);
for (int j = 1; j < Lstep.cols - 1; j++)
{
float xpos = (c_curr[j] + c_curr[j+1])*(ld_curr[j+1] - ld_curr[j]);
float xneg = (c_curr[j-1] + c_curr[j]) *(ld_curr[j] - ld_curr[j-1]);
float ypos = (c_curr[j] + c_next[j]) *(ld_next[j] - ld_curr[j]);
float yneg = (c_prev[j] + c_curr[j]) *(ld_curr[j] - ld_prev[j]);
dst[j] = 0.5f*stepsize*(xpos - xneg + ypos - yneg);
}
}
}
private:
cv::Mat * _Ld;
const cv::Mat * _c;
cv::Mat * _Lstep;
float stepsize;
};
/* ************************************************************************* */
/**
* @brief This function performs a scalar non-linear diffusion step
* @param Ld Output image in the evolution
* @param c Conductivity image
* @param Lstep Previous image in the evolution
* @param stepsize The step size in time units
* @note Forward Euler Scheme 3x3 stencil
* The function c is a scalar value that depends on the gradient norm
* dL_by_ds = d(c dL_by_dx)_by_dx + d(c dL_by_dy)_by_dy
*/
void nld_step_scalar(cv::Mat& Ld, const cv::Mat& c, cv::Mat& Lstep, float stepsize) {
CV_INSTRUMENT_REGION();
cv::parallel_for_(cv::Range(1, Lstep.rows - 1), Nld_Step_Scalar_Invoker(Ld, c, Lstep, stepsize), (double)Ld.total()/(1 << 16));
float xneg, xpos, yneg, ypos;
float* dst = Lstep.ptr<float>(0);
const float* cprv = NULL;
const float* ccur = c.ptr<float>(0);
const float* cnxt = c.ptr<float>(1);
const float* ldprv = NULL;
const float* ldcur = Ld.ptr<float>(0);
const float* ldnxt = Ld.ptr<float>(1);
for (int j = 1; j < Lstep.cols - 1; j++) {
xpos = (ccur[j] + ccur[j+1]) * (ldcur[j+1] - ldcur[j]);
xneg = (ccur[j-1] + ccur[j]) * (ldcur[j] - ldcur[j-1]);
ypos = (ccur[j] + cnxt[j]) * (ldnxt[j] - ldcur[j]);
dst[j] = 0.5f*stepsize*(xpos - xneg + ypos);
}
dst = Lstep.ptr<float>(Lstep.rows - 1);
ccur = c.ptr<float>(Lstep.rows - 1);
cprv = c.ptr<float>(Lstep.rows - 2);
ldcur = Ld.ptr<float>(Lstep.rows - 1);
ldprv = Ld.ptr<float>(Lstep.rows - 2);
for (int j = 1; j < Lstep.cols - 1; j++) {
xpos = (ccur[j] + ccur[j+1]) * (ldcur[j+1] - ldcur[j]);
xneg = (ccur[j-1] + ccur[j]) * (ldcur[j] - ldcur[j-1]);
yneg = (cprv[j] + ccur[j]) * (ldcur[j] - ldprv[j]);
dst[j] = 0.5f*stepsize*(xpos - xneg - yneg);
}
ccur = c.ptr<float>(1);
ldcur = Ld.ptr<float>(1);
cprv = c.ptr<float>(0);
ldprv = Ld.ptr<float>(0);
int r0 = Lstep.cols - 1;
int r1 = Lstep.cols - 2;
for (int i = 1; i < Lstep.rows - 1; i++) {
cnxt = c.ptr<float>(i + 1);
ldnxt = Ld.ptr<float>(i + 1);
dst = Lstep.ptr<float>(i);
xpos = (ccur[0] + ccur[1]) * (ldcur[1] - ldcur[0]);
ypos = (ccur[0] + cnxt[0]) * (ldnxt[0] - ldcur[0]);
yneg = (cprv[0] + ccur[0]) * (ldcur[0] - ldprv[0]);
dst[0] = 0.5f*stepsize*(xpos + ypos - yneg);
xneg = (ccur[r1] + ccur[r0]) * (ldcur[r0] - ldcur[r1]);
ypos = (ccur[r0] + cnxt[r0]) * (ldnxt[r0] - ldcur[r0]);
yneg = (cprv[r0] + ccur[r0]) * (ldcur[r0] - ldprv[r0]);
dst[r0] = 0.5f*stepsize*(-xneg + ypos - yneg);
cprv = ccur;
ccur = cnxt;
ldprv = ldcur;
ldcur = ldnxt;
}
Ld += Lstep;
}
/* ************************************************************************* */
/**
* @brief This function downsamples the input image using OpenCV resize
* @param src Input image to be downsampled
* @param dst Output image with half of the resolution of the input image
*/
void halfsample_image(const cv::Mat& src, cv::Mat& dst) {
// Make sure the destination image is of the right size
CV_Assert(src.cols / 2 == dst.cols);
CV_Assert(src.rows / 2 == dst.rows);
resize(src, dst, dst.size(), 0, 0, cv::INTER_AREA);
}
/* ************************************************************************* */
/**
* @brief This function checks if a given pixel is a maximum in a local neighbourhood
* @param img Input image where we will perform the maximum search
* @param dsize Half size of the neighbourhood
* @param value Response value at (x,y) position
* @param row Image row coordinate
* @param col Image column coordinate
* @param same_img Flag to indicate if the image value at (x,y) is in the input image
* @return 1->is maximum, 0->otherwise
*/
bool check_maximum_neighbourhood(const cv::Mat& img, int dsize, float value, int row, int col, bool same_img) {
bool response = true;
for (int i = row - dsize; i <= row + dsize; i++) {
for (int j = col - dsize; j <= col + dsize; j++) {
if (i >= 0 && i < img.rows && j >= 0 && j < img.cols) {
if (same_img == true) {
if (i != row || j != col) {
if ((*(img.ptr<float>(i)+j)) > value) {
response = false;
return response;
}
}
}
else {
if ((*(img.ptr<float>(i)+j)) > value) {
response = false;
return response;
}
}
}
}
}
return response;
}
}
@@ -0,0 +1,47 @@
/**
* @file nldiffusion_functions.h
* @brief Functions for non-linear diffusion applications:
* 2D Gaussian Derivatives
* Perona and Malik conductivity equations
* Perona and Malik evolution
* @date Dec 27, 2011
* @author Pablo F. Alcantarilla
*/
#ifndef __OPENCV_FEATURES_2D_NLDIFFUSION_FUNCTIONS_H__
#define __OPENCV_FEATURES_2D_NLDIFFUSION_FUNCTIONS_H__
/* ************************************************************************* */
// Declaration of functions
namespace cv
{
// Gaussian 2D convolution
void gaussian_2D_convolution(const cv::Mat& src, cv::Mat& dst, int ksize_x, int ksize_y, float sigma);
// Diffusivity functions
void pm_g1(InputArray Lx, InputArray Ly, OutputArray dst, float k);
void pm_g2(InputArray Lx, InputArray Ly, OutputArray dst, float k);
void weickert_diffusivity(InputArray Lx, InputArray Ly, OutputArray dst, float k);
void charbonnier_diffusivity(InputArray Lx, InputArray Ly, OutputArray dst, float k);
float compute_k_percentile(const cv::Mat& img, float perc, float gscale, int nbins, int ksize_x, int ksize_y);
// Image derivatives
void compute_scharr_derivatives(const cv::Mat& src, cv::Mat& dst, int xorder, int yorder, int scale);
void compute_derivative_kernels(cv::OutputArray _kx, cv::OutputArray _ky, int dx, int dy, int scale);
void image_derivatives_scharr(const cv::Mat& src, cv::Mat& dst, int xorder, int yorder);
// Nonlinear diffusion filtering scalar step
void nld_step_scalar(cv::Mat& Ld, const cv::Mat& c, cv::Mat& Lstep, float stepsize);
// For non-maxima suppression
bool check_maximum_neighbourhood(const cv::Mat& img, int dsize, float value, int row, int col, bool same_img);
// Image downsampling
void halfsample_image(const cv::Mat& src, cv::Mat& dst);
}
#endif
+42
View File
@@ -0,0 +1,42 @@
#ifndef __OPENCV_FEATURES_2D_KAZE_UTILS_H__
#define __OPENCV_FEATURES_2D_KAZE_UTILS_H__
/* ************************************************************************* */
/**
* @brief This function computes the value of a 2D Gaussian function
* @param x X Position
* @param y Y Position
* @param sigma Standard Deviation
*/
inline float gaussian(float x, float y, float sigma) {
return expf(-(x*x + y*y) / (2.0f*sigma*sigma));
}
/* ************************************************************************* */
/**
* @brief This function checks descriptor limits
* @param x X Position
* @param y Y Position
* @param width Image width
* @param height Image height
*/
inline void checkDescriptorLimits(int &x, int &y, int width, int height) {
if (x < 0) {
x = 0;
}
if (y < 0) {
y = 0;
}
if (x > width - 1) {
x = width - 1;
}
if (y > height - 1) {
y = height - 1;
}
}
#endif
+293
View File
@@ -0,0 +1,293 @@
/*M///////////////////////////////////////////////////////////////////////////////////////
//
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
// By downloading, copying, installing or using the software you agree to this license.
// If you do not agree to this license, do not download, install,
// copy or use the software.
//
//
// License Agreement
// For Open Source Computer Vision Library
//
// Copyright (C) 2008, Willow Garage Inc., all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistribution's of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistribution's in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// * The name of Intel Corporation may not be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// This software is provided by the copyright holders and contributors "as is" and
// any express or implied warranties, including, but not limited to, the implied
// warranties of merchantability and fitness for a particular purpose are disclaimed.
// In no event shall the Intel Corporation or contributors be liable for any direct,
// indirect, incidental, special, exemplary, or consequential damages
// (including, but not limited to, procurement of substitute goods or services;
// loss of use, data, or profits; or business interruption) however caused
// and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of
// the use of this software, even if advised of the possibility of such damage.
//
//M*/
#include "precomp.hpp"
namespace cv
{
struct KeypointResponseGreaterThanOrEqualToThreshold
{
KeypointResponseGreaterThanOrEqualToThreshold(float _value) :
value(_value)
{
}
inline bool operator()(const KeyPoint& kpt) const
{
return kpt.response >= value;
}
float value;
};
struct KeypointResponseGreater
{
inline bool operator()(const KeyPoint& kp1, const KeyPoint& kp2) const
{
return kp1.response > kp2.response;
}
};
// takes keypoints and culls them by the response
void KeyPointsFilter::retainBest(std::vector<KeyPoint>& keypoints, int n_points)
{
//this is only necessary if the keypoints size is greater than the number of desired points.
if( n_points >= 0 && keypoints.size() > (size_t)n_points )
{
if (n_points==0)
{
keypoints.clear();
return;
}
//first use nth element to partition the keypoints into the best and worst.
std::nth_element(keypoints.begin(), keypoints.begin() + n_points - 1, keypoints.end(), KeypointResponseGreater());
//this is the boundary response, and in the case of FAST may be ambiguous
float ambiguous_response = keypoints[n_points - 1].response;
//use std::partition to grab all of the keypoints with the boundary response.
std::vector<KeyPoint>::const_iterator new_end =
std::partition(keypoints.begin() + n_points, keypoints.end(),
KeypointResponseGreaterThanOrEqualToThreshold(ambiguous_response));
//resize the keypoints, given this new end point. nth_element and partition reordered the points inplace
keypoints.resize(new_end - keypoints.begin());
}
}
struct RoiPredicate
{
RoiPredicate( const Rect& _r ) : r(_r)
{}
bool operator()( const KeyPoint& keyPt ) const
{
// workaround for https://github.com/opencv/opencv/issues/26016
// To keep its behaviour, keyPt.pt casts to Point_<int>.
return !r.contains( Point_<int>(keyPt.pt) );
}
Rect r;
};
void KeyPointsFilter::runByImageBorder( std::vector<KeyPoint>& keypoints, Size imageSize, int borderSize )
{
if( borderSize > 0)
{
if (imageSize.height <= borderSize * 2 || imageSize.width <= borderSize * 2)
keypoints.clear();
else
keypoints.erase( std::remove_if(keypoints.begin(), keypoints.end(),
RoiPredicate(Rect(Point(borderSize, borderSize),
Point(imageSize.width - borderSize, imageSize.height - borderSize)))),
keypoints.end() );
}
}
struct SizePredicate
{
SizePredicate( float _minSize, float _maxSize ) : minSize(_minSize), maxSize(_maxSize)
{}
bool operator()( const KeyPoint& keyPt ) const
{
float size = keyPt.size;
return (size < minSize) || (size > maxSize);
}
float minSize, maxSize;
};
void KeyPointsFilter::runByKeypointSize( std::vector<KeyPoint>& keypoints, float minSize, float maxSize )
{
CV_Assert( minSize >= 0 );
CV_Assert( maxSize >= 0);
CV_Assert( minSize <= maxSize );
keypoints.erase( std::remove_if(keypoints.begin(), keypoints.end(), SizePredicate(minSize, maxSize)),
keypoints.end() );
}
class MaskPredicate
{
public:
MaskPredicate( const Mat& _mask ) : mask(_mask) {}
bool operator() (const KeyPoint& key_pt) const
{
return mask.at<uchar>( (int)(key_pt.pt.y + 0.5f), (int)(key_pt.pt.x + 0.5f) ) == 0;
}
MaskPredicate& operator=(const MaskPredicate&) = delete;
// To avoid -Wdeprecated-copy warning, copy constructor is needed.
MaskPredicate(const MaskPredicate&) = default;
private:
const Mat mask;
};
void KeyPointsFilter::runByPixelsMask( std::vector<KeyPoint>& keypoints, const Mat& mask )
{
CV_INSTRUMENT_REGION();
if( mask.empty() )
return;
keypoints.erase(std::remove_if(keypoints.begin(), keypoints.end(), MaskPredicate(mask)), keypoints.end());
}
/*
* Remove objects from some image and a vector by mask for pixels of this image
*/
template <typename T>
void runByPixelsMask2(std::vector<KeyPoint> &keypoints, std::vector<T> &removeFrom, const Mat &mask)
{
if (mask.empty())
return;
MaskPredicate maskPredicate(mask);
removeFrom.erase(std::remove_if(removeFrom.begin(), removeFrom.end(),
[&](const T &x)
{
auto index = &x - &removeFrom.front();
return maskPredicate(keypoints[index]);
}),
removeFrom.end());
keypoints.erase(std::remove_if(keypoints.begin(), keypoints.end(), maskPredicate), keypoints.end());
}
void KeyPointsFilter::runByPixelsMask2VectorPoint(std::vector<KeyPoint> &keypoints, std::vector<std::vector<Point> > &removeFrom, const Mat &mask)
{
runByPixelsMask2(keypoints, removeFrom, mask);
}
struct KeyPoint_LessThan
{
KeyPoint_LessThan(const std::vector<KeyPoint>& _kp) : kp(&_kp) {}
bool operator()(int i, int j) const
{
const KeyPoint& kp1 = (*kp)[i];
const KeyPoint& kp2 = (*kp)[j];
if( kp1.pt.x != kp2.pt.x )
return kp1.pt.x < kp2.pt.x;
if( kp1.pt.y != kp2.pt.y )
return kp1.pt.y < kp2.pt.y;
if( kp1.size != kp2.size )
return kp1.size > kp2.size;
if( kp1.angle != kp2.angle )
return kp1.angle < kp2.angle;
if( kp1.response != kp2.response )
return kp1.response > kp2.response;
if( kp1.octave != kp2.octave )
return kp1.octave > kp2.octave;
if( kp1.class_id != kp2.class_id )
return kp1.class_id > kp2.class_id;
return i < j;
}
const std::vector<KeyPoint>* kp;
};
void KeyPointsFilter::removeDuplicated( std::vector<KeyPoint>& keypoints )
{
int i, j, n = (int)keypoints.size();
AutoBuffer<int> kpidx(n);
AutoBuffer<uchar> mask(n, (uchar)1);
for( i = 0; i < n; i++ )
kpidx[i] = i;
std::sort(kpidx.begin(), kpidx.end(), KeyPoint_LessThan(keypoints));
for( i = 1, j = 0; i < n; i++ )
{
KeyPoint& kp1 = keypoints[kpidx[i]];
KeyPoint& kp2 = keypoints[kpidx[j]];
if( kp1.pt.x != kp2.pt.x || kp1.pt.y != kp2.pt.y ||
kp1.size != kp2.size || kp1.angle != kp2.angle )
j = i;
else
mask[kpidx[i]] = 0;
}
for( i = j = 0; i < n; i++ )
{
if( mask[i] )
{
if( i != j )
keypoints[j] = keypoints[i];
j++;
}
}
keypoints.resize(j);
}
struct KeyPoint12_LessThan
{
bool operator()(const KeyPoint &kp1, const KeyPoint &kp2) const
{
if( kp1.pt.x != kp2.pt.x )
return kp1.pt.x < kp2.pt.x;
if( kp1.pt.y != kp2.pt.y )
return kp1.pt.y < kp2.pt.y;
if( kp1.size != kp2.size )
return kp1.size > kp2.size;
if( kp1.angle != kp2.angle )
return kp1.angle < kp2.angle;
if( kp1.response != kp2.response )
return kp1.response > kp2.response;
if( kp1.octave != kp2.octave )
return kp1.octave > kp2.octave;
return kp1.class_id > kp2.class_id;
}
};
void KeyPointsFilter::removeDuplicatedSorted( std::vector<KeyPoint>& keypoints )
{
int i, j, n = (int)keypoints.size();
if (n < 2) return;
std::sort(keypoints.begin(), keypoints.end(), KeyPoint12_LessThan());
for( i = 0, j = 1; j < n; ++j )
{
const KeyPoint& kp1 = keypoints[i];
const KeyPoint& kp2 = keypoints[j];
if( kp1.pt.x != kp2.pt.x || kp1.pt.y != kp2.pt.y ||
kp1.size != kp2.size || kp1.angle != kp2.angle ) {
keypoints[++i] = keypoints[j];
}
}
keypoints.resize(i + 1);
}
}
+52
View File
@@ -0,0 +1,52 @@
/*M///////////////////////////////////////////////////////////////////////////////////////
//
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
// By downloading, copying, installing or using the software you agree to this license.
// If you do not agree to this license, do not download, install,
// copy or use the software.
//
//
// License Agreement
// For Open Source Computer Vision Library
//
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
// Copyright (C) 2015, Itseez Inc., all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistribution's of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistribution's in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// * The name of the copyright holders may not be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// This software is provided by the copyright holders and contributors "as is" and
// any express or implied warranties, including, but not limited to, the implied
// warranties of merchantability and fitness for a particular purpose are disclaimed.
// In no event shall the Intel Corporation or contributors be liable for any direct,
// indirect, incidental, special, exemplary, or consequential damages
// (including, but not limited to, procurement of substitute goods or services;
// loss of use, data, or profits; or business interruption) however caused
// and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of
// the use of this software, even if advised of the possibility of such damage.
//
//M*/
//
// Library initialization file
//
#include "precomp.hpp"
IPP_INITIALIZER_AUTO
/* End of file. */
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+646
View File
@@ -0,0 +1,646 @@
// 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
/**
* @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
*/
__kernel void
AKAZE_pm_g2(__global const float* lx, __global const float* ly, __global float* dst,
float k, int size)
{
int i = get_global_id(0);
// OpenCV plays with dimensions so we need explicit check for this
if (!(i < size))
{
return;
}
const float k2inv = 1.0f / (k * k);
dst[i] = 1.0f / (1.0f + ((lx[i] * lx[i] + ly[i] * ly[i]) * k2inv));
}
__kernel void
AKAZE_nld_step_scalar(__global const float* lt, int lt_step, int lt_offset, int rows, int cols,
__global const float* lf, __global float* dst, float step_size)
{
/* The labeling scheme for this five star stencil:
[ a ]
[ -1 c +1 ]
[ b ]
*/
// column-first indexing
int i = get_global_id(1);
int j = get_global_id(0);
// OpenCV plays with dimensions so we need explicit check for this
if (!(i < rows && j < cols))
{
return;
}
// get row indexes
int a = (i - 1) * cols;
int c = (i ) * cols;
int b = (i + 1) * cols;
// compute stencil
float res = 0.0f;
if (i == 0) // first rows
{
if (j == 0 || j == (cols - 1))
{
res = 0.0f;
} else
{
res = (lf[c + j] + lf[c + j + 1])*(lt[c + j + 1] - lt[c + j]) +
(lf[c + j] + lf[c + j - 1])*(lt[c + j - 1] - lt[c + j]) +
(lf[c + j] + lf[b + j ])*(lt[b + j ] - lt[c + j]);
}
} else if (i == (rows - 1)) // last row
{
if (j == 0 || j == (cols - 1))
{
res = 0.0f;
} else
{
res = (lf[c + j] + lf[c + j + 1])*(lt[c + j + 1] - lt[c + j]) +
(lf[c + j] + lf[c + j - 1])*(lt[c + j - 1] - lt[c + j]) +
(lf[c + j] + lf[a + j ])*(lt[a + j ] - lt[c + j]);
}
} else // inner rows
{
if (j == 0) // first column
{
res = (lf[c + 0] + lf[c + 1])*(lt[c + 1] - lt[c + 0]) +
(lf[c + 0] + lf[b + 0])*(lt[b + 0] - lt[c + 0]) +
(lf[c + 0] + lf[a + 0])*(lt[a + 0] - lt[c + 0]);
} else if (j == (cols - 1)) // last column
{
res = (lf[c + j] + lf[c + j - 1])*(lt[c + j - 1] - lt[c + j]) +
(lf[c + j] + lf[b + j ])*(lt[b + j ] - lt[c + j]) +
(lf[c + j] + lf[a + j ])*(lt[a + j ] - lt[c + j]);
} else // inner stencil
{
res = (lf[c + j] + lf[c + j + 1])*(lt[c + j + 1] - lt[c + j]) +
(lf[c + j] + lf[c + j - 1])*(lt[c + j - 1] - lt[c + j]) +
(lf[c + j] + lf[b + j ])*(lt[b + j ] - lt[c + j]) +
(lf[c + j] + lf[a + j ])*(lt[a + j ] - lt[c + j]);
}
}
dst[c + j] = res * step_size;
}
/**
* @brief Compute determinant from hessians
* @details Compute Ldet by (Lxx.mul(Lyy) - Lxy.mul(Lxy)) * sigma
*
* @param lxx spatial derivates
* @param lxy spatial derivates
* @param lyy spatial derivates
* @param dst output determinant
* @param sigma determinant will be scaled by this sigma
*/
__kernel void
AKAZE_compute_determinant(__global const float* lxx, __global const float* lxy, __global const float* lyy,
__global float* dst, float sigma, int size)
{
int i = get_global_id(0);
// OpenCV plays with dimensions so we need explicit check for this
if (!(i < size))
{
return;
}
dst[i] = (lxx[i] * lyy[i] - lxy[i] * lxy[i]) * sigma;
}
/**
* @brief Find scale space extrema in 3x3 neighborhood
* @details Detects local maxima in Hessian response within 3x3 neighborhood
*
* @param ldet Hessian determinant response
* @param rows image height
* @param cols image width
* @param threshold detector response threshold
* @param border border to ignore
* @param keypoint_mask output binary mask of detected keypoints
*/
__kernel void
AKAZE_find_extrema_same_scale(__global const float* ldet, int rows, int cols,
float threshold, int border, __global uchar* keypoint_mask)
{
int x = get_global_id(0);
int y = get_global_id(1);
// Check bounds
if (x < border || x >= cols - border || y < border || y >= rows - border)
{
return;
}
int idx = y * cols + x;
float value = ldet[idx];
// Filter by threshold
if (value <= threshold)
{
keypoint_mask[idx] = 0;
return;
}
// 3x3 local maxima check
// Check horizontal neighbors
if (value <= ldet[idx - 1] || value <= ldet[idx + 1])
{
keypoint_mask[idx] = 0;
return;
}
// Check vertical neighbors
int prev_row = (y - 1) * cols + x;
int next_row = (y + 1) * cols + x;
if (value <= ldet[prev_row] || value <= ldet[next_row])
{
keypoint_mask[idx] = 0;
return;
}
// Check diagonal neighbors
if (value <= ldet[prev_row - 1] || value <= ldet[prev_row + 1] ||
value <= ldet[next_row - 1] || value <= ldet[next_row + 1])
{
keypoint_mask[idx] = 0;
return;
}
// This is a local maximum
keypoint_mask[idx] = 1;
}
/**
* @brief Cross-scale non-maximum suppression (lower scale filtering)
* @details For each keypoint in current level, project to lower level and suppress if weaker
*
* @param keypoints_current keypoint mask for current scale level
* @param keypoints_lower keypoint mask for lower scale level
* @param ldet_current Hessian response for current level
* @param ldet_lower Hessian response for lower level
* @param rows image height
* @param cols image width
* @param diff_ratio ratio to project from current to lower level
* @param search_radius search radius in lower level
*/
__kernel void
AKAZE_cross_scale_filter_lower(__global const uchar* keypoints_current,
__global uchar* keypoints_lower,
__global const float* ldet_current,
__global const float* ldet_lower,
int rows, int cols,
int lower_rows, int lower_cols,
int diff_ratio, int search_radius)
{
int x = get_global_id(0);
int y = get_global_id(1);
if (x >= cols || y >= rows)
return;
int idx = y * cols + x;
// Only process keypoints in current level
if (keypoints_current[idx] == 0)
return;
// Project to lower scale level
int p_x = x * diff_ratio;
int p_y = y * diff_ratio;
// Search for neighbor in lower level within radius
int radius_sq = search_radius * search_radius;
int found = 0;
int neighbor_idx = -1;
// Brute force search within radius
for (int dy = -search_radius; dy <= search_radius; dy++)
{
for (int dx = -search_radius; dx <= search_radius; dx++)
{
int nx = p_x + dx;
int ny = p_y + dy;
if (nx >= 0 && nx < lower_cols && ny >= 0 && ny < lower_rows)
{
int nidx = ny * lower_cols + nx;
if (keypoints_lower[nidx] == 1)
{
if (dx * dx + dy * dy <= radius_sq)
{
found = 1;
neighbor_idx = nidx;
break;
}
}
}
}
if (found) break;
}
// If neighbor found and current response is higher, suppress neighbor
if (found && ldet_current[idx] > ldet_lower[neighbor_idx])
{
keypoints_lower[neighbor_idx] = 0;
}
}
/**
* @brief Cross-scale non-maximum suppression (upper scale filtering)
* @details For each keypoint in current level, project to upper level and suppress if weaker
*
* @param keypoints_current keypoint mask for current scale level
* @param keypoints_upper keypoint mask for upper scale level
* @param ldet_current Hessian response for current level
* @param ldet_upper Hessian response for upper level
* @param rows image height
* @param cols image width
* @param diff_ratio ratio to project from current to upper level
* @param search_radius search radius in upper level
*/
__kernel void
AKAZE_cross_scale_filter_upper(__global const uchar* keypoints_current,
__global uchar* keypoints_upper,
__global const float* ldet_current,
__global const float* ldet_upper,
int rows, int cols,
int upper_rows, int upper_cols,
int diff_ratio, int search_radius)
{
int x = get_global_id(0);
int y = get_global_id(1);
if (x >= cols || y >= rows)
return;
int idx = y * cols + x;
// Only process keypoints in current level
if (keypoints_current[idx] == 0)
return;
// Project to upper scale level
int p_x = x / diff_ratio;
int p_y = y / diff_ratio;
// Search for neighbor in upper level within radius
int radius_sq = search_radius * search_radius;
int found = 0;
int neighbor_idx = -1;
// Brute force search within radius
for (int dy = -search_radius; dy <= search_radius; dy++)
{
for (int dx = -search_radius; dx <= search_radius; dx++)
{
int nx = p_x + dx;
int ny = p_y + dy;
if (nx >= 0 && nx < upper_cols && ny >= 0 && ny < upper_rows)
{
int nidx = ny * upper_cols + nx;
if (keypoints_upper[nidx] == 1)
{
if (dx * dx + dy * dy <= radius_sq)
{
found = 1;
neighbor_idx = nidx;
break;
}
}
}
}
if (found) break;
}
// If neighbor found and current response is higher, suppress neighbor
if (found && ldet_current[idx] > ldet_upper[neighbor_idx])
{
keypoints_upper[neighbor_idx] = 0;
}
}
/**
* @brief Combined subpixel refinement and orientation for single level
* @details Processes subpixel refinement and orientation together to eliminate intermediate transfers
*
* @param keypoints keypoint mask for this level
* @param ldet Hessian response for this level
* @param Lx gradient in x direction for this level
* @param Ly gradient in y direction for this level
* @param rows image height
* @param cols image width
* @param octave_ratio scale ratio for this level
* @param esigma evolution sigma for this level
* @param octave octave number for this level
* @param level evolution level index
* @param output_count atomic counter for number of refined keypoints
* @param output_x output x coordinates
* @param output_y output y coordinates
* @param output_size output keypoint sizes
* @param output_response output keypoint responses
* @param output_octave output octave numbers
* @param output_class_id output level indices
* @param output_angle output orientation angles
* @param max_output maximum number of keypoints that can be output
*/
__kernel void
AKAZE_subpixel_refinement_orientation(__global const uchar* keypoints,
__global const float* ldet,
__global const float* Lx,
__global const float* Ly,
int rows, int cols,
float octave_ratio, float esigma, int octave, int level,
__global int* output_count,
__global float* output_x,
__global float* output_y,
__global float* output_size,
__global float* output_response,
__global int* output_octave,
__global int* output_class_id,
__global float* output_angle,
int max_output)
{
int x = get_global_id(0);
int y = get_global_id(1);
if (x >= cols || y >= rows)
return;
int idx = y * cols + x;
// Only process keypoints
if (keypoints[idx] == 0)
return;
// Compute gradient for subpixel refinement
float Dx = 0.5f * (ldet[idx + 1] - ldet[idx - 1]);
float Dy = 0.5f * (ldet[idx + cols] - ldet[idx - cols]);
// Compute Hessian
float Dxx = ldet[idx + 1] + ldet[idx - 1] - 2.0f * ldet[idx];
float Dyy = ldet[idx + cols] + ldet[idx - cols] - 2.0f * ldet[idx];
float Dxy = 0.25f * (ldet[idx + cols + 1] + ldet[idx - cols - 1] -
ldet[idx - cols + 1] - ldet[idx + cols - 1]);
// Solve 2x2 linear system
float det = Dxx * Dyy - Dxy * Dxy;
if (fabs(det) < 1e-10f)
return;
float inv_det = 1.0f / det;
float dx = (-Dyy * Dx + Dxy * Dy) * inv_det;
float dy = (Dxy * Dx - Dxx * Dy) * inv_det;
if (fabs(dx) > 1.0f || fabs(dy) > 1.0f)
return;
int out_idx = atomic_inc(output_count);
if (out_idx >= max_output)
return;
float refined_x = x * octave_ratio + dx * octave_ratio + 0.5f * (octave_ratio - 1.0f);
float refined_y = y * octave_ratio + dy * octave_ratio + 0.5f * (octave_ratio - 1.0f);
output_x[out_idx] = refined_x;
output_y[out_idx] = refined_y;
output_size[out_idx] = esigma * 3.0f; // derivative_factor(1.5) * 2.0(diameter) = 3.0
output_response[out_idx] = ldet[idx];
output_octave[out_idx] = octave;
output_class_id[out_idx] = level;
// Compute orientation using gradient histogram
float scale_f = 0.5f * output_size[out_idx] / octave_ratio;
int scale = (int)(scale_f + 0.5f);
float x0_f = refined_x / octave_ratio;
int x0 = (int)(x0_f + (x0_f >= 0 ? 0.5f : -0.5f));
float y0_f = refined_y / octave_ratio;
int y0 = (int)(y0_f + (y0_f >= 0 ? 0.5f : -0.5f));
float histogram[36];
for (int b = 0; b < 36; b++)
histogram[b] = 0.0f;
const int radius = 6 * scale;
const int radius_sq = radius * radius;
for (int dy = -radius; dy <= radius; dy++) {
for (int dx = -radius; dx <= radius; dx++) {
if (dx * dx + dy * dy > radius_sq)
continue;
int sx = x0 + dx;
int sy = y0 + dy;
if (sx < 0 || sx >= cols || sy < 0 || sy >= rows)
continue;
int sidx = sy * cols + sx;
float gx = Lx[sidx];
float gy = Ly[sidx];
float magnitude = sqrt(gx * gx + gy * gy);
float angle = atan2(gy, gx);
if (angle < 0.0f)
angle += 2.0f * M_PI_F;
float dist = sqrt((float)(dx * dx + dy * dy));
float weight = exp(-dist * dist / (2.0f * scale * scale));
int bin = (int)(angle * 36 / (2.0f * M_PI_F));
bin = bin % 36;
histogram[bin] += magnitude * weight;
}
}
float max_hist = 0.0f;
int max_bin = 0;
for (int b = 0; b < 36; b++) {
if (histogram[b] > max_hist) {
max_hist = histogram[b];
max_bin = b;
}
}
if (max_hist == 0.0f)
{
output_angle[out_idx] = 0.0f;
return;
}
int prev_bin = (max_bin - 1 + 36) % 36;
int next_bin = (max_bin + 1) % 36;
float prev_val = histogram[prev_bin];
float curr_val = histogram[max_bin];
float next_val = histogram[next_bin];
float denom = prev_val - 2.0f * curr_val + next_val;
float delta = (fabs(denom) > 1e-10f) ? 0.5f * (prev_val - next_val) / denom : 0.0f;
float refined_angle = (max_bin + delta) * (2.0f * M_PI_F / 36);
output_angle[out_idx] = refined_angle * 180.0f / M_PI_F;
}
/**
* @brief Compute MLDB descriptor for a keypoint (single level, upright version)
* @details Samples binary comparisons from gradient responses matching CPU implementation
*
* @param keypoints_x keypoint x coordinates
* @param keypoints_y keypoint y coordinates
* @param keypoints_size keypoint sizes
* @param keypoints_angle keypoint orientations (unused in upright version)
* @param num_keypoints number of keypoints
* @param Lx gradient in x direction
* @param Ly gradient in y direction
* @param Lt flow response
* @param rows image height
* @param cols image width
* @param octave_ratio octave ratio for this level
* @param descriptor_size size of descriptor in bytes
* @param output_descriptors output descriptor matrix
*/
__kernel void
AKAZE_compute_mldb_descriptor_level(__global const float* keypoints_x,
__global const float* keypoints_y,
__global const float* keypoints_size,
__global const float* keypoints_angle,
int num_keypoints,
__global const float* Lx,
__global const float* Ly,
__global const float* Lt,
int rows, int cols, float octave_ratio,
int descriptor_size,
__global uchar* output_descriptors)
{
int i = get_global_id(0);
if (i >= num_keypoints)
return;
float kpt_x = keypoints_x[i];
float kpt_y = keypoints_y[i];
float kpt_size = keypoints_size[i];
// cvRound implementation: rounds to nearest, half away from zero
float scale_f = 0.5f * kpt_size / octave_ratio;
int scale = (int)(scale_f + (scale_f >= 0 ? 0.5f : -0.5f));
float xf = kpt_x / octave_ratio;
float yf = kpt_y / octave_ratio;
// Pattern size (default 10)
const int pattern_size = 10;
// Sample steps for 3 grids: 2x2, 3x3, 4x4
const int sample_step[3] = {
pattern_size,
(pattern_size * 2 + 2) / 3, // divUp(pattern_size * 2, 3)
pattern_size / 2
};
// Buffer for M-LDB descriptor values (max 16 cells * 3 channels)
float values[48];
__global uchar* desc = &output_descriptors[i * descriptor_size];
// Initialize descriptor to zero
for (int b = 0; b < descriptor_size; b++)
desc[b] = 0;
int dcount1 = 0;
// For the three grids (2x2, 3x3, 4x4)
for (int z = 0; z < 3; z++) {
int dcount2 = 0;
const int step = sample_step[z];
for (int i = -pattern_size; i < pattern_size; i += step) {
for (int j = -pattern_size; j < pattern_size; j += step) {
float di = 0.0f, dx = 0.0f, dy = 0.0f;
int nsamples = 0;
// Sample within each cell
for (int k = 0; k < step; k++) {
for (int l = 0; l < step; l++) {
// Get the coordinates of the sample point
const float sample_y = yf + (l + j) * scale;
const float sample_x = xf + (k + i) * scale;
// cvRound implementation for sampling coordinates
const int y1 = (int)(sample_y + (sample_y >= 0 ? 0.5f : -0.5f));
const int x1 = (int)(sample_x + (sample_x >= 0 ? 0.5f : -0.5f));
if (y1 < 0 || y1 >= rows || x1 < 0 || x1 >= cols)
continue; // Boundaries
const int idx = y1 * cols + x1;
const float ri = Lt[idx];
const float rx = Lx[idx];
const float ry = Ly[idx];
di += ri;
dx += rx;
dy += ry;
nsamples++;
}
}
if (nsamples > 0) {
const float nsamples_inv = 1.0f / nsamples;
di *= nsamples_inv;
dx *= nsamples_inv;
dy *= nsamples_inv;
}
// Store values (3 channels: Lt, Lx, Ly)
values[dcount2 * 3] = di;
values[dcount2 * 3 + 1] = dx;
values[dcount2 * 3 + 2] = dy;
dcount2++;
}
}
// Do binary comparison for this grid
const int num = (z + 2) * (z + 2);
const int chan = 3;
// Apply CV_TOGGLE_FLT to handle signed floats correctly
// This toggles the sign bit to allow correct integer comparison of floats
int* ivalues = (int*)values;
for (int i = 0; i < num * chan; i++) {
ivalues[i] = ivalues[i] ^ (ivalues[i] < 0 ? 0x7fffffff : 0);
}
// Match CPU comparison order: iterate Cell FIRST, then Channel
// This produces: [Cell0-Ch0, Cell0-Ch1, Cell0-Ch2, Cell1-Ch0, Cell1-Ch1, ...]
for (int i = 0; i < num; i++) {
for (int j = i + 1; j < num; j++) {
for (int pos = 0; pos < chan; pos++) {
int ival = ivalues[chan * i + pos];
if (ival > ivalues[chan * j + pos]) {
desc[dcount1 / 8] |= (1 << (dcount1 % 8));
}
dcount1++;
}
}
}
}
}
@@ -0,0 +1,694 @@
/*M///////////////////////////////////////////////////////////////////////////////////////
//
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
// By downloading, copying, installing or using the software you agree to this license.
// If you do not agree to this license, do not download, install,
// copy or use the software.
//
//
// License Agreement
// For Open Source Computer Vision Library
//
// Copyright (C) 2010-2012, Multicoreware, Inc., all rights reserved.
// Copyright (C) 2010-2012, Advanced Micro Devices, Inc., all rights reserved.
// Third party copyrights are property of their respective owners.
//
// @Authors
// Nathan, liujun@multicorewareinc.com
// Peng Xiao, pengxiao@outlook.com
// Baichuan Su, baichuan@multicorewareinc.com
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistribution's of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistribution's in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// * The name of the copyright holders may not be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// This software is provided by the copyright holders and contributors "as is" and
// any express or implied warranties, including, but not limited to, the implied
// warranties of merchantability and fitness for a particular purpose are disclaimed.
// In no event shall the Intel Corporation or contributors be liable for any direct,
// indirect, incidental, special, exemplary, or consequential damages
// (including, but not limited to, procurement of substitute goods or services;
// loss of use, data, or profits; or business interruption) however caused
// and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of
// the use of this software, even if advised of the possibility of such damage.
//
//M*/
#pragma OPENCL EXTENSION cl_khr_global_int32_base_atomics:enable
#define MAX_FLOAT 3.40282e+038f
#ifndef T
#define T float
#endif
#ifndef BLOCK_SIZE
#define BLOCK_SIZE 16
#endif
#ifndef MAX_DESC_LEN
#define MAX_DESC_LEN 64
#endif
#define BLOCK_SIZE_ODD (BLOCK_SIZE + 1)
#ifndef SHARED_MEM_SZ
# if (BLOCK_SIZE < MAX_DESC_LEN)
# define SHARED_MEM_SZ (kercn * (BLOCK_SIZE * MAX_DESC_LEN + BLOCK_SIZE * BLOCK_SIZE))
# else
# define SHARED_MEM_SZ (kercn * 2 * BLOCK_SIZE_ODD * BLOCK_SIZE)
# endif
#endif
#ifndef DIST_TYPE
#define DIST_TYPE 2
#endif
// dirty fix for non-template support
#if (DIST_TYPE == 2) // L1Dist
# ifdef T_FLOAT
typedef float result_type;
# if (8 == kercn)
typedef float8 value_type;
# define DIST(x, y) {value_type d = fabs((x) - (y)); result += d.s0 + d.s1 + d.s2 + d.s3 + d.s4 + d.s5 + d.s6 + d.s7;}
# elif (4 == kercn)
typedef float4 value_type;
# define DIST(x, y) {value_type d = fabs((x) - (y)); result += d.s0 + d.s1 + d.s2 + d.s3;}
# else
typedef float value_type;
# define DIST(x, y) result += fabs((x) - (y))
# endif
# else
typedef int result_type;
# if (8 == kercn)
typedef int8 value_type;
# define DIST(x, y) {value_type d = abs((x) - (y)); result += d.s0 + d.s1 + d.s2 + d.s3 + d.s4 + d.s5 + d.s6 + d.s7;}
# elif (4 == kercn)
typedef int4 value_type;
# define DIST(x, y) {value_type d = abs((x) - (y)); result += d.s0 + d.s1 + d.s2 + d.s3;}
# else
typedef int value_type;
# define DIST(x, y) result += abs((x) - (y))
# endif
# endif
# define DIST_RES(x) (x)
#elif (DIST_TYPE == 4) // L2Dist
typedef float result_type;
# if (8 == kercn)
typedef float8 value_type;
# define DIST(x, y) {value_type d = ((x) - (y)); result += dot(d.s0123, d.s0123) + dot(d.s4567, d.s4567);}
# elif (4 == kercn)
typedef float4 value_type;
# define DIST(x, y) {value_type d = ((x) - (y)); result += dot(d, d);}
# else
typedef float value_type;
# define DIST(x, y) {value_type d = ((x) - (y)); result = mad(d, d, result);}
# endif
# define DIST_RES(x) sqrt(x)
#elif (DIST_TYPE == 6) // Hamming
# if (8 == kercn)
typedef int8 value_type;
# elif (4 == kercn)
typedef int4 value_type;
# else
typedef int value_type;
# endif
typedef int result_type;
# define DIST(x, y) result += popcount( (x) ^ (y) )
# define DIST_RES(x) (x)
#endif
inline result_type reduce_block(
__local value_type *s_query,
__local value_type *s_train,
int lidx,
int lidy
)
{
result_type result = 0;
#pragma unroll
for (int j = 0 ; j < BLOCK_SIZE ; j++)
{
DIST(s_query[lidy * BLOCK_SIZE_ODD + j], s_train[j * BLOCK_SIZE_ODD + lidx]);
}
return DIST_RES(result);
}
inline result_type reduce_block_match(
__local value_type *s_query,
__local value_type *s_train,
int lidx,
int lidy
)
{
result_type result = 0;
#pragma unroll
for (int j = 0 ; j < BLOCK_SIZE ; j++)
{
DIST(s_query[lidy * BLOCK_SIZE_ODD + j], s_train[j * BLOCK_SIZE_ODD + lidx]);
}
return result;
}
inline result_type reduce_multi_block(
__local value_type *s_query,
__local value_type *s_train,
int block_index,
int lidx,
int lidy
)
{
result_type result = 0;
#pragma unroll
for (int j = 0 ; j < BLOCK_SIZE ; j++)
{
DIST(s_query[lidy * MAX_DESC_LEN + block_index * BLOCK_SIZE + j], s_train[j * BLOCK_SIZE + lidx]);
}
return result;
}
__kernel void BruteForceMatch_Match(
__global T *query,
__global T *train,
__global int *bestTrainIdx,
__global float *bestDistance,
int query_rows,
int query_cols,
int train_rows,
int train_cols,
int step
)
{
const int lidx = get_local_id(0);
const int lidy = get_local_id(1);
const int groupidx = get_group_id(0);
const int queryIdx = mad24(BLOCK_SIZE, groupidx, lidy);
const int queryOffset = min(queryIdx, query_rows - 1) * step;
__global TN *query_vec = (__global TN *)(query + queryOffset);
query_cols /= kercn;
__local float sharebuffer[SHARED_MEM_SZ];
__local value_type *s_query = (__local value_type *)sharebuffer;
#if 0 < MAX_DESC_LEN
__local value_type *s_train = (__local value_type *)sharebuffer + BLOCK_SIZE * MAX_DESC_LEN;
// load the query into local memory.
#pragma unroll
for (int i = 0; i < MAX_DESC_LEN / BLOCK_SIZE; i++)
{
const int loadx = mad24(BLOCK_SIZE, i, lidx);
s_query[mad24(MAX_DESC_LEN, lidy, loadx)] = loadx < query_cols ? query_vec[loadx] : 0;
}
#else
__local value_type *s_train = (__local value_type *)sharebuffer + BLOCK_SIZE_ODD * BLOCK_SIZE;
const int s_query_i = mad24(BLOCK_SIZE_ODD, lidy, lidx);
const int s_train_i = mad24(BLOCK_SIZE_ODD, lidx, lidy);
#endif
float myBestDistance = MAX_FLOAT;
int myBestTrainIdx = -1;
// loopUnrolledCached to find the best trainIdx and best distance.
for (int t = 0, endt = (train_rows + BLOCK_SIZE - 1) / BLOCK_SIZE; t < endt; t++)
{
result_type result = 0;
const int trainOffset = min(mad24(BLOCK_SIZE, t, lidy), train_rows - 1) * step;
__global TN *train_vec = (__global TN *)(train + trainOffset);
#if 0 < MAX_DESC_LEN
#pragma unroll
for (int i = 0; i < MAX_DESC_LEN / BLOCK_SIZE; i++)
{
//load a BLOCK_SIZE * BLOCK_SIZE block into local train.
const int loadx = mad24(BLOCK_SIZE, i, lidx);
s_train[mad24(BLOCK_SIZE, lidx, lidy)] = loadx < train_cols ? train_vec[loadx] : 0;
//synchronize to make sure each elem for reduceIteration in share memory is written already.
barrier(CLK_LOCAL_MEM_FENCE);
result += reduce_multi_block(s_query, s_train, i, lidx, lidy);
barrier(CLK_LOCAL_MEM_FENCE);
}
#else
for (int i = 0, endq = (query_cols + BLOCK_SIZE - 1) / BLOCK_SIZE; i < endq; i++)
{
const int loadx = mad24(i, BLOCK_SIZE, lidx);
//load query and train into local memory
if (loadx < query_cols)
{
s_query[s_query_i] = query_vec[loadx];
s_train[s_train_i] = train_vec[loadx];
}
else
{
s_query[s_query_i] = 0;
s_train[s_train_i] = 0;
}
barrier(CLK_LOCAL_MEM_FENCE);
result += reduce_block_match(s_query, s_train, lidx, lidy);
barrier(CLK_LOCAL_MEM_FENCE);
}
#endif
result = DIST_RES(result);
const int trainIdx = mad24(BLOCK_SIZE, t, lidx);
if (queryIdx < query_rows && trainIdx < train_rows && result < myBestDistance /*&& mask(queryIdx, trainIdx)*/)
{
myBestDistance = result;
myBestTrainIdx = trainIdx;
}
}
barrier(CLK_LOCAL_MEM_FENCE);
__local float *s_distance = (__local float *)sharebuffer;
__local int *s_trainIdx = (__local int *)(sharebuffer + BLOCK_SIZE_ODD * BLOCK_SIZE);
//findBestMatch
s_distance += lidy * BLOCK_SIZE_ODD;
s_trainIdx += lidy * BLOCK_SIZE_ODD;
s_distance[lidx] = myBestDistance;
s_trainIdx[lidx] = myBestTrainIdx;
barrier(CLK_LOCAL_MEM_FENCE);
//reduce -- now all reduce implement in each threads.
#pragma unroll
for (int k = 0 ; k < BLOCK_SIZE; k++)
{
if (myBestDistance > s_distance[k])
{
myBestDistance = s_distance[k];
myBestTrainIdx = s_trainIdx[k];
}
}
if (queryIdx < query_rows && lidx == 0)
{
bestTrainIdx[queryIdx] = myBestTrainIdx;
bestDistance[queryIdx] = myBestDistance;
}
}
//radius_match
__kernel void BruteForceMatch_RadiusMatch(
__global T *query,
__global T *train,
float maxDistance,
__global int *bestTrainIdx,
__global float *bestDistance,
__global int *nMatches,
int query_rows,
int query_cols,
int train_rows,
int train_cols,
int bestTrainIdx_cols,
int step,
int ostep
)
{
const int lidx = get_local_id(0);
const int lidy = get_local_id(1);
const int groupidx = get_group_id(0);
const int groupidy = get_group_id(1);
const int queryIdx = mad24(BLOCK_SIZE, groupidy, lidy);
const int queryOffset = min(queryIdx, query_rows - 1) * step;
__global TN *query_vec = (__global TN *)(query + queryOffset);
const int trainIdx = mad24(BLOCK_SIZE, groupidx, lidx);
const int trainOffset = min(mad24(BLOCK_SIZE, groupidx, lidy), train_rows - 1) * step;
__global TN *train_vec = (__global TN *)(train + trainOffset);
query_cols /= kercn;
__local float sharebuffer[SHARED_MEM_SZ];
__local value_type *s_query = (__local value_type *)sharebuffer;
__local value_type *s_train = (__local value_type *)sharebuffer + BLOCK_SIZE_ODD * BLOCK_SIZE;
result_type result = 0;
const int s_query_i = mad24(BLOCK_SIZE_ODD, lidy, lidx);
const int s_train_i = mad24(BLOCK_SIZE_ODD, lidx, lidy);
for (int i = 0 ; i < (query_cols + BLOCK_SIZE - 1) / BLOCK_SIZE ; ++i)
{
//load a BLOCK_SIZE * BLOCK_SIZE block into local train.
const int loadx = mad24(BLOCK_SIZE, i, lidx);
if (loadx < query_cols)
{
s_query[s_query_i] = query_vec[loadx];
s_train[s_train_i] = train_vec[loadx];
}
else
{
s_query[s_query_i] = 0;
s_train[s_train_i] = 0;
}
//synchronize to make sure each elem for reduceIteration in share memory is written already.
barrier(CLK_LOCAL_MEM_FENCE);
result += reduce_block(s_query, s_train, lidx, lidy);
barrier(CLK_LOCAL_MEM_FENCE);
}
if (queryIdx < query_rows && trainIdx < train_rows && convert_float(result) < maxDistance)
{
int ind = atom_inc(nMatches + queryIdx);
if(ind < bestTrainIdx_cols)
{
bestTrainIdx[mad24(queryIdx, ostep, ind)] = trainIdx;
bestDistance[mad24(queryIdx, ostep, ind)] = result;
}
}
}
__kernel void BruteForceMatch_knnMatch(
__global T *query,
__global T *train,
__global int2 *bestTrainIdx,
__global float2 *bestDistance,
int query_rows,
int query_cols,
int train_rows,
int train_cols,
int step
)
{
const int lidx = get_local_id(0);
const int lidy = get_local_id(1);
const int groupidx = get_group_id(0);
const int queryIdx = mad24(BLOCK_SIZE, groupidx, lidy);
const int queryOffset = min(queryIdx, query_rows - 1) * step;
__global TN *query_vec = (__global TN *)(query + queryOffset);
query_cols /= kercn;
__local float sharebuffer[SHARED_MEM_SZ];
__local value_type *s_query = (__local value_type *)sharebuffer;
#if 0 < MAX_DESC_LEN
__local value_type *s_train = (__local value_type *)sharebuffer + BLOCK_SIZE * MAX_DESC_LEN;
// load the query into local memory.
#pragma unroll
for (int i = 0 ; i < MAX_DESC_LEN / BLOCK_SIZE; i ++)
{
int loadx = mad24(BLOCK_SIZE, i, lidx);
s_query[mad24(MAX_DESC_LEN, lidy, loadx)] = loadx < query_cols ? query_vec[loadx] : 0;
}
#else
__local value_type *s_train = (__local value_type *)sharebuffer + BLOCK_SIZE_ODD * BLOCK_SIZE;
const int s_query_i = mad24(BLOCK_SIZE_ODD, lidy, lidx);
const int s_train_i = mad24(BLOCK_SIZE_ODD, lidx, lidy);
#endif
float myBestDistance1 = MAX_FLOAT;
float myBestDistance2 = MAX_FLOAT;
int myBestTrainIdx1 = -1;
int myBestTrainIdx2 = -1;
for (int t = 0, endt = (train_rows + BLOCK_SIZE - 1) / BLOCK_SIZE; t < endt ; t++)
{
result_type result = 0;
int trainOffset = min(mad24(BLOCK_SIZE, t, lidy), train_rows - 1) * step;
__global TN *train_vec = (__global TN *)(train + trainOffset);
#if 0 < MAX_DESC_LEN
#pragma unroll
for (int i = 0 ; i < MAX_DESC_LEN / BLOCK_SIZE ; i++)
{
//load a BLOCK_SIZE * BLOCK_SIZE block into local train.
const int loadx = mad24(BLOCK_SIZE, i, lidx);
s_train[mad24(BLOCK_SIZE, lidx, lidy)] = loadx < train_cols ? train_vec[loadx] : 0;
//synchronize to make sure each elem for reduceIteration in share memory is written already.
barrier(CLK_LOCAL_MEM_FENCE);
result += reduce_multi_block(s_query, s_train, i, lidx, lidy);
barrier(CLK_LOCAL_MEM_FENCE);
}
#else
for (int i = 0, endq = (query_cols + BLOCK_SIZE -1) / BLOCK_SIZE; i < endq ; i++)
{
const int loadx = mad24(BLOCK_SIZE, i, lidx);
//load query and train into local memory
if (loadx < query_cols)
{
s_query[s_query_i] = query_vec[loadx];
s_train[s_train_i] = train_vec[loadx];
}
else
{
s_query[s_query_i] = 0;
s_train[s_train_i] = 0;
}
barrier(CLK_LOCAL_MEM_FENCE);
result += reduce_block_match(s_query, s_train, lidx, lidy);
barrier(CLK_LOCAL_MEM_FENCE);
}
#endif
result = DIST_RES(result);
const int trainIdx = mad24(BLOCK_SIZE, t, lidx);
if (queryIdx < query_rows && trainIdx < train_rows)
{
if (result < myBestDistance1)
{
myBestDistance2 = myBestDistance1;
myBestTrainIdx2 = myBestTrainIdx1;
myBestDistance1 = result;
myBestTrainIdx1 = trainIdx;
}
else if (result < myBestDistance2)
{
myBestDistance2 = result;
myBestTrainIdx2 = trainIdx;
}
}
}
barrier(CLK_LOCAL_MEM_FENCE);
__local float *s_distance = (__local float *)sharebuffer;
__local int *s_trainIdx = (__local int *)(sharebuffer + BLOCK_SIZE_ODD * BLOCK_SIZE);
// find BestMatch
s_distance += lidy * BLOCK_SIZE_ODD;
s_trainIdx += lidy * BLOCK_SIZE_ODD;
s_distance[lidx] = myBestDistance1;
s_trainIdx[lidx] = myBestTrainIdx1;
float bestDistance1 = MAX_FLOAT;
float bestDistance2 = MAX_FLOAT;
int bestTrainIdx1 = -1;
int bestTrainIdx2 = -1;
barrier(CLK_LOCAL_MEM_FENCE);
if (lidx == 0)
{
for (int i = 0 ; i < BLOCK_SIZE ; i++)
{
float val = s_distance[i];
if (val < bestDistance1)
{
bestDistance2 = bestDistance1;
bestTrainIdx2 = bestTrainIdx1;
bestDistance1 = val;
bestTrainIdx1 = s_trainIdx[i];
}
else if (val < bestDistance2)
{
bestDistance2 = val;
bestTrainIdx2 = s_trainIdx[i];
}
}
}
barrier(CLK_LOCAL_MEM_FENCE);
s_distance[lidx] = myBestDistance2;
s_trainIdx[lidx] = myBestTrainIdx2;
barrier(CLK_LOCAL_MEM_FENCE);
if (lidx == 0)
{
for (int i = 0 ; i < BLOCK_SIZE ; i++)
{
float val = s_distance[i];
if (val < bestDistance2)
{
bestDistance2 = val;
bestTrainIdx2 = s_trainIdx[i];
}
}
}
myBestDistance1 = bestDistance1;
myBestDistance2 = bestDistance2;
myBestTrainIdx1 = bestTrainIdx1;
myBestTrainIdx2 = bestTrainIdx2;
if (queryIdx < query_rows && lidx == 0)
{
bestTrainIdx[queryIdx] = (int2)(myBestTrainIdx1, myBestTrainIdx2);
bestDistance[queryIdx] = (float2)(myBestDistance1, myBestDistance2);
}
}
#ifdef HAVE_INT64_ATOMICS
#pragma OPENCL EXTENSION cl_khr_int64_base_atomics : enable
__kernel void BruteForceMatch_CrossCheckMatch(
__global T *query,
__global T *train,
__global int *bestTrainIdx,
__global float *bestDistance,
__global ulong *revBest,
int query_rows,
int query_cols,
int train_rows,
int train_cols,
int step
)
{
const int lidx = get_local_id(0);
const int lidy = get_local_id(1);
const int groupidx = get_group_id(0);
const int queryIdx = mad24(BLOCK_SIZE, groupidx, lidy);
const int queryOffset = min(queryIdx, query_rows - 1) * step;
__global TN *query_vec = (__global TN *)(query + queryOffset);
query_cols /= kercn;
__local float sharebuffer[SHARED_MEM_SZ];
__local value_type *s_query = (__local value_type *)sharebuffer;
#if 0 < MAX_DESC_LEN
__local value_type *s_train = (__local value_type *)sharebuffer + BLOCK_SIZE * MAX_DESC_LEN;
#pragma unroll
for (int i = 0; i < MAX_DESC_LEN / BLOCK_SIZE; i++)
{
const int loadx = mad24(BLOCK_SIZE, i, lidx);
s_query[mad24(MAX_DESC_LEN, lidy, loadx)] = loadx < query_cols ? query_vec[loadx] : 0;
}
#else
__local value_type *s_train = (__local value_type *)sharebuffer + BLOCK_SIZE_ODD * BLOCK_SIZE;
const int s_query_i = mad24(BLOCK_SIZE_ODD, lidy, lidx);
const int s_train_i = mad24(BLOCK_SIZE_ODD, lidx, lidy);
#endif
float myBestDistance = MAX_FLOAT;
int myBestTrainIdx = -1;
for (int t = 0, endt = (train_rows + BLOCK_SIZE - 1) / BLOCK_SIZE; t < endt; t++)
{
result_type result = 0;
const int trainOffset = min(mad24(BLOCK_SIZE, t, lidy), train_rows - 1) * step;
__global TN *train_vec = (__global TN *)(train + trainOffset);
#if 0 < MAX_DESC_LEN
#pragma unroll
for (int i = 0; i < MAX_DESC_LEN / BLOCK_SIZE; i++)
{
const int loadx = mad24(BLOCK_SIZE, i, lidx);
s_train[mad24(BLOCK_SIZE, lidx, lidy)] = loadx < train_cols ? train_vec[loadx] : 0;
barrier(CLK_LOCAL_MEM_FENCE);
result += reduce_multi_block(s_query, s_train, i, lidx, lidy);
barrier(CLK_LOCAL_MEM_FENCE);
}
#else
for (int i = 0, endq = (query_cols + BLOCK_SIZE - 1) / BLOCK_SIZE; i < endq; i++)
{
const int loadx = mad24(BLOCK_SIZE, i, lidx);
if (loadx < query_cols)
{
s_query[s_query_i] = query_vec[loadx];
s_train[s_train_i] = train_vec[loadx];
}
else
{
s_query[s_query_i] = 0;
s_train[s_train_i] = 0;
}
barrier(CLK_LOCAL_MEM_FENCE);
result += reduce_block_match(s_query, s_train, lidx, lidy);
barrier(CLK_LOCAL_MEM_FENCE);
}
#endif
result = DIST_RES(result);
const int trainIdx = mad24(BLOCK_SIZE, t, lidx);
if (queryIdx < query_rows && trainIdx < train_rows)
{
if (result < myBestDistance)
{
myBestDistance = result;
myBestTrainIdx = trainIdx;
}
uint dist_bits = as_uint(result);
ulong packed = ((ulong)dist_bits << 32) | (ulong)queryIdx;
atom_min(&revBest[trainIdx], packed);
}
}
barrier(CLK_LOCAL_MEM_FENCE);
__local float *s_distance = (__local float *)sharebuffer;
__local int *s_trainIdx = (__local int *)(sharebuffer + BLOCK_SIZE_ODD * BLOCK_SIZE);
s_distance += lidy * BLOCK_SIZE_ODD;
s_trainIdx += lidy * BLOCK_SIZE_ODD;
s_distance[lidx] = myBestDistance;
s_trainIdx[lidx] = myBestTrainIdx;
barrier(CLK_LOCAL_MEM_FENCE);
#pragma unroll
for (int k = 0; k < BLOCK_SIZE; k++)
{
if (myBestDistance > s_distance[k])
{
myBestDistance = s_distance[k];
myBestTrainIdx = s_trainIdx[k];
}
}
if (queryIdx < query_rows && lidx == 0)
{
bestTrainIdx[queryIdx] = myBestTrainIdx;
bestDistance[queryIdx] = myBestDistance;
}
}
#endif
+162
View File
@@ -0,0 +1,162 @@
// OpenCL port of the FAST corner detector.
// Copyright (C) 2014, Itseez Inc. See the license at http://opencv.org
inline int cornerScore(__global const uchar* img, int step)
{
int k, tofs, v = img[0], a0 = 0, b0;
int d[16];
#define LOAD2(idx, ofs) \
tofs = ofs; d[idx] = (short)(v - img[tofs]); d[idx+8] = (short)(v - img[-tofs])
LOAD2(0, 3);
LOAD2(1, -step+3);
LOAD2(2, -step*2+2);
LOAD2(3, -step*3+1);
LOAD2(4, -step*3);
LOAD2(5, -step*3-1);
LOAD2(6, -step*2-2);
LOAD2(7, -step-3);
#pragma unroll
for( k = 0; k < 16; k += 2 )
{
int a = min((int)d[(k+1)&15], (int)d[(k+2)&15]);
a = min(a, (int)d[(k+3)&15]);
a = min(a, (int)d[(k+4)&15]);
a = min(a, (int)d[(k+5)&15]);
a = min(a, (int)d[(k+6)&15]);
a = min(a, (int)d[(k+7)&15]);
a = min(a, (int)d[(k+8)&15]);
a0 = max(a0, min(a, (int)d[k&15]));
a0 = max(a0, min(a, (int)d[(k+9)&15]));
}
b0 = -a0;
#pragma unroll
for( k = 0; k < 16; k += 2 )
{
int b = max((int)d[(k+1)&15], (int)d[(k+2)&15]);
b = max(b, (int)d[(k+3)&15]);
b = max(b, (int)d[(k+4)&15]);
b = max(b, (int)d[(k+5)&15]);
b = max(b, (int)d[(k+6)&15]);
b = max(b, (int)d[(k+7)&15]);
b = max(b, (int)d[(k+8)&15]);
b0 = min(b0, max(b, (int)d[k]));
b0 = min(b0, max(b, (int)d[(k+9)&15]));
}
return -b0-1;
}
__kernel
void FAST_findKeypoints(
__global const uchar * _img, int step, int img_offset,
int img_rows, int img_cols,
volatile __global int* kp_loc,
int max_keypoints, int threshold )
{
int j = get_global_id(0) + 3;
int i = get_global_id(1) + 3;
if (i < img_rows - 3 && j < img_cols - 3)
{
__global const uchar* img = _img + mad24(i, step, j + img_offset);
int v = img[0], t0 = v - threshold, t1 = v + threshold;
int k, tofs, v0, v1;
int m0 = 0, m1 = 0;
#define UPDATE_MASK(idx, ofs) \
tofs = ofs; v0 = img[tofs]; v1 = img[-tofs]; \
m0 |= ((v0 < t0) << idx) | ((v1 < t0) << (8 + idx)); \
m1 |= ((v0 > t1) << idx) | ((v1 > t1) << (8 + idx))
UPDATE_MASK(0, 3);
if( (m0 | m1) == 0 )
return;
UPDATE_MASK(2, -step*2+2);
UPDATE_MASK(4, -step*3);
UPDATE_MASK(6, -step*2-2);
#define EVEN_MASK (1+4+16+64)
if( ((m0 | (m0 >> 8)) & EVEN_MASK) != EVEN_MASK &&
((m1 | (m1 >> 8)) & EVEN_MASK) != EVEN_MASK )
return;
UPDATE_MASK(1, -step+3);
UPDATE_MASK(3, -step*3+1);
UPDATE_MASK(5, -step*3-1);
UPDATE_MASK(7, -step-3);
if( ((m0 | (m0 >> 8)) & 255) != 255 &&
((m1 | (m1 >> 8)) & 255) != 255 )
return;
m0 |= m0 << 16;
m1 |= m1 << 16;
#define CHECK0(i) ((m0 & (511 << i)) == (511 << i))
#define CHECK1(i) ((m1 & (511 << i)) == (511 << i))
if( CHECK0(0) + CHECK0(1) + CHECK0(2) + CHECK0(3) +
CHECK0(4) + CHECK0(5) + CHECK0(6) + CHECK0(7) +
CHECK0(8) + CHECK0(9) + CHECK0(10) + CHECK0(11) +
CHECK0(12) + CHECK0(13) + CHECK0(14) + CHECK0(15) +
CHECK1(0) + CHECK1(1) + CHECK1(2) + CHECK1(3) +
CHECK1(4) + CHECK1(5) + CHECK1(6) + CHECK1(7) +
CHECK1(8) + CHECK1(9) + CHECK1(10) + CHECK1(11) +
CHECK1(12) + CHECK1(13) + CHECK1(14) + CHECK1(15) == 0 )
return;
{
int idx = atomic_inc(kp_loc);
if( idx < max_keypoints )
{
kp_loc[1 + 2*idx] = j;
kp_loc[2 + 2*idx] = i;
}
}
}
}
///////////////////////////////////////////////////////////////////////////
// nonmaxSupression
__kernel
void FAST_nonmaxSupression(
__global const int* kp_in, volatile __global int* kp_out,
__global const uchar * _img, int step, int img_offset,
int rows, int cols, int counter, int max_keypoints)
{
const int idx = get_global_id(0);
if (idx < counter)
{
int x = kp_in[1 + 2*idx];
int y = kp_in[2 + 2*idx];
__global const uchar* img = _img + mad24(y, step, x + img_offset);
int s = cornerScore(img, step);
if( (x < 4 || s > cornerScore(img-1, step)) +
(y < 4 || s > cornerScore(img-step, step)) != 2 )
return;
if( (x >= cols - 4 || s > cornerScore(img+1, step)) +
(y >= rows - 4 || s > cornerScore(img+step, step)) +
(x < 4 || y < 4 || s > cornerScore(img-step-1, step)) +
(x >= cols - 4 || y < 4 || s > cornerScore(img-step+1, step)) +
(x < 4 || y >= rows - 4 || s > cornerScore(img+step-1, step)) +
(x >= cols - 4 || y >= rows - 4 || s > cornerScore(img+step+1, step)) == 6)
{
int new_idx = atomic_inc(kp_out);
if( new_idx < max_keypoints )
{
kp_out[1 + 3*new_idx] = x;
kp_out[2 + 3*new_idx] = y;
kp_out[3 + 3*new_idx] = s;
}
}
}
}
+296
View File
@@ -0,0 +1,296 @@
// 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
inline float gaussian(float x, float y, float sigma)
{
return exp(-(x*x + y*y) / (2.0f*sigma*sigma));
}
/**
* @brief Compute KAZE upright 64-dimensional descriptor
* @details Matches CPU Get_KAZE_Upright_Descriptor_64 exactly:
* - integer scale via round(kpt_size/2)
* - bilinear with y1=(int)(y-0.5), y2=(int)(y+0.5) convention
* - clamp-on-border (not skip)
*
* @param Lx First-order x-derivative (sigma_size-scaled), row-major float array
* @param Ly First-order y-derivative (sigma_size-scaled), row-major float array
* @param lx_step Row stride of Lx/Ly in float elements (= cols for continuous mat)
* @param lx_rows Image height
* @param lx_cols Image width
* @param keypoints_x Keypoint x coordinates
* @param keypoints_y Keypoint y coordinates
* @param keypoints_size Keypoint sizes (diameter = 2*sigma)
* @param descriptors Output descriptor matrix, shape [nkeypoints, 64]
* @param nkeypoints Number of keypoints
*/
__kernel void
KAZE_compute_upright_descriptor_64(
__global const float* Lx,
__global const float* Ly,
int lx_step, int lx_rows, int lx_cols,
__global const float* keypoints_x,
__global const float* keypoints_y,
__global const float* keypoints_size,
__global float* descriptors,
int nkeypoints)
{
int idx = get_global_id(0);
if (idx >= nkeypoints)
return;
const int dsize = 64;
const int sample_step = 5;
const int pattern_size = 12;
float kpt_x = keypoints_x[idx];
float kpt_y = keypoints_y[idx];
float kpt_sz = keypoints_size[idx];
int scale = (int)(kpt_sz / 2.0f + 0.5f); // cvRound equivalent
float xf = kpt_x;
float yf = kpt_y;
float dx = 0.0f, dy = 0.0f, mdx = 0.0f, mdy = 0.0f;
float gauss_s1, gauss_s2;
float rx, ry;
float sample_x, sample_y;
int x1, y1, x2, y2;
int kx, ky, i, j, dcount = 0;
float fx, fy;
float res1, res2, res3, res4;
float len = 0.0f;
float cx = -0.5f, cy = 0.5f;
i = -8;
while (i < pattern_size)
{
j = -8;
i = i - 4;
cx += 1.0f;
cy = -0.5f;
while (j < pattern_size)
{
dx = dy = mdx = mdy = 0.0f;
cy += 1.0f;
j = j - 4;
ky = i + sample_step;
kx = j + sample_step;
float ys = yf + (float)(ky * scale);
float xs = xf + (float)(kx * scale);
for (int k = i; k < i + 9; k++)
{
for (int l = j; l < j + 9; l++)
{
sample_y = (float)k * scale + yf;
sample_x = (float)l * scale + xf;
gauss_s1 = gaussian(xs - sample_x, ys - sample_y, 2.5f * scale);
// Match CPU bilinear convention: y1=(int)(y-0.5), y2=(int)(y+0.5)
y1 = (int)(sample_y - 0.5f);
x1 = (int)(sample_x - 0.5f);
y1 = clamp(y1, 0, lx_rows - 1);
x1 = clamp(x1, 0, lx_cols - 1);
y2 = (int)(sample_y + 0.5f);
x2 = (int)(sample_x + 0.5f);
y2 = clamp(y2, 0, lx_rows - 1);
x2 = clamp(x2, 0, lx_cols - 1);
fx = sample_x - (float)x1;
fy = sample_y - (float)y1;
res1 = Lx[y1 * lx_step + x1];
res2 = Lx[y1 * lx_step + x2];
res3 = Lx[y2 * lx_step + x1];
res4 = Lx[y2 * lx_step + x2];
rx = (1.0f - fx)*(1.0f - fy)*res1 + fx*(1.0f - fy)*res2
+ (1.0f - fx)*fy*res3 + fx*fy*res4;
res1 = Ly[y1 * lx_step + x1];
res2 = Ly[y1 * lx_step + x2];
res3 = Ly[y2 * lx_step + x1];
res4 = Ly[y2 * lx_step + x2];
ry = (1.0f - fx)*(1.0f - fy)*res1 + fx*(1.0f - fy)*res2
+ (1.0f - fx)*fy*res3 + fx*fy*res4;
rx = gauss_s1 * rx;
ry = gauss_s1 * ry;
dx += rx;
dy += ry;
mdx += fabs(rx);
mdy += fabs(ry);
}
}
gauss_s2 = gaussian(cx - 2.0f, cy - 2.0f, 1.5f);
descriptors[idx * dsize + dcount++] = dx * gauss_s2;
descriptors[idx * dsize + dcount++] = dy * gauss_s2;
descriptors[idx * dsize + dcount++] = mdx * gauss_s2;
descriptors[idx * dsize + dcount++] = mdy * gauss_s2;
len += (dx*dx + dy*dy + mdx*mdx + mdy*mdy) * gauss_s2 * gauss_s2;
j += 9;
}
i += 9;
}
// L2 normalize
len = sqrt(len);
if (len > 1e-10f)
{
float len_inv = 1.0f / len;
for (i = 0; i < dsize; i++)
descriptors[idx * dsize + i] *= len_inv;
}
}
/**
* @brief Compute KAZE upright 128-dimensional descriptor (extended mode)
* @details Matches CPU Get_KAZE_Upright_Descriptor_128: splits dx/dy by sign of the
* cross-component for 8 values per subregion.
*/
__kernel void
KAZE_compute_upright_descriptor_128(
__global const float* Lx,
__global const float* Ly,
int lx_step, int lx_rows, int lx_cols,
__global const float* keypoints_x,
__global const float* keypoints_y,
__global const float* keypoints_size,
__global float* descriptors,
int nkeypoints)
{
int idx = get_global_id(0);
if (idx >= nkeypoints)
return;
const int dsize = 128;
const int sample_step = 5;
const int pattern_size = 12;
float kpt_x = keypoints_x[idx];
float kpt_y = keypoints_y[idx];
float kpt_sz = keypoints_size[idx];
int scale = (int)(kpt_sz / 2.0f + 0.5f);
float xf = kpt_x;
float yf = kpt_y;
float dxp = 0.0f, dxn = 0.0f, mdxp = 0.0f, mdxn = 0.0f;
float dyp = 0.0f, dyn = 0.0f, mdyp = 0.0f, mdyn = 0.0f;
float gauss_s1, gauss_s2;
float rx, ry;
float sample_x, sample_y;
int x1, y1, x2, y2;
int kx, ky, i, j, dcount = 0;
float fx, fy;
float res1, res2, res3, res4;
float len = 0.0f;
float cx = -0.5f, cy = 0.5f;
i = -8;
while (i < pattern_size)
{
j = -8;
i = i - 4;
cx += 1.0f;
cy = -0.5f;
while (j < pattern_size)
{
dxp = dxn = mdxp = mdxn = 0.0f;
dyp = dyn = mdyp = mdyn = 0.0f;
cy += 1.0f;
j = j - 4;
ky = i + sample_step;
kx = j + sample_step;
float ys = yf + (float)(ky * scale);
float xs = xf + (float)(kx * scale);
for (int k = i; k < i + 9; k++)
{
for (int l = j; l < j + 9; l++)
{
sample_y = (float)k * scale + yf;
sample_x = (float)l * scale + xf;
gauss_s1 = gaussian(xs - sample_x, ys - sample_y, 2.5f * scale);
y1 = (int)(sample_y - 0.5f);
x1 = (int)(sample_x - 0.5f);
y1 = clamp(y1, 0, lx_rows - 1);
x1 = clamp(x1, 0, lx_cols - 1);
y2 = (int)(sample_y + 0.5f);
x2 = (int)(sample_x + 0.5f);
y2 = clamp(y2, 0, lx_rows - 1);
x2 = clamp(x2, 0, lx_cols - 1);
fx = sample_x - (float)x1;
fy = sample_y - (float)y1;
res1 = Lx[y1 * lx_step + x1];
res2 = Lx[y1 * lx_step + x2];
res3 = Lx[y2 * lx_step + x1];
res4 = Lx[y2 * lx_step + x2];
rx = (1.0f - fx)*(1.0f - fy)*res1 + fx*(1.0f - fy)*res2
+ (1.0f - fx)*fy*res3 + fx*fy*res4;
res1 = Ly[y1 * lx_step + x1];
res2 = Ly[y1 * lx_step + x2];
res3 = Ly[y2 * lx_step + x1];
res4 = Ly[y2 * lx_step + x2];
ry = (1.0f - fx)*(1.0f - fy)*res1 + fx*(1.0f - fy)*res2
+ (1.0f - fx)*fy*res3 + fx*fy*res4;
rx = gauss_s1 * rx;
ry = gauss_s1 * ry;
if (ry >= 0.0f) { dxp += rx; mdxp += fabs(rx); }
else { dxn += rx; mdxn += fabs(rx); }
if (rx >= 0.0f) { dyp += ry; mdyp += fabs(ry); }
else { dyn += ry; mdyn += fabs(ry); }
}
}
gauss_s2 = gaussian(cx - 2.0f, cy - 2.0f, 1.5f);
descriptors[idx * dsize + dcount++] = dxp * gauss_s2;
descriptors[idx * dsize + dcount++] = dxn * gauss_s2;
descriptors[idx * dsize + dcount++] = mdxp * gauss_s2;
descriptors[idx * dsize + dcount++] = mdxn * gauss_s2;
descriptors[idx * dsize + dcount++] = dyp * gauss_s2;
descriptors[idx * dsize + dcount++] = dyn * gauss_s2;
descriptors[idx * dsize + dcount++] = mdyp * gauss_s2;
descriptors[idx * dsize + dcount++] = mdyn * gauss_s2;
len += (dxp*dxp + dxn*dxn + mdxp*mdxp + mdxn*mdxn
+ dyp*dyp + dyn*dyn + mdyp*mdyp + mdyn*mdyn) * gauss_s2 * gauss_s2;
j += 9;
}
i += 9;
}
len = sqrt(len);
if (len > 1e-10f)
{
float len_inv = 1.0f / len;
for (i = 0; i < dsize; i++)
descriptors[idx * dsize + i] *= len_inv;
}
}
+254
View File
@@ -0,0 +1,254 @@
// OpenCL port of the ORB feature detector and descriptor extractor
// Copyright (C) 2014, Itseez Inc. See the license at http://opencv.org
//
// The original code has been contributed by Peter Andreas Entschev, peter@entschev.com
#define LAYERINFO_SIZE 1
#define LAYERINFO_OFS 0
#define KEYPOINT_SIZE 3
#define ORIENTED_KEYPOINT_SIZE 4
#define KEYPOINT_X 0
#define KEYPOINT_Y 1
#define KEYPOINT_Z 2
#define KEYPOINT_ANGLE 3
/////////////////////////////////////////////////////////////
#ifdef ORB_RESPONSES
__kernel void
ORB_HarrisResponses(__global const uchar* imgbuf, int imgstep, int imgoffset0,
__global const int* layerinfo, __global const int* keypoints,
__global float* responses, int nkeypoints )
{
int idx = get_global_id(0);
if( idx < nkeypoints )
{
__global const int* kpt = keypoints + idx*KEYPOINT_SIZE;
__global const int* layer = layerinfo + kpt[KEYPOINT_Z]*LAYERINFO_SIZE;
__global const uchar* img = imgbuf + imgoffset0 + layer[LAYERINFO_OFS] +
(kpt[KEYPOINT_Y] - blockSize/2)*imgstep + (kpt[KEYPOINT_X] - blockSize/2);
int i, j;
int a = 0, b = 0, c = 0;
for( i = 0; i < blockSize; i++, img += imgstep-blockSize )
{
for( j = 0; j < blockSize; j++, img++ )
{
int Ix = (img[1] - img[-1])*2 + img[-imgstep+1] - img[-imgstep-1] + img[imgstep+1] - img[imgstep-1];
int Iy = (img[imgstep] - img[-imgstep])*2 + img[imgstep-1] - img[-imgstep-1] + img[imgstep+1] - img[-imgstep+1];
a += Ix*Ix;
b += Iy*Iy;
c += Ix*Iy;
}
}
responses[idx] = ((float)a * b - (float)c * c - HARRIS_K * (float)(a + b) * (a + b))*scale_sq_sq;
}
}
#endif
/////////////////////////////////////////////////////////////
#ifdef ORB_ANGLES
#define _DBL_EPSILON 2.2204460492503131e-16f
#define atan2_p1 (0.9997878412794807f*57.29577951308232f)
#define atan2_p3 (-0.3258083974640975f*57.29577951308232f)
#define atan2_p5 (0.1555786518463281f*57.29577951308232f)
#define atan2_p7 (-0.04432655554792128f*57.29577951308232f)
inline float fastAtan2( float y, float x )
{
float ax = fabs(x), ay = fabs(y);
float a, c, c2;
if( ax >= ay )
{
c = ay/(ax + _DBL_EPSILON);
c2 = c*c;
a = (((atan2_p7*c2 + atan2_p5)*c2 + atan2_p3)*c2 + atan2_p1)*c;
}
else
{
c = ax/(ay + _DBL_EPSILON);
c2 = c*c;
a = 90.f - (((atan2_p7*c2 + atan2_p5)*c2 + atan2_p3)*c2 + atan2_p1)*c;
}
if( x < 0 )
a = 180.f - a;
if( y < 0 )
a = 360.f - a;
return a;
}
__kernel void
ORB_ICAngle(__global const uchar* imgbuf, int imgstep, int imgoffset0,
__global const int* layerinfo, __global const int* keypoints,
__global float* responses, const __global int* u_max,
int nkeypoints, int half_k )
{
int idx = get_global_id(0);
if( idx < nkeypoints )
{
__global const int* kpt = keypoints + idx*KEYPOINT_SIZE;
__global const int* layer = layerinfo + kpt[KEYPOINT_Z]*LAYERINFO_SIZE;
__global const uchar* center = imgbuf + imgoffset0 + layer[LAYERINFO_OFS] +
kpt[KEYPOINT_Y]*imgstep + kpt[KEYPOINT_X];
int u, v, m_01 = 0, m_10 = 0;
// Treat the center line differently, v=0
for( u = -half_k; u <= half_k; u++ )
m_10 += u * center[u];
// Go line by line in the circular patch
for( v = 1; v <= half_k; v++ )
{
// Proceed over the two lines
int v_sum = 0;
int d = u_max[v];
for( u = -d; u <= d; u++ )
{
int val_plus = center[u + v*imgstep], val_minus = center[u - v*imgstep];
v_sum += (val_plus - val_minus);
m_10 += u * (val_plus + val_minus);
}
m_01 += v * v_sum;
}
// we do not use OpenCL's atan2 intrinsic,
// because we want to get _exactly_ the same results as the CPU version
responses[idx] = fastAtan2((float)m_01, (float)m_10);
}
}
#endif
/////////////////////////////////////////////////////////////
#ifdef ORB_DESCRIPTORS
__kernel void
ORB_computeDescriptor(__global const uchar* imgbuf, int imgstep, int imgoffset0,
__global const int* layerinfo, __global const int* keypoints,
__global uchar* _desc, const __global int* pattern,
int nkeypoints, int dsize )
{
int idx = get_global_id(0);
if( idx < nkeypoints )
{
int i;
__global const int* kpt = keypoints + idx*ORIENTED_KEYPOINT_SIZE;
__global const int* layer = layerinfo + kpt[KEYPOINT_Z]*LAYERINFO_SIZE;
__global const uchar* center = imgbuf + imgoffset0 + layer[LAYERINFO_OFS] +
kpt[KEYPOINT_Y]*imgstep + kpt[KEYPOINT_X];
float angle = as_float(kpt[KEYPOINT_ANGLE]);
angle *= 0.01745329251994329547f;
float cosa;
float sina = sincos(angle, &cosa);
__global uchar* desc = _desc + idx*dsize;
#define GET_VALUE(idx) \
center[mad24(convert_int_rte(pattern[(idx)*2] * sina + pattern[(idx)*2+1] * cosa), imgstep, \
convert_int_rte(pattern[(idx)*2] * cosa - pattern[(idx)*2+1] * sina))]
for( i = 0; i < dsize; i++ )
{
int val;
#if WTA_K == 2
int t0, t1;
t0 = GET_VALUE(0); t1 = GET_VALUE(1);
val = t0 < t1;
t0 = GET_VALUE(2); t1 = GET_VALUE(3);
val |= (t0 < t1) << 1;
t0 = GET_VALUE(4); t1 = GET_VALUE(5);
val |= (t0 < t1) << 2;
t0 = GET_VALUE(6); t1 = GET_VALUE(7);
val |= (t0 < t1) << 3;
t0 = GET_VALUE(8); t1 = GET_VALUE(9);
val |= (t0 < t1) << 4;
t0 = GET_VALUE(10); t1 = GET_VALUE(11);
val |= (t0 < t1) << 5;
t0 = GET_VALUE(12); t1 = GET_VALUE(13);
val |= (t0 < t1) << 6;
t0 = GET_VALUE(14); t1 = GET_VALUE(15);
val |= (t0 < t1) << 7;
pattern += 16*2;
#elif WTA_K == 3
int t0, t1, t2;
t0 = GET_VALUE(0); t1 = GET_VALUE(1); t2 = GET_VALUE(2);
val = t2 > t1 ? (t2 > t0 ? 2 : 0) : (t1 > t0);
t0 = GET_VALUE(3); t1 = GET_VALUE(4); t2 = GET_VALUE(5);
val |= (t2 > t1 ? (t2 > t0 ? 2 : 0) : (t1 > t0)) << 2;
t0 = GET_VALUE(6); t1 = GET_VALUE(7); t2 = GET_VALUE(8);
val |= (t2 > t1 ? (t2 > t0 ? 2 : 0) : (t1 > t0)) << 4;
t0 = GET_VALUE(9); t1 = GET_VALUE(10); t2 = GET_VALUE(11);
val |= (t2 > t1 ? (t2 > t0 ? 2 : 0) : (t1 > t0)) << 6;
pattern += 12*2;
#elif WTA_K == 4
int t0, t1, t2, t3, k;
int a, b;
t0 = GET_VALUE(0); t1 = GET_VALUE(1);
t2 = GET_VALUE(2); t3 = GET_VALUE(3);
a = 0, b = 2;
if( t1 > t0 ) t0 = t1, a = 1;
if( t3 > t2 ) t2 = t3, b = 3;
k = t0 > t2 ? a : b;
val = k;
t0 = GET_VALUE(4); t1 = GET_VALUE(5);
t2 = GET_VALUE(6); t3 = GET_VALUE(7);
a = 0, b = 2;
if( t1 > t0 ) t0 = t1, a = 1;
if( t3 > t2 ) t2 = t3, b = 3;
k = t0 > t2 ? a : b;
val |= k << 2;
t0 = GET_VALUE(8); t1 = GET_VALUE(9);
t2 = GET_VALUE(10); t3 = GET_VALUE(11);
a = 0, b = 2;
if( t1 > t0 ) t0 = t1, a = 1;
if( t3 > t2 ) t2 = t3, b = 3;
k = t0 > t2 ? a : b;
val |= k << 4;
t0 = GET_VALUE(12); t1 = GET_VALUE(13);
t2 = GET_VALUE(14); t3 = GET_VALUE(15);
a = 0, b = 2;
if( t1 > t0 ) t0 = t1, a = 1;
if( t3 > t2 ) t2 = t3, b = 3;
k = t0 > t2 ? a : b;
val |= k << 6;
pattern += 16*2;
#else
#error "unknown/undefined WTA_K value; should be 2, 3 or 4"
#endif
desc[i] = (uchar)val;
}
}
}
#endif
File diff suppressed because it is too large Load Diff
+56
View File
@@ -0,0 +1,56 @@
/*M///////////////////////////////////////////////////////////////////////////////////////
//
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
//
// By downloading, copying, installing or using the software you agree to this license.
// If you do not agree to this license, do not download, install,
// copy or use the software.
//
//
// License Agreement
// For Open Source Computer Vision Library
//
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
// Third party copyrights are property of their respective owners.
//
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
// * Redistribution's of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Redistribution's in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// * The name of the copyright holders may not be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// This software is provided by the copyright holders and contributors "as is" and
// any express or implied warranties, including, but not limited to, the implied
// warranties of merchantability and fitness for a particular purpose are disclaimed.
// In no event shall the Intel Corporation or contributors be liable for any direct,
// indirect, incidental, special, exemplary, or consequential damages
// (including, but not limited to, procurement of substitute goods or services;
// loss of use, data, or profits; or business interruption) however caused
// and on any theory of liability, whether in contract, strict liability,
// or tort (including negligence or otherwise) arising in any way out of
// the use of this software, even if advised of the possibility of such damage.
//
//M*/
#ifndef __OPENCV_PRECOMP_H__
#define __OPENCV_PRECOMP_H__
#include "opencv2/features2d.hpp"
#include "opencv2/imgproc.hpp"
#include "opencv2/core/utility.hpp"
#include "opencv2/core/private.hpp"
#include "opencv2/core/ocl.hpp"
#include "opencv2/core/hal/hal.hpp"
#include <algorithm>
#endif

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