שירים להורדה חינם לפלאפון
ויאגרה למכירה
וואלה תרגום עברי ערבי
рейтинг брокеров бинарных опционов 2016
קמגרה
али эспресс

Tag Archives: programación

titi 0.2.0, sale con fritas

Si bien vengo atrasado, por cada opúsculo vertido, hay docenas de borradores en el lado oscuro de mis párpados, entreverados con maravillosas jugadas futbolísticas que se desvanecen por la mañana.

Pero ese tiempo de imaginación se terminó y acá vengo a contarles la nueva versión de Titi. Que qué es? Es un Software Libre para visualizar y extraer valores de imágenes satelitales. La última versión liberada es la 0.2.0 y está disponible para todo el mundo desde acá: https://github.com/emilopez/titi/releases/tag/v0.2.0

No solo para mirar está el mundo y por ese motivo agregué algunas funciones útiles a la hora de procesar una gran cantidad de imágenes. Ponele que tenés 678 mapas de evapotranspiración y que necesitás extraer el valor de un pixel de cada una de ellas. Ahora, el drama viene porque ese pixel -imaginate que corresponde al patio de tu casa- no siempre se encuentra en la misma fila y columna, viste que los satélites tienen esa loca manía de moverse, entonces la foto que sacan no siempre está encuadrada sobre la misma superficie, es más, nunca es igual.

Entonces?
Bueno, miro el metadata de la imagen, y con la información que saco de ahí calculo la fila y columna correspondiente a la latitud y longitud del patio en cuestión, eso lo hago con cada imagen y mientras ese laburo lo hace titi vos te armás unos ricos mates o, si es de tardecita y vivís en Santa Fe, te destapás una birra. El módulo que realiza estos cálculos masivos se llama titi-k, me maté, y acá una rápida grabación sobre su uso.

Por otro lado, cabe destacar que hay un nuevo programador aportando a este software, que si bien me tiró con un fardo de laburo -decenas de centenas de líneas de código-, en breve habrá una nueva versión con soporte para visualizar las imágenes del reciente satélite desarrollado por la CONAE de Argentina y la NASA, el Aquarius SAC-D. Así que, se paciente, que esto pinta lindo.

Y ahora sí, lo que todos estábamos esperando:

Internet de las cosas

Así se refieren ahora al envío y recepción de información a Internet por aparatos. Es un concepto algo viejo pero que en los últimos años vino tomando fuerza. Asusta un poco que los aparatos tomen decisiones en forma autónoma, y nos termine pasando lo de Matrix, tranqui, mientras podamos programarlos todavía tenemos el poder.

A causa del tsunami de 2011, que devastó gran parte de Japón, incluyendo la planta nuclear de Fukushima, pasó a tener vital importancia la medición de radioactividad por parte de instrumentos Geiger que subían automáticamente los valores registrados a la web, específicamente a Pachube. Según leo por ahí, a los pocos días, miles de personas conectaron dispositivos de este tipo para que suban los valores de radiación detectados.

Pachube ahora se llama Xively, y provee una plataforma (PaaS, del inglés Platform as a service) para subir datos en tiempo real, además de una API disponible para varios lenguajes -entre ellos python- como así también a través de la amada consola usando curl (command line tool for transferring data with URL syntax).

Si bien la API está en github con licencia BSD, no estoy seguro que tan libre es la plataforma. Más allá de esto, uno puede elegir compartir sus datos, y según leo en sus FAQs la info ahora también será de ellos, cosa que no me molesta, “compartí con tus hermanos me decía mi vieja”:

I’m concerned about my data, who owns it?

We are too, that’s why Xively’s Terms of Service put you in control! You own your data. You choose whether to share all, part, or none of it. You choose who you want to share it with. You choose the terms that you want to share it under. You can view our terms of service here: https://secure.logmein.com/policies/privacy.aspx
 

Entonces, supongamos que hasta tanto no armemos nuestro propio servidor para atender esta info usamos xively. Supongamos que tenemos un arduino midiendo alguna variable. Supongamos que Sabella no nos considera argentinos. Supongamos que aún no mandamos a Internet directamente desde arduino porque no lo probamos y también te pido que supongas que usamos un router liberado con OpenWRT para agarrar estas medidas que nos escupe arduino. Entonces, conectamos el router y la placa arduino mediante USB y ahora, cazamos esto haciendo uso de pyserial, y lo mostramos en pantalla, para ver qué llega:

#!/usr/bin/env python
import sys, serial
import time
import datetime
 
port = '/dev/ttyACM0'
ser = serial.Serial(port, 9600)
while True:
    try:
        line = ser.readline()
        print datetime.datetime.fromtimestamp(time.time()).strftime('%Y-%m-%d %H:%M:%S'), line
        print line
    except KeyboardInterrupt:
        print 'exiting'
        break

Fijate que ahí estoy usando un timestamp generado desde python que NO es lo recomendado, pero como aún no tengo un RTC en arduino que me tire la hora, lo uso para probar. Ojo, capaz no te anda el diálogo entre los dos aparatos, te está faltanto instalar en OWRT:

opkg install python pyserial kmod-usb-acm

Ahora voy a dejar para el lector entusiasta y aplicado que con esos datos leídos por el puerto genere un JSON llamado update_feed_historical.json, que por ejemplo contenga datos históricos de un mismo sensor. Se tendría que verse algo así:

{
  "version":"1.0.0",
   "datastreams" : [ {
        "id" : "sensor1",
        "datapoints":[
        {"at":"2014-05-01T19:35:43Z","value":"42"},
        {"at":"2013-04-01T19:40:43Z","value":"84"},
        {"at":"2013-04-01T19:45:43Z","value":"41"},
        {"at":"2013-04-01T19:50:43Z","value":"83"}
        ],
        "current_value" : "40"
    }
  ]
}

Ahora, nuestro router podría ejecutar un script desde el cron, ponele una vez por día, y me sube todo lo registrado a xively. Entonces, el contenido del script bash tendría algo así:

curl --request PUT \
     --data-binary @update_feed_historical.json \
     --header "X-ApiKey: TU_KEY" https://api.xively.com/v2/feeds/TU_FEED

Bien, muy domingo es hoy y me estoy recuperando de una migraña terrible, razón por la cual escribí esto a las apuradas y con poca claridad. Pero resumamos para que lo entienda alguien más que yo.

  • Leemos con Arduino el valor de sus sensores y lo escupo por el puerto USB (pendiente de publicar acá, ya se viene, en breve, NO seas ansiosa!)
  • Conecto un router SOHO al que previamente le reemplazamos el firmware con una distro de GNU/Linux llamada OpenWRT.
  • Programamos un script python dentro del router, que agarre cada tanto lo que reciba por USB y lo vaya agregando a un archivo JSON con la estructura que necesita xively (ver en el sitio oficial)
  • Cada otro tanto, mas espaciado que el tanto previo, ejecutamos un script bash que usa curl para subir el bloque de datos a xively

Por último, supongamos que me creés que todo esto anda 20 puntos!

Y ahora sí, lo que todos estábamos esperando:

Te encontrare donde pueda,
me llevarás hasta el cielo,
perdurarás en el aire
mientras te vuelvo un sueño

De certificados y esas rarezas humanas

Y ya en el ocaso del mes rasguño la mitad de las obligaciones autoimpuestas. El tiempo últimamente se me escurre como agua entre los dedos de los pies, sin embargo acá estoy, firme como un soldado, cumpliendo, aún con los ojos llenos de sueño.

En base a una detallada observación del reino animal, puedo decir que la raza humana se diferencia del resto de los animales en su obsesión por los certificados. Quiere una constancia de cada charla que presenció, ya sea sobre las similitudes entre un dogo argentino y el tema Cantata de Puentes Amarrillos de Luis Alberto, y no te das una idea de la cantidad de mails que son capaces de mandarte pidiéndote dicho “pelpa”, mas aún si les dijiste que se los ibas a dar. Ay, ay, ay, yo y mi bocota! (estimado lector, si ud. se siente agraviado, dese cuenta que es un chiste y sea feliz).

Así que bueno, 72 gentes (saludos a Corrientes!) querían un certificado por haber asistido al Festival de instalación de software libre mas grande del mundo, el FLISOL, y para darle lo que ellos querían, robé y usé código de por ahí, mas algo de lo que ya mostré otras veces en este hermoso lugar que tan a menudo nos encuentra. La receta fue la siguiente:

Ingredientes

  • Archivo CSV con 4 columnas: nombre, apellido, email, mas un campo que dice si quiere certificado (si/No)
  • Plantilla svg para el certificado
  • Código certg de Facundo Batista para generar certificados
  • Código desarrollado anteriormente para mandar emails

Preparación

Aprovechando nuestra inmensa capacidad en las artes pictóricas, hacemos una plantilla con inkscape poniendo una etiqueta para que sea reemplazada con el nombre y apellido de la persona:  {{name}}

El código certg va a generar un pdf poniendo en esa etiqueta los nombres que lea de un archivo yaml. Entonces, para no modificar ese código, hacemos un breve script que lea el CSV donde se encuentran todos los datos de las personas que asistieron al evento y los escriba en un nuevo archivo basándose en el yaml base, y obviamente manteniendo el formato. ¿Por qué me decidí por esta alternativa? porque quería aprender a generar y leer yaml. Entonces, ahí va:

# -*- coding: utf-8 -*-
#!/usr/bin/env python3

import yaml
import csv

fh = open('base.yaml', 'rt')
f2 = open('flisol2014.yaml', 'w')

config = yaml.load(fh)

#Columna del apellido y nombre en el archivo csv
col_ape = 0
col_nom = 1

with open('flisol-2014-asistentes.csv', 'rb') as csvfile:
    lista_inscriptos = csv.reader(csvfile, delimiter=';', quotechar='|')
    for inscripto in lista_inscriptos:
        if inscripto[3]=='si':
            config['replace_info'].append({'stopit':'.','name':inscripto[1]+
            ' '+inscripto[0]})

f2.write(yaml.dump(config,encoding=None))

Ahora tenemos en el archivo flisol2014.yaml todo listo para generar los certificados. Dentro de este archivo tenemos que poner el nombre del template svg que creamos antes -entre otros detalles menores-, y finalmente lo ejecutamos así:

./certg.py flisol2014.yaml

Ya está casi todo. Tenemos los 72 certificados, ahora lo que vamos a hacer es crear un script que lea del CSV los emails, nombres y, que adjunte según el nombre de archivo el certificado que corresponda. A esto lo hice del siguiente modo:

# -*- coding: utf-8 -*-
# Importamos libs
import smtplib
import mimetypes
import csv

# Importamos los modulos necesarios
from email.MIMEMultipart import MIMEMultipart
from email.Encoders import encode_base64
from email.mime.text import MIMEText
from email.MIMEBase import MIMEBase
from time import sleep
from string import Template

# Autenticamos
mailServer = smtplib.SMTP('smtp.gmail.com',587)
mailServer.ehlo()
mailServer.starttls()
mailServer.ehlo()
mailServer.login("TUMAIL@mail.com","TUPASS")

sms = Template(
"""Estimad@ $nombre,

Le enviamos el certificado de asistencia por haber participado en el
Festival Latinoamericano de Instalación de Software Libre realizado en la
ciudad de Santa Fe.

Le pedimos disculpas por el retraso y principalmente le agradecemos por
apoyar el FLISOL!

Le informaremos sobre futuros eventos relacionados con el software y la cultura libre.

Atte.

Grupo de Usuarios de Linux del Litoral (LUGLi).
http://lugli.org.ar
http://flisolsantafe.org.ar
"""
)

inscriptos_file = 'flisol-2014-asistentes.csv'
col_ape = 0
col_nom = 1
col_mail = 2
col_cert = 3

with open(inscriptos_file, 'rb') as csvfile:
    lista_inscriptos = csv.reader(csvfile, delimiter=';', quotechar='|')
    for inscripto in lista_inscriptos:
        if inscripto[col_cert] == 'si':
            ape = inscripto[col_ape]
            nom = inscripto[col_nom]
            mailto = inscripto[col_mail]

            msg = MIMEMultipart()
            msg['From'] = "Emiliano López"
            msg['To'] = mailto
            msg['Subject'] = "Certificado FLISOL 2014"
            msg.attach(MIMEText(sms.substitute({ 'nombre': nom})))

            # adjunta certificado
            distinct = (nom+ape).lower().replace(" ", "")
            cert_fname = "certificate-{}.pdf".format(distinct)
            attachFile = MIMEBase('application', 'pdf')
            attachFile.set_payload(file(cert_fname).read())
            encode_base64(attachFile)
            attachFile.add_header('Content-Disposition', 'attachment', filename=cert_fname)
            msg.attach(attachFile)

            ## Enviamos
            mailServer.sendmail("TUMAIL@mail.com", mailto, msg.as_string())
            print mailto+"    "+cert_fname
            sleep(4)

# Cerramos conexion
mailServer.close()

Al cabo de unos minutos, nos empiezan a responder agradeciendo por el certificado recibido, y claro, no es para menos, fijate que lindura el certificado que les hice:
certbillAhora sí, lo que todos estábamos esperando, el tema que bautizó al primer torneo de la liga de partidos al diome:

Invitando al FLISOL 2014 Santa Fe desde Python

Esto tenía que salir rápido por lo que es muy probable que la solución implementada en esta publicación esté lejos de ser la mejor. Pero andaba con poco tiempo para indagar, con cansancio para mejorar y con dolor de cabeza como para renegar. Contundencia y pragmatismo era lo que necesitaba.

El tema es el siguiente, tengo en un archivo CSV información sobre los asistentes al FLISOL del año pasado, entre esos datos está su email, nombre y apellido. Es una lista de 137 inscriptos y quería invitarlos usando un método más automático que escribirle a uno por uno a manopla, entonces necesitaba lograr un email algo amigable, que incluya su nombre en el contenido, el mensaje y que además adjunte la hermosa imagen del FLISOL 2014.

Bien, qué hago? simple, parseo el archivo extrayendo email, nombre y apellido, agarro un mensaje de invitación que escribí en el mismo programa, reemplazo ciertas etiquetas con el nombre y apellido, adjunto ese texto y la imagen al email y mando usando mi misma cuenta de gmail.

Itero hasta llegar al final y listo el pollo. Así sería la cosa:

# -*- coding: utf-8 -*-
# Importamos libs
import smtplib
import mimetypes

# Importamos los modulos
from email.MIMEMultipart import MIMEMultipart
from email.MIMEImage import MIMEImage
from email.Encoders import encode_base64
from email.mime.text import MIMEText
# para esperar y para reemplazar el nombre y apellido
from time import sleep
from string import Template

# Adjuntamos Imagen
file = open("FLISOL2014SFe.png", "rb")
attach_image = MIMEImage(file.read())
attach_image.add_header('Content-Disposition', 'attachment; filename = "FLISOL2014SFe.png"')

# Autenticamos con gmail
mailServer = smtplib.SMTP('smtp.gmail.com',587)
mailServer.ehlo()
mailServer.starttls()
mailServer.ehlo()
mailServer.login("ACA_EMAIL@gmail.com","ACA_TU_PASS")

# Texto del mail para reemplazar luego nombre y apellido
sms = Template("""Estimad@ $nombre $apellido,

Le enviamos una especial invitación a la nueva edición del
Festival Latinoamericano de Software Libre a realizarse en la
ciudad de Santa Fe.

Este año el evento se llevará a cabo en la Facultad de Ingeniería Química - UNL,
ubicada en Santiago del Estero 2829.

Inscripción y más información en http://flisolsantafe.org.ar .
Esperamos contar con su presencia!!!

Atte.
Grupo de Usuarios de Linux del Litoral (LUGLi).
http://lugli.org.ar""")

f = open("Flisol2013_participantes.csv",'r')
# Leemos todas las lineas
lines = f.readlines()
i = 0
for l in lines:
    i += 1
    dat = l.split(';')
    ape = dat[2]
    nom = dat[3]
    mailto = dat[4]
    # armamos el mail
    msg = MIMEMultipart()
    msg['From'] = "Emiliano López"
    msg['To'] = mailto
    msg['Subject'] = "FLISOL 2014 en Santa Fe"
    # reemplazo el nombre y apellido por lo leido
    msg.attach(MIMEText(sms.substitute({ 'nombre': nom, 'apellido': ape})))
    msg.attach(attach_image)

    # Enviamos
    mailServer.sendmail("TU_MAIL@gmail.com", mailto, msg.as_string())
    print str(i)+" "+mailto+"    ok"
    sleep(2)

#cierra el archivo
f.close()

# Cerramos conexion
mailServer.close()

Ahora me quedo sentado a esperar que se llene de personas la FIQ y desparramar software libre a troche y moche!

Ahora sí, lo que todos estábamos esperando:

titi sigue siguiendo

Tengo cinco minutos tengo.

No me olvidé del desarrollo de Titi, y si bien aún no largué oficialmente una nueva versión, en la rama de desarrollo (lo que se llama trunk) hay varios cambios que en breve pretendo darles el golpe de gracia.

Ahora sos canción decía el socio de Ricardo Mollo en Pepe Luí. Me gusta esa mirada de pretender la vida eterna de alguien a partir de una canción. De ahí que quise hacer algo parecido, en vez de canciones lo que yo hago son programas (además de lujos en una cancha). Entonces me dije, “hacete un homenaje  para tu abuela y ponele su nombre a un software, no seas quedado”, de ahí que nació Titi, así que mi abuela perdurará en forma de bits (perdoname titi, es lo mejor que encontré!) mientras dure Internet, que supongo tiene para unos meses más.

Pero no termina ahí la cosa, además de convertirla en bits le puse licencia Libre, es GPL 3, con lo cual nadie lo va a poder cerrar, y se va a poder multiplicar ad eternum. Hey!, vos que estás ahí, sí, a vos te hablo, si cuando muero me hacés un software, hacelo GPL, tamos? y si puede ser en Python mejor.

Ya se cumplieron los cinco minutos, acá va un video de las últimas funcionalidades implementadas, además de una interfaz renovada!!

Los 24 de Marzo

 

Me preguntaba cómo habrán sido las tapas de los diarios argentinos a lo largo de la historia para los 24 de marzo, por supuesto desde 1976 al día de la fecha.

Como ya había estado descargando anteriormente todas las tapas de la mayor corporación mediática de Argentina con serios tintes mafiosos, me dije, “vamos a compararlos con otros”.

Sería interesante ver qué publicaban en tapa y contrastar a Página 12, Clarin y La Nación.

Acá me encontré con una serie de complicaciones que no esperaba. Primero, lo único que tenía resuelto antes de arrancar ya no funcionaba, el gran diario argentino había cambiado el modo de acceder a sus tapas, con lo cuál tenía que empezar de cero.

Lamentablemente el diario conservador y ultra-católico de los Mitre no publica un registro de sus tapas, al menos, luego de haber recorrido su sitio web no pude encontrarlas.

Por lo tanto me quedaba ver si el Página 12 tenía un modo de acceder a sus tapas y ya olfateaba que esto iba a ser poco menos que un parto. Por suerte logré encontrar cómo acceder a las imágenes, sin embargo es un diario bastante nuevo comprarado con los otros, existe desde 1987, y el acceso a sus tapas data desde el 1998.

Entonces, hagamos lo que se puede, vamos a comparar las tapas de cada 24 de Marzo desde 1998 a 2013, ya que clarín no permite el acceso a la última tapa, aunque supongo que la mayoría intuirá la relevancia que tuvo en la portada este nuevo aniversario del golpe de estado.

La solución a este breve desafío se puede resumir en lo siguiente:

  • Descargar las tapas de cada uno de los diarios en el rango de fechas.
  • Acomodar el tamaño para que sean parecidos, lo mejor posible, porque tampoco siempre tienen el mismo tamaño.
  • Unir las dos tapas en una imagen.

Para resolver el primer item tuve que analizar en cada sitio web cuál es el patrón de las URLs para acceder a las tapas. En clarín es muy fácil el acceso, la url que hay que armar es la siguiente:

http://tapas.clarin.com/tapa/2013/03/24/20130324_thumb.jpg

Donde la fecha hay que armarla haciendo y + “/” + m + “/” + d + “/” + y + m + d, y finalmente concatenarla a “http://tapas.clarin.com/tapa/” y agregándole al final “_thumb.jpg”.

En Página el acceso a las tapas es un verdadero lío, sobretodo en los primeros años de su creación. El nombre de archivo no es siempre el mismo, varían anárquicamente entre: tapan.jpg, Tapan.jpg , TAPAN.JPG, tapagn.jpg y TAPAGN.JPG. Además de esto, a veces se almacenan en

http://www.pagina12.com.ar/fotos/”+fecha+”/diario/”+filename

donde fecha es algo así: y + “/” + y[2] + y[3] +”-“+ m +”/”+ y[2] + y[3] +”-“+ m +”-“+ d

Y otras veces están en http://www.pagina12.com.ar/”+fecha+”/”+filename donde fecha es y+m+d.

Entonces, todo esto en un script python en el que para resolver esta última anarquía comento a manopla las urls, fecha y filename donde buscar (ejemplo 1 y ejemplo 2 en el código).

from urllib2 import Request, urlopen, URLError, HTTPError

def get_tapa(diario,file_mode,y,m,d):

    if diario == "clarin":
        # Armo la url de clarin
        fecha = y + "/" + m + "/" + d + "/" + y + m + d
        filename = "_thumb.jpg"
        url = "http://tapas.clarin.com/tapa/"+fecha+filename
    elif diario == "pagina12":
        # Armo las urls de pagina12

        # 1 - Ejemplo: http://www.pagina12.com.ar/2000/00-03/00-03-24/tapan.jpg
        fecha = y + "/" + y[2] + y[3] +"-"+ m +"/"+ y[2] + y[3] +"-"+ m +"-"+ d
        filename = "tapan.jpg"
        url = "http://www.pagina12.com.ar/"+fecha+"/"+filename

        # 2 - Ejemplo: http://www.pagina12.com.ar/fotos/20130324/diario/
        # El filename puede ser:
        #                tapan.jpg Tapan.jpg TAPAN.JPG
        #                tapagn.jpg TAPAGN.JPG
        #filename = "TAPAGN.JPG"
        #fecha = y+m+d
        #url = "http://www.pagina12.com.ar/fotos/"+fecha+"/diario/"+filename

    # Hago el request
    req = Request(url)

    try:
        # Abro url
        f = urlopen(req)
        print "downloading " + url

        # Armo el nombre de archivo usando la fecha y el filename
        local_file = open("tapas_24Marzo/"+diario+"/"+diario+ y +"-"+ m +"-"+ d +"-"+filename[:-4]+".jpg", "w" + file_mode)

        # Escribo el archivo localmente
        local_file.write(f.read())
        local_file.close()		

    # handle errors
    except HTTPError, e:
        print "HTTP Error:",e.code , url

    except URLError, e:
        print "URL Error:",e.reason , url

if __name__ == "__main__":

    print "starting..."

    # Fecha inicio y final
    y,m,d = "1974", "03", "24"
    y_fin = "2014"

    # Itero entre el intervalo
    for a_year in range(int(y),int(y_fin)+1):
    	get_tapa("clarin","b",str(a_year),m,d)
    	get_tapa("pagina12","b",str(a_year),m,d)

Las imágenes, como era de esperar tienen diferente tamaño y no siempre el mismo, las de clarín son un poco más de el doble. Así que voy a hacer un script en bash usando imagemagick para redimensionar todas imágenes a un 45% de su tamaño original. Sería:

#!/bin/bash

for i in $(ls tapas_24Marzo/clarin/*.jpg)
do
	convert $i -resize 45% tapas_24Marzo/clarin/half/${i:21}
	#echo tapas_24Marzo/clarin/half/${i:21}
done

Ahora queda hacer otro script para unir en una imagen las dos tapas. Esto no lo generalizo porque no le puse un mismo nombre de archivo a los de Página, tuve la mala idea de dejarle el nombre original, que como les decía es un caos. Sin embargo, es una tarea que no reviste mayor complejidad para hacerlo manualmente:

convert +append clarin2013-03-24.jpg pagina122013-03-24-tapan.jpg 2013-03-24_clarin-pagina12.jpg

Al inicio del post está el álbum comparativo, pero ya que estamos muestro las tapas de clarin de los 24 de Marzo desde 1974 al 2013, salvo algunas que sospechosamente no son accesibles. Estas son:

 

Cualquier análisis sobre el contenido, por mencionar alguno, en 1976 se tituló “NUEVO GOBIERNO”, lo dejo para charlas personales. Por último, me encanta la pluma de José Pablo Feinmann, y me encontré con un artículo en la contratapa del Página de 2001 que les recomiendo.

Titi 0.1.0 is out!

Estoy empezando a escupir mi código a la web. Y el primer escupitajo tiene que ver con un software hecho en dos fines de semana de noches mas que largas, se llama Titi y de acá se lo pueden bajar.

¿Qué es Titi?

Es un visualizador de imágenes satelitales o de radar multibanda que soporta una inmensa cantidad de formatos (mas de 100). Además permite extraer (por ahora sólo para imágenes georeferenciadas en la cabecera) valores de la imagen a partir de una latitud y longitud ingresada, así como también obtener el valor de la imagen a partir de un click sobre la misma.

Oficialmente titi es un acrónimo recursivo rebuscado de Titi Is a saTellital Image viewer, pero ciertamente es un homenaje a una de mis abuelas. Acá les muestro un par de capturas de pantalla de titi en acción mostrando imágenes ASTER GDEM v2 (Advanced Spaceborne Thermal Emission and Reflection Radiometer Global Digital Elevation Model Version 2) con diferentes mapas de colores.

titi0.1.1-ncartiti0.1.0

Características

– Soporte para un inmensa cantidad de formatos gracias al uso de Geospacial Data Abstraction Library (GDAL).
– Extracción de valores a partir de una latitud y longitud dada.
– Extracción de valores a partir de clickear sobre el pixel deseado.
– Soporte para diferentes mapas de color.
– Soporte para imágenes multibanda.

 ¿Cómo está hecho?

Obviamente está programado en Python. Para la interfaz gráfica usé WxPython. Para leer las imágenes usé PyRaster que es  wrapper de python-gdal (Python bindings to the Geospatial Data Abstraction Library) que me las devuelve a las imágenes en formato de matrices NumPy, por lo que usé NumPy y finalmente lo visualizo usando la librería gráfica Matplotlib.

Tenía desarrollado para uso propio un visualizador en consola (ShowSatImg), así que me basé en eso y le agregué todas esas pavaditas gráficas usando como base un template de http://www.oneminutepython.com. Como verán soy degenerado a la hora de hacer interfaces gráficas, es más las detesto, pero debo reconocer que para que sea usada por colegas, amigos, etc, hay que escaparle a los scripts en consola.

¿Cómo sigue esta historia?

Voy a reescribirla desde cero, pero así como está ya es funcional, por lo que pueden bajarla y probarla. Hasta ahora hice todo bien a pata, lo que me permitió comprender mejor el tema de las GUI del #$%$, pero ahora pretendo usar algún entorno que me facilite las tareas de diseño.

To Do List

– Soporte para operaciones matemáticas sobre distintas bandas.
– Operaciones matemáticas predefinidas (NDVI, NDWI, etc.).
– Acciones masivas sobre un conjunto de imágenes seleccionadas (recorte, extracción, etc).
– Soporte para georeferenciar imágenes cuya información no se encuentra en la cabecera.
– Visualización 3D usando mayavi.
– …

 

Juego de la vida

Dicta wikipedia: El juego de la vida es el mejor ejemplo de un autómata celular, diseñado por el matemático británico John Horton Conway en 1970.

No se bien por qué me atraen bastante estos jueguitos, debe ser porque (acá viene un razonamiento serio) leí de muchos laburos con los que logran imitar comportamientos muy complejos con reglas realmente simples, tales como la infección de células con HIV en el organismo humano, o la dinámica de un incendio  o también debe ser (y acá viene la boludez) porque ya terminé de ver todas las temporadas de Capusotto.

El tema surge porque hace un par de días un alumno me consultó sobre este ejercicio y lo planteamos medio por arriba, más por lo laborioso que por su complejidad. Así que, para evitar que me atormente la culpa y la angustia, me repartí la siesta del sábado y del domingo para programarlo en Python y, de paso para hacer unas animaciones y comprender mejor el comportamiento de estos bichos. Además, lo plasmo acá debido al incentivo de un ex profesor y ex jefe que a veces lee este estático blog, Gracias Pablo!

Entonces, el juego consiste en un conjunto de bichos vivos o muertos cuyo estado cambia en el tiempo (de generación en generación) dependiendo de lo que haya a su alrededor, es decir, varía en función de su estado (vivo o muerto) y de sus vecinos.

Las reglas de evolución son la siguientes:

  • Una célula muerta con exactamente 3 vecinas vivas => vive
  • Una célula viva con mas de 3 vecinas vivas => muere
  • Una célula viva con 2 o 3 vecinas vivas => vive
  • Una célula viva con menos de 2 vecinas vivas => muere

Desde el punto de vista de su implementación, lo hice del siguiente modo:

  • La población de células es una matriz de mxn, cuyo valor es 1 o 0 (viva o muerta respectivamente) que se inicia en forma aleatoria.
  • Los vecinos son los que están alrededor, donde hay que tener en cuenta que no todos tienen la misma cantidad de vecinos:
  • Las cuatro células de las esquinas tienen 3 vecinos.
  • Las del borde sin contar las esquinas tienen 5.
  • Las del interior están rodeadas, por lo que se debe controlar el estado de las 8 células vecinas.

Entonces, teniendo en cuenta estos detalles e iterando sobre la cantidad de generaciones para observar su evolución nos queda un hermoso código de aproximadamente 100 líneas:

import numpy as np
import matplotlib.pyplot as plt
import scitools.std as sci

def nuevo_estado(celula_viva,vecinos):
    ''' Reglas de evolucion:
        - celula  muerta  con  exactamente 3 vecinas vivas => vive
        - celula viva con mas de 3 vecinas vivas           => muere
        - celula viva con 2 o 3 celulas vecinas vivas      => vive
        - celula viva con menos de 2 vecinas vivas         => muere
    '''
    vive = 1
    muere = 0
    estado = celula_viva.copy()
    vecinas_vivas = sum(vecinos)

    if celula_viva:
        # celula viva => contemplo solo los casos en los que muere
        if vecinas_vivas > 3:
            estado = muere
        elif vecinas_vivas < 2:
            estado = muere
    else:
        # celula muerta
        if vecinas_vivas == 3:
            estado = vive
    return estado

def main(m,n,generaciones):
    # mi semilla
    np.random.seed()

    poblacion = np.random.randint(2,size=(m,n))
    gen_tempo = np.zeros((m,n), int)

    celulas_vivas = []
    celulas_vivas.append(np.sum(poblacion))
    # BW
    plt.imshow(poblacion, cmap='binary', interpolation='nearest')
    # Grey
    #plt.imshow(poblacion, cmap='binary')
    counter = 1
    plt.savefig('img/tmp%04d.png' %counter)
    #plt.show()

    for g in range(1,generaciones+1):
        # ------------------------------
        # Hay 3 casos para especiales:
        # ------------------------------
        # 1- En las esquinas de la matriz

        # -- Superior e infrerior izquierda
        vecinos = np.array([poblacion[0,1], poblacion[1,1], poblacion[1,0]])
        gen_tempo[0,0] = nuevo_estado(poblacion[0,0],vecinos)

        vecinos = np.array([poblacion[m-2,0], poblacion[m-2,1], poblacion[m-1,1]])
        gen_tempo[m-1,0] = nuevo_estado(poblacion[m-1,0],vecinos)

        # -- Superior e inferior derecha
        vecinos = np.array([poblacion[0,n-2], poblacion[1,n-2], poblacion[1,n-1]])
        gen_tempo[0,n-1] = nuevo_estado(poblacion[0,n-1],vecinos)

        vecinos = np.array([poblacion[m-1,n-2], poblacion[m-2,n-2], poblacion[m-2,n-1]])
        gen_tempo[m-1,n-1] = nuevo_estado(poblacion[m-1,n-1],vecinos)

        # 2- En la matriz sin los bordes
        for i in range(1,m-1):
            for j in range(1,n-1):
                vecinos = np.array([poblacion[i-1,j-1],poblacion[i-1,j],poblacion[i-1,j+1,],
                                    poblacion[i,j-1],poblacion[i,j+1],
                                    poblacion[i+1,j-1],poblacion[i+1,j],poblacion[i+1,j+1]])
                gen_tempo[i,j] = nuevo_estado(poblacion[i,j],vecinos)

        # 3- En los 4 bordes sin las esquinas
        for i in range(1,m-1):
            # Columna izquierdo
            vecinos = np.array([poblacion[i-1,0],
                                poblacion[i-1,1],poblacion[i,1],poblacion[i+1,1],
                                poblacion[i+1,0]])
            gen_tempo[i,0] = nuevo_estado(poblacion[i,0],vecinos)
            # Columna derecha
            vecinos = np.array([poblacion[i-1,n-1],
                                poblacion[i-1,n-2],poblacion[i,n-2],poblacion[i+1,n-2],
                                poblacion[i+1,n-1]])
            gen_tempo[i,n-1] = nuevo_estado(poblacion[i,n-1],vecinos)

        for i in range(1,n-1):
            # Fila superior
            vecinos = np.array([poblacion[0,i-1],
                                poblacion[1,i-1],poblacion[1,i],poblacion[1,i+1],
                                poblacion[0,i+1]])
            gen_tempo[0,i] = nuevo_estado(poblacion[0,i],vecinos)
            # Fila inferior
            vecinos = np.array([poblacion[m-1,i-1],
                                poblacion[m-2,i-1],poblacion[m-2,i],poblacion[m-2,i+1],
                                poblacion[m-1,i+1]])
            gen_tempo[m-1,i] = nuevo_estado(poblacion[m-1,i],vecinos)

        poblacion = gen_tempo.copy()
        plt.imshow(poblacion, cmap='binary', interpolation='nearest')
        #plt.imshow(poblacion, cmap='binary')
        plt.savefig('img/tmp%04d.png' %(counter+g))
    sci.movie('img/tmp*.png', output_file='img/juegoDeLaVida_'+str(m)+'x'+str(n)+'_'+str(generaciones)+'gen.gif')

if __name__ == "__main__":
    filas = 100
    columnas = 100
    generaciones = 200
    main(filas,columnas,generaciones)

Y bueno, el gif generado con el código previo, para una población de 100×100 y una evolución de 200 generaciones (me siento Darwin) se puede observar haciendo click sobre la siguiente imagen.

juegoDeLaVida_100x100_200genBW

Para la visualización usé matplotlib, para la animación scitools, y el laburo de arreglos usando numpy. Nada del otro mundo. Solo le faltaría una interfaz gráfica de usuario, con botoncitos, cuadritos, florcitas y esas yerbas, pero la verdad, detesto programarlas, ya subiré el código a un repositorio público (estoy laburando en eso) para que quien quiera lo modifique o sino que lo pida y capaz lo agarro con más ganas.

Por último, recomiendo hacer como ejercicio inicial este juego para aprender a programar, tiene un mix de todo sin alcanzar una complejidad elevada, y además, una vez concluido permite filosofar sobre la evolución de las especies, los límites del universo y por qué no hay canchas de futbol 5 con el nombre de Messi todavía, todo eso con buen vinito de por medio.

Tratame bien!

Tratar imágenes satelitales suele obligarnos a utilizar paquetes de software propietarios (cerrados!) desarrollados para tal fin. Estas aplicaciones suelen tener una universo de funcionalidades tan inmenso que no solo marean al usuario inexperto (como yo) sino que para tareas muy específicas termina siendo más complejo adaptarnos al “bendito” programa que resolver el problema en cuestión.

Si bien ya soy bastante manso, un dejo de rebeldía adolescente y la inquietud de una divina amiga franchuto-argentina me llevó a indagar otro tipo de solución.

Por suerte existe Python. Un lenguaje en el que se pueden hacer muchas cosas respetables, sabiendo muy poco del mismo (bue, tampoco es que yo soy muy respetuoso). El tema es que usando PyRaster -una colección de herramientas basadas en Python- se puede procesar eficientemente imágenes raster. Esta herramienta usa la Librería de Abstracción de Datos Geoespaciales GDAL (Geospatial Data Abstraction Library) para leer/escribir alrededor de 100 formatos de imágenes y tratarlos como una matriz estándar, por lo que, independientemente del formato se utilizan los mismos procesos.

Instalación

Descargar el módulo PyRaster de acá y luego importalo desde el programa principal.

Dependencias

-No me digás que tenés que tener instalado Python?
-Obvio querid@, (probado con la versión 2.6 sin problemas), pero además se debe instalar:

  • Numerical Python: Numpy 1.2.1 or greater
  • Pyhon bindings for GDAL: python-gdal 1.5.4 or greater

En debian/ubuntu:

#aptitude install python-numpy python-gdal

Aprendiendo a leer y escribir

En el siguiente código se lee la imagen prueba0.img (una imagen Erdas Imagine .img de formato HFA) y se obtienen los metadatos (header o información general sobre la imagen), así que los valores de todos los píxeles de la banda/layer 1 de la imagen en forma de matriz. Al final, y se muestra en pantalla el pixel de la fila 0, columna 1.

import rasterIO

file_pointer = rasterIO.opengdalraster(‘prueba0.img’)
driver, XSize, YSize, proj_wkt, geo_t_params = rasterIO.readrastermeta(file_pointer)
kelvin_band = rasterIO.readrasterband(file_pointer, 1)
print kelvin_band[0][1]

Y bue, así se hace eso que dije. Justo iba a escribir algo trascendental pero me acabo de quedar sin tinta.

Matrix revolutions

Si cuando era un puber con excesivo brillo en el rostro alguien me decía que laburar con matrices iba a estar relacionado con informática, hubiese levantado sutilmente mis hombros con mi mejor cara de “oliendo excremento”.

El tema es que las matrices andan por todos lados. En primer año de la carrera uno las agarraba, las manoseaba (gauss-jordan), las daba vuelta (inversión), las tumbaba (transposición) y un sin fin más de operaciones (casi todo lo que indica el libro hindú) sin tener real dimensión de cuán involucradas estaban en el mundo.

No solo fotos o imágenes satelitales, cualquier problema que involucre varias incógnitas y ecuaciones termina idefectiblemente en una matriz a resolver (Ax=b).

El mundo gnulinuxero nos ha bien-acostumbrado a buscar, renegar, probar, indagar una y otra vez en un ciclo casi infinito (casi infinito = infinito) hasta alcanzar la solución a nuestros problemas. Así es que uno presupone que al transitar un nuevo camino se topará con decenas de obstáculos hasta alcanzar lo buscado.

Ah Si?

NO!
No fue así, en el primer intento ya estaba resolviendo un simple sistema matricial utilizando rutinas REcontraREmilREprobadas y que por años luz serán más eficientes de las que pueda llegar a programar yo en mis mejores sueños. Asi es como puse a buscar las librerías BLAS y LAPACK para que hagan el laburo por mi o mis compañeros.

Lo primero que vamos a hacer es instalarlas, luego algún lenguaje para usarlas (por ahora solo en fortran) y finalmente un ejemplo para probar que la cosa ande.

Instalación BLAS+LACPACK

#aptitude install libblas3gf libblas-doc libblas-dev liblapack3gf liblapack-doc liblapack-dev

Instalación gfortran

#aptitude install gfortran

Ejemplito a resolver

  <pre>\begin{bmatrix}  3.1 & 1.3 & -5.7 \\  1.0 & -6.9 & 5.8 \\  3.4 & 7.2 & -8.8  \end{bmatrix}  \begin{bmatrix}  x_1 \\  x_2 \\  x_3  \end{bmatrix}  =  \begin{bmatrix}  -1.3\\  -0.1\\  1.8  \end{bmatrix}</pre>

Programita a compilar (test1.f)

c solving the matrix equation A*x=b using LAPACK
	Implicit none
c declarations, notice single precision
	Real*4 A(3,3), b(3)
	integer i, j, pivot(3), ok
c define matrix A
	A(1,1)=3.1
	A(1,2)=1.3
	A(1,3)=-5.7
	A(2,1)=1.0
	A(2,2)=-6.9
	A(2,3)=5.8
	A(3,1)=3.4
	A(3,2)=7.2
	A(3,3)=-8.8
c define vector b, make b a matrix and you can solve multiple
c equations with the same A but different b
	b(1)=-1.3
	b(2)=-0.1
        b(3)=1.8
c find the solution using the LAPACK routine SGESV
	call SGESV(3, 1, A, 3, pivot, b, 3, ok)
c
c parameters in the order as they appear in the function call
c    order of matrix A, number of right hand sides (b), matrix A,
c    leading dimension of A, array that records pivoting,
c    result vector b on entry, x on exit, leading dimension of b
c    return value
c
c print the vector x
	do i=1, 3
	   write(*,*) b(i)
	end do
	end

Compilación

$gfortran -lblas -llapack test1.f -o test1.bin

Ejecución y resultado

$./test1.bin
1.0000001
1.0000004
1.0000004

Para mayor información: busque más información!!

C’est fini

 

Social Widgets powered by AB-WebLog.com.

Social Widgets powered by AB-WebLog.com.

Social Widgets powered by AB-WebLog.com.