TSN(ECCV2016)论文复现(1)

2020/08/03 ActionRecognition 共 9286 字,约 27 分钟

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

文档信息

Search

    Table of Contents