1. 程式人生 > >用opencv做的靜態圖片人臉識別

用opencv做的靜態圖片人臉識別

兩張 msh pda 跟蹤 截取 一位 深度 https poi

  這次給大家分享一個圖像識別方面的小項目,主要功能是識別圖像中的人臉並根據人臉在圖片庫找出同一個與它最相似的圖片,也就是辨別不同的人。

  環境: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();                    //吃掉回車符
}

下面是我的圖片庫:

技術分享圖片

效果:

技術分享圖片

不足:大家可以看到,檢測圖片中的人臉時,把旁邊的也識別成人臉了,還有就是圖片多的時候,識別效果會很差,所以說實用性不強,歡迎交流。下次嘗試用深度學習來做。

用opencv做的靜態圖片人臉識別