subject_center.py  ·  analisis-centrado.html

Manual de referencia de las medidas

Reference manual of the measurements

Definición operativa, fórmula, unidades, rango de validez y modo de fallo de cada magnitud que calculan el analizador de línea de comandos y el panel interactivo. Cada ficha cita la función exacta del código fuente y, cuando aporta algo que la fórmula sola no transmite, una comprobación numérica hecha contra esa misma función.

Operational definition, formula, units, range of validity and failure mode of every quantity computed by the command-line analyser and by the interactive panel. Each datasheet cites the exact function in the source code and, where it conveys something the formula alone does not, a numerical check made against that very function.

Archivos descritos
subject_center.py
analisis-centrado.html
Dependencias del cálculo
NumPy, Pillow (Python)
sin librerías en el navegador
Espacio de color de referencia
sRGB IEC 61966‑2‑1
CIE 1931 / D65
Convención angular
grados sexagesimales
0° = eje +a*
Files described
subject_center.py
analisis-centrado.html
Dependencies of the computation
NumPy, Pillow (Python)
no libraries in the browser
Reference colour space
sRGB IEC 61966‑2‑1
CIE 1931 / D65
Angular convention
sexagesimal degrees
0° = +a* axis
1

Convenciones y sistema de coordenadas

Conventions and coordinate system

subject_center.py · cabecera del módulo subject_center.py · module header

El origen de coordenadas del fotograma está en su centro geométrico, no en la esquina superior izquierda que usa la matriz de píxeles. Para una imagen de ancho W y alto H, con el centroide del sujeto en (cx, cy) medido en píxeles desde la esquina superior izquierda:

The origin of the frame's coordinates lies at its geometric centre, not at the top-left corner used by the pixel matrix. For an image of width W and height H, with the subject's centroid at (cx, cy) measured in pixels from the top-left corner:

xnorm = (cx − W/2) / (W/2)  ∈  [−1, 1] ynorm = (H/2 − cy) / (H/2)  ∈  [−1, 1]

xnorm crece hacia la derecha; ynorm crece hacia arriba, invirtiendo el sentido de la coordenada de fila de la imagen para que el signo coincida con la convención de un plano cartesiano y no con la de una matriz. La distancia al centro se agrega con la semidiagonal como unidad, y esa unidad incorpora ya la relación de aspecto a = W/H:

xnorm increases towards the right; ynorm increases upwards, reversing the sense of the image's row coordinate so that the sign agrees with the convention of a Cartesian plane and not with that of a matrix. The distance to the centre is aggregated taking the half-diagonal as the unit, and that unit already incorporates the aspect ratio a = W/H:

rnorm = √(xnorm²a² + ynorm²)√(a² + 1)

Con esta normalización, rnorm = 0 en el centro exacto y rnorm = 1 en cualquiera de las cuatro esquinas, sea el formato 24×36, 6×6 o un recorte arbitrario. Sin el factor a, un mismo desplazamiento en píxeles produciría valores de rnorm distintos en un fotograma horizontal y en uno vertical del mismo sujeto, porque la distancia a la esquina no es igual en las dos direcciones cuando a ≠ 1.

With this normalisation, rnorm = 0 at the exact centre and rnorm = 1 at any of the four corners, whether the format is 24×36, 6×6 or an arbitrary crop. Without the factor a, one and the same displacement in pixels would yield different values of rnorm in a landscape frame and in a portrait one of the same subject, because the distance to the corner is not equal in the two directions when a ≠ 1.

La misma unidad de semidiagonal mide la dispersión del sujeto (spread_norm, radio de giro de la componente conexa) y sirve de referencia para confidence: una máscara uniforme que cubriera todo el fotograma tendría radio de giro

The same half-diagonal unit measures the dispersion of the subject (spread_norm, the radius of gyration of the connected component) and serves as the reference for confidence: a uniform mask covering the whole frame would have a radius of gyration of

rg,uniforme = 1/√3 ≈ 0,5774
rg,uniform = 1/√3 ≈ 0.5774

valor que aparece en el código como la constante RG_UNIFORM y que se usa como divisor para acotar confidence en [0, 1].

a value that appears in the code as the constant RG_UNIFORM and that is used as the divisor bounding confidence within [0, 1].

1.1Notación estadística usada en todo el documento

1.1Statistical notation used throughout this document

símbolosignificadosímbolosignificado
μ, σmedia y desviación típica τtau de Kendall (concordancia de rangos)
med(x)mediana ρrho de Spearman (correlación de rangos)
MADdesviación absoluta mediana, med(|x−med(x)|) δdelta de Cliff (tamaño de efecto no paramétrico)
1,4826×MADestimador robusto de σ para una normal qvalor p corregido por tasa de falso descubrimiento
IC95intervalo de confianza del 95 %, por percentil bootstrap O(·)orden de complejidad computacional
symbolmeaningsymbolmeaning
μ, σmean and standard deviation τKendall's tau (rank concordance)
med(x)median ρSpearman's rho (rank correlation)
MADmedian absolute deviation, med(|x−med(x)|) δCliff's delta (non-parametric effect size)
1.4826×MADrobust estimator of σ for a normal distribution qp-value corrected for the false discovery rate
CI9595 % confidence interval, by bootstrap percentile O(·)order of computational complexity

Todas las medianas, MAD e intervalos de confianza de este documento se calculan por remuestreo o por fórmula cerrada; ningún resultado numérico mostrado aquí se ha inventado a mano, se ha recalculado con las funciones citadas en cada ficha durante la redacción de este manual.

Every median, MAD and confidence interval in this document is computed by resampling or in closed form; no numerical result shown here has been invented by hand, each was recomputed with the functions cited in the corresponding datasheet while this manual was being written.

2

Geometría del sujeto

Subject geometry

sharpness_map · contrast_map · centroid

El sujeto no se localiza con ningún modelo entrenado. Se construye un mapa escalar m(i,j) sobre una rejilla de --grid celdas en el lado mayor (48 por defecto) y se toma como sujeto la componente conexa de ese mapa que contiene su máximo. Hay dos generadores de m, seleccionables con --method, y una combinación convexa de ambos.

The subject is not located with any trained model. A scalar map m(i,j) is built on a grid of --grid cells along the longer side (48 by default), and the subject is taken to be the connected component of that map which contains its maximum. There are two generators of m, selectable with --method, and a convex combination of the two.

2.1Mapa de nitidez

2.1Sharpness map

method = sharpness subject_center.py · sharpness_map()
Definición
energía del laplaciano discreto de 5 puntos, normalizada por la luminancia local suavizada, promediada por celda.
Fórmula
∇²g[i,j] = 4g[i,j] − g[i−1,j] − g[i+1,j] − g[i,j−1] − g[i,j+1] mij = ⟨|∇²g|⟩celda ijG8*g + 0,05
donde g es la luma de la imagen (véase la advertencia al final de esta sección) y G8*g es g difuminada con un núcleo gaussiano de σ = 8 px. La constante aditiva 0,05 evita dividir por valores cercanos a cero en sombras profundas; sin ella, el ruido de cuantización de una sombra casi negra produciría cocientes desproporcionados y el mapa marcaría como "sujeto" el ruido de las zonas oscuras en lugar del sujeto real.
Justificación física
a diafragma abierto, el plano enfocado concentra la energía de alta frecuencia; el resto del fotograma, difuminado por la profundidad de campo finita, la pierde. El laplaciano es el operador lineal más simple sensible a la curvatura de la señal y, por tanto, a los bordes.
Degrada con
escenas a hiperfocal (todo enfocado, sin contraste de nitidez que discriminar) y con grano de película muy marcado, que introduce energía de alta frecuencia en todo el fotograma, no solo en el sujeto.
method = sharpness subject_center.py · sharpness_map()
Definition
energy of the discrete five-point Laplacian, normalised by the smoothed local luminance and averaged over each cell.
Formula
∇²g[i,j] = 4g[i,j] − g[i−1,j] − g[i+1,j] − g[i,j−1] − g[i,j+1] mij = ⟨|∇²g|⟩cell ijG8*g + 0.05
where g is the luma of the image (see the warning at the end of this section) and G8*g is g blurred with a Gaussian kernel of σ = 8 px. The additive constant 0.05 prevents division by values close to zero in deep shadows; without it, the quantisation noise of an almost black shadow would produce disproportionate ratios and the map would mark the noise of the dark areas as the "subject" instead of the real subject.
Physical rationale
at a wide aperture, the plane of focus concentrates the high-frequency energy; the rest of the frame, blurred by the finite depth of field, loses it. The Laplacian is the simplest linear operator sensitive to the curvature of the signal and therefore to edges.
Degrades with
scenes shot at the hyperfocal distance (everything in focus, with no sharpness contrast to discriminate) and with very pronounced film grain, which introduces high-frequency energy across the whole frame, not only in the subject.

2.2Mapa de contraste cromático

2.2Chromatic contrast map

method = contrast subject_center.py · contrast_map()
Definición
contraste de histograma de Cheng y otros: cada celda puntúa según la distancia cromática de su color medio al de todas las demás celdas, ponderada por la frecuencia de cada color.
Fórmula
S(ck) = ∑k′ fk′ · ‖ck − ck′‖2
con los colores cuantizados a bins = 4 niveles por canal (4³ = 64 colores posibles), fk′ la frecuencia relativa del color k′ y ‖·‖2 la distancia euclídea en RGB. Cada celda hereda la puntuación de su color cuantizado.
Detecta el sujeto por
anomalía cromática o tonal, no por enfoque: útil cuando el sujeto se diferencia por color (un objeto rojo sobre fondo verde) aunque toda la escena esté igual de nítida.
method = contrast subject_center.py · contrast_map()
Definition
the histogram contrast of Cheng and others: each cell is scored by the chromatic distance of its mean colour to that of all the remaining cells, weighted by the frequency of each colour.
Formula
S(ck) = ∑k′ fk′ · ‖ck − ck′‖2
with the colours quantised to bins = 4 levels per channel (4³ = 64 possible colours), fk′ the relative frequency of colour k′ and ‖·‖2 the Euclidean distance in RGB. Each cell inherits the score of its quantised colour.
Detects the subject by
chromatic or tonal anomaly, not by focus: useful when the subject stands out by colour (a red object against a green background) even though the whole scene is equally sharp.

2.3Combinación y localización de la componente

2.3Combination and location of the component

Con method=both (opción por defecto), el mapa final es la combinación convexa m = w·sharpness + (1−w)·contrast, con w = --w-sharp = 0,65 por defecto, y ambos mapas renormalizados a [0, 1] antes de mezclarse. El centroide no se calcula sobre m completo, que daría un valor trivialmente próximo al centro por simple promedio de todo el fotograma, sino sobre la componente conexa de m ≥ --level·max(m) que contiene el máximo, hallada por inundación en cuatro vecindades (_flood()). Si esa componente ocupa más de --max-area del fotograma (0,35 por defecto), el umbral sube en pasos de 0,05 hasta que deja de hacerlo.

With method=both (the default), the final map is the convex combination m = w·sharpness + (1−w)·contrast, with w = --w-sharp = 0.65 by default, both maps being renormalised to [0, 1] before being mixed. The centroid is not computed over the whole of m, which would give a value trivially close to the centre by simple averaging over the entire frame, but over the connected component of m ≥ --level·max(m) that contains the maximum, found by four-neighbour flood fill (_flood()). If that component occupies more than --max-area of the frame (0.35 by default), the threshold is raised in steps of 0.05 until it no longer does.

cx = ∑(i,j)∈C mijxij∑(i,j)∈C mij    rg² = ∑(i,j)∈C mij·d(i,j)²∑(i,j)∈C mij

con C la componente conexa, d(i,j) la distancia euclídea de la celda al centroide en unidades de semidiagonal (idéntica normalización que rnorm) y confidence = max(0, 1 − rg/0,5774).

with C the connected component, d(i,j) the Euclidean distance from the cell to the centroid in units of half-diagonal (the same normalisation as rnorm) and confidence = max(0, 1 − rg/0.5774).

Miniatura de control generada por --overlay
Figura 2.1. Salida real de --overlay sobre una fotografía sintética (sujeto de textura nítida, fondo de espectro 1/f desenfocado a σ = 6,5 px). Líneas grises: tercios y centro del fotograma. Círculo rojo: radio de giro de la componente conexa. Cruz roja: centroide. Valores medidos: xnorm = −0,335, ynorm = +0,132, rnorm = 0,289, confidence = 0,752.
Figure 2.1. Actual output of --overlay on a synthetic photograph (a subject of sharp texture, a 1/f-spectrum background defocused to σ = 6.5 px). Grey lines: the rule of thirds and the centre of the frame. Red circle: radius of gyration of the connected component. Red cross: the centroid. Measured values: xnorm = −0.335, ynorm = +0.132, rnorm = 0.289, confidence = 0.752.
Estado interno de subject_map y centroid
Figura 2.2. Estado interno de subject_map() y centroid() sobre la misma fotografía de la figura 2.1, generado llamando directamente a esas dos funciones. Panel izquierdo: luma de entrada. Panel central: mapa m, con la curva de nivel 0,5·max(m) en cian y el máximo marcado con una cruz. Panel derecho: componente conexa devuelta por _flood() (magenta) y centroide resultante (marca amarilla). El área que ocupa la componente, 7,75 % del fotograma, es area_frac.
Figure 2.2. Internal state of subject_map() and centroid() on the same photograph as figure 2.1, produced by calling those two functions directly. Left panel: the input luma. Centre panel: the map m, with the 0.5·max(m) contour in cyan and the maximum marked with a cross. Right panel: the connected component returned by _flood() (magenta) and the resulting centroid (yellow marker). The area occupied by the component, 7.75 % of the frame, is area_frac.

2.4Fichas de columnas

2.4Column datasheets

r_normcentroid()
Rango
[0, 1]. 0 en el centro exacto, 1 en cualquier esquina.
Unidades
semidiagonales del fotograma (adimensional)
Límite
hereda el sesgo propio del estimador de saliencia. Para comparar dos cuerpos o dos épocas eso no invalida la diferencia, porque el sesgo es común a ambos grupos; sí invalida leer un valor aislado de r_norm como una medida absoluta de encuadre.
r_normcentroid()
Range
[0, 1]. 0 at the exact centre, 1 at any corner.
Units
half-diagonals of the frame (dimensionless)
Limit
it inherits the bias of the saliency estimator itself. For comparing two bodies or two periods this does not invalidate the difference, since the bias is common to both groups; it does invalidate reading an isolated value of r_norm as an absolute measure of framing.
confidencecentroid()
Rango
[0, 1]
Mide
compacidad geométrica de la componente conexa, no la existencia real de un sujeto.
Límite
una imagen de ruido puro con una concentración casual de energía puede dar confianza alta sin que exista ningún sujeto. Sirve para descartar fotogramas sin región dominante (cielos, texturas uniformes), no para validar que la detección es correcta. El propio manual de uso del script recomienda inspeccionar entre 30 y 40 miniaturas de --overlay antes de fiarse de un lote.
confidencecentroid()
Range
[0, 1]
Measures
the geometric compactness of the connected component, not the actual existence of a subject.
Limit
an image of pure noise with a chance concentration of energy can yield high confidence without any subject being present. It serves to discard frames with no dominant region (skies, uniform textures), not to confirm that the detection is correct. The script's own usage manual recommends inspecting between 30 and 40 --overlay thumbnails before trusting a batch.
Advertencia de dominio · luma frente a luminancia. sharpness_map(), contrast_map(), texture_traits() y todo el bloque de desenfoque (sección 6) operan sobre gray, calculado como 0,2126R′ + 0,7152G′ + 0,0722B′ directamente sobre los canales sRGB codificados con gamma, sin linealizar. Es luma de Rec. 709, una magnitud perceptual, no la luminancia física. La sección 3 (tono), en cambio, sí linealiza antes de promediar (srgb_to_linear()) para obtener Y. La distinción importa: dos fotogramas con la misma luminancia física pero distinta clave de exposición no producen el mismo campo de gradiente en luma, así que la nitidez y el grano medidos aquí están débilmente acoplados a la exposición, mientras que key_stops y dr_stops están definidos exactamente para medir eso.
Domain warning · luma versus luminance. sharpness_map(), contrast_map(), texture_traits() and the whole defocus block (section 6) operate on gray, computed as 0.2126R′ + 0.7152G′ + 0.0722B′ directly on the gamma-encoded sRGB channels, without linearisation. This is Rec. 709 luma, a perceptual quantity, not physical luminance. Section 3 (tone), by contrast, does linearise before averaging (srgb_to_linear()) in order to obtain Y. The distinction matters: two frames with the same physical luminance but a different exposure key do not produce the same gradient field in luma, so the sharpness and grain measured here are weakly coupled to exposure, whereas key_stops and dr_stops are defined precisely to measure that.
3

Tono

Tone

tone_traits()

Toda la sección parte de la luminancia linealizada, no de la luma de la sección anterior. La transferencia sRGB (IEC 61966‑2‑1) se deshace canal a canal:

The whole section starts from linearised luminance, not from the luma of the previous section. The sRGB transfer function (IEC 61966‑2‑1) is undone channel by channel:

Rlin = R′12,92  si R′ ≤ 0,04045      Rlin = (R′+0,055)1,0552,4  en el resto
Rlin = R′12.92  if R′ ≤ 0.04045      Rlin = (R′+0.055)1.0552.4  otherwise
Y = 0,212673 Rlin + 0,715152 Glin + 0,072175 Blin
Y = 0.212673 Rlin + 0.715152 Glin + 0.072175 Blin

con los coeficientes del primer renglón de la matriz sRGB/D65 (M_RGB2XYZ), idénticos a los pesos de luminancia relativa de Rec. 709. Y queda acotada por abajo en 10⁻⁵ (np.clip) para que los logaritmos siguientes no diverjan en negro absoluto.

with the coefficients of the first row of the sRGB/D65 matrix (M_RGB2XYZ), identical to the relative luminance weights of Rec. 709. Y is bounded from below at 10⁻⁵ (np.clip) so that the logarithms that follow do not diverge at absolute black.

3.1Clave de exposición

3.1Exposure key

key_stopstone_traits() · línea 464
Fórmula
keystops = log2med(Y)0,18
la mediana, no la media, para no dejar que un cielo saturado o una sombra cerrada desplacen la lectura de clave, exactamente el motivo por el que un fotómetro de integración pondera de forma distinta que uno puntual.
Rango típico
de −5 a +5 diafragmos; sin cota dura
Unidades
diafragmos (EV) respecto al gris del 18 %
Cero de la escala
0,18 en luminancia lineal es la reflectancia estándar de una carta gris fotográfica; no es una convención de este script sino el punto de referencia habitual en fotometría de exposición.
key_stopstone_traits() · line 464
Formula
keystops = log2med(Y)0.18
the median, not the mean, so that neither a blown sky nor a blocked-up shadow can displace the reading of the key, precisely the reason why an integrating light meter weights differently from a spot meter.
Typical range
from −5 to +5 stops; with no hard bound
Units
stops (EV) with respect to the 18 % grey
Zero of the scale
0.18 in linear luminance is the standard reflectance of a photographic grey card; it is not a convention of this script but the usual reference point in exposure photometry.

3.2Latitud registrada

3.2Recorded latitude

dr_stopstone_traits() · línea 468
Fórmula
drstops = log2Y0,995Y0,005
con Y0,005 y Y0,995 los percentiles 0,5 % y 99,5 % de Y sobre todos los píxeles. Se usan percentiles y no el mínimo y el máximo exactos para que un solo píxel de ruido térmico o un reflejo puntual no dispare el número; ver la sección 10 sobre HDR para la limitación asociada a esta elección.
Interpretación
diafragmos que separan la sombra casi más oscura de la luz casi más clara que sobreviven en el archivo, no en la escena original. Un tono mapeado agresivo puede mostrar dr_stops alto sin que la escena real lo fuera, y a la inversa.
Límite duro
con una imagen de 8 bits por canal el cociente teórico máximo es log2(255/1) ≈ 8 diafragmos, y en la práctica bastante menos porque el recorte a percentiles ya descarta los extremos. Ningún archivo de 8 bits procesado por este script puede informar más latitud que esa, exista o no en la escena.
dr_stopstone_traits() · line 468
Formula
drstops = log2Y0.995Y0.005
with Y0.005 and Y0.995 the 0.5 % and 99.5 % percentiles of Y over all the pixels. Percentiles are used rather than the exact minimum and maximum so that a single pixel of thermal noise or an isolated specular highlight cannot inflate the figure; see section 10 on HDR for the limitation attached to this choice.
Interpretation
the stops separating the nearly darkest shadow from the nearly brightest highlight that survive in the file, not in the original scene. An aggressive tone mapping may show a high dr_stops without the real scene having been contrasty, and conversely.
Hard limit
with an image of 8 bits per channel the maximum theoretical ratio is log2(255/1) ≈ 8 stops, and in practice appreciably less, because the percentile trimming already discards the extremes. No 8-bit file processed by this script can report more latitude than that, whether or not it exists in the scene.
Comprobación. Sobre un degradado sintético construido para abarcar exactamente 6 diafragmos entre percentiles (np.logspace en el dominio lineal), dr_stops midió 5,97, un 0,5 % por debajo del valor de construcción, diferencia atribuible al propio recorte a percentiles.
Check. On a synthetic gradient constructed to span exactly 6 stops between percentiles (np.logspace in the linear domain), dr_stops measured 5.97, some 0.5 % below the value it was built to, a difference attributable to the percentile trimming itself.

3.3El sistema de once zonas

3.3The eleven-zone system

Las columnas z00 a z10 reparten la superficie del fotograma en once bandas iguales en L*, la claridad perceptual de CIE 1976, no en diafragmos de exposición:

The columns z00 to z10 distribute the area of the frame into eleven bands equal in L*, the perceptual lightness of CIE 1976, and not in stops of exposure:

zona(i,j) = ⌊ L*(i,j) · 11 / 100 ⌋, acotado a [0, 10]
zone(i,j) = ⌊ L*(i,j) · 11 / 100 ⌋, clamped to [0, 10]

La alternativa obvia, once bandas de un diafragmo centradas en el gris del 18 %, se descartó porque el propio sistema de zonas de Ansel Adams está pensado como una escala de densidades de copia aproximadamente equiespaciadas en claridad perceptual, no en diafragmos de la escena. La figura 3.1 muestra por qué esas dos escalas no son intercambiables: del gris del 18 % al blanco de papel solo hay 2,4739 diafragmos (log2(1/0,18)), de modo que definir la zona como un diafragmo entero dejaría las tres últimas bandas casi vacías, mientras que la banda equivalente a las sombras más profundas se extendería sobre docenas de diafragmos sin fondo físico real.

The obvious alternative, eleven one-stop bands centred on the 18 % grey, was rejected because Ansel Adams's zone system is itself conceived as a scale of print densities approximately equally spaced in perceptual lightness, not in stops of the scene. Figure 3.1 shows why the two scales are not interchangeable: from the 18 % grey to paper white there are only 2.4739 stops (log2(1/0.18)), so that defining the zone as a whole stop would leave the last three bands almost empty, while the band corresponding to the deepest shadows would extend over dozens of stops with no real physical floor.

Comparación entre zonas de L* y zonas de un diafragmo
Figura 3.1. Izquierda: L* en función de los diafragmos respecto al 18 % (curva calculada linealizando exactamente como rgb_to_lab()), con las once bandas iguales de L* superpuestas. El gris del 18 % cae en L* = 49,5, zona V; el blanco de papel, a +2,47 diafragmos, cierra la zona X. Derecha: qué ocurriría si la zona se definiera como un diafragmo entero desde el 18 %—las zonas +3 a +5 quedarían comprimidas en menos de un punto de L*.
Figure 3.1. Left: L* as a function of the stops relative to the 18 % grey (a curve computed by linearising exactly as rgb_to_lab() does), with the eleven equal L* bands superimposed. The 18 % grey falls at L* = 49.5, zone V; paper white, at +2.47 stops, closes zone X. Right: what would happen if the zone were defined as a whole stop from the 18 % grey—zones +3 to +5 would be compressed into less than one point of L*.
Comprobación. Sobre una imagen de gris del 18 % uniforme, z05 = 1,0 y el resto, 0. Sobre blanco puro, z10 = 1,0. Sobre negro puro, z00 = 1,0. Los tres casos límite caen exactamente en la zona que la fórmula predice.
Check. On an image of uniform 18 % grey, z05 = 1.0 and the rest, 0. On pure white, z10 = 1.0. On pure black, z00 = 1.0. The three limiting cases fall exactly in the zone that the formula predicts.
Perfil de zonas real del panel interactivo
Figura 3.2. Captura real de la pestaña Tono del panel (drawZones()) sobre dos grupos de referencia, uno de clave alta y otro de clave baja: el perfil se desplaza en bloque hacia las zonas altas o bajas, pero ambos conservan la misma anchura relativa, porque las once bandas están definidas en L* y no dependen de dónde caiga la exposición.
Figure 3.2. An actual capture of the Tone tab of the panel (drawZones()) on two reference groups, one high-key and one low-key: the profile shifts bodily towards the high or the low zones, yet both retain the same relative width, because the eleven bands are defined in L* and do not depend on where the exposure falls.

3.4Recorte, entropía y contraste

3.4Clipping, entropy and contrast

columnafórmularangolectura
clip_lo media( maxcanal(RGB) ≤ 2/255 ) [0,1]fracción de píxeles con las sombras cortadas a negro
clip_hi media( mincanal(RGB) ≥ 253/255 ) [0,1]fracción de píxeles con las luces quemadas
contrast_sdσ(L*) ≥0dispersión tonal global, en unidades de L*
lstar_medianmed(L*) [0,100]claridad global del fotograma
tone_entropy −∑pklog2pk [0, log264] entropía de Shannon del histograma de L* en 64 cajas; un fotograma con toda la masa en pocas cajas (alto contraste puro, dos modas) tiene entropía baja frente a uno con la claridad repartida de forma uniforme.
columnformularangereading
clip_lo mean( maxchannel(RGB) ≤ 2/255 ) [0,1]fraction of pixels whose shadows are clipped to black
clip_hi mean( minchannel(RGB) ≥ 253/255 ) [0,1]fraction of pixels whose highlights are blown
contrast_sdσ(L*) ≥0global tonal dispersion, in units of L*
lstar_medianmed(L*) [0,100]global lightness of the frame
tone_entropy −∑pklog2pk [0, log264] Shannon entropy of the L* histogram in 64 bins; a frame with all its mass in a few bins (pure high contrast, two modes) has low entropy compared with one whose lightness is spread out uniformly.

El criterio de recorte usa el máximo o el mínimo entre canales, no la luminancia: un píxel con R=255, G=40, B=40 cuenta como luz quemada aunque su luminancia no sea extrema, porque un canal saturado ya ha perdido información irrecuperable en la captura o en el escaneo, con independencia de los otros dos canales.

The clipping criterion uses the maximum or the minimum across channels, not the luminance: a pixel with R=255, G=40, B=40 counts as a blown highlight even though its luminance is not extreme, because a saturated channel has already lost information irrecoverably at capture or at scanning, independently of the other two channels.

4

Color

Colour

color_traits()
Advertencia de dominio, válida para toda la sección. Estos descriptores miden la emulsión, el revelado y el perfil del escáner tanto como la luz de la escena. Comparar color entre dos cuerpos cargados con películas distintas no dice nada del fotógrafo. Si el software del escáner equilibra el color fotograma a fotograma, la dominante media queda destruida y solo sobreviven los descriptores de las secciones 3 y 5.
Domain warning, valid for the whole section. These descriptors measure the emulsion, the processing and the scanner profile as much as the light of the scene. Comparing colour between two bodies loaded with different films says nothing about the photographer. If the scanner software balances colour frame by frame, the mean colour cast is destroyed and only the descriptors of sections 3 and 5 survive.

4.1CIELAB y cromaticidad

4.1CIELAB and chromaticity

La imagen linealizada se lleva a XYZ con la misma matriz M_RGB2XYZ de la sección 3 y de ahí a L*a*b* con blanco de referencia D65 (WHITE_D65 = [0,95047; 1; 1,08883]):

The linearised image is taken to XYZ with the same M_RGB2XYZ matrix as in section 3 and thence to L*a*b* with the D65 reference white (WHITE_D65 = [0.95047, 1, 1.08883]):

f(t) = ∛t  si t > (6/29)³     f(t) = 841108t + 429  en el resto L* = 116f(Y/Yn) − 16     a* = 500[f(X/Xn) − f(Y/Yn)]     b* = 200[f(Y/Yn) − f(Z/Zn)]
f(t) = ∛t  if t > (6/29)³     f(t) = 841108t + 429  otherwise L* = 116f(Y/Yn) − 16     a* = 500[f(X/Xn) − f(Y/Yn)]     b* = 200[f(Y/Yn) − f(Z/Zn)]

b* > 0 es amarillo, b* < 0 es azul; a* > 0 es magenta, a* < 0 es verde. El croma es C* = √(a*²+b*²), y chroma_mean/chroma_p95 son su media y su percentil 95 sobre el fotograma.

b* > 0 is yellow, b* < 0 is blue; a* > 0 is magenta, a* < 0 is green. The chroma is C* = √(a*²+b*²), and chroma_mean/chroma_p95 are its mean and its 95th percentile over the frame.

4.2Colorido

4.2Colourfulness

colorfulnesscolor_traits() · Hasler y Süsstrunk (2003)
Fórmula
rg = R−G    yb = ½(R+G)−B colorfulness = √(σrg²+σyb²) + 0,3√(μrg²+μyb²)
con R, G, B en la escala 0–255 sRGB sin linealizar, sobre los ejes oponentes rojo–verde y amarillo–azul.
Diferencia con el croma
C* mide la saturación perceptual de cada píxel y se promedia; colorfulness mide la dispersión estadística de los ejes oponentes en todo el fotograma, así que dos colores saturados pero opuestos (mitad roja, mitad verde) elevan colorfulness aunque el croma medio de cada píxel por separado sea el mismo.
colorfulnesscolor_traits() · Hasler and Süsstrunk (2003)
Formula
rg = R−G    yb = ½(R+G)−B colorfulness = √(σrg²+σyb²) + 0.3√(μrg²+μyb²)
with R, G, B on the 0–255 sRGB scale without linearisation, over the red–green and yellow–blue opponent axes.
Difference from chroma
C* measures the perceptual saturation of each pixel and is then averaged; colorfulness measures the statistical dispersion of the opponent axes across the whole frame, so that two saturated but opposite colours (half red, half green) raise colorfulness even though the mean chroma of each pixel taken separately is the same.

4.3Temperatura de color y Duv

4.3Colour temperature and Duv

La cromaticidad CIE 1931 (x,y) del color medio del fotograma se convierte a CIE 1960 UCS:

The CIE 1931 (x,y) chromaticity of the mean colour of the frame is converted to CIE 1960 UCS:

u = 4XX+15Y+3Z     v = 6YX+15Y+3Z
cct_kmccamy_cct() · McCamy (1992)
Fórmula
n = x − 0,3320y − 0,1858 CCT = −449n³ + 3525n² − 6823,3n + 5520,33
ajuste polinómico empírico sobre el locus de Planck, sin significado físico fuera de su entorno de ajuste.
Singularidad
el denominador se anula en y = 0,1858; cerca de ese valor la fórmula es numéricamente inestable y puede devolver temperaturas de decenas o cientos de miles de grados, o negativas, sin ningún significado. El código comprueba |y−0,1858| > 10⁻⁵ antes de evaluar, pero eso solo evita la división por cero exacta, no la inestabilidad del entorno cercano.
Guardián aplicado en color_traits()
if not (isfinite(cct) and 1000 ≤ cct ≤ 25000):
    cct, duv = NaN, NaN
elif |duv| > 0,05:
    cct = NaN
cct_k y duv se anulan a la vez si la CCT bruta cae fuera de [1000,25000] K; si la CCT es razonable pero el punto está lejos del locus (|Duv| > 0,05), solo se anula cct_k. Un valor vacío en cct_k significa que el color medio de la escena no se parece a la luz de ningún cuerpo negro a ninguna temperatura, caso en el que la calidez se lee en lab_b, no en kelvin.
cct_kmccamy_cct() · McCamy (1992)
Formula
n = x − 0.3320y − 0.1858 CCT = −449n³ + 3525n² − 6823.3n + 5520.33
an empirical polynomial fit along the Planckian locus, with no physical meaning outside the neighbourhood over which it was fitted.
Singularity
the denominator vanishes at y = 0.1858; near that value the formula is numerically unstable and may return temperatures of tens or hundreds of thousands of degrees, or negative ones, with no meaning whatever. The code checks that |y−0.1858| > 10⁻⁵ before evaluating, but that only avoids exact division by zero, not the instability of the surrounding neighbourhood.
Guard applied in color_traits()
if not (isfinite(cct) and 1000 ≤ cct ≤ 25000):
    cct, duv = NaN, NaN
elif |duv| > 0.05:
    cct = NaN
cct_k and duv are voided together if the raw CCT falls outside [1000,25000] K; if the CCT is reasonable but the point lies far from the locus (|Duv| > 0.05), only cct_k is voided. An empty value in cct_k means that the mean colour of the scene does not resemble the light of any black body at any temperature, in which case the warmth is read from lab_b, not in kelvin.
duvohno_duv() · Ohno (2013)
Fórmula
distancia firmada del punto (u,v) al locus de Planck, por la aproximación angular de Ohno: se calcula el ángulo θ = arccos((u−0,292)/ℓ) respecto al centro de curvatura aproximado (0,292; 0,240), con ℓ = √((u−0,292)²+(v−0,240)²), y se evalúa un polinomio de sexto grado en θ que aproxima la distancia del locus a ese mismo centro; Duv es la diferencia entre ℓ y ese polinomio.
Signo
positivo por encima del locus (hacia el verde), negativo por debajo (hacia el magenta); la mayoría de fuentes de luz artificial y de emulsiones de película caen dentro de ±0,02.
duvohno_duv() · Ohno (2013)
Formula
the signed distance of the point (u,v) from the Planckian locus, by Ohno's angular approximation: the angle θ = arccos((u−0.292)/ℓ) is computed with respect to the approximate centre of curvature (0.292, 0.240), with ℓ = √((u−0.292)²+(v−0.240)²), and a sixth-degree polynomial in θ is evaluated which approximates the distance from the locus to that same centre; Duv is the difference between ℓ and that polynomial.
Sign
positive above the locus (towards green), negative below it (towards magenta); most artificial light sources and film emulsions fall within ±0.02.
Locus de Planck en CIE 1960 UCS con puntos de prueba
Figura 4.1. Locus de Planck en (u,v) calculado con la aproximación racional de Krystek (1985, válida de 1000 a 15 000 K, error <3×10⁻⁴), usada aquí solo para trazar la referencia visual. Los puntos son valores reales obtenidos ejecutando color_traits() sobre imágenes uniformes: un gris de reflectancia 0,18 en sRGB (D65 exacto, CCT = 6503,46 K, Duv = +0,0032) y un naranja de tungsteno sintético RGB(0,85; 0,55; 0,25) (CCT = 2529,9 K). El punto azul saturado RGB(0,35; 0,45; 0,85) da una CCT bruta de −391 844 K, fuera de [1000,25000]: el guardián anula cct_k y duv a la vez, y la calidez de esa imagen solo queda registrada en lab_b = −55,3.
Figure 4.1. The Planckian locus in (u,v) computed with Krystek's rational approximation (1985, valid from 1000 to 15 000 K, error <3×10⁻⁴), used here solely to draw the visual reference. The points are real values obtained by running color_traits() on uniform images: a grey of reflectance 0.18 in sRGB (exact D65, CCT = 6503.46 K, Duv = +0.0032) and a synthetic tungsten orange RGB(0.85, 0.55, 0.25) (CCT = 2529.9 K). The saturated blue point RGB(0.35, 0.45, 0.85) gives a raw CCT of −391 844 K, outside [1000,25000]: the guard voids cct_k and duv together, and the warmth of that image is recorded only in lab_b = −55.3.
Comprobación. Los tres valores de la figura se generaron llamando a color_traits() directamente, no reconstruidos a mano; el caso D65 coincide, salvo un residuo de 0,54 K, con el valor de referencia habitual de 6504 K para el iluminante D65, diferencia atribuible a la propia aproximación polinómica de McCamy y no a un error del código.
Check. The three values in the figure were generated by calling color_traits() directly, not reconstructed by hand; the D65 case agrees, but for a residue of 0.54 K, with the usual reference value of 6504 K for the D65 illuminant, a difference attributable to McCamy's polynomial approximation itself and not to an error in the code.

4.4Estadística circular del tono

4.4Circular statistics of hue

El ángulo de tono h = atan2(b*,a*) es una variable circular: la media aritmética de 359° y 1° debería ser 0°, no 180°. Se usa la media vectorial ponderada por croma, restringida a píxeles con C* > 3 para excluir el gris casi neutro, que tiene un ángulo de tono indefinido en la práctica:

The hue angle h = atan2(b*,a*) is a circular variable: the arithmetic mean of 359° and 1° ought to be 0°, not 180°. The chroma-weighted vector mean is used, restricted to pixels with C* > 3 so as to exclude the almost neutral grey, whose hue angle is in practice undefined:

̄R = ‖∑C*k(cos hk, sin hk)‖∑C*k     σcirc = √(−2 ln ̄R)
̄R = ‖∑C*k(cos hk, sin hk)‖∑C*k     σcirc = √(−2 ln ̄R)

̄R (hue_conc) es la longitud de la resultante, entre 0 (tonos repartidos por todo el círculo, cancelación total) y 1 (todo el croma en un único ángulo). σcirc (hue_sd_deg) es la desviación típica circular de Mardia, expresada en grados. Si menos de 32 píxeles superan el umbral de croma, las tres columnas quedan vacías: no hay tono que promediar en una imagen casi acromática.

̄R (hue_conc) is the length of the resultant, between 0 (hues spread over the whole circle, total cancellation) and 1 (all the chroma at a single angle). σcirc (hue_sd_deg) is Mardia's circular standard deviation, expressed in degrees. If fewer than 32 pixels exceed the chroma threshold, the three columns are left empty: there is no hue to average in an almost achromatic image.

4.5Tono partido

4.5Split toning

Se separa el fotograma por percentiles de L* (25 y 75) en sombras y luces, y se compara la dominante media de cada mitad:

The frame is separated by percentiles of L* (25 and 75) into shadows and highlights, and the mean colour cast of each half is compared:

splitdab = √((a*luces−a*sombras)² + (b*luces−b*sombras)²)
splitdab = √((a*high−a*shad)² + (b*high−b*shad)²)

split_dir_deg es el ángulo de ese vector, atan2(Δb*, Δa*) en [0, 360). Un revelado con look de laboratorio (sombras frías, luces cálidas) produce split_dab alto y split_dir_deg próximo a 45°–90°.

split_dir_deg is the angle of that vector, atan2(Δb*, Δa*) in [0, 360). A processing with a laboratory look (cool shadows, warm highlights) produces a high split_dab and a split_dir_deg close to 45°–90°.

Plano a*b* del panel interactivo con vectores de tono partido
Figura 4.2. Captura real de la pestaña Color del panel (drawAB()) sobre un CSV de referencia con dos grupos de dominante opuesta. Cada cruz es un fotograma en el plano a*b*; la elipse, el contorno gaussiano del 50 % (véase la sección 8.4); la flecha, el vector de tono partido de la mediana del grupo, de sombras a luces.
Figure 4.2. An actual capture of the Colour tab of the panel (drawAB()) on a reference CSV with two groups of opposite colour cast. Each cross is a frame in the a*b* plane; the ellipse, the 50 % Gaussian contour (see section 8.4); the arrow, the split-toning vector of the group's median, from shadows to highlights.
5

Textura

Texture

texture_traits() · estimate_noise() · spectral_slope()

5.1Acutancia

5.1Acutance

acutancetexture_traits() · línea 548
Fórmula
acutance = 255 · med(|∇²g|)
mediana, no media, del valor absoluto del mismo laplaciano de 5 puntos de la sección 2, sobre el fotograma completo (recortado un píxel de margen para evitar el borde). El factor 255 expresa el resultado en niveles de un archivo de 8 bits.
Diferencia con el mapa de nitidez
aquí no hay normalización por luminancia local ni por celda: es una única cifra global, sensible tanto al enfoque óptico como al revelado o al escaneo con máscara de enfoque.
acutancetexture_traits() · line 548
Formula
acutance = 255 · med(|∇²g|)
the median, not the mean, of the absolute value of the same five-point Laplacian as in section 2, over the entire frame (trimmed by one pixel of margin to avoid the border). The factor 255 expresses the result in levels of an 8-bit file.
Difference from the sharpness map
here there is no normalisation by local luminance nor by cell: it is a single global figure, sensitive to optical focus as much as to the processing or to scanning with an unsharp mask.

5.2Grano

5.2Grain

grain_levelsestimate_noise()
Fórmula
la imagen se divide en teselas de 16×16 píxeles; se calcula la varianza del laplaciano dentro de cada tesela y se retiene el 20 % de teselas con menor varianza, bajo la suposición de que ahí no hay detalle real que contaminar la medida. Sobre la unión de esas teselas:
σruido = 1,4826 · MAD(∇²g)√20     grain_levels = 255 σruido
El divisor √20 viene de que, para ruido blanco gaussiano de varianza σ², el operador discreto ∇² (suma de cinco muestras con pesos 4,−1,−1,−1,−1) tiene varianza Var(∇²g) = (4²+4·1²)σ² = 20σ², de donde σ = √(Var(∇²g)/20); el factor 1,4826 convierte la MAD en un estimador consistente de σ bajo normalidad (1/Φ⁻¹(3/4)).
Límite, documentado en la cabecera del script
en escenas con textura fina repartida por todo el cuadro (follaje, gravilla, tejidos) no existen teselas realmente planas, la suposición de partida falla y el valor queda sobrestimado; en las pruebas de este mismo desarrollo, hasta un factor 1,5. Sirve para comparar dentro de un corpus homogéneo, no como medida absoluta de la emulsión.
grain_levelsestimate_noise()
Formula
the image is divided into tiles of 16×16 pixels; the variance of the Laplacian within each tile is computed and the 20 % of tiles with the lowest variance are retained, on the assumption that there is no real detail there to contaminate the measurement. Over the union of those tiles:
σnoise = 1.4826 · MAD(∇²g)√20     grain_levels = 255 σnoise
The divisor √20 follows from the fact that, for white Gaussian noise of variance σ², the discrete operator ∇² (a sum of five samples with weights 4,−1,−1,−1,−1) has variance Var(∇²g) = (4²+4·1²)σ² = 20σ², whence σ = √(Var(∇²g)/20); the factor 1.4826 turns the MAD into a consistent estimator of σ under normality (1/Φ⁻¹(3/4)).
Limit, documented in the script's header
in scenes with fine texture spread over the whole frame (foliage, gravel, fabrics) there are no genuinely flat tiles, the starting assumption fails and the value is overestimated; in the tests made during this very development, by as much as a factor of 1.5. It serves for comparison within a homogeneous corpus, not as an absolute measure of the emulsion.

5.3Superficie en foco y anisotropía

5.3Area in focus and anisotropy

columnafórmulalectura
focus_frac media( sh32×32 > 0,25·P98(sh) ) fracción de teselas de una rejilla de 32×32 cuya nitidez local supera el 25 % del percentil 98 de nitidez del fotograma; una escena a f/16 tiende a 1, un retrato a f/1,4 con fondo liso, a un valor bajo.
edge_vert (∑|∂g/∂x| − ∑|∂g/∂y|) / (∑|∂g/∂x| + ∑|∂g/∂y|) anisotropía de bordes en [−1,1]: positivo cuando predominan los bordes verticales (gradiente horizontal fuerte), negativo cuando predominan los horizontales.
columnformulareading
focus_frac mean( sh32×32 > 0.25·P98(sh) ) fraction of the tiles of a 32×32 grid whose local sharpness exceeds 25 % of the 98th percentile of the frame's sharpness; a scene at f/16 tends to 1, a portrait at f/1.4 against a plain background, to a low value.
edge_vert (∑|∂g/∂x| − ∑|∂g/∂y|) / (∑|∂g/∂x| + ∑|∂g/∂y|) edge anisotropy in [−1,1]: positive when vertical edges predominate (a strong horizontal gradient), negative when horizontal ones predominate.

5.4Pendiente espectral

5.4Spectral slope

alpha_spectralspectral_slope() · Field (1987)
Fórmula
se enventana la imagen con una ventana de Hann bidimensional, se calcula su espectro de potencia bidimensional |ƒ̂(g)|² y se promedia en anillos concéntricos de frecuencia radial f (28 anillos logarítmicamente espaciados entre f=4 y f=0,25·min(H,W) ciclos por ventana). Se ajusta una recta por mínimos cuadrados en el plano log⁠-⁠log:
ln P(f) = −α ln f + cte
y alpha_spectral es la pendiente cambiada de signo.
Referencia física
Field (1987) muestra que el espectro de potencia radial de imágenes de escenas naturales cae aproximadamente como f−2, consecuencia de la invariancia de escala estadística de los contornos naturales (bordes con estructura autosimilar en un rango amplio de frecuencias). Valores de α mayores indican estructura de grano grueso y transiciones suaves; valores menores, textura fina o ruido repartido por todo el cuadro. α=0 es ruido blanco.
alpha_spectralspectral_slope() · Field (1987)
Formula
the image is windowed with a two-dimensional Hann window, its two-dimensional power spectrum |ƒ̂(g)|² is computed and averaged over concentric rings of radial frequency f (28 rings logarithmically spaced between f=4 and f=0.25·min(H,W) cycles per window). A straight line is fitted by least squares in the log⁠-⁠log plane:
ln P(f) = −α ln f + const
and alpha_spectral is that slope with its sign reversed.
Physical reference
Field (1987) shows that the radial power spectrum of images of natural scenes falls off approximately as f−2, a consequence of the statistical scale invariance of natural contours (edges with self-similar structure over a wide range of frequencies). Larger values of α indicate coarse-grained structure and smooth transitions; smaller values, fine texture or noise spread over the whole frame. α=0 is white noise.
Ajuste log-log de la pendiente espectral sobre tres texturas sintéticas
Figura 5.1. Ajuste de spectral_slope() sobre tres texturas sintéticas de espectro de potencia conocido por construcción (P(f) ∝ f−α exacto). El exponente recuperado α̂ coincide con el de construcción dentro de 0,1 en los tres casos; los puntos son la potencia promedio por anillo y la línea, el ajuste log-log.
Figure 5.1. The fit of spectral_slope() on three synthetic textures whose power spectrum is known by construction (P(f) ∝ f−α exactly). The recovered exponent α̂ agrees with the one built in to within 0.1 in all three cases; the points are the mean power per ring and the line, the log-log fit.
Confusión con la exposición. acutance, grain_levels y alpha_spectral operan sobre gray (luma sin linealizar, sección 2.4). Un fotograma subexpuesto y luego corregido en el escaneo puede mostrar más grano aparente que el mismo negativo bien expuesto, no porque la emulsión tenga más grano físico, sino porque la amplificación de la señal en zonas oscuras amplifica también el ruido de captura del escáner antes de que este script vea la imagen.
Confounding with exposure. acutance, grain_levels and alpha_spectral operate on gray (luma without linearisation, section 2.4). An underexposed frame afterwards corrected at the scanning stage may show more apparent grain than the same negative correctly exposed, not because the emulsion has more physical grain, but because amplifying the signal in the dark areas amplifies the scanner's capture noise as well, before this script ever sees the image.
6

Desenfoque y cremosidad

Defocus and creaminess

blur_ratio() · blur_traits()

6.1Índice de desenfoque por cociente de escalas

6.1Defocus index by ratio of scales

blur_all · blur_subject · blur_bg blur_ratio()
Definición
sea E(σ) la energía media del gradiente de la imagen tras difuminarla con un núcleo gaussiano de anchura σ:
E(σ) = ⟨|∇(Gσ*g)|²⟩
con σ1 = 1 y σ2 = 3 px fijas (BLUR_S1, BLUR_S2). El índice es
blur_ratio = min(1, Ê(σ2)/Ê(σ1)) ∈ (0,1]
donde Ê es E con una corrección de ruido restada (apartado 6.2). En una imagen ya nítida, el segundo difuminado destruye mucho más gradiente que el primero y el cociente es pequeño; en una imagen ya borrosa, ninguno de los dos cambia gran cosa y el cociente tiende a 1. Es una magnitud relativa entre dos escalas de la misma imagen, no una medida absoluta de círculo de confusión en micras.
Por qué un cociente y no una diferencia
al ser un cociente, se cancela el contraste global de la imagen: un fondo cremoso de bajo contraste no se confunde con uno nítido de bajo contraste, porque ambos términos de la razón escalan igual con el contraste. Una diferencia de energías, en cambio, no tiene esa propiedad.
Por qué no el índice de reenfoque de Crete y otros (2007)
ese método, usado en una versión anterior de este mismo script, vuelve a difuminar la imagen y mide cuánto cambian las diferencias entre vecinos; funciona bien a desenfoques pequeños pero, con este pipeline concreto, deja de ser monótono más allá de unos pocos píxeles de desenfoque, porque las diferencias entre vecinos caen por debajo del escalón de cuantización de un archivo de 8 bits antes de que el propio desenfoque óptico termine de manifestarse. El cociente de energías entre dos escalas fijas no depende de ese umbral relativo.
blur_subject / blur_bg
el mismo cálculo restringido a la máscara de la componente conexa (sujeto) o a su complementario dilatado (fondo, bg); blur_sep = blur_bg − blur_subject.
blur_all · blur_subject · blur_bg blur_ratio()
Definition
let E(σ) be the mean energy of the image gradient after blurring it with a Gaussian kernel of width σ:
E(σ) = ⟨|∇(Gσ*g)|²⟩
with σ1 = 1 and σ2 = 3 px fixed (BLUR_S1, BLUR_S2). The index is
blur_ratio = min(1, Ê(σ2)/Ê(σ1)) ∈ (0,1]
where Ê is E with a noise correction subtracted (subsection 6.2). In an already sharp image, the second blurring destroys far more gradient than the first and the ratio is small; in an already blurred image, neither of the two changes much and the ratio tends to 1. It is a quantity relative to two scales of the same image, not an absolute measure of the circle of confusion in micrometres.
Why a ratio and not a difference
being a ratio, the global contrast of the image cancels out: a creamy background of low contrast is not confused with a sharp one of low contrast, because both terms of the ratio scale alike with contrast. A difference of energies, by contrast, does not have that property.
Why not the refocusing index of Crete and others (2007)
that method, used in an earlier version of this same script, blurs the image again and measures how much the differences between neighbours change; it works well at small amounts of defocus but, with this particular pipeline, ceases to be monotonic beyond a few pixels of defocus, because the differences between neighbours fall below the quantisation step of an 8-bit file before the optical defocus itself has finished manifesting. The ratio of energies between two fixed scales does not depend on that relative threshold.
blur_subject / blur_bg
the same computation restricted to the mask of the connected component (the subject) or to its dilated complement (the background, bg); blur_sep = blur_bg − blur_subject.

6.2Corrección de ruido

6.2Noise correction

El ruido de captura o de cuantización aporta energía de gradiente incluso donde no hay estructura óptica real, y esa aportación no se reparte igual entre las dos escalas: un desenfoque suave de σ = 1 apenas atenúa el ruido de alta frecuencia, mientras que uno de σ = 3 lo suprime con más eficacia. Para ruido blanco de varianza unidad, la contribución esperada a E(σ) tras pasar por el mismo operador de difuminado y de gradiente que usa el resto del código es una constante K(σ), calculada una sola vez por simulación al importar el módulo:

Capture or quantisation noise contributes gradient energy even where there is no real optical structure, and that contribution is not distributed equally between the two scales: a gentle blur of σ = 1 barely attenuates high-frequency noise, whereas one of σ = 3 suppresses it far more effectively. For white noise of unit variance, the expected contribution to E(σ) after passing through the same blurring and gradient operators used by the rest of the code is a constant K(σ), computed once only by simulation when the module is imported:

K(σ) = ⟨|∇(Gσ*w)|²⟩,   w ∼ N(0,1)  en una rejilla de prueba de 192×192
K(σ) = ⟨|∇(Gσ*w)|²⟩,   w ∼ N(0,1)  on a 192×192 test grid

y el índice corregido resta esa contribución antes de dividir:

and the corrected index subtracts that contribution before dividing:

Ê(σ) = E(σ) − σ̂ruido² K(σ)
Ê(σ) = E(σ) − σ̂noise² K(σ)

con σ̂ruido el mismo estimador de estimate_noise() de la sección 5.2. Si Ê(σ1) queda por debajo del 5 % de E(σ1) bruto, o Ê(σ2) resulta negativo, la corrección ha consumido toda la señal disponible y el índice se anula en lugar de devolver un cociente sin sentido.

with σ̂noise the same estimator from estimate_noise() as in section 5.2. If Ê(σ1) falls below 5 % of the raw E(σ1), or Ê(σ2) comes out negative, the correction has consumed all the available signal and the index is voided rather than returning a meaningless ratio.

Monotonía del índice de desenfoque y efecto de la corrección de ruido
Figura 6.1. Izquierda: blur_ratio() frente al desenfoque óptico real aplicado, en tres niveles de contraste global; la curva es monótona y las tres condiciones prácticamente se superponen, confirmando la invariancia al contraste. Derecha: con el desenfoque óptico fijado en cero, se inyecta ruido gaussiano de amplitud creciente antes de cuantizar a 8 bits. Sin restar la línea base instrumental σ̂²K(σ), el grano solo se lee como más nítido (el cociente baja de 0,140 a 0,079 según crece el grano, en la dirección equivocada); con la resta, el índice permanece cerca de su valor sin ruido (de 0,152 a 0,142) en todo el rango probado.
Figure 6.1. Left: blur_ratio() against the actual optical defocus applied, at three levels of global contrast; the curve is monotonic and the three conditions practically superimpose, confirming the invariance to contrast. Right: with the optical defocus set to zero, Gaussian noise of increasing amplitude is injected before quantising to 8 bits. Without subtracting the instrumental baseline σ̂²K(σ), grain is read simply as greater sharpness (the ratio falls from 0.140 to 0.079 as the grain grows, in the wrong direction); with the subtraction, the index stays close to its noise-free value (from 0.152 to 0.142) over the whole range tested.
El mecanismo es formalmente idéntico a restar la corriente de oscuridad de un detector antes de calcular un cociente de señales: sin la resta, un piso instrumental constante contamina de forma desigual al numerador y al denominador y sesga la medida en una dirección predecible. En una fuente sintética sin grano, estimate_noise() puede caer exactamente a cero en cuanto se aplica un desenfoque suave, porque no queda ninguna tesela con variabilidad de subpíxel; en una película real, con grano físico, el ruido no desaparece al desenfocar y el término de corrección sigue actuando en todo el rango de σ.
The mechanism is formally identical to subtracting a detector's dark current before computing a ratio of signals: without the subtraction, a constant instrumental floor contaminates numerator and denominator unequally and biases the measurement in a predictable direction. On a synthetic source without grain, estimate_noise() can fall to exactly zero as soon as a gentle blur is applied, because no tile is left with sub-pixel variability; on real film, with physical grain, the noise does not disappear on defocusing and the correction term keeps acting over the whole range of σ.

6.3Cremosidad

6.3Creaminess

creaminessblur_traits() · línea 640
Fórmula
bgrel = bg_microbg_micro + subj_micro creaminess = blur_bg × (1 − bgrel)
con bg_micro y subj_micro la raíz cuadrada media del mismo cociente laplaciano/luminancia local de sharpness_map() (sección 2.1), evaluado sobre el fondo y sobre el sujeto respectivamente, en niveles de 8 bits.
Naturaleza de la fórmula
es un convenio explícito, producto de dos magnitudes medidas y sin constantes libres ajustadas, no una medida canónica de bokeh de la literatura óptica. Combina "cuánto se ha perdido la nitidez del fondo respecto a las escalas de referencia" con "qué fracción del microcontraste total del fotograma sigue viniendo del fondo en vez de del sujeto": un fondo con blur_bg alto pero que conserva bordes de alto contraste (ramas, rejas) tiene bg_rel alto y por tanto creaminess bajo, aun estando bastante desenfocado.
Límite explícito
nada en esta fórmula distingue un desenfoque suave de un doble contorno por aberración esférica de una lente determinada; para eso hace falta inspeccionar uno a uno los círculos de las luces especulares del fondo, algo que ninguna magnitud escalar puede resumir. Depende de la nitidez del propio sujeto por construcción, lo cual es intencional: un fotograma cuyo sujeto no está nítido no tiene "fondo cremoso" en ningún sentido fotográfico útil, y la fórmula lo refleja en vez de ocultarlo.
Condición de cómputo
si la componente conexa del sujeto cubre menos de 64 px² muestreados o el fondo menos de 1024, todas las columnas de esta sección quedan vacías: no hay separación fiable entre sujeto y fondo con tan pocos píxeles de alguno de los dos.
creaminessblur_traits() · line 640
Formula
bgrel = bg_microbg_micro + subj_micro creaminess = blur_bg × (1 − bgrel)
with bg_micro and subj_micro the root mean square of the same Laplacian/local-luminance ratio as in sharpness_map() (section 2.1), evaluated over the background and over the subject respectively, in 8-bit levels.
Nature of the formula
it is an explicit convention, the product of two measured quantities and with no free constants fitted, not a canonical measure of bokeh from the optical literature. It combines "how much sharpness the background has lost with respect to the reference scales" with "what fraction of the frame's total micro-contrast still comes from the background rather than from the subject": a background with a high blur_bg but which retains high-contrast edges (branches, railings) has a high bg_rel and therefore a low creaminess, even while being considerably defocused.
Explicit limit
nothing in this formula distinguishes a gentle defocus from the double contour produced by the spherical aberration of a particular lens; that calls for inspecting one by one the discs of the specular highlights of the background, something no scalar quantity can summarise. It depends on the sharpness of the subject itself by construction, which is intentional: a frame whose subject is not sharp has no "creamy background" in any useful photographic sense, and the formula reflects that instead of concealing it.
Condition for computation
if the subject's connected component covers fewer than 64 px² sampled, or the background fewer than 1024, all the columns of this section are left empty: there is no reliable separation between subject and background with so few pixels in either of the two.
7

Metadatos inferidos

Inferred metadata

infer_date() · infer_focal() · read_exif()

La película sin escanear con perfil de cámara no lleva EXIF de captura, solo el que añade el escáner (fecha de digitalización, marca del escáner). Estas funciones, activadas con --infer-date e --infer-focal, completan datetime y focal35_mm a partir de convenciones de nombrado de archivo, solo cuando el campo EXIF correspondiente está vacío.

Film not scanned with a camera profile carries no capture EXIF, only that which the scanner adds (date of digitisation, make of the scanner). These functions, activated with --infer-date and --infer-focal, complete datetime and focal35_mm from file-naming conventions, but only when the corresponding EXIF field is empty.

7.1Fecha

7.1Date

patrón reconocidoexpresión regularejemplo
fecha completa con separador (19|20)(\d{2})[‑_./](\d{2})[‑_./](\d{2}) 2019‑07‑14
fecha compacta de ocho dígitos (19|20)(\d{2})(\d{2})(\d{2}) 20190714_120000.jpg
año y mes (19|20)(\d{2})[‑_./](\d{2}) 2019‑07
año como componente de directorio ^(19|20)(\d{2})(?:[^\d].*)?$ /fotos/1998/verano/
pattern recognisedregular expressionexample
full date with separator (19|20)(\d{2})[‑_./](\d{2})[‑_./](\d{2}) 2019‑07‑14
compact eight-digit date (19|20)(\d{2})(\d{2})(\d{2}) 20190714_120000.jpg
year and month (19|20)(\d{2})[‑_./](\d{2}) 2019‑07
year as a directory component ^(19|20)(\d{2})(?:[^\d].*)?$ /fotos/1998/verano/

El día se fija a 15 cuando solo hay año y mes, y a 1 de julio cuando solo hay año, para no introducir una estacionalidad artificial en las series temporales del panel. Se rechazan años anteriores a 1900 o posteriores al año en curso.

The day is set to the 15th when only year and month are available, and to 1 July when only the year is, so as not to introduce an artificial seasonality into the panel's time series. Years before 1900 or later than the current year are rejected.

Por qué el año no se acepta dentro de un nombre de archivo. Un patrón como YEAR_DIR_RE exige que el año sea el componente completo de directorio, nunca una subcadena de un nombre de archivo. Un archivo llamado DSC_1988.jpg lleva casi con toda seguridad un número de serie de cámara, no una fecha; interpretarlo como el año 1988 llenó, en una versión temprana de este mismo desarrollo, el eje temporal del panel de fotogramas fantasma repartidos por las décadas de 1980 y 1990, cada uno proveniente de un número de serie que por azar caía en el rango 1900–2026.
Why a year is not accepted inside a file name. A pattern such as YEAR_DIR_RE requires the year to be the complete directory component, never a substring of a file name. A file called DSC_1988.jpg almost certainly carries a camera serial number, not a date; interpreting it as the year 1988 filled the panel's time axis, in an early version of this very development, with ghost frames scattered across the 1980s and 1990s, each one arising from a serial number that happened to fall in the range 1900–2026.

7.2Focal y etiquetas manuales

7.2Focal length and manual tags

infer_focal() busca el patrón (\d{2,3})\s?mm en la ruta completa y acepta el valor solo si cae en [6, 1200] mm, el rango de cualquier objetivo fotográfico real, para no confundir con otros números que aparezcan por azar seguidos de esas dos letras. La opción --tag campo=valor, repetible, fija una columna cualquiera del esquema para todo el lote analizado en esa invocación, pensada para escaneos sin ningún metadato utilizable: --tag model=M5 --tag film=TRI-X.

infer_focal() looks for the pattern (\d{2,3})\s?mm in the full path and accepts the value only if it falls within [6, 1200] mm, the range of any real photographic lens, so as not to be misled by other numbers that happen to appear followed by those two letters. The option --tag field=value, which may be repeated, fixes any column of the schema for the whole batch analysed in that invocation, and is intended for scans with no usable metadata at all: --tag model=M5 --tag film=TRI-X.

8

Estadística de contraste entre grupos

Between-group comparison statistics

analisis-centrado.html · JavaScript, sin dependencias analisis-centrado.html · JavaScript, no dependencies

Todo lo que sigue se ejecuta en el navegador, sin ninguna librería externa. Los generadores de números pseudoaleatorios usados en remuestreos son mulberry32 con semilla fija por cada llamada, de modo que dos ejecuciones sobre el mismo CSV producen exactamente los mismos intervalos y valores p.

Everything that follows runs in the browser, without any external library. The pseudorandom number generators used in resampling are mulberry32 with a seed fixed for each call, so that two runs over the same CSV produce exactly the same intervals and p-values.

8.1Percentiles robustos y recorte de ejes

8.1Robust percentiles and axis trimming

La mediana se calcula por selección parcial (quickselect), no ordenando el vector completo, lo que evita el coste O(n log n) de una ordenación cuando solo hace falta O(n) en el caso medio. El recorte de ejes (robustRange()) sustituye el mínimo y el máximo exactos por los percentiles 1 y 99 cuando hay al menos 50 observaciones: un solo fotograma con un valor extremo no debe aplastar el resto de la escala contra un borde del gráfico. Los puntos que caen fuera del rango recortado se cuentan y se informan en pantalla, nunca se descartan en silencio.

The median is computed by partial selection (quickselect), not by sorting the whole vector, which avoids the O(n log n) cost of a sort when only O(n) is needed in the average case. Axis trimming (robustRange()) replaces the exact minimum and maximum by the 1st and 99th percentiles whenever there are at least 50 observations: a single frame with an extreme value must not crush the rest of the scale against one edge of the plot. Points falling outside the trimmed range are counted and reported on screen; they are never discarded silently.

8.2Intervalo de confianza por bootstrap

8.2Confidence interval by bootstrap

bootstrapCI()percentil, remuestreo con reemplazo
Método
se generan B remuestras con reemplazo del mismo tamaño que la muestra original, se calcula la mediana de cada una y el intervalo de confianza del 95 % son los percentiles 2,5 y 97,5 de esas B medianas (B = 1200 a 2000 según el contexto: 2000 en la tabla por grupo y en los intervalos del contraste entre dos grupos, 1200 en la diferencia entre el primer y el último tercio de una serie, y el campo «remuestreos» del panel Criterio acotado a 2000 en el veredicto de encuadre). Es el método del percentil, el más simple de los bootstrap de Efron; no aplica la corrección de sesgo y aceleración (BCa), por lo que puede ser ligeramente anticonservador con distribuciones muy asimétricas.
Condición mínima
con menos de 6 observaciones el intervalo no se calcula y se muestra vacío en vez de un rango sin significado estadístico.
Variante de la media
bootstrapMeanCI() repite el mismo procedimiento sobre la media en lugar de la mediana. La usa el veredicto de encuadre de la sección 2.5 para el sesgo direccional: xnorm e ynorm llevan signo y se cancelan entre sí, de modo que lo que hay que acotar es su media, no su mediana.
bootstrapCI()percentile, resampling with replacement
Method
B resamples with replacement of the same size as the original sample are generated, the median of each is computed and the 95 % confidence interval is given by the 2.5th and 97.5th percentiles of those B medians (B = 1200 to 2000 according to context: 2000 in the per-group table and in the intervals of the contrast between two groups, 1200 in the difference between the first and the last third of a series, and the “resamples” field of the Criterion panel capped at 2000 in the framing verdict). This is the percentile method, the simplest of Efron's bootstraps; it does not apply the bias-corrected and accelerated adjustment (BCa), and so it can be slightly anticonservative with strongly skewed distributions.
Minimum condition
with fewer than 6 observations the interval is not computed and is shown empty rather than as a range without statistical meaning.
Mean variant
bootstrapMeanCI() repeats the same procedure on the mean rather than the median. The framing verdict of section 2.5 uses it for the directional bias: xnorm and ynorm are signed and cancel one another, so what has to be bounded is their mean, not their median.

8.3Contraste de dos grupos

8.3Testing two groups

estadísticofórmula o métodolectura
permTest() diferencia de medianas observada; se mezclan las dos muestras y se recalcula la diferencia B veces con la etiqueta de grupo permutada al azar, siendo B el campo «remuestreos» del panel Criterio (10 000 por defecto, acotado a [1000, 50 000]); el valor p es (b+1)/(B+1), con b el número de permutaciones cuya diferencia absoluta iguala o supera a la observada. El +1 en el numerador y en el denominador es la corrección de continuidad que impide informar p = 0. no asume normalidad ni varianzas iguales; válido con cualquier forma de distribución.
mannWhitney()
U = R1 − n1(n1+1)2     δCliff = 2Un1n2 − 1
con R1 la suma de rangos del primer grupo sobre la unión ordenada de ambos, y corrección de empates en la varianza: Var(U) = n1n212 [(N+1) − ∑(t³−t)/(N(N−1))].
δCliff ∈ [−1,1]: 0 sin efecto, ±1 separación total entre los dos grupos, sin solape alguno.
ksTest() estadístico D de Kolmogórov‑Smirnov de dos muestras (distancia máxima entre las dos acumuladas empíricas), con el valor p por la serie asintótica de Kolmogórov evaluada hasta convergencia numérica (100 términos como máximo, corte cuando el término deja de aportar). sensible a diferencias de forma, no solo de posición: dos distribuciones con la misma mediana pero distinta dispersión pueden dar D grande y U de Mann‑Whitney pequeña.
statisticformula or methodreading
permTest() the observed difference of medians; the two samples are pooled and the difference is recomputed B times with the group label permuted at random, B being the “resamples” field of the Criterion panel (10,000 by default, clamped to [1000, 50,000]); the p-value is (b+1)/(B+1), with b the number of permutations whose absolute difference equals or exceeds the observed one. The +1 in numerator and denominator is the continuity correction that prevents reporting p = 0. it assumes neither normality nor equal variances; valid for any shape of distribution.
mannWhitney()
U = R1 − n1(n1+1)2     δCliff = 2Un1n2 − 1
with R1 the sum of the ranks of the first group over the sorted union of both, and a correction for ties in the variance: Var(U) = n1n212 [(N+1) − ∑(t³−t)/(N(N−1))].
δCliff ∈ [−1,1]: 0 for no effect, ±1 for total separation between the two groups, with no overlap at all.
ksTest() the two-sample Kolmogorov‑Smirnov D statistic (the maximum distance between the two empirical cumulative distributions), with the p-value from Kolmogorov's asymptotic series evaluated to numerical convergence (100 terms at most, cut off when the term ceases to contribute). sensitive to differences of shape, not only of location: two distributions with the same median but different dispersion can give a large D and a small Mann‑Whitney U.
Un error real, corregido durante este mismo desarrollo. La primera versión de la serie de Kolmogórov se sumaba con signos alternos sin criterio de parada:
// version con error
p += 2 * (-1)**(k-1) * exp(-2*k*k*lambda*lambda)
Esa serie oscila y no converge cuando λ (proporcional a D) es pequeño, que es exactamente el caso de dos muestras idénticas: en vez de converger a p = 1, el resultado dependía de en qué término se cortara la suma. La versión corregida acumula el valor absoluto de cada término y detiene la suma cuando dos criterios de convergencia se cumplen a la vez (término relativo al anterior o término relativo a la suma acumulada), lo que hace que dos muestras idénticas den p = 1 de forma estable.
A real bug, corrected during this very development. The first version of the Kolmogorov series was summed with alternating signs and no stopping criterion:
// buggy version
p += 2 * (-1)**(k-1) * exp(-2*k*k*lambda*lambda)
That series oscillates and does not converge when λ (proportional to D) is small, which is exactly the case of two identical samples: instead of converging to p = 1, the result depended on which term the sum was cut off at. The corrected version accumulates the absolute value of each term and stops the summation when two convergence criteria are met at once (the term relative to the previous one, or the term relative to the accumulated sum), which makes two identical samples give p = 1 stably.

8.4Elipse del 50 %

8.4The 50 % ellipse

ellipse50()autovalores de la matriz de covarianza 2×2
Fórmula
con S la matriz de covarianza muestral de (x,y), sus autovalores λ1,2 se obtienen en forma cerrada para una matriz 2×2:
λ1,2 = tr(S)2 ± √(tr(S)²4 − det(S))     θ = ½atan2(2sxy, sxx−syy)
y los semiejes de la elipse son √(χ²2(0,5))·√λ1,2, con χ²2(0,5) = 1,3863 el percentil 50 de una χ² con 2 grados de libertad: el contorno que, bajo el supuesto de normalidad bivariante, encierra el 50 % de la masa de probabilidad.
Error corregido
una versión anterior calculaba el ángulo como atan2(λ1−sxx, sxy), fórmula degenerada cuando la covarianza cruzada sxy tiende a cero (el caso más común, dos variables poco correlacionadas): el cociente se vuelve 0/0 y el ángulo pierde precisión numérica. La forma ½atan2(2sxy, sxx−syy) es la solución estándar del problema de autovectores de una matriz simétrica 2×2 y no tiene esa singularidad.
ellipse50()eigenvalues of the 2×2 covariance matrix
Formula
with S the sample covariance matrix of (x,y), its eigenvalues λ1,2 are obtained in closed form for a 2×2 matrix:
λ1,2 = tr(S)2 ± √(tr(S)²4 − det(S))     θ = ½atan2(2sxy, sxx−syy)
and the semi-axes of the ellipse are √(χ²2(0.5))·√λ1,2, with χ²2(0.5) = 1.3863 the 50th percentile of a χ² with 2 degrees of freedom: the contour which, under the assumption of bivariate normality, encloses 50 % of the probability mass.
Corrected error
an earlier version computed the angle as atan2(λ1−sxx, sxy), a formula that degenerates when the cross-covariance sxy tends to zero (the commonest case, two weakly correlated variables): the ratio becomes 0/0 and the angle loses numerical precision. The form ½atan2(2sxy, sxx−syy) is the standard solution of the eigenvector problem for a symmetric 2×2 matrix and has no such singularity.
9

Series temporales y detección de hallazgos

Time series and detection of findings

pestañas Evolución y Conclusiones Evolution and Conclusions tabs

9.1Sesionización

9.1Sessionisation

Pseudorreplicación. Los fotogramas de una misma sesión de disparo comparten escena, luz y, en película, el mismo negativo o rollo: tratarlos como observaciones estadísticamente independientes multiplica la evidencia por el tamaño de la ráfaga sin que exista tanta información real. Con 36 tomas casi idénticas de una misma escena, un contraste que trate cada fotograma como independiente puede declarar significativo un efecto que, mirado por sesión, no lo es en absoluto.
Pseudoreplication. The frames of one and the same shooting session share scene, light and, on film, the same negative or roll: treating them as statistically independent observations multiplies the evidence by the size of the burst without that much real information being present. With 36 nearly identical exposures of one scene, a test that treats each frame as independent may declare significant an effect which, viewed by session, is nothing of the sort.

sessionsOf() agrupa los fotogramas por grupo y, si hay fecha, por día natural; si no hay fecha, por bloques consecutivos de 36 fotogramas (la extensión de un carrete de 135). Todo el motor de hallazgos de la pestaña Conclusiones y los contrastes de tendencia de la pestaña Evolución se calculan sobre la mediana de cada sesión, no sobre el fotograma individual.

sessionsOf() groups the frames by group and, where a date is available, by calendar day; where there is no date, by consecutive blocks of 36 frames (the length of a 135 cassette). The whole findings engine of the Conclusions tab and the trend tests of the Evolution tab are computed on the median of each session, not on the individual frame.

Comprobación. Sobre un conjunto construido a propósito de 630 fotogramas repartidos en 14 sesiones, con una tendencia real nula entre sesiones (p = 0,58 calculado de forma independiente sobre las 14 medianas) pero con variación aleatoria fina dentro de cada sesión, tratar cada fotograma como observación independiente da z ≈ 3,6 en la prueba de la sección 9.2 (equivalente a p ≈ 3×10⁻⁴, un falso hallazgo muy marcado). El mismo conjunto, contrastado por sesión como hace el panel, no declara ningún cambio.
Check. On a set built for the purpose, of 630 frames distributed over 14 sessions, with a genuinely null trend between sessions (p = 0.58 computed independently on the 14 medians) but with fine random variation within each session, treating each frame as an independent observation gives z ≈ 3.6 in the test of section 9.2 (equivalent to p ≈ 3×10⁻⁴, a very pronounced false finding). The same set, tested by session as the panel does, declares no change at all.

9.2Tendencia monótona: tau de Mann‑Kendall

9.2Monotonic trend: the Mann‑Kendall tau

mannKendall()Mann (1945) · Kendall (1975)
Estadístico
S = ∑i<j sgn(yj − yi)     τ = S√((n0−Tx)(n0−Ty)),   n0=n(n−1)2
con Tx, Ty las correcciones por empates (∑t(t−1)/2 sobre cada grupo de valores repetidos). No supone linealidad ni normalidad: solo cuenta, de todos los pares posibles ordenados por el eje temporal, cuántos tienen el mismo signo de variación y cuántos el signo contrario.
Complejidad
contar todos los pares es O(n²), inviable con miles de fotogramas y treinta descriptores por contraste. La implementación cuenta S con un árbol de Fenwick (árbol binario indexado) sobre el rango de valores y: al procesar las observaciones en orden de x, para cada yk se consulta cuántos valores menores ya se han insertado (suma de prefijo, O(log n)) y se inserta yk (O(log n)), lo que deja el cálculo completo en O(n log n). Los empates en x se procesan por bloques, insertando todo el bloque después de haber consultado a todos sus miembros, para que dos observaciones simultáneas no cuenten como concordantes ni discordantes entre sí.
mannKendall()Mann (1945) · Kendall (1975)
Statistic
S = ∑i<j sgn(yj − yi)     τ = S√((n0−Tx)(n0−Ty)),   n0=n(n−1)2
with Tx, Ty the corrections for ties (∑t(t−1)/2 over each group of repeated values). It assumes neither linearity nor normality: it merely counts, among all the possible pairs ordered along the time axis, how many share the same sign of variation and how many the opposite one.
Complexity
counting all the pairs is O(n²), unfeasible with thousands of frames and thirty descriptors per comparison. The implementation counts S with a Fenwick tree (a binary indexed tree) over the range of y values: processing the observations in order of x, for each yk it queries how many smaller values have already been inserted (a prefix sum, O(log n)) and inserts yk (O(log n)), which leaves the whole computation at O(n log n). Ties in x are processed in blocks, the whole block being inserted after all its members have been queried, so that two simultaneous observations count as neither concordant nor discordant with each other.
Comprobación. S calculado con el árbol de Fenwick coincide de forma exacta con el recuento por fuerza bruta O(n²) en 300 series aleatorias con empates tanto en x como en y. Con n = 6000 y 31 descriptores, el tiempo de cálculo bajó de varios minutos a 367 ms.
Check. S computed with the Fenwick tree agrees exactly with the O(n²) brute-force count on 300 random series with ties in both x and y. With n = 6000 and 31 descriptors, the computation time fell from several minutes to 367 ms.

9.3Pendiente robusta: Theil‑Sen

9.3Robust slope: Theil‑Sen

theilSen()Theil (1950) · Sen (1968)
Fórmula
mediana de las pendientes de todos los pares admisibles:
m = medi<j, |xj−xi|>ε yj−yixj−xi
insensible a valores atípicos porque, a diferencia de la pendiente de mínimos cuadrados, cada observación individual solo puede desplazar la mediana de las pendientes, no arrastrarla proporcionalmente a su distancia al resto.
Umbral de separación mínima, ε
ε se fija en el 0,1 % del recorrido total del eje x, no en un valor absoluto. Se exige además un mínimo de 8 observaciones y al menos 8 pares admisibles.
theilSen()Theil (1950) · Sen (1968)
Formula
the median of the slopes of all the admissible pairs:
m = medi<j, |xj−xi|>ε yj−yixj−xi
insensitive to outliers because, unlike the least-squares slope, any individual observation can only displace the median of the slopes, not drag it in proportion to its distance from the rest.
Minimum separation threshold, ε
ε is set at 0.1 % of the total span of the x axis, not at an absolute value. A minimum of 8 observations and at least 8 admissible pairs are further required.
Un error real, corregido durante este mismo desarrollo. La primera versión usaba un umbral absoluto (|xj−xi| > 10⁻¹²). Con marcas de tiempo en milisegundos, dos fotogramas de la misma ráfaga distan unos pocos miles de unidades en x: muy por encima de ese umbral absoluto, pero una fracción ínfima del recorrido total del eje (típicamente varios años en milisegundos, ∼10⁵⁵ unidades). La pendiente de un par así, con denominador minúsculo en términos relativos, se disparaba a valores de miles de unidades por año, y con pocas observaciones esa pendiente aberrante arrastraba la mediana. El síntoma observado durante las pruebas fueron pendientes anuales de hasta −17 682 unidades por año en carpetas con varias tomas por día. El umbral relativo al recorrido total resuelve el problema sin más parámetros libres.
A real bug, corrected during this very development. The first version used an absolute threshold (|xj−xi| > 10⁻¹²). With timestamps in milliseconds, two frames of the same burst are a few thousand units apart in x: far above that absolute threshold, but a minute fraction of the total span of the axis (typically several years in milliseconds, ∼10⁵⁵ units). The slope of such a pair, with a denominator minute in relative terms, shot up to values of thousands of units per year, and with few observations that aberrant slope dragged the median with it. The symptom observed during the tests was annual slopes of as much as −17 682 units per year in folders with several exposures per day. A threshold relative to the total span resolves the problem with no further free parameters.

9.4Punto de ruptura: prueba de Pettitt

9.4Change point: Pettitt's test

pettitt()Pettitt (1979)
Estadístico
versión no paramétrica de la prueba de razón de verosimilitud de Mann‑Whitney aplicada a todos los posibles puntos de corte:
Ut = Ut−1 + #{yj>yt : j>t} − #{yj<yt : j>t} K = maxt|Ut|     p ≈ 2 exp−6K²n³+n²
El punto de corte más probable es el índice t que maximiza |Ut|. La identidad recursiva de acumulación permite calcular Ut para todo t en O(n log n) mediante búsqueda binaria sobre el vector ordenado, en vez de recontar todas las comparaciones en cada t.
Uso en el panel
solo se informa una fecha de ruptura cuando p < 0,05 y el punto de corte no cae en el 10 % inicial o final de la serie, para no señalar como "ruptura" lo que en realidad es el principio o el final del propio archivo.
pettitt()Pettitt (1979)
Statistic
a non-parametric version of the Mann‑Whitney likelihood-ratio test applied to every possible change point:
Ut = Ut−1 + #{yj>yt : j>t} − #{yj<yt : j>t} K = maxt|Ut|     p ≈ 2 exp−6K²n³+n²
The most probable change point is the index t that maximises |Ut|. The recursive accumulation identity allows Ut to be computed for every t in O(n log n) by binary search over the sorted vector, instead of recounting all the comparisons at each t.
Use in the panel
a change-point date is reported only when p < 0.05 and the change point does not fall in the first or last 10 % of the series, so as not to flag as a "change point" what is in reality the beginning or the end of the archive itself.
Comprobación. K coincide de forma exacta con el cálculo por fuerza bruta en 200 series aleatorias con empates. Sobre un escalón sintético de dos desviaciones típicas en t = 30 de una serie de 60 puntos, la prueba localiza el corte en t = 29 con p ≈ 4,9×10⁻¹⁰.
Check. K agrees exactly with the brute-force computation on 200 random series with ties. On a synthetic step of two standard deviations at t = 30 in a series of 60 points, the test locates the change at t = 29 with p ≈ 4.9×10⁻¹⁰.

9.5Corrección por comparaciones múltiples

9.5Correction for multiple comparisons

benjaminiHochberg()Benjamini y Hochberg (1995)
Procedimiento
se ordenan los m valores p de menor a mayor, p(1) ≤…≤p(m), y el valor q de cada uno es
q(k) = minj≥k min(1, p(j)·mj)
el mínimo acumulado desde el extremo de mayor p, que garantiza que la secuencia de q sea monótona no decreciente.
Por qué hace falta
con los 32 descriptores del catálogo SENTIDO contrastados a α = 0,05 cada uno, la probabilidad de al menos un falso positivo por puro azar es 1−0,95³² ≈ 81 %, no el 5 % que parece a primera vista. El procedimiento de Benjamini‑Hochberg controla la proporción esperada de falsos hallazgos entre los declarados, no la probabilidad de cometer al menos uno, lo que permite seguir declarando varios hallazgos genuinos sin inflar el número de falsos.
benjaminiHochberg()Benjamini and Hochberg (1995)
Procedure
the m p-values are sorted from smallest to largest, p(1) ≤…≤p(m), and the q-value of each is
q(k) = minj≥k min(1, p(j)·mj)
the running minimum from the large-p end, which guarantees that the sequence of q-values is monotonically non-decreasing.
Why it is needed
with the 32 descriptors of the SENTIDO catalogue each tested at α = 0.05, the probability of at least one false positive by pure chance is 1−0.95³² ≈ 81 %, not the 5 % that it appears to be at first sight. The Benjamini‑Hochberg procedure controls the expected proportion of false findings among those declared, not the probability of committing at least one, which makes it possible to go on declaring several genuine findings without inflating the number of false ones.
Comprobación. Sobre 300 conjuntos de 30 valores p uniformes en [0,1] (ausencia total de efecto por construcción), la fracción de veces que el procedimiento declara algún q < 0,1 es del 9,3 %, coherente con el control de la tasa de falso descubrimiento y no con una prueba familywise estricta. Sobre conjuntos con 5 efectos reales fuertes mezclados entre 25 nulos, detecta al menos 4 de los 5 en el 100 % de las 200 repeticiones.
Check. Over 300 sets of 30 p-values uniform on [0,1] (a total absence of effect by construction), the fraction of times the procedure declares some q < 0.1 is 9.3 %, consistent with control of the false discovery rate and not with a strict family-wise test. Over sets with 5 strong real effects mixed among 25 null ones, it detects at least 4 of the 5 in 100 % of the 200 repetitions.

El panel declara un hallazgo (solido()) solo si q < 0,10 y |δCliff| ≥ 0,15 a la vez. La segunda condición es indispensable: con miles de sesiones, una τ de 0,02, sin ninguna relevancia práctica, puede alcanzar significación estadística.

The panel declares a finding (solido()) only if q < 0.10 and |δCliff| ≥ 0.15 at the same time. The second condition is indispensable: with thousands of sessions, a τ of 0.02, of no practical relevance whatever, can attain statistical significance.

9.6Reducción de dimensión: componentes principales por rotaciones de Jacobi

9.6Dimensionality reduction: principal components by Jacobi rotations

robustPCA() · jacobiEigen()Jacobi (1846)
Estandarización
cada descriptor se centra en su mediana y se escala por 1,4826·MAD, no por la media y la desviación típica: un puñado de fotogramas extremos no debe fijar la escala de los ejes. Los valores se recortan a ±6 desviaciones robustas y los ausentes se imputan a la mediana (z=0), sin contribuir ni a favor ni en contra de ninguna dirección.
Diagonalización
los autovalores y autovectores de la matriz de covarianza p×p se obtienen por el método clásico de Jacobi: en cada barrido se anula, mediante una rotación plana de ángulo
θ = Aqq−App2Apq,   t = sgn(θ)|θ|+√(θ²+1),   c = 1√(t²+1), s=tc
el elemento fuera de la diagonal más grande en valor absoluto, hasta que la suma de cuadrados de los elementos fuera de la diagonal cae por debajo de 10⁻¹⁸ o se agotan 100 barridos. Es un método sin dependencias externas, adecuado para matrices pequeñas (aquí, del orden de 30×30 descriptores) y numéricamente estable porque cada paso es una rotación ortogonal exacta.
Signo canónico
el signo de cada componente se fija para que su carga de mayor magnitud sea positiva; sin este convenio, dos ejecuciones con datos casi idénticos podrían devolver ejes reflejados y la trayectoria de deriva aparecería invertida sin que nada real hubiera cambiado.
robustPCA() · jacobiEigen()Jacobi (1846)
Standardisation
each descriptor is centred on its median and scaled by 1.4826·MAD, not by the mean and the standard deviation: a handful of extreme frames must not set the scale of the axes. Values are clipped to ±6 robust deviations and missing ones are imputed to the median (z=0), so as to contribute neither for nor against any direction.
Diagonalisation
the eigenvalues and eigenvectors of the p×p covariance matrix are obtained by Jacobi's classical method: in each sweep the largest off-diagonal element in absolute value is annihilated by a plane rotation of angle
θ = Aqq−App2Apq,   t = sgn(θ)|θ|+√(θ²+1),   c = 1√(t²+1), s=tc
until the sum of the squares of the off-diagonal elements falls below 10⁻¹⁸ or 100 sweeps are exhausted. It is a method without external dependencies, suited to small matrices (here, of the order of 30×30 descriptors) and numerically stable because each step is an exact orthogonal rotation.
Canonical sign
the sign of each component is fixed so that its largest loading in magnitude is positive; without this convention, two runs on almost identical data could return reflected axes and the drift trajectory would appear inverted without anything real having changed.
Comprobación. Sobre matrices de covarianza construidas a mano con autovalores y autovectores conocidos en forma cerrada (incluida una matriz aleatoria simétrica 5×5), el residuo máximo de Av − λv es del orden de 10⁻¹⁵ (1,1×10⁻¹⁵ en la ejecución registrada), y la suma de autovalores coincide con la traza de la matriz dentro de la misma tolerancia. Los casos 2×2 con solución cerrada conocida coinciden dentro de 10⁻¹⁰.
Check. On covariance matrices constructed by hand with eigenvalues and eigenvectors known in closed form (including a random symmetric 5×5 matrix), the maximum residual of Av − λv is of the order of 10⁻¹⁵ (1.1×10⁻¹⁵ in the run on record), and the sum of the eigenvalues agrees with the trace of the matrix to within the same tolerance. The 2×2 cases with a known closed-form solution agree to within 10⁻¹⁰.

9.7Rectitud de la trayectoria

9.7Straightness of the trajectory

Sobre los centroides de sesiones agrupados en bloques temporales sucesivos en el plano de los dos primeros componentes, la rectitud mide si el conjunto avanza en una dirección sostenida o deambula sin rumbo:

Over the session centroids grouped into successive temporal blocks in the plane of the first two components, straightness measures whether the body of work advances in a sustained direction or wanders without a course:

S = ‖centroideúltimo − centroideprimero‖∑‖centroidek − centroidek−1‖ ∈ (0, 1]
S = ‖centroidlast − centroidfirst‖∑‖centroidk − centroidk−1‖ ∈ (0, 1]

S = 1 es una línea recta perfecta (desplazamiento neto igual a la distancia recorrida); valores próximos a 0 indican una trayectoria que vuelve sobre sí misma. El valor p se calcula permutando al azar el orden de las sesiones y recalculando S 400 veces: si el orden temporal real no aporta nada sobre un orden aleatorio, la rectitud observada no debería destacar frente a las permutadas.

S = 1 is a perfect straight line (net displacement equal to the distance travelled); values close to 0 indicate a trajectory that doubles back on itself. The p-value is computed by permuting the order of the sessions at random and recomputing S 400 times: if the real temporal order adds nothing over a random order, the observed straightness ought not to stand out against the permuted ones.

Comprobación. Con puntos generados a lo largo de una recta con ruido pequeño, S = 1,00 y p = 0,0033; con puntos distribuidos al azar sin ninguna dirección, S = 0,105 y p = 0,8505, la trayectoria no se distingue de un paseo aleatorio.
Check. With points generated along a straight line with small noise, S = 1.00 and p = 0.0033; with points distributed at random with no direction whatever, S = 0.105 and p = 0.8505, the trajectory being indistinguishable from a random walk.

9.8Correlación de rangos: rho de Spearman

9.8Rank correlation: Spearman's rho

ρ es la correlación de Pearson calculada sobre los rangos de cada variable en vez de sobre sus valores; detecta relaciones monótonas, no solo lineales. El valor p sale de permutar al azar los rangos de una de las dos variables y recalcular ρ; con miles de fotogramas, 2000 permutaciones bastan para acotar el valor p con margen suficiente, así que el número de iteraciones se ajusta de forma adaptativa, entre 500 y 2000, según n, para mantener acotado el coste total (iteraciones × n) cuando hay muchas observaciones.

ρ is the Pearson correlation computed on the ranks of each variable rather than on their values; it detects monotonic relations, not merely linear ones. The p-value comes from permuting at random the ranks of one of the two variables and recomputing ρ; with thousands of frames, 2000 permutations suffice to bound the p-value with an ample margin, so the number of iterations is adjusted adaptively, between 500 and 2000, according to n, in order to keep the total cost (iterations × n) bounded when there are many observations.

10

Catálogo de límites y modos de fallo

Catalogue of limits and failure modes

consolidado, con referencia cruzada a cada sección consolidated, cross-referenced to each section

Esta sección reúne en un solo lugar los límites que ya se han citado dispersos, y añade uno que no se había hecho explícito hasta ahora: la resolución real a la que trabaja todo el análisis.

This section gathers in one place the limits already cited in scattered form, and adds one that has not been made explicit until now: the actual resolution at which the whole analysis works.

10.1Techo de resolución de todo el análisis

10.1The resolution ceiling of the whole analysis

Ninguna medida de este documento se calcula sobre el archivo a su resolución nativa. analyse() reduce cada imagen en dos pasos antes de calcular nada: primero im.draft("RGB", (2·max_side, 2·max_side)), que pide al descodificador JPEG una versión aproximada a esa escala directamente en el dominio de la transformada (evita descodificar un escaneo de 50 Mpx entero), y después im.thumbnail((max_side, max_side)) con interpolación bilineal, que sí aplica un filtro anti‑alias antes de decimar. Con el valor por defecto --max-side 512, un escaneo de 6000×4000 px (24 Mpx) queda en 512×341 px antes de que sharpness_map(), tone_traits(), color_traits(), texture_traits() o blur_traits() vean un solo píxel: un factor 11,7× de reducción lineal, 137,5× en área.
No measurement in this document is computed on the file at its native resolution. analyse() reduces every image in two steps before computing anything: first im.draft("RGB", (2·max_side, 2·max_side)), which asks the JPEG decoder for a version approximate to that scale directly in the transform domain (thereby avoiding the decoding of an entire 50 Mpx scan), and then im.thumbnail((max_side, max_side)) with bilinear interpolation, which does apply an anti‑aliasing filter before decimating. With the default value --max-side 512, a scan of 6000×4000 px (24 Mpx) is left at 512×341 px before sharpness_map(), tone_traits(), color_traits(), texture_traits() or blur_traits() sees a single pixel: a factor of 11.7× in linear reduction, 137.5× in area.

Las consecuencias no son iguales para todas las secciones. La geometría del sujeto (sección 2) y el tono global (sección 3) resumen regiones amplias del fotograma, y 512 px suelen bastar para localizar dónde está la región enfocada o cuál es la clave de exposición. El grano (grain_levels), la acutancia y la pendiente espectral (sección 5) son otra historia: el grano físico de una película, que en un escaneo a resolución completa ocupa un puñado de píxeles, queda promediado por el filtro anti‑alias del redimensionado antes de que estimate_noise() pueda medirlo. Lo que grain_levels informa por defecto no es el grano de la emulsión a resolución de escaneo, sino una micro‑variación tonal a la escala de 512 px, una magnitud relacionada pero distinta, y comparable entre fotogramas de este mismo corpus solo si todos se procesaron con el mismo --max-side. Subir ese valor (por ejemplo, --max-side 2000) acerca la medida al grano real a costa de un tiempo de proceso mayor.

The consequences are not the same for every section. Subject geometry (section 2) and global tone (section 3) summarise broad regions of the frame, and 512 px are usually enough to locate where the region in focus lies or what the exposure key is. Grain (grain_levels), acutance and spectral slope (section 5) are another matter: the physical grain of a film, which in a full-resolution scan occupies a handful of pixels, is averaged away by the anti‑aliasing filter of the resizing before estimate_noise() can measure it. What grain_levels reports by default is not the grain of the emulsion at scanning resolution, but a tonal micro‑variation at the scale of 512 px, a related but distinct quantity, and comparable between frames of this same corpus only if all of them were processed with the same --max-side. Raising that value (for instance, --max-side 2000) brings the measurement closer to the real grain, at the cost of a longer processing time.

10.1bis¿Influye la resolución nativa del sensor o del escáner?

10.1bisDoes the native resolution of the sensor or the scanner matter?

La pregunta natural, dado lo anterior: si dos cuerpos digitalizan a resoluciones nativas distintas y ambos acaban reducidos a los mismos ≤512 px antes de medir nada, ¿queda alguna huella de la resolución de partida? Se comprobó de forma empírica en vez de razonarlo solo en abstracto, generando ocho escenas sintéticas independientes, cada una guardada dos veces—a 6,76 Mpx y a 3,38 Mpx, la misma razón de área que separa un sensor de 40 Mpx de uno de 20 Mpx— mediante una reducción de área propia (Image.BOX, sin alias), y pasando ambas versiones de cada escena por la función analyse() real, con el mismo --max-side 512 por defecto.

The natural question, given the foregoing: if two bodies digitise at different native resolutions and both end up reduced to the same ≤512 px before anything is measured, does any trace of the starting resolution remain? This was checked empirically rather than reasoned out in the abstract, by generating eight independent synthetic scenes, each saved twice—at 6.76 Mpx and at 3.38 Mpx, the same area ratio that separates a 40 Mpx sensor from a 20 Mpx one— by means of a proper area reduction (Image.BOX, without aliasing), and passing both versions of each scene through the real analyse() function, with the same default --max-side 512.

descriptormediana a 6,76 Mpxmediana a 3,38 Mpx diferenciaconsistencia de signo
acutance28,029,0 −1,08/8
grain_levels6,967,29 −0,338/8
area_frac0,04360,0425 +0,00267/8
r_norm0,6010,606 −0,0047/8
alpha_spectral2,0992,094 +0,0068/8
key_stops0,0090,009 0,0000/8
dr_stops8,0128,019 −0,0074/8
focus_frac1,0001,000 0,0000/8
descriptormedian at 6.76 Mpxmedian at 3.38 Mpx differencesign consistency
acutance28.029.0 −1.08/8
grain_levels6.967.29 −0.338/8
area_frac0.04360.0425 +0.00267/8
r_norm0.6010.606 −0.0047/8
alpha_spectral2.0992.094 +0.0068/8
key_stops0.0090.009 0.0000/8
dr_stops8.0128.019 −0.0074/8
focus_frac1.0001.000 0.0000/8
Hay un efecto, es pequeño, y no va en la dirección que cabría suponer. Con una razón de área 2:1 en el origen (equivalente a 40 frente a 20 Mpx), acutance y grain_levels se desplazan entre un 3 % y un 5 % relativo de forma consistente en las ocho repeticiones, y area_frac y r_norm un 1–3 %. Pero el sentido del desplazamiento es el contrario al que se esperaría de "más resolución nativa preserva más detalle real": en esta prueba, el archivo de menor resolución nativa dio más acutancia y más grano aparente, no menos. La causa más plausible no es que se pierda información al reducir, sino cómo interactúa el filtro de área de Image.BOX con el espectro casi autosimilar (1/f²) de una escena natural: el filtrado de área no es una renormalización perfecta de ese tipo de campo, y dos reducciones de distinta magnitud parten de un residuo espectral ligeramente distinto cerca de su propia frecuencia de Nyquist. No hay, por tanto, un factor de corrección simple ("divide por los megapíxeles") que deshaga el efecto, porque ni siquiera el signo es uniforme entre descriptores: alpha_spectral y las columnas de tono (key_stops, dr_stops) resultaron esencialmente insensibles (variación menor al 1 % relativo, o sin consistencia de signo, es decir, dentro del ruido), mientras que focus_frac dio una diferencia nula en esta prueba concreta, aunque con una reserva importante: la escena sintética usada no incluye ninguna región genuinamente desenfocada por óptica, así que ese resultado nulo pesa menos que los demás.
There is an effect, it is small, and it does not run in the direction one might suppose. With an area ratio of 2:1 at the source (equivalent to 40 against 20 Mpx), acutance and grain_levels shift by between 3 % and 5 % in relative terms, consistently across the eight repetitions, and area_frac and r_norm by 1–3 %. But the sense of the shift is the opposite of what one would expect from "more native resolution preserves more real detail": in this test, the file of lower native resolution gave more acutance and more apparent grain, not less. The likeliest cause is not that information is lost in reducing, but the way the area filter of Image.BOX interacts with the almost self-similar (1/f²) spectrum of a natural scene: area filtering is not a perfect renormalisation of that kind of field, and two reductions of different magnitude start from a slightly different spectral residue near their own Nyquist frequency. There is therefore no simple correction factor ("divide by the megapixels") that would undo the effect, because not even the sign is uniform across descriptors: alpha_spectral and the tone columns (key_stops, dr_stops) proved essentially insensitive (a variation of less than 1 % in relative terms, or without sign consistency, that is, within the noise), while focus_frac gave a null difference in this particular test, though with an important reservation: the synthetic scene used includes no region genuinely defocused by optics, so that null result carries less weight than the others.

La consecuencia práctica es concreta: antes de leer una diferencia de acutance, grain_levels, area_frac o r_norm entre dos grupos como una diferencia de comportamiento fotográfico, hay que descartar que sea, en realidad, una diferencia de resolución de origen entre los archivos de cada grupo. width, height y una columna derivada, mpx (megapíxeles de origen, ancho×alto/10&sup6;), están disponibles en la pestaña Explorar del panel exactamente para esa comprobación: si la resolución de origen correlaciona con el descriptor que se está comparando dentro de cada grupo, la matriz de correlaciones de la sección 9.8 lo hará visible antes de que ninguna conclusión de la pestaña Conclusiones se dé por buena.

The practical consequence is concrete: before reading a difference of acutance, grain_levels, area_frac or r_norm between two groups as a difference of photographic behaviour, one must rule out that it is, in reality, a difference of source resolution between the files of each group. width, height and a derived column, mpx (source megapixels, width×height/10&sup6;), are available in the panel's Explore tab for precisely that check: if the source resolution correlates with the descriptor being compared within each group, the correlation matrix of section 9.8 will make it visible before any conclusion from the Conclusions tab is accepted as sound.

10.2Archivos HDR y de alto rango dinámico

10.2HDR and high-dynamic-range files

Image.open().convert("RGB") fuerza 8 bits por canal en [0,255] incondicionalmente. Si el archivo de entrada es HDR real sin aplanar (coma flotante de 32 bits, EXR, un TIFF lineal, o un archivo compuesto que aún conserve información por encima de blanco de referencia), esa conversión trunca cualquier valor fuera de [0,255] antes de que dr_stops o cualquier otro descriptor lo vea. El límite teórico duro de dr_stops para cualquier archivo que pase por este pipeline es log2(255/1) ≈ 8 diafragmos, y en la práctica bastante menos, porque el recorte a percentiles de la sección 3.2 ya descarta los extremos: ninguna escena, por amplia que sea su latitud real, puede informar más que eso.
Image.open().convert("RGB") forces 8 bits per channel in [0,255] unconditionally. If the input file is genuine unflattened HDR (32-bit floating point, EXR, a linear TIFF, or a composited file still retaining information above reference white), that conversion truncates any value outside [0,255] before dr_stops or any other descriptor sees it. The hard theoretical limit of dr_stops for any file passing through this pipeline is log2(255/1) ≈ 8 stops, and in practice appreciably less, because the percentile trimming of section 3.2 already discards the extremes: no scene, however wide its real latitude, can report more than that.

Con un HDR fusionado y mapeado tonalmente para su visualización, que es el caso habitual en un flujo de trabajo fotográfico, no hay error técnico: el archivo que llega al script ya es un RGB convencional de 8 o 16 bits. Pero dr_stops mide entonces la latitud que sobrevive en los píxeles del archivo final, no la de la escena capturada. El mapeo tonal comprime deliberadamente los diafragmos de la escena para que quepan en un rango de visualización estrecho: una escena de 12 diafragmos puede acabar ocupando 6 u 8 en el archivo, y un mapeo tonal agresivo de estilo "plano" puede mostrar dr_stops alto por haber estirado el histograma, sin que la escena original fuera especialmente contrastada. El número es honesto sobre lo que mide (diafragmos que sobreviven en los píxeles recibidos); no sabe nada de la escena real si hubo mapeo tonal de por medio.

With an HDR merged and tone-mapped for display, which is the usual case in a photographic workflow, there is no technical error: the file reaching the script is already a conventional 8- or 16-bit RGB. But dr_stops then measures the latitude that survives in the pixels of the final file, not that of the scene captured. Tone mapping deliberately compresses the stops of the scene so that they fit within a narrow display range: a scene of 12 stops may end up occupying 6 or 8 in the file, and an aggressive tone mapping of the "flat" kind may show a high dr_stops for having stretched the histogram, without the original scene having been particularly contrasty. The number is honest about what it measures (the stops that survive in the pixels received); it knows nothing of the real scene if tone mapping intervened.

10.3Tabla consolidada

10.3Consolidated table

columna o mecanismosecciónmodo de fallo
r_norm, confidence2 sesgo propio del estimador de saliencia; válido para comparar grupos, no como valor absoluto. confidence alta no implica que exista un sujeto real.
grain_levels, acutance5, 10.1 sobrestimación de hasta 1,5× con textura fina repartida por todo el cuadro; techo de resolución de 10.1 antes de la medida.
cct_k, duv4 anulados fuera de [1000,25000] K o con |Duv|>0,05; la fórmula de McCamy es inestable cerca de su propia singularidad en y=0,1858.
estimate_noise()5, 6 puede caer exactamente a cero sobre fuentes sintéticas suavizadas sin grano real; en película con grano físico no ocurre, porque el grano no desaparece al desenfocar.
dr_stops y toda la sección 410.2 truncados a 8 bits en la carga del archivo; no aptos para HDR sin aplanar sin modificar la carga de imagen.
sesiones y hallazgos9 una sesión sin fecha se define por bloques de archivo de 36 fotogramas; solo es una cronología válida si el nombrado del archivo es consecutivo en el tiempo.
estratificación por focal / diafragma9 con menos de 12 sesiones dentro de la banda modal, se informa "sin datos"; no es una prueba de ausencia de confusión, solo de que no hay muestra suficiente para comprobarlo.
color de escaneos de película4 describe la emulsión, el revelado y el perfil del escáner tanto como la escena; un equilibrio de color automático fotograma a fotograma en el escáner destruye la dominante media real.
paleta categórica del panel— 12 colores fijos; con más de 12 grupos activos a la vez los colores se repiten, y el panel lo advierte en pantalla en vez de fallar en silencio.
column or mechanismsectionfailure mode
r_norm, confidence2 the bias inherent in the saliency estimator; valid for comparing groups, not as an absolute value. A high confidence does not imply that a real subject exists.
grain_levels, acutance5, 10.1 overestimation by as much as 1.5× with fine texture spread over the whole frame; the resolution ceiling of 10.1 applies before the measurement.
cct_k, duv4 voided outside [1000,25000] K or with |Duv|>0.05; McCamy's formula is unstable near its own singularity at y=0.1858.
estimate_noise()5, 6 it can fall to exactly zero on smoothed synthetic sources without real grain; on film with physical grain this does not occur, because grain does not disappear on defocusing.
dr_stops and the whole of section 410.2 truncated to 8 bits when the file is loaded; not suitable for unflattened HDR without modifying the image loading.
sessions and findings9 a session without a date is defined by file blocks of 36 frames; this is a valid chronology only if the file naming is consecutive in time.
stratification by focal length / aperture9 with fewer than 12 sessions within the modal band, "no data" is reported; this is not proof of the absence of confounding, only that there is no sufficient sample to check for it.
colour of film scans4 it describes the emulsion, the processing and the scanner profile as much as the scene; an automatic colour balance frame by frame in the scanner destroys the real mean colour cast.
the panel's categorical palette— 12 fixed colours; with more than 12 groups active at once the colours repeat, and the panel warns of it on screen instead of failing silently.

Ningún límite de esta tabla es un defecto oculto: todos están declarados en el propio código fuente, en el docstring del módulo o en los comentarios de la función correspondiente, y este manual no hace más que reunirlos con su justificación numérica.

No limit in this table is a hidden defect: all of them are declared in the source code itself, in the module docstring or in the comments of the corresponding function, and this manual does no more than gather them together with their numerical justification.

11

Referencia de columnas del CSV

CSV column reference

71 columnas · dataclass Row, subject_center.py 71 columns · dataclass Row, subject_center.py

Orden de aparición en el archivo de salida, agrupado por bloque temático en vez de alfabético, con enlace a la sección de este manual donde se define cada una.

In order of appearance in the output file, grouped by thematic block rather than alphabetically, with a link to the section of this manual where each is defined.

columnasecciónunidad / rangodescripción breve
path—textoruta del archivo analizado
group—textoclave de agrupación, según --group-by
width, height§10.1pxdimensiones tras deshacer la orientación EXIF; seleccionables en Explorar junto con mpx (derivada) para comprobar confusión por resolución de origen
orientation—textohorizontal / vertical / cuadrado
aspect—W/Hrelación de aspecto del fotograma completo
make, model, lens—textoEXIF 0x010F, 0x0110, 0xA434
focal_mm, focal35_mm—mmEXIF 0x920A, 0xA405; completable con --infer-focal
f_number—f/EXIF 0x829D
datetime—AAAA:MM:DD hh:mm:ssEXIF 0x9003 o 0x0132; completable con --infer-date
method—textosharpness / contrast / both, según --method
x_norm, y_norm§2[-1,1]posición del centroide del sujeto
r_norm§2[0,1]distancia del sujeto al centro, en semidiagonales
spread_norm§2≥0radio de giro de la componente conexa
area_frac§2[0,1]fracción del fotograma que ocupa el sujeto
confidence§2[0,1]compacidad geométrica de la componente
peak_x_norm, peak_y_norm§2[-1,1]posición de la celda de máxima saliencia
key_stops§3.1diafragmosclave de exposición sobre el 18 %
lum_mean§3[0,1]luminancia lineal media
lstar_median§3[0,100]L* mediano del fotograma
contrast_sd§3.4L*desviación típica de L*
dr_stops§3.2diafragmoslatitud entre percentiles 0,5 y 99,5
clip_lo, clip_hi§3.4[0,1]fracción de píxeles cortados a negro o a blanco
tone_entropy§3.4bits, [0,6]entropía de Shannon del histograma de L*
z00 … z10§3.3[0,1], suman 1perfil de once zonas iguales en L*
lab_a, lab_b§4.1CIELABdominante media verde‑magenta y azul‑amarillo
chroma_mean, chroma_p95§4.1C*saturación perceptual media y percentil 95
colorfulness§4.2≥0dispersión de Hasler‑Süsstrunk
cct_k§4.3K, [1000,25000] o vacíotemperatura de color correlacionada
duv§4.3±0,05 o vacíodistancia firmada al locus de Planck
hue_mean_deg§4.4[0,360)ángulo de tono medio, ponderado por croma
hue_conc§4.4[0,1]concentración circular del tono
hue_sd_deg§4.4°dispersión circular de Mardia
sh_a, sh_b, hi_a, hi_b§4.5CIELABdominante media de sombras y de luces
split_dab§4.5≥0magnitud del tono partido
split_dir_deg§4.5[0,360)dirección del tono partido
acutance§5.1niveles de 8 bitsmediana del laplaciano absoluto
grain_levels§5.2, 10.1niveles de 8 bitsσ de ruido en teselas planas
focus_frac§5.3[0,1]fracción de teselas por encima del umbral de nitidez
edge_vert§5.3[-1,1]anisotropía de bordes verticales frente a horizontales
alpha_spectral§5.4adimensionalexponente del ajuste P(f)∝f⁻α
blur_all, blur_subject, blur_bg§6.1(0,1] o vacíoíndice de desenfoque por cociente de escalas
blur_sep§6.1real, puede ser negativoblur_bg − blur_subject
bg_micro, subj_micro§6.3niveles de 8 bitsmicro‑contraste RMS de fondo y sujeto
bg_rel_micro§6.3[0,1]parte del micro‑contraste total que aporta el fondo
creaminess§6.3[0,1]blur_bg × (1−bg_rel_micro)
error—textoexcepción capturada; vacío si el fotograma se procesó bien
columnsectionunit / rangebrief description
path—textpath of the file analysed
group—textgrouping key, according to --group-by
width, height§10.1pxdimensions after undoing the EXIF orientation; selectable in Explore together with mpx (derived) in order to check for confounding by source resolution
orientation—textlandscape / portrait / square
aspect—W/Haspect ratio of the complete frame
make, model, lens—textEXIF 0x010F, 0x0110, 0xA434
focal_mm, focal35_mm—mmEXIF 0x920A, 0xA405; can be completed with --infer-focal
f_number—f/EXIF 0x829D
datetime—YYYY:MM:DD hh:mm:ssEXIF 0x9003 or 0x0132; can be completed with --infer-date
method—textsharpness / contrast / both, according to --method
x_norm, y_norm§2[-1,1]position of the subject's centroid
r_norm§2[0,1]distance of the subject from the centre, in half-diagonals
spread_norm§2≥0radius of gyration of the connected component
area_frac§2[0,1]fraction of the frame occupied by the subject
confidence§2[0,1]geometric compactness of the component
peak_x_norm, peak_y_norm§2[-1,1]position of the cell of maximum saliency
key_stops§3.1stopsexposure key relative to the 18 % grey
lum_mean§3[0,1]mean linear luminance
lstar_median§3[0,100]median L* of the frame
contrast_sd§3.4L*standard deviation of L*
dr_stops§3.2stopslatitude between the 0.5 and 99.5 percentiles
clip_lo, clip_hi§3.4[0,1]fraction of pixels clipped to black or to white
tone_entropy§3.4bits, [0,6]Shannon entropy of the L* histogram
z00 … z10§3.3[0,1], summing to 1profile of eleven zones equal in L*
lab_a, lab_b§4.1CIELABmean green‑magenta and blue‑yellow colour cast
chroma_mean, chroma_p95§4.1C*mean perceptual saturation and 95th percentile
colorfulness§4.2≥0Hasler‑Süsstrunk dispersion
cct_k§4.3K, [1000,25000] or emptycorrelated colour temperature
duv§4.3±0.05 or emptysigned distance from the Planckian locus
hue_mean_deg§4.4[0,360)mean hue angle, weighted by chroma
hue_conc§4.4[0,1]circular concentration of hue
hue_sd_deg§4.4°Mardia's circular dispersion
sh_a, sh_b, hi_a, hi_b§4.5CIELABmean colour cast of the shadows and of the highlights
split_dab§4.5≥0magnitude of the split toning
split_dir_deg§4.5[0,360)direction of the split toning
acutance§5.18-bit levelsmedian of the absolute Laplacian
grain_levels§5.2, 10.18-bit levelsnoise σ over flat tiles
focus_frac§5.3[0,1]fraction of tiles above the sharpness threshold
edge_vert§5.3[-1,1]anisotropy of vertical against horizontal edges
alpha_spectral§5.4dimensionlessexponent of the fit P(f)∝f⁻α
blur_all, blur_subject, blur_bg§6.1(0,1] or emptydefocus index by ratio of scales
blur_sep§6.1real, may be negativeblur_bg − blur_subject
bg_micro, subj_micro§6.38-bit levelsRMS micro‑contrast of background and subject
bg_rel_micro§6.3[0,1]share of the total micro‑contrast contributed by the background
creaminess§6.3[0,1]blur_bg × (1−bg_rel_micro)
error—textexception caught; empty if the frame was processed correctly
12

Bibliografía

Bibliography

métodos citados por nombre en el código o en este manual methods cited by name in the code or in this manual
Adams, A. — The Negative / The Print. Sistema de zonas, referencia conceptual de §3.3. Little, Brown and Company.
Benjamini & Hochberg (1995) — Controlling the False Discovery Rate. Journal of the Royal Statistical Society B, 57(1), 289–300. §9.5.
Cheng et al. (2015) — Global contrast based salient region detection. IEEE TPAMI, 37(3). §2.2.
CIE (2004) — Publicación CIE 15:2004, Colorimetry, 3.ª edición. Definiciones de CIELAB, CIE 1931 y CIE 1960 UCS usadas en §4.
Cliff, N. (1993) — Dominance statistics: ordinal analyses to answer ordinal questions. Psychological Bulletin, 114(3), 494–509. Delta de Cliff, §8.3.
Crete et al. (2007) — The blur effect: perception and estimation with a new no-reference perceptual blur metric. SPIE Human Vision and Electronic Imaging. Método descartado, discutido en §6.1.
Field, D. J. (1987) — Relations between the statistics of natural images and the response properties of cortical cells. Journal of the Optical Society of America A, 4(12), 2379–2394. §5.4.
Hasler & Süsstrunk (2003) — Measuring colorfulness in natural images. Proc. SPIE Human Vision and Electronic Imaging VIII, 5007. §4.2.
IEC 61966‑2‑1 (1999) — Multimedia systems and equipment: colour measurement and management. Part 2‑1: colour management. Default RGB colour space, sRGB. §3, §4.
Jacobi, C. G. J. (1846) — Über ein leichtes Verfahren die in der Theorie der Säcularstörungen vorkommenden Gleichungen numerisch aufzulösen. Journal für die reine und angewandte Mathematik, 30. §9.6.
Kendall, M. G. (1975) — Rank Correlation Methods, 4.ª edición. Griffin. §9.2.
Krystek, M. (1985) — An algorithm to calculate correlated colour temperature. Color Research & Application, 10(1), 38–40. Aproximación del locus de Planck usada solo para la figura 4.1, no por el código analizado.
Mann, H. B. (1945) — Nonparametric tests against trend. Econometrica, 13(3), 245–259. §9.2.
Mann-Whitney (1947) — Mann, H. B. & Whitney, D. R. On a test of whether one of two random variables is stochastically larger than the other. Annals of Mathematical Statistics, 18(1), 50–60. §8.3.
Mardia, K. V. (1972) — Statistics of Directional Data. Academic Press. Estadística circular, §4.4 y §2.5.
McCamy, C. S. (1992) — Correlated color temperature as an explicit function of chromaticity coordinates. Color Research & Application, 17(2), 142–144. §4.3.
Ohno, Y. (2013) — Practical use and calculation of CCT and Duv. Leukos, 10(1), 47–55. §4.3.
Pettitt, A. N. (1979) — A non-parametric approach to the change-point problem. Journal of the Royal Statistical Society C, 28(2), 126–135. §9.4.
Sen, P. K. (1968) — Estimates of the regression coefficient based on Kendall's tau. Journal of the American Statistical Association, 63(324), 1379–1389. §9.3.
Spearman, C. (1904) — The proof and measurement of association between two things. American Journal of Psychology, 15(1), 72–101. §9.8.
Theil, H. (1950) — A rank-invariant method of linear and polynomial regression analysis. Nederlandse Akademie Wetenschappen, 53, 386–392. §9.3.
Adams, A. — The Negative / The Print. The zone system, the conceptual reference for §3.3. Little, Brown and Company.
Benjamini & Hochberg (1995) — Controlling the False Discovery Rate. Journal of the Royal Statistical Society B, 57(1), 289–300. §9.5.
Cheng et al. (2015) — Global contrast based salient region detection. IEEE TPAMI, 37(3). §2.2.
CIE (2004) — CIE Publication 15:2004, Colorimetry, 3rd edition. The definitions of CIELAB, CIE 1931 and CIE 1960 UCS used in §4.
Cliff, N. (1993) — Dominance statistics: ordinal analyses to answer ordinal questions. Psychological Bulletin, 114(3), 494–509. Cliff's delta, §8.3.
Crete et al. (2007) — The blur effect: perception and estimation with a new no-reference perceptual blur metric. SPIE Human Vision and Electronic Imaging. The method rejected, discussed in §6.1.
Field, D. J. (1987) — Relations between the statistics of natural images and the response properties of cortical cells. Journal of the Optical Society of America A, 4(12), 2379–2394. §5.4.
Hasler & Süsstrunk (2003) — Measuring colorfulness in natural images. Proc. SPIE Human Vision and Electronic Imaging VIII, 5007. §4.2.
IEC 61966‑2‑1 (1999) — Multimedia systems and equipment: colour measurement and management. Part 2‑1: colour management. Default RGB colour space, sRGB. §3, §4.
Jacobi, C. G. J. (1846) — Über ein leichtes Verfahren die in der Theorie der Säcularstörungen vorkommenden Gleichungen numerisch aufzulösen. Journal für die reine und angewandte Mathematik, 30. §9.6.
Kendall, M. G. (1975) — Rank Correlation Methods, 4th edition. Griffin. §9.2.
Krystek, M. (1985) — An algorithm to calculate correlated colour temperature. Color Research & Application, 10(1), 38–40. The approximation of the Planckian locus used only for figure 4.1, not by the code under description.
Mann, H. B. (1945) — Nonparametric tests against trend. Econometrica, 13(3), 245–259. §9.2.
Mann-Whitney (1947) — Mann, H. B. & Whitney, D. R. On a test of whether one of two random variables is stochastically larger than the other. Annals of Mathematical Statistics, 18(1), 50–60. §8.3.
Mardia, K. V. (1972) — Statistics of Directional Data. Academic Press. Circular statistics, §4.4 and §2.5.
McCamy, C. S. (1992) — Correlated color temperature as an explicit function of chromaticity coordinates. Color Research & Application, 17(2), 142–144. §4.3.
Ohno, Y. (2013) — Practical use and calculation of CCT and Duv. Leukos, 10(1), 47–55. §4.3.
Pettitt, A. N. (1979) — A non-parametric approach to the change-point problem. Journal of the Royal Statistical Society C, 28(2), 126–135. §9.4.
Sen, P. K. (1968) — Estimates of the regression coefficient based on Kendall's tau. Journal of the American Statistical Association, 63(324), 1379–1389. §9.3.
Spearman, C. (1904) — The proof and measurement of association between two things. American Journal of Psychology, 15(1), 72–101. §9.8.
Theil, H. (1950) — A rank-invariant method of linear and polynomial regression analysis. Nederlandse Akademie Wetenschappen, 53, 386–392. §9.3.
↑ portada ↑ cover