Últimos Cambios |
||
Blog personal: El hilo del laberinto |
Última Actualización: 12 de enero de 2013
A.J.I.V. (Asociación Juvenil para la Informática Viguesa) fue una asociación fundada en 1988, con el fin de promover la informática, a un nivel técnico. La asociación tuvo bastante relevancia en su momento. Por ejemplo, fuimos miembros del consejo de la juventud de Galicia.
Fieles a ese compromiso técnico, en 1990 empezamos a elaborar una revista en papel, gracias a subvenciones oficiales. Lamentablemente la revista no tenía una periodicidad fija. Simplemente íbamos acumulando artículos hasta que completábamos cada número. Tampoco conseguimos nunca tener un grupo fijo de colaboradores.
Por supuesto, el nivel técnico de la revista, hoy en día, no es muy alto, pero hay que ser conscientes del contexto histórico: antes de Internet y realizada por gente joven autodidacta sin titulación universitaria, con recursos informáticos domésticos.
A pesar de todo se llegaron a publicar nueve números, interesantes por la perspectiva histórica y la precariedad de los medios puestos en juego.
La asociación se cerró oficialmente en 2001, tras un largo período de inactividad. Al ser una asociación juvenil, es preciso que los cargos directivos estén en manos de personas menores de treinta años, y no conseguimos una renovación generacional. Lástima.
Las revistas se hicieron con ordenadores Atari ST y el software Calamus. Imprimíamos con una impresora de inyección de tinta HP DeskJet 500 (pionera en su época).
Años después (2003) discutimos la posibilidad de recuperar las revistas para el mundo digital.
La primera idea era utilizar una versión moderna de Calamus para leer las revistas, y generar PDFs a partir de ellas. Pero no conseguimos encontrar a nadie que tuviese el soft y que pudiese probarlo con nosotros. En 2012 Simón probó la versión moderna de Calamus para PC y los resultados fueron bastante buenos, pero nos encontramos con el problema de recuperar los ficheros informáticos originales, como describo más abajo, además de verse algunas inconsistencias en el formato.
Nuestra segunda idea fue utilizar el software original en un emulador, imprimiendo en una HP DeskJet 500 virtual. El emulador genera un fichero con el volcado de los comandos de la impresora. Un programa en Python, funcionando en nativo (Linux), cogía ese volcado de impresora e interpretaba los códigos de la HP para generar un PNG, sin pérdida de calidad.
La prueba de concepto del decodificador es: (basado en Hewlett-Packard Laser and DeskJet Printer Escape Code Sequences)
#!/usr/bin/env python from PIL import Image a = open("/tmp/hp.prn").read() im = Image.new("1", (1000, 1000), 255) secuencia = "\\x1b*b0052W" p = 0 y = 0 while True : p = a.find(secuencia, p) if p == -1 : break p += len(secuencia) v = a[p:p+52] for i,j in enumerate(v) : x = i*8 j = ord(j) for dummy in xrange(8) : if j&128 : im.putpixel((x,y), 0) j *= 2 x += 1 y += 1 im.show()
El problema ahora era localizar y leer los discos originales. Alfonso, Pablo y yo invertimos para ello medio verano. Las revistas se conservaban en diskettes de 3.5 pulgadas que, hoy en día, son prácticamente ilegibles (tanto por no disponer de lectores compatibles como porque el propio soporte físico está degradado), o faltan componentes necesarios como tipos de letra, etc. Algunas revistas faltaban por completo, y otras eran borradores inacabados, no la versión final que finalmente se publicó.
Tras intentar localizar todos los diskettes posibles, nos dimos por vencidos. Esta opción era la que proporcionaría mejor calidad final, pero no era viable.
Yo, como editor y coleccionista impenitente, conservaba varias copias en papel de las revistas originales. En la mayoría de los casos, incluso los "masters" -sacados por impresora- que usábamos para fotocopiar las versiones finales. Alfonso y yo invertimos algo de tiempo en las navidades de 2010 escaneando algunas páginas y haciendo pruebas para la conversión a dos niveles de color (blanco y negro, sin grises). Los resultados eran buenos, pero la operación muy manual (fijar el umbral de corte se hacía página a página, a mano). Tras las navidades yo volví a Madrid y Alfonso se dedicó a escanear todas las revistas y pasarlas a blanco y negro, mientras yo escribía el software para generar los PDFs.
El siguiente verano comparamos notas, y la conversión a blanco y negro manual no nos acababa de convencer. Se perdían detalles y la calidad no era homogénea. Tras investigar opciones, descubrimos los algoritmos de thresholding. Hice algunas pruebas y acabamos reconvirtiendo otra vez todas las imágenes (que Alfonso había escaneado y conservado en tonos de gris) de nuevo de forma automatizada.
La generación de PDFs se completó en enero de 2012. Y ahora las entregamos al mundo, con una mezcla de vergüenza propia :-) y nostalgia de ver de dónde venimos.
Los scripts toman los PNG originales en tonos de grises, los corta, los gira, los convierte a un bit por píxel (blanco y negro) usando el algoritmo de thresholding "Otsu" y genera un PDF en formato A5.
La idea de usar "Otsu" fue comprobando que el algoritmo de "thresholding" de GIMP es bastante bueno y, mirando el código fuente (¡ventajas del Open Source!) había referencias a dicho algoritmo.
Los scripts para generar estos PDF a partir de los PNG en tonos de grises originales son los siguientes:
#!/usr/bin/env python # Otsu Thresholding # http://www.labbookpages.co.uk/software/imgProc/otsuThreshold.html import sys, math from PIL import Image from reportlab.pdfgen import canvas from reportlab.lib.pagesizes import portrait, A5 from reportlab.lib.utils import ImageReader #reportlab.lib.utils.Image = Image a=canvas.Canvas("z.pdf", pagesize=portrait(A5)) def otsu_1(width, heigh, histogram) : l = width*height umbral = 0 varianza_umbral = 1e+100 for i in xrange(257) : cb = float(sum((v for j,v in enumerate(histogram[:i])))) cw = float(sum((v for j,v in enumerate(histogram[i:], i)))) if (cb == 0) or (cw == 0) : continue wb = cb/l ww = cw/l mb = sum((j*v for j,v in enumerate(histogram[:i])))/cb mw = sum((j*v for j,v in enumerate(histogram[i:], i)))/cw vb = sum(((j-mb)*(j-mb)*v for j,v in enumerate(histogram[:i])))/cb vw = sum(((j-mw)*(j-mw)*v for j,v in enumerate(histogram[i:], i)))/cw varianza = wb*vb + ww*vw if varianza < varianza_umbral : umbral = i varianza_umbral = varianza return umbral # Esta rutina, para mis escaneos, es 2-3 veces mas # rapida, y el umbral calculado es el mismo. def otsu_2(width, heigh, histogram) : l = width*height umbral = 0 varianza2_umbral = -1 for i in xrange(257) : cb = float(sum((v for j,v in enumerate(histogram[:i])))) cw = float(sum((v for j,v in enumerate(histogram[i:], i)))) if (cb == 0) or (cw == 0) : continue wb = cb/l ww = cw/l mb = sum((j*v for j,v in enumerate(histogram[:i])))/cb mw = sum((j*v for j,v in enumerate(histogram[i:], i)))/cw varianza2 = wb*ww*(mb-mw)*(mb-mw) if varianza2 > varianza2_umbral : umbral = i varianza2_umbral = varianza2 return umbral for fichero in sys.argv[1:] : im = Image.open(fichero) im = im.transpose(Image.ROTATE_90).convert("L") histogram = im.histogram() width, height = im.size umbral = otsu_2(width, height, histogram) print fichero, umbral im = Image.eval(im, lambda x : 0 if x<umbral else 255) im = im.convert("1") #im.save("z.png") #im.show() im=im.crop((0,0, width/8*8, height)) im.load() # a.drawImage(ImageReader(im), 0, 0, width=419, height=419*math.sqrt(2), # preserveAspectRatio=True) a.drawInlineImage(im, 0, 0, width=419, height=419*math.sqrt(2), preserveAspectRatio=True) a.showPage() a.save()
Un problema imprevisto fue que los PDF generados eran enormes, mucho más grandes que las imágenes originales, lo que no tenía sentido. Se investigó y se parcheó :-). ¡Open Source!.
Por último, todo este trabajo se presentó en una reunión mensual de Python-Madrid.
Data Bus 1, Abril 1990
Data Bus 2, Diciembre 1990
Data Bus 3, Abril 1991
Data Bus 1, segunda época, Octubre 1991
Data Bus 2, segunda época, Noviembre 1991
Data Bus 3, segunda época, Octubre 1992
Data Bus 4, segunda época, Marzo 1993
Data Bus 5, segunda época, Agosto 1993
Data Bus 6, segunda época, Septiembre 1994
Más información sobre los OpenBadges
Donación BitCoin: 19niBN42ac2pqDQFx6GJZxry2JQSFvwAfS