Shangchih Huang
Shangchih Huang

Reputation: 319

How to reduce the effect of pixelation by computing the color value at several points within each pixel and taking the average?

This is an exercise from The Go Programming Language by Donovan & Kernighan:

Exercise 3.6: Supersampling is a technique to reduce the effect of pixelation by computing the color value at several points within each pixel and taking the average. The simplest method is to divide each pixel into four "subpixels". Implement it.

Here is my solution:

// Mandelbrot emits a PNG image of the Mandelbrot fractal.
package main

import (
    //"fmt"
    "image"
    "image/color"
    "image/png"
    "math/cmplx"
    "os"
)

func main() {
    const (
        xmin, ymin, xmax, ymax = -2, -2, +2, +2
        width, height = 1024, 1024
        swidth, sheight = width * 2, height * 2
    )

    var superColors [swidth][sheight]color.Color


    for py := 0; py < sheight; py++ {
        y := float64(py) / sheight * (ymax - ymin) + ymin
        for px := 0; px < swidth; px++ {
            x := float64(px) / swidth * (xmax - xmin) + xmin
            z := complex(x, y)

            superColors[px][py] = mandelbrot(z)
        }
    }

    img := image.NewRGBA(image.Rect(0, 0, width, height))
    for j := 0; j < height; j++ {
        for i := 0; i < width; i++ {
            si, sj := 2*i, 2*j

            r1, g1, b1, a1 := superColors[si][sj].RGBA()
            r2, g2, b2, a2 := superColors[si+1][sj].RGBA()
            r3, g3, b3, a3 := superColors[si+1][sj+1].RGBA()
            r4, g4, b4, a4 := superColors[si][sj+1].RGBA()

            avgColor := color.RGBA{
                uint8((r1 + r2 + r3 + r4) / 4),
                uint8((g1 + g2 + g3 + g4) / 4),
                uint8((b1 + b2 + b3 + b4) / 4),
                uint8((a1 + a2 + a3 + a4) / 4)}

            img.Set(i, j, avgColor)
        }
    }

    png.Encode(os.Stdout, img)
}

func mandelbrot(z complex128) color.Color {
    const iterations = 200
    const contrast = 15

    var v complex128

    for n := uint8(0); n < iterations; n++ {
        v = v*v + z
        if cmplx.Abs(v) > 2 {
            return color.Gray{255 - contrast*n}
        }
    }

    return color.Black
}

However, the result of my solution seems that it doesn't reduce the effect of pixelation:

enter image description here

Is my solution wrong?

Upvotes: 1

Views: 364

Answers (3)

Nico v
Nico v

Reputation: 21

If you add two uint8, it's overflow the memory. For example :
var a, b uint8 = 200 , 100 fmt.Printf(a+b) // result = 44 (300-256)

Just convert your uint8 to int before to add them together :
var a, b uint8 = 200 , 100 fmt.Printf(int(a)+int(b)) // result = 300

and in your case :
var a, b uint8 = 200 , 100 fmt.Printf(uint8((int(a)+int(b)/2)) // result = 150

Upvotes: 0

Alex Dvoretskiy
Alex Dvoretskiy

Reputation: 367

You can convert values from uint32 to uint8 in this case by performing right shift operation:

 r := r >> 8

This will speed up computation time.

See also: Why does golang RGBA.RGBA() method use | and <<?

Upvotes: 0

putu
putu

Reputation: 6454

In go, when you obtained the RGBA values through Color.RGBA(), each color component (R, G, B, A) is represented by 16-bit unsigned, thus the range is between 0-0xffff (0-65535). However, when you save the image to PNG, each component is between 0-0xff (0-255). You need to correctly scale down each color component using the following formula:

//e.g. red component
r := ((r1+r2+r3+r4)/4)*(255/65535) => (r1+r2+r3+r4)/1028

In your case, the correct formula is:

avgColor := color.RGBA{
    uint8((r1 + r2 + r3 + r4) / 1028),
    uint8((g1 + g2 + g3 + g4) / 1028),
    uint8((b1 + b2 + b3 + b4) / 1028),
    uint8((a1 + a2 + a3 + a4) / 1028)}

UPDATE:
Output image looks like
Supersampled Madlebrot image

Upvotes: 2

Related Questions