Nota
Haga clic aquí para descargar el código de ejemplo completo
Tutorial de imagen #
Un breve tutorial sobre el trazado de imágenes con Matplotlib.
Comandos de inicio #
Primero, comencemos con IPython. Es una excelente mejora del indicador estándar de Python y se relaciona especialmente bien con Matplotlib. Inicie IPython directamente en un shell o con Jupyter Notebook (donde IPython es un kernel en ejecución).
Con IPython iniciado, ahora necesitamos conectarnos a un bucle de eventos GUI. Esto le dice a IPython dónde (y cómo) mostrar los gráficos. Para conectarse a un bucle GUI, ejecute la magia %matplotlib en el indicador de IPython. Hay más detalles sobre lo que hace esto exactamente en la documentación de IPython sobre los bucles de eventos de GUI .
Si usa Jupyter Notebook, los mismos comandos están disponibles, pero la gente suele usar un argumento específico para la magia de %matplotlib:
In [1]: %matplotlib inline
Esto activa el trazado en línea, donde los gráficos de trazado aparecerán en su cuaderno. Esto tiene implicaciones importantes para la interactividad. Para el trazado en línea, los comandos en las celdas debajo de la celda que genera un gráfico no afectarán el gráfico. Por ejemplo, no es posible cambiar el mapa de colores desde las celdas debajo de la celda que crea un gráfico. Sin embargo, para otros backends, como Qt, que abren una ventana separada, las celdas debajo de las que crean el gráfico cambiarán el gráfico: es un objeto vivo en la memoria.
Este tutorial utilizará la interfaz de trazado implícita de Matplotlib, pyplot. Esta interfaz mantiene el estado global y es muy útil para experimentar rápida y fácilmente con varias configuraciones de trazado. La alternativa es el explícito, que es más adecuado para el desarrollo de aplicaciones grandes. Para obtener una explicación de las compensaciones entre las interfaces implícitas y explícitas, consulte Interfaces de aplicaciones (API) de Matplotlib y la Guía de inicio rápido para comenzar a usar la interfaz explícita. Por ahora, sigamos con el enfoque implícito:
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
Importación de datos de imagen en matrices Numpy #
Matplotlib se basa en la biblioteca Pillow para cargar datos de imagen.
Aquí está la imagen con la que vamos a jugar:
Es una imagen PNG RGB de 24 bits (8 bits para cada R, G, B). Dependiendo de dónde obtenga sus datos, los otros tipos de imágenes que probablemente encontrará son imágenes RGBA, que permiten la transparencia, o imágenes en escala de grises (luminosidad) de un solo canal. Descarga stinkbug.png a tu computadora para el resto de este tutorial.
Y aquí vamos...
img = mpimg.imread('../../doc/_static/stinkbug.png')
print(img)
[[[0.40784314 0.40784314 0.40784314]
[0.40784314 0.40784314 0.40784314]
[0.40784314 0.40784314 0.40784314]
...
[0.42745098 0.42745098 0.42745098]
[0.42745098 0.42745098 0.42745098]
[0.42745098 0.42745098 0.42745098]]
[[0.4117647 0.4117647 0.4117647 ]
[0.4117647 0.4117647 0.4117647 ]
[0.4117647 0.4117647 0.4117647 ]
...
[0.42745098 0.42745098 0.42745098]
[0.42745098 0.42745098 0.42745098]
[0.42745098 0.42745098 0.42745098]]
[[0.41960785 0.41960785 0.41960785]
[0.41568628 0.41568628 0.41568628]
[0.41568628 0.41568628 0.41568628]
...
[0.43137255 0.43137255 0.43137255]
[0.43137255 0.43137255 0.43137255]
[0.43137255 0.43137255 0.43137255]]
...
[[0.4392157 0.4392157 0.4392157 ]
[0.43529412 0.43529412 0.43529412]
[0.43137255 0.43137255 0.43137255]
...
[0.45490196 0.45490196 0.45490196]
[0.4509804 0.4509804 0.4509804 ]
[0.4509804 0.4509804 0.4509804 ]]
[[0.44313726 0.44313726 0.44313726]
[0.44313726 0.44313726 0.44313726]
[0.4392157 0.4392157 0.4392157 ]
...
[0.4509804 0.4509804 0.4509804 ]
[0.44705883 0.44705883 0.44705883]
[0.44705883 0.44705883 0.44705883]]
[[0.44313726 0.44313726 0.44313726]
[0.4509804 0.4509804 0.4509804 ]
[0.4509804 0.4509804 0.4509804 ]
...
[0.44705883 0.44705883 0.44705883]
[0.44705883 0.44705883 0.44705883]
[0.44313726 0.44313726 0.44313726]]]
Tenga en cuenta el dtype allí - float32. Matplotlib ha reescalado los datos de 8 bits de cada canal a datos de punto flotante entre 0,0 y 1,0. Como nota al margen, el único tipo de datos con el que puede trabajar Pillow es uint8. El trazado de Matplotlib puede manejar float32 y uint8, pero la lectura/escritura de imágenes para cualquier formato que no sea PNG está limitada a datos uint8. ¿Por qué 8 bits? La mayoría de las pantallas solo pueden representar 8 bits por canal de gradación de color. ¿Por qué solo pueden renderizar 8 bits/canal? Porque eso es todo lo que el ojo humano puede ver. Más aquí (desde el punto de vista de la fotografía): Tutorial de profundidad de bits de Luminous Landscape .
Cada lista interna representa un píxel. Aquí, con una imagen RGB, hay 3 valores. Como es una imagen en blanco y negro, R, G y B son similares. Un RGBA (donde A es alfa o transparencia), tiene 4 valores por lista interna, y una imagen de luminancia simple solo tiene un valor (y, por lo tanto, es solo una matriz 2-D, no una matriz 3-D). Para imágenes RGB y RGBA, Matplotlib admite tipos de datos float32 y uint8. Para la escala de grises, Matplotlib solo admite float32. Si los datos de su matriz no cumplen con una de estas descripciones, debe volver a escalarla.
Trazado de matrices numpy como imágenes #
Entonces, tiene sus datos en una matriz numpy (ya sea importándolos o generándolos). Vamos a renderizarlo. En Matplotlib, esto se realiza mediante la imshow()
función. Aquí tomaremos el objeto de la trama. Este objeto le brinda una manera fácil de manipular la trama desde el indicador.
imgplot = plt.imshow(img)
También puede trazar cualquier matriz numpy.
Aplicación de esquemas de pseudocolor a gráficos de imágenes #
Pseudocolor puede ser una herramienta útil para mejorar el contraste y visualizar sus datos más fácilmente. Esto es especialmente útil cuando se realizan presentaciones de sus datos utilizando proyectores, ya que su contraste suele ser bastante pobre.
El pseudocolor solo es relevante para imágenes de luminosidad de escala de grises de un solo canal. Actualmente tenemos una imagen RGB. Dado que R, G y B son todos similares (véalo usted mismo arriba o en sus datos), podemos elegir un canal de nuestros datos:
lum_img = img[:, :, 0]
# This is array slicing. You can read more in the `Numpy tutorial
# <https://numpy.org/doc/stable/user/quickstart.html>`_.
plt.imshow(lum_img)
<matplotlib.image.AxesImage object at 0x7f2cdd608610>
Ahora, con una imagen de luminosidad (2D, sin color), se aplica el mapa de colores predeterminado (también conocido como tabla de búsqueda, LUT). El valor predeterminado se llama viridis. Hay muchos otros para elegir.
plt.imshow(lum_img, cmap="hot")
<matplotlib.image.AxesImage object at 0x7f2cddcc2aa0>
Tenga en cuenta que también puede cambiar mapas de colores en objetos de trazado existentes utilizando el
set_cmap()
método:
imgplot = plt.imshow(lum_img)
imgplot.set_cmap('nipy_spectral')
Nota
Sin embargo, recuerde que en Jupyter Notebook con el backend en línea, no puede realizar cambios en los gráficos que ya se han representado. Si crea imgplot aquí en una celda, no puede llamar a set_cmap() en una celda posterior y esperar que cambie el gráfico anterior. Asegúrese de ingresar estos comandos juntos en una celda. Los comandos plt no cambiarán los gráficos de las celdas anteriores.
Hay muchos otros esquemas de mapas de colores disponibles. Ver la lista e imágenes de los mapas de colores .
Número de referencia de la escala de colores
Es útil tener una idea de qué valor representa un color. Podemos hacerlo agregando una barra de color a su figura:
<matplotlib.colorbar.Colorbar object at 0x7f2cdf5297e0>
Examinando un rango de datos específico #
A veces desea mejorar el contraste en su imagen o expandir el contraste en una región particular mientras sacrifica el detalle en colores que no varían mucho o no importan. Una buena herramienta para encontrar regiones interesantes es el histograma. Para crear un histograma de nuestros datos de imagen, usamos la hist()
función.
(array([2.000e+00, 2.000e+00, 3.000e+00, 3.000e+00, 2.000e+00, 2.000e+00,
3.000e+00, 1.000e+00, 7.000e+00, 9.000e+00, 7.000e+00, 2.000e+00,
7.000e+00, 1.000e+01, 1.100e+01, 1.500e+01, 1.400e+01, 2.700e+01,
2.100e+01, 2.400e+01, 1.400e+01, 3.100e+01, 2.900e+01, 2.800e+01,
2.400e+01, 2.400e+01, 4.000e+01, 2.600e+01, 5.200e+01, 3.900e+01,
5.700e+01, 4.600e+01, 8.400e+01, 7.600e+01, 8.900e+01, 8.000e+01,
1.060e+02, 1.130e+02, 1.120e+02, 9.000e+01, 1.160e+02, 1.090e+02,
1.270e+02, 1.350e+02, 9.800e+01, 1.310e+02, 1.230e+02, 1.110e+02,
1.230e+02, 1.160e+02, 1.010e+02, 1.170e+02, 1.000e+02, 1.010e+02,
9.000e+01, 1.060e+02, 1.260e+02, 1.040e+02, 1.070e+02, 1.110e+02,
1.380e+02, 1.000e+02, 1.340e+02, 1.210e+02, 1.400e+02, 1.320e+02,
1.390e+02, 1.160e+02, 1.330e+02, 1.180e+02, 1.080e+02, 1.170e+02,
1.280e+02, 1.200e+02, 1.210e+02, 1.100e+02, 1.160e+02, 1.180e+02,
9.700e+01, 9.700e+01, 1.140e+02, 1.070e+02, 1.170e+02, 8.700e+01,
1.070e+02, 9.800e+01, 1.040e+02, 1.120e+02, 1.110e+02, 1.180e+02,
1.240e+02, 1.340e+02, 1.200e+02, 1.410e+02, 1.520e+02, 1.360e+02,
1.610e+02, 1.380e+02, 1.620e+02, 1.570e+02, 1.350e+02, 1.470e+02,
1.690e+02, 1.710e+02, 1.820e+02, 1.980e+02, 1.970e+02, 2.060e+02,
2.160e+02, 2.460e+02, 2.210e+02, 2.520e+02, 2.890e+02, 3.450e+02,
3.620e+02, 3.760e+02, 4.480e+02, 4.630e+02, 5.170e+02, 6.000e+02,
6.200e+02, 6.410e+02, 7.440e+02, 7.120e+02, 8.330e+02, 9.290e+02,
1.061e+03, 1.280e+03, 1.340e+03, 1.638e+03, 1.740e+03, 1.953e+03,
2.151e+03, 2.290e+03, 2.440e+03, 2.758e+03, 2.896e+03, 3.384e+03,
4.332e+03, 5.584e+03, 6.197e+03, 6.422e+03, 6.404e+03, 7.181e+03,
8.196e+03, 7.968e+03, 7.474e+03, 7.926e+03, 8.460e+03, 8.091e+03,
9.148e+03, 8.563e+03, 6.747e+03, 6.074e+03, 6.328e+03, 5.291e+03,
6.472e+03, 6.268e+03, 2.864e+03, 3.760e+02, 1.620e+02, 1.180e+02,
1.270e+02, 9.500e+01, 7.600e+01, 8.200e+01, 6.200e+01, 6.700e+01,
5.600e+01, 5.900e+01, 4.000e+01, 4.200e+01, 3.000e+01, 3.400e+01,
3.200e+01, 4.300e+01, 4.200e+01, 2.300e+01, 2.800e+01, 1.900e+01,
2.200e+01, 1.600e+01, 1.200e+01, 1.800e+01, 9.000e+00, 1.000e+01,
1.700e+01, 5.000e+00, 2.100e+01, 1.300e+01, 8.000e+00, 1.200e+01,
1.000e+01, 8.000e+00, 8.000e+00, 5.000e+00, 1.300e+01, 6.000e+00,
3.000e+00, 7.000e+00, 6.000e+00, 2.000e+00, 1.000e+00, 5.000e+00,
3.000e+00, 3.000e+00, 1.000e+00, 1.000e+00, 1.000e+00, 5.000e+00,
0.000e+00, 1.000e+00, 3.000e+00, 0.000e+00, 1.000e+00, 1.000e+00,
2.000e+00, 1.000e+00, 0.000e+00, 0.000e+00, 0.000e+00, 0.000e+00,
0.000e+00, 0.000e+00, 0.000e+00, 0.000e+00, 0.000e+00, 0.000e+00,
0.000e+00, 0.000e+00, 0.000e+00, 0.000e+00, 0.000e+00, 0.000e+00,
0.000e+00, 0.000e+00, 0.000e+00, 0.000e+00, 0.000e+00, 0.000e+00,
0.000e+00, 0.000e+00, 0.000e+00, 0.000e+00, 0.000e+00, 0.000e+00,
0.000e+00, 0.000e+00, 0.000e+00, 0.000e+00, 0.000e+00, 0.000e+00,
0.000e+00, 0.000e+00, 0.000e+00, 0.000e+00]), array([0. , 0.00390625, 0.0078125 , 0.01171875, 0.015625 ,
0.01953125, 0.0234375 , 0.02734375, 0.03125 , 0.03515625,
0.0390625 , 0.04296875, 0.046875 , 0.05078125, 0.0546875 ,
0.05859375, 0.0625 , 0.06640625, 0.0703125 , 0.07421875,
0.078125 , 0.08203125, 0.0859375 , 0.08984375, 0.09375 ,
0.09765625, 0.1015625 , 0.10546875, 0.109375 , 0.11328125,
0.1171875 , 0.12109375, 0.125 , 0.12890625, 0.1328125 ,
0.13671875, 0.140625 , 0.14453125, 0.1484375 , 0.15234375,
0.15625 , 0.16015625, 0.1640625 , 0.16796875, 0.171875 ,
0.17578125, 0.1796875 , 0.18359375, 0.1875 , 0.19140625,
0.1953125 , 0.19921875, 0.203125 , 0.20703125, 0.2109375 ,
0.21484375, 0.21875 , 0.22265625, 0.2265625 , 0.23046875,
0.234375 , 0.23828125, 0.2421875 , 0.24609375, 0.25 ,
0.25390625, 0.2578125 , 0.26171875, 0.265625 , 0.26953125,
0.2734375 , 0.27734375, 0.28125 , 0.28515625, 0.2890625 ,
0.29296875, 0.296875 , 0.30078125, 0.3046875 , 0.30859375,
0.3125 , 0.31640625, 0.3203125 , 0.32421875, 0.328125 ,
0.33203125, 0.3359375 , 0.33984375, 0.34375 , 0.34765625,
0.3515625 , 0.35546875, 0.359375 , 0.36328125, 0.3671875 ,
0.37109375, 0.375 , 0.37890625, 0.3828125 , 0.38671875,
0.390625 , 0.39453125, 0.3984375 , 0.40234375, 0.40625 ,
0.41015625, 0.4140625 , 0.41796875, 0.421875 , 0.42578125,
0.4296875 , 0.43359375, 0.4375 , 0.44140625, 0.4453125 ,
0.44921875, 0.453125 , 0.45703125, 0.4609375 , 0.46484375,
0.46875 , 0.47265625, 0.4765625 , 0.48046875, 0.484375 ,
0.48828125, 0.4921875 , 0.49609375, 0.5 , 0.50390625,
0.5078125 , 0.51171875, 0.515625 , 0.51953125, 0.5234375 ,
0.52734375, 0.53125 , 0.53515625, 0.5390625 , 0.54296875,
0.546875 , 0.55078125, 0.5546875 , 0.55859375, 0.5625 ,
0.56640625, 0.5703125 , 0.57421875, 0.578125 , 0.58203125,
0.5859375 , 0.58984375, 0.59375 , 0.59765625, 0.6015625 ,
0.60546875, 0.609375 , 0.61328125, 0.6171875 , 0.62109375,
0.625 , 0.62890625, 0.6328125 , 0.63671875, 0.640625 ,
0.64453125, 0.6484375 , 0.65234375, 0.65625 , 0.66015625,
0.6640625 , 0.66796875, 0.671875 , 0.67578125, 0.6796875 ,
0.68359375, 0.6875 , 0.69140625, 0.6953125 , 0.69921875,
0.703125 , 0.70703125, 0.7109375 , 0.71484375, 0.71875 ,
0.72265625, 0.7265625 , 0.73046875, 0.734375 , 0.73828125,
0.7421875 , 0.74609375, 0.75 , 0.75390625, 0.7578125 ,
0.76171875, 0.765625 , 0.76953125, 0.7734375 , 0.77734375,
0.78125 , 0.78515625, 0.7890625 , 0.79296875, 0.796875 ,
0.80078125, 0.8046875 , 0.80859375, 0.8125 , 0.81640625,
0.8203125 , 0.82421875, 0.828125 , 0.83203125, 0.8359375 ,
0.83984375, 0.84375 , 0.84765625, 0.8515625 , 0.85546875,
0.859375 , 0.86328125, 0.8671875 , 0.87109375, 0.875 ,
0.87890625, 0.8828125 , 0.88671875, 0.890625 , 0.89453125,
0.8984375 , 0.90234375, 0.90625 , 0.91015625, 0.9140625 ,
0.91796875, 0.921875 , 0.92578125, 0.9296875 , 0.93359375,
0.9375 , 0.94140625, 0.9453125 , 0.94921875, 0.953125 ,
0.95703125, 0.9609375 , 0.96484375, 0.96875 , 0.97265625,
0.9765625 , 0.98046875, 0.984375 , 0.98828125, 0.9921875 ,
0.99609375, 1. ]), <BarContainer object of 256 artists>)
La mayoría de las veces, la parte "interesante" de la imagen está alrededor del pico, y puede obtener un contraste adicional recortando las regiones por encima y/o por debajo del pico. En nuestro histograma, parece que no hay mucha información útil en el extremo superior (no hay muchas cosas blancas en la imagen). Ajustemos el límite superior, de modo que efectivamente "acerquemos" parte del histograma. Hacemos esto pasando el argumento clim a imshow. También puede hacer esto llamando al
set_clim()
método del objeto de trazado de imagen, pero asegúrese de hacerlo en la misma celda que su comando de trazado cuando trabaje con Jupyter Notebook; no cambiará los trazados de las celdas anteriores.
Puede especificar el clim en la llamada a plot
.
imgplot = plt.imshow(lum_img, clim=(0.0, 0.7))
También puede especificar el clima usando el objeto devuelto
fig = plt.figure()
ax = fig.add_subplot(1, 2, 1)
imgplot = plt.imshow(lum_img)
ax.set_title('Before')
plt.colorbar(ticks=[0.1, 0.3, 0.5, 0.7], orientation='horizontal')
ax = fig.add_subplot(1, 2, 2)
imgplot = plt.imshow(lum_img)
imgplot.set_clim(0.0, 0.7)
ax.set_title('After')
plt.colorbar(ticks=[0.1, 0.3, 0.5, 0.7], orientation='horizontal')
<matplotlib.colorbar.Colorbar object at 0x7f2cdf75fa30>
Esquemas de interpolación de matrices #
La interpolación calcula cuál "debería" ser el color o el valor de un píxel, según diferentes esquemas matemáticos. Un lugar común en el que esto sucede es cuando cambia el tamaño de una imagen. El número de píxeles cambia, pero desea la misma información. Dado que los píxeles son discretos, falta espacio. La interpolación es cómo llenas ese espacio. Esta es la razón por la que tus imágenes a veces se ven pixeladas cuando las amplías. El efecto es más pronunciado cuanto mayor es la diferencia entre la imagen original y la imagen ampliada. Tomemos nuestra imagen y redujémosla. De hecho, estamos descartando píxeles y conservando solo unos pocos. Ahora, cuando lo trazamos, esos datos se amplían al tamaño de su pantalla. Los píxeles antiguos ya no están allí y la computadora tiene que dibujar píxeles para llenar ese espacio.
Usaremos la biblioteca de almohadas que usamos para cargar la imagen también para cambiar el tamaño de la imagen.
from PIL import Image
img = Image.open('../../doc/_static/stinkbug.png')
img.thumbnail((64, 64)) # resizes image in-place
imgplot = plt.imshow(img)
Aquí tenemos la interpolación por defecto, bilineal, ya que no dimos imshow()
ningún argumento de interpolación.
Probemos algunos otros. Aquí está el "más cercano", que no interpola.
imgplot = plt.imshow(img, interpolation="nearest")
y bicúbico:
imgplot = plt.imshow(img, interpolation="bicubic")
La interpolación bicúbica se usa a menudo cuando se amplían las fotos: la gente tiende a preferir las borrosas a las pixeladas.
Tiempo total de ejecución del script: (0 minutos 8.344 segundos)