例如原图:

那么生成素描格式的图片就是这样子的:

在很多php空间上,一些外部的插件是无法使用的,能够用的只有GD库,例如SAE。

因此外部滤镜不在我们考虑范围内。

图片转换,需要以下几个步骤(参见:http://stackoverflow.com/questions/9826273/photo-image-to-sketch-algorithm):

*s = Read-File-Into-Image("/path/to/image")
*g = Convert-To-Gray-Scale(s)
*i = Invert-Colors(g)
*b = Apply-Gaussian-Blur(i)
*result = Color-Dodge-Blend-Merge(b,g)

那么在php中,大致就是这样几个步骤(以JPG格式为例):

$im_gray = imagecreatefromjpeg($image_path);

$width = imagesx($im_gray);
$height = imagesy($im_gray);

imagefilter($im_gray, IMG_FILTER_GRAYSCALE);

$im_invert = imagecreatetruecolor($width, $height);
imagecopy($im_invert, $im_gray, 0, 0, 0, 0, $width, $height);
imagefilter($im_invert, IMG_FILTER_NEGATE);
imagefilter($im_invert, IMG_FILTER_GAUSSIAN_BLUR);

$image = $this->color_dodge_blend($im_gray, $im_invert, $width, $height); //这个算法最后再给出
imagejpeg($image);
可是试验之后,发现效果并不是非常理想

最大的问题是,线条过细,整个图片显得很单薄。
其次,颜色稍显于繁杂,不同的灰度颜色太多。
最后,对于某些图片,背景颜色非纯白,而是灰色的,不太美观。

因此在原来的算法之上,需要再增加一些处理。
1. 高斯模糊,不止使用一次(经过试验,10次的效果还不错)
2. 在color_dodge算法中,将不同颜色做一定的归一化,例如允许的RGB分别都必须是30的倍数
3. 统计图片中每个像素的颜色,颜色最多的,即认为是“背景色”,将所有“背景色”的像素,置成白色0xFFFFFF。

那么上面算法中,最后一个步骤的算法即:


static $GAUSS_COUNT = 10;
static $COLOR_STEP = 30;

private function color_dodge($mask, $image)
{
	$result = ($image === 255) ? $image : min(255, (($mask << 8 ) / (255 - $image)));
	//$result = ($result <= 210) ? 0 : $result;
	$result = (int) ($result / SketchAction::$COLOR_STEP) * SketchAction::$COLOR_STEP;
	return $result;
}

private function kmax1($array)
{
	return array_search(max($array), $array);
}

private function color_dodge_blend($source, $layer, $width, $height)
{
	$image = imagecreatetruecolor($width, $height);
	$color_array = array();
	for ($x = 0; $x < $width; $x++)
	{
		for ($y = 0; $y < $height; $y++)
		{
			$rgb = ImageColorAt($source, $x, $y);
			$r = ($rgb >> 16) & 0xFF;
			$g = ($rgb >> 8) & 0xFF;
			$b = $rgb & 0xFF;

			$rgb2 = ImageColorAt($layer, $x, $y);
			$r2 = ($rgb2 >> 16) & 0xFF;
			$g2 = ($rgb2 >> 8) & 0xFF;
			$b2 = $rgb2 & 0xFF;

			$r = $this->color_dodge($r, $r2);
			$g = $this->color_dodge($g, $g2);
			$b = $this->color_dodge($b, $b2);

			$rgb = ($r << 16) | ($g << 8) | $b;
			$color_array[$rgb] = key_exists($rgb, $color_array) ? $color_array[$rgb] + 1 : 1;
			imagesetpixel($image, $x, $y, $rgb);
		}
	}

	$background = $this->kmax1($color_array);

	for ($x = 0; $x < $width; $x++)
	{
		for ($y = 0; $y < $height; $y++)
		{
			$rgb = ImageColorAt($image, $x, $y);
			if ($rgb === $background)
			{
			    	imagesetpixel($image, $x, $y, 0xFFFFFF);
			}
		}
	}

	return $image;
}

至此,算法完成。