ビットマップ画像の周期的境界条件下での解析¶

このチュートリアルでは、ビットマップ画像を周期的境界条件下で解析する方法について解説します。

このノートブックと同じディレクトリに periodic-binary-image.png というファイルが保存されています。この画像を対象に解析を行っていきます。

画像を表示します。

In [2]:
from IPython.display import display, Image
In [3]:
display(Image("periodic-binary-image.png"))
No description has been provided for this image

上下の境界が周期的に接続されると、白い円が現れるような構造になっていることが分かります。

画像の読み込み¶

imageioを使って画像を読み込みます。

In [4]:
import imageio
In [5]:
image = imageio.v3.imread("periodic-binary-image.png", mode="L")

画像のサイズと画素値のヒストグラムを確認します。

In [6]:
image.shape
Out[6]:
(128, 128)
In [7]:
import matplotlib.pyplot as plt
import numpy as np
In [8]:
plt.hist(np.ravel(image), range=(0,256), bins=256); None
No description has been provided for this image

このチュートリアルでは黒い領域に注目しましょう。

128より小さいピクセルを抽出します。

In [9]:
bitmap = image < 128

周期境界条件を指定しない場合¶

ここでは、周期境界条件を考慮せずにパーシステンス図を計算します。 処理内容は、二値画像のチュートリアルと同様です。

計算されたパーシステンス図は bitmap.idiagram というファイルに保存されます。

In [10]:
import homcloud.interface as hc
In [11]:
hc.PDList.from_bitmap_levelset(hc.distance_transform(bitmap, signed=True), save_to="bitmap.pdgm")
Out[11]:
PDList(path=bitmap.pdgm)

ファイルからパーシステンス図を読み出します。

In [12]:
pdlist = hc.PDList("bitmap.pdgm")

パーシステンス図の可視化¶

周期境界条件を指定しなかった場合のパーシステンス図を表示します。 このチュートリアルでは、1次元のパーシステンス図、つまり「孔」に着目して解析を行います。 ここでは、黒い領域を背景とし、その中に現れる白い孔(1次元の特徴)に注目しています。

周期境界条件を指定しないことで、画像の端に現れる構造が解析にどのような影響を与えるかを確認できます。

In [13]:
pd1 = pdlist.dth_diagram(1)
pd1.histogram(x_bins=28).plot(colorbar={"type":"log"})
No description has been provided for this image

(-6, 26)の位置に1つペアがあります。

In [14]:
pd1.pairs()
Out[14]:
[Pair(-6.0, 26.0)]

逆解析を行い、このペアに対応する構造を確認します。 可視化には death pixel を用いて、対応する特徴の位置を表示します。

In [15]:
death_pixel_image = hc.draw_birthdeath_pixels_2d(
    pd1.pairs(), image, draw_death=True
)
display(death_pixel_image)
No description has been provided for this image

このペアは中央の白い孔に対応していることがわかります。

周期境界条件を指定する場合¶

次に、周期境界条件を指定してパーシステンス図を計算します。

周期境界の指定には periodicity パラメーターを使用します。
このパラメーターは画像の次元数と同じ長さの配列で、各軸に対して周期的に接続するかどうかを True(周期あり)または False(周期なし)で指定します。

このチュートリアルでは対象の画像は2次元なので、periodicity には2要素の配列を渡します。
今回は Y 方向(上下)のみ周期的に接続するため、[True, False] を指定します。
なお、NumPy の2次元配列で画像を表現する場合、座標の順序が Y, X となることに注意してください。

この周期情報は、距離変換とパーシステンス図の計算の両方に必要です。
そのため、distance_transform 関数と PDList.from_bitmap_levelset 関数の両方に periodicity 引数を指定する必要があります。

なお、保存するファイル名に含まれる pbc は、Periodic Boundary Condition(周期境界条件)の略です。

In [16]:
periodicity=[True, False]

hc.PDList.from_bitmap_levelset(
    hc.distance_transform(bitmap, signed=True, periodicity=periodicity), 
    periodicity=periodicity, 
    save_to="bitmap_pbc.pdgm"
)
Out[16]:
PDList(path=bitmap_pbc.pdgm)
In [17]:
pdlist_pbc = hc.PDList("bitmap_pbc.pdgm")
pd1_pbc = pdlist_pbc.dth_diagram(1)

周期境界を指定した場合の1次元パーシステンス図を描画します。

In [18]:
pd1_pbc.histogram(x_bins=28).plot(colorbar={"type":"log"})
No description has been provided for this image
In [19]:
pd1_pbc.pairs()
Out[19]:
[Pair(-6.0, 25.0), Pair(-37.0, 26.0)]

周期境界を指定しない場合、パーシステンス図には (-6, 26) の1つのペアしか現れませんでした。 しかし、周期境界を指定すると、ペアが2つに増加します。

これは、画像の上下を周期的に接続することで、新たにもう1つの孔が現れたことを意味します。 つまり、この画像は周期境界を考慮することで、2つの独立した孔を持つ構造として認識されるのです。

それでは、逆解析によってそれぞれの孔が画像のどこに対応しているのか、具体的な位置を確認してみましょう。

In [20]:
death_pixel_image = hc.draw_birthdeath_pixels_2d(
    pd1_pbc.pairs(), image, draw_death=True
)
display(death_pixel_image)
No description has been provided for this image

以上でこのチュートリアルは終了です。