请求解析图像卷积矩阵算法原理及模糊效果示例代码
Alright, let's unpack this convolution code line by line to see exactly how it creates that soft blur effect. First, a quick recap of the core idea: Convolution works by sliding a small matrix (your weights array here) over every pixel in the image. For each output pixel, we calculate a weighted sum of the surrounding source pixels that sit under the matrix. For blur, these weights are usually equal (or normalized) so we're just averaging nearby pixels.
Now let's walk through the code step by step:
1. Setup: Temporary Canvas Helper
Filters.tmpCanvas = document.createElement('canvas'); Filters.tmpCtx = Filters.tmpCanvas.getContext('2d'); Filters.createImageData = function(w,h) { return this.tmpCtx.createImageData(w,h); };
- We create an off-screen canvas and its context here—this is just a quick helper to generate empty
ImageDataobjects later. Browsers require a canvas context to usecreateImageData, so this wrapper makes it easier to spin up blank image data of a specific width/height.
2. The Main Convolution Function: Filters.convolute
This is where all the action happens. Let's start with its parameters:
pixels: The source image data (fromcanvas.getContext('2d').getImageData()). It has adataarray with raw RGBA values (each pixel uses 4 entries: red, green, blue, alpha) pluswidthandheightproperties.weights: The convolution matrix, flattened into a single array. For example, a 3x3 blur matrix would be[1,1,1,1,1,1,1,1,1](you'd usually normalize this to1/9per entry to keep brightness consistent).opaque: A boolean flag to force the output image to be fully opaque, if needed.
Step 2.1: Figure Out the Matrix Size
var side = Math.round(Math.sqrt(weights.length)); var halfSide = Math.floor(side/2);
- Since
weightsis a flattened array (like 9 elements for a 3x3 matrix), we find the matrix's side length by taking the square root of the array length. halfSidetells us how far the matrix extends from the center pixel. For a 3x3 matrix, this is 1—meaning we look 1 pixel up, down, left, and right from the current pixel.
Step 2.2: Prep Source & Destination Data
var src = pixels.data; var sw = pixels.width; var sh = pixels.height; var w = sw; var h = sh; var output = Filters.createImageData(w, h); var dst = output.data;
srcis a direct reference to the source image's raw RGBA data array.swandshstore the source image's width and height.- We create an
outputImageData object (same size as the source) to hold our blurred result, anddstpoints to its raw data array where we'll write the final pixel values.
Step 2.3: Opaque Handling Setup
var alphaFac = opaque ? 1 : 0;
- If
opaqueis true, we'll force every pixel's alpha channel to 255 (fully opaque) later. ThisalphaFacmultiplier makes that calculation straightforward.
Step 2.4: Loop Through Every Output Pixel
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; // ... weighted sum calculation ... // ... write values to destination ... } }
- We loop over every x/y coordinate in the output image—this means we process one pixel at a time.
dstOffcalculates the index in thedstarray for the current pixel: each pixel takes 4 entries (RGBA), so we multiply the pixel's linear position (y*w + x) by 4 to get the starting index.
Step 2.5: Calculate the Weighted Sum for the Current Pixel
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; } } }
- We initialize
r,g,b,ato 0—these will hold the running total of weighted values for each color channel. - The inner loops iterate over every cell in the convolution matrix (
cy= matrix row,cx= matrix column). scyandscxcalculate which source pixel corresponds to the current matrix cell: we offset from the destination pixel (sy/sx) bycy - halfSideandcx - halfSide—this centers the matrix perfectly on the destination pixel.- The
ifcheck ensures we don't try to access pixels outside the source image (like negative coordinates or ones beyond the image's width/height—no out-of-bounds errors here!). srcOffis the index in the source data array for the current source pixel we're looking at.wtis the weight from the convolution matrix for this cell. We multiply each source channel value by this weight and add it to our running totals. For blur, this is like averaging all the pixels in the matrix.
Step 2.6: Write the Calculated Values to the Output Pixel
dst[dstOff] = r; dst[dstOff+1] = g; dst[dstOff+2] = b; dst[dstOff+3] = a + alphaFac*(255-a);
- We set the red, green, and blue channels of the output pixel to our weighted sums.
- For the alpha channel: if
opaqueis true (alphaFac=1), we computea + 1*(255 - a)which simplifies to 255—forcing full opacity. Ifopaqueis false,alphaFac=0, so we just use the weighted alpha sum we calculated.
3. Return the Blurred Result
return output;
Finally, we return the ImageData object with our blurred image. You can draw this to a canvas using ctx.putImageData(output, 0, 0) to see the effect.
Quick Example for Blur
If you pass a 3x3 blur matrix like [1/9, 1/9, 1/9, 1/9, 1/9, 1/9, 1/9, 1/9, 1/9], each output pixel is just the average of the 3x3 grid around the source pixel. This averages out small details, creating that classic soft blur.
内容的提问来源于stack exchange,提问作者Andreea

