Copy link to clipboard
Copied
Hello,
so here is the problem:
I have set of files:
OldImage0001.jpg, OldImage0002.jpg etc. going to OldImage0252.jpg lets say (252 files)
Then I have
Mask0001.jpg, Mask0002.jpg etc. going to Mask0252.jpg
And finally
NewImage0001.jpg, NewImage0002.jpg etc. going to NewImage0252.jpg lets say (252 files)
How do I do this:
Open OldImage0001.jpg put over NewImage0001.jpg and mask it with Mask0001.jpg - merge everything and produce FinishedImage0001.jpg.
Then let this to be done over all 252 image groups?
Thank you.
Here is my take, it could be optimized and have error checking added, however it works as expected in my tests using three random sets of image sequence numbers.
/* Combine Old - New - Mask Images to JPEG.jsx
//community.adobe.com/t5/photoshop/batch-process-groups-of-files-based-on-numbering/td-p/10809093
NOTE: There is no error checking, the 3 input folders must all contain the same quantity of images!
Input files are expected to have filenames such as:
OldImage0001.jpg, OldImage00
...
Copy link to clipboard
Copied
You would have to do that with a script. Using the plugin scriptListener, you can get much of the code that you need. There have been other threads that want to do things somewhat similar, in which you can find some rough examples.
Copy link to clipboard
Copied
Jpeg files have a single background layer that does not support transparency so can not have a mask. If you were to paste a new image in the open old image jpeg background layer from a second open new image jpeg a new layer would be added over the background layer it would contain no transparency. Depending on its size it would cover all or part of the document canvas. So all or part if the old image will be covered. The second layer will be a normal raster image layer that can be masked. If you mask that layer using the third jpeg image luminosity part or the second image layer will contain transparency which will reveal parts of the old image below it. Saving this as a jpeg you will have a composite of the old and new image. Is this what you want to do?
Copy link to clipboard
Copied
The first question that pops into mind is – are the source images in 3 separate folders, or in a single folder?
Copy link to clipboard
Copied
Hello,
thank you.
will try scriptListener but would be cool to see example even here 🙂
I can put files in separate folder - no problem.
do you know how to do it then?
Copy link to clipboard
Copied
Try this. There is no error checking, so you have to set things up exactly with 4 folders on your desktop, labeled: "old images," "new images," "mask images," and "done images." You can edit the code to place the folders whereever you want. Each folder has to have the same number of files in them, except the done folder - obivously. You could also have all the files in one folder, but then you would have to create a new mask for each getFiles command. So instead of '*.jpg' it would be 'NewImage????.jpg' etc.
#target photoshop
var oldFolder = new Folder('~/desktop/old images/');
var newFolder = new Folder('~/desktop/new images/');
var maskFolder = new Folder('~/desktop/mask images/');
var doneFolder = new Folder('~/desktop/done images/');
var mask = '*.jpg';
var oldFiles = oldFolder.getFiles (mask);
var newFiles = newFolder.getFiles (mask);
var maskFiles = maskFolder.getFiles (mask);
var jpegOptions = new JPEGSaveOptions();
jpegOptions.quality = 8;
var count = 10001
for(var i=0;i<oldFiles.length;i++){
var doc = open(oldFiles[i]);
var newF = open(newFiles[i]);
newF.activeLayer.duplicate (doc.activeLayer, ElementPlacement.PLACEBEFORE);
newF.close(SaveOptions.DONOTSAVECHANGES);
var maskF = open(maskFiles[i]);
activeDocument = doc;
var mLay = doc.activeLayer = doc.layers[0];
addMask ();
addMaskImage (maskF.name);
maskF.close(SaveOptions.DONOTSAVECHANGES);
doc.flatten();
doc.saveAs (new File(doneFolder+'/done-'+count.toString().substr (1, 4)),jpegOptions)
count++
doc.close(SaveOptions.DONOTSAVECHANGES);
}
function addMask(){
var idMk = charIDToTypeID( "Mk " );
var desc572 = new ActionDescriptor();
var idNw = charIDToTypeID( "Nw " );
var idChnl = charIDToTypeID( "Chnl" );
desc572.putClass( idNw, idChnl );
var idAt = charIDToTypeID( "At " );
var ref76 = new ActionReference();
var idChnl = charIDToTypeID( "Chnl" );
var idChnl = charIDToTypeID( "Chnl" );
var idMsk = charIDToTypeID( "Msk " );
ref76.putEnumerated( idChnl, idChnl, idMsk );
desc572.putReference( idAt, ref76 );
var idUsng = charIDToTypeID( "Usng" );
var idUsrM = charIDToTypeID( "UsrM" );
var idRvlA = charIDToTypeID( "RvlA" );
desc572.putEnumerated( idUsng, idUsrM, idRvlA );
executeAction( idMk, desc572, DialogModes.NO );
}
function addMaskImage(fName){
var idAppI = charIDToTypeID( "AppI" );
var desc576 = new ActionDescriptor();
var idWith = charIDToTypeID( "With" );
var desc577 = new ActionDescriptor();
var idT = charIDToTypeID( "T " );
var ref77 = new ActionReference();
var idChnl = charIDToTypeID( "Chnl" );
var idChnl = charIDToTypeID( "Chnl" );
var idRGB = charIDToTypeID( "RGB " );
ref77.putEnumerated( idChnl, idChnl, idRGB );
var idLyr = charIDToTypeID( "Lyr " );
var idBckg = charIDToTypeID( "Bckg" );
ref77.putProperty( idLyr, idBckg );
var idDcmn = charIDToTypeID( "Dcmn" );
ref77.putName( idDcmn, fName );
desc577.putReference( idT, ref77 );
var idPrsT = charIDToTypeID( "PrsT" );
desc577.putBoolean( idPrsT, true );
var idClcl = charIDToTypeID( "Clcl" );
desc576.putObject( idWith, idClcl, desc577 );
executeAction( idAppI, desc576, DialogModes.NO );
}
Copy link to clipboard
Copied
Hi Chuck, I get the following error when running your script (Mac OS Mojave 10.14.6) with CC 2019:
In addition to the error, it is also incorrectly combining two images from different sequence numbers.
EDIT: I see that the crappy new forum software has resized the image into illegibility, sigh.
Copy link to clipboard
Copied
Open in new tab or after clicking on an image press on Windows Ctrl + few times.
Copy link to clipboard
Copied
Stephen, is your mask, grayscale? I just used a rgb file, so that would throw an error. If the files are out of sequence, then a sort on each of the arrays should fix that.
Copy link to clipboard
Copied
Yes, the JPEG mask files are RGB, no alphas or layers. Happy to share them if needed, they work fine with my script.
Copy link to clipboard
Copied
Sent you a PM, Stephen, about the files.
Copy link to clipboard
Copied
I had the same problem in both CC2018 and CC2019, so this appears to be a Mac issue related to file input that is different to Windows.
I did as you suggested Chuck, although there were a number of false starts as I was not sure where to apply the sorts...
In the end everything worked fine when I added the following code after the variables were created, before the jpegOptions variable block:
oldFiles.sort();
newFiles.sort();
maskFiles.sort();
So, what I am taking away from this is:
For a cross-platform script that relies on the input sort order being correct, one should always include a sort!
Copy link to clipboard
Copied
Yes, that's what I would have done with the sorts, on each array list. Never had issues with sort order on Win, so never thought about it.
Copy link to clipboard
Copied
I have some ideas and might be able to help, however, this may be a little advanced for my scripting level.
can you provide download links to 3 sets of images?
Copy link to clipboard
Copied
Here is my take, it could be optimized and have error checking added, however it works as expected in my tests using three random sets of image sequence numbers.
/* Combine Old - New - Mask Images to JPEG.jsx
//community.adobe.com/t5/photoshop/batch-process-groups-of-files-based-on-numbering/td-p/10809093
NOTE: There is no error checking, the 3 input folders must all contain the same quantity of images!
Input files are expected to have filenames such as:
OldImage0001.jpg, OldImage0002.jpg etc.
Mask0001.jpg, Mask0002.jpg etc.
NewImage0001.jpg, NewImage0002.jpg etc.
Output files will be modified from the input name:
FinishedImage0001.jpg, FinishedImage0002.jpg etc.
The mask data is based off the RGB composite channel as there was no other info to go by...
It is also assumed that the old/new/mask files all have the same width/height/resolution.
*/
// Prompt for input and output folders
var oldImages = Folder.selectDialog('Select the old images folder...', '~/desktop/'); // select the old images folder
var newImages = Folder.selectDialog('Select the new images folder...', '~/desktop/'); // select the new images folder
var maskImages = Folder.selectDialog('Select the mask images folder...', '~/desktop/'); // select the mask images folder
var outFolder = Folder.selectDialog('Select the save/output folder...', '~/desktop/'); // select the output images folder
// JPG Search Mask
var searchMask = '*.jpg';
var fileList1 = oldImages.getFiles(searchMask);
var fileList2 = newImages.getFiles(searchMask);
var fileList3 = maskImages.getFiles(searchMask);
// Force an alpha-numeric sort
fileList1.sort();
fileList2.sort();
fileList3.sort();
// JPEG Options
var jpegOptions = new JPEGSaveOptions();
jpegOptions.quality = 12; // Quality Level
jpegOptions.embedColorProfile = true; // or false
jpegOptions.formatOptions = FormatOptions.STANDARDBASELINE;
jpegOptions.matte = MatteType.NONE;
// Input Loop
for (var i = 0; i < fileList1.length; i++) {
var doc1 = open(fileList1[i]);
var doc2 = open(fileList2[i]);
var doc3 = open(fileList3[i]);
// Start - Doing stuff to open files
var maskDoc = app.documents[2];
var newDoc = app.documents[1];
var oldDoc = app.documents[0];
app.activeDocument.selection.selectAll();
app.activeDocument.selection.copy(); // Copy the third image 'mask'
app.activeDocument.close(SaveOptions.DONOTSAVECHANGES);
app.activeDocument = oldDoc; // Target first image 'old'
app.activeDocument.paste();
app.activeDocument = newDoc; // Target second image 'new'
app.activeDocument.selection.selectAll();
app.activeDocument.selection.copy(); // Copy the third image 'mask'
app.activeDocument.close(SaveOptions.DONOTSAVECHANGES);
app.activeDocument = oldDoc; // Target first image 'old'
app.activeDocument.paste();
// Add layer mask (Clean SL)
makeLayermask();
function makeLayermask() {
var c2t = function (s) {
return app.charIDToTypeID(s);
};
var s2t = function (s) {
return app.stringIDToTypeID(s);
};
var descriptor = new ActionDescriptor();
var reference = new ActionReference();
descriptor.putClass(s2t("new"), s2t("channel"));
reference.putEnumerated(s2t("channel"), s2t("channel"), s2t("mask"));
descriptor.putReference(s2t("at"), reference);
descriptor.putEnumerated(s2t("using"), c2t("UsrM"), s2t("revealAll"));
executeAction(s2t("make"), descriptor, DialogModes.NO);
}
// Apply image mask layer (Clean SL)
applyImage(true);
function applyImage(preserveTransparency) {
var c2t = function (s) {
return app.charIDToTypeID(s);
};
var s2t = function (s) {
return app.stringIDToTypeID(s);
};
var descriptor = new ActionDescriptor();
var descriptor2 = new ActionDescriptor();
var reference = new ActionReference();
reference.putEnumerated(s2t("channel"), s2t("channel"), s2t("RGB"));
reference.putName(s2t("layer"), "Layer 1");
descriptor2.putReference(s2t("to"), reference);
descriptor2.putBoolean(s2t("preserveTransparency"), preserveTransparency);
descriptor.putObject(s2t("with"), c2t("Clcl"), descriptor2);
executeAction(s2t("applyImageEvent"), descriptor, DialogModes.NO);
}
app.activeDocument.activeLayer = app.activeDocument.layers.getByName("Layer 1");
app.activeDocument.activeLayer.remove();
// Finish - Doing stuff to open files
// Save JPEG
var docName = app.activeDocument.name.replace(/^Old/i, '');
app.activeDocument.flatten();
app.activeDocument.saveAs(new File(outFolder + '/' + 'Finished' + docName.split('.')[0] + '.jpg'), jpegOptions);
app.activeDocument.close(SaveOptions.DONOTSAVECHANGES);
}
alert('Script completed!');
Copy link to clipboard
Copied
Hello everyone and thank you for your input - it is very very helpful!
I would love to give a little update on this.
In the first topic I described adding mask as an example basically - as I though there might be a better - less script like way around it.
What I would really love to be able to do is to apply recorded action - on however many files.
So I would do this - start recording and import SomeFile0001 - then overlay with SomeOtherFile0001 - do masking, brushing - whatever needed - then maybe use EvenSomeOtherFile0001 etc... and then stop recording.
The script would then apply the same action on all files (SomeFile0002 + SomeOtherFile0002 + EvenSomeOtherFile0002) (SomeFile0003 + SomeOtherFile0003 + EvenSomeOtherFile0003) etc. Though there might be however many files - usually two or three, but might be more.
I am so very sorry for not bringing up this example earlier - I did not think about it till right now.
I value all your input so much and all the scripts you posted here too.
Jakub
Copy link to clipboard
Copied
I don't think you'll be able to do this with an action, as actions can't handle different file names. If you just had one file that was changes, you could use a batch process, but not with three different files. Only way to do that with an action is to open all three files in the same order, then create an action, using keyboard shortcuts to select the files, so that the action doesn't record the name of the files.
Copy link to clipboard
Copied
As Chuck said, scripts are required for the stuff that actions can't do...
In the case of the script that I posted, you could alter it to make a hybrid approach. The script is designed to open three files from three different folders. It is easy enough to change this into two folders, or four folders etc. If you need help just ask (I'm fairly new to scripting and I have not forgotten how alien it can be when used to a GUI and actions). The bulk of the script is between the following two comment lines:
// Start - Doing stuff to open files
// Finish - Doing stuff to open files
So you could delete all of that code in the middle between the comments, then insert a link to an action that you have written, such as this example using one of the default Photoshop action sets/actions:
// Start - Doing stuff to open files
app.doAction("Molten Lead","Default Actions.atn");
// Finish - Doing stuff to open files
So leverage the script where needed, then use your action for the stuff where you need control so that you don't need to rely on pure script code.
This is what we already do with the Batch command, or Image Processor or Image Processor Pro.
Copy link to clipboard
Copied
Hi Stephen, ive been looking for a similar solution for days now. The OP's request is same as mine but much much simpler. I just need to combine images in 2 or more folders. regardless of the file name (because i can batch process the file name myself) the important thing is that 001.file from folder A just needs to match with 001.file with folder B. Also my files are not JPG but PNG files.
Is there also a way to not lock the file name to Oldimage or NewImage but rather just match the last few characters eg. 001, 002 This way I can reuse this script regardless of the file name.
Im really not a scripting guy so how would you alter your script? thanks.
Copy link to clipboard
Copied
I'll need to know exactly how many source folders need to be combined. Is it 2, 3 etc?
How should the final stacked files be layered? Masks, blend modes, opacity, layer order etc.
What is the file format for input?
What is the file format and specific options for output?
Can you provide at least two sets of input files and a combined layered version of each that demonstrates exactly how the files should be combined?
P.S. the script previously posted does not have a fixed filename requirement, it just needs the file names in all three source folders to sort in the same consistent manner.
Copy link to clipboard
Copied
Hi Stephen, thanks for your response. The actual material is quite sensitive so I wont be at liberty to give you an actual sample but It is relatively easy to understand if I explain it this way. What im trying to do is actually a 360 degree turn table for a car configurator. For example's sake you can imagine the body and the wheels of the car both contain a series of 40 images. I have here different body colors and different wheel types so you can imagine how many this could be. So there will be 2 folders [Body] and [Wheel] each folder will have 40 images each. Their file names could be anything but they will for sure be numberd eg. body01 wheel01
I just need a way to match these 2 and assemble them. Both are transparent PNGs but the body will be on top of the wheel. Both files have the exact same dimentions as well. I just need to bring these 2 together.
There will be some cases where there will be a 3rd element like a different roof but if this adds extra complexity then im fine with just 2 for now. The output would be a transparent PNG as well with the same size and resolution as the original files. The final output doesnt need to be layered.
Thanks in advance.
Copy link to clipboard
Copied
I just tested with a copy/paste in the script from my body into the wheels file, however the files were not positioned correctly... So I'm going to have to use another method to combine the two files such as Apply Image or Duplicate Layer. Are your files both the same pixel dimensions, mine were but the transparency is obviously throwing off the positioning.
Copy link to clipboard
Copied
OK francist96540321 – please try the following script to combine your coloured car bodies with the matching wheels and let me know how it goes.
/*
Combine PNG Car Body Over Matching Wheels to PNG.jsx
by Stephen Marsh - 2020
//community.adobe.com/t5/photoshop/automated-layer-mask-from-separate-silhouette-file/td-p/10865377
//community.adobe.com/t5/photoshop/batch-process-groups-of-files-based-on-numbering/td-p/10809093
NOTE:
No Files should be open.
There is no error checking, the 2 input folders must all contain the same quantity of alphabetically sorting images.
Original file names will have a prefix of "Combined_" replacing the original filename, retaining the original numbering (it is presumed that no numbering is used elsewhere in the filename).
*/
#target photoshop
/* Start Open Document Error Check - Part A: If */
if (app.documents.length == 0) {
(function () {
// Prompt for input and output folders
var folder1 = Folder.selectDialog('Select the wheels folder...', '~/desktop/');
// Test if CANCEL returns null, then do nothing.
if (folder1 == null) {
return
};
var folder2 = Folder.selectDialog('Select the body folder...', '~/desktop/');
// Test if CANCEL returns null, then do nothing.
if (folder2 == null) {
return
};
var outputFolder = Folder.selectDialog('Select the save/output folder...', '~/desktop/');
// Test if CANCEL returns null, then do nothing.
if (outputFolder == null) {
return
};
// File list - PNG
var searchMask = '*.png';
var fileList1 = folder1.getFiles(searchMask);
var fileList2 = folder2.getFiles(searchMask);
// Alpha-numeric sort
fileList1.sort();
fileList2.sort();
// File input loop
for (var i = 0; i < fileList1.length; i++) {
var doc1 = open(fileList1[i]);
var doc2 = open(fileList2[i]);
// Start - doing stuff to open files
// Duplicate body doc layer to wheels doc
displayDialogs = DialogModes.NO
app.activeDocument.activeLayer.duplicate(documents[0])
// app.activeDocument.layers[0].duplicate(documents[0])
// Close body doc without saving
app.activeDocument.close(SaveOptions.DONOTSAVECHANGES);
// Finish - doing stuff to open files
// Save PNG
var docName = app.activeDocument.name.replace(/\.[^\.]+$/, '').replace(/[^\d]/g, '');
var saveFilePNG = new File(new File(outputFolder + '/' + 'Combined_' + docName.split('.')[0] + '.png'));
SavePNG(saveFilePNG);
// Setup PNG options
function SavePNG(saveFilePNG) {
exportOptions = new ExportOptionsSaveForWeb();
exportOptions.format = SaveDocumentType.PNG;
exportOptions.PNG8 = false; // false = PNG-24
exportOptions.transparency = true; // true = transparent
exportOptions.interlaced = false; // true = interlacing on
exportOptions.includeProfile = true; // false = don't embedd ICC profile
app.activeDocument.exportDocument(saveFilePNG, ExportType.SAVEFORWEB, exportOptions);
}
app.activeDocument.close(SaveOptions.DONOTSAVECHANGES);
}
alert('Script completed!');
})();
}
/* Finish Open Document Error Check - Part A: If */
/* Start Open Document Error Check - Part B: Else */
else {
alert('Please close all open files before running this script!');
}
/* Finish Open Document Error Check - Part B: Else */
Copy link to clipboard
Copied
Copy link to clipboard
Copied
Hey Stephen,
I know months passed but I got to it only now (uf!).
All works and I wanted to thank you so much for writing it!
Jakub