C++ OpenCV SVM 预测不工作

Posted

技术标签:

【中文标题】C++ OpenCV SVM 预测不工作【英文标题】:C++ OpenCV SVM Predict Not Working 【发布时间】:2015-03-15 02:03:06 【问题描述】:

试图创建一个功能性的SVM。我有 114 张训练图像、60 张正面图像/54 张负面图像和 386 张测试图像供 SVM 预测。

我将训练图像特征读入float,如下所示:

trainingDataFloat[i][0] = trainFeatures.rows;
trainingDataFloat[i][1] = trainFeatures.cols;

测试图像也是如此:

testDataFloat[i][0] = testFeatures.rows;
testDataFloat[i][2] = testFeatures.cols;

然后,使用 Micka 的 answer to this question,我将 testDataFloat 转换为一维数组,然后像这样将其输入到 Mat,以便在 SVM 上进行预测:

float* testData1D = (float*)testDataFloat;
Mat testDataMat1D(height*width, 1, CV_32FC1, testData1D);
float testPredict = SVMmodel.predict(testDataMat1D);

一切就绪后,就会出现以下调试错误:

cvPreparePredictData 中输入参数的大小不匹配(样本大小与用于训练的大小不同)

看着this post,我发现(感谢berak):

“所有图像(用于训练和预测)的大小必须相同”

所以我添加了一个调整大小的功能,可以将图像重新调整为您希望的任意大小的正方形 (100x100, 200x200, 1000, 1000 etc.)

再次运行它,将图像重新调整到程序现在从中加载图像的新目录,我得到与以前完全相同的错误:

cvPreparePredictData 中输入参数的大小不匹配(样本大小与用于训练的大小不同)

我只是不知道该怎么做。为什么它仍然抛出该错误?

编辑

我变了

Mat testDataMat1D(TestDFheight*TestDFwidth, 1, CV_32FC1, testData1D);

Mat testDataMat1D(1, TestDFheight*TestDFwidth, CV_32FC1, testData1D);

并将.predict 放在将features 分配给float 的循环中,以便每个图像都单独分配给.predict,因为this question。随着 int 的交换,.cols = 1 和 .rows = TestDFheight*TestDFwidth 程序似乎实际运行,但随后在图像 160 (.exe has stopped working) 上停止...所以这是一个新问题。

编辑 2

添加了一个简单的

std::cout << testPredict;

查看 SVM 的确定输出,它似乎正匹配所有内容,直到 Image 160 停止运行:

【问题讨论】:

请发布您的svm.train(...); 行并详细说明您提供给它的参数。 我正在使用默认的 SVM 参数和火车:SVMmodel.train(trainingDataMat, labelsMat, Mat(), Mat(), SVMParams()); trainingDataMat 的尺寸是多少,您的训练和预测图像的尺寸是多少? Training Mat: Mat trainingDataMat(height, width, CV_32FC1, trainingDataFloat); - height = 114 width = 2. 训练和测试的图像都调整为 500x500 根据***.com/questions/14694810/… 它应该类似于:Mat trainingDataMat(114, 500*500, CV_32FC1, trainingDataFloat); 而不是...其中 114 是输入图像的数量,而 500*500 是您想要输入的一维数组的大小稍后预测。 【参考方案1】:

请检查您的训练和测试特征向量。

我假设您的特征数据是某种形式的 cv::Mat,每行都包含特征。 在这种情况下,您希望您的训练矩阵是每个图像中每个特征矩阵的串联。 这些行看起来不正确:

trainingDataFloat[i][0] = trainFeatures.rows;
trainingDataFloat[i][1] = trainFeatures.cols;

这是将二维矩阵的元素设置为 trainFeatures 中的行数和列数。这与 trainFeatures 矩阵中的实际数据无关。

你想检测什么?如果每个图像都是正面和负面的例子,那么您是否试图检测图像中的某些东西?你有什么特点?

如果您尝试在每张图像的基础上检测图像中的对象,那么您需要一个特征向量在一个向量中描述整个图像。在这种情况下,您会对训练数据执行以下操作:

int N; // Set to number of images you plan on using for training
int feature_size; // Set to the number of features extracted in each image.  Should be constant across all images.

cv::Mat X = cv::Mat::zeros(N, feature_size, CV_32F); // Feature matrix
cv::Mat Y = cv::Mat::zeros(N, 1, CV_32F); // Label vector
// Now use a for loop to copy data into X and Y, Y = +1 for positive examples and -1 for negative examples
for(int i = 0; i < trainImages.size(); ++i)

  X.row(i) = trainImages[i].features; // Where features is a cv::Mat row vector of size N of the extracted features
  Y.row(i) = trainImages[i].isPositive ? 1:-1; 

// Now train your cv::SVM on X and Y.

【讨论】:

当你说“应该在所有图像中保持不变。”,你的意思是每个图像提取的特征数量应该相同吗?并设置feature_size 我会喂它trainFeatures.rows?你会在一个循环中完成所有这些,然后再训练吗? 另外,trainImages 将是 vector 是吗?而且我不明白你的措辞“特征是提取特征的大小为 N 的 cv::Mat 行向量” 是的,要能够训练 SVM,您需要恒定数量的特征。我假设 trainImages 是所有特征图像结构的向量。该结构将只包含一个用于该图像特征的 cv::Mat 和一个布尔参数 isPositive,如果它是一个正类则为 true。 但是如果你将所有特征的SVMMat 一起提供,为什么每张图像都需要检测到完全相同数量的特征呢?我的印象是,你不能一次训练一个 SVM ,而是给它一个 Mat,其中每个 row 包含每个单独图像的特征。 您确实需要将所有特征连接到我上面描述的 X 矩阵中。该矩阵每行包含一个样本。现在的问题更是如此,你在寻找什么?如果您从每个图像中提取可变数量的特征,并且您知道每个特征对应于您的目标,那么您可以在每个特征上训练 SVM 是可行的。但是例如,如果您要提取 sift 并在 sift 上训练 SVM,那么您正在训练 SVM 以基于单个提取点检测整个对象。所以请更清楚地描述你的目标。

以上是关于C++ OpenCV SVM 预测不工作的主要内容,如果未能解决你的问题,请参考以下文章

OpenCV 3.0 svm 总是预测 0 (java)

OpenCV4Android SVM 没有给出正确的预测

LibSVM 预测不工作

SVM 训练 C++ OpenCV

opencv 3 (C++) 自动训练的 SVM 加载问题

opencv C++ SVM模型训练与分类实现