#Arreglos - Pantalla Verde ![main1.png](images/main1.png) ![main2.png](images/main2.png) ![main3.png](images/main3.png) [versión 2016-4-18] Los arreglos de datos (*arrays*) nos facilitan guardar y trabajar con grupos de datos del mismo tipo. Los datos se guardan en espacios de memoria consecutivos a los que se puede acceder utilizando el nombre del arreglo e índices o suscritos que indican la posición en que se encuentra el dato. Las estructuras de repetición nos proveen una manera simple de acceder a los datos de un arreglo. En la experiencia de laboratorio de hoy practicarás el uso de ciclos anidados en la manipulación de arreglos bi-dimensionales usando técnicas de "pantalla verde". ##Objetivos: 1. Practicar el acceso y manipulación de datos en un arreglo. 2. Aplicar ciclos anidados para implementar técnicas de "pantalla verde". 3. Utilizar expresiones aritméticas y estructuras de selección para transformar colores de píxeles. 4. Acceder píxeles en una imagen y descomponerlos en sus componentes rojo, azul y verde. ##Pre-Lab: Antes de llegar al laboratorio debes: 1. Haber repasado los conceptos básicos relacionados a estructuras de repetición, ciclos anidados y arreglos bi-dimensionales. 2. Conocer los métodos básicos de `QImage` para manipular los pixeles de las imágenes. 3. Haber estudiado los conceptos e instrucciones para la sesión de laboratorio. 4. Visitar la siguiente página de Facebook para ver cómo se ha usado la tecnología de pantalla verde en algunas películas de Hollywood: https://www.facebook.com/video.php?v=920387528037801 --- --- ##Tecnología de pantalla verde ("Green Screen") En esta experiencia de laboratorio, aprenderás los conceptos y destrezas básicas de la tecnología de pantalla verde que se usa en boletines informativos de televisión, películas, juegos de video y otros. La composición de pantalla verde, o composición cromática, es una técnica que se usa para combinar dos imágenes o cuadros de video [1]. Esta técnica de post-producción crea efectos especiales al componer dos imágenes o transmisiones de video sustituyendo el área de un color sólido por otra imágen [2]. La composición cromática se puede hacer con imágenes de objetos sobre fondos de cualquier color que sean uniformes y diferentes a los de la imagen. Los fondos azules y verdes son los que se usan con más frecuencia porque se distinguen con más facilidad de los tonos de la mayoría de los colores de piel humanos. Para esta experiencia de laboratorio te proveemos un interfaz gráfico (GUI) simple que permite al usuario cargar una imagen con un objeto sobre un fondo de color sólido (preferiblemente azul o verde) y una imagen para sustituir el fondo. Tu tarea es crear e implementar una función que cree una tercera imagen compuesta en la cual, a la imagen del objeto con el fondo de color sólido se le removerá el color de fondo y el objeto aparecerá sobre la imagen que será el nuevo fondo. La Figura 1 muestra un ejemplo de los resultados esperados. --- ![figure1.png](images/figure1.png) **Figura 1.** Ejemplo de los resultados esperados. El objeto de interés es la mano con las gafas. --- Con el propósito de ilustrar el procedimiento, llamemos la imagen del objeto con el fondo de color sólido *imagen A*, y supongamos que el color sólido en el fondo tiene un "RGB" `0x00ff00` (verde puro). Llamemos *imagen B* a la imagen que usaremos para el fondo, un fondo que resulte interesante. Para este ejemplo, supongamos también que los tamaños de ambas imágenes son iguales (mismo ancho y alto). Para producir la imagen compuesta (*imagen C*), podríamos comenzar copiando toda la *imagen B* que usaremos de fondo a la *imagen C*. Luego, para insertar solo el objeto que nos interesa en la imagen compuesta podemos recorrer la *imagen A* píxel por píxel. Compararíamos el color de cada píxel *p* en la *imagen A* con el color de fondo `0x00ff00`. Si son *similares*, el píxel de la *imagen A* corresponde al color sólido de fondo y dejamos el píxel de la *imagen C* como está (el fondo nuevo). Si el color de *p* no es *similar* a `0x00ff00`, modificamos el píxel correspondiente en la *imagen C*, copiando el color del píxel del objeto a la imagen compuesta. Esto se ilustra en la Figura 2. --- ![figure2.png](images/figure2.png) **Figura 2.** Ilustración de cómo el algoritmo decide cuáles píxeles de la *imagen A* incluir en la *imagen C*. --- --- ##Píxeles Al elemento más pequeño de una imagen se le llama un *píxel*. Esta unidad consiste de un solo color. Como cada color es una combinación de tonalidades de los colores primarios rojo, verde y azul, se codifica como un entero sin signo cuyos bytes representan los tonos de rojo, verde y azul del pixel (Figura 3). A esta combinación se le llama el *RGB* del color por las siglas de "Red-Green-Blue". Por ejemplo un píxel de color rojo (puro) tiene una representación RGB `0x00ff0000`, mientras que un píxel de color blanco tiene una representación RGB de `0x00FFFFFF` (ya que el color blanco es la combinación de los tonos rojo, verde y azul en toda su intensidad). --- ![figure3.png](images/figure3.png) **Figura 3.** Distribución de bits para las tonalidades de rojo, verde y azul dentro de la representación RGB. Cada tonalidad puede tener valores entre 0x00 (los ocho bits en 0) y 0xFF (los 8 bits en 1). --- En `Qt` se utiliza el tipo `QRgb` para representar valores `RGB`. Utilizando ciertas funciones que describimos abajo podemos obtener los componentes rojo, verde y azul del valor `QRgb` del píxel y así manipular imágenes. ###Biblioteca La experiencia de laboratorio de hoy utilizará la clase `QImage`. Esta clase permite acceder a los datos de los pixeles de una imagen para poder manipularla. La documentación de la clase `QImage` se encuentra en http://doc.qt.io/qt-4.8/qimage.html. El código que te proveemos contiene los siguiente objetos de la clase `QImage`: * `originalImage` // contiene la información de la imagen original que vas a editar * `editedImage` // contendrá la imagen editada Los objetos de clase `QImage` tienen los siguiente métodos que serán útiles para la experiencia de laboratorio de hoy: * `width()` // devuelve el valor entero del ancho de la imagen * `height()` // devuelve el valor entero de la altura de la imagen * `pixel(i, j)` // devuelve el `QRgb` del píxel en la posición `(i,j)` * `setPixel(i,j, pixel)` // modifica el valor del píxel en la posición `(i, j)` al valor píxel `QRgb` Las siguientes funciones son útiles para trabajar con datos de tipo `QRgb`: * `qRed(pixel)` // devuelve el tono del color rojo del píxel * `qGreen(pixel)` // devuelve el tono del color verde del píxel * `qBlue(pixel)` // devuelve el tono del color azul del píxel * `qRgb(int red, int green, int blue)` // devuelve un píxel `QRgb` compuesto de los valores de rojo, verde y azul recibidos. ####Ejemplos: 1. `QRgb myRgb = qRgb(0xff, 0x00, 0xff);`: Asigna a `myRgb` el valor `0xff00ff` que representa el color ![figure4.png](images/figure4.png) Nota que el valor `0xff00ff` representa los valores `0xff`, `0x0`, `0xff`, que corresponden a los componentes rojo, verde y azul de `myRgb`. 2. Si la siguiente imagen `4 x 4` de píxeles representa el objeto `originalImage`, ![ejemplo.png](images/ejemplo.png) entonces `originalImage.pixel(2,1)` devuelve un valor `rgb` que representa el color azul (`0x0000ff`). 3. La siguiente instrucción asigna el color rojo al píxel en posición `(2,3)` en la imagen editada: `editedImage.setPixel(2,3,qRgb(0xff,0x00,0x00));`. 4. La siguiente instrucción le asigna a `greenContent` el valor del tono de verde que contiene el pixel `(1,1)` de `originalImage`: `int greenContent = qGreen(originalImage.pixel(1,1));`. 5. El siguiente programa crea un objeto de clase `QImage` e imprime los componentes rojo, verde y azul del pixel en el centro de la imagen. La imagen utilizada es la que se especifica dentro del paréntesis durante la creación del objeto, esto es, el archivo `chuck.png`. --- ```cpp #include #include using namespace std; int main() { QImage myImage(“/Users/rarce/Downloads/chuck.png”); QRgb centralPixel; centralPixel = myImage.pixel(myImage.width() / 2, myImage.height() / 2); cout << hex; cout << “Los componentes rojo, verde y azul del pixel central son: “ << qRed(centralPixel) << “, “ << qGreen(centralPixel) << “, “ << qBlue(centralPixel) << endl; return 0; } ``` --- ### Midiendo la similaridad de los colores de los pixeles Observa la Figura 4 abajo. Aunque el fondo en la *imagen A* parece uniforme, realmente incluye píxeles de diferentes colores (aunque parecidos). --- ![figure5.png](images/figure5.png) **Figura 4.** Lo que puede parecer un color *sólido*, realmente no lo es. --- Por esto, en lugar de solo considerar como parte del fondo sólido los píxeles cuyo color es **exactamente** `0x00FF00`, medimos la *distancia* del valor del color del píxel al valor del color *puro*. Una distancia pequeña significa que el color es *casi* verde puro. La ecuación para la *distancia* es: $$distancia = \sqrt{(P_R-S_R)^2+(P_G-S_G)^2+(P_B-S_B)^2},$$ donde $$P_R, P_G, P_B$$ son los valores de los componentes rojo, verde y azul del píxel bajo consideración, y $$S_R, S_G, S_B$$ son los valores de los componentes rojo, verde y azul del fondo sólido. En nuestro ejemplo, $$S_R=S_B=0$$ y $$S_G=255$$. --- --- !INCLUDE "../../eip-diagnostic/green-screen/es/diag-green-screen-01.html" !INCLUDE "../../eip-diagnostic/green-screen/es/diag-green-screen-02.html" !INCLUDE "../../eip-diagnostic/green-screen/es/diag-green-screen-03.html" !INCLUDE "../../eip-diagnostic/green-screen/es/diag-green-screen-04.html" !INCLUDE "../../eip-diagnostic/green-screen/es/diag-green-screen-05.html" --- --- ##Sesión de laboratorio: En el laboratorio de hoy, comenzando con una imagen con un objeto de interés sobre un fondo de color sólido y una imagen para utilizar como fondo, definirás e implantarás una función que cree una tercera imagen compuesta en la cual, a la imagen del objeto de interés se le removerá el color de fondo y aparecerá sobre la imagen para el fondo. Estarás trabajando con el archivo `Filter.cpp`. Lo que sigue es un resumen de las variables en este archivo. * `objectImage`: referencia a la imagen del objeto de interés y fondo sólido * `backgroundImage`: referencia a la imagen para el fondo * `mergedImage`: referencia a la imagen compuesta * `threshold`: valor umbral usado para comparar las distancias entre el valor del color del píxel de la imagen con el objeto sobre fondo sólido. En el código que se provee, el valor del umbral se lee del valor de la barra deslizable. * `ghost`: valor Booleano utilizado para aplicar el filtro "fantasma" a los pixeles. * `(x, y)`: coordenadas de un pixel de la imagen del objeto sobre fondo sólido. El valor por defecto es `(0,0)`. * `(offset_x, offset_y)`: coordenadas de la imagen compuesta en donde la esquina superior izquierda de la imagen del objeto sobre fondo sólido será insertada. El valor por defecto es `(0,0)`. ###Ejercicio 1: Crear imagen compuesta ####Instrucciones 1. Descarga la carpeta `Arrays-GreenScreen` de `Bitbucket` usando un terminal, moviéndote al directorio `Documents/eip`, y escribiendo el comando `git clone http://bitbucket.org/eip-uprrp/arrays-greenscreen`. 2. Carga a Qt creator el proyecto `GreenScreenLab` haciendo doble "click" en el archivo `GreenScreenLab.pro` que se encuentra en la carpeta `Documents/eip/Arrays-GreenScreen` de tu computadora. 3. Configura el proyecto y corre el programa. El código que te proveemos crea la interfaz de la Figura 5. Los botones *Select Image* y *Select Background Image* ya han sido programados. --- ![figure6.png](images/figure6.png) **Figura 5.** Interfaz de la aplicación GreenScreen. --- 4. Marca el botón para cargar una imagen del objeto de interés sobre fondo sólido, luego marca el botón para seleccionar la imagen para el fondo. El directorio con los archivos fuente contiene una carpeta llamada `landscapes` que contiene imágenes de fondo, y una carpeta llamada `green_background` que contiene imágenes de objetos sobre fondo de color sólido. 5. Tu primera tarea es completar la función `MergeImages` en el archivo `Filter.cpp`. La función `MergeImages` se invoca cuando el usuario marca el botón `Merge Images` y cuando se desliza la barra. La función `MergeImages` recibe las referencias a la imagen con objeto de interés y fondo sólido, la imagen para el fondo y la imagen compuesta, un valor umbral, las coordenadas `(x,y)` de un píxel de la imagen del objeto sobre fondo sólido, y las coordenadas `(offset_x, offset_y)` de la imagen compuesta. Para este ejercicio puedes ignorar el filtro "fantasma" `ghost` y las coordenadas `(offset_x, offset_y)`, y solo componer la imagen con el objeto de interés en la imagen de fondo, comenzando en la posición `(0,0)`. **Algoritmo** 1. Adquiere el valor del color sólido. El color sólido será el color del píxel en la posición `(x,y)` en la imagen del objeto sobre fondo sólido. El valor por defecto para `(x,y)` es `(0,0)`. 2. Para todas las posiciones `(i,j)`, adquiere el valor del color del píxel en la posición `(i,j)` de la imagen con el objeto. Computa la distancia entre el color de la imagen con el objeto y el valor del color sólido. Si la distancia entre el color sólido y el color del píxel de la imagen es mayor que que el valor umbral, cambia el valor del color del píxel en la posición `(i,j)` de la imagen de fondo al valor del color de la imagen con el objeto. Prueba tu implantación cargando imágenes de objetos e imágenes para el fondo y verificando la imagen compuesta. ###Ejercicio 2: Crear imagen compuesta usando filtro `ghost` En este ejercicio modificarás el Ejercicio 1 para aplicar el filtro fantasma a cada uno de los píxeles que se compondrán sobre la imagen de fondo en el caso de que la variable `ghost` sea cierta. El filtro fantasma creará el efecto de que el objeto en la imagen compuesta se verá como un "fantasma" sobre la imagen de fondo, como en la Figura 6. --- ![figure7.png](images/figure7.png) **Figura 6.** Imagen con filtro fantasma. En este ejemplo, el perro en la imagen con el fondo sólido se compone sobre la imagen de fondo utilizando el filtro fantasma. --- El efecto fantasma se consigue promediando el valor del color del píxel del fondo con el valor del color del píxel correspondiente del objeto, en lugar de solo reemplazar el valor del color del píxel del fondo por el del objeto. Calculamos el promedio de cada uno de los componentes (rojo, verde y azul) $$N_R=\frac{S_R+B_R}{2}$$ $$N_G=\frac{S_G+B_G}{2}$$ $$N_B=\frac{S_B+B_B}{2},$$ en donde $$N_R, N_G, N_B$$ son los componentes rojo, verde y azul del nuevo píxel fantasma, $$S_R, S_G, S_B$$ son los componentes de la imagen del objeto, y $$B_R, B_G, B_B$$ son los componentes de la imagen de fondo. ###Ejercicio 3: Crear imagen compuesta colocando el objeto en una posición específica El "widget" que despliega el fondo fue programado para que detecte la posición marcada por el usuario. En este ejercicio programarás la función `MergeImages` para que el objeto sea desplegado en la posición marcada por el usuario en la imagen de fondo, en lugar de ser desplegado en la esquina superior izquierda. Las Figuras 7 y 8 muestran el efecto. Nota los valores de `Selected Coord` bajo la imagen del medio. --- ![figure8.png](images/figure8.png) **Figura 7.** En este ejemplo, la imagen del fondo no ha sido marcada y `Selected Coord` tiene `(0,0)` que es su valor por defecto. El perro se inserta en la imagen compuesta con su esquina superior izquierda en el lugar `(0,0)`. --- ![figure9.png](images/figure9.png) **Figura 8.** En este ejemplo, la imagen del fondo fue marcada en las coordenadas `(827,593)`. La imagen del perro se inserta en la imagen compuesta con su esquina superior izquierda en la posición `(827,593)`. --- Tu tarea en este ejercicio es la misma que en el Ejercicio 1, pero esta vez debes ajustar la imagen del objeto dentro de la composición con las cantidades especificadas en los parámetros `x_offset` y `y_offset`. Recuerda tomar en consideración los límites de la imagen compuesta cuando insertes el objeto; el usuario pudiera especificar unos parámetros que se salgan de los límites y el objeto se cortará, como sucede en la Figura 9. --- ![figure10.png](images/figure10.png) **Figura 9.** En este ejemplo, el usuario seleccionó una posición que asignó valores muy grandes para `x_offset` y `y_offset`; la implementación hizo el ajuste para que parte de la imagen del perro saliera en la imagen compuesta. --- El ejemplo de la Figura 10 muestra cómo se comportará la imagen del objeto al sobreponerla en la imagen que queremos de fondo. Las variables `offset_x, offset_y` representan el punto en la imagen de fondo en el que se colocará la esquina superior izquierda de la imagen del objeto. Nota que si se escoge un punto muy cerca del borde para la composición, parte de la imagen del objeto se sale de los límites de la imagen de fondo. Como hemos visto en la manipulación de arreglos, si se intenta acceder o alterar elementos que estan fuera del rango de tamaño del arreglo, al compilar occurre un error fatal. Lo mismo sucede con las imágenes. Debes asegurarte de que tu implementación toma en cuenta los valores de `offset x` y `offset y` para que la composición no intente acceder o alterar píxeles fuera del límite de la imagen de fondo. Si intentas acceder o alterar píxeles fuera de esos límites, resulta en un error fatal. --- ![figure11.png](images/figure11.png) **Figura 10.** Ilustración de la imagen del objeto de interés con píxeles que se salen de los límites de la imagen de fondo. Si no se toma en consideración esta posibilidad en la implementación, ocurrirá un error fatal. --- Valida tu implantación seleccionando varios valores para el ajuste de "offset" y observando el efecto que tienen en la imagen compuesta. Asegúrate de tratar casos en los que tus valores para `x_offset` y `y_offset` ocasionarían que la imagen fuera cortada como ocurrió en la imagen compuesta de la Figura 9. --- --- ##Entrega Utiliza "Entrega" en Moodle para entregar el archivo `Filter.cpp` que contiene la función `MergeImages`. Recuerda utilizar buenas prácticas de programación, incluir el nombre de los programadores y documentar tu programa. --- --- ## Referencias [1] http://en.wikipedia.org/wiki/Green_screen_(disambiguation) [2] http://en.wikipedia.org/wiki/Chroma_key [3] http://doc.qt.io/qt-4.8/qimage.html. --- --- ---