Flujo óptico Gunnar Farneback
El en tutorial anterior estudiamos el flujo óptico, vimos que es y mencionamos algunas de sus aplicaciones, además construimos una pequeña aplicación usando el método creado por Lucas-Kanade, esta vez veremos la función calcOpticalFlowFarneback(...)
la cual calcula el flujo óptico mediante el algoritmo propuesto por Gunnar Farneback en el año 2003, este método a diferencia del anterior calcula el flujo para todos los puntos en la imagen.
Para aplicar el algoritmo requerimos la imagen original y la anterior, ambas a escala de grises, este método puede ser más lento que el anterior al tener que calcular el flujo para todos los puntos en comparación con el anterior que utiliza un conjunto de puntos definido, el resultado es un arreglo de dos dimensiones que contiene los vectores que representan el desplazamiento de cada punto.
La función está definida de la siguiente manera:
void cv::calcOpticalFlowFarneback(
InputArray prev,
InputArray next,
InputOutputArray flow,
double pyr_scale,
int levels,
int winsize,
int iterations,
int poly_n,
double poly_sigma,
int flags
)
Loa parámetros prev, next, flow, establecen la imagen anterior, la imagen siguiente y el objeto que almacenará el resultado, este último es un arreglo de dos dimensiones que contiene los vectores que representan el flujo para cada punto, visita la documentación para obtener la descripción de los otros parámetros de la función cv::calcOpticalFlowFarneback.
Código de ejemplo, para el calculo de flujo óptico en tiempo real usando la webcam.
#include <iostream>
#include <opencv2\opencv.hpp>
using namespace cv;
using namespace std;
// Function to compute the optical flow map
void drawOpticalFlow(const Mat& flowImage, const Mat& flowImageGray)
{
int stepSize = 16;
Scalar color = Scalar(0, 255, 0);
// Draw the uniform grid of points on the input image along with the motion vectors
for (int y = 0; y < flowImageGray.rows; y += stepSize)
{
for (int x = 0; x < flowImageGray.cols; x += stepSize)
{
// Circles to indicate the uniform grid of points
circle(flowImageGray, Point(x, y), 1, color, FILLED);
// Lines to indicate the motion vectors
const Point2f& pt = flowImage.at<Point2f>(y, x);
line(flowImageGray, Point(x, y), Point(cvRound(x + pt.x), cvRound(y + pt.y)), color);
}
}
}
void main()
{
String window = "OpticalFlow :: Farneback";
VideoCapture capture(0);
Mat prev_gray;
// crear la ventana
namedWindow(window);
// bucle de captura de video
while (true) {
Mat frame, gray, flow;
// capturar el cuadro actual
capture >> frame;
// si no hay datos continuar
if (frame.empty()) continue;
// escalar a la mitad, para mejorar rendimiento
resize(frame, frame, Size(), 0.6, 0.6, INTER_LINEAR);
// convertir a escala de grises
cvtColor(frame, gray, COLOR_BGR2GRAY);
if (!prev_gray.empty())
{
calcOpticalFlowFarneback(prev_gray, gray, flow, 0.5, 3, 15, 3, 5, 1.1, 0);
drawOpticalFlow(flow, frame);
}
// esperar por 30 ms ha que se presione una tecla
if(waitKey(30) == 27) break;
// mostrar la imagen
imshow(window, frame);
// intercambiar las imagenes, la actual es ahora la anterior.
cv::swap(prev_gray, gray);
}
}
Las partes más relevantes de nuestro código:
Mat frame;
capture >> frame;
Capturamos la imagen de la cámara.
resize(frame, frame, Size(), 0.6, 0.6, INTER_LINEAR);
cvtColor(frame, gray, COLOR_BGR2GRAY);
Primero reducimos el tamaño de la imagen capturada a un 60%, esto simplemente para aumentar el rendimiento, no es estrictamente necesario, luego cambiamos la imagen a escala de grises.
calcOpticalFlowFarneback(prev_gray, gray, flow, 0.5, 3, 15, 3, 5, 1.1, 0);
drawOpticalFlow(flow, frame);
Calculamos el flujo óptico y dibujamos el resultado mediante la función drawOpticalFlow(...)
, esta la hemos creado nosotros y se define de esta forma:
void drawOpticalFlow(const Mat& flowImage, const Mat& flowImageGray)
{
int stepSize = 16;
Scalar color = Scalar(0, 255, 0);
// Draw the uniform grid of points on the input image along with the motion vectors
for (int y = 0; y < flowImageGray.rows; y += stepSize)
{
for (int x = 0; x < flowImageGray.cols; x += stepSize)
{
// Circles to indicate the uniform grid of points
circle(flowImageGray, Point(x, y), 1, color, FILLED);
// Lines to indicate the motion vectors
const Point2f& pt = flowImage.at<Point2f>(y, x);
line(flowImageGray, Point(x, y), Point(cvRound(x + pt.x), cvRound(y + pt.y)), color);
}
}
}
Lo que debemos hacer es recorrer la imagen devuelta por el algoritmo, marcaremos los puntos en una rejilla de 16 pixeles, cada punto de la rejilla lo dibujamos usando la función circle(...)
a parte también dibujamos una línea para representar el vector del flujo, para esto usamos la función line(...)
.
Si te resulta un poco lento y deseas acelerar el proceso puedes probar una la T-API para aumentar el rendimiento mediante OpenCL, si tienes el hardware que lo soporte, para un ejemplo puedes ver Tutorial OpenCV + OpenCL.
De momento es todo, nos vemos en la siguiente publicación.
Descargar código: flujo-óptico-gf.zip
Comentarios
Publicar un comentario