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 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