PHP - Make image backgrounds transparent with tolerance - php

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]);

Related

How I can apply color threshold into an image created from `imagecreatefromstring`?

I have the following piece of code:
define(RED_THESHOLD,100);
define(GREEN_THESHOLD,200);
define(BLUE_THESHOLD,100);
function thresholdImage(String $imgdata){
$original_limit = ini_get('memory_limit');
ini_set('memory_limit', '-1');
$imageResource = imagecreatefromstring($imgData);
// Limit red green and blue color channels here
}
But I do not know how I can apply the color the constants:
RED_THESHOLD
GREEN_THESHOLD
BLUE_THESHOLD
According to the classic algorithms I need to read pixel by pixel each channel and apply the threshold by the following piece of code (I use images red channel as an example):
$new_pixel_value = ($red_pixel_value>RED_THESHOLD)?RED_THESHOLD:$red_pixel_value;
Do you know how I can do this?
This can be done by finding the color index for each pixel, converting that into RGBA, then constraining those values, converting it back into a color index and setting the pixel.
<?php
const RED_THESHOLD = 255;
const GREEN_THESHOLD = 10;
const BLUE_THESHOLD = 10;
$image = imagecreatefrompng('test.png');
$maxX = imagesx($image);
$maxY = imagesy($image);
for ($y = 0; $y < $maxY; ++$y) {
for ($x = 0; $x < $maxX; ++$x) {
$existing = imagecolorsforindex($image, imagecolorat($image, $x, $y));
$red = ($existing['red'] > RED_THESHOLD) ? RED_THESHOLD : $existing['red'];
$green = ($existing['green'] > GREEN_THESHOLD) ? GREEN_THESHOLD : $existing['green'];
$blue = ($existing['blue'] > BLUE_THESHOLD) ? BLUE_THESHOLD : $existing['blue'];
$new = imagecolorexact($image, $red, $green, $blue);
imagesetpixel($image, $x, $y, $new);
}
}
imagepng($image, 'test2.png');
Here is a comparison picture:

How can I have a nice radial transparent gradient applied to an image with PHP?

How can one specify an image and apply a radial transparent gradient where it fades out radially. I do not have Imagemagick installed.
Marginal example:
Introduction
I think you should get Imagemagick installed because what you want is a simple vignette effect, You can easily so that with ImageMagic (convert input.jpg -background black -vignette 70x80 output.png) without having to loop every pixel which can be very slow when dealing with large images
Original Image
$file = __DIR__ . "/golf.jpg";
Effect 1
$image = new imagick($file);
$image->vignetteImage(20, 20, 40, - 20);
header("Content-Type: image/png");
echo $image;
Effect 2
$image = new imagick($file);
$image->vignetteImage(100, 100, 200, 200);
header("Content-Type: image/png");
echo $image;
vignette with GD
Well if you are forced to use GB ... Use can use this cool vignette script
function vignette($im) {
$width = imagesx($im);
$height = imagesy($im);
$effect = function ($x, $y, &$rgb) use($width, $height) {
$sharp = 0.4; // 0 - 10 small is sharpnes,
$level = 0.7; // 0 - 1 small is brighter
$l = sin(M_PI / $width * $x) * sin(M_PI / $height * $y);
$l = pow($l, $sharp);
$l = 1 - $level * (1 - $l);
$rgb['red'] *= $l;
$rgb['green'] *= $l;
$rgb['blue'] *= $l;
};
for($x = 0; $x < imagesx($im); ++ $x) {
for($y = 0; $y < imagesy($im); ++ $y) {
$index = imagecolorat($im, $x, $y);
$rgb = imagecolorsforindex($im, $index);
$effect($x, $y, $rgb);
$color = imagecolorallocate($im, $rgb['red'], $rgb['green'], $rgb['blue']);
imagesetpixel($im, $x, $y, $color);
}
}
return (true);
}
Faster GD vignette approach
A better approached used in GD Filter testing would be ... to create a mask and over lay
$overlay = 'vignette_white.png';
$png = imagecreatefrompng($overlay);
imagecopyresampled($filter, $png, 0, 0, 0, 0, $width, $height, $width, $height);
Full Code
Cool Demos Of the filter combination
The only disadvantage is that The image must be the same size with the mask for the effect to look cool
Conclusion
If this is what you mean by radial transparent gradient then i advice you to get ImageMagic if not at least the lady the picture is cute.
Thanks to the function linked by #Baba I was able to alter the script to allow for semi transparent vignette effect.
<?php
class PhotoEffect
{
private $_photoLocation;
private $_width;
private $_height;
private $_type;
private $_originalImage;
private $_afterImage;
/**
* Load image URL in constructor
*/
final public function __construct($photoLocation)
{
$this->_photoLocation = $photoLocation;
if (!$size = #getimagesize($this->_photoLocation)){
throw new Exception('Image cannot be handled');
}
$this->_width = $size[0];
$this->_height = $size[1];
$this->_type = $size[2];
switch ( $this->_type ) {
case IMAGETYPE_GIF:
$this->_originalImage = imagecreatefromgif($this->_photoLocation);
break;
case IMAGETYPE_JPEG:
$this->_originalImage = imagecreatefromjpeg($this->_photoLocation);
break;
case IMAGETYPE_PNG:
$this->_originalImage = imagecreatefrompng($this->_photoLocation);
break;
default:
throw new Exception('Unknown image type');
}
}
/**
* Destroy created images
*/
final private function __destruct() {
if (!empty($this->_originalImage))
{
imagedestroy($this->_originalImage);
}
if (!empty($this->_afterImage))
{
imagedestroy($this->_afterImage);
}
}
/**
* Apply vignette effect
*/
final public function Vignette($sharp=0.4, $level=1, $alpha=1)
{
if (empty($this->_originalImage))
{
throw new Exception('No image');
}
if (!is_numeric($sharp) || !($sharp>=0 && $sharp<=10))
{
throw new Exception('sharp must be between 0 and 10');
}
if (!is_numeric($level) || !($level>=0 && $level<=1))
{
throw new Exception('level must be between 0 and 10');
}
if (!is_numeric($alpha) || !($alpha>=0 && $alpha<=10))
{
throw new Exception('alpha must be between 0 and 1');
}
$this->_afterImage = imagecreatetruecolor($this->_width, $this->_height);
imagesavealpha($this->_afterImage, true);
$trans_colour = imagecolorallocatealpha($this->_afterImage, 0, 0, 0, 127);
imagefill($this->_afterImage, 0, 0, $trans_colour);
for($x = 0; $x < $this->_width; ++$x){
for($y = 0; $y < $this->_height; ++$y){
$index = imagecolorat($this->_originalImage, $x, $y);
$rgb = imagecolorsforindex($this->_originalImage, $index);
$l = sin(M_PI / $this->_width * $x) * sin(M_PI / $this->_height * $y);
$l = pow($l, $sharp);
$l = 1 - $level * (1 - $l);
$rgb['red'] *= $l;
$rgb['green'] *= $l;
$rgb['blue'] *= $l;
$rgb['alpha'] = 127 - (127 * ($l*$alpha));
$color = imagecolorallocatealpha($this->_afterImage, $rgb['red'], $rgb['green'], $rgb['blue'], $rgb['alpha']);
imagesetpixel($this->_afterImage, $x, $y, $color);
}
}
}
/**
* Ouput PNG with correct header
*/
final public function OutputPng()
{
if (empty($this->_afterImage))
{
if (empty($this->_originalImage))
{
throw new Exception('No image');
}
$this->_afterImage = $this->_originalImage;
}
header('Content-type: image/png');
imagepng($this->_afterImage);
}
/**
* Save PNG
*/
final public function SavePng($filename)
{
if (empty($filename)) {
throw new Exception('Filename is required');
}
if (empty($this->_afterImage))
{
if (empty($this->_originalImage))
{
throw new Exception('No image');
}
$this->_afterImage = $this->_originalImage;
}
imagepng($this->_afterImage, $filename);
}
}
/**
* How to use
*/
$effect = new PhotoEffect('test.jpg');
$effect->Vignette();
$effect->OutputPng();
?>
Working phpfiddle with the only image I could find on their server, so not that big.
I am aware that this is a PHP related question, but you can achieve nice transparent gradients with use of javascript and html5 canvas element.
So I wrote this small script that:
detects browsers that support canvas element, if browser doesn't support canvas (fortunately only a few percent of users are left) then full image is displayed.
creates canvas and appends element after image
arguments in the create_gradient() function can be changed for custom shapes
it works with all popular image formats ( tested with .jpg, .bmp, .gif, .png )
you can add more ‘colorstops’ ( grd.addColorStop() ) to change the flow of the gradient
script
window.onload = function() {
if ( typeof CanvasRenderingContext2D !== 'function' ) {
document.getElementById('gradient-image').style.visibility = "visible";
return;
}
var image = document.getElementById('gradient-image');
// these are the default values, change them for custom shapes
create_gradient( image, image.width/2, image.height/2, image.height/4, image.width/2, image.height/2, image.height/2 );
}
function create_gradient( image, start_x, start_y, start_r, end_x, end_y, end_r ){
var canvas = document.createElement('canvas');
var parent = image.parentNode;
if ( parent.lastchild == image ) parent.appendChild(canvas);
else parent.insertBefore(canvas, image.nextSibling);
canvas.width = image.width;
canvas.height = image.height;
var context = canvas.getContext('2d');
var grd = context.createRadialGradient( start_x, start_y, start_r, end_x, end_y, end_r );
grd.addColorStop(0, 'rgba(0,0,0,1)' );
// grd.addColorStop(0.2, 'rgba(0,0,0,0.8)' );
grd.addColorStop(1, 'rgba(0,0,0,0)' );
context.fillStyle = grd;
context.fillRect(0, 0, image.width, image.height);
var grd_data = context.getImageData(0, 0, image.width, image.height);
context.drawImage( image, 0, 0);
var img_data = context.getImageData(0, 0, image.width, image.height);
var grd_pixel = grd_data.data;
var img_pixel = img_data.data;
var length = img_data.data.length
for ( i = 3; i < length; i += 4 ) {
img_pixel[i] = grd_pixel[i];
}
context.putImageData(img_data, 0, 0);
}
html
<img id="gradient-image"src="">
css
#gradient-image {
position: absolute;
visibility: hidden;
}
There is also a vignette filter available in this php library, which only uses GD:
https://github.com/elgervb/imagemanipulation

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);
}

Knowing if an image has transparency? [duplicate]

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

Categories