Aucune description

README-es.md 14KB

Arreglos - Editor de Imagenes Simple

main1.png main2.png main3.png

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 diseñarás e implementarás algoritmos simples de procesamiento de imágenes para practicar el uso de ciclos anidados en la manipulación de arreglos bi-dimensionales.

Objetivos:

  1. Practicar el acceder y manipular datos en un arreglo.

  2. Aplicar ciclos anidados para implementar algoritmos simples de procesamiento de imágenes.

  3. Utilizar expresiones aritméticas para transformar colores de pixeles.

  4. Acceder pixeles en una imagen y descomponerlos en sus componentes rojo, azul y verde.

Pre-Lab:

Antes de llegar al laboratorio debes:

  1. Conseguir y tener disponible uno o más archivos con una imagen a color en alguno de los siguientes formatos: tiff, jpg, png.

  2. Haber repasado los conceptos básicos relacionados a estructuras de repetición y ciclos anidados.

  3. Conocer las funciones básicas de QImage para manipular los pixeles de las imágenes.

  4. Haber estudiado los conceptos e instrucciones para la sesión de laboratorio.

  5. Haber tomado el quiz Pre-Lab que se encuentra en Moodle.



Edición de imágenes

En esta experiencia de laboratorio, trabajarás con varios conceptos y destrezas básicas de edición de imágenes. Te proveemos un interface gráfico (GUI) simple que le permite al usuario cargar una imagen e invertirla vertical y horizontalmente. Tu tarea es crear e implementar una función para convertir una imagen a color a una imagen con tonos de gris, y otra función que convierta una imagen a color a una imagen en blanco y negro.

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 1). 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).


figure1.png

Figura 1. 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 figure2.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

    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.


#include <QImage>
#include <iostream>

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;
}


!INCLUDE “../../eip-diagnostic/simple-image-editor/es/diag-simple-image-editor-01.html”

!INCLUDE “../../eip-diagnostic/simple-image-editor/es/diag-simple-image-editor-02.html”



Sesión de laboratorio:

En la experiencia de laboratorio de hoy diseñarás e implementarás algoritmos simples de procesamiento de imágenes para practicar el uso de ciclos anidados en la manipulación de arreglos bi-dimensionales.

Ejercicio 1 - Entender el código provisto

Instrucciones:

  1. Carga a QtCreator el proyecto SimpleImageEditor. Hay dos maneras de hacer esto:

    • Utilizando la máquina virtual: Haz doble “click” en el archivo SimpleImageEditor.pro que se encuentra en el directorio /home/eip/labs/arrays-simpleimageeditor de la máquina virtual.
    • Descargando la carpeta del proyecto de Bitbucket: Utiliza un terminal y escribe el commando git clone http:/bitbucket.org/eip-uprrp/arrays-simpleimageeditor para descargar la carpeta arrays-simpleimageeditor de Bitbucket. En esa carpeta, haz doble “click” en el archivo SimpleImageEditor.pro.
  2. El código que te proveemos crea la interface de la Figura 2.


    figure3.png

    Figura 2. Interface del editor de imágenes.


  3. Estarás trabajando con el archivo filter.cpp. Estudia la función HorizontalFlip del archivo filter.cpp para que entiendas su operación.

    En los ejercicios siguientes estarás usando mayormente los objetos originalImage y editedImage de clase QImage. ¿Cuál crees que es el propósito la variable pixel?

  4. El código que te proveemos ya tiene programado el funcionamiento de los botones de la interface gráfica. NO tienes que cambiar nada en este código pero te incluimos las siguientes explicaciones para que conozcas un poco del funcionamiento de los botones. En el archivo mainwindow.cpp, las etiquetas lblOriginalImage y lblEditedImage corresponden a las partes de la interface que identifican la imagen original y la imagen procesada. Los botones

    • btnLoadImage
    • btnSaveImage
    • btnInvertThreshold
    • btnFlipImageHorizontally
    • btnFlipImageVertically
    • btnGreyScaleFilter
    • btnRevertImage

están conectados a funciones de modo que cuando se presione el botón de la interface se haga alguna tarea. Por ejemplo, cuando se presiona LoadImage, saldrá una ventana para seleccionar el archivo con la imagen para editar, al seleccionar el archivo, se lee y se asigna la imagen al objeto originalImage. El deslizador thresholdSlider puede asumir valores entre 0 y 255.

  1. Compila y corre el programa. Prueba los botones Load New Image y Flip Image Horizontally con las imágenes que trajiste para que valides las operaciones de los botones.

Ejercicio 2: Convertir una imagen a colores a una imagen en tonos de gris

El “image grayscale” es una operación que se usa para convertir una imagen a color a una imagen que solo tenga tonalidades de gris. Para hacer esta conversión se usa la siguiente fórmula en cada uno de los píxeles:

gray = (red * 11 + green * 16 + blue * 5)/32 ;

donde red, green y blue son los valores para los tonos de los colores rojo, verde y azul en el píxel de la imagen original a color, y gray será el color asignado a los colores rojo, verde y azul en el píxel de la imagen editada. Esto es,

editedImage.setPixel( i, j, qRgb(gray, gray, gray) ).

Instrucciones

  1. Utilizando pseudocódigo, expresa el algoritmo para convertir una imagen a color a una imagen solo con tonalidades de gris. El apéndice de este documento contiene algunos consejos sobre buenas prácticas al hacer pseudocódigos.

  2. Completa la función GreyScale en el archivo filter.cpp para implementar el algoritmo de tonalidades de gris. La función debe producir un resultado similar al de la Figura 3, en donde la imagen de la izquierda es la imagen original y la de la derecha es la imagen editada.


    chuck-color.png chuck-gris.png

    Figura 3. Imagen original e imagen luego de aplicar la función GreyScale.


Ejercicio 3: Convertir una imagen a colores a una imagen en blanco y negro (“Thresholding”)

“Thresholding” es una operación que se puede utilizar para convertir una imagen a color a una imagen en blanco y negro. Para hacer esta conversión debemos decidir cuáles colores de la imagen original van a convertirse en píxeles blancos y cuáles serán negros. Una manera sencilla de decidir esto es computando el promedio de los componentes rojo, verde y azul de cada píxel. Si el promedio es menor que el valor umbral (“threshold”), entonces cambiamos el píxel a negro; de lo contrario se cambia a blanco.

Instrucciones

  1. Utilizando pseudocódigo, expresa el algoritmo para “thresholding”. Presume que utilizarás el valor del deslizador como umbral.

  2. En el programa, si la caja chkboxThreshold está marcada, se hace una invocación a la función applyThresholdFilter. La función applyThresholdFilter también es invocada cada vez que se cambia el valor del deslizador.

  3. Completa la función ThresholdFilter de modo que implemente el algoritmo de “threshold” en la imagen a color utilizando el valor del deslizador como umbral. Si se implementa correctamente, la imagen de la derecha debe ser la imagen original pero en blanco y negro. El valor umbral es un parámetro de la función ThresholdFilter. El código provisto en mainwindow.h tiene definidas las constantes BLACK y WHITE con el valor hexadecimal de los colores negro y blanco respectivamente; puedes aprovechar esto y utilizarlas en tu código.

  4. El parámetro booleano invertColor tendrá el valor true si la opción de invertir los colores fue seleccionada. Escribe código de modo que los colores blanco y negro se inviertan en la imagen si invertColor asume el valor true.

  5. Prueba tu programa con distintas imágenes y distintos valores de umbral.


    chuck-color.png chuck-threshold.png

    Figura 4. Imagen original e imagen luego de aplicar la función ThresholdFilter.




Entrega

Utiliza “Entrega” en Moodle para entregar el archivo filter.cpp que contiene las funciones GreyScale y Threshold. Recuerda utilizar buenas prácticas de programación, incluir el nombre de los programadores y documentar tu programa.



Apéndice: Buenas prácticas al escribir pseudocódigos

  1. Provee una descripción de los datos de entrada y salida
  2. Enumera los pasos
  3. Usa estructuras de repetición y decisión comunes: if, else, for, while
  4. Indenta los bloques de pasos que están dentro de una estructura de repetición o decisión, “Python-style”
  5. No necesitas declarar los tipos de las variables pero si debes inicializarlas. Esto es especialmente importante para contadores y acumuladores
  6. Recuerda que el propósito de un pseudocódigo es que un humano lo pueda entender.

Ejemplo:

Input: n, a positive integer
Output: true if n is prime, false otherwise
---------------------------------------------------------
1. for i = 3 to n / 2
2.   if n % i == 0:
3.      return false
4. return true


Referencias

[1] http://www.willamette.edu/~gorr/classes/GeneralGraphics/imageFormats/24bits.gif

[2] http://doc.qt.io/qt-4.8/qimage.html.