PHP: grayscale jpgs, gifs, pngs... also with alpha channels? - php

$im = ImageCreateFromString(file_get_contents($source_file));
ImageFilter($im, IMG_FILTER_GRAYSCALE);
any idea what i could do, to properly grayscale gifs and pngs with transperancy? This snippet actually works good, it transforms jpgs and pngs to grayscale. However gifs are a little bit "buggy" - they don't always work, it depends on the image. Sometimes there are a few pale colors left in them. Moreover this snippet doesn't work with alpha-channels. If i convert a gif or a png with transparancy the transparent parts always get blackened.
Of course im querying the image-type and after "grayscaling" it, i'll set the proper type again.
Have you any ideas?

This code should preserve the alpha, but it's slower than imagefilter:
$im = ImageCreateFromString(file_get_contents($source_file));
$width=imagesx();
$height=imagesy();
for($x=0;$x<$width;$x++)
for($y=0;$y<$height;$y++)
{
$rgb=imagecolorsforindex($im,imagecolorat($im,$x,$y));
$average=ceil(($rgb["red"]+$rgb["green"]+$rgb["blue"])/3);
imagesetpixel($im,$x,$y,imagecolorallocatealpha($im,$average,$average,$average,$rgb['alpha']));
}
If you still have problems try to write this after the image creation (before the $width=..):
imagesavealpha($im,true);

For pngs a simple call to imagesavealpha() solves the problem of black pixels on the alpha channel, complete code:
$im = ImageCreateFromString(file_get_contents($source))
imagefilter($im, IMG_FILTER_GRAYSCALE);
imagesavealpha($im,true);
imagepng( $im, $output );

Related

Imagemagick resize images dynamically

I have created a App wehere the user can upload an Image and choose a region to crop. Afterward the image should be resized and cropped with the chosen coordinates.
The problem is, that those image are printed afterwards, so the quality should be as close to the original image as possible(without artifacts, or visible blur).
Anyway resizing causes a loss of quality and using unsharpmask() after resizing makes it a lot better, but it still differs from image to image.
Oh and sometimes the colours don't even match with the original :(
I know this function's parameters depend on things like the size and saturation of the image, but is there any way to calculate the best parameters?
Here's what I have so far:
$origPath = "ThePathToTheFileOnTheServer.jpg";
move_uploaded_file( $_FILES['imagefile']['tmp_name'],$origPath);
$img = new Imagick();
$file_handle_for_viewing_image = fopen($origPath, 'a+');
$img->readImageFile($file_handle_for_viewing_image);
fclose($file_handle_for_viewing_image);
$img->setImageFormat('jpg');
$img->setImageCompression(Imagick::COMPRESSION_JPEG);
$img->setImageCompressionQuality(100);
$img->resizeImage ( 0, $targ_h, Imagick::FILTER_LANCZOS, 1);
$img->unsharpMaskImage(0 , 0.5 , 1 , 0.05);
$img->cropImage($targ_w, $targ_h, $xPos, 0);
$newimg = new Imagick();
$newimg->newImage($targ_w,$targ_h,new ImagickPixel('white'));
$newimg->setImageFormat('jpg');
$newimg->setImageCompression(Imagick::COMPRESSION_JPEG);
$newimg->setImageCompressionQuality(100);
$newimg->compositeImage($img,Imagick::COMPOSITE_OVER,0,0);
$newimg->writeImage("TheNewImage.jpg");
It does what it should, but can I somehow calculate the parameters of $img->unsharpMaskImage(0 , 0.5 , 1 , 0.05); to fix the current Image?
Thanks for your help!
Here's an example:
original:
after resize:
You could try resizing in the Lab colourspace and then converting back to sRGB afterwards. At the command-line, that would be
convert input.jpg -colorspace Lab -resize ... -colorspace sRGB result.jpg

imagerotate changes original image's color

I am encountering a very strange behavior when rotating images with PHP. The original image is being changed during the rotation. As an example, here are the side by side images. It's hard to see, but if you look closely, the white area surrounding the knife is being changed from #FFFFFF to #FDFDFD.
I am using imagerotate with no options specified, however, if I try the same thing with a black background color, I get a rotated image with black "fill", but the white part of the image is still changed from #FFFFFF to #FDFDFD. I am baffled. It's almost like PHP is "rounding" the colors.
original image: http://i.imgur.com/iYwvxAE.jpg
rotated image: http://i.imgur.com/0lXXuO6.jpg
edit:
here's my code
$img = imagecreatefromjpeg($localFile);
$img = imagerotate($img, 45, 0);
imagejpeg($img, '/tmp/a.jpg');
I got it! On the php doc for imagerotate (http://php.net/manual/en/function.imagerotate.php) I noticed
"Note: This function is affected by the interpolation method set by imagesetinterpolation()."
I then tried a few different interpolation algorithms. IMG_BELL seems to maintain white.
<?php
function LoadJpeg($imgname)
{
$im = imagecreatefromjpeg($imgname);
imagesetinterpolation($im, IMG_BELL);
$im = imagerotate($im, 45, 0);
return $im;
}
$img = LoadJpeg('test.jpg');
imagejpeg($img, 'C:\temp\a.jpg', 100);
imagedestroy($img);

Partial black background when watermarking PNG image with GD PHP

I have pieced together a PHP class to perform various image related functions using GD functions of PHP.
It works great for all image types. Rotate, flip, resize, crop and to a lesser extent, watermark.
All but the latter work perfectly. For example after a few changes, rotated PNG images retained their transparency whereas before they were losing that and the background turning black. Common problem, it appears. But all working now.
Where I'm still getting stuck is watermarking a PNG image with another PNG image. It appears to work fine with JPG and other images. This is the code (simplified):
public function writeWatermarkSimple()
{
$watermarkFile = 'watermark.png';
$watermarkImage = imagecreatefrompng($watermarkFile);
imagealphablending($watermarkImage, false);
imagesavealpha($watermarkImage, true);
$imageFile = 'image.png';
$baseImage = imagecreatefrompng($imageFile);
imagealphablending($baseImage, false);
imagesavealpha($baseImage, true);
$marginH = imagesx($baseImage) - imagesx($watermarkImage);
$marginV = imagesy($baseImage) - imagesy($watermarkImage);
$cut = imagecreatetruecolor(imagesx($watermarkImage), imagesy($watermarkImage));
imagecopy($cut, $baseImage, 0, 0, $marginH, $marginV, imagesx($watermarkImage), imagesy($watermarkImage));
imagecopy($cut, $watermarkImage, 0, 0, 0, 0, imagesx($watermarkImage), imagesy($watermarkImage));
imagecopymerge($baseImage, $cut, $marginH, $marginV, 0, 0, imagesx($watermarkImage), imagesy($watermarkImage), 80);
if (!imagepng($baseImage, 'watermarked_image.png'))
{
return false;
}
return true;
}
This has been pieced together with various guides and advice people have given based on a similar issue. Again, working perfectly with JPG images and PNG watermarks, but not PNG & PNG.
Some example images:
http://i.imgur.com/hHRWinj.png - This is the watermark I'm using.
http://i.imgur.com/6sy8Ncs.png - This is the image I'm applying the watermark to.
http://i.imgur.com/ghovYLm.png - This is the end result.
The bit I find interesting is that any part of the watermark that is overlaid on a non-transparent portion of the image is working fine. Just the rest of it has the black background.
This leads me to believe I'm close, and I hope that the expertise of you fine people may lead me to the solution.
Thanks ever so for reading.
So, I'm not giving up on finding the correct answer to do this using GD. However, I was overjoyed to find that what needed up to 30 lines of code with GD can be achieved using much less with ImageMagick:
$image = new Imagick();
$image->readimage($this->_image);
$watermark = new Imagick();
$watermark->readimage($this->_watermark->_getImage());
$watermark->evaluateImage(Imagick::EVALUATE_DIVIDE, 2, Imagick::CHANNEL_ALPHA);
$image->compositeImage($watermark, imagick::COMPOSITE_OVER, $marginH, $marginV);
So this is before (with GD):
http://i.imgur.com/AlS0TcO.png
And after (with ImageMagick and the code above):
http://i.imgur.com/zBxlC3R.png
If anyone has an answer that is purely GD then I'd be immensely grateful.
Ran into some similar issues recently and while this may not exactly solve your problem, these were some useful discoveries that I made.
In my case, I have an original .jpg image and a watermark .png image. The watermark image has a fully transparent background. I wanted to specify the opacity in my script and have it change the watermark opacity before placing it on top of the origina image. Most posts out there regarding PHP watermarking assume that the original watermark .png file already has the solid watermark portion set to the correct opacity rather than changing it via the script.
gd didn't like a 24 bit .png and caused some goofy issues. Switching to 8 bit resolved that with gd. On the other hand, imagick works very well with a 24 bit .png and the final result seems to be better.
For me, using gd worked just fine if I was opening the original watermark .png and using imagecopymerge() to set the watermark transparency. If however I tried to scale the original watermark .png (which has transparent background) first, then I would get similar results as you with black or white background portion of where watermark image is. See How do I resize pngs with transparency in PHP? for a partial solution by filling the new wm image with transparent rectangle first. For me this still produced an opaque white background on the final result no matter what I tried.
I switched to imagick and was using setImageOpacity() to change the transparency of my watermark .png before applying it on top of my original image and I was still getting the same effect with a black background. Finally read in the PHP doc for setImageOpacity() that if the original .png has any transparent pixels and you try to lower the opacity, those pixels become opaque (black) with the new transparency applied. Instead, need to use the evaluateImage() function. This will instead evaluate each pixel's alpha channel only and divide by the specifid number.
I assume the black / white background issue with gd is likely due to similar ways that it treats alpha channels when scaling / combining as compared to imagick and if you want to do it all in gd you just need to find some similar way to evaluate and manipulate the alpha channel per-pixel because the "easy" ways seem to take an already transparent background and make it opaque.
So, the solution:
Assuming you want to apply your watermark at an opacity of 45% and you're using imagick, then instead of this:
$watermark->setImageOpacity(.45);
do this
$watermark->evaluateImage(Imagick::EVALUATE_DIVIDE, (1/.45), Imagick::CHANNEL_ALPHA);
You need to divide 1 by your opacity to get the demoninator by which the function will divide the alpha channel value for each pixel. In this case, 1/.45 = 2.2222, so then the function will divide the alpha channel of each pixel by 2.2222. This means a solid pixel (alpha of 1) would result in 1/2.2222 or .45 alpha or transparency when finished. Any pixels that were already transparent (alpha 0) would stay transparent because 0 divided by anything is always what? Zero!
After you change the watermark transparency then you can use compositeImage() to merge the watermark onto the original image.

PHP Image manipulation moire effect

I am making a site where people can upload their own background images. One of the things users really having a problem understanding is image sizes and resolution. If I say 'Make sure your background is at least 1080x1920 pixels (Full HiDef)' I am guessing a good 70% of people will not know what this means or how to do it.
So what I would like to do is enlarge small images in a nicer way than just making them blurry. What I would like to do is something like this:-
http://nanotux.com/plugins/fullscreenr/index.html
where basically every other dot is a black pixel, that way small images will be twice the size when they are stretched to 1080x1920 and generally look better.
Does anyone know of a way to do this with PHP's image functions?
(as an aside does anyone know what this type of effect should be called? Moire? would that be accurate?)
Thanks in advance
If I understood you well, you want to set every second pixel in x and y dimensions to black on the stretched image?
This should do the trick (not tested, I relied on PHP documentation).
$initialImage = ... // handle to the image
$srcWidth = imagesx($initialImage);
$srcHeight = imagesy($initialImage);
$maxX = 1920;
$maxY = 1080;
$newImage = imagecreatetruecolor($max_x, $max_y);
imagecopyresampled ($newImage, $initialImage, 0,0,0,0, $maxX, $maxY, $srcWidth, $srcHeight);
$BLACK = imagecolorallocate($newImage, 0, 0, 0);
for($x=0; $x+=2; $x<$max_x){
for($y=0; $y+=2; $y<$max_y){
imagesetpixel($newImage, $x, $y, $BLACK);
}
}
Documentation: imagesetpixel, imagecolorallocate, imagecopyresampled,
imagecreatetruecolor.
Read PHP documentation and examples there. Remember to use imagecopyresampled instead of imagecopyresized to have better quality.
The most optimal way would be to create a transparent raster (.png) "by hand" (i.e. create it programmatically as in jakub.gieryluk's solution), and overlay that, multiple times if needed, via imagecopy().
Drawing pixel by pixel is painfully slow.

Bad quality of GIF image in PHP GD

I have a watermark script I am working on, the results are good on PNG and on JPG images however a gif image, not so good. I am using PHP and GD
Below you can see the difference in quality of the watermark.
Anyone know how to improve this?
For the gif version I am using
$image = imagecreatefromgif($source_file);
imagecopymerge($image, $watermark, $x, $y, 0, 0, $water_width, $water_height, 65);
imagegif($image, $source_file);
gif image = bad quality
gif image http://img2.pict.com/fd/46/00/1471179/0/gif.gif
jpg image = good
jpg image http://img2.pict.com/82/a1/5a/1471181/0/jpg.jpg
GIF images have a fixed palette that can contain a maximum of 256 colors. The issue here is probably that the image your inserting uses colors that isn't available in the target image.
I have never tried this, but it might be worth a shot. You could try converting the gif image to a true color image first, then do the watermarking and after that converting it back to gif.
$image = imagecreatefromgif($source_file);
// create a true color image of the same size
$image2 = imagecreatetruecolor(imagesx($image), imagesy($image));
// copy the original gif image on to the true color image
imagecopy($image2, $image, 0, 0, 0, 0, imagesx($image), imagesy($image));
// copy the watermark onto the new true color image
imagecopymerge($image2, $watermark, $x, $y, 0, 0, $water_width, $water_height, 65);
// write the new image to disk
imagegif($image2, $source_file);
Try it and see if it makes a difference.
There's also a couple of palette manipulation functions available that might help:
imagecolormatch()
imagetruecolortopalette()
imagepalettecopy()
I'm not sure how you would apply them, but I'm guessing that there's a few things you could do to improve the results.
GIF only supports a color palette of 256 colors. Therefore, the colors of your watermark image have to be mapped to this palette. This caused them to be rendered and saved with different colors than before. Due to this small palette, GIF is not recommended for photos in general, anyways.
GIF images will never look great, as the colour palette is 256 colours. As MrMage says, colour mapping causes a severe approximation of the true colours of the image. You are better off with PNGs, they do support transparency.

Categories