Skip to main content

Overview

Most perceptual hashing algorithms resize input images to a fixed dimension before computing the hash. The interpolation method used during this resize operation can significantly impact hash quality and computation speed. imghash provides 7 interpolation algorithms, each with different trade-offs between quality and performance.
type Interpolation int

Available Methods

NearestNeighbor

Selects the pixel value from the nearest source pixel without any blending.
const NearestNeighbor Interpolation = 0

Speed

Fastest - No computation, direct pixel lookup

Quality

Lowest - Can produce blocky, aliased results

Example

algo, _ := imghash.NewAverage(
    imghash.WithInterpolation(imghash.NearestNeighbor),
)

hash, _ := algo.Calculate(img)

Characteristics

  • No anti-aliasing
  • Sharp edges preserved (can be jagged)
  • Best for: pixel art, binary images, maximum speed
  • Avoid for: natural images with gradients
NearestNeighbor can produce inconsistent hashes when image dimensions change slightly, as different source pixels may be selected.

Bilinear

Default for most algorithms. Blends the 4 nearest pixels using linear interpolation.
const Bilinear Interpolation = 1

Speed

Fast - Simple 4-pixel weighted average

Quality

Good - Smooth results for most use cases

Example

// Bilinear is the default
algo1, _ := imghash.NewPHash()

// Explicit specification
algo2, _ := imghash.NewPHash(
    imghash.WithInterpolation(imghash.Bilinear),
)

// Both are equivalent

Algorithm

For each output pixel at (x, y):
  Find 4 surrounding source pixels
  Compute weights based on distance
  result = w1*p1 + w2*p2 + w3*p3 + w4*p4

Characteristics

  • Smooth gradients
  • Minimal blur
  • Good balance of speed and quality
  • Recommended for: Average, Difference, Median, PHash, most algorithms
Bilinear is the sweet spot for most applications - good quality without significant overhead.

BilinearExact

More precise bilinear interpolation using exact floating-point math.
const BilinearExact Interpolation = 6

Speed

Slightly slower than standard Bilinear

Quality

More accurate floating-point calculations

Example

algo, _ := imghash.NewColorMoment(
    imghash.WithInterpolation(imghash.BilinearExact),
)

Use When

  • You need precise color values (ColorMoment, CLD)
  • Reproducibility across platforms is critical
  • Computational cost is not a concern

Bicubic

Uses 16 nearest pixels with cubic polynomial weighting for smoother results.
const Bicubic Interpolation = 2

Speed

Slower - 16 pixel computations per output pixel

Quality

Higher - Smoother than bilinear, less aliasing

Example

algo, _ := imghash.NewZernike(
    imghash.WithInterpolation(imghash.Bicubic),
)

hash, _ := algo.Calculate(highResImage)

Algorithm

For each output pixel at (x, y):
  Find 16 surrounding source pixels (4x4 grid)
  Apply cubic convolution kernel
  Weighted sum with negative lobes for sharpness

Characteristics

  • Smoother gradients than bilinear
  • Can introduce slight ringing near edges
  • Better preservation of high-frequency details
  • Recommended for: GIST, Zernike, high-quality requirements
Bicubic can produce slightly sharper results than bilinear, which may improve discriminability for some algorithms.

MitchellNetravali

Advanced bicubic filter with balanced sharpness and smoothness.
const MitchellNetravali Interpolation = 3

Speed

Similar to Bicubic - 16 pixel kernel

Quality

Excellent - Optimized trade-off of blur and ringing

Example

algo, _ := imghash.NewGIST(
    imghash.WithInterpolation(imghash.MitchellNetravali),
)

Characteristics

  • Uses Mitchell-Netravali filter with B=1/3, C=1/3
  • Perceptually balanced - minimal blur and ringing
  • Preferred by many image processing experts
  • Recommended for: Production systems requiring high quality
MitchellNetravali is often considered the “best looking” interpolation for natural images.

Lanczos2

Sinc-based filter with 2-lobe kernel for high-quality resampling.
const Lanczos2 Interpolation = 4

Speed

Slow - 4x4 kernel with sinc calculations

Quality

Very High - Excellent detail preservation

Example

algo, _ := imghash.NewPDQ(
    imghash.WithInterpolation(imghash.Lanczos2),
)

hash, _ := algo.Calculate(photographImage)

Algorithm

Lanczos kernel:
  L(x) = sinc(x) * sinc(x/a)  where a=2
  
For each output pixel:
  Apply 4x4 windowed sinc kernel
  Normalized weighted sum

Characteristics

  • Based on sinc function (ideal low-pass filter)
  • Sharp transitions, minimal blur
  • Can introduce ringing artifacts near edges
  • Recommended for: Downsampling photographs, detail-critical applications

Lanczos3

Sinc-based filter with 3-lobe kernel - highest quality, slowest speed.
const Lanczos3 Interpolation = 5

Speed

Slowest - 6x6 kernel with sinc calculations

Quality

Highest - Maximum detail retention

Example

// For maximum quality when speed is not critical
algo, _ := imghash.NewHOGHash(
    imghash.WithInterpolation(imghash.Lanczos3),
    imghash.WithHOGCellSize(8),
)

Algorithm

Lanczos kernel:
  L(x) = sinc(x) * sinc(x/a)  where a=3
  
For each output pixel:
  Apply 6x6 windowed sinc kernel
  Normalized weighted sum

Characteristics

  • Widest kernel - most source pixels considered
  • Sharpest results, best frequency preservation
  • Most prone to ringing artifacts
  • Recommended for: Critical applications, large downsample ratios, research
Lanczos3 is computationally expensive. Only use when quality justifies the cost.

Setting Interpolation

All algorithms that perform resizing accept the WithInterpolation option:
import "github.com/ajdnik/imghash/v2"

// Average hash with nearest neighbor (fastest)
avg, _ := imghash.NewAverage(
    imghash.WithInterpolation(imghash.NearestNeighbor),
)

// PHash with Lanczos3 (highest quality)
phash, _ := imghash.NewPHash(
    imghash.WithInterpolation(imghash.Lanczos3),
)

// GIST with default bilinear
gist, _ := imghash.NewGIST()
// No WithInterpolation specified = Bilinear

Algorithms Using Interpolation

These algorithms resize images and support WithInterpolation:
  • Average, Difference, Median, PHash
  • BlockMean, MarrHildreth
  • ColorMoment, CLD, EHD
  • LBP, HOGHash
  • RadialVariance, Zernike
  • GIST, WHash
Some algorithms (PDQ, BoVW, RASH) use fixed internal resizing and don’t expose interpolation as an option.

Performance Comparison

MethodRelative SpeedQualityKernel SizeUse Case
NearestNeighbor1.0x (baseline)1x1Speed-critical, pixel art
Bilinear0.8x⭐⭐⭐2x2Default - balanced
BilinearExact0.75x⭐⭐⭐2x2Precise color values
Bicubic0.4x⭐⭐⭐⭐4x4Natural images
MitchellNetravali0.4x⭐⭐⭐⭐4x4Production quality
Lanczos20.3x⭐⭐⭐⭐⭐4x4Photographs
Lanczos30.2x⭐⭐⭐⭐⭐6x6Maximum quality
Speed measurements are approximate and depend on image size and CPU architecture.

Quality vs Speed Trade-offs

// Real-time applications, large batch processing
algo, _ := imghash.NewAverage(
    imghash.WithInterpolation(imghash.NearestNeighbor),
)

// or
algo, _ := imghash.NewAverage(
    imghash.WithInterpolation(imghash.Bilinear),
)
Use when:
  • Processing millions of images
  • Real-time video hashing
  • Mobile/embedded devices

Interpolation Impact on Hashes

Different interpolation methods produce slightly different hashes from the same image:
img := loadImage("photo.jpg")

algoNN, _ := imghash.NewAverage(
    imghash.WithInterpolation(imghash.NearestNeighbor),
)
algoLanczos, _ := imghash.NewAverage(
    imghash.WithInterpolation(imghash.Lanczos3),
)

hashNN, _ := algoNN.Calculate(img)
hashLanczos, _ := algoLanczos.Calculate(img)

// Hashes will differ slightly
dist, _ := similarity.Hamming(hashNN, hashLanczos)
fmt.Printf("Distance between interpolations: %.0f bits\n", dist)
// Typically 1-5 bits difference for Average hash
If you need to compare hashes across systems, ensure all systems use the same interpolation method.

Best Practices

1

Use Bilinear as Default

Unless you have specific requirements, Bilinear provides excellent quality with minimal overhead.
2

Match Interpolation to Algorithm

Simple algorithms (Average, Difference) don’t benefit much from high-quality interpolation. Save Lanczos for complex algorithms like GIST or Zernike.
3

Consider Downsample Ratio

Larger downsample ratios (e.g., 4K → 8x8) benefit more from high-quality interpolation than small ratios.
4

Profile Your Application

Measure actual performance impact. Interpolation cost is often small compared to algorithm computation.
5

Stay Consistent

Use the same interpolation method throughout your application to ensure comparable hashes.

Recommendations by Algorithm

Recommended: Bilinear (default)These algorithms threshold pixels to binary values, so subtle interpolation differences matter less.
algo, _ := imghash.NewAverage(
    imghash.WithInterpolation(imghash.Bilinear),
)
Recommended: Bicubic or MitchellNetravaliDCT benefits from smooth frequency domain representation.
algo, _ := imghash.NewPHash(
    imghash.WithInterpolation(imghash.Bicubic),
)
Recommended: BilinearExact or BicubicColor precision matters for these algorithms.
algo, _ := imghash.NewColorMoment(
    imghash.WithInterpolation(imghash.BilinearExact),
)
Recommended: Bicubic or Lanczos2Edge preservation is important for gradient calculation.
algo, _ := imghash.NewHOGHash(
    imghash.WithInterpolation(imghash.Lanczos2),
)
Recommended: Lanczos2 or Lanczos3These algorithms analyze fine details and benefit from high-quality resampling.
algo, _ := imghash.NewGIST(
    imghash.WithInterpolation(imghash.Lanczos3),
)

Error Handling

import "errors"

// Invalid interpolation value
algo, err := imghash.NewAverage(
    imghash.WithInterpolation(Interpolation(999)),
)

if errors.Is(err, imghash.ErrInvalidInterpolation) {
    fmt.Println("Invalid interpolation method")
}
Valid interpolation values are 0-6 (NearestNeighbor through BilinearExact).

String Representation

interp := imghash.Bilinear
fmt.Println(interp.String()) // "Bilinear"

invalid := imghash.Interpolation(999)
fmt.Println(invalid.String()) // "Unknown"

Advanced: Custom Interpolation

For custom resize logic, you can pre-process images before hashing:
import "image"

// Custom resize function
func customResize(img image.Image, width, height int) image.Image {
    // Your custom interpolation logic
    return resizedImage
}

// Pre-resize, then use NearestNeighbor (no-op)
resized := customResize(img, 8, 8)

algo, _ := imghash.NewAverage(
    imghash.WithSize(8, 8),
    imghash.WithInterpolation(imghash.NearestNeighbor),
)

hash, _ := algo.Calculate(resized)
This approach gives full control but requires more code and understanding of the algorithm’s expected input size.

Build docs developers (and LLMs) love