1 // Copyright (c) 2018, ETH Zurich and UNC Chapel Hill.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are met:
6 //
7 // * Redistributions of source code must retain the above copyright
8 // notice, this list of conditions and the following disclaimer.
9 //
10 // * Redistributions in binary form must reproduce the above copyright
11 // notice, this list of conditions and the following disclaimer in the
12 // documentation and/or other materials provided with the distribution.
13 //
14 // * Neither the name of ETH Zurich and UNC Chapel Hill nor the names of
15 // its contributors may be used to endorse or promote products derived
16 // from this software without specific prior written permission.
17 //
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
22 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 // POSSIBILITY OF SUCH DAMAGE.
29 //
30 // Author: Johannes L. Schoenberger (jsch-at-demuc-dot-de)
31
32 #include "util/bitmap.h"
33
34 #include <unordered_map>
35
36 #include <boost/filesystem/operations.hpp>
37 #include <boost/regex.hpp>
38
39 #include "base/camera_database.h"
40 #include "VLFeat/imopv.h"
41 #include "util/logging.h"
42 #include "util/math.h"
43 #include "util/misc.h"
44
45 namespace colmap {
46
Bitmap()47 Bitmap::Bitmap()
48 : data_(nullptr, &FreeImage_Unload), width_(0), height_(0), channels_(0) {}
49
Bitmap(const Bitmap & other)50 Bitmap::Bitmap(const Bitmap& other) : Bitmap() {
51 if (other.data_) {
52 SetPtr(FreeImage_Clone(other.data_.get()));
53 }
54 }
55
Bitmap(Bitmap && other)56 Bitmap::Bitmap(Bitmap&& other) : Bitmap() {
57 data_ = std::move(other.data_);
58 width_ = other.width_;
59 height_ = other.height_;
60 channels_ = other.channels_;
61 }
62
Bitmap(FIBITMAP * data)63 Bitmap::Bitmap(FIBITMAP* data) : Bitmap() { SetPtr(data); }
64
operator =(const Bitmap & other)65 Bitmap& Bitmap::operator=(const Bitmap& other) {
66 if (other.data_) {
67 SetPtr(FreeImage_Clone(other.data_.get()));
68 }
69 return *this;
70 }
71
operator =(Bitmap && other)72 Bitmap& Bitmap::operator=(Bitmap&& other) {
73 if (this != &other) {
74 data_ = std::move(other.data_);
75 width_ = other.width_;
76 height_ = other.height_;
77 channels_ = other.channels_;
78 }
79 return *this;
80 }
81
Allocate(const int width,const int height,const bool as_rgb)82 bool Bitmap::Allocate(const int width, const int height, const bool as_rgb) {
83 FIBITMAP* data = nullptr;
84 width_ = width;
85 height_ = height;
86 if (as_rgb) {
87 const int kNumBitsPerPixel = 24;
88 data = FreeImage_Allocate(width, height, kNumBitsPerPixel);
89 channels_ = 3;
90 } else {
91 const int kNumBitsPerPixel = 8;
92 data = FreeImage_Allocate(width, height, kNumBitsPerPixel);
93 channels_ = 1;
94 }
95 data_ = FIBitmapPtr(data, &FreeImage_Unload);
96 return data != nullptr;
97 }
98
Deallocate()99 void Bitmap::Deallocate() {
100 data_.reset();
101 width_ = 0;
102 height_ = 0;
103 channels_ = 0;
104 }
105
NumBytes() const106 size_t Bitmap::NumBytes() const {
107 if (data_) {
108 return ScanWidth() * height_;
109 } else {
110 return 0;
111 }
112 }
113
ConvertToRawBits() const114 std::vector<uint8_t> Bitmap::ConvertToRawBits() const {
115 const unsigned int scan_width = ScanWidth();
116 const unsigned int bpp = BitsPerPixel();
117 const bool kTopDown = true;
118 std::vector<uint8_t> raw_bits(scan_width * height_, 0);
119 FreeImage_ConvertToRawBits(raw_bits.data(), data_.get(), scan_width, bpp,
120 FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK,
121 FI_RGBA_BLUE_MASK, kTopDown);
122 return raw_bits;
123 }
124
ConvertToRowMajorArray() const125 std::vector<uint8_t> Bitmap::ConvertToRowMajorArray() const {
126 std::vector<uint8_t> array(width_ * height_ * channels_);
127 size_t i = 0;
128 for (int y = 0; y < height_; ++y) {
129 const uint8_t* line = FreeImage_GetScanLine(data_.get(), height_ - 1 - y);
130 for (int x = 0; x < width_; ++x) {
131 for (int d = 0; d < channels_; ++d) {
132 array[i] = line[x * channels_ + d];
133 i += 1;
134 }
135 }
136 }
137 return array;
138 }
139
ConvertToColMajorArray() const140 std::vector<uint8_t> Bitmap::ConvertToColMajorArray() const {
141 std::vector<uint8_t> array(width_ * height_ * channels_);
142 size_t i = 0;
143 for (int d = 0; d < channels_; ++d) {
144 for (int x = 0; x < width_; ++x) {
145 for (int y = 0; y < height_; ++y) {
146 const uint8_t* line =
147 FreeImage_GetScanLine(data_.get(), height_ - 1 - y);
148 array[i] = line[x * channels_ + d];
149 i += 1;
150 }
151 }
152 }
153 return array;
154 }
155
GetPixel(const int x,const int y,BitmapColor<uint8_t> * color) const156 bool Bitmap::GetPixel(const int x, const int y,
157 BitmapColor<uint8_t>* color) const {
158 if (x < 0 || x >= width_ || y < 0 || y >= height_) {
159 return false;
160 }
161
162 const uint8_t* line = FreeImage_GetScanLine(data_.get(), height_ - 1 - y);
163
164 if (IsGrey()) {
165 color->r = line[x];
166 return true;
167 } else if (IsRGB()) {
168 color->r = line[3 * x + FI_RGBA_RED];
169 color->g = line[3 * x + FI_RGBA_GREEN];
170 color->b = line[3 * x + FI_RGBA_BLUE];
171 return true;
172 }
173
174 return false;
175 }
176
SetPixel(const int x,const int y,const BitmapColor<uint8_t> & color)177 bool Bitmap::SetPixel(const int x, const int y,
178 const BitmapColor<uint8_t>& color) {
179 if (x < 0 || x >= width_ || y < 0 || y >= height_) {
180 return false;
181 }
182
183 uint8_t* line = FreeImage_GetScanLine(data_.get(), height_ - 1 - y);
184
185 if (IsGrey()) {
186 line[x] = color.r;
187 return true;
188 } else if (IsRGB()) {
189 line[3 * x + FI_RGBA_RED] = color.r;
190 line[3 * x + FI_RGBA_GREEN] = color.g;
191 line[3 * x + FI_RGBA_BLUE] = color.b;
192 return true;
193 }
194
195 return false;
196 }
197
GetScanline(const int y) const198 const uint8_t* Bitmap::GetScanline(const int y) const {
199 CHECK_GE(y, 0);
200 CHECK_LT(y, height_);
201 return FreeImage_GetScanLine(data_.get(), height_ - 1 - y);
202 }
203
Fill(const BitmapColor<uint8_t> & color)204 void Bitmap::Fill(const BitmapColor<uint8_t>& color) {
205 for (int y = 0; y < height_; ++y) {
206 uint8_t* line = FreeImage_GetScanLine(data_.get(), height_ - 1 - y);
207 for (int x = 0; x < width_; ++x) {
208 if (IsGrey()) {
209 line[x] = color.r;
210 } else if (IsRGB()) {
211 line[3 * x + FI_RGBA_RED] = color.r;
212 line[3 * x + FI_RGBA_GREEN] = color.g;
213 line[3 * x + FI_RGBA_BLUE] = color.b;
214 }
215 }
216 }
217 }
218
InterpolateNearestNeighbor(const double x,const double y,BitmapColor<uint8_t> * color) const219 bool Bitmap::InterpolateNearestNeighbor(const double x, const double y,
220 BitmapColor<uint8_t>* color) const {
221 const int xx = static_cast<int>(std::round(x));
222 const int yy = static_cast<int>(std::round(y));
223 return GetPixel(xx, yy, color);
224 }
225
InterpolateBilinear(const double x,const double y,BitmapColor<float> * color) const226 bool Bitmap::InterpolateBilinear(const double x, const double y,
227 BitmapColor<float>* color) const {
228 // FreeImage's coordinate system origin is in the lower left of the image.
229 const double inv_y = height_ - 1 - y;
230
231 const int x0 = static_cast<int>(std::floor(x));
232 const int x1 = x0 + 1;
233 const int y0 = static_cast<int>(std::floor(inv_y));
234 const int y1 = y0 + 1;
235
236 if (x0 < 0 || x1 >= width_ || y0 < 0 || y1 >= height_) {
237 return false;
238 }
239
240 const double dx = x - x0;
241 const double dy = inv_y - y0;
242 const double dx_1 = 1 - dx;
243 const double dy_1 = 1 - dy;
244
245 const uint8_t* line0 = FreeImage_GetScanLine(data_.get(), y0);
246 const uint8_t* line1 = FreeImage_GetScanLine(data_.get(), y1);
247
248 if (IsGrey()) {
249 // Top row, column-wise linear interpolation.
250 const double v0 = dx_1 * line0[x0] + dx * line0[x1];
251
252 // Bottom row, column-wise linear interpolation.
253 const double v1 = dx_1 * line1[x0] + dx * line1[x1];
254
255 // Row-wise linear interpolation.
256 color->r = dy_1 * v0 + dy * v1;
257 return true;
258 } else if (IsRGB()) {
259 const uint8_t* p00 = &line0[3 * x0];
260 const uint8_t* p01 = &line0[3 * x1];
261 const uint8_t* p10 = &line1[3 * x0];
262 const uint8_t* p11 = &line1[3 * x1];
263
264 // Top row, column-wise linear interpolation.
265 const double v0_r = dx_1 * p00[FI_RGBA_RED] + dx * p01[FI_RGBA_RED];
266 const double v0_g = dx_1 * p00[FI_RGBA_GREEN] + dx * p01[FI_RGBA_GREEN];
267 const double v0_b = dx_1 * p00[FI_RGBA_BLUE] + dx * p01[FI_RGBA_BLUE];
268
269 // Bottom row, column-wise linear interpolation.
270 const double v1_r = dx_1 * p10[FI_RGBA_RED] + dx * p11[FI_RGBA_RED];
271 const double v1_g = dx_1 * p10[FI_RGBA_GREEN] + dx * p11[FI_RGBA_GREEN];
272 const double v1_b = dx_1 * p10[FI_RGBA_BLUE] + dx * p11[FI_RGBA_BLUE];
273
274 // Row-wise linear interpolation.
275 color->r = dy_1 * v0_r + dy * v1_r;
276 color->g = dy_1 * v0_g + dy * v1_g;
277 color->b = dy_1 * v0_b + dy * v1_b;
278 return true;
279 }
280
281 return false;
282 }
283
ExifFocalLength(double * focal_length) const284 bool Bitmap::ExifFocalLength(double* focal_length) const {
285 const double max_size = std::max(width_, height_);
286
287 //////////////////////////////////////////////////////////////////////////////
288 // Focal length in 35mm equivalent
289 //////////////////////////////////////////////////////////////////////////////
290
291 std::string focal_length_35mm_str;
292 if (ReadExifTag(FIMD_EXIF_EXIF, "FocalLengthIn35mmFilm",
293 &focal_length_35mm_str)) {
294 const boost::regex regex(".*?([0-9.]+).*?mm.*?");
295 boost::cmatch result;
296 if (boost::regex_search(focal_length_35mm_str.c_str(), result, regex)) {
297 const double focal_length_35 = std::stold(result[1]);
298 if (focal_length_35 > 0) {
299 *focal_length = focal_length_35 / 35.0 * max_size;
300 return true;
301 }
302 }
303 }
304
305 //////////////////////////////////////////////////////////////////////////////
306 // Focal length in mm
307 //////////////////////////////////////////////////////////////////////////////
308
309 std::string focal_length_str;
310 if (ReadExifTag(FIMD_EXIF_EXIF, "FocalLength", &focal_length_str)) {
311 boost::regex regex(".*?([0-9.]+).*?mm");
312 boost::cmatch result;
313 if (boost::regex_search(focal_length_str.c_str(), result, regex)) {
314 const double focal_length_mm = std::stold(result[1]);
315
316 // Lookup sensor width in database.
317 std::string make_str;
318 std::string model_str;
319 if (ReadExifTag(FIMD_EXIF_MAIN, "Make", &make_str) &&
320 ReadExifTag(FIMD_EXIF_MAIN, "Model", &model_str)) {
321 CameraDatabase database;
322 double sensor_width;
323 if (database.QuerySensorWidth(make_str, model_str, &sensor_width)) {
324 *focal_length = focal_length_mm / sensor_width * max_size;
325 return true;
326 }
327 }
328
329 // Extract sensor width from EXIF.
330 std::string pixel_x_dim_str;
331 std::string x_res_str;
332 std::string res_unit_str;
333 if (ReadExifTag(FIMD_EXIF_EXIF, "PixelXDimension", &pixel_x_dim_str) &&
334 ReadExifTag(FIMD_EXIF_EXIF, "FocalPlaneXResolution", &x_res_str) &&
335 ReadExifTag(FIMD_EXIF_EXIF, "FocalPlaneResolutionUnit",
336 &res_unit_str)) {
337 regex = boost::regex(".*?([0-9.]+).*?");
338 if (boost::regex_search(pixel_x_dim_str.c_str(), result, regex)) {
339 const double pixel_x_dim = std::stold(result[1]);
340 regex = boost::regex(".*?([0-9.]+).*?/.*?([0-9.]+).*?");
341 if (boost::regex_search(x_res_str.c_str(), result, regex)) {
342 const double x_res = std::stold(result[2]) / std::stold(result[1]);
343 // Use PixelXDimension instead of actual width of image, since
344 // the image might have been resized, but the EXIF data preserved.
345 const double ccd_width = x_res * pixel_x_dim;
346 if (ccd_width > 0 && focal_length_mm > 0) {
347 if (res_unit_str == "cm") {
348 *focal_length = focal_length_mm / (ccd_width * 10.0) * max_size;
349 return true;
350 } else if (res_unit_str == "inches") {
351 *focal_length = focal_length_mm / (ccd_width * 25.4) * max_size;
352 return true;
353 }
354 }
355 }
356 }
357 }
358 }
359 }
360
361 return false;
362 }
363
ExifLatitude(double * latitude) const364 bool Bitmap::ExifLatitude(double* latitude) const {
365 std::string str;
366 if (ReadExifTag(FIMD_EXIF_GPS, "GPSLatitude", &str)) {
367 const boost::regex regex(".*?([0-9.]+):([0-9.]+):([0-9.]+).*?");
368 boost::cmatch result;
369 if (boost::regex_search(str.c_str(), result, regex)) {
370 const double hours = std::stold(result[1]);
371 const double minutes = std::stold(result[2]);
372 const double seconds = std::stold(result[3]);
373 *latitude = hours + minutes / 60.0 + seconds / 3600.0;
374 return true;
375 }
376 }
377 return false;
378 }
379
ExifLongitude(double * longitude) const380 bool Bitmap::ExifLongitude(double* longitude) const {
381 std::string str;
382 if (ReadExifTag(FIMD_EXIF_GPS, "GPSLongitude", &str)) {
383 const boost::regex regex(".*?([0-9.]+):([0-9.]+):([0-9.]+).*?");
384 boost::cmatch result;
385 if (boost::regex_search(str.c_str(), result, regex)) {
386 const double hours = std::stold(result[1]);
387 const double minutes = std::stold(result[2]);
388 const double seconds = std::stold(result[3]);
389 *longitude = hours + minutes / 60.0 + seconds / 3600.0;
390 return true;
391 }
392 }
393 return false;
394 }
395
ExifAltitude(double * altitude) const396 bool Bitmap::ExifAltitude(double* altitude) const {
397 std::string str;
398 if (ReadExifTag(FIMD_EXIF_GPS, "GPSAltitude", &str)) {
399 const boost::regex regex(".*?([0-9.]+).*?/.*?([0-9.]+).*?");
400 boost::cmatch result;
401 if (boost::regex_search(str.c_str(), result, regex)) {
402 *altitude = std::stold(result[1]) / std::stold(result[2]);
403 return true;
404 }
405 }
406 return false;
407 }
408
Read(const std::string & path,const bool as_rgb)409 bool Bitmap::Read(const std::string& path, const bool as_rgb) {
410 if (!ExistsFile(path)) {
411 return false;
412 }
413
414 const FREE_IMAGE_FORMAT format = FreeImage_GetFileType(path.c_str(), 0);
415
416 if (format == FIF_UNKNOWN) {
417 return false;
418 }
419
420 FIBITMAP* fi_bitmap = FreeImage_Load(format, path.c_str());
421 if (fi_bitmap == nullptr) {
422 return false;
423 }
424
425 data_ = FIBitmapPtr(fi_bitmap, &FreeImage_Unload);
426
427 if (!IsPtrRGB(data_.get()) && as_rgb) {
428 FIBITMAP* converted_bitmap = FreeImage_ConvertTo24Bits(fi_bitmap);
429 data_ = FIBitmapPtr(converted_bitmap, &FreeImage_Unload);
430 } else if (!IsPtrGrey(data_.get()) && !as_rgb) {
431 FIBITMAP* converted_bitmap = FreeImage_ConvertToGreyscale(fi_bitmap);
432 data_ = FIBitmapPtr(converted_bitmap, &FreeImage_Unload);
433 }
434
435 if (!IsPtrSupported(data_.get())) {
436 data_.reset();
437 return false;
438 }
439
440 width_ = FreeImage_GetWidth(data_.get());
441 height_ = FreeImage_GetHeight(data_.get());
442 channels_ = as_rgb ? 3 : 1;
443
444 return true;
445 }
446
Write(const std::string & path,const FREE_IMAGE_FORMAT format,const int flags) const447 bool Bitmap::Write(const std::string& path, const FREE_IMAGE_FORMAT format,
448 const int flags) const {
449 FREE_IMAGE_FORMAT save_format;
450 if (format == FIF_UNKNOWN) {
451 save_format = FreeImage_GetFIFFromFilename(path.c_str());
452 if (save_format == FIF_UNKNOWN) {
453 // If format could not be deduced, save as PNG by default.
454 save_format = FIF_PNG;
455 }
456 } else {
457 save_format = format;
458 }
459
460 int save_flags = flags;
461 if (save_format == FIF_JPEG && flags == 0) {
462 // Use superb JPEG quality by default to avoid artifacts.
463 save_flags = JPEG_QUALITYSUPERB;
464 }
465
466 bool success = false;
467 if (save_flags == 0) {
468 success = FreeImage_Save(save_format, data_.get(), path.c_str());
469 } else {
470 success =
471 FreeImage_Save(save_format, data_.get(), path.c_str(), save_flags);
472 }
473
474 return success;
475 }
476
Smooth(const float sigma_x,const float sigma_y)477 void Bitmap::Smooth(const float sigma_x, const float sigma_y) {
478 std::vector<float> array(width_ * height_);
479 std::vector<float> array_smoothed(width_ * height_);
480 for (int d = 0; d < channels_; ++d) {
481 size_t i = 0;
482 for (int y = 0; y < height_; ++y) {
483 const uint8_t* line = FreeImage_GetScanLine(data_.get(), height_ - 1 - y);
484 for (int x = 0; x < width_; ++x) {
485 array[i] = line[x * channels_ + d];
486 i += 1;
487 }
488 }
489
490 vl_imsmooth_f(array_smoothed.data(), width_, array.data(), width_, height_,
491 width_, sigma_x, sigma_y);
492
493 i = 0;
494 for (int y = 0; y < height_; ++y) {
495 uint8_t* line = FreeImage_GetScanLine(data_.get(), height_ - 1 - y);
496 for (int x = 0; x < width_; ++x) {
497 line[x * channels_ + d] =
498 TruncateCast<float, uint8_t>(array_smoothed[i]);
499 i += 1;
500 }
501 }
502 }
503 }
504
Rescale(const int new_width,const int new_height,const FREE_IMAGE_FILTER filter)505 void Bitmap::Rescale(const int new_width, const int new_height,
506 const FREE_IMAGE_FILTER filter) {
507 SetPtr(FreeImage_Rescale(data_.get(), new_width, new_height, filter));
508 }
509
Clone() const510 Bitmap Bitmap::Clone() const { return Bitmap(FreeImage_Clone(data_.get())); }
511
CloneAsGrey() const512 Bitmap Bitmap::CloneAsGrey() const {
513 if (IsGrey()) {
514 return Clone();
515 } else {
516 return Bitmap(FreeImage_ConvertToGreyscale(data_.get()));
517 }
518 }
519
CloneAsRGB() const520 Bitmap Bitmap::CloneAsRGB() const {
521 if (IsRGB()) {
522 return Clone();
523 } else {
524 return Bitmap(FreeImage_ConvertTo24Bits(data_.get()));
525 }
526 }
527
CloneMetadata(Bitmap * target) const528 void Bitmap::CloneMetadata(Bitmap* target) const {
529 CHECK_NOTNULL(target);
530 CHECK_NOTNULL(target->Data());
531 FreeImage_CloneMetadata(data_.get(), target->Data());
532 }
533
ReadExifTag(const FREE_IMAGE_MDMODEL model,const std::string & tag_name,std::string * result) const534 bool Bitmap::ReadExifTag(const FREE_IMAGE_MDMODEL model,
535 const std::string& tag_name,
536 std::string* result) const {
537 FITAG* tag = nullptr;
538 FreeImage_GetMetadata(model, data_.get(), tag_name.c_str(), &tag);
539 if (tag == nullptr) {
540 *result = "";
541 return false;
542 } else {
543 if (tag_name == "FocalPlaneXResolution") {
544 // This tag seems to be in the wrong category.
545 *result = std::string(FreeImage_TagToString(FIMD_EXIF_INTEROP, tag));
546 } else {
547 *result = FreeImage_TagToString(model, tag);
548 }
549 return true;
550 }
551 }
552
SetPtr(FIBITMAP * data)553 void Bitmap::SetPtr(FIBITMAP* data) {
554 if (!IsPtrSupported(data)) {
555 FreeImage_Unload(data);
556 data = FreeImage_ConvertTo24Bits(data);
557 }
558
559 data_ = FIBitmapPtr(data, &FreeImage_Unload);
560 width_ = FreeImage_GetWidth(data);
561 height_ = FreeImage_GetHeight(data);
562 channels_ = IsPtrRGB(data) ? 3 : 1;
563 }
564
IsPtrGrey(FIBITMAP * data)565 bool Bitmap::IsPtrGrey(FIBITMAP* data) {
566 return FreeImage_GetColorType(data) == FIC_MINISBLACK &&
567 FreeImage_GetBPP(data) == 8;
568 }
569
IsPtrRGB(FIBITMAP * data)570 bool Bitmap::IsPtrRGB(FIBITMAP* data) {
571 return FreeImage_GetColorType(data) == FIC_RGB &&
572 FreeImage_GetBPP(data) == 24;
573 }
574
IsPtrSupported(FIBITMAP * data)575 bool Bitmap::IsPtrSupported(FIBITMAP* data) {
576 return IsPtrGrey(data) || IsPtrRGB(data);
577 }
578
Red(const float gray)579 float JetColormap::Red(const float gray) { return Base(gray - 0.25f); }
580
Green(const float gray)581 float JetColormap::Green(const float gray) { return Base(gray); }
582
Blue(const float gray)583 float JetColormap::Blue(const float gray) { return Base(gray + 0.25f); }
584
Base(const float val)585 float JetColormap::Base(const float val) {
586 if (val <= 0.125f) {
587 return 0.0f;
588 } else if (val <= 0.375f) {
589 return Interpolate(2.0f * val - 1.0f, 0.0f, -0.75f, 1.0f, -0.25f);
590 } else if (val <= 0.625f) {
591 return 1.0f;
592 } else if (val <= 0.87f) {
593 return Interpolate(2.0f * val - 1.0f, 1.0f, 0.25f, 0.0f, 0.75f);
594 } else {
595 return 0.0f;
596 }
597 }
598
Interpolate(const float val,const float y0,const float x0,const float y1,const float x1)599 float JetColormap::Interpolate(const float val, const float y0, const float x0,
600 const float y1, const float x1) {
601 return (val - x0) * (y1 - y0) / (x1 - x0) + y0;
602 }
603
604 } // namespace colmap
605