1. 程式人生 > >人臉識別經典演算法實現(三)——LBP演算法

人臉識別經典演算法實現(三)——LBP演算法

第三種演算法稱之為LBP演算法,這個演算法的思路與PCA和Fisher有很大不同,他是考慮區域性特徵運算元,並不是全域性考慮。

這種演算法定義了一種LBP特徵,這種特徵與我們經常見到的Haar特徵、HoG特徵沒有啥太大不同,都是特徵運算元,只是演算法不同。因此,我們按照理解特徵運算元一類的演算法去理解LBP就可以了。

注意,LBP對關照不敏感,為什麼?因為LBP運算元是一種相對性質的數量關係,相比於PCA或者Fsiher,直接使用灰度值去參與運算,LBP更能反映出的是一種變化趨勢。

最後一次當個搬運工

http://blog.csdn.net/feirose/article/details/39552977,LBP運算元寫的不算太清楚,但是整個演算法的完整流程講明白了,而且最後如何判定的直方圖交叉核和卡方檢驗都有說明。

http://blog.csdn.net/pi9nc/article/details/18623971,這個部落格的LBP運算元說得很好,而且有opencv的例程。

注意,這裡的樣本影象組織形式與前面兩個演算法又有不同,因為他不需要把影象變成列向量,因此影象矩陣不需要做什麼處理就可以加入列表備用了。

程式碼如下:

#encoding=utf-8
import numpy as np
import os
import cv2

class LBP(object):
    def __init__(self,threshold,dsize,blockNum):
        self.dsize = dsize # 統一尺寸大小
        self.blockNum = blockNum # 分割塊數目
        self.threshold = threshold # 閾值,暫未使用

    def loadImg(self,fileName,dsize):
        '''
        載入影象,灰度化處理,統一尺寸,直方圖均衡化
        :param fileName: 影象檔名
        :param dsize: 統一尺寸大小。元組形式
        :return: 影象矩陣
        '''
        img = cv2.imread(fileName)
        retImg = cv2.resize(img,dsize)
        retImg = cv2.cvtColor(retImg,cv2.COLOR_RGB2GRAY)
        retImg = cv2.equalizeHist(retImg)
        # cv2.imshow('img',retImg)
        # cv2.waitKey()
        return retImg

    def loadImagesList(self,dirName):
        '''
        載入影象矩陣列表
        :param dirName:資料夾路徑
        :return: 包含最原始的影象矩陣的列表和標籤矩陣
        '''
        imgList = []
        label = []
        for parent,dirnames,filenames in os.walk(dirName):
            # print parent
            # print dirnames
            # print filenames
            for dirname in dirnames:
                for subParent,subDirName,subFilenames in os.walk(parent+'/'+dirname):
                    for filename in subFilenames:
                        img = self.loadImg(subParent+'/'+filename,self.dsize)
                        imgList.append(img) # 原始影象矩陣不做任何處理,直接加入列表
                        label.append(subParent+'/'+filename)
        return imgList,label


    def getHopCounter(self,num):
        '''
        計算二進位制序列是否只變化兩次
        :param num: 數字
        :return: 01變化次數
        '''
        binNum = bin(num)
        binStr = str(binNum)[2:]
        n = len(binStr)
        if n < 8:
            binStr = "0"*(8-n)+binStr
        n = len(binStr)
        counter = 0
        for i in range(n):
            if i != n-1:
                if binStr[i+1] != binStr[i]:
                    counter += 1
            else:
                if binStr[0] != binStr[i]:
                    counter += 1
        return counter

    def createTable(self):
        '''
        生成均勻對應字典
        :return: 均勻LBP特徵對應字典
        '''
        self.table = {}
        temp = 1
        print type(temp)
        for i in range(256):
            if self.getHopCounter(i) <= 2:
                self.table[i] = temp
                temp += 1
            else:
                self.table[i] = 0
        return self.table

    def getLBPfeature(self,img):
        '''
        計算LBP特徵
        :param img:影象矩陣
        :return: LBP特徵圖
        '''
        m = img.shape[0];n = img.shape[1]
        neighbor = [0]*8
        featureMap = np.mat(np.zeros((m,n)))
        for y in xrange(1,m-1):
            for x in xrange(1,n-1):
                neighbor[0] = img[y-1,x-1]
                neighbor[1] = img[y-1,x]
                neighbor[2] = img[y-1,x+1]
                neighbor[3] = img[y,x+1]
                neighbor[4] = img[y+1,x+1]
                neighbor[5] = img[y+1,x]
                neighbor[6] = img[y+1,x-1]
                neighbor[7] = img[y,x-1]
                center = img[y,x]
                temp = 0
                for k in range(8):
                    temp += (neighbor[k] >= center)*(1<<k)
                featureMap[y,x] = self.table[temp]
        featureMap = featureMap.astype('uint8') # 資料型別轉換為無符號8位型,如不轉換則預設為float64位,影響最終效果
        return featureMap

    def calcHist(self,roi):
        '''
        計算直方圖
        :param roi:影象區域
        :return: 直方圖矩陣
        '''
        hist = cv2.calcHist([roi],[0],None,[59],[0,256]) # 第四個引數是直方圖的橫座標數目,經過均勻化降維後這裡一共有59種畫素
        return hist

    def compare(self,sampleImg,testImg):
        '''
        比較函式,這裡使用的是歐氏距離排序,也可以使用KNN,在此處更改
        :param sampleImg: 樣本影象矩陣
        :param testImg: 測試影象矩陣
        :return: k2值
        '''
        testImg = cv2.resize(testImg,self.dsize)
        testImg = cv2.cvtColor(testImg,cv2.COLOR_RGB2GRAY)
        testFeatureMap = self.getLBPfeature(testImg)
        sampleFeatureMap = self.getLBPfeature(sampleImg)
        # 計算步長,分割整個影象為小塊
        ystep = self.dsize[0]/self.blockNum
        xstep = self.dsize[1]/self.blockNum
        k2 = 0
        for y in xrange(0,self.dsize[0],ystep):
            for x in xrange(0,self.dsize[1],xstep):
                testroi = testFeatureMap[y:y+ystep,x:x+xstep]
                sampleroi =sampleFeatureMap[y:y+ystep,x:x+xstep]
                testHist = self.calcHist(testroi)
                sampleHist = self.calcHist(sampleroi)
                k2 += np.sum((sampleHist-testHist)**2)/np.sum((sampleHist+testHist))
        print 'k2的值為',k2
        return k2

    def predict(self,dirName,testImgName):
        '''
        預測函式
        :param dirName:樣本影象資料夾路徑
        :param testImgName: 測試影象檔名
        :return: 最相近影象名稱
        '''
        table = self.createTable()
        testImg = cv2.imread(testImgName)
        imgList,label = self.loadImagesList(dirName)
        k2List = []
        for img in imgList:
            k2 = self.compare(img,testImg)
            k2List.append(k2)
        order = np.argsort(k2List)
        return label[order[0]]

if __name__ == "__main__":

    lbp = LBP(20,(50,50),5)
    ans = lbp.predict('d:/face','d:/face_test/9.bmp')
    print ans