浅谈php扩展imagick


Posted in PHP onJune 02, 2014

PHP建图通常都用GD库,因为是内置的不需要在服务器上额外安装插件,所以用起来比较省心,但是如果你的程序主要的功能就是处理图像,那?就不建议用GD了,因为GD不但低效能而且能力也比较弱,?子玫南低匙试匆财亩?另外GD的creatfrom也有bug,而imagick却是一个很好的替代品,为此最近把我的一个项目由GD改成了imagick,但是改完之后出现了一些状况在此分享给大家.

首先说一下我这边出现的状况:

状况一:需要重写图像操作class

状况二:imagick多线程时会导致cpu使用率暴增到100%

在此顺便提一下imagick在centos6.4的安装方法:

1、安装ImageMagick

wget http://soft.vpser.net/web/imagemagick/ImageMagick-6.7.1-2.tar.gz
tar zxvf ImageMagick-6.7.1-2.tar.gz
cd ImageMagick-6.7.1-2/
./configure --prefix=/usr/local/imagemagick --disable-openmp
make && make install
ldconfig

测试ImageMagick是否可以正常运行:

/usr/local/imagemagick/bin/convert -version

2、安装PHP扩展:imagick

wget http://pecl.php.net/get/imagick-3.0.1.tgz
tar zxvf imagick-3.0.1.tgz
cd imagick-3.0.1/
/usr/local/php/bin/phpize
./configure --with-php-config=/usr/local/php/bin/php-config --with-imagick=/usr/local/imagemagick
make && make install
ldconfig
vi /usr/local/php/etc/php.ini
添加:extension = "imagick.so"

重启lnmp

/root/lnmp reload

接下来我们针对上述两个状况分别提出解决办法:

状况一的解决办法如下:

/**
    Imagick图像处理类
    用法:
        //引入Imagick物件
        if(!defined('CLASS_IMAGICK')){require(Inc.'class_imagick.php');}
        $Imagick=new class_imagick();
        $Imagick->open('a.gif');
        $Imagick->resize_to(100,100,'scale_fill');
        $Imagick->add_text('1024i.com',10,20);
        $Imagick->add_watermark('1024i.gif',10,50);
        $Imagick->save_to('x.gif');
        unset($Imagick);
/**/
define('CLASS_IMAGICK',TRUE);
class class_imagick{
    private $image=null;
    private $type=null;
    // 构造
    public function __construct(){}
    // 析构
    public function __destruct(){
        if($this->image!==null){$this->image->destroy();}
    }
    // 载入图像
    public function open($path){
        if(!file_exists($path)){
            $this->image=null;
            return ;
        }
        $this->image=new Imagick($path);
        if($this->image){
            $this->type=strtolower($this->image->getImageFormat());
        }
        $this->image->stripImage();
        return $this->image;
    }
    /**
        图像裁切
    /**/
    public function crop($x=0,$y=0,$width=null,$height=null){
        if($width==null) $width=$this->image->getImageWidth()-$x;
        if($height==null) $height=$this->image->getImageHeight()-$y;
        if($width<=0 || $height<=0) return;
        if($this->type=='gif'){
            $image=$this->image;
            $canvas=new Imagick();
            $images=$image->coalesceImages();
            foreach($images as $frame){
                $img=new Imagick();
                $img->readImageBlob($frame);
                $img->cropImage($width,$height,$x,$y);
                $canvas->addImage($img);
                $canvas->setImageDelay($img->getImageDelay());
                $canvas->setImagePage($width,$height,0,0);
            }
            $image->destroy();
            $this->image=$canvas;
        }else{
            $this->image->cropImage($width,$height,$x,$y);
        }
    }
    /**
        更改图像大小
        参数:
            $width:新的宽度
            $height:新的高度
            $fit: 适应大小
                'force': 把图像强制改为$width X $height
                'scale': 按比例在$width X $height内缩放图片,结果不完全等於$width X $height
                'scale_fill':按比例在$width X $height内缩放图片,没有像素的地方填充?色$fill_color=array(255,255,255)(红,绿,蓝,透明度[0不透明-127全透明])
                其他:智能模式,缩放图片并从正中裁切$width X $height的大小
        注意:
            $fit='force','scale','scale_fill'时输出完整图像
            $fit=图像方位时输出指定位置部份的图像
        字母与图像的对应关系如下:
            north_west   north   north_east
            west         center        east
            south_west   south   south_east
    /**/
    public function resize_to($width=100,$height=100,$fit='center',$fill_color=array(255,255,255,0)){
        switch($fit){
        case 'force':
            if($this->type=='gif'){
                $image=$this->image;
                $canvas=new Imagick();
                $images=$image->coalesceImages();
                foreach($images as $frame){
                    $img=new Imagick();
                    $img->readImageBlob($frame);
                    $img->thumbnailImage($width,$height,false);
                    $canvas->addImage($img);
                    $canvas->setImageDelay($img->getImageDelay());
                }
                $image->destroy();
                $this->image=$canvas;
            }else{
                $this->image->thumbnailImage($width,$height,false);
            }
            break;
        case 'scale':
            if($this->type=='gif'){
                $image=$this->image;
                $images=$image->coalesceImages();
                $canvas=new Imagick();
                foreach($images as $frame){
                    $img=new Imagick();
                    $img->readImageBlob($frame);
                    $img->thumbnailImage($width,$height,true);
                    $canvas->addImage($img);
                    $canvas->setImageDelay($img->getImageDelay());
                }
                $image->destroy();
                $this->image=$canvas;
            }else{
                $this->image->thumbnailImage($width,$height,true);
            }
            break;
        case 'scale_fill':
            $size=$this->image->getImagePage();
            $src_width=$size['width'];
            $src_height=$size['height'];
            $x=0;
            $y=0;
            $dst_width=$width;
            $dst_height=$height;
            if($src_width*$height > $src_height*$width){
                $dst_height=intval($width*$src_height/$src_width);
                $y=intval(($height-$dst_height)/2);
            }else{
                $dst_width=intval($height*$src_width/$src_height);
                $x=intval(($width-$dst_width)/2);
            }
            $image=$this->image;
            $canvas=new Imagick();
            $color='rgba('.$fill_color[0].','.$fill_color[1].','.$fill_color[2].','.$fill_color[3].')';
            if($this->type=='gif'){
                $images=$image->coalesceImages();
                foreach($images as $frame){
                    $frame->thumbnailImage($width,$height,true);
                    $draw=new ImagickDraw();
                    $draw->composite($frame->getImageCompose(),$x,$y,$dst_width,$dst_height,$frame);
                    $img=new Imagick();
                    $img->newImage($width,$height,$color,'gif');
                    $img->drawImage($draw);
                    $canvas->addImage($img);
                    $canvas->setImageDelay($img->getImageDelay());
                    $canvas->setImagePage($width,$height,0,0);
                }
            }else{
                $image->thumbnailImage($width,$height,true);
                $draw=new ImagickDraw();
                $draw->composite($image->getImageCompose(),$x,$y,$dst_width,$dst_height,$image);
                $canvas->newImage($width,$height,$color,$this->get_type());
                $canvas->drawImage($draw);
                $canvas->setImagePage($width,$height,0,0);
            }
            $image->destroy();
            $this->image=$canvas;
            break;
        default:
            $size=$this->image->getImagePage();
            $src_width=$size['width'];
            $src_height=$size['height'];
            $crop_x=0;
            $crop_y=0;
            $crop_w=$src_width;
            $crop_h=$src_height;
            if($src_width*$height > $src_height*$width){
                $crop_w=intval($src_height*$width/$height);
            }else{
                $crop_h=intval($src_width*$height/$width);
            }
            switch($fit){
                case 'north_west':
                    $crop_x=0;
                    $crop_y=0;
                    break;
                case 'north':
                    $crop_x=intval(($src_width-$crop_w)/2);
                    $crop_y=0;
                    break;
                case 'north_east':
                    $crop_x=$src_width-$crop_w;
                    $crop_y=0;
                    break;
                case 'west':
                    $crop_x=0;
                    $crop_y=intval(($src_height-$crop_h)/2);
                    break;
                case 'center':
                    $crop_x=intval(($src_width-$crop_w)/2);
                    $crop_y=intval(($src_height-$crop_h)/2);
                    break;
                case 'east':
                    $crop_x=$src_width-$crop_w;
                    $crop_y=intval(($src_height-$crop_h)/2);
                    break;
                case 'south_west':
                    $crop_x=0;
                    $crop_y=$src_height-$crop_h;
                    break;
                case 'south':
                    $crop_x=intval(($src_width-$crop_w)/2);
                    $crop_y=$src_height-$crop_h;
                    break;
                case 'south_east':
                    $crop_x=$src_width-$crop_w;
                    $crop_y=$src_height-$crop_h;
                    break;
                default:
                    $crop_x=intval(($src_width-$crop_w)/2);
                    $crop_y=intval(($src_height-$crop_h)/2);
            }
            $image=$this->image;
            $canvas=new Imagick();
            if($this->type=='gif'){
                $images=$image->coalesceImages();
                foreach($images as $frame){
                    $img=new Imagick();
                    $img->readImageBlob($frame);
                    $img->cropImage($crop_w,$crop_h,$crop_x,$crop_y);
                    $img->thumbnailImage($width,$height,true);
                    $canvas->addImage($img);
                    $canvas->setImageDelay($img->getImageDelay());
                    $canvas->setImagePage($width,$height,0,0);
                }
            }else{
                $image->cropImage($crop_w,$crop_h,$crop_x,$crop_y);
                $image->thumbnailImage($width,$height,true);
                $canvas->addImage($image);
                $canvas->setImagePage($width,$height,0,0);
            }
            $image->destroy();
            $this->image=$canvas;
        }
    }
    /**
        添加图片水印
        参数:
            $path:水印图片(包含完整路径)
            $x,$y:水印座标
    /**/
    public function add_watermark($path,$x=0,$y=0){
        $watermark=new Imagick($path);
        $draw=new ImagickDraw();
        $draw->composite($watermark->getImageCompose(),$x,$y,$watermark->getImageWidth(),$watermark->getimageheight(),$watermark);
        if($this->type=='gif'){
            $image=$this->image;
            $canvas=new Imagick();
            $images=$image->coalesceImages();
            foreach($image as $frame){
                $img=new Imagick();
                $img->readImageBlob($frame);
                $img->drawImage($draw);
                $canvas->addImage($img);
                $canvas->setImageDelay($img->getImageDelay());
            }
            $image->destroy();
            $this->image=$canvas;
        }else{
            $this->image->drawImage($draw);
        }
    }
    /**
        添加文字水印
        参数:
            $text:水印文字
            $x,$y:水印座标
    /**/
    public function add_text($text,$x=0,$y=0,$angle=0,$style=array()){
        $draw=new ImagickDraw();
        if(isset($style['font'])) $draw->setFont($style['font']);
        if(isset($style['font_size'])) $draw->setFontSize($style['font_size']);
        if(isset($style['fill_color'])) $draw->setFillColor($style['fill_color']);
        if(isset($style['under_color'])) $draw->setTextUnderColor($style['under_color']);
        if($this->type=='gif'){
            foreach($this->image as $frame){
                $frame->annotateImage($draw,$x,$y,$angle,$text);
            }
        }else{
            $this->image->annotateImage($draw,$x,$y,$angle,$text);
        }
    }
    /**
        图片存档
        参数:
            $path:存档的位置和新的档案名
    /**/
    public function save_to($path){
        $this->image->stripImage();
        switch($this->type){
        case 'gif':
            $this->image->writeImages($path,true);
            return ;
        case 'jpg':
        case 'jpeg':
            $this->image->setImageCompressionQuality($_ENV['ImgQ']);
            $this->image->writeImage($path);
            return ;
        case 'png':
            $flag = $this->image->getImageAlphaChannel();
            // 如果png背景不透明则压缩
            if(imagick::ALPHACHANNEL_UNDEFINED == $flag or imagick::ALPHACHANNEL_DEACTIVATE == $flag){
                $this->image->setImageType(imagick::IMGTYPE_PALETTE);
                $this->image->writeImage($path);
            }else{
                $this->image->writeImage($path);
            }unset($flag);
            return ;
        default:
            $this->image->writeImage($path);
            return ;
        }
    }
    // 直接输出图像到萤幕
    public function output($header=true){
        if($header) header('Content-type: '.$this->type);
        echo $this->image->getImagesBlob();
    }
    /**
        建立缩小图
        $fit为真时,将保持比例并在$width X $height内?生缩小图
    /**/
    public function thumbnail($width=100,$height=100,$fit=true){$this->image->thumbnailImage($width,$height,$fit);}
    /**
        给图像添加边框
        $width: 左右边框宽度
        $height: 上下边框宽度
        $color: ?色
    /**/
    public function border($width,$height,$color='rgb(220,220,220)'){
        $color=new ImagickPixel();
        $color->setColor($color);
        $this->image->borderImage($color,$width,$height);
    }
    //取得图像宽度
    public function get_width(){$size=$this->image->getImagePage();return $size['width'];}
    //取得图像高度
    public function get_height(){$size=$this->image->getImagePage();return $size['height'];}
    // 设置图像类型
    public function set_type($type='png'){$this->type=$type;$this->image->setImageFormat($type);}
    // 取得图像类型
    public function get_type(){return $this->type;}
    public function blur($radius,$sigma){$this->image->blurImage($radius,$sigma);} // 模糊
    public function gaussian_blur($radius,$sigma){$this->image->gaussianBlurImage($radius,$sigma);} // 高斯模糊
    public function motion_blur($radius,$sigma,$angle){$this->image->motionBlurImage($radius,$sigma,$angle);} // 运动模糊
    public function radial_blur($radius){$this->image->radialBlurImage($radius);} // 径向模糊
    public function add_noise($type=null){$this->image->addNoiseImage($type==null?imagick::NOISE_IMPULSE:$type);} // 添加噪点
    public function level($black_point,$gamma,$white_point){$this->image->levelImage($black_point,$gamma,$white_point);} // 调整色阶
    public function modulate($brightness,$saturation,$hue){$this->image->modulateImage($brightness,$saturation,$hue);} // 调整亮度,饱和度,色调
    public function charcoal($radius,$sigma){$this->image->charcoalImage($radius,$sigma);} // 素描效果
    public function oil_paint($radius){$this->image->oilPaintImage($radius);} // 油画效果
    public function flop(){$this->image->flopImage();} // 水平翻转
    public function flip(){$this->image->flipImage();} // 垂直翻转
}

状况二的解决办法如下:

首先用/usr/local/imagemagick/bin/convert -version指令查看一下输出内容是否已经开?⒘硕嘞叱?Features:的值为空说明是单线程,如果Features:的值是openMP说明是多线程.imagick的多线程模式有一个bug,他会导致多核心的cpu使用率瞬间?升到100所以一定要使用它的单线程模式才行.

Version: ImageMagick 6.7.1-2 2014-05-29 Q16 http://www.imagemagick.org
Copyright: Copyright (C) 1999-2011 ImageMagick Studio LLC
Features:  

 上边是我配置正确时显示的结果,如果没有配置正确会显示下边的结果

Version: ImageMagick 6.7.1-2 2014-05-29 Q16 http://www.imagemagick.org
Copyright: Copyright (C) 1999-2011 ImageMagick Studio LLC
Features: openMP

 第一种结果是单线程模式,第二种结果是多线程模式,因为imagick的多线程模式有bug,所以如果您刚开始是用多线程模式安装的imagick那就必须要yum remove imagemagick将其卸载掉重新安装才行.

经过重写class,重装imagick之后一切正常,而且处理图像的效能比之以前有了大幅提升

 

PHP 相关文章推荐
php 面试碰到过的问题 在此做下记录
Jun 09 PHP
PHP上传文件时文件过大$_FILES为空的解决方法
Nov 26 PHP
phpMyAdmin自动登录和取消自动登录的配置方法
May 12 PHP
php使用fputcsv()函数csv文件读写数据的方法
Jan 06 PHP
PHP实现的购物车类实例
Jun 17 PHP
PHP实现对png图像进行缩放的方法(支持透明背景)
Jul 15 PHP
php抽象方法和抽象类实例分析
Dec 07 PHP
laravel中命名路由的使用方法
Feb 24 PHP
php处理静态页面:页面设置缓存时间实例
Jun 22 PHP
php+croppic.js实现剪切上传图片功能
Aug 14 PHP
laravel 关联关系遍历数组的例子
Oct 10 PHP
解决Laravel5.5下的toArray问题
Oct 15 PHP
PHP小技巧之函数重载
Jun 02 #PHP
mac下Apache + MySql + PHP搭建网站开发环境
Jun 02 #PHP
在PHP中使用X-SendFile头让文件下载更快
Jun 01 #PHP
PHP is_subclass_of函数的一个BUG和解决方法
Jun 01 #PHP
PHP中数组的分组排序实例
Jun 01 #PHP
php_screw安装使用教程(另一个PHP代码加密实现)
May 29 #PHP
PHP Curl出现403错误的解决办法
May 29 #PHP
You might like
php操作sqlserver关于时间日期读取的小小见解
2009/11/29 PHP
PHP输出Excel PHPExcel的方法
2018/07/26 PHP
laravel框架数据库配置及操作数据库示例
2019/10/10 PHP
Javascript四舍五入Math.round()与Math.pow()使用介绍
2013/12/27 Javascript
jquery sortable的拖动方法示例详解
2014/01/16 Javascript
javascript setinterval 的正确语法如何书写
2014/06/17 Javascript
JS使用for循环遍历Table的所有单元格内容
2014/08/21 Javascript
ionic组件ion-tabs选项卡切换效果实例
2016/08/27 Javascript
CSS3 media queries结合jQuery实现响应式导航
2016/09/30 Javascript
详解网站中图片日常使用以及优化手法
2017/01/09 Javascript
php输出全部gb2312编码内的汉字方法
2017/03/04 Javascript
JS表格组件神器bootstrap table使用指南详解
2017/04/12 Javascript
vue指令只能输入正数并且只能输入一个小数点的方法
2018/06/08 Javascript
jquery操作select常见方法大全【7种情况】
2019/05/28 jQuery
vue swipe自定义组件实现轮播效果
2019/07/03 Javascript
Nuxt使用Vuex的方法示例
2019/09/06 Javascript
mpvue微信小程序开发之实现一个弹幕评论
2019/11/24 Javascript
javascript 设计模式之享元模式原理与应用详解
2020/04/08 Javascript
Vue-cli4 配置 element-ui 按需引入操作
2020/09/11 Javascript
[37:03]完美世界DOTA2联赛PWL S3 INK ICE vs GXR 第二场 12.16
2020/12/18 DOTA
在Python中测试访问同一数据的竞争条件的方法
2015/04/23 Python
Python对文件操作知识汇总
2016/05/15 Python
Django实现的自定义访问日志模块示例
2017/06/23 Python
Python定义二叉树及4种遍历方法实例详解
2018/07/05 Python
python 实现兔子生兔子示例
2019/11/21 Python
使用Python pip怎么升级pip
2020/08/11 Python
HTML5 Canvas 破碎重组的视频特效的示例代码
2019/09/24 HTML / CSS
社区活动邀请函范文
2014/01/29 职场文书
大二学生职业生涯规划书
2014/02/05 职场文书
军训自我鉴定100字
2014/02/13 职场文书
员工三分钟演讲稿
2014/08/19 职场文书
教师四风对照检查材料思想汇报
2014/09/17 职场文书
乡镇党员干部四风对照检查材料思想汇报
2014/09/27 职场文书
董事长开业致辞
2015/07/29 职场文书
写给同事的离职感言
2015/08/04 职场文书
通过T-SQL语句创建游标与实现数据库加解密功能
2022/03/16 SQL Server