LBP特徵提取+SVM二分類器 判定是或不是

參考博客:LBP特徵的實現及LBP+SVM分類

運行環境:ubuntu18.04+opencv3.2.0   識別速度3.87ms/張

LBP特徵提取網上很多,包括其他Haar特徵什麼的,SVM級聯分類器網上也有介紹訓練和使用的博客,這裏用的是簡單的二分類,即是或不是。

 

直接複製粘貼代碼吧,別的也沒有用,原理我也不懂,我只是完成對攝像頭誤拍的非人圖像進行去除,背景大概是這樣的。

 

智能攝像頭對場景進行智能人臉檢測,但是會有概率誤檢,比如錯誤把某動物或者某植物檢測爲人臉,然後拍照並顯示到大屏幕上,這樣效果很不好。哪怕沒拍到人臉,是個後腦勺或者半個身子,但是隻要主體是人還能圓過去,弄個動物或者植物這種風馬牛不相及的照片上屏根本無力解釋,只能凸出算法效果不好,但是提升人臉檢測算法難度較大,就想到在AI檢測後做一個SVM分類器,判定拍攝圖片是否是人或不是人,把不是人的去掉,是人的才顯示到屏幕上。

 

基本流程:挑選正負樣本圖片集 >> 讀取正負樣本並貼標籤 >>提取LBP特徵 >> 訓練xml模型 >> 加載模型識別圖片

 

整體實現的基本流程就是這樣很簡單,但這裏需要特別提醒,就是最開始的挑選正負樣本圖片集這一步纔是最關鍵的,其他的LBP特徵提取理不理解原理都不影響從網上直接貼源碼調用,中間的函數調用也是常用的,也就是說如果你的代碼編譯或者運行不正確,會報錯,如果代碼沒報錯,能夠正常運行,基本上就是對的,最終識別效果不好9.9成跟數據集有關係。

 

我解釋一下原因,上面說過背景是把人和非人的圖片分開,對AI攝像頭誤檢照片進行二次篩選,那麼我訓練的xml分類器要分類的所有照片,其實本質上都是我的AI經過層層計算最終的評定爲“是人”的結果,也就是我們看到的“非人”和“人”在AI那都是符合“人”的特徵的,不然攝像頭本身就不會拍攝;比如我用svm級聯分類器提取haar特徵去檢測誤檢的“盆栽”,顯示結果就是把葉子部分識別爲人臉,甚至你對檢測出來的葉子的區域做haar特徵的人眼檢測,依然能夠檢測到符合人眼的特徵,所以結論就是,所有AI攝像頭輸出的圖片,對AI來說,都是符合人的特徵的。

 

這裏講既然都符合人的特徵,我怎麼判定區分是人或不是人?

首先是AI攝像頭本身的誤檢率就只有10%不到,如果誤檢率太高,應該先提升算法精確度,也就是AI自身能夠識別出絕大多數“非人”的情況,只有在某些特定條件下纔會被誤導拍攝,這些誤檢的圖片都應該包含這些特殊信息,否則AI不會只拍攝這些圖片,畢竟更多的時間場景是不變的,要真認爲盆栽是人,會一直盯着盆栽一直拍,所以真相就是當光線變化或者有風等等特殊條件的存在時,誤導了AI,那麼誤導AI的10%的圖像都必然有同一特點,把這些特點提取出來,用xml分類器t貼上"非人"標籤過濾出去就好。

 

上面說了一大堆,總結就是訓練的正負樣本集一定要從AI檢測後拍攝的圖像中來,私自添加其他數據集的,會導致xml二分類識別準確率下降,原因就是其他數據集的特徵是你人眼看的,不是AI自己檢測過濾的,私自添加的特徵不符合我的AI對人的檢測標準。比如我們通過人眼識別的"非人"照片裏存在AI檢測爲人的“特徵”,分類器把這個特徵貼上“非人”的標籤,在實際分類的時候,會將原來AI通過該特徵檢測爲人的圖片判定爲“非人”,降低準確率。

 

至於用Haar還是LBP特徵提取,或者其他什麼的,都隨便,因爲二分類物體判定是或不是,本身就是特徵差異特別大的纔會這麼簡單暴力區分,要時刻謹記,檢測的數據來源是AI,我們只處理AI誤檢的,影響AI檢測的特殊條件特徵都會集中在AI的誤檢照片裏,不需要考慮更換場景後xml分類器是否試用,換個場景AI依然能夠有90%以上的正確識別率,如果現在的xml效果差了,就逐步添加AI不同場景下的誤檢樣本就可以。

 

LBP.h

//////////////////////////////////////////////////////////////////////////
//  LBP.h (2.0)
// 2015-6-30,by QQ
//
// Please contact me if you find any bugs, or have any suggestions.
// Contact:
//		Telephone:17761745857
//		Email:[email protected]
//		Blog: http://blog.csdn.net/qianqing13579
//////////////////////////////////////////////////////////////////////////
// updated 2016-12-12 01:12:55 by QQ, LBP 1.1,GetMinBinary()函數修改爲查找表,提高了計算速度
// updated 2016-12-13 14:41:58 by QQ, LBP 2.0,先計算整幅圖像的LBP特徵圖,然後計算每個cell的LBP直方圖

#ifndef __LBP_H__
#define __LBP_H__
#include "opencv2/opencv.hpp"
#include<vector>
using namespace std;
using namespace cv;

class LBP
{
public:

	// 計算基本的256維LBP特徵向量
	void ComputeLBPFeatureVector_256(const Mat &srcImage, Size cellSize, Mat &featureVector);
	void ComputeLBPImage_256(const Mat &srcImage, Mat &LBPImage);// 計算256維LBP特徵圖

	// 計算灰度不變+等價模式LBP特徵向量(58種模式)
	void ComputeLBPFeatureVector_Uniform(const Mat &srcImage, Size cellSize, Mat &featureVector);
	void ComputeLBPImage_Uniform(const Mat &srcImage, Mat &LBPImage);// 計算等價模式LBP特徵圖

	// 計算灰度不變+旋轉不變+等價模式LBP特徵向量(9種模式)
	void ComputeLBPFeatureVector_Rotation_Uniform(const Mat &srcImage, Size cellSize, Mat &featureVector);
	void ComputeLBPImage_Rotation_Uniform(const Mat &srcImage, Mat &LBPImage); // 計算灰度不變+旋轉不變+等價模式LBP特徵圖,使用查找表

	// Test
	void Test();// 測試灰度不變+旋轉不變+等價模式LBP
	void TestGetMinBinaryLUT();

private:
	void BuildUniformPatternTable(int *table); // 計算等價模式查找表
	int GetHopCount(int i);// 獲取i中0,1的跳變次數

	void ComputeLBPImage_Rotation_Uniform_2(const Mat &srcImage, Mat &LBPImage);// 計算灰度不變+旋轉不變+等價模式LBP特徵圖,不使用查找表
	int ComputeValue9(int value58);// 計算9種等價模式
	int GetMinBinary(int binary);// 通過LUT計算最小二進制
	uchar GetMinBinary(uchar *binary); // 計算得到最小二進制

};

#endif

LBP.cpp

#include"LBP.h"

//獲取i中0,1的跳變次數
int LBP::GetHopCount(int i)
{
	// 轉換爲二進制
	int a[8] = { 0 };
	int k = 7;
	while (i)
	{
		// 除2取餘
		a[k] = i % 2;
		i /= 2;
		--k;
	}

	// 計算跳變次數
	int count = 0;
	for (int k = 0; k < 8; ++k)
	{
		// 注意,是循環二進制,所以需要判斷是否爲8
		if (a[k] != a[k + 1 == 8 ? 0 : k + 1])
		{
			++count;
		}
	}
	return count;

}

// 建立等價模式表
// 這裏爲了便於建立LBP特徵圖,58種等價模式序號從1開始:1~58,第59類混合模式映射爲0
void LBP::BuildUniformPatternTable(int *table)
{
	memset(table, 0, 256 * sizeof(int));
	uchar temp = 1;
	for (int i = 0; i < 256; ++i)
	{
		if (GetHopCount(i) <= 2)
		{
			table[i] = temp;
			temp++;
		}
	}

}

void LBP::ComputeLBPFeatureVector_256(const Mat &srcImage, Size cellSize, Mat &featureVector)
{
	// 參數檢查,內存分配
	//CV_Assert(srcImage.depth() == CV_8U&&srcImage.channels() == 1);

	Mat LBPImage;
	ComputeLBPImage_256(srcImage, LBPImage);

	// 計算cell個數
	int widthOfCell = cellSize.width;
	int heightOfCell = cellSize.height;
	int numberOfCell_X = srcImage.cols / widthOfCell;// X方向cell的個數
	int numberOfCell_Y = srcImage.rows / heightOfCell;

	// 特徵向量的個數
	int numberOfDimension = 256 * numberOfCell_X*numberOfCell_Y;
	featureVector.create(1, numberOfDimension, CV_32FC1);
	featureVector.setTo(Scalar(0));

	// 計算LBP特徵向量
	int stepOfCell = srcImage.cols;
	int pixelCount = cellSize.width*cellSize.height;
	float *dataOfFeatureVector = (float *)featureVector.data;

	// cell的特徵向量在最終特徵向量中的起始位置
	int index = -256;
	for (int y = 0; y <= numberOfCell_Y - 1; ++y)
	{
		for (int x = 0; x <= numberOfCell_X - 1; ++x)
		{
			index += 256;

			// 計算每個cell的LBP直方圖
			Mat cell = LBPImage(Rect(x * widthOfCell, y * heightOfCell, widthOfCell, heightOfCell));
			uchar *rowOfCell = cell.data;
			for (int y_Cell = 0; y_Cell <= cell.rows - 1; ++y_Cell, rowOfCell += stepOfCell)
			{
				uchar *colOfCell = rowOfCell;
				for (int x_Cell = 0; x_Cell <= cell.cols - 1; ++x_Cell, ++colOfCell)
				{
					++dataOfFeatureVector[index + colOfCell[0]];
				}
			}

			// 一定要歸一化!否則分類器計算誤差很大
			for (int i = 0; i <= 255; ++i)
				dataOfFeatureVector[index + i] /= pixelCount;

		}
	}

}

//srcImage:灰度圖
//LBPImage:LBP圖
void LBP::ComputeLBPImage_256(const Mat &srcImage, Mat &LBPImage)
{
	// 參數檢查,內存分配
	//CV_Assert(srcImage.depth() == CV_8U&&srcImage.channels() == 1);
	LBPImage.create(srcImage.size(), srcImage.type());

	// 擴充原圖像邊界,便於邊界處理
	Mat extendedImage;
	copyMakeBorder(srcImage, extendedImage, 1, 1, 1, 1, BORDER_DEFAULT);

	// 計算LBP特徵圖
	int heightOfExtendedImage = extendedImage.rows;
	int widthOfExtendedImage = extendedImage.cols;
	int widthOfLBP = LBPImage.cols;
	uchar *rowOfExtendedImage = extendedImage.data + widthOfExtendedImage + 1;
	uchar *rowOfLBPImage = LBPImage.data;
	for (int y = 1; y <= heightOfExtendedImage - 2; ++y, rowOfExtendedImage += widthOfExtendedImage, rowOfLBPImage += widthOfLBP)
	{
		// 列
		uchar *colOfExtendedImage = rowOfExtendedImage;
		uchar *colOfLBPImage = rowOfLBPImage;
		for (int x = 1; x <= widthOfExtendedImage - 2; ++x, ++colOfExtendedImage, ++colOfLBPImage)
		{
			// 計算LBP值
			int LBPValue = 0;
			if (colOfExtendedImage[0 - widthOfExtendedImage - 1] >= colOfExtendedImage[0])
				LBPValue += 128;
			if (colOfExtendedImage[0 - widthOfExtendedImage] >= colOfExtendedImage[0])
				LBPValue += 64;
			if (colOfExtendedImage[0 - widthOfExtendedImage + 1] >= colOfExtendedImage[0])
				LBPValue += 32;
			if (colOfExtendedImage[0 + 1] >= colOfExtendedImage[0])
				LBPValue += 16;
			if (colOfExtendedImage[0 + widthOfExtendedImage + 1] >= colOfExtendedImage[0])
				LBPValue += 8;
			if (colOfExtendedImage[0 + widthOfExtendedImage] >= colOfExtendedImage[0])
				LBPValue += 4;
			if (colOfExtendedImage[0 + widthOfExtendedImage - 1] >= colOfExtendedImage[0])
				LBPValue += 2;
			if (colOfExtendedImage[0 - 1] >= colOfExtendedImage[0])
				LBPValue += 1;

			colOfLBPImage[0] = LBPValue;

		}  // x

	}// y


}

// cellSize:每個cell的大小,如16*16
void LBP::ComputeLBPFeatureVector_Uniform(const Mat &srcImage, Size cellSize, Mat &featureVector)
{
	// 參數檢查,內存分配
	//CV_Assert(srcImage.depth() == CV_8U&&srcImage.channels() == 1);

	Mat LBPImage;
	ComputeLBPImage_Uniform(srcImage, LBPImage);

	// 計算cell個數
	int widthOfCell = cellSize.width;
	int heightOfCell = cellSize.height;
	int numberOfCell_X = srcImage.cols / widthOfCell;// X方向cell的個數
	int numberOfCell_Y = srcImage.rows / heightOfCell;

	// 特徵向量的個數
	int numberOfDimension = 58 * numberOfCell_X*numberOfCell_Y;
	featureVector.create(1, numberOfDimension, CV_32FC1);
	featureVector.setTo(Scalar(0));

	// 計算LBP特徵向量
	int stepOfCell = srcImage.cols;
	int index = -58;// cell的特徵向量在最終特徵向量中的起始位置
	float *dataOfFeatureVector = (float *)featureVector.data;
	for (int y = 0; y <= numberOfCell_Y - 1; ++y)
	{
		for (int x = 0; x <= numberOfCell_X - 1; ++x)
		{
			index += 58;

			// 計算每個cell的LBP直方圖
			Mat cell = LBPImage(Rect(x * widthOfCell, y * heightOfCell, widthOfCell, heightOfCell));
			uchar *rowOfCell = cell.data;
			int sum = 0; // 每個cell的等價模式總數
			for (int y_Cell = 0; y_Cell <= cell.rows - 1; ++y_Cell, rowOfCell += stepOfCell)
			{
				uchar *colOfCell = rowOfCell;
				for (int x_Cell = 0; x_Cell <= cell.cols - 1; ++x_Cell, ++colOfCell)
				{
					if (colOfCell[0] != 0)
					{
						// 在直方圖中轉化爲0~57,所以是colOfCell[0] - 1
						++dataOfFeatureVector[index + colOfCell[0] - 1];
						++sum;
					}
				}
			}

			// 一定要歸一化!否則分類器計算誤差很大
			for (int i = 0; i <= 57; ++i)
				dataOfFeatureVector[index + i] /= sum;

		}
	}
}

// 計算等價模式LBP特徵圖,爲了方便表示特徵圖,58種等價模式表示爲1~58,第59種混合模式表示爲0
// 注:你可以將第59類混合模式映射爲任意數值,因爲要突出等價模式特徵,所以非等價模式設置爲0比較好
void LBP::ComputeLBPImage_Uniform(const Mat &srcImage, Mat &LBPImage)
{
	// 參數檢查,內存分配
	//CV_Assert(srcImage.depth() == CV_8U&&srcImage.channels() == 1);
	LBPImage.create(srcImage.size(), srcImage.type());

	// 計算LBP圖
	// 擴充原圖像邊界,便於邊界處理
	Mat extendedImage;
	copyMakeBorder(srcImage, extendedImage, 1, 1, 1, 1, BORDER_DEFAULT);

	// 構建LBP 等價模式查找表
	//int table[256];
	//BuildUniformPatternTable(table);

	// LUT(256種每一種模式對應的等價模式)
	static const int table[256] = { 1, 2, 3, 4, 5, 0, 6, 7, 8, 0, 0, 0, 9, 0, 10, 11, 12, 0, 0, 0, 0, 0, 0, 0, 13, 0, 0, 0, 14, 0, 15, 16, 17, 0, 0, 0,
		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 0, 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 20, 0, 21, 22, 23, 0, 0, 0, 0, 0, 0, 0, 0,
		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 25,
		0, 0, 0, 0, 0, 0, 0, 26, 0, 0, 0, 27, 0, 28, 29, 30, 31, 0, 32, 0, 0, 0, 33, 0, 0, 0, 0, 0, 0, 0, 34, 0, 0, 0, 0
		, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 35, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
		0, 0, 0, 0, 36, 37, 38, 0, 39, 0, 0, 0, 40, 0, 0, 0, 0, 0, 0, 0, 41, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42
		, 43, 44, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 47, 48, 49, 0, 50, 0, 0, 0, 51, 52, 53, 0, 54, 55, 56, 57, 58 };

	// 計算LBP
	int heightOfExtendedImage = extendedImage.rows;
	int widthOfExtendedImage = extendedImage.cols;
	int widthOfLBP = LBPImage.cols;
	uchar *rowOfExtendedImage = extendedImage.data + widthOfExtendedImage + 1;
	uchar *rowOfLBPImage = LBPImage.data;
	for (int y = 1; y <= heightOfExtendedImage - 2; ++y, rowOfExtendedImage += widthOfExtendedImage, rowOfLBPImage += widthOfLBP)
	{
		// 列
		uchar *colOfExtendedImage = rowOfExtendedImage;
		uchar *colOfLBPImage = rowOfLBPImage;
		for (int x = 1; x <= widthOfExtendedImage - 2; ++x, ++colOfExtendedImage, ++colOfLBPImage)
		{
			// 計算LBP值
			int LBPValue = 0;
			if (colOfExtendedImage[0 - widthOfExtendedImage - 1] >= colOfExtendedImage[0])
				LBPValue += 128;
			if (colOfExtendedImage[0 - widthOfExtendedImage] >= colOfExtendedImage[0])
				LBPValue += 64;
			if (colOfExtendedImage[0 - widthOfExtendedImage + 1] >= colOfExtendedImage[0])
				LBPValue += 32;
			if (colOfExtendedImage[0 + 1] >= colOfExtendedImage[0])
				LBPValue += 16;
			if (colOfExtendedImage[0 + widthOfExtendedImage + 1] >= colOfExtendedImage[0])
				LBPValue += 8;
			if (colOfExtendedImage[0 + widthOfExtendedImage] >= colOfExtendedImage[0])
				LBPValue += 4;
			if (colOfExtendedImage[0 + widthOfExtendedImage - 1] >= colOfExtendedImage[0])
				LBPValue += 2;
			if (colOfExtendedImage[0 - 1] >= colOfExtendedImage[0])
				LBPValue += 1;

			colOfLBPImage[0] = table[LBPValue];

		} // x

	}// y
}

// 計算9種等價模式,等價模式編號也是從1開始:1~9
int LBP::ComputeValue9(int value58)
{
	int value9 = 0;
	switch (value58)
	{
	case 1:
		value9 = 1;
		break;
	case 2:
		value9 = 2;
		break;
	case 4:
		value9 = 3;
		break;
	case 7:
		value9 = 4;
		break;
	case 11:
		value9 = 5;
		break;
	case 16:
		value9 = 6;
		break;
	case 22:
		value9 = 7;
		break;
	case 29:
		value9 = 8;
		break;
	case 58:
		value9 = 9;
		break;
	}

	return value9;

}

int LBP::GetMinBinary(int binary)
{
	static const int miniBinaryLUT[256] = { 0, 1, 1, 3, 1, 5, 3, 7, 1, 9, 5, 11, 3, 13, 7, 15, 1, 17, 9, 19, 5,
		21, 11, 23, 3, 25, 13, 27, 7, 29, 15, 31, 1, 9, 17, 25, 9, 37, 19, 39, 5, 37, 21, 43, 11, 45,
		23, 47, 3, 19, 25, 51, 13, 53, 27, 55, 7, 39, 29, 59, 15, 61, 31, 63, 1, 5, 9, 13, 17, 21, 25,
		29, 9, 37, 37, 45, 19, 53, 39, 61, 5, 21, 37, 53, 21, 85, 43, 87, 11, 43, 45, 91, 23, 87, 47, 95,
		3, 11, 19, 27, 25, 43, 51, 59, 13, 45, 53, 91, 27, 91, 55, 111, 7, 23, 39, 55, 29, 87, 59, 119, 15,
		47, 61, 111, 31, 95, 63, 127, 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31, 9, 25, 37,
		39, 37, 43, 45, 47, 19, 51, 53, 55, 39, 59, 61, 63, 5, 13, 21, 29, 37, 45, 53, 61, 21, 53, 85,
		87, 43, 91, 87, 95, 11, 27, 43, 59, 45, 91, 91, 111, 23, 55, 87, 119, 47, 111, 95, 127, 3,
		7, 11, 15, 19, 23, 27, 31, 25, 39, 43, 47, 51, 55, 59, 63, 13, 29, 45, 61, 53, 87, 91, 95, 27, 59,
		91, 111, 55, 119, 111, 127, 7, 15, 23, 31, 39, 47, 55, 63, 29, 61, 87, 95, 59, 111, 119, 127, 15, 31, 47, 63,
		61, 95, 111, 127, 31, 63, 95, 127, 63, 127, 127, 255 };

	return miniBinaryLUT[binary];
}

// 獲取循環二進制的最小二進制模式
uchar LBP::GetMinBinary(uchar *binary)
{
	// 計算8個二進制
	uchar LBPValue[8] = { 0 };
	for (int i = 0; i <= 7; ++i)
	{
		LBPValue[0] += binary[i] << (7 - i);
		LBPValue[1] += binary[(i + 7) % 8] << (7 - i);
		LBPValue[2] += binary[(i + 6) % 8] << (7 - i);
		LBPValue[3] += binary[(i + 5) % 8] << (7 - i);
		LBPValue[4] += binary[(i + 4) % 8] << (7 - i);
		LBPValue[5] += binary[(i + 3) % 8] << (7 - i);
		LBPValue[6] += binary[(i + 2) % 8] << (7 - i);
		LBPValue[7] += binary[(i + 1) % 8] << (7 - i);
	}

	// 選擇最小的
	uchar minValue = LBPValue[0];
	for (int i = 1; i <= 7; ++i)
	{
		if (LBPValue[i] < minValue)
		{
			minValue = LBPValue[i];
		}
	}

	return minValue;

}
// cellSize:每個cell的大小,如16*16
void LBP::ComputeLBPFeatureVector_Rotation_Uniform(const Mat &srcImage, Size cellSize, Mat &featureVector)
{
	// 參數檢查,內存分配
	//CV_Assert(srcImage.depth() == CV_8U&&srcImage.channels() == 1);

	Mat LBPImage;
	ComputeLBPImage_Rotation_Uniform(srcImage, LBPImage);

	// 計算cell個數
	int widthOfCell = cellSize.width;
	int heightOfCell = cellSize.height;
	int numberOfCell_X = srcImage.cols / widthOfCell;// X方向cell的個數
	int numberOfCell_Y = srcImage.rows / heightOfCell;

	// 特徵向量的個數
	int numberOfDimension = 9 * numberOfCell_X*numberOfCell_Y;
	featureVector.create(1, numberOfDimension, CV_32FC1);
	featureVector.setTo(Scalar(0));

	// 計算LBP特徵向量
	int stepOfCell = srcImage.cols;
	int index = -9;// cell的特徵向量在最終特徵向量中的起始位置
	float *dataOfFeatureVector = (float *)featureVector.data;
	for (int y = 0; y <= numberOfCell_Y - 1; ++y)
	{
		for (int x = 0; x <= numberOfCell_X - 1; ++x)
		{
			index += 9;

			// 計算每個cell的LBP直方圖
			Mat cell = LBPImage(Rect(x * widthOfCell, y * heightOfCell, widthOfCell, heightOfCell));
			uchar *rowOfCell = cell.data;
			int sum = 0; // 每個cell的等價模式總數
			for (int y_Cell = 0; y_Cell <= cell.rows - 1; ++y_Cell, rowOfCell += stepOfCell)
			{
				uchar *colOfCell = rowOfCell;
				for (int x_Cell = 0; x_Cell <= cell.cols - 1; ++x_Cell, ++colOfCell)
				{
					if (colOfCell[0] != 0)
					{
						// 在直方圖中轉化爲0~8,所以是colOfCell[0] - 1
						++dataOfFeatureVector[index + colOfCell[0] - 1];
						++sum;
					}
				}
			}

			// 直方圖歸一化
			for (int i = 0; i <= 8; ++i)
				dataOfFeatureVector[index + i] /= sum;

		}
	}
}

void LBP::ComputeLBPImage_Rotation_Uniform(const Mat &srcImage, Mat &LBPImage)
{
	// 參數檢查,內存分配
//	CV_Assert(srcImage.depth() == CV_8U&&srcImage.channels() == 1);
	LBPImage.create(srcImage.size(), srcImage.type());

	// 擴充圖像,處理邊界情況
	Mat extendedImage;
	copyMakeBorder(srcImage, extendedImage, 1, 1, 1, 1, BORDER_DEFAULT);

	// 構建LBP 等價模式查找表
	//int table[256];
	//BuildUniformPatternTable(table);

	// 查找表
	static const int table[256] = { 1, 2, 3, 4, 5, 0, 6, 7, 8, 0, 0, 0, 9, 0, 10, 11, 12, 0, 0, 0, 0, 0, 0, 0, 13, 0, 0, 0, 14, 0, 15, 16, 17, 0, 0, 0,
		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 0, 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 20, 0, 21, 22, 23, 0, 0, 0, 0, 0, 0, 0, 0,
		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 25,
		0, 0, 0, 0, 0, 0, 0, 26, 0, 0, 0, 27, 0, 28, 29, 30, 31, 0, 32, 0, 0, 0, 33, 0, 0, 0, 0, 0, 0, 0, 34, 0, 0, 0, 0
		, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 35, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
		0, 0, 0, 0, 36, 37, 38, 0, 39, 0, 0, 0, 40, 0, 0, 0, 0, 0, 0, 0, 41, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42
		, 43, 44, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 47, 48, 49, 0, 50, 0, 0, 0, 51, 52, 53, 0, 54, 55, 56, 57, 58 };

	int heigthOfExtendedImage = extendedImage.rows;
	int widthOfExtendedImage = extendedImage.cols;
	int widthOfLBPImage = LBPImage.cols;

	uchar *rowOfExtendedImage = extendedImage.data + widthOfExtendedImage + 1;
	uchar *rowOfLBPImage = LBPImage.data;
	for (int y = 1; y <= heigthOfExtendedImage - 2; ++y, rowOfExtendedImage += widthOfExtendedImage, rowOfLBPImage += widthOfLBPImage)
	{
		// 列
		uchar *colOfExtendedImage = rowOfExtendedImage;
		uchar *colOfLBPImage = rowOfLBPImage;
		for (int x = 1; x <= widthOfExtendedImage - 2; ++x, ++colOfExtendedImage, ++colOfLBPImage)
		{
			// 計算LBP值
			int LBPValue = 0;
			if (colOfExtendedImage[0 - widthOfExtendedImage - 1] >= colOfExtendedImage[0])
				LBPValue += 128;
			if (colOfExtendedImage[0 - widthOfExtendedImage] >= colOfExtendedImage[0])
				LBPValue += 64;
			if (colOfExtendedImage[0 - widthOfExtendedImage + 1] >= colOfExtendedImage[0])
				LBPValue += 32;
			if (colOfExtendedImage[0 + 1] >= colOfExtendedImage[0])
				LBPValue += 16;
			if (colOfExtendedImage[0 + widthOfExtendedImage + 1] >= colOfExtendedImage[0])
				LBPValue += 8;
			if (colOfExtendedImage[0 + widthOfExtendedImage] >= colOfExtendedImage[0])
				LBPValue += 4;
			if (colOfExtendedImage[0 + widthOfExtendedImage - 1] >= colOfExtendedImage[0])
				LBPValue += 2;
			if (colOfExtendedImage[0 - 1] >= colOfExtendedImage[0])
				LBPValue += 1;

			int minValue = GetMinBinary(LBPValue);

			// 計算58種等價模式LBP
			int value58 = table[minValue];

			// 計算9種等價模式
			colOfLBPImage[0] = ComputeValue9(value58);
		}

	}

}

void LBP::ComputeLBPImage_Rotation_Uniform_2(const Mat &srcImage, Mat &LBPImage)
{
	// 參數檢查,內存分配
	//CV_Assert(srcImage.depth() == CV_8U&&srcImage.channels() == 1);
	LBPImage.create(srcImage.size(), srcImage.type());

	// 擴充圖像,處理邊界情況
	Mat extendedImage;
	copyMakeBorder(srcImage, extendedImage, 1, 1, 1, 1, BORDER_DEFAULT);

	// 構建LBP 等價模式查找表
	//int table[256];
	//BuildUniformPatternTable(table);

	// 通過查找表
	static const int table[256] = { 1, 2, 3, 4, 5, 0, 6, 7, 8, 0, 0, 0, 9, 0, 10, 11, 12, 0, 0, 0, 0, 0, 0, 0, 13, 0, 0, 0, 14, 0, 15, 16, 17, 0, 0, 0,
		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 0, 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 20, 0, 21, 22, 23, 0, 0, 0, 0, 0, 0, 0, 0,
		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 25,
		0, 0, 0, 0, 0, 0, 0, 26, 0, 0, 0, 27, 0, 28, 29, 30, 31, 0, 32, 0, 0, 0, 33, 0, 0, 0, 0, 0, 0, 0, 34, 0, 0, 0, 0
		, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 35, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
		0, 0, 0, 0, 36, 37, 38, 0, 39, 0, 0, 0, 40, 0, 0, 0, 0, 0, 0, 0, 41, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42
		, 43, 44, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 47, 48, 49, 0, 50, 0, 0, 0, 51, 52, 53, 0, 54, 55, 56, 57, 58 };

	uchar binary[8] = { 0 };// 記錄每個像素的LBP值
	int heigthOfExtendedImage = extendedImage.rows;
	int widthOfExtendedImage = extendedImage.cols;
	int widthOfLBPImage = LBPImage.cols;

	uchar *rowOfExtendedImage = extendedImage.data + widthOfExtendedImage + 1;
	uchar *rowOfLBPImage = LBPImage.data;
	for (int y = 1; y <= heigthOfExtendedImage - 2; ++y, rowOfExtendedImage += widthOfExtendedImage, rowOfLBPImage += widthOfLBPImage)
	{
		// 列
		uchar *colOfExtendedImage = rowOfExtendedImage;
		uchar *colOfLBPImage = rowOfLBPImage;
		for (int x = 1; x <= widthOfExtendedImage - 2; ++x, ++colOfExtendedImage, ++colOfLBPImage)
		{
			// 計算旋轉不變LBP(最小的二進制模式)
			binary[0] = colOfExtendedImage[0 - widthOfExtendedImage - 1] >= colOfExtendedImage[0] ? 1 : 0;
			binary[1] = colOfExtendedImage[0 - widthOfExtendedImage] >= colOfExtendedImage[0] ? 1 : 0;
			binary[2] = colOfExtendedImage[0 - widthOfExtendedImage + 1] >= colOfExtendedImage[0] ? 1 : 0;
			binary[3] = colOfExtendedImage[0 + 1] >= colOfExtendedImage[0] ? 1 : 0;
			binary[4] = colOfExtendedImage[0 + widthOfExtendedImage + 1] >= colOfExtendedImage[0] ? 1 : 0;
			binary[5] = colOfExtendedImage[0 + widthOfExtendedImage] >= colOfExtendedImage[0] ? 1 : 0;
			binary[6] = colOfExtendedImage[0 + widthOfExtendedImage - 1] >= colOfExtendedImage[0] ? 1 : 0;
			binary[7] = colOfExtendedImage[0 - 1] >= colOfExtendedImage[0] ? 1 : 0;

			int minValue = GetMinBinary(binary);

			// 計算58種等價模式LBP
			int value58 = table[minValue];

			// 計算9種等價模式
			colOfLBPImage[0] = ComputeValue9(value58);
		}

	}
}


// 驗證灰度不變+旋轉不變+等價模式種類
void LBP::Test()
{
	uchar LBPValue[8] = { 0 };
	int k = 7, j;
	int temp;
	LBP lbp;
	int number[256] = { 0 };
	int numberOfMinBinary = 0;

	// 旋轉不變
	for (int i = 0; i < 256; ++i)
	{
		k = 7;
		temp = i;
		while (k >= 0)
		{
			LBPValue[k] = temp & 1;
			temp = temp >> 1;
			--k;
		}
		int minBinary = lbp.GetMinBinary(LBPValue);

		// 查找有無重複的
		for (j = 0; j <= numberOfMinBinary - 1; ++j)
		{
			if (number[j] == minBinary)
				break;
		}
		if (j == numberOfMinBinary)
		{
			number[numberOfMinBinary++] = minBinary;
		}
	}
	cout << "旋轉不變一共有:" << numberOfMinBinary << "種" << endl;

	// LUT
	static const int table[256] = { 1, 2, 3, 4, 5, 0, 6, 7, 8, 0, 0, 0, 9, 0, 10, 11, 12, 0, 0, 0, 0, 0, 0, 0, 13, 0, 0, 0, 14, 0, 15, 16, 17, 0, 0, 0,
		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 0, 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 20, 0, 21, 22, 23, 0, 0, 0, 0, 0, 0, 0, 0,
		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 25,
		0, 0, 0, 0, 0, 0, 0, 26, 0, 0, 0, 27, 0, 28, 29, 30, 31, 0, 32, 0, 0, 0, 33, 0, 0, 0, 0, 0, 0, 0, 34, 0, 0, 0, 0
		, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 35, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
		0, 0, 0, 0, 36, 37, 38, 0, 39, 0, 0, 0, 40, 0, 0, 0, 0, 0, 0, 0, 41, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42
		, 43, 44, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 47, 48, 49, 0, 50, 0, 0, 0, 51, 52, 53, 0, 54, 55, 56, 57, 58 };

	for (int i = 0; i <= numberOfMinBinary - 1; ++i)
	{
		cout << "旋轉不變的LBP:" << number[i] << " " << "對應的等價模式:" << table[number[i]] << endl;
	}

}

void LBP::TestGetMinBinaryLUT()
{
	for (int i = 0; i <= 255; ++i)
	{
		uchar a[8] = { 0 };
		int k = 7;
		int j = i;
		while (j)
		{
			// 除2取餘
			a[k] = j % 2;
			j /= 2;
			--k;
		}
		uchar minBinary = GetMinBinary(a);
		printf("%d,", minBinary);

	}
}


main.c

 

#include <stdio.h>  
#include <time.h>  
#include <opencv2/opencv.hpp>  
#include <opencv/cv.h>  
#include <iostream> 
#include <opencv2/core/core.hpp>  
#include <opencv2/highgui/highgui.hpp>  
#include <opencv2/ml/ml.hpp>  
#include <sys/io.h>
#include <dirent.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <iostream>
#include <fstream> 
#include <unistd.h>

#include"LBP.h"

#define CELLSIZE_LBP 16 // LBP的窗口大小,4,8,16,32

#define	TRUE_SWATCH_PATH	"/home/qushy/share/OpenCV/SVM/1data/data/test/true"	    //訓練正樣本路徑
#define	FALSE_SWATCH_PATH	"/home/qushy/share/OpenCV/SVM/1data/data/test/false"	//訓練負樣本路徑
#define	TEST_SWATCH_PATH	"/home/qushy/share/OpenCV/SVM/1data/data/test/src"      //識別測試樣本
#define SAVE_TRUE_BUBBLE    "/home/qushy/share/OpenCV/SVM/1data/data/test/bubble"   //識別正樣本存儲路徑
#define SAVE_FALSE_BUBBLE    "/home/qushy/share/OpenCV/SVM/1data/data/test/nobubble"//識別負樣本存儲路徑

using namespace std;
using namespace cv;
using namespace cv::ml;

long what_time_is_it_now(void)
{
    struct timeval t;
    gettimeofday(&t, NULL);
    return t.tv_sec * 1000 + t.tv_usec / 1000;
}

void cp_result_pic(const char* src, const char* dst)
{
    char cmd[256] = { 0 };
    sprintf(cmd, "cp %s %s", src, dst);
    system(cmd);
}

void getFiles(string path, vector<string>& files)
{
    DIR* dir = NULL;
    struct dirent* pDir = NULL;
    string p;
    dir = opendir(path.c_str());

    if (dir != NULL)
    {
        cout << path.c_str() << endl;
        while (1)
        {
            pDir = readdir(dir);

            if (pDir == NULL) break;

            if (pDir->d_type == DT_REG)
            {
                files.push_back(p.assign(path).append("/").append(pDir->d_name));
            }
        }
    }
    closedir(dir);
}

//獲取測試樣本
void getTestBubble(vector<string>& imagePaths)
{
    string filePath = TEST_SWATCH_PATH;
    vector<string> files;
    getFiles(filePath, files);
    int number = files.size();

    for (int i = 0; i < number; i++)
    {
        imagePaths.push_back(files[i].c_str());
    }
    cout << "getTestBubble " << number << endl;
}

//獲取正樣本
//並貼標籤爲1
void getBubble(vector<string>& imagePaths, vector<int>& imageClass)
{
    string filePath = TRUE_SWATCH_PATH;
    vector<string> files;
    getFiles(filePath, files);
    int number = files.size();

    for (int i = 0; i < number; i++)
    {
        imagePaths.push_back(files[i].c_str());
        imageClass.push_back(1); //該樣本爲數字1
    }
    cout << "getBubble " << number << endl;
}

//獲取負樣本
//並貼標籤爲0
void getNoBubble(vector<string>& imagePaths, vector<int>& imageClass)
{
    string filePath = FALSE_SWATCH_PATH;
    vector<string> files;
    getFiles(filePath, files);
    int number = files.size();

    for (int i = 0; i < number; i++)
    {
        imagePaths.push_back(files[i].c_str());
        imageClass.push_back(0); //該樣本爲數字0
    }
    cout << "getNoBubble " << number << endl;
}

void LBP_SVM_Rotation()
{
    // 讀入訓練樣本路徑和類別
    vector<string> imagePaths;
    vector<int> imageClass;

    getBubble(imagePaths, imageClass);
    getNoBubble(imagePaths, imageClass);

    // 計算樣本LBP特徵向量矩陣和類別矩陣
    int lengthOfFeatureVector = (32 / CELLSIZE_LBP) * (64 / CELLSIZE_LBP) * 9; // 特徵向量的維數
    Mat featureVectorOfSample;
    Mat classOfSample;
    vector<string>::size_type numberOfSample = imagePaths.size();
    Mat srcImage;
    LBP lbp;
    Mat featureVector;
    cout << "computer lbp feature vector rotation" <<endl;
    for (vector<string>::size_type i = 0; i <= numberOfSample - 1; ++i)
    {
        //cout << imagePaths[i].c_str() << endl;
        // 讀入圖片
        srcImage = imread(imagePaths[i].c_str(), 0);
        resize(srcImage, srcImage, Size(256, 256), 0, 0, INTER_LINEAR);

        // 計算樣本LBP特徵向量
        lbp.ComputeLBPFeatureVector_Rotation_Uniform(srcImage, Size(CELLSIZE_LBP, CELLSIZE_LBP), featureVector);
        if (featureVector.empty()) printf("ComputeLBPFeatureVector_Rotation_Uniform faild\n");
        featureVectorOfSample.push_back(featureVector);
        classOfSample.push_back(imageClass[i]);
    }

    // 使用SVM分類器訓練
    // 參數設置
    Ptr<SVM> svm = SVM::create();
    svm->setType(SVM::C_SVC);
    svm->setKernel(SVM::RBF);//核函數
    svm->setDegree(0);
    svm->setGamma(1);
    svm->setCoef0(0);
    svm->setC(1);
    svm->setNu(0);
    svm->setP(0);
    svm->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER + TermCriteria::EPS, 100, 1e-6));

    cout << "training svm ..." << endl;
    svm->train(featureVectorOfSample, ROW_SAMPLE, classOfSample);
    svm->save("Classifier.xml");
    cout << "save svm Classifier.xml" << endl;
}

void LBP_SVM_Rotation_Test()
{
    Ptr<ml::SVM>svm = ml::SVM::load("Classifier.xml");
    // 使用訓練好的分類器進行識別
    vector<string> testImagePath;
    getTestBubble(testImagePath);

    // 識別
    LBP lbp;
    
    int lengthOfFeatureVector = (32 / CELLSIZE_LBP) * (64 / CELLSIZE_LBP) * 9;
    vector<string>::size_type numberOfTestImage = testImagePath.size();
    Mat featureVectorOfTestImage;
    int result_true = 0;
    int result_false = 0;

    cout << "computer predict result:" << endl;
    long t_start = what_time_is_it_now();
    // 注意將循環體內的耗時變量和操作提取到循環體內
    for (vector<string>::size_type i = 0; i <= numberOfTestImage - 1; ++i)
    {
        Mat testImage = imread(testImagePath[i].c_str(), 0);
        resize(testImage, testImage, Size(256, 256), 0, 0, INTER_LINEAR);
        //cout << testImagePath[i].c_str() << endl;

        // 計算LBP特徵向量
        lbp.ComputeLBPFeatureVector_Rotation_Uniform(testImage, Size(CELLSIZE_LBP, CELLSIZE_LBP), featureVectorOfTestImage);
        int predict = (int)svm->predict(featureVectorOfTestImage);

        if (predict)
        {
            result_true++;
            //cp_result_pic(testImagePath[i].c_str(), SAVE_TRUE_BUBBLE);
        }
        else
        {
            result_false++;
            //cp_result_pic(testImagePath[i].c_str(), SAVE_FALSE_BUBBLE);
        }
    }
    long t_end = what_time_is_it_now();

    cout << "result true " << result_true << endl;
    cout << "result false " << result_false << endl;
    cout << "total " << t_end - t_start << " ms" << "  average " << (t_end - t_start) * 1.0 / numberOfTestImage << " ms" << endl;
}

int main()
{
    LBP_SVM_Rotation();
    LBP_SVM_Rotation_Test();

    return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章