源代码:THOTH - Dense Trajectories Video Description - Heng Wang(是作者2013年九月发布的第三版,距今(2019年11月)已经6年了)
测试成功的平台:opencv2.4.13.6(目前opencv2的最新版),ubunut16.04(gcc 5.4.0,g++ 5.4.0,make 4.1)
(注:标明平台不是为了限制和我用的一样,只是记录一下这样的配置可以编译成功,其他的opencv版本以及平台也应该可以,关键是opencv要能够正常解码视频)
一. 跑通代码
1.编译opencv (注意opencv编译的时候要打开ffmpeg,编译之前还需要装的一些库可以参考网上其他资料或者官网)
cd opencv2.4.13
mkdir build
cd build
cmake .. -DCMAKE_BUILD_TYPE=RELEASE -DCMAKE_INSTALL_PERFIX=/usr/local -DWITH_FFMPEG=ON
# -DWITH_FFMPEG=ON就是打开ffmpeg的接口
# opencv编译通过基本上就没什么问题了
make
# 然后安装即可
sudo make install
# 添加环境变量
gedit ~/.bashrc
export PATH=/usr/local/bin:$PATH
export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH
export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig:$PKG_CONFIG_PATH
2. 测试opencv读取视频是否正常
将下面两个文件(main.cpp, CMakeLists.txt)拷贝下来,创建一个test文件夹放到里面
cd test
cmake . #(注意cmake +空格+点)
make
#运行看视频播放是否成功,如果不成功,请看第3步
./display_video xx/xx/xx.avi
/*************main.cpp**************/
#include <iostream>
#include <opencv/highgui.h>
using namespace std;
using namespace cv;
int main(int argc, char** argv){
char* video = argv[1];
VideoCapture capture(video);
if(!capture.isOpened())
{
cout<<"Movie open Error"<<endl;
return -1;
}
double rate=capture.get(CV_CAP_PROP_FPS);
cout<<"帧率为:"<<" "<<rate<<endl;
cout<<"总帧数为:"<<" "<<capture.get(CV_CAP_PROP_FRAME_COUNT)<<endl;
Mat frame;
namedWindow("Movie Player");
double position=0.0;
capture.set(CV_CAP_PROP_POS_FRAMES,position);
while(capture.isOpened())
{
if(!capture.read(frame))
break;
imshow("Movie Player",frame);
char c=waitKey(33);
if(c==27)
break;
}
capture.release();
destroyWindow("Movie Player");
return 0;
}
/*********************CMakeLists.txt******************************/
# cmake needs this line
cmake_minimum_required(VERSION 2.8)
# Define project name
project(display_video)
# Find OpenCV, you may need to set OpenCV_DIR variable
# to the absolute path to the directory containing OpenCVConfig.cmake file
# via the command line or GUI
find_package(OpenCV REQUIRED)
# If the package has been found, several variables will
# be set, you can find the full list with descriptions
# in the OpenCVConfig.cmake file.
# Print some message showing some of them
message(STATUS "OpenCV library status:")
message(STATUS " version: ${OpenCV_VERSION}")
message(STATUS " libraries: ${OpenCV_LIBS}")
message(STATUS " include path: ${OpenCV_INCLUDE_DIRS}")
if(CMAKE_VERSION VERSION_LESS "2.8.11")
# Add OpenCV headers location to your include paths
include_directories(${OpenCV_INCLUDE_DIRS})
endif()
# Declare the executable target built from your sources
add_executable(display_video main.cpp ${BACKWARD_ENABLE})
# Link your application with OpenCV libraries
target_link_libraries(display_video ${OpenCV_LIBS})
3. 如果播放不成功,肯定是ffmpeg的问题。其中一种原因是编译opencv时ffmpeg接口没编译进来,即便指定-DWITH_FFMPEG=ON,但是编译时没找到系统的ffmpeg库所在位置,会忽略掉这个参数(所以在make之前,仔细看看cmake的输出信息,配置参数FFMPEG是否真的打开);另一种是系统已有的ffmpeg库和opencv不匹配。总之需要为系统重新安装ffmpeg库,两种方式安装。(建议方式1)
(1).建议直接从源安装ffmpeg库, 这样会比较契合当前系统。
# 下载编译好的ffmpeg库
apt-get download ffmpeg
# 解压
dpkg -x ffmpeg-xxx.deb ./
# 解压后就有ffmpeg的头文件以及库文件,把它放到合适的位置即可
(2).通过源码安装ffmpeg,最好选择与opencv发行日期接近的版本,否则依旧会有不匹配的问题.(注意如果自定义了安装路径,安装完了加环境变量,否则系统认为没安装)
4. 读取视频没问题的话,按照源码的readme文件指导,编译Dense Trajectories 项目代码,查看提取出来的特征文件。
注意: 要修改Makefile文件中链接库的路径,LDFLAGS = -L/opt/lib -pipe -Wall $(LDFLAGS_$(BUILD))中的/opt/lib要改成你实际安装opencv的路径,比如上面在编译opencv时我们指定的 --prefix=/usr/local,所以要将/opt/lib 改为/usr/local/lib。修改后的Makefile片段如下:
...
# set some flags and compiler/linker specific commands
CXXFLAGS = -pipe -D __STDC_CONSTANT_MACROS -D STD=std -Wall $(CXXFLAGS_$(BUILD)) -I. -I/opt/include
CXXFLAGS_debug := -ggdb
CXXFLAGS_release := -O3 -DNDEBUG -ggdb
LDFLAGS = -L/usr/local/lib -pipe -Wall $(LDFLAGS_$(BUILD))
LDFLAGS_debug := -ggdb
LDFLAGS_release := -O3 -ggdb
include make/generic.mk
...
二.代码分析
运行实例会得到一个out.features.gz文件,解压出来的文件看不懂是什么意思,所以需要读源代码理解。(看论文只能理解思想,具体到问题上还是得一句一句分析代码)。论文提到最后每条轨迹的描述信息是426维,但得到的输出文件每行436维。经过分析代码可知,特征向量的格式为:
curent_frame_index trajectory_points_x_mean trajectory_points_y_mean trajectory_points_x_var trajectory_points_y_var sum_sqrt(x^2+y^2) current_pyr_scale x_norm y_norm frame_index_norm ...(and concate 426 dimensions of track_point_pos(x,y) and hog_hof_MBH descriptors)
前10个数字是对这条轨迹的整体信息进行统计:
current_frame_index: 表示该条轨迹在此帧长到了15(论文中说到的规定一个轨迹最长15),意味着该条轨迹的起点在第(current_frame_index - 15)帧,且在此后的15帧中都没有断。
trajectory_points_x_mean: 这条轨迹上15个点横坐标的均值
trajectory_points_y_mean: 这条轨迹上15个点纵坐标的均值
trajectory_points_x_var: 这条轨迹上15个点横坐标的方差
trajectory_points_y_var: 这条轨迹上15个点纵坐标的方差
trajectory_points_x_mean: 这条轨迹上15个点坐标模求和
current_pyr_scale: 该轨迹处在金字塔层的尺度因子(1,根号2,2,2*根号2,4,4*根号2,8,8*根号2,八种尺度中的一种)
x_norm: x_mean的归一化值
y_norm: y_mean的归一化值
frame_index_norm: frame_index的归一化值
剩余的426个数字对应论文里所说的displace(x,
y)_hog_hof_mbhx_mby
几点说明:
1. 每帧图像都会有新的轨迹起点加进来,从第二帧开始每帧也会有轨迹断掉被drop掉,只有坚持到长度长到15的时候才会将这条轨迹信息保留下来
2.轨迹起点的选择:将当前帧图像密集切分成3*3的小网格,使用cornerMinEigenVal检测网格的角点。然后去掉在已有轨迹一定范围的角点,剩余的点全部作为轨迹起点。
3. drop跟丢的轨迹:如果一个小局部前后两帧变化过于剧烈(比如遮挡,物体一闪而过),预测的光流场位移将会很大,加到轨迹的前一个点上,肯定会超出整个图像边界,认为这样的轨迹是断掉了,会将整条残缺的轨迹删掉。
4. 每个轨迹点track_point会为其计算二维图像平面上32*32大小区域的hog,hof,mbhx,mbhy, 分成2*2=4个小块,得到(4*8+4*9+4*8+4*8)=132维向量。只有在轨迹长度达到15之后,沿轨迹长度分为3小段,每段求和,得到3段132维的向量,concate起来,就形成了132*3=396的轨迹时空直方图描述子。(10+30+396)=436,其中30是15个相邻个轨迹点的横纵坐标位移:,作者还进行了位移的归一化,即除以轨迹长度(
)。如果想画出轨迹,可以修改源码,让其输出绝对坐标。