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?
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.
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.
# 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.
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)
.
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.
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.
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.
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 .
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]