LBP入門
LBPとは,Local Binary Patternのことで,イメージの局所的な特徴を記述するための演算子です.LBPは,顔認識やターゲット検出によく利用されます.OpenCVには,顔認識にLBP特徴を利用するインタフェースが用意されており,LBP特徴を利用してターゲット検出分類器を学習するメソッドも用意されています.OpenCVは,LBP特徴の計算を実装していますが,LBP特徴を計算するための個別のインタフェースは提供していません.OpenCVはLBP特徴量を実装していますが,LBP特徴量を計算するための個別のインタフェースは提供していません.言い換えれば,OpenCV は LBP アルゴリズムを利用しますが,関数インタフェースは提供しません.
2.2 アルゴリズム理論入門
準備数学:円のパラメトリック方程式
は対応する円上の点の半径からx軸への角度を表し、and は円上の点の座標。
2.2.1 LBP原理の紹介LBP特徴は、イメージの局所領域の共同分布を用いてイメージのテクスチャ特徴を記述します。 局所近傍領域の画素数を仮定すると、テクスチャ特徴の共同分布は以下のように表現できます:
ここで、, は対応する局所近傍領域の中心ピクセルのグレー値を表し、, は中心ピクセルの中心にある半径Rの円上のピクセルのグレー値を表します。
中心画素と近傍画素が互いに独立であると仮定すると、上記の定義式は次のように書けます:
これは局所領域の全体的な明るさを決定するもので、テクスチャ特徴の場合はこの項目を無視して最終的な値を得ることができます:
上記の式は、テクスチャ特徴を、輝度平均にほとんど依存しないように、近傍ピクセルと中心ピクセルとの差の共同分布関数として定義することを示しており、統計量Tはこの時点で輝度平均、すなわちグレーレベルに依存しない値であることが上記の式からわかります。
最後に、特性関数は次のように定義されます:
グレーレベル不変LBPを次のように定義します:
つまり、2進数の符号化式。
人気のある説明:
元のLBP演算子は画素の近傍領域で定義され、近傍領域の中心画素を閾値として、近傍8画素のグレー値を近傍領域の中心画素の値と比較します。この2進数が中心ピクセルのLBP値で、LBP値は全部で256通りあります。中心ピクセルのLBP値は、そのピクセル周辺のテクスチャ情報を反映します。
注意:LBP特徴量を計算するために使用するイメージは、グレースケールイメージでなければなりません。
2.2.2 円形LBP演算子
基本的なLBP演算子の最大の欠点は、一定の半径内の小さな領域しかカバーできないことであり、これは明らかに異なるサイズや周波数のテクスチャのニーズを満たすことができません。異なるスケールのテクスチャ特徴に対応し、グレーレベルと回転不変性の要件を満たすために、Ojalaらは3×3の近傍領域を任意の近傍領域に拡張し、正方形の近傍領域を円形の近傍領域に置き換えることでLBP演算子を改良しました。このように、P個のサンプリング点を含む半径Rの円形領域が得られるようなLBP演算子は、次のように表されます;
与えられた中心点に対して、その近傍のピクセル位置は、そのサンプリング点は、以下の式を使用して計算されます:
3.2.3 LBP回転不変性と等価パターンLPB特徴はグレースケール不変ですが、回転不変ではなく、同じイメージでも回転後、その特徴は大きく異なり、マッチングの精度に影響を与えます。
実装方法:最初に定義された一連のLPB値を得るために円形近傍領域を回転させ続け、最小の値をこの近傍領域の値とします。
ここで、回転不変のLBP特徴量を表します。は回転関数で、ループされる右循環ビットを表します。
1、たとえば、混乱している、画素点の値の近傍内の各画素点のイメージが固定され、値を取得する処理後のサンプリングポイントを取得するには、1または0のいずれかが決定されています。それは円形のフィールドの回転を達成する方法ですか?
実際には、別の意味で、例として理解されるべきです。
半径1の近傍領域と8個のサンプリング点を使用するイメージを指定し、この領域内に0と同定された点が4個、他の4個が1と同定された点が不定な組み合わせ順で存在するとします。例えば、次のような場合があります。では、この順序の並べ替えがいくつあるか考えてみましょう。それは次のようになります: ẽ ẽ
- 実際、半径1の近傍領域とサンプルポイント8の近傍領域のみを使用するように指示された場合、円形のLBP値は、任意の領域に対して256通りの出力を生成します。
- 近傍領域は半径とサンプリング点の両方によって決定され,イメージ内のすべてのピクセルに対して同じ種類の近傍領域が使われることに注意してください。
2.もう少し簡単に説明するために、別のシナリオを簡略化します。
ある画素点について計算された2値パターンが11100001であると仮定し、回転不変処理後のLBP値を求めます。
まず、"11100001 "からわかるように、LBP演算子を計算する画素点の近傍は、標本点が8個になるように設定されています。 さらに、中心点の近傍の8個の画素点のうち、中心画素の値よりも大きい値を持つ画素点が4個の位置に存在します。"1110001 "のLBP値は225です。 次に、値が1である標本点が4個ある8個の標本点の2値パターンを計算します。"11100001 "と同様に、"11100001 "の他に、以下のように7個のパターンがあります。次に、1の値が4つあるサンプリング点8点の2値パターンを計算し、「11100001」と同様に、1の値が4つある点を隣り合わせに回転させた2値パターンを計算すると、下図のように11100001以外に7パターンあります。計算の結果、15が全8パターンの中で最小のLBP値であることがわかりますので、回転処理後の11100001のLBP値は225ではなく、15になります。同様に、ある画素点の2値モードが次の7つのモードのいずれかであるとき、その回転不変のLBP値は15になります!
等価モデル:
LBPオペレータは、異なるバイナリパターンを生成することができます。例えば、近傍には様々なパターンがあります。このような大量の2値パターンは、情報抽出にも認識にも不向きです。
Ojalaらは、実際のイメージではLPBパターンの大半は1から0、または0から1へのジャンプを多くても2回含んでいると論じています。
等価パターン:特定のローカル・バイナリ・パターンに対応する巡回バイナリ数が、0から1、または1から0へ最大2回ジャンプする場合、そのローカル・バイナリ・パターンに対応するバイナリは等価パターンと呼ばれます。
例えば、00000000, 11111111, 11110010, 10111111はすべて等価なパターンです。
あるパターンが等価なパターンかどうかを調べます:
それを1ビットずつずらした後のバイナリパターンと引き算します。そして絶対値を合計します。Uが2以下なら、そのパターンは等価。
混合モード:等価モードの他に混合モードと呼ばれるものがあります。
改善された LPB モードの数は、2 次元から .Ojala氏は、等価モードが全モードの大部分を占めていると考えています。次の図では、等価モードがそれぞれ 88%、93%、76%を占めています。
等価モードの割合は、ローパスフィルタリングによって高めることができます。グラフをガウシアンフィルタリングした後、等価モードの割合を 90% まで高めることができます。
ヒストグラムの計算ヒストグラムはLBPの次元と同じ数の区間を持ち、ヒストグラムの計算は各区間内のピクセル数を数えることから始まります。一般的に、ヒストグラムはいくつかのサブ領域に分割されます。
次に、各領域のヒストグラムがカウントされ、つまり各LBP値の出現頻度がカウントされます。最後に、各領域の統計ヒストグラムを連結して特徴ベクトルとし、これはイメージ全体のLBPテクスチャ特徴ベクトルとなります。最後に分類器を用いて分類を行います。
MB-LBP 従来のLBP特徴量では取得できる情報が限られているため、MB-LBP特徴量ではイメージを複数の矩形領域に分割し、矩形領域内の特徴量を個別に取得します。
2.3.4 顔検出処理顔検出処理では、マルチスケールスライディングウィンドウサーチを使用し、各スケールが20x20の大きさのウィンドウを一定のステップサイズでインターセプトし、ウィンドウを分類器に入れて顔かどうかを判定します。
OpenCVベースの実装
- モデルの事前学習にOpenCVのLBPを使用
パイソン
#coding:utf-8
import cv2 as cv
# 元イメージを読み込む
img= cv.imread('E:/python-project/deep-learning/picture/test2.jpg')
#face_detect = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')
face_detect = cv.CascadeClassifier("E:/python-project/deep-learning/picture/lbpcascade_frontalface_improved.xml")
# 顔検出
# グレースケール処理
gray = cv.cvtColor(img, code=cv.COLOR_BGR2GRAY)
# 检查人脸 按照1.1倍放到 周围最小像素为5
face_zone = face_detect.detectMultiScale(gray, scaleFactor = 2, minNeighbors = 2) # maxSize = (55,55)
print ('を使って顔の情報を認識する:\n',face_zone)
# 矩形と円を描いて顔を検出する
for x, y, w, h in face_zone:
# 長方形の顔領域を描画する
cv.rectangle(img, pt1 = (x, y), pt2 = (x+w, y+h), color = [0,0,255], thickness=2)
# 绘制圆形人脸区域 radius表示半径
cv.circle(img, center = (x + w//2, y + h//2), radius = w//2, color = [0,255,0], thickness = 2)
# 手動でリサイズするイメージを設定する
cv.namedWindow("Easmount-", 0)
# イメージを表示する
cv.imshow("Easmount-", img)
# 等待显示 设置任意键退出程序
cv.waitKey(0)
cv.destroyAllWindows()
c++
uchar 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;
}
//9つの同値モデルを計算する
int 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;
}
//グレースケール不変な通常のLBP
void NormalLBPImage(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;
}
}
}
// 等価グレースケール不変LBP
void UniformNormalLBPImage(const Mat &srcImage, Mat &LBPImage)// 等価パターンLBP特徴マップを計算する
{
// パラメータチェック、メモリ割り当て
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;
//BuildUniformPatternTable(table);
// LUT(256(各タイプに相当するモデル)
static const int table = { 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];
}
}
}
// 等価回転不変LBP
void UniformRotInvLBPImage(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;
//BuildUniformPatternTable(table);
// ルックアップテーブルによる
static const int table = { 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);
}
}
}
//グレースケール不変な通常のLBP特徴量
void NormalLBPFeature(const Mat &srcImage, Size cellSize, Mat &featureVector)
{
// パラメータチェック、メモリ割り当て
CV_Assert(srcImage.depth() == CV_8U && srcImage.channels() == 1);
Mat LBPImage;
NormalLBPImage(srcImage, LBPImage);
// セル数を計算する
int widthOfCell = cellSize.width;
int heightOfCell = cellSize.height;
int numberOfCell_X = srcImage.cols / widthOfCell;// X方向セルの数
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;
// 各セルの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;
}
}
}
// 等価グレースケール不変LBP特徴量
void UniformNormalLBPFeature(const Mat &srcImage, Size cellSize, Mat &featureVector)
{
// パラメータチェック、メモリ割り当て
CV_Assert(srcImage.depth() == CV_8U && srcImage.channels() == 1);
Mat LBPImage;
UniformNormalLBPImage(srcImage, LBPImage);
// セル数を計算する
int widthOfCell = cellSize.width;
int heightOfCell = cellSize.height;
int numberOfCell_X = srcImage.cols / widthOfCell;// X方向セルの数
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;
// 各セルのLBPヒストグラムを計算する
Mat cell = LBPImage(Rect(x * widthOfCell, y * heightOfCell, widthOfCell, heightOfCell));
uchar *rowOfCell = cell.data;
int sum = 0; // セルあたりの等価パターンの総数
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特徴量
void UniformRotInvLBPFeature(const Mat &srcImage, Size cellSize, Mat &featureVector)
{
// パラメータチェック、メモリ割り当て
CV_Assert(srcImage.depth() == CV_8U && srcImage.channels() == 1);
Mat LBPImage;
UniformRotInvLBPImage(srcImage, LBPImage);
// セル数を計算する
int widthOfCell = cellSize.width;
int heightOfCell = cellSize.height;
int numberOfCell_X = srcImage.cols / widthOfCell;// X方向セルの数
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;
// 各セルのLBPヒストグラムを計算する
Mat cell = LBPImage(Rect(x * widthOfCell, y * heightOfCell, widthOfCell, heightOfCell));
uchar *rowOfCell = cell.data;
int sum = 0; // セルあたりの等価パターンの総数
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;
}
}
}