I've got a Zenphoto site up and running using Steve Moody's StopK2 theme to integrate with my Wordpress blog. I wanted to show EXIF data with my pictures, and hacked up the image.php in the theme to do that using exifer1_5, a PHP package for decoding EXIF data (for those of us on hosted systems where PHP native EXIF support can't be compiled in). I really wanted to be able to show under the image something like "Shot by Eric DeSilva on October 12, 2006 with a Nikon D200 and a 18-200mm f/3.5-5.6 AF-S VR lens (24mm actual) on Manual. Exposure at f/3.5 for 1/250th of a second."
Using my hacked up version of StopK2, I could do all of that, except for the lens part. Even though I could see "lens used" data in my Adobe apps looking at the jpg files, I couldn't find that data in the EXIF associated with the file...
Then I discovered the problem... That particular data element is associated with a "makernote" in EXIF--basically an extension that is manufacturer-specific. Adobe applications like photoshop (Adobe is not the only offender here--most image manipulation programs do this) read the makernotes, but don't write them... Adobe actually does store the data in the jpg (as long as you don't "save for web"), but its in XMP format--something of their very own that they are trying to standardize. (Yeah, I could use a file that hasn't been run through Adobe, but I tend to shoot 6MP or 10MP NEF (Nikon RAW) images--kind of web-unfriendly.)
Eventually, I found a PHP toolkit that will decode EXIF *and* XMP metadata. Its ozhiker's PHP Metadata Toolkit:
http://www.ozhiker.com/electronics/pjmt/library/documentation/example.htmlIt is a *lot* of PHP code, however. I've started sorting through it, and its not particularly easy to extract the specific data I want. But, I may still try. Which brings me to my real question...
My intuition tells me that if I'm forcing the server to run a whole bunch of PHP every time an image is displayed, that is really going to slow down the interface. The other option is to do something like write a standalone PHP tool that goes through the data table, does a one-time extraction of the XMP data I want, and then writes the data into a new column in the table which is then displayed by zenphoto. Before I embark on this, since I'm a pretty new PHP programmer, anyone care to comment? It kind of offends me to write data into a table that is already there (albeit embedded in a jpg). But, is my sense that rerunning the XMP parsing over and over again a bad idea? Any thoughts on how to get this done? I was thinking a new button on the admin page--"get metadata" or something--that would invoke a procedure to go through the table, row by row, extract metadata if it isn't present already, and write it to the table.
Any comments, suggestions before I dive in?
Comments
Some examples are:
http://www.thinkdreams.com/zenphoto/bushwoodtools/IMG_3107.JPG
and an example taken with a Canon Digital Rebel (not my camera) so you can see what data is collected by other cameras.
http://www.thinkdreams.com/zenphoto/familypics2006/3.jpg
Now it should be mentioned you can "tailor" what you want to display my modifying the parameters in the image.php and associating them with the fields in the EXIF module which in turn uses the EXIF library to collect the data. Sounds complicated, but it's not. The only other option is to utilize a built-in library from your webhost, but this works better I think since not everyone has EXIF support in their PHP implementations.
The components I use are:
http://www.sanisoft.com/phpexifrw/
and my hacked together function file:
`
require_once("exif.inc.php");
// Prints EXIF Information - using phpEXIFrw library called above
function getExif() {
$info = array();
$er = new phpExifRW("../" . getFullImageURL());
$er->processFile();
// Thumbnail Display
if($er->thumbnailURL) {
$info['thumb'] = $er->thumbnailURL;
}
//Camera Model
if ($er->ImageInfo[TAG_MAKE]){
$info['model'] = trim($er->ImageInfo[TAG_MODEL]);
}
// Date/Time Taken
if ($er->ImageInfo["DateTime"]){
$info['datetime'] = trim($er->ImageInfo[TAG_DATETIME_ORIGINAL]);
}
// Height and Width Information
$info['width'] = $er->ImageInfo["Width"];
$info['height'] = $er->ImageInfo["Height"];
// Flash Yes or No
if ($er->ImageInfo[TAG_FLASH] >= 0){
$info['flash'] = $er->ImageInfo[TAG_FLASH] ? "Yes" :"No";
}
// F number information
if ($er->ImageInfo[TAG_FNUMBER]){
$info['fnum'] = (double)$er->ImageInfo[TAG_FNUMBER][0];
}
// Focal Length information
if ($er->ImageInfo[TAG_FOCALLENGTH]){
$info['focal'] = $er->ImageInfo[TAG_FOCALLENGTH][0];
}
// Focal Length information
if ($er->ImageInfo[TAG_EXPOSURETIME]){
$info['exptime'] = $er->ImageInfo[TAG_EXPOSURETIME][0];
}
// Focal Length information
if ($er->ImageInfo[TAG_SHUTTERSPEED]){
$info['shutterspd'] = $er->ImageInfo[TAG_SHUTTERSPEED][0];
}
// Compression Level
if ($er->ImageInfo[TAG_COMPRESSION_LEVEL]){
$info['complevel'] = (int)$er->ImageInfo[TAG_COMPRESSION_LEVEL][0];
}
// ISO Equivalency Information
if ($er->ImageInfo[TAG_ISO_EQUIVALENT]){
$info['iso'] = (int)$er->ImageInfo[TAG_ISO_EQUIVALENT];
}
// Orientation Information
if ($er->ImageInfo[TAG_ORIENTATION]){
$info['orientation'] = $er->ImageInfo[TAG_ORIENTATION];
switch ($info['orientation']) {
case 1:
$info['orientation'] = "No rotation, No flip";
break;
case 2:
$info['orientation'] = "No Rotation, Flipped Horizontally";
break;
case 3:
$info['orientation'] = "Rotated 180 degrees, No Flip";
break;
case 4:
$info['orientation'] = "No Rotation, Flipped Vertically";
break;
case 5:
$info['orientation'] = "Flipped Horizontally, Rotated 90 degrees counter clockwise";
break;
case 6:
$info['orientation'] = "No Flip, Rotated 90 degrees clockwise";
break;
case 7:
$info['orientation'] = "Flipped Horizontally, Rotated 90 degrees clockwise";
break;
case 8:
$info['orientation'] = "No Flip, Rotated 90 degrees counter clockwise";
break;
default:
break;
}
}
return($info);
}
`
Then in my image.php I call:
`
<?php $exif = getExif(); ?>
Exif Data
<?php<br />
if($exif['thumb']) echo'
if($exif['model']) echo'
if($exif['datetime']) echo'
if($exif['width'] && $exif['height']) echo'
if($exif['flash']) echo'
if($exif['fnum']) echo'
if($exif['iso']) echo'
if($exif['orientation']) echo'
if($exif['focal']) echo'
if($exif['exptime']) echo'
if($exif['shutterspd']) echo'
if($exif['complevel']) echo'
?>
`
The rest is just a nifty little lightbox-style app which pops the EXIF up in a window.
You're exactly right about the last part too -- you don't want to be reading EXIF from every file when each is viewed.
This is zenphoto's philosophy for most expensive operations: First, don't do anything until something is actually requested (lazy evaluation), and second, store unchanging values in the database for fast access later (caching). Together they provide a consistently fast interface.
For EXIF, I was planning on doing exactly the same thing. There will be EXIF methods in the Image class whose source is (initially) the image file, and after processing once for EXIF data (upon request), everything is stored to and read from the database from then on. That's the way to do it.
If you want to help with this effort (or just hack it for your own use), I highly recommend you wait until the upcoming 1.0.4 release is out. There is a huge change to the database persistence model that you'll enjoy -- individual SQL calls are replaced with get() set() and save(), and all you have to do is add the columns in the database to use them that way. Makes it much easier to do anything in the database. :-)
I'll try to get the new release out today or tomorrow.
Oh, I also wanted to mention that for the official EXIF implementation, I believe I will use Exifer. It's lightweight and fast. I think I might absorb it into Zenphoto as well and take over development since it's been abandoned. Unfortunately that means we still won't be able to read the actual lens model... I don't think it's that important for the majority of users. I'll look into it though.
Is it Exifer or Exifixer? I can't find any references to Exifer that don't relate to a windows app for editing EXIF data on the images. I'm going to assume you mean:
Exifixer
Not
Exifer
Right? Or am I just wonky?
At any rate, EXIF within the database would certainly be the better way to go, although the PHPExifRW implementation I use hasn't been awful, it does however have to read each image's EXIF info beforehand, which I'm sure isn't the best way.....
I look forward to it...
I'll take a better look at PHPExifRW before I start implementation, but I like the idea of absorbing development of Exifixer. We'll see how they look!
I did basically what you are talking about, Thinkdreams, only using exif(ix)er. Was scoping out the size of trying to do it using PJMT so I could also get access to XMP data. Frankly, the PJMT toolkit seems exhaustive--it will act on a variety of different files and decode a huge amount of metadata, whether EXIF or XMP or whatever else. After reviewing what metadata is attached to a simple jpg, I can actually understand why Adobe Photoshop's "save for web" function strips it all out... The data payload has to be pretty big and 99.9% of any web apps aren't going to make use of it.
That said, it would have been nice of them to leave stuff like the copyright in place...
I took another look at PJMT, and hacking that one is going to be rough. Exif(ix)er certainly has the benefit of compactness and an awful lot of bang for the buck. Its nicely commented, too. And, it seems compatible with a lot of cameras; while I shoot a variety of Nikons, I had it decode some stuff shot with point-and-shoots, and it did a good job.
Perhaps I'm feeling lazy right now, but I'm thinking the marginal benefit of a robust PJMT implementation is probably not worth it just to get lens data. Frankly, I'm wondering if there is some persistent EXIF field I can simply copy the data into--I typically fill in the "Artist" and "Copyright" fields, so one more is no big deal (esp. since its a cut-n-paste from another metadata field I can get to in Adobe Bridge, and since I probably shoot with one lens 80% of the time).
Tristan, if you are going to pick up exif(ix)er, I'll look back at what I did. I think I ran into some oddities with the way he did certain things, like long exposures--"300/10s" and stuff like that. If I can figure out what I changed, I'll try to post the modifications. He also uses PHP short codes in a couple of includes that my host barfs on.
If we had read-write support, you could even save your title/description to XMP to "back it up" so to speak, so losing your database would be no big deal. We'll see....
Read/write support for XMP is an excellent idea. Is this now part of Zenphoto?