Skip to main content
Inspiring
March 29, 2015
Answered

Quickly retrieve Exif metadata

  • March 29, 2015
  • 3 replies
  • 2601 views

I've got a script for Photoshop CC 2014 that makes some exposure value (EV) calculations based on Exif metadata in camera raw files and then adjusts the image exposure to the target EV.  I know that this information is quickly available in Bridge, but when I try to get the metadata in Photoshop, I have to use the Document.open() command, and that opens the file (which takes a long time) instead of just grabbing the Exif metadata.  I can get it quickly in a Bridge script, but I'm having a hard time embedding that stuff in a Photoshop-targeted script (the BridgeTalk stuff is a bit confusing to me, or it isn't working on my computer).

I've also attempted to use "bridge.executeScript()", but the bridge object apparently is not available to scripts targeting Photoshop (or else something is messed up with my installation).  Bridge is available in bridge-targeted scripts only.

Here's how it's working in Photoshop scripting (I include only the relevant part).

    for (var nfile = nstart; nfile < nstop; nfile++){
        var Name = File(testListing[nfile]).name.replace(/\.[^\.]+$/, '');
        var file = File(testListing[nfile].path + "/" + Name + ".JPG");
        if (file.exists){
            app.open(file);
            }
        else{
            app.open(testListing[nfile]);
            }
        var DocInfo = app.activeDocument.info;
        var docRef = app.activeDocument
       EV[nfile] = CalcEV (DocInfo, BaseISO);
        app.activeDocument.close(SaveOptions.DONOTSAVECHANGES);
        }

(FYI, I use the jpeg file if it's there because Photoshop reads the exif from that file more quickly than it does from the raw file)

Here's how it's working in Bridge scripting:

firstfile = '7A7A5842.CR2';

lastfile = '7A7A5843.CR2';

var ExifProperty = 'exif:DateTimeOriginal';

var ExifNamespace = 'http://ns.adobe.com/exif/1.0/';

app.document.deselectAll();

app.document.selectAll();

var myThumbs = app.document.getSelection("cr2");

var fstart = -999;

var fend = -999;

for (var n = 0; n < myThumbs.length; n++){

    if (myThumbs.name == firstfile) fstart = n;

    if (myThumbs.name == lastfile) fend = n;

}

var NumFiles = myThumbs.length;

var fileThumbs = myThumbs.slice(fstart,fend);

flen = fend-fstart+1;

var EVarray = new Array(flen);

for (var n = 0; n < fileThumbs.length; n++){

    var md = fileThumbs.metadata;

    ISO = md.read(ExifNamespace,'exif:ISOSpeedRatings');

    SS_string = md.read(ExifNamespace,'exif:ExposureTime');

    var test = SS_string.split("/");

    ShutterSpeed = test[0]/test[1];

    var Aperture_string = md.read(ExifNamespace,'exif:FNumber');

    if (Aperture_string == '') {

        var Aperture = Number(1.4); // assume Samyang set at f1.4

    }

    else {

        var test = Aperture_string.split("/");

        Aperture = test[0]/test[1];

    }

    var ISOadjust = Number(1.);

    EVarray = Math.log(Math.pow(Aperture,2)/(ShutterSpeed*ISOadjust))/Math.LN2;

    $.writeln('ISO ',ISO,' SS ',SS,' Av ',Aperture, ' EV=',EVarray);

}

app.document.deselectAll();

"Done...";

This topic has been closed for replies.
Correct answer I have gone

This example works for me with CS6 (don't have CC)

Create a "test" folder off the desktop and place a few cr2 files in the folder and try the following script.

#target photoshop;

main();

function main(){

var bt = new BridgeTalk();

bt.target = "bridge";

bt.body = "var ftn = " + getInfoFromBridge.toSource() + "; ftn();";

bt.onResult = function( info ) { processInfo(eval( info.body ));}

bt.send(10);

};

function processInfo(info){

//do whatever to data

alert(info.join('\n'));

};

function getInfoFromBridge(){

var Info = [];

app.document.thumbnail = Folder("~/desktop/test");

app.document.deselectAll();

var thumbs = app.document.getSelection("cr2");

for(var a in thumbs){

var t = new Thumbnail(thumbs);   

var md = t.synchronousMetadata;

Info.push([[thumbs.name],[md.read( "http://ns.adobe.com/exif/1.0/","exif:FNumber")]]);

}

return Info.toSource();

};

3 replies

Inspiring
April 10, 2015

I wrote a js file that handles a lot of variant ways of dealing with metadata:

http://ps-scripts.cvs.sourceforge.net/viewvc/ps-scripts/xtools/xlib/metadata.js

You can pass the Metadata constructor a Document, File, (XML) String, or XMPMeta and retrieve metadata fairly easily.

It also has an strf method so you can do stuff like:

  var doc = app.activeDocument;

  var md = new Metadata(doc);

  var str3 = "%X{xap:CreateDate} %I{Author} %X{dc:format}";

  alert(md.strf(str3));

The strf function requires a couple of strf functions from stdlib.js, and I should probably document the interesting stuff, but everything is there.

There is not Bridge support because using File is probably as fast or faster and doesn't need to have Bridge running.

But I may add Bridge support just because it might be an interesting exercise.

Inspiring
April 15, 2015

One quick note: If you want metadata.js to work with Files, you need to first //@include this file:

http://ps-scripts.cvs.sourceforge.net/viewvc/ps-scripts/xtools/xlib/XMPTools.jsx

If handles all of the code that interfaces with XMPScript.

-X

BobXXYBAuthor
Inspiring
August 9, 2015

I upgraded my O.S. hard drive to a solid state drive, and I also upgraded to Windows 8 (and again to Windows 10 last week).  Bridge seems to pass the information much better now.

BobXXYBAuthor
Inspiring
March 30, 2015

Philip,

The script worked mostly as intended.  On the first try, the alert box is empty, but on the second try, it worked.  It seems Bridge needs to be on the folder at the beginning of the script.  If it has to change folders to display /desktop/test in the content pane, it doesn't get the exif info back to Photoshop.  I'm thinking there has to be another function, maybe a call back function, to get Bridge to the right place before getting the info.

I'll have to look into the Info data type (?) and the "push" and "join" methods, which I have not tried before. For some reason, the values returned from functions I've used did not get back to Photoshop, but there's a lot about the scripting that I don't understand.

Thanks for your help!

[edit] I now realize Info is an Array object that has "push" and "join" methods.  Still not sure what I was doing wrong before, but maybe it simply not putting a "return" statement at the end of the function.

-Bob

BobXXYBAuthor
Inspiring
March 31, 2015

I was doing the following wrong:

1) not making sure the returned object was a string

2) not being aware of the timeout value in bt.send(timeout_value)

3) using $.writeln commands in the bt.body.  They seem to hang the execution of the function to be evaluated.

Now, I'm wondering if there's any way to get the result of the execution of the script back into the main() function.  For now, there seems to be no way of extracting it from the bt.onResult function unless you write it out to a file.

-Bob

Inspiring
March 31, 2015

Hi Bob, if Bridge was closed then no info would be returned, so make sure Bridge is running.

Here is the script with slight modifications showing how to process the data in the main function.

#target photoshop;

main();

function main(){

var bt = new BridgeTalk();

cameraRawData = new Object();

bt.target = "bridge";

bt.body = "var ftn = " + getInfoFromBridge.toSource() + "; ftn();";

bt.onResult = function( returnedData ) {  cameraRawData = eval( returnedData.body );}

bt.onError = function( sumatWentWrong ) {  alert(sumatWentWrong.body);}

bt.send(10);

//If you get here all should be well and you can process the data

for(var a = 0; a< cameraRawData.Name.length; a++){

$.writeln(cameraRawData.Name + " - " + cameraRawData.FNumber);

}

};

function getInfoFromBridge(){

/* all comments in this function must be in this format ! */

Info = {}; /* new Object, can be written as var Info = new Object(); */

/* create new arrays within the object */

Info.Name = new Array();

Info.FNumber = new Array();

/* change folder to */

app.document.thumbnail = Folder("~/desktop/test");

app.document.deselectAll();

/*get  a list of all cr2 documents */

var thumbs = app.document.getSelection("cr2");

for(var a in thumbs){

var t = new Thumbnail(thumbs);   

var md = t.synchronousMetadata;

/*populate the arrays */

Info.Name.push(thumbs.name);

Info.FNumber.push(md.read( "http://ns.adobe.com/exif/1.0/","exif:FNumber"));

}

return Info.toSource();

};

BobXXYBAuthor
Inspiring
March 30, 2015

Looks like I'm running up against a problem with BridgeTalk between Photoshop and Bridge.  Bridge is not returning messages for Bridgetalk.OnResult to evaluate.  I can use the supplied SndSendArray.jsx script and see that it works, but I cannot do it backwards.

I have goneCorrect answer
Inspiring
March 30, 2015

This example works for me with CS6 (don't have CC)

Create a "test" folder off the desktop and place a few cr2 files in the folder and try the following script.

#target photoshop;

main();

function main(){

var bt = new BridgeTalk();

bt.target = "bridge";

bt.body = "var ftn = " + getInfoFromBridge.toSource() + "; ftn();";

bt.onResult = function( info ) { processInfo(eval( info.body ));}

bt.send(10);

};

function processInfo(info){

//do whatever to data

alert(info.join('\n'));

};

function getInfoFromBridge(){

var Info = [];

app.document.thumbnail = Folder("~/desktop/test");

app.document.deselectAll();

var thumbs = app.document.getSelection("cr2");

for(var a in thumbs){

var t = new Thumbnail(thumbs);   

var md = t.synchronousMetadata;

Info.push([[thumbs.name],[md.read( "http://ns.adobe.com/exif/1.0/","exif:FNumber")]]);

}

return Info.toSource();

};

BobXXYBAuthor
Inspiring
March 30, 2015

That looks promising.  I'll give it a shot when I'm back at my home desktop this evening.

There's a lot about Javascript I don't know.  Certainly, it might be better to ask questions here much earlier in the process rather than spending most of the weekend frustrated .  Thanks for your help!  I'll let you know how it works.