【问题标题】:Fastest dithering / halftoning library in CC中最快的抖动/半色调库
【发布时间】:2010-12-02 00:45:45
【问题描述】:

我正在开发一个定制的瘦客户端服务器,为它的客户端提供渲染的网页。服务器运行在多核 Linux 机器上,Webkit 提供 html 渲染引擎。

唯一的问题是客户端显示受限于 4 位(16 色)灰度调色板。我目前正在使用 LibGraphicsMagick 抖动图像(RGB-> 4 位灰度),这显然是服务器性能的瓶颈。分析表明,超过 70% 的时间用于运行 GraphicsMagick 抖动函数。

我已经探索了 stackoverflow 和 Interwebs 以获得良好的高性能解决方案,但似乎没有人对各种图像处理库和抖动解决方案进行任何基准测试。

我会更高兴发现:

  1. 在抖动/半色调/将 RGB 图像量化为 4 位灰度方面性能最高的库是什么。
  2. 是否有任何特殊的抖动库或任何公共域代码 sn-ps 可供您指出?
  3. 就高性能而言,您更喜欢使用哪些库来处理图形?

首选 C 语言库。

【问题讨论】:

    标签: c image-processing quantization dithering


    【解决方案1】:

    抖动会花费相当多的时间,具体取决于所选的算法。

    实现Bayer (Matrix)Floyd-Steinberg (Diffusion) dithering 相当简单。

    使用 MMX/SSE 进行编码以处理并行像素时,Bayer 过滤可以变得非常快。您还可以使用 GPU 着色器进行抖动/转换。

    FWIW,您已经在使用 GraphicsMagick,但有完整的 OSS 图形库列表here

    【讨论】:

    【解决方案2】:

    我知道它不是一个 C 库,但这让我很好奇 .NET 可以使用什么来进行错误扩散,我大约 20 年前在一个项目中使用了这种方法。我找到了this,特别是这个method

    但为了尝试有所帮助:) 我发现了这个C library

    【讨论】:

    • “C 库”链接当前已损坏。
    【解决方案3】:

    从 Adisak 提供的列表中,未经任何测试,我会赌AfterImage。 Afterstep 人痴迷于速度,还描述了一个聪明的算法。

    如果您的服务器可以配备一个不错的支持 OpenGL 的 PCI-express 显卡,您可以采用另一种方法。 Here 是来自 Nvidia 的一些规格。搜索“索引模式”。您可以做的是选择 16 或 256 色显示模式,将图像渲染为平面多边形(如立方体的一侧)上的纹理,然后读取帧。

    当从 OpenGL 卡读取帧时,重要的是来自卡的带宽良好,因此需要 PCI-express。正如文档所述,您还必须在索引模式下选择颜色以获得良好的效果。

    【讨论】:

    • @Spike0xff,实际上,它甚至不需要是通用 GPU(可编程),它可以用于任何适用于 OpenGL 或 DirectX 的东西。
    【解决方案4】:

    这是用于半色调的 Floyd-Steinberg 方法的实现:

    #include <opencv2/opencv.hpp>
    
    using namespace cv;
    
    int main(){
    
    uchar scale = 128; // change this parameter to 8, 32, 64, 128 to change the dot size
    Mat img = imread("../halftone/tom.jpg", CV_LOAD_IMAGE_GRAYSCALE);
    for (int r=1; r<img.rows-1; r++) {
        for (int c=1; c<img.cols-1; c++) {
            uchar oldPixel = img.at<uchar>(r,c);
            uchar newPixel = oldPixel / scale * scale;
            img.at<uchar>(r,c) = newPixel;
            int quantError = oldPixel - newPixel;
            img.at<uchar>(r+1,c)   +=  7./16. * quantError;
            img.at<uchar>(r-1,c+1) +=  3./16. * quantError;
            img.at<uchar>(r,c+1) +=  5./16. * quantError;
            img.at<uchar>(r+1,c+1) +=  1./16. * quantError;
        }
    }
    imshow("halftone", img);
    waitKey();
    }
    

    【讨论】:

      【解决方案5】:

      这是您正在寻找的解决方案。 这是一个使用颜色参数执行有序抖动 (Bayer) 的 C 函数。 它的速度足以用于实时处理。

      #ifndef MIN
      #define MIN(a,b)            (((a) < (b)) ? (a) : (b))
      #endif
      
      #ifndef MAX
      #define MAX(a,b)            (((a) > (b)) ? (a) : (b))
      #endif
      
      #ifndef CLAMP
      //  This produces faster code without jumps
      #define     CLAMP( x, xmin, xmax )      (x) = MAX( (xmin), (x) );   \
                                              (x) = MIN( (xmax), (x) )
      #define     CLAMPED( x, xmin, xmax )    MAX( (xmin), MIN( (xmax), (x) ) )
      #endif
      
      const   int BAYER_PATTERN_16X16[16][16] =   {   //  16x16 Bayer Dithering Matrix.  Color levels: 256
                                                      {     0, 191,  48, 239,  12, 203,  60, 251,   3, 194,  51, 242,  15, 206,  63, 254  }, 
                                                      {   127,  64, 175, 112, 139,  76, 187, 124, 130,  67, 178, 115, 142,  79, 190, 127  },
                                                      {    32, 223,  16, 207,  44, 235,  28, 219,  35, 226,  19, 210,  47, 238,  31, 222  },
                                                      {   159,  96, 143,  80, 171, 108, 155,  92, 162,  99, 146,  83, 174, 111, 158,  95  },
                                                      {     8, 199,  56, 247,   4, 195,  52, 243,  11, 202,  59, 250,   7, 198,  55, 246  },
                                                      {   135,  72, 183, 120, 131,  68, 179, 116, 138,  75, 186, 123, 134,  71, 182, 119  },
                                                      {    40, 231,  24, 215,  36, 227,  20, 211,  43, 234,  27, 218,  39, 230,  23, 214  },
                                                      {   167, 104, 151,  88, 163, 100, 147,  84, 170, 107, 154,  91, 166, 103, 150,  87  },
                                                      {     2, 193,  50, 241,  14, 205,  62, 253,   1, 192,  49, 240,  13, 204,  61, 252  },
                                                      {   129,  66, 177, 114, 141,  78, 189, 126, 128,  65, 176, 113, 140,  77, 188, 125  },
                                                      {    34, 225,  18, 209,  46, 237,  30, 221,  33, 224,  17, 208,  45, 236,  29, 220  },
                                                      {   161,  98, 145,  82, 173, 110, 157,  94, 160,  97, 144,  81, 172, 109, 156,  93  },
                                                      {    10, 201,  58, 249,   6, 197,  54, 245,   9, 200,  57, 248,   5, 196,  53, 244  },
                                                      {   137,  74, 185, 122, 133,  70, 181, 118, 136,  73, 184, 121, 132,  69, 180, 117  },
                                                      {    42, 233,  26, 217,  38, 229,  22, 213,  41, 232,  25, 216,  37, 228,  21, 212  },
                                                      {   169, 106, 153,  90, 165, 102, 149,  86, 168, 105, 152,  89, 164, 101, 148,  85  }
                                                  };
      
      //  This is the ultimate method for Bayer Ordered Diher with 16x16 matrix
      //  ncolors - number of colors diapazons to use. Valid values 0..255, but interesed are 0..40
      //  1       - color (1 bit per color plane,  3 bits per pixel)
      //  3       - color (2 bit per color plane,  6 bits per pixel)
      //  7       - color (3 bit per color plane,  9 bits per pixel)
      //  15      - color (4 bit per color plane, 12 bits per pixel)
      //  31      - color (5 bit per color plane, 15 bits per pixel)
      void    makeDitherBayerRgbNbpp( BYTE* pixels, int width, int height, int ncolors )  noexcept
      {
          int divider = 256 / ncolors;
      
          for( int y = 0; y < height; y++ )
          {
              const int   row = y & 15;   //  y % 16
              
              for( int x = 0; x < width; x++ )
              {
                  const int   col = x & 15;   //  x % 16
      
                  const int   t       = BAYER_PATTERN_16X16[col][row];
                  const int   corr    = (t / ncolors);
      
                  const int   blue    = pixels[x * 3 + 0];
                  const int   green   = pixels[x * 3 + 1];
                  const int   red     = pixels[x * 3 + 2];
          
                  int i1  = (blue  + corr) / divider; CLAMP( i1, 0, ncolors );
                  int i2  = (green + corr) / divider; CLAMP( i2, 0, ncolors );
                  int i3  = (red   + corr) / divider; CLAMP( i3, 0, ncolors );
      
                  //  If you want to compress the image, use the values of i1,i2,i3
                  //  they have values in the range 0..ncolors
                  //  So if the ncolors is 4 - you have values: 0,1,2,3 which is encoded in 2 bits
                  //  2 bits for 3 planes == 6 bits per pixel
      
                  pixels[x * 3 + 0]   = CLAMPED( i1 * divider, 0, 255 );  //  blue
                  pixels[x * 3 + 1]   = CLAMPED( i2 * divider, 0, 255 );  //  green
                  pixels[x * 3 + 2]   = CLAMPED( i3 * divider, 0, 255 );  //  red
              }
      
              pixels  += width * 3;
          }
      }
      

      在您的情况下,您需要使用参数 ncolors=4 调用该函数 这意味着每个颜色平面(对于灰度是 1 个平面)将使用每个像素 4 位。

      所以,你必须打电话:

      makeDitherBayerRgbNbpp( pixels, width, height, 4 );
      

      输入像素为 BGR 格式。 出于可视化目的,输出像素也是 BGR 格式。 要获取这些位,您必须替换此代码:

      pixels[x * 3 + 0]   = CLAMPED( i1 * divider, 0, 255 );  //  blue
      pixels[x * 3 + 1]   = CLAMPED( i2 * divider, 0, 255 );  //  green
      pixels[x * 3 + 2]   = CLAMPED( i3 * divider, 0, 255 );  //  red
      

      这样的:

      out.writeBit( i1 ); // blue
      out.writeBit( i2 ); // green
      out.writeBit( i3 ); // red
      

      这是带有您的参数的示例图片(4位灰度)

      更多抖动源码和demo app,可以看here

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-11-13
        • 2010-10-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多