Search code examples
imagegocolorsrgba

Go image manipulation


I need to load an image and search for colors and replace them. For example on an image I need to search for all red pixels and convert them to purple.

I am doing the following (img is a valid .png image):

func colorize(img image.Image) {
    b := image.NewRGBA(img.Bounds())
    draw.Draw(b, b.Bounds(), img, image.ZP, draw.Src)
    for x := 0; x < b.Bounds().Dx(); x++ {
        for y := 0; y < b.Bounds().Dy(); y++ {
            log.Println(b.At(x, y).RGBA())
        }
    }
}

Thing is img.At().RGBA() doesn't seem to return the proper R, G, B, A codes? I am getting numbers bigger than 255 for example.

So how should I read all the image pixels while being able to know the x and y position of them?


Solution

  • img.At().RGBA() is Color.RGBA(). Quoting its doc:

    // RGBA returns the alpha-premultiplied red, green, blue and alpha values
    // for the color. Each value ranges within [0, 0xffff], but is represented
    // by a uint32 so that multiplying by a blend factor up to 0xffff will not
    // overflow.
    //
    // An alpha-premultiplied color component c has been scaled by alpha (a),
    // so has valid values 0 <= c <= a.
    

    Components returned by RGBA() are in range 0..0xffff, not 0..0xff, and they are also alpha-premultiplied.

    Manual decoding

    One way to get back the red, green, blue components in the 0..255 range is to shift right by 8 for example:

    r, g, b, a := b.At(x, y).RGBA()
    r, g, b, a = r>>8, g>>8, b>>8, a>>8
    log.Println(r, g, b) // Now in range 0..255
    

    Converting to color.RGBA

    Another way is to convert the color to color.RGBA which is a struct, containing the components plain and simple:

    type RGBA struct {
            R, G, B, A uint8
    }
    

    Since you are using image.NewRGBA() which returns an image of type image.RGBA, the colors returned by the Image.At() method will be of dynamic type color.RGBA, so you can simply use a type assertion:

    rgbacol := b.At(x, y).(color.RGBA)
    log.Println(rgbacol.R, rgbacol.G, rgbacol.B, rgbacol.A)
    

    In general (if image is not of type image.RGBA), Image.At() may or may not be of concrete type color.RGBA.

    So in the general case you need to convert the color to a value of type color.RGBA. Conversions between color models are modeled by color.Model, and the image/color package has predefined converters. What you need is color.RGBAModel. color.RGBAModel.Convert() will return a color.Color value whose dynamic type is surely color.RGBA.

    Example using color.RGBAModel:

    var c color.Color
    c = color.Gray{160}
    
    rgbacol := color.RGBAModel.Convert(c).(color.RGBA)
    
    fmt.Println(rgbacol.R, rgbacol.G, rgbacol.B, rgbacol.A)
    

    Output (try it on the Go Playground):

    160 160 160 255
    

    So in your loop do:

    rgbacol := color.RGBAModel.Convert(b.At(x, y).(color.RGBA)
    // rgbacol is a struct of type color.RGBA, components are in range 0..255
    

    Note:

    Above solutions still give you back the alpha pre-multiplied components. If you want to undo the alpha pre-multiplication, you may use color.NRGBAModel converter (instead of color.RGBAModel).