C中最快的抖动/半色调库
Posted
技术标签:
【中文标题】C中最快的抖动/半色调库【英文标题】:Fastest dithering / halftoning library in C 【发布时间】:2010-12-02 00:45:45 【问题描述】:我正在开发一个定制的瘦客户端服务器,为它的客户端提供渲染的网页。服务器运行在多核 Linux 机器上,Webkit 提供 html 渲染引擎。
唯一的问题是客户端显示受限于 4 位(16 色)灰度调色板。我目前正在使用 LibGraphicsMagick 抖动图像(RGB-> 4 位灰度),这显然是服务器性能的瓶颈。分析表明,超过 70% 的时间用于运行 GraphicsMagick 抖动函数。
我已经探索了 *** 和 Interwebs 以获得良好的高性能解决方案,但似乎没有人对各种图像处理库和抖动解决方案进行任何基准测试。
我会更高兴发现:
-
在抖动/半色调/将 RGB 图像量化为 4 位灰度方面性能最高的库是什么。
是否有任何特殊的抖动库或任何公共域代码 sn-ps 可供您指出?
就高性能而言,您更喜欢使用哪些库来处理图形?
首选 C 语言库。
【问题讨论】:
【参考方案1】:抖动会花费相当多的时间,具体取决于所选的算法。
实现Bayer (Matrix) 和Floyd-Steinberg (Diffusion) dithering 相当简单。
使用 MMX/SSE 进行编码以处理并行像素时,Bayer 过滤可以变得非常快。您还可以使用 GPU 着色器进行抖动/转换。
FWIW,您已经在使用 GraphicsMagick,但有完整的 OSS 图形库列表here
【讨论】:
感谢这份庞大的列表,我只是希望已经有一些基准测试。 @Jamie:已编辑的链接 - 旧链接 (scien.stanford.edu/class/psych221/projects/02/mdeleon/…) 不起作用。【参考方案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
【讨论】:
以上是关于C中最快的抖动/半色调库的主要内容,如果未能解决你的问题,请参考以下文章
R语言ggplot2可视化:通过水平半小提琴图和抖动数据点可视化雨云图(Rain Cloud plots)自定义雨云图中数据点的颜色(数据点的颜色和半小提琴图一致)