PHP/GD2 Duotone converter
Voor de website van de StadsOmroep, wilde ik dynamisch foto's in Duotone foto's kunnen omzetten. Na wat zoeken op het net kon ik het niet vinden en ben dus maar zelf achter de editor gekropen...
De eerste stap is het omzetten in grijswaarden. Hiervoor gebruik ik een heel eenvoudig algoritme: Het gemiddelde van de RGB waarden (setGrey werkt iets anders, maar het resultaat is hetzelfde).
De volgende stap is het toevoegen van kleur... Hier heb ik wat meer tijd aan moeten besteden voordat ik tot een bevredigend resultaat kwam.
De eerste poging was om de duotone kleur met de grijswaarde te middelen. Helaas verlies je zo een hoop contrast en het resultaat is wat mat (setDull). Vervolgens heb ik iets anders geprobeerd wat niet in de buurt kwam, maar wel een leuk resultaat geeft (setFun).
De definitieve algoritme is gebasseerd op de volgende kleurverloop: zwart->duotone kleur->wit. Om dat te kunnen doen moet je een grijswaarde vaststellen die overeenkomt met de kleur en vervolgens de rest uitrekenen. In eerste instantie gebruikte ik daarvoor de vaste waarde 127 (setFix) maar in de laatste variant gebruik ik de grijswaarde van de kleur.
Have fun with it, zou ik zeggen...
<?php $img = new image("source.jpg"); $img->setDuotone(255,255,0); $img->saveJPG("output.jpg"); $img->outputJPG(true); class image { var $in; var $out; var $table; var $w,$h; var $converted = false; function image($filename) { $this->in = imagecreatefromjpeg($filename); $this->w = imagesx($this->in); $this->h = imagesy($this->in); $this->clear(); } function clear() { $this->converted=false; if(isset($this->out)) imagedestroy($this->out); $this->out = imagecreatetruecolor($this->w,$this->h); } /** * Set output to greyscale, using grey as the center duotone */ function setGrey() { $this->setDuotone(127,127,127); } /** * Set output to duotone, using a simpler algorith resulting in a duller image */ function setDull($r,$g,$b) { $this->clear(); $this->table = array(); for($i=0;$i<256;$i++) { $this->table[$i] = imagecolorallocate($this->out,($i+$r)/2,($i+$g)/2,($i+$b)/2); } } /** * Set output to duotone, using a fixed center color */ function setFix($r,$g,$b) { $this->setDuotone($r,$g,$b,127); } /** * Set output to duotone */ function setDuotone($r,$g,$b,$center=false,$min=0,$max=255) { $this->clear(); $this->table = array(); if($center === false) $center = ($r+$g+$b)/3; for($i=0;$i<256;$i++) { $this->table[$i] = imagecolorallocate($this->out ,$this->_duotone($i,$r,$center,$min,$max) ,$this->_duotone($i,$g,$center,$min,$max) ,$this->_duotone($i,$b,$center,$min,$max) ); } } /** * Calculates the appropriate duotone color per channel */ function _duotone($black,$color,$center,$min=0,$max=255) { if($black <= $center) { return ($black / $center) * $color; } else { return $max - (($max - $black) / ($max - $center) * ($max - $color)); } } /** * Something I discoverd while trying to come up with a duotone algorithm */ function setFun($r,$g,$b) { $this->clear(); $this->table = array(); for($i=0;$i<256;$i++) { $this->table[$i] = imagecolorallocate($this->out,(((255-$i)/255)*$i)+((($i)/255)*$r),(((255-$i)/255)*$i)+((($i)/255)*$g),(((255-$i)/255)*$i)+((($i)/255)*$b)); } } /** * Convert the image and save it in the object. */ function convert() { var_dump($this); if(!isset($this->table)) $this->setGrey(); for($x=0;$x < $this->w;$x++) { for($y=0;$y < $this->h;$y++) { list($r,$g,$b,$bw) = $this->_colors($x,$y); imagesetpixel($this->out,$x,$y,$this->table[$bw]); } } $this->converted=true; } /** * Helper function to get color information in an array/list */ function _colors($x,$y) { $rgb = ImageColorAt($this->in,$x,$y); $r = ($rgb >> 16) & 0xFF; $g = ($rgb >> 8) & 0xFF; $b = $rgb & 0xFF; $bw = ($r+$g+$b)/3; // simple B/W value return array($r,$g,$b,$bw); } /** * Save the result in a file */ function saveJPG($filename,$qual=80) { if(!$this->converted) $this->convert(); return imagejpeg($this->out,$filename,$qual); } /** * Return the result to the brower */ function outputJPG($headers=false,$qual=80) { if(!$this->converted) $this->convert(); if($headers) header("Content-type: image/jpeg"); return imagejpeg($this->out,'',$qual); } } ?>