#ifndef VISUALDL_UTILS_IMAGE_H #define VISUALDL_UTILS_IMAGE_H #include #include #include "visualdl/utils/logging.h" namespace visualdl { using uint8_t = unsigned char; /* * 2: height*width, channel */ template using ImageDT = Eigen::Matrix; using Uint8Image = ImageDT; /* * hw: height*width * depth: number of channels */ static void NormalizeImage(Uint8Image* image, const float* buffer, int hw, int depth) { // Both image and buffer should be used in row major. Eigen::Map> values(buffer, depth, hw); CHECK_EQ(image->size(), hw * depth); CHECK_EQ(image->row(0).size(), hw); CHECK_EQ(image->col(0).size(), depth); std::vector infinite_pixels; // compute min and max ignoring nonfinite pixels float image_min = std::numeric_limits::infinity(); float image_max = -image_min; for (int i = 0; i < hw; i++) { bool finite = true; for (int j = 0; j < depth; j++) { // if infinite, skip this pixel if (!std::isfinite(values(j, i))) { infinite_pixels.emplace_back(i); finite = false; break; } } if (finite) { for (int j = 0; j < depth; j++) { float v = values(j, i); image_min = std::min(image_min, v); image_max = std::max(image_max, v); } } } // Pick an affine transform into uint8 const float kZeroThreshold = 1e-6; float scale, offset; if (image_min < 0) { float max_val = std::max(std::abs(image_min), image_max); scale = (max_val < kZeroThreshold ? 0.0f : 127.0f) / max_val; } else { scale = (image_max < kZeroThreshold ? 0.0f : 255.0f) / image_max; offset = 0.0f; } // Transform image, turning nonfinite values to bad_color for (int i = 0; i < depth; i++) { auto tmp = scale * values.row(i).array() + offset; image->row(i) = tmp.cast(); } for (int pixel : infinite_pixels) { for (int i = 0; i < depth; i++) { // TODO(ChunweiYan) use some highlight color to represent infinite pixels. (*image)(pixel, i) = (uint8_t)0; } } } } // namespace visualdl #endif