Related
I have the following PHP script that takes an existing jpeg image and resizes it to a smaller thumbnail, while adding a png watermark to it.
The problem I have is that the reduction is done with GD library which makes the output thumb not so sharp after reducing it (it's much sharper when no size reduction is required, ie. if the original and output size are the same).
I was told that resizing algorithm in gd isn't very good and was advised to use ImageMagick instead, with the adaptive-resize option. I basically want to convert the script to use ImageMagick (with bicubic sharper) instead of the GD library:
<?php
if (isset($_GET['image']) && isset($_GET['width'])) {
$image = $_GET['image'];
$max_width = $_GET['width'];
$max_height = 800;
$wmark='watermark.png';
$wmarks='watermark_s.png';
$wmarkm='watermark_m.png';
$wmarkno='nowatermark.png';
$noimg='noimg.png';
if (file_exists($image)) {
$cached='cache/'.preg_replace('/(\.\w+$)/',".{$max_width}\\1",$image);
if (file_exists($cached)) {
$cst=stat($cached);
$fst=stat($image);
if ($fst[9]<=$cst[9] && $fst[10]<=$cst[10]) {
if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE'])>=$cst[9]) {
header("HTTP/1.0 304 Not Modified");
} else {
header('Content-type: image/jpeg');
header('Last-Modified: '.gmdate('D, d M Y H:i:s',$cst[9]).' GMT');
header('Cache-Control: private');
print file_get_contents($cached);
}
exit;
}
}
$size = GetImageSize($image);
$watermark_img = imagecreatefrompng($wmark);
$watermarks_img = imagecreatefrompng($wmarks);
$watermarkm_img = imagecreatefrompng($wmarkm);
$watermarkno_img = imagecreatefrompng($wmarkno);
$wmrk_size = getimagesize($wmark);
$wmrks_size = getimagesize($wmarks);
$wmrkm_size = getimagesize($wmarkm);
$wmrkno_size = getimagesize($wmarkno);
$width = $size[0];
$height = $size[1];
#$x_ratio = $max_width / $width;
#$y_ratio = $max_height / $height;
if (($width <= $max_width) && ($height <= $max_height))
{
$tn_height = $height;
$tn_width = $width;
}
else if (($x_ratio * $height) < $max_height)
{
$tn_height = ceil($x_ratio * $height);
$tn_width = $max_width;
}
else
{
$tn_height = $max_height;
$tn_width = ceil($y_ratio * $width);
}
if ((($tn_width) <>0) && (($tn_height)<>0)) {
$src = ImageCreateFromJPEG($image);
$dst = ImageCreateTrueColor($tn_width, $tn_height);
ImageCopyResampled($dst, $src, 0, 0, 0, 0, $tn_width, $tn_height, $width, $height);
//$dst = imagecreatefromjpeg($dst);
if (ImageSX($dst) > 300) {
$posx = (ImageSX($dst) - ImageSX($watermark_img))/2;
$posy = (ImageSY($dst) - ImageSY($watermark_img))/2;
imagecopy($dst, $watermark_img, $posx, $posy, 0, 0, $wmrk_size[0], $wmrk_size[1]);
} else {
$posxs = (ImageSX($dst) - ImageSX($watermarkno_img))/2;
$posys = (ImageSY($dst) - ImageSY($watermarkno_img))/2;
imagecopy($dst, $watermarkno_img, $posxs, $posys, 0, 0, $wmrkno_size[0], $wmrkno_size[1]);
}
header('Content-type: image/jpeg');
header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
header('Cache-Control: private');
ImageJPEG($dst, null, 90);
ImageJPEG($dst, $cached, 90);
ImageDestroy($src);
ImageDestroy($dst);
}
}
}
?>
I have modified it myself using ImageMagick. Much sharper and nicer output than GD.
Below is the modified version. Hope this may help others.
<?php
if (isset($_GET['image']) && isset($_GET['width']) && is_numeric($_GET['width']) ) {
// Get image name
$original_image = $_GET['image'];
// Watermarks
$wmark='watermark.png'; //largest watermark
$wmarkm='watermark_m.png'; //medium watermark
$wmarks='watermark_s.png'; //smallest watermark
$wmarkno='nowatermark.png'; //No watermark
// Maximum image width
$max_width = (int)$_GET['width'];
// Maximum image height
$max_height = "800";
if (file_exists($original_image)) {
$cached='cache/'.preg_replace('/(\.\w+$)/',".{$max_width}\\1",$original_image);
if (file_exists($cached)) {
$cst=stat($cached);
$fst=stat($original_image);
if ($fst[9]<=$cst[9] && $fst[10]<=$cst[10]) {
if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE'])>=$cst[9]) {
header('Last-Modified: '.gmdate('D, d M Y H:i:s', filemtime($cached)).' GMT', true, 304);
} else {
header('Content-type: image/jpeg');
header('Last-Modified: '.gmdate('D, d M Y H:i:s',$cst[9]).' GMT');
header('Cache-Control: private');
readfile($cached);
}
exit;
}
}
if ($max_width > 300) {
$watermark=$wmark;
} elseif ($max_width > 152 && $max_width < 300) {
$watermark=$wmarkm;
}elseif ($max_width > 50 && $max_width < 151){
$watermark=$wmarks;
} else {
$watermark=$wmarkno;
}
// Resize the image, save and output to browser with headers
exec("convert -filter Lanczos $original_image -thumbnail {$max_width}x{$max_height} -quality 90 {$watermark} -gravity center -unsharp 2x0.5+0.2+0 -composite {$cached}");
header('Content-type: image/jpeg');
header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
header('Cache-Control: private');
readfile($cached);
}
}
?>
EDIT
Seems that ImageMagick was using excessive resources on my server, as it was doing bulk resizing. I eventually decided to switch to GraphicsMagick instead, which outputs the same image quality I need, with almost the same file size, while working much faster and using far less resources on my server.
To do that, I just installed GraphicsMagick and changed the exec line from:
exec("convert -filter Lanczos $original_image -thumbnail {$max_width}x{$max_height} -quality 90 {$watermark} -gravity center -unsharp 2x0.5+0.2+0 -composite {$cached}");
To:
//create the resized image
exec("gm convert -filter Lanczos {$original_image} -thumbnail {$max_width}x{$max_height} -quality 90 -unsharp 2x0.5+0.2+0 {$cached}");
//apply the watermark and recreate the watermarked image, overwriting the previously resized image
exec("gm composite -quality 90 -dissolve 100 -gravity center {$watermark} {$cached} {$cached}");
EDIT 2
Another way, for anyone who wants or needs to keep working with GD, is to use the following excellent unsharp mask function (taken from http://vikjavev.no/computing/ump.php):
<?php
/*
New:
- In version 2.1 (February 26 2007) Tom Bishop has done some important speed enhancements.
- From version 2 (July 17 2006) the script uses the imageconvolution function in PHP
version >= 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;
}
?>
PHP 5.5 and above has an undocumented function imagesetinterpolation() that let you change the interpolation method. I only saw it in the source code. Never tried it myself. Your mileage might vary.
The function takes two parameters: the image resource and one of the following constants.
IMG_BELL
IMG_BESSEL
IMG_BILINEAR_FIXED
IMG_BICUBIC
IMG_BICUBIC_FIXED
IMG_BLACKMAN
IMG_BOX
IMG_BSPLINE
IMG_CATMULLROM
IMG_GAUSSIAN
IMG_GENERALIZED_CUBIC
IMG_HERMITE
IMG_HAMMING
IMG_HANNING
IMG_MITCHELL
IMG_POWER
IMG_QUADRATIC
IMG_SINC
IMG_NEAREST_NEIGHBOUR
IMG_WEIGHTED4
IMG_TRIANGLE
IMG_BILINEAR_FIXED is the default.
I need to make this effect with php. I know that there is IMG_FILTER_PIXELATE in PHP image filter. But I need it to be smoother and embossed? like in this image:
This effect will make any image uploaded by user become pixelated and the edge of the picture become red (I know IMG_FILTER_EDGEDETECT but I don't know how to use it to change edge color).
I have no idea how to do this.
As the last answer was theoretical and seemed to be not enough, I've created a practical example:
Note: This is far from the "ideal" and perfect pixelate effect function, but it does it's job. Feel free to edit it according to your own needs.
<?php
/* Function to make pixelated images
* Supported input: .png .jpg .jpeg .gif
*
*
* Created on 24.01.2011 by Henrik Peinar
*/
/*
* image - the location of the image to pixelate
* pixelate_x - the size of "pixelate" effect on X axis (default 10)
* pixelate_y - the size of "pixelate" effect on Y axis (default 10)
* output - the name of the output file (extension will be added)
*/
function pixelate($image, $output, $pixelate_x = 20, $pixelate_y = 20)
{
// check if the input file exists
if(!file_exists($image))
echo 'File "'. $image .'" not found';
// get the input file extension and create a GD resource from it
$ext = pathinfo($image, PATHINFO_EXTENSION);
if($ext == "jpg" || $ext == "jpeg")
$img = imagecreatefromjpeg($image);
elseif($ext == "png")
$img = imagecreatefrompng($image);
elseif($ext == "gif")
$img = imagecreatefromgif($image);
else
echo 'Unsupported file extension';
// now we have the image loaded up and ready for the effect to be applied
// get the image size
$size = getimagesize($image);
$height = $size[1];
$width = $size[0];
// start from the top-left pixel and keep looping until we have the desired effect
for($y = 0;$y < $height;$y += $pixelate_y+1)
{
for($x = 0;$x < $width;$x += $pixelate_x+1)
{
// get the color for current pixel
$rgb = imagecolorsforindex($img, imagecolorat($img, $x, $y));
// get the closest color from palette
$color = imagecolorclosest($img, $rgb['red'], $rgb['green'], $rgb['blue']);
imagefilledrectangle($img, $x, $y, $x+$pixelate_x, $y+$pixelate_y, $color);
}
}
// save the image
$output_name = $output .'_'. time() .'.jpg';
imagejpeg($img, $output_name);
imagedestroy($img);
}
pixelate("test.jpg", "testing");
?>
This is the example function to create pixelated effect on images.
Here's an example results of using this function:
Original:
Pixelated 5px:
Pixelated 10px:
Pixelated 20px:
Thank you for your answer. I used your function and added another loop to change color of the outer pixel of the squares using a function called imagelinethick in http://www.php.net/manual/en/function.imageline.php. So it became:
<?php
$image = imagecreatefromjpeg('Penguins.jpg');
$imagex = imagesx($image);
$imagey = imagesy($image);
$pixelate_y=10;
$pixelate_x=10;
$height=$imagey;
$width=$imagex;
for($y = 0;$y < $height;$y += $pixelate_y+1)
{
for($x = 0;$x < $width;$x += $pixelate_x+1)
{
// get the color for current pixel
$rgb = imagecolorsforindex($image, imagecolorat($image, $x, $y));
// get the closest color from palette
$color = imagecolorclosest($image, $rgb['red'], $rgb['green'], $rgb['blue']);
imagefilledrectangle($image, $x, $y, $x+$pixelate_x, $y+$pixelate_y, $color);
}
}
for($y = 0;$y < $height;$y += $pixelate_y+1)
{
for($x = 0;$x < $width;$x += $pixelate_x+1)
{
//make a border line for each square
$rgb = imagecolorsforindex($image, imagecolorat($image, $x, $y));
$color = imagecolorclosest($image, 123, 123, 123);
imagelinethick($image, $x, $y, $x, $y+$pixelate_y, $color, 1);
imagelinethick($image, $x, $y, $x+$pixelate_x, $y, $color, 2);
}
}
function imagelinethick($image, $x1, $y1, $x2, $y2, $color, $thick = 1)
{
/* this way it works well only for orthogonal lines
imagesetthickness($image, $thick);
return imageline($image, $x1, $y1, $x2, $y2, $color);
*/
if ($thick == 1) {
return imageline($image, $x1, $y1, $x2, $y2, $color);
}
$t = $thick / 2 - 0.5;
if ($x1 == $x2 || $y1 == $y2) {
return imagefilledrectangle($image, round(min($x1, $x2) - $t), round(min($y1, $y2) - $t), round(max($x1, $x2) + $t), round(max($y1, $y2) + $t), $color);
}
$k = ($y2 - $y1) / ($x2 - $x1); //y = kx + q
$a = $t / sqrt(1 + pow($k, 2));
$points = array(
round($x1 - (1+$k)*$a), round($y1 + (1-$k)*$a),
round($x1 - (1-$k)*$a), round($y1 - (1+$k)*$a),
round($x2 + (1+$k)*$a), round($y2 - (1-$k)*$a),
round($x2 + (1-$k)*$a), round($y2 + (1+$k)*$a),
);
imagefilledpolygon($image, $points, 4, $color);
return imagepolygon($image, $points, 4, $color);
}
header("Content-Type: image/JPEG");
imageJPEG($image, "", 75);
?>
The result is like this: http://www.flickr.com/photos/52700219#N06/6759029339/
But I think this still need some improvement to make it smoother.
Here goes theoretically:
You have a image:
RGBRGBRGBRGB
GBRGBRGBRGBR
GBRGBRGBRRGB
BGRGBGRGGRBG
Take the color of the first pixel and set the same color for a square of next pixels (both down and right). Then take the color of a 5th pixel (as 4 ones in the start have already the same color). If you are done for the first row, go +3 rows down and start again.
So you get:
RRRRGGGBBBB
RRRRGGGBBBB
RRRRGGGBBBB
RRRRGGGBBBB
In PHP you can use the following functions to make this:
http://php.net/manual/en/function.imagecolorat.php to select the color of a pixel
http://php.net/manual/en/function.imagecolorset.php to set the color of a pixel
http://php.net/manual/en/function.imagesx.php get image width
http://php.net/manual/en/function.imagesy.php get image height
use for loops thru the pixels of a image
This is my attempt at the problem.
You can alter the pixelate block size and you can apply a blur which softens the effect on high contrast images. Can be a slow on large images with small pixelate block sizes though.
The scripts stores the colours of relevant pixels in an array. It then embosses the image, alters the contrast as required, pixelates the image using the imagefilter() function and then (if tile enhance is set) embosses it again (this increases the 3D effect on the final tiles). If blur is required the script the applies the Gaussian blur. The script then draws filled squares using the colour array to create the colourful pixelated effect within the embossed tile borders.
function pixelatemboss($image,$blockwidth=10,$blur=5,$tileenhance="true",$contrast=0,$negate="true")
{
if($blockwidth>1)
{
imagefilter($image,IMG_FILTER_CONTRAST,$contrast);
for($x=1;$x<imagesx($image);$x=$x+$blockwidth)
{
for($y=1;$y<imagesy($image);$y=$y+$blockwidth)
{
$color[$x][$y]=imagecolorat($image,$x,$y);
}
}
imagefilter($image,IMG_FILTER_EMBOSS);
imagefilter($image,IMG_FILTER_CONTRAST,$contrast);
imagefilter($image,IMG_FILTER_PIXELATE,$blockwidth,false);
if($tileenhance=="true")
{
imagefilter($image,IMG_FILTER_EMBOSS);
}
for($b=0;$b<$blur;$b++)
{
imagefilter($image,IMG_FILTER_GAUSSIAN_BLUR);
}
for($x=1;$x<imagesx($image);$x=$x+$blockwidth)
{
for($y=1;$y<imagesy($image);$y=$y+$blockwidth)
{
$rgb=$color[$x][$y];
$r = ($rgb >> 16) & 0xFF;
$g = ($rgb >> 8) & 0xFF;
$b = $rgb & 0xFF;
$col=imagecolorallocate($image,$r,$g,$b);
imagefilledrectangle($image,$x,$y,$x+($blockwidth-2),$y+($blockwidth-2),$col);
}
}
}
return $image;
}
Note for php 5.4 and up you need to use:
imageJPEG($image, NULL, 75);
You can no longer specify NULL by using a double quote (like this example):
imageJPEG($image, "", 75);
I have an image, and I want to make it transparent if it's completely white. I have the following code already for GD for getting a part of the image:
$srcp = imagecreatefrompng("../images/".$_GET['b'].".png");
$destp = imagecreate(150, 150);
imagecopyresampled($destp, $srcp, 0, 0, 40, 8, 150, 150, 8, 8);
header('Content-type: image/png');
imagepng($destp);
But how can I have it first check if the image is completely white, if so change it to transparent, and apply that to $destp?
EDIT:
Based on re-reading the question, and the discussion below, I believe this is what you're looking for:
$width = 150;
$height = 150;
$srcp = imagecreatefrompng("../images/".$_GET['b'].".png");
$destp = imagecreatetruecolor(150, 150);
$white = 0;
for ($y = 0; $y < $height; $y++)
{
for ($x = 0; $x < $width; $x++)
{
$currentColor = imagecolorat($srcp, $x, $y);
$colorParts = imagecolorsforindex($srcp, $currentColor);
if (($colorParts['red'] == 255 &&
$colorParts['green'] == 255 &&
$colorParts['blue'] == 255))
{
$white++;
}
}
}
if ($white == ($width * $height))
{
imagecopyresampled($destp, $srcp, 0, 0, 40, 8, 150, 150, 8, 8);
}
else
{
imagesavealpha($destp, true);
imagefill($destp, 0, 0, imagecolorallocatealpha($destp, 0, 0, 0, 127));
}
header('Content-type: image/png');
imagepng($destp);
This produces a blank image if the original image's 8x8 slice is all white, otherwise it resamples the 8x8 slice to 150x150.
Original:
I haven't ever done anything with PHP GD before, and thought it would be a fun challenge. Here's how I ended up making this happen:
$filename = 'test.png'; // input image
$image = imagecreatefrompng($filename);
// grab the input image's size
$size = getimagesize($filename);
$width = $size[0];
$height = $size[1];
$newImage = imagecreatetruecolor($width, $height);
// make sure that the image will retain alpha when saved
imagesavealpha($newImage, true);
// fill with transparent pixels first or else you'll
// get black instead of transparent
imagefill($newImage, 0, 0, imagecolorallocatealpha($newImage, 0, 0, 0, 127));
// loop through all the pixels
for ($y = 0; $y < $height; $y++)
{
for ($x = 0; $x < $width; $x++)
{
// get the current pixel's colour
$currentColor = imagecolorat($image, $x, $y);
// then break it into easily parsed bits
$colorParts = imagecolorsforindex($image, $currentColor);
// if it's NOT white
if (!($colorParts['red'] == 255 &&
$colorParts['green'] == 255 &&
$colorParts['blue'] == 255))
{
// then keep the same colour
imagesetpixel($newImage, $x, $y, $currentColor);
}
}
}
// final output, the second arg is the filename
imagepng($newImage, 'newImage.png');
It allowed me to turn this:
Into this (hard to see the alpha here, but you can open it to see):
A simple strightforward solution would be to use imagecolorat and iterate through all the pixels of the png and if all are white change it to transparent.
Hope this helps.
After a quick look through the GD manual pages, I think that the following should work for you (it did on my test server, anyway):
<?php
// Create image instance
$im = imagecreatefromgif('test.gif');
if (imagecolorstotal($im) == 1) {
$rgb = imagecolorat($im, 1, 1); // feel free to test any pixel, but if they're all
// the same colour, then it shouldn't really matter
$colors = imagecolorsforindex($im, $rgb);
if ($colors['red'] == 255 && $colors['green'] == 255 && $colors['blue'] == 255) {
// I'm ignoring $colors['alpha'] but that's optional.
echo "Yup; it's white...";
}
} else {
// if you've got more than one colour then the image has to be at least
// two colours, so not really worth checking if any pixels are white
}
// Free image
imagedestroy($im);
?>
References:
imagecolorat().
imagecolorstotal().
This function cropit, which I shamelessly stole off the internet, crops a 90x60 area from an existing image.
In this code, when I use the function for more than one item (image) the one will display on top of the other (they come to occupy the same output space).
I think this is because the function has the same (static) name ($dest) for the destination of the image when it's created (imagecopy).
I tried, as you can see to include a second argument to the cropit function which would serve as the "name" of the $dest variable, but it didn't work.
In the interest of full disclosure I have 22 hours of PHP experience (incidentally the same number of hours since the last I slept) and I am not that smart to begin with.
Even if there's something else at work here entirely, seems to me that generally it must be useful to have a way to secure that a variable is always given a unique name.
<?php
function cropit($srcimg, $dest) {
$im = imagecreatefromjpeg($srcimg);
$img_width = imagesx($im);
$img_height = imagesy($im);
$width = 90;
$height = 60;
$tlx = floor($img_width / 2) - floor ($width / 2);
$tly = floor($img_height / 2) - floor ($height / 2);
if ($tlx < 0)
{
$tlx = 0;
}
if ($tly < 0)
{
$tly = 0;
}
if (($img_width - $tlx) < $width)
{
$width = $img_width - $tlx;
}
if (($img_height - $tly) < $height)
{
$height = $img_height - $tly;
}
$dest = imagecreatetruecolor ($width, $height);
imagecopy($dest, $im, 0, 0, $tlx, $tly, $width, $height);
imagejpeg($dest);
imagedestroy($dest);
}
$img = "imagefolder\imageone.jpg";
$img2 = "imagefolder\imagetwo.jpg";
cropit($img, $i1);
cropit($img2, $i2);
?>
In this code, when I use the function for more than one item (image) the one will display on top of the other (they come to occupy the same output space).
You are creating the raw image data: you cannot serve multiple images at once in an HTTP request (you could save an unlimited amount to file ofcourse, imagejpg can take more parameters), no decent browser would know what to make of it.
If you want to overlay one image on another, look at imagecopyresampled()
I think this is because the function has the same (static) name ($dest) for the destination of the image when it's created (imagecopy).
This is not the case, as soon as your function exits $dest doesn't exist anymore (it only existed within the function scope. See http://php.net/manual/en/language.variables.scope.php
I hope I understood you. You want to save the cropped image to a filename you have in the variables $i1 and $i2?
Then the last part is probably wrong. It should be like this:
<?php
function cropit($srcimg, $filename) {
$im = imagecreatefromjpeg($srcimg);
$img_width = imagesx($im);
$img_height = imagesy($im);
$width = 90;
$height = 60;
$tlx = floor($img_width / 2) - floor ($width / 2);
$tly = floor($img_height / 2) - floor ($height / 2);
if ($tlx < 0)
{
$tlx = 0;
}
if ($tly < 0)
{
$tly = 0;
}
if (($img_width - $tlx) < $width)
{
$width = $img_width - $tlx;
}
if (($img_height - $tly) < $height)
{
$height = $img_height - $tly;
}
$dest = imagecreatetruecolor ($width, $height);
imagecopy($dest, $im, 0, 0, $tlx, $tly, $width, $height);
imagejpeg($dest, $filename); // Second parameter
imagedestroy($dest);
}
imagejpeg has a second parameter which takes the filename it should be saved as.
I want to skew an image into a trapezoidal shape. The left and right edges need to be straight up and down; the top and left edges need to be angular. I have no idea what the best way to do this is.
I'm using GD Library and PHP. Can anyone point me in the right direction?
Thanks,
Jason
Try this:
<?
// Set it up
$img_name = "grid.jpg";
$src_img = imagecreatefromjpeg($img_name);
$magnify = 4;
// Magnify the size
$w = imagesx($src_img);
$h = imagesy($src_img);
$dst_img = imagecreatetruecolor($w * $magnify, $h * $magnify);
imagecopyresampled($dst_img, $src_img, 0, 0, 0, 0, $w * $magnify, $h * $magnify, $w, $h);
$src_img = $dst_img;
// Skew it
$w *= $magnify;
$h *= $magnify;
$new_lh = abs($h * 0.66);
$new_rh = $h ;
$step = abs((($new_rh - $new_lh) / 2) / $w);
$from_top = ($new_rh - $new_lh) / 2 ;
$dst_img = imagecreatetruecolor($w, $new_rh);
$bg_colour = imagecolorallocate($dst_img, 255, 255, 255);
imagefill($dst_img, 0, 0, $bg_colour);
for ($i = 0 ; $i < $w ; $i ++)
{
imagecopyresampled($dst_img, $src_img, $i, $from_top - $step * $i, $i, 0, 1, $new_lh + $step * $i * 2, 1, $h);
}
// Reduce the size to "anti-alias" it
$src_img = $dst_img;
$dst_img = imagecreatetruecolor($w / $magnify * 0.85, $new_rh / $magnify);
imagecopyresampled ($dst_img, $src_img, 0, 0, 0, 0, $w / $magnify * 0.85, $h / $magnify, $w, $h);
header("Content-Type: image/jpg");
imagejpeg($dst_img);
?>
I found this thread (translated Dutch -> English) discussing the same thing. Looks like it might be what you're after. I think it's clear that you can't skew with GD without writing your own function to do so. If you have ImageMagick available, you might find this to be easier to achieve.
Good luck.
Make a nested loop for x = 0 to width and inside y = 0 to height, get pixel (rgba) at that coordinate, calculate new xy for skew rotation (using sine cosine, you have to have skew origin for angle calculation) and then copy pixels to empty image. Just high school math