Plus tôt, j'ai écrit un article comme celui-ci (reconnaissons les objets rouges avec python) (http://qiita.com/odaman68000/items/ae28cf7bdaf4fa13a65b). C'est une méthode pour convertir simplement une image en HSV (Teinte, Saturation, Valeur) et trouver une région avec une forte composante rouge.
Cette fois, considérons un objet "vert" comme une application. Et puis je parlerai de "tracking" des objets dans un flux vidéo continu. Si vous regardez de près le flux vidéo continu, il s'agit d'une image fixe continue. Si vous continuez à reconnaître l'objet vert à partir de cette image fixe, vous pouvez le suivre naturellement. Ce n'est pas une grosse erreur de penser cela. Cependant, il existe une caméra différente de l'œil humain. Si l'angle de l'objet cible dévie un peu, il brillera en blanc en fonction de la quantité de lumière et s'écartera du jugement (le jugement échoue et vous le perdez de vue), le point de jugement vole, ou vous le perdez de vue même si la lumière ambiante change un peu. Il est. Que se passe-t-il si de mauvaises conditions se chevauchent et que vous la perdez de vue? L'ordinateur déterminera que l'objet est "absent". Mais dans l'instant suivant, les conditions peuvent s'améliorer et réapparaître. Il apparaît et disparaît, il réapparaît, et il n'y a aucun sentiment de stabilité, et une précision de détection de 100% ne peut pas toujours être attendue.
Par conséquent, il est nécessaire d'abandonner l'idée qu'une image fixe doit être analysée à chaque fois même s'il s'agit d'un flux vidéo, et de reconsidérer le flux vidéo comme une "image fixe continue". En d'autres termes, il devrait être prédit et recherché en utilisant pleinement l'idée de probabilité et de statistique selon laquelle "il devrait être ici parce que c'était ici la dernière fois".
Il existe de bonnes façons de résoudre ces problèmes. C'est ce qu'on appelle un "filtre à particules". Je n'en sais pas assez pour expliquer la définition académique et le contenu du filtre à particules, alors j'aimerais laisser cela à de nombreuses autres pages de commentaires. Cependant, sur le plan conceptuel, l'analyse d'image à l'aide d'un filtre à particules
Si vous continuez ces procédures 1 à 5, vous pourrez naturellement réaliser ce qui précède. L'état "J'étais ici la dernière fois" est rappelé par les particules dispersées, et pour chaque particule, "Est-ce de ce côté-ci?" Les particules pauvres qui continuent de nuire à leurs prédictions seront éliminées. Les particules prédisent désespérément l'image suivante et survivent.
Il est assez difficile de décrire ces séries de processus en C / C ++, mais principalement en raison de la puissance de numpy, c'est un grand tokoro de python + numpy qu'une petite quantité de code est nécessaire.
Tout d'abord, à propos de l'importation. On suppose que vous avez importé comme suit.
import cv2
import numpy as np
Tout d'abord, définissez la structure des particules. Faisons comme suit. «x» et «y» sont les positions des particules et «poids» est la probabilité (poids).
particle = [x, y, weight]
Ensuite, créez une fonction pour calculer la probabilité (poids) des particules.
Cette fonction scanne une plage de 30x30 pixels autour des coordonnées spécifiées et renvoie le pourcentage de 900 pixels qui répondent aux critères. Par exemple, si la plage de 30 x 30 pixels est entièrement NG, une valeur de 0,0, si tout est OK, une valeur de 1,0 et si environ la moitié est OK, une valeur d'environ 0,5 est renvoyée.
La fonction de décision func ()
est désactivée et permet à l'appelant de spécifier la fonction de décision.
def likelihood(x, y, func, image, w=30, h=30):
x1 = max(0, x - w / 2)
y1 = max(0, y - h / 2)
x2 = min(image.shape[1], x + w / 2)
y2 = min(image.shape[0], y + h / 2)
region = image[y1:y2, x1:x2]
count = region[func(region)].size
return (float(count) / image.size) if count > 0 else 0.0001
Vient ensuite la fonction d'initialisation des particules.
Le paramètre func
est censé spécifier la fonction de décision à sortir comme auparavant.
Cette fonction crée beaucoup (500) de ces particules.
Comme valeur initiale, les particules sont positionnées près de la plus grande zone de la zone déterminée par la fonction func ()
.
Le "500" de "(500, 3)" spécifié dans l'appel de "np.ndarray ()" est le nombre de particules, et "3" est le nombre d'éléments des "x", "y", "poids" ci-dessus. est.
def init_particles(func, image):
mask = image.copy()
mask[func(mask) == False] = 0
contours, _ = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
if len(contours) <= 0:
return None
max_contour = max(contours, key=cv2.contourArea)
max_rect = np.array(cv2.boundingRect(max_contour))
max_rect = max_rect[:2] + max_rect[2:] / 2
weight = likelihood(max_rect[0], max_rect[1], func, image)
particles = np.ndarray((500, 3), dtype=np.float32)
particles[:] = [max_rect[0], max_rect[1], weight]
return particles
C'est la partie du mouvement du filtre à particules dans le vrai sens du terme. Le filtre à particules passe par les quatre procédures suivantes.
Lors du rééchantillonnage, en utilisant des nombres aléatoires, les particules de mauvaise qualité sont éliminées et remplacées par des particules de bonne qualité.
La méthode cumsum ()
utilisée pour créer le tableau weights
calcule la somme cumulée.
De plus, en définissant (poids> poids) .argmax ()
, le tableau poids
sera analysé et l'index du tableau sera renvoyé lorsqu'une valeur supérieure à poids
apparaît pour la première fois.
def resample(particles):
tmp_particles = particles.copy()
weights = particles[:, 2].cumsum()
last_weight = weights[weights.shape[0] - 1]
for i in xrange(particles.shape[0]):
weight = np.random.rand() * last_weight
particles[i] = tmp_particles[(weights > weight).argmax()]
particles[i][2] = 1.0
En fait, déplacez les particules vers l'image suivante. Ajoutez le coefficient spécifié par «variance» multiplié par le résultat de «numpy.random.randn ()». Ce coefficient de «variance» est une valeur numérique qui doit être réglée en fonction de l'intensité du mouvement de la cible. Le mouvement aléatoire des particules est appelé «prédiction». Les particules qui se déplacent dans la bonne direction survivent, et les particules qui se déplacent dans la mauvaise direction sont destinées à être éliminées (écrasées).
def predict(particles, variance=13.0):
particles[:, 0] += np.random.randn((particles.shape[0])) * variance
particles[:, 1] += np.random.randn((particles.shape[0])) * variance
Déterminez la probabilité (poids) de chaque particule. Ce sera le matériau pour juger du résultat de la prédiction précédente. Cette vraisemblance (poids) est calculée en appelant la fonction «vraisemblance ()» créée précédemment.
def weight(particles, func, image):
for i in xrange(particles.shape[0]):
particles[i][2] = likelihood(particles[i][0], particles[i][1], func, image)
sum_weight = particles[:, 2].sum()
particles[:, 2] *= (particles.shape[0] / sum_weight)
Mesurez les particules pour savoir où sont concentrées les particules les plus performantes.
def measure(particles):
x = (particles[:, 0] * particles[:, 2]).sum()
y = (particles[:, 1] * particles[:, 2]).sum()
weight = particles[:, 2].sum()
return x / weight, y / weight
Enfin, le traitement mis en œuvre jusqu'à présent se résume à une fonction d'utilité.
Pendant le max_frame
spécifié par l'argument, si aucun composant vert n'est trouvé, les particules sont réinitialisées.
particle_filter_cur_frame = 0
def particle_filter(particles, func, image, max_frame=10):
global particle_filter_cur_frame
if image[func(image)].size <= 0:
if particle_filter_cur_frame >= max_frame:
return None, -1, -1
particle_filter_cur_frame = min(particle_filter_cur_frame + 1, max_frame)
else:
particle_filter_cur_frame = 0
if particles is None:
particles = init_particles(func, image)
if particles is None:
return None, -1, -1
resample(particles)
predict(particles)
weight(particles, func, image)
x, y = measure(particles)
return particles, x, y
Alors, créons un programme qui suit les objets verts en utilisant les filtres à particules que nous avons implémentés jusqu'à présent.
Ici, les données de trame (image BGR) acquises par cv2.VideoCapture ()
sont converties en HSV, et S (Saturation) et V (Valeur) sont multipliées par le seuil de OTSU, respectivement, et la profondeur de couleur et la luminosité sont multipliées. Dans les deux cas, le processus consistant à n'utiliser que suffisamment de pixels est exécuté (la composante H est masquée par des pixels de passage de S et V et remplie de 0).
Je veux trouver la plage de vert (50-85), donc effacer 0 est raisonnable. (Par contre, lors de la détection du rouge, il sera étrange que vous ne le remplissiez pas avec une valeur autre que 0.)
Ce qui est réellement passé à la fonction particule_filter ()
est le composant H qui a été filtré ci-dessus. Après cela, je pense que la fonction particule_filter ()
gère les particules et suit bien la partie verte.
import cv2
import numpy as np
if __name__ == "__main__":
def is_green(region):
return (region >= 50) | (region < 85)
cap = cv2.VideoCapture(0)
particles = None
while cv2.waitKey(30) < 0:
_, frame = cap.read()
frame_hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV_FULL)
frame_h = frame_hsv[:, :, 0]
_, frame_s = cv2.threshold(frame_hsv[:, :, 1], 0, 255, cv2.THRESH_BINARY|cv2.THRESH_OTSU)
_, frame_v = cv2.threshold(frame_hsv[:, :, 2], 0, 255, cv2.THRESH_BINARY|cv2.THRESH_OTSU)
frame_h[(frame_s == 0) | (frame_v == 0)] = 0
particles, x, y = particle_filter(particles, is_green, frame_h)
if particles is not None:
valid_particles = particles[(particles[:, 0] >= 0) & (particles[:, 0] < frame.shape[1]) &
(particles[:, 1] >= 0) & (particles[:, 1] < frame.shape[0])]
for i in xrange(valid_particles.shape[0]):
frame[valid_particles[i][1], valid_particles[i][0]] = [255, 0, 0]
p = np.array([x, y], dtype=np.int32)
cv2.rectangle(frame, tuple(p - 15), tuple(p + 15), (0, 0, 255), thickness=2)
cv2.imshow('green', frame)
cap.release()
cv2.destroyAllWindows()
Ce filtre à particules est extrêmement résistant au bruit et aux mouvements et chassera un objet de manière assez persistante.
De plus, la position de détection obtenue en conséquence est également très stable.
Une fois que vous avez implémenté le filtre à particules, je pense que la clé est de savoir comment implémenter la fonction likelihood ()
. La vie et la mort des particules dépendent de cette «vraisemblance ()».
Observez le mouvement désespéré des particules pour survivre. Peu à peu, le désespoir de chaque particule, qui est impitoyablement évalué par «vraisemblance ()», devient intéressant. C'est un filtre à particules qui suit avec précision, mais dans l'ombre de celui-ci, d'innombrables particules naissent et meurent, et vous pouvez ressentir une telle douleur.
Recommended Posts