利用libyuv实现I420、NV12、NV21的resize

使用libyuv进行I420、NV12、NV21图像resize操作
本文介绍了如何利用libyuv库进行I420、NV12和NV21格式的图像resize。详细讲述了自定义结构过程,包括I420的简单resize以及NV12和NV21的resize方法,通过分离UV、resize UV后再合并来实现。并附带了resize前后的图片对比。
部署运行你感兴趣的模型镜像

首先自定义定义的结构

class Scaler {
  struct Buffer {
    uint32_t width, height;
    uint8_t *data[3];
    uint32_t stride[3];
    ColorFormat color;
  };
  enum ColorFormat {
    YUV_I420 = 0,
    YUV_NV21,
    YUV_NV12,
    BGR ,
    RGB ,
    BGRA,
    RGBA,
    ABGR,
    ARGB,
  };
void Scaler::LibyuvResize_NV21(const Buffer *src,
                             Buffer *dst); 
void Scaler::LibyuvResize_I420(const Buffer *src,
                               Buffer *dst);
};

1、I420的resize

这个最简单,libyuv中提供现成的接口

void Scaler::LibyuvResize_I420(const Buffer *src,
                               Buffer *dst) {
  // you should make complete info of buffer src, stride data width height 
  dst->stride[0] = dst->width;
  dst->stride[1] = dst->width >> 1;
  dst->stride[2] = dst->width >> 1;
  uint32_t dst_y_size = dst->width * dst->height;
  uint32_t dst_u_size = dst->stride[1] * (dst->height >> 1);
  //uint32_t dst_v_size = dst->stride[2] * (dst->height >> 1);
  dst->data[1] = dst->data[0] + dst_y_size;
  dst->data[2] = dst->data[1] + dst_u_size; 
  dst->color = Scaler::ColorFormat::YUV_I420;  

  libyuv::I420Scale(src->data[0], src->stride[0],
                    src->data[1], src->stride[1],
                    src->data[2], src->stride[2],
                    src->width, src->height,
                    dst->data[0], dst->stride[0],
                    dst->data[1],dst->stride[1],
                    dst->data[2],dst->stride[2],
                    dst->width, dst->height,
                    libyuv::FilterMode::kFilterNone);
}

结果

原图
resize结果

NV12或NV12的resize

libyuv中没有直接提供nv21或者nv12的resize,这里我进行了简单的分离uv, resize uv,最后再merge resize_u_v
NV21和NV12相似只提供一种

void Scaler::LibyuvResize_NV21(const Buffer *src,
                             Buffer *dst) {
  uint32_t src_halfheight = SUBSAMPLE(src->height, 1, 1);
  uint32_t src_halfwidth = SUBSAMPLE(src->width, 1, 1);
  dst->stride[0] = dst->width;
  dst->stride[1] = dst->width;
  dst->stride[2] = 0;
  dst->color = src->color;
  // resize y_plane
  libyuv::ScalePlane(src->data[0], src->stride[0], 
                        src->width, src->height, 
                        dst->data[0], dst->stride[0],
                        dst->width, dst->height,
                        libyuv::FilterMode::kFilterNone);
  // Split VUplane
  uint8_t* uv_data = new uint8_t[src->width * src->height / 2];
  uint8_t* v_data = uv_data;
  uint8_t* u_data = uv_data + src->width * src->height / 4;
  //malloc memory to store temp u v 
  uint8_t* temp_uv_data = new uint8_t[dst->width * dst->height / 2];
  uint8_t* temp_v_data = temp_uv_data;
  uint8_t* temp_u_data = temp_uv_data + dst->width * dst->height / 4;
  libyuv::SplitUVPlane(src->data[1], src->stride[1],
                       v_data, src->stride[1] >> 1,
                       u_data, src->stride[1] >> 1,
                       src_halfwidth, src_halfheight);
  // resize u and v
  libyuv::ScalePlane(u_data, src->stride[1] >> 1,
                    src->width >> 1, src_halfheight,
                    temp_u_data, dst->stride[1] >> 1,
                    dst->width >> 1, dst->height >> 1,
                    libyuv::FilterMode::kFilterNone);

  libyuv::ScalePlane(v_data, src->stride[1] >> 1,
                    src->width >> 1, src_halfheight,
                    temp_v_data, dst->stride[1] >> 1,
                    dst->width >> 1, dst->height >> 1,
                    libyuv::FilterMode::kFilterNone);
                    
  libyuv::MergeUVPlane(temp_v_data, dst->stride[1] >> 1,
                       temp_u_data, dst->stride[1] >> 1, 
                       dst->data[1], dst->stride[1],
                       dst->width >> 1, dst->height >> 1);
  delete[] uv_data;
  u_data = v_data = uv_data = nullptr;
  delete[] temp_uv_data;
  temp_u_data = temp_v_data = temp_uv_data = nullptr;
}

NV resize结果

原图

resize结果

您可能感兴趣的与本文相关的镜像

Linly-Talker

Linly-Talker

AI应用

Linly-Talker是一款创新的数字人对话系统,它融合了最新的人工智能技术,包括大型语言模型(LLM)、自动语音识别(ASR)、文本到语音转换(TTS)和语音克隆技术

### 使用 OpenCL 实现 NV12 图像格式缩放 对于使用 OpenCL 来处理 NV12 格式的图像并执行缩放操作,通常涉及以下几个方面: #### 1. 准备工作 确保环境配置正确,安装必要的库文件以及驱动程序。特别是要确认已经成功启用了 OpenCL 支持。 ```bash pip install opencv-python-headless==4.5.5.62 pyopencl numpy ``` 这段命令用于安装特定版本的 `opencv-python` 和其他依赖项[^1]。 #### 2. 加载 NV12 数据到内存中 由于 NV12 是一种特殊的 YUV 编码方式,因此需要先读取原始数据流,并将其转换成适合 GPU 处理的形式。 ```python import numpy as np import cv2 def load_nv12_data(file_path, width, height): with open(file_path, 'rb') as f: data = np.frombuffer(f.read(), dtype=np.uint8) y_size = width * height uv_size = (width // 2) * (height // 2) # 将Y分量和U/V交织部分分离出来 y_channel = data[:y_size].reshape((height, width)) u_v_interleaved = data[y_size:y_size + 2*uv_size] return y_channel, u_v_interleaved.reshape((-1, 2)).view(np.dtype([('u', '<u1'), ('v', '<u1')])) ``` 此函数负责加载 NV12 文件中的亮度(Y)和平面 UV 组件。 #### 3. 创建 OpenCL 上下文与队列 设置好计算设备之后就可以创建上下文对象来管理资源分配了。 ```python import pyopencl as cl context = cl.create_some_context() queue = cl.CommandQueue(context) ``` 上述代码片段展示了如何初始化一个简单的 OpenCL 运行时环境。 #### 4. 编写内核代码进行重采样 编写一段专门针对 NV12 的 OpenCL C 内核来进行双线性插值或其他形式的重采样算法。 ```c __kernel void resize_nv12( __read_only image2d_t src_y, __write_only image2d_t dst_y, float scale_x, float scale_y ){ int x = get_global_id(0); int y = get_global_id(1); if(x >= get_image_width(dst_y) || y >= get_image_height(dst_y)) return; const sampler_t sampler = CLK_NORMALIZED_COORDS_FALSE | CLK_ADDRESS_CLAMP_TO_EDGE | CLK_FILTER_LINEAR; float2 pos = (float2)(x / scale_x, y / scale_y); float val = read_imagef(src_y, sampler, pos).x; write_imagef(dst_y, (int2)(x,y), (float4)(val,val,val,1)); } ``` 该内核实现了基于双线性插值法对 Y 分量图层实施缩小/放大变换的功能。 请注意这只是一个简化版的例子,在实际应用当中还需要考虑 U,V 分量以及其他边界条件等问题。 #### 5. 执行内核调用完成最终处理过程 最后一步就是把之前准备好的参数传递给编译后的内核实例化体,并提交任务至指定平台上的处理器上运行。 ```python program = cl.Program(context, kernel_code).build() src_y_buf = cl.image_from_array(context, y_channel.astype(np.float32), mode='r') dst_shape = tuple(int(s*scale_factor) for s in y_channel.shape[::-1]) dst_y_buf = cl.Image(context, mem_flags=cl.mem_flags.WRITE_ONLY, format=cl.ImageFormat(cl.channel_order.INTENSITY, cl.channel_type.FLOAT), shape=dst_shape) global_work_size = list(reversed(dst_shape)) event = program.resize_nv12(queue, global_work_size, None, src_y_buf, dst_y_buf, np.float32(scale_factor), np.float32(scale_factor)) event.wait() result = np.empty(shape=(dst_shape[1], dst_shape[0]), dtype=np.float32) cl.enqueue_copy(queue, result, dst_y_buf, origin=(0, 0), region=list(reversed(result.shape))) ``` 通过这种方式可以在支持 OpenCL 的硬件加速器上来高效地调整 NV12 像素阵列大小。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值