图像处理软件中通常都会有个高斯模糊的选项,能够将一张清晰的图片变得模糊。
模糊的实现很简单,就是将像素点周围格子按照指定的权重分配,求和后取平均值,即为该像素点最终的值。
假设某个像素点与周围格子的颜色值如下,按照右边的权重分配:
| 112 | 140 | 168 | 1/9 | 1/9 | 1/9 | |
| 196 | 224 | 252 | => | 1/9 | 1/9 | 1/9 |
| 28 | 56 | 84 | 1/9 | 1/9 | 1/9 |
那么该像素点的取值为:112 * 1/9 + 140 * 1/9 + … + 84 * 1/9 = 140
这是一个简单的模糊例子,周围权重按等比例分配的模糊,叫均值模糊。
而高斯模糊与均值模糊的区别仅仅是在权重的分配上,高斯模糊是越靠近中心点的格子权值越高,计算公式:

模糊半径为 1 的权重分配:
| 0.0453542 | 0.0566406 | 0.0453542 |
| 0.0566406 | 0.0707355 | 0.0566406 |
| 0.0453542 | 0.0566406 | 0.0453542 |
权重之和需要等于 1 ,最终的权重为:
| 0.094741 | 0.118318 | 0.094741 |
| 0.118318 | 0.147761 | 0.118318 |
| 0.094741 | 0.118318 | 0.094741 |
代码实现:
$step = 1;
$sigma = 1.5;
$weights = [];
$weightSum = 0;
// 通过公式计算权重分配
for ($y = -$step; $y <= $step; $y ++) {
$weights[$y] = [];
for ($x = -$step; $x <= $step; $x ++) {
$weights[$y][$x] = 1 / (2 * pi() * $sigma ** 2) * exp(- ($x ** 2 + $y ** 2) / (2 * $sigma ** 2));
$weightSum += $weights[$y][$x];
}
}
// 计算最终权重
for ($y = -$step; $y <= $step; $y ++) {
for ($x = -$step; $x <= $step; $x ++) {
$weights[$y][$x] /= $weightSum;
}
}
完整代码:
$img = imagecreatefromstring(file_get_contents('lenna.jpg'));
$width = imagesx($img);
$height = imagesy($img);
$output = imagecreatetruecolor($width, $height);
$maxX = $width - 1;
$maxY = $height - 1;
// 获取图像所有像素点的颜色值
for ($y = 0; $y < $height; $y ++) {
for ($x = 0; $x < $width; $x ++) {
$hex = imagecolorat($img, $x, $y);
$colors[$y][$x] = [
$hex >> 16 & 0xff,
$hex >> 8 & 0xff,
$hex & 0xff
];
}
}
// 遍历每个像素点
for ($y = 0; $y < $height; $y ++) {
for ($x = 0; $x < $width; $x ++) {
// 将r、g、b分别抽离出来计算
$r = 0;
$g = 0;
$b = 0;
// 遍历周围像素点
for ($y2 = -$step; $y2 <= $step; $y2 ++) {
for ($x2 = -$step; $x2 <= $step; $x2 ++) {
$px = max(min($x + $x2, $maxX), 0);
$py = max(min($y + $y2, $maxY), 0);
$rgb = $colors[$py][$px];
$weight = $weights[$y2][$x2];
$r += $rgb[0] * $weight;
$g += $rgb[1] * $weight;
$b += $rgb[2] * $weight;
}
}
// 绘制最终像素点
imagesetpixel($output, $x, $y, ($r << 16) + ($g << 8) + $b);
}
}
// 输出
header('Content-Type:image/png');
imagepng($output);
效果:

可以看出半径为 1 的模糊效果不是十分明显,如果需要更模糊,可以尝试将半径的值调大(但按照这种写法性能极差),或是半径为 1 多模糊几次。
