Golang Overlay image always black and white - image

I am trying to put an overlay on qrcode(image). The problem is my original overlay image is colored but the end result is black and white. Below is the code:
func (e Encoder) Encode(str string, logo image.Image, size int) (*bytes.Buffer, error) {
var buf bytes.Buffer
code, err := qr.New(str, e.QRLevel)
if err != nil {
return nil, err
}
img := code.Image(size)
e.overlayLogo(img, logo)
err = png.Encode(&buf, img)
if err != nil {
return nil, err
}
return &buf, nil
}
func (e Encoder) overlayLogo(dst, src image.Image) {
offset := dst.Bounds().Max.X/2 - src.Bounds().Max.X/2
yOffset := dst.Bounds().Max.Y/2 - src.Bounds().Max.Y/2
draw.Draw(dst.(draw.Image), dst.Bounds().Add(image.Pt(offset, yOffset)), src, image.Point{}, draw.Over)
}
Can someone please help me here?
Image Used

QR code images use 2 colors which makes them easier to scan / recognize. The library you're using github.com/skip2/go-qrcode creates paletted images that use 2 colors only (black and white by default). You can check the source code of QRCode.Image() method you're calling, source here:
p := color.Palette([]color.Color{q.BackgroundColor, q.ForegroundColor})
img := image.NewPaletted(rect, p)
This means whatever you draw on such images, color for each pixel will be chosen from this 2-sized palette (either back or white). The color information of the drawn image will be lost.
If you want to retain all the colors, you must create an image that supports all (or at least the used) colors, draw the QR code image on that, and then the overlay.

My work needs it too. So from #icza I summarized :-
resultImg := image.NewRGBA(qrImg.Bounds())
overlayLogo(resultImg, qrImg)
overlayLogo(resultImg, logo)
So we get the resultImg, QR code with logo.

Related

Golang image/gif EncodeAll have many black dots

I have many png images and want encode them to a gif animation.
These png images dont have any black dots ,but the gif result have many dots.
g := new(gif.GIF)
frames := len(images)
g.Image = make([]*image.Paletted, frames)
g.Delay = make([]int, frames)
eg := errgroup.Group{}
var cl color.Palette = palette.Plan9
for k, img := range images {
img := img
k := k
eg.Go(func() error {
Paletted := image.NewPaletted(img.Bounds(), cl)
draw.FloydSteinberg.Draw(Paletted, img.Bounds(), img, image.Point{})
g.Image[k] = Paletted
g.Delay[k] = deply
return nil
})
}
if err := eg.Wait(); err != nil {
return nil, err
}
var buf bytes.Buffer
err := gif.EncodeAll(&buf, g)
origin png:
with num:
my png info:
File Type : PNG
File Type Extension : png
MIME Type : image/png
Bit Depth : 8
Color Type : RGB with Alpha
Compression : Deflate/Inflate
Filter : Adaptive
Interlace : Noninterlaced
SRGB Rendering : Perceptual
Exif Byte Order : Big-endian (Motorola, MM)
Color Space : sRGB
gif with black dots:
used palgen.Generate(img, 256):
GIF uses a 256 color palette, whereas PNG typically is RGBA with at least 8 bits per color channel (and alpha). In your incomplete example code you use the predefined palette.Plan9 color palette. Picking at least the most dominant color in the origin PNG shows it's the RGB color #f08740. But there is no matching color in the Plan9 palette, so FloydSteinberg will have to dither using "nearest" colors from the Plan9 palette. This obviously doesn't work well.
You need to use an adapted palette to avoid dithering either at all or at least minimize it. As you are giving a non-minimal and incomplete example, I had to roll a minimal example myself that creates a custom palette for the GIF based on the only PNG source given (and please, do upload separate images next time, don't put everything into a single image, it makes things really inconvenient).
A quick google search reveals the Go module github.com/xyproto/palgen that does create a custom color.Palette based on an input image and with the specified numbers of colors; this module seems to be actively maintained and I had immediate success in using it:
img, _, err := image.Decode(f)
pal, err := palgen.Generate(img, 256)
The full example that produces for me a suitable GIF (gif.gif) without dithering, given a source PNG in source.png:
package main
import (
"image"
"image/draw"
"image/gif"
_ "image/png"
"os"
"github.com/xyproto/palgen"
)
func main() {
g := new(gif.GIF)
g.Image = make([]*image.Paletted, 1)
g.Delay = make([]int, 1)
f, err := os.Open("source.png")
if err != nil {
panic(err)
}
defer f.Close()
img, _, err := image.Decode(f)
pal, err := palgen.Generate(img, 256)
Paletted := image.NewPaletted(img.Bounds(), pal)
draw.FloydSteinberg.Draw(Paletted, img.Bounds(), img, image.Point{})
g.Image[0] = Paletted
g.Delay[0] = 100
out, err := os.Create("gif.gif")
if err != nil {
panic(err)
}
defer out.Close()
err = gif.EncodeAll(out, g)
if err != nil {
panic(err)
}
}
Source image source.png:
Final GIF gif.gif:
Use:
draw.Draw(Paletted,img.Bounds(),img,image.ZP, draw.Src)
Replace:
draw.FloydSteinberg.Draw(Paletted, img.Bounds(), img, image.Point{})

Golang some images are rotating on image upload

I am facing a rather weird problem, I am using Golang as a backend restful API and I upload images and resize them with Go . I have an app for I-phone that I am testing and if I upload an image using my real device the image gets saved in my s3 account sideways. For some reason resizing my image is rotating it, however if I upload the image from my Xcode IDE then the image gets saved correctly without rotation . I am thinking that maybe something is getting stripped however I have no idea what that could, my code is this
func UploadStreamImage(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
var buff bytes.Buffer
var result string
wg := sync.WaitGroup{}
print("Exoler-Streams")
wg.Add(1)
go func() {
defer wg.Done()
sess, _ := 's3 credentials'
svc := s3.New(sess)
file, handler, err := r.FormFile("file")
if err != nil {
fmt.Println("Error Uploading Image")
return
}
defer file.Close()
// resize image
img,err := imaging.Decode(file)
if err != nil {
print("Imaging Open error")
}
new_image := imaging.Resize(img, 300, 300, imaging.Lanczos)
var buf bytes.Buffer
err = imaging.Encode(&buf,new_image, imaging.JPEG)
if err != nil {
log.Println(err)
return
}
// end resize
r := bytes.NewReader(buf.Bytes())
read_file,err := ioutil.ReadAll(r)
if err != nil {
fmt.Println("Error Reading file")
}
// s3 specific code
}
The library I am using is this https://github.com/disintegration/imaging and I am just thinking that something is being stripped when uploading the image from my real device thus it is messing up the rotation . The code on the front-end is all the same for my real device and Xcode .
The image is not being rotated in the process, the original image was shown in some image view software in rotated mode depending on the image orientation tag (val x00112) in the Exif part of the file. When you strip the Exif part, as image package does, you lose that information and the image is shown in standard camera orientation format (landscape).
Old question but i thought i would add some extra information here to help anyone who is confused about what Exif is.
When an image is uploaded from a phone it stays in the orientation that it was taken in. However meta data is then added to that image under the key Orientation to tell you which way the image was taken. Phone libaries can then use this to show you the image the correct way around and so can you. It can be a number between 0 - 8. The only orientation numbers you will need to worry about is 3 (rotate 180), 6 (rotate 270) and 8 (rotate 90).
To get a hold of the meta information on an image you can use something like github.com/rwcarlsen/goexif/exif. Here is a snippet on how you can then get the rotation from that.
x, err := exif.Decode(openedFileExif)
var rotation float64 = 0
if err == nil {
orientationRaw, err := x.Get("Orientation")
if err == nil {
orientation := orientationRaw.String()
if orientation == "3" {
rotation = 180
} else if orientation == "6" {
rotation = 270
} else if orientation == "8" {
rotation = 90
}
}
}
Now that you have the rotation required you just need to rotate the image by that amount and you will have your normally oriented picture.
You can use something like github.com/disintegration/imaging for that. uploadedImage needs to be golang image.Image
if rotation != 0 {
uploadedImage = imaging.Rotate(uploadedImage, rotation, color.Gray{})
}

Convert an image to grayscale in Go

I'm trying to convert an image to grayscale using Go.
I've found the below code, however, I'm struggling to understand it.
It would be extremely helpful if you could explain what each function is doing and where to define the incoming and outgoing file.
package main
import (
"image"
_ "image/jpeg" // Register JPEG format
"image/png" // Register PNG format
"image/color"
"log"
"os"
)
// Converted implements image.Image, so you can
// pretend that it is the converted image.
type Converted struct {
Img image.Image
Mod color.Model
}
// We return the new color model...
func (c *Converted) ColorModel() color.Model{
return c.Mod
}
// ... but the original bounds
func (c *Converted) Bounds() image.Rectangle{
return c.Img.Bounds()
}
// At forwards the call to the original image and
// then asks the color model to convert it.
func (c *Converted) At(x, y int) color.Color{
return c.Mod.Convert(c.Img.At(x,y))
}
func main() {
if len(os.Args) != 3 { log.Fatalln("Needs two arguments")}
infile, err := os.Open(os.Args[1])
if err != nil {
log.Fatalln(err)
}
defer infile.Close()
img, _, err := image.Decode(infile)
if err != nil {
log.Fatalln(err)
}
// Since Converted implements image, this is now a grayscale image
gr := &Converted{img, color.GrayModel}
// Or do something like this to convert it into a black and
// white image.
// bw := []color.Color{color.Black,color.White}
// gr := &Converted{img, color.Palette(bw)}
outfile, err := os.Create(os.Args[2])
if err != nil {
log.Fatalln(err)
}
defer outfile.Close()
png.Encode(outfile,gr)
}
I'm quite new to Go so any suggestions or help would be appreciated.
So as Atomic_alarm pointed out, https://maxhalford.github.io/blog/halftoning-1/ explains how to do this succinctly.
But you're question, if I understand correctly, is about the file opening and creation?
The first step is to use the image package to Decode the opened file into an image.Image struct:
infile, err := os.Open("fullcolor.png")
if err != nil {
return nil, err
}
defer infile.Close()
img, _, err := image.Decode(infile) // img -> image.Image
if err != nil {
return nil, err
}
With this Go image.Image struct, you can convert it to a grayscaled image, image.Gray and then, finally, write or encode the image onto an outgoing file on the disk:
outfile, _ := os.Create("grayscaled.png")
defer outfile.Close()
png.Encode(outfile, grayscaledImage) // grayscaledImage -> image.Gray
Inbetween the infile opening and outfile creating, you have to, of course, convert the image to grayscale. Again, try the link above, and you'll find this function, which takes an image.Image and returns a pointer to a image.Gray:
func rgbaToGray(img image.Image) *image.Gray {
var (
bounds = img.Bounds()
gray = image.NewGray(bounds)
)
for x := 0; x < bounds.Max.X; x++ {
for y := 0; y < bounds.Max.Y; y++ {
var rgba = img.At(x, y)
gray.Set(x, y, rgba)
}
}
return gray
}
Concerning the code you provided (and your comment), you were opening a file with os.Args[1], and creating the file os.Args[2]. os.Args is a slice of arguments passed when running the program, 0 will always be the program itself (main), and whatever follows will with 1, 2, etc. The docs states:
Args hold the command-line arguments, starting with the program name.
var Args []string
so you would run your code above like this:
$ go run main.go infile.png outfile.png
infile.png must to be a file on disk (inside the directory you are running the code from, or the complete path to file).
What I have provide above doesn't use os.Args but rather hard codes the file names into the program.

Golang Converting Image magick object to []byte

I am using the following code which fetch the object from Amazon s3 and after performing resizing and cropping. I want to store it on s3. But the problem is i am not able convert the mw (Image maigck object) to byte array. Which will be used for storing it on s3. Moreover in current method it uses jpeg.Encode. What if the image in .png or .gif format. How will we convert it to []byte?
Could you please also tell me how to evenly crop an image just passing the aspect ratio not cropping coordinates. imgStream.Crop((int)originalWidth, ((int)(originalWidth / masterAspectRatio)), Gravity.Center) like we do it in .net. Reason i am asking is there is no method in library which provides this flexibility.
s3Client := s3.New(session.New(), &aws.Config{Region: aws.String(region)})
params := &s3.GetObjectInput{
Bucket: aws.String(bucketName),
Key: aws.String(keyName),
}
out, err := s3Client.GetObject(params)
if err != nil {
log.Fatal(err)
}
img, err := ioutil.ReadAll(out.Body)
if err != nil {
log.Fatal(err)
}
mw := imagick.NewMagickWand()
err = mw.ReadImageBlob(img)
if err != nil {
log.Fatal(err)
}
//Perform resizing and cropping on mw object
buf := new(bytes.Buffer)
err = jpeg.Encode(buf, mw, nil)
sendmw_s3 := buf.Bytes()
paramsPut := &s3.PutObjectInput{
Bucket: aws.String(masterBucketName),
Key: aws.String(keyName),
Body: bytes.NewReader(sendmw_s3),
}
resp, err := s3Client.PutObject(paramsPut)
if err != nil {
log.Fatal(err)
}
Error :
cannot use mw (type *imagick.MagickWand) as type image.Image in argument to jpeg.Encode:
*imagick.MagickWand does not implement image.Image (missing At method)
You need to use the func (mw *MagickWand) GetImageBlob() []byte function.
It returns a slice of bytes containing a complete encoded image for the current file format (JPEG, gif, PNG...).
The returned data can therefore be saved to disk, or sent to s3 as-is.
See https://gowalker.org/github.com/gographics/imagick/imagick#MagickWand_GetImageBlob for the documentation.
This question is actually two questions, and #SirDarius answered one of them, by suggesting the use of GetImageBlob(). You can also use SetImageFormat() to change the image format before generating the blob.
For the part about the crop, I am sure there are a bunch of ways to do this with ImageMagick. The way I have done it, to achieve a center crop is to first transform the image so that the smaller dimension fits into my desired target resolution. And then to crop away the parts that overflow.
// Create a new image where smallest dimension is fit
// and the rest overflows the dimensions
size := fmt.Sprintf("%dx%d^+0+0", w, h)
tx := wand.TransformImage("", size)
// Center Crop away the extra parts of the image, to perform
tx.SetImageGravity(imagick.GRAVITY_CENTER)
offsetX := -(int(w) - int(tx.GetImageWidth())) / 2
offsetY := -(int(h) - int(tx.GetImageHeight())) / 2
err := tx.ExtentImage(w, h, offsetX, offsetY)
...

How to split gif into images

How can I split gif into images in go?
image/gif's DecodeAll return GIF, which contains an array of palette. But don't know how to convert each palette into an image?
Consider the following:
Frames can contain transparent pixels or areas, a good example is this image on wikipedia which (I guess) has one of these full-color blocks per frame and the rest of the frame transparent.
This introduces a problem for you: Especially with animated GIFs, that do not use multiple frames to create a true-colored static image, the frames that DecodeAll returns are not what you actually see if you, for example, open the image in your browser.
You'll have to process the image in the same way your browser would, i.e. leave the old frames on a kind of canvas and overpaint with the new frame. BUT this is not always true. GIF frames can, AFAIK, contain a disposal method, specifying how (or if?) you should dispose of the frame.
Anyways, to get to your point, the most simple approach that will also work in most cases is something like
import (
"errors"
"fmt"
"image"
"image/draw"
"image/gif"
"image/png"
"io"
"os"
)
// Decode reads and analyzes the given reader as a GIF image
func SplitAnimatedGIF(reader io.Reader) (err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("Error while decoding: %s", r)
}
}()
gif, err := gif.DecodeAll(reader)
if err != nil {
return err
}
imgWidth, imgHeight := getGifDimensions(gif)
overpaintImage := image.NewRGBA(image.Rect(0, 0, imgWidth, imgHeight))
draw.Draw(overpaintImage, overpaintImage.Bounds(), gif.Image[0], image.ZP, draw.Src)
for i, srcImg := range gif.Image {
draw.Draw(overpaintImage, overpaintImage.Bounds(), srcImg, image.ZP, draw.Over)
// save current frame "stack". This will overwrite an existing file with that name
file, err := os.Create(fmt.Sprintf("%s%d%s", "<some path>", i, ".png"))
if err != nil {
return err
}
err = png.Encode(file, overpaintImage)
if err != nil {
return err
}
file.Close()
}
return nil
}
func getGifDimensions(gif *gif.GIF) (x, y int) {
var lowestX int
var lowestY int
var highestX int
var highestY int
for _, img := range gif.Image {
if img.Rect.Min.X < lowestX {
lowestX = img.Rect.Min.X
}
if img.Rect.Min.Y < lowestY {
lowestY = img.Rect.Min.Y
}
if img.Rect.Max.X > highestX {
highestX = img.Rect.Max.X
}
if img.Rect.Max.Y > highestY {
highestY = img.Rect.Max.Y
}
}
return highestX - lowestX, highestY - lowestY
}
(untested, but should work)
Note that gif.DecodeAll can and will panic frequently, because a lot of the GIF images on the internet are somewhat broken. Your browser tries to decode them and will, for example, replace missing colors with black. image/gif will not do that, but panic instead. That's why we defer the recover.
Also, I used the getGifDimensions for a similar reason as stated above: single frames need not be what you see in your browser. In this case, the frames are just smaller than the complete image, that's why we have to iterate over all frames and get the "true" dimensions of the image.
If you really really want to do it right, you should probably read the GIF spec GIF87a, GIF89a and something like this article which is a lot easier to understand. From that, you should decide how to dispose of the frames and what to do with transparency while overpainting.
EDIT: Some of the effects mentioned earlier can be observed easily if you split some GIFs online, for example this or this - play around with "Ignore optimizations" and "Redraw every frame with details from previous frames" to see what I mean.
image.Image is an interface, and *image.Paletted implements the interface, so for example if you want to save every frame of a GIF into a PNG file, you can just encode every image:
for i, frame := range img.Image {
frameFile, err := os.OpenFile(fmt.Sprintf("%d.png", i+1), os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0666)
if err != nil {
log.Fatal(err)
}
err = png.Encode(frameFile, frame)
if err != nil {
log.Fatal(err)
}
// Not using defer here because we're in a loop, not a function.
frameFile.Close()
}

Resources