AVFrame和cv::Mat互转

本文介绍如何在FFmpeg视频解码后使用OpenCV进行图像处理,包括将AVFrame转换为Mat,以及将Mat转换回AVFrame的方法,实现音视频应用开发中图像处理的需求。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

音视频应用开发系列文章目录

ffmpeg视频解码后得到图像经常需要一些图像处理,opencv是图像处理的神器。所以就需要两者之间互相转化,ffmpeg解码后的数据类型是AVFrame,而opencv的图像数据结构是Mat,这就需要做个转化,本文实现了avframe->mat,mat->avframe。封装成函数

cv::Mat avframe2cvmat(AVFrame *avframe, int w = 0, int h = 0);

AVFrame *cvmat2avframe(cv::Mat mat);

avframe->mat

cv::Mat avframe2cvmat(AVFrame *avframe, int w, int h) {

	if (w <= 0) w = avframe->width;
	if (h <= 0) h = avframe->height;
	struct SwsContext *sws_ctx = NULL;
	sws_ctx = sws_getContext(avframe->width, avframe->height, (enum AVPixelFormat)avframe->format,
		w, h, AV_PIX_FMT_BGR24, SWS_BICUBIC, NULL, NULL, NULL);

	cv::Mat mat;
	mat.create(cv::Size(w, h), CV_8UC3);
	AVFrame *bgr24frame = av_frame_alloc();
	bgr24frame->data[0] = (uint8_t *)mat.data;
	avpicture_fill((AVPicture *)bgr24frame, bgr24frame->data[0], AV_PIX_FMT_BGR24, w, h);
	sws_scale(sws_ctx,
		(const uint8_t* const*)avframe->data, avframe->linesize,
		0, avframe->height, // from cols=0,all rows trans
		bgr24frame->data, bgr24frame->linesize);

	av_free(bgr24frame);
	sws_freeContext(sws_ctx);
	return mat;
}

w,h可以对输入图像进行resize,默认和原图大小一致,不resize。

mat->avframe

AVFrame *cvmat2avframe(cv::Mat mat) {

	// alloc avframe
	AVFrame *avframe = av_frame_alloc();
	if (avframe && !mat.empty()) {

		avframe->format = AV_PIX_FMT_YUV420P;
		avframe->width = mat.cols;
		avframe->height = mat.rows;
		av_frame_get_buffer(avframe, 0);
		av_frame_make_writable(avframe);
		cv::Mat yuv; // convert to yuv420p first
		cv::cvtColor(mat, yuv, cv::COLOR_BGR2YUV_I420);
		// calc frame size
		int frame_size = mat.cols * mat.rows;
		unsigned char *pdata = yuv.data;
		// fill yuv420
		// yyy yyy yyy yyy
		// uuu
		// vvv
		avframe->data[0] = pdata; // fill y
		avframe->data[1] = pdata + frame_size; // fill u
		avframe->data[2] = pdata + frame_size * 5 / 4; // fill v
	}
	return avframe;
}

emmm···,注释的很清楚了!用完了别玩了释放AVFrame~~

 

ffmpeg 的 `AVFrame` 是 YUV 格式的数据结构,用于处理视频编码解码过程中的帧数据。要将其换为 OpenCV 的 `cv::Mat` 对象,你需要完成以下几个步骤: 1. 获取 AVFrame 中的 YUV 数据:YUV 分为三个分量,通常存储在 `data[0]`、`data[1]` `data[2]` 中,分别对应 Y (亮度)、U (蓝色差值) V (红色差值)。 2. 设置 cv::Mat 尺寸:`AVFrame` 的尺寸信息通常在 `width`, `height` 变量里。创建一个与之匹配的 OpenCV `Mat`,比如使用 `cv::Mat yuv Mat(height, width, CV_8UC3)`,其中 `CV_8UC3` 表示三通道,每通道深度为8位。 3. 复制 YUV 数据到 cv::Mat:将 YUV 数据按顺序复制到 OpenCV 的 `Mat` 的三个通道(BGR),因为 YUV 到 BGR 的换公式通常是固定的,例如: - B (蓝色) = Y + 1.402 * V - G (绿色) = Y - 0.344 * U - 0.714 * V - R (红色) = Y + 1.772 * U 你可以手动计算并填充,也可以使用第三方库如 `libyuv` 或 `OpenCV` 自带的函数来进行换。 4. 创建并初始化 cv::Mat:根据步骤3的结果创建一个新的 `cv::Mat` 并填充数据。 下面是简单的代码示例(假设已有一个名为 `frame` 的 `AVFrame` 对象): ```cpp // 初始化一个 cv::Mat 用于存放换后的图像 cv::Mat yuvMat(frame.height, frame.width, CV_8UC3); uchar* yData = frame.data[0]; uchar* uData = frame.data[1]; uchar* vData = frame.data[2]; // YUV to BGR 换 for (int i = 0; i < frame.height * frame.width; ++i) { int yIndex = i * 3; int uIndex = yIndex + 1; int vIndex = yIndex + 2; yuvMat.at<cv::Vec3b>(i) = {yData[yIndex], // B ((yData[uIndex] - 128) << 16) | (vData[vIndex] - 128), // G and R combined as one byte (16 bits) vData[vIndex]}; // V } // 现在 yuvMat 就包含了从 AVFrame 换过来的 BGR 图像数据 ```
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值