gpt4 book ai didi

c++ - 如何使用 OpenCV::Mat 类型的 jpeg 图像中的 avcodec 创建视频?

转载 作者:塔克拉玛干 更新时间:2023-11-03 06:53:06 24 4
gpt4 key购买 nike

我有 OpenCV::Mat 类型的彩色 jpeg 图像,我使用 avcodec 从它们创建视频。我得到的视频是上下颠倒的,黑白的,每一帧的每一行都被移动了,我得到了对角线。这种输出的原因可能是什么?关注this链接观看我使用 avcodec 获得的视频。我正在使用 acpicture_fill 函数从 cv::Mat 帧创建 avFrame!

附言每个 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);
}

我现在得到的视频(here)几乎是完美的。它不是颠倒的,不是黑白的,但似乎缺少一个 RGB 组件。每一种棕色/红色都变成了蓝色(在原始图像中它应该是反之)。可能是什么问题呢?将 (sws_scale) 重新缩放为 AV_PIX_FMT_YUV420P 格式会导致这种情况吗?

最佳答案

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

有两种可能的解决方案:

创建一个数组,其中输入数据是连续的,行之间没有填充。您必须将 cv::Mat 中的每一行 memcopy 到该数组中。然后将其传递给 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),但我想你明白我的意思。

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

关于c++ - 如何使用 OpenCV::Mat 类型的 jpeg 图像中的 avcodec 创建视频?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13654789/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com