概要
Pillowモジュールを使って画像をnearest neighborでresizeするとき、
どのピクセルから値を取得するのか(最近傍とするのか)を検証してみました。
各アルゴリズム(ニアレストネイバー、バイリニア、バイキュービック)で
画像がどのようになるのか(滑らかになるのかどうかなど)
について言及した記事はよく見ますが、
各ピクセルの値がどうなるのかについての情報が少なかったので書いてみました。
ニアレストネイバー(Nearest neighbor、最近傍補完)について
アルゴリズム自体は単純。
下のリンク先などが参考になります。
詳しい内容はここでは割愛しますが、
要は一番近い場所のピクセル値をそのまま持ってくるアルゴリズムです。
https://algorithm.joho.info/image-processing/nearest-neighbor-linear-interpolation/
検証してみる
画像サンプルの作成
検証用として画像サンプルを作ってみました
import numpy as np
from PIL import Image
import matplotlib.pyplot as plt
A = np.eye(10, dtype=int)
B = np.arange(10, dtype=int) * 20
C = Image.fromarray(B * A)
plt.figure()
plt.imshow(C)
print(np.array(C))
格納されている値は以下になります
[[ 0 0 0 0 0 0 0 0 0 0]
[ 0 20 0 0 0 0 0 0 0 0]
[ 0 0 40 0 0 0 0 0 0 0]
[ 0 0 0 60 0 0 0 0 0 0]
[ 0 0 0 0 80 0 0 0 0 0]
[ 0 0 0 0 0 100 0 0 0 0]
[ 0 0 0 0 0 0 120 0 0 0]
[ 0 0 0 0 0 0 0 140 0 0]
[ 0 0 0 0 0 0 0 0 160 0]
[ 0 0 0 0 0 0 0 0 0 180]]
対角の要素に0以外の値が入っているような感じですね
画像サンプルのリサイズ 10×10 ⇒ 5×5
この画像を、Pillowのresize(nearest neighbor)でリサイズしてみます
まず、10×10の元画像を5×5に圧縮します
C_nn5 = C.resize([5, 5], resample=Image.NEAREST)
plt.figure()
plt.imshow(C_nn5)
print(np.array(C_nn5))
[[ 20 0 0 0 0]
[ 0 60 0 0 0]
[ 0 0 100 0 0]
[ 0 0 0 140 0]
[ 0 0 0 0 180]]
ここで、値を見るとわかりますが、
最近傍はピクセルの「後寄り」の値(20)になっています。
例えばリサイズ後の[0, 0]ピクセルは、
元画像の[1, 1]ピクセルの値が格納されています。
また、リサイズ後の[1, 1]ピクセルは
元画像の[3, 3]ピクセルの値(60)が格納されています。
ところが参考にした数式とは合わない…?
数式通りだとピクセルの「前寄り」のはず
I'(0, 0) = I([0/0.5], [0/0.5]) = I(0, 0) = 0
I'(1, 1) = I([1/0.5], [1/0.5]) = I(2, 2) = 40
なので、
例えばリサイズ後の[0, 0]ピクセルは、
元画像の[0, 0]ピクセルの値(0)が格納されるはず
なぜかを解決してくれている記事がありました
https://qiita.com/yoya/items/3b4a8080516259ece684
(むしろこの記事書く意味あったんかという…)
なるほどピクセルのスタートが0.5に変更されたんですね…
そうすると、
I'(0, 0) = I([0.5/0.5], [0.5/0.5]) = I(1, 1) = 20
I'(1, 1) = I([1.5/0.5], [1.5/0.5]) = I(3, 3) = 60
で値がちゃんと合いました。
画像サンプルのリサイズ 10×10 ⇒ 3×3
[[ 20 0 0]
[ 0 100 0]
[ 0 0 160]]
I'(0, 0) = I([0.5/0.3], [0.5/0.3]) = I(2, 2) = 20
I'(1, 1) = I([1.5/0.3], [1.5/0.3]) = I(5, 5) = 100
I'(2, 2) = I([2.5/0.3], [2.5/0.3]) = I(8, 8) = 160
はい、問題なく値が合いました。
まとめ
nearest neighborでresizeの場合、
計算式から最近傍のピクセルを計算することができるが、
元画像は「0.5」開始であることに注意