概要
画像の任意の座標が中心にくるように余白を追加(Padding)する方法を紹介する。
実装
Google Colabで作成した本記事のコードは、こちらにあります。
Paddingサイズを最小限に位置合わせ
画像の任意の座標が中心にくるように最小限のPaddingをする方法です。メリットは、最小限のPaddingのため、画像サイズが大きくなりにくいところです。
import numpy as np
import matplotlib.pyplot as plt
def trim_img_coord2centering(img, coord):
x_shift = img.shape[1]-1-2*coord[0]
y_shift = img.shape[0]-1-2*coord[1]
if x_shift > 0:
pad_x = (x_shift, 0)
else:
pad_x = (0, -x_shift)
if y_shift > 0:
pad_y = (y_shift, 0)
else:
pad_y = (0, -y_shift)
centering_img = np.pad(img, (pad_y, pad_x), 'constant')
return centering_img
img = np.arange(5*7).reshape(5, 7)
coord = (2, 1)
centering_img = trim_img_coord2centering(img, coord)
# グラフに表示
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(10, 5))
ax1.imshow(img)
ax1.scatter(*coord, c='r', label='coord')
ax1.legend()
ax2.imshow(centering_img)
ax2.scatter(centering_img.shape[1]//2, centering_img.shape[0]//2, c='r', label='coord2centering')
ax2.legend()
左図の赤点が中心に位置合わせする座標で、右図は中心に位置合わせした後の画像です。
- コードの簡単な説明
x_shift = img.shape[1]-1-2*coord[0]
で、画像の端の座標と任意の座標の2倍のした値を比較します。ここで、x座標はx_shift
が正であれば、任意の座標は画像サイズの半分よりも小さく、負であれば半分をより大きい座標であると判定できます。x_shift
の正負に合わせ、任意の座標が中心にくるようにpad_x
を定義します。y座標も同様に計算します。
数式による説明
簡単のため1次元配列で考えます。
img_{index} = [0, 1, 2, ..., i, ..., n-1]
$img_{index}$は画像のindexとします。座標$i$が中心となるようにPaddingします。コードに従い
shift = n-1-2i
とします。$shift>0$の時、$img_{index}$の左端に追加するのでPadding後の$centeringimg_{index}$は、
centeringimg_{index} = [0, 1, 2, ...,i+shift, ..., n-1+shift] = [0, 1, 2, ...,n-1-i, ..., 2n-2-2i]
となります。$centeringimg$の中心の座標のindexは、$centeringimg$が奇数なので最後のindexの半分となり、
\frac{2n-2-2i}{2}=n-1-i=i+shift
となります。これは$i$番目の座標の移動した値と等しいので$shift>0$の時に成り立ちます。
一方、$shift\leq 0$の時、$img_{index}$の右端に追加するのでPadding後の$centeringimg_{index}$は、
centeringimg_{index} = [0, 1, 2, ...,i, ..., n-1-shift] = [0, 1, 2, ...,i, ..., 2i]
となります。$centeringimg$の中心の座標のindexは、$centeringimg$が奇数なので最後のindexの半分となり、
\frac{2i}{2}=i
これは$i$番目の座標の移動した値と等しいので$shift\leq 0$の時も成り立ちます。従って、任意の$i$においてこのコードは成り立ちます。
出力画像サイズを固定して位置合わせ
出力画像が入力サイズによって一意に決まるコードになります。こちらはstack overflow: Rotate a 2D image around specified origin in Pythonを参考にしています。参考にした記事では、本記事で対応させるとpad_x = (img.shape[1] - coord[0], coord[0])
と同じ意味で書かれると思いますが、本記事では中心の座標を一意に決めたいため、pad_x = (img.shape[1] - coord[0]+1, coord[0]+1)
と書き換えています。参考にした記事では、出力サイズが入力サイズの2倍のため偶数となり中心の座標を1座標で表せないため、+1
しています。中心の座標を厳密に定義して実装したいかの違いです。
メリットは、コードがシンプルであるのと、複数の同じサイズの画像で中心を位置合わせした場合に画像毎に出力画像が一意に決まるため、管理がしやすいところです。
import numpy as np
import matplotlib.pyplot as plt
def fix_size_coord2centering(img, coord):
pad_x = (img.shape[1] - coord[0], coord[0]+1)
pad_y = (img.shape[0] - coord[1], coord[1]+1)
centering_img = np.pad(img, (pad_y, pad_x), 'constant')
return centering_img
img = np.arange(5*7).reshape(5, 7)
coord = (2, 1)
centering_img = fix_size_coord2centering(img, coord)
# グラフに表示
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(10, 5))
ax1.imshow(img)
ax1.scatter(*coord, c='r', label='coord')
ax1.legend()
ax2.imshow(centering_img)
ax2.scatter(centering_img.shape[1]//2, centering_img.shape[0]//2, c='r', label='coord2centering')
ax2.legend()
左図の赤点が中心に位置合わせする座標で、右図は中心に位置合わせした後の画像です。
- コードの簡単な説明
x座標のpad_x = (img.shape[1] - coord[0], coord[0]+1)
より、Paddingする数は、img.shape[1] - coord[0] + coord[0] + 1=img.shape[1] + 1となり、coord[0]
の値によらず常に一定であることが分かると思います。y軸も同様です。このコードは一見処理が分かりにくいと思いますが数式による説明で成り立つことを確認できます。
数式による説明
簡単のため1次元配列で考えます。
img_{index} = [0, 1, 2, ..., i, ..., n-1]
$img_{index}$は画像のindexとします。座標$i$が中心となるようにPaddingします。まず、$img_{index}$の左端に$n-i$個分追加すると、
処理後の$centeringimg_{index}$は、
centeringimg_{index} = [0, 1, 2, ...,i+(n-i), ..., n-1+(n-i)] = [0, 1, 2, ...,n, ..., 2n-1-i]
となります。次に、右端にも$i+1$個分追加すると、
centeringimg_{index} = [0, 1, 2, ...,n, ..., 2n-1-i+(i+1)] = [0, 1, 2, ...,n, ..., 2n]
となります。
\frac{2n}{2}=n
これは$i$番目の座標の移動した値と等しいので任意の$i$においてこのコードは成り立ちます。
補足
RGB画像の場合は、
centering_img = np.pad(img, (pad_y, pad_x, (0, 0)), 'constant')
とすると動きます。
まとめ
状況に合わせて好きなものを使いましょう。
参考資料