Skip to content

Format probing, metadata inspection, and pixel statistics

Copy page

Before running an image pipeline or accepting an external upload, applications usually need to answer two distinct kinds of questions:

  1. Header probing: What format is this file? What are its width, height, band count, page count, EXIF Orientation, and ICC profile status? These queries parse only the container header without decoding full pixels.
  2. Pixel sampling and statistics: What is the RGB value at coordinate (x, y)? What is the average brightness, standard deviation, entropy, or dominant colour across the image? These queries trigger actual pixel decoding.

Because libvips opens images lazily, reading Width(), Height(), Bands(), Pages(), or sharp(input).metadata() inspects only the header bytes. Use header probing at upload gateways to enforce dimension limits (such as rejecting images whose total pixel count exceeds your memory budget) before any pixel decompression runs.

In govips, you can also inspect magic bytes with vips.DetermineImageType(buf) and verify runtime codec availability with vips.IsTypeSupported(imgType).

package main
import (
"fmt"
"os"
"github.com/davidbyttow/govips/v2/vips"
)
func main() {
if err := vips.Startup(nil); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
defer vips.Shutdown()
image, err := vips.NewImageFromFile("input.jpg")
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
defer image.Close()
fmt.Printf("format=%v size=%dx%d bands=%d alpha=%v icc=%v exif=%v pages=%d orientation=%d\n",
vips.ImageTypes[image.Format()],
image.Width(),
image.Height(),
image.Bands(),
image.HasAlpha(),
image.HasProfile(),
image.HasExif(),
image.Pages(),
image.Orientation(),
)
}

Metadata retention, selective stripping, and binding defaults

Section titled “Metadata retention, selective stripping, and binding defaults”

The four interfaces do not share the same default metadata policy on export:

  • sharp strips most metadata by default (EXIF, XMP, IPTC) to reduce output size and remove private camera tags; call .keepMetadata(), .keepExif(), .keepIccProfile(), or .withExifMerge() when metadata must be preserved.
  • govips, pyvips, and vips CLI preserve input metadata by default; to strip metadata, set ExportParams.StripMetadata = true, call image.RemoveMetadata(), or pass keep="none" (or strip=True) to the saver.

In govips, image.RemoveMetadata(keepFields...) accepts an allowlist of field names — calling image.RemoveMetadata("icc-profile-data") strips private camera EXIF and GPS tags while keeping the embedded ICC colour profile intact.

// Strip EXIF / XMP / IPTC metadata while keeping the embedded ICC profile
if err := image.RemoveMetadata("icc-profile-data"); err != nil {
return err
}

Single-pixel sampling and global statistics (GetPoint / Stats)

Section titled “Single-pixel sampling and global statistics (GetPoint / Stats)”

Beyond header fields, libvips computes pixel samples and summary statistics without exporting the full image to your application layer:

  • Single-pixel sampling (GetPoint / getpoint): reads band values at (x, y), useful for automated test assertions, eyedropper background detection, or border checks.
  • Global and per-band statistics (Stats / Average / HistogramEntropy): computes minimum, maximum, mean, standard deviation, and histogram entropy. sharp.stats() additionally computes a sharpness estimate and the dominant sRGB colour.
// 1. Sample pixel band values ([]float64) at coordinate (969, 646)
pixel, err := image.GetPoint(969, 646)
if err != nil {
return err
}
// 2. Compute whole-image average, minimum, and histogram entropy
avg, err := image.Average()
if err != nil {
return err
}
entropy, err := image.HistogramEntropy()
if err != nil {
return err
}
fmt.Printf("pixel(969,646)=%v avg=%.2f entropy=%.4f\n", pixel, avg, entropy)
Capability govips v2.20.0 sharp 0.35.5 pyvips 3.2.0 vips CLI 8.17.3
Magic-byte format sniff DetermineImageType(buf) (await sharp(buf).metadata()).format Image.find_load_buffer(buf) vipsheader -f vips-loader
Single-pixel sample img.GetPoint(x, y) No dedicated method (use extract({ left: x, top: y, width: 1, height: 1 }).raw()) img.getpoint(x, y) or img(x, y) vips getpoint in x y
Whole-image mean / extrema img.Average(), img.Min(), img.Stats() (await img.stats()).channels img.avg(), img.min(), img.max(), img.stats() vips avg, vips min, vips max, vips stats
Entropy / dominant colour img.HistogramEntropy() stats.entropy, stats.dominant, stats.sharpness img.hist_entropy() vips hist_entropy