Small Go package for fast high-level image processing using libvips via C bindings. Provides a simple, elegant and fluent programmatic API.
bimg was designed to be a small and efficient library supporting a common set of image operations such as crop, resize, rotate, zoom or watermark. It can read JPEG, PNG, WEBP and TIFF formats and output to JPEG, PNG and WEBP, including conversion between them.
bimg uses internally libvips, a powerful library written in C for image processing which requires a low memory footprint
and it's typically 4x faster than using the quickest ImageMagick and GraphicsMagick settings or Go native image
package, and in some cases it's even 8x faster processing JPEG images.
To get started you could take a look to the examples and API documentation.
If you're looking for a HTTP based image processing solution, see imaginary. bimg was heavily inspired in sharp, its homologous package built for node.js.
- libvips v7.40.0+ (7.42.0+ recommended)
- C compatible compiler such as gcc 4.6+ or clang 3.0+
- Go 1.3+
go get -u gopkg.in/h2non/bimg.v0
Run the following script as sudo
(supports OSX, Debian/Ubuntu, Redhat, Fedora, Amazon Linux):
curl -s https://raw.githubusercontent.com/lovell/sharp/master/preinstall.sh | sudo bash -
If you wanna take the advantage of OpenSlide, simply add --with-openslide
to enable it:
curl -s https://raw.githubusercontent.com/lovell/sharp/master/preinstall.sh | sudo bash -s --with-openslide
The install script requires curl
and pkg-config
For platform specific installations, see Mac OS tips or Windows tips
- Resize
- Enlarge
- Crop
- Rotate (with auto-rotate based on EXIF orientation)
- Flip (with auto-flip based on EXIF metadata)
- Flop
- Zoom
- Thumbnail
- Extract area
- Watermark (text-based)
- Gaussian blur effect
- Custom output color space (RGB, grayscale...)
- Format conversion (with additional quality/compression settings)
- EXIF metadata (size, alpha channel, profile, orientation...)
libvips is probably the faster open source solution for image processing. Here you can see some performance test comparisons for multiple scenarios:
Tested using Go 1.4.1 and libvips-7.42.3 in OSX i7 2.7Ghz
BenchmarkResizeLargeJpeg 50 43400480 ns/op
BenchmarkResizePng 20 57592174 ns/op
BenchmarkResizeWebP 500 2872295 ns/op
BenchmarkConvertToJpeg 30 41835497 ns/op
BenchmarkConvertToPng 10 153382204 ns/op
BenchmarkConvertToWebp 10000 264542 ns/op
BenchmarkCropJpeg 30 52267699 ns/op
BenchmarkCropPng 30 56477454 ns/op
BenchmarkCropWebP 5000 274302 ns/op
BenchmarkExtractJpeg 50 27827670 ns/op
BenchmarkExtractPng 2000 769761 ns/op
BenchmarkExtractWebp 3000 513954 ns/op
BenchmarkZoomJpeg 10 159272494 ns/op
BenchmarkZoomPng 20 65771476 ns/op
BenchmarkZoomWebp 5000 368327 ns/op
BenchmarkWatermarkJpeg 100 10026033 ns/op
BenchmarkWatermarPng 200 7350821 ns/op
BenchmarkWatermarWebp 200 9014197 ns/op
ok 30.698s
import (
"fmt"
"os"
"gopkg.in/h2non/bimg.v0"
)
buffer, err := bimg.Read("image.jpg")
if err != nil {
fmt.Fprintln(os.Stderr, err)
}
newImage, err := bimg.NewImage(buffer).Resize(800, 600)
if err != nil {
fmt.Fprintln(os.Stderr, err)
}
size, err := bimg.NewImage(newImage).Size()
if size.Width == 400 && size.Height == 300 {
fmt.Println("The image size is valid")
}
bimg.Write("new.jpg", newImage)
buffer, err := bimg.Read("image.jpg")
if err != nil {
fmt.Fprintln(os.Stderr, err)
}
newImage, err := bimg.NewImage(buffer).Rotate(90)
if err != nil {
fmt.Fprintln(os.Stderr, err)
}
bimg.Write("new.jpg", newImage)
buffer, err := bimg.Read("image.jpg")
if err != nil {
fmt.Fprintln(os.Stderr, err)
}
newImage, err := bimg.NewImage(buffer).Convert(bimg.PNG)
if err != nil {
fmt.Fprintln(os.Stderr, err)
}
if bimg.NewImage(newImage).Type() == "png" {
fmt.Fprintln(os.Stderr, "The image was converted into png")
}
buffer, err := bimg.Read("image.jpg")
if err != nil {
fmt.Fprintln(os.Stderr, err)
}
newImage, err := bimg.NewImage(buffer).ForceResize(1000, 500)
if err != nil {
fmt.Fprintln(os.Stderr, err)
}
size := bimg.Size(newImage)
if size.Width != 1000 || size.Height != 500 {
fmt.Fprintln(os.Stderr, "Incorrect image size")
}
buffer, err := bimg.Read("image.jpg")
if err != nil {
fmt.Fprintln(os.Stderr, err)
}
newImage, err := bimg.NewImage(buffer).Colourspace(bimg.INTERPRETATION_B_W)
if err != nil {
fmt.Fprintln(os.Stderr, err)
}
colourSpace, _ := bimg.ImageInterpretation(newImage)
if colourSpace != bimg.INTERPRETATION_B_W {
fmt.Fprintln(os.Stderr, "Invalid colour space")
}
See Options struct to discover all the available fields
options := bimg.Options{
Width: 800,
Height: 600,
Crop: true,
Quality: 95,
Rotate: 180,
Interlace: true,
}
buffer, err := bimg.Read("image.jpg")
if err != nil {
fmt.Fprintln(os.Stderr, err)
}
newImage, err := bimg.NewImage(buffer).Process(options)
if err != nil {
fmt.Fprintln(os.Stderr, err)
}
bimg.Write("new.jpg", newImage)
buffer, err := bimg.Read("image.jpg")
if err != nil {
fmt.Fprintln(os.Stderr, err)
}
watermark := bimg.Watermark{
Text: "Chuck Norris (c) 2315",
Opacity: 0.25,
Width: 200,
DPI: 100,
Margin: 150,
Font: "sans bold 12",
Background: bimg.Color{255, 255, 255},
}
newImage, err := bimg.NewImage(buffer).Watermark(watermark)
if err != nil {
fmt.Fprintln(os.Stderr, err)
}
bimg.Write("new.jpg", newImage)
buffer, err := bimg.Read("image.jpg")
if err != nil {
fmt.Fprintln(os.Stderr, err)
}
image := bimg.NewImage(buffer)
// first crop image
_, err := image.CropByWidth(300)
if err != nil {
fmt.Fprintln(os.Stderr, err)
}
// then flip it
newImage, err := image.Flip()
if err != nil {
fmt.Fprintln(os.Stderr, err)
}
// save the cropped and flipped image
bimg.Write("new.jpg", newImage)
Run the process passing the DEBUG
environment variable
DEBUG=bimg ./app
Enable libvips traces (note that a lot of data will be written in stdout):
VIPS_TRACE=1 ./app
const HasMagickSupport = int(C.VIPS_MAGICK_SUPPORT) == 1
const Version = "0.1.20"
const WATERMARK_FONT = "sans 10"
func ColourspaceIsSupported(buf []byte) (bool, error)
Check in the image colourspace is supported by libvips
func DetermineImageTypeName(buf []byte) string
Determines the image type format by name (jpeg, png, webp or tiff)
func Initialize()
Explicit thread-safe start of libvips. Only call this function if you've previously shutdown libvips
func IsTypeNameSupported(t string) bool
Check if a given image type name is supported
func IsTypeSupported(t ImageType) bool
Check if a given image type is supported
func Read(path string) ([]byte, error)
func Resize(buf []byte, o Options) ([]byte, error)
func Shutdown()
Thread-safe function to shutdown libvips. You can call this to drop caches as well. If libvips was already initialized, the function is no-op
func VipsDebugInfo()
Output to stdout vips collected data. Useful for debugging
func Write(path string, buf []byte) error
type Angle int
const (
D0 Angle = 0
D90 Angle = 90
D180 Angle = 180
D270 Angle = 270
)
type Color struct {
R, G, B uint8
}
Color represents a traditional RGB color scheme
type Direction int
const (
HORIZONTAL Direction = C.VIPS_DIRECTION_HORIZONTAL
VERTICAL Direction = C.VIPS_DIRECTION_VERTICAL
)
type GaussianBlur struct {
Sigma float64
MinAmpl float64
}
type Gravity int
const (
CENTRE Gravity = iota
NORTH
EAST
SOUTH
WEST
)
type Image struct {
}
func NewImage(buf []byte) *Image
Creates a new image
func (i *Image) Colourspace(c Interpretation) ([]byte, error)
Colour space conversion
func (i *Image) ColourspaceIsSupported() (bool, error)
Check if the current image has a valid colourspace
func (i *Image) Convert(t ImageType) ([]byte, error)
Convert image to another format
func (i *Image) Crop(width, height int, gravity Gravity) ([]byte, error)
Crop the image to the exact size specified
func (i *Image) CropByHeight(height int) ([]byte, error)
Crop an image by height (auto width)
func (i *Image) CropByWidth(width int) ([]byte, error)
Crop an image by width (auto height)
func (i *Image) Enlarge(width, height int) ([]byte, error)
Enlarge the image by width and height. Aspect ratio is maintained
func (i *Image) EnlargeAndCrop(width, height int) ([]byte, error)
Enlarge the image by width and height with additional crop transformation
func (i *Image) Extract(top, left, width, height int) ([]byte, error)
Extract area from the by X/Y axis
func (i *Image) Flip() ([]byte, error)
Flip the image about the vertical Y axis
func (i *Image) Flop() ([]byte, error)
Flop the image about the horizontal X axis
func (i *Image) ForceResize(width, height int) ([]byte, error)
Force resize with custom size (aspect ratio won't be maintained)
func (i *Image) Image() []byte
Get image buffer
func (i *Image) Interpretation() (Interpretation, error)
Get the image interpretation type See: http://www.vips.ecs.soton.ac.uk/supported/current/doc/html/libvips/VipsImage.html#VipsInterpretation
func (i *Image) Metadata() (ImageMetadata, error)
Get image metadata (size, alpha channel, profile, EXIF rotation)
func (i *Image) Process(o Options) ([]byte, error)
Transform the image by custom options
func (i *Image) Resize(width, height int) ([]byte, error)
Resize the image to fixed width and height
func (i *Image) ResizeAndCrop(width, height int) ([]byte, error)
Resize the image to fixed width and height with additional crop transformation
func (i *Image) Rotate(a Angle) ([]byte, error)
Rotate the image by given angle degrees (0, 90, 180 or 270)
func (i *Image) Size() (ImageSize, error)
Get image size
func (i *Image) Thumbnail(pixels int) ([]byte, error)
Thumbnail the image by the a given width by aspect ratio 4:4
func (i *Image) Type() string
Get image type format (jpeg, png, webp, tiff)
func (i *Image) Watermark(w Watermark) ([]byte, error)
Add text as watermark on the given image
func (i *Image) Zoom(factor int) ([]byte, error)
Zoom the image by the given factor. You should probably call Extract() before
type ImageMetadata struct {
Orientation int
Channels int
Alpha bool
Profile bool
Type string
Space string
Colourspace string
Size ImageSize
}
func Metadata(buf []byte) (ImageMetadata, error)
Extract the image metadata (size, type, alpha channel, profile, EXIF orientation...)
type ImageSize struct {
Width int
Height int
}
func Size(buf []byte) (ImageSize, error)
Get the image size by width and height pixels
type ImageType int
const (
UNKNOWN ImageType = iota
JPEG
WEBP
PNG
TIFF
MAGICK
)
func DetermineImageType(buf []byte) ImageType
Determines the image type format (jpeg, png, webp or tiff)
type Interpolator int
const (
BICUBIC Interpolator = iota
BILINEAR
NOHALO
)
func (i Interpolator) String() string
type Interpretation int
Image interpretation type See: http://www.vips.ecs.soton.ac.uk/supported/current/doc/html/libvips/VipsImage.html#VipsInterpretation
const (
INTERPRETATION_ERROR Interpretation = C.VIPS_INTERPRETATION_ERROR
INTERPRETATION_MULTIBAND Interpretation = C.VIPS_INTERPRETATION_MULTIBAND
INTERPRETATION_B_W Interpretation = C.VIPS_INTERPRETATION_B_W
INTERPRETATION_CMYK Interpretation = C.VIPS_INTERPRETATION_CMYK
INTERPRETATION_RGB Interpretation = C.VIPS_INTERPRETATION_RGB
INTERPRETATION_sRGB Interpretation = C.VIPS_INTERPRETATION_sRGB
INTERPRETATION_RGB16 Interpretation = C.VIPS_INTERPRETATION_RGB16
INTERPRETATION_GREY16 Interpretation = C.VIPS_INTERPRETATION_GREY16
INTERPRETATION_scRGB Interpretation = C.VIPS_INTERPRETATION_scRGB
INTERPRETATION_LAB Interpretation = C.VIPS_INTERPRETATION_LAB
INTERPRETATION_XYZ Interpretation = C.VIPS_INTERPRETATION_XYZ
)
func ImageInterpretation(buf []byte) (Interpretation, error)
Get the image interpretation type See: http://www.vips.ecs.soton.ac.uk/supported/current/doc/html/libvips/VipsImage.html#VipsInterpretation
type Options struct {
Height int
Width int
AreaHeight int
AreaWidth int
Top int
Left int
Extend int
Quality int
Compression int
Zoom int
Crop bool
Enlarge bool
Embed bool
Flip bool
Flop bool
Force bool
NoAutoRotate bool
NoProfile bool
Interlace bool
Rotate Angle
Gravity Gravity
Watermark Watermark
Type ImageType
Interpolator Interpolator
Interpretation Interpretation
GaussianBlur GaussianBlur
}
type VipsMemoryInfo struct {
Memory int64
MemoryHighwater int64
Allocations int64
}
func VipsMemory() VipsMemoryInfo
Get memory info stats from vips (cache size, memory allocs...)
type Watermark struct {
Width int
DPI int
Margin int
Opacity float32
NoReplicate bool
Text string
Font string
Background Color
}
Special thanks to people who freely contributed to improve bimg
in some or other way.
MIT - Tomas Aparicio