Con esta nota abro la sección Python, donde escribiré periodicamente artículos sobre programación en este magnífico lenguaje. Hoy veremos lo facil que es utilizar el lenguage Python para realizar procesamiento de imágenes. Haremos un programa para graficar un histograma RGB de una imagen, y ademas generaremos el negativo de una imagen.

Requisitos: Para poder ejecutar los siguientes ejemplos, es necesario tener instalado lo siguiente: ( consular el apendice al final del articulo sobre la instalacion de estas librerias )

  • Python
  • Python Imaging Library
  • Python Tkinter

En este ejemplo, usaremos la libreŕia PIL – Python Imaging Library, que agrega capacidades de procesamiento de imágenes al interprete python. Y usaremos la libreria Tkinter para construir una interfaz grafica rudimentaria a nuestro programa, para mostrar el grafico con el histograma. Nuestro programa será capaz de, por un lado, leer una imagen jpeg (o cualquier otro formato ) en un arreglo en memoria, procesar estos datos para calcular el histograma, y finalmente graficar el histograma de colores. No obstante, el código fuente de nuestro ejemplo es relativamente pequeño.

Para ejecutar el script, simplemente escribimos el nombre del script, seguido del nombre de la imagen que queremos procesar: $ ./drawhistogram imagen.jpg

Aqui un screenshot de nuestro programa graficador de histogramas: Histograma en python

Histograma en Python

#!/usr/bin/python
from Tkinter import *
import sys
import Image
from  math import *

################################################################
# LA APLICACION PRINCIPAL ES UNA CLASE QUE HEREDA DE FRAME (GUI)
class Application(Frame):
    def __init__(self, master=None):
        Frame.__init__(self, master,width=700, height=600)
        self.grid(columnspan=8)
        self.createWidgets()

    ###############################################
    # DRAWHISTOGRAM:EL METODO QUE DIBUJA EL HISTOGRAMA
    def drawHistogram( self ):

        global histogramlist

        # donde esta el punto (0,0)?
        xoffset = 0   #int( self.canvas.winfo_width() / 2 )
        yoffset = int( self.canvas.winfo_height() ) - 100

        stepx = 1
        stepy = 0.05

        # 1 = <escala> cantidad de pixels
        escala = 0.5

        maxX = 768   #xoffset * 2 / escala

        # TRAZAMOS EJE
        xborder = 450
        self.canvas.create_line( 1, 0, 1, yoffset+2, arrow=FIRST )
        self.canvas.create_line( xborder, yoffset+2, 0, yoffset+2, arrow=FIRST )

        # RECORREMOS LA LISTA DE HISTOGRAMA Y VAMOS TRAZANDO ...
        x = 0
        colorstr="black"
        while x < maxX:

            if 0 <= x < 256:
                colorstr = "red"
            if 256 <= x < 512:
                colorstr = "green"
            if 512 <= x < 768:
                colorstr = "blue"

            y = histogramlist[int(x)]

            # el -y es por que en la pc el eje y esta invertido
            c = int(x * escala) + xoffset
            f = int(-y * escala) + yoffset

            self.canvas.create_oval( c, yoffset, c, f, \
               fill=colorstr, outline=colorstr)

            x += stepx

    #######################################################
    # CREATEWIDGETS: METODO CREA LOS CONTROLES GRAFICOS VISUALES (GUI)
    # DE LA APLICACION (BOTONES, LABELS, ETC )
    def createWidgets(self):

        self.quitButton = Button( self,  text="Quit", justify=LEFT,
                                  command=self.quit)
        self.quitButton.grid(column=0,row=0, sticky=W)


	self.drawButton = Button(self, text="histo", justify=RIGHT, command= self.drawHistogram )
	self.drawButton.grid( column=0, row=4)

        self.canvas = Canvas( self, width=700, height=500 )
        self.canvas.grid(row=1, column=1, rowspan=7, columnspan=7 )

        #idBorder = self.canvas.create_rectangle( 5, 5, 500, 500 )

        idText = self.canvas.create_text( 200, 50, font=('verdana', 15, 'bold'),
					text="Histograma Python ", fill="blue")


##########################################
# MAIN ENTRY POINT

#default image filename
imagefilename = "yoyo.jpg"

# si hay argumentos de linea de comandos
# lo tomamos como el nombre de archivo imagen a procesar
# FALTA COMPRABAR QUE EL NOMBRE SEA VALIDO
if len( sys.argv ) > 1:
    imagefilename = sys.argv[1]


im=Image.open( imagefilename )
histogramlist = im.histogram()

app = Application()
app.master.title("Histograma " + imagefilename + " - Histograma en Python")
app.master.option_add('*font', ('verdana', 10, 'bold'))
app.mainloop()

Explicacion del script

El script es un archivo de texto que puede abrirse con cualquier editor de texto. Y puede ejecutarse directamente haciendo doble click ( habiendo instalado el interprete python previamente )

Las partes interesantes del script son el metodo histogram()

histogramlist = im.histogram()

Es un metodo que ya viene en la clase Image de la libreria Python Imaging Library. Para ver todos los metodos disponibles por favor consultar el manual “pil-handbook.pdf”.

histogram() devuelve en el caso de RGB una lista[0..767] donde pone la cuenta de cuantas veces aparace cada tono en la imagen. El ‘arreglo’ se divide en lista[0..255] para los tonos rojos lista[256..511] mapea los tonos 0 a 255 del verde y lista[512..767] mapea los tonos 0 a 255 del azul.

Entonces por ejemplo histogramlist[257] me devuelve el contador del tono 1 (empezando en 0) de los verdes, histogramlist[766] me develve el contador del tono 254 del azul, etc.

Con estos datos lo unico que hice fue dibujar el grafico como puede verse en la funcion drawHistogram.

NOTA: En el script esta mezclado el codigo que correponde al armado de la interfaz de usuario, que no es estrictamente necesario y puede hacer parecer el codigo un poco complicado, pero las partes relevantes con respecto al ‘ploteo’ es la simple invocacion al metodo histogram() y la funcion que dibuja a partir de los datos.

NOTA2: Tambien notar que en el script hice uso del metodo histogram() ya definido en la libreria, pero que no es dificil armar uno mismo la lista con los siguientes datos:

print im.format, im.size, im.mode JPEG (384, 423) RGB

teniendo im.size se puede saber los limites para recorrer la matriz

# obtener el valor de un pixel en particular

im.getpixel((210,220)) (15, 54, 93) obteniendo los valores de cada pixel en particular se puede ir armando la lista del histograma. En este ejemplo, tendriamos red[15] += 1 green[54] +=1 blue[93] += 1

El script es solo un ejemplo, no tiene control de errores ni esta depurado, pero da una buena idea de lo que se puede hacer con python.

NOTA2: Tambien notar que en el script hice uso del metodo histogram() ya definido en la libreria, pero que no es dificil armar uno mismo la lista con los siguientes datos:

print im.format, im.size, im.mode JPEG (384, 423) RGB

teniendo im.size se puede saber los limites para recorrer la matriz

# obtener el valor de un pixel en particular

im.getpixel((210,220)) (15, 54, 93) obteniendo los valores de cada pixel en particular se puede ir armando la lista del histograma. En este ejemplo, tendriamos red[15] += 1 green[54] +=1 blue[93] += 1

El script es solo un ejemplo, no tiene control de errores ni esta depurado, pero da una buena idea de lo que se puede hacer con python.

Uso de la libreria Python Imaging Library

COMANDOS BASICOS

# serie de comandos en el interprete python # para mostrar una imagen.

python image negative

>>> import Image
>>> im=Image.open("yoyo.jpg")
>>> print im.format, im.size, im.mode
JPEG (384, 423) RGB
>>> im.show()

# obtener el valor de un pixel en particular
>>> im.getpixel((10,20))
(254, 254, 254)
>>> im.getpixel((210,220))
(15, 54, 93)

# Para producir el negativo de la imagen cargada: python image negative 2

# para cada pixel hace la resta 255 - pixel # y el resultado lo pone en el objeto imagen out # Se logra el efecto invertir imagen o negativo

out = im.point(lambda i: 255 -i ) out.show()

# Notar el poder de abstraccion de Python. # En el ultimo ejemplo, no es necesario recorrer # la matriz con 2 o 3 fors, estilo lenguaje c, # sino que se usa un iterador que recorre automaticamente # todos los pixeles y se le pasa como parametro la funcion # “al vuelo” que debe operar en cada pixel. # (Claro que tambien se puede recorrer la matriz “a mano” # si uno necesita ).

# Por ejemplo, para multiplicar cada pixel * 1.2 out = im.point(lambda i: i * 1.2 )

# Tambien, notar que Python sabe como multiplicar un pixel # compuesto RGB por un escalar (a travez de operator overloadig) # Lo que hace es multiplicar por ejemplo (15, 54, 93) * 1.2 # En estos ejemplos puede verse el alto grado de abstraccion # y la orientacion a objetos.

Apendice: Problemas y soluciones

He aqui un resumen de los problemas con los que nos podemos encontrar durante la ejecucion de los ejemplos:

Error: Interprete incorrecto

Este problema a veces puede deberse a que la ruta del comando python se encuentra mal escrita en el inicio del script, o tambien se produce cuando usamos un script que habia sido escrito en una PC con otro sistema operativo que no sea Linux. En el ejemplo de abajo muestro como convertir un archivo de texto del formato DOS al formato Linux.

$ ./drawhistogram.py warnerranch.jpg
bash: ./drawhistogram.py: /usr/bin/python^M: intérprete incorrecto: No existe el fichero ó directorio

$ apt-cache search dos2unix
tofrodos - Converts DOS <-> Unix text files, alias tofromdos
sysutils - Miscellaneous small system utilities - dummy package

$ sudo aptitude install tofrodos

$ dos2unix -b drawhistogram.py

ImportError: No module named _tkinter

Este error se debe a que nos falta agregar la libreria tkinter:

$ ./drawhistogram.py warnerranch.jpg
Traceback (most recent call last):
  File "./drawhistogram.py", line 2, in <module>
    from Tkinter import *
  File "/usr/lib/python2.5/lib-tk/Tkinter.py", line 41, in <module>
    raise ImportError, str(msg) + ', please install the python-tk package'
ImportError: No module named _tkinter, please install the python-tk package

$ sudo aptitude install python-tk

Des:1 http://ar.archive.ubuntu.com hardy/main tcl8.4 8.4.16-4ubuntu1 [1164kB]
Des:2 http://ar.archive.ubuntu.com hardy/main tk8.4 8.4.16-2ubuntu1 [1001kB]
46% [2 tk8.4 359906/1001kB 35%]
Des:3 http://ar.archive.ubuntu.com hardy/main blt 2.4z-4ubuntu1 [1061kB]
68% [3 blt 78407/1061kB 7%]
...
Configurando python-tk (2.5.2-0ubuntu2)

Con respecto a Python

Python es un lenguaje multiplataforma (corre tanto en Windows como en Linux ) Es open source. Orientado a Objetos. Es de muy alto nivel ( mas alto nivel que Java).

El instalador para Windows pesa +- 10 MB, y puede ser descargado libremente para cualquier uso desde el sitio:

Python.org

(En la mayoria de las distros de Linux ya viene preinstalado).

Notar que para poder ejecutar los ejemplos, tambien hay que instalar la libreria PIL (Python Imaging Library). Se puede descargar desde aca:

Python Imaging Library

Tambien te puede interesar:

En la pagina de MPM, podemos encontrar ejemplos de procesamiento de imagenes en formato .bmp, en lenguaje C. Ejemplo de procesamiento de imagen .bmp en lenguage C - mpmlab

No dudes en dejar un comentario haciendo una pregunta, pidiendo ayuda, solicitando una nota sobre un tema en particular, o simplemente agradeciendo.

Copyright: http://snarvaez.poweredbygnulinux.com Sebastian Narvaez

este documento ha sido publicado bajo la siguiente licencia: “Verbatim copying and distribution of this entire article are permitted in any medium provided this notice is preserved.”