cors 污染图像上的映射颜色变换
Mapped color transformation on cors tainted image
我需要转换从不允许 crossOrigin 的 s3 服务器获取的图像中的颜色。
这是我需要的功能:
const img = new Image();
img.src = src;
ctx.drawImage(img, 0, 0);
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const colors = [25,50,100,255];
const data = imageData.data;
for (let i = 0; i < data.length; i += 4) {
if(data[i] == 1){data[i] = colors[1]}
if(data[i] == 2){data[i] = colors[2]}
if(data[i] == 3){data[i] = colors[3]}
}
但被 crossOrigin 错误污染:
我知道在这种情况下我不能使用getImageData
。我不想读取图像数据。
但也许可以通过其他一些 webGL / canvas 操作来完成我的任务?
无法在服务器上呈现或代理。
WebGL 根本不适用于跨源纹理。草案中有an extension,但尚未标准化,只允许在有限的场景下使用then,所以使用此类纹理的采样器仍然不能用于条件表达式。
使用经典canvas,您可以通过巧妙地使用混合模式来实现大量的色彩效果。有一个列表 on MDN, or a more technical description on W3.
特别是,一个非常有用的技巧涉及使用 color-dodge
操作。通过使用非常明亮的恒定颜色作为源,它有效地将背景图像乘以一个大值,这让我们可以进行阈值操作。
在您的情况下,您可以使用以下代码 "select" 具有特定颜色的所有像素:
// 1. Use 'difference' to make all matching pixels black
ctx.globalCompositeOperation = "difference";
ctx.fillStyle = srcColor;
ctx.fillRect(0, 0, w, h);
// 2. Use 'color-dodge' trick to turn all non-black pixels into white
ctx.globalCompositeOperation = "color-dodge";
ctx.fillStyle = "#fefefe";
ctx.fillRect(0, 0, w, h);
// Steps 3 and 4 are only necessary if full RGB matching is required
// Without these steps, matching will be done on per-channel basis
// 3*. Desaturate the image, ensuring all three channels have the same value
ctx.globalCompositeOperation = "saturation";
ctx.fillStyle = "#ffffff";
ctx.fillRect(0, 0, w, h);
// 4*. Use color-dodge again, to mask pixels where all 3 components matched
ctx.globalCompositeOperation = "color-dodge";
ctx.fillStyle = "#fefefe";
ctx.fillRect(0, 0, w, h);
// 5. Invert the image to make matching pixels white and the rest black
ctx.globalCompositeOperation = "difference";
ctx.fillStyle = "#ffffff";
ctx.fillRect(0, 0, w, h);
// 6. Multiply by desired color
ctx.globalCompositeOperation = "multiply";
ctx.fillStyle = dstColor;
ctx.fillRect(0, 0, w, h);
这是一个实现您的代码的实例(输出图像的左半部分看起来是黑色的,因为您看不到像 rgb(1,0,0)
这样的深色):
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
function makeImage(w, h) {
const c = document.createElement("canvas");
c.width = w;
c.height = h;
return c;
}
function paintSource(c) {
const ctx = c.getContext("2d");
const data = ctx.getImageData(0, 0, c.width, c.height);
for (let x = 0; x < c.width; ++x) {
for (let y = 0; y < c.height; ++y) {
const i = (y * c.width + x) * 4;
const v = Math.round((x + y) / 2);
// 1, 2 or 3
data.data[i] = Math.floor(x * 3 / c.width) + 1;
data.data[i + 3] = 255;
}
}
ctx.putImageData(data, 0, 0);
}
function selectColor(srcCanvas, dstCanvas, srcColor, dstColor) {
const ctx = dstCanvas.getContext("2d");
ctx.drawImage(srcCanvas, 0, 0);
const w = srcCanvas.width, h = srcCanvas.height;
ctx.globalCompositeOperation = "difference";
ctx.fillStyle = srcColor;
ctx.fillRect(0, 0, w, h);
ctx.globalCompositeOperation = "color-dodge";
ctx.fillStyle = "#fefefe";
ctx.fillRect(0, 0, w, h);
ctx.globalCompositeOperation = "difference";
ctx.fillStyle = "#ffffff";
ctx.fillRect(0, 0, w, h);
ctx.globalCompositeOperation = "multiply";
ctx.fillStyle = dstColor;
ctx.fillRect(0, 0, w, h);
}
const source = makeImage(256, 256);
paintSource(source);
const c1 = makeImage(256, 256);
selectColor(source, c1, "rgb(1,0,0)", "rgb(50,0,0)");
const c2 = makeImage(256, 256);
selectColor(source, c2, "rgb(2,0,0)", "rgb(100,0,0)");
const c3 = makeImage(256, 256);
selectColor(source, c3, "rgb(3,0,0)", "rgb(255,0,0)");
ctx.drawImage(source, 0, 0);
ctx.globalCompositeOperation = "lighter";
ctx.drawImage(c1, 256, 0);
ctx.drawImage(c2, 256, 0);
ctx.drawImage(c3, 256, 0);
<canvas id="canvas" width="512" height="256"></canvas>
这里有一个稍微复杂一点的例子来演示阈值:https://jsfiddle.net/Rivvy/kq2ga90z/35/
我需要转换从不允许 crossOrigin 的 s3 服务器获取的图像中的颜色。
这是我需要的功能:
const img = new Image();
img.src = src;
ctx.drawImage(img, 0, 0);
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const colors = [25,50,100,255];
const data = imageData.data;
for (let i = 0; i < data.length; i += 4) {
if(data[i] == 1){data[i] = colors[1]}
if(data[i] == 2){data[i] = colors[2]}
if(data[i] == 3){data[i] = colors[3]}
}
但被 crossOrigin 错误污染:
我知道在这种情况下我不能使用getImageData
。我不想读取图像数据。
但也许可以通过其他一些 webGL / canvas 操作来完成我的任务?
无法在服务器上呈现或代理。
WebGL 根本不适用于跨源纹理。草案中有an extension,但尚未标准化,只允许在有限的场景下使用then,所以使用此类纹理的采样器仍然不能用于条件表达式。
使用经典canvas,您可以通过巧妙地使用混合模式来实现大量的色彩效果。有一个列表 on MDN, or a more technical description on W3.
特别是,一个非常有用的技巧涉及使用 color-dodge
操作。通过使用非常明亮的恒定颜色作为源,它有效地将背景图像乘以一个大值,这让我们可以进行阈值操作。
在您的情况下,您可以使用以下代码 "select" 具有特定颜色的所有像素:
// 1. Use 'difference' to make all matching pixels black
ctx.globalCompositeOperation = "difference";
ctx.fillStyle = srcColor;
ctx.fillRect(0, 0, w, h);
// 2. Use 'color-dodge' trick to turn all non-black pixels into white
ctx.globalCompositeOperation = "color-dodge";
ctx.fillStyle = "#fefefe";
ctx.fillRect(0, 0, w, h);
// Steps 3 and 4 are only necessary if full RGB matching is required
// Without these steps, matching will be done on per-channel basis
// 3*. Desaturate the image, ensuring all three channels have the same value
ctx.globalCompositeOperation = "saturation";
ctx.fillStyle = "#ffffff";
ctx.fillRect(0, 0, w, h);
// 4*. Use color-dodge again, to mask pixels where all 3 components matched
ctx.globalCompositeOperation = "color-dodge";
ctx.fillStyle = "#fefefe";
ctx.fillRect(0, 0, w, h);
// 5. Invert the image to make matching pixels white and the rest black
ctx.globalCompositeOperation = "difference";
ctx.fillStyle = "#ffffff";
ctx.fillRect(0, 0, w, h);
// 6. Multiply by desired color
ctx.globalCompositeOperation = "multiply";
ctx.fillStyle = dstColor;
ctx.fillRect(0, 0, w, h);
这是一个实现您的代码的实例(输出图像的左半部分看起来是黑色的,因为您看不到像 rgb(1,0,0)
这样的深色):
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
function makeImage(w, h) {
const c = document.createElement("canvas");
c.width = w;
c.height = h;
return c;
}
function paintSource(c) {
const ctx = c.getContext("2d");
const data = ctx.getImageData(0, 0, c.width, c.height);
for (let x = 0; x < c.width; ++x) {
for (let y = 0; y < c.height; ++y) {
const i = (y * c.width + x) * 4;
const v = Math.round((x + y) / 2);
// 1, 2 or 3
data.data[i] = Math.floor(x * 3 / c.width) + 1;
data.data[i + 3] = 255;
}
}
ctx.putImageData(data, 0, 0);
}
function selectColor(srcCanvas, dstCanvas, srcColor, dstColor) {
const ctx = dstCanvas.getContext("2d");
ctx.drawImage(srcCanvas, 0, 0);
const w = srcCanvas.width, h = srcCanvas.height;
ctx.globalCompositeOperation = "difference";
ctx.fillStyle = srcColor;
ctx.fillRect(0, 0, w, h);
ctx.globalCompositeOperation = "color-dodge";
ctx.fillStyle = "#fefefe";
ctx.fillRect(0, 0, w, h);
ctx.globalCompositeOperation = "difference";
ctx.fillStyle = "#ffffff";
ctx.fillRect(0, 0, w, h);
ctx.globalCompositeOperation = "multiply";
ctx.fillStyle = dstColor;
ctx.fillRect(0, 0, w, h);
}
const source = makeImage(256, 256);
paintSource(source);
const c1 = makeImage(256, 256);
selectColor(source, c1, "rgb(1,0,0)", "rgb(50,0,0)");
const c2 = makeImage(256, 256);
selectColor(source, c2, "rgb(2,0,0)", "rgb(100,0,0)");
const c3 = makeImage(256, 256);
selectColor(source, c3, "rgb(3,0,0)", "rgb(255,0,0)");
ctx.drawImage(source, 0, 0);
ctx.globalCompositeOperation = "lighter";
ctx.drawImage(c1, 256, 0);
ctx.drawImage(c2, 256, 0);
ctx.drawImage(c3, 256, 0);
<canvas id="canvas" width="512" height="256"></canvas>
这里有一个稍微复杂一点的例子来演示阈值:https://jsfiddle.net/Rivvy/kq2ga90z/35/