精华内容
下载资源
问答
  • idt算法
    万次阅读 热门讨论
    2016-11-18 19:03:29

    转载请注明出处:http://blog.csdn.net/wzmsltw/article/details/53221179

    在上一篇笔记:iDT算法 中,对iDT算法的原理做了简单的介绍。由于iDT算法提供了算法源码,自己也用它做了不少实验,因此介绍一下其代码的使用方法,并对源代码做一些解析。iDT算法的代码在作者个人主页可以下载到,也可以点击此处下载:iDT算法源码

    除了本篇博客之外,还有iDT用法及源码剖析 这篇文章介绍的也不错,供参考。

    基本功能

    iDT算法框架中还包括Fisher Vector编码和SVM分类两阶段的工作,但作者提供的代码只包括到输出iDT特征的阶段,后续步骤需要使用其他代码或工具。其中有人编写了专门用与DT特征的FV编码C++程序:DTFV 。SVM则可以使用liblinear,对于高维度数据速度比较快。
    因此,此处要讨论的iDT算法代码的输入为一段视频,输出为iDT特征的列表,每行为1个特征,对应视频中的某段轨迹。每个特征的维度为426维:Trajectory-30, HOG-96, HOF-108, MBH-192。每种特征的维数是如何得到的见iDT算法那篇博客。

    编译与基本使用

    详细内容见文件夹中的README

    iDT代码的依赖包括两个库:

    • OpenCV: readme中推荐用2.4.2, 实际上用最新的2.4.13也没问题。但OpenCV3就不知道能不能用了,没有试过。
    • ffmpeg: readme中推荐用0.11.1。实际上装最新的版本也没有问题

    这两个库的安装教程网上很多,就不再多做介绍了。而且也都是很常用的库。

    在安装完以上两个库后,就可以进行代码编译了。只需要在代码文件夹下make一下就好,编译好的可执行文件在./release/下。

    使用时输入 视频文件的路径作为参数即可./release/DenseTrackStab ./test_sequences/person01_boxing_d1_uncomp.avi

    代码结构

    iDT代码中主要包括如下几个代码文件

    • DenseTrackStab.cpp:iDT算法主程序
    • DenseTrackStab.h:轨迹跟踪的一些参数,以及一些数据结构体的定义
    • Descriptors.h:特征相关的各种函数
    • Initialize.h:初始化相关的各种函数
    • OpticalFlow.h:光流相关的各种函数
    • Video.cpp: 这个程序与iDT算法无关,只是作者提供用来测试两个依赖库是否安装成功的。

    bound box相关内容

    bound box即提供视频帧中人体框的信息,在计算前后帧的投影变换矩阵时,不使用人体框中的匹配点对。从而排除人体运动干扰,使得对相机运动的估计更加准确。

    作者提供的文件中没有bb_file的格式,代码中也没有读入bb_file的接口,若需要用到需要在代码中添加一条读入文件语句(下面的代码解析中已经添加)。bb_file的格式如下所示

    frame_id a1 a2 a3 a4 a5 b1 b2 b3 b4 b5

    其中frame_id是帧的编号,从0开始。代码中还有检查步骤,保证bb_file的长度与视频的帧数相同。

    后面的数据5个一组,为人体框的参数。按顺序分别为:框左上角点的x,框左上角点的y,框右下角点的x,框右下角点的y,置信度。需要注意的是虽然要输入置信度,但实际上这个置信度在代码里也没有用上的样子,所以取任意值也不影响使用。

    至于如何获得这些bound box的数据,最暴力的方法当然是手工标注,不过这样太辛苦了。在项目中我们采用了SSD(single shot multibox detector)算法检测人体框的位置。

    主程序代码解析

    iDT算法代码的大致思路为:

    1. 读入新的一帧
    2. 通过SURF特征和光流计算当前帧和上一帧的投影变换矩阵
    3. 使用求得的投影变换矩阵对当前帧进行warp,消除相机运动影响
    4. 利用warp后的当前帧图像和上一帧图像计算光流
    5. 在各个图像尺度上跟踪轨迹并计算特征
    6. 保存当前帧的相关信息,跳到1

    以下通过一些简单的注释对代码进行解析

    #include "DenseTrackStab.h"
    #include "Initialize.h"
    #include "Descriptors.h"
    #include "OpticalFlow.h"
    
    #include <time.h>
    
    using namespace cv;
    
    //如果要可视化轨迹,将show_track设置为1
    int show_track = 0;
    
    int main(int argc, char** argv)
    {
        //读入并打开视频文件
        VideoCapture capture;
        char* video = argv[1];
        int flag = arg_parse(argc, argv);
        capture.open(video);
        if(!capture.isOpened()) {
            fprintf(stderr, "Could not initialize capturing..\n");
            return -1;
        }
    
        //这句代码是我自己添加的,源代码中没有提供bb_file的输入接口
        char* bb_file = argv[2];
    
        int frame_num = 0;
        TrackInfo trackInfo;
        DescInfo hogInfo, hofInfo, mbhInfo;
    
        //初始化轨迹信息变量
        InitTrackInfo(&trackInfo, track_length, init_gap);
        InitDescInfo(&hogInfo, 8, false, patch_size, nxy_cell, nt_cell);
        InitDescInfo(&hofInfo, 9, true, patch_size, nxy_cell, nt_cell);
        InitDescInfo(&mbhInfo, 8, false, patch_size, nxy_cell, nt_cell);
    
        SeqInfo seqInfo;
        InitSeqInfo(&seqInfo, video);
    
        //初始化bb信息,将bb_file中的信息加载到bb_list中
        std::vector<Frame> bb_list;
        if(bb_file) {
            LoadBoundBox(bb_file, bb_list);
            assert(bb_list.size() == seqInfo.length);
        }
    
        if(flag)
            seqInfo.length = end_frame - start_frame + 1;
    
    
    
        if(show_track == 1)
            namedWindow("DenseTrackStab", 0);
    
        //初始化surf特征检测器
        //此处200为阈值,数值越小则用于匹配的特征点越多,效果越好(不一定),速度越慢
        SurfFeatureDetector detector_surf(200);
        SurfDescriptorExtractor extractor_surf(true, true);
    
        std::vector<Point2f> prev_pts_flow, pts_flow;
        std::vector<Point2f> prev_pts_surf, pts_surf;
        std::vector<Point2f> prev_pts_all, pts_all;
    
        std::vector<KeyPoint> prev_kpts_surf, kpts_surf;
        Mat prev_desc_surf, desc_surf;
        Mat flow, human_mask;
    
        Mat image, prev_grey, grey;
    
        std::vector<float> fscales(0);
        std::vector<Size> sizes(0);
    
        std::vector<Mat> prev_grey_pyr(0), grey_pyr(0), flow_pyr(0), flow_warp_pyr(0);
        std::vector<Mat> prev_poly_pyr(0), poly_pyr(0), poly_warp_pyr(0);
    
        std::vector<std::list<Track> > xyScaleTracks;
        int init_counter = 0; // 记录何时应该计算新的特征点
        while(true) {
            Mat frame;
            int i, j, c;
    
            // 读入新的帧
            capture >> frame;
            if(frame.empty())
                break;
    
            if(frame_num < start_frame || frame_num > end_frame) {
                frame_num++;
                continue;
            }
    
            /*-----------------------对第一帧做处理-------------------------*/
            //由于光流需要两帧进行计算,故第一帧不计算光流
            if(frame_num == start_frame) {
                image.create(frame.size(), CV_8UC3);
                grey.create(frame.size(), CV_8UC1);
                prev_grey.create(frame.size(), CV_8UC1);
    
                InitPry(frame, fscales, sizes);
    
                BuildPry(sizes, CV_8UC1, prev_grey_pyr);
                BuildPry(sizes, CV_8UC1, grey_pyr);
                BuildPry(sizes, CV_32FC2, flow_pyr);
                BuildPry(sizes, CV_32FC2, flow_warp_pyr);
                BuildPry(sizes, CV_32FC(5), prev_poly_pyr);
                BuildPry(sizes, CV_32FC(5), poly_pyr);
                BuildPry(sizes, CV_32FC(5), poly_warp_pyr);
    
                xyScaleTracks.resize(scale_num);
    
                frame.copyTo(image);
                cvtColor(image, prev_grey, CV_BGR2GRAY);
    
                //对于每个图像尺度分别密集采样特征点
                for(int iScale = 0; iScale < scale_num; iScale++) {
                    if(iScale == 0)
                        prev_grey.copyTo(prev_grey_pyr[0]);
                    else
                        resize(prev_grey_pyr[iScale-1], prev_grey_pyr[iScale], prev_grey_pyr[iScale].size(), 0, 0, INTER_LINEAR);
                    // 密集采样特征点
                    std::vector<Point2f> points(0);
                    DenseSample(prev_grey_pyr[iScale], points, quality, min_distance);
                    // 保存特征点
                    std::list<Track>& tracks = xyScaleTracks[iScale];
                    for(i = 0; i < points.size(); i++)
                        tracks.push_back(Track(points[i], trackInfo, hogInfo, hofInfo, mbhInfo));
                }
                // compute polynomial expansion
                my::FarnebackPolyExpPyr(prev_grey, prev_poly_pyr, fscales, 7, 1.5);
                //human_mask即将人体框外的部分记作1,框内部分记作0
                //在计算surf特征时不计算框内特征(即不使用人身上的特征点做匹配)
                human_mask = Mat::ones(frame.size(), CV_8UC1);
                if(bb_file)
                    InitMaskWithBox(human_mask, bb_list[frame_num].BBs);
                detector_surf.detect(prev_grey, prev_kpts_surf, human_mask);
                extractor_surf.compute(prev_grey, prev_kpts_surf, prev_desc_surf);
    
                frame_num++;
                continue;
            }
            /*-----------------------对后续帧做处理-------------------------*/
            init_counter++;
            frame.copyTo(image);
            cvtColor(image, grey, CV_BGR2GRAY);
    
            // 计算新一帧的surf特征,并与前一帧的surf特帧做匹配
            // surf特征只在图像的原始尺度上计算
            if(bb_file)
                InitMaskWithBox(human_mask, bb_list[frame_num].BBs);
            detector_surf.detect(grey, kpts_surf, human_mask);
            extractor_surf.compute(grey, kpts_surf, desc_surf);
            ComputeMatch(prev_kpts_surf, kpts_surf, prev_desc_surf, desc_surf, prev_pts_surf, pts_surf);
            // 在所有尺度上计算光流,并用光流计算前后帧的匹配
            my::FarnebackPolyExpPyr(grey, poly_pyr, fscales, 7, 1.5);
            my::calcOpticalFlowFarneback(prev_poly_pyr, poly_pyr, flow_pyr, 10, 2);
            MatchFromFlow(prev_grey, flow_pyr[0], prev_pts_flow, pts_flow, human_mask);
            // 结合SURF的匹配和光流的匹配
            MergeMatch(prev_pts_flow, pts_flow, prev_pts_surf, pts_surf, prev_pts_all, pts_all);
    
            //用上述点匹配计算前后两帧图像之间的投影变换矩阵H
            //为了避免由于匹配点多数量过少造成 投影变换矩阵计算出错,当匹配很少时直接取单位矩阵作为H
            Mat H = Mat::eye(3, 3, CV_64FC1);
            if(pts_all.size() > 50) {
                std::vector<unsigned char> match_mask;
                Mat temp = findHomography(prev_pts_all, pts_all, RANSAC, 1, match_mask);
                if(countNonZero(Mat(match_mask)) > 25)
                    H = temp;
            }
    
            //使用上述得到的投影变换矩阵H对当前帧图像进行warp,从而消除相机造成的运动
            Mat H_inv = H.inv();
            Mat grey_warp = Mat::zeros(grey.size(), CV_8UC1);
            MyWarpPerspective(prev_grey, grey, grey_warp, H_inv); // warp the second frame
    
            // 用变换后的图像重新计算各个尺度上的光流图像
            my::FarnebackPolyExpPyr(grey_warp, poly_warp_pyr, fscales, 7, 1.5);
            my::calcOpticalFlowFarneback(prev_poly_pyr, poly_warp_pyr, flow_warp_pyr, 10, 2);
    
            //在每个尺度分别计算特征
            for(int iScale = 0; iScale < scale_num; iScale++) {
                //尺度0不缩放,其余尺度使用插值方法缩放
                if(iScale == 0)
                    grey.copyTo(grey_pyr[0]);
                else
                    resize(grey_pyr[iScale-1], grey_pyr[iScale], grey_pyr[iScale].size(), 0, 0, INTER_LINEAR);
    
                int width = grey_pyr[iScale].cols;
                int height = grey_pyr[iScale].rows;
    
                // compute the integral histograms
                DescMat* hogMat = InitDescMat(height+1, width+1, hogInfo.nBins);
                HogComp(prev_grey_pyr[iScale], hogMat->desc, hogInfo);
    
                DescMat* hofMat = InitDescMat(height+1, width+1, hofInfo.nBins);
                HofComp(flow_warp_pyr[iScale], hofMat->desc, hofInfo);
    
                DescMat* mbhMatX = InitDescMat(height+1, width+1, mbhInfo.nBins);
                DescMat* mbhMatY = InitDescMat(height+1, width+1, mbhInfo.nBins);
                MbhComp(flow_warp_pyr[iScale], mbhMatX->desc, mbhMatY->desc, mbhInfo);
    
                // 在当前尺度 追踪特征点的轨迹,并计算相关的特征
                std::list<Track>& tracks = xyScaleTracks[iScale];
                for (std::list<Track>::iterator iTrack = tracks.begin(); iTrack != tracks.end();) {
                    int index = iTrack->index;
                    Point2f prev_point = iTrack->point[index];
                    int x = std::min<int>(std::max<int>(cvRound(prev_point.x), 0), width-1);
                    int y = std::min<int>(std::max<int>(cvRound(prev_point.y), 0), height-1);
    
                    Point2f point;
                    point.x = prev_point.x + flow_pyr[iScale].ptr<float>(y)[2*x];
                    point.y = prev_point.y + flow_pyr[iScale].ptr<float>(y)[2*x+1];
    
                    if(point.x <= 0 || point.x >= width || point.y <= 0 || point.y >= height) {
                        iTrack = tracks.erase(iTrack);
                        continue;
                    }
    
                    iTrack->disp[index].x = flow_warp_pyr[iScale].ptr<float>(y)[2*x];
                    iTrack->disp[index].y = flow_warp_pyr[iScale].ptr<float>(y)[2*x+1];
    
                    // get the descriptors for the feature point
                    RectInfo rect;
                    GetRect(prev_point, rect, width, height, hogInfo);
                    GetDesc(hogMat, rect, hogInfo, iTrack->hog, index);
                    GetDesc(hofMat, rect, hofInfo, iTrack->hof, index);
                    GetDesc(mbhMatX, rect, mbhInfo, iTrack->mbhX, index);
                    GetDesc(mbhMatY, rect, mbhInfo, iTrack->mbhY, index);
                    iTrack->addPoint(point);
    
                    // 在原始尺度上可视化轨迹
                    if(show_track == 1 && iScale == 0)
                        DrawTrack(iTrack->point, iTrack->index, fscales[iScale], image);
    
                    // 若轨迹的长度达到了预设长度,在iDT中应该是设置为15
                    // 达到长度后就可以输出各个特征了
                    if(iTrack->index >= trackInfo.length) {
                        std::vector<Point2f> trajectory(trackInfo.length+1);
                        for(int i = 0; i <= trackInfo.length; ++i)
                            trajectory[i] = iTrack->point[i]*fscales[iScale];
    
                        std::vector<Point2f> displacement(trackInfo.length);
                        for (int i = 0; i < trackInfo.length; ++i)
                            displacement[i] = iTrack->disp[i]*fscales[iScale];
    
                        float mean_x(0), mean_y(0), var_x(0), var_y(0), length(0);
                        if(IsValid(trajectory, mean_x, mean_y, var_x, var_y, length) && IsCameraMotion(displacement)) {
                            // output the trajectory
                            printf("%d\t%f\t%f\t%f\t%f\t%f\t%f\t", frame_num, mean_x, mean_y, var_x, var_y, length, fscales[iScale]);
    
                            // for spatio-temporal pyramid
                            printf("%f\t", std::min<float>(std::max<float>(mean_x/float(seqInfo.width), 0), 0.999));
                            printf("%f\t", std::min<float>(std::max<float>(mean_y/float(seqInfo.height), 0), 0.999));
                            printf("%f\t", std::min<float>(std::max<float>((frame_num - trackInfo.length/2.0 - start_frame)/float(seqInfo.length), 0), 0.999));
    
                            // output the trajectory
                            for (int i = 0; i < trackInfo.length; ++i)
                                printf("%f\t%f\t", displacement[i].x, displacement[i].y);
                            //实际上,traj特征的效果一般,可以去掉,那么输出以下几个就好了
                            //如果需要保存输出的特征,可以修改PrintDesc函数
                            PrintDesc(iTrack->hog, hogInfo, trackInfo);
                            PrintDesc(iTrack->hof, hofInfo, trackInfo);
                            PrintDesc(iTrack->mbhX, mbhInfo, trackInfo);
                            PrintDesc(iTrack->mbhY, mbhInfo, trackInfo);
                            printf("\n");
                        }
    
                        iTrack = tracks.erase(iTrack);
                        continue;
                    }
                    ++iTrack;
                }
                ReleDescMat(hogMat);
                ReleDescMat(hofMat);
                ReleDescMat(mbhMatX);
                ReleDescMat(mbhMatY);
    
                if(init_counter != trackInfo.gap)
                    continue;
    
                // detect new feature points every gap frames
                std::vector<Point2f> points(0);
                for(std::list<Track>::iterator iTrack = tracks.begin(); iTrack != tracks.end(); iTrack++)
                    points.push_back(iTrack->point[iTrack->index]);
    
                DenseSample(grey_pyr[iScale], points, quality, min_distance);
                // save the new feature points
                for(i = 0; i < points.size(); i++)
                    tracks.push_back(Track(points[i], trackInfo, hogInfo, hofInfo, mbhInfo));
            }
    
            //这里有好多个copyTo prev_xxx
            //因为计算光流,surf匹配等都需要上一帧的信息,故在每帧处理完后保存该帧信息,用作下一帧计算时用
            init_counter = 0;
            grey.copyTo(prev_grey);
            for(i = 0; i < scale_num; i++) {
                grey_pyr[i].copyTo(prev_grey_pyr[i]);
                poly_pyr[i].copyTo(prev_poly_pyr[i]);
            }
    
            prev_kpts_surf = kpts_surf;
            desc_surf.copyTo(prev_desc_surf);
    
            frame_num++;
    
            if( show_track == 1 ) {
                imshow( "DenseTrackStab", image);
                c = cvWaitKey(3);
                if((char)c == 27) break;
            }
        }
    
        if( show_track == 1 )
            destroyWindow("DenseTrackStab");
    
        return 0;
    }
    <std::list
    
    

    以上只是对程序代码的简单解析,如果需要使用到iDT的代码还是需要自己好好研究代码的,此篇笔记也只算是自己的一个笔记啦。个人感受iDT算法的思路非常经典,有很多值得参考的地方,代码也写的很好,可以进行修改用到别的地方。

    更多相关内容
  • iDT算法

    千次阅读 2015-08-21 14:58:40
    Improve dense trajectory简称iDT,是一种用来提取视频密集跟踪轨迹的算法;通常基于该轨迹进行取块计算descriptor; 1.iDT计算 (1)概念:在视频序列中对每一帧的兴趣点进行跟踪就形成trajectory,若是对...

    Improve dense trajectory简称iDT,是一种用来提取视频密集跟踪轨迹的算法;通常基于该轨迹进行取块计算descriptor;


    1.iDT计算

    (1)概念:在视频序列中对每一帧的兴趣点进行跟踪就形成trajectory,若是对每一帧密集采样兴趣点进行跟踪就形成dense trajectory;

    (2)算法:

    1)对整个视频序列进行光流场计算(光流场,它是指图像中所有像素点构成的一种二维瞬时速度场,其中的二维速度矢量是景物中可见点的三维速度矢量在成像表面的投影);

    2)对初始帧进行像素点密集采样,每隔W个像素点采样一个;

    3)对采样点进行跟踪:由光流判断跟踪点在下一帧的位置;

    M为中值滤波器,w为光流场

    4)对每个点跟踪都会形成一条trajectory,为了避免长时间跟踪而产生的跟踪点漂移现象,可以对跟踪的长度L进行约束(L=15)


    5)现实视频中存在摄像头运动的缺陷,因此需要相应算法消除摄像头影响,得到最终的iDT;

    2.基于iDT计算descriptor

    (1)轨迹特征:每条trajectory都可以提取一个轨迹特征向量S'(当L=15,S’为30维),对局部动作模式进行编码


    (2)HOG/HOF特征

    1)HOG特征对视频块进行表面特征描述;HOF特征对局部动作信息进行描述;

    2)以trajectory的每个点为中心,沿着轨迹取块,大小为N*N*L,为了引入结构信息,还对视频块进行网格划分,分为个cell,对每个cell做HOG/HOF特征提取(若N=32,L=15,,则每个cell的大小为16*16*5,每个cell的HOG特征维数为8,HOF特征维数为9,因此整个N*N*L的HOG特征维数为8*2*2*3=96,HOF特征维数为9*2*2*3=108);

    (3)MBH特征

    1)HOF统计的是视频的绝对运动信息(0阶运动信息),MBH统计的是视频的相对运动信息(1阶运动信息)同时也可以消除掉摄像头运动的影响;

    2)MBH特征就是对光流图就行HOG特征统计,由于光流场有x,y分量,所以就有MBHx特征与MBHy特征;

    3)以trajectory的每个点为中心,沿着轨迹取块,大小为N*N*L,为了引入结构信息,还对视频块进行网格划分,分为个cell,对每个cell做MBH特征提取(若N=32,L=15,,则每个cell的大小为16*16*5,每个cell的MBHx特征维数为8,MBHy特征维数为,因此整个N*N*L的MBHx特征维数为8*2*2*3=96,MBHy特征维数为8*2*2*3=96);

    参考文献:Action Recognition with Improved Trajectories

    展开全文
  • IDT算法分析PPT教案.pptx
  • 运行iDT算法代码及后续特征编码

    千次阅读 热门讨论 2017-11-27 09:28:20
    IDT算法应该算是行为识别领域中经典中的经典了,自从13年提出以来,在HMDB-51和UCF-101等若干个数据库上得到了非常好的效果,虽然DT&amp;IDT算法做不过深度学习,但是由于其算法的有效性,现在大部分都是以...

    DT&IDT算法应该算是行为识别领域中经典中的经典了,自从13年提出以来,在HMDB-51和UCF-101等若干个数据库上得到了非常好的效果,虽然DT&IDT算法做不过深度学习,但是由于其算法的有效性,现在大部分都是以“Ours+IDT”的形式呈现在论文里,并且加上了IDT后,结果的确能得到很明显的提升。
    具体IDT算法的讲解可以参考博客:行为识别笔记:improved dense trajectories算法(iDT算法)
    IDT算法的代码可以在作者的主页上下载,另外作者给出的代码只包括了密集采样+轨迹跟踪+特征提取三个部分,后续的特征编码部分请点击此处下载。


    1. 运行IDT算法代码

    下载好代码后可以仔细阅读一下Readme文件,首先需要配置好Opencv和ffmpeg两个库,具体的版本请参考Readme文件。配置好后就可以在目录下直接make,编译好的可执行文件在./release/下。
    可以运行他的demo视频来看一下是否编译成功:./release/DenseTrackStab ./test_sequences/person01_boxing_d1_uncomp.avi,如果编译成功的话会在屏幕上打印出一系列的数字,这些就是轨迹特征以及HOG, HOF和MBH特征。
    IDT代码只是将各个特征打印了出来,如果需要改成保存到文件的话,更改第248-251行的代码即可:

    PrintDesc(iTrack->hog, hogInfo, trackInfo);
    PrintDesc(iTrack->hof, hofInfo, trackInfo);
    PrintDesc(iTrack->mbhX, mbhInfo, trackInfo);
    PrintDesc(iTrack->mbhY, mbhInfo, trackInfo);

    另外,在做自己的数据库时,不可能一个视频一个视频的去输入,这时可以写一个vim脚本,来批量运行,可以参考如下代码:

    #!/usr/bin/env bash
    
    cmd="release/DenseTrackStab"
    filePath="" #数据存放路径
    fileType=".txt"
    function readfile(){
        for file in `ls $1`
        do
            if [ -d $1"/"$file ]
            then
                readfile $1"/"$file
            else
                echo $file
                $cmd $1"/"$file
            fi
        done
    }
    readfile $filePath

    2. 特征编码

    后续的特征编码采用了dtfv代码运行,具体可以参考src/中的Readme.rst文件,大致步骤如下:

    1. 下载vl_feat并编译

    编译好vl_feat工具箱后,将vlfeats/bin/glnx64中的libvl.so文件拷贝到dtfv/src/vl文件夹中,如果拷贝glnx86中的libvl.so后编译时会报错

    2. 编译

    1. 在src文件夹中输入make进行编译:
      这里写图片描述
    2. 改变路径
      在../script/extract_fv.py文件中更改一下几个变量:dtBin、fvBin、tmpDir、pID、pcaList,具体可以参考Readme文件。
      这里贴一下我所用的变量:
      这里写图片描述

    3. 运行代码
      在../data/文件夹中运行:python extract_fv.py [videoList] [outputDir] [totalCores]。之后会在tmpDir中得到resize之后的视频文件,每个视频将在outputDir文件夹中得到五个文件,分别以[Videoname].avi.[traj|hog|hof|mbhx|mbhy].fv.txt命名。


    遇到的bug:
    这里写图片描述
    error while loading shared libraries: libvl.so: cannot open shared object file: No such file or directory
    【解决方法】LD_LIBRARY_PATH=$LD_LIBRARY_PATH:dtfv-master/src/vl
    LD_LIBRARY_PATH=$LD_LIBRARY_PATH: dtfv-master/src/alglib

    (注意,路径要输入的是绝对路径)

    展开全文
  • 视频行为识别第一讲:iDT算法

    千次阅读 2018-03-14 23:50:49
    转发请注明出处:http://blog.csdn.net/wzmsltw/article/details/53023363iDT算法是...目前基于深度学习的行为识别算法效果已经超过了iDT算法,但与iDT的结果做ensemble总还是能获得一些提升。所以这几年好多论文的...

    转发请注明出处:http://blog.csdn.net/wzmsltw/article/details/53023363

    iDT算法是行为识别领域中非常经典的一种算法,在深度学习应用于该领域前也是效果最好的算法。由INRIA的IEAR实验室于2013年发表于ICCV。目前基于深度学习的行为识别算法效果已经超过了iDT算法,但与iDT的结果做ensemble总还是能获得一些提升。所以这几年好多论文的最优效果都是“Our method+iDT”的形式。

    此前由于项目原因,对iDT算法进行了很多研究和实验,故此处对其核心思路与一些实施的细节进行总结,方便后续回顾,也希望能够在此过程中获得一些新的启发。

    介绍的内容主要包含两篇文章的内容,分别是”Dense Trajectories and Motion Boundary Descriptors for Action Recognition”和”Action Recognition with Improved Trajectories”。这两篇都是H. Wang的文章,前者要更早一些,介绍了DT(Dense Trajectories)算法。后者则在前者的基础上进行了改进(improved),主要是引入了对背景光流的消除方法,使得特征更集中于对人的运动的描述。两者的框架大致相同,本文先对DT算法进行介绍,再介绍iDT算法的改进之处。

    iDT的代码可以在其个人主页上下到,也可以点击此处下载。

    对iDT特征进行FV编码的代码可以在dtfv 下载。

    更新了iDT算法的代码解析,见iDT算法用法与代码解析

    密集轨迹算法(DT算法)

    算法基本框架



    如图所示即为算法的基本框架,包括密集采样特征点,特征点轨迹跟踪和基于轨迹的特征提取几个部分。后续的特征编码和分类过程则没有在图中画出。

    密集采样

    DT方法通过网格划分的方式在图片的多个尺度上分别密集采样特征点。在多个空间尺度上采样能保证采样的特征点覆盖了所有空间位置和尺度,通常8个空间尺度已经非常足够了,若图像很大,可以适当增加。后续的特征提取也是在各个尺度上分别进行的。特征点采样的间隔(即网格的大小)W通常取W=5。

    下一步的目标即在时间序列上跟踪这些特征点,但在缺乏变化的区域(比如一块白色墙壁中间的点)中跟踪特征点是无法实现的。因此在进行跟踪前要先去除一些特征点。此处的方法是计算每个像素点自相关矩阵的特征值,并设置阈值去除低于阈值的特征点。阈值由下式决定: 

    T=0.001×maxiImin(λ1i,λ2i)T=0.001×maxi∈Imin(λi1,λi2)

    式中 (λ1i,λ2i)(λi1,λi2) 是图像I中像素点i的特征值。0.001为实验确定的一个比较合适的值。下图即为密集采样的一个示例效果图片。 


     

    轨迹与轨迹描述子(trajectories)

    设上一步中密集采样到的某个特征点的坐标为Pt=(xt,yt)Pt=(xt,yt),则我们可以用下式来计算该特征点在下一帧图像中的位置。 

    Pt+1=(xt+1,yt+1)=(xt,yt)+(Mωt)|xt,ytPt+1=(xt+1,yt+1)=(xt,yt)+(M∗ωt)|xt,yt

    式中 ωt=(ut,vt)ωt=(ut,vt) 为密集光流场,是由 ItIt It+1It+1 计算得到的,u和v分别代表光流的水平和垂直分量。而M则代表中值滤波器,尺寸为3*3。故该式子是通过计算特征点邻域内的光流中指来得到特征点的运动方向的。

    某个特征点在连续的L帧图像上的位置即构成了一段轨迹(Pt,Pt+1,...,Pt+L)(Pt,Pt+1,...,Pt+L),后续的特征提取即沿着各个轨迹进行。由于特征点的跟踪存在漂移现象,故长时间的跟踪是不可靠的,所以每L帧要重新密集采样一次特征点,重新进行跟踪。在DT/iDT算法中,选取L=15。

    此外,轨迹本身也可以构成轨迹形状特征描述子。对于一个长度为L的轨迹,其形状可以用(ΔPt,...,ΔPt+L1)(ΔPt,...,ΔPt+L−1)来描述,其中位移矢量ΔPt=(Pt+1Pt)=(xt+1xt,yt+1yt)ΔPt=(Pt+1−Pt)=(xt+1−xt,yt+1−yt)。在进行正则化后就可以得到轨迹特征描述子了。正则化方式为: 

    T=(ΔPt,...,ΔPt+L1)t+L1j=t||ΔPj||T=(ΔPt,...,ΔPt+L−1)∑j=tt+L−1||ΔPj||

    故最终得到的轨迹特征为15*2=30维向量。

    运动/结构描述子(HOF,HOG,MBH)

    除了轨迹形状特征,我们还需要更有力的特征来描述光流,DT/iDT中使用了HOF,HOG和MBH三种特征。此前我写了一篇博客对这几种特征进行了介绍。

    首先对这几种特征提取的通用部分进行介绍。沿着某个特征点的长度为L的轨迹,在每帧图像上取特征点周围的大小为N×NN×N的区域,则构成了一个时间-空间体(volume),如算法基本框架图的右半部分所示。对于这个时间-空间体,在进行一次网格划分,空间上每个方向上分为nσ份,时间上则均匀选取nτ份。故在时间-空间体中共分出nσ×nσ×nτnσ×nσ×nτ份区域用作特征提取。在DT/iDT中,取N=32,nσ=2,nτ=3N=32,nσ=2,nτ=3,接下来对各个特征的提取细节进行介绍。

    • HOG特征:HOG特征计算的是灰度图像梯度的直方图。直方图的bin数目取为8。故HOG特征的长度为96(2*2*3*8)。
    • HOF特征:HOF计算的是光流(包括方向和幅度信息)的直方图。直方图的bin数目取为8+1,前8个bin于HOG相同,额外的一个bin用于统计光流幅度小于某个阈值的像素。故HOF的特征长度为108(2*2*3*9)。
    • MBH特征:MBH计算的是光流图像梯度的直方图,也可以理解为在光流图像上计算的HOG特征。由于光流图像包括x方向和y方向,故分别计算MBHx和MBHy。MBH总的特征长度为192(2*96)。

    在计算完后,还需要进行特征的归一化,DT算法中对HOG,HOF和MBH均使用L2范数归一化。

    特征编码—Bag of Features

    对于一段视频,存在着大量的轨迹,每段轨迹都对应着一组特征(trajectory,HOG,HOF,MBH),因此需要对这些特征组进行编码,得到一个定长的编码特征来进行最后的视频分类。

    DT算法中使用Bag of Features方法进行特征的编码,Bag of Features方法的介绍见这篇博文——Bag of Features(BOF)图像检索算法,这篇博文也是转载的,但无奈原文地址已经打不开了。在训练码书时,DT算法随机选取了100000组特征进行训练。码书的大小则设置为4000。

    在训练完码书后,对每个视频的特征组进行编码,就可以得到视频对应的特征。

    分类-SVM

    在得到视频对应的特征后,DT算法采用SVM(RBFχ2RBF−χ2核)分类器进行分类,采用one-against-rest策略训练多类分类器。

    提升的密集轨迹算法(iDT算法)

    iDT算法的基本框架和DT算法相同,主要改进在于对光流图像的优化,特征正则化方式的改进以及特征编码方式的改进。这几处改进使得算法的效果有了巨大的提升,在UCF50数据集上的准确率从84.5%提高到了91.2%,在HMDB51数据集上的准确率从46.6%提高到了57.2%。下面分别对几处改进进行进行介绍。

    相机运动估计

    首先是最重要的一处改进,通过估计相机运动估计来消除背景上的光流以及轨迹。首先看DT算法中在没消除背景干扰时的轨迹分布。 



    可以看出,由于相机在运动,所以背景上也有很多轨迹,人的轨迹也受到相机运动的很大影响。而这些信息与要识别的动作关系是不大的,属于干扰信息。因此就希望能够识别并消除这些轨迹。而实际上轨迹的运动也是通过计算光流信息进行计算的,因此需要通过估计相机运动,来消除背景区域的光流。

    由于相邻两帧图像之间变化比较小,iDT算法假设相邻的两帧图像之间的关系可以用一个投影变换矩阵来描述,即后一帧图像是前一帧图像通过投影变换得到的。因此,估计相机运动的问题就变成了利用前后帧图像计算投影变换矩阵的问题。

    为了准确得估计投影变换,iDT算法中采用了两种方法来获得匹配点对。分别为SURF特征以及光流特征。在获得匹配的点对后,就可以利用RANSAC算法估计投影变换矩阵了。具体操作为:记t时刻和t+1时刻的灰度图像分别为ItItIt+1It+1,用两张图像计算得到投影变换矩阵H(It+1=H×ItIt+1=H×It)。然后用H的逆对It+1It+1进行变换(warp),即Iwarpt+1=H1×It+1It+1warp=H−1×It+1Iwarpt+1It+1warp代表假设不存在相机运动时t+1时刻的图像。用ItItIwarpt+1It+1warp就可以计算得到优化过的光流。

    此处可以注意到很大的一个问题,那就是图像中人的动作可能比较显著,人身上的匹配点对会使得投影矩阵的估计不准确。因此iDT算法中使用一个huaman detector检测人的位置框,并去除该框中的匹配点对。从而使得人的运动不影响投影矩阵的估计。iDT中使用的是当时效果最好的human detector,其文章为”Weakly supervised learning of interactions between humans and objects”。而现在物体检测领域已经完全被深度学习攻陷了,比较好的方法包括faster-rcnn,ssd等算法。其中ssd的速度很快,效果也不错,推荐使用。 


    综合以上的改进算法,可以得到如上效果图。图像左侧两列图是不使用human detector的效果,右边两列图是使用human detector时的效果。可以看出加入human detector对效果的提升非常巨大。最下面一列则是比较失败的情况,按照文章中的分析,原因主要包括两点:1)运动模糊;2)人物占图像比例高时相机运动估计不准。

    从光流中消除相机运动带来的影响主要有两点好处: 
    1. 运动描述子(主要指HOF和MBH)能更准确的描述动作,用单一描述子的分类准确率比起DT中有很大的提高 
    2. 由于轨迹也是利用光流进行运算的,因此,可以通过设置阈值,消除优化后的光流中位移矢量的幅值小于阈值的轨迹。

    特征归一化方式

    在iDT算法中,对于HOF,HOG和MBH特征采取了与DT算法(L2范数归一化)不同的方式——L1正则化后再对特征的每个维度开平方。这样做能够给最后的分类准确率带来大概0.5%的提升。

    特征编码—Fisher Vector

    特征编码阶段iDT算法不再使用Bag of Features方法,而是使用效果更好的Fisher Vector编码,其具体编码方式见我之前写的博文: 机器学习笔记:Fisher Vector基本原理与用法。Fisher Vector同样也是先用大量特征训练码书,再用码书对特征进行编码。在iDT中使用的Fisher Vector的各个参数为:

    • 用于训练的特征长度:trajectory+HOF+HOG+MBH=30+96+108+192=426维
    • 用于训练的特征个数:从训练集中随机采样了256000个
    • PCA降维比例:2,即维度除以2,降维后特征长度为213。先降维,后编码
    • Fisher Vector中高斯聚类的个数K:K=256

    故编码后得到的特征维数为2KD2KD个,即109056维。在编码后iDT同样也使用了SVM进行分类。在实际的实验中,推荐使用liblinear,速度比较快。

    总结与讨论

    iDT算法作为深度学习之前最好的行为识别算法,有着优良的效果和很好的鲁棒性。其中有很多非常值得借鉴的思路,比如相机运动引起的背景光流的消除,比如沿着轨迹提取特征的思路。CVPR2015中的”Action Recognition with Trajectory-Pooled Deep-Convolutional Descriptors”的思路就是沿着轨迹利用CNN提取特征,取得了进一步的效果提升。由于iDT还公开了源码,里面对算法的实现思路非常清晰,所以后续可能还会写一篇iDT的代码分析。

    展开全文
  • iDT算法是行为识别领域中非常经典的一种算法,在深度学习应用于该领域前也是效果最好的算法。由INRIA的IEAR实验室于2013年发表于ICCV。目前基于深度学习的行为识别算法效果已经超过了iDT算法,但与iDT的结果做...
  • iDT算法之后的FV编码 一个视频会生成 *.avi.hof.fv.txt,*.avi.hog.fv.txt等五个文本, 我在使用libsvm训练以前将每个视频产生的五个文本合为一个1*5n的txt文本, 然后在合成成m*5n(m为训练样本个数)的训练样本 发现...
  • 3 提升的密集轨迹算法(iDT算法iDT算法的基本框架和DT算法相同,主要改进在于对光流图像的优化,特征正则化方式的改进以及特征编码方式的改进。这几处改进使得算法的效果有了巨大的提升,在UCF50数据集上的准确...
  • iDT 算法 windows上编译

    千次阅读 热门讨论 2017-11-27 17:06:32
    improved dense trajectories 算法,论文中给出的代码是在Linux系统上编译的,有几个与Linux系统相关的函数,自己在windows vs2013上编译的时候将这些函数用其他的函数代替了。 改过之后的代码:链接:...
  • 一.下载OpenCV和ffmpeg的源码包 第一次写教程~ ...最近在跑IDT(Improved Dense Trajectories)的代码,配置环境什么的都是新手上路~所以记录一下。 opencv可以官网直接下载,附链接: opencv-2.4.9 ...
  • iDT算法及后续DTFV编码笔记(ubuntu)iDT算法环境搭建遇到的问题1.输入make指令时报错——lopencv_nonfree和libavdevice缺失(报错如下)lopencv_nonfreelavdevice2.使用g++编译指令时报错(报错如下)解决方法3....
  • Ubantu16.04下opencv-2.4.13及ffmpeg-3.4.8安装教程(含iDT算法运行) -------本文仅为学习笔记,不做任何商业用途------- 前言     最近在复现一篇多视角图像融合的论文,而论文中对于视频特征的提取首先是通过...
  • 最近在学习iDT算法,刚刚接触Ubuntu也刚刚接触c++,很多东西不是很懂,请教各位: 我在编译iDT算法的时候,需要依赖opencv和ffmpeg两个库,我安装成功并且确保这两个库能用之后,去编译iDT算法,'make'之后报错: ...
  • iDT(改进的密集轨迹)和Fisher Vector算法用C++实现 IDT + FisherVector编码 IDT 特征提取 IDT 的官方代码在这里 https://lear.inrialpes.fr/people/wang/dense_trajectories 依赖的工具包是OpenCV2.4 和 ffmpeg....
  • iDT-FV-for-action-recogniton iDT(mproved dense trajectories ) and Fisher Vector algorithm implement with C++ IDT + FisherVector编码 IDT 特征提取 IDT 的官方代码在这里 依赖的工具包是OpenCV2.4 和 ffmpeg...
  • 主要参考博文 行为识别笔记:improved dense trajectories算法(iDT算法)  一.DT介绍  先简单介绍DT(Dense Trajectories)方法:利用光流场来获得视频序列中的轨迹,在沿着轨迹提取轨迹形状特征和HOF,HOG,MBH...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 2,035
精华内容 814
关键字:

idt算法