Warning: Undefined variable $position in /home/pystyles/pystyle.info/public_html/wp/wp-content/themes/lionblog/functions.php on line 4897

OpenCV – floodFill() で指定した点と同じ色を塗りつぶす方法

OpenCV – floodFill() で指定した点と同じ色を塗りつぶす方法

概要

OpenCV の cv2.floodFill() で指定した点と同じ色を塗りつぶす方法について解説します。

Advertisement

cv2.floodFill

retval, image, mask, rect = cv2.floodFill(
    image, mask, seedPoint, newVal[, loDiff[, upDiff[, flags]]])
引数
名前 デフォルト値
image ndarray
1または3チャンネルで型が 8bit または浮動小数点数の画像
mask ndarray
image に1画素分パディングした型が 8bit の画像
seedPoint tuple of 2 ints
塗りつぶしを開始する画素
newVal int / tuple of ints
塗りつぶし後の値
loDiff int / tuple of ints 0 / (0, 0, 0)
連結成分を探す際の下限
upDiff int / tuple of ints 0 / (0, 0, 0)
連結成分を探す際の上限
flags int 4
塗りつぶし方法などを指定するフラグ
返り値
名前 説明
retval 塗りつぶした画素数
image 塗りつぶし後の画像
mask マスク
rect 連結成分に外接する長方形

塗りつぶしの仕組み

seedPoint に指定した画素と画素値の差が指定範囲内の画素を同じグループ (連結成分) と見なして塗りつぶしを行います。flags 引数で fixed range を指定したかどうかで連結成分を探す方法が異なります。

fixed range を指定した場合、seedPoint との画素値の差が指定範囲内の画素は連結成分に追加します。

$$ \text{src}({seedPoint}_x, {seedPoint}_y) − loDiff \le \text{src}(x, y) \le \text{src}({seedPoint}_x, {seedPoint}_y) + upDiff $$

fixed range を指定した場合、すでに見つかっている隣の連結成分との画素値の差が指定範囲内の画素は連結成分に追加します。

$$ \text{src}(x’, y’) – loDiff \le \text{src}(x, y) \le \text{src}(x’, y’) + upDiff $$

塗りつぶし方法

flags の指定方法

ビット列で塗りつぶし方法を設定します。

ビット 意味 許容値 デフォルト
1 ~ 8 4: 4近傍、8: 8近傍 {4, 8} 4
9 ~ 16 塗りつぶした画素に対応したマスクの位置の値も変更されるが、その値 [1, 255] 1
17 走査する際に塗りつぶすかどうか判定する基準 {0, 1} 1
18 1の場合、マスクのみ変更し、入力画像の塗りつぶしは行わない {0, 1} 0

フラグ指定方法の例

# 4連結、マスクを塗りつぶす値は255、fixed_range、mask_only 有効
flag = 4 | 255 << 8 | cv2.FLOODFILL_FIXED_RANGE | cv2.FLOODFILL_MASK_ONLY
Advertisement

塗りつぶしを行う

sample.jpg

In [1]:
import cv2
import numpy as np
from IPython.display import Image, display
from matplotlib import pyplot as plt


def imshow(img):
    """ndarray 配列をインラインで Notebook 上に表示する。
    """
    ret, encoded = cv2.imencode(".jpg", img)
    display(Image(encoded))


# 画像を読み込む。
img = cv2.imread("sample.jpg")

# 幅、高さとも2ピクセルずつ大きいサイズのマスクを作成する。
h, w = img.shape[:2]
mask = np.zeros((h + 2, w + 2), dtype=np.uint8)

# 塗りつぶす。
retval, img, mask, rect = cv2.floodFill(
    img,
    mask,
    seedPoint=(100, 100),
    newVal=(0, 0, 255),
    loDiff=(20, 20, 20),
    upDiff=(20, 20, 20),
    flags=4 | 255 << 8,
)
print("retval", retval)  # retval 6339
print("rect", rect)  # rect (49, 42, 90, 90)
imshow(mask)
imshow(img)
retval 6146
rect (50, 43, 88, 88)

青色の円が赤色に塗りつぶされました。マスクには塗りつぶした領域が255で表されています。

マスクを使用する

mask の画素値が0のピクセルのみが塗りつぶし対象なので、塗りつぶしの対象外としたい画素は0以外の値を mask に設定しておきます。 下記の例では、マスクに circle() で白い円を描画することで、その部分を塗りつぶしの対象から外しています。

In [2]:
# 画像を読み込む。
img = cv2.imread("sample.jpg")

# 幅、高さとも2ピクセルずつ大きいサイズのマスクを作成する。
h, w = img.shape[:2]
mask = np.zeros((h + 2, w + 2), dtype=np.uint8)
cv2.circle(mask, (150, 100), 70, color=100, thickness=-1)

# 塗りつぶす。
retval, img, mask, rect = cv2.floodFill(
    img,
    mask,
    seedPoint=(60, 100),
    newVal=(0, 0, 255),
    loDiff=(20, 20, 20),
    upDiff=(20, 20, 20),
    flags=4 | 255 << 8,
)

imshow(mask)
imshow(img)

cv2.floodFill() に渡したマスクの灰色の画素は塗りつぶされずに元の青色のままになっていることがわかります。

指定した色を透過する

塗りつぶす代わりに背景の1点を指定し、それと似た色を画素を cv2.floodFill() の結果として受け取ります。そのために、flagscv2.FLOODFILL_MASK_ONLYP フラグを指定します。

In [3]:
# 画像を読み込む。
img = cv2.imread("sample.jpg")

# 幅、高さとも2ピクセルずつ大きいサイズのマスクを作成する。
h, w = img.shape[:2]
mask = np.zeros((h + 2, w + 2), dtype=np.uint8)

# 塗りつぶしを実行する。
flags = 4 | 255 << 8 | cv2.FLOODFILL_MASK_ONLY
cv2.floodFill(
    img,
    mask,
    seedPoint=(2, 2),
    newVal=(0, 0, 255),
    loDiff=(20, 20, 20),
    upDiff=(20, 20, 20),
    flags=flags,
)

# マスク作成時に追加した周囲1ピクセルは除く
mask = mask[1:-1, 1:-1]

# アルファチャンネル追加する。
dst = cv2.cvtColor(img, cv2.COLOR_BGR2BGRA)

# マスクの値が 255 のアルファチャンネルは0にする。
dst[mask == 255] = 0
cv2.imwrite("result.png", dst);