web-dev-qa-db-fra.com

OpenCV - Appliquer un masque à une image couleur

Comment appliquer un masque à une image couleur dans la dernière liaison Python (cv2)? Dans les précédentes liaisons python, le moyen le plus simple consistait à utiliser cv.Copy par ex.

cv.Copy(dst, src, mask)

Mais cette fonction n'est pas disponible dans cv2 binding. Existe-t-il une solution de contournement sans utiliser le code standard?

21
pzo

Ici, vous pouvez utiliser la fonction cv2.bitwise_and si vous avez déjà l'image du masque.

Pour vérifier le code ci-dessous:

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

La sortie sera comme suit pour une image en lena et pour un masque rectangulaire.

enter image description here

39
Abid Rahman K

Eh bien, voici une solution si vous voulez que l’arrière-plan soit autre que noir. Il suffit d’inverser le masque et de l’appliquer à une image d’arrière-plan de même taille, puis de combiner l’arrière-plan et le premier plan. Un pro de cette solution est que l’arrière-plan pourrait être n'importe quoi (même une autre image).

Cet exemple est modifié à partir de Hough Circle Transform . La première image est le logo OpenCV, la deuxième le masque d'origine, la troisième l'arrière-plan et l'avant-plan combinés.

 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)

Remarque: il est préférable d'utiliser les méthodes open source car elles sont optimisées.

10
lmiguelmh

Les autres méthodes décrites supposent un masque binaire. Si vous souhaitez utiliser une image en niveaux de gris monocanal à valeur réelle comme masque (par exemple, à partir d'un canal alpha), vous pouvez l'étendre à trois canaux, puis l'utiliser pour l'interpolation:

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

Notez que mask doit être dans la plage 0..1 pour que l'opération réussisse. Il est également supposé que 1.0 encode en gardant uniquement le premier plan, alors que 0.0 signifie ne conserver que l'arrière-plan.

Si le masque peut avoir la forme (h, w, 1), ceci aide:

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

Ici, np.atleast_3d(mask) crée le masque (h, w, 1) s'il est (h, w) et np.squeeze(...) remodèle le résultat de (h, w, 3, 1) à (h, w, 3).

3
sunside
import cv2 as cv

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

À ce stade, vous avez une image en couleur et grise. Nous traitons ici avec les images 8-bit, uint8. Cela signifie que les images peuvent avoir des valeurs de pixel dans la plage de [0, 255] et que les valeurs doivent être des entiers.

 left-color,right-gray

Faisons une opération de seuillage binaire. Il crée une image masquée. Les régions noires ont la valeur 0 et les régions blanches 255

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

Le masque binaire est visible en bas à gauche. L'image à sa droite est le résultat de l'application de l'opération bitwise_and entre l'image grise et le masque. Ce qui s’est passé est que les emplacements spatiaux où le masque avait une valeur de pixel zéro (noir), sont devenus une valeur de pixel zéro dans l’image obtenue. Les emplacements où le masque avait une valeur de pixel de 255 (blanc), l’image résultante conservait sa valeur de gris d’origine.

 left-mask,right-bitwise_and_with_mask

Pour appliquer ce masque à notre image couleur d'origine, nous devons convertir le masque en une image à 3 canaux, car l'image en couleur d'origine est une image à 3 canaux.

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

Ensuite, nous pouvons appliquer ce masque à notre image couleur d'origine en utilisant la même fonction bitwise_and.

im_thresh_color = cv.bitwise_and(im_color, mask3)

mask3 du code est l'image ci-dessous à gauche, et im_thresh_color est à sa droite.

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

Vous pouvez tracer les résultats et voir par vous-même.

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)

L'image originale est lenacolor.png que j'ai trouvé ici .

0
Sounak

La réponse donnée par Abid Rahman K n’est pas tout à fait correcte. J'ai aussi essayé et trouvé très utile, mais je suis resté coincé.

Voici comment je copie une image avec un masque donné.

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]

C'est un peu lent mais donne des résultats corrects.

MODIFIER:

Façon pythonique.

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