光流法总结最初是由Horn和Schunck于一九八二年建议的,可是会导致过拟合与参数过多总括量太大

听说opencv3完毕运动物体识别

发表于2017/9/22 22:26:30  927人阅读

分类: Opencv_Learning-Diary

一:背景减法

 

       
 对于三个祥和的监察场景而言,在没有挪动指标,光照没有转变的情况下,摄像图像中相继像素点的灰度值是吻合自由可能率分布的。由于录制机在征集图像的历程中,会不可防止地引入噪声,那一个灰度值以某三个均值为基准线,在邻近做肯定限制内的轻易振荡,那种光景正是所谓的“背景”。

        背景减法(Background
subtraction)是现阶段运动目的检查和测试技能中央银行使较为普遍的一类措施,它的骨干思维和帧间差分法相类似,都以应用分化图像的差分运算提取指标区域。但是与帧间差分法分裂的是,背景减法不是将日前帧图像与相邻帧图像相减,而是将如今帧图像与七个不断更新的背景模型相减,在差分图像中领到运动指标。

                                图片 1

 
 背景减法的演算进程如图2-6 所示。首先使用数学建立模型的措施成立一幅背景图像帧B ,记当前图像帧为fn,背景帧和如今帧对应像素点的灰度值分别记为B(x, y )和fn(x , y ) ,依据式2.17 将两帧图像对应像素点的灰度值进行相减,并取其相对值,获得差分图像D
n:

                                   
  图片 2

 
 设定阈值 T ,遵照式2.18 各个对像素点举行二值化处理,得到二值化图像
CR-Vn’ 。个中,灰度值为255 的点即为前景(运动指标)点,灰度值为0 的点即为背景点;对图像
奔驰G级n’进行连通性分析,最后可获取含有完整运动指标的图像陆风X8n 。

                                     图片 3

 
 背景减法总括较为简单,由于背景图像中绝非移动目的,当前图像中有运动指标,将两幅图像相减,明显能够领到出完全的运动目的,消除了帧间差分法提取的对象内部含有“空洞”的标题。

 
 利用背景减法达成指标检测主要回顾七个环节:背景建模,背景更新,目的检查和测试,前期处理。当中,背景建立模型和背景更新是背景减法中的主题难点。背景模型建立的上下直接影响到对象检测的效用。所谓背景建立模型,就是经过数学方法,营造出一种能够表征“背景”的模子。获取背景的最出彩方法是在向来不移动指标的景色下获得一帧“纯净”的图像作为背景,不过,在骨子里情状中,由于光照变化、雨雪天气、指标活动等诸多成分的影响,那种情状是很难完成。

代码达成:

// Vedio_detect_human.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
// 运动物体检测——背景减法
#include "opencv2/opencv.hpp"  
using namespace cv;
#include <iostream>  
using namespace std;
// 运动物体检测函数声明  
Mat MoveDetect(Mat background, Mat frame);

int main()
{

    VideoCapture video("D:\\opencv\\soft\\3.3.0\\win\\opencv\\sources\\samples\\data\\vtest.avi");//定义VideoCapture类video  
    if (!video.isOpened())  //对video进行异常检测  
    {
        cout << "video open error!" << endl;
        return 0;
    }
    // 获取帧数
    int frameCount = video.get(CV_CAP_PROP_FRAME_COUNT);
    // 获取FPS 
    double FPS = video.get(CV_CAP_PROP_FPS);
    // 存储帧
    Mat frame;
    // 存储背景图像
    Mat background;
    // 存储结果图像
    Mat result;
    for (int i = 0; i < frameCount; i++)
    {
        // 读帧进frame
        video >> frame;
        imshow("frame", frame);
        // 对帧进行异常检测
        if (frame.empty())
        {
            cout << "frame is empty!" << endl;
            break;
        }
        // 获取帧位置(第几帧)
        int framePosition = video.get(CV_CAP_PROP_POS_FRAMES); 
        cout << "framePosition: " << framePosition << endl;
        // 将第一帧作为背景图像
        if (framePosition == 1)
            background = frame.clone();
        // 调用MoveDetect()进行运动物体检测,返回值存入result
        result = MoveDetect(background, frame);
        imshow("result", result);
        // 按原FPS显示
        if (waitKey(1000.0 / FPS) == 27)
        {
            cout << "ESC退出!" << endl;
            break;
        }
    }
    return 0;
}
Mat MoveDetect(Mat background, Mat frame)
{
    Mat result = frame.clone();
    // 1.将background和frame转为灰度图  
    Mat gray1, gray2;
    cvtColor(background, gray1, CV_BGR2GRAY);
    cvtColor(frame, gray2, CV_BGR2GRAY);
    // 2.将background和frame做差  
    Mat diff;
    absdiff(gray1, gray2, diff);
    imshow("diff", diff);
    // 3.对差值图diff_thresh进行阈值化处理  
    Mat diff_thresh;
    threshold(diff, diff_thresh, 50, 255, CV_THRESH_BINARY);
    imshow("diff_thresh", diff_thresh);
    // 4.腐蚀  
    Mat kernel_erode = getStructuringElement(MORPH_RECT, Size(3, 3));
    Mat kernel_dilate = getStructuringElement(MORPH_RECT, Size(15, 15));
    erode(diff_thresh, diff_thresh, kernel_erode);
    imshow("erode", diff_thresh);
    // 5.膨胀  
    dilate(diff_thresh, diff_thresh, kernel_dilate);
    imshow("dilate", diff_thresh);
    // 6.查找轮廓并绘制轮廓  
    vector<vector<Point>> contours;
    findContours(diff_thresh, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);
    // 在result上绘制轮廓
    drawContours(result, contours, -1, Scalar(0, 0, 255), 2); 
    // 7.查找正外接矩形  
    vector<Rect> boundRect(contours.size());
    for (int i = 0; i < contours.size(); i++)
    {
        boundRect[i] = boundingRect(contours[i]);
        // 在result上绘制正外接矩形
        rectangle(result, boundRect[i], Scalar(0, 255, 0), 2); 
    }
    // 返回result
    return result;
}

图片 4

 

图片 5

 

 

二:帧差法

   
  帧间差分方法应用图像种类中相邻两帧要么三帧图像对应像素值相减,然后取差值图像举办阈值化处理提取出图像中的运动区域:

图片 6

代码:

// Vedio_detect_human.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
// 运动物体检测——帧差法  
#include "opencv2/opencv.hpp"  
using namespace cv;
#include <iostream>  
using namespace std;
// 运动物体检测函数声明  
Mat MoveDetect(Mat temp, Mat frame);

int main()
{
    // 定义VideoCapture类video
    VideoCapture video("D:\\opencv\\soft\\3.3.0\\win\\opencv\\sources\\samples\\data\\vtest.avi");
    if (!video.isOpened())  //对video进行异常检测  
    {
        cout << "video open error!" << endl;
        return 0;
    }
    // 获取帧数
    int frameCount = video.get(CV_CAP_PROP_FRAME_COUNT);
    // 获取FPS
    double FPS = video.get(CV_CAP_PROP_FPS);
    // 存储帧
    Mat frame;
    // 存储前一帧图像
    Mat temp;
    // 存储结果图像
    Mat result;
    for (int i = 0; i < frameCount; i++)
    {
        // 读帧进frame
        video >> frame;
        imshow("frame", frame);
        // 对帧进行异常检测
        if (frame.empty())
        {
            cout << "frame is empty!" << endl;
            break;
        }
        // 获取帧位置(第几帧)
        int framePosition = video.get(CV_CAP_PROP_POS_FRAMES); 
        cout << "framePosition: " << framePosition << endl;
        // 如果为第一帧(temp还为空)
        if (i == 0)
        {
            // 调用MoveDetect()进行运动物体检测,返回值存入result
            result = MoveDetect(frame, frame);
        }
        //若不是第一帧(temp有值了)
        else
        {
            // 调用MoveDetect()进行运动物体检测,返回值存入result
            result = MoveDetect(temp, frame);
        }
        imshow("result", result);
        // 按原FPS显示
        if (waitKey(1000.0 / FPS) == 27)
        {
            cout << "ESC退出!" << endl;
            break;
        }
        temp = frame.clone();
    }
    return 0;


}
Mat MoveDetect(Mat temp, Mat frame)
{
    Mat result = frame.clone();
    // 1.将background和frame转为灰度图  
    Mat gray1, gray2;
    cvtColor(temp, gray1, CV_BGR2GRAY);
    cvtColor(frame, gray2, CV_BGR2GRAY);
    // 2.将background和frame做差  
    Mat diff;
    absdiff(gray1, gray2, diff);
    imshow("diff", diff);
    // 3.对差值图diff_thresh进行阈值化处理  
    Mat diff_thresh;
    threshold(diff, diff_thresh, 50, 255, CV_THRESH_BINARY);
    imshow("diff_thresh", diff_thresh);
    // 4.腐蚀  
    Mat kernel_erode = getStructuringElement(MORPH_RECT, Size(3, 3));
    Mat kernel_dilate = getStructuringElement(MORPH_RECT, Size(18, 18));
    erode(diff_thresh, diff_thresh, kernel_erode);
    imshow("erode", diff_thresh);
    // 5.膨胀  
    dilate(diff_thresh, diff_thresh, kernel_dilate);
    imshow("dilate", diff_thresh);
    // 6.查找轮廓并绘制轮廓  
    vector<vector<Point>> contours;
    findContours(diff_thresh, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);
    // 在result上绘制轮廓
    drawContours(result, contours, -1, Scalar(0, 0, 255), 2);
    // 7.查找正外接矩形  
    vector<Rect> boundRect(contours.size());
    for (int i = 0; i < contours.size(); i++)
    {
        boundRect[i] = boundingRect(contours[i]);
        // 在result上绘制正外接矩形
        rectangle(result, boundRect[i], Scalar(0, 255, 0), 2);
    }
    // 返回result
    return result;
}

 

图片 7图片 8

 

优点:

 

  • 帧间差分方法简便、运算量小且易于落到实处。
  • 帧间差分方法进行活动指标检查和测试能够较强地适应动态环境的生成,有效地去除系统误差和噪声的熏陶,对气象中光照的变型不灵敏而且不易受阴影的震慑。

 

缺点:

 

  • 不能够完全提取全部有关的性状像素点,也不能够获得运动指标的完整轮廓,只好得到运动区域的差不多轮廓;
  • 检查和测试到的区域大小受物体的活动速度制约:对火速移动的物体,须要选取较小的岁月间隔,假诺选择不体面,当物体在内外两帧中尚无重叠时,会被检查和测试为三个分其余物体;对于慢速活动的物体,应该选用较大的时日差,若是时间接选举用不安妥,当物体在内外两帧中大致统统重合时,则检查和测试不到物体。
  • 不难在运动实体内部差生空洞现象。

三:光流法

 

简介:在电脑视觉中,Lucas–Kanade光流算法是一种两帧差分的光流推断算法。它由BruceD. Lucas 和 Takeo

Kanade提出。
光流的定义:(Optical flow or optic flow)
它是一种运动情势,那种移动方式指的是三个物体、表面、边缘在3个意见下由八个观望者(比如眼睛、摄像头等)

和背景之间形成的明显位移。光流技术,如运动物检疫查和测试和图像分割,时间冲撞,运动补偿编码,三维立体视差,都以

行使了那种边缘或外部运动的技巧。
二维图像的移动相对于观望者而言是三维物体移动的在图像平面包车型地铁阴影。
稳步的图像能够推断出二维图像的弹指图像速率或离散图像转移。

光流算法:
它评估了两幅图像的之间的变形,它的基本若是是体素和图像像素守恒。它假如3个实体的颜料在内外两帧没有惊天动地

而拨云见日的变动。基于那么些思路,大家得以获得图像约束方程。差别的光流算法解决了如若了分歧附加条件的光流难题。

Lucas–Kanade算法:
以此算法是最普遍,最风靡的。它总结两帧在岁月t 到t +
δt之间每一个各个像素点地点的移位。 由于它是依照图像信号

的泰勒级数,那种艺术称为差分,那正是对于空间和岁月坐标使用偏导数。

图像约束方程能够写为I (x ,y ,z ,t ) = I (x + δx ,y + δy ,z + δz ,t +
δt )
I(x, y,z, t) 为在(x,y,z)地方的体素。
咱俩只要移动丰裕的小,那么对图像约束方程使用Taylor公式,大家得以得到:
图片 9

H.O.T. 指更高阶,在活动充足小的状态下得以忽略。从那个方程中我们能够收获:
图片 10
或者
图片 11
大家取得:

图片 12
V x ,V y ,V z 分别是I(x,y,z,t)的光流向量中x,y,z的重组。 图片 13图片 14图片 15和 图片 16则是图像在(x ,y ,z ,t )这或多或少向相应方向的差分 。
所以

I x V x + I y V y + I z V z = − I t。

写做:

图片 17

本条方程有几个未知量,尚不能被消除,那相当于所谓光流算法的光圈难点。那么要找到光流向量则供给另一套消除的方案。而Lucas-Kanade算法

是八个非迭代的算法:

纵然流(Vx,Vy,Vz)在3个尺寸为m*m*m(m>1)的小窗中是3个常数,那么从像素1…n , n = m 3 中得以拿走下列一组方程:

图片 18

图片 19

图片 20

图片 21

多个未知数不过有多于四个的方程,这几个方程组自然是个超定方程,相当于说方程组内有冗余,方程组能够代表为:

图片 22

记作:图片 23

为了缓解这一个超定难题,大家选拔最小二乘法:

图片 24or

图片 25

得到:

图片 26

里头的求和是从1到n。

那也正是说寻找光流能够由此在四维上海体育地方像导数的各自增加得出。大家还索要三个权重函数W(i,
j,k) , 图片 27来优良窗口中央点的

坐标。高斯函数做这项工作是十二分得体的,

其一算法的阙如在于它不可能发生2个密度很高的流向量,例如在活动的边缘和黑大的同质区域中的微小移动方面流新闻会火速的褪去。它的优点在于

有噪音存在的鲁棒性还能的。

简单来讲的话,上海教室表现的正是光流,光流描述的是图像上各样像素点的灰度的岗位(速度)变化情状,光流的商讨是采取图像连串中的像素强度数据的时域变化和相关性来规定各自像素地点的“运动”。研究光流场的指标就是为着从图片系列中好像得到无法一贯拿走的操场。
光流法的前提如若:
(1)相邻帧之间的亮度恒定;
(2)相邻录制帧的取帧时间总是,或然,相邻帧之间物体的移动比较“微小”;
(3)保持空间一致性;即,同一子图像的像素点具有相同的移位;

代码1:

 

// Vedio_detect_human.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
// 运动物体检测——光流法--LK金字塔
#include<iostream>
#include<opencv2\highgui\highgui.hpp>
#include<opencv2\nonfree\nonfree.hpp>
#include<opencv2\video\tracking.hpp>

using namespace std;
using namespace cv;

Mat image1, image2;
vector<Point2f> point1, point2, pointCopy;
vector<uchar> status;
vector<float> err;

int main()
{
    VideoCapture video("D:\\opencv\\soft\\3.3.0\\win\\opencv\\sources\\samples\\data\\vtest.avi");
    // 获取视频帧率
    double fps = video.get(CV_CAP_PROP_FPS);
    // 两幅画面中间间隔
    double pauseTime = 1000 / fps; 
    video >> image1;
    Mat image1Gray, image2Gray;
    cvtColor(image1, image1Gray, CV_RGB2GRAY);
    goodFeaturesToTrack(image1Gray, point1, 100, 0.01, 10, Mat());
    pointCopy = point1;
    // 绘制特征点位
    for (int i = 0; i<point1.size(); i++)
    {
        circle(image1, point1[i], 1, Scalar(0, 0, 255), 2);
    }
    namedWindow("LK--角点特征光流", 0);
    imshow("LK--角点特征光流", image1);
    while (true)
    {
        video >> image2;
        // 图像为空或Esc键按下退出播放
        if (!image2.data || waitKey(pauseTime) == 27)
        {
            break;
        }
        cvtColor(image2, image2Gray, CV_RGB2GRAY);
        // LK金字塔实现
        calcOpticalFlowPyrLK(image1Gray, image2Gray, point1, point2, status, err, Size(20, 20), 3);
        for (int i = 0; i<point2.size(); i++)
        {
            circle(image2, point2[i], 1, Scalar(0, 0, 255), 2);
            line(image2, pointCopy[i], point2[i], Scalar(255, 0, 0), 2);
        }
        imshow("LK金字塔实现--角点特征光流", image2);
        swap(point1, point2);
        image1Gray = image2Gray.clone();
    }
    return 0;
}

图片 28

 

代码2:

// Vedio_detect_human.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
// 运动物体检测——光流法--LK金字塔
#include <iostream>    
#include <opencv2/opencv.hpp>  
#include <opencv2/core/core.hpp>    
#include <opencv2/highgui/highgui.hpp>   
#include <opencv2/imgproc/imgproc.hpp>  // Gaussian Blur  
#include <opencv2/ml/ml.hpp>  
#include <opencv2/contrib/contrib.hpp>  

using namespace cv;
using namespace std;

void duan_OpticalFlow(Mat &frame, Mat & result);
bool addNewPoints();
bool acceptTrackedPoint(int i);

Mat image;
vector<Point2f> point1, point2, pointCopy;
Mat curgray;    // 当前图片  
Mat pregray;    // 预测图片  
vector<Point2f> point[2]; // point0为特征点的原来位置,point1为特征点的新位置  
vector<Point2f> initPoint;    // 初始化跟踪点的位置  
vector<Point2f> features; // 检测的特征  
int maxCount = 1000;         // 检测的最大特征数  
double qLevel = 0.01;   // 特征检测的等级  
double minDist = 10.0;  // 两特征点之间的最小距离  
vector<uchar> status; // 跟踪特征的状态,特征的流发现为1,否则为0  
vector<float> err;

int main()
{
    Mat matSrc;
    Mat matRst;

    VideoCapture cap("D:\\opencv\\soft\\3.3.0\\win\\opencv\\sources\\samples\\data\\vtest.avi");
    int totalFrameNumber = cap.get(CV_CAP_PROP_FRAME_COUNT);

    cap >> image;
    Mat imageGray, image2Gray;
    cvtColor(image, imageGray, CV_RGB2GRAY);
    goodFeaturesToTrack(imageGray, point1, 100, 0.01, 10, Mat());
    pointCopy = point1;
    // 绘制特征点位
    for (int i = 0; i<point1.size(); i++)
    {
        circle(image, point1[i], 1, Scalar(0, 0, 255), 2);
    }
    namedWindow("LK--角点特征光流", 0);
    imshow("LK--角点特征光流", image);

    // perform the tracking process  
    cout << "开始检测视频,按下ESC键推出。" << endl;
    for (int nFrmNum = 0; nFrmNum < totalFrameNumber; nFrmNum++) {
        // 读取视频
        cap >> matSrc;
        if (!matSrc.empty())
        {
            duan_OpticalFlow(matSrc, matRst);
            cout << "该图片帧是 " << nFrmNum << endl;
        }
        else
        {
            cout << "获取视频帧数错误!" << endl;
        }
        if (waitKey(1) == 27) break;
    }

    waitKey(0);

    return 0;
}

void duan_OpticalFlow(Mat &frame, Mat & result)
{
    cvtColor(frame, curgray, CV_BGR2GRAY);
    frame.copyTo(result);
    // 添加特征点
    if (addNewPoints())
    {
        goodFeaturesToTrack(curgray, features, maxCount, qLevel, minDist);
        point[0].insert(point[0].end(), features.begin(), features.end());
        initPoint.insert(initPoint.end(), features.begin(), features.end());
    }

    if (pregray.empty())
    {
        curgray.copyTo(pregray);
    }

    calcOpticalFlowPyrLK(pregray, curgray, point[0], point[1], status, err);
    // 去除部分不好的光电
    int k = 0;
    for (size_t i = 0; i<point[1].size(); i++)
    {
        if (acceptTrackedPoint(i))
        {
            initPoint[k] = initPoint[i];
            point[1][k++] = point[1][i];
        }
    }

    point[1].resize(k);
    initPoint.resize(k);
    // 现实特征点和运动轨迹
    for (size_t i = 0; i<point[1].size(); i++)
    {
        line(result, initPoint[i], point[1][i], Scalar(0, 0, 255));
        circle(result, point[1][i], 3, Scalar(0, 255, 0), -1);
    }
    // 更新该次结果作为下一次的参考
    swap(point[1], point[0]);
    swap(pregray, curgray);

    imshow("LK--Demo", result);
    // waitKey(0);  
}

bool addNewPoints()
{
    return point[0].size() <= 10;
}

bool acceptTrackedPoint(int i)
{
    return status[i] && ((abs(point[0][i].x - point[1][i].x) + 
        abs(point[0][i].y - point[1][i].y)) > 2);
}

图片 29

opencv源码给出的demo:

// Vedio_detect_human.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
// 运动物体检测——光流法--LK金字塔
#include <stdio.h>  
#include <iostream>    
#include <opencv2/opencv.hpp>  
#include <opencv2/core/core.hpp>    
#include <opencv2/highgui/highgui.hpp>   
#include <opencv2/imgproc/imgproc.hpp>  // Gaussian Blur  
#include <opencv2/ml/ml.hpp>  
#include <opencv2/contrib/contrib.hpp>  

#include <opencv2/video/tracking.hpp>  


using namespace cv;

static void convertFlowToImage(const Mat &flow_x, const Mat &flow_y, Mat &img_x, Mat &img_y, double lowerBound, double higherBound) {
#define CAST(v, L, H) ((v) > (H) ? 255 : (v) < (L) ? 0 : cvRound(255*((v) - (L))/((H)-(L))))  
    for (int i = 0; i < flow_x.rows; ++i) {
        for (int j = 0; j < flow_y.cols; ++j) {
            float x = flow_x.at<float>(i, j);
            float y = flow_y.at<float>(i, j);
            img_x.at<uchar>(i, j) = CAST(x, lowerBound, higherBound);
            img_y.at<uchar>(i, j) = CAST(y, lowerBound, higherBound);
        }
    }
#undef CAST  
}

static void drawOptFlowMap(const Mat& flow, Mat& cflowmap, int step, double, const Scalar& color)
{
    for (int y = 0; y < cflowmap.rows; y += step)
        for (int x = 0; x < cflowmap.cols; x += step)
        {
            const Point2f& fxy = flow.at<Point2f>(y, x);
            line(cflowmap, Point(x, y), Point(cvRound(x + fxy.x), cvRound(y + fxy.y)),
                color);
            circle(cflowmap, Point(x, y), 2, color, -1);
        }
}

int main(int argc, char** argv)
{
    // IO operation  

    const char* keys =
    {
        "{ f  | vidFile      | ex2.avi | filename of video }"
        "{ x  | xFlowFile    | flow_x | filename of flow x component }"
        "{ y  | yFlowFile    | flow_y | filename of flow x component }"
        "{ i  | imgFile      | flow_i | filename of flow image}"
        "{ b  | bound | 15 | specify the maximum of optical flow}"
    };

    //CommandLineParser cmd(argc, argv, keys);  
    //string vidFile = cmd.get<string>("vidFile");  
    //string xFlowFile = cmd.get<string>("xFlowFile");  
    //string yFlowFile = cmd.get<string>("yFlowFile");  
    //string imgFile = cmd.get<string>("imgFile");  
    //int bound = cmd.get<int>("bound");  
    string vidFile = "vidFile";
    string xFlowFile = "xFlowFile";
    string yFlowFile = "yFlowFile";
    string imgFile = "imgFile";
    int bound = 80;


    namedWindow("video", 1);
    namedWindow("imgX", 1);
    namedWindow("imgY", 1);
    namedWindow("Demo", 1);
    //VideoCapture capture(vidFile);  
    VideoCapture capture("D:\\opencv\\soft\\3.3.0\\win\\opencv\\sources\\samples\\data\\vtest.avi");

    if (!capture.isOpened()) {
        printf("Could not initialize capturing..\n");
        return -1;
    }

    int frame_num = 0;
    Mat image, prev_image, prev_grey, grey, frame, flow, cflow;

    while (true) {
        capture >> frame;
        if (frame.empty())
            break;
        imshow("video", frame);

        if (frame_num == 0) {
            image.create(frame.size(), CV_8UC3);
            grey.create(frame.size(), CV_8UC1);
            prev_image.create(frame.size(), CV_8UC3);
            prev_grey.create(frame.size(), CV_8UC1);

            frame.copyTo(prev_image);
            cvtColor(prev_image, prev_grey, CV_BGR2GRAY);

            frame_num++;
            continue;
        }

        frame.copyTo(image);
        cvtColor(image, grey, CV_BGR2GRAY);

        // calcOpticalFlowFarneback(prev_grey,grey,flow,0.5, 3, 15, 3, 5, 1.2, 0 );  
        calcOpticalFlowFarneback(prev_grey, grey, flow, 0.702, 5, 10, 2, 7, 1.5, cv::OPTFLOW_FARNEBACK_GAUSSIAN);

        prev_image.copyTo(cflow);
        drawOptFlowMap(flow, cflow, 12, 1.5, Scalar(0, 255, 0));
        imshow("cflow", cflow);

        Mat flows[2];
        split(flow, flows);
        Mat imgX(flows[0].size(), CV_8UC1);
        Mat imgY(flows[0].size(), CV_8UC1);
        convertFlowToImage(flows[0], flows[1], imgX, imgY, -bound, bound);
        //char tmp[20];  
        //sprintf(tmp, "_%04d.jpg", int(frame_num));  
        //imwrite(xFlowFile + tmp, imgX);  
        //imwrite(yFlowFile + tmp, imgY);  
        //imwrite(imgFile + tmp, image);  

        std::swap(prev_grey, grey);
        std::swap(prev_image, image);
        frame_num = frame_num + 1;

        imshow("imgX", imgX);
        imshow("imgY", imgY);
        imshow("Demo", image);
        if (waitKey(1) == 27) break;
    }
    waitKey(0);
    return 0;
}

 

图片 30

转自于(http://blog.csdn.net/u014568921/article/details/46638557

科研:

光流是图像亮度的移动消息描述。光流法总计最初是由Horn和Schunck于1984年提议的,创建性地将二维速度场与灰度相关联,引入光流约束方程,获得光流总括的大旨算法.光流总计基于物体移动的光学性子建议了3个假使:

一 、光流算法

①运动物体的灰度在相当短的间隔时间内保障不变;
②给定邻域内的进程向量场变化是迟迟的。

@灰度恒常约束

光流是图像亮度的移动消息描述。光流法总结最初是由Horn和Schunck于一九八一年提议的,成立性地将二维速度场与灰度相关联,引入光流约束方程,获得光流计算的中坚算法.光流总结基于物体移动的光学个性建议了三个比方:

①活动物体的灰度在相当的短的间隔时间内维持不变;

②给定邻域内的快慢向量场变化是慢性的。

算法原理见链接:光流法基本原理

算法原理

万一图像上一个像素点(x,y),在t时刻的亮度为E(x+Δx,y+Δy,t+Δt),同时用u(x,y0和v(x,y)来表示该点光流在档次和垂直方向上的活动分量:

u=dx/dt

v=dy/dt

在经过一段时间间隔Δt后该点对应点亮度为E(x+Δx,y+Δy,t+Δt),当Δt非常的小趋近于0时,大家得以认为该点亮度不变,所以能够有:
E(x,y,t)=E(x+Δx,y+Δy,t+Δt)
当该点的亮度有转变时,将移步后点的亮度由Taylor公式展幵,可得:
图片 31
疏忽其二阶无穷小,由于Δt趋近于0时,有:

图片 32
式中w=(u,v),所以上式正是大旨的光流约束方程。
其中令图片 33代表图像中像素点灰度沿x,y,t方向的梯度,可将上式改写成:
图片 34

 

Lucas-Kanade是一种普遍运用的光流估量的差分方法,这几个办法是由Bruce D.
卢卡s和Takeo
Kanade发明的。它假如光流在像素点的邻域是三个常数,然后使用最小二乘法对邻域中的全部像素点求解基本的光流方程。
由此整合几个近乎像素点的音讯,卢卡斯-金出方法(简称为L-K方法)经常能够解决光流方程里的多义性。而且,与逐点总计的措施相比,L-K方法对图像噪声不灵动。但是,由于那是一种局地方法,所以在图像的均匀区域内部,L-K方法不可能提供光流新闻。

@LK算法:

Lucas-Kanade是一种常见应用的光流推断的差分方法,那几个方法是由Bruce D.
Lucas和Takeo
Kanade发明的。它固然光流在像素点的邻域是贰个常数,然后利用最小二乘法对邻域中的全体像素点求解基本的光流方程。


Lucas-Kanade革新算法

姬恩-Yves
Bouguet提议一种基于金字塔分层,针对仿射变换的改良Lucas-Kanade算法。

 

怎么要用金字塔?因为lk算法的自律原则即:小速度,亮度不变以及区域一致性都以较强的假若,并不很简单获得满意。如当物体运动速度较快时,假若不创制,那么继续的只要就会有较大的错误,使得最终求出的光流值有较大的误差。

考虑物体的移动速度较大时,算法会出现较大的误差。那么就期待能减小图像中物体的活动速度。1个直观的法子正是,减弱图像的尺寸。即使当图像为400×400时,物体速度为[16
16],那么图像缩短为200×200时,速度变为[8,8]。缩小为100*100时,速度减弱到[4,4]。所以在源图像缩放了无数未来,原算法又变得适用了。所以光流可以经过生成
原图像的金字塔图像,逐层求解,不断规范来求得。简单的话上层金字塔(低分辨率)中的一个像素能够表示下层的五个。

 

借使I和J是两幅2D的灰度图像,对于图像上种种像素点的灰度值定义为:

I(x)=I(x,y)   和  J(x)=j(x,y)

其中x=(x,y)是图像上像素点的图像坐标。
在实质上处境中图像I和图像J能够象征前后两帧图像。对于图像特征点金字塔跟踪来说的指标是:对于前一帧的图像I上一点u(ux,uy),要在后一帧图像J上找到一点v(ux+dx,uy+dy)与之相匹配,即灰度值最相仿。那么向量d=[dx,dy]即使图像在点u处的移位速度,也正是所说像素点u的光流。为了越发验证向量d的含义。大家借使前一帧图像经历了仿射变换成后一帧图像,定义变换矩阵为

图片 35

在那之中八个参数dxx,dyy,dxy,dyx表征着图像中的仿射变形。所以光流计算的目标转变成找到向量d和转换矩阵A使得图像上一块区域内灰度差最小。
概念误差

图片 36

个中多少个整数wx和wy设定了图像上矩形窗口的分寸(2*wx+1)和(2*wy+1)。
独占鳌头的wx和wy取值为1,2,3,4,5,6,八个像素,相似度的函数被在(2ωx+1,
2ωy+1)的区域钦命义。注目的在于金字塔各层窗口的大大小小是维持一定的尺码

对此Lucas-Kanade革新算法来说,重要的步骤有三步:建立金字塔,基于金字塔跟踪,迭代过程。

 

 

二、Generic Object Tracking Using Regression Networks(GOTURN)

什么样明白Regression

全总小说的关键点正是那,回归的是怎么?当然是bounding-box的坐标,那么回归的输入变量正是current
frame,输出为bounding-box的坐标。当然前提是明白previous
frame中object的坐标在基本地方。那么那个Regression
Network学习到的正是:object在摄像中前后帧的motion到object坐标的浮动!知道了object在前一帧的着力,找到object在当下帧的职责。


金字塔的确立

令I0 = I 是第 0
层的图像,它是金字塔图像中分辨率最高的图像,图像的宽窄和可观分别定义为nx0
= nx 和 ny0 =
ny 。以一种递归的主意确立金字塔:从I0中总结I1,从I第11中学总计I2 ,···。令L
=1, 2,…代表金字塔的层数,L常常取2,3,4。IL−1 是第L−1层的图像,nxL−1 和
nyL−1分别是图像IL−1 的上升幅度和冲天。图像IL可按如下情势由IL−1 求得:
图片 37

即用八个[0.25 0.5 0.25]的低通滤波器对IL-1进行卷积。

 

三、Siamese Network

Siamese互联网是一种相似性衡量方法,当体周全多,但各样项目标范本数量少的景观下可用以项目标辨识、分类等

图片 38

金字塔跟踪

全部来讲,金字塔特征跟踪算法描述如下:首先,光流和仿射变换矩阵在最高级中学一年级层的图像上测算出;将上一层的推断结果作为开端值传递给下一层图像,这一层的图像在那几个开端值的底子上,计算这一层的光流和仿射变化矩阵;再将这一层的光流和仿射矩阵作为早先值传递给下一层图像,直到传递给最后一层,即原始图像层,这一层计算出来的光流和仿射变换矩阵作为最终的光流和仿射变换矩阵的结果。

图片 39
对于L=0,1,2,…L,定义图片 40是图像中像素点u在第L层对应点的坐标。依据上一步中图像金字塔的概念,能够测算出

图片 41

咱俩用数学的构思重新描述在L层和L+1层迭代运算,假定在第L层有对被跟踪目标的地点有个大体推断,而从第L+1层传递到L层的移动矢量,即光流总括初值为图片 42(前边会对gL做二个诠释)并且对于最上层的转移矩阵推断
图片 43

为了在L层上计算光流和仿射变换矩阵,必要再行定义在L层上的匹配误差ξL:
图片 44
个中图像图片 45图片 46是原来图像在L层上采样出来的图像,基于那层中的光流和仿射矩阵初值gL和GL能够估测计算出多个对应图像图片 47图片 48
图片 49
此间用L+1层获得的中期揣摸gL对L层作预平移,L层在gL的根基上求该层的光流dL,这样求得的残余光流向量dL=
[dLx,
dLy]T就足够小,因而能够通过标准的光流法来求出这一个运动矢量。然后拿走的dL结合gL又有啥不可对L-1层的gL-1做估计。最后的光流和便是在全数层的道岔光流d的叠加。使用金字塔图像总计光流的3个显明的功利是,对于2个持有较大的像素偏移的矢量d,能够由此估测计算多少个比较小的残余光流来获得。那里正是金字塔跟踪算法的主干。

 

接下去即是估测计算该层上的光流dL和更换矩阵AL,大家将在下一步中探讨。今后,假诺在这一层上的光流和转换矩阵己经计算出来。接着将结果传递给下一层,计算出下一层的只要初值:

图片 50
将gL-1和GL-1作为初值,重新循环上面包车型地铁步骤,直到最上一层,总计出光流d和仿射变换矩阵A。

由于金字塔的缩放减小了光流值,最高层的光流估算值能够设为0,设顶层时的发轫为:

图片 51

那种算法最显明的优势在于对于每一层的光流都会保持非常的小,不过最终总括来的光流能够开始展览累积,便于有效地跟踪特征点。

 

最首要思想:

壹 、输入不再是单个样本,而是一对样本,不再给单个的范本确切的竹签,而且给定一些样书是不是来自同1个类的标签,是正是0,不是就是1

贰 、设计了八个一律的网络,网络共享权值W,对出口举办了偏离衡量,能够说l① 、l2等。

叁 、针对输入的范本对是或不是来自同3个体系设计了损失函数,损失函数方式稍微类似交叉熵损失:

图片 52

终极动用获得的损失函数,使用梯度反传去更新四个互联网共享的权值W。

迭代进度

这一步是算法的着力步骤。在金字塔的每一层,指标是计量出光流dL和仿射变换矩阵AL从而使误差ξL最小。由于每一层的迭代进程是平等的,所以大家就讲述从一层到下一层的迭代过程。首先将上一层的光流u和A传给这一层,总括这一帧图像中像素点的宿州图片 53,同时总计出图像在该点x方向和y方向上的偏导

Ix=[I(x+1,y)-I(x-1,y)]/2

Iy=[I(x,y+1)-I(x,y-1)]/2

在此基础上,总结出空间梯度矩阵:

图片 54

履新光流v=2*v

迭代经过:计算后一帧图像中对应像素点的灰度图片 55,计算两
帧图像间相同地方点的灰度值之差图片 56,在测算图像之间的误差
向量:
图片 57
最后总计针对仿射光流

图片 58
更新跟踪结果

图片 59
直到图片 60有个别阈值,结束在这一层的迭代进度。

 

 

网络布局:

图片 61

特征点选用

因而,可依据以下的步子采纳特征点:
一 、计算图像 I 中每三个像素的矩阵G和纤Witt征值λm。
二 、寻找整副图像中型小型小的特征值 λm 中的最大特色值λmax。
三 、保留最小特征值 λm 大于给定阈值的像素点。阈值经常取5% λmax ~10%
λmax 。
四 、保留 λm 局地最大值的像素:像素特征值 λm 大于其3*3
邻域中别的像素的特征值 λm 。
五 、剔除像素密集区域中的一些像素,确定保障图像中相邻像素的离开都不止给定的阈值(常取5~10
pixels)。
上述操作完毕后,图像 I
中剩下的像素即为选取的特征点,并视作跟踪特征点。特征点接纳算法的步骤5
担保了特色点间的蝇头距离。

没有要求取多个大的归纳窗口接纳特征点(或计算矩阵G)。多量尝试验证,wx =
wy =1的 3*3 大小的归纳窗口能够收获满足的功用。

 

 

四、Inception v1-v4

金字塔中度的选项

在大部分的情事下,超越4的金字塔图像层次没有太大的含义。

 

有时候为了简化能够将仿射变换矩阵G简化为单位矩阵。

 

1.Inception v1:

深层框架结构与经典总括机视觉模型结合

频率进一步重要,通过扩大网络的size,即深度和每层的units数量

只是会造成过拟合与参数过多总计量太大

so:全连接——>稀疏连接(同时也满足了多规格)

图片 62

干什么加了一个1*1的卷积层之后,总结量就收缩了吗?维度就下落了呢?

对于贰个3*3的卷积,输入维度是100*100*500(通道是500),输出后是100*100*200(有200种3*3的卷积核,当然能够数值一样,然而三个3*二头能取得多少个特征图,一张),那么这么些卷积的参数为3*3*500*200(卷积大小*输入维度*出口维度)

所以,一个1*1的卷积,能够下跌参数(即维度部分),将一切特征图纵向变窄~

如下图:

“#3×3 reduce” and “#5×5 reduce” stands for the number of 1×1 filters
in the reduction

layer used before the 3×3 and 5×5 convolutions

图片 63


算法流程

图片 64

图片 65

图片 66

 

2.Inception v2

相关文章