web-dev-qa-db-fra.com

Équivalent de copyTo dans les liaisons OpenCV Python?

OpenCV possède la fonction copyTo, qui permet de copier une zone masquée d’un Mat à un autre. 

http://docs.opencv.org/3.1.0/d3/d63/classcv_1_1Mat.html#a4331fa88593a9a9c14c0998574695ebb

Quel est l'équivalent de cela dans les liaisons Python? J'aimerais copier une zone d'une image sur une autre image, avec un masque binaire. 

10
mikevanis

cv::Mat::copyTo effectue l'une des deux opérations selon que la matrice de sortie a été initialisée ou non. Si votre matrice de sortie n'est pas initialisée, l'utilisation de copyTo avec un masque crée une nouvelle matrice de sortie du même type que l'entrée et toutes les valeurs sont définies sur 0 sur tous les canaux. Une fois que cela se produit, les données d'image définies par le masque sont copiées et le reste de la matrice est défini sur 0. Si votre matrice de sortie est initialisée et contient déjà du contenu, copyTo copie sur les pixels qui sont défini dans le masque à partir de la source et laisse les pixels qui ne faisaient pas partie du masque intactes dans la destination. Par conséquent, un remplacement des pixels définis par le masque de l'image source est copié dans la sortie.

Comme OpenCV utilise maintenant numpy pour s’interfacer avec la bibliothèque, il est très facile de faire l’une ou l’autre des méthodes. Pour différencier l'autre réponse vue dans cet article, la première méthode peut être accomplie en multipliant simplement le masque avec l'image de manière élémentaire. En supposant que votre entrée s'appelle img et que votre masque binaire s'appelle mask, où je suppose que le masque est en 2D, procédez comme suit:

import numpy as np
import cv2

mask = ... # define mask here
img = cv2.imread(...) # Define input image here

# Create new image
new_image = img * (mask.astype(img.dtype))

Le code ci-dessus suppose cependant que img et mask partagent le même nombre de canaux. Cela devient difficile si vous utilisez une image couleur comme source et le masque 2D comme je l’avais déjà supposé. Par conséquent, le nombre total de canaux est 2 et non 3 et la syntaxe ci-dessus vous donnera une erreur car les dimensions entre les deux ne sont plus compatibles. Vous devrez vous adapter à cela lorsque vous utilisez des images couleur. Vous pouvez le faire en ajoutant une troisième dimension singleton au masque afin que la diffusion puisse être exploitée.

import numpy as np
import cv2

mask = ... # define mask here
img = cv2.imread(...) # Define input image here

# Create new image
# Case #1 - Other image is grayscale and source image is colour
if len(img.shape) == 3 and len(mask.shape) != 3:
    new_image = img * (mask[:,:,None].astype(img.dtype))
# Case #2 - Both images are colour or grayscale
Elif (len(img.shape) == 3 and len(mask.shape) == 3) or \
   (len(img.shape) == 1 and len(mask.shape) == 1):
    new_image = img * (mask.astype(img.dtype))
# Otherwise, we can't do this
else:
    raise Exception("Incompatible input and mask dimensions")

Pour la deuxième approche, supposons que nous ayons une autre image appelée other_image dans laquelle vous souhaitez copier le contenu de cette image définie par votre masque vers l'image cible img. Dans ce cas, commencez par déterminer tous les emplacements du masque non nuls à l’aide de numpy.where , puis utilisez-les pour indexer ou découper dans votre image ainsi que vers l’endroit où vous souhaitez effectuer la copie. Nous devons également être conscients du nombre de canaux entre les deux images, comme avec la première approche:

import numpy as np
import cv2

mask = ... # define mask here
img = cv2.imread(...) # Define input image here
other_image = cv2.imread(...) # Define other image here

locs = np.where(mask != 0) # Get the non-zero mask locations

# Case #1 - Other image is grayscale and source image is colour
if len(img.shape) == 3 and len(other_image.shape) != 3:
    img[locs[0], locs[1]] = other_image[locs[0], locs[1], None]
# Case #2 - Both images are colour or grayscale
Elif (len(img.shape) == 3 and len(other_image.shape) == 3) or \
   (len(img.shape) == 1 and len(other_image.shape) == 1):
    img[locs[0], locs[1]] = other_image[locs[0], locs[1]]
# Otherwise, we can't do this
else:
    raise Exception("Incompatible input and output dimensions")

Voici un exemple exécuté pour les deux approches. Je vais utiliser l'image Cameraman qui est une image test standard utilisée dans la plupart des algorithmes de traitement d'image. 

 enter image description here

J'ai également créé artificiellement la couleur de l'image, même si elle est visualisée en niveaux de gris, mais les intensités seront copiées sur tous les canaux. Je vais aussi définir un masque qui est simplement la sous-région 100 x 100 en haut à gauche et nous allons donc créer une image de sortie qui ne copie que cette sous-région:

import numpy as np
import cv2

# Define image
img = cv2.imread("cameraman.png")

# Define mask
mask = np.zeros(img.shape, dtype=np.bool)
mask[:100, :100] = True

Lorsque vous utilisez la première méthode et que nous affichons les résultats, nous obtenons:

 enter image description here

Nous pouvons voir que nous avons créé une image de sortie dans laquelle la sous-région supérieure gauche 100 x 100 contient nos données d'image, le reste des pixels étant défini sur 0. Cela est soumis aux emplacements de masque définis à True. Pour la deuxième approche, nous allons créer une autre image aléatoire de la même taille que l’image d’entrée qui s'étend de [0, 255] pour tous les canaux.

# Define other image
other_image = (255*np.random.Rand(*img.shape)).astype(np.uint8)

Une fois que nous avons parcouru le code avec la deuxième approche, j'obtiens cette image maintenant:

 enter image description here

Comme vous pouvez le constater, le coin supérieur gauche de l'image a été mis à jour en fonction des emplacements de masque définis sur True.

12
rayryeng

Notez bien si c'est exactement ce que vous voulez, mais pour copier avec des masques en Python, j'utiliserais cv2.bitwise_

new_image = cv2.bitwise_and(old_image,binary_mask)
3
Soltius