Files
Pichome/core/class/class_ImagePalette.php
2024-01-31 01:00:33 +08:00

473 lines
12 KiB
PHP

<?php
/**
* This file is part of the ImagePalette package.
*
* (c) Brian McDonald <brian@brianmcdonald.io>
* (c) gandalfx - https://github.com/gandalfx
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Class ImagePalette
*
* Gets the prominent colors in a given image. To get common color matching, all pixels are matched
* against a white-listed color palette.
*
* @package BrianMcdo\ImagePalette
*/
class ImagePalette implements IteratorAggregate
{
/**
* File or Url
* @var string
*/
protected $file;
/**
* Loaded Image
* @var object
*/
protected $loadedImage;
/**
* Process every Nth pixel
* @var int
*/
protected $precision;
/**
* Width of image
* @var integer
*/
protected $width;
/**
* Height of image
* @var integer
*/
protected $height;
/**
* Number of colors to return
* @var integer
*/
protected $paletteLength;
/**
* Colors Whitelist
* @var array
*/
/* protected $whiteList = array(
0x660000, 0x990000, 0xcc0000, 0xcc3333, 0xea4c88, 0x993399,
0x663399, 0x333399, 0x0066cc, 0x0099cc, 0x66cccc, 0x77cc33,
0x669900, 0x336600, 0x666600, 0x999900, 0xcccc33, 0xffff00,
0xffcc33, 0xff9900, 0xff6600, 0xcc6633, 0x996633, 0x663300,
0x000000, 0x999999, 0xcccccc, 0xffffff, 0xE7D8B1, 0xFDADC7,
0x424153, 0xABBCDA, 0xF5DD01
);*/
protected $whiteList = array(
0x111111,0xFFFFFF,0x9E9E9E,0xA48057,0xFC85B3,0xFF2727,0xFFA34B,0xFFD534,0x47C595,0x51C4C4,0x2B76E7,0x6D50ED
);
protected $whiteList1 = array();
/**
* Colors that were found to be prominent
* Array of Color objects
*
* @var array
*/
protected $palette;
protected $colorNumber;
/**
* Library used
* Supported are GD and Imagick
* @var string
*/
protected $lib;
/**
* Constructor
* @param string $file
* @param int $precision
* @param int $paletteLength
* @param string $library
*/
public function __construct($file, $precision = 10, $paletteLength = 5, $library = 'gd',$whiteList=array())
{
$this->file = $file;
$this->precision = $precision;
$this->paletteLength = $paletteLength;
// use provided libname or auto-detect
$this->lib = $this->graphicsLibrary($library);
// create an array with color ints as keys
$this->whiteList = array_fill_keys($whiteList?$whiteList:$this->whiteList, 0);
$this->whiteList1 = array_fill_keys($whiteList?$whiteList:$this->whiteList, array());
$this->process($this->lib);
}
/**
* Select graphics library to use for processing
*
* @param string $lib
* @return mixed
* @throws \Exception
*/
protected function graphicsLibrary($lib = 'gd')
{
$libraries = [
'gd' => 'GD',
'imagick' => 'Imagick',
//'gmagick' => [false, 'Gmagick']
];
if( ! array_key_exists($lib, $libraries))
{
throw new Exception('This extension specified is not supported.');
}
if( ! extension_loaded($lib))
{
throw new Exception('This extension is not installed');
}
return $libraries[$lib];
}
/**
* Select a graphical library and start generating the Image Palette
* @param string $lib
* @throws \Exception
*/
protected function process($lib)
{
if(!$this->{'setWorkingImage' . $lib} ()) return false;
$this->{'setImagesize' . $lib} ();
$this->readPixels();
$ps=array_keys($this->whiteList);
// sort whiteList
arsort($this->whiteList1);
$total=0;
foreach($this->whiteList as $key => $v){
$total+=intval($v);
}
$arr=array();
$arr1=array();
foreach($this->whiteList1 as $key =>$val){
if(!($this->whiteList[$key])) continue;
arsort($val);
$v=array_keys($val);
$arr[$v[0]]=round(($this->whiteList[$key]/$total)*100,2);
$arr1[$v[0]]=array_search($key,$ps);
}
$arr=array_slice($arr,0,$this->paletteLength,true);
$arr1=array_slice($arr1,0,$this->paletteLength,true);
$this->palette=$arr;
$this->colorNumber=$arr1;
// sort whiteList accordingly
// $this->filter($arr);
/*array_map(
function($color) {
return new Color($color);
},
array_keys($arr)
);*/
}
public function getPalette(){
return $this->palette;
}
public function getColorNumber(){
return $this->colorNumber;
}
public function getWhiteList1(){
return $this->whiteList1;
}
public function getWidth(){
return $this->width;
}
public function getHeight(){
return $this->height;
}
/**
* Load and set the working image.
*/
protected function setWorkingImageGD()
{
//$extension = 'jpg';//pathinfo($this->file, PATHINFO_EXTENSION);
$info=@getimagesize($this->file);
switch ($info['mime'])
{
case 'image/png':
$this->loadedImage = imagecreatefrompng($this->file);
break;
case 'image/jpeg':
$this->loadedImage = imagecreatefromjpeg($this->file);
break;
case 'image/gif':
$this->loadedImage = imagecreatefromgif($this->file);
break;
case 'image/webp':
$this->loadedImage = imagecreatefromwebp($this->file);
break;
default:
throw new Exception("The file type .$extension is not supported.");
}
return ture;
}
/**
* Load and set working image
*
* @todo needs work
* @return mixed
*/
protected function setWorkingImageImagick()
{
$file = file_get_contents($this->file);
$temp = tempnam(DZZ_ROOT.'./data/attachment/cache', uniqid("ImagePalette_", true));
if(!file_put_contents($temp, $file)) return false;
$this->loadedImage = new Imagick($temp);
$this->loadedImage ->thumbnailImage(64,64,true);
@unlink($temp);
return true;
}
/**
* Load and set working image
*
* @todo needs work
* @throws \Exception
* @return mixed
*/
protected function setWorkingImageGmagick()
{
throw new Exception("Gmagick not supported");
}
/**
* Get and set size of the image using GD.
*/
protected function setImageSizeGD()
{
list($this->width, $this->height) = getimagesize($this->file);
}
/**
* Get and set size of image using ImageMagick.
*/
protected function setImageSizeImagick()
{
$d = $this->loadedImage->getImageGeometry();
$this->width = $d['width'];
$this->height = $d['height'];
}
/**
* For each interesting pixel, add its closest color to the loaded colors array
*
* @return mixed
*/
protected function readPixels()
{
// Row
for ($x = 0; $x < $this->width; $x += $this->precision)
{
// Column
for ($y = 0; $y < $this->height; $y += $this->precision)
{
$color = $this->getPixelColor($x, $y);
// transparent pixels don't really have a color
if ($color->isTransparent())
continue 1;
// increment closes whiteList color (key)
$this->whiteList[ $this->getClosestColor($color) ]++;
//$this->whiteList1[$color->toInt()]++;
}
}
}
/**
* Get closest matching color
*
* @param Color $color
* @return int
*/
protected function getClosestColor(Color $color)
{
$cint=$color->toInt();
$bestDiff = PHP_INT_MAX;
// default to black so hhvm won't cry
$bestColor = 0x000000;
$rgbarr=array();
foreach ($this->whiteList as $wlColor => $hits)
{
//echo $wlColor;
// calculate difference (don't sqrt)
$diff = $color->getDiff($wlColor);
// see if we got a new best
if ($diff < $bestDiff)
{
$bestDiff = $diff;
$bestColor = $wlColor;
}
}
if(!isset( $this->whiteList1[$bestColor][$cint])) $this->whiteList1[$bestColor][$cint]=1;
else {
$this->whiteList1[$bestColor][$cint]++;
}
return $bestColor;
}
/**
* Returns an array describing the color at x,y
* At index 0 is the color as a whole int (may include alpha)
* At index 1 is the color's red value
* At index 2 is the color's green value
* At index 3 is the color's blue value
*
* @param int $x
* @param int $y
* @return Color
*/
protected function getPixelColor($x, $y)
{
return $this->{'getPixelColor' . $this->lib} ($x, $y);
}
/**
* Using to retrieve color information about a specified pixel
*
* @see getPixelColor()
* @param int $x
* @param int $y
* @return Color
*/
protected function getPixelColorGD($x, $y)
{
$color = imagecolorat($this->loadedImage, $x, $y);
return new Color (
$color
// $rgb['red'],
// $rgb['green'],
// $rgb['blue']
);
}
/**
* Using to retrieve color information about a specified pixel
*
* @see getPixelColor()
* @param int $x
* @param int $y
* @return Color
*/
protected function getPixelColorImagick($x, $y)
{
$rgb = $this->loadedImage->getImagePixelColor($x, $y)->getColor();
foreach($rgb as $k => $v){
if($v<0) $v=0;
$rgb[$k]=$v;
}
return new Color([
$rgb['r'],
$rgb['g'],
$rgb['b'],
]);
}
protected function getPixelColorGmagick($x, $y)
{
throw new Exception("Gmagick not supported: ($x, $y)");
}
/**
* Returns an array of Color objects
*
* @param int $paletteLength
* @return array
*/
public function getColors($paletteLength = null)
{
// allow custom length calls
if ( ! is_numeric($paletteLength))
{
$paletteLength = $this->paletteLength;
}
// take the best hits
return array_slice($this->palette, 0, $paletteLength, true);
}
/**
* Returns a json encoded version of the palette
*
* @return string
*/
public function __toString()
{
// Color PHP 5.3 compatible -> not JsonSerializable :(
return json_encode(array_map(
function($color) {
return (string) $color;
},
$this->getColors()
));
}
/**
* Convenient getter access as properties
*
* @param $name
* @throws \Exception
* @return mixed
*/
public function __get($name)
{
$method = 'get' . ucfirst($name);
if (method_exists($this, $method))
{
return $this->$method();
}
throw new Exception("Method $method does not exist");
}
/**
* Returns the palette for implementation of the IteratorAggregate interface
* Used in foreach loops
*
* @see getColors()
* @return \ArrayIterator
*/
public function getIterator()
{
return new ArrayIterator($this->getColors());
}
}