这次给大家分享一个图像识别方面的小项目,主要功能是识别图像中的人脸并根据人脸在图片库找出同一个与它最相似的图片,也就是辨别不同的人。
环境:VS2013+opencv2.4.13
主要是算法:opencv中人脸识别算法(截取人脸)+哈希算法(辨别人脸)
opencv中人脸识别算法:这个很常用,就是普通的人脸识别算法,直接上代码:
void IdentifyFace(Mat image) //识别并截取人脸 { CascadeClassifier ccf; ccf.load(xmlPath); vector<Rect> faces; Mat gray; cvtColor(image, gray, CV_BGR2GRAY); equalizeHist(gray, gray); //直方图均匀化 ccf.detectMultiScale(gray, faces, 1.1, 3, 0, Size(50, 50), Size(500, 500)); //检测人脸 for (vector<Rect>::const_iterator iter = faces.begin(); iter != faces.end(); iter++) { rectangle(image, *iter, Scalar(0, 0, 255), 2, 8); //画出脸部矩形 } for (size_t i = 0; i<faces.size(); i++) { Point center(faces[i].x + faces[i].width / 2, faces[i].y + faces[i].height / 2); image = image(Rect(faces[i].x, faces[i].y, faces[i].width, faces[i].height)); } }
哈希算法:主要是用来视觉目标跟踪,主要的思路如下:
(1)缩小尺寸:pHash以小图片开始,但图片大于8*8,32*32是最好的。这样做的目的是简化了DCT的计算,而不是减小频率。
(2)简化色彩:将图片转化成灰度图像,进一步简化计算量。
(3)计算DCT:计算图片的DCT变换,得到32*32的DCT系数矩阵。
(4)缩小DCT:虽然DCT的结果是32*32大小的矩阵,但我们只要保留左上角的8*8的矩阵,这部分呈现了图片中的最低频率。
(5)计算平均值:如同均值哈希一样,计算DCT的均值。
(6)计算hash值:这是最主要的一步,根据8*8的DCT矩阵,设置0或1的64位的hash值,大于等于DCT均值的设为”1”,小于DCT均值的设为“0”。组合在一起,就构成了一个64位的整数,这就是这张图片的指纹。
结果并不能告诉我们真实性的低频率,只能粗略地告诉我们相对于平均值频率的相对比例。只要图片的整体结构保持不变,hash结果值就不变。能够避免伽马校正或颜色直方图被调整带来的影响。
pHash同样可以用汉明距离来进行比较。(只需要比较每一位对应的位置并算计不同的位的个数)
代码:
string calHashValue(Mat &src) //得到图片的哈希值 { string zeroonestr(64, ‘\0‘); //定义一个字符串并初始化 Mat origianlImage, dctImage; //定义几个矩阵 Mat floatImage, imageDct; if (src.channels() == 3) //判断通道数量 cvtColor(src, origianlImage, CV_BGR2GRAY); //转换格式 else origianlImage = src.clone(); //不用转换 resize(origianlImage, origianlImage, Size(32, 32)); //缩小图片尺寸为32*32 origianlImage.convertTo(floatImage, CV_32FC1); //转换类型 dct(floatImage, imageDct); //进行DCT运算(离散余弦变换) Rect roi(0, 0, 8, 8); //定义一个8*8的矩阵 dctImage = imageDct(roi); //将8*8的矩阵赋给dctImage uchar *pData; for (int i = 0; i<dctImage.rows; i++) { pData = dctImage.ptr<uchar>(i); for (int j = 0; j<dctImage.cols; j++) { pData[j] = pData[j] / 4; } } int average = mean(dctImage).val[0];//求平均值 Mat mask = (dctImage >= (uchar)average); //与平均值进行比较 int index = 0; //用于计数 for (int i = 0; i<mask.rows; i++) { pData = mask.ptr<uchar>(i); for (int j = 0; j<mask.cols; j++) { if (pData[j] == 0) zeroonestr[index++] = ‘0‘; else zeroonestr[index++] = ‘1‘; } } return zeroonestr; //返回一个64位的全是0或1的字符串 }
计算两幅图片汉明距离代码
int calHanmingDistance(string &str1, string &str2) //求两张图片的海明距离 { if ((str1.size() != 64) || (str2.size() != 64)) return -1; int countstoreHamingdistance = 0; for (int i = 0; i<64; i++) { if (str1[i] != str2[i]) countstoreHamingdistance++; } return countstoreHamingdistance; }
整个工程代码:
#include <iostream> #include <string> #include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp> #include <opencv2/gpu/gpu.hpp> using namespace cv; using namespace std; string xmlPath = "E:\\VS2013\\openCV\\opencv\\build\\share\\OpenCV\\haarcascades\\haarcascade_frontalface_default.xml"; //这个路径要根据你电脑的位置来设置 const int ImageSumNumber = 10; //10张图片,图片越多越难正确识别 string calHashValue(Mat &src) //得到图片的哈希值 { string zeroonestr(64, ‘\0‘); //定义一个字符串并初始化 Mat origianlImage, dctImage; //定义几个矩阵 Mat floatImage, imageDct; if (src.channels() == 3) //判断通道数量 cvtColor(src, origianlImage, CV_BGR2GRAY); //转换格式 else origianlImage = src.clone(); //不用转换 resize(origianlImage, origianlImage, Size(32, 32)); //缩小图片尺寸为32*32 origianlImage.convertTo(floatImage, CV_32FC1); //转换类型 dct(floatImage, imageDct); //进行DCT运算(离散余弦变换) Rect roi(0, 0, 8, 8); //定义一个8*8的矩阵 dctImage = imageDct(roi); //将8*8的矩阵赋给dctImage uchar *pData; for (int i = 0; i<dctImage.rows; i++) { pData = dctImage.ptr<uchar>(i); for (int j = 0; j<dctImage.cols; j++) { pData[j] = pData[j] / 4; } } int average = mean(dctImage).val[0];//求平均值 Mat mask = (dctImage >= (uchar)average); //与平均值进行比较 int index = 0; //用于计数 for (int i = 0; i<mask.rows; i++) { pData = mask.ptr<uchar>(i); for (int j = 0; j<mask.cols; j++) { if (pData[j] == 0) zeroonestr[index++] = ‘0‘; else zeroonestr[index++] = ‘1‘; } } return zeroonestr; //返回一个64位的全是0或1的字符串 } int calHanmingDistance(string &str1, string &str2) //求两张图片的海明距离 { if ((str1.size() != 64) || (str2.size() != 64)) return -1; int countstoreHamingdistance = 0; for (int i = 0; i<64; i++) { if (str1[i] != str2[i]) countstoreHamingdistance++; } return countstoreHamingdistance; } void IdentifyFace(Mat image) //识别并截取人脸 { CascadeClassifier ccf; ccf.load(xmlPath); vector<Rect> faces; Mat gray; cvtColor(image, gray, CV_BGR2GRAY); equalizeHist(gray, gray); //直方图均匀化 ccf.detectMultiScale(gray, faces, 1.1, 3, 0, Size(50, 50), Size(500, 500)); //检测人脸 for (vector<Rect>::const_iterator iter = faces.begin(); iter != faces.end(); iter++) { rectangle(image, *iter, Scalar(0, 0, 255), 2, 8); //画出脸部矩形 } for (size_t i = 0; i<faces.size(); i++) { Point center(faces[i].x + faces[i].width / 2, faces[i].y + faces[i].height / 2); image = image(Rect(faces[i].x, faces[i].y, faces[i].width, faces[i].height)); } } void DrawFaceRectangl(Mat image) //检测图片中的人脸,并用矩形画出 { CascadeClassifier ccf; ccf.load(xmlPath); vector<Rect> faces; Mat gray; cvtColor(image, gray, CV_BGR2GRAY); equalizeHist(gray, gray); //直方图均匀化 ccf.detectMultiScale(gray, faces, 1.1, 3, 0, Size(50, 50), Size(500, 500)); //检测人脸 for (vector<Rect>::const_iterator iter = faces.begin(); iter != faces.end(); iter++) { rectangle(image, *iter, Scalar(0, 0, 255), 2, 8); //画出脸部矩形 } imshow("图片中的人脸检测", image); } int main(int argc, char** argv) { cout << "请输入想要选择的图片" << endl; int referencepicture; //用于参考的图片 int ImageNum; //用于遍历的计数变量 int storeHamingdistance[9]; //用于储存图片的海明距离 cin >> referencepicture; //输入参考图片的编号 const string path1 = format("H:\\picture\\opencv\\jackchen\\%d.jpg", referencepicture);//获取参考图片的路径 //这个路径要根据你电脑的位置来设置 Mat originalImage, processImage, faceImage; originalImage = imread(path1, -1); //获取图片 faceImage = imread(path1, -1); //获取图片 string str1, str2, path2; //定义几个字符串 cvNamedWindow("选择的图片", 1); /*cvResizeWindow("选择的图片",700,500);*/ imshow("选择的图片", originalImage); DrawFaceRectangl(faceImage); //截取人脸,并显示出来 IdentifyFace(originalImage); //识别并截取人脸 str1 = calHashValue(originalImage); //求出汉明距离 cvWaitKey(300); for (ImageNum = 1; ImageNum <= ImageSumNumber; ImageNum++)//因为我完成的就是8张图片的检测,所以循环值为8 { path2 = format("H:\\picture\\opencv\\jackchen\\%d.jpg", ImageNum); //这个路径要根据你电脑的位置来设置 processImage = imread(path2, -1); IdentifyFace(processImage); str2 = calHashValue(processImage); storeHamingdistance[ImageNum] = calHanmingDistance(str1, str2); } int min = 65; //这个自己确定,大于64就可以了 int storemostsimilarImage; //用于储存最相似的图片的编号 for (ImageNum = 1; ImageNum <= ImageSumNumber; ImageNum++) //循环值为8,求与原图片汉明距离最小的那张图片 { if (storeHamingdistance[ImageNum]<min && storeHamingdistance[ImageNum] != 0) { min = storeHamingdistance[ImageNum]; storemostsimilarImage = ImageNum; //检测出的标记为t } } path2 = format("H:\\picture\\opencv\\jackchen\\%d.jpg", storemostsimilarImage); //这个路径要根据你电脑的位置来设置 processImage = imread(path2, -1);//将最相似的图片显示出来 cvNamedWindow("相似的图片", 1); imshow("相似的图片", processImage);//这时显示的就是最相似的照片 cvWaitKey(0); cin.get(); //吃掉回车符 }
下面是我的图片库:
效果:
不足:大家可以看到,检测图片中的人脸时,把旁边的也识别成人脸了,还有就是图片多的时候,识别效果会很差,所以说实用性不强,欢迎交流。下次尝试用深度学习来做。