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-linearbi-cubic 插值。您还可以提高样本模糊度并降低样本以使内容平滑一点...任何样本输入和想要的输出? 嗯,缩放是一种选择,但最终抗锯齿也是如此。目前,我以稍后要显示的大小创建数组。我可以轻松地将它创建得更大,然后按比例缩小。但是抗锯齿对我来说似乎更直接。输入是带有颜色的二维 int 数组(十六进制数):int[][] lights = new int[150][10]; 类似这样。 只有当你通过矢量图形(光栅化)渲染你的东西时才能进行抗锯齿,因此像素艺术与 WU 和类似算法无关......因为 PixelArt 意味着光栅图像(不是矢量)这就是为什么缩放是方式 啊,好吧。我猜有道理。有什么好的资源可以推荐,我可以看看缩放算法? 只是谷歌 bi-linearbi-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] -&gt; 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 的抗锯齿算法的主要内容,如果未能解决你的问题,请参考以下文章

透明位图上的抗锯齿文本

基于图片的抗锯齿方法

游戏中的抗锯齿杂谈

paintComponent() 方法中的抗锯齿

CALayer 中的抗锯齿绘图

游戏中的抗锯齿技术Anti-Alasing提炼总结