Imágenes y píxeles#

Esquema del capítulo

  • Las imágenes digitales se componen de píxeles

  • Cada píxel tiene un valor numérico (a menudo relacionado con la luz detectada)

  • Los mismos valores de píxeles se pueden mostrar de forma diferente usando tablas de búsqueda (LUT)

  • Podemos modificar la apariencia de la imagen de dos formas principales:

    1. Cambiar los valores de píxeles

    2. Cambiar la LUT

  • Preservar los valores de los píxeles es crucial para la mayoría de los análisis, por lo que es esencial saber qué está haciendo su software.

Hide code cell content
"""
Nothing very interesting to see here:
these are just default imports that help the rest of the code on this page run.
"""

%load_ext autoreload
%autoreload 2

# Default imports
import sys
sys.path.append('../../../')
from helpers import *
from matplotlib import pyplot as plt
from myst_nb import glue
import numpy as np
from scipy import ndimage

Introducción#

La imagen está compuesta de píxeles.

La palabra “píxel” se deriva de picture element (elemento de imagen por su traducción en inglés) y, en lo que respecta a la computadora, cada píxel es solo un número.

Cuando se muestran los datos de la imagen, los valores de los píxeles generalmente se convierten en cuadrados de colores particulares, pero esto es sólo para nuestro beneficio. Los cuadrados de colores no son más que una visualización útil que nos permite obtener una impresión rápida del contenido de la imagen, es decir, los valores aproximados de los píxeles y su ubicación relativa.

Cuando se trata de procesamiento y análisis, debemos ir más allá de la visualización y profundizar en los datos reales: los números (Figura 3).

Hide code cell content
"""
Welcome! Here you can see the first example of Python code used to generate
figures in this book.

If you are working with a 'live' version of the book, you should be able to
make some adjustments to regenerate new figures.

Each code block contains two main parts:
1. A part responsible for loading images, applying processing etc.
2. A part that plots the result in a figure (usually with matplotlib)

The first of these is the most important, because it complements the main text.
My hope is that, by seeing the code involved, concepts might become clearer.

The plotting itself can be quite fiddly to make everything align, and can often
be ignored.

As a rule, I use the following conventions:
- A variable starting with 'im_' refers to a 'normal' image
- A variable starting with 'bw_' refers to a binary image
- A variable starting with 'lab_' refers to a labeled image

When it comes to plotting, 'fig' invariably refers to the figure being plotted
and 'plt' refers to 'pyplot' (standard in matplotlib).
'show_image', 'show_plot' and 'show_histogram' are helper methods to standardize
display throughout the book.
'glue' and 'glue_fig' other what enable the plots to be incorporating into the book.
"""


# Read an image & select a detailed region
im = load_image('images/couple.png')
x, y, width, height = (205, 130, 12, 12)
im_detail = im[y:y+height, x:x+width]


# Show plots
fig = create_figure(figsize=(8, 4))
import matplotlib.patches as patches
rect = patches.Rectangle((x, y), width, height, linewidth=1, edgecolor='r', facecolor='none')

show_image(im, vmin=0, vmax=255, title='(A) Original image', pos=131)
plt.gca().add_patch(rect)

show_image(im_detail, vmin=0, vmax=255, title='(B) Enlarged view from (A)', pos=132)

show_image(im_detail, vmin=0, vmax=255, alpha = 1, title='(C) Pixel values from (B)', pos=133)
for (i, j), z in np.ndenumerate(im_detail):
    plt.text(j, i, str(z), ha='center', va='center', fontdict={'size': 5})

glue_fig('fig_couple', fig)
../../../_images/6e2cbc480d62f4219ca70f721ddedd4baa880e47b9d201c19897ce71580ff46f.png

Figura 3 Una imagen que muestra una pareja interesante que vi mientras caminaba a casa desde el trabajo. (A) y (B) La imagen se muestra utilizando pequeños cuadrados de diferentes tonos de gris, donde cada cuadrado corresponde a un solo píxel. Esta es sólo una convención utilizada para visualización; los píxeles mismos se almacenan como matrices de números (C), pero al mirar los números directamente nos resulta bastante difícil visualizar los contenidos de la imagen.#

Datos de imagen y su visualización.#

La distinción entre el valor numérico de un píxel y el color utilizado para mostrarlo puede parecer un detalle menor, pero definitivamente no lo es: no reconocer esta diferencia es la fuente de muchos errores.

Si no tenemos cuidado, dos hechos relacionados pueden causarnos una enorme cantidad de problemas:

Advertencia

  1. Las imágenes que parecen iguales pueden contener valores de píxeles diferentes

  2. Las imágenes que se ven diferentes pueden contener los mismos valores de píxeles

Hide code cell content
"""
Create four images that look exactly the same, but all contain different pixel values.
"""

im = load_image('sunny_cell.tif')
assert im.dtype == np.uint16
fig = create_figure(figsize=(10, 4))

# Original image, with 1% clipped for display
vmin = np.percentile(im, 1)
vmax = np.percentile(im, 99)
show_image(im, cmap='gray', title='Original 16-bit image', vmin=vmin, vmax=vmax, pos=141)

# 32-bit, rescaled
im2 = (im.astype(np.float32) - im.mean()) / im.std() * 50
vmin2 = np.percentile(im2, 1)
vmax2 = np.percentile(im2, 99)
show_image(im2, cmap='gray', title='32-bit processed', vmin=vmin2, vmax=vmax2, pos=142)

# 8-bit, clipped
im3 = im.astype(np.float32)
im3 = (im3 - vmin) / (vmax - vmin) * 255
im3 = np.clip(im3, 0, 255)
im3 = im3.astype(np.uint8)
show_image(im3, cmap='gray', title='8-bit clipped', vmin=0, vmax=255, pos=143)

# 8-bit, clipped, then inverted with inverted LUT
im4 = 255 - im3
show_image(im4, cmap='gray_r', title='Inverted with inverted LUT', vmin=0, vmax=255, pos=144)

glue_fig('fig_images_look_same', fig)
../../../_images/677b450c20be232168dd531e89aa39097138c8e664a6d9485f49d1e0561d652e.png

Figura 4 Imágenes que parecen iguales, pero contienen valores de píxeles diferentes.\ Medir cada una de estas imágenes daría resultados diferentes, por razones que veremos en capítulos posteriores.#

Hide code cell content
"""
Display the same image in 4 different ways by changing the LUT/colormap.
"""

im = load_image('sunny_cell.tif')
assert im.dtype == np.uint16
fig = create_figure(figsize=(10, 4))

# Original image, with 1% clipped for display
vmin = np.percentile(im, 1)
vmax = np.percentile(im, 99)
show_image(im, cmap='gray', title='Original 16-bit image', vmin=vmin, vmax=vmax, pos=141)

# Original image, with 10% clipped for display
vmin2 = np.percentile(im, 10)
vmax2 = np.percentile(im, 90)
show_image(im, cmap='gray', title='Enhanced contrast LUT', vmin=vmin2, vmax=vmax2, pos=142)

# Invert the LUT (in matplotlib, just add '_r' at the end)
show_image(im, cmap='gray_r', title='Inverted LUT', vmin=vmin, vmax=vmax, pos=143)

# Use an alternative LUT
show_image(im, cmap='magma', title='Magma LUT', vmin=vmin, vmax=vmax, pos=144)

glue_fig('fig_images_look_different', fig)
../../../_images/8d3443c745d4d40113043c25f39d2e89626bbabdfea2972a11a3856b5cba67cd.png

Figura 5 Imágenes que parecen diferentes, pero contienen los mismos valores de píxeles.\ Medir cada una de estas imágenes daría los mismos resultados.#

Esto es crucial porque es completamente posible analizar dos imágenes diferentes que parecen idénticas, pero obtener resultados muy diferentes (y muy incorrectos).

Esto está lejos de ser un problema teórico. Sucede mucho en la práctica cuando alguien inocentemente hace un ajuste a una imagen (por ejemplo, para hacerla lucir más brillante o cambiar los colores de visualización) sin darse cuenta de que el ajuste en realidad ha cambiado los valores de los píxeles y, por lo tanto, ha comprometido los datos subyacentes. Esto puede socavar fatalmente la integridad de cualquier análisis posterior.

Lo que es peor, estos errores pueden pasar completamente desapercibidos, agravando subrepticiamente el problema de la replicabilidad en la ciencia.

Lo que nos lleva al mensaje clave de este capítulo:

¡No confíes (simplemente) en tus ojos!

En ciencia, necesitamos saber qué sucede cada vez que abrimos, ajustamos y guardamos nuestras imágenes. Si no lo hacemos, corremos el riesgo de malinterpretar nuestros datos.

Afortunadamente, saber un poco sobre imágenes y análisis de imágenes es suficiente para evitar cometer estos errores. Saber más que un poquito puede abrir nuevos mundos de posibilidades para extraer información útil de imágenes científicas.

El objetivo de este manual es explicar estas ideas. Comenzaremos considerando dos preguntas:

  1. ¿De dónde provienen los valores de píxeles?

  2. ¿Cómo se convierten los valores de píxeles en colores para su visualización?

Es difícil dar una respuesta detallada, y a la vez general, a la primera pregunta, porque el origen y la interpretación de los valores de los píxeles dependen de cómo se creó la imagen y hay muchas formas diferentes de generar una imagen.

Sin embargo, las ideas clave son similares en todas partes. A modo de ilustración, consideraremos un caso muy común en bioimagen donde los valores de píxeles se relacionan con la luz detectada, específicamente, usando el ejemplo de un microscopio de fluorescencia, antes de pasar a ver cómo se muestran estos valores.

Un microscopio simple#

Cuando trabajo con imágenes de fluorescencia, tengo en mi cabeza una imagen muy sencilla de cómo se forma la imagen. Puede que no sea muy exacta, pero lo encuentro extremadamente útil como base a la que podemos añadir detalles cuando los necesitemos. Revisaremos esta imagen más adelante en el libro para ayudar a organizar las consideraciones interrelacionadas, relevantes para el análisis de imágenes.

En mi modelo simplificado, sólo hay tres componentes de los que debemos preocuparnos:

  1. Muestra: lo que queremos ver

  2. Lente objetivo: lo que capta la luz y la enfoca para su detección.

  3. Detector: lo que detecta la luz para formar la imagen digital (en este caso, una cámara CCD)

El proceso se ilustra a continuación:

Hay un par de cosas a tener en cuenta en este punto:

  • No se detecta toda la luz emitida por la muestra. Gran parte nunca entra en la lente del objetivo.

  • Nuestras imágenes no son perfectas. Exploraremos los problemas de desenfoque, ruido y tamaño de píxel más adelante.

Por ahora, lo que más nos interesa es el paso de detección y cómo genera una imagen digital. Acercándonos para ver esto con más detalle, podemos imaginar lo que sucede cuando la luz incide en la cámara. El sensor de la cámara en sí está dividido en píxeles físicos, que corresponderán a los píxeles de la imagen final. Cuando un fotón choca contra el detector, es posible que se libere un electrón en uno de los píxeles físicos. Durante la adquisición de una imagen, muchos fotones chocan contra el detector, lo que puede provocar que se liberen muchos electrones en diferentes píxeles físicos. Estos electrones contribuyen al valor de un píxel en la imagen final: más electrones → valores de píxel más altos.

El punto importante es que los valores de píxeles solo están relacionados indirectamente con lo que sea que queramos medir en nuestra muestra.

En este ejemplo, se obtuvieron cuantificando la carga de las nubes de electrones reunidas en cada píxel físico. Esto debe ser proporcional a la cantidad de luz detectada que se originó en un volumen particular de la muestra. Esto, a su vez, depende de lo que realmente está presente en la muestra, pero hay muchas cosas que pueden influir en los valores finales en relación con los parámetros de adquisición, los factores de conversión y la física. Por lo general, estos no están relacionados directamente con lo que desea cuantificar.

Algunos de los factores que influyen en los valores de los píxeles.

  • Cantidad de tiempo dedicado a detectar fotones

    • Más tiempo → Más fotones → Más electrones → Valores de píxeles más altos

  • Apertura numérica de la lente del objetivo

    • Esto se relaciona con el ángulo de luz aceptado por el objetivo.

    • Mayor NA → Mayor ángulo → Más fotones → Más electrones → Valores de píxeles más altos

  • Sensibilidad del detector (Eficiencia Cuántica)

    • No todos los fotones producen necesariamente un electrón. Pienso en esto como si el fotón golpeara el detector, pero no con la fuerza suficiente para desalojar un electrón. Es probable que un detector con baja sensibilidad «pierda» más fotones, de modo que nunca contribuyan al valor del píxel.

    • Mayor sensibilidad → Más electrones → Valores de píxeles más altos

En última instancia, esto lleva a la advertencia:

¡No sobreinterpretes los valores de los píxeles!

Los valores de píxeles individuales rara vez son muy significativos de forma aislada: normalmente nos interesan las diferencias relativas entre grupos de píxeles.

Como veremos, esto significa que a menudo necesitamos promediar valores y normalizarlos a algo cuando queremos realizar mediciones en una imagen. Por lo general, no podemos desenredar las influencias lo suficientemente bien como para inferir algo con confianza a partir del valor de un solo píxel.

Pero las limitaciones en lo que los valores de los píxeles pueden decirnos no disminuyen su importancia: por el contrario, los valores de los píxeles siguen siendo nuestros datos sin procesar y es esencial que los conservemos lo más fielmente posible. Eso es mucho más difícil de lo que cabría esperar. Requiere saber cuándo y cómo los valores de los píxeles pueden cambiar cada vez que trabajamos con nuestras imágenes. Esto es tan crucial que será el tema central durante toda la primera parte de este libro.

Tablas de búsqueda#

Así que las imágenes en realidad se componen de muchos números (los valores de los píxeles), aunque normalmente los visualizamos como formas y colores.

Entonces es hora de considerar nuestra segunda pregunta: ¿Cómo se convierten los valores de píxeles para su visualización?

La idea básica es simple: el software que muestra la imagen utiliza una tabla de búsqueda (LUT) que asigna cada valor de píxel a un color. A la hora de mostrar la imagen, cada píxel se sustituye por un puntito o cuadrado en pantalla que tiene el color correspondiente.

Por lo tanto, las LUT proporcionan una manera de cambiar la apariencia de una imagen sin cambiar sus valores de píxeles.

Esto es extremadamente útil en la práctica. Dado que las imágenes en biología suelen tener valores de píxeles bastante bajos (formados a partir de una pequeña cantidad de luz detectada), muy a menudo queremos cambiar su brillo para su visualización.

Una forma en que podríamos hacer que una imagen sea más brillante es cambiar los valores de los píxeles mismos: multiplicarlos por 2, por ejemplo. De hecho, esto normalmente haría que la imagen se vea más brillante, pero corremos el riesgo de crear un desastre terrible con nuestros datos si nos permitimos realizar tales cambios. Como se describió anteriormente, realmente no queremos modificar innecesariamente nuestros datos sin procesar.

Una manera mucho mejor de cambiar el brillo de una imagen es cambiar solo la LUT.

El peligro es que no todo el software se preocupa tanto por preservar los valores de los píxeles. A alguien que quiera mejorar sus fotos de vacaciones probablemente no le importe conservar los valores de píxeles originales para cuantificarlos más adelante; más bien, sólo quieren que las imágenes se vean lo más bonitas posible.

Por esta razón, muchos programas diseñados para trabajar con imágenes realmente cambiarán la escala de los valores de píxeles cuando haces algo tan simple como ajustar el brillo. Y por eso es completamente posible abrir una imagen, ajustar ligeramente la visualización de la pantalla para ver las cosas con mayor claridad y, al hacerlo, dañar irreparablemente la imagen, perdiendo los datos sin procesar, necesarios para un análisis posterior.

Es por eso que usted debe usar software científico para el análisis de imágenes científicas, y no cualquier software de edición de imágenes general que pueda encontrar.

Pero incluso cuando se utiliza software científico, a menudo es posible cambiar los valores de los píxeles cuando realmente se prefiere cambiar las tablas de búsqueda. El próximo capítulo mostrará cómo comprobar cuándo esto sucede.