Merge Images To Layers
Script for Adobe Photoshop
Merge two images into one with a layer for each input image.
- Combine single or multiple layer images
- Output layered PSD
- Adapt open source to customize or create other scripts
How-to Video
How to use the script
The interface has three sections: Input 1, Input 2, and Output. Choose the folders and click the OK button to begin. A progress bar is displayed as images are processed.
Section 1: Input 1
Folder — select a folder of images. The layers of each image in the folder will become the highest layers in the output file.
Section 2: Input 2
Folder — select a folder of images. Any image found that matches the name of an image from input folder 1, the layers of the image are copied to the output image below the layers of input image 1.
Section 3: Output
Folder — select a folder to which output images are saved. Each image is saved the same name as the two input images but in Photoshop format and the extension PSD.
When files are output, any existing files of the same name in the output folder are replaced without alert.
Source code
(download button below)
/*
Merge Images To Layers
Copyright 2023 William Campbell
All Rights Reserved
https://www.marspremedia.com/contact
Permission to use, copy, modify, and/or distribute this software
for any purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
(function () {
var title = "Merge Images To Layers";
if (!/photoshop/i.test(app.name)) {
alert("Script for Photoshop", title, false);
return;
}
app.displayDialogs = DialogModes.ERROR;
// Script variables.
var doneMessage;
var error;
var files1;
var files2;
var folderInput1;
var folderInput2;
var folderOutput;
var progress;
// Reusable UI variables.
var g; // group
var p; // panel
var w; // window
// Permanent UI variables.
var btnCancel;
var btnFolderInput1;
var btnFolderInput2;
var btnFolderOutput;
var btnOk;
var txtFolderInput1;
var txtFolderInput2;
var txtFolderOutput;
// CREATE PROGRESS WINDOW
progress = new Window("palette", "Progress");
progress.t = progress.add("statictext");
progress.t.preferredSize.width = 450;
progress.b = progress.add("progressbar");
progress.b.preferredSize.width = 450;
progress.add("statictext", undefined, "Press ESC to cancel");
progress.display = function (message) {
message && (this.t.text = message);
this.show();
app.refresh();
};
progress.increment = function () {
this.b.value++;
};
progress.set = function (steps) {
this.b.value = 0;
this.b.minvalue = 0;
this.b.maxvalue = steps;
};
// CREATE USER INTERFACE
w = new Window("dialog", title);
w.alignChildren = "fill";
// Panel 'Input 1'
p = w.add("panel", undefined, "Input 1");
p.alignChildren = "left";
p.margins = [18, 18, 18, 18];
g = p.add("group");
btnFolderInput1 = g.add("button", undefined, "Folder...");
txtFolderInput1 = g.add("statictext", undefined, undefined, {
truncate: "middle"
});
txtFolderInput1.preferredSize.width = 350;
// Panel 'Input 2'
p = w.add("panel", undefined, "Input 2");
p.alignChildren = "left";
p.margins = [18, 18, 18, 18];
g = p.add("group");
btnFolderInput2 = g.add("button", undefined, "Folder...");
txtFolderInput2 = g.add("statictext", undefined, undefined, {
truncate: "middle"
});
txtFolderInput2.preferredSize.width = 350;
// Panel 'Output'
p = w.add("panel", undefined, "Output");
p.alignChildren = "left";
p.margins = [18, 18, 18, 18];
g = p.add("group");
btnFolderOutput = g.add("button", undefined, "Folder...");
txtFolderOutput = g.add("statictext", undefined, "", {
truncate: "middle"
});
txtFolderOutput.preferredSize.width = 380;
// Action Buttons
g = w.add("group");
g.alignment = "center";
btnOk = g.add("button", undefined, "OK");
btnCancel = g.add("button", undefined, "Cancel");
// Panel Copyright
p = w.add("panel");
p.add("statictext", undefined, "Copyright 2023 William Campbell");
// UI ELEMENT EVENT HANDLERS
btnFolderInput1.onClick = function () {
var f = Folder.selectDialog("Select input folder 1", txtFolderInput1.text);
if (f) {
txtFolderInput1.text = Folder.decode(f.fullName);
}
};
btnFolderInput2.onClick = function () {
var f = Folder.selectDialog("Select input folder 2", txtFolderInput2.text);
if (f) {
txtFolderInput2.text = Folder.decode(f.fullName);
}
};
btnFolderOutput.onClick = function () {
var f = Folder.selectDialog("Select output folder", txtFolderOutput.text);
if (f) {
txtFolderOutput.text = Folder.decode(f.fullName);
}
};
btnOk.onClick = function () {
folderInput1 = new Folder(txtFolderInput1.text);
if (!(folderInput1 && folderInput1.exists)) {
alert("Select input folder 1", " ", false);
return;
}
folderInput2 = new Folder(txtFolderInput2.text);
if (!(folderInput2 && folderInput2.exists)) {
alert("Select input folder 2", " ", false);
return;
}
folderOutput = new Folder(txtFolderOutput.text);
if (!(folderOutput && folderOutput.exists)) {
txtFolderOutput.text = "";
alert("Select output folder", " ", false);
return;
}
if (txtFolderInput1.text == txtFolderOutput.text || txtFolderInput2.text == txtFolderOutput.text) {
alert("Input and output folders cannot be the same", " ", false);
return;
}
w.close(1);
};
btnCancel.onClick = function () {
w.close(0);
};
// DISPLAY THE DIALOG
if (w.show() == 1) {
doneMessage = "";
try {
process();
doneMessage = doneMessage || "Done";
} catch (e) {
if (/User cancel/.test(e.message)) {
doneMessage = "Canceled";
} else {
error = error || e;
doneMessage = "An error has occurred.\nLine " + error.line + ": " + error.message;
}
}
app.bringToFront();
progress.close();
doneMessage && alert(doneMessage, title, error);
}
//====================================================================
// END PROGRAM EXECUTION, BEGIN FUNCTIONS
//====================================================================
function process() {
var baseName;
var doc1;
var doc2;
var file1;
var file2;
var i;
var ii;
app.displayDialogs = DialogModes.NO;
progress.display("Reading folders...");
try {
files1 = folderInput1.getFiles(function (f) {
if (f instanceof File && !f.hidden) {
return true;
}
return false;
});
files2 = folderInput2.getFiles(function (f) {
if (f instanceof File && !f.hidden) {
return true;
}
return false;
});
if (!files1.length || !files2.length) {
doneMessage = "No files found in one or more selected folders";
return;
}
progress.set(files1.length);
for (i = 0; i < files1.length; i++) {
file1 = files1[i];
file2 = null;
progress.increment();
progress.display(File.decode(file1.name));
// Look for matching file.
// Same name minus extension.
baseName = file1.name.replace(/\.[^\.]*$/, "");
for (ii = 0; ii < files2.length; ii++) {
if (files2[ii].name.replace(/\.[^\.]*$/, "") == baseName) {
// Match.
file2 = files2[ii];
break;
}
}
if (file2) {
// Found match. Open both images and process.
doc1 = app.open(file1);
doc2 = app.open(file2);
processDoc(doc1, doc2);
doc1.close(SaveOptions.DONOTSAVECHANGES);
doc2.close(SaveOptions.DONOTSAVECHANGES);
}
}
} finally {
app.displayDialogs = DialogModes.ERROR;
}
}
function processDoc(doc1, doc2) {
var baseName;
var fileOutput;
var i;
var layer;
var saveOptions;
app.activeDocument = doc1;
layer = doc1.layers[doc1.layers.length - 1];
if (layer.isBackgroundLayer) {
layer.isBackgroundLayer = false;
layer.name = "image 1";
}
app.activeDocument = doc2;
layer = doc2.layers[doc2.layers.length - 1];
if (layer.isBackgroundLayer) {
layer.isBackgroundLayer = false;
layer.name = "image 2";
}
for (i = 0; i < doc2.layers.length; i++) {
doc2.layers[i].duplicate(doc1, ElementPlacement.PLACEATEND);
}
app.activeDocument = doc1;
// Save PSD
baseName = doc1.name.replace(/\.[^\.]*$/, "");
fileOutput = new File(folderOutput + "/" + baseName + ".psd");
saveOptions = new PhotoshopSaveOptions();
saveOptions.alphaChannels = true;
saveOptions.annotations = true;
saveOptions.embedColorProfile = true;
saveOptions.layers = true;
saveOptions.spotColors = true;
doc1.saveAs(fileOutput, saveOptions);
}
})();
Merge Images To Layers
For help installing scripts, see How to Install and Use Scripts in Adobe Creative Cloud Applications.
IMPORTANT: scripts are developed for the latest Adobe Creative Cloud applications. Many scripts work in CC 2018 and later, even some as far back as CS6, but may not perform as expected, or run at all, when used in versions prior to 2018. Photoshop features Select Subject and Preserve Details 2.0 definitely fail prior to CC 2018 (version 19) as the features do not exist in earlier versions. For best results use the latest versions of Adobe Creative Cloud applications.
IMPORTANT: by downloading any of the scripts on this page you agree that the software is provided without any warranty, express or implied. USE AT YOUR OWN RISK. Always make backups of important data.
IMPORTANT: fees paid for software products are the purchase of a non-exclusive license to use the software product and do not grant the purchaser any degree of ownership of the software code. Author of the intellectual property and copyright holder William Campbell retains 100% ownership of all code used in all software products regardless of the inspiration for the software product design or functionality.