Knowing if an image has transparency? [duplicate] - php

How do I check if a PNG image has transparent pixels using PHP's GD extension?

I know this is old, but I just found this on the comments of the PHP docs. (link)
Here is the function which determines whether the PNG image contains alpha or not:
<?php
function is_alpha_png($fn){
return (ord(#file_get_contents($fn, NULL, NULL, 25, 1)) == 6);
}
?>
The color type of PNG image is stored at byte offset 25. Possible values of that 25'th byte is:
0 - greyscale
2 - RGB
3 - RGB with palette
4 - greyscale + alpha
6 - RGB + alpha
Only works for PNG images though.

It doesn't look like you can detect transparency at a glance.
The comments on the imagecolorat manual page suggest that the resulting integer when working with a true-color image can actually be shifted four times total, with the fourth being the alpha channel (the other three being red, green and blue). Therefore, given any pixel location at $x and $y, you can detect alpha using:
$rgba = imagecolorat($im,$x,$y);
$alpha = ($rgba & 0x7F000000) >> 24;
$red = ($rgba & 0xFF0000) >> 16;
$green = ($rgba & 0x00FF00) >> 8;
$blue = ($rgba & 0x0000FF);
An $alpha of 127 is apparently completely transparent, while zero is completely opaque.
Unfortunately you might need to process every single pixel in the image just to find one that is transparent, and then this only works with true-color images. Otherwise imagecolorat returns a color index, which you must then look up using imagecolorsforindex, which actually returns an array with an alpha value.

I know this is an old thread, but in my opinion it needs improvement since walking through a huge png by checking all pixels only to find out it is not transparent is a waste of time. So after some googleing I found Jon Fox's Blog and I improved his code with the help of the W3C PNG Specification further to be reliable, fast and have a minimum on memory imprint:
function IsTransparentPng($File){
//32-bit pngs
//4 checks for greyscale + alpha and RGB + alpha
if ((ord(file_get_contents($File, false, null, 25, 1)) & 4)>0){
return true;
}
//8 bit pngs
$fd=fopen($File, 'r');
$continue=true;
$plte=false;
$trns=false;
$idat=false;
while($continue===true){
$continue=false;
$line=fread($fd, 1024);
if ($plte===false){
$plte=(stripos($line, 'PLTE')!==false);
}
if ($trns===false){
$trns=(stripos($line, 'tRNS')!==false);
}
if ($idat===false){
$idat=(stripos($line, 'IDAT')!==false);
}
if ($idat===false and !($plte===true and $trns===true)){
$continue=true;
}
}
fclose($fd);
return ($plte===true and $trns===true);
}

It can be done!
I've combined all answers and comments into one function which should be fast & reliable:
function hasAlpha($imgdata) {
$w = imagesx($imgdata);
$h = imagesy($imgdata);
if($w>50 || $h>50){ //resize the image to save processing if larger than 50px:
$thumb = imagecreatetruecolor(10, 10);
imagealphablending($thumb, FALSE);
imagecopyresized( $thumb, $imgdata, 0, 0, 0, 0, 10, 10, $w, $h );
$imgdata = $thumb;
$w = imagesx($imgdata);
$h = imagesy($imgdata);
}
//run through pixels until transparent pixel is found:
for($i = 0; $i<$w; $i++) {
for($j = 0; $j < $h; $j++) {
$rgba = imagecolorat($imgdata, $i, $j);
if(($rgba & 0x7F000000) >> 24) return true;
}
}
return false;
}
//SAMPLE USE:
hasAlpha( imagecreatefrompng("myfile.png") ); //returns true if img has transparency

Pretty strait forward function, it will check if there is any transparent pixel in the image, if it is, it will return true.
$im = imagecreatefrompng('./transparent.png');
if(check_transparent($im)) {
echo 'DA';
}
else {
echo 'NU';
}
function check_transparent($im) {
$width = imagesx($im); // Get the width of the image
$height = imagesy($im); // Get the height of the image
// We run the image pixel by pixel and as soon as we find a transparent pixel we stop and return true.
for($i = 0; $i < $width; $i++) {
for($j = 0; $j < $height; $j++) {
$rgba = imagecolorat($im, $i, $j);
if(($rgba & 0x7F000000) >> 24) {
return true;
}
}
}
// If we dont find any pixel the function will return false.
return false;
}

This is how I detect 8-32 bit transparency. It only work with PNG's.
function detect_transparency($file){
if(!#getimagesize($file)) return false;
if(ord(file_get_contents($file, false, null, 25, 1)) & 4) return true;
$content = file_get_contents($file);
if(stripos($content,'PLTE') !== false && stripos($content, 'tRNS') !== false) return true;
return false;
}

cronoklee's function is very good, but when I was using it I found a bug. It does not work for images with 8 bit pallet. Here is the fixed variant:
public function hasAlpha($imgdata)
{
$w = imagesx($imgdata);
$h = imagesy($imgdata);
if($w>100 || $h>100){ //resize the image to save processing
$thumb = imagecreatetruecolor(100, 100);
imagealphablending($thumb, FALSE);
imagecopyresized( $thumb, $imgdata, 0, 0, 0, 0, 100, 100, $w, $h );
$imgdata = $thumb;
$w = imagesx($imgdata);
$h = imagesy($imgdata);
}
//run through pixels until transparent pixel is found:
for($i = 0; $i<$w; $i++) {
for($j = 0; $j < $h; $j++) {
$ci = imagecolorat($imgdata, $i, $j);
$rgba = imagecolorsforindex($imgdata, $ci);
if($rgba['alpha']) { return true; }
}
}
return false;
}

Improved cronoklee's function. Removed unnecessary bit shifting for each pixel, reduced false negatives count, added explanation in function description.
/**
* Estimates, if image has pixels with transparency. It shrinks image to 64 times smaller
* size, if necessary, and searches for the first pixel with non-zero alpha byte.
* If image has 1% opacity, it will be detected. If any block of 8x8 pixels has at least
* one semi-opaque pixel, the block will trigger positive result. There are still cases,
* where image with hardly noticeable transparency will be reported as non-transparent,
* but it's almost always safe to fill such image with monotonic background.
*
* Icons with size <= 64x64 (or having square <= 4096 pixels) are fully scanned with
* absolutely reliable result.
*
* #param resource $image
* #return bool
*/
function hasTransparency ($image): bool {
if (!is_resource($image)) {
throw new \InvalidArgumentException("Image resource expected. Got: " . gettype($image));
}
$shrinkFactor = 64.0;
$minSquareToShrink = 64.0 * 64.0;
$width = imagesx($image);
$height = imagesy($image);
$square = $width * $height;
if ($square <= $minSquareToShrink) {
[$thumb, $thumbWidth, $thumbHeight] = [$image, $width, $height];
} else {
$thumbSquare = $square / $shrinkFactor;
$thumbWidth = (int) round($width / sqrt($shrinkFactor));
$thumbWidth < 1 and $thumbWidth = 1;
$thumbHeight = (int) round($thumbSquare / $thumbWidth);
$thumb = imagecreatetruecolor($thumbWidth, $thumbHeight);
imagealphablending($thumb, false);
imagecopyresized($thumb, $image, 0, 0, 0, 0, $thumbWidth, $thumbHeight, $width, $height);
}
for ($i = 0; $i < $thumbWidth; $i++) {
for ($j = 0; $j < $thumbHeight; $j++) {
if (imagecolorat($thumb, $i, $j) & 0x7F000000) {
return true;
}
}
}
return false;
}
Usage:
hasTransparency( imagecreatefrompng("myfile.png") ); //returns true if img has transparency

Related

How to convert a grayscale image to pure black and white in php?

I'm trying to convert a grayscale image to pure black and white in PHP using the GD library.
The purpose would be to detect the cervical cells within the image.
I'll leave the PHP code and a MatLab one (I wrote this code in MatLab and I'm trying to obtain the same result in PHP). Basically, I'm having trouble accessing each individual pixel's color and modifying it. sample image
PHP:
<?php
$im = imagecreatefromjpeg("celule.jpg");
function imagetograyscale($im)
{
if (imageistruecolor($im)) {
imagetruecolortopalette($im, false, 256);
}
for ($c = 0; $c < imagecolorstotal($im); $c++) {
$col = imagecolorsforindex($im, $c);
$gray = round(0.299 * $col['red'] + 0.587 * $col['green'] + 0.114 * $col['blue']);
imagecolorset($im, $c, $gray, $gray, $gray);
}
}
imagetograyscale($im);
//imagefilter($im, IMG_FILTER_CONTRAST, -255); //i'm not looking for this effect
header('Content-type: image/jpeg');
imagejpeg($im);
$C = imagesx($im); //width
$L = imagesy($im); //height
echo "Dimensiuni imagine: latime $C, inaltime $L <br>";
//scanning through the image
for($x = 0; $x < $L; $x++) { //each line
for($y = 0; $y < $C; $y++) { //each column
// pixel color at (x, y)
$color = imagecolorat($im, $y, $x);
$color = imagecolorsforindex($im, $color); //getting rgb values
$RED[$x][$y] = $color["red"]; //each rgb component
$GREEN[$x][$y] = $color["green"];
$BLUE[$x][$y] = $color["blue"];
}
}
?>
MATLAB:
clear all, clc, close all;
I = imread('celule.jpg');
imshow(I)
title('original');
a=rgb2gray(I);
figure;
imshow(a)
title('grayscale');
s=size(a);
for i=1:s(1)
for j=1:s(2)
if a(i,j)>190
a(i,j)=0;
else a(i,j)=255;
end
end
end
figure;
imshow(a)
title('pure black and white');
Here's a way to do that with gd:
#!/usr/bin/php -f
<?php
// Open image and get dimensions
$im = imagecreatefromjpeg("cellule.jpg");
$w = imagesx($im);
$h = imagesy($im);
// Convert to greyscale
imagefilter($im,IMG_FILTER_GRAYSCALE);
imagepng($im, "grey.png"); // DEBUG only
// Allocate a new palette image to hold the b&w output
$out = imagecreate($w,$h);
// Allocate b&w palette entries
$black = imagecolorallocate($out,0,0,0);
$white = imagecolorallocate($out,255,255,255);
// Iterate over all pixels, thresholding to pure b&w
for ($x = 0; $x < $w; $x++) {
for ($y = 0; $y < $h; $y++) {
// Get current color
$index = imagecolorat($im, $x, $y);
$grey = imagecolorsforindex($im, $index)['red'];
// Set pixel white if below threshold - don't bother settting black as image is initially black anyway
if ($grey <= 190) {
imagesetpixel($out,$x,$y,$white);
}
}
}
imagepng($out, "result.png");
?>

PHP - Make image backgrounds transparent with tolerance

so I am making three images transparent and layer them over each other. I followed example code in this post: Remove Image background with php and save transparent png but the extracted images all have ugly white borders around them due to the images do not have a complete uniform single-color background. Even though they may appear white, in fact there are usually different shades of grey or even blue involved.
So now I want to remove these ugly white borders in the image.
I found a java function online which solves the problem: http://www.logikdev.com/2011/10/05/make-image-backgrounds-transparent-with-tolerance/ Here is the code he uses:
private Image makeColorTransparent(final BufferedImage im, final Color color, int tolerance) {
int temp = 0;
if (tolerance < 0 || tolerance > 100) {
System.err.println("The tolerance is a percentage, so the value has to be between 0 and 100.");
temp = 0;
} else {
temp = tolerance * (0xFF000000 | 0xFF000000) / 100;
}
final int toleranceRGB = Math.abs(temp);
final ImageFilter filter = new RGBImageFilter() {
// The color we are looking for (white)... Alpha bits are set to opaque
public int markerRGBFrom = (color.getRGB() | 0xFF000000) - toleranceRGB;
public int markerRGBTo = (color.getRGB() | 0xFF000000) + toleranceRGB;
public final int filterRGB(final int x, final int y, final int rgb) {
if ((rgb | 0xFF000000) >= markerRGBFrom && (rgb | 0xFF000000) <= markerRGBTo) {
// Mark the alpha bits as zero - transparent
return 0x00FFFFFF & rgb;
} else {
// Nothing to do
return rgb;
}
}
};
final ImageProducer ip = new FilteredImageSource(im.getSource(), filter);
return Toolkit.getDefaultToolkit().createImage(ip);
}
But I don't know how to do this with php.
Anyone who can help me?
You can use the IMagick::paintTransparentImage
The signature for this method is as follows
bool Imagick::paintTransparentImage ( mixed $target , float $alpha , float $fuzz );
An example use case for this would be:
$im = new Imagick("test.jpg");
$im->paintTransparentImage(($im->getImagePixelColor(0, 0), 0, 1200));
$im->setImageFormat("png");
$im->writeImage("test.png");
You will have to play arround with the $fuzz parameter to get the kind of results you are looking for.
function transparant($url){
$img = urldecode(trim($url));
$imgx = imagecreatefromjpeg($img);
$img_w = imagesx($imgx);
$img_h = imagesy($imgx);
$im = imagecreatetruecolor($img_w, $img_h);
imagesavealpha( $im, true );
$rgb = imagecolorallocatealpha( $im, 0, 0, 0, 127 );
imagefill( $im, 0, 0, $rgb );
$color = imagecolorat( $imgx, 1, 1);
$temp = 0;
$tolerance = 20;
$temp = $tolerance * (0xFF000000 | 0xFF000000) / 100;
$toleranceRGB = abs($temp);
$startcolor = $color - $toleranceRGB;
$endcolor = $color + $toleranceRGB;
for( $x = 0; $x < $img_w; $x++ ) {
for( $y = 0; $y < $img_h; $y++ ) {
$c = imagecolorat( $imgx, $x, $y );
$startcolor."|".$c."|".$endcolor."<br>";
if ($c > $startcolor && $c < $endcolor){}else{
imagesetpixel( $im, $x, $y, $c);
}
}
}
$filename = basename($img);
header('Content-type: image/png');
imagepng($im, null);
imagedestroy($im);
imagedestroy($imgx);
}
transparant([url encoded image]);

Php Image Transformation not Doing What I Expect It to Do

The following block of php code is supposed to take this image as input snd produce this image as output (convert the black to yellow and the light-blue to black):
However, I'm getting this image as output instead.
Can anyone see the problem with my code?
$im = imagecreatefrompng("./input.png");
$width = imagesx($im);
$height = imagesy($im);
$new = imagecreate($width, $height);
imagecopy($new, $im, 0, 0, 0, 0, $width, $height);
imagecolorset($new, imagecolorexact($new, 0, 0, 0), 255, 255, 0);
for($i = 0; $i < $width; $i++) {
for($j = 0; $j < $height; $j++) {
$index = imagecolorat($new, $i, $j);
$rgb = imagecolorsforindex($new, $index);
if($rgb['red'] != 255 && $rgb['green'] != 255 && $rgb['blue'] != 0) {
echo '(' . $i . ', ' . $j . ')' . 'color => (' . (255 - $rgb['blue']) . ', ' . (255 - $rgb['blue']) . ', 0)<br />';
$color = imagecolorallocate($new, 255 - $rgb['blue'], 255 - $rgb['blue'], 0);
imagesetpixel($new, $i, $j, $color);
}
unset($index);
unset($rgb);
}
}
imagepng($new, 'tesst.png');
imagedestroy($im);
imagedestroy($new);
I believe the source of the issue here is that when using a palette based image, such as the one you have created by calling imagecreate(), it is possible to declare the same color at multiple indexes within the palette.
This means that, because you are calling imagecolorallocate() on every iteration, the palette eventually becomes full and imagecolorallocate() starts returning false (this can be seen if you var_dump($color); before the imagesetpixel() call). false evaluates to zero when cast to an integer - so when the palette fills up, all remaining pixels are effectively converted to the background color.
There are two things that you could do about this, the first and probably easiest is just to use a true-color image, which is just a simple case of changing imagecreate($width, $height); to imagecreatetruecolor($width, $height);.
If you want to stick with the palette-based image (for example for reasons of output image data size - with an image containing so few colors, a palette-based image will be considerably smaller), you will need to cache the allocated colors manually so you can re-use them, something like this:
// ...
$colors = array();
for ($x = 0; $x < $width; $x++) { // iterate x axis
for ($y = 0; $y < $height; $y++) { // iterate y axis
// Get the color at this index
$index = imagecolorat($new, $x, $y);
// Only allocate a new color if not already done
if (!isset($colors[$index])) {
$rgb = imagecolorsforindex($new, $index);
if ($rgb['red'] != 255 || $rgb['green'] != 255 || $rgb['blue'] != 0) {
// If it's not the background color allocate a new color
$r = $g = 255 - $rgb['blue'];
$b = 0;
$colors[$index] = imagecolorallocate($new, $r, $g, $b);
} else {
// Otherwise set the index to false, we can ignore it
$colors[$index] = false;
}
}
// If there's something to do, do it
if ($colors[$index] !== false) {
imagesetpixel($new, $x, $y, $colors[$index]);
}
}
}
// ...
You may also wish to track the colors in use in the image so you can "cleanse the palette" afterwards (i.e. deallocate any colors that are no longer in use in the image, which will further help with the data size reduction). Although arguably it would be better, in this case, to start with a clean palette and inspect the old image resource to get the pixel details, instead of copying the original to a new image resource.
yes your
$color = imagecolorallocate($new, 255 - $rgb['blue'], 255 - $rgb['blue'], 0);
is messing everything up..
if you desire the same output... just paste the line outside for loop this will solve your problem if you want the specific image:
$color = imagecolorallocate($new, 35, 35, 0); //got from debugging
and it will get u the desired output.
Dins

Multiply filter with PHP's GD library

I've tried experimenting with the GD library to simulate Photoshop's muliply effect, but I haven't found a working solution yet.
According to Wikipedia, the multiply blend mode:
[...] multiplies the numbers for each pixel of the top layer with the corresponding pixel for the bottom layer. The result is a darker picture.
Does anyone know of a way to achieve this using PHP? Any help would be much appreciated.
You need to take every pixel of your image, then multiply each RGB value with your background color / 255 (it's the Photoshop formula). For example, a JPG file with a red background color multiply filter, saved as a PNG file for better results:
<?php
$filter_r=216;
$filter_g=0;
$filter_b=26;
$suffixe="_red";
$path=YOURPATHFILE;
if(is_file($path)){
$image=#imagecreatefromjpeg($path);
$new_path=substr($path,0,strlen($path)-4).$suffixe.".png";
$imagex = imagesx($image);
$imagey = imagesy($image);
for ($x = 0; $x <$imagex; ++$x) {
for ($y = 0; $y <$imagey; ++$y) {
$rgb = imagecolorat($image, $x, $y);
$TabColors=imagecolorsforindex ( $image , $rgb );
$color_r=floor($TabColors['red']*$filter_r/255);
$color_g=floor($TabColors['green']*$filter_g/255);
$color_b=floor($TabColors['blue']*$filter_b/255);
$newcol = imagecolorallocate($image, $color_r,$color_g,$color_b);
imagesetpixel($image, $x, $y, $newcol);
}
}
imagepng($image,$new_path);
}
?>
I've been looking for Multiply blend between two images as well and couldn't find any native-php solution for it. It appears that only way (for now) is to "manually" set pixels, pixel-by-pixel. Here's my code that does Multiply blend between two images, assuming that images are of the same size. You can adjust it to handle different sizes if you like.
function multiplyImage($dst,$src)
{
$ow = imagesx($dst);
$oh = imagesy($dst);
$inv255 = 1.0/255.0;
$c = imagecreatetruecolor($ow,$oh);
for ($x = 0; $x <$ow; ++$x)
{
for ($y = 0; $y <$oh; ++$y)
{
$rgb_src = imagecolorsforindex($src,imagecolorat($src, $x, $y));
$rgb_dst = imagecolorsforindex($dst,imagecolorat($dst, $x, $y));
$r = $rgb_src['red'] * $rgb_dst['red']*$inv255;
$g = $rgb_src['green'] * $rgb_dst['green']*$inv255;
$b = $rgb_src['blue'] * $rgb_dst['blue']*$inv255;
$rgb = imagecolorallocate($c,$r,$g,$b);
imagesetpixel($c, $x, $y, $rgb);
}
}
return $c;
}
Function returns image object so you should ensure to do imagedestroy after you're done using it.
There should be a workaround using overlay native-php blend, which suggests that 50% gray pixels of destination image will be affected by source pixels. In theory, if you do need to blend two black-and-white images (no gray tones), if you set contrast of destination image so white will become 50%-gray, and then overlay-blend source image over it, should give you something similar to multiply. But for color images, or grayscale images, this wouldn't work - above method appears to be the only option.
I was led into this thread when I needed to blend two images in GD. It seems there is no code specifically for that so I will just leave this here for future visitors to this page.
This is a fork from the answer of colivier that supports multiply-blending of two images.
The two images need not be of the same size BUT the overlaying image will be resized and cropped to the size of the bottom layer. I made a fit helper function to do just that but don't bother with that.
imagecolorat returns the base color, even with PNGs with transparency. That is, a 50% black (visible as (128, 128, 128)) will be returned as (0, 0, 0, 64) 64 being the alpha value. This code takes into consideration translucency and converts the translucent colors to the visible color values.
// bottom layer
$img1 = imagecreatefromjpeg(realpath(__DIR__.'/profilePic.jpg'));
// top layer
$img2 = imagecreatefrompng(realpath(__DIR__.'/border2.png'));
imagealphablending($img2, false);
imagesavealpha($img2, true);
$imagex = imagesx($img1);
$imagey = imagesy($img1);
$imagex2 = imagesx($img2);
$imagey2 = imagesy($img2);
// Prereq: Resize img2 to match img1, cropping beyond the aspect ratio
$w1 = max(min($imagex2, $imagex), $imagex);
$h1 = max(min($imagey2, $imagey), $imagey);
$w_using_h1 = round($h1 * $imagex2 / $imagey2);
$h_using_w1 = round($w1 * $imagey2 / $imagex2);
if ($w_using_h1 > $imagex) {
fit($img2, $imagex, $imagey, 'HEIGHT', true);
}
fit($img2, $imagex, $imagey, 'WIDTH', true);
// Actual multiply filter
for ($x = 0; $x < $imagex; ++$x) {
for ($y = 0; $y < $imagey; ++$y) {
$rgb1 = imagecolorat($img1, $x, $y);
$rgb2 = imagecolorat($img2, $x, $y);
$idx1 = imagecolorsforindex($img1, $rgb1);
$idx2 = imagecolorsforindex($img2, $rgb2);
// Shift left 8, then shift right 7
// same as multiply by 256 then divide by 128
// approximate multiply by 255 then divide by 127
// This is basically multiply by 2 but, expanded to show that
// we are adding a fraction of white to the translucent image
// $adder = ($idx2['alpha'] << 8 >> 7);
$adder = ($idx2['alpha'] << 1);
$rmul = min(255, $idx2['red'] + $adder);
$gmul = min(255, $idx2['green'] + $adder);
$bmul = min(255, $idx2['blue'] + $adder);
$color_r = floor($idx1['red'] * $rmul / 255);
$color_g = floor($idx1['green'] * $gmul / 255);
$color_b = floor($idx1['blue'] * $bmul / 255);
$newcol = imagecolorallocatealpha($img1, $color_r, $color_g, $color_b, 0);
imagesetpixel($img1, $x, $y, $newcol);
}
}
imagejpeg($img1, __DIR__.'/out.jpg');
/**
* Fits an image to a $w x $h canvas
*
* #param type $w Target width
* #param type $h Target height
* #param int $fit_which Which dimension to fit
* #param bool $upscale If set to true, will scale a smaller image to fit the given dimensions
* #param bool $padded If set to true, will add padding to achieve given dimensions
*
* #return Image object
*/
function fit(&$img, $w, $h, $fit_which = 'BOTH', $upscale = false, $padded = true) {
if (!in_array($fit_which, array('WIDTH', 'HEIGHT', 'BOTH'))) {
$fit_which = 'BOTH';
}
$w0 = imagesx($img);
$h0 = imagesy($img);
if (!$upscale && $w0 <= $w && $h0 <= $h)
return $this;
if ($padded) {
$w1 = max(min($w0, $w), $w);
$h1 = max(min($h0, $h), $h);
}
else {
$w1 = min($w0, $w);
$h1 = min($h0, $h);
}
$w_using_h1 = round($h1 * $w0 / $h0);
$h_using_w1 = round($w1 * $h0 / $w0);
// Assume width, crop height
if ($fit_which == 'WIDTH') {
$w2 = $w1;
$h2 = $h_using_w1;
}
// Assume height, crop width
elseif ($fit_which == 'HEIGHT') {
$w2 = $w_using_h1;
$h2 = $h1;
}
elseif ($fit_which == 'BOTH') {
if (!$padded) {
$w2 = $w = min($w, $w_using_h1);
$h2 = $h = min($h, $h_using_w1);
}
else {
// Extend vertically
if ($h_using_w1 <= $h) {
$w2 = $w1;
$h2 = $h_using_w1;
}
// Extend horizontally
else {
$w2 = $w_using_h1;
$h2 = $h1;
}
}
}
$im2 = imagecreatetruecolor($w, $h);
imagealphablending($im2, true);
imagesavealpha($im2, true);
$transparent = imagecolorallocatealpha($im2, 255, 255, 255, 127);
imagefill($im2, 0, 0, $transparent);
imagealphablending($img, true);
imagesavealpha($img, true);
// imagefill($im, 0, 0, $transparent);
imagecopyresampled($im2, $img, ($w - $w2) / 2, ($h - $h2) / 2, 0, 0, $w2, $h2, $w0, $h0);
$img = $im2;
}
Have you tried to use php manual?
For people looking to apply a 'multiply' effect on images like the one in Photoshop (generally b&w ones), you can achieve it with the IMG_FILTER_COLORIZE filter.
<?php
function multiplyColor(&$im, $color = array(255, 0, 0)) {
//get opposite color
$opposite = array(255 - $color[0], 255 - $color[1], 255 - $color[2]);
//now we subtract the opposite color from the image
imagefilter($im, IMG_FILTER_COLORIZE, -$opposite[0], -$opposite[1], -$opposite[2]);
}
?>
If used with png image and alpha must be well and works very well
$filter_r=215;
$filter_g=5;
$filter_b=5;
$alpha=70;
$suffixe="_red";
$path="./img/foto_220_590.png";
if(is_file($path)){
$image=imagecreatefrompng($path);
$new_path=substr($path,0,strlen($path)-4).$suffixe.".png";
echo $imagex = imagesx($image);
echo $imagey = imagesy($image);
for ($x = 0; $x <$imagex; ++$x) {
for ($y = 0; $y <$imagey; ++$y) {
$rgb = imagecolorat($image, $x, $y);
$TabColors=imagecolorsforindex ( $image , $rgb );
$color_r=floor($TabColors['red']*$filter_r/255);
$color_g=floor($TabColors['green']*$filter_g/255);
$color_b=floor($TabColors['blue']*$filter_b/255);
//$newcol = imagecolorallocate($image, $color_r,$color_g,$color_b);
// this new alpha
$newcol = imagecolorallocatealpha($image, $color_r,$color_g,$color_b,$alpha);
imagesetpixel($image, $x, $y, $newcol);
}
}
imagepng($image,$new_path);
I updated #colivier script to be able to myltiply two images, and not just an image with a color:
/**
* Multiply $pathToDst and $pathToSrc to $resultPath
*
* #param string $pathToDst
* #param string $pathToSrc
* #param string $resultPath
*/
function multiply($pathToDst, $pathToSrc, $resultPath) {
switch (pathinfo($pathToDst, PATHINFO_EXTENSION)) {
case "gif" :
$resourceDst = imagecreatefromgif($pathToDst);
break;
case "png" :
$resourceDst = imagecreatefrompng($pathToDst);
break;
default :
$resourceDst = imagecreatefromjpeg($pathToDst);
break;
}
switch (pathinfo($pathToSrc, PATHINFO_EXTENSION)) {
case "gif" :
$resourceSrc = imagecreatefromgif($pathToSrc);
break;
case "png" :
$resourceSrc = imagecreatefrompng($pathToSrc);
break;
default :
$resourceSrc = imagecreatefromjpeg($pathToSrc);
break;
}
for ($x = 0; $x < 400; ++$x) {
for ($y = 0; $y < 400; ++$y) {
$TabColorsFlag = imagecolorsforindex($resourceDst, imagecolorat($resourceDst, $x, $y));
$TabColorsPerso = imagecolorsforindex($resourceSrc, imagecolorat($resourceSrc, $x, $y));
$color_r = floor($TabColorsFlag['red'] * $TabColorsPerso['red'] / 255);
$color_g = floor($TabColorsFlag['green'] * $TabColorsPerso['green'] / 255);
$color_b = floor($TabColorsFlag['blue'] * $TabColorsPerso['blue'] / 255);
imagesetpixel($resourceDst, $x, $y, imagecolorallocate($resourceSrc, $color_r, $color_g, $color_b));
}
}
imagepng($resourceDst, $resultPath, 0);
imagedestroy($resourceDst);
imagedestroy($resourceSrc);
}

Performance improvements PHP GD resize and trim image background keeping the correct ratio

I developed an image resizing & trimming class using PHP GD library. I used skibulks image trim script to trim the image background in the first step and scale the image in the second step to the needed sizes (maintaining the original ratio).
Question: Is it really necessary to do the first imagecopy job after getting the new trimmed image sizes from the $this->_trimBackground() function to recreate the image first by imagecopy with the new trimmed sizes (and resize it afterwards again)? Or, is it possible to merge this job with the following resizing part imagecopyresampled?
Are there other possible performance improvements possible which I don't know? Every performance suggestion is welcome!
Function 1:
/**
* Resize image file
*
* #param string $filepath the image filepath
* #param integer $width the width to resize
* #param integer $height the height to resize
* #return (image blob|boolean status)
* #throws Asset_Model_Image_Exception
*/
private function _resizeImageByFilepathAndReturn($filepath, $width, $height) {
list($imageWidth, $imageHeight, $imageType) = getimagesize($filepath);
switch($imageType) {
case IMAGETYPE_GIF:
$gdImage = imagecreatefromgif($filepath);
break;
case IMAGETYPE_JPEG:
$gdImage = imagecreatefromjpeg($filepath);
break;
case IMAGETYPE_PNG:
$gdImage = imagecreatefrompng($filepath);
break;
default:
return false;
}
if($box = $this->_trimBackground($gdImage)) {
$gdTrimmed = imagecreatetruecolor($box['w'], $box['h']);
imagecopy($gdTrimmed, $gdImage, 0, 0, $box['l'], $box['t'], $box['w'], $box['h']);
$imageWidth = $box['w'];
$imageHeight = $box['h'];
$gdImage = $gdTrimmed;
unset($gdTrimmed);
}
if($imageWidth <= $width && $imageHeight <= $height) {
$fwidth = $imageWidth;
$fheight = $imageHeight;
} else {
$wscale = $width / $imageWidth;
$hscale = $height / $imageHeight;
$scale = min($wscale, $hscale);
$fwidth = $scale * $imageWidth;
$fheight = $scale * $imageHeight;
}
$gdThumbnail = imagecreatetruecolor($width, $height);
imagefill($gdThumbnail, 0, 0, 0x00FFFFFF);
imagecopyresampled($gdThumbnail, $gdImage, ($width - $fwidth) / 2, ($height - $fheight) / 2, 0, 0, $fwidth, $fheight, $imageWidth, $imageHeight);
ob_start();
imagejpeg($gdThumbnail, null, 90);
$image = ob_get_contents();
ob_end_clean();
imagedestroy($gdImage);
imagedestroy($gdThumbnail);
return $image;
}
Function 2:
/**
* Trim image background
*
* #param $gdImage image ressource
*/
private function _trimBackground($gdImage){
$hex = imagecolorat($gdImage, 0,0);
$width = imagesx($gdImage);
$height = imagesy($gdImage);
$bTop = 0;
$bLft = 0;
$bBtm = $height - 1;
$bRt = $width - 1;
for(; $bTop < $height; ++$bTop) {
for($x = 0; $x < $width; ++$x) {
if(imagecolorat($gdImage, $x, $bTop) != $hex) {
break 2;
}
}
}
if($bTop == $height) {
return false;
}
for(; $bBtm >= 0; --$bBtm) {
for($x = 0; $x < $width; ++$x) {
if(imagecolorat($gdImage, $x, $bBtm) != $hex) {
break 2;
}
}
}
for(; $bLft < $width; ++$bLft) {
for($y = $bTop; $y <= $bBtm; ++$y) {
if(imagecolorat($gdImage, $bLft, $y) != $hex) {
break 2;
}
}
}
for(; $bRt >= 0; --$bRt) {
for($y = $bTop; $y <= $bBtm; ++$y) {
if(imagecolorat($gdImage, $bRt, $y) != $hex) {
break 2;
}
}
}
$bBtm++;
$bRt++;
return array('l' => $bLft, 't' => $bTop, 'r' => $bRt, 'b' => $bBtm, 'w' => $bRt - $bLft, 'h' => $bBtm - $bTop);
}
imagecopy() copies a section of your $gdImage to $gdTrimmed and a couple of lines later overwrites $gdImage with $gdTrimmed.
Is it really necessary to do the first imagecopy?
That's something you should ask yourself.
Using imagedestroy() function instead of unset() might greatly improve your performance. Here's a useful comment on imagedestroy():
Reusing an image variable does NOT clear the old data out of memory!
You must use imagedestroy() to clear the data out. (I don't know if
unset() works as well).
Also note that the image data in memory is raw, so don't base how much
memory you are using based on the original filesize of the compressed
image (such as jpeg or png).

Categories