图像处理软件中通常都会有个高斯模糊的选项,能够将一张清晰的图片变得模糊。
模糊的实现很简单,就是将像素点周围格子按照指定的权重分配,求和后取平均值,即为该像素点最终的值。
假设某个像素点与周围格子的颜色值如下,按照右边的权重分配:
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
多模糊几次。