0

I have an Image, or Pixelart for lack of better word, of very small size. It is actually just an array of numbers of around this size: new int[150][10]. I draw lines and curves on this array, mostly one colour on a black background. It is meant to control an LED-Strip later on. So now I am searching for a method to antialias the lines, curves and shapes I draw. I just want to input my array, kind of like this:

int[][] antiAlias(int[][] image) {
    int[][] result = new int[image.length][image[0].length];

    // do magic here

    return result;
}

I have stumbled across Wu's antialiasing, but as far as I can tell it's only for drawing lines. I would really appreciate if someone could give me a hint as to what kind of algorithm I should look for.

I also read that the antialias-effect can be achieved with downsampling. As it would be no problem for me to create the lines and curves in an array with higher resolution, this could also be an option. But I don't have any idea how to perform downsampling, and everything I can find on the internet about it always works with Image- Objects and uses libraries, which is of course no option since I am not using an actual image. I would like a downsampling function like this:

// 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;
}

Again, if anyone has a good idea for me what kind of algorithms I could look into, I would very much appreciate it.

  • I got the feeling you do not want anti-aliasing at all. Instead you want scaling/resizing (that is what up/down sampling is) methods like **bi-linear** and **bi-cubic** interpolation on already rasterized image. You can also up sample blur a bit and downsample back to smooth the stuff a bit... Any sample input and wanted output? – Spektre May 21 '19 at 07:43
  • Well, scaling is an option, but ultimately so is antialiasing. At the moment I create the array at the size I want to display it later on. I could easily create it far bigger and then scale it down. But Antialiasing seems more straightforward to me. Input is a two-dimensional int-array with colors (Hex-numbers): `int[][] lights = new int[150][10];` something like this. – MusicIsLife May 21 '19 at 14:57
  • Anti-aliasing is possible only if you render your stuff by vector graphics (rasterization) so pixel art is out of question with WU and similar algorithms... Because PixelArt means raster image (not vector) that is why scaling is the way – Spektre May 21 '19 at 17:30
  • Ah, okay. Makes sense I guess. Any good resources you can recommend where I can have a look into scaling algorithms? – MusicIsLife May 21 '19 at 17:43
  • just Google **bi-linear** and **bi-cubic** filtering or interpolation its really simple and there are tons of stuff about it on the WEB. – Spektre May 21 '19 at 18:02

1 Answers1

1

I looked at bilinear interpolation, just as suggested in the comments. This is what I came up with. The algorithm works only for downscaling when the results dimensions are exactly the half of the original. Because a lot of brightness gets lost during the downscaling process, I brighten all pixels up again. Still need a better solution for that, but it works for now.

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;
}
  • that is not bilinear filtering but just averaging instead... You should do 3 times linear interpolation so if you got `c0,c1,c2,c3` colors around floating `(x,y)` position it would be like `t=x-floor(x); c0 = c0 + (c1-c0)*t; c1 = c2 + (c3-c2)*t; t=y-floor(y); c0 = c0 + (c1-c0)*t;` where `c0` is the final color ... it works for both enlarging and shrinking ... See [Inversing an interpolation of rotation](https://stackoverflow.com/a/41937686/2521214) and look for `// bilinear interpolation A[fx][fy] -> B[x][y]` in the code – Spektre May 23 '19 at 06:46
  • Well, I took the algorithm from here: http://tech-algorithm.com/articles/bilinear-image-scaling/ And since I can safely assume that I will always downscale images by an exact power of 2, I can safely assume that w=0.5 and h=0.5 (w, h in the algorithm I linked to). and with w and h = 0.5, the formula can be simplified until it leaves me with the averaging. – MusicIsLife May 23 '19 at 11:01
  • Yep but if you leave it in the bi-linear form then it works for any scaling factor ... also your channel extraction look too complicated take a look at this: [Converting BMP image to set of instructions for a plotter?](https://stackoverflow.com/a/36820654/2521214) and [Effective gif/image color quantization?](https://stackoverflow.com/a/30265253/2521214) look for the `union` in the code ... you do not have to bit-shift and mask at all – Spektre May 23 '19 at 11:36