14.26: adaptive Contrast, Highlights and Shadows; Dehaze, Texture, Clarity and Sharpening

This commit is contained in:
ZacharyZhang-NY committed 2026-10-11 13:33:45 -04:00
1 parent a1453df517
commit 32f6362787
12 files changed
+289 -153

No files matched your search

+5 -4
View File
@@ -135,10 +135,11 @@ QImage CameraRawSettings::apply(const QImage &image, std::optional<CameraRawClip
if (!clipping && !sharpenMask && settings.calibration.adjusts())
settings.applyCalibration(pixels, width, height, stride);
if (settings.adjustsLight() || settings.adjustsColor() || clipping) {
const CameraRawTables::Stages stages = CameraRawTables::stages(settings);
const std::vector<float> before = CameraRawTables::compose(stages.before), after = CameraRawTables::compose(stages.after);
adjust_camera_raw(pixels, w, h, s, before.empty() ? nullptr : before.data(), after.empty() ? nullptr : after.data(), CameraRawTables::grid,
settings.highlights, settings.shadows, mode);
const CameraRawTables::Brightness brightness = settings.contrast != 0 || settings.highlights != 0 || settings.shadows != 0
? CameraRawTables::brightness(pixels, width, height, stride)
: CameraRawTables::Brightness();
const std::vector<float> table = CameraRawTables::compose(CameraRawTables::stages(settings, brightness));
adjust_camera_raw(pixels, w, h, s, table.empty() ? nullptr : table.data(), CameraRawTables::grid, mode);
}
if (paintColor)
settings.applyCurveColor(pixels, width, height, stride, visualizePointColor);
+46 -12
View File
@@ -4,6 +4,7 @@
#include "Logging.h"
#include <QFile>
#include <array>
#include <algorithm>
#include <stdexcept>
#include <zlib.h>
@@ -11,9 +12,11 @@
namespace CameraRawTables {
namespace {
constexpr size_t entries = size_t(size) * size * size * 3;
// White balance 9 × 9, then the six other sliders.
constexpr int exposureStart = 81, contrastStart = 102, whitesStart = 111, blacksStart = 120;
constexpr int saturationStart = 129, vibranceStart = 138, count = 147;
// White balance, five sliders, then three adaptive ones.
constexpr int exposureStart = 81, whitesStart = 102, blacksStart = 111, saturationStart = 120, vibranceStart = 129;
constexpr int contrastStart = 138, highlightsStart = 187, shadowsStart = 236, count = 285;
constexpr std::array<double, 7> brightnesses{53, 80, 104, 130, 160, 190, 224};
constexpr std::array<double, 7> adaptiveValues{-100, -50, -25, 0, 25, 50, 100};
// Raw DEFLATE, as Foundation's `.zlib` writes it.
std::vector<uint8_t> inflated(const QByteArray &packed, size_t expected)
@@ -83,6 +86,26 @@ Between between(double value, double low, double step, int tables)
return {index, position - index};
}
// An adaptive slider: four tables round value and brightness.
Between between(double value, const std::array<double, 7> &points)
{
const double held = std::min(points.back(), std::max(points.front(), value));
int index = 0;
while (index < int(points.size()) - 2 && held > points[index + 1])
++index;
return {index, (held - points[index]) / (points[index + 1] - points[index])};
}
// An adaptive slider: four tables round its value and brightness.
CameraRawStage adaptive(int start, double value, double brightness)
{
const Between row = between(brightness, brightnesses), column = between(value, adaptiveValues);
const double down = row.fraction, across = column.fraction;
const int width = int(adaptiveValues.size()), base = start + row.index * width + column.index;
return CameraRawStage{{table(base), table(base + 1), table(base + width), table(base + width + 1)},
{float((1 - down) * (1 - across)), float((1 - down) * across), float(down * (1 - across)), float(down * across)}};
}
CameraRawStage stage(int start, double value, double low, double step, int tables)
{
const Between at = between(value, low, step, tables);
@@ -106,23 +129,34 @@ CameraRawStage whiteBalance(double temperature, double tint)
{float((1 - down) * (1 - across)), float((1 - down) * across), float(down * (1 - across)), float(down * across)}};
}
Stages stages(const CameraRawSettings &settings)
Brightness brightness(const uchar *pixels, int width, int height, qsizetype stride)
{
Stages out;
double out[3] = {0, 0, 0};
camera_raw_statistics(pixels, size_t(width), size_t(height), size_t(stride), out);
return {out[0] * 255, out[1] * 255, out[2] * 255};
}
std::vector<CameraRawStage> stages(const CameraRawSettings &settings, const Brightness &brightness)
{
std::vector<CameraRawStage> out;
if (settings.exposure != 0)
out.before.push_back(stage(exposureStart, settings.exposure, -5, 0.5, 21));
out.push_back(stage(exposureStart, settings.exposure, -5, 0.5, 21));
if (settings.temperature != 0 || settings.tint != 0)
out.before.push_back(whiteBalance(settings.temperature, settings.tint));
out.push_back(whiteBalance(settings.temperature, settings.tint));
if (settings.contrast != 0)
out.before.push_back(stage(contrastStart, settings.contrast, -100, 25, 9));
out.push_back(adaptive(contrastStart, settings.contrast, brightness.brightest));
if (settings.highlights != 0)
out.push_back(adaptive(highlightsStart, settings.highlights, brightness.logBrightest));
if (settings.shadows != 0)
out.push_back(adaptive(shadowsStart, settings.shadows, brightness.luminance));
if (settings.whites != 0)
out.after.push_back(stage(whitesStart, settings.whites, -100, 25, 9));
out.push_back(stage(whitesStart, settings.whites, -100, 25, 9));
if (settings.blacks != 0)
out.after.push_back(stage(blacksStart, settings.blacks, -100, 25, 9));
out.push_back(stage(blacksStart, settings.blacks, -100, 25, 9));
if (settings.saturation != 0)
out.after.push_back(stage(saturationStart, settings.saturation, -100, 25, 9));
out.push_back(stage(saturationStart, settings.saturation, -100, 25, 9));
if (settings.vibrance != 0)
out.after.push_back(stage(vibranceStart, settings.vibrance, -100, 25, 9));
out.push_back(stage(vibranceStart, settings.vibrance, -100, 25, 9));
return out;
}
+9 -6
View File
@@ -8,7 +8,7 @@ extern "C" {
#include <optional>
#include <vector>
// Photoshop's Light and Color sliders, measured as 17³ colour tables.
// Photoshop's Light and Color, measured as 17³ tables; some adapt.
namespace CameraRawTables {
constexpr int size = 17;
// The grid the stages compose onto, finer than the tables.
@@ -18,12 +18,15 @@ constexpr int grid = 33;
const std::vector<uint8_t> &tables();
// Temperature and Tint: the four grid tables around them.
CameraRawStage whiteBalance(double temperature, double tint);
struct Stages {
std::vector<CameraRawStage> before;
std::vector<CameraRawStage> after;
// What adaptive sliders read first, in sRGB levels.
struct Brightness {
double brightest = 128;
double luminance = 128;
double logBrightest = 128;
};
// Before Highlights and Shadows, and after; zero sliders left out.
Stages stages(const CameraRawSettings &settings);
Brightness brightness(const uchar *pixels, int width, int height, qsizetype stride);
// Camera Raw's order, sliders at zero left out.
std::vector<CameraRawStage> stages(const CameraRawSettings &settings, const Brightness &brightness);
// The stages over the composing grid; empty without stages.
std::vector<float> compose(const std::vector<CameraRawStage> &stages);
// Temperature and Tint turning a straight sRGB colour gray.