在 OpenCV 上使用 SVM 训练图像

Posted

技术标签:

【中文标题】在 OpenCV 上使用 SVM 训练图像【英文标题】:Training images using SVM on OpenCV 【发布时间】:2015-06-03 14:07:07 【问题描述】:

我正在尝试对图像进行分类(下一步我将根据特征进行分类,但现在只想尝试是否正确)

这是我的代码。

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/ml/ml.hpp>

using namespace cv;
using namespace std;



int main()


    Mat image[2];
    image[0]= imread("image.jpg",0);

    image[1]= imread("wrongimage.jpg",0);

    Mat rotated = imread("image.jpg",0);


    image[0] = image[0].reshape(0, 1); //SINGLE LINE
    image[1] = image[1].reshape(0, 1); //SINGLE LINE


    //  image[0].convertTo(image[0], CV_32FC1); //CONVERT TO 32FC1
    //  image[1].convertTo(image[1], CV_32FC1); //CONVERT TO 32FC1


    Mat new_image(2,1,CV_32FC1,image); //CONVERT TO 32FC1



    float labels[2] = 1.0, -1.0;
    Mat labelsmat(2,1,CV_32FC1,labels); //correct labels 1

    labelsmat.convertTo(labelsmat, CV_32FC1);



    CvSVMParams params;
    params.svm_type = CvSVM::C_SVC;
    params.kernel_type = CvSVM::LINEAR;
    params.gamma = 3;
    params.degree = 3;
    CvSVM svm;
    svm.train(new_image, labelsmat, Mat(),Mat(),params);

    //    svm.train(training_mat2, labelsmat, Mat(),Mat(),params);

    // svm.train(training_mat2, labelsmat, Mat(), Mat(), params);
    svm.save("svm.xml"); // saving


    svm.load("svm.xml"); // loading


    rotated = rotated.reshape(0,1);
    rotated.convertTo(rotated, CV_32FC1);


     svm.predict(rotated);

由于使用 opencv svm 训练图像缺乏记录,我试图通过阅读 using OpenCV and SVM with images 和 http://docs.opencv.org/doc/tutorials/ml/introduction_to_svm/introduction_to_svm.html 来管理一些东西

我设法训练了我的图像,但我发现这个训练 xml 文件不正确,因为我没有指出哪个图像是正确的 (1) 或错误的 (-1)

当我尝试用我训练过的图像进行 svm 预测时,我会出错

OpenCV 错误:输入参数的大小不匹配(样本大小 与用于培训的不同)在 cvPreparePredictData,文件 /tmp/opencv-DXLi8/opencv-2.4.9/modules/ml/src/inner_functions.cpp, 第 1114 行 libc++abi.dylib:以未捕获的类型异常终止 简历::异常: /tmp/opencv-DXLLi8/opencv-2.4.9/modules/ml/src/inner_functions.cpp:1114: 错误:(-209)样本量与所使用的不同 在函数 cvPreparePredictData 中训练

这里也是 SVM 生成的 xml。

<?xml version="1.0"?>
<opencv_storage>
<my_svm type_id="opencv-ml-svm">
  <svm_type>C_SVC</svm_type>
  <kernel><type>LINEAR</type></kernel>
  <C>1.</C>
  <term_criteria><epsilon>1.1920928955078125e-07</epsilon>
    <iterations>1000</iterations></term_criteria>
  <var_all>1</var_all>
  <var_count>1</var_count>
  <class_count>2</class_count>
  <class_labels type_id="opencv-matrix">
    <rows>1</rows>
    <cols>2</cols>
    <dt>i</dt>
    <data>
      -1 1</data></class_labels>
  <sv_total>1</sv_total>
  <support_vectors>
    <_>
      -1.56709105e-02</_></support_vectors>
  <decision_functions>
    <_>
      <sv_count>1</sv_count>
      <rho>-1.</rho>
      <alpha>
        1.</alpha>
      <index>
        0</index></_></decision_functions></my_svm>
</opencv_storage>

更新

我已经根据 guneykayim 的建议更改了我的代码,但现在我得到了 EXC_BAD_ACCESS(代码=1 地址=...)错误。我的更新代码如下。

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/ml/ml.hpp>

using namespace cv;
using namespace std;



int main()


    Mat image[2];
    image[0]= imread("image.jpg",0);

    image[1]= imread("wrongimage.jpg",0);

    Mat rotated = imread("image.jpg",0);


   image[0] = image[0].reshape(0, 1); //SINGLE LINE
   image[1] = image[1].reshape(0, 1); //SINGLE LINE

 //   int size = sizeof(image)/sizeof(Mat);
    //  image[0].convertTo(image[0], CV_32FC1); //CONVERT TO 32FC1
    //  image[1].convertTo(image[1], CV_32FC1); //CONVERT TO 32FC1

    Mat new_image(2,341318,CV_32FC1,image); //CONVERT TO 32FC1

    float labels[2] = 1.0, -1.0;
    Mat labelsmat(2,1,CV_32FC1,labels); //correct labels 1

    labelsmat.convertTo(labelsmat, CV_32FC1);


    cout<<image[0].size()<<endl;
    cout<<new_image.size()<<endl;



    CvSVMParams params;
    params.svm_type = CvSVM::C_SVC;
    params.kernel_type = CvSVM::LINEAR;
    params.gamma = 3;
    params.degree = 3;
    CvSVM svm;
    svm.train_auto(new_image, labelsmat,Mat(),Mat(),params);

    //  svm.train_(new_image, labelsmat, Mat(),Mat(),params);
    //    svm.train(training_mat2, labelsmat, Mat(),Mat(),params);

    // svm.train(training_mat2, labelsmat, Mat(), Mat(), params);
    svm.save("svm.xml"); // saving


    svm.load("svm.xml"); // loading


    rotated = rotated.reshape(0,1);
    rotated.convertTo(rotated, CV_32FC1);

    cout<<svm.predict(rotated)<<endl;


我的图像尺寸是:[170569 x 1] new_image 大小为 [341318 x 2]

【问题讨论】:

【参考方案1】:

有几件事你做错了,或者你不知道自己在做什么。

    您说您没有指出哪个图像是正确的 (1) 或错误的 (-1),但是您使用以下行指出了:float labels[2] = 1.0, -1.0; 您创建的训练集有误。假设您的图像大小为640x480。然后当你重塑它们时,它们将是307200 大小的向量,这很好。因此,您称为new_image 的训练集应该是2x307200 大小的,并且每一行应该代表一个图像,但是您创建的是 2x1 大小的训练集。这就是为什么当您尝试预测时会收到错误消息Sizes of input arguments do not match。您使用 2x1 大小的训练集训练了 SVM,并尝试使用 1x307200 向量进行预测。

除此之外,您不应该使用幻数设置 SVM 参数,您需要通过交叉验证来优化它们。在这个玩具例子中你当然不能做参数优化,我想说的是你应该知道设置 SVM 参数是一项非常关键的任务。

我已经回答了几个关于 SVM 的问题,您可以从我的个人资料中查看。

希望这会有所帮助。

更新

您可以通过以下代码检查您的 creatnig new_image 是否正确:

Mat image[2];
image[0]= imread("image.jpg",0);
image[1]= imread("wrongimage.jpg",0);

// dont reshape them for debugging purposes...
//image[0] = image[0].reshape(0, 1); //SINGLE LINE
//image[1] = image[1].reshape(0, 1); //SINGLE LINE

// I assume that images are 640x480, change the value accordig to the image sizes, or directly use values in the size parameter
Mat new_image(1280, 480, CV_32FC1, image); //CONVERT TO 32FC1

// visualize the image see if the previous line of code does its job correctly.
imshow("new_image", new_image);

更新 2

您需要从您的 cv::Mat 数组创建单个 cv::Mat 对象。显然,以下行无法正确执行。

Mat new_image(2,341318,CV_32FC1,image); 

找个合适的方法吧,目前我没有安装OpenCV环境,你基本上可以再问一个问题。

请记住,您的new_image 变量的大小应为[image_count * (image_width * image_height)],并且每一行应将图像矢量表示为1 * (image_width * image_height) 的大小。

【讨论】:

@Rodrane 很可能您无法正确创建new_image。确保您正确创建它。为什么注释掉图片类型转换代码?请使用新代码更新您的问题,不要分享这样的链接。最好将更新后的代码贴在原始问题的底部。 您好,我也更新了我的问题,但您的解决方案并没有帮助我重塑或正常 @Rodrane 没有帮助是什么意思?当您尝试显示图像时发生了什么?你看到正确的图像了吗?如果你这样做了,我目前不知道问题是什么,可能是cv::Mat 类型。如果您没有看到正确的图像,那么您的问题肯定是创建 new_image 变量。 因为我已经发布了大小的图像,它似乎很好,但是我仍然收到 exc_bad_access 错误。 @Rodrane 您没有阅读我写的内容 :) 请按照我在更新的答案中所写的那样可视化输出,以查看是否正确生成了 new_image。正确的大小并不意味着它是正确创建的。

以上是关于在 OpenCV 上使用 SVM 训练图像的主要内容,如果未能解决你的问题,请参考以下文章

OpenCV 3.2.0:读取 Canny 图像以训练 SVM

SVM 训练 C++ OpenCV

OpenCV for Android - 使用 SURF 描述符训练 SVM

如何使用基于一组图像的 opencv 训练 SVM?

训练 SVM 时出现 OpenCV 错误(错误参数)

训练 SVM 并使用 OpenCV 3.0 保存它