more fine-tuning of the histogram matching

make sure we do not place curve points in regions of the histogram that are
outside the dynamic range of the input images -- that would be wild guessing,
better to just leave this to the spline interpolation
This commit is contained in:
Alberto Griggio 2018-01-31 10:34:50 +01:00
parent fdde31839d
commit 79324ed5d0

View File

@ -36,20 +36,36 @@ extern const Settings *settings;
namespace {
std::vector<int> getCdf(const IImage8 &img)
struct CdfInfo {
std::vector<int> cdf;
int min_val;
int max_val;
CdfInfo(): cdf(256), min_val(-1), max_val(-1) {}
};
CdfInfo getCdf(const IImage8 &img)
{
std::vector<int> ret(256);
CdfInfo ret;
for (int y = 0; y < img.getHeight(); ++y) {
for (int x = 0; x < img.getWidth(); ++x) {
int lum = LIM(0, int(Color::rgbLuminance(float(img.r(y, x)), float(img.g(y, x)), float(img.b(y, x)))), 255);
++ret[lum];
++ret.cdf[lum];
}
}
int sum = 0;
for (size_t i = 0; i < ret.size(); ++i) {
sum += ret[i];
ret[i] = sum;
for (size_t i = 0; i < ret.cdf.size(); ++i) {
if (ret.cdf[i] > 0) {
if (ret.min_val < 0) {
ret.min_val = i;
}
ret.max_val = i;
}
sum += ret.cdf[i];
ret.cdf[i] = sum;
}
return ret;
@ -105,12 +121,15 @@ void mappingToCurve(const std::vector<int> &mapping, std::vector<double> &curve)
[&](int start, int stop, int step, bool addstart) -> void
{
int prev = start;
if (addstart) {
if (addstart && mapping[start] >= 0) {
curve.push_back(coord(start));
curve.push_back(coord(mapping[start]));
}
for (int i = start; i < stop; ++i) {
int v = mapping[i];
if (v < 0) {
continue;
}
bool change = i > 0 && v != mapping[i-1];
int diff = i - prev;
if ((change && std::abs(diff - step) <= 1) || diff > step * 2) {
@ -120,20 +139,22 @@ void mappingToCurve(const std::vector<int> &mapping, std::vector<double> &curve)
}
}
};
doit(0, idx, idx > step ? step : idx / 2, true);
doit(idx, int(mapping.size()), step, idx - step > step / 2);
curve.push_back(0.0);
curve.push_back(0.0);
int start = 0;
while (start < idx && (mapping[start] < 0 || start < idx / 2)) {
++start;
}
doit(start, idx, idx > step ? step : idx / 2, true);
doit(idx, int(mapping.size()), step, idx - step > step / 2 && std::abs(curve[curve.size()-2] - coord(idx)) > 0.01);
if (curve.size() > 2 && (1 - curve[curve.size()-2] <= step / (256.0 * 3))) {
curve.pop_back();
curve.pop_back();
}
// remove the plateau at the end (if any)
size_t j = curve.size() / 2;
for (; j > 1; --j) {
if (std::abs(curve[2*(j-1)-1] - curve.back()) > 0.01) {
break;
}
}
curve.resize(2*j);
curve.push_back(1.0);
curve.push_back(1.0);
@ -259,14 +280,18 @@ void RawImageSource::getAutoMatchedToneCurve(std::vector<double> &outCurve)
target->resizeImgTo(source->getWidth(), source->getHeight(), TI_Nearest, tmp);
target.reset(tmp);
}
std::vector<int> scdf = getCdf(*source);
std::vector<int> tcdf = getCdf(*target);
CdfInfo scdf = getCdf(*source);
CdfInfo tcdf = getCdf(*target);
std::vector<int> mapping;
int j = 0;
for (size_t i = 0; i < tcdf.size(); ++i) {
j = findMatch(tcdf[i], scdf, j);
mapping.push_back(j);
for (int i = 0; i < int(tcdf.cdf.size()); ++i) {
j = findMatch(tcdf.cdf[i], scdf.cdf, j);
if (i >= tcdf.min_val && i <= tcdf.max_val && j >= scdf.min_val && j <= scdf.max_val) {
mapping.push_back(j);
} else {
mapping.push_back(-1);
}
}
mappingToCurve(mapping, outCurve);