In-memory buffers, raw pixels, and host image interop
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).


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)}import { readFile } from 'node:fs/promises';import sharp from 'sharp';
const inputBuffer = await readFile('input.jpg');
const { data, info } = await sharp(inputBuffer, { sequentialRead: true }) .webp({ quality: 82 }) .toBuffer({ resolveWithObject: true });
console.log(`encoded ${data.byteLength} bytes (${info.width}x${info.height})`);from pathlib import Pathimport pyvips
input_bytes = Path("input.jpg").read_bytes()
image = pyvips.Image.new_from_buffer(input_bytes, "", access="sequential")output_bytes = image.write_to_buffer(".webp", Q=82)print(f"encoded {len(output_bytes)} bytes ({image.width}x{image.height})")#!/usr/bin/env bashset -euo pipefail
# The CLI uses file descriptors (stdin/stdout) with format suffixes for fileless pipelinesvips thumbnail_source '[descriptor=0]' '.webp[Q=82]' 1938 < input.jpg > output.webpDirect raw uncompressed pixel memory
Section titled “Direct raw uncompressed pixel memory”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 bufferwidth, height, bands := 64, 64, 3rawPixels := 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 sliceoutRaw, err := img.ToBytes()if err != nil { return err}_ = outRawimport sharp from 'sharp';
const width = 64;const height = 64;const channels = 3;const rawPixels = Buffer.alloc(width * height * channels, 0x2d);
const { data, info } = await sharp(rawPixels, { raw: { width, height, channels },}) .raw() .toBuffer({ resolveWithObject: true });
console.log(info.width, info.height, info.channels, data.byteLength);import pyvips
width, height, bands = 64, 64, 3raw_pixels = bytes([29, 45, 53] * (width * height))
img = pyvips.Image.new_from_memory(raw_pixels, width, height, bands, "uchar")out_raw = img.write_to_memory()# Dump raw headerless pixel bytes and reload them by specifying dimensions, bands, and formatvips rawsave input.jpg raw.binvips rawload raw.bin from-raw.webp 1938 1292 3 --format ucharZero-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 ImageRefvipsImg, err := vips.NewImageFromGoImage(stdImg)if err != nil { return err}defer vipsImg.Close()
// govips ImageRef -> Go standard library image.ImageoutStdImg, err := vipsImg.ToGoImage()if err != nil { return err}_ = outStdImg// Export raw RGBA bytes as a Uint8ClampedArray for WebAssembly / Canvas consumersconst { data, info } = await sharp('input.jpg') .ensureAlpha() .raw() .toBuffer({ resolveWithObject: true });
const rgbaView = new Uint8ClampedArray(data.buffer, data.byteOffset, data.byteLength);console.log(rgbaView.length, info.width, info.height);import numpy as npimport pyvips
image = pyvips.Image.new_from_file("input.jpg")# pyvips Image -> NumPy ndarray (H, W, C)arr = np.asarray(image)
# NumPy ndarray -> pyvips Imagerebuilt = pyvips.Image.new_from_array(arr)# The CLI operates on files and pipes rather than in-process language objectsvips rawsave input.jpg - | wc -c