The following Go program will open an image, resize it to be 300 pixels wide with a variable height that preserves the aspect ratio, and write the result to a new file. The intent of this is to be used for a photo-sharing application where the user will see a photo gallery with lots of thumbnails.

The width is more important than height when displaying images in a browser. A fixed width allows for more predictability in making columns of pictures, for instance, and it is easier to design for both desktop and mobile use.

Here is the code:

package main

import (
    "image"
    "image/jpeg"
    "image/png"
    "io"
    "math"
    "os"

    "golang.org/x/image/draw"
)

func main() {
    input, err := os.Open("input.jpg")
    check(err)
    defer input.Close()

    output, err := os.Create("resized.jpg")
    check(err)

    err = thumbnail(input, "image/jpeg", 300, output)
    check(err)

    err = output.Close()
    check(err)
}

// thumbnail creates a resized image from the reader and writes it to
// the writer. The mimetype determines how the image will be decoded
// and must be either "image/jpeg" or "image/png". The desired width
// of the thumbnail is specified in pixels, and the resulting height
// will be calculated to preserve the aspect ratio.
func thumbnail(r io.Reader, w io.Writer, mimetype string, width int) error {
    var src image.Image
    var err error

    switch mimetype {
    case "image/jpeg":
        src, err = jpeg.Decode(r)
    case "image/png":
        src, err = png.Decode(r)
    }

    if err != nil {
        return err
    }

    ratio := (float64)(src.Bounds().Max.Y) / (float64)(src.Bounds().Max.X)
    height := int(math.Round(float64(width) * ratio))

    dst := image.NewRGBA(image.Rect(0, 0, width, height))

    draw.NearestNeighbor.Scale(dst, dst.Rect, src, src.Bounds(), draw.Over, nil)

    err = jpeg.Encode(w, dst, nil)
    if err != nil {
        return err
    }

    return nil
}

func check(err error) {
    if err != nil {
        panic(err)
    }
}

A few notes:

The resizing is done with the x/image/draw library using its NearestNeighbor implementation. This is the quick-and-dirty approach, but for prettier results (at the cost of more processing time), there are alternatives: ApproxBiLinear, BiLinear, and CatmullRom.

I only care about JPEG and PNG files, though one could also easily support GIF as well by including image/gif and using Go’s image/gif in the exact same way as the jpeg.Decode and png.Decode functions.

All thumbnails are encoded as lossy JPEG files instead of lossless PNG, since scaling the image down is already a lossy process. The purpose of a thumbnail is not to look pristine in its own right, but rather to give the viewer a quick preview.