/*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 "cvtest.h"

#if 0

#include "highgui.h"
#include <vector>
#include <string>
using namespace std;

using namespace cv;

class CV_CalonderTest : public CvTest
{
public:
    CV_CalonderTest();
    ~CV_CalonderTest();    
protected:    
    void run(int);


	void cvmSet6(CvMat* m, int row, int col, float val1, float val2, float val3, float val4, float val5, float val6);
	void FindAffineTransform(const vector<CvPoint>& p1, const vector<CvPoint>& p2, CvMat* affine);
	void MapVectorAffine(const vector<CvPoint>& p1, vector<CvPoint>& p2, CvMat* transform);
	float CalcAffineReprojectionError(const vector<CvPoint>& p1, const vector<CvPoint>& p2, CvMat* transform);
	void ExtractFeatures(const IplImage* image, vector<CvPoint>& points);
	void TrainDetector(RTreeClassifier& detector, int/* patch_size*/, const vector<CvPoint>& train_points,const IplImage* train_image, int n_keypoints = 0);
	void GetCorrespondences(const RTreeClassifier& detector, int patch_size,
						const vector<CvPoint>& objectKeypoints, const vector<CvPoint>& imageKeypoints, const IplImage* image,
						vector<CvPoint>& object, vector<CvPoint>& features);

	// Scales the source image (x and y) and rotate to the angle (Positive values mean counter-clockwise rotation) 
	void RotateAndScale(const IplImage* src, IplImage* dst, float angle, float scale_x, float scale_y);
	// Scales the source image point and rotate to the angle (Positive values mean counter-clockwise rotation) 
	void RotateAndScale(const CvPoint& src, CvPoint& dst, const CvPoint& center, float angle, float scale_x, float scale_y);
	float RunTestsSeries(const IplImage* train_image, vector<CvPoint>& keypoints/*, bool drawResults = false*/);
	//returns 1 in the case of success, 0 otherwise
	int SaveKeypoints(const vector<CvPoint>& points, const char* path);
	////returns 1 in the case of success, 0 otherwise
	int LoadKeypoints(vector<CvPoint>& points, const char* path);

	void ExtractKeypointSignatures(const IplImage* test_image, int patch_size, const RTreeClassifier& detector, const vector<CvPoint>& keypoints, vector<vector<float> >& signatures);
	//returns 1 in the case of success, 0 otherwise
	int SaveKeypointSignatures(const char* path, const vector<vector<float> >& signatures);
	//returns 1 in the case of success, 0 otherwise
	int LoadKeypointSignatures(const char* path, vector<vector<float> >& signatures);

	//returns 1 in the case signatures are identical, 0 otherwise
	int CompareSignatures(const vector<vector<float> > & signatures1, const vector<vector<float> >& signatures2);


};

void CV_CalonderTest::cvmSet6(CvMat* m, int row, int col, float val1, float val2, float val3, float val4, float val5, float val6)
{
    cvmSet(m, row, col, val1);
    cvmSet(m, row, col + 1, val2);
    cvmSet(m, row, col + 2, val3);
    cvmSet(m, row, col + 3, val4);
    cvmSet(m, row, col + 4, val5);
    cvmSet(m, row, col + 5, val6);
}

void CV_CalonderTest::FindAffineTransform(const vector<CvPoint>& p1, const vector<CvPoint>& p2, CvMat* affine)
{
    int eq_num = 2*(int)p1.size();
    CvMat* A = cvCreateMat(eq_num, 6, CV_32FC1);
    CvMat* B = cvCreateMat(eq_num, 1, CV_32FC1);
    CvMat* X = cvCreateMat(6, 1, CV_32FC1);
    
    for(int i = 0; i < (int)p1.size(); i++)
    {
        cvmSet6(A, 2*i, 0, (float)p1[i].x, (float)p1[i].y, 1, 0, 0, 0);
        cvmSet6(A, 2*i + 1, 0, 0, 0, 0, (float)p1[i].x, (float)p1[i].y, 1);
        cvmSet(B, 2*i, 0, (double)p2[i].x);
        cvmSet(B, 2*i + 1, 0, (double)p2[i].y);
    }
    
    cvSolve(A, B, X, CV_SVD);
    
    cvmSet(affine, 0, 0, cvmGet(X, 0, 0));
    cvmSet(affine, 0, 1, cvmGet(X, 1, 0));
    cvmSet(affine, 0, 2, cvmGet(X, 2, 0));
    cvmSet(affine, 1, 0, cvmGet(X, 3, 0));
    cvmSet(affine, 1, 1, cvmGet(X, 4, 0));
    cvmSet(affine, 1, 2, cvmGet(X, 5, 0));
    
    cvReleaseMat(&A);
    cvReleaseMat(&B);
    cvReleaseMat(&X);
}

void CV_CalonderTest::MapVectorAffine(const vector<CvPoint>& p1, vector<CvPoint>& p2, CvMat* transform)
{
    double a = cvmGet(transform, 0, 0);
    double b = cvmGet(transform, 0, 1);
    double c = cvmGet(transform, 0, 2);
    double d = cvmGet(transform, 1, 0);
    double e = cvmGet(transform, 1, 1);
    double f = cvmGet(transform, 1, 2);
    
    for(int i = 0; i < (int)p1.size(); i++)
    {
        double x = a*p1[i].x + b*p1[i].y + c;
        double y = d*p1[i].x + e*p1[i].y + f;
        p2.push_back(cvPoint((int)x, (int)y));
    }
}


float CV_CalonderTest::CalcAffineReprojectionError(const vector<CvPoint>& p1, const vector<CvPoint>& p2, CvMat* transform)
{
    vector<CvPoint> mapped_p1;
    MapVectorAffine(p1, mapped_p1, transform);
    float error = 0;
    for(int i = 0; i < (int)p2.size(); i++)
    {
        //float l = length(p2[i] - mapped_p1[i]);
        error += ((p2[i].x - mapped_p1[i].x)*(p2[i].x - mapped_p1[i].x)+(p2[i].y - mapped_p1[i].y)*(p2[i].y - mapped_p1[i].y));
    }
    
    error /= p2.size();
    
    return error;
}

void CV_CalonderTest::ExtractFeatures(const IplImage* image, vector<CvPoint>& points)
{
	points.clear();
	CvMemStorage* storage = cvCreateMemStorage(0);
    CvSeq *keypoints = 0, *descriptors = 0;
    CvSURFParams params = cvSURFParams(1000, 1);
	cvExtractSURF( image, 0, &keypoints, &descriptors, storage, params );

	
	CvSURFPoint* point;
	for (int i=0;i<keypoints->total;i++)
	{
		point=(CvSURFPoint*)cvGetSeqElem(keypoints,i);
		points.push_back(cvPoint((int)(point->pt.x),(int)(point->pt.y)));
	}
	cvReleaseMemStorage(&storage);
}

void CV_CalonderTest::TrainDetector(RTreeClassifier& detector, int/* patch_size*/, const vector<CvPoint>& train_points,const IplImage* train_image, int n_keypoints)
{
	vector<BaseKeypoint> base_set;
	int n = (int)(train_points.size());
	if (n_keypoints)
		n = n_keypoints;
	for (int i=0;i<n;i++)
	{
		base_set.push_back(BaseKeypoint(train_points[i].x,train_points[i].y,const_cast<IplImage*>(train_image)));
	}

	//Detector training
	//CvRNG r = cvRNG(1);
	RNG rng( cvRandInt(this->ts->get_rng()));
    PatchGenerator gen(0,255,2,false,0.7,1.3,-CV_PI/3,CV_PI/3,-CV_PI/3,CV_PI/3);

	//int64 t0 = cvGetTickCount();
	detector.train(base_set,rng,gen,6,DEFAULT_DEPTH,3000,(int)base_set.size(),detector.DEFAULT_NUM_QUANT_BITS,false);
	//int64 t1 = cvGetTickCount();
	//printf("Train: %f s\n",(float)(t1-t0)/cvGetTickFrequency()*1e-6);


}

void CV_CalonderTest::GetCorrespondences(const RTreeClassifier& detector, int patch_size,
						const vector<CvPoint>& objectKeypoints, const vector<CvPoint>& imageKeypoints, const IplImage* image,
						vector<CvPoint>& object, vector<CvPoint>& features)
{
	IplImage* test_image = cvCloneImage(image);
	object.clear();
	features.clear();

	float* signature = new float[(const_cast<RTreeClassifier&>(detector)).original_num_classes()];
	float* best_corr;
	int* best_corr_idx;
	if (imageKeypoints.size() > 0)
	{
		best_corr = new float[(int)imageKeypoints.size()];
		best_corr_idx = new int[(int)imageKeypoints.size()];


	for(int i=0; i < (int)imageKeypoints.size(); i++)
	{
		int part_idx = -1;
		float prob = 0.0f;
		//CvPoint center = cvPoint((int)(imageKeypoints[i].x),(int)(imageKeypoints[i].y));

		CvRect roi = cvRect((int)(imageKeypoints[i].x) - patch_size/2,(int)(imageKeypoints[i].y) - patch_size/2, patch_size, patch_size);
		cvSetImageROI(test_image, roi);
		roi = cvGetImageROI(test_image);
		if(roi.width != patch_size || roi.height != patch_size)
		{
			best_corr_idx[i] = part_idx;
			best_corr[i] = prob;
		}
		else
		{
			cvSetImageROI(test_image, roi);
			IplImage* roi_image = cvCreateImage(cvSize(roi.width, roi.height), test_image->depth, test_image->nChannels);
			cvCopy(test_image,roi_image);

			(const_cast<RTreeClassifier&>(detector)).getSignature(roi_image, signature);


			for (int j = 0; j< (const_cast<RTreeClassifier&>(detector)).original_num_classes();j++)
			{
				if (prob < signature[j])
				{
					part_idx = j;
					prob = signature[j];
				}
			}

			best_corr_idx[i] = part_idx;
			best_corr[i] = prob;

			
			if (roi_image)
				cvReleaseImage(&roi_image);
		}
		cvResetImageROI(test_image);
	}

	float min_prob = 0.0f;

	for (int j=0;j<(int)objectKeypoints.size();j++)
	{
		float prob = 0.0f;
		int idx = -1;
		for (int i = 0; i<(int)imageKeypoints.size();i++)
		{
			if ((best_corr_idx[i]!=j)||(best_corr[i] < min_prob))
				continue;

			if (best_corr[i] > prob)
			{
				prob = best_corr[i];
				idx = i;
			}
		}
		if (idx >=0)
		{
			object.push_back(objectKeypoints[j]);
			features.push_back(imageKeypoints[idx]);
		}
	}

	if (best_corr)
		delete[] best_corr;
	if (best_corr_idx)
		delete[] best_corr_idx;
		}
	cvReleaseImage(&test_image);
	if (signature)
		delete[] signature;
}


// Scales the source image (x and y) and rotate to the angle (Positive values mean counter-clockwise rotation) 
void CV_CalonderTest::RotateAndScale(const IplImage* src, IplImage* dst, float angle, float scale_x, float scale_y)
{
	IplImage* temp = cvCreateImage(cvSize((int)(src->width*scale_x),(int)(src->height*scale_y)),src->depth,src->nChannels);

	cvResize(src,temp);

	CvMat* transform = cvCreateMat(2,3,CV_32FC1);
	cv2DRotationMatrix(cvPoint2D32f(((double)temp->width)/2,((double)temp->height)/2), angle*180/CV_PI,
		1.0f, transform );

	cvWarpAffine( temp, dst, transform,CV_INTER_LINEAR+CV_WARP_FILL_OUTLIERS);
	cvReleaseImage(&temp);
	cvReleaseMat(&transform);
}

// Scales the source image point and rotate to the angle (Positive values mean counter-clockwise rotation) 
void CV_CalonderTest::RotateAndScale(const CvPoint& src, CvPoint& dst, const CvPoint& center, float angle, float scale_x, float scale_y)
{
	CvPoint temp;
	temp.x = (int)(src.x*scale_x);
	temp.y = (int)(src.y*scale_y);

	CvMat* transform = cvCreateMat(2,3,CV_32FC1);
	cv2DRotationMatrix(cvPoint2D32f((double)center.x*scale_x,(double)center.y*scale_y), angle*180/CV_PI,
		1.0f, transform );

    double a = cvmGet(transform, 0, 0);
    double b = cvmGet(transform, 0, 1);
    double c = cvmGet(transform, 0, 2);
    double d = cvmGet(transform, 1, 0);
    double e = cvmGet(transform, 1, 1);
    double f = cvmGet(transform, 1, 2);
    
    double x = a*temp.x + b*temp.y + c;
    double y = d*temp.x + e*temp.y + f;
    dst= cvPoint((int)x, (int)y);

	cvReleaseMat(&transform);
}

float CV_CalonderTest::RunTestsSeries(const IplImage* train_image, vector<CvPoint>& keypoints)
{
	float angles[] = {(float)-CV_PI/4,(float)CV_PI/4};
	float scales_x[] = {0.85f,1.15f};
	float scales_y[] = {0.85f,1.15f};
	int n_angles = 4;
	int n_scales_x = 3;
	int n_scales_y = 3;
	int accuracy = 4;
	int are_keypoints_loaded = (int)keypoints.size();

	int total_cases = n_angles*n_scales_x*n_scales_y;
	int n_case = 0;

	int length = max(train_image->width,train_image->height);
	int move_x = (int)(1.5*scales_x[0]*length);
	int move_y = (int)(1.5*scales_y[0]*length);
	IplImage* test_image = cvCreateImage(cvSize((int)(scales_x[1]*(move_x+length*1.5)),(int)(scales_y[1]*(move_y+length*1.5))),
				train_image->depth, train_image->nChannels);
	cvSet(test_image,cvScalar(0));

	cvSetImageROI(test_image,cvRect(move_x,move_y,train_image->width,train_image->height));
	cvCopy(train_image,test_image);
	cvResetImageROI(test_image);

	vector<CvPoint> objectKeypoints;
	if (!are_keypoints_loaded)
	{
		ExtractFeatures(train_image,objectKeypoints);
		for (int i=0;i<(int)objectKeypoints.size();i++)
		{
			keypoints.push_back(objectKeypoints[i]);
		}
	}
	else
	{
		for (int i=0;i<(int)keypoints.size();i++)
		{
			objectKeypoints.push_back(keypoints[i]);
		}
	}

	//Checking signatures are identical
	vector <vector<float> > signatures1;
	string signatures_path = string(ts->get_data_path()) + "calonder/signatures.txt";
	int can_load_signatures = LoadKeypointSignatures(signatures_path.c_str(),signatures1);
	// end of region

	RTreeClassifier detector;
	int patch_size = PATCH_SIZE;
	//this->update_progress(1,0,total_cases,5);
	TrainDetector(detector,patch_size,objectKeypoints,train_image,20);

	//Checking signatures are identical
	vector <vector<float> > signatures2;
	ExtractKeypointSignatures(train_image,patch_size,detector,objectKeypoints,signatures2);
	if (!can_load_signatures)
	{
		//SaveKeypointSignatures(signatures_path.c_str(),signatures2);
	}
	else
	{
	//	if (!CompareSignatures(signatures1,signatures2))
	//		return 0;
	}
	// end of region



	int points_total = 0;
	int points_correct = 0;
	

	vector<CvPoint> imageKeypoints;
	vector<CvPoint> object;
	vector<CvPoint> features;
	IplImage* temp = cvCloneImage(test_image);

	int progress = 0;


	//int64 t0 = cvGetTickCount();
	//printf("\n\n-----------\nTest started\n-----------\n");
	for (float angle = angles[0]; angle<=angles[1];angle+=(n_angles > 1 ?(angles[1]-angles[0])/n_angles : 1))
	{
		for (float scale_x = scales_x[0]; scale_x<=scales_x[1];scale_x+=(n_scales_x > 1 ? (scales_x[1]-scales_x[0])/n_scales_x : 1))
		{
			for (float scale_y = scales_y[0]; scale_y<=scales_y[1];scale_y+=(n_scales_y > 1 ? (scales_y[1]-scales_y[0])/n_scales_y : 1))
			{
				//printf("---\nAngle: %f, scaleX: %f, scaleY: %f\n", angle,scale_x,scale_y);
				cvSet(temp,cvScalar(0));
				imageKeypoints.clear();
				object.clear();
				features.clear();

				RotateAndScale(test_image,temp,angle,scale_x,scale_y);
				ExtractFeatures(temp,imageKeypoints);
				GetCorrespondences(detector,patch_size,objectKeypoints,imageKeypoints,temp,object,features);

				int correct = 0;
				CvPoint res;
				for (int i = 0; i< (int)object.size(); i++)
				{

					CvPoint current = object[i];
					current.x+=move_x;
					current.y+=move_y;
					RotateAndScale(current,res,cvPoint(temp->width/2,temp->height/2),angle,scale_x,scale_y);
					int dist = (res.x - features[i].x)*(res.x - features[i].x)+(res.y - features[i].y)*(res.y - features[i].y);
					if (dist < accuracy*accuracy)
						correct++;
				}
				//printf("Image points: %d\nCorrespondences found: %d/%d\n", (int)imageKeypoints.size(), correct, (int)object.size());
				points_correct+=correct;
				points_total+=(int)object.size();
				progress = update_progress( progress, n_case++, total_cases, 0 );
				//if (drawResults)
				//{					
				//	DrawResult(train_image, temp,object,features);
				//}				
			}
		}
	}
//	int64 t1 = cvGetTickCount();
	//printf("%f s\n",(float)(t1-t0)/cvGetTickFrequency()*1e-6);
	cvReleaseImage(&temp);
	cvReleaseImage(&test_image);
	//printf("\n\n-----------\nTest completed\n-----------\n");
	//printf("Total correspondences found: %d/%d\n", points_correct, points_total);
	//FILE* f = fopen("test_result.txt","w");
	//fprintf(f,"Total correspondences found: %d/%d\n", points_correct, points_total);
	//fclose(f);
	if (points_total < 1)
	{
		points_correct = 0;
		points_total = 1;
	}
	return (float)points_correct/(float)points_total;

}

CV_CalonderTest::CV_CalonderTest() : CvTest("calonder","RTreeClassifier")
{
}

CV_CalonderTest::~CV_CalonderTest() {}

int CV_CalonderTest::SaveKeypoints(const vector<CvPoint>& points, const char* path)
{
	FILE* f = fopen(path,"w");
	if (f==NULL)
	{
		return 0;
	}
	for (int i=0;i<(int)points.size();i++)
	{
		fprintf(f,"%d,%d\n",points[i].x,points[i].y);
	}
	fclose(f);
	return 1;
}

int CV_CalonderTest::LoadKeypoints(vector<CvPoint>& points, const char* path)
{
	FILE* f = fopen(path,"r");
	points.clear();

	if (f==NULL)
	{
		return 0;
	}
	
	while (!feof(f))
	{
		int x,y;
		fscanf(f,"%d,%d\n",&x,&y);
		points.push_back(cvPoint(x,y));
	}
	fclose(f);
	return 1;
}

void CV_CalonderTest::ExtractKeypointSignatures(const IplImage* test_image, int patch_size, const RTreeClassifier& detector, const vector<CvPoint>& keypoints, vector<vector<float> >& signatures)
{
	IplImage* _test_image = cvCloneImage(test_image);
	signatures.clear();

	float* signature = new float[(const_cast<RTreeClassifier&>(detector)).original_num_classes()];

	for (int i=0;i<(int)keypoints.size();i++)
	{
		CvRect roi = cvRect((int)(keypoints[i].x) - patch_size/2,(int)(keypoints[i].y) - patch_size/2, patch_size, patch_size);
		cvSetImageROI(_test_image, roi);
		roi = cvGetImageROI(_test_image);
		if(roi.width != patch_size || roi.height != patch_size)
		{
			continue;
		}

		cvSetImageROI(_test_image, roi);
		IplImage* roi_image = cvCreateImage(cvSize(roi.width, roi.height), _test_image->depth, _test_image->nChannels);
		cvCopy(_test_image,roi_image);

		(const_cast<RTreeClassifier&>(detector)).getSignature(roi_image, signature);
		
		vector<float> vec;

		for (int j=0;j<(const_cast<RTreeClassifier&>(detector)).original_num_classes();j++)
		{
			vec.push_back(signature[j]);
		}
		signatures.push_back(vec);

		cvReleaseImage(&roi_image);

	}

	delete[] signature;
	cvReleaseImage(&_test_image);
}




int CV_CalonderTest::SaveKeypointSignatures(const char* path, const vector<vector<float> >& signatures)
{
	FILE* f = fopen(path,"w");
	if (!f)
		return 0;

	for (int i=0;i<(int)signatures.size();i++)
	{
		for (int j=0;j<(int)signatures[i].size();j++)
		{
			fprintf(f,"%f",signatures[i][j]);
			if (j<((int)signatures[i].size()-1))
				fprintf(f,",");
		}
		if (i<((int)signatures.size()-1))
			fprintf(f,"\n");
	}
	fclose(f);

	return 1;
}

int CV_CalonderTest::LoadKeypointSignatures(const char* path, vector<vector<float> >& signatures)
{
	signatures.clear();
	FILE* f = fopen(path,"r");
	if (!f)
		return 0;

	char line[4096];
	vector<float> vec;
	char* tok;

	while(fgets(line,4096,f))
	{
		vec.clear();	
		float val;
		tok = strtok(line,",");
		if (tok)
		{
			sscanf(tok,"%f",&val);
			vec.push_back(val);
			tok = strtok(NULL,",");
			while (tok)
			{
				sscanf(tok,"%f",&val);
				vec.push_back(val);
				tok = strtok(NULL,",");
			}
			signatures.push_back(vec);
		}
	}

	fclose(f);
	return(1);
}

int CV_CalonderTest::CompareSignatures(const vector<vector<float> >& signatures1, const vector<vector<float> >& signatures2)
{
	if (signatures1.size() != signatures2.size())
	{
		return 0;
	}

	float accuracy = 0.05f;
	for (int i=0;i<(int)signatures1.size();i++)
	{
		if (signatures1[i].size() != signatures2[i].size())
		{
			return 0;
		}
		for (int j=0;j<(int)signatures1[i].size();j++)
		{
			if (abs(signatures1[i][j]-signatures2[i][j]) > accuracy)
				return 0;
		}
	}
	return 1;
}


void CV_CalonderTest::run( int /* start_from */)
{
	string train_image_path = string(ts->get_data_path()) + "calonder/baboon200.jpg";
	string train_keypoints_path = string(ts->get_data_path()) + "calonder/train_features.txt";
	IplImage* train_image = cvLoadImage(train_image_path.c_str(),0);

	if (!train_image)
	{
		ts->printf( CvTS::LOG, "Unable to open train image calonder/baboon200.jpg");
		ts->set_failed_test_info(CvTS::FAIL_MISSING_TEST_DATA);
		return;
	}



	// Testing rtree classifier
	float min_accuracy = 0.35f;
	vector<CvPoint> train_keypoints;
	train_keypoints.clear();
	float correctness;
	if (!LoadKeypoints(train_keypoints,train_keypoints_path.c_str()))
	{
		correctness = RunTestsSeries(train_image,train_keypoints);
		SaveKeypoints(train_keypoints,train_keypoints_path.c_str());
	}
	else
	{
		correctness = RunTestsSeries(train_image,train_keypoints);
	}
	if (correctness > min_accuracy)
		ts->set_failed_test_info(CvTS::OK);
	else
	{
		ts->set_failed_test_info(CvTS::FAIL_BAD_ACCURACY);
		ts->printf( CvTS::LOG, "Correct correspondences: %f, less than %f",correctness,min_accuracy);
	}
}

CV_CalonderTest calonder_test;

#endif