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 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
110 changes: 103 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,112 @@ 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_WARNING("Non consistant exposures between poses have been detected. Most likely the dataset has been captured with an automatic exposure mode enabled. Final result can be impacted.");
g = v_exposuresSetting.size();
break;
}
}
}
}

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 an error is thrown
// 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) || (std::stoi(lines[0].c_str()) != groups.size()) || (std::stoi(lines[1].c_str()) < groups[0].size()))
{
ALICEVISION_THROW_ERROR("File: " << lumaStatFilepath << " is not a valid file");
}
int nbGroup = std::stoi(lines[0].c_str());
int nbExp = std::stoi(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 nbItem;
double exposure, 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);
}

// adjust last index to avoid non increasing luminance curve due to saturation in highlights
int lastIdx = v_lumaMeanMean.size() - 1;
while ((lastIdx > 1) && ((v_lumaMeanMean[lastIdx] < v_lumaMeanMean[lastIdx - 1]) || (v_lumaMeanMean[lastIdx] < v_lumaMeanMean[lastIdx - 2])))
{
lastIdx--;
}

double minDiffWithLumaTarget = 1000.0;
targetIndex = 0;

for (int k = 0; k < lastIdx; ++k)
{
const double diffWithLumaTarget = (v_lumaMeanMean[k] > meanTargetedLuma) ? (v_lumaMeanMean[k] - meanTargetedLuma) : (meanTargetedLuma - v_lumaMeanMean[k]);
if (diffWithLumaTarget < minDiffWithLumaTarget)
{
minDiffWithLumaTarget = diffWithLumaTarget;
targetIndex = k;
}
}
ALICEVISION_LOG_INFO("offsetRefBracketIndex parameter automaticaly set to " << targetIndex - middleIndex);
}

for (auto& group : groups)
{
//Set the ldr ancestors id
for (auto v : group)
{
Expand All @@ -119,6 +213,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);

}
}
8 changes: 7 additions & 1 deletion src/aliceVision/hdr/hdrTestCommon.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,14 @@ bool extractSamplesGroups(std::vector<std::vector<ImageSample>>& out_samples,
imgReadOptions.rawColorInterpretation = image::ERawColorInterpretation::LibRawWhiteBalancing;
imgReadOptions.workingColorSpace = image::EImageColorSpace::LINEAR;

std::vector<IndexT> viewIds;
for (size_t id = 0; id < imagePaths[idGroup].size(); ++id)
{
viewIds.push_back(id);
}

std::vector<ImageSample> groupSamples;
if (!Sampling::extractSamplesFromImages(groupSamples, imagePaths[idGroup],
if (!Sampling::extractSamplesFromImages(groupSamples, imagePaths[idGroup], viewIds,
times[idGroup], width, height,
channelQuantization, imgReadOptions,
Sampling::Params{}))
Expand Down
Loading