/* * imageio.cpp Copyright (C) 2009-2010 Paolo Medici * * 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. * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include <fstream> #include <iostream> #include <string.h> /** color format */ enum ColorFormat { AUTO, ///< reserved GREY8, ///< 1 BYTE per PIXEL RGB24 ///< 3 BYTE per PIXEL }; /** Image class */ class Image { /** The image format */ ColorFormat m_format; /** The Image Geomtry */ unsigned int m_width, m_height; /** Image stride */ long m_stride; /** The inner buffer */ unsigned char *m_buffer; private: static void pnm_skip_comments(std::istream & i) { while (isspace(i.peek())) { while (isspace(i.peek())) i.get(); if (i.peek() == '#') while (i.peek()!='\r' && i.peek()!='\n') { i.get(); } } } static char pnm_read_header(std::istream &iss, unsigned int & width, unsigned int &height) { char h, t; int max_val; // check pnm header h = iss.get(); t = iss.get(); if(!((h=='P') && ((t=='5')||(t=='6')))) return '\0'; pnm_skip_comments(iss); iss>>width; pnm_skip_comments(iss); iss>>height; pnm_skip_comments(iss); iss>>max_val; iss.get(); // TODO: use a getline fn return t; } /// private costructor to avoid copy Image(const Image & prv) { } public: Image() : m_width(0), m_height(0), m_buffer(0) { } ~Image() { delete [] m_buffer; } /// destroy image and realloc buffer bool alloc(unsigned int width, unsigned int height, ColorFormat format) { delete [] m_buffer; // buffer size m_width = width; m_height = height; m_format = format; m_stride = m_width * bytes_per_pixel(); m_buffer = new unsigned char [m_stride * m_height]; } /** return the bytes per pixel */ inline unsigned int bytes_per_pixel() const { return (m_format==GREY8)?1:(m_format==RGB24)?3:0; } /** return the image size in bytes */ inline unsigned int memsize() const { return m_stride * m_height; } /** return the image size in pixels */ inline unsigned int size() const { return m_width * m_height; } /** image width */ inline unsigned int width() const { return m_width; } /** image height */ inline unsigned int height() const { return m_height; } /** color format */ inline ColorFormat format() const { return m_format; } /** data access */ inline const unsigned char *data() const { return m_buffer; } inline unsigned char *data() { return m_buffer; } /** data access */ inline unsigned char operator [] (int i) const { return m_buffer[i]; } inline unsigned char & operator [] (int i) { return m_buffer[i]; } /** data access (column, row) */ inline unsigned char operator () (int i, int j) const { return m_buffer[i + j * m_stride ]; } inline unsigned char & operator () (int i, int j) { return m_buffer[i + j * m_stride ]; } /// Load a PGM/PPM image, reserve memory with new and return geometry /// @param [in] file filename /// @return true if image is loaded correctly bool load(const char *file, ColorFormat format = AUTO ) { std::ifstream istr(file); if(!istr) { std::cerr << "cannot access " << file << std::endl; return false; } unsigned int width, height; char header = pnm_read_header(istr, width, height); if(header != '5' && header != '6') { std::cerr << file << " is not a PNM RawBits file: Format " << header << " and only 5 or 6 are allowed" << std::endl; return false; } if(header=='5' && ( (format == GREY8) || (format == AUTO) ) ) { alloc(width, height, GREY8); istr.read(reinterpret_cast<char *>(m_buffer), m_stride * m_height); return true; } else if(header=='6' && ( (format == RGB24) || (format == AUTO) ) ) { alloc(width, height, RGB24); istr.read(reinterpret_cast<char *>(m_buffer), m_stride * m_height); return true; } else return false; } /** Write a PGM/PPM file * @param filename a file * @return true if file is created */ bool save(const char *filename) const { if(m_format == RGB24) { std::ofstream out(filename, std::ios::out | std::ios::binary); if(out) { out << "P6\n" << m_width << ' ' << m_height << "\n255\n"; out.write(reinterpret_cast<const char *>(m_buffer), m_width*m_height*3); return true; } else return false; } else if(m_format == GREY8) { std::ofstream out(filename, std::ios::out | std::ios::binary); if(out) { out << "P5\n" << m_width << ' ' << m_height << "\n255\n"; out.write(reinterpret_cast<const char *>(m_buffer), m_width*m_height); return true; } else return false; } } }; ////////////////////////////// Example of uses ////////////////////////// ///////////////////////////////////////////////////// PM DEBAYER ////////////////////////////////// struct pBGGR; struct pGRBG; struct pGBRG; struct pRGGB; struct pBGGR { static unsigned char R(const unsigned char *src, unsigned int w) { return src[w+1]; } static unsigned char G1(const unsigned char *src, unsigned int w) { return src[1]; } static unsigned char G2(const unsigned char *src, unsigned int w) { return src[w]; } static unsigned char B(const unsigned char *src, unsigned int w) { return src[0]; } typedef pGBRG horz; typedef pGRBG vert; }; struct pGBRG { static unsigned char R(const unsigned char *src, unsigned int w) { return src[w]; } static unsigned char G1(const unsigned char *src, unsigned int w) { return src[0]; } static unsigned char G2(const unsigned char *src, unsigned int w) { return src[w+1]; } static unsigned char B(const unsigned char *src, unsigned int w) { return src[1]; } typedef pBGGR horz; typedef pRGGB vert; }; struct pGRBG { static unsigned char R(const unsigned char *src, unsigned int w) { return src[1]; } static unsigned char G1(const unsigned char *src, unsigned int w) { return src[0]; } static unsigned char G2(const unsigned char *src, unsigned int w) { return src[w+1]; } static unsigned char B(const unsigned char *src, unsigned int w) { return src[w]; } typedef pRGGB horz; typedef pBGGR vert; }; struct pRGGB { static unsigned char R(const unsigned char *src, unsigned int w) { return src[0]; } static unsigned char G1(const unsigned char *src, unsigned int w) { return src[1]; } static unsigned char G2(const unsigned char *src, unsigned int w) { return src[w]; } static unsigned char B(const unsigned char *src, unsigned int w) { return src[w+1]; } typedef pGRBG horz; typedef pGBRG vert; }; // PM Bayer Simple! (Because Simply is the best) template<class T> void bayer_simple(const unsigned char *src, unsigned char *dst, unsigned int w, unsigned int h) { unsigned int i,j; T a; typename T::horz b; typename T::vert c; typename T::horz::vert d; for (i=0;i<h-2;i+=2) { for (j=0;j<w-2;j+=2) { dst[0] = a.R(src,w); dst[1] = (a.G1(src,w) + a.G2(src,w))>>1; dst[2] = a.B(src,w); dst +=3; src++; dst[0] = b.R(src,w); dst[1] = (b.G1(src,w) + b.G2(src,w))>>1; dst[2] = b.B(src,w); dst +=3; src++; } // FIX: ultima colonna dst[3]=dst[0] = a.R(src,w); dst[4]=dst[1] = (a.G1(src,w) + a.G2(src,w))>>1; dst[5]=dst[2] = a.B(src,w); dst += 6; src += 2; for (j=0;j<w-2;j+=2) { dst[0] = c.R(src,w); dst[1] = (c.G1(src,w) + c.G2(src,w))>>1; dst[2] = c.B(src,w); dst +=3; src++; dst[0] = d.R(src,w); dst[1] = (d.G1(src,w) + d.G2(src,w))>>1; dst[2] = d.B(src,w); dst +=3; src++; } // FIX: ultima colonna dst[3]=dst[0] = c.R(src,w); dst[4]=dst[1] = (c.G1(src,w) + c.G2(src,w))>>1; dst[5]=dst[2] = c.B(src,w); dst += 6; src += 2; } // FIX: ultima riga! for (j=0;j<w-2;j+=2) { dst[w] = dst[0] = a.R(src,w); dst[w+1] = dst[1] = (a.G1(src,w) + a.G2(src,w))>>1; dst[w+2] = dst[2] = a.B(src,w); dst +=3; src++; dst[w] = dst[0] = b.R(src,w); dst[w+1] = dst[1] = (b.G1(src,w) + b.G2(src,w))>>1; dst[w+2] = dst[2] = b.B(src,w); dst +=3; src++; } // FIx: ultima riga, ultima colonna: dst[w+3]=dst[w]=dst[3]=dst[0] = a.R(src,w); dst[w+4]=dst[w+1]=dst[4]=dst[1] = (a.G1(src,w) + a.G2(src,w))>>1; dst[w+5]=dst[w+2]=dst[5]=dst[2] = a.B(src,w); } int main(int argc, char *argv[]) { if(argc == 4) { Image img; if(!img.load(argv[1])) std::cerr << "cannot load image" << std::endl; else { Image imgRGB; imgRGB.alloc(img.width(), img.height(), RGB24); if(!strcmp(argv[3], "RGGB") ) bayer_simple<pRGGB>(img.data(), imgRGB.data(), img.width(), img.height()); else if(!strcmp(argv[3], "BGGR") ) bayer_simple<pBGGR>(img.data(), imgRGB.data(), img.width(), img.height()); else if(!strcmp(argv[3], "GRBG") ) bayer_simple<pGRBG>(img.data(), imgRGB.data(), img.width(), img.height()); else if(!strcmp(argv[3], "GBRG") ) bayer_simple<pGBRG>(img.data(), imgRGB.data(), img.width(), img.height()); imgRGB.save(argv[2]); } } else std::cout << "debayer <input pgm image> <output ppm image> <bayer pattern>" << std::endl; return 0; }