Skip to content

Commit

Permalink
Merge pull request #891 from alicevision/dev/handleHighIntensities
Browse files Browse the repository at this point in the history
HDR Panorama: Handle very high intensities
  • Loading branch information
fabiencastan authored Sep 12, 2020
2 parents 1f41ca1 + 4ddde85 commit 9bdebf7
Show file tree
Hide file tree
Showing 6 changed files with 147 additions and 11 deletions.
5 changes: 1 addition & 4 deletions src/aliceVision/hdr/hdrMerge.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -199,15 +199,14 @@ void hdrMerge::postProcessHighlight(const std::vector< image::Image<image::RGBfC
image::Image<float> isPixelClamped_g(width, height);
image::ImageGaussianFilter(isPixelClamped, 1.0f, isPixelClamped_g, 3, 3);

const float maxHalfFloat = 65504.0f;
#pragma omp parallel for
for (int y = 0; y < height; ++y)
{
for (int x = 0; x < width; ++x)
{
image::RGBfColor& radianceColor = radiance(y, x);

double clampingCompensation = highlightCorrectionFactor * (isPixelClamped_g(y, x) / 3.0);
double clampingCompensation = highlightCorrectionFactor * isPixelClamped_g(y, x);
double clampingCompensationInv = (1.0 - clampingCompensation);
assert(clampingCompensation <= 1.0);

Expand All @@ -216,8 +215,6 @@ void hdrMerge::postProcessHighlight(const std::vector< image::Image<image::RGBfC
if(highlightTarget > radianceColor(channel))
{
radianceColor(channel) = float(clampingCompensation * highlightTarget + clampingCompensationInv * radianceColor(channel));
// Ensure that values can be stored in half float for openexr export
radianceColor(channel) = std::min(maxHalfFloat, radianceColor(channel));
}
}
}
Expand Down
99 changes: 95 additions & 4 deletions src/aliceVision/image/io.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ EImageFileType EImageFileType_stringToEnum(const std::string& imageFileType)
if(type == "tif" || type == "tiff") return EImageFileType::TIFF;
if(type == "exr") return EImageFileType::EXR;

throw std::out_of_range("Invalid image file type : " + imageFileType);
throw std::out_of_range("Invalid image file type: " + imageFileType);
}

std::string EImageFileType_enumToString(const EImageFileType imageFileType)
Expand Down Expand Up @@ -101,6 +101,53 @@ bool isSupported(const std::string& ext)
return (std::find(start, end, boost::to_lower_copy(ext)) != end);
}


std::string EStorageDataType_informations()
{
return EStorageDataType_enumToString(EStorageDataType::Float) + ", " +
EStorageDataType_enumToString(EStorageDataType::Half) + ", " +
EStorageDataType_enumToString(EStorageDataType::HalfFinite) + ", " +
EStorageDataType_enumToString(EStorageDataType::Auto);
}

EStorageDataType EStorageDataType_stringToEnum(const std::string& dataType)
{
std::string type = dataType;
std::transform(type.begin(), type.end(), type.begin(), ::tolower); //tolower

if (type == "float") return EStorageDataType::Float;
if (type == "half") return EStorageDataType::Half;
if (type == "halffinite") return EStorageDataType::HalfFinite;
if (type == "auto") return EStorageDataType::Auto;

throw std::out_of_range("Invalid EStorageDataType: " + dataType);
}

std::string EStorageDataType_enumToString(const EStorageDataType dataType)
{
switch (dataType)
{
case EStorageDataType::Float: return "Float";
case EStorageDataType::Half: return "Half";
case EStorageDataType::HalfFinite: return "HalfFinite";
case EStorageDataType::Auto: return "Auto";
}
throw std::out_of_range("Invalid EStorageDataType enum");
}

std::ostream& operator<<(std::ostream& os, EStorageDataType dataType)
{
return os << EStorageDataType_enumToString(dataType);
}

std::istream& operator>>(std::istream& in, EStorageDataType& dataType)
{
std::string token;
in >> token;
dataType = EStorageDataType_stringToEnum(token);
return in;
}

// Warning: type conversion problems from string to param value, we may lose some metadata with string maps
oiio::ParamValueList getMetadataFromMap(const std::map<std::string, std::string>& metadataMap)
{
Expand Down Expand Up @@ -280,7 +327,7 @@ void readImage(const std::string& path,

// compute luminance via a weighted sum of R,G,B
// (assuming Rec709 primaries and a linear scale)
const float weights[3] = {.2126, .7152, .0722};
const float weights[3] = {.2126f, .7152f, .0722f};
oiio::ImageBuf grayscaleBuf;
oiio::ImageBufAlgo::channel_sum(grayscaleBuf, inBuf, weights, convertionROI);
inBuf.copy(grayscaleBuf);
Expand Down Expand Up @@ -324,6 +371,24 @@ void readImage(const std::string& path,
}
}

bool containsHalfFloatOverflow(const oiio::ImageBuf& image)
{
oiio::ImageBufAlgo::PixelStats stats;
oiio::ImageBufAlgo::computePixelStats(stats, image);

for(auto maxValue: stats.max)
{
if(maxValue > HALF_MAX)
return true;
}
for (auto minValue: stats.min)
{
if (minValue < -HALF_MAX)
return true;
}
return false;
}

template<typename T>
void writeImage(const std::string& path,
oiio::TypeDesc typeDesc,
Expand Down Expand Up @@ -368,8 +433,34 @@ void writeImage(const std::string& path,
oiio::ImageBuf formatBuf; // buffer for image format modification
if(isEXR)
{
formatBuf.copy(*outBuf, oiio::TypeDesc::HALF); // override format, use half instead of float
outBuf = &formatBuf;
const std::string storageDataTypeStr = imageSpec.get_string_attribute("AliceVision:storageDataType", EStorageDataType_enumToString(EStorageDataType::HalfFinite));
EStorageDataType storageDataType = EStorageDataType_stringToEnum(storageDataTypeStr);

if (storageDataType == EStorageDataType::Auto)
{
if (containsHalfFloatOverflow(*outBuf))
{
storageDataType = EStorageDataType::Float;
}
else
{
storageDataType = EStorageDataType::Half;
}
ALICEVISION_LOG_DEBUG("writeImage storageDataTypeStr: " << storageDataType);
}

if (storageDataType == EStorageDataType::HalfFinite)
{
oiio::ImageBufAlgo::clamp(colorspaceBuf, *outBuf, -HALF_MAX, HALF_MAX);
outBuf = &colorspaceBuf;
}

if (storageDataType == EStorageDataType::Half ||
storageDataType == EStorageDataType::HalfFinite)
{
formatBuf.copy(*outBuf, oiio::TypeDesc::HALF); // override format, use half instead of float
outBuf = &formatBuf;
}
}

// write image
Expand Down
20 changes: 20 additions & 0 deletions src/aliceVision/image/io.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ enum class EImageFileType
EXR
};


/**
* @brief get informations about each image file type
* @return String
Expand Down Expand Up @@ -91,6 +92,25 @@ std::vector<std::string> getSupportedExtensions();
*/
bool isSupported(const std::string& ext);


/**
* @brief Data type use to write the image
*/
enum class EStorageDataType
{
Float, //< Use full floating point precision to store
Half, //< Use half (values our of range could become inf or nan)
HalfFinite, //< Use half, but ensures out-of-range pixels are clamps to keep finite pixel values
Auto //< Use half if all pixels can be stored in half without clamp, else use full float
};

std::string EStorageDataType_informations();
EStorageDataType EStorageDataType_stringToEnum(const std::string& dataType);
std::string EStorageDataType_enumToString(const EStorageDataType dataType);
std::ostream& operator<<(std::ostream& os, EStorageDataType dataType);
std::istream& operator>>(std::istream& in, EStorageDataType& dataType);


/**
* @brief convert a metadata string map into an oiio::ParamValueList
* @param[in] metadataMap string map
Expand Down
7 changes: 7 additions & 0 deletions src/software/pipeline/main_LdrToHdrMerge.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ std::string getHdrImagePath(const std::string& outputPath, std::size_t g)
return hdrImagePath;
}


int aliceVision_main(int argc, char** argv)
{
std::string verboseLevel = system::EVerboseLevel_enumToString(system::Logger::getDefaultVerboseLevel());
Expand All @@ -59,6 +60,8 @@ int aliceVision_main(int argc, char** argv)
float highlightCorrectionFactor = 1.0f;
float highlightTargetLux = 120000.0f;

image::EStorageDataType storageDataType = image::EStorageDataType::Float;

int rangeStart = -1;
int rangeSize = 1;

Expand Down Expand Up @@ -92,6 +95,8 @@ int aliceVision_main(int argc, char** argv)
("highlightCorrectionFactor", po::value<float>(&highlightCorrectionFactor)->default_value(highlightCorrectionFactor),
"float value between 0 and 1 to correct clamped highlights in dynamic range: use 0 for no correction, 1 for "
"full correction to maxLuminance.")
("storageDataType", po::value<image::EStorageDataType>(&storageDataType)->default_value(storageDataType),
("Storage data type: " + image::EStorageDataType_informations()).c_str())
("rangeStart", po::value<int>(&rangeStart)->default_value(rangeStart),
"Range image index start.")
("rangeSize", po::value<int>(&rangeSize)->default_value(rangeSize),
Expand Down Expand Up @@ -287,6 +292,8 @@ int aliceVision_main(int argc, char** argv)

// Write an image with parameters from the target view
oiio::ParamValueList targetMetadata = image::readImageMetadata(targetView->getImagePath());
targetMetadata.push_back(oiio::ParamValue("AliceVision:storageDataType", image::EStorageDataType_enumToString(storageDataType)));

image::writeImage(hdrImagePath, HDRimage, image::EImageColorSpace::AUTO, targetMetadata);
}

Expand Down
15 changes: 14 additions & 1 deletion src/software/pipeline/main_panoramaCompositing.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ typedef struct {
std::string weights_path;
} ConfigView;


/**
* @brief Maxflow computation based on a standard Adjacency List graph reprensentation.
*
Expand Down Expand Up @@ -1842,6 +1843,10 @@ class GraphcutSeams {
if (((mask(i, j) & 1) && (mask(i + 1, j) & 2)) || ((mask(i, j) & 2) && (mask(i + 1, j) & 1))) {
float d1 = (color_label(i, j) - color_other(i, j)).norm();
float d2 = (color_label(i + 1, j) - color_other(i + 1, j)).norm();

d1 = std::min(2.0f, d1);
d2 = std::min(2.0f, d2);

w = (d1 + d2) * 100.0 + 1.0;
}

Expand Down Expand Up @@ -2146,6 +2151,8 @@ int aliceVision_main(int argc, char **argv)
bool showBorders = false;
bool showSeams = false;

image::EStorageDataType storageDataType = image::EStorageDataType::Float;

system::EVerboseLevel verboseLevel = system::Logger::getDefaultVerboseLevel();

// Program description
Expand All @@ -2167,7 +2174,9 @@ int aliceVision_main(int argc, char **argv)
optionalParams.add_options()
("compositerType,c", po::value<std::string>(&compositerType)->required(), "Compositer Type [replace, alpha, multiband].")
("overlayType,c", po::value<std::string>(&overlayType)->required(), "Overlay Type [none, borders, seams, all].")
("useGraphCut,c", po::value<bool>(&useGraphCut)->default_value(useGraphCut), "Do we use graphcut for ghost removal ?");
("useGraphCut,c", po::value<bool>(&useGraphCut)->default_value(useGraphCut), "Do we use graphcut for ghost removal ?")
("storageDataType", po::value<image::EStorageDataType>(&storageDataType)->default_value(storageDataType),
("Storage data type: " + image::EStorageDataType_informations()).c_str());
allParams.add(optionalParams);

// Setup log level given command line
Expand Down Expand Up @@ -2480,6 +2489,10 @@ int aliceVision_main(int argc, char **argv)
// Store output
ALICEVISION_LOG_INFO("Write output panorama to file " << outputPanorama);
const aliceVision::image::Image<image::RGBAfColor> & panorama = compositer->getPanorama();

// Select storage data type
outputMetadata.push_back(oiio::ParamValue("AliceVision:storageDataType", image::EStorageDataType_enumToString(storageDataType)));

image::writeImage(outputPanorama, panorama, image::EImageColorSpace::AUTO, outputMetadata);

return EXIT_SUCCESS;
Expand Down
12 changes: 10 additions & 2 deletions src/software/pipeline/main_panoramaWarping.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1003,9 +1003,12 @@ int aliceVision_main(int argc, char **argv)
std::string outputDirectory;

std::pair<int, int> panoramaSize = {0, 0};
int percentUpscale = 50;

image::EStorageDataType storageDataType = image::EStorageDataType::Float;

int rangeStart = -1;
int rangeSize = 1;
int percentUpscale = 50;

// Program description
po::options_description allParams (
Expand All @@ -1027,6 +1030,8 @@ int aliceVision_main(int argc, char **argv)
"Panorama Width in pixels.")
("percentUpscale", po::value<int>(&percentUpscale)->default_value(percentUpscale),
"Percentage of upscaled pixels.")
("storageDataType", po::value<image::EStorageDataType>(&storageDataType)->default_value(storageDataType),
("Storage data type: " + image::EStorageDataType_informations()).c_str())
("rangeStart", po::value<int>(&rangeStart)->default_value(rangeStart),
"Range image index start.")
("rangeSize", po::value<int>(&rangeSize)->default_value(rangeSize),
Expand Down Expand Up @@ -1197,9 +1202,12 @@ int aliceVision_main(int argc, char **argv)
{
const aliceVision::image::Image<image::RGBfColor> & cam = warper.getColor();

oiio::ParamValueList viewMetadata = metadata;
viewMetadata.push_back(oiio::ParamValue("AliceVision:storageDataType", image::EStorageDataType_enumToString(storageDataType)));

const std::string viewFilepath = (fs::path(outputDirectory) / (viewIdStr + ".exr")).string();
ALICEVISION_LOG_INFO("Store view " << i << " with path " << viewFilepath);
image::writeImage(viewFilepath, cam, image::EImageColorSpace::AUTO, metadata);
image::writeImage(viewFilepath, cam, image::EImageColorSpace::AUTO, viewMetadata);
}
{
const aliceVision::image::Image<unsigned char> & mask = warper.getMask();
Expand Down

0 comments on commit 9bdebf7

Please sign in to comment.