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