平滑也称模糊, 是一项简单且使用频率很高的图像处理方法。
平滑处理时需要用到一个滤波器
。 最常用的滤波器是线性 滤波器,线性滤波处理的输出像素值(例如:)是输入像素值(例如:)的加权平均:
称为核
, 它仅仅是一个加权系数。这里涉及一种叫做“卷积”的运算,那么卷积是什么呢?
卷积是在每一个图像块与某个算子(核)之间进行的运算。
核?!
nbsp;dsds
核就是一个固定大小的数值数组。该数组带有一个锚点
,一般位于数组中央。可是这怎么运算啊?
假如你想得到图像的某个特定位置的卷积值,可用下列方法计算:
将核的锚点放在该特定位置的像素上,同时,核内的其他值与该像素邻域的各像素重合;将核内各值与相应像素值相乘,并将乘积相加;将所得结果放到与锚点对应的像素上;对图像所有像素重复上述过程。用公式表示上述过程如下:
在图像边缘的卷积怎么办呢?
计算卷积前,需要通过复制源图像的边界创建虚拟像素,这样边缘的地方也有足够像素计算卷积了。这就是为什么上一篇文章需要做虚拟边缘函数。
均值平滑
均值平滑实际上就是内核元素全是1的卷积运算,然后再除以内核的大小,用数学表达式来表示就是:
下面我们来实现均值平滑函数blur:
复制代码 代码如下:
function blur(__src, __size1, __size2, __borderType, __dst){
if(__src.type && __src.type == "CV_RGBA"){
var height = __src.row,
width = __src.col,
dst = __dst || new Mat(height, width, CV_RGBA),
dstData = dst.data;
var size1 = __size1 || 3,
size2 = __size2 || size1,
size = size1 * size2;
if(size1 % 2 !== 1 || size2 % 2 !== 1){
console.error("size大小必须是奇数");
return __src;
}
var startX = Math.floor(size1 / 2),
startY = Math.floor(size2 / 2);
var withBorderMat = copyMakeBorder(__src, startY, startX, 0, 0, __borderType),
mData = withBorderMat.data,
mWidth = withBorderMat.col;
var newValue, nowX, offsetY, offsetI;
var i, j, c, y, x;
for(i = height; i--;){
offsetI = i * width;
for(j = width; j--;){
for(c = 3; c--;){
newValue = 0;
for(y = size2; y--;){
offsetY = (y + i) * mWidth * 4;
for(x = size1; x--;){
nowX = (x + j) * 4 + c;
newValue += mData[offsetY + nowX];
}
}
dstData[(j + offsetI) * 4 + c] = newValue / size;
}
dstData[(j + offsetI) * 4 + 3] = mData[offsetY + startY * mWidth * 4 + (j + startX) * 4 + 3];
}
}
}else{
console.error("不支持类型。");
}
return dst;
}
其中size1和size2分别是核的横向和纵向大小,并且必须是正奇数。
高斯平滑
最有用的滤波器 (尽管不是最快的)。 高斯滤波是将输入数组的每一个像素点与高斯内核
卷积将卷积和当作输出像素值。
参考一维高斯函数,我们可以看见,他是个中间大两边小的函数。
所以高斯滤波器其加权数是中间大,四周小的。
其二维高斯函数为: