Transforming transparent `gif` to grayscale while saving transparency - php

First, I resize the image while saving transparency:
/*
all the classic routine etcetera:
$canvas = imagecreatefrom[png|gif|jpeg]();
$resize = imagecreatetruecolor();
*/
if($blending){
$transparentIndex = imagecolortransparent($canvas);
if($transparentIndex >= 0){
#GIF
imagepalettecopy($canvas, $resize);
imagefill($resize, 0, 0, $transparentIndex);
imagecolortransparent($resize, $transparentIndex);
imagetruecolortopalette($resize, true, 256);
}else{
#PNG
imagealphablending($resize, false);
imagesavealpha($resize, true);
$transparent = imagecolorallocatealpha($resize, 255, 255, 255, 127);
imagefill($resize, 0, 0, $transparent);
}
}
imagecopyresampled($resize, $canvas, 0, 0, 0, 0, $nx, $ny, $x, $y);
// image[png|gif|jpeg]... (image gets saved)
Then, I want to apply grayscale filter to that previously saved image (within a new function):
/*
classic routine again:
$canvas = imagecreatefrom[png|gif|jpeg]()
*/
if($blending){
imagealphablending($canvas, false);
imagesavealpha($canvas, true);
}
imagefilter($canvas, IMG_FILTER_GRAYSCALE);
/*
This fully filters PNG's to Grayscale while saving transparency,
but for GIF, black background is added to my grayscaled picture,
plus, the picture isn't fully grayscale (more like gets high-contrasted with acidic colors).
*/
// image[png|gif|jpeg]
What would be the fix, to preserve transparency when applying IMG_FILTER_GRAYSCALE to gif?
What would be the fix, in order to transform gif to grayscale while saving the transparency? (Question revised due to answer provided by #Pierre)
Thanks in advance!

imagefilter used with the gray scale filter does take care of the alpha channel.
However, gif does not support alpha. So you won't be able to store it using the GIF format.
It is also important to note that the background color (one single color or color index being used as background) has nothing to do with the alpha (level of transparency of a given color or pixel). That means you are responsible to set the background color to the desired single color.
Update
It is not directly possible as the color used as transparent will be modified. That could be considered as a bug as the transparent color may or should be ignored by the filter. But that's another topic.
A workaround could be:
$logo = imagecreatefromgif('php.gif');
$newimg = imagecreatetruecolor(imagesx($logo), imagesy($logo));
/* copy ignore the transparent color
* so that we can use black (0,0,0) as transparent, which is what
* the image is filled with when created.
*/
$transparent = imagecolorallocate($newimg, 0,0,0);
imagecolortransparent($newimg, $transparent);
imagecopy($newimg, $logo, 0,0, 0, 0,imagesx($logo), imagesx($logo));
imagefilter($newimg, IMG_FILTER_GRAYSCALE);
imagegif($newimg, 'a.gif');
this code simply fetch the value of the existing transparent color, convert it to gray and set it back to the color index. This code will only work for palette image like gif but that's the idea.

Related

Crop and Resize image from X and Y Position

I'm trying to crop then resize an image on PHP v5.4, I've read these resources
Put PNG over a JPG in PHP
http://php.net/manual/en/function.imagecopy.php
http://php.net/manual/en/function.imagecopyresampled.php
http://php.net/manual/en/function.call-user-func-array.phPP
PHP watermarking
http://php.net/manual/en/function.imagecreatetruecolor.php
My code is based off the answer from Cropping image in PHP (the dimensions between these images vary alot).
I want to resize this image from 1151x768 to 200x82 and crop the background section at x: 0, y: 686
I'd prefer not bloating the question with the entire 600 lines in this question, $output refers to setwidth1200nzpioneerthursday08398 image
<?php
$output = imagecreatefromjpeg("setwidth1200nzpioneerthursday08398.jpg");
$source_crop_image = imagecreatetruecolor(200, 82);
if(!is_resource($source_crop_image)) {
return $source_crop_image;
}
imagealphablending($output, true);
$source_copy_result = imagecopy($output, $source_crop_image, 0, 0, 0, 686, 200, 82);
$source_copy_result = (bool) $source_copy_result;
if(!$source_copy_result) {
return false;
}
$source_image_result = imagejpeg($source_crop_image, "images/mynewimage.jpg");
$source_image_result = (bool) $source_image_result;
?>
My Image setwidth1200nzpioneerthursday08398
Ideally I'm trying to get it crop the RED SECTION, while keeping the scale intact then resizing to 200x82
My Result
My Expected Result (I created this image using GIMP).
I have no idea why my resulting image is a black box ..
You have imagecopy() arguments in wrong order.
The right one is $source_copy_result = imagecopy($source_crop_image, $output, 0, 0, 0, 686, 200, 82);

How to clean background captcha image using imagick

I try to clean background captcha image using php imagick method and i used convert method as an alternative on console but i got same result. But it's not really worked.
Then i will use tesseract ocr for getting captcha code. There is a code for tesseract:
tesseract -psm 7 captcha.png output
Captcha image:
captcha
There is my code:
//Example Tutorial::backgroundMasking
function backgroundMasking()
{
//Load the image
$imagick = new \Imagick(realpath('captchaa.png'));
$backgroundColor = "rgb(162, 234, 160)";
$fuzzFactor = 0.1;
// Create a copy of the image, and paint all the pixels that
// are the background color to be transparent
$outlineImagick = clone $imagick;
$outlineImagick->transparentPaintImage(
$backgroundColor, 0, $fuzzFactor * \Imagick::getQuantum(), false
);
// Copy the input image
$mask = clone $imagick;
// Deactivate the alpha channel if the image has one, as later in the process
// we want the mask alpha to be copied from the colour channel to the src
// alpha channel. If the mask image has an alpha channel, it would be copied
// from that instead of from the colour channel.
$mask->setImageAlphaChannel(\Imagick::ALPHACHANNEL_DEACTIVATE);
//Convert to gray scale to make life simpler
$mask->transformImageColorSpace(\Imagick::COLORSPACE_GRAY);
// DstOut does a "cookie-cutter" it leaves the shape remaining after the
// outlineImagick image, is cut out of the mask.
$mask->compositeImage(
$outlineImagick,
\Imagick::COMPOSITE_DSTOUT,
0, 0
);
// The mask is now black where the objects are in the image and white
// where the background is.
// Negate the image, to have white where the objects are and black for
// the background
$mask->negateImage(false);
$fillPixelHoles = false;
if ($fillPixelHoles == true) {
// If your image has pixel sized holes in it, you will want to fill them
// in. This will however also make any acute corners in the image not be
// transparent.
// Fill holes - any black pixel that is surrounded by white will become
// white
$mask->blurimage(2, 1);
$mask->whiteThresholdImage("rgb(10, 10, 10)");
// Thinning - because the previous step made the outline thicker, we
// attempt to make it thinner by an equivalent amount.
$mask->blurimage(2, 1);
$mask->blackThresholdImage("rgb(255, 255, 255)");
}
//Soften the edge of the mask to prevent jaggies on the outline.
$mask->blurimage(2, 2);
// We want the mask to go from full opaque to fully transparent quite quickly to
// avoid having too many semi-transparent pixels. sigmoidalContrastImage does this
// for us. Values to use were determined empirically.
$contrast = 15;
$midpoint = 0.7 * \Imagick::getQuantum();
$mask->sigmoidalContrastImage(true, $contrast, $midpoint);
// Copy the mask into the opacity channel of the original image.
// You are probably done here if you just want the background removed.
$imagick->compositeimage(
$mask,
\Imagick::COMPOSITE_COPYOPACITY,
0, 0
);
// To show that the background has been removed (which is difficult to see
// against a plain white webpage) we paste the image over a checkboard
// so that the edges can be seen.
// Create the check canvas
$canvas = new \Imagick();
$canvas->newPseudoImage(
$imagick->getImageWidth(),
$imagick->getImageHeight(),
"pattern:checkerboard"
);
// Copy the image with the background removed over it.
$canvas->compositeimage($imagick, \Imagick::COMPOSITE_OVER, 0, 0);
$max = $imagick->getQuantumRange();
$max = $max["quantumRangeLong"];
$canvas->thresholdimage(0.77 * $max);
$canvas->negateImage(false, \Imagick::CHANNEL_ALL);
//Output the final image
$canvas->setImageFormat('png');
header("Content-Type: image/png");
echo $canvas->getImageBlob();
}
Result captcha image: enter image description here
what can i do for getting better result?

Converting JPEG/PNGs to PNG for database storage but can't keep background transparency/colour when displaying, in PHP

I've a script I use to convert JPEGs or PNGs to PNGs for database storage, I encode the file into base64 and store it in a database, I pull the file from the database or cache and when displaying it on a website I just pull the image data from the database or cache and do a base64 decode on it and display it using a standard IMG tag.
The issue I'm having is, no matter if the background is transparent or what colour I create as the image's background it always shows up as black when being displayed on the webpage.
I've tried numerous of the questions on the right side and none of the answers seems to work for me.
Encode Function
public function encode($image, $resize = false, $dirLevel = '')
{
$vTempFileName = TEMP_DIR . '_'.rand(1111111, 9999999) . '.png';
// Convert image to PNG
$image = imagecreatefromstring(file_get_contents($image));
imagealphablending($image, true);
imagepng($image, $vTempFileName);
$vImageDetails = array(base64_encode(file_get_contents($vTempFileName)), filesize($vTempFileName));
// Remove temporary file after processing
#unlink($vTempFileName);
return $vImageDetails;
}
Decode Function
echo base64_decode($image);
I've also tried using the below two functions
To save transparency you need to add imagesavealpha See http://php.net/manual/en/function.imagesavealpha.php
public function encode($image, $resize = false, $dirLevel = '')
{
$vTempFileName = TEMP_DIR . '_'.rand(1111111, 9999999) . '.png';
// Convert image to PNG
$image = imagecreatefromstring(file_get_contents($image));
imagealphablending($image, true);
imagesavealpha($image, true);
imagepng($image, $vTempFileName);
$vImageDetails = array(base64_encode(file_get_contents($vTempFileName)), filesize($vTempFileName));
// Remove temporary file after processing
#unlink($vTempFileName);
return $vImageDetails;
}
imagefill will perform a flood fill on the selected coordinates with the desired color defined as $transparent See: http://php.net/manual/en/function.imagefill.php
$fillColor = imagecolorallocatealpha($image, 0, 0, 0, 127);
imagefill($image, 0, 0, $fillColor);
To flood fill the adjoining color at the top left of the image with the desired fillColor.
Otherwise to add transparency use imagecolortransparent see http://php.net/manual/en/function.imagecolortransparent.php
$replaceBlack = imagecolorallocate($image, 0, 0, 0);
imagecolortransparent($image, $replaceBlack);
to replace the color black as transparent.
This may or may not help you but... you really don't want to store images into a database if you can avoid it. Is there a good reason you are doing this? File systems are good at storing binary files. Databases are good at storing data. Use the appropriate tool for the job – store it as a file.

PHP ImagickDraw with outlined text issues

I'm learning and practicing my Imagick skills.
I have issues with outlined text using Imagick stroke. I would like to achieve an effect visible on this image: a popular Internet meme:
Here's the code I have so far:
$draw = new \ImagickDraw();
$outputImage = new \Imagick('meme.jpg');
$draw->setFillColor('#fff');
$draw->setFont('impact.ttf');
$draw->setFontSize(40);
$draw->setGravity(\Imagick::GRAVITY_NORTH);
$draw->setStrokeColor('#000');
$draw->setStrokeWidth(1);
$draw->setStrokeAntialias(true);
$draw->setTextAntialias(true);
$outputImage->annotateImage($draw, 0, 5, 0, 'Sample text');
$outputImage->setFormat('png');
$outputImage->writeimage('tmp/meme.png');
The issue: text stroke does not look nice. I've found a tip on Imagick discussion board about annotating image second time, but without stroke. Does not work.
Before writing image:
$draw->setStrokeColor('transparent');
$outputImage->annotateImage($draw, 0, 5, 0, 'Sample text');
Can anybody give me a clue?
Update
To conclude, my generated image looks as following:
As you can see, I have some issues with 2px stroke while using different font size. On big fonts, it looks nice but with smaller font there are some issues with the stroke and font.
Version1: resizing
Version 2: composite over and resizing
Version 2 gives a much better result. See code below. Depending on the final size, you need to play around with font and stroke size, as the resizing may give unwanted effects. You may also try version 2 without resizing.
Version 1: resizing
$draw = new ImagickDraw();
$draw->setFillColor('#fff');
$draw->setFont('impact.ttf');
$draw->setFontSize(100); //use a large font-size
$draw->setStrokeColor('#000');
$draw->setStrokeWidth(4);
$draw->setStrokeAntialias(true); //try with and without
$draw->setTextAntialias(true); //try with and without
$outputImage = new Imagick();
$outputImage->newImage(1400,400, "transparent"); //transparent canvas
$outputImage->annotateImage($draw, 20, 100, 0, 'STOP ME FROM MEMEING');
$outputImage->trimImage(0); //Cut off transparent border
$outputImage->resizeImage(300,0, imagick::FILTER_CATROM, 0.9, false); //resize to final size
/*
Now you can compositve over the image
*/
//Clean up
$draw->clear();
$draw->destroy();
$outputImage->clear();
$outputImage->destroy();
Version 2: composite over and resizing
$draw = new ImagickDraw();
$draw->setFont('impact.ttf');
$draw->setFontSize(100); //use a large font-size
$draw->setStrokeAntialias(true); //try with and without
$draw->setTextAntialias(true); //try with and without
//Create text
$draw->setFillColor('#fff');
$textOnly = new Imagick();
$textOnly->newImage(1400,400, "transparent"); /transparent canvas
$textOnly->annotateImage($draw, 21, 101, 0, 'STOP ME FROM MEMEING'); //parameters depend of stroke and text size
//Create stroke
$draw->setFillColor('#000'); //same as stroke color
$draw->setStrokeColor('#000');
$draw->setStrokeWidth(8);
$strokeImage = new Imagick();
$strokeImage->newImage(1400,400, "transparent");
$strokeImage->annotateImage($draw, 20, 100, 0, 'STOP ME FROM MEMEING');
//Composite text over stroke
$strokeImage->compositeImage($textOnly, imagick::COMPOSITE_OVER, 0, 0, Imagick::CHANNEL_ALPHA );
$strokeImage->trimImage(0); //cut transparent border
$strokeImage->resizeImage(300,0, imagick::FILTER_CATROM, 0.9, false); //resize to final size
/*
Now you can compositve over the image
*/
//Clean up
$draw->clear();
$draw->destroy();
$strokeImage->clear();
$strokeImage->destroy();
$textOnly->clear();
$textOnly->destroy();
Can you post your result or be more specific what doesn't look fine to you?
A solution could be to create the text (with stroke) first on a transaprent background and then composite it over the image. You could create the text in bigger font size and resize to make it look smoother on the final image.

php imagick, how to make an area transparent

I want to make an area transparent within an Imagick object with a specific width, height and a top position.
For example I need a transparent area with 30px x 30px from the 15th px to the top but I can't find a way to do it.
$canvas1 = new Imagick();
$canvas1->newImage(30,60,'black','png');
Please help.
This may be a slightly simpler way of doing it. I recycled #AndreKR's setup code to get started:
$im = new Imagick();
$im->newImage(100,100, 'red');
$im->setImageAlphaChannel(Imagick::ALPHACHANNEL_ACTIVATE); // make sure it has an alpha channel
$box=$im->getImageRegion(30,30,15,15);
$box->setImageAlphaChannel(Imagick::ALPHACHANNEL_TRANSPARENT);
$im->compositeImage($box,Imagick::COMPOSITE_REPLACE,15,15);
While you can flood fill with transparency ink (not transparent ink) like this:
$im->floodFillPaintImage('#FF000000', 10, '#FFFFFF', 0, 0, false);
in this post, Anthony, apparently some important figure in the ImageMagick universe, says that you cannot draw with transparency.
So it seems you have to create a punch image and then use it to punch the transparent areas out in your actual image. To create the punch here I draw the rectangle opaque on a transparent brackground and then invert the whole image:
$punch = new Imagick();
$punch->newImage(100,100, 'transparent');
$drawing = new ImagickDraw();
$drawing->setFillColor(new ImagickPixel('black'));
$drawing->rectangle(15, 15, 45, 45);
$punch->drawImage($drawing);
$punch->negateImage(true, Imagick::CHANNEL_ALPHA);
Here's the actual image before the punching:
$im = new Imagick();
$im->newImage(100,100, 'red');
$im->setImageAlphaChannel(Imagick::ALPHACHANNEL_ACTIVATE); // make sure it has
// an alpha channel
Now we can copy over the alpha channel from our punch image. For a reason unknown to me the obvious way does not work:
// Copy over the alpha channel from one image to the other
// this does NOT work, the $channel parameter seems to be useless:
// $im->compositeImage($punch, Imagick::COMPOSITE_SRC, 0, 0, Imagick::CHANNEL_ALPHA);
However, these both work:
// Copy over the alpha channel from one image to the other
// $im->compositeImage($punch, Imagick::COMPOSITE_COPYOPACITY, 0, 0);
// $im->compositeImage($punch, Imagick::COMPOSITE_DSTIN, 0, 0);
(The light blue is the background of the Windows photo viewer, indicating transparent areas.)
You can set the opacity as follows
$image->setImageOpacity(0.0);
If you set it to 0.0 the image what you have crated will become transparent
for more information you can Set opacity in Imagick
if you want it for a particular area part then you need to change the approach by using GD library functions by doing some what like this
$img = imagecreatefrompng($imgPath); // load the image
list($width,$height) = getimagesize($imgPath); // get its size
$c = imagecolortransparent($img,imagecolorallocate($img,255,1,254)); // create transparent color, (255,1,254) is a color that won't likely occur in your image
$border = 10;
imagefilledrectangle($img, $border, $border, $width-$border, $height-$border, $c); // draw transparent box
imagepng($img,'after.png'); // save
I Could see a similar requirement which is posted in another forum here
Try
$canvas1->setImageOpacity(0);

Categories