如何提取与现有代码簿对应的BOW描述符?

时间:2015-12-08 23:32:04

标签: c++ opencv3.0

我使用A-KAZE功能描述符训练了一个BOW码本(词汇表),并尝试使用BFMatcherknnMatch将新提取的功能与码本进行比较。

相反,我收到以下错误,

OpenCV Error: Assertion failed (_queryDescriptors.type() == trainDescType) in knnMatchImpl, file /home/cecilia/opencv-3.0.0/modules/features2d/src/matchers.cpp, line 722 terminate called after throwing an instance of 'cv::Exception'   what():  /home/cecilia/opencv-3.0.0/modules/features2d/src/matchers.cpp:722: error: (-215) _queryDescriptors.type() == trainDescType in function knnMatchImpl

我使用以下示例

我的直觉是我将代码簿错误地添加到匹配器中,但我找不到支持其他方法的任何文档或示例。如何在新示例中使用我的代码簿。

MCVE

/* BOWTest.cpp*/
#include <opencv2/imgcodecs.hpp>
#include <opencv2/videoio.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/features2d.hpp>
#include <opencv2/opencv.hpp>

#include <iostream>
#include <string>
#include <stdio.h>
#include <dirent.h>

using namespace cv;
using namespace std;

std::string outputFile = "test_codebook.png";
std::string trainingDir = "dataset/";
std::string outputPrefix = "output/res_custom_";

void train(Mat codebook, int codebook_n, Ptr<Feature2D> akaze);
void test(Mat codebook, Ptr<Feature2D> akaze);

int main(int ac, char** av) {

    Ptr<Feature2D> feature = AKAZE::create();
    Mat codebook;
    int codebook_n = 100;
    //train(codebook, codebook_n, feature);
    test(codebook, feature);
}

//I included the train method to show how the codebook is trained, but it is not actually called in this example
void train(Mat codebook, int codebook_n, Ptr<Feature2D> akaze){
    //defining terms for bowkmeans trainer
    TermCriteria tc(TermCriteria::MAX_ITER + TermCriteria::EPS, 10, 0.001);
    int retries = 1;
    int flags = KMEANS_PP_CENTERS;
    BOWKMeansTrainer bowTrainer(codebook_n, tc, retries, flags);

    int i = 0;
    unsigned long numPoints = 0;
    DIR           *d;
    struct dirent *dir;
    d = opendir(trainingDir.c_str());
    if (d)  {
        while ((dir = readdir(d)) != NULL){

            try {
            Mat img;
            std::string imgName = trainingDir + dir->d_name;
            i = i + 1;

            printf("%d, %lu: %s ...", i,numPoints, imgName.c_str());
            img = imread(imgName, CV_LOAD_IMAGE_COLOR);
            if(img.empty()){ //not image
                printf("bad.\n");
                continue;
            }

            printf("loaded.\n");
            resize(img, img, Size(200, 200));

            Mat features;
            vector<KeyPoint> keypoints;
            akaze->detectAndCompute(img, Mat(), keypoints, features);
            features.convertTo(features, CV_32F);
            bowTrainer.add(features);

            Mat res;
            drawKeypoints(img, keypoints, res);
            std::string output_img =  outputPrefix + dir->d_name;
            imwrite(output_img, res);

            numPoints += features.rows;

            }catch(int e){
                cout << "An exception occurred. Nr. " << e << '\n';
            }
        }

        printf("Read images!");
        closedir(d);

        codebook = bowTrainer.cluster();
        imwrite(outputFile, codebook);
    }
}

void test(Mat codebook, Ptr<Feature2D> akaze){
    codebook = imread(outputFile);
    int codebook_n = codebook.rows;

    BFMatcher matcher(NORM_L2);
    matcher.add(std::vector<cv::Mat>(1, codebook));

    Mat res(Size(codebook_n * 10, 3*10), CV_8UC3, Scalar(0));
    vector<int> res_idx(codebook_n, 0);

    try {
        Mat img;
        String imgName = trainingDir + "dog1.jpeg";
        img = imread(imgName, CV_LOAD_IMAGE_COLOR);
        if(img.empty()){ //not image
            printf("bad.\n");
        }else{
            printf("loaded.\n");
            resize(img, img, Size(200, 200));

            Mat features;
            vector<KeyPoint> keypoints;
            akaze->detectAndCompute(img, noArray(), keypoints, features);
            features.convertTo(features, CV_32F);

            vector< vector< DMatch > > nn_matches;
            matcher.knnMatch(features, nn_matches, 1);

            printf("%d matched keypoints", nn_matches.size());
        }

    }catch(int e){
        cout << "An exception occurred. Nr. " << e << '\n';
    }
}

test_codebook.png

codebook

dog1.jpeg

dog1.jpeg

输出

loaded.
OpenCV Error: Assertion failed (_queryDescriptors.type() == trainDescType) in knnMatchImpl, file /home/cecilia/opencv-3.0.0/modules/features2d/src/matchers.cpp, line 722
terminate called after throwing an instance of 'cv::Exception'
  what():  /home/cecilia/opencv-3.0.0/modules/features2d/src/matchers.cpp:722: error: (-215) _queryDescriptors.type() == trainDescType in function knnMatchImpl

1 个答案:

答案 0 :(得分:3)

您不应将codebook保存为图像。 imwrite最终会扩展并转换代码簿的值。 imread使用默认参数将其加载为3通道图像CV_8UC3。要存储不是严格图像的矩阵,您应该使用FileStorage

保存

FileStorage fs(outputFile, FileStorage::WRITE);
// Store codebook
fs << "codebook" << codebook;

<强>负载:

FileStorage fs(outputFile, FileStorage::READ);
fs["codebook"] >> codebook;

您应该使用BOWImgDescriptorExtractor从您的功能开始计算BoW图像描述符,在这种情况下为AKAZE:

Ptr<DescriptorMatcher> matcher = BFMatcher::create("BruteForce");
BOWImgDescriptorExtractor bow(akaze, matcher);
bow.setVocabulary(codebook);

// Mat img = ...

// AKAZE features
Mat features;
vector<KeyPoint> keypoints;
akaze->detectAndCompute(img, noArray(), keypoints, features);
features.convertTo(features, CV_32F);

// BoW descriptor
Mat bowFeatures;
vector<vector<int>> pointsIdxsOfCluster;
bow.compute(features, bowFeatures, &pointsIdxsOfCluster);

您可以使用内置glob从文件夹中读取图片,避免使用dirent

vector<String> fileNames;
glob(trainingDir, fileNames);

for (int i=0; i<fileNames.size(); ++i)
{
    Mat img = imread(fileNames[i]);
    ...

您可以将iostreamcout一起使用,而不是printf

这就是代码的样子:

#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;

std::string outputFile = "test_codebook.yml";
std::string trainingDir = "path_to_train_folder/";
std::string outputPrefix = "path_to_output_folder/";

void train(Mat codebook, int codebook_n, Ptr<Feature2D> akaze);
void test(Mat codebook, Ptr<Feature2D> akaze);

int main(int ac, char** av) {

    Ptr<Feature2D> feature = AKAZE::create();
    Mat codebook;
    int codebook_n = 100;

    train(codebook, codebook_n, feature);
    test(codebook, feature);
}

//I included the train method to show how the codebook is trained, but it is not actually called in this example
void train(Mat codebook, int codebook_n, Ptr<Feature2D> akaze){
    //defining terms for bowkmeans trainer
    TermCriteria tc(TermCriteria::MAX_ITER + TermCriteria::EPS, 10, 0.001);
    int retries = 1;
    int flags = KMEANS_PP_CENTERS;
    BOWKMeansTrainer bowTrainer(codebook_n, tc, retries, flags);

    int i = 0;
    unsigned long numPoints = 0;

    vector<String> fileNames;
    glob(trainingDir, fileNames);

    for (int i=0; i<fileNames.size(); ++i)
    {
        try {
            Mat img;
            std::string imgName = fileNames[i];
            std::string filename = imgName.substr(trainingDir.length());

            cout << i << ", " << numPoints << " : " << imgName;
            img = imread(imgName, CV_LOAD_IMAGE_COLOR);
            if (img.empty()){ //not image
                cout << " bad" << endl;
                continue;
            }

            cout << " loaded" << endl;
            resize(img, img, Size(200, 200));

            Mat features;
            vector<KeyPoint> keypoints;
            akaze->detectAndCompute(img, Mat(), keypoints, features);
            features.convertTo(features, CV_32F);
            bowTrainer.add(features);

            Mat res;
            drawKeypoints(img, keypoints, res);
            std::string output_img = outputPrefix + filename;
            imwrite(output_img, res);

            numPoints += features.rows;

        }
        catch (int e){
            cout << "An exception occurred. Nr. " << e << '\n';
        }
    }

    cout << "Read images!" << endl;

    codebook = bowTrainer.cluster();

    {
        FileStorage fs(outputFile, FileStorage::WRITE);

        // Store codebook
        fs << "codebook" << codebook;

        // You can also store additional info, like the list of images

        //// Store train images filenames
        //fs << "train" << "[";
        //for (int i = 0; i < fileNames.size(); ++i)
        //{
        //  fs << fileNames[i];
        //}
        //fs << "]";
    }
}

void test(Mat codebook, Ptr<Feature2D> akaze)
{
    vector<String> trainFileNames;
    {
        FileStorage fs(outputFile, FileStorage::READ);
        fs["codebook"] >> codebook;

        /*FileNode trainingImages = fs["train"];
        FileNodeIterator it = trainingImages.begin(), it_end = trainingImages.end();
        int idx = 0;
        for (; it != it_end; ++it, idx++)
        {
            trainFileNames.push_back(*it);
        }*/
    }

    int codebook_n = codebook.rows;

    Ptr<DescriptorMatcher> matcher = BFMatcher::create("BruteForce");
    BOWImgDescriptorExtractor bow(akaze, matcher);
    bow.setVocabulary(codebook);

    try {
        Mat img;
        String imgName = "path_to_test_image";
        img = imread(imgName, CV_LOAD_IMAGE_COLOR);
        if (img.empty()){ //not image
            cout << "bad" << endl;
        }
        else{
            cout << "loaded" << endl;
            resize(img, img, Size(200, 200));

            Mat features;
            vector<KeyPoint> keypoints;
            akaze->detectAndCompute(img, noArray(), keypoints, features);
            features.convertTo(features, CV_32F);

            Mat bowFeatures;
            vector<vector<int>> pointsIdxsOfCluster;
            bow.compute(features, bowFeatures, &pointsIdxsOfCluster);

            // bowFeatures is the descriptor you're looking for
            // pointsIdxsOfCluster contains the indices of keypoints that belong to the cluster.
        }
    }
    catch (int e){
        cout << "An exception occurred. Nr. " << e << endl;
    }
}