Check if image is white; if so make transparent - php

I have an image, and I want to make it transparent if it's completely white. I have the following code already for GD for getting a part of the image:
$srcp = imagecreatefrompng("../images/".$_GET['b'].".png");
$destp = imagecreate(150, 150);
imagecopyresampled($destp, $srcp, 0, 0, 40, 8, 150, 150, 8, 8);
header('Content-type: image/png');
imagepng($destp);
But how can I have it first check if the image is completely white, if so change it to transparent, and apply that to $destp?

EDIT:
Based on re-reading the question, and the discussion below, I believe this is what you're looking for:
$width = 150;
$height = 150;
$srcp = imagecreatefrompng("../images/".$_GET['b'].".png");
$destp = imagecreatetruecolor(150, 150);
$white = 0;
for ($y = 0; $y < $height; $y++)
{
for ($x = 0; $x < $width; $x++)
{
$currentColor = imagecolorat($srcp, $x, $y);
$colorParts = imagecolorsforindex($srcp, $currentColor);
if (($colorParts['red'] == 255 &&
$colorParts['green'] == 255 &&
$colorParts['blue'] == 255))
{
$white++;
}
}
}
if ($white == ($width * $height))
{
imagecopyresampled($destp, $srcp, 0, 0, 40, 8, 150, 150, 8, 8);
}
else
{
imagesavealpha($destp, true);
imagefill($destp, 0, 0, imagecolorallocatealpha($destp, 0, 0, 0, 127));
}
header('Content-type: image/png');
imagepng($destp);
This produces a blank image if the original image's 8x8 slice is all white, otherwise it resamples the 8x8 slice to 150x150.
Original:
I haven't ever done anything with PHP GD before, and thought it would be a fun challenge. Here's how I ended up making this happen:
$filename = 'test.png'; // input image
$image = imagecreatefrompng($filename);
// grab the input image's size
$size = getimagesize($filename);
$width = $size[0];
$height = $size[1];
$newImage = imagecreatetruecolor($width, $height);
// make sure that the image will retain alpha when saved
imagesavealpha($newImage, true);
// fill with transparent pixels first or else you'll
// get black instead of transparent
imagefill($newImage, 0, 0, imagecolorallocatealpha($newImage, 0, 0, 0, 127));
// loop through all the pixels
for ($y = 0; $y < $height; $y++)
{
for ($x = 0; $x < $width; $x++)
{
// get the current pixel's colour
$currentColor = imagecolorat($image, $x, $y);
// then break it into easily parsed bits
$colorParts = imagecolorsforindex($image, $currentColor);
// if it's NOT white
if (!($colorParts['red'] == 255 &&
$colorParts['green'] == 255 &&
$colorParts['blue'] == 255))
{
// then keep the same colour
imagesetpixel($newImage, $x, $y, $currentColor);
}
}
}
// final output, the second arg is the filename
imagepng($newImage, 'newImage.png');
It allowed me to turn this:
Into this (hard to see the alpha here, but you can open it to see):

A simple strightforward solution would be to use imagecolorat and iterate through all the pixels of the png and if all are white change it to transparent.
Hope this helps.

After a quick look through the GD manual pages, I think that the following should work for you (it did on my test server, anyway):
<?php
// Create image instance
$im = imagecreatefromgif('test.gif');
if (imagecolorstotal($im) == 1) {
$rgb = imagecolorat($im, 1, 1); // feel free to test any pixel, but if they're all
// the same colour, then it shouldn't really matter
$colors = imagecolorsforindex($im, $rgb);
if ($colors['red'] == 255 && $colors['green'] == 255 && $colors['blue'] == 255) {
// I'm ignoring $colors['alpha'] but that's optional.
echo "Yup; it's white...";
}
} else {
// if you've got more than one colour then the image has to be at least
// two colours, so not really worth checking if any pixels are white
}
// Free image
imagedestroy($im);
?>
References:
imagecolorat().
imagecolorstotal().

Related

Generate pixels from image template in php not working :(

I tried this code to generate pixels around a black and white template, but it only gives me scrambled machine code.
Can anyone tell my how to fix it, or why exactly this happens?
https://nowat.org/test44.php
<?php
// Load the image of Nelson Mandela
$original = imagecreatefromjpeg('https://www.bigissue.org.za/wp-content/uploads/2016/07/Nelson-Mandela_AFP-Photo-2.jpg');
// Get the dimensions of the original image
$width = imagesx($original);
$height = imagesy($original);
// Create a blank image with the same dimensions
$image = imagecreatetruecolor($width, $height);
// Generate random pixels
for ($x = 0; $x < $width; $x++) {
for ($y = 0; $y < $height; $y++) {
// Get the color of the current pixel from the original image
$rgb = imagecolorat($original, $x, $y);
$colors = imagecolorsforindex($original, $rgb);
// Generate a random color variation
$red = rand($colors['red'] - 50, $colors['red'] + 50);
$green = rand($colors['green'] - 50, $colors['green'] + 50);
$blue = rand($colors['blue'] - 50, $colors['blue'] + 50);
$color = imagecolorallocate($image, $red, $green, $blue);
// Set the pixel color
imagesetpixel($image, $x, $y, $color);
}
}
// Output the image as a PNG
header('Content-Type: image/png');
imagepng($image);
// Clean up
imagedestroy($image);
?>
Any Help appreciated!
Your random color are not correct. Color must be between 0 and 255.
...
var_dump($red, $blue, $green);
...
int(261) <--- HERE
PHP Fatal error: Uncaught ValueError: imagecolorallocate(): Argument #2 ($red) must be between 0 and 255 (inclusive) in test.php:24
Stack trace:
#0 test.php(24): imagecolorallocate()
#1 {main}
thrown in test.php on line 24
Try this:
$red = abs(rand($colors['red'] - 50, $colors['red'] + 50) % 255);
$green = abs(rand($colors['green'] - 50, $colors['green'] + 50) % 255);
$blue = abs(rand($colors['blue'] - 50, $colors['blue'] + 50) % 255);
Result here

Watermarking: Display the same text on multiple parts of the image

I want to add a text to a image. The text should be displayed in multiple areas of the image (not just one).
For example I want to watermark with a text stack. Stack should be displayed in the image at least 8 times in different areas in the image.
I just learned about imagestring() and imagettftext(), but these two only displays my text on a single spot.
Image is not fixed size, so i cannot specify exact and multiple location in advance. It should work on all sizes of images
<?php
/*
image.php
*/
header("Content-type: image/jpeg");
$imgPath = 'olximage.jpg';
$image = imagecreatefromjpeg($imgPath);
$color = imagecolorallocate($image, 255, 255, 255);
$string = "stack overflow";
$fontSize = 3;
$x = 15;
$y = 185;
imagestring($image, $fontSize, $x, $y, $string, $color);
$x = 15;
$y = 175;
imagestring($image, $fontSize, $x, $y, $string, $color);
imagejpeg($image);
?>
Thanks in advance
For example:
<?php
/*
image.php
*/
header("Content-type: image/jpeg");
$imgPath = 'olximage.jpg';
$image = imagecreatefromjpeg($imgPath);
$color = imagecolorallocate($image, 255, 255, 255);
$string = "stack overflow";
$fontSize = 3;
$imageHeight = imagesy($image);
$distanceY = 10;
$maxImageStrings = max(8, $imageHeight / $distanceY);
$x = 15;
for ($i = 0; $i < $maxImageStrings; $i++) {
$y = $i * $distanceY;
imagestring($image, $fontSize, $x, $y, $string, $color);
}
imagejpeg($image);
You can finetune calculations for your needs.
I'm using Imagick extension for same. If you want to go with this then follow detail:
PHP:
// Create objects
$image = new Imagick('image.png');
$watermark = new Imagick();
// Watermark text
$text = 'Copyright';
// Create a new drawing palette
$draw = new ImagickDraw();
$watermark->newImage(140, 80, new ImagickPixel('none'));
// Set font properties
$draw->setFont('Arial');
$draw->setFillColor('grey');
$draw->setFillOpacity(.5);
// Position text at the top left of the watermark
$draw->setGravity(Imagick::GRAVITY_NORTHWEST);
// Draw text on the watermark
$watermark->annotateImage($draw, 10, 10, 0, $text);
// Position text at the bottom right of the watermark
$draw->setGravity(Imagick::GRAVITY_SOUTHEAST);
// Draw text on the watermark
$watermark->annotateImage($draw, 5, 15, 0, $text);
// Repeatedly overlay watermark on image
for ($w = 0; $w < $image->getImageWidth(); $w += 140) {
for ($h = 0; $h < $image->getImageHeight(); $h += 80) {
$image->compositeImage($watermark, Imagick::COMPOSITE_OVER, $w, $h);
}
}
// Set output image format
$image->setImageFormat('png');
// Output the new image
header('Content-type: image/png');
echo $image;
although there are plenty of command-line examples to be found on the ImageMagick website, so that is where we shall begin.

Change color of non-transparent parts of png in PHP

I want to fill the png non transparent part with any color or image using php.
Following is the base image
Following is the target image
I have used following code of php to fill non transparent part of the png.
$im = imagecreatefrompng(dirname(__FILE__) . '/images/cat_1.png');
$red = imagecolorallocate($im, 255, 0, 0);
imagefill($im, 0, 0, $red);
header('Content-type: image/png');
imagepng($im);
imagedestroy($im);
But it gives me following output.
please help me to complete my task.
Thanks in advance.
Save this version of the base image:
This image has been saved as an indexed-PNG format, making it perfect for colour substitution. In this case, index 0 is the cat's colour, and index 1 is the background (not ideal, but that's what GIMP gave me)
In this case:
$img = imagecreatefrompng("cat_1.png");
imagecolorset($img,0, 255,0,0);
imagepng($img); // output red cat
Image editing in general is made much easier if you have a base image that lends itself to easy editing in this way ;)
This line imagefill($im, 0, 0, $red); fills the image with the color red, at the coordinate (0,0), which is topleft. So it starts filling at the top left and fills everything like when you use the paintbucket in MSPaint. You could use imagefill($im, 150, 150, $red); for example (if 150,150 is the center).
use this function, it returns a base64 image <img src="output">
public static function colorImage($url, $hex = null, $r = null, $g = null, $b = null)
{
if ($hex != null) {
$hex = str_replace("#", "", $hex);
$r = hexdec(substr($hex, 0, 2));
$g = hexdec(substr($hex, 2, 2));
$b = hexdec(substr($hex, 4, 2));
}
$im = imagecreatefrompng($url);
imageAlphaBlending($im, true);
imageSaveAlpha($im, true);
if (imageistruecolor($im)) {
$sx = imagesx($im);
$sy = imagesy($im);
for ($x = 0; $x < $sx; $x++) {
for ($y = 0; $y < $sy; $y++) {
$c = imagecolorat($im, $x, $y);
$a = $c & 0xFF000000;
$newColor = $a | $r << 16 | $g << 8 | $b;
imagesetpixel($im, $x, $y, $newColor);
}
}
}
ob_start();
imagepng($im);
imagedestroy($im);
$image_data = ob_get_contents();
ob_end_clean();
$image_data_base64 = "data:image/png;base64," . base64_encode($image_data);
return $image_data_base64;
}

Replace transparent color in PHP

I have set of images. In this images exist green alpha color. I need replace that transparent color to another color (i want replace to white alpha color).
Its my code:
$img = imagecreatefrompng($path . $file);
$white_color_transparent = imagecolorallocatealpha($img, 255, 255, 255, 127);
for ($y = 0; $y < imagesy($img); $y++) {
for ($x = 0; $x < imagesx($img); $x++) {
$rgb = imagecolorat($img, $x, $y);
$pixel_color = imagecolorsforindex($img, $rgb);
if ($pixel_color['alpha'] != 0 && $pixel_color['alpha'] != 127){
imagesetpixel($img, $x, $y, $white_color_transparent);
}
}
}
imagealphablending($img, false);
imagesavealpha($img, true);
imagepng($img, $path . $file);
And my result color its the same than id source image, when i add before
imagesetpixel($img, $x, $y, $white_color_transparent);
that line:
imagesetpixel($img, $x, $y, $white_color);
i get only white color without transparency.
Without seeing your image, I'm not sure there's an easy way to do it.
EDIT 2:
One thing I noticed from my first edit is that it didn't work for an image with transparency and had no opaque layer behind it.
Here is my attempt to deal with an image that has some transparency. As you can see by the example. It didn't work out perfectly.
<?php
//These colors here are the rgba values of the green transparency I used in my test image. You will need to know this to reverse the rgb blending.
$red = 28;
$green = 198;
$blue = 72;
$alpha = .48;
$img = imagecreatefrompng('test.png');
//Adding in the image blending that Zombaya brought up. Need this so it overwrites the pixel instead of blending it
imagealphablending($img, false);
for ($y = 0; $y < imagesy($img); $y++) {
for ($x = 0; $x < imagesx($img); $x++) {
$rgb = imagecolorat($img, $x, $y);
$pixel_color = imagecolorsforindex($img, $rgb);
//If pixel color matches our alpha layer color, we change it white transparent
//Not sure how accurate the rounding for the alpha conversion is. Maybe the reason for the green edges in this example:
if($pixel_color['red'] == $red &&
$pixel_color['green'] == $green &&
$pixel_color['blue'] == $blue &&
$pixel_color['alpha'] == round(127 - ($alpha * 127))){
$color = imagecolorallocatealpha($img, 255, 255, 255, 127);
} else {
//This is the algorithm from the link. But reordered to get the old color before the transparent color went over it.
$oldR = ($pixel_color['red'] - $alpha * $red) / (1 - $alpha);
$oldG = ($pixel_color['green'] - $alpha * $green ) / (1 - $alpha);
$oldB = ($pixel_color['blue'] - $alpha * $blue) / (1 - $alpha);
$color = imagecolorallocatealpha($img, $oldR, $oldG, $oldB, $pixel_color['alpha']);
}
imagesetpixel($img, $x, $y, $color);
}
}
imagesavealpha($img, true);
imagepng($img, 'test_result.png');
-> Original without transparency
-> Starting image with transparency
-> Resulting image. Notice the green edges. And I may have got lucky with using similar opacity for the red and green. The white part is transparent
EDIT:
I thought about this some more and I came across this link: How to calculate an RGB colour by specifying an alpha blending amount?
This helped me understand how to fix your problem. Although this is dependent on if you have just a solid color that is transparent over the image. If it is a gradient it is more difficult.
<?php
//These colors here are the rgba values of the green transparency I used in my test image. You will need to know this to reverse the rgb blending.
$red = 44;
$green = 215;
$blue = 56;
$alpha = .45;
$img = imagecreatefrompng('test2.png');
for ($y = 0; $y < imagesy($img); $y++) {
for ($x = 0; $x < imagesx($img); $x++) {
$rgb = imagecolorat($img, $x, $y);
$pixel_color = imagecolorsforindex($img, $rgb);
//This is the algorithm from the link. But reordered to get the old color before the transparent color went over it.
$oldR = ($pixel_color['red'] - $alpha * $red) / (1 - $alpha);
$oldG = ($pixel_color['green'] - $alpha * $green ) / (1 - $alpha);
$oldB = ($pixel_color['blue'] - $alpha * $blue) / (1 - $alpha);
$color = imagecolorallocate($img, $oldR, $oldG, $oldB);
imagesetpixel($img, $x, $y, $color);
}
}
imagesavealpha($img, true);
imagepng($img, 'test_result.png');
- Starting Image
- After Image
Here is an explanation of the rgb calculation used in the code above. The link shows the basic formula:
out = alpha * new + (1 - alpha) * old
out is the resulting color after mixing the original with the new color.
So we need to rearrange the formula to get the old color, which yields this:
old = (out - alpha * new) / (1 - alpha)
OLD ANSWER:
Why this line doesn't work:
$white_color_transparent = imagecolorallocatealpha($img, 255, 255, 255, 127);
127 means fully transparent, so that means it is invisible. Which is why the result image is the same as the starting image.
Why this line creates a fully white image:
$white_color = imagecolorallocatealpha($img, 255, 255, 255, 0);
or
$white_color = imagecolorallocate($img, 255, 255, 255);
This basically just places a white pixel over the selected pixel (0 means no transparency), which is why the selected pixels become fully white.
Now if you do the following:
$white_color_semi_transparent = imagecolorallocatealpha($img, 255, 255, 255, 40);
Instead of replace the transparent green with a transparent white, you know get a lighter green instead of a semi transparent white.
See my test example:
- Starting Image
- Resulting Image with color 255,255,255,40
So this tells us two things:
First is that colors are basically being applied on top of the existing pixel's color, hence the lighter transparent green instead of a transparent white.
Second, notice that the brown part is not affected. The brown part was actually an opaque red color beneath the green opacity layer. What this says is that the image doesn't take into account layers like photoshop and basically reads the brown color as brown and not a red color behind a green transparent color. And that makes sense since PNG files don't come with layer info.
I'm not sure how you'd solve your issue. It would depend on your image. I think it's possible to get the coloring you want, but it obviously the more complex your image is the more difficult it will be. For example, my two overlapped squares with two colors versus say a photo.
I used Gohn67's image as something to work width.
The trick is to turn off alpha blending before trying to transform your image. This way you can use imagesetpixel() to set the pixel to the exact value you want.
As from the manual on imagealphablending:
In non-blending mode, the drawing color is copied literally with its
alpha channel information, replacing the destination pixel.
This resulted in this code:
$img = imagecreatefrompng($path.$file);
imagealphablending($img, false); // Turn off blending
$white_color_transparent = imagecolorallocatealpha($img, 255, 255, 255, 127);
for ($y = 0; $y < imagesy($img); $y++) {
for ($x = 0; $x < imagesx($img); $x++) {
$rgb = imagecolorat($img, $x, $y);
$pixel_color = imagecolorsforindex($img, $rgb);
if ($pixel_color['alpha'] != 0 && $pixel_color['alpha'] != 127){
imagesetpixel($img, $x, $y, $white_color_transparent);
}
}
}
imagesavealpha($img, true);
imagepng($img, $path.$file);
Original image
Resulting image
(The background is actually transparent, but impossible to see here)
I would like to say that right now, it replaces all transparent colors with this totally transparent white. If you would prefer to replace all colored transparency with a transparent white with the same alpha-value, you would do something like this:
imagesetpixel($img, $x, $y, imagecolorallocatealpha($img, 255, 255, 255, $pixel_color['alpha']));
And to select only the green transparent pixels, I would calculate the hue of each pixel and check if it falls in a specified range.

Can I swap colors in image using GD library in PHP?

I got the image like this (it's a graph):
(source: kitconet.com)
I want to change the colours, so the white is black, the graph line is light blue, etc.. is it possible to achieve with GD and PHP?
This will replace the white color with Gray
$imgname = "test.gif";
$im = imagecreatefromgif ($imgname);
$index = imagecolorclosest ( $im, 255,255,255 ); // get White COlor
imagecolorset($im,$index,92,92,92); // SET NEW COLOR
$imgname = "result.gif";
imagegif($im, $imgname ); // save image as gif
imagedestroy($im);
I had trouble making this solution work. The image cannot be a true color image. Convert it first with imagetruecolortopalette();
$imgname = "test.gif";
$im = imagecreatefromgif ($imgname);
imagetruecolortopalette($im,false, 255);
$index = imagecolorclosest ( $im, 255,255,255 ); // get White COlor
imagecolorset($im,$index,92,92,92); // SET NEW COLOR
$imgname = "result.gif";
imagegif($im, $imgname ); // save image as gif
imagedestroy($im);
I know this is late and after the fact, but I've put together a script that will do this on a slightly larger scale. Hopefully someone who comes across this post can use it. It takes an number of source images that are one-color layers (your choice). You provide it with a source color and the tint of each layer and the script returns a composite image (with full transparency) colored specifically to your provided hex code.
Check out the code below. A more detailed explanation can be found on my blog.
function hexLighter($hex, $factor = 30) {
$new_hex = '';
$base['R'] = hexdec($hex{0}.$hex{1});
$base['G'] = hexdec($hex{2}.$hex{3});
$base['B'] = hexdec($hex{4}.$hex{5});
foreach ($base as $k => $v) {
$amount = 255 - $v;
$amount = $amount / 100;
$amount = round($amount * $factor);
$new_decimal = $v + $amount;
$new_hex_component = dechex($new_decimal);
$new_hex .= sprintf('%02.2s', $new_hex_component);
}
return $new_hex;
}
// Sanitize/Validate provided color variable
if (!isset($_GET['color']) || strlen($_GET['color']) != 6) {
header($_SERVER['SERVER_PROTOCOL'] . ' 400 Bad Request', true, 400);
exit(0);
}
if (file_exists( "cache/{$_GET['color']}.png" )) {
header( 'Content-Type: image/png' );
readfile( "cache/{$_GET['color']}.png" );
exit(0);
}
// Desired final size of image
$n_width = 50;
$n_height = 50;
// Actual size of source images
$width = 125;
$height = 125;
$image = imagecreatetruecolor($width, $height);
imagesavealpha($image, true);
imagealphablending($image, false);
$n_image = imagecreatetruecolor($n_width, $n_height);
imagesavealpha($n_image, true);
imagealphablending($n_image, false);
$black = imagecolorallocate($image, 0, 0, 0);
$transparent = imagecolorallocatealpha($image, 255, 255, 255, 127);
imagefilledrectangle($image, 0, 0, $width, $height, $transparent);
$layers = array();
$layers_processed = array();
$layers[] = array( 'src' => 'layer01.gif', 'level' => 0 ); // Border
$layers[] = array( 'src' => 'layer02.gif', 'level' => 35 ); // Background
$layers[] = array( 'src' => 'layer03.gif', 'level' => 100 ); // White Quotes
foreach ($layers as $idx => $layer) {
$img = imagecreatefromgif( $layer['src'] );
$processed = imagecreatetruecolor($width, $height);
imagesavealpha($processed, true);
imagealphablending($processed, false);
imagefilledrectangle($processed, 0, 0, $width, $height, $transparent);
$color = hexLighter( $_GET['color'], $layer['level'] );
$color = imagecolorallocate($image,
hexdec( $color{0} . $color{1} ),
hexdec( $color{2} . $color{3} ),
hexdec( $color{4} . $color{5} )
);
for ($x = 0; $x < $width; $x++)
for ($y = 0; $y < $height; $y++)
if ($black === imagecolorat($img, $x, $y))
imagesetpixel($processed, $x, $y, $color);
imagecolortransparent($processed, $transparent);
imagealphablending($processed, true);
array_push($layers_processed, $processed);
imagedestroy( $img );
}
foreach ($layers_processed as $processed) {
imagecopymerge($image, $processed, 0, 0, 0, 0, $width, $height, 100);
imagedestroy( $processed );
}
imagealphablending($image, true);
imagecopyresampled($n_image, $image, 0, 0, 0, 0, $n_width, $n_height, $width, $height);
imagealphablending($n_image, true);
header( 'Content-Type: image/png' );
imagepng( $n_image, "cache/{$_GET['color']}.png" );
imagepng( $n_image );
// Free up memory
imagedestroy( $n_image );
imagedestroy( $image );
I haven't tried it myself but you can look at the function imagecolorset() in the GD library It does a color fill like effect, that could help with the white background.
You could try the imagefilter function http://lv.php.net/imagefilter - but that will not give your direct access to replace one color with another, just changing the r/g/b components.
A very low level solution could be implemented using imagesetpixel http://nl2.php.net/imagesetpixel to set the new pixel values.
IMG_FILTER_NEGATE: Reverses all colors of the image.
http://www.php.net/manual/en/function.imagefilter.php
Could that be a solution?
The slow but sure approach, iterating over every pixel.
function ReplaceColour($img, $r1, $g1, $b1, $r2, $g2, $b2)
{
if(!imageistruecolor($img))
imagepalettetotruecolor($img);
$col1 = (($r1 & 0xFF) << 16) + (($g1 & 0xFF) << 8) + ($b1 & 0xFF);
$col2 = (($r2 & 0xFF) << 16) + (($g2 & 0xFF) << 8) + ($b2 & 0xFF);
$width = imagesx($img);
$height = imagesy($img);
for($x=0; $x < $width; $x++)
for($y=0; $y < $height; $y++)
{
$colrgb = imagecolorat($img, $x, $y);
if($col1 !== $colrgb)
continue;
imagesetpixel ($img, $x , $y , $col2);
}
}
I'm not aware of any ready-made functions. But I suppose you could go trough every pixel of the image and change it's colour...

Categories