這是從相機中獲取照片並且保存
// FaceRecog.cpp : 定義控制檯應用程序的入口點。
//
#include "stdafx.h"
#include "opencv2\opencv.hpp"
#include <vector>
#include <iostream>
#include<stdio.h>
//#include <cv.h>
#pragma warning(disable:4996)
using namespace std;
using namespace cv;
int capture()
{
CascadeClassifier cascada;
cascada.load("haarcascade_frontalface_alt2.xml");
VideoCapture cap(0);
Mat frame, myFace;
int pic_num = 1;
while (1) {
//攝像頭讀圖像
cap >> frame;
vector<Rect> faces;//vector容器存檢測到的faces
Mat frame_gray;
cvtColor(frame, frame_gray, COLOR_BGR2GRAY);//轉灰度化,減少運算
cascada.detectMultiScale(frame_gray, faces, 1.1, 4, CV_HAAR_DO_ROUGH_SEARCH, Size(70, 70), Size(1000, 1000));
printf("檢測到人臉個數:%d\n", faces.size());
//1.frame_gray表示的是要檢測的輸入圖像 2.faces表示檢測到的人臉目標序列,3. 1.1表示每次圖像尺寸減小的比例
//4. 4表示每一個目標至少要被檢測到3次纔算是真的目標(因爲周圍的像素和不同的窗口大小都可以檢測到人臉表示每一個目標至少要被檢測到3次纔算是真的目標(因爲周圍的像素和不同的窗口大小都可以檢測到人臉
/*5.flags–要麼使用默認值,要麼使用CV_HAAR_DO_CANNY_PRUNING,
函數將會使用Canny邊緣檢測來排除邊緣過多或過少的區域,
因爲這些區域通常不會是人臉所在區域;opencv3 以後都不用這個參數了*/
//6. Size(100, 100)爲目標的最小尺寸 一般爲30*30 是最小的了 也夠了
//7. Size(500, 500)爲目標的最大尺寸 其實可以不用這個,opencv會自動去找這個最大尺寸
//適當調整5,6,7兩個參數可以用來排除檢測結果中的干擾項。
//識別到的臉用矩形圈出
for (int i = 0; i < faces.size(); i++)
{
rectangle(frame, faces[i], Scalar(255, 0, 0), 2, 8, 0);
}
//當只有一個人臉時,開始拍照
if (faces.size() == 1)
{
Mat faceROI = frame_gray(faces[0]);//在灰度圖中將圈出的臉所在區域裁剪出
//cout << faces[0].x << endl;//測試下face[0].x
resize(faceROI, myFace, Size(92, 112));//將興趣域size爲92*112
putText(frame, to_string(pic_num), faces[0].tl(), 3, 1.2, (0, 0, 225), 2, 0);//在 faces[0].tl()的左上角上面寫序號
string filename = format("%d.jpg", pic_num); //存放在當前項目文件夾以1-10.jpg 命名,format就是轉爲字符串
imwrite(filename, myFace);//存在當前目錄下
imshow(filename, myFace);//顯示下size後的臉
waitKey(500);//等待500us
destroyWindow(filename);//:銷燬指定的窗口
pic_num++;//序號加1
if (pic_num == 11)
{
return 0;//當序號爲11時退出循環
}
}
int c = waitKey(10);
if ((char)c == 27) { break; } //10us內輸入esc則退出循環
imshow("frame", frame);//顯示視頻流
waitKey(100);//等待100us
}
return 0;
}
int main()
{
capture();
return 0;
}
訓練樣本
#include "stdafx.h"
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/face/facerec.hpp>
#include <iostream>
#include <fstream> //文件操作的集合,以流的方式進行
#include <sstream> //此庫定義了stringstream類,即:流的輸入輸出操作。
//使用string對象代替字符數組,避免緩衝區溢出的危險
using namespace cv;
using namespace std;
using namespace cv::face;
//歸一化圖像矩陣函數
static Mat norm_0_255(InputArray _src)
{
Mat src = _src.getMat(); //將傳入的類型爲InputArray的參數轉換爲Mat的結構
Mat dst; //創建和返回一個歸一化後的圖像
switch (src.channels())
{
case 1:
normalize(_src, dst, 0, 255, NORM_MINMAX, CV_8UC1);
break;
case 3:
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 = ';')
{
ifstream file(filename.c_str(), ifstream::in); //以輸入方式打開文件
//c_str()函數將字符串轉化爲字符數組,返回指針
if (!file)
{
string error_massage = "No valid input file was given,please check the given filename!";
CV_Error(CV_StsBadArg, error_massage);
}
string line, path, classlabel;
while (getline(file, line)) //getline(字符數組,字符個數n,終止標誌字符)
{
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())); //atoi函數將字符串轉換爲整數值
}
}
}
int main(int argc, const char* argv[])
{
//[1] 檢測合法的命令,顯示用法
//如果沒有參數輸入,則退出
//if (argc < 2)
//{
// cout << "usage:" << argv[0] << "<csv.ext> <output_folder>" << endl;
// exit(1);
//}
string output_folder;
output_folder = string("E:\\workspace\\openCV\\訓練樣本180\\result");
//[2] 讀取CSV文件路徑
string fn_csv = string("E:\\workspace\\openCV\\訓練樣本180\\a.txt");
//兩個容器來存放圖像數據和對應的標籤
vector<Mat> images;
vector<int> labels;
//讀取數據,如果文件不合法就會出錯。輸入的文件名已經有了
try
{
read_csv(fn_csv, images, labels);
}
catch (Exception& e)
{
cerr << "Error opening file" << fn_csv << ".Reason:" << e.msg << endl;
exit(1);
}
//沒有讀取到足夠多的圖片,也需要退出
if (images.size() <= 1)
{
string error_message = "This demo need at least 2 images,please add more images to your data set!";
CV_Error(CV_StsError, error_message);
}
//[3] 得到第一張圖片的高度,在下面對圖像變形得到他們原始大小時需要
int height = images[0].rows;
//[4]下面代碼僅從數據集中移除最後一張圖片,用於做測試,需要根據自己的需要進行修改
Mat testSample = images[images.size() - 1];
int testLabel = labels[labels.size() - 1];
images.pop_back(); //刪除最後一張圖片
labels.pop_back(); //刪除最後一個標籤
//[5] 創建一個特徵臉模型用於人臉識別
//通過CSV文件讀取的圖像和標籤訓練它
//這裏是一個完整的PCA 變換
//如果想保留10個主成分,使用如下代碼 cv::createEigenFaceRecognizer(10);
//如果希望使用置信度閾值來初始化,使用代碼 cv::createEigenFaceRecognizer(10, 123.0);
//如果使用所有特徵並使用一個閾值,使用代碼 cv::createEigenFaceRecognizer(0, 123.0);
//Ptr<BasicFaceRecognizer> model = createEigenFaceRecognizer();
//model->train(images, labels);
//model->save("MyFacePCAModel.xml");//保存路徑可自己設置,但注意用“\\”
Ptr<BasicFaceRecognizer> model = createEigenFaceRecognizer();
model->train(images, labels);
model->save("MyFacePCAModel.xml");//保存路徑可自己設置,但注意用“\\”
//Ptr<BasicFaceRecognizer> model1 = cre();
//model1->train(images, labels);
//model1->save("MyFaceFisherModel.xml");
//Ptr<LBPHFaceRecognizer> model2 = createEigenFaceRecognizer();
//model2->train(images, labels);
//model2->save("MyFaceLBPHModel.xml");
//[6] 對測試圖像進行預測,predictedLabel是預測標籤結果
int predictedLabel = model->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);
cout << result_message << endl;
//[7] 如何獲取特徵臉模型的特徵值例子,使用getEigenValues方法
Mat eigenvalues = model->getEigenValues();
//[8] 獲取特徵向量
Mat W = model->getEigenVectors();
//[9] 得到訓練圖像的均值向量
Mat mean = model->getMean();
//[10] 顯示或保存
imshow("mean", norm_0_255(mean.reshape(1, images[0].rows)));
imwrite(format("%s/mean.png", output_folder.c_str()), norm_0_255(mean.reshape(1, images[0].rows)));
//[11] 顯示或保存特徵臉
for (int i = 0; i < min(6, W.cols); i++) //修改數值10可以修改特徵臉的數目
{
string msg = format("Eigenvalue #%d = %.5f", i, eigenvalues.at<double>(i));
cout << msg << endl;
//得到第i個特徵向量
Mat ev = W.col(i).clone();
//把它變成原始大小,把數據顯示歸一化到0-255
Mat grayscale = norm_0_255(ev.reshape(1, height));
//使用僞彩色來顯示結果,爲了更好的觀察
Mat cgrayscale;
applyColorMap(grayscale, cgrayscale, COLORMAP_JET);
//顯示或保存
imshow(format("eigenface_%d", i), cgrayscale);
imwrite(format("%s/eigenface_%d.png", output_folder.c_str(), i), norm_0_255(cgrayscale));
}
//[12] 預測過程中,顯示或保存重建後的圖像
//修改值300可改變重構的圖像的數目
for (int num_components = min(W.cols, 6); num_components < min(W.cols, 300); num_components += 15)
{
//從模型中的特徵向量截取一部分
Mat evs = Mat(W, Range::all(), Range(0, num_components));
//在重構時,images[0]爲ORL人臉庫的第一張人臉圖,
//修改此數值0的大小可對其他人臉圖像進行特徵臉處理與重構的實驗
Mat projection = LDA::subspaceProject(evs, mean, images[0].reshape(1, 1));
//投影樣本到LDA子空間
Mat reconstruction = LDA::subspaceReconstruct(evs, mean, projection);
//重構來自於LDA子空間的投影
//歸一化結果
reconstruction = norm_0_255(reconstruction.reshape(1, images[0].rows));
//[13] 若不是存放到文件夾中就顯示他,使用暫定等待鍵盤輸入
imshow(format("eigenface_reconstruction_%d", num_components), reconstruction);
imwrite(format("%s/eigenface_reconstruction_%d.png", output_folder.c_str(), num_components), reconstruction);
}
waitKey(0);
return 0;
}
詳細訓練樣本見連接: