我相信我已经成功训练了一个SVM
模型,但是当我尝试使用它进行预测时,输出全是1。
我的训练代码如下:
for(size_t i = 0; i < (testPosArraySize); i++){ testGivenImg = imread(imagePosDir[i]); detector->detect(testGivenImg, testKeypointsPos); bowDE.compute(testGivenImg, testKeypointsPos, testFeaturesPos); testFeaturesPos.reshape(1, 1); testFeaturesVec.push_back(testFeaturesPos);}for(size_t i = 0; i < (testNegaArraySize); i++){ testGivenImg = imread(image[i]); detector->detect(testGivenImg, testKeypointsNega); bowDE.compute(testGivenImg, testKeypointsNega, testFeaturesNega); testFeaturesNega.reshape(1, 1); testFeaturesVec.push_back(testFeaturesNega);}Mat labels(numSamples, 1, CV_32F);labels.rowRange(0, testPosArraySize).setTo(1);labels.rowRange(testPosArraySize + 1, numSamples).setTo(-1);SVM.model.train(fileTestFeat, labels, Mat(), Mat(), SVMParams());
我的预测代码如下:
vector<Mat> predictMatVec(predictArraySize); // -- 测试图像的数量for(size_t i = 0; i < (predictArraySize); i++){ predictImg = imread(imageNegaDir[i]); detector->detect(predictImg, predictKeypoints); bowDE.compute(predictImg, predictKeypoints, predictFeatures); predictFeatures.reshape(1, 1); predictMatVec[i].push_back(predictFeatures); Mat predictMat = Mat(predictMatVec); float* predictFloat1D = (float*)predictMat.data; Mat predictMat1D(1, fileTestFeat.cols, CV_32FC1, predictFloat1D); float predictFloat = model.predict(predictMat1D); cout << " -- SVM输出: " << predictFloat << endl; }
但它只返回1。
哪里出了问题?
回答:
所以,词汇表已经创建好了(例如通过BOWKMeansTrainer
),然后你开始训练你的SVM分类器,对吗?
此时,你已经有了一个特征检测器、提取器、匹配器和一个BOW图像描述符提取器(用于使用视觉词袋计算图像描述符),例如:
cv::Ptr<cv::FeatureDetector> detector = cv::FeatureDetector::create("SURF");cv::Ptr<cv::DescriptorExtractor> extractor = cv::DescriptorExtractor::create("SURF");cv::Ptr<cv::DescriptorMatcher> matcher = cv::DescriptorMatcher::create("BruteForce ");cv::BOWImgDescriptorExtractor bowide(extractor, matcher);bowide->setVocabulary(vocabulary);
首先,我们需要在训练集中搜索我们的直方图:
cv::Mat samples;cv::Mat labels(0, 1, CV_32FC1);for(auto& it : imagePosDir){ cv::Mat image = cv::imread(it); std::vector<cv::KeyPoint> keypoints; detector->detect(image, keypoints); if(keypoints.empty()) continue; // 对词汇表的响应 cv::Mat imgDescriptor; bowide.compute(image, keypoints, imgDescriptor); if(imgDescriptor.empty()) continue; if(samples.empty()) { samples.create(0, imgDescriptor.cols, imgDescriptor.type()); } // 复制类样本和标签 std::cout << "添加 " << imgDescriptor.rows << " 个正样本。" << std::endl; samples.push_back(imgDescriptor); cv::Mat classLabels = cv::Mat::ones(imgDescriptor.rows, 1, CV_32FC1); labels.push_back(classLabels);}
对imagePosNeg
做同样的事情,只是classLabels
将具有零值,例如:
...cv::Mat classLabels = cv::Mat::zeros(imgDescriptor.rows, 1, CV_32FC1);labels.push_back(classLabels);...
注意我是如何构建样本和标签的,我用标签’1’标记了正样本,然后用标签’0’标记了负样本。所以我们有每个类别的训练数据(这里是正样本和负样本)在samples
中。让我们开始训练:
cv::Mat samples_32f; samples.convertTo(samples_32f, CV_32F);CvSVM svm; svm.train(samples_32f, labels);// 对分类器做一些事情,比如将其保存到文件中
然后进行测试,让我们测试分类器:
for(auto& it : testDir){ cv::Mat image = cv::imread(it); std::vector<cv::KeyPoint> keypoints; detector->detect(image, keypoints); if(keypoints.empty()) continue; // 对词汇表的响应 cv::Mat imgDescriptor; bowide.compute(image, keypoints, imgDescriptor); if(imgDescriptor.empty()) continue; float res = svm.predict(imgDescriptor, true); std::cout << "- 预测结果: " << res << std::endl;}
它工作了吗?
更新#1:
在这里,我做了一个关于OpenCV 3.0下BOW+SVM的简单示例:https://github.com/bkornel/OpenCV_BOW_SVM/blob/master/main.cpp
这个示例对我来说很好地分类了可口可乐/百事可乐的瓶子。我还发布了二进制文件,以便你可以在你的数据库上尝试一下。希望它能工作 🙂