Rastreador de movimento

Particularmente gosto de trabalhar com processamento de vídeo porque isso envolve várias áreas da engenharia, além do processamento de imagem. podemos citar, física clássica, álgebra, controle e lógica de programação.

É claro, temos muito trabalho pela frente, mas ver o resultado de tudo isso funcionando não tem preço.

Um das principais etapas em rastreamento de objeto é a detecção. O sucesso nesse processo impacta diretamente o resultado. Por esse motivo sempre buscamos método que fornece uma boa predição.

Hoje, essa busca nos levará em alguma técnica baseada em Deep Learning. Entretanto, um vídeo tem milhares de imagens o que vai custar muito caro. Assim, geralmente fazermos uma predição e saltarmos alguns frames.

A predição é o ponto inicial, para não perder toda informação contida nos próximos frames vamos tentar prever a movimentação do objeto. Nos primeiros frames isso vai parecer muito bom, mais esses cálculos podem falhar, por isso depois de alguns frames realizamos uma predição novamente.

Existem várias técnicas para rastreamento. Hoje vamos falar do cálculo de fluxo óptico Sugerido por Lucas Kanada no seu artigo LINK AQUI.

O método de fluxo óptico tentam calcular o movimento entre dois quadros de imagem que são capturado nos tempos $t$ e $t +\Delta t$.

Os pixels que sofre variação em duas direções são os cantos. Quando a variação acontece em apenas uma direção, são as bordas. fonte: http://www.lps.usp.br/hae/projform/2010_gabriel_ramires/fluxo_optico.html

Na imagem abaixo mostramos o rastreamento de uma caneta, o retângulo em vermelho é um conjunto de pixeis selecionado para procurar no próximo frame.

Imagem anteior a esquerda no instante t e proxíma imagem a direita.

Supondo que nossa imagem seja definida por uma função.

$$I((x,y),t)$$

Onde, $x,y$ são as coordenada no plano 2D da imagem. Então o próximo frame pode ser definido como:

$$I((x,y)+\Delta (x,y),t+\Delta t)$$

Podemos dizer que o valor dos pixels no frame em $t$ é igual em $t +\Delta t$

$$I(p+\Delta p,t+\Delta t)=I(p,t)$$

O termo do lado esquerdo da equação pode ser aproximado por uma série de Taylor apenas com os dois primeiro termos. Como estamos fazendo uma aproximação linear a solução será restrita a um $\Delta$ pequeno,

$$I((x+\Delta x,y+\Delta y),t+\Delta t)=I((x,y),t)+\frac{\partial I}{\partial x}\Delta x+\frac{\partial I}{\partial y}\Delta y+\frac{\partial I}{\partial t}\Delta t$$

Considerando a equação anterior, isso só é valido se o termo adicional for igual a 0:

$$\frac{\partial I}{\partial x}\Delta x+\frac{\partial I}{\partial y}\Delta y+\frac{\partial I}{\partial t}\Delta t=0$$

Dividindo tudo por $\Delta t$:

$$\frac{\partial I}{\partial x}\frac{\Delta x}{\Delta t}+\frac{\partial I}{\partial y}\frac{\Delta y}{\Delta t}+\frac{\partial I}{\partial t}=0$$

A derivada parcial é as componentes do vetor gradiente da imagem naquela posição, portanto podemos calcular. As taxas de variações é a velocidade do deslocamento, que não sabemos.

$$\nabla I .\vec{v} = -\frac{\partial I}{\partial t}$$

Para resolver essa equação, basta pegar um conjunto de pixel. Assim, podemos descrever várias equações e montar um sistema possível e determinado (wikipedia).

A solução desse sistema é um vetor $\vec{m}=u+v$ que aponta para a nova posição daquele pixel no instante $t+ \Delta t$

Implementação

A parte legal, é que esse método já esta implementado no OpenCV (cv2.calcOpticalFlowPyrLK). Basicamente precisamos fornecer os parámetros solicitados.

Primeiro vamos criar um script em python para captura de vídeo da webcam.

import numpy as np
import cv2

cap=cv2.VideoCapture(0)

prev_gray = None
point_selected = False
point = ()
old_points = np.array([[]])
lk_params = dict(winSize=(21, 21),
                 maxLevel=5,
                 criteria=(cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 3, 0.01))

lk_params é um dicionário que contém os critérios de parada. Com exceção do winSize que é a região de pixel que será mapeada.

Aqui não vamos usar um algoritmo para predição, vamos determinar a posição inicial com o cursor do mouse. Assim você testar o rastreamento para qualquer objeto.

Para capturar a posição do mause vamos registrar uma função callback (callback é uma função cuja execução é disparada por um evento, toda vez que ocorrer um evento do mause (movimento click) nossa função mauser_point vai ser executada e se houver um click a variável point é atualizada.)

def mause_point(event, x, y, flags, params):
  global point, point_selected, old_points
  if event == cv2.EVENT_LBUTTONDOWN:
    point = (x, y)
    point_selected = True
    old_points = np.array([[x, y]], dtype=np.float32)

cv2.namedWindow("Frame")
cv2.setMouseCallback("Frame", mause_point)

Vamos criar um loop para percorrer os frames do vídeo. O cálculo de fluxo é realizado em imagem de duas dimensões, então precisamos converter para escala de cinza.

while cap.isOpened():
  _, frame = cap.read()
  curr_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
  if prev_gray is None:
    prev_gray=curr_frame.copy()
  if point_selected is True:
    new_points, status, error = cv2.calcOpticalFlowPyrLK(prev_gray, curr_frame, old_points, None, **lk_params)
    print(new_points,old_points)
    prev_gray = curr_frame.copy()
    old_points = new_points
    px,py=int(new_points[0][0]),int(new_points[0][1])
    cv2.circle(frame, (px,py), 2, (0, 255, 255),cv2.FILLED)
    cv2.rectangle(frame,(px-10,py-10),(px+10,py+10),(0,0,255),2)

    save.write_frame(frame)
    print(frame.shape)

  cv2.imshow("Frame",frame)
  k = cv2.waitKey(230)
  if k==ord("q"):
    save.close()
    break

Na linha 28 passamos os seguintes parametros

  • prev_gray: frame anterior (instante $t$)
  • curr_frame: frame no instante $t + \Delta t$
  • old_points: O ponto em prev_gray que precisa ser encontrado em curr_frame.
  • lk_params: critérios de paradas
  • new_point: É o ponto em curr_frame correspondente ao old_points.

Para cada ciclo, nos atualizamos old_points e prev_gray.

Considerações finais

Alguns pontos são melhores que outros para rastreamento. Pontos próximo de cantos, curvas e contornos.

Os pontos em supereficiente uniforme tende de sofrer desvios.

Existem alguns métodos para encontrar pontos ótimos. Porém, para rastreamento de alguns frames, uma abordagem mais simples pode funcionar..

Muito obrigado por chegar até aqui, se você encontrou algum erro nesse artigo, se ficou alguma dúvida, por favor deixe nos comentários.

17 Replies to “Rastreador de movimento”

  1. Hi! I just would like to offer you a huge thumbs up for your excellent information you have got right here on this post. I am returning to your web site for more soon. Anne-Corinne Phillipe Hernando

  2. Hello there! I just wish to offer you a big thumbs up for your excellent information you have here on this post.
    I’ll be coming back to your website for more soon.

Deixe um comentário

O seu endereço de email não será publicado. Campos obrigatórios marcados com *