带你走进程序员眼中的“像素”世界,深入理解图显系统中的Rgb与Yuv

系统
对所有人而言,像素这个词并不陌生。各大手机厂商新品发布会时高频出现的一个词就是xxx手机像素多少多少,分辨率多高多高。那么这里说的手机像素是什么含义呢?

 [[426582]]

对所有人而言,像素这个词并不陌生。各大手机厂商新品发布会时高频出现的一个词就是xxx手机像素多少多少,分辨率多高多高。那么这里说的手机像素是什么含义呢?

以500万像素为例,其含义是在一英寸的CCD面积上有500万个象素点,500万像素的分辨率是2592*1944或者2560*1920。

[[426583]]

一块积木就好比是一个像素点

针对像素格式,介绍和分析其在图显系统中的实现原理和细节。

像素格式基本概念

像素格式描述了像素数据存储所用的格式,定义了像素在内存中的编码方式。

像素表示方式

下图标识了不同应用场景下所看到的像素格式的不同表示方式,虽然说像素格式不同但他们都是为了同一个目的:呈现色彩空间。

色彩空间的不同表示方式

bpp

bpp这个参数在代码中经常可见。例如libdrm代码中申请创建framebuffer的函数,bpp作为创建framebuffer的一个入参。

  1. static struct bo * 
  2. bo_create_dumb(int fd, unsigned int width, unsigned int height, unsigned int bpp) 
  3. ... 
  4.  arg.bpp = bpp; 
  5.  arg.width = width; 
  6.  arg.height = height; 
  7.  
  8.  ret = drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &arg); 
  9. ... 
  10.  
  11.  return bo; 

bpp:bits per pixel,简称像素深度。它的含义是每个像素占用的二进制bit位数,例如在fourcc定义的像素格式会标明其所占的bit数。

  1. /* 8 bpp RGB */ 
  2. #define DRM_FORMAT_RGB332 fourcc_code('R''G''B''8'
  3. /* 16 bpp RGB */ 
  4. #define DRM_FORMAT_XRGB4444 fourcc_code('X''R''1''2')  
  5. /* 24 bpp RGB */ 
  6. #define DRM_FORMAT_RGB888 fourcc_code('R''G''2''4')  
  7. /* 32 bpp RGB */ 
  8. #define DRM_FORMAT_XRGB8888 fourcc_code('X''R''2''4')  

stride

在内核代码中也成为pitch,确切的说,stride是framebuffer的概念,它指定了每行像素所占的内存空间大小,出于内存对齐的考虑,通常stride>=bppN。假如使用libdrm,那么在应用层填充像素数据时是不考虑对齐的。stride==bppN。

但在驱动代码中会对stride做对齐处理,例如:

  1. int 
  2. i915_gem_dumb_create(struct drm_file *file, 
  3.        struct drm_device *dev, 
  4.        struct drm_mode_create_dumb *args) 
  5.  int cpp = DIV_ROUND_UP(args->bpp, 8); 
  6. ... 
  7.  /* have to work out size/pitch and return them */ 
  8.  args->pitch = ALIGN(args->width * cpp, 64); 
  9. ... 
  10.  return i915_gem_create(file, to_i915(dev), 
  11.           &args->size, &args->handle); 

创建好的framebuffer如下图所示:

亮度

亮度在图显系统中称之为luminance。YUV格式的像素Y分量变和此相关。当亮度最低时,图片为黑色;亮度最高时,图片为白色。对于bpp小于32的LCD屏幕而言,亮度是通过PWM来调节背光来实现的。

最暗和最亮

RGB

RGB即是代表红、绿、蓝三个通道的颜色,通过三者的不同程度的叠加,构造出人类视力所能感知的所有颜色,是运用最广的颜色系统之一。RGB按照色深分为1位、8位、16位、24位、32位。

根据RGB的数据格式特点可以构造图像数据,构造好的数据填充到DRM framebuffer,由图显处理器取出发送到显示设备。代码实现如下。

构造RGB16图像代码:

  1. static void fill_tiles_rgb16(const struct util_format_info *info, void *mem, 
  2.         unsigned int width, unsigned int height, 
  3.         unsigned int stride) 
  4.  const struct util_rgb_info *rgb = &info->rgb; 
  5.  void *mem_base = mem; 
  6.  unsigned int x, y; 
  7.  
  8.  for (y = 0; y < height; ++y) { 
  9.   for (x = 0; x < width; ++x) { 
  10.    div_t d = div(x+y, width); 
  11.    uint32_t rgb32 = 0x00130502 * (d.quot >> 6) 
  12.            + 0x000a1120 * (d.rem >> 6); 
  13.    uint16_t color = 
  14.     MAKE_RGBA(rgb, (rgb32 >> 16) & 0xff, 
  15.        (rgb32 >> 8) & 0xff, rgb32 & 0xff, 
  16.        255); 
  17.  
  18.    ((uint16_t *)mem)[x] = color; 
  19.   } 
  20.   mem += stride; 
  21.  } 
  22.  
  23.  make_pwetty(mem_base, width, height, stride, info->format); 

构造RGB24图像代码:

  1. static void fill_tiles_rgb24(const struct util_format_info *info, void *mem, 
  2.         unsigned int width, unsigned int height, 
  3.         unsigned int stride) 
  4.  const struct util_rgb_info *rgb = &info->rgb; 
  5.  unsigned int x, y; 
  6.  
  7.  for (y = 0; y < height; ++y) { 
  8.   for (x = 0; x < width; ++x) { 
  9.    div_t d = div(x+y, width); 
  10.    uint32_t rgb32 = 0x00130502 * (d.quot >> 6) 
  11.            + 0x000a1120 * (d.rem >> 6); 
  12.    struct color_rgb24 color = 
  13.     MAKE_RGB24(rgb, (rgb32 >> 16) & 0xff, 
  14.         (rgb32 >> 8) & 0xff, rgb32 & 0xff); 
  15.  
  16.    ((struct color_rgb24 *)mem)[x] = color; 
  17.   } 
  18.   mem += stride; 
  19.  } 

构造RGB32图像代码:

  1. static void fill_tiles_rgb32(const struct util_format_info *info, void *mem, 
  2.         unsigned int width, unsigned int height, 
  3.         unsigned int stride) 
  4.  const struct util_rgb_info *rgb = &info->rgb; 
  5.  void *mem_base = mem; 
  6.  unsigned int x, y; 
  7.  
  8.  for (y = 0; y < height; ++y) { 
  9.   for (x = 0; x < width; ++x) { 
  10.    div_t d = div(x+y, width); 
  11.    uint32_t rgb32 = 0x00130502 * (d.quot >> 6) 
  12.            + 0x000a1120 * (d.rem >> 6); 
  13.    uint32_t alpha = ((y < height/2) && (x < width/2)) ? 127 : 255; 
  14.    uint32_t color = 
  15.     MAKE_RGBA(rgb, (rgb32 >> 16) & 0xff, 
  16.        (rgb32 >> 8) & 0xff, rgb32 & 0xff, 
  17.        alpha); 
  18.    ((uint32_t *)mem)[x] = color; 
  19.   } 
  20.   mem += stride; 
  21.  } 
  22.  
  23.  make_pwetty(mem_base, width, height, stride, info->format); 

YUV

YUV表示了色彩空间中的亮度和色度,主要应用于视频传输、显示。其中Y表示亮度(Luminance、Luma),U和V表示色度(Chrominance、Chroma)。另外,在视频压缩、传输过程中还会看到YCbCr的概念。其实,二者从本质上基本是一样的。YUV的优点之一是色度通道比Y通道具有更低的采样率,而不会显著降低感知质量。

YUV采样

各个视频设备厂商或编码芯片厂商定义了多种A:B:C格式的YUV采用规则。

4:4:4

表示没有对色度通道进行降采样,其中圆圈代表Y,X代表UV,以下类同。

4:2:2

表示2:1水平下采样,没有垂直下采样。每两条扫描线包含四个Y样本,对应两个U或V样本。

4:2:0

表示水平下采样2:1,垂直下采样2:1。

YUV存储

YUV存储分为packed格式和planar格式。

  • packed格式

将Y、U和V组件打包存储在一个阵列中。像素被组织成一组宏像素,其布局取决于格式。如YUYV,即1个plane。

  • planar格式

平面格式是将Y、U、V三个分量分别存储为三个独立的平面。如I420、YV12,即3个plane。另外,还可以支持semi-planar。Y连续存储,U、V交叉存储,如NV21、NV12,即2个plane。

对于不同的YUV表示形式,它们在内存中的排布式不同的。

  • YUV444存储格式

  • YUV422存储格式

构造YUV图像

构造YUV packed图像代码如下:

  1. static void fill_tiles_yuv_packed(const struct util_format_info *info, 
  2.       void *mem, unsigned int width, 
  3.       unsigned int height, unsigned int stride) 
  4.  const struct util_yuv_info *yuv = &info->yuv; 
  5.  unsigned char *y_mem = (yuv->order & YUV_YC) ? mem : mem + 1; 
  6.  unsigned char *c_mem = (yuv->order & YUV_CY) ? mem : mem + 1; 
  7.  unsigned int u = (yuv->order & YUV_YCrCb) ? 2 : 0; 
  8.  unsigned int v = (yuv->order & YUV_YCbCr) ? 2 : 0; 
  9.  unsigned int x; 
  10.  unsigned int y; 
  11.  
  12.  for (y = 0; y < height; ++y) { 
  13.   for (x = 0; x < width; x += 2) { 
  14.    div_t d = div(x+y, width); 
  15.    uint32_t rgb32 = 0x00130502 * (d.quot >> 6) 
  16.            + 0x000a1120 * (d.rem >> 6); 
  17.    struct color_yuv color = 
  18.     MAKE_YUV_601((rgb32 >> 16) & 0xff, 
  19.           (rgb32 >> 8) & 0xff, rgb32 & 0xff); 
  20.  
  21.    y_mem[2*x] = color.y; 
  22.    c_mem[2*x+u] = color.u; 
  23.    y_mem[2*x+2] = color.y; 
  24.    c_mem[2*x+v] = color.v; 
  25.   } 
  26.  
  27.   y_mem += stride; 
  28.   c_mem += stride; 
  29.  } 

构造YUV planar图像代码如下:

  1. static void fill_tiles_yuv_planar(const struct util_format_info *info, 
  2.       unsigned char *y_mem, unsigned char *u_mem, 
  3.       unsigned char *v_mem, unsigned int width, 
  4.       unsigned int height, unsigned int stride) 
  5.  const struct util_yuv_info *yuv = &info->yuv; 
  6.  unsigned int cs = yuv->chroma_stride; 
  7.  unsigned int xsub = yuv->xsub; 
  8.  unsigned int ysub = yuv->ysub; 
  9.  unsigned int x; 
  10.  unsigned int y; 
  11.  
  12.  for (y = 0; y < height; ++y) { 
  13.   for (x = 0; x < width; ++x) { 
  14.    div_t d = div(x+y, width); 
  15.    uint32_t rgb32 = 0x00130502 * (d.quot >> 6) 
  16.            + 0x000a1120 * (d.rem >> 6); 
  17.    struct color_yuv color = 
  18.     MAKE_YUV_601((rgb32 >> 16) & 0xff, 
  19.           (rgb32 >> 8) & 0xff, rgb32 & 0xff); 
  20.  
  21.    y_mem[x] = color.y; 
  22.    u_mem[x/xsub*cs] = color.u; 
  23.    v_mem[x/xsub*cs] = color.v; 
  24.   } 
  25.  
  26.   y_mem += stride; 
  27.   if ((y + 1) % ysub == 0) { 
  28.    u_mem += stride * cs / xsub; 
  29.    v_mem += stride * cs / xsub; 
  30.   } 
  31.  } 

本文转载自微信公众号「Linux与SoC」,可以通过以下二维码关注。转载本文请联系Linux与SoC公众号。

 

 

责任编辑:武晓燕 来源: Linux与SoC
相关推荐

2018-10-26 15:30:49

程序员MySQL数据库

2019-11-11 09:02:51

MySQL数据库索引

2015-04-08 11:09:28

优秀程序员深入理解你的代码

2024-02-26 08:25:00

C++编程

2011-05-13 14:34:02

程序员

2023-11-21 21:47:31

2009-02-13 09:45:27

程序员JavaPHP

2021-04-25 10:45:59

Docker架构Job

2015-08-05 15:46:36

代码程序员

2017-11-20 11:05:23

数据库MongoDB索引

2016-05-10 10:28:35

2020-10-25 15:27:30

程序员技术网络

2022-11-09 08:12:07

2010-09-14 10:15:24

2018-01-17 16:18:06

2015-09-30 10:04:09

2015-06-17 14:24:48

优秀程序员整洁代码

2019-07-24 08:49:36

Docker容器镜像

2018-01-22 17:02:48

Python字符编码ASCII

2013-06-14 11:16:14

点赞
收藏

51CTO技术栈公众号