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);
const Point* pt = &msers[i][0];
size_t j, n = msers[i].size();
for( j = 0; j < n; j++ )
img.at<Vec3b>(pt[j]) = color;
} }
while (!feof(f))
{
CvBox2D box;
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);
CV_Assert(values_read == 5);
boxes.push_back(box);
}
fclose(f);
return 1;
} }
#endif
int CV_MserTest::SaveBoxes(const char* path, const vector<CvBox2D>& boxes) TEST(Features2d_MSER, cases)
{ {
FILE* f = fopen(path,"w"); uchar buf[] =
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); 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,
fclose(f); 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,
return 1; 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,
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,
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,
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,
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,
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,
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,
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
};
Mat big_image = imread(cvtest::TS::ptr()->get_data_path() + "mser/puzzle.png", 0);
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;
int CV_MserTest::CompareBoxes(const vector<CvBox2D>& boxes1,const vector<CvBox2D>& boxes2, float max_rel_diff) RNG rng((uint64)123456);
{
if (boxes1.size() != boxes2.size())
return 0;
for (int i=0; i<(int)boxes1.size();i++) for( int i = 0; i < 100; i++ )
{ {
float rel_diff; bool use_big_image = rng.uniform(0, 7) != 0;
if (!((boxes1[i].angle == 0.0f) && (abs(boxes2[i].angle) < max_rel_diff))) bool invert = rng.uniform(0, 2) != 0;
{ bool binarize = use_big_image ? rng.uniform(0, 5) != 0 : false;
float angle_diff = (float)fmod(boxes1[i].angle - boxes2[i].angle, 180); bool blur = rng.uniform(0, 2) != 0;
// for angular correctness, it makes no sense to use a "relative" error. int thresh = thresharr[rng.uniform(0, 5)];
// a 1-degree error around 5 degrees is equally bas as around 250 degrees.
// in correct cases, angle_diff can now be a bit above 0 or a bit below 180
if (angle_diff > 90.0f)
{
angle_diff -= 180.0f;
}
rel_diff = (float)fabs(angle_diff);
if (rel_diff > max_rel_diff)
return i;
}
if (!((boxes1[i].center.x == 0.0f) && (abs(boxes2[i].center.x) < max_rel_diff)))
{
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)))
{
rel_diff = abs(boxes1[i].center.y-boxes2[i].center.y)/abs(boxes1[i].center.y);
if (rel_diff > max_rel_diff)
return i;
}
if (!((boxes1[i].size.width == 0.0f) && (abs(boxes2[i].size.width) < max_rel_diff)))
{
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