Copy link to clipboard
Copied
I am looking for a way to place many transparent PNGs (Say 500 PNGs) on top of many different backgrounds (Over 20 backgrounds).
The end result would be 500 JPGs for each PNG file but each with a randomly picked background.
Any help please?
You can try this:
// place files on random background and save jpg;
// 2022, use it at your own risk;
var theOrigFiles = "H:\Product Photos\Designs";
var theBackgroundFiles = "H:\Product Photos\BK";
var theTargetFolder = "H:\Product Photos";
if (Folder(theOrigFiles).exists == true) {
var theOrigFiles = Folder(theOrigFiles).getFiles(/\.(psd|png)$/i);
} else {
var theOrigFiles = selectFile(true)
};
if (Folder(theBackgroundFiles).exists == true) {
var theBackgroundFiles = Folder(theB
...
Here is the final 1.4 script, there is no longer any need to use an action or batch/image processor/image processor pro as this script is now self-contained. You will be prompted to select the JPG, PNG and Save folders and the script will then create the final combined JPG files with random backgrounds.
/*
Place Random Background Image.jsx
Version 1.4, Stephen Marsh, 3rd April 2022
https://community.adobe.com/t5/photoshop-ecosystem-discussions/place-many-pngs-on-many-different-backgrounds/
...
Copy link to clipboard
Copied
Please provide a meaningful description of and file and folder structure and the naming conventions.
How familiar are you with JavaScript and Photoshop’s DOM?
Copy link to clipboard
Copied
Unforutranetly I am not familiar with JavaScript but if provided with the script I can run it 🙂
As for the naming I am flexbile, the only thing that is needed for the original PNG name to stay the same in the end JPG file.
Ideally the PNG files would be named as per the SKUs which would be C001a.png for example (Letter and 3 digit sequntial number then letter)
The backgrounds are named
bk (1).jpg
bk (2).jpg
bk (3).jpg
Many thanks for the help!
Copy link to clipboard
Copied
*The end result would be 1 JPG file for each PNG file...The total files would be only 500 which is the number of the PNG files 🙂
Copy link to clipboard
Copied
@hameedfarah wrote:
*The end result would be 1 JPG file for each PNG file...The total files would be only 500 which is the number of the PNG files 🙂
Was that supposed to answer my request »Please provide a meaningful description of and file and folder structure and the naming conventions.«?
Because it does not seem to do that. (edited)
Where are the two groups of files located, can that be coded into the Script or do you want to use File-/Folder-Selection-dialogs, where are the new files to be stored, how are they supposed to be named, …?
Copy link to clipboard
Copied
I don't want to make it harder really, the easiet possible solution and I will cope 🙂
If it's possible to choose the files or folders for the images, backgrounds and final outputs then great otherwise plesae assume the following
Input images are located in
H:\Product Photos\Designs\C001a.png
H:\Product Photos\Designs\C002a.png
H:\Product Photos\Designs\C003a.png
H:\Product Photos\Designs\C004a.png
....
H:\Product Photos\Designs\C500a.png
Backgrounds are located in
H:\Product Photos\BK\bk (1).jpg
H:\Product Photos\BK\bk (2).jpg
H:\Product Photos\BK\bk (3).jpg
...
H:\Product Photos\BK\bk (20).jpg
The output will be in
H:\Product Photos\C001a.jpg
H:\Product Photos\C002a.jpg
H:\Product Photos\C003a.jpg
H:\Product Photos\C004a.jpg
...
H:\Product Photos\C500a.png
Please note that the number of PNGs and backgrounds is not always fixed
Copy link to clipboard
Copied
Another point:
Do the pngs and the backgrounds (which file format are those by the way?) have the same proportions, pixel dimensions and resolution?
If not: How exactly are the pngs and the backgrounds to be combined? Could you post examples?
By @c.pfaffenbichler
The background images are all 1000x1000 px but the PNG images are resized to fit within 800x800 px
The PNGs need to be aligned in the center
Copy link to clipboard
Copied
To make it clearer, the PNG images are not all squares. The longest edge is resized to be 800px and the other edge is proprtional
So the images mught be
800x800
800x700
600x800 etc.
So a center alignment is needed
Copy link to clipboard
Copied
You can try this:
// place files on random background and save jpg;
// 2022, use it at your own risk;
var theOrigFiles = "H:\Product Photos\Designs";
var theBackgroundFiles = "H:\Product Photos\BK";
var theTargetFolder = "H:\Product Photos";
if (Folder(theOrigFiles).exists == true) {
var theOrigFiles = Folder(theOrigFiles).getFiles(/\.(psd|png)$/i);
} else {
var theOrigFiles = selectFile(true)
};
if (Folder(theBackgroundFiles).exists == true) {
var theBackgroundFiles = Folder(theBackgroundFiles).getFiles(/\.(psd|jpg)$/i);
} else {
var theBackgroundFiles = selectFile(true)
};
if (Folder(theTargetFolder).exists == false) {
theTargetFolder = Folder.selectDialog ("please select target folder")
};
// jpg options;
var jpegOptions = new JPEGSaveOptions();
jpegOptions.quality = 7;
jpegOptions.embedColorProfile = true;
// process files;
for (var m = 0; m < theOrigFiles.length; m++) {
var theFile = File(theOrigFiles[m]);
var theIndex = Math.floor(Math.random()*theBackgroundFiles.length);
var theBG = File(theBackgroundFiles[theIndex]);
var myDocument = app.open(theBG/*File(theBackgroundFiles[theIndex])*/);
// fit on screen;
app.runMenuItem(charIDToTypeID('FtOn'));
// place image;
placeScaleRotateFile (theFile, 0, 0, 100, 100, 0, false);
myDocument.saveAs((new File(theTargetFolder+'/'+theFile.name.match(/(.*)\.[^\.]+$/)[1]+".jpg")),jpegOptions,true);
myDocument.close(SaveOptions.DONOTSAVECHANGES)
};
////////////////////////////////////
////// place //////
function placeScaleRotateFile (file, xOffset, yOffset, theXScale, theYScale, theAngle, linked) {
var idPxl = charIDToTypeID( "#Pxl" );
var idPrc = charIDToTypeID( "#Prc" );
var idPlc = charIDToTypeID( "Plc " );
var desc5 = new ActionDescriptor();
var idnull = charIDToTypeID( "null" );
desc5.putPath( idnull, new File( file ) );
var idFTcs = charIDToTypeID( "FTcs" );
var idQCSt = charIDToTypeID( "QCSt" );
var idQcsa = charIDToTypeID( "Qcsa" );
desc5.putEnumerated( idFTcs, idQCSt, idQcsa );
var idOfst = charIDToTypeID( "Ofst" );
var desc6 = new ActionDescriptor();
var idHrzn = charIDToTypeID( "Hrzn" );
desc6.putUnitDouble( idHrzn, idPxl, xOffset );
var idVrtc = charIDToTypeID( "Vrtc" );
desc6.putUnitDouble( idVrtc, idPxl, yOffset );
var idOfst = charIDToTypeID( "Ofst" );
desc5.putObject( idOfst, idOfst, desc6 );
var idWdth = charIDToTypeID( "Wdth" );
desc5.putUnitDouble( idWdth, idPrc, theYScale );
var idHght = charIDToTypeID( "Hght" );
desc5.putUnitDouble( idHght, idPrc, theXScale );
var idAngl = charIDToTypeID( "Angl" );
var idAng = charIDToTypeID( "#Ang" );
desc5.putUnitDouble( idAngl, idAng,theAngle );
if (linked == true) {
var idLnkd = charIDToTypeID( "Lnkd" );
desc5.putBoolean( idLnkd, true );
};
executeAction( idPlc, desc5, DialogModes.NO );
// get layerid;
var ref = new ActionReference();
ref.putProperty (stringIDToTypeID ("property"), stringIDToTypeID ("layerID"));
ref.putEnumerated( charIDToTypeID("Lyr "), charIDToTypeID("Ordn"), charIDToTypeID("Trgt") );
var layerDesc = executeActionGet(ref);
var layerID = layerDesc.getInteger (stringIDToTypeID ("layerID"));
// return [app.activeDocument.activeLayer, layerID];
return layerID;
};
////// select file //////
function selectFile (multi) {
if (multi == true) {var theString = "please select files"}
else {var theString = "please select one file"};
if ($.os.search(/windows/i) != -1) {var theFiles = File.openDialog (theString, '*.jpg;*.tif;*.psd;*.png', multi)}
else {var theFiles = File.openDialog (theString, getFiles, multi)};
////// filter files for mac //////
function getFiles (theFile) {
if (theFile.name.match(/\.(jpg|tif|psd|png)$/i) || theFile.constructor.name == "Folder") {
return true
};
};
return theFiles
};
Note that existing jpgs of the same name would be replaced without warning.
Edit: My test results (the names differ):
Copy link to clipboard
Copied
I originally thought that this task was beyond my abilities, however, with some time to think about it I have a solution.
Here is the background folder, in this example, it has 6 background images, which could be 20+. It is important that the various filenames are consistent and based on digits/numbers:
Here are the PNG files, in this example, there are 12 files, however, this could be 500+:
And here is the combined result:
Here is a second run, with only 6 random images and 12 input files, there is obviously a lot of repetition. I don't know if there are "better" random number generators?
I will make a separate post with the code and screenshots of how to run...
Copy link to clipboard
Copied
The JS code (original code edited):
/*
Place Random Background Image.jsx
Version 1.1, Stephen Marsh, 28th March 2022
https://community.adobe.com/t5/photoshop-ecosystem-discussions/place-many-pngs-on-many-different-backgrounds/td-p/12839669
*/
//#target photoshop
(function () {
// Set the random background folder
var bgFolder = Folder("~/Desktop/random-bg/");
// Get and limit the returned objects to JPEG files
var bgFiles = bgFolder.getFiles(/\.jpg$/i);
// Count the JPEG files
var bgCount = bgFiles.length;
// Only used for debug console instead of alert
$.writeln(bgCount);
var fileNumber = Math.floor(Math.random() * bgCount) + 1;
var fileName = "~/Desktop/random-bg/bk (" + fileNumber + ").jpg"
placeFile(fileName, 100);
/***** FUNCTIONS *****/
function placeFile(file, scale) {
try {
var idPlc = charIDToTypeID("Plc ");
var desc2 = new ActionDescriptor();
var idnull = charIDToTypeID("null");
desc2.putPath(idnull, new File(file));
var idFTcs = charIDToTypeID("FTcs");
var idQCSt = charIDToTypeID("QCSt");
var idQcsa = charIDToTypeID("Qcsa");
desc2.putEnumerated(idFTcs, idQCSt, idQcsa);
var idOfst = charIDToTypeID("Ofst");
var desc3 = new ActionDescriptor();
var idHrzn = charIDToTypeID("Hrzn");
var idPxl = charIDToTypeID("#Pxl");
desc3.putUnitDouble(idHrzn, idPxl, 0.000000);
var idVrtc = charIDToTypeID("Vrtc");
var idPxl = charIDToTypeID("#Pxl");
desc3.putUnitDouble(idVrtc, idPxl, 0.000000);
var idOfst = charIDToTypeID("Ofst");
desc2.putObject(idOfst, idOfst, desc3);
var idWdth = charIDToTypeID("Wdth");
var idPrc = charIDToTypeID("#Prc");
desc2.putUnitDouble(idWdth, idPrc, scale);
var idHght = charIDToTypeID("Hght");
var idPrc = charIDToTypeID("#Prc");
desc2.putUnitDouble(idHght, idPrc, scale);
var idAntA = charIDToTypeID("AntA");
desc2.putBoolean(idAntA, true);
executeAction(idPlc, desc2, DialogModes.NO);
} catch (e) {}
}
}());
This presumes that the random background folder is named "random-bg" and is located on the desktop.
As the script uses the place image command, to avoid sizing issues, the PPI values must be the same between the random background images and the various transparent PNG images. Both sets of images also had the same pixel width and height so positioning was not required.
I recorded the script into an action:
Note that I have included a step to move the active layer backwards, as the background image has to be behind the transparent image. I could have added this in the script, but took the easy path for this example.
I then used Image Processor Pro to run the action over the 12 images:
It works with the basic Image Processor script that is installed with Photoshop, however, I only had a successful result with PSD, using JPEG I had an incorrect result with only the background image being visible, which is why I used the Image Processor Pro script. One could also try the Batch command.
More could be done, I don't have much time tonight so this was the best that I could come up with.
Copy link to clipboard
Copied
Thank you so much, this looks exactly what I need...
I will try it and get back to you.
Thank you
Copy link to clipboard
Copied
It is working fine excpet that sometimes the placed background file is off center.
And can the script be modified to take variable number of backgrounds (whatever backgrounds containted in the folder) so I don't have to change the number each time I run the script?
Copy link to clipboard
Copied
Yes, it should be possible to make these additions.
@hameedfarah – I have updated the original code to a 1.1 version where the random background folder count is variable and not hard-coded. I plan to make further changes when I have time, this 1.1 update is still very much a proof of concept script.
Copy link to clipboard
Copied
… And can the script be modified to take variable number of backgrounds (whatever backgrounds containted in the folder) so I don't have to change the number each time I run the script?
By @hameedfarah
You can try something like that:
var count = Folder("~/desktop/YourFolderName").getFiles(); // if there are no subfolders or nested folders
alert(count.length)
Copy link to clipboard
Copied
@pixxxelschubser – Good point, I have been assuming that the input folder does not contain sub-folders.
I used a file filter to exclude anything from the count that is not a .jpg file, so sub-folders are also ignored.
var bgFolder = Folder("~/Desktop/random-bg/");
var bgFiles = bgFolder.getFiles(/\.jpg$/i);
var bgCount = bgFiles.length;
alert(bgCount);
Copy link to clipboard
Copied
Another point:
Do the pngs and the backgrounds (which file format are those by the way?) have the same proportions, pixel dimensions and resolution?
If not: How exactly are the pngs and the backgrounds to be combined? Could you post examples?
Copy link to clipboard
Copied
Here is the final 1.4 script, there is no longer any need to use an action or batch/image processor/image processor pro as this script is now self-contained. You will be prompted to select the JPG, PNG and Save folders and the script will then create the final combined JPG files with random backgrounds.
/*
Place Random Background Image.jsx
Version 1.4, Stephen Marsh, 3rd April 2022
https://community.adobe.com/t5/photoshop-ecosystem-discussions/place-many-pngs-on-many-different-backgrounds/td-p/12839669
Notes:
* Random background images in JPEG format, 1000 x 1000 px
* Random background image filename must be consistent and have variable digits,
i.e.: bk (5).jpg, bk (20).jpg or Post_4.jpg, Post_11.jpg etc
* Variable foreground images in transparent PNG format, various sizes fitted to 800 x 800px
* Foreground to be centered on background
*/
#target photoshop
(function () {
try {
// JPEG folder selection
var jpgFolder = Folder.selectDialog("Select the random JPG background image folder:");
if (jpgFolder === null) {
alert('Script cancelled!');
return;
}
// PNG folder selection
var pngFolder = Folder.selectDialog("Select the transparent PNG foreground image folder:");
if (pngFolder === null) {
alert('Script cancelled!');
return;
}
// Validate input folders
var validateInputDir = (jpgFolder.fsName === pngFolder.fsName);
if (validateInputDir === true) {
alert("Script cancelled as both the input folders are the same!");
return;
}
// Limit the file input
var jpgList = jpgFolder.getFiles(/\.(jpg|jpeg)$/i);
var pngList = pngFolder.getFiles(/\.(png)$/i);
// Validate that the JPEG list isn't empty
var validateJPEGEmptyList = (jpgList.length > 0);
if (validateJPEGEmptyList === false) {
alert("Script cancelled as the JPEG input folder is empty!");
return;
}
// Validate that the PNG list isn't empty
var validatePNGEmptyList = (jpgList.length > 0);
if (validatePNGEmptyList === false) {
alert("Script cancelled as the PNG input folder is empty!");
return;
}
// Output folder selection
var saveFolder = Folder.selectDialog("Please select the folder to save to...");
if (saveFolder === null) {
alert('Script cancelled!');
return;
}
// Save and set the dialog display settings
var savedDisplayDialogs = app.displayDialogs;
app.displayDialogs = DialogModes.NO;
// Perform the stacking and saving
for (var i = 0; i < pngList.length; i++) {
var doc = open(pngList[i]);
var docName = activeDocument.name.replace(/\.[^\.]+$/, '');
activeDocument.resizeCanvas(UnitValue(1000, "px"), UnitValue(1000, "px"), AnchorPosition.MIDDLECENTER);
// Count the JPEG files
var bgCount = jpgList.length;
// Random generator based on the JPEG files count
var bgNumber = Math.floor(Math.random() * bgCount) + 1;
// Random background file name using random variable
var randomBaseName = jpgList[0].displayName.replace(/\d+/, bgNumber);
var bgFileName = jpgFolder + "/" + randomBaseName;
placeFile(bgFileName, 100);
centreActiveLayerToCanvas();
moveLayerRelativeStack("previous");
// JPEG save options
var jpgOptions = new JPEGSaveOptions();
jpgOptions.formatOptions = FormatOptions.STANDARDBASELINE;
jpgOptions.embedColorProfile = true;
jpgOptions.matte = MatteType.NONE;
jpgOptions.quality = 10;
doc.saveAs(new File(saveFolder + '/' + docName + '.jpg'), jpgOptions);
doc.close(SaveOptions.DONOTSAVECHANGES);
}
// End of script
app.displayDialogs = savedDisplayDialogs;
app.beep();
alert('Script completed!' + '\r' + pngList.length + ' JPEG files (quality level 10) saved to:' + '\r' + saveFolder.fsName);
} catch (err) {
while (documents.length > 0) {
activeDocument.close(SaveOptions.DONOTSAVECHANGES);
}
alert("An unexpected error has occurred!");
}
/***** FUNCTIONS *****/
function centreActiveLayerToCanvas() {
var doc = app.activeDocument;
var docLay = app.activeDocument.activeLayer;
docLay.translate(doc.width / 2 - (docLay.bounds[0] + docLay.bounds[2]) / 2,
doc.height / 2 - (docLay.bounds[1] + docLay.bounds[3]) / 2);
}
function moveLayerRelativeStack(relPos) {
// "previous" or "next" or "front" or "back"
var c2t = function (s) {
return app.charIDToTypeID(s);
};
var s2t = function (s) {
return app.stringIDToTypeID(s);
};
var descriptor = new ActionDescriptor();
var reference = new ActionReference();
var reference2 = new ActionReference();
reference.putEnumerated(s2t("layer"), s2t("ordinal"), s2t("targetEnum"));
descriptor.putReference(c2t("null"), reference);
reference2.putEnumerated(s2t("layer"), s2t("ordinal"), s2t(relPos));
descriptor.putReference(s2t("to"), reference2);
executeAction(s2t("move"), descriptor, DialogModes.NO);
}
function placeFile(file, scale) {
try {
var idPlc = charIDToTypeID("Plc ");
var desc2 = new ActionDescriptor();
var idnull = charIDToTypeID("null");
desc2.putPath(idnull, new File(file));
var idFTcs = charIDToTypeID("FTcs");
var idQCSt = charIDToTypeID("QCSt");
var idQcsa = charIDToTypeID("Qcsa");
desc2.putEnumerated(idFTcs, idQCSt, idQcsa);
var idOfst = charIDToTypeID("Ofst");
var desc3 = new ActionDescriptor();
var idHrzn = charIDToTypeID("Hrzn");
var idPxl = charIDToTypeID("#Pxl");
desc3.putUnitDouble(idHrzn, idPxl, 0.000000);
var idVrtc = charIDToTypeID("Vrtc");
var idPxl = charIDToTypeID("#Pxl");
desc3.putUnitDouble(idVrtc, idPxl, 0.000000);
var idOfst = charIDToTypeID("Ofst");
desc2.putObject(idOfst, idOfst, desc3);
var idWdth = charIDToTypeID("Wdth");
var idPrc = charIDToTypeID("#Prc");
desc2.putUnitDouble(idWdth, idPrc, scale);
var idHght = charIDToTypeID("Hght");
var idPrc = charIDToTypeID("#Prc");
desc2.putUnitDouble(idHght, idPrc, scale);
var idAntA = charIDToTypeID("AntA");
desc2.putBoolean(idAntA, true);
executeAction(idPlc, desc2, DialogModes.NO);
} catch (e) {}
}
}());
Copy link to clipboard
Copied
Thank you so much @Stephen_A_Marsh the script is great.
But two issues,
-Can the output be the same size as the background JOG rather than the input PNG?
-When the JPG files were named Post_01.jpg the script ignored them and just saved the PNGs as JPGs, after renaming them to bk (1).jpg the script worked correctly
Copy link to clipboard
Copied
You previously stated that the backgrounds used a set naming convention of:
bk (##).jpg
This is what is hard-coded into the 1.2 version script. Are you now saying that the random background image filename is variable and not consistent?
Copy link to clipboard
Copied
I am sorry but I am not quite sure what is meant by "reveal all" but all the background images are 1000x000 px but the resulting JPGs have different sizes
As for the second point, I thought since it is asking for a folder where the backgrounds are stored it would take all the JPGs inside it regardless of name which would make things easier...Not a major issue, just an observation 🙂
Thank you again
Copy link to clipboard
Copied
@hameedfarah wrote:
I am sorry but I am not quite sure what is meant by "reveal all" but all the background images are 1000x000 px but the resulting JPGs have different sizes
As in Image > Reveal All
This should have automatically resized the 800px image to accommodate the 1000px image, but it was not working as intended in the 1.2 version.
As for the second point, I thought since it is asking for a folder where the backgrounds are stored it would take all the JPGs inside it regardless of name which would make things easier...
Variable filenames have to be programmed, they don't just magically happen due to a folder being selected. I initially built the random background file selection based on your static example name.
I am writing and testing on a Mac, so drive letters are not valid. It is more convenient for me to create a cross-platform folder selection step than hard-coding in a static cross-platform directory path such as the ~/Desktop. It was not my intention to set the expectation that any filename was valid. In hindsight, it would have been best to take variable filenames into account from the beginning.
I have updated the previous script code to use a variable background image name. As the script allows variable folder input/output, variable random background counts and variable background filenames – it should be quite flexible for ongoing projects where elements may change.
Please let me know how these new 1.3 revisions work for you...
Copy link to clipboard
Copied
Copy link to clipboard
Copied
@hameedfarah wrote:
Thank you again and again 🙂
one last issue, the background image is shifted upwards by a random value for some reason...
I have added some code to centre the background layer in an updated 1.4 version, let me know how it works, thanks!
Copy link to clipboard
Copied
That's perfect!
Thank you again. You are awesome!