= 5.1, which improves the performance considerably.
Unsharp masking is a traditional darkroom technique that has proven very suitable for
digital imaging. The principle of unsharp masking is to create a blurred copy of the image
and compare it to the underlying original. The difference in colour values
between the two images is greatest for the pixels near sharp edges. When this
difference is subtracted from the original image, the edges will be
accentuated.
The Amount parameter simply says how much of the effect you want. 100 is 'normal'.
Radius is the radius of the blurring circle of the mask. 'Threshold' is the least
difference in colour values that is allowed between the original and the mask. In practice
this means that low-contrast areas of the picture are left unrendered whereas edges
are treated normally. This is good for pictures of e.g. skin or blue skies.
Any suggenstions for improvement of the algorithm, expecially regarding the speed
and the roundoff errors in the Gaussian blur process, are welcome.
*/
function UnsharpMask($img, $amount, $radius, $threshold) {
////////////////////////////////////////////////////////////////////////////////////////////////
////
//// Unsharp Mask for PHP - version 2.1.1
////
//// Unsharp mask algorithm by Torstein Hønsi 2003-07.
//// thoensi_at_netcom_dot_no.
//// Please leave this notice.
////
///////////////////////////////////////////////////////////////////////////////////////////////
// $img is an image that is already created within php using
// imgcreatetruecolor. No url! $img must be a truecolor image.
// Attempt to calibrate the parameters to Photoshop:
if ($amount > 500) $amount = 500;
$amount = $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 $img; imagedestroy($img); break; }
$w = imagesx($img); $h = imagesy($img);
$imgCanvas = imagecreatetruecolor($w, $h);
$imgBlur = imagecreatetruecolor($w, $h);
// Gaussian blur matrix:
//
// 1 2 1
// 2 4 2
// 1 2 1
//
//////////////////////////////////////////////////
if (function_exists('imageconvolution')) { // PHP >= 5.1
$matrix = array(
array( 1, 2, 1 ),
array( 2, 4, 2 ),
array( 1, 2, 1 )
);
imagecopy ($imgBlur, $img, 0, 0, 0, 0, $w, $h);
imageconvolution($imgBlur, $matrix, 16, 0);
}
else {
// Move copies of the image around one pixel at the time and merge them with weight
// according to the matrix. The same matrix is simply repeated for higher radii.
for ($i = 0; $i < $radius; $i++) {
imagecopy ($imgBlur, $img, 0, 0, 1, 0, $w - 1, $h); // left
imagecopymerge ($imgBlur, $img, 1, 0, 0, 0, $w, $h, 50); // right
imagecopymerge ($imgBlur, $img, 0, 0, 0, 0, $w, $h, 50); // center
imagecopy ($imgCanvas, $imgBlur, 0, 0, 0, 0, $w, $h);
imagecopymerge ($imgBlur, $imgCanvas, 0, 0, 0, 1, $w, $h - 1, 33.33333 ); // up
imagecopymerge ($imgBlur, $imgCanvas, 0, 1, 0, 0, $w, $h, 25); // down
}
}
if($threshold>0){
// Calculate the difference between the blurred pixels and the original
// and set the pixels
for ($x = 0; $x < $w-1; $x++) { // each row
for ($y = 0; $y < $h; $y++) { // each pixel
$rgbOrig = ImageColorAt($img, $x, $y);
$rOrig = (($rgbOrig >> 16) & 0xFF);
$gOrig = (($rgbOrig >> 8) & 0xFF);
$bOrig = ($rgbOrig & 0xFF);
$rgbBlur = ImageColorAt($imgBlur, $x, $y);
$rBlur = (($rgbBlur >> 16) & 0xFF);
$gBlur = (($rgbBlur >> 8) & 0xFF);
$bBlur = ($rgbBlur & 0xFF);
// When the masked pixels differ less from the original
// than the threshold specifies, they are set to their original value.
$rNew = (abs($rOrig - $rBlur) >= $threshold)
? max(0, min(255, ($amount * ($rOrig - $rBlur)) + $rOrig))
: $rOrig;
$gNew = (abs($gOrig - $gBlur) >= $threshold)
? max(0, min(255, ($amount * ($gOrig - $gBlur)) + $gOrig))
: $gOrig;
$bNew = (abs($bOrig - $bBlur) >= $threshold)
? max(0, min(255, ($amount * ($bOrig - $bBlur)) + $bOrig))
: $bOrig;
if (($rOrig != $rNew) || ($gOrig != $gNew) || ($bOrig != $bNew)) {
$pixCol = ImageColorAllocate($img, $rNew, $gNew, $bNew);
ImageSetPixel($img, $x, $y, $pixCol);
}
}
}
}
else{
for ($x = 0; $x < $w; $x++) { // each row
for ($y = 0; $y < $h; $y++) { // each pixel
$rgbOrig = ImageColorAt($img, $x, $y);
$rOrig = (($rgbOrig >> 16) & 0xFF);
$gOrig = (($rgbOrig >> 8) & 0xFF);
$bOrig = ($rgbOrig & 0xFF);
$rgbBlur = ImageColorAt($imgBlur, $x, $y);
$rBlur = (($rgbBlur >> 16) & 0xFF);
$gBlur = (($rgbBlur >> 8) & 0xFF);
$bBlur = ($rgbBlur & 0xFF);
$rNew = ($amount * ($rOrig - $rBlur)) + $rOrig;
if($rNew>255){$rNew=255;}
elseif($rNew<0){$rNew=0;}
$gNew = ($amount * ($gOrig - $gBlur)) + $gOrig;
if($gNew>255){$gNew=255;}
elseif($gNew<0){$gNew=0;}
$bNew = ($amount * ($bOrig - $bBlur)) + $bOrig;
if($bNew>255){$bNew=255;}
elseif($bNew<0){$bNew=0;}
$rgbNew = ($rNew << 16) + ($gNew <<8) + $bNew;
ImageSetPixel($img, $x, $y, $rgbNew);
}
}
}
imagedestroy($imgCanvas);
imagedestroy($imgBlur);
return $img;
}
function exif_get_float($value) {
$pos = strpos($value, '/');
if ($pos === false) return (float) $value;
$a = (float) substr($value, 0, $pos);
$b = (float) substr($value, $pos+1);
return ($b == 0) ? ($a) : ($a / $b);
}
function exif_get_shutter(&$exif) {
if (!isset($exif['ShutterSpeedValue'])) return false;
$apex = exif_get_float($exif['ShutterSpeedValue']);
$shutter = pow(2, -$apex);
if ($shutter == 0) return false;
if ($shutter >= 1) return round($shutter) . 's';
return '1/' . round(1 / $shutter) . 's';
}
//template for the HTML page
$html_template = "
Pic a Day 2010 - Gabriel Bouvigne
|
/>
|
|
Picture a Day 2010 gallery
";
$html_dual_panel = "
|
style='opacity:0' class='main_pic' onclick='showoverlay()' onmouseover='showexifs()' onmouseout='hideexifs()'/>
|
";
$html_single_panel = "
|
";
define("MODE_PIC","pic");
define("MODE_GALLERY","gallery");
define("MODE_THUMB","thumb");
define("MODE_THUMBS","thumbs");
define("MODE_SOURCE","source");
define("DEFAULT_COLS",3);
define("DEFAULT_ROWS",3);
define("DEFAULT_QUAL",98);
define("DEFAULT_SIZE",700);
class PhotoGallery
{
###############
## Variables ##
###############
var $_rt = './'; // Where the base "root" of our photos are
var $lst = array(); // Will hold our list of files
var $current_lst = array(); // Will hold our list of neighbors files
var $allowed_ext = array(); //list of allowed file extensions
var $jpg_preview_quality = 80;
var $jpg_thumb_quality = 90;
var $thumb_coverage = 95;
var $thumb_size = 96;
var $html;
var $current_pic_name = ''; //current picture;
var $current_pic_width = '';
var $current_pic_height = '';
############################################################# $this->html = str_replace("", $main_pic_hq, $this->html);
######
## user definable Variables, which can be defined within the URL ##
###################################################################
var $current_pic = ''; //current picture;
var $mode = MODE_GALLERY; //MODE_PIC/MODE_GALLERY/MODE_THUMB - script working mode
var $cols = DEFAULT_COLS; // How many thumbnails columns there will be
var $rows = DEFAULT_ROWS; // How many thumbnails rows there will be across
var $size = DEFAULT_SIZE; // main picture size (highest value within width and height)
var $jpg_quality = DEFAULT_QUAL; //main picture quality value, from 1 to 100
###############
## Functions ##
###############
function PhotoGallery()
{
global $html_template;
$this->html = $html_template;
array_push($this->allowed_ext, 'jpg');
array_push($this->allowed_ext, 'jpeg');
//try to retrieve arguments
if (array_key_exists('mode', $_GET)) {
$mode = $_GET['mode'];
if ($mode == MODE_PIC || $mode == MODE_THUMB || $mode == MODE_THUMBS || $mode == MODE_SOURCE) {
$this->mode = $mode;
}
}
if (array_key_exists('cols', $_GET) && is_numeric ($_GET['cols'])) {
$this->cols = abs(intval($_GET['cols']));
}
if (array_key_exists('rows', $_GET) && is_numeric ($_GET['rows'])) {
$this->rows = abs(intval($_GET['rows']));
}
if (array_key_exists('size', $_GET) && is_numeric ($_GET['size'])) {
$this->size = abs(intval($_GET['size']));
}
if (array_key_exists('qual', $_GET) && is_numeric ($_GET['qual'])) {
$this->jpg_quality = abs(intval($_GET['qual']));
$this->jpg_quality = min($this->jpg_quality, 100);
$this->jpg_quality = max($this->jpg_quality, 1);
$this->jpg_preview_quality = min($this->jpg_preview_quality, $this->jpg_quality);
}
$this->current_pic = $_GET['pic']; //try to retrieve current picture from arguments
}
function file_ext($file) {
$fileExp = explode('.', $file); // make array off the periods
return $fileExp[count($fileExp) -1]; // file extension will be last index in array, -1 for 0-based indexes
}
function get_extra_params()
{
$extra_params = "";
if ($this->cols != DEFAULT_COLS)
$extra_params .= "&cols=$this->cols";
if ($this->rows != DEFAULT_ROWS)
$extra_params .= "&rows=$this->rows";
if ($this->size != DEFAULT_SIZE)
$extra_params .= "&size=$this->size";
if ($this->jpg_quality != DEFAULT_QUAL)
$extra_params .= "&qual=$this->jpg_quality";
return $extra_params;
}
function exif_date($file)
{
$exif = exif_read_data($file);
// Date/time
$date="";
if (isset($exif['DateTimeOriginal']))
$date=$exif['DateTimeOriginal'];
if (empty($date) && isset($exif['DateTime']))
$date=$exif['DateTime'];
return $date;
}
function file_date($file)
{
$date = $this->exif_date($file);
if (!empty($date)) {
$date = strtotime($date);
}
return $date;
}
function read_directory()
{
//using the opendir function
$dir_handle = @opendir($this->_rt) or die("Unable to open $this->_rt");
while ($file = readdir($dir_handle))
{
$ext = $this->file_ext($file);
$ext = strtolower($ext);
if(in_array($ext, $this->allowed_ext)) {
$this->lst[$this->file_date($file)] = $file;
}
}
//closing the directory
closedir($dir_handle);
//sort by date
ksort($this->lst);
}
function date_to_string($date)
{
return date("Y/m/d", $date);
}
function create_exifs()
{
$exif = exif_read_data($this->current_pic);
$exifs_str = "";
if (isset($exif['FocalLength']))
$exifs_str .='' . round(exif_get_float($exif['FocalLength'])) . 'mm';
if (isset($exif['ShutterSpeedValue']))
$exifs_str .='' . exif_get_shutter($exif) . '';
if (isset($exif['ISOSpeedRatings']))
$exifs_str .='ISO ' . $exif['ISOSpeedRatings'] . '';
if (isset($exif['Model']))
$exifs_str .='' . $exif['Model'] . '';
$this->html = str_replace("", $exifs_str, $this->html);
}
function create_thumbs_list()
{
$idx = 1;
$thumbs = "";
$extra_params = $this->get_extra_params();
foreach ($this->current_lst as $date => $file) {
$date = $this->date_to_string($date);
$thumbs .= "\n";
if ($this->mode != MODE_THUMBS && $idx%$this->cols == 0) {
$thumbs .= "
\n";
}
$idx++;
}
$this->html = str_replace("", $thumbs, $this->html);
}
function create_navigation()
{
$extra_params = $this->get_extra_params();
reset($this->lst);
$first = $this->lst[key($this->lst)];
end($this->lst);
$last = $this->lst[key($this->lst)];
$rand_val = $this->lst[array_rand($this->lst)];
$navi = "
";
$this->html = str_replace("", $navi, $this->html);
$wh = "
";
$this->html = str_replace("", $wh, $this->html);
}
function create_page()
{
global $html_dual_panel;
$this->html = str_replace("", $html_dual_panel, $this->html);
$main_pic = " src='?pic=$this->current_pic&mode=pic&size=$this->size&qual=$this->jpg_preview_quality' width='$this->current_pic_width' height='$this->current_pic_height' alt='$this->current_pic_name' ";
$this->html = str_replace("", $main_pic, $this->html);
$main_pic_hq = "'?pic=$this->current_pic&mode=pic&size=$this->size&qual=$this->jpg_quality'";
$this->html = str_replace("", $main_pic_hq, $this->html);
$main_pic_overlay = "'?pic=$this->current_pic&mode=pic&size=$this->size&qual=$this->jpg_preview_quality'";
$this->html = str_replace("", $main_pic_overlay, $this->html);
$this->html = str_replace("", $this->current_pic_name, $this->html);
$this->create_thumbs_list();
$this->create_exifs();
$this->create_navigation();
echo $this->html;
}
function create_thumbs_page()
{
global $html_single_panel;
$this->html = str_replace("", $html_single_panel, $this->html);
$this->create_thumbs_list();
echo $this->html;
}
function sharpen($img, $resampling_ratio = 1, $fast=false)
{
if ($fast)
{
if (function_exists('imageconvolution') && $resampling_ratio != 1) {
$sharpness = 6;
$sharpenMatrix = array(
array(-1, -1, -1),
array(-1, $sharpness + 9, -1),
array(-1, -1, -1)
);
$divisor = 1+$sharpness;
$offset = 0;
imageconvolution($img, $sharpenMatrix, $divisor, $offset);
}
} else {
if (function_exists('imageconvolution')) {
UnsharpMask($img, 90, 0.5, 3);
}
}
}
function create_pic()
{
$image = imageCreateFromJPEG($this->current_pic);
$width_orig = imageSX($image);
$height_orig = imageSY($image);
$max_length = max($width_orig, $height_orig);
$ratio = $this->size/$max_length;
$width_dest = $width_orig*$ratio;
$height_dest = $height_orig*$ratio;
$img_new = imagecreatetruecolor($width_dest, $height_dest);
imagecopyresampled($img_new, $image,
0, 0,
0, 0,
$width_dest, $height_dest,
$width_orig, $height_orig);
ImageDestroy($image);
//retrieve resulting output dimensions, as they might be +-1 pixel off from target
$width_dest = imageSX($img_new);
$height_dest = imageSY($img_new);
$new_size = max($width_dest, $height_dest);
$ratio = $new_size/$max_length;
$fast = false;
if ($this->jpg_quality == $this->jpg_preview_quality) {
$fast = true;
}
$this->sharpen($img_new, $ratio, $fast);
//Tell the browser what kind of file is come in
header("Content-Type: image/jpeg");
//Output the newly created image in jpeg format
ImageInterlace($img_new, false);
ImageJpeg($img_new, '', $this->jpg_quality);
ImageDestroy($img_new);
}
function create_thumb()
{
$image = imageCreateFromJPEG($this->current_pic);
$width_orig = imageSX($image);
$height_orig = imageSY($image);
$min_length = min($width_orig, $height_orig);
$extracted_size = $min_length*$this->thumb_coverage/100;
$img_new = imagecreatetruecolor($this->thumb_size, $this->thumb_size);
fastimagecopyresampled($img_new, $image,
0, 0,
($width_orig-$extracted_size)/2, ($height_orig-$extracted_size)/2,
$this->thumb_size, $this->thumb_size,
$extracted_size, $extracted_size);
ImageDestroy($image);
//Tell the browser what kind of file is come in
header("Content-Type: image/jpeg");
//Output the newly created image in jpeg format
ImageInterlace($img_new, false);
ImageJpeg($img_new, '', $this->jpg_thumb_quality);
ImageDestroy($img_new);
}
function current_pic_data()
{
//check if current_pic is correct
if (!in_array($this->current_pic, $this->lst)) {
$this->current_pic = '';
}
if (empty($this->current_pic)) {
$this->current_pic = end($this->lst); //default: last picture
}
$this->current_pic_name = $this->date_to_string($this->file_date($this->current_pic));
list($width_orig, $height_orig) = getimagesize($this->current_pic);
$max_length = max($width_orig, $height_orig);
$this->current_pic_width = round($width_orig*$this->size/$max_length);
$this->current_pic_height = round($height_orig*$this->size/$max_length);
$idx = count($this->lst);
end($this->lst);
While ($idx > 0) {
$idx--;
if (current($this->lst) == $this->current_pic)
break;
prev($this->lst);
}
$current_index = $idx;
$num_pics = $this->rows * $this->cols;
$first_index = floor($num_pics/2);
$first_index = $current_index - $first_index;
$first_index = max(0, $first_index);
$this->current_lst = array_slice($this->lst, $first_index, $num_pics, true);
}
function thumbs_data()
{
$this->current_lst = $this->lst;
}
function get_src()
{
header('Content-Type: text/plain');
$currentFile = $_SERVER["SCRIPT_NAME"];
$parts = Explode('/', $currentFile);
$currentFile = $parts[count($parts) - 1];
$source = file_get_contents($currentFile);
echo $source;
}
}
$pg = new PhotoGallery();
if ($pg->mode == MODE_GALLERY) {
$pg->read_directory();
$pg->current_pic_data();
$pg->create_page();
} else if ($pg->mode == MODE_PIC) {
$pg->create_pic();
} else if ($pg->mode == MODE_THUMB) {
$pg->create_thumb();
} else if ($pg->mode == MODE_THUMBS) {
$pg->read_directory();
$pg->thumbs_data();
$pg->create_thumbs_page();
} else if ($pg->mode == MODE_SOURCE) {
$pg->get_src();
}
?>