Search code examples
c++opencvmatfile-storagekeypoint

opencv c++ mat to vector<keypoint>


After searching for an answer to this (1, 2, 3, 4) I am still confused on both

  • If this is possible
  • and, if so, how to do it

I want to be able to read from an .xml file which has had a cv::Mat containing Point2f keypoints written to it, reading it back into its original state of std::vector<cv::KeyPoint>.

My writing to file looks like this:

cv::Ptr<cv::FeatureDetector> detector = new cv::OrbFeatureDetector(featureSize);
cv::FileStorage fsKpts("keypoints.xml", cv::FileStorage::WRITE);
for(size_t i = 0; i < ImgVec.size(); i ++){
    std::vector<cv::KeyPoint> imgKpts;
    std::vector<cv::Point2f> points;
    std::vector<cv::KeyPoint>::iterator it;
    detector->detect(ImgVec[i], imgKpts);
    for(it = imgKpts.begin(); it != imgKpts.end(); it ++) points.push_back(it->pt);
    cv::Mat matKpts(points);
    cv::string iValue = std::to_string(i);
    cv::string filename = "Image_" + iValue;
    fsKpts << filename << matKpts;
}
fsKpts.release();

But so far, I have been utterly unable to read this back out into its original state of std::vector<cv::KeyPoint> so to do keypoint matching.

Any help?

EDIT

Current guess for reading in:

std::vector<cv::KeyPoint> fileKptsVec;
std::vector<cv::Point2f> filePoints;
cv::FileStorage fs("keypoints.xml", cv::FileStorage::READ);
for(size_t i = 0; i < imgVec.size(); i++){
    cv::string iValue = std::to_string(i);
    cv::string filename = "Image_" + iValue;
    fs[filename] >> filePoints[i];
    fileKptsVec.push_back(filePoints[i]); // -- Doesn't work
}
fs.release();

EDIT 2

.xml contents:

<?xml version="1.0"?>
<opencv_storage>
<Image_0 type_id="opencv-matrix">
  <rows>500</rows>
  <cols>1</cols>
  <dt>"2f"</dt>
  <data>
    456. 640. 458. 638. 603. 525. 411. 353. 604. 548. 417. 334. 600.
    515. 382. 334. 594. 448. 572. 481. 449. 603. 574. 612. 441. 648.
    470. 334. 439. 650. 412. 387. 438. 550. 568. 514. 575. 602. 452.
    ...

</data></Image_0>
<Image_1 type_id="opencv-matrix">
  <rows>500</rows>
  <cols>1</cols>
  <dt>"2f"</dt>
  <data>
    521. 353. 467. 563. 537. 510. 505. 378. 479. 286. 451. 552. 460.
    554. 541. 545. 463. 554. 492. 404. 457. 552. 534. 546. 463. 562.
    541. 550. 519. 350. 490. 285. 498. 473. 497. 635. 451. 578. 461.
    ...

</data></Image_1>

etc. etc.

Solution

  • You should do as in: http://docs.opencv.org/2.4.9/modules/core/doc/xml_yaml_persistence.html for features.

    If you want to use Mat however, when accessing a Mat element, basically with http://docs.opencv.org/2.4.9/modules/core/doc/basic_structures.html#mat-at your template will be Point2f.

    Mat image_i;
    fs2["Image_i"] >> image_i;
    Point2f firstPoint = image_i.at<Point2f>(0);
    

    EDIT:

    I checked your code, so, you should do:

    std::vector<std::vector<cv::KeyPoint>> fileKptsVec;
    std::vector<cv::Mat> filesVec(imgVec.size());
    cv::FileStorage fs("keypoints.xml", cv::FileStorage::READ);
    for(size_t i = 0; i < imgVec.size(); i++){
        std::vector<cv::KeyPoint> imageIKeypoints;
        cv::string iValue = std::to_string(i);
        cv::string filename = "Image_" + iValue;
        fs[filename] >> filesVec[i];
        for(size_t j = 0; j < filesVec[i].rows; j++) {
            cv::KeyPoint kp;
            kp.pt = filesVec[i].at<cv::Point2f>(j);
            imageIKeypoints.push_back(kp);
        }
        fileKptsVec.push_back(imageIKeypoints);
    }
    fs.release();
    

    However, when you get back KeyPoint, you have location but loosed other information. If it is a problem: http://docs.opencv.org/2.4.9/modules/core/doc/xml_yaml_persistence.html for features.