Golang how can I upload external images without rotating them - go

I have been working on an image upload functionality for a few weeks and I just about have it done. I am using Golang as a backend language and it's purpose is to upload images sent from IOS devices to amazon s3 . In the process of uploading the image I also resize them and this has caused problems primarily the decode method sometimes rotates images which I do not want
file, handler, err := r.FormFile("file")
if err != nil {
fmt.Println("Error Uploading Image")
return
}
defer file.Close()
// the code below sometimes rotates an image
img,err := imaging.Decode(file)
if err != nil {
print("Imaging Open error")
}
new_image := imaging.Resize(img,400,400, imaging.Lanczos)
The library I am using is this one https://github.com/disintegration/imaging which is fantastic and the example they showed was this
src, err := imaging.Open("testdata/lena_512.png")
if err != nil {
log.Fatalf("Open failed: %v", err)
}
src = imaging.Resize(src, 256, 0, imaging.Lanczos)
That example is fine however my images are not stored locally they are coming from IOS devices is there something that I can do to fix this problem ? Some of my images are being saved like this
Some images are rotated like this and it is the Decode method doing it
I can correct it by rotating the image however some other images that do not get rotated by decode end up being rotated by the Rotate270 method .
img,err := imaging.Decode(file)
if err != nil {
print("Imaging Open error")
}
// rotate the image
img = imaging.Rotate270(img)
new_image := imaging.Resize(img,400,400, imaging.Lanczos)
This is how the image is saved and looks after I rotate it . Is there someway I can upload external images without having to use decode or just fixing the decode issue ? Imaging.Resize first parameter takes in type image.Image and here is my full code
func myImages(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
var buff bytes.Buffer
file, handler, err := r.FormFile("file")
if err != nil {
fmt.Println("Error Uploading Image")
return
}
defer file.Close()
img,err := imaging.Decode(file)
if err != nil {
print("Imaging Open error")
}
new_image := imaging.Resize(img,400,400, imaging.Lanczos)
var buf bytes.Buffer
err = imaging.Encode(&buf,new_image, imaging.JPEG)
if err != nil {
log.Println(err)
return
}
}

Unfortunately the go stdlib image package does not handle images which are marked as rotated with exif tags (like those here taken on a device upside down). The bug about this is here:
https://github.com/golang/go/issues/4341
You can see an example of handling this in camlistore, but it is quite involved:
https://camlistore.googlesource.com/camlistore/+/master/pkg/images/images.go
First you have to decode exif options (DecodeOpts), then you have to check which orientation it has (out of 8 possible), then rotate as necessary. It's painful and there is no easy solution as yet. You can use this package to read exif data though:
https://github.com/rwcarlsen/goexif

I had the same problem and solved it with a help of two libraries:
go get -u github.com/disintegration/imaging
go get -u github.com/rwcarlsen/goexif/exif
with exif you can obtain the actual orientation of the image and with imaging you can perform the operations which are needed to revert the image to orientation 1.
Since a replaced image has no exif information left, the result is then shown correctly.
// ReadImage makes a copy of image (jpg,png or gif) and applies
// all necessary operation to reverse its orientation to 1
// The result is a image with corrected orientation and without
// exif data.
func ReadImage(fpath string) *image.Image {
var img image.Image
var err error
// deal with image
ifile, err := os.Open(fpath)
if err != nil {
logrus.Warnf("could not open file for image transformation: %s", fpath)
return nil
}
defer ifile.Close()
filetype, err := GetSuffix(fpath)
if err != nil {
return nil
}
if filetype == "jpg" {
img, err = jpeg.Decode(ifile)
if err != nil {
return nil
}
} else if filetype == "png" {
img, err = png.Decode(ifile)
if err != nil {
return nil
}
} else if filetype == "gif" {
img, err = gif.Decode(ifile)
if err != nil {
return nil
}
}
// deal with exif
efile, err := os.Open(fpath)
if err != nil {
logrus.Warnf("could not open file for exif decoder: %s", fpath)
}
defer efile.Close()
x, err := exif.Decode(efile)
if err != nil {
if x == nil {
// ignore - image exif data has been already stripped
}
logrus.Errorf("failed reading exif data in [%s]: %s", fpath, err.Error())
}
if x != nil {
orient, _ := x.Get(exif.Orientation)
if orient != nil {
logrus.Infof("%s had orientation %s", fpath, orient.String())
img = reverseOrientation(img, orient.String())
} else {
logrus.Warnf("%s had no orientation - implying 1", fpath)
img = reverseOrientation(img, "1")
}
imaging.Save(img, fpath)
}
return &img
}
// reverseOrientation amply`s what ever operation is necessary to transform given orientation
// to the orientation 1
func reverseOrientation(img image.Image, o string) *image.NRGBA {
switch o {
case "1":
return imaging.Clone(img)
case "2":
return imaging.FlipV(img)
case "3":
return imaging.Rotate180(img)
case "4":
return imaging.Rotate180(imaging.FlipV(img))
case "5":
return imaging.Rotate270(imaging.FlipV(img))
case "6":
return imaging.Rotate270(img)
case "7":
return imaging.Rotate90(imaging.FlipV(img))
case "8":
return imaging.Rotate90(img)
}
logrus.Errorf("unknown orientation %s, expect 1-8", o)
return imaging.Clone(img)
}
You can find this implementation also here:
https://github.com/Macilias/go-images-orientation

Related

how generating transparent GIF with golang using png images?

I have a function that reads a lists of files and create a gif using each file on the list as a frame. But I have a problem. If my frames are png images with transparent background the output GIF have a black background.
I've read on the Internet that image.Paletted is related to the problem but I don't quite understand the issue.
func createAnimation(files []string, directory, filename string) {
outGif := &gif.GIF{}
for _, name := range files {
input := fmt.Sprintf("%s/%s", directory, name)
f, err := os.Open(input)
if err != nil {
log.Fatal(err)
}
imageData, _, err := image.Decode(f)
if err != nil {
log.Fatal(err)
}
buf := bytes.Buffer{}
if err = gif.Encode(&buf, imageData, nil); err != nil {
log.Fatal(err)
}
inGif, err := gif.Decode(&buf)
if err != nil {
log.Fatal(err)
}
f.Close()
outGif.Image = append(outGif.Image, inGif.(*image.Paletted))
outGif.Delay = append(outGif.Delay, 0)
}
output := fmt.Sprintf("FINAL_%s.gif", filename)
f, err := os.Create(output)
if err != nil {
log.Fatal(err)
}
gif.EncodeAll(f, outGif)
err = os.Rename(output, fmt.Sprintf("%s/%s", directory, output))
if err != nil {
log.Fatal(err)
}
}
Files is a slice of filenames e.g. {"base1.png", "base2.png"} and so on.
What should I check or modify in order to generate a transparent gif?
instead of encoding and decoding image you can use draw.Draw nethod. Just create a image.Paleted of size of the loaded image, specify palette you need (include transparent color) and perform draw call.
func Draw(dst Image, r image.Rectangle, src image.Image, sp image.Point, op Op)
// so
draw.Draw(newEmptyPalettedInage, loadedImage.Bounds(), loadedImage, loadedImage.Bounds().Min, 0)

how to read exif data and decode the image from same io.Reader

I'm writing a go application that uses gqlgen for graphQL API.
when a user uploads a file using the api I get it as a type of graphql.Upload
type Upload struct {
File io.Reader
Filename string
Size int64
ContentType string
}
I want to be able to load the exif data of the image, decode the image and rotate it based on the exif orientation.
I use github.com/rwcarlsen/goexif/exif to fetch the exif information and github.com/disintegration/imaging for rotation, but I cannot open graphql.Upload.File twice. it fails the 2hd time.
func updateImage(dir string, id int, imgFile *graphql.Upload) error {
image := dbimage.GetImagePathNoTemplate(dir, id)
imageThumbnail := dbimage.GetImageThumbnailPathNoTemplate(dir, id)
var myImage image2.Image
var err error
switch imgFile.ContentType {
case "image/png":
if myImage, err = png.Decode(imgFile.File); err != nil {
return err
}
break
case "image/jpeg":
if myImage, err = jpeg.Decode(imgFile.File); err != nil {
return err
}
break
case "image/webp":
if myImage, err = webpbin.Decode(imgFile.File); err != nil {
return err
}
break
default:
return errors.Errorf("unknown image mimetype %v", imgFile.ContentType)
}
FAILS HERE: metaData, err := exif.Decode(imgFile.File)
...
}
of course if I extract the exif first and then decode the image, then the image decode fails.
I don't get a full path to the file and I get only one io.Reader. what can I do to fetch both exif and decode the image ?
thanks
guys thank you for your comments.
I noticed that I cannot cast io.Reader to io.Seeker.
for some reason I thought that io.Reader got some sort of rewind method that I missed but this is not the case.
so what I did is read the data to a byte array and created new Reader object whenever I need it:
byteArray, err := ioutil.ReadAll(imgFile.File)
if err != nil {
return err
}
switch imgFile.ContentType {
case "image/png":
if myImage, err = png.Decode(bytes.NewReader(byteArray)); err != nil {
return err
}
break
case "image/jpeg":
if myImage, err = jpeg.Decode(bytes.NewReader(byteArray)); err != nil {
return err
}
break
case "image/webp":
if myImage, err = webpbin.Decode(bytes.NewReader(byteArray)); err != nil {
return err
}
break
default:
return errors.Errorf("unknown image mimetype %v", imgFile.ContentType)
}
metaData, err := exif.Decode(bytes.NewReader(byteArray))

Golang how can I integrate new package into my image upload code

I am new to Golang and have a function that uploads an image to my Amazon s3 account and saves it . Everything works great however I was looking for a way to resize images as some images are up to 4MB in size. I found a package that works correctly for resizing images https://github.com/disintegration/imaging . My question is how can I use the new resized image instead of the old file to upload ? For instance this is my original code that uploads an image and does not resize
func UploadProfile(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
var buff bytes.Buffer
var result string
sess, _ := "access keys"
svc := s3.New(sess)
file, handler, err := r.FormFile("file")
if err != nil {
fmt.Println("Error Uploading Image")
return
}
defer file.Close()
read_file,err := ioutil.ReadAll(file)
fileBytes := bytes.NewReader(read_file)
if err != nil {
fmt.Println("Error Reading file")
return
}
file.Read(read_file)
path := handler.Filename
params := &s3.PutObjectInput{
Bucket: aws.String("amazon-bucket"),
Key: aws.String(path),
Body: fileBytes,
}
resp, err := svc.PutObject(params)
if err != nil {
fmt.Printf("bad response: %s", err)
}
}
The issue I am now having is with this snippet below
file_open,err := imaging.Open("myImage.jpg")
if err != nil {
print("Imaging Open error")
}
new_image := imaging.Resize(file_open, 300, 300, imaging.Lanczos)
errs := imaging.Save(new_image, "dst.jpg")
if errs != nil {
print(err)
}
The code above gets any image and resizes it to 300 by 300 which is exactly what I want, now how can I use that new Image and send it to my s3 image bucket ? The part that confuses me is that the new_image is of type image.NRGBA while the original image is of type Multipart.File . When I upload an image to Amazon it wants to know how many bytes it has and I can't do something like
read_file,err := ioutil.ReadAll(new_image)
if err != nil {
fmt.Println("Error Reading file")
return
}
because it only expects something of type io.Reader is there any work around this ? so that I can save my new Image to Amazon s3 ?
You're saving the new image to a file, then not doing anything with that file. You could read the file off the disk if you want, or you could encode it in memory:
...
resized := imaging.Resize(fileOpen, 300, 300, imaging.Lanczos)
var buf byte.Buffer
err = imaging.Encode(&buf, resized, imaging.JPEG)
if err != nil {
log.Println(err)
return
}
params := &s3.PutObjectInput{
Bucket: aws.String("amazon-bucket"),
Key: aws.String(path),
Body: buf.Bytes(),
}
...

Golang png draw transparent

I am trying to draw multiple transparent images to form a big one and save it as PNG
func generateUserImage(username string, items []models.Item) error {
imageFile, err := os.Create("public/items/users/" + username + ".png")
if err != nil {
return err
}
profileImage := image.NewRGBA(image.Rect(0, 0, 261, 336))
for _, item := range items {
revel.INFO.Println(item)
itemFile, err := os.Open("public/items/universe/" + item.Type + "/" + item.Name + ".png")
if err != nil {
return err
}
itemImage, err := png.Decode(itemFile)
if err != nil {
return err
}
draw.Draw(profileImage, profileImage.Bounds(), itemImage, image.Point{0, 0}, draw.Src)
itemFile.Close()
}
err = png.Encode(imageFile, profileImage)
if err != nil {
return err
}
defer imageFile.Close()
return nil
}
Everything seems to be working fine except that the final image will only contain the LAST image of the range loop (even tho the range loops 5 times). All images are .png and with transparent background. Here is a demo on how images look
You can try to save the image and see that the background is transparent... So I have no idea why the final image only contains 1 image and not all
Thanks
As mentioned in the comments.
draw.Draw(profileImage, profileImage.Bounds(), itemImage, image.Point{0, 0}, draw.Over)

Go image upload

I am currently uploading an image to my server doing the following:
func (base *GuildController) GuildLogo(w http.ResponseWriter, req *http.Request, ps httprouter.Params) {
...
logo, _, err := req.FormFile("logo")
defer logo.Close()
logoGif, format, err := image.Decode(logo)
if err != nil {
base.Error = "Error while decoding your guild logo"
return
}
logoImage, err := os.Create(pigo.Config.String("template")+"/public/guilds/"+ps.ByName("name")+".gif")
if err != nil {
base.Error = "Error while trying to open guild logo image"
return
}
defer logoImage.Close()
//resizedLogo := resize.Resize(64, 64, logoGif, resize.Lanczos3)
err = gif.Encode(logoImage, logoGif, &gif.Options{
256,
nil,
nil,
})
if err != nil {
base.Error = "Error while encoding your guild logo"
return
}
...
}
So everything is working good. But gifs lose the animation.
For example here is a gif I want to upload
And here is the saved one
Not sure what I am doing wrong
As hinted in the comments, you are just working with one frame:
func Decode(r io.Reader) (image.Image, error) Decode reads a GIF image
from r and returns the first embedded image as an image.Image.
But you need
func DecodeAll(r io.Reader) (*GIF, error) DecodeAll reads a GIF image
from r and returns the sequential frames and timing information.
and
func EncodeAll(w io.Writer, g *GIF) error EncodeAll writes the images
in g to w in GIF format with the given loop count and delay between
frames.
Look at this post for details.
Here's an example that slows down the image to 0.5s each frame:
package main
import (
"image/gif"
"os"
)
func main() {
logo, err := os.Open("yay.gif")
if err != nil {
panic(err)
}
defer logo.Close()
inGif, err := gif.DecodeAll(logo)
if err != nil {
panic(err)
}
outGif, err := os.Create("done.gif")
if err != nil {
panic(err)
}
defer outGif.Close()
for i := range inGif.Delay {
inGif.Delay[i] = 50
}
if err := gif.EncodeAll(outGif, inGif); err != nil {
panic(err)
}
}
Results:
Side note
Even if in my browser (Firefox) I see the output image animated, and I can see the the frames in The GIMP, I cannot see it animated on my desktop viewers (gifview, comix). I do not know (yet) what is the cause of this.

Resources