36 #include <unordered_map>
38 #include "VLFeat/imopv.h"
48 void FreeImageErrorHandler(FREE_IMAGE_FORMAT fif,
const char *message) {
49 std::string error_info =
"FreeImage error";
50 if(fif != FIF_UNKNOWN) {
51 error_info +=
" (" + std::string(FreeImage_GetFormatFromFIF(fif)) +
")";
53 LOG(ERROR) << error_info <<
": " << message;
56 struct FreeImageInitializer {
57 FreeImageInitializer() {
58 FreeImage_SetOutputMessage(FreeImageErrorHandler);
59 FreeImage_Initialise();
61 ~FreeImageInitializer() { FreeImage_DeInitialise(); }
64 const static auto initializer = FreeImageInitializer();
70 : data_(nullptr, &FreeImage_Unload), width_(0), height_(0), channels_(0) {}
74 SetPtr(FreeImage_Clone(other.data_.get()));
79 data_ = std::move(other.data_);
80 width_ = other.width_;
81 height_ = other.height_;
82 channels_ = other.channels_;
89 SetPtr(FreeImage_Clone(other.data_.get()));
96 data_ = std::move(other.data_);
97 width_ = other.width_;
98 height_ = other.height_;
99 channels_ = other.channels_;
105 FIBITMAP*
data =
nullptr;
109 const int kNumBitsPerPixel = 24;
113 const int kNumBitsPerPixel = 8;
117 data_ = FIBitmapPtr(
data, &FreeImage_Unload);
118 return data !=
nullptr;
137 const unsigned int scan_width =
ScanWidth();
139 const bool kTopDown =
true;
140 std::vector<uint8_t> raw_bits(scan_width * height_, 0);
141 FreeImage_ConvertToRawBits(raw_bits.data(), data_.get(), scan_width, bpp,
142 FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK,
143 FI_RGBA_BLUE_MASK, kTopDown);
148 std::vector<uint8_t> array(width_ * height_ * channels_);
150 for (
int y = 0;
y < height_; ++
y) {
151 const uint8_t* line = FreeImage_GetScanLine(data_.get(), height_ - 1 -
y);
152 for (
int x = 0;
x < width_; ++
x) {
153 for (
int d = 0; d < channels_; ++d) {
154 array[i] = line[
x * channels_ + d];
163 std::vector<uint8_t> array(width_ * height_ * channels_);
165 for (
int d = 0; d < channels_; ++d) {
166 for (
int x = 0;
x < width_; ++
x) {
167 for (
int y = 0;
y < height_; ++
y) {
168 const uint8_t* line =
169 FreeImage_GetScanLine(data_.get(), height_ - 1 -
y);
170 array[i] = line[
x * channels_ + d];
180 if (x < 0 || x >= width_ || y < 0 || y >= height_) {
184 const uint8_t* line = FreeImage_GetScanLine(data_.get(), height_ - 1 -
y);
189 }
else if (
IsRGB()) {
190 color->r = line[3 *
x + FI_RGBA_RED];
191 color->g = line[3 *
x + FI_RGBA_GREEN];
192 color->b = line[3 *
x + FI_RGBA_BLUE];
201 if (x < 0 || x >= width_ || y < 0 || y >= height_) {
205 uint8_t* line = FreeImage_GetScanLine(data_.get(), height_ - 1 -
y);
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;
222 CHECK_LT(
y, height_);
223 return FreeImage_GetScanLine(data_.get(), height_ - 1 -
y);
227 for (
int y = 0;
y < height_; ++
y) {
228 uint8_t* line = FreeImage_GetScanLine(data_.get(), height_ - 1 -
y);
229 for (
int x = 0;
x < width_; ++
x) {
232 }
else if (
IsRGB()) {
233 line[3 *
x + FI_RGBA_RED] =
color.r;
234 line[3 *
x + FI_RGBA_GREEN] =
color.g;
235 line[3 *
x + FI_RGBA_BLUE] =
color.b;
243 const int xx =
static_cast<int>(std::round(
x));
244 const int yy =
static_cast<int>(std::round(
y));
251 const double inv_y = height_ - 1 -
y;
254 const int x1 = x0 + 1;
255 const int y0 =
static_cast<int>(
std::floor(inv_y));
256 const int y1 = y0 + 1;
258 if (x0 < 0 || x1 >= width_ || y0 < 0 || y1 >= height_) {
262 const double dx =
x - x0;
263 const double dy = inv_y - y0;
264 const double dx_1 = 1 - dx;
265 const double dy_1 = 1 - dy;
267 const uint8_t* line0 = FreeImage_GetScanLine(data_.get(), y0);
268 const uint8_t* line1 = FreeImage_GetScanLine(data_.get(), y1);
272 const double v0 = dx_1 * line0[x0] + dx * line0[x1];
275 const double v1 = dx_1 * line1[x0] + dx * line1[x1];
278 color->r = dy_1 * v0 + dy * v1;
280 }
else if (
IsRGB()) {
281 const uint8_t* p00 = &line0[3 * x0];
282 const uint8_t* p01 = &line0[3 * x1];
283 const uint8_t* p10 = &line1[3 * x0];
284 const uint8_t* p11 = &line1[3 * x1];
287 const double v0_r = dx_1 * p00[FI_RGBA_RED] + dx * p01[FI_RGBA_RED];
288 const double v0_g = dx_1 * p00[FI_RGBA_GREEN] + dx * p01[FI_RGBA_GREEN];
289 const double v0_b = dx_1 * p00[FI_RGBA_BLUE] + dx * p01[FI_RGBA_BLUE];
292 const double v1_r = dx_1 * p10[FI_RGBA_RED] + dx * p11[FI_RGBA_RED];
293 const double v1_g = dx_1 * p10[FI_RGBA_GREEN] + dx * p11[FI_RGBA_GREEN];
294 const double v1_b = dx_1 * p10[FI_RGBA_BLUE] + dx * p11[FI_RGBA_BLUE];
297 color->r = dy_1 * v0_r + dy * v1_r;
298 color->g = dy_1 * v0_g + dy * v1_g;
299 color->b = dy_1 * v0_b + dy * v1_b;
308 std::string make_str;
309 std::string model_str;
310 std::string focal_length;
312 if (
ReadExifTag(FIMD_EXIF_MAIN,
"Make", &make_str)) {
313 *camera_model += (make_str +
"-");
318 if (
ReadExifTag(FIMD_EXIF_MAIN,
"Model", &model_str)) {
319 *camera_model += (model_str +
"-");
324 if (
ReadExifTag(FIMD_EXIF_EXIF,
"FocalLengthIn35mmFilm", &focal_length)) {
325 *camera_model += (focal_length +
"-");
326 }
else if (
ReadExifTag(FIMD_EXIF_EXIF,
"FocalLength", &focal_length)) {
327 *camera_model += (focal_length +
"-");
337 const double max_size = std::max(width_, height_);
343 std::string focal_length_35mm_str;
344 if (
ReadExifTag(FIMD_EXIF_EXIF,
"FocalLengthIn35mmFilm",
345 &focal_length_35mm_str)) {
346 const std::regex regex(
".*?([0-9.]+).*?mm.*?");
348 if (std::regex_search(focal_length_35mm_str.c_str(),
result, regex)) {
349 const double focal_length_35 = std::stold(
result[1]);
350 if (focal_length_35 > 0) {
351 *focal_length = focal_length_35 / 35.0 * max_size;
361 std::string focal_length_str;
362 if (
ReadExifTag(FIMD_EXIF_EXIF,
"FocalLength", &focal_length_str)) {
363 std::regex regex(
".*?([0-9.]+).*?mm");
365 if (std::regex_search(focal_length_str.c_str(),
result, regex)) {
366 const double focal_length_mm = std::stold(
result[1]);
369 std::string make_str;
370 std::string model_str;
371 if (
ReadExifTag(FIMD_EXIF_MAIN,
"Make", &make_str) &&
372 ReadExifTag(FIMD_EXIF_MAIN,
"Model", &model_str)) {
376 *focal_length = focal_length_mm / sensor_width * max_size;
382 std::string pixel_x_dim_str;
383 std::string x_res_str;
384 std::string res_unit_str;
385 if (
ReadExifTag(FIMD_EXIF_EXIF,
"PixelXDimension", &pixel_x_dim_str) &&
386 ReadExifTag(FIMD_EXIF_EXIF,
"FocalPlaneXResolution", &x_res_str) &&
387 ReadExifTag(FIMD_EXIF_EXIF,
"FocalPlaneResolutionUnit",
389 regex = std::regex(
".*?([0-9.]+).*?");
390 if (std::regex_search(pixel_x_dim_str.c_str(),
result, regex)) {
391 const double pixel_x_dim = std::stold(
result[1]);
392 regex = std::regex(
".*?([0-9.]+).*?/.*?([0-9.]+).*?");
393 if (std::regex_search(x_res_str.c_str(),
result, regex)) {
394 const double x_res = std::stold(
result[2]) / std::stold(
result[1]);
397 const double ccd_width = x_res * pixel_x_dim;
398 if (ccd_width > 0 && focal_length_mm > 0) {
399 if (res_unit_str ==
"cm") {
400 *focal_length = focal_length_mm / (ccd_width * 10.0) * max_size;
402 }
else if (res_unit_str ==
"inches") {
403 *focal_length = focal_length_mm / (ccd_width * 25.4) * max_size;
419 if (
ReadExifTag(FIMD_EXIF_GPS,
"GPSLatitudeRef", &str)) {
422 if (!str.empty() && str[0] ==
's') {
426 if (
ReadExifTag(FIMD_EXIF_GPS,
"GPSLatitude", &str)) {
427 const std::regex regex(
".*?([0-9.]+):([0-9.]+):([0-9.]+).*?");
429 if (std::regex_search(str.c_str(),
result, regex)) {
430 const double hours = std::stold(
result[1]);
431 const double minutes = std::stold(
result[2]);
432 const double seconds = std::stold(
result[3]);
433 double value = hours + minutes / 60.0 + seconds / 3600.0;
434 if (value > 0 && sign < 0) {
447 if (
ReadExifTag(FIMD_EXIF_GPS,
"GPSLongitudeRef", &str)) {
450 if (!str.empty() && str[0] ==
'w') {
454 if (
ReadExifTag(FIMD_EXIF_GPS,
"GPSLongitude", &str)) {
455 const std::regex regex(
".*?([0-9.]+):([0-9.]+):([0-9.]+).*?");
457 if (std::regex_search(str.c_str(),
result, regex)) {
458 const double hours = std::stold(
result[1]);
459 const double minutes = std::stold(
result[2]);
460 const double seconds = std::stold(
result[3]);
461 double value = hours + minutes / 60.0 + seconds / 3600.0;
462 if (value > 0 && sign < 0) {
474 if (
ReadExifTag(FIMD_EXIF_GPS,
"GPSAltitude", &str)) {
475 const std::regex regex(
".*?([0-9.]+).*?/.*?([0-9.]+).*?");
477 if (std::regex_search(str.c_str(),
result, regex)) {
478 *altitude = std::stold(
result[1]) / std::stold(
result[2]);
490 const FREE_IMAGE_FORMAT
format = FreeImage_GetFileType(
path.c_str(), 0);
492 if (
format == FIF_UNKNOWN) {
496 FIBITMAP* fi_bitmap = FreeImage_Load(
format,
path.c_str(), JPEG_EXIFROTATE);
497 if (fi_bitmap ==
nullptr) {
501 data_ = FIBitmapPtr(fi_bitmap, &FreeImage_Unload);
503 if (!IsPtrRGB(data_.get()) && as_rgb) {
504 FIBITMAP* converted_bitmap = FreeImage_ConvertTo24Bits(fi_bitmap);
505 data_ = FIBitmapPtr(converted_bitmap, &FreeImage_Unload);
506 }
else if (!IsPtrGrey(data_.get()) && !as_rgb) {
507 FIBITMAP* converted_bitmap = FreeImage_ConvertToGreyscale(fi_bitmap);
508 data_ = FIBitmapPtr(converted_bitmap, &FreeImage_Unload);
511 if (!IsPtrSupported(data_.get())) {
516 width_ = FreeImage_GetWidth(data_.get());
517 height_ = FreeImage_GetHeight(data_.get());
518 channels_ = as_rgb ? 3 : 1;
524 const int flags)
const {
525 FREE_IMAGE_FORMAT save_format;
526 if (
format == FIF_UNKNOWN) {
527 save_format = FreeImage_GetFIFFromFilename(
path.c_str());
528 if (save_format == FIF_UNKNOWN) {
530 save_format = FIF_PNG;
536 int save_flags = flags;
537 if (save_format == FIF_JPEG && flags == 0) {
539 save_flags = JPEG_QUALITYSUPERB;
542 bool success =
false;
543 if (save_flags == 0) {
544 success = FreeImage_Save(save_format, data_.get(),
path.c_str());
547 FreeImage_Save(save_format, data_.get(),
path.c_str(), save_flags);
554 std::vector<float> array(width_ * height_);
555 std::vector<float> array_smoothed(width_ * height_);
556 for (
int d = 0; d < channels_; ++d) {
558 for (
int y = 0;
y < height_; ++
y) {
559 const uint8_t* line = FreeImage_GetScanLine(data_.get(), height_ - 1 -
y);
560 for (
int x = 0;
x < width_; ++
x) {
561 array[i] = line[
x * channels_ + d];
566 vl_imsmooth_f(array_smoothed.data(), width_, array.data(), width_, height_,
567 width_, sigma_x, sigma_y);
570 for (
int y = 0;
y < height_; ++
y) {
571 uint8_t* line = FreeImage_GetScanLine(data_.get(), height_ - 1 -
y);
572 for (
int x = 0;
x < width_; ++
x) {
573 line[
x * channels_ + d] =
574 TruncateCast<float, uint8_t>(array_smoothed[i]);
582 const FREE_IMAGE_FILTER filter) {
583 SetPtr(FreeImage_Rescale(data_.get(), new_width, new_height, filter));
592 return Bitmap(FreeImage_ConvertToGreyscale(data_.get()));
600 return Bitmap(FreeImage_ConvertTo24Bits(data_.get()));
605 CHECK_NOTNULL(target);
606 CHECK_NOTNULL(target->
Data());
607 FreeImage_CloneMetadata(data_.get(), target->
Data());
611 const std::string& tag_name,
612 std::string*
result)
const {
613 FITAG* tag =
nullptr;
614 FreeImage_GetMetadata(model, data_.get(), tag_name.c_str(), &tag);
615 if (tag ==
nullptr) {
619 if (tag_name ==
"FocalPlaneXResolution") {
621 *
result = std::string(FreeImage_TagToString(FIMD_EXIF_INTEROP, tag));
623 *
result = FreeImage_TagToString(model, tag);
629 void Bitmap::SetPtr(FIBITMAP*
data) {
630 if (!IsPtrSupported(
data)) {
631 FreeImage_Unload(
data);
632 data = FreeImage_ConvertTo24Bits(
data);
635 data_ = FIBitmapPtr(
data, &FreeImage_Unload);
636 width_ = FreeImage_GetWidth(
data);
637 height_ = FreeImage_GetHeight(
data);
638 channels_ = IsPtrRGB(
data) ? 3 : 1;
641 bool Bitmap::IsPtrGrey(FIBITMAP*
data) {
642 return FreeImage_GetColorType(
data) == FIC_MINISBLACK &&
643 FreeImage_GetBPP(
data) == 8;
646 bool Bitmap::IsPtrRGB(FIBITMAP*
data) {
647 return FreeImage_GetColorType(
data) == FIC_RGB &&
648 FreeImage_GetBPP(
data) == 24;
651 bool Bitmap::IsPtrSupported(FIBITMAP*
data) {
652 return IsPtrGrey(
data) || IsPtrRGB(
data);
661 float JetColormap::Base(
const float val) {
664 }
else if (val <= 0.375f) {
665 return Interpolate(2.0f * val - 1.0f, 0.0f, -0.75f, 1.0f, -0.25f);
666 }
else if (val <= 0.625f) {
668 }
else if (val <= 0.87f) {
669 return Interpolate(2.0f * val - 1.0f, 1.0f, 0.25f, 0.0f, 0.75f);
675 float JetColormap::Interpolate(
const float val,
const float y0,
const float x0,
676 const float y1,
const float x1) {
677 return (val - x0) * (y1 - y0) / (x1 - x0) + y0;
filament::Texture::InternalFormat format
bool SetPixel(const int x, const int y, const BitmapColor< uint8_t > &color)
std::vector< uint8_t > ConvertToRowMajorArray() const
bool GetPixel(const int x, const int y, BitmapColor< uint8_t > *color) const
bool ExifCameraModel(std::string *camera_model) const
void Fill(const BitmapColor< uint8_t > &color)
bool Read(const std::string &path, const bool as_rgb=true)
std::vector< uint8_t > ConvertToRawBits() const
const uint8_t * GetScanline(const int y) const
bool Allocate(const int width, const int height, const bool as_rgb)
void Rescale(const int new_width, const int new_height, const FREE_IMAGE_FILTER filter=FILTER_BILINEAR)
void Smooth(const float sigma_x, const float sigma_y)
bool InterpolateBilinear(const double x, const double y, BitmapColor< float > *color) const
void CloneMetadata(Bitmap *target) const
unsigned int ScanWidth() const
std::vector< uint8_t > ConvertToColMajorArray() const
bool InterpolateNearestNeighbor(const double x, const double y, BitmapColor< uint8_t > *color) const
bool ExifAltitude(double *altitude) const
const FIBITMAP * Data() const
bool ExifFocalLength(double *focal_length) const
bool ReadExifTag(const FREE_IMAGE_MDMODEL model, const std::string &tag_name, std::string *result) const
bool ExifLongitude(double *longitude) const
bool ExifLatitude(double *latitude) const
Bitmap CloneAsGrey() const
Bitmap & operator=(const Bitmap &other)
Bitmap CloneAsRGB() const
unsigned int BitsPerPixel() const
bool Write(const std::string &path, const FREE_IMAGE_FORMAT format=FIF_UNKNOWN, const int flags=0) const
bool QuerySensorWidth(const std::string &make, const std::string &model, double *sensor_width)
static float Red(const float gray)
static float Blue(const float gray)
static float Green(const float gray)
static const std::string path
MiniVec< float, N > floor(const MiniVec< float, N > &a)
void StringTrim(std::string *str)
void StringToLower(std::string *str)
bool ExistsFile(const std::string &path)
std::string to_string(const T &n)