Commit ef8182e1 authored by Vadim Pisarevsky's avatar Vadim Pisarevsky

Merge pull request #4025 from vpisarev:features2d_fixes

parents 021473e9 af47b655
...@@ -660,6 +660,7 @@ CV_EXPORTS void write( FileStorage& fs, const String& name, const String& value ...@@ -660,6 +660,7 @@ CV_EXPORTS void write( FileStorage& fs, const String& name, const String& value
CV_EXPORTS void write( FileStorage& fs, const String& name, const Mat& value ); CV_EXPORTS void write( FileStorage& fs, const String& name, const Mat& value );
CV_EXPORTS void write( FileStorage& fs, const String& name, const SparseMat& value ); CV_EXPORTS void write( FileStorage& fs, const String& name, const SparseMat& value );
CV_EXPORTS void write( FileStorage& fs, const String& name, const std::vector<KeyPoint>& value); CV_EXPORTS void write( FileStorage& fs, const String& name, const std::vector<KeyPoint>& value);
CV_EXPORTS void write( FileStorage& fs, const String& name, const std::vector<DMatch>& value);
CV_EXPORTS void writeScalar( FileStorage& fs, int value ); CV_EXPORTS void writeScalar( FileStorage& fs, int value );
CV_EXPORTS void writeScalar( FileStorage& fs, float value ); CV_EXPORTS void writeScalar( FileStorage& fs, float value );
...@@ -678,6 +679,7 @@ CV_EXPORTS void read(const FileNode& node, String& value, const String& default_ ...@@ -678,6 +679,7 @@ CV_EXPORTS void read(const FileNode& node, String& value, const String& default_
CV_EXPORTS void read(const FileNode& node, Mat& mat, const Mat& default_mat = Mat() ); CV_EXPORTS void read(const FileNode& node, Mat& mat, const Mat& default_mat = Mat() );
CV_EXPORTS void read(const FileNode& node, SparseMat& mat, const SparseMat& default_mat = SparseMat() ); CV_EXPORTS void read(const FileNode& node, SparseMat& mat, const SparseMat& default_mat = SparseMat() );
CV_EXPORTS void read(const FileNode& node, std::vector<KeyPoint>& keypoints); CV_EXPORTS void read(const FileNode& node, std::vector<KeyPoint>& keypoints);
CV_EXPORTS void read(const FileNode& node, std::vector<DMatch>& matches);
template<typename _Tp> static inline void read(const FileNode& node, Point_<_Tp>& value, const Point_<_Tp>& default_value) template<typename _Tp> static inline void read(const FileNode& node, Point_<_Tp>& value, const Point_<_Tp>& default_value)
{ {
......
...@@ -5594,6 +5594,35 @@ void read(const FileNode& node, std::vector<KeyPoint>& keypoints) ...@@ -5594,6 +5594,35 @@ void read(const FileNode& node, std::vector<KeyPoint>& keypoints)
} }
} }
void write(FileStorage& fs, const String& objname, const std::vector<DMatch>& matches)
{
cv::internal::WriteStructContext ws(fs, objname, CV_NODE_SEQ + CV_NODE_FLOW);
int i, n = (int)matches.size();
for( i = 0; i < n; i++ )
{
const DMatch& m = matches[i];
cv::write(fs, m.queryIdx);
cv::write(fs, m.trainIdx);
cv::write(fs, m.imgIdx);
cv::write(fs, m.distance);
}
}
void read(const FileNode& node, std::vector<DMatch>& matches)
{
matches.resize(0);
FileNodeIterator it = node.begin(), it_end = node.end();
for( ; it != it_end; )
{
DMatch m;
it >> m.queryIdx >> m.trainIdx >> m.imgIdx >> m.distance;
matches.push_back(m);
}
}
int FileNode::type() const { return !node ? NONE : (node->tag & TYPE_MASK); } int FileNode::type() const { return !node ? NONE : (node->tag & TYPE_MASK); }
bool FileNode::isNamed() const { return !node ? false : (node->tag & NAMED) != 0; } bool FileNode::isNamed() const { return !node ? false : (node->tag & NAMED) != 0; }
......
This diff is collapsed.
...@@ -347,3 +347,97 @@ TEST( Features2d_DescriptorExtractor_AKAZE, regression ) ...@@ -347,3 +347,97 @@ TEST( Features2d_DescriptorExtractor_AKAZE, regression )
Hamming(), AKAZE::create()); Hamming(), AKAZE::create());
test.safe_run(); test.safe_run();
} }
TEST( Features2d_DescriptorExtractor, batch )
{
string path = string(cvtest::TS::ptr()->get_data_path() + "detectors_descriptors_evaluation/images_datasets/graf");
vector<Mat> imgs, descriptors;
vector<vector<KeyPoint> > keypoints;
int i, n = 6;
Ptr<ORB> orb = ORB::create();
for( i = 0; i < n; i++ )
{
string imgname = format("%s/img%d.png", path.c_str(), i+1);
Mat img = imread(imgname, 0);
imgs.push_back(img);
}
orb->detect(imgs, keypoints);
orb->compute(imgs, keypoints, descriptors);
ASSERT_EQ((int)keypoints.size(), n);
ASSERT_EQ((int)descriptors.size(), n);
for( i = 0; i < n; i++ )
{
EXPECT_GT((int)keypoints[i].size(), 100);
EXPECT_GT(descriptors[i].rows, 100);
}
}
TEST( Features2d_Feature2d, no_crash )
{
const String& pattern = string(cvtest::TS::ptr()->get_data_path() + "shared/*.png");
vector<String> fnames;
glob(pattern, fnames, false);
sort(fnames.begin(), fnames.end());
Ptr<AKAZE> akaze = AKAZE::create();
Ptr<ORB> orb = ORB::create();
Ptr<KAZE> kaze = KAZE::create();
Ptr<BRISK> brisk = BRISK::create();
size_t i, n = fnames.size();
vector<KeyPoint> keypoints;
Mat descriptors;
orb->setMaxFeatures(5000);
for( i = 0; i < n; i++ )
{
printf("%d. image: %s:\n", (int)i, fnames[i].c_str());
if( strstr(fnames[i].c_str(), "MP.png") != 0 )
continue;
bool checkCount = strstr(fnames[i].c_str(), "templ.png") == 0;
Mat img = imread(fnames[i], -1);
printf("\tAKAZE ... "); fflush(stdout);
akaze->detectAndCompute(img, noArray(), keypoints, descriptors);
printf("(%d keypoints) ", (int)keypoints.size()); fflush(stdout);
if( checkCount )
{
EXPECT_GT((int)keypoints.size(), 0);
}
ASSERT_EQ(descriptors.rows, (int)keypoints.size());
printf("ok\n");
printf("\tKAZE ... "); fflush(stdout);
kaze->detectAndCompute(img, noArray(), keypoints, descriptors);
printf("(%d keypoints) ", (int)keypoints.size()); fflush(stdout);
if( checkCount )
{
EXPECT_GT((int)keypoints.size(), 0);
}
ASSERT_EQ(descriptors.rows, (int)keypoints.size());
printf("ok\n");
printf("\tORB ... "); fflush(stdout);
orb->detectAndCompute(img, noArray(), keypoints, descriptors);
printf("(%d keypoints) ", (int)keypoints.size()); fflush(stdout);
if( checkCount )
{
EXPECT_GT((int)keypoints.size(), 0);
}
ASSERT_EQ(descriptors.rows, (int)keypoints.size());
printf("ok\n");
printf("\tBRISK ... "); fflush(stdout);
brisk->detectAndCompute(img, noArray(), keypoints, descriptors);
printf("(%d keypoints) ", (int)keypoints.size()); fflush(stdout);
if( checkCount )
{
EXPECT_GT((int)keypoints.size(), 0);
}
ASSERT_EQ(descriptors.rows, (int)keypoints.size());
printf("ok\n");
}
}
...@@ -543,3 +543,13 @@ TEST( Features2d_DescriptorMatcher_FlannBased, regression ) ...@@ -543,3 +543,13 @@ TEST( Features2d_DescriptorMatcher_FlannBased, regression )
DescriptorMatcher::create("FlannBased"), 0.04f ); DescriptorMatcher::create("FlannBased"), 0.04f );
test.safe_run(); test.safe_run();
} }
TEST( Features2d_DMatch, read_write )
{
FileStorage fs(".xml", FileStorage::WRITE + FileStorage::MEMORY);
vector<DMatch> matches;
matches.push_back(DMatch(1,2,3,4.5f));
fs << "Match" << matches;
String str = fs.releaseAndGetString();
ASSERT_NE( strstr(str.c_str(), "4.5"), (char*)0 );
}
...@@ -41,171 +41,121 @@ ...@@ -41,171 +41,121 @@
//M*/ //M*/
#include "test_precomp.hpp" #include "test_precomp.hpp"
#include "opencv2/imgproc/imgproc_c.h" #include "opencv2/highgui.hpp"
#if 0
#include <vector> #include <vector>
#include <string> #include <string>
using namespace std; using namespace std;
using namespace cv; using namespace cv;
class CV_MserTest : public cvtest::BaseTest #undef RENDER_MSERS
{ #define RENDER_MSERS 0
public:
CV_MserTest();
protected:
void run(int);
int LoadBoxes(const char* path, vector<CvBox2D>& boxes);
int SaveBoxes(const char* path, const vector<CvBox2D>& boxes);
int CompareBoxes(const vector<CvBox2D>& boxes1,const vector<CvBox2D>& boxes2, float max_rel_diff = 0.01f);
};
CV_MserTest::CV_MserTest()
{
}
int CV_MserTest::LoadBoxes(const char* path, vector<CvBox2D>& boxes) #if defined RENDER_MSERS && RENDER_MSERS
static void renderMSERs(const Mat& gray, Mat& img, const vector<vector<Point> >& msers)
{ {
boxes.clear(); cvtColor(gray, img, COLOR_GRAY2BGR);
FILE* f = fopen(path,"r"); RNG rng((uint64)1749583);
for( int i = 0; i < (int)msers.size(); i++ )
if (f==NULL)
{ {
return 0; uchar b = rng.uniform(0, 256);
} uchar g = rng.uniform(0, 256);
uchar r = rng.uniform(0, 256);
Vec3b color(b, g, r);
while (!feof(f)) const Point* pt = &msers[i][0];
{ size_t j, n = msers[i].size();
CvBox2D box; for( j = 0; j < n; j++ )
int values_read = fscanf(f,"%f,%f,%f,%f,%f\n",&box.angle,&box.center.x,&box.center.y,&box.size.width,&box.size.height); img.at<Vec3b>(pt[j]) = color;
CV_Assert(values_read == 5);
boxes.push_back(box);
} }
fclose(f);
return 1;
}
int CV_MserTest::SaveBoxes(const char* path, const vector<CvBox2D>& boxes)
{
FILE* f = fopen(path,"w");
if (f==NULL)
{
return 0;
}
for (int i=0;i<(int)boxes.size();i++)
{
fprintf(f,"%f,%f,%f,%f,%f\n",boxes[i].angle,boxes[i].center.x,boxes[i].center.y,boxes[i].size.width,boxes[i].size.height);
}
fclose(f);
return 1;
} }
#endif
int CV_MserTest::CompareBoxes(const vector<CvBox2D>& boxes1,const vector<CvBox2D>& boxes2, float max_rel_diff) TEST(Features2d_MSER, cases)
{ {
if (boxes1.size() != boxes2.size()) uchar buf[] =
return 0; {
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
for (int i=0; i<(int)boxes1.size();i++) 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
float rel_diff; 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
if (!((boxes1[i].angle == 0.0f) && (abs(boxes2[i].angle) < max_rel_diff))) 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
{ 255, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255,
float angle_diff = (float)fmod(boxes1[i].angle - boxes2[i].angle, 180); 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255,
// for angular correctness, it makes no sense to use a "relative" error. 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255,
// a 1-degree error around 5 degrees is equally bas as around 250 degrees. 255, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 255, 255,
// in correct cases, angle_diff can now be a bit above 0 or a bit below 180 255, 255, 255, 255, 255, 255, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 255, 255, 255, 255, 255,
if (angle_diff > 90.0f) 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
angle_diff -= 180.0f; 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
} 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255
rel_diff = (float)fabs(angle_diff); };
if (rel_diff > max_rel_diff) Mat big_image = imread(cvtest::TS::ptr()->get_data_path() + "mser/puzzle.png", 0);
return i; Mat small_image(14, 26, CV_8U, buf);
} static const int thresharr[] = { 0, 70, 120, 180, 255 };
const int kDelta = 5;
Ptr<MSER> mserExtractor = MSER::create( kDelta );
vector<vector<Point> > msers;
vector<Rect> boxes;
if (!((boxes1[i].center.x == 0.0f) && (abs(boxes2[i].center.x) < max_rel_diff))) RNG rng((uint64)123456);
{
rel_diff = abs(boxes1[i].center.x-boxes2[i].center.x)/abs(boxes1[i].center.x);
if (rel_diff > max_rel_diff)
return i;
}
if (!((boxes1[i].center.y == 0.0f) && (abs(boxes2[i].center.y) < max_rel_diff))) for( int i = 0; i < 100; i++ )
{ {
rel_diff = abs(boxes1[i].center.y-boxes2[i].center.y)/abs(boxes1[i].center.y); bool use_big_image = rng.uniform(0, 7) != 0;
if (rel_diff > max_rel_diff) bool invert = rng.uniform(0, 2) != 0;
return i; bool binarize = use_big_image ? rng.uniform(0, 5) != 0 : false;
} bool blur = rng.uniform(0, 2) != 0;
if (!((boxes1[i].size.width == 0.0f) && (abs(boxes2[i].size.width) < max_rel_diff))) int thresh = thresharr[rng.uniform(0, 5)];
{
rel_diff = abs(boxes1[i].size.width-boxes2[i].size.width)/abs(boxes1[i].size.width);
if (rel_diff > max_rel_diff)
return i;
}
if (!((boxes1[i].size.height == 0.0f) && (abs(boxes2[i].size.height) < max_rel_diff))) /*if( i == 0 )
{ {
rel_diff = abs(boxes1[i].size.height-boxes2[i].size.height)/abs(boxes1[i].size.height); use_big_image = true;
if (rel_diff > max_rel_diff) invert = binarize = blur = false;
return i; }*/
}
}
return -1; const Mat& src0 = use_big_image ? big_image : small_image;
} Mat src = src0.clone();
void CV_MserTest::run(int) int kMinArea = use_big_image ? 256 : 10;
{ int kMaxArea = (int)src.total()/4;
string image_path = string(ts->get_data_path()) + "mser/puzzle.png";
Mat img = imread( image_path ); mserExtractor->setMinArea(kMinArea);
if (img.empty()) mserExtractor->setMaxArea(kMaxArea);
{
ts->printf( cvtest::TS::LOG, "Unable to open image mser/puzzle.png\n");
ts->set_failed_test_info(cvtest::TS::FAIL_MISSING_TEST_DATA);
return;
}
Mat yuv; if( invert )
cvtColor(img, yuv, COLOR_BGR2YCrCb); bitwise_not(src, src);
vector<vector<Point> > msers; if( binarize )
MSER()(yuv, msers); threshold(src, src, thresh, 255, THRESH_BINARY);
if( blur )
GaussianBlur(src, src, Size(5, 5), 1.5, 1.5);
vector<CvBox2D> boxes; int minRegs = use_big_image ? 7 : 2;
vector<CvBox2D> boxes_orig; int maxRegs = use_big_image ? 1000 : 15;
for ( size_t i = 0; i < msers.size(); i++ ) if( binarize && (thresh == 0 || thresh == 255) )
{ minRegs = maxRegs = 0;
RotatedRect box = fitEllipse(msers[i]);
box.angle=(float)CV_PI/2-box.angle;
boxes.push_back(box);
}
string boxes_path = string(ts->get_data_path()) + "mser/boxes.txt"; mserExtractor->detectRegions( src, msers, boxes );
string calc_boxes_path = string(ts->get_data_path()) + "mser/boxes.calc.txt"; int nmsers = (int)msers.size();
ASSERT_EQ(nmsers, (int)boxes.size());
if (!LoadBoxes(boxes_path.c_str(),boxes_orig)) if( maxRegs < nmsers || minRegs > nmsers )
{ {
SaveBoxes(boxes_path.c_str(),boxes); printf("%d. minArea=%d, maxArea=%d, nmsers=%d, minRegs=%d, maxRegs=%d, "
ts->printf( cvtest::TS::LOG, "Unable to open data file mser/boxes.txt\n"); "image=%s, invert=%d, binarize=%d, thresh=%d, blur=%d\n",
ts->set_failed_test_info(cvtest::TS::FAIL_MISSING_TEST_DATA); i, kMinArea, kMaxArea, nmsers, minRegs, maxRegs, use_big_image ? "big" : "small",
return; (int)invert, (int)binarize, thresh, (int)blur);
#if defined RENDER_MSERS && RENDER_MSERS
Mat image;
imshow("source", src);
renderMSERs(src, image, msers);
imshow("result", image);
waitKey();
#endif
} }
const float dissimularity = 0.01f; ASSERT_LE(minRegs, nmsers);
int n_box = CompareBoxes(boxes_orig,boxes,dissimularity); ASSERT_GE(maxRegs, nmsers);
if (n_box < 0)
{
ts->set_failed_test_info(cvtest::TS::OK);
}
else
{
SaveBoxes(calc_boxes_path.c_str(), boxes);
ts->set_failed_test_info(cvtest::TS::FAIL_BAD_ACCURACY);
ts->printf( cvtest::TS::LOG, "Incorrect correspondence in box %d\n",n_box);
} }
} }
TEST(Features2d_MSER, DISABLED_regression) { CV_MserTest test; test.safe_run(); }
#endif
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment