1.准备pair 对数据 (大概3000对相同的人脸数据, 3000对不同的人脸数据)
2.训练好识别模型 (可以基于 CASIA数据集 共 10575)
3. 预测各对,根据结果选择最优阈值。
(代码使用了 三种判断方式,L1距离, L2距离, Cos角度, 选择阈值的方式也比较简单,直接根据 相同对的均值 和 不同对的均值 的中点作为阈值)
#include <iostream>
#include <vector>
#include <fstream>
#include <istream>
#include <string>
#include <io.h>
#include <direct.h>
#include "../CascadRegression/MCLC.h"
#include <opencv2/opencv.hpp>
#include "util.h"
using namespace glasssix;
using namespace std;
using namespace caffe;
using namespace cv;
#define DEVICE 0
#define FEATUREDIM 128
struct PairImg
{
string filename1;
string filename2;
vector<float> feature1;
vector<float> feature2;
float distanceL2;
float distanceL1;
float distanceCos;
bool isPair;
};
struct PairDistance
{
float distanceL2;
float distanceL1;
float distanceCos;
bool isPair;
};
template <class Type>
Type stringToNum(const string& str)
{
istringstream iss(str);
Type num;
iss >> num;
return num;
}
void clearFileData(string filePath)
{
if (filePath == "")
{
return;
}
ofstream in;
in.open(filePath, ios::trunc);
in.close();
}
double extractFeature(MCLC & mclc, int id, Mat &img, int dims, vector<float> & feature)
{
double time = 0;
double t0 = (double)cvGetTickCount();
vector<Mat> imgdata;
imgdata.push_back(img);
//cout << "img info:" << img.rows <<" "<< img.cols <<" "<< img.channels() << endl;
unordered_map<std::string, DataBlob> result = mclc.Forward(imgdata, id);
double t1 = (double)cvGetTickCount();
time = (t1 - t0) / ((double)cvGetTickFrequency() * 1000);
//cout << "time is:" << time << " ms" << endl;
for (unordered_map<string, DataBlob>::iterator iter = result.begin(); iter != result.end(); iter++) {
//cout << "key value is" << iter->first << " the mapped value is " << " result" << endl;
string key = iter->first;
DataBlob result = iter->second;
if (result.name == "fc5")
{
for (int i = 0; i < dims; i++)
{
float x = *(result.data);
feature.push_back(x);
//cout << "x :" << x << endl;
(result.data)++;
}
}
}
return time;
}
void getFeatureImg(DataPrepareUtil &dpu, Mat &src, Mat &dst)
{
const int scale = 9;
Mat garyImg;
cvtColor(src, garyImg, CV_BGR2GRAY);
Mat dst1;
dpu.getMultiScaleBlockLBPFeature(garyImg, dst1, scale);
vector<Mat> merge_dst1;
for (int j = 0; j < 3; j++)
{
Mat dst0(dst1.rows, dst1.cols, CV_8UC1);
dst1.copyTo(dst0);
merge_dst1.push_back(dst0);
}
merge(merge_dst1, dst);
}
void getSplitLbpFeature(DataPrepareUtil &dpu, Mat &src, Mat &dst)
{
int scale = 9;
vector<Mat> splitMat3;
vector<Mat> merge_dst3;
split(src, splitMat3);
Mat dst3_0, dst3_1, dst3_2, dst3_11, dst3_22;
splitMat3[0].copyTo(dst3_0);
dpu.lbp_normal(splitMat3[1], dst3_1);
dpu.getMultiScaleBlockLBPFeature(splitMat3[2], dst3_2, scale);
copyMakeBorder(dst3_1, dst3_11, 1, 1, 1, 1, BORDER_CONSTANT, 0);
copyMakeBorder(dst3_2, dst3_22, 4, 4, 4, 4, BORDER_CONSTANT, 0);
merge_dst3.push_back(dst3_0);
merge_dst3.push_back(dst3_11);
merge_dst3.push_back(dst3_22);
merge(merge_dst3, dst);
}
void getAllFeatureData(MCLC & mclc, int id, vector<PairImg> &data, string path)
{
vector<PairImg>::iterator itor;
itor = data.begin();
//int length = data.size();
DataPrepareUtil dpu;
int i = 0;
for (itor; itor != data.end();)
{
i++;
if (i % 1000 == 0)
{
cout << "extractFeature is:" << i << endl;
}
vector<float> feature1;
vector<float> feature2;
string img_path1 = path + (*itor).filename1;
if (_access(img_path1.c_str(), 0) == -1)
{
cout << "coun't found filename" << img_path1 << endl;
data.erase(itor);
continue;
}
string img_path2 = path + (*itor).filename2;
if (_access(img_path2.c_str(), 0) == -1)
{
cout << "coun't found filename" << img_path1 << endl;
data.erase(itor);
continue;
}
Mat img1 = imread(img_path1, CV_LOAD_IMAGE_COLOR);
Mat img2 = imread(img_path2, CV_LOAD_IMAGE_COLOR);
Mat dst1, dst2;
//getFeatureImg(dpu, img1, dst1);
//getFeatureImg(dpu, img2, dst2);
getSplitLbpFeature(dpu, img1, dst1);
getSplitLbpFeature(dpu, img2, dst2);
//cout << "img2.path:" << path + data[i].filename2 << endl;
//cout <<img1.rows<<" "<<img1.cols<<" " <<img1.channels() << endl;
//cout << img2.rows << " " << img2.cols << " " << img2.channels() << endl;
//imshow("img", img1);
//waitKey(0);
extractFeature(mclc, id, dst1, FEATUREDIM, feature1);
extractFeature(mclc, id, dst2, FEATUREDIM, feature2);
//data[i].feature1.insert(data[i].feature1.end(), feature1.begin(), feature1.end());
//data[i].feature2.insert(data[i].feature2.end(), feature2.begin(), feature2.end());
(*itor).feature1 = feature1;
(*itor).feature2 = feature2;
itor++;
}
}
void splitString(const string& s, vector<string>& v, const string& c)
{
string::size_type pos1, pos2;
pos2 = s.find(c);
pos1 = 0;
while (string::npos != pos2)
{
v.push_back(s.substr(pos1, pos2 - pos1));
pos1 = pos2 + c.size();
pos2 = s.find(c, pos1);
}
if (pos1 != s.length()) {
v.push_back(s.substr(pos1));
}
}
void parseData(string buf, PairImg& mark)
{
vector<string> result;
splitString(buf, result, "\t");
if (result.size() == 3)
{
mark.isPair = true;
mark.filename1 = result[0] + "\\" + result[0] + "_" + ((result[1].size() == 1) ? "000" : "00") + result[1] + ".jpg";
mark.filename2 = result[0] + "\\" + result[0] + "_" + ((result[2].size() == 1) ? "000" : "00") + result[2] + ".jpg";
}
else if (result.size() == 4)
{
mark.isPair = false;
mark.filename1 = result[0] + "\\" + result[0] + "_" + ((result[1].size() == 1) ? "000" : "00") + result[1] + ".jpg";
mark.filename2 = result[2] + "\\" + result[2] + "_" + ((result[3].size() == 1) ? "000" : "00") + result[3] + ".jpg";
}
}
void readData(string filePath, vector<PairImg> & result, int count = INT_MAX)
{
ifstream fileA(filePath);
if (!fileA)
{
cout << "没有找到需要读取的 " << filePath << " 请将文件放到指定位置再次运行本程序。" << endl << " 按任意键以退出";
return;
}
for (int i = 0; !fileA.eof() && (i < count); i++)
{
PairImg atb;
string buf;
getline(fileA, buf, '\n');
if (buf == "")
{
cout << "buf is empty." << endl;
continue;
}
parseData(buf, atb);
result.push_back(atb);
}
fileA.close();
}
void writeDatatoFile(std::string filePath, vector<PairImg> & data)
{
if (filePath == "" || data.size() == 0)
{
return;
}
ofstream pair_text;
pair_text.open(filePath, ios::app);
int length = data.size();
for (int i = 0; i < length; i++)
{
string line = to_string((int)data[i].isPair);
line.append(" ");
line.append(to_string(data[i].distanceL2));
line.append(" ");
line.append(to_string(data[i].distanceL1));
line.append(" ");
line.append(to_string(data[i].distanceCos));
pair_text << line << "\n";
}
pair_text.close();
}
void getDistance(vector<PairImg> &data)
{
int length = data.size();
for (int i = 0; i < length; i++)
{
float distanceL2 = 0, distanceL1 = 0, distanceCos = 0;
float nor_feature1 = 0, nor_feature2 = 0;
for (int j = 0; j < FEATUREDIM; j++)
{
distanceL2 += pow((data[i].feature1[j] - data[i].feature2[j]), 2);
distanceL1 += abs(data[i].feature1[j] - data[i].feature2[j]);
distanceCos += data[i].feature1[j] * data[i].feature2[j];
nor_feature1 += pow(data[i].feature1[j], 2);
nor_feature2 += pow(data[i].feature2[j], 2);
}
data[i].distanceL2 = sqrt(distanceL2) / FEATUREDIM;
data[i].distanceL1 = distanceL1 / FEATUREDIM;
data[i].distanceCos = distanceCos / (sqrt(nor_feature1)*sqrt(nor_feature2));
}
}
void readPairDistanceData(string pair_dis, vector<PairDistance> &data1, vector<PairDistance> &data2)
{
ifstream fileA(pair_dis);
if (!fileA)
{
cout << "没有找到需要读取的 " << pair_dis << " 请将文件放到指定位置再次运行本程序。" << endl << " 按任意键以退出";
return;
}
for (int i = 0; !fileA.eof(); i++)
{
PairDistance dis1;
PairDistance dis2;
string buf;
getline(fileA, buf, '\n');
if (buf == "")
{
cout << "buf is empty." << endl;
continue;
}
vector<string> result1;
splitString(buf, result1, " ");
if (result1[0] == "1")
{
dis1.distanceL2 = stringToNum<float>(result1[1]);
dis1.distanceL1 = stringToNum<float>(result1[2]);
dis1.distanceCos = stringToNum<float>(result1[3]);
data1.push_back(dis1);
}
else if (result1[0] == "0")
{
dis2.distanceL2 = stringToNum<float>(result1[1]);
dis2.distanceL1 = stringToNum<float>(result1[2]);
dis2.distanceCos = stringToNum<float>(result1[3]);
data2.push_back(dis2);
}
}
fileA.close();
}
void getMean(vector<PairDistance> &data1, vector<PairDistance> &data2, float mean1[], float mean2[])
{
int length1 = data1.size();
int length2 = data2.size();
float mean1_L2 = 0, mean1_L1 = 0, mean1_Cos = 0;
float mean2_L2 = 0, mean2_L1 = 0, mean2_Cos = 0;
for (int i = 0; i < length1; i++)
{
mean1_L2 += data1[i].distanceL2;
mean1_L1 += data1[i].distanceL1;
mean1_Cos += data1[i].distanceCos;
}
mean1_L2 = mean1_L2 / length1;
mean1_L1 = mean1_L1 / length1;
mean1_Cos = mean1_Cos / length1;
mean1[0] = mean1_L2;
mean1[1] = mean1_L1;
mean1[2] = mean1_Cos;
for (int i = 0; i < length2; i++)
{
mean2_L2 += data2[i].distanceL2;
mean2_L1 += data2[i].distanceL1;
mean2_Cos += data2[i].distanceCos;
}
mean2_L2 = mean2_L2 / length2;
mean2_L1 = mean2_L1 / length2;
mean2_Cos = mean2_Cos / length2;
mean2[0] = mean2_L2;
mean2[1] = mean2_L1;
mean2[2] = mean2_Cos;
cout << "mean1 is:" << mean1_L2 << " " << mean1_L1 << " " << mean1_Cos << endl;
cout << "mean2 is:" << mean2_L2 << " " << mean2_L1 << " " << mean2_Cos << endl;
}
void showAverage(vector<PairDistance> &data1, vector<PairDistance> &data2, float mean1[], float mean2[])
{
float threadL2 = 0, threadL1 = 0, threadCos = 0;
threadL2 = (mean1[0] + mean2[0]) / 2;
threadL1 = (mean1[1] + mean2[1]) / 2;
threadCos = (mean1[2] + mean2[2]) / 2;
int length1 = data1.size();
int length2 = data2.size();
cout << "length1 is:" << length1 << " length2 is:" << length2 << endl;
float avg_L2 = 0, avg_L1 = 0, avg_Cos = 0;
int errorL2 = 0, errorL1 = 0, errorCos = 0;
for (int i = 0; i < length1; i++)
{
if (data1[i].distanceL2 >= threadL2)
{
errorL2++;
}
if (data1[i].distanceL1 >= threadL1)
{
errorL1++;
}
if (data1[i].distanceCos <= threadCos)
{
errorCos++;
}
}
for (int i = 0; i < length2; i++)
{
if (data2[i].distanceL2 <= threadL2)
{
errorL2++;
}
if (data2[i].distanceL1 <= threadL1)
{
errorL1++;
}
if (data2[i].distanceCos >= threadCos)
{
errorCos++;
}
}
cout << "accuracy L2 is:" << (1 - (float)errorL2 / (length1 + length2)) << " with threadholdL2 is:"<< threadL2<< endl;
cout << "accuracy L1 is:" << (1 - (float)errorL1 / (length1 + length2)) << " with threadholdL1 is:" << threadL1 << endl;
cout << "accuracy Cos is:" << (1 - (float)errorCos / (length1 + length2)) << " with threadholdCos is:" << threadCos << endl;
}
string dis_text = "E:\\work\\face_recongnized\\dis_pair_2.txt";
void generate_distance()
{
//string base = "E:\\work\\other_attribution\\model\\";
//string protext = "E:\\work\\other_attribution\\model\\test_500000.prototxt";
//string model = "E:\\work\\other_attribution\\model\\save_path\\_iter_500000.caffemodel";
string base = "E:\\work\\face_recongnized\\lbp_model\\";
string protext = base + "test.prototxt";
string model = base + "save_path2\\_iter_init.caffemodel";
MCLC mclc;
int id = mclc.AddNet(protext, model, DEVICE);
string dir_img = "E:\\work\\face_recongnized\\Alignment_LFW_Equalized\\";
string text = "E:\\work\\face_recongnized\\pair.txt";
vector<PairImg> data;
readData(text, data);
getAllFeatureData(mclc, id, data, dir_img);
getDistance(data);
clearFileData(dis_text);
writeDatatoFile(dis_text, data);
}
void predict_distance()
{
vector<PairDistance>data1;
vector<PairDistance>data2;
readPairDistanceData(dis_text, data1, data2);
float mean1[3], mean2[3];
getMean(data1, data2, mean1, mean2);
showAverage(data1, data2, mean1, mean2);
}
int main()
{
generate_distance();
predict_distance();
system("PAUSE");
return 0;
}
#pragma once
#include <opencv2\opencv.hpp>
namespace glasssix
{
class DataPrepareUtil
{
public :
void getMultiScaleBlockLBPFeature(cv::Mat & src, cv::Mat & dst, int scale);
void lbp_normal(cv::Mat& src, cv::Mat &dst);
};
}
#include "util.h"
#include <iostream>
//原始LBP特征计算
using namespace glasssix;
using namespace cv;
template <typename _tp>
void getOriginLBPFeature(Mat & src, Mat & dst, int cellsize)
{
dst.create(src.rows - 2 * cellsize, src.cols - 2 * cellsize, CV_8UC1);
dst.setTo(0);
for (int i = cellsize; i<src.rows - cellsize; i = i + 1)
{
for (int j = cellsize; j<src.cols - cellsize; j = j + 1)
{
_tp center = src.at<_tp>(i, j);
unsigned char lbpCode = 0;
lbpCode |= (src.at<_tp>(i - cellsize, j - cellsize) > center) << 7;
lbpCode |= (src.at<_tp>(i - cellsize, j) > center) << 6;
lbpCode |= (src.at<_tp>(i - cellsize, j + cellsize) > center) << 5;
lbpCode |= (src.at<_tp>(i, j + cellsize) > center) << 4;
lbpCode |= (src.at<_tp>(i + cellsize, j + cellsize) > center) << 3;
lbpCode |= (src.at<_tp>(i + cellsize, j) > center) << 2;
lbpCode |= (src.at<_tp>(i + cellsize, j - cellsize) > center) << 1;
lbpCode |= (src.at<_tp>(i, j - cellsize) > center) << 0;
dst.at<uchar>(i - cellsize, j - cellsize) = lbpCode;
}
}
}
//MB-LBP特征的计算
void DataPrepareUtil::getMultiScaleBlockLBPFeature(Mat & src, Mat & dst, int scale)
{
//定义并计算积分图像
int cellSize = scale / 3;
int offset = cellSize / 2;
Mat cellImage(src.rows - 2 * offset, src.cols - 2 * offset, CV_8UC1);
for (int i = offset; i<src.rows - offset; i++)
{
for (int j = offset; j<src.cols - offset; j++)
{
int temp = 0;
for (int m = -offset; m<offset + 1; m++)
{
for (int n = -offset; n<offset + 1; n++)
{
temp += src.at<uchar>(i + n, j + m);
}
}
temp /= (cellSize*cellSize);
cellImage.at<uchar>(i - cellSize / 2, j - cellSize / 2) = uchar(temp);
}
}
getOriginLBPFeature<uchar>(cellImage, dst, cellSize);
}
void DataPrepareUtil::lbp_normal(Mat& src, Mat &dst)
{
dst.create(src.rows - 2, src.cols - 2, CV_8UC1);
// 循环处理图像数据
for (int i = 1; i < src.rows - 1; i++)
{
for (int j = 1; j < src.cols - 1; j++)
{
uchar tt = 0;
int tt1 = 0;
uchar u = src.at<uchar>(i, j);
if (src.at<uchar>(i - 1, j - 1) > u) { tt += 1 << tt1; }
tt1++;
if (src.at<uchar>(i - 1, j) > u) { tt += 1 << tt1; }
tt1++;
if (src.at<uchar>(i - 1, j + 1) > u) { tt += 1 << tt1; }
tt1++;
if (src.at<uchar>(i, j + 1) > u) { tt += 1 << tt1; }
tt1++;
if (src.at<uchar>(i + 1, j + 1) > u) { tt += 1 << tt1; }
tt1++;
if (src.at<uchar>(i + 1, j) > u) { tt += 1 << tt1; }
tt1++;
if (src.at<uchar>(i + 1, j - 1) > u) { tt += 1 << tt1; }
tt1++;
if (src.at<uchar>(i - 1, j) > u) { tt += 1 << tt1; }
tt1++;
dst.at<uchar>(i - 1, j - 1) = tt;
}
}
}