• Global community
    • Language:
      • Deutsch
      • English
      • Español
      • Français
      • Português
  • 日本語コミュニティ
    Dedicated community for Japanese speakers
  • 한국 커뮤니티
    Dedicated community for Korean speakers
Exit
1

Is scripting the only way to align about 4000 image pairs?

Explorer ,
Nov 19, 2023 Nov 19, 2023

Copy link to clipboard

Copied

Hello,

 

I have 4000 image pairs, numbered and named like this: "0000_FIXED.png" and "0000_MOVING.png", etc.

and I need to align them in Photoshop. By hand, I can load the image pairs into a stack and indicate that they should be aligned, then I save the layers of the stack out as separate aligned files. It's just three steps, which is great for a few files.

 

The problem comes in when I try and use Action and Batch functions to process all 4000 pairs. Batch seems to have no way of referring to two input files per batch interation. I had hoped there was a "counting token" or something like that available where files could be referred like "[token_ctr]_FIXED.png" and "[token_ctr]_MOVING.png" and similarly when saving the final aligned layers.

 

But Batch doesn't seem to be able to do anything like that, directly at least. It's unaware of the numbering of the input files, it just accesses the files in the input folder singly in order, one by one per batch iteration. It can add a number field to the output images, but also is unable to save off more than a single numbered file each iteration and the number increments each iteration.

 

If I'm missing something in Batch and Actions capabilities, I'd appreciate hearing of a Batch/Action-oriented solution. Alternatively, would anyone be able to say definitively that that approach can't work and the only remaining avenue is scripting?

 

I can program in VBA, but don't really want to learn how to do this for Photoshop if I can avoid it. I'm not going to be able to avoid it, though, am I? <|:-)>

 

Thank you for any help with this, I appreciate it.

 

Ian J.

TOPICS
Actions and scripting

Views

677

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines

correct answers 2 Correct answers

Explorer , Nov 19, 2023 Nov 19, 2023

Hi Stephen,

I should be able to do the last bit with an Action, I believe.

Thank you, I really appreciate your helping me with this.

Ian J.

Votes

Translate

Translate
Community Expert , Nov 19, 2023 Nov 19, 2023

@Ian Jaffray 

 

Here is an updated script to save each auto aligned layer as a PNG file using a suffix of "_ALIGNED". You can just change line 100 from:

 

var suffix = "_ALIGNED";

 

To:

 

var suffix = "";

 

To remove the suffix altogether (or change it to whatever you want within the quote marks).

 

I would generally advise that you work in smaller batches than 4K, perhaps just 1K (or fewer) input files at a time... But you might luck out and run all 4K input files in one go without hitting memo

...

Votes

Translate

Translate
Adobe
Community Expert ,
Nov 19, 2023 Nov 19, 2023

Copy link to clipboard

Copied

@Ian Jaffray I have some generic "framework" scripts to quickly create scripts for such tasks.

 

Are the input files in a single folder, or two separate folders?

 

Can you post a screenshot of the layers panel so that I can see the stacking?

 

Can you record an action to do what you require once the paired files are layered/stacked?

 

The script can run the action on the stacked files, then it can save using your desired file format and options, naming, location etc.

 

The more detail that you can provide the better.

 

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Explorer ,
Nov 19, 2023 Nov 19, 2023

Copy link to clipboard

Copied

Hi Stephen,

Thanks for taking a look at this, I appreciate the help.

The input image file pairs are currently in a single folder but could be separated into _"FIXED" and "_MOVING" folders. They also could, in those separate folders, be stripped of anything other than the incrementing number "0000" to "3999".

 

A built-in-to Photoshop script, "Load Files into Stack", takes the two hand-specified image files and then aligns them (because "Attempt To Automatically Align Source Images" is checked.

IanJaffray_0-1700430446389.png

 

Below is the Layers panel after the above script loads the two source images and aligns them. As part of that, the Background layer automatically gets changed to Layer 0, as a Background layer can't be aligned. The images on the two layers are now aligned and ready to be saved separately.

IanJaffray_4-1700430821104.png

 

Now the two aligned files on separate layers can be saved using "Export Layers To Files".

IanJaffray_5-1700430927904.png

This all works because I'm specifying the two input files by hand, something that can't be readily done when 4K files are involved.

 

I don't think the "Export Layers To Files" part of the operation poses a problem while using Actions/Batch, it's the first step of opening the pairs of image files, something that I haven't had any success doing. The results of the "Export Layers To Files" command, in this case, creates two files:

     Untitled-1_0000_0000_MOVING.png.png"
     Untitled-1_0001_0000_FIXED.png.png"

Those results would be fine, renamed of course, but again, I can't figure out how to do the loading of two paired images into the stack automatically. Thus, I'm wondering if a script might be the only way to go.

 

Thanks again, Stephen, for your help.

Ian J.

 

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Nov 19, 2023 Nov 19, 2023

Copy link to clipboard

Copied

My script will automatically stack the pairs and save, the sticking point is the auto alignment as that will not be using the standard load files script.

 

Can you post samples of 2 or 3 pairs for testing? Either to the forum or via private message.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Explorer ,
Nov 19, 2023 Nov 19, 2023

Copy link to clipboard

Copied

Stephen,

Yeah, the aligning part is the necessary part that is proving difficult.

I've attached 3 pairs of files.

Thanks!

Ian J.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Nov 19, 2023 Nov 19, 2023

Copy link to clipboard

Copied

@Ian Jaffray 

 

Here is a v1.0 draft script, the auto-alignment is slightly different to the "Load files into stack" script, I'll take a look at that when I have time. I'm hoping it is OK. It's currently using the "auto" method from Edit > Auto-align-layers and if you have a preferred configuration just take a screenshot and I'll change it over to see if that provides a better result. Note that there may be some transparency at the layer edges due to the auto-alignment.

 

At the moment the script just saves out a layered PSD, as I want your feedback before putting in any further work. There is no point moving on if you are not happy with the auto-align currently in use. If you are happy, then each layer can be saved separately to PNG.

 

/* 
Auto Align Image Pairs.jsx
https://community.adobe.com/t5/photoshop-ecosystem-discussions/is-scripting-the-only-way-to-align-about-4000-image-pairs/td-p/14244613
v1.0 - 20th November 2023, Stephen Marsh
Based on:
Stack N Number of Document Sets to Layers - Top Level Folder.jsx
20th January 2023 Version, Stephen Marsh
*/

#target photoshop

// app.documents.length === 0
if (!app.documents.length) {

    try {

        // Save and disable dialogs
        var restoreDialogMode = app.displayDialogs;
        app.displayDialogs = DialogModes.NO;

        // Main script function
        (function () {

            // Select the input folder
            var inputFolder = Folder.selectDialog('Please select the folder with files to process');
            if (inputFolder === null) return;

            // Limit the file format input, add or remove as required
            var fileList = inputFolder.getFiles(/\.(png|jpg|jpeg|tif|tiff|psd|psb|webp)$/i);

            // Force alpha-numeric list sort
            // Use fileList.sort().reverse(); for the first filename in the merged file
            fileList.sort();

            // Static Set Quantity
            var setQty = 2;

            // Validate that the file list is not empty
            var inputCount = fileList.length;
            var cancelScript1 = (inputCount === 0);
            if (cancelScript1 === true) {
                alert('Zero input files found, script cancelled!');
                return;
            }
            // Validate the input count vs. output count - Thanks to Kukurykus for the advice to test using % modulus
            var cancelScript2 = !(inputCount % setQty);
            alert(inputCount + ' input files stacked into sets of ' + setQty + ' will produce ' + inputCount / setQty + ' output files.');
            // Test if false, then terminate the script
            if (cancelScript2 === false) {
                alert('Script cancelled as the quantity of input files are not evenly divisible by the set quantity.');
                return;
            }

            // Select the output folder
            var outputFolder = Folder.selectDialog("Please select the folder to save to");
            if (outputFolder === null) {
                alert('Script cancelled!');
                return;
            }

            // or

            /*
            // Create the output sub-directory
            var outputFolder = Folder(decodeURI(inputFolder + '/Output Sets Folder'));
            if (!outputFolder.exists) outputFolder.create();
            */

            // Set the file processing counter
            var fileCounter = 0;

            // Loop through and open the file sets
            while (fileList.length) {
                // Sets of N quantity files
                for (var a = 0; a < setQty; a++) {
                    try {
                        app.open(fileList.pop());
                    } catch (e) { }
                }

                // Set the base doc layer name
                app.activeDocument = documents[0];
                docNameToLayerName();

                // Stack all open docs to the base doc
                while (app.documents.length > 1) {
                    app.activeDocument = documents[1];
                    docNameToLayerName();
                    app.activeDocument.activeLayer.duplicate(documents[0]);
                    app.activeDocument = documents[0];

                    app.runMenuItem(stringIDToTypeID('selectAllLayers'));
                    autoAlign();

                    app.documents[1].close(SaveOptions.DONOTSAVECHANGES);
                }

                // Delete XMP metadata to reduce final file size of output files
                //removeXMP();

                // Save name + suffix & save path
                var Name = app.activeDocument.name.replace(/(\d+)(.+)(\.[^\.]+$)/, '$1').replace(/\.[^\.]+$/, '');
                var saveFile = File(outputFolder + '/' + Name + '_x' + setQty + '-Sets' + '.psd');
                // var saveFile = File(outputFolder + '/' + Name + '_x' + setQty + '-Sets' + '.jpg');

                // Call the save function
                savePSD(saveFile);
                //savePNG(saveFile);

                // Close all open files without saving
                while (app.documents.length) {
                    app.activeDocument.close(SaveOptions.DONOTSAVECHANGES);
                }

                // Increment the file saving counter
                fileCounter++;


                ///// Functions /////

                function savePSD(saveFile) {
                    psdSaveOptions = new PhotoshopSaveOptions();
                    psdSaveOptions.embedColorProfile = true;
                    psdSaveOptions.alphaChannels = true;
                    psdSaveOptions.layers = true;
                    psdSaveOptions.annotations = true;
                    psdSaveOptions.spotColors = true;
                    // Save as
                    app.activeDocument.saveAs(saveFile, psdSaveOptions, true, Extension.LOWERCASE);
                }

                /* Not currently used, a placeholder to swap in/out as needed
                function savePNG(saveFile) {
                    var pngOptions = new PNGSaveOptions();
                    pngOptions.compression = 0; // 0-9
                    pngOptions.interlaced = false;
                    // Save as
                    app.activeDocument.saveAs(saveFile, pngOptions, true, Extension.LOWERCASE);
                }
                */

                function docNameToLayerName() {
                    var layerName = app.activeDocument.name.replace(/\.[^\.]+$/, '');
                    app.activeDocument.activeLayer.name = layerName;
                }

                function removeXMP() {
                    if (!documents.length) return;
                    if (ExternalObject.AdobeXMPScript == undefined) ExternalObject.AdobeXMPScript = new ExternalObject("lib:AdobeXMPScript");
                    var xmp = new XMPMeta(activeDocument.xmpMetadata.rawData);
                    XMPUtils.removeProperties(xmp, "", "", XMPConst.REMOVE_ALL_PROPERTIES);
                    app.activeDocument.xmpMetadata.rawData = xmp.serialize();
                }

                function autoAlign() {
                    var desc = new ActionDescriptor();
                    var ref = new ActionReference();
                    ref.putEnumerated(charIDToTypeID('Lyr '), charIDToTypeID('Ordn'), charIDToTypeID('Trgt'));
                    desc.putReference(charIDToTypeID('null'), ref);
                    desc.putEnumerated(charIDToTypeID('Usng'), charIDToTypeID('ADSt'), stringIDToTypeID('ADSContent'));
                    desc.putEnumerated(charIDToTypeID('Aply'), stringIDToTypeID('projection'), charIDToTypeID('Auto'));
                    desc.putBoolean(stringIDToTypeID('vignette'), false);
                    desc.putBoolean(stringIDToTypeID('radialDistort'), false);
                    executeAction(charIDToTypeID('Algn'), desc, DialogModes.NO);
                }

            }

            // Restore saved dialogs
            app.displayDialogs = restoreDialogMode;

            // End of script notification
            app.beep();
            alert('Script completed!' + '\n' + fileCounter + ' combined files saved to:' + '\n' + outputFolder.fsName);

            // Open the output folder in the Finder or Explorer
            // outputFolder.execute();

        }());

    } catch (e) {
        // Restore saved dialogs
        app.displayDialogs = restoreDialogMode;
        alert("If you see this message, something went wrong!" + "\r" + e + ' ' + e.line);

    }
}

else {
    alert('Auto Align Image Pairs:' + '\n' + 'Please close all open documents before running this script!');
}

 

https://prepression.blogspot.com/2017/11/downloading-and-installing-adobe-scripts.html

 

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Explorer ,
Nov 19, 2023 Nov 19, 2023

Copy link to clipboard

Copied

Dear Stephen,

 

 Wow! Thank you for this. It's dark and cold where I am now, so if you don't mind I will give this the attention it deserves by tomorrow and i will post again. The default "Auto" alignment type is good, I think, and I believe that is the same default going the other route with the "Load files to Stack."

 

That's a lot of code. Where do I send your check?!

 

Thanks tons, Stephen.

 

Ian J.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Explorer ,
Nov 19, 2023 Nov 19, 2023

Copy link to clipboard

Copied

Hi Stephen,

I couldn't wait until tomorrow, so I installed the script you wrote (as AutoAlignImagePairs.jsx) in the C:\Program Files\Adobe\Adobe Photoshop 2024\Presets\Scripts folder, rebooted PS, ran it, and it worked a treat!

Output:

   0000_x2-Sets.psd
   0001_x2-Sets.psd
   0002_x2-Sets.psd

If each of these can be unlayered into two files with a common ordinal (ideally named 0000_FIXED and 0000_MOVING, etc.,  but I imagine there will be a final renaming happening anyway, so anything that distinguishes one image from the next would work) then we are wonderfully done!

 

Thanks, Stephen, this is really great! You've also given me a framework that I can muck about with for further work, a helpful foundational leg up for me on getting familar with scripting.

 

Ian J.

 

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Explorer ,
Nov 19, 2023 Nov 19, 2023

Copy link to clipboard

Copied

Hi Stephen,

I should be able to do the last bit with an Action, I believe.

Thank you, I really appreciate your helping me with this.

Ian J.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Nov 19, 2023 Nov 19, 2023

Copy link to clipboard

Copied

@Ian Jaffray 

 

Here is an updated script to save each auto aligned layer as a PNG file using a suffix of "_ALIGNED". You can just change line 100 from:

 

var suffix = "_ALIGNED";

 

To:

 

var suffix = "";

 

To remove the suffix altogether (or change it to whatever you want within the quote marks).

 

I would generally advise that you work in smaller batches than 4K, perhaps just 1K (or fewer) input files at a time... But you might luck out and run all 4K input files in one go without hitting memory issues.

 

I'm using Save As for the PNG. The compression level is set on line 103. Resolution metadata is retained, however, depending on your version of Photoshop the ICC profile may not be retained (bug). If needed, the Save As code could be swapped out to use Save for Web instead, however, that would strip the resolution metadata but it would retain the ICC profile. 

 

New code replacing the previous script:

 

/* 
Auto Align Image Pair Layers to PNG.jsx
https://community.adobe.com/t5/photoshop-ecosystem-discussions/is-scripting-the-only-way-to-align-about-4000-image-pairs/td-p/14244613
v1.0 - 20th November 2023, Stephen Marsh
Based on:
Stack N Number of Document Sets to Layers - Top Level Folder.jsx
20th January 2023 Version, Stephen Marsh
*/

#target photoshop

// app.documents.length === 0
if (!app.documents.length) {

    try {

        // Save and disable dialogs
        var restoreDialogMode = app.displayDialogs;
        app.displayDialogs = DialogModes.NO;

        // Main script function
        (function () {

            // Select the input folder
            var inputFolder = Folder.selectDialog('Please select the folder with files to process');
            if (inputFolder === null) return;

            // Limit the file format input, add or remove as required
            var fileList = inputFolder.getFiles(/\.(png|jpg|jpeg|tif|tiff|psd|psb|webp)$/i);

            // Force alpha-numeric list sort
            // Use fileList.sort().reverse(); for the first filename in the merged file
            fileList.sort();

            // Static Set Quantity
            var setQty = 2;

            // Validate that the file list is not empty
            var inputCount = fileList.length;
            var cancelScript1 = (inputCount === 0);
            if (cancelScript1 === true) {
                alert('Zero input files found, script cancelled!');
                return;
            }
            // Validate the input count vs. output count - Thanks to Kukurykus for the advice to test using % modulus
            var cancelScript2 = !(inputCount % setQty);
            // Test if false, then terminate the script
            if (cancelScript2 === false) {
                alert('Script cancelled as the quantity of input files are not evenly divisible by 2.');
                return;
            }

            // Select the output folder
            var outputFolder = Folder.selectDialog("Please select the folder to save to");
            if (outputFolder === null) {
                alert('Script cancelled!');
                return;
            }

            // Set the file processing counter
            var fileCounter = 0;

            // Loop through and open the file sets
            while (fileList.length) {
                // Sets of N quantity files
                for (var a = 0; a < setQty; a++) {
                    try {
                        app.open(fileList.pop());
                    } catch (e) { }
                }

                // Set the base doc layer name
                app.activeDocument = documents[0];
                docNameToLayerName();

                // Stack all open docs to the base doc
                while (app.documents.length > 1) {
                    app.activeDocument = documents[1];
                    docNameToLayerName();
                    app.activeDocument.activeLayer.duplicate(documents[0]);
                    app.activeDocument = documents[0];

                    app.runMenuItem(stringIDToTypeID('selectAllLayers'));
                    autoAlign();

                    app.documents[1].close(SaveOptions.DONOTSAVECHANGES);
                }

                // Delete XMP metadata to reduce final file size of output files
                //removeXMP();

                // Loop over layers and save to PNG
                for (var i = 0; i < app.activeDocument.layers.length; i++) {
                    for (var j = 0; j < app.activeDocument.layers.length; j++) {
                        app.activeDocument.layers[j].visible = false;
                    }
                    var layerIndex = i;
                    app.activeDocument.layers[layerIndex].visible = true;
                    var layerName = app.activeDocument.layers[layerIndex].name;
                    var suffix = "_ALIGNED";
                    var file = new File(outputFolder + "/" + layerName + suffix + ".png");
                    var saveOptions = new PNGSaveOptions();
                    saveOptions.compression = 0; // 0-9
                    saveOptions.interlaced = false;
                    app.activeDocument.saveAs(file, saveOptions, true, Extension.LOWERCASE);
                    // Increment the file saving counter
                    fileCounter++;
                }

                // Close all open files without saving
                while (app.documents.length) {
                    app.activeDocument.close(SaveOptions.DONOTSAVECHANGES);
                }


                ///// Functions /////
           
                function docNameToLayerName() {
                    var layerName = app.activeDocument.name.replace(/\.[^\.]+$/, '');
                    app.activeDocument.activeLayer.name = layerName;
                }

                function removeXMP() {
                    if (!documents.length) return;
                    if (ExternalObject.AdobeXMPScript == undefined) ExternalObject.AdobeXMPScript = new ExternalObject("lib:AdobeXMPScript");
                    var xmp = new XMPMeta(activeDocument.xmpMetadata.rawData);
                    XMPUtils.removeProperties(xmp, "", "", XMPConst.REMOVE_ALL_PROPERTIES);
                    app.activeDocument.xmpMetadata.rawData = xmp.serialize();
                }

                function autoAlign() {
                    var desc = new ActionDescriptor();
                    var ref = new ActionReference();
                    ref.putEnumerated(charIDToTypeID('Lyr '), charIDToTypeID('Ordn'), charIDToTypeID('Trgt'));
                    desc.putReference(charIDToTypeID('null'), ref);
                    desc.putEnumerated(charIDToTypeID('Usng'), charIDToTypeID('ADSt'), stringIDToTypeID('ADSContent'));
                    desc.putEnumerated(charIDToTypeID('Aply'), stringIDToTypeID('projection'), charIDToTypeID('Auto'));
                    desc.putBoolean(stringIDToTypeID('vignette'), false);
                    desc.putBoolean(stringIDToTypeID('radialDistort'), false);
                    executeAction(charIDToTypeID('Algn'), desc, DialogModes.NO);
                }

            }

            // Restore saved dialogs
            app.displayDialogs = restoreDialogMode;

            // End of script notification
            app.beep();
            alert('Script completed!' + '\n' + fileCounter + ' PNG files aligned and saved to:' + '\n' + outputFolder.fsName);

            // Open the output folder in the Finder or Explorer
            // outputFolder.execute();

        }());

    } catch (e) {
        // Restore saved dialogs
        app.displayDialogs = restoreDialogMode;
        alert("If you see this message, something went wrong!" + "\r" + e + ' ' + e.line);

    }
}

else {
    alert('Auto Align Image Pair Layers to PNG:' + '\n' + 'Please close all open documents before running this script!');
}

 

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Explorer ,
Nov 20, 2023 Nov 20, 2023

Copy link to clipboard

Copied

Stephen,

Your final code runs great and I couldn't be happier!

Thank you for your work on this, you've exceeded my expectations in every respect.

I'll take your advice on memory use and see what happens.

Ian J.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Nov 20, 2023 Nov 20, 2023

Copy link to clipboard

Copied

LATEST

@Ian Jaffray - You're welcome!

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines