Home | History | Annotate | Download | only in examples
      1 // Ceres Solver - A fast non-linear least squares minimizer
      2 // Copyright 2012 Google Inc. All rights reserved.
      3 // http://code.google.com/p/ceres-solver/
      4 //
      5 // Redistribution and use in source and binary forms, with or without
      6 // modification, are permitted provided that the following conditions are met:
      7 //
      8 // * Redistributions of source code must retain the above copyright notice,
      9 //   this list of conditions and the following disclaimer.
     10 // * Redistributions in binary form must reproduce the above copyright notice,
     11 //   this list of conditions and the following disclaimer in the documentation
     12 //   and/or other materials provided with the distribution.
     13 // * Neither the name of Google Inc. nor the names of its contributors may be
     14 //   used to endorse or promote products derived from this software without
     15 //   specific prior written permission.
     16 //
     17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
     18 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     19 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     20 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
     21 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     22 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     23 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     24 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     25 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     26 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     27 // POSSIBILITY OF SUCH DAMAGE.
     28 //
     29 // Author: strandmark (at) google.com (Petter Strandmark)
     30 //
     31 // Simple class for accessing PGM images.
     32 
     33 #ifndef CERES_EXAMPLES_PGM_IMAGE_H_
     34 #define CERES_EXAMPLES_PGM_IMAGE_H_
     35 
     36 #include <algorithm>
     37 #include <cstring>
     38 #include <fstream>
     39 #include <iostream>
     40 #include <sstream>
     41 #include <string>
     42 #include <vector>
     43 
     44 #include "glog/logging.h"
     45 
     46 namespace ceres {
     47 namespace examples {
     48 
     49 template<typename Real>
     50 class PGMImage {
     51  public:
     52   // Create an empty image
     53   PGMImage(int width, int height);
     54   // Load an image from file
     55   explicit PGMImage(std::string filename);
     56   // Sets an image to a constant
     57   void Set(double constant);
     58 
     59   // Reading dimensions
     60   int width() const;
     61   int height() const;
     62   int NumPixels() const;
     63 
     64   // Get individual pixels
     65   Real* MutablePixel(int x, int y);
     66   Real  Pixel(int x, int y) const;
     67   Real* MutablePixelFromLinearIndex(int index);
     68   Real  PixelFromLinearIndex(int index) const;
     69   int LinearIndex(int x, int y) const;
     70 
     71   // Adds an image to another
     72   void operator+=(const PGMImage& image);
     73   // Adds a constant to an image
     74   void operator+=(Real a);
     75   // Multiplies an image by a constant
     76   void operator*=(Real a);
     77 
     78   // File access
     79   bool WriteToFile(std::string filename) const;
     80   bool ReadFromFile(std::string filename);
     81 
     82   // Accessing the image data directly
     83   bool SetData(const std::vector<Real>& new_data);
     84   const std::vector<Real>& data() const;
     85 
     86  protected:
     87   int height_, width_;
     88   std::vector<Real> data_;
     89 };
     90 
     91 // --- IMPLEMENTATION
     92 
     93 template<typename Real>
     94 PGMImage<Real>::PGMImage(int width, int height)
     95   : height_(height), width_(width), data_(width*height, 0.0) {
     96 }
     97 
     98 template<typename Real>
     99 PGMImage<Real>::PGMImage(std::string filename) {
    100   if (!ReadFromFile(filename)) {
    101     height_ = 0;
    102     width_  = 0;
    103   }
    104 }
    105 
    106 template<typename Real>
    107 void PGMImage<Real>::Set(double constant) {
    108   for (int i = 0; i < data_.size(); ++i) {
    109     data_[i] = constant;
    110   }
    111 }
    112 
    113 template<typename Real>
    114 int PGMImage<Real>::width() const {
    115   return width_;
    116 }
    117 
    118 template<typename Real>
    119 int PGMImage<Real>::height() const {
    120   return height_;
    121 }
    122 
    123 template<typename Real>
    124 int PGMImage<Real>::NumPixels() const {
    125   return width_ * height_;
    126 }
    127 
    128 template<typename Real>
    129 Real* PGMImage<Real>::MutablePixel(int x, int y) {
    130   return MutablePixelFromLinearIndex(LinearIndex(x, y));
    131 }
    132 
    133 template<typename Real>
    134 Real PGMImage<Real>::Pixel(int x, int y) const {
    135   return PixelFromLinearIndex(LinearIndex(x, y));
    136 }
    137 
    138 template<typename Real>
    139 Real* PGMImage<Real>::MutablePixelFromLinearIndex(int index) {
    140   CHECK(index >= 0);
    141   CHECK(index < width_ * height_);
    142   CHECK(index < data_.size());
    143   return &data_[index];
    144 }
    145 
    146 template<typename Real>
    147 Real  PGMImage<Real>::PixelFromLinearIndex(int index) const {
    148   CHECK(index >= 0);
    149   CHECK(index < width_ * height_);
    150   CHECK(index < data_.size());
    151   return data_[index];
    152 }
    153 
    154 template<typename Real>
    155 int PGMImage<Real>::LinearIndex(int x, int y) const {
    156   return x + width_*y;
    157 }
    158 
    159 // Adds an image to another
    160 template<typename Real>
    161 void PGMImage<Real>::operator+= (const PGMImage<Real>& image) {
    162   CHECK(data_.size() == image.data_.size());
    163   for (int i = 0; i < data_.size(); ++i) {
    164     data_[i] += image.data_[i];
    165   }
    166 }
    167 
    168 // Adds a constant to an image
    169 template<typename Real>
    170 void PGMImage<Real>::operator+= (Real a) {
    171   for (int i = 0; i < data_.size(); ++i) {
    172     data_[i] += a;
    173   }
    174 }
    175 
    176 // Multiplies an image by a constant
    177 template<typename Real>
    178 void PGMImage<Real>::operator*= (Real a) {
    179   for (int i = 0; i < data_.size(); ++i) {
    180     data_[i] *= a;
    181   }
    182 }
    183 
    184 template<typename Real>
    185 bool PGMImage<Real>::WriteToFile(std::string filename) const {
    186   std::ofstream outputfile(filename.c_str());
    187   outputfile << "P2" << std::endl;
    188   outputfile << "# PGM format" << std::endl;
    189   outputfile << " # <width> <height> <levels> " << std::endl;
    190   outputfile << " # <data> ... " << std::endl;
    191   outputfile << width_ << ' ' << height_ << " 255 " << std::endl;
    192 
    193   // Write data
    194   int num_pixels = width_*height_;
    195   for (int i = 0; i < num_pixels; ++i) {
    196     // Convert to integer by rounding when writing file
    197     outputfile << static_cast<int>(data_[i] + 0.5) << ' ';
    198   }
    199 
    200   return bool(outputfile);  // Returns true/false
    201 }
    202 
    203 namespace  {
    204 
    205 // Helper function to read data from a text file, ignoring "#" comments.
    206 template<typename T>
    207 bool GetIgnoreComment(std::istream* in, T& t) {
    208   std::string word;
    209   bool ok;
    210   do {
    211     ok = true;
    212     (*in) >> word;
    213     if (word.length() > 0 && word[0] == '#') {
    214       // Comment; read the whole line
    215       ok = false;
    216       std::getline(*in, word);
    217     }
    218   } while (!ok);
    219 
    220   // Convert the string
    221   std::stringstream sin(word);
    222   sin >> t;
    223 
    224   // Check for success
    225   if (!in || !sin) {
    226     return false;
    227   }
    228   return true;
    229 }
    230 }  // namespace
    231 
    232 template<typename Real>
    233 bool PGMImage<Real>::ReadFromFile(std::string filename) {
    234   std::ifstream inputfile(filename.c_str());
    235 
    236   // File must start with "P2"
    237   char ch1, ch2;
    238   inputfile >> ch1 >> ch2;
    239   if (!inputfile || ch1 != 'P' || (ch2 != '2' && ch2 != '5')) {
    240     return false;
    241   }
    242 
    243   // Read the image header
    244   int two_fifty_five;
    245   if (!GetIgnoreComment(&inputfile, width_)  ||
    246       !GetIgnoreComment(&inputfile, height_) ||
    247       !GetIgnoreComment(&inputfile, two_fifty_five) ) {
    248     return false;
    249   }
    250   // Assert that the number of grey levels is 255.
    251   if (two_fifty_five != 255) {
    252     return false;
    253   }
    254 
    255   // Now read the data
    256   int num_pixels = width_*height_;
    257   data_.resize(num_pixels);
    258   if (ch2 == '2') {
    259     // Ascii file
    260     for (int i = 0; i < num_pixels; ++i) {
    261       int pixel_data;
    262       bool res = GetIgnoreComment(&inputfile, pixel_data);
    263       if (!res) {
    264         return false;
    265       }
    266       data_[i] = pixel_data;
    267     }
    268     // There cannot be anything else in the file (except comments). Try reading
    269     // another number and return failure if that succeeded.
    270     int temp;
    271     bool res = GetIgnoreComment(&inputfile, temp);
    272     if (res) {
    273       return false;
    274     }
    275   } else {
    276     // Read the line feed character
    277     if (inputfile.get() != '\n') {
    278       return false;
    279     }
    280     // Binary file
    281     // TODO(strandmark): Will not work on Windows (linebreak conversion).
    282     for (int i = 0; i < num_pixels; ++i) {
    283       unsigned char pixel_data = inputfile.get();
    284       if (!inputfile) {
    285         return false;
    286       }
    287       data_[i] = pixel_data;
    288     }
    289     // There cannot be anything else in the file. Try reading another byte
    290     // and return failure if that succeeded.
    291     inputfile.get();
    292     if (inputfile) {
    293       return false;
    294     }
    295   }
    296 
    297   return true;
    298 }
    299 
    300 template<typename Real>
    301 bool PGMImage<Real>::SetData(const std::vector<Real>& new_data) {
    302   // This function cannot change the dimensions
    303   if (new_data.size() != data_.size()) {
    304     return false;
    305   }
    306   std::copy(new_data.begin(), new_data.end(), data_.begin());
    307   return true;
    308 }
    309 
    310 template<typename Real>
    311 const std::vector<Real>& PGMImage<Real>::data() const {
    312   return data_;
    313 }
    314 
    315 }  // namespace examples
    316 }  // namespace ceres
    317 
    318 
    319 #endif  // CERES_EXAMPLES_PGM_IMAGE_H_
    320