一个由通义千问以及FFmpeg的AVFrame、buffer引起的bug:前面几帧影响后面帧数据

news2025/4/19 3:40:38

目录

1 问题描述

2 我最开始的代码----错误代码

3 正确的代码

4 为什么前面帧的结果会叠加到了后面帧上----因为ffmpeg新一帧只更新上一帧变化的部分

5 以后不要用通义千问写代码


1 问题描述

某个项目中,需要做人脸马赛克,然后这个是君正的某款芯片,他没有硬件解码,我就用了ffmpeg解码,然后我让通义千问给我写一个用ffmpeg解码h264视频的代码,然后对人脸区域做马赛克处理,出现的问题就是比如我在打人脸马赛克的时候出现重影、残留,就是比如我前面几帧做的马赛克在后面几帧上竟然还有,

这个看现象就是前面帧的马赛克效果被叠加到后面了。

2 我最开始的代码----错误代码



int SkuDetect::face_mosaic(const char* video_path) {
    // 初始化 FFmpeg
    av_register_all();

    // 打开视频文件
    AVFormatContext* format_context = nullptr;
    if (avformat_open_input(&format_context, video_path, nullptr, nullptr) != 0) {
        std::cerr << "Could not open source file " << video_path << "\n";
        return -1;
    }

    if (avformat_find_stream_info(format_context, nullptr) < 0) {
        std::cerr << "Failed to retrieve input stream information.\n";
        return -1;
    }

    int video_stream_index = -1;
    for (unsigned i = 0; i < format_context->nb_streams; ++i) {
        if (format_context->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
            video_stream_index = i;
            break;
        }
    }

    if (video_stream_index == -1) {
        std::cerr << "File does not contain any video stream.\n";
        return -1;
    }

    AVCodecParameters* codec_parameters = format_context->streams[video_stream_index]->codecpar;
    const AVCodec* codec = avcodec_find_decoder(codec_parameters->codec_id);
    AVCodecContext* codec_context = avcodec_alloc_context3(codec);
    avcodec_parameters_to_context(codec_context, codec_parameters);

    if (avcodec_open2(codec_context, codec, nullptr) < 0) {
        std::cerr << "Failed to open codec.\n";
        return -1;
    }

    printf("Pixel format: %d\n", codec_context->pix_fmt);

    // 获取视频的原始宽高
    int orig_w = codec_context->width;
    int orig_h = codec_context->height;

    // 加载人脸坐标
    std::map<int, std::vector<std::array<int, 4>>> face_coords_map = load_face_coordinates(video_path, orig_w, orig_h);

    //AVFrame* frame = av_frame_alloc();
    AVPacket packet;

    int frameCount = 0; // 记录帧数
    while (av_read_frame(format_context, &packet) >= 0) 
    {
        AVFrame* frame = av_frame_alloc();
        if (packet.stream_index == video_stream_index) {
            if (avcodec_send_packet(codec_context, &packet) != 0) {
                std::cerr << "Error sending a packet for decoding." << std::endl;
                continue;
            }

            while (avcodec_receive_frame(codec_context, frame) == 0) 
            {
                // 深拷贝 AVFrame 数据
                AVFrame* cloned_frame = av_frame_alloc();//用了深拷贝之后也还是有残影,并且深拷贝之后里面的data[0]的地址跟frame值一样的,指向同一块内存地址。
                cloned_frame = av_frame_clone(frame);
                

                // 获取当前帧的人脸坐标
                auto it = face_coords_map.find(frameCount);
                if (it != face_coords_map.end()) {
                    const std::vector<std::array<int, 4>>& faces = it->second;
                    printf("faces.size(): %d\n", faces.size());

                    //用了深拷贝,并且memeset,然后前面这里保存依然会有马赛克残影,
                    //save_frame_as_image(frame, frameCount, "result_image", it != face_coords_map.end() ? it->second : std::vector<std::array<int, 4>>());

                    // 遍历所有人脸区域
                    for (const auto& face : faces) {
                        int x = face[0];
                        int y = face[1];
                        int width = face[2];
                        int height = face[3];
                        printf("frameCount:%d,    x: %d, y: %d, width: %d, height: %d\n",frameCount, x, y, width, height);
                        printf("frame->data[0]:0x%x, frame->linesize[0]:%d \n", frame->data[0], frame->linesize[0]);
                        
                        


                        // 对 Y 分量进行马赛克
                        apply_uniform_mosaic_y(dst_frame->data[0], dst_frame->linesize[0], x, y, width, height);

                        // 对 U 和 V 分量进行马赛克
                        int x_uv = x_expanded / 2, y_uv = y_expanded / 2;
                        int width_uv = width_expanded / 2, height_uv = height_expanded / 2;
                        apply_uniform_mosaic_uv(frame->data[1], frame->linesize[1], x_uv, y_uv, width_uv, height_uv);
                        apply_uniform_mosaic_uv(frame->data[2], frame->linesize[2], x_uv, y_uv, width_uv, height_uv);
                    }
                }
                
                // 保存处理后的帧为图片
                //save_frame_as_image(frame, frameCount, "result_image_mosaic", it != face_coords_map.end() ? it->second : std::vector<std::array<int, 4>>());

                // 保存处理后的帧为图片
                save_frame_as_image(dst_frame, frameCount, "result_image_mosaic", it != face_coords_map.end() ? it->second : std::vector<std::array<int, 4>>());
                av_frame_free(&dst_frame);  
                  
                //memset(frame, 0, sizeof(AVFrame));//用了memset之后也还是有残影。

                // 释放克隆帧
                av_frame_unref(cloned_frame);
                av_frame_free(&cloned_frame);

                frameCount++; // 增加帧数
            }
        }
        // 重置 AVFrame
        av_frame_unref(frame);
        av_packet_unref(&packet);
        av_frame_free(&frame);
    }

    // 清理资源
    //
    avcodec_free_context(&codec_context);
    avformat_close_input(&format_context);

    std::cout << "Processing completed." << std::endl;
    return 0;
}

上面的代码是让通义千问给写的,刚开始不对,他还让加上了clone函数做深拷贝,但其实深拷贝不是这样的,

3 正确的代码

AVFrame* apply_mosaic_with_copy(const AVFrame* src_frame, int x, int y, int width, int height, int block_size) {
    if (!src_frame || !src_frame->data[0]) {
        return nullptr;
    }
    
    // 创建新帧
    AVFrame* dst_frame = av_frame_alloc();
    if (!dst_frame) {
        return nullptr;
    }
    
    // 复制帧属性
    dst_frame->format = src_frame->format;
    dst_frame->width = src_frame->width;
    dst_frame->height = src_frame->height;
    
    // 分配缓冲区
    if (av_frame_get_buffer(dst_frame, 32) < 0) {
        av_frame_free(&dst_frame);
        return nullptr;
    }
    
    // 复制原始数据
    if (av_frame_copy(dst_frame, src_frame) < 0) {
        av_frame_free(&dst_frame);
        return nullptr;
    }
    
    // 设置其他属性
    if (av_frame_copy_props(dst_frame, src_frame) < 0) {
        av_frame_free(&dst_frame);
        return nullptr;
    }
    
    // 应用马赛克(使用修改后的版本)
    if (apply_mosaic_to_avframe(dst_frame, x, y, width, height, block_size) < 0) {
        av_frame_free(&dst_frame);
        return nullptr;
    }
    
    return dst_frame;
}

正确的深拷贝的代码应该是这样的,不只是alloc一个frame,还需要av_frame_get_buffer,重点就在这里的buffer。

4 为什么前面帧的结果会叠加到了后面帧上----因为ffmpeg新一帧只更新上一帧变化的部分

看完下面几个问题和答案之后就明白为什么了,下面几个问题是用deepseek问的,不是通义千问,通义千问的答案明显不行

5 以后不要用通义千问写代码

通义千问不适合写代码,不行,以前的h264转yuv视频的时候,也是通义千问给写的代码错了,将.mp4视频文件转成.yuv视频文件C++代码备份_mp4视频保存为yuv格式-CSDN博客

这个以前的代码就是通义千问给写错了,而且错误很隐蔽,转出来的yuv是对的,只是稍微有点色差,当初这个bug也耽误了不少时间。 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2336769.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

12.第二阶段x64游戏实战-远程调试

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 本次游戏没法给 内容参考于&#xff1a;微尘网络安全 上一个内容&#xff1a;11.第二阶段x64游戏实战-框架代码细节优化 本次写的内容是关于调试、排错相关的…

Coze 和 n8n 的详细介绍及多维度对比分析,涵盖功能、架构、适用场景、成本等关键指标

以下是 Coze 和 n8n 的详细介绍及多维度对比分析&#xff0c;涵盖功能、架构、适用场景、成本等关键指标&#xff1a; 一、Coze 详细介绍 1. 基础信息 类型&#xff1a;低代码自动化平台&#xff08;SaaS&#xff09;。开源性&#xff1a;闭源&#xff08;企业版需付费&…

咋用fliki的AI生成各类视频?AI生成视频教程

最近想制作视频&#xff0c;多方考查了决定用fliki&#xff0c;于是订阅了一年试试&#xff0c;这个AI生成的视频效果来看真是不错&#xff0c;感兴趣的自己官网注册个账号体验一下就知道了。 fliki官网 Fliki生成视频教程 创建账户并登录 首先&#xff0c;访问fliki官网并注…

Linux : 进程等待以及进程终止

进程控制之进程等待 &#xff08;一&#xff09;fork函数1*fork函数返回值2.父子进程的写时拷贝 &#xff08;二&#xff09;进程终止1.进程退出码2.进程常见退出方法&#xff08;1&#xff09;_exit&#xff08;2&#xff09;exit&#xff08;3&#xff09;return 3.进程的异常…

LSTM结合LightGBM高纬时序预测

1. LSTM 时间序列预测 LSTM 是 RNN&#xff08;Recurrent Neural Network&#xff09;的一种变体&#xff0c;它解决了普通 RNN 训练时的梯度消失和梯度爆炸问题&#xff0c;适用于长期依赖的时间序列建模。 LSTM 结构 LSTM 由 输入门&#xff08;Input Gate&#xff09;、遗…

【统信UOS操作系统】python3.11安装numpy库及导入问题解决

一、安装Python3.11.4 首先来安装Python3.11.4。所用操作系统&#xff1a;统信UOS 前提是准备好Python3.11.4的安装包&#xff08;可从官网下载&#xff08;链接&#xff09;&#xff09;&#xff0c;并解压到本地&#xff1a; 右键&#xff0c;选择“在终端中打开”&#xff…

【中间件】nginx反向代理实操

一、说明 nginx用于做反向代理&#xff0c;其目标是将浏览器中的请求进行转发&#xff0c;应用场景如下&#xff1a; 说明&#xff1a; 1、用户在浏览器中发送请求 2、nginx监听到浏览器中的请求时&#xff0c;将该请求转发到网关 3、网关再将请求转发至对应服务 二、具体操作…

鸿蒙应用(医院诊疗系统)开发篇2·Axios网络请求封装全流程解析

一、项目初始化与环境准备 1. 创建鸿蒙工程 src/main/ets/ ├── api/ │ ├── api.ets # 接口聚合入口 │ ├── login.ets # 登录模块接口 │ └── request.ets # 网络请求核心封装 └── pages/ └── login.ets # 登录页面逻辑…

突发重磅消息!!!CVE项目将被取消?

突发重磅消息&#xff01;&#xff01;&#xff01;CVE项目将被取消&#xff1f;突发&#xff01;来自可靠消息来源。MITRE 对 CVE 项目的支持将于明天到期。附件信件已发送给 CVE 董事会成员。https://mp.weixin.qq.com/s/N3qkiHaDfzDuBMK3JbBCjw

详解与FTP服务器相关操作

目录 什么是FTP服务器 搭建FTP服务器相关 ​编辑 Unity中与FTP相关的类 上传文件到FTP服务器 使用FTP服务器上传文件的关键点 开始上传 从FTP服务器下载文件到客户端 使用FTP下载文件的关键点 开始下载 关于FTP服务器的其他操作 将文件的上传&#xff0c;下载&…

解决 .Net 6.0 项目发布到IIS报错:HTTP Error 500.30

今天在将自己开发许久的项目上线的时候&#xff0c;发现 IIS 发布后请求后端老是报一个 HTTP Error 500.30 的异常&#xff0c;如下图所示。   后来仔细调查了一下发现是自己的程序中写了 UseStaticFiles 的依赖注入&#xff0c;这个的主要作用就是发布后端后&#xff0c;想…

STM32F103_HAL库+寄存器学习笔记16 - 监控CAN发送失败(轮询方式)

导言 《STM32F103_HAL库寄存器学习笔记15 - 梳理CAN发送失败时&#xff0c;涉及哪些寄存器》从上一章节看到&#xff0c;当CAN消息发送失败时&#xff0c;CAN错误状态寄存器ESR的TEC会持续累加&#xff0c;LEC等于0x03&#xff08;ACK错误&#xff09;。本次实验的目的是编写一…

实现定长的内存池

池化技术 所谓的池化技术&#xff0c;就是程序预先向系统申请过量的资源&#xff0c;然后自己管理起来&#xff0c;以备不时之需。这个操作的价值就是&#xff0c;如果申请与释放资源的开销较大&#xff0c;提前申请资源并在使用后并不释放而是重复利用&#xff0c;能够提高程序…

vs2022使用git方法

1、创建git 2、在cmd下执行 git push -f origin master &#xff0c;会把本地代码全部推送到远程&#xff0c;同时会覆盖远程代码。 3、需要设置【Git全局设置】&#xff0c;修改的代码才会显示可以提交&#xff0c;否则是灰色的不能提交。 4、创建的分支&#xff0c;只要点击…

Mysql中表的使用(3)

目录 1.updata的使用 2.delete(删除表中数据)drop&#xff08;删除表&#xff09; 数据库的约束 1.NOT NULL 指定列不能为空 2.UNIQUE指定列唯一 3.DEFAULT(默认值) 4.PRIMARY KEY 5.自增主键 1.updata的使用 1.0update 表名 set 列名x where 列名y; 2.0update 表名 s…

BUUCTF-Web(1-20)

目录 一.SQL注入 (1)[极客大挑战 2019]EasySQL 万能密码 (7)[SUCTF 2019]EasySQL 堆叠注入 解一&#xff1a; 解二&#xff1a; (10)[强网杯 2019]随便注 堆叠注入 解一&#xff1a; 解二&#xff1a; 解三&#xff1a; (8)[极客大挑战 2019]LoveSQL 联…

Uniapp:确认框

目录 一、 出现场景二、 效果展示三、具体使用 一、 出现场景 在项目的开发中&#xff0c;会经常出现删除数据的情况&#xff0c;如果直接删除的话&#xff0c;可能会存在误删&#xff0c;用户体验不好&#xff0c;所以需要增加一个消息提示&#xff0c;提醒用户是否删除。 二…

实验四 中断实验

一、实验目的 掌握中断服务程序的编写。 二、实验电路 三、实验内容 1&#xff0e;实验用PC机内部的中断控制器8259A&#xff0c;中断源用TPC-ZK实验箱上的单脉冲电路&#xff0c;将单脉冲电路的输出接中断请求信号IRQ&#xff0c;每按一次单脉冲按键产生一次…

腾势品牌欧洲市场冲锋,科技豪华席卷米兰

在时尚与艺术的交汇点&#xff0c;米兰设计周的舞台上&#xff0c;一场汽车界的超级风暴正在酝酿&#xff0c;腾势品牌如一头勇猛无畏的雄狮&#xff0c;以雷霆万钧之势正式向欧洲市场发起了冲锋。其最新力作——腾势Z9GT的登场&#xff0c;仿佛是一道闪电划破夜空&#xff0c;…

Java 中的各种锁详解

&#x1f9d1; 博主简介&#xff1a;CSDN博客专家&#xff0c;历代文学网&#xff08;PC端可以访问&#xff1a;https://literature.sinhy.com/#/literature?__c1000&#xff0c;移动端可微信小程序搜索“历代文学”&#xff09;总架构师&#xff0c;15年工作经验&#xff0c;…