2

我有彩色 jpegOpenCV::Mat类型的图像,并使用avcodec. 我得到的视频是颠倒的,黑白的,每一帧的每一行都移动了,我得到了对角线。这种输出的原因可能是什么?按照链接观看我使用 avcodec 获得的视频。我正在使用acpicture_fill函数avFramecv::Mat框架创建!

PS 每个 cv::Mat cvFrame 的宽度 = 810,高度 = 610,步长 = 2432 我注意到 avFrame(由 acpicture_fill 填充)linesize[0]=2430 我尝试手动设置avFrame->linesizep0]=2432而不是 2430,但它仍然没有帮助。

======== 代码 ========================================== =================

AVCodec *encoder = avcodec_find_encoder(AV_CODEC_ID_H264);
AVStream *outStream = avformat_new_stream(outContainer, encoder);
avcodec_get_context_defaults3(outStream->codec, encoder);

outStream->codec->pix_fmt = AV_PIX_FMT_YUV420P;
outStream->codec->width = 810;
outStream->codec->height = 610;
//...

SwsContext *swsCtx = sws_getContext(outStream->codec->width, outStream->codec->height, PIX_FMT_RGB24,
                                    outStream->codec->width, outStream->codec->height,  outStream->codec->pix_fmt, SWS_BICUBIC, NULL, NULL, NULL);

for (uint i=0; i < frameNums; i++)
{
    // get frame at location I using OpenCV
    cv::Mat cvFrame;
    myReader.getFrame(cvFrame, i); 
    cv::Size frameSize = cvFrame.size();    
    //Each cv::Mat cvFrame has  width=810, height=610, step=2432


1.  // create AVPicture from cv::Mat frame
2.  avpicture_fill((AVPicture*)avFrame, cvFrame.data, PIX_FMT_RGB24, outStream->codec->width, outStream->codec->height);
3avFrame->width = frameSize.width;
4.  avFrame->height = frameSize.height;

    // rescale to outStream format
    sws_scale(swsCtx, avFrame->data, avFrame->linesize, 0, outStream->codec->height, avFrameRescaledFrame->data, avFrameRescaledFrame ->linesize);
encoderRescaledFrame->pts=i;
avFrameRescaledFrame->width = frameSize.width;
    avFrameRescaledFrame->height = frameSize.height;

av_init_packet(&avEncodedPacket);
    avEncodedPacket.data = NULL;
    avEncodedPacket.size = 0;

    // encode rescaled frame
    if(avcodec_encode_video2(outStream->codec, &avEncodedPacket, avFrameRescaledFrame, &got_frame) < 0) exit(1);
    if(got_frame)
    {
        if (avEncodedPacket.pts != AV_NOPTS_VALUE)
            avEncodedPacket.pts =  av_rescale_q(avEncodedPacket.pts, outStream->codec->time_base, outStream->time_base);
        if (avEncodedPacket.dts != AV_NOPTS_VALUE)
            avEncodedPacket.dts = av_rescale_q(avEncodedPacket.dts, outStream->codec->time_base, outStream->time_base);

        // outContainer is "mp4"
        av_write_frame(outContainer, & avEncodedPacket);

        av_free_packet(&encodedPacket);
    }
}

更新

正如@Alex 建议的那样,我用下面的代码更改了第 1-4 行

int width = frameSize.width, height = frameSize.height; 
avpicture_alloc((AVPicture*)avFrame, AV_PIX_FMT_RGB24, outStream->codec->width, outStream->codec->height);
for (int h = 0; h < height; h++)
{
     memcpy(&(avFrame->data[0][h*avFrame->linesize[0]]), &(cvFrame.data[h*cvFrame.step]), width*3);
}

我现在得到的视频(这里)几乎是完美的。它不是颠倒的,不是黑白的,但似乎缺少一个 RGB 组件。每个棕色/红色都变成蓝色(在原始图像中应该是相反的)。可能是什么问题呢?rescaling( sws_scale) to AV_PIX_FMT_YUV420Pformat 会导致这种情况吗?

4

3 回答 3

2

简而言之,问题是:avpicture_fill()期望行之间没有填充,即步幅(步长)等于width*sizeof(pixel),即 810*3 = 2430。如您所说,cv::Mat 步长中数据的实际步幅是 2432,这是不同的,所以直接传递数据是行不通的。没有办法告诉avpicture_fill()输入数据使用不同的步幅;它不是 API 的一部分(你可能会说它应该是 :)

有两种可能的解决方案:

创建一个数组,其中输入数据是连续的,行之间没有填充。您必须将 cv::Mat 中的每一行内存复制到该数组中。然后将其传递给avpicture_fill().

int width, height; // get from mat
uint8_t* buf = malloc(width * height * 3); // 3 bytes per pixel
for (int i = 0; i < height; i++)
{
    memcpy( &( buf[ i*width*3 ] ), &( mat->data[ i*mat->step ] ), width*3 );
}
avpicture_fill(..., buf, ...)

顺便说一句,要垂直翻转视频,您可以这样做将最后一行复制到第一行,依此类推:

...
    memcpy( &( buf[ i*width*3 ] ), &( mat->data[ (height - i - 1)*mat->step ] ), width*3 );
...

或者,自己填写 AVPicture:

AVPicture* pic = malloc(sizeof(AVPicture));
avpicture_alloc(pic, PIX_FMT_BGR24, width, height);
for (int i = 0; i < height; i++)
{
    memcpy( &( pic->data[0][ i*pic->linesize[0] ] ),  &( mat->data[ i*mat->step ] ), width*3);
}

无需分配 pic->data[0] 或设置 pic->linesize[0],avpicture_alloc() 应该这样做。也不需要填写data[1]或data[2],应该为null。

编辑:删除了显示将 R、G、B 复制到单独平面的旧代码。PIX_FMT_BGR24 不是平面格式。

我对 OpenCV C++ API 不够熟悉,无法弄清楚如何获取宽度和高度(显然它不是 mat->width),但我想你知道我的意思。

PS顺便说一句,您的视频实际上并不是黑白的。只是每个连续的行偏移两个字节,因此颜色会旋转:红色变为绿色,绿色变为蓝色,等等。结果是灰度的,但如果你仔细观察,各个行都是彩色的。

于 2012-12-01T23:57:14.943 回答
0

您是否考虑过使用OpenCV 的功能为您创建视频?由于您的数据已经存储在cv::Mat.

如果你想保持你的方法,你可以简单地旋转cv::Mat.

于 2012-12-01T10:51:19.443 回答
0

关于原帖UPDATE中的颜色问题。是不是因为,

OpenCV Mat 是 (BGR) -> FFmpeg AVFrame 是 (RGB) ?

如果是这样,请尝试,

cvtColor( cvFrame , cvFrame , CV_BGR2RGB ) ; 

在第 1 行之前。

于 2013-09-09T04:00:41.667 回答