1. 程式人生 > >opencv影象識別訓練例項

opencv影象識別訓練例項

我一度以為影象訓練會很難,覺得很高深,直到我親自做了一個影象的訓練才認識到如果僅僅是單純地學習和使用,真的很簡單。

本文按照如下順序來進行詳細解釋如何進行影象識別訓練過程:

  1. 製作影象
  2. 影象資料
  3. 影象訓練
  4. 影象識別例項

選取影象集

我這裡下載了12張楊冪的照片作為資料集來提取臉部照片ROI。

#include <QApplication>

#include "opencv2/opencv.hpp"

#include <QDebug>

using namespace cv;


/** Function Headers */
void detectAndDisplay(Mat frame);

/** Global variables */
String face_cascade_name = "../haarcascade_frontalface_default.xml";
String eyes_cascade_name = "../haarcascade_eye_tree_eyeglasses.xml";
CascadeClassifier face_cascade;   //定義人臉分類器
CascadeClassifier eyes_cascade;   //定義人眼分類器
String window_name = "Capture - Face detection";




int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    int i = 1;
    for(; i < 13; i++)
    {
        string  str_path = format("../imgs/ym/%d.jpg", i);
        Mat frame = imread(str_path);
        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;
        }
        detectAndDisplay(frame);

    }
    return a.exec();
}



void detectAndDisplay(Mat frame)
{
    static int num = 1;
    std::vector<Rect> faces;
    Mat frame_gray;

    cvtColor(frame, frame_gray, COLOR_BGR2GRAY);
    //直方圖均衡化
    equalizeHist(frame_gray, frame_gray);

    //-- Detect faces
    //face_cascade.detectMultiScale(frame_gray, faces, 1.1, 5, 0 | CV_HAAR_SCALE_IMAGE, Size(5, 5));

    face_cascade.detectMultiScale(frame_gray, faces, 1.1, 3, CV_HAAR_DO_ROUGH_SEARCH, Size(50, 50));

    qDebug()<< "faces number:" << faces.size();
    for (size_t i = 0; i < faces.size(); i++)
    {
        num++;
        //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, 255), 4, 8, 0);
        rectangle(frame, faces[i],Scalar(255,0,0),2,8,0);

        Mat faceROI = frame_gray(faces[i]);
        Mat MyFace;
        if (faceROI.cols > 100)
        {
            resize(faceROI, MyFace, Size(92, 112));
            string  str = format("../generateFaces/MyFcae%d.pgm", num);
            imwrite(str, MyFace);
            imshow("ii", MyFace);
        }


        std::vector<Rect> eyes;

        //-- In each face, detect eyes
        eyes_cascade.detectMultiScale(faceROI, eyes, 1.1, 1, CV_HAAR_DO_ROUGH_SEARCH, Size(3, 3));

        for (size_t j = 0; j < eyes.size(); j++)
        {
            Rect rect(faces[i].x + eyes[j].x, faces[i].y + eyes[j].y, eyes[j].width, eyes[j].height);

            //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, 0), 4, 8, 0);
            rectangle(frame, rect, Scalar(0, 255, 0), 2, 8, 0);
        }
    }
    //-- Show what you got
    namedWindow(window_name, 2);
    imshow(window_name, frame);
}

 

影象資料

本次用的資料集市opencv給出的教程裡面的第一個資料集:The AT&T Facedatabase。又稱ORL人臉資料庫,40個人,

資料集地址:https://www.cl.cam.ac.uk/research/dtg/attarchive/facedatabase.html

可以直接下載。下載完畢解壓後是40個資料夾(s1-s40,每個資料夾中包含10張圖片)每人10張照片。照片在不同時間、不同光照、不同表情(睜眼閉眼、笑或者不笑)、不同人臉細節(戴眼鏡或者不戴眼鏡)下采集。所有的影象都在一個黑暗均勻的背景下采集的,正面豎直人臉(有些有有輕微旋轉)。

我們新增我們自己的影象資料夾s41,並且新增上一步驟生成的ROI圖。

拷貝opencv-2.4.13.3/modules/contrib/doc/facerec/src/create_csv.py到程式生成目錄,然後對其做相應的修改(注意這裡必須要把路徑寫正確,否則訓練時會由於路徑不正確引發執行錯誤,這一點對新手來說打擊還是挺大的)。

執行create_csv.py生成at.txt

影象訓練

#include <QApplication>

#include "opencv2/opencv.hpp"
#include "opencv2/contrib/contrib.hpp"

#include <iostream>
#include <fstream>
#include <sstream>
#include <math.h>

#include <QDebug>

using namespace cv;
using namespace std;


static Mat norm_0_255(InputArray _src) {
    Mat src = _src.getMat();
    // 建立和返回一個歸一化後的影象矩陣:
    Mat dst;
    switch (src.channels()) {
    case1:
        cv::normalize(_src, dst, 0, 255, NORM_MINMAX, CV_8UC1);
        break;
    case3:
        cv::normalize(_src, dst, 0, 255, NORM_MINMAX, CV_8UC3);
        break;
    default:
        src.copyTo(dst);
        break;
    }
    return dst;
}


//使用CSV檔案去讀影象和標籤,主要使用stringstream和getline方法
static void read_csv(const string& filename, vector<Mat>& images, vector<int>& labels, char separator = ';') {
    std::ifstream file(filename.c_str(), ifstream::in);
    if (!file) {
        string error_message = "No valid input file was given, please check the given filename.";
        CV_Error(CV_StsBadArg, error_message);
    }
    string line, path, classlabel;
    while (getline(file, line)) {
        stringstream liness(line);
        getline(liness, path, separator);
        getline(liness, classlabel);
        if (!path.empty() && !classlabel.empty()) {
            images.push_back(imread(path, 0));
            labels.push_back(atoi(classlabel.c_str()));
        }
    }
}




int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    //讀取你的CSV檔案路徑.
    //string fn_csv = string(argv[1]);
    string fn_csv = "at.txt";

    // 2個容器來存放影象資料和對應的標籤
    vector<Mat> images;
    vector<int> labels;
    // 讀取資料. 如果檔案不合法就會出錯
    // 輸入的檔名已經有了.
    try
    {
        read_csv(fn_csv, images, labels);
    }
    catch (cv::Exception& e)
    {
        cerr << "Error opening file \"" << fn_csv << "\". Reason: " << e.msg << endl;
        exit(1);
    }

    qDebug() << "read_csv ok";
    qDebug() << "images.size(): " << images.size();
    // 如果沒有讀取到足夠圖片,也退出.
    if (images.size() <= 1) {
        string error_message = "This demo needs at least 2 images to work. Please add more images to your data set!";
        CV_Error(CV_StsError, error_message);
    }

    // 下面的幾行程式碼僅僅是從你的資料集中移除最後一張圖片
    //[gm:自然這裡需要根據自己的需要修改,他這裡簡化了很多問題]
    Mat testSample = images[images.size() - 1];
    int testLabel = labels[labels.size() - 1];
    images.pop_back();
    labels.pop_back();
    // 下面幾行建立了一個特徵臉模型用於人臉識別,
    // 通過CSV檔案讀取的影象和標籤訓練它。
    // T這裡是一個完整的PCA變換
    //如果你只想保留10個主成分,使用如下程式碼
    //      cv::createEigenFaceRecognizer(10);
    //
    // 如果你還希望使用置信度閾值來初始化,使用以下語句:
    //      cv::createEigenFaceRecognizer(10, 123.0);
    //
    // 如果你使用所有特徵並且使用一個閾值,使用以下語句:
    //      cv::createEigenFaceRecognizer(0, 123.0);

    Ptr<FaceRecognizer> model = createEigenFaceRecognizer();
    model->train(images, labels);
    model->save("MyFacePCAModel.xml");

    Ptr<FaceRecognizer> model1 = createFisherFaceRecognizer();
    model1->train(images, labels);
    model1->save("MyFaceFisherModel.xml");

    Ptr<FaceRecognizer> model2 = createLBPHFaceRecognizer();
    model2->train(images, labels);
    model2->save("MyFaceLBPHModel.xml");


    // 下面對測試影象進行預測,predictedLabel是預測標籤結果
    int predictedLabel = model->predict(testSample);
    int predictedLabel1 = model1->predict(testSample);
    int predictedLabel2 = model2->predict(testSample);

    // 還有一種呼叫方式,可以獲取結果同時得到閾值:
    //      int predictedLabel = -1;
    //      double confidence = 0.0;
    //      model->predict(testSample, predictedLabel, confidence);

    string result_message = format("Predicted class = %d / Actual class = %d.", predictedLabel, testLabel);
    string result_message1 = format("Predicted class = %d / Actual class = %d.", predictedLabel1, testLabel);
    string result_message2 = format("Predicted class = %d / Actual class = %d.", predictedLabel2, testLabel);
    cout << result_message << endl;
    cout << result_message1 << endl;
    cout << result_message2 << endl;


    return a.exec();
}

 

影象識別例項

#include <QApplication>

#include "opencv2/opencv.hpp"

using namespace cv;
using namespace std;



int main(int argc, char *argv[])
{
    QApplication a(argc, argv);


    //VideoCapture cap(0);    //開啟預設攝像頭
//    if (!cap.isOpened())
//    {
//        return -1;
//    }
    Mat frame;
    Mat edges;
    Mat gray;

    CascadeClassifier cascade;
    bool stop = false;
    //訓練好的檔名稱,放置在可執行檔案同目錄下
    cascade.load("../haarcascade_frontalface_alt.xml");

    Ptr<FaceRecognizer> modelPCA = createEigenFaceRecognizer();
    modelPCA->load("../MyFacePCAModel.xml");

    //while (!stop)
    {
        //cap >> frame;
        frame = imread("../imgs/5.jpg");


        //建立用於存放人臉的向量容器
        vector<Rect> faces(0);

        cvtColor(frame, gray, CV_BGR2GRAY);
        //改變影象大小,使用雙線性差值
        //resize(gray, smallImg, smallImg.size(), 0, 0, INTER_LINEAR);
        //變換後的影象進行直方圖均值化處理
        equalizeHist(gray, gray);

        cascade.detectMultiScale(gray, faces,
                                 1.1, 2, 0
                                 //|CV_HAAR_FIND_BIGGEST_OBJECT
                                 //|CV_HAAR_DO_ROUGH_SEARCH
                                 | CV_HAAR_SCALE_IMAGE,
                                 Size(50, 50));

        Mat face;
        Point text_lb;

        for (size_t i = 0; i < faces.size(); i++)
        {
            if (faces[i].height > 0 && faces[i].width > 0)
            {
                face = gray(faces[i]);
                text_lb = Point(faces[i].x, faces[i].y);

                rectangle(frame, faces[i], Scalar(255, 0, 0), 1, 8, 0);
            }
        }

        Mat face_test;

        int predictPCA = 0;
        if (face.rows >= 120)
        {
            resize(face, face_test, Size(92, 112));

        }
        //Mat face_test_gray;
        //cvtColor(face_test, face_test_gray, CV_BGR2GRAY);

        if (!face_test.empty())
        {
            //測試影象應該是灰度圖
            predictPCA = modelPCA->predict(face_test);
        }

        cout << predictPCA << endl;
        if (predictPCA == 29)
        {
            string name = "YangMi";
            putText(frame, name, text_lb, FONT_HERSHEY_COMPLEX, 1, Scalar(0, 0, 255));
        }

        imshow("face", frame);


        return a.exec();
    }
}