Pixelart 的抗锯齿算法
Posted
技术标签:
【中文标题】Pixelart 的抗锯齿算法【英文标题】:Anti-Aliasing Algorithm for Pixelart 【发布时间】:2019-05-21 01:07:58 【问题描述】:我有一个图像,或者 Pixelart,因为没有更好的词,尺寸非常小。它实际上只是这个大小的数字数组:new int[150][10]
。我在这个数组上绘制线条和曲线,主要是黑色背景上的一种颜色。它旨在稍后控制 LED 灯条。所以现在我正在寻找一种方法来消除我绘制的线条、曲线和形状的锯齿。我只想输入我的数组,有点像这样:
int[][] antiAlias(int[][] image)
int[][] result = new int[image.length][image[0].length];
// do magic here
return result;
我偶然发现了 Wu 的抗锯齿,但据我所知,它仅用于绘制线条。如果有人能给我提示我应该寻找什么样的算法,我将不胜感激。
我还了解到可以通过下采样来实现抗锯齿效果。因为我可以在具有更高分辨率的阵列中创建直线和曲线,这也是一种选择。但我不知道如何执行下采样,而且我在互联网上可以找到的所有关于它的东西总是适用于 Image
- 对象并使用库,这当然是没有选择的,因为我没有使用实际的图像。
我想要一个像这样的下采样函数:
// scale should be power of 2 (I guess??)
int[][] downsample(int[][] image, int scale)
int[][] result = new int[image.length / 2][image[0].length / 2];
// do magic here
if (scale > 2) return downsample(result, scale / 2);
return result;
再一次,如果有人对我可以研究什么样的算法有一个好主意,我将非常感激。
【问题讨论】:
我感觉你根本不想要抗锯齿。相反,您希望在已经光栅化的图像上进行缩放/调整大小(即上/下采样)方法,例如 bi-linear 和 bi-cubic 插值。您还可以提高样本模糊度并降低样本以使内容平滑一点...任何样本输入和想要的输出? 嗯,缩放是一种选择,但最终抗锯齿也是如此。目前,我以稍后要显示的大小创建数组。我可以轻松地将它创建得更大,然后按比例缩小。但是抗锯齿对我来说似乎更直接。输入是带有颜色的二维 int 数组(十六进制数):int[][] lights = new int[150][10];
类似这样。
只有当你通过矢量图形(光栅化)渲染你的东西时才能进行抗锯齿,因此像素艺术与 WU 和类似算法无关......因为 PixelArt 意味着光栅图像(不是矢量)这就是为什么缩放是方式
啊,好吧。我猜有道理。有什么好的资源可以推荐,我可以看看缩放算法?
只是谷歌 bi-linear 和 bi-cubic 过滤或插值非常简单,网上有很多关于它的东西。
【参考方案1】:
正如 cmets 中所建议的那样,我查看了双线性插值。这就是我想出的。该算法仅适用于当结果尺寸恰好是原始尺寸的一半时的缩小。因为在缩小过程中会丢失很多亮度,所以我再次将所有像素变亮。仍然需要一个更好的解决方案,但它现在有效。
int[][] bilinearDownscale(int[][] original, int scale, boolean brighten)
int[][] result = new int[original.length / 2][original[0].length / 2];
// the four pixels from which we derive our downscaled pixel
// i = 0 -> red, i = 1 -> green, i = 2 -> blue
int a[] = new int[3];
int b[] = new int[3];
int c[] = new int[3];
int d[] = new int[3];
for (int x = 0; x < result.length; x++)
for (int y = 0; y < result[0].length; y++)
// get the individual color values of the old pixels
a[0] = (original[x * 2][y * 2]) >> 16 & 0xFF;
b[0] = (original[x * 2 + 1][y * 2]) >> 16 & 0xFF;
c[0] = (original[x * 2][y * 2 + 1]) >> 16 & 0xFF;
d[0] = (original[x * 2 + 1][y * 2 + 1]) >> 16 & 0xFF;
a[1] = (original[x * 2][y * 2]) >> 8 & 0xFF;
b[1] = (original[x * 2 + 1][y * 2]) >> 8 & 0xFF;
c[1] = (original[x * 2][y * 2 + 1]) >> 8 & 0xFF;
d[1] = (original[x * 2 + 1][y * 2 + 1]) >> 8 & 0xFF;
a[2] = original[x * 2][y * 2] & 0xFF;
b[2] = original[x * 2 + 1][y * 2] & 0xFF;
c[2] = original[x * 2][y * 2 + 1] & 0xFF;
d[2] = original[x * 2 + 1][y * 2 + 1] & 0xFF;
// get the individually interpolated color values
int red = (int) (0.25 * (a[0] + b[0] + c[0] + d[0]));
int green = (int) (0.25 * (a[1] + b[1] + c[1] + d[1]));
int blue = (int) (0.25 * (a[2] + b[2] + c[2] + d[2]));
// apply saturation if so desired
if (brighten)
float hsb[] = Color.RGBtoHSB(red, green, blue, null);
hsb[2] = -((hsb[2] - 1) * (hsb[2] - 1)) + 1;
// compute the new color value
result[x][y] = Color.HSBtoRGB(hsb[0], hsb[1], hsb[2]);
else
// compute the new color value
result[x][y] = (red << 16) | (green << 8) | blue;
// yay recursion
if (scale > 2)
return bilinearDownscale(result, scale / 2, brighten);
return result;
【讨论】:
这不是双线性过滤,而是取平均...你应该做 3 次线性插值,所以如果你在浮动(x,y)
位置周围有 c0,c1,c2,c3
颜色,它会像 t=x-floor(x); c0 = c0 + (c1-c0)*t; c1 = c2 + (c3-c2)*t; t=y-floor(y); c0 = c0 + (c1-c0)*t;
where @ 987654329@ 是最终颜色...它适用于放大和缩小...请参阅Inversing an interpolation of rotation 并在代码中查找// bilinear interpolation A[fx][fy] -> B[x][y]
好吧,我从这里得到了算法:tech-algorithm.com/articles/bilinear-image-scaling 因为我可以安全地假设我总是将图像缩小 2 的精确幂,所以我可以安全地假设 w=0.5 和 h= 0.5(我链接到的算法中的 w,h)。并且 w 和 h = 0.5,公式可以简化,直到它让我得到平均。
是的,但是如果您将其保留为双线性形式,那么它适用于任何比例因子......而且您的通道提取看起来太复杂了看看这个:Converting BMP image to set of instructions for a plotter? 和 Effective gif/image color quantization?在代码中查找union
...您根本不需要移位和屏蔽以上是关于Pixelart 的抗锯齿算法的主要内容,如果未能解决你的问题,请参考以下文章