diff --git a/src/aliceVision/hdr/brackets.cpp b/src/aliceVision/hdr/brackets.cpp index f647bacbd5..29dddefecb 100644 --- a/src/aliceVision/hdr/brackets.cpp +++ b/src/aliceVision/hdr/brackets.cpp @@ -184,15 +184,8 @@ int selectTargetViews(std::vector>& targetViews, return targetIndex; } -std::vector> splitBasedir(const std::vector & luminanceInfos) +std::vector correctPaths(const std::vector & luminanceInfos) { - std::vector> splitted; - - if (luminanceInfos.size() == 0) - { - return splitted; - } - //Ignore non existing files //Remove relative paths //Remove symlinks @@ -210,6 +203,19 @@ std::vector> splitBasedir(const std::vector> splitBasedir(const std::vector & luminanceInfos) +{ + std::vector> splitted; + + if (luminanceInfos.size() == 0) + { + return splitted; + } + + std::vector correctedPaths = correctPaths(luminanceInfos); //Sort luminanceinfos by names std::sort(correctedPaths.begin(), @@ -253,6 +259,69 @@ std::vector> splitMonotonics(const std::vector current; + for (int i = 0; i < luminanceInfos.size(); i++) + { + current.push_back(luminanceInfos[i]); + if (i % 2 == 1) + { + splitted.push_back(current); + current.clear(); + } + } + return splitted; + } + } + } + + // Check the corner case of 3 items per group ordered with middle exposure first and without outlier + if (luminanceInfos.size() % 3 == 0) + { + float exp0 = luminanceInfos[0].mexposure; + float exp1 = luminanceInfos[1].mexposure; + float exp2 = luminanceInfos[2].mexposure; + if (exp0 > exp1 && exp0 < exp2 || exp0 < exp1 && exp0 > exp2) + { + int idx = 3; + bool ok = true; + while (ok && idx < luminanceInfos.size()) + { + ok = ok && luminanceInfos[idx].mexposure == exp0 && luminanceInfos[idx+1].mexposure == exp1 && luminanceInfos[idx+2].mexposure == exp2; + idx += 3; + } + if (idx == luminanceInfos.size()) + { + std::vector current; + for (int i = 0; i < luminanceInfos.size(); i++) + { + current.push_back(luminanceInfos[i]); + if (i % 3 == 2) + { + splitted.push_back(current); + current.clear(); + } + } + return splitted; + } + } + } + //Split the luminanceInfos into groups which have monotonic values //(either increasing or decreasing) std::vector current; @@ -317,7 +386,7 @@ int extractIndex(const std::vector & smaller, const std::vector> estimateGroups(const std::vector } } - //check coherency - bool coherency = true; - for (int idref = 1; idref < monotonics.size(); ++idref) + // In some cases (some Nikon cameras for instance) the medium exposure is at the first position of the ldr images group. + // Check that case and try to insert the resulting luminanceInfos seen as outliers at the mid index in the correponding groups. + if ((luminanceInfos.size() - monotonics.size() * monotonics[0].size() >= monotonics.size()) && // at least as many remaining outliers as groups + (monotonics[0].size() % 2 == 0)) // Even number of ldr images in a group { - const int idprev = idref - 1; - for (int idExposure = 0; idExposure < monotonics[idref].size(); ++idExposure) + + //Sort luminanceinfos by names + std::vector LumInfoCorrectPath = correctPaths(luminanceInfos); + std::sort(LumInfoCorrectPath.begin(), + LumInfoCorrectPath.end(), + [](const LuminanceInfo& a, const LuminanceInfo& b) -> bool { + return (a.mpath < b.mpath); + }); + + // Extract remaining outliers (all the images in the original list that don't belong to a group) + std::vector outliers; + for (const auto & li: LumInfoCorrectPath) + { + bool notInMonotonics = true; + int idx = 0; + while (notInMonotonics && idx < monotonics.size()) + { + int idxl = 0; + while (notInMonotonics && idxl < monotonics[idx].size()) + { + notInMonotonics = !(monotonics[idx][idxl].mpath == li.mpath); + idxl++; + } + idx++; + } + if (notInMonotonics) + { + outliers.push_back(li); + } + } + + // Check if for all groups we can find an outlier located just before the first item of the group in the global list + // sorted by name and with a mexposure in between the two middle items of the group + + std::vector firstGroupItemPositions; + for (int idxg = 0; idxg < monotonics.size(); ++idxg) { - if (!(monotonics[idref][idExposure].mexposure == monotonics[idprev][idExposure].mexposure)) + int pos = 0; + while (LumInfoCorrectPath[pos].mpath != monotonics[idxg][0].mpath && pos < LumInfoCorrectPath.size()) { - ALICEVISION_LOG_WARNING("Non consistent exposures between poses have been detected.\ - Most likely the dataset has been captured with an automatic exposure mode enabled.\ - Final result can be impacted."); - coherency = false; - - break; + pos++; } + firstGroupItemPositions.push_back(pos); } + // The nth item of firstGroupItemPositions is the position in the global list sorted by name of the first element of the nth group + - if (!coherency) + LuminanceInfo emptyLumInfo(0, "", 0.0); + std::vector lumInfosToBeAdded(monotonics.size(), emptyLumInfo); + + for (const auto & item: outliers) { - break; + int pos = 0; + while (LumInfoCorrectPath[pos].mpath != item.mpath && pos < LumInfoCorrectPath.size()) + { + pos++; + } + int idx = 0; + while (firstGroupItemPositions[idx] != pos + 1 && idx < firstGroupItemPositions.size()) + { + idx++; + } + if (idx < firstGroupItemPositions.size()) + { + lumInfosToBeAdded[idx] = item; + } + } + + // Check that all candidate have the same mexposure value in the right range + const float mexpRef = lumInfosToBeAdded[0].mexposure; + const int groupSize = monotonics[0].size(); + const float mexpMin = monotonics[0][groupSize/2 - 1].mexposure; + const float mexpMax = monotonics[0][groupSize/2].mexposure; + bool lumInfosToBeAddedIsValid = mexpMin < mexpRef && mexpRef < mexpMax; + if (lumInfosToBeAddedIsValid) + { + for (int idx = 1; idx < lumInfosToBeAdded.size(); ++idx) + { + lumInfosToBeAddedIsValid = lumInfosToBeAddedIsValid && lumInfosToBeAdded[idx].mexposure == mexpRef; + } + if (lumInfosToBeAddedIsValid) + { + // Add candidate outliers at the mid position of the corresponding group + for (int idx = 0; idx < lumInfosToBeAdded.size(); ++idx) + { + std::vector::iterator mid = monotonics[idx].begin() + groupSize / 2; + monotonics[idx].insert(mid, lumInfosToBeAdded[idx]); + } + } + } + } + + //check coherency + bool coherency = monotonics.size() * monotonics[0].size() <= luminanceInfos.size(); + if (!coherency) + { + ALICEVISION_LOG_WARNING("Non coherent number of ldr images per group.\ + Most likely the original ldr image set was split incorrectly.\ + Final result can be impacted."); + } + else + { + for (int idref = 1; idref < monotonics.size(); ++idref) + { + const int idprev = idref - 1; + for (int idExposure = 0; idExposure < monotonics[idref].size(); ++idExposure) + { + if (!(monotonics[idref][idExposure].mexposure == monotonics[idprev][idExposure].mexposure)) + { + ALICEVISION_LOG_WARNING("Non consistent exposures between poses have been detected.\ + Most likely the dataset has been captured with an automatic exposure mode enabled.\ + Final result can be impacted."); + coherency = false; + + break; + } + } + + if (!coherency) + { + break; + } } } @@ -484,7 +659,7 @@ std::vector> estimateGroups(const std::vector groups.push_back(group); } - ALICEVISION_LOG_INFO("Groups found : " << monotonics.size()); + ALICEVISION_LOG_INFO(monotonics.size() << " group(s) of " << monotonics[0].size() << " bracket(s) found"); return groups; }