# 图像处理

houdunren.com @ 向军大叔

网站开发中需要经常处理图片,如课程缩略图,用户头像等,使用PHP处理图像非常简单。

# 配置环境

PHP中图像处理需要GD库的支持。

在windows系统中修改php.ini文件,删除 extension=php_gd2.dll 前面的;开启图像处理支持。

centos中使用 yum install php-gd*

ubuntu系统中使用 apt-get install php7.3-gd

检测GD库是否加载

$has = extension_loaded('GD');
var_dump($has);

# 使用方法

PHP创建图像步骤

  1. 发送HTTP头信息,声明内容为图像
  2. 创建画布
  3. 创建绘图所需要的颜色
  4. 绘图(填充画布、画圆、画方块、画线条、画布上写字)
  5. 输出图像
  6. 释放画布资源

# 头信息

通过header() 函数告诉浏览器,输出的是一个图像而不是文本或HTML,这样浏览器就可以正常显示图像了。

  • header('Content-type:image/gif');
  • header('Content-type:image/jpeg');
  • header('Content-type:image/png');
header('Content-type:image/jpeg');
readfile('user.jpeg');

# 创建画布

imageCreateTrueColor(width,height)

  • 传入的两个参数分别为画布的宽和高,在绘图时超出宽高的部分将不予显示,且此尺寸即为生成图片文件时的尺寸。
  • 返回值为资源类型。

# 设置颜色

imageColorAllocate(img_resource,R,G,B)

  • 颜色从属于某个图像资源而存在。
  • 颜色实际上是一个整形数值。
  • 颜色的后三个参数需传入值的范围是0~255

# 填充颜色

imageFill(img_resource,x,y,color)

  • (x,y)表示从哪个点开始填充颜色的坐标
  • 不填充画布的话,默认是黑色
header('Content-type:image/jpeg');
$res = imagecreatetruecolor(1000, 500);
$red = imagecolorallocate($res, 255, 0, 0);
imagefill($res, 0, 0, $red);
imagejpeg($res);

# 绘制矩形

绘制空心矩形

  • imageRectangle(img_res,x1,y1,x2,y2,color)
header('Content-type:image/jpeg');
$res = imagecreatetruecolor(1000, 500);
$red = imagecolorallocate($res, 255, 0, 0);
$green = imagecolorallocate($res, 0, 255, 0);
imagefill($res, 0, 0, $red);
imagerectangle($res, 100, 100, 200, 200, $green);
imagejpeg($res);

绘制填充好的实心矩形

imageFilledRectangle (img_res,x1,y1,x2,y2,color)

  • (x1,y1)为左上角坐标, (x2,y2)为右下角坐标
header('Content-type:image/jpeg');
$res = imagecreatetruecolor(1000, 500);
$red = imagecolorallocate($res, 255, 0, 0);
$green = imagecolorallocate($res, 0, 255, 0);
imagefill($res, 0, 0, $red);
imagefilledrectangle($res, 100, 100, 300, 300, $green);
imagejpeg($res);

# 绘制圆形

绘制空心圆形

  • imageEllipse(img_res,x,y,w,h,color)

绘制填充好的实心圆

  • imageFilledEllipse(img_res,x,y,w,h,color)

说明:

  • (x,y)为圆心坐标。 w为宽度,h为高度
header('Content-type:image/jpeg');
$res = imagecreatetruecolor(500, 500);
$red = imagecolorallocate($res, 255, 0, 0);
$green = imagecolorallocate($res, 0, 255, 0);
imagefill($res, 0, 0, $red);
imageellipse($res, 250, 250, 100, 100, $green);
imagejpeg($res);

# 绘制线条

imageLine(img_res,x1,y1,x2,y2,color)

  • (x1,y1)为起始点坐标
  • (x2,y2)为结束点坐标
$res = imagecreatetruecolor(500, 500);
$red = imagecolorallocate($res, 255, 0, 0);
$green = imagecolorallocate($res, 0, 255, 0);
imagefill($res, 0, 0, $red);
imageline($res, 0, 0, 499, 499, $green);
imagepng($res);

# 绘制像素

bool imagesetpixel ( resource $image , int $x , int $y , int $color )

  • (x1,y1)为点坐标
header('Content-type:image/jpeg');
$res = imagecreatetruecolor(500, 500);
$red = imagecolorallocate($res, 255, 0, 0);
$green = imagecolorallocate($res, 0, 255, 0);
imagefill($res, 0, 0, $red);
for ($i = 0; $i < 600; $i++) {
    imagesetpixel($res, mt_rand(0, 500), mt_rand(0, 500), $green);
}
imagepng($res);

# 输出图像

输出不同格式的图像用不同的方法:

  • imagegif(img_resource[,filename])
  • imagejpeg(img_resource[,filename])
  • imagepng(img_resource[,filename])
  • imagebmp(img_resource[,filename])
  • 当设置第二个参数时表示储存文件,如果存在同名文件会覆盖

# 释放图像

imageDestroy(img_resource)

  • 图像输出完毕及时释放资源,把内存空间留给更需要的程序。

# 输入文本

array imagettftext ( resource $image , float $size , float $angle , int $x , int $y , int $color , string $fontfile , string $text )

  • 参数说明:图像资源,字体尺寸,角度,第一个字符的基本点(大概是字符的左下角),Y 坐标(字体基线的位置),颜色 ,字体文件,文本字符串(UTF-8 编码)
header('Content-type:image/png');
$res = imagecreatetruecolor(500, 500);
$red = imagecolorallocate($res, 255, 0, 0);
$green = imagecolorallocate($res, 0, 255, 0);
imagefill($res, 0, 0, $red);
$font = realpath('source.otf');
imagettftext($res, 50, 0, 0, 50, $green, $font, 'houdunren.com');
imagepng($res);
imagedestroy($res);

array imagettfbbox ( float $size , float $angle , string $fontfile , string $text )

  • 文本范围的盒子大小,可以方便控制文本输出位置
  • 返回一个含有 8 个单元的数组表示了文本外框的四个角:
变量 位置
0 左下角 X 位置
1 左下角 Y 位置
2 右下角 X 位置
3 右下角 Y 位置
4 右上角 X 位置
5 右上角 Y 位置
6 左上角 X 位置
7 左上角 Y 位置

文字在画布右上角显示

$font = realpath('source.otf');
$text = 'houdunren.com';
$size = 16;
$box = imagettfbbox($size, 0, $font, 'houdunren.com');
$width  = $box[2] - $box[0];
$height = $box[0] - $box[7];
imagettftext($res, $size, 0, 500 - $width, $height, $green, $font, 'houdunren.com');

文字在画布中间显示

...
imagettftext($res, $size, 0, 250 - $width / 2, 250 - $height / 2, $green, $font, 'houdunren.com');
...

# 外部图像

打开图片文件

  • imageCreateFromgif(filename/url)
  • imageCreateFromjpeg(filename/url)
  • imageCreateFrompng(filename/url)
  • imageCreateFrombmp(filename/url)
  • 返回一个资源类型

# 获得信息

imagesx(img_resource)

  • 取得图像宽度

imagesy(img_resource)

  • 取得图像高度

getimagesize(img_file)

  • array getimagesize ( string $filename [, array &$imageinfo ] )

# 图像复制

拷贝图像的一部分

  • bool imagecopy ( resource $dst_im , resource $src_im , int $dst_x , int $dst_y , int $src_x , int $src_y , int $src_w , int $src_h )

拷贝并合并图像的一部分

  • bool imagecopymerge ( resource $dst_im , resource $src_im , int $dst_x , int $dst_y , int $src_x , int $src_y , int $src_w , int $src_h , int $pct )

# 图片缩放

拷贝部分图像并调整大小

  • bool imagecopyresampled ( resource $dst_image , resource $src_image , int $dst_x , int $dst_y , int $src_x , int $src_y , int $dst_w , int $dst_h , int $src_w , int $src_h )

# 验证码

使用方法

# 后台code.php
include 'Captcha.php';
$code = new Captcha(100, 30, 20);
$code->make();

# 前台
<img src="code.php" alt="">

验证码类

<?php
class Captcha
{
    protected $width;
    protected $height;
    protected $res;
    protected $len = 4;
    protected $font;
    protected $size;
    public function __construct(int $width = 100, int $height = 30, $size = 20)
    {
        $this->width = $width;
        $this->height = $height;
        $this->font = realpath('source.otf');
        $this->size = $size;
    }
    public function make()
    {
        $res = imagecreatetruecolor($this->width, $this->height);
        imagefill($this->res = $res, 0, 0, imagecolorallocate($res, 200, 200, 200));
        $this->text();
        $this->line();
        $this->pix();
        $this->render();
    }
    protected function text()
    {
        $text = 'abcdefghigk123456789';

        for ($i = 0; $i < $this->len; $i++) {
            $x = $this->width / $this->len * $i;
            $box = imagettfbbox($this->size, 0, $this->font, $text[$i]);
            imagettftext(
                $this->res,
                $this->size,
                mt_rand(-20, 20),
                $x,
                $this->height / 2 + ($box[0] - $box[7]) / 2,
                $this->textColor(),
                $this->font,
                strtoupper($text[$i])
            );
        }
    }
    protected function pix()
    {
        for ($i = 0; $i < 300; $i++) {
            imagesetpixel(
                $this->res,
                mt_rand(0, $this->width),
                mt_rand(0, $this->height),
                $this->color()
            );
        }
    }
    protected function line()
    {
        for ($i = 0; $i < 6; $i++) {
            imagesetthickness($this->res, mt_rand(1, 3));
            imageline(
                $this->res,
                mt_rand(0, $this->width),
                mt_rand(0, $this->height),
                mt_rand(0, $this->width),
                mt_rand(0, $this->height),
                $this->color()
            );
        }
    }
    protected function color()
    {
        return imagecolorallocate($this->res, mt_rand(100, 200), mt_rand(100, 200), mt_rand(100, 200));
    }
    protected function textColor()
    {
        return imagecolorallocate($this->res, mt_rand(50, 150), mt_rand(50, 150), mt_rand(50, 150));
    }
    protected function render()
    {
        header('Content-type:image/png');
        imagepng($this->res);
    }
}

# 水印类

调用方式

<?php
include "Water.php";
$water = new Water("logo.png");
$water->make('sun.png', '2.png', 5);

水印类源码

<?php
class Water
{
    //水印资源
    protected $water;
    //水印图片
    public function __construct(string $water)
    {
        $this->water = $water;
    }
    //入口方法
    public function make(string $image, string $filename = null, int $pos = 9)
    {
        $res = $this->resource($image);
        $water = $this->resource($this->water);
        $postion = $this->position($res, $water, $pos);
        imagecopy($res, $water,  $postion['x'], $postion['y'], 0, 0, imagesx($water), imagesy($water));
        $this->showAction($image)($res, $filename ?: $image);
    }
    //获取资源对象
    protected function resource($image)
    {
        $info = getimagesize($image);
        $function = [1 => 'imagecreatefromgif', 2 => 'imagecreatefromjpeg', 3 => 'imagecreatefrompng'];
        $call = $function[$info[2]];
        return $call($image);
    }
    //根据类型输出图片
    protected function showAction(string $image)
    {
        $info = getimagesize($image);
        $function = [1 => 'imagegif', 2 => 'imagejpeg', 3 => 'imagepng'];
        return $function[$info[2]];
    }
    //位置
    protected function position($des,  $res, int $pos)
    {
        $info = ['x' => 0, 'y' => 0];
        switch ($pos) {
            case 1:
                break;
            case 2:
                $info['x'] = (imagesx($des) - imagesx($res)) / 2;
                break;
            case 3:
                $info['x'] = (imagesx($des) - imagesx($res));
                break;
            case 4:
                $info['y'] = (imagesy($des) - imagesy($res)) / 2;
                break;
            case 5:
                $info['x'] = (imagesx($des) - imagesx($res)) / 2;
                $info['y'] = (imagesy($des) - imagesy($res)) / 2;
                break;
        }
        return $info;
    }
}

# 缩略图

调用方式

include 'Resize.php';
$image =  new Resize;

$image->make('sun.png', '1.jpeg', 200, 200, 3);

缩略图类源码

<?php
class Resize
{
    public function make(string $file, string $to = null, int $width = 200, int $height = 200, int $type = 3)
    {
        $image = $this->resource($file);
        $info = $this->size($width, $height, imagesx($image), imagesy($image), $type);
        $res = imagecreatetruecolor($info[0], $info[1]);
        imagecopyresampled($res, $image, 0, 0, 0, 0, $info[0], $info[1], $info[2], $info[3]);
        header('Content-type:image/jpeg');
        imagejpeg($res);
    }
    protected function size($rw, $rh, $iw, $ih, int $type)
    {
        switch ($type) {
            case 1:
                //固定宽度,高度自动
                $rh = $rw / $iw * $ih;
                break;
            case 2:
                //高度固定,宽度自动 
                $rw = $rh / $ih * $iw;
                break;
            case 3:
                //固定宽度,高度裁切
                $ih = $iw / $rw * $rh;
                break;
            default:
                if (($iw / $rw) > ($ih / $rh)) {
                    $iw = $ih / $rh * $rh;
                } else {
                    $ih = $iw / $rw * $rw;
                }
        }

        return [$rw, $rh, $iw, $ih];
    }
    protected function resource(string $image)
    {
        if (!file_exists($image) || getimagesize($image) === false) {
            throw new Exception("file dont exists or it's not an image file");
        }
        $functions = [1 => 'imagecreatefromgif', 2 => 'imagecreatefromjpeg', 3 => 'imagecreatefrompng'];
        $info = getimagesize($image);
        $call = $functions[$info[2]];
        return $call($image);
    }
}