a LOT of obsolete stuff has been moved to the legacy module.

This commit is contained in:
Vadim Pisarevsky
2012-03-30 12:19:25 +00:00
parent 7e5726e251
commit beb7fc3c92
42 changed files with 3711 additions and 1960 deletions

View File

@@ -0,0 +1,264 @@
/*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 "test_precomp.hpp"
#include <algorithm>
#include <vector>
#include <iostream>
using namespace cv;
using namespace cv::flann;
//--------------------------------------------------------------------------------
class NearestNeighborTest : public cvtest::BaseTest
{
public:
NearestNeighborTest() {}
protected:
static const int minValue = 0;
static const int maxValue = 1;
static const int dims = 30;
static const int featuresCount = 2000;
static const int K = 1; // * should also test 2nd nn etc.?
virtual void run( int start_from );
virtual void createModel( const Mat& data ) = 0;
virtual int findNeighbors( Mat& points, Mat& neighbors ) = 0;
virtual int checkGetPoins( const Mat& data );
virtual int checkFindBoxed();
virtual int checkFind( const Mat& data );
virtual void releaseModel() = 0;
};
int NearestNeighborTest::checkGetPoins( const Mat& )
{
return cvtest::TS::OK;
}
int NearestNeighborTest::checkFindBoxed()
{
return cvtest::TS::OK;
}
int NearestNeighborTest::checkFind( const Mat& data )
{
int code = cvtest::TS::OK;
int pointsCount = 1000;
float noise = 0.2f;
RNG rng;
Mat points( pointsCount, dims, CV_32FC1 );
Mat results( pointsCount, K, CV_32SC1 );
std::vector<int> fmap( pointsCount );
for( int pi = 0; pi < pointsCount; pi++ )
{
int fi = rng.next() % featuresCount;
fmap[pi] = fi;
for( int d = 0; d < dims; d++ )
points.at<float>(pi, d) = data.at<float>(fi, d) + rng.uniform(0.0f, 1.0f) * noise;
}
code = findNeighbors( points, results );
if( code == cvtest::TS::OK )
{
int correctMatches = 0;
for( int pi = 0; pi < pointsCount; pi++ )
{
if( fmap[pi] == results.at<int>(pi, 0) )
correctMatches++;
}
double correctPerc = correctMatches / (double)pointsCount;
if (correctPerc < .75)
{
ts->printf( cvtest::TS::LOG, "correct_perc = %d\n", correctPerc );
code = cvtest::TS::FAIL_BAD_ACCURACY;
}
}
return code;
}
void NearestNeighborTest::run( int /*start_from*/ ) {
int code = cvtest::TS::OK, tempCode;
Mat desc( featuresCount, dims, CV_32FC1 );
randu( desc, Scalar(minValue), Scalar(maxValue) );
createModel( desc );
tempCode = checkGetPoins( desc );
if( tempCode != cvtest::TS::OK )
{
ts->printf( cvtest::TS::LOG, "bad accuracy of GetPoints \n" );
code = tempCode;
}
tempCode = checkFindBoxed();
if( tempCode != cvtest::TS::OK )
{
ts->printf( cvtest::TS::LOG, "bad accuracy of FindBoxed \n" );
code = tempCode;
}
tempCode = checkFind( desc );
if( tempCode != cvtest::TS::OK )
{
ts->printf( cvtest::TS::LOG, "bad accuracy of Find \n" );
code = tempCode;
}
releaseModel();
ts->set_failed_test_info( code );
}
//--------------------------------------------------------------------------------
class CV_LSHTest : public NearestNeighborTest
{
public:
CV_LSHTest() {}
protected:
virtual void createModel( const Mat& data );
virtual int findNeighbors( Mat& points, Mat& neighbors );
virtual void releaseModel();
struct CvLSH* lsh;
CvMat desc;
};
void CV_LSHTest::createModel( const Mat& data )
{
desc = data;
lsh = cvCreateMemoryLSH( data.cols, data.rows, 70, 20, CV_32FC1 );
cvLSHAdd( lsh, &desc );
}
int CV_LSHTest::findNeighbors( Mat& points, Mat& neighbors )
{
const int emax = 20;
Mat dist( points.rows, neighbors.cols, CV_64FC1);
CvMat _dist = dist, _points = points, _neighbors = neighbors;
cvLSHQuery( lsh, &_points, &_neighbors, &_dist, neighbors.cols, emax );
return cvtest::TS::OK;
}
void CV_LSHTest::releaseModel()
{
cvReleaseLSH( &lsh );
}
//--------------------------------------------------------------------------------
class CV_FeatureTreeTest_C : public NearestNeighborTest
{
public:
CV_FeatureTreeTest_C() {}
protected:
virtual int findNeighbors( Mat& points, Mat& neighbors );
virtual void releaseModel();
CvFeatureTree* tr;
CvMat desc;
};
int CV_FeatureTreeTest_C::findNeighbors( Mat& points, Mat& neighbors )
{
const int emax = 20;
Mat dist( points.rows, neighbors.cols, CV_64FC1);
CvMat _dist = dist, _points = points, _neighbors = neighbors;
cvFindFeatures( tr, &_points, &_neighbors, &_dist, neighbors.cols, emax );
return cvtest::TS::OK;
}
void CV_FeatureTreeTest_C::releaseModel()
{
cvReleaseFeatureTree( tr );
}
//--------------------------------------
class CV_SpillTreeTest_C : public CV_FeatureTreeTest_C
{
public:
CV_SpillTreeTest_C() {}
protected:
virtual void createModel( const Mat& data );
};
void CV_SpillTreeTest_C::createModel( const Mat& data )
{
desc = data;
tr = cvCreateSpillTree( &desc );
}
//--------------------------------------
class CV_KDTreeTest_C : public CV_FeatureTreeTest_C
{
public:
CV_KDTreeTest_C() {}
protected:
virtual void createModel( const Mat& data );
virtual int checkFindBoxed();
};
void CV_KDTreeTest_C::createModel( const Mat& data )
{
desc = data;
tr = cvCreateKDTree( &desc );
}
int CV_KDTreeTest_C::checkFindBoxed()
{
Mat min(1, dims, CV_32FC1 ), max(1, dims, CV_32FC1 ), indices( 1, 1, CV_32SC1 );
float l = minValue, r = maxValue;
min.setTo(Scalar(l)), max.setTo(Scalar(r));
CvMat _min = min, _max = max, _indices = indices;
// TODO check indices
if( cvFindFeaturesBoxed( tr, &_min, &_max, &_indices ) != featuresCount )
return cvtest::TS::FAIL_BAD_ACCURACY;
return cvtest::TS::OK;
}
TEST(Features2d_LSH, regression) { CV_LSHTest test; test.safe_run(); }
TEST(Features2d_SpillTree, regression) { CV_SpillTreeTest_C test; test.safe_run(); }
TEST(Features2d_KDTree_C, regression) { CV_KDTreeTest_C test; test.safe_run(); }

View File

@@ -0,0 +1,356 @@
/*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 "test_precomp.hpp"
#include "opencv2/video/tracking.hpp"
#include <string>
#include <iostream>
#include <fstream>
#include <iterator>
#include <limits>
using namespace cv;
using namespace std;
class CV_OptFlowTest : public cvtest::BaseTest
{
public:
CV_OptFlowTest();
~CV_OptFlowTest();
protected:
void run(int);
bool runDense(const Point& shift = Point(3, 0));
bool runSparse();
};
CV_OptFlowTest::CV_OptFlowTest() {}
CV_OptFlowTest::~CV_OptFlowTest() {}
Mat copnvert2flow(const Mat& velx, const Mat& vely)
{
Mat flow(velx.size(), CV_32FC2);
for(int y = 0 ; y < flow.rows; ++y)
for(int x = 0 ; x < flow.cols; ++x)
flow.at<Point2f>(y, x) = Point2f(velx.at<float>(y, x), vely.at<float>(y, x));
return flow;
}
void calcOpticalFlowLK( const Mat& prev, const Mat& curr, Size winSize, Mat& flow )
{
Mat velx(prev.size(), CV_32F), vely(prev.size(), CV_32F);
CvMat cvvelx = velx; CvMat cvvely = vely;
CvMat cvprev = prev; CvMat cvcurr = curr;
cvCalcOpticalFlowLK( &cvprev, &cvcurr, winSize, &cvvelx, &cvvely );
flow = copnvert2flow(velx, vely);
}
void calcOpticalFlowBM( const Mat& prev, const Mat& curr, Size bSize, Size shiftSize, Size maxRange, int usePrevious, Mat& flow )
{
Size sz((curr.cols - bSize.width)/shiftSize.width, (curr.rows - bSize.height)/shiftSize.height);
Mat velx(sz, CV_32F), vely(sz, CV_32F);
CvMat cvvelx = velx; CvMat cvvely = vely;
CvMat cvprev = prev; CvMat cvcurr = curr;
cvCalcOpticalFlowBM( &cvprev, &cvcurr, bSize, shiftSize, maxRange, usePrevious, &cvvelx, &cvvely);
flow = copnvert2flow(velx, vely);
}
void calcOpticalFlowHS( const Mat& prev, const Mat& curr, int usePrevious, double lambda, TermCriteria criteria, Mat& flow)
{
Mat velx(prev.size(), CV_32F), vely(prev.size(), CV_32F);
CvMat cvvelx = velx; CvMat cvvely = vely;
CvMat cvprev = prev; CvMat cvcurr = curr;
cvCalcOpticalFlowHS( &cvprev, &cvcurr, usePrevious, &cvvelx, &cvvely, lambda, criteria );
flow = copnvert2flow(velx, vely);
}
void calcAffineFlowPyrLK( const Mat& prev, const Mat& curr,
const vector<Point2f>& prev_features, vector<Point2f>& curr_features,
vector<uchar>& status, vector<float>& track_error, vector<float>& matrices,
TermCriteria criteria = TermCriteria(TermCriteria::COUNT+TermCriteria::EPS,30, 0.01),
Size win_size = Size(15, 15), int level = 3, int flags = 0)
{
CvMat cvprev = prev;
CvMat cvcurr = curr;
size_t count = prev_features.size();
curr_features.resize(count);
status.resize(count);
track_error.resize(count);
matrices.resize(count * 6);
cvCalcAffineFlowPyrLK( &cvprev, &cvcurr, 0, 0,
(const CvPoint2D32f*)&prev_features[0], (CvPoint2D32f*)&curr_features[0], &matrices[0],
(int)count, win_size, level, (char*)&status[0], &track_error[0], criteria, flags );
}
double showFlowAndCalcError(const string& name, const Mat& gray, const Mat& flow,
const Rect& where, const Point& d,
bool showImages = false, bool writeError = false)
{
const int mult = 16;
if (showImages)
{
Mat tmp, cflow;
resize(gray, tmp, gray.size() * mult, 0, 0, INTER_NEAREST);
cvtColor(tmp, cflow, CV_GRAY2BGR);
const float m2 = 0.3f;
const float minVel = 0.1f;
for(int y = 0; y < flow.rows; ++y)
for(int x = 0; x < flow.cols; ++x)
{
Point2f f = flow.at<Point2f>(y, x);
if (f.x * f.x + f.y * f.y > minVel * minVel)
{
Point p1 = Point(x, y) * mult;
Point p2 = Point(cvRound((x + f.x*m2) * mult), cvRound((y + f.y*m2) * mult));
line(cflow, p1, p2, CV_RGB(0, 255, 0));
circle(cflow, Point(x, y) * mult, 2, CV_RGB(255, 0, 0));
}
}
rectangle(cflow, (where.tl() + d) * mult, (where.br() + d - Point(1,1)) * mult, CV_RGB(0, 0, 255));
namedWindow(name, 1); imshow(name, cflow);
}
double angle = atan2((float)d.y, (float)d.x);
double error = 0;
bool all = true;
Mat inner = flow(where);
for(int y = 0; y < inner.rows; ++y)
for(int x = 0; x < inner.cols; ++x)
{
const Point2f f = inner.at<Point2f>(y, x);
if (f.x == 0 && f.y == 0)
continue;
all = false;
double a = atan2(f.y, f.x);
error += fabs(angle - a);
}
double res = all ? numeric_limits<double>::max() : error / (inner.cols * inner.rows);
if (writeError)
cout << "Error " + name << " = " << res << endl;
return res;
}
Mat generateImage(const Size& sz, bool doBlur = true)
{
RNG rng;
Mat mat(sz, CV_8U);
mat = Scalar(0);
for(int y = 0; y < mat.rows; ++y)
for(int x = 0; x < mat.cols; ++x)
mat.at<uchar>(y, x) = (uchar)rng;
if (doBlur)
blur(mat, mat, Size(3, 3));
return mat;
}
Mat generateSample(const Size& sz)
{
Mat smpl(sz, CV_8U);
smpl = Scalar(0);
Point sc(smpl.cols/2, smpl.rows/2);
rectangle(smpl, Point(0,0), sc - Point(1,1), Scalar(255), CV_FILLED);
rectangle(smpl, sc, Point(smpl.cols, smpl.rows), Scalar(255), CV_FILLED);
return smpl;
}
bool CV_OptFlowTest::runDense(const Point& d)
{
Size matSize(40, 40);
Size movSize(8, 8);
Mat smpl = generateSample(movSize);
Mat prev = generateImage(matSize);
Mat curr = prev.clone();
Rect rect(Point(prev.cols/2, prev.rows/2) - Point(movSize.width/2, movSize.height/2), movSize);
Mat flowLK, flowBM, flowHS, flowFB, flowFB_G, flowBM_received, m1;
m1 = prev(rect); smpl.copyTo(m1);
m1 = curr(Rect(rect.tl() + d, rect.br() + d)); smpl.copyTo(m1);
calcOpticalFlowLK( prev, curr, Size(15, 15), flowLK);
calcOpticalFlowBM( prev, curr, Size(15, 15), Size(1, 1), Size(15, 15), 0, flowBM_received);
calcOpticalFlowHS( prev, curr, 0, 5, TermCriteria(TermCriteria::MAX_ITER, 400, 0), flowHS);
calcOpticalFlowFarneback( prev, curr, flowFB, 0.5, 3, std::max(d.x, d.y) + 10, 100, 6, 2, 0);
calcOpticalFlowFarneback( prev, curr, flowFB_G, 0.5, 3, std::max(d.x, d.y) + 10, 100, 6, 2, OPTFLOW_FARNEBACK_GAUSSIAN);
flowBM.create(prev.size(), CV_32FC2);
flowBM = Scalar(0);
Point origin((flowBM.cols - flowBM_received.cols)/2, (flowBM.rows - flowBM_received.rows)/2);
Mat wcp = flowBM(Rect(origin, flowBM_received.size()));
flowBM_received.copyTo(wcp);
double errorLK = showFlowAndCalcError("LK", prev, flowLK, rect, d);
double errorBM = showFlowAndCalcError("BM", prev, flowBM, rect, d);
double errorFB = showFlowAndCalcError("FB", prev, flowFB, rect, d);
double errorFBG = showFlowAndCalcError("FBG", prev, flowFB_G, rect, d);
double errorHS = showFlowAndCalcError("HS", prev, flowHS, rect, d); (void)errorHS;
//waitKey();
const double thres = 0.2;
if (errorLK > thres || errorBM > thres || errorFB > thres || errorFBG > thres /*|| errorHS > thres */)
{
ts->set_failed_test_info(cvtest::TS::FAIL_MISMATCH);
return false;
}
return true;
}
bool CV_OptFlowTest::runSparse()
{
Mat prev = imread(string(ts->get_data_path()) + "optflow/rock_1.bmp", 0);
Mat next = imread(string(ts->get_data_path()) + "optflow/rock_2.bmp", 0);
if (prev.empty() || next.empty())
{
ts->set_failed_test_info( cvtest::TS::FAIL_INVALID_TEST_DATA );
return false;
}
Mat cprev, cnext;
cvtColor(prev, cprev, CV_GRAY2BGR);
cvtColor(next, cnext, CV_GRAY2BGR);
vector<Point2f> prev_pts;
vector<Point2f> next_ptsOpt;
vector<Point2f> next_ptsAff;
vector<uchar> status_Opt;
vector<uchar> status_Aff;
vector<float> error;
vector<float> matrices;
Size netSize(10, 10);
Point2f center = Point(prev.cols/2, prev.rows/2);
for(int i = 0 ; i < netSize.width; ++i)
for(int j = 0 ; j < netSize.width; ++j)
{
Point2f p(i * float(prev.cols)/netSize.width, j * float(prev.rows)/netSize.height);
prev_pts.push_back((p - center) * 0.5f + center);
}
calcOpticalFlowPyrLK( prev, next, prev_pts, next_ptsOpt, status_Opt, error );
calcAffineFlowPyrLK ( prev, next, prev_pts, next_ptsAff, status_Aff, error, matrices);
const double expected_shift = 25;
const double thres = 1;
for(size_t i = 0; i < prev_pts.size(); ++i)
{
circle(cprev, prev_pts[i], 2, CV_RGB(255, 0, 0));
if (status_Opt[i])
{
circle(cnext, next_ptsOpt[i], 2, CV_RGB(0, 0, 255));
Point2f shift = prev_pts[i] - next_ptsOpt[i];
double n = sqrt(shift.ddot(shift));
if (fabs(n - expected_shift) > thres)
{
ts->set_failed_test_info(cvtest::TS::FAIL_MISMATCH);
return false;
}
}
if (status_Aff[i])
{
circle(cnext, next_ptsAff[i], 4, CV_RGB(0, 255, 0));
Point2f shift = prev_pts[i] - next_ptsAff[i];
double n = sqrt(shift.ddot(shift));
if (fabs(n - expected_shift) > thres)
{
ts->set_failed_test_info(cvtest::TS::FAIL_MISMATCH);
return false;
}
}
}
/*namedWindow("P"); imshow("P", cprev);
namedWindow("N"); imshow("N", cnext);
waitKey();*/
return true;
}
void CV_OptFlowTest::run( int /* start_from */)
{
if (!runDense(Point(3, 0)))
return;
if (!runDense(Point(0, 3)))
return;
//if (!runDense(Point(3, 3))) return; //probably LK works incorrectly in this case.
if (!runSparse())
return;
ts->set_failed_test_info(cvtest::TS::OK);
}
TEST(Video_OpticalFlow, accuracy) { CV_OptFlowTest test; test.safe_run(); }

View File

@@ -6,6 +6,7 @@
#include "opencv2/imgproc/imgproc_c.h"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/highgui/highgui_c.h"
#include "opencv2/legacy/legacy.hpp"
#include <iostream>
#endif

View File

@@ -0,0 +1,204 @@
/*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 "test_precomp.hpp"
using namespace cv;
using namespace std;
class CV_PyrSegmentationTest : public cvtest::BaseTest
{
public:
CV_PyrSegmentationTest();
protected:
void run(int);
};
#define SCAN 0
CV_PyrSegmentationTest::CV_PyrSegmentationTest()
{
}
void CV_PyrSegmentationTest::run( int /*start_from*/ )
{
Mat _image_f, _image, _image_s;
const int level = 5;
const double range = 15;
int code = cvtest::TS::OK;
CvPoint _cp[] ={{33,33}, {43,33}, {43,43}, {33,43}};
CvPoint _cp2[] ={{50,50}, {70,50}, {70,70}, {50,70}};
CvPoint* cp = _cp;
CvPoint* cp2 = _cp2;
CvConnectedComp *dst_comp[3];
CvRect rect[3] = {{50,50,21,21}, {0,0,128,128}, {33,33,11,11}};
double a[3] = {441.0, 15822.0, 121.0};
/* ippiPoint cp3[] ={130,130, 150,130, 150,150, 130,150}; */
/* CvPoint cp[] ={0,0, 5,5, 5,0, 10,5, 10,0, 15,5, 15,0}; */
int nPoints = 4;
int block_size = 1000;
CvMemStorage *storage; /* storage for connected component writing */
CvSeq *comp;
RNG& rng = ts->get_rng();
int i, j, iter;
IplImage *image, *image_f, *image_s;
CvSize size = {128, 128};
const int threshold1 = 50, threshold2 = 50;
rect[1].width = size.width;
rect[1].height = size.height;
a[1] = size.width*size.height - a[0] - a[2];
OPENCV_CALL( storage = cvCreateMemStorage( block_size ) );
for( iter = 0; iter < 2; iter++ )
{
int channels = iter == 0 ? 1 : 3;
int mask[] = {0,0,0};
image = cvCreateImage(size, 8, channels );
image_s = cvCloneImage( image );
image_f = cvCloneImage( image );
if( channels == 1 )
{
int color1 = 30, color2 = 110, color3 = 190;
cvSet( image, cvScalarAll(color1));
cvFillPoly( image, &cp, &nPoints, 1, cvScalar(color2));
cvFillPoly( image, &cp2, &nPoints, 1, cvScalar(color3));
}
else
{
CvScalar color1 = CV_RGB(30,30,30), color2 = CV_RGB(255,0,0), color3 = CV_RGB(0,255,0);
assert( channels == 3 );
cvSet( image, color1 );
cvFillPoly( image, &cp, &nPoints, 1, color2);
cvFillPoly( image, &cp2, &nPoints, 1, color3);
}
_image_f = cvarrToMat(image_f);
cvtest::randUni( rng, _image_f, cvScalarAll(0), cvScalarAll(range*2) );
cvAddWeighted( image, 1, image_f, 1, -range, image_f );
cvPyrSegmentation( image_f, image_s,
storage, &comp,
level, threshold1, threshold2 );
if(comp->total != 3)
{
ts->printf( cvtest::TS::LOG,
"The segmentation function returned %d (not 3) components\n", comp->total );
code = cvtest::TS::FAIL_INVALID_OUTPUT;
goto _exit_;
}
/* read the connected components */
dst_comp[0] = (CvConnectedComp*)CV_GET_SEQ_ELEM( CvConnectedComp, comp, 0 );
dst_comp[1] = (CvConnectedComp*)CV_GET_SEQ_ELEM( CvConnectedComp, comp, 1 );
dst_comp[2] = (CvConnectedComp*)CV_GET_SEQ_ELEM( CvConnectedComp, comp, 2 );
/*{
for( i = 0; i < 3; i++ )
{
CvRect r = dst_comp[i]->rect;
cvRectangle( image_s, cvPoint(r.x,r.y), cvPoint(r.x+r.width,r.y+r.height),
CV_RGB(255,255,255), 3, 8, 0 );
}
cvNamedWindow( "test", 1 );
cvShowImage( "test", image_s );
cvWaitKey(0);
}*/
_image = cvarrToMat(image);
_image_s = cvarrToMat(image_s);
code = cvtest::cmpEps2( ts, _image, _image_s, 10, false, "the output image" );
if( code < 0 )
goto _exit_;
for( i = 0; i < 3; i++)
{
for( j = 0; j < 3; j++ )
{
if( !mask[j] && dst_comp[i]->area == a[j] &&
dst_comp[i]->rect.x == rect[j].x &&
dst_comp[i]->rect.y == rect[j].y &&
dst_comp[i]->rect.width == rect[j].width &&
dst_comp[i]->rect.height == rect[j].height )
{
mask[j] = 1;
break;
}
}
if( j == 3 )
{
ts->printf( cvtest::TS::LOG, "The component #%d is incorrect\n", i );
code = cvtest::TS::FAIL_BAD_ACCURACY;
goto _exit_;
}
}
cvReleaseImage(&image_f);
cvReleaseImage(&image);
cvReleaseImage(&image_s);
}
_exit_:
cvReleaseMemStorage( &storage );
cvReleaseImage(&image_f);
cvReleaseImage(&image);
cvReleaseImage(&image_s);
if( code < 0 )
ts->set_failed_test_info( code );
}
TEST(Imgproc_PyrSegmentation, regression) { CV_PyrSegmentationTest test; test.safe_run(); }
/* End of file. */

View File

@@ -0,0 +1,722 @@
/*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*/
/*
This is a regression test for stereo matching algorithms. This test gets some quality metrics
discribed in "A Taxonomy and Evaluation of Dense Two-Frame Stereo Correspondence Algorithms".
Daniel Scharstein, Richard Szeliski
*/
#include "test_precomp.hpp"
#include <limits>
#include <cstdio>
using namespace std;
using namespace cv;
const float EVAL_BAD_THRESH = 1.f;
const int EVAL_TEXTURELESS_WIDTH = 3;
const float EVAL_TEXTURELESS_THRESH = 4.f;
const float EVAL_DISP_THRESH = 1.f;
const float EVAL_DISP_GAP = 2.f;
const int EVAL_DISCONT_WIDTH = 9;
const int EVAL_IGNORE_BORDER = 10;
const int ERROR_KINDS_COUNT = 6;
//============================== quality measuring functions =================================================
/*
Calculate textureless regions of image (regions where the squared horizontal intensity gradient averaged over
a square window of size=evalTexturelessWidth is below a threshold=evalTexturelessThresh) and textured regions.
*/
void computeTextureBasedMasks( const Mat& _img, Mat* texturelessMask, Mat* texturedMask,
int texturelessWidth = EVAL_TEXTURELESS_WIDTH, float texturelessThresh = EVAL_TEXTURELESS_THRESH )
{
if( !texturelessMask && !texturedMask )
return;
if( _img.empty() )
CV_Error( CV_StsBadArg, "img is empty" );
Mat img = _img;
if( _img.channels() > 1)
{
Mat tmp; cvtColor( _img, tmp, CV_BGR2GRAY ); img = tmp;
}
Mat dxI; Sobel( img, dxI, CV_32FC1, 1, 0, 3 );
Mat dxI2; pow( dxI / 8.f/*normalize*/, 2, dxI2 );
Mat avgDxI2; boxFilter( dxI2, avgDxI2, CV_32FC1, Size(texturelessWidth,texturelessWidth) );
if( texturelessMask )
*texturelessMask = avgDxI2 < texturelessThresh;
if( texturedMask )
*texturedMask = avgDxI2 >= texturelessThresh;
}
void checkTypeAndSizeOfDisp( const Mat& dispMap, const Size* sz )
{
if( dispMap.empty() )
CV_Error( CV_StsBadArg, "dispMap is empty" );
if( dispMap.type() != CV_32FC1 )
CV_Error( CV_StsBadArg, "dispMap must have CV_32FC1 type" );
if( sz && (dispMap.rows != sz->height || dispMap.cols != sz->width) )
CV_Error( CV_StsBadArg, "dispMap has incorrect size" );
}
void checkTypeAndSizeOfMask( const Mat& mask, Size sz )
{
if( mask.empty() )
CV_Error( CV_StsBadArg, "mask is empty" );
if( mask.type() != CV_8UC1 )
CV_Error( CV_StsBadArg, "mask must have CV_8UC1 type" );
if( mask.rows != sz.height || mask.cols != sz.width )
CV_Error( CV_StsBadArg, "mask has incorrect size" );
}
void checkDispMapsAndUnknDispMasks( const Mat& leftDispMap, const Mat& rightDispMap,
const Mat& leftUnknDispMask, const Mat& rightUnknDispMask )
{
// check type and size of disparity maps
checkTypeAndSizeOfDisp( leftDispMap, 0 );
if( !rightDispMap.empty() )
{
Size sz = leftDispMap.size();
checkTypeAndSizeOfDisp( rightDispMap, &sz );
}
// check size and type of unknown disparity maps
if( !leftUnknDispMask.empty() )
checkTypeAndSizeOfMask( leftUnknDispMask, leftDispMap.size() );
if( !rightUnknDispMask.empty() )
checkTypeAndSizeOfMask( rightUnknDispMask, rightDispMap.size() );
// check values of disparity maps (known disparity values musy be positive)
double leftMinVal = 0, rightMinVal = 0;
if( leftUnknDispMask.empty() )
minMaxLoc( leftDispMap, &leftMinVal );
else
minMaxLoc( leftDispMap, &leftMinVal, 0, 0, 0, ~leftUnknDispMask );
if( !rightDispMap.empty() )
{
if( rightUnknDispMask.empty() )
minMaxLoc( rightDispMap, &rightMinVal );
else
minMaxLoc( rightDispMap, &rightMinVal, 0, 0, 0, ~rightUnknDispMask );
}
if( leftMinVal < 0 || rightMinVal < 0)
CV_Error( CV_StsBadArg, "known disparity values must be positive" );
}
/*
Calculate occluded regions of reference image (left image) (regions that are occluded in the matching image (right image),
i.e., where the forward-mapped disparity lands at a location with a larger (nearer) disparity) and non occluded regions.
*/
void computeOcclusionBasedMasks( const Mat& leftDisp, const Mat& _rightDisp,
Mat* occludedMask, Mat* nonOccludedMask,
const Mat& leftUnknDispMask = Mat(), const Mat& rightUnknDispMask = Mat(),
float dispThresh = EVAL_DISP_THRESH )
{
if( !occludedMask && !nonOccludedMask )
return;
checkDispMapsAndUnknDispMasks( leftDisp, _rightDisp, leftUnknDispMask, rightUnknDispMask );
Mat rightDisp;
if( _rightDisp.empty() )
{
if( !rightUnknDispMask.empty() )
CV_Error( CV_StsBadArg, "rightUnknDispMask must be empty if _rightDisp is empty" );
rightDisp.create(leftDisp.size(), CV_32FC1);
rightDisp.setTo(Scalar::all(0) );
for( int leftY = 0; leftY < leftDisp.rows; leftY++ )
{
for( int leftX = 0; leftX < leftDisp.cols; leftX++ )
{
if( !leftUnknDispMask.empty() && leftUnknDispMask.at<uchar>(leftY,leftX) )
continue;
float leftDispVal = leftDisp.at<float>(leftY, leftX);
int rightX = leftX - cvRound(leftDispVal), rightY = leftY;
if( rightX >= 0)
rightDisp.at<float>(rightY,rightX) = max(rightDisp.at<float>(rightY,rightX), leftDispVal);
}
}
}
else
_rightDisp.copyTo(rightDisp);
if( occludedMask )
{
occludedMask->create(leftDisp.size(), CV_8UC1);
occludedMask->setTo(Scalar::all(0) );
}
if( nonOccludedMask )
{
nonOccludedMask->create(leftDisp.size(), CV_8UC1);
nonOccludedMask->setTo(Scalar::all(0) );
}
for( int leftY = 0; leftY < leftDisp.rows; leftY++ )
{
for( int leftX = 0; leftX < leftDisp.cols; leftX++ )
{
if( !leftUnknDispMask.empty() && leftUnknDispMask.at<uchar>(leftY,leftX) )
continue;
float leftDispVal = leftDisp.at<float>(leftY, leftX);
int rightX = leftX - cvRound(leftDispVal), rightY = leftY;
if( rightX < 0 && occludedMask )
occludedMask->at<uchar>(leftY, leftX) = 255;
else
{
if( !rightUnknDispMask.empty() && rightUnknDispMask.at<uchar>(rightY,rightX) )
continue;
float rightDispVal = rightDisp.at<float>(rightY, rightX);
if( rightDispVal > leftDispVal + dispThresh )
{
if( occludedMask )
occludedMask->at<uchar>(leftY, leftX) = 255;
}
else
{
if( nonOccludedMask )
nonOccludedMask->at<uchar>(leftY, leftX) = 255;
}
}
}
}
}
/*
Calculate depth discontinuty regions: pixels whose neiboring disparities differ by more than
dispGap, dilated by window of width discontWidth.
*/
void computeDepthDiscontMask( const Mat& disp, Mat& depthDiscontMask, const Mat& unknDispMask = Mat(),
float dispGap = EVAL_DISP_GAP, int discontWidth = EVAL_DISCONT_WIDTH )
{
if( disp.empty() )
CV_Error( CV_StsBadArg, "disp is empty" );
if( disp.type() != CV_32FC1 )
CV_Error( CV_StsBadArg, "disp must have CV_32FC1 type" );
if( !unknDispMask.empty() )
checkTypeAndSizeOfMask( unknDispMask, disp.size() );
Mat curDisp; disp.copyTo( curDisp );
if( !unknDispMask.empty() )
curDisp.setTo( Scalar(numeric_limits<float>::min()), unknDispMask );
Mat maxNeighbDisp; dilate( curDisp, maxNeighbDisp, Mat(3, 3, CV_8UC1, Scalar(1)) );
if( !unknDispMask.empty() )
curDisp.setTo( Scalar(numeric_limits<float>::max()), unknDispMask );
Mat minNeighbDisp; erode( curDisp, minNeighbDisp, Mat(3, 3, CV_8UC1, Scalar(1)) );
depthDiscontMask = max( (Mat)(maxNeighbDisp-disp), (Mat)(disp-minNeighbDisp) ) > dispGap;
if( !unknDispMask.empty() )
depthDiscontMask &= ~unknDispMask;
dilate( depthDiscontMask, depthDiscontMask, Mat(discontWidth, discontWidth, CV_8UC1, Scalar(1)) );
}
/*
Get evaluation masks excluding a border.
*/
Mat getBorderedMask( Size maskSize, int border = EVAL_IGNORE_BORDER )
{
CV_Assert( border >= 0 );
Mat mask(maskSize, CV_8UC1, Scalar(0));
int w = maskSize.width - 2*border, h = maskSize.height - 2*border;
if( w < 0 || h < 0 )
mask.setTo(Scalar(0));
else
mask( Rect(Point(border,border),Size(w,h)) ).setTo(Scalar(255));
return mask;
}
/*
Calculate root-mean-squared error between the computed disparity map (computedDisp) and ground truth map (groundTruthDisp).
*/
float dispRMS( const Mat& computedDisp, const Mat& groundTruthDisp, const Mat& mask )
{
checkTypeAndSizeOfDisp( groundTruthDisp, 0 );
Size sz = groundTruthDisp.size();
checkTypeAndSizeOfDisp( computedDisp, &sz );
int pointsCount = sz.height*sz.width;
if( !mask.empty() )
{
checkTypeAndSizeOfMask( mask, sz );
pointsCount = countNonZero(mask);
}
return 1.f/sqrt((float)pointsCount) * (float)norm(computedDisp, groundTruthDisp, NORM_L2, mask);
}
/*
Calculate fraction of bad matching pixels.
*/
float badMatchPxlsFraction( const Mat& computedDisp, const Mat& groundTruthDisp, const Mat& mask,
float _badThresh = EVAL_BAD_THRESH )
{
int badThresh = cvRound(_badThresh);
checkTypeAndSizeOfDisp( groundTruthDisp, 0 );
Size sz = groundTruthDisp.size();
checkTypeAndSizeOfDisp( computedDisp, &sz );
Mat badPxlsMap;
absdiff( computedDisp, groundTruthDisp, badPxlsMap );
badPxlsMap = badPxlsMap > badThresh;
int pointsCount = sz.height*sz.width;
if( !mask.empty() )
{
checkTypeAndSizeOfMask( mask, sz );
badPxlsMap = badPxlsMap & mask;
pointsCount = countNonZero(mask);
}
return 1.f/pointsCount * countNonZero(badPxlsMap);
}
//===================== regression test for stereo matching algorithms ==============================
const string ALGORITHMS_DIR = "stereomatching/algorithms/";
const string DATASETS_DIR = "stereomatching/datasets/";
const string DATASETS_FILE = "datasets.xml";
const string RUN_PARAMS_FILE = "_params.xml";
const string RESULT_FILE = "_res.xml";
const string LEFT_IMG_NAME = "im2.png";
const string RIGHT_IMG_NAME = "im6.png";
const string TRUE_LEFT_DISP_NAME = "disp2.png";
const string TRUE_RIGHT_DISP_NAME = "disp6.png";
string ERROR_PREFIXES[] = { "borderedAll",
"borderedNoOccl",
"borderedOccl",
"borderedTextured",
"borderedTextureless",
"borderedDepthDiscont" }; // size of ERROR_KINDS_COUNT
const string RMS_STR = "RMS";
const string BAD_PXLS_FRACTION_STR = "BadPxlsFraction";
class QualityEvalParams
{
public:
QualityEvalParams() { setDefaults(); }
QualityEvalParams( int _ignoreBorder )
{
setDefaults();
ignoreBorder = _ignoreBorder;
}
void setDefaults()
{
badThresh = EVAL_BAD_THRESH;
texturelessWidth = EVAL_TEXTURELESS_WIDTH;
texturelessThresh = EVAL_TEXTURELESS_THRESH;
dispThresh = EVAL_DISP_THRESH;
dispGap = EVAL_DISP_GAP;
discontWidth = EVAL_DISCONT_WIDTH;
ignoreBorder = EVAL_IGNORE_BORDER;
}
float badThresh;
int texturelessWidth;
float texturelessThresh;
float dispThresh;
float dispGap;
int discontWidth;
int ignoreBorder;
};
class CV_StereoMatchingTest : public cvtest::BaseTest
{
public:
CV_StereoMatchingTest()
{ rmsEps.resize( ERROR_KINDS_COUNT, 0.01f ); fracEps.resize( ERROR_KINDS_COUNT, 1.e-6f ); }
protected:
// assumed that left image is a reference image
virtual int runStereoMatchingAlgorithm( const Mat& leftImg, const Mat& rightImg,
Mat& leftDisp, Mat& rightDisp, int caseIdx ) = 0; // return ignored border width
int readDatasetsParams( FileStorage& fs );
virtual int readRunParams( FileStorage& fs );
void writeErrors( const string& errName, const vector<float>& errors, FileStorage* fs = 0 );
void readErrors( FileNode& fn, const string& errName, vector<float>& errors );
int compareErrors( const vector<float>& calcErrors, const vector<float>& validErrors,
const vector<float>& eps, const string& errName );
int processStereoMatchingResults( FileStorage& fs, int caseIdx, bool isWrite,
const Mat& leftImg, const Mat& rightImg,
const Mat& trueLeftDisp, const Mat& trueRightDisp,
const Mat& leftDisp, const Mat& rightDisp,
const QualityEvalParams& qualityEvalParams );
void run( int );
vector<float> rmsEps;
vector<float> fracEps;
struct DatasetParams
{
int dispScaleFactor;
int dispUnknVal;
};
map<string, DatasetParams> datasetsParams;
vector<string> caseNames;
vector<string> caseDatasets;
};
void CV_StereoMatchingTest::run(int)
{
string dataPath = ts->get_data_path();
string algorithmName = name;
assert( !algorithmName.empty() );
if( dataPath.empty() )
{
ts->printf( cvtest::TS::LOG, "dataPath is empty" );
ts->set_failed_test_info( cvtest::TS::FAIL_BAD_ARG_CHECK );
return;
}
FileStorage datasetsFS( dataPath + DATASETS_DIR + DATASETS_FILE, FileStorage::READ );
int code = readDatasetsParams( datasetsFS );
if( code != cvtest::TS::OK )
{
ts->set_failed_test_info( code );
return;
}
FileStorage runParamsFS( dataPath + ALGORITHMS_DIR + algorithmName + RUN_PARAMS_FILE, FileStorage::READ );
code = readRunParams( runParamsFS );
if( code != cvtest::TS::OK )
{
ts->set_failed_test_info( code );
return;
}
string fullResultFilename = dataPath + ALGORITHMS_DIR + algorithmName + RESULT_FILE;
FileStorage resFS( fullResultFilename, FileStorage::READ );
bool isWrite = true; // write or compare results
if( resFS.isOpened() )
isWrite = false;
else
{
resFS.open( fullResultFilename, FileStorage::WRITE );
if( !resFS.isOpened() )
{
ts->printf( cvtest::TS::LOG, "file %s can not be read or written\n", fullResultFilename.c_str() );
ts->set_failed_test_info( cvtest::TS::FAIL_BAD_ARG_CHECK );
return;
}
resFS << "stereo_matching" << "{";
}
int progress = 0, caseCount = (int)caseNames.size();
for( int ci = 0; ci < caseCount; ci++)
{
progress = update_progress( progress, ci, caseCount, 0 );
printf("progress: %d%%\n", progress);
fflush(stdout);
string datasetName = caseDatasets[ci];
string datasetFullDirName = dataPath + DATASETS_DIR + datasetName + "/";
Mat leftImg = imread(datasetFullDirName + LEFT_IMG_NAME);
Mat rightImg = imread(datasetFullDirName + RIGHT_IMG_NAME);
Mat trueLeftDisp = imread(datasetFullDirName + TRUE_LEFT_DISP_NAME, 0);
Mat trueRightDisp = imread(datasetFullDirName + TRUE_RIGHT_DISP_NAME, 0);
if( leftImg.empty() || rightImg.empty() || trueLeftDisp.empty() )
{
ts->printf( cvtest::TS::LOG, "images or left ground-truth disparities of dataset %s can not be read", datasetName.c_str() );
code = cvtest::TS::FAIL_INVALID_TEST_DATA;
continue;
}
int dispScaleFactor = datasetsParams[datasetName].dispScaleFactor;
Mat tmp; trueLeftDisp.convertTo( tmp, CV_32FC1, 1.f/dispScaleFactor ); trueLeftDisp = tmp; tmp.release();
if( !trueRightDisp.empty() )
trueRightDisp.convertTo( tmp, CV_32FC1, 1.f/dispScaleFactor ); trueRightDisp = tmp; tmp.release();
Mat leftDisp, rightDisp;
int ignBorder = max(runStereoMatchingAlgorithm(leftImg, rightImg, leftDisp, rightDisp, ci), EVAL_IGNORE_BORDER);
leftDisp.convertTo( tmp, CV_32FC1 ); leftDisp = tmp; tmp.release();
rightDisp.convertTo( tmp, CV_32FC1 ); rightDisp = tmp; tmp.release();
int tempCode = processStereoMatchingResults( resFS, ci, isWrite,
leftImg, rightImg, trueLeftDisp, trueRightDisp, leftDisp, rightDisp, QualityEvalParams(ignBorder));
code = tempCode==cvtest::TS::OK ? code : tempCode;
}
if( isWrite )
resFS << "}"; // "stereo_matching"
ts->set_failed_test_info( code );
}
void calcErrors( const Mat& leftImg, const Mat& /*rightImg*/,
const Mat& trueLeftDisp, const Mat& trueRightDisp,
const Mat& trueLeftUnknDispMask, const Mat& trueRightUnknDispMask,
const Mat& calcLeftDisp, const Mat& /*calcRightDisp*/,
vector<float>& rms, vector<float>& badPxlsFractions,
const QualityEvalParams& qualityEvalParams )
{
Mat texturelessMask, texturedMask;
computeTextureBasedMasks( leftImg, &texturelessMask, &texturedMask,
qualityEvalParams.texturelessWidth, qualityEvalParams.texturelessThresh );
Mat occludedMask, nonOccludedMask;
computeOcclusionBasedMasks( trueLeftDisp, trueRightDisp, &occludedMask, &nonOccludedMask,
trueLeftUnknDispMask, trueRightUnknDispMask, qualityEvalParams.dispThresh);
Mat depthDiscontMask;
computeDepthDiscontMask( trueLeftDisp, depthDiscontMask, trueLeftUnknDispMask,
qualityEvalParams.dispGap, qualityEvalParams.discontWidth);
Mat borderedKnownMask = getBorderedMask( leftImg.size(), qualityEvalParams.ignoreBorder ) & ~trueLeftUnknDispMask;
nonOccludedMask &= borderedKnownMask;
occludedMask &= borderedKnownMask;
texturedMask &= nonOccludedMask; // & borderedKnownMask
texturelessMask &= nonOccludedMask; // & borderedKnownMask
depthDiscontMask &= nonOccludedMask; // & borderedKnownMask
rms.resize(ERROR_KINDS_COUNT);
rms[0] = dispRMS( calcLeftDisp, trueLeftDisp, borderedKnownMask );
rms[1] = dispRMS( calcLeftDisp, trueLeftDisp, nonOccludedMask );
rms[2] = dispRMS( calcLeftDisp, trueLeftDisp, occludedMask );
rms[3] = dispRMS( calcLeftDisp, trueLeftDisp, texturedMask );
rms[4] = dispRMS( calcLeftDisp, trueLeftDisp, texturelessMask );
rms[5] = dispRMS( calcLeftDisp, trueLeftDisp, depthDiscontMask );
badPxlsFractions.resize(ERROR_KINDS_COUNT);
badPxlsFractions[0] = badMatchPxlsFraction( calcLeftDisp, trueLeftDisp, borderedKnownMask, qualityEvalParams.badThresh );
badPxlsFractions[1] = badMatchPxlsFraction( calcLeftDisp, trueLeftDisp, nonOccludedMask, qualityEvalParams.badThresh );
badPxlsFractions[2] = badMatchPxlsFraction( calcLeftDisp, trueLeftDisp, occludedMask, qualityEvalParams.badThresh );
badPxlsFractions[3] = badMatchPxlsFraction( calcLeftDisp, trueLeftDisp, texturedMask, qualityEvalParams.badThresh );
badPxlsFractions[4] = badMatchPxlsFraction( calcLeftDisp, trueLeftDisp, texturelessMask, qualityEvalParams.badThresh );
badPxlsFractions[5] = badMatchPxlsFraction( calcLeftDisp, trueLeftDisp, depthDiscontMask, qualityEvalParams.badThresh );
}
int CV_StereoMatchingTest::processStereoMatchingResults( FileStorage& fs, int caseIdx, bool isWrite,
const Mat& leftImg, const Mat& rightImg,
const Mat& trueLeftDisp, const Mat& trueRightDisp,
const Mat& leftDisp, const Mat& rightDisp,
const QualityEvalParams& qualityEvalParams )
{
// rightDisp is not used in current test virsion
int code = cvtest::TS::OK;
assert( fs.isOpened() );
assert( trueLeftDisp.type() == CV_32FC1 && trueRightDisp.type() == CV_32FC1 );
assert( leftDisp.type() == CV_32FC1 && rightDisp.type() == CV_32FC1 );
// get masks for unknown ground truth disparity values
Mat leftUnknMask, rightUnknMask;
DatasetParams params = datasetsParams[caseDatasets[caseIdx]];
absdiff( trueLeftDisp, Scalar(params.dispUnknVal), leftUnknMask );
leftUnknMask = leftUnknMask < numeric_limits<float>::epsilon();
assert(leftUnknMask.type() == CV_8UC1);
if( !trueRightDisp.empty() )
{
absdiff( trueRightDisp, Scalar(params.dispUnknVal), rightUnknMask );
rightUnknMask = rightUnknMask < numeric_limits<float>::epsilon();
assert(leftUnknMask.type() == CV_8UC1);
}
// calculate errors
vector<float> rmss, badPxlsFractions;
calcErrors( leftImg, rightImg, trueLeftDisp, trueRightDisp, leftUnknMask, rightUnknMask,
leftDisp, rightDisp, rmss, badPxlsFractions, qualityEvalParams );
if( isWrite )
{
fs << caseNames[caseIdx] << "{";
cvWriteComment( fs.fs, RMS_STR.c_str(), 0 );
writeErrors( RMS_STR, rmss, &fs );
cvWriteComment( fs.fs, BAD_PXLS_FRACTION_STR.c_str(), 0 );
writeErrors( BAD_PXLS_FRACTION_STR, badPxlsFractions, &fs );
fs << "}"; // datasetName
}
else // compare
{
ts->printf( cvtest::TS::LOG, "\nquality of case named %s\n", caseNames[caseIdx].c_str() );
ts->printf( cvtest::TS::LOG, "%s\n", RMS_STR.c_str() );
writeErrors( RMS_STR, rmss );
ts->printf( cvtest::TS::LOG, "%s\n", BAD_PXLS_FRACTION_STR.c_str() );
writeErrors( BAD_PXLS_FRACTION_STR, badPxlsFractions );
FileNode fn = fs.getFirstTopLevelNode()[caseNames[caseIdx]];
vector<float> validRmss, validBadPxlsFractions;
readErrors( fn, RMS_STR, validRmss );
readErrors( fn, BAD_PXLS_FRACTION_STR, validBadPxlsFractions );
int tempCode = compareErrors( rmss, validRmss, rmsEps, RMS_STR );
code = tempCode==cvtest::TS::OK ? code : tempCode;
tempCode = compareErrors( badPxlsFractions, validBadPxlsFractions, fracEps, BAD_PXLS_FRACTION_STR );
code = tempCode==cvtest::TS::OK ? code : tempCode;
}
return code;
}
int CV_StereoMatchingTest::readDatasetsParams( FileStorage& fs )
{
if( !fs.isOpened() )
{
ts->printf( cvtest::TS::LOG, "datasetsParams can not be read " );
return cvtest::TS::FAIL_INVALID_TEST_DATA;
}
datasetsParams.clear();
FileNode fn = fs.getFirstTopLevelNode();
assert(fn.isSeq());
for( int i = 0; i < (int)fn.size(); i+=3 )
{
string name = fn[i];
DatasetParams params;
string sf = fn[i+1]; params.dispScaleFactor = atoi(sf.c_str());
string uv = fn[i+2]; params.dispUnknVal = atoi(uv.c_str());
datasetsParams[name] = params;
}
return cvtest::TS::OK;
}
int CV_StereoMatchingTest::readRunParams( FileStorage& fs )
{
if( !fs.isOpened() )
{
ts->printf( cvtest::TS::LOG, "runParams can not be read " );
return cvtest::TS::FAIL_INVALID_TEST_DATA;
}
caseNames.clear();;
caseDatasets.clear();
return cvtest::TS::OK;
}
void CV_StereoMatchingTest::writeErrors( const string& errName, const vector<float>& errors, FileStorage* fs )
{
assert( (int)errors.size() == ERROR_KINDS_COUNT );
vector<float>::const_iterator it = errors.begin();
if( fs )
for( int i = 0; i < ERROR_KINDS_COUNT; i++, ++it )
*fs << ERROR_PREFIXES[i] + errName << *it;
else
for( int i = 0; i < ERROR_KINDS_COUNT; i++, ++it )
ts->printf( cvtest::TS::LOG, "%s = %f\n", string(ERROR_PREFIXES[i]+errName).c_str(), *it );
}
void CV_StereoMatchingTest::readErrors( FileNode& fn, const string& errName, vector<float>& errors )
{
errors.resize( ERROR_KINDS_COUNT );
vector<float>::iterator it = errors.begin();
for( int i = 0; i < ERROR_KINDS_COUNT; i++, ++it )
fn[ERROR_PREFIXES[i]+errName] >> *it;
}
int CV_StereoMatchingTest::compareErrors( const vector<float>& calcErrors, const vector<float>& validErrors,
const vector<float>& eps, const string& errName )
{
assert( (int)calcErrors.size() == ERROR_KINDS_COUNT );
assert( (int)validErrors.size() == ERROR_KINDS_COUNT );
assert( (int)eps.size() == ERROR_KINDS_COUNT );
vector<float>::const_iterator calcIt = calcErrors.begin(),
validIt = validErrors.begin(),
epsIt = eps.begin();
bool ok = true;
for( int i = 0; i < ERROR_KINDS_COUNT; i++, ++calcIt, ++validIt, ++epsIt )
if( *calcIt - *validIt > *epsIt )
{
ts->printf( cvtest::TS::LOG, "bad accuracy of %s (valid=%f; calc=%f)\n", string(ERROR_PREFIXES[i]+errName).c_str(), *validIt, *calcIt );
ok = false;
}
return ok ? cvtest::TS::OK : cvtest::TS::FAIL_BAD_ACCURACY;
}
//----------------------------------- StereoGC test -----------------------------------------------------
class CV_StereoGCTest : public CV_StereoMatchingTest
{
public:
CV_StereoGCTest()
{
name = "stereogc";
fill(rmsEps.begin(), rmsEps.end(), 3.f);
fracEps[0] = 0.05f; // all
fracEps[1] = 0.05f; // noOccl
fracEps[2] = 0.25f; // occl
fracEps[3] = 0.05f; // textured
fracEps[4] = 0.10f; // textureless
fracEps[5] = 0.10f; // borderedDepthDiscont
}
protected:
struct RunParams
{
int ndisp;
int iterCount;
};
vector<RunParams> caseRunParams;
virtual int readRunParams( FileStorage& fs )
{
int code = CV_StereoMatchingTest::readRunParams(fs);
FileNode fn = fs.getFirstTopLevelNode();
assert(fn.isSeq());
for( int i = 0; i < (int)fn.size(); i+=4 )
{
string caseName = fn[i], datasetName = fn[i+1];
RunParams params;
string ndisp = fn[i+2]; params.ndisp = atoi(ndisp.c_str());
string iterCount = fn[i+3]; params.iterCount = atoi(iterCount.c_str());
caseNames.push_back( caseName );
caseDatasets.push_back( datasetName );
caseRunParams.push_back( params );
}
return code;
}
virtual int runStereoMatchingAlgorithm( const Mat& _leftImg, const Mat& _rightImg,
Mat& leftDisp, Mat& rightDisp, int caseIdx )
{
RunParams params = caseRunParams[caseIdx];
assert( _leftImg.type() == CV_8UC3 && _rightImg.type() == CV_8UC3 );
Mat leftImg, rightImg, tmp;
cvtColor( _leftImg, leftImg, CV_BGR2GRAY );
cvtColor( _rightImg, rightImg, CV_BGR2GRAY );
leftDisp.create( leftImg.size(), CV_16SC1 );
rightDisp.create( rightImg.size(), CV_16SC1 );
CvMat _limg = leftImg, _rimg = rightImg, _ldisp = leftDisp, _rdisp = rightDisp;
CvStereoGCState *state = cvCreateStereoGCState( params.ndisp, params.iterCount );
cvFindStereoCorrespondenceGC( &_limg, &_rimg, &_ldisp, &_rdisp, state );
cvReleaseStereoGCState( &state );
leftDisp = - leftDisp;
return 0;
}
};
TEST(Calib3d_StereoGC, regression) { CV_StereoGCTest test; test.safe_run(); }

View File

@@ -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.
//
//
// 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 "test_precomp.hpp"
using namespace cv;
using namespace std;
class CV_SubdivTest : public cvtest::BaseTest
{
public:
CV_SubdivTest();
~CV_SubdivTest();
void clear();
protected:
int read_params( CvFileStorage* fs );
int prepare_test_case( int test_case_idx );
int validate_test_results( int test_case_idx );
void run_func();
int min_log_img_size, max_log_img_size;
CvSize img_size;
int min_log_point_count;
int max_log_point_count;
int point_count;
CvSubdiv2D* subdiv;
CvMemStorage* storage;
};
CV_SubdivTest::CV_SubdivTest()
{
test_case_count = 100;
min_log_point_count = 1;
max_log_point_count = 10;
min_log_img_size = 1;
max_log_img_size = 10;
storage = 0;
}
CV_SubdivTest::~CV_SubdivTest()
{
clear();
}
void CV_SubdivTest::clear()
{
cvtest::BaseTest::clear();
cvReleaseMemStorage( &storage );
}
int CV_SubdivTest::read_params( CvFileStorage* fs )
{
int code = cvtest::BaseTest::read_params( fs );
int t;
if( code < 0 )
return code;
test_case_count = cvReadInt( find_param( fs, "test_case_count" ), test_case_count );
min_log_point_count = cvReadInt( find_param( fs, "min_log_point_count" ), min_log_point_count );
max_log_point_count = cvReadInt( find_param( fs, "max_log_point_count" ), max_log_point_count );
min_log_img_size = cvReadInt( find_param( fs, "min_log_img_size" ), min_log_img_size );
max_log_img_size = cvReadInt( find_param( fs, "max_log_img_size" ), max_log_img_size );
min_log_point_count = cvtest::clipInt( min_log_point_count, 1, 10 );
max_log_point_count = cvtest::clipInt( max_log_point_count, 1, 10 );
if( min_log_point_count > max_log_point_count )
CV_SWAP( min_log_point_count, max_log_point_count, t );
min_log_img_size = cvtest::clipInt( min_log_img_size, 1, 10 );
max_log_img_size = cvtest::clipInt( max_log_img_size, 1, 10 );
if( min_log_img_size > max_log_img_size )
CV_SWAP( min_log_img_size, max_log_img_size, t );
return 0;
}
int CV_SubdivTest::prepare_test_case( int test_case_idx )
{
RNG& rng = ts->get_rng();
int code = cvtest::BaseTest::prepare_test_case( test_case_idx );
if( code < 0 )
return code;
clear();
point_count = cvRound(exp((cvtest::randReal(rng)*
(max_log_point_count - min_log_point_count) + min_log_point_count)*CV_LOG2));
img_size.width = cvRound(exp((cvtest::randReal(rng)*
(max_log_img_size - min_log_img_size) + min_log_img_size)*CV_LOG2));
img_size.height = cvRound(exp((cvtest::randReal(rng)*
(max_log_img_size - min_log_img_size) + min_log_img_size)*CV_LOG2));
storage = cvCreateMemStorage( 1 << 10 );
return 1;
}
void CV_SubdivTest::run_func()
{
}
static inline double sqdist( CvPoint2D32f pt1, CvPoint2D32f pt2 )
{
double dx = pt1.x - pt2.x;
double dy = pt1.y - pt2.y;
return dx*dx + dy*dy;
}
static int
subdiv2DCheck( CvSubdiv2D* subdiv )
{
int i, j, total = subdiv->edges->total;
CV_Assert( subdiv != 0 );
for( i = 0; i < total; i++ )
{
CvQuadEdge2D* edge = (CvQuadEdge2D*)cvGetSetElem(subdiv->edges,i);
if( edge && CV_IS_SET_ELEM( edge ))
{
for( j = 0; j < 4; j++ )
{
CvSubdiv2DEdge e = (CvSubdiv2DEdge)edge + j;
CvSubdiv2DEdge o_next = cvSubdiv2DNextEdge(e);
CvSubdiv2DEdge o_prev = cvSubdiv2DGetEdge(e, CV_PREV_AROUND_ORG );
CvSubdiv2DEdge d_prev = cvSubdiv2DGetEdge(e, CV_PREV_AROUND_DST );
CvSubdiv2DEdge d_next = cvSubdiv2DGetEdge(e, CV_NEXT_AROUND_DST );
// check points
if( cvSubdiv2DEdgeOrg(e) != cvSubdiv2DEdgeOrg(o_next))
return 0;
if( cvSubdiv2DEdgeOrg(e) != cvSubdiv2DEdgeOrg(o_prev))
return 0;
if( cvSubdiv2DEdgeDst(e) != cvSubdiv2DEdgeDst(d_next))
return 0;
if( cvSubdiv2DEdgeDst(e) != cvSubdiv2DEdgeDst(d_prev))
return 0;
if( j % 2 == 0 )
{
if( cvSubdiv2DEdgeDst(o_next) != cvSubdiv2DEdgeOrg(d_prev))
return 0;
if( cvSubdiv2DEdgeDst(o_prev) != cvSubdiv2DEdgeOrg(d_next))
return 0;
if( cvSubdiv2DGetEdge(cvSubdiv2DGetEdge(cvSubdiv2DGetEdge(
e,CV_NEXT_AROUND_LEFT),CV_NEXT_AROUND_LEFT),CV_NEXT_AROUND_LEFT) != e )
return 0;
if( cvSubdiv2DGetEdge(cvSubdiv2DGetEdge(cvSubdiv2DGetEdge(
e,CV_NEXT_AROUND_RIGHT),CV_NEXT_AROUND_RIGHT),CV_NEXT_AROUND_RIGHT) != e)
return 0;
}
}
}
}
return 1;
}
// the whole testing is done here, run_func() is not utilized in this test
int CV_SubdivTest::validate_test_results( int /*test_case_idx*/ )
{
int code = cvtest::TS::OK;
RNG& rng = ts->get_rng();
int j, k, real_count = point_count;
double xrange = img_size.width*(1 - FLT_EPSILON);
double yrange = img_size.height*(1 - FLT_EPSILON);
subdiv = cvCreateSubdivDelaunay2D(
cvRect( 0, 0, img_size.width, img_size.height ), storage );
CvSeq* seq = cvCreateSeq( 0, sizeof(*seq), sizeof(CvPoint2D32f), storage );
CvSeqWriter writer;
cvStartAppendToSeq( seq, &writer );
// insert random points
for( j = 0; j < point_count; j++ )
{
CvPoint2D32f pt;
CvSubdiv2DPoint* point;
pt.x = (float)(cvtest::randReal(rng)*xrange);
pt.y = (float)(cvtest::randReal(rng)*yrange);
CvSubdiv2DPointLocation loc =
cvSubdiv2DLocate( subdiv, pt, 0, &point );
if( loc == CV_PTLOC_VERTEX )
{
int index = cvSeqElemIdx( (CvSeq*)subdiv, point );
CvPoint2D32f* pt1;
cvFlushSeqWriter( &writer );
pt1 = (CvPoint2D32f*)cvGetSeqElem( seq, index - 3 );
if( !pt1 ||
fabs(pt1->x - pt.x) > FLT_EPSILON ||
fabs(pt1->y - pt.y) > FLT_EPSILON )
{
ts->printf( cvtest::TS::LOG, "The point #%d: (%.1f,%.1f) is said to coinside with a subdivision vertex, "
"however it could be found in a sequence of inserted points\n", j, pt.x, pt.y );
code = cvtest::TS::FAIL_INVALID_OUTPUT;
goto _exit_;
}
real_count--;
}
point = cvSubdivDelaunay2DInsert( subdiv, pt );
if( point->pt.x != pt.x || point->pt.y != pt.y )
{
ts->printf( cvtest::TS::LOG, "The point #%d: (%.1f,%.1f) has been incorrectly added\n", j, pt.x, pt.y );
code = cvtest::TS::FAIL_INVALID_OUTPUT;
goto _exit_;
}
if( (j + 1) % 10 == 0 || j == point_count - 1 )
{
if( !subdiv2DCheck( subdiv ))
{
ts->printf( cvtest::TS::LOG, "Subdivision consistency check failed after inserting the point #%d\n", j );
code = cvtest::TS::FAIL_INVALID_OUTPUT;
goto _exit_;
}
}
if( loc != CV_PTLOC_VERTEX )
{
CV_WRITE_SEQ_ELEM( pt, writer );
}
}
if( code < 0 )
goto _exit_;
cvCalcSubdivVoronoi2D( subdiv );
seq = cvEndWriteSeq( &writer );
if( !subdiv2DCheck( subdiv ))
{
ts->printf( cvtest::TS::LOG, "The subdivision failed consistency check after building the Voronoi tesselation\n" );
code = cvtest::TS::FAIL_INVALID_OUTPUT;
goto _exit_;
}
for( j = 0; j < MAX((point_count - 5)/10 + 5, 10); j++ )
{
CvPoint2D32f pt;
double minDistance;
pt.x = (float)(cvtest::randReal(rng)*xrange);
pt.y = (float)(cvtest::randReal(rng)*yrange);
CvSubdiv2DPoint* point = cvFindNearestPoint2D( subdiv, pt );
CvSeqReader reader;
if( !point )
{
ts->printf( cvtest::TS::LOG, "There is no nearest point (?!) for the point (%.1f, %.1f) in the subdivision\n",
pt.x, pt.y );
code = cvtest::TS::FAIL_INVALID_OUTPUT;
goto _exit_;
}
cvStartReadSeq( seq, &reader );
minDistance = sqdist( pt, point->pt );
for( k = 0; k < seq->total; k++ )
{
CvPoint2D32f ptt;
CV_READ_SEQ_ELEM( ptt, reader );
double distance = sqdist( pt, ptt );
if( minDistance > distance && sqdist(ptt, point->pt) > FLT_EPSILON*1000 )
{
ts->printf( cvtest::TS::LOG, "The triangulation vertex (%.3f,%.3f) was said to be nearest to (%.3f,%.3f),\n"
"whereas another vertex (%.3f,%.3f) is closer\n",
point->pt.x, point->pt.y, pt.x, pt.y, ptt.x, ptt.y );
code = cvtest::TS::FAIL_BAD_ACCURACY;
goto _exit_;
}
}
}
_exit_:
if( code < 0 )
ts->set_failed_test_info( code );
return code;
}
TEST(Imgproc_Subdiv, correctness) { CV_SubdivTest test; test.safe_run(); }
/* End of file. */