Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HDR Panorama: Handle very high intensities #891

Merged
merged 3 commits into from
Sep 12, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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