我有以下画布有许多小(15x15)块:
我要做的是添加一个过滤器来移除框之间的边界,使图片更加一致,这样矩形就不会太明显而且很光滑。我可以使用什么过滤器或算法来实现这一目标?
请注意,我无法改变我创建画布的方式,我必须绘制一堆矩形才能制作最终形状。如果问题不够明确,请道歉。
答案 0 :(得分:2)
使用getImageData
在任意一个像素周围临时获取数据,并将它们在临时画布上融合在一起。在主画布上以drawImage
结尾。
var c = document.body.appendChild(document.createElement("canvas"));
var size = c.width = c.height = 50;
var tileSize = 5;
var padding = 0;
var ctx = c.getContext("2d");
c.style.width = c.style.height = "400px";
for (var x = 0; x < size; x += tileSize) {
for (var y = 0; y < size; y += tileSize) {
ctx.fillStyle = (Math.random() > 0.5 ? "#FF0000" : (Math.random() > 0.5 ? "#00FF00" : '#0000FF'));
ctx.fillRect(x + padding, y + padding, tileSize - padding, tileSize - padding);
}
}
function blur(canvas) {
var ctx = canvas.getContext("2d");
var tempCanvas = canvas.cloneNode();
var tempCTX = tempCanvas.getContext("2d");
var divideBy = 9;
for (var x = 0; x < size; x += 1) {
for (var y = 0; y < size; y += 1) {
var data = ctx.getImageData(x - 1, y - 1, 3, 3).data;
var arr = [0, 0, 0, 1];
for (var dataIndex = 0; dataIndex < data.length; dataIndex += 4) {
arr[0] += (data[dataIndex] === 0 ? 0 : data[dataIndex] / divideBy);
arr[1] += (data[dataIndex + 1] === 0 ? 0 : data[dataIndex + 1] / divideBy);
arr[2] += (data[dataIndex + 2] === 0 ? 0 : data[dataIndex + 2] / divideBy);
}
arr = arr.map(function(a) {
return Math.round(a);
});
tempCTX.fillStyle = "rgba(" + (arr.join(',')) + ")";
tempCTX.fillRect(x, y, 1, 1);
}
}
ctx.drawImage(tempCanvas, 0, 0);
}
//TEST
var output = document.body.appendChild(document.createElement("p"));
var step = 0;
function blurStep() {
blur(c);
step++;
output.innerHTML = "Blur step: " + step;
if (step < 20) {
setTimeout(blurStep, 1000 / 60);
}
}
setTimeout(blurStep, 1000);
&#13;
编辑1 - 边缘控制
轻微编辑,删除当我的函数超出现有画布时生成的黑色边框:
var output = document.body.appendChild(document.createElement("p"));
var c = document.body.appendChild(document.createElement("canvas"));
var size = c.width = c.height = 50;
var tileSize = 5;
var padding = 0;
var ctx = c.getContext("2d");
c.style.width = c.style.height = "800px";
for (var x = 0; x < size; x += tileSize) {
for (var y = 0; y < size; y += tileSize) {
ctx.fillStyle = (Math.random() > 0.5 ? "#FF0000" : (Math.random() > 0.5 ? "#00FF00" : '#0000FF'));
ctx.fillRect(x + padding, y + padding, tileSize - padding, tileSize - padding);
}
}
function blur(canvas) {
var ctx = canvas.getContext("2d");
var tempCanvas = canvas.cloneNode();
var tempCTX = tempCanvas.getContext("2d");
var divideBy = 9;
for (var x = 0; x < size; x += 1) {
for (var y = 0; y < size; y += 1) {
var X = (x >= 1 ? x - 1 : x);
var Y = (y >= 1 ? y - 1 : y);
var overflowX = size - X < 3;
var overflowY = size - Y < 3;
var data = ctx.getImageData(X, Y, 3, 3).data;
if (overflowX) {
var i = 8;
do {
data[i] = data[i - 4];
data[i + 1] = data[i - 4 + 1];
data[i + 2] = data[i - 4 + 2];
i += 12;
} while (i <= 32);
}
if (overflowY) {
var i = 24;
do {
data[i] = data[i - 12];
i++;
} while (i <= 32);
}
var arr = [0, 0, 0, 1];
for (var dataIndex = 0; dataIndex < data.length; dataIndex += 4) {
arr[0] += (data[dataIndex] === 0 ? 0 : data[dataIndex] / divideBy);
arr[1] += (data[dataIndex + 1] === 0 ? 0 : data[dataIndex + 1] / divideBy);
arr[2] += (data[dataIndex + 2] === 0 ? 0 : data[dataIndex + 2] / divideBy);
}
arr = arr.map(function(a) {
return Math.round(a);
});
tempCTX.fillStyle = "rgba(" + (arr.join(',')) + ")";
tempCTX.fillRect(x, y, 1, 1);
}
}
ctx.drawImage(tempCanvas, 0, 0);
}
//TEST
var step = 0;
var steps = 20;
function blurStep() {
blur(c);
step++;
output.innerHTML = "Blur step: " + step + "/" + steps;
if (step < steps) {
requestAnimationFrame(blurStep);
}
}
requestAnimationFrame(blurStep);
&#13;
答案 1 :(得分:2)
这样做比起真正的帮助更有趣。但是你可以得到基本的想法,请继续阅读:)
生成位图,获取纯色数组(例如imageData),获取图像宽度并将其乘以4([r,g,b,a]为一个像素)(some docs about manipulation inside canvas)
如果你愿意 - 你可以通过做一些“放置数据”循环并将方向乘以15来修改它以获得这种“块状”样式!
然后 - 做你想做的事。我会这样做的目的:
const channelResolution = 4; // if you don't have somehow alpha channel - make it 3.
const lineDistance = imageWidth * channelResolution;
const imageData = [ /* your ImageData from canvas */];
const [r, g, b, a] = [0, 1, 2, 3]; // directions inside channels
const [left, right, up, down] = [
-channelResolution,
channelResolution,
-lineDistance,
lineDistance]; // directions inside pixels
// return array of channel values in given directions
const scan = (data, scanChannel, scanPixel, scanDirections) => {
const scanResult = [];
scanResult.push(data[scanPixel + scanChannel]);
return scanResult.concat(scanDirections.map(direction => {
return data[scanPixel + scanChannel + direction];
}));
};
// mixer filter
const mixChannel = (array) => {
let sum = 0;
array.map(channel => sum+=channel);
return sum / (array.length + 1);
};
// blur edge filter/shader
const blurEdges = () => {
const resultData = clone(imageData); // (you can do json transformation)
for(let pointer = 0; pointer < imageData * channelResolution - channelResolution; pointer += channelResolution) {
const [red, green, blue, alpha] = [
mixChannel(scan(imageData, r, pointer, [up, left, down, right])),
mixChannel(scan(imageData, g, pointer, [up, left, down, right])),
mixChannel(scan(imageData, b, pointer, [up, left, down, right])),
mixChannel(scan(imageData, a, pointer, [up, left, down, right])),
];
resultData[pointer + r] = red;
resultData[pointer + g] = green;
resultData[pointer + b] = blue;
resultData[pointer + a] = alpha;
}
return resultData; // or putImageData
}
答案 2 :(得分:1)
有许多模糊方法。不幸的是,大多数都是错误的,导致图像比原始图像更暗。模糊不应该失去光明
我已经从 EmilS.Jørgensen中获取了代码并更正了所使用的数学,以便在图像模糊时图像亮度不受影响。
对于每个通道r,g,b,您可以得到每个邻近像素的平方均值,并使用其中的sqrt来设置新的模糊像素
因此,如果您在0-255范围内有3个像素(仅查看红色通道,r1,r2,r3)以获得平均亮度,则必须将对数通道值转换为线性光子计数。得到均值并转换回来。因此绿色和蓝色meanR = Math.sqrt*((r1 * r1 + r2 * r2 + r3 * r3)/3);
相同。不是alpha。
我改变的代码有点像黑客,我会在评论中尝试适合它的重要部分,但不适合
很容易看出差异,所以你甚至不需要并排。
var c = document.body.appendChild(document.createElement("canvas"));
var size = c.width = c.height = 50;
var tileSize = 5;
var padding = 0;
var ctx = c.getContext("2d");
c.style.width = c.style.height = "400px";
for (var x = 0; x < size; x += tileSize) {
for (var y = 0; y < size; y += tileSize) {
ctx.fillStyle = (Math.random() > 0.5 ? "#FF0000" : (Math.random() > 0.5 ? "#00FF00" : '#0000FF'));
ctx.fillRect(x + padding, y + padding, tileSize - padding, tileSize - padding);
}
}
function blur(canvas) {
var r,g,b,i,c;
var s = (v)=>Math.sqrt(v); // sqrt of a value
var a = () => d[i] * d[i++]; // sqrt pixel channel and step to next
var ctx = canvas.getContext("2d");
var tempCanvas = canvas.cloneNode();
var tempCTX = tempCanvas.getContext("2d");
var divideBy = 9;
for (var x = 0; x < size; x += 1) {
for (var y = 0; y < size; y += 1) {
var d = ctx.getImageData(x - 1, y - 1, 3, 3).data;
//============================
// Change start by BM67
//============================
c = i = r = g = b = 0;
while(i < 36){
if(d[i+3] !== 0){ r += a(); g += a(); b += a(); i ++; c++; }
else { i+= 4 }
}
tempCTX.fillStyle = `rgb(${s(r/c)|0},${s(g/c)|0},${s(b/c)|0})`;
//============================
// End change by BM67
//============================
tempCTX.fillRect(x, y, 1, 1);
}
}
ctx.drawImage(tempCanvas, 0, 0);
}
//TEST
var output = document.body.appendChild(document.createElement("p"));
var step = 0;
function blurStep() {
blur(c);
step++;
output.innerHTML = "Blur step: " + step;
if (step < 20) {
setTimeout(blurStep, 1000 / 60);
}
}
setTimeout(blurStep, 1000);