PHP image gallery- dealing with iPhone / iPad images - php

Ive created an image gallery with php and mysql, incorporating several ways to add images & ability to sort by addition method and/or category. After noticing some images from apple devices showed up with the 'wrong' orientation, I created another page to edit orientation and other file info, then save said changes back to file and db. Only after I thought I had solved this problem did I view the altered images on an apple device, only to realize that image was now in 'wrong' orientation on said device. I've been googling this, but can't quite figure out exactly what I need to learn now to deal with images from apple devices in this situation. A shove in the right direction would be greatly appreciated.
Thanks!

Seeing the same problem on my "gallery3" photo gallery. Like machouinard, I am using "jhead -norot filename.jpg" to strip the orientation header from the images. This fixes the Apple rotation problem, and it does not seem to mess up the other browsers.
To edit a bunch of the files in place, I go directly to the album storage area in (gallerytop)/lib/albums and do find -type f | xargs sudo jhead -norot. It is smart enough to only modify the files that need to be modified, and it'll print out a list of them to stdout as it is working.
To get gallery3 to update the thumbnails, I go into the database and set the "dirty" flags like this: echo "update items set thumb_dirty=1,resize_dirty=1 where relative_path_cache like 'ALBUMNAME/%';" | mysql -u root -p gallery3 . Then I go into the gallery maintenance mode and run the "rebuild images" utility.

Forgot I posted this. I figured out how to handle the orientation issue so I thought I'd share. Seems simple now. I've since recreated this project using Codeigniter. CI is great and saves a lot of time, but I'm glad I wrote it all myself the first time. I sure learned more that way.
First, if the file is a jpg, I get the EXIF data with PEL .
$new is the uploaded file to be checked for orientation.
$this->load->library('pel/PelJpeg');
if($ext == 'jpg'){
$pdw= new PelDataWindow(file_get_contents($new));
if(PelJpeg::isValid($pdw)){
$input_jpg = new PelJpeg($new);
$exif = $input_jpg->getExif();
}
}
Then if EXIF exists, get the orientation value and run it through a switch statement, rotate it accordingly and then reset the orientation value. I'm using image_moo and Codeigniter, but this can obviously be changed to use any image manipulation library.
I'm honestly not sure if all those IF statements need to be there, but I kept running into trouble with jpg's that only included some EXIF info and would blow up the script without them.
if($exif !== NULL){
if($tiff = $exif->getTiff()){
if($ifd0 = $tiff->getIfd()){
if($orient = $ifd0->getEntry(PelTag::ORIENTATION)){
$this->image_moo->load($new);
//find the orientation value from the orientation tag. May be a better way, but this works for me.
$orientation = str_replace(' ', '', $orient);
//The orientation value from the orientation tag is after 'Value:'
if (($tmp = strstr($orientation, 'Value:')) !== false) {
$str = substr($tmp, 6, 1);
}
switch ($str)
{
// up is pointing to the right
case 8:
$this->image_moo->rotate(90);
$orient->setValue(1);
break;
// image is upside-down
case 3:
$this->image_moo->rotate(180);
$orient->setValue(1);
break;
// up is pointing to the left
case 6:
$this->image_moo->rotate(270);
$orient->setValue(1);
break;
// correct orientation
case 1:
break;
}
$this->image_moo->save($new,TRUE);
if ($this->image_moo->errors) print $this->image_moo->display_errors();
$this->image_moo->clear();
}
}
}
}
Hope this will be helpful to someone else struggling with the same issues.
If you see anything that could be improved, please let me know. But this works great for me.
Mark

Related

Right way of watermarking & storing & displaying images in PHP

I'm building a web based system, which will host loads and loads of highres images, and they will be available for sale. Of course I will never display the highres image, instead when browsing people will only see a low resolution, watermarked image. Currently the workflow is as follows:
PHP script handles the highres image upload, when image is uploaded, it's automatically re-sized to a low res image and to a thumbnail image as well and both of the files are saved on the server, (no watermark is added).
When people are browsing, the page displays the thumbnail of the image, on click, it enlarges and displays the lowres image with watermark as well. At the time being I apply the watermark on the fly whenever the lowres image is opened.
My question is, what is the correct way:
1) Should I save a 2nd copy of the lowres image with thumbnail, only when it's access for the first time? I mean if somebody access the image, I add the watermark on the fly, then display the image & store it on the server. Next time the same image is accessed if a watermarked copy exist just display the wm copy, otherwise apply watermark on the fly. (in case watermark.png is changed, just delete the watermarked images and they will be recreated as accessed).
2) Should I keep applying watermarks on the fly like I'm doing now.
My biggest question is how big is the difference between a PHP file_exists(), and adding a watermark to an image, something like:
$image = new Imagick();
$image->readImage($workfolder.$event . DIRECTORY_SEPARATOR . $cat . DIRECTORY_SEPARATOR .$mit);
$watermark = new Imagick();
$watermark->readImage($workfolder.$event . DIRECTORY_SEPARATOR . "hires" . DIRECTORY_SEPARATOR ."WATERMARK.PNG");
$image->compositeImage($watermark, imagick::COMPOSITE_OVER, 0, 0);
All lowres images are 1024x1024, JPG with a quality setting of 45%, and all unnecessary filters removed, so the file size of a lowres image is about 40Kb-80Kb.
It is somehow related to this question, just the scale and the scenarios is a bit different.
I'm on a dedicated server (Xeon E3-1245v2) cpu, 32 GB ram, 2 TB storage), the site does not have a big traffic overall, but it has HUGE spikes from time to time. When images are released we get a few thousand hits per hours with people browsing trough the images, downloading, purchasing, etc. So while on normal usage I'm sure that generating on the fly is the right approach, I'm a bit worried about the spike period.
Need to mention that I'm using ImageMagick library for image processing, not GD.
Thanks for your input.
UPDATE
None of the answers where a full complete solution, but that is good since I never looked for that. It was a hard decision which one to accept and whom to accord the bounty.
#Ambroise-Maupate solution is good, but yet it's relay on the PHP to do the job.
#Hugo Delsing propose to use the web server for serving cached files, lowering the calls to PHP script, which will mean less resources used, on the other hand it's not really storage friendly.
I will use a mixed-merge solution of the 2 answers, relaying on a CRON job to remove the garbage.
Thanks for the directions.
Personally I would create a static/cookieless subdomain in a CDN kinda way to handle these kind of images. The main reasons are:
Images are only created once
Only accessed images are created
Once created, an image is served from cache and is a lot faster.
The first step would be to create a website on a subdomain that points to an empty folder. Use the settings for IIS/Apache or whatever to disable sessions for this new website. Also set some long caching headers on the site, because the content shouldn't change
The second step would be to create an .htaccess file containing the following.
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*) /create.php?path=$1 [L]
This will make sure that if somebody would access an existing image, it will show the image directly without PHP interfering. Every non-existing request will be handled by the create.php script, which is the next thing you should add.
<?php
function NotFound()
{
if (!headers_sent()) {
$protocol = (isset($_SERVER['SERVER_PROTOCOL']) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0');
header($protocol . ' 404 Not Found');
echo '<h1>Not Found</h1>';
exit;
}
}
$p = $_GET['path'];
//has path
if (strlen($p)<=1)
NotFound();
$clean = explode('?', $p);
$clean = explode('#', $clean[0]);
$params = explode('/', substr($clean[0], 1)); //drop first /
//I use a check for two, because I dont allow images in the root folder
//I also use the path to determine how it should look
//EG: thumb/125/90/imagecode.jpg
if (count($params)<2)
NotFound();
$type = $params[0];
//I use the type to handle different methods. For this example I only used the full sized image
//You could use the same to handle thumbnails or cropped/watermarked
switch ($type) {
//case "crop":if (Crop($params)) return; else break;
//case "thumb":if (Thumb($params)) return; else break;
case "image":if (Image($params)) return; else break;
}
NotFound();
?>
<?php
/*
Just some example to show how you could create a responds
Since you already know how to create thumbs, I'm not going into details
Array
(
[0] => image
[1] => imagecode.JPG
)
*/
function Image($params) {
$tmp = explode('.', $params[1]);
if (count($tmp)!=2)
return false;
$code = $tmp[0];
//WARNING!! SQL INJECTION
//USE PROPER DB METHODS TO GET REALPATH, THIS IS JUST EXAMPLE
$query = "SELECT realpath FROM images WHERE Code='".$code."'";
//exec query here to $row
$realpath = $row['realpath'];
$f = file_get_contents($realpath);
if (strlen($f)<=0)
return false;
//create folder structure
#mkdir($params[0]);
//if you had more folders, continue creating the structure
//#mkdir($params[0].'/'.$params[1]);
//store the image, so a second request won't access this script
file_put_contents($params[0].'/'.$params[1], $f);
//you could directly optimize the image for web to make it even better
//optimizeImage($params[0].'/'.$params[1]);
//now serve the file to the browser, because even the first request needs to show the image
$finfo = finfo_open(FILEINFO_MIME_TYPE);
header('Content-Type: '.finfo_file($finfo, $params[0].'/'.$params[1]));
echo $f;
return true;
}
?>
I would suggest you to create watermarked images on-the-fly and to cache them at the same time as everybody suggested.
Then you could create a garbage-collector PHP script that will be executed every days (using cron). This script will browse your cache folder to read every image access time. This can done using fileatime() PHP method. Then when a cached wm image has not been accessed within 24 or 48 hours, just delete it.
With this method, you can handle spike periods as images are cached at the first request. AND you will save your HDD space as your garbage-collector script will delete unused images for you.
This method will only work if your server partition has atime updates enabled.
See http://php.net/manual/en/function.fileatime.php
For most scenarios, lazily applying the watermark would probably make most sense (generate the watermarked image on the fly when requested then cache the result) however if you have big spikes in demand you are creating a mechanism to DOS yourself: create the watermarked version on upload.
Considering your HDD storage capacity and Pikes.
I would only create a watermarked image if it is viewed.(so yes on the fly) In that way you dont use to much space with a bunch a files that are or might not be viewed.
I would not watermark thumbnails i would rather make a filter that fake watermark and protect from being saved. That filter would apply to all thumbnails without creating a second image.
In this way all your thumbbails are watermarked (Fake with onther element on top).
Then if one of these thumbnails is viewed it generate a watermarked image (only once) since after its generated you load the new watermarked image.
This would be the most efficient way to deal with your HDD storage and Pikes.
The other option would be to upgrade your hosting services. Godaddy offer unlimited storage and bandwith for about 50$ a year.

PHP, getting Thumbnail for an image in Elfinder

I use Elfinder 2.1 and i'm looking for a possibility to get the right thumbnail path of an image with PHP.
By default a .tmb folder exist in every folder and contains the resized thumbnails with an (i think) md5 hashed filename.
How can i retrieve the correct thumbnail for a specific image in PHP?
The goal is to show only the thumbnails in another PHP Script and with a click the original imgage shows up.
Thanks, was useful. I added one thing.
protected function tmbname($stat) {
$ext = 'png';
if($stat['mime'] == 'image/jpeg'){$ext = 'jpg';}
if($stat['mime'] == 'image/gif'){$ext = 'gif';}
return current(explode('.', $stat['name'])).'.'.$ext;
//$stat['hash'].$stat['ts'].'.png';
}
Works well and deletes too.
Take a look at this issue, it may point you in the right direction:
https://github.com/Studio-42/elFinder/issues/671
I answered that question just a few minutes ago on the above link. In short:
search for the function tmbname($stat) in the class elFinderVolumeDriver.class.php
replace the return value with this: current(explode('.', $stat['name'])).'.png';
This way the created thumbnails will have the same name as the original image (of course with .png extension). If that will arise security questions/problems, I don't know. Hope it does help.

image editing in php

I want to have a system in my Joomla website where user can upload their image and create and design a product image by having the product template. For example the keychain needs to be design.
Can any one recommend is this possible with PhP? or I need some other platform to work on.
Thanks
This can be done with GD or Imagemagick libraries for PHP.
Here is a sample script I wrote a while back to composite an image to a blank coffee mug using PHP and imagemagick. This should be very similar to what you're trying to do.
<?php
$im = new Imagick('image.jpg');
$mug = new Imagick('images/mug.png');
$im->scaleImage(300, 0);
$im->setImageFormat('png');
$im->setImageVirtualPixelMethod(Imagick::VIRTUALPIXELMETHOD_TRANSPARENT);
$im->setImageMatte(true);
$height = $im->getImageHeight();
$mug->compositeImage($im, $im->getImageCompose(), 80, 130);
header("Content-Type: image/png");
echo $mug;
?>
If you want to provide a kind of graphic editor to users most likely you need a JavaScript implementation with PHP only handling the final result of it.
A common way to edit images with PHP is to use Imagick, but your case doesn't really sound like that. This is more applicable to, say, cases when you need to resize all the uploaded image or to save them in a different format.

Rotating a picture in PHP once it's saved

I have this PHP script that saves a picture on my website. I would like to, once the picture is saved, rotate the picture by 90 degrees if it's not landscape. This a piece of my script:
$uploadfile = 'path/where/to/save/picture.jpg';
if (move_uploaded_file($_FILES['userfile']['tmp_name'], $uploadfile)) {
//The code under here is not working
if($_GET['landscape'] == false || $_GET['landscape'] == 'false'){
$img = imagecreatefromjpeg($uploadfile);
$newimg = imagerotate($img, 90.0, 0);
imagejpeg($newimg, $uploadfile);
}
//this is code under here is working
$prev = create_preview($filename, $uploadfile, $ext, true);
}
As you can see I move_uploaded_file() and then I have an if statement that if the picture is not landscape (so landscape == false) I rotate it. Then I create a preview of the picture.
If I comment out the if statement that checks the landscape the code WORKS, so it uploads the picture but it's NOT ROTATED as I want and creates a preview. If I let the if uncommented (like in this case) it seems like it's not saved anymore because when I try to visualize it I see nothing. This means that the code flow goes into the if, then something happens and the picture is not visualized anymore. The problem is in the if statement that rotates the picture.
So there is definitely something wrong in the procedure to rotate the picture, but I don't understand exactly what's wrong, I create the image from the location it has been uploaded to, I rotate it using the PHP function and then recreate the image in the same exact location.
Can anyone of you see where I'm getting this wrong?
Thanks,
Masiar
It's a shame you can't see the error messages. I suggest writing a small test page that will "fake" the iPhone side of things and allow you to test by submitting images without using the phone. Or simply write a short script that will open an image, imagerotate() it and save it, and use that for testing.
Also, as gnud points out, the PHP errors may even already be being written out to a server logfile. (Typically, they'll be somewhere like /var/log/apache/..., but figuring out where they are on your box will be more of a question for SuperUser, I guess...)
Having said that, given all you've said, I suspect that the PHP package for the distribution of Linux you're using does not support the (fairly-heavily-edited) PHP "packaged" version of the GD library. See this enhancement request for Ubuntu for some details.
This means that the imagerotate() function that you're using simply doesn't exist. You'd verify this easily if you enabled error reporting and used a test script.
As a workaround, your options are either to find a version of the GD library that you can install on your box to replace the standard one, or perhaps use a function written in PHP to do the rotation manually.
Alternatively, as Eamorr suggests, you could shell out to a command-line tool like ImageMagick (or maybe use ImageMagick via the PECL ImageMagick library, but that's probably overkill for one rotate.)
As an aside, I believe the iPhone specifically "rotates" photos that it's taken (e.g. in landscape orientation) by setting a flag in the image metadata, rather than actually rotating the image data, so if you're rotating images taken on an iPhone, make sure that you test with images taken in both portrait and landscape orientations, and check that your image rotation is doing the right thing in each case.
If I recall correctly, the photo data is always in portrait orientation, and just has the "landscapeness" set in the EXIF orientation data if the photo was taken with the phone held in landscape orientation. There are different values for if the phone's held upside-down, too.
To flip 180 degrees:
exec('mogrify -flip /path/to/your/picture');
You need to install imagemagick
I'm sure you can figure out how to flip it 90 degrees very easily.

How to compress images in CodeIgniter, yet do not change their dimensions?

I have a site where users can upload images. I process these images directly and resize them into 5 additional formats using the CodeIgniter Image Manipulation class. I do this quite efficiently as follow:
I always resize from the previous format, instead of from the original
I resize using an image quality of 90% which about halves the file size of jpegs
The above way of doing things I implemented after advise I got from another question I asked. My test case is a 1.6MB JPEG in RGB mode with a high resolution of 3872 x 2592. For that image, which is kind of borderline case, the resize process in total takes about 2 secs, which is acceptable to me.
Now, only one challenge remains. I want the original file to be compressed using that 90% quality but without resizing it. The idea being that that file too will take half the file size. I figured I could simply resize it to its' current dimensions, but that doesn't seem to do anything to the file or its size. Here's my code, somewhat simplified:
$sourceimage = "test.jpg";
$resize_settings['image_library'] = 'gd2';
$resize_settings['source_image'] = $sourceimage;
$resize_settings['maintain_ratio'] = false;
$resize_settings['quality'] = '90%';
$this->load->library('image_lib', $resize_settings);
$resize_settings['width'] = $imagefile['width'];
$resize_settings['height'] = $imagefile['height'];
$resize_settings['new_image'] = $filename;
$this->image_lib->initialize($resize_settings);
$this->image_lib->resize();
The above code works fine for all formats except the original. I tried debugging into the CI class to see why nothing happens and I noticed that the script detects that the dimensions did not change. Next, it simply makes a copy of that file without processing it at all. I commented that piece of code to force it to resize but now still nothing happens.
Does anybody know how to compress an image (any image, not just jpegs) to 90% using the CI class without changing the dimensions?
I guess you could do something like this:
$original_size = getimagesize('/path/to/original.jpg');
And then set the following options like this:
$resize_settings['width'] = $original_size[0];
$resize_settings['height'] = $original_size[1];
Ok, so that doesn't work due to CI trying to be smart, the way I see it you've three possible options:
Rotate the Image by 360ยบ
Watermark the Image (with a 1x1 Transparent Image)
Do It Yourself
The DIY approach is really simple, I know you don't want to use "custom" functions but take a look:
ImageJPEG(ImageCreateFromString(file_get_contents('/path/to/original.jpg')), '/where/to/save/optimized.jpg', 90);
As you can see, it's even more simpler than using CI.
PS: The snippet above can open any type of image (GIF, PNG and JPEG) and it always saves the image as JPEG with 90% of quality, I believe this is what you're trying to archive.

Categories