Notas de Clase - Alojamientos Universidad de Valladolid

Anuncio
Universidad de Valladolid
T
Tratamiento de Imagen y Sonido
DR
AF
Notas de Clase
Author:
Luis M. Fuentes
March 8, 2013
Chapter 1
Qt
1.1
1.1.1
Introducción a Qt
Presentación
DR
AF
T
Podemos definir Qt como una libreria/biblioteca de clases para el desarrollo de interfaces de usuario basados en C++. La aplicación ası́ desarrollada puede ser compilada y
ejecutada en Windows, Mac OSX, linux y varias ramas de Unix. Existe una versión de
Qt para la ejecución en plataformas móbiles.
Qt is a cross platform
development framework written in C++
Qt incluye soporte para otras tecnologı́as como OpenGL, XML, bases de datos, Webkit, multimedia, multithreading, iternacionalización, etc. Dispone además de enlaces o
bindings para utilizar otros leguajes de programación como Python, Ruby o C#.
DR
AF
T
Qt proporciona además un entorno de programación o IDE propio: Qt Creator
ası́ como un programa para la creación gráfica de los GUI s Qt Designer y un sistema
de ayuda propio Qt Assistant, como programa externo, además del incorporado en el
entorno de programación.
Figure 1.1: Program de ayuda para Qt: Assistant
Multitud de programas comerciales utilizan Qt, podemos citar Autodesk Maya, The
Foundry’s Nuke, Adobe Photoshop Album, Google Earth, Skype, VLC media player,
VirtualBox, Dassault DraftSight, Mathematica, etc. y empresas o entidades como la
Agencia Espacial Europea, DreamWorks, HP, KDE, Lucasfilm, Panasonic, Philips, Samsung, Siemens, Volvo, Walt Disney Animation Studios o Research In Motion, etc. En
resumen, Qt presenta una opción para la programación de GUI s con multiples ventajas,
entre las que destacan la portabilidad entre los sistemas operativos más comunes y la
presencia de untorno de programación integrado. Para finalizar, Qt es gratuito para el
desarrollo de aplicaciones de código abierto: http://qt-project.org
Figure 1.2: Algunos usuarios y aplicaciones Qt
Luis M. Fuentes
Tratamiento de Imagen y Sonido, UVa
3
1.1.2
Instalación
La forma más sencilla de instalar Qt es la descarga del Qt SDK que contiene:
• El entorno de trabajo Qt: las librerias de clases tanto en
binario como en código fuente, los ficheros de cabecera y
la documentación.
• El entorno de desarrollo integrado IDE Qt Creator
• Una utilidad para la actualizacion de los módulos del kit
• Las APIs para desarrollar una gran variedad de aplicaciones y servicios de plataformas móviles o familiares
para los usuarios de dichas plataformas, Qt Mobility.
DR
AF
T
• La nueva tecnologı́a declarativa para el desarrollo rápido
de GUIs, Qt Quick. Incluye QML, un lenguaje similar
a CSS + JavaScript.
Al escribir estas notas, el SDK conteniendo las librerias Qt 5.0.1 y el entorno de desarrollo Qt Creator 2.6.2 se puede descargar de (http://qt-project.org/downloads).
El archivo descargado es un ejecutable (.exe en Windows, .dmg en MacOSX y .run en
linux) preparado para instalar las librerı́as en formato y localización acorde con el sistema
operativo (es probable que, bajo linux, haya primero que hacer ejecutable el fichero de
instalacion chmod u+x instalador.run). No obstante, el paquete de linux está desarrollado para Ubunto, por lo que, para evitar complicaciones, es recomendable instalar las
librerias y el entorno de desarrollo usando los gestores de paquetes de la distribución que
se utilice:
• (K)ubuntu : qt-sdk
• Debian, OpenSUSE y Gentoo : qt-creator
• Arch Linux : qt qt-doc qt-creator
• Chakra linux : qt5-5.0.1-1 qtcreator-2.6.2-1 .....
Luis M. Fuentes
Tratamiento de Imagen y Sonido, UVa
4
En particular, en las distribuciones usando el escritorio KDE (entre las que recomendamos Chakra) las librerias ya están instaladas por defecto, luego solo se precisa la
instalación del entorno de desarrollo y la documentación.
Por último señalar que Qt está programada en C++ y los programas que se creen
con esta librerı́a deberan ser compilados con un compilador de C++. Esto es posible
hacerlo en un entorno de programacio como Qt Creator u otros como Visual Studio
o Eclipse descargando e instalando los plug-ins o añadidos necesarios. Sin embargo, es
tambien posible compilar y ejecutar desde la linea de comando. Para ello existe la utilidad
qmake que simplifica este proceso y lo hace viable independientemente de la plataforma
utilizada. qmake genera el archivo makefile, requerido para la compilacion con make,
o proyectos para Microsoft Visual Studio basandose en la inforación sobre el programa
contenida en el fichero proyecto (programa.pro).
1.1.3
Qt Creator
DR
AF
Introducción
T
Qt Creator1 es un entorno de desarrollo integrado (IDE) pensado para la creación
de aplicaciones que utilicen el entorno de programación Qt, desarrollado para la creación
de programas con interfaces de usuario complejas para su posterior distribución a varios
R
R
R
sistemas operatorios (Microsoft Windows
, Mac OS X
, and Linux
).
Qt Creator fué desarrollado para responder a las necesidades de los drogramadores
que utilizaban Qt y buscaban simplicidad, productividad, capacidad de expansión y
apertura manteniendo a la vez una curva de aprendizaje asumible para los recien llegados.
Las caracterı́sticas de Qt Creator permiten a los programadores:
• iniciar el desarrollo de una aplicación de una forma sencilla y rápida con el Project
Wizard o reanudar el trabajo donde se dejó mediante un acceso rápido y sencillo a
las sesiones y proyectos más recientes.
• utillizar el editor integrado (Qt Designer ) para el desarrollo de aplicaciones con
interfaz de usuario basado en las widgets de Qt.
• desarrollar aplicaciones utilzando el editor de código C++ .
• construir, ejecutar y empaquetar proyectos basados en Qt para multitud de plataformas desktop y móbiles como Microsoft Windows, Mac OS X, Linux, Symbian,
MeeGo, o Maemo.
• hacer debug con los debuggers GNU y CDB usando el entorno gráfico
• acceder de forma rápida y sencilla a la documentación con el sistema integrado de
ayuda Qt Help
Qt Creator está también disponible (http://qt-project.org/downloads#qt-creator)
para su descarga individual, proporcionando archivos de instalación para distintas plataformas.
1
http://qt-project.org/wiki/QtCreatorWhitepaper
Luis M. Fuentes
Tratamiento de Imagen y Sonido, UVa
5
Crear un proyecto
Al ejecutar por primera vez Qt Creator, o si no está programado para cargar automáticamente el último proyecto, el programa despliega el modo Welcome con las siguientes pestaña:
• Getting started, con una introducción al IDE, su interfaz de usuario, etc.
• Develop, que muestra los proyectos y archivos más recientes
• Examples, que permite el estudio y ejecución de la colección de ejemplos que ilustra
el funcionamiento de los distintos módulos de Qt
• Tutorials, videos para el aprendizaje de Qt
Como ejemplo sobre el que trabajar más adelante, creemos un proyecto con el sistema
automático que proporciona el programa:
T
1. Para ello seleccionamos la pestaña Develop en el modo Welcome (los modos se
encuentra en la parte superior del lateral izquierdo), seleccionando del panel de la
izquierda la opción Create Project.
DR
AF
2. Se abre una ventana para elegir el template del nuevo proyecto: seleccionamos Applications y de las opciones ofrecidas para este template, elegimos Qt Gui Application
y presionamos el botón choose.
3. Ahora seleccionamos el lugar en nuestro sistema de archivos donde se almacenrá
el proyecto y su nombre prueba. Acontinuación se selecciona el kit, es decir, la
plataforma para la que se desarrolla, elegiremos Desktop y sólo el modo Debug,
presionando continue.
4. A continuación se nos pregunta por el nombre de la clase principal de la GUI de
nuestro programa, normalmente la llamarameos MainWindow y heredará la clase
QMainWindow de Qt; los nombres de los archivos cabecera mainwindow.h y fuente
mainwindow.cpp serán sugeridos teniendo en cuenta el nombre elegido para la clas.
Por último nos aseguramos que la opción para le edición de la interfaz con Qt
Designer está desactivada (generate form sin seleccionar).
5. Tras presionar continue nos aparece un resumen del proyecto y se nos pregunta
si queremos mantener un sistema de versiones, que no seleccionaremos, por lo que
terminaremos presionando Done.
El proyecto ası́ creado ya es compilable y ejecutable y crea una ventana vacia.
Luis M. Fuentes
Tratamiento de Imagen y Sonido, UVa
6
T
DR
AF
Figure 1.3: Pantalla de entorno de desarrollo qt-creator
Escribiendo código
Para empezar a escribir código se selecciona el archivo en la lista de la derecha estando en el modo Edit. El editor de texto ofrece, entre otras, las siguientes ayudas al
programador:
• resalta de las palabras clave, sı́mbolos, constantes y macros en los ficheros C++.
• ofrece completar la palabra para elementos, propiedades, incluyendo las clases
creads por el usuario.
• comprueba la sintaxis del código, marcando errores mientras se escribe, haciendo
fácil la localización de errores tipográficos sin necesidad de compilar.
• puede comprimir o expandir (code folding) funciones en el código fuente, facilitando
la navegación por el mismo.
• localiza de forma rápida, ficheros, funciones, sı́mbolos y otro tipo de información
• permite hacer debug con inspección de variables, puntos de ruptura, etc.
• integra un sistema de ayuda presionando F1 sobre la palabra clave o clase elegida,
o activando el modo de ayuda con busqueda por temas, etc.
En definitiva, Qt Creator ofrece un entorno de desarrollo bastante completo para la
creación de aplicaciones basadas en Qt.
Luis M. Fuentes
Tratamiento de Imagen y Sonido, UVa
7
1.1.4
Módulos de Qt
Qt es una librerı́a modular que se ha ido expandiendo desde el módulo principal de
widgets para la creación de interfaces gráficos de usuario.
QtCore
Es el módulo que contiene el nucleo de las clases no gráficas. Todos los demás módulos
se basan en el. Contiene clases como QDir para gestionar directorios, QFile para escritura
o lectura de ficheros o QVector para el manejo de matrices dinámicas.
QtGui
QtOpenGL
DR
AF
T
El módulo que contiene los componentes necesarios para la creacion de interfaces
gráficas, widgets, es QtGui. Entendemos por widget cualquiera de los elementos que
forma parte de una interfaz gráfica de usuario, desde la ventana principal a un botón, ası́
como otras necesarias para definir sus atributos, comportamiento o colocación. Podemos
poner como ejemplo QColor para la definición y manejo de colores, QPushButton para
la creación de botones para presionar o QToolBar para la creación de una barra de
herramientas.
Este módulo ofrece clases que facilitan el uso de OpenGL en aplicaciones Qt, siendo
la más importante QGLWidget, una widget que admite y ejecuta todos los comandos
OpenGL.
QtNetwork
Aglomera las clases creadas para interaccionar con la red y crear aplicaciones que lo
hagan, tales como QFtp, QSslKey o QTcpSocket.
QtMultimedia
Es un módulo esencial que proporciona un conjunto de clases para el manejo de
contenido multimedia y el acceso al hardware multimedia, camara, microfono y altavoces,
tales como QCamera, QAudioInput o QMediaPlayer.
QtSql
Permite la programación de aplicaciones para el tratamiendo de bases de datos. Las
APIs de este módulo están divididas en tres capas: driver, SQL API e interfaz de usuario.
La primera de ellas Driver Layer proporciona el puente a bajo nivel para conectar la base
de datos y las APIs SQL usando clases como QSqlDriver o QSqlResult. La capa API
SQL API Layer proporciona acceso a las bases de datos usando las clases QSqlDatabase
o QSqlQuery. Finalemenet, la capa de interfaz de usuario User Interface Layer comunica
los batos de la base de datos con las widgets preparadas para acogerlos usando clases
como QSqlQueryModel.
Luis M. Fuentes
Tratamiento de Imagen y Sonido, UVa
8
Otros
Qt dispone además de otros módulos para la programación de dontenidos especı́ficos,
como QtSvg, QtQml, etc. Se puede obtener una información más completa ası́ como la
enumeración de todas las clase implicads y ejemplos de uso en:
h t t p : // q t −p r o j e c t . o r g / doc / q t −5.0/ q t d o c / modules . html
1.2
qmake y archivos de proyecto .pro
Los ficheros de proyecto *.pro contienen toda la información necesaria para que
qmake construya la aplicación, librerı́a o plugin. Las fuentes utilizadas en el proceso
se especifcan en el fichero mediante una serie de declaraciones. Modificadores adicionales
permiten la especificación de recursos especı́ficos de cada plataforma. A continuación
aparece un resumen del contenido del manual on-line sobre qmake,
1.2.1
Variables
T
h t t p : // q t −p r o j e c t . o r g / doc / q t −4.8/ qmake−manual . html
DR
AF
En el fichero de proyecto las varables se utlizan como contenedores de listas de cadenas
de caractéres (strings). En un fichero de proyecto muy simple aparecen únicamente la
configuracion a usar y los nombres de ficheros involucrados
TARGET = prueba
TEMPLATE = app
SOURCES += main . cpp mainwindow . cpp
HEADERS += mainwindow . h
qmake busca ciertas variables en el fichero de proyecto y usa su contenido para la
creación del fichero Makefile. En el ejemplo anterior, se debe de construir una aplicación
llamada prueba con los ficheros fuente main.cpp y mainwindow.cpp y el cabecera mainwindow.h. Veamos algunas de estas variables:
• CONFIG Opciones generales del proyecto (release, debug, warn on, ...)
• DESTDIR El directorio en el que se situará el ejecutable o fichero final
• FORMS Lista de ficheros .ui con la información de la interfaz gráfica
• HEADERS Lista de ficheros cabecera .h usados en el proyecto
• QT Opciones de configuración especı́ficas de Qt (+opengl)
• RESOURCES lista deficheros .rc a incluir eb el proyecto final
• SOURCES lista de ficheros de código fuente utilizados en la construcción del programa
• TEMPLATE el patró usado en la construcción de la aplicación (app, lib, vcapp, ...)
Se puede acceder a los contenidos de una variable con el operador $$ y asignar, por
ejemplo, los contenidos de una variable a otra:
TEMP SOURCES = $$SOURCES
Luis M. Fuentes
Tratamiento de Imagen y Sonido, UVa
9
1.2.2
Otros conceptos
Espacios en blanco
Normalmente, las variables contiene listas de valores separados por espacios en blanco.
Sin embargo, cuando uno de estos valores contiene un espacio en blanco, el valor debe
aparecer entre comillas,
DEST = ” Program F i l e s ”
INCLUDEPATH += ”C: / m y l i b s / e x t r a h e a d e r s ”
tratandose al texto entre comillas como un elemento de la lista de valores. Se usa también
el entrecomillado para especificar paths que contengan espacios
Comentarios
Es posible añadir comentarios en un fichero de proyecto. Los comentarios deben
comenzar con el carácter # y pueden extenderse sólo una lı́nea
DR
AF
T
# Comments u s u a l l y s t a r t a t t h e b e g i n n i n g o f a l i n e , but they
# can a l s o f o l l o w o t h e r c o n t e n t on t h e same l i n e .
Funciones internas y control del flujo
Existen funciones internas a qmake que permiten el procesamiento del contenido de
las variables. La más usada es la función include seguida del nombre de un fichero de
proyecto cuyo contenido es incluido en lugar de la función.
i n c l u d e ( o t h e r . pro ) .
Se pueden usar tambien contenidos condicionados, como los llamados scope que permiten condicionar una parte del fichero a la plataforma, pues sólo se ejecutan cuando la
condición se cumple:
win32 {
SOURCES += p a i n t w i d g e t w i n . cpp
}
La variable win32 adquiere automáticamente el valor true bajo el sistema opertivo Windows, pero puede inicializarse tambien en otras plataformas con la opción -win32 en la
ejecución de qmake. hay que destacar que el primer paréntesis debe situarse en la misma
lı́nea que la condición. Hay que destacar que kas opciones especificadas en CONFIG
pueden usarse tambien como condicionante:
CONFIG( o p e n g l ) {
message ( B u i l d i n g with OpenGL s u p p o r t . )
} else {
message (OpenGL s u p p o r t i s not a v a i l a b l e . )
}
Luis M. Fuentes
Tratamiento de Imagen y Sonido, UVa
10
1.2.3
Declaración de librerı́as
Si la variable CONFIG contiene el valor qt se habilita el soporte para aplicaciones
Qt. Esto posibilita ajustar los módulos de Qt que van a utilizarse en la aplicación. Esto
se sonsigue utilizando la variable QT, que puede utilizarse para declarar los módulos que
requerirá la aplicación. Ası́, por ejemplo, para utlizar los módulos de red y bases de datos
introducirı́amos en el fichero de proyecto:
CONFIG += qt
QT += network xml
Por defecto, qmake incluye los dos módulod básicos en la construcción del programa
QtCore y QtGui. Si, por ejemplo, no queremos usar interfaz gráfica podemos eliminar el
segundo de ellos con
QT −= g u i
LIBS += −L/ u s r / l o c a l / l i b −lmath
T
Si se desea utilizar otras librerı́as externas en la aplicación, hay que decalararlas en el
fichero de proyecto. Los directorios en los que qmake debe buscar las librerı́as especificadas se añaden a la variable LIBS usando la notación tı́pica de los sistemas estilo UNIX:
−L para especificar directorios y −l para ficheros, sin espacios en blanco:
DR
AF
La especificación de las directorios de los ficheros cabecera se realiza usando la variable
INCLUDEPATH, separados por un espacio en blanco.
INCLUDEPATH = c : / msdev/ i n c l u d e d : / s t l / i n c l u d e
1.2.4
Resources: Imagenes, iconos, etc
Se pueden usar imágenes para mejorar el impacto visual, iconos para personalizar el
ejecutable, sonidos para determinados eventos, etc. Qt permite que sus proyectos utilicen
o incorporen dichos elementos, llamándolos fuentes (resources). Este tipo de archivos
están almacenados en diferentes directorios del disco lo que dificulta la portabilidad del
proyecto. La ventaja de incorporar dependencias como éstas en el fichero de proyecto es
que se puede acceder a ellas usando paths que no dependen de la computadora en uso y
que se incorporan automáticamente al instalar el ejecutable.
Qt proporciona dos métodos para usar iconos estándar: uno es obtenerlo del estilo
usado en la presentación del desktop QStyle::standardIcon() y el otro del algun plugin con temas o iconos QIcon::fromTheme(). También es posible usar iconos o imágenes
de otras fuentes. Para ello se crea un fichero de fuentes (.qrc), en formato XML, que se
ha de añadir al proyecto:
<!DOCTYPE RCC>
<RCC v e r s i o n=” 1 . 0 ”>
<q r e s o u r c e >
< f i l e >images / copy . png</ f i l e >
< f i l e >images / c u t . png</ f i l e >
< f i l e >images /new . png</ f i l e >
</q r e s o u r c e >
</RCC>
Luis M. Fuentes
Tratamiento de Imagen y Sonido, UVa
11
Por defecto, los ficheros son accesibles desde la aplicación usando el mismo nombre
especificado en el fichero de fuentes, con el prefijo :/ (:/images/cut.png) o accediendo en
formato URL qrc (qrc:///images/cut.png). Pero podemos ismplificar el acceso, caso de
que el nombre resultaria demasiado largo, usando el atributo alias del comenado file:
< f i l e a l i a s=” c u t . png”>images / c u t . png</ f i l e >
y accediendo a el como :/cut.png. También es posible especificar un path comun para
todos los ficheros usando el atributo prefix del comando qresource y accediendo con el
path :/resources/cut.png:
<q r e s o u r c e p r e f i x=” / r e s o u r c e s ”>
< f i l e a l i a s=” c u t . png”>images / c u t . png</ f i l e >
</ q r e s o u r c e
Antes de usarse, el fichero de fuentes ha de ser compilado. Para ello se introduce en
el proyecto añadiendo en el archivo .pro la lı́nea (normalmente nombre coincide con el
nombre de la aplicacion):
RESOURCE += nombre . q r c
1.3
1.3.1
DR
AF
T
Qt-Creator es también un editor de archivos de fuentes y puede ser utilizado para su
creación o modificación, siendo más sencillo añadir ası́ nuevos ficheros.
QObject: Signals y Slots
QObject
QObject es la clase base de todos los objetos Qt (clases derivadas de ella), la base
de su modelo de funcionamiento con signals y slots para comunicarse con otros objetos.
Se puede conectar una signal con un slot con connect() y deshacer la conexión con
disconnect(), bloquear señales con blockSignals() o seguir la estela de conexiones
con las funciones protegidas connectNotify() y disconnectNotify().
Los objetos se organizan en árboles. Cuando se crea unobjeto nuevo especificando
otro objeto como padre, este último le añade de forma automática a su lista de hijos,
tomando posesión de él, es decir, destruirá automáticamente todos sus hijos en su destrutor. Cuando se destruye un objeto, se emite de forma autmática la señal destroyed().
QObject es la clase base en la mayorı́a de las clases QT. Algunos ejemplos de excepciones son las clases que deben ser pequeñas o ligeras (como las primitivas gráficas), las
clases que son contenedores de datos (como QString o Qlist) y las clases que necesitan
la posibilidad de ser copiadas (los QObjects no puden copiarse). Esto se debe a una
caracterı́stica de QObject, cada instancia es única:
QObject instances are individuals!
y, por lo tanto, pueden tener un nombre propio (QObject::objectName),pueden comunicarse con otros objetos y ocupan un lugar en la jerarquı́a de objetos.
Todos los objetos se preprocesan y convierten en meta-objetos. Cada clase que se derive de un QObject (o de otra clase derivada) y quiera contar con el mecanismo de signals
Luis M. Fuentes
Tratamiento de Imagen y Sonido, UVa
12
y slots debe incorporar en la primera linea de coódigo de su definición la macro Q OBJECT.
El meta-objeto tiene información sobre la clase del objeto (QObject::className), sus
propiedades, sus signals y slots e información en general suminstrada en la definición de
la clase. Esta información se recopila en tiempo de compilación de los ficheros de cabecera
que contienen las declaraciones de las clases mediante el met-object compiler o moc:
T
Figure 1.4: Proceso de construcción de un proyecto Qt C++
DR
AF
Las propiedades de los QObjects son manipuladas usando métodos estádar denominados coloquialmente setter y getter, uno para dar un valor a la propiedad y otro para
obtenerlo: setter devuelve void y lleva el valor a asignar como único argumento mientras
que getter no tiene argumentos y devuelve el valor. La existencia de este tipo de funciones otorga ciertas ventajas, como la posibilidad de comprobar los valores asignados o
reaccionar a los cambios. Existe también un convenio para sus nombres:
• No boleanas : propiedad - setPropiedad
• boleanas : isEnabled - setEnabled
Estas propiedades pueden accederse directamente, usando getter y setter o a través del
sistema de meta-objetos:
QString t e x t = l a b e l −>t e x t ( ) ;
QString t e x t = o b j e c t −>p r o p e r t y ( ” t e x t ” ) . t o S t r i n g ( ) ;
l a b e l −>s e t T e x t ( ” H e l l o World” ) ;
o b j e c t −>s e t P r o p e r t y ( ” t e x t ” , ” H e l l o World” ) ;
para finalizar volvamos con el manejo de memoria. Como se mencionó con anterioridad, cuando en la creación de un objeto se especifica su padre, el objeto creado pasa
a pertenecer al arbol del progenitor y por lo tanto será destruido de forma automática
cuando este se destruya, lo que simplifica enormementa la programación al no tener que
verificar la destrucción de dichos objetos; resulta muy útil al implementar jerarquias
visuales en las ventanas:
QDialog ∗ padre = new QDialog ( ) ;
QGroupBox ∗ c a j a = new QGroupBox ( padre ) ;
QPushButton ∗ boton = new QPushButton ( padre ) ;
QRadioButton ∗ o p c i o n 1 = new QRadioButton ( c a j a ) ;
QRadioButton ∗ o p c i o n 2 = new QRadioButton ( c a j a ) ;
delete p a r e n t ;
Luis M. Fuentes
Tratamiento de Imagen y Sonido, UVa
13
donde padre destruye caja y boton mientras caja borra opcion1 y opcion2. Por supuesto,
los objetos pueden cambiar de progenitor:
obj−>s e t P a r e n t ( nuevoPadre ) ;
1.3.2
Signals y Slots
Signals y Slots, o señales y procedimientos, constituyen la herramienta básica en
la comunicación entre QObjects y la caracterı́stica principal de las librerı́as Qt, que hace
fácil la implementación del Observer Pattern.
Cualquier Widget (o clase derivada de QObject creada por el usuario) emite señales
cuando ciertos eventos tienen lugar. Por ejemplo, un botón emite la señal clicked cuando
es presionado. El usuario puede optar por conectar esa señal (signal) creando una función
(slot) y luego conectando ambas con la función connect().
c o n n e c t ( button , SIGNAL( c l i c k e d ( ) ) , qApp , SLOT( q u i t ( ) ) ) ;
DR
AF
T
Esta comunicación no requiere que las clases que emite la señal y la que ejecutará el
procedimiento se conozcan entre sı́, lo que simplifica la reutilización de las clases. Signals
y Slots son type-safe, es decir, sólo admiten argumentos del tipo declarado y los errores
de tipo se anuncian como advertencias sin causar la interrupción del programa.
Figure 1.5: Mecanismo Signal-Slot de Qt
Además, se pueden añadir o eliminar conexiones en tiempo de ejecución, se pueden
ejecutar los meétodos de forma inmediata cuando se emite la señal o ponerlos en cola para
una ejecución retardada e incluso se pueden comunicar objetos que se están ejecutando
en distintos hilos.
La implementación del mecanismo signal-slot se realiza en C++ usando el preprocesador y el compilador moc (Meta-object Compiler). La generación de éste código se realiza
Luis M. Fuentes
Tratamiento de Imagen y Sonido, UVa
14
de forma automática y el programador no necesita editar o incluso ojear dicho código.
Para finalizar, un apunte sobre codificación 2 , Signals y Slots son más reutilizables si
no utilizan argumentos de tipos especiales.
Signals
Slots
DR
AF
T
Un objeto emite una señal cuando su estado interno ha cambiado de forma que pueda
resultar interesante para el cliento o el propietario de dicho objeto. Sólo la clase en la
que está definida una señal, o sus derivadas, pueden emitir dicha señal.
Cuando se emite una señal, los slots conectados a ella suelen ser ejecutados inmediatamente, como cualquier llamada a una función, haciendo al mecanismo signal-slot totalmente independiente de cualquier evento generado por la GUI. La ejecución del código
situado detras de la función emit se llevará a cabo una vez que todos los slots hayan
terminado su ejecución. Cuando se ejecutan conexiones a cola, el codigo que sigue a emit
se ejecuta inmediatamente y los slots se ejecutarán posteriormente.
Si hay varios slots conectados con la misma signal, éstos serán ejecutados uno tras
otro en el mismo orden en que se declaró la conexión tras la emisión de la señal.
El moc genera automáticamente la implementación de las señales que el programador
debe únicamente definir, en el fichero de declaración de la clase (un fichero cabecera .h),
usando el tipo void, ya que no pueden devolver ningún valor.
Un slots es ejecutado cuando la señal a la que está conectado es emitida. Los slots
son funciones C++ normales y pueden ser ejecutados fuera de la conexión signal-slots,
siendo si caracterı́stica peculiar el hecho de que pueden ser conectados con señales.
Al ser funciones normales, siguen las reglas básicas de acceso según su tipo de declaraciõ
(public, protected o private). Como slots, sin embargo, peden ser llamados fuera de esas
reglas via conexión con señales. Esto significa que una señal emitida por un ejemplar de
una clase arbitraria puede causar la ejecución de un private slot en un ejemplar de una
clase no relacionada.
Comparados con otro tipo de tratamiento de eventos, como los callbacks, signals
and slots es logeramente más lento debido a su mayor flexibilidad, aunque dicha diferencia
en aplicaciones reales es insignificante. En general, emitir una señal conectada a varios
slots es unas 10 veces más lento que evocar los procedimientos directamente. Aunque
pueda parecer mucho, este retraso es menor que le conlleva laejecución de operaciones
como new o delete. Para una mayor ilustración del tiempo requerido, comentar que con
un procesador i586-500 se pueden emitir unos 2.000.000 de señales por segundo, si están
conectadas a un sólo slot o unas 1.200.000 si tienen dos receptores.
Conexión
Una señal de un objeto puede conectarse con slots de uno o más objetos siempre y
cuando dichos slots existan y los parámetro sean compatibles. la sintaxis completa de la
función de asignación connect es :
2
http://qt-project.org/doc/qt-4.8/signalsandslots.html
Luis M. Fuentes
Tratamiento de Imagen y Sonido, UVa
15
Figure 1.6: Conexiones viables segun los argumentos
QObject : : c o n n e c t ( QObject ∗ , char ∗ , QObject ∗ , char ∗ , Qt : : ConnectionType )
siendo su redacción usual algo como:
connect
T
bool
( e m i s o r ∗ , SIGNAL( s i g n a l ( type ) ) , r e c e p ∗ , SLOT( s l o t ( type ) ) ) ;
DR
AF
El último argumento enum Qt::ConnectionType es opcional siendo e valor por defecto Qt::AutoConnection. Esta variable enum describe el tipo de conexión a establecer,
en particular, determina cuando la señal es enviada al receptor inmediatamente o a través
de una cola. Los posible valores que puede tomar son:
• Qt::AutoConnection (0) : es el valor por defecto. Si la señal es emitida desde un
hilo distinto al del receptor, se situa a la cola (Qt::QueuedConnection), si no ocurre
ası́, el slot es invocado inmediatamente (Qt::DirectConnection). Esto hace que el
tipo de conexión se determine en el momento en que se emite la señal.
• Qt::DirectConnection (1) : el slot se ejecuta justo después de emitirse la señal.
• Qt::QueuedConnection (2) : la ejecución del slot se retrasa hasta el momento
en que el control vuelve al ciclo de eventos del hilo del receptor: el slot se ejecuta
en el ciclo del receptor.
• Qt::BlockingQueuedConnection (4) : Igual que el anterior salvo que el hilo en
ejecución se bloquea hasta que el slot devuelve el control. Esta conexxión sólo debe
usarse cuando emisor y receptor están en distintos hilos de ejcución, de no hacerse
ası́ la aplicación puede entrar en un ciclo infinito.
• Qt::UniqueConnection (0x80) : Igual que el primero pero la conexión se realiza
sólo si no repite una conexión pre-existente, es decir, si la misma señal ya está
conectada con el mismo slot.
• Qt::AutoCompatConnection (3) : Es la conexión por defecto cuando se habilita
la compatibilidad con Qt 3. Es igual que la primera pero causará algunas alarmas
en determinadas situaciones.
Luis M. Fuentes
Tratamiento de Imagen y Sonido, UVa
16
Con conexión a cola, los parámetros deben ser de tipos conocidos por moc, puesto que
Qt debe almacenarlos en un evento oculto. Qt puede ignorar argumentos pero no puede
crearlos de la nada. Esta regla sirve para saber qué conexiones son posibles y cuales no,
como muestra la figura ??. La conexión signal-slot se rompe cuando cualquiera de los
objetos involucrados es destruido o cuando se realiza dicha desconexión usando la funcion
bool
1.3.3
QObject : : d i s c o n n e c t ( QObject ∗ , char ∗ , QObject ∗ , char ∗ )
QApplication y el bucle de eventos
Las aplicaciones Qt con interfaces gráficas interactivas tienen un control de flujo distinto de las aplicaciones de linea de comando: estan guiadas por eventos (event-driven).
Patrón de Diseño OBSERVER
DR
AF
T
Al escribir programas guiados por eventos las GUI necesitan responder al cambio de
estado de ciertos objetos. Cunado se produce un cambio de estado en un objeto se
necesita una forma de advertir (y quizas enviar información) a los objetos son observadores. Los observadors son objetos que están esperando eventos de cambio de estado
y, cuando se producen, responden adecuadamente. El patrón de diseño que permite los
mecanismos de transmisión de mensajes de este tipo entre objetos se llama Patrón
Observvador. Por supuesto, hay muchas formas de implementar este patrón, pero
todas ellas tienen algunas caracterı́sticas comunes:
• Se permite la separación concreta entre clases sujeto y clases observador
• Se permite la emisión múltiple de comunicaciones 1 a n
• El mecanismo utilizado para enviar la información del sujeto al observador queda
especificado en la definición de clase del sujeto
La clase QEvent encapsula un evento de bajo nivel. Es la clase base para las clases asociadas con eventos especı́ficos como QActionEvent, QFileOpenEvent, QHoverEvent, QInputEvent o QMouseEvent. El sistema gráfico crea eventos de este tipo como respuesta a
acciones del usuario (QMouseEvent), a intervalos de tiempo determinados (QTimerEvent)
o de forma explı́cita por una aplicación. La función miembro type() devuelve una variable
enum identificando el evento (Close, DragEnter, Enter, MouseMove, Resize, etc).
El bucle de eventos (event-loop) es una estructura de programa que permite priorizar,
poner en lista de espera y enviar eventos y mensajes entre objetos. Escribir una aplicación
basada en eventos implica implementar una estructura pasiva de funciones que respondan
a eventos como clicks de ratón, teclas, señales emitidas, eventos del gestor de ventanas
o mensajes de otros prgramas hasta la recepción del evento que pare su ejecución. Este
bucle de eventos se gestioan en una aplicación Qt en el programa main mediante el
procedimiento exec() de la clase QApplication, como sucede en la aplicación ejemplo
creada anteriormente:
#i n c l u d e ”mainwindow . h”
#i n c l u d e <QApplication >
Luis M. Fuentes
Tratamiento de Imagen y Sonido, UVa
17
int main ( int argc , char ∗ argv [ ] )
{
Q A p p l i c a t i o n a ( argc , argv ) ;
MainWindow w ;
w. show ( ) ;
return a . e x e c ( ) ;
}
La llamada a la función QApplication::exec() se ejecuta en la sentencia return al final
del programa dando lugar al inicio de la parte interactiva de la aplicación.
1.4
Widgets
DR
AF
T
Una widget es un objeto gráfico de una clase derivada de QWidget que tiene una
represntación visual en la pantalla. La estructura original de QWidget, figura ??, muestra
multi-herencia: es un QObject, lo que le permite tener padres e hijos junto con señales y
slots, y un QPaintDevice, la clase base de todos los objetos mostrados en pantalla.
Figure 1.7: Linaje de QWidget en el arbol de clases de Qt
Al ser elementos gráficos, las widgets incorporan relaciones visuales en el parentaje:
una widget sin padre es una ventana o una widget hijo está contenida dentro del padre y
colocada según indique la disposició asociada a ésta. Aunque QWidget es la más sencilla
de las clases de QtGui (consiste en un rectángulo vacı́o), es una cas complicada con
cientos de funciones y capaz de manejar eventos respondiendo a mensajes, pintarse en la
panatalla, borrarse de la misma y respetando siempre el resto de elementos presentes.
Una aplicación con GUI para computadoras Desktop puede contener muchas (a veces
cientos) de widgets creadas segú una relación padre-hijo determinada y dispuestas de
acuerdo a las espcificaciones de ciertos layouts.
1.4.1
Tipos o categorı́as de widgets
Las QWidgets se pueden ordenar en tipos o categorı́as par facilitar su búsqueda. Algunas de ellas, las más complicadas, podrı́an pertenecer a más de una de las cuatro categorı́as
Luis M. Fuentes
Tratamiento de Imagen y Sonido, UVa
18
Figure 1.8: Algunos ejemplos de widgtes
1.4.2
DR
AF
T
básicas: Button widgets (QPushBotton), Input widgets (QSpinBox), Display widgets
(QLabel) y Container widgets (QToolBar). Estas widgets básicas son los ladrillos con
los que se construyen widgets más complicadas con funciones muy determinadas como
QFileDialog (para seleccionar un archivo del sistema) o QErrorMessage (para mostrar
mensajes de error).
Layouts: disposición espacial de las widgets
Hay widgets que se presentan como ventanas individuales (QDialog) pero la mayorı́a
pueden formar parte de una ventana de mayor tamaño. Cuando queramos organizar
widgets pequeñas dentro de una mayor debemos usarLayouts. Un layout es un objeto
(hereda QObject pero no QPaintDevice, luego no es una widget) que pertenece (es hijo
de) una sóla widget y su tarea es controlar de forma dinámica el espacio ocupado y la
distribución de las wodget hijas de su dueña.
Hay varios tipos de layouts que organizan las widgets de distinta forma: QHBoxLayout
horizontalmente, QVBoxLayout verticalmente, QGridLayout en una tabla o QFormLayout
en un impreso. Los layouts pueden usarse recurrentemente (de forma anidada).
Las widgets se organizan secuencialmente cuando se añaden al layout usando la
función:
void QLayout : : addWidget ( QWidget ∗ )
Cuando se añade una widget a un layout, pasa a ser hija de la widget que posee el layout
(porque una widget no puede ser nunca hija de un layout y sólo puede tener un hijo
layout). Finalmente, los layouts pueden tener hijos layouts: pueden añadirse igual que
una widget usando le función:
void QLayout : : addLayout ( QLayout ∗ )
1.4.3
Tamaño de widgets y layouts
Aunque cada widget puede tener un tamaño fijo establecido con setGeometry(),
posiciones y ta,años absolutos no suelen usarse por eliminar la flexibilidad del cambio de
Luis M. Fuentes
Tratamiento de Imagen y Sonido, UVa
19
tamaño de la ventana. Sin embargo, puede controlarse el comportamiento por defecto
cuando haya un redimensionamiento usando sizePolicy() y sizeHint(). La priopiedad
sizeHint almacena el tamaño recomendado para cada widget (el utilizado en su primera
presentación en la pantalla) mediante un valor QSize (almacena anchura y altura). Hay
tambien varias funciones que permiten controlar el tamaño de una widget en tiempo
de ejcución como setMinimumSize(), setMaximumSize(), setMinimumHeight(), setMaximumWidth(), setSizeHint(), etc. Además, cada widget (y layout) tienen reglas para
regular su redimensionamiento vertical y horizontal expresados usando QSizePolicy.
QBoxLayout tiene funciones especı́ficas para regular los espacios entre las widgets que
maneja:
• addSpacing(int size) añade un número fijo de pixels al final del layout
• addStretch(int stretch=0) añade un numero de pixels expandible: empieza con un
mı́nimo y se puede expandir hasta usar todo el espacio disponible.
• addStrut(int size) impone un mı́nimo al tamaño del layout en su dirección perpendicular (anchura de un QVBoxLayout o altura de un QHBoxLayout).
1.4.4
DR
AF
T
Estas caracterı́sticas se pueden utilizar para regular el comportamiento de la widget
cuando ésta se redimensiona.
Widgets complejas
Frente a las widgets simples que componen los elemntos básicos de una GUI, Qt ofrece
también widgets más complejas, productos terminados, listos para ser utilizados con un
propósito muy determinado. Veamos algunos ejemplos.
• Para la obtención del nombre de un archivo con el que posteriormente trabajar, Qt
ofrece un procedimiento asociado con la clase QFileDialog:getOpenFileName() con
una flexibilidad total de uso, sin argumentos o especificando path inicial y filtros
para los ficheros a mostrar:
QString f i c h e r o = Q F i l e D i a l o g : : getOpenFileName ( ) ;
QString f i c h e r o = Q F i l e D i a l o g : : getOpenFileName
( this , ”Open F i l e ” , ” /home” , ” Images ( ∗ . png ∗ . xpm ∗ . j p g ) ” ) ;
Luis M. Fuentes
Tratamiento de Imagen y Sonido, UVa
20
• Para mostrar un dialogo de opción SI o NO (por ejemplo respondiendo a la pregunta Desea abandonar la aplicación? ), utilizando el procedimiento ligado a la clase
QMessageBox, una heredera de QDialog, QMessageBox::question
int r e s p u e s t a =
QMessageBox : : q u e s t i o n ( this , NULL,
” Desea abandonar l a a p l i c a c i o n ? ” , QMessageBox : : Yes | QMessageBox : : No ) ;
T
• Existen también dialogos ya creados para elegir un color o un tipo de letra usando
los métodos QColorDialog::getColor y QFontDialog::getFont, por ejemplo de
la siguiente forma:
DR
AF
bool ok ;
QColor c o l o r = QColorDialog : : g e t C o l o r ( ) ;
QFont f o n t = QFontDialog : : getFont (&ok , QFont ( ” H e l v e t i c a ” , 1 0 ) , t h i s ) ;
1.5
1.5.1
Odds and Ends
Memoria
A la hora de crear interfaces gráficas se nos plantea el problema de la localización:
heap o stack. Cuando se crea un objeto con el comando new la memoria utilizada está
situada en el heap y debe ser liberada explicitamente con el comando delete, aunque
hasta ese momento esta accesible. Las variables locales se almacenan en el stack y son
Luis M. Fuentes
Tratamiento de Imagen y Sonido, UVa
21
destruidas automaticamente al abandonar el entorno (normalmente una función) en el
que se crearon.
Recordando entonces la relación de los objetos con sus progenitores, sólo el padre
debe ser almacenado en la pila o stack:
int main ( int argc , char ∗∗ argv )
{
Q A p p l i c a t i o n a ( argc , argv ) ;
mainWindow w ;
w. show ( ) ;
return a . e x e c ( ) ;
}
mainWindow : : mainWindow ( . . . )
{
QLabel ∗ l b = new QLabel ( t h i s ) ;
new . . . . . .
}
To get automatic memory management, only the parent needs to be allocated on the
stack.
1.5.2
Constructores
T
Existen algunas reglas de etiqueta a seguir en la codificación de constructores. Veamos
algunas de ellas:
DR
AF
• Casi todos los QObjects pueden tener un progenitor con valor por defecto nulo 0
QObject ( QObject ∗ p a r e n t =0);
• Los progenitores de QWidgets son otras QWidgets
• En Qt las clases tienden a proporcionar muchos constructores, incluyendo uno con
el progenitor como único argumento.
QPushButton ( QWidget ∗ p a r e n t =0);
QPushButton ( const QString &t e x t , QWidget ∗ p a r e n t =0);
QPushButton ( const QIcon &i c o n , const QString &t e x t , QWidget ∗ p a r e n t =0);
• El progenitor es, normalmente, el primer argumento con un valor por defecto
QLabel ( const QString &t e x t o , QWidget ∗ p a r e n t =0, Qt : : WindowFlags f =0);
Según esto, al crear un QObject (una QWidget por ejemplo) conviene permitir que
el progenitor tenga valor nulo, tener un constructor que tenga como único argumento al
progenitor y que cuando tenga más, que sea éste el primero con valor por defecto.
1.5.3
Impresión de informacion en tiempo de ejecución
Para obtener información de algunos parámetros en tiempo de ejecución sin entrar
en modo Debug, podemos usar la clase QDebug. Su utlización es muy sencilla, se debe
incluir el include en el fichero cpp correspondiente y luego redirigir variables o cadenas
segun formato C++
Luis M. Fuentes
Tratamiento de Imagen y Sonido, UVa
22
c l a s s MiClase : public QObject
{
Q OBJECT
\\ Macro
Q CLASSINFO( ” a u t o r ” , ” L u i s M. Fuentes ” ) \\ I n f o r m a c i o n G e n e r a l
Q PROPERTY ( Dato dato READ dato WRITE s e t D a t o )
public :
MiClase ( const Dato &dat , QObject ∗ p a r e n t =0) ;
Dato dato
( ) const ;
\\ G e t t e r
public s l o t s :
\\ Pa labr a Clave de Qt
void s e t D a t o ( const Dato &dat ) ;
\\ S e t t e r
signals :
\\ Pa lab ar a Clave de Qt
void datoCambiado
( Dato ) ;
private :
Dato mDato ;
} ;
T
Figure 1.9: Ejemplo de código de una clase basada en QObject
DR
AF
#i n c l u d e <QDebug>
.............
int v a l o r = 234 , v a l o r 1 = 564 ;
QString s t = QString ( ” terminada mainWindow” ) ;
QDate hoy = QDate ( 2 0 1 3 , 2 , 2 1 ) ;
QSize s z ( 1 8 , 9 ) ;
qDebug ( ) << v a l o r << ” metros y ” << v a l o r 1 << ” c e n t i m e t r o s ” ;
qDebug ( ) << s t << hoy << hoy . t o J u l i a n D a y ( ) << s z ;
Es capaz de mostrar variables definidas por Qt como el dı́a con QDate, un tamaño con
QSize, ası́ en el ejemplo mostrado se obtendrı́a una salida como:
234 metros y 564 centimetros
"terminada mainWindow" QDate("Thu Feb 21 2013") 2456345 QSize(18, 9)
1.6
Ejercicios y preguntas
1. Que es Qt?
2. Que lineas de codigo necesitas para una aplicacion minima de Qt?
3. Que es un fichero .pro?
4. Que es qmake y cuando es una buena idea su uso?
5. Que es un modulo Qt y como lo habilitas en tu proyecto?
6. Conexión entre widgets. Relacionar dos widgets ( QSpinBox y QSlider o QSpinBox
y QDial) de forma que el cambio en una se refleje inmediatamente en la otra.
Luis M. Fuentes
Tratamiento de Imagen y Sonido, UVa
23
T
DR
AF
Figure 1.10: Ejemplo de una aplicación Qt para el tratamiento de sequencias de video
Luis M. Fuentes
Tratamiento de Imagen y Sonido, UVa
24
Chapter 2
OpenCV
2.1
2.1.1
Introducción a OpenCV
Presentación
DR
AF
T
OpenCV (Open Source Computer Vision) es una librerı́a abierta que contiene más
de 500 algoritmos oprimizados para el análisis de imágenes y videos y disponible en
http://SourceForge.net/projects/opencvlibrary. Esta librerı́a está escrita en C y
C++, existiendo versiones para Windows, Linux y Mac OS X, con desarrollo activo para
otros lenguajes como Python, Ruby, Matlab etc.
Desde su creación en 1999 ha sido adoptada como la herramienta de referencia por
la comunidad de investigadores en Computer Vision1 . Desarrollado originalemente como
R
un proyecto de Intel
liderado por Gary Bradski como una iniciativa para lanzar la
investigación el algoritmos de visón artificial. Después de una serie de lanzamientos beta,
la versión 1.0.0 se lanzó en octubre de 2006. A ella le siguieron la 2.0.0 en octubre de
2009 y la 2.1.0 en abril del 2010. A partir de entonces los lanzamientos se sucedieron a
un ritmo mayor hasta llegar a la versión actual 2.4.3 lanzada en noviembre del 2012.
OpenCV fué diseñada pensando en su eficiencia computacional y con vistas a aplicaciones a tiempo real (desarrollado en un principio en paralelo con la librerı́a Intel’s
Integrated Performance Primitives -IPP- y que OpenCV utiliza de forma automática si
están disponibles). Desarrollada inicialmente en C y con posibilidad de utilizar procesadores multinucleo, la versión 2.0 introdujo un cambio sustancial al incorporar el uso de
C++. Las funciones disponibles abarcan multitud de areas dentor de Computer Vision
como inspección de productos, tratamiento de imágenes médicas, calibración de cámaras,
visión estereo, robótica e inteligencia artificial (Machine Learning Library).
1
En estas notas se usará el término inglés Computer Vision en vez de su traducción al español
Visión por ordenador o visión artificial
2.1.2
Instalación
Se pueden descargar los ficheros de instalación del link principal del proyecto en
SpurceForge o de la nueva web OpenCV.org:
• http://SourceForge.net/projects/opencvlibrary
• http://opencv.org/downloads.html
• http://opencv.willowgarage.com/
que ofrece versiones para Windows, Linux/Mac, Android e iOS, con links a las páginas
correspondientes en SourceForge. En el primer caso el archivo descargado es un ejecutable
(.exe) y en el segundo un archivo comprimido (.tar.bz2). Para obtener información
sobre OpenCV, como su instalación, se puede consultar también la tercera opción.
Windows
DR
AF
T
La instalación es extremadamente sencilla, simplemente se ejecuta el programa de
instalación que instalará los ficheros necesarios, registraá los filtros DirectShow y creará
las variables de entorno necesarias para que el compilador encuentre los archivos.
Linux
Debido a que los sistemas linux son muy variados y las versiones del compilador gcc
y las librerias básicas como glibc varı́an con la distribución, bajo linux deben compilarse
todas las librerı́as de OpenCV. Como regla general se necesitará la instalción previa de
las siguientes librerı́as y cabeceras: pkconfig, libpng, zlib, libjpeg, libtiff and libjasper.
Tambié se necesitará Python 2.6 en versión development ası́ como las librerı́as asociadas
a ffmpeg: libavcodec, libavformat and libavutil. Para compilar e instalar OpenCV, se
ejecutará desde un terminal en el directorio principal creado al descomprimir el archivo:
$>
$>
$>
$>
./ configure
make
sudo make i n s t a l l
sudo l d c o n f i g
La librerı́as se instalarán, por defecto, en /usr/local/lib/ y los ficheros cabecera en /usr/local/include/opencv/. Para que los compiladores o IDEs encuentren dichas librerı́as,
se puede especificar dicho directorio en el fichero de configuración o añadirlo en el archivo
/etc/ld.so.con y ejecutar ldconfig después. También existe la posibilidad de añadirlo al
la variable de entorno LD LIBRARY PATH.
2.1.3
Linux o Mac
También es posible construir OpenCV usando el compilador del sistema, gcc 4.3
o superior en linux y XCode 3.2 o superior en Mac OSX, y el programa CMake
(http://www.cmake.org) en versión 2.6 o superior. El sistema debe tener instalado el
siguiente software opcional en version de desarrollo (librerı́as y ficheros de cabecera):
Luis M. Fuentes
Tratamiento de Imagen y Sonido, UVa
26
1. Python en versiones 2.6.x o 2.7.x también en version de desarrollo
2. Intel TBB versión 2.2 o superior, para permitir codificación en paralelo de algunos
algoritmos (opción WITH TBB=ON en CMake).
3. Qt versión 4.6 o superior, con lo que las ventanas HighGUI utilizarán esas librerı́as
gráficas (opción WITH QT=ON en CMake).
4. CUDA la versión del toolkit más reciente, para aprovechar la GPU (solo para
NVidia GPUs).
DR
AF
T
A continuación ejecutamos el programa CMake con interfaz gráfica. Tras elegir el
directorio donde está descomprimida la versión de OpenCV a construir y el directorio
donde se generarán los binarios, se presiona la opción Configure y se enmendan los errores
que surjan o se completa la información requerida (valores en rojo). Se presiona Configure
otra vez y, cuando no se reclame correcciones u opciones se prsiona Generate, que generará
los archivos necesarios (Makefile) para que la orden make compile OpenCV para el sistema
operativo instalado.
Figure 2.1: Pantalla de Cmake tras ejecutar Configure
Si la compilación se llevó a cabo sin errores, ya hay una librerı́a OpenCV instalada
y lista para su uso en el directorio especificado en CMake. Comprobar que las variables
de entorno están definidas para que los compiladores puedan acceder a los ficheros de
cabecera y librerı́as (en Windows → Control Panel → System Utility → Advanced Tab
→ Environment variables).
Luis M. Fuentes
Tratamiento de Imagen y Sonido, UVa
27
2.2
Usando OpenCV
Desde la versión 2.2 OpenCV se divide en varios módulos (algo similar a lo que ocurre
con Qt). Estos módulos se corresponden con ficheros de librerı́as que habrá que incluir
en el proyecto:
• opencv core, el módulo básico que contiene las funcionalidades centrales, incluyendo las estructuras de datos básicas y las funciones aritméticas.
• opencv imgproc contiene las principales funciones usadas en el tratamiento de
imágenes.
• opencv highgui es el módulo que contiene las funciones de lectura y escritura
de imágenes y videos, ası́ como la creación de interfaces de usuario ofrecida por
OpenCV.
• opencv features2d contiene las funcionalidades para Feature Point Detectors y
el marco de trabajo de Feature Point Matching.
T
• opencv calib3d que contiene las funciones para la calibración de cámaras, estimación de la geometrı́a con dos vistas y las funciones de visión estereo.
DR
AF
• opencv video se utiliza en la estimación de movimiento, seguimiento y extracción
de objetos.
• opencv objdetect contiene las funciones de detcción de objetos, especialmente de
caras y personas.
Existen además otros módulos que contienen utilidades como funciones de aprendizaje
Machine Learning (opencv ml), algoritmos de geometrı́a computacional (opencv flann)
y código especializado (opencv contrib), obsoleto (opencv legacy) o diseñado para
sacar provecho de la GPU (opencv gpu).
Cada uno de estos módulos dispone del fichero de cabecera asociado y que tiene que
ser incluı́do si vamos a hacer uso de las funciones incluı́das en el módulo:
#i n c l u d e <opencv2 / c o r e / c o r e . hpp>
#i n c l u d e <opencv2 / imgproc / imgproc . hpp>
#i n c l u d e <opencv2 / h i g h g u i / h i g h g u i . hpp>
Además hay que proporcionar el acceso a la ruta de dichos includes, lo que haremos en el
fichero de proyecto de Qt añadiendo tantas como sistemas operativos en los que se pueda
compilar el proyecto, por ejemplo, Windows en el ordenador de casa y Linux en el del
laboratorio:
macx : INCLUDEPATH += / opt / l o c a l / i n c l u d e
l i n u x −g++: INCLUDEPATH += / u s r / i n c l u d e
win32 : INCLUDEPATH += C: \ OpenCV2 . 2 \ i n c l u d e \
macx : LIBS += −L” / opt / l o c a l / l i b ” −l o p e n c v c o r e
l i n u x −g++: LIBS += −l o p e n c v c o r e −l o p e n c v h i g h g u i −l o p e n c v i m g p r o c
win32 : LIBS += −LC: \ OpenCV2 . 2 \ l i b −l o p e n c v c o r e 2 2 0
Luis M. Fuentes
Tratamiento de Imagen y Sonido, UVa
28
Bibliografı́a
DR
AF
2.3
T
Figure 2.2: Vistazo modular de la bibioteca de funciones OpenCV
Se pueden encontrar textos de OpenCV,
• Learning OpenCV: Computer Vision with the OpenCV Library.
• OpenCV 2 Computer Vision Application Programming Cookbook
• Mastering OpenCV with Practical Computer Vision Projects.
Figure 2.3: Libros de OpenCV
Luis M. Fuentes
Tratamiento de Imagen y Sonido, UVa
29
Chapter 3
Imágenes : Qt + OpenCV
3.1
Introducción
T
Todos tenemos una idea de lo que constituye una imagen. El diccionario de la Real
Academia de la Lengua nos dá varias definiciones:
• Figura, representación, semejanza y apariencia de algo.
DR
AF
• Estatua, efigie o pintura de una divinidad o de un personaje sagrado.
• Reproducción de la figura de un objeto por la combinación de los rayos de luz que
proceden de él.
• Representación viva y eficaz de una intuición o visión poética por medio del lenguaje.
Todas ellas identificadas en el momento de leerlas. Ninguna de estas definiciones es válida
en el marco en el que nos encontramos. Necesitamos hablar de la imagen digital. En el
caso partucular de imagen digital monocroma, la definición es sencilla: una imagen digital
es una representación bidimensional de una imagen a partir de una matriz numérica, es
decir, es una función discreta I de N2 en N restringida a unos intervalos determinados:
I : [0, w] × [0, h] −→ [0, 2n − 1]
(x, y) −→ I(x, y)
donde w es la anchura y h la altura de la imagen y n es la profundidad de bits (normalmente n = 8). Cada punto del conjunto P = [0, w]×[0, h] se denomina Pixel mientras que
el valor I(x, y) está relacionado con la irradiancia de la zona representada por el punto
(x, y) en el mundo real.
La generalización a imagen digital en color es inmediata: en lugar de asignar a cada
pixel de la imagen un valor numérico correspondiente a su intensidad luminosa, le asociamos un conjunto de números que nos permita reproducir su color y alguna caracterı́stica
adicional. En general se utilizan tres valores numéricos que determinan su color RGB,
pudiendo añadir uno extra para informacion adicional (como el grado de transparencia o
el valor γ en un monitor), RGBA. En general para una imagen en color de tres canales,
hay tres valores numéricos (u1 ,u2 ,u3 ), normalmente de 8 bits, asociados con cada pixel
p0 (x, y) de la imagen. Estos valores caracterizan el color del pixel en el espacio de color
elegido[?].
Desde el punto de vista de la imagen digital, existen dos grupos de espacio de color,
aquellos que separan la información de luminancia o intensidad luminosa de la información
de color (Y U V, Y CB CR , Lab, ...) y los basados en las curvas de sensibilidad espectral de
los receptores dej ojo humano (RGB, XY Z, ...). La gran mayoria de las imágenes en
formato digital se almacenan en el sistema RGB, pero el tratamiento o la codificación
pueden mejorarse usando otros sistemas con separación de color como el Lab o el Y uv
respectivamente.
3.2
Imágenes en Qt
3.2.1
DR
AF
T
Las librerı́as de Qt proporcionan tres clases para trabajar con imágenes: QImage,
QPixmap and QPicture. La primera de ellas, QImage está diseñada para permitir el
acceso directo a los pı́xeles, su manipulación y sencillas operaciones de lectura/escritura
(I/O). QPixmap está diseñada y optimizada para su representación en pantalla mientras
que QPicture es un lienzo (paint device) que permite grabar y reproducir comandos
QPainter (de escritura de palabras, dibujar cı́rculos, rectángulos, lı́neas, etc.). Parece
claro que para el tratamiendo digital de imágenes nos interesa trabajas con QImage1 .
Antes de desarrolar las caracetrı́sticas especı́ficas de QImage, debemos mencionar que
hereda QPaintDevice y, por lo tanto, se puede utilizar directamente como lienzo en
cualquier comando de QPainter.
Formatos
QImage está diseñada para almacenar imágenes y puede hacerlo usando diferentes formatos, descritos por una variable enum denominada Format. A continuación se enumeran
algunas de los posibles formatos para el almacenamiento de la imagen:
• QImage::Format Invalid (0) : La imagen no es válida.
• QImage::Format Mono (1) : se usa un bit por pixel (blanco o negro), almacenando primero el bit más significativo (MSB).
• QImage::Format Indexed8 (3) : ı́ndice de 8 bits a un mapa de color.
• QImage::Format RGB32 (4) : formato RGB con 32 bits (0xffRRGGBB).
• QImage::Format ARGB32 (5) : formato ARGB usando 32 bits (0xAARRGGBB).
• QImage::Format RGB16 (7) : formato RGB de 16 bits (5-6-5).
• QImage::Format RGB888 (13) : formato RGB de 24 bits (8-8-8).
Además de almacenar la imagen, la clase QImage proporciona una colección de
métodos para la obtención de una gran variedad de información sobre la imagen, para su
manipuación o su transformación.
1
http://qt-project.org/doc/qt-5.0/qtgui/qimage.html
Luis M. Fuentes
Tratamiento de Imagen y Sonido, UVa
31
Extensión
BMP
GIF
JPG
JPEG
PNG
PBM
PGM
PPM
TIFF
XBM
XPM
Descripción
Soporte Qt
Windows Bitmap
Read/write
Graphic Interchange Format (optional) Read
Joint Photographic Experts Group
Read/write
Joint Photographic Experts Group
Read/write
Portable Network Graphics
Read/write
Portable Bitmap
Read
Portable Graymap
Read
Portable Pixmap
Read/write
Tagged Image File Format
Read/write
X11 Bitmap
Read/write
X11 Pixmap
Read/write
Lectura/Escritura
DR
AF
3.2.2
T
Table 3.1: Formatos manejados por QImage
La clase QImage proporciona varios métodos para cargar una imagen con los datos
que la representan. Se pueden extraer de un archivo en disco, bien al crear el objet0:
QImage ( const char ∗ const [ ] xpm )
QImage ( const QString & fileName , const char ∗ format = 0 )
leyendo una imagen XPM en el primer caso o un archivo imagen con extensiones varias,
tabla ??, o posteriormente leyendo de un archivo con el método load:
bool QImage : : l o a d ( QString & fileName , char ∗ format = 0 )
que intentará leer una imagen del archivo filename con el formato format, de no proveer
dicho formato, el método lee la cabecera del fichero para conocer el formato. Finalmente,
también es posible crear una QImagen con una conjunto de datos si se especifica el formato
para poder interpretarlos:
QImage
QImage
QImage
QImage
(
(
(
(
uchar
uchar
uchar
uchar
∗ data
∗ data
∗ data
∗ data
,
,
,
,
int
int
int
int
width , int h e i g h t , Format fm
width , int h e i g h t , Format fm
wid , int h e i , int b y t e s L i n e ,
wid , int h e i , int b y t e s L i n e ,
)
)
Format fm )
Format fm )
Para grabar la imagen almacenada por QImage se usa el método save(), con posibilidad
de especificar el formato (“PNG”) siendo el factor de calidad un valor entero comprendido
entre 0 (pequeña y muy comprimida) y 100 (grandes y sin comprimir) siendo −1 el valor por
defecto.
bool
s a v e ( QString &fileName , char ∗ format =0, int q u a l i t y=−1 )
Es, por lo tanto, muy sencillo leer y escribir imágenes desde QImage. Existe un añadido o
plug-in que proporciona soporte para otros tipos de formato (MNG, TGA, TIFF o WBMP).
Luis M. Fuentes
Tratamiento de Imagen y Sonido, UVa
32
3.2.3
Información sobre la imagen
La clase QImage proporciona una colección muy completa de funciones que proporcionan
una muy variada información sobre la imagen almacenada por ella:
• Geometrı́a. Podemos obtener información sobre la forma de la imagen con métodos como
size() para obtener el tamaño de la imagen en una varoable QSize o los métodos width()
y height() para obtener la anchura y la altura separadamente. También hay acceso a
datos más técnicos como el equivalente a pı́xeles por pulgada (ppi) dotsPerMeterX()
o dotsPerMeterY() mientras que rect() devuelve un objeto QRect con el rectángulo
ocupado por la imagen, o (0, 0, width(), height()).
T
• Color. El color correspondiente a un pixel de la imagen se obtiene con la función pixel()
que devuelve una variable QRgb (una definición de tipo equivalente a cuatro bytes para
almacenatr el formato 0xAARRGGBB) independientemente del formato de la imagen o
su equivalente para establecer el valor (230,128,45) del pixel en un punto dado P (a, b):
DR
AF
s e t P i x e l ( QPoint ( a , b ) , QRgb( 2 3 0 , 1 2 8 , 4 5 ) )
En caso de imágenes monocromas o de color a 8-bits, las funciones colorCount() y
colorTable()+pixelIndex() proveen de la información necesaria para interpretar el
valor almacenado. Podemos extraer información genérica sobre si hay canal alpha presente
hasAlphaChannel() o si la imagen es en balnco y negro (niveles de gris en lugar de colores,
los tres colores on el mismo valor) isGrayscale() o allGray().
• Low level. La información de la imagen a bajo nivel nos servirá para interaccionar con
otras librerı́as, como OpenCV, para inetrcambiar los datos que constituyen la imagen
bits() o scanline(). Asi la función depth() devuelve el tamaño en bits del pixel,
con valores posibles 1 para imágenes monocromas o 8, 16, 24 y 32 bits. Funciones como
bitPlaneCount(), format(), bytesPerLine() y byteCount() proporcionan información
adicional. Por último, una función curiosa: cacheKey() que devuleve un úmero que
identificará el contenido de la imagen de forma única.
3.2.4
Mostrar una imagen en Qt
La forma más sencilla de mostrar una imagen en Qt es asociarla con el pixmap de una
widget QLabel. Si tenemos ya una imagen cargada en un objeto QImage llamado img y el label
ya esta creado siendo un puntero llamado lab:
lab−>setPixmap ( QPixmap : : fromImage ( img ) ) ;
Luis M. Fuentes
Tratamiento de Imagen y Sonido, UVa
33
T
Figure 3.1: Programa de lectura de imágenes
DR
AF
Código
MainWindow : : MainWindow ( QWidget ∗ p a r e n t ) : QMainWindow ( p a r e n t )
{
QWidget ∗cw = new QWidget ( t h i s ) ;
imgL = new QLabel ( ) ;
imgL−>setPixmap ( QPixmap ( 3 8 4 , 2 8 8 ) ) ;
QPushButton ∗ l o a d I = new QPushButton ( ”Load Image ” ) ;
QPushButton ∗ s a l i r = new QPushButton ( ” S a l i r ” ) ;
QHBoxLayout ∗ h l = new QHBoxLayout ( ) ;
QVBoxLayout ∗ v l = new QVBoxLayout ( ) ;
hl−>addWidget ( l o a d I ) ;
hl−>addWidget ( s a l i r ) ;
vl −>addWidget ( imgL ) ;
vl −>addLayout ( h l ) ;
cw−>s e t L a y o u t ( v l ) ;
s e t C e n t r a l W i d g e t ( cw ) ;
c o n n e c t ( s a l i r , SIGNAL( c l i c k e d ( ) ) , this , SLOT( c l o s e ( ) ) ) ;
c o n n e c t ( l o a d I , SIGNAL( c l i c k e d ( ) ) , this , SLOT( loadImage ( ) ) ) ;
setFixedSize ( sizeHint ()) ;
}
MainWindow : : ˜ MainWindow ( ) {
}
bool MainWindow : : loadImage ( )
{
QString fname = Q F i l e D i a l o g : : getOpenFileName
( this , ” A b r i r Imagen ” , ” path / t o / Images ” , ” Images ( ∗ . png ∗ . j p g ) ” ) ;
imgL−>setPixmap ( QPixmap ( fname ) ) ;
return true ;
}
Luis M. Fuentes
Tratamiento de Imagen y Sonido, UVa
34
3.3
Imágenes en OpenCV
La evolución de OpenCV ha producido varias formas de almacenamiento de imágenes2 :
IplImage, CvMat y Mat. La primera y más antigua se corresponde con la época de desarrollo
paralelo con las Intel Performance Libraries, la segunda es la version en C y la tercera es la clase
C++. Por ser la versión más avanzada y en la que se utilzará en posteriores versiones, usaremos
la version C++. Como no especificaremos espacios de trabajo (namespaces) por defecto, la clase
debe ir asociada con su namespace, cv::Mat. Esto lo usaremos con todas las clases y funciones
de OpenCV.
La clase cv::Mat es la estructura de datos utilizada en OpenCV para almacenar imágenes.
Como su nombre indica, es la misma estructura que se utiliza con matrices de datos. The class
cv::Mat is the data structure used to hold your images (and obviously other matrix data). Por
defecto, crea una matriz de tamaño nulo, pero puede especificarse su tamaño en el constructor de
múltiples maneras. Por ejemplo, los creadores siguientes crean una imagen de tamaño 240×320)
de 1 byte de profundidad (monocroma) y llena con el valor 100:
cv : : Mat imagen ( 2 4 0 , 3 2 0 , CV 8U , cv : : S c a l a r ( 1 0 0 ) ) ;
T
donde aparece una variable CV 8U, que especifica la profundidad de la imagen (numero de bits
por pixel), si es de variable entera sin signo o con signo (U o S) o real(F) y el número de canales
(imagen en color tres o cuatro canales):
DR
AF
CV <b i t d e p t h >(U| F)C<n u m b e r o f c h a n n e l s >
es decir, para crear una imagen en color compatible con el formato Qt deberı́amos poner:
cv : : Mat imagen ( 2 4 0 , 3 2 0 , CV 8UC4 , cv : : S c a l a r ( 1 0 0 ) ) ;
OpenCV también implementa un mecanismo de borrado cuando se abandona el ámbito de
aplicación de un objeto cv::Mat. Además, esta clase implementa un mecanismo de referencias
y asigna copias huecas hasta que una de ellas se modifica (cuando una imagen se asigna a otra
con un = los datos no se copian sino que ambos objetos comparten la dirección de memoria
de los datos). Mantener la cuenta de las referencias a los mismos datos permite que sólo al
destruirse la última se destruyan los datos (se incluye imágenes devueltas por funciones que se
asignan antes de destruirse al acabr la función). Para crear una copia real (dos imágenes en
memoria) se debe usar el procedimiento copyTo():
cv : : Mat image2 , image3 ;
image2= r e s u l t ; // l a s dos usan l o s mismos d a t o s
r e s u l t . copyTo ( image3 ) ; // s e c r e a una c o p i a nueva
El almacenamiento en memoria de la imagen por OpenCV presenta una peculiaridad adicional: se almacena por tripletes ordenados BGR en vez de hacerlo según el orden convencional
RGB (sigue siendo el mismo espacio de color). OpenCV siempre ha usado este orden, que se
corresponde con el orden del espctro de luz visible en longitudes de onda, azlu la más corta y
rojo la más larga.
Finalmente indicar que, por supuesto, OpenCV también dispone de funciones para la lectura
y escritura de imágenes de disco:
cv : : Mat
bool
2
cv : : imread ( s t r i n g& fname , int f l a g s )
cv : : i m w r i t e ( s t r i n g& fname , InputArray img , v e c t o r <int>& par )
http://docs.opencv.org
Luis M. Fuentes
Tratamiento de Imagen y Sonido, UVa
35
3.4
Conversión entre formatos OpenCV y Qt
La conversión de imágenes entre formatos Qt y OpenCV tiene dos puntos delicados, el
orden de los canales de color y el canal alpha adicional. Veamos en primer lugar como pasar
de una imagen Mat a una QImage. Si la transformación es únicamente para mostrar la imagen
sólo requiere de la reordenación de los canales, para lo que se utiliza la función de OpenCV
cvtColor:
void c v t C o l o r ( InputArray s r c , OutputArray dst , int code , int dstCn=0)
DR
AF
T
una función generalista que sirve para cambiar los valores de los pı́xeles entre distintos espacios
de color (CV BGR2Lab, CM RGB2YUV, etc.). Posteriormente, sólo hay que acceder a los
datos de la imagen con imageD.data y usarlos en la construcción de un objeto QImage junto
con el numero de filas imageD.rows y el de columnas imageD.cols teniendo en cuenta que no
hay canal alpha (formato RGB de 24 bits). Finalmente se escribe en el QLabel usado para
mostrar imágenes:
cv : : c v t C o l o r ( imageO , imageD ,CV BGR2RGB) ;
QImage img = QImage ( ( const unsigned char ∗ ) ( imageD . data ) ,
imageD . c o l s , imageD . rows , QImage : : Format RGB888 ) ;
imgL−>setPixmap ( QPixmap : : fromImage ( img ) ) ;
O, de un modo alternativo, podemos poner también, aprovechando para cambiar el formato a
ARGB (32 bits) y usando la función de QImage para rotar canales:
Código
QImage
i 1 , i 2 , imgF ;
cv : : Mat
frm ;
const uchar ∗ qIB ;
\\ Se o b t i e n e u o p e r a con l a imagen en OpenCV frm
qIB = frm . p t r ( ) ;
i 1 = QImage ( qIB , frm . c o l s , frm . rows , frm . s t e p , QImage : : Format RGB888 ) ;
i 2 = i 1 . convertToFormat ( QImage : : Format ARGB32 , Qt : : AutoColor ) ;
imgF = i 2 . rgbSwapped ( )
;
La conversión opuesta, es un poco más complicada. Implica la creacion de un objeto Mat
de cuatro canales para almacenar los datos de la imagen, ası́ como otros dos objetos Mat,
uno con tres canales, para la imagen, y otro de uno para almacenar los datos del canal alpha.
Aprovechando el método mixChannels, una funcón que permite barajar y separar canales dentro
de una imagen, para cambiar el orden de los canales y separar el canal alpha.
Luis M. Fuentes
Tratamiento de Imagen y Sonido, UVa
36
Código
DR
AF
T
cv : : Mat matF ;
QImage
imgO ;
uchar ∗ p t r ;
int
a l t o , ancho , b p l ;
\\ Se o b t i e n e u o p e r a con l a imagen en Qt imgO
a l t o = imgO . h e i g h t ( ) ;
ancho = imgO . width ( ) ;
p t r = ( uchar ∗ ) b i t s ( ) ;
bpl = bytesPerLine ( ) ;
cv : : Mat mat = cv : : Mat ( a l t o , ancho , CV 8UC4 , ptr , b p l ) ;
cv : : Mat r g b I = cv : : Mat ( mat . rows , mat . c o l s , CV 8UC3 ) ;
cv : : Mat a l p h = cv : : Mat ( mat . rows , mat . c o l s , CV 8UC1 ) ;
cv : : Mat o c v I [ ] = { r g b I , a l p h } ;
int fromTo [ ] = { 0 , 2 , 1 , 1 , 2 , 0 , 3 , 3 } ;
cv : : mixChannels ( &mat , 1 , ocvI , 2 , fromTo , 4 ) ;
matI = r g b I ;
Luis M. Fuentes
Tratamiento de Imagen y Sonido, UVa
37
Descargar