Answered
The following script will save each coin to JPEG in the same folder as the original with the scanned black background in an approximately "square-ish" crop.
This isn't the most elegant or cleanest code, but it should get the job done. Tested on an Intel Mac with Ps2021 and Ps2024.
This script is only intended for the current active document, one open document at a time. If it works for you, it could be recorded into an action for use with the File > Automate > Batch command.
/*
Isolate Multi Coin Scan Sheet to Individual Coins.jsx
v1.0 - 29th February 2024, Stephen Marsh
https://community.adobe.com/t5/photoshop-ecosystem-discussions/how-do-i-seperate-subjects-into-different-seperate-files-automatically/td-p/14449644
*/
#target photoshop
// Select > Select Subject
var idautoCutout = stringIDToTypeID( "autoCutout" );
var desc547 = new ActionDescriptor();
var idsampleAllLayers = stringIDToTypeID( "sampleAllLayers" );
desc547.putBoolean( idsampleAllLayers, false );
executeAction( idautoCutout, desc547, DialogModes.NO );
// Contract selection to remove minor imperfections
var idcontract = stringIDToTypeID( "contract" );
var desc549 = new ActionDescriptor();
var idby = stringIDToTypeID( "by" );
var idpixelsUnit = stringIDToTypeID( "pixelsUnit" );
desc549.putUnitDouble( idby, idpixelsUnit, 50 );
var idselectionModifyEffectAtCanvasBounds = stringIDToTypeID( "selectionModifyEffectAtCanvasBounds" );
desc549.putBoolean( idselectionModifyEffectAtCanvasBounds, false );
executeAction( idcontract, desc549, DialogModes.NO );
// Smooth the selection to further reduce minor imperfections
var idsmoothness = stringIDToTypeID( "smoothness" );
var desc554 = new ActionDescriptor();
var idradius = stringIDToTypeID( "radius" );
var idpixelsUnit = stringIDToTypeID( "pixelsUnit" );
desc554.putUnitDouble( idradius, idpixelsUnit, 25 );
var idselectionModifyEffectAtCanvasBounds = stringIDToTypeID( "selectionModifyEffectAtCanvasBounds" );
desc554.putBoolean( idselectionModifyEffectAtCanvasBounds, false );
executeAction( idsmoothness, desc554, DialogModes.NO );
// Expand the selection for the crop area
var idexpand = stringIDToTypeID( "expand" );
var desc556 = new ActionDescriptor();
var idby = stringIDToTypeID( "by" );
var idpixelsUnit = stringIDToTypeID( "pixelsUnit" );
desc556.putUnitDouble( idby, idpixelsUnit, 100 );
var idselectionModifyEffectAtCanvasBounds = stringIDToTypeID( "selectionModifyEffectAtCanvasBounds" );
desc556.putBoolean( idselectionModifyEffectAtCanvasBounds, false );
executeAction( idexpand, desc556, DialogModes.NO );
// Create a temporary work path from the selection
var idmake = stringIDToTypeID( "make" );
var desc574 = new ActionDescriptor();
var idnull = stringIDToTypeID( "null" );
var ref189 = new ActionReference();
var idpath = stringIDToTypeID( "path" );
ref189.putClass( idpath );
desc574.putReference( idnull, ref189 );
var idfrom = stringIDToTypeID( "from" );
var ref190 = new ActionReference();
var idselectionClass = stringIDToTypeID( "selectionClass" );
var idselection = stringIDToTypeID( "selection" );
ref190.putProperty( idselectionClass, idselection );
desc574.putReference( idfrom, ref190 );
var idtolerance = stringIDToTypeID( "tolerance" );
var idpixelsUnit = stringIDToTypeID( "pixelsUnit" );
desc574.putUnitDouble( idtolerance, idpixelsUnit, 1 );
executeAction(idmake, desc574, DialogModes.NO);
// Create separate paths
workpathToSeparatePaths()
// Remove the top/first working path
app.activeDocument.pathItems[0].remove();
// Create rectangular selection channels
pathsToRectangularSelectionToAlpha();
// Create layers from the alphas
alphasToLayers();
// Delete the Background layer
app.activeDocument.backgroundLayer.remove();
// Rename the layers
var docName = app.activeDocument.name.replace(/\.[^\.]+$/, '');
var zeroPadLength = 3;
var startNumber = 1;
for (var i = 0; i < activeDocument.layers.length; i++) {
activeDocument.layers[i].name = docName + "_" + zeroPad(startNumber, zeroPadLength);
startNumber++
}
function zeroPad(num, digit) {
var tmp = num.toString();
while (tmp.length < digit) {
tmp = "0" + tmp;
}
return tmp;
}
// Remove all paths
for (var i = app.activeDocument.pathItems.length - 1; i >= 0; i--) {
app.activeDocument.pathItems[i].remove();
}
// Remove all alphas
for (i = app.activeDocument.channels.length - 1; i >= 0; i--) {
if (app.activeDocument.channels[i].kind == "ChannelType.MASKEDAREA" || app.activeDocument.channels[i].kind == "ChannelType.SELECTEDAREA") {
app.activeDocument.channels[i].remove();
}
}
// Save all layers
layersToFiles();
app.activeDocument.close(SaveOptions.DONOTSAVECHANGES);
///// FUNCTIONS /////
function layersToFiles() {
var outputPath = app.activeDocument.path.fsName;
processAllLayersAndSets(app.activeDocument);
// Loop over top level layers
function processAllLayersAndSets(obj) {
// Process all layers
for (var al = obj.artLayers.length - 1; 0 <= al; al--) {
app.activeDocument.activeLayer = obj.artLayers[al];
// Skip the Background layer
if (!activeDocument.activeLayer.isBackgroundLayer) {
var filename = app.activeDocument.activeLayer.name;
dupeSelectedLayers(filename);
activeDocument.trim(TrimType.TRANSPARENT);
var sfwOptions = new ExportOptionsSaveForWeb();
sfwOptions.format = SaveDocumentType.JPEG;
sfwOptions.includeProfile = true;
sfwOptions.interlaced = 0;
sfwOptions.optimized = true;
sfwOptions.quality = 75;
var jpgFile = new File(outputPath + "/" + filename + ".jpg");
app.activeDocument.exportDocument(jpgFile, ExportType.SAVEFORWEB, sfwOptions);
var sfwOptions = new ExportOptionsSaveForWeb();
app.activeDocument.close(SaveOptions.DONOTSAVECHANGES);
function dupeSelectedLayers(theDocName) {
function s2t(s) {
return app.stringIDToTypeID(s);
}
var descriptor = new ActionDescriptor();
var reference = new ActionReference();
var reference2 = new ActionReference();
reference.putClass(s2t("document"));
descriptor.putReference(s2t("null"), reference);
descriptor.putString(s2t("name"), theDocName);
reference2.putEnumerated(s2t("layer"), s2t("ordinal"), s2t("targetEnum"));
descriptor.putReference(s2t("using"), reference2);
executeAction(s2t("make"), descriptor, DialogModes.NO);
}
}
}
}
}
function alphasToLayers() {
// Loop over the alpha channels
for (var i = activeDocument.channels.length - 1; i >= 0; i--) {
if (app.activeDocument.channels[i].kind === ChannelType.SELECTEDAREA || app.activeDocument.channels[i].kind === ChannelType.MASKEDAREA) {
var theChannel = new Array(app.activeDocument.channels[i]);
app.activeDocument.activeChannels = theChannel;
// Set a selection from each channel
var idset = stringIDToTypeID("set");
var desc1095 = new ActionDescriptor();
var idnull = stringIDToTypeID("null");
var ref448 = new ActionReference();
var idchannel = stringIDToTypeID("channel");
var idselection = stringIDToTypeID("selection");
ref448.putProperty(idchannel, idselection);
desc1095.putReference(idnull, ref448);
var idto = stringIDToTypeID("to");
var ref449 = new ActionReference();
var idchannel = stringIDToTypeID("channel");
var idordinal = stringIDToTypeID("ordinal");
var idtargetEnum = stringIDToTypeID("targetEnum");
ref449.putEnumerated(idchannel, idordinal, idtargetEnum);
desc1095.putReference(idto, ref449);
executeAction(idset, desc1095, DialogModes.NO);
// Select the RGB composite channel
var idselect = stringIDToTypeID("select");
var desc1109 = new ActionDescriptor();
var idnull = stringIDToTypeID("null");
var ref462 = new ActionReference();
var idchannel = stringIDToTypeID("channel");
var idchannel = stringIDToTypeID("channel");
var idRGB = stringIDToTypeID("RGB");
ref462.putEnumerated(idchannel, idchannel, idRGB);
desc1109.putReference(idnull, ref462);
executeAction(idselect, desc1109, DialogModes.NO);
// Copy the selection to a new layer
var idcopyToLayer = stringIDToTypeID("copyToLayer");
executeAction(idcopyToLayer, undefined, DialogModes.NO);
// Select the Background layer
var idselect = stringIDToTypeID("select");
var desc1118 = new ActionDescriptor();
var idnull = stringIDToTypeID("null");
var ref468 = new ActionReference();
var idlayer = stringIDToTypeID("layer");
ref468.putName(idlayer, "Background");
desc1118.putReference(idnull, ref468);
var idmakeVisible = stringIDToTypeID("makeVisible");
desc1118.putBoolean(idmakeVisible, false);
var idlayerID = stringIDToTypeID("layerID");
var list43 = new ActionList();
list43.putInteger(1);
desc1118.putList(idlayerID, list43);
executeAction(idselect, desc1118, DialogModes.NO);
}
}
}
function pathsToRectangularSelectionToAlpha() {
for (i = app.activeDocument.pathItems.length - 1; i > -1; i--) {
// Select each path in the loop
thePath = app.activeDocument.pathItems[i];
thePath.select();
// Path to selection
function s2t(s) {
return app.stringIDToTypeID(s);
}
var descriptor = new ActionDescriptor();
var reference = new ActionReference();
var reference2 = new ActionReference();
reference.putProperty(s2t("channel"), s2t("selection"));
descriptor.putReference(s2t("null"), reference);
reference2.putEnumerated(s2t("path"), s2t("ordinal"), s2t("targetEnum"));
descriptor.putReference(s2t("to"), reference2);
descriptor.putInteger(s2t("version"), 1);
descriptor.putBoolean(s2t("vectorMaskParams"), true);
executeAction(s2t("set"), descriptor, DialogModes.NO);
// Selection to rectangle
var theBounds = app.activeDocument.selection.bounds;
var theArray = [[theBounds[0], theBounds[1]], [theBounds[2], theBounds[1]], [theBounds[2], theBounds[3]], [theBounds[0], theBounds[3]]];
app.activeDocument.selection.select(theArray, SelectionType.REPLACE, 0, false);
// Selection to alpha channel
var idduplicate = stringIDToTypeID( "duplicate" );
var desc828 = new ActionDescriptor();
var idnull = stringIDToTypeID( "null" );
var ref327 = new ActionReference();
var idchannel = stringIDToTypeID( "channel" );
var idselection = stringIDToTypeID( "selection" );
ref327.putProperty( idchannel, idselection );
desc828.putReference( idnull, ref327 );
executeAction( idduplicate, desc828, DialogModes.NO );
app.activeDocument.selection.deselect();
}
}
function workpathToSeparatePaths() {
// Copy Close Paths hack of c.pfaffenbichler script
// 2015, use it at your own risk;
#target photoshop
try {
if (app.documents.length > 0) {
var myDocument = app.activeDocument;
if (myDocument.pathItems.length > 0) {
if (!selectedPath()) alert("No Path");
var thePath = selectedPath();
if (thePath != undefined) {
var thePathKind = thePath.kind;
// check if selected path is vector mask;
if (thePathKind == PathKind.VECTORMASK && thePath == myDocument.pathItems[myDocument.pathItems.length - 1]) {var replace = true}
else {var replace = false};
var theArray = collectPathInfoFromDesc2012 (myDocument, selectedPath ());
//logInfo(theArray);
var theNewArray = new Array;
// creae new array;
for (var m = 0; m < theArray.length; m++) {
var thisSub = theArray[m];
theNewArray.push(new Array);
for (var n = 0; n < thisSub.length - 2; n++) {
var thisPoint = thisSub[n];
//alert( thisPoint[0] + ", " + thisPoint[1] + ", " + thisPoint[2] + ", " + false );
theNewArray[0].push([thisPoint[0], thisPoint[1], thisPoint[2], false]);
};
theNewArray[0].push(thisSub[thisSub.length-2]);
theNewArray[0].push(thisSub[thisSub.length-1]);
var theNewPath = createPath2015(theNewArray, thePath.name+"p" + m) ;
theNewArray = [];
};
// create copy path;
//logInfo(theNewArray);
//var theNewPath = createPath2015(theNewArray, thePath.name+"x")
};
}
};
}
catch(e) { alert(e + ': on line ' + e.line, 'Photoshop Error', true); }
// from »Flatten All Masks.jsx« by jeffrey tranberry;
///////////////////////////////////////////////////////////////////////////////
// Function: hasVectorMask
// Usage: see if there is a vector layer mask
// Input: <none> Must have an open document
// Return: true if there is a vector mask
///////////////////////////////////////////////////////////////////////////////
function hasVectorMask() {
var hasVectorMask = false;
try {
var ref = new ActionReference();
var keyVectorMaskEnabled = app.stringIDToTypeID( 'vectorMask' );
var keyKind = app.charIDToTypeID( 'Knd ' );
ref.putEnumerated( app.charIDToTypeID( 'Path' ), app.charIDToTypeID( 'Ordn' ), keyVectorMaskEnabled );
var desc = executeActionGet( ref );
if ( desc.hasKey( keyKind ) ) {
var kindValue = desc.getEnumerationValue( keyKind );
if (kindValue == keyVectorMaskEnabled) {
hasVectorMask = true;
}
}
}
catch(e) {
hasVectorMask = false;
}
return hasVectorMask;
};
////// collect path info from actiondescriptor, smooth added //////
function collectPathInfoFromDesc2012 (myDocument, thePath) {
var originalRulerUnits = app.preferences.rulerUnits;
app.preferences.rulerUnits = Units.POINTS;
// based of functions from xbytor’s stdlib;
var ref = new ActionReference();
for (var l = 0; l < myDocument.pathItems.length; l++) {
var thisPath = myDocument.pathItems[l];
if (thisPath == thePath && thisPath.kind == PathKind.WORKPATH) {
ref.putProperty(cTID("Path"), cTID("WrPt"));
};
if (thisPath == thePath && thisPath.kind != PathKind.WORKPATH && thisPath.kind != PathKind.VECTORMASK) {
ref.putIndex(cTID("Path"), l + 1);
};
if (thisPath == thePath && thisPath.kind == PathKind.VECTORMASK) {
var idPath = charIDToTypeID( "Path" );
var idPath = charIDToTypeID( "Path" );
var idvectorMask = stringIDToTypeID( "vectorMask" );
ref.putEnumerated( idPath, idPath, idvectorMask );
};
};
var desc = app.executeActionGet(ref);
var pname = desc.getString(cTID('PthN'));
// create new array;
var theArray = new Array;
var pathComponents = desc.getObjectValue(cTID("PthC")).getList(sTID('pathComponents'));
// for subpathitems;
for (var m = 0; m < pathComponents.count; m++) {
var listKey = pathComponents.getObjectValue(m).getList(sTID("subpathListKey"));
var operation1 = pathComponents.getObjectValue(m).getEnumerationValue(sTID("shapeOperation"));
switch (operation1) {
case 1097098272:
var operation = 1097098272 //cTID('Add ');
break;
case 1398961266:
var operation = 1398961266 //cTID('Sbtr');
break;
case 1231975538:
var operation = 1231975538 //cTID('Intr');
break;
default:
// case 1102:
var operation = sTID('xor') //ShapeOperation.SHAPEXOR;
break;
};
// for subpathitem’s count;
for (var n = 0; n < listKey.count; n++) {
theArray.push(new Array);
var points = listKey.getObjectValue(n).getList(sTID('points'));
try {var closed = listKey.getObjectValue(n).getBoolean(sTID("closedSubpath"))}
catch (e) {var closed = false};
// for subpathitem’s segment’s number of points;
for (var o = 0; o < points.count; o++) {
var anchorObj = points.getObjectValue(o).getObjectValue(sTID("anchor"));
var anchor = [anchorObj.getUnitDoubleValue(sTID('horizontal')), anchorObj.getUnitDoubleValue(sTID('vertical'))];
var thisPoint = [anchor];
try {
var left = points.getObjectValue(o).getObjectValue(cTID("Fwd "));
var leftDirection = [left.getUnitDoubleValue(sTID('horizontal')), left.getUnitDoubleValue(sTID('vertical'))];
thisPoint.push(leftDirection)
}
catch (e) {
thisPoint.push(anchor)
};
try {
var right = points.getObjectValue(o).getObjectValue(cTID("Bwd "));
var rightDirection = [right.getUnitDoubleValue(sTID('horizontal')), right.getUnitDoubleValue(sTID('vertical'))];
thisPoint.push(rightDirection)
}
catch (e) {
thisPoint.push(anchor)
};
try {
var smoothOr = points.getObjectValue(o).getBoolean(cTID("Smoo"));
thisPoint.push(smoothOr)
}
catch (e) {thisPoint.push(false)};
theArray[theArray.length - 1].push(thisPoint);
};
theArray[theArray.length - 1].push(closed);
theArray[theArray.length - 1].push(operation);
};
};
// by xbytor, thanks to him;
function cTID (s) { return cTID[s] || cTID[s] = app.charIDToTypeID(s); };
function sTID (s) { return sTID[s] || sTID[s] = app.stringIDToTypeID(s); };
// reset;
app.preferences.rulerUnits = originalRulerUnits;
return theArray;
};
////// create a path from collectPathInfoFromDesc2012-array //////
function createPath2015(theArray, thePathsName) {
var originalRulerUnits = app.preferences.rulerUnits;
app.preferences.rulerUnits = Units.POINTS;
// thanks to xbytor;
cTID = function(s) { return app.charIDToTypeID(s); };
sTID = function(s) { return app.stringIDToTypeID(s); };
var desc1 = new ActionDescriptor();
var ref1 = new ActionReference();
ref1.putProperty(cTID('Path'), cTID('WrPt'));
desc1.putReference(sTID('null'), ref1);
var list1 = new ActionList();
for (var m = 0; m < theArray.length; m++) {
var thisSubPath = theArray[m];
var desc2 = new ActionDescriptor();
desc2.putEnumerated(sTID('shapeOperation'), sTID('shapeOperation'), thisSubPath[thisSubPath.length - 1]);
var list2 = new ActionList();
var desc3 = new ActionDescriptor();
desc3.putBoolean(cTID('Clsp'), thisSubPath[thisSubPath.length - 2]);
var list3 = new ActionList();
for (var n = 0; n < thisSubPath.length - 2; n++) {
var thisPoint = thisSubPath[n];
var desc4 = new ActionDescriptor();
var desc5 = new ActionDescriptor();
desc5.putUnitDouble(cTID('Hrzn'), cTID('#Rlt'), thisPoint[0][0]);
desc5.putUnitDouble(cTID('Vrtc'), cTID('#Rlt'), thisPoint[0][1]);
desc4.putObject(cTID('Anch'), cTID('Pnt '), desc5);
var desc6 = new ActionDescriptor();
desc6.putUnitDouble(cTID('Hrzn'), cTID('#Rlt'), thisPoint[1][0]);
desc6.putUnitDouble(cTID('Vrtc'), cTID('#Rlt'), thisPoint[1][1]);
desc4.putObject(cTID('Fwd '), cTID('Pnt '), desc6);
var desc7 = new ActionDescriptor();
desc7.putUnitDouble(cTID('Hrzn'), cTID('#Rlt'), thisPoint[2][0]);
desc7.putUnitDouble(cTID('Vrtc'), cTID('#Rlt'), thisPoint[2][1]);
desc4.putObject(cTID('Bwd '), cTID('Pnt '), desc7);
desc4.putBoolean(cTID('Smoo'), thisPoint[3]);
list3.putObject(cTID('Pthp'), desc4);
};
desc3.putList(cTID('Pts '), list3);
list2.putObject(cTID('Sbpl'), desc3);
desc2.putList(cTID('SbpL'), list2);
list1.putObject(cTID('PaCm'), desc2);
};
desc1.putList(cTID('T '), list1);
executeAction(cTID('setd'), desc1, DialogModes.NO);
for (var x = 0; x < activeDocument.pathItems.length; x++) {
if (activeDocument.pathItems[x].kind == PathKind.WORKPATH) {
app.activeDocument.pathItems[x].name = thePathsName;
var myPathItem = app.activeDocument.pathItems[x]
}
};
app.preferences.rulerUnits = originalRulerUnits;
return myPathItem
};
////// determine selected path, updated 08.2011 //////
function selectedPath () {
try {
var ref = new ActionReference();
ref.putEnumerated( charIDToTypeID("Path"), charIDToTypeID("Ordn"), charIDToTypeID("Trgt") );
var desc = executeActionGet(ref);
var theName = desc.getString(charIDToTypeID("PthN"));
// save work path;
if (app.activeDocument.pathItems[app.activeDocument.pathItems.length - 1].kind == PathKind.WORKPATH) {
// =======================================================
var idslct = charIDToTypeID( "slct" );
var desc3 = new ActionDescriptor();
var idnull = charIDToTypeID( "null" );
var ref2 = new ActionReference();
var idPath = charIDToTypeID( "Path" );
var idWrPt = charIDToTypeID( "WrPt" );
ref2.putProperty( idPath, idWrPt );
desc3.putReference( idnull, ref2 );
executeAction( idslct, desc3, DialogModes.NO );
// =======================================================
var idMk = charIDToTypeID( "Mk " );
var desc4 = new ActionDescriptor();
var idnull = charIDToTypeID( "null" );
var ref3 = new ActionReference();
var idPath = charIDToTypeID( "Path" );
ref3.putClass( idPath );
desc4.putReference( idnull, ref3 );
var idFrom = charIDToTypeID( "From" );
var ref4 = new ActionReference();
var idPath = charIDToTypeID( "Path" );
var idWrPt = charIDToTypeID( "WrPt" );
ref4.putProperty( idPath, idWrPt );
desc4.putReference( idFrom, ref4 );
var idNm = charIDToTypeID( "Nm " );
desc4.putString( idNm, dateString() );
executeAction( idMk, desc4, DialogModes.NO );
};
//
if (theName == "Work Path") {
return app.activeDocument.pathItems[app.activeDocument.pathItems.length - 1]
}
else {
return app.activeDocument.pathItems.getByName(theName)
}
}
catch (e) {
return undefined
}
};
////// function to get the date //////
function dateString () {
var now = new Date();
var day = now.getDate();
var month = now.getMonth();
month++;
var year = now.getFullYear();
var hour = now.getHours();
var minutes = now.getMinutes();
var seconds = now.getSeconds();
var myDateText = day+"-"+month+"-"+year+"_"+hour+"-"+minutes+"-"+seconds;
return myDateText
};
function logInfo(Txt){ // Create write and open log file
try {
var file = new File(Folder.desktop + "/copypath].txt");
file.open("w", "TEXT", "????");
file.encoding = "UTF8";
file.seek(0,2);
$.os.search(/windows/i) != -1 ? file.lineFeed = 'windows' : file.lineFeed = 'macintosh';
file.writeln(Txt);
if (file.error) alert(file.error);
file.close();
file.execute();
}
catch(e) { alert(e + ': on line ' + e.line, 'Script Error', true); }
};
}
https://prepression.blogspot.com/2017/11/downloading-and-installing-adobe-scripts.html
Sign up
Already have an account? Login
To post, reply, or follow discussions, please sign in with your Adobe ID.
Sign inSign in to Adobe Community
To post, reply, or follow discussions, please sign in with your Adobe ID.
Sign inEnter your E-mail address. We'll send you an e-mail with instructions to reset your password.
