blob: 3d2df63445c3a1484896f2735c377d9ff570f3fa [file] [log] [blame]
Austin Schuh70cc9552019-01-21 19:46:48 -08001// Ceres Solver - A fast non-linear least squares minimizer
2// Copyright 2015 Google Inc. All rights reserved.
3// http://ceres-solver.org/
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
Austin Schuh1d1e6ea2020-12-23 21:56:30 -080049template <typename Real>
Austin Schuh70cc9552019-01-21 19:46:48 -080050class 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);
Austin Schuh1d1e6ea2020-12-23 21:56:30 -080066 Real Pixel(int x, int y) const;
Austin Schuh70cc9552019-01-21 19:46:48 -080067 Real* MutablePixelFromLinearIndex(int index);
Austin Schuh1d1e6ea2020-12-23 21:56:30 -080068 Real PixelFromLinearIndex(int index) const;
Austin Schuh70cc9552019-01-21 19:46:48 -080069 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
Austin Schuh1d1e6ea2020-12-23 21:56:30 -080093template <typename Real>
Austin Schuh70cc9552019-01-21 19:46:48 -080094PGMImage<Real>::PGMImage(int width, int height)
Austin Schuh1d1e6ea2020-12-23 21:56:30 -080095 : height_(height), width_(width), data_(width * height, 0.0) {}
Austin Schuh70cc9552019-01-21 19:46:48 -080096
Austin Schuh1d1e6ea2020-12-23 21:56:30 -080097template <typename Real>
Austin Schuh70cc9552019-01-21 19:46:48 -080098PGMImage<Real>::PGMImage(std::string filename) {
99 if (!ReadFromFile(filename)) {
100 height_ = 0;
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800101 width_ = 0;
Austin Schuh70cc9552019-01-21 19:46:48 -0800102 }
103}
104
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800105template <typename Real>
Austin Schuh70cc9552019-01-21 19:46:48 -0800106void PGMImage<Real>::Set(double constant) {
107 for (int i = 0; i < data_.size(); ++i) {
108 data_[i] = constant;
109 }
110}
111
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800112template <typename Real>
Austin Schuh70cc9552019-01-21 19:46:48 -0800113int PGMImage<Real>::width() const {
114 return width_;
115}
116
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800117template <typename Real>
Austin Schuh70cc9552019-01-21 19:46:48 -0800118int PGMImage<Real>::height() const {
119 return height_;
120}
121
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800122template <typename Real>
Austin Schuh70cc9552019-01-21 19:46:48 -0800123int PGMImage<Real>::NumPixels() const {
124 return width_ * height_;
125}
126
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800127template <typename Real>
Austin Schuh70cc9552019-01-21 19:46:48 -0800128Real* PGMImage<Real>::MutablePixel(int x, int y) {
129 return MutablePixelFromLinearIndex(LinearIndex(x, y));
130}
131
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800132template <typename Real>
Austin Schuh70cc9552019-01-21 19:46:48 -0800133Real PGMImage<Real>::Pixel(int x, int y) const {
134 return PixelFromLinearIndex(LinearIndex(x, y));
135}
136
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800137template <typename Real>
Austin Schuh70cc9552019-01-21 19:46:48 -0800138Real* PGMImage<Real>::MutablePixelFromLinearIndex(int index) {
139 CHECK(index >= 0);
140 CHECK(index < width_ * height_);
141 CHECK(index < data_.size());
142 return &data_[index];
143}
144
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800145template <typename Real>
146Real PGMImage<Real>::PixelFromLinearIndex(int index) const {
Austin Schuh70cc9552019-01-21 19:46:48 -0800147 CHECK(index >= 0);
148 CHECK(index < width_ * height_);
149 CHECK(index < data_.size());
150 return data_[index];
151}
152
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800153template <typename Real>
Austin Schuh70cc9552019-01-21 19:46:48 -0800154int PGMImage<Real>::LinearIndex(int x, int y) const {
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800155 return x + width_ * y;
Austin Schuh70cc9552019-01-21 19:46:48 -0800156}
157
158// Adds an image to another
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800159template <typename Real>
160void PGMImage<Real>::operator+=(const PGMImage<Real>& image) {
Austin Schuh70cc9552019-01-21 19:46:48 -0800161 CHECK(data_.size() == image.data_.size());
162 for (int i = 0; i < data_.size(); ++i) {
163 data_[i] += image.data_[i];
164 }
165}
166
167// Adds a constant to an image
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800168template <typename Real>
169void PGMImage<Real>::operator+=(Real a) {
Austin Schuh70cc9552019-01-21 19:46:48 -0800170 for (int i = 0; i < data_.size(); ++i) {
171 data_[i] += a;
172 }
173}
174
175// Multiplies an image by a constant
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800176template <typename Real>
177void PGMImage<Real>::operator*=(Real a) {
Austin Schuh70cc9552019-01-21 19:46:48 -0800178 for (int i = 0; i < data_.size(); ++i) {
179 data_[i] *= a;
180 }
181}
182
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800183template <typename Real>
Austin Schuh70cc9552019-01-21 19:46:48 -0800184bool PGMImage<Real>::WriteToFile(std::string filename) const {
185 std::ofstream outputfile(filename.c_str());
186 outputfile << "P2" << std::endl;
187 outputfile << "# PGM format" << std::endl;
188 outputfile << " # <width> <height> <levels> " << std::endl;
189 outputfile << " # <data> ... " << std::endl;
190 outputfile << width_ << ' ' << height_ << " 255 " << std::endl;
191
192 // Write data
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800193 int num_pixels = width_ * height_;
Austin Schuh70cc9552019-01-21 19:46:48 -0800194 for (int i = 0; i < num_pixels; ++i) {
195 // Convert to integer by rounding when writing file
196 outputfile << static_cast<int>(data_[i] + 0.5) << ' ';
197 }
198
199 return bool(outputfile); // Returns true/false
200}
201
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800202namespace {
Austin Schuh70cc9552019-01-21 19:46:48 -0800203
204// Helper function to read data from a text file, ignoring "#" comments.
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800205template <typename T>
Austin Schuh70cc9552019-01-21 19:46:48 -0800206bool GetIgnoreComment(std::istream* in, T& t) {
207 std::string word;
208 bool ok;
209 do {
210 ok = true;
211 (*in) >> word;
212 if (word.length() > 0 && word[0] == '#') {
213 // Comment; read the whole line
214 ok = false;
215 std::getline(*in, word);
216 }
217 } while (!ok);
218
219 // Convert the string
220 std::stringstream sin(word);
221 sin >> t;
222
223 // Check for success
224 if (!in || !sin) {
225 return false;
226 }
227 return true;
228}
229} // namespace
230
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800231template <typename Real>
Austin Schuh70cc9552019-01-21 19:46:48 -0800232bool PGMImage<Real>::ReadFromFile(std::string filename) {
233 std::ifstream inputfile(filename.c_str());
234
235 // File must start with "P2"
236 char ch1, ch2;
237 inputfile >> ch1 >> ch2;
238 if (!inputfile || ch1 != 'P' || (ch2 != '2' && ch2 != '5')) {
239 return false;
240 }
241
242 // Read the image header
243 int two_fifty_five;
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800244 if (!GetIgnoreComment(&inputfile, width_) ||
Austin Schuh70cc9552019-01-21 19:46:48 -0800245 !GetIgnoreComment(&inputfile, height_) ||
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800246 !GetIgnoreComment(&inputfile, two_fifty_five)) {
Austin Schuh70cc9552019-01-21 19:46:48 -0800247 return false;
248 }
249 // Assert that the number of grey levels is 255.
250 if (two_fifty_five != 255) {
251 return false;
252 }
253
254 // Now read the data
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800255 int num_pixels = width_ * height_;
Austin Schuh70cc9552019-01-21 19:46:48 -0800256 data_.resize(num_pixels);
257 if (ch2 == '2') {
258 // Ascii file
259 for (int i = 0; i < num_pixels; ++i) {
260 int pixel_data;
261 bool res = GetIgnoreComment(&inputfile, pixel_data);
262 if (!res) {
263 return false;
264 }
265 data_[i] = pixel_data;
266 }
267 // There cannot be anything else in the file (except comments). Try reading
268 // another number and return failure if that succeeded.
269 int temp;
270 bool res = GetIgnoreComment(&inputfile, temp);
271 if (res) {
272 return false;
273 }
274 } else {
275 // Read the line feed character
276 if (inputfile.get() != '\n') {
277 return false;
278 }
279 // Binary file
280 // TODO(strandmark): Will not work on Windows (linebreak conversion).
281 for (int i = 0; i < num_pixels; ++i) {
282 unsigned char pixel_data = inputfile.get();
283 if (!inputfile) {
284 return false;
285 }
286 data_[i] = pixel_data;
287 }
288 // There cannot be anything else in the file. Try reading another byte
289 // and return failure if that succeeded.
290 inputfile.get();
291 if (inputfile) {
292 return false;
293 }
294 }
295
296 return true;
297}
298
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800299template <typename Real>
Austin Schuh70cc9552019-01-21 19:46:48 -0800300bool PGMImage<Real>::SetData(const std::vector<Real>& new_data) {
301 // This function cannot change the dimensions
302 if (new_data.size() != data_.size()) {
303 return false;
304 }
305 std::copy(new_data.begin(), new_data.end(), data_.begin());
306 return true;
307}
308
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800309template <typename Real>
Austin Schuh70cc9552019-01-21 19:46:48 -0800310const std::vector<Real>& PGMImage<Real>::data() const {
311 return data_;
312}
313
314} // namespace examples
315} // namespace ceres
316
Austin Schuh70cc9552019-01-21 19:46:48 -0800317#endif // CERES_EXAMPLES_PGM_IMAGE_H_