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);
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 have this function that transforms image to trapezoid using PHP GD:
function perspective($i,$gradient=0.9,$rightdown=true,$background=0xFFFFFF) {
$mult=3;
$w=imagesx($i);
$h=imagesy($i);
$image=imagecreatetruecolor($w*$mult,$h*$mult);
imagecopyresized($image,$i,0,0,0,0,$w*$mult,$h*$mult,$w,$h);
imagedestroy($i);
$w*=$mult;
$h*=$mult;
$im=imagecreatetruecolor($w,$h);
$background=imagecolorallocate($im,($background>>16)&0xFF,($background>>8)&0xFF,$background&0xFF);
imagefill($im,0,0,$background);
imageantialias($im,true);
$nh=$h-($h*$gradient);
for ($x=0; $x<$w; $x++) {
$ni=(($rightdown) ? $x : $w-$x);
$p=intval($h-(($ni/$w)*$nh));
if (($p%2)<>0)
$p-=1;
$nx=intval(($p-$h)/2);
imagecopyresampled($im,$image,$x,0,$x,$nx,1,$p,1,$h-1);
imageline($im,$x,$h-1,$x,$h+$nx,$background);
imageline($im,$x,0,$x,-$nx-1,$background);
}
imagedestroy($image);
imagefilter($im,IMG_FILTER_SMOOTH,10);
$i=imagecreatetruecolor($w/$mult,$h/$mult);
imageantialias($i,true);
imagecopyresampled($i,$im,0,0,0,0,$w,$h,$w*$mult,$h*$mult);
imagedestroy($im);
return $i;
}
But i cant modify it to produce isosceles trapezoid, i think there needs just one small modification, but i cant figure it outh (i tried lot of things).
Can someone help?
Right, basically that code should generate the right values, but due to a bug has a lot of cludges in place to get a trapezium shape. The bug is that the copy of each line has the destination-y and the source-y values transposed. The source-y should always be 0, the destination-y should change.
There were also a few other small numerical bugs and double rounding at unnecessary points throwing off the results.
Also, the variable naming was atrocious, so I have rewritten it so that the entire function is clear.
Try the following:
function makeTrapeziumImage($image, $gradient, $rightToLeft = false, $background = 0xFFFFFF, $supersampleScale = 3) {
$originalWidth = imagesx($image);
$originalHeight = imagesy($image);
$supersampledWidth = $originalWidth * $supersampleScale;
$supersampledHeight = $originalHeight * $supersampleScale;
$supersampledImage = imagecreatetruecolor($supersampledWidth, $supersampledHeight);
imagecopyresized($supersampledImage, $image,
0, 0, 0, 0,
$supersampledWidth, $supersampledHeight, $originalWidth, $originalHeight);
$workingImage = imagecreatetruecolor($supersampledWidth, $supersampledHeight);
$backgroundColour = imagecolorallocate($workingImage, ($background >> 16) & 0xFF, ($background >> 8) & 0xFF, $background & 0xFF);
imagefill($workingImage, 0, 0, $backgroundColour);
imageantialias($workingImage,true);
$endHeight = $supersampledHeight - ($supersampledHeight * $gradient);
for ($x = 0; $x < $supersampledWidth; $x++) {
$cX = ($rightToLeft ? $supersampledWidth - $x : $x);
$dstHeight = $supersampledHeight - ((($cX + 1) / $supersampledWidth) * $endHeight);
$dstY = intval(($supersampledHeight - $dstHeight) / 2) - 1; // -1 required as zero-indexed
$dstY = ($dstY < 0 ? 0 : $dstY); // Rounding can make $dstY = -1
$dstHeight = intval($dstHeight); // Round the height after calculating $dstY
imagecopyresampled($workingImage, $supersampledImage,
$cX, $dstY, $cX, 0,
1, $dstHeight, 1, $supersampledHeight);
}
imagedestroy($supersampledImage);
imagefilter($workingImage, IMG_FILTER_SMOOTH, 10);
$resizedImage = imagecreatetruecolor($originalWidth, $originalHeight);
imageantialias($resizedImage, true);
imagecopyresampled($resizedImage, $workingImage,
0, 0, 0, 0,
$originalWidth, $originalHeight, $supersampledWidth, $supersampledHeight);
imagedestroy($workingImage);
return $resizedImage;
}
The essential mechanism of the inner loop, as before, is to take each column of pixels, along the x-axis and resize them over a gradient. It naturally creates an isosceles trapezium. In order to create a non-isosceles trapezoid, another gradient would have to be specified. Alternatively, a set of start and end y-values could be specified and the gradients calculated from them.
Whilst this example works along the x-axis, in either direction as before, it could just as easily work along the y-axis (or the image could be rotated 90 degrees, processed, then rotated back).
I am trying to center an image to the middle of an
$thumbnail_gd_image = imagecreatetruecolor(600, 300);
command. If a user uploads an image that is 100x50 I generate a larger version to 600x300 with a white background. Now at the moment the actual image is placed in the top left corner. I would like this image in the middle of the 600x300 generated image.
On the other side, if a user uploads an image that is larger than 600x300 then I resize down to keep within those parameters.
I am building an image upload/cropping tool however the croppable area is always twice as wide as what it is high so I need the useable cropping area to be within 600x300 for any detailed cropping.
Can this be done? The center of a smaller than 600x300 image?
Thanks
EDIT:
Tried the below code but it didnt like it.
This is my code (Probably not the cleanest but it needs to be semi working quickly).
if($source_image_width < 600 && $source_image_height < 300){
$x = (600 / 2) - ($source_image_width / 2);
$y = (300 / 2) - ($source_image_height / 2);
}else if($source_image_width > 600){
$x = 0;
if($source_image_height < 300){
$y = (300 / 2) - ($source_image_height / 2);
}else{
$y = 0;
}
}else if($source_image_height > 300){
if($source_image_width < 600){
$x = (600 / 2) - ($source_image_width / 2);
}else{
$x = 0;
}
$y = 0;
}else{
$x = 0;
$y = 0;
}
The above code puts a 400 wide x 800 high image slightly to the left of center (about 100px). The hight on any image works perfectly but just not the width.
you could try:
$W = 600;
$H = 300;
$im = imagecreatefromjpeg($filename);
list($w,$h) = getimagesize($filename);
$x = ($W / 2) - ($w/2);
$y = ($H / 2) - ($h/2);
$newIm = imagecreatetruecolor($new_w, $new_h);
// i know I could have used a better function for this, but...
imagecopyresampled($newIm, $im, $x, $y, 0, 0, $w, $h, $w, $h);
header("Content-type: image/jpeg");
imagejpeg($thumb);
I didn't have to time to test it (sorry) but it should work, if it doesn't, just tell me, and I will debug it.
EDIT 1:
for large images, you must crop from it. All that you have to do might be just
$W = 600;
$H = 300;
$im = imagecreatefromjpeg($filename);
list($w,$h) = getimagesize($filename);
$x = ($W / 2) - ($w/2);
$y = ($H / 2) - ($h/2);
$x = sqrt($x * $x);
$y = sqrt($y * $y);
$newIm = imagecreatetruecolor($new_w, $new_h);
// i know I could have used a better function for this, but...
imagecopyresampled($newIm, $im, $W > $w ? $x : 0, $W > $w ? $y : 0, $W > $w ? 0 : $x, $W > $w ? 0 : $y, $w, $h, $w, $h);
header("Content-type: image/jpeg");
imagejpeg($thumb);
try it and let me know the result. Yet again, I didn't test it, it might fail in an epic manner.
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.