Commit 796a8c20 authored by Amir Mohammadi's avatar Amir Mohammadi
Browse files

Merge pull request #6 from bioidiap/netpbm

replace netpbm with pnmio
parents 602d7adc f12541a8
......@@ -15,7 +15,7 @@ matrix:
before_install:
- sudo add-apt-repository -y ppa:biometrics/bob
- sudo apt-get update -qq
- sudo apt-get install -qq --force-yes libboost-all-dev libblitz1-dev libjpeg8-dev libnetpbm10-dev libpng12-dev libtiff4-dev libgif-dev libhdf5-serial-dev libatlas-dev libatlas-base-dev liblapack-dev texlive-latex-recommended texlive-latex-extra texlive-fonts-recommended
- sudo apt-get install -qq --force-yes libboost-all-dev libblitz1-dev libjpeg8-dev libpng12-dev libtiff4-dev libgif-dev libhdf5-serial-dev libatlas-dev libatlas-base-dev liblapack-dev texlive-latex-recommended texlive-latex-extra texlive-fonts-recommended
- pip install --upgrade pip
- pip install --find-links https://www.idiap.ch/software/bob/wheels/travis/ --use-wheel sphinx nose coverage cpp-coveralls
- pip install --find-links https://www.idiap.ch/software/bob/wheels/travis/ --use-wheel -r requirements.txt --pre coveralls
......
......@@ -20,8 +20,113 @@
#include <bob.io.base/File.h>
extern "C" {
// This header must come last, as it brings a lot of global stuff that messes up other headers...
#include <pam.h>
#include "pnmio.h"
}
typedef unsigned long sample;
struct pam {
/* This structure describes an open PAM image file. It consists
entirely of information that belongs in the header of a PAM image
and filesystem information. It does not contain any state
information about the processing of that image.
This is not considered to be an opaque object. The user of Netbpm
libraries is free to access and set any of these fields whenever
appropriate. The structure exists to make coding of function calls
easy.
*/
/* 'size' and 'len' are necessary in order to provide forward and
backward compatibility between library functions and calling programs
as this structure grows.
*/
unsigned int size;
/* The storage size of this entire structure, in bytes */
unsigned int len;
/* The length, in bytes, of the information in this structure.
The information starts in the first byte and is contiguous.
This cannot be greater than 'size'
*/
FILE *file;
int format;
/* The format code of the raw image. This is PAM_FORMAT
unless the PAM image is really a view of a PBM, PGM, or PPM
image. Then it's PBM_FORMAT, RPBM_FORMAT, etc.
*/
unsigned int plainformat;
/* Logical: the format above is a plain (text) format as opposed
to a raw (binary) format. This is entirely redundant with the
'format' member and exists as a separate member only for
computational speed.
*/
int height; /* Height of image in rows */
int width;
/* Width of image in number of columns (tuples per row) */
unsigned int depth;
/* Depth of image (number of samples in each tuple). */
sample maxval; /* Maximum defined value for a sample */
unsigned int bytes_per_sample;
/* Number of bytes used to represent each sample in the image file.
Note that this is strictly a function of 'maxval'. It is in a
a separate member for computational speed.
*/
char tuple_type[256];
/* The tuple type string from the image header. Netpbm does
not define any values for this except the following, which are
used for a PAM image which is really a view of a PBM, PGM,
or PPM image: PAM_PBM_TUPLETYPE, PAM_PGM_TUPLETYPE,
PAM_PPM_TUPLETYPE.
*/
};
/* File open/close that handles "-" as stdin/stdout and checks errors. */
FILE*
pm_openr(const char * const name) {
FILE* f;
if (strcmp(name, "-") == 0)
f = stdin;
else {
#ifndef VMS
f = fopen(name, "rb");
#else
f = fopen (name, "r", "ctx=stm");
#endif
}
return f;
}
FILE*
pm_openw(const char * const name) {
FILE* f;
if (strcmp(name, "-") == 0)
f = stdout;
else {
#ifndef VMS
f = fopen(name, "wb");
#else
f = fopen (name, "w", "mbc=32", "mbf=2"); /* set buffer factors */
#endif
}
return f;
}
void
pm_close(FILE * const f) {
// fprintf(stderr, "I am closing a file\n");
fflush( f );
// if ( ferror( f ) )
// boost::format m("a file read or write error occurred at some point");
// throw std::runtime_error(m.str());
if ( f != stdin )
fclose( f );
// if ( fclose( f ) != 0 )
// boost::format m("cannot close file.");
// throw std::runtime_error(m.str());
}
static boost::shared_ptr<std::FILE> make_cfile(const char *filename, const char *flags)
......@@ -39,6 +144,107 @@ static boost::shared_ptr<std::FILE> make_cfile(const char *filename, const char
return boost::shared_ptr<std::FILE>(fp, pm_close);
}
static void pnm_readpaminit(FILE *file, struct pam * const pamP, const int size) {
int pnm_type=0;
int x_dim=256, y_dim=256;
int enable_ascii=1, img_colors=1;
int read_err;
pamP->file = file;
pnm_type = get_pnm_type(pamP->file);
rewind(pamP->file);
pamP->format = pnm_type;
/* Read the image file header (the input file has been rewinded). */
if ((pnm_type == PBM_ASCII) || (pnm_type == PBM_BINARY)) {
read_err = read_pbm_header(file, &x_dim, &y_dim, &enable_ascii);
pamP->bytes_per_sample = 1;
} else if ((pnm_type == PGM_ASCII) || (pnm_type == PGM_BINARY)) {
read_err = read_pgm_header(file, &x_dim, &y_dim, &img_colors, &enable_ascii);
if (img_colors >> 8 == 0) pamP->bytes_per_sample = 1;
else if (img_colors >> 16 == 0) pamP->bytes_per_sample = 2;
} else if ((pnm_type == PPM_ASCII) || (pnm_type == PPM_BINARY)) {
read_err = read_ppm_header(file, &x_dim, &y_dim, &img_colors, &enable_ascii);
if (img_colors >> 8 == 0) pamP->bytes_per_sample = 1;
else if (img_colors >> 16 == 0) pamP->bytes_per_sample = 2;
} else {
boost::format m("Unknown PNM/PFM image format.");
throw std::runtime_error(m.str());
}
if (read_err != 0) {
boost::format m("Something went wrong when reading the image file.");
throw std::runtime_error(m.str());
}
/* Perform operations. */
if ((pnm_type == PPM_ASCII) || (pnm_type == PPM_BINARY)) {
pamP->depth = 3;
} else {
pamP->depth = 1;
}
pamP->width = x_dim;
pamP->height = y_dim;
pamP->plainformat = enable_ascii;
pamP->maxval = img_colors;
}
static int * pnm_allocpam(struct pam * const pamP) {
int *img_data;
/* Perform operations. */
if ((pamP->format == PPM_ASCII) || (pamP->format == PPM_BINARY)) {
img_data = (int *) malloc((3 * pamP->width * pamP->height) * sizeof(int));
} else {
img_data = (int *) malloc((pamP->width * pamP->height) * sizeof(int));
}
return (img_data);
}
static void pnm_readpam(struct pam * const pamP, int *img_data) {
int read_err=1;
/* Read the image data. */
if ((pamP->format == PBM_ASCII) || (pamP->format == PBM_BINARY)) {
read_err = read_pbm_data(pamP->file, img_data, pamP->width * pamP->height, pamP->plainformat, pamP->width);
} else if ((pamP->format == PGM_ASCII) || (pamP->format == PGM_BINARY)) {
read_err = read_pgm_data(pamP->file, img_data, pamP->width * pamP->height, pamP->plainformat, pamP->bytes_per_sample);
} else if ((pamP->format == PPM_ASCII) || (pamP->format == PPM_BINARY)) {
read_err = read_ppm_data(pamP->file, img_data, 3 * pamP->width * pamP->height, pamP->plainformat, pamP->bytes_per_sample);
}
if (read_err != 0) {
boost::format m("Something went wrong when reading the image file.");
throw std::runtime_error(m.str());
}
}
static void pnm_writepam(struct pam * const pamP, int *img_data) {
int write_err=1;
/* Write the output image file. */
if ((pamP->format == PBM_ASCII) || (pamP->format == PBM_BINARY)) {
write_err = write_pbm_file(pamP->file, img_data,
pamP->width, pamP->height, 1, 1, 32, pamP->plainformat
);
} else if ((pamP->format == PGM_ASCII) || (pamP->format == PGM_BINARY)) {
write_err = write_pgm_file(pamP->file, img_data,
pamP->width, pamP->height, 1, 1, pamP->maxval, 16, pamP->plainformat,
pamP->bytes_per_sample
);
} else if ((pamP->format == PPM_ASCII) || (pamP->format == PPM_BINARY)) {
write_err = write_ppm_file(pamP->file, img_data,
pamP->width, pamP->height, 1, 1, pamP->maxval, pamP->plainformat,
pamP->bytes_per_sample
);
}
if (write_err != 0) {
boost::format m("Something went wrong when writing the image file.");
throw std::runtime_error(m.str());
}
}
/**
* LOADING
*/
......@@ -46,12 +252,7 @@ static void im_peek(const std::string& path, bob::io::base::array::typeinfo& inf
struct pam in_pam;
boost::shared_ptr<std::FILE> in_file = make_cfile(path.c_str(), "r");
#ifdef PAM_STRUCT_SIZE
// For version >= 10.23
pnm_readpaminit(in_file.get(), &in_pam, PAM_STRUCT_SIZE(tuple_type));
#else
pnm_readpaminit(in_file.get(), &in_pam, sizeof(struct pam));
#endif
if( in_pam.depth != 1 && in_pam.depth != 3)
{
......@@ -87,62 +288,53 @@ static void im_peek(const std::string& path, bob::io::base::array::typeinfo& inf
template <typename T> static
void im_load_gray(struct pam *in_pam, bob::io::base::array::interface& b) {
const bob::io::base::array::typeinfo& info = b.type();
int c=0;
T *element = static_cast<T*>(b.ptr());
tuple *tuplerow = pnm_allocpamrow(in_pam);
int *img_data = pnm_allocpam(in_pam);
pnm_readpam(in_pam, img_data);
for(size_t y=0; y<info.shape[0]; ++y)
{
pnm_readpamrow(in_pam, tuplerow);
for(size_t x=0; x<info.shape[1]; ++x)
{
*element = tuplerow[x][0];
*element = img_data[c];
++element;
c++;
}
}
pnm_freepamrow(tuplerow);
}
template <typename T> static
void imbuffer_to_rgb(size_t size, const tuple* tuplerow, T* r, T* g, T* b) {
for (size_t k=0; k<size; ++k) {
r[k] = tuplerow[k][0];
g[k] = tuplerow[k][1];
b[k] = tuplerow[k][2];
}
free(img_data);
}
template <typename T> static
void im_load_color(struct pam *in_pam, bob::io::base::array::interface& b) {
const bob::io::base::array::typeinfo& info = b.type();
int c=0;
long unsigned int frame_size = info.shape[2] * info.shape[1];
T *element_r = static_cast<T*>(b.ptr());
T *element_g = element_r+frame_size;
T *element_b = element_g+frame_size;
int row_color_stride = info.shape[2]; // row_stride for each component
tuple *tuplerow = pnm_allocpamrow(in_pam);
int *img_data = pnm_allocpam(in_pam);
pnm_readpam(in_pam, img_data);
for(size_t y=0; y<info.shape[1]; ++y)
{
pnm_readpamrow(in_pam, tuplerow);
imbuffer_to_rgb(row_color_stride, tuplerow, element_r, element_g, element_b);
element_r += row_color_stride;
element_g += row_color_stride;
element_b += row_color_stride;
for(size_t x=0; x<info.shape[2]; ++x)
{
element_r[y*info.shape[2] + x] = img_data[c+0];
element_g[y*info.shape[2] + x] = img_data[c+1];
element_b[y*info.shape[2] + x] = img_data[c+2];
c = c + 3;
}
}
pnm_freepamrow(tuplerow);
free(img_data);
}
static void im_load (const std::string& filename, bob::io::base::array::interface& b) {
struct pam in_pam;
boost::shared_ptr<std::FILE> in_file = make_cfile(filename.c_str(), "r");
#ifdef PAM_STRUCT_SIZE
// For version >= 10.23
pnm_readpaminit(in_file.get(), &in_pam, PAM_STRUCT_SIZE(tuple_type));
#else
pnm_readpaminit(in_file.get(), &in_pam, sizeof(struct pam));
#endif
const bob::io::base::array::typeinfo& info = b.type();
......@@ -179,48 +371,48 @@ static void im_load (const std::string& filename, bob::io::base::array::interfac
template <typename T>
static void im_save_gray(const bob::io::base::array::interface& b, struct pam *out_pam) {
const bob::io::base::array::typeinfo& info = b.type();
int c=0;
const T *element = static_cast<const T*>(b.ptr());
tuple *tuplerow = pnm_allocpamrow(out_pam);
for(size_t y=0; y<info.shape[0]; ++y) {
for(size_t x=0; x<info.shape[1]; ++x) {
tuplerow[x][0] = *element;
int *img_data = pnm_allocpam(out_pam);
for(size_t y=0; y<info.shape[0]; ++y)
{
for(size_t x=0; x<info.shape[1]; ++x)
{
img_data[c] = *element;
++element;
c++;
}
pnm_writepamrow(out_pam, tuplerow);
}
pnm_freepamrow(tuplerow);
pnm_writepam(out_pam, img_data);
free(img_data);
}
template <typename T> static
void rgb_to_imbuffer(size_t size, const T* r, const T* g, const T* b, tuple* tuplerow) {
for (size_t k=0; k<size; ++k) {
tuplerow[k][0] = r[k];
tuplerow[k][1] = g[k];
tuplerow[k][2] = b[k];
}
}
template <typename T>
static void im_save_color(const bob::io::base::array::interface& b, struct pam *out_pam) {
const bob::io::base::array::typeinfo& info = b.type();
int c=0;
long unsigned int frame_size = info.shape[2] * info.shape[1];
const T *element_r = static_cast<const T*>(b.ptr());
const T *element_g = element_r + frame_size;
const T *element_b = element_g + frame_size;
int row_color_stride = info.shape[2]; // row_stride for each component
tuple *tuplerow = pnm_allocpamrow(out_pam);
for(size_t y=0; y<info.shape[1]; ++y) {
rgb_to_imbuffer(row_color_stride, element_r, element_g, element_b, tuplerow);
pnm_writepamrow(out_pam, tuplerow);
element_r += row_color_stride;
element_g += row_color_stride;
element_b += row_color_stride;
int *img_data = pnm_allocpam(out_pam);
for(size_t y=0; y<info.shape[1]; ++y)
{
for(size_t x=0; x<info.shape[2]; ++x)
{
img_data[c+0] = element_r[y*info.shape[2] + x];
img_data[c+1] = element_g[y*info.shape[2] + x];
img_data[c+2] = element_b[y*info.shape[2] + x];
c = c + 3;
}
}
pnm_freepamrow(tuplerow);
pnm_writepam(out_pam, img_data);
free(img_data);
}
static void im_save (const std::string& filename, const bob::io::base::array::interface& array) {
......@@ -235,44 +427,32 @@ static void im_save (const std::string& filename, const bob::io::base::array::in
// Sets the parameters of the pam structure according to the bca::interface properties
out_pam.size = sizeof(out_pam);
#ifdef PAM_STRUCT_SIZE
// For version >= 10.23
out_pam.len = PAM_STRUCT_SIZE(tuple_type);
#else
out_pam.len = out_pam.size;
#endif
out_pam.file = out_file.get();
out_pam.plainformat = 0; // writes in binary
out_pam.height = (info.nd == 2 ? info.shape[0] : info.shape[1]);
out_pam.width = (info.nd == 2 ? info.shape[1] : info.shape[2]);
out_pam.depth = (info.nd == 2 ? 1 : 3);
out_pam.maxval = (bob::io::base::array::t_uint8 ? 255 : 65535);
out_pam.maxval = (info.dtype == bob::io::base::array::t_uint8 ? 255 : 65535);
out_pam.bytes_per_sample = (info.dtype == bob::io::base::array::t_uint8 ? 1 : 2);
out_pam.format = PAM_FORMAT;
if( ext.compare(".pbm") == 0)
{
out_pam.maxval = 1;
out_pam.format = PBM_FORMAT;
strcpy(out_pam.tuple_type, PAM_PBM_TUPLETYPE);
out_pam.format = PBM_BINARY;
}
else if( ext.compare(".pgm") == 0)
{
out_pam.format = PGM_FORMAT;
strcpy(out_pam.tuple_type, PAM_PGM_TUPLETYPE);
out_pam.format = PGM_BINARY;
}
else
{
out_pam.format = PPM_FORMAT;
strcpy(out_pam.tuple_type, PAM_PPM_TUPLETYPE);
out_pam.format = PPM_BINARY;
}
if(out_pam.depth == 3 && ext.compare(".ppm")) {
throw std::runtime_error("cannot save a color image into a file of this type.");
}
// Writes header in file
pnm_writepaminit(&out_pam);
// Writes content
if(info.dtype == bob::io::base::array::t_uint8) {
......@@ -415,12 +595,7 @@ class ImageNetpbmFile: public bob::io::base::File {
std::string ImageNetpbmFile::s_codecname = "bob.image_netpbm";
static bool netpbm_initialized = false;
boost::shared_ptr<bob::io::base::File> make_netpbm_file (const char* path, char mode) {
if (!netpbm_initialized) {
pm_init("bob",0);
netpbm_initialized = true;
}
return boost::make_shared<ImageNetpbmFile>(path, mode);
}
/*
* File : pnmio.c
* Description: I/O facilities for PBM, PGM, PPM (PNM) ASCII images.
* Author : Nikolaos Kavvadias <nikolaos.kavvadias@gmail.com>
* Copyright : (C) Nikolaos Kavvadias 2012, 2013, 2014, 2015, 2016
* Website : http://www.nkavvadias.com
*
* This file is part of libpnmio, and is distributed under the terms of the
* Modified BSD License.
*
* A copy of the Modified BSD License is included with this distribution
* in the file LICENSE.
* libpnmio is free software: you can redistribute it and/or modify it under the
* terms of the Modified BSD License.
* libpnmio is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the Modified BSD License for more details.
*
* You should have received a copy of the Modified BSD License along with
* libpnmio. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <math.h>
#include "pnmio.h"
#define MAXLINE 1024
// #define LITTLE_ENDIAN -1
// #define BIG_ENDIAN 1
#define GREYSCALE_TYPE 0 /* used for PFM */
#define RGB_TYPE 1 /* used for PFM */
/* get_pnm_type:
* Read the header contents of a PBM/PGM/PPM/PFM file up to the point of
* extracting its type. Valid types for a PNM image are as follows:
* PBM_ASCII = 1
* PGM_ASCII = 2
* PPM_ASCII = 3
* PBM_BINARY = 4
* PGM_BINARY = 5
* PPM_BINARY = 6
* PAM = 7
* PFM_RGB = 16
* PFM_GREYSCALE = 17
*
* The result (pnm_type) is returned.
*/
int get_pnm_type(FILE *f)
{
int flag=0;
int pnm_type=0;
unsigned int i;
char magic[MAXLINE];
char line[MAXLINE];
/* Read the PNM/PFM file header. */
while (fgets(line, MAXLINE, f) != NULL) {
flag = 0;
for (i = 0; i < strlen(line); i++) {
if (isgraph(line[i])) {
if ((line[i] == '#') && (flag == 0)) {
flag = 1;
}
}
}
if (flag == 0) {
sscanf(line, "%s", magic);
break;
}
}
/* NOTE: This part can be written more succinctly, however,
* it is better to have the PNM types decoded explicitly.
*/
if (strcmp(magic, "P1") == 0) {
pnm_type = PBM_ASCII;
} else if (strcmp(magic, "P2") == 0) {
pnm_type = PGM_ASCII;
} else if (strcmp(magic, "P3") == 0) {
pnm_type = PPM_ASCII;
} else if (strcmp(magic, "P4") == 0) {
pnm_type = PBM_BINARY;
} else if (strcmp(magic, "P5") == 0) {
pnm_type = PGM_BINARY;
} else if (strcmp(magic, "P6") == 0) {
pnm_type = PPM_BINARY;
} else if (strcmp(magic, "P7") == 0) {
pnm_type = PAM;
} else if (strcmp(magic, "PF") == 0) {
pnm_type = PFM_RGB;
} else if (strcmp(magic, "Pf") == 0) {
pnm_type = PFM_GREYSCALE;
} else {
pnm_type = -1;
}
return (pnm_type);
}
/* read_pbm_header:
* Read the header contents of a PBM (Portable Binary Map) file.
* An ASCII PBM image file follows the format:
* P1
* <X> <Y>
* <I1> <I2> ... <IMAX>
* A binary PBM image file uses P4 instead of P1 and
* the data values are represented in binary.
* NOTE1: Comment lines start with '#'.
* NOTE2: < > denote integer values (in decimal).
*/
int read_pbm_header(FILE *f, int *img_xdim, int *img_ydim, int *is_ascii)
{
int flag=0;
int x_val, y_val;
unsigned int i;
char magic[MAXLINE];
char line[MAXLINE];
int count=0;
/* Read the PBM file header. */
while (fgets(line, MAXLINE, f) != NULL) {
flag = 0;
for (i = 0; i < strlen(line); i++) {
if (isgraph(line[i])) {
if ((line[i] == '#') && (flag == 0)) {
flag = 1;
}
}
}
if (flag == 0) {
if (count == 0) {
count += sscanf(line, "%s %d %d", magic, &x_val, &y_val);
} else if (count == 1) {
count += sscanf(line, "%d %d", &x_val, &y_val);
} else if (count == 2) {
count += sscanf(line, "%d", &y_val);
}
}
if (count == 3) {
break;
}
}