基于SVM的分类识别 opencv3.1.0

//#include <iostream>
//#include <string>
//#include <sstream>
//#include <cmath>


#include "opencv2/core.hpp"
#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/ml.hpp"
#include "utils/MultipleImageWindow.h"
using namespace std;
using namespace cv;
using namespace cv::ml;


MultipleImageWindow* miw;
Mat light_pattern;
//创建一个SVM模型
Ptr<SVM> svm;


Scalar green(0, 255, 0), blue(255, 0, 0), red(0, 0, 255);
float ar1, area1;
/**
* Extract the features for all objects in one image  检测一幅图中所有物体的特征
*
* @param Mat img input image
* @param vector<int> left output of left coordinates for each object 某个轮廓中心的x坐标
* @param vector<int> top output of top coordintates for each object 某个轮廓中心的y坐标
* @return vector< vector<float> > a matrix of rows of features for each object detectedoutput
第一层有n个元素,n即有多少个零件,第二次有2个元素,分别是面积和长宽比两个特征
**/
vector< vector<float> > ExtractFeatures(Mat img, vector<int>* left = NULL, vector<int>* top = NULL)
{
vector< vector<float> > output;
vector<vector<cv::Point> > contours; //存放轮廓数据
Mat input1 = img.clone();          //把原图复制一份,然后用这个副本去做轮廓检测,因为findContours函数会改变图像
Mat input;
vector<Vec4i> hierarchy;
cvtColor(input1, input, COLOR_BGR2GRAY);


findContours(input, contours, hierarchy, RETR_CCOMP, CHAIN_APPROX_SIMPLE);
// Check the number of objects detected
if (contours.size() == 0)
{
return output;
}
RNG rng(0xFFFFFFFF);
for (int i = 0; i<contours.size(); i++)
{
//先生成一个全黑的图像
Mat mask = Mat::zeros(img.rows, img.cols, CV_8UC1);
//图像上画出轮廓来,颜色为1
drawContours(mask, contours, i, Scalar(1), FILLED, LINE_8, hierarchy, 1);
//sum函数统计当前轮廓对应的图中每个通道的总和,这里取出0通道的非零点的总和
Scalar area_s = sum(mask);
float area = area_s[0];

if (area>500)//满足第一个条件面积条件
{
//宽高比
RotatedRect r = minAreaRect(contours[i]);
float width = r.size.width;
float height = r.size.height;
float ar = (width<height) ? height / width : width / height;


//output第一层有n个元素,n即有多少个零件,第二次有2个元素,分别是面积和长宽比两个特征
vector<float> row;
row.push_back(area);
row.push_back(ar);
output.push_back(row);
//如果轮廓中心到图像左上角和到图像顶部的坐标忽略了,那就加上
if (left != NULL)
{
left->push_back((int)r.center.x);
}
if (top != NULL)
{
top->push_back((int)r.center.y);
}


waitKey(10);
}
}
return output;
}


//使用光纹删除背景
Mat removeLight(Mat img, Mat pattern)
{

Mat aux;
// Require change our image to 32 float for division
Mat img32, pattern32;
img.convertTo(img32, CV_32F);
pattern.convertTo(pattern32, CV_32F);
//cout << pattern.channels() << endl;
//cout << img.channels() << endl;
//imshow("a",pattern32);
//waitKey(0);
//Divide the imabe by the pattern
/**/
aux = 1 - (img32 / pattern32);
// Scale it to convert o 8bit format
aux = aux * 255;
// Convert 8 bits format
aux.convertTo(aux, CV_8U);
//equalizeHist( aux, aux );
return aux;
}


/**对输入图像,进行滤波,去光照(背景),然后二值化
* Preprocess an input image to extract components and stats
* @params Mat input image to preprocess
* @return Mat binary image
*/
Mat preprocessImage(Mat input)
{

Mat result;
// Remove noise
Mat img_noise, img_box_smooth;
medianBlur(input, img_noise, 3);

//Apply the light pattern
Mat img_no_light;
img_noise.copyTo(img_no_light);
img_no_light = removeLight(img_noise, light_pattern);

// Binarize image for segment
threshold(img_no_light, result, 30, 255, THRESH_BINARY);


return result;
}


/**一次性读取完num_for_test张图片,每读一张照片然后提取特征,放到特征的向量里面
这里就是告诉电脑,有个图他是螺母(标签),他的面积特征是多少,长宽比特征是多少,电脑就根据这个去训练模型
* Read all images in a folder creating the train and test vectors
* @param label 因为螺母螺钉各自在不同文件夹里面,所以每文件夹内图片的标签是一样的,这里用标签0表示螺母,1垫圈,2螺钉
* @param number of images used for test and evaluate algorithm error
* @param trainingData 训练环节特征提取出来的两个特征,把这两个都放到trainingData这个向量里面
* @param reponsesData 训练环节的图片的label,数量是(总数-num_for_test)里面的所有特征数之和
* @param testData     测试环节的特征数据,也是两个特征放到一个里面
* @param testResponsesData 存储测试环节的label
* @return true if can read the folder images, false in error case
**/


bool readFolderAndExtractFeatures(string folder, int label, int num_for_test,
vector<float> &trainingData, vector<int> &responsesData,
vector<float> &testData, vector<float> &testResponsesData)
{
//使用VideoCapture读取文件夹内的图片
VideoCapture images;
//这个VideoCapture::open可以返回能不能读当前文件夹的图
if (images.open(folder) == false)
{
cout << "Can not open the folder images" << endl;
return false;
}
Mat frame;
int img_index = 0;
//VideoCapture::read则是每次从这个文件夹内读一张给frame,读完了这个while也就结束

while (images.read(frame))
{
//调用了图像预处理函数,功能是去光照背景,并二值化

Mat pre = preprocessImage(frame);

// Extract features,提取当前图片的特征,每一幅如螺母的图里面,可能有一个或几个螺母
vector< vector<float> > features = ExtractFeatures(pre);
for (int i = 0; i< features.size(); i++)
{
//前num_for_test张图片用来做训练
if (img_index <= num_for_test)
{
trainingData.push_back(features[i][0]);
trainingData.push_back(features[i][1]);
responsesData.push_back(label);
}
//后面的用来做测试
else
{


testData.push_back(features[i][0]);
testData.push_back(features[i][1]);
testResponsesData.push_back((float)label);
}
}
img_index++;
}

return true;
}
void plotTrainData(Mat trainData, Mat labels, float *error = NULL)
{
float area_max, ar_max, area_min, ar_min;
area_max = ar_max = 0;
area_min = ar_min = 99999999;
// Get the min and max of each feature for normalize plot image
for (int i = 0; i< trainData.rows; i++)
{
float area = trainData.at<float>(i, 0);
float ar = trainData.at<float>(i, 1);
if (area > area_max)
area_max = area;
if (ar > ar_max)
ar_max = ar;
if (area < area_min)
area_min = area;
if (ar < ar_min)
ar_min = ar;
}


// Create Image for plot
Mat plot = Mat::zeros(512, 512, CV_8UC3);
// Plot each of two features in a 2D graph using an image
// where x is area and y is aspect ratio
for (int i = 0; i< trainData.rows; i++){
// Set the X y pos for each data
float area = trainData.at<float>(i, 0);
float ar = trainData.at<float>(i, 1);
int x = (int)(512.0f*((area - area_min) / (area_max - area_min)));
int y = (int)(512.0f*((ar - ar_min) / (ar_max - ar_min)));


// Get label
int label = labels.at<int>(i);
// Set color depend of label
Scalar color;
if (label == 0)
color = green; // NUT
else if (label == 1)
color = blue; // RING
else if (label == 2)
color = red; // SCREW


circle(plot, Point(x, y), 3, color, -1, 8);
}


if (error != NULL){
stringstream ss;
ss << "Error: " << *error << "\%";
putText(plot, ss.str().c_str(), Point(20, 512 - 40), FONT_HERSHEY_SIMPLEX, 0.75, Scalar(200, 200, 200), 1, LINE_AA);
}
miw->addImage("Plot", plot);


}
//创建并训练SVM模型
//注意要先创建一个Ptr<SVM> svm;
void trainAndTest()
{
//它们四个的详细意义见readFolderAndExtractFeatures函数
vector< float > trainingData;
vector< int > responsesData;
vector< float > testData;
vector< float > testResponsesData;
//把多少张图拿来用作训练集
int num_for_test = 70;

//把螺母螺钉垫片三种图片各自放在三个不同的文件夹里,那么读取不同文件夹时,同时就可以判断是不同的label
readFolderAndExtractFeatures("data\\nut\\tuerca_%04d.pgm", 0, num_for_test, trainingData, responsesData, testData, testResponsesData);


readFolderAndExtractFeatures("data\\ring\\arandela_%04d.pgm", 1, num_for_test, trainingData, responsesData, testData, testResponsesData);


readFolderAndExtractFeatures("data\\screw\\tornillo_%04d.pgm", 2, num_for_test, trainingData, responsesData, testData, testResponsesData);

cout << "Num of train samples: " << responsesData.size() << endl;
cout << "Num of test samples: " << testResponsesData.size() << endl;


// 把向量里面的特征和标签转换为Mat格式以便传给tranning函数
Mat trainingDataMat(trainingData.size() / 2, 2, CV_32FC1, &trainingData[0]);
Mat responses(responsesData.size(), 1, CV_32SC1, &responsesData[0]);


Mat testDataMat(testData.size() / 2, 2, CV_32FC1, &testData[0]);
Mat testResponses(testResponsesData.size(), 1, CV_32FC1, &testResponsesData[0]);




svm = SVM::create();
svm->setType(SVM::C_SVC);
svm->setKernel(SVM::CHI2);
//设置使用的内核和停止学习过程的标准,最大迭代次数为100
svm->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER, 100, 1e-6));


//trainingData 训练环节特征提取出来的两个特征,把这两个都放到trainingData这个向量里面
//reponsesData 训练环节的图片的label,数量是(总数 - num_for_test)里面的所有特征数之和
svm->train(trainingDataMat, ROW_SAMPLE, responses);
//使用剩下的图作为测试集,来评估模型的准确性
if (testResponsesData.size()>0)
{
cout << "Evaluation" << endl;
cout << "==========" << endl;
// Test the ML Model
Mat testPredict;
svm->predict(testDataMat, testPredict);
cout << "Prediction Done" << endl;
//预测结果与测试集的标签不同就说明预测错误
Mat errorMat = testPredict != testResponses;
float error = 100.0f * countNonZero(errorMat) / testResponsesData.size();
cout << "Error: " << error << "\%" << endl;
//plotTrainData(trainingDataMat, responses, &error);
}
else{
plotTrainData(trainingDataMat, responses);
}
}






int main()
{
cout << "wait" << endl;
//pgm是灰度图像格式中一种最简单的格式标准
//背景图是用来进行图像背景的归一化,每次都把图像减去背景
String img_file = "data\\test2.pgm";
String light_pattern_file = "data\\pattern.pgm";




//Load image to process
Mat img = imread(img_file, -1);
if (img.data == NULL)
{
cout << "Error loading image " << img_file << endl;
return 0;
}


Mat img_output = img.clone();
cvtColor(img_output, img_output, COLOR_GRAY2BGR);


light_pattern = imread(light_pattern_file, 1);
if (light_pattern.data == NULL)
{
cout << "ERROR: Not light patter loaded" << endl;
return 0;
}
medianBlur(light_pattern, light_pattern, 3);

//使用准备好的数据,训练SVM模型
trainAndTest();

//对待分类的图像预处理
cvtColor(img, img, COLOR_GRAY2BGR);
Mat pre = preprocessImage(img);


//提取待分类图像的特征,得到待提取图片中每个轮廓(零件)的特征值
vector<int> pos_top, pos_left;
vector< vector<float> > features = ExtractFeatures(pre, &pos_left, &pos_top);


cout << "Num objects extracted features " << features.size() << endl;


//对于每个轮廓,使用训练好的SVM预测属于哪一类零件
for (int i = 0; i< features.size(); i++){
cout << "Data Area AR: " << features[i][0] << " " << features[i][1] << endl;
Mat trainingDataMat(1, 2, CV_32FC1, &features[i][0]);
cout << "Features to predict: " << trainingDataMat << endl;
float result = svm->predict(trainingDataMat);
cout << result << endl;




stringstream ss;
Scalar color;
if (result == 0)
{
color = green; // NUT
ss << "NUT-" <<  features[i][0]<<endl;
}
else if (result == 1){
color = blue; // RING
ss << "RING-" <<  features[i][0] << endl;
}
else if (result == 2){
color = red; // SCREW
ss << "SCREW-" <<  features[i][0] << endl;
}


putText(img_output,
ss.str(),
Point2d(pos_left[i], pos_top[i]),
FONT_HERSHEY_SIMPLEX,
0.4,
color);


}


//vector<int> results= evaluate(features);


/*// Show images
miw->addImage("Binary image", pre);
miw->addImage("Result", img_output);
miw->render();*/
imshow("out",img_output);
waitKey(0);





return 0;
}


/*******************************************************************************************************************************************************/

//以下为MultipleImageWindow.的头文件

#include "MultipleImageWindow.h"


MultipleImageWindow::MultipleImageWindow(string window_title, int cols, int rows, int flags)
{
    this->window_title= window_title;
    this->cols= cols;
    this->rows= rows;
    namedWindow(window_title, flags);
    moveWindow(window_title, 0, 0);
    // ToDo: detect resolution of desktop and show fullresolution canvas
    this->canvas_width= 1200;
    this->canvas_height= 700;
    this->canvas= Mat(this->canvas_height, this->canvas_width, CV_8UC3);     
    imshow(this->window_title, this->canvas);
}


int MultipleImageWindow::addImage(string title, Mat image, bool render)
{
    // Check if the title exists in the vector titles
    int index=-1;
    for(int i=0; i<this->titles.size(); i++){
        string t=this->titles[i];
        if(t.compare(title)==0){
            index=i;
            break;
        }
    }
    if(index==-1){
        this->titles.push_back(title);
        this->images.push_back(image);
    }else{
        this->images[index]= image;
    }
    if(render)
        this->render();
    return this->images.size()-1;
}


void MultipleImageWindow::removeImage(int pos)
{
    this->titles.erase(this->titles.begin()+pos);
    this->images.erase(this->images.begin()+pos);
}


void MultipleImageWindow::render()
{
    // Clean our canvas
    this->canvas.setTo( Scalar(20,20,20) );
    // width and height of cell. add 10 px of padding between images
    int cell_width= (canvas_width/cols); 
    int cell_height= (canvas_height/rows);
    int max_images=(this->images.size()>cols*rows)?cols*rows:this->images.size();
    int i=0;
    vector<string>::iterator titles_it= this->titles.begin();
    for(vector<Mat>::iterator it= this->images.begin(); it!= this->images.end(); ++it)
    {
        string title= *titles_it;
        int cell_x= (cell_width)*((i)%cols); // A
        int cell_y= (cell_height)*floor((i)/(float)cols);
        Rect mask(cell_x, cell_y, cell_width, cell_height);
        
        //For each cell draw an image if exists
        Mat cell(this->canvas, mask);
        // resize image to cell size
        Mat resized;
        double cell_aspect= (double)cell_width/(double)cell_height;
        Mat img= *it;
        double img_aspect= (double)img.cols/(double)img.rows;
        double f=(cell_aspect<img_aspect)?(double)cell_width/(double)img.cols:(double)cell_height/(double)img.rows;
        resize(img, resized, Size(0,0), f, f);
        if(resized.channels()==1){
            cvtColor(resized, resized, COLOR_GRAY2BGR); 
        }


        // Assign the image
        //Mat sub_cell(this->canvas, Rect(cell_x+1,cell_y+1,resized.cols, resized.rows));
        Mat sub_cell(this->canvas, Rect(cell_x+((cell_width-resized.cols)/2),cell_y+((cell_height-resized.rows)/2),resized.cols, resized.rows));
        resized.copyTo(sub_cell);


        putText(cell, title.c_str(), Point(20,20), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(200,0,0), 1, LINE_AA);


        // Draw a rectangle for each cell mat 
        rectangle(canvas, Rect(cell_x, cell_y, cell_width, cell_height), Scalar(200,200,200), 1);


        i++;
        ++titles_it;
        if(i==max_images)
            break;
    }


    // show image
    imshow(this->window_title, this->canvas);
}


  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值