Documentos de Académico
Documentos de Profesional
Documentos de Cultura
imágenes en R
Matı́as Bordese
Walter Daniel Alini
Director: Dr. Oscar Humberto Bustos
30 de noviembre de 2007
Nicolás Wolovick
Clasificación:
Palabras clave:
por
Matı́as Bordese
Walter Daniel Alini
Resumen
El presente trabajo describe un paquete de procesamiento de imágenes realizado en R, un lengua-
je y entorno computacional libres, enfocado en estadı́stica y gráficos estadı́sticos. Las distintas
funciones del paquete, denominado biOps, fueron especificadas utilizando la notación Z -un len-
guaje formal de especificaciones usado para describir y modelar sistemas de computación- e
implementadas usando R mediante la codificación e integración de código C.
biOps fue liberado bajo licencia libre GPL y aceptado por la comunidad de R para formar parte
de su repositorio oficial de paquetes.
Agradecimientos
Al Dr. Oscar H. Bustos, por la dirección del trabajo.
Al Dr. Pedro R. D’Argenio, por su apoyo, consejos y opiniones.
A la Dra. Laura Alonso y al MSc. Maximiliano Cristiá, por su desinteresada colaboración.
A Kurt Hornik y Uwe Ligges, del R Development Core Team, nuestros R-gurús.
A nuestros familiares y grupo de amigos.
iii
Índice general
Resumen II
Agradecimientos III
1. Introducción 1
2. R 4
2.1. Antecedente: El lenguaje S . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
2.2. R como implementación de S . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
2.3. Interfaz contra lenguajes compilados . . . . . . . . . . . . . . . . . . . . . . . . . 7
2.4. R puro vs. interfaz C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
2.5. Colaboración a CRAN . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
3. Z 12
3.1. Las especificaciones formales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
3.2. El lenguaje de especificación Z . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
3.3. Definiciones en Z . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
3.3.1. Declaraciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
3.3.2. Abreviaciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
3.3.3. Definiciones axiomáticas . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
3.3.4. Definiciones genéricas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
3.3.5. Esquemas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
3.4. f uzz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
3.5. Especificación en Z . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
3.5.1. Especificación de reales . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
3.5.2. Resto de las especificaciones . . . . . . . . . . . . . . . . . . . . . . . . . . 20
4. Imagen digital 21
4.1. Representación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
4.2. Resolución espacial y de profundidad . . . . . . . . . . . . . . . . . . . . . . . . . 22
4.3. Modelos de color . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
4.3.1. RGB . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
4.3.2. CYM . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
4.3.3. HSI . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
4.4. Nuestra implementación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
4.4.1. Especificación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
iv
Índice general v
5.2. Aplicaciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
5.2.1. Astronomı́a y exploración del espacio . . . . . . . . . . . . . . . . . . . . . 29
5.2.2. Inteligencia y aplicación militar . . . . . . . . . . . . . . . . . . . . . . . . 29
5.2.3. Ciencias de la tierra . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
5.2.4. Gobierno . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
5.2.5. Visualización de datos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
5.2.6. Entretenimiento . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
5.2.7. Medicina . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
5.2.8. Procesamiento de documentos . . . . . . . . . . . . . . . . . . . . . . . . . 31
5.2.9. Aplicaciones industriales y visión de máquinas . . . . . . . . . . . . . . . 31
5.2.10. Aplicaciones hogareñas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
8. Operaciones geométricas 48
8.1. Mapeo de valores: “hacia adelante” vs. “hacia atrás” . . . . . . . . . . . . . . . . 48
8.2. Interpolación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
8.2.1. Interpolación por el vecino más cercano . . . . . . . . . . . . . . . . . . . 49
8.2.2. Interpolación bilineal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
8.2.3. Interpolación por B-Spline . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
8.2.4. Interpolación convolucional cúbica . . . . . . . . . . . . . . . . . . . . . . 51
8.3. Operaciones implementadas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
8.3.1. Escalar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
8.3.2. Encoger . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
8.3.3. Rotar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
8.3.4. Espejar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
8.3.5. Trasladar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
8.3.6. Recortar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
12.Operaciones morfológicas 82
12.1. Operaciones sobre imágenes binarias . . . . . . . . . . . . . . . . . . . . . . . . . 82
12.1.1. Dilatación binaria . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
12.1.2. Erosión binaria . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85
12.1.3. Apertura y clausura binarias . . . . . . . . . . . . . . . . . . . . . . . . . 86
12.2. Operaciones sobre imágenes en escala de grises . . . . . . . . . . . . . . . . . . . 88
13.Clasificación de imágenes 90
13.1. Conceptos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90
13.2. Clasificación supervisada y no supervisada . . . . . . . . . . . . . . . . . . . . . . 91
13.3. Métodos de clasificación no supervisados . . . . . . . . . . . . . . . . . . . . . . . 92
13.3.1. K-means . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93
13.3.1.1. Complejidad . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96
13.3.2. Isodata . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
14.Conclusiones 99
14.1. Trabajo futuro . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100
14.2. Estadı́sticas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101
A. Profiling 103
Bibliografı́a 110
Índice de figuras
9.1. Convolución . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
9.2. Aplicación de sharpening . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
9.3. Aplicación de filtro por mediana . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
vii
List of Figures viii
Introducción
Presentamos en este escrito el resumen de varios meses de trabajo. Intentamos ser precisos al
introducir los conceptos manejados, para que el lector tenga una buena lectura preparatoria, y
analizar en detalle las especificaciones, utilidades e implementaciones de los algoritmos elegidos
para formar parte del paquete.
1
Capı́tulo 1. Introducción 2
Creemos que el paquete obtenido es una importante colaboración con la comunidad R, que no
contaba con paquetes multipropósito de importancia en el procesamiento de imágenes. biOps, en
este sentido, resulta de gran utilidad, fácilmente extensible y con una amplia gama de algoritmos.
Consideramos que los trabajos futuros para la mejora del paquete debieran considerar la exten-
sión de la interfaz gráfica, diversificar los formatos de imagen soportados, reconsiderar el manejo
en memoria de la representación de imágenes y añadir algoritmos para ampliar su utilidad (al-
goritmos supervisados de clasificación de imágenes, filtros, reconocimiento de patrones, machine
vision, etc.).
Este texto se compone de dos partes principales: los capı́tulos 2 al 5 introducen conceptos re-
lacionados a las etapas previas a la codificación. Se presentan la notación Z, lenguaje utilizado
para las especificaciones formales en este trabajo, y el lenguaje R, sobre el cual se implementaron
los algoritmos estudiados. Se desarrollan además, los conceptos relacionados con imágenes, sus
representaciones, modelos de color y los usos en las diversas áreas de aplicación. El capı́tulo 6
presenta una descripción de las secciones posteriores (capı́tulos 7 al 13), en los cuales se profun-
dizan los conceptos, utilidades, especificación e implementación correspondientes a cada una de
las divisiones del paquete.
Para una visión global de este trabajo, recomendamos la lectura de los capı́tulos 1 (esta In-
troducción), 6 (descripción del paquete, contenidos del trabajo y capı́tulos posteriores) y 14
(recapitulación, evaluación, conclusiones y desafı́os para el trabajo futuro). Quien desee profun-
dizar acerca de los lenguajes y notaciones utilizados, puede concentrarse en los capı́tulos 2 (el
lenguaje R, y sus interfaz con el lenguaje C) y 3 (la notación Z, de especificación de modelos
de sistemas computacionales). A los interesados en conceptos o implementaciones en una deter-
minada rama del procesamiento digital de imágenes que hayan sido tratados en este trabajo,
sugerimos la lectura del capı́tulo correspondiente.
A continuación se presenta un breve resumen del contenido de los capı́tulos de este trabajo:
Z [Cap. 3]: Z es el nombre de la notación que utilizamos para la especificación de nuestro traba-
jo. Se presentan los conceptos básicos, definiciones de objetos necesarios para comprender
esta notación e implementación de los algoritmos del paquete y de una representación de los
números reales, basado esto último en la publicación de R. D. Arthan [Art96]. Se menciona
también a f uzz, con el cual realizamos la verificación de tipos de estas especificaciones.
Capı́tulo 1. Introducción 3
Imagen digital [Cap. 4]: Se presentan los conceptos necesarios para comprender la represen-
tación computacional de imágenes: la resolución espacial y de profundidad (detalles en una
imagen) y los modelos de color más conocidos (RGB, CYM y HSI). Se detalla además la
representación elegida para este trabajo e implementación para la obtención de imágenes
mediante R.
El “Comprehensive R Archive Network” es una red de sitios con las librerı́as que están disponibles
para el uso en R. Para colaborar con CRAN es necesario cumplir con una serie de requisitos que
hacen que los paquetes puedan funcionar correctamente y estar documentados de una manera
homogénea.
Desde la segunda parte del siglo XX, y gracias al incremento del poder de cálculo de la compu-
tación, la estadı́stica se ha visto sustancialmente impactada. Este impacto ha traı́do dos con-
secuencias fundamentales: por un lado, la automatización del cálculo para los viejos métodos
estadı́sticos; y por el otro, el resurgimiento del interés en métodos menos estudiados, como los
4
Capı́tulo 2. R 5
no lineales, encabezados por las redes neuronales y los árboles de decisión. La abundancia en
recursos ha causado también el renacer de nuevos modelos lineales descartados con anterioridad.
Alrededor del año 1980 comienzan a surgir los lenguajes de programación especializados en
análisis estadı́sticos. Hoy en dı́a hay tantos como programadores emprendedores hubo en las
últimas décadas.
Entre los lenguajes que más popularidad han logrado, se encuentra S . La historia de este lenguaje
se remonta a mediados de los ’70, en los Laboratorios Bell. Hasta ese entonces, mucho de los
investigadores se valı́an de librerı́as del lenguaje Fortran (acrónimo de For mula Translator) para
realizar sus cálculos, sobre todo la librerı́a SCS (Statistical Computing Subroutines), rutinas
que se extendı́an según las necesidades. El impulso a realizar cálculos más simplistas que los que
proponı́a esta librerı́a, sumado a la paulatina disminución de código Fortran necesario para los
cálculos, hacen que Rick Becker, Allan Wilks y John Chambers comiencen el desarrollo de S
como una unidad independente.
S no fue el primer lenguaje con funcionalidad estadı́stica realizado por los Laboratorios Bell, pero
sı́ el primero en ser implementado. La primera implementación data del 1976 y funcionaba sobre
el sistema operativo GCOS (General Comprehensive Operating System). El nombre ’S’ (escrito
en un principio ası́, con comillas simples) fue elegido por ser esta letra comúnmente usada en
computación estadı́stica, siendo consistente con otros lenguajes de programación desarrollados
en la misma institución (léase el lenguaje de programación C , de uso frecuente en nuestros dı́as).
Tras una mutación no demasiado importante que hizo que pudiera empezar a utilizarse en el sis-
tema operativo UNIX , por el año 1988, S sufre una serie de cambios de peso (en implementación
y, sobre todo, en sintaxis) y en 1991 se introduce el concepto de notación de fórmulas.
Este “nuevo” lenguaje es bastante parecido a las implementaciones actuales: S − Plus (versión
comercial de S , también conocida como S +), desarrollado por la empresa Insightful , y R (versión
libre), objeto de nuestro estudio, y en el cual centraremos toda la atención.
Actualmente, además de S − Plus 2 existen otras alternativas comerciales, que si bien no son
objeto de estudio en este trabajo, vale la pena mencionarlas: SAS 3 , Minitab 4 y SPSS 5 .
La primera implementación de S como proyecto de software libre fue diseñada por Ross Ihaka
y Robert Gentleman en el Departamento de Estadı́sticas de la Universidad de Aukland, Nueva
1 http://www.schemers.org
2 http://www.insightful.com/products/splus
3 http://www.sas.com
4 http://www.minitab.com
5 http://www.spss.com
Capı́tulo 2. R 6
Zelanda. Le llamaron R, que surge por un juego con S , principal antecesor, y el primer nombre
de ambos autores.
Un gran grupo de personas han contribuido con el desarrollo de R mediante el aporte de código y
reportes de bugs desde su creación. Hacia mediados de 1997 se creó un grupo de desarrolladores
con permisos de modificación de las fuentes de R, el “R Core Team”, que se compone actualmente
de 17 personas, entre ellas sus primeros programadores Ihaka y Gentleman.
R integra programas para la manipulación de datos, cálculo y gráficos. Dispone de una gran
cantidad de librerı́as, con un fuerte hincapié en el manejo de datos y funcionalidades estadı́sticas.
Cuenta además con:
R puede integrarse con distintas bases de datos y existen librerı́as que facilitan su utilización
desde lenguajes de programación interpretados (como Perl y Python) o desde lenguajes de código
compilado (como C , C + + y Fortran), como veremos más adelante para el caso particular que
nos interesa. La lista de los lenguajes en los cuales pueden agregarse funcionalidad está creciendo
con el correr del tiempo, a medida que éstos aumentan en eficiencia o popularidad, y a medida
que R crece como utilidad para el usuario.
R se distribuye bajo la licencia GNU GPL y está disponible para la mayorı́a de los sistemas
operativos existentes (incluidas excentricidades como adaptaciones para funcionar en la consola
PlayStation2 y otras)
La distribución de R cuenta con muchos procedimientos con fines estadı́sticos, entre los que se en-
cuentran: modelos lineales y generalizados, modelos de regresión no lineales y análisis de tiempos
de series, asi como también funcionalidad de gráficos y representaciones de datos. Es relativa-
mente sencillo agregar nuevas utilidades, mediante lo que se denominan “add-on”s, módulos de
propósitos especı́ficos.
R nos ofrece la posibilidad de acceder a código compilado que haya sido linkeado previamente.
Este link se puede realizar en tiempo de creación del módulo o bien dinámicamente mediante
la función dyn.load . A través de la función .C se genera una interfaz a código compilado en C
o C + +. Los argumentos que se le pasan a esta función son generalmente copiados antes de la
ejecución del código, y también son copiados a una lista de argumentos en R cuando la función a
la cual accedemos ha retornado su valor. Los argumentos pueden pasarse con nombre, de forma
tal de tener un fácil acceso a ellos en su posterior manejo. R tiene un mecanismo de pasajes de
parámetros por defecto que transforma cada tipo del código en un tipo del código C . La lista de
tipos para los cuales R conoce mecanismos de transformación es acotada, pero puede extenderse,
en caso de requerirse, de una manera sencilla. Para este último caso, es preferible el uso de otras
funciones de ejecución de código compilado. La función .Call es la que se utiliza generalmente,
y que da un mecanismo para pasar directamente a C algunos tipos más complejos de R como
las listas. En el caso del lenguaje C , de interés para este trabajo, podemos ver en la siguiente
tabla la tranformación que sufren los principales modos de almacenamiento:
Mapeo de tipos
R C
logical int∗
integer int∗
double double∗
complex Rcomplex ∗
character char ∗ ∗
raw char ∗
8 http://www.itp.phys.ethz.ch/econophysics/R
Capı́tulo 2. R 8
Con type∗ se denota al puntero a type, es decir, la dirección de memoria de una variable de tipo
type. Rcomplex se refiere a una estructura en C incluida en los archivos de cabecera que provee
el lenguaje R.
La facilidad que presenta R de escribir add − ons en otros lenguajes (nombrados de forma breve
anteriormente) se enfrenta con las ventajas que encuentran algunos desarrolladores de basar sus
módulos sin la intervención explı́cita de otros lenguajes. La mayor parte de las librerı́as de R
están escritas en C , por la indiscutible eficiencia de este lenguaje.
Existe una forma de generar un análisis estadı́stico de un script en R que muestre el uso de
procesador y el porcentaje de tiempo de ejecución que cada parte del script ha utilizado. Lo
anterior es mucho más fácil de decir en inglés, para lo cual tenemos una palabra que lo expresa:
profiling.
Para hacer profiling en R puede llamarse a la función Rprof , entre cuyos argumentos se encuen-
tran el tiempo (medido en segundos) a esperar para hacer un muestreo del stack del proceso (en
general, este número debe ser cercano a 15/20 milisegundos, ya que un número menor harı́a que
el tiempo necesario para recolectar la información se vea superpuesto con la siguiente consulta al
stack, y un número mayor perjudicarı́a la precisión del análisis), y el nombre del archivo en el cual
(sobre)escribir la información recolectada. De esta manera, si bien el script que se está corriendo
baja un poco su performance, es posible identificar las partes en que la ejecución ha invertido
más o menos tiempo. Los mecanismos que se usan para el profiling son los mismos que usa el
lenguaje C, con lo que estas herramientas no pueden usarse conjuntamente.
Los test para Windows y sistemas operativos UNIX puede que arrojen resultados distintos,
puesto que el intervalo fijo que se establece para el muestreo del stack corresponde a uso del
tiempo del CPU en UNIX , y simplemente tiempo nominal en Windows. Sin embargo, ante igual
carga de CPU, los resultados no deberı́an variar para los distintos sistemas operativos.
Una función del lenguaje llamada summaryRprof que devuelve un objeto en R que puede
ser analizado.
Este tipo de análisis se utilizan para identificar “cuellos de botella” o partes de código en R que
pueda ser beneficioso reemplazar por código compilado. Para que los resultados sean provechosos,
es necesario que las corridas sean lo suficientemente grandes como para que el tiempo en que el
lenguaje realiza garbage collections sean depreciables; caso contrario es posible que encontremos
resultados que no sean demostrativos para la experiencia que realizamos.
Capı́tulo 2. R 9
Para ello, codificamos una selección de algoritmos tanto con acceso a código C como sin él
(y aquı́ hablamos de “sin acceso explı́cito”), para realizar luego un análisis con la herramienta
anteriormente mencionada.
A continuación se muestran los resultados obtenidos para un algoritmo de Look-up tables (de-
crementar contraste, función imgDecreaseContrast), que se detallan en 7.1.1, y, para uno de
operaciones aritméticas (diferencia de imágenes, función imgDiffer ), detallados en 7.2. El resto
de los resultados pueden encontrarse en el Apéndice A:
r_ de c_ con tr as t vs . i m g D e c r e a s e C o n t r a s t
Each sample represents 0.15 seconds .
Total run time : 1 9 7 7 . 9 0 0 0 0 0 0 0 0 4 7 seconds .
% total % self
total seconds self seconds name
99.79 1973.70 0.00 0.00 " r_ de c_c on tr as t "
99.78 1973.55 48.40 957.30 " r _ l o o k _u p _ t a b l e "
...
0.21 4.20 0.00 0.00 " imgDecreaseContrast "
0.21 4.20 0.00 0.00 " . imgContrast "
...
0.06 1.20 0.06 1.20 ".C"
...
% self % total
self seconds total seconds name
48.40 957.30 99.78 1973.55 " r _ l o o k _u p _ t a b l e "
...
0.06 1.20 0.06 1.20 ".C"
0.05 0.90 0.05 0.90 " as . vector "
...
r_imgDiffer vs . imgDiffer
Each sample represents 0.15 seconds .
Total run time : 3 5 9 2 . 5 0 0 0 0 0 0 0 1 4 5 seconds .
% total % self
total seconds self seconds name
99.61 3578.40 53.47 1920.90 " r_imgDiffer "
...
0.39 14.10 0.00 0.00 ". imgArithmeticOperator "
0.39 14.10 0.00 0.00 " imgDiffer "
0.29 10.35 0.29 10.35 ".C"
...
% self % total
self seconds total seconds name
Capı́tulo 2. R 10
Notamos para el caso de la función de decrementar contraste (r dec contrast vs. imgDecreaseContrast)
que la relación de uso de CPU fue de aproximadamente 475 a 1 (475.1904) y para la función de
diferencia de imágenes (r imgDiffer vs. imgDiffer ) fue de 255 (255.4102) a 1, en ambos casos a
favor de las implementaciones con acceso a código C.
No resta demasiado análisis por hacer. Lo que valdrı́a preguntarse es el por qué de semejante
diferencia. La respuesta puede buscarse de entre las siguientes justificaciones:
El uso, en algunos casos, de funciones no del todo adecuadas pero que se pegaban más a
las especificaciones de los algoritmos. Por caso, en las look-up tables, se usa una estructura
de memoria contigua (tal como lo describen los algoritmos). Sin embargo, esta razón no es
del todo válida: una evaluación para estos casos (cambiando es uso de memoria contigua
por las funciones mapply y el uso de funciones en los parámetros) arrojó, para el caso de
decrementar contraste, una relación de 433.78 a 1. Es decir, del mismo orden de magnitud
que las pruebas anteriores.
La colaboración con la comunidad R puede hacerse de diversas formas. Existen sistemas de bug-
tracking, para el reporte y discusión de bugs, manejo de versiones, utilidades diversas como de
testeo de nuevos paquetes, interfaz de intérprete por web y un largo etcétera. La comunidad
R crece a un ritmo sorprendente, y es uno de los mejores ejemplos de cómo la colaboración de
anónimos puede hacer crecer el software libre muy por encima de los programas de software
privativo.
Capı́tulo 2. R 11
CRAN (explicado brevemente en la sección anterior) recibe las colaboraciones de paquetes. Antes
de subir un paquete nuevo, es necesario seguir ciertos pasos que garanticen su funcionabilidad y
documentación, entre otras cosas. El grupo de desarrollo de R ha creado un comando a tal fin:
check . Este comando verifica que el paquete pueda instalarse, que los ejemplos corran y que la
documentación con la cual debe liberarse exista, esté completa y pueda ser procesada por los
formateadores (la documentación de un paquete se crea en los formatos de texto plano, HTML
y TEX). Si es necesario compilar código, también chequea que esto pueda hacerse correctamente.
Se verifica además que la estructura de archivos y directorios sea la adecuada: es necesario que
existan ciertos archivos de configuración y de ayuda, los cuales usualmente contienen scripts de
verificación de librerı́as requeridas e información acerca de las licencias y caracterı́sticas generales.
Este comando debe finalizar su ejecución sin errores ni advertencias para que el paquete sea
aceptado en el repositorio. Con el comando build puede generarse un archivo comprimido listo
para liberar una versión de nuestro paquete. La “entrega” se realiza mediante la carga del
archivo a un repositorio temporario (FTP ) de paquetes y el envı́o de un correo electrónico a los
mantenedores de CRAN .
Capı́tulo 3
Las especificaciones pueden ser provechosas en muchos sentidos: describen propiedades sin in-
miscuirse en implementaciones, son referencia constante para todos los individuos involucrados
de una u otra forma en el proceso de creación de software (investigadores, codificadores, testers,
documentadores, clientes, etc.) y forman la estructura básica para la etapa de codificación. La
matemática ha ayudado a formalizar estos conceptos a través del concepto de tipos.
Al disponer sólo del tipo de los números enteros (caracterı́stica de Z ), vimos la necesidad de
definir el tipo que represente los números reales (y varios de sus subconjuntos), de modo de
clarificar notaciones y hacer nuestras especificaciones de lectura natural e intuitiva. Para ello nos
basamos en una publicación de R. D. Arthan que axiomatiza este conjunto de forma precisa.
Con esta extensión fue posible definir nuestro esquema de representación de una imagen y a
partir de allı́ modelar los algoritmos que componen este trabajo, y que serán tratados en los
sucesivos capı́tulos.
Las especificaciones formales usan la notación matemática para describir de una forma precisa las
propiedades que debe tener un sistema de información, sin restringir excesivamente la forma en
que estas propiedades son alcanzadas. Describen qué debe hacer el sistema sin decir cómo debe
12
Capı́tulo 3. Z 13
hacerlo. Esta abstracción hace de la especificación formal una herramienta útil en el proceso de
desarrollo de sistemas de computación, porque permiten que las preguntas acerca de lo que hace
el sistema puedan ser respondidas de una manera confiable, sin la necesidad de desenmarañar la
información de una masa de código detallada, o especular acerca del significado de frases en una
descripción en prosa imprecisa.
Una especificación formal puede servir como un punto de referencia simple y confiable para
quienes investiguen las necesidades de los clientes, para quienes implementen los programas para
satisfacer esas necesidades, para aquellos que testeen los resultados y para aquellos que escriban
la documentación del sistema. En definitiva, es una herramienta que puede ser útil para todos
los integrantes del proceso de desarrollo.
Al ser independiente del código del programa, la especificación formal de un sistema puede ser
realizada en las primeras etapas del proceso de desarrollo. Aún cuando cambie a medida que se
gane en comprensión del problema y percepción de la evolución de las necesidades del cliente,
puede ser una media apreciable para promover un entendimiento común entre todos los roles
involucrados en el sistema.
Una forma en que la notación matemática puede ayudar a alcanzar estos objetivos es a través
del modelo de tipos de datos matemáticos del sistema. Estos tipos de datos no están orientados a
la representación computacional, pero responden a un conjunto de leyes que hacen posible sacar
conclusiones efectivas acerca del comportamiento que tendrá un sistema especificado.
Z es un lenguaje de especificación que trabaja a altos niveles de abstracción. Esto permite que
aún comportamientos complejos puedan ser descriptos precisa y consisamente. Originalmente
propuesto por Jean-Raymond Abrial en 1977 con la ayuda de Steve Schuman y Bertrand Meyer,
fue desarrollado por el grupo de Investigación de Programación de la Universidad de Oxford.
Ha sido sometido en los últimos años a estandarización de la Organizacion Internacional de
Estandarización (ISO).
La semántica de Z es matemática; de esta manera las fórmulas pueden ser manipuladas algebraica
y lógicamente.
Otro aspecto es cómo se puede estructurar este lenguaje. En Z esto se responde con el concepto de
esquemas: una declaración de patrones y restricciones. El lenguaje de esquemas puede ser usado
Capı́tulo 3. Z 14
para describir el estado del sistema, y las formas en que este estado puede cambiar. También
puede describir propiedades del sistema y ayudar a pensar acerca de posibles refinamientos del
diseño.
Los esquemas se utilizan para describir aspectos dinámicos y estáticos. Estos últimos incluyen:
Una de las caracterı́sticas principales de Z es el uso de tipos. Además de ser esto un enlace de
extrema utilidad para el momento de la codificación, puede ser sujeto de chequeos automáticos.
Existen varias herramientas a tal fin, entre las que se encuentra f uzz, la cual describiremos
brevemente más adelante (sección 3.4).
Otro aspecto es el uso del lenguaje natural: usamos el lenguaje matemático para determinar el
problema y eventualmente encontrar soluciones, e incluso para probar que los diseños cumplen
con la especificación. El uso del lenguaje natural relaciona la matemática con los objetos de la
vida real, y es esencial para hacer que las especificaciones sean realmente obvias para el lector.
3.3. Definiciones en Z
A modo introductivo presentamos algunos de los conceptos sobre los cuales se basa el lenguaje
de especificación Z , que serán de utilidad para la comprensión de las especificaciones del trabajo.
3.3.1. Declaraciones
Es la forma más simple de declarar un objeto en Z . Se utiliza en especial para tipos básicos o
conjuntos dados. Se denotan por una declaración del nombre entre corchetes:
[A]
Este tipo de declaraciones introduce un nuevo tipo, con lo que podremos declarar variables con
ese tipo en el futuro:
Capı́tulo 3. Z 15
0:A
3.3.2. Abreviaciones
Es la manera en que se puede definir un objeto a partir de otros existentes, cuando sus objetos
y estados son iguales:
Se pueden introducir objetos con restricciones, como las que deben asumirse cuando un sı́mbolo
es usado. Estas restricciones se interpretan como axiomas del objeto:
declaracion
predicado
donde predicado simboliza las restricciones del objeto u objetos declarados en declaracion.
Por ejemplo:
TopValue : N
TopValue = MaxValue + 1
Introduce un nuevo sı́mbolo, TopValue, que satisface el predicado que se menciona. Como en
este ejemplo, las declaraciones pueden restringirse hasta el punto que se denote sólo un objeto.
Se utilizan para definir una familia de constantes globales, parametrizadas por algún conjunto:
[Y ]
y :Y
predicado
Capı́tulo 3. Z 16
introduce una constante genérica de tipo Y, satisfaciendo el predicado predicado. Notar que Y
es, en este caso, un parámetro formal: puede considerarse como un tipo básico con visibilidad en
esta definición genérica.
A modo de ejemplo, tenemos la definición utilizada en el trabajo para obtener el largo de una
secuencia:
[X ]
# : seq X "N
#hi = 0
∀ i : seq X | i 6= hi • # i = 1 + # (tail i)
3.3.5. Esquemas
NombreDeEsquema
declaraciones
predicados
Image
v : VALUES
width, height : N
3.4. f uzz
Entre las herramientas de formateo se incluyen archivos de estilo para LATEX, y la definición
de un conjunto con sı́mbolos especiales propios de estas especificaciones. Para su uso f uzz pro-
vee, entre otros, de los siguientes entornos, los cuales fueron mencionados en la sección 3.3: zed ,
Capı́tulo 3. Z 17
axdef , gendef y schema, respectivamente para texto en prosa y fuera de estructuras, definicio-
nes axiomáticas, definiciones genéricas y esquemas. Existen otros entornos disponibles que no
mencionaremos en este breve resumen.
Para este trabajo hicimos uso de sus dos funcionalidades principales. En la impresión actual se
utilizaron las herramientas que permiten que los diagramas y sı́mbolos especiales puedan verse
correctamente y mezclarse con texto en prosa, como es caracterı́stico en muchos formatos de
especificación. Y para la diagramación del código Z para los algoritmos implementados, hicimos
uso del chequeador de tipos y alcance de variables, lo cual es mı́nimamente necesario en cualquier
chequeo de especificaciones.
El comando f uzz puede configurarse para tener dos tipos de salida: con la opción −v obtenemos
un reporte en código ASCII de una representación de cada párrafo en Z ; y con la opción −t se
listan el tipo de cada nombre definido globalmente, en una representación fácil de leer. Además,
los esquemas son expandidos, para que resulte claro ver qué componente tiene cada uno. La
salida de esta última opción se incluye en formato digital con este trabajo (tal como se describe
en la sección 6.6).
3.5. Especificación en Z
La notación Z tiene un solo tipo built − in (esto es, propio de la notación): el conjunto de todos
los enteros Z . Cualquier otro tipo puede construirse a base de éste, o de valores de tipos básicos
(sobre los cuales no pueden asumirse ninguna propiedad).
Muchos de los algoritmos que presentamos en nuestra implementación requieren de una precisión
que los enteros no nos brindan de forma natural. Es fácil determinar una biyección entre los
números enteros y los reales de precisión acotada, pero el manejo de los mismos se torna tedioso
y la representación no obedece a las costumbres sobre el manejo de valores que arrastramos
en la educación que recibimos. Por esta razón, y por la estructura de imágenes que creimos
conveniente utilizar (aunque esta estructura y la representación de valores están ı́ntimamente
relacionadas) y que mencionaremos en esta sección, es que necesitamos la especificación de un
tipo que represente más fidelignamente a los reales.
Para tal fin nos basamos en la publicación de [Art96], “Arithmetics for Z”, la cual está fuertemen-
te inspirada en el estándar [Dep95]. La especificación que realizamos incluye la axiomatización
necesaria para definir el conjunto de los números reales y sus operaciones básicas (de acuerdo a
lo que nos resultaba excluyente disponer).
2. El campo de los reales puede ordenarse linealmente de forma que este orden sea compatible
con la suma y la multiplicación. Para definir dicho orden es suficiente con encontrar un
conjunto R, cerrado por multiplicación y suma, tales que Rp , Rn y {0} conformen una
partición del campo.
3. Cualquier subconjunto no vacı́o de los reales, acotado inferiormente con respecto al orden
establecido en el punto anterior, tiene una cota inferior maximal.
Estas propiedades caracterizan a los reales (o cualquier isomorfismo) y una consecuencia de ello
es la existencia de un anillo incluido en este conjunto, que es isomorfo a los enteros.
Esta axiomatización es similar a las vistas en los libros de cálculo. Comenzamos con un conjunto
maximal, que llamamos A
[A]
Z : A
0:A
1:A
+ :A×AA
∼
:AA
N : Z
(Z × Z ) ( + ) ∈ Z × Z "Z
Z (∼
)∈Z "Z
{0, 1} ⊆ Z
∀ i , j , k : Z • (i + j ) + k = i + (j + k )
∧i +j =j +i
∧ i + ∼i = 0
∧i +0=i
∀ h : Z • 1 ∈ h ∧ (∀ i, j : h • i + j ∈ h ∧ ∼ i ∈ h) ⇒ h = Z
N = {s : Z | 0 ∈ s ∧ {i : s • i + 1} ⊆ s}
T
∼
1∈
/N
Capı́tulo 3. Z 19
− :A×AA
(Z × Z ) ( − ) ∈ Z × Z "Z
∼
∀ i, j : Z • i − j = i + ( j )
∀ i , j : Z • (i ≤ j ⇔ j − i ∈ N )
∧ (i < j ⇔ i + 1 ≤ j)
∧ (i ≥ j ⇔ j ≤ i )
∧ (i > j ⇔ j < i )
∗ :A×AA
(Z × Z ) ( ∗ ) ∈ Z × Z "Z
∀ i , j , k : Z • (i ∗ j ) ∗ k = i ∗ (j ∗ k )
∧i ∗j =j ∗i
∧ i ∗ (j + k ) = i ∗ j + i ∗ k
∧1∗i=i
div , mod : A × A A
R : 1 A
/ :A×AA
(R × R) ( + ) ∈ R × R " R
(R × R) ( ∗ ) ∈ R × R " R
(R × R \ {0}) ( / ) ∈ R × R \ {0} " R
R (∼ ) ∈ R " R
Z ⊆R
∀ x , y, z : R • (x + y) + z = x + (y + z )
∧x +y =y +x
∧ x + ∼x = 0
∧x +0=x
∀ x , y, z : R • (x ∗ y) ∗ z = x ∗ (y ∗ z )
∧x ∗y =y ∗x
∧ x ∗ (y + z ) = x ∗ y + x ∗ z
∧1∗x=x
∀ x : R • ∀ y : R \ {0} • (x / y) ∗ y = x
Capı́tulo 3. Z 20
Rp, Rn : 1 A
Con esta “creación” del tipo R, muchas de las operaciones sobre imágenes que fueron especificadas
(y que se mostrarán pertinentemente, a medida que lo consideremos necesario) resultaron más
claras e intuitivas.
Image
v : VALUES
width, height : N
se especificaron las operaciones sobre imágenes que corresponden al presente trabajo. Las mismas
se presentarán en las secciones particulares de los algoritmos, cuando creamos necesario hacer
alguna aclaración. De todas formas, los archivos correspondientes a estas descripciones pueden
encontrarse en formato digital, con el material que acompaña este impreso (ver sección 6.6 para
más detalles).
Nótese que no se hacen diferencias de acuerdo a la cantidad de canales que tenga la imagen en
cuestión. Esto fue una decisión arbitraria y responde a una necesidad de claridad de notación y en
algunos casos a similitudes en los diversos canales de una imagen. Vale decir que las especificacio-
nes realizadas en Z nos guiaron a través de nuestro desarrollo, pero no nos restringieron. Es por
eso que algunas caracterı́sticas esperadas en las imágenes resultantes de la aplicación de algún
algoritmo sólo se describe a través de una definición axiomática y algunas otras directamente se
asumen como disponibles para su uso.
Capı́tulo 4
Imagen digital
Cuando se captura una imagen del mundo real a través de una computadora, la continuidad de
tamaño, intensidad y colores es truncada. La combinación de caracterı́sticas fı́sicas continuas que
nuestra mente se encarga de manejar deben ser convertidas en números finitos para ser utilizados
por una computadora. Esa visión continua debe ser discretizada para obtener una imagen digital.
En esa conversión se determinan la resolución espacial y la profundidad de color.
4.1. Representación
Una imagen se puede definir como una función de dos dimensiones, f (x , y), donde x , y son
coordenadas espaciales, en el plano, y la amplitud de f en cualquier par de coordenadas (x , y)
se llama intensidad de la imagen en ese punto. La denominación escala de grises se usa para
referirse a la intensidad en imágenes monocromáticas. Las imágenes en color están formadas por
la combinación de imágenes 2-D. Por ejemplo, en el sistema de color RGB (red, green, blue),
una imagen consiste de tres imágenes componentes individuales (rojo, verde, azul). Por esta
razón, muchas de las técnicas desarrolladas para imágenes monocromáticas se pueden extender
a imágenes color mediante el procesamiento de cada una de las componentes individuales. En
general hablaremos en términos de imágenes en escala de grises, haciendo las aclaraciones y
distinciones para extender a imágenes color cuando sea necesario.
Una imagen puede ser continua respecto a los ejes de coordenadas, como ası́ también en am-
plitud. Convertir dicha imagen a formato digital requiere que tanto las coordenadas como la
intensidad sean digitalizadas. El proceso de digitalizar las coordenadas se llama sampling (mues-
treo), mientras que el de digitalizar la amplitud se llama quantization. De esta manera, cuando
x , y, y la amplitud de f son valores finitos y discretos tenemos una imagen digital.
21
Capı́tulo 4. Imagen digital 22
El lado derecho de la igualdad es por definición una imagen digital. Cada elemento de esta matriz
se llama pixel (picture element). Usaremos los términos imagen y pixel de aquı́ en adelante para
denotar una imagen digital y sus elementos, respectivamente.
El sampling determina la resolución espacial de una imagen. La resolución espacial define el me-
nor detalle discernible en una imagen. Supongamos que tenemos un cuadro con lı́neas verticales
de ancho W , con un espacio entre estas lı́neas también de ancho W . Un par consiste de una
lı́nea y el correspondiente espacio adyacente. Entonces el ancho de un par es 2W , y hay 1/2W
pares por unidad de distancia. Una definición de resolución es simplemente el menor número de
pares discernibles por unidad de distancia; por ejemplo, 100 pares por milı́metro.
Hay que tener en cuenta que cada pixel no representa sólo un punto en la imagen, sino una región
rectangular. De esta forma, con pixels grandes no sólo la resolución espacial es baja, sino que el
valor del nivel de gris correspondiente hace aparecer discontinuidades en los bordes de los pixels.
A medida que los pixels se hacen más pequeños, el efecto se hace menos pronunciado, hasta el
punto en que se tiene la sensación de una imagen continua. Esto sucede cuando el tamaño de
los pixels es menor que la resolución espacial de nuestro sistema visual. Para una tarea dada el
tamaño de pixel deberı́a ser lo suficientemente pequeño de acuerdo a los objetos que queramos
estudiar de la imagen.
Lo que los humanos percibimos como color es una combinación de caracterı́sticas fı́sicas. Un
modelo (o espacio) de color es una representación matemática de esas caracterı́sticas. El objetivo
es también facilitar la especificación de colores mediante alguna forma estándar y aceptada. En
esencia se tratan de sistemas de coordenadas y subespacios en que cada color se representa por
un único punto.
Brevemente repasaremos estos distintos esquemas. Si bien la mayorı́a de los procesos con imáge-
nes digitales trabajan en RGB, muchas aplicaciones requieren la conversión a otros espacios de
color.
4.3.1. RGB
Todos los espacios de color son sistemas ortogonales tridimensionales de coordenadas, es decir
que los tres ejes (en este caso las intensidades de rojo, verde y azul) son perpendiculares entre
sı́. La intensidad del rojo empieza en cero y se incrementa en uno de los ejes. Análogamente
para el verde y el azul en sus correspondientes ejes. Asumiendo 8 bits de profundidad, cada color
puede tener un valor máximo de 255, dando como resultado una estructura cúbica. La escala de
grises (puntos de valores RGB iguales) se extiende desde el negro hasta el blanco, a lo largo de
la diagonal que une estos dos puntos.
De esta manera tenemos un modelo matemático que nos permite definir cualquier color dando
sus valores de rojo, verde y azul, es decir coordenadas en el cubo. El RGB es un espacio de color
aditivo, porque su origen está en el negro y cualquier otro color se deriva sumando valores de
intensidad. Es el modelo usado en la práctica para los monitores color y muchas cámaras de
video.
4.3.2. CYM
Este espacio de color es el inverso exacto del RGB. En este caso, el origen es blanco y los ejes
primarios son cyan, amarillo y magenta. Ası́, el color rojo es una combinación de amarillo y
magenta, el verde de amarillo y cyan, y el azul de cyan y magenta. A continuación se detallan
las ecuaciones que permiten pasar de un sistema a otro:
Si se muestra una imagen en CYM como si fuera RGB veremos una imagen con sus colores
invertidos o negativos. El CYM se usa principalmente en la industria de la impresión, donde
las imágenes empiezan sobre un papel blanco y la tinta se aplica para obtener los colores. Se
han desarrollado técnicas para obtener imágenes de mayor calidad y a un menor costo. Uno de
estos avances es el llamado “under color removal” que modifica CYM en CYMK, donde la K
representa al negro.
Este proceso, sabiendo que todo color tiene un gris subyacente, es decir una misma cantidad de
cyan, magenta y amarillo, genera esa componente con tinta negra (más barata) y utiliza menor
cantidad de tinta de color para lograr el tono correcto.
4.3.3. HSI
La visión humana tiende a observar los colores de una forma diferente. No vemos las cosas como
una mezcla de colores primarios en una proporción particular, sino como tonos (hue), saturación
(saturation) e intensidad (intensity). Todavı́a se trata de un espacio tridimensional, aunque
bastante diferente del RGB o CYM.
En la imagen 4.3 vemos un eje que recorre el centro del cono, que representa la intensidad. Sobre
este eje se encuentran todos los valores de gris, con el negro en el origen del cono y el blanco
en la base. Cuanto mayor es la distancia sobre esta lı́nea al origen, la intensidad es mayor, más
brillante.
Si vemos la base del cono desde arriba, se convierte en un cı́rculo. Los diferentes tonos están
definidos por posiciones especı́ficas alrededor del cı́rculo. Los tonos están dados por su posición
angular en esta rueda.
Capı́tulo 4. Imagen digital 25
La saturación, o riqueza de color, está definida como la distancia perpendicular al eje de intensi-
dad. Los colores más cercanos al eje central tienen menor saturación y se ven pastel. Los colores
cercanos al borde del cono tienen mayor saturación y son más marcados en apariencia. A veces
es preferible modificar una imagen en HSI en lugar de RGB. Por ejemplo, si quisiéramos cambiar
el color amarillo de un auto a azul, pero sin afectar el brillo ni las sombras. Esto es relativamente
sencillo en HSI. Basta cambiar el valor de tono, sin modificar la intensidad ni la saturación.
Siguiendo el esquema visto hasta aquı́ elegimos representar una imagen digital mediante una
matriz. Nos inclinamos por usar matrices de R, de dos dimensiones si la imagen tiene un único
nivel de profundidad de color o tres dimensiones si se trata de imágenes RGB, el espacio de color
base del cual partimos. Sin embargo, esta decisión también afectarı́a nuestra forma de trabajar
en el lenguaje C.
Esta elección significarı́a manejar arreglos lineales en C con una distribución particular de los
datos, que es la forma en que R hace la conversión de matrices. Para hacer más comprensible el
manejo de ı́ndices sobre dicho arreglo se definió una macro que hace la traducción de coordenadas
en la imagen a ı́ndices en ese arreglo lineal.
(r0,0 , g0,0 , b0,0 ) (r0,1 , g0,1 , b0,1 ) (r0,2 , g0,2 , b0,2 ) ···
(r1,0 , g1,0 , b1,0 ) (r1,1 , g1,1 , b1,1 ) (r1,2 , g1,2 , b1,2 ) ···
.. .. .. ..
. . . .
Capı́tulo 4. Imagen digital 26
r0,0 r1,0 ... r0,1 r1,1 ··· g0,0 g1,0 ··· b0,0 b1,0 ···
Los formatos de imagen soportados son jpeg, a través de la librerı́a libjpeg, y tiff, mediante libtiff.
A partir de ellas se desarrollaron las funciones para leer y escribir archivos de imágenes. libjpeg es
una librerı́a escrita en C que implementa un codificador/decodificador JPEG. Es mantenida por
el Grupo JPEG Independiente 1 . La versión actual es la 6b. Similarmente, libtiff2 es una librerı́a
que permite leer y escribir archivos en formato TIFF. Actualmente la última versión estable es la
3.8.2. Ambas librerı́as son libres, y se distribuyen tanto su código fuente como versiones binarias
para distintas plataformas.
4.4.1. Especificación
A lo largo del trabajo se explican las distintas técnicas y filtros mediante especificaciones en el
lenguaje Z. A continuación se describen los esquemas que caracterizan a la representación de
imagen elegida.
Existen un valor mı́nimo y un valor máximo. Para el caso de imágenes de 8 bits de profundidad,
tendremos MinValue = 0 y MaxValue = 255.
MinValue, MaxValue : N
Los posibles valores para cada pixel oscilan en el intervalo determinado por el mı́nimo y máximo
dados.
VALUES define el espacio que va de un par (que representa las coordenadas de la imagen) en
un VALUE . Especifica el espacio de las matrices imagen.
VALUES == (N × N VALUE )
El esquema estado de una imagen está dado por una matriz, y las dimensiones de alto y ancho.
En este caso se trata de imágenes con una sola componente de color.
Image
v : VALUES
width, height : N
1 http://www.ijg.org/
2 http://www.remotesensing.org/libtiff
Capı́tulo 5
El procesamiento digital de
imágenes
La vista es el más avanzado de nuestros sentidos, tal es ası́ que las imágenes tienen un papel
importante en la percepción humana. Sin embargo, a diferencia del ser humano que está limitado
a la banda visual del espectro electromagnético, las máquinas pueden cubrir distintas bandas,
desde las ondas gamma hasta las de radio. Pueden trabajar con imágenes generadas a partir de
fuentes que los humanos no están acostumbrados a asociar con imágenes: ultrasonido, visualiza-
ción de modelos matemáticos o visión por computadora, por citar algunos ejemplos. El campo
del procesamiento digital de imágenes se refiere al proceso de trabajar con imágenes digitales
mediante computadoras. Cubre una amplia gama de técnicas, utilizadas en numerosas aplica-
ciones: para mejorar o distorsionar una imagen, destacar ciertas caracterı́sticas, crear una nueva
imagen desde otras o restaurar una imagen degradada (por transmisión, adquisición). Actual-
mente puede ser llevada a cabo por cualquier persona con una computadora personal. De esta
manera se observa el uso de técnicas de procesamiento de imágenes entre artistas, cientı́ficos y
otros, aún sin conocimientos especı́ficos.
5.1. Orı́genes
Una de las primeras aplicaciones de las imágenes digitales fue en la industria de los periódicos,
cuando se enviaban fotos a través de un cable submarino entre Londres y Nueva York. De esta
forma se redujo la transmisión de una foto a través del Atlántico, en 1920, de más de una semana
a menos de tres horas. Un sistema de impresión especializado recibı́a y reconstruı́a las imágenes
codificadas enviadas a través del cable. Algunos de los problemas iniciales fueron mejorar la
calidad visual de estas imágenes en función los procedimientos de impresión y la distribución de
los niveles de intensidad.
Hasta ese momento tenemos ejemplos que involucran imágenes digitales, pero que no pueden
considerarse como ejemplos de procesamiento digital de imágenes, ya que no habı́a computadoras
27
Capı́tulo 5. El procesamiento digital de imágenes 28
Las primeras computadoras suficientemente poderosas para ejecutar tareas significativas de pro-
cesamiento de imágenes aparecieron en la década del ’60. El nacimiento de lo que consideramos
el procesamiento digital de imágenes se puede remontar a la disponibilidad de esas máquinas y
el desarrollo del programa espacial de ese perı́odo. La combinación de estos dos factores sacó a la
luz el potencial del campo de procesamiento de imágenes. El uso de técnicas con computadoras
para mejorar imágenes espaciales empezó en el Jet Propulsion Laboratory (California) en 1964,
donde las imágenes de la Luna transmitidas por el Ranger 7 fueron procesadas por una compu-
tadora para corregir diferentes distorsiones inherentes a la cámara de televisión utilizada. Estas
técnicas constituyeron la base para nuevos métodos que se utilizarı́an más tarde para mejorar y
restaurar imágenes de misiones posteriores.
Los procedimientos para mejorar y restaurar imágenes se utilizan para procesar imágenes de-
gradadas de objetos irrecuperables o resultados experimentales demasiados costosos de repetir.
En arqueologı́a, por ejemplo, se usan estos métodos para restaurar imágenes con ruido que son
el único registro de artı́culos raros, perdidos o dañados después de ser fotografiados. En fı́sica
y campos relacionados se usan técnicas para procesar imágenes de experimentos en áreas ta-
les como plasma de alta energı́a y microscopı́a del electrón. Y de la misma manera se pueden
encontrar casos de aplicación en astronomı́a, biologı́a, medicina nuclear, defensa o en la industria.
Todos estos ejemplos ilustran la utilidad de los resultados del procesamiento de imágenes con
la finalidad de la interpretación del hombre. La segunda mayor área de aplicación del proce-
samiento de imágenes es en el tratamiento de problemas relacionados con la percepción de las
máquinas. En estos casos el interés se centra en procedimientos para extraer información de una
imagen para ser utilizada por una máquina, y por lo tanto, no necesariamente estos resultados
tienen que ver con las formas de interpretación humana. Ejemplos de información utilizada por
las máquinas son los momentos estadı́sticos, los coeficientes de la transformada de Fourier y me-
didas de distancias multidimensionales. Problemas tı́picos en este campo son el reconocimiento
automático de caracteres, visión de máquinas, aplicaciones militares, procesamiento de huellas
digitales, visualización de rayos X y muestras de sangre, y procesamiento de imágenes satelitales
para la predicción del clima y análisis del medio ambiente.
Capı́tulo 5. El procesamiento digital de imágenes 29
5.2. Aplicaciones
El uso del procesamiento digital de imágenes se ha ido extendiendo a distintas áreas, y ha dejado
de ser una actividad exclusiva de un grupo de cientı́ficos, para ir teniendo cada vez mayor impacto
en nuestra vida cotidiana. A continuación se describen algunas aplicaciones especı́ficas.
Este campo ha sido desde el comienzo una de las áreas más activas en el desarrollo de técnicas
y avances en el procesamiento digital de imágenes. Debido a las señales débiles en la captura de
imágenes de los objetos celestes, se debieron desarrollar métodos para extraer información; es
ası́ como surgen muchos de los filtros disponibles hoy: promedio de imágenes, filtros de convolu-
ción y transformadas de Fourier, por ejemplo.
Los sistemas de imágenes diseñados en esta área, en general, atribuyen menor importancia al
color, buscando el detalle. Es por eso que en gran medida se trabaja con imágenes en escala de
grises, aunque en algunos casos se añaden colores para resaltar determinada información.
En este caso se utiliza como herramienta para la interpretación de fotografı́as, con el objetivo
de identificar áreas de interés y extraer toda la información posible de la imagen. Puede ser en
búsqueda de instalaciones militares, facilidades para la investigación, complejos industriales o
estructuras residenciales. Una de las principales necesidades es la velocidad. Se hace zoom sobre
determinadas zonas de una imagen, rotaciones para lograr una perspectiva particular, o puede
ser necesario mejorar el contraste de la fotografı́a. Adicionalmente también se requiere hacer
anotaciones sobre la imagen.
Otro uso en este campo es la combinación de mapas digitalizados e imágenes satelitales para el
mejor conocimiento de una zona dada, sumado a la reconstrucción del terreno y animaciones,
que permiten conocer las caracterı́sticas topográficas del lugar.
Los geólogos pueden aprender mucho de imágenes tomadas de la superficie. Pueden identificar
fácilmente fallas en la corteza de la Tierra, especialmente a partir de imágenes multiespectrales,
es decir cuando se cuenta con muchas imágenes capturadas de una misma área en diferentes
espectros electromagnéticos.
5.2.4. Gobierno
Este tipo de análisis puede ayudar a los gobiernos a estimar el crecimiento urbano y el planea-
miento de facilidades y servicios. La representación visual de los datos abstractos en general
ofrece una mejor vista de situaciones del mundo real que los números y las estadı́sticas.
5.2.6. Entretenimiento
La industria del entretenimiento se ha convertido en los últimos años en una de las principales
usuarias del procesamiento de imágenes. Los efectos visuales no se usan sólo en pelı́culas y
televisión, sino también en parques temáticos y eventos especiales. El uso de computadoras
transformó la industria y abrió la posibilidad al desarrollo de la creatividad. De hecho, el uso del
procesamiento digital de imágenes en la industria del entretenimiento impulsa el avance de los
lı́mites tecnológicos en lo que a computadoras y almacenamiento de datos se refiere.
5.2.7. Medicina
La medicina ha usado imágenes digitales durante muchos años, y nuevas técnicas hacen que
esta tendencia vaya en aumento. Los métodos en este campo son limitados, aunque hay que
tener en cuenta que deben proveer gran precisión y confiabilidad puesto que en muchos casos
está la vida en juego. Podemos citar por caso el uso de rayos X, como un método no intrusivo
que permite investigar un cuerpo, mostrando detalles finos de sus estructuras internas y que
se utiliza para diagnóstico y tratamiento. Actualmente estas imágenes se pueden digitalizar, y
Capı́tulo 5. El procesamiento digital de imágenes 31
además de integrar esa información en bases de datos, se tiene la posibilidad de realzar, escalar,
rotar, filtrar y manipular los datos de distintas maneras.
Existen diversas técnicas especializadas para operar sobre este tipo de datos. Una de las áreas
de mayor investigación es la de la compresión. Sin embargo, muchas veces contamos con esa
información en forma de imagen. Ası́ surge la necesidad de convertir una imagen digital en ca-
racteres ASCII. Este proceso se denomina Reconocimiento Óptico de Caracteres (OCR). Usando
distintas operaciones y filtros sobre la imagen, ésta se puede reducir a sus partes mı́nimas y luego
aplicar técnicas de búsqueda de patrones para distinguir los caracteres.
Ası́ como los robots se han hecho cargo de tareas repetitivas o peligrosas, también se les ha dado
la habilidad de “ver” y tomar decisiones basadas en esas observaciones.
Una aplicación es el ordenar y reconocer objetos, por ejemplo los productos que vienen en una
cinta transportadora. Se toma una captura de imagen, y usando filtros de contraste, threshold
y otras técnicas, se pueden aislar e inspeccionar objetos individuales mediante un software es-
pecializado, y determinar la corrección de un objeto para pasar a una siguiente etapa en el
proceso.
biOps: un paquete de
procesamiento de imágenes para
R
biOps 1 , acrónimo de Basic Image Operations, es el nombre del paquete publicado en los reposi-
torios de R con los algoritmos que en su mayorı́a se decriben en este trabajo. El nombre se ha
instaurado por razones históricas, al ser la primer idea del proyecto la publicación de varios pa-
quetes, con el mismo contenido que el actual dividido de acuerdo a su funcionalidad. Esta idea se
descartó por razones de experiencia en el uso de los paquetes y de dependencias y funcionalidades
en común entre ellos.
En este capı́tulo se describen otros paquetes R para el manejo de imágenes, parte de la investi-
gación previa al desarrollo de biOps. A continuación se detallan los componentes del paquete y
una introducción a su interfaz gráfica de usuario (biOpsGUI), el testing realizado, la estructura
y el contenido del material en formato digital que acompaña el presente impreso y la organiza-
ción de los próximos capı́tulos, en donde profundizaremos conceptos, teorı́a y codificación de los
algoritmos implementados.
Para una visión global del contenido y funcionalidad provista por el paquete, recomendamos la
lectura de este capı́tulo. Para entrar en detalle en algún algoritmo o área particular, puede ser
conveniente la lectura del capı́tulo correspondiente.
Nuestro estudio previo incluyó un rastreo y análisis de paquetes de R relacionados con el ma-
nejo y procesamiento de imágenes. En la actualidad, no hay muchos antecedentes en CRAN, el
repositorio oficial de paquetes R (analizado en la sección 2.2). Aquı́ una lista de paquetes que
analizamos y un breve comentario de ellos:
1 http://cran.r-project.org/src/contrib/Descriptions/biOps.html
32
Capı́tulo 6. biOps: un paquete de procesamiento de imágenes para R 33
adimpro 2 : maneja formatos de imágenes pgm, ppm y pnm, los cuales no serán tratados en
este trabajo. Si se tiene instalada la librerı́a ImageMagick soporta más formatos y cambio
de representaciones (algo que analizamos en 4.3 ya que esta librerı́a también resultó del
interés de biOps, como se detalla en 14.1). Provee funcionalidad de rotar imagen, unos pocos
métodos de detección de bordes y extracción de máscaras para aplicación de algoritmos
mediante el Propagation-Separation approach 3 , un enfoque de imágenes que se basa en
adaptación estructural, las cuales usan aproximaciones por modelos parámetricos. Este
último enfoque es central en los algoritmos de este paquete.
edci 4 : provee algunos métodos de detección de puntos en bordes mediante algoritmos ba-
sados en M-estimators, un concepto que utiliza la librerı́a de modelado en Java, JVMA.
PET 5 : algoritmos para escalar y rotar imágenes en formatos pet y fif. Pueden utilizarse
más formatos, pero requieren del paquete adimpro. Provee también implementaciones de
algunas transformaciones, como la de Hough, Radon y Radon inversa6
rimage 7 : un paquete con implementación de algoritmos multi propósito para imágenes jpeg.
Provee métodos de lectura de archivos, filtros pasalto y pasabajo, un par de algoritmos de
detección de bordes (Sobel y Laplace), filtro por transformada de Fourier y de impresión
de imágenes por pantalla.
biOps es más abarcativo que los paquetes mencionados, tanto en ramas del procesamiento digital
de imágenes y diversidad de algoritmos, como en alternativas de implementación (interpolación
-capı́tulo 8- y generalidad en detección de bordes -capı́tulo 10-, por ejemplo). El paquete rima-
ge es, actualmente, el único que presenta algunos algoritmos multi propósito, pero no ha sido
actualizado desde principios de 2005.
ChangeLog
configure
data /
DESCRIPTION
inst /
LICENSE
man /
biOps-package . Rd
imgAdd . Rd
...
NAMESPACE
R/
arithmetics . R
2 http://cran.r-project.org/src/contrib/Descriptions/adimpro.html
3 http://www.wias-berlin.de/project-areas/stat/projects/aws.html
4 http://cran.r-project.org/src/contrib/Descriptions/edci.html
5 http://cran.r-project.org/src/contrib/Descriptions/PET.html
6 http://eivind.imm.dtu.dk/staff/ptoft/ptoft papers.html
7 http://cran.r-project.org/src/contrib/Descriptions/rimage.html
Capı́tulo 6. biOps: un paquete de procesamiento de imágenes para R 34
convolution . R
...
README
src /
arithmetics . c
convolution . c
...
data: archivos que pueden ser cargados con la función de R data(). Estos son representacio-
nes de objetos o código R. En nuestro caso incluimos un objeto que representa la imagen
del logo de la comunidad.
inst: Se ubican los directorios que requieren ser copiados en la instalación. En nuestro caso,
ubicamos aquı́ algunas imágenes de muestra.
man: páginas del manual. Cada función pública en R debe tener su correspondiente archivo
en este directorio, en un formato similar a LATEX, donde se indican (entre otros) tipos,
descripción y ejemplos de uso. El comando check de R usa estos archivos para correr los
ejemplos en cada función, y detectar posibles errores en la página de manual o en las
implementaciones.
En la figura 6.1 puede verse un diagrama con la organización del paquete. Cada rectángulo
representa una de nuestras divisiones: los nombres que se incluyen corresponden a los archivos
en código C, de los cuales el código R actúa como interfaz. Se indica además, en qué capı́tulo se
trata cada uno de estas divisiones.
6.3. Testing
Para verificar el correcto funcionamiento de los algoritmos implementados se utilizó un script, es-
crito en R, que permite correr casos de prueba evaluando los resultados obtenidos en la aplicación
de las funciones provistas por el paquete.
Un caso de prueba consiste de una matriz numérica que representa una posible imagen, de la
cual conocemos de antemano el resultado de una determinada operación. De esta manera, se
Capı́tulo 6. biOps: un paquete de procesamiento de imágenes para R 35
Esta metodologı́a se puso en práctica para aquellos algoritmos que consideramos susceptibles de
esta forma de testeo, en particular en los casos de las operaciones por pixel, aritméticas, lógicas,
por vecino, morfológicas y geométricas. Mientras que, por ejemplo, en el caso de la clasificación
de imágenes, donde intervienen factores probabilı́sticos y aleatorios, y los resultados están sujetos
a la interpretación del usuario según su necesidad, no fue posible su verificación mediante este
tipo de testeo.
En todos los casos se efectuaron pruebas y aplicaciones de la implementación con imágenes va-
riadas obteniendo resultados esperados. Por otra parte, desde su primera publicación, el paquete
ha estado a disposición de los usuarios quienes pueden hacer llegar sus reportes de uso a través
de la lista de correo de la comunidad R. Al momento, sólo se han recibido comentarios de algu-
nos inconvenientes con la instalación de biOps en el sistema operativo Windows, que han sido
subsanados en la recientemente liberada versión 0.2.
Capı́tulo 6. biOps: un paquete de procesamiento de imágenes para R 36
Con el objetivo de brindar una mejor experiencia de usuario, comenzamos con la implementación
de una interfaz gráfica de usuario para biOps, llamada biOpsGUI. Este paquete requiere para su
uso de RGtk2 8 , versión portada a R de GTK 9 , un conjunto de herramientas para crear interfaces
de usuario.
La interfaz gráfica estuvo fuera del planeamiento de este proyecto, sin embargo pudimos imple-
mentar funciones para mostrar una imagen, manteniendo su tamaño original, y utilidades para
visualizar las coordenadas y valores de los pixels de una imagen.
Es nuestro deseo el continuar desarrollando este paquete, como explicamos en la sección 14.1.
Los próximos capitulos desarrollan la teorı́a detrás de los algoritmos y los detalles de especifica-
ción e implementación. La distribución de capı́tulos es la siguiente:
Operaciones por pixel [Cap. 7]: Son, quizá, las modificaciones más simples que pueden
realizarse: el valor de un pixel destino sólo depende del correspondiente pixel fuente. Se
presentan algoritmos implementados mediante “tabla de reemplazos” o look-up tables (ma-
peo de valores en valores) y operaciones aritméticas y lógicas. Se introduce también una
representación gráfica de los valores de una imagen: los histogramas, útiles para ajus-
tar parámetros en diversos algoritmos. Por último, se desarrolla el concepto de ruido en
imágenes, y se describen dos formas de generarlo: Gaussiana e impulsiva.
Operaciones geométricas [Cap. 8]: Modifican la ubicación de los pixels mediante una trans-
formación geométrica. Se introduce el concepto de interpolación, necesaria para “cubrir”
vacı́os propios de estos mapeos. Si bien no es un proceso geométrico, es usado en muchas
de las transformaciones de este capı́tulo. Se detallan las operaciones de rotación, escalado,
espejado, recortado (crop), encogido (shrink ) y traslación.
Operaciones por vecino [Cap. 9]: Generan el pixel destino a partir del pixel fuente y
sus vecinos. Se introducen el concepto de convolución (suma con peso de los pixels de
una sección de imagen, llamada ventana) y los filtros que pueden ser aplicados con ella.
También se describen filtros no lineales: mediana, mı́nimo y máximo.
Algoritmos de detección de bordes [Cap. 10]: Los bordes son los lı́mites entre objetos, y
entre objetos y fondo en una imagen. Existen aplicaciones para su detección en muchas de
las ramas del procesamiento digital de imágenes. Se revisarán algoritmos sencillos y rápidos
(homogeneidad y diferencia), métodos clásicos basados en convolución (Sobel, Prewitt,
Roberts, etc.) y técnicas avanzadas (Shen Castan, Marr Hildreth, etc.).
8 http://cran.r-project.org/src/contrib/Descriptions/RGtk2.html
9 http://www.gtk.org
Capı́tulo 6. biOps: un paquete de procesamiento de imágenes para R 37
Operaciones morfológicas [Cap. 12]: Son operaciones matemáticas sobre una representación
de una imagen mediante un conjunto, y se utilizan para resaltar aspectos especı́ficos de la
forma. Se tratarán las operaciones básicas, para imágenes binarias y de escala de grises,
de erosión, por la cual se borran ciertos pixels, dilatación, donde se establece un patrón
alrededor de un pixel, y sus combinaciones: apertura y clausura.
Clasificación de imágenes [Cap. 13]: Se trata de obtener una nueva imagen, donde los pixels
han sido discriminados en diferentes categorı́as. Se estudian los conceptos de clasificación
supervisada y no supervisada, desarrollando los algoritmos no supervisados de Isodata y
K-Means, ofreciendo para este último varias alternativas de implementación.
output: se incluyen la salida de f uzz, con la opción -t, para los archivos de especificación
(como se vio en la subsección 3.5.1) y las salidas completas del profiling (introducido en la
sección 2.4 y ampliado en el apéndice A).
packages: algunos de los paquetes que se describieron en este escrito: fuzz, R y rGTK
Las operaciones por pixel son, quizá, las más simples de las modificaciones que puedan sufrir
las imágenes. Esto es porque, para determinar el valor de un pixel en la imagen destino, sólo es
necesario tener en cuenta el valor para el mismo pixel en la imagen fuente, independientemente
del resto de los valores para los demás componentes.
Componen también esta categorı́a las operaciones aritméticas y lógicas, manipulaciones naturales
que se realizan sobre valores numéricos.
Los histogramas son representaciones gráficas de la distribución del rango de valores de una
imagen, que tiene utilidad para determinar parámetros para muchas de las operaciones que se
implementaron en este trabajo.
El ruido es un vicio propio de cualquier señal, y las imágenes no escapan a este problema. En este
trabajo estudiaremos algunos métodos para eliminarlo y en este capı́tulo, dos para generarlo: el
Gaussiano y el impulsivo. Estos métodos son útiles para evaluar la validez de filtros de eliminación
o para mejorar otros algoritmos.
A priori, este tipo de procesamiento puede parecer banal, pero no debe minimizarse el potencial
que presenta, como trataremos de mostrar en este capı́tulo.
38
Capı́tulo 7. Operaciones por pixel 39
El primer grupo de algoritmos que analizaremos son los que utilizan una “tabla de reemplazos”
como estructura de datos, mejor definida en inglés como look-up table, o LUT . Responden a
transformaciones numéricas, descriptas genéricamente por la siguiente ecuación:
La ventaja de este tipo de implementaciones se basa en el ahorro del cálculo repetido: como la
LUT se llena completamente, no es necesario hacer reiteradas veces un mismo cálculo. El cálculo
realizado es constante, independientemente del tamaño de la imagen. La polı́tica seguida para los
valores que se exceden de los lı́mites permitidos para un pixel es la de forzar su ingreso ajustando
el valor al más cercano permitido. Ası́, en nuestro caso, todo valor que supere 255 (máximo valor
para un pixel) será ajustado a 255. Similarmente para los valores que desciendan más allá del
mı́nimo (en nuestro caso 0, que se llevan a este valor). Es importante notar que la misma imagen
que tomamos como parámetro puede usarse para llenar el buffer de la imagen de retorno.
Este proceso puede verse en la figura 7.1. Usar la misma imagen como entrada y salida trae
aparejado un ahorro importante en la cantidad de memoria utilizada.
Esta transformación numérica puede escribirse en notación de función, como veremos en las
aplicaciones de esta sección. Muchas veces resultan más fácil de visualizar si se las representa
gráficamente. Por eso acompañamos para algunos casos un mapeo: el eje horizontal representa
el valor del pixel de entrada, y el eje vertical el resultado de la aplicación de la operación.
Cualquier función que pueda ser descripta en términos matemáticos (y que mapee valores en
valores), puede ser implementada como una tabla de reemplazos. Para el trabajo hicimos una
elección arbitraria de ellas, incluyendo las que nos parecı́an más representativas y útiles. De
todas formas, queda la implementación de nuestra función en R llamada r look up table, por
la cual puede fácilmente extenderse este trabajo a la inclusión de alguna otra función deseada.
La sencillez del procedimiento queda reflejado en la implementación de esta función:
r_look _ u p _ t a bl e <- function ( imgdata , table ) {
for ( i in 1: length ( imgdata ) ) {
imgdata [ i ] <- table [ imgdata [ i ]+1]
}
imgdata
}
El contraste en una imagen es su distribución de pixels claros y oscuros. Las imágenes con
poco contraste son en general mayormente claras, mayormente oscuras o mayormente “medio
tono”. Aquellas con mayor contraste tienen regiones de claros y oscuros, dado que usan más
ampliamente el rango de valores.
El problema con las imágenes de alto contraste es que tienen grandes regiones de oscuros y de
claros. Por ejemplo, la fotografı́a de una persona parada delante de una ventana en un dı́a de
sol tiene alto contraste: la persona está oscura y la ventana brillante. Las imágenes con buen
contraste exhiben un amplio rango de valores de pixels. Ninguno domina exageradamente por
sobre el resto, sino que todo el rango de valores es utilizado.
0
n < min limit
f (x ) = x − min limit min limit ≤ x ≤ max limit
255 x > max limit
(7.2)
de algún orden de estas dos funciones no resulta en la misma imagen que al comienzo. Toma los
valores máximo y mı́nimo que deseamos que tenga la imagen resultado, y distribuye los valores
linealmente sobre esos parámetros:
La intensidad es el nivel de color (o de gris, para imágenes en escala de grises) de una imagen.
Visualmente, el cambio de la intensidad da una sensación de alteración en el brillo de la imagen.
Los procedimientos que implementamos (funciones imgIncreaseIntensity e imgDecreaseIntensity)
toman como parámetro el porcentaje de intensidad que deseamos modificar en la imagen en
cuestión. Las funciones subyacentes de estas transformaciones son:
Una de las más simples modificaciones que se suele realizar es la de inversión de los valores
de una imagen para obtener su negativo (imgNegative). La función relacionada y el gráfico de
mapeo se muestra en la figura 7.1.3.
f (x ) = 255 − x (7.6)
Negative
∆Image
∀ a : dom v • v 0 a = MaxValue − v a
width 0 = width
height 0 = height
Muchas veces es útil separar regiones de una imagen correspondientes a objetos que son de
nuestro interés con respecto a objetos que son parte del fondo de la imagen. El thresholding
(figura 7.1.3) es en general conveniente para este tipo de acción. Se establece un umbral o lı́mite
por el cual los valores que lo superen serán mapeados al valor máximo disponible, y los que no
al valor mı́nimo.
La modificación gamma se trata de un mapeo exponencial. Se usa para cambiar el rango dinámi-
co de una imagen. El resultado visual de esta aplicación es el de resaltar los valores con alta
intensidad en la imagen (figura 7.1.3).
Capı́tulo 7. Operaciones por pixel 43
(
0 x < thr value
f (x ) = (7.7)
255 x ≥ thr value
x 1/gamma
f (x ) = b( ) × 255c (7.8)
255
Como las imágenes digitales se componen de valores numéricos, resulta natural aplicar aritmética
sobre ellos. Estas operaciones en general son binarias, y pueden expresarse con la siguiente
ecuación:
c(x , y) = a(x , y)hoperacionib(x , y) (7.9)
BinaryOp
∆Image
op? : VALUE × VALUE " VALUE
input? : Image
Otras de las operaciones que implementamos son las de promedio (imgAverage), aunque esta
no necesariamente es una operación binaria: toma como parámetro una lista de imágenes de
la misma profundidad de color y calcula el valor promedio coordenada a coordenada, y la de
máximo (imgMaximum), que toma el máximo de cada coordenada entre dos imágenes y que se
usará en implementaciones que veremos en los próximos capı́tulos.
Las aplicaciones de estas funcionalidades son variadas. Por ejemplo, el promedio entre imágenes
se utiliza en la eliminación de ruido, pixels superfluos claros u oscuros que no son fiel reflejo
de la realidad. Estos “intrusos” aparecen en distintas intensidades y posiciones dentro de una
imagen (en general, puede asumirse que el ruido es aleatorio). Este hecho puede ser aprovechado
para eliminar el ruido: si se cuenta con una determinada cantidad de imágenes del mismo objeto
(como suele suceder con las fotos planetarias o satelitales, por ejemplo), se procede a obtener el
promedio de todas ellas:
a1 (x , y) + a2 (x , y) + ... + an (x , y)
r (x , y) = (7.10)
n
Se experimentan buenos resultados al promediar al menos tres o cuatro imágenes, aunque con
dos imágenes pueden obtenerse comportamientos aceptables.
La suma y diferencia contra imágenes constantes suele utilizarse también para la corrección de
brillo de una imagen. Esto está fuertemente relacionado con las operaciones de intensidad, vistas
en la sección anterior, ası́ como las operaciones de multiplicación y división, que modifican el
contraste de la imagen cuando son operadas contra imágenes constantes.
Capı́tulo 7. Operaciones por pixel 45
La implementación en C de estas operaciones aprovecha los operadores lógicos entre bits: & (∧),
| (∨) y ∧ (xor )
7.3. Histogramas
El histograma de una imagen se refiere al histograma de los valores de intesidad de sus pixels.
Esto es, un gráfico que muestra el número de pixels de una imagen en cada intensidad encontrada.
El uso de los histogramas es realmente amplio. Uno de los más comunes es decidir el valor por el
cual aplicar la operación de thresholding (7.1.3). Si es conveniente aplicar esta operación a una
imagen, es común que el histograma sea “separable” en dos grandes grupos de valores (lo que se
denomina histogramas bimodales). Entonces, un buen valor para pasarle a la función podrı́a ser
uno entre los dos “picos” que se darán en el histograma.
Dos operadores que están relacionados con los histogramas son la normalización de contraste
(estiramiento de los valores para que ocupen todo el rango, como se vio en 7.1.1), ya que para
que esta operación tenga sentido debe cumplirse que haya extremos en el rango de valores que
no estén siendo utilizados, y la ecualización de histogramas, métodos para modificar el rango
dinámico y el contraste de una imagen mediante la alteración de las intensidades del histograma,
ecualizaciones sobre las cuales no hemos hecho hincapié en este trabajo.
Todo proceso de señales tiene que tratar un evento aleatorio de fondo como es el ruido. Las
principales fuentes de ruido en las imágenes digitales se presentan durante la adquisición (digita-
lización) y/o la transmisión. No es parte de las señales ideales y puede ser causado por diversos
factores, entre ellos la variación en la sensibilidad de los detectores, alteraciones en el ambiente,
radiaciones, errores de transmisión, etc. Las caracterı́sticas del ruido dependen de su origen,
aunque lo mismo ocurre para el operador que mejor reduce sus efectos.
La generación de ruido consiste en corromper deliberadamente una imagen. Esto puede reali-
zarse, por ejemplo, para probar la resistencia de algún operador al ruido o de intentar mejorar
los filtros existentes para la eliminación del mismo.
La caracterización del ruido se hace mediante la función probabilı́stica de densidad (PDF , por
sus siglas en inglés de probability density function). Dos de los más comunes los presentaremos
a continuación, por haber sido los elegidos para este trabajo: el ruido Gaussiano y el ruido
impulsivo (salt & pepper o sal y pimienta).
1 2
/2σ 2
p(z ) = √ × e −(z −µ) (7.11)
2πσ
donde µ representa la media y σ el desvı́o estándar. Para introducir ruido de este tipo (función
imgGaussianNoise) utilizamos el método de Box-Muller, el cual usa una técnica de transforma-
da inversa para pasar de dos variables aleatorias uniformemente distribuidas a dos aleatorias
normales de media 0 y varianza 1, X e Y , las cuales pueden ser fácilmente modificables para los
diferentes valores de media y varianza (σ 2 ) usando la siguiente relación:
√
X 0 = µ + σ2 × X (7.12a)
Capı́tulo 7. Operaciones por pixel 47
√
Y 0 = µ + σ2 × Y (7.12b)
estas variables se suman a los pixels de a dos por vez, X 0 para el primero e Y 0 para el segundo.
El ruido impulsivo, también llamado salt & pepper se caracteriza por ocurrencias aleatorias de
valores mı́nimos o máximos en los canales de la imagen. Para imágenes de un solo canal, estos
valores corresponden a las tonalidades de blanco y negro, con lo que visualmente resulta en
“salpicados” blancos y negros, lo que da origen al nombre que recibe.
Operaciones geométricas
Los procesos geométricos modifican la ubicación de los pixels basados en alguna transformación
geométrica. La idea es mover los pixels alrededor de la imagen sin alterar, idealmente, sus valores.
Sin embargo, si algún proceso intenta mapear un pixel desde una ubicación que no existe, se ge-
nerará un nuevo pixel. Este proceso de generación se conoce como interpolación. La interpolación
propiamente dicha no es un proceso geométrico, pero es usado en muchas de las transformaciones
que veremos en este capı́tulo. Se presentarán los conceptos básicos de los procesos geométricos
y las diferentes funciones que se utilizaron en la implementación de los métodos.
En esta sección se detallan la implementación de las funciones de rotar, escalar, espejar, recortar
(crop), encoger y trasladar ; para muchas de las cuales, como veremos, puede elegirse el método
de interpolación a aplicar.
Transferir el pixel de entrada hacia un pixel de salida a través de una función se denomina mapeo
“hacia adelante” (forward mapping). Esta alternativa trae aparejado ciertos problemas: agujeros
y solapamientos. Los agujeros son pixels cuyos valores no están definidos, y el pixel destino no
tiene en estos casos su correspondiente pixel fuente. Los solapamientos ocurren cuando dos (o
más) pixels se mapean al mismo pixel de destino. ¿Qué valor se le asigna en esos casos?
Para resolver estos problemas se utiliza otro tipo de mapeo, “hacia atrás” (reverse mapping).
Notar que en este caso surgen los mismos inconvenientes que en el mapeo “hacia adelante”, pero
no son problemas ya que cada pixel de la imagen destino tiene un valor asociado (es decir, los
agujeros quedarán en la imagen fuente, y los solapamientos no son problema al quedar los pixels
de la imagen destino con el mismo valor).
48
Capı́tulo 8. Operaciones geométricas 49
Por esta razón es que se hace imprescindible el uso del mapeo “hacia atrás”, que se utilizará en
las implementaciones de las operaciones geométricas de este capı́tulo.
8.2. Interpolación
El mapeo a veces genera problemas. Por ejemplo: ¿qué pasa si nuestra función de mapeo calcula
una dirección de pixel no entera? Para que esto resulte más visible, consideremos la siguiente
transformación:
xd yd
xs = ys =
2 2
xs e ys denotan las coordenadas x e y del pixel fuente (respectivamente) y xd e yd las del pixel
destino.
El pixel para (0, 0) del destino vendrá del (0, 0) del fuente. Pero, ¿qué pasa con el pixel (1, 1)
del destino? La transformación reversa buscarı́a en (0.5, 0.5) del fuente, que no existe.
Para este tipo de problemas disponemos de una técnica que se denomina interpolación, un
proceso para generar valores de direcciones que se ubican “entre pixels”. Existen varias técnicas
de interpolación; la más adecuada para usar depende mucho de la aplicación en cuestión: los
algoritmos más sofisticados mejoran la calidad de la imagen, pero hacen el proceso más complejo
y computacionalmente más costoso (y lo opuesto pasa para los algoritmos más sencillos).
A continuación presentamos los métodos de interpolación que pueden aplicarse en las operaciones
(que lo requieren) de este capı́tulo.
La idea para el vecino más cercano es la de asignar como salida el pixel que minimice la distancia
a la dirección generada (sin considerar en absoluto el resto de los pixels). La implementación de
esta técnica consiste en redondear la fracción obtenida al entero más cercano. La suma en 0.5 y el
redondeo logran este cometido. En el siguiente código C puede verse una posible implementación:
fx = map ( x_dest ) ;
fy = map ( y_dest ) ;
x_src = ( int ) ( fx + 0.5) ;
y_src = ( int ) ( fy + 0.5) ;
Como no se genera ningún pixel, todos los valores son obtenidos del conjunto de entrada. En
general, a mayor cantidad de pixels asignados a uno mismo de entrada, mayor es la imprecisión
que se logra en la imagen final. Esto puede verse, por ejemplo, en el escalado de imágenes cuando
el factor de escala es muy grande.
Capı́tulo 8. Operaciones geométricas 50
Otra técnica común de interpolación es la bilineal. El pixel generado es una suma de pesos de los
cuatro vecinos más cercanos. Los pesos son determinados linealmente. Cada peso es directamente
proporcional a la distancia a cada pixel existente.
Esta técnica requiere tres interpolaciones lineales. Una de las formas de proceder, como veremos
en el siguiente código, es interpolar linealmente el par de pixels ubicado más arriba y el par
ubicado más abajo. Con ellos, se realiza la tercera interpolación lineal, para obtener el valor
deseado:
pesoEO = fx - floor ( x ) ;
pesoNS = fy - floor ( y ) ;
/* 1 ra interpolacion */
EOarriba = NO + pesoEO * ( NE - NO ) ;
/* 2 da interpolacion */
EOabajo = SO + pesoEO * ( SE - SO ) ;
/* 3 ra interpolacion */
dest = EOarriba + pesoNS * ( EOabajo - EOarriba ) ;
La interpolación bilineal resulta en una imagen más suave y lisa, en comparación a la que se
obtiene con la interpolación por vecino más cercano. Sin embargo, al realizar tres interpolaciones
lineales, requiere claramente más computación que la mencionada anteriormente.
El método del vecino más cercano requiere un pixel de entrada. La interpolación bilineal requiere
cuatro pixels de entrada. En este caso, veremos un método de orden más alto, que requiere de
los 16 pixels más cercanos. Se trata de B-Spline. La función está definida ası́:
1 2
| x |3 − | x |2 +
0 ≤| x |< 1
2 3
f (x ) = 1 3 2
4 (8.1)
−
6 | x | + | x | −2 | x | + 1 ≤| x |< 2
3
0 2 ≤| x |
El principio es el mismo que para el resto de las interpolaciones de alto orden (que, salvo por la
convolucional cúbica, no serán profundizadas en este trabajo): la función se centra en el punto de
interés y sus valores en los puntos de muestra son multiplicados por los valores de la función. La
suma de estos productos es el nuevo pixel generado. Se opera primero en cada fila, obteniendo
un resultado por cada una. Estos valores vuelven a procesarse, obteniendo un solo valor, que
corresponde al resultado de la interpolación.
Capı́tulo 8. Operaciones geométricas 51
Al igual que B-Spline, la interpolación cúbica utiliza los 16 pixels más cercanos para generar el
nuevo pixel. En este caso, la familia de funciones está definida de la siguiente manera:
(a + 2) | x |3 −(a + 3) | x |2 +1 0 ≤| x |< 1
f (x ) = a | x |3 −5a | x |2 +8a | x | −4a 1 ≤| x |< 2 (8.2)
0 2 ≤| x |
El valor de la constante a es arbitrario, aunque se sugieren -0.5, -0.75 y -1.0. Las pruebas han
demostrado que para resultados visuales, el valor -1.0 es la mejor opción.
Este método es quizá el que más agudice la diferencia de valores. Una de las caracterı́sticas
notables es que puede tomar valores negativos o excederse de nuestro rango de valores. La salida
en estos casos deberá ser alterada para satisfacer nuestras especificaciones.
x 3 + 2x 2 + 3x + 4
= (x 3 + 2x 2 + 3x ) + 4
= x (x 2 + 2x + 3) + 4
= x ((x 2 + 2x ) + 3) + 4
= x (x (x + 2) + 3) + 4
= (((x + 2)x + 3)x + 4)
8.3.1. Escalar
El escalar es la función por la cual se lleva la imagen a un tamaño (mayor) deseado. Esta
operación recibe muchos nombres: magnificar, zoom, estiramiento, etc. Hay dos cosas que deben
tenerse en cuenta cuando escalamos: la primera es que no se mejorará la resolución de la imagen
original. No tenemos más información de la que nos brinda la imagen original. Lo que sı́ puede
hacerse es una interpolación que promedie de alguna manera e “invente” esos datos que estarán
faltando. La segunda cuestión es que, a menos que todos los escalados se realicen a partir de la
imagen original, los resultados serán siempre más degradados. Al escalar, se están creando pixels
Capı́tulo 8. Operaciones geométricas 52
“artificiales”, con lo que las sucesivas aplicaciones generarán nuevos pixels a partir de estos, ya
creados anteriormente.
Esta operación, y aquellas que requieren de interpolación para determinar sus valores, fueron
implementadas utilizando las operaciones mencionadas en la sección anterior. Para el caso de
escalar una imagen, puede llamarse a la función imgScale con, además de la imagen en cuestión y
los factores de escala, alguno de las siguientes secuencia de caracteres, que identifican la operación
de interpolación a utilizar:
“bilinear” (bilineal)
“spline” (B-Spline)
Esta identificación de métodos es una constante a lo largo del trabajo. Es posible también invocar
directamente a un método en particular: esto se hace a través de las funciones
imgBilinearScale (bilineal)
imgSplineScale (B-Spline)
Estas operaciones no restringen su utilización para reducir el tamaño de una imagen; aunque
para ello, como veremos, es conveniente el uso de funciones especı́ficas para encoger.
8.3.2. Encoger
En esta sección se analizan dos algoritmos implementados para la reducción del tamaño de una
imagen. El uso tı́pico de esta operación es la creación de imágenes en miniatura (comúnmente
conocidas como thumbnails), y la idea que manejan es la de representar un conjunto de pixels
con un único pixel. Para ello disponemos de varias técnicas, entre las que elegimos las dos más
usadas: la de representación por mediana y por promedio.
Ambas técnicas toman una ventana de n × n que van “deslizando” por sobre la imagen. El
valor de n depende del factor de reducción que busquemos en la imagen: estos son inversamente
proporcionales, puesto que se requiere una ventana más grande para determinar una cantidad
menor de pixels.
Capı́tulo 8. Operaciones geométricas 53
Esto tiene el mismo orden que quick sort, O(n × log(n)). Podemos notar que si k es menor que
la longitud de L1, no es necesario ordenar L3. Lo mismo si k es mayor que la concatenación de
L1 y L2. De esta forma podemos ahorrar un poco de cálculo. También podemos ahorrar (pero
no mucho) si no hacemos la concatenación, simplemente mirando en el lugar que corresponda:
quick_select ( L ) {
elegir x en L
particionar L en L1 <x , L2 =x , L3 > x
if ( k <= longitud ( L1 ) ) {
quick_sort ( L1 )
devolver k-esimo de L1
} else if ( k > longitud ( L1 ) + longitud ( L2 ) ) {
quick_sort ( L3 )
devolver ( k - longitud ( L1 ) - longitud ( L2 ) ) - esimo de L3
} else {
devolver x
}
}
Esto sigue siendo O(n×log(n)), pero con una constante menor. Podemos hacer una nueva mejora:
el código de cada rama if ordena la lista y devuelve la posición que corresponde, exactamente el
problema que estamos resolviendo. Luego, podemos hacer las mismas mejoras que hasta ahora:
quick_select (L , k ) {
elegir x en L
particionar L en L1 <x , L2 =x , L3 > x
if ( k <= longitud ( L1 ) ) {
devolver quick_select ( L1 , k )
} else if ( k > longitud ( L1 ) + longitud ( L2 ) ) {
devolver quick_select ( L3 , k - longitud ( L1 ) - longitud ( L2 ) )
} else {
devolver x
}
}
8.3.3. Rotar
La operación de rotar cambiará las dimensiones de la imagen para que ésta pueda verse completa-
mente, completando los vacı́os que deje la rotación con algún color predeterminado (tı́picamente
negro -caso de nuestra implementación-). En la figura 8.1 pueden verse los sectores de la imagen
que no tendrán valor asociado ante una rotación de A grados. Además se indica con diferentes
colores los altos y anchos de la imagen original y de la rotada.
Una vez que se determinaron estos valores, deben ser interpolados. Para ello implementamos,
como en el resto de las operaciones que lo requerı́an, funciones con las diversas interpolaciones:
imgNearestNeighborRotate, imgBilinearRotate, imgSplineRotate e imgCubicRotate. Lo impor-
tante para esta operación es considerar los valores de xs e ys que caen dentro de los lı́mites de
la imagen fuente.
Capı́tulo 8. Operaciones geométricas 55
Si el ángulo de rotación α es un múltiplo de 90o , no es una buena idea aplicar las ecuaciones vistas
anteriormente, ya que lo único que se precisa es una reubicación de pixels; más precisamente una
trasposición de filas y columnas. Para ello se implementaron las rotaciones de 90o en sentido
horario (imgRotate90Clockwise) y antihorario (imgRotate90CounterClockwise).
8.3.4. Espejar
Espejar una imagen es, simplemente, darla vuelta sobre algunos de los ejes. El espejado horizontal
(imgHorizontalMirroring) voltea la imagen en el eje y. Ası́, los objetos que antes aparecı́an a la iz-
quierda de la imagen, ahora aparecerán a la derecha. El espejado vertical (imgVerticalMirroring)
da vuelta la imagen en el eje x , con lo que los objetos que aparecı́an en la parte superior de la
imagen, aparecerán ahora en la parte inferior, y viceversa.
Es importante destacar que en esta operación no hay intervención de interpolación, puesto que
el espejado es un mero reacomodo de la posición de los pixels en la imagen.
En la figura 8.2 puede verse la imagen original y sus espejados en ambos ejes.
8.3.5. Trasladar
La traslación consiste en mover un sector de una imagen a otra parte. Para ello debe utilizarse un
buffer secundario, de modo de no sobreescribir información que sea útil en la misma operación.
El uso de un único buffer para este tipo de operaciones es un error común que puede causar
operaciones recursivas sobre la imagen.
En la figura 8.3 puede verse una imagen de 512 por 512 pixels (reducida para este impreso),
donde se ha trasladado un rectángulo de 110 (ancho) por 40 (alto) pixels desde la posición (245,
Capı́tulo 8. Operaciones geométricas 56
245) hasta la posición (245, 285), produciendo la duplicación de ojos de la bella Lenna, famosa
imagen utilizada en procesamiento de imágenes. En la figura 8.3(b) se demarcan los sectores de
destino y fuente de la operación.
(c) Trasladado
8.3.6. Recortar
El recortado, o crop, es quizá la operación más sencilla de entre las geométricas. Consiste en
reducir una imagen a una parte de la misma. El tamaño en general es alterado y se requiere de
un segundo buffer para almacenar el resultado. Es una operación muy común a la hora de hacer
zoom de una imagen o, simplemente, de eliminar bordes que no son deseados. La implementación
de esta función, imgCrop, toma como parámetros las coordenadas de inicio del rectángulo que
deseamos conservar, y el ancho y alto correspondientes. Notar que este ancho y alto será el
tamaño final de la imagen, como puede verse en la especificación Z de la operación:
Capı́tulo 8. Operaciones geométricas 57
ImageCrop
∆Image
x ?, y? : N
width?, height? : N
0 ≤ x? < width
0 ≤ y? < height
0 ≤ width? < (width − x? + 1)
0 ≤ height? < (height − y? + 1)
width 0 = width?
height 0 = height?
∀ x , y : N | x ∈ 0 . . (width? − 1) ∧ y ∈ 0 . . (height? − 1) •
v 0 (x , y) = v (x ? + x , y? + y)
Podemos notar en este esquema, que se exige que el ancho y alto que se pasan por parámetro
(width? y height? en este caso) no se excedan de los lı́mites que disponemos en la imagen
(habiendo fijado las coordenadas correspondientes a la margen superior izquierda del rectángulo
que deseamos conservar).
Capı́tulo 9
Las operaciones por vecino, también denominadas procesos de imágenes por área, toman por
entrada un pixel y los pixels alrededor de éste para generar el valor del pixel de salida.
Entre estas operaciones tenemos los llamados filtros espaciales lineales que trabajan sobre una
ventana de la imagen y una máscara o kernel del tamaño de esa ventana. El término filtro proviene
del procesamiento de señales en el espacio de frecuencias, a partir de la transformada de Fourier,
que veremos más detalladamente en 11.3. Aquı́ veremos filtros que operan directamente en los
pixels de la imagen, implementados a partir de la convolución de la imagen de entrada con un
kernel predefinido.
Describiremos algunos filtros no lineales, que también operan sobre ventanas de la imagen. Sin
embargo, la operación de filtrado se basa en los valores de los pixels en la ventana y no se usa
una máscara con coeficientes para operar con ellos. Es el caso de los filtros por mediana, mı́nimo
y máximo.
9.1. Convolución
Entonces se mantiene una ventana corrediza que se centra en cada pixel de la imagen de entrada
y se generan nuevos pixels de salida. Cada nuevo valor se calcula multiplicando los pixels en
la ventana por su correspodiente peso en la matriz de convolución y sumando esos productos
(figura 9.1). Es importante guardar los valores obtenidos en una nueva imagen, para calcular los
subsiguientes valores a partir de los pixels originales de la imagen.
La suma de los pesos de una máscara de convolución afectan la intensidad global de la imagen
resultante. Muchas máscaras tienen coeficientes cuya suma es igual a 1. En estos casos la imagen
58
Capı́tulo 9. Operaciones por vecino 59
producto de la convolución tendrá el mismo promedio de intensidad que la original. Otras másca-
ras (por ejemplo las de detección de bordes, ver 10.3) tienen coeficientes negativos y suman 0.
De esta forma se pueden obtener valores de pixel negativos. A ese valor se le suma una constante
(como la mitad de la máxima intensidad); si el resultado todavı́a es negativo, el pixel se pone a
0.
a
X b
X
g(x , y) = w (s, t)f (x + s, y + t) (9.1)
s=−a t=−b
(m − 1) (n − 1)
donde a = yb= .
2 2
Uno de los problemas que se plantean al momento de implementar filtros por convolución es
cómo tratar los bordes de la imagen. Cuando la ventana de convolución se centra en el pixel
(0, 0), qué valores se deben multiplicar con los coeficientes de la máscara que quedan fuera de la
imagen? Existen distintas alternativas para manejar esta situación.
Una es tratar las celdas vacı́as de la ventana como ceros (zero padding). Es una solución fácil,
pero le resta importancia a los bordes de la imagen.
Otra posibilidad es iniciar la convolución en la primera posición tal que la ventana queda to-
talmente dentro de la imagen. Es decir, si la máscara es 3 × 3 empezarı́a en (1, 1). Es simple
de implementar, y se suele copiar los bordes de la convolución para obtener una imagen con las
mismas dimensiones que la original.
Hay alternativas que se basan en extender la imagen original antes de aplicar el filtro. Una forma
es duplicar los bordes. Si se usa una máscara 3 × 3, se duplican las filas de los bordes superior
Capı́tulo 9. Operaciones por vecino 60
e inferior, y las columnas de los bordes izquierdo y derecho. Esta es la variante que elegimos en
nuestra implementación.
Otro método es “envolver” (wrap) la imagen. O sea, si quisiéramos aplicar una convolución a
una imagen de 512 × 512 con una máscara 3 × 3, la primera ventana operarı́a sobre los pixels
(511, 511), (0, 511), (1, 511), (511, 0), (0, 0), (1, 0), (511, 1), (0, 1), (1, 1).
Algo para tener en cuenta también es el hecho de que a medida que crece la máscara de convo-
lución crece exponencialmente la carga computacional.
Convolution
∆Image
mask ? : Mask
op? : Mask × VALUES " VALUE
bias? : VALUE
width 0 = width
height 0 = height
∀ c : dom v 0 • v 0 (c) =
clipPixel (op? (mask ?, getSlice (v , width, height, first c,
second c, mask ?.width, mask ?.height)) + bias?)
donde op? es la función que aplica la convolución propiamente dicha a partir de la máscara dada
(mask ?) y la ventana de la imagen con las dimensiones de la máscara correspondiente a un pixel
dado (el resultado de getSlice); al valor devuelto por op? se le suma bias?, un valor constante,
como se describió anteriormente. Y finalmente clipPixel garantiza que el valor del pixel final
esté en el rango válido.
Al trabajar con imágenes color tenemos dos opciones. Una, operar sobre el canal de intensidad
en el modelo de color HSI. La otra es operar sobre cada uno de los canales de una imagen
RGB. El primero tiene la ventaja de que preserva la información de tonos original, pero requiere
conversiones de un modelo a otro. El método más popular es el de hacer la convolución sobre los
canales RGB, y es la alternativa que seguimos. Qué técnica es mejor depende del objetivo de la
aplicación y los filtros.
Nuestro paquete ofrece una función de convolución, imgConvolve, que aplica el filtro especi-
ficado por una máscara de entrada, definida por el usuario, sobre la imagen dada. También
se implementaron algunos filtros predefinidos para blurring (imgBlur en biOps) y sharpening
(imgSharpen).
9.1.1. Blurring
En general se utilizan máscaras cuyos coeficientes son iguales. En una máscara 3 × 3 todos los
elementos son iguales a 1/9; en una 5×5, a 1/25. Como se puede ver se trata de un promedio entre
los vecinos. Cuanto mayor es la máscara, mayor será el efecto y el tiempo de cálculo requerido.
El blurring es una forma efectiva de reducir el ruido Gaussiano de una imagen, no ası́ para ruido
impulsivo (i.e. cuando no hay una correlación con el valor original del pixel). Además se reducen
los valores extremos en cada ventana, y por lo tanto tiende a disminuir el contraste de la imagen.
Otra máscara usada es la que elige los coeficientes de tal manera de no afectar el promedio de
intensidad de la imagen, aproximando un perfil Gaussiano y haciendo la suma de los coeficientes
igual a 1.
El problema de usar filtros pasobajo para reducir el ruido de una imagen es que los bordes de los
objetos en la imagen se tornan difusos. Cuando se busca filtrar el ruido de una imagen el filtro
de mediana puede ser una mejor alternativa, ya que preserva mejor los bordes.
9.1.2. Sharpening
El sharpening produce el efecto opuesto al blurring. El sharpening enfatiza los detalles de una
imagen. Si una imagen es difusa puede llevarse a un nivel aceptable mediante este filtro. Claro
que también tiende a amplificar el ruido y se incrementa el contraste.
Cuando α = 1, el resultado es una imagen pasoalto. Si α > 1, una fracción de la imagen original se
añade al resultado del pasoalto, lo que restablece algunos de los componentes de baja frecuencia.
El filtro high-boost retiene más información del fondo de la imagen original. A medida que se
Capı́tulo 9. Operaciones por vecino 62
incrementa α, la imagen se torna más clara, ya que una mayor proporción de la imagen original
se suma al resultado y entonces los valores de los pixels son mayores.
Ya hemos mencionado que un filtro pasobajo puede resultar útil para remover ruido Gaussiano,
pero no impulsivo. Una imagen con ruido impulsivo tiene pixels corruptos con valores de inten-
sidad de 0 o 255. Una manera efectiva de remover el ruido impulsivo es el filtro por mediana
(figura 9.3). Una de las ventajas de este filtro sobre el pasobajo es que preserva mejor los bordes
y detalles.
El filtro por mediana se aplica llevando una ventana corrediza sobre la imagen original y or-
denando los pixels en la ventana en orden ascendente. La mediana (el pixel del centro en ese
ordenamiento) será el valor del pixel correspodiente en la imagen resultado. La función principal
es forzar a los puntos cuya intensidad es muy distinta de sus vecinos a parecerse a ellos, elimi-
nando picos de intensidad. Al implementar el algoritmo surge el mismo inconveniente que con la
convolución: cómo tratar las celdas de la ventana que no caen dentro de la imagen? Además de
las alternativas presentadas, se puede considerar una más, que fue la elegida en nuestra imple-
mentación (imgBlockMedianFilter ): ignorar las celdas vacı́as y operar sólo sobre los valores de
la imagen en la ventana.
El procedimiento para filtrar imágenes color es diferente. El algoritmo para ordenar los pixels
debe ser distinto. Una posibilidad serı́a aplicar el filtro descripto en cada uno de los canales y
combinar las salidas. Esto tiene el problema de que se pierde la correlación entre los componentes
de color. Además una de las caracterı́sticas del filtro es que no se introducen nuevos valores en
la salida, sino que cada valor de pixel en el resultado se corresponde con alguno en la imagen de
entrada.
Capı́tulo 9. Operaciones por vecino 63
Sin embargo hay una propiedad de la mediana que podemos aprovechar en este caso. La suma
de las diferencias entre un valor de mediana y todos los demás valores en un conjunto será menor
que la suma de las diferencias para cualquier otro valor del conjunto:
N
X N
X
| xmed − xi | ≤ | y − xi | (9.3)
i=1 i=1
Entonces ahora podemos considerar sumas de diferencias en lugar de preocuparnos por cómo
ordenar los pixels color. Para cada pixel en nuestra ventana sumamos la diferencia entre los
componentes rojo, verde y azul con el resto de los pixels. El pixel con la menor suma es el valor
de salida. Es decir que para cada uno de los N pixels de la ventana se debe calcular la suma de
las diferencias para cada componente.
N
X
Distancei = (| redi − redj | + | greeni − greenj | + | bluei − bluej |) (9.4)
j =1
Donde i es el pixel que se está procesando y j representa los demás pixels en la ventana; la menor
distancia, i , corresponderá al pixel de salida xi .
Esta técnica funciona bien tanto para ventanas de dimensiones impares como pares, aunque
tradicionalmente se utilizan dimensiones impares.
El filtro por mı́nimo remueve picos de blanco. De esta manera, un pixel es representado por el
más oscuro de la ventana, y por lo tanto la intensidad de la imagen resultante se verá reducida
respecto de la original. El filtro por máximo remueve los picos oscuros, y la intensidad de la
imagen de salida será mayor que la de la original.
Ambos filtros fallan a la hora de remover ruido impulsivo, ya que cada uno realza los picos
negativos (mı́nimo) o los picos positivos (máximo). Una cascada de filtros por máximo y mı́nimo
pueden servir para eliminar este ruido ”salt & pepper”. Un filtro por máximo seguido por uno
por mı́nimo se llama filtro de closing, mientras que uno por mı́nimo seguido por uno por máximo
es llamado filtro de opening.
Capı́tulo 10
Algoritmos de detección de
bordes
Los bordes en una imagen suministran mucha información acerca de la misma. Por ejemplo
marcan los lı́mites entre un objeto y el fondo, y entre distintos objetos. Es decir que si se pueden
identificar los bordes con precisión, se pueden localizar objetos y determinar algunas propiedades
básicas como área, perı́metro o forma.
Existen numerosas aplicaciones para la detección de bordes, por ejemplo en visión de compu-
tadoras o en el proceso de identificar regiones en una imagen (segmentación).
A lo largo de esta sección revisamos distintos algoritmos para la detección de bordes: algunos
métodos sencillos y rápidos, los métodos tradicionales basados en máscaras de convolución y
también algunas técnicas avanzadas.
10.1. Generalidades
Diremos que existe un borde donde la intensidad de la imagen pasa de un valor bajo a uno alto
o viceversa. Como los bordes consisten principalmente de frecuencias altas, podrı́amos detectar
bordes aplicando un filtro pasoalto en el espacio de Fourier (ver 11.4), o aplicando una convolución
con una máscara apropiada en la representación espacial. En la práctica se suele utilizar esta
última alternativa, ya que es computacionalmente menos costosa y se obtienen muchas veces
mejores resultados.
Hay un número infinito de orientaciones, anchos y formas de bordes. Y hay muchas técnicas
para su detección, cada una con sus ventajas y desventajas. En algunos casos la experimentación
ayuda a determinar cuál es la mejor técnica para aplicar en cada caso.
64
Capı́tulo 10. Algoritmos de detección de bordes 65
especifica sólo uno, los pixels cuyos valores estén por encima se setean al máximo valor posible,
y aquellos que estén por debajo se setean a cero. Si se definen un valor de threshold superior y
uno inferior, los valores por debajo del inferior se setean a cero, aquellos entre los dos valores
dados no cambian y los que están por encima del valor superior se setean al máximo posible.
Los detectores de bordes más simples y rápidos determinan el máximo valor a partir de una serie
de diferencias entre pixels. El operador de homogeneidad calcula la diferencia entre cada uno de
los 8 pixels y el del centro de una ventana de 3 × 3. El valor del pixel de salida es el máximo
entre los valores absolutos de las diferencias (ver figura 10.1). Puede ser necesario utilizar un
offset para acomodar los valores en la imagen final. En biOps está implementado bajo el nombre
imgHomogeneityEdgeDetection.
res = max {| 11−11 |, | 11−13 |, | 11−15 |, | 11−16 |, | 11−11 |, | 11−16 |, | 11−12 |, | 11−11 |} = 5
Similar al operador de homogeneidad se define el detector de bordes por diferencia (en biOps,
imgDifferenceEdgeDetection). Es más rápido porque requiere cuatro restas por pixel. Las dife-
rencias que se calculan son superior izquierda - inferior derecha, medio izquierda - medio derecha,
inferior izquierda - superior derecha, y medio superior - medio inferior (figura 10.2).
res = max {| 11 − 11 |, | 13 − 12 |, | 15 − 16 |, | 11 − 16 |} = 5
Estos métodos son rápidos, pero a veces se necesitan técnicas más complejas. En la figura 10.3
se puede ver un ejemplo de una aplicación del operador por diferencia.
Los operadores de gradiente encuentran bordes horizontales y verticales, es decir que podemos
usar las derivadas de la imagen. Se puede ver que la posición de los bordes puede estimarse a
partir del máximo de la primera derivada o a partir de los llamados zero-crossings de la segunda
derivada (puntos en que la función cruza el cero). Por lo tanto, necesitamos una forma de calcular
la derivada de una imagen.
Para una función discreta de una dimensión la primera derivada se puede aproximar por:
Capı́tulo 10. Algoritmos de detección de bordes 67
df (i )
= f (i + 1) − f (i ) (10.1)
d (i )
El cálculo de esta fórmula es equivalente a una convolución de la función con [-1 1]. De mane-
ra similar, la segunda derivada se puede estimar convolviendo f (i ) con [1 -2 1]. Entonces los
operadores por gradiente los podemos obtener por convolución.
Existen diferentes máscaras de detección de bordes basadas en la fórmula descripta, que nos
permiten calcular la primera o segunda derivada de una imagen. Hay dos aproximaciones para
estimar la primera derivada de una imagen: gradient edge detection y compass edge detection.
Los coeficientes de estas máscaras suman 0. Si esto no fuera ası́, entonces al convolver con una
imagen constante obtendrı́amos una imagen distinta de 0, lo que implicarı́a erronéamente la
existencia de bordes.
Es una de las técnicas más utilizadas. Se aplican dos máscaras de convolución sobre la imagen,
una que estima el gradiente en la dirección de x (Gx ), y otra en la dirección de y (Gy ). La
magnitud absoluta del gradiente está dada por:
q
| G |= Gx2 + Gy2 (10.2)
| G |=| Gx | + | Gy | (10.3)
Las máscaras más comunes, y que fueron implementadas, son Sobel (imgSobel , ver figura 10.5),
Roberts (imgRoberts), Prewitt (imgPrewitt) y Frei-Chen (imgFreiChen). A continuación se des-
criben las correspondientes máscaras, tanto para la dirección horizontal como vertical. Notar que
una es la rotación de 90o de la otra.
Capı́tulo 10. Algoritmos de detección de bordes 68
1 0 −1 1 2 1
Sobelx =
2 0 −2
Sobely =
0 0 0
1 0 −1 −1 −2 −1
0 0 −1 0 0 0
Robertsx =
0 1 0
Robertsy =
0 1 0
0 0 0 0 0 −1
1 0 −1 1 1 1
Prewittx =
1 0 −1
Prewitty =
0 0 0
1 0 −1 −1 −1 −1
√
1 0 −1 1 2 1
√ √
FreiChenx = 2
0 − 2 FreiCheny =
0 0 0
√
1 0 −1 −1 − 2 −1
Los operadores por compass gradient encuentran bordes en ocho direcciones diferentes. Esto
requiere convolver la imagen con un conjunto de (en general ocho) máscaras, cada una sensible
a distintas orientaciones. La salida de la operación corresponde al máximo de las convoluciones
aplicadas.
Hay que tener en cuenta que cuanto menor son las máscaras, son más sensibles al ruido, mien-
tras que las máscaras más grandes no pueden resolver detalles finos, además de ser el cálculo
computacionalmente más costoso.
Capı́tulo 10. Algoritmos de detección de bordes 69
1 1 −1
Prewitt =
1 −2 −1
1 1 −1
5 −3 −3
Kirsch =
5 0 −3
5 −3 −3
1 0 −1
Robinson3Level =
1 0 −1
1 0 −1
1 0 −1
Robinson5Level =
2 0 −2
1 0 −1
Los operadores por gradiente vistos hasta aquı́ producen una respuesta grande a lo largo del área
donde hay bordes. Idealmente, un detector de bordes deberı́a determinar el centro de los bordes.
Este concepto se denomina localización. Si un detector de bordes devuelve bordes de varios pixels
de ancho es difı́cil definir el centro de los bordes. Se hace necesario aplicar un proceso de thinning
para reducir el ancho de los bordes a un pixel. Los detectores de bordes basados en la segunda
derivada proveen una mejor localización, importante en visión de máquinas.
Otra ventaja de los operadores de segunda derivada es que los bordes detectados son curvas
cerradas, importante para el proceso de segmentación. Además, no responden ante áreas de
variaciones lineales leves en la intensidad.
Un problema con Laplacian es que es un operador susceptible al ruido, y entonces los zero-
crossings pueden indicar más bordes que los esperados. En estos casos se debe aplicar un threshold
para filtrar el resultado.
Capı́tulo 10. Algoritmos de detección de bordes 70
1 x 2 + y2 2
+y 2 )/2σ 2
LoG(x , y) = 1 − e −(x (10.5)
πσ 4 2σ 2
Cuanto más ancha sea la función, más ancho serán los bordes detectados; una función más
angosta detectará bordes más finos y mayor detalle. Mientras mayor sea el σ, mayor será la
máscara de convolución necesaria. Por otro lado, la detección de bordes basados en suavizado
gaussiano, al reducir el ruido en la imagen, reducen el número de bordes falsos detectados.
Como aproximación al LoG se suele usar el Difference of Gaussian (DoG) que tiene un menor
costo computacional para ser calculado:
2
+y 2 )/2σ12 2
+y 2 )/2σ22
e −(x e −(x
DoG(x , y) = − s (10.6)
2πσ12 2πσ22
Este operador convuelve una imagen con una máscara que resulta de la diferencia de dos máscaras
Gaussianas con diferentes valores de σ. El cociente σ1 /σ2 = 1,6 da una buena aproximación a
LoG. Variando los valores de σ1 y σ2 se puede especificar el ancho de los bordes a detectar.
Este algoritmo (1970, Marr y Hildreth) está basado en el LoG. Consiste de los siguientes pasos:
3. Los pixels correspondientes a bordes son los zero-crossings del resultado anterior
Este método tiene un par de limitaciones. En primer lugar, produce “falsos bordes”, es decir
genera respuestas donde no existen bordes; por otro lado, tampoco tiene buena localización. Fue
implementado en la función imgMarrHildreth, que tiene por argumentos una imagen y un valor
para el σ de la máscara Gaussiana.
10.4.2. Canny
El detector Canny (1986, John Canny) está definido a partir de una serie de objetivos a cumplir:
Localización: La distancia entre los bordes detectados y los reales debe ser mı́nima;
Capı́tulo 10. Algoritmos de detección de bordes 71
Respuesta: No se deben detectar múltiples pixels de borde cuando sólo existe uno;
Para satisfacer estos criterios se utiliza el cálculo de variaciones, que permite encontrar la función
que optimiza un funcional dado. En el caso de Canny, esa función se describe como la suma de
cuatro términos exponenciales; sin embargo se puede aproximar por la primera derivada de una
Gaussiana.
En biOps se invoca a través de la función imgCanny (ver figura 10.6), que toma como parámetros
además de la imagen sobre la cual aplicar el algoritmo, el σ del filtro Gaussiano, y opcionalmente
los valores de threshold para el proceso de hysteresis.
Capı́tulo 10. Algoritmos de detección de bordes 72
(1 − b)b |i|+|j |
f [i , j ] = (10.7)
1+b
donde b es el factor de suavizado usado por el filtro, y toma valores reales entre 0 y 1.
6. Hysteresis
Es el mismo método que en Canny, pero adaptado para el caso en que los bordes están
representados por zero-crossings.
Este algoritmo puede correrse sobre una imagen a través de la función imgShenCastan que toma
argumentos para definir el factor de suavizado, un factor de thinning, el tamaño de la ventana
del threshold por gradiente adaptativo, un porcentaje que indica la cantidad de pixels que debe
haber por encima del valor de threshold máximo, y un booleano que determina si se aplica
hysteresis o no.
Otra definición sostiene que un borde existe si está presente en los tres canales, rojo, verde
y azul. En este caso se puede hacer la detección en cada componente y después combinarlas,
obteniendo una imagen resultado color. También podrı́a hacerse la detección por componente y
luego sumarlas para crear una imagen en escala de grises.
Está visto que la gran mayorı́a de los bordes encontrados en las componentes de color de una
imagen también se encuentran en la componente de intensidad. De esta manera serı́a suficiente
hacer la detección de bordes sobre el canal de intensidad. Sin embargo hay casos en imágenes
de bajo contraste en que existen bordes que no se detectan por luminosidad pero sı́ en las
componentes cromáticas. La decisión entonces dependerá principalmente de la aplicación. En
nuestro caso, los algoritmos implementados trabajan sobre las componentes de color, trabajando
con imágenes en representación RGB.
Capı́tulo 11
Filtros en el espacio de
frecuencias
Gran parte del procesamiento digital de señales se hace en un espacio matemático conocido como
espacio de frecuencias. El espacio de frecuencias de una imagen se refiere a la tasa de cambio en la
intensidad de los pixels. Para representar la información en este espacio es necesario aplicar algún
tipo de transformación. Una de las más difundidas y estudiadas en este caso es la transformada
de Fourier.
Una vez que tenemos la representación de la imagen en el espacio de frecuencias podemos analizar
su espectro de frecuencias, aplicar distintos filtros en este espacio e incluso, por una propiedad
de la transformada de Fourier, calcular mediante el producto de matrices complejas lo que en
representación espacial hacı́amos por convolución, lo que es especialmente útil para máscaras de
convolución grandes.
Se denomina espacio de frecuencias porque los parámetros del seno son amplitud y frecuencia.
El hecho de que una imagen se pueda convertir al espacio de frecuencias implica que se puede
74
Capı́tulo 11. Filtros en el espacio de frecuencias 75
reconocer información de baja y alta frecuencia. Una zona de la imagen que cambia lentamente
a lo largo de las columnas corresponde en el espacio de frecuencias a una función seno o coseno
con baja frecuencia. Por otro lado, si cambia rápidamente, como un borde, tendrá componentes
con frecuencias altas.
El espacio de frecuencias de una imagen se refiere a la tasa en que la intensidad de los pixels
cambia. Las frecuencias altas se caracterizan por los grandes cambios de amplitud, mientras que
las bajas por zonas de valores casi constantes.
De esta manera es posible construir filtros para remover o realzar determinadas frecuencias en una
imagen, lo que permite en ciertas ocasiones producir efectos de restauración. De hecho, el ruido
consiste principalmente de información de frecuencias altas, y entonces filtrar las frecuencias
altas deberı́a producir una reducción del ruido. Sin embargo, en este caso, también se obtiene
una reducción de los bordes.
La transformada de Fourier convierte una imagen (o una señal, en una dimensión) en un conjunto
de componentes de seno y coseno. Es importante mantener estas componentes separadas, y por
esta razón se suele usar vectores de la forma (coseno, seno) para cada punto de la representación
en el espacio de frecuencias de una imagen. Una forma de representar estos vectores es mediante
números complejos. Cada número complejo consiste de una parte real y una parte imaginaria, y
puede ser pensado como un vector. Un número complejo tiene la siguiente forma:
z = (x , j y) = x + j y (11.1)
√
donde j es el número imaginario −1. El exponencial de un número complejo se puede repre-
sentar como la suma de un seno y un coseno, que es exactamente lo que queremos:
La transformada de Fourier opera sobre funciones continuas de longitud infinita. Para una función
de dos dimensiones:
Z ∞ Z ∞
H (u, v ) = h(x , y)e −j 2π(ux +vy) dx dy (11.3)
−∞ −∞
Z ∞ Z ∞
h(u, v ) = H (u, v )e j 2π(ux +vy) du dv (11.4)
−∞ −∞
Capı́tulo 11. Filtros en el espacio de frecuencias 76
Sin embargo al trabajar con imágenes no tenemos funciones continuas, sino que contamos con
un número finito de pixels que tienen valores discretos. Por lo tanto necesitamos definir una
transformación de Fourier discreta (DFT, Discrete Fourier Transformation), que no es más que
un caso especial de la continua. La fórmula para computar la DFT de una imagen de M × N es:
M −1 N −1
1 X X
H (u, v ) = h(x , y)e −j 2π(ux /M +vy/N ) (11.5)
MN x =0 y=0
y la inversa:
M
X −1 N
X −1
h(x , y) = H (u, v )e j 2π(ux /M +vy/N ) (11.6)
u=0 v =0
tenemos
I (u, v )
φ(u, v ) = tan −1 (11.9)
R(u, v )
donde R(u, v ) e I (u, v ) son la parte real e imaginaria de H (u, v ), respectivamente. A | H (u, v ) |
se le llama magnitud o espectro de la transformación, y a φ(u, v ), ángulo de fase. A la hora de
trabajar con imágenes se usa especialmente el espectro.
No entraremos en más detalles acerca del cálculo e implementación de FFT, ya que irı́an más
allá de lo necesario para la comprensión de este capı́tulo. En nuestro desarrollo utilizamos FFTW
(Fast Fourier Transformation in the West), una librerı́a libre bajo licencia GPL para calcular la
FFT en una o más dimensiones. Esta librerı́a puede manejar arreglos de tamaños arbitrarios, y
nos permite obtener rápidamente la DFT de una imagen.
Ahora que podemos obtener la transformación de una imagen queremos mostrar la información.
Sin embargo existen algunas complicaciones que debemos superar para mostrar el espectro de
una imagen. Uno de los problemas que tenemos es que cada punto está representado por un
Capı́tulo 11. Filtros en el espacio de frecuencias 77
número flotante, que no necesariamente está en el rango 0 - 255. Una solución usual es tomar el
logaritmo del espectro, es decir:
donde c es una constante, que representa el parámetro de escala; además se suma 1 a cada pixel
para evitar pasar el valor 0 a la función logaritmo.
Una imagen del espectro tiene la componente cero en la esquina superior izquierda, como se ve
en 11.1(b). La forma convencional de mostrar el espectro es hacer un remapeo de los cuadrantes,
haciendo un intercambio (o “shift”) horizontal de la imagen en la mitad del ancho, y vertical en
la mitad del alto (11.1(c)).
¿Cómo interpretamos esta información? Cada pixel en el espectro (11.1(d)) representa un cambio
en el espacio de frecuencias de un ciclo por ancho de la imagen. El origen, en el centro del espectro
cuando éste está ordenado, es el término constante. Si todos los pixels de la imagen fueran grises
entonces habrı́a un único valor en el espectro de frecuencias, y estarı́a en el origen. El siguiente
pixel a la derecha del origen representa un ciclo por ancho de la imagen, el siguiente 2 ciclos
por ancho de imagen y ası́ sucesivamente. Es decir que las amplitudes de las frecuencias bajas
se encuentran en las esquinas del espectro, mientras que las altas están alrededor del centro (el
origen del espectro).
biOps en este campo ofrece funciones para hacer la transformación (imgFFT ) y su inversa
(imgFFTInv ). Se puede decidir la organización de los cuadrantes tanto al momento de aplicar
FFT como una vez obtenido el resultado mediante la función imgFFTShift. Todas las funciones
mencionadas, a excepción de imgFFTInv que devuelve una imagen, trabajan con matrices de
números complejos. Para obtener una imagen del espectro se puede invocar a imgFFTSpectrum,
y para generar la imagen de la información de fase, imgFFTPhase.
FFTMatrix
matrix : N × N Complex
width, height : N
∀ x : Image
• (∃1 y : FFTMatrix • fft (x ) = y ∧ x .width = y.width ∧ x .height = y.height)
Capı́tulo 11. Filtros en el espacio de frecuencias 78
11.3. Convolución
Una razón por la cual es útil generar la información de frecuencia de una imagen es para aplicarle
filtros. Hemos visto filtros por convolución en la representación espacial (ver 9.1). Una convolución
en la representación espacial es equivalente a una multiplicación de espectros en el espacio de
frecuencias.
Sean F (u, v ) y H (u, v ) las FFT de f (x , y) y h(x , y), respectivamente. Denotaremos a la operación
de convolución por ∗. El teorema de convolución demuestra que f (x , y)∗h(x , y) y F (u, v )H (u, v )
constituyen un par FFT:
La flecha doble indica que la expresión de la izquierda (convolución espacial) puede ser obtenida
tomando la FFT inversa de la expresión de la derecha (el producto en el espacio de frecuencias).
De la misma forma la expresión de la derecha se obtiene mediante la FFT de la expresión de la
izquierda. Un resultado análogo es que la convolución en el espacio de frecuencias reduce a la
multiplicación en la representación espacial, y viceversa, es decir:
Necesitamos crear la máscara. Existen dos métodos: uno es partir de una máscara en representa-
ción espacial y hacer la transformación, y el otro directamente calcular la máscara en el espacio
de frecuencias.
Para utilizar una máscara en representación espacial, se debe centrar ésta en la imagen y comple-
tar con ceros de tal forma de cubrir la imagen. Luego, transformar esta máscara y multiplicarla
por la FFT de la imagen, mediante multiplicación de complejos. Al resultado se le aplica la FFT
inversa. La imagen obtenida es la misma que si se hubiera hecho la convolución en la represen-
tación espacial con la máscara original. Este método se usa en general cuando se trabaja con
máscaras muy grandes.
Convolve
∆FFTMatrix
mask ? : Image
Existen muchos tipos de filtros por frecuencia pero la mayorı́a son una derivación o combinación
de los siguientes cuatro: pasobajo, pasoalto, bandpass y bandstop.
El filtro pasobajo deja pasar las frecuencias bajas atenuando las más altas. El pasoalto, en cambio,
atenua las más bajas mientras deja pasar las altas. Bandpass permite pasar sin modificaciones
una determinada banda de frecuencias, atenuando las frecuencias fuera del rango. Bandstop, por
el contrario, bloquea sólo una banda especı́fica de frecuencias, sin alterar aquellas fuera de esa
banda. Bandpass y bandstop se pueden obtener como combinación de sustracción y adición de
los resultados de los filtros pasobajo y pasoalto.
Los filtros provistos por biOps son: imgFFTLowPass (filtro pasobajo) y imgFFTHighPass (filtro
pasoalto), que toman por argumento, además de la transformada de la imagen, un valor de
radio por el cual filtrar las frecuencias; imgFFTBandPass e imgFFTBandStop, que esperan la
transformada y dos valores de radio que delimitan la banda.
A modo de ejemplo se muestra el esquema que describe el filtro de pasoalto y se muestra una
aplicación particular de este filtro (11.3).
HighPass
∆FFTMatrix
r? : R
width 0 = width
height 0 = height
∀ x : dom matrix | euclideanDistance(x , (width div 2, height div 2)) ≤ r?
• (matrix 0 x ).re = 0 ∧ (matrix0 x).im = 0
Capı́tulo 11. Filtros en el espacio de frecuencias 81
Operaciones morfológicas
Morfologı́a significa “la forma y estructura de un objeto”, o “la colocación e interrelación entre
las partes de un objeto”. A diferencia de otras operaciones vistas en este trabajo, diseñadas
para alterar la apariencia de una imagen, las morfológicas están relacionadas con la forma, y la
morfologı́a digital es una manera de describir o analizar la forma de un objeto digital.
Las operaciones básicas, y que se tratarán en este capı́tulo, son la erosión, por la cual se borran
pixels de la imagen que cumplan con ciertas condiciones, y dilatación, en donde se establece un
patrón alrededor de un pixel. A partir de éstas se definen la apertura u opening y la clausura o
closing.
Se tratarán sólo operaciones sobre dos tipos de imágenes: las denominadas binarias que corres-
ponden a las imágenes en “blanco y negro”, y las de canal único, o de “escala de grises”. Las
imágenes de color podrı́an tratarse como una generalización de escala de grises (trabajando so-
bre cada canal) o pensarse como dominios de aplicación separados por color. En ambos casos,
los resultados que se obtienen hacen que sea realmente difı́cil estructurarlos para llevar a cabo
una tarea particular. Sin embargo, este campo del procesamiento de imágenes está creciendo
rápidamente.
Las operaciones morfológicas sobre imágenes binarias se basan en imágenes de dos niveles: el
valor de cada pixel pertenece a un conjunto de dos elementos que contiene sólo el mı́nimo
y máximo aceptados (en nuestra especificación, MinValue y MaxValue, respectivamente, y en
nuestra implementación, 0 y 255). Este tipo de imágenes puede ser interpretado como un conjunto
matemático de pixels negros. Como cada pixel se identifica con sus coordenadas, decimos que
82
Capı́tulo 12. Operaciones morfológicas 83
(A)x = {c | c = a + x , a ∈ A} (12.1)
Para nuestro ejemplo de la imagen de la figura 12.1, tomando x = (1, 2), tendrı́amos que:
(B1 )(1,2) = {(1, 2), (2, 2), (2, 3), (3, 4)}
 = {c | c = −a, a ∈ A} (12.2)
Ac = {c | c ∈
/ A} (12.3)
A ∩ B = {c | c ∈ A ∧ c ∈ B } (12.4)
A ∪ B = {c | c ∈ A ∨ c ∈ B } (12.5)
A − B = {c | c ∈ A ∧ c ∈
/ B} (12.6)
A ⊕ B = {c | c = a + b, a ∈ A, b ∈ B } (12.7)
La figura 12.2 grafica la operación, mostrando el efecto causado para este caso.
La forma en que se calcula la dilatación nos hace conjeturar que puede ser definida como la unión
de todas las traslaciones de los elementos de la ventana. Esto es:
[
A⊕B = (A)b (12.8)
b∈B
[
A⊕B = (B )a (12.9)
a∈A
Esto da un pista con respecto a la implementación para el operador de dilatación (en nuestro
código, imgBinaryDilation): cuando el centro de la ventana se alinea con un pixel negro de la
imagen, todos los pixels de la imagen que corresponden a un pixel negro de la ventana se marcan
para ser cambiados a negro. Cuando terminamos de recorrer la imagen, habremos marcado los
pixels que deben ser convertidos a negro. En general, y en nuestro caso particular, se usa un
buffer secundario (inicialmente en blanco) para ir cargando los valores de la imagen resultado.
Esto es beneficioso en términos de tiempos de ejecución, pero perjudicial en cuanto a uso de
memoria.
Una de las aplicaciones más comunes para este tipo de operación (y por la cual ha tomado este
nombre), es la de hacer que las zonas negras de una imagen crezcan, o se “dilaten”. Para ello
implementamos también la función imgStdBinaryDilation, que aplica el método anteriormente
analizado utilizando una ventana estándar igual a 0, con dimensión pasada por parámetro. Esta
operación genera pixels negros alrededor de los ya existentes, “engrosando” de esta manera a los
objetos presentes. Un ejemplo concreto, utilizando la ventana estándar de dimensión 5, puede
verse en la figura 12.3.
Capı́tulo 12. Operaciones morfológicas 85
Ası́ como puede decirse que la dilatación resulta en agregar pixels negros en los objetos de las
imágenes binarias (o hacerlos más “gruesos” o “grandes”), la erosión resulta en sacar pixels
negros de los objetos (o hacerlos más “finos” o “pequeños”).
Con los conceptos introducidos en la subsección anterior, podemos definir la erosión de una
imagen A y un elemento estructural o ventana B como sigue:
A B = {c | (B )c ⊆ A} (12.10)
lo cual es el conjunto de pixels c tal que el elemento estructural B trasladado por c corresponde
al conjunto de pixels negros en A.
De los cuales sólo (B2 )(1,0) queda incluido en B1 y, por consiguiente, aparecerá en la erosión de
B1 . En la figura 12.4 se muestra esta operación.
A partir de las operaciones vistas en la subsección anterior definiremos algunas más, que son de
uso cotidiano en el procesamiento de imágenes digitales.
Es importante destacar que las operaciones de erosión y dilatación no son inversas. Aunque haya
casos en que la aplicación en cascada de estas operaciones resulte en la imagen original, no es
Capı́tulo 12. Operaciones morfológicas 87
(A B )c = Ac ⊕ B̂ (12.11)
La aplicación de una erosión inmediatamente seguida de una dilatación usando el mismo elemento
estructural se llama de apertura (en inglés, opening). En nuestro paquete puede encontrarse con
el nombre de imgBinaryOpening. Es un nombre descriptivo ya que pareciera que la operación
tiende a “abrir” los pequeños espacios entre los objetos que se tocan en una imagen. Después
de la aplicación de apertura, los objetos parecen estar mejor aislados que en la imagen original.
Esta operación puede ser útil a la hora de contar o clasificar los objetos que se encuentran en
ella.
Otra aplicación es la eliminación de ruido. La operación de erosión quitará los pixels aislados y
algunos bordes de los objetos, pero (la mayor parte de) estos últimos podrán ser recuperados con
la operación de dilatación, sin recuperar en este caso los pixels extraños agregados por el ruido.
Es necesario aclarar, de todas formas, que esta técnica da buenos resultados para la eliminación
de puntos negros, pero no hará lo propio con puntos blancos.
Una clausura (closing, en inglés) es similar a una operación de apertura, salvo que la dilatación
se realiza antes que la erosión. La función de biOps que implementamos a tal fin se denomi-
na imgBinaryClosing. La operación tiende a “cerrar” o “rellenar” los pequeños espacios entre
objetos.
La clausura también puede usarse para suavizar los contornos de los objetos de una imagen y
disminuir la apariencia de “dentado” que suelen aparecer en los objetos de algunas imágenes,
sobre todo las que han pasado por un proceso de thresholding.
Para ambas operaciones, y al igual que en el caso de dilatación y erosión, se han implementado las
variantes de aplicación con ventana estándar: pueden usarse las funciones imgStdBinaryOpening
e imgStdBinaryClosing. Un ejemplo de cada una de estas funciones puede verse en la figura 12.6.
(a) Imagen original (b) Apertura (dim=3) (c) Apertura (dim=2) (d) Clausura (dim=3)
El uso de imágenes en escala de grises para las operaciones vistas en la sección anterior introduce
muchas complicaciones, tanto conceptuales como computacionales. La noción alrededor de la
teorı́a de conjuntos desaparece, puesto que los valores que pueden tomar los pixels se expande a
un rango notablemente más grande.
Haremos un acercamiento intuitivo a las operaciones morfológicas, con la esperanza de que tengan
sentido aplicarlas para obtener resultados satisfactorios.
En las imágenes que consideramos en la sección anterior, el valor de los pixels se restringı́a al
máximo o mı́nimo permitidos. Estos valores se distinguı́an uno del otro para aplicar las opera-
ciones de erosión y dilatación. Es posible realizar una analogı́a para las imágenes en escala de
grises.
Definimos la dilatación en escala de grises de una imagen A con un elemento estructural S como
sigue:
2. Computar la suma de los pares conformados por cada valor de la imagen con el pixel
correspondiente de la ventana
3. Buscar el máximo de estas sumas, y establecer este valor como pixel de salida
Para esta implementación debe tenerse presente que los valores pueden salirse del rango permi-
tido, en cuyo caso deberemos hacer el ajuste necesario para respetar nuestras especificaciones.
Podemos definir también la erosión en escala de grises de A con una ventana S , de modo tal
que respete la dualidad planteada en la ecuación 12.11, de la siguiente manera:
Clasificación de imágenes
13.1. Conceptos
Dada una imagen, su clasificación consiste básicamente en obtener una nueva imagen, del mismo
tamaño y caracterı́sticas que la original, con la diferencia de que los valores de los pixels repre-
sentan una etiqueta que identifica la categorı́a asignada a cada pixel. Es importante considerar
que no pueden aplicarse ciertas operaciones estadı́sticas a una imagen clasificada, ya que, pese a
ser digital, no es una variable cuantitativa sino cualitativa.
90
Capı́tulo 13. Clasificación de imágenes 91
En nuestra implementación, los pixels resultado tienen el valor del pixel que representa a
la clase.
Suelen distinguirse dos tipos de clases: informacionales y espectrales. Las primeras son las que
constituyen la leyenda de trabajo que pretende deducir el intérprete. Las segundas, correspon-
den a los grupos de valores espectrales homogéneos en la imagen, en función de ofrecer una
reflectividad similar.
Idealmente habrı́a de producirse una correspondencia biunı́voca entre las dos, es decir, que a
cada clase de cobertura le corresponda un único grupo espectral, y que cada grupo espectral
corresponda a una sola clase temática. Este caso es poco frecuente. Normalmente se produce
alguna de las siguientes situaciones:
Dos o más categorı́as informacionales comparten una clase espectral: en este caso lo más
razonable es optar por una clave más general;
Varias clases informacionales comparten clases espectrales: frente a esta situación se puede
intentar con las soluciones anteriores, pero también puede ser necesario reconsiderar la
estrategia.
Como se puede ver, el método supervisado pretende definir clases informacionales, mientras el
no supervisado tiende a identificar las clases espectrales presentes en la imagen.
En nuestro trabajo se optó por desarrollar e implementar dos algoritmos de clasificación no super-
visada. En la siguiente sección se describen, de forma más detallada, los métodos no supervisados
en general y los algoritmos elegidos: K-means e Isodata.
Capı́tulo 13. Clasificación de imágenes 92
Estos métodos están dirigidos a definir las clases espectrales presentes en la imagen. No implican
ningún conocimiento del área de estudio, por lo que la intervención humana se centra más en la
interpretación que en la consecución de los resultados.
Se asume que los valores de los pixels forman una serie de agrupaciones o conglomerados
(clusters), más o menos nı́tidos según los casos. Estos grupos equivaldrı́an a pixels con un com-
portamiento espectral homogéneo, y por tanto, deberı́an definir clases temáticas de interés. Sin
embargo, como ya vimos, estas categorı́as espectrales no siempre pueden equipararse a las clases
informacionales que el usuario pretende deducir, por lo que resta a éste interpretar el significado
temático de dichas categorı́as espectrales.
Classification
input? : Image
k? : Z
clusters? : seq1 VALUE
output! : Image
output!.width = input?.width
output!.height = input?.height
#clusters? = k ?
∀ x : dom output!.v •
let c == min{i : Z | 1 ≤ i ≤ k? • valueDistance(input?.v(x), getCluster(i, clusters?))} •
(∃ v : Z | getCluster (v , clusters?) = c • output!.v (x ) = v )
El método para definir los agrupamientos espectrales se basa en la selección de tres parámetros:
Para medir la similitud entre pixels se han propuesto diversos criterios. El más utilizado
se basa en la distancia euclideana:
v
um
uX
da,b =t (Ia,i − Ib,i )2 (13.1)
i=1
donde da,b denota la distancia entre dos pixels cualesquiera a y b; Ia,i y Ib,i los valores de
cada pixel en la banda i , y m el número de bandas de la imagen.
13.3.1. K-means
Dado un conjunto de n puntos (en nuestro caso particular, los pixels de la imagen) en un espacio
d -dimensional y un entero k , el problema consiste en determinar un conjunto de k puntos,
llamados centroides, tales que se minimiza la distancia cuadrada media entre cada punto y el
centroide más cercano a éste.
Este algoritmo, además de la imagen a clasificar, tiene por entrada un valor k , que representa el
número de clusters a construir, y un entero maxit, que denota el número máximo de iteraciones
a realizar.
2. Para cada pixel, encontrar el centroide más cercano. Asociar el pixel al cluster correspon-
diente.
Al momento de buscar el centroide más cercano la implementación anterior revisa uno por uno
los k clusters. Sin embargo, existe una manera de estructurar la información de los centroides
Capı́tulo 13. Clasificación de imágenes 94
para evitar calcular la distancia a cada uno cada vez, guardando esos puntos en un kd-tree
[Moo91].
A partir de un kd-tree, y dado un punto x , queremos encontrar el vecino más cercano en el árbol.
Una primera aproximación es inicialmente la hoja cuya celda contiene a x . En la figura 13.3(a),
x está denotado por X y el punto dueño de la hoja que contiene a x está coloreado en negro.
Como se puede ver en este caso, la primera aproximación no es necesariamente el punto buscado
(i.e. no se trata del vecino más cercano) pero al menos sabemos que cualquier potencial vecino
más cercano debe estar más próximo, y por lo tanto dentro del cı́rculo centrado en x y que tiene
por radio la distancia de x al dueño del nodo. Subimos entonces al padre del nodo actual. En
la figura 13.3(b), el nodo negro. Calculamos si es posible una solución más cercana que la que
tenı́amos. En este caso no es posible, ya que el cı́rculo no interseca el espacio (sombreado) que
ocupa el otro hijo del nodo actual (el“hermano” de la hoja anterior). Si no puede existir un
vecino más cercano en el otro hijo, el algoritmo sigue hacia arriba en el árbol. El próximo nodo
padre deberá ser chequeado, es decir, considerar la distancia al punto dueño del nodo, puesto que
el área que le corresponde (norte de la lı́nea horizontal central) es intersecada por el cı́rculo. Esta
mecánica se aplica sucesivamente hasta alcanzar la raı́z del árbol. La descripción del algoritmo
Capı́tulo 13. Clasificación de imágenes 95
(a) Árbol en 2 dimensiones. No se indican los pla- (b) Representación del árbol anterior como un kd-tree
nos que dividen. El nodo (2,5) divide a lo largo del
plano por la coordenada y=5 y el nodo (3,8) del
plano por x=3
para construir los kd-trees y efectuar la búsqueda del vecino más cercano, y algunos detalles de
implementación se encuentran en [Moo91].
A partir de esta estructura de datos se implementó imgKDKMeans que utiliza el kd-tree para
realizar las búsquedas de centroide más cercano. Esta variante no significó una mejora notable,
ya que en general el número de clusters no es alto (y por lo tanto el número de centroides contra
los que comparar tampoco). Existe otra implementación de k-means que no desarrollamos que
Capı́tulo 13. Clasificación de imágenes 96
utiliza kd-trees ligeramente modificados para mapear todos los puntos de la imagen y eficientizar
el algoritmo [KNW02].
Sin embargo, encontramos otra manera de optimizar el orden de complejidad de k-means [FSTR06].
En cada iteración el algoritmo calcula la distancia entre cada punto y todos los centroides. ¿Por
qué no usar la información de las iteraciones anteriores? Para cada punto podemos mantener la
distancia al centroide del cluster más cercano. En la siguiente iteración, calculamos la distancia
al nuevo centroide de ese cluster. Si la nueva distancia es menor o igual que la que habı́amos
guardado, el punto se queda en el cluster y no hay necesidad de calcular la distancia con los
demás centroides.
La idea surge del hecho de que k-means descubre clusters de forma esférica, cuyo centro se va
moviendo a medida que se agregan puntos al cluster. Esto hace que el centro esté más cerca de
algunos puntos, y de esa forma, esos puntos cercanos permanecen en el cluster y no es necesario
encontrar la distancia a los otros clusters. Los puntos más alejados pueden cambiar de cluster
y en esos casos sı́ se recalculan las distancias. La variante implementada bajo el nombre de
imgEKMeans realiza las 2 primeras iteraciones del algoritmo original y las siguientes aplicando
la mejora descripta.
13.3.1.1. Complejidad
El algoritmo k-means converge a un mı́nimo local. Antes de converger, se calculan los centroides
varias veces y se hace una redistribución de todos los puntos de acuerdo a los nuevos centroides.
Esto tiene O(nkl ), donde n esel número de puntos, k el número de clusters y l el número de
iteraciones.
La variante que usa kd-trees para resolver la búsqueda de vecino más cercano no cambia el orden
de complejidad, pero tiene un mejor caso promedio ya que en el mejor caso se hacen O(log k )
inspecciones; aunque en el peor caso siguen siendo necesarias las k distancias. Además tiene por
desventaja el hecho de que es necesario reconstruir el árbol en cada iteración y eso también tiene
un costo.
La última propuesta, para obtener los cluster iniciales requiere O(nk ). Luego, algunos puntos se
mantienen en un cluster y otros cambian. Si un punto se mantiene en el cluster, esto requiere
O(1); caso contrario, requiere O(k ). Si suponemos que la mitad de los puntos se cambian de
cluster, requiere O(nk /2); como el algoritmo converge a un mı́nimo local, el número de puntos
que cambian de cluster decrece en cada iteración. Entonces se espera que el costo total sea
Xl
nk 1/i . Incluso para un número grande de iteraciones, este valor es mucho menor que nkl , y
i=1
por lo tanto esta mejora nos provee aproximadamente un O(nk ).
Capı́tulo 13. Clasificación de imágenes 97
13.3.2. Isodata
Este algoritmo puede ser considerado como una mejora al enfoque de k-means. También busca
minimizar el error cuadrático asignando los pixels al centroide más cercano. Sin embargo, a dife-
rencia del anterior, no se maneja con un número fijo de clusters sino con k clusters, permitiendo
que k varı́e en un intervalo que contiene la cantidad de clusters pedida por el usuario. Esta
situación se debe a que se descartan los clusters con pocos elementos. Por otro lado, se combinan
clusters si hay muchos o si existen algunos muy cercanos (operación merge). También un cluster
se puede dividir si hay pocos clusters o si contiene pixels demasiado disı́miles (operación split).
min dist: distancia mı́nima permitida entre los centroides de los clusters.
iter body: máximo número de iteraciones del loop principal del algoritmo.
El uso y significado de estos parámetros se describen con mayor detalle a continuación, junto
con los pasos del algoritmo:
2. Para cada pixel, encontrar el centroide más cercano. Asociar el pixel al cluster correspon-
diente.
4. Si al menos un cluster cambió y el número de iteraciones es menos que iter start, volver a
2.
5. Descartar los clusters con menos de min elements pixels, y descartar esos pixels también.
7. Si la distancia entre dos centroides es menor que min dist, combinar estos clusters y
actualizar el centroide; caso contrario, ir a 8. Repetir hasta max merge veces e ir a 8.
9. Encontrar un cluster que tenga desviación estándar para alguna variable, digamos x , que
sea mayor que split sd . De no haber, ir a 10. Sino, calcular la media para x en el cluster.
Separar los pixels del cluster en dos conjuntos, uno conteniendo aquellos pixels en los que x
es mayor o igual que la media, y el otro aquellos en que x es menor. Calcular los centroides
de estos dos nuevos clusters. Si la distancia entre ellos es mayor o igual que 1,1 ∗ min dist,
reemplazar el cluster original por los dos creados; caso contrario, el cluster no se divide.
10. Si este paso ha sido ejecutado iter body veces o no hubo cambios en los clusters desde su
última ejecución, detenerse. Sino, volver a 2.
Conclusiones
Este proyecto concluye con la publicación del paquete biOps en los repositorios de R. La licencia
GPL garantiza, a quienes ası́ lo deseen, la posibilidad de usar, copiar, modificar y redistribuir
este paquete. Estimamos que se mantendrá y mejorará su utilidad con el correr del tiempo, tanto
por nuestro aporte como el de los desarrolladores de R. La cooperación que caracteriza a esta
filosofı́a hace que quienes comulgamos con ella trabajemos por códigos de calidad y de constante
evolución y corrección.
Creemos que este trabajo resultó en un aporte importante a la comunidad R, y por extensión a la
comunidad del Software Libre. Los antecedentes en el procesamiento de imágenes en R, como se
vio en la sección 6.1 son escasos, y en su mayorı́a aportan a aspectos muy especı́ficos o áreas muy
particulares del manejo de imágenes. biOps, en este sentido, resulta un paquete multipropósito,
fácilmente extensible y con una amplia gama de algoritmos.
geométricas
morfológicas
aritméticas
lógicas
de manipulación de frecuencias
de tablas de reemplazo
de detección de bordes
de convolución.
99
Capı́tulo 14. Conclusiones 100
de clasificación de imágenes
f uzz (sección 3.4) es una herramienta muy práctica para el chequeo de tipos de las especi-
ficaciones Z. Nos adaptamos muy rápidamente tanto a ella como a su paquete para el uso
en LATEX.
El área del procesamiento digital de imágenes es muy amplia y está en constante evolución.
A lo largo del proceso de desarrollo de este trabajo fuimos estudiando muchas ramas de esta
ciencia, profundizando en aquellos aspectos que consideramos más valiosos, de mayor interés y
que hicieran a la buena funcionalidad del paquete.
Por ello que muchas aplicaciones han quedado relegadas. A continuación describimos los puntos
en que creemos conveniente focalizar el trabajo futuro de este proyecto:
1 http://subversion.tigris.org
2 http://www.tigris.org
3 http://trac.edgewall.org
Capı́tulo 14. Conclusiones 101
Conversión entre espacios de color: Como vimos en 4.3, existen distintos modelos de color
que permiten trabajar sobre diferentes aspectos de una imagen. biOps se maneja actual-
mente en el espacio RGB, pero está pensado incorporar funciones para el cambio entre
espacios, además de adaptar las funciones que sean necesarias para la manipulación de las
distintas representaciones.
Selección manual de colores para las categorı́as de clasificación: Permitir modificar los
colores de las clases en el resultado según la voluntad del usuario, para identificar con
tonos arbitrarios una categorı́a espectral con su correspondiente categorı́a informacional.
Interfaz gráfica de usuario: La librerı́a gráfica Gtk ha sido portada a R, dando la posibi-
lidad de generar un entorno de trabajo mediante ventanas y botones haciendo más fácil
la experiencia del usuario. Al momento sólo se ha implementado una ventana para ver las
imágenes que brinda información adicional, como las coordenadas y valores de los pixels
(ver 6.4).
14.2. Estadı́sticas
∼20 herramientas, lenguajes y programas usados para codificar, especificar, testear y do-
cumentar
Apéndice A
Profiling
En la sección 2.4 hemos visto una comparación entre implementaciones de diversos algoritmos
usando solamente código R y usando llamadas a código C. A continuación se detallan estos
resultados:
% imgAdd
Each sample represents 0.15 seconds .
Total run time : 3 5 5 7 . 2 5 0 0 0 0 0 0 1 4 3 seconds .
% total % self
total seconds self seconds name
99.64 3544.50 53.93 1918.35 " r_imgAdd "
22.53 801.30 22.53 801.30 "["
12.31 438.00 12.31 438.00 " [ <- "
6.98 248.40 6.98 248.40 " <= "
3.65 129.90 3.65 129.90 "+"
0.36 12.75 0.00 0.00 ". imgArithmeticOperator "
0.36 12.75 0.00 0.00 " imgAdd "
0.26 9.30 0.26 9.30 ".C"
0.24 8.55 0.24 8.55 ":"
0.05 1.80 0.05 1.80 " as . vector "
0.05 1.80 0.00 0.00 " array "
0.04 1.35 0.02 0.75 " imagedata "
0.03 0.90 0.00 0.00 " as . integer "
0.03 0.90 0.03 0.90 " as . integer . default "
% self % total
self seconds total seconds name
53.93 1918.35 99.64 3544.50 " r_imgAdd "
22.53 801.30 22.53 801.30 "["
12.31 438.00 12.31 438.00 " [ <- "
6.98 248.40 6.98 248.40 " <= "
3.65 129.90 3.65 129.90 "+"
0.26 9.30 0.26 9.30 ".C"
0.24 8.55 0.24 8.55 ":"
0.05 1.80 0.05 1.80 " as . vector "
0.03 0.90 0.03 0.90 " as . integer . default "
0.02 0.75 0.04 1.35 " imagedata "
% imgAverage
Each sample represents 0.15 seconds .
103
Apéndice A. Profiling 104
% total % self
total seconds self seconds name
99.53 2653.20 48.85 1302.30 " r_imgAverage "
30.59 815.40 30.59 815.40 "["
16.19 431.70 16.19 431.70 " [ <- "
3.48 92.85 3.48 92.85 "+"
0.47 12.45 0.04 1.05 " imgAverage "
0.33 8.85 0.33 8.85 ":"
0.16 4.35 0.08 2.10 " imagedata "
0.14 3.60 0.14 3.60 ".C"
0.09 2.40 0.09 2.40 " as . vector "
0.09 2.40 0.00 0.00 " array "
0.08 2.10 0.08 2.10 "/"
0.07 1.95 0.07 1.95 " list "
0.05 1.35 0.00 0.00 " as . integer "
0.05 1.35 0.05 1.35 " as . integer . default "
% self % total
self seconds total seconds name
48.85 1302.30 99.53 2653.20 " r_imgAverage "
30.59 815.40 30.59 815.40 "["
16.19 431.70 16.19 431.70 " [ <- "
3.48 92.85 3.48 92.85 "+"
0.33 8.85 0.33 8.85 ":"
0.14 3.60 0.14 3.60 ".C"
0.09 2.40 0.09 2.40 " as . vector "
0.08 2.10 0.16 4.35 " imagedata "
0.08 2.10 0.08 2.10 "/"
0.07 1.95 0.07 1.95 " list "
0.05 1.35 0.05 1.35 " as . integer . default "
0.04 1.05 0.47 12.45 " imgAverage "
% r_de c_ co ntr as t
Each sample represents 0.15 seconds .
Total run time : 1 9 7 7 . 9 0 0 0 0 0 0 0 0 4 7 seconds .
% total % self
total seconds self seconds name
99.79 1973.70 0.00 0.00 " r_ de c_c on tr as t "
99.78 1973.55 48.40 957.30 " r _ l o o k _u p _ t a b l e "
25.06 495.75 25.06 495.75 "["
25.02 494.85 25.02 494.85 " [ <- "
1.27 25.05 1.27 25.05 "+"
0.21 4.20 0.00 0.00 " imgDecreaseContrast "
0.21 4.20 0.00 0.00 " . imgContrast "
0.10 1.95 0.08 1.65 " imagedata "
0.06 1.20 0.06 1.20 ".C"
0.05 0.90 0.05 0.90 " as . vector "
0.05 0.90 0.00 0.00 " array "
0.03 0.60 0.03 0.60 ":"
0.03 0.60 0.01 0.15 " as . integer "
0.02 0.45 0.02 0.45 " as . integer . default "
% self % total
self seconds total seconds name
48.40 957.30 99.78 1973.55 " r _ l o o k _u p _ t a b l e "
25.06 495.75 25.06 495.75 "["
Apéndice A. Profiling 105
% r_d e c _ i n te n s i t y
Each sample represents 0.15 seconds .
Total run time : 1 9 9 7 . 2 5 0 0 0 0 0 0 0 4 8 seconds .
% total % self
total seconds self seconds name
99.76 1992.45 0.00 0.00 " r _ d e c _ in t e n s i t y "
99.75 1992.30 47.71 952.80 " r _ l o o k _u p _ t a b l e "
25.61 511.50 25.61 511.50 " [ <- "
24.89 497.10 24.89 497.10 "["
1.51 30.15 1.51 30.15 "+"
0.24 4.80 0.00 0.00 " imgDecreaseIntensity "
0.24 4.80 0.00 0.00 " . imgIntensity "
0.12 2.40 0.10 1.95 " imagedata "
0.06 1.20 0.06 1.20 ".C"
0.06 1.20 0.06 1.20 " as . vector "
0.06 1.20 0.00 0.00 " array "
0.04 0.75 0.04 0.75 ":"
0.03 0.60 0.01 0.15 " as . integer "
0.02 0.45 0.02 0.45 " as . integer . default "
0.01 0.15 0.00 0.00 " max "
% self % total
self seconds total seconds name
47.71 952.80 99.75 1992.30 " r _ l o o k _u p _ t a b l e "
25.61 511.50 25.61 511.50 " [ <- "
24.89 497.10 24.89 497.10 "["
1.51 30.15 1.51 30.15 "+"
0.10 1.95 0.12 2.40 " imagedata "
0.06 1.20 0.06 1.20 ".C"
0.06 1.20 0.06 1.20 " as . vector "
0.04 0.75 0.04 0.75 ":"
0.02 0.45 0.02 0.45 " as . integer . default "
0.01 0.15 0.03 0.60 " as . integer "
% r_imgDiffer
Each sample represents 0.15 seconds .
Total run time : 3 5 9 2 . 5 0 0 0 0 0 0 0 1 4 5 seconds .
% total % self
total seconds self seconds name
99.61 3578.40 53.47 1920.90 " r_imgDiffer "
22.99 825.75 22.99 825.75 "["
12.32 442.65 12.32 442.65 " [ <- "
5.13 184.20 5.13 184.20 " <= "
2.98 107.10 2.98 107.10 " <"
2.47 88.80 2.47 88.80 "-"
0.39 14.10 0.00 0.00 ". imgArithmeticOperator "
0.39 14.10 0.00 0.00 " imgDiffer "
0.29 10.35 0.29 10.35 ".C"
Apéndice A. Profiling 106
% self % total
self seconds total seconds name
53.47 1920.90 99.61 3578.40 " r_imgDiffer "
22.99 825.75 22.99 825.75 "["
12.32 442.65 12.32 442.65 " [ <- "
5.13 184.20 5.13 184.20 " <= "
2.98 107.10 2.98 107.10 " <"
2.47 88.80 2.47 88.80 "-"
0.29 10.35 0.29 10.35 ".C"
0.25 9.00 0.25 9.00 ":"
0.05 1.95 0.05 1.95 " as . vector "
0.03 1.05 0.04 1.50 " imagedata "
0.02 0.75 0.02 0.75 " as . integer . default "
% r_gamma
Each sample represents 0.15 seconds .
Total run time : 1 9 9 0 . 2 0 0 0 0 0 0 0 0 4 8 seconds .
% total % self
total seconds self seconds name
99.77 1985.70 0.01 0.15 " r_gamma "
99.77 1985.55 47.87 952.80 " r _ l o o k _u p _ t a b l e "
25.50 507.60 25.50 507.60 "["
24.97 496.95 24.97 496.95 " [ <- "
1.39 27.60 1.39 27.60 "+"
0.23 4.50 0.00 0.00 " imgGamma "
0.11 2.25 0.08 1.50 " imagedata "
0.07 1.35 0.07 1.35 " as . vector "
0.07 1.35 0.00 0.00 " array "
0.06 1.20 0.06 1.20 ".C"
0.03 0.60 0.03 0.60 ":"
0.02 0.45 0.00 0.00 " as . integer "
0.02 0.45 0.02 0.45 " as . integer . default "
% self % total
self seconds total seconds name
47.87 952.80 99.77 1985.55 " r _ l o o k _u p _ t a b l e "
25.50 507.60 25.50 507.60 "["
24.97 496.95 24.97 496.95 " [ <- "
1.39 27.60 1.39 27.60 "+"
0.08 1.50 0.11 2.25 " imagedata "
0.07 1.35 0.07 1.35 " as . vector "
0.06 1.20 0.06 1.20 ".C"
0.03 0.60 0.03 0.60 ":"
0.02 0.45 0.02 0.45 " as . integer . default "
0.01 0.15 99.77 1985.70 " r_gamma "
% r_in c_ co ntr as t
Each sample represents 0.15 seconds .
Total run time : 1 9 8 9 . 6 0 0 0 0 0 0 0 0 4 8 seconds .
% total % self
Apéndice A. Profiling 107
% self % total
self seconds total seconds name
47.72 949.35 99.78 1985.25 " r _ l o o k _u p _ t a b l e "
25.59 509.10 25.59 509.10 "["
24.98 497.10 24.98 497.10 " [ <- "
1.44 28.65 1.44 28.65 "+"
0.08 1.50 0.11 2.10 " imagedata "
0.06 1.20 0.06 1.20 ".C"
0.06 1.20 0.06 1.20 " as . vector "
0.05 1.05 0.05 1.05 ":"
0.02 0.45 0.02 0.45 " as . integer . default "
% imgIncreaseIntensity
Each sample represents 0.15 seconds .
Total run time : 2 0 0 9 . 5 5 0 0 0 0 0 0 0 4 9 seconds .
% total % self
total seconds self seconds name
99.78 2005.05 0.00 0.00 " r _ i n c _ in t e n s i t y "
99.77 2004.90 47.74 959.40 " r _ l o o k _u p _ t a b l e "
25.33 508.95 25.33 508.95 "["
25.21 506.70 25.21 506.70 " [ <- "
1.43 28.80 1.43 28.80 "+"
0.22 4.50 0.00 0.00 " imgIncreaseIntensity "
0.22 4.50 0.00 0.00 " . imgIntensity "
0.10 2.10 0.07 1.35 " imagedata "
0.07 1.50 0.07 1.50 " as . vector "
0.07 1.50 0.00 0.00 " array "
0.06 1.20 0.06 1.20 ".C"
0.05 1.05 0.05 1.05 ":"
0.03 0.60 0.00 0.00 " as . integer "
0.03 0.60 0.03 0.60 " as . integer . default "
0.01 0.15 0.00 0.00 " min "
% self % total
self seconds total seconds name
47.74 959.40 99.77 2004.90 " r _ l o o k _u p _ t a b l e "
25.33 508.95 25.33 508.95 "["
25.21 506.70 25.21 506.70 " [ <- "
1.43 28.80 1.43 28.80 "+"
0.07 1.50 0.07 1.50 " as . vector "
0.07 1.35 0.10 2.10 " imagedata "
0.06 1.20 0.06 1.20 ".C"
0.05 1.05 0.05 1.05 ":"
0.03 0.60 0.03 0.60 " as . integer . default "
Apéndice A. Profiling 108
% imgMaximum
Each sample represents 0.15 seconds .
Total run time : 2 8 3 8 . 9 0 0 0 0 0 0 0 0 9 9 seconds .
% total % self
total seconds self seconds name
99.71 2830.80 21.69 615.75 " r_imgMaximum "
61.69 1751.25 31.25 887.25 " max "
30.43 864.00 30.43 864.00 "["
15.93 452.25 15.93 452.25 " [ <- "
0.36 10.35 0.36 10.35 ":"
0.29 8.10 0.02 0.45 " imgMaximum "
0.11 3.00 0.11 3.00 ".C"
0.06 1.80 0.06 1.80 " list "
0.06 1.65 0.06 1.65 " as . vector "
0.06 1.65 0.00 0.00 " array "
0.05 1.50 0.04 1.05 " imagedata "
0.05 1.35 0.00 0.00 " as . integer "
0.05 1.35 0.05 1.35 " as . integer . default "
% self % total
self seconds total seconds name
31.25 887.25 61.69 1751.25 " max "
30.43 864.00 30.43 864.00 "["
21.69 615.75 99.71 2830.80 " r_imgMaximum "
15.93 452.25 15.93 452.25 " [ <- "
0.36 10.35 0.36 10.35 ":"
0.11 3.00 0.11 3.00 ".C"
0.06 1.80 0.06 1.80 " list "
0.06 1.65 0.06 1.65 " as . vector "
0.05 1.35 0.05 1.35 " as . integer . default "
0.04 1.05 0.05 1.50 " imagedata "
0.02 0.45 0.29 8.10 " imgMaximum "
% imgNegative
Each sample represents 0.15 seconds .
Total run time : 1 8 6 5 . 5 50 0 0 0 0 0 0 4 seconds .
% total % self
total seconds self seconds name
99.57 1857.60 43.70 815.25 " r _ l o o k _u p _ t a b l e "
99.57 1857.60 0.00 0.00 " r_ ne gat iv e_ lu t "
27.15 506.55 27.15 506.55 " [ <- "
27.04 504.45 27.04 504.45 "["
1.65 30.75 1.65 30.75 "+"
0.28 5.25 0.00 0.00 " imgNegative "
0.15 2.85 0.10 1.95 " imagedata "
0.14 2.70 0.00 0.00 " r_negative "
0.14 2.70 0.14 2.70 "-"
0.10 1.80 0.10 1.80 " as . vector "
0.10 1.80 0.00 0.00 " array "
0.07 1.35 0.07 1.35 ".C"
0.03 0.60 0.03 0.60 ":"
0.01 0.15 0.00 0.00 " as . integer "
0.01 0.15 0.01 0.15 " as . integer . default "
% self % total
self seconds total seconds name
Apéndice A. Profiling 109
% imgThreshold
Each sample represents 0.15 seconds .
Total run time : 1 9 7 4 . 9 0 0 0 0 0 0 0 0 4 7 seconds .
% total % self
total seconds self seconds name
99.76 1970.25 47.84 944.85 " r _ l o o k _u p _ t a b l e "
99.76 1970.25 0.00 0.00 " r_threshold "
25.53 504.15 25.53 504.15 "["
24.84 490.50 24.84 490.50 " [ <- "
1.54 30.45 1.54 30.45 "+"
0.24 4.65 0.00 0.00 " imgThreshold "
0.12 2.40 0.08 1.65 " imagedata "
0.07 1.35 0.07 1.35 " as . vector "
0.07 1.35 0.00 0.00 " array "
0.06 1.20 0.06 1.20 ".C"
0.02 0.45 0.00 0.00 " as . integer "
0.02 0.45 0.02 0.45 " as . integer . default "
0.02 0.30 0.02 0.30 ":"
% self % total
self seconds total seconds name
47.84 944.85 99.76 1970.25 " r _ l o o k _u p _ t a b l e "
25.53 504.15 25.53 504.15 "["
24.84 490.50 24.84 490.50 " [ <- "
1.54 30.45 1.54 30.45 "+"
0.08 1.65 0.12 2.40 " imagedata "
0.07 1.35 0.07 1.35 " as . vector "
0.06 1.20 0.06 1.20 ".C"
0.02 0.45 0.02 0.45 " as . integer . default "
0.02 0.30 0.02 0.30 ":"
Bibliografı́a
[Bec90] Richard A. Becker. A brief history of S. AT&T Bell Laboratories - Murray Hill -
New Jersey, 1990.
[Cra97] Randy Crane. A simplified approach to image processing. Prentice Hall, 1997.
[FPWW94] Bob Fisher, Simon Perkins, Ashley Walker, and Erik Wolfart. Hypermedia image
processing reference, Marzo 1994.
[FSTR06] Fahim, Salem, Torkey, and Ramadan. An efficient enhanced k-means clustering
algorithm. Journal of Zhejiang University, 2006.
[GJJ96] Earl Gose, Richard Johnsonbaugh, and Steve Jost. Pattern recognition and image
analysis. Prentice Hall, 1996.
[GW02] Rafael Gonzalez and Richard Woods. Digital Image Processing. Prentice Hall, 2002.
[KNW02] Tapas Kanungo, Nathan Netanyahu, and Angela Wu. An efficient enhanced k-means
clustering algorithm: Analysis and implementation. IEEE Transactions on pattern
analysis and machine intelligence, 2002.
[Moo91] Andrew Moore. Efficient Memory-based Learning for Robot Control. An introductory
tutorial on kd-trees. PhD thesis, Carnegie Mellon University, 1991.
[Par96] J. R. Parker. Algorithms for image processing and computer vision. Wiley Computer,
1996.
[WD95] Jim Woodcock and Jim Davies. Using Z. University of Oxford, 1995.
110