Skip to content

In-memory buffers, raw pixels, and host image interop

Copy page

In HTTP services, message queues, and ML inference pipelines, images often already live in memory. In the libvips ecosystem, in-memory I/O splits into two distinct paths: compressed container buffers (such as encoded JPEG or WebP bytes) and uncompressed raw pixel memory (contiguous width × height × bands RGB or RGBA arrays).

Gate of the Orient night photograph read into an in-memory byte slice
Input: JPEG byte buffer1938 × 1292, 610,191 bytes
Night photograph encoded to a WebP buffer in memory
Output: WebP byte buffer1938 × 1292, 399,106 bytes (govips reference output)

Reading and exporting compressed format buffers

Section titled “Reading and exporting compressed format buffers”

When the input is an encoded JPEG, PNG, WebP, or AVIF byte slice, construct the image directly from memory and export it back to a buffer without touching the filesystem.

In govips, ExportWebp (along with ExportJpeg, ExportPng, ExportAvif, and others) returns three values: the encoded []byte, the post-export *vips.ImageMetadata, and an error. In sharp, .toBuffer() (or .toBuffer({ resolveWithObject: true }) for { data, info }) and .toUint8Array() return the encoded output in memory.

package main
import (
"fmt"
"os"
"github.com/davidbyttow/govips/v2/vips"
)
func transcodeBuffer(inputBytes []byte) ([]byte, *vips.ImageMetadata, error) {
params := vips.NewImportParams()
params.Access.Set(vips.AccessSequential)
image, err := vips.LoadImageFromBuffer(inputBytes, params)
if err != nil {
return nil, nil, err
}
defer image.Close()
export := vips.NewWebpExportParams()
export.Quality = 82
return image.ExportWebp(export)
}
func main() {
if err := vips.Startup(nil); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
defer vips.Shutdown()
src, err := os.ReadFile("input.jpg")
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
out, meta, err := transcodeBuffer(src)
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
fmt.Printf("encoded %d bytes (%dx%d)\n", len(out), meta.Width, meta.Height)
}

When interfacing with custom renderers, video decoders, or ML tensors, bypass PNG/JPEG container encoding and pass interleaved raw pixel memory directly.

Starting in govips v2.19.0, vips.NewImageFromMemory(pixels, width, height, bands, format, interpretation) pairs with image.ToBytes() for raw pixel import and export.

// Allocate a 64×64, 3-band (RGB), 8-bit unsigned raw pixel buffer
width, height, bands := 64, 64, 3
rawPixels := make([]byte, width*height*bands)
for i := 0; i < len(rawPixels); i += 3 {
rawPixels[i] = 29 // R
rawPixels[i+1] = 45 // G
rawPixels[i+2] = 53 // B
}
// govips v2.19.0+: construct an ImageRef directly from raw pixels (with BandFormat and Interpretation)
img, err := vips.NewImageFromMemory(rawPixels, width, height, bands, vips.BandFormatUchar, vips.InterpretationSRGB)
if err != nil {
return err
}
defer img.Close()
// Export processed pixels back to an uncompressed raw byte slice
outRaw, err := img.ToBytes()
if err != nil {
return err
}
_ = outRaw

Zero-encode interop with host language image types

Section titled “Zero-encode interop with host language image types”

In Go and Python, you often need to move between libvips images and standard ecosystem types (image.Image in Go, numpy.ndarray or PIL.Image in Python). Never encode to PNG and decode back just to cross that boundary — use the direct memory bridge instead:

Host type Recommended API Behavior
Go image.Image → govips vips.NewImageFromGoImage(goImg) Builds *vips.ImageRef directly from pixel values without container encoding
govips → Go image.Image img.ToGoImage() Converts ImageRef pixels directly into a standard library image.Image
Python numpy.ndarray ↔ pyvips Image.new_from_array(arr) / np.asarray(img) Uses PEP 3118 buffer protocol and __array_interface__
Node.js TypedArray ↔ sharp sharp(uint8Array, { raw }) / .raw().toUint8Array() Transfers raw interleaved pixels directly to/from Uint8Array
// Go standard library image.Image -> govips ImageRef
vipsImg, err := vips.NewImageFromGoImage(stdImg)
if err != nil {
return err
}
defer vipsImg.Close()
// govips ImageRef -> Go standard library image.Image
outStdImg, err := vipsImg.ToGoImage()
if err != nil {
return err
}
_ = outStdImg