While working on the images in PHP, i noticed a strange behavior, not sure why.
I read a jpeg image, then create a new image from it. Then I read the new image. But, displaying the color values give different results. (Though, images look same.)
Here is my code:
$img = imagecreatefromjpeg('test2.jpg');
imagejpeg($img, 'test100.jpg');
$new= imagecreatefromjpeg('test100.jpg');
list($width,$height) = getimagesize('test100.jpg');
for($x=0; $x<$width; $x++){
for($y=0; $y<$height; $y++){
echo "At [$x][$y] --".imagecolorat($img,$x,$y)."--".imagecolorat($new,$x,$y)."<br>";
}
}
I get the following output:
At [0][0] --14732734--14667454
At [0][1] --14601148--14666941
At [0][2] --13810862--13810862
At [0][3] --14271156--14139570
At [0][4] --15717832--15652039
At [0][5] --14598838--14401972
At [0][6] --11440262--11309188
And so on...
I'm not getting the why the values are getting changed, though images look similar.
Please help.
Thank you
JPEG is an image file format with lossy compression. That means you will loose data each time you save an image.
That you see different color values per pixel means nothing different than data-loss. Instead of opening and re-saving, just copy the jpeg file with the file-system functions
$img = imagecreatefromjpeg('test2.jpg');
copy('test2.jpg', 'test100.jpg'); ## <-- copy image _file_
$new= imagecreatefromjpeg('test100.jpg');
you will then get the identical results:
At [0][0] --855561--855561
At [0][1] --855561--855561
At [0][2] --921353--921353
At [0][3] --987144--987144
At [0][4] --1052937--1052937
At [0][5] --1250570--1250570
At [0][6] --1579535--1579535
The quality of the image saved to test100.jpg would be default at 75% if no value is specified when saving... use the following to save 100% quality.
imagejpeg($img, 'test100.jpg', 100);
Related
So since animated GIFs are a series of GIFs concatenated together with "\x00\x21\xF9\x04", I am able to explode the GIF and implode it to take it apart and build it again. However I can't seem to get GD to create an image from the data.
Is there something I need to append in order to have GD recognize the data?
$im = file_get_contents('test.gif'); //get the data for the file
$imgarray = explode("\x00\x21\xF9\x04", $im); //split up the frames
foreach ($imgarray as $frame) {
$img[] = imagecreatefromstring($frame); //this is the line that fails
}
$new_gif = implode("\x00\x21\xF9\x04", $img); //this should work but imagecreatefromstring fails
$new_gif = implode("\x00\x21\xF9\x04", $imgarray); (Does work as it just puts the image back together)
A GIF does not contain just separate images appended after each other. A frame in a GIF may change just a part of the image - it does not have to cover the whole frame. It can also contain a local palette, but otherwise it relies on the global palette of the image - which is stored for the file itself and not just the frame.
I.e. - you can't just explode the file and decode each segment separately and except to get useful images from GD.
You'll at least have to add the gif header to each set of image data, but I strongly recommend using the PHP ImageMagick interface instead if possible - it has far better support for iterating through frames in an image.
Another option is using a pure PHP implementation that does what you want, such as GifFrameExtractor.
The relevant code is located at line 137 of the source file:
$img = imagecreatefromstring(
$this->fileHeader["gifheader"] .
$this->frameSources[$i]["graphicsextension"] .
$this->frameSources[$i]["imagedata"] .
chr(0x3b)
);
As you can see, there is far more data necessary (the header, the extension (87a vs 89) and a terminating character) to make it valid GIF data.
In Imagemagick, this is pretty trivial. You can coalesce the image to fill out any frames that have been optimized, then do your processing, then optimize it again.
Input:
convert bunny.gif -coalesce -resize 50% -layers optimize bunny_small.gif
Lets say I have an arrays of pixels. Basically an array of this data {x,y,r,g,b,a} for each pixel.
How would I go about converting all this data into an actual image file (png preferably)?
Could not find a solution. Any help would be very appreciated.
I had some time to code up a little example. You should be able to see and note that:
the red component increases towards the bottom of the image
the green component increases towards the right of the image
the blue component is absent
the alpha channel is random and between 0 (opaque) and 127 (fully transparent)
// Define width and height
$w=800;
$h=600;
// Create truecolour image so we can have infinitely many colours rather than a limited palette
$img=imagecreatetruecolor($w,$h);
imagesavealpha($img,true);
imagealphablending($img,false);
// Iterate over all pixels
for($y=0;$y<$h;$y++){
for($x=0;$x<$w;$x++){
$r = round(255*$y/$h);
$g = round(255*$x/$w);
$b = 0;
$alpha = rand(0,127);
$color = imagecolorallocatealpha($img,$r,$g,$b,$alpha);
imagesetpixel($img,$x,$y,$color);
}
}
// Save result
imagepng($img,"result.png");
I'll admit I haven't actually used this API, but looks like PHP has what you're looking for.
You create an image identifier with imagecreate or one of the related functions, then color in each pixel with imagesetpixel, using a color identifier created with imagecolorallocatealpha. From there you should be able to output as a PNG with imagepng.
It's worth noting that this image library seems to support drawing lines and shapes and other structures higher than the per-pixel level, so I'd also look into whether your code necessarily needs to build a big pixel array, rather than drawing the image some other way.
The following script is supposed to create an image with lower quality and so smaller file size (kb), instead it create an image with lower quality but bigger file size.
On my test the original is about 300kb, using 90% quality the output is almost the double and using 100% quality, the output is more than 1mb ...
<?php
$quality = 90;
$path = '/var/www/TEST/';
$inputSrc = $path . 'original.jpg';
$outputSrc = $path . 'after' . $quality . '.jpg';
$handler = imagecreatefromjpeg($inputSrc);
imagejpeg($handler, $outputSrc, $quality);
I assume the issue is related to imagejpeg bad implementation ...
is there any way to workaround this ?
isImageMagicka better solution ?
Thanks
Update
I was curious so I gave a try to ImageMagick and unfortunately I have similar result (slightly better).
Full test results:
Original size: 294.6Kb
GD (imagejpeg) 90%: 581.7Kb
GD (imagejpeg) 100%: 1.1Mb
ImageMagick 90%: 431.7Kb
ImageMagick 100%: 780.9kb
Update 2
I did some more test with GIMP and looks that in order to obtain a file with very similar size to the original one you have to check the option use quality setting from original image.
Now I'm confused more ... since when I select that setting Gimp automatically change the output quality to 74% (for the example image).
I was assuming that the JPEG quality value, if lower that 100%, decrease the image quality at every iteration of a save ... but I start to think I'm wrong here.
Update 3
With ImageMagick is not necessary to specify the quality of the sample and if you leave it emptyImageMagick will use the same quality detected in the input image.
So for the example image it is detected as a quality of 69 and the outfile is 326kb. That is the best result so far.
Here the image I'm using:
I had a little look at this. You can work backwards in ImageMagick and, rather than define the quality and see what size results, you can define the size and see what quality results. So, for a concrete example, you can say you want the output file not to exceed 100kB, like this:
convert MShRR.jpg -define jpeg:extent=100k out.jpg
and you get 99kB like this:
-rw-r--r--# 1 mark staff 294608 14 Jan 09:36 MShRR.jpg
-rw-r--r--# 1 mark staff 99989 14 Jan 09:44 out.jpg
To my eyes, the resulting image is a little posterised:
You can often add a tiny amount of blur to disguise this, as follows:
convert MShRR.jpg -blur x0.5 -define jpeg:extent=100k out.jpg
YMMV - Your Mileage May Vary !!!
I made a tool where people can upload photos and modify them, including desaturation, resulting in a greyscale image.
I generate the final image using PHP's GD library.
When printing these images the colors appear wrong so using Image Magick I add a color profile.
This works great except for images that have been greyscaled. The color profile gets added, but when I open the image in Photoshop, it says "The embedded ICC profile cannot be used because the ICC profile is invalid. Ignoring the profile".
In Photoshop the image is set to Greyscale rather than RGB, hence the attached RGB profile is wrong. I need it to be RGB.
I'm using the following code to add all the possible information in an attempt to make the image RGB:
<?php
$i = new Imagick();
$i->readimage('image.jpg');
$i->setimagetype(Imagick::IMGTYPE_TRUECOLOR);
$i->setimagecolorspace(Imagick::COLORSPACE_RGB);
$i->profileimage('icc', file_get_contents('AdobeRGB1998.icc'));
$i->writeimage($d);
$i->destroy();
?>
Does anyone know how to successfully set the image to RGB and attach the profile?
I did try the different methods and combinations for 'setImageProfile' and 'profileImage', also for colorspace and imagetype, but the result is always the same.
#a34z says in a comment:
"Somehow I must let PS know it is an RGB image with only grey pixels in it or something like that."
It is a fundamental error to assume that an RGB image could even contain 'gray' pixels as such!
RGB images do have pixels that are always composed of a mix of 3 colors: R ed + G reen + B lue. These are the 3 channels which are available, no more. There is no such thing as a gray channel in RGB.
What makes an RGB image look gray to our eyes is the fact that each of the 3 numerical channel values are equal or less strictly speaking, at least 'similar enough'. Of course, there is also software that can analyze the color values of the 3 channels and tell you which pixels are 'gray'. ImageMagick's histogram output would happily tell you which shades of gray you would say and use different names for those Grays. But don't be fooled by that color name: the pixel will still be composed from 3 colors with the same (or very similar) intensities, and ImageMagick will also report these values.
If you really need a pure grayscale image (that uses only one channel for the level of gray, not three), then you have to convert it to such an image type.
The two images may still look the same (if the conversion was done correctly, and if your monitor is calibrated, and if your not red-green-blind) -- but their internal file structure is different.
RGB images need ICC profiles that deal with RGB (if any), such as sRGB. For grayscale you cannot use sRGB, there you may want to use DeviceGray or something...
This worked for me to have it recognized as a truecolor image. Assuming $img is the Imagick object containing the greyscaled image, I check if it is indeed greyscale and then edit 1 random pixel and modify its red value by adding or substracting 5 values, depending on red being greater than 5 or not.
<?php
if ($img->getImageType() == Imagick::IMGTYPE_GRAYSCALE)
{
// Get the image dimensions
$dim = $img->getimagegeometry();
// Pick a random pixel
$x = rand(0, $dim['width']-1);
$y = rand(0, $dim['height']-1);
// Define our marge
$marge = 5;
//$x = 0;
//$y = 0;
// Debug info
echo "\r\nTransform greyscale to true color\r\n";
echo "Pixel [$x,$y]\n";
// Get the pixel from the image and get its color value
$pixel = $img->getimagepixelcolor($x, $x);
$color = $pixel->getcolor();
array_pop($color); // remove alpha value
// Determine old color for debug
$oldColor = 'rgb(' . implode(',',$color) . ')';
// Set new red value
$color['r'] = $color['r'] >= $marge ? $color['r']-$marge : $color['r'] + $marge;
// Build new color string
$newColor = 'rgb(' . implode(',',$color) . ')';
// Set the pixel's new color value
$pixel->setcolor($newColor);
echo "$oldColor -> $newColor\r\n\r\n";
// Draw the pixel on the image using an ImagickDraw object on the given coordinates
$draw = new ImagickDraw();
$draw->setfillcolor($pixel);
$draw->point($x, $y);
$img->drawimage($draw);
// Done,
unset($draw, $pixel);
}
// Do other stuff with $img here
?>
Hope this helps anyone in the future.
PHP and GD seem to have trouble creating images from PNGs of type greyscale with alpha when using imagecreatefrompng(). The results are incredibly distorted.
I was wondering if anyone knew of a way to test for the colour type in order to notify the user of the incompatibility?
Example:
Original Image: http://dl.dropbox.com/u/246391/Robin.png
Resulting Image: http://dl.dropbox.com/u/246391/Robin_result.png
Code:
<?php
$resource = imagecreatefrompng('./Robin.png');
header('Content-type: image/png');
imagepng($resource);
imagedestroy($resource);
Cheers,
Aron
The colour type of a PNG image is stored at byte offset 25 in the file (counting from 0). So if you can get hold of the actual bytes of the PNG file, simply look at byte 25 (I don't do PHP, so I don't know how to do that):
0 - greyscale
2 - RGB
3 - RGB with palette
4 - greyscale + alpha
6 - RGB + alpha
The preceding byte (offset 24) gives the number of bits per channel. See the PNG spec for more details.
In a slight twist a PNG file may have "1-bit alpha" (like GIFs) by having a tRNS chunk (when it is colour type 0 2 or 3).
i landed here today searching for a way to tell (via php) if a specific .png image is an alpha-png one -
David Jones' answer points to the right direction, really easy to implement in php:
file_get_contents to load just that 25' byte (there it is, indeed!), and
ord() to get its ASCII value, to test it (against '6' in my case)
if(ord(file_get_contents($alpha_png_candidate, NULL, NULL, 25, 1)) == 6) {
is_alpha_png_so_do_something();
}
actually i needed that for assuring backward compatibility with ie6
within cms-user-generated-pages, to replace all alpha-png < img > tags with inline-block < spans > - the alpha-png file will then be served as variable for the ms-proprietary css property filter
.alpha_png_span{
filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(
src='$alpha_png_candidate', sizingMethod='crop')
}
...and it all works, so thanks!
paolo
see this answer
:
Another usefull note for those using ImageCreateFromPng:
PHP and GD do not recognize grayscale/alpha images.
So if you use grayscale images with transparency between 0% and 100%, then save the image as RGB.
At least this is true for PHP Version 4.4.2-1 and in 5.1.2-1 with pictures made with GIMP 2.2.8.
url :
http://php.net/manual/en/function.imagecreatefrompng.php