在 OpenCV 上使用 SVM 训练图像
Training images using SVM on OpenCV
我正在尝试对图像进行分类(下一步我将根据特征进行分类,但现在只想尝试我是否做对了)
这是我的代码。
#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 and http://docs.opencv.org/doc/tutorials/ml/introduction_to_svm/introduction_to_svm.html
来管理一些东西
我以某种方式设法训练了我的图像,但我发现这训练 xml 文件不正确,因为我没有指出哪个图像是正确的 (1) 或错误的 (-1)
而且当我尝试用我训练到 svm 的图像进行预测时,出现了错误
OpenCV Error: Sizes of input arguments do not match (The sample size
is different from what has been used for training) in
cvPreparePredictData, file
/tmp/opencv-DXLLi8/opencv-2.4.9/modules/ml/src/inner_functions.cpp,
line 1114 libc++abi.dylib: terminating with uncaught exception of type
cv::Exception:
/tmp/opencv-DXLLi8/opencv-2.4.9/modules/ml/src/inner_functions.cpp:1114:
error: (-209) The sample size is different from what has been used for
training in function cvPreparePredictData
也在这里 xml 由 SVM 生成。
<?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),但你用以下行做了:
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的问题,你可以去我的个人资料中查看。
希望对您有所帮助。
更新
您可以通过以下代码检查您是否创建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)
的大小。
我正在尝试对图像进行分类(下一步我将根据特征进行分类,但现在只想尝试我是否做对了)
这是我的代码。
#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 and http://docs.opencv.org/doc/tutorials/ml/introduction_to_svm/introduction_to_svm.html
来管理一些东西我以某种方式设法训练了我的图像,但我发现这训练 xml 文件不正确,因为我没有指出哪个图像是正确的 (1) 或错误的 (-1)
而且当我尝试用我训练到 svm 的图像进行预测时,出现了错误
OpenCV Error: Sizes of input arguments do not match (The sample size is different from what has been used for training) in cvPreparePredictData, file /tmp/opencv-DXLLi8/opencv-2.4.9/modules/ml/src/inner_functions.cpp, line 1114 libc++abi.dylib: terminating with uncaught exception of type cv::Exception: /tmp/opencv-DXLLi8/opencv-2.4.9/modules/ml/src/inner_functions.cpp:1114: error: (-209) The sample size is different from what has been used for training in function cvPreparePredictData
也在这里 xml 由 SVM 生成。
<?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),但你用以下行做了:
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的问题,你可以去我的个人资料中查看。
希望对您有所帮助。
更新
您可以通过以下代码检查您是否创建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)
的大小。