在OpenCV上使用SVM训练图像

4

我正在尝试使用图像进行分类(下一步将基于特征进行分类,但现在只想尝试一下是否正确)

这是我的代码。

#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的训练图像缺乏文档记录,我尝试通过阅读使用OpenCV和SVM处理图像以及http://docs.opencv.org/doc/tutorials/ml/introduction_to_svm/introduction_to_svm.html来管理一些东西。

不知何故,我成功地训练了我的图像,但我认为这个训练XML文件不正确,因为我没有指出哪个图像是正确的(1)或错误的(-1)。

当我尝试使用已经训练好的图像进行预测时,它给我一个错误。

OpenCV错误:输入参数的大小不匹配(样本大小与训练所用的不同)在cvPreparePredictData中, 文件/tmp/opencv-DXLLi8/opencv-2.4.9/modules/ml/src/inner_functions.cpp, 行1114 libc++abi.dylib:以未捕获的异常类型终止 cv :: Exception: /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 (code=1 address=...) 错误。下面是我的更新后的代码。

#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],而新图片的尺寸是 [341318 x 2]

1个回答

8
有几件事情你做错了或者你没有意识到。
  1. 你说你没有指出哪个图像是正确的(1)还是错误的(-1),但是你在下面这行代码中已经指出了:float labels[2] = {1.0, -1.0};
  2. 你创建训练集的方法不对。假设你的图像大小为640x480。当你将它们重塑时,它们将成为307200大小的向量,这很好。因此,你称之为new_image的训练集应该是2x307200大小的,每行代表一张图片,但是你却创建了一个2x1大小的训练集。这就是为什么当你尝试预测时会出现错误,提示输入参数的大小不匹配。你使用2x1大小的训练集来训练SVM,并尝试使用1x307200向量进行预测。

除此之外,你不应该使用魔法数字来设置SVM参数,你需要使用交叉验证来优化它们。在这个玩具例子中,当然你无法进行参数优化,我想说的是你应该意识到设置SVM参数是一个非常关键的任务。

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

希望这能帮到你。

更新

你可以通过以下代码检查是否正确创建了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)


1
@Rodrane 很可能是你无法正确创建 new_image。请确保你正在正确地创建它。为什么要注释掉图像类型转换代码?请更新你的问题并附上新代码,不要分享这样的链接。最好在原始问题的底部发布更新后的代码。 - guneykayim
嗨,我也更新了我的问题,但你的解决方案没有帮助我改变或正常化。 - Anar Bayramov
@Rodrane,你说的“没有帮助”是什么意思?当你尝试显示图像时发生了什么?你看到了一个正确的图像吗?如果是的话,我目前不知道问题出在哪里,可能与cv::Mat类型有关。如果你没有看到一个正确的图像,那么你的问题肯定是在创建new_image变量时出现了问题。 - guneykayim
2
@Rodrane 你没有仔细阅读我的话 :) 请按照我在更新的答案中所写的方式可视化输出,以查看 new_image 是否正确生成。正确的大小并不意味着它被正确创建。 - guneykayim
是的,它不能简单地可视化图像,因此在重新调整大小后,我需要正确地将图像一层层添加到另一个上面,然后再在SVM上进行训练。 - Anar Bayramov
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接