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@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 46namespace ceres { 47namespace examples { 48 49template<typename Real> 50class 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 93template<typename Real> 94PGMImage<Real>::PGMImage(int width, int height) 95 : height_(height), width_(width), data_(width*height, 0.0) { 96} 97 98template<typename Real> 99PGMImage<Real>::PGMImage(std::string filename) { 100 if (!ReadFromFile(filename)) { 101 height_ = 0; 102 width_ = 0; 103 } 104} 105 106template<typename Real> 107void PGMImage<Real>::Set(double constant) { 108 for (int i = 0; i < data_.size(); ++i) { 109 data_[i] = constant; 110 } 111} 112 113template<typename Real> 114int PGMImage<Real>::width() const { 115 return width_; 116} 117 118template<typename Real> 119int PGMImage<Real>::height() const { 120 return height_; 121} 122 123template<typename Real> 124int PGMImage<Real>::NumPixels() const { 125 return width_ * height_; 126} 127 128template<typename Real> 129Real* PGMImage<Real>::MutablePixel(int x, int y) { 130 return MutablePixelFromLinearIndex(LinearIndex(x, y)); 131} 132 133template<typename Real> 134Real PGMImage<Real>::Pixel(int x, int y) const { 135 return PixelFromLinearIndex(LinearIndex(x, y)); 136} 137 138template<typename Real> 139Real* 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 146template<typename Real> 147Real 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 154template<typename Real> 155int PGMImage<Real>::LinearIndex(int x, int y) const { 156 return x + width_*y; 157} 158 159// Adds an image to another 160template<typename Real> 161void 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 169template<typename Real> 170void 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 177template<typename Real> 178void PGMImage<Real>::operator*= (Real a) { 179 for (int i = 0; i < data_.size(); ++i) { 180 data_[i] *= a; 181 } 182} 183 184template<typename Real> 185bool 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 203namespace { 204 205// Helper function to read data from a text file, ignoring "#" comments. 206template<typename T> 207bool 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 232template<typename Real> 233bool 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 300template<typename Real> 301bool 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 310template<typename Real> 311const 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