Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Move PNMI/O code over to the proper image loading system.

Fix some minor edge case bugs.
  • Loading branch information...
commit 30241cbbb6b9366c53c6f5710e361af27237ba93 1 parent 9031088
edrosten authored
View
4 cvd/image_io.h
@@ -234,7 +234,7 @@ namespace CVD
throw Exceptions::Image_IO::EofBeforeImage();
if(c == 'P')
- PNM::readPNM(im, i);
+ CVD::Internal::readImage<I, PNM::Reader>(im, i);
#ifdef CVD_HAVE_JPEG
else if(c == 0xff)
CVD::Internal::readImage<I, JPEG::reader>(im, i);
@@ -296,7 +296,7 @@ namespace CVD
case ImageType::PNM:
case ImageType::Automatic:
case ImageType::Unknown:
- Internal::writeImage<PixelType, PNM::pnm_writer>(im, o, p); break;
+ Internal::writeImage<PixelType, PNM::Writer>(im, o, p); break;
#ifdef CVD_HAVE_JPEG
case ImageType::JPEG: Internal::writeImage<PixelType, JPEG::writer>(im,o, p); break;
#endif
View
159 cvd/internal/io/pnm_grok.h
@@ -32,128 +32,43 @@ namespace CVD
{
namespace PNM
{
- class pnm_in
- {
+ class pnm_in;
+
+ using CVD::Internal::TypeList;
+ using CVD::Internal::Head;
+ class Reader
+ {
public:
- pnm_in(std::istream&);
- bool is_2_byte()const {return m_is_2_byte;}
- int channels(){return m_channels;}
- long x_size() const {return xs;}
- long y_size() const {return ys;}
- long elements_per_line() const {return xs * m_channels;}
- void get_raw_pixel_lines(unsigned char*, unsigned long nlines);
- void get_raw_pixel_lines(unsigned short*, unsigned long nlines);
-
-
+ Reader(std::istream&);
+ ~Reader();
+
+ ImageRef size();
+
+ void get_raw_pixel_line(bool*);
+ void get_raw_pixel_line(unsigned char*);
+ void get_raw_pixel_line(unsigned short*);
+ void get_raw_pixel_line(Rgb<unsigned char>*);
+ void get_raw_pixel_line(Rgb<unsigned short>*);
+
+ std::string datatype();
+ std::string name();
+
+ typedef TypeList<bool,
+ TypeList<byte,
+ TypeList<unsigned short,
+ TypeList<Rgb<byte>,
+ TypeList<Rgb<unsigned short>,
+ Head> > > > > Types;
+
private:
- std::istream& i;
- bool is_text;
- int type, maxval;
- int lines_so_far;
- void read_header();
- bool can_proc_lines(unsigned long);
- long xs, ys;
- bool m_is_2_byte;
- int m_channels;
- };
-
- template <class T, class S, int N> struct PNMReader;
-
- template <class T, class S> struct PNMReader<T,S,3>
- {
- typedef Rgb<S> array;
- static void readPixels(BasicImage<T>& im, pnm_in& pnm)
- {
- std::vector<array> rowbuf(pnm.x_size());
- for (int r=0; r<pnm.y_size(); r++)
- {
- pnm.get_raw_pixel_lines((S*) &(rowbuf[0]), 1);
- Pixel::ConvertPixels<array, T>::convert(&(rowbuf[0]), im[r], pnm.x_size());
- }
- }
+ std::auto_ptr<pnm_in> p;
+
};
-
- template <class T, class S> struct PNMReader<T,S,1>
- {
- static void readPixels(BasicImage<T>& im, pnm_in& pnm)
- {
- std::vector<S> rowbuf(pnm.x_size());
- for (int r=0; r<pnm.y_size(); r++)
- {
- pnm.get_raw_pixel_lines(&(rowbuf[0]), 1);
- Pixel::ConvertPixels<S, T>::convert(&(rowbuf[0]), im[r], pnm.x_size());
- }
- }
- };
-
- template <> struct PNMReader<Rgb<byte>,byte,3>
- {
- static void readPixels(BasicImage<Rgb<byte> >& im, pnm_in& pnm)
- {
- pnm.get_raw_pixel_lines((byte*)im.data(), pnm.y_size());
- }
- };
- template <> struct PNMReader<byte,byte,1>
- {
- static void readPixels(BasicImage<byte>& im, pnm_in& pnm)
- {
- pnm.get_raw_pixel_lines(im.data(), pnm.y_size());
- }
- };
- template <> struct PNMReader<Rgb<unsigned short>,unsigned short,3>
- {
- static void readPixels(BasicImage<Rgb<unsigned short> >& im, pnm_in& pnm)
- {
- pnm.get_raw_pixel_lines((unsigned short*)im.data(), pnm.y_size());
- }
- };
- template <> struct PNMReader<unsigned short,unsigned short,1>
- {
- static void readPixels(BasicImage<unsigned short>& im, pnm_in& pnm)
- {
- pnm.get_raw_pixel_lines(im.data(), pnm.y_size());
- }
- };
-
- template <class T> void readPNM(BasicImage<T>& im, pnm_in& pnm)
- {
- if (pnm.is_2_byte())
- {
- if (pnm.channels() == 3)
- PNMReader<T,unsigned short,3>::readPixels(im, pnm);
- else
- PNMReader<T,unsigned short,1>::readPixels(im, pnm);
- }
- else
- {
- if (pnm.channels() == 3)
- PNMReader<T,unsigned char,3>::readPixels(im, pnm);
- else
- PNMReader<T,unsigned char,1>::readPixels(im, pnm);
- }
- }
-
- template <class T> void readPNM(BasicImage<T>&im, std::istream& in)
- {
- pnm_in pnm(in);
- ImageRef size(pnm.x_size(), pnm.y_size());
- if(size != im.size())
- throw Exceptions::Image_IO::ImageSizeMismatch(size, im.size());
-
- readPNM(im, pnm);
- }
-
- template <class T> void readPNM(Image<T>&im, std::istream& in)
- {
- pnm_in pnm(in);
- im.resize(ImageRef(pnm.x_size(), pnm.y_size()));
- readPNM(im, pnm);
- }
////////////////////////////////////////////////////////////////////////////////
//
// PNM writing.
@@ -164,11 +79,12 @@ namespace CVD
template<> struct ComponentMapper<0,1> { typedef byte type; };
template<> struct ComponentMapper<0,0> { typedef unsigned short type; };
- class pnm_writer
+ class pnm_writer;
+ class Writer
{
public:
- pnm_writer(std::ostream&, ImageRef size, const std::string& type, const std::map<std::string, Parameter<> >& p);
- ~pnm_writer();
+ Writer(std::ostream&, ImageRef size, const std::string& type, const std::map<std::string, Parameter<> >& p);
+ ~Writer();
//void write_raw_pixel_line(const bool*);
void write_raw_pixel_line(const unsigned char*);
@@ -184,14 +100,7 @@ namespace CVD
std::numeric_limits<Element>::digits <= 8>::type type;
};
private:
-
- template<class P> void sanity_check(const P*);
- void write_shorts(const unsigned short*, int n);
-
- long row;
- std::ostream& o;
- ImageRef size;
- std::string type;
+ std::auto_ptr<pnm_writer> p;
};
View
997 pnm_src/pnm_grok.cxx
@@ -1,26 +1,26 @@
/*
- This file is part of the CVD Library.
+ This file is part of the CVD Library.
- Copyright (C) 2005 The Authors
+ Copyright (C) 2005 The Authors
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
- This library 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 GNU
- Lesser General Public License for more details.
+ This library 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 GNU
+ Lesser General Public License for more details.
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, write to the Free Software
- Foundation, Inc.,
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/*******************************************************************************
- pnm_grok.cxx Ed Rosten 2003
+ pnm_grok.cxx Ed Rosten 2003
This file provides very basic functionality for accessing PNM images. PNM images
are either greyscale (PBMs are treated as PGMs of depth 1) or RGB, and either 1
@@ -86,511 +86,618 @@ namespace CVD
namespace PNM
{
- //All possible error codes. These are loosely divided in to data errors (ie
- //problems with the input image) and programmer errors (runtime errors
- //which are almost definitely caused by some logic error in the program).
- enum errors
- {
- E_NONE = 0, //Data errors
- E_NOT_PNM,
- E_HEOF,
- E_DEOF,
- E_BAD_SIZE,
- E_BAD_MAXVAL,
- E_PBM_NOT_IMPLEMENTED,
- E_OUT_OF_MEMORY,
- E_UNTERMINATED_HEADER,
- E_PAM_NOT_IMPLEMENTED,
- E_LAST_DATA_ERROR,
-
- E_PROGRAMMER_ERROR = 255, //All data errors are sit below this
+ //All possible error codes. These are loosely divided in to data errors (ie
+ //problems with the input image) and programmer errors (runtime errors
+ //which are almost definitely caused by some logic error in the program).
+ enum errors
+ {
+ E_NONE = 0, //Data errors
+ E_NOT_PNM,
+ E_HEOF,
+ E_DEOF,
+ E_BAD_SIZE,
+ E_BAD_MAXVAL,
+ E_PBM_NOT_IMPLEMENTED,
+ E_OUT_OF_MEMORY,
+ E_UNTERMINATED_HEADER,
+ E_PAM_NOT_IMPLEMENTED,
+ E_LAST_DATA_ERROR,
+
+ E_PROGRAMMER_ERROR = 255, //All data errors are sit below this
- //Probably programmer errors:
- E_NO_LONG_PBM,
- E_WRITE_PAST_END,
- E_NOT_2_BYTE,
- E_NOT_1_BYTE,
- E_READ_PAST_END,
-
- E_MAX //All errors sit below this
- };
-
- string name_error(errors e)
- {
- const char* data_error_names[]=
- {
- "None.",
- "Not a PNM.",
- "EOF in header.",
- "EOF in data.",
- "Bad size specified.",
- "Invalid colour MAXVAL.",
- "Binary PBMs are not implemented.",
- "Out of memory.",
- "Unterminated header.",
- "Portable Arbirtary Maps are not implemented.",
- "Last data error. This should *never* occur."
+ //Probably programmer errors:
+ E_NO_LONG_PBM,
+ E_WRITE_PAST_END,
+ E_NOT_2_BYTE,
+ E_NOT_1_BYTE,
+ E_READ_PAST_END,
+
+ E_MAX //All errors sit below this
};
-
- const char* prog_error_names[]=
+
+ string name_error(errors e)
{
- "Long PBMs are silly.",
- "Trying to write past end of file.",
- "This is not a 2 byte PNM.",
- "This is not a 1 byte PNM.",
- "Trying to read past the end of the file."
- };
+ const char* data_error_names[]=
+ {
+ "None.",
+ "Not a PNM.",
+ "EOF in header.",
+ "EOF in data.",
+ "Bad size specified.",
+ "Invalid colour MAXVAL.",
+ "Binary PBMs are not implemented.",
+ "Out of memory.",
+ "Unterminated header.",
+ "Portable Arbirtary Maps are not implemented.",
+ "Last data error. This should *never* occur."
+ };
+
+ const char* prog_error_names[]=
+ {
+ "Long PBMs are silly.",
+ "Trying to write past end of file.",
+ "This is not a 2 byte PNM.",
+ "This is not a 1 byte PNM.",
+ "Trying to read past the end of the file."
+ };
+
+ const char* invalid = "Not a valid error.";
+
+ string ret;
+
+ if(e < 0 || e >= E_MAX)
+ ret=string("internal error: ") + invalid;
+ else if(e >= E_LAST_DATA_ERROR && e <= E_PROGRAMMER_ERROR)
+ ret=string("internal error: ") + invalid;
+ else if(e < E_LAST_DATA_ERROR)
+ ret=data_error_names[e];
+ else
+ ret=string("internal error: ") + prog_error_names[e-E_PROGRAMMER_ERROR - 1];
- const char* invalid = "Not a valid error.";
+ return "Error in PNM image: " + ret;
+ }
- string ret;
- if(e < 0 || e >= E_MAX)
- ret=string("internal error: ") + invalid;
- else if(e >= E_LAST_DATA_ERROR && e <= E_PROGRAMMER_ERROR)
- ret=string("internal error: ") + invalid;
- else if(e < E_LAST_DATA_ERROR)
- ret=data_error_names[e];
- else
- ret=string("internal error: ") + prog_error_names[e-E_PROGRAMMER_ERROR - 1];
+ class pnm_in
+ {
+ public:
+ pnm_in(std::istream&);
+ ImageRef size();
+ string datatype();
+
+ template<class C>
+ void get_raw_pixel_lines(C * c, unsigned long nlines);
+
+ private:
+ bool m_is_2_byte;
+ int m_channels;
+ string elemtype;
+ std::istream& i;
+ bool is_text;
+ int type, maxval;
+ int lines_so_far;
+ void read_header();
+ bool can_proc_lines(unsigned long);
+ long xs, ys;
+ };
- return "Error in PNM image: " + ret;
- }
+ bool clean(istream& i)
+ {
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Strip leading whitespae and PNM comments from an istream
+ // comments start with a '#' and and with a newline
+ unsigned char c;
+ for(;;)
+ {
+ i >> c;
+
+ if(i.eof()) //I'm pedantic like that.
+ return 0;
+ else if(c == '#') //Eat a whole line of comment
+ {
+ do
+ {
+ i >> c;
+ if(i.eof()) //I'm still a pedant
+ return 0;
+ }while(c != '\n');
+ }
+ else if(!isspace(c))
+ {
+ i.putback(c);
+ return 1;
+ }
+ }
+ }
+ ////////////////////////////////////////////////////////////////////////////////
+ //
+ // base PNM class definitions
+ //
+ ////////////////////////////////////////////////////////////////////////////////
- bool clean(istream& i)
- {
- ////////////////////////////////////////////////////////////////////////////
- //
- // Strip leading whitespae and PNM comments from an istream
- // comments start with a '#' and and with a newline
+ bool pnm_in::can_proc_lines(unsigned long nl)
+ {
+ //Slightly oddly named. Update the number of processed lines and return
+ //an error condition if the lines can not be processed.
- unsigned char c;
+ lines_so_far += nl;
+ if(lines_so_far > ys)
+ return false;
+ else
+ return true;
+ }
- for(;;)
- {
- i >> c;
+ ////////////////////////////////////////////////////////////////////////////////
+ //
+ // Input PNM class definitions
+ //
+ ////////////////////////////////////////////////////////////////////////////////
- if(i.eof()) //I'm pedantic like that.
- return 0;
- else if(c == '#') //Eat a whole line of comment
- {
- do
- {
- i >> c;
- if(i.eof()) //I'm still a pedant
- return 0;
- }while(c != '\n');
- }
- else if(!isspace(c))
+ pnm_in::pnm_in(std::istream& in)
+ :i(in)
{
- i.putback(c);
- return 1;
+ lines_so_far=0;
+ read_header();
}
- }
- }
-
- ////////////////////////////////////////////////////////////////////////////////
- //
- // base PNM class definitions
- //
- ////////////////////////////////////////////////////////////////////////////////
-
- bool pnm_in::can_proc_lines(unsigned long nl)
- {
- //Slightly oddly named. Update the number of processed lines and return
- //an error condition if the lines can not be processed.
-
- lines_so_far += nl;
- if(lines_so_far > ys)
- return false;
- else
- return true;
- }
-
- ////////////////////////////////////////////////////////////////////////////////
- //
- // Input PNM class definitions
- //
- ////////////////////////////////////////////////////////////////////////////////
-
- pnm_in::pnm_in(std::istream& in)
- :i(in)
- {
- lines_so_far=0;
- read_header();
- }
-
- void pnm_in::read_header()
- {
- //Fix the stream before returning.
-#define RETURN(X) do{ \
- i >> skipws; \
- if(X == E_NONE) \
- return; \
- else \
- throw Exceptions::Image_IO::MalformedImage(name_error(X)); \
- }while(0)
-
-#define CLEAN do{ \
- if(!clean(i)) \
- throw Exceptions::Image_IO::MalformedImage(name_error(E_HEOF)); \
- }while(0)
-
-#define GET(X) do{ \
- CLEAN; \
- i>>X; \
- if(i.eof()) \
- throw Exceptions::Image_IO::MalformedImage(name_error(E_HEOF)); \
- }while(0)
-
- char c1, c2;
-
- // i >> (int) will eat whitespace before and after the integer. This breaks
- // reading maxval since it can (and does) eat in to the image.
- i >> noskipws;
+ void pnm_in::read_header()
+ {
+ //Fix the stream before returning.
+#define RETURN(X) do{ \
+ i >> skipws; \
+ if(X == E_NONE) \
+ return; \
+ else \
+ throw Exceptions::Image_IO::MalformedImage(name_error(X)); \
+ }while(0)
+
+#define CLEAN do{ \
+ if(!clean(i)) \
+ throw Exceptions::Image_IO::MalformedImage(name_error(E_HEOF)); \
+ }while(0)
+
+#define GET(X) do{ \
+ CLEAN; \
+ i>>X; \
+ if(i.eof()) \
+ throw Exceptions::Image_IO::MalformedImage(name_error(E_HEOF)); \
+ }while(0)
+
+ char c1, c2;
+
+ // i >> (int) will eat whitespace before and after the integer. This breaks
+ // reading maxval since it can (and does) eat in to the image.
+ i >> noskipws;
- //Read and check magic number
-
- i >> c1 >> c2;
- if(i.eof())
- RETURN(E_HEOF);
- if(c1 != 'P' || c2 < '1' || c2 > '7')
- RETURN(E_NOT_PNM);
+ //Read and check magic number
- if(c2 == '7')
- RETURN(E_PAM_NOT_IMPLEMENTED);
+ i >> c1 >> c2;
+ if(i.eof())
+ RETURN(E_HEOF);
- if(strchr("123", c2))
- is_text = true;
- else
- is_text = false;
+ if(c1 != 'P' || c2 < '1' || c2 > '7')
+ RETURN(E_NOT_PNM);
- if(c2 == '1' || c2 == '4')
- type = PBM;
- else if(c2 == '2' || c2 == '5')
- type = PGM;
- else
- type = PPM;
+ if(c2 == '7')
+ RETURN(E_PAM_NOT_IMPLEMENTED);
+ if(strchr("123", c2))
+ is_text = true;
+ else
+ is_text = false;
- if(type == PGM)
- m_channels = 1;
- else
- m_channels = 3;
+ if(c2 == '1' || c2 == '4')
+ type = PBM;
+ else if(c2 == '2' || c2 == '5')
+ type = PGM;
+ else
+ type = PPM;
+
- //Read and check image dimensions
- GET(xs);
- GET(ys);
+ if(type == PGM)
+ m_channels = 1;
+ else
+ m_channels = 3;
- if(xs <= 0 || ys <= 0)
- RETURN(E_BAD_SIZE);
-
- //Read if necessasy and set MAXVAL
- if(type != PBM)
- {
- GET(maxval);
+ //Read and check image dimensions
+ GET(xs);
+ GET(ys);
- if(maxval <= 0 || maxval > 65535)
- RETURN(E_BAD_MAXVAL);
+ if(xs <= 0 || ys <= 0)
+ RETURN(E_BAD_SIZE);
+
+ //Read if necessasy and set MAXVAL
+ if(type != PBM)
+ {
+ GET(maxval);
+
+ if(maxval <= 0 || maxval > 65535)
+ RETURN(E_BAD_MAXVAL);
+
+ if(maxval <= 255)
+ m_is_2_byte = false;
+ else
+ m_is_2_byte = true;
+ }
+ else
+ {
+ maxval = 255;
+ m_is_2_byte = false;
+ }
+
+ if(type == PBM)
+ elemtype = "bool";
+ else if(type == PGM)
+ {
+ if(m_is_2_byte)
+ elemtype = PNM::type_name<unsigned short>::name();
+ else
+ elemtype = PNM::type_name<byte>::name();
+ }
+ if(type == PPM)
+ {
+ if(m_is_2_byte)
+ elemtype = PNM::type_name<Rgb<unsigned short> >::name();
+ else
+ elemtype = PNM::type_name<Rgb<byte> >::name();
+ }
+
- if(maxval <= 255)
- m_is_2_byte = false;
- else
- m_is_2_byte = true;
- }
- else
- {
- maxval = 255;
- m_is_2_byte = false;
- }
-
- //Remove everything to the beginning of the image
- if(is_text)
- CLEAN;
- else
- {
- //Binary PNMs have a single byte of whitespace.
-
- unsigned char tmp;
- i >> tmp;
- if(!isspace(tmp))
- RETURN(E_UNTERMINATED_HEADER);
- }
+
+ //Remove everything to the beginning of the image
+ if(is_text)
+ CLEAN;
+ else
+ {
+ //Binary PNMs have a single byte of whitespace.
+
+ unsigned char tmp;
+ i >> tmp;
+ if(!isspace(tmp))
+ RETURN(E_UNTERMINATED_HEADER);
+ }
- //hee hee hee. This makes the SGI compiler segfault :-)
- // RETURN(E_NONE);
- i >> skipws;
+ //hee hee hee. This makes the SGI compiler segfault :-)
+ // RETURN(E_NONE);
+ i >> skipws;
- //HACK
- //cerr << "is_rgb=" << m_is_rgb <<". xsize=" << xs << ". ysize=" << ys << ". is_text=" << is_text << ". type=" << type << ". maxval=" << maxval << endl;
+ //HACK
+ //cerr << "is_rgb=" << m_is_rgb <<". xsize=" << xs << ". ysize=" << ys << ". is_text=" << is_text << ". type=" << type << ". maxval=" << maxval << endl;
-
- return;
+
+ return;
#undef RETURN
#undef CLEAN
#undef GET
- }
+ }
+
+
+ //Text reading functions
+ void get_num(istream& i, byte* c)
+ {
+ int j=0;
+ i >> j; //Ignore EOF: be leniant
+ *c = j & 0xff;
+ }
- void pnm_in::get_raw_pixel_lines(unsigned char* c, unsigned long nlines)
- {
- //Reading into uchars is sufficiently different from reading in to
- //ushorts, so it has ben done as 2 functions as opposed to one
- //tmeplated one.
+ void get_num(istream& i, bool* c)
+ {
+ i >> ws;
+ int j = cin.get();
+ //PBMs are inverted
+ *c = (j != '0');
+ }
-#define RETURN(X) do{ \
- if(X == E_NONE) \
- return; \
- else \
- throw Exceptions::Image_IO::MalformedImage(name_error(X)); \
- }while(0)
+ void get_num(istream& i, unsigned short* c)
+ {
+ int j=0;
+ i >> j; //Ignore EOF: be leniant
+ *c = j & 0xffff;
+ }
- unsigned long k, j, npix;
- unsigned char* cc = c;
+ template<class C> void get_num(istream& i, Rgb<C>* c)
+ {
+ get_num(i, & (c->red));
+ get_num(i, & (c->green));
+ get_num(i, & (c->blue));
+ }
+
+ //Data reading functions
+ void really_swap(unsigned char* s, size_t n_elem)
+ {
+ #ifdef SWAP_BYTES
+ for(size_t j=0; j < n_elem; j++)
+ swap(s[2*j], s[2*j+1]);
+ #endif
+ }
- if(!can_proc_lines(nlines))
- RETURN(E_READ_PAST_END);
-
- //Load a bunch of pixels in to memory without doing internal buffering
-
- npix = xs * nlines;
+ void maybe_swap(unsigned short* s, size_t n)
+ {
+ really_swap((unsigned char*)s, n);
+ }
+ void maybe_swap(Rgb<unsigned short>*s , size_t n)
+ {
+ really_swap((unsigned char*)s, n*3);
+ }
- if(m_is_2_byte)
- RETURN(E_NOT_1_BYTE);
-
- if(type == PPM)
- npix *= 3;
-
- if(is_text) //Load text PNM data
- {
- //Don't report errors. The spec says to be really leniant with
- //respect to errors in text only pnms
+ void maybe_swap(bool*, size_t)
+ {}
+ void maybe_swap(byte*, size_t)
+ {}
+ void maybe_swap(Rgb<byte>*, size_t)
+ {}
- for(k=0; k < npix; k++)
+ template<class C>
+ void pnm_in::get_raw_pixel_lines(C * c, unsigned long nlines)
{
- i >> j;
+ if(datatype() != PNM::type_name<C>::name())
+ throw ReadTypeMismatch(datatype(), PNM::type_name<C>::name());
+
+ //Reading into uchars is sufficiently different from reading in to
+ //ushorts, so it has ben done as 2 functions as opposed to one
+ //tmeplated one.
+
+ unsigned long k, npix;
+ if(!can_proc_lines(nlines))
+ throw Exceptions::Image_IO::MalformedImage(name_error(E_READ_PAST_END));
+
+ //Load a bunch of pixels in to memory without doing internal buffering
+ npix = xs * nlines;
+
+ if(is_text) //Load text PNM data
+ {
+ for(k=0; k < npix; k++)
+ get_num(i, c++);
+ }
+ else if(type != PBM)
+ {
+ if(type == PBM)
+ throw Exceptions::Image_IO::MalformedImage(name_error(E_PBM_NOT_IMPLEMENTED));
- if(i.eof())
- j=0;
+ i.read((char*)c, m_channels*npix * (m_is_2_byte?2:1));
+ maybe_swap(c, npix);
+ }
+
+ //Don't report errors. The spec says to be really leniant with
+ //respect to errors in text only pnms
+ if(!is_text && i.fail())
+ throw Exceptions::Image_IO::MalformedImage(name_error(E_DEOF));
- *c++ = j & 0xff;
}
- if(type == PBM)
+ ImageRef pnm_in::size()
{
- //PBMs are inverted. Also, converto to a sensible maxval
- for(k=0; k < npix; k++, cc++)
- {
- if(*cc)
- *cc=0;
- else
- *cc=maxval;
- }
+ return ImageRef(xs, ys);
}
- }
- else if(type != PBM)
- {
- i.read((char*)c, (int)npix);
- }
- else
- RETURN(E_PBM_NOT_IMPLEMENTED);
-
- if(i.eof())
- RETURN(E_DEOF);
- }
+ string pnm_in::datatype()
+ {
+ return elemtype;
+ }
- void pnm_in::get_raw_pixel_lines(unsigned short* s, unsigned long nlines)
- {
- unsigned long k, j, npix;
-
- //Load a bunch of pixels in to memory without doing internal buffering
-
- if(!can_proc_lines(nlines))
- RETURN(E_READ_PAST_END);
- if(!m_is_2_byte)
- RETURN(E_NOT_2_BYTE);
- npix = xs * nlines;
-
- if(type == PPM)
- npix *= 3;
-
- if(is_text) //Load text PNM data
- {
- //Don't report errors. The spec says to be really leniant with
- //respect to errors in text only pnms
+ ////////////////////////////////////////////////////////////////////////////////
+ //
+ // Implementation of PNM reader class
+ //
- for(k=0; k < npix; k++)
+ Reader::~Reader()
{
- i >> j;
- if(i.eof())
- j=0;
- *s++ = j & 0xffff;
}
- }
- else
- {
-#ifndef LONG_PNM_FAST_LOAD
-
- unsigned char i1, i2;
- for(j=0; j < npix; j++)
+ Reader::Reader(istream& i)
+ :p(new pnm_in(i))
{
- i.read((char*)&i1, 1);
- i.read((char*)&i2, 1);
-
- //Data is big-endian
- *s++ = i1 << 8 | i2;
+ }
- if(i.eof())
- cerr << j << " ";
+ string Reader::datatype()
+ {
+ return p->datatype();
}
-#else
- i.read((char*)s, npix*2);
-
-#ifdef SWAP_BYTES
- unsigned char t;
- for(j=0; j < npix; j++, s++)
+ string Reader::name()
{
- t = *(unsigned char*)s;
- *(unsigned char*)s = *(1+(unsigned char*)s);
- *(1+(unsigned char*)s) = t;
+ return "PNM";
}
-#endif
-#endif
- }
- if(i.eof())
- RETURN(E_DEOF);
- }
- ////////////////////////////////////////////////////////////////////////////////
- //
- // Output PNM class definitions
- //
- ////////////////////////////////////////////////////////////////////////////////
-
- void writePNMHeader(ostream& out, int channels, ImageRef size, int maxval, bool text, const std::string& comments)
- {
- char m[3] = {'P',' ', '\n'};
- if (channels == 1)
- m[1] = '2';
- else
- m[1] = '3';
- if (!text)
- m[1] += 3;
- out.write(m, 3);
- bool freshLine = true;
- for (size_t i=0; i<comments.length(); i++) {
- if (freshLine)
- out << "# ";
- char c = comments[i];
- if (c == '\n') {
- freshLine = true;
- out << endl;
- } else {
- out << c;
- freshLine = false;
+ ImageRef Reader::size()
+ {
+ return p->size();
}
- }
- if (!freshLine)
- out << endl;
- out << size.x << " " << size.y << endl << maxval << endl;
- }
- pnm_writer::pnm_writer(std::ostream& out, ImageRef size_, const std::string& type_, const std::map<std::string, Parameter<> >&)
- :row(0),o(out),size(size_),type(type_)
- {
-
- if(type == "unsigned char")
- writePNMHeader(out, 1, size, 255, 0, "");
- else if(type == "unsigned short")
- writePNMHeader(out, 1, size, 65535, 0, "");
- else if(type == "CVD::Rgb<unsigned char>")
- writePNMHeader(out, 3, size, 255, 0, "");
- else if(type == "CVD::Rgb<unsigned short>")
- writePNMHeader(out, 3, size, 65535, 0, "");
- else
- throw UnsupportedImageSubType("PNM", type);
- }
- pnm_writer::~pnm_writer()
- {}
+ //Mechanically generate the pixel reading calls.
+ #define GEN1(X) void Reader::get_raw_pixel_line(X*d){p->get_raw_pixel_lines(d, 1);}
+ #define GEN2(X) GEN1(X) GEN1(Rgb<X>)
+ GEN1(bool)
+ GEN2(unsigned char)
+ GEN2(unsigned short)
+
+
+ #undef GEN1
+ #undef GEN3
+ ////////////////////////////////////////////////////////////////////////////////
+ //
+ // Output PNM class definitions
+ //
+ ////////////////////////////////////////////////////////////////////////////////
+
+ class pnm_writer
+ {
+ public:
+ pnm_writer(std::ostream&, ImageRef size, const std::string& type, const std::map<std::string, Parameter<> >& p);
+ ~pnm_writer();
+
+ //void write_raw_pixel_line(const bool*);
+ template<class C> void write_raw_pixel_line(const C*);
+
+ private:
+
+ void write_shorts(const unsigned char* data, size_t n_shorts);
+ void write_binary(const unsigned char* data, size_t n);
+ void write_binary(const unsigned short* data, size_t n);
+ void write_binary(const Rgb<unsigned char>* data, size_t n);
+ void write_binary(const Rgb<unsigned short>* data, size_t n);
+
+
+ template<class P> void sanity_check(const P*);
+
+ bool text;
+ long row;
+ std::ostream& o;
+ ImageRef size;
+ std::string type;
+ vector<unsigned char> rowbuf;
+ };
+
+
+ void writePNMHeader(ostream& out, int channels, ImageRef size, int maxval, bool text, const std::string& comments)
+ {
+ char m[3] = {'P',' ', '\n'};
+ if (channels == 1)
+ m[1] = '2';
+ else
+ m[1] = '3';
+ if (!text)
+ m[1] += 3;
+ out.write(m, 3);
+ bool freshLine = true;
+ for (size_t i=0; i<comments.length(); i++) {
+ if (freshLine)
+ out << "# ";
+ char c = comments[i];
+ if (c == '\n') {
+ freshLine = true;
+ out << endl;
+ } else {
+ out << c;
+ freshLine = false;
+ }
+ }
+ if (!freshLine)
+ out << endl;
+ out << size.x << " " << size.y << endl << maxval << endl;
+ }
- void pnm_writer::write_shorts(const unsigned short* data, int n)
- {
-
- #ifdef CVD_ARCH_LITTLE_ENDIAN
- const unsigned char* bdata = (const unsigned char*)data;
- for (int i=0; i<n; i++, bdata+=2)
+ pnm_writer::pnm_writer(std::ostream& out, ImageRef size_, const std::string& type_, const std::map<std::string, Parameter<> >& p)
+ :text(0),row(0),o(out),size(size_),type(type_)
+ {
+ if(p.count("pnm.raw"))
{
- unsigned char lohi[2] = {bdata[1], bdata[0]};
- o.write((const char*)lohi,2);
+ try{
+ text=!(p.find("pnm.raw")->second.get<bool>());
+ }
+ catch(std::bad_cast c){
+ cerr << "Warning pnm.raw is not a bool.\n";
+ }
}
- #else
- o.write((const char*)data, n*sizeof(unsigned short));
- #endif
- }
+
+ if(type == "unsigned char")
+ writePNMHeader(out, 1, size, 255, text, "");
+ else if(type == "unsigned short")
+ writePNMHeader(out, 1, size, 65535, text, "");
+ else if(type == "CVD::Rgb<unsigned char>")
+ writePNMHeader(out, 3, size, 255, text, "");
+ else if(type == "CVD::Rgb<unsigned short>")
+ writePNMHeader(out, 3, size, 65535, text, "");
+ else
+ throw UnsupportedImageSubType("PNM", type);
+ }
- template<class P> void pnm_writer::sanity_check(const P*)
- {
- if(type != PNM::type_name<P>::name())
- throw WriteTypeMismatch(type, PNM::type_name<P>::name());
-
- //Do some sanity checking
- if(row >= size.y)
- throw InternalLibraryError("CVD", "Write past end of image.");
-
- row++;
- }
+ pnm_writer::~pnm_writer()
+ {}
- void pnm_writer::write_raw_pixel_line(const unsigned char* data)
- {
- sanity_check(data);
- o.write(reinterpret_cast<const char*>(data), size.x);
- }
+ void pnm_writer::write_shorts(const unsigned char* data, size_t n_shorts)
+ {
+ #ifdef CVD_ARCH_LITTLE_ENDIAN
+ rowbuf.resize(n_shorts*sizeof(short));
+ copy(data, data + sizeof(short)*n_shorts, rowbuf.begin());
+
+ for (size_t i=0; i<rowbuf.size(); i+=2)
+ swap(rowbuf[i], rowbuf[i+1]);
+
+ o.write((char*)&rowbuf[0], rowbuf.size());
+ //for (size_t i=0; i<n_shorts; i++)
+ //o << data[2*i+1] << data[2*i];
+ #else
+ o.write((const char*)data, n*sizeof(unsigned short));
+ #endif
+ }
- void pnm_writer::write_raw_pixel_line(const unsigned short* data)
- {
- sanity_check(data);
- write_shorts(data, size.x);
- }
+ void pnm_writer::write_binary(const unsigned char* data, size_t n)
+ {
+ o.write(reinterpret_cast<const char*>(data), n);
+ }
+ void pnm_writer::write_binary(const unsigned short* data, size_t n)
+ {
+ write_shorts(reinterpret_cast<const unsigned char*>(data), n);
+ }
+ void pnm_writer::write_binary(const Rgb<unsigned char>* data, size_t n)
+ {
+ o.write(reinterpret_cast<const char*>(data), n*3);
+ }
+ void pnm_writer::write_binary(const Rgb<unsigned short>* data, size_t n)
+ {
+ write_shorts(reinterpret_cast<const unsigned char*>(data), n*3);
+ }
- void pnm_writer::write_raw_pixel_line(const Rgb<unsigned char>* data)
- {
- sanity_check(data);
- o.write(reinterpret_cast<const char*>(data), 3*size.x);
- }
+ template<class C> void write_text(const C* data, size_t n, ostream& o)
+ {
+ for(size_t i=0; i < n; i++)
+ o << (int) data[i] << endl;
+ }
- void pnm_writer::write_raw_pixel_line(const Rgb<unsigned short>* data)
- {
- sanity_check(data);
- write_shorts(reinterpret_cast<const unsigned short*>(data), 3*size.x);
- }
+ template<class C> void write_text(const Rgb<C>* data, size_t n, ostream& o)
+ {
+ for(size_t i=0; i < n; i++)
+ o << (int) data[i].red << " " << (int)data[i].green << " " << (int)data[i].blue << endl;
+ }
+
+ template<class C>
+ void pnm_writer::write_raw_pixel_line(const C* data)
+ {
+ if(type != PNM::type_name<C>::name())
+ throw WriteTypeMismatch(type, PNM::type_name<C>::name());
+
+ //Do some sanity checking
+ if(row >= size.y)
+ throw InternalLibraryError("CVD", "Write past end of image.");
+
+ row++;
+
+ if(text)
+ write_text(data, size.x, o);
+ else
+ write_binary(data, size.x);
+ }
+ ////////////////////////////////////////////////////////////////////////////////
+ //
+ // Public interface
+ //
-/*
- template <class T> void writePNMPixelsText(ostream& out, const T* data, size_t count)
- {
- size_t lines = count / 25;
- size_t k=0;
- for (size_t i=0; i<lines; i++) {
- for (size_t j=0; j<25; j++)
- out << (int)(data[k++]) << " ";
- out << endl;
- }
- while (k<count)
- out << (int)(data[k++]);
- out << endl;
- }
-*/
+ Writer::Writer(std::ostream& out, ImageRef size_, const std::string& type_, const std::map<std::string, Parameter<> >& p)
+ :p(new pnm_writer(out, size_, type_, p))
+ {}
- }
+ Writer::~Writer()
+ {}
+
+ //Mechanically generate the pixel writing calls.
+ #define GEN1(X) void Writer::write_raw_pixel_line(const X*d){p->write_raw_pixel_line(d);}
+ #define GEN2(X) GEN1(X) GEN1(Rgb<X>)
+ //GEN1(bool)
+ GEN2(unsigned char)
+ GEN2(unsigned short)
+
+
+
+ }
}
View
55 test/test_images.cxx
@@ -206,17 +206,25 @@ template<class T> void loadsave_safe(const char*n)
}
}
+map<string, Parameter<> > empty()
+{
+ map<string, Parameter<> > nothing;
+ return nothing;
+}
+
template<class T> struct randtest
{
typedef typename T::Type Type;
- static void exec(ImageType::ImageType fmt)
- {
- try{
- for(int i=0; i < 10; i++)
- {
+ static void exec(ImageType::ImageType fmt, const map<string, Parameter<> >& p = empty())
+ {
+ for(int i=0; i < 10; i++)
+ {
+ try{
//Make a random image
- Image<Type> in(ImageRef(1000,1000)), out;
+ Image<Type> in(ImageRef(1000+i,1000+i)), out;
+
+ cerr << "Testing " << in.size() << " " << fmt << " " << CVD::PNM::type_name<Type>::name() << " ";
for(int y=0; y < in.size().y; y++)
for(int x=0; x < in.size().x; x++)
@@ -225,7 +233,7 @@ template<class T> struct randtest
stringstream s;
//Save the image
- img_save(in, s, fmt);
+ img_save(in, s, fmt, p);
s.seekg(0, ios_base::beg);
s.seekp(0, ios_base::beg);
@@ -235,10 +243,10 @@ template<class T> struct randtest
//Compare the results
if(out.size() != in.size())
- cerr << "Image R/W test for type " << fmt << " " << CVD::PNM::type_name<Type>::name() << " size mismatch.\n";
+ cerr << " size mismatch.\n";
else if(!equal(in.begin(), in.end(), out.begin()))
{
- cerr << "Image R/W test for type " << fmt << " " << CVD::PNM::type_name<Type>::name() << " data mismatch.\n";
+ cerr << " data mismatch.\n";
typedef typename Pixel::Component<Type>::type Ct;
double t=0, minval = HUGE_VAL, maxval=-HUGE_VAL;
@@ -264,22 +272,23 @@ template<class T> struct randtest
}
else
- cerr << "Image R/W test for type " << fmt << " " << CVD::PNM::type_name<Type>::name() << " OK.\n";
+ cerr << "OK.\n";
+ }
+ catch(Exceptions::All w)
+ {
+ cerr << w.what << endl;
}
- }
- catch(Exceptions::All w)
- {
- cerr << w.what << endl;
}
- randtest<typename T::Next>::exec(fmt);
+ randtest<typename T::Next>::exec(fmt,p);
}
};
template<> struct randtest<Head>
{
- static void exec(ImageType::ImageType){}
+ static void exec(ImageType::ImageType, const map<string, Parameter<> >& = empty())
+ {}
};
int main(int ac, char** av)
@@ -322,7 +331,7 @@ int main(int ac, char** av)
loadsave_safe<CVD::Rgba<unsigned int> >(av[i]);
}
- cerr << "Testing TEXT (type " << ImageType::BMP << ")\n";
+ cerr << "Testing TEXT (type " << ImageType::TEXT << ")\n";
randtest<
TypeList<double,
TypeList<float,
@@ -333,7 +342,7 @@ int main(int ac, char** av)
TypeList<byte,
TypeList<Rgb<byte>,
Head> > >::exec(ImageType::BMP);
-
+
cerr << "Testing PNM (type " << ImageType::PNM << ")\n";
randtest<
TypeList<byte,
@@ -342,6 +351,16 @@ int main(int ac, char** av)
TypeList<Rgb<unsigned short>,
Head> > > > >::exec(ImageType::PNM);
+ cerr << "Testing PNM (type " << ImageType::PNM << "), text I/O\n";
+ map<string, Parameter<> > p;
+ p["pnm.raw"] = Parameter<bool>(0);
+ randtest<
+ TypeList<byte,
+ TypeList<unsigned short,
+ TypeList<Rgb<byte>,
+ TypeList<Rgb<unsigned short>,
+ Head> > > > >::exec(ImageType::PNM, p);
+
cerr << "Testing FITS (type " << ImageType::FITS << ")\n";
randtest<
TypeList<byte,
Please sign in to comment.
Something went wrong with that request. Please try again.