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?
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).
Related
I'm trying to create a PDF by decoding image files (in PNG, JPG, GIF, and BMP format) using the image.Decode() method to get the image.Image. Then, I write the pixel data into a PDF stream, which is later compressed. The problem I'm encountering is that when I decode a JPEG, the colors are incorrect in the resulting PDF. All other image formats are working as expected. I've attached a screenshot of the issue.
Screenshot:
https://i.imgur.com/Bzz6EnD.png
Does anyone know what could be causing this problem? Is there a specific way that JPEGs need to be handled differently when using image.Decode()? Any suggestions on how to fix this issue would be greatly appreciated!
Edit:
Code:
var iData image.Image
iFile, err := os.Open(path)
if err != nil {
[...]
} else {
iData, _, err = image.Decode(iFile)
}
[...]
x.Dictionary.Set("ColorSpace", "/DeviceRGB")
x.Dictionary.Set("BitsPerComponent", 8)
for j := 0; j < iData.Bounds().Dy()/pixelMul; j++ {
for k := 0; k < iData.Bounds().Dx()/pixelMul; k++ {
r, g, b, _ := iData.At(k*pixelMul, j*pixelMul).RGBA()
x.Write([]byte{byte(r), byte(g), byte(b)})
}
}
[...]
The resulting image in the pdf looks the same when using the jpeg.Decode directly.
I exptect the image in the resulting pdf to look just like the original png with possibly a bit of degredation.
Original PNG: https://i.imgur.com/cjjOdxj.png
Converted JPG: https://i.imgur.com/I5kxTab.jpeg
Other JPEGs also have the same issue, e.g. the first test JPEG from w3c https://www.w3.org/MarkUp/Test/xhtml-print/20050519/tests/A_2_1-BF-01.htm
Color.RGBA() returns the alpha-premultiplied color components in the range of 0..0xffff.
Converting such a value to byte like byte(r) will keep its lowest 8 bits which will seemingly be just random compared to the original value. You need an 8-bit color component, do not convert it to byte but use the higher 8 bits, which means shift right by 8 (or divide by 256):
x.Write([]byte{byte(r>>8), byte(g>>8), byte(b>>8)})
Explanation why it still worked for PNG and GIF, but not for JPEG:
Decoding PNG and GIF images likely uses an image model that uses the color.RGBA color model, which stores components using 8-bit values. But its RGBA.RGBA() method converts these values to 16-bit values by duplicating the original 8-bit values:
func (c RGBA) RGBA() (r, g, b, a uint32) {
r = uint32(c.R)
r |= r << 8
g = uint32(c.G)
g |= g << 8
b = uint32(c.B)
b |= b << 8
a = uint32(c.A)
a |= a << 8
return
}
Which means if you take the lower 8 bits, you get the same original value just as if you take the 8 higher bits. Decoding JPEG images will likely use the color.YCbCr color type which does not reproduce this "implementation behavior".
Do not depend on this. When you need an 8-bit component from a 16-bit component, always use the higher 8 bits.
I need to drop shadow from an object of type image.Image which has got an alpha channel. The goal is not to have the rectangle shadowed, but the alpha.
What I wanted to do is:
take out the alpha channel
turn it into a black RGB image
expand to N pixels (N=the size of the blur)
apply a gaussian blur
apply it as a background
The last point is quite simple, thanks to the "image/draw" API (I have no problem to cut an image in circle, and apply the mask, for example).
Outputting the alpha channel seems simple, too (for each pixel, apply a 255*alpha multiplication on R, G, and B, or use a grayscale image, and finally invert the white color to black)
It's clearly the dilation and blurring that I have a problem with.
I have nothing against the fact that the image changes size for this operation (at worst I will reduce the original image before transformation)
The question is "how to dilate and blur the alpha channel" with Go on an image.Image?
OK, after a while, I finally made this.
I rebuild the alpha and devide it by 2 (this will be a paramters).
I'm using github.com/disintegration/imaging package to blur the alpha.
func DropShadow(img image.Image, size float64) image.Image {
bounds := img.Bounds()
sizeInt := int(math.Ceil(size)) * 4
final := imaging.New(bounds.Dx()+sizeInt, bounds.Dy()+sizeInt, color.Alpha{})
for x := 0; x < bounds.Dx(); x++ {
for y := 0; y < bounds.Dy(); y++ {
_, _, _, a := img.At(x, y).RGBA()
final.Set(x+sizeInt/2, y+sizeInt/2, color.RGBA{0x0, 0x0, 0x0, uint8(a / 2)})
}
}
final = imaging.Blur(final, size)
final = imaging.Overlay(final, img, image.Point{sizeInt / 2, sizeInt / 2}, 1)
return final
}
It's only a bit curious that I need to scale by 4 the image to not have the shadow sticking to the border. But it does the job...
So I'm making some rgba images pixel by pixel following a certain pattern and saving them as png later on and noticed that when alpha channel es changed with certain colors it changes the whole pixel color when stored as png.
I made a test to show what is currently happening:
img := image.NewRGBA(image.Rect(0, 0, 250, 250))
for y := 0; y < height; y++ {
for x := 0; x < width; x++ {
f.Read(b)
img.SetRGBA(x, y, color.RGBA{
249,
214,
133,
255,
})
}
}
var buff bytes.Buffer
err := png.Encode(&buff, img)
if err != nil {
log.Println(err)
return
}
This will print an image of color #F9D685. But if I change alpha into 200 it will print another one with #6844BC and transparency instead of printing the original color with it's transparency.
Is there a way to solve this? I believe that it's because I'm missing something but can't really figure it out and didn't find anything similar to what's happening to me on google/here.
That one is simple:
go doc color.RGBA
package color // import "image/color"
type RGBA struct {
R, G, B, A uint8
}
RGBA represents a traditional 32-bit alpha-premultiplied color, having 8
bits for each of red, green, blue and alpha.
An alpha-premultiplied color component C has been scaled by alpha (A), so
has valid values 0 <= C <= A.
You might be looking for color.NRGBA.
(Always, really always, consult the documentation of the involved types and functions. Always.)
Go's image.Image interface has three methods: Bounds (clearly necessary for determining the size of an image), At (which returns the actual color at each pixel), and ColorModel. This last method returns a color.Model, which is capable of converting a color from any model into the representation that this image uses.
Why is ColorModel a part of this interface? How is it used by consumers of the image.Image type? If I have an image img, and I know nothing about its underlying representation, what good does img.ColorModel() do me? I can convert any color into the proper model, but I don't see what I can use this converted color for; the other two ways of interacting with an image, At and Bounds, do not take colors as arguments.
Am I missing something? Do any standard library functions call the ColorModel method of an image.Image, and what for?
I'm not sure if I understand your question entirely, but I do not think the purpose of ColorModel() is to change the color. It is only to get the color model.
The standard library uses it mostly when encoding images, like in the png package:
switch m.ColorModel() {
case color.GrayModel:
e.cb = cbG8
case color.Gray16Model:
e.cb = cbG16
case color.RGBAModel, color.NRGBAModel, color.AlphaModel:
if opaque(m) {
e.cb = cbTC8
} else {
e.cb = cbTCA8
}
default:
if opaque(m) {
e.cb = cbTC16
} else {
e.cb = cbTCA16
}
}
Another hint on it's intended use can be found in the jpeg/writer:
// TODO(wathiede): switch on m.ColorModel() instead of type.
To extend the accepted answer:
Although the color.Model is capable of converting a color to a different color type, as said in the docs:
Interface ColorModel describes the image's color model.
i.e. it's not the pixel's color model. It looks similar, but the latter implies that an image may contain pixel(s) with a different color model.
Note that an image represents a rectangular grid of homogeneous colors, i.e. all pixels have the same color model. Once you understand the color model of an image, it's better and more efficient to cast the image to a particular concrete image type, then work directly on that particular image. The following snippet illustrates the idea:
switch img.ColorModel() {
case color.RGBAModel:
// Try to cast to RGBA first
m, ok := img.(*image.RGBA)
if !ok {
//not an RGBA image, do generic/custom processing,
//e.g. using interface exposed by image.Image
return
}
//Direct pixel access for performance
for y := m.Rect.Min.Y; y < m.Rect.Max.Y; y++ {
yp := (y - m.Rect.Min.Y) * m.Stride
for x := m.Rect.Min.X; x < m.Rect.Max.X; x++ {
rgba := m.Pix[yp+(x-m.Rect.Min.X)*4:] //[]byte{r, g, b, a}
//get rgba component
r, g, b, a := rgba[0], rgba[1], rgba[2], rgba[3]
//set r channel to RED
rgba[0] = 255
//...
}
}
}
is more efficient compared to the following code
// Less efficient image processing
// a type-switch on the color returned by the `At` method
b := img.Bounds()
for y := b.Min.Y; y < b.Max.Y; y++ {
for x := b.Min.X; x < b.Max.X; x++ {
col := img.At(x, y)
switch col := col.(type) {
case color.RGBA:
//do something with pixel
}
}
}
I am trying to draw over an image using a template, the template image is the following
I want to colorize the image red, green, blue and yellow colors with custom colors and achieve something like this:
In order to achieve this I currently use this image as a base
And then draw over the template using draw.Draw(outfitImage, outfitImage.Bounds(), generatorImage, image.ZP, draw.Over)
This however gives a very weird result (nothing near the expected result), this is how I replace pixels
func paintPixels(img *image.NRGBA, base color.Color, dst color.Color) {
br, bg, bb, ba := base.RGBA()
dr, dg, db, _ := dst.RGBA()
for x := 0; x < img.Bounds().Dx(); x++ {
for y := 0; y < img.Bounds().Dy(); y++ {
r, g, b, a := img.At(x, y).RGBA()
if br == r && bg == g && bb == b && ba == a {
img.Set(x, y, color.RGBA{uint8(dr), uint8(dg), uint8(db), 255})
}
}
}
}
The result can vary depending on the alpha value I use when colorizing the image template. So I cant think of a way to achieve the expected result, I guess I should use a mask with draw.DrawMask but I have no clue where to start or how to achieve the result I am looking for
You look like you're just replacing pixels with the colour if all components match. If you look at the compositing methods in bild/blend you should find one that suits you for combining images - you probably want Opacity or Multiply modes and could extract code from this file:
https://github.com/anthonynsimon/bild/blob/master/blend/blend.go