Este método de visión por ordenador ignora por completo y tiene cero interés en la identificación de los objetos observados. Funciona mediante el análisis del movimiento individual de píxeles. Es útil para el seguimiento, análisis en 3D, medición de altitud, y medición de la velocidad. Este método tiene la ventaja de que puede trabajar con cámaras de baja resolución, mientras que los algoritmos más simples requieren capacidad de procesamiento mínima.

Flujo óptico es un campo vectorial que muestra la dirección y la magnitud de estos cambios en la intensidad de una imagen a la otra, como se muestra aquí:

Empleo del "Flujo Óptico" en la vida real

Seguimiento

El método de flujo óptico de seguimiento combina la sustracción de fondo y el flujo óptico, al eliminar el fondo, todo lo que hay que hacer es analizar el movimiento de los píxeles en movimiento (si quiere saber mas de la sustracción de fondo por favor haga click en el siguiente enlace:  CUDA 7.5 con OpenCV 3.1 sobre QT 5 en Linux - Remover Fondo en Video) .

Análisis de escenas en 3D

Mediante el análisis de movimiento de todos los píxeles, es posible generar mediciones 3D en bruto de la escena observada. Por ejemplo, la imagen de abajo del tren subterráneo: los píxeles en el extremo izquierdo se mueve rápido, y ambos están convergiendo y ralentizar hacia el centro de la imagen. Con esta información, la información 3D del tren se puede calcular (incluyendo la velocidad del tren, y el ángulo de la vía).

Análisis de escenas en 3D
Medición de la velocidad (con una cámara a altitud constante)

Mediante el análisis de la velocidad de pixel es posible analizar la velocidad de un objeto, esta técnica es utilizada a menudo para medir la velocidad de vuelo y dirección de los insectos.

Requisitos

Los requerimientos para poder desarrollar este proyecto son tener instalado y configurado tanto Qt5 como OpenCV 3.1 ademas de la integración de CUDA para ello puedes seguir la guía que publique anteriormente: OpenCV 3.1 con CUDA 7.5 sobre QT 5 en Debían 8

Creación del proyecto

Seguiremos los pasos standart para la creacion de un proyecto en QT, Seleccionando crear un nuevo proyecto.

Creación de un nuevo proyecto QT5

Configuramos el archivo .pro agregando las librerías necesarias para el correcto funcionamiento de OpenCV, estas librerias son las correspondientes a OpenCV y CUDA

INCLUDEPATH += /usr/local/include/opencv-3.1.0
LIBS += `pkg-config opencv --libs --cflags`

En mi caso el archivo en cuestión FlujoOptico-OpenCV-CUDA.pro queda de esta forma:

QT       += core gui

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

TARGET = FlujoOptico-OpenCV-CUDA
TEMPLATE = app

INCLUDEPATH += /usr/local/include/opencv-3.1.0
LIBS += `pkg-config opencv --libs --cflags`

SOURCES += main.cpp\
        mainwindow.cpp

HEADERS  += mainwindow.h

FORMS    += mainwindow.ui

Headers

Agregamos los header de OpenCV (incluyendo las librerías de CUDA para OpenCV) y QFileDialog (para poder obtener el seleccionador de archivos) a nuestro proyecto en el archivo "mainwindow.h".


#include <QMainWindow>
#include <QFileDialog>

#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/videoio.hpp>
#include <opencv2/core/cuda.hpp>
#include <opencv2/imgproc.hpp>

#include <opencv2/cudawarping.hpp>
#include <opencv2/cudaimgproc.hpp>
#include <opencv2/cudaarithm.hpp>
#include <opencv2/cudaoptflow.hpp>


Interfaz de usuario (UI)

El diseño se basa en los anteriores diseños, en todo caso lo básico que debe tener la aplicación son dos "botones" con uno configurado como checkable para el "play", "parar" y otro para seleccionar el archivo, ademas de los "Radio Buttons" para identificar la entrada de vídeo.
Dejare el código fuente completo por si presentan dudas.

QT OpenCV Video Texto
Código de las funciones principales

Tal como en tutoriales anteriores se omitieron los namespaces para fines académicos, ya que de esta forma el lector sabrá a que clase corresponde cada método.


#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget* parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
}
MainWindow::~MainWindow()
{
    delete ui;
}
/**
* Metodo para obtener la direccion del video
* @brief MainWindow::SeleccionarVideo
*/
void MainWindow::SeleccionarVideo()
{
    // Declara la variable con la ruta del archivo
    QString archivo = QFileDialog::getOpenFileName(this, tr("Abrir Video"),
        "",
        tr("Videos (*.avi *.mp4 *.mov)"));
    //Agrega la ruta del archivo
    ui->labelVideo->setText(archivo);
    ui->radioVideo->setChecked(true);
}
/**
* Metodo para procesar el video frame a frame si ckecked==true
* @brief MainWindow::ProcesarVideo
* @param checked
*
*/
void MainWindow::ProcesarVideo(bool checked)
{
    cv::destroyAllWindows(); // Para cerrar todas las ventanas
    cv::Mat frame, next, prvs, flujo_xy; // Frame como array multidimencional
    int s = 1;
    cv::cuda::GpuMat prvs_cuda, next_cuda, flujo_xy_cuda;
    cv::cuda::GpuMat prvs_cuda_o, next_cuda_o;
    cv::cuda::GpuMat prvs_cuda_c, next_cuda_c;
    if (!checked) { // Si !checked detiene el video si no lo procesa
        ui->play->setText("Iniciar video");
        cap.release();
    }
    else {
        ui->play->setText("Parar video");
        if (ui->radioVideo->isChecked()) { // si el "radio button" esta seleccionado ejecuta el video si no la webcam
            cap.open(ui->labelVideo->text().toStdString().c_str());
        }
        else {
            cap.open(0);
        }
        cap >> frame;
        prvs_cuda_o.upload(frame);
        cv::cuda::resize(prvs_cuda_o, prvs_cuda_c, cv::Size(frame.size().width / s, frame.size().height / s));
        cv::cuda::cvtColor(prvs_cuda_c, prvs_cuda, CV_BGR2GRAY);
    }
    //dense optical flujo
    cv::Ptr<cv::cuda::FarnebackOpticalFlow> fbOF = cv::cuda::FarnebackOpticalFlow::create();
    while (checked) // bucle hasta que se precione "parar video"
    {
        cap >> frame; // obtiene un nuevo frame del video o camara
        if (frame.empty())
            break; // detiene el bucle si elframe esta vacio
        //cuda upload, resize, color convert
        next_cuda_o.upload(frame);
        cv::cuda::resize(next_cuda_o, next_cuda_c, cv::Size(frame.size().width / s, frame.size().height / s));
        cv::cuda::cvtColor(next_cuda_c, next_cuda, CV_BGR2GRAY);
        fbOF->calc(prvs_cuda, next_cuda, flujo_xy_cuda);
        //copiar el vector para dibjar el flujo
        cv::Mat cflujo;
        cv::resize(frame, cflujo, cv::Size(frame.size().width / s, frame.size().height / s));
        flujo_xy_cuda.download(flujo_xy);
        mostrarFlujo(flujo_xy, cflujo, 10, CV_RGB(0, 255, 0));
        cv::namedWindow("Reproductor", cv::WINDOW_KEEPRATIO); // creamos una ventana la cual permita redimencionar
        cv::imshow("Reproductor", cflujo); // se muestran los frames
        next_cuda.download(next);
        prvs_cuda.download(prvs);
        prvs_cuda = next_cuda.clone();
        char key = (char)cv::waitKey(20); //espera 20ms por la tecla ESC
        if (key == 27)
            break; //detiene el bucle
    }
}
void MainWindow::mostrarFlujo(const cv::Mat& flujo_xy, cv::Mat& cflujomap, int step, const cv::Scalar& color)
{
    for (int y = 0; y < cflujomap.rows; y += step)
        for (int x = 0; x < cflujomap.cols; x += step) {
            cv::Point2f fxy;
            fxy.x = cvRound(flujo_xy.at<cv::Vec2f>(y, x)[0] + x);
            fxy.y = cvRound(flujo_xy.at<cv::Vec2f>(y, x)[1] + y);
            cv::line(cflujomap, cv::Point(x, y), cv::Point(fxy.x, fxy.y), color);
            cv::circle(cflujomap, cv::Point(fxy.x, fxy.y), 1, color, -1);
        }
}
void MainWindow::on_toolButton_clicked()
{
    SeleccionarVideo();
}
void MainWindow::on_actionAbrir_Video_triggered()
{
    SeleccionarVideo();
}
void MainWindow::on_play_toggled(bool checked)
{
    ProcesarVideo(checked);
}

Código fuente