Location: PHPKode > scripts > Image Booth > image-booth/ImageBooth.php
<?
// ***********************************************************************************
// * ImageBooth by Abbey Hawk Sparrow
// ***********************************************************************************
// * This class abstracts image manipulations functions into an Object Oriented
// * abstraction layer. The purpose of this is to make working with images in code
// * equivilent to using your favorite image editor. You can expect, for the near term
// * my priority will be to implement most of what older releases of photoshop provide.
// *
// * This started as a preprocessor to optimize images for OCR, but is expanding in
// * scope. I will listen to feature requests.
// ***********************************************************************************
// * KEY FEATURES *
// ****************
// * 1) Layers
// * 2) Sizing: Image and Canvas
// * 3) Filters: blur, threshold, greyscale, emboss, lines and edges
// ***********************************************************************************
// * EXAMPLE *
// ***********
// * Let's say I want a greyscale thumbnail with a watermark:
// * $image = ImageBooth::newImage(imagecreatefromjpeg('./image.jpg'));
// * $image->filter('greyscale');
// * $image->newLayer(imagecreatefrompng('./watermark_overlay.png'));
// * $image->resize(50, 50);
// * $image->dumpToBrowser();
// ***********************************************************************************

// pre PHP 5.1 convolver function credit to: mgcclx at gmail dot com
if(!function_exists('imageconvolution')){ function imageconvolution($src, $filter, $filter_div, $offset){
if ($src==NULL) return 0; $sx = imagesx($src); $sy = imagesy($src); $srcback = ImageCreateTrueColor ($sx, $sy); ImageCopy($srcback, $src,0,0,0,0,$sx,$sy); if($srcback==NULL) return 0; $pxl = array(1,1); for ($y=0; $y<$sy; ++$y){ for($x=0; $x<$sx; ++$x){ $new_r = $new_g = $new_b = 0; $alpha = imagecolorat($srcback, $pxl[0], $pxl[1]); $new_a = $alpha >> 24; for ($j=0; $j<3; ++$j) { $yv = min(max($y - 1 + $j, 0), $sy - 1); for ($i=0; $i<3; ++$i) { $pxl = array(min(max($x - 1 + $i, 0), $sx - 1), $yv); $rgb = imagecolorat($srcback, $pxl[0], $pxl[1]); $new_r += (($rgb >> 16) & 0xFF) * $filter[$j][$i]; $new_g += (($rgb >> 8) & 0xFF) * $filter[$j][$i]; $new_b += ($rgb & 0xFF) * $filter[$j][$i]; } } $new_r = ($new_r/$filter_div)+$offset; $new_g = ($new_g/$filter_div)+$offset; $new_b = ($new_b/$filter_div)+$offset; $new_r = ($new_r > 255)? 255 : (($new_r < 0)? 0:$new_r); $new_g = ($new_g > 255)? 255 : (($new_g < 0)? 0:$new_g); $new_b = ($new_b > 255)? 255 : (($new_b < 0)? 0:$new_b); $new_pxl = ImageColorAllocateAlpha($src, (int)$new_r, (int)$new_g, (int)$new_b, $new_a); if ($new_pxl == -1) $new_pxl = ImageColorClosestAlpha($src, (int)$new_r, (int)$new_g, (int)$new_b, $new_a); if (($y >= 0) && ($y < $sy)) imagesetpixel($src, $x, $y, $new_pxl); } } imagedestroy($srcback); return 1; }
}

class ImageBooth{
    private static $initialized = false;
    private static $filters = false;
    
    public static $color = '000000';
    public static $background_color = 'FFFFFF';

    
    public static function newImage($var1 = null, $var2 = null, $var3 = null){
        ImageBooth::checkInit();
        return new ImBo_Image($var1, $var2, $var3);
    }
    
    public static function filter($name, $image, $controls = null){
        ImageBooth::checkInit();
        if(isset(ImageBooth::$filters[$name])) return ImageBooth::$filters[$name]->filter($image, $controls);
        else throw new Exception('Filter '.$name.' unsupported!');
    }
    
    protected static function checkInit(){
        if(!ImageBooth::$initialized){
            ImageBooth::$filters['blur'] = new ImBo_Gaussian_Blur_Filter();
            ImageBooth::$filters['threshold'] = new ImBo_Threshold_Filter();
            ImageBooth::$filters['greyscale'] = new ImBo_Greyscale_Filter();
            ImageBooth::$filters['emboss'] = new ImBo_Gradient_Detection_Filter();
            ImageBooth::$filters['lines'] = new ImBo_Line_Detection_Filter();
            ImageBooth::$filters['edges'] = new ImBo_Sobel_Filter();
            ImageBooth::$initialized = true;
        }
    }
    
    public static function threshold(&$original_image, $new_image, $threshold){
        //TODO: thresholding function (original vs modified)
    }
    
    public static function convolve(&$image, $matrix){
        return imageconvolution($image, $matrix, 16, 0);
    }
    
    private static function load_filters($directory){
        //TODO: externally loadable filters
    }
    
    
}

class ImBo_Image{
    private $layers = array();
    private $height = null;
    private $width = null;
    function __construct($var1 = null, $var2 = null, $var3 = null){ //(image), (x, y), (x, y, bit_depth), (x, y, color)
        if(is_numeric($var1) && is_numeric($var2)){
            // we have x & y
            $this->width = $var1;
            $this->height = $var2;
        }else if($var1 != null){
            // we have an image
            $image = $var1;
            $this->width = imagesy($image);
            $this->height = imagesx($image);
            $this->newLayer($image);
        }
    }
    
    public function filter($name, $controls = null){
        $layer = current($this->layers);
        return $layer->filter($name, $controls);
    }
    
    public function dumpToBrowser(){
        $image = $this->image();
        header("Content-type: image/png");
        imagepng($image);
        imagedestroy($image);
    }
    
    public function newLayer($image = null){
        imagealphablending($image, false);
        imagesavealpha($image, true);
        $this->layers[] = new ImBo_Layer($image);
        return $this->layers[count($this->layers)-1];
    }
    
    public function selectLayer($name){
        //todo
    }
    
    public function composite($dst_image, $src_image){
        imagecopyresampled($dst_image,$src_image, 0,0,0,0, imagesx($src_image),imagesy($src_image),imagesx($dst_image),imagesy($dst_image));
        return $dst_image;
    }
    
    public function image(){
        if(count($this->layers) == 1){
            $arr = array_keys($this->layers);
            return $this->layers[$arr[0]]->getImage();
        }else{
            //composite the layers
            $composite_image = imagecreatetruecolor( $this->height, $this->width );
            $transparent_color = imagecolorallocate($composite_image, 255, 0, 0);
            imagecolortransparent($composite_image, $transparent_color);
            imagefilledrectangle($composite_image,0,0,imagesx($composite_image),imagesy($composite_image),$transparent_color);
            
            foreach($this->layers as $layer){
                $layer_image = $layer->getImage();
                $composite_image = $this->composite($composite_image, $layer_image);
            }
            return $composite_image;
        }
    }
    
    public function resize($height, $width){
        $this->height = $height;
        $this->width = $width;
        foreach($this->layers as $layer) $layer->resize($height, $width);
    }
    
    public function resizeCanvas($height, $width, $horizontalAnchor='middle', $verticalAnchor='center'){
        $this->height = $height;
        $this->width = $width;
        foreach($this->layers as $layer) $layer->resizeCanvas($height, $width, $horizontalAnchor, $verticalAnchor);
    }
    
    public function rgbAt($x, $y){
        $image = $this->image();
        $rgb = imagecolorat($image, $x, $y);
        return array(($rgb >> 16) & 0xFF, ($rgb >> 8) & 0xFF, $rgb & 0xFF);
    }
    
    //public function crop($x, $y, $width, $height){ }
    //public function rotate($degrees){ }
    //public function scale($vertical, $horizontal){ }
    //public function skew($top_offset, $bottom_offset){ }
    //public function brightnessAndContrast($brightness, $contrast){ }
    //public function freeTransform($p1_x_drift, $p1_y_drift, $p2_x_drift, $p2_y_drift, $p3_x_drift, $p3_y_drift, $p4_x_drift, $p4_y_drift){ }
}

class ImBo_Layer{
    private $image;
    //private $mask = null;
    //private $shapes = array();
    
    function __construct($image, $name = null){
        $this->image = $image;
    }
    
    public function getImage(){
        return $this->image;
    }
    
    public function resize($height, $width){
        $image_resized = imagecreatetruecolor( $height, $width );
        imagealphablending($image_resized, false);
        imagesavealpha($image_resized, true);
        imagecopyresampled($image_resized, $this->image, 0,0,0,0,$height, $width, imagesx($this->image),imagesy($this->image));
        $this->image = $image_resized;
    }
    
    public function resizeCanvas($height, $width, $horizontalAnchor='middle', $verticalAnchor='center'){
        //calc x & y based on alignment
        $current_width = imagesx($this->image);
        $current_height = imagesy($this->image);
        switch($horizontalAnchor){
            case 'left':
                $x = 0;
                break;
            case 'middle':
                $current_middle = ($current_width/2);
                $new_middle = ($width/2);
                $x = $new_middle - $current_middle;
                break;
            case 'right':
                $x = $width - $current_width;
                break;
        }
        switch($verticalAnchor){
            case 'top':
                $y = 0;
                break;
            case 'center':
                $current_center = ($current_height/2);
                $new_center = ($height/2);
                $y = $new_center - $current_center;
                break;
            case 'bottom':
                $y = $height - $current_height;
                break;
        }
        $image_resized = imagecreatetruecolor( $height, $width );
        imagealphablending($image_resized, false);
        imagesavealpha($image_resized, true);
        $transparent_color = imagecolorallocate($image_resized, 255, 0, 0);
            imagecolortransparent($image_resized, $transparent_color);
            imagefilledrectangle($image_resized,0,0,imagesx($image_resized),imagesy($image_resized),$transparent_color);
        imagecopyresampled($image_resized, $this->image, $x,$y,0,0, $current_width, $current_height, $current_width, $current_height);
        $this->image = $image_resized;
    }
    
    public function move(){
    
    }
    
    public function filter($name, $controls = null){
        $this->image = ImageBooth::filter($name, $this->image, $controls);
    }
    
    public function rgbAt($x, $y){
        $rgb = imagecolorat($this->image, $x, $y);
        return array(($rgb >> 16) & 0xFF, ($rgb >> 8) & 0xFF, $rgb & 0xFF);
    }
}

abstract class ImBo_Filter{
    public abstract function name();
    public abstract function filter(&$image, $controls=null);
    
    public function getControls(){
        return array();
    }
}

// DEFAULT FILTERS
class ImBo_Gaussian_Blur_Filter extends ImBo_Filter{
    public function name(){ return 'blur';}
    private $gaussian_blur_matrix = array(
                array( 1.0, 2.0, 1.0 ),
                array( 2.0, 4.0, 2.0 ),
                array( 1.0, 2.0, 1.0 )
            );
    
    public function filter(&$image, $incoming_controls=null){
        if($controls == null ) $controls = $this->getControls();
        
        // let's cleanup the controls so it's just name = value
        foreach($controls as $name => $values) if(is_array($values)) $controls[$name] = $values['value'];
        if($incoming_controls != null) foreach($incoming_controls as $name => $value) if( isset($value) && $value != null ) $controls[$name] = $value;
        
        //set up control values
        $amount = $controls['amount'];
        $radius = $controls['radius'];
        $threshold = $controls['threshold'];
        if ($amount > 500) $amount = 500;
        $amount = ((float)$amount) * 0.016;
        if ($radius > 50) $radius = 50;
        $radius = $radius * 2;
        if ($threshold > 255) $threshold = 255;
        $radius = abs(round($radius)); // Only integers make sense.  
        if ($radius == 0) { return $image; imagedestroy($image); break; }
        
        // make a copy to blur
        $blurred_image = imagecreatetruecolor(imagesx($image), imagesy($image));
        imagecopy($blurred_image, $image, 0, 0, 0, 0, imagesx($image), imagesy($image));
        
        //blur
        ImageBooth::convolve($blurred_image, $this->gaussian_blur_matrix);
        return $blurred_image;
    }

    public function getControls(){
        // attempts to mimic PS settings
        return array(
            'amount' => array(
                'value' => '500',
                'default' => '500',
                'upper_bound' => '500',
                'lower_bound' => '0'
            ),
            'radius' => array(
                'value' => '50',
                'default' => '50',
                'upper_bound' => '50',
                'lower_bound' => '0'
            ),
            'threshold' => array(
                'value' => '255',
                'default' => '255',
                'upper_bound' => '255',
                'lower_bound' => '0'
            ),
        
        );
    }
}

class ImBo_Threshold_Filter extends ImBo_Filter{
    public function name(){ return 'threshold';}
    
    public function filter(&$image, $incoming_controls=null){
        if($controls == null ) $controls = $this->getControls();
        // let's cleanup the controls so it's just name = value
        foreach($controls as $name => $values) if(is_array($values)) $controls[$name] = $values['value'];
        if($incoming_controls != null) foreach($incoming_controls as $name => $value) if( isset($value) && $value != null ) $controls[$name] = $value;
        
        // make a copy to blur
        $im = imagecreatetruecolor(imagesx($image), imagesy($image));
        imagecopy($im, $image, 0, 0, 0, 0, imagesx($image), imagesy($image));
        
        $level = $controls['level'];
        if ($level > 255) $level = 255;
        
        // from class.image.php : Andrew Collington, 2005
        // hide@address.com, http://php.amnuts.com/
        $sx = imagesx($im);
        $sy = imagesy($im);
        $black = imagecolorallocate($im, 0, 0, 0);
        $white = imagecolorallocate($im, 255, 255, 255);
        for ($x = 0; $x < $sx; $x++) {
            for ($y = 0; $y < $sy; $y++) {
                $rgb = imagecolorat($image, $x, $y);
                $rgb = array(($rgb >> 16) & 0xFF, ($rgb >> 8) & 0xFF, $rgb & 0xFF);
                $intensity = ($rgb[0] + $rgb[1] + $rgb[2]) / 3;
                imagesetpixel($im, $x, $y, ($intensity < $level ? $black : $white));
            }
        }
        return $im;
    }

    public function getControls(){
        // attempts to mimic PS settings
        return array(
            'level' => array(
                'value' => '128',
                'default' => '128',
                'upper_bound' => '255',
                'lower_bound' => '0'
            )
        );
    }
}

class ImBo_Greyscale_Filter extends ImBo_Filter{
    public function name(){ return 'greyscale';}
    
    public function filter(&$image, $incoming_controls=null){
        if($controls == null ) $controls = $this->getControls();
        // let's cleanup the controls so it's just name = value
        foreach($controls as $name => $values) if(is_array($values)) $controls[$name] = $values['value'];
        if($incoming_controls != null) foreach($incoming_controls as $name => $value) if( isset($value) && $value != null ) $controls[$name] = $value;
        
        // make a copy to blur
        $im = imagecreatetruecolor(imagesx($image), imagesy($image));
        imagecopy($im, $image, 0, 0, 0, 0, imagesx($image), imagesy($image));
        
        $level = $controls['level'];
        if ($level > 255) $level = 255;
        
        // from class.image.php : Andrew Collington, 2005
        // hide@address.com, http://php.amnuts.com/
        $sx = imagesx($im);
        $sy = imagesy($im);
        for ($x = 0; $x < $sx; $x++) {
            for ($y = 0; $y < $sy; $y++) {
                //$rgb = Image::getPixelRGB($im, $x, $y);
                $rgb = imagecolorat($image, $x, $y);
                $rgb = array(($rgb >> 16) & 0xFF, ($rgb >> 8) & 0xFF, $rgb & 0xFF);
                $colour = ($rgb[0] + $rgb[1] + $rgb[2]) / 3;
                imagesetpixel($im, $x, $y, imagecolorallocate($im, $colour, $colour, $colour));
            }
        } 
        return $im;
    }
}

class ImBo_Line_Detection_Filter extends ImBo_Filter{
    public function name(){ return 'line_detect';}
    private $horizontal_gradient_matrix = array(
                array( -1.0, -1.0, -1.0 ),
                array( 2.0, 2.0, 2.0 ),
                array( -1.0, -1.0, -1.0 )
            );
    private $vertical_gradient_matrix = array(
                array( -1.0, 2.0, -1.0 ),
                array( -1.0, 2.0, -1.0 ),
                array( -1.0, 2.0, -1.0 )
            );
    private $left_diagonal_gradient_matrix = array(
                array( 2.0, -1.0, -1.0 ),
                array( -1.0, 2.0, -1.0 ),
                array( -1.0, -1.0, 2.0 )
            );
    private $right_diagonal_gradient_matrix = array(
                array( -1.0, -1.0, 2.0 ),
                array( -1.0, 2.0, -1.0 ),
                array( 2.0, -1.0, -1.0 )
            );
    
    public function filter(&$image, $incoming_controls=null){
        if($controls == null ) $controls = $this->getControls();
        
        // let's cleanup the controls so it's just name = value
        foreach($controls as $name => $values) if(is_array($values)) $controls[$name] = $values['value'];
        if($incoming_controls != null) foreach($incoming_controls as $name => $value) if( isset($value) && $value != null ) $controls[$name] = $value;
        
        // make a copy to detect
        $convolved_image = imagecreatetruecolor(imagesx($image), imagesy($image));
        imagecopy($convolved_image, $image, 0, 0, 0, 0, imagesx($image), imagesy($image));
        
        //detect
        if($controls['direction'] == 'horizontal'){
            ImageBooth::convolve($convolved_image, $this->horizontal_gradient_matrix);
        }else if($controls['direction'] == 'vertical'){
            ImageBooth::convolve($convolved_image, $this->vertical_gradient_matrix);
        }else if($controls['direction'] == 'left-diagonal'){
            ImageBooth::convolve($convolved_image, $this->left_diagonal_gradient_matrix);
        }else if($controls['direction'] == 'right-diagonal'){
            ImageBooth::convolve($convolved_image, $this->right_diagonal_gradient_matrix);
        }
        return $convolved_image;
    }

    public function getControls(){
        // attempts to mimic PS settings
        return array(
            'direction' => array(
                'value' => 'left-diagonal',
                'default' => 'left-diagonal',
                'enumeration' => 'horizontal,vertical,left-diagonal,right-diagonal'
            ),
        );
    }
}

class ImBo_Gradient_Detection_Filter extends ImBo_Filter{
    public function name(){ return 'gradient_detect';}
    private $north_matrix = array(
                array( -1.0, -2.0, -1.0 ),
                array( 0.0, 0.0, 0.0 ),
                array( 1.0, 2.0, 1.0 )
            );
    private $west_matrix = array(
                array( -1.0, 0.0, 1.0 ),
                array( -2.0, 0.0, 2.0 ),
                array( -1.0, 0.0, 1.0 )
            );
    private $east_matrix = array(
                array( 1.0, 0.0, -1.0 ),
                array( 2.0, 0.0, -2.0 ),
                array( 1.0, 0.0, -1.0 )
            );
    private $south_matrix = array(
                array( 1.0, 2.0, 1.0 ),
                array( 0.0, 0.0, 0.0 ),
                array( -1.0, -2.0, -1.0 )
            );
            
    private $north_east_matrix = array(
                array( 0.0, -1.0, -2.0 ),
                array( 1.0, 0.0, -1.0 ),
                array( 2.0, 1.0, 0.0 )
            );
    private $south_west_matrix = array(
                array( 0.0, 1.0, 2.0 ),
                array( -1.0, 0.0, 1.0 ),
                array( -2.0, -1.0, 0.0 )
            );
    private $north_west_matrix = array(
                array( -2.0, -1.0, 0.0 ),
                array( -1.0, 0.0, 1.0 ),
                array( 0.0, 1.0, 2.0 )
            );
    private $south_east_matrix = array(
                array( 2.0, 1.0, 0.0 ),
                array( 1.0, 0.0, -1.0 ),
                array( 0.0, -1.0, -2.0 )
            );
    
    public function filter(&$image, $incoming_controls=null){
        if($controls == null ) $controls = $this->getControls();
        
        // let's cleanup the controls so it's just name = value
        foreach($controls as $name => $values) if(is_array($values)) $controls[$name] = $values['value'];
        if($incoming_controls != null) foreach($incoming_controls as $name => $value) if( isset($value) && $value != null ) $controls[$name] = $value;
        
        // make a copy to detect
        $convolved_image = imagecreatetruecolor(imagesx($image), imagesy($image));
        imagecopy($convolved_image, $image, 0, 0, 0, 0, imagesx($image), imagesy($image));
        
        //detect
        switch($controls['direction']){
            case 'north':
                ImageBooth::convolve($convolved_image, $this->north_matrix);
                break;
            case 'north-east':
                ImageBooth::convolve($convolved_image, $this->north_east_matrix);
                break;
            case 'east':
                ImageBooth::convolve($convolved_image, $this->east_matrix);
                break;
            case 'south-east':
                ImageBooth::convolve($convolved_image, $this->south_east_matrix);
                break;
            case 'south':
                ImageBooth::convolve($convolved_image, $this->south_matrix);
                break;
            case 'south-west':
                ImageBooth::convolve($convolved_image, $this->south_west_matrix);
                break;
            case 'west':
                ImageBooth::convolve($convolved_image, $this->west_matrix);
                break;
            case 'north-west':
                ImageBooth::convolve($convolved_image, $this->north_west_matrix);
                break;
        }
        return $convolved_image;
    }

    public function getControls(){
        // attempts to mimic PS settings
        return array(
            'direction' => array(
                'value' => 'left-diagonal',
                'default' => 'left-diagonal',
                'enumeration' => 'north,north-east,east,south-east,south,south-west,west,north-west'
            ),
        );
    }
}

class ImBo_Laplacian_Filter extends ImBo_Filter{
    public function name(){ return 'gradient_detect';}
    private $laplacian_matrix = array(
                array( 0.0, -1.0, 0.0 ),
                array( -1.0, 4.0, -1.0 ),
                array( 0.0, -1.0, 0.0 )
            );
    
    public function filter(&$image, $incoming_controls=null){
        if($controls == null ) $controls = $this->getControls();
        
        // let's cleanup the controls so it's just name = value
        foreach($controls as $name => $values) if(is_array($values)) $controls[$name] = $values['value'];
        if($incoming_controls != null) foreach($incoming_controls as $name => $value) if( isset($value) && $value != null ) $controls[$name] = $value;
        
        // make a copy to detect
        $convolved_image = imagecreatetruecolor(imagesx($image), imagesy($image));
        imagecopy($convolved_image, $image, 0, 0, 0, 0, imagesx($image), imagesy($image));
        
        //detect
        ImageBooth::convolve($convolved_image, $this->laplacian_matrix);
        return $convolved_image;
    }

    public function getControls(){
        // attempts to mimic PS settings
        return array(
        );
    }
}

class ImBo_Sobel_Filter extends ImBo_Filter{
    public function name(){ return 'edge_detect';}
    private $sobel_gx_matrix = array(
                array( -1.0, 0.0, 1.0 ),
                array( -2.0, 0.0, 2.0 ),
                array( -1.0, 0.0, 1.0 )
            );
    private $sobel_gy_matrix = array(
                array( 1.0, 2.0, 1.0 ),
                array( 0.0, 0.0, 0.0 ),
                array( -1.0, -2.0, -1.0 )
            );
    
    public function filter(&$image, $incoming_controls=null){
        if($controls == null ) $controls = $this->getControls();
        
        // let's cleanup the controls so it's just name = value
        foreach($controls as $name => $values) if(is_array($values)) $controls[$name] = $values['value'];
        if($incoming_controls != null) foreach($incoming_controls as $name => $value) if( isset($value) && $value != null ) $controls[$name] = $value;
        
        // make a copy to detect
        $convolved_image = imagecreatetruecolor(imagesx($image), imagesy($image));
        imagecopy($convolved_image, $image, 0, 0, 0, 0, imagesx($image), imagesy($image));
        
        //detect
        ImageBooth::convolve($convolved_image, $this->sobel_gx_matrix);
        ImageBooth::convolve($convolved_image, $this->sobel_gy_matrix);
        return $convolved_image;
    }

    public function getControls(){
        // attempts to mimic PS settings
        return array(
        );
    }
}

?>
Return current item: Image Booth