Batch HDR Merging/Toning Script

Community Beginner ,
Aug 17, 2013

Copy link to clipboard

Copied

I've seen a number of different posts on these forums about trying to do this and I never really found a complete and suitable solution. It also has really annoyed me that adobe never implemented this (and it's not possible to do with actions because the action just runs a script and you can't record the HDR toning settings, the dialog always pops up, its in the script). So I finally had some time to get my hands dirty with ExtendScript and I have created a fully featured script complete with UI that allows batch creation of HDR tonned images in photoshop.

The code I got working from various snipets of code I found around the 'net and single stepping through the MergeToHDR.jsx script that is built into photoshop.

This has only been partially tested on photoshop CC on a mac. Hopefully I made it portable enough and it will work on windows and older versions. I don't think the MergeToHDR script has changed much in the last several versions (and that is mainly what I call to do the work) so I think it should work. If you have issues please let me know.

My motivation for this script was coming up with a way to do batch HDR toning for timelapse and so this script's features lean toward using it for that end. I really didn't want to buy another separate program for doing this like photomatix, when I knew it should at least be possible to do in photoshop.

I uploaded the script to github:

https://github.com/davidmilligan/PhotoshopBatchHDR/blob/master/Batch%20HDR.jsx

How to use:

  • Drop the script in the '[photoshop folder]/presets/scripts' folder and restart photoshop if it is already running.
  • The script should appear as a menu item under File>Automate>Batch HDR...
  • A dialog will appear where you specify all the settings for the batch
  • Input Section:
    • Click browse and select the source folder that contains the images to be merged/toned
    • Specify "Number of Brackets" this is how many separate images to group into a single HDR output image (the script will group the images in the source folder in order based on this parameter). You can use a value of 1, which means each image in the source folder is separate and will be toned and output. (I find that many times when shooting RAW, 14 bits is ample to capture the full dynamic of the scene, so I do not waste storage space and shutter acutations using more than one exposure, just make sure to ETTR, also post-processing with this script will be faster)
    • If there are other files in your source folder (for example .xmp files) that you need to exclude you can specify a filte. For example: *.cr2
    • Typically for timelapse you are using a tripod and do not need to have photoshop align the images, but I provided that capability for anyone who might need it b/c it was easy to enable. Note that enabling this will make the process MUCH slower.
  • Toning Section:
    • These settings are all the same as the photoshop HDR toning dialog for "Local Adaptation"
    • To preview what your toning settings will look like you can select any file(s) in your sequence by clicking "Select File(s)..." under "Preview". The file(s) you select will open and merge (if more than one file). When that's finished you can click the "Update Preview" button to apply the current toning settings and see what they will look like (I can't do the fast, automatic previews like the builtin dialog, sorry - somebody from adobe want to show me how to do this?)
    • You can see what the settings will look like at another point in your sequence by clicking the "Select File(s).." button again. (I recommend previewing a shot near the beginning, middle, and end of your sequence)
    • The dialog prevents you from using anything in photoshop while its open so I provided a zoom to allow you to zoom in on the preview
    • The tone curve you must enter as a comma separated list of points (sorry, ScriptUI is not really adequate to create the graphical histogram overlay for the curves like in the builtin dialog). The points are grouped into x,y pairs so for example: x1,y1,x2,y2,etc. Typically you will start with 0,0 and end with 255,255 (note that 255 is the max, full white). Like the curves tool, x represents the input and y represents the output
  • Output Section:
    • Click browse and select a folder for the script to save the output files to
    • The rest of this section should be self explanitory. NOTE: If you select to output 32bit files, the toning will not be applied, the files will simply be merged into a 32bit HDR image and saved.
  • Once you have specified the input and output folders the OK button should be enabled, when you are ready, click OK to start the process
  • Go watch a movie or take a nap, this is going to take a while. The script will display a progress window showing you how far along the proces is (the photoshop progress windows may occasionally appear on top of it, I usually just move them up a little so I can see both)

Limitations:

  • Only the local adaptation method is supported. It's really the only one I ever use, and I didn't really feel like implementing the other ones, but feel free to modify the script yourself to add this support
  • You canot load or save presets from the builtin toning dialog. The format of the preset files is binary and would require reverse engineering of the format and it would be very difficult to read to the files in javascript anyway. If it were xml, it would be much easier, alas it is not. Feel free to grab a hex editor and do this yourself. For now you'll just have to copy all of the values of a preset by hand into the dialog if you want to use a preset. If I have more time this is something I might work on.
  • Preview is slow because it has to do the actual full toning, I don't know a way to do the fast real time previews like the builtin dialog.
  • I don't think the "Cancel" button on the progress window really works, once running, the script is too busy to register a click, anybody know how I might fix this?
  • There may potentially be some ways to improve performance of this script in certain scenarios, IDK, but I'm just glad it works

You're welcome Adobe, you can send me a check at:

[REDACTED]

[REDACTED]

[REDACTED]

Please hit me up with comments/suggestions,

David

TOPICS
Actions and scripting

Views

33.6K

Likes

Translate

Translate

Report

Report
Reply
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Participant ,
Sep 14, 2013

Copy link to clipboard

Copied

I wish Adobe will enable a preview functionality just like in Gaussian Blur filter.

Small 100% Preview window and big preview on the main window.

Likes

Translate

Translate

Report

Report
Reply
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
New Here ,
Nov 08, 2013

Copy link to clipboard

Copied

David, thank you!! I'm reinstalling PS this weekend and absolutely looking forward to trying this out!

Likes

Translate

Translate

Report

Report
Reply
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
New Here ,
Nov 10, 2013

Copy link to clipboard

Copied

Thank you for your script, it's very useful.

I just changed a few things :

The Checkboxes didn't work so I changed them :
alignCheckBox.onChanged by alignCheckBox.onClick
deghostCheckBox.onChanged by deghostCheckBox.onClick
smoothEdgesBox.onChange by smoothEdgesBox.onClick

And for best alignment, I changed :
mergeToHDR.mergeFilesToHDR( currentFileList, false, hdrDeghosting ? kMergeToHDRDeghostBest : kMergeToHDRDeghostOff );
by :
mergeToHDR.mergeFilesToHDR( currentFileList, mergeToHDR.useAlignment, hdrDeghosting ? kMergeToHDRDeghostBest : kMergeToHDRDeghostOff );

And lastly :
My language is french so I had to modify loadPreset function because my own preset file contains the string "P a r   d é f a u t" in place of "D e f a u l t" so that the data is shifted.

Here is the code :

#target photoshop


/*********************************************************************
Batch HDR Script by David Milligan
*********************************************************************/


/*********************************************************************
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.


This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.


You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
**********************************************************************/


/*
// BEGIN__HARVEST_EXCEPTION_ZSTRING


<javascriptresource>
<name>Batch HDR...</name>
<menu>automate</menu>
</javascriptresource>


// END__HARVEST_EXCEPTION_ZSTRING
*/


//these lines import the 'Merge To HDR.jsx' script that is built in to photoshop, we will make calls to that script and some of the scripts that it includes
var runMergeToHDRFromScript = true;
var g_ScriptFolderPath = app.path + "/"+ localize("$$$/ScriptingSupport/InstalledScripts=Presets/Scripts");
var g_ScriptPath = File( g_ScriptFolderPath+'/Merge To HDR.jsx' );
$.evalFile( g_ScriptPath );
//$.level = 2;


//default settings:
mergeToHDR.useAlignment = false;
mergeToHDR.useACRToning = false;
var numberOfBrackets = 3;
var userCanceled = false;
var sourceFolder;
var outputFolder;
var saveType = "JPEG";
var jpegQuality = 10;
var progress;
var statusText;
var progressWindow;
var fileMask = "*";
var outputFilename = "hdr_output_";


var hdrRadius = 100;
var hdrStrength = 0.5;
var hdrGamma = 1.0;
var hdrExposure = 0.0;
var hdrDetail = 100;
var hdrShadow = 0;
var hdrHighlights = 0;
var hdrVibrance = 20;
var hdrSaturation = 30;
var hdrSmooth = false;
var hdrDeghosting = true;
var hdrCurve = "0,0,255,255";
var estTimeRemaining = "";


var previewDoc;
var originalDoc;


function main()
{
    promptUser();
   
    //make sure user didn't cancel
    if(sourceFolder != null && outputFolder != null && sourceFolder.exists && outputFolder.exists && numberOfBrackets > 0)
    {
        initializeProgress();
        var files =  sourceFolder.getFiles(fileMask);
        var currentFileList = new Array();
        for(var index = 0;  index < files.length; index++)
        {
            if((index % numberOfBrackets) == numberOfBrackets - 1)
            {
                var start = new Date();
                progress.value = 100 * index / files.length;
                currentFileList.push(files[index]);
                if(userCanceled) break;
                if(numberOfBrackets > 1)
                {
                    statusText.text = "Merging files "+(index-numberOfBrackets+2)+" - "+(index+1)+" of "+files.length + estTimeRemaining;
                    //for braketed exposures use the mergeToHDR script to merge the files into a single 32 bit image
                    mergeToHDR.outputBitDepth= 32;
                    mergeToHDR.mergeFilesToHDR( currentFileList, mergeToHDR.useAlignment, hdrDeghosting ? kMergeToHDRDeghostBest : kMergeToHDRDeghostOff );
                    statusText.text = "Toning files "+(index-numberOfBrackets+2)+" - "+(index+1)+" of "+files.length+ estTimeRemaining;
                }
                else
                {
                    statusText.text = "Loading file "+(index+1)+" of "+files.length+ estTimeRemaining;
                    //otherwise just open the file
                    doOpenFile(files[index]);
                    statusText.text = "Toning file "+(index+1)+" of "+files.length+ estTimeRemaining;
                }
                progress.value = 100 * (index + numberOfBrackets / 2 ) / files.length;
                if(userCanceled) break;
                if(app.activeDocument != null)
                {
                    //apply the actual tone mapping to the HDR image to get it back down to 8 bits
                    doHDRToning();
                }
               
                //save the result and close
                //TODO: add leading zeros to index in filename
               
                if(numberOfBrackets > 1)
                {
                    statusText.text = "Saving result "+(index-numberOfBrackets+2)+" - "+(index+1)+" of "+files.length+ estTimeRemaining;
                }
                else
                {
                    statusText.text = "Saving result "+(index+1)+" of "+files.length+ estTimeRemaining;
                }
                if(userCanceled) break;
                doSaveFile(outputFolder.absoluteURI + "/" + outputFilename + ZeroPad(Math.round((index + 1)/numberOfBrackets), 5) );
                activeDocument.close(SaveOptions.DONOTSAVECHANGES);
               
                //reset our file list
                currentFileList = new Array();
               
                //calculate time remaining
                var end = new Date();
                var timeElapsed = end.getTime() - start.getTime();
                var mins = timeElapsed / 60000 * ((files.length - index - 1) / numberOfBrackets);
                estTimeRemaining = " | Remaining: " + ZeroPad((mins / 60).toFixed(0),2) + ":" + ZeroPad((mins % 60).toFixed(0),2);
            }
            else
            {
                currentFileList.push(files[index]);
            }
        }
        progressWindow.hide();
    }
}


function doOpenFile(filename)
{
    const eventOpen = app.charIDToTypeID('Opn ');
    var desc = new ActionDescriptor();
    desc.putPath( typeNULL, new File( filename ) );
    desc.putBoolean( kpreferXMPFromACRStr, true ); //not sure what this does or if it is needed
    executeAction( eventOpen, desc, DialogModes.NO );
    //if we don't convert the image to 32bit the mergeToHDR script will not tone our image when we call it, it will simply downconvert it to 8 bit
    convertTo32Bit ();
}


function convertTo32Bit()
{
    var idCnvM = charIDToTypeID( "CnvM" );
    var desc6 = new ActionDescriptor();
    var idDpth = charIDToTypeID( "Dpth" );
    desc6.putInteger( idDpth, 32 );
    var idMrge = charIDToTypeID( "Mrge" );
    desc6.putBoolean( idMrge, false );
    var idRstr = charIDToTypeID( "Rstr" );
    desc6.putBoolean( idRstr, false );
    executeAction( idCnvM, desc6, DialogModes.NO );
}


function doSaveFile(filename)
{
    if(saveType == "JPEG")
    {
        var jpgSaveOptions = new JPEGSaveOptions();
        jpgSaveOptions.embedColorProfile = true;
        jpgSaveOptions.formatOptions = FormatOptions.STANDARDBASELINE;
        jpgSaveOptions.matte = MatteType.NONE;
        jpgSaveOptions.quality = jpegQuality;
        activeDocument.saveAs(new File(filename), jpgSaveOptions, true /*Save As Copy*/, Extension.LOWERCASE /*Append Extention*/);
    }
    else if(saveType == "TIFF")
    {
        var tifSaveOptions = new TiffSaveOptions();
        tifSaveOptions.embedColorProfile = true;
        activeDocument.saveAs(new File(filename), tifSaveOptions, true /*Save As Copy*/, Extension.LOWERCASE /*Append Extention*/);
    }
    else if(saveType == "TIFF LZW")
    {
        var tifSaveOptions = new TiffSaveOptions();
        tifSaveOptions.embedColorProfile = true;
        tifSaveOptions.imageCompression = TIFFEncoding.TIFFLZW;
        activeDocument.saveAs(new File(filename), tifSaveOptions, true /*Save As Copy*/, Extension.LOWERCASE /*Append Extention*/);
    }
    else if(saveType == "TIFF ZIP")
    {
        var tifSaveOptions = new TiffSaveOptions();
        tifSaveOptions.embedColorProfile = true;
        tifSaveOptions.imageCompression = TIFFEncoding.TIFFZIP;
        activeDocument.saveAs(new File(filename), tifSaveOptions, true /*Save As Copy*/, Extension.LOWERCASE /*Append Extention*/);
    }
    else
    {
        activeDocument.saveAs(new File(filename), undefined, true /*Save As Copy*/, Extension.LOWERCASE /*Append Extention*/);
    }
}


function doHDRToning()
{
    //TODO: Reverse engineer the HDR Preset file format and allow user to select a preset file and create the ActionDescriptor from the file
   
    //create the ActionDescriptor that describes the HDR toning settings to use
    var hdDesc = new ActionDescriptor;
    hdDesc.putInteger( stringIDToTypeID( 'version' ), 6 );//I'm not sure what this does
    hdDesc.putInteger(  kmethodStr, 3 );// the toning method to use, 3 = local adaptation
    hdDesc.putDouble( stringIDToTypeID( 'radius' ), hdrRadius );
    hdDesc.putDouble( stringIDToTypeID( 'threshold' ), hdrStrength );// strength
    hdDesc.putDouble( stringIDToTypeID( 'center' ), hdrGamma );// gamma
    hdDesc.putDouble( stringIDToTypeID( 'brightness' ), hdrExposure );// exposure
    hdDesc.putDouble( stringIDToTypeID( 'detail' ), hdrDetail );
    hdDesc.putDouble( stringIDToTypeID( 'shallow' ), hdrShadow );
    hdDesc.putDouble( stringIDToTypeID( 'highlights' ), hdrHighlights );
    hdDesc.putDouble( stringIDToTypeID( 'vibrance' ), hdrVibrance );
    hdDesc.putDouble( stringIDToTypeID( 'saturation' ), hdrSaturation);
    hdDesc.putBoolean( stringIDToTypeID( 'smooth' ), hdrSmooth );
    hdDesc.putBoolean( stringIDToTypeID( 'deghosting' ), hdrDeghosting );
   
    //create the tone curve
    var cDesc = new ActionDescriptor;
    cDesc.putString( stringIDToTypeID( 'name' ), 'Default');
    var cList = new ActionList;
    var points = hdrCurve.split(',');
    for(var i = 0; i < points.length; i++)
    {
        if(i % 2 == 1)
        {
            var pDesc = new ActionDescriptor;
            pDesc.putDouble( stringIDToTypeID( 'horizontal' ), points[i-1] );
            pDesc.putDouble( stringIDToTypeID( 'vertical' ), points );
            pDesc.putBoolean( keyContinuity , false );// ?????
            cList.putObject( charIDToTypeID( 'Pnt ' ), pDesc );
        }
    }
    cDesc.putList( stringIDToTypeID( 'curve' ), cList );
    hdDesc.putObject( kclassContour, classShapingCurve, cDesc );
   
    //call the script that actually invokes the toning plugin
    convertFromHDRNoDialog( outputBitDepth, hdDesc );
}


function initializeProgress()
{
    progressWindow = new Window("palette { text:'Batch HDR Progress', \
        statusText: StaticText { text: 'Processing Images...', preferredSize: [350,20] }, \
        progressGroup: Group { \
            progress: Progressbar { minvalue: 0, maxvalue: 100, value: 0, preferredSize: [300,20] }, \
            cancelButton: Button { text: 'Cancel' } \
        } \
    }");
    statusText = progressWindow.statusText;
    progress = progressWindow.progressGroup.progress;
    progressWindow.progressGroup.cancelButton.onClick = function() { userCanceled = true; }
    progressWindow.show();
}


function promptUser()
{
    var setupWindow = new Window("dialog { orientation: 'row', text: 'Batch HDR', alignChildren:'top', \
        leftGroup: Group { orientation: 'column', alignChildren:'fill', \
            inputPanel: Panel { text: 'Input', \
                sourceGroup: Group { \
                    sourceBox: EditText { characters: 40, text: '' }, \
                    sourceBrowse: Button { text: 'Browse' } \
                }, \
                bracketGroup: Group{ \
                    bracketLabel: StaticText { text: 'Number of Brackets: ' }, \
                    bracketBox: EditText { characters: 2 }, \
                    filterLabel: StaticText { text: 'File Filter: ' }, \
                    filterText: EditText { characters: 5 }, \
                    alignCheckBox: Checkbox { text: 'Align' }\
                    deghostCheckBox: Checkbox { text: 'Deghost' }\
                } \
            }, \
            toningPanel: Panel { text: 'Toning', orientation:'row', alignChildren:'top' } ,\
            outputPanel: Panel { text: 'Output', \
                outputGroup: Group { \
                    outputBox: EditText { characters: 40, text: '' }, \
                    outputBrowse: Button { text: 'Browse' } \
                }, \
                outputOptionsGroup: Group { \
                    outputFilenameLabel: StaticText { text: 'Filename Format: ' }, \
                    outputFilenameText: EditText { characters: 10 }, \
                    outputFilenamePost: StaticText { text: '00001.jpg' }, \
                }, \
                saveSettingsGroup: Group { \
                    saveTypeLabel: StaticText { text: 'Save As: ' }, \
                    saveDropDown: DropDownList { }, \
                    jpegQualityLabel: StaticText { text: 'JPEG Quality (1-10): ' }, \
                    jpegQualityText: EditText { characters: 2}, \
                    outputBitDepthLabel: StaticText { text: 'Bit Depth', enabled:false }, \
                    outputBitDepthDropDown: DropDownList { enabled:false }, \
                } \
            } \
        }, \
        rightGroup: Group { orientation: 'column', alignChildren:'fill', \
            okButton: Button { text: 'OK', enabled: false } \
            cancelButton: Button { text: 'Cancel' } \
        } \
    } ");
   
    generateToningPanel(setupWindow.leftGroup.toningPanel);
   
    //shortcut variables
    var sourceBox = setupWindow.leftGroup.inputPanel.sourceGroup.sourceBox;
    var sourceBrowse = setupWindow.leftGroup.inputPanel.sourceGroup.sourceBrowse;
    var bracketBox = setupWindow.leftGroup.inputPanel.bracketGroup.bracketBox;
    var filterText = setupWindow.leftGroup.inputPanel.bracketGroup.filterText;
    var alignCheckBox = setupWindow.leftGroup.inputPanel.bracketGroup.alignCheckBox;
    var outputBox = setupWindow.leftGroup.outputPanel.outputGroup.outputBox;
    var outputBrowse = setupWindow.leftGroup.outputPanel.outputGroup.outputBrowse;
    var outputFilenameText = setupWindow.leftGroup.outputPanel.outputOptionsGroup.outputFilenameText;
    var saveDropDown = setupWindow.leftGroup.outputPanel.saveSettingsGroup.saveDropDown;
    var jpegQualityText = setupWindow.leftGroup.outputPanel.saveSettingsGroup.jpegQualityText;
    var jpegQualityLabel = setupWindow.leftGroup.outputPanel.saveSettingsGroup.jpegQualityLabel;
    var outputBitDepthDropDown = setupWindow.leftGroup.outputPanel.saveSettingsGroup.outputBitDepthDropDown;
    var outputBitDepthLabel = setupWindow.leftGroup.outputPanel.saveSettingsGroup.outputBitDepthLabel;
    var okButton = setupWindow.rightGroup.okButton;
    var cancelButton = setupWindow.rightGroup.cancelButton;
    var toningPanel = setupWindow.leftGroup.toningPanel;
    var deghostCheckBox = setupWindow.leftGroup.inputPanel.bracketGroup.deghostCheckBox;
   
    //set default values
    bracketBox.text = numberOfBrackets;
    filterText.text = fileMask;
    //mergeToHDR.useAlignment = true;
    alignCheckBox.value = mergeToHDR.useAlignment;
    deghostCheckBox.value = hdrDeghosting;
    outputFilenameText.text = outputFilename;
    jpegQualityText.text = jpegQuality;
    saveDropDown.add("item", "JPEG");
    saveDropDown.add("item", "TIFF");
    saveDropDown.add("item", "TIFF LZW");
    saveDropDown.add("item", "TIFF ZIP");
    saveDropDown.add("item", "PSD");
    saveDropDown.selection = 0;
    outputBitDepthDropDown.add("item", "8");
    outputBitDepthDropDown.add("item", "16");
    outputBitDepthDropDown.add("item", "32");
    outputBitDepthDropDown.selection = 0;
   
    //event handlers
    sourceBox.onChange = function()
    {
        sourceFolder = new Folder(sourceBox.text);
        okButton.enabled = sourceFolder != null && outputFolder != null && sourceFolder.exists && outputFolder.exists;
    };
    sourceBrowse.onClick = function()
    {
        sourceFolder = Folder.selectDialog ("Select the source folder");
        if(sourceFolder != null)
        {
            sourceBox.text = sourceFolder.fullName;
        }
        okButton.enabled = sourceFolder != null && outputFolder != null && sourceFolder.exists && outputFolder.exists;
    };
    bracketBox.onChange = function() { numberOfBrackets = bracketBox.text; };
    filterText.onChange = function() { fileMask = filterText.text; };
    alignCheckBox.onClick = function() { mergeToHDR.useAlignment = alignCheckBox.value; };
    deghostCheckBox.onClick = function() { hdrDeghosting = deghostCheckBox.value; };
    outputBox.onChange = function()
    {
        outputFolder = new Folder(outputBox.text);
        okButton.enabled = sourceFolder != null && outputFolder != null && sourceFolder.exists && outputFolder.exists;
    };
    outputBrowse.onClick = function()
    {
        outputFolder = Folder.selectDialog ("Select the output folder");
        if(outputFolder != null)
        {
            outputBox.text = outputFolder.fullName;
        }
        okButton.enabled = sourceFolder != null && outputFolder != null && sourceFolder.exists && outputFolder.exists;
    };
    outputFilenameText.onChange = function() { outputFilename = outputFilenameText.text; };
    saveDropDown.onChange = function()
    {
        saveType = saveDropDown.selection.text;
        jpegQualityText.enabled = saveDropDown.selection.text == "JPEG";
        jpegQualityLabel.enabled = saveDropDown.selection.text == "JPEG";
        if(saveDropDown.selection.text == "JPEG")
        {
            outputBitDepthDropDown.selection = 0;
        }
        outputBitDepthDropDown.enabled = saveDropDown.selection.text != "JPEG";
        outputBitDepthLabel.enabled = saveDropDown.selection.text != "JPEG";
       
    };
    jpegQualityText.onChange = function() { jpegQuality = jpegQualityText.text; };
    outputBitDepthDropDown.onChange = function()
    {
        outputBitDepth = outputBitDepthDropDown.selection.text;
        toningPanel.enabled = outputBitDepth != 32;
    }
    okButton.onClick = function() { setupWindow.hide(); cleanUpPreviews(); };
    cancelButton.onClick = function() { sourceFolder = null, setupWindow.hide(); cleanUpPreviews(); };
   
    saveDropDown.onChange();
    outputBitDepthDropDown.onChange();
   
    setupWindow.show();
}


function cleanUpPreviews()
{
    if(originalDoc != null)
    {
        originalDoc.close(SaveOptions.DONOTSAVECHANGES);
        originalDoc = null;
    }
    if(previewDoc != null)
    {
        previewDoc.close(SaveOptions.DONOTSAVECHANGES);
        previewDoc = null;
    }
}


function generateToningPanel(toningPanel)
{
    var leftToningGroup = toningPanel.add("group{orientation:'column',alignChildren:'fill'}");
    var rightToningGroup = toningPanel.add("group{orientation:'column',alignChildren:'fill'}");
    var presetGroup = leftToningGroup.add("group{orientation:'row'}");
    var presetDropDown = presetGroup.add("dropdownlist");
    var loadPresetButton = presetGroup.add("button", undefined, "Load Preset");
    var edgePanel = leftToningGroup.add("panel",undefined,"Edge Glow");
    var radiusSlider = createSliderControl(edgePanel.add("group"), "  Radius: ", "px", 0, 500, 0, hdrRadius, function(newValue){ hdrRadius = newValue; });
    var strengthSlider = createSliderControl(edgePanel.add("group"), "Strength: ", "", 0, 4.0, 2, hdrStrength, function(newValue){ hdrStrength = newValue; });
    var edgeGroup = edgePanel.add("group");
    var smoothEdgesBox = edgeGroup.add("checkbox",undefined, "Smooth Edges");
    var detailPanel = leftToningGroup.add("panel",undefined,"Tone and Detail");
    var gammaSlider = createSliderControl(detailPanel.add("group"), "  Gamma: ", "", 0.1, 2.0, 2, hdrGamma, function(newValue){ hdrGamma = newValue; });
    var exposureSlider = createSliderControl(detailPanel.add("group"), "Exposure: ", "", -5.0, 5.0, 2, hdrExposure, function(newValue){ hdrExposure = newValue; });
    var detailSlider = createSliderControl(detailPanel.add("group"), "     Detail: ", "%", -300, 300, 0, hdrDetail, function(newValue){ hdrDetail = newValue; });
    var advancedPanel = leftToningGroup.add("panel",undefined,"Advanced");
    var shadowSlider = createSliderControl(advancedPanel.add("group"), "  Shadow: ", "%", -100, 100, 0, hdrShadow, function(newValue){ hdrShadow = newValue; });
    var highlightSlider = createSliderControl(advancedPanel.add("group"), " Highlight: ", "%",  -100, 100, 0, hdrHighlights, function(newValue){ hdrHighlights = newValue; });
    var vibranceSlider = createSliderControl(advancedPanel.add("group"), "  Vibrance: ", "%",  -100, 100, 0, hdrVibrance, function(newValue){ hdrVibrance = newValue; });
    var saturationSlider = createSliderControl(advancedPanel.add("group"), "Saturation: ", "%",  -100, 100, 0, hdrSaturation, function(newValue){ hdrSaturation = newValue; });
    var toningCurvePanel = leftToningGroup.add("panel{text:'Toning Curve',alignChildren:'fill'}");
    var curveBox = toningCurvePanel.add("edittext", undefined, hdrCurve);
    //right side (preview panel)
    var previewGroup = rightToningGroup.add("panel", undefined, "Preview");
    var selectPreviewButton = previewGroup.add("button",undefined,"Select File(s)...");
    var previewButton = previewGroup.add("button",undefined,"Update Preview");
    var zoomGroup = previewGroup.add("group");
    zoomGroup.add("statictext",undefined, "zoom");
    var zoomBox = zoomGroup.add("edittext { text: '100', characters: 3, enabled: false } ");
    var previewZoomSlider = previewGroup.add("slider { minvalue: 10, maxvalue: 200, value: 100, enabled: false }");
   
    //default values
    smoothEdgesBox.value = hdrSmooth;
    previewButton.enabled = app.documents.length > 0;
    var presetFiles = getPresetFiles();
    var updateSliders = function()
    {
        radiusSlider(hdrRadius);
        strengthSlider(hdrStrength);
        smoothEdgesBox.value = hdrSmooth;
        exposureSlider(hdrExposure);
        gammaSlider(hdrGamma);
        detailSlider(hdrDetail);
        shadowSlider(hdrShadow);
        highlightSlider(hdrHighlights);
        vibranceSlider(hdrVibrance);
        saturationSlider(hdrSaturation);
        curveBox.text = hdrCurve;
    }
    if(presetFiles.length > 0)
    {
        for(var f in presetFiles)
        {
            presetDropDown.add("item", presetFiles.displayName.replace(".hdt",""));
        }
        presetDropDown.selection = 0;
        loadPreset(presetFiles[0]);
        presetDropDown.onChange = function()
        {
            loadPreset(presetFiles[presetDropDown.selection.index]);
            updateSliders();
        };
    }
   
    //event handlers
    loadPresetButton.onClick = function()
    {
        loadPreset(null);
        updateSliders();
    };
    smoothEdgesBox.onClick = function () { hdrSmooth = smoothEdgesBox.value; };
    curveBox.onChange = function () { hdrCurve = curveBox.text; };
    selectPreviewButton.onClick = function()
    {
        var selectedFiles = File.openDialog("Select file(s) to load for preview", function(){return true;}, true);
        if(selectedFiles != null)
        {
            cleanUpPreviews();
            if(selectedFiles instanceof Array)
            {
                if(selectedFiles.length > 1)
                {
                    mergeToHDR.outputBitDepth= 32;
                    mergeToHDR.mergeFilesToHDR( selectedFiles, false, -2 );
                }
                else
                {
                    doOpenFile(selectedFiles[0].fullName);
                }
            }
            else
            {
                doOpenFile(selectedFiles.fullName);
            }
            originalDoc = app.activeDocument;
            previewButton.enabled = originalDoc != null;
            zoomBox.text = getZoomLevel();
            previewZoomSlider.value = getZoomLevel();
        }
    };
    previewButton.onClick = function()
    {
        if(originalDoc != null)
        {
            var tempOutputBitDepth = outputBitDepth;
            outputBitDepth = 16;
            if(previewDoc != null)
            {
                previewDoc.close(SaveOptions.DONOTSAVECHANGES);
            }
            previewDoc = originalDoc.duplicate("HDR Preview");
            app.activeDocument = previewDoc;
            setZoomLevel(previewZoomSlider.value);
            convertTo32Bit();
            doHDRToning();
            outputBitDepth = tempOutputBitDepth;
            waitForRedraw();
            zoomBox.enabled = previewDoc != null;
            previewZoomSlider.enabled = previewDoc != null;
        }
    };
    zoomBox.onChange = function()
    {
        if(previewDoc != null)
        {
            previewZoomSlider.value = zoomBox.text;
            setZoomLevel(previewZoomSlider.value);
        }
    }
    previewZoomSlider.onChange = function()
    {
        if(previewDoc != null)
        {
            zoomBox.text = previewZoomSlider.value.toFixed(0);
            setZoomLevel(previewZoomSlider.value);
        }
    };
   
    updateSliders();
}


function createSliderControl(group,label,postLabel,min,max,round,value,onValueChanged)
{
    var ignoreChange = false;
    group.add("statictext", undefined, label);
    var slider = group.add("slider",undefined,value,min,max);
    slider.alignment = "fill";
    var box = group.add("edittext",undefined,value);
    box.characters = 6;
    group.add("statictext", undefined, postLabel);
    slider.onChange = function()
    {
        if(!ignoreChange)
        {
            ignoreChange = true;
            box.text = slider.value.toFixed(round);
            onValueChanged(slider.value);
            ignoreChange = false;
        }
    };
    box.onChange = function()
    {
        if(!ignoreChange)
        {
            ignoreChange = true;
            slider.value = box.text;
            onValueChanged(box.text);
            ignoreChange = false;
        }
    };
    return function(newValue)
    {
        slider.value = newValue;
        box.text = newValue.toFixed(round);
    };
}


//forces a redraw while a script dialog is active (for preview)
var waitForRedraw = function()
{
    var desc = new ActionDescriptor();
    desc.putEnumerated(charIDToTypeID("Stte"), charIDToTypeID("Stte"), charIDToTypeID("RdCm"));
    executeAction(charIDToTypeID("Wait"), desc, DialogModes.NO);
}


function getZoomLevel()
{
    var ref = new ActionReference();
    ref.putEnumerated( charIDToTypeID("Dcmn"), charIDToTypeID("Ordn"), charIDToTypeID("Trgt") );
    var desc = executeActionGet(ref);
    return Number(desc.getDouble(stringIDToTypeID('zoom'))*100).toFixed(1);
}


function setZoomLevel( zoom )
{
    if(zoom < 1 ) zoom =1;
    var ref = new ActionReference();
    ref.putEnumerated( charIDToTypeID("capp"), charIDToTypeID("Ordn"), charIDToTypeID("Trgt") );
    var getScrRes = executeActionGet(ref).getObjectValue(stringIDToTypeID('unitsPrefs')).getUnitDoubleValue(stringIDToTypeID('newDocPresetScreenResolution'))/72;
    var docRes = activeDocument.resolution;
    activeDocument.resizeImage( undefined, undefined, getScrRes/(zoom/100), ResampleMethod.NONE );
    var desc = new ActionDescriptor();
    ref = null;
    ref = new ActionReference();
    ref.putEnumerated( charIDToTypeID( "Mn  " ), charIDToTypeID( "MnIt" ), charIDToTypeID( 'PrnS' ) );
    desc.putReference( charIDToTypeID( "null" ), ref );
    executeAction( charIDToTypeID( "slct" ), desc, DialogModes.NO );
    activeDocument.resizeImage( undefined, undefined, docRes, ResampleMethod.NONE );
}


function ZeroPad(number,numZeros)
{
    var result = number.toString();
    while(result.length < numZeros)
    {
        result = "0" + result;
    }
    return result;
}


var getPresetFiles = function()
{
    var presetFolder = new Folder(app.path + "/Presets/HDR Toning");
    return presetFolder.getFiles("*.hdt");
}


var loadPreset = function(presetFile)
{
    if(presetFile == null)
    {
        presetFile = File.openDialog("Select Preset","*.hdt");
    }
    if(presetFile != null)
    {
        var tmpStr = new String();
        var binaryData = new Array();
        presetFile.encoding = "BINARY";
        presetFile.open('r');
        while(!presetFile.eof)
        {
            var ch = presetFile.readch();
            if ( ch.charCodeAt(0) == 0 ){
                tmpStr += ' ';
            }
            else {
                tmpStr += ch;
            }
            binaryData.push(ch.charCodeAt(0));
        }
        presetFile.close();
        if(binaryData.length >= 40)
        {
            // init start position for reading datas
            // start position for english version ( string "D e f a u l t" is in the preset file )
            var startPos = 38;
            if ( tmpStr.search ("P a r   d é f a u t") > -1 ){
                // start position for french preset file version ( string "P a r   d é f a u t" is in the preset file ) (==> + 6 bytes)
                startPos = 44;
            }
            // if your preset file can't be read, try this : open it in notepad to see the string "D e f a u l t" in your language and add the code here to set startPos to 38 + diff between the length of ("D e f a u l t") and length of ("D e f a u l t" in your language)

            var curvePointCount = getUInt16(binaryData, startPos);
            if(binaryData.length >= 104 + curvePointCount * 4)
            {
                var curvePointStr = "";
                for(var i = 0; i < curvePointCount; i++)
                {
                    curvePointStr += getUInt16(binaryData, startPos + 4 + i * 4) + "," + getUInt16(binaryData, startPos + 2 + i * 4) + ((i < curvePointCount - 1) ? "," : "");
                }
                hdrCurve = curvePointStr;
               
                hdrStrength =  getFloat32(binaryData,8);
                hdrRadius = getFloat32(binaryData, startPos + 10 + 5 * curvePointCount);
                hdrExposure = getFloat32(binaryData, startPos + 34 + 5 * curvePointCount);
                hdrSaturation = getFloat32(binaryData, startPos + 38 + 5 * curvePointCount);
                hdrDetail = getFloat32(binaryData, startPos + 42 + 5 * curvePointCount);
                hdrShadow = getFloat32(binaryData, startPos + 46 + 5 * curvePointCount);
                hdrHighlights = getFloat32(binaryData, startPos + 50 + 5 * curvePointCount);
                hdrGamma = getFloat32(binaryData, startPos + 54 + 5 * curvePointCount);
                hdrVibrance = getFloat32(binaryData, startPos + 58 + 5 * curvePointCount);
                hdrSmooth = getUInt16(binaryData, startPos + 62 + 5 * curvePointCount) != 0;
            }
            else
            {
                alert("Error Loading File", "Error", true);
            }
        }
        else
        {
            alert("Error Loading File", "Error", true);
        }
    }
}


function getUInt16(byteArray,offset)
{
    return byteArray[offset] * 0x100 + byteArray[offset + 1];
}


function getUInt32(byteArray,offset)
{
    return byteArray[offset] * 0x1000000 + byteArray[offset + 1] * 0x10000 + byteArray[offset + 2] * 0x100 + byteArray[offset + 3];
}


function getFloat32(byteArray,offset)
{
    var bytes = getUInt32(byteArray,offset);
    var sign = (bytes & 0x80000000) ? -1 : 1;
    var exponent = ((bytes >> 23) & 0xFF) - 127;
    var significand = (bytes & ~(-1 << 23));
   
    if (exponent == 128)
        return sign * ((significand) ? Number.NaN : Number.POSITIVE_INFINITY);
   
    if (exponent == -127) {
        if (significand == 0) return sign * 0.0;
        exponent = -126;
        significand /= (1 << 22);
    } else significand = (significand | (1 << 23)) / (1 << 23);
   
    return sign * significand * Math.pow(2, exponent);
}


main();
       

Likes

Translate

Translate

Report

Report
Reply
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
d_13 AUTHOR
Community Beginner ,
Nov 10, 2013

Copy link to clipboard

Copied

Thank you! Would you mind submitting those changes as a pull request via github so I can easily merge them? If not I can do it myself, just wanted you to have the credit in the repo. (Also, there was another pull request I just merged so there will be some slight differences since you made your change - you might want to do a diff of your changes against the version you changed and then apply the diff to the current version).

Likes

Translate

Translate

Report

Report
Reply
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
New Here ,
Nov 23, 2013

Copy link to clipboard

Copied

Hi,

Please do the merge yourself (I don't mind if you take credit for it).

Thanks!

Likes

Translate

Translate

Report

Report
Reply
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
d_13 AUTHOR
Community Beginner ,
Nov 10, 2013

Copy link to clipboard

Copied

You guys might also be interested in this other script I wrote for Bridge:

http://forums.adobe.com/message/5830577#5830577

Likes

Translate

Translate

Report

Report
Reply
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Community Beginner ,
Nov 24, 2013

Copy link to clipboard

Copied

This script is fantastic for stuff shot on tripod. However, it appears that the 'Align' & "De-ghost' fucntions do not seem to work. I shot some hand-held HDR stuff and the ghosting and alignment issues are very present. When I process the shots individually with 'mergetoHDRpro' they turn out fine. I replaced the few lines in the script above with the hope that it may fix this issue but alas, no deal. This isn't a massive hassle as it's only 20 or so HDR shots so not a big deal to process them individually.

For timelpase stuff this is beyond awesome. The ability to process with Camera Raw and get some realistic HDR toning in high contrast situations is something I've been looking for a very long time!

Thanks for taking the time to write this script!

Likes

Translate

Translate

Report

Report
Reply
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
Community Beginner ,
Nov 24, 2013

Copy link to clipboard

Copied

Now I'm getting an error on line 121 " if(app.activeDocument != null) ". Just reverted the script back to the original and it works. Not sure what's going on.

Fixed! I replaced the whole section around line 121. Somthing to do with bracket length instead of number of brackets. (Not handy with code)

Likes

Translate

Translate

Report

Report
Reply
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
New Here ,
Dec 04, 2013

Copy link to clipboard

Copied

Hi Lucas,

I haven't got any problem with 'Align' & "De-ghost' applied on hand-held photos, are you sure to use the latest version (get it again from GITHUB).

You may get an error "if(app.activeDocument != null)" if you have a wrong number of files in source directory or your file mask is "*" and you have a text file in the source folder.

Likes

Translate

Translate

Report

Report
Reply
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
New Here ,
Dec 04, 2013

Copy link to clipboard

Copied

Hi David,

I have done a small change from the latest version again :


To have the same behavior than "sourceBrowse.onClick" (copy to outputBox), I changed this :

    //event handlers
    sourceBox.onChange = function()
    {
        sourceFolder = new Folder(sourceBox.text);
        okButton.enabled = sourceFolder != null && outputFolder != null && sourceFolder.exists && outputFolder.exists;
    };


by :

    //event handlers
    sourceBox.onChange = function()
    {
        sourceFolder = new Folder(sourceBox.text);
        if(sourceFolder != null)
        {
            if (outputFolder == null)
            {
                outputFolder = sourceFolder;
                outputBox.text = outputFolder.fullName;
            }
        }
        okButton.enabled = sourceFolder != null && outputFolder != null && sourceFolder.exists && outputFolder.exists;
    };


and, for our friend "Lucas", line 136, I changed this : (it's more "user friendly")
if(app.activeDocument != null)
{
//apply the actual tone mapping to the HDR image to get it back down to 8 bits
        doHDRToning();
}
 
by :

try{
if(app.activeDocument != null)
        {
         //apply the actual tone mapping to the HDR image to get it back down to 8 bits
                doHDRToning();
}
}
catch(e){
alert("Something is wrong : check files mask and/or files in "+sourceFolder.fullName+" (numbers could be incorrect).");
        return;
}

Likes

Translate

Translate

Report

Report
Reply
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
New Here ,
Nov 30, 2013

Copy link to clipboard

Copied

David,

The script works great,  I process 25 to 30 7 frame images at a time and 80% of the Tiff are just what I expect,  If I could get the program to choose 0.0 Exposure value for remove ghost in 32-bit mode All the images would be perfect. I have tried a few thing but just seem to make matters worse. any and all help is greatly appreciated.

Matt

Likes

Translate

Translate

Report

Report
Reply
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
d_13 AUTHOR
Community Beginner ,
Dec 07, 2013

Copy link to clipboard

Copied

@Matthew M. Ross

When it comes to floating point data (32 bit), there's really no difference in what you set as the "exposure". Floating point means that all brightness ranges have the same weight. When exporting as 32-bit float the image may appear darker depending on how the program you are viewing it with decides to deal with the extra bit depth, but the data is still there, you'll have to tone it or stretch it how you like it to go back down to the low, 8 bit depth of a screen (i.e. there's a lot more data in a 32bit image than you can 'see' on an 8 bit display). 32bit float output is simply meant to be an intermediate format if you only want to use this script to merge, and you want to tone with something else.

If you want to tone with Photoshop, then set the output to 8 or 16 bit and use the tonning settings. If you want to tone with something else (like ACR), and just use the script to merge, export as 32-bit TIFF, and use whatever tool you'd like to use on the resulting TIFF seqeunce.

Likes

Translate

Translate

Report

Report
Reply
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
New Here ,
Dec 19, 2013

Copy link to clipboard

Copied

Thank you for the script, it does exactly what I need! But I have a problem with the 32bits files created with the BatchHDR, they are completely wrong. I'm merging 11 CR2 files together. Any idea?

And is there a way to start that script from Bridge?

Thanks!

Likes

Translate

Translate

Report

Report
Reply
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
d_13 AUTHOR
Community Beginner ,
Dec 28, 2013

Copy link to clipboard

Copied

@Chafouin,

Try it now, I might have fixed it. It skips the toning step if outputing 32 bit format.

@All,

I have implemented two new output formats, OpenEXR (16-bit floating point) and Radiance (RGBE). Toning will not be applied if you use either of these formats.

Likes

Translate

Translate

Report

Report
Reply
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
New Here ,
Jan 02, 2014

Copy link to clipboard

Copied

Yes it works now, thanks a lot

Likes

Translate

Translate

Report

Report
Reply
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
New Here ,
Mar 31, 2014

Copy link to clipboard

Copied

Hi,

thanks a lot for the script, ti's exactly what I need. Unfortunately, when I run it on a folder that contains three images per bracket, the script doesn't go in the order of the filenames, but starts with the second and after that takes the last two and the first. How is the order determined in which the script picks the files?

Thanks a lot,

C

Likes

Translate

Translate

Report

Report
Reply
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
d_13 AUTHOR
Community Beginner ,
Mar 31, 2014

Copy link to clipboard

Copied

someone had a very similar problem recently, try the most recent version, I just added a fix, let me know if it works

Likes

Translate

Translate

Report

Report
Reply
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
New Here ,
Jun 09, 2014

Copy link to clipboard

Copied

I'm having some major issues... whenever I try to do a TL with 3 brackets... I keep getting an error message after the first one saying "Error: No such element Check number of files in source folder"
Anybody have a solution to this? As you can imagine it makes the script unusable for me.

Likes

Translate

Translate

Report

Report
Reply
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
d_13 AUTHOR
Community Beginner ,
Jun 10, 2014

Copy link to clipboard

Copied

Did you try using the file filter? I'm thinking I might make this default to something and be required.

Likes

Translate

Translate

Report

Report
Reply
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
New Here ,
Jun 10, 2014

Copy link to clipboard

Copied

I fixed it last night. That was it. Thanks though.

Austin Kelm

Cinematographer - Wake Studios

wakestudiosjax.com

Likes

Translate

Translate

Report

Report
Reply
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
New Here ,
Jul 23, 2014

Copy link to clipboard

Copied

Hello, all this is way over my head, coding wise.  I copy and pasted the code to a text file and then changed the ext. to jsx and put it in the folder but when I try and run it in photoshop i get this

Line: 1

->  {\rtf1\ansi\ansicpg1252\cocoartf1265\cocoasubrtf210

as an error.  I am working on a mac and the newest version of photoshop.  Any Help?  I also is under the scripts menu not the not the automate.

Likes

Translate

Translate

Report

Report
Reply
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
New Here ,
Jan 14, 2015

Copy link to clipboard

Copied

Howdy!

So I've installed your script and it worked only one time but now I'm being given the error "Error: No such element, Check number of files in source folder"

Thus I've checked and I find that I have (smaller folder amount) 21 images, the flat image, a stop above and a stop below. But then I check the number of files (under more info on the folder) and find that there are 22 files and I can't seem to figure out why?

Have there been any new updates to this script? I really don't want to have to go through all 1000+ photos for my HDR timelapse.

Many thanks in advance and really great add in to PS!

-Hawk

Likes

Translate

Translate

Report

Report
Reply
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
New Here ,
Feb 18, 2015

Copy link to clipboard

Copied

d@v!dm

It would be so so so amazing if this script worked for me. But I am getting the same error as a few people in this thread: "Error: No such element, Check number of files in source folder"

I have 420 images in my folder. My "Number of Brackets" is set to 3. 420 divided by 3 is 140 – an even number.

So i'm curious why this script is behaving as if I have an indivisible number of images in my folder?

I tried the "File Filter" field, but couldn't get it to work. admiralaelove said it helped his issue when he had the same error. But not sure why.

If anyone has a clue I would be very grateful. This script is so very helpful. If only it worked for me!

Thanks,


Matt

Likes

Translate

Translate

Report

Report
Reply
Community Guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
New Here ,
Feb 18, 2015

Copy link to clipboard

Copied

Likes

Translate

Translate

Report

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