HOG+SVM行人检测的两种方法

来源:互联网 发布:楚天手动编程g代码结尾 编辑:程序博客网 时间:2024/05/20 18:17

HOG+SVM行人检测的两种方法

轉載http://blog.csdn.net/qianqing13579/article/details/46509037

2015年06月15日 22:34:31

关于HOG+SVM,CSDN上有一些非常好的文章,这里给出我觉得写的比较好的几篇,仅供大家参考

目标检测的图像特征提取之(一)HOG特征

HOG:从理论到OpenCV实践

opencv 学习笔记-入门(21)之三线性插值-hog(二)
这篇博客写的是关于三线性插值的,为了减少混叠效应的,写的很好

OpenCV中的HOG+SVM物体分类

OpenCV HOGDescriptor 参数图解

利用Hog特征和SVM分类器进行行人检测

以上就是个人觉得写的比较好的博客,基本上将上面的博客看懂了,HOG也比较理解了,如果还想输入了解HOG,建议直接看OpenCV HOG的源码

下面,就说说使用OpenCV 中的HOG+SVM实现行人检测的两种方式

说明:程序运行环境为VS2013+OpenCV3.0

第一种

先说第一种方式,直接上代码:

///////////////////////////////////HOG+SVM识别方式2///////////////////////////////////////////////////  void Train(){    ////////////////////////////////读入训练样本图片路径和类别///////////////////////////////////////////////////    //图像路径和类别    vector<string> imagePath;    vector<int> imageClass;    int numberOfLine = 0;    string buffer;    ifstream trainingData(string(FILEPATH)+"TrainData.txt");    unsigned long n;    while (!trainingData.eof())    {        getline(trainingData, buffer);        if (!buffer.empty())        {            ++numberOfLine;            if (numberOfLine % 2 == 0)            {                //读取样本类别                imageClass.push_back(atoi(buffer.c_str()));            }            else            {                //读取图像路径                imagePath.push_back(buffer);            }        }    }    //关闭文件      trainingData.close();    ////////////////////////////////获取样本的HOG特征///////////////////////////////////////////////////    //样本特征向量矩阵    int numberOfSample = numberOfLine / 2;    Mat featureVectorOfSample(numberOfSample, 3780, CV_32FC1);//矩阵中每行为一个样本    //样本的类别    Mat classOfSample(numberOfSample, 1, CV_32SC1);    Mat convertedImage;    Mat trainImage;    // 计算HOG特征    for (vector<string>::size_type i = 0; i <= imagePath.size() - 1; ++i)    {        //读入图片        Mat src = imread(imagePath[i], -1);        if (src.empty())        {            cout << "can not load the image:" << imagePath[i] << endl;            continue;        }        //cout << "processing:" << imagePath[i] << endl;        // 归一化        resize(src, trainImage, Size(64, 128));        // 提取HOG特征        HOGDescriptor hog(cvSize(64, 128), cvSize(16, 16), cvSize(8, 8), cvSize(8, 8), 9);        vector<float> descriptors;        double time1 = getTickCount();        hog.compute(trainImage, descriptors);//这里可以设置检测窗口步长,如果图片大小超过64×128,可以设置winStride        double time2 = getTickCount();        double elapse_ms = (time2 - time1) * 1000 / getTickFrequency();        //cout << "HOG dimensions:" << descriptors.size() << endl;        //cout << "Compute time:" << elapse_ms << endl;        //保存到特征向量矩阵中        for (vector<float>::size_type j = 0; j <= descriptors.size() - 1; ++j)        {            featureVectorOfSample.at<float>(i, j) = descriptors[j];        }        //保存类别到类别矩阵        //!!注意类别类型一定要是int 类型的        classOfSample.at<int>(i, 0) = imageClass[i];    }    ///////////////////////////////////使用SVM分类器训练///////////////////////////////////////////////////        //设置参数,注意Ptr的使用    Ptr<SVM> svm = SVM::create();    svm->setType(SVM::C_SVC);    svm->setKernel(SVM::LINEAR);//注意必须使用线性SVM进行训练,因为HogDescriptor检测函数只支持线性检测!!!    svm->setTermCriteria(TermCriteria(CV_TERMCRIT_ITER, 1000, FLT_EPSILON));    //使用SVM学习             svm->train(featureVectorOfSample, ROW_SAMPLE, classOfSample);    //保存分类器(里面包括了SVM的参数,支持向量,α和rho)    svm->save(string(FILEPATH) + "Classifier.xml");    /*    SVM训练完成后得到的XML文件里面,有一个数组,叫做support vector,还有一个数组,叫做alpha,有一个浮点数,叫做rho;    将alpha矩阵同support vector相乘,注意,alpha*supportVector,将得到一个行向量,将该向量前面乘以-1。之后,再该行向量的最后添加一个元素rho。    如此,变得到了一个分类器,利用该分类器,直接替换opencv中行人检测默认的那个分类器(cv::HOGDescriptor::setSVMDetector()),    */    //获取支持向量机:矩阵默认是CV_32F    Mat supportVector = svm->getSupportVectors();//    //获取alpha和rho    Mat alpha;//每个支持向量对应的参数α(拉格朗日乘子),默认alpha是float64的    Mat svIndex;//支持向量所在的索引    float rho = svm->getDecisionFunction(0, alpha, svIndex);    //转换类型:这里一定要注意,需要转换为32的    Mat alpha2;    alpha.convertTo(alpha2, CV_32FC1);    //结果矩阵,两个矩阵相乘    Mat result(1, 3780, CV_32FC1);    result = alpha2*supportVector;    //乘以-1,这里为什么会乘以-1?    //注意因为svm.predict使用的是alpha*sv*another-rho,如果为负的话则认为是正样本,在HOG的检测函数中,使用rho+alpha*sv*another(another为-1)    for (int i = 0; i < 3780; ++i)        result.at<float>(0, i) *= -1;    //将分类器保存到文件,便于HOG识别    //这个才是真正的判别函数的参数(ω),HOG可以直接使用该参数进行识别    FILE *fp = fopen((string(FILEPATH) + "HOG_SVM.txt").c_str(), "wb");    for (int i = 0; i<3780; i++)    {        fprintf(fp, "%f \n", result.at<float>(0,i));    }    fprintf(fp, "%f", rho);    fclose(fp);}// 使用训练好的分类器识别void Detect(){    Mat img;    FILE* f = 0;    char _filename[1024];    // 获取测试图片文件路径    f = fopen((string(FILEPATH) + "TestData.txt").c_str(), "rt");    if (!f)    {        fprintf(stderr, "ERROR: the specified file could not be loaded\n");        return;    }    //加载训练好的判别函数的参数(注意,与svm->save保存的分类器不同)    vector<float> detector;    ifstream fileIn(string(FILEPATH) + "HOG_SVM.txt", ios::in);    float val = 0.0f;    while (!fileIn.eof())    {        fileIn >> val;        detector.push_back(val);    }    fileIn.close();    //设置HOG    HOGDescriptor hog;    hog.setSVMDetector(detector);// 使用自己训练的分类器    //hog.setSVMDetector(HOGDescriptor::getDefaultPeopleDetector());//可以直接使用05 CVPR已训练好的分类器,这样就不用Train()这个步骤了    namedWindow("people detector", 1);    // 检测图片    for (;;)    {        // 读取文件名        char* filename = _filename;        if (f)        {            if (!fgets(filename, (int)sizeof(_filename)-2, f))                break;            //while(*filename && isspace(*filename))            //  ++filename;            if (filename[0] == '#')                continue;            //去掉空格            int l = (int)strlen(filename);            while (l > 0 && isspace(filename[l - 1]))                --l;            filename[l] = '\0';            img = imread(filename);        }        printf("%s:\n", filename);        if (!img.data)            continue;        fflush(stdout);        vector<Rect> found, found_filtered;        double t = (double)getTickCount();        // run the detector with default parameters. to get a higher hit-rate        // (and more false alarms, respectively), decrease the hitThreshold and        // groupThreshold (set groupThreshold to 0 to turn off the grouping completely).        //多尺度检测        hog.detectMultiScale(img, found, 0, Size(8, 8), Size(32, 32), 1.05, 2);        t = (double)getTickCount() - t;        printf("detection time = %gms\n", t*1000. / cv::getTickFrequency());        size_t i, j;        //去掉空间中具有内外包含关系的区域,保留大的        for (i = 0; i < found.size(); i++)        {            Rect r = found[i];            for (j = 0; j < found.size(); j++)            if (j != i && (r & found[j]) == r)                break;            if (j == found.size())                found_filtered.push_back(r);        }        // 适当缩小矩形        for (i = 0; i < found_filtered.size(); i++)        {            Rect r = found_filtered[i];            // the HOG detector returns slightly larger rectangles than the real objects.            // so we slightly shrink the rectangles to get a nicer output.            r.x += cvRound(r.width*0.1);            r.width = cvRound(r.width*0.8);            r.y += cvRound(r.height*0.07);            r.height = cvRound(r.height*0.8);            rectangle(img, r.tl(), r.br(), cv::Scalar(0, 255, 0), 3);        }        imshow("people detector", img);        int c = waitKey(0) & 255;        if (c == 'q' || c == 'Q' || !f)            break;    }    if (f)        fclose(f);    return;}void HOG_SVM2(){    //如果使用05 CVPR提供的默认分类器,则不需要Train(),直接使用Detect检测图片    Train();    Detect();}int main(){    //HOG+SVM识别方式1:直接输出类别    //HOG_SVM1();    //HOG+SVM识别方式2:输出图片中的存在目标的矩形    HOG_SVM2();}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256

这里我想说明一下TrainData.txt,这个文件放置了所有样本的路径和类别,如下:
这里写图片描述

于如何读取正负样本的路径到txt文件,可以使用批处理文件,批处理文件我上传到了CSDN,大家可以去下载
点击下载

正负样本至少保证有1000,不能太少,否则效果就不好了,其中HOG_SVM.txt里面包含了判别函数的参数,这个参数可以直接给HOG用
下面就是我的测试效果:
这里写图片描述

这里写图片描述

检测效果还可以.
测试图片我也上传到网上了
点击下载

当然你也可以不用自己训练分类器,直接使用OpenCV自带的分类器,OpenCV自带的分类器使用的是05年CVPR那篇文章中作者训练好的分类器,下面我们就来看看效果:
这里写图片描述

这里写图片描述

图中可以看出,OpenCV自带的分类器效果要比自己训练的好,主要原因大概有以下几点
1.训练样本不足,我的正负样本才900多
2.正样本图片不够清晰,导致特征提取有比较大的误差

最近有人在运行博客中程序的时候出现了问题,让我看看程序,我也不太清楚什么问题,所以我将整个程序和程序测试数据打包了一下,上传到了CSDN上。
点击下载
解压后,将”Pedestrians64x128”文件夹放置在D盘根目录,然后使用HOG.cpp新建一个工程,直接可以运行。

注:环境是VS2013+OpenCV3.0.0,Release版本

第二种

下面说说第二种方式,第二种方式就是传统的方式了,就是对于测试样本,提取特征,然后使用训练好的分类器进行识别,代码

///////////////////////////////////HOG+SVM识别方式1///////////////////////////////////////////////////void HOG_SVM1(){    ////////////////////////////////读入训练样本图片路径和类别///////////////////////////////////////////////////    //图像路径和类别    vector<string> imagePath;    vector<int> imageClass;    int numberOfLine = 0;    string buffer;    ifstream trainingData(string(FILEPATH) + "TrainData.txt", ios::in);    unsigned long n;    while (!trainingData.eof())    {        getline(trainingData, buffer);        if (!buffer.empty())        {            ++numberOfLine;            if (numberOfLine % 2 == 0)            {                //读取样本类别                imageClass.push_back(atoi(buffer.c_str()));            }            else            {                //读取图像路径                imagePath.push_back(buffer);            }        }    }    trainingData.close();    ////////////////////////////////获取样本的HOG特征///////////////////////////////////////////////////    //样本特征向量矩阵    int numberOfSample = numberOfLine / 2;    Mat featureVectorOfSample(numberOfSample, 3780, CV_32FC1);//矩阵中每行为一个样本    //样本的类别    Mat classOfSample(numberOfSample, 1, CV_32SC1);    //开始计算训练样本的HOG特征    for (vector<string>::size_type i = 0; i <= imagePath.size() - 1; ++i)    {        //读入图片        Mat src = imread(imagePath[i], -1);        if (src.empty())        {            cout << "can not load the image:" << imagePath[i] << endl;            continue;        }        cout << "processing" << imagePath[i] << endl;        //缩放        Mat trainImage;        resize(src, trainImage, Size(64, 128));        //提取HOG特征        HOGDescriptor hog(Size(64, 128), Size(16, 16), Size(8, 8), Size(8, 8), 9);        vector<float> descriptors;        hog.compute(trainImage, descriptors);//这里可以设置检测窗口步长,如果图片大小超过64×128,可以设置winStride        cout << "HOG dimensions:" << descriptors.size() << endl;        //保存特征向量矩阵中        for (vector<float>::size_type j = 0; j <= descriptors.size() - 1; ++j)        {            featureVectorOfSample.at<float>(i, j) = descriptors[j];        }        //保存类别到类别矩阵        //!!注意类别类型一定要是int 类型的        classOfSample.at<int>(i, 0) = imageClass[i];    }    ///////////////////////////////////使用SVM分类器训练///////////////////////////////////////////////////        //设置参数    //参考3.0的Demo    Ptr<SVM> svm = SVM::create();    svm->setKernel(SVM::RBF);    svm->setType(SVM::C_SVC);    svm->setC(10);    svm->setCoef0(1.0);    svm->setP(1.0);    svm->setNu(0.5);    svm->setTermCriteria(TermCriteria(CV_TERMCRIT_EPS, 1000, FLT_EPSILON));    //使用SVM学习             svm->train(featureVectorOfSample, ROW_SAMPLE, classOfSample);    //保存分类器    svm->save("Classifier.xml");    ///////////////////////////////////使用训练好的分类器进行识别///////////////////////////////////////////////////    vector<string> testImagePath;    ifstream testData(string(FILEPATH) + "TestData.txt", ios::out);    while (!testData.eof())    {        getline(testData, buffer);        //读取        if (!buffer.empty())            testImagePath.push_back(buffer);    }    testData.close();    ofstream fileOfPredictResult(string(FILEPATH) + "PredictResult.txt"); //最后识别的结果    for (vector<string>::size_type i = 0; i <= testImagePath.size() - 1; ++i)    {        //读取测试图片        Mat src = imread(testImagePath[i], -1);        if (src.empty())        {            cout << "Can not load the image:" << testImagePath[i] << endl;            continue;        }        //缩放        Mat testImage;        resize(src, testImage, Size(64, 64));        //测试图片提取HOG特征        HOGDescriptor hog(cvSize(64, 64), cvSize(16, 16), cvSize(8, 8), cvSize(8, 8), 9);        vector<float> descriptors;        hog.compute(testImage, descriptors);        cout << "HOG dimensions:" << descriptors.size() << endl;        Mat featureVectorOfTestImage(1, descriptors.size(), CV_32FC1);        for (int j = 0; j <= descriptors.size() - 1; ++j)        {            featureVectorOfTestImage.at<float>(0, j) = descriptors[j];        }        //对测试图片进行分类并写入文件        int predictResult = svm->predict(featureVectorOfTestImage);        char line[512];        //printf("%s %d\r\n", testImagePath[i].c_str(), predictResult);        std::sprintf(line, "%s %d\n", testImagePath[i].c_str(), predictResult);        fileOfPredictResult << line;    }    fileOfPredictResult.close();}int main(){    //HOG+SVM识别方式1:直接输出类别    HOG_SVM1();    //HOG+SVM识别方式2:输出图片中的存在目标的矩形    //HOG_SVM2();}
原创粉丝点击