Error al guardar imagen PGM P2

Iniciado por AdrianGL13, 31 Mayo 2018, 07:26 AM

0 Miembros y 1 Visitante están viendo este tema.

AdrianGL13

Hola, buenas noches. Espero alguien me pueda ayudar con este problema.

Necesito realizar un programa en C++ en el cual ingrese una imagen PGM P2, la lea, cree una copia, la pase a negativo (invertir) y la guarde.

Tengo este código y compila, sin embargo al momento de guardar, la imagen arroja valores muy extraños, en lugar de ser números, da letras o símbolos. Ya intente cambiar valores char a int pero sigue sin funcionar. Espero alguien me pueda ayudar. Muchas gracias.

include <iostream>
#include <string.h>
#include <stdio.h>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <math.h>
#include <ctime>
#include <time.h>

using namespace std;

typedef struct imagen
{
    char nombre[200];
    int comentarios;
    int ancho;
    int alto;
    int escala;
    unsigned char im[1000][1000];
}
IMAGEN;

int tipo_imagen(char *nombre)
{
    FILE *archivo;

    unsigned char c1, c2;

    archivo = fopen(nombre,"r");

    if(archivo == NULL)
    {
        return 0;
    }

    c1 = fgetc(archivo);
    c2 = fgetc(archivo);

    fclose(archivo);

    if((c1=='P') && (c2=='2'))
    {
        cout<<"ARCHIVO CARGADO"<<endl;
        return 1;
    }
    else
    {
        cout<<"ERROR - Imagen no soportada"<<endl;
        return 2;
    }
}

IMAGEN leer_imagen(char *nombre)
{
    IMAGEN imagen;
    unsigned int c1, c2;
    unsigned char caracter;

    FILE *archivo;

    int i, j, ancho, alto, escala;

    archivo = fopen(nombre, "r");

    c1 = fgetc(archivo);
    c2 = fgetc(archivo);
    fgetc(archivo);

    caracter = fgetc(archivo);
    imagen.comentarios = 0;
    while(caracter=='#')
    {
        imagen.comentarios = imagen.comentarios + 1;
        caracter = fgetc(archivo);
        while(caracter != '\n')
        {
            caracter = fgetc(archivo);
        }
        caracter = fgetc(archivo);
    }

    ungetc(caracter,archivo);
    fscanf(archivo,"%d%d",&ancho,&alto);
    fscanf(archivo,"%d",&escala);
    imagen.ancho = ancho;
    imagen.alto = alto;
    imagen.escala = escala;
    strcpy(imagen.nombre,nombre);
    for(i=0; i<imagen.alto; i++)
    {
        for(j=0; j<imagen.ancho; j++)
        {
            caracter = fgetc(archivo);
            imagen.im[i][j] = caracter;
        }
    }
    fclose(archivo);
    return(imagen);

    cout<<"ARCHIVO LEIDO"<<endl;
}

void mostrar_datos_imagen(IMAGEN *imagen)
{
    cout<<"DATOS DE ARCHIVO"<<endl;
    printf("\nNombre: %s",imagen->nombre);
    printf("\nComentarios : %d",imagen->comentarios);
    printf("\nAncho : %d",imagen->ancho);
    printf("\nAlto  : %d",imagen->alto);
    printf("\nEscala: %d\n",imagen->escala);
}

IMAGEN duplica_imagen(IMAGEN *imagen)
{
    IMAGEN duplica;
    int i,j;

    strcpy(duplica.nombre,"Dup_");
    strcat(duplica.nombre,imagen->nombre);
    duplica.comentarios = imagen->comentarios;
    duplica.ancho = imagen->ancho;
    duplica.alto = imagen->alto;
    duplica.escala = imagen->escala;
    for(i=0; i<imagen->alto; i++)
    {
        for(j=0; j<imagen->ancho; j++)
        {
            duplica.im[i][j]=imagen->im[i][j];
        }
    }
    return duplica;

    cout<<"ARCHIVO DUPLICADO"<<endl;
}

void invertir_imagen(IMAGEN *imagen)
{
    int i,j;
    for(i=0; i<imagen->alto; i++)
    {
        for(j=0; j<imagen->ancho; j++)
        {
            imagen->im[i][j]=255 - imagen->im[i][j];
        }
    }

    cout<<"ARCHIVO INVERTIDO"<<endl;
}

void grabar_imagen(IMAGEN *imagen)
{
    int i,j;
    unsigned char c;
    FILE *copia;

    copia=fopen(imagen->nombre,"w");

    fprintf(copia,"P2\n");
    fprintf(copia,"#Creado por NanoGrupito\n");
    fprintf(copia,"%d %d\n",imagen->ancho,imagen->alto);
    fprintf(copia,"%d\n",imagen->escala);

    for(i=0; i<imagen->alto;i++)
    {
        for(j=0; j<imagen->ancho; j++)
        {
            c=imagen->im[i][j];
            fputc(c,copia);
        }
    }

    fclose(copia);

    cout<<"ARCHIVO GRABADO"<<endl;
}

int main()
{
    char nombre[200];
    int imagen_tipo;
    IMAGEN imagen;
    IMAGEN imagen_analizada;

    cout<<"Ingresa el nombre de la imagen"<<endl;
    cin>>nombre;

    imagen_tipo = tipo_imagen(nombre);

    if(imagen_tipo==1)
    {
        imagen = leer_imagen(nombre);
        mostrar_datos_imagen(&imagen);
        imagen_analizada = duplica_imagen(&imagen);
        mostrar_datos_imagen(&imagen_analizada);
        invertir_imagen(&imagen_analizada);
        grabar_imagen(&imagen_analizada);
    }
}

srWhiteSkull

#1
Con el fopen deberías trabajar en modo binario para esas cosas pero en teoría los valores da igual que sean caracteres o numéricos pues eso es simplemente la forma en la que son representados. Los valores internamente se manejan en registros de varios bits (depende de la arquitectura).

http://www.cplusplus.com/reference/cstdio/fopen/

MAFUS

Es cierto. Pero tiene al menos tres enteros, y sabiendo que son de 4 bytes cada uno pues sí, debería ser un archivo binario.

Serapis

#3
No he repasado tod el código, me he detenido donde ya he visto errores...

Hasta donde yo sé, el formato PGM la cabecera es muy sencilla y no incluye un campo llamado 'escala'. No había visto la estructura y el nombre elegido, se presta a confusión...


Estructura CabeceraPGM
   Magic       // dos bytes "P2" ó "P5" para formato ASCII y binario respectivamente.
   Ancho     // píxeles de ancho en formato ASCII.
   Alto          // Ídem de alto    "
   NivelesGris   // cantidad de niveles de grises usados -1. Es decir un valor 255, implica 256 niveles de gris.
fin estructura


Y luego vienen los datos... a razón de 3 bytes (caracteres, digitos por píxel)... es decir
Si el primer píxel tiene el valor 82, en el fichero aparece " 82"
Si luego le siguen píxeles con estos valores: 7, 123, 94, 5, 0 210, estará así escrito tal cual lo ves aquí (a continuación), igual si lo abres con el bloc de notas...
  " 82  7123 94  5  0210"

Y efectivamente saltando las líneas de comentarios, que empiezan con sharp '#' y acaba en la propia línea (hasta encontrar el salto de línea). Aunque no hay restricción en esa cantidad de caracteres de comentarios típicamente se limita a no más de 70, pero no conviene fiarse de ese valor, pués es solo una sugerencia del creador.

Fíjate que al comienzo de cada línea de la imagen es valído ambas situaciones: para un valor 7, 22, 33
"  7 22 33"
" 7 22 33"
"7 22 33"
Es decir el primer píxel de una línea no exige los 3 caracteres, porque el valor podrá leerse sin ambigüedad, puede tener un espacio, 2 o ninguno. Nota además que esto es válido para cualquier línea, es decir una puede tener las 3 cifra, otra 2 y otra 1... aunque debe leerse así, para escribir, conviene que todas tengan la misma cantidad, porque el objetivo primordial de estos formatos es poder abrirlos y editarlos con el bloc de notas. Una desalineación al comienzo, puede complicar precisamente la edición y claridad de la imagen.

Rehaz de nuevo tu código para leer correctamente de esta manera y si sigues teniendo errores pasa a preguntar de nuevo...



mmm... acabo de ver la estructura que tenías y por tanto con 'escala', no te estés refiriendo a nada relativo a la imagen si no a los niveles de grises, que has querido llamar 'escala de grises', pero acortado... ok...

Nota que cuando, niveles de gris (escala), es distinto de 255, exigirá interpolar los valores, es decir si en origen una imagen tiene el valor 255 y ahora tu lo reduces a solo 8 niveles de gris, al ir los valores entre 0 y 7, deberás dividir cada valor entre 32, ya que 256/8 = 32... (para escribir en fichero, para mostrar la imagen siempre habrá que hacerlo en el rango 0-255)

...pero igualmente se seguirán necesitando SIEMPRE 3 digitos para representar en fichero el valor de cada píxel...

srWhiteSkull

Además a mi parecer el código no es bueno, porque encima reserva memoria estática en la misma estructura sin haberlo usado o sin tener nada (tanto para el nombre como los datos, el im). De esa forma está tomando memoria de la pila y es raro que la aplicación no se te rompa al ejecutarla a no ser que hayas modificado el tamaño de esta en el compilador.

Lo normal es que trabajes con punteros, con memoria dinámica. La estructura debería quedar algo así:

Código (cpp) [Seleccionar]
...
typedef struct {
char * nombre;
int comentarios;
int ancho;
int alto;
int escala;
unsigned char * datos;
} IMAGEN;

// luego definir un objeto IMAGEN
IMAGEN miImagen;

// luego una vez sabes el tamaño, mirando la cabecera, calculas la memoria que necesitas y la reservas
int ancho=320, alto=200;
miImagen.datos = (unsigned char*)malloc(sizeof(char)*(ancho*alto));

// luego para recorrer el puntero como si fuera la pantalla haces
miImagen.datos[x*ancho+y]=255;

char pixel = miImagen.datos[x*ancho+y];
...