使用ffmpeg绘制运动向量MV

本文将使用ffmpeg提取每帧视频的运动向量MV,并使用opencv将其绘制出来。

运动向量MV

了解视频编码的人都对运动向量不陌生,它是在进行帧间预测时标记当前块和参考块位置关系的一个向量。帧间预测包括单向预测(P帧)和双向预测(B帧),单向预测只需要一个MV,双向预测需要两个MV。

MV在ffmpeg中的定义如下:

typedef struct AVMotionVector {/*** Where the current macroblock comes from; negative value when it comes* from the past, positive value when it comes from the future.* XXX: set exact relative ref frame reference instead of a +/- 1 "direction".*///表明参考块在前面帧(负)还是后面帧(正)int32_t source;/*** Width and height of the block.*///所属块的宽和高uint8_t w, h;/*** Absolute source position. Can be outside the frame area.*/int16_t src_x, src_y;/*** Absolute destination position. Can be outside the frame area.*/int16_t dst_x, dst_y;/*** Extra flag information.* Currently unused.*/uint64_t flags;/*** Motion vector* src_x = dst_x + motion_x / motion_scale* src_y = dst_y + motion_y / motion_scale*/int32_t motion_x, motion_y;uint16_t motion_scale;} AVMotionVector;

ffmpeg的示例代码中提供了mv提取的实例程序,可以在提取mv后使用opencv将其绘制在图像上。

extern "C"
{
#include <libavutil/motion_vector.h>
#include <libavformat/avformat.h>
}
#include <opencv.hpp>
using namespace cv;
​
static AVFormatContext *fmt_ctx = NULL;
static AVCodecContext *video_dec_ctx = NULL;
static AVStream *video_stream = NULL;
static const char *src_filename = NULL;
​
static int video_stream_idx = -1;
static AVFrame *frame = NULL;
static int video_frame_count = 0;
​
FILE *fout;
VideoWriter out;
​
static int decode_packet(const AVPacket *pkt)
{int ret = avcodec_send_packet(video_dec_ctx, pkt);if (ret < 0) {printf("Error while sending a packet to the decoder: %s\n");return ret;}
​while (ret >= 0)  {ret = avcodec_receive_frame(video_dec_ctx, frame);if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {break;}else if (ret < 0) {printf("Error while receiving a frame from the decoder: %s\n");return ret;}
​if (ret >= 0) {int i;AVFrameSideData *sd;
​video_frame_count++;sd = av_frame_get_side_data(frame, AV_FRAME_DATA_MOTION_VECTORS);//获取每帧数据cv::Mat yuvImg;yuvImg.create(frame->height * 3 / 2, frame->width, CV_8UC1);memcpy(yuvImg.data, frame->data[0], frame->linesize[0] * frame->height*sizeof(uint8_t));memcpy(yuvImg.data + frame->linesize[0] * frame->height*sizeof(uint8_t), frame->data[1], frame->linesize[1] * frame->height/2*sizeof(uint8_t));memcpy(yuvImg.data + (frame->linesize[0] * frame->height + frame->linesize[1] * frame->height / 2)*sizeof(uint8_t), frame->data[2], frame->linesize[2] * frame->height / 2 * sizeof(uint8_t));cv::Mat rgbImg;cv::cvtColor(yuvImg, rgbImg, CV_YUV2BGR_I420);if (sd) {const AVMotionVector *mvs = (const AVMotionVector *)sd->data;for (i = 0; i < sd->size / sizeof(*mvs); i++) {const AVMotionVector *mv = &mvs[i];//绘制mvline(rgbImg, Point(mv->src_x, mv->src_y), Point(mv->dst_x, mv->dst_y), Scalar(0, 0, 255));}}//将带mv的帧写入文件out << rgbImg;av_frame_unref(frame);}}
​return 0;
}
​
static int open_codec_context(AVFormatContext *fmt_ctx, enum AVMediaType type)
{int ret;AVStream *st;AVCodecContext *dec_ctx = NULL;AVCodec *dec = NULL;AVDictionary *opts = NULL;
​ret = av_find_best_stream(fmt_ctx, type, -1, -1, &dec, 0);if (ret < 0) {fprintf(stderr, "Could not find %s stream in input file '%s'\n",av_get_media_type_string(type), src_filename);return ret;}else {int stream_idx = ret;st = fmt_ctx->streams[stream_idx];
​dec_ctx = avcodec_alloc_context3(dec);if (!dec_ctx) {fprintf(stderr, "Failed to allocate codec\n");return AVERROR(EINVAL);}
​ret = avcodec_parameters_to_context(dec_ctx, st->codecpar);if (ret < 0) {fprintf(stderr, "Failed to copy codec parameters to codec context\n");return ret;}
​/* Init the video decoder */av_dict_set(&opts, "flags2", "+export_mvs", 0);if ((ret = avcodec_open2(dec_ctx, dec, &opts)) < 0) {fprintf(stderr, "Failed to open %s codec\n",av_get_media_type_string(type));return ret;}
​video_stream_idx = stream_idx;video_stream = fmt_ctx->streams[video_stream_idx];video_dec_ctx = dec_ctx;}
​return 0;
}
​
int main(int argc, char **argv)
{fout = fopen("out.yuv","wb");//out.open("out.avi", CV_FOURCC('X', 'V', 'I', 'D'),25, Size(640, 272));out.open("out.mp4", CV_FOURCC('D', 'I', 'V', 'X'), 25, Size(640, 272));int ret = 0;AVPacket pkt = { 0 };
​if (argc != 2) {fprintf(stderr, "Usage: %s <video>\n", argv[0]);exit(1);}src_filename = argv[1];
​if (avformat_open_input(&fmt_ctx, src_filename, NULL, NULL) < 0) {fprintf(stderr, "Could not open source file %s\n", src_filename);exit(1);}
​if (avformat_find_stream_info(fmt_ctx, NULL) < 0) {fprintf(stderr, "Could not find stream information\n");exit(1);}
​open_codec_context(fmt_ctx, AVMEDIA_TYPE_VIDEO);
​av_dump_format(fmt_ctx, 0, src_filename, 0);
​if (!video_stream) {fprintf(stderr, "Could not find video stream in the input, aborting\n");ret = 1;goto end;}
​frame = av_frame_alloc();if (!frame) {fprintf(stderr, "Could not allocate frame\n");ret = AVERROR(ENOMEM);goto end;}
​printf("framenum,source,blockw,blockh,srcx,srcy,dstx,dsty,flags\n");
​/* read frames from the file */while (av_read_frame(fmt_ctx, &pkt) >= 0) {if (pkt.stream_index == video_stream_idx)ret = decode_packet(&pkt);av_packet_unref(&pkt);if (ret < 0)break;}
​/* flush cached frames */decode_packet(NULL);
​
end:avcodec_free_context(&video_dec_ctx);avformat_close_input(&fmt_ctx);av_frame_free(&frame);fclose(fout);system("pause");return ret < 0;
}

问题

ffmpeg中提取的mv有几个问题:

  • 没有给出宏块在图像中的位置

  • 对于双向预测没有特别指出其两个mv

  • 没有指出其具体的参考图像

感兴趣的请关注微信公众号Video Coding

使用ffmpeg绘制运动向量MV相关推荐

  1. ffmpeg中获取mv/mb_type/dct_coeff/qp和MBSize等数据(H.264)

    ffmpeg是一个很复杂的库,在我看来,比JM要复杂很多,刨除其包含各种编解码方案,算法的全面性,以及其各种平台的汇编优化等因素,其运行逻辑结构和函数之间的调用关系等都要复杂很多.今天我们不泛泛而谈, ...

  2. [FFmpeg] 绘制矩形框

    最简单的是使用滤镜 # 查看滤镜帮助 ffplay -h filter=drawbox # 单个矩形 ffplay -i fpx.gif -vf drawbox:x=10:y=10:w=50:h=50 ...

  3. Python和FFmpeg将语音记录转换成可共享的视频,非常炫酷。

    在本教程中,我们将学习如何使用Python和FFmpeg这将使我们能够把录音变成很酷的视频,可以很容易地在社交媒体上分享. 在本教程的末尾,我们将把声音录制转换成类似于以下内容的视频: 教程要求 要遵 ...

  4. HEVC代码学习——帧间预测:预测MV获取(xEstimateMvPredAMVP、fillMVPCand)

    HEVC帧间预测在AMVP模式下是依靠xEstimateMvPredAMVP函数获取预测MV(MVP)的. 这部分内容的学习还可以参考这两篇博客: HEVC代码学习15:AMVP相关函数 HM编码器代 ...

  5. FFmpeg任意文件读取漏洞分析

    6月24号的时候hackerone网站上公布了一个ffmpeg的本地文件泄露的漏洞,可以影响ffmpeg很多版本,包括3.2.2.3.2.5.3.1.2.2.6.8等等. hackerone网站上的漏 ...

  6. 利用第三方解码器ffmpeg让群晖DSM6.2.4版本的Video Station支持DTS视频编码和EAC3音频编码

    前言 截至2022年5月6日,此方法可用! 本文章可以解决群晖版本6.2.4-25556 Update5(Video Station版本2.5.0-1656)在播放dts.eac3音频编码的视频时提示 ...

  7. 使用libx264静态编译库编译FFmpeg

    获取最新 FFmpeg 源码 重命名 FFmpeg 文件夹 mv ffmpeg-2.4.3 ffmpeg-gpl 进入文件夹 cd ffmpeg-gpl/ 获取最新 libx264 源码 git cl ...

  8. H.264 入门篇 - 00 (简介)

    目录 1.Profiles 2.应用领域 3.Level 4.层次结构 4.0.整个过程 4.1.数据切分 4.1.1.Macroblock (宏块) 4.2.帧内预测 (Intra-Frame Pr ...

  9. GPU视频压缩2—Multiple Layer Parallel Motion Estimation on GPU for High Efficiency Video Coding (HEVC)

    本系列记录使用GPU作为计算设备辅助传统视频压缩(H264/HEVC/VVC等)的相关论文,GPU可能用于视频压缩的某个中间环节也可能用于压缩整流程. 论文: <Multiple Layer P ...

最新文章

  1. 22.Chain of Responsibility(职责链)模式
  2. 函数重载和 函数模板
  3. 教育部:建设100+AI特色专业, 500万AI人才缺口要补上!
  4. 创建多级目录函数MakeSureDirectoryPathExists()所需头文件
  5. linux at java,Linux-Tutorial/Java-bin.md at master · linsanityHuang/Linux-Tutorial · GitHub
  6. clion IDEA 2019 Activation Code
  7. 陕西移动宽带光猫 GM219-S 路由功能分离
  8. JQuery中$(document)、$(document).ready()是什么意思?
  9. led灯光衰怎么解决_揭秘LED灯具光衰原因
  10. idea怎么进行c语言编程_idea编写c语言
  11. 软件测试人员的一般职业规划是如何的?
  12. Datawhale组队学习NLP之transformer Task 01
  13. IDEA如何配置 Gradle 及 Gradle 安装过程(详细版)
  14. python拟合非线性模型_python-绘制分段拟合到非线性数据
  15. Spring的下载及目录结构
  16. php语言加减乘除函数,php的chr和ord函数实现字符加减乘除运算实现代码_PHP教程...
  17. Cadence Allegro元件封装制作流程
  18. SAP-PP 工艺路线的作用
  19. ABBYY Screenshot Reader功能详解
  20. 日本姓名武尊的正确读音

热门文章

  1. 典型2R机械臂结构分析 2R-manipulator Geometric Modeling
  2. C++:实现量化Integration积分测试实例
  3. HEVC区域划分Slice Tile CTU CU PU TU
  4. 盘点数据分析师笔试题 你会做几道?
  5. DiskGenius安装教程
  6. requests+bs4批量爬取反爬虫图片网站
  7. AI遮天传 DL-回归与分类
  8. JSON入门学习总结
  9. 本题要求实现一个计算m~n(m<n)之间所有整数的和的简单函数
  10. 服务器自带ddos工具,详解DDoS工具 一款流行DDoS木马工具