blob: 033ab4dffdc1843c918c74de822ee0eec1d73402 [file] [log] [blame]
Austin Schuh70cc9552019-01-21 19:46:48 -08001// Ceres Solver - A fast non-linear least squares minimizer
Austin Schuh3de38b02024-06-25 18:25:10 -07002// Copyright 2023 Google Inc. All rights reserved.
Austin Schuh70cc9552019-01-21 19:46:48 -08003// 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
Austin Schuh3de38b02024-06-25 18:25:10 -070046namespace ceres::examples {
Austin Schuh70cc9552019-01-21 19:46:48 -080047
Austin Schuh1d1e6ea2020-12-23 21:56:30 -080048template <typename Real>
Austin Schuh70cc9552019-01-21 19:46:48 -080049class PGMImage {
50 public:
51 // Create an empty image
52 PGMImage(int width, int height);
53 // Load an image from file
54 explicit PGMImage(std::string filename);
55 // Sets an image to a constant
56 void Set(double constant);
57
58 // Reading dimensions
59 int width() const;
60 int height() const;
61 int NumPixels() const;
62
63 // Get individual pixels
64 Real* MutablePixel(int x, int y);
Austin Schuh1d1e6ea2020-12-23 21:56:30 -080065 Real Pixel(int x, int y) const;
Austin Schuh70cc9552019-01-21 19:46:48 -080066 Real* MutablePixelFromLinearIndex(int index);
Austin Schuh1d1e6ea2020-12-23 21:56:30 -080067 Real PixelFromLinearIndex(int index) const;
Austin Schuh70cc9552019-01-21 19:46:48 -080068 int LinearIndex(int x, int y) const;
69
70 // Adds an image to another
71 void operator+=(const PGMImage& image);
72 // Adds a constant to an image
73 void operator+=(Real a);
74 // Multiplies an image by a constant
75 void operator*=(Real a);
76
77 // File access
78 bool WriteToFile(std::string filename) const;
79 bool ReadFromFile(std::string filename);
80
81 // Accessing the image data directly
82 bool SetData(const std::vector<Real>& new_data);
83 const std::vector<Real>& data() const;
84
85 protected:
86 int height_, width_;
87 std::vector<Real> data_;
88};
89
90// --- IMPLEMENTATION
91
Austin Schuh1d1e6ea2020-12-23 21:56:30 -080092template <typename Real>
Austin Schuh70cc9552019-01-21 19:46:48 -080093PGMImage<Real>::PGMImage(int width, int height)
Austin Schuh1d1e6ea2020-12-23 21:56:30 -080094 : height_(height), width_(width), data_(width * height, 0.0) {}
Austin Schuh70cc9552019-01-21 19:46:48 -080095
Austin Schuh1d1e6ea2020-12-23 21:56:30 -080096template <typename Real>
Austin Schuh70cc9552019-01-21 19:46:48 -080097PGMImage<Real>::PGMImage(std::string filename) {
98 if (!ReadFromFile(filename)) {
99 height_ = 0;
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800100 width_ = 0;
Austin Schuh70cc9552019-01-21 19:46:48 -0800101 }
102}
103
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800104template <typename Real>
Austin Schuh70cc9552019-01-21 19:46:48 -0800105void PGMImage<Real>::Set(double constant) {
106 for (int i = 0; i < data_.size(); ++i) {
107 data_[i] = constant;
108 }
109}
110
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800111template <typename Real>
Austin Schuh70cc9552019-01-21 19:46:48 -0800112int PGMImage<Real>::width() const {
113 return width_;
114}
115
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800116template <typename Real>
Austin Schuh70cc9552019-01-21 19:46:48 -0800117int PGMImage<Real>::height() const {
118 return height_;
119}
120
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800121template <typename Real>
Austin Schuh70cc9552019-01-21 19:46:48 -0800122int PGMImage<Real>::NumPixels() const {
123 return width_ * height_;
124}
125
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800126template <typename Real>
Austin Schuh70cc9552019-01-21 19:46:48 -0800127Real* PGMImage<Real>::MutablePixel(int x, int y) {
128 return MutablePixelFromLinearIndex(LinearIndex(x, y));
129}
130
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800131template <typename Real>
Austin Schuh70cc9552019-01-21 19:46:48 -0800132Real PGMImage<Real>::Pixel(int x, int y) const {
133 return PixelFromLinearIndex(LinearIndex(x, y));
134}
135
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800136template <typename Real>
Austin Schuh70cc9552019-01-21 19:46:48 -0800137Real* PGMImage<Real>::MutablePixelFromLinearIndex(int index) {
138 CHECK(index >= 0);
139 CHECK(index < width_ * height_);
140 CHECK(index < data_.size());
141 return &data_[index];
142}
143
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800144template <typename Real>
145Real PGMImage<Real>::PixelFromLinearIndex(int index) const {
Austin Schuh70cc9552019-01-21 19:46:48 -0800146 CHECK(index >= 0);
147 CHECK(index < width_ * height_);
148 CHECK(index < data_.size());
149 return data_[index];
150}
151
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800152template <typename Real>
Austin Schuh70cc9552019-01-21 19:46:48 -0800153int PGMImage<Real>::LinearIndex(int x, int y) const {
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800154 return x + width_ * y;
Austin Schuh70cc9552019-01-21 19:46:48 -0800155}
156
157// Adds an image to another
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800158template <typename Real>
159void PGMImage<Real>::operator+=(const PGMImage<Real>& image) {
Austin Schuh70cc9552019-01-21 19:46:48 -0800160 CHECK(data_.size() == image.data_.size());
161 for (int i = 0; i < data_.size(); ++i) {
162 data_[i] += image.data_[i];
163 }
164}
165
166// Adds a constant to an image
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800167template <typename Real>
168void PGMImage<Real>::operator+=(Real a) {
Austin Schuh70cc9552019-01-21 19:46:48 -0800169 for (int i = 0; i < data_.size(); ++i) {
170 data_[i] += a;
171 }
172}
173
174// Multiplies an image by a constant
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800175template <typename Real>
176void PGMImage<Real>::operator*=(Real a) {
Austin Schuh70cc9552019-01-21 19:46:48 -0800177 for (int i = 0; i < data_.size(); ++i) {
178 data_[i] *= a;
179 }
180}
181
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800182template <typename Real>
Austin Schuh70cc9552019-01-21 19:46:48 -0800183bool PGMImage<Real>::WriteToFile(std::string filename) const {
184 std::ofstream outputfile(filename.c_str());
185 outputfile << "P2" << std::endl;
186 outputfile << "# PGM format" << std::endl;
187 outputfile << " # <width> <height> <levels> " << std::endl;
188 outputfile << " # <data> ... " << std::endl;
189 outputfile << width_ << ' ' << height_ << " 255 " << std::endl;
190
191 // Write data
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800192 int num_pixels = width_ * height_;
Austin Schuh70cc9552019-01-21 19:46:48 -0800193 for (int i = 0; i < num_pixels; ++i) {
194 // Convert to integer by rounding when writing file
195 outputfile << static_cast<int>(data_[i] + 0.5) << ' ';
196 }
197
198 return bool(outputfile); // Returns true/false
199}
200
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800201namespace {
Austin Schuh70cc9552019-01-21 19:46:48 -0800202
203// Helper function to read data from a text file, ignoring "#" comments.
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800204template <typename T>
Austin Schuh70cc9552019-01-21 19:46:48 -0800205bool GetIgnoreComment(std::istream* in, T& t) {
206 std::string word;
207 bool ok;
208 do {
209 ok = true;
210 (*in) >> word;
211 if (word.length() > 0 && word[0] == '#') {
212 // Comment; read the whole line
213 ok = false;
214 std::getline(*in, word);
215 }
216 } while (!ok);
217
218 // Convert the string
219 std::stringstream sin(word);
220 sin >> t;
221
222 // Check for success
223 if (!in || !sin) {
224 return false;
225 }
226 return true;
227}
228} // namespace
229
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800230template <typename Real>
Austin Schuh70cc9552019-01-21 19:46:48 -0800231bool PGMImage<Real>::ReadFromFile(std::string filename) {
232 std::ifstream inputfile(filename.c_str());
233
234 // File must start with "P2"
235 char ch1, ch2;
236 inputfile >> ch1 >> ch2;
237 if (!inputfile || ch1 != 'P' || (ch2 != '2' && ch2 != '5')) {
238 return false;
239 }
240
241 // Read the image header
242 int two_fifty_five;
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800243 if (!GetIgnoreComment(&inputfile, width_) ||
Austin Schuh70cc9552019-01-21 19:46:48 -0800244 !GetIgnoreComment(&inputfile, height_) ||
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800245 !GetIgnoreComment(&inputfile, two_fifty_five)) {
Austin Schuh70cc9552019-01-21 19:46:48 -0800246 return false;
247 }
248 // Assert that the number of grey levels is 255.
249 if (two_fifty_five != 255) {
250 return false;
251 }
252
253 // Now read the data
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800254 int num_pixels = width_ * height_;
Austin Schuh70cc9552019-01-21 19:46:48 -0800255 data_.resize(num_pixels);
256 if (ch2 == '2') {
257 // Ascii file
258 for (int i = 0; i < num_pixels; ++i) {
259 int pixel_data;
260 bool res = GetIgnoreComment(&inputfile, pixel_data);
261 if (!res) {
262 return false;
263 }
264 data_[i] = pixel_data;
265 }
266 // There cannot be anything else in the file (except comments). Try reading
267 // another number and return failure if that succeeded.
268 int temp;
269 bool res = GetIgnoreComment(&inputfile, temp);
270 if (res) {
271 return false;
272 }
273 } else {
274 // Read the line feed character
275 if (inputfile.get() != '\n') {
276 return false;
277 }
278 // Binary file
279 // TODO(strandmark): Will not work on Windows (linebreak conversion).
280 for (int i = 0; i < num_pixels; ++i) {
281 unsigned char pixel_data = inputfile.get();
282 if (!inputfile) {
283 return false;
284 }
285 data_[i] = pixel_data;
286 }
287 // There cannot be anything else in the file. Try reading another byte
288 // and return failure if that succeeded.
289 inputfile.get();
290 if (inputfile) {
291 return false;
292 }
293 }
294
295 return true;
296}
297
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800298template <typename Real>
Austin Schuh70cc9552019-01-21 19:46:48 -0800299bool PGMImage<Real>::SetData(const std::vector<Real>& new_data) {
300 // This function cannot change the dimensions
301 if (new_data.size() != data_.size()) {
302 return false;
303 }
304 std::copy(new_data.begin(), new_data.end(), data_.begin());
305 return true;
306}
307
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800308template <typename Real>
Austin Schuh70cc9552019-01-21 19:46:48 -0800309const std::vector<Real>& PGMImage<Real>::data() const {
310 return data_;
311}
312
Austin Schuh3de38b02024-06-25 18:25:10 -0700313} // namespace ceres::examples
Austin Schuh70cc9552019-01-21 19:46:48 -0800314
Austin Schuh70cc9552019-01-21 19:46:48 -0800315#endif // CERES_EXAMPLES_PGM_IMAGE_H_