Golang png draw transparent - image

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)

Related

Encoding with image/jpeg cause image saturation / wrong pixels

I have been having this problem for some time: I'm creating a module to process images, one of my functions is to go through each pixel of an image and invert colors. The function returns the expected results when encoding .png images, but it "saturates" .jpeg/jpg images.
Example when processing a .png image (correct): https://i.imgur.com/DG35RsR.png
Example when processing a .jpeg image (error): https://i.imgur.com/DZQmxJ2.png
I was researching and the closest I found to my problem, although it's not an answer, is this issue from the Go repository: https://github.com/golang/go/issues/23936
// InvertColors function
func InvertColors(img image.Image) image.Image {
bounds := img.Bounds()
width := bounds.Max.X
height := bounds.Max.Y
inverted := image.NewRGBA(bounds)
for y := bounds.Min.Y; y < height; y++ {
for x := bounds.Min.X; x < width; x++ {
r, g, b, a := img.At(x, y).RGBA()
c := color.RGBA{uint8(255 - r), uint8(255 - g), uint8(255 - b), uint8(a)}
inverted.SetRGBA(x, y, c)
}
}
return inverted
}
// main example
func main() {
link := "https://i.imgur.com/n5hsdl4.jpg"
img, err := GetImageFromURL(link)
if err != nil {
panic(err)
}
buf := new(bytes.Buffer)
Encode(buf, img, "jpg")
ioutil.WriteFile("temp.jpg", buf.Bytes(), 0666)
invImg := InvertColors(img)
buf = new(bytes.Buffer)
Encode(buf, invImg, "jpg")
ioutil.WriteFile("temp2.jpg", buf.Bytes(), 0666)
}
// GetImageFromURL
func GetImageFromURL(link string) (image.Image, error) {
_, format, err := ParseURL(link)
if err != nil {
return nil, err
}
req, err := http.NewRequest("GET", link, nil)
if err != nil {
return nil, err
}
// Required to make a request.
req.Close = true
req.Header.Set("Content-Type", "image/"+format)
res, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
defer res.Body.Close()
b, err := ioutil.ReadAll(res.Body)
if err != nil {
return nil, err
}
img, err := Decode(bytes.NewReader(b), format)
if err != nil {
return nil, err
}
return img, nil
}
func ParseURL(link string) (u *url.URL, format string, err error) {
u, err = url.Parse(link)
if err != nil {
return u, "", err
}
format = u.Path[len(u.Path)-4:]
if strings.Contains(format, ".") {
format = strings.Split(format, ".")[1]
}
if format != "png" && format != "jpg" && format != "jpeg" {
return u, "", fmt.Errorf("Unsupported format: %s", format)
}
return u, format, nil
}
func Decode(r io.Reader, format string) (img image.Image, err error) {
if format == "png" {
img, err = png.Decode(r)
if err != nil {
return nil, err
}
} else if format == "jpg" || format == "jpeg" {
img, err = jpeg.Decode(r)
if err != nil {
return nil, err
}
} else {
return nil, fmt.Errorf("Unsupported format: %s", format)
}
return img, nil
}
You have two bugs in your code related to color-handling (the second of which is probably not relevant).
First, the RGBA() method returns 16-bit R, G, B, A, but you're treating them like 8-bit values.
Second, color.RGBA values are alpha-premultiplied, so the inverse of (R, G, B, A) is (A-R, A-G, A-B, A) and not (MAX-R, MAX-G, MAX-B, A). This is probably not relevant, because it looks like your picture does not have any significant alpha.
One way to fix to your code is to replace this:
r, g, b, a := img.At(x, y).RGBA()
c := color.RGBA{uint8(255 - r), uint8(255 - g), uint8(255 - b), uint8(a)}
with this:
r, g, b, a := img.At(x, y).RGBA()
c := color.RGBA{uint8((a - r)>>8), uint8((a - g)>>8), uint8((a - b)>>8), uint8(a>>8)}
(Note, that you may find that first converting your image to image.NRGBA (if it's not already) and then iterating over underlying byte slice that stores the (non-alpha-premultiplied) RGBA channels for the image is much faster than using the more abstract interfaces provided by the image and color packages.)

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)

Convert base64 string to JPG

I am getting the image as base64 string ( dataurl ), Below is my function that converts the dataurl into the image,
Now if the image is png then png library will create the image and jpg library will throw an error and vice versa.
Problem is when i upload png it works well but when i upload jpg file
returns this error
invalid JPEG format: missing SOI marker
func (ProfileController) SetProfilePic(w http.ResponseWriter, r *http.Request) {
session, _ := session.UserSession.Get(r, "mvc-user-session")
var fieldMapForBasic map[string]*json.RawMessage
var f *os.File
data, _ := ioutil.ReadAll(r.Body)
json.Unmarshal(data, &fieldMapForBasic)
image, _ := json.Marshal(fieldMapForBasic["image"])
coI := strings.Index(string(image), ",")
rawImage := string(image)[coI+1:]
// Encoded Image DataUrl //
unbased, _ := base64.StdEncoding.DecodeString(string(rawImage))
res := bytes.NewReader(unbased)
path, _ := os.Getwd()
// Path to store the image //
newpath := filepath.Join(path + "/storage", strconv.Itoa(session.Values["id"].(int)))
os.MkdirAll(newpath, os.ModePerm)
uid := uuid.NewV4()
fmt.Println(newpath)
// if image is png this function will create the image from dataurl string //
pngI, errPng := png.Decode(res)
if errPng == nil {
f, _ = os.OpenFile(newpath + "/" + uid.String() + ".png", os.O_WRONLY | os.O_CREATE, 0777)
png.Encode(f, pngI)
fmt.Println("Png generated")
user := model.User{}
user.ProfilePic = "storage/" + strconv.Itoa(session.Values["id"].(int)) + "/" + uid.String()
session.Values["profile_pic"] = user.ProfilePic
session.Save(r, w)
database.Connection.Id(session.Values["id"].(int)).Update(&user)
} else {
fmt.Println(errPng.Error())
}
// If image is Jpg this will generate the Image //
jpgI, errJpg := jpeg.Decode(res)
if errJpg == nil {
f, _ = os.OpenFile(newpath + "/" + uid.String() + ".jpg", os.O_WRONLY | os.O_CREATE, 0777)
jpeg.Encode(f, jpgI, &jpeg.Options{Quality: 75})
fmt.Println("Jpg created")
} else {
fmt.Println(errJpg.Error())
}
defer func() {
f.Close()
}()
render.Json(w, "ok")
}
Input dataurl is:
"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAYABgAAD/2wBDAAIBAQIBAQICAgICAgICAwUDAwMDAwYEBAMFBwYHBwcGBwcICQsJCAgKCAcHCg0KCgsMDAwMBwkODw0MDgsMDAz/2wBDAQICAgMDAwYDAwYMCAcIDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAz/wAARCADeAN4DASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwDtKKKK/cD8rCiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKAP/Z"
This might help
You're passing your io.Reader to png.Decode(), which begins consuming the reader, only to discover that the input is not a valid PNG, so returns an error.
Then your partly-consumed reader is passed to jpeg.Decode(), which reads the data not yet read, which is not a valid JPEG, and returns the error you observe.
You need to create a new reader for each decoder:
pngI, errPng := png.Decode(bytes.NewReader(unbased))
// ...
jpgI, errJpg := jpeg.Decode(bytes.NewReader(unbased))
Or better yet, consider the MIME type, and only call the proper decoder:
switch strings.TrimSuffix(image[5:coI], ";base64") {
case "image/png":
pngI, err = png.Decode(res)
// ...
case "image/jpeg":
jpgI, err = jpeg.Decode(res)
// ...
}
Here's something which may help, demonstrates quite a few other things too (you need to have two files: flower.jpg and mouse.png to make this work, available in the repository https://github.com/rravishankar/golangtraining/tree/master/test/jpegencode):
package main
import (
"bufio"
"encoding/base64"
"fmt"
"image"
"image/jpeg"
"image/png"
"strings"
"log"
"os"
)
func main() {
createJpg()
createPng()
base64toPng()
base64toJpg(getJPEGbase64("flower.jpg"))
}
//Take an existing jpg srcFileName and decode/encode it
func createJpg() {
srcFileName := "flower.jpg"
dstFileName := "newFlower.jpg"
// Decode the JPEG data. If reading from file, create a reader with
reader, err := os.Open(srcFileName)
if err != nil {
log.Fatal(err)
}
defer reader.Close()
//Decode from reader to image format
m, formatString, err := image.Decode(reader)
if err != nil {
log.Fatal(err)
return
}
fmt.Println("Got format String", formatString)
fmt.Println(m.Bounds())
//Encode from image format to writer
f, err := os.OpenFile(dstFileName, os.O_WRONLY|os.O_CREATE, 0777)
if err != nil {
log.Fatal(err)
return
}
err = jpeg.Encode(f, m, &jpeg.Options{Quality: 75})
if err != nil {
log.Fatal(err)
return
}
fmt.Println("Jpg file", dstFileName, "created")
}
//Take an existing png srcFileName and decode/encode it
func createPng() {
srcFileName := "mouse.png"
dstFileName := "newMouse.png"
reader, err := os.Open(srcFileName)
if err != nil {
log.Fatal(err)
}
defer reader.Close()
//Decode from reader to image format
m, formatString, err := image.Decode(reader)
if err != nil {
log.Fatal(err)
return
}
fmt.Println("Got format String", formatString)
fmt.Println(m.Bounds())
//Encode from image format to writer
f, err := os.OpenFile(dstFileName, os.O_WRONLY|os.O_CREATE, 0777)
if err != nil {
log.Fatal(err)
return
}
err = png.Encode(f, m)
if err != nil {
log.Fatal(err)
return
}
fmt.Println("Png file", dstFileName, "created")
}
//Converts pre-existing base64 data (found in example of https://golang.org/pkg/image/#Decode) to test.png
func base64toPng() {
const data = `
/9j/4AAQSkZJRgABAQIAHAAcAAD/2wBDABALDA4MChAODQ4SERATGCgaGBYWGDEjJR0oOjM9PDkzODdA
SFxOQERXRTc4UG1RV19iZ2hnPk1xeXBkeFxlZ2P/2wBDARESEhgVGC8aGi9jQjhCY2NjY2NjY2NjY2Nj
Y2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2P/wAARCABnAJYDASIAAhEBAxEB/8QA
HwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIh
MUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVW
V1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXG
x8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQF
BgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAV
YnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOE
hYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq
8vP09fb3+Pn6/9oADAMBAAIRAxEAPwDlwKMD0pwzSiuK57QzGDxS7D6in8Y5ximnAPUfSlcq4m3ilUYp
2OKXHvRcVxnTtS7c07HNFK4DQPakC4PNOA+tOx70XAjK/So5gBGP94fzqfvUVx/qxx/EP51UXqRP4WSE
cmgjilP3jSEZqS0IO/NGDnpUiocDg/McDjvV6HTPOdVWYgsM5KcfzzQ2JySM2jp6VYu7SWzmMUwG4cgj
kMPUVBjjtTGtRu0Zopw+lFFxhinrGzuqqMsxAA9yaXFSRv5cqSEcIwYj6GpuZ30O30fSLKzhUpbpNMv3
5XGTn29BV28jt7pPLuIVljPBBFVreYx+VbqAjycgt3x14zRcNOxGyVFHQkIc/wA61exyKLbuzjdZ046d
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
`
reader := base64.NewDecoder(base64.StdEncoding, strings.NewReader(data))
m, formatString, err := image.Decode(reader)
if err != nil {
log.Fatal(err)
}
bounds := m.Bounds()
fmt.Println(bounds, formatString)
//Encode from image format to writer
pngFilename := "test.png"
f, err := os.OpenFile(pngFilename, os.O_WRONLY|os.O_CREATE, 0777)
if err != nil {
log.Fatal(err)
return
}
err = png.Encode(f, m)
if err != nil {
log.Fatal(err)
return
}
fmt.Println("Png file", pngFilename, "created")
}
//Given a base64 string of a JPEG, encodes it into an JPEG image test.jpg
func base64toJpg(data string) {
reader := base64.NewDecoder(base64.StdEncoding, strings.NewReader(data))
m, formatString, err := image.Decode(reader)
if err != nil {
log.Fatal(err)
}
bounds := m.Bounds()
fmt.Println("base64toJpg", bounds, formatString)
//Encode from image format to writer
pngFilename := "test.jpg"
f, err := os.OpenFile(pngFilename, os.O_WRONLY|os.O_CREATE, 0777)
if err != nil {
log.Fatal(err)
return
}
err = jpeg.Encode(f, m, &jpeg.Options{Quality: 75})
if err != nil {
log.Fatal(err)
return
}
fmt.Println("Jpg file", pngFilename, "created")
}
//Gets base64 string of an existing JPEG file
func getJPEGbase64(fileName string) string {
imgFile, err := os.Open(fileName)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
defer imgFile.Close()
// create a new buffer base on file size
fInfo, _ := imgFile.Stat()
var size = fInfo.Size()
buf := make([]byte, size)
// read file content into buffer
fReader := bufio.NewReader(imgFile)
fReader.Read(buf)
imgBase64Str := base64.StdEncoding.EncodeToString(buf)
//fmt.Println("Base64 string is:", imgBase64Str)
return imgBase64Str
}
You may also want to read the example in https://golang.org/pkg/image/#Decode
Hope this helps.

Golang how can I upload external images without rotating them

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

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(),
}
...

Resources