Commit 6bed680f authored by Amir Mohammadi's avatar Amir Mohammadi
Browse files

initial attempt to replace netpbm with pnmio

parent 602d7adc
......@@ -20,23 +20,183 @@
#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.
*/
};
// void overflow_add(int a, int b) {
// if( a > INT_MAX - b)
// boost::format m("(netpbm) object too large");
// throw std::runtime_error(m.str());
// }
// void overflow2(int a, int b) {
// if(a < 0 || b < 0)
// boost::format m("(netpbm) object too large");
// throw std::runtime_error(m.str());
// if(b == 0)
// return;
// if(a > INT_MAX / b)
// boost::format m("(netpbm) object too large");
// throw std::runtime_error(m.str());
// }
// void *malloc2(int a, int b) {
// overflow2(a, b);
// if(a*b == 0)
// boost::format m("(netpbm) Zero byte allocation");
// throw std::runtime_error(m.str());
// return malloc(a*b);
// }
static boost::shared_ptr<std::FILE> make_cfile(const char *filename, const char *flags)
{
std::FILE* fp;
if(strcmp(flags, "r") == 0)
fp = pm_openr(filename);
fp = fopen(filename, "r");
else // write
fp = pm_openw(filename);
fp = fopen(filename, "w");
if(fp == 0) {
boost::format m("cannot open file `%s'");
m % filename;
throw std::runtime_error(m.str());
}
return boost::shared_ptr<std::FILE>(fp, pm_close);
return boost::shared_ptr<std::FILE>(fp, fclose);
}
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;
pamP->file = file;
pnm_type = get_pnm_type(pamP->file);
rewind(pamP->file);
// fprintf(stderr, "%d\n", pnm_type);
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_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_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_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 {
fprintf(stderr, "Error: Unknown PNM/PFM image format. Exiting...\n");
exit(1);
}
/* 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) {
/* Read the image data. */
if ((pamP->format == PBM_ASCII) || (pamP->format == PBM_BINARY)) {
read_pbm_data(pamP->file, img_data, pamP->plainformat);
} else if ((pamP->format == PGM_ASCII) || (pamP->format == PGM_BINARY)) {
read_pgm_data(pamP->file, img_data, pamP->plainformat);
} else if ((pamP->format == PPM_ASCII) || (pamP->format == PPM_BINARY)) {
read_ppm_data(pamP->file, img_data, pamP->plainformat);
}
}
static void pnm_writepam(struct pam * const pamP, int *img_data) {
char * file_name="";
/* Write the output image file. */
if ((pamP->format == PBM_ASCII) || (pamP->format == PBM_BINARY)) {
write_pbm_file(pamP->file, img_data, file_name,
pamP->width, pamP->height, 1, 1, 32, pamP->plainformat
);
} else if ((pamP->format == PGM_ASCII) || (pamP->format == PGM_BINARY)) {
write_pgm_file(pamP->file, img_data, file_name,
pamP->width, pamP->height, 1, 1, pamP->maxval, 16, pamP->plainformat
);
} else if ((pamP->format == PPM_ASCII) || (pamP->format == PPM_BINARY)) {
write_ppm_file(pamP->file, img_data, file_name,
pamP->width, pamP->height, 1, 1, pamP->maxval, pamP->plainformat
);
}
}
/**
......@@ -46,12 +206,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 +242,56 @@ 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);
for(size_t y=0; y<info.shape[1]; ++y)
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);
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[1]; ++x)
{
element_r[x] = img_data[c+0];
element_g[x] = img_data[c+1];
element_b[x] = img_data[c+2];
++element_r;
++element_g;
++element_b;
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 +328,51 @@ 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[0]; ++y)
{
for(size_t x=0; x<info.shape[1]; ++x)
{
img_data[c+0] = element_r[x];
img_data[c+1] = element_g[x];
img_data[c+2] = element_b[x];
++element_r;
++element_g;
++element_b;
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,12 +387,7 @@ 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]);
......@@ -248,22 +395,18 @@ static void im_save (const std::string& filename, const bob::io::base::array::in
out_pam.depth = (info.nd == 2 ? 1 : 3);
out_pam.maxval = (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")) {
......@@ -271,7 +414,7 @@ static void im_save (const std::string& filename, const bob::io::base::array::in
}
// Writes header in file
pnm_writepaminit(&out_pam);
// pnm_writepaminit(&out_pam);
// Writes content
if(info.dtype == bob::io::base::array::t_uint8) {
......@@ -415,12 +558,12 @@ class ImageNetpbmFile: public bob::io::base::File {
std::string ImageNetpbmFile::s_codecname = "bob.image_netpbm";
static bool netpbm_initialized = false;
// 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;
}
// 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 {
fprintf(stderr, "Error: Unknown PNM/PFM file; wrong magic number!\n");
exit(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).
*/
void 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;
}
}
if (strcmp(magic, "P1") == 0) {
*is_ascii = 1;
} else if (strcmp(magic, "P4") == 0) {
*is_ascii = 0;
} else {
fprintf(stderr, "Error: Input file not in PBM format!\n");
exit(1);
}
fprintf(stderr, "Info: magic=%s, x_val=%d, y_val=%d\n",
magic, x_val, y_val);
*img_xdim = x_val;
*img_ydim = y_val;
}
/* read_pgm_header:
* Read the header contents of a PGM (Portable Grey[scale] Map) file.
* An ASCII PGM image file follows the format:
* P2
* <X> <Y>
* <levels>
* <I1> <I2> ... <IMAX>
* A binary PGM image file uses P5 instead of P2 and
* the data values are represented in binary.
* NOTE1: Comment lines start with '#'.
* NOTE2: < > denote integer values (in decimal).
*/
void read_pgm_header(FILE *f, int *img_xdim, int *img_ydim, int *img_colors, int *is_ascii)
{
int flag=0;
int x_val, y_val, maxcolors_val;
unsigned int i;
char magic[MAXLINE];
char line[MAXLINE];
int count=0;
/* Read the PGM file header. */
while (fgets(line, MAXLINE, f) != NULL) {
flag = 0;
for (i = 0; i < strlen(line); i++) {