基于opencv实现两路yuv数据拼接合成一张大图

news2024/12/23 17:13:32

背景

实时音视频通话(RTC)越来越注重安全审核,特别是在1v1娱乐社交场景中,对于视频反垃圾的需求也越来越大。随之而来的是客户对审核成本降低的诉求日益强烈。针对1v1场景,将两路视频拼接成一张图片进行审核相比于分别审核两路视频可以降低约50%的成本。然而,这种方法存在缺点:某些检测细节准确度会稍微降低一些,因为同一个特征在合成图里尺寸会变小。

前置条件

  • ubuntu 18.04
  • 安装opencv
    sudo apt install libopencv-dev

处理流程

  • 读取两路YUV数据
  • 将两路YUV数据进行拼接
  • 将拼接后的数据保存为一张大图

实现细节

  • 彩色图像转换:使用cv::cvtColor函数将yuv转换为rgb,示例如下:

cv::cvtColor(yuv1, bgr1, cv::COLOR_YUV2BGR_I420)

  • 图像缩放模式:使用cv::resize函数实现图片缩放,示例如下:

cv::resize(bgr1, scaledImage, scaledSize, 0, 0, cv::INTER_LINEAR);

cv::resize(bgr1, outputImage1, dst_half_size, 0, 0,cv ::INTER_LINEAR);

使用双线性插值算法(cv::INTER_LINEAR)进行处理。

  • Fit模式:图片尺寸等比缩放。优先保证图片内容全部显示。若图片尺寸与显示视窗尺寸不一致,则未被填满的区域填充背景色(黑色)。

  • Full Fill模式:图片尺寸非等比缩放。保证图片内容全部显示,并且填满视窗。

  • 图像拼接:将两个经过缩放处理的图像分别拷贝到输出图像的左半部分和右半部分,实现图像拼接。示例如下:

outputImage1.copyTo(dst(cv ::Rect(cv ::Point(0 ,0), dst_half_size)));

outputImage2.copyTo(dst(cv ::Rect(cv ::Point(out_w /2 ,0), dst_half_size)));

测试结果(可执行程序由下面的源码编译)

  • 运行指令
    ./a.out [yuv1 yuv1_width yuv1_height] [yuv2 yuv2_width yuv2_height] [output.jpeg output_width output_height] [mode]

    mode:
    0: Fit
    1: Full Fill

  • 输入yuv
    • 1280x720.yuv
    • 1920x1080.yuv
  • 输出jpeg
    举例1: 输出1920x1080, Fit
    在这里插入图片描述
    举例2: 输出1920x1080, Full Fill
    在这里插入图片描述

举例3: 输出500x500, Fit
在这里插入图片描述

举例4: 输出500x500, Full Fill
在这里插入图片描述

源码

源文件:two_yuv_to_one_jpeg.cpp
编译:g++ two_yuv_to_one_jpeg.cpp `pkg-config --cflags --libs opencv

#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <iostream>
#include <string>

int main(int argc, char** argv)
{
    if (argc != 11) {
        std::cout << "Usage: " << argv[0] << " yuv_file1 width height yuv_file2 width height output_jpeg_file width height mode" << std::endl;
        return -1;
    }

    // 打开第一个YUV文件
    FILE* fp1 = fopen(argv[1], "rb");
    if (!fp1) {
        std::cout << "Failed to open file1 " << argv[1] << std::endl;
        return -1;
    }

    // 打开第二个YUV文件
    FILE* fp2 = fopen(argv[4], "rb");
    if (!fp2) {
        std::cout << "Failed to open file2 " << argv[4] << std::endl;
        return -1;
    }
    int w1 = std::stoi(argv[2]);
    int h1 = std::stoi(argv[3]);
    int w2 = std::stoi(argv[5]);
    int h2 = std::stoi(argv[6]);
    int out_w = std::stoi(argv[8]);
    int out_h = std::stoi(argv[9]);
    int mode = std::stoi(argv[10]);

    // 创建输出图像
    cv::Mat dst(out_h, out_w, CV_8UC3);
    // 两个yuv图像各占输出的图像的一半(左右排列),单个图像大小
    cv::Size dst_half_size(out_w / 2, out_h); 

    // 图像1处理
    cv::Mat yuv1(h1 + h1 / 2 , w1, CV_8UC1);
    for (int i = 0; i < h1 + h1 / 2; i++) {
        fread(yuv1.ptr(i), 1, w1, fp1);
    }
    // I420转RGB
    cv::Mat bgr1;
    cv::cvtColor(yuv1, bgr1, cv::COLOR_YUV2BGR_I420);
            
    // 创建一个临时输出图像,大小为dst_half_size,颜色为黑色
    cv::Mat outputImage1(dst_half_size, CV_8UC3, cv::Scalar(0, 0, 0));
    if (mode == 0) { // fit
        // 计算缩放比例
        double scale = std::min((double) dst_half_size.width / bgr1.cols, (double) dst_half_size.height / bgr1.rows);
        // 图像缩放
        cv::Mat scaledImage;
        cv::Size scaledSize(cvRound(bgr1.cols * scale), cvRound(bgr1.rows * scale));
        cv::resize(bgr1, scaledImage, scaledSize, 0, 0, cv::INTER_LINEAR);
        // 将缩放后的图像复制到临时输出图像中指定的位置
        scaledImage.copyTo(outputImage1(cv::Rect((dst_half_size.width - scaledSize.width) / 2, (dst_half_size.height - scaledSize.height) / 2, scaledSize.width, scaledSize.height)));
    } else { // full fill
        cv::resize(bgr1, outputImage1, dst_half_size, 0, 0, cv::INTER_LINEAR);
    }
    
    // 将图像拷贝到输出图像的左半部分
    outputImage1.copyTo(dst(cv::Rect(cv::Point(0, 0), dst_half_size)));

    // 图像2处理
    cv::Mat yuv2(h2 + h2 / 2 , w2, CV_8UC1);
    for (int i = 0; i < (h2 + h2 / 2); i++) {
        fread(yuv2.ptr(i), 1, w2, fp2);
    }
    // I420转RGB
    cv::Mat bgr2;
    cv::cvtColor(yuv2, bgr2, cv::COLOR_YUV2BGR_I420);

    // 创建一个临时输出图像,大小为dst_half_size,颜色为黑色
    cv::Mat outputImage2(dst_half_size, CV_8UC3, cv::Scalar(0, 0, 0));
    if (mode == 0) { // fit
        // 计算缩放比例
        double scale = std::min((double) dst_half_size.width / bgr2.cols, (double) dst_half_size.height / bgr2.rows);
        // 图像缩放
        cv::Mat scaledImage;
        cv::Size scaledSize(cvRound(bgr2.cols * scale), cvRound(bgr2.rows * scale));
        cv::resize(bgr2, scaledImage, scaledSize, 0, 0, cv::INTER_LINEAR);
        // 将缩放后的图像复制到临时输出图像中指定的位置
        scaledImage.copyTo(outputImage2(cv::Rect((dst_half_size.width - scaledSize.width) / 2, (dst_half_size.height - scaledSize.height) / 2, scaledSize.width, scaledSize.height))); 
    } else {
        cv::resize(bgr2, outputImage2, dst_half_size, 0, 0, cv::INTER_LINEAR);
    }

    // 将图像拷贝到输出图像的左半部分
    outputImage2.copyTo(dst(cv::Rect(cv::Point(out_w / 2, 0), dst_half_size)));

    // 关闭文件
    fclose(fp1);
    fclose(fp2);

    // 保存输出图像
    imwrite(argv[7], dst);
    
    // 显示图像
    imshow("YUV to Mat", dst);
    cv::waitKey(0);
    
    return 0;
}

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

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

相关文章

大数据Doris(三十一):Broker Load导入HDFS json格式数据和注意事项

文章目录 Broker Load导入HDFS json格式数据和注意事项 一、导入HDFS json格式数据 1、创建Doris表

nginx(八十一)rewrite模块指令再探之(三)重定向

一 return和rewrite重定向再探 ① 前言 多种重定向跳转方式的差异 nginx与Location响应头细节探讨 本为不涉及讨论如下的绝对重定向1) return 301 http://www.wzj.com:6443/url?namewzj2) rewrite ... http://www.wzj.com:6443/url 2) rewrite ... http://www.wzj.com:64…

一分钟学一个 Linux 命令 - pwd

前言 大家好&#xff0c;我是 god23bin。欢迎大家继续围观《一分钟学一个 Linux 命令》&#xff0c;每天只需一分钟&#xff0c;记住一个 Linux 命令不成问题。本篇文章将聚焦于 pwd 命令&#xff0c;一个超级简单又常用的命令。在接下来的内容中&#xff0c;我将快速介绍 pwd…

Elasticsearch总结

详细描述一下 Elasticsearch 搜索的过程&#xff1f; 1、搜索被执行成一个两阶段过程&#xff0c;我们称之为 Query Then Fetch&#xff1b; 2、在初始查询阶段时&#xff0c;查询会广播到索引中每一个分片拷贝&#xff08;主分片或者副本分片&#xff09;。 每个分片在本地执…

chatgpt赋能python:使用Python关闭所有子进程

使用Python关闭所有子进程 如果您使用Python编写了多进程应用程序&#xff0c;那么您可能会遇到一些关闭所有子进程的问题。这种情况可能是您的主进程已经完成了&#xff0c;但是子进程却没有关闭&#xff0c;从而导致资源浪费和程序崩溃。在这篇文章中&#xff0c;我们将讨论…

STM32F1xx -- Systick 系统滴答定时器

1. SysTick 是一个向 CPU 提供定时中断信号的计数器&#xff0c;其计数速率是由 Cortex-M 系列处理器的系统时钟频率和 SysTick 计数器的重载值共同决定的。 1.1 Systick 时钟来源之一&#xff0c;Systick 一般设置为1ms 中断一次&#xff0c;为系统任务调度提供服务&#xff…

R语言:集卡活动概率测算模拟

背景&#xff1a;以支付宝集五福活动为代表的集卡类营销活动背后&#xff0c;每张卡出现的概率测算是非常重要的&#xff0c;假设我们可以预估有多少人参与活动以及大致每人能抽多少次&#xff0c;且限定一共有多少人能够集齐&#xff0c;在这些限定条件下&#xff0c;每张卡出…

CentOS 系统上安装 Jenkins

#######################注意我这里安装jenkins版本要求实际是要安装jdk11版本的~~~我一开始弄错了 您可以按照以下步骤在 CentOS 上安装 JDK&#xff1a; 1. 首先&#xff0c;打开终端并使用 yum 命令更新系统软件包列表。输入以下命令来执行此操作&#xff1a; sudo yu…

chatgpt赋能python:Python为什么闪退?

Python为什么闪退&#xff1f; Python作为一种高级编程语言&#xff0c;已经赢得了世界各地许多开发者的青睐。但是&#xff0c;有时候Python会因为各种原因而突然闪退&#xff0c;给开发者带来极大的困扰。那么&#xff0c;Python为什么会闪退呢&#xff1f; 1. 内存泄漏 内…

环境感知算法——2.CenterNet基于KITTI数据集训练

1. CenterNet简介 CenterNet采用了一种新的检测思路&#xff0c;即以目标中心点为基础&#xff0c;直接回归出目标的位置和大小。而传统的目标检测算法通常会先产生大量候选框&#xff08;Anchor&#xff09;&#xff0c;再通过分类器进行筛选&#xff0c;这种方法比较复杂。C…

selenium浏览器自动化测试

Selenium是一个用于Web应用程序的自动化测试工具。它直接运行在浏览器中&#xff0c;可以模拟用户在浏览器上面的行为操作。 Selenium入门 下载驱动 查看谷歌浏览器版本 谷歌浏览器输入网址的地方输入&#xff1a;chrome://version 下载与浏览器对应&#xff08;或相近&#x…

RK3588平台开发系列讲解(驱动基础篇)驱动模块传参

平台内核版本安卓版本RK3588Linux 5.10Android 12文章目录 一、驱动模块传参简介二、驱动模块传普通参数三、驱动模块传数组沉淀、分享、成长,让自己和他人都能有所收获!😄 📢在编写应用程序的时候,我们可以给 main 函数传递参数,比如 void main(int argc,char *argv[]…

【ChatGPT】如何利用ChatGPT来快速统计Excel数据?

系列文章目录 老板让你写个PPT没有头绪&#xff1f;没事&#xff0c;ChatGPT来帮你&#xff01;传送门 文章目录 系列文章目录前言一、不会公式&#xff1f;帮你生成二、不会处理数据&#xff1f;帮你处理写在最后 前言 自从人工智能横空而出&#xff0c;它在人们的生活中产生…

Apache配置与应用与网页安全优化

Apache配置与应用与网页安全优化 构建虚拟Web主机httpd服务支持的三种虚拟机类型基于域名的虚拟主机Options指令解释AllowOverride指令解析 基于IP地址的虚拟主机基于端口的虚拟主机Apache连接保持构建Web虚拟目录与用户授权限制Apache 日志分割AWStats分析系统 网页压缩网页缓…

研发工程师玩转Kubernetes——多Worker Node部署

在之前的系列中&#xff0c;我们都是在单Node上“玩转”kubernetes&#xff0c;熟悉了它很多指令和特性。从本节开始&#xff0c;我们开始探索多Worker Node的相关特性。 部署虚拟机 因为desktop版ubuntu非常占用内存&#xff0c;而且我们已经熟悉了一些基本操作&#xff0c;…

电能质量监测装置在半导体公司“大放异彩”

摘 要&#xff1a;半导体生产制造业在国民经济中起着举足轻重的作用&#xff0c;相关企业的规模也越来越大。其供配电系统稳定、可靠的运维不仅是其安全生产的基本保证&#xff0c;还关系到产品质量和生产的顺利进行。而半导体行业中大部分工艺设备对电能质量比较敏感&#xff…

kafka 的内部结构和 kafka 的工作原理

基本设置 让我们开始安装kafka。下载最新的 Kafka 版本并解压缩。打开终端并启动 kafka 和 zookeeper。 $ cd $HOME $ tar -xzf kafka_<version>.tgz $ cd kafka_<version> $ bin/zookeeper-server-start.sh config/zookeeper.properties # open another termina…

Web安全之常见攻防

前言&#xff1a; 在当下&#xff0c;数据安全与个人隐私受到了前所未有的挑战。如何才能更好地保护我们的数据&#xff1f;接下来分析几种常见的攻击的类型以及防御的方法。 一、XSS&#xff08;Cross Site Script&#xff09; 首先了解最常见的 XSS 漏洞&#xff0c;XSS (Cr…

Nginx的网站服务

Nginx网站服务 一、Nginx与apache的差异二、编译安装Nginx服务三、Nginx命令四、向系统添加nginx服务五、nginx配置文件六、http块的配置 一、Nginx与apache的差异 nginx相对于apache的优点&#xff1a; 轻量级&#xff0c;同样是web服务&#xff0c;比apache占用内存更少及资…

chatgpt赋能python:Python为什么运行不出结果?

Python为什么运行不出结果&#xff1f; 简介 Python是一种高级编程语言&#xff0c;可以帮助程序员快速开发软件应用。不过&#xff0c;在使用Python的过程中&#xff0c;你可能会遇到一些麻烦。其中一个常见的问题是Python运行不出结果。 如果你正在遇到这个问题&#xff0…