📅  最后修改于: 2023-12-03 15:11:48.519000             🧑  作者: Mango
在图形处理领域,色调映射(Tone mapping)是一种将高动态范围(HDR)图像映射到低动态范围(LDR)显示器上的过程。
下面介绍三个常用的 JavaScript 库来实现色调映射。
Reinhard Tone Mapping 是最常用和最简单的映射方法之一,它通过简单的缩放和偏移图像的亮度和色度来实现。以下是使用 Reinhard Tone Mapping 的 JavaScript 代码:
function mapReinhard(image, key, phi) {
var ldr = [];
var sum = 0.0;
var pixels = image.width * image.height;
for (var i = 0; i < pixels; i++) {
var r = image.data[i * 4];
var g = image.data[i * 4 + 1];
var b = image.data[i * 4 + 2];
var lum = 0.27 * r + 0.67 * g + 0.06 * b;
sum += Math.log(lum + 0.00001);
}
var lavg = Math.exp(sum / pixels);
var scale = key / lavg;
var w = 1.0 / phi;
for (var i = 0; i < pixels; i++) {
var r = image.data[i * 4];
var g = image.data[i * 4 + 1];
var b = image.data[i * 4 + 2];
var lum = 0.27 * r + 0.67 * g + 0.06 * b;
var mapped = (lum * scale) / (1.0 + lum * scale);
var rnew = r * mapped / lum;
var gnew = g * mapped / lum;
var bnew = b * mapped / lum;
var color = [rnew, gnew, bnew];
for (var j = 0; j < 3; j++) {
color[j] = Math.pow(color[j], w);
}
ldr.push(color[0] * 255);
ldr.push(color[1] * 255);
ldr.push(color[2] * 255);
ldr.push(255);
}
return new ImageData(new Uint8ClampedArray(ldr), image.width, image.height);
}
Fattal Tone Mapping 是一种较为复杂的方法,它被证明在视觉上是一种自然的映射方法。以下是使用 Fattal Tone Mapping 的 JavaScript 代码:
function mapFattal(image, alpha, beta, saturation) {
var MAX_ITERATION = 50;
var ldr = [];
var logLuminance = [];
var pixels = image.width * image.height;
for (var i = 0; i < pixels; i++) {
var r = image.data[i * 4];
var g = image.data[i * 4 + 1];
var b = image.data[i * 4 + 2];
var lum = 0.27 * r + 0.67 * g + 0.06 * b;
logLuminance.push(Math.log(lum + 0.00001));
}
var A = Math.max.apply(null, logLuminance);
var t = 1.0;
for (var n = 0; n < MAX_ITERATION; n++) {
var denominator = 0.0;
var Luminance = [];
for (var i = 0; i < pixels; i++) {
var r = image.data[i * 4];
var g = image.data[i * 4 + 1];
var b = image.data[i * 4 + 2];
var lum = 0.27 * r + 0.67 * g + 0.06 * b;
var logDelta = Math.log(lum + 0.00001) - A - Math.log(t);
var weight = 1.0 / (1.0 + logDelta * logDelta);
denominator += weight;
Luminance.push(weight * lum);
}
var Lw = Luminance.reduce(function (a, b) {
return a + b;
}, 0) / denominator;
var Ld = alpha * logLuminance.map(function (x) {
return x - A + beta * Math.log(Lw);
});
var Ls = logLuminance.map(function (x) {
return x - Math.min.apply(null, Ld);
});
var LFinal = Ls.map(function (x) {
return saturation * x / (x + 1.0);
});
for (var i = 0; i < pixels; i++) {
var r = image.data[i * 4];
var g = image.data[i * 4 + 1];
var b = image.data[i * 4 + 2];
var lum = 0.27 * r + 0.67 * g + 0.06 * b;
var logDelta = Math.log(lum + 0.00001) - A - Math.log(t);
var weight = 1.0 / (1.0 + logDelta * logDelta);
var mapped = LFinal[i] / lum;
var rnew = r * mapped / lum;
var gnew = g * mapped / lum;
var bnew = b * mapped / lum;
ldr.push(rnew * 255);
ldr.push(gnew * 255);
ldr.push(bnew * 255);
ldr.push(255);
}
t = Math.exp(Math.log(t) - Math.log(Lw / (alpha * Ld.reduce(function (a, b) {
return a + b;
}, 0))));
}
return new ImageData(new Uint8ClampedArray(ldr), image.width, image.height);
}
Mantiuk Tone Mapping 是一种基于科学数据和人类感知的算法,它在将 HDR 图像映射到 LDR 显示器上时表现出色彩和亮度的平衡。以下是使用 Mantiuk Tone Mapping 的 JavaScript 代码:
function mapMantiuk(image, alpha, beta, delta) {
var ldr = [];
var w = image.width;
var h = image.height;
var pixels = w * h;
var luminance = new Float32Array(pixels);
var chrominance = new Float32Array(pixels * 2);
for (var i = 0; i < pixels; i++) {
var r = image.data[i * 4];
var g = image.data[i * 4 + 1];
var b = image.data[i * 4 + 2];
luminance[i] = 0.27 * r + 0.67 * g + 0.06 * b;
chrominance[i * 2] = r;
chrominance[i * 2 + 1] = b;
}
var LW = Math.pow(luminance.reduce(function (a, b) {
return a + b;
}, 0) / (w * h), alpha);
var Lv = new Float32Array(pixels);
for (var i = 0; i < pixels; i++) {
Lv[i] = delta * luminance[i] / (1 + LW);
}
var Lwhite = Math.pow(Math.max.apply(null, luminance), alpha);
var ldr = new Uint8ClampedArray(pixels * 4);
for (var i = 0; i < pixels; i++) {
var lum = Math.pow(luminance[i], alpha);
lum = lum * (1 + lum / Math.pow(Lwhite, alpha)) / (1 + lum);
lum = lum * (1 + Lv[i] / luminance[i]);
lum = lum * 0.18 / luminance[i];
lum = lum * beta;
var r = chrominance[i * 2];
var g = luminance[i];
var b = chrominance[i * 2 + 1];
var norm = Math.sqrt(r * r + b * b);
r = r / norm;
b = b / norm;
r = (r + 1) / 2;
g = (g - 0.05) / 0.95;
b = (b + 1) / 2;
ldr[i * 4] = Math.floor(r * 255);
ldr[i * 4 + 1] = Math.floor(g * 255);
ldr[i * 4 + 2] = Math.floor(b * 255);
ldr[i * 4 + 3] = 255;
}
return new ImageData(ldr, w, h);
}
以上是三个常用的 JavaScript 库实现色调映射。根据实际需求,选择适合的色调映射方法可以提高图像的显示质量。