精华内容
下载资源
问答
  • github地址一、Haar特征 级联分类器 (CascadeClassifier) AdaBoost强分类器串接级联分类器是将若干个分类器进行连接,从而构成一种多项式级的强分类器。从弱分类器到强分类器的级联(AdaBoost 集成学习 改变训练...

    github地址

    一、Haar特征 级联分类器 (CascadeClassifier)

    AdaBoost强分类器串接
    级联分类器是将若干个分类器进行连接,从而构成一种多项式级的强分类器。
    从弱分类器到强分类器的级联(AdaBoost 集成学习  改变训练集)

    级联分类器使用前要先进行训练,怎么训练?


    用目标的特征值去训练,对于人脸来说,通常使用Haar特征进行训练。
    其他还有 LBP特征


    【1】提出积分图(Integral image) 加速特征提取。在该论文中作者使用的是Haar-like特征,
           然后使用积分图能够非常迅速的计算不同尺度上的Haar-like特征。
    【2】使用AdaBoost作为  特征选择的方法选择少量的特征在使用AdaBoost构造强分类器。
    【3】以级联的方式,从简单到 复杂 逐步 串联 强分类器,形成 级联分类器。

    级联分类器。该分类器由若干个简单的AdaBoost强分类器串接得来。
    假设AdaBoost分类器要实现99%的正确率,1%的误检率需要200维特征,
    而实现具有99.9%正确率和50%的误检率的AdaBoost分类器仅需要10维特征,
    那么通过级联,假设10级级联,最终得到的正确率和误检率分别为:
    (99.9%)^10 = 99%
    (0.5)^10   = 0.1

    检测体系:是以现实中很大一副图片作为输入,然后对图片中进行多区域,多尺度的检测,

    所谓多区域,是要对图片划分多块,对每个块进行检测,

    由于训练的时候一般图片都是20*20左右的小图片,

    所以对于大的人脸,还需要进行多尺度的检测。

    多尺度检测一般有两种策略:

        一种    是不改变搜索窗口的大小,而不断缩放图片,

            这种方法需要对每个缩放后的图片进行区域特征值的运算,效率不高,

      另一种方法 改变搜索窗口,

           是不断初始化搜索窗口size为训练时的图片大小,不断扩大搜索窗口进行搜索。

    在区域放大的过程中会出现同一个人脸被多次检测,这需要进行区域的合并。
    无论哪一种搜索方法,都会为输入图片输出大量的子窗口图像,
    这些子窗口图像经过筛选式级联分类器会不断地被每个节点筛选,抛弃或通过。


    级联分类器的策略是,将若干个强分类器由简单到复杂排列,
    希望经过训练使每个强分类器都有较高检测率,而误识率可以放低。

    AdaBoost训练出来的强分类器一般具有较小的误识率,但检测率并不很高,
    一般情况下,高检测率会导致高误识率,这是强分类阈值的划分导致的,
    要提高强分类器的检测率既要降低阈值,要降低强分类器的误识率就要提高阈值,
    这是个矛盾的事情。据参考论文的实验结果,
    增加分类器个数可以在提高强分类器检测率的同时降低误识率,

    所以级联分类器在训练时要考虑如下平衡,

    一是弱分类器的个数和计算时间的平衡,

    二是强分类器检测率和误识率之间的平衡。

    #include "opencv2/objdetect/objdetect.hpp"
    #include "opencv2/highgui/highgui.hpp"
    #include "opencv2/imgproc/imgproc.hpp"
    
    #include <iostream>
    #include <stdio.h>
    
    using namespace std;
    using namespace cv;
    
    //函数声明
    void detectAndDisplay( Mat frame );
    
    //全局变量
    string face_cascade_name = "../../common/data/cascade/haarcascades/haarcascade_frontalface_alt.xml";
    string eyes_cascade_name = "../../common/data/cascade/haarcascades/haarcascade_eye_tree_eyeglasses.xml";
    // 级联分类器 类
    CascadeClassifier face_cascade;
    CascadeClassifier eyes_cascade;
    
    string window_name = "Capture - Face detection";
    //RNG rng(12345);
    
    // @主函数
    int main( int argc, const char** argv )
    {
    	//CvCapture* capture;
    	VideoCapture capture;
    	Mat frame;
    
    //==========【1】 加载级联分类器文件模型==============
    	// 加载级联分类器
    	if( !face_cascade.load( face_cascade_name ) ){ printf("--(!)Error loading\n"); return -1; };
    	if( !eyes_cascade.load( eyes_cascade_name ) ){ printf("--(!)Error loading\n"); return -1; };
    
    //==========【2】打开内置摄像头视频流==============
    	capture.open( -1 );
            if ( ! capture.isOpened() ) { printf("--(!)Error opening video capture\n"); return -1; }
    	while( capture.read(frame) )
    	{
    //=========【3】对当前帧使用分类器进行检测============
    		if( !frame.empty() )
    		{ detectAndDisplay( frame ); }
    		else
    		{ printf(" --(!) No captured frame -- Break!"); break; }
    
    		int c = waitKey(10);
    		if( (char)c == 'c' ) { break; }
    	}
    	
    	return 0;
    }
    
    // @函数 detectAndDisplay 
    void detectAndDisplay( Mat frame )
    {
    	std::vector<Rect> faces;//检测到的人脸 矩形区域 左下点坐标 长和宽
    	Mat frame_gray;
    
    	cvtColor( frame, frame_gray, CV_BGR2GRAY );//转换成灰度图
    	equalizeHist( frame_gray, frame_gray );//直方图均衡画
    
    	//-- 多尺寸检测人脸
    	face_cascade.detectMultiScale( frame_gray, faces, 1.1, 2, 0|CV_HAAR_SCALE_IMAGE, Size(30, 30) );
    	// image:当然是输入图像了,要求是8位无符号图像,即灰度图
    	//objects:输出向量容器(保存检测到的物体矩阵)
    	//scaleFactor:每张图像缩小的尽寸比例 1.1 即每次搜索窗口扩大10%
    	//minNeighbors:每个候选矩阵应包含的像素领域
    	//flags:表示此参数模型是否更新标志位; 0|CV_HAAR_SCALE_IMAGE   0|CASCADE_FIND_BIGGEST_OBJECT  CASCADE_DO_ROUGH_SEARCH
    	//minSize :表示最小的目标检测尺寸;
    	//maxSize:表示最大的目标检测尺寸;
    	//
    	for( int i = 0; i < faces.size(); i++ )//画出椭圆区域
    	{//矩形中心点
    	   Point center( faces[i].x + faces[i].width*0.5, faces[i].y + faces[i].height*0.5 );
    	   ellipse( frame, center, Size( faces[i].width*0.5, faces[i].height*0.5), 0, 0, 360, Scalar( 255, 0, 255 ), 4, 8, 0 );
    
    	   Mat faceROI = frame_gray( faces[i] );//对应区域的像素图片
    	   std::vector<Rect> eyes;//眼睛
    
    	   //-- 在每张人脸上检测双眼
    	   eyes_cascade.detectMultiScale( faceROI, eyes, 1.1, 2, 
    					0 
    					//|CASCADE_FIND_BIGGEST_OBJECT
    					//|CASCADE_DO_ROUGH_SEARCH
    					|CV_HAAR_SCALE_IMAGE, Size(30, 30) );
    
    	   for( int j = 0; j < eyes.size(); j++ )
    	   {    // 中心点
    		Point center( faces[i].x + eyes[j].x + eyes[j].width*0.5,
    		              faces[i].y + eyes[j].y + eyes[j].height*0.5 );
    		int radius = cvRound( (eyes[j].width + eyes[i].height)*0.25 );
    		circle( frame, center, radius, Scalar( 255, 0, 0 ), 4, 8, 0 );// 画圆
    	}
    	}
    	//-- 显示结果图像 显示人脸椭圆区域  人眼睛
    	imshow( window_name, frame );
    }
    
    

    二、LBP特征级联分类器

     与Haar特征相比,LBP特征是整数特征,因此训练和检测过程都会比Haar特征快几倍。
    LBP和Haar特征用于检测的准确率,是依赖训练过程中的训练数据的质量和训练参数。
    训练一个与基于Haar特征同样准确度的LBP的分类器是可能的。

    级联分类器的策略是,将若干个( AdaBoost训练出来的强分类器 ) 由简单到复杂排列,

     希望经过训练使每个强分类器都有较高检测率,而误识率可以放低。

    #include "opencv2/objdetect.hpp"
    #include "opencv2/videoio.hpp"
    #include "opencv2/highgui.hpp"
    #include "opencv2/imgproc.hpp"
    
    #include <iostream>
    #include <stdio.h>
    
    using namespace std;
    using namespace cv;
    
    //函数声明
    void detectAndDisplay( Mat frame );
    
    //全局变量
    String face_cascade_name = "../../common/data/cascade/lbpcascades/lbpcascade_frontalface.xml";
    String eyes_cascade_name = "../../common/data/cascade/haarcascades/haarcascade_eye_tree_eyeglasses.xml";
    CascadeClassifier face_cascade;//这里可是试试 指针方式  会快一些
    // CascadeClassifier* face_cascade_prt;
    CascadeClassifier eyes_cascade;
    String window_name = "Capture - Face detection";
    
    // @主函数
    int main( void )
    {
    	VideoCapture capture;
    	Mat frame;
    
    //==========【1】 加载级联分类器文件模型==============
    	// 加载级联分类器
            // face_cascade_prt = new CascadeClassifier();//指针方式
            //  face_cascade_prt->load( face_cascade_name )   
    	if( !face_cascade.load( face_cascade_name ) ){ printf("--(!)Error loading face cascade\n"); return -1; };
    	if( !eyes_cascade.load( eyes_cascade_name ) ){ printf("--(!)Error loading eyes cascade\n"); return -1; };
    
    //==========【2】打开内置摄像头视频流==============
    	capture.open( -1 );
    	if ( ! capture.isOpened() ) { printf("--(!)Error opening video capture\n"); return -1; }
    
    	while ( capture.read(frame) )
    	{
    	if( frame.empty() )
    	{
    	    printf(" --(!) No captured frame -- Break!");
    	    break;
    	}
    
    //=========【3】对当前帧使用分类器进行检测============
    	detectAndDisplay( frame );
    
    	//-- bail out if escape was pressed
    	int c = waitKey(10);
    	if( (char)c == 27 ) { break; }
    	}
    	return 0;
    }
    
    // @函数 detectAndDisplay 
    void detectAndDisplay( Mat frame )
    {
    	std::vector<Rect> faces;//检测到的人脸 矩形区域 左下点坐标 长和宽
    	Mat frame_gray;
    
    	cvtColor( frame, frame_gray, COLOR_BGR2GRAY );//转换成灰度图
    	equalizeHist( frame_gray, frame_gray );//直方图均衡画
    
    	//-- 多尺寸检测人脸
    	face_cascade.detectMultiScale( frame_gray, faces, 1.1, 2, 0, Size(80, 80) );
    	// image:当然是输入图像了,要求是8位无符号图像,即灰度图
    	//objects:输出向量容器(保存检测到的物体矩阵)
    	//scaleFactor:每张图像缩小的尽寸比例 1.1 即每次搜索窗口扩大10%
    	//minNeighbors:每个候选矩阵应包含的像素领域
    	//flags:表示此参数模型是否更新标志位; 0|CV_HAAR_SCALE_IMAGE   0|CASCADE_FIND_BIGGEST_OBJECT  CASCADE_DO_ROUGH_SEARCH
    	//minSize :表示最小的目标检测尺寸;
    	//maxSize:表示最大的目标检测尺寸;
    	for( size_t i = 0; i < faces.size(); i++ )
    	{
    	Mat faceROI = frame_gray( faces[i] );
    	std::vector<Rect> eyes;
    
    	//-- In each face, detect eyes
    	eyes_cascade.detectMultiScale( faceROI, eyes, 1.1, 2, 0 |CASCADE_SCALE_IMAGE, Size(30, 30) );
    	if( eyes.size() == 2)
    	{
    	    // 画出椭圆区域
    	    Point center( faces[i].x + faces[i].width/2, faces[i].y + faces[i].height/2 );//矩形中心点
    	    ellipse( frame, center, Size( faces[i].width/2, faces[i].height/2 ), 0, 0, 360, Scalar( 255, 0, 0 ), 2, 8, 0 );
    	    //-- 在每张人脸上检测双眼
    	    for( size_t j = 0; j < eyes.size(); j++ )
    	    { //-- Draw the eyes // 中心点
    		Point eye_center( faces[i].x + eyes[j].x + eyes[j].width/2, faces[i].y + eyes[j].y + eyes[j].height/2 );
    		int radius = cvRound( (eyes[j].width + eyes[j].height)*0.25 );
    		circle( frame, eye_center, radius, Scalar( 255, 0, 255 ), 3, 8, 0 );// 画圆
    	    }
    	}
    
    	}
    //====显示结果图像 显示人脸椭圆区域  人眼睛=====
    	imshow( window_name, frame );
    }
    
    
    

    三、支持向量机(SVM) 分类器 Support Vector Machines

    支持向量机 (SVM) 是一个类分类器,正式的定义是一个能够将不同类样本
    在样本空间分隔的超平面(n-1 demension)。
     换句话说,给定一些标记(label)好的训练样本 (监督式学习),
    SVM算法输出一个最优化的分隔超平面。

    假设给定一些分属于两类的2维点,这些点可以通过直线分割, 我们要找到一条最优的分割线.

    |
    |
    |              +
    |            +    +
    |               +
    |  *               +
    |                        +
    |   *   *           +
    |   *                 +
    |  *      *             +
    |   *    *
    ——————————————————————————————————>

    w * 超平面 = 0  ,w 为超平面(分割面)的垂线段向量
    x+ 为正类    x- 为负类
    w * x+ + b >= 1   向量点乘  在河另一边的为 正类
    w * x- + b <= -1
     
    w * x+0 + b =  1        ,    y = +1  河边样本
    w * x-0 + b = -1         ,   y = -1

    河宽
    (x+0 - x-0) * w/||w|| =
     
    x+0 * w/||w||  - x-0 * w/||w|| =

    (1-b) /||w|| - (-1-b)/||w||  =

    2 * /||w||   maxmize
    =======>

    min 1/2 * ||w||^2

    这是一个拉格朗日优化问题

    L = 1/2 * ||w||^2 - SUM(a * yi *( w* xi +b) - 1)

    dL/dw  = ||w||  - sum(a * yi * xi)   令为  0    计算 w
     
    dL/db  =  SUM(a * yi)                令为  to 0     

    ====>
    ||w||  = sum(a * yi * xi)
    SUM(a * yi)  = 0
    =====>

    L =  -1/2  SUM(SUM(ai*aj * yi*yj * xi*xi))


    #include <opencv2/core.hpp>
    #include <opencv2/imgproc.hpp>
    #include "opencv2/imgcodecs.hpp"
    #include <opencv2/highgui.hpp>
    #include <opencv2/ml.hpp>
    using namespace cv;
    using namespace cv::ml;
    int main(int, char**)
    {
        // 图像大小
        int width = 512, height = 512;
        Mat image = Mat::zeros(height, width, CV_8UC3);
    
        // 建立训练样本 training data
        int labels[4] = {1, -1, -1, -1};// 由分属于两个类别的2维点组成, 其中一类包含一个样本点,另一类包含三个点。
        float trainingData[4][2] = { {501, 10}, {255, 10}, {501, 255}, {10, 501} };
        Mat trainingDataMat(4, 2, CV_32FC1, trainingData);
        Mat labelsMat(4, 1, CV_32SC1, labels);
    
        // Train the SVM
        Ptr<SVM> svm = SVM::create();
        svm->setType(SVM::C_SVC);// SVM类型.  C_SVC 该类型可以用于n-类分类问题 (n \geq 2)
        svm->setKernel(SVM::LINEAR);// SVM 核类型.  将训练样本映射到更有利于可线性分割的样本集
        // 算法终止条件.   最大迭代次数和容许误差
        svm->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER, 100, 1e-6));
        // 训练支持向量
        svm->train(trainingDataMat, ROW_SAMPLE, labelsMat);
    
        // SVM区域分割
        Vec3b green(0,255,0), blue (255,0,0);
        for (int i = 0; i < image.rows; ++i)
            for (int j = 0; j < image.cols; ++j)
            {
                Mat sampleMat = (Mat_<float>(1,2) << j,i);// generate data
                float response = svm->predict(sampleMat);// 分类类别
                if (response == 1)// 绿色表示标记为1的点,蓝色表示标记为-1的点。
                    image.at<Vec3b>(i,j)  = green;
                else if (response == -1)
                    image.at<Vec3b>(i,j)  = blue;
            }
    
        // Show the training data
        int thickness = -1;
        int lineType = 8;
        circle( image, Point(501,  10), 5, Scalar(  0,   0,   0), thickness, lineType );
        circle( image, Point(255,  10), 5, Scalar(255, 255, 255), thickness, lineType );
        circle( image, Point(501, 255), 5, Scalar(255, 255, 255), thickness, lineType );
        circle( image, Point( 10, 501), 5, Scalar(255, 255, 255), thickness, lineType );
    
        // 支持向量
        thickness = 2;
        lineType  = 8;
        Mat sv = svm->getUncompressedSupportVectors();
        for (int i = 0; i < sv.rows; ++i)
        {
            const float* v = sv.ptr<float>(i);
            circle( image,  Point( (int) v[0], (int) v[1]),   6,  Scalar(128, 128, 128), thickness, lineType);
        }
    
        imwrite("result.png", image);        // save the image
        imshow("SVM Simple Example", image); // show it to the user
        waitKey(0);
    }
    

    3.2 支持向量机对线性不可分数据的处理 考虑 错分类样本离同类区域的距离

    为什么需要将支持向量机优化问题扩展到线性不可分的情形?
    在多数计算机视觉运用中,我们需要的不仅仅是一个简单的SVM线性分类器,
    我们需要更加强大的工具来解决 训练数据无法用一个超平面分割 的情形。

    我们以人脸识别来做一个例子,训练数据包含一组人脸图像和一组非人脸图像(除了人脸之外的任何物体)。
    这些训练数据超级复杂,以至于为每个样本找到一个合适的表达 (特征向量) 以让它们能够线性分割是非常困难的。


    最优化问题的扩展

    还记得我们用支持向量机来找到一个最优超平面。 既然现在训练数据线性不可分,
    我们必须承认这个最优超平面会将一些样本划分到错误的类别中。 在这种情形下的优化问题,
    需要将 错分类(misclassification) 当作一个变量来考虑。
    新的模型需要包含原来线性可分情形下的最优化条件,即最大间隔(margin),
    以及在线性不可分时分类错误最小化。


    比如,我们可以最小化一个函数,该函数定义为在原来模型的基础上再加上一个常量乘以样本被错误分类的次数:
    L = 1/2 * ||w||^2 - SUM(a * yi *( w* xi +b) - 1)  +  c * ( 样本被错误分类的次数)

    它没有考虑错分类的样本距离同类样本所属区域的大小。 因此一个更好的方法是考虑 错分类样本离同类区域的距离:

    L = 1/2 * ||w||^2 - SUM(a * yi *( w* xi +b) - 1)  +  c * ( 错分类样本离同类区域的距离)


       C比较大时分类错误率较小,但是间隔也较小。 在这种情形下,
    错分类对模型函数产生较大的影响,既然优化的目的是为了最小化这个模型函数,
    那么错分类的情形必然会受到抑制。

       C比较小时间隔较大,但是分类错误率也较大。 在这种情形下,
    模型函数中错分类之和这一项对优化过程的影响变小,
    优化过程将更加关注于寻找到一个能产生较大间隔的超平面。


    #include <iostream>
    #include <opencv2/core.hpp>
    #include <opencv2/imgproc.hpp>
    #include "opencv2/imgcodecs.hpp"
    #include <opencv2/highgui.hpp>
    #include <opencv2/ml.hpp>
    
    #define NTRAINING_SAMPLES   100 // 每类训练样本数量  
    
    #define FRAC_LINEAR_SEP     0.9f// Fraction of samples which compose the linear separable part
    
    using namespace cv;
    using namespace cv::ml;
    using namespace std;
    
    static void help()
    {
        cout<< "\n--------------------------------------------------------------------------" << endl
            << "This program shows Support Vector Machines for Non-Linearly Separable Data. " << endl
            << "Usage:"                                                               << endl
            << "./non_linear_svms" << endl
            << "--------------------------------------------------------------------------"   << endl
            << endl;
    }
    int main()
    {
        help();
    
        // 图像大小
        const int WIDTH = 512, HEIGHT = 512;
        Mat I = Mat::zeros(HEIGHT, WIDTH, CV_8UC3);
    
    //======【1】建立训练样本 =======================================
        Mat trainData(2*NTRAINING_SAMPLES, 2, CV_32FC1);//训练数据
        Mat labels   (2*NTRAINING_SAMPLES, 1, CV_32SC1);//标签
        RNG rng(100); //随机数
        // 线性规律 数据量
        int nLinearSamples = (int) (FRAC_LINEAR_SEP * NTRAINING_SAMPLES);
    
        //=== 线性规律 数据 class 1=========
        Mat trainClass = trainData.rowRange(0, nLinearSamples);
        // The x coordinate of the points is in [0, 0.4)
        Mat c = trainClass.colRange(0, 1);
        rng.fill(c, RNG::UNIFORM, Scalar(1), Scalar(0.4 * WIDTH));
        // The y coordinate of the points is in [0, 1)
        c = trainClass.colRange(1,2);
        rng.fill(c, RNG::UNIFORM, Scalar(1), Scalar(HEIGHT));
    
    //===== 非 线性规律 数据  class 2==========
        trainClass = trainData.rowRange(2*NTRAINING_SAMPLES-nLinearSamples, 2*NTRAINING_SAMPLES);
        // The x coordinate of the points is in [0.6, 1]
        c = trainClass.colRange(0 , 1);
        rng.fill(c, RNG::UNIFORM, Scalar(0.6*WIDTH), Scalar(WIDTH));
        // The y coordinate of the points is in [0, 1)
        c = trainClass.colRange(1,2);
        rng.fill(c, RNG::UNIFORM, Scalar(1), Scalar(HEIGHT));
    
    //=====非 线性规律 数据 ===========
        // Generate random points for the classes 1 and 2
        trainClass = trainData.rowRange(  nLinearSamples, 2*NTRAINING_SAMPLES-nLinearSamples);
        // The x coordinate of the points is in [0.4, 0.6)
        c = trainClass.colRange(0,1);
        rng.fill(c, RNG::UNIFORM, Scalar(0.4*WIDTH), Scalar(0.6*WIDTH));
        // The y coordinate of the points is in [0, 1)
        c = trainClass.colRange(1,2);
        rng.fill(c, RNG::UNIFORM, Scalar(1), Scalar(HEIGHT));
    
    //=========Set up the labels for the classes ===============
        labels.rowRange(                0,   NTRAINING_SAMPLES).setTo(1);  // Class 1
        labels.rowRange(NTRAINING_SAMPLES, 2*NTRAINING_SAMPLES).setTo(2);  // Class 2
    
    
    //============= 2. Set up the support vector machines parameters ===============
        //=======3. Train the svm =======
        cout << "Starting training process" << endl;
        Ptr<SVM> svm = SVM::create();
        svm->setType(SVM::C_SVC);// SVM类型.  C_SVC 该类型可以用于n-类分类问题 (n \geq 2)
        svm->setC(0.1);//  错分类样本离同类区域的距离 的权重
        svm->setKernel(SVM::LINEAR);// SVM 核类型.  将训练样本映射到更有利于可线性分割的样本集
        // 算法终止条件.   最大迭代次数和容许误差
        svm->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER, (int)1e7, 1e-6));
        svm->train(trainData, ROW_SAMPLE, labels);
        cout << "Finished training process" << endl;
    
    //================ 4. Show the decision regions ==================
        Vec3b green(0,100,0), blue (100,0,0);
        for (int i = 0; i < I.rows; ++i)
            for (int j = 0; j < I.cols; ++j)
            {
                Mat sampleMat = (Mat_<float>(1,2) << i, j);
                float response = svm->predict(sampleMat);
                if      (response == 1)    I.at<Vec3b>(j, i)  = green;
                else if (response == 2)    I.at<Vec3b>(j, i)  = blue;
            }
    //================5. Show the training data ====================
        int thick = -1;
        int lineType = 8;
        float px, py;
     // Class 1===============
        for (int i = 0; i < NTRAINING_SAMPLES; ++i)
        {
            px = trainData.at<float>(i,0);
            py = trainData.at<float>(i,1);
            circle(I, Point( (int) px,  (int) py ), 3, Scalar(0, 255, 0), thick, lineType);
        }
      // Class 2======================
        for (int i = NTRAINING_SAMPLES; i <2*NTRAINING_SAMPLES; ++i)
        {
            px = trainData.at<float>(i,0);
            py = trainData.at<float>(i,1);
            circle(I, Point( (int) px, (int) py ), 3, Scalar(255, 0, 0), thick, lineType);
        }
    
    //============== 6. Show support vectors =====================
        thick = 2;
        lineType  = 8;
        Mat sv = svm->getSupportVectors();
        for (int i = 0; i < sv.rows; ++i)
        {
            const float* v = sv.ptr<float>(i);
            circle( I,  Point( (int) v[0], (int) v[1]), 6, Scalar(128, 128, 128), thick, lineType);
        }
        imwrite("result.png", I);                      // save the Image
        imshow("SVM for Non-Linear Training Data", I); // show it to the user
        waitKey(0);
    }
    
    


    四、主成分分析  数据降维   协方差矩阵 的特征值和特征向量

    PCA(Principal Component Analysis)主成分分析
    主要用于数据降维

    样本  协方差矩阵 的特征值和特征向量  取前四个特征值所对应的特征向量
    特征矩阵 投影矩阵


    对于一组样本的feature组成的多维向量,多维向量里的某些元素本身没有区分
    我们的目的是找那些变化大的元素,即方差大的那些维,
    而去除掉那些变化不大的维,从而使feature留下的都是最能代表此元素的“精品”,
    而且计算量也变小了。

    对于一个k维的feature来说,相当于它的每一维feature与
    其他维都是正交的(相当于在多维坐标系中,坐标轴都是垂直的),
    那么我们可以变化这些维的坐标系,从而使这个feature在某些维上方差大,
    而在某些维上方差很


    求得一个k维特征的投影矩阵,这个投影矩阵可以将feature从高维降到低维。
    投影矩阵也可以叫做变换矩阵。新的低维特征必须每个维都正交,特征向量都是正交的

    通过求样本矩阵的协方差矩阵,然后求出协方差矩阵的特征向量,这些特征向量就可以构成这个投影矩阵了。
    特征向量的选择取决于协方差矩阵的特征值的大

    对于一个训练集,100个样本,feature是10维,那么它可以建立一个100*10的矩阵,作为样本。
    求这个样本的协方差矩阵,得到一个10*10的协方差矩阵,然后求出这个协方差矩阵的特征值和特征向量,
    应该有10个特征值和特征向量,我们根据特征值的大小,取前四个特征值所对应的特征向量,
    构成一个10*4的矩阵,这个矩阵就是我们要求的特征矩阵,100*10的样本矩阵乘以这个10*4的特征矩阵,
    就得到了一个100*4的新的降维之后的样本矩阵,每个样本的维数下


    #include "opencv2/core.hpp"
    #include "opencv2/imgproc.hpp"
    #include "opencv2/highgui.hpp"
    #include <iostream>
    
    using namespace std;
    using namespace cv;
    
    // Function declarations
    void drawAxis(Mat&, Point, Point, Scalar, const float);
    double getOrientation(const vector<Point> &, Mat&);
    
    /**
     * @function drawAxis
     */
    void drawAxis(Mat& img, Point p, Point q, Scalar colour, const float scale = 0.2)
    {
    //! [visualization1]
        double angle;
        double hypotenuse;
        angle = atan2( (double) p.y - q.y, (double) p.x - q.x ); // angle in radians
        hypotenuse = sqrt( (double) (p.y - q.y) * (p.y - q.y) + (p.x - q.x) * (p.x - q.x));
    //    double degrees = angle * 180 / CV_PI; // convert radians to degrees (0-180 range)
    //    cout << "Degrees: " << abs(degrees - 180) << endl; // angle in 0-360 degrees range
    
        // Here we lengthen the arrow by a factor of scale
        q.x = (int) (p.x - scale * hypotenuse * cos(angle));
        q.y = (int) (p.y - scale * hypotenuse * sin(angle));
        line(img, p, q, colour, 1, LINE_AA);
    
        // create the arrow hooks
        p.x = (int) (q.x + 9 * cos(angle + CV_PI / 4));
        p.y = (int) (q.y + 9 * sin(angle + CV_PI / 4));
        line(img, p, q, colour, 1, LINE_AA);
    
        p.x = (int) (q.x + 9 * cos(angle - CV_PI / 4));
        p.y = (int) (q.y + 9 * sin(angle - CV_PI / 4));
        line(img, p, q, colour, 1, LINE_AA);
    //! [visualization1]
    }
    
    /**
     * @function getOrientation
     */
    double getOrientation(const vector<Point> &pts, Mat &img)
    {
    //! [pca]
        //Construct a buffer used by the pca analysis
        int sz = static_cast<int>(pts.size());
        Mat data_pts = Mat(sz, 2, CV_64FC1);
        for (int i = 0; i < data_pts.rows; ++i)
        {
            data_pts.at<double>(i, 0) = pts[i].x;
            data_pts.at<double>(i, 1) = pts[i].y;
        }
    
        //Perform PCA analysis
        PCA pca_analysis(data_pts, Mat(), PCA::DATA_AS_ROW);
    
        //Store the center of the object
        Point cntr = Point(static_cast<int>(pca_analysis.mean.at<double>(0, 0)),
                          static_cast<int>(pca_analysis.mean.at<double>(0, 1)));
    
        //Store the eigenvalues and eigenvectors
        vector<Point2d> eigen_vecs(2);
        vector<double> eigen_val(2);
        for (int i = 0; i < 2; ++i)
        {
            eigen_vecs[i] = Point2d(pca_analysis.eigenvectors.at<double>(i, 0),
                                    pca_analysis.eigenvectors.at<double>(i, 1));
    
            eigen_val[i] = pca_analysis.eigenvalues.at<double>(i);
        }
    
    //! [pca]
    //! [visualization]
        // Draw the principal components
        circle(img, cntr, 3, Scalar(255, 0, 255), 2);
        Point p1 = cntr + 0.02 * Point(static_cast<int>(eigen_vecs[0].x * eigen_val[0]), static_cast<int>(eigen_vecs[0].y * eigen_val[0]));
        Point p2 = cntr - 0.02 * Point(static_cast<int>(eigen_vecs[1].x * eigen_val[1]), static_cast<int>(eigen_vecs[1].y * eigen_val[1]));
        drawAxis(img, cntr, p1, Scalar(0, 255, 0), 1);
        drawAxis(img, cntr, p2, Scalar(255, 255, 0), 5);
    
        double angle = atan2(eigen_vecs[0].y, eigen_vecs[0].x); // orientation in radians
    //! [visualization]
    
        return angle;
    }
    
    /**
     * @function main
     */
    int main(int argc, char** argv)
    {
    //! [pre-process]
        // Load image
        CommandLineParser parser(argc, argv, "{@input | ../../common/data/pca_test1.jpg | input image}");
        parser.about( "This program demonstrates how to use OpenCV PCA to extract the orienation of an object.\n" );
        parser.printMessage();
    
        Mat src = imread(parser.get<String>("@input"));
    
        // Check if image is loaded successfully
        if(src.empty())
        {
            cout << "Problem loading image!!!" << endl;
            return EXIT_FAILURE;
        }
    
        imshow("src", src);
    
        // Convert image to grayscale
        Mat gray;
        cvtColor(src, gray, COLOR_BGR2GRAY);
    
        // Convert image to binary
        Mat bw;
        threshold(gray, bw, 50, 255, THRESH_BINARY | THRESH_OTSU);
    //! [pre-process]
    
    //! [contours]
        // Find all the contours in the thresholded image
        vector<vector<Point> > contours;
        findContours(bw, contours, RETR_LIST, CHAIN_APPROX_NONE);
    
        for (size_t i = 0; i < contours.size(); ++i)
        {
            // Calculate the area of each contour
            double area = contourArea(contours[i]);
            // Ignore contours that are too small or too large
            if (area < 1e2 || 1e5 < area) continue;
    
            // Draw each contour only for visualisation purposes
            drawContours(src, contours, static_cast<int>(i), Scalar(0, 0, 255), 2, LINE_8);
            // Find the orientation of each shape
            getOrientation(contours[i], src);
        }
    //! [contours]
    
        imshow("output", src);
    
        waitKey(0);
        return 0;
    }

    五、人工神经网络(ANN) 简称神经网络(NN)

    人工神经网络(ANN) 简称神经网络(NN),
    能模拟生物神经系统对物体所作出的交互反应,
    是由具有适应性的简单单元(称为神经元)组成的广泛并行互连网络。
     神经元
     y = w  * x + b   线性变换   (旋转 平移 伸缩 升/降维)
     z = a(y)         非线性变换 激活函数

    常用 Sigmoid 函数作激活函数

    y = sigmod(x)  = 1/(1+exp(-x))  映射到0 ~ 1之间
     
     OpenCV 中使用的激活函数是另一种形式,

    f(x) = b *  (1 - exp(-c*x)) / (1 + exp(-c*x))

    当 α = β = 1 时
    f(x) =(1 - exp(-x)) / (1 + exp(-x))
    该函数把可能在较大范围内变化的输入值,“挤压” 到 (-1, 1) 的输出范围内

    // 设置激活函数,目前只支持 ANN_MLP::SIGMOID_SYM
    virtual void cv::ml::ANN_MLP::setActivationFunction(int type, double param1 = 0, double param2 = 0);

    神经网络
    2.1  感知机 (perceptron)
      感知机由两层神经元组成,输入层接收外界输入信号,而输出层则是一个 M-P 神经元。
      实际上,感知机可视为一个最简单的“神经网络”,用它可很容易的实现逻辑与、或、非等简单运算。

    2.2 层级结构
      常见的神经网络,可分为三层:输入层、隐含层、输出层。
      输入层接收外界输入,隐层和输出层负责对信号进行加工,输出层输出最终的结果。

    2.3  层数设置
          OpenCV 中,设置神经网络层数和神经元个数的函数为 setLayerSizes(InputArray _layer_sizes),
        // (a) 3层,输入层神经元个数为 4,隐层的为 6,输出层的为 4
        Mat layers_size = (Mat_<int>(1,3) << 4,6,4);

        // (b) 4层,输入层神经元个数为 4,第一个隐层的为 6,第二个隐层的为 5,输出层的为 4
        Mat layers_size = (Mat_<int>(1,4) << 4,6,5,4);

    1)  创建
        static Ptr<ANN_MLP> cv::ml::ANN_MLP::create();  // 创建空模型

    2) 设置参数

    // 设置神经网络的层数和神经元数量
    virtual void cv::ml::ANN_MLP::setLayerSizes(InputArray _layer_sizes);

    // 设置激活函数,目前只支持 ANN_MLP::SIGMOID_SYM
    virtual void cv::ml::ANN_MLP::setActivationFunction(int type, double param1 = 0, double param2 = 0);

    // 设置训练方法,默认为 ANN_MLP::RPROP,较常用的是 ANN_MLP::BACKPROP
    // 若设为 ANN_MLP::BACKPROP,则 param1 对应 setBackpropWeightScale()中的参数,
    // param2 对应 setBackpropMomentumScale() 中的参数
    virtual void cv::ml::ANN_MLP::setTrainMethod(int method, double param1 = 0, double param2 = 0);
    virtual void cv::ml::ANN_MLP::setBackpropWeightScale(double val); // 默认值为 0.1
    virtual void cv::ml::ANN_MLP::setBackpropMomentumScale(double val); // 默认值为 0.1
     
    // 设置迭代终止准则,默认为 TermCriteria(TermCriteria::MAX_ITER + TermCriteria::EPS, 1000, 0.01)
    virtual void cv::ml::ANN_MLP::setTermCriteria(TermCriteria val);

    3)  训练

    // samples - 训练样本; layout - 训练样本为 “行样本” ROW_SAMPLE 或 “列样本” COL_SAMPLE; response - 对应样本数据的分类结果
    virtual bool cv::ml::StatModel::train(InputArray samples,int layout,InputArray responses);  

    4)  预测

    // samples,输入的样本书数据;results,输出矩阵,默认不输出;flags,标识,默认为 0
    virtual float cv::ml::StatModel::predict(InputArray samples, OutputArray results=noArray(),int flags=0) const;      

    5) 保存训练好的神经网络参数
        bool trained = ann->train(tData);  
        if (trained) {
              ann->save("ann_param");
         }

    6) 载入训练好的神经网络
     方法1 Ptr<ml::ANN_MLP> ann = ml::ANN_MLP::load("ann_param");

    neuralNetwork = Algorithm::load<ANN_MLP>(fileName);

    #include<opencv2/opencv.hpp>  
    #include <iostream>    
    #include <string>    
      
    using namespace std;  
    using namespace cv;  
    using namespace ml;  
    int main()  
    {  
        // 标签
        float labels[10][2]={ { 0.9,0.1 },
    			  { 0.1,0.9 },
    			  { 0.9,0.1 },
    			  { 0.1,0.9 },
    			  { 0.9,0.1 },
    			  { 0.9,0.1 },
    			  { 0.1,0.9 },
    			  { 0.1,0.9 },
    			  { 0.9,0.1 },
    			  { 0.9,0.1 } };  
        //这里对于样本标记为0.1和0.9而非0和1,主要是考虑到sigmoid函数的输出为一般为0和1之间的数,
        // 只有在输入趋近于-∞和+∞才逐渐趋近于0和1,而不可能达到。  
        Mat labelsMat(10, 2, CV_32FC1, labels);  
        // 训练集
        float trainingData[10][2] = { { 11,12   },
    				  { 111,112 },
    				  { 21,22   },
    			 	  { 211,212 },
    				  { 51,32   },
    				  { 71,42   },
    				  { 441,412 },
    				  { 311,312 },
    				  { 41,62   },
    				  { 81,52   } };  
        Mat trainingDataMat(10, 2, CV_32FC1, trainingData);  
    
        // BP 神经网络 模型  创建和参数设置
        Ptr<ANN_MLP> ann = ANN_MLP::create();
        //5层:输入层,3层隐藏层和输出层,每层均为两个神经元perceptron    
        Mat layerSizes = (Mat_<int>(1, 5) << 2, 2, 2, 2, 2); 
        ann->setLayerSizes(layerSizes);//  
        // 设置激活函数,目前只支持 ANN_MLP::SIGMOID_SYM 
        ann->setActivationFunction(ANN_MLP::SIGMOID_SYM,1.0,1.0);//
        // 设置迭代结束条件 
        //  ann->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER + TermCriteria::EPS, 300, FLT_EPSILON));  
        // 设置训练方法   反向传播  动量 下山发
        ann->setTrainMethod(ANN_MLP::BACKPROP,0.1,0.9);  
        // 训练数据
        Ptr<TrainData> tData = TrainData::create(trainingDataMat, ROW_SAMPLE, labelsMat);
        // 执行训练  
        bool trained = ann->train(tData);  
        // 保存训练好的神经网络参数
        if (trained) {
              ann->save("ann_param");
         }
    
        // 载入训练好的神经网络
        //    Ptr<ml::ANN_MLP> ann = ml::ANN_MLP::load("ann_param");
    
        //  512 x 512 零矩阵   
        int width = 512, height = 512;  
        Mat image = Mat::zeros(height, width, CV_8UC3);  
    
        Vec3b green(0, 255, 0), blue(255, 0, 0);  
        for (int i = 0; i < image.rows; ++i)  
        {  
            for (int j = 0; j < image.cols; ++j)  
            {  
                Mat sampleMat = (Mat_<float>(1, 2) << i, j);//生成数
                Mat responseMat;//预测结果  我前面定义的 输出为两维
                ann->predict(sampleMat, responseMat);  
                float* p = responseMat.ptr<float>(0);  
                if (p[0] > p[1])  
                {  
                    image.at<Vec3b>(j, i) = green;// 正样本 绿色
                }  
                else  
                {  
                    image.at<Vec3b>(j, i) = blue; // 负样本 蓝色
                }  
            }  
        }  
        // 画出训练样本数据  
        int thickness = -1;  
        int lineType = 8;
        int r = 8;  //半径
        circle(image, Point(111, 112), r, Scalar(0, 0, 0), thickness, lineType);  
        circle(image, Point(211, 212), r, Scalar(0, 0, 0), thickness, lineType);  
        circle(image, Point(441, 412), r, Scalar(0, 0, 0), thickness, lineType);  
        circle(image, Point(311, 312), r, Scalar(0, 0, 0), thickness, lineType);  
        circle(image, Point(11, 12), r, Scalar(255, 255, 255), thickness, lineType);  
        circle(image, Point(21, 22), r, Scalar(255, 255, 255), thickness, lineType);  
        circle(image, Point(51, 32), r, Scalar(255, 255, 255), thickness, lineType);  
        circle(image, Point(71, 42), r, Scalar(255, 255, 255), thickness, lineType);  
        circle(image, Point(41, 62), r, Scalar(255, 255, 255), thickness, lineType);  
        circle(image, Point(81, 52), r, Scalar(255, 255, 255), thickness, lineType);  
      
        imwrite("result.png", image);       //  保存训练的结果  
      
        imshow("BP Simple Example", image); //    
        waitKey(0);  
      
        return 0;  
    }  

    六、利用级联分类器检测人脸  人工神经网络来区别是否为笑脸

    使用 级联分类器 检测出人脸
    缩放到 统一大小  放入
    人工神经网络内 训练
    笑脸 / 非笑脸
    保存分类器

    OpenCV 中使用的激活函数是另一种形式,
    f(x) = b *  (1 - exp(-c*x)) / (1 + exp(-c*x))
    当 α = β = 1 时

    f(x) =(1 - exp(-x)) / (1 + exp(-x))

    #include "network.h"
    
    CascadeClassifier* faceCascade;  //人脸检测 级联分类器 指针
    Ptr<ANN_MLP> neuralNetwork;      //人工神经网络
    
    //初始化一个用于训练的神经网络
    //参数:无
    //返回:0 - 正常,-1 - 异常
    int networkInit(void)
    {
    	/*加载人脸检测分类器*/
    	faceCascade = new CascadeClassifier();
    	if(!faceCascade->load("../../common/data/cascade/haarcascades/haarcascade_frontalface_alt.xml"))
    	{
    		printf("Can't load face cascade.\n");
    		return -1;
    	}
    
    	/*设置神经网络参数*/
        neuralNetwork = ANN_MLP::create();// 创建
        Mat layerSize = (Mat_<int>(1,3)<<2500,50,2); //网络层数及神经元数  
    // 50*50 pix像素的图片 展开成一列 2500 像素输入 50个隐藏神经元 2个输出神经元
        neuralNetwork->setLayerSizes(layerSize);// 设置网络尺寸
        neuralNetwork->setActivationFunction(ANN_MLP::SIGMOID_SYM,1.0,1.0);//激活函数 sigmod函数 
        neuralNetwork->setTrainMethod(ANN_MLP::BACKPROP,0.0001,0.001); //训练方法  反向传播 动量 下山发
        neuralNetwork->setTermCriteria(TermCriteria(TermCriteria::COUNT+TermCriteria::EPS,30000,0.0001)); //迭代终止条件
    
    	return 0;
    }
    
    //初始化一个用于分类的神经网络
    //参数:训练好的 神经网络数据文件名
    //返回:0 - 正常,-1 - 异常
    int networkInit(const char *fileName)
    {
       // 加载人脸检测 分类器
        faceCascade = new CascadeClassifier();
    // ../../common/data/cascade/lbpcascades/lbpcascade_frontalface.xml  // lbp 特征
        if(!faceCascade->load("../../common/data/cascade/haarcascades/haarcascade_frontalface_alt.xml"))
        {
            printf("Can't load face cascade.\n");
            return -1;
        }
    	/*从指定文件中加载神经网络数据*/
        printf("Loading network form %s\n",fileName);
        neuralNetwork = Algorithm::load<ANN_MLP>(fileName);
    // Ptr<ml::ANN_MLP> ann = ml::ANN_MLP::load("ann_param");
        return 0;
    }
    
    //训练神经网络并保存到指定的文件
    //参数:正样本容器,负样本容器,保存神经网络数据的文件名
    //返回:0 - 正常,-1 - 异常
    int networkTrain(std::vector<Mat>* posFaceVector,std::vector<Mat>* negFaceVector, const char *fileName)
    {
    //初始化训练数据矩阵和类标矩阵====================
    	//训练数据矩阵每行为一个样本,行数=正样本数+负样本数,列数=样本维数=样本宽度x样本高度=输入层神经元个数
    	//类标矩阵每行为一个样本的类标,行数同上,列数=2=输出层神经元个数,(1,0)代表正样本,(0,1)代表负样本
        Mat trainDataMat(posFaceVector->size() + negFaceVector->size(), 2500, CV_32FC1);// 数据量 × 2500维度
        Mat labelMat = Mat::zeros(posFaceVector->size() + negFaceVector->size(),2,CV_32FC1);//标签 数据量 × 2维度
    
    // 生成训练数据矩阵和类标矩阵================
        for(unsigned int i = 0;i < posFaceVector->size();i++)
        {
          // 进来的图像为矩阵 reshap 到 一列 在复制到 trainDataMat的一行 并且 归一化到 0~1 之间  防止计算溢出
            posFaceVector->at(i).reshape(0,1).convertTo(trainDataMat.row(i),CV_32FC1,0.003921569,0);  //0.003921569=1/255
            labelMat.at<float>(i,0) = 1;//类标签 样本
        }
    
        for(unsigned int i = 0;i < negFaceVector->size();i++)
        {
            negFaceVector->at(i).reshape(0,1).convertTo(trainDataMat.row(i + posFaceVector->size()),CV_32FC1,0.003921569,0);
            labelMat.at<float>(i + posFaceVector->size(), 1) = 1;
        }
    
    // 训练网络=================
        neuralNetwork->train(trainDataMat,ROW_SAMPLE,labelMat);
    
    // 保存网络数据=================
        neuralNetwork->save(fileName);
    
        return 0;
    }

    #include <stdio.h>
    #include <vector>
    #include <unistd.h>
    #include <string.h>
    #include <dirent.h>
    #include <sys/time.h>
    
    #include <opencv2/opencv.hpp>
    
    #include "network.h"
    
    using namespace std;
    using namespace cv;
    
    void printHelpMessage(void);      //打印帮助信息
    int  preview(void);               //预览摄像头图像
    int  trainNetwork(void);          //训练神经网络
    int  smileFaceDetect(void);       //在摄像头图像中执行笑脸检测
    
    char devPath[256] = "/dev/video0";                       //摄像头设备文件名
    char posPath[256] = "../trainset/positive/";             //正训练集路径
    char negPath[256] = "../trainset/negtive/";              //负训练集路径
    char networkFilename[256] = "../networks/default.xml";   //保存神经网络数据的文件名
    
    bool preview_flag = false;  //预览标志
    bool train_flag = false;    //训练标志
    bool detect_flag = false;   //笑脸检测标志
    
    std::vector<Mat> posFaces;  //存放正样本的 Mat 容器
    std::vector<Mat> negFaces;  //存放负样本的 Mat 容器
    
    //主函数
    int main(int argc, char *argv[])
    {
    	/*命令行参数解析*/
    	int opt;
    
    	if(argc == 1) //没有参数
    		printHelpMessage();
    
        while((opt = getopt(argc,argv,"hpted:T:F:N:")) != -1)//获取下一个命令行参数
    	{
    		switch(opt)
    		{
    			case 'h':printHelpMessage();    break; //打印帮助信息
    			case 'd':strcpy(devPath,optarg);break; //指定摄像头设备
    			case 'p':preview_flag = true;   break; //预览摄像头图像
    			case 't':train_flag = true;     break; //训练神经网络
                            case 'e':detect_flag = true;    break; //从摄像头捕获视频中检测笑脸
    			case 'T':strcpy(posPath,optarg);break; //指定正训练集路径
    			case 'F':strcpy(negPath,optarg);break; //指定负训练集路径
                   case 'N':strcpy(networkFilename,optarg); break; //指定神经网络文件名
    			default:printf("Invalid argument %c!\n",opt);return -1;//非法参数
    		}
    	}
    
    	// 如果路径字符串末位无'\',则添加之
    	if(posPath[strlen(posPath)-1] != '/')
    		strcat(posPath,"/");
    	if(negPath[strlen(negPath)-1] != '/')
    		strcat(negPath,"/");
    
    	/*根据标志决定程序的功能*/
    	if(train_flag == true)
    		trainNetwork();//训练神经网络
    	else if(preview_flag == true)
    		preview();     //预览摄像头图像
        else if(detect_flag == true)
            smileFaceDetect();     //从摄像头捕获视频中检测笑脸
    
        usleep(300000);
        return 0;
    }
    
    //将摄像头设备名转换为设备索引
    //参数:设备名字符串 /dev/video0 ~ /dev/video100
    //返回:设备索引,范围0-9999
    int getDeviceIndex(const char *devicePath)
    {
    	// 检查输入字符串是否为Linux下的V4L2设备名
    	if(strncmp(devicePath,"/dev/video",10))
    	{
    		printf("Invalid device path!\n");
    		return -1;
    	}
    
    	char devIndex[5];// 保存设备号
    	unsigned int i;
    
    	/*截取"/dev/video"之后的子串*/
    	for(i = 10;i < strlen(devicePath);i++)
    		devIndex[i-10] = devicePath[i];
    	devIndex[i-10] = '\0';
    	/*将字符串转换为整形数并返回*/
    	return atoi(devIndex);
    }
    
    //训练一个神经网络,并将网络数据保存到文件
    //参数:无
    //返回:0 - 正常,-1 - 异常
    int trainNetwork(void)
    {
    	networkInit();           //初始化网络
    	posFaces.reserve(1000);  //为容器预留1000个样本的内存
    	negFaces.reserve(1000);
    
    // 遍历正样本
    	DIR *posDir = opendir(posPath);//打开 正训练集路径
    	if(posDir == NULL)
    	{
    		printf("Can't open directory %s\n",posPath);
    		perror(posPath);
    		return -1;
    	}
    	struct dirent *posDirent;
    	while((posDirent = readdir(posDir)) != NULL)//读取下一个文件入口
    	{
    		/*跳过两个特殊文件*/
    		if(strcmp(posDirent->d_name,".") == 0 || strcmp(posDirent->d_name,"..") == 0)
    			continue;
    		/*获得当前文件的完整文件名*/
    		char posFileName[256];
    		strcpy(posFileName, posPath);
    		strcat(posFileName, posDirent->d_name);
    
    		//处理当前文件 
    		Mat temp;
    		int result;
    		result = detectFaceFormImg(posFileName, &temp);  //对当前文件做人脸检测
    		if(result == 1)
    		posFaces.push_back(temp);                        //若检测成功,则将人脸添加到正样本容器中
    	}
            printf("Got %d positive samples.\n",(int)posFaces.size());
    
    // 遍历负样本 
    	DIR *negDir = opendir(negPath);//打开负训练集路径
    	if(negDir == NULL)
    	{
    		printf("Can't open directory %s\n",negPath);
    		perror(negPath);
    		return -1;
    	}
    	struct dirent *negDirent;
    	while((negDirent = readdir(negDir)) != NULL)     //读取下一个文件入口
    	{
    		/*跳过两个特殊文件*/
    		if(strcmp(negDirent->d_name,".") == 0 || strcmp(negDirent->d_name,"..") == 0)
    			continue;
    
    		/*获得当前文件的完整文件名*/
    		char negFileName[256];
    		strcpy(negFileName,negPath);
    		strcat(negFileName,negDirent->d_name);
    
    		/*处理当前文件*/
    		Mat temp;
    		int result;
    		result = detectFaceFormImg(negFileName,&temp);//对当前文件做人脸检测
    		if(result == 1)
    		negFaces.push_back(temp);                     //若检测成功,则将人脸添加到负样本容器
    	}
            printf("Got %d negtive samples.\n",(int)negFaces.size());
    
    	struct timeval before, after;//时间
    
    	/*训练神经网络,并保存到文件*/
    	printf("begin network training...\n");
    	gettimeofday(&before,NULL);
    	networkTrain(&posFaces, &negFaces, networkFilename);
    	gettimeofday(&after,NULL);
    	long int usec = (after.tv_sec - before.tv_sec)*1000000 + (after.tv_usec-before.tv_usec);
    	printf("training completed,use %lds %ldms\n",usec/1000000,(usec%1000000)/1000);
    	return 0;
    }
    
    //在摄像头捕获视频中检测笑脸
    //参数:无
    //返回:0 - 正常,-1 - 异常
    int smileFaceDetect(void)
    {
    	networkInit(networkFilename);//初始化检测神经网络
    	VideoCapture capture;
    	capture.open(getDeviceIndex(devPath));//打开摄像头捕获
    	if(!capture.isOpened())
    	{
    	printf("Can not open device!\n");
    	return -1;
    	}
            //设置捕获图像分辨率  320*240
    	capture.set(CAP_PROP_FRAME_WIDTH, 640);     
    	capture.set(CAP_PROP_FRAME_HEIGHT,480);
    
    	Mat frame;
    	while(waitKey(10) != 'q') //循环直到按下键盘上的Q键
    	{
    		capture>>frame;                 //捕获一帧图像
    		Rect  face_dec;
    		if(networkClassify(&frame, face_dec) == 1)//如果当前帧中检测到笑脸,则绘制一个醒目的红色矩形框
    		{
    		    // rectangle(frame,Point(0,0),Point(319,239),Scalar(0,0,255),10,4,0);
    
    	   	   Point center( face_dec.x + face_dec.width*0.5, face_dec.y + face_dec.height*0.5 );
    	   	   ellipse( frame, center, Size(face_dec.width*0.5, face_dec.height*0.5), 0, 0, 360, Scalar( 0, 0, 255 ), 10, 4, 0 );
    		}
    		imshow("smile face detecting",frame);  //显示当前帧
    	}
    
    	capture.release();
    
    	return 0;
    }
    
    //预览摄像头捕获图像
    //参数:无
    //返回:0 - 正常,-1 - 异常
    int preview(void)
    {
    	VideoCapture capture;
    	capture.open(getDeviceIndex(devPath));    //打开摄像头捕获
    	if(!capture.isOpened())
    	{
    		printf("Can not open device!\n");
    		return -1;
    	}
    
    	capture.set(CAP_PROP_FRAME_WIDTH,640);   //设置捕获图像分辨率
    	capture.set(CAP_PROP_FRAME_HEIGHT,480);
    
    	Mat frame;
    	
    	while(waitKey(10) != 'q')                //循环直到按下键盘上的Q键
    	{
    		capture>>frame;                      //捕获一帧图像
    		imshow("real-time video",frame);     //显示当前帧
    	}
    
    	capture.release();
    
    	return 0;
    }
    
    //打印帮助信息
    //参数:无
    //返回:无
    void printHelpMessage(void)
    {
    	printf("A BP artificial neural network demo	for smile detection.\n");
    	printf("Usage:\n");
    	printf("\tsmileDetection [options] ...\n\n");
    	printf("Options:\n");
    	printf("\t-h\tprint this help message\n");
    	printf("\t-p\tpreview the real-time video\n");
    	printf("\t-d\tspecify the camera device, default /dev/video0\n");
    	printf("\t-t\ttrain a network only\n");
        printf("\t-e\tdetect smile face form camera capture stream\n");
    	printf("\t-T\tspecify the positive sample location, default ../trainset/positive/\n");
    	printf("\t-F\tspecify the negtive sample location, default ../trainset/negtive/\n");
    	printf("\t-N\tspecify the network file, default ../networks/default.xml\n");
    }


    七、SVM分类 笑脸 检测

    /*
    使用 级联分类器 检测出人脸
    缩放到 统一大小  放入
    SVM 训练
    笑脸 / 非笑脸
    保存分类器
    
    OpenCV 中使用的激活函数是另一种形式,
    f(x) = b *  (1 - exp(-c*x)) / (1 + exp(-c*x))
    当 α = β = 1 时
    f(x) =(1 - exp(-x)) / (1 + exp(-x))
    
    */
    #include "svm_class.h"
    
    CascadeClassifier* faceCascade;  //人脸检测 级联分类器 指针
    // Ptr<ANN_MLP> neuralNetwork;      //人工神经网络
    // Ptr<SVM> svm = SVM::create();// SVM分类器
    Ptr<SVM> svm_ptr;
    
    //初始化一个用于训练的SVM
    //参数:无
    //返回:0 - 正常,-1 - 异常
    int SVMInit(void)
    {
    	/*加载人脸检测分类器*/
    	faceCascade = new CascadeClassifier();
    	if(!faceCascade->load("../../common/data/cascade/haarcascades/haarcascade_frontalface_alt.xml"))
    	{
    		printf("Can't load face cascade.\n");
    		return -1;
    	}
    
    	// SVM分类器参数
        svm_ptr = SVM::create();// 创建
        // 50*50 pix像素的图片 展开成一列 2500 像素输入 
        svm_ptr->setType(SVM::C_SVC);   //  SVM类型.  C_SVC 该类型可以用于n-类分类问题 (n \geq 2)
        svm_ptr->setC(0.5);             //  错分类样本离同类区域的距离 的权重
        svm_ptr->setKernel(SVM::LINEAR);   //  
        // 算法终止条件.   最大迭代次数和容许误差
        svm_ptr->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER, (int)1e7, 1e-6)); 
    	return 0;
    }
    
    //初始化一个用于分类的 SVM
    //参数:训练好的 SVM分类器
    //返回:0 - 正常,-1 - 异常
    int SVMInit(const char *fileName)
    {
    	// 加载人脸检测 分类器
        faceCascade = new CascadeClassifier();
    // ../../common/data/cascade/lbpcascades/lbpcascade_frontalface.xml  // lbp 特征
        if(!faceCascade->load("../../common/data/cascade/haarcascades/haarcascade_frontalface_alt.xml"))
        {
            printf("Can't load face cascade.\n");
            return -1;
        }
    	/*从指定文件中加载神经网络数据*/
        printf("Loading SVM form %s\n",fileName);
        svm_ptr = Algorithm::load<SVM>(fileName);
    // Ptr<ml::ANN_MLP> ann = ml::ANN_MLP::load("ann_param");
        return 0;
    }
    
    //训练 SVM 并保存到指定的文件
    //参数:正样本容器,负样本容器,保存 SVM 数据的文件名
    //返回:0 - 正常,-1 - 异常
    int SVMTrain(std::vector<Mat>* posFaceVector,std::vector<Mat>* negFaceVector, const char *fileName)
    {
    //初始化训练数据矩阵和类标矩阵====================
    	//训练数据矩阵每行为一个样本,行数=正样本数+负样本数,列数=样本维数=样本宽度x样本高度
    	//类标矩阵每行为一个样本的类标,行数同上,列数=1 +1代表正样本 -1代表负样本
        Mat trainDataMat(posFaceVector->size() + negFaceVector->size(), 2500, CV_32FC1);// 数据量 × 2500维度
    // SVM 标签不能为 float or double   CV_32FC1 -----> CV_32S
        Mat labelMat = Mat::zeros(posFaceVector->size() + negFaceVector->size(),1,CV_32S);//标签 数据量 × 1维度
    
    // 生成训练数据矩阵和类标矩阵================
        for(unsigned int i = 0;i < posFaceVector->size();i++)
        {
          // 进来的图像为矩阵 reshap 到 一列 在复制到 trainDataMat的一行 并且 归一化到 0~1 之间  防止计算溢出
            posFaceVector->at(i).reshape(0,1).convertTo(trainDataMat.row(i),CV_32FC1,0.003921569,0);  //0.003921569=1/255
            labelMat.at<float>(i) = 1;//类标签 样本  1代表正样本  
        }
    
        for(unsigned int i = 0;i < negFaceVector->size();i++)
        {
            negFaceVector->at(i).reshape(0,1).convertTo(trainDataMat.row(i + posFaceVector->size()),CV_32FC1,0.003921569,0);
            labelMat.at<float>(i + posFaceVector->size()) = -1;// 代表负样本
        }
    
    // 训练网络=================
        svm_ptr->train(trainDataMat,ROW_SAMPLE,labelMat);
    
    // 保存网络数据=================
        svm_ptr->save(fileName);
    
        return 0;
    }
    
    //用训练好的神经网络进行分类
    //参数:待分类的样本
    //返回:1 - 正样本(笑脸),0 - 负样本(非笑脸),-1 - 未检测到人脸
    int SVMClassify(Mat *image, Rect& face_dec)
    {
    	// 人脸检测
        Mat gray,roi,classifyDataMat;
        std::vector<Rect> faces;// 级联分类器 检测到的人脸矩阵 检测到的人脸 矩形区域 左下点坐标 长和宽
        cvtColor(*image,gray,CV_BGR2GRAY);   //将样本转换为灰度图
        //equalizeHist(gray,gray);             //直方图均衡化
    
        faceCascade->detectMultiScale(
                gray,
                faces,
                1.1,// 每张图像缩小的尽寸比例 1.1 即每次搜索窗口扩大10%
                4,// 每个候选矩阵应包含的像素领域
                CASCADE_SCALE_IMAGE|CASCADE_FIND_BIGGEST_OBJECT|CASCADE_DO_ROUGH_SEARCH,
                Size(50,50)); //在样本中检测人脸,只检测最大的人脸  至少50*50 像素的 表示最小的目标检测尺寸
    
        if(faces.size() < 1)                 //样本中检测不到人脸
            return -1;
        face_dec = faces[0];// 人脸
        roi = gray(faces[0]);// 取灰度图像的 第一个人脸
    // Point center( faces[0].x + faces[0].width*0.5, faces[0].y + faces[0].height*0.5 );//中心点
        resize(roi,roi,Size(50,50),0,0,CV_INTER_LINEAR);  //将检测到的人脸区域缩放到50x50像素
    
    // 对检测到的人脸进行分类,即判断是笑脸还是非笑脸 
        roi = Mat_<float>(roi);
        roi.reshape(0,1).convertTo(classifyDataMat,CV_32FC1,0.003921569,0);//转换为待分类数据  50×50---> 1× 2500 归一化到 0~1 
        float response = svm_ptr->predict(classifyDataMat);
        cout<< response << endl;  //打印分类结果 -1   1
        if(response >= 1)
            return 1;  //正类 1
        else
            return 0;  //否则为负样本(非笑脸)
    }
    
    //从指定图像中检测人脸
    //参数:图像文件名,存放人脸区域的矩阵
    //返回:1 - 成功检测到人脸,0 - 未检测到人脸,-1 - 异常
    int detectFaceFormImg(const char *imgFilename, Mat *faceRoi)
    {
        Mat img,gray,roi;
    	
        std::vector<Rect> faces;// 级联分类器 检测到的人脸矩阵 检测到的人脸 矩形区域 左下点坐标 长和宽
    	/*读取图像文件*/
    	img = imread(imgFilename);
    	if(img.empty())
    	{
    		printf("Can't read file %s\n",imgFilename);
    		return -1;
    	}
    
    	cvtColor(img,gray,CV_BGR2GRAY);  //将图像转换为灰度图
    	//equalizeHist(gray,gray);//直方图均衡化
    
    	faceCascade->detectMultiScale(
    			gray,
                            faces,
    			1.1,// 每张图像缩小的尽寸比例 1.1 即每次搜索窗口扩大10%
    			6,// 每个候选矩阵应包含的像素领域
    			CASCADE_SCALE_IMAGE|CASCADE_FIND_BIGGEST_OBJECT|CASCADE_DO_ROUGH_SEARCH,
    			Size(40,40));//在图像中检测人脸 表示最小的目标检测尺寸
    
        if(faces.size() < 1)             //未检测到人脸
    	{
    		printf("Can't find face in %s, skip it.\n",imgFilename); 
    		return 0;
    	}
    
        roi = gray(faces[0]);                           //人脸区域ROI
        resize(roi,roi,Size(50,50),0,0,CV_INTER_LINEAR);//将人脸区域ROI缩放到50x50像素
    
        *faceRoi = roi.clone();                         //将人脸区域ROI拷贝到输出矩阵
    
        return 1;
    }
    /*
    
    使用 级联分类器 检测出人脸
    缩放到 统一大小  放入
    SVM 训练
    笑脸 / 非笑脸
    保存分类器
    
    OpenCV 中使用的激活函数是另一种形式,
    f(x) = b *  (1 - exp(-c*x)) / (1 + exp(-c*x))
    当 α = β = 1 时
    f(x) =(1 - exp(-x)) / (1 + exp(-x))
    
    使用 ./smile_dec -e   检测笑脸
    ./smile_dec -t        训练笑脸分类器
    
     
    
    */
    #include <stdio.h>
    #include <vector>
    #include <unistd.h>
    #include <string.h>
    #include <dirent.h>
    #include <sys/time.h>
    
    #include <opencv2/opencv.hpp>
    
    #include "svm_class.h"
    
    using namespace std;
    using namespace cv;
    
    void printHelpMessage(void);      //打印帮助信息
    int  preview(void);               //预览摄像头图像
    int  trainSVM(void);              //SVM
    int  smileFaceDetect(void);       //在摄像头图像中执行笑脸检测
    
    char devPath[256] = "/dev/video0";                       //摄像头设备文件名
    char posPath[256] = "../trainset/positive/";             //正训练集路径
    char negPath[256] = "../trainset/negtive/";              //负训练集路径
    char SVMFilename[256] = "../networks/SVMdefault.xml";    //保存神经网络数据的文件名
    
    bool preview_flag = false;  //预览标志
    bool train_flag   = false;  //训练标志
    bool detect_flag  = false;  //笑脸检测标志
    
    std::vector<Mat> posFaces;  //存放正样本的 Mat 容器
    std::vector<Mat> negFaces;  //存放负样本的 Mat 容器
    
    //主函数
    int main(int argc, char *argv[])
    {
    	/*命令行参数解析*/
    	int opt;
    
    	if(argc == 1) //没有参数
    		printHelpMessage();
    
        while((opt = getopt(argc,argv,"hpted:T:F:N:")) != -1)//获取下一个命令行参数
    	{
    		switch(opt)
    		{
    			case 'h':printHelpMessage();    break; //打印帮助信息
    			case 'd':strcpy(devPath,optarg);break; //指定摄像头设备
    			case 'p':preview_flag = true;   break; //预览摄像头图像
    			case 't':train_flag = true;     break; //训练神经网络
                            case 'e':detect_flag = true;    break; //从摄像头捕获视频中检测笑脸
    			case 'T':strcpy(posPath,optarg);break; //指定正训练集路径
    			case 'F':strcpy(negPath,optarg);break; //指定负训练集路径
                       case 'N':strcpy(SVMFilename,optarg); break; //指定神经网络文件名
    			default:printf("Invalid argument %c!\n",opt);return -1;//非法参数
    		}
    	}
    
    	/*如果路径字符串末位非'\',则添加之*/
    	if(posPath[strlen(posPath)-1] != '/')
    		strcat(posPath,"/");
    	if(negPath[strlen(negPath)-1] != '/')
    		strcat(negPath,"/");
    
    	/*根据标志决定程序的功能*/
    	if(train_flag == true)
    		trainSVM();       //训练SVM
    	else if(preview_flag == true)
    		preview();        //预览摄像头图像
            else if(detect_flag == true)
            	smileFaceDetect();//从摄像头捕获视频中检测笑脸
    
        usleep(300000);
        return 0;
    }
    
    //将摄像头设备名转换为设备索引
    //参数:设备名字符串 /dev/video0 ~ /dev/video100
    //返回:设备索引,范围0-9999
    int getDeviceIndex(const char *devicePath)
    {
    	/*检查输入字符串是否为Linux下的V4L2设备名*/
    	if(strncmp(devicePath,"/dev/video",10))
    	{
    		printf("Invalid device path!\n");
    		return -1;
    	}
    
    	char devIndex[5];
    	unsigned int i;
    
    	/*截取"/dev/video"之后的子串*/
    	for(i = 10;i < strlen(devicePath);i++)
    		devIndex[i-10] = devicePath[i];
    	devIndex[i-10] = '\0';
    	/*将字符串转换为整形数并返回*/
    	return atoi(devIndex);
    }
    
    //训练一个神经网络,并将网络数据保存到文件
    //参数:无
    //返回:0 - 正常,-1 - 异常
    int trainSVM(void)
    {
    	SVMInit();               //初始化网络
    	posFaces.reserve(1000);  //为容器预留1000个样本的内存
    	negFaces.reserve(1000);
    
    // 遍历正样本
    	DIR *posDir = opendir(posPath);//打开 正训练集路径
    	if(posDir == NULL)
    	{
    		printf("Can't open directory %s\n",posPath);
    		perror(posPath);
    		return -1;
    	}
    	struct dirent *posDirent;
    	while((posDirent = readdir(posDir)) != NULL)//读取下一个文件入口
    	{
    		/*跳过两个特殊文件*/
    		if(strcmp(posDirent->d_name,".") == 0 || strcmp(posDirent->d_name,"..") == 0)
    			continue;
    		/*获得当前文件的完整文件名*/
    		char posFileName[256];
    		strcpy(posFileName, posPath);
    		strcat(posFileName, posDirent->d_name);
    
    		//处理当前文件 
    		Mat temp;
    		int result;
    		result = detectFaceFormImg(posFileName, &temp);  //对当前文件做人脸检测
    		if(result == 1)
    		posFaces.push_back(temp);                        //若检测成功,则将人脸添加到正样本容器中
    	}
            printf("Got %d positive samples.\n",(int)posFaces.size());
    
    	// 遍历负样本 
    	DIR *negDir = opendir(negPath);//打开负训练集路径
    	if(negDir == NULL)
    	{
    		printf("Can't open directory %s\n",negPath);
    		perror(negPath);
    		return -1;
    	}
    	struct dirent *negDirent;
    	while((negDirent = readdir(negDir)) != NULL)     //读取下一个文件入口
    	{
    		/*跳过两个特殊文件*/
    		if(strcmp(negDirent->d_name,".") == 0 || strcmp(negDirent->d_name,"..") == 0)
    			continue;
    
    		/*获得当前文件的完整文件名*/
    		char negFileName[256];
    		strcpy(negFileName,negPath);
    		strcat(negFileName,negDirent->d_name);
    
    		/*处理当前文件*/
    		Mat temp;
    		int result;
    		result = detectFaceFormImg(negFileName,&temp);//对当前文件做人脸检测
    		if(result == 1)
    		negFaces.push_back(temp);                     //若检测成功,则将人脸添加到负样本容器
    	}
            printf("Got %d negtive samples.\n",(int)negFaces.size());
    
    	struct timeval before, after;//时间
    
    	/*训练神经网络,并保存到文件*/
    	printf("begin network training...\n");
    	gettimeofday(&before,NULL);
    	SVMTrain(&posFaces, &negFaces, SVMFilename);
    	gettimeofday(&after,NULL);
    	long int usec = (after.tv_sec - before.tv_sec)*1000000 + (after.tv_usec-before.tv_usec);
    	printf("training completed,use %lds %ldms\n",usec/1000000,(usec%1000000)/1000);
    	return 0;
    }
    
    //在摄像头捕获视频中检测笑脸
    //参数:无
    //返回:0 - 正常,-1 - 异常
    int smileFaceDetect(void)
    {
    	SVMInit(SVMFilename);//初始化检测神经网络
    	VideoCapture capture;
    	capture.open(getDeviceIndex(devPath));//打开摄像头捕获
    	if(!capture.isOpened())
    	{
    	printf("Can not open device!\n");
    	return -1;
    	}
            //设置捕获图像分辨率  320*240
    	capture.set(CAP_PROP_FRAME_WIDTH, 640);     
    	capture.set(CAP_PROP_FRAME_HEIGHT,480);
    
    	Mat frame;
    	while(waitKey(10) != 'q') //循环直到按下键盘上的Q键
    	{
    		capture>>frame;                 //捕获一帧图像
    		Rect  face_dec;
    		if(SVMClassify(&frame, face_dec) == 1)//如果当前帧中检测到笑脸,则绘制一个醒目的红色矩形框
    		{
    		    // rectangle(frame,Point(0,0),Point(319,239),Scalar(0,0,255),10,4,0);
    
    	   	   Point center( face_dec.x + face_dec.width*0.5, face_dec.y + face_dec.height*0.5 );
    	   	   ellipse( frame, center, Size(face_dec.width*0.5, face_dec.height*0.5), 0, 0, 360, Scalar( 0, 0, 255 ), 10, 4, 0 );
    		}
    		imshow("smile face detecting",frame);  //显示当前帧
    	}
    
    	capture.release();
    
    	return 0;
    }
    
    //预览摄像头捕获图像
    //参数:无
    //返回:0 - 正常,-1 - 异常
    int preview(void)
    {
    	VideoCapture capture;
    	capture.open(getDeviceIndex(devPath));    //打开摄像头捕获
    	if(!capture.isOpened())
    	{
    		printf("Can not open device!\n");
    		return -1;
    	}
    
    	capture.set(CAP_PROP_FRAME_WIDTH,640);   //设置捕获图像分辨率
    	capture.set(CAP_PROP_FRAME_HEIGHT,480);
    
    	Mat frame;
    	
    	while(waitKey(10) != 'q')                //循环直到按下键盘上的Q键
    	{
    		capture>>frame;                      //捕获一帧图像
    		imshow("real-time video",frame);     //显示当前帧
    	}
    
    	capture.release();
    
    	return 0;
    }
    
    //打印帮助信息
    //参数:无
    //返回:无
    void printHelpMessage(void)
    {
    	printf("A BP artificial neural network demo	for smile detection.\n");
    	printf("Usage:\n");
    	printf("\tsmileDetection [options] ...\n\n");
    	printf("Options:\n");
    	printf("\t-h\tprint this help message\n");
    	printf("\t-p\tpreview the real-time video\n");
    	printf("\t-d\tspecify the camera device, default /dev/video0\n");
    	printf("\t-t\ttrain a network only\n");
        printf("\t-e\tdetect smile face form camera capture stream\n");
    	printf("\t-T\tspecify the positive sample location, default ../trainset/positive/\n");
    	printf("\t-F\tspecify the negtive sample location, default ../trainset/negtive/\n");
    	printf("\t-N\tspecify the network file, default ../networks/default.xml\n");
    }


    展开全文
  • pca主成分分析,在多变量选择上效果较好,对数据的主成分进行分析,常用于分类、聚类、实验数据处理
  • 机器学习pca 降维,内有人脸数据,可用于机器学习下的降维算法
  • 用2DPCA和PCA进行人脸识别,算法识别率高
  • 利用主成分分析(PCA)实现对人脸特征的降维,然后利用最近邻实现人脸识别
  • 介绍了人工嗅觉系统对不同酒类样本的定性识别,尝试利用主成分分析(PCA)和人工神经网络(ANN)中改进的BP算法、改进的RBF算法(最近邻RBF与k均值RBF相结合选取中心的算法)和k均值RBF算法,实现对酒类的定性识别。...
  • 基于人工神经网络(ANN)的入侵检测系统是一个非常活跃的领域,可以检测网络上的正常或攻击连接,并且可以提高入侵检测系统(IDS)的性能,可以降低建立入侵活动时的闪烁警报率。 当前,越来越多的用户使用计算机...
  • 人工神经网络 作者:樱花猪 摘要: 本文为七月算法(julyedu.com)12月机器学习第十次次课在线笔记。人工神经网络在支持图像处理、文本、语言以及序列多种类型的数据处理时都用用到。本次课程更加侧重于实践,...

    人工神经网络

    作者:樱花猪

     

    摘要:

    本文为七月算法(julyedu.com12月机器学习第十次次课在线笔记。人工神经网络在支持图像处理、文本、语言以及序列多种类型的数据处理时都用用到。本次课程更加侧重于实践,把抽象的人工神经网络用程序展现出来,课上讲述了编程使用的工具和方法,对于日后实验有非常重要的帮助。

     

    引言:

    人工神经网络(Artificial Neural Network,即ANN ),是20世纪80 年代以来人工智能领域兴起的研究热点。它从信息处理角度对人脑神经元网络进行抽象,建立某种简单模型,按不同的连接方式组成不同的网络。在工程与学术界也常直接简称为神经网络或类神经网络。本次课程讨论了神经网络的基本框架、非常热门的BP算法以及实际编程的要点。

     

    预备知识:

    概率论;梯度下降法

     

    人工神经网络

    前向全连接网路

    人工神经网络求法

     

    一、人工神经网络

        人工神经网络基础形态是前向全连接网络,同时拥有多种变形,这些变形构成了深度学习的主要内容。

    卷积网路(CNN):属于部分连接网络,是深度学习结构核心之一

    递归网络(RNN):是一种更为复杂的网络结构,能够很好的应对序列数据。

       自编码器(Auto Encoder),一种数据特征学习,类似于PCA的作用

     

    二、前向全连接网路(Full connected Forward Network)

    1、基础神经单元

    神经元是构成神经网络的基本单元,一个神经元的组成包括:

    输入:n 维向量x;

    线性加权:

    激活函数:,求非线性,容易求导;

    输出标量:

     

    2、常用激活函数:

    a、sigmoid函数。输出值域不对称,两头过于平坦。

    b、tanh函数。两头过于平坦

    C、Rectified linear unit

       收敛速度比sigmoid和tanh快,计算简单高效。是目前最广泛使用的激活函数,特别应用在CNN。

     

    3、丛神经元到网络

    输入错,输入向量;

    中间层(隐含层)

    输出层,输出向量,用于预测、分类以及回归。

     

    三、人工神经网络求法

    1、损失函数(Loss Function)

    配合模型训练,有Loss/Error Fuction:

    Softmax / Cross-entropy loss:

    SVM / hinge loss:

     Least squares (L2 loss):

     

    2、输入数据的预处理:

        如果输入数据各个维度数据是相关的,将导致各个权重相互影响,不利于优化算法寻找局部最优解。

        如果各个维度的规模不一致,将导致对应的Error Surface容易出现狭窄区域,不利于优化算法寻找局部最优。

        通常对向量数据必须要用PCA和白化处理,而对于一般的数据只需要做到均值化和归一化。

     

    3、训练算法(BP算法)

       神经网络的训练目标是找到W和B使得损失函数最小。优化算法需要计算dW和dB。BP算法就是计算W和B导数的算法。

       BP算法的核心是链式规则。

    算法步骤:

    a. forward计算一次

    b. 逐层计算backwoard得到各个层的dW和dB。

    c. 应用随机梯度下降法SGD,更新W,B

     

    4、数值校验

       实现BP算法的过程中比较容易出错,因此有必要进行检查。

     

    5、算法优化

       多层网络的Error Surface非常复杂,存在很多局部最优,我们要寻找尽可能大的范围内的局部最优。

       动量法不随更新W合适更新“更新W”的速度。

       当位于距离较长的“坡”的时候动量法可以加快滑动速度;当位于平缓的区域时,动量法也能够保持一定的速度。





    转载于:https://www.cnblogs.com/Dr-XLJ/p/5369970.html

    展开全文
  • 本文提出了一种基于人工神经网络 (ANN) 的预测模型,通过实施特征选择 (FS)。 问卷旨在使用 LimeSurvey 和谷歌表格收集学生的答案。 问卷共61道题,涵盖体育、健康、居住、学术活动、社会和管理信息等多个领域。 ...
  • 人工神经网络与字符分割及识别

    千次阅读 2013-12-09 11:06:22
    周而复始的信息正向传播和误差反向传播过程,是各层权值不断调整的过程,也是神经网络学习训练的过程,此过程一直进行到网络输出的误差减少到可以接受的程度,或者预先设定的学习次数为止。  BP神经网络模型BP...

    模式识别算法交流群:272410620


    Abstract 2

    一 引言:... 3

    二 字符图像获取:... 3

    三 字符预处理... 4

    3.2 字符区域………………………………………………………………………….4

    3.2 字符区域分割:... 4

    3.3 单个字体分割:... 4

    3.4 单个字体裁剪... 5

    四 模板字符识别... 5

    4.2 字符模板归一化... 5

    五BP神经网络字符识别... 5

    5.1 训练样本制作………………………………………………………………………………….6

    5.2设计BP神经网络………………………………………………………………………………6

    5.3 BP训练... 7

    六 识别结果发送下位机... 7

    5.1 MATLAB下的串口工具:... 7

    5.2 下位机处理... 7

    5.2. 3 串口通信图:... 9

    七 总结:... 9

    摘 要

    在MATLAB环境下利用USB摄像头采集字符图像,读取一帧保存为图像,然后对读取保存的字符图像,灰度化,二值化,在此基础上做倾斜矫正,对矫正的图像进行滤波平滑处理,然后对字符区域进行提取分割出单个字符,识别方法一是采用模板匹配的方法逐个对字符与预先制作好的字符模板比较,如果结果小于某一阈值则结果就是模板上的字符;二是采用BP神经网络训练,通过训练好的net对待识别字符进行识别。最然后将识别结果通过MATLAB下的串口工具输出51单片机上用液晶显示出来。

                                                                                                                                                                     

    关键字: 倾斜矫正,字符分割,模板匹配,BP神经网络,液晶显示

     

     

    Abstract

    In the MATLAB environmentusing USB camera capture the character images, saved as an image reading, thenread the saved character images, grayscale, binary, on this basis do tilt correction,the correction image smoothing filter, and then extract the character regionsegmentation of a single character, and then one by one using a templatematching method of character with good character template is a pre-production,if the result is less than a certain threshold, the result is a template of thecharacter. Second, the BP neural network trained by the trained net to identifythe character towards recognition The results will identify the most and thenthe serial port through the MATLAB tool output 51 under microcontroller withLCD display.

     

     

    Keyword: Tilt correction, character segmentation,template matching, liquid crystal display

    一 引言:

    光学字符识别(OCR,Optical Character Recognition)是指对文本资料进行扫描,然后对图像文件进行分析处理,获取文字及版面信息的过程。已有30多年历史,近几年又出现了图像字符识别(image character recognition,ICR)和智能字符识别(intelligent character recognition,ICR),实际上这三种自动识别技术的基本原理大致相同。

    关于字符识别的方法有很多种,最简单的就是模板匹配,还有根据采集到的字符用BP神经网络或者SVM来训练得到结果的方式。本文主要针对模板匹配的方式,在MATLAB环境下编程实现。

     

    二 字符图像获取:

    在MATLAB下利用image acquisition toolbox获取视频帧,并保存图像在工程文件夹内。摄像头采用普通的USB摄像头,由于这种摄像头拍摄的照片延时比较大,所以先用image acquisition toolbox下的对视频进行预览,调整出最佳的效果来,采集的图像效果越好则识别率越高。 根据测试,实验选择640*480的视频获取窗口,颜色空间选取为RGB空间,获取一帧后保存为jpg的存储格式。


    三 字符预处理

    3.1字符矫正

    由于摄像头拍摄的图像存在一定存在的倾斜度,在分割字符区域时,应先对字符进行矫正。过程如下:

    将通过摄像头获取的保存帧图像灰度化,然后对其进行边缘提取,再在1到180度角内对图像进行旋转,记录下边缘提取后的图像在x轴方向上的投影,当x轴方向上的投影最小的时候即表示图像中字符平行于y轴,已经完成矫正,此时记录下旋转的倾斜角。然后利用imrotate函数实现对字符图像的矫正。

     

    3.2 字符区域分割:

    在第三步完成对字符图像的倾斜矫正后,将图像分别做x轴和y轴方向上的投影既可以知道字符区域在x轴上的像素分布范围和y轴上的像素分布范围,然后对根据这个范围对图像做分割,在MATLAB中表示为:

    goal=I(ix1:iy1,jx1:jy1);

    其中goal为分割后的图像,I为分割前的图像,ix1和ix2分别为x轴上投影的像素范围的起始坐标值和终止坐标值,iy1和iy2分别为y轴上投影的像素范围的起始坐标值和终止坐标值。

     

    3.3 单个字体分割:

    在分割得到的字符区域图像上,只需要做y轴上的投影就可以知道每个字符在y轴上的分布区间,然后利用这个分布区间就可以分割出单个字符。


    3.4 单个字体裁剪

    在第五步分割出来的字符基础上进一步对字符的像素区域进行裁剪,原理也是分别做x轴,y轴方向上的投影,求的字符的区间再做剪裁。

     

    四 模板字符识别

    4.1字符模板制作:

    模板的要求是与要识别的字符的字体格式一致,实验中采用word上的标准字符,通过截图软件截图后按照3-6步的处理过程制作出需要的字符模板,从0到9共10个数字,A到Z共26个字母。

     

    4.2 字符模板归一化

    在满足识别率的条件下,尽量采用小模板识别可以提神运算速度,具体的模板大小,可以根据后面的与待识别字符的比较中调节。

     

    4.3识别过程:

    将待识别字符与字符模板做同样的归一化处理,然后遍历与字符模板比较,处理方法为先和字符模板做差,然后计算做差后的图像的总像素值,如果小于每一个阈值,则表示该待识别字符和该模板是同一个字符,这样就完成了一次识别。

    循环对要识别的字符做同样的处理就可以识别出所有的字符,将结果保存在字符串中。

     

    五 BP神经网络字符识别

    BP(Back Propagation)网络是1986年由Rumelhart和McCelland为首的科学家小组提出,是一种按误差逆传播算法训练的多层前馈网络,是目前应用最广泛的神经网络模型之一。BP网络能学习和存贮大量的输入-输出模式映射关系,而无需事前揭示描述这种映射关系的数学方程。它的学习规则是使用最速下降法,通过反向传播来不断调整网络的权值和阈值,使网络的误差平方和最小。BP神经网络模型拓扑结构包括输入层(input)、隐层(hide layer)和输出层(outputlayer)。

    BP (Back Propagation)神经网络,即误差反传误差反向传播算法的学习过程,由信息的正向传播和误差的反向传播两个过程组成。输入层各神经元负责接收来自外界的输入信息,并传递给中间层各神经元;中间层是内部信息处理层,负责信息变换,根据信息变化能力的需求,中间层可以设计为单隐层或者多隐层结构;最后一个隐层传递到输出层各神经元的信息,经进一步处理后,完成一次学习的正向传播处理过程,由输出层向外界输出信息处理结果。当实际输出与期望输出不符时,进入误差的反向传播阶段。误差通过输出层,按误差梯度下降的方式修正各层权值,向隐层、输入层逐层反传。周而复始的信息正向传播和误差反向传播过程,是各层权值不断调整的过程,也是神经网络学习训练的过程,此过程一直进行到网络输出的误差减少到可以接受的程度,或者预先设定的学习次数为止。

      BP神经网络模型BP网络模型包括其输入输出模型、作用函数模型、误差计算模型和自学习模型。

      (1)节点输出模型

      隐节点输出模型:Oj=f(∑Wij×Xi-qj) (1)

      输出节点输出模型:Yk=f(∑Tjk×Oj-qk) (2)

      f-非线形作用函数;q -神经单元阈值。

      图1 典型BP网络结构模型

      (2)作用函数模型

      作用函数是反映下层输入对上层节点刺激脉冲强度的函数又称刺激函数,一般取为(0,1)内连续取值Sigmoid函数: f(x)=1/(1+e) (3)

      (3)误差计算模型

      误差计算模型是反映神经网络期望输出与计算输出之间误差大小的函数:

      Ep=1/2×∑(tpi-Opi) (4)

      tpi- i节点的期望输出值;Opi-i节点计算输出值。

      (4)自学习模型

      神经网络的学习过程,即连接下层节点和上层节点之间的权重拒阵Wij的设定和误差修正过程。BP网络有师学习方式-需要设定期望值和无师学习方式-只需输入模式之分。自学习模型为

      △Wij(n+1)= h ×Фi×Oj+a×△Wij(n) (5)

    h -学习因子;Фi-输出节点i的计算误差;Oj-输出节点j的计算输出;a-动量因子。

     

    5.1 训练样本制作:

    在不同分辨率和不同倾斜角度下分别采集几组图片作为训练样本,本实验为了节省计算时间,根据需要只做12345ABCDE这10个字符的识别,因此只各采集了10组数据。不同分辨率下5组,不同倾斜角度下5组。然后按照2-4上的操作过程的对字符进行处理,获得32*32大小的训练样本共100个。

     

    5.2设计BP神经网络

    利用MATLAB下的神经网络工具设计一个,以字符图像的x轴y轴像素值为输入特征作为输入层的输入;以logsig函数作为隐含层,隐含层设计节点25个,输出层就是预期的结果,共十种可能,所以有输出层有十种输出。

    net=newff(pr,[25 1],{'logsig' 'purelin'},'traingdx', 'learngdm');

    net.trainParam.epochs=250;%训练步数

    net.trainParam.goal=0.001;%目标误差

    net.trainParam.show=10;%系统每10步显示一次训练误差的变化曲线

    net.trainParam.lr=0.05; %学习速度

    net=train(net,p,t)%训练

    并保存训练结果save name net

     

    5.3 BP训练

    首先对待识别字符预处理,然后读取读取训练好的网络load name net,通过sim函数对字符进行识别,结果输出,保存在一个字符串内。

     

    六 识别结果发送下位机

    利用MTLAB下的串口工具发送识别出的结果给下位机,下位机为51核的单片机,然后在单片机内经过程序处理驱动LM1602液晶显示结果。

     

    5.1 MATLAB下的串口工具:

    在Matlab6.0以上版本中新增的设备控制工具条(instrument control toolbox)具备支持计算机与其它具有串口的外部设备之间的通信的功能。其特点如下:

    a、支持基于串行接口(RS-232、RS-422、RS-485)的通信;

    b、通信数据支持二进制和文本(ASCII)两种方式;

    c、支持异步通信和同步通信;

    d、支持基于事件驱动的通信(亦称中断方式)。

     

    5.2 下位机处理

    5.2.1主控电路

    主控芯片采用基于51核的STC12A50S8,51单片机是对目前所有兼容Intel 8031指令系统的单片机的统称。

    ·8位CPU·4kbytes 程序存储器(ROM)(52为8K)

      ·256bytes的数据存储器(RAM)(52有384bytes的RAM)

      ·32条I/O口线·111条指令,大部分为单字节指令

      ·21个专用寄存器

      ·2个可编程定时/计数器·5个中断源,2个优先级(52有6个)

      ·一个全双工串行通信口

      ·外部数据存储器寻址空间为64kB

      ·外部程序存储器寻址空间为64kB

      ·逻辑操作位寻址功能·双列直插40PinDIP封装

      ·单一+5V电源供电

      CPU:由运算和控制逻辑组成,同时还包括中断系统和部分外部特殊功能寄存器;

      RAM:用以存放可以读写的数据,如运算的中间结果、最终结果以及欲显示的数据;

      ROM:用以存放程序、一些原始数据和表格;

      I/O口:四个8位并行I/O口,既可用作输入,也可用作输出;

      T/C:两个定时/记数器,既可以工作在定时模式,也可以工作在记数模式;

      五个中断源的中断控制系统;

      一个全双工UART(通用异步接收发送器)的串行I/O口,用于实现单片机之间或单片机与微机之间的串行通信;

      片内振荡器和时钟产生电路,石英晶体和微调电容需要外接。最高振荡频率为12M。

    5.2.2 液晶显示电路

    实验中显示模板采用1602字符型液晶,它是工业字符型液晶,能够同时显示16x02即32个字符。602液晶模块内部的字符发生存储器(CGROM)已经存储了160个不同的点阵字符图形,这些字符有:阿拉伯数字、英文字母的大小写、常用的符号、和日文假名等,每一个字符都有一个固定的代码,比如大写的英文字母“A”的代码是01000001B(41H),显示时模块把地址41H中的点阵字符图形显示出来,我们就能看到字母“A”。

    因为1602识别的是ASCII码,试验可以用ASCII码直接赋值,在单片机编程中还可以用字符型常量或变量赋值,如'A’。

    5.2. 3 串口通信图:

    由于单片机的串口输出为TTL电平,与PC机通信是需要采用转换为RS232电平,实验中使用美信公司的MAX232芯片。它是美信公司专门为电脑的RS-232标准串口设计的单电源电平转换芯片,使用+5v单电源供电。

     

    七 总结:

    本实验完成了usb摄像头的视频帧图像采集,并对采集图像进行了数字图像处理,采用模板匹配和BP神经网络训练的方式对字符进行识别,并利用MATLAB下的串口工具和下位机单片机通信,发送识别结果显示在字符液晶上。

    试验中存在的问题:一是,对图像字符进行分割的时候,如果图像采集的分辨率过低的话会出现字符断裂的情况,这时候要做的就是对字符进行连通域检测。二是在做本实验的程序都是针对特定字符进行处理的,没有做自适应的字符个数检测。三是BP训练样本数太少,所以训练后的网络对字符的识别结果并不好。这些都需要后续的改进。

     

    八参考文献:

    [ 1]  王鹏.基于神经网络的手写体字符识别 北京工业大学 , 2002

    [ 2]  闫雪梅 ,王晓华 ,夏兴高. 基于 PCA和 BP神经网络算法的车牌字符识别 北京理工大学信息科学技术学院 2007

    [ 3]  金城 二维图像特征研究 浙江大学博士论文 2006

    [ 4]  MATLAB2010R image processing tools

     

    附录:

    MATLAB程序:

    一、模板匹配:

    %%%%%%%%%%%%%%%%%模板字符识别1.0%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    %朱本福
    %利用usb摄像头在MATLAB中通过image acquisition toolbox
    %获取一帧图片并保存为ocr.jpg在读取图片进行处理,采用模板匹配的
    %方式进行识别,模板为根据操作制作的,最后将结果通过串口发送出去显示在单片机上
    %MATLAB2010下开发
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    clc;
    clear all;
    close all;
    vid = videoinput('winvideo', 1, 'YUY2_640x480');%视频获取字符图像
    src = getselectedsource(vid);
    vid.FramesPerTrigger = 1;
    preview(vid);%预览
    start(vid) ;
    vid.ReturnedColorspace = 'rgb';
    vid.FramesPerTrigger =1;
    imaqmem(10000000);
    frame = getsnapshot(vid) ;%获取一帧
    figure(1);
    imshow(frame)
    imwrite(frame,'ocr.jpg','jpg');%保存该帧图片

    stop(vid);
    delete(vid);


    I=imread('ocr.jpg');%读取测试图片
    [m,n,z]=size(I)
    figure(1);
    imshow(I);title('测试图片')
    I1=rgb2gray(I);
        imwrite(I1,'I1.jpg');%保存二值化图像
    figure(2);
    imshow(I1);%显示二值化图像
    %figure(3);
    %imhist(I1);


    Ic = imcomplement(I1);%取反
        imwrite(Ic,'Ic.jpg');
    figure(33);imshow(Ic);
    BW = im2bw(Ic, graythresh(Ic));%二值化
        imwrite(BW,'BW.jpg');
    figure(5), imshow(BW); 


    bw=edge(I1,'prewitt');%边缘提取
        imwrite(bw,'bw.jpg');
    figure(32);imshow(bw);
    theta=1:180;
    [R,xp]=radon(bw,theta);
    [I0,J]=find(R>=max(max(R)));%J记录了倾斜角
    qingxiejiao=90-J
    goal1=imrotate(BW,qingxiejiao,'bilinear','crop');
        imwrite(goal1,'goal1.jpg');
    figure,imshow(I1);title('correct image');



    %中值滤波
    goal3=medfilt2(goal1,[3,3]);
    figure(19), imshow(goal3);


    goal3=medfilt2(goal3,[3,3]);
        imwrite(goal3,'goal3.jpg');
    figure(19), imshow(goal3);


    %求解X方向的投影像素范围
    [ix1,iy1]=xfenge(goal1)


    %求解y方向的投影像素范围
    [jx1,jy1]=yfenge(goal1)


    %字符区域分割:
    goal4=goal3(ix1:iy1,jx1:jy1);
        imwrite(goal4,'goal4.jpg');
    figure(21);imshow(goal4);
    [m,n]=size(goal4)
    %[L,num] = bwlabel(goal4,8);%区域标记,1,2,3,4


    ysum(n-1)=0;
    for y=1:n-1
    ysum(y)=sum(goal4(:,y));
    end
    %y=1:n-1;
    %figure(12)
    %plot(y,ysum)%画出y方向上的像素分布


    %找出ysum分布的几个与y轴交点就是单个字符在y轴上分布的区间
    i=1;j=1;
    for y=1:n-2
        if (ysum(y)==0)&(ysum(y+1)~=0)
            yy(i)=y
            i=i+1;
        end
        
        if (ysum(y)~=0)&(ysum(y+1)==0)
            yx(j)=y
            j=j+1;
        end
    end
    [m_yy,n_yy]=size(yy);%求出字符的分布并分割
    if n_yy==3 %根据n_yy和n_yx的个数及分布区间,选择分割区间
    %segment
    num1=goal4(1:m , 1:yx(1));%分割出字符1
    figure(23);imshow(num1);


    num2=goal4(1:m ,yy(1):yx(2));%分割出字符2
    figure(24);imshow(num2);


    num3=goal4(1:m ,  yy(2):yx(3));%分割出字符3
    figure(25);imshow(num3);


    num4=goal4(1:m ,  yy(3):n);%分割出字符4
    figure(26);imshow(num4);


    %[m1,n1]=size(num1)%求出各个字符的大小
    %[m2,n2]=size(num2)
    %[m3,n3]=size(num3)
    %[m4,n4]=size(num4)
    %对单个字符细分,避免字体出现大小不一致的情况,也就是再在x轴上进行分割
    [ix1,iy1]=xfenge(num1)
    [ix2,iy2]=xfenge(num2)
    [ix3,iy3]=xfenge(num3)
    [ix4,iy4]=xfenge(num4)
    num1=goal4(ix1:iy1 , 1:yx(1));
    figure(23);imshow(num1);



    num2=goal4(ix2:iy2 ,yy(1):yx(2));
    figure(24);imshow(num2);


    num3=goal4(ix3:iy3 ,  yy(2):yx(3));
    figure(25);imshow(num3);


    num4=goal4(ix4:iy4 ,  yy(3):n);
    figure(26);imshow(num4);


    imwrite(num1, 'nnum1.bmp'); 
    imwrite(num2, 'nnum2.bmp'); 
    imwrite(num3, 'nnum3.bmp'); 
    imwrite(num4, 'nnum4.bmp'); 
    end


    if n_yy==4 %根据n_yy和n_yx的个数及分布区间,选择分割区间
        %segment
    num1=goal4(1:m , yy(1):yx(1));
    figure(23);imshow(num1);


    num2=goal4(1:m ,yy(2):yx(2));
    figure(24);imshow(num2);


    num3=goal4(1:m ,  yy(3):yx(3));
    figure(25);imshow(num3);


    num4=goal4(1:m ,  yy(4):n);
    figure(26);imshow(num4);
    %[m1,n1]=size(num1)
    %[m2,n2]=size(num2)
    %[m3,n3]=size(num3)
    %[m4,n4]=size(num4)
    %对单个字符细分
    [ix1,iy1]=xfenge(num1)
    [ix2,iy2]=xfenge(num2)
    [ix3,iy3]=xfenge(num3)
    [ix4,iy4]=xfenge(num4)
    num1=goal4(ix1:iy1 , yy(1):yx(1));
    figure(23);imshow(num1);


    num2=goal4(ix2:iy2 ,yy(2):yx(2));
    figure(24);imshow(num2);


    num3=goal4(ix3:iy3 ,  yy(3):yx(3));
    figure(25);imshow(num3);


    num4=goal4(ix4:iy4 ,  yy(4):n);
    figure(26);imshow(num4);
    imwrite(num1, 'nnum1.bmp'); 
    imwrite(num2, 'nnum2.bmp'); 
    imwrite(num3, 'nnum3.bmp'); 
    imwrite(num4, 'nnum4.bmp'); 
    end




    for i=1:36 %将模板归一化
        imageName=strcat(num2str(i),'.bmp');
        I = imread(imageName);
       % figure;imshow(imageName);
       M{i}=imresize(I,[16,12],'nearest');
       %figure;imshow(M{i});
    end



    Rchar(4)='0';
    for i=1:4 
        imagenum=strcat('nnum',num2str(i),'.bmp');
        J=imread(imagenum);
         [m,n]=size(J)
        num{i}=imresize(J,[16,12],'nearest');  %待识别字符归一化
        [m,n]=size(num{i})
        figure;imshow(num{i});
     for j=1:36   
        MM{j}=xor(num{i} , M{j});  %差分比较
       % figure;imshow(MM{j});
        dist(j)=sum(sum(MM{j}))
        
            if dist(j)<=28  %这个可以根据dist的具体情况选取,这里去18分割效果不错
            char(i)=j
            end
        end

          
        
    switch char(i)%遍历比较
             case 1 
                 Rchar(i)='1' ;
             case 2 
                 Rchar(i)='2';
             case 3 
                 Rchar(i)='3';
             case 4
                 Rchar(i)='4' ;
             case 5 
                 Rchar(i)='5';
             case 6 
                 Rchar(i)='6';
             case 7 
                 Rchar(i)='7';
             case 8 
                 Rchar(i)='8';
             case 9 
                 Rchar(i)='9';
             case 10 
                 Rchar(i)='0';
             case 11 
                 Rchar(i)='A' ;
             case 12 
                 Rchar(i)='B';
             case 13 
                 Rchar(i)='C';
             case 14 
                 Rchar(i)='D' ;
             case 15 
                 Rchar(i)='E';
             case 16 
                 Rchar(i)='F';
             case 17
                 Rchar(i)='G' ;
             case 18 
                 Rchar(i)='H';
             case 19 
                 Rchar(i)='I';
             case 20 
                 Rchar(i)='J' ;
             case 21 
                 Rchar(i)='K';
             case 22 
                 Rchar(i)='L';
             case 23 
                 Rchar(i)='M' ;
             case 24 
                 Rchar(i)='N';
            case 25 
                Rchar(i)='O';
             case 26 
                 Rchar(i)='P' ;
             case 27 
                 Rchar(i)='Q';
             case 28 
                 Rchar(i)='R';
             case 29 
                 Rchar(i)='S' ;
             case 30 
                 Rchar(i)='T';
             case 31 
                 Rchar(i)='U';
             case 32 
                 Rchar(i)='V' ;
             case 33 
                 Rchar(i)='W';
             case 34
                 Rchar(i)='X';
             case 35 
                 Rchar(i)='Y' ;
             case 36 
                 Rchar(i)='Z';
             otherwise 
                 Rchar(i)='false'   ;
         end
     end
     schar=[Rchar(1),Rchar(2),Rchar(3),Rchar(4)]
     
    %schar=['4','5','A','B'] 


    sport1=serial('COM4');%串口输出字符
    sport1.BaudRate=9600;
    fopen(sport1);
    fwrite(sport1,schar);
    INSTRFIND
    fclose(sport1);
    delete(sport1);
    clear sport1;
    INSTRFIND

    子函数:

    %对分割出的单个字符进行分割x方向的再分割
    function [ix,iy]=xfenge(goal1)
    [m,n]=size(goal1);
    ix(m)=0;
    xx=0;j=1;
    for  x=1:m
        for y=1:n
            if goal1(x,y)==1;
                xx=1;
            end
        end
        if xx==1
            ix(j)=x;
            j=j+1;
        end
    end
    ix=ix(1);


    iy(m)=0;
    xx=0;j=1;
    for  x=m:-1:1
        for y=n:-1:1
            if goal1(x,y)==1;
                xx=1;
            end
        end
        if xx==1
            iy(j)=x;
            j=j+1;
        end
    end
    iy=iy(1);

    子函数:

    %对分割出的单个字符进行分割y方向的再分割
    function [jx,jy]=yfenge(goal1)
    [m,n]=size(goal1);
    jx(m)=0;
    xx=0;j=1;
    for  y=1:n
        for x=1:m
            if goal1(x,y)==1;
                xx=1;
            end
        end
        if xx==1
            jx(j)=y;
            j=j+1;
        end
    end
    jx=jx(1)


    jy(m)=0;
    xx=0;j=1;
    for  y=n:-1:1
        for x=m:-1:1
            if goal1(x,y)==1;
                xx=1;
            end
        end
        if xx==1
            jy(j)=y;
            j=j+1;
        end
    end
    jy=jy(1)


    二、BP神经网络训练后识别:

    clc;
    clear all;
    close all;




    for kk = 0:99
        p1=ones(16,16);
        m=strcat('muban3\',int2str(kk),'.bmp');
        x=imread(m,'bmp');


        [i,j]=find(x==1);
        imin=min(i);
        imax=max(i);
        jmin=min(j);
        jmax=max(j);
        bw1=x(imin:imax,jmin:jmax);%words segmentation
        
        bw1=imresize(bw1,[16,16],'nearest');
        [i,j]=size(bw1);
        i1=round((16-i)/2);
        j1=round((16-j)/2);
        p1(i1+1:i1+i,j1+1:j1+j)=bw1;
        
        for m=0:15
            p(m*16+1:(m+1)*16,kk+1)=p1(1:16,m+1);
        end
        
        switch kk
            case{0,10,20,30,40,50,60,70,80,90}
                t(kk+1)=1;
            case{1,11,21,31,41,51,61,71,81,91}
                t(kk+1)=2;
            case{2,12,22,32,42,52,62,72,82,92}
                t(kk+1)=3;
            case{3,13,23,33,43,53,63,73,83,93}
                t(kk+1)=4;
            case{4,14,24,34,44,54,64,74,84,94}
                t(kk+1)=5;
            case{5,15,25,35,45,55,65,75,85,95}
                t(kk+1)=6;
            case{6,16,26,36,46,56,66,76,86,96}
                t(kk+1)=7;
            case{7,17,27,37,47,57,67,77,87,97}
                t(kk+1)=8;
            case{8,18,28,38,48,58,68,78,88,98}
                t(kk+1)=9;
            case{9,19,29,39,49,59,69,79,89,99}
                t(kk+1)=10;
        end
    end


    % 创建和训练BP网络 
    pr(1:256,1)=0;
    pr(1:256,2)=1;
    net=newff(pr,[25 1],{'logsig' 'purelin'}, 'traingdx', 'learngdm');
    net.trainParam.epochs=250;
    net.trainParam.goal=0.001;
    net.trainParam.show=10;
    net.trainParam.lr=0.05;
    net=train(net,p,t)



    save E52net net;



    % 识别 
    I=imread('ocr.jpg');%读取测试图片
    [m,n,z]=size(I)
    figure(1);
    imshow(I);title('测试图片')
    I1=rgb2gray(I);
     %   imwrite(I1,'I1.jpg');%保存二值化图像
    figure(2);
    imshow(I1);%显示二值化图像
    %figure(3);
    %imhist(I1);


    Ic = imcomplement(I1);%取反
      %  imwrite(Ic,'Ic.jpg');
    figure(33);imshow(Ic);
    BW = im2bw(Ic, graythresh(Ic));%二值化
      %  imwrite(BW,'BW.jpg');
    figure(5), imshow(BW); 


    bw=edge(I1,'prewitt');%边缘提取
     %   imwrite(bw,'bw.jpg');
    figure(32);imshow(bw);
    theta=1:180;
    [R,xp]=radon(bw,theta);
    [I0,J]=find(R>=max(max(R)));%J记录了倾斜角
    qingxiejiao=90-J
    goal1=imrotate(BW,qingxiejiao,'bilinear','crop');
      %  imwrite(goal1,'goal1.jpg');
    figure,imshow(I1);title('correct image');


    %中值滤波
    goal3=medfilt2(goal1,[3,3]);
    figure(19), imshow(goal3);


    goal3=medfilt2(goal3,[3,3]);
      %  imwrite(goal3,'goal3.jpg');
    figure(19), imshow(goal3);


    %求解X方向的投影像素范围
    [ix1,iy1]=xfenge(goal1)


    %求解y方向的投影像素范围
    [jx1,jy1]=yfenge(goal1)


    %字符区域分割:
    goal4=goal3(ix1:iy1,jx1:jy1);
      %  imwrite(goal4,'goal4.jpg');
    figure(21);imshow(goal4);
    [m,n]=size(goal4)
    %[L,num] = bwlabel(goal4,8);%区域标记,1,2,3,4


    ysum(n-1)=0;
    for y=1:n-1
    ysum(y)=sum(goal4(:,y));
    end
    %y=1:n-1;
    %figure(12)
    %plot(y,ysum)%画出y方向上的像素分布


    %找出ysum分布的几个与y轴交点就是单个字符在y轴上分布的区间
    i=1;j=1;
    for y=1:n-2
        if (ysum(y)==0)&(ysum(y+1)~=0)
            yy(i)=y
            i=i+1;
        end
        
        if (ysum(y)~=0)&(ysum(y+1)==0)
            yx(j)=y
            j=j+1;
        end
    end
    [m_yy,n_yy]=size(yy);%求出字符的分布并分割
    if n_yy==3 %根据n_yy和n_yx的个数及分布区间,选择分割区间
    %segment
    num1=goal4(1:m , 1:yx(1));%分割出字符1
    figure(41);imshow(num1);


    num2=goal4(1:m ,yy(1):yx(2));%分割出字符2
    figure(42);imshow(num2);


    num3=goal4(1:m ,  yy(2):yx(3));%分割出字符3
    figure(43);imshow(num3);


    num4=goal4(1:m ,  yy(3):n);%分割出字符4
    figure(44);imshow(num4);
    %[m1,n1]=size(num1)%求出各个字符的大小
    %[m2,n2]=size(num2)
    %[m3,n3]=size(num3)
    %[m4,n4]=size(num4)
    %对单个字符细分,避免字体出现大小不一致的情况,也就是再在x轴上进行分割
    [ix1,iy1]=xfenge(num1)
    [ix2,iy2]=xfenge(num2)
    [ix3,iy3]=xfenge(num3)
    [ix4,iy4]=xfenge(num4)


    num1=goal4(ix1:iy1 , 1:yx(1));
    figure(51);imshow(num1);


    num2=goal4(ix2:iy2 ,yy(1):yx(2));
    figure(52);imshow(num2);


    num3=goal4(ix3:iy3 ,  yy(2):yx(3));
    figure(53);imshow(num3);


    num4=goal4(ix4:iy4 ,  yy(3):n);
    figure(54);imshow(num4);


    imwrite(num1, '1.bmp'); 
    imwrite(num2, '2.bmp'); 
    imwrite(num3, '3.bmp'); 
    imwrite(num4, '4.bmp'); 
    end


    if n_yy==4 %根据n_yy和n_yx的个数及分布区间,选择分割区间
        %segment
    num1=goal4(1:m , yy(1):yx(1));
    figure(61);imshow(num1);


    num2=goal4(1:m ,yy(2):yx(2));
    figure(62);imshow(num2);


    num3=goal4(1:m ,  yy(3):yx(3));
    figure(63);imshow(num3);


    num4=goal4(1:m ,  yy(4):n);
    figure(64);imshow(num4);


    %[m1,n1]=size(num1)
    %[m2,n2]=size(num2)
    %[m3,n3]=size(num3)
    %[m4,n4]=size(num4)
    %对单个字符细分
    [ix1,iy1]=xfenge(num1)
    [ix2,iy2]=xfenge(num2)
    [ix3,iy3]=xfenge(num3)
    [ix4,iy4]=xfenge(num4)


    num1=goal4(ix1:iy1 , yy(1):yx(1));imwrite(num1, '1.bmp'); 
    figure(71);imshow(num1);


    num2=goal4(ix2:iy2 ,yy(2):yx(2));imwrite(num2, '2.bmp'); 
    figure(72);imshow(num2);


    num3=goal4(ix3:iy3 ,  yy(3):yx(3));imwrite(num3, '3.bmp'); 
    figure(73);imshow(num3);


    num4=goal4(ix4:iy4 ,  yy(4):n);imwrite(num4, '4.bmp'); 
    figure(74);imshow(num4);
    end


    for i=1:4
        imagenum=strcat(num2str(i),'.bmp');
        J=imread(imagenum);
         [m,n]=size(J)
        num{i}=imresize(J,[32,32],'nearest');  %待识别字符归一化
        [m,n]=size(num{i})
        figure;imshow(num{i});
        imwrite(num{i}, [num2str(i),'.bmp']);
        
    end
       
     clear all;
      for i=1:4  
        p(1:256,1)=1;
        p1=ones(16,16);
        load E52net net;
       % test=input('FileName:', 's');
        %x=imread(test,'bmp');
        m=strcat(num2str(i),'.bmp');
        x=imread(m,'bmp');


        [i,j]=find(x==1);
        imin=min(i);
        imax=max(i);
        jmin=min(j);
        jmax=max(j);
        bw1=x(imin:imax,jmin:jmax);%words segmentation
        
        bw1=imresize(bw1,[16,16],'nearest');
        [i,j]=size(bw1);
        i1=round((16-i)/2);
        j1=round((16-j)/2);
        p1(i1+1:i1+i,j1+1:j1+j)=bw1;
        
        for m=0:15
            p(m*16+1:(m+1)*16,1)=p1(1:16,m+1);
        end
        [a,Pf,Af]=sim(net,p);
        figure;imshow(p1);
        a=round(a)
        
            switch a
            case 1
                Rchar(i)='1'
            case 2
                Rchar(i)='2'
            case 3
                Rchar(i)='3'
            case 4
                Rchar(i)='4'
            case 5
                Rchar(i)='5'
            case 6
               Rchar(i)='A'
            case 7
               Rchar(i)='B'
            case 8
                Rchar(i)='C'
            case 9
               Rchar(i)='D'
            case 10
               Rchar(i)='E'
             otherwise 
                Rchar(i)='false'   ;
        end
    end


     schar=[Rchar(1),Rchar(2),Rchar(3),Rchar(4)]

    展开全文
  • > 【编者按】机器学习的算法很多。很多时候困惑人们都是,很多算法是一类算法,而有些算法又是从其他算法中延伸出来的。这里,我们从两个方面来给大家...在机器学习或者人工智能领域,人们首先会考虑算法的学习方式
    > 
    

    【编者按】机器学习的算法很多。很多时候困惑人们都是,很多算法是一类算法,而有些算法又是从其他算法中延伸出来的。这里,我们从两个方面来给大家介绍,第一个方面是学习的方式,第二个方面是算法的类似性。本文来自IT经理网。原作者王萌。



    以下为原文:

    学习方式

    根据数据类型的不同,对一个问题的建模有不同的方式。在机器学习或者人工智能领域,人们首先会考虑算法的学习方式。在机器学习领域,有几种主要的学习方式。将算法按照学习方式分类是一个不错的想法,这样可以让人们在建模和算法选择的时候考虑能根据输入数据来选择最合适的算法来获得最好的结果。

    监督式学习:


    在监督式学习下,输入数据被称为“训练数据”,每组训练数据有一个明确的标识或结果,如对防垃圾邮件系统中“垃圾邮件”“非垃圾邮件”,对手写数字识别中的“1“,”2“,”3“,”4“等。在建立预测模型的时候,监督式学习建立一个学习过程,将预测结果与“训练数据”的实际结果进行比较,不断的调整预测模型,直到模型的预测结果达到一个预期的准确率。监督式学习的常见应用场景如分类问题和回归问题。常见算法有逻辑回归(Logistic Regression)和反向传递神经网络(Back Propagation Neural Network)

    非监督式学习:


    在非监督式学习中,数据并不被特别标识,学习模型是为了推断出数据的一些内在结构。常见的应用场景包括关联规则的学习以及聚类等。常见算法包括Apriori算法以及k-Means算法。

    半监督式学习:


    在此学习方式下,输入数据部分被标识,部分没有被标识,这种学习模型可以用来进行预测,但是模型首先需要学习数据的内在结构以便合理的组织数据来进行预测。应用场景包括分类和回归,算法包括一些对常用监督式学习算法的延伸,这些算法首先试图对未标识数据进行建模,在此基础上再对标识的数据进行预测。如图论推理算法(Graph Inference)或者拉普拉斯支持向量机(Laplacian SVM.)等。

    强化学习:


    在这种学习模式下,输入数据作为对模型的反馈,不像监督模型那样,输入数据仅仅是作为一个检查模型对错的方式,在强化学习下,输入数据直接反馈到模型,模型必须对此立刻作出调整。常见的应用场景包括动态系统以及机器人控制等。常见算法包括Q-Learning以及时间差学习(Temporal difference learning)

    在企业数据应用的场景下, 人们最常用的可能就是监督式学习和非监督式学习的模型。 在图像识别等领域,由于存在大量的非标识的数据和少量的可标识数据, 目前半监督式学习是一个很热的话题。 而强化学习更多的应用在机器人控制及其他需要进行系统控制的领域。

    算法类似性

    根据算法的功能和形式的类似性,我们可以把算法分类,比如说基于树的算法,基于神经网络的算法等等。当然,机器学习的范围非常庞大,有些算法很难明确归类到某一类。而对于有些分类来说,同一分类的算法可以针对不同类型的问题。这里,我们尽量把常用的算法按照最容易理解的方式进行分类。

    回归算法


    回归算法是试图采用对误差的衡量来探索变量之间的关系的一类算法。回归算法是统计机器学习的利器。在机器学习领域,人们说起回归,有时候是指一类问题,有时候是指一类算法,这一点常常会使初学者有所困惑。常见的回归算法包括:最小二乘法(Ordinary Least Square),逻辑回归(Logistic Regression),逐步式回归(Stepwise Regression),多元自适应回归样条(Multivariate Adaptive Regression Splines)以及本地散点平滑估计(Locally Estimated Scatterplot Smoothing)

    基于实例的算法


    基于实例的算法常常用来对决策问题建立模型,这样的模型常常先选取一批样本数据,然后根据某些近似性把新数据与样本数据进行比较。通过这种方式来寻找最佳的匹配。因此,基于实例的算法常常也被称为“赢家通吃”学习或者“基于记忆的学习”。常见的算法包括 k-Nearest Neighbor(KNN), 学习矢量量化(Learning Vector Quantization, LVQ),以及自组织映射算法(Self-Organizing Map , SOM)

    正则化方法


    正则化方法是其他算法(通常是回归算法)的延伸,根据算法的复杂度对算法进行调整。正则化方法通常对简单模型予以奖励而对复杂算法予以惩罚。常见的算法包括:Ridge Regression, Least Absolute Shrinkage and Selection Operator(LASSO),以及弹性网络(Elastic Net)。

    决策树学习


    决策树算法根据数据的属性采用树状结构建立决策模型, 决策树模型常常用来解决分类和回归问题。常见的算法包括:分类及回归树(Classification And Regression Tree, CART), ID3 (Iterative Dichotomiser 3), C4.5, Chi-squared Automatic Interaction Detection(CHAID), Decision Stump, 随机森林(Random Forest), 多元自适应回归样条(MARS)以及梯度推进机(Gradient Boosting Machine, GBM)

    贝叶斯方法


    贝叶斯方法算法是基于贝叶斯定理的一类算法,主要用来解决分类和回归问题。常见算法包括:朴素贝叶斯算法,平均单依赖估计(Averaged One-Dependence Estimators, AODE),以及Bayesian Belief Network(BBN)。

    基于核的算法


    基于核的算法中最著名的莫过于支持向量机(SVM)了。 基于核的算法把输入数据映射到一个高阶的向量空间, 在这些高阶向量空间里, 有些分类或者回归问题能够更容易的解决。 常见的基于核的算法包括:支持向量机(Support Vector Machine, SVM), 径向基函数(Radial Basis Function ,RBF), 以及线性判别分析(Linear Discriminate Analysis ,LDA)等。

    聚类算法


    聚类,就像回归一样,有时候人们描述的是一类问题,有时候描述的是一类算法。聚类算法通常按照中心点或者分层的方式对输入数据进行归并。所以的聚类算法都试图找到数据的内在结构,以便按照最大的共同点将数据进行归类。常见的聚类算法包括 k-Means算法以及期望最大化算法(Expectation Maximization, EM)。

    关联规则学习


    关联规则学习通过寻找最能够解释数据变量之间关系的规则,来找出大量多元数据集中有用的关联规则。常见算法包括 Apriori算法和Eclat算法等。

    人工神经网络


    人工神经网络算法模拟生物神经网络,是一类模式匹配算法。通常用于解决分类和回归问题。人工神经网络是机器学习的一个庞大的分支,有几百种不同的算法。(其中深度学习就是其中的一类算法,我们会单独讨论),重要的人工神经网络算法包括:感知器神经网络(Perceptron Neural Network), 反向传递(Back Propagation), Hopfield网络,自组织映射(Self-Organizing Map, SOM)。学习矢量量化(Learning Vector Quantization, LVQ)

    深度学习


    深度学习算法是对人工神经网络的发展。 在近期赢得了很多关注, 特别是 百度也开始发力深度学习后, 更是在国内引起了很多关注。  在计算能力变得日益廉价的今天,深度学习试图建立大得多也复杂得多的神经网络。很多深度学习的算法是半监督式学习算法,用来处理存在少量未标识数据的大数据集。常见的深度学习算法包括:受限波尔兹曼机(Restricted Boltzmann Machine, RBN), Deep Belief Networks(DBN),卷积网络(Convolutional Network), 堆栈式自动编码器(Stacked Auto-encoders)。

    降低维度算法


    像聚类算法一样,降低维度算法试图分析数据的内在结构,不过降低维度算法是以非监督学习的方式试图利用较少的信息来归纳或者解释数据。这类算法可以用于高维数据的可视化或者用来简化数据以便监督式学习使用。常见的算法包括:主成份分析(Principle Component Analysis, PCA),偏最小二乘回归(Partial Least Square Regression,PLS), Sammon映射,多维尺度(Multi-Dimensional Scaling, MDS),  投影追踪(Projection Pursuit)等。

    集成算法


    集成算法用一些相对较弱的学习模型独立地就同样的样本进行训练,然后把结果整合起来进行整体预测。集成算法的主要难点在于究竟集成哪些独立的较弱的学习模型以及如何把学习结果整合起来。这是一类非常强大的算法,同时也非常流行。常见的算法包括:Boosting, Bootstrapped Aggregation(Bagging), AdaBoost,堆叠泛化(Stacked Generalization, Blending),梯度推进机(Gradient Boosting Machine, GBM),随机森林(Random Forest)。

    原文链接: 机器学习常见算法分类汇总

    展开全文
  • 人工神经网络ANN的一些概念的集合

    千次阅读 2014-01-10 01:00:22
    神经网络,学生时,特意修了这门课,但完全找不到感觉,内容基本不理解,实验无头绪。经历了一些项目实践,现在回头再理解NN,一些概念自然就理解了。 1.  单层神经网络: 输入 输出 激活函数: 非线性,...
  • 巩固4种基本的分类算法的算法思想:朴素贝叶斯算法,决策树算法,人工神经网络,支持向量机算法; 能够使用现有的分类器算法代码进行分类操作 学习如何调节算法的参数以提高分类性能; 实验内容及步骤 利用现有的...
  • 机器学习在于算法的创建和开发,这些算法允许机器学习自身,并随着时间的流逝逐渐... 在这项工作中,通过定义和实现适当的人工神经网络模型,提出了一种计算PCA减少空间的方法,该方法可以准确而灵活地减小问题的维数。

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 9,662
精华内容 3,864
关键字:

pca人工神经网络