Temporal Segment Network (TSN, ECCV2016) 代码笔记 —— 数据预处理: 光流提取。
# build_of.py
def dump_frames(video_path); # 并未在光流提取这一步被调用
def run_optical_flow(vid_item, dev_id=0); # 负责提取tvl1光流
def run_warp_optical_flow(vid_item, dev_id=0); # 负责提取warp_tvl1光流
def nonintersection(lst1, lst2); # 从截断点开始继续提取光流
build_of.py 主函数调用:
vid_list = glob.glob(src_path+'/*.'+ext) # 查找source目录下所有的.$ext文件, ext默认为avi
print("total number of videos found: ", len(vid_list))
pool = Pool(num_worker) # 并行执行, from multiprocessing import Pool
if flow_type == 'tvl1':
pool.map(run_optical_flow, zip(vid_list, range(len(vid_list))))
elif flow_type == 'warp_tvl1':
pool.map(run_warp_optical_flow, zip(vid_list, range(len(vid_list))))
build_of.py tvl1光流提取脚本函数:
def run_optical_flow(vid_item, dev_id=0):
vid_path = vid_item[0] # vid_path 视频原路径地址
vid_id = vid_item[1] # vid_id 视频ID
vid_name = vid_path.split('/')[-1].split('.')[0] # vid_name 视频名称
out_full_path = os.path.join(out_path, vid_name)
try:
os.mkdir(out_full_path)
except OSError:
pass
current = current_process()
dev_id = (int(current._identity[0]) - 1) % NUM_GPU
image_path = '{}/img'.format(out_full_path) # 提取帧
flow_x_path = '{}/flow_x'.format(out_full_path) # 提取光流X
flow_y_path = '{}/flow_y'.format(out_full_path) # 提取光流Y
# df_path: path to the dense_flow toolbox, 默认为'./lib/dense_flow/' # out_format: default='dir', choices=['dir','zip']
# new_size: 新采样帧的大小
# 所以命令 cmd 表示为:
"""
./lib/dense_flow/build/extract_gpu -f vid_path
-x flow_x_path
-y flow_y_path
-i image_path
-b 20
-t 1
-d dev_id
-s 1
-o out_format
-w new_size[0]
-h new_size[1]
"""
cmd = os.path.join(df_path + 'build/extract_gpu') +
' -f {} -x {} -y {} -i {} -b 20 -t 1 -d {} -s 1 -o {} -w {} -h {}'.format(
quote(vid_path),
quote(flow_x_path),
quote(flow_y_path),
quote(image_path),
dev_id,
out_format,
new_size[0], new_size[1])
os.system(cmd)
print('{} {} done'.format(vid_id, vid_name))
sys.stdout.flush()
return True
由于没有找到./lib/dense_flow/build/extract_gpu 这个函数(没有build本工程),于是查了下
lib/dense_flow/tools/extract_flow_gpu.cpp 文件(应该就是它了,argv参数都能对应上):
#include "dense_flow.h"
#include "utils.h"
INITIALIZE_EASYLOGGINGPP
using namespace cv::gpu;
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 y component }"
"{ i | imgFile | flow_i | filename of flow image}"
"{ b | bound | 15 | specify the maximum of optical flow}"
"{ t | type | 0 | specify the optical flow algorithm }"
"{ d | device_id | 0 | set gpu id}"
"{ s | step | 1 | specify the step for frame sampling}"
"{ o | out | zip | output style}"
"{ w | newWidth | 0 | output style}"
"{ h | newHeight | 0 | output style}"
};
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");
string output_style = cmd.get<string>("out");
int bound = cmd.get<int>("bound");
int type = cmd.get<int>("type");
int device_id = cmd.get<int>("device_id");
int step = cmd.get<int>("step");
int new_height = cmd.get<int>("newHeight");
int new_width = cmd.get<int>("newWidth");
vector<vector<uchar> > out_vec_x, out_vec_y, out_vec_img;
calcDenseFlowGPU(vidFile, bound, type, step, device_id,
out_vec_x, out_vec_y, out_vec_img, new_width, new_height);
if (output_style == "dir") {
writeImages(out_vec_x, xFlowFile);
writeImages(out_vec_y, yFlowFile);
writeImages(out_vec_img, imgFile);
}else{
// LOG(INFO)<<"Writing results to Zip archives";
writeZipFile(out_vec_x, "x_%05d.jpg", xFlowFile+".zip");
writeZipFile(out_vec_y, "y_%05d.jpg", yFlowFile+".zip");
writeZipFile(out_vec_img, "img_%05d.jpg", imgFile+".zip");
}
return 0;
}
在该cpp文件中调用了 calcDenseFlowGPU 函数提取光流,于是找到该函数的定义在
/lib/dense_flow/include/dense_flow.h
void calcDenseFlowGPU(string file_name, int bound, int type, int step, int dev_id,
vector<vector<uchar> >& output_x,
vector<vector<uchar> >& output_y,
vector<vector<uchar> >& output_img,
int new_width=0, int new_height=0);
/lib/dense_flow/src/dense_flow_gpu.cpp
Background: opencv对cpp的扩展
1. cv::Mat
Mat中像素数据的存储形式:一般是二维向量,如果是灰度图,一般存放<uchar>类型;如果是RGB彩色图,存放<Vec3b>类型。单通道灰度图数据存放格式:
2. void cv::resize()
void cv::resize(InputArray src, OutputArray dst, Size dsize)
3. void cv::cvtColor()
void cvtColor(InputArray src, OutputArray dst, int code, int dstCn=0 );
- InputArray src: 输入图像即要进行颜色空间变换的原图像,可以是Mat类
- OutputArray dst: 输出图像即进行颜色空间变换后存储图像,也可以Mat类
- int code: 转换的代码或标识,即在此确定将什么制式的图片转换成什么制式的图片
- int dstCn = 0: 目标图像通道数,如果取值为0,则由src和code决定
4. alg_farn/alg_tvl1/alg_brox
分别代表各种提取光流的方法,为了代码可读将各算法实例化为 alg_farn/alg_tvl1/alg_brox 三个函数。
FarnebackOpticalFlow alg_farn;
OpticalFlowDual_TVL1_GPU alg_tvl1;
BroxOpticalFlow alg_brox(0.197f, 50.0f, 0.8f, 10, 77, 10);
以TVL1算法举例:
void cv::gpu::OpticalFlowDual_TVL1_GPU::operator() (
const GpuMat & I0,
const GpuMat & I1,
GpuMat & flowx,
GpuMat & flowy
)
正文: opencv2 提取光流
#include "dense_flow.h"
#include "opencv2/gpu/gpu.hpp"
using namespace cv::gpu;
void calcDenseFlowGPU(string file_name, int bound, int type, int step, int dev_id,
vector<vector<uchar> >& output_x,
vector<vector<uchar> >& output_y,
vector<vector<uchar> >& output_img,
int new_width, int new_height){
// cv::VideoCapture capture(const string& filename);
// 读取视频文件为 VideoCapture 对象
VideoCapture video_stream(file_name);
CHECK(video_stream.isOpened())<<"Cannot open video stream \""
<<file_name
<<"\" for optical flow extraction.";
setDevice(dev_id);
Mat capture_frame, capture_image, prev_image, capture_gray, prev_gray;
Mat flow_x, flow_y;
Size new_size(new_width, new_height);
GpuMat d_frame_0, d_frame_1;
GpuMat d_flow_x, d_flow_y;
FarnebackOpticalFlow alg_farn;
OpticalFlowDual_TVL1_GPU alg_tvl1;
BroxOpticalFlow alg_brox(0.197f, 50.0f, 0.8f, 10, 77, 10);
bool do_resize = (new_height > 0) && (new_width > 0);
bool initialized = false;
int cnt = 0;
while(true){
if (!initialized){ // 读取第一帧
video_stream >> capture_frame; // 从视频中读取一帧记为 capture_frame
if (capture_frame.empty()) return; // read frames until end
if (!do_resize){
initializeMats(capture_frame, capture_image, capture_gray,
prev_image, prev_gray);
capture_frame.copyTo(prev_image);
}else{ // 需要对帧进行resize,在TSN中do_resize==true
// 读取第一帧时,需要创建cv::Mat对象,包括capture_image等
// RGB用CV_8UC3; gray用CV_8UC1
capture_image.create(new_size, CV_8UC3);
capture_gray.create(new_size, CV_8UC1);
prev_image.create(new_size, CV_8UC3);
prev_gray.create(new_size, CV_8UC1);
cv::resize(capture_frame, prev_image, new_size);
}
cvtColor(prev_image, prev_gray, CV_BGR2GRAY);
initialized = true;
for(int s = 0; s < step; ++s){
video_stream >> capture_frame;
cnt ++;
if (capture_frame.empty()) return; // read frames until end
}
}
else { // 读取剩下的帧
if (!do_resize)
capture_frame.copyTo(capture_image);
else
cv::resize(capture_frame, capture_image, new_size);
cvtColor(capture_image, capture_gray, CV_BGR2GRAY);
d_frame_0.upload(prev_gray); // 上个采样帧的灰度图
d_frame_1.upload(capture_gray); // 本次采样帧的灰度图
switch(type){ // 对应了3种求光流图的方法
case 0: {
alg_farn(d_frame_0, d_frame_1, d_flow_x, d_flow_y);
break;
}
case 1: {
alg_tvl1(d_frame_0, d_frame_1, d_flow_x, d_flow_y);
break;
}
case 2: {
GpuMat d_buf_0, d_buf_1;
d_frame_0.convertTo(d_buf_0, CV_32F, 1.0 / 255.0);
d_frame_1.convertTo(d_buf_1, CV_32F, 1.0 / 255.0);
alg_brox(d_buf_0, d_buf_1, d_flow_x, d_flow_y);
break;
}
default:
LOG(ERROR)<<"Unknown optical method: "<<type;
}
//prefetch while gpu is working
bool hasnext = true;
for(int s = 0; s < step; ++s){
video_stream >> capture_frame;
cnt ++;
hasnext = !capture_frame.empty();
// read frames until end
}
//get back flow map
d_flow_x.download(flow_x);
d_flow_y.download(flow_y);
vector<uchar> str_x, str_y, str_img;
// where is encodeFlowMap() --> ./lib/dense_flow/src/common.cpp
encodeFlowMap(flow_x, flow_y, str_x, str_y, bound);
imencode(".jpg", capture_image, str_img);
// 由于 output_x/output_y/output_img 的类型都是 vector<vector<uchar> >&
// 所以直接 push_back 是可以的
output_x.push_back(str_x);
output_y.push_back(str_y);
output_img.push_back(str_img);
std::swap(prev_gray, capture_gray);
std::swap(prev_image, capture_image);
if (!hasnext){
return;
}
}
}
}
目前 dense_flow 模块编译错误:
(pytorch_py37) ldy@sjtujiangli-PowerEdge-T640:~/TSN/temporal-segment-networks/lib/dense_flow/build$ cmake .. && make -j – The C compiler identification is GNU 7.5.0 – The CXX compiler identification is GNU 7.5.0 – Check for working C compiler: /usr/bin/cc – Check for working C compiler: /usr/bin/cc – works – Detecting C compiler ABI info – Detecting C compiler ABI info - done – Detecting C compile features – Detecting C compile features - done – Check for working CXX compiler: /usr/bin/c++ – Check for working CXX compiler: /usr/bin/c++ – works – Detecting CXX compiler ABI info – Detecting CXX compiler ABI info - done – Detecting CXX compile features – Detecting CXX compile features - done – Performing Test COMPILER_SUPPORTS_CXX11 – Performing Test COMPILER_SUPPORTS_CXX11 - Success – Performing Test COMPILER_SUPPORTS_CXX0X – Performing Test COMPILER_SUPPORTS_CXX0X - Success CMake Error at CMakeLists.txt:18 (find_package): By not providing “FindOpenCV.cmake” in CMAKE_MODULE_PATH this project has asked CMake to find a package configuration file provided by “OpenCV”, but CMake did not find one.
Could not find a package configuration file provided by “OpenCV” with any of the following names:
OpenCVConfig.cmake opencv-config.cmake
Add the installation prefix of “OpenCV” to CMAKE_PREFIX_PATH or set “OpenCV_DIR” to a directory containing one of the above files. If “OpenCV” provides a separate development package or SDK, be sure it has been installed.
– Configuring incomplete, errors occurred! See also “/home/ldy/TSN/temporal-segment-networks/lib/dense_flow/build/CMakeFiles/CMakeOutput.log”.
无法解决,LibZip 需要 root 才能安装
CMakeLists.txt 的 Line 18, 19 长这样:
find_package( OpenCV REQUIRED ) // Line 18
find_package( LibZip REQUIRED ) // Line 19
文档信息
- 本文作者:Mengqi Cao
- 本文链接:https://rogercmq.github.io//2020/08/03/%E8%AE%BA%E6%96%87%E5%A4%8D%E7%8E%B0-TSN%E5%AE%9E%E7%8E%B0(1)/
- 版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证)