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
との画素値の差が指定範囲内の画素は連結成分に追加します。
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
塗りつぶしを行う
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()
の結果として受け取ります。そのために、flags
に cv2.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);
-
前の記事
OpenCV – cv2.HoughCircles で円を検出する方法 2020.09.01
-
次の記事
OpenCV – cv2.watershed で繋がっている輪郭を分離する方法 2020.09.01