Copy link to clipboard
Copied
I shoot about 1000 pictures a day and Bridge takes 10-15 minutes to analyze and put them into stacks (and even then, it misses some).
Is it possible to write a script that just stacked every image shot within 10 seconds of each other (or some other arbitrary time period).
thanks
Please give this a try, it should be within 10 seconds.
To use: Stacks - Ten Second Stack
(Might take a bit longer than 10 seconds )
...#target bridge;
if( BridgeTalk.appName == "bridge" ) {
tenStack = MenuElement.create("command", "Ten Second Stack", "at the end of submenu/Stack");
}
tenStack.onSelect = function () {
//Change to suit
///////////////////////////////////
var Secs = 10; ///
//////////////////////////////////
Secs = Secs * 1000;
if (ExternalObject.AdobeXMPScript == undefined) Ext
Copy link to clipboard
Copied
Please give this a try, it should be within 10 seconds.
To use: Stacks - Ten Second Stack
(Might take a bit longer than 10 seconds )
#target bridge;
if( BridgeTalk.appName == "bridge" ) {
tenStack = MenuElement.create("command", "Ten Second Stack", "at the end of submenu/Stack");
}
tenStack.onSelect = function () {
//Change to suit
///////////////////////////////////
var Secs = 10; ///
//////////////////////////////////
Secs = Secs * 1000;
if (ExternalObject.AdobeXMPScript == undefined) ExternalObject.AdobeXMPScript = new ExternalObject("lib:AdobeXMPScript");
var folder1 = Folder(app.document.presentationPath);
var fileList = folder1.getFiles(/\.(jpg|jpe|jpeg|gif|eps|dng|bmp|tif|tiff|psd|crw|cr2|rle|dib|cin|dpx|ps|pcd|pict|vda|icb|vst|wbm|sct|pbm|flm|psb|exr|pcx|pdp|nef|dcr|dc2|erf|raf|orf|tga|mrw|mos|srf|pic|pct|pxr|pdd|pef|png|x3f|raw|rw2)$/i);
var fileArray = new Array();
for(var a in fileList){
var md = new Thumbnail(fileList).synchronousMetadata;
md.namespace = "http://ns.adobe.com/xap/1.0/";
var dateTime = new XMPDateTime(new Thumbnail(fileList).metadata.read("http://ns.adobe.com/exif/1.0/","DateTimeOriginal")).getDate().getTime();
fileArray.push([[decodeURI(fileList.name)],[dateTime]]);
}
fileArray = fileArray.sort(function(a,b){return a[1]-b[1];});
while( fileArray.length > 2){
count=0;
tmpArray=[];
tmpArray.push(fileArray.shift());
cmp = Number(tmpArray[0][1]) + Secs;
count = getCount(cmp);
for(var y =0;y <count;y++){
tmpArray.push(fileArray.shift());
}
if(count > 0) {
app.document.deselectAll();
for(var s in tmpArray){
var splitIt=tmpArray
.toString().split(',');app.document.select(new Thumbnail(new File(app.document.presentationPath + "/" + splitIt[0])));
}
app.document.chooseMenuItem("StackGroup");
}
}
function getCount(){
count=0;
for(var g = 0;g<fileArray.length;g++){
if(Number(fileArray
[1]) <= cmp){ count++;
}else{break;}
}
return count;
};
};
Copy link to clipboard
Copied
Fantastic as usual, Phillip!
Not as fast as lightroom - but it does do the trick. It took 5 minutes on 500 pics. 11 minutes on 800.
It also behaves differently than lightroom in that it stacks all images taken in the 10 second window of the initial photo (which is what I wanted) Lightroom will stack anything taken within 10- seconds of the last photo in the stack, often resulting in the stacking of 2 unrelated photos.
thank you so much.
Copy link to clipboard
Copied
HI Philip, I have one last problem. If shooting with a friend, our pictures get stacked together via capture time.
Our pictures do have different "custom text plus sequence" names. Is it possible to read file names and only stack them if they start with the same 3 or 4 letters (or numbers)
I don't know if this language has the ability to read file names as strings and compare?
Copy link to clipboard
Copied
If there are only two cameras it might be best to to stack them on time and camera serial number.
I will have a look at this this weekend.
Copy link to clipboard
Copied
Could you please test this code, it should now support two cameras.
On a test with 350 CR2 files on the old laptop (window 7, Bridge CS6) it took eight seconds.
#target bridge;
if( BridgeTalk.appName == "bridge" ) {
tenStack = MenuElement.create("command", "Ten Second Stack", "at the end of submenu/Stack");
}
tenStack.onSelect = function () {
//Change to suit
///////////////////////////////////
var Secs = 10; ///
//////////////////////////////////
Secs = Secs * 1000;
if (ExternalObject.AdobeXMPScript == undefined) ExternalObject.AdobeXMPScript = new ExternalObject("lib:AdobeXMPScript");
var folder1 = Folder(app.document.presentationPath);
var fileList = folder1.getFiles(/\.(jpg|jpe|jpeg|gif|eps|dng|bmp|tif|tiff|psd|crw|cr2|rle|dib|cin|dpx|ps|pcd|pict|vda|icb|vst|wbm|sct|pbm|flm|psb|exr|pcx|pdp|nef|dcr|dc2|erf|raf|orf|tga|mrw|mos|srf|pic|pct|pxr|pdd|pef|png|x3f|raw|rw2)$/i);
var fileArray = new Array();
for(var a in fileList){
var md = new Thumbnail(fileList).synchronousMetadata;
md.namespace = "http://ns.adobe.com/xap/1.0/";
var dateTime = new XMPDateTime(new Thumbnail(fileList).metadata.read("http://ns.adobe.com/exif/1.0/","DateTimeOriginal")).getDate().getTime();
var cameraSerial = md.read("http://ns.adobe.com/exif/1.0/aux/","aux:SerialNumber");
fileArray.push([[decodeURI(fileList.name)],[dateTime],[cameraSerial]]);
}
fileArray = fileArray.sort(function(a,b){return a[1]-b[1];});
var Camera1=[];
var Camera2=[];
var firstCamera = Number(fileArray[0][2]);
for(var c in fileArray){
if(Number(fileArray
[2]) == Number(firstCamera)){ Camera1.push([[fileArray
[0].toString()],[Number(fileArray [1])]]); }else{
Camera2.push([[fileArray
[0].toString()],[Number(fileArray [1])]]); }
}
fileArray=[];
while( Camera1.length > 2){
count=0;
tmpArray=[];
tmpArray.push(Camera1.shift());
cmp = Number(tmpArray[0][1]) + Secs;
for(var g = 0;g<Camera1.length;g++){
if(Number(Camera1[0][1]) <= cmp){
tmpArray.push(Camera1.shift());
}else{break;}
}
if(tmpArray.length > 1) Stack();
}
while( Camera2.length > 2){
count=0;
tmpArray=[];
tmpArray.push(Camera2.shift());
cmp = Number(tmpArray[0][1]) + Secs;
for(var g = 0;g<Camera2.length;g++){
if(Number(Camera2[0][1]) <= cmp){
tmpArray.push(Camera2.shift());
}else{break;}
}
if(tmpArray.length > 1) Stack();
}
function Stack(){
app.document.deselectAll();
for(var s in tmpArray){
var splitIt=tmpArray
.toString().split(',');app.document.select(new Thumbnail(new File(app.document.presentationPath + "/" + splitIt[0])));
}
app.document.chooseMenuItem("StackGroup");
}
};
Copy link to clipboard
Copied
Works!
Although, I didn't get a chance to reply prior to your mentioning "only 2 cameras". We actually run 5-8 cameras some days.
I'm reading this code and wondering if I can duplicate those 2 while loops for additional cameras, provided I add the extra variables?
Copy link to clipboard
Copied
Duplicating the loops won't help, i will set up a few files and try again. Watch this space.
Copy link to clipboard
Copied
Okay this version should cope with any number of camera's.
Please give this one a try.
#target bridge;
if( BridgeTalk.appName == "bridge" ) {
tenStackII = MenuElement.create("command", "Ten Second Stack", "at the end of submenu/Stack");
}
tenStack.onSelect = function () {
//////////////////////////////////
var Secs = 10;
//////////////////////////////////
Secs = Secs * 1000;
if (ExternalObject.AdobeXMPScript == undefined) ExternalObject.AdobeXMPScript = new ExternalObject("lib:AdobeXMPScript");
var folder1 = Folder(app.document.presentationPath);
var fileList = folder1.getFiles(/\.(jpg|jpe|jpeg|gif|eps|dng|bmp|tif|tiff|psd|crw|cr2|rle|dib|cin|dpx|ps|pcd|pict|vda|icb|vst|wbm|sct|pbm|flm|psb|exr|pcx|pdp|nef|dcr|dc2|erf|raf|orf|tga|mrw|mos|srf|pic|pct|pxr|pdd|pef|png|x3f|raw|rw2)$/i);
var fileArray = new Array();
for(var a in fileList){
var md = new Thumbnail(fileList).synchronousMetadata;
md.namespace = "http://ns.adobe.com/xap/1.0/";
var dateTime = new XMPDateTime(new Thumbnail(fileList).metadata.read("http://ns.adobe.com/exif/1.0/","DateTimeOriginal")).getDate().getTime();
var cameraSerial = md.read("http://ns.adobe.com/exif/1.0/aux/","aux:SerialNumber");
fileArray.push([[decodeURI(fileList.name)],[dateTime],[cameraSerial]]);
}
fileArray = fileArray.sort(function(a,b){return a[1]-b[1];});
var serNos = new Array();
for(var s in fileArray){serNos.push(Number(fileArray
[2]));}serNos = uniqueSortedList(serNos);
var Camera = new Object();
for(var g in serNos){Camera[serNos
] = new Array();}; for(var d in fileArray){
Camera[fileArray
[2]].push([[fileArray [0].toString()],[Number(fileArray [1])]]); }
fileArray=[];
//for (var z in serNos){$.writeln(Camera[serNos
].length);} for(var k in serNos){
var Camera1 = Camera[serNos
]; while( Camera1.length > 2){
count=0;
tmpArray=[];
tmpArray.push(Camera1.shift());
cmp = Number(tmpArray[0][1]) + Secs;
for(var g = 0;g<Camera1.length;g++){
if(Number(Camera1[0][1]) <= cmp){
tmpArray.push(Camera1.shift());
}else{break;}
}
if(tmpArray.length > 1) Stack();
}
}
function Stack(){
app.document.deselectAll();
for(var s in tmpArray){
var splitIt=tmpArray
.toString().split(',');app.document.select(new Thumbnail(new File(app.document.presentationPath + "/" + splitIt[0])));
}
app.document.chooseMenuItem("StackGroup");
}
function uniqueSortedList(array){
var unduped = new Object;
for (var i in array) {unduped[array] = array;}
var uniques = new Array;
for (var k in unduped) {uniques.push(unduped
);} return uniques.sort();
};
};
Copy link to clipboard
Copied
Hi phillip,
the first time i ran it, it threw an error (I think it was related to line 3 saying tenStackII - so I changed that to tenStack however then it didn't run.
Copy link to clipboard
Copied
Sorry about that, I have added a couple of checks so hopefully it should now work with any number of camera's.
#target bridge;
if( BridgeTalk.appName == "bridge" ) {
tenStack = MenuElement.create("command", "Ten Second Stack", "at the end of submenu/Stack");
}
tenStack.onSelect = function () {
//////////////////////////////////
var Secs = 10;
//////////////////////////////////
Secs = Secs * 1000;
if (ExternalObject.AdobeXMPScript == undefined) ExternalObject.AdobeXMPScript = new ExternalObject("lib:AdobeXMPScript");
var folder1 = Folder(app.document.presentationPath);
var fileList = folder1.getFiles(/\.(jpg|jpe|jpeg|gif|eps|dng|bmp|tif|tiff|psd|crw|cr2|rle|dib|cin|dpx|ps|pcd|pict|vda|icb|vst|wbm|sct|pbm|flm|psb|exr|pcx|pdp|nef|dcr|dc2|erf|raf|orf|tga|mrw|mos|srf|pic|pct|pxr|pdd|pef|png|x3f|raw|rw2)$/i);
var fileArray = new Array();
for(var a in fileList){
var md = new Thumbnail(fileList).synchronousMetadata;
try{
var dateTime = new XMPDateTime(new Thumbnail(fileList).metadata.read("http://ns.adobe.com/exif/1.0/","DateTimeOriginal")).getDate().getTime();
}catch(e){dateTime = 1;}
var cameraSerial = md.read("http://ns.adobe.com/exif/1.0/aux/","aux:SerialNumber");
if(Number(cameraSerial > 100 && Number(dateTime) > 1 )) fileArray.push([[decodeURI(fileList.name)],[dateTime],[cameraSerial]]);
}
fileArray = fileArray.sort(function(a,b){return a[1]-b[1];});
var serNos = new Array();
for(var s in fileArray){serNos.push(Number(fileArray
[2]));}serNos = uniqueSortedList(serNos);
var Camera = new Object();
for(var g in serNos){Camera[serNos
] = new Array();}; for(var d in fileArray){
Camera[fileArray
[2]].push([[fileArray [0].toString()],[Number(fileArray [1])]]); }
fileArray=[];
for(var k in serNos){
var Camera1 = Camera[serNos
]; while( Camera1.length > 2){
count=0;
tmpArray=[];
tmpArray.push(Camera1.shift());
cmp = Number(tmpArray[0][1]) + Secs;
for(var g = 0;g<Camera1.length;g++){
if(Number(Camera1[0][1]) <= cmp){
tmpArray.push(Camera1.shift());
}else{break;}
}
if(tmpArray.length > 1) Stack();
}
}
function Stack(){
app.document.deselectAll();
for(var s in tmpArray){
var splitIt=tmpArray
.toString().split(',');app.document.select(new Thumbnail(new File(app.document.presentationPath + "/" + splitIt[0])));
}
app.document.chooseMenuItem("StackGroup");
}
function uniqueSortedList(array){
var unduped = new Object;
for (var i in array) {unduped[array] = array;}
var uniques = new Array;
for (var k in unduped) {uniques.push(unduped
);} return uniques.sort();
}
};
Copy link to clipboard
Copied
You need not apologize, I am forever grateful for your help.
Although, for whatever reason, it's not running (clicking 10 second stack doens't seem to do anything.)
Copy link to clipboard
Copied
I am not sure why it's not working for you, just added another check to make sure the files have Exif Info also altered the naming so there should be no conflicts.
I have tested the code on a folder full of files from one camera and another folder with files from nine cameras, also ran it on a folder of jpgs with no exif which gave me the alert.
#target bridge;
if( BridgeTalk.appName == "bridge" ) {
tenStackII = MenuElement.create("command", "Ten Second Stack II", "at the end of submenu/Stack");
}
tenStackII.onSelect = function () {
//////////////////////////////////
var Secs = 10;
//////////////////////////////////
Secs = Secs * 1000;
if (ExternalObject.AdobeXMPScript == undefined) ExternalObject.AdobeXMPScript = new ExternalObject("lib:AdobeXMPScript");
var folder1 = Folder(app.document.presentationPath);
var fileList = folder1.getFiles(/\.(jpg|jpe|jpeg|gif|eps|dng|bmp|tif|tiff|psd|crw|cr2|rle|dib|cin|dpx|ps|pcd|pict|vda|icb|vst|wbm|sct|pbm|flm|psb|exr|pcx|pdp|nef|dcr|dc2|erf|raf|orf|tga|mrw|mos|srf|pic|pct|pxr|pdd|pef|png|x3f|raw|rw2)$/i);
var fileArray = new Array();
for(var a in fileList){
var md = new Thumbnail(fileList).synchronousMetadata;
try{
var dateTime = new XMPDateTime(new Thumbnail(fileList).metadata.read("http://ns.adobe.com/exif/1.0/","DateTimeOriginal")).getDate().getTime();
}catch(e){dateTime = 1;}
var cameraSerial = md.read("http://ns.adobe.com/exif/1.0/aux/","aux:SerialNumber");
if(Number(cameraSerial > 100 && Number(dateTime) > 1 )) fileArray.push([[decodeURI(fileList.name)],[dateTime],[cameraSerial]]);
}
if(fileArray.length < 2){
alert("There are no files with Exif Info in this folder");
return;
}
fileArray = fileArray.sort(function(a,b){return a[1]-b[1];});
var serNos = new Array();
for(var s in fileArray){serNos.push(Number(fileArray
[2]));}serNos = uniqueSortedList(serNos);
var Camera = new Object();
for(var g in serNos){Camera[serNos
] = new Array();}; for(var d in fileArray){
Camera[fileArray
[2]].push([[fileArray [0].toString()],[Number(fileArray [1])]]); }
fileArray=[];
for(var k in serNos){
var Camera1 = Camera[serNos
]; while( Camera1.length > 2){
count=0;
tmpArray=[];
tmpArray.push(Camera1.shift());
cmp = Number(tmpArray[0][1]) + Secs;
for(var g = 0;g<Camera1.length;g++){
if(Number(Camera1[0][1]) <= cmp){
tmpArray.push(Camera1.shift());
}else{break;}
}
if(tmpArray.length > 1) Stack();
}
}
function Stack(){
app.document.deselectAll();
for(var s in tmpArray){
var splitIt=tmpArray
.toString().split(',');app.document.select(new Thumbnail(new File(app.document.presentationPath + "/" + splitIt[0])));
}
app.document.chooseMenuItem("StackGroup");
}
function uniqueSortedList(array){
var unduped = new Object;
for (var i in array) {unduped[array] = array;}
var uniques = new Array;
for (var k in unduped) {uniques.push(unduped
);} return uniques.sort();
}
};
Copy link to clipboard
Copied
Gotcha. thanks for adding the alert.
I just figured out the problem - sony cameras (at least the a77) do not record the serial number in the exif data!
I will try to find a way to add it tonight.
Copy link to clipboard
Copied
Hi Philip, so it looks like I can't add the serial number in lightroom. I can add IPTC info like "copyright" or "creator" (which reads as "artist in the lightroom library module) upon import which could differentiate the cameras.
Can we read one of these instead of the serial number (i can't figure out the syntax - I tried going to the site referenced in the script, but couldn't access it)
Copy link to clipboard
Copied
Could you please give this script a try.
It need all the files selected that want a serial number, before running the script from the Tools menu.
You can put any number you wish.
N.B. This can only be done once as this field is normally read only, but if it dose not exist it can be created.
#target bridge;
if( BridgeTalk.appName == "bridge" ) {
addCameraNumber = MenuElement.create("command", "Add Camera Number", "at the end of tools");
}
addCameraNumber.onSelect = function () {
var win = new Window('dialog','Add Camera Number');
g = win.graphics;
var myBrush = g.newBrush(g.BrushType.SOLID_COLOR, [0.99, 0.99, 0.99, 1]);
g.backgroundColor = myBrush;
win.alignChildren="row";
win.g10 = win.add('group');
win.g10.orientation = "row";
win.title = win.g10.add('statictext',undefined,"Add Camera Number");
win.title.alignment="bottom";
var g = win.title.graphics;
g.font = ScriptUI.newFont("Georgia","BOLDITALIC",26);
win.p1= win.add("panel", undefined, undefined, {borderStyle:"black"});
win.p1.alignChildren="fill";
win.g3 =win.p1.add('group');
win.g3.orientation = "row";
win.g3.alignment="left";
win.g3.st1 = win.g3.add('statictext',undefined,'Enter Camera Number');
win.g3.et1 = win.g3.add('edittext',undefined,'123456789');
win.g3.et1.active=true;
win.g3.et1.preferredSize=[120,20];
win.g3.et1.onChanging = function() {
if (this.text.match(/[^\-\.\d]/)) {
this.text = this.text.replace(/[^\-\.\d]/g, '');
}
};
win.g150 =win.p1.add('group');
win.g150.spacing=10;
win.g150.orientation = 'row';
win.g150.alignment="top";
win.g150.bu1 = win.g150.add('button',undefined,"Process");
win.g150.bu1.preferredSize=[120,30];
win.g150.bu2 = win.g150.add('button',undefined,"Cancel");
win.g150.bu2.preferredSize=[120,30];
win.g150.bu1.onClick = function(){
if(win.g3.et1.text == ''){
alert("A Camera Number Needs To Be Entered!");
return;
}
var sels = app.document.selections;
if(sels.length < 1){
alert("No documents have been selected!");
win.close(2);
}
var cameraserial =Number(win.g3.et1.text);
win.close(0);
for(var a in sels){
if(sels.type != "file") continue;
var md = sels.synchronousMetadata;
md.namespace = "http://ns.adobe.com/exif/1.0/aux/";
md.SerialNumber = cameraserial;
}
}
win.show();
};
Copy link to clipboard
Copied
Just had a thought, the above script should work on all but raw files, if you were using raw files this might work? It adds the Serial number to the xmp file, creating one if it dose not exist.
#target bridge;
if( BridgeTalk.appName == "bridge" ) {
addCameraNumberII = MenuElement.create("command", "Add Camera NumberII", "at the end of tools");
}
addCameraNumberII.onSelect = function () {
var win = new Window('dialog','Add Camera Number');
g = win.graphics;
var myBrush = g.newBrush(g.BrushType.SOLID_COLOR, [0.99, 0.99, 0.99, 1]);
g.backgroundColor = myBrush;
win.alignChildren="row";
win.g10 = win.add('group');
win.g10.orientation = "row";
win.title = win.g10.add('statictext',undefined,"Add Camera Number");
win.title.alignment="bottom";
var g = win.title.graphics;
g.font = ScriptUI.newFont("Georgia","BOLDITALIC",26);
win.p1= win.add("panel", undefined, undefined, {borderStyle:"black"});
win.p1.alignChildren="fill";
win.g3 =win.p1.add('group');
win.g3.orientation = "row";
win.g3.alignment="left";
win.g3.st1 = win.g3.add('statictext',undefined,'Enter Camera Number');
win.g3.et1 = win.g3.add('edittext',undefined,'123456789');
win.g3.et1.active=true;
win.g3.et1.preferredSize=[120,20];
win.g3.et1.onChanging = function() {
if (this.text.match(/[^\-\.\d]/)) {
this.text = this.text.replace(/[^\-\.\d]/g, '');
}
};
win.g150 =win.p1.add('group');
win.g150.spacing=10;
win.g150.orientation = 'row';
win.g150.alignment="top";
win.g150.bu1 = win.g150.add('button',undefined,"Process");
win.g150.bu1.preferredSize=[120,30];
win.g150.bu2 = win.g150.add('button',undefined,"Cancel");
win.g150.bu2.preferredSize=[120,30];
win.g150.bu1.onClick = function(){
if(win.g3.et1.text == ''){
alert("A Camera Number Needs To Be Entered!");
return;
}
var sels = app.document.selections;
if(sels.length < 1){
alert("No documents have been selected!");
win.close(2);
}
var cameraserial =Number(win.g3.et1.text);
win.close(0);
for(var a in sels){
if(sels.type != "file") continue;
setCameraNo(sels, cameraserial );
}
}
win.show();
function setCameraNo( thumb, SerialNumber ){
if(SerialNumber == undefined) return;
var file = new File(thumb.spec.toString().replace(/...$/,"xmp"));
try{
if (ExternalObject.AdobeXMPScript == undefined) ExternalObject.AdobeXMPScript = new ExternalObject("lib:AdobeXMPScript");
if(file.exists){
file.open('r');
file.encoding = "UTF8";
file.lineFeed = "unix";
file.open("r", "TEXT", "????");
var xmpStr = file.read();
file.close();
}else{
var xmpStr='';
}
var xmp = new XMPMeta( xmpStr );
xmp.setProperty(XMPConst.NS_EXIF_AUX, "SerialNumber",SerialNumber);
file.open('w');
file.encoding = "UTF8";
file.lineFeed = "unix";
file.write( xmp.serialize() );
file.close();
}catch(e){alert(e+"-"+e.line);}
};
};