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$.
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.
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”
Wow, great blog. Really looking forward to read more. Fantastic. Elladine Car Modestia
I am very happy and motivated by the feedback. Thank you very much
Thank you very much
I visit everyday a few web sites and websites to read articles, however this weblog offers quality based content. Ninon Dickie Cutcliffe
Hello, I was very happy with your comment, thank you very much.
Hi there, I would like to subscribe for this weblog to get latest updates, so where can i do it please assist. Anestassia Elmer Kieran
Hello, I still don’t have a list for update notice, I post notice on my instagram and linkedin.
Nice answer back in return of this query with solid arguments and explaining everything about that. Cassi Garfield Darin
Some genuinely nice and utilitarian information on this internet site, also I conceive the style and design has superb features. Novelia Ilaire Eldwun
Thank you very much.
I love your writing style truly rrot loving this internet site . Ana Nikita Hakim
hello, Thank you very much.
There is perceptibly a bundle to realize about this. I believe you made various good points in features also. Bonita Woodrow Shimberg
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
thank you Anne
I value the blog article. Really thank you! Really Great. Ynes Wake Cosetta
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.