はじめに
はい、またもや『画像認識』(講談社 著:原田達也)を読んでいて、実装したくなったものがあるので、同じ日に連続で画像処理関係の記事を投稿します。
今回は「局所バイナリパターン(LBP)」というものです。名前からしていかにも難しそうな雰囲気が漂っていますが、内容は割と単純で、すぐに実装できそうな内容だったため、やってみました。
局所バイナリパターン(LBP)とは
局所バイナリパターンと(local binary pattern)は、画像などのテクスチャ情報を表すための記述子で、記述子とは、局所領域の内容について記述された情報のことです。要するに画像の一部分の特徴量のようなものですね。計算量が少なく高速であること、輝度の変化に頑強(画像の明るさが変わっても、同じ物体として認識できる)であることなどがメリットとして挙げられます。
英名から推測できるように、局所的な情報をバイナリ(2進数)を使って表します。具体的には、ある中心画素の周辺の画素について、それぞれの画素と中心画素との差分を取り、その差分が正の値(中心画素よりも周辺画素が大きい場合)は1、そうでない場合は0を設定し、それらに左上から$ 2^{i} $(局所領域がn × n なら、i = 0, 1, 2,....., n*n)を重みとして足し合わせていきます。例えば3×3の局所領域で、差分を0,1に変換したベクトルが[1, 0, 1, 0, 0, 0, 1, 1, 0]なら、
LBP
= 1 * $ 2^{0} $ + 0 * $ 2^{1} $ + 1 * $ 2^{2} $ + 0 * $ 2^{3} $ + 0 * $ 2^{4} $ + 0 * $ 2^{5} $ + 1 * $ 2^{6} $ + 1 * $ 2^{7} $ + 0 * $ 2^{8} $
= 197
となります。要するに周辺画素との画素値の大小関係を2進数の一つの数値で表している、ということですね。考えた人すごい!!
さて、あとはこれを実装するのみです。簡単そうでしょ??
実装
実行環境
・Python3
・Google Colaboratory
・Windows10
コード
#必要なライブラリのimport
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np
%matplotlib inline
from sklearn.cluster import KMeans
from sklearn import datasets
import pandas as pd
from tqdm import tqdm # 進捗状況可視化に使う
from google.colab import drive
drive.mount('/content/drive')
前回同様、使うライブラリをインポートして、Googleドライブ内にあるデータを読み込むためにドライブにマウントします。
from PIL import Image
image = Image.open("/content/drive/MyDrive/MyWorks/img/piyo.jpg")
image_array = np.array(image)
print(image_array.shape)
image
カラー画像のままだと、カラーチャンネルが3つあって扱いにくいので、グレースケール化します。
image_gray = image.convert("L")
image_gray_array = np.array(image_gray)
print(image_gray_array.shape)
続いてLBPを計算するための関数を定義します。
def LBP(image):
"""
画像の配列を入力に受け取り、そこから各画素におけるLBPを算出して、各画素の値をそのLBPに変換した配列を返す関数
"""
width = image.shape[0]
height = image.shape[1]
image = np.pad(image, 1) # チェックする半径分、周囲に余白を追加。(チェックするときに範囲外に出ないようにするため)
for x in tqdm(np.arange(width)):
for y in np.arange(height):
# 中心画素と周辺画素との差分を取る
diff = np.zeros(9)
diff[0] = image[x-1, y-1] - image[x, y]
diff[1] = image[x, y-1] - image[x, y]
diff[2] = image[x+1, y-1] - image[x, y]
diff[3] = image[x-1, y] - image[x, y]
diff[4] = image[x, y] - image[x, y]
diff[5] = image[x+1, y] - image[x, y]
diff[6] = image[x-1, y+1] - image[x, y]
diff[7] = image[x, y+1] - image[x, y]
diff[8] = image[x+1, y+1] - image[x, y]
# 周辺画素の方が高ければ1,そうでなければ0にする。
diff_new = [1 if diff[i] > 0 else 0 for i in range(0, 9)]
# それぞれの位置の0,1に重みをかけて足す
LBP = 0
for j in range(0, 9):
LBP += diff_new[j] * (2 ** j)
# 元々の中心画素の画素値に、LBPを代入する。
image[x, y] = LBP
return image
これだけです。あとは用意した画像を関数にかけて表示するだけです。
LBP_image = LBP(image_gray_array)
plt.imshow(LBP_image, cmap='Greys')
このような不思議な画像ができました。これがテクスチャ情報のようです。
続いて、LBPは「輝度の変化に強い」とのことなので、それも実験で確認してみます。まず先程の画像の各画素値に100を加えて、より明るい画像を作成します。
image_gray_array2 = np.array(image_gray_array) + 100
plt.imshow(image_gray_array2, cmap='Greys')
続いて、この画像からLBPを算出すると、どのような画像が出力されるか確かめます。
LBP_image2 = LBP(image_gray_array2)
plt.imshow(LBP_image2, cmap='Greys')
同じなんですよね~。目視だと本当に同じか確認できないので、数値上も本当に同じか確かめます。
np.array_equal(LBP_image, LBP_image2)
出力:True
ということで、シンプルなのにテクスチャの情報を表現できて輝度の変化にも強い、万能(?)なLBPの紹介でした。
おわりに
画像処理めちゃくちゃ楽しいですね。特に古典手法はロマンがあってワクワクします。GANとかも素晴らしいんですけどね。沼にハマってしまいそうです。
それではまた。
参考
・『画像認識』講談社(MLP 機械学習プロフェッショナルシリーズ) 著:原田達也
・局所バイナリパターンについて
https://qiita.com/tancoro/items/959ae9c14048c06bea8e
・記述子の定義
https://graziegrazie.hatenablog.com/entry/2019/03/16/084321