2012-01-11 36 views
6

Estoy tratando de escribir (usando libpng) una imagen en escala de grises de 16 bits donde cada color de punto es igual a la suma de sus coordenadas. El siguiente código debería producir un PNG de 16 bits, pero en su lugar produce 8 bits como this. ¿Por qué?escala de grises de 16 bits png

#include <stdio.h> 
#include <stdlib.h> 
#include <stdint.h> 
#include <png.h> 

void save_png(FILE* fp, long int size) 
{ 
    png_structp png_ptr = NULL; 
    png_infop info_ptr = NULL; 
    size_t x, y; 
    png_bytepp row_pointers; 

    png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); 
    if (png_ptr == NULL) { 
     return ; 
    } 

    info_ptr = png_create_info_struct(png_ptr); 
    if (info_ptr == NULL) { 
     png_destroy_write_struct(&png_ptr, NULL); 
     return ; 
    } 

    if (setjmp(png_jmpbuf(png_ptr))) { 
     png_destroy_write_struct(&png_ptr, &info_ptr); 
     return ; 
    } 

    png_set_IHDR(png_ptr, info_ptr, 
       size, size, // width and height 
       16, // bit depth 
       PNG_COLOR_TYPE_GRAY, // color type 
       PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); 

    /* Initialize rows of PNG. */ 
    row_pointers = (png_bytepp)png_malloc(png_ptr, 
     size*png_sizeof(png_bytep)); 

    for (int i=0; i<size; i++) 
     row_pointers[i]=NULL; 

    for (int i=0; i<size; i++) 
     row_pointers[i]=png_malloc(png_ptr, size*2); 

    //set row data 
    for (y = 0; y < size; ++y) { 
     png_bytep row = row_pointers[y]; 
     for (x = 0; x < size; ++x) { 
       short color = x+y; 
       *row++ = (png_byte)(color & 0xFF); 
       *row++ = (png_byte)(color >> 8); 
     } 
    } 

    /* Actually write the image data. */ 
    png_init_io(png_ptr, fp); 
    png_set_rows(png_ptr, info_ptr, row_pointers); 
    png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL); 
    //png_write_image(png_ptr, row_pointers); 

    /* Cleanup. */ 
    for (y = 0; y < size; y++) { 
     png_free(png_ptr, row_pointers[y]); 
    } 
    png_free(png_ptr, row_pointers); 
    png_destroy_write_struct(&png_ptr, &info_ptr); 
} 

int main() 
{ 
    FILE* f; 
    if((f=fopen("test.png", "wb"))!=NULL) 
    { 
    save_png(f, 257); 

    fclose(f); 
    } 
    return 0; 
} 

Respuesta

5

La imagen vinculada muestra como "16 bits" en "Propiedades" de Windows 7. Supongo que solo está viendo varias aplicaciones retrocediendo para convertirlas a 8 bits para su visualización, lo que (supongo) es bastante esperado ya que la mayoría de los dispositivos de visualización no admiten 16 bits.

+0

Windows 7 muestra dos triángulos rellenos de degradado. Esto debería ser un cuadrado para una imagen de 16 bits y un triángulo para 8 bits. – Yagg

+0

Oh, tienes razón. Leí la imagen con libpng nuevamente y pruebo los colores del punto. Ellos como deberían ser. – Yagg

1

Lo siento por resucitar un hilo viejo, pero llegué aquí después de buscar en Google cómo escribir imágenes en escala de grises de 16 bits. Me encontré con problemas similares, y pensé que sería útil publicar cómo resolví el problema.

TL; DR:

a) Los bytes tienen que ser proporcionados a la biblioteca MSB primero, por lo que funciona si se le da la vuelta las líneas anteriores a este:

*row++ = (png_byte)(color >> 8); 
*row++ = (png_byte)(color & 0xFF); 

b) Para ver realmente un valor de 16 bits en una pantalla de 8 bits, cualquier valor por debajo de 256 simplemente se recortará en negro. En términos prácticos, los valores que son varios múltiplos de 256 se deben usar para ver cualquier cosa. El código de color = x + y anterior probablemente no produjo valores que fueran lo suficientemente brillantes.

cómo llegué a las conclusiones anteriores:

Empecé con el código anterior, utilizando sólo 'x' como el color, no 'x + y'.

La intención era tener un degradado que se desvaneciera desde el negro de la izquierda hasta el max x de la derecha.

Sin embargo, en lugar de tener una pendiente larga, en su lugar obtuve varios degradados estrechos. ¡Esto gritó "¡INICIACIÓN INCORRECTA!"

He intentado invertir los bits, pero luego obtuve una imagen en negro. Me llevó un rato darme cuenta, pero como la pantalla solo se muestra en 8 bits, incluso el valor máximo de (en mi caso) 968 era demasiado oscuro. Esto se asigna a 2 o 3 en una pantalla de 8 bits, e incluso con una gamma alta no pude ver la diferencia.

Como sabía que mi X máxima era aproximadamente 1000, y que el valor máximo para un valor de 16 bits es 65000 ish, entonces usé (x * 60) como mi color. Eso terminó produciendo un resultado visible.

Gracias por la publicación original. Fue un excelente ejemplo para comenzar.

Cuestiones relacionadas