Copy an image and preserve its EXIF/IPTC data with PHP imageCreateFromJpeg? - php

I having some problems with an image that has EXIF/IPTC data stored in it.
When I use imageCreateFromJpeg (to rotate/crop or etc) the newly stored file doesn't preserve the EXIF/IPTC data.
My current code looks like this:
<?php
// Before executing - EXIF/IPTC data is there (checked)
$image = "/path/to/my/image.jpg";
$source = imagecreatefromjpeg($image);
$rotate = imagerotate($source,90,0);
imageJPEG($rotate,$image);
// After executing - EXIF/IPTC data doesn't exist anymore.
?>
Am I doing something wrong?

You aren't doing anything wrong, but GD doesn't deal with Exif of IPTC data at all as its beyond the scope of what GD does.
You will have to use a 3rd party library or other PHP extension to read the data from the source image and re-insert it to the output image created by imagejpeg.
Here are some libraries of interest: pel (php exif library), an example on php.net showing how to use pel to do what you want, php metadata toolkit, iptcembed() function.

Here is an example of image scaling using gd, and copying Exif and ICC color profile using PEL:
function scaleImage($inputPath, $outputPath, $scale) {
$inputImage = imagecreatefromjpeg($inputPath);
list($width, $height) = getimagesize($inputPath);
$outputImage = imagecreatetruecolor($width * $scale, $height * $scale);
imagecopyresampled($outputImage, $inputImage, 0, 0, 0, 0, $width * $scale, $height * $scale, $width, $height);
imagejpeg($outputImage, $outputPath, 100);
}
function copyMeta($inputPath, $outputPath) {
$inputPel = new \lsolesen\pel\PelJpeg($inputPath);
$outputPel = new \lsolesen\pel\PelJpeg($outputPath);
if ($exif = $inputPel->getExif()) {
$outputPel->setExif($exif);
}
if ($icc = $inputPel->getIcc()) {
$outputPel->setIcc($icc);
}
$outputPel->saveFile($outputPath);
}
copy('https://i.stack.imgur.com/p42W6.jpg', 'input.jpg');
scaleImage('input.jpg', 'without_icc.jpg', 0.2);
scaleImage('input.jpg', 'with_icc.jpg', 0.2);
copyMeta('input.jpg', 'with_icc.jpg');
Output images:
Input image:

The answer by #drew010 is correct in that this cannot be done without an external library or other program. However, that answer is quite old and there are now at least two good ways of doing it. #Thiago Barcala gave one answer, using PEL.
Here is a completely different one using a different one, the PERL-script exiftool by Paul Harvey. I prefer this solution because exiftool has a longer history of development and use, is better documented, and seems more stable and reliable to me. PEL is newer by nearly 10 years, has an unstable API, a history of the project changing hands, and has yet to reach version 1.0. I tried setting it up and ran into some roadblocks, and found no documentation for overcoming them, whereas setting up exiftool worked out-of-the-box.
Install exiftool, then, after saving the old image to a new path run:
exec('exiftool -TagsFromFile /full/path/to/original_image.jpg /full/path/to/newly_saved_image.jpg');
You must leave both files in existence for this to work; if you overwrite the file as your original code does, the EXIF data will be lost.
Make sure your php.ini allows for the exec() call; sometimes it is disallowed for security reasons. Also, take great care that you do not allow any user-generated input in any parameters you pass to that call because it could allow an attacker to execute an arbitrary command under the privileges of the web server. The exec call is most secure if your script generates the filenames according to some formula, such as alphanumeric characters only, with a fixed directory path, and then feed them into the exec call.
If you don't want to install exiftool globally, you can just replace exiftool by the full path to it. If you are using SELinux, make sure to set the context on the file for the exiftool script to httpd_exec_t to allow it to be executed by the web server, and make sure the directory the whole script is in has context httpd_sys_content_t or some other context that allows access by the webserver.

Related

php How to reduce file size using gd and upload to folder [duplicate]

I have a site with about 1500 JPEG images, and I want to compress them all. Going through the directories is not a problem, but I cannot seem to find a function that compresses a JPEG that is already on the server (I don't want to upload a new one), and replaces the old one.
Does PHP have a built in function for this? If not, how do I read the JPEG from the folder into the script?
Thanks.
you're not telling if you're using GD, so i assume this.
$img = imagecreatefromjpeg("myimage.jpg"); // load the image-to-be-saved
// 50 is quality; change from 0 (worst quality,smaller file) - 100 (best quality)
imagejpeg($img,"myimage_new.jpg",50);
unlink("myimage.jpg"); // remove the old image
I prefer using the IMagick extension for working with images. GD uses too much memory, especially for larger files. Here's a code snippet by Charles Hall in the PHP manual:
$img = new Imagick();
$img->readImage($src);
$img->setImageCompression(Imagick::COMPRESSION_JPEG);
$img->setImageCompressionQuality(90);
$img->stripImage();
$img->writeImage($dest);
$img->clean();
You will need to use the php gd library for that... Most servers have it installed by default. There are a lot of examples out there if you search for 'resize image php gd'.
For instance have a look at this page http://911-need-code-help.blogspot.nl/2008/10/resize-images-using-phpgd-library.html
The solution provided by vlzvl works well. However, using this solution, you can also overwrite an image by changing the order of the code.
$image = imagecreatefromjpeg("image.jpg");
unlink("image.jpg");
imagejpeg($image,"image.jpg",50);
This allows you to compress a pre-existing image and store it in the same location with the same filename.

Merge multiple pictures into one

Let's suppose I want to create a face generator, where I would design several pictures for face form, ears, noses, mouth, hair. Given a combination of those pictures how can I create a new picture in PHP? For instance, a simplified version:
There is face1.png, face2.png, nose1.png and nose2.png. How could I programmatically merge face1.png with nose2.png, so the result picture would hold content from both picture?
You've basically got three options: GD, Cairo or ImageMagic. I'd recommend using the Imagick class if it's available. If not, ImageMagick through PHP system calls. If that's not available, GD will probably suffice.
It depends on your server configuration, which of these are available and which would require additional packages to be installed.
There's a very simple example in the Imagick documentation of combining images: https://secure.php.net/manual/en/imagick.compositeimage.php
I also found this example for GD:
Merge two PNG images with PHP GD library
There is a function named imagecopy. This function overrides a part of the destination image using a source image. The given part is specified as parameters. Before you tell me that this does not solve your problem, I have to add that the pixels in the destination picture will not be overriden by the pixels of the source picture if the pixels are transparent. You need to use imagealphablending and imagesavealpha on the source picture, like this:
public static function experimental($images, $width, $height, $dest = null) {
$index = 0;
if ($dest === null) {
$dest = $images[$index++];
}
while ($index < count($images)) {
imagealphablending($images[$index], true);
imagesavealpha($images[$index], true );
imagecopy($dest, $images[$index++], 0, 0, 0, 0, $width, $height);
}
return $dest;
}
If we have these two pictures:
The result will be this:
You really want make it with PHP?
Ok.
1) You can use GD library for image processing.
2) You can use imagemagic PHP wrapper -- imagic.
But I think you should use canvas on client side. And for saving result you can send base64 png image representation of canvas (or separated layers/pictures) to backend.

PHP & ImageMagick - image code into image?

I want to be able to generate image thumbnails without saving them to the server. So far, I've come up with this code, but I'm not sure what to do with $code.
$code = system("convert galleries/13_0.jpg -resize 400x270 /dev/stdout");
How would I go about plugging $code into the PHP/HTML to get the raw image code to display as a jpg?
I would advise you not to do this from the system as you have described.
PHP has libraries for doing this sort of thing.
http://www.php.net/manual/en/ref.image.php.
http://php.net/manual/en/book.imagick.php.
And there are libraries the wrap these native functions for manipulating images. https://imagine.readthedocs.org/en/latest/
That way all your code is in PHP and you are not relying on the system to do anything (providing PHP has been compiled with the libraries as described. They are standard libraries available in most PHP builds and you can enable them if they are not included).
Edit: I'm advocating that you.
Open the source image.
Convert the source image
Return the source image
with imagine you would do it thus:
$imagine = new Imagine\Gd\Imagine();
$size = new Imagine\Image\Box(400, 270);
$imagine->open('/path/to/large_image.jpg')
->resize($size)
->show('jpg');
If the $code is the raw data, could you have another file getimage.php which would write the data out but set the header content type to image/JPEG? So your img src would be getimage.php?image=13_0.jpg for example?
I would save it to memory, then read it, then delete it. /dev/shm is a ramdrive on most Linux systems.
$tmp = '/dev/shm/'.uniqid('',true).'.jpg';
system("convert galleries/13_0.jpg -resize 400x270 $tmp");
header("Content-Type: image/jpeg");
header('Content-Length: '.filesize($tmp));
readfile($tmp);
unlink($tmp);

PHP Imagick - convert image to greyscale (very bad result)

I was doing some image editing with PHP, since GD provides less functionalities, I switched to Imagick.
One of the processes is to greyscale images. Everything went fine (locally on Windows 7, Imagick 2.2.1-dev 6.5.8-7 Q16) till I uploaded the script to my web hosting server (Linux, Imagick 3.0.1, 6.2.8, 2010-10-20, Q16).
I'v tried to change the quality, but it didn't improve anything.
$img->setImageCompression(imagick::COMPRESSION_JPEG);
$img->setImageCompressionQuality(100);
Here is the results from GD, Imagick and Photoshop
I believe something's wrong with version 3.0.1. Can someone please confirm that?
Q1: Is there an alternative way to convert an image to greyscale with Imagick?
Q2: Is it possible to convert a GD resource to Imagick? So I can use imagefilter($img, IMG_FILTER_GRAYSCALE); to get the correct result and then output with Imagick.
ps: For Q2, you might suggest me to just use GD to process the image. But the problem is that imagejpeg() cannot save images with resolution preserved. and that is actually the reason I switched to Imagick.
This is my preferred way to make a B&W photo in php/imagick: $im = $im->fxImage('intensity');
That applies a function to the image, where intensity is equal to 0.299*red+0.587*green+0.114*blue.
That formula is based on how our eyes are more sensitive to different colours, and as such the difference between that and a "flat" grayscale image really is night and day.
More details here:
http://php.net/manual/en/imagick.fximage.php
http://www.imagemagick.org/script/fx.php
function ImagickToGD($imagick){
$tmpfile = tmpfile();
$imagick->writeImage($tmpfile);
return imagecreatefromstring(file_get_contents($tmpfile));
}
Note that this function does not do any cleanup (except the temp file, which PHP cleans automatically).
So, for example, your code should look like:
$img = new Imagick();
// ...
$gd = ImagickToGD($img);
unset($img); // destroy imagick
imagefilter($gd, IMG_FILTER_GRAYSCALE);
imagejpeg($gd, $target_name, 100);
imagedestroy($gd);
Also, I did not understand the part about "preserving resolution". There is nothing in these operations relating to resolution. My guess is you meant compression? If you want full quality (ie, no compression) simply use 100 as compression value (as I did).
This results in maintaining the existing quality, since opening an image of 70% quality and saving it back with 70% quality actually decreases the final quality by 49% (70% of 70%).
function GDToImagickTo($gd){
$tmpfile = tmpfile();
imagepng($tmpfile); // Png is our best image deal:
// lossless compression, transparency etc..
$imagick = new Imagick()
$imagick->readImage($tmpfile);
return $imagick;
}
Refer this website and check out the image Magick operators found here www.rubblewebs.co.uk/imagemagick/
Also go with www.fmwconcepts.com/imagemagick/ you will find some examples out here...
You can use the image class what you prefer and then use the method readImageBlob to send it to the imagick http://www.php.net/manual/en/imagick.readimageblob.php

Preferred way to copy JPG files from a remote server using PHP

I'm using PHP to copy JPGs from a remote server to my own server. Is it best to simply use the copy() function, or are the jpeg-specific functions better? For example:
$copy = copy($remote_url, $dest_file);
-OR-
$img = imagecreatefromjpeg($remote_url);
$copy = imagejpeg($img, $dest_file);
imagedestroy($img);
What would the best option be in terms of speed and memory load? Also, would there be any difference in the resulting image quality? I should add that this script is required to copy a large number of photos (typically hundreds, but sometimes it may be a couple thousand).
Thanks, Brian
if all you want is a copy, copy() is better.
using the gd library functions (imagecreatefromjpeg/imagejpeg) will end up re-compressing the image (probably, maybe it's smart enough not to, but probably). If you wanted to convert the images to .png or something, then you'd want to use gd (or ImageMagick)

Categories