Trabajo Pr egingroup let elax elax endgroup

Transcripción

Trabajo Pr egingroup let elax elax endgroup
1
Universidad Tecnológica Nacional
Facultad Regional Buenos Aires
Técnicas Digitales III
Trabajo Práctico No 3
Aplicaciones de procesamiento digital de señales
e Imágenes sobre Linux
Ciclo Lectivo 2013
2
Índice general
1. General
1.1. Alcance y Condiciones . . . . . . .
1.1.1. Acerca de la Aplicación . .
1.1.2. Acerca de los entregables . .
1.1.3. Acerca de la documentación
1.1.4. Acerca de los Autores . . .
.
.
.
.
.
1
1
1
1
2
3
2. Introducción Teórica
2.1. Detección de Bordes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.2. Desarrollo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.3. Informe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5
5
8
8
3. Enunciado
3.1. Sistema de procesamiento y distribución de video en tiempo real . . . . . . . . . . . . . . . . . . . .
3.1.1. Server . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.1.2. Cliente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
9
9
9
13
A. Doxygen
A.1. ¿Que es doxygen? . . . . . . . . . . . . . . .
A.1.1. ¿Quienes usan Doxygen? . . . . . . .
A.2. Instalación . . . . . . . . . . . . . . . . . . .
A.3. Comenzando... . . . . . . . . . . . . . . . .
A.4. Configuración . . . . . . . . . . . . . . . . .
A.4.1. Doxyfile . . . . . . . . . . . . . . .
A.5. Generando la documentación . . . . . . . . .
A.6. Preparando el código para su documentación
A.6.1. Documentando variables . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
15
15
15
15
16
16
17
17
17
18
B. Opencv
B.1. Características . . . . . . . . . . . . . . . . .
B.1.1. Componentes y Nomenclaturas . . .
B.2. Primer Ejemplo . . . . . . . . . . . . . . . .
B.2.1. Invocar funciones básicas y compilar
B.2.2. Análisis . . . . . . . . . . . . . . . .
B.2.3. IplImage . . . . . . . . . . . . . . .
B.2.4. Opencv gira alrrededor deIplImage .
B.3. Aplicaciones y más funciones . . . . . . . .
B.3.1. Crear una imagen . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
21
21
21
22
22
23
24
26
26
26
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
3
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
4
ÍNDICE GENERAL
B.3.2. Operaciones Básicas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
27
B.3.3. Manejando pixeles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
28
B.4. Segundo Ejemplo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
29
B.4.1. Manejando video . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
29
B.4.2. Análisis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
31
Capítulo 1
General
1.1.
Alcance y Condiciones
1.1.1.
Acerca de la Aplicación
El presente trabajo práctico tiene como objetivo desarrollar una aplicación para un sistema de cómputo de propósito general, que trabaje en modo cliente servidor. El cliente debe ser apto para acceder a la aplicación servidor, desde
cualquier plataforma de cómputo de escritorio o portátil (embedded).
El código se debe escribir en lenguaje C, con los comentarios suficientes y en el formato necesario para emplear alguna herramienta de documentación automática que genere documentación clara y consistente acerca de las diferentes
funciones y módulos que componen el software. El código específico de tratamiento de la(s) señal(es) y/o imagen(es),
debe ser escrito además en assembler utilizando instrucciones SIMD, de modo de comparar el rendimiento de éstas
instrucciones respecto del mismo código escrito en C compilado con opciones de optimización de código.
El servidor debe trabajar con procesamiento paralelo, es decir debe ser una aplicación multiprocess o multithreaded,
sincronizando las instancias child (en multiprocess) o los threads (en multithreaded) mediante mecanismos de intercomunicación vistos en case.
El cliente debe acceder a los servicios de la aplicación servidor, mediante una conexión de red utilizando el protocolo
adecuado a la aplicación (conectado o no según se requiera).
Las aplicaciones componentes de la solución, deben manejar todas las condiciones de error, presentando los mensajes
adecuados al usuario y terminando la 1aplicación de forma ordenada y limpia.
1.1.2.
Acerca de los entregables
El producto a entregar debe constar de los siguientes ítems (obligatoriamente)
Programas fuente, .c y .asm.
Doxyfile (o equivalente para generar la documentación de desarrollo)
Makefile. Este script debe contener como regla principal la compilación y enlace (linkeo) completo del proyecto, y además debe contener las reglas clean, encargada de limpiar por completo los objetos y ejecutables, y
documentar la cual debe ejecutar la script de generación de la documentación.
1
2
CAPÍTULO 1. GENERAL
Informe en formato pdf con una breve explicación del uso del sistema y una sección en donde se muestren los
resultados de la comparación de performance entre las rutinas escritas en C y las correspondientes en Assembler
utilizando instrucciones SIMD
Los ítems componentes del proyecto deben entregarse en el SVN, en la carpeta correspondiente.
1.1.3.
Acerca de la documentación
No se considerará aprobado un Trabajo presentado sin una documentación adecuada. La documentación es tan
importante como los fuentes en sí. En los programas fuentes debe existir una mínima descripción de los diferentes
módulos, y dentro de cada uno, de las diferentes funciones que componen cada uno.
Como ejemplo, considerando el uso de Doxygen para lo cual se dispone de un tutorial en el Apéndice A, se ilustra a
modo de ejemplo el encabezado mínimo que debe contar cada uno de los archivos fuente que componen el proyecto.
1
2
3
4
5
6
7
8
9
10
11
12
13
/∗ ∗
∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗
\ f i l e t p 1 2 −4. c
\ details
∗ " D e p a r t a m e n t o de E l e c t r ó n i c a , FRBA , UTN"
∗ E j e r c i c i o 4 d e l TP 1 2 .
∗ O b j e t i v o m a n e j a r s e ñ a l e s , c r e a r y c o n t r o l a r p r o c e s o s , j u g a n d o con l a s s y s c a l l
∗ POSIX
\ a u t h o r A l e j a n d r o F u r f a r o , M a r c e l o D o a l l o , Darío A l p e r n , J u a n Montenegro , G u s t a v o
Nudelman
\ date 2011.05.08
\ version 1.0.0
∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗
∗/
Listing 1.1: Comentario Doxygen: Modelo de encabezado de un archivo fuente
Por otra parte, cada función debe tener del mismo modo un encabezado que la describa. A modo de ejemplo se
provee el siguiente código con los mínimos datos requeridos.
1
2
3
4
5
6
7
8
9
10
/∗ ∗
∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗
\ fn void set_auio_params ( i n t fd )
\ b r i e f F u n c i ón que s e t e a p a r á m e t r o s de l a p l a c a de a u d i o
\ d e t a i l s Se e n c a r g a de e s t a b l e c e r l o s v a l o r e s de v e l o c i d a d de m u e s t r e o ( S a m p l i n g R a t e )
Sample S i z e ( tamaño de l a m u e s t r a ) , y número de c a n a l e s , en l a p l a c a de a u d i o d e l
sistema .
Utilza la s y s c a l l i o c t l ( ) , ( input output control ) , para acceder al d i s p o s i t i v o
m e d i a n t e e l f o r m a t o e s t a b l e c i d o p o r l a i n t e r f a z de s o n i d o de LINUX , y e l f o r m a t o
gené r i c o de i o c t l ( f i l e _ d e s c r i p t o r , nombre d e l p a r ámetro , p u n t e r o a l v a l o r a
escribir ) ;
\ param f d F i l e d e s c r i p t o r o b t e n i d o de l a p l a c a de a u d i o
\ r e t u r n void
\ author Cristian Nigri
∗/
11
12
13
∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗
∗/
1.1. ALCANCE Y CONDICIONES
3
Listing 1.2: Comentario Doxygen: Modelo de encabezado de una función
Finalmente, se requiere documentar adecuadamente las estructuras, uniones, campos de bits y demás variables
complejas definidas en el programa, describiendo cada uno de los miembros que la componen, como se muestra en
los siguientes ejemplos.
1
2
3
4
5
6
7
8
9
10
11
12
13
/∗ ∗
∗ \ struct float_bits
∗ \ b r i e f E s t r u c t u r a de campo de b i t s de un f l o a t
∗ \ d e t a i l s C o n t i e n e l o s t r e s campos de un f l o a t : m a n t i s a o p a r t e f r a c c i o n a l ,
e x p o n e n t e ( r e c o r d a r que e s en complemento a 2 ) , y b i t de s i g n o ( 1 s i e l número e s
negativo y 0 s i es p o s i t i v o ) . \ n
∗ \ a u t h o r Oscar Paniagua
∗ \ date 21.08.2012
∗/
typedef s t r u c t f
{
u n s i g n e d i n t m a n t i s a : MANTISA_SP ;
u n s i g n e d i n t e x p o n e n t e : EXPONENTE_SP ;
u n s i g n e d i n t s i g n o : SIGNO ;
} float_bits ;
Listing 1.3: Comentario Doxygen: Modelo de documentación de una estructura
1
2
3
4
5
6
7
8
9
10
11
12
/∗ ∗
∗ \ union s i n g l e _ p r e c i s i o n
∗ \ b r i e f Unión de un f l o a t y l a E s t r u c t u r a de campo de b i t s de un f l o a t
∗ \ d e t a i l s C o n t i e n e un f l o a t y una e s t r u c t u r a de campo de b i t s de un f l o a t , de modo
de p o d e r a c c e d e r a un número en f o r m a c o m p l e t a o p o d e r r e p r e s e n t a r s u s campos de
manera i n d i v i d u a l ( m a n t i s a o p a r t e f r a c c i o n a l , e x p o n e n t e y s i g n o ) . \ n
∗ \ a u t h o r Mariano Gonzalez
∗ \ date 21.08.2012
∗/
typedef union _ s i n g l e
{
float
numero ;
float_bits
bits ;
} single_precision ;
Listing 1.4: Comentario Doxygen: Modelo de documentación de una estructura
1.1.4.
Acerca de los Autores
El Trabajo puede ser elaborado en forma individual o en grupos de hasta 3 miembros máximo (sin excepción).
La autoría de cada módulo debe figurar en el archivo fuente correspondiente, en el comentario correspondiente en el
encabezado de cada función, utilizando (en el caso de Doxygen la macro \author.
En el momento de la firma de los trabajos prácticos, cada autor mantendrá un breve coloquio con el ayudante de
Trabajos Prácticos del curso para demostrar su autoría.
4
CAPÍTULO 1. GENERAL
Capítulo 2
Introducción Teórica
2.1.
Detección de Bordes
La detección de bordes en una imagen reduce significativamente la cantidad de información de la misma, filtrando
lo innecesario y preservando sus características fundamentales. De allí que los métodos para detectar bordes sean una
herramienta muy poderosa para el procesamiento de imágenes. Podemos definir como un borde a los píxeles donde
la intensidad de la imagen cambia de forma abrupta. Si consideramos a la imagen como una función de intensidad,
I(x, y), entonces lo que buscamos son saltos en dicha función. Para simplificar, estudiaremos el caso de imágenes en
escala de grises.
El gradiente de una imagen en la posición (x, y) viene dado por el vector:

Ix
∇I = 


=
Iy
∂I
∂x
∂I
∂y


(2.1)
El vector gradiente siempre apunta en la dirección de la máxima variación de la imagen I en el punto (x, y). Para
los métodos de gradiente nos interesa conocer la magnitud de este vector, denominado simplemente como gradiente
de la imagen, denotado por ||∇I|| y dado por:
||∇I|| =
q
Ix2 + Iy2
(2.2)
Esta cantidad representa la variación de la imagen I(x, y) por unidad de distancia en la dirección del vector ∇I.
En general, el gradiente de la imagen se suele aproximar mediante la expresión
||∇I|| ≈ |Ix | + |Iy |
(2.3)
que es mucho más simple de implementar computacionalmente.
Para el cálculo del gradiente de la imagen en cada píxel tenemos que obtener las derivadas parciales. Las derivadas
se pueden implementar digitalmente de varias formas. Una forma es mediante máscaras de tamaño 3×3 o incluso más
grandes. Por otro lado, las máscaras normalmente tienen tamaños impares, de forma que los operadores se encuentran
centrados sobre el píxel en el que se calcula el gradiente.
5
6
CAPÍTULO 2. INTRODUCCIÓN TEÓRICA
Los operadores de Roberts, Prewitt, Sobel y Frei-Chen son operadores dobles o de dos etapas. La detección de
bordes se realiza en dos pasos. En el primero se buscan bordes horizontales utilizando la máscara que corresponde a
la derivada parcial en x. En el segundo paso se buscan los bordes verticales utilizando la máscara que corresponde
a la derivada parcial en y. Finalmente, se suman ambos para obtener el gradiente de la imagen en cada pixel. En la
figura 2.1 se pueden ver los operadores de Roberts, Prewitt, Sobel y Frei-Chen para determinar bordes horizontales
(izquierda) y verticales (derecha).
1
0
0
-1
0
-1
1
0
-1
0
1
-1
0
1
-1
0
1
-1
0
1
-2
0
2
-1
0
1
(a)
-1
-1
-1
0
0
0
1
1
1
(b)
-1
-2
-1
0
0
0
1
2
1
(c)
-1
√
- 2
-1
0
0
0
1
-1
0
1
√
2
1
-
√
2
0
√
2
-1
0
1
(d)
Figura 2.1: Operadores de derivación: (a) de Roberts, (b) de Prewitt, (c) de Sobel y (d) de Frei-Chen.
Por último, para simplificar el problema tratando sólo con imágenes en escala de grises. Para eso, debemos contar
con una función que sea capaz de monocromatizar una imagen a color. La función más sencilla para hacer esto es:
f (p) =
p
αR + βG + γB (2.4)
La función f se aplica a cada píxel p de la imagen; R, G, B son sus componentes de color, α, β y γ son coeficientes
entre 0 y 1 y es un exponente entre 1 e ∞.
2.1. DETECCIÓN DE BORDES
7
Los resultados de aplicar los filtros y de pasar a escala de grises son los siguientes:
Figura 2.2: Imagen original
Figura 2.3: Imagen filtrada. Filtros: Roberts, Prewitt, Sobel, Frei-Chen (de izquierda a derecha).
Figura 2.4: Imagen en escala de grises. Tomando: = 1, = ∞ (de izquierda a derecha).
8
CAPÍTULO 2. INTRODUCCIÓN TEÓRICA
2.2.
Desarrollo
Para la manipulación de las imágenes (cargar, grabar, etc.) el programa hace uso de la biblioteca OpenCV. Para
instalar esta biblioteca en las distribuciones basadas en Debian sólo basta con ejecutar lo siguiente:
$ sudo apt-get install libcv-dev libhighgui-dev libcvaux-dev
2.3.
Informe
El informe debe incluir las siguientes secciones:
1. Carátula: La carátula del informe con el número/nombre del grupo, los nombres y apellidos de cada uno de
los integrantes junto con número de legajo y e-mail.
2. Introducción: Describen qué es lo que realizaron en el trabajo práctico.
3. Desarrollo: Describen en profundidad cada una de las funciones que implementaron. Para la descripción de
cada función deberán mostrar gráficamente (mostrando el contenido de los registros XMM) cómo es una iteración del ciclo de la función. Es decir, cómo bajan los datos de la imagen a los registros, cómo los reordenan
para poder procesarlos, las operaciones que le aplican a los datos, etc.
4. Resultados: Deberán analizar y comparar las implementaciones de cada funciones en su versión C y Assembler y mostrar los resultados obtenidos a través de tablas y gráficos. También deberán comentar sobre los
resultados que obtuvieron. En el caso de que sucediera que la versión en C anduviese más rápido que su versión
en Assembler justificar fuertemente a que se debe esto.
5. Conclusión: Comentar sobre qué les dejó el trabajo práctico, la programación vectorial a bajo nivel, problemas
que se encontraron en el camino, etc.
Capítulo 3
Enunciado
3.1.
Sistema de procesamiento y distribución de video en tiempo real
El sistema a desarrollar debe adquirir video de resolución estándar desde una cámara conectada por USB al equipo
servidor para su procesamiento y transmisión en vivo, y de imágenes almacenadas en archivos avi o flv en una carpeta
específica del file system del sistema operativo que opera el equipo, en nuestro caso Linux.
El sistema tiene un módulo servidor que opera en el equipo de adquisición y almacenamiento de los videos, y un
cliente a instalar en cualquier dispositivo remoto también operado bajo Linux.
El servidor se compone del proceso principal, los procesos de adquisición, y los hilos de procesamiento y distribución.
3.1.1.
Server
Proceso principal
El proceso principal consiste de un servidor concurrente. Ni bien inicia su ejecución inicializa el sistema y crea un
thread que debe leer una cámara de video conectada al port usb del sistema. Las imágenes obtenidas de la cámara se
almacenan en un buffer de video(∗) dimensionado para almacenar un frame completo, para su posterior procesamiento
y distribución. El thread ejecuta de manera indefinida.
Luego de creado este thread, el proceso principal queda escuchando pedidos de conexión por el puerto TCP 23444.
Cada vez que reciba una conexión por dicho puerto, creará un proceso hijo que transmitirá los datos de video de la
manera en que se explicará en el ítem posterior.
Nota ∗ : La distribución del contenido del buffer no corre por cuenta de este proceso.
Procesos hijos
Cada proceso hijo creado en repuesta a un pedido de conexión, trabajará con el contenido del buffer de video adquirido desde el thread descripto en el ítem anterior, transmitiendo el contenido del frame completo pero solo cuando
se ha detectado un movimiento en la imagen capturada por la cámara. Esto es: cuando un frame difiere significativamente del anterior, entonces se transmite. Caso contrario no se transmite nada.
La transmisión se realizará por medio de un socket UDP, empleándose el primer port libre a partir del 23445.
Para detectar movimiento el programa principal (el proceso padre) creará un thread específico que mediante una
9
10
CAPÍTULO 3. ENUNCIADO
función escrita en assembler utilizando instrucciones SIMD calcule para cada frame la detección de bordes de modo
de hacer mas fácil la comparación con el frame anterior. Los filtros se detallan en la sub sección siguiente.
A efectos de liberar recursos por fallas en el sistema de transmisión o apagado del cliente remoto que solicitó las
imágenes, cada proceso hijo utilizará el socket TCP duplicado provisto por la función accept () en el server, para
esperar desde el cliente, un paquete que contenga el mensaje “Keep Alive“ cada 30 segundos, que le indique que aun
sigue leyendo el video transmitido. Transcurridos 60 segundos sin recibir el keep alive desde el cliente, el proceso
hijo termina su ejecución, no sin antes liberar los recursos que esté utilizando fundamentalmente el socket y su puerto
asociado.
Filtros
Basado en la intrducción teórica del Capítulo 2, implementar uno de los filtros descriptos anteriormente para
imágenes en escala de grises, tanto en lenguaje C como en Assembler. El Filtro será asignado a cada grupo por los
docentes del curso.
Precisamente, se deberá implementa un par de las siguientes funciones:
1. void roberts_c (unsigned char* src, unsigned char* dst, int h, int w, int row_size)
2. void prewitt_c (unsigned char* src, unsigned char* dst, int h, int w, int row_size)
3. void sobel_c (unsigned char* src, unsigned char* dst, int h, int w, int row_size)
4. void freichen_c (unsigned char* src, unsigned char* dst, int h, int w, int row_size)
5. void roberts_asm (unsigned char* src, unsigned char* dst, int h, int w, int row_size)
6. void prewitt_asm (unsigned char* src, unsigned char* dst, int h, int w, int row_size)
7. void sobel_asm (unsigned char* src, unsigned char* dst, int h, int w, int row_size)
8. void freichen_asm (unsigned char* src, unsigned char* dst, int h, int w, int row_size)
Donde:
src : Es el puntero al inicio de la matriz de elementos de 8 bits sin signo que representa a la imagen
de entrada. Como la imagen está en escala de grises, cada píxel se corresponde con un elemento
de la matriz.
dst : Es el puntero al inicio de la matriz de elementos de 8 bits sin signo que representa a la imagen
de salida. Como la imagen está en escala de grises, cada píxel se corresponde con un elemento de
la matriz.
h: Representa el alto en píxeles de la imagen, es decir, la cantidad de filas de las matrices de entrada
y salida.
w : Representa el ancho en píxeles de la imagen, es decir, la cantidad de columnas de las matrices
de entrada y salida.
3.1. SISTEMA DE PROCESAMIENTO Y DISTRIBUCIÓN DE VIDEO EN TIEMPO REAL
11
row_size: Representa la cantidad de bytes que ocupa una fila de, tanto la matriz de entrada como la
matriz de salida. Dado que cada píxel está representado por un byte se esperaría que el tamaño en
bytes de cada fila de las matrices de entrada y salida sea igual al ancho de la imagen. Esto no es
cierto en muchos casos y tiene que ver con el formato en cual está almacenada la imagen (i.e. bmp,
jpg, png, etc). Algunos formatos extienden el tamaño (en bytes) de la filas de la imagen para que sea
múltiplo de un valor conveniente para su posterior acceso, por ejemplo, 4. Es decir, si una imagen
tiene 10 píxeles de ancho ocuparía 10 bytes de ancho pero se la extiende a 12 bytes para que sea
más cómodo para procesar. Esto no afecta a la imagen en sí, mas bien es la representación interna
de la misma.
Concretamente la aplicación de un filtro se resume en el siguiente cálculo:
dstij = saturar(abs(f iltrox ) + abs(f iltroy ))
(3.1)
f iltrox =
srci−1j−1 ∗ fx 00
srcij−1 ∗ fx 10
srci+1j−1 ∗ fx 20
+ srci−1j ∗ fx 01
+
srcij ∗ fx 11
+ srci+1j ∗ fx 21
+
+
+
srci−1j+1 ∗ fx 02
srcij+1 ∗ fx 12
srci+1j+1 ∗ fx 22
+
+
(3.2)
f iltroy =
srci−1j−1 ∗ fy 00
srcij−1 ∗ fy 10
srci+1j−1 ∗ fy 20
+ srci−1j ∗ fy 01
+
srcij ∗ fy 11
+ srci+1j ∗ fy 21
+
+
+
srci−1j+1 ∗ fy 02
srcij+1 ∗ fy 12
srci+1j+1 ∗ fy 22
+
+
(3.3)
Donde fx y fy son los filtros horizontal y vertical respectivamente (para el caso del filtro de 2x2 la aplicación es
análoga a la expuesta).
Es decir, para generar el píxel dstij de la imagen destino (filtrada), primero se aplica la máscara en x, luego en
y (ambas máscaras centradas en el píxel srcij de la imagen de origen) y por último se suman los valores absolutos
de ambos resultados. Notar que el resultado de esta operación puede ser mayor que 255 por lo que hay que saturar el
valor.
Monocromatización
Para facilitar el algoritmo de detección de borde, es conveniente monocromatizar la imágen. Se pide en consecuencia, implementar una función de conversión a escala de grises, tanto en lenguaje C como en Assembler. Para esta
versión analizaremos los casos extremos de en la función ( = 1 y = ∞) y fijaremos α = 1/4, β = 1/2, γ = 1/4.
El Valor de para cada gruppo será dado por el equipo docente. Entonces nos quedan dos posibles funciones para
monocromatizar:
funo (p) =
(R + 2G + B)
( = 1)
4
finf inito (p) = max(R, G, B)( = ∞)
(3.4)
(3.5)
Precisamente, deberá implementar un par de las siguientes funciones:
1. void gris_epsilon_uno_c (unsigned char* src, unsigned char* dst, int h, int w, int src_row_size, int dst_row_size)
12
CAPÍTULO 3. ENUNCIADO
2. void gris_epsilon_inf_c (unsigned char* src, unsigned char* dst, int h, int w, int src_row_size, int dst_row_size)
3. void gris_epsilon_uno_asm (unsigned char* src, unsigned char* dst, int h, int w, int src_row_size, int dst_row_size)
4. void gris_epsilon_inf_asm (unsigned char* src, unsigned char* dst, int h, int w, int src_row_size, int dst_row_size)
Donde:
src : Es el puntero al inicio de la matriz de elementos de 24 bits sin signo (el primer byte representa
el canal azul de la imagen (B), el segundo el verde (G) y el tercero el rojo (R)) que representa a la
imagen de entrada. Es decir, como la imagen está en color, cada píxel está representado por 3 bytes.
dst : Es el puntero al inicio de la matriz de elementos de 8 bits sin signo que representa a la imagen
de salida. Como la imagen está en escala de grises cada píxel se corresponde con un elemento de
la matriz.
h: Representa el alto en píxeles de la imagen, es decir, la cantidad de filas de las matrices de entrada
y salida.
w : Representa el ancho en píxeles de la imagen, es decir, la cantidad de columnas de las matrices
de entrada y salida.
src_row_size: Idem row_size pero para la imagen fuente.
dst_row_size: Idem row_size pero para la imagen destino.
Consideraciones
Las funciones que deban implementar en Assembler deben utilizar el juego de instrucciones SSE para optimizar
la performance de las mismas.
Tener en cuenta lo siguiente:
El ancho de las imágenes es siempre mayor a 16 píxeles.
No se debe perder precisión en ninguno de los cálculos.
Para el caso de las funciones implementadas en Assembler deberán trabajar con al menos 8 bytes simultáneamente, procesando la mayor cantidad de píxeles posibles según el caso. Es decir, para las imágenes en escala de
gris deberán generar por lo menos 6 píxeles de la imagen destino en cada iteración y, en el caso de las imágenes
a color, por lo menos 2 píxeles.
Mediciones de tiempo
Utilizando la instrucción de assembler rdtsc podemos obtener el valor del Time Stamp Counter (TSC) del
procesador. Este registro se incrementa en uno con cada ciclo del procesador. Restando el valor del registro antes de
llamar a una función al valor del registro luego de la llamada, podemos obtener la duración en ciclos de esa ejecución
de la función.
Esta cantidad de ciclos no es siempre igual entre invocaciones de la función, ya que este registro es global del
procesador y si nuestro programa es interrumpido por el scheduler para realizar un cambio de contexto, contaremos
muchos más ciclos que si nuestra función se ejecutara sin interrupción. Por esta razón el programa principal del TP
permite especificar una cantidad de iteraciones para repetir el filtro, con el objetivo de suavizar este tipo de outliers.
3.1. SISTEMA DE PROCESAMIENTO Y DISTRIBUCIÓN DE VIDEO EN TIEMPO REAL
3.1.2.
13
Cliente
El cliente es un sencillo programa que conecta al servidor por el port TCP indicado y presenta las imágenes que
se reciben en una ventana. Su principal finalidad es corroborar el funcionamiento del servidor.
14
CAPÍTULO 3. ENUNCIADO
Apéndice A
Doxygen
A.1.
¿Que es doxygen?
Doxygen es una herramienta para convertir los comentarios de un programa escrito en C, C++, VHDL, PHP, C#,
y Java (entre otros lenguajes), en documentación suficientemente prolija y presentable como para poser publicarse.
Maneja la salida de documentación en formatos: html, LATEX, pdf, rtf, entre otros. Y lo mas interesante es que la
documentación se genera de manera automática, y a partir de los archivos fuente de modo que siempre será consistente
con éstos.
Entre Ventajas de usar Doxygen podemos conntar:
1. Al escribir la documentación en el mismo archivo fuente en forma de comentarios, nos releva de mantener otra
documentación separada de los fuentes.
2. La documentación se genera en forma automática agregando además diagramas de interacción entre los diferentes módulos
3. Permite documentar variables, estructuras de datos además de las funciones.
4. Es una herramienta de documentación automática. Hay otras pero su uso genera el hábito metodológico de
trabajo.
A.1.1.
¿Quienes usan Doxygen?
A.2.
Instalación
Hace falta instalar los siguientes paquetes
1
2
s u d o a p t −g e t i n s t a l l doxygen
s u d o a p t −g e t i n s t a l l g r a p h v i z
Listing A.1: Doxygen: Comandos para instalar Doxygen
Opcionalmente (aunque no estaría de mas...)
1
s u d o a p t −g e t i n s t a l l k a t e
Listing A.2: Doxygen: Comando para instalar Kate
15
16
APÉNDICE A. DOXYGEN
A.3.
Comenzando...
1. Escribir comentarios claros, y suficientes para describir cada función, antes de comenzar a “codearla”.
2. Una vez dentro de ella, insertar comentarios de modo de describir la operación (al menos los “big steps”)
3. Observar buenas prácticas de programación: Identar código, Usar convenciones para nombres de variables y
funciones, Escribir código de manera “legible”.
4. Entender al comentario como parte escencial de la aplicación: Cada agregado o modificación debe ser documentado describiendo sus características, fecha y autor.
A.4.
Configuración
Abrimos una terminal e ingresamos al directorio que contiene nuestros archivos de trabajo.
En ese directorio generamos el archivo de configuración para Doxygen.
Para ello tipear:
1
doxygen −g
A.5. GENERANDO LA DOCUMENTACIÓN
17
Listing A.3: Doxygen: Generación de un archivo de configuración genérico
A.4.1.
Doxyfile
Es el archivo de texto plano utilizado para la configuración de doxygen. Todo lo que comienza con # se toma como
comentario. Cada línea válida tiene el formato siguiente:
PARAMETRO = VALOR Si bien el archivo aparece como gigantesco al editarlo, los Parámetros de interés son los
siguientes:
Parámetro
PROJECT_NAME
PROJECT_NUMBER
OUTPUT_DIRECTORY
CREATE_SUBDIRS
OUTPUT_LANGUAGE
TAB_SIZE
OPTIMIZE_OUTPUT_FOR_C
EXTRACT_ALL
INPUT
INPUT_ENCODING
GENERATE_LATEX
A.5.
Descripción / Valor
Nombre del Proyecto. Ejemplo: Trabajo Práctico No 4.
Número o versión del proyecto. Ej: Ejercicio 3.4..
Es el directorio a partir del cual se generará el árbol de archivos y subdirectorios (ver
opción siguiente) que componen la documentación. Generalmente usamos =./doxy, de
modo que se genere dentro del directorio del proyecto.
Valor default YES. En este caso crea dos niveles de subdirectorios a partir del de documentación para almacenar, los diferentes archivos que la componen.
Lenguaje en el que queremos generar la documentación. Default English.
Es la cantidad de espacios por los que reemplazará cada Tab encontrado en el código.
Default: 8.
Si el proyecto es enteramente escrito en C conviene activar esta opción para que Doxygen
optimice la salida para este lenguaje. En tal caso es = YES.
Si está en YES, doxygen agregará a la documentación todo lo que encuentre (funciones,
variables, etc), aunque no se hayan documentado.
Es el directorio del proyecto, que se toma como entrada de información para Doxygen.
En caso de poner Doxyfile en el mismo directorio del proyecto (como es nuestro caso)
colocamos =./
Establece el sistema de codificación de caracteres con el que se generará la documentación: Si se trabaja en inux colocamos UTF-8, para Windows ISO-8859.
Conviene ponerlo en NO (el default es YES) si no queremos generar la documentación
en LATEX.
Generando la documentación
Es la parte mas fácil. Solo hace falta tipear en una consola dentro del directorio del proyecto el siguiente comando:
1
doxygen D o x y f i l e
Listing A.4: Doxygen: Comando para la generación de la documentación del proyecto
Luego, simplemente hay que abrir el archivo ./doxy/index.html con un navegador cualquiera y tenemos lista la documentación.
A.6.
Preparando el código para su documentación
Básicamente la clave consiste en como armar los comentarios:
1
2
3
4
/∗ ∗
∗ E s t i l o JavaDoc : I n i c i a r con ’ / ∗ ∗ ’
Apto p a r a p r o g r a m a s en C
Los a s t e r i s c o s i n t e r m e d i o s s o n o p c i o n a l e s
18
5
APÉNDICE A. DOXYGEN
∗/
6
7
8
9
10
/∗ !
∗ E s t i l o QT
Los a s t e r i s c o s i n t e r m e d i o s s o n o p c i o n a l e s
∗/
11
12
13
// /
/ / E s t i l o C++
Listing A.5: Doxygen:Estilos de comentarios por lenguaje para generar documentación
Luego, conocer algunas de las Macros que permiten mejorar y organizar la documentación general del programa.
1
2
3
4
5
6
7
8
9
10
11
/∗ ∗
\ f i l e programa . c
\ b r i e f Este a r c h i v o c o n t i e n e e l programa
principal
\ d e t a i l s Aquí n o s e x p l a y a m o s s o b r e l a t a r e a
que r e a i z a r á e l p r o g r a m a o l a
f u n c i ón
\ author Alejandro Furfaro afurfaro@ieee . org
\ d a t e 30 de Marzo de 2011
\ version 1.0.0
∗/
Listing A.6: Doxygen: Comentario tipo para encabezamiento de programa
Las mismas y algunas otras Macros ayudan con la Documentación general de las funciones.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/∗ ∗
\ fn void p r i n t _ t i m e (∗ s t r u c tv )
\ b r e f Funcion para o b t e n e r l a fecha y l a hora
con f o r m a t o .
\ d e t a i l s En p r i m e r i n s t a n c i a s e l l a m a a g e t t i m e o f d a y ,
p a s a n d o como a r g u m e n t o e l p u n t e r o a una
e s t r u c t u r a t i m e v a l ( t v ) . Luego con s t r f t i m e ( )
s e l e da un f o r m a t o a p t o p a r a p r e s e n t a r un
t i m e s t a m p con e l f o r m a t o
año−mes−día h o r a s : m i n u t o s : s e g u n d o s .
\ param [ i n ] ∗ s t r u c t v : p u n t e r o a una
e s t r u c t u r a timeval ( tv )
\ r e t u r n Nada ( No r e g r e s a no r e g r e s a v a l o r e s
\ author Alejandro Furfaro afurfaro@ieee . org
\ date 2011.05.08
\ version 1.0.0
∗/
Listing A.7: Doxygen: Comentario tipo para encabezamiento de función
A.6.1.
Documentando variables
Versión compacta:
A.6. PREPARANDO EL CÓDIGO PARA SU DOCUMENTACIÓN
1
unsigned char Buffer [ Buffersize ] ;
/ / !< Buffer para r e c i b i r c a r a c t e r e s
Listing A.8: Doxygen: Comentario tipo para una variable
Versión extendida
1
2
3
4
5
6
7
/∗ ∗
\ var unsigned char Buffer [ Buffersize ] ;
\ b r i e f Buffer para r e c i b i r c a r a c t e r e s
\ d e t a i l s Aquí s i e s n e c e s a r i o podemos e x p l a y a r n o s
a c e r c a de l a v a r i a b l e .
∗/
unsigned char Buffer [ Buffersize ] ;
Listing A.9: Doxygen: Documentación extendida para una variable
Estructuras
1
2
3
4
5
6
7
8
9
/∗ ∗
\ s t r u c t coordenada
\ b r i e f V a r i a b l e p a r a a l m a c e n a r un p a r de c o o r d e n a d a s ( x , y )
\ d e t a i l s Aquí s i e s n e c e s a r i o podemos e x p l a y a r n o s a c e r c a de l a e s t r u c t u r a .
∗/
s t r u c t coordenada {
i n t x ; / / ! < Coordenada x ;
i n t y ; / / ! < Coordenada y ;
};
Listing A.10: Doxygen: Comentario tipo para documentación de una estructura
En general
1
2
3
4
5
6
\ struct
\ enum
\ union
\ class
\ def
\ typedef
Listing A.11: Doxygen: Macro tipo para documentación de las diferentes variables y tipos de C
19
20
APÉNDICE A. DOXYGEN
Apéndice B
Opencv
B.1.
Características
OpenCV es una biblioteca open source para C/C++ para procesamiento de imágenes y visión computarizada,
desarrollada inicialmente por Intel. Su primer versión estable fue liberada en 2006. En Octubre de 2009, se libera el
segundo release mayor: OpenCV v2.
Su documentación no es muy exhaustiva. Mas bien la descripción de las diferentes funciones y sus principales variables
tipos, y estructuras de datos. Se puede acceder en: http://opencv.willowgarage.com/wiki/.
OpenCV se encuentra disponible para los sistemas operativos Linux, Mac, y Windows, y sus funciones de acceso a
recursos gráficos interactúan con las API de las GUI de cada sistema de modo de proveer un conjunto de recursos, si
bien limitado en cuanto a posibilidades, 100 % portable a cada una de estas plataformas.
Tiene estructuras básicas de datos para operaciones con matrices y procesamiento de imágenes, permite visualizar
datos muy sencillamente y extraer información de imágenes y videos, independientemente de los diversos formatos
que soporta en cada caso. Tiene funciones de captura y presentación de imágenes.
B.1.1.
Componentes y Nomenclaturas
Opencv se compone de 4 Módulos.
cv. Contiene las Funciones principales de la biblioteca
cvaux. Contiene las Funciones Auxiliares (experimental)
cxcore. Contiene las Estructuras de Datos y Funciones de soporte para Álgebra lineal
Highgui. Funciones para manejo de la GUI
Cada uno de estos está relacionado con un header.
1
2
3
4
# include
# include
# include
# include
<cv . h>
< c v a u x . h>
< h i g h g u i . h>
< c x c o r e . h>
/ / i n n e c e s a r i o , i n c l u i d o en cv . h
Listing B.1: Opencv: Headers para incluir en los programas
21
22
APÉNDICE B. OPENCV
Existen convenciones para los Nombres de las Funciones y Datos. En general el formato es:
cvActionTargetMod (...)
Action: Función core. Ej: set, create.
Target: Elemento destino de la Acción. Ej: Contorno, polígono.
Mod : Modificadores opcionales. Ej: Tipo de argumento.
Respecto de Parámetros de imágenes, están definidos diferentes parámetros y sus valores, como por ejemplo:
IPL_DEPTH_<bit_depth>(S|U|F)
Ejemplos de este parámetro:
1
2
IPL_DEPTH_8U
IPL_DEPTH_32F
/ / imagen de 8 b i t s no s i g n a d o s .
/ / imagen de 32 b i t s p u n t o f l o t a n t e .
Listing B.2: Opencv: Ejemplos de definición de la profundidad de bit
B.2.
Primer Ejemplo
B.2.1.
Invocar funciones básicas y compilar
Nos proponemos simplemente abrir un archivo de imagen cualquiera y presentarlo en la pantalla en una ventana.
1
2
3
4
5
6
7
8
9
10
11
12
/∗ ∗
∗ \ f i l e imgproc
∗ \ b r i e f P r o g r a m a s e n c i l l o p a r a a b r i r una imagen y p r e s e n t a r l a en una v e n t a n a
∗ \ d e t a i l s U t i l i z a m o s l a b i b l i o t e c a Opencv p a r a a b r i r l a imagen a p r o v e c h a n d o
∗ s u s f u n c i o n e s i n d e p e n d i e n t e s d e l t i p o de a r c h i v o , y t a m b i én p a r a c r e a r una
∗ v e n t a n a s e n c i l l a ( s i n menú ) , p a r a p r e s e n t a r l a en l a GUI d e l s i s t e m a o p e r a t i v o
∗ que s e t e n g a ( Linux , Windows , o MAC OS ) .
∗ E s t e p r o g r a m a c o m p i l a en Linux , m e d i a n t e e l comando s i g u i e n t e :
∗ g c c −o img img . c ‘ pkg−c o n f i g −−c f l a g s −− l i b s opencv ‘ −g −ggdb −Wall
∗ \ a u t h o r A l e j a n d r o F u r f a r o . a f u r f a r o @ e l e c t r o n . f r b a . u t n . edu . a r
∗ \ date 12.05.2011
∗/
13
14
15
# i n c l u d e <cv . h>
# i n c l u d e < h i g h g u i . h>
16
17
18
19
20
21
22
23
24
i n t main ( )
{
/ / I n s t a n c i a una e s t r u c t u r a I p l I m a g e , d e f i n i d a en openCV p a r a
/ / c a r g a r l a i n f o r m a c i ón de l a imagen o r i g e n
I p l I m a g e ∗ image = cvLoadImage ( "LENA . JPG " , 0 ) ;
/ / Creamos una v e n t a n a l l a m a d a " e j e m p l o 1 " , a u t o a j u s t a b l e a l
/ / tamaño de l a imagen que l e vamos a c o l o c a r d e n t r o
cvNamedWindow ( " e j e m p l o 1 " ,CV_WINDOW_AUTOSIZE) ;
B.2. PRIMER EJEMPLO
/ / Movemos l a v e n t a n a a l p i x e l 100 de l a f i l a 100
cvMoveWindow ( " e j e m p l o 1 " , 1 0 0 , 1 0 0 ) ;
/ / Mostramos l a imagen l e ída en l a v e n t a n a c r e a d a
cvShowImage ( " e j e m p l o 1 " , image ) ;
/ / E s p e r a m o s que e l u s u a r i o p r e s i o n e c u a l q u i e r t e c l a . . .
cvWaitKey ( 0 ) ;
/ / Liberamos l o s r e c u r s o s u t i l i z a d o s
c v R e l e a s e I m a g e (& image ) ;
/ / Terminamos e l p r o g r a m a
return 0;
25
26
27
28
29
30
31
32
33
34
35
23
}
Listing B.3: Listado de código del programam de ejemplo No 1
Para empezar a trabajar abrir con el editor de texto preferido, el archivo eje1.c que contiene el código del listado B.3.
Además se necesita una consola cuyo prompt esté posicionado en el directorio de trabajo en el que está el programa
eje1.c. Para compilar, ejecutar en la consola el comando indicado en el encabezado del programa del listado B.3, que
reproducimos a continuación para mayor claridad:
1
g c c −o e j e 1 e j e 1 . c −g −ggdb ‘ pkg−c o n f i g −−c f l a g s −− l i b s opencv ‘ −Wall
Listing B.4: Comando de compilación del código de B.3
B.2.2.
Análisis
Los comentarios del programa listado en B.3, son suficientes para entender que hace cada línea de código.
En principio vemos lo simple del mismo, en especial la manipulación de ventanas a cargo de la biblioteca highgui,
que nos independiza del manejo de los detalles que requiere el software de base. Revisemos la nomenclatura explicada anteriormente en el código ejemplo. Es muy fácil de ver cuales son funciones de esta biblioteca y cuales nó,
simplemente teniendo en cuenta que las funciones Opencv comienzan con el prefijo cv.
Carga de una imagen
IplImage *image = cvLoadImage("Lena.bmp");
Crear y Ubicar una ventana
cvNamedWindow ("ejemplo1", CV_WINDOW_AUTOSIZE);
cvMoveWindow ("ejemplo1", 100, 100);//desde borde superior izquierdo
Mostrar la imagen en la ventana creada
cvShowImage("ejemplo1", image);
Liberar recursos
cvReleaseImage(&image);
Con respecto a la acción de cada función:
Carga de una imagen
IplImage *image = cvLoadImage("Lena.bmp");
24
APÉNDICE B. OPENCV
Crear y Ubicar una ventana
cvNamedWindow ("ejemplo1", CV_WINDOW_AUTOSIZE);
cvMoveWindow ("ejemplo1", 100, 100);//desde borde superior izquierdo
Mostrar la imagen en la ventana creada
cvShowImage("ejemplo1", image);
Liberar recursos
cvReleaseImage(&image);
Y finalmente respecto del Destino de la función:
Carga de una imagen
IplImage *image = cvLoadImage("Lena.bmp");
Crear y Ubicar una ventana
cvNamedWindow("ejemplo1", CV_WINDOW_AUTOSIZE);
cvMoveWindow("ejemplo1", 100, 100);//desde borde superior izquierdo
Mostrar la imagen en la ventana creada
cvShowImage("ejemplo1", image);
Liberar recursos
cvReleaseImage(&image);
B.2.3.
IplImage
IplImage es “La” Estructura
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
typedef struct_IplImage
{
int
nSize ;
i n t ID ;
int
nChannels ;
int
alphaChannel ;
int
depth ;
char colorModel [ 4 ] ;
char channelSeq [ 4 ] ;
int
dataOrder ;
int
origin ;
int
align ;
int
width ;
int
height ;
s t r u c t _IplROI ∗ r o i ;
s t r u c t _ I p l I m a g e ∗maskROI ;
void ∗ imageId ;
struct _IplTileInfo ∗ tileInfo ;
int
imageSize ;
char ∗ imageData ;
B.2. PRIMER EJEMPLO
21
22
23
24
25
26
int
int
int
char
25
widthStep ;
BorderMode [ 4 ] ;
BorderConst [ 4 ] ;
∗ imageDataOrigin ;
}
IplImage ;
Listing B.5: Declaración de la estructura IplImage
Si bien está en la documentación de la Biblioteca, es oportuno desglosar aquí el significado de cada uno de sus
miembros
Nsize: sizeof (IplImage), es decir su tamaño en bytes.
ID: Versión, siempre igual a 0
nchannels: Número de canales. La mayoría de las funciones OpenCV soportan 1 a 4 canales.
alphaChannel: Ignorado por OpenCV
depth: Profundidad del canal en bits + el bit de signo opcional (IPL_DEPTH_SIGN).
• IPL_DEPTH_8U: entero no signado de 8 bits.
• IPL_DEPTH_8S: entero signado de 8 bits.
• IPL_DEPTH_16U: entero no signado de 16 bits.
• IPL_DEPTH_16S: entero signado de 16 bits.
• IPL_DEPTH_32S: entero signado de 32 bits.
• IPL_DEPTH_32F: Punto flotante simple precisión.
• IPL_DEPTH_64F: Punto flotante doble precisión.
colorModel: Ignorado por OpenCV. La función CvtColor de OpenCV requiere los espacios de color origen
y destino como parámetros.
channelSeq: Ignorado por OpenCV.
dataOrder:
• 0: IPL_DATA_ORDER_PIXEL - canales de color entrelazados.
• 1: canales de color separados.
• CreateImage solo crea imágenes con canales entrelazados. Por ejemplo, el layout común de colores de una
imagen es: b_00 g_00 r_00 b_10 g_10 r_10 ...
origin:
• 0: origen extremo superior izquierdo.
• 1: origen extremo inferior izquierdo, (estilo Windows bitmap).
align: Alineación de las filas de la imagen(4 u 8). OpenCV ignora este campo usando en su lugar widthStep.
width: Ancho de la Imagen en pixels.
height: Alto de la Imagen en pixels.
26
APÉNDICE B. OPENCV
roi: Region Of Interest (ROI). Si no es NULL, se procesa solo esta región de la imagen.
maskROI: Debe ser NULL en OpenCV.
imageId: Debe ser NULL en OpenCV.
tileInfo: Debe ser NULL en OpenCV.
imageSize: Tamaño en bytes de la imagen. Para datos entrelazados, equivale a: image->height * image->widthStep
imageData: Puntero a los datos alineados de la imagen.
widthStep: Tamaño en bytes de una fila de la imagen alineada
BorderMode y BorderConst: Modo de completamiento del borde, ignorado por OpenCV.
imageDataOrigin: Puntero el origen de los datos de la imagen (no necesariamente alineados). Usado para
desalojar la imagen.
B.2.4.
Opencv gira alrrededor deIplImage
La estructura IplImage se hereda de la Librería original de Intel. Su formato es nativo, aunque OpenCV solo
soporta un subset de formatos posibles de IplImage.
Además de las restricciones anteriores, OpenCV maneja las ROIs de modo diferente. Las funciones de OpenCV
requieren que los tamaños de las imágenes o los de las ROI de todas las imágenes fuente y destino coincidan exactamente.
Por otra parte, la Biblioteca de Intel de Procesamiento de Imágenes procesa el área de intersección entre las imágenes
origen y destino (o ROIs), permitiéndoles variar de forma independiente.
El tema es que cualquier imagen va a parar a una estructura de este tipo, incluso video. Al respecto, OpenCV permite
visualizar videos desde dos fuentes de información: cualquier Cámara web conectada a la PC, o desde archivo formato avi, o flv. Una imagen de video se compone de cuadros de n*m pixeles. Cada cuadro se carga en una estructura
IplImage
B.3.
Aplicaciones y más funciones
B.3.1.
Crear una imagen
El siguiente bloque de código permite crear una imagen. Su contenido inicial es vacío.
1
I p l I m a g e ∗ cvCreateImage ( CvSize s i z e , i n t depth , i n t c h a n n e l s ) ;
Listing B.6: Función para creación de una imagen
size: Tamaño en pixels del frame que va a contener la imagen:
typedef struct CvSize. Es una estructura que contiene la dimensión de una imagen:
B.3. APLICACIONES Y MÁS FUNCIONES
1
2
3
4
5
27
Typedef s t r u c t CvSize
{
i n t width ;
int height ;
} CvSize ;
Listing B.7: Estructura CvSize
Por su parte en general para muchas estructuras simples ocurre lo mismo, existe una función relacionada que
oficia de “Constructora”. En este caso:
cv Si ze ( i n t width , i n t h e i g h t ) ;
1
Listing B.8: Estructura CvSize
depth:profundidad del pixel en bits: IPL_DEPTH_8U, IPL_DEPTH_32F.
channels: Número de canales por pixel. Sus valores posibles son 1, 2, 3 o 4. Los canales están entrelazados. El
layout de datos usual de una imagen color es b0 g0 r0 b1 g1 r1.
Utilizando estas funciones tenemos los ejemplos de creación de una imagen.
1
2
3
4
/ / C r e a r una imagen con c a n a l de 1 b y t e
I p l I m a g e ∗ img1= c v C r e a t e I m a g e ( c v S i z e ( 6 4 0 , 4 8 0 ) , IPL \ _DEPTH \ _8U , 1 ) ;
/ / C r e a r una imagen con t r e s c a n a l de f l o a t
I p l I m a g e ∗ img2= c v C r e a t e I m a g e ( c v S i z e ( 6 4 0 , 4 8 0 ) , IPL \ _DEPTH \ _32F , 3 ) ; } }
Listing B.9: Ejemplos de creación de una imagen con cvSize y CvSize
B.3.2.
Operaciones Básicas
Es necesario además Cerrar y Clonar Imágenes. Los siguientes códigos ilustran estas operaciones con las funciones
que se utilizan.
Cerrar una imagen
1
c v R e l e a s e I m a g e ( \ & img ) ;
Listing B.10: Función para Cerrar una imagen
Clonar una imagen
1
2
3
I p l I m a g e ∗ img1= c v C r e a t e I m a g e ( c v S i z e ( 6 4 0 , 4 8 0 ) , IPL \ _DEPTH \ _8U , 1 ) ;
I p l I m a g e ∗ img2 ;
img2 = c v C l o n e I m a g e ( img1 ) ; } }
Listing B.11: Código para Clonar una imagen
28
APÉNDICE B. OPENCV
En la mayoría de las aplicaciones nos concentramos en cierta región de la pantalla, donde está la información que
queremos procesar. Esta zona se denomina ROI, siglas de Región Of Interest. Es como una sub matriz de la matriz
general. Setear u obtener la región de interés (ROI), requiere de algunas funciones en Opencv.
1
2
v o i d cvSetImageROI ( I p l I m a g e ∗ image , CvRect r e c t ) ;
CvRect cvGetImageROI ( c o n s t I p l I m a g e ∗ image ) ;
Listing B.12: Prototipos de las funciones para setear u obtener una ROI
Al trabajar con ROIs estamos definiendo “cajas“ dentro de la Imagen. Para ello es importante la estructura CvRect,
que define las coordenadas de la esquina superior izquierda y el tamaño del rectángulo, que arma la caja de la ROI.
1
2
3
4
5
6
7
t y p e d e f s t r u c t CvRect {
int x;
/ / c o o r d e n a d a x de l a e s q u i n a s u p e r i o r i z q u i e r d a
int y;
/ / c o o r d e n a d a y de l a e s q u i n a s u p e r i o r i z q u i e r d a
i n t width ;
/ / ancho d e l r e c t ángulo
i n t h e i g h t ; / / a l t o del r e c t ángulo
}
i n l i n e CvRect c v R e c t ( i n t x , i n t y , i n t w i d t h , i n t h e i g h t ) ; / / i n i c i a l i z a c i ón
Listing B.13: Estructura CvRect y su función constructora
B.3.3.
Manejando pixeles
Existen algunas otras Estructuras asociadas, para manejo de pixeles. Es una estructura CvScalar. Es un contenedor de un arreglo de 1, 2, 3, o 4 doubles. Cada double pertenece al valor R G B y Alfa. En caso de imágenes
monocromo contiene el valor en escala de gris en formato double. Su prototipo es:
1
2
3
typedef s t r u c t CvScalar {
double val [ 4 ] ;
} CvScalar ;
Listing B.14: Estructura CvScalar
Para pixeles color y monocromáticos a continuación algunos ejemplos de código para inicializarlos en valores
determinados.
1
2
3
4
5
6
/ / I n i c i a l i z a r v a l [ 0 ] con v a l 0 , v a l [ 1 ] con v a l 1 , e t c .
i n l i n e C v S c a l a r c v S c a l a r ( d o u b l e v a l 0 , d o u b l e v a l 1 =0 , d o u b l e v a l 2 =0 , d o u b l e v a l 3 = 0 ) ;
/ / I n i c i a l i z a r l o s c u a t r o e l e m e n t o s v a l [ 0 ] . . . v a l [ 3 ] con e l v a l o r v a l 0 1 2 3 .
i n l i n e CvScalar cvScalarAll ( double val0123 ) ;
/ / I n i c i a l i z a r v a l [ 0 ] con v a l 0 , y e l r e s t o ( v a l [ 1 ] . . . v a l [ 3 ] ) con c e r o s
i n l i n e CvScalar cvRealScalar ( double val0 ) ;
Listing B.15: Inicialización de pixeles
Obtener el valor de un pixel es fundamental a menudo para aplicar cualquier acción de transformación, procesamiento,
etc. En al figura siguiente vemos su significado. La función CvGet2D es útil a estos propósitos entregando CvScalar.
1
/ / P r o t o t i p o de cvGet2D
B.4. SEGUNDO EJEMPLO
2
C v S c a l a r s = cvGet2D ( img , row , c o l )
Listing B.16: Prototipo de cvGet2D
Si la imagen está en escala de grises, s.val[0] es el valor del pixel.
Si la imagen está en color,s.val[0],s.val[1], y s.val[2] son respectivamente R, G,y B.
Img es un puntero a la IplImage obtenida al abrir o crear la imagen.
row y col con x e y del slide anterior.
B.4.
Segundo Ejemplo
B.4.1.
Manejando video
Fuente: archivo avi
1
2
3
4
5
6
7
8
9
/∗ ∗
∗ \ f i l e aviexample . c
∗ \ b r i e f P r o g r a m a s e n c i l l o p a r a a b r i r un a r c h i v o a v i y r e p r o d u c i r l o en una v e n t a n a
∗ \ d e t a i l s U t i l i z a m o s l a b i b l i o t e c a Opencv p a r a a b r i r e l a r c h i v o a p r o v e c h a n d o
∗ s u s f u n c i o n e s i n d e p e n d i e n t e s d e l t i p o de a r c h i v o ( con a l g u n a s l i m i t a c i o n e s a
∗ l o s t i p o s mpeg , y t a m b i én p a r a c r e a r una v e n t a n a s e n c i l l a ( s i n menú ) , p a r a
∗ r e p r o d u c i r l o en l a GUI d e l s i s t e m a o p e r a t i v o que s e t e n g a ( Linux , Windows , o
∗ MAC OS ) .
∗ E s t e p r o g r a m a c o m p i l a en Linux , m e d i a n t e e l comando s i g u i e n t e :
29
30
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
APÉNDICE B. OPENCV
∗ g c c −o videmo a v i e x a m p l e . c ‘ pkg−c o n f i g −−c f l a g s −− l i b s opencv ‘ −g −ggdb −Wall
∗ \ a u t h o r A l e j a n d r o F u r f a r o . a f u r f a r o @ e l e c t r o n . f r b a . u t n . edu . a r
∗ \ date 22.06.2011
∗/
\ # i n c l u d e <cv . h>
\ # i n c l u d e < h i g h g u i . h>
i n t main ( i n t a r g c , c h a r ∗∗ a r g v )
{
/ / Creamos una v e n t a n a l l a m a d a " V i d e o P l a y e r " , a u t o a j u s t a b l e a l
/ / tamaño de l a imagen que l e vamos a c o l o c a r d e n t r o
cvNamedWindow ( " V i d e o P l a y e r " , CV\_WINDOW\ _AUTOSIZE ) ;
/ / E s t a f u n c i ón O b t i e n e e l I d e n t i f i c a d o r d e l d i s p o s i t i v o de c a p t u r a , en e s t e
/ / c a s o un a r c h i v o a v i .
CvCapture ∗ c a p t u r e = c v C r e a t e F i l e C a p t u r e ( argv [ 1 ] ) ;
/∗ ∗
∗ Solicitamos l a s c a r a c t e r í s t i c a s del d i s p o s i t i v o mediante la s i g u i e n t e
∗ f u n c i ón :
∗
double c v G e t C a p t u r e P r o p e r t y ( CvCapture ∗ c a p t u r e , i n t p r o p e r t y _ i d )
∗ O b t i e n e l a s p r o p i e d a d e s d e l d i s p o s i t i v o de c a p t u r a de v i d e o .
∗
Parámetros :
∗
c a p t u r e − E s t r u c t u r a de c a p t u r a de v i d e o .
∗
p r o p e r t y _ i d − I d e n t i f i c a d o r de l a p r o p i e d a d que s e q u i e r e o b t e n e r .
∗
Puede s e r a l g u n o de l o s s i g u i e n t e s v a l o r e s :
∗
CV_CAP_PROP_POS_MSEC − P o s i c i ón a c t u a l d e l F i l m en mseg . o
∗
t i m e s t a m p de c a p t u r a de v i d e o .
∗
CV_CAP_PROP_POS_FRAMES − I n d i c e b a s e −0 d e l f r a m e a d e c o d i f i c a r o
∗
capturar
∗
CV_CAP_PROP_POS_AVI_RATIO − P o s i c i ón r e l a t i v a d e l a r c h i v o de
∗
v i d e o ( 0 − s t a r t o f f i l m , 1 − end o f
∗
film ) .
∗
CV_CAP_PROP_FRAME_WIDTH − a n c h o de l o s f r a m e s d e l s t r e a m de v i d e o
∗
CV_CAP_PROP_FRAME_HEIGHT − a l t o de l o s f r a m e s d e l s t r e a m de v i d e o
∗
CV_CAP_PROP_FPS − Frame r a t e
∗
CV_CAP_PROP_FOURCC − Có d i g o de 4 b y t e s d e l c o d e c
∗
CV_CAP_PROP_FRAME_COUNT − Número de f r a m e s d e l a r c h i v o de v i d e o .
∗
CV_CAP_PROP_BRIGHTNESS − B r i l l o de l a imagen ( s o l o p a r a cá m a r a s )
∗
CV_CAP_PROP_CONTRAST − C o n t r a s t e de l a imagen ( s o l o p a r a cá m a r a s )
∗
CV_CAP_PROP_SATURATION − S a t u r a c i ón de l a imagen ( s o l o p a r a
∗
cá m a r a s )
∗
CV_CAP_PROP_HUE − Hue de l a imagen ( s o l o p a r a cá m a r a s )
∗/
d o u b l e f p s = c v G e t C a p t u r e P r o p e r t y ( c a p t u r e , CV_CAP_PROP_FPS ) ;
p r i n t f ( " Frame p o r s e g : %d \ n " , ( i n t ) f p s ) ;
getchar () ;
/ / I n s t a n c i a una e s t r u c t u r a I p l I m a g e , d e f i n i d a en openCV p a r a
/ / c a r g a r l a i n f o r m a c i ón de c a d a f r a m e
IplImage ∗ frame ;
while (1)
{
/ / Lee un f r a m e d e s d e e l d i s p o s i t i v o de c a p t u r a cuyo i d e n t i f i c a d o r o b t u v o
/ / al principio
f r a m e = cvQueryFrame ( c a p t u r e ) ;
i f ( ! frame )
B.4. SEGUNDO EJEMPLO
b r e a k ; / / S i e l p u n t e r o e s NULL, f a l l ó => S a l e
/ / La l e c t u r a f u e c o r r e c t a , p o r l o t a n t o p r e s e n t a e l f r a m e en l a v e n t a n a
/ / que d e f i n i ó
cvShowImage ( " V i d e o P l a y e r " , f r a m e ) ;
/ / E s t a l í n e a a d a p t a l a v e l o c i d a d de r e p r o d u c c i ón . P a r a e n t e n d e r l a , c o m e n t a r l a
compilar y reproducir : )
c h a r c= cvWaitKey ( ( i n t ) ( 1 0 0 0 / f p s ) ) ;
/ / De p a s o . . . s i p u l s a ESC s a l e
i f ( c == 27 )
b r e a k ; / / s i e s t á aquí s e p u l s ó ESC
63
64
65
66
67
68
69
70
71
}
/ / O l l e g ó a l f i n de l a p e l i . . . o f a l l ó a l g u n a l e c t u r a de f r a m e . . .
/ / Libera recursos y sale
c v R e l e a s e C a p t u r e ( &c a p t u r e ) ;
cvDestroyWindow ( " V i d e o P l a y e r " ) ;
return 0;
72
73
74
75
76
77
78
31
}
Listing B.17: Ejemplo2: Código para abrir y reproducir un archivo avi
Para empezar a trabajar abrir con el editor de texto preferido, el archivo aviexample.c que contiene el código del
listado B.17. Además se necesita una consola cuyo prompt esté posicionado en el directorio de trabajo en el que está
el programa aviexample.c. Para compilar, ejecutar en la consola el comando indicado en el encabezado del programa
del listado B.17, que reproducimos a continuación para mayor claridad:
1
g c c −o videmo a v i e x a m p l e . c −g −ggdb ‘ pkg−c o n f i g −−c f l a g s −− l i b s opencv ‘ −Wall
Listing B.18: Comando de compilación del código de B.17
B.4.2.
Análisis
¿Que hicimos?
Creamos una ventana llamada VideoPlayer.
1
cvNamedWindow ( " V i d e o P l a y e r " , CV_WINDOW_AUTOSIZE) ;
Listing B.19: Creación de una ventana VideoPlayer
Tomar un dispositivo de captura de Video.
1
CvCapture ∗ c a p t u r e = c v C r e a t e F i l e C a p t u r e ( argv [ 1 ] ) ;
Listing B.20: Obtención de un identificador para el dispositivo de captura
Obtiene los Frames por segundo del video contenido en el archivo.
1
d o u b l e f p s = c v G e t C a p t u r e P r o p e r t y ( c a p t u r e , CV_CAP_PROP_FPS ) ;
Listing B.21: Obtención de un identificador para el dispositivo de captura
32
APÉNDICE B. OPENCV
Crear un puntero a una estructura IplImage en donde se guardarán los frames.
1
IplImage ∗ frame ;
Listing B.22: Instancia de una estructura IplImage para almacenar los frames leídos
Luego entramos a un bucle infinito
Se obtiene cada frame del avi
1
f r a m e = cvQueryFrame ( c a p t u r e ) ;
Listing B.23: Lee un frame desde el dispositivo de captura
Y lo mostramos (esto ya lo aprendimos). . .
1
cvShowImage ( " V i d e o P l a y e r " , f r a m e ) ;
Listing B.24: Presenta el cuadro (frame) leido en la ventana
Finaliza cuando el puntero al frame es NULL (encontró EOF, o el archivo está corrupto).
1
i f ( ! frame ) break ;
Listing B.25: Presenta el cuadro (frame) leido en la ventana
Esperamos una tecla (Opencv tiene una función para esto también): Sin embargo la línea siguiente sirve fundamentalmente para sincronizar los frame a la velocidad de Frames per second obtenida.
1
c h a r c = cvWaitKey ( 3 3 ) ;
Listing B.26: Presenta el cuadro (frame) leido en la ventana
Liberamos recursos
1
2
c v R e l e a s e C a p t u r e ( &c a p t u r e ) ;
cvDestroyWindow ( " avidemo " ) ;
Listing B.27: Libera recursos antes de salir

Documentos relacionados