From 1c246d476b7d13ebd9798b8e04cd2f18340d632f Mon Sep 17 00:00:00 2001 From: biagio montesano Date: Sat, 9 Aug 2014 18:34:40 +0200 Subject: [PATCH] Detection test, smoothed removal, setter modified, samples corrected --- .../opencv2/line_descriptor/descriptor.hpp | 6 +- .../line_descriptor/ed_line_detector.hpp | 8 +- .../samples/compute_descriptors.cpp | 22 -- .../line_descriptor/samples/knn_matching.cpp | 3 + modules/line_descriptor/samples/matching.cpp | 98 +++-- modules/line_descriptor/src/LSDDetector.cpp | 2 +- .../line_descriptor/src/binary_descriptor.cpp | 66 +++- modules/line_descriptor/src/draw.cpp | 24 +- .../line_descriptor/src/ed_line_detector.cpp | 16 +- .../test/test_detector_regression.cpp | 341 ++++++++++++++++++ modules/line_descriptor/test/test_main.cpp | 3 + modules/line_descriptor/test/test_precomp.hpp | 24 ++ 12 files changed, 521 insertions(+), 92 deletions(-) create mode 100644 modules/line_descriptor/test/test_detector_regression.cpp create mode 100644 modules/line_descriptor/test/test_main.cpp create mode 100644 modules/line_descriptor/test/test_precomp.hpp diff --git a/modules/line_descriptor/include/opencv2/line_descriptor/descriptor.hpp b/modules/line_descriptor/include/opencv2/line_descriptor/descriptor.hpp index 8ff786781..0fd78c25e 100644 --- a/modules/line_descriptor/include/opencv2/line_descriptor/descriptor.hpp +++ b/modules/line_descriptor/include/opencv2/line_descriptor/descriptor.hpp @@ -190,14 +190,12 @@ class CV_EXPORTS_W BinaryDescriptor : public Algorithm /* requires descriptors computation (only one image) */ CV_WRAP - void compute( const Mat& image, CV_OUT CV_IN_OUT std::vector& keylines, CV_OUT Mat& descriptors, bool returnFloatDescr = false, - bool useDetectionData = false ) const; + void compute( const Mat& image, CV_OUT CV_IN_OUT std::vector& keylines, CV_OUT Mat& descriptors, bool returnFloatDescr = false ) const; /* requires descriptors computation (more than one image) */ CV_WRAP void compute( const std::vector& images, std::vector >& keylines, std::vector& descriptors, bool returnFloatDescr = - false, - bool useDetectionData = false ) const; + false ) const; /* returns descriptor size */ CV_WRAP diff --git a/modules/line_descriptor/include/opencv2/line_descriptor/ed_line_detector.hpp b/modules/line_descriptor/include/opencv2/line_descriptor/ed_line_detector.hpp index 5bca07bc2..2899e3b8b 100644 --- a/modules/line_descriptor/include/opencv2/line_descriptor/ed_line_detector.hpp +++ b/modules/line_descriptor/include/opencv2/line_descriptor/ed_line_detector.hpp @@ -106,21 +106,19 @@ class EDLineDetector /*extract edges from image *image: In, gray image; *edges: Out, store the edges, each edge is a pixel chain - *smoothed: In, flag to mark whether the image has already been smoothed by Gaussian filter. *return -1: error happen */ - int EdgeDrawing( cv::Mat &image, EdgeChains &edgeChains, bool smoothed ); + int EdgeDrawing( cv::Mat &image, EdgeChains &edgeChains ); /*extract lines from image *image: In, gray image; *lines: Out, store the extracted lines, - *smoothed: In, flag to mark whether the image has already been smoothed by Gaussian filter. *return -1: error happen */ - int EDline( cv::Mat &image, LineChains &lines, bool smoothed ); + int EDline( cv::Mat &image, LineChains &lines ); /* extract line from image, and store them */ - int EDline( cv::Mat &image, bool smoothed ); + int EDline( cv::Mat &image ); cv::Mat dxImg_; //store the dxImg; diff --git a/modules/line_descriptor/samples/compute_descriptors.cpp b/modules/line_descriptor/samples/compute_descriptors.cpp index 0c7526734..326dace13 100644 --- a/modules/line_descriptor/samples/compute_descriptors.cpp +++ b/modules/line_descriptor/samples/compute_descriptors.cpp @@ -61,19 +61,6 @@ static void help() << std::endl; } -inline void writeMat( cv::Mat m, std::string name, int n ) -{ - std::stringstream ss; - std::string s; - ss << n; - ss >> s; - std::string fileNameConf = name + s; - cv::FileStorage fsConf( fileNameConf, cv::FileStorage::WRITE ); - fsConf << "m" << m; - - fsConf.release(); -} - int main( int argc, char** argv ) { /* get parameters from command line */ @@ -103,18 +90,9 @@ int main( int argc, char** argv ) std::vector keylines; bd->detect( imageMat, keylines, mask ); - /* select only lines from first octave */ - /*std::vector octave0; - for ( size_t i = 0; i < keylines.size(); i++ ) - { - if( keylines[i].octave == 0 ) - octave0.push_back( keylines[i] ); - }*/ - /* compute descriptors */ cv::Mat descriptors; bd->compute( imageMat, keylines, descriptors); - writeMat( descriptors, "bd_descriptors", 1 ); } diff --git a/modules/line_descriptor/samples/knn_matching.cpp b/modules/line_descriptor/samples/knn_matching.cpp index 880d4898f..088697129 100644 --- a/modules/line_descriptor/samples/knn_matching.cpp +++ b/modules/line_descriptor/samples/knn_matching.cpp @@ -192,5 +192,8 @@ int main( int argc, char** argv ) /* require knn match */ bdm->knnMatch( descr1, descr2, matches, 6 ); + + + } diff --git a/modules/line_descriptor/samples/matching.cpp b/modules/line_descriptor/samples/matching.cpp index 6385226ad..568d65274 100644 --- a/modules/line_descriptor/samples/matching.cpp +++ b/modules/line_descriptor/samples/matching.cpp @@ -92,7 +92,7 @@ int main( int argc, char** argv ) cv::Mat mask2 = Mat::ones( imageMat2.size(), CV_8UC1 ); /* create a pointer to a BinaryDescriptor object with default parameters */ - Ptr bd = BinaryDescriptor::createBinaryDescriptor(); + Ptr bd = BinaryDescriptor::createBinaryDescriptor( ); /* compute lines and descriptors */ std::vector keylines1, keylines2; @@ -101,27 +101,48 @@ int main( int argc, char** argv ) ( *bd )( imageMat1, mask1, keylines1, descr1, false, false ); ( *bd )( imageMat2, mask2, keylines2, descr2, false, false ); + /* select keylines from first octave and their descriptors */ + std::vector lbd_octave1, lbd_octave2; + Mat left_lbd, right_lbd; + for ( int i = 0; i < (int) keylines1.size(); i++ ) + { + if( keylines1[i].octave == 0 ) + { + lbd_octave1.push_back( keylines1[i] ); + left_lbd.push_back( descr1.row( i ) ); + } + } + + for ( int j = 0; j < (int) keylines2.size(); j++ ) + { + if( keylines2[j].octave == 0 ) + { + lbd_octave2.push_back( keylines2[j] ); + right_lbd.push_back( descr2.row( j ) ); + } + } /* create a BinaryDescriptorMatcher object */ Ptr bdm = BinaryDescriptorMatcher::createBinaryDescriptorMatcher(); /* require match */ std::vector matches; - bdm->match( descr1, descr2, matches ); + bdm->match( left_lbd, right_lbd, matches ); /* select best matches */ std::vector good_matches; - for(int i = 0; i<(int)matches.size(); i++) + for ( int i = 0; i < (int) matches.size(); i++ ) { - if(matches[i].distance < MATCHES_DIST_THRESHOLD) - good_matches.push_back(matches[i]); + if( matches[i].distance < MATCHES_DIST_THRESHOLD ) + good_matches.push_back( matches[i] ); } /* plot matches */ cv::Mat outImg; + cv::Mat scaled1, scaled2; std::vector mask( matches.size(), 1 ); - drawLineMatches( imageMat1, keylines1, imageMat2, keylines2, good_matches , outImg, Scalar::all( -1 ), Scalar::all( -1 ), mask, - DrawLinesMatchesFlags::DEFAULT ); + drawLineMatches( imageMat1, lbd_octave1, imageMat2, lbd_octave2, good_matches, outImg, Scalar::all( -1 ), Scalar::all( -1 ), mask, + DrawLinesMatchesFlags::DEFAULT ); imshow( "Matches", outImg ); waitKey(); @@ -132,48 +153,55 @@ int main( int argc, char** argv ) /* detect lines */ std::vector klsd1, klsd2; Mat lsd_descr1, lsd_descr2; - lsd->detect(imageMat1, klsd1, 2, 2, mask1); - lsd->detect(imageMat2, klsd2, 2, 2, mask2); - - /* select lines from first octave */ - std::vector octave0_1, octave0_2; - for(int i = 0; i<(int)klsd1.size(); i++) - { - if(klsd1[i].octave == 0) - octave0_1.push_back(klsd1[i]); - } - - for(int j = 0; j<(int)klsd1.size(); j++) - { - if(klsd2[j].octave == 0) - octave0_2.push_back(klsd2[j]); - } - + lsd->detect( imageMat1, klsd1, 2, 2, mask1 ); + lsd->detect( imageMat2, klsd2, 2, 2, mask2 ); /* compute descriptors for lines from first octave */ - bd->compute( imageMat1, octave0_1, lsd_descr1 ); - bd->compute( imageMat2, octave0_2, lsd_descr2 ); + bd->compute( imageMat1, klsd1, lsd_descr1 ); + bd->compute( imageMat2, klsd2, lsd_descr2 ); + + /* select lines and descriptors from first octave */ + std::vector octave0_1, octave0_2; + Mat leftDEscr, rightDescr; + for ( int i = 0; i < (int) klsd1.size(); i++ ) + { + if( klsd1[i].octave == 1 ) + { + octave0_1.push_back( klsd1[i] ); + leftDEscr.push_back( lsd_descr1.row( i ) ); + } + } + + for ( int j = 0; j < (int) klsd2.size(); j++ ) + { + if( klsd2[j].octave == 1 ) + { + octave0_2.push_back( klsd2[j] ); + rightDescr.push_back( lsd_descr2.row( j ) ); + } + } /* compute matches */ std::vector lsd_matches; - bdm->match( lsd_descr1, lsd_descr2, lsd_matches); + bdm->match( leftDEscr, rightDescr, lsd_matches ); /* select best matches */ good_matches.clear(); - for(int i = 0; i<(int)lsd_matches.size(); i++) - { - if(lsd_matches[i].distance < MATCHES_DIST_THRESHOLD) - good_matches.push_back(lsd_matches[i]); - } - + for ( int i = 0; i < (int) lsd_matches.size(); i++ ) + { + if( lsd_matches[i].distance < MATCHES_DIST_THRESHOLD ) + good_matches.push_back( lsd_matches[i] ); + } /* plot matches */ cv::Mat lsd_outImg; + resize( imageMat1, imageMat1, Size( imageMat1.cols / 2, imageMat1.rows / 2 ) ); + resize( imageMat2, imageMat2, Size( imageMat2.cols / 2, imageMat2.rows / 2 ) ); std::vector lsd_mask( matches.size(), 1 ); - drawLineMatches( imageMat1, octave0_1, imageMat2, octave0_2, good_matches , lsd_outImg, Scalar::all( -1 ), Scalar::all( -1 ), lsd_mask, + drawLineMatches( imageMat1, octave0_1, imageMat2, octave0_2, good_matches, lsd_outImg, Scalar::all( -1 ), Scalar::all( -1 ), lsd_mask, DrawLinesMatchesFlags::DEFAULT ); - imshow("LSD matches", lsd_outImg); + imshow( "LSD matches", lsd_outImg ); waitKey(); } diff --git a/modules/line_descriptor/src/LSDDetector.cpp b/modules/line_descriptor/src/LSDDetector.cpp index 7315eb09b..3cf760a58 100644 --- a/modules/line_descriptor/src/LSDDetector.cpp +++ b/modules/line_descriptor/src/LSDDetector.cpp @@ -56,7 +56,7 @@ void LSDDetector::computeGaussianPyramid( const Mat& image, int numOctaves, int /* insert input image into pyramid */ cv::Mat currentMat = image.clone(); - cv::GaussianBlur( currentMat, currentMat, cv::Size( 5, 5 ), 1 ); + //cv::GaussianBlur( currentMat, currentMat, cv::Size( 5, 5 ), 1 ); gaussianPyrs.push_back( currentMat ); /* fill Gaussian pyramid */ diff --git a/modules/line_descriptor/src/binary_descriptor.cpp b/modules/line_descriptor/src/binary_descriptor.cpp index 43f6ebb04..3f04f1d54 100644 --- a/modules/line_descriptor/src/binary_descriptor.cpp +++ b/modules/line_descriptor/src/binary_descriptor.cpp @@ -111,6 +111,46 @@ int BinaryDescriptor::getWidthOfBand() void BinaryDescriptor::setWidthOfBand( int width ) { params.widthOfBand_ = width; + + /* reserve enough space for EDLine objects and images in Gaussian pyramid */ + edLineVec_.resize( params.numOfOctave_ ); + images_sizes.resize( params.numOfOctave_ ); + + for ( int i = 0; i < params.numOfOctave_; i++ ) + edLineVec_[i] = Ptr( new EDLineDetector() ); + + /* prepare a vector to host local weights F_l*/ + gaussCoefL_.resize( params.widthOfBand_ * 3 ); + + /* compute center of central band (every computation involves 2-3 bands) */ + double u = ( params.widthOfBand_ * 3 - 1 ) / 2; + + /* compute exponential part of F_l */ + double sigma = ( params.widthOfBand_ * 2 + 1 ) / 2; // (widthOfBand_*2+1)/2; + double invsigma2 = -1 / ( 2 * sigma * sigma ); + + /* compute all local weights */ + double dis; + for ( int i = 0; i < params.widthOfBand_ * 3; i++ ) + { + dis = i - u; + gaussCoefL_[i] = exp( dis * dis * invsigma2 ); + } + + /* prepare a vector for global weights F_g*/ + gaussCoefG_.resize( NUM_OF_BANDS * params.widthOfBand_ ); + + /* compute center of LSR */ + u = ( NUM_OF_BANDS * params.widthOfBand_ - 1 ) / 2; + + /* compute exponential part of F_g */ + sigma = u; + invsigma2 = -1 / ( 2 * sigma * sigma ); + for ( int i = 0; i < NUM_OF_BANDS * params.widthOfBand_; i++ ) + { + dis = i - u; + gaussCoefG_[i] = exp( dis * dis * invsigma2 ); + } } int BinaryDescriptor::getReductionRatio() @@ -351,6 +391,12 @@ unsigned char BinaryDescriptor::binaryConversion( float* f1, float* f2 ) /* requires line detection (only one image) */ void BinaryDescriptor::detect( const Mat& image, CV_OUT std::vector& keylines, const Mat& mask ) { + if( image.data == NULL ) + { + std::cout << "Error: input image for detection is empty" << std::endl; + return; + } + if( mask.data != NULL && ( mask.size() != image.size() || mask.type() != CV_8UC1 ) ) throw std::runtime_error( "Mask error while detecting lines: please check its dimensions and that data type is CV_8UC1" ); @@ -361,6 +407,13 @@ void BinaryDescriptor::detect( const Mat& image, CV_OUT std::vector& ke /* requires line detection (more than one image) */ void BinaryDescriptor::detect( const std::vector& images, std::vector >& keylines, const std::vector& masks ) const { + + if( images.size() == 0 ) + { + std::cout << "Error: input image for detection is empty" << std::endl; + return; + } + /* detect lines from each image */ for ( size_t counter = 0; counter < images.size(); counter++ ) { @@ -444,18 +497,18 @@ void BinaryDescriptor::detectImpl( const Mat& imageSrc, std::vector& ke } /* requires descriptors computation (only one image) */ -void BinaryDescriptor::compute( const Mat& image, CV_OUT CV_IN_OUT std::vector& keylines, CV_OUT Mat& descriptors, bool returnFloatDescr, - bool useDetectionData ) const +void BinaryDescriptor::compute( const Mat& image, CV_OUT CV_IN_OUT std::vector& keylines, CV_OUT Mat& descriptors, + bool returnFloatDescr ) const { - computeImpl( image, keylines, descriptors, returnFloatDescr, useDetectionData ); + computeImpl( image, keylines, descriptors, returnFloatDescr, false ); } /* requires descriptors computation (more than one image) */ void BinaryDescriptor::compute( const std::vector& images, std::vector >& keylines, std::vector& descriptors, - bool returnFloatDescr, bool useDetectionData ) const + bool returnFloatDescr ) const { for ( size_t i = 0; i < images.size(); i++ ) - computeImpl( images[i], keylines[i], descriptors[i], returnFloatDescr, useDetectionData ); + computeImpl( images[i], keylines[i], descriptors[i], returnFloatDescr, false ); } /* implementation of descriptors computation */ @@ -621,7 +674,6 @@ int BinaryDescriptor::OctaveKeyLines( cv::Mat& image, ScaleLines &keyLines ) /* loop over number of octaves */ for ( int octaveCount = 0; octaveCount < params.numOfOctave_; octaveCount++ ) { - /* matrix storing results from blurring processes */ cv::Mat blur; @@ -631,7 +683,7 @@ int BinaryDescriptor::OctaveKeyLines( cv::Mat& image, ScaleLines &keyLines ) images_sizes[octaveCount] = blur.size(); /* for current octave, extract lines */ - if( ( edLineVec_[octaveCount]->EDline( blur, true ) ) != 1 ) + if( ( edLineVec_[octaveCount]->EDline( blur ) ) != 1 ) { return -1; } diff --git a/modules/line_descriptor/src/draw.cpp b/modules/line_descriptor/src/draw.cpp index b265ca3a1..4d046c2c8 100644 --- a/modules/line_descriptor/src/draw.cpp +++ b/modules/line_descriptor/src/draw.cpp @@ -97,13 +97,15 @@ void drawLineMatches( const Mat& img1, const std::vector& keylines1, co for ( size_t i = 0; i < keylines1.size(); i++ ) { KeyLine k1 = keylines1[i]; - line( outImg, Point2f( k1.startPointX, k1.startPointY ), Point2f( k1.endPointX, k1.endPointY ), singleLineColorRGB, 2 ); + //line( outImg, Point2f( k1.startPointX, k1.startPointY ), Point2f( k1.endPointX, k1.endPointY ), singleLineColorRGB, 2 ); + line( outImg, Point2f( k1.sPointInOctaveX, k1.sPointInOctaveY ), Point2f( k1.ePointInOctaveX, k1.ePointInOctaveY ), singleLineColorRGB, 2 ); + } for ( size_t j = 0; j < keylines2.size(); j++ ) { KeyLine k2 = keylines2[j]; - line( outImg, Point2f( k2.startPointX + offset, k2.startPointY ), Point2f( k2.endPointX + offset, k2.endPointY ), singleLineColorRGB, 2 ); + line( outImg, Point2f( k2.sPointInOctaveX + offset, k2.sPointInOctaveY ), Point2f( k2.ePointInOctaveX + offset, k2.ePointInOctaveY ), singleLineColorRGB, 2 ); } } @@ -133,13 +135,21 @@ void drawLineMatches( const Mat& img1, const std::vector& keylines1, co matchColorRGB = matchColor; /* draw lines if necessary */ - line( outImg, Point2f( left.startPointX, left.startPointY ), Point2f( left.endPointX, left.endPointY ), singleLineColorRGB, 2 ); +// line( outImg, Point2f( left.startPointX, left.startPointY ), Point2f( left.endPointX, left.endPointY ), singleLineColorRGB, 2 ); +// +// line( outImg, Point2f( right.startPointX + offset, right.startPointY ), Point2f( right.endPointX + offset, right.endPointY ), singleLineColorRGB, +// 2 ); +// +// /* link correspondent lines */ +// line( outImg, Point2f( left.startPointX, left.startPointY ), Point2f( right.startPointX + offset, right.startPointY ), matchColorRGB, 1 ); - line( outImg, Point2f( right.startPointX + offset, right.startPointY ), Point2f( right.endPointX + offset, right.endPointY ), singleLineColorRGB, - 2 ); + line( outImg, Point2f( left.sPointInOctaveX, left.sPointInOctaveY ), Point2f( left.ePointInOctaveX, left.ePointInOctaveY ), singleLineColorRGB, 2 ); - /* link correspondent lines */ - line( outImg, Point2f( left.startPointX, left.startPointY ), Point2f( right.startPointX + offset, right.startPointY ), matchColorRGB, 1 ); + line( outImg, Point2f( right.sPointInOctaveX + offset, right.sPointInOctaveY ), Point2f( right.ePointInOctaveX + offset, right.ePointInOctaveY ), singleLineColorRGB, + 2 ); + + /* link correspondent lines */ + line( outImg, Point2f( left.sPointInOctaveX, left.sPointInOctaveY ), Point2f( right.sPointInOctaveX + offset, right.sPointInOctaveY ), matchColorRGB, 1 ); } } } diff --git a/modules/line_descriptor/src/ed_line_detector.cpp b/modules/line_descriptor/src/ed_line_detector.cpp index 8f1e82178..f1fbe81c6 100644 --- a/modules/line_descriptor/src/ed_line_detector.cpp +++ b/modules/line_descriptor/src/ed_line_detector.cpp @@ -117,18 +117,12 @@ EDLineDetector::~EDLineDetector() } } -int EDLineDetector::EdgeDrawing( cv::Mat &image, EdgeChains &edgeChains, bool smoothed ) +int EDLineDetector::EdgeDrawing( cv::Mat &image, EdgeChains &edgeChains ) { imageWidth = image.cols; imageHeight = image.rows; unsigned int pixelNum = imageWidth * imageHeight; - /*if( !smoothed ) - { //input image hasn't been smoothed. - cv::Mat InImage = image.clone(); - cv::GaussianBlur( InImage, image, cv::Size( ksize_, ksize_ ), sigma_ ); - }*/ - unsigned int edgePixelArraySize = pixelNum / 5; unsigned int maxNumOfEdge = edgePixelArraySize / 20; //compute dx, dy images @@ -925,12 +919,12 @@ int EDLineDetector::EdgeDrawing( cv::Mat &image, EdgeChains &edgeChains, bool sm return 1; } -int EDLineDetector::EDline( cv::Mat &image, LineChains &lines, bool smoothed ) +int EDLineDetector::EDline( cv::Mat &image, LineChains &lines ) { //first, call EdgeDrawing function to extract edges EdgeChains edges; - if( ( EdgeDrawing( image, edges, smoothed ) ) != 1 ) + if( ( EdgeDrawing( image, edges ) ) != 1 ) { cout << "Line Detection not finished" << endl; return -1; @@ -1411,9 +1405,9 @@ bool EDLineDetector::LineValidation_( unsigned int *xCors, unsigned int *yCors, } } -int EDLineDetector::EDline( cv::Mat &image, bool smoothed ) +int EDLineDetector::EDline( cv::Mat &image ) { - if( ( EDline( image, lines_, smoothed ) ) != 1 ) + if( ( EDline( image, lines_/*, smoothed*/ ) ) != 1 ) { return -1; } diff --git a/modules/line_descriptor/test/test_detector_regression.cpp b/modules/line_descriptor/test/test_detector_regression.cpp new file mode 100644 index 000000000..c4082ebee --- /dev/null +++ b/modules/line_descriptor/test/test_detector_regression.cpp @@ -0,0 +1,341 @@ +/*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) 2014, Biagio Montesano, all rights reserved. + // Third party copyrights are property of their respective owners. + // + // Redistribution and use in source and binary forms, with or without modification, + // are permitted provided that the following conditions are met: + // + // * Redistribution's of source code must retain the above copyright notice, + // this list of conditions and the following disclaimer. + // + // * Redistribution's in binary form must reproduce the above copyright notice, + // this list of conditions and the following disclaimer in the documentation + // and/or other materials provided with the distribution. + // + // * The name of the copyright holders may not be used to endorse or promote products + // derived from this software without specific prior written permission. + // + // This software is provided by the copyright holders and contributors "as is" and + // any express or implied warranties, including, but not limited to, the implied + // warranties of merchantability and fitness for a particular purpose are disclaimed. + // In no event shall the Intel Corporation or contributors be liable for any direct, + // indirect, incidental, special, exemplary, or consequential damages + // (including, but not limited to, procurement of substitute goods or services; + // loss of use, data, or profits; or business interruption) however caused + // and on any theory of liability, whether in contract, strict liability, + // or tort (including negligence or otherwise) arising in any way out of + // the use of this software, even if advised of the possibility of such damage. + // + //M*/ + +#include "test_precomp.hpp" + +using namespace cv; + +/****************************************************************************************\ +* Regression tests for line detector comparing keylines. * + \****************************************************************************************/ + +const std::string LINE_DESCRIPTOR_DIR = "line_descriptor"; +const std::string IMAGE_FILENAME = "cameraman.jpg"; +const std::string DETECTOR_DIR = LINE_DESCRIPTOR_DIR + "/detectors"; + +class CV_BinaryDescriptorDetectorTest : public cvtest::BaseTest +{ + + public: + CV_BinaryDescriptorDetectorTest( std::string fs ) + { + bd = BinaryDescriptor::createBinaryDescriptor(); + fs_name = fs; + } + + protected: + bool isSimilarKeylines( const KeyLine& k1, const KeyLine& k2 ); + void compareKeylineSets( const std::vector& validKeylines, const std::vector& calcKeylines ); + void createMatFromVec( const std::vector& linesVec, Mat& output ); + void createVecFromMat( Mat& inputMat, std::vector& output ); + + void emptyDataTest(); + void regressionTest(); + virtual void run( int ); + + Ptr bd; + std::string fs_name; + +}; + +void CV_BinaryDescriptorDetectorTest::emptyDataTest() +{ + /* one image */ + Mat image; + std::vector keylines; + + try + { + bd->detect( image, keylines ); + } + + catch ( ... ) + { + ts->printf( cvtest::TS::LOG, "detect() on empty image must return empty keylines vector (1).\n" ); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT ); + } + + if( !keylines.empty() ) + { + ts->printf( cvtest::TS::LOG, "detect() on empty image must return empty keylines vector (1).\n" ); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT ); + return; + } + + /* more than one image */ + std::vector images; + std::vector > keylineCollection; + + try + { + bd->detect( images, keylineCollection ); + } + + catch ( ... ) + { + ts->printf( cvtest::TS::LOG, "detect() on empty image vector must not generate exception (2).\n" ); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT ); + } + +} + +void CV_BinaryDescriptorDetectorTest::createMatFromVec( const std::vector& linesVec, Mat& output ) +{ + output = Mat( (int) linesVec.size(), 17, CV_32FC1 ); + + for ( int i = 0; i < (int) linesVec.size(); i++ ) + { + std::vector klData; + KeyLine kl = linesVec[i]; + klData.push_back( kl.angle ); + klData.push_back( kl.class_id ); + klData.push_back( kl.ePointInOctaveX ); + klData.push_back( kl.ePointInOctaveY ); + klData.push_back( kl.endPointX ); + klData.push_back( kl.endPointY ); + klData.push_back( kl.lineLength ); + klData.push_back( kl.numOfPixels ); + klData.push_back( kl.octave ); + klData.push_back( kl.pt.x ); + klData.push_back( kl.pt.y ); + klData.push_back( kl.response ); + klData.push_back( kl.sPointInOctaveX ); + klData.push_back( kl.sPointInOctaveY ); + klData.push_back( kl.size ); + klData.push_back( kl.startPointX ); + klData.push_back( kl.startPointY ); + + float* pointerToRow = output.ptr( i ); + for ( int j = 0; j < 17; j++ ) + { + *pointerToRow = klData[j]; + pointerToRow++; + } + } +} + +void CV_BinaryDescriptorDetectorTest::createVecFromMat( Mat& inputMat, std::vector& output ) +{ + for ( int i = 0; i < inputMat.rows; i++ ) + { + std::vector tempFloat; + KeyLine kl; + float* pointerToRow = inputMat.ptr( i ); + + for ( int j = 0; j < 17; j++ ) + { + tempFloat.push_back( *pointerToRow ); + pointerToRow++; + } + + kl.angle = tempFloat[0]; + kl.class_id = tempFloat[1]; + kl.ePointInOctaveX = tempFloat[2]; + kl.ePointInOctaveY = tempFloat[3]; + kl.endPointX = tempFloat[4]; + kl.endPointY = tempFloat[5]; + kl.lineLength = tempFloat[6]; + kl.numOfPixels = tempFloat[7]; + kl.octave = tempFloat[8]; + kl.pt.x = tempFloat[9]; + kl.pt.y = tempFloat[10]; + kl.response = tempFloat[11]; + kl.sPointInOctaveX = tempFloat[12]; + kl.sPointInOctaveY = tempFloat[13]; + kl.size = tempFloat[14]; + kl.startPointX = tempFloat[15]; + kl.startPointY = tempFloat[16]; + + output.push_back( kl ); + } +} + +bool CV_BinaryDescriptorDetectorTest::isSimilarKeylines( const KeyLine& k1, const KeyLine& k2 ) +{ + const float maxPtDif = 1.f; + const float maxSizeDif = 1.f; + const float maxAngleDif = 2.f; + const float maxResponseDif = 0.1f; + + float dist = (float) norm( k1.pt - k2.pt ); + return ( dist < maxPtDif && fabs( k1.size - k2.size ) < maxSizeDif && abs( k1.angle - k2.angle ) < maxAngleDif + && abs( k1.response - k2.response ) < maxResponseDif && k1.octave == k2.octave && k1.class_id == k2.class_id ); +} + +void CV_BinaryDescriptorDetectorTest::compareKeylineSets( const std::vector& validKeylines, const std::vector& calcKeylines ) +{ + const float maxCountRatioDif = 0.01f; + + // Compare counts of validation and calculated keylines. + float countRatio = (float) validKeylines.size() / (float) calcKeylines.size(); + if( countRatio < 1 - maxCountRatioDif || countRatio > 1.f + maxCountRatioDif ) + { + ts->printf( cvtest::TS::LOG, "Bad keylines count ratio (validCount = %d, calcCount = %d).\n", validKeylines.size(), calcKeylines.size() ); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_OUTPUT ); + return; + } + + int progress = 0; + int progressCount = (int) ( validKeylines.size() * calcKeylines.size() ); + int badLineCount = 0; + int commonLineCount = max( (int) validKeylines.size(), (int) calcKeylines.size() ); + for ( size_t v = 0; v < validKeylines.size(); v++ ) + { + int nearestIdx = -1; + float minDist = std::numeric_limits::max(); + + for ( size_t c = 0; c < calcKeylines.size(); c++ ) + { + progress = update_progress( progress, (int) ( v * calcKeylines.size() + c ), progressCount, 0 ); + float curDist = (float) norm( calcKeylines[c].pt - validKeylines[v].pt ); + if( curDist < minDist ) + { + minDist = curDist; + nearestIdx = (int) c; + } + } + + assert( minDist >= 0 ); + if( !isSimilarKeylines( validKeylines[v], calcKeylines[nearestIdx] ) ) + badLineCount++; + } + + ts->printf( cvtest::TS::LOG, "badLineCount = %d; validLineCount = %d; calcLineCount = %d\n", badLineCount, validKeylines.size(), + calcKeylines.size() ); + + if( badLineCount > 0.9 * commonLineCount ) + { + ts->printf( cvtest::TS::LOG, " - Bad accuracy!\n" ); + ts->set_failed_test_info( cvtest::TS::FAIL_BAD_ACCURACY ); + return; + } + + ts->printf( cvtest::TS::LOG, " - OK\n" ); +} + +void CV_BinaryDescriptorDetectorTest::regressionTest() +{ + assert( bd ); + std::string imgFilename = std::string( ts->get_data_path() ) + LINE_DESCRIPTOR_DIR + "/" + IMAGE_FILENAME; + std::string resFilename = std::string( ts->get_data_path() ) + DETECTOR_DIR + "/" + fs_name + ".yaml"; + + std::cout << "PATH " << std::string( ts->get_data_path() ) << std::endl; + + // Read the test image. + Mat image = imread( imgFilename ); + if( image.empty() ) + { + ts->printf( cvtest::TS::LOG, "Image %s can not be read.\n", imgFilename.c_str() ); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA ); + return; + } + + // open a storage for reading + FileStorage fs( resFilename, FileStorage::READ ); + + // Compute keylines. + std::vector calcKeylines; + bd->detect( image, calcKeylines ); + + if( fs.isOpened() ) // Compare computed and valid keylines. + { + // Read validation keylines set. + std::vector validKeylines; + Mat storedKeylines; + fs["keylines"] >> storedKeylines; + createVecFromMat( storedKeylines, validKeylines ); + + if( validKeylines.empty() ) + { + ts->printf( cvtest::TS::LOG, "keylines can not be read.\n" ); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA ); + return; + } + + compareKeylineSets( validKeylines, calcKeylines ); + } + + else // Write detector parameters and computed keylines as validation data. + { + fs.open( resFilename, FileStorage::WRITE ); + if( !fs.isOpened() ) + { + ts->printf( cvtest::TS::LOG, "File %s can not be opened to write.\n", resFilename.c_str() ); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA ); + return; + } + + else + { + fs << "detector_params" << "{"; + bd->write( fs ); + fs << "}"; + Mat lines; + createMatFromVec( calcKeylines, lines ); + fs << "keylines" << lines; + } + } +} + +void CV_BinaryDescriptorDetectorTest::run( int ) +{ + if( !bd ) + { + ts->printf( cvtest::TS::LOG, "Feature detector is empty.\n" ); + ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA ); + return; + } + + emptyDataTest(); + regressionTest(); + + ts->set_failed_test_info( cvtest::TS::OK ); +} + +/****************************************************************************************\ +* Tests registrations * + \****************************************************************************************/ + +TEST( BinaryDescriptor_Detector, regression ) +{ + CV_BinaryDescriptorDetectorTest test( std::string( "edl_detector_keylines_cameraman" ) ); + test.safe_run(); +} diff --git a/modules/line_descriptor/test/test_main.cpp b/modules/line_descriptor/test/test_main.cpp new file mode 100644 index 000000000..6b2499344 --- /dev/null +++ b/modules/line_descriptor/test/test_main.cpp @@ -0,0 +1,3 @@ +#include "test_precomp.hpp" + +CV_TEST_MAIN("cv") diff --git a/modules/line_descriptor/test/test_precomp.hpp b/modules/line_descriptor/test/test_precomp.hpp new file mode 100644 index 000000000..0cb1a210d --- /dev/null +++ b/modules/line_descriptor/test/test_precomp.hpp @@ -0,0 +1,24 @@ +#ifdef __GNUC__ +# pragma GCC diagnostic ignored "-Wmissing-declarations" +# if defined __clang__ || defined __APPLE__ +# pragma GCC diagnostic ignored "-Wmissing-prototypes" +# pragma GCC diagnostic ignored "-Wextra" +# endif +#endif + +#ifndef __OPENCV_TEST_PRECOMP_HPP__ +#define __OPENCV_TEST_PRECOMP_HPP__ + +#include "opencv2/opencv.hpp" +#include "opencv2/ts.hpp" +#include "opencv2/imgproc.hpp" +#include "opencv2/features2d.hpp" +#include "opencv2/highgui.hpp" +#include "opencv2/line_descriptor.hpp" +#include + + +#include +#include + +#endif