蛇使いな彼女BLOG
【第103回】輝度とパターンマッチング①~輝度変換~
2024.04.19
皆さんこんにちは。今回は少し技術的な話をしましょう😇
去年もTensorの時系列予測やプーリングなど難しい技術について説明してきましたが、それに続く“パターンマッチング“についてです。
かれこれ6年この仕事を続けてきて最近思うことは、〇〇法と名前のついた技術は沢山ありますが、それらに共通する理論や計算式は一貫しているような気がします。
特に、予測とパターンマッチングに関しては似ている部分が多かったので、今回は代表としてZNCC(正規化相互相関)法によるテンプレートマッチングの理解を深めていきたいと思います。
Pythonの処理自体は画像認識の工程とほぼ同じなので馴染みのある人も多いかもしれません。ZNCCとCNNは画像認知と類似性を示すという意味で同じですが、一般的な方法を見ていると両者の違いは、未知のデータを判断するときワンクラスに対する相関係数を示すか(ZNCC)、マルチクラスに対する確率 (CNN)を示すかで異なっていると考えています。確率要素がDeepLearningと深く関わってきます。また、最近ではこの二つを融合した識別方法も開発されていますね。
前置きはこのくらいにして、演算に使用する画像として我が家のミニチュアダックス(チョコ)に協力して貰います。
画像認識系の操作をする場合、用意した写真をグレースケールに変換してから扱うのが基本ですが、この理由について輝度という成分が関係しているので、まず簡単に説明を挟んでおきます。
【画像認知と輝度】
私たちが普段見ている光景を思い浮かべてください。人間の視覚は周囲の明るさの変化に対して恒常性を持っている為、物体を正確に認識できます。対してディスプレイ上に写る物体はカメラやモニターを介した値であるため、実際の色彩とは異なったり(モニターによって表現出来る明るさに限界がある)、背景の光源が物体の見え方に影響を及ぼすこともありますね。ネットショッピングでよくある商品写真と実際に届いた実物で「全然違うやん!」みたいな感じです(笑)
加えて、屋内照明や屋外では太陽の角度によって明るさが異なる点や、季節や天気等の条件によって色の見え方が変化します。
輝度はこのような明るさを示す尺度であり、一般的なガンマ補正のかかったRGB成分からは以下式で表せます
Y=0.299R+0.587G+0.114B (※リニアRGBの場合比率が異なる)
変換式を見ると、赤緑青の中で最も明るさの感度が高いのは緑だと分かりますね。
画像認識技術は基本的に輝度を対象に設計されています。
どうしてなのか、理由はモトハシにもよく分からないのですが、カメラ、パソコン、プリンターなど・・・デジタル機械の間では互換出来る方法で色彩データのやりとりを行っている事を考えると、輝度が画像データ全般に共通する安定した概念だったのかも?
実際、特徴抽出のしやすさと計算コスト面で扱い易いのは事実です。
次に、上記変換式を使ってRGBから輝度値Yへ変換した結果を示します。
変換手順は以下コードを参考にしてください。
# coding:utf-8 import cv2 import numpy as np #AdobeCommunity #輝度Y=0.299R+0.587G+0.114B(写真にはガンマ補正が入っているものとして) photo='../Choco.jpg' img = cv2.imread(photo) h = img.shape[0] w = img.shape[1] #元の画像が大きすぎるのでサイズの縮小 img = cv2.resize(img,(int(w/4), int(h/4)))
>>> img.shape (756, 1008, 3) >>>
#輝度計算 Y=img*np.array([0.114,0.587,0.299]) #ガンマ補正済みとして計算(リニアRGBとは異なる) Y=Y[:,:,0]+Y[:,:,1]+Y[:,:,2] # *uint8 => 8bit(0~255)type * int8 => -128~127 Y=Y.astype(np.uint8)
>>> print(Y) [[129 134 139 ... 150 153 116] [135 132 131 ... 124 155 179] [136 127 125 ... 102 122 178] ... [166 173 176 ... 230 211 169] [193 195 188 ... 232 244 215] [203 192 188 ... 176 191 167]] >>>
Pythonの輝度計算に関して補足すると、変数imgはcv2.imreadで読み込みを行っているため、この時点でRGB成分がBGRの順番に置き換わっています。そのため各成分に係数を掛けるとき、順番に気をつけてください。また、計算後は元のuint8に戻します。そうしないと上手く画像の表示が出来ません。
また、輝度計算のもう一つの方法として以下のようにcv2.COLOR_BGR2GRAYを使うとより簡単にRGBから輝度値への変換が可能です。
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
>>> print(gray) [[130 135 140 ... 150 153 117] [136 133 132 ... 125 156 179] [137 128 126 ... 102 122 178] ... [167 174 176 ... 230 212 170] [193 195 188 ... 233 244 215] [204 192 188 ... 177 192 167]] >>>
このように二通りの方法があり、どちらを選んでも画像の出力上は同じです。
ただ、変換式を使った場合と、cv2.COLOR_BGR2GRAYで輝度値に変化した結果を比べると、ピクセル当たりの輝度値が1程度少ない特性があります。これについて、変換式の有効桁数もしくはfloat⇒uint8で型を変換したときに起こる誤差ではないかと考えています。大きな違いではないので許容範囲と考えて良いでしょう。