浅谈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加密解密的代码
Oct 09 PHP
php MsSql server时遇到的中文编码问题
Jun 11 PHP
PHP 解决utf-8和gb2312编码转换问题
Mar 18 PHP
PHP设计模式之装饰者模式
Feb 29 PHP
自己写了一个php检测文件编码的函数
Apr 21 PHP
php实现过滤字符串中的中文和数字实例
Jul 29 PHP
PHP 实现的将图片转换为TXT
Oct 21 PHP
php的4种常用运行方式详解
Dec 22 PHP
Laravel框架实现model层的增删改查(CURD)操作示例
May 12 PHP
原生JS实现Ajax通过POST方式与PHP进行交互的方法示例
May 12 PHP
PHP中实现中文字串截取无乱码的解决方法
May 29 PHP
thinkPHP事务操作简单案例分析
Oct 17 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将向Java靠拢
2006/10/09 PHP
人大复印资料处理程序_查询篇
2006/10/09 PHP
php $_SERVER[&quot;REQUEST_URI&quot;]获取值的通用解决方法
2010/06/21 PHP
php pki加密技术(openssl)详解
2013/07/01 PHP
Laravel模板引擎Blade中section的一些标签的区别介绍
2015/02/10 PHP
四个常见html网页乱码问题及解决办法
2015/09/08 PHP
详解no input file specified 三种解决方法
2019/11/29 PHP
this[] 指的是什么内容 讨论
2007/03/24 Javascript
JavaScript 编程引入命名空间的方法与代码
2007/08/13 Javascript
jQuery 渐变下拉菜单
2009/12/15 Javascript
关于JavaScript中var声明变量作用域的推断
2010/12/16 Javascript
javascript 函数及作用域总结介绍
2013/11/12 Javascript
node.js使用nodemailer发送邮件实例
2014/03/10 Javascript
javascript移出节点removeChild()使用介绍
2014/04/03 Javascript
响应式表格之固定表头的简单实现
2016/08/26 Javascript
jQuery基本选择器之标签名选择器
2016/09/03 Javascript
浅析JavaScript中作用域和作用域链
2016/12/06 Javascript
koa2实现登录注册功能的示例代码
2018/12/03 Javascript
[02:16]深扒TI7聊天轮盘语音出处2
2017/05/11 DOTA
python中将字典形式的数据循环插入Excel
2018/01/16 Python
Python实现检测文件MD5值的方法示例
2018/04/11 Python
python3 对list中每个元素进行处理的方法
2018/06/29 Python
深入了解Python在HDA中的应用
2019/09/05 Python
Python如何实现的二分查找算法
2020/05/27 Python
英国家用电器购物网站:Hughes
2018/02/23 全球购物
Lookfantastic瑞典:英国知名美妆购物网站
2018/04/06 全球购物
怎么写好自荐信
2013/10/30 职场文书
求职简历中个人的自我评价
2013/12/25 职场文书
超市开学活动方案
2014/03/01 职场文书
员工工作及收入证明
2014/10/28 职场文书
医院合作意向书范本
2015/05/08 职场文书
开展警示教育活动总结
2015/05/09 职场文书
2015年班组建设工作总结
2015/05/13 职场文书
大国崛起观后感
2015/06/02 职场文书
《月光曲》教学反思
2016/02/16 职场文书
Java 通过手写分布式雪花SnowFlake生成ID方法详解
2022/04/07 Java/Android