Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 362
Raggaer Avatar asked Feb 06 '23 21:02

Raggaer


1 Answers

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).

like image 67
icza Avatar answered Feb 15 '23 04:02

icza