added tests
authorMatthias Bady <aegirxx@gmail.com>
Thu, 19 Dec 2013 23:05:50 +0000 (00:05 +0100)
committerMatthias Bady <aegirxx@gmail.com>
Thu, 19 Dec 2013 23:05:50 +0000 (00:05 +0100)
modules/ocl/include/opencv2/ocl.hpp
modules/ocl/perf/perf_brief.cpp
modules/ocl/src/brief.cpp
modules/ocl/src/opencl/brief.cl
modules/ocl/test/test_brief.cpp [new file with mode: 0644]

index 76f3dcc..8df9e15 100644 (file)
@@ -1558,10 +1558,12 @@ namespace cv
             /*
              * 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.
+             * keypoints    The input keypoints.
              * descriptors  Copmputed descriptors. Row i is the descriptor for keypoint i.
              */
-            void compute( const oclMat& image, oclMat& keypoints, oclMat& descriptors ) const;
+            void compute( const oclMat& image, const oclMat& keypoints, oclMat& mask, oclMat& descriptors ) const;
+
+            static int getBorderSize();
         protected:
 
             int bytes;
index 5cba785..b3784a6 100644 (file)
@@ -50,54 +50,64 @@ using namespace ocl;
 using namespace perf;
 
 ///////////// BRIEF ////////////////////////
+typedef TestBaseWithParam<std::tr1::tuple<std::string, int, size_t> > OCL_BRIEF;
 
-typedef TestBaseWithParam<std::tr1::tuple<std::string, int> > OCL_BRIEF;
-
-#define BRIEF_IMAGES \
-    "cv/detectors_descriptors_evaluation/images_datasets/leuven/img1.png",\
-    "stitching/a3.png"
-
-PERF_TEST_P( OCL_BRIEF, extract, testing::Combine( testing::Values( BRIEF_IMAGES ), testing::Values( 16, 32, 64 ) ) )
+PERF_TEST_P( OCL_BRIEF, extract, testing::Combine(
+                                                   testing::Values( string( "gpu/opticalflow/rubberwhale1.png" ),
+                                                                    string( "gpu/stereobm/aloe-L.png" )
+                                                                    ), testing::Values( 16, 32, 64 ), testing::Values( 250, 500, 1000, 2500, 3000 ) ) )
 {
-    const int threshold = 20;
     const std::string filename = std::tr1::get<0>(GetParam( ));
     const int bytes = std::tr1::get<1>(GetParam( ));
-    const Mat img = imread( getDataPath( filename ), IMREAD_GRAYSCALE );
-    ASSERT_FALSE( img.empty( ) );
+    const size_t numKp = std::tr1::get<2>(GetParam( ));
 
-    if ( RUN_OCL_IMPL )
+    Mat img = imread( getDataPath( filename ), IMREAD_GRAYSCALE );
+    ASSERT_TRUE( !img.empty( ) ) << "no input image";
+
+    int threshold = 15;
+    std::vector<KeyPoint> keypoints;
+    while (threshold > 0 && keypoints.size( ) < numKp)
     {
-        oclMat d_img( img );
-        oclMat d_keypoints;
-        FAST_OCL fast( threshold );
-        fast( d_img, oclMat( ), d_keypoints );
+        FastFeatureDetector fast( threshold );
+        fast.detect( img, keypoints, Mat( ) );
+        threshold -= 5;
+        KeyPointsFilter::runByImageBorder( keypoints, img.size( ), BRIEF_OCL::getBorderSize( ) );
+    }
+    ASSERT_TRUE( keypoints.size( ) >= numKp ) << "not enough keypoints";
+    keypoints.resize( numKp );
 
+    if ( RUN_OCL_IMPL )
+    {
+        Mat kpMat( 2, keypoints.size( ), CV_32FC1 );
+        for ( size_t i = 0; i < keypoints.size( ); ++i )
+        {
+            kpMat.col( i ).row( 0 ) = keypoints[i].pt.x;
+            kpMat.col( i ).row( 1 ) = keypoints[i].pt.y;
+        }
         BRIEF_OCL brief( bytes );
-
-        OCL_TEST_CYCLE( )
+        oclMat imgCL( img ), keypointsCL(kpMat), mask;
+        while (next( ))
         {
-            oclMat d_descriptors;
-            brief.compute( d_img, d_keypoints, d_descriptors );
+            startTimer( );
+            oclMat descriptorsCL;
+            brief.compute( imgCL, keypointsCL, mask, descriptorsCL );
+            cv::ocl::finish( );
+            stopTimer( );
         }
-
-        std::vector<KeyPoint> ocl_keypoints;
-        fast.downloadKeypoints( d_keypoints, ocl_keypoints );
-        SANITY_CHECK_KEYPOINTS( ocl_keypoints );
+        SANITY_CHECK_NOTHING( )
     }
     else if ( RUN_PLAIN_IMPL )
     {
-        std::vector<KeyPoint> keypoints;
-        FAST( img, keypoints, threshold );
-
         BriefDescriptorExtractor brief( bytes );
 
-        TEST_CYCLE( )
+        while (next( ))
         {
+            startTimer( );
             Mat descriptors;
             brief.compute( img, keypoints, descriptors );
+            stopTimer( );
         }
-
-        SANITY_CHECK_KEYPOINTS( keypoints );
+        SANITY_CHECK_NOTHING( )
     }
     else
         OCL_PERF_ELSE;
index 6d54eb5..d176a5e 100644 (file)
@@ -53,35 +53,39 @@ BRIEF_OCL::BRIEF_OCL( int _bytes ) : bytes( _bytes )
 {
 }
 
-void
-BRIEF_OCL::compute( const oclMat& image, oclMat& keypoints, oclMat& descriptors ) const
+void BRIEF_OCL::compute( const oclMat& image, const oclMat& keypoints, oclMat& mask, oclMat& descriptors ) const
 {
-    oclMat grayImage = image;
-    if ( image.type( ) != CV_8U ) cvtColor( image, grayImage, COLOR_BGR2GRAY );
-
+    CV_Assert( image.type( ) == CV_8UC1 );
+    if ( keypoints.size( ).area( ) == 0 ) return;
+    descriptors = oclMat( Mat( keypoints.cols, bytes, CV_8UC1 ) );
+    if( mask.cols != keypoints.cols )
+    {
+        mask = oclMat( Mat::ones( 1, keypoints.cols, CV_8UC1 ) );
+    }
     oclMat sum;
-    integral( grayImage, sum, CV_32S );
+    integral( image, sum, CV_32S );
     cl_mem sumTexture = bindTexture( sum );
-
-    //TODO filter keypoints by border
-
-    descriptors = oclMat( keypoints.cols, bytes, CV_8U );
-
     std::stringstream build_opt;
-    build_opt << " -D BYTES=" << bytes << " -D KERNEL_SIZE=" << KERNEL_SIZE;
-
+    build_opt
+            << " -D BYTES=" << bytes
+            << " -D KERNEL_SIZE=" << KERNEL_SIZE
+            << " -D BORDER=" << getBorderSize();
     const String kernelname = "extractBriefDescriptors";
-    size_t localThreads[3]  = {bytes, 1, 1};
+    size_t localThreads[3] = {bytes, 1, 1};
     size_t globalThreads[3] = {keypoints.cols * bytes, 1, 1};
-
+    Context* ctx = Context::getContext( );
     std::vector< std::pair<size_t, const void *> > args;
     args.push_back( std::make_pair( sizeof (cl_mem), (void *) &sumTexture ) );
     args.push_back( std::make_pair( sizeof (cl_mem), (void *) &keypoints.data ) );
     args.push_back( std::make_pair( sizeof (cl_int), (void *) &keypoints.step ) );
     args.push_back( std::make_pair( sizeof (cl_mem), (void *) &descriptors.data ) );
     args.push_back( std::make_pair( sizeof (cl_int), (void *) &descriptors.step ) );
-
-    Context* ctx = Context::getContext( );
+    args.push_back( std::make_pair( sizeof (cl_mem), (void *) &mask.data ) );
     openCLExecuteKernel( ctx, &brief, kernelname, globalThreads, localThreads, args, -1, -1, build_opt.str( ).c_str( ) );
     openCLFree( sumTexture );
 }
+
+int BRIEF_OCL::getBorderSize( )
+{
+    return PATCH_SIZE / 2 + KERNEL_SIZE / 2;
+}
index f75c99a..343e95b 100644 (file)
 //
 //M*/
 
-#define X_ROW 0
-#define Y_ROW 1
-
 #ifndef BYTES
  #define BYTES 16
 #endif
 
 #ifndef KERNEL_SIZE
- #define KERNEL_SIZE 32
+ #define KERNEL_SIZE 9
+#endif
+
+#ifndef BORDER
+ #define BORDER 0
 #endif
 
 #define HALF_KERNEL (KERNEL_SIZE/2)
@@ -128,29 +129,45 @@ __constant char tests[32 * BYTES] =
 #endif
 };
 
-inline int smoothedSum(__read_only image2d_t sum, const int2 pt)
+inline int smoothedSum(__read_only image2d_t sum, const int2 kpPos, const int2 pt)
 {
-    return ( read_imagei( sum, sampler, pt + (int2)(  HALF_KERNEL + 1,  HALF_KERNEL + 1 ))
-           - read_imagei( sum, sampler, pt + (int2)( -HALF_KERNEL,      HALF_KERNEL + 1 ))
-           - read_imagei( sum, sampler, pt + (int2)(  HALF_KERNEL + 1, -HALF_KERNEL ))
-           + read_imagei( sum, sampler, pt + (int2)( -HALF_KERNEL,     -HALF_KERNEL ))).x;
+    return ( read_imagei( sum, sampler, kpPos + pt + (int2)(  HALF_KERNEL + 1,  HALF_KERNEL + 1 ))
+           - read_imagei( sum, sampler, kpPos + pt + (int2)( -HALF_KERNEL,      HALF_KERNEL + 1 ))
+           - read_imagei( sum, sampler, kpPos + pt + (int2)(  HALF_KERNEL + 1, -HALF_KERNEL ))
+           + read_imagei( sum, sampler, kpPos + pt + (int2)( -HALF_KERNEL,     -HALF_KERNEL ))).x;
 }
 
 __kernel void extractBriefDescriptors(
-    __read_only image2d_t sumImg, __global float* keypoints, int kpRowStep, __global uchar* descriptors, int dscRowStep)
+    __read_only image2d_t sumImg,
+    __global float* keypoints, int kpRowStep,
+    __global uchar* descriptors, int dscRowStep,
+    __global uchar* mask)
 {
-    const int  byte = get_local_id(0);
-    const int  kpId = get_group_id(0);
-    const int2 kpPos = (int2)(keypoints[X_ROW * (kpRowStep/4) + kpId] + 0.5, keypoints[Y_ROW * (kpRowStep/4) + kpId] + 0.5);
+    const int  byte  = get_local_id(0);
+    const int  kpId  = get_group_id(0);
 
+    if( !mask[kpId])
+    {
+        return;
+    }
+    const float2 kpPos = (float2)(keypoints[kpId], keypoints[kpRowStep/4 + kpId]);
+    if( kpPos.x < BORDER
+     || kpPos.y < BORDER
+     || kpPos.x >= (get_image_width(  sumImg ) - BORDER)
+     || kpPos.y >= (get_image_height( sumImg ) - BORDER) )
+    {
+        if( byte == 0) mask[kpId] = 0;
+        return;
+    }
     uchar descByte = 0;
+    const int2 pt = (int2)( kpPos.x + 0.5f, kpPos.y + 0.5f );
     for(int i = 0; i<8; ++i)
     {
         descByte |= (
-            smoothedSum(sumImg, (int2)( tests[byte * 32 + (i * 4) + 0], tests[byte * 32 + (i * 4) + 1] ))
-          < smoothedSum(sumImg, (int2)( tests[byte * 32 + (i * 4) + 2], tests[byte * 32 + (i * 4) + 3] ))
+            smoothedSum(sumImg, pt, (int2)( tests[byte * 32 + (i * 4) + 1], tests[byte * 32 + (i * 4) + 0] ))
+          < smoothedSum(sumImg, pt, (int2)( tests[byte * 32 + (i * 4) + 3], tests[byte * 32 + (i * 4) + 2] ))
           ) << (7-i);
     }
-
     descriptors[kpId * dscRowStep + byte] = descByte;
+    if( byte == 0) mask[kpId] = 1;
 }
diff --git a/modules/ocl/test/test_brief.cpp b/modules/ocl/test/test_brief.cpp
new file mode 100644 (file)
index 0000000..81c638a
--- /dev/null
@@ -0,0 +1,115 @@
+/*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.
+//
+// @Authors
+//    Matthias Bady aegirxx ==> gmail.com
+//
+// Redistribution and use in source and binary forms, with or without modification,
+// are permitted provided that the following conditions are met:
+//
+//   * Redistribution's of source code must retain the above copyright notice,
+//     this list of conditions and the following disclaimer.
+//
+//   * Redistribution's in binary form must reproduce the above copyright notice,
+//     this list of conditions and the following disclaimer in the documentation
+//     and/or other materials provided with the distribution.
+//
+//   * The name of the copyright holders may not be used to endorse or promote products
+//     derived from this software without specific prior written permission.
+//
+// This software is provided by the copyright holders and contributors "as is" and
+// any express or implied warranties, including, but not limited to, the implied
+// warranties of merchantability and fitness for a particular purpose are disclaimed.
+// In no event shall the Intel Corporation or contributors be liable for any direct,
+// indirect, incidental, special, exemplary, or consequential damages
+// (including, but not limited to, procurement of substitute goods or services;
+// loss of use, data, or profits; or business interruption) however caused
+// and on any theory of liability, whether in contract, strict liability,
+// or tort (including negligence or otherwise) arising in any way out of
+// the use of this software, even if advised of the possibility of such damage.
+//
+//M*/
+
+#include "test_precomp.hpp"
+
+using namespace std;
+using namespace cv;
+using namespace ocl;
+
+#ifdef HAVE_OPENCL
+
+namespace
+{
+IMPLEMENT_PARAM_CLASS( BRIEF_Bytes, int )
+}
+
+PARAM_TEST_CASE( BRIEF, BRIEF_Bytes )
+{
+    int bytes;
+
+    virtual void SetUp( )
+    {
+        bytes = GET_PARAM( 0 );
+    }
+};
+
+OCL_TEST_P( BRIEF, Accuracy )
+{
+    Mat img = readImage( "gpu/opticalflow/rubberwhale1.png", IMREAD_GRAYSCALE );
+    ASSERT_TRUE( !img.empty( ) ) << "no input image";
+
+    FastFeatureDetector fast( 20 );
+    std::vector<KeyPoint> keypoints;
+    fast.detect( img, keypoints, Mat( ) );
+
+    Mat descriptorsGold;
+    BriefDescriptorExtractor brief( bytes );
+    brief.compute( img, keypoints, descriptorsGold );
+
+    Mat kpMat( 2, keypoints.size( ), CV_32FC1 );
+    for ( size_t i = 0; i < keypoints.size( ); ++i )
+    {
+        kpMat.col( i ).row( 0 ) = keypoints[i].pt.x;
+        kpMat.col( i ).row( 1 ) = keypoints[i].pt.y;
+    }
+    oclMat imgOcl( img ), keypointsOcl( kpMat ), descriptorsOcl, maskOcl;
+
+    BRIEF_OCL briefOcl( bytes );
+    briefOcl.compute( imgOcl, keypointsOcl, maskOcl, descriptorsOcl );
+    Mat mask, descriptors;
+    maskOcl.download( mask );
+    descriptorsOcl.download( descriptors );
+
+    const int numDesc = cv::countNonZero( mask );
+    if ( numDesc != descriptors.cols )
+    {
+        size_t idx = 0;
+        Mat tmp( numDesc, bytes, CV_8UC1 );
+        for ( int i = 0; i < descriptors.rows; ++i )
+        {
+            if ( mask.at<uchar>(i) )
+            {
+                descriptors.row( i ).copyTo( tmp.row( idx++ ) );
+            }
+        }
+        descriptors = tmp;
+    }
+    ASSERT_TRUE( descriptors.size( ) == descriptorsGold.size( ) ) << "Different number of descriptors";
+    ASSERT_TRUE( 0 == norm( descriptors, descriptorsGold, NORM_HAMMING ) ) << "Descriptors different";
+}
+
+INSTANTIATE_TEST_CASE_P( OCL_Features2D, BRIEF, testing::Values( 16, 32, 64 ) );
+#endif
\ No newline at end of file