Python • DESARROLLO

Imágenes satélite en Python

VIGILANTES DEL PLANETA
¿Quién no ha querido alguna vez sentirse como esos informáticos de la NASA en su centro de control? Hoy nos construiremos el nuestro y controlaremos el planeta y sus alrededores. POR JOSÉ MARÍA RUIZ Y PEDRO ORANTES

C

ada vez que vemos el lanzamiento de un cohete todos quedamos asombrados ante la explosión del despegue, la atenta mirada de todos esos científicos a los paneles de control, y la monstruosa cifra que nos dicen que se han gastado en el proyecto.

¿A donde van los impuestos?
Es entonces cuando surge la pregunta ¿y eso a mí en qué me repercute? Un día estando en el despacho de la Rama del IEEE de Málaga tuve una conversación en la que me contaron que la mayor parte de los satélites emiten “al mundo” las imágenes y los datos que recogen. Es decir, si se posee el equipo necesario es posible recibir en tu propia casa imágenes fascinantes del universo, de Marte o de la Tierra.

La temperatura del océano, imágenes meteorológicas, imágenes del campo magnético del sol o de las misiones a Marte son enviadas constantemente a la Tierra desde estos engendros espaciales. Y el efecto es siempre el mismo, el espectador es deslumbrado por el presentador de televisión con una imágenes increíbles mientras se escuchan acordes de sintetizador. ¿Acaso no son esas imágenes de dominio público? ¿Dónde puedo conseguirlas? En el presente artículo utilizaremos Python para crear un script CGI que nos permita recoger y mantener actualizadas las imágenes que queremos en una especie de “collage” o mural. Construiremos nuestro propio centro de control espacial.

Recoger las imágenes
Lo primero será encontrar las imágenes y reunirlas. Vamos a usar como ejemplo cuatro imágenes de carácter científico. Se actualizan a distintos intervalos, de manera que podremos ver cómo evolu-

Curiosidad
Poco tiempo después de finalizar este artículo apareció una noticia en Slashdot (ver Recursos [4]) hablando de una llamarada solar de tal tamaño que iba a alterar las comunicaciones. Cuando se dan este tipo de eventos en muchos centros de control de satélites los ingenieros cruzan los dedos para que sus satélites no caigan ante la ola de viento solar que se origina. El lector puede apreciar la llamarada en la Figura 4.

WWW.LINUX- MAGAZINE.ES

Número 10

61

DESARROLLO • Python

En la variable c almacenamos el objeto que representa la conexión realizada y podemos enviar peticiones.
>>> c.request("GET","/issue/08") >>>

Figura 1: La imagen original que vamoa a modificar con PIL.

cionan las eventos que se registran. Puedes encontrar las URLs en Recursos [1]. Debemos descargar las imágenes y almacenarlas dentro de nuestro programa, haremos uso de la librería httplib que es parte de la distribución estándar de Python. Esta librería nos permitirá hablar de tú a tú con un servidor web remoto sin tener que preocuparnos de los detalles de más bajo nivel. Esta conversación la realizaremos usando el protocolo HTTP. Este protocolo es bastante simple y de él sólo necesitaremos una parte mínima. Cuando Tim Berners Lee realizó el diseño original de la Web quiso que el protocolo para pedir los documentos fuese lo más simple posible. HTTP se reduce a la recepción y el envío de información al servidor, eso y sólo eso. Se compone de varios comandos, pero los más conocidos son GET, que podemos traducir como “tomar”, y POST, que podemos traducir en este contexto como “enviar” o “mandar”. Así que tomamos documentos y enviamos información. Una parte importante de HTTP es URL que nos sirve para darle nombre a esos documentos. Todos estamos acostumbrados a tratar con urls, generalmente del tipo http://www.linux-magazine.es/ issue/08. La url se compone de: [protocolo]://[maquina]/[ruta]/[objeto]. Vamos a ver ahora porqué es tan importante que sepamos esto. La librería httplib de Python establece en un primer paso una conexión con el servidor remoto mediante el método HTTPConnection
>>> c = httplib.HTTPConnectionU ("www.linux-magazine.es") >>>

Usamos el comando GET, con lo que estamos solicitando un objeto. El segundo parámetro del método es la “ruta” hasta el objeto. Así que la URL que estamos solicitando es http://www. linux-magazine.es/index.html. Es importante que la ruta comience con una barra “/”, como si fuese la ruta de un fichero de una máquina. ¿Cómo sabemos si todo ha ido bien?
>>> r = c.getresponse() >>> print r.status,r.reason 200 OK >>>

Figura 2: La imagen del pequeño demonio de BSD rotado 45º con PIL.

>>> c.close() >>>

Para obtener las imágenes vamos a hacer exactamente lo mismo, abriremos una conexión, pediremos la imagen, la almacenaremos en una diccionario y cerraremos la conexión.

Con getresponse podemos conseguir un objeto que representa los datos devueltos por la conexión. Este objeto tiene, entre otros, los atributos status y reason que nos indican el estado, un número con un significado especial, y la explicación del mismo. En este caso todo ha ido bien y por eso recibimos un “OK”. En caso contrario, si no existiese la ruta que pedimos habríamos obtenido:
>>> r = c.getresponse() >>> print r.status, r.reason 400 Bad Request >>>

Python Imaging Library
Nuestra idea original era realizar un mural o collage con las imágenes recuperadas. Python no nos provee de una librería de tratamiento gráfico en su distribución estándar. Eso no quiere decir que no exista tal librería, y no sólo existe, sino que además es muy potente y útil. Nos referimos a Python Imaging Library (ver URL [2] en el Listado de Recursos al final del artículo). La librería PIL (Python Imaging Library) nos va a permitir tratar imágenes en una gran cantidad de formatos. Podremos convertirlas a otro formato, rotarlas, escalarlas, mezclarlas, etc. Aquel lector que haya tenido contacto con programas de manipulación gráfica, como por ejemplo GIMP (ver URL [3] en el Listado Recursos), comprenderá la potencia de una librería con estas funcionalidades.

Ahora ya tenemos la página, solo tenemos que leerla, usando el método read() del objeto respuesta.
>>> print r.read() <html> <head> <base href="http://www.U linux-magazine.es/issue/08/" /> <title>Linux Magazine -U Spamassasin, Hypermail,U Encriptación GPG, SDL,U ...

Listado 1: Ejemplo de uso de PIL
01 >>>mural = Image.new('RGB',(600,480)) 02 >>> im = Image.open("daemon.jpg") 03 >>> im.thumbnail((300,200), Image.ANTIALIAS) 04 >>> mural.paste(im,(0,0)) 05 >>> mural.paste(im,(300,0)) 06 >>> mural.show() 07 >>>

Cuando hayamos finalizado debemos cerrar la conexión invocando el método close() del objeto que representa la conexión, en este caso sería:

62

Número 10

WWW.LINUX- MAGAZINE.ES

Python • DESARROLLO

Figura 3: Creamos una imagen vacía con PIL y después colocamos otras imágenes en su interior como mosaico.

Como no viene de serie con Python deberemos instalarla en nuestra distribución o sistema operativo. Existen paquetes RPM y DEB de la misma. ¿Cómo se trabaja con PIL? Pues mediante la manipulación de objetos de la clase Image. Esta clase es capaz de albergar imágenes de casi cualquier formato, permitiéndonos manipularlas. Vemos un ejemplo. En la Figura 1 podemos ver la imagen original del fichero daemon.jpg en mi equipo. Vamos a rotarla 45 grados:
>>> import Image >>> im = Image.openU ("daemon.jpg") >>> img.rotate(45).show() >>>

en el resultado hemos invocado el método show() que mostrará el resultado mediante el programa xv (para cerrar xv solo tenemos que pulsar “q”). Nosotros no buscamos rotar imágenes, sino escalarlas. Las imágenes presentes en la web suelen ser de gran tamaño y nosotros queremos crear un mural de un tamaño estático. Tendremos que adaptar las imágenes descargadas para que quepan en el mural. Para hacerlo vamos a insertar las imágenes en una mayor, pero hay muchas maneras de hacer esto. La solución que adaptaremos en nuestro caso es la de dividir la imagen-mural en tantos recuadros como imágenes vayamos a insertar. ¿Cómo sabremos la cantidad de cuadrículas? Pues escogeremos la menor potencia de 2 que sea mayor que nuestro número de imágenes. No es muy complicado, por ejemplo, si tenemos 7 imágenes, 8 (2 elevado a 3) será suficiente. Básicamente multiplicaremos 2 por sí mismo hasta que sea mayor que el número de imágenes que queramos mostrar. Gráficamente lo que haremos será ir dividiendo en anchura y en altura la imagen en cuadrículas, en cada iteración se multiplicará por 2 el número de cuadrículas. Con este método perderemos espacio en la imagen, pero al ser tan sencillo no complicará mucho el código.

Figura 4: Llamaradas solares que amenazan con dejar fuera de combate a los satélites de comunicadciones.

300x200 pixels. Puede aceptar un parámetro adicional, en nuestro caso es Image.ANTIALIAS que debería mejorar la resolución de la nueva imagen. A continuación usamos el método paste() de Image que nos permite “pegar” una imagen dentro de otra en las coordenadas indicadas como segundo parámetro. Pegamos la imagen “daemon” dos veces, la primera en la posición (0,0) del mural y la segunda en la posición (300,0). Podemos ver el resultado usando el método show().

El fichero de configuración
Las URLs y la resolución deben ser recogidas por el programa, pero ¿cómo? Existen varias opciones, podríamos pasárselas al programa cuando se ejecute. Las URLs tienen el problema de ser bastante largas en ocasiones, así que la linea de comando para ejecutar el programa puede ser engorrosa. En lugar de eso vamos a usar un fichero de configuración. Cada vez que el programa se ejecute leerá este fichero y recogerá los parámetros oportunos. ¿Qué forma tendrá el fichero? La última tendencia es crear ficheros XML de configuración. Pero el XML puede ser demasiado complicado si tenemos en cuenta que nuestro fichero de configuración puede no tener más de 10 líneas. En UNIX la tendencia es la de usar el formato de “clave = valor” y ese es el que usaremos. El fichero será como el que se muestra en el Listado 2. Leeremos cada línea del fichero, la dividiremos usando el “=” y usaremos la primera parte como clave en un diccionario, y la segunda como valor. Si ya existe la clave, usaremos una lista como valor con

En la Figura 2 podemos ver el resultado. Hemos usado el método rotate() al que hemos pasado un ángulo de 45 grados, y

Creemos el thumbnail
Primero creemos una imagen vacía, ver Listado 1. La Figura 3 muestra el resultado. Esta vez no cargamos ninguna imagen, sino que usamos el método new() que necesita el tipo de pixel (‘RGB’ viene de Red->Rojo Green->Verde Blue>Azul, es uno de los formatos estándar) y el tamaño de la imagen medido en pixels. En nuestro caso hemos escogido 600 pixels de ancho por 480 de alto (presta atención a los “()”, porque la resolución se expresa como una secuencia del tipo “(x,y)” ). Esta nueva imagen no contiene nada a excepción de un decepcionante fondo negro. ¡Vamos a poner algo de color! Cogemos la imagen del “daemon” e invocamos el método thumbnail() que escala la imagen tanto vertical como horizontalmente. Tenemos que pasarle el tamaño deseado como una secuencia, la nueva imagen tendrá un tamaño de

Listado 2: collage.conf
01 02 03 04 05 06 [tamaño] horizontal = 800 vertical = 600

[imágenes] url1 = http://www-mgcm.arc.nasa.gov/M arsToday/marstoday.gif 07 url2 = http://www.sec.noaa.gov/sxi/cu rrent_sxi_4MKcorona.png 08 url3 = http://www.ssec.wisc.edu/data/ sst/latest_sst.gif 09 url4 = http://www.wetterzentrale.de/pics/D2u.jpg

WWW.LINUX- MAGAZINE.ES

Número 10

63

DESARROLLO • Python

sobre el collage, de manera los distintos valores como que sea posible pulsar sobre entrada. Pero ¿por qué vamos las distintas imágenes que en a realizar nosotros el trabajo él aparecen. Al hacerlo se carduro cuando alguien ya lo ha gará la imagen a tamaño naturesuelto? ral. El mapa se genera recoPython trae en su distriburriendo el diccionario de imáción estándar una librería que genes. Cada entrada del dicnos será de enorme utilidad. cionario contiene un objeto de Alguien consideró oportuno la clase Imagen. elaborar un analizador de Imagen alberga la informaarchivos de configuración, se ción de cada imagen descarllama ConfigParser. Con ella gada mientras el programa la podemos extraer la informaalmacena. Se almacenan los ción del archivo de configuradatos propios de cada imación. gen, como por ejemplo las El archivo de configuración coordenadas que ocupará se compone de “Secciones” y Figura 5: Nuestro panel de control espacial terminado y colocado en finalmente en el collage. “Opciones”. Cada sección conuna página web generada dinámicamente. Como siempre, se espera tiene varias opciones, y los que el lector dedique algo de tiempo a nombres de las secciones y opciones Cuando un método comienza con “__” jugar con el programa para adaptarlo a deben ser únicos. Por eso las URLs se convierte en privado. Cualquier intensus necesidades o ideas. comienzan con “url1”, “url2” y “url3”. to de hacer uso de ese método generará Pero esto no será un problema, vemos una excepción. Por tanto esos métodos Conclusión cómo funciona ConfigParser (ver Listado no pueden ser invocados desde fuera del 3). Como podemos apreciar en el ejemobjeto Collage. De esta manera Collage La complejidad de un programa Python plo, el uso de ConfigParser es muy sencisólo tiene un método accesible desde el no depende de la cantidad de líneas de llo. Primero se crea el analizador, guarexterior, generaCollage(). Se ha separado código que contenga, sino más bien del dándolo en la variable config. Después la generación de HTML de la del collage nivel al que trabaje. En el programa de cargamos con el método readfp() el fichepara posibilitar las futuras extensiones este artículo hemos hecho uso intensivo ro de configuración; este método también del objeto. Por ejemplo podríamos no de librerías que han realizado acciones analiza el fichero. A partir de ese momenquerer generar un fichero HTML sino muy complicadas por nosotros. Python to podemos realizar preguntas al objeto incorporar la imagen en un programa. En posee una amplio abanico de librerías a almacenado en config. Con sections() tal caso heredaríamos de Collage y creaexplotar, muchas de ellas con años de obtenemos una lista de las secciones y ríamos un nuevo método desarrollo esperando a programadores con options() de las opciones. Con esa generaCollage() que solo generase la con ideas originales que poner en práctiinformación ya podemos recoger los imagen y la devolviese. ca. s datos necesarios usando el método get(), El método __generaHTML() genera el al que pasamos una sección y una código HTML de la página web. Un opción. punto a resaltar es que genera un mapa RECURSOS

Ensamblemos las partes
Ahora ya tenemos: • Un sistema de configuración, usando ConfigParser. • Un sistema para descargar las imágenes, usando httplib. • Un sistema para manipular las imágenes, usando PIL. Nos toca ahora unirlo todo para que genere la página que aparece en la Figura 5. El resultado final se puede descargar de [5]. Crearemos una clase Collage con los métodos • __cargaConf() • __descarga() • __totalXY() • generaCollage() • __generaImagen() • __generaHTML()

Listado 3: Uso de ConfigParser
01 >>> config = ConfigParser.ConfigParser() 02 >>> config.readfp(open('collage.conf')) 03 >>> config.sections() 04 ['tamaño', 'imágenes'] 05 >>> 06 >>> config.options('imagen') 07 ['url1', 'url3', 'url2'] 08 >>> 09 >>> config.get('imagen','url1') 10 '"http://www-mgcm.arc.nasa.gov /MarsToday/marstoday.gif"'

[1] Gráficos que usaremos: http://www-mgcm.arc.nasa.gov/ MarsToday/marstoday.gif http://www.sec.noaa.gov/sxi/ current_sxi_4MKcorona.png http://www.ssec.wisc.edu/data/sst/ latest_sst.gif http://www.wetterzentrale.de/pics/D2u. jpg [2] Python Imaging Library: http://www. pythonware.com/products/pil/ [3] The Gimp: http://www.gimp.org [4] Noticia sobre llamarada solar en Slashdot: http://science.slashdot.org/ science/05/09/08/1933205. shtml?tid=215&tid=14 [5] Listado del programa final de este artículo:

64

Número 10

WWW.LINUX- MAGAZINE.ES

Sign up to vote on this title
UsefulNot useful