web-dev-qa-db-ja.com

OpenCV-マスクをカラー画像に適用

最新のpythonバインディング(cv2)でカラー画像にマスクを適用するにはどうすればよいですか?前のpythonバインディングで最も簡単な方法は_cv.Copy_を使用することでした例えば.

cv.Copy(dst, src, mask)

ただし、この関数はcv2バインディングでは使用できません。定型コードを使用せずに回避策はありますか?

31
pzo

ここでは、cv2.bitwise_and関数は、既にマスクイメージを持っている場合。

以下のコードを確認するには:

img = cv2.imread('lena.jpg')
mask = cv2.imread('mask.png',0)
res = cv2.bitwise_and(img,img,mask = mask)

出力は、lenaイメージおよび矩形マスクの場合、次のようになります。

enter image description here

51
Abid Rahman K

さて、ここで背景を黒一色以外にしたい場合の解決策があります。マスクを反転し、同じサイズの背景画像に適用するだけですそして、背景と前景の両方を組み合わせます。このソリューションの長所は、背景が何でも(他の画像でも)できることです。

この例は Hough Circle Transform から変更されています。最初の画像はOpenCVロゴ、2番目は元のマスク、3番目は背景と前景を組み合わせたものです。

apply mask and get a customized background

# http://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_imgproc/py_houghcircles/py_houghcircles.html
import cv2
import numpy as np

# load the image
img = cv2.imread('E:\\FOTOS\\opencv\\opencv_logo.png')
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

# detect circles
gray = cv2.medianBlur(cv2.cvtColor(img, cv2.COLOR_RGB2GRAY), 5)
circles = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, 1, 20, param1=50, param2=50, minRadius=0, maxRadius=0)
circles = np.uint16(np.around(circles))

# draw mask
mask = np.full((img.shape[0], img.shape[1]), 0, dtype=np.uint8)  # mask is only 
for i in circles[0, :]:
    cv2.circle(mask, (i[0], i[1]), i[2], (255, 255, 255), -1)

# get first masked value (foreground)
fg = cv2.bitwise_or(img, img, mask=mask)

# get second masked value (background) mask must be inverted
mask = cv2.bitwise_not(mask)
background = np.full(img.shape, 255, dtype=np.uint8)
bk = cv2.bitwise_or(background, background, mask=mask)

# combine foreground+background
final = cv2.bitwise_or(fg, bk)

注:opencvメソッドは最適化されているため、使用することをお勧めします。

13
lmiguelmh
import cv2 as cv

im_color = cv.imread("lena.png", cv.IMREAD_COLOR)
im_gray = cv.cvtColor(im_color, cv.COLOR_BGR2GRAY)

この時点で、色とグレーの画像ができました。ここでは8-bituint8画像を扱っています。つまり、画像は[0, 255]の範囲のピクセル値を持つことができ、値は整数でなければなりません。

left-color,right-gray

バイナリしきい値処理を行いましょう。マスクされた画像を作成します。黒い領域の値は0で、白い領域の値は255です

_, mask = cv.threshold(im_gray, thresh=180, maxval=255, type=cv.THRESH_BINARY)
im_thresh_gray = cv.bitwise_and(im_gray, mask)

バイナリマスクは左下にあります。右の画像は、グレーの画像とマスクの間にbitwise_and操作を適用した結果です。起こったのは、マスクのピクセル値がゼロ(黒)であった空間位置が、結果イメージのピクセル値ゼロになったことです。マスクのピクセル値が255(白)であった場所、結果の画像は元のグレー値を保持していました。

left-mask,right-bitwise_and_with_mask

このマスクを元のカラー画像に適用するには、元のカラー画像が3チャンネル画像であるため、マスクを3チャンネル画像に変換する必要があります。

mask3 = cv.cvtColor(mask, cv.COLOR_GRAY2BGR)  # 3 channel mask

次に、同じbitwise_and関数を使用して、このマスクを元のカラー画像に適用できます。

im_thresh_color = cv.bitwise_and(im_color, mask3)

コードのmask3は左下の画像で、im_thresh_colorはその右にあります。

left-mask-3channel,right-bitwise_and_with_3channel-mask

結果をプロットして、自分で確認できます。

cv.imshow("original image", im_color)
cv.imshow("binary mask", mask)
cv.imshow("3 channel mask", mask3)
cv.imshow("im_thresh_gray", im_thresh_gray)
cv.imshow("im_thresh_color", im_thresh_color)
cv.waitKey(0)

元の画像は、lenacolor.pngでした ここ を見つけました。

3
Sounak

説明されている他の方法では、バイナリマスクを想定しています。実数値の単一チャネルグレースケールイメージをマスクとして(たとえば、アルファチャネルから)使用する場合、3つのチャネルに展開し、補間に使用できます。

_assert len(mask.shape) == 2 and issubclass(mask.dtype.type, np.floating)
assert len(foreground_rgb.shape) == 3
assert len(background_rgb.shape) == 3

alpha3 = np.stack([mask]*3, axis=2)
blended = alpha3 * foreground_rgb + (1. - alpha3) * background_rgb
_

操作を成功させるには、maskが_0..1_の範囲にある必要があることに注意してください。また、_1.0_はフォアグラウンドのみを保持してエンコードし、_0.0_はバックグラウンドのみを保持することを意味します。

マスクの形状が_(h, w, 1)_の可能性がある場合、これは役立ちます。

_alpha3 = np.squeeze(np.stack([np.atleast_3d(mask)]*3, axis=2))
_

ここで、np.atleast_3d(mask)は、マスクが_(h, w, 1)_である場合、マスクを_(h, w)_にし、np.squeeze(...)は、結果を_(h, w, 3, 1)_から_(h, w, 3)_に再整形します。

3
sunside

Abid Rahman Kの回答は完全に正しいものではありません。私も試してみましたが、とても役に立ちましたが、行き詰まりました。

これは、指定されたマスクで画像をコピーする方法です。

x, y = np.where(mask!=0)
pts = Zip(x, y)
# Assuming dst and src are of same sizes
for pt in pts:
   dst[pt] = src[pt]

これは少し遅いですが、正しい結果が得られます。

編集:

Pythonicな方法。

idx = (mask!=0)
dst[idx] = src[idx]
0
Froyo