Heuristics

VigilCV implements three core image quality heuristics, each with a precise mathematical definition and configurable threshold.

1. Laplacian Variance (Blur Detection)

Formula

The discrete 3×3 Laplacian kernel is applied to the grayscale image:

L = [[0,  1, 0],

[1, -4, 1],

[0, 1, 0]]

The response image R = L * I captures second-order intensity discontinuities (edges and textures). The variance of R is the blur score:

blur_score = Var(L * I_gray)

= E[(L * I)²] - E[L * I]²

Interpretation

blur_scoreImage Quality
< 50Severely blurred or occluded
50–100Moderately blurred (fails default threshold)
100–500Acceptable sharpness
> 500High-frequency detail (sharp, textured)

Implementation Note

VigilCV computes this in pure NumPy without scipy.ndimage to minimize import overhead:

from PIL import Image

import numpy as np

def laplacian_variance(img: Image.Image) -> float:

gray = np.array(img.convert("L"), dtype=np.float32)

# Manual convolution via array slicing

lap = (

gray[:-2, 1:-1] + gray[2:, 1:-1] +

gray[1:-1, :-2] + gray[1:-1, 2:] -

4 * gray[1:-1, 1:-1]

)

return float(lap.var())


2. Shannon Entropy (Texture / Detail Richness)

Formula

Given the 8-bit luminance histogram H[b] (b ∈ [0, 255]):

P(b) = H[b] / N          (probability of bin b)

H_Shannon = -∑ P(b) · log₂(P(b)) (for all P(b) > 0)

Maximum entropy is 8.0 bits (uniform distribution across all 256 bins — perfectly random image). Minimum is 0.0 bits (solid single-color image).

Interpretation

Entropy (bits)Meaning
< 1.0Nearly uniform (solid color, lens cap)
1.0–3.0Low detail (flat sky, blank wall)
3.0–6.0Normal scene complexity
> 6.0High-frequency texture or noise

Why Entropy?

Entropy is robust to mild blur (which preserves overall histogram shape) while being highly sensitive to total information collapse. It is a complementary measure to Laplacian variance: a bright, uniformly overexposed image may score high on blur (large uniform regions → low Laplacian) but low on entropy.


3. Exposure Clipping Ratios

Formula

For the luminance channel L ∈ [0, 255]:

underexposure_ratio = |{pixels : L < 10}| / N

overexposure_ratio = |{pixels : L > 245}| / N

Both ratios are in [0, 1]. Default thresholds are 0.20 (20% clipped pixels).

Why These Thresholds?

A 20% clip ratio is a well-established photographic signal for destructive clipping — where detail is irretrievably lost. Images exceeding this fail the exposure gate even if blur and entropy are acceptable, because the model's color-sensitive channels receive a distorted signal.


Configuring Thresholds

All thresholds are exposed as constructor arguments on VisionSentinel:

from vigilcv import VisionSentinel

sentinel = VisionSentinel(

blur_threshold=100.0, # Laplacian variance minimum

min_entropy=3.0, # Shannon entropy minimum (bits)

max_underexposure_ratio=0.20, # Max dark-clipped pixel fraction

max_overexposure_ratio=0.20, # Max saturated pixel fraction

raise_on_fail=True, # Raise or return on failure

)

See the API Reference for the full parameter contract.