(著)山たー
ペイントソフトとかでの塗りつぶしってどういうアルゴリズムなのかと思い、実装してみました。今はもっと洗練されたアルゴリズムを用いていると思いますが、今回はWikipediaに書いてあった一番簡単な方法を使ってみました。
Flood-fill アルゴリズム
Wikipediaに書いてあったのは以下のようなアルゴリズムです。
Flood-fill (node, target-color, replacement-color):
1. If target-color is equal to replacement-color, return.
2. If the color of node is not equal to target-color, return.
3. Set the color of node to replacement-color.
4. Perform Flood-fill (one step to the south of node, target-color, replacement-color).
Perform Flood-fill (one step to the north of node, target-color, replacement-color).
Perform Flood-fill (one step to the west of node, target-color, replacement-color).
Perform Flood-fill (one step to the east of node, target-color, replacement-color).
5. Return.
これを実装します。実用性を考えないので、アニメーションにしてみました。
実装
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
arr = np.array([[1,1,1,1,1,1,1,1,1],
[1,0,0,0,1,0,0,0,1],
[1,0,0,0,1,0,0,0,1],
[1,0,0,1,0,0,0,0,1],
[1,1,1,0,0,0,1,1,1],
[1,0,0,0,0,1,0,0,1],
[1,0,0,0,1,0,0,0,1],
[1,0,0,0,1,0,0,0,1],
[1,1,1,1,1,1,1,1,1]])
fig = plt.figure(figsize=(3,3))
ims = [] # List to save frames
im = plt.imshow(arr, animated=True)
plt.clim(0,2) # 色の範囲を0~2に
ims.append([im])
def FloodFill(beginY, beginX, tcolor=0, rcolor=2):
"""
tcolor :target-color
rcolor :replacement-color
"""
idxc = arr[beginY, beginX]
if tcolor == rcolor:
return
else:
if not idxc == tcolor:
return
else:
if not idxc == rcolor:
arr[beginY, beginX] = rcolor
im = plt.imshow(arr, animated=True)
ims.append([im])
else:
return
if beginX + 1 < 9:
FloodFill(beginY+1, beginX) # South
if beginX - 1 >=0:
FloodFill(beginY-1, beginX) # North
if beginY + 1 < 9:
FloodFill(beginY, beginX+1) # East
if beginY - 1 >= 0:
FloodFill(beginY, beginX-1) # West
FloodFill(4,4)
ani = animation.ArtistAnimation(fig, ims)
ani.save('FloodFill.mp4', writer="ffmpeg")
plt.show()
実装には再帰関数を用いています。アニメーションにすると、どういう順番で実行されているかがよく分かります。
OpenCVでFlood-fillを用いる
自分で実装しなくても、OpenCVには cv2.floodFill があります。
コード
import numpy as np
import matplotlib.pyplot as plt
import cv2
arr = np.array([[1,1,1,1,1,1,1,1,1],
[1,0,0,0,1,0,0,0,1],
[1,0,0,0,1,0,0,0,1],
[1,0,0,1,0,0,0,0,1],
[1,1,1,0,0,0,1,1,1],
[1,0,0,0,0,1,0,0,1],
[1,0,0,0,1,0,0,0,1],
[1,0,0,0,1,0,0,0,1],
[1,1,1,1,1,1,1,1,1]])
h, w = arr.shape
mask = np.zeros((h+2, w+2), np.uint8)
# Floodfill from point (4, 4)
# 配列の値を2に置換する
cv2.floodFill(arr, mask, (4,4), 2)
fig = plt.figure(figsize=(3,3))
plt.imshow(arr)
plt.savefig("result.png")
結果
参考にしたサイト
・python - Flood fill a shape in a binary image - Stack Overflow
・Filling holes in an image using OpenCV ( Python / C++ ) | Learn OpenCV
実はマスク画像の生成のためにflood fillについて調べていました。
コメントをお書きください