【问题标题】:ffmpeg AVFrame to opencv Mat conversionffmpeg AVFrame 到 opencv Mat 转换
【发布时间】:2015-05-29 13:29:27
【问题描述】:

我目前正在开发一个使用 ffmepg 解码接收到的帧的项目,解码后,我想将 AVFrame 转换为 opencv Mat 帧,以便我可以在 imShow 函数上播放它。

我所拥有的是字节流,我将其读入缓冲区,解码为 AVFrame:

f = fopen(filename, "rb");
if (!f) {
    fprintf(stderr, "Could not open %s\n", filename);
    exit(1);
}

frame = avcodec_alloc_frame();
if (!frame) {
    fprintf(stderr, "Could not allocate video frame\n");
    exit(1);
}

framergb = avcodec_alloc_frame();
if (!framergb) {
    fprintf(stderr, "Could not allocate video frame\n");
    exit(1);
}

bytes=avpicture_get_size(PIX_FMT_RGB24, CAMER_WIDTH, CAMER_HEIGHT);
buffer=(uint8_t *)av_malloc(bytes*sizeof(uint8_t));
avpicture_fill((AVPicture *)framergb, buffer, PIX_FMT_RGB24,
                CAMER_WIDTH, CAMER_HEIGHT);

frame_count = 0;
for(;;) {
    avpkt.size = fread(inbuf, 1, INBUF_SIZE, f);
    if (avpkt.size == 0)
        break;

    avpkt.data = inbuf;
    while (avpkt.size > 0)
        if (decode_write_frame(outfilename, c, frame, &frame_count, &avpkt, 0) < 0)
            exit(1);
}

avpkt.data = NULL;
avpkt.size = 0;
decode_write_frame(outfilename, c, frame, &frame_count, &avpkt, 1);

decode_write_frame 定义如下:

static int decode_write_frame(const char *outfilename, AVCodecContext *avctx,AVFrame *frame, int *frame_count, AVPacket *pkt, int last)
{
int len, got_frame;
char buf[1024];
struct SwsContext *convert_ctx;

len = avcodec_decode_video2(avctx, frame, &got_frame, pkt);
if (len < 0) {
    fprintf(stderr, "Error while decoding frame %d\n", *frame_count);
    return len;
}
if (got_frame) {
    printf("Saving %sframe %3d\n", last ? "last " : "", *frame_count);
    fflush(stdout);

int w = avctx->width;
int h = avctx->height;
convert_ctx = sws_getContext(w, h, avctx->pix_fmt,
                    w, h, PIX_FMT_RGB24, SWS_BICUBIC,
                    NULL, NULL, NULL);

if(convert_ctx == NULL) {
    fprintf(stderr, "Cannot initialize the conversion context!\n");
    exit(1);
}

sws_scale(convert_ctx, frame->data,
            frame->linesize, 0,
            h,
            framergb->data, framergb->linesize);

    /* the picture is allocated by the decoder, no need to free it */
    snprintf(buf, sizeof(buf), outfilename, *frame_count);

    bmp_save(framergb->data[0], framergb->linesize[0],
             avctx->width, avctx->height, buf);
    (*frame_count)++;
}
if (pkt->data) {
    pkt->size -= len;
    pkt->data += len;
}
return 0;
}

这里的bmp_save()是原代码作者定义的,用于实现AVFrame到bmp图片的转换。我想在这里进行修改,以便让 AVFrame 转换为 opencv Mat 帧。我应该如何进行这种转换?

提前致谢。

【问题讨论】:

    标签: c++ opencv ffmpeg video-streaming


    【解决方案1】:

    使用适当的Mat constructor,将 bmp_save 行替换为:

    Mat mat(avctx->height, avctx->width, CV_8UC3, framergb->data[0], framergb->linesize[0]);
    imshow("frame", mat);
    waitKey(10);
    

    同样将 sws_getContext 中的 PIX_FMT_RGB24 标志替换为 PIX_FMT_BGR24,因为 OpenCV 内部使用 BGR 格式。

    【讨论】:

    • 仍然,framergb-&gt;data[0] 只是第一个元素,而不是指针。 -第二次尝试,请;)
    • 类型参数的好点,谢谢,我太快了 ;-) 对于数据,我想我是对的,avframe 中的数据字段是指针数组,而不是指针。不是 100% 确定这是正确的,因为我没有测试过代码,但这是我最好的猜测。
    • 哦,抱歉,没看到,该数组中到底有什么 ;)
    • hasanaga.info/tag/ffmpeg-avframe-to-opencv-mat 此代码为更通用的情况提供了一个很好的示例。
    【解决方案2】:

    谢谢你的回答,我也是这样解决的:

    说 AVFrame *frame 是准备转换的原始 ffmepg 帧,

    Mat m;
    AVFrame dst;
    int w = frame->width;
    int h = frame->height;
    m = cv::Mat(h, w, CV_8UC3);
    dst.data[0] = (uint8_t *)m.data;
    avpicture_fill( (AVPicture *)&dst, dst.data[0], PIX_FMT_BGR24, w, h);
    
    enum PixelFormat src_pixfmt = (enum PixelFormat)frame->format;
    enum PixelFormat dst_pixfmt = PIX_FMT_BGR24;
    convert_ctx = sws_getContext(w, h, src_pixfmt, w, h, dst_pixfmt,
                        SWS_FAST_BILINEAR, NULL, NULL, NULL);
    
    if(convert_ctx == NULL) {
        fprintf(stderr, "Cannot initialize the conversion context!\n");
        exit(1);
    }
    
    sws_scale(convert_ctx, frame->data, frame->linesize, 0, h,
                        dst.data, dst.linesize);
    imshow("MyVideo", m);
    waitKey(30);
    

    效果很好!

    【讨论】:

    • 请注意,如果 Mat 构造函数选择的 linesize 与 avpicture_fill 选择的 linesize 不同,这可能会失败。我不知道如何在 avpicture_fill 中选择该值,因此我无法告诉您这是否可能发生,但这是有可能的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-02-05
    • 2015-11-06
    • 2014-10-21
    • 2017-05-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多