前面几篇介绍了在颜色空间对图像进行处理的技术,如亮度对比度调整、直方图均衡、曲线操作、颜色量化等。另一大类图像处理技术则是在图像自身的空间——二维像素矩阵空间——来进行操作。这类图像处理技术称为空间滤镜(Spatial Filters),通常简称为滤镜(Filters),他们是各大图像处理软件中必备的功能之一,往往还是某些软件的杀手锏功能。这是因为设计良好的滤镜往往能够带来非凡的图像处理效果,可以毫不夸张地说,各种功能各异的滤镜拥有点石成金、化腐朽为神奇的功能。
空间滤镜,简单来说就是对于图像中的每一个像素通过其邻居像素值来调整改变自身的操作。例如最简单的3x3滤镜,它可以表示为如下操作:
f(x, y) = sum( w(x+dx, y+dy) * p(x+dx, y+dy) ) / sum( w(x+dx, y+dy) )
其中dx, dy = {-1, 0, 1},w是加权函数, sum( * )表示对 * 求和。p(x, y)是在(x,y)位置的像素点的值,而f(x, y)则是经过滤镜处理之后的值。对于更大的滤镜,比如说5x5,7x7,或者更大,只需要将上面的式子中dx和dy的取值范围相应地改成[-2, 2]和[-3,3]或者[-(n-1)/2, (n-1)/2]就好了。值得注意的是,一般而言我们只考虑大小为奇数的滤镜,因为偶数大小的滤镜在实现上比奇数大小的滤镜复杂但是却没有特别的优势。此外我们可能需要在滤镜的输出值加上一个偏移量来获得满意的效果,这可以通过修改上面的式子,直接在最后加上偏移量来做到。
f(x, y) = sum( w(x+dx, y+dy) * p(x+dx, y+dy) ) / sum( w(x+dx, y+dy) ) + bias
根据上面的定义,空间滤镜实现起来非常容易:
function spatialfilter(src, f) {
// src is the source image, f is the filter
// width and height of the source image
var w = src.w, h = src.h;
// filter width and height
var wf = Math.floor((f.width - 1) / 2);
var hf = Math.floor((f.height - 1) / 2);
// filter weights
var weights = f.weights;
// filter bias
var bias = f.bias;
var invfactor = 1.0 / f.factor;
return src.map(function(c0, x, y, w, h) {
var r = 0, g = 0, b = 0;
for(var i=-hf, fi= 0, fidx = 0;i<=hf;i++, fi++) {
var py = clamp(i+y, 0, h-1);
for(var j=-wf, fj=0;j<=wf;j++, fj++, fidx++) {
var px = clamp(j+x, 0, w-1);
var wij = weights[fidx];
var cij = src.getPixel(px, py);
r += cij.r * wij;
g += cij.g * wij;
b += cij.b * wij;
}
}
r = r * invfactor + bias;
g = g * invfactor + bias;
b = b * invfactor + bias;
var c = new Color(r, g, b, c0.a);
return c.round().clamp();
});
}
上面的代码定义了通用的滤镜操作,具体的图像处理效果还得通过构造不同的滤镜来获得。例如最简单的高斯模糊滤镜定义如下:
function gaussianfilter(size, sigma) {
var weights = new Float32Array(size * size);
// create a gaussian blur filter
var cx = (size-1) * 0.5;
var cy = (size-1) * 0.5;
var r2 = 2.0 * sigma * sigma;
var wsum = 0;
for(var i= 0,idx=0;i<size;i++) {
var dy = i - cy;
for(var j=0;j<size;j++,idx++) {
var dx = j - cx;
weights[idx] = Math.exp(-(dx*dx + dy*dy) / (r2));
wsum += weights[idx];
}
}
return {
width: size,
height : size,
factor : wsum,
bias : 0,
weights : weights
};
}
对下面的海豹图像应用高斯模糊得到的效果
对高斯模糊滤镜稍加修改,即可得到锐化滤镜:
function sharpenfilter(size, sigma, amount) {
var f = gaussianfilter(size, sigma);
for(var i=0;i< f.weights.length;i++) {
f.weights[i] *= -1.0;
}
var mid = Math.floor((size * size - 1) / 2);
f.weights[mid] = f.factor + f.weights[mid] + amount;
f.factor = amount;
return f;
}
锐化滤镜的效果
图像边缘提取也可以很容易地实现,例如通过拉普拉斯算子来提取边缘:
function laplacian() {
return {
width : 3,
height : 3,
factor : 1.0,
bias : 0.0,
weights : [
-1, -1, -1,
-1, 8, -1,
-1, -1, -1
]
};
}
效果如下