| # The Go image package | |
| 21 Sep 2011 | |
| Tags: image, libraries, technical | |
| Summary: An introduction to 2-D image processing with the Go image package. | |
| OldURL: /go-image-package | |
| Nigel Tao | |
| ## Introduction | |
| The [image](https://golang.org/pkg/image/) and [image/color](https://golang.org/pkg/image/color/) | |
| packages define a number of types: | |
| `color.Color` and `color.Model` describe colors, | |
| `image.Point` and `image.Rectangle` describe basic 2-D geometry, | |
| and `image.Image` brings the two concepts together to represent a rectangular grid of colors. | |
| A [separate article](https://golang.org/doc/articles/image_draw.html) | |
| covers image composition with the [image/draw](https://golang.org/pkg/image/draw/) package. | |
| ## Colors and Color Models | |
| [Color](https://golang.org/pkg/image/color/#Color) is an interface that | |
| defines the minimal method set of any type that can be considered a color: | |
| one that can be converted to red, green, blue and alpha values. | |
| The conversion may be lossy, such as converting from CMYK or YCbCr color spaces. | |
| type Color interface { | |
| // RGBA returns the alpha-premultiplied red, green, blue and alpha values | |
| // for the color. Each value ranges within [0, 0xFFFF], but is represented | |
| // by a uint32 so that multiplying by a blend factor up to 0xFFFF will not | |
| // overflow. | |
| RGBA() (r, g, b, a uint32) | |
| } | |
| There are three important subtleties about the return values. | |
| First, the red, green and blue are alpha-premultiplied: | |
| a fully saturated red that is also 25% transparent is represented by RGBA returning a 75% r. | |
| Second, the channels have a 16-bit effective range: | |
| 100% red is represented by RGBA returning an r of 65535, | |
| not 255, so that converting from CMYK or YCbCr is not as lossy. | |
| Third, the type returned is `uint32`, even though the maximum value is 65535, | |
| to guarantee that multiplying two values together won't overflow. | |
| Such multiplications occur when blending two colors according to an alpha | |
| mask from a third color, | |
| in the style of [Porter and Duff's](https://en.wikipedia.org/wiki/Alpha_compositing) classic algebra: | |
| dstr, dstg, dstb, dsta := dst.RGBA() | |
| srcr, srcg, srcb, srca := src.RGBA() | |
| _, _, _, m := mask.RGBA() | |
| const M = 1<<16 - 1 | |
| // The resultant red value is a blend of dstr and srcr, and ranges in [0, M]. | |
| // The calculation for green, blue and alpha is similar. | |
| dstr = (dstr*(M-m) + srcr*m) / M | |
| The last line of that code snippet would have been more complicated if we | |
| worked with non-alpha-premultiplied colors, | |
| which is why `Color` uses alpha-premultiplied values. | |
| The image/color package also defines a number of concrete types that implement | |
| the `Color` interface. | |
| For example, [`RGBA`](https://golang.org/pkg/image/color/#RGBA) is a struct | |
| that represents the classic "8 bits per channel" color. | |
| type RGBA struct { | |
| R, G, B, A uint8 | |
| } | |
| Note that the `R` field of an `RGBA` is an 8-bit alpha-premultiplied color | |
| in the range [0, 255]. | |
| `RGBA` satisfies the `Color` interface by multiplying that value by 0x101 | |
| to generate a 16-bit alpha-premultiplied color in the range [0, 65535]. | |
| Similarly, the [`NRGBA`](https://golang.org/pkg/image/color/#NRGBA) struct | |
| type represents an 8-bit non-alpha-premultiplied color, | |
| as used by the PNG image format. | |
| When manipulating an `NRGBA`'s fields directly, | |
| the values are non-alpha-premultiplied, but when calling the `RGBA` method, | |
| the return values are alpha-premultiplied. | |
| A [`Model`](https://golang.org/pkg/image/color/#Model) is simply something | |
| that can convert `Color`s to other `Color`s, possibly lossily. | |
| For example, the `GrayModel` can convert any `Color` to a desaturated [`Gray`](https://golang.org/pkg/image/color/#Gray). | |
| A `Palette` can convert any `Color` to one from a limited palette. | |
| type Model interface { | |
| Convert(c Color) Color | |
| } | |
| type Palette []Color | |
| ## Points and Rectangles | |
| A [`Point`](https://golang.org/pkg/image/#Point) is an (x, | |
| y) co-ordinate on the integer grid, with axes increasing right and down. | |
| It is neither a pixel nor a grid square. A `Point` has no intrinsic width, | |
| height or color, but the visualizations below use a small colored square. | |
| type Point struct { | |
| X, Y int | |
| } | |
| .image image/image-package-01.png | |
| p := image.Point{2, 1} | |
| A [`Rectangle`](https://golang.org/pkg/image/#Rectangle) is an axis-aligned | |
| rectangle on the integer grid, | |
| defined by its top-left and bottom-right `Point`. | |
| A `Rectangle` also has no intrinsic color, | |
| but the visualizations below outline rectangles with a thin colored line, | |
| and call out their `Min` and `Max` `Point`s. | |
| type Rectangle struct { | |
| Min, Max Point | |
| } | |
| For convenience, `image.Rect(x0, y0, x1, y1)` is equivalent to `image.Rectangle{image.Point{x0, y0}, image.Point{x1, y1}}`, | |
| but is much easier to type. | |
| A `Rectangle` is inclusive at the top-left and exclusive at the bottom-right. | |
| For a `Point p` and a `Rectangle r`, `p.In(r)` if and only if `r.Min.X <= p.X && p.X < r.Max.X`, | |
| and similarly for `Y`. | |
| This is analogous to how a slice `s[i0:i1]` is inclusive at the low end | |
| and exclusive at the high end. | |
| (Unlike arrays and slices, a `Rectangle` often has a non-zero origin.) | |
| .image image/image-package-02.png | |
| r := image.Rect(2, 1, 5, 5) | |
| // Dx and Dy return a rectangle's width and height. | |
| fmt.Println(r.Dx(), r.Dy(), image.Pt(0, 0).In(r)) // prints 3 4 false | |
| Adding a `Point` to a `Rectangle` translates the `Rectangle`. | |
| Points and Rectangles are not restricted to be in the bottom-right quadrant. | |
| .image image/image-package-03.png | |
| r := image.Rect(2, 1, 5, 5).Add(image.Pt(-4, -2)) | |
| fmt.Println(r.Dx(), r.Dy(), image.Pt(0, 0).In(r)) // prints 3 4 true | |
| Intersecting two Rectangles yields another Rectangle, which may be empty. | |
| .image image/image-package-04.png | |
| r := image.Rect(0, 0, 4, 3).Intersect(image.Rect(2, 2, 5, 5)) | |
| // Size returns a rectangle's width and height, as a Point. | |
| fmt.Printf("%#v\n", r.Size()) // prints image.Point{X:2, Y:1} | |
| Points and Rectangles are passed and returned by value. | |
| A function that takes a `Rectangle` argument will be as efficient as a function | |
| that takes two `Point` arguments, | |
| or four `int` arguments. | |
| ## Images | |
| An [Image](https://golang.org/pkg/image/#Image) maps every grid square | |
| in a `Rectangle` to a `Color` from a `Model`. | |
| "The pixel at (x, y)" refers to the color of the grid square defined by the points (x, | |
| y), (x+1, y), (x+1, y+1) and (x, y+1). | |
| type Image interface { | |
| // ColorModel returns the Image's color model. | |
| ColorModel() color.Model | |
| // Bounds returns the domain for which At can return non-zero color. | |
| // The bounds do not necessarily contain the point (0, 0). | |
| Bounds() Rectangle | |
| // At returns the color of the pixel at (x, y). | |
| // At(Bounds().Min.X, Bounds().Min.Y) returns the upper-left pixel of the grid. | |
| // At(Bounds().Max.X-1, Bounds().Max.Y-1) returns the lower-right one. | |
| At(x, y int) color.Color | |
| } | |
| A common mistake is assuming that an `Image`'s bounds start at (0, 0). | |
| For example, an animated GIF contains a sequence of Images, | |
| and each `Image` after the first typically only holds pixel data for the area that changed, | |
| and that area doesn't necessarily start at (0, 0). | |
| The correct way to iterate over an `Image` m's pixels looks like: | |
| b := m.Bounds() | |
| for y := b.Min.Y; y < b.Max.Y; y++ { | |
| for x := b.Min.X; x < b.Max.X; x++ { | |
| doStuffWith(m.At(x, y)) | |
| } | |
| } | |
| `Image` implementations do not have to be based on an in-memory slice of pixel data. | |
| For example, a [`Uniform`](https://golang.org/pkg/image/#Uniform) is an | |
| `Image` of enormous bounds and uniform color, | |
| whose in-memory representation is simply that color. | |
| type Uniform struct { | |
| C color.Color | |
| } | |
| Typically, though, programs will want an image based on a slice. | |
| Struct types like [`RGBA`](https://golang.org/pkg/image/#RGBA) and [`Gray`](https://golang.org/pkg/image/#Gray) | |
| (which other packages refer to as `image.RGBA` and `image.Gray`) hold slices | |
| of pixel data and implement the `Image` interface. | |
| type RGBA struct { | |
| // Pix holds the image's pixels, in R, G, B, A order. The pixel at | |
| // (x, y) starts at Pix[(y-Rect.Min.Y)*Stride + (x-Rect.Min.X)*4]. | |
| Pix []uint8 | |
| // Stride is the Pix stride (in bytes) between vertically adjacent pixels. | |
| Stride int | |
| // Rect is the image's bounds. | |
| Rect Rectangle | |
| } | |
| These types also provide a `Set(x, y int, c color.Color)` method that allows modifying the image one pixel at a time. | |
| m := image.NewRGBA(image.Rect(0, 0, 640, 480)) | |
| m.Set(5, 5, color.RGBA{255, 0, 0, 255}) | |
| If you're reading or writing a lot of pixel data, | |
| it can be more efficient, but more complicated, | |
| to access these struct type's `Pix` field directly. | |
| The slice-based `Image` implementations also provide a `SubImage` method, | |
| which returns an `Image` backed by the same array. | |
| Modifying the pixels of a sub-image will affect the pixels of the original image, | |
| analogous to how modifying the contents of a sub-slice `s[i0:i1]` will affect | |
| the contents of the original slice `s`. | |
| .image image/image-package-05.png | |
| m0 := image.NewRGBA(image.Rect(0, 0, 8, 5)) | |
| m1 := m0.SubImage(image.Rect(1, 2, 5, 5)).(*image.RGBA) | |
| fmt.Println(m0.Bounds().Dx(), m1.Bounds().Dx()) // prints 8, 4 | |
| fmt.Println(m0.Stride == m1.Stride) // prints true | |
| For low-level code that works on an image's `Pix` field, | |
| be aware that ranging over `Pix` can affect pixels outside an image's bounds. | |
| In the example above, the pixels covered by `m1.Pix` are shaded in blue. | |
| Higher-level code, such as the `At` and `Set` methods or the [image/draw package](https://golang.org/pkg/image/draw/), | |
| will clip their operations to the image's bounds. | |
| ## Image Formats | |
| The standard package library supports a number of common image formats, | |
| such as GIF, JPEG and PNG. | |
| If you know the format of a source image file, | |
| you can decode from an [`io.Reader`](https://golang.org/pkg/io/#Reader) directly. | |
| import ( | |
| "image/jpeg" | |
| "image/png" | |
| "io" | |
| ) | |
| // convertJPEGToPNG converts from JPEG to PNG. | |
| func convertJPEGToPNG(w io.Writer, r io.Reader) error { | |
| img, err := jpeg.Decode(r) | |
| if err != nil { | |
| return err | |
| } | |
| return png.Encode(w, img) | |
| } | |
| If you have image data of unknown format, | |
| the [`image.Decode`](https://golang.org/pkg/image/#Decode) function can detect the format. | |
| The set of recognized formats is constructed at run time and is not limited | |
| to those in the standard package library. | |
| An image format package typically registers its format in an init function, | |
| and the main package will "underscore import" such a package solely for | |
| the side effect of format registration. | |
| import ( | |
| "image" | |
| "image/png" | |
| "io" | |
| _ "code.google.com/p/vp8-go/webp" | |
| _ "image/jpeg" | |
| ) | |
| // convertToPNG converts from any recognized format to PNG. | |
| func convertToPNG(w io.Writer, r io.Reader) error { | |
| img, _, err := image.Decode(r) | |
| if err != nil { | |
| return err | |
| } | |
| return png.Encode(w, img) | |
| } |