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] Compute the center exposure of the hdr automatically #1315

Merged
merged 18 commits into from
Feb 14, 2023
Merged
Show file tree
Hide file tree
Changes from 15 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
139 changes: 132 additions & 7 deletions src/aliceVision/hdr/brackets.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#include "brackets.hpp"

#include <fstream>

#include <aliceVision/numeric/numeric.hpp>

#include <boost/filesystem.hpp>
Expand Down Expand Up @@ -87,6 +89,7 @@ bool estimateBracketsFromSfmData(std::vector<std::vector<std::shared_ptr<sfmData
groups.push_back(group);
}

std::vector< std::vector<sfmData::ExposureSetting>> v_exposuresSetting;
for(auto & group : groups)
{
// Sort all images by exposure time
Expand All @@ -96,21 +99,141 @@ bool estimateBracketsFromSfmData(std::vector<std::vector<std::shared_ptr<sfmData
return true;
return (a->getCameraExposureSetting().getExposure() < b->getCameraExposureSetting().getExposure());
});
std::vector<sfmData::ExposureSetting> exposuresSetting;
for (auto& v : group)
{
exposuresSetting.push_back(v->getCameraExposureSetting());
}
v_exposuresSetting.push_back(exposuresSetting);
}

// Check exposure consistency between group
if (v_exposuresSetting.size() > 1)
{
for (int g=1 ; g < v_exposuresSetting.size(); g++)
{
for (int e = 0; e < v_exposuresSetting[g].size(); e++)
{
if (!(v_exposuresSetting[g][e] == v_exposuresSetting[g - 1][e]))
demoulinv marked this conversation as resolved.
Show resolved Hide resolved
{
ALICEVISION_LOG_ERROR("Non consistant exposures between groups have been detected. Check your dataset metadata.");
return false;
}
}
}
}

return true;
}

void selectTargetViews(std::vector<std::shared_ptr<sfmData::View>> & out_targetViews, const std::vector<std::vector<std::shared_ptr<sfmData::View>>> & groups, int offsetRefBracketIndex)
void selectTargetViews(std::vector<std::shared_ptr<sfmData::View>> & out_targetViews, const std::vector<std::vector<std::shared_ptr<sfmData::View>>> & groups, int offsetRefBracketIndex, const std::string& lumaStatFilepath, const double meanTargetedLuma)
{
for(auto & group : groups)
// If targetIndexesFilename cannot be opened or is not valid target views are derived from the middle exposed views
// For odd number, there is no ambiguity on the middle image.
// For even number, we arbitrarily choose the more exposed view (as we usually have more under-exposed images than over-exposed).
const int viewNumberPerGroup = groups[0].size();
const int middleIndex = viewNumberPerGroup / 2;
int targetIndex = middleIndex + offsetRefBracketIndex;

out_targetViews.clear();

if ((targetIndex >= 0) && (targetIndex < viewNumberPerGroup))
{
ALICEVISION_LOG_INFO("Use offsetRefBracketIndex parameter");
}
else // try to use the luminance statistics of the LDR images stored in the file
{
// Target views are the middle exposed views
// For add number, there is no ambiguity on the middle image.
// For even number, we arbitrarily choose the more exposed view (as we usually have more under-exposed images than over-exposed).
const int middleIndex = int(group.size()) / 2;
const int targetIndex = clamp(middleIndex + offsetRefBracketIndex, 0, int(group.size()) - 1);
ALICEVISION_LOG_INFO("offsetRefBracketIndex parameter out of range, read file containing luminance statistics to compute an estimation");
std::ifstream file(lumaStatFilepath);
if (!file)
ALICEVISION_THROW_ERROR("Failed to open file: " << lumaStatFilepath);
std::vector<std::string> lines;
std::string line;
while (std::getline(file, line))
{
lines.push_back(line);
}
if ((lines.size() < 3) || (atoi(lines[0].c_str()) != groups.size()) || (atoi(lines[1].c_str()) < groups[0].size()))
demoulinv marked this conversation as resolved.
Show resolved Hide resolved
{
ALICEVISION_THROW_ERROR("File: " << lumaStatFilepath << " is not a valid file");
}
int nbGroup = atoi(lines[0].c_str());
int nbExp = atoi(lines[1].c_str());

std::vector<double> v_lumaMeanMean;

for (int i = 0; i < nbExp; ++i)
{
double lumaMeanMean = 0.0;
for (int j = 0; j < nbGroup; ++j)
{
std::istringstream iss(lines[3 + j * nbExp + i]);
aliceVision::IndexT srcId;
int exposure, nbItem;
double lumaMean, lumaMin, lumaMax;
if (!(iss >> srcId >> exposure >> nbItem >> lumaMean >> lumaMin >> lumaMax))
{
ALICEVISION_THROW_ERROR("File: " << lumaStatFilepath << " is not a valid file");
}
lumaMeanMean += lumaMean;
}
v_lumaMeanMean.push_back(lumaMeanMean / nbGroup);
}

// Makes sure the luminance curve is monotonic
int firstvalid = -1;
int lastvalid = 0;
for (std::size_t k = 1; k < v_lumaMeanMean.size(); ++k)
{
bool valid = false;

if (v_lumaMeanMean[k] > v_lumaMeanMean[k - 1])
{
valid = true;
}

if (valid)
{
if (firstvalid < 0)
{
firstvalid = int(k) - 1;
}
lastvalid = int(k);
}
else
{
if (lastvalid != 0)
{
break;
}
}
}

if (lastvalid >= firstvalid && firstvalid >= 0)
{
double minDiffWithLumaTarget = 1000.0;
targetIndex = 0;

for (int k = firstvalid; k <= lastvalid; ++k)
{
const double diffWithLumaTarget = fabs(v_lumaMeanMean[k] - meanTargetedLuma);
if (diffWithLumaTarget < minDiffWithLumaTarget)
{
minDiffWithLumaTarget = diffWithLumaTarget;
targetIndex = k;
}
}
ALICEVISION_LOG_INFO("offsetRefBracketIndex parameter automaticaly set to " << targetIndex - middleIndex);
}
else
{
targetIndex = middleIndex;
ALICEVISION_LOG_WARNING("Non valid luminance statistics file, offsetRefBracketIndex parameter set to 0 (medium exposure)");
}
}

for (auto& group : groups)
{
//Set the ldr ancestors id
for (auto v : group)
{
Expand All @@ -119,6 +242,8 @@ void selectTargetViews(std::vector<std::shared_ptr<sfmData::View>> & out_targetV

out_targetViews.push_back(group[targetIndex]);
}
return;
}

}
}
70 changes: 69 additions & 1 deletion src/aliceVision/hdr/brackets.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,71 @@
namespace aliceVision {
namespace hdr {

enum class ECalibrationMethod
{
LINEAR,
DEBEVEC,
GROSSBERG,
LAGUERRE,
};

/**
* @brief convert an enum ECalibrationMethod to its corresponding string
* @param ECalibrationMethod
* @return String
*/
inline std::string ECalibrationMethod_enumToString(const ECalibrationMethod calibrationMethod)
{
switch (calibrationMethod)
{
case ECalibrationMethod::LINEAR:
return "linear";
case ECalibrationMethod::DEBEVEC:
return "debevec";
case ECalibrationMethod::GROSSBERG:
return "grossberg";
case ECalibrationMethod::LAGUERRE:
return "laguerre";
}
throw std::out_of_range("Invalid method name enum");
}

/**
* @brief convert a string calibration method name to its corresponding enum ECalibrationMethod
* @param ECalibrationMethod
* @return String
*/
inline ECalibrationMethod ECalibrationMethod_stringToEnum(const std::string& calibrationMethodName)
{
std::string methodName = calibrationMethodName;
std::transform(methodName.begin(), methodName.end(), methodName.begin(), ::tolower);

if (methodName == "linear")
return ECalibrationMethod::LINEAR;
if (methodName == "debevec")
return ECalibrationMethod::DEBEVEC;
if (methodName == "grossberg")
return ECalibrationMethod::GROSSBERG;
if (methodName == "laguerre")
return ECalibrationMethod::LAGUERRE;

throw std::out_of_range("Invalid method name : '" + calibrationMethodName + "'");
}

inline std::ostream& operator<<(std::ostream& os, ECalibrationMethod calibrationMethodName)
{
os << ECalibrationMethod_enumToString(calibrationMethodName);
return os;
}

inline std::istream& operator>>(std::istream& in, ECalibrationMethod& calibrationMethod)
{
std::string token;
in >> token;
calibrationMethod = ECalibrationMethod_stringToEnum(token);
return in;
}

/**
* @brief Estimate brackets information from sfm data
* @param[out] groups: estimated groups
Expand All @@ -25,8 +90,11 @@ bool estimateBracketsFromSfmData(std::vector<std::vector<std::shared_ptr<sfmData
* @param[out] targetViews: estimated target views
* @param[in] groups: groups of Views corresponding to multi-bracketing. Warning: Needs be sorted by exposure time.
* @param[in] offsetRefBracketIndex: 0 mean center bracket and you can choose +N/-N to select the reference bracket
* @param[in] targetIndexesFilename: in case offsetRefBracketIndex is out of range the number of views in a group target indexes can be read from a text file
* if the file cannot be read or does not contain the expected number of values (same as view group number) and
* if offsetRefBracketIndex is out of range the number of views then a clamped values of offsetRefBracketIndex is considered
*/
void selectTargetViews(std::vector<std::shared_ptr<sfmData::View>> & out_targetViews, const std::vector<std::vector<std::shared_ptr<sfmData::View>>>& groups, int offsetRefBracketIndex);
void selectTargetViews(std::vector<std::shared_ptr<sfmData::View>> & out_targetViews, const std::vector<std::vector<std::shared_ptr<sfmData::View>>>& groups, int offsetRefBracketIndex, const std::string& targetIndexesFilename = "", const double meanTargetedLuma = 0.4);

}
}
Loading