Esta sección resume las magnitudes que MatLens calcula sobre imágenes de microscopía (TEM, SEM, HRTEM, SAED) y cómo se obtienen, tal como están implementadas en el código.
Calibración espacial
La escala de la imagen se expresa como s (nm/px). A partir de una barra de escala:
| Símbolo | Significado | Unidades |
|---|---|---|
| s | tamaño de píxel | nm/px |
| v | valor rotulado de la barra | unidad de la barra |
| f_u | factor de conversión de la unidad a nm | nm/unidad |
| L_\mathrm{bar} | longitud medida de la barra | px |
Sin calibración, todas las longitudes se informan en px (s = 1). Código: matlens/core/scalebar.py::scale_from_bar.
Referencia: implementación propia.
Diámetro equivalente (ECD)
| Símbolo | Significado | Unidades |
|---|---|---|
| A_\mathrm{px} | número de píxeles de la partícula | px² |
| s | tamaño de píxel | nm/px |
| d_\mathrm{ECD} | diámetro del círculo de igual área | nm (o px) |
El área calibrada es A = A_\mathrm{px}s^2. Código: matlens/core/imaging.py::measure, matlens/core/microscopy.py::measure_particles, matlens/core/segmentation_metrics.py::equivalent_diameters.
Validez: la discretización domina para partículas de pocos píxeles; por eso el análisis clásico descarta objetos con d_\mathrm{ECD}/s < 8 px (min_size_px = 8) y la detección multiescala exige \geq 5 px.
Referencia: implementación propia.
Diámetros de Feret
Los píxeles se tratan como cuadrados unitarios: a cada vértice de la envolvente convexa de los centros de píxel se le añaden las cuatro esquinas (\pm 0.5, \pm 0.5) y se recalcula la envolvente. Se proyecta sobre direcciones \theta = 0°, 1°, \dots, 179°:
| Símbolo | Significado | Unidades |
|---|---|---|
| \mathbf{c}_i | vértices de la envolvente convexa (con esquinas de píxel) | px |
| \hat{\mathbf{u}}_\theta | vector unitario (\cos\theta, \sin\theta) | — |
| F_\mathrm{max}, F_\mathrm{min} | diámetros de calibre máximo y mínimo | nm (o px) |
Para menos de 3 píxeles se devuelve F_\mathrm{max} = F_\mathrm{min} = 1 px. Código: matlens/core/microscopy.py::_feret.
Nota (diferencia con la literatura): el Feret exacto se obtiene con calibres rotatorios sobre las aristas de la envolvente; MatLens muestrea el ángulo cada 1°, por lo que F_\mathrm{min} puede sobrestimarse ligeramente (error relativo máximo \approx 1-\cos(0.5°) \approx 4\times10^{-5} para F_\mathrm{max}, algo mayor para F_\mathrm{min} en objetos muy alargados).
Referencia: implementación propia.
Elipse equivalente, relación de aspecto y elongación
Los ejes mayor a y menor b son los ejes completos de la elipse con los mismos momentos de segundo orden que la región (skimage.measure.regionprops):
La "relación de aspecto" de la tabla de medidas y la "elongación" de la clasificación de formas son la misma magnitud a/b (si b = 0: NaN en la tabla, \infty en la clasificación). La orientación se informa en grados. Código: matlens/core/imaging.py::measure, matlens/core/particle_tools.py::shape_table.
Referencia: implementación propia.
Circularidad
| Símbolo | Significado | Unidades |
|---|---|---|
| A_\mathrm{px} | área | px² |
| P_\mathrm{px} | perímetro estimado por regionprops.perimeter | px |
| C | circularidad (1 = círculo) | — |
Si P = 0, C = NaN. Código: matlens/core/imaging.py::measure, matlens/core/microscopy.py::measure_particles.
Nota (diferencia con la literatura): el perímetro discreto subestima el perímetro real en objetos pequeños, lo que puede dar 4\pi A/P^2 > 1; MatLens recorta el valor a 1.
Referencia: implementación propia.
Solidez
donde A_\mathrm{conv} es el área (en píxeles) de la envolvente convexa (regionprops.solidity). Se usa como filtro opcional (min_solidity, 0 = desactivado), para clasificar formas y, en la detección multiescala, para decidir si un objeto grande es una partícula real (S \geq 0.85, real_solidity). Código: matlens/core/imaging.py::measure.
Referencia: implementación propia.
Rectangularidad y clasificación de formas
El rectángulo de área mínima se busca con calibres rotatorios: para cada arista e de la envolvente convexa de los centros de píxel, \Delta_u y \Delta_v son las extensiones (pico a pico) de la envolvente a lo largo de la arista y de su normal; el +1 cuenta los píxeles como cuadrados unitarios. Píxeles colineales: rectángulo de (\text{extensión}+1)\times 1. El lado característico es \sqrt{A}.
Clasificación (umbrales por defecto, editables en ShapeRules), evaluada en este orden:
| Clase | Condición |
|---|---|
| cubo | S \geq 0.90, R \geq 0.80, a/b < 1.6 |
| varilla | a/b \geq 2.2, S \geq 0.80 |
| agregado | S < 0.80 |
| compacta | el resto |
Código: matlens/core/particle_tools.py::min_area_rect, matlens/core/particle_tools.py::classify, matlens/core/particle_tools.py::shape_table.
Referencia: implementación propia.
Distribución de tamaños: ajustes normal, log-normal y Gamma
Se usan solo los diámetros finitos y positivos. Pesos por partícula w_i: 1 (número), d_i^2 (área) o d_i^3 (volumen).
Ajuste log-normal, con \ell_i = \ln d_i (logaritmo natural):
Sin pesos (ponderación por número), \sigma es la desviación estándar muestral de \ln d con n-1 en el denominador. El ajuste normal es análogo sobre d. El ajuste Gamma (forma k, escala \theta) es de máxima verosimilitud ponderada: con \bar d = \sum \tilde w_i d_i y s = \ln\bar d - \sum \tilde w_i\ln d_i (\tilde w normalizados), k resuelve \ln k - \psi(k) = s (Brent, partiendo de k_0 = \left(3-s+\sqrt{(s-3)^2+24s}\right)/(12s)) y \theta = \bar d/k.
| Símbolo | Significado | Unidades |
|---|---|---|
| d_i | diámetro (ECD u otro) de la partícula i | nm (o px) |
| \mu, \sigma | media y desviación estándar de \ln d | — |
| d_g | media geométrica = mediana log-normal | nm |
| \sigma_g | desviación estándar geométrica | — |
| k, \theta | parámetros Gamma | —, nm |
Selección del modelo: para cada ajuste se calcula \ln\mathcal{L} = \sum_i \tilde w_i n \ln f(d_i) (sin pesos: \sum \ln f), \mathrm{AIC} = 4 - 2\ln\mathcal{L} (2 parámetros en los tres modelos) y el estadístico de Kolmogorov–Smirnov D = \max|F_n - F| frente a la FDA empírica ponderada; se elige el menor AIC. El histograma se expresa en % por intervalo y las curvas se escalan por 100\,\Delta x. Percentiles d_{10}, d_{50}, d_{90}: ponderados por interpolación de la FDA acumulada (\sum w - w_i/2)/\sum w.
Incertidumbre: intervalos de confianza de la media y la mediana (ponderadas) por bootstrap percentil remuestreando partículas, nivel 95 %, 1000 réplicas en el resumen (2000 por defecto en la función), semilla fija 0 (resultados reproducibles). No se propaga incertidumbre a \mu o \sigma.
Diámetros de momentos para comparar con tamaños de cristalito de DRX: D_{3,2} = \sum d^3/\sum d^2 y D_{4,3} = \sum d^4/\sum d^3 (Scherrer es ponderado en volumen, más cercano a D_{4,3}).
Código: matlens/core/microscopy.py::fit_lognormal, matlens/core/microscopy.py::size_stats, matlens/core/microscopy.py::distribution, matlens/core/imaging.py::fit_gamma, matlens/core/imaging.py::fit_distributions, matlens/core/imaging.py::bootstrap_ci, matlens/core/imaging.py::xrd_comparison_values.
Nota (diferencia con la literatura): el docstring de fit_lognormal lo describe como MLE, pero sin pesos usa n-1 en \sigma (estimador insesgado de la varianza, no el MLE con n); con pesos usa el denominador \sum w_i (forma MLE). Se requieren al menos 3 partículas para el histograma y los ajustes.
Referencia: implementación propia.
Algoritmo exacto de los estadísticos de tamaño
size_statsdescarta valores no finitos o \le 0. Con ponderación por número: media y DE muestral (n-1), percentiles d_{10}, d_{50}, d_{90} por interpolación lineal (numpy.percentile), \hat\mu=\overline{\ln d}, \hat\sigma=s(\ln d) con n-1. Con ponderación por volumen (w=d^3): media y DE ponderadas con denominador \sum w (sin corrección de sesgo).fit_distributions(al menos 3 partículas) ajusta normal, log-normal y Gamma; el log-verosímil ponderado es \ln L=\sum_i n\,\tilde w_i\ln p(d_i) con \tilde w_i=w_i/\sum w; AIC =2\cdot 2-2\ln L (2 parámetros en los tres modelos) y KS =\max|F_n-F| evaluado sólo en los datos ordenados. El mejor modelo es el de menor AIC; no se calcula un valor p de KS (los parámetros se estiman de los mismos datos).bootstrap_ci: intervalo de percentiles remuestreando partículas (n_\text{boot}=2000 por defecto, semilla 0, nivel 95 %). Sólo cubre la variabilidad de muestreo de las partículas medidas, no el sesgo de segmentación ni el de calibración.
Ejemplo resuelto
200 diámetros log-normales sintéticos (mediana verdadera 20 nm, \sigma_{\ln} = 0.3, semilla fija). size_stats: n = 200, media = 21.71 nm, DE = 7.257 nm, d_{10}/d_{50}/d_{90} = 13.42/20.52/31.5 nm, \hat\mu = 3.026, \hat\sigma = 0.3202, media geométrica = 20.62 nm, GSD = 1.377.
fit_distributions (AIC = 2\cdot2 - 2\ln L, KS = máx. |ECDF − CDF|):
| Modelo | Mediana (nm) | AIC | KS |
|---|---|---|---|
| normal | 21.71 | 1363.35 | 0.101 |
| lognormal | 20.62 | 1325.6 | 0.0373 |
| gamma | 20.98 | 1330.93 | 0.058 |
Mejor por AIC: lognormal. IC 95 % bootstrap de percentiles de la mediana (bootstrap_ci, 2000 remuestreos, semilla 0): 20.52 nm [19.63, 21.63].
Segmentación por fondo local e histéresis
- Normalización robusta a [0,1] con los percentiles 0.5 y 99.5: g = (I - I_{0.5})/(I_{99.5}-I_{0.5}), recortado.
- Señal positiva para partículas: s = g (partículas claras) o s = 1-g (oscuras); polaridad automática según qué fase tras Otsu forma más objetos compactos (solidez \geq 0.6) sin tocar el borde.
- Suavizado gaussiano con \sigma = \text{smooth\_nm}/s_\mathrm{px}.
- Fondo por bloques: percentil p de cada bloque de lado B, interpolado bilinealmente entre centros de bloque; contraste c = s - \mathrm{bg}_p(s).
- Umbral de histéresis: un píxel con c \geq t_\mathrm{low} se conserva si está conectado a un píxel con c \geq t_\mathrm{high}.
- Relleno de huecos, apertura morfológica (disco de radio r_\mathrm{open}, solo si \geq 0.5 px), eliminación de objetos con área < \max(\tfrac{\pi}{4}d_\mathrm{min}^2, 2) px², separación por watershed de h-máximos (ver abajo), y filtros de contraste medio, tamaño, solidez y borde.
Valores por defecto (LOCAL_BG_DEFAULTS; longitudes en nm, o px sin calibración):
| Parámetro | Valor | Significado |
|---|---|---|
| smooth_nm | 0.4714 | \sigma del suavizado previo |
| block_nm | 5.0 | lado B del bloque de fondo |
| bg_percentile | 16.6179 | percentil p tomado como fondo |
| low, high | 0.042, 0.0874 | t_\mathrm{low}, t_\mathrm{high} (fracción del rango 0.5–99.5 %) |
| open_nm | 0.9495 | radio de apertura |
| h_nm | 1.6813 | profundidad h de los h-máximos |
| ws_smooth_nm | 0.3776 | suavizado de la transformada de distancia |
| min_contrast | 0.0376 | contraste medio mínimo de un objeto |
| min_ecd_nm | 1.5389 | ECD mínimo |
| max_ecd_nm | 0 | sin límite superior |
| min_solidity | 0 | filtro de solidez desactivado |
Los valores se ajustaron por búsqueda aleatoria en la partición de entrenamiento/validación del conjunto HRTEM CC0 de Sytwu, Rangel DaCosta y Scott, maximizando la media de IoU de píxel y F1 de objetos (IoU \geq 0.5). Código: matlens/core/imaging.py::segment_local_background, matlens/core/imaging.py::local_contrast, matlens/core/imaging.py::block_percentile_background, matlens/core/imaging.py::mask_to_particles.
Validez: los parámetros por defecto corresponden a HRTEM de nanopartículas (Au, Ag, CdSe) a 0.08–0.17 nm/px; fuera de ese rango deben reajustarse.
Referencias: Canny, IEEE PAMI 8, 679 (1986) (histéresis); Soille, Morphological Image Analysis (2003) (h-máximos); Beucher & Meyer (1993) (watershed); datos: Dryad doi:10.7941/D1SP93 / MDF doi:10.18126/z4mr-xwk5. La composición es propia de MatLens.
Watershed de h-máximos
Se calcula la transformada de distancia euclídea D(\mathbf{x}) de la máscara, se suaviza con una gaussiana de \sigma (smooth, px) y se buscan los h-máximos regionales objeto por objeto:
donde R^{\delta}_D es la reconstrucción morfológica por dilatación bajo D. Se inunda -D desde las semillas dentro de la máscara (compacidad 0 por defecto). Dos partículas se separan solo si el cuello es al menos h px menos profundo que ambos centros. Componentes sin semilla conservan su propia etiqueta. Por defecto h = 2 px y \sigma = 1 px; en el método de fondo local, h = \text{h\_nm}/s_\mathrm{px}. Modo alternativo intensity: se inunda la intensidad suavizada (normalizada) con h = 0.05 del rango.
Código: matlens/core/imaging.py::split_watershed, matlens/core/imaging.py::_h_maxima_per_object.
Nota (diferencia con la literatura): el análisis clásico de microscopy.py (split_touching) usa en cambio semillas por máximos locales con una distancia mínima entre semillas (min_distance = 8 px), no h-máximos.
Referencias: Soille, Morphological Image Analysis (2003); Beucher & Meyer (1993).
Umbralización clásica
En el análisis clásico (matlens/core/microscopy.py::analyze_particles) la imagen preprocesada (inversión si las partículas son oscuras, suavizado gaussiano \sigma = 2 px por defecto, fondo opcional por rolling ball o top-hat, normalización a [0,1], CLAHE opcional) se umbraliza con Otsu (por defecto), Li, triángulo, manual o adaptativo local (bloque 51 px). Filtros: d_\mathrm{ECD} \geq 8 px, límites opcionales de tamaño y exclusión de partículas que tocan el borde. Código: matlens/core/microscopy.py::preprocess, matlens/core/microscopy.py::threshold_value.
Referencia: implementación propia (métodos de umbral de scikit-image).
Detección multiescala por bandas y límite de 5 px
Rango detectable para un tamaño de píxel s y una imagen H\times W:
Bandas por defecto: 2–8, 8–30 y 30–100 nm, procesadas de mayor a menor. Tamaño de píxel objetivo para una banda [d_\mathrm{lo}, d_\mathrm{hi}]:
de modo que una partícula de la banda ocupe unos 8–40 px (exactamente si d_\mathrm{hi}/d_\mathrm{lo}\leq 5). La imagen se remuestrea por promedio de área con factor \min(1, s/s_\mathrm{b}) (nunca se sobremuestrea).
En cada banda: señal suavizada con \sigma = \max(0.25\,d_\mathrm{lo}, 0.5\,\text{px}), fondo por bloques de lado \max(4\ \text{px}, 2\,d_\mathrm{hi}) en el percentil 50, contraste c = s - \mathrm{bg} y desviación robusta
Histéresis con t_\mathrm{low} = 2\hat\sigma, t_\mathrm{high} = 4.5\hat\sigma; cada objeto se rebordea al umbral \max(0.3\,c_{95}, 2\hat\sigma), con c_{95} el percentil 95 de su contraste; apertura de radio 0.15\,d_\mathrm{lo}; watershed de h-máximos con h = \max(0.3\,d_\mathrm{lo}, 0.5\ \text{px}); se descartan objetos con contraste medio < 3\hat\sigma. Se aceptan objetos con d_\mathrm{lo}/1.15 \leq d_\mathrm{ECD} < 1.15\,d_\mathrm{hi} y d_\mathrm{ECD}\geq d_\mathrm{min}.
Bandas con d_\mathrm{hi} \leq d_\mathrm{min} no se analizan; bandas con d_\mathrm{lo} < d_\mathrm{min} generan un aviso y sus objetos menores de 5 px no se informan; bandas con d_\mathrm{lo}\geq d_\mathrm{max} se omiten. Fusión: los objetos de bandas mayores con solidez \geq 0.85 se aceptan y se borran (rellenados con el fondo) en las bandas menores; un objeto menor se descarta si \geq 50 % de su área cae en uno ya aceptado o si su IoU con él es \geq 0.3.
Código: matlens/core/multiscale.py::detectable_range, matlens/core/multiscale.py::band_pixel_size, matlens/core/multiscale.py::segment_band, matlens/core/multiscale.py::robust_sd, matlens/core/multiscale.py::detect_multiscale.
Referencias: Canny, IEEE PAMI 8, 679–698 (1986); Soille, Morphological Image Analysis (2003); Beucher & Meyer (1993). El esquema de bandas y fusión es propio de MatLens.
Métricas de segmentación: IoU, Dice y F1
Nivel de píxel, con P y R los primeros planos predicho y de referencia:
(ambas valen 1 si los dos conjuntos están vacíos).
Nivel de objeto: se calcula la matriz IoU entre todas las etiquetas predichas p y de referencia r; los pares con \mathrm{IoU}\geq\tau (\tau = 0.5 por defecto) se emparejan de forma voraz uno a uno en orden decreciente de IoU. Con TP pares, FP = n_p - TP, FN = n_r - TP:
Convenciones para conjuntos vacíos: precisión/exhaustividad = 1 si ambos están vacíos, 0 si solo uno; F_1 = 1 si el denominador es 0. También se informa el IoU medio de los pares emparejados. Con \tau = 0.5 el emparejamiento es necesariamente único.
Errores de tamaño: error relativo de la mediana del ECD y del número de partículas, (x_\mathrm{pred}-x_\mathrm{ref})/x_\mathrm{ref}.
Código: matlens/core/segmentation_metrics.py::pixel_iou, matlens/core/segmentation_metrics.py::dice, matlens/core/segmentation_metrics.py::iou_matrix, matlens/core/segmentation_metrics.py::match_objects, matlens/core/segmentation_metrics.py::size_errors.
Referencia: Everingham et al., IJCV 88, 303 (2010).
Ejemplo resuelto
Referencia: cuadrado 20×20 px y rectángulo 10×15 px; predicción: el cuadrado desplazado 2 px y un falso positivo 3×8 px. IoU de píxel = 0.5863, Dice = 0.7392; match_objects (IoU ≥ 0.5): TP = 1, FP = 1, FN = 1, precisión = 0.5, sensibilidad = 0.5, F1 = 0.5, IoU medio de los pares = 0.8182.
SAED: longitud de onda, constante de cámara y distancias interplanares
Longitud de onda relativista del electrón (constantes CODATA 2018):
(200 kV da 0.02508 Å). Ecuación de cámara R\,d = \lambda L. Con un detector pixelado, la escala recíproca por píxel y la constante de cámara son:
| Símbolo | Significado | Unidades |
|---|---|---|
| V | tensión de aceleración (200 kV si no se da) | V |
| \lambda | longitud de onda | Å |
| L | longitud de cámara | mm |
| p | tamaño de píxel del detector | µm |
| g_\mathrm{px} | \lvert g\rvert por píxel | nm⁻¹/px |
| K | constante de cámara | Å·px |
| r | radio del anillo o distancia del spot al centro | px |
| d | distancia interplanar | Å |
Calibración con un patrón (Au, Al, Si, TlCl): cada combinación de uno de los 4 anillos más internos con una de las 8 primeras líneas del patrón propone K = r\,d; se asignan los anillos con desviación \leq 1.5 %; se elige la K que indexa más anillos (desempate: menor rms) y se refina por mínimos cuadrados de los errores relativos:
Se requieren al menos dos anillos, y al menos dos asignados.
Código: matlens/core/saed.py::electron_wavelength_A, matlens/core/saed.py::calibration_from_camera, matlens/core/saed.py::DiffCalibration, matlens/core/saed.py::calibrate_with_standard.
Validez: las intensidades electrónicas son solo cualitativas (difracción dinámica, doble difracción), por lo que la indexación puntúa únicamente posiciones.
Referencia: Williams & Carter, Transmission Electron Microscopy, 2.ª ed., Springer (2009), ec. 1.6 y cap. 18 (§18.6).
Constantes y límites (SAED)
Constantes de matlens/core/saed.py (CODATA 2018, valores exactos o recomendados): h = 6.62607015\times10^{-34} J·s, m_0 = 9.1093837015\times10^{-31} kg, e = 1.602176634\times10^{-19} C, c = 299\,792\,458 m/s. La longitud de onda relativista es \lambda = h/\sqrt{2m_0eU(1+eU/2m_0c^2)}; si no se da tensión se usa 200 kV. La calibración por cámara usa la aproximación de ángulo pequeño r=\lambda L/d (error relativo del orden de \theta^2, despreciable a 2θ de pocos grados) y no corrige la distorsión elíptica de la lente proyectora ni la incertidumbre de L nominal, que suele ser de varios por ciento; por eso la calibración con un patrón (calibrate_with_standard) es preferible. Error si L\le0 o el tamaño de píxel \le0.
Ejemplo resuelto
Longitud de onda relativista (electron_wavelength_A):
| U (kV) | λ (Å) |
|---|---|
| 80 | 0.041757 |
| 120 | 0.033492 |
| 200 | 0.025079 |
| 300 | 0.019687 |
Calibración por cámara (calibration_from_camera, L = 300 mm, píxel 14 µm, 200 kV): |g|/px = 0.018608 nm⁻¹, K = d\,r = 537.41 Å·px, \lambda L = 7.5238 Å·mm. Un anillo con d = 2.338 Å (Au 111) cae entonces a r = 229.86 px.
Distancias interplanares en HRTEM (FFT y franjas)
Frecuencia espacial de un punto (x,y) de la FFT (rellenada con ceros, factor 2, ventana de Hann) de una región N_y\times N_x:
con f en nm⁻¹ y s en nm/px. Espaciado de franjas en un perfil: por FFT (relleno a \geq 16n, interpolación parabólica del pico) y por posiciones de picos (subpíxel parabólico, prominencia \geq 25 % del rango, al menos 3 picos), con d igual a la pendiente de posición frente a índice de franja; la incertidumbre informada es la desviación estándar (n-1) de las separaciones entre picos consecutivos. Las d se comparan con líneas de referencia dentro de \pm 3 %.
Código: matlens/core/microscopy.py::spot_frequency, matlens/core/microscopy.py::spot_d_angstrom, matlens/core/microscopy.py::fringe_spacing, matlens/core/microscopy.py::match_d.
Referencia: implementación propia.
Ejemplo resuelto (franjas)
El método FFT usa una ventana de Hann, relleno con ceros hasta \ge 16n (potencia de 2) e interpolación parabólica del máximo; el de picos exige al menos 3 máximos con prominencia \ge 25\,\% del rango y separación mínima de media franja, refina cada posición con una parábola de tres puntos y toma la pendiente de posición frente a índice. Antes se resta una recta (inclinación del perfil). La DE de las separaciones es una medida de dispersión de la red, no una incertidumbre de calibración.
Perfil sintético de 400 px a 0.02 nm/px con franjas de 3.135 Å (Si 111), ruido gaussiano σ = 0.2 y pendiente lineal. fringe_spacing: d_\mathrm{FFT} = 3.1326 Å; 25 máximos, d_\mathrm{picos} = 3.1321 Å, DE de las separaciones = 0.276 Å.