工作杂记-YUV的dump和read

news2024/11/26 16:23:16

工作小记-YUV的dump和read

  • 工作杂记-YUV的dump和read
  • 利用dump生成图片 yuv2img
  • yuv2img代码

工作杂记-YUV的dump和read

工作中涉及到模型验证相关的工作,这里是三个模型的共同作用,在感知模型读取图片的时候,把输入替换成自己给定的输入,保证一致性,再来看输出。
知道了模型输入的宽高和步长之后,有如下的dump_yuv和read_yuv函数。其中bpu_addr和bpu_addr_uv是y分量和uv分量的地址。

bool ReadYUVImage(const std::string &img_path, int width, int height,
                  uint64_t bpu_addr, uint64_t bpu_addr_uv) {
  std::ifstream input_img_file(img_path, std::ios::binary);
  input_img_file.seekg(0, std::ios::end);
  int len = input_img_file.tellg();
  input_img_file.seekg(0, std::ios::beg);
  std::vector<uint8_t> img_data(width * height * 3 / 2);
  if (len < width * height * 3 / 2) {
    HSLOG_E << "file length is not right " << len << " img_file:" << img_path;
    return false;
  } else {
    input_img_file.read(reinterpret_cast<char *>(img_data.data()),
                        width * height * 3 / 2);
    memcpy(reinterpret_cast<void *>(bpu_addr), img_data.data(), width * height);
    memcpy(reinterpret_cast<void *>(bpu_addr_uv),
           img_data.data() + width * height, width * height / 2);
  }
  input_img_file.close();
  return true;
}

void DumpYUVImage(const std::string &out_file, int width, int height,
                  uint64_t bpu_addr, uint64_t bpu_addr_uv) {
  std::ofstream fout(out_file, std::ios::out | std::ios::binary);
  std::vector<uint8_t> src_input_image(width * height * 3 / 2);
  memcpy(src_input_image.data(), reinterpret_cast<void *>(bpu_addr),
         width * height);
  memcpy(src_input_image.data() + width * height,
         reinterpret_cast<void *>(bpu_addr_uv), width * height / 2);
  fout.write(reinterpret_cast<char *>(src_input_image.data()),
             width * height * 3 / 2);
  fout.close();
}

利用dump生成图片 yuv2img

这里需要了解yuv相关的排布知识。
YUV格式有两大类:planar和packed。
对于planar的YUV格式,先连续存储所有像素点的Y,紧接着存储所有像素点的U,随后是所有像素点的V。
对于packed的YUV格式,每个像素点的Y,U,V是连续交*存储的。

YUV,分为三个分量,“Y”表示明亮度(Luminance或Luma),也就是灰度值;而“U”和“V” 表示的则是色度(Chrominance或Chroma),作用是描述影像色彩及饱和度,用于指定像素的颜色。

planar的YUV格式分为YUV420P和YUV420SP,YUV420P包含I420和YV12。I420格式和YV12格式的不同处在U平面和V平面的位置不同。在I420格式中,U平面紧跟在Y平面之后,然后才是V平面(即:YUV);但YV12则是相反(即:YVU)。
YUV420SP, Y分量平面格式,UV打包格式, 即NV12。 NV12与NV21类似,U 和 V 交错排列,不同在于UV顺序。
I420: YYYYYYYY UU VV =>YUV420P
YV12: YYYYYYYY VV UU =>YUV420P
NV12: YYYYYYYY UVUV =>YUV420SP
NV21: YYYYYYYY VUVU =>YUV420SP
yuv排布

注意转化时要知道文件的uv排列先后顺序,实测u,v颠倒后的图片色差会有变化。
正确的结果 👇
在这里插入图片描述
错误的结果👇
在这里插入图片描述

核心代码,这里有一个opencv的坑,在merge时 会报错Invalid number of channels in input image: ‘VScn::contains(scn)’ ‘scn’,原因时Y U V的shape不一致,解决方案时把UV插值成和Y一样的,在merge,这样shape就成了(h,w,3)这种形式。再调用cv2.COLOR_YUV2BGR去做转化。

    # 分离UV通道为U和V
    U = UV[:, ::2]
    V = UV[:, 1::2]

    # 扩展U和V通道为与Y通道一致的大小
    U = cv2.resize(U, (width, height), interpolation=cv2.INTER_LINEAR)
    V = cv2.resize(V, (width, height), interpolation=cv2.INTER_LINEAR)

    # 合并Y、U、V通道数据
    YUV420p_image = cv2.merge((Y, U, V))
    # 转换为RGB格式
    image_rgb = cv2.cvtColor(YUV420p_image, cv2.COLOR_YUV2BGR)

yuv2img代码

如下是根据dump的yuv文件生成图片,其中三个函数,分别为灰度(only y 分量),420sp直接转RGB,420sp 提取 U、V分量后,再插值和Y分量组成YUV444,再转成RGB。

Y = np.frombuffer(buffer[:Y_size], dtype=np.uint8).reshape(height, width)
UV = np.frombuffer(buffer[Y_size:], dtype=np.uint8).reshape(height // 2, width)

# 分离UV通道为U和V
U = UV[:, ::2] # 实际上是选择 UV 数组的所有行,但只选择每一行中的偶数列元素,即第0列、第2列、第4列等
V = UV[:, 1::2] 

# 扩展U和V通道为与Y通道一致的大小
U = cv2.resize(U, (width, height), interpolation=cv2.INTER_LINEAR)
V = cv2.resize(V, (width, height), interpolation=cv2.INTER_LINEAR)

# 合并Y、U、V通道数据
YUV420p_image = cv2.merge((Y, U, V))
import cv2
import numpy as np
import re
import os

def convert_YUV420sp_RGB(yuv_file):
        # 从文件名中提取宽度和高度
    match = re.search(r'_w_(\d+)_h_(\d+)', yuv_file)
    if match:
        width = int(match.group(1))
        height = int(match.group(2))
    else:
        print("无法提取分辨率信息:", yuv_file)
        return
    print(width, height)
    with open(yuv_file, "rb") as f:
        buffer = f.read()
    image = np.frombuffer(buffer, np.uint8).reshape(height*3//2, width)
    print(image.size, image.shape)
    image_rgb = cv2.cvtColor(image, cv2.COLOR_YUV420SP2RGB)
    
    yuv_file = os.path.basename(yuv_file)
    output_file = yuv_file.replace(".yuv", ".jpg")
    output_file = os.path.join(output_folder_path, f"{output_file}.jpg")
    cv2.imwrite(output_file, image_rgb)
    # print(output_file)

def convert_YUV420sp_YUV444_RGB(yuv_file):
    # 从文件名中提取宽度和高度
    match = re.search(r'_w_(\d+)_h_(\d+)', yuv_file)
    if match:
        width = int(match.group(1))
        height = int(match.group(2))
    else:
        print("无法提取分辨率信息:", yuv_file)
        return

    # 读取YUV420sp图像数据
    with open(yuv_file, "rb") as f:
        buffer = f.read()

    # 解析Y、UV通道数据
    Y_size = width * height
    UV_size = width * height // 2

    Y = np.frombuffer(buffer[:Y_size], dtype=np.uint8).reshape(height, width)
    UV = np.frombuffer(buffer[Y_size:], dtype=np.uint8).reshape(height // 2, width)

    # 分离UV通道为U和V
    U = UV[:, ::2]
    V = UV[:, 1::2]

    # 扩展U和V通道为与Y通道一致的大小
    U = cv2.resize(U, (width, height), interpolation=cv2.INTER_LINEAR)
    V = cv2.resize(V, (width, height), interpolation=cv2.INTER_LINEAR)

    # 合并Y、U、V通道数据
    YUV420p_image = cv2.merge((Y, U, V))
    # 转换为RGB格式
    image_rgb = cv2.cvtColor(YUV420p_image, cv2.COLOR_YUV2BGR)

    yuv_file = os.path.basename(yuv_file)
    output_file = yuv_file.replace(".yuv", ".jpg")
    output_file = os.path.join(output_folder_path, f"{output_file}.jpg")
    cv2.imwrite(output_file, image_rgb)
    # print(output_file)

def convert_YUV420sp_GRAY(yuv_file):
    file_name = os.path.splitext(yuv_file)[0]
    match = re.search(r'_w_(\d+)_h_(\d+)', yuv_file)
    if match:
        width = int(match.group(1))
        height = int(match.group(2))
    else:
        print("无法提取分辨率信息:", yuv_file)
        return

    # 打开YUV文件并读取数据
    
    with open(yuv_file, 'rb') as file:
        buffer = file.read()

    Y_size = width * height
    Y_data = buffer[:Y_size]

    # 将Y数据转换为NumPy数组
    Y = np.frombuffer(Y_data, dtype=np.uint8).reshape((height, width))
    gray_image = cv2.cvtColor(Y, cv2.COLOR_GRAY2BGR)

    yuv_file = os.path.basename(yuv_file)
    output_file = yuv_file.replace(".yuv", ".jpg")
    output_file = os.path.join(output_folder_path, f"{output_file}.jpg")
    cv2.imwrite(output_file, gray_image)
    return

# 定义输入YUV文件夹路径
input_folder_path = "./dump_yuv"  # 替换为包含YUV文件的文件夹路径

# 定义输出JPEG文件夹路径
output_folder_path = input_folder_path + "_output"  # 保存JPEG文件的文件夹路径
if not os.path.exists(output_folder_path):
    os.makedirs(output_folder_path)

# 获取输入文件夹中的所有YUV文件
yuv_files = [file for file in os.listdir(input_folder_path) if file.endswith(".yuv")]

for yuv_file in yuv_files:
    print(yuv_file)
    # convert_YUV420sp_YUV444_RGB(input_folder_path + "/" + yuv_file)
    # convert_YUV420sp_RGB(input_folder_path + "/" + yuv_file)
    convert_YUV420sp_GRAY(input_folder_path + "/" + yuv_file)
model_id:0 percepts_[0] rect num:1
model_id:0 rect:388.95 1034.04 520.791 1163.19 7.03816 0
model_id:0 percepts_[5] rect num:8
model_id:0 rect:2164.7 1034.81 2261.26 1110.25 8.7623 5
model_id:0 rect:2464.56 996.751 2519.25 1059.25 7.19807 5
model_id:0 rect:2654.99 593.22 2744.83 696.733 8.5591 5
model_id:0 rect:2914.5 570.069 3007.19 666.134 8.76954 5
model_id:0 rect:3044.18 553.215 3152.04 664.448 8.68193 5
model_id:0 rect:3185.64 543.125 3295.19 652.673 7.15572 5
model_id:0 rect:3750.75 609.548 3836 781.291 8.73659 5
model_id:0 rect:3761.16 792.687 3836 1073.72 9.44854 5
model_id:0 percepts_[11] rect num:3
model_id:0 rect:247.464 1250.77 334.153 1311.08 7.29449 11
model_id:0 rect:1664.7 1409.73 2243.79 1526.45 7.30121 11
model_id:0 rect:3057.62 1149.15 3528.76 1186.85 7.29843 11
model_id:0 percepts_[13] rect num:3
model_id:0 rect:1390.61 1212.43 1441.98 1423.03 8.79426 13
model_id:0 rect:1992.3 1189.52 2025.68 1335.91 7.29169 13
model_id:0 rect:2816.59 1182.99 2865.39 1378.19 7.29921 13

再额外附一段画框的代码

import cv2
import re

# 读取图片
image_path = './dump_yuv_v3_output/model_50_frame_1694608049106_w_3840_h_2160.jpg.jpg'
image = cv2.imread(image_path)

# 打开包含矩形框坐标的文件
with open('rect.txt', 'r') as file:
    lines = file.readlines()

# 正则表达式模式,用于匹配包含model_id和percepts的行
model_pattern = re.compile(r'model_id:\d+ percepts_\[(\d+)\] rect num:(\d+)')

# 正则表达式模式,用于匹配包含model_id和rect的行
rect_pattern = re.compile(r'model_id:\d+ rect:(\d+(?:\.\d+)?) (\d+(?:\.\d+)?) (\d+(?:\.\d+)?) (\d+(?:\.\d+)?)')

# 初始化一个字典来存储percepts_值和颜色的映射
percepts_color_mapping = {
    "0": (0, 0, 255),   # 红色
    "5": (0, 255, 0),   # 绿色
    "11": (255, 0, 0),   # 蓝色
    "13": (0, 255, 255), # 黄色
}


# 遍历文件中的每一行
for line in lines:
    # 尝试匹配包含矩形坐标的行
    model_match = model_pattern.match(line)
    if model_match:
        percept_index = model_match.group(1)
        num_rects = model_match.group(2)
        print(f"Percept Index: {percept_index}")
        print(f"Number of Rectangles: {num_rects}")

    # 匹配对应的框
    rect_match = rect_pattern.search(line)
    if rect_match:
        x1, y1, x2, y2 = map(float, rect_match.groups())
        
        # 获取颜色
        color = percepts_color_mapping.get(percept_index)  # 默认为红色
        
        # 绘制矩形框
        x1, y1, x2, y2 = int(x1), int(y1), int(x2), int(y2)
        print(x1, y1, x2, y2)
        cv2.rectangle(image, (x1, y1), (x2, y2), color, 2)

        font = cv2.FONT_HERSHEY_SIMPLEX
        cv2.putText(image, str(percept_index), (x1, y1 - 10), font, 0.5, color, 1, cv2.LINE_AA)

# 保存包含绘制框的图像
output_image_path = 'output_image.png'
cv2.imwrite(output_image_path, image)

# 保存包含绘制框的图像
output_image_path = 'output_image.png'
cv2.imwrite(output_image_path, image)

在这里插入图片描述

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

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

相关文章

Python中如何快速解析JSON对象数组

嗨喽~大家好呀&#xff0c;这里是魔王呐 ❤ ~! python更多源码/资料/解答/教程等 点击此处跳转文末名片免费获取 由于浏览器可以迅速地解析JSON对象&#xff0c;它们有助于在客户端和服务器之间传输数据。 本文将描述如何使用Python的JSON模块来传输和接收JSON数据。 JavaSc…

优思学院|揭秘六西格玛:七大迷思你不可不知!

六西格玛的核心理念起源于1970年在摩托罗拉公司诞生。其基本精神一直是持续改进和提升品质&#xff0c;随后在各国呈爆炸性的发展。自2000年开始引进中国后&#xff0c;已经过了约16年的应用。但以2017年的角度回顾中国整体六西格玛的应用广度及熟悉度&#xff0c;发现六西格玛…

【ftp篇】 vsftp(ftp) 每天生成一个动态密码

这里写目录标题 前言为什么需要动态每日生成一个密码&#xff1f;编写脚本定时任务java对应的代码 前言 社长最近接到一个需求&#xff0c;需要ftp每天动态生成一个密码 为什么需要动态每日生成一个密码&#xff1f; 在软硬件通讯过程中&#xff0c;就以共享单车为例&#xff0…

Java解析E文件工具类

import lombok.extern.slf4j.Slf4j;import java.io.*; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.List;/*** Description E文件工具类*/ Slf4j public class EFileUtils {/*** 读字符串* param text …

智慧公厕的原理与优势,了解一种更智能的卫生设施

智慧公厕是一种基于现代科技的智能化卫生设施&#xff0c;它的出现给人们的生活带来了巨大的改变和便利。本文将详细介绍智慧公厕的原理和优势&#xff0c;让我们一起了解一种更智能的卫生设施。 智慧公厕的原理主要基于物联网技术。通过将公厕内部各种设备和设施连接到互联网…

Java使用Hutool工具包将汉字转换成汉语拼音

主题&#xff1a;使用Java将汉字转换成拼音 介绍 在Java开发中&#xff0c;有时候我们需要将汉字转换成拼音&#xff0c;以方便进行数据处理、搜索和排序等操作。本文将介绍如何使用Hutool和Pinyin4j这两个Java库来实现汉字转拼音的功能。 依赖库介绍 在开始之前&#xff0c;…

无人直播矩阵系统源码开发------

全自动无人直播系统是一款让商家和企业实现无人直播的系统软件&#xff0c;让商家在门店直播卖货&#xff0c;实现解放双手&#xff0c;无需过多的人工干预。为了满足不同用户的需求&#xff0c;我们推出了OEM功能&#xff0c;让用户可以轻松地将该系统集成到自己的应用程序中。…

软考高项-第九章:项目范围管理

重要知识点&#xff1a; 以上总结&#xff0c;仅供参考。

视频通话中的Camera操作

视频通话也有打开本地摄像头预览的场景&#xff0c;但打开本地Camera预览逻辑&#xff0c;并非在Dailer APP中实现&#xff0c;具体流程图如下。 Dialer app中只调用 1、setCamera用于打开摄像头 相关动作在Ims apk中实现&#xff0c;open函数最后调用了VTSource.java中的doOp…

Python+Pickle/Parquet/HDF5...不同文件格式存储模式下的量化因子计算性能对比

在量化交易中&#xff0c;基于金融市场 L1/L2 报价和交易高频数据进行高频因子计算是一项常见的投研需求。随着金融市场数据量的不断增加&#xff0c;传统的关系数据库已经难以满足大规模数据的存储和查询需求。为了应对这一挑战&#xff0c;一部分用户选择了分布式文件系统&am…

2023年中国在线语言教育行业发展趋势分析:预计2026年在线语言教育市场规模有望实现1182.3亿元[图]

在线语言教育是指在信息化时代的背景下&#xff0c;使用电脑、手机、平板等基于网络的工具在任何地方、任何时间都可以对语言进行学习的一种教育方式&#xff0c;包括校内在线语言教育和校外在线语言教育。在线语言教育是对未来教育模式的探索&#xff0c;以数字化为杠杆&#…

03_学习springdoc与微服务结合_简述

文章目录 1 前言2 基本结构3 网关的配置3.1 ✍️ pom.xml 引入依赖3.2 &#x1f33f; application.yml 的配置3.2.1 Gateway 的配置3.2.2 Eureka Client 的配置3.2.3 Springdoc 的配置 3.3 Springdoc 配置类 4 影片服务 backend-film 的配置4.1 ✍️ pom.xml 引入依赖4.2 &…

Go语句与表达式深度解析:全案例手册

目录 语句1. 声明语句1.1 变量声明1.2 常量声明 2. 赋值语句3. 控制流语句3.1 条件语句if语句switch语句 3.2 循环语句for语句 3.3 跳转语句break语句continue语句return语句goto语句 4. 其他语句4.1 defer语句4.2 go语句 实战案例 表达式介绍、详解、举例1. 基础表达式1.1 字面…

在线免费无时长限制录屏工具 - 录猎在线版

需要录屏的小伙伴注意啦&#xff0c;想要长时间录制又不想花钱的&#xff0c;可以看下这款在线版录屏软件 —— 录猎在线版&#xff0c;一个录屏软件所需要的基本功能它都有&#xff0c;设置录制范围、录制的声音来源、摄像头也能录制的。同时它是支持Windows和Mac系统的&#…

Flink session集群运维

1、集群job manager挂了 kubectl describe pod session-deployment-only-84b8d674c7-ckl9w -n flink kubectl get pod -n flink -owide kubectl describe pod session-deployment-only-84b8d674c7-ms758 -n flink 两个job manager都挂了 准备重新部署集群 删除操作(删除fli…

GTX314L国产替代SI314—低功耗14通道电容触摸传感器芯片

Si314是一款具有自动灵敏度校准功能的14通道电容传感器&#xff0c;其工作电压范围为1.8~5.5V。 Si314设置休眠模式来节省功耗&#xff0c;此时&#xff0c;功耗电流为10uA3.3V。 Si314各个感应通道可实现独立使能、校准、灵敏度调节&#xff0c;可以确保可靠性&#xff0c;且具…

Mysql创建新用户控制权限信息

目录 登录 进入mysql数据库 创建新用户及设置密码 设置用户可远程连接登录 刷新权限 限制新用户只能从特定的主机或IP地址访问MySQL服务器 限制用户只对特定数据库的访问权限 限制用户只能访问特定数据库中的特定表 撤销给用户授予的特定权限 查看用户的权限信息 注 …

力扣:129. 求根节点到叶节点数字之和(Python3)

题目&#xff1a; 给你一个二叉树的根节点 root &#xff0c;树中每个节点都存放有一个 0 到 9 之间的数字。 每条从根节点到叶节点的路径都代表一个数字&#xff1a; 例如&#xff0c;从根节点到叶节点的路径 1 -> 2 -> 3 表示数字 123 。 计算从根节点到叶节点生成的 所…

Android攻城狮学鸿蒙-配置

1、config.json配置 鸿蒙中的config.json应该类似于Android开发中Manifest.xml&#xff0c;可以进行页面的配置。根据顺序&#xff0c;会识别启动应用的时候&#xff0c;要打开哪个界面。 2、 Ability详解&#xff0c;以及与Android的Activity对比。 他人的学习文章连接&…

【运维日常】运维必备的 免费 在线画图工具,真的很好用!

本站以分享各种运维经验和运维所需要的技能为主 《python零基础入门》&#xff1a;python零基础入门学习 《python运维脚本》&#xff1a; python运维脚本实践 《shell》&#xff1a;shell学习 《terraform》持续更新中&#xff1a;terraform_Aws学习零基础入门到最佳实战 《k8…