• 【图像处理】使用SDL预览webp图片


    写在前面的话

    WebP是Google开发的一种图像格式,支持图像数据的有损和无损压缩。保留动画和alpha透明通道数据。

    可以创建和JPEG、PNG和GIF图像格式在质量相同或质量更高,但是数据量更小的一种图像格式。

    如下简单的分析一下webp图像格式,并使用sdl显示图片。

    webp项目地址:https://github.com/webmproject/libwebp

    sdl项目地址:https://libsdl.org/

    webp格式图片说明

    webp格式图像数据由两部分组成,RIFF头和图像负载信息。

     0                   1                   2                   3
     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    |      'R'      |      'I'      |      'F'      |      'F'      |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    |                           File Size                           |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    |      'W'      |      'E'      |      'B'      |      'P'      |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

    RIFF头信息由21个字节组成。

    0-3 四个字节是 RIFF 四个字符,表示 资源交换格式Resource Interchange File Format的简写。

    4-7 四个字节是 WEBP文件的全部长度,这个长度包含RIFF

    8-11 四个字节是 资源交换格式的名称,填WEBP这四个字符

    12-15 四个字节是数据块Chunk的负载信息的编码格式,取值有VP8表示无损vp8压缩,VP8L表示有损vp8压缩

    16-19 四个字节是有损压缩时的VP8数据负载信息的长度

    20-以后数vp8格式的图像数据帧。

    VP8格式的定义如下

    struct VP8Io {
      // set by VP8GetHeaders()
      int width, height;         // picture dimensions, in pixels (invariable).
                                 // These are the original, uncropped dimensions.
                                 // The actual area passed to put() is stored
                                 // in mb_w / mb_h fields.
    
      // set before calling put()
      int mb_y;                  // position of the current rows (in pixels)
      int mb_w;                  // number of columns in the sample
      int mb_h;                  // number of rows in the sample
      const uint8_t* y, *u, *v;  // rows to copy (in yuv420 format)
      int y_stride;              // row stride for luma
      int uv_stride;             // row stride for chroma
      const uint8_t* a;
    };

    VP8使用14位表示图像的宽和高,因此webp格式图像宽高最大是2^14 = 16384像素。

    VP8的每一个像素的值是通过 左上top-left 上top 右上top-right 和左left这4个像素做预测得到。

    预测的方法如下

      // L = left pixel, T = top pixel, TL = top left pixel.
    
      // ARGB component estimates for prediction.
      int pAlpha = ALPHA(L) + ALPHA(T) - ALPHA(TL);
      int pRed = RED(L) + RED(T) - RED(TL);
      int pGreen = GREEN(L) + GREEN(T) - GREEN(TL);
      int pBlue = BLUE(L) + BLUE(T) - BLUE(TL);
    
      // Manhattan distances to estimates for left and top pixels.
      int pL = abs(pAlpha - ALPHA(L)) + abs(pRed - RED(L)) +
               abs(pGreen - GREEN(L)) + abs(pBlue - BLUE(L));
      int pT = abs(pAlpha - ALPHA(T)) + abs(pRed - RED(T)) +
               abs(pGreen - GREEN(T)) + abs(pBlue - BLUE(T));

    VP8的详细分析,后面增加一篇对应的文章分析。

    环境准备

    centos环境可以先安装png tiff jpeg和sdl

    yum install libjpeg-devel
    yum install libpng-devel
    yum install libtiff-devel

    osx系统可以使用brew命令按照对应的包

    使用cmake编译webp项目后会生成如下小工具

    cwebp 工具可以将其他格式图片转成webp图片

    ./cwebp -h
    Usage:
    
       cwebp [options] -q quality input.png -o output.webp
    
    where quality is between 0 (poor) to 100 (very good).
    Typical value is around 80.

    dwebp 工具可以将webp图片转成png jpg tiff ppm等格式

    ./dwebp -h
    Usage: dwebp in_file [options] [-o out_file]
    
      -pam ......... save the raw RGBA samples as a color PAM
      -ppm ......... save the raw RGB samples as a color PPM
      -bmp ......... save as uncompressed BMP format
      -tiff ........ save as uncompressed TIFF format
      -pgm ......... save the raw YUV samples as a grayscale PGM
                     file with IMC4 layout
      -yuv ......... save the raw YUV samples in flat layout

    vwebp_sdl 工具可以通过sdl显示webp图片

    做SDL显示的一个例子

    首先找一张png的图片,将这个图片转成webp格式的图像。

    ./cwebp leopard2.png -o  leopard2.webp
    
    Saving file 'leopard2.webp'
    File:      leopard2.png
    Dimension: 842 x 1134
    Output:    59610 bytes Y-U-V-All-PSNR 40.28 44.99 46.35   41.45 dB
               (0.50 bpp)
    block count:  intra4:       2898  (77.01%)
                  intra16:       865  (22.99%)
                  skipped:         1  (0.03%)
    bytes used:  header:            203  (0.3%)
                 mode-partition:  13872  (23.3%)
     Residuals bytes  |segment 1|segment 2|segment 3|segment 4|  total
        macroblocks:  |      12%|      37%|      10%|      42%|    3763
          quantizer:  |      36 |      32 |      27 |      16 |
       filter level:  |      11 |       8 |      12 |      15 |

    如下是png格式图片是webp格式图片大小的3.4倍,png格式的图像大小是1.4M,webp格式图像的大小是58k

    1.4M  9  8 16:29 leopard2.png
    58K  9  8 16:30 leopard2.webp

    显示webp图片

    ./vwebp_sdl leopard2.webp

    SDL图像显示原理

    SDL库做图像显示时,渲染表面接收RGB或者YUV格式的图像数据。

    因此,若显示webp格式的图像,需要将webp格式的图像转成RGB或者YUV格式,再将图像数据传递给SDL的显示表面实现显示效果。

    注:其他格式的图片也是同样的做法,其他格式转成RGB或者YUV格式数据交给SDL显示表面。

    显示逻辑如下:

     结合SDL显示图像的原理如下:

    SDL显示webp的主要代码

    int WebpToSDL(const char* data, unsigned int data_size) {
      int ok = 0;
      // 第一步 声明webp的配置和属性
      VP8StatusCode status;
      WebPDecoderConfig config;
      WebPBitstreamFeatures* const input = &config.input;
      WebPDecBuffer* const output = &config.output;
    
      SDL_Surface* screen = NULL;
      SDL_Surface* surface = NULL;
      // 第二步 初始化webp解码配置信息
      if (!WebPInitDecoderConfig(&config)) {
        fprintf(stderr, "Library version mismatch!
    ");
        return 0;
      }
    
      if (!init_ok) {
        SDL_Init(SDL_INIT_VIDEO);
        init_ok = 1;
      }
      // 第三步 获取webp图像数据
      status = WebPGetFeatures((uint8_t*)data, (size_t)data_size, &config.input);
      if (status != VP8_STATUS_OK) goto Error;
    
      screen = SDL_SetVideoMode(input->width, input->height, 32, SDL_SWSURFACE);
      if (screen == NULL) {
        fprintf(stderr, "Unable to set video mode (32bpp %dx%d)!
    ",
                input->width, input->height);
        goto Error;
      }
    
      surface = SDL_CreateRGBSurface(SDL_SWSURFACE,
                                     input->width, input->height, 32,
                                     0x000000ffu,   // R mask
                                     0x0000ff00u,   // G mask
                                     0x00ff0000u,   // B mask
                                     0xff000000u);  // A mask
    
      if (surface == NULL) {
        fprintf(stderr, "Unable to create %dx%d RGBA surface!
    ",
                input->width, input->height);
        goto Error;
      }
      if (SDL_MUSTLOCK(surface)) SDL_LockSurface(surface);
    
    #if SDL_BYTEORDER == SDL_BIG_ENDIAN
      output->colorspace = MODE_BGRA;
    #else
      output->colorspace = MODE_RGBA;
    #endif
      output->width  = surface->w;
      output->height = surface->h;
      output->u.RGBA.rgba   = surface->pixels;
      output->u.RGBA.stride = surface->pitch;
      output->u.RGBA.size   = surface->pitch * surface->h;
      output->is_external_memory = 1;
      // 第四步 解码webp格式成rgb格式的图像数据
      status = WebPDecode((const uint8_t*)data, (size_t)data_size, &config);
      if (status != VP8_STATUS_OK) {
        fprintf(stderr, "Error decoding image (%d)
    ", status);
        goto Error;
      }
    
      if (SDL_MUSTLOCK(surface)) SDL_UnlockSurface(surface);
      // 第五步 将rgb的图像数据显示到SDL的目标表面,实现显示
      if (SDL_BlitSurface(surface, NULL, screen, NULL) ||
          SDL_Flip(screen)) {
        goto Error;
      }
    
      ok = 1;
      
     Error:
      SDL_FreeSurface(surface);
      SDL_FreeSurface(screen);
      WebPFreeDecBuffer(output);
      return ok;
    }

    参考材料:

    https://developers.google.cn/speed/webp/docs/riff_container

    done.

    祝玩的开心~

  • 相关阅读:
    Scars To Your Beautiful
    【BZOJ1833】【ZJOI2010】数字计数(数位DP)
    滑块拼图
    神经元(prufer序列+dp)
    sequence(dp+hash+二分搜索+前缀和优化)
    循环移位(后缀自动机)
    MST(最小生成树+倍增)
    nyoj 845【水】
    分割问题【知识点】
    扩展欧几里得【知识点】
  • 原文地址:https://www.cnblogs.com/voipman/p/15244037.html
Copyright © 2020-2023  润新知