ffmpeg获取运动矢量及显示

最近有个项目需要研究视频压缩域,主要对于运动矢量。获取运动矢量的各种算法比较复杂,发现ffmpeg可以直接获取,非常简便。

初学ffmpeg,雷霄骅大神的博客提供了非常大的帮助,http://blog.csdn.net/leixiaohua1020/article/details/15811977/

下载ffmpeg源码,在doc/example下有extract_mvs.c文件 ,一个提取运动矢量简单例程。在vs2010编译,遇到一些麻烦,参考http://blog.csdn.net/leixiaohua1020/article/details/12747899http://blog.csdn.net/leixiaohua1020/article/details/12029697

关于运动矢量的显示,或者说原始帧数据的显示,雷神用的是SDL,这里使用的是opencv.

在源码基础上改的,图像bgr数据保存在pFrameRGB->data[0],这是关键。转为IplImage然后画出运动矢量,显示播放。

//#ifdef __cplusplus
//
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
using namespace std;

extern "C"
{
#include <libavutil/motion_vector.h>
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"

};
//#endif
//#pragma comment(lib,"avutil.lib")
//#pragma comment(lib,"avcodec.lib")
//#pragma comment(lib,"avformat.lib")


#include <cv.h>
#include <highgui.h>
using namespace std;

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 AVFrame *pFrameRGB=NULL;
static AVPicture *pFrame=NULL;
static AVPacket pkt;
static int video_frame_count = 0;
static SwsContext *img_convert_ctx=NULL;
//FILE *output;
FILE *fp;
static IplImage *imgShow=NULL;
static IplImage *imgShow2=NULL;
static unsigned char* frameData=0;
typedef struct{
int mv_sx;
int mv_sy;
int mv_x;
int mv_y;
}MV_DATA;
const int K=3;

void uchar2IplImageBGR(unsigned char *inArrayCur, int img_w, int img_h,IplImage* pImg);

int img_w,img_h;
const int mbNum=131100;//1920*1080/16+1920/4*2+1080/4*2;


int num,nozero;
CvPoint p1,p2;
MV_DATA global_mv;
int j,k;
unsigned char *out_roi;
unsigned char *double_show1;
unsigned char *double_show2;
IplImage* roi_show=NULL;
IplImage* double_show =NULL;
unsigned char *gray_roi;
IplImage* ipl_gray_roi =NULL;


static int decode_packet(int *got_frame, int cached)
{
    MV_DATA* mv_data=(MV_DATA*)malloc(mbNum*sizeof(MV_DATA));//读取运动矢量保存到mv_data中

    num=0,nozero=0;
    ave_x=0,ave_y=0;//平均值
    out_roi=(unsigned char *)malloc(img_w*img_h);
    roi_show=cvCreateImage(cvSize(img_w,img_h),IPL_DEPTH_8U,1);
    double_show = cvCreateImage(cvSize(img_w*2,img_h),IPL_DEPTH_8U,3);
    double_show1=(unsigned char *)malloc(img_w*img_h*3);
    double_show2=(unsigned char *)malloc(img_w*img_h*3);
    gray_roi=(unsigned char *)malloc(img_w*img_h);
    ipl_gray_roi = cvCreateImage(cvSize(img_w,img_h),IPL_DEPTH_8U,1);

    vector<MV_DATA> mv_datas;

    for(int s=0;s<img_w*img_h;s++)
    {
        out_roi[s]=0;
    }
    int decoded = pkt.size;
    *got_frame = 0;

    if (pkt.stream_index == video_stream_idx) {
        int ret = avcodec_decode_video2(video_dec_ctx, frame, got_frame, &pkt);
        if (ret < 0) {
            //fprintf(stderr, "Error decoding video frame (%s)\n", av_err2str(ret));
            return ret;
        }

        if (*got_frame) {
            int i;
            AVFrameSideData *sd;

            video_frame_count++;
            sd = av_frame_get_side_data(frame, AV_FRAME_DATA_MOTION_VECTORS);
            if (sd) {

                img_convert_ctx = sws_getContext(video_dec_ctx->width, video_dec_ctx->height, video_dec_ctx->pix_fmt, video_dec_ctx->width, video_dec_ctx->height, AV_PIX_FMT_BGR24, SWS_BICUBIC, NULL, NULL, NULL);   
                sws_scale(img_convert_ctx, (const uint8_t* const*)frame->data, frame->linesize, 0, video_dec_ctx->height, pFrameRGB->data, pFrameRGB->linesize);
                //fwrite(pFrameRGB->data[0],(video_dec_ctx->width)*(video_dec_ctx->height)*3,1,output);

                //memcpy(frameData,pFrameRGB->data[0],video_dec_ctx->width*video_dec_ctx->height*3);
                uchar2IplImageBGR(pFrameRGB->data[0],video_dec_ctx->width,video_dec_ctx->height,imgShow);
                cvCopy(imgShow,imgShow2);
                const AVMotionVector *mvs = (const AVMotionVector *)sd->data;

                for (i = 0; i < sd->size / sizeof(*mvs); i++) {
                    const AVMotionVector *mv = &mvs[i];
                    //printf("%d,%2d,%2d,%2d,%4d,%4d,%4d,%4d,0x%"PRIx64"\n",
                           //video_frame_count, mv->source,
                           //mv->w, mv->h, mv->src_x, mv->src_y,
                           //mv->dst_x, mv->dst_y);

                    p1.x=mv->src_x;
                    p1.y=mv->src_y;
                    p2.x=mv->dst_x;
                    p2.y=mv->dst_y;

                    cvLine(imgShow,p2,p2,cvScalar( 0,0,255 ),1,8,0);

                }
                cvShowImage("1",imgShow);
                if(cvWaitKey(50)>=32)
                    cvWaitKey(0);
                sws_freeContext(img_convert_ctx);
            }
        }
    }
    free(out_roi);
    free(double_show1);
    free(double_show2);
    free(gray_roi);
    cvReleaseImage(&roi_show);
    cvReleaseImage(&double_show);
    cvReleaseImage(&ipl_gray_roi);
    free(mv_data);
    mv_datas.clear();
    return decoded;
}


static int open_codec_context(int *stream_idx,
                              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, NULL, 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 {
        *stream_idx = ret;
        st = fmt_ctx->streams[*stream_idx];

        /* find decoder for the stream */
        dec_ctx = st->codec;
        dec = avcodec_find_decoder(dec_ctx->codec_id);
        if (!dec) {
            fprintf(stderr, "Failed to find %s codec\n",
                    av_get_media_type_string(type));
            return AVERROR(EINVAL);
        }

        /* 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;
        }
    }

    return 0;
}


int main(int argc, char** argv)
{
    int ret = 0, got_frame;
    //string name;
    //getline(cin,name,'\n');
    src_filename = "1.mp4";
    cvNamedWindow("1",0);
    cvNamedWindow("2",0);

    char motionname[] = "MV_Data.txt";    
    fp = fopen(motionname, "w"); 

    av_register_all();

    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);
    }

    if (open_codec_context(&video_stream_idx, fmt_ctx, AVMEDIA_TYPE_VIDEO) >= 0) {
        video_stream = fmt_ctx->streams[video_stream_idx];
        video_dec_ctx = video_stream->codec;
    }

    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();
    pFrameRGB=av_frame_alloc();

    uint8_t *out_buffer; 
    out_buffer=new uint8_t[avpicture_get_size(AV_PIX_FMT_BGR24, video_dec_ctx->width, video_dec_ctx->height)];  
    avpicture_fill((AVPicture *)pFrameRGB, out_buffer, AV_PIX_FMT_BGR24, video_dec_ctx->width, video_dec_ctx->height); 
    imgShow = cvCreateImage(cvSize(video_dec_ctx->width,video_dec_ctx->height),IPL_DEPTH_8U,3);
    imgShow2 = cvCreateImage(cvSize(video_dec_ctx->width,video_dec_ctx->height),IPL_DEPTH_8U,3);
    frameData=(unsigned char *)malloc(video_dec_ctx->width*video_dec_ctx->height*3);
    img_w=video_dec_ctx->width;
    img_h=video_dec_ctx->height;

    if (!frame) {
        fprintf(stderr, "Could not allocate frame\n");
        ret = AVERROR(ENOMEM);
        goto end;
    }


    /* initialize packet, set data to NULL, let the demuxer fill it */
    av_init_packet(&pkt);
    pkt.data = NULL;
    pkt.size = 0;

    //output=fopen("out.rgb","wb+"); 
    /* read frames from the file */
    while (av_read_frame(fmt_ctx, &pkt) >= 0) {
        AVPacket orig_pkt = pkt;
        do {
            ret = decode_packet(&got_frame, 0);
            if (ret < 0)
                break;
            pkt.data += ret;
            pkt.size -= ret;
        } while (pkt.size > 0);
        av_packet_unref(&orig_pkt);
    }

    /* flush cached frames */
    pkt.data = NULL;
    pkt.size = 0;
    do {
        decode_packet(&got_frame, 1);
    } while (got_frame);

end:

    //fclose(output);
    fclose(fp);  
    avcodec_close(video_dec_ctx);
    avformat_close_input(&fmt_ctx);
    av_frame_free(&frame);
    av_frame_free(&pFrameRGB);
    cvReleaseImage(&imgShow);
    free(frameData);
    getchar();
    return ret < 0;

}  



 void uchar2IplImageBGR(unsigned char *inArrayCur, int img_w, int img_h,IplImage* pImg)
{
    int i,j;

    for (i = 0; i < img_h; i++)
    {
        for (j = 0; j < img_w*3; j++)
        {
            *(pImg->imageData + i*pImg->widthStep+j)=inArrayCur[(i)*img_w*3+j] ;            
        }
    }
}

后话:这是几个月前的东西,很多细节和遇到的问题都记不太清了。工作中很多知识没有总结,一段时间过后忘记了,跟没做过一样,没有任何进步,有时候想起来非常惋惜,这也是写博客的意义所在。不要找借口说工作太忙,其实就是懒!!

  • 5
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
FFmpeg是一款功能强大的多媒体处理工具,可以用于处理音视频文件。其中,运动矢量是一种用于描述视频中物体运动的概念。通过分析每一帧图像之间的像素变化,可以计算运动矢量,即物体在横向和纵向上的运动速度和方向。 要实现FFmpeg中的运动矢量可视化,需要进行以下步骤: 1. 提取视频帧:使用FFmpeg的命令行工具,可以将视频文件逐帧提取为图片。可以通过以下命令实现:ffmpeg -i input.mp4 -vf "select='eq(n,0)'" -vsync vfr output.png,其中input.mp4为输入视频文件名,output.png为输出的第一帧图片。 2. 计算运动矢量:使用FFmpeg运动估计功能,可以计算出每一帧图像之间的运动矢量。可以通过以下命令实现:ffmpeg -i input.mp4 -vf "split[a][b];[a]crop=ih:iw/2:0:0[left];[b]crop=ih:iw/2:iw/2:0[right];[left][right]blend=all_expr='A*(1-0.5)+B*0.5'" -c:v mpeg4 motion_vectors.mp4,其中input.mp4为输入视频文件名,motion_vectors.mp4为输出的运动矢量视频文件。 3. 可视化运动矢量:使用FFmpeg的滤镜功能,可以将运动矢量添加到原视频中进行可视化。可以通过以下命令实现:ffmpeg -i input.mp4 -vf "setpts=PTS-STARTPTS,drawgrid=w=32:h=32:t=2:c=green@0.5:thickness=1,drawgrid=w=8:h=8:t=2:c=red@0.5:thickness=2" -c:v mpeg4 visualized_motion_vectors.mp4,其中input.mp4为输入视频文件名,visualized_motion_vectors.mp4为输出的可视化运动矢量视频文件。 通过以上步骤,我们可以使用FFmpeg提取视频帧,计算运动矢量并将其可视化。这样可以帮助我们更好地理解视频中物体的运动特征,对于视频分析和处理等领域具有重要的应用价值。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值