BaxterInterface/ASIFT_tests/demo_ASIFT_src/io_png/io_png.c

709 lines
22 KiB
C
Raw Normal View History

/*
* Copyright (c) 2010, Nicolas Limare <nicolas.limare@cmla.ens-cachan.fr>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* The views and conclusions contained in the software and
* documentation are those of the authors and should not be
* interpreted as representing official policies, either expressed
* or implied, of the copyright holder.
*/
/**
* @mainpage image read/write simplified interface
*
* README.txt:
* @verbinclude README.txt
*/
/**
* @file io_png.c
* @brief PNG read/write simplified interface
*
* This is a front-end to libpng, with routines to:
* @li read a PNG file as a deinterlaced 8bit integer or float array
* @li write a 8bit integer or float array to a PNG file
*
* Multi-channel images are handled : grey, grey+alpha, rgb and
* rgb+alpha, as well as on-the-fly color model conversion.
*
* @todo handle lossless 16bit data
* @todo add a test suite
* @todo internally handle RGB/gray conversion in read_png_raw()
* @todo handle deinterlacing as a libpng transform function
*
* @author Nicolas Limare <nicolas.limare@cmla.ens-cachan.fr>
*/
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <assert.h>
/* option to use a local version of the libpng */
#ifdef _LOCAL_LIBS
#include "png.h"
#else
#include <png.h>
/* Guoshen Yu, Windows version, 2010.10.16 */
/* #include "./libs/png/png.h" */
#endif
/* ensure consistency */
#include "io_png.h"
#define PNG_SIG_LEN 4
/* internal only datatype identifiers */
#define IO_PNG_U8 0x0001 /* 8bit unsigned integer */
#define IO_PNG_F32 0x0002 /* 32bit float */
/*
* READ
*/
/**
* @brief internal function used to cleanup the memory when
* png_read_raw() fails
*
* @param fp file pointer to close, ignored if NULL
* @param png_ptr_p, info_ptr_p, pointers to PNG structure pointers,
* ignored if NULL
* @return NULL
*/
static void *read_png_abort(FILE * fp,
png_structp * png_ptr_p, png_infop * info_ptr_p)
{
png_destroy_read_struct(png_ptr_p, info_ptr_p, NULL);
if (NULL != fp && stdin != fp)
(void) fclose(fp);
return NULL;
}
/**
* @brief internal function used to read a PNG file into an array
*
* @todo don't loose 16bit info
*
* @param fname PNG file name, "-" means stdin
* @param nx, ny, nc pointers to variables to be filled
* with the number of columns, lines and channels of the image
* @param transform a PNG_TRANSFORM to be added to the default read transforms
* @param dtype identifier for the data type to be used for output
* @return pointer to an allocated array of pixels,
* or NULL if an error happens
*/
static void *read_png_raw(const char *fname,
size_t * nx, size_t * ny, size_t * nc,
int transform, int dtype)
{
png_byte png_sig[PNG_SIG_LEN];
png_structp png_ptr;
png_infop info_ptr;
png_bytepp row_pointers;
png_bytep row_ptr;
/* volatile : because of setjmp/longjmp */
FILE *volatile fp = NULL;
void *data = NULL;
unsigned char *data_u8 = NULL;
unsigned char *data_u8_ptr = NULL;
float *data_f32 = NULL;
float *data_f32_ptr = NULL;
size_t size;
size_t i, j, k;
/* parameters check */
if (NULL == fname || NULL == nx || NULL == ny || NULL == nc)
return NULL;
if (IO_PNG_U8 != dtype && IO_PNG_F32 != dtype)
return NULL;
/* open the PNG input file */
if (0 == strcmp(fname, "-"))
fp = stdin;
else if (NULL == (fp = fopen(fname, "rb")))
return NULL;
/* read in some of the signature bytes and check this signature */
if ((PNG_SIG_LEN != fread(png_sig, 1, PNG_SIG_LEN, fp))
|| 0 != png_sig_cmp(png_sig, (png_size_t) 0, PNG_SIG_LEN))
return read_png_abort(fp, NULL, NULL);
/*
* create and initialize the png_struct
* with the default stderr and error handling
*/
if (NULL == (png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
NULL, NULL, NULL)))
return read_png_abort(fp, NULL, NULL);
/* allocate/initialize the memory for image information */
if (NULL == (info_ptr = png_create_info_struct(png_ptr)))
return read_png_abort(fp, &png_ptr, NULL);
/* set error handling */
if (0 != setjmp(png_jmpbuf(png_ptr)))
/* if we get here, we had a problem reading the file */
/* free all of the memory associated with the png_ptr and info_ptr */
return read_png_abort(fp, &png_ptr, &info_ptr);
/* set up the input control using standard C streams */
png_init_io(png_ptr, fp);
/* let libpng know that some bytes have been read */
png_set_sig_bytes(png_ptr, PNG_SIG_LEN);
/*
* set the read filter transforms, to get 8bit RGB whatever the
* original file may contain:
* PNG_TRANSFORM_STRIP_16 strip 16-bit samples to 8 bits
* PNG_TRANSFORM_PACKING expand 1, 2 and 4-bit
* samples to bytes
*/
transform |= (PNG_TRANSFORM_STRIP_16 | PNG_TRANSFORM_PACKING);
/* read in the entire image at once */
png_read_png(png_ptr, info_ptr, transform, NULL);
/* get image informations */
*nx = (size_t) png_get_image_width(png_ptr, info_ptr);
*ny = (size_t) png_get_image_height(png_ptr, info_ptr);
*nc = (size_t) png_get_channels(png_ptr, info_ptr);
row_pointers = png_get_rows(png_ptr, info_ptr);
/*
* allocate the output data RGB array
* deinterlace and convert png RGB RGB RGB 8bit to RRR GGG BBB
* the image is deinterlaced layer after layer
* this generic loop also works for one single channel
*/
size = *nx * *ny * *nc;
switch (dtype)
{
case IO_PNG_U8:
if (NULL == (data_u8 =
(unsigned char *) malloc(size * sizeof(unsigned char))))
return read_png_abort(fp, &png_ptr, &info_ptr);
data = (void *) data_u8;
for (k = 0; k < *nc; k++)
{
/* channel loop */
data_u8_ptr = data_u8 + (size_t) (*nx * *ny * k);
for (j = 0; j < *ny; j++)
{
/* row loop */
row_ptr = row_pointers[j] + k;
for (i = 0; i < *nx; i++)
{
/* pixel loop */
*data_u8_ptr++ = (unsigned char) *row_ptr;
row_ptr += *nc;
}
}
}
break;
case IO_PNG_F32:
if (NULL == (data_f32 = (float *) malloc(size * sizeof(float))))
return read_png_abort(fp, &png_ptr, &info_ptr);
data = (void *) data_f32;
for (k = 0; k < *nc; k++)
{
/* channel loop */
data_f32_ptr = data_f32 + (size_t) (*nx * *ny * k);
for (j = 0; j < *ny; j++)
{
/* row loop */
row_ptr = row_pointers[j] + k;
for (i = 0; i < *nx; i++)
{
/* pixel loop */
*data_f32_ptr++ = (float) *row_ptr;
row_ptr += *nc;
}
}
}
break;
}
/* clean up and free any memory allocated, close the file */
(void) read_png_abort(fp, &png_ptr, &info_ptr);
return data;
}
/**
* @brief read a PNG file into a 8bit integer array
*
* The array contains the deinterlaced channels.
* 1, 2 and 4bit images are converted to 8bit.
* 16bit images are previously downscaled to 8bit.
*
* @todo don't downscale 16bit images.
*
* @param fname PNG file name
* @param nx, ny, nc pointers to variables to be filled with the number of
* columns, lines and channels of the image
* @return pointer to an allocated unsigned char array of pixels,
* or NULL if an error happens
*/
unsigned char *read_png_u8(const char *fname,
size_t * nx, size_t * ny, size_t * nc)
{
/* read the image as unsigned char */
return (unsigned char *) read_png_raw(fname, nx, ny, nc,
PNG_TRANSFORM_IDENTITY, IO_PNG_U8);
}
/**
* @brief read a PNG file into a 8bit integer array, converted to RGB
*
* See read_png_u8() for details.
*/
unsigned char *read_png_u8_rgb(const char *fname, size_t * nx, size_t * ny)
{
size_t nc;
unsigned char *img;
/* read the image */
img = (unsigned char *) read_png_raw(fname, nx, ny, &nc,
PNG_TRANSFORM_STRIP_ALPHA,
IO_PNG_U8);
if (NULL == img)
/* error */
return NULL;
if (3 == nc)
/* already RGB */
return img;
else
{
/* convert to RGB */
unsigned char *ptr_r, *ptr_g, *ptr_b, *ptr_end;
/* resize the image */
img = realloc(img, 3 * *nx * *ny * sizeof(unsigned char));
/* gray->RGB conversion */
ptr_r = img;
ptr_end = ptr_r + *nx * *ny;
ptr_g = img + *nx * *ny;
ptr_b = img + 2 * *nx * *ny;
while (ptr_r < ptr_end)
{
*ptr_g++ = *ptr_r;
*ptr_b++ = *ptr_r++;
}
return img;
}
}
/**
* @brief read a PNG file into a 8bit integer array, converted to gray
*
* See read_png_u8() for details.
*/
unsigned char *read_png_u8_gray(const char *fname, size_t * nx, size_t * ny)
{
size_t nc;
unsigned char *img;
/* read the image */
img = (unsigned char *) read_png_raw(fname, nx, ny, &nc,
PNG_TRANSFORM_STRIP_ALPHA,
IO_PNG_U8);
if (NULL == img)
/* error */
return NULL;
if (1 == nc)
/* already gray */
return img;
else
{
/* convert to gray */
unsigned char *ptr_r, *ptr_g, *ptr_b, *ptr_gray, *ptr_end;
/*
* RGB->gray conversion
* Y = (6969 * R + 23434 * G + 2365 * B)/32768
* integer approximation of
* Y = 0.212671 * R + 0.715160 * G + 0.072169 * B
*/
ptr_r = img;
ptr_g = img + *nx * *ny;
ptr_b = img + 2 * *nx * *ny;
ptr_gray = img;
ptr_end = ptr_gray + *nx * *ny;
while (ptr_gray < ptr_end)
*ptr_gray++ = (unsigned char) (6969 * *ptr_r++
+ 23434 * *ptr_g++
+ 2365 * *ptr_b++) / 32768;
/* resize and return the image */
img = realloc(img, *nx * *ny * sizeof(unsigned char));
return img;
}
}
/**
* @brief read a PNG file into a 32bit float array
*
* The array contains the deinterlaced channels.
* 1, 2, 4 and 8bit images are converted to float values
* between 0. and 1., 3., 15. or 255.
* 16bit images are also downscaled to 8bit before conversion.
*
* @param fname PNG file name
* @param nx, ny, nc pointers to variables to be filled with the number of
* columns, lines and channels of the image
* @return pointer to an allocated unsigned char array of pixels,
* or NULL if an error happens
*/
float *read_png_f32(const char *fname, size_t * nx, size_t * ny, size_t * nc)
{
/* read the image as float */
return (float *) read_png_raw(fname, nx, ny, nc,
PNG_TRANSFORM_IDENTITY, IO_PNG_F32);
}
/**
* @brief read a PNG file into a 32bit float array, converted to RGB
*
* See read_png_f32() for details.
*/
float *read_png_f32_rgb(const char *fname, size_t * nx, size_t * ny)
{
size_t nc;
float *img;
/* read the image */
img = (float *) read_png_raw(fname, nx, ny, &nc,
PNG_TRANSFORM_STRIP_ALPHA, IO_PNG_F32);
if (NULL == img)
/* error */
return NULL;
if (3 == nc)
/* already RGB */
return img;
else
{
/* convert to RGB */
float *ptr_r, *ptr_g, *ptr_b, *ptr_end;
/* resize the image */
img = realloc(img, 3 * *nx * *ny * sizeof(float));
/* gray->RGB conversion */
ptr_r = img;
ptr_end = ptr_r + *nx * *ny;
ptr_g = img + *nx * *ny;
ptr_b = img + 2 * *nx * *ny;
while (ptr_r < ptr_end)
{
*ptr_g++ = *ptr_r;
*ptr_b++ = *ptr_r++;
}
return img;
}
}
/**
* @brief read a PNG file into a 32bit float array, converted to gray
*
* See read_png_f32() for details.
*/
float *read_png_f32_gray(const char *fname, size_t * nx, size_t * ny)
{
size_t nc;
float *img;
/* read the image */
img = (float *) read_png_raw(fname, nx, ny, &nc,
PNG_TRANSFORM_STRIP_ALPHA, IO_PNG_F32);
if (NULL == img)
/* error */
return NULL;
if (1 == nc)
/* already gray */
return img;
else
{
/* convert to gray */
float *ptr_r, *ptr_g, *ptr_b, *ptr_gray, *ptr_end;
/*
* RGB->gray conversion
* Y = (6969 * R + 23434 * G + 2365 * B)/32768
* integer approximation of
* Y = 0.212671 * R + 0.715160 * G + 0.072169 * B
*/
ptr_r = img;
ptr_g = img + *nx * *ny;
ptr_b = img + 2 * *nx * *ny;
ptr_gray = img;
ptr_end = ptr_gray + *nx * *ny;
while (ptr_gray < ptr_end)
*ptr_gray++ = (float) (6969 * *ptr_r++
+ 23434 * *ptr_g++
+ 2365 * *ptr_b++) / 32768;
/* resize and return the image */
img = realloc(img, *nx * *ny * sizeof(float));
return img;
}
}
/*
* WRITE
*/
/**
* @brief internal function used to cleanup the memory when
* png_write_raw() fails
*
* @param fp file pointer to close, ignored if NULL
* @param idata, row_pointers arrays to free, ignored if NULL
* @param png_ptr_p, info_ptr_p, pointers to PNG structure pointers,
* ignored if NULL
* @return -1
*/
static int write_png_abort(FILE * fp,
png_byte * idata, png_bytep * row_pointers,
png_structp * png_ptr_p, png_infop * info_ptr_p)
{
png_destroy_write_struct(png_ptr_p, info_ptr_p);
if (NULL != row_pointers)
free(row_pointers);
if (NULL != idata)
free(idata);
if (NULL != fp && stdout != fp)
(void) fclose(fp);
return -1;
}
/**
* @brief internal function used to write a byte array as a PNG file
*
* The PNG file is written as a 8bit image file, interlaced,
* truecolor. Depending on the number of channels, the color model is
* gray, gray+alpha, rgb, rgb+alpha.
*
* @todo handle 16bit
*
* @param fname PNG file name, "-" means stdout
* @param data deinterlaced (RRR..GGG..BBB..AAA) image byte array
* @param nx, ny, nc number of columns, lines and channels
* @param dtype identifier for the data type to be used for output
* @return 0 if everything OK, -1 if an error occured
*/
static int write_png_raw(const char *fname, const void *data,
size_t nx, size_t ny, size_t nc, int dtype)
{
png_structp png_ptr;
png_infop info_ptr;
png_byte *idata = NULL, *idata_ptr = NULL;
png_bytep *row_pointers = NULL;
png_byte bit_depth;
/* volatile : because of setjmp/longjmp */
FILE *volatile fp;
const unsigned char *data_u8 = NULL;
const unsigned char *data_u8_ptr = NULL;
const float *data_f32 = NULL;
const float *data_f32_ptr = NULL;
float tmp;
int color_type, interlace, compression, filter;
size_t size;
size_t i, j, k;
/* parameters check */
if (0 >= nx || 0 >= ny || 0 >= nc)
return -1;
if (NULL == fname || NULL == data)
return -1;
if (IO_PNG_U8 != dtype && IO_PNG_F32 != dtype)
return -1;
/* open the PNG output file */
if (0 == strcmp(fname, "-"))
fp = stdout;
else if (NULL == (fp = fopen(fname, "wb")))
return -1;
/* allocate the interlaced array and row pointers */
size = nx * ny * nc;
if (NULL == (idata = (png_byte *) malloc(size * sizeof(png_byte))))
return write_png_abort(fp, NULL, NULL, NULL, NULL);
if (NULL == (row_pointers = (png_bytep *) malloc(ny * sizeof(png_bytep))))
return write_png_abort(fp, idata, NULL, NULL, NULL);
/*
* create and initialize the png_struct
* with the default stderr and error handling
*/
if (NULL == (png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
NULL, NULL, NULL)))
return write_png_abort(fp, idata, row_pointers, NULL, NULL);
/* allocate/initialize the memory for image information */
if (NULL == (info_ptr = png_create_info_struct(png_ptr)))
return write_png_abort(fp, idata, row_pointers, &png_ptr, NULL);
/* set error handling */
if (0 != setjmp(png_jmpbuf(png_ptr)))
/* if we get here, we had a problem reading the file */
return write_png_abort(fp, idata, row_pointers, &png_ptr, &info_ptr);
/* set up the input control using standard C streams */
png_init_io(png_ptr, fp);
/* set image informations */
bit_depth = 8;
switch (nc)
{
case 1:
color_type = PNG_COLOR_TYPE_GRAY;
break;
case 2:
color_type = PNG_COLOR_TYPE_GRAY_ALPHA;
break;
case 3:
color_type = PNG_COLOR_TYPE_RGB;
break;
case 4:
color_type = PNG_COLOR_TYPE_RGB_ALPHA;
break;
default:
png_destroy_read_struct(&png_ptr, NULL, NULL);
free(row_pointers);
free(idata);
(void) fclose(fp);
return -1;
}
interlace = PNG_INTERLACE_ADAM7;
compression = PNG_COMPRESSION_TYPE_BASE;
filter = PNG_FILTER_TYPE_BASE;
/* set image header */
png_set_IHDR(png_ptr, info_ptr, (png_uint_32) nx, (png_uint_32) ny,
bit_depth, color_type, interlace, compression, filter);
/* TODO : significant bit (sBIT), gamma (gAMA), comments (text) chunks */
png_write_info(png_ptr, info_ptr);
/*
* interlace and convert RRR GGG BBB to RGB RGB RGB
* the image is interlaced layer after layer
* this involves more memory exchange, but allows a generic loop
*/
switch (dtype)
{
case IO_PNG_U8:
data_u8 = (unsigned char *) data;
for (k = 0; k < nc; k++)
{
/* channel loop */
data_u8_ptr = data_u8 + (size_t) (nx * ny * k);
idata_ptr = idata + (size_t) k;
for (j = 0; j < ny; j++)
{
/* row loop */
for (i = 0; i < nx; i++)
{
/* pixel loop */
*idata_ptr = (png_byte) * data_u8_ptr++;
idata_ptr += nc;
}
}
}
break;
case IO_PNG_F32:
data_f32 = (float *) data;
for (k = 0; k < nc; k++)
{
/* channel loop */
data_f32_ptr = data_f32 + (size_t) (nx * ny * k);
idata_ptr = idata + (size_t) k;
for (j = 0; j < ny; j++)
{
/* row loop */
for (i = 0; i < nx; i++)
{
/* pixel loop */
tmp = floor(*data_f32_ptr++ + .5);
*idata_ptr = (png_byte) (tmp < 0. ? 0. :
(tmp > 255. ? 255. : tmp));
idata_ptr += nc;
}
}
}
break;
}
/* set row pointers */
for (j = 0; j < ny; j++)
row_pointers[j] = idata + (size_t) (nc * nx * j);
/* write out the entire image and end it */
png_write_image(png_ptr, row_pointers);
png_write_end(png_ptr, info_ptr);
/* clean up and free any memory allocated, close the file */
(void) write_png_abort(fp, idata, row_pointers, &png_ptr, &info_ptr);
return 0;
}
/**
* @brief write a 8bit unsigned integer array into a PNG file
*
* @param fname PNG file name
* @param data array to write
* @param nx, ny, nc number of columns, lines and channels of the image
* @return 0 if everything OK, -1 if an error occured
*/
int write_png_u8(const char *fname, const unsigned char *data,
size_t nx, size_t ny, size_t nc)
{
return write_png_raw(fname, (void *) data,
(png_uint_32) nx, (png_uint_32) ny, (png_byte) nc,
IO_PNG_U8);
}
/**
* @brief write a float array into a PNG file
*
* The float values are rounded to 8bit integers, and bounded to [0, 255].
*
* @todo handle 16bit images and flexible min/max
*
* @param fname PNG file name
* @param data array to write
* @param nx, ny, nc number of columns, lines and channels of the image
* @return 0 if everything OK, -1 if an error occured
*/
int write_png_f32(const char *fname, const float *data,
size_t nx, size_t ny, size_t nc)
{
return write_png_raw(fname, (void *) data,
(png_uint_32) nx, (png_uint_32) ny, (png_byte) nc,
IO_PNG_F32);
}