Reputation: 1081
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
}
}
Upvotes: 5
Views: 3256
Reputation: 3533
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
Upvotes: 2
Reputation: 9623
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
Upvotes: 8