Modificar imágenes con C++, desplazamientos de bits y negativos
Siguendo con las prácticas de la asignatura MP (Metodología de la programación), UGR Otra práctica interesante el enunciado del
problema:
Los objetivos de este guión de prácticas son los siguientes:
1. Practicar con un problema donde es necesaria la modularización. Para desarrollar los programas de esta práctica, el alumno debe crear distintos archivos, compilarlos, y enlazarlos para obtener los ejecutables.
2. Practicar con el uso de la memoria dinámica. El alumno deberá usar estructuras de datos que se alojan en memoria dinámica.
3. Introducirse en los conceptos relacionados con la abstracción de tipos de datos alojados en memoria dinámica, como una motivación e introducción previa al estudio de las clases en C++.
Los requisitos para poder realizar esta práctica son:
1. Saber manejar punteros y memoria dinámica.
2. Conocer el diseño de programas en módulos independientes, así como la compilación separada, incluyendo la creación de bibliotecas y de archivos makefile.
Encapsulamiento
En esta práctica, vamos a encapsular la representación de una imagen, es decir, vamos a crear un módulo para manejar un nuevo tipo “Imagen”. En este módulo encapsulamos la representación con una interfaz, de forma que los programas que lo usen sean independientesde los detalles internos de la representación, ya que sólo necesitarán conocer dicha interfaz.
La siguiente figura muestra gráficamente esta idea:
En esta práctica vamos a proponer el desarrollo de varios programas que usan el módulo imagen. Estos programas serán válidos independientemente de la representación interna que
seleccionemos. Para mostrar su validez, se realizarán cambios en la representación interna,obteniendo nuevos ejecutables que, usando otra representación, obtienen los mismos resultados.
Para poder desarrollar el módulo Imagen será necesario escoger una representación interna para el tipo Imagen. Tenga en cuenta que esta representación no será relevante para desarrollar los programas negativo o desplazar, ya que éstos usan la interfaz del módulo.
El alumno debe realizar 4 versiones del tipo imagen, cada una de ellas con una representación distinta, aunque todas con la misma interfaz. Las representaciones son las siguientes:
1. Representación en base a dos enteros (filas y columnas) más un vector -en memoria dinámica- con todos los elementos de la imagen consecutivos por filas. En la siguiente figura se muestra un ejemplo de imagen de 3 filas y 4 columnas:
2. Representación en base a dos enteros (filas y columnas) más un vector -en memoria dinámica- de punteros a las filas. Cada puntero apuntará a un vector, también en memoria dinámica, que contiene los elementos de la fila correspondiente. En la siguiente figura se muestra un ejemplo de 3 filas por 4 columnas:
3. Representación en base a dos enteros (filas y columnas) más un vector -en memoria dinámica- de punteros a las filas. El primer puntero apunta a un vector con todos los elementos de la imagen consecutivos por filas. El resto de punteros apunta al comienzo
de cada fila. La siguiente figura muestra un ejemplo de 3 filas y 4 columnas:
4. Representación en base a 1 entero (las columnas) más un puntero a una lista de celdas enlazadas (en memoria dinámica) desde las que cuelgan los vectores fila, también en memoria dinámica. La cantidad de filas se obtendrá contando el número de celdas enlazadas.
Como se puede ver, unas son mejores que otras, ya sea porque ocupan menos espacio, o porque las operaciones del tipo tendrán mejores tiempos de ejecución.
Comprobando la abstracción
Si realizamos la abstracción correctamente, es decir, implementamos el módulo imagen correctamente, ofreciendo una interfaz independiente de la representación, y el resto demódulos se desarrollan en base a ella, podremos cambiar de representación sin que afecte a nuestros programas.
Obviamente, el cambio de representación deriva en un nuevo ejecutable que puede ser más o menos eficiente, pero que tiene la misma funcionalidad. En la siguiente figura mostramos que
podemos obtener 4 ejecutables distintos, que hacen lo mismo, pero que usan las distintas representaciones propuestas.
Problemas a resolver
En esta práctica vamos a desarrollar dos aplicaciones sobre imágenes para resolver dos problemas independientes:
En esta práctica vamos a desarrollar dos aplicaciones sobre imágenes para resolver dos problemas independientes:
1. Negativo de una imagen.
2. Desplazamiento de bits.
Una
vez hecha la práctica, el resultado de la imagen en negativo es el
mismo que el de la práctica anterior.
./negativo ../imagenes/hombro.pgm negativo.pgm
hombro.pgm |
negativo.pgm |
ejecutamos el programa que desplazar un número determinado de bits los píxeles de la imágen, a continuación os dejo algunas salidas de ir desplazando los bits de la imagen primero 1 bit, luego 4 y por último 7:
./desplazar 1 ../imagenes/lenna.pgm transformar_1.pgm
./desplazar 4 ../imagenes/lenna.pgm transformar_4.pgm
./desplazar 7 ../imagenes/lenna.pgm transformar_7.pgm Si en la anterior práctica se ocultaban mensajes dentro de la imagen, ahora se ocultan imágenes dentro de ella.
Código 1: Primera representación
# include <iostream>
#include "imagenES.h"
#include "imagen1.h"
using namespace std;
void Crear (Imagen& img, int f, int c)
{ // Reserva recursos
img.filas=f;
img.columnas=c;
unsigned char *dat = new unsigned char [f*c];
img.buffer=dat;
}
int Filas (const Imagen& img)// Devuelve el número filas de m
{
return img.filas;
}
int Columnas (const Imagen& img)// Devuelve el número columnas de m
{
return img.columnas;
}
void Set (Imagen& img, int i, int j, unsigned char v)// Hace img(i,j)=v
{
int n = j+i*Columnas(img);
*(img.buffer+n)= v;
}
unsigned char Get (const Imagen& img, int i, int j)// Devuelve img(i,j)
{
int n = j+i*Columnas(img);
return *(img.buffer+n);
}
void Destruir (Imagen& img)// Libera recursos de m
{
delete []img.buffer;
}
bool LeerImagen(const char file[], Imagen& img) // Carga imagen file en img
{
bool exito=false;
int f=0,c=0;
TipoImagen n = LeerTipoImagen(file,f,c);
if(n==IMG_PGM)
{
unsigned char *buffer= new unsigned char [f*c];
if(LeerImagenPGM (file,f,c,buffer))
{
Crear(img,f,c);
unsigned char cad;
for(int i=0;i<Filas(img);i++)
for(int j=0;j<Columnas(img);j++)
{
cad=buffer[j+i*Columnas(img)];
Set(img,i,j,cad);
exito=true;
}
delete[] buffer;
}
}
return exito;
}
bool EscribirImagen(const char file[], const Imagen& img) //Salva img en file
{
int f=Filas(img);
int c=Columnas(img);
bool exito=false;
unsigned char *buffer;
buffer = new unsigned char[f*c];
unsigned char *aux = &buffer[0];
for(int i=0;i<Filas(img);i++)
for(int j=0;j<Columnas(img);j++)
*(aux++)= Get(img,i,j);
//buffer[j+i*Columnas(img)]=Get(img,i,j);
if(EscribirImagenPGM (file, buffer, f, c))
exito=true;
delete [] buffer;
return exito;
}
//-------------------------------------------------
Codigo 2: Realización del negativo y desplazamiento:
void Negativo(Imagen& img)
{
int filas=Filas(img);
int columnas=Columnas(img);
for(int i=0;i<filas;i++)
for(int j=0;j<columnas;j++)
{
unsigned char n=255-Get(img,i,j);
Set (img,i,j,n);
}
}
{
int filas=Filas(img);
int columnas=Columnas(img);
for(int i=0;i<filas;i++)
for(int j=0;j<columnas;j++)
{
unsigned char n=255-Get(img,i,j);
Set (img,i,j,n);
}
}
void Desplazar(Imagen &img,int nbits)
{
int filas=Filas(img);
int columnas=Columnas(img);
for(int i=0;i<filas;i++)
for(int j=0;j<columnas;j++)
{
unsigned char n = Get(img,i,j) << nbits;
Set (img,i,j,n);
}
}
No hay comentarios:
Publicar un comentario