安装C3D v1.0提取视频特征

文章目录

  • 前言
  • 理论部分
    • 贡献
    • 动机
  • 结构
  • 安装CUDA&CUDNN
  • 安装依赖
  • 安装Opencv
  • 默认Python2
  • 安装boost
  • 安装C3D
  • C3D测试
  • 保存环境
  • 使用C3D提特征
  • (提特征模型结构)

前言

使用模型前不需要缩放图片,程序内部会自动进行操作
特征提取使用Sports-1M上训练的C3D v1.0模型,caffe框架

主要参考教程

官网

官方User Guide

官方github

个人caffe安装教程

层 特征长度:fc6-1 4096 fc7-1 4096 fc8-1 487 prob 487

可能用到的文件或压缩包:

链接:https://pan.baidu.com/s/1vIRxSJHRoBnrnsjczISOPg?pwd=7asr
提取码:7asr
复制这段内容后打开百度网盘手机App,操作更方便哦

下面所有命令均在root权限下执行,若提示权限不足,则在前面加sudo

主要参考教程若网址失效,可以在网盘里看到“主要参考教程网页备份”

本文所介绍的是C3D v1.0,若想使用C3D v1.1,则其和caffe安装的过程一模一样,跟着“个人caffe安装教程”走一遍,然后再安装C3D v1.1即可

理论部分

原论文

C3D: Convolutional 3D

贡献

实验表明,三维卷积深度网络是很好的特征学习机,可以同时建模外观和运动。
我们根据经验发现,所有层的3 × 3 × 3卷积核在有限的探索架构中工作得最好。
在4个不同的任务和6个不同的基准上,用简单的线性模型提出的特征优于或接近目前最好的方法。它们也很紧凑,计算效率很高。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pB6rj3ig-1663554505307)(C:/Users/shang/AppData/Roaming/Typora/typora-user-images/image-20220919093158716.png)]
Sports-1M:目前最大的视频分类基准数据集,包含1.1million运动视频,487个运动类别。
UCF101: 一个现实动作视频的动作识别数据集,收集自YouTube,提供了来自101个动作类别的13320个视频。

动机

安装C3D v1.0提取视频特征_第1张图片

2D卷积存在的问题:输入一个图片 -> 输出一个图片;输入多个图片(看做不同的channel),输出一个图片。仅一次conv就丢失了全部的时间信息。

结构

安装C3D v1.0提取视频特征_第2张图片

clip shape[c,l,h,w]
c: channels  l:frames  h:height  w:width

conv and pool kernel: shape[d, k, k]

图片的预处理:
	训练:resize(128, 171)  random 抽取16帧 crop(112, 112)实现抖动  50%水平翻转

	测试:resize(128, 171)  均匀采样16 中心裁剪(112, 112) 无翻转

shape[3, 16, 112, 112],l为16,经过42->1的池化操作后,时间信息就会完全丢失,所以pool1没有进行在时间上的池化,pool2到pool5共4次在时间上的池化

安装CUDA&CUDNN

安装CUDA 10.2及对应的CUDNN(参考“主要参考教程”),为了简化操作作者使用了“矩池云平台”:

安装C3D v1.0提取视频特征_第3张图片

已经配置好了CUDA和cuDNN,就不用自己配置了。开一个1元/时的环境就够用

安装依赖

apt-get update
apt-get install -y libprotobuf-dev libleveldb-dev libsnappy-dev libopencv-dev libhdf5-serial-dev protobuf-compiler
apt-get install --no-install-recommends libboost-all-dev
apt-get install -y libopenblas-dev liblapack-dev libatlas-base-dev libgflags-dev libgoogle-glog-dev liblmdb-dev git cmake build-essential
# 打开环境配置文件
vim ~/.bashrc

# 在打开的文件末尾添加下面两行
export LD_LIBRARY_PATH=/usr/lib/x86_64-linux-gnu:$LD_LIBRARY_PATH
export LD_LIBRARY_PATH=/lib/x86_64-linux-gnu:$LD_LIBRARY_PATH

# 使环境配置文件生效
source ~/.bashrc

安装Opencv

  1. 将opencv-3.4.10.zip复制到/root目录下

  2. unzip opencv-3.4.10.zip

  3. 删除opencv-3.4.10.zip(为了节省空间)

  4. 执行下面的命令

# 进入文件夹
cd opencv-3.4.10
# 创建build文件夹
mkdir build
# 进入build文件夹
cd build
# 编译
cmake -D CMAKE_BUILD_TYPE=Release -D CMAKE_INSTALL_PREFIX=/usr/local ..
make
# 安装
make install
# 查看是否安装成功 成功将输出:3.4.10
pkg-config --modversion opencv 

默认Python2

alias python=python2
## 输出python版本为2.?则为成功
python -V

安装boost

  1. 将boost_1_72_0.7z复制到/root目录下

  2. apt-get install p7zip-full

  3. 7z x boost_1_72_0.7z

  4. 删除boost_1_72_0.7z(为了节省空间)

  5. 执行下面的命令

# 进入文件夹
cd boost_1_72_0.7z

./bootstrap.sh --with-libraries=all 
./b2
./b2 install

安装C3D

  1. 将C3D-master.zip复制到/root目录下

  2. unzip C3D-master.zip

  3. 删除C3D-master.zip(为了节省空间)

  4. 执行下面的命令

# 进入文件夹
cd C3D-master/C3D-v1.0/python
# 安装依赖包
for req in $(cat requirements.txt); do pip install $req; done
# 返回上一级目录
cd ..
# 复制文件
cp Makefile.config.example Makefile.config
  1. 修改文件

    1) 修改Makefile.config

CUDA_ARCH := -gencode arch=compute_20,code=sm_20 \
-gencode arch=compute_20,code=sm_21 \
-gencode arch=compute_30,code=sm_30 \
-gencode arch=compute_35,code=sm_35 \
-gencode=arch=compute_50,code=sm_50 \
#-gencode=arch=compute_50,code=compute_50	
修改为
CUDA_ARCH := #-gencode arch=compute_20,code=sm_20 \
#-gencode arch=compute_20,code=sm_21 \
#-gencode arch=compute_30,code=sm_30 \
#-gencode arch=compute_35,code=sm_35 \
-gencode=arch=compute_50,code=sm_50 \
-gencode=arch=compute_50,code=compute_50

# OPENCV_VERSION := 3
修改为
OPENCV_VERSION := 3

INCLUDE_DIRS := $(PYTHON_INCLUDE) /usr/local/include
LIBRARY_DIRS := $(PYTHON_LIB) /usr/local/lib /usr/lib 
修改为
INCLUDE_DIRS := $(PYTHON_INCLUDE) /usr/local/include /usr/include/hdf5/serial
LIBRARY_DIRS := $(PYTHON_LIB) /usr/local/lib /usr/lib /usr/lib/x86_64-linux-gnu /usr/lib/x86_64-linux-gnu/hdf5/serial

2) 修改Makefile

NVCCFLAGS +=-ccbin=$(CXX) -Xcompiler-fPIC $(COMMON_FLAGS)
修改为
NVCCFLAGS += -D_FORCE_INLINES -ccbin=$(CXX) -Xcompiler -fPIC $(COMMON_FLAGS)

LIBRARIES := cudart cublas curand \
pthread \
glog protobuf leveldb snappy \
boost_system \
hdf5_hl hdf5 \
opencv_core opencv_highgui opencv_imgproc
修改为
LIBRARIES := cudart cublas curand \
pthread \
glog protobuf leveldb snappy \
boost_system \
hdf5_serial_hl hdf5_serial \
opencv_core opencv_highgui opencv_imgproc

3) 修改/src/caffe/test/test_gradient_check_util.hpp

Dtype scale = std::max(
std::max(fabs(computed_gradient), fabs(estimated_gradient)), 1.);
修改为
Dtype scale = std::max(
std::max(fabs(computed_gradient), fabs(estimated_gradient)),
Dtype(1.));
  1. 编译
make all
# 可能有错误:recipe for target 'build/examples/mnist/convert_mnist_data.o' failed
# 不用管

make test

make runtest
# 可能有错误,不用管
# Makefile:285: recipe for target 'runtest' failed
# make: *** [runtest] Error 127

C3D测试

  1. 将文件conv3d_deepnetA_sport1m_iter_1900000复制到C3D-master/C3D-v1.0/examples/c3d_feature_extraction/

  2. sh c3d_sport1m_feature_extraction_frm.sh

    出现错误:…/…/build/tools/extract_image_features.bin: error while loading shared libraries: libopencv_core.so.3.4: cannot open shared object file: No such file or directory

    解决:在/etc/ld.so.conf.d/下创建OpenCV.conf,内容为/usr/local/lib,然后执行命令sudo ldconfig

  3. sh c3d_sport1m_feature_extraction_frm.sh

    执行成功,E0707 14:27:41.620281 36381 extract_image_features.cpp:121] Successfully extracted 16 features!

保存环境

安装C3D v1.0提取视频特征_第4张图片

使用C3D提特征

将百度网盘中malloclu_c3d_feature_extraction复制到C3D-master目录下,使用下面的程序提取特征,输入为视频(.mp4,.avi),输出为特征向量字典(.pkl)

"""
功能:
    使用c3d v1.0提取视频特征,执行完成后得到output//c3d____.pkl,
        内容为一个字典,key为(视频名),value为list内含多个特征向量
        其中第i个特征向量(从0开始)所表示视频中的帧为 [startFrame + i * step, startFrame + i * step + stride * 16 - 1]
    例如:python main.py --videoDir testVideo


参数:
    videoDir: 多个视频所在文件夹
    frameSize=None: 是否缩放帧图片,例如(320, 240)
    startFrame=1: 开始帧(标号从1开始)
    stride=1: 采样间隔(后采样帧-前采样帧=stride)(stride * 16即为获得的特征向量所表示的 视频片段长度)
    step=16: 步幅,stride的整数倍,本次的startFrm与下次的startFrm之间的间隔帧数
    keepCookie=False: 是否保存中间文件
    layer='fc7-1': 获取c3d特征向量的层数,可选fc6-1 fc7-1 fc8-1 prob

     if not keepCookie:
        删除产生的除 c3dfreature-.pkl 之外所有的中间文件


程序执行流程:
    截取视频帧保存到 output//frame//
    写prototxt/input_list_frm.txt
    写prototxt/output_list_prefix.txt
    执行c3d,每个视频特征暂存到 output//feature//
    所有特征保存到 output//c3d____.pkl

"""

import argparse
import datetime
import os
import cv2
import subprocess
import math
import numpy as np
import pickle
import shutil


def createDirs(dirs):
    """
    不存在时,创建多级目录
    存在时,无操作
    :param dirs: 多级目录
    :return: None
    """
    if not os.path.exists(dirs):
        os.makedirs(dirs)


def saveFrame(videoPath, frameSize, startFrame, stride, saveDir):
    """
    保存视频帧图片,保存名为 递增的f'{:06d}.jpg'
    从startFrame开始每隔stride保存一张,图片可选缩放

    :param videoPath: 视频路径
    :param frameSize: 是否缩放帧图片,例如(320, 240)
    :param startFrame: 开始帧(标号从1开始)
    :param stride: 采样间隔(后采样帧-前采样帧=stride)(stride * 16即为获得的特征向量所表示的 视频片段长度)
    :param saveDir: 保存文件夹
    :return: 保存图片数/-1
    """
    vidcap = cv2.VideoCapture(videoPath)
    len = int(vidcap.get(cv2.CAP_PROP_FRAME_COUNT))

    if not os.path.exists(saveDir):
        os.makedirs(saveDir)
    if os.listdir(saveDir).__len__() > 0:
        print(f'【x 保存文件夹{saveDir}不为空】')
        return -1

    cnt = 1
    for i in range(len):
        ret, frame = vidcap.read()
        if not ret:
            print("读取帧时发生错误")
            return -1

        if i + 1 >= startFrame and (i + 1 - startFrame) % stride == 0:
            if frameSize:
                frame = cv2.resize(frame, frameSize)
            cv2.imwrite(os.path.join(saveDir, f'{cnt:06d}.jpg'), frame)
            cnt = cnt + 1

    return cnt - 1


def read_binary_blob(fn):
    """
    读取c3d提取的特征文件->numpy array float32
    :param fn: 文件名
    :return:
    """
    s = np.fromfile(fn, dtype=np.dtype('int32'), count=5)

    # s contains size of the blob e.g. num x chanel x length x height x width
    m = s[0] * s[1] * s[2] * s[3] * s[4]

    # data is the blob binary data in single precision (e.g float in C++)
    data = np.fromfile(fn, dtype=np.dtype('float32'), count=m, offset=20)

    return s, data


if __name__ == '__main__':
    parser = argparse.ArgumentParser()

    parser.add_argument('--videoDir', type=str, required=True)
    parser.add_argument('--frameSize', type=tuple, default=None)
    parser.add_argument('--startFrame', type=int, default=1)
    parser.add_argument('--stride', type=int, default=1)
    parser.add_argument('--step', type=int, default=16)
    parser.add_argument('--keepCookie', type=bool, default=False)
    parser.add_argument('--layer', type=str, default='fc7-1', choices=['fc6-1', 'fc7-1', 'fc8-1', 'prob'])

    args = parser.parse_args()
    print(args)
    print('--------------------------------------------------------------')
    if not args.step % args.stride == 0:
        print('【x 参数step需为参数stride的整数倍】')
        exit(1)

    curTime = datetime.datetime.strftime(datetime.datetime.now(), '%Y%m%d%H%M%S')

    # 截取视频帧
    cntDict = {}
    videoList = os.listdir(args.videoDir)
    for item in videoList:
        if item.endswith('.avi') or item.endswith('.mp4'):
            createDirs(os.path.join('output', curTime, 'frame', item))
            createDirs(os.path.join('output', curTime, 'feature', item))
            tcnt = saveFrame(os.path.join(args.videoDir, item), None,
                      args.startFrame, args.stride, os.path.join('output', curTime, 'frame', item))
            cntDict[item] = tcnt

    createDirs('prototxt')
    # 写prototxt//input_list_frm.txt
    # 写prototxt//output_list_prefix.txt
    sStep = args.step // args.stride
    wlinput = []
    wloutput = []
    for item in videoList:
        if item.endswith('.avi') or item.endswith('.mp4'):
            sStart = 1
            while sStart + 15 <= cntDict[item]:
                wlinput.append(f'output/{curTime}/frame/{item}/ {sStart} 0\n')
                wloutput.append(f'output/{curTime}/feature/{item}/{sStart:06d}\n')
                sStart = sStart + sStep
    with open('prototxt/input_list_frm.txt', 'w') as f:
        f.writelines(wlinput)
    with open('prototxt/output_list_prefix.txt', 'w') as f:
        f.writelines(wloutput)

    # 执行c3d
    status = subprocess.getstatusoutput('GLOG_logtosterr=1 ../C3D-v1.0/build/tools/extract_image_features.bin '
                                     'c3d_sport1m_feature_extractor_frm.prototxt '
                                    'conv3d_deepnetA_sport1m_iter_1900000 0 '
                                     f'50 {math.ceil(len(wlinput) / 50)} '
                                     f'prototxt/output_list_prefix.txt {args.layer}')[0]
    if status != 0:
        print('【x c3d运行失败】')
        exit(1)

    # 保存特征
    result = {}
    for item in wloutput:
        item = item.strip('\n')
        _, data = read_binary_blob(item + f'.{args.layer}')
        name = item.split('/')[-2]
        if name not in result.keys():
            result[name] = []
        result[name].append(data)

    saveName = f'c3d_{args.startFrame}_{args.stride}_{args.step}_{args.layer}.pkl'
    saveName = os.path.join('output', curTime, saveName)

    with open(saveName, 'wb') as f:
        pickle.dump(result, f)

    # 删除缓存
    if not args.keepCookie:
        if os.path.exists(f'output/{curTime}/frame'):
            shutil.rmtree(f'output/{curTime}/frame')
        if os.path.exists(f'output/{curTime}/feature'):
            shutil.rmtree(f'output/{curTime}/feature')
        if os.path.exists('prototxt'):
            shutil.rmtree('prototxt')

    print(f'【v 运行成功 保存名{saveName}】')
    exit(0)

(提特征模型结构)

name: "DeepConv3DNet_Sport1M_Val"
layers {
  name: "data"
  type: VIDEO_DATA
  top: "data"
  top: "label"
  image_data_param {
    source: "prototxt/input_list_frm.txt"
    use_image: true
    mean_file: "sport1m_train16_128_mean.binaryproto"
    batch_size: 50
    crop_size: 112
    mirror: false
    show_data: 0
    new_height: 128
    new_width: 171
    new_length: 16
    shuffle: false
  }
}
# ----------- 1st layer group -----------
...
# ----------- 2nd layer group -----------
...
# ----------- 3rd layer group -----------
...
# ----------- 4th layer group -----------
...
# ----------- 5th layer group -----------
...
# ----------- fc layers -----------------
layers {
  name: "fc6-1"
  num_output: 4096
}
layers {
  name: "relu6"
}
layers {
  name: "drop6"
}
layers {
  name: "fc7-1"
  num_output: 4096
}
layers {
  name: "relu7"
}
layers {
  name: "drop7"
}
layers {
  name: "fc8-1"
  num_output: 487
}
layers {
  name: "prob"
  type: SOFTMAX
}
layers {
  name: "accuracy"
  type: ACCURACY
}

你可能感兴趣的:(特征提取和预训练,c语言,caffe,开发语言)