我一直在尝试使用javascript学习过滤器,一直在关注
https://www.html5rocks.com/en/tutorials/canvas/imagefilters/ 本教程。
我遇到了一些我不了解的代码,有什么机构可以帮助我理解这些代码。
Filters.convolute = function(pixels, weights, opaque) {
var side = Math.round(Math.sqrt(weights.length));
var halfSide = Math.floor(side/2);
var src = pixels.data;
var sw = pixels.width;
var sh = pixels.height;
// pad output by the convolution matrix
var w = sw;
var h = sh;
var output = Filters.createImageData(w, h);
var dst = output.data;
// go through the destination image pixels
var alphaFac = opaque ? 1 : 0;
for (var y=0; y<h; y++) {
for (var x=0; x<w; x++) {
var sy = y;
var sx = x;
var dstOff = (y*w+x)*4;
// calculate the weighed sum of the source image pixels that
// fall under the convolution matrix
var r=0, g=0, b=0, a=0;
for (var cy=0; cy<side; cy++) {
for (var cx=0; cx<side; cx++) {
var scy = sy + cy - halfSide;
var scx = sx + cx - halfSide;
if (scy >= 0 && scy < sh && scx >= 0 && scx < sw) {
var srcOff = (scy*sw+scx)*4;
var wt = weights[cy*side+cx];
r += src[srcOff] * wt;
g += src[srcOff+1] * wt;
b += src[srcOff+2] * wt;
a += src[srcOff+3] * wt;
}
}
}
dst[dstOff] = r;
dst[dstOff+1] = g;
dst[dstOff+2] = b;
dst[dstOff+3] = a + alphaFac*(255-a);
}
}
return output;
};
side和halfSide是什么,为什么使用4 for nested loop。我被困在这里就像很多天。
答案 0 :(得分:0)
我确实和您一样,我正在尝试使用Javascript-TypeScript实现卷积过滤器。
之所以为4,是因为我们有r,g,b,a
where r = red
where g = green
where b = blue
where a = alpha
此图像数据位于类型为Uint8ClampedArray
的数组中
您可以通过以下方式获得此信息:
const width = canvas.width;
const height = canvas.height;
const imageData = ctx.getImageData(0, 0, width, height);
,然后获取真实图像数据: const pixel = imageData.data;
像素数据是Uint8ClampedArray
的一种,可以这样表示:
[r, g, b, a, r, g, b, a, r, g, b, a ]
,数组中的每4个元素都会获得像素索引,内核中心每1.5次获得像素索引,但这取决于内核大小3x3或9x9
const image = imageData.data
唯一对我有用的代码是这个。
init() {
const img = new Image();
const img2 = new Image();
img.src = '../../../assets/graffiti.jpg';
img2.src = '../../../assets/graffiti.jpg';
const canvas: HTMLCanvasElement = this.canvas1.nativeElement;
const canvas2: HTMLCanvasElement = this.canvas2.nativeElement;
const ctx: CanvasRenderingContext2D = canvas.getContext('2d');
const ctx2: CanvasRenderingContext2D = canvas2.getContext('2d');
this.onImgLoad(img, ctx, canvas.width, canvas.height);
this.input(img2, ctx2, canvas2.width, canvas2.height);
}
input(img, ctx: CanvasRenderingContext2D, width, height) {
img.onload = () => {
ctx.drawImage(img, 0, 0);
};
}
onImgLoad(img, ctx: CanvasRenderingContext2D, width, height) {
img.onload = () => {
ctx.drawImage(img, 0, 0);
const kernelArr = new Kernel([
[0, 1, 0],
[0, 1, 0],
[0, 1, 0],
]);
const kernel = [
0, 1, 0,
0, 1, 0,
0, 1, 0
];
console.log(kernel);
const newImg = new Filter2D(ctx, width, height);
// const imgData = newImg.inverse(width, height); // applys inverse filter
const imgData = newImg.applyKernel(kernel);
ctx.putImageData(imgData, 0, 0);
};
}
class Filter2D {
width: number;
height: number;
ctx: CanvasRenderingContext2D;
imgData: ImageData;
constructor(ctx: CanvasRenderingContext2D, width: number, height: number) {
this.width = width;
this.height = height;
this.ctx = ctx;
this.imgData = ctx.getImageData(0, 0, width, height);
console.log(this.imgData);
}
grey(width: number, height: number): ImageData {
return this.imgData;
}
inverse(width: number, height: number): ImageData {
console.log('Width: ', width);
console.log('Height: ', height);
const pixels = this.imgData.data;
for (let i = 0; i < pixels.length; i += 4) {
pixels[i] = 255 - pixels[i]; // red
pixels[i + 1] = 255 - pixels[i + 1]; // green
pixels[i + 2] = 255 - pixels[i + 2]; // blue
}
return this.imgData;
}
applyKernel(kernel: any[]): ImageData {
const k1: number[] = [
1, 0, -1,
2, 0, -2,
1, 0, -1
];
const k2: number[] = [
-1, -1, -1,
-1, 8, -1,
-1, -1, -1
];
kernel = k2;
const dim = Math.sqrt(kernel.length);
const pad = Math.floor(dim / 2);
const pixels: Uint8ClampedArray = this.imgData.data;
const width: number = this.imgData.width;
const height: number = this.imgData.height;
console.log(this.imgData);
console.log('applyKernelMethod start');
console.log('Width: ', width);
console.log('Height: ', height);
console.log('kernel: ', kernel);
console.log('dim: ', dim); // 3
console.log('pad: ', pad); // 1
console.log('dim % 2: ', dim % 2); // 1
console.log('pixels: ', pixels);
if (dim % 2 !== 1) {
console.log('Invalid kernel dimension');
}
let pix, i, r, g, b;
const w = width;
const h = height;
const cw = w + pad * 2; // add padding
const ch = h + pad * 2;
for (let row = 0; row < height; row++) {
for (let col = 0; col < width; col++) {
r = 0;
g = 0;
b = 0;
for (let kx = -pad; kx <= pad; kx++) {
for (let ky = -pad; ky <= pad; ky++) {
i = (ky + pad) * dim + (kx + pad); // kernel index
pix = 4 * ((row + ky) * cw + (col + kx)); // image index
r += pixels[pix++] * kernel[i];
g += pixels[pix++] * kernel[i];
b += pixels[pix ] * kernel[i];
}
}
pix = 4 * ((row - pad) * w + (col - pad)); // destination index
pixels[pix++] = r;
pixels[pix++] = g;
pixels[pix++] = b;
pixels[pix ] = 255; // we want opaque image
}
}
console.log(pixels);
return this.imgData;
}
}