You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
543 lines
13 KiB
543 lines
13 KiB
/** @file pgm.c
|
|
** @brief Portable graymap format (PGM) parser - Definition
|
|
** @author Andrea Vedaldi
|
|
**/
|
|
|
|
/*
|
|
Copyright (C) 2007-12 Andrea Vedaldi and Brian Fulkerson.
|
|
Copyright (C) 2013 Andrea Vedaldi.
|
|
All rights reserved.
|
|
|
|
This file is part of the VLFeat library and is made available under
|
|
the terms of the BSD license (see the COPYING file).
|
|
*/
|
|
|
|
/** @file pgm.h
|
|
|
|
This module implements basic input and ouptut of images in PGM
|
|
format.
|
|
|
|
Extracting an image encoded in PGM format from an imput
|
|
file stream involves the following steps:
|
|
|
|
- use ::vl_pgm_extract_head to extract the image meta data
|
|
(size and bit depth);
|
|
- allocate a buffer to store the image data;
|
|
- use ::vl_pgm_extract_data to extract the image data to the allocated
|
|
buffer.
|
|
|
|
Writing an image in PGM format to an ouptut file stream
|
|
can be done by using ::vl_pgm_insert.
|
|
|
|
To quickly read/write a PGM image from/to a given file, use
|
|
::vl_pgm_read_new() and ::vl_pgm_write(). To to the same from a
|
|
buffer in floating point format use ::vl_pgm_read_new_f() and
|
|
::vl_pgm_write_f().
|
|
|
|
**/
|
|
|
|
#include "pgm.h"
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
/** ------------------------------------------------------------------
|
|
** @internal @brief Remove all characters to the next new-line.
|
|
** @param f file to strip.
|
|
** @return number of characters removed.
|
|
**/
|
|
|
|
static int
|
|
remove_line(FILE* f)
|
|
{
|
|
int count = 0 ;
|
|
int c ;
|
|
|
|
while (1) {
|
|
c = fgetc(f) ;
|
|
++ count ;
|
|
|
|
switch(c) {
|
|
case '\n' :
|
|
goto quit_remove_line ;
|
|
|
|
case EOF :
|
|
-- count ;
|
|
goto quit_remove_line ;
|
|
}
|
|
}
|
|
quit_remove_line :
|
|
return count ;
|
|
}
|
|
|
|
/** ------------------------------------------------------------------
|
|
** @internal @brief Remove white-spaces and comments.
|
|
** @param f file to strip.
|
|
** @return number of characters removed.
|
|
**/
|
|
|
|
static int
|
|
remove_blanks(FILE* f)
|
|
{
|
|
int count = 0 ;
|
|
int c ;
|
|
|
|
while (1) {
|
|
c = fgetc(f) ;
|
|
|
|
switch(c) {
|
|
|
|
case '\t' : case '\n' :
|
|
case '\r' : case ' ' :
|
|
++ count ;
|
|
break ;
|
|
|
|
case '#' :
|
|
count += 1 + remove_line(f) ;
|
|
break ;
|
|
|
|
case EOF :
|
|
goto quit_remove_blanks ;
|
|
|
|
default:
|
|
ungetc(c, f) ;
|
|
goto quit_remove_blanks ;
|
|
}
|
|
}
|
|
quit_remove_blanks:
|
|
return count ;
|
|
}
|
|
|
|
/** ------------------------------------------------------------------
|
|
** @brief Get PGM image number of pixels.
|
|
** @param im PGM image descriptor.
|
|
** @return number of pixels of the image.
|
|
**
|
|
** The functions returns the number of pixels of the PGM image @a im.
|
|
**
|
|
** To calculate the image data size in bytes, this value must be
|
|
** multiplied by the number of byte per pixels (see
|
|
** ::vl_pgm_get_bpp()).
|
|
**/
|
|
|
|
VL_EXPORT vl_size
|
|
vl_pgm_get_npixels (VlPgmImage const *im)
|
|
{
|
|
return im->width * im->height ;
|
|
}
|
|
|
|
/** ------------------------------------------------------------------
|
|
** @brief Get PGM image bytes per pixel.
|
|
** @param im PGM image descriptor.
|
|
** @return number of bytes per pixel.
|
|
**
|
|
** The function returns the number of bytes for each pixel of the
|
|
** PGM image @a im.
|
|
**/
|
|
|
|
VL_EXPORT vl_size
|
|
vl_pgm_get_bpp (VlPgmImage const *im)
|
|
{
|
|
return (im->max_value >= 256) + 1 ;
|
|
}
|
|
|
|
/** ------------------------------------------------------------------
|
|
** @brief Extract PGM header from stream.
|
|
** @param f input file.
|
|
** @param im image structure to fill.
|
|
** @return error code.
|
|
**
|
|
** The function extracts from the file @a f the meta-data section of
|
|
** an image encoded in PGM format. The function fills the structure
|
|
** ::VlPgmImage accordingly.
|
|
**
|
|
** The error may be either ::VL_ERR_PGM_INV_HEAD or ::VL_ERR_PGM_INV_META
|
|
** depending whether the error occurred in decoding the header or
|
|
** meta section of the PGM file.
|
|
**/
|
|
|
|
VL_EXPORT int
|
|
vl_pgm_extract_head (FILE* f, VlPgmImage *im)
|
|
{
|
|
char magic [2] ;
|
|
int c ;
|
|
int is_raw ;
|
|
int width ;
|
|
int height ;
|
|
int max_value ;
|
|
size_t sz ;
|
|
vl_bool good ;
|
|
|
|
/* -----------------------------------------------------------------
|
|
* check magic number
|
|
* -------------------------------------------------------------- */
|
|
sz = fread(magic, 1, 2, f) ;
|
|
|
|
if (sz < 2) {
|
|
return vl_set_last_error(VL_ERR_PGM_INV_HEAD, "Invalid PGM header") ;
|
|
}
|
|
|
|
good = magic [0] == 'P' ;
|
|
|
|
switch (magic [1]) {
|
|
case '2' : /* ASCII format */
|
|
is_raw = 0 ;
|
|
break ;
|
|
|
|
case '5' : /* RAW format */
|
|
is_raw = 1 ;
|
|
break ;
|
|
|
|
default :
|
|
good = 0 ;
|
|
break ;
|
|
}
|
|
|
|
if( ! good ) {
|
|
return vl_set_last_error(VL_ERR_PGM_INV_HEAD, "Invalid PGM header") ;
|
|
}
|
|
|
|
/* -----------------------------------------------------------------
|
|
* parse width, height, max_value
|
|
* -------------------------------------------------------------- */
|
|
good = 1 ;
|
|
|
|
c = remove_blanks(f) ;
|
|
good &= c > 0 ;
|
|
|
|
c = fscanf(f, "%d", &width) ;
|
|
good &= c == 1 ;
|
|
|
|
c = remove_blanks(f) ;
|
|
good &= c > 0 ;
|
|
|
|
c = fscanf(f, "%d", &height) ;
|
|
good &= c == 1 ;
|
|
|
|
c = remove_blanks(f) ;
|
|
good &= c > 0 ;
|
|
|
|
c = fscanf(f, "%d", &max_value) ;
|
|
good &= c == 1 ;
|
|
|
|
/* must end with a single blank */
|
|
c = fgetc(f) ;
|
|
good &=
|
|
c == '\n' ||
|
|
c == '\t' ||
|
|
c == ' ' ||
|
|
c == '\r' ;
|
|
|
|
if(! good) {
|
|
return vl_set_last_error(VL_ERR_PGM_INV_META, "Invalid PGM meta information");
|
|
}
|
|
|
|
if(! max_value >= 65536) {
|
|
return vl_set_last_error(VL_ERR_PGM_INV_META, "Invalid PGM meta information");
|
|
}
|
|
|
|
/* exit */
|
|
im-> width = width ;
|
|
im-> height = height ;
|
|
im-> max_value = max_value ;
|
|
im-> is_raw = is_raw ;
|
|
return 0 ;
|
|
}
|
|
|
|
/** ------------------------------------------------------------------
|
|
** @brief Extract PGM data from stream.
|
|
** @param f input file.
|
|
** @param im PGM image descriptor.
|
|
** @param data data buffer to fill.
|
|
** @return error code.
|
|
**
|
|
** The function extracts from the file @a f the data section of an
|
|
** image encoded in PGM format. The function fills the buffer @a data
|
|
** according. The buffer @a data should be ::vl_pgm_get_npixels() by
|
|
** ::vl_pgm_get_bpp() bytes large.
|
|
**/
|
|
|
|
VL_EXPORT
|
|
int
|
|
vl_pgm_extract_data (FILE* f, VlPgmImage const *im, void *data)
|
|
{
|
|
vl_size bpp = vl_pgm_get_bpp(im) ;
|
|
vl_size data_size = vl_pgm_get_npixels(im) ;
|
|
vl_bool good = 1 ;
|
|
size_t c ;
|
|
|
|
/* -----------------------------------------------------------------
|
|
* read data
|
|
* -------------------------------------------------------------- */
|
|
|
|
/*
|
|
In RAW mode we read directly an array of bytes or shorts. In
|
|
the latter case, however, we must take care of the
|
|
endianess. PGM files are sorted in big-endian format. If our
|
|
architecture is little endian, we must do a conversion.
|
|
*/
|
|
if (im->is_raw) {
|
|
|
|
c = fread( data,
|
|
bpp,
|
|
data_size,
|
|
f ) ;
|
|
good = (c == data_size) ;
|
|
|
|
/* adjust endianess */
|
|
#if defined(VL_ARCH_LITTLE_ENDIAN)
|
|
if (bpp == 2) {
|
|
vl_uindex i ;
|
|
vl_uint8 *pt = (vl_uint8*) data ;
|
|
for(i = 0 ; i < 2 * data_size ; i += 2) {
|
|
vl_uint8 tmp = pt [i] ;
|
|
pt [i] = pt [i+1] ;
|
|
pt [i+1] = tmp ;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
/*
|
|
In ASCII mode we read a sequence of decimal numbers separated
|
|
by whitespaces.
|
|
*/
|
|
else {
|
|
vl_uindex i ;
|
|
int unsigned v ;
|
|
for(good = 1, i = 0 ;
|
|
i < data_size && good ;
|
|
++i) {
|
|
c = fscanf(f, " %ud", &v) ;
|
|
if (bpp == 1) {
|
|
* ((vl_uint8* ) data + i) = (vl_uint8) v ;
|
|
} else {
|
|
* ((vl_uint16*) data + i) = (vl_uint16) v ;
|
|
}
|
|
good &= c == 1 ;
|
|
}
|
|
}
|
|
|
|
if(! good ) {
|
|
return vl_set_last_error(VL_ERR_PGM_INV_DATA, "Invalid PGM data") ;
|
|
}
|
|
return 0 ;
|
|
}
|
|
|
|
/** ------------------------------------------------------------------
|
|
** @brief Insert a PGM image into a stream.
|
|
** @param f output file.
|
|
** @param im PGM image meta-data.
|
|
** @param data image data.
|
|
** @return error code.
|
|
**/
|
|
|
|
VL_EXPORT
|
|
int
|
|
vl_pgm_insert(FILE* f, VlPgmImage const *im, void const *data)
|
|
{
|
|
vl_size bpp = vl_pgm_get_bpp (im) ;
|
|
vl_size data_size = vl_pgm_get_npixels (im) ;
|
|
size_t c ;
|
|
|
|
/* write preamble */
|
|
fprintf(f,
|
|
"P5\n%d\n%d\n%d\n",
|
|
(signed)im->width,
|
|
(signed)im->height,
|
|
(signed)im->max_value) ;
|
|
|
|
/* take care of endianness */
|
|
#if defined(VL_ARCH_LITTLE_ENDIAN)
|
|
if (bpp == 2) {
|
|
vl_uindex i ;
|
|
vl_uint8* temp = vl_malloc (2 * data_size) ;
|
|
memcpy(temp, data, 2 * data_size) ;
|
|
for(i = 0 ; i < 2 * data_size ; i += 2) {
|
|
vl_uint8 tmp = temp [i] ;
|
|
temp [i] = temp [i+1] ;
|
|
temp [i+1] = tmp ;
|
|
}
|
|
c = fwrite(temp, 2, data_size, f) ;
|
|
vl_free (temp) ;
|
|
}
|
|
else {
|
|
#endif
|
|
c = fwrite(data, bpp, data_size, f) ;
|
|
#if defined(VL_ARCH_LITTLE_ENDIAN)
|
|
}
|
|
#endif
|
|
|
|
if(c != data_size) {
|
|
return vl_set_last_error(VL_ERR_PGM_IO, "Error writing PGM data") ;
|
|
}
|
|
return 0 ;
|
|
}
|
|
|
|
/** ------------------------------------------------------------------
|
|
** @brief Read a PGM file.
|
|
** @param name file name.
|
|
** @param im a pointer to the PGM image structure to fill.
|
|
** @param data a pointer to the pointer to the allocated buffer.
|
|
** @return error code.
|
|
**
|
|
** The function reads a PGM image from file @a name and initializes
|
|
** the structure @a im and the buffer @a data accordingly.
|
|
**
|
|
** The ownership of the buffer @a data is transfered to the caller.
|
|
** @a data should be freed by means of ::vl_free().
|
|
**
|
|
** @bug Only PGM files with 1 BPP are supported.
|
|
**/
|
|
|
|
VL_EXPORT
|
|
int vl_pgm_read_new (char const *name, VlPgmImage *im, vl_uint8** data)
|
|
{
|
|
int err = 0 ;
|
|
FILE *f = fopen (name, "rb") ;
|
|
|
|
if (! f) {
|
|
return vl_set_last_error(VL_ERR_PGM_IO, "Error opening PGM file `%s' for reading", name) ;
|
|
}
|
|
|
|
err = vl_pgm_extract_head(f, im) ;
|
|
if (err) {
|
|
fclose (f) ;
|
|
return err ;
|
|
}
|
|
|
|
if (vl_pgm_get_bpp(im) > 1) {
|
|
return vl_set_last_error(VL_ERR_BAD_ARG, "PGM with BPP > 1 not supported") ;
|
|
}
|
|
|
|
*data = vl_malloc (vl_pgm_get_npixels(im) * sizeof(vl_uint8)) ;
|
|
err = vl_pgm_extract_data(f, im, *data) ;
|
|
|
|
if (err) {
|
|
vl_free (data) ;
|
|
fclose (f) ;
|
|
}
|
|
|
|
fclose (f) ;
|
|
return err ;
|
|
}
|
|
|
|
/** ------------------------------------------------------------------
|
|
** @brief Read floats from a PGM file.
|
|
** @param name file name.
|
|
** @param im a pointer to the PGM image structure to fill.
|
|
** @param data a pointer to the pointer to the allocated buffer.
|
|
** @return error code.
|
|
**
|
|
** The function reads a PGM image from file @a name and initializes
|
|
** the structure @a im and the buffer @a data accordingly. The buffer
|
|
** @a data is an array of floats in the range [0, 1].
|
|
**
|
|
** The ownership of the buffer @a data is transfered to the caller.
|
|
** @a data should be freed by means of ::vl_free().
|
|
**
|
|
** @bug Only PGM files with 1 BPP are supported.
|
|
**/
|
|
|
|
VL_EXPORT
|
|
int vl_pgm_read_new_f (char const *name, VlPgmImage *im, float** data)
|
|
{
|
|
int err = 0 ;
|
|
size_t npixels ;
|
|
vl_uint8 *idata ;
|
|
|
|
err = vl_pgm_read_new (name, im, &idata) ;
|
|
if (err) {
|
|
return err ;
|
|
}
|
|
|
|
npixels = vl_pgm_get_npixels(im) ;
|
|
*data = vl_malloc (sizeof(float) * npixels) ;
|
|
{
|
|
size_t k ;
|
|
float scale = 1.0f / (float)im->max_value ;
|
|
for (k = 0 ; k < npixels ; ++ k) (*data)[k] = scale * idata[k] ;
|
|
}
|
|
|
|
vl_free (idata) ;
|
|
return 0 ;
|
|
}
|
|
|
|
/** ------------------------------------------------------------------
|
|
** @brief Write bytes to a PGM file.
|
|
** @param name file name.
|
|
** @param data data to write.
|
|
** @param width width of the image.
|
|
** @param height height of the image.
|
|
** @return error code.
|
|
**
|
|
** The function dumps the image @a data to the PGM file of the specified
|
|
** name. This is an helper function simplifying the usage of
|
|
** vl_pgm_insert().
|
|
**/
|
|
|
|
VL_EXPORT
|
|
int vl_pgm_write (char const *name, vl_uint8 const* data, int width, int height)
|
|
{
|
|
int err = 0 ;
|
|
VlPgmImage pgm ;
|
|
FILE *f = fopen (name, "wb") ;
|
|
|
|
if (! f) {
|
|
return vl_set_last_error(VL_ERR_PGM_IO,
|
|
"Error opening PGM file '%s' for writing", name) ;
|
|
}
|
|
|
|
pgm.width = width ;
|
|
pgm.height = height ;
|
|
pgm.is_raw = 1 ;
|
|
pgm.max_value = 255 ;
|
|
|
|
err = vl_pgm_insert (f, &pgm, data) ;
|
|
fclose (f) ;
|
|
|
|
return err ;
|
|
}
|
|
|
|
/** -------------------------------------------------------------------
|
|
** @brief Write floats to PGM file
|
|
** @param name file name.
|
|
** @param data data to write.
|
|
** @param width width of the image.
|
|
** @param height height of the image.
|
|
** @return error code.
|
|
**
|
|
** The function dumps the image @a data to the PGM file of the
|
|
** specified name. The data is re-scaled to fit in the range 0-255.
|
|
** This is an helper function simplifying the usage of
|
|
** vl_pgm_insert().
|
|
**/
|
|
|
|
VL_EXPORT
|
|
int vl_pgm_write_f (char const *name, float const* data, int width, int height)
|
|
{
|
|
int err = 0 ;
|
|
int k ;
|
|
float min = + VL_INFINITY_F ;
|
|
float max = - VL_INFINITY_F ;
|
|
float scale ;
|
|
|
|
vl_uint8 * buffer = vl_malloc (sizeof(float) * width * height) ;
|
|
|
|
for (k = 0 ; k < width * height ; ++k) {
|
|
min = VL_MIN(min, data [k]) ;
|
|
max = VL_MAX(max, data [k]) ;
|
|
}
|
|
|
|
scale = 255 / (max - min + VL_EPSILON_F) ;
|
|
|
|
for (k = 0 ; k < width * height ; ++k) {
|
|
buffer [k] = (vl_uint8) ((data [k] - min) * scale) ;
|
|
}
|
|
|
|
err = vl_pgm_write (name, buffer, width, height) ;
|
|
|
|
vl_free (buffer) ;
|
|
return err ;
|
|
}
|