Introduce
video slice,翻译做视频切片,在《Analysis of Spatio-temporal Slices for Video Content Representation》一文中被用来做镜头检测,我也以video slice为基础写了一个镜头检测算法,本文并不讲解镜头检测算法,单单讲解video slice 的提取和特征。
Video slice 提取方法
视频切片,顾名思义,从视频中提取的一部分内容,我的提取方法是:
1)对视频帧,取最中间的一行像素进行转置,即行像素变成列像素,在提取行像素前,会对中间三行像素用模板[1/.4, 1/2, 1/4]进行卷积;
2)将列像素按帧的时间顺序合并成一张图片,这张图片就是视频的slice图像。
slice图像高度是视频帧图像的宽度,slice图像的高度值是视频的帧数量值。由于视频的帧一般很多,10分钟的视频帧的数量一般上万,本文中所示图片均是slice图像的一部分。
Video slice特征
video slice 有两个特征:1)清楚的反映视频的镜头切换,2)反映镜头内图像的运动情况。
如图1,图2,图3,图4所示,分别包含cut,fade,dissolve,wipe镜头:
图1 图2
图3 图4
图1中竖直方向上明显的直线是表示的是cut镜头切换,图2中有好几个fade镜头,fade镜头是由3帧以上的画面形成,slice图像在fade图像处会成途中所示模糊变换,dissolve镜头是指从复杂画面向纯色画面变化,图三中红色变为黑色就是这种类型,wipe镜头则在相应的部分形成斜线,如图四中那条明显的斜线。
图5是256帧视频画面提取的slice,其中slice图像的左边对应镜头运动比较快的场景,而右边对应镜头几乎静止的场景。
Video slice 的应用
针对视频slice图像的上述特征,《Analysis of Spatio-temporal Slices for Video Content Representation》给出了四种镜头的检测方法,都有一定的道理,由于文章叙述的过于复杂,很多公式本人无法看懂,因此未能进行相关的测试。但是基于slice的镜头检测是可行的,我在工作中总结了一套十分高效的镜头检测方法。
另外,如果是需要静止视频画面检测,slice方法是非常不错的选择。
扩展
其实可以讲从视频每帧提取的像素为帧画面的全局信息,因此可以用直方图等全局特征代替这行像素来形成slice图像。下面的代码是关于slice提取的,基于opencv,参数是视频和slice图像输出目录。
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <stdio.h>
#define FIXED_SIZE 256
cv::Mat extract_line(cv::Mat &img)
{
cv::Mat line = img.row(FIXED_SIZE / 2 - 1).mul(0.25) +
img.row(FIXED_SIZE / 2).mul(0.5) +
img.row(FIXED_SIZE / 2 + 1).mul(0.25);
return line.t();
}
int main(int argc, char **argv)
{
if(argc < 3)
{
printf("Usage: %s [video] [outdir]\n");
return 1;
}
cv::VideoCapture cap(argv[1]);
if(!cap.isOpened())
{
printf("Can't open video %s\n");
return 2;
}
long totalFrameNumber = cap.get(CV_CAP_PROP_FRAME_COUNT);
cv::Mat frame;
cv::Mat slice(FIXED_SIZE, FIXED_SIZE, CV_8UC3, cv::Scalar::all(0));
int count = 0;
for(long frameNumber = 0; frameNumber < totalFrameNumber; frameNumber++)
{
cap >> frame;
cv::resize(frame, frame, cv::Size(FIXED_SIZE, frame.rows * FIXED_SIZE / frame.cols));
slice.col(count) += extract_line(frame);
count++;
if(count == FIXED_SIZE)
{
char outname[256];
sprintf(outname, "%s/%ld.jpg", argv[2], frameNumber);
if(!cv::imwrite(outname, slice))
{
printf("Can't write image %s\n", outname);
break;
}
count = 0;
slice = cv::Scalar::all(0);
}
printf("%.2f%%\r", 100.0 * frameNumber / totalFrameNumber);
fflush(stdout);
}
return 0;
}