web-dev-qa-db-fra.com

Accéder aux valeurs de pixels dans une limite de contour à l'aide d'OpenCV en Python

J'utilise OpenCV 3.0.0 sur Python 2.7.9. J'essaie de suivre un objet dans une vidéo avec un arrière-plan immobile et d'estimer certaines de ses propriétés. Puisqu'il peut y avoir plusieurs objets en mouvement dans une image, je souhaite pouvoir les différencier et les suivre individuellement tout au long des images restantes de la vidéo.

Une façon de le faire était de convertir l’image en binaire, d’obtenir les contours des blobs (objet suivi, dans ce cas) et d’obtenir les coordonnées du contour de l’objet. Ensuite, je peux accéder aux coordonnées de cette limite dans l'image en niveaux de gris, obtenir les intensités de pixels entourées par cette limite et suivre ce gradient de couleur/intensités de pixels dans les autres images. De cette façon, je pourrais garder deux objets séparés l'un de l'autre, afin qu'ils ne soient pas considérés comme de nouveaux objets dans l'image suivante.

J'ai les coordonnées de contour, mais je ne sais pas comment récupérer les intensités de pixels dans cette limite. Quelqu'un pourrait-il m'aider s'il vous plaît avec ça?

Merci!

9
Kaya311

En vous appuyant sur nos commentaires, vous pouvez créer une liste de tableaux numpy, où chaque élément correspond aux intensités décrivant l'intérieur du contour de chaque objet. Spécifiquement, pour chaque contour, créez un masque binaire qui remplit l'intérieur du contour, trouvez les coordonnées (x,y) de l'objet rempli, puis indexez-vous dans votre image et saisissez les intensités.

Je ne sais pas exactement comment vous avez configuré votre code, mais supposons que vous ayez une image en niveaux de gris appelée img. Vous devrez peut-être convertir l'image en niveaux de gris, car cv2.findContours fonctionne avec des images en niveaux de gris. Avec cela, appelez cv2.findContours normalement:

import cv2
import numpy as np

#... Put your other code here....
#....

# Call if necessary
#img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# Call cv2.findContours
contours,_ = cv2.findContours(img, cv2.RETR_LIST, cv2.cv.CV_CHAIN_APPROX_NONE)

contours est maintenant une liste de tableaux numpy 3D où chacun est de taille N x 1 x 2N est le nombre total de points de contour pour chaque objet.

En tant que tel, vous pouvez créer notre liste comme suit:

# Initialize empty list
lst_intensities = []

# For each list of contour points...
for i in range(len(contours)):
    # Create a mask image that contains the contour filled in
    cimg = np.zeros_like(img)
    cv2.drawContours(cimg, contours, i, color=255, thickness=-1)

    # Access the image pixels and create a 1D numpy array then add to list
    pts = np.where(cimg == 255)
    lst_intensities.append(img[pts[0], pts[1]])

Pour chaque contour, nous créons une image vierge puis dessinons le contour rempli dans cette image vierge. Vous pouvez renseigner la zone occupée par le contour en spécifiant le paramètre thickness sur -1. J'ai défini l'intérieur du contour sur 255. Ensuite, nous utilisons numpy.where pour trouver tous les emplacements de rangées et de colonnes dans un tableau qui correspondent à une certaine condition. Dans notre cas, nous voulons trouver les valeurs égales à 255. Après, nous utilisons ces points pour indexer dans notre image afin de saisir les intensités de pixels qui sont à l'intérieur du contour.

lst_intensities contient cette liste de tableaux 1D numpy où chaque élément vous donne les intensités qui appartiennent à l'intérieur du contour de chaque objet. Pour accéder à chaque tableau, faites simplement lst_intensities[i]i est le contour auquel vous souhaitez accéder.

15
rayryeng

La réponse de @rayryeng est excellente!

Une petite chose de mon implémentation est: La np.where() renvoie un Tuple, qui contient un tableau d’index de lignes et un tableau d’index de colonnes. Donc, pts[0] inclut une liste de row indices, qui correspond à la hauteur de l'image, pts[1] comprend une liste de column indices, qui correspond à la largeur de l'image. Le img.shape renvoie (rows, cols, channels). Je pense donc que ce devrait être img[pts[0], pts[1]] pour trancher la ndarray derrière l’img.

1
Jundong