今回は画像特徴量の一つであるLBP(Local Binary Pattern)特徴を計算するプログラムを書いてみました。
LBPは1994年に発表された画像特徴量で、中心画素と周辺画素の画素値の関係性を元に算出される特徴量です。
計算の手段は以下のようになります。
ある注目画素に関し、周辺8画素が注目画素よりも輝度値が大きいか小さいかを元に0、1で示します。この周辺8画素という数がミソで、0 or 1が8bit分で表現できるため、ちょうど1byte(0~255)のレンジに値が収まります。
このような計算方法で得られる特徴量をLBP(Local Binary Pattern)と呼び、比較的照明変化などに強い特徴量であると言われています。
また、上記の説明で完結するように、他の特徴量と比べると非常にシンプルな印象を受けます。実装もそんなに難しくありません。
このような特徴量をベースにして、あらかじめ抽出したい対象(例えば人物など)のLBP特徴を計算しておき、同じような特徴を示す場所を探すことで画像中からの物体検出などにも応用することが可能です。
それでは、早速コードなどを載せていきます。
動作環境
・OS : Windows10(64bit)
・Visual Studio 2015 Community
・OpenCV 2.4.11
(上記全て環境構築済の状態でビルド)
今回はOpenCVは環境済の状態でスタートするので、OpenCVの環境構築は以下の記事などを参照してください。
過去記事①:Nugetを使ってOpenCVの環境構築(Visual Studio 2015)
過去記事②:Visual Studio 2015でOpenCV 3.4環境構築(Windows10)
ソースコード
#include <opencv2/opencv.hpp> | |
#include <ctime> | |
std::string input_image_name = "test.jpg"; | |
int lbp_weights[8] = {128, 64, 32, 16, 8, 4, 2, 1}; | |
int main(int argc, char* argv[]) { | |
// 入力画像の取得 | |
cv::Mat input_image; | |
input_image = cv::imread(input_image_name, 0); | |
if (input_image.empty() == true) { | |
std::cerr << input_image_name << "が見つからない" << std::endl; | |
return -1; | |
} | |
std::cout << "Size(input_image) : " << input_image.cols << "×" << input_image.rows << std::endl; | |
cv::imshow("Input_image", input_image); | |
cv::waitKey(0); | |
// 出力用画像の用意 | |
cv::Mat1b lbp_image(input_image.rows, input_image.cols); | |
// 入力画像の上下左右に1pixel分のPaddingを追加する | |
cv::Mat input_pad_image; | |
copyMakeBorder(input_image, input_pad_image, 1, 1, 1, 1, cv::BORDER_REPLICATE); | |
std::cout << "Size(input_pad_image) : " << input_pad_image.cols << "×" << input_pad_image.rows << std::endl; | |
// 計算時間測定用 | |
int start = clock(); | |
// LBP特徴量の計算 | |
for (int y = 1; y < input_pad_image.rows - 1; y++) { | |
for (int x = 1; x < input_pad_image.cols - 1; x++) { | |
// 周辺画素入力用配列 | |
int pixel_value[8] = { 0 }; | |
int thresholded_value[8] = { 0 }; | |
// 注目画素 | |
int center_value = input_pad_image.at<unsigned char>(y, x); | |
// 周辺画素の値を取得 | |
pixel_value[0] = input_pad_image.at<unsigned char>(y - 1, x - 1); | |
pixel_value[1] = input_pad_image.at<unsigned char>(y - 1, x); | |
pixel_value[2] = input_pad_image.at<unsigned char>(y - 1, x + 1); | |
pixel_value[3] = input_pad_image.at<unsigned char>(y, x + 1); | |
pixel_value[4] = input_pad_image.at<unsigned char>(y + 1, x + 1); | |
pixel_value[5] = input_pad_image.at<unsigned char>(y + 1, x); | |
pixel_value[6] = input_pad_image.at<unsigned char>(y + 1, x - 1); | |
pixel_value[7] = input_pad_image.at<unsigned char>(y, x - 1); | |
int lbp_result = 0; | |
// LBP値を計算(0~255) | |
for (int i = 0; i < 8; i++) { | |
if (pixel_value[i] >= center_value){ | |
lbp_result += lbp_weights[i]; | |
} | |
} | |
// 結果の取得 | |
lbp_image.at<unsigned char>(y - 1, x - 1) = lbp_result; | |
} | |
} | |
// 計算時間測定用 | |
int end = clock(); | |
std::cout << "Processing time : " << end - start << "[ms]" << std::endl; | |
// 結果の保存と表示 | |
imwrite("output.png", lbp_image); | |
cv::imshow("LBP_result_image", lbp_image); | |
cv::waitKey(0); | |
return 0; | |
} |
画像の端での処理の制御がめんどくさかったため、画像全体に1pixelのパディングを入れてからLBPを計算する形で実装しています。
実行結果
以下の画像が結果です。上が入力画像で、下がLBP特徴の画像です。
実行結果は以下です。1600×1066のサイズで24msでしたが、実装はあまり計算時間を意識せずに組んでしまったので、もっと効率的な書き方はありそうです。
まとめ
Local Binary Patternの計算をOpenCVとC++のプログラムとして実装してみました。次はLBPを用いた物体抽出プログラムや、他の特徴量に関するプログラムも組んで、理解を深めてみたいです。