PHP 图像处理与压缩

PHP图像处理虽然不比其他语言性能高,但应付日常来说业务来说是够用的。本类大部分是自己编写其他则参考来自PHP手册的代码。另外,要想正确使用记得要启用GD库。

简单的使用像这样:

        $src= '/path/IMAGE_NAME.jpg';
        $img = new UImage($src);
        
        $path = $img->negate();  // 颜色翻转
        $path = $img->compress(); // 图像压缩
        
        // 第二个参数如果启用的话将会覆盖原图, 其他操作一样
        $img = new UImage($src, true);

注意的是处理后的图像并不会覆盖源文件,重命名的部分可以自行调整代码

<?php

/**
 * YCZ - UImage
 * present by YCZ
 * Author: Nomandia
 * Date: 2019/7/31 10:40
 */
class UImage
{
    /**
     * @var int 图片宽
     */
    public $width = 0;

    /**
     * @var int 图片高
     */
    public $height = 0;

    /**
     * @var int 图片类型
     */
    public $type;

    /**
     * @var string 图片扩展名
     */
    public $extension;

    /**
     * @var string 图片MimeType
     */
    public $mimeType;

    /**
     * @var array 图片属性
     */
    public $attr;

    /**
     * @var string 原始图像地址
     */
    public $src;

    /**
     * @var string 创建临时图像的方法
     */
    public $createfunc;

    /**
     * @var string 输出图像的方法
     */
    public $exportfunc;

    /**
     * @var bool 是否处理后覆盖原图
     */
    public $cover = false;

    /**
     * @var string 输出路径
     */
    public $exportPath;

    /**
     * UImage constructor.
     * @param string $src
     * @param bool $cover
     * @return $this
     */
    function __construct($src = null, $cover = false)
    {
        $this->setSrc($src);
        $this->cover = $cover;
        return $this;
    }

    /**
     * @param string $src
     * @return $this|bool
     * Author: Great Nomandia
     * Created At 2019/7/31 12:16
     */
    function setSrc($src)
    {
        if (!$src || !is_file($src) || !getimagesize($src)) {
            return false;
        }
        // getimagesize 获取图像的基础信息
        list($this->width, $this->height, $this->type, $this->attr) = getimagesize($this->src = $src);
        $this->extension = image_type_to_extension($this->type, false);
        $this->mimeType = image_type_to_mime_type($this->type);

        // 这里根据扩展名获取相应的处理方法
        $this->createfunc = 'imagecreatefrom' . $this->extension;
        if (!function_exists($this->createfunc)) {
            Yii::error($this->exportfunc . ' not exists', 'api.uimage.createfunc');
            return false;
        }
        $this->exportfunc = 'image' . $this->extension;
        if (!function_exists($this->exportfunc)) {
            Yii::error($this->exportfunc . ' not exists', 'api.uimage.exportfunc');
            return false;
        }
        return $this;
    }

    /**
     * 输出路径
     * @param string $exportPath
     * @param string $suffix
     * @return string
     * Author: Great Nomandia
     * Created At 2019/7/31 18:07
     */
    protected function getExportPath($exportPath = null, $suffix = null)
    {
        // 指定了输出路径直接返回
        if ($exportPath) {
            return $exportPath;
        }
        if ($this->cover) {
            // 覆盖时返回源文件路径
            return $this->src;
        }
        $pathinfo = pathinfo($this->src);
        return $this->exportPath = $pathinfo['dirname'] . DIRECTORY_SEPARATOR . $pathinfo['filename'] . $suffix . '.' . $this->extension;
    }

    /**
     * 翻转图
     * @param string $exportPath
     * @return bool|UImage
     * Author: Great Nomandia
     * Created At 2019/7/31 15:15
     */
    function negate($exportPath = null)
    {
        return $this->process(IMG_FILTER_NEGATE, $exportPath);
    }

    /**
     * 灰度图
     * @param string $exportPath
     * @return bool|UImage
     * Author: Great Nomandia
     * Created At 2019/7/31 15:24
     */
    function grayscale($exportPath = null)
    {
        return $this->process(IMG_FILTER_GRAYSCALE, $exportPath);
    }

    /**
     * 边界图
     * @param string $exportPath
     * @return bool|UImage
     * Author: Great Nomandia
     * Created At 2019/7/31 15:24
     */
    function edgedetect($exportPath = null)
    {
        return $this->process(IMG_FILTER_EDGEDETECT, $exportPath);
    }

    /**
     * 浮雕图
     * @param string $exportPath
     * @return bool|UImage
     * Author: Great Nomandia
     * Created At 2019/7/31 15:24
     */
    function emboss($exportPath = null)
    {
        return $this->process(IMG_FILTER_EMBOSS, $exportPath);
    }

    /**
     * 高斯模糊
     * @param string $exportPath
     * @return bool|UImage
     * Author: Great Nomandia
     * Created At 2019/7/31 15:43
     */
    function gaussianblur($exportPath = null)
    {
        return $this->process(IMG_FILTER_GAUSSIAN_BLUR, $exportPath);
    }

    /**
     * 严选
     * @param string $exportPath
     * @return bool|UImage
     * Author: Great Nomandia
     * Created At 2019/7/31 15:24
     */
    function selective($exportPath = null)
    {
        return $this->process(IMG_FILTER_SELECTIVE_BLUR, $exportPath);
    }

    /**
     * 轮廓图
     * @param string $exportPath
     * @return bool|UImage
     * Author: Great Nomandia
     * Created At 2019/7/31 15:23
     */
    function meanremoval($exportPath = null)
    {
        return $this->process(IMG_FILTER_MEAN_REMOVAL, $exportPath);
    }

    /**
     * 亮度
     * @param string $exportPath
     * @param int $level 0-100
     * @return bool|UImage
     * Author: Great Nomandia
     * Created At 2019/7/31 15:23
     */
    function brightness($exportPath = null, $level = 50)
    {
        return $this->process(IMG_FILTER_BRIGHTNESS, $exportPath, $level);
    }

    /**
     * 对比度
     * @param string $exportPath
     * @param int $level 0-100 越高颜色越少
     * @return bool|UImage
     * Author: Great Nomandia
     * Created At 2019/7/31 15:23
     */
    function contrast($exportPath = null, $level = 30)
    {
        return $this->process(IMG_FILTER_CONTRAST, $exportPath, $level);
    }

    /**
     * 柔化
     * @param string $exportPath
     * @param int $level
     * @return bool|UImage
     * Author: Great Nomandia
     * Created At 2019/7/31 15:23
     */
    function smooth($exportPath = null, $level = 3)
    {
        return $this->process(IMG_FILTER_SMOOTH, $exportPath, $level);
    }

    /**
     * 彩色灰度图
     * @param string $exportPath
     * @param int $red
     * @param int $blue
     * @param int $green
     * @return bool|UImage
     * Author: Great Nomandia
     * Created At 2019/7/31 15:21
     */
    function colorize($exportPath = null, $red = 255, $blue = 0, $green = 0)
    {
        return $this->process(IMG_FILTER_COLORIZE, $exportPath, $red, $blue, $green);
    }

    /**
     * @param string $mark_src
     * @param int $x
     * @param int $y
     * @return $this
     * Author: Great Nomandia
     * Created At 2019/7/31 18:13
     */
    function watermark($mark_src, $x, $y)
    {
        $image = call_user_func($this->createfunc, $this->src);
        list($w, $h, $type, $attr) = getimagesize($mark_src);
        $extension = image_type_to_extension($type, false);

        // 加载水印图
        $marker = call_user_func('imagecreatefrom' . $extension, $mark_src);

        imagecopy($image, $marker, $x, $y, 0, 0, $this->width, $this->height);

        // 注意这里会覆盖原图
        call_user_func($this->exportfunc, $image, $this->getExportPath($this->src, 'WM'));

        imagedestroy($image);
        imagedestroy($marker);

        return $this;
    }

    /**
     * 图片旋转
     * @param int $angle 旋转角度(正数为逆时针旋转 负数为顺时针)
     * @param int $bgd_color 背景颜色
     * @return $this|UImage
     * Author: Great Nomandia
     * Created At 2019/7/31 17:14
     */
    function rotate($angle, $bgd_color = 0, $exportPath = null)
    {
        $image = call_user_func($this->createfunc, $this->src);
        $image = imagerotate($image, $angle, $bgd_color);

        call_user_func($this->exportfunc, $image, $this->getExportPath($exportPath, '.R'));

        imagedestroy($image);

        return $this;
    }

    /**
     * X轴翻转 垂直翻转
     * @param string $exportPath
     * @return UImage
     * Author: Great Nomandia
     * Created At 2019/7/31 18:35
     */
    function flipX($exportPath = null)
    {
        return $this->flip(IMG_FLIP_VERTICAL, $exportPath);
    }

    /**
     * Y轴翻转 水平翻转
     * @param string $exportPath
     * @return mixed
     * Author: Great Nomandia
     * Created At 2019/7/31 18:34
     */
    function flipY($exportPath = null)
    {
        return $this->flip(IMG_FLIP_HORIZONTAL, $exportPath);
    }

    /**
     * 双向翻转
     * @param string $exportPath
     * @return UImage
     * Author: Great Nomandia
     * Created At 2019/7/31 18:34
     */
    function flipBoth($exportPath = null)
    {
        return $this->flip(IMG_FLIP_BOTH, $exportPath);
    }

    /**
     * @param int $mode
     * @param string $exportPath
     * @return $this|UImage
     * Author: Great Nomandia
     * Created At 2019/7/31 17:23
     */
    function flip($mode = IMG_FLIP_HORIZONTAL, $exportPath = null)
    {
        $image = call_user_func($this->createfunc, $this->src);
        imageflip($image, $mode);

        call_user_func($this->exportfunc, $image, $this->getExportPath($exportPath, '.F'));
        imagedestroy($image);
        return $this;
    }

    /**
     * 裁剪图像
     * @param int $dst_x
     * @param int $dst_y
     * @param int $width
     * @param int $height
     * @param int $quality
     * @param bool $no_blank 无留空,即当裁切区域不足以填充新图时也会留空白, true则不会但宽高会被裁剪
     * @param null $exportPath
     * @return string|null
     * Author: Great Nomandia
     * Created At 2019/7/31 16:51
     */
    function cut($dst_x, $dst_y, $width, $height, $quality = 80, $no_blank = false, $exportPath = null)
    {
        if ($no_blank) {
            $width = $this->width - $dst_x > $width ? $width : $this->width - $dst_x;
            $height = $this->height - $dst_y > $height ? $height : $this->height - $dst_y;
        }
        return $this->copy($exportPath, 0, 0, $dst_x, $dst_y, $width, $height, $width, $height, $quality);
    }

    /**
     * 等比缩放
     * @param int $scale
     * @param int $quality
     * @param string $exportPath
     * @return string|null
     * Author: Great Nomandia
     * Created At 2019/7/31 16:42
     */
    function scale($scale = 1.0, $quality = 80, $exportPath = null)
    {
        return $this->copy($exportPath, 0, 0, 0, 0, $this->width * $scale, $this->height * $scale, null, null, $quality);
    }

    /**
     * 重置大小
     * @param int $width
     * @param int $height
     * @param int $quality
     * @param string $exportPath
     * @return string|null
     * Author: Great Nomandia
     * Created At 2019/7/31 16:33
     */
    function resize($width, $height, $quality = 80, $exportPath = null)
    {
        return $this->copy($exportPath, 0, 0, 0, 0, $width, $height, null, null, $quality);
    }

    /**
     * 复制图像
     * @param string $exportPath
     * @param int $dst_x
     * @param int $dst_y
     * @param int $src_x
     * @param int $src_y
     * @param int $dst_width
     * @param int $dst_height
     * @param int $quality
     * @return string|null
     * Author: Great Nomandia
     * Created At 2019/7/31 16:32
     */
    function copy($exportPath = null, $dst_x = 0, $dst_y = 0, $src_x = 0, $src_y = 0, $dst_width = null, $dst_height = null, $src_width = null, $src_height = null, $quality = 70)
    {
        $image = call_user_func($this->createfunc, $this->src);

        $dst_width = $dst_width ?: $this->width;
        $dst_height = $dst_height ?: $this->height;

        $src_width = $src_width ?: $this->width;
        $src_height = $src_height ?: $this->height;

        $thump = function_exists('imagecreatetruecolor') ?
            imagecreatetruecolor($dst_width, $dst_height) :
            imagecreate($dst_width, $dst_height);

        // 注意如果纵横比改变了那图像将被拉伸
        imagecopyresampled($thump, $image, $dst_x, $dst_y, $src_x, $src_y, $dst_width, $dst_height, $src_width, $src_height);

        call_user_func($this->exportfunc, $thump, $this->getExportPath($exportPath, $dst_width . 'x' . $dst_height), $quality);

        imagedestroy($image);
        imagedestroy($thump);
        return $exportPath;
    }

    /**
     * 处理图片
     * @param int $filter
     * @param string $exportPath
     * @return $this|bool
     * Author: Great Nomandia
     * Created At 2019/7/31 14:59
     */
    function process($filter, $exportPath = null)
    {
        if (null === $filter) {
            return false;
        }
        $image = call_user_func($this->createfunc, $this->src);

        switch ($filter) {
            case IMG_FILTER_NEGATE: // 0. 颜色翻转
            case IMG_FILTER_GRAYSCALE: // 1. 灰度图
            case IMG_FILTER_EDGEDETECT: // 5. 边缘图
            case IMG_FILTER_EMBOSS: // 6. 浮雕图
            case IMG_FILTER_GAUSSIAN_BLUR: // 7. 高斯模糊
            case IMG_FILTER_SELECTIVE_BLUR: // 8. 别样模糊
            case IMG_FILTER_MEAN_REMOVAL : // 9. 轮廓图
                imagefilter($image, $filter);
                break;
            case IMG_FILTER_BRIGHTNESS: // 2. 亮度调节
            case IMG_FILTER_CONTRAST: // 3. 对比度
                imagefilter($image, $filter, func_get_arg(2));
                break;
            case IMG_FILTER_SMOOTH: // 10. 柔化
                imagefilter($image, $filter, func_get_arg(2));
                break;
            case IMG_FILTER_COLORIZE: // 4. 指定颜色的灰度图, 参数1=R 2=B 3=G, 值范围0-255
                imagefilter($image, $filter, func_get_arg(2), func_get_arg(3), func_get_arg(4));
                break;
            default:
                // 非有效图像不做处理
                imagedestroy($image);
                return $this;
        }

        call_user_func($this->exportfunc, $image, $this->getExportPath($exportPath, '.' . $filter));

        imagedestroy($image);

        return $this;
    }

    /**
     * 模糊处理
     * @param string $exportPath 输出路径(含文件名)
     * @param int $factor
     * @return $this|bool
     * Author: Great Nomandia
     * Created At 2019/7/31 14:37
     */
    function blur($exportPath = null, $factor = 3)
    {
        if (!$this->createfunc) {
            return false;
        }
        $image = call_user_func($this->createfunc, $this->src);
        $factor = round($factor);

        $originalWidth = imagesx($image);
        $originalHeight = imagesy($image);

        $smallestWidth = ceil($originalWidth * pow(0.5, $factor));
        $smallestHeight = ceil($originalHeight * pow(0.5, $factor));

        // for the first run, the previous image is the original input
        $prevImage = $image;
        $prevWidth = $originalWidth;
        $prevHeight = $originalHeight;

        // scale way down and gradually scale back up, blurring all the way
        for ($i = 0; $i < $factor; $i++) {
            // determine dimensions of next image
            $nextWidth = $smallestWidth * pow(2, $i);
            $nextHeight = $smallestHeight * pow(2, $i);

            // resize previous image to next size
            $nextImage = imagecreatetruecolor($nextWidth, $nextHeight);
            imagecopyresized($nextImage, $prevImage, 0, 0, 0, 0, $nextWidth, $nextHeight, $prevWidth, $prevHeight);

            // apply blur filter
            imagefilter($nextImage, IMG_FILTER_GAUSSIAN_BLUR);

            // now the new image becomes the previous image for the next step
            $prevImage = $nextImage;
            $prevWidth = $nextWidth;
            $prevHeight = $nextHeight;
        }

        // scale back to original size and blur one more time
        imagecopyresized($image, $nextImage, 0, 0, 0, 0, $originalWidth, $originalHeight, $nextWidth, $nextHeight);
        imagefilter($image, IMG_FILTER_GAUSSIAN_BLUR);

        // clean up
        imagedestroy($prevImage);

        call_user_func($this->exportfunc, $image, $this->getExportPath($exportPath, '.B' . $factor));

        imagedestroy($image);

        return $this;
    }

    /**
     * 压缩图像
     * @param float $ratio 保持原图比例(等比缩放)
     * @param int $quality 图片质量 0-100,越大越好
     * @param string $exportPath 保存路径 null 时将覆盖源文件
     * @return string
     * Author: Great Nomandia
     * Created At 2019/7/31 10:38
     */
    function compress($ratio = 1.0, $quality = 60, $exportPath = null)
    {
        // 从SRC中创建一个图像
        if (!$this->createfunc) {
            return false;
        }
        $image = call_user_func($this->createfunc, $this->src);
        if (!$image) {
            // 临时图创建失败
            return false;
        }
        // 临时图的宽高
        $new_height = $this->height * $ratio;
        $new_width = $this->width * $ratio;

        // 创建临时图
        if (function_exists('imagecreatetruecolor')) {
            // 需要GD2支持,老版用imagecreate
            $thump = imagecreatetruecolor($new_width, $new_height);
        } else { // 此方法试过会失真
            $thump = imagecreate($new_width, $new_height);
        }

        // 复制源图像到临时文件
        imagecopyresampled($thump, $image, 0, 0, 0, 0, $new_width, $new_height, $this->width, $this->height);

        // 压缩后的图片的命名,以及回存路径

        // IOS 图片旋转问题
        // 1:0°,6:顺时针90°, 8:逆时针90°,3:180°
        if ((IMAGETYPE_JPEG == $this->type || IMAGETYPE_TIFF_II == $this->type)
            && function_exists('exif_read_data')) {
            // JPEG 或 TIFF 文件中读取 EXIF 头信息, 这里自动处理旋转的照片
            $exif = exif_read_data($this->src);
            if (isset($exif['Orientation'])) {
                switch ($exif['Orientation']) {
                    case 3:
                        $thump = imagerotate($thump, 180, 0);
                        break;
                    case 6:
                        $thump = imagerotate($thump, -90, 0);
                        break;
                    case 8:
                        $thump = imagerotate($thump, 90, 0);
                        break;
                }
            }
        }

        // 生成图片, 注意quality并非所有方法都试用
        // 另外 quality实测高于80时无效果,文件反而更大
        call_user_func($this->exportfunc, $thump, $this->getExportPath($exportPath, '.C'), $quality);

        // 销毁
        imagedestroy($thump);
        imagedestroy($image);

        return $this;
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,290评论 6 491
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,107评论 2 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,872评论 0 347
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,415评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,453评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,784评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,927评论 3 406
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,691评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,137评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,472评论 2 326
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,622评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,289评论 4 329
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,887评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,741评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,977评论 1 265
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,316评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,490评论 2 348